001: /*
002: * Created on Oct 17, 2007
003: */
004: package net.sf.thingamablog.transport;
005:
006: import java.io.ByteArrayInputStream;
007: import java.io.IOException;
008: import java.io.StringWriter;
009: import java.util.ArrayList;
010: import java.util.HashSet;
011: import java.util.Iterator;
012: import java.util.List;
013: import java.util.Properties;
014:
015: import javax.mail.Address;
016: import javax.mail.BodyPart;
017: import javax.mail.Flags;
018: import javax.mail.Folder;
019: import javax.mail.Message;
020: import javax.mail.MessagingException;
021: import javax.mail.Multipart;
022: import javax.mail.Session;
023: import javax.mail.Store;
024: import javax.mail.internet.InternetAddress;
025: import javax.xml.transform.OutputKeys;
026: import javax.xml.transform.Result;
027: import javax.xml.transform.Source;
028: import javax.xml.transform.Transformer;
029: import javax.xml.transform.TransformerConfigurationException;
030: import javax.xml.transform.TransformerException;
031: import javax.xml.transform.TransformerFactory;
032: import javax.xml.transform.dom.DOMSource;
033: import javax.xml.transform.stream.StreamResult;
034:
035: import net.sf.thingamablog.blog.Author;
036: import net.sf.thingamablog.blog.BlogEntry;
037:
038: import org.w3c.dom.Document;
039: import org.w3c.dom.Node;
040: import org.w3c.dom.NodeList;
041: import org.w3c.tidy.Tidy;
042:
043: /**
044: * @author Bob Tantlinger
045: *
046: */
047: public class EMailTransport extends RemoteTransport {
048: //pop3 or imap
049: private String protocol = "pop3";
050: private Store store = null;
051: private String failureReason = null;
052: private String postDirective = "POST";
053:
054: private Tidy tidy = new Tidy();
055:
056: public EMailTransport() {
057: this .setPort(110);
058: tidy.setXHTML(true);
059: tidy.setQuiet(true);
060: tidy.setShowWarnings(false);
061: }
062:
063: /* (non-Javadoc)
064: * @see net.sf.thingamablog.transport.MailTransport#getEntries()
065: */
066: public List getEntries(Author[] authors, String[] catNames,
067: MailTransportProgress prg) throws Exception {
068: List entries = new ArrayList();
069: prg.emailCheckStarted(getAddress());
070: try {
071: Folder folder = store.getFolder("INBOX");
072: if (folder != null) {
073: folder.open(Folder.READ_WRITE);
074: Message[] message = folder.getMessages();
075: if (message != null) {
076: prg.numberOfMessagesToCheck(message.length);
077: for (int i = 0; i < message.length; i++) {
078: if (prg.isAborted())
079: break;
080: String subj = message[i].getSubject();
081:
082: boolean isAdded = false;
083: BlogEntry be = this .createEntryFromMessage(
084: message[i], authors, catNames);
085: if (be != null) {
086: entries.add(be);
087: message[i]
088: .setFlag(Flags.Flag.DELETED, true);
089: isAdded = true;
090: }
091:
092: prg.messageChecked(
093: (subj == null) ? "(No subject)" : subj,
094: isAdded);
095: }
096: }
097:
098: folder.close(true);
099: }
100: } catch (Exception ex) {
101: prg.mailCheckFailed(ex.getLocalizedMessage());
102: throw ex;
103: } finally {
104: prg.emailCheckComplete();
105: }
106:
107: return entries;
108: }
109:
110: private BlogEntry createEntryFromMessage(Message m, Author[] auths,
111: String[] cats) throws MessagingException, IOException {
112: String subj = m.getSubject();
113: if (subj == null
114: || !subj.toLowerCase().startsWith(
115: postDirective.toLowerCase())) //this isn't a post email
116: return null;
117:
118: int colonIndex = subj.indexOf(':', postDirective.length());//subj.indexOf(':');
119: if (colonIndex == -1)
120: return null; //didn't end with a colon
121:
122: Author auth = getAuthor(m, auths);
123: if (auth == null)
124: return null; //we don't know who this message is from
125:
126: BlogEntry be = new BlogEntry();
127: be.setTitle(subj.substring(colonIndex + 1, subj.length())
128: .trim());
129: be.setAuthor(auth);
130:
131: String postPrefix = subj.substring(postDirective.length(),
132: colonIndex).trim().toLowerCase();
133:
134: //now figure out what cats there are
135: if (postPrefix.equals("")) //no cats
136: {
137: //System.err.println("NO CATS");
138: } else if (postPrefix.startsWith("in ")) //we have some cats
139: {
140: postPrefix = postPrefix.substring(3, postPrefix.length());
141: //System.err.println(postPrefix);
142:
143: String[] ecats = postPrefix.split(",");
144: HashSet entryCats = new HashSet();
145: for (int i = 0; i < ecats.length; i++) {
146: String temp = ecats[i].trim();
147: for (int j = 0; j < cats.length; j++) {
148: //the blog cat we're looking for
149: String theCat = cats[j].toLowerCase().trim();
150:
151: //if the blog cat has a comma or colon in it
152: //we'll replace it with the char entity so we
153: //can compare it with the cat in the email subject
154: //comma=,, colon entity= :
155: theCat = theCat.replaceAll(",", ",");
156: theCat = theCat.replaceAll(":", ":");
157: if (temp.equals(theCat))
158: entryCats.add(cats[j]);
159: }
160: }
161:
162: Iterator it = entryCats.iterator();
163: while (it.hasNext()) {
164: be.addCategory(it.next().toString());
165: }
166: } else {
167: //malformed - has non cats between 'POST' and ':'
168: //System.err.println("MALFORMED: " + postPrefix);
169: return null;
170: }
171:
172: be.setDate(m.getSentDate());
173: be.setText(getMessageBody(m));
174: return be;
175: }
176:
177: private Author getAuthor(Message m, Author[] auths)
178: throws MessagingException {
179: Address[] adr = m.getFrom();
180: if (adr != null && adr.length > 0
181: && adr[0] instanceof InternetAddress) {
182: String iAdr = ((InternetAddress) adr[0]).getAddress();
183: if (iAdr != null && !iAdr.equals("")) {
184: for (int i = 0; i < auths.length; i++) {
185: if (auths[i].getEmailAddress().equals(iAdr))
186: return auths[i];
187: }
188: }
189: }
190:
191: return null;
192: }
193:
194: private String getMessageBody(Message m) throws MessagingException,
195: IOException {
196: String bodyText = null;
197:
198: Object content = m.getContent();
199: if (content instanceof String) //text/plain
200: return getTextBetweenDelimiters((String) content);
201: if (content instanceof Multipart) {
202: Multipart multiPart = (Multipart) content;
203: int partCount = multiPart.getCount();
204:
205: for (int i = 0; i < partCount; i++) {
206: BodyPart bp = multiPart.getBodyPart(i);
207:
208: //if it has HTML text, use it instead
209: if (bp.getContentType().toLowerCase().startsWith(
210: "text/html")) {
211: String rawHtml = getTextBetweenDelimiters(bp
212: .getContent().toString());
213: ByteArrayInputStream bin = new ByteArrayInputStream(
214: rawHtml.getBytes());
215: Document doc = tidy.parseDOM(bin, null);
216: return getBodyText(doc);
217: }
218:
219: if (bodyText == null
220: && bp.getContentType().toLowerCase()
221: .startsWith("text/plain"))
222: bodyText = bp.getContent().toString();
223: }
224: }
225:
226: if (bodyText == null)
227: return "";
228: return getTextBetweenDelimiters(bodyText);
229: }
230:
231: public String getTextBetweenDelimiters(String text) {
232: String openDelim = "$body_start$";
233: String closeDelim = "$body_end$";
234:
235: int openStart = text.indexOf(openDelim);
236: int closeStart = text.lastIndexOf(closeDelim);
237: if (openStart != -1 && closeStart != -1
238: && openStart < closeStart) {
239: return text.substring(openStart + openDelim.length(),
240: closeStart);
241: }
242:
243: //no delims so just return the original text
244: return text;
245: }
246:
247: private String getBodyText(Document doc) {
248: NodeList nodelist = doc.getElementsByTagName("body");
249: Node body = null;
250: if (nodelist != null) {
251: body = nodelist.item(0);
252: NodeList bodyChildren = body.getChildNodes();
253: StringBuffer sb = new StringBuffer();
254: for (int i = 0; i < bodyChildren.getLength(); i++) {
255: sb.append(xmlToString(bodyChildren.item(i)));
256: }
257:
258: //System.err.println(sb.toString());
259:
260: return sb.toString();
261: }
262:
263: return "";
264: }
265:
266: private String xmlToString(Node node) {
267: try {
268: Source source = new DOMSource(node);
269: StringWriter stringWriter = new StringWriter();
270: Result result = new StreamResult(stringWriter);
271: TransformerFactory factory = TransformerFactory
272: .newInstance();
273: Transformer transformer = factory.newTransformer();
274: transformer.setOutputProperty(OutputKeys.INDENT, "yes");
275: transformer.setOutputProperty(
276: OutputKeys.OMIT_XML_DECLARATION, "yes");
277: transformer.transform(source, result);
278:
279: return stringWriter.getBuffer().toString();
280: } catch (TransformerConfigurationException e) {
281: e.printStackTrace();
282: } catch (TransformerException e) {
283: e.printStackTrace();
284: }
285:
286: return null;
287: }
288:
289: /**
290: *Sets the protocol name
291: *@param protocol the name of the protocol (supposes "pop3" or "imap")
292: */
293: public void setProtocol(String prot)
294: throws IllegalArgumentException {
295: if (prot.compareToIgnoreCase("pop3") == 0) {
296: protocol = "pop3";
297: //port="110";
298: } else if (prot.compareToIgnoreCase("imap") == 0) {
299: protocol = "imap";
300: //port="143";
301: } else {
302: throw new IllegalArgumentException("Invalid protocol: "
303: + prot);
304: }
305: }
306:
307: /* (non-Javadoc)
308: * @see net.sf.thingamablog.transport.Transport#connect(net.sf.thingamablog.transport.TransportProgress)
309: */
310: public boolean connect() {
311: failureReason = null;
312: Properties props = new Properties();
313: props.put("mail." + protocol + ".port", getPort() + "");
314: Session session = Session.getDefaultInstance(props, null);
315:
316: try {
317: store = session.getStore(protocol);
318: store.connect(getAddress(), getUserName(), getPassword());
319: } catch (Exception ex) {
320: ex.printStackTrace();
321: failureReason = ex.getLocalizedMessage();
322: //prg.mailCheckFailed(failureReason);
323: return false;
324: }
325:
326: return true;
327: }
328:
329: /* (non-Javadoc)
330: * @see net.sf.thingamablog.transport.Transport#disconnect()
331: */
332: public boolean disconnect() {
333: if (store != null) {
334: try {
335: store.close();
336: } catch (MessagingException e) {
337: e.printStackTrace();
338: return false;
339: }
340: }
341:
342: return true;
343: }
344:
345: /* (non-Javadoc)
346: * @see net.sf.thingamablog.transport.Transport#getFailureReason()
347: */
348: public String getFailureReason() {
349: return failureReason;
350: }
351:
352: /**
353: * @return the protocol
354: */
355: public String getProtocol() {
356: return protocol;
357: }
358:
359: /**
360: * @return the postDirective
361: */
362: public String getPostDirective() {
363: return postDirective;
364: }
365:
366: /**
367: * @param postDirective the postDirective to set
368: */
369: public void setPostDirective(String postDirective) {
370: this.postDirective = postDirective;
371: }
372: }
|