1.1 --- a/java/Postak/src/cz/frantovo/postak/Postak.java Tue Nov 29 17:13:30 2011 +0100
1.2 +++ b/java/Postak/src/cz/frantovo/postak/Postak.java Mon Apr 23 00:24:31 2012 +0200
1.3 @@ -1,6 +1,8 @@
1.4 package cz.frantovo.postak;
1.5
1.6 import java.io.File;
1.7 +import java.io.StringReader;
1.8 +import java.io.StringWriter;
1.9 import java.util.ArrayList;
1.10 import java.util.Collection;
1.11 import java.util.Properties;
1.12 @@ -13,210 +15,251 @@
1.13 import javax.mail.PasswordAuthentication;
1.14 import javax.mail.Session;
1.15 import javax.mail.Transport;
1.16 +import javax.mail.internet.MimeBodyPart;
1.17 import javax.mail.internet.MimeMessage;
1.18 +import javax.mail.internet.MimeMultipart;
1.19 +import javax.xml.transform.Transformer;
1.20 +import javax.xml.transform.TransformerFactory;
1.21 +import javax.xml.transform.stream.StreamResult;
1.22 +import javax.xml.transform.stream.StreamSource;
1.23
1.24 /**
1.25 * Odešle hromadnou zprávu pomocí SMTP.
1.26 - *
1.27 + *
1.28 * @author fiki
1.29 */
1.30 -public class Postak {
1.31 -
1.32 - private static final Logger log = Logger.getLogger(Postak.class.getName());
1.33 - /** Regulární výraz pro správnou e-mailovou adresu */
1.34 - private static String REGULARNI_EMAIL = "^[_a-zA-Z0-9\\.\\-]+@[_a-zA-Z0-9\\.\\-]+\\.[a-zA-Z]{2,4}$";
1.35 -
1.36 - private Nastaveni nastaveni;
1.37 +public class Postak {
1.38
1.39 - public Postak(Nastaveni nastaveni) {
1.40 - this.nastaveni = nastaveni;
1.41 - }
1.42 + private static final String KÓDOVÁNÍ = "UTF-8";
1.43 + private static final Logger log = Logger.getLogger(Postak.class.getName());
1.44 + /** Regulární výraz pro správnou e-mailovou adresu */
1.45 + private static String REGULARNI_EMAIL = "^[_a-zA-Z0-9\\.\\-]+@[_a-zA-Z0-9\\.\\-]+\\.[a-zA-Z]{2,4}$";
1.46 + private Nastaveni nastaveni;
1.47 + private TransformerFactory transformerFactory;
1.48
1.49 - public void setNastaveni(Nastaveni nastaveni) {
1.50 - this.nastaveni = nastaveni;
1.51 - }
1.52 + public Postak(Nastaveni nastaveni) {
1.53 + this.nastaveni = nastaveni;
1.54 + transformerFactory = TransformerFactory.newInstance();
1.55 + }
1.56
1.57 - /**
1.58 - * Nízkoúrovňová odesílací metoda, která už nekontroluje limit příjemců.
1.59 - * Pokud se nevejdou do limitu SMTP serveru, vyhazuje výjimku.
1.60 - *
1.61 - * TODO: lepší to bude nestaické - nastavení si vyžádat v konstruktoru
1.62 - */
1.63 - private void odesliSMTP(HromadnaZprava zprava) throws MessagingException {
1.64 + public void setNastaveni(Nastaveni nastaveni) {
1.65 + this.nastaveni = nastaveni;
1.66 + }
1.67
1.68 - if (zkontrolujZpravu(zprava) && zkontrolujNastaveni(nastaveni)) {
1.69 + /**
1.70 + * Nízkoúrovňová odesílací metoda, která už nekontroluje limit příjemců.
1.71 + * Pokud se nevejdou do limitu SMTP serveru, vyhazuje výjimku.
1.72 + *
1.73 + * TODO: lepší to bude nestaické - nastavení si vyžádat v konstruktoru
1.74 + */
1.75 + private void odesliSMTP(HromadnaZprava zprava) throws MessagingException {
1.76
1.77 - /** Inicializace SMTP */
1.78 - Properties smtpVlastnosti = System.getProperties();
1.79 - smtpVlastnosti.put("mail.smtp.host", nastaveni.getPostovniServer());
1.80 - smtpVlastnosti.put("mail.smtp.port", String.valueOf(nastaveni.getPostovniPort()));
1.81 + if (zkontrolujZpravu(zprava) && zkontrolujNastaveni(nastaveni)) {
1.82
1.83 - if (nastaveni.getPostovniPort() == 465) {
1.84 - if (new File(nastaveni.getCestaKCertifikatum()).exists()) {
1.85 - System.setProperty("javax.net.ssl.trustStore", nastaveni.getCestaKCertifikatum());
1.86 - log.log(Level.INFO, "Používám vlastní důvěryhodné certifikáty: {0}", nastaveni.getCestaKCertifikatum());
1.87 - }
1.88 - /** TODO: neřídit se číslem portu, ale přidat příznak pro šifrování */
1.89 - smtpVlastnosti.put("mail.smtp.starttls.enable", "true");
1.90 - smtpVlastnosti.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
1.91 - smtpVlastnosti.put("mail.smtp.socketFactory.port", String.valueOf(nastaveni.getPostovniPort()));
1.92 - smtpVlastnosti.put("mail.smtp.socketFactory.fallback", "false");
1.93 - /**
1.94 - * NAHRÁNÍ CERTIFIKÁTU:
1.95 - * 1) stáhneme si certifikát (---BEGIN CERTIFICATE---) a uložíme do vse_ca.cer
1.96 - * 2) keytool -importcert -file vse_ca.cer -keystore DuveryhodneCertifikaty.keystore -storepass "changeit"
1.97 - * Pokud daný soubor existuje, program ho používá, pokud ne, používá certifikáty uložené v systému (Javovské).
1.98 - */
1.99 - }
1.100 + /** Inicializace SMTP */
1.101 + Properties smtpVlastnosti = System.getProperties();
1.102 + smtpVlastnosti.put("mail.smtp.host", nastaveni.getPostovniServer());
1.103 + smtpVlastnosti.put("mail.smtp.port", String.valueOf(nastaveni.getPostovniPort()));
1.104
1.105 - PostakuvHeslovnik heslovnik = new PostakuvHeslovnik();
1.106 - if (nastaveni.getPostovniJmeno() != null && nastaveni.getPostovniJmeno().length() > 0) {
1.107 - heslovnik.setJmenoHeslo(nastaveni.getPostovniJmeno(), nastaveni.getPostovniHeslo());
1.108 - smtpVlastnosti.put("mail.smtp.auth", "true");
1.109 - log.log(Level.FINEST, "Používám pro SMTP jméno a heslo");
1.110 - }
1.111 + if (nastaveni.getPostovniPort() == 465) {
1.112 + if (new File(nastaveni.getCestaKCertifikatum()).exists()) {
1.113 + System.setProperty("javax.net.ssl.trustStore", nastaveni.getCestaKCertifikatum());
1.114 + log.log(Level.INFO, "Používám vlastní důvěryhodné certifikáty: {0}", nastaveni.getCestaKCertifikatum());
1.115 + }
1.116 + /** TODO: neřídit se číslem portu, ale přidat příznak pro šifrování */
1.117 + smtpVlastnosti.put("mail.smtp.starttls.enable", "true");
1.118 + smtpVlastnosti.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
1.119 + smtpVlastnosti.put("mail.smtp.socketFactory.port", String.valueOf(nastaveni.getPostovniPort()));
1.120 + smtpVlastnosti.put("mail.smtp.socketFactory.fallback", "false");
1.121 + /**
1.122 + * NAHRÁNÍ CERTIFIKÁTU:
1.123 + * 1) stáhneme si certifikát (---BEGIN CERTIFICATE---) a uložíme do vse_ca.cer
1.124 + * 2) keytool -importcert -file vse_ca.cer -keystore DuveryhodneCertifikaty.keystore
1.125 + * -storepass "changeit"
1.126 + * Pokud daný soubor existuje, program ho používá, pokud ne, používá certifikáty
1.127 + * uložené v systému (Javovské).
1.128 + */
1.129 + }
1.130
1.131 - Session smtpRelace = Session.getInstance(smtpVlastnosti, heslovnik);
1.132 + PostakuvHeslovnik heslovnik = new PostakuvHeslovnik();
1.133 + if (nastaveni.getPostovniJmeno() != null && nastaveni.getPostovniJmeno().length() > 0) {
1.134 + heslovnik.setJmenoHeslo(nastaveni.getPostovniJmeno(), nastaveni.getPostovniHeslo());
1.135 + smtpVlastnosti.put("mail.smtp.auth", "true");
1.136 + log.log(Level.FINEST, "Používám pro SMTP jméno a heslo");
1.137 + }
1.138
1.139 - smtpRelace.setDebug(true);
1.140 - smtpRelace.setDebugOut(System.out);
1.141 + Session smtpRelace = Session.getInstance(smtpVlastnosti, heslovnik);
1.142
1.143 - /** Sestavení zprávy */
1.144 - MimeMessage mimeZprava = new MimeMessage(smtpRelace);
1.145 - mimeZprava.setFrom(zprava.getOdesilatel());
1.146 - if (zprava.getOdpovedetKomu() != null) {
1.147 - Address[] odpovedetKomu = {zprava.getOdpovedetKomu()};
1.148 - mimeZprava.setReplyTo(odpovedetKomu);
1.149 - }
1.150 - naplnPrijemce(mimeZprava, zprava);
1.151 - mimeZprava.setSubject(zprava.getPredmet());
1.152 - mimeZprava.setHeader("User-Agent", "https://frantovo.cz/projekty/SuperPostak/");
1.153 - mimeZprava.setHeader("Precedence", "bulk");
1.154 - if (zprava.isFormatHTML()) {
1.155 - mimeZprava.setText(zprava.getText(), "UTF-8", "html");
1.156 - } else {
1.157 - mimeZprava.setText(zprava.getText(), "UTF-8");
1.158 - }
1.159 + smtpRelace.setDebug(true);
1.160 + smtpRelace.setDebugOut(System.out);
1.161
1.162 - /** Vlastní odeslání */
1.163 - Transport.send(mimeZprava);
1.164 + /** Sestavení zprávy */
1.165 + MimeMessage mimeZprava = new MimeMessage(smtpRelace);
1.166 + mimeZprava.setFrom(zprava.getOdesilatel());
1.167 + if (zprava.getOdpovedetKomu() != null) {
1.168 + Address[] odpovedetKomu = {zprava.getOdpovedetKomu()};
1.169 + mimeZprava.setReplyTo(odpovedetKomu);
1.170 + }
1.171 + naplnPrijemce(mimeZprava, zprava);
1.172 + mimeZprava.setSubject(zprava.getPredmet());
1.173 + mimeZprava.setHeader("User-Agent", "https://frantovo.cz/projekty/SuperPostak/");
1.174 + mimeZprava.setHeader("Precedence", "bulk");
1.175
1.176 - } else {
1.177 - MessagingException e = new MessagingException("Zpráva nebo nastavení jsou nevyhovující");
1.178 - log.log(Level.SEVERE, null, e);
1.179 - throw e;
1.180 - }
1.181 + switch (zprava.getFormát()) {
1.182 + case PROSTÝ_TEXT:
1.183 + mimeZprava.setText(zprava.getText(), KÓDOVÁNÍ);
1.184 + break;
1.185 + case XHTML:
1.186 + mimeZprava.setText(zprava.getText(), KÓDOVÁNÍ, "html");
1.187 + break;
1.188 + case OBOJE:
1.189 + MimeMultipart multipart = new MimeMultipart("alternative");
1.190 + mimeZprava.setContent(multipart);
1.191
1.192 - }
1.193 + MimeBodyPart textováČást = new MimeBodyPart();
1.194 + MimeBodyPart xhtmlČást = new MimeBodyPart();
1.195
1.196 - /**
1.197 - * Nastaví zprávě (MimeMessage) všechny příjemce, které najde ve zprávě a nastavení.
1.198 - * Respektuje typy příjemců: komu, kopie, skrytá kopie.
1.199 - */
1.200 - private static void naplnPrijemce(MimeMessage mimeZprava, HromadnaZprava zprava) throws MessagingException {
1.201 - /**
1.202 - * Příjemci se budou definovat pouze ve zprávě.
1.203 - * Z nastavení se brát nebudou (výchozí příjemci už ve zprávě budou).
1.204 - */
1.205 - ArrayList<InternetAddressKomu> prijemci = zprava.getPrijemci();
1.206 - for (InternetAddressKomu komu : prijemci) {
1.207 - if (zkontrolujAdresu(komu)) {
1.208 - mimeZprava.addRecipient(komu.getTyp(), komu);
1.209 - }
1.210 - }
1.211 - }
1.212 + textováČást.setText(xhtmlNaProstýText(zprava.getText()), KÓDOVÁNÍ);
1.213 + xhtmlČást.setContent(zprava.getText(), "text/html; charset=" + KÓDOVÁNÍ);
1.214
1.215 - /** Vypíše do logu seznam příjemců */
1.216 - public static void vypisPrijemce(Collection<InternetAddressKomu> prijemci) {
1.217 - for (InternetAddressKomu p : prijemci) {
1.218 - log.log(Level.INFO, p.toString());
1.219 - }
1.220 - }
1.221 + multipart.addBodyPart(textováČást);
1.222 + multipart.addBodyPart(xhtmlČást);
1.223 + break;
1.224 + }
1.225
1.226 - /** Veřejná odesílací metoda.
1.227 - * Postará se o rozdělení zpráv na dílčí (které se vejdou do limitu příjemců)
1.228 - */
1.229 - public void odesli(HromadnaZprava zprava) throws MessagingException {
1.230 - Collection<HromadnaZprava> zpravy = zprava.getDilciZpravy(nastaveni.getLimitZprav());
1.231 + /** Vlastní odeslání */
1.232 + Transport.send(mimeZprava);
1.233
1.234 - for (HromadnaZprava dilciZprava : zpravy) {
1.235 - odesliSMTP(dilciZprava);
1.236 - }
1.237 - }
1.238 + } else {
1.239 + MessagingException e = new MessagingException("Zpráva nebo nastavení jsou nevyhovující");
1.240 + log.log(Level.SEVERE, null, e);
1.241 + throw e;
1.242 + }
1.243
1.244 - private static boolean zkontrolujAdresu(InternetAddressKomu a) {
1.245 - if (a.getTyp() == null) {
1.246 - log.log(Level.WARNING, "Neplatná adresa (typ): {0}", a.getAddress());
1.247 - return false;
1.248 - }
1.249 + }
1.250
1.251 - if (a.getAddress() == null || a.getAddress().length() < 1) {
1.252 - log.log(Level.WARNING, "Neplatná adresa (address): {0}", a.getPersonal());
1.253 - return false;
1.254 - }
1.255 + private String xhtmlNaProstýText(String xhtml) throws MessagingException {
1.256 + try {
1.257 + Transformer textTransformer = transformerFactory.newTransformer(new StreamSource(getClass().getClassLoader().getResourceAsStream("cz/frantovo/postak/odchozíEmailText.xsl")));
1.258
1.259 - if (!zkontrolujAdresu(a.getAddress())) {
1.260 - log.log(Level.WARNING, "Adresa nevyhovuje regulárnímu výrazu: {0}", a.getAddress());
1.261 - return false;
1.262 - }
1.263 + StringReader input = new StringReader(xhtml);
1.264 + StringWriter output = new StringWriter(xhtml.length());
1.265 + textTransformer.transform(new StreamSource(input), new StreamResult(output));
1.266
1.267 - return true;
1.268 - }
1.269 + return output.toString();
1.270 + } catch (Exception e) {
1.271 + throw new MessagingException("Chyba při XSL transformaci zprávy na prostý text.", e);
1.272 + }
1.273 + }
1.274
1.275 - /** Zkontroluje e-mailovou adresu pomocí regulárního výrazu. */
1.276 - public static boolean zkontrolujAdresu(String adresa) {
1.277 - return adresa != null && Pattern.matches(REGULARNI_EMAIL, adresa);
1.278 - }
1.279 + /**
1.280 + * Nastaví zprávě (MimeMessage) všechny příjemce, které najde ve zprávě a nastavení.
1.281 + * Respektuje typy příjemců: komu, kopie, skrytá kopie.
1.282 + */
1.283 + private static void naplnPrijemce(MimeMessage mimeZprava, HromadnaZprava zprava) throws MessagingException {
1.284 + /**
1.285 + * Příjemci se budou definovat pouze ve zprávě.
1.286 + * Z nastavení se brát nebudou (výchozí příjemci už ve zprávě budou).
1.287 + */
1.288 + ArrayList<InternetAddressKomu> prijemci = zprava.getPrijemci();
1.289 + for (InternetAddressKomu komu : prijemci) {
1.290 + if (zkontrolujAdresu(komu)) {
1.291 + mimeZprava.addRecipient(komu.getTyp(), komu);
1.292 + }
1.293 + }
1.294 + }
1.295
1.296 - /** @return Vrací true, pokud je zpráva v pořádku */
1.297 - private static boolean zkontrolujZpravu(HromadnaZprava z) {
1.298 + /** Vypíše do logu seznam příjemců */
1.299 + public static void vypisPrijemce(Collection<InternetAddressKomu> prijemci) {
1.300 + for (InternetAddressKomu p : prijemci) {
1.301 + log.log(Level.INFO, p.toString());
1.302 + }
1.303 + }
1.304
1.305 - if (z.getPrijemci() == null) {
1.306 - log.log(Level.WARNING, "getPrijemci() == null");
1.307 - return false;
1.308 - }
1.309 + /** Veřejná odesílací metoda.
1.310 + * Postará se o rozdělení zpráv na dílčí (které se vejdou do limitu příjemců)
1.311 + */
1.312 + public void odesli(HromadnaZprava zprava) throws MessagingException {
1.313 + Collection<HromadnaZprava> zpravy = zprava.getDilciZpravy(nastaveni.getLimitZprav());
1.314
1.315 - if (z.getPrijemci().size() < 1) {
1.316 - log.log(Level.WARNING, "getPrijemci().size() < 1");
1.317 - return false;
1.318 - }
1.319 + for (HromadnaZprava dilciZprava : zpravy) {
1.320 + odesliSMTP(dilciZprava);
1.321 + }
1.322 + }
1.323
1.324 - if (z.getOdesilatel() == null) {
1.325 - log.log(Level.WARNING, "getOdesilatel() == null");
1.326 - return false;
1.327 - }
1.328 + private static boolean zkontrolujAdresu(InternetAddressKomu a) {
1.329 + if (a.getTyp() == null) {
1.330 + log.log(Level.WARNING, "Neplatná adresa (typ): {0}", a.getAddress());
1.331 + return false;
1.332 + }
1.333
1.334 - if (z.getPredmet() == null) {
1.335 - log.log(Level.WARNING, "getPredmet() == null");
1.336 - return false;
1.337 - }
1.338 + if (a.getAddress() == null || a.getAddress().length() < 1) {
1.339 + log.log(Level.WARNING, "Neplatná adresa (address): {0}", a.getPersonal());
1.340 + return false;
1.341 + }
1.342
1.343 - return true;
1.344 - }
1.345 + if (!zkontrolujAdresu(a.getAddress())) {
1.346 + log.log(Level.WARNING, "Adresa nevyhovuje regulárnímu výrazu: {0}", a.getAddress());
1.347 + return false;
1.348 + }
1.349
1.350 - private static boolean zkontrolujNastaveni(Nastaveni n) {
1.351 + return true;
1.352 + }
1.353
1.354 - if (n.getPostovniServer() == null || n.getPostovniServer().length() < 1) {
1.355 - return false;
1.356 - }
1.357 + /** Zkontroluje e-mailovou adresu pomocí regulárního výrazu. */
1.358 + public static boolean zkontrolujAdresu(String adresa) {
1.359 + return adresa != null && Pattern.matches(REGULARNI_EMAIL, adresa);
1.360 + }
1.361
1.362 - return true;
1.363 - }
1.364 + /** @return Vrací true, pokud je zpráva v pořádku */
1.365 + private static boolean zkontrolujZpravu(HromadnaZprava z) {
1.366
1.367 - /** Slouží k uložení jména a hesla pro SMTP */
1.368 - private static class PostakuvHeslovnik extends Authenticator {
1.369 + if (z.getPrijemci() == null) {
1.370 + log.log(Level.WARNING, "getPrijemci() == null");
1.371 + return false;
1.372 + }
1.373
1.374 - private String jmeno = "user";
1.375 - private char[] heslo = "luser".toCharArray();
1.376 + if (z.getPrijemci().size() < 1) {
1.377 + log.log(Level.WARNING, "getPrijemci().size() < 1");
1.378 + return false;
1.379 + }
1.380
1.381 - @Override
1.382 - public PasswordAuthentication getPasswordAuthentication() {
1.383 - return new PasswordAuthentication(jmeno, String.valueOf(heslo));
1.384 - }
1.385 + if (z.getOdesilatel() == null) {
1.386 + log.log(Level.WARNING, "getOdesilatel() == null");
1.387 + return false;
1.388 + }
1.389
1.390 - public void setJmenoHeslo(String jmeno, char[] heslo) {
1.391 - this.jmeno = jmeno;
1.392 - this.heslo = heslo;
1.393 - }
1.394 - }
1.395 + if (z.getPredmet() == null) {
1.396 + log.log(Level.WARNING, "getPredmet() == null");
1.397 + return false;
1.398 + }
1.399 +
1.400 + return true;
1.401 + }
1.402 +
1.403 + private static boolean zkontrolujNastaveni(Nastaveni n) {
1.404 +
1.405 + if (n.getPostovniServer() == null || n.getPostovniServer().length() < 1) {
1.406 + return false;
1.407 + }
1.408 +
1.409 + return true;
1.410 + }
1.411 +
1.412 + /** Slouží k uložení jména a hesla pro SMTP */
1.413 + private static class PostakuvHeslovnik extends Authenticator {
1.414 +
1.415 + private String jmeno = "user";
1.416 + private char[] heslo = "luser".toCharArray();
1.417 +
1.418 + @Override
1.419 + public PasswordAuthentication getPasswordAuthentication() {
1.420 + return new PasswordAuthentication(jmeno, String.valueOf(heslo));
1.421 + }
1.422 +
1.423 + public void setJmenoHeslo(String jmeno, char[] heslo) {
1.424 + this.jmeno = jmeno;
1.425 + this.heslo = heslo;
1.426 + }
1.427 + }
1.428 }