package ilarkesto.email; import ilarkesto.Servers; import ilarkesto.base.Str; import ilarkesto.base.Sys; import ilarkesto.base.Utl; import ilarkesto.base.time.DateAndTime; import ilarkesto.core.logging.Log; import ilarkesto.io.IO; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.Collection; import java.util.Date; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.Properties; import java.util.ResourceBundle; import java.util.Set; import java.util.UUID; import javax.activation.DataHandler; import javax.activation.DataSource; import javax.activation.FileDataSource; import javax.mail.Address; import javax.mail.BodyPart; import javax.mail.Flags; import javax.mail.Folder; import javax.mail.Header; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.NoSuchProviderException; import javax.mail.Part; import javax.mail.PasswordAuthentication; import javax.mail.Session; import javax.mail.Store; import javax.mail.Transport; import javax.mail.URLName; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMessage.RecipientType; import javax.mail.internet.MimeMultipart; /** * Set of static methods for sending and receiving emails */ public class Eml { public static void main(String[] args) throws Throwable { Log.setDebugEnabled(true); Sys.setFileEncoding(IO.UTF_8); Message msg = createTextMessage(createDummySession(), "aaa" + Str.UE + "aaa", "aaa" + Str.UE + "aaa", "wi@koczewski.de", "wi@koczewski.de"); OutputStream out = new FileOutputStream("g:/inbox/email-test.msg"); writeMessage(msg, out); out.close(); // Store store = getStore("imaps", "imap.googlemail.com", "witoslaw.koczewski@googlemail.com", "xxx"); // try { // Folder folder = store.getFolder("INBOX"); // folder.open(Folder.READ_ONLY); // LOG.debug("folder:", folder.getName()); // for (Message message : folder.getMessages()) { // LOG.debug(" message:", getSubject(message), "->", getContentAsText(message)); // } // } finally { // closeStore(store); // } System.exit(0); } private static final Log LOG = Log.get(Eml.class); public static final String HEADER_FROM = "From"; public static final String HEADER_MESSAGE_ID = "Message-ID"; public static final String HEADER_MESSAGE_CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding"; public static final String HEADER_MESSAGE_CONTENT_TYPE = "Content-Type"; public static final String HEADER_REPLY_TO = "Reply-To"; public static final String HEADER_IN_REPLY_TO = "In-Reply-To"; public static final String HEADER_X_MAILER = "X-Mailer"; public static final String HEADER_X_PRIORITY = "X-Priority"; public static final String HEADER_X_CONFIRM_READING_TO = "X-Confirm-Reading-To"; public static final String PROTOCOL_IMAP = "imap"; public static final String PROTOCOL_POP3 = "pop3"; public static final String PROTOCOL_SMTP = "smtp"; private static final String X_MAILER = "Witoslaw Koczewski Email-Toolbox, http://www.koczewski.de)"; private static String charset; static { setCharset(IO.ISO_LATIN_1); } public static void writeMessage(Message message, OutputStream out) { try { Enumeration<Header> enu = message.getAllHeaders(); while (enu.hasMoreElements()) { Header header = enu.nextElement(); IO.writeText(out, header.getName() + ": " + header.getValue() + "\n", charset); } IO.writeText(out, "\n", charset); IO.copyData(message.getInputStream(), out); } catch (IOException ex) { throw new RuntimeException(ex); } catch (MessagingException ex) { throw new RuntimeException(ex); } } public static Message getMessageById(String id, Folder folder) { try { for (Message message : folder.getMessages()) { if (id.equals(getMessageId(message))) return message; } } catch (MessagingException ex) { throw new RuntimeException(ex); } return null; } public static Set<String> getAttachmentFilenames(Part part) { try { Set<String> result = new HashSet<String>(); if (part.getContentType().toLowerCase().startsWith("multipart")) { MimeMultipart multipart; try { multipart = (MimeMultipart) part.getContent(); int count = multipart.getCount(); for (int i = 0; i < count; i++) { result.addAll(getAttachmentFilenames(multipart.getBodyPart(i))); } } catch (NullPointerException ex) { // part.getContent() throws NullPointerException LOG.info(ex); } } else { String filename = part.getFileName(); if (filename != null) result.add(Str.decodeQuotedPrintable(filename)); } return result; } catch (Exception ex) { throw new RuntimeException(ex); } } public static InputStream getAttachment(Part part, String filename) { try { if (filename.equals(part.getFileName())) return part.getInputStream(); if (part.getContentType().toLowerCase().startsWith("multipart")) { MimeMultipart multipart; multipart = (MimeMultipart) part.getContent(); int count = multipart.getCount(); for (int i = 0; i < count; i++) { InputStream in = getAttachment(multipart.getBodyPart(i), filename); if (in != null) return in; } } } catch (Throwable ex) { throw new RuntimeException(ex); } return null; } public static String getContentAsText(Part part) { String result = getPlainTextContent(part); if (result == null) result = Str.html2text(getHtmlTextContent(part)); return result; } public static String getHtmlTextContent(Part part) { return getTextContent(part, "html"); } public static String getPlainTextContent(Part part) { String text = getTextContent(part, "plain"); if (text == null) { text = getTextContent(part, "calendar"); } return text; } public static String getTextContent(Part part, String type) { if (part == null) return null; try { String contentType; try { contentType = part.getContentType(); } catch (Throwable t) { contentType = "unknown"; } if (contentType.toLowerCase().startsWith("text/" + type)) { // ContentType ct = new ContentType(contentType); // String charset = ct.getParameter("charset"); try { Object content = part.getContent(); if (content == null) return null; if (content instanceof String) return (String) content; if (content instanceof InputStream) { String encoding = charset; if (contentType.toLowerCase().contains("UTF")) encoding = IO.UTF_8; if (contentType.toLowerCase().contains("ISO")) encoding = IO.ISO_LATIN_1; return IO.readToString((InputStream) content, encoding); } return Utl.toStringWithType(content); } catch (UnsupportedEncodingException ex) { LOG.warn(ex); return null; } catch (IOException e) { String message = e.getMessage(); if (message != null) { if ("No content".equals(message)) { return null; } if (message.toLowerCase().startsWith("unknown encoding")) { LOG.warn(e); return null; } } throw e; } catch (Throwable t) { LOG.warn(t); return Str.getStackTrace(t); } } if (contentType.toLowerCase().startsWith("multipart")) { MimeMultipart multipart; try { multipart = (MimeMultipart) part.getContent(); } catch (NullPointerException ex) { LOG.warn(ex); return null; } int count = multipart.getCount(); for (int i = 0; i < count; i++) { BodyPart subPart = multipart.getBodyPart(i); String filename = subPart.getFileName(); if (filename != null) continue; String text = getTextContent(subPart, type); if (text != null) return text.trim(); } return null; } return null; } catch (Exception ex) { throw new RuntimeException(ex); } } public static MimeMessage loadMessage(File file) throws MessagingException, IOException { BufferedInputStream in = new BufferedInputStream(new FileInputStream(file)); MimeMessage message = loadMessage(createDummySession(), in); in.close(); return message; } public static MimeMessage loadMessage(Session session, InputStream in) throws MessagingException, IOException { MimeMessage message = new MimeMessage(session, in); message.getContent(); return message; } public static void moveMessage(Message message, Folder destination) { copyMessage(message, destination); try { message.setFlag(Flags.Flag.DELETED, true); } catch (MessagingException ex) { throw new RuntimeException(ex); } LOG.debug("Message", toString(message), "moved to ", destination.getName()); } public static void copyMessage(Message message, Folder destination) { boolean sourceOpened = false; boolean destinationOpened = false; Folder source = message.getFolder(); try { if (!source.isOpen()) { source.open(Folder.READ_ONLY); sourceOpened = true; } if (!destination.isOpen()) { destination.open(Folder.READ_WRITE); destinationOpened = true; } try { source.copyMessages(new Message[] { message }, destination); } catch (MessagingException e) { destination.appendMessages(new Message[] { message }); } } catch (MessagingException ex) { throw new RuntimeException("Copying message " + toString(message) + " from " + source.getName() + " to " + destination.getName() + " failed.", ex); } finally { if (sourceOpened) closeFolder(source, false); if (destinationOpened) closeFolder(destination, false); } } public static String toString(Message message) { StringBuilder sb = new StringBuilder(); sb.append(getFromFormated(message)); sb.append(":"); try { sb.append(message.getSubject()); } catch (MessagingException ex) { throw new RuntimeException(ex); } return sb.toString(); } public static String getToFormated(Message msg) throws MessagingException { StringBuffer sb = new StringBuffer(); Address[] aa = msg.getRecipients(Message.RecipientType.TO); for (int i = 0; i < aa.length; i++) { sb.append(aa[i].toString()); if (i < aa.length - 1) sb.append(", "); } return sb.toString(); } public static String getReplyTo(Message msg) { Address[] aa; try { aa = msg.getReplyTo(); } catch (MessagingException ex) { throw new RuntimeException(ex); } if (aa == null) return null; if (aa.length > 0) { return Str.decodeQuotedPrintable(aa[0].toString()); } return null; } public static String getFrom(Message msg) { return getHeaderFieldValue(msg, HEADER_FROM); } public static String getFromFormated(Message msg) { StringBuffer sb = new StringBuffer(); Address[] aa; try { aa = msg.getFrom(); } catch (MessagingException ex) { throw new RuntimeException(ex); } if (aa == null) { sb.append("<Kein Absender>"); } else { for (int i = 0; i < aa.length; i++) { sb.append(Str.decodeQuotedPrintable(aa[i].toString())); if (i < aa.length - 1) sb.append(", "); } } return sb.toString(); } public static Set<String> getTosFormated(Message msg) { return getRecipientsFormated(msg, javax.mail.Message.RecipientType.TO); } public static Set<String> getCcsFormated(Message msg) { return getRecipientsFormated(msg, javax.mail.Message.RecipientType.CC); } public static Set<String> getRecipientsFormated(Message msg, javax.mail.Message.RecipientType type) { Address[] aa; try { aa = msg.getRecipients(type); } catch (MessagingException ex) { throw new RuntimeException(ex); } Set<String> result = new HashSet<String>(); if (aa != null) { for (Address a : aa) { result.add(Str.decodeQuotedPrintable(a.toString())); } } return result; } public static DateAndTime getSentTime(Message msg) { Date date; try { date = msg.getSentDate(); } catch (MessagingException ex) { throw new RuntimeException(ex); } if (date == null) return null; DateAndTime result = new DateAndTime(date); if (result.isFuture()) result = DateAndTime.now(); return result; } public static String getSubject(Message msg) { try { return Str.decodeQuotedPrintable(msg.getSubject()); } catch (MessagingException ex) { throw new RuntimeException(ex); } } public static MimeMessage createTextMessage(Session session, String subject, String text, String from, String to) { try { return createTextMessage(session, subject, text, InternetAddress.parse(from)[0], InternetAddress.parse(to.replace(';', ','))); } catch (AddressException ex) { throw new RuntimeException(ex); } } public static MimeMessage createTextMessage(Session session, String subject, String text, Address from, Address[] to) { MimeMessage msg = createEmptyMimeMessage(session); try { msg.setSubject(subject, charset); msg.setText(text, charset); msg.setFrom(from); msg.setRecipients(Message.RecipientType.TO, to); } catch (MessagingException ex) { throw new RuntimeException(ex); } return msg; } public static MimeMessage createTextMessageWithAttachments(Session session, String subject, String text, String from, String to, Attachment... attachments) { try { return createTextMessageWithAttachments(session, subject, text, InternetAddress.parse(from)[0], InternetAddress.parse(to), attachments); } catch (AddressException ex) { throw new RuntimeException(ex); } } public static MimeMessage createTextMessageWithAttachments(Session session, String subject, String text, Address from, Address[] to, Attachment... attachments) { MimeMessage msg = createEmptyMimeMessage(session); try { msg.setSubject(subject, charset); msg.setFrom(from); msg.setRecipients(Message.RecipientType.TO, to); Multipart multipart = new MimeMultipart(); MimeBodyPart textBodyPart = new MimeBodyPart(); textBodyPart.setText(text, charset); multipart.addBodyPart(textBodyPart); if (attachments != null) { for (Attachment attachment : attachments) { appendAttachment(multipart, attachment); } } msg.setContent(multipart); } catch (MessagingException ex) { throw new RuntimeException(ex); } return msg; } private static void appendAttachment(Multipart multipart, Attachment attachment) throws MessagingException { BodyPart fileBodyPart = new MimeBodyPart(); fileBodyPart.setDataHandler(new DataHandler(attachment.getDataSource())); fileBodyPart.setFileName(attachment.getFileName()); multipart.addBodyPart(fileBodyPart); } public static MimeMessage createEmptyMimeMessage(Session session) { MimeMessage msg = new MimeMessage(session); setHeaderFieldValue(msg, HEADER_MESSAGE_ID, UUID.randomUUID() + "@" + Servers.SERVISTO); setHeaderFieldValue(msg, HEADER_MESSAGE_CONTENT_TRANSFER_ENCODING, "8bit"); return msg; } public static Session createDummySession() { Properties p = new Properties(); p.setProperty("mail.smtp.host", "localhost"); p.setProperty("mail.smtp.auth", "true"); Session session = Session.getInstance(p); return session; } public static void sendSmtpMessage(Session session, Message message) { try { sendSmtpMessage(session, message, message.getAllRecipients()); } catch (MessagingException ex) { throw new RuntimeException(ex); } } public static Session createSmtpSession(String host, Integer port, boolean tls, String user, String password) { if (Str.isBlank(host)) throw new IllegalArgumentException("host ist blank"); if (Str.isBlank(user)) user = null; if (Str.isBlank(password)) password = null; Properties p = new Properties(); p.setProperty("mail.mime.charset", charset); p.setProperty("mail.transport.protocol", "smtp"); p.setProperty("mail.smtp.host", host); if (port != null) p.put("mail.smtp.port", port); p.put("mail.smtp.starttls.enable", String.valueOf(tls)); boolean auth = user != null && password != null; p.setProperty("mail.smtp.auth", String.valueOf(auth)); if (user != null) p.setProperty("mail.smtp.auth.user", user); if (password != null) p.setProperty("mail.smtp.auth.password", password); Session session = Session.getInstance(p); if (auth) { session.setPasswordAuthentication(new URLName("local"), new PasswordAuthentication(user, password)); } return session; } public static void sendSmtpMessage(String host, Integer port, boolean tls, String user, String password, Message message, Address[] recipients) throws MessagingException { sendSmtpMessage(createSmtpSession(host, port, tls, user, password), message, recipients); } public static void sendSmtpMessage(Session session, Message message, Address[] recipients) throws MessagingException { // message = cloneMimeMessage(message); message.setSentDate(new Date()); setHeaderFieldValue(message, HEADER_X_MAILER, X_MAILER); message.saveChanges(); LOG.info("Sending message '" + message.getSubject() + "' from '" + Str.format(message.getFrom()) + "' to '" + Str.format(recipients) + "'."); Transport trans = session.getTransport("smtp"); Properties properties = session.getProperties(); trans.connect(properties.getProperty("mail.smtp.host"), properties.getProperty("mail.smtp.auth.user"), properties.getProperty("mail.smtp.auth.password")); trans.sendMessage(message, recipients); trans.close(); } public static MimeMessage cloneMimeMessage(Message msg) throws MessagingException { MimeMessage newmsg = new MimeMessage((MimeMessage) msg); return newmsg; } public static String getMessageId(Message msg) { String[] header; try { header = msg.getHeader(HEADER_MESSAGE_ID); } catch (MessagingException ex) { throw new RuntimeException(ex); } if (header == null) { LOG.debug("Message has no id.'"); return null; } return header[0]; } public static String getHeaderFieldValue(Message msg, String fieldName) { String[] header; try { header = msg.getHeader(fieldName); } catch (MessagingException ex) { throw new RuntimeException(ex); } if (header == null) return null; return header[0]; } public static void setHeaderFieldValue(Message msg, String fieldName, String value) { try { msg.setHeader(fieldName, value); } catch (MessagingException ex) { throw new RuntimeException(ex); } } public static void setReplyTo(Message msg, String email) { try { msg.setReplyTo(new Address[] { new InternetAddress(email) }); } catch (AddressException ex) { throw new RuntimeException(ex); } catch (MessagingException ex) { throw new RuntimeException(ex); } } public static void addHeaderField(Message msg, String fieldName, String value) { try { msg.addHeader(fieldName, value); } catch (MessagingException ex) { throw new RuntimeException(ex); } } public static void addBCC(Message msg, String addresses) { try { msg.addRecipients(RecipientType.BCC, InternetAddress.parse(addresses.replace(';', ','))); } catch (Exception ex) { throw new RuntimeException(ex); } } public static void addCC(Message msg, String addresses) { try { msg.addRecipients(RecipientType.CC, InternetAddress.parse(addresses.replace(';', ','))); } catch (Exception ex) { throw new RuntimeException(ex); } } public static Store getStore(String protocol, String host, String user, String password) { Properties properties = new Properties(); properties.setProperty("mail.user", user); properties.setProperty("mail.host", host); Session session = Session.getInstance(properties); Store store; try { store = session.getStore(protocol); store.connect(host, user, password); } catch (NoSuchProviderException ex) { throw new RuntimeException(ex); } catch (MessagingException ex) { throw new RuntimeException(ex); } return store; } public static void closeStore(Store store) { if (store == null) return; try { store.close(); } catch (MessagingException ex) { ex.printStackTrace(); // nop } } public static Folder getFolder(Store store, String name, boolean autoCreate) { int sepIdx = name.indexOf('/'); if (sepIdx > 0) { String firstName = name.substring(0, sepIdx); String lastName = name.substring(sepIdx + 1); Folder parent = getFolder(store, firstName, autoCreate); if (parent == null) return null; return getFolder(parent, lastName, autoCreate); } Folder folder; try { folder = store.getFolder(name); } catch (MessagingException ex) { throw new RuntimeException("Getting folder failed: " + name, ex); } boolean folderExists; try { folderExists = folder.exists(); } catch (MessagingException ex) { throw new RuntimeException("Querying folder for existence failed: " + name, ex); } if (!folderExists) { if (!autoCreate) return null; boolean created; try { created = folder.create(Folder.HOLDS_MESSAGES); } catch (MessagingException ex) { throw new RuntimeException("Creating folder failed: " + name, ex); } if (!created) throw new RuntimeException("Creating folder failed: " + name); LOG.info("Mailbox folder created:", name); } return folder; } public static Folder getFolder(Folder store, String name, boolean autoCreate) { int sepIdx = name.indexOf('/'); if (sepIdx > 0) { String firstName = name.substring(0, sepIdx); String lastName = name.substring(sepIdx + 1); Folder parent = getFolder(store, firstName, autoCreate); if (parent == null) return null; return getFolder(parent, lastName, autoCreate); } Folder folder; try { folder = store.getFolder(name); } catch (MessagingException ex) { throw new RuntimeException("Getting folder failed: " + name, ex); } boolean folderExists; try { folderExists = folder.exists(); } catch (MessagingException ex) { throw new RuntimeException("Querying folder for existence failed: " + name, ex); } if (!folderExists) { if (!autoCreate) return null; boolean created; try { created = folder.create(Folder.HOLDS_MESSAGES); } catch (MessagingException ex) { throw new RuntimeException("Creating folder failed: " + name, ex); } if (!created) throw new RuntimeException("Creating folder failed: " + name); LOG.info("Mailbox folder created:", name); } return folder; } public static void closeFolder(Folder folder, boolean delete) { if (folder == null) return; if (!folder.isOpen()) return; try { folder.close(delete); } catch (Exception ex) { if (delete) throw new RuntimeException(ex); } } public static Store getStore(String email) { ResourceBundle bundle = ResourceBundle.getBundle("mailstores"); return getStore(bundle.getString(email + ".protocol"), bundle.getString(email + ".host"), bundle.getString(email + ".user"), bundle.getString(email + ".password")); } public static Address[] parseAddresses(String s) throws AddressException { String[] tokens = Str.tokenize(s, ",;:"); InternetAddress[] ads = new InternetAddress[tokens.length]; for (int i = 0; i < ads.length; i++) { ads[i] = new InternetAddress(tokens[i]); } return ads; } public static String parsePureEmail(String richEmailAddress) { InternetAddress a; try { a = new InternetAddress(richEmailAddress); } catch (AddressException ex) { return null; } return a.getAddress(); } public static Address[] toAddressArray(Collection<Address> c) { Address[] aa = new Address[c.size()]; Iterator<Address> it = c.iterator(); int i = 0; while (it.hasNext()) { aa[i] = it.next(); i++; } return aa; } public static Attachment[] createAttachmentsFromDirContents(File dir) { if (dir == null || !dir.exists()) return null; return toAttachments(dir.listFiles()); } public static Attachment[] toAttachments(File... files) { if (files == null) return null; Attachment[] attachments = new Attachment[files.length]; for (int i = 0; i < files.length; i++) { attachments[i] = new Attachment(files[i]); } return attachments; } public static void setCharset(String charset) { Eml.charset = charset; Sys.setProperty("mail.mime.charset", charset); } public static class Attachment { private String fileName; private DataSource dataSource; public Attachment(String fileName, DataSource dataSource) { this.fileName = fileName; this.dataSource = dataSource; } public Attachment(File file) { this(file.getName(), new FileDataSource(file)); } public String getFileName() { return fileName; } public DataSource getDataSource() { return dataSource; } } }