package cz.frantovo.postak; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; import javax.mail.Address; import javax.mail.Authenticator; import javax.mail.MessagingException; import javax.mail.PasswordAuthentication; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.MimeMessage; /** * Odešle hromadnou zprávu pomocí SMTP. * * @author fiki */ public class Postak { private static final Logger log = Logger.getLogger(Postak.class.getName()); /** Regulární výraz pro správnou e-mailovou adresu */ private static String REGULARNI_EMAIL = "^[_a-zA-Z0-9\\.\\-]+@[_a-zA-Z0-9\\.\\-]+\\.[a-zA-Z]{2,4}$"; private Nastaveni nastaveni; public Postak(Nastaveni nastaveni) { this.nastaveni = nastaveni; } public void setNastaveni(Nastaveni nastaveni) { this.nastaveni = nastaveni; } /** * Nízkoúrovňová odesílací metoda, která už nekontroluje limit příjemců. * Pokud se nevejdou do limitu SMTP serveru, vyhazuje výjimku. * * TODO: lepší to bude nestaické - nastavení si vyžádat v konstruktoru */ private void odesliSMTP(HromadnaZprava zprava) throws MessagingException { if (zkontrolujZpravu(zprava) && zkontrolujNastaveni(nastaveni)) { /** Inicializace SMTP */ Properties smtpVlastnosti = System.getProperties(); smtpVlastnosti.put("mail.smtp.host", nastaveni.getPostovniServer()); smtpVlastnosti.put("mail.smtp.port", String.valueOf(nastaveni.getPostovniPort())); if (nastaveni.getPostovniPort() == 465) { if (new File(nastaveni.getCestaKCertifikatum()).exists()) { System.setProperty("javax.net.ssl.trustStore", nastaveni.getCestaKCertifikatum()); log.log(Level.INFO, "Používám vlastní důvěryhodné certifikáty: {0}", nastaveni.getCestaKCertifikatum()); } /** TODO: neřídit se číslem portu, ale přidat příznak pro šifrování */ smtpVlastnosti.put("mail.smtp.starttls.enable", "true"); smtpVlastnosti.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); smtpVlastnosti.put("mail.smtp.socketFactory.port", String.valueOf(nastaveni.getPostovniPort())); smtpVlastnosti.put("mail.smtp.socketFactory.fallback", "false"); /** * NAHRÁNÍ CERTIFIKÁTU: * 1) stáhneme si certifikát (---BEGIN CERTIFICATE---) a uložíme do vse_ca.cer * 2) keytool -importcert -file vse_ca.cer -keystore DuveryhodneCertifikaty.keystore -storepass "changeit" * Pokud daný soubor existuje, program ho používá, pokud ne, používá certifikáty uložené v systému (Javovské). */ } PostakuvHeslovnik heslovnik = new PostakuvHeslovnik(); if (nastaveni.getPostovniJmeno() != null && nastaveni.getPostovniJmeno().length() > 0) { heslovnik.setJmenoHeslo(nastaveni.getPostovniJmeno(), nastaveni.getPostovniHeslo()); smtpVlastnosti.put("mail.smtp.auth", "true"); log.log(Level.FINEST, "Používám pro SMTP jméno a heslo"); } Session smtpRelace = Session.getInstance(smtpVlastnosti, heslovnik); smtpRelace.setDebug(true); smtpRelace.setDebugOut(System.out); /** Sestavení zprávy */ MimeMessage mimeZprava = new MimeMessage(smtpRelace); mimeZprava.setFrom(zprava.getOdesilatel()); if (zprava.getOdpovedetKomu() != null) { Address[] odpovedetKomu = {zprava.getOdpovedetKomu()}; mimeZprava.setReplyTo(odpovedetKomu); } naplnPrijemce(mimeZprava, zprava); mimeZprava.setSubject(zprava.getPredmet()); mimeZprava.setHeader("User-Agent", "https://frantovo.cz/projekty/SuperPostak/"); mimeZprava.setHeader("Precedence", "bulk"); if (zprava.isFormatHTML()) { mimeZprava.setText(zprava.getText(), "UTF-8", "html"); } else { mimeZprava.setText(zprava.getText(), "UTF-8"); } /** Vlastní odeslání */ Transport.send(mimeZprava); } else { MessagingException e = new MessagingException("Zpráva nebo nastavení jsou nevyhovující"); log.log(Level.SEVERE, null, e); throw e; } } /** * Nastaví zprávě (MimeMessage) všechny příjemce, které najde ve zprávě a nastavení. * Respektuje typy příjemců: komu, kopie, skrytá kopie. */ private static void naplnPrijemce(MimeMessage mimeZprava, HromadnaZprava zprava) throws MessagingException { /** * Příjemci se budou definovat pouze ve zprávě. * Z nastavení se brát nebudou (výchozí příjemci už ve zprávě budou). */ ArrayList prijemci = zprava.getPrijemci(); for (InternetAddressKomu komu : prijemci) { if (zkontrolujAdresu(komu)) { mimeZprava.addRecipient(komu.getTyp(), komu); } } } /** Vypíše do logu seznam příjemců */ public static void vypisPrijemce(Collection prijemci) { for (InternetAddressKomu p : prijemci) { log.log(Level.INFO, p.toString()); } } /** Veřejná odesílací metoda. * Postará se o rozdělení zpráv na dílčí (které se vejdou do limitu příjemců) */ public void odesli(HromadnaZprava zprava) throws MessagingException { Collection zpravy = zprava.getDilciZpravy(nastaveni.getLimitZprav()); for (HromadnaZprava dilciZprava : zpravy) { odesliSMTP(dilciZprava); } } private static boolean zkontrolujAdresu(InternetAddressKomu a) { if (a.getTyp() == null) { log.log(Level.WARNING, "Neplatná adresa (typ): {0}", a.getAddress()); return false; } if (a.getAddress() == null || a.getAddress().length() < 1) { log.log(Level.WARNING, "Neplatná adresa (address): {0}", a.getPersonal()); return false; } if (!zkontrolujAdresu(a.getAddress())) { log.log(Level.WARNING, "Adresa nevyhovuje regulárnímu výrazu: {0}", a.getAddress()); return false; } return true; } /** Zkontroluje e-mailovou adresu pomocí regulárního výrazu. */ public static boolean zkontrolujAdresu(String adresa) { return adresa != null && Pattern.matches(REGULARNI_EMAIL, adresa); } /** @return Vrací true, pokud je zpráva v pořádku */ private static boolean zkontrolujZpravu(HromadnaZprava z) { if (z.getPrijemci() == null) { log.log(Level.WARNING, "getPrijemci() == null"); return false; } if (z.getPrijemci().size() < 1) { log.log(Level.WARNING, "getPrijemci().size() < 1"); return false; } if (z.getOdesilatel() == null) { log.log(Level.WARNING, "getOdesilatel() == null"); return false; } if (z.getPredmet() == null) { log.log(Level.WARNING, "getPredmet() == null"); return false; } return true; } private static boolean zkontrolujNastaveni(Nastaveni n) { if (n.getPostovniServer() == null || n.getPostovniServer().length() < 1) { return false; } return true; } /** Slouží k uložení jména a hesla pro SMTP */ private static class PostakuvHeslovnik extends Authenticator { private String jmeno = "user"; private char[] heslo = "luser".toCharArray(); @Override public PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(jmeno, String.valueOf(heslo)); } public void setJmenoHeslo(String jmeno, char[] heslo) { this.jmeno = jmeno; this.heslo = heslo; } } }