/* * Created on Oct 17, 2007 */ package net.sf.thingamablog.transport; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Properties; import javax.mail.Address; import javax.mail.BodyPart; import javax.mail.Flags; import javax.mail.Folder; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.Session; import javax.mail.Store; import javax.mail.internet.InternetAddress; import javax.xml.transform.OutputKeys; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import net.sf.thingamablog.blog.Author; import net.sf.thingamablog.blog.BlogEntry; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.tidy.Tidy; /** * @author Bob Tantlinger * */ public class EMailTransport extends RemoteTransport { //pop3 or imap private String protocol = "pop3"; private Store store = null; private String failureReason = null; private String postDirective = "POST"; private Tidy tidy = new Tidy(); public EMailTransport() { this.setPort(110); tidy.setXHTML(true); tidy.setQuiet(true); tidy.setShowWarnings(false); } /* (non-Javadoc) * @see net.sf.thingamablog.transport.MailTransport#getEntries() */ public List getEntries(Author[] authors, String[] catNames, MailTransportProgress prg) throws Exception { List entries = new ArrayList(); prg.emailCheckStarted(getAddress()); try { Folder folder = store.getFolder("INBOX"); if(folder != null) { folder.open(Folder.READ_WRITE); Message[] message = folder.getMessages(); if(message != null) { prg.numberOfMessagesToCheck(message.length); for(int i = 0; i < message.length; i++) { if(prg.isAborted()) break; String subj = message[i].getSubject(); boolean isAdded = false; BlogEntry be = this.createEntryFromMessage(message[i], authors, catNames); if(be != null) { entries.add(be); message[i].setFlag(Flags.Flag.DELETED, true); isAdded = true; } prg.messageChecked((subj == null) ? "(No subject)" : subj, isAdded); } } folder.close(true); } } catch(Exception ex) { prg.mailCheckFailed(ex.getLocalizedMessage()); throw ex; } finally { prg.emailCheckComplete(); } return entries; } private BlogEntry createEntryFromMessage(Message m, Author[] auths, String[] cats) throws MessagingException, IOException { String subj = m.getSubject(); if(subj == null || !subj.toLowerCase().startsWith(postDirective.toLowerCase())) //this isn't a post email return null; int colonIndex = subj.indexOf(':', postDirective.length());//subj.indexOf(':'); if(colonIndex == -1) return null; //didn't end with a colon Author auth = getAuthor(m, auths); if(auth == null) return null; //we don't know who this message is from BlogEntry be = new BlogEntry(); be.setTitle(subj.substring(colonIndex + 1, subj.length()).trim()); be.setAuthor(auth); String postPrefix = subj.substring(postDirective.length(), colonIndex).trim().toLowerCase(); //now figure out what cats there are if(postPrefix.equals("")) //no cats { //System.err.println("NO CATS"); } else if(postPrefix.startsWith("in ")) //we have some cats { postPrefix = postPrefix.substring(3, postPrefix.length()); //System.err.println(postPrefix); String[] ecats = postPrefix.split(","); HashSet entryCats = new HashSet(); for(int i = 0; i < ecats.length; i++) { String temp = ecats[i].trim(); for(int j = 0; j < cats.length; j++) { //the blog cat we're looking for String theCat = cats[j].toLowerCase().trim(); //if the blog cat has a comma or colon in it //we'll replace it with the char entity so we //can compare it with the cat in the email subject //comma=,, colon entity= : theCat = theCat.replaceAll(",", ","); theCat = theCat.replaceAll(":", ":"); if(temp.equals(theCat)) entryCats.add(cats[j]); } } Iterator it = entryCats.iterator(); while(it.hasNext()) { be.addCategory(it.next().toString()); } } else { //malformed - has non cats between 'POST' and ':' //System.err.println("MALFORMED: " + postPrefix); return null; } be.setDate(m.getSentDate()); be.setText(getMessageBody(m)); return be; } private Author getAuthor(Message m, Author[] auths) throws MessagingException { Address[] adr = m.getFrom(); if(adr != null && adr.length > 0 && adr[0] instanceof InternetAddress) { String iAdr = ((InternetAddress)adr[0]).getAddress(); if(iAdr != null && !iAdr.equals("")) { for(int i = 0; i < auths.length; i++) { if(auths[i].getEmailAddress().equals(iAdr)) return auths[i]; } } } return null; } private String getMessageBody(Message m) throws MessagingException, IOException { String bodyText = null; Object content = m.getContent(); if(content instanceof String) //text/plain return getTextBetweenDelimiters((String)content); if(content instanceof Multipart) { Multipart multiPart = (Multipart)content; int partCount = multiPart.getCount(); for(int i = 0; i < partCount; i++) { BodyPart bp = multiPart.getBodyPart(i); //if it has HTML text, use it instead if(bp.getContentType().toLowerCase().startsWith("text/html")) { String rawHtml = getTextBetweenDelimiters(bp.getContent().toString()); ByteArrayInputStream bin = new ByteArrayInputStream(rawHtml.getBytes()); Document doc = tidy.parseDOM(bin, null); return getBodyText(doc); } if(bodyText == null && bp.getContentType().toLowerCase().startsWith("text/plain")) bodyText = bp.getContent().toString(); } } if(bodyText == null) return ""; return getTextBetweenDelimiters(bodyText); } public String getTextBetweenDelimiters(String text) { String openDelim = "$body_start$"; String closeDelim = "$body_end$"; int openStart = text.indexOf(openDelim); int closeStart = text.lastIndexOf(closeDelim); if(openStart != -1 && closeStart != -1 && openStart < closeStart) { return text.substring(openStart + openDelim.length(), closeStart); } //no delims so just return the original text return text; } private String getBodyText(Document doc) { NodeList nodelist = doc.getElementsByTagName("body"); Node body = null; if(nodelist != null) { body = nodelist.item(0); NodeList bodyChildren = body.getChildNodes(); StringBuffer sb = new StringBuffer(); for(int i = 0; i < bodyChildren.getLength(); i++) { sb.append(xmlToString(bodyChildren.item(i))); } //System.err.println(sb.toString()); return sb.toString(); } return ""; } private String xmlToString(Node node) { try { Source source = new DOMSource(node); StringWriter stringWriter = new StringWriter(); Result result = new StreamResult(stringWriter); TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); transformer.transform(source, result); return stringWriter.getBuffer().toString(); } catch(TransformerConfigurationException e) { e.printStackTrace(); } catch(TransformerException e) { e.printStackTrace(); } return null; } /** *Sets the protocol name *@param protocol the name of the protocol (supposes "pop3" or "imap") */ public void setProtocol(String prot) throws IllegalArgumentException { if(prot.compareToIgnoreCase("pop3") == 0) { protocol = "pop3"; //port="110"; } else if(prot.compareToIgnoreCase("imap") == 0) { protocol = "imap"; //port="143"; } else { throw new IllegalArgumentException("Invalid protocol: " + prot); } } /* (non-Javadoc) * @see net.sf.thingamablog.transport.Transport#connect(net.sf.thingamablog.transport.TransportProgress) */ public boolean connect() { failureReason = null; Properties props = new Properties(); props.put("mail." + protocol + ".port", getPort() + ""); Session session = Session.getDefaultInstance(props, null); try { store = session.getStore(protocol); store.connect(getAddress(), getUserName(), getPassword()); } catch(Exception ex) { ex.printStackTrace(); failureReason = ex.getLocalizedMessage(); //prg.mailCheckFailed(failureReason); return false; } return true; } /* (non-Javadoc) * @see net.sf.thingamablog.transport.Transport#disconnect() */ public boolean disconnect() { if(store != null) { try { store.close(); } catch(MessagingException e) { e.printStackTrace(); return false; } } return true; } /* (non-Javadoc) * @see net.sf.thingamablog.transport.Transport#getFailureReason() */ public String getFailureReason() { return failureReason; } /** * @return the protocol */ public String getProtocol() { return protocol; } /** * @return the postDirective */ public String getPostDirective() { return postDirective; } /** * @param postDirective the postDirective to set */ public void setPostDirective(String postDirective) { this.postDirective = postDirective; } }