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