Index: java/Postak/src/cz/frantovo/postak/Postak.java
===================================================================
--- java/Postak/src/cz/frantovo/postak/Postak.java	(revision 11:c1246cbb7f4c)
+++ java/Postak/src/cz/frantovo/postak/Postak.java	(revision 17:9cb46ca7e26c)
@@ -2,4 +2,6 @@
 
 import java.io.File;
+import java.io.StringReader;
+import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -14,209 +16,250 @@
 import javax.mail.Session;
 import javax.mail.Transport;
+import javax.mail.internet.MimeBodyPart;
 import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
 
 /**
  * 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<InternetAddressKomu> 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<InternetAddressKomu> 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<HromadnaZprava> 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;
-        }
-    }
+public class Postak {
+
+	private static final String KÓDOVÁNÍ = "UTF-8";
+	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;
+	private TransformerFactory transformerFactory;
+
+	public Postak(Nastaveni nastaveni) {
+		this.nastaveni = nastaveni;
+		transformerFactory = TransformerFactory.newInstance();
+	}
+
+	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");
+
+			switch (zprava.getFormát()) {
+				case PROSTÝ_TEXT:
+					mimeZprava.setText(zprava.getText(), KÓDOVÁNÍ);
+					break;
+				case XHTML:
+					mimeZprava.setText(zprava.getText(), KÓDOVÁNÍ, "html");
+					break;
+				case OBOJE:
+					MimeMultipart multipart = new MimeMultipart("alternative");
+					mimeZprava.setContent(multipart);
+
+					MimeBodyPart textováČást = new MimeBodyPart();
+					MimeBodyPart xhtmlČást = new MimeBodyPart();
+
+					textováČást.setText(xhtmlNaProstýText(zprava.getText()), KÓDOVÁNÍ);
+					xhtmlČást.setContent(zprava.getText(), "text/html; charset=" + KÓDOVÁNÍ);
+
+					multipart.addBodyPart(textováČást);
+					multipart.addBodyPart(xhtmlČást);
+					break;
+			}
+
+			/** 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;
+		}
+
+	}
+
+	private String xhtmlNaProstýText(String xhtml) throws MessagingException {
+		try {
+			Transformer textTransformer = transformerFactory.newTransformer(new StreamSource(getClass().getClassLoader().getResourceAsStream("cz/frantovo/postak/odchozíEmailText.xsl")));
+
+			StringReader input = new StringReader(xhtml);
+			StringWriter output = new StringWriter(xhtml.length());
+			textTransformer.transform(new StreamSource(input), new StreamResult(output));
+
+			return output.toString();
+		} catch (Exception e) {
+			throw new MessagingException("Chyba při XSL transformaci zprávy na prostý text.", 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<InternetAddressKomu> 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<InternetAddressKomu> 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<HromadnaZprava> 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;
+		}
+	}
 }
