Show
Ignore:
Timestamp:
04/23/12 00:24:31 (12 years ago)
Author:
František Kučera <franta-hg@…>
Branch:
default
Tags:
v1.1
Message:

#4 multipart/alternative zprávy: XHTML + prostý text (generuje se pomocí XSLT)

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • java/Postak/src/cz/frantovo/postak/Postak.java

    r11 r17  
    22 
    33import java.io.File; 
     4import java.io.StringReader; 
     5import java.io.StringWriter; 
    46import java.util.ArrayList; 
    57import java.util.Collection; 
     
    1416import javax.mail.Session; 
    1517import javax.mail.Transport; 
     18import javax.mail.internet.MimeBodyPart; 
    1619import javax.mail.internet.MimeMessage; 
     20import javax.mail.internet.MimeMultipart; 
     21import javax.xml.transform.Transformer; 
     22import javax.xml.transform.TransformerFactory; 
     23import javax.xml.transform.stream.StreamResult; 
     24import javax.xml.transform.stream.StreamSource; 
    1725 
    1826/** 
    1927 * Odešle hromadnou zprávu pomocí SMTP. 
    20  *  
     28 * 
    2129 * @author fiki 
    2230 */ 
    23 public class Postak {    
    24      
    25     private static final Logger log = Logger.getLogger(Postak.class.getName()); 
    26     /** Regulární výraz pro správnou e-mailovou adresu */ 
    27     private static String REGULARNI_EMAIL = "^[_a-zA-Z0-9\\.\\-]+@[_a-zA-Z0-9\\.\\-]+\\.[a-zA-Z]{2,4}$"; 
    28      
    29     private Nastaveni nastaveni; 
    30  
    31     public Postak(Nastaveni nastaveni) { 
    32         this.nastaveni = nastaveni; 
    33     } 
    34  
    35     public void setNastaveni(Nastaveni nastaveni) { 
    36         this.nastaveni = nastaveni; 
    37     } 
    38  
    39     /**  
    40      * Nízkoúrovňová odesílací metoda, která už nekontroluje limit příjemců. 
    41      * Pokud se nevejdou do limitu SMTP serveru, vyhazuje výjimku. 
    42      *  
    43      * TODO: lepší to bude nestaické - nastavení si vyžádat v konstruktoru 
    44      */ 
    45     private void odesliSMTP(HromadnaZprava zprava) throws MessagingException { 
    46  
    47         if (zkontrolujZpravu(zprava) && zkontrolujNastaveni(nastaveni)) { 
    48  
    49             /** Inicializace SMTP */ 
    50             Properties smtpVlastnosti = System.getProperties(); 
    51             smtpVlastnosti.put("mail.smtp.host", nastaveni.getPostovniServer()); 
    52             smtpVlastnosti.put("mail.smtp.port", String.valueOf(nastaveni.getPostovniPort())); 
    53  
    54             if (nastaveni.getPostovniPort() == 465) { 
    55                 if (new File(nastaveni.getCestaKCertifikatum()).exists()) { 
    56                     System.setProperty("javax.net.ssl.trustStore", nastaveni.getCestaKCertifikatum()); 
    57                     log.log(Level.INFO, "Používám vlastní důvěryhodné certifikáty: {0}", nastaveni.getCestaKCertifikatum()); 
    58                 } 
    59                 /** TODO: neřídit se číslem portu, ale přidat příznak pro šifrování */ 
    60                 smtpVlastnosti.put("mail.smtp.starttls.enable", "true"); 
    61                 smtpVlastnosti.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); 
    62                 smtpVlastnosti.put("mail.smtp.socketFactory.port", String.valueOf(nastaveni.getPostovniPort())); 
    63                 smtpVlastnosti.put("mail.smtp.socketFactory.fallback", "false"); 
    64             /** 
    65              * NAHRÁNÍ CERTIFIKÁTU: 
    66              * 1) stáhneme si certifikát (---BEGIN CERTIFICATE---) a uložíme do vse_ca.cer              
    67              * 2) keytool -importcert -file vse_ca.cer -keystore DuveryhodneCertifikaty.keystore -storepass "changeit" 
    68              * Pokud daný soubor existuje, program ho používá, pokud ne, používá certifikáty uložené v systému (Javovské). 
    69              */ 
    70             } 
    71  
    72             PostakuvHeslovnik heslovnik = new PostakuvHeslovnik(); 
    73             if (nastaveni.getPostovniJmeno() != null && nastaveni.getPostovniJmeno().length() > 0) { 
    74                 heslovnik.setJmenoHeslo(nastaveni.getPostovniJmeno(), nastaveni.getPostovniHeslo()); 
    75                 smtpVlastnosti.put("mail.smtp.auth", "true"); 
    76                 log.log(Level.FINEST, "Používám pro SMTP jméno a heslo"); 
    77             } 
    78  
    79             Session smtpRelace = Session.getInstance(smtpVlastnosti, heslovnik); 
    80  
    81             smtpRelace.setDebug(true); 
    82             smtpRelace.setDebugOut(System.out); 
    83  
    84             /** Sestavení zprávy */ 
    85             MimeMessage mimeZprava = new MimeMessage(smtpRelace); 
    86             mimeZprava.setFrom(zprava.getOdesilatel()); 
    87             if (zprava.getOdpovedetKomu() != null) { 
    88                 Address[] odpovedetKomu = {zprava.getOdpovedetKomu()}; 
    89                 mimeZprava.setReplyTo(odpovedetKomu); 
    90             } 
    91             naplnPrijemce(mimeZprava, zprava); 
    92             mimeZprava.setSubject(zprava.getPredmet()); 
    93             mimeZprava.setHeader("User-Agent", "https://frantovo.cz/projekty/SuperPostak/"); 
    94             mimeZprava.setHeader("Precedence", "bulk"); 
    95             if (zprava.isFormatHTML()) { 
    96                 mimeZprava.setText(zprava.getText(), "UTF-8", "html"); 
    97             } else { 
    98                 mimeZprava.setText(zprava.getText(), "UTF-8"); 
    99             } 
    100  
    101             /** Vlastní odeslání */ 
    102             Transport.send(mimeZprava); 
    103  
    104         } else { 
    105             MessagingException e = new MessagingException("Zpráva nebo nastavení jsou nevyhovující"); 
    106             log.log(Level.SEVERE, null, e); 
    107             throw e; 
    108         } 
    109  
    110     } 
    111  
    112     /** 
    113      * Nastaví zprávě (MimeMessage) všechny příjemce, které najde ve zprávě a nastavení. 
    114      * Respektuje typy příjemců: komu, kopie, skrytá kopie.     
    115      */ 
    116     private static void naplnPrijemce(MimeMessage mimeZprava, HromadnaZprava zprava) throws MessagingException { 
    117         /**  
    118          * Příjemci se budou definovat pouze ve zprávě. 
    119          * Z nastavení se brát nebudou (výchozí příjemci už ve zprávě budou). 
    120          */ 
    121         ArrayList<InternetAddressKomu> prijemci = zprava.getPrijemci(); 
    122         for (InternetAddressKomu komu : prijemci) { 
    123             if (zkontrolujAdresu(komu)) { 
    124                 mimeZprava.addRecipient(komu.getTyp(), komu); 
    125             } 
    126         } 
    127     } 
    128  
    129     /** Vypíše do logu seznam příjemců */ 
    130     public static void vypisPrijemce(Collection<InternetAddressKomu> prijemci) { 
    131         for (InternetAddressKomu p : prijemci) { 
    132             log.log(Level.INFO, p.toString()); 
    133         } 
    134     } 
    135  
    136     /** Veřejná odesílací metoda. 
    137      * Postará se o rozdělení zpráv na dílčí (které se vejdou do limitu příjemců) 
    138      */ 
    139     public void odesli(HromadnaZprava zprava) throws MessagingException { 
    140         Collection<HromadnaZprava> zpravy = zprava.getDilciZpravy(nastaveni.getLimitZprav()); 
    141  
    142         for (HromadnaZprava dilciZprava : zpravy) { 
    143             odesliSMTP(dilciZprava); 
    144         } 
    145     } 
    146  
    147     private static boolean zkontrolujAdresu(InternetAddressKomu a) { 
    148         if (a.getTyp() == null) { 
    149             log.log(Level.WARNING, "Neplatná adresa (typ): {0}", a.getAddress()); 
    150             return false; 
    151         } 
    152  
    153         if (a.getAddress() == null || a.getAddress().length() < 1) { 
    154             log.log(Level.WARNING, "Neplatná adresa (address): {0}", a.getPersonal()); 
    155             return false; 
    156         } 
    157  
    158         if (!zkontrolujAdresu(a.getAddress())) { 
    159             log.log(Level.WARNING, "Adresa nevyhovuje regulárnímu výrazu: {0}", a.getAddress()); 
    160             return false; 
    161         } 
    162  
    163         return true; 
    164     } 
    165  
    166     /** Zkontroluje e-mailovou adresu pomocí regulárního výrazu. */ 
    167     public static boolean zkontrolujAdresu(String adresa) { 
    168         return adresa != null && Pattern.matches(REGULARNI_EMAIL, adresa); 
    169     } 
    170  
    171     /** @return  Vrací true, pokud je zpráva v pořádku */ 
    172     private static boolean zkontrolujZpravu(HromadnaZprava z) { 
    173  
    174         if (z.getPrijemci() == null) { 
    175             log.log(Level.WARNING, "getPrijemci() == null"); 
    176             return false; 
    177         } 
    178  
    179         if (z.getPrijemci().size() < 1) { 
    180             log.log(Level.WARNING, "getPrijemci().size() < 1"); 
    181             return false; 
    182         } 
    183  
    184         if (z.getOdesilatel() == null) { 
    185             log.log(Level.WARNING, "getOdesilatel() == null"); 
    186             return false; 
    187         } 
    188  
    189         if (z.getPredmet() == null) { 
    190             log.log(Level.WARNING, "getPredmet() == null"); 
    191             return false; 
    192         } 
    193  
    194         return true; 
    195     } 
    196  
    197     private static boolean zkontrolujNastaveni(Nastaveni n) { 
    198  
    199         if (n.getPostovniServer() == null || n.getPostovniServer().length() < 1) { 
    200             return false; 
    201         } 
    202  
    203         return true; 
    204     } 
    205  
    206     /** Slouží k uložení jména a hesla pro SMTP */ 
    207     private static class PostakuvHeslovnik extends Authenticator { 
    208  
    209         private String jmeno = "user"; 
    210         private char[] heslo = "luser".toCharArray(); 
    211  
    212         @Override 
    213         public PasswordAuthentication getPasswordAuthentication() { 
    214             return new PasswordAuthentication(jmeno, String.valueOf(heslo)); 
    215         } 
    216  
    217         public void setJmenoHeslo(String jmeno, char[] heslo) { 
    218             this.jmeno = jmeno; 
    219             this.heslo = heslo; 
    220         } 
    221     } 
     31public class Postak { 
     32 
     33        private static final String KÓDOVÁNÍ = "UTF-8"; 
     34        private static final Logger log = Logger.getLogger(Postak.class.getName()); 
     35        /** Regulární výraz pro správnou e-mailovou adresu */ 
     36        private static String REGULARNI_EMAIL = "^[_a-zA-Z0-9\\.\\-]+@[_a-zA-Z0-9\\.\\-]+\\.[a-zA-Z]{2,4}$"; 
     37        private Nastaveni nastaveni; 
     38        private TransformerFactory transformerFactory; 
     39 
     40        public Postak(Nastaveni nastaveni) { 
     41                this.nastaveni = nastaveni; 
     42                transformerFactory = TransformerFactory.newInstance(); 
     43        } 
     44 
     45        public void setNastaveni(Nastaveni nastaveni) { 
     46                this.nastaveni = nastaveni; 
     47        } 
     48 
     49        /** 
     50         * Nízkoúrovňová odesílací metoda, která už nekontroluje limit příjemců. 
     51         * Pokud se nevejdou do limitu SMTP serveru, vyhazuje výjimku. 
     52         * 
     53         * TODO: lepší to bude nestaické - nastavení si vyžádat v konstruktoru 
     54         */ 
     55        private void odesliSMTP(HromadnaZprava zprava) throws MessagingException { 
     56 
     57                if (zkontrolujZpravu(zprava) && zkontrolujNastaveni(nastaveni)) { 
     58 
     59                        /** Inicializace SMTP */ 
     60                        Properties smtpVlastnosti = System.getProperties(); 
     61                        smtpVlastnosti.put("mail.smtp.host", nastaveni.getPostovniServer()); 
     62                        smtpVlastnosti.put("mail.smtp.port", String.valueOf(nastaveni.getPostovniPort())); 
     63 
     64                        if (nastaveni.getPostovniPort() == 465) { 
     65                                if (new File(nastaveni.getCestaKCertifikatum()).exists()) { 
     66                                        System.setProperty("javax.net.ssl.trustStore", nastaveni.getCestaKCertifikatum()); 
     67                                        log.log(Level.INFO, "Používám vlastní důvěryhodné certifikáty: {0}", nastaveni.getCestaKCertifikatum()); 
     68                                } 
     69                                /** TODO: neřídit se číslem portu, ale přidat příznak pro šifrování */ 
     70                                smtpVlastnosti.put("mail.smtp.starttls.enable", "true"); 
     71                                smtpVlastnosti.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); 
     72                                smtpVlastnosti.put("mail.smtp.socketFactory.port", String.valueOf(nastaveni.getPostovniPort())); 
     73                                smtpVlastnosti.put("mail.smtp.socketFactory.fallback", "false"); 
     74                                /** 
     75                                 * NAHRÁNÍ CERTIFIKÁTU: 
     76                                 * 1) stáhneme si certifikát (---BEGIN CERTIFICATE---) a uložíme do vse_ca.cer 
     77                                 * 2) keytool -importcert -file vse_ca.cer -keystore DuveryhodneCertifikaty.keystore 
     78                                 * -storepass "changeit" 
     79                                 * Pokud daný soubor existuje, program ho používá, pokud ne, používá certifikáty 
     80                                 * uložené v systému (Javovské). 
     81                                 */ 
     82                        } 
     83 
     84                        PostakuvHeslovnik heslovnik = new PostakuvHeslovnik(); 
     85                        if (nastaveni.getPostovniJmeno() != null && nastaveni.getPostovniJmeno().length() > 0) { 
     86                                heslovnik.setJmenoHeslo(nastaveni.getPostovniJmeno(), nastaveni.getPostovniHeslo()); 
     87                                smtpVlastnosti.put("mail.smtp.auth", "true"); 
     88                                log.log(Level.FINEST, "Používám pro SMTP jméno a heslo"); 
     89                        } 
     90 
     91                        Session smtpRelace = Session.getInstance(smtpVlastnosti, heslovnik); 
     92 
     93                        smtpRelace.setDebug(true); 
     94                        smtpRelace.setDebugOut(System.out); 
     95 
     96                        /** Sestavení zprávy */ 
     97                        MimeMessage mimeZprava = new MimeMessage(smtpRelace); 
     98                        mimeZprava.setFrom(zprava.getOdesilatel()); 
     99                        if (zprava.getOdpovedetKomu() != null) { 
     100                                Address[] odpovedetKomu = {zprava.getOdpovedetKomu()}; 
     101                                mimeZprava.setReplyTo(odpovedetKomu); 
     102                        } 
     103                        naplnPrijemce(mimeZprava, zprava); 
     104                        mimeZprava.setSubject(zprava.getPredmet()); 
     105                        mimeZprava.setHeader("User-Agent", "https://frantovo.cz/projekty/SuperPostak/"); 
     106                        mimeZprava.setHeader("Precedence", "bulk"); 
     107 
     108                        switch (zprava.getFormát()) { 
     109                                case PROSTÝ_TEXT: 
     110                                        mimeZprava.setText(zprava.getText(), KÓDOVÁNÍ); 
     111                                        break; 
     112                                case XHTML: 
     113                                        mimeZprava.setText(zprava.getText(), KÓDOVÁNÍ, "html"); 
     114                                        break; 
     115                                case OBOJE: 
     116                                        MimeMultipart multipart = new MimeMultipart("alternative"); 
     117                                        mimeZprava.setContent(multipart); 
     118 
     119                                        MimeBodyPart textováČást = new MimeBodyPart(); 
     120                                        MimeBodyPart xhtmlČást = new MimeBodyPart(); 
     121 
     122                                        textováČást.setText(xhtmlNaProstýText(zprava.getText()), KÓDOVÁNÍ); 
     123                                        xhtmlČást.setContent(zprava.getText(), "text/html; charset=" + KÓDOVÁNÍ); 
     124 
     125                                        multipart.addBodyPart(textováČást); 
     126                                        multipart.addBodyPart(xhtmlČást); 
     127                                        break; 
     128                        } 
     129 
     130                        /** Vlastní odeslání */ 
     131                        Transport.send(mimeZprava); 
     132 
     133                } else { 
     134                        MessagingException e = new MessagingException("Zpráva nebo nastavení jsou nevyhovující"); 
     135                        log.log(Level.SEVERE, null, e); 
     136                        throw e; 
     137                } 
     138 
     139        } 
     140 
     141        private String xhtmlNaProstýText(String xhtml) throws MessagingException { 
     142                try { 
     143                        Transformer textTransformer = transformerFactory.newTransformer(new StreamSource(getClass().getClassLoader().getResourceAsStream("cz/frantovo/postak/odchozíEmailText.xsl"))); 
     144 
     145                        StringReader input = new StringReader(xhtml); 
     146                        StringWriter output = new StringWriter(xhtml.length()); 
     147                        textTransformer.transform(new StreamSource(input), new StreamResult(output)); 
     148 
     149                        return output.toString(); 
     150                } catch (Exception e) { 
     151                        throw new MessagingException("Chyba při XSL transformaci zprávy na prostý text.", e); 
     152                } 
     153        } 
     154 
     155        /** 
     156         * Nastaví zprávě (MimeMessage) všechny příjemce, které najde ve zprávě a nastavení. 
     157         * Respektuje typy příjemců: komu, kopie, skrytá kopie. 
     158         */ 
     159        private static void naplnPrijemce(MimeMessage mimeZprava, HromadnaZprava zprava) throws MessagingException { 
     160                /** 
     161                 * Příjemci se budou definovat pouze ve zprávě. 
     162                 * Z nastavení se brát nebudou (výchozí příjemci už ve zprávě budou). 
     163                 */ 
     164                ArrayList<InternetAddressKomu> prijemci = zprava.getPrijemci(); 
     165                for (InternetAddressKomu komu : prijemci) { 
     166                        if (zkontrolujAdresu(komu)) { 
     167                                mimeZprava.addRecipient(komu.getTyp(), komu); 
     168                        } 
     169                } 
     170        } 
     171 
     172        /** Vypíše do logu seznam příjemců */ 
     173        public static void vypisPrijemce(Collection<InternetAddressKomu> prijemci) { 
     174                for (InternetAddressKomu p : prijemci) { 
     175                        log.log(Level.INFO, p.toString()); 
     176                } 
     177        } 
     178 
     179        /** Veřejná odesílací metoda. 
     180         * Postará se o rozdělení zpráv na dílčí (které se vejdou do limitu příjemců) 
     181         */ 
     182        public void odesli(HromadnaZprava zprava) throws MessagingException { 
     183                Collection<HromadnaZprava> zpravy = zprava.getDilciZpravy(nastaveni.getLimitZprav()); 
     184 
     185                for (HromadnaZprava dilciZprava : zpravy) { 
     186                        odesliSMTP(dilciZprava); 
     187                } 
     188        } 
     189 
     190        private static boolean zkontrolujAdresu(InternetAddressKomu a) { 
     191                if (a.getTyp() == null) { 
     192                        log.log(Level.WARNING, "Neplatná adresa (typ): {0}", a.getAddress()); 
     193                        return false; 
     194                } 
     195 
     196                if (a.getAddress() == null || a.getAddress().length() < 1) { 
     197                        log.log(Level.WARNING, "Neplatná adresa (address): {0}", a.getPersonal()); 
     198                        return false; 
     199                } 
     200 
     201                if (!zkontrolujAdresu(a.getAddress())) { 
     202                        log.log(Level.WARNING, "Adresa nevyhovuje regulárnímu výrazu: {0}", a.getAddress()); 
     203                        return false; 
     204                } 
     205 
     206                return true; 
     207        } 
     208 
     209        /** Zkontroluje e-mailovou adresu pomocí regulárního výrazu. */ 
     210        public static boolean zkontrolujAdresu(String adresa) { 
     211                return adresa != null && Pattern.matches(REGULARNI_EMAIL, adresa); 
     212        } 
     213 
     214        /** @return Vrací true, pokud je zpráva v pořádku */ 
     215        private static boolean zkontrolujZpravu(HromadnaZprava z) { 
     216 
     217                if (z.getPrijemci() == null) { 
     218                        log.log(Level.WARNING, "getPrijemci() == null"); 
     219                        return false; 
     220                } 
     221 
     222                if (z.getPrijemci().size() < 1) { 
     223                        log.log(Level.WARNING, "getPrijemci().size() < 1"); 
     224                        return false; 
     225                } 
     226 
     227                if (z.getOdesilatel() == null) { 
     228                        log.log(Level.WARNING, "getOdesilatel() == null"); 
     229                        return false; 
     230                } 
     231 
     232                if (z.getPredmet() == null) { 
     233                        log.log(Level.WARNING, "getPredmet() == null"); 
     234                        return false; 
     235                } 
     236 
     237                return true; 
     238        } 
     239 
     240        private static boolean zkontrolujNastaveni(Nastaveni n) { 
     241 
     242                if (n.getPostovniServer() == null || n.getPostovniServer().length() < 1) { 
     243                        return false; 
     244                } 
     245 
     246                return true; 
     247        } 
     248 
     249        /** Slouží k uložení jména a hesla pro SMTP */ 
     250        private static class PostakuvHeslovnik extends Authenticator { 
     251 
     252                private String jmeno = "user"; 
     253                private char[] heslo = "luser".toCharArray(); 
     254 
     255                @Override 
     256                public PasswordAuthentication getPasswordAuthentication() { 
     257                        return new PasswordAuthentication(jmeno, String.valueOf(heslo)); 
     258                } 
     259 
     260                public void setJmenoHeslo(String jmeno, char[] heslo) { 
     261                        this.jmeno = jmeno; 
     262                        this.heslo = heslo; 
     263                } 
     264        } 
    222265}