root/java/Postak/src/cz/frantovo/postak/Postak.java @ 20:22ce4474d4a9

Revision 17:9cb46ca7e26c, 8.7 KB (checked in by František Kučera <franta-hg@…>, 13 years ago)

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

Line 
1package cz.frantovo.postak;
2
3import java.io.File;
4import java.io.StringReader;
5import java.io.StringWriter;
6import java.util.ArrayList;
7import java.util.Collection;
8import java.util.Properties;
9import java.util.logging.Level;
10import java.util.logging.Logger;
11import java.util.regex.Pattern;
12import javax.mail.Address;
13import javax.mail.Authenticator;
14import javax.mail.MessagingException;
15import javax.mail.PasswordAuthentication;
16import javax.mail.Session;
17import javax.mail.Transport;
18import javax.mail.internet.MimeBodyPart;
19import 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;
25
26/**
27 * Odešle hromadnou zprávu pomocí SMTP.
28 *
29 * @author fiki
30 */
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        }
265}
Note: See TracBrowser for help on using the browser.