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