0001: /**
0002: * Copyright (c) 2003-2007, David A. Czarnecki
0003: * All rights reserved.
0004: *
0005: * Redistribution and use in source and binary forms, with or without
0006: * modification, are permitted provided that the following conditions are met:
0007: *
0008: * Redistributions of source code must retain the above copyright notice, this list of conditions and the
0009: * following disclaimer.
0010: * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
0011: * following disclaimer in the documentation and/or other materials provided with the distribution.
0012: * Neither the name of "David A. Czarnecki" and "blojsom" nor the names of its contributors may be used to
0013: * endorse or promote products derived from this software without specific prior written permission.
0014: * Products derived from this software may not be called "blojsom", nor may "blojsom" appear in their name,
0015: * without prior written permission of David A. Czarnecki.
0016: *
0017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
0018: * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
0019: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
0020: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
0021: * EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
0022: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0025: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
0026: * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
0027: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0028: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
0029: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0030: */package org.blojsom.plugin.moblog;
0031:
0032: import org.apache.commons.logging.Log;
0033: import org.apache.commons.logging.LogFactory;
0034: import org.blojsom.blog.Blog;
0035: import org.blojsom.blog.Entry;
0036: import org.blojsom.event.EventBroadcaster;
0037: import org.blojsom.fetcher.Fetcher;
0038: import org.blojsom.fetcher.FetcherException;
0039: import org.blojsom.plugin.PluginException;
0040: import org.blojsom.plugin.admin.event.EntryAddedEvent;
0041: import org.blojsom.plugin.email.EmailConstants;
0042: import org.blojsom.plugin.email.SimpleAuthenticator;
0043: import org.blojsom.plugin.velocity.StandaloneVelocityPlugin;
0044: import org.blojsom.util.BlojsomConstants;
0045: import org.blojsom.util.BlojsomMetaDataConstants;
0046: import org.blojsom.util.BlojsomUtils;
0047:
0048: import javax.mail.*;
0049: import javax.mail.internet.InternetAddress;
0050: import javax.mail.internet.MimeMultipart;
0051: import javax.naming.Context;
0052: import javax.naming.InitialContext;
0053: import javax.naming.NamingException;
0054: import javax.servlet.http.HttpServletRequest;
0055: import javax.servlet.http.HttpServletResponse;
0056: import java.io.*;
0057: import java.net.ConnectException;
0058: import java.util.*;
0059: import java.util.regex.Matcher;
0060: import java.util.regex.Pattern;
0061:
0062: /**
0063: * Moblog Plugin
0064: *
0065: * @author David Czarnecki
0066: * @author Mark Lussier
0067: * @version $Id: MoblogPlugin.java,v 1.6 2007/01/17 02:35:11 czarneckid Exp $
0068: * @since blojsom 3.0
0069: */
0070: public class MoblogPlugin extends StandaloneVelocityPlugin {
0071:
0072: private Log _logger = LogFactory.getLog(MoblogPlugin.class);
0073:
0074: private static final String MOBLOG_ENTRY_TEMPLATE = "org/blojsom/plugin/moblog/moblog-plugin-template.vm";
0075:
0076: private static final String MOBLOG_SUBJECT = "MOBLOG_SUBJECT";
0077: private static final String MOBLOG_BODY_TEXT = "MOBLOG_BODY_TEXT";
0078: private static final String MOBLOG_IMAGES = "MOBLOG_IMAGES";
0079: private static final String MOBLOG_ATTACHMENTS = "MOBLOG_ATTACHMENTS";
0080: private static final String MOBLOG_ATTACHMENT = "MOBLOG_ATTACHMENT";
0081: private static final String MOBLOG_ATTACHMENT_URL = "MOBLOG_ATTACHMENT_URL";
0082: private static final String MOBLOG_IMAGE = "MOBLOG_IMAGE";
0083: private static final String MOBLOG_IMAGE_URL = "MOBLOG_IMAGE_URL";
0084:
0085: /**
0086: * Multipart/alternative mime-type
0087: */
0088: private static final String MULTIPART_ALTERNATIVE_MIME_TYPE = "multipart/alternative";
0089:
0090: /**
0091: * Text/html mime-type
0092: */
0093: private static final String TEXT_HTML_MIME_TYPE = "text/html";
0094:
0095: /**
0096: * Default mime-types for text
0097: */
0098: public static final String DEFAULT_TEXT_MIME_TYPES = "text/plain, text/html";
0099:
0100: /**
0101: * Default mime-types for images
0102: */
0103: public static final String DEFAULT_IMAGE_MIME_TYPES = "image/jpg, image/jpeg, image/gif, image/png";
0104:
0105: /**
0106: * Multipart mime-type
0107: */
0108: private static final String MULTIPART_TYPE = "multipart/*";
0109:
0110: /**
0111: * Default store
0112: */
0113: private static final String DEFAULT_MESSAGE_STORE = "pop3";
0114:
0115: /**
0116: * Default poll time (10 minutes)
0117: */
0118: private static final int DEFAULT_POLL_TIME = 720;
0119:
0120: /**
0121: * Moblog confifguration parameter for web.xml
0122: */
0123: public static final String PLUGIN_MOBLOG_CONFIGURATION_IP = "plugin-moblog";
0124:
0125: /**
0126: * Moblog configuration parameter for mailbox polling time (5 minutes)
0127: */
0128: public static final String PLUGIN_MOBLOG_POLL_TIME = "plugin-moblog-poll-time";
0129:
0130: /**
0131: * Moblog configuration parameter for message store provider
0132: */
0133: public static final String PLUGIN_MOBLOG_STORE_PROVIDER = "plugin-moblog-store-provider";
0134:
0135: /**
0136: * Default moblog authorization properties file which lists valid e-mail addresses who can moblog entries
0137: */
0138: public static final String DEFAULT_MOBLOG_AUTHORIZATION_FILE = "moblog-authorization.properties";
0139:
0140: /**
0141: * Configuration property for moblog authorization properties file to use
0142: */
0143: public static final String PROPERTY_AUTHORIZATION = "moblog-authorization";
0144:
0145: /**
0146: * Configuration property for mailhost
0147: */
0148: public static final String PROPERTY_HOSTNAME = "moblog-hostname";
0149:
0150: /**
0151: * Configuration property for mailbox user ID
0152: */
0153: public static final String PROPERTY_USERID = "moblog-userid";
0154:
0155: /**
0156: * Configuration property for mailbox user password
0157: */
0158: public static final String PROPERTY_PASSWORD = "moblog-password";
0159:
0160: /**
0161: * Configuration property for moblog category
0162: */
0163: public static final String PROPERTY_CATEGORY = "moblog-category";
0164:
0165: /**
0166: * Configuration property for whether or not moblog is enabled for this blog
0167: */
0168: public static final String PROPERTY_ENABLED = "moblog-enabled";
0169:
0170: /**
0171: * Configuration property for the secret word that must be present at the beginning of the subject
0172: */
0173: public static final String PLUGIN_MOBLOG_SECRET_WORD = "moblog-secret-word";
0174:
0175: /**
0176: * Configuration property for image mime-types
0177: */
0178: public static final String PLUGIN_MOBLOG_IMAGE_MIME_TYPES = "moblog-image-mime-types";
0179:
0180: /**
0181: * Configuration property for attachment mime-types
0182: */
0183: public static final String PLUGIN_MOBLOG_ATTACHMENT_MIME_TYPES = "moblog-attachment-mime-types";
0184:
0185: /**
0186: * Configuration property for text mime-types
0187: */
0188: public static final String PLUGIN_MOBLOG_TEXT_MIME_TYPES = "moblog-text-mime-types";
0189:
0190: /**
0191: * Configuration property for regular expression to ignore a certain portion of text
0192: */
0193: public static final String PLUGIN_MOBLOG_IGNORE_EXPRESSION = "moblog-ignore-expression";
0194:
0195: public static final String PLUGIN_MOBLOG_AUTHORIZED_ADDRESSES = "moblog-authorized-addresses";
0196:
0197: private int _pollTime;
0198:
0199: private Session _storeSession;
0200: private boolean _finished = false;
0201: private MailboxChecker _checker;
0202: private String _storeProvider;
0203:
0204: private EventBroadcaster _eventBroadcaster;
0205: private Fetcher _fetcher;
0206:
0207: /**
0208: * Set the {@link Fetcher}
0209: *
0210: * @param fetcher {@link Fetcher}
0211: */
0212: public void setFetcher(Fetcher fetcher) {
0213: _fetcher = fetcher;
0214: }
0215:
0216: /**
0217: * Set the {@link EventBroadcaster}
0218: *
0219: * @param eventBroadcaster {@link EventBroadcaster}
0220: */
0221: public void setEventBroadcaster(EventBroadcaster eventBroadcaster) {
0222: _eventBroadcaster = eventBroadcaster;
0223: }
0224:
0225: /**
0226: * Initialize this plugin. This method only called when the plugin is
0227: * instantiated.
0228: *
0229: * @throws PluginException If there is an error initializing the plugin
0230: */
0231: public void init() throws PluginException {
0232: super .init();
0233:
0234: String moblogPollTime = _servletConfig
0235: .getInitParameter(PLUGIN_MOBLOG_POLL_TIME);
0236: if (BlojsomUtils.checkNullOrBlank(moblogPollTime)) {
0237: _pollTime = DEFAULT_POLL_TIME;
0238: } else {
0239: try {
0240: _pollTime = Integer.parseInt(moblogPollTime);
0241: } catch (NumberFormatException e) {
0242: if (_logger.isErrorEnabled()) {
0243: if (_logger.isErrorEnabled()) {
0244: _logger.error("Invalid time specified for: "
0245: + PLUGIN_MOBLOG_POLL_TIME);
0246: }
0247: }
0248: _pollTime = DEFAULT_POLL_TIME;
0249: }
0250: }
0251:
0252: _storeProvider = _servletConfig
0253: .getInitParameter(PLUGIN_MOBLOG_STORE_PROVIDER);
0254: if (BlojsomUtils.checkNullOrBlank(_storeProvider)) {
0255: _storeProvider = DEFAULT_MESSAGE_STORE;
0256: }
0257:
0258: _checker = new MailboxChecker();
0259: _checker.setDaemon(true);
0260:
0261: String hostname = _servletConfig
0262: .getInitParameter(EmailConstants.SMTPSERVER_IP);
0263: if (hostname != null) {
0264: if (hostname.startsWith("java:comp/env")) {
0265: try {
0266: Context context = new InitialContext();
0267: _storeSession = (Session) context.lookup(hostname);
0268: } catch (NamingException e) {
0269: if (_logger.isErrorEnabled()) {
0270: _logger.error(e);
0271: }
0272:
0273: throw new PluginException(e);
0274: }
0275: } else {
0276: String username = _servletConfig
0277: .getInitParameter(EmailConstants.SMTPSERVER_USERNAME_IP);
0278: String password = _servletConfig
0279: .getInitParameter(EmailConstants.SMTPSERVER_PASSWORD_IP);
0280:
0281: Properties props = new Properties();
0282: props.put(EmailConstants.SESSION_NAME, hostname);
0283: if (BlojsomUtils.checkNullOrBlank(username)
0284: || BlojsomUtils.checkNullOrBlank(password)) {
0285: _storeSession = Session.getInstance(props, null);
0286: } else {
0287: _storeSession = Session
0288: .getInstance(props,
0289: new SimpleAuthenticator(username,
0290: password));
0291: }
0292: }
0293: }
0294:
0295: _checker.start();
0296: }
0297:
0298: /**
0299: * Process the blog entries
0300: *
0301: * @param httpServletRequest Request
0302: * @param httpServletResponse Response
0303: * @param blog {@link org.blojsom.blog.Blog} instance
0304: * @param context Context
0305: * @param entries Blog entries retrieved for the particular request
0306: * @return Modified set of blog entries
0307: * @throws PluginException If there is an error processing the blog entries
0308: */
0309: public Entry[] process(HttpServletRequest httpServletRequest,
0310: HttpServletResponse httpServletResponse, Blog blog,
0311: Map context, Entry[] entries) throws PluginException {
0312: return entries;
0313: }
0314:
0315: /**
0316: * Perform any cleanup for the plugin. Called after {@link #process}.
0317: *
0318: * @throws PluginException If there is an error performing cleanup for this plugin
0319: */
0320: public void cleanup() throws PluginException {
0321: }
0322:
0323: /**
0324: * Called when BlojsomServlet is taken out of service
0325: *
0326: * @throws PluginException If there is an error in finalizing this plugin
0327: */
0328: public void destroy() throws PluginException {
0329: _finished = true;
0330: }
0331:
0332: /**
0333: * Thread that polls the mailboxes
0334: */
0335: private class MailboxChecker extends Thread {
0336:
0337: /**
0338: * Allocates a new <code>Thread</code> object. This constructor has
0339: * the same effect as <code>Thread(null, null,</code>
0340: * <i>gname</i><code>)</code>, where <b><i>gname</i></b> is
0341: * a newly generated name. Automatically generated names are of the
0342: * form <code>"Thread-"+</code><i>n</i>, where <i>n</i> is an integer.
0343: *
0344: * @see Thread#Thread(ThreadGroup,
0345: * Runnable, String)
0346: */
0347: public MailboxChecker() {
0348: super ();
0349: }
0350:
0351: /**
0352: * Perform the actual work of checking the POP3 mailbox configured for the blog user.
0353: *
0354: * @param mailbox Mailbox to be processed
0355: */
0356: private void processMailbox(Mailbox mailbox) {
0357: Folder folder = null;
0358: Store store = null;
0359: String subject;
0360:
0361: try {
0362: store = _storeSession.getStore(_storeProvider);
0363: store.connect(mailbox.getHostName(), mailbox
0364: .getUserId(), mailbox.getPassword());
0365:
0366: // -- Try to get hold of the default folder --
0367: folder = store.getDefaultFolder();
0368: if (folder == null) {
0369: if (_logger.isErrorEnabled()) {
0370: _logger.error("Default folder is null.");
0371: }
0372: _finished = true;
0373: }
0374:
0375: // -- ...and its INBOX --
0376: folder = folder.getFolder(mailbox.getFolder());
0377: if (folder == null) {
0378: if (_logger.isErrorEnabled()) {
0379: _logger.error("No POP3 folder called "
0380: + mailbox.getFolder());
0381: }
0382: _finished = true;
0383: }
0384:
0385: // -- Open the folder for read only --
0386: folder.open(Folder.READ_WRITE);
0387:
0388: // -- Get the message wrappers and process them --
0389: Message[] msgs = folder.getMessages();
0390:
0391: if (_logger.isDebugEnabled()) {
0392: _logger.debug("Found [" + msgs.length
0393: + "] messages");
0394: }
0395:
0396: for (int msgNum = 0; msgNum < msgs.length; msgNum++) {
0397: String from = ((InternetAddress) msgs[msgNum]
0398: .getFrom()[0]).getAddress();
0399: if (_logger.isDebugEnabled()) {
0400: _logger.debug("Processing message: " + msgNum);
0401: }
0402:
0403: if (!checkSender(mailbox, from)) {
0404: if (_logger.isDebugEnabled()) {
0405: _logger
0406: .debug("Unauthorized sender address: "
0407: + from);
0408: _logger
0409: .debug("Deleting message: "
0410: + msgNum);
0411: }
0412:
0413: msgs[msgNum].setFlag(Flags.Flag.DELETED, true);
0414: } else {
0415: Message email = msgs[msgNum];
0416: subject = email.getSubject();
0417:
0418: StringBuffer description = new StringBuffer();
0419: Part messagePart;
0420: messagePart = email;
0421: Pattern pattern = null;
0422: List moblogImages = new ArrayList();
0423: List moblogAttachments = new ArrayList();
0424: Map moblogContext = new HashMap();
0425:
0426: if (mailbox.getIgnoreExpression() != null) {
0427: pattern = Pattern.compile(mailbox
0428: .getIgnoreExpression(),
0429: Pattern.CASE_INSENSITIVE
0430: | Pattern.MULTILINE
0431: | Pattern.UNICODE_CASE
0432: | Pattern.DOTALL);
0433: }
0434:
0435: if (subject == null) {
0436: subject = "";
0437: } else {
0438: subject = subject.trim();
0439: }
0440:
0441: String secretWord = mailbox.getSecretWord();
0442: if (secretWord != null) {
0443: if (!subject.startsWith(secretWord)) {
0444: if (_logger.isErrorEnabled()) {
0445: _logger
0446: .error("Message does not begin with secret word for user id: "
0447: + mailbox
0448: .getUserId());
0449: }
0450: msgs[msgNum].setFlag(
0451: Flags.Flag.DELETED, true);
0452:
0453: continue;
0454: } else {
0455: subject = subject.substring(secretWord
0456: .length());
0457: }
0458: }
0459:
0460: if (email.isMimeType(MULTIPART_TYPE)) {
0461: // Check for multipart/alternative
0462: String overallType = email.getContentType();
0463: overallType = sanitizeContentType(overallType);
0464:
0465: boolean isMultipartAlternative = false;
0466: if (MULTIPART_ALTERNATIVE_MIME_TYPE
0467: .equals(overallType)) {
0468: isMultipartAlternative = true;
0469: }
0470:
0471: Multipart mp = (Multipart) messagePart
0472: .getContent();
0473: int count = mp.getCount();
0474:
0475: for (int i = 0; i < count; i++) {
0476: BodyPart bp = mp.getBodyPart(i);
0477: String type = bp.getContentType();
0478: if (type != null) {
0479: type = sanitizeContentType(type);
0480: Map imageMimeTypes = mailbox
0481: .getImageMimeTypes();
0482: Map attachmentMimeTypes = mailbox
0483: .getAttachmentMimeTypes();
0484: Map textMimeTypes = mailbox
0485: .getTextMimeTypes();
0486:
0487: // Check for multipart alternative as part of a larger e-mail block
0488: if (MULTIPART_ALTERNATIVE_MIME_TYPE
0489: .equals(type)) {
0490: Object mimeMultipartContent = bp
0491: .getContent();
0492: if (mimeMultipartContent instanceof MimeMultipart) {
0493: MimeMultipart mimeMultipart = (MimeMultipart) mimeMultipartContent;
0494: int mimeMultipartCount = mimeMultipart
0495: .getCount();
0496: for (int j = 0; j < mimeMultipartCount; j++) {
0497: BodyPart mimeMultipartBodyPart = mimeMultipart
0498: .getBodyPart(j);
0499: String mmpbpType = mimeMultipartBodyPart
0500: .getContentType();
0501: if (mmpbpType != null) {
0502: mmpbpType = sanitizeContentType(mmpbpType);
0503: if (TEXT_HTML_MIME_TYPE
0504: .equals(mmpbpType)) {
0505: if (_logger
0506: .isDebugEnabled()) {
0507: _logger
0508: .debug("Using HTML part of multipart/alternative: "
0509: + type);
0510: }
0511: InputStream is = bp
0512: .getInputStream();
0513:
0514: BufferedReader reader = new BufferedReader(
0515: new InputStreamReader(
0516: is,
0517: BlojsomConstants.UTF8));
0518: String this Line;
0519:
0520: while ((this Line = reader
0521: .readLine()) != null) {
0522: description
0523: .append(this Line);
0524: description
0525: .append(BlojsomConstants.LINE_SEPARATOR);
0526: }
0527:
0528: reader.close();
0529: if (pattern != null) {
0530: Matcher matcher = pattern
0531: .matcher(description);
0532: if (!matcher
0533: .find()
0534: && !matcher
0535: .matches()) {
0536: //entry.append(description);
0537: moblogContext
0538: .put(
0539: MOBLOG_BODY_TEXT,
0540: description
0541: .toString());
0542: }
0543: } else {
0544: //entry.append(description);
0545: moblogContext
0546: .put(
0547: MOBLOG_BODY_TEXT,
0548: description
0549: .toString());
0550: }
0551: } else {
0552: if (_logger
0553: .isDebugEnabled()) {
0554: _logger
0555: .debug("Skipping non-HTML part of multipart/alternative block");
0556: }
0557: }
0558: } else {
0559: if (_logger
0560: .isInfoEnabled()) {
0561: _logger
0562: .info("Unknown mimetype for multipart/alternative block");
0563: }
0564: }
0565: }
0566: } else {
0567: if (_logger
0568: .isDebugEnabled()) {
0569: _logger
0570: .debug("Multipart alternative block not instance of MimeMultipart");
0571: }
0572: }
0573: } else {
0574: if (imageMimeTypes
0575: .containsKey(type)) {
0576: if (_logger
0577: .isDebugEnabled()) {
0578: _logger
0579: .debug("Creating image of type: "
0580: + type);
0581: }
0582: String outputFilename = BlojsomUtils
0583: .digestString(bp
0584: .getFileName()
0585: + "-"
0586: + new Date()
0587: .getTime());
0588: String extension = BlojsomUtils
0589: .getFileExtension(bp
0590: .getFileName());
0591: if (BlojsomUtils
0592: .checkNullOrBlank(extension)) {
0593: extension = "";
0594: }
0595:
0596: if (_logger
0597: .isDebugEnabled()) {
0598: _logger
0599: .debug("Writing to: "
0600: + mailbox
0601: .getOutputDirectory()
0602: + File.separator
0603: + outputFilename
0604: + "."
0605: + extension);
0606: }
0607: MoblogPluginUtils
0608: .saveFile(
0609: mailbox
0610: .getOutputDirectory()
0611: + File.separator
0612: + outputFilename,
0613: "."
0614: + extension,
0615: bp
0616: .getInputStream());
0617:
0618: String baseurl = mailbox
0619: .getBlogBaseURL();
0620: Map moblogImageInformation = new HashMap();
0621: moblogImageInformation
0622: .put(
0623: MOBLOG_IMAGE,
0624: outputFilename
0625: + "."
0626: + extension);
0627: moblogImageInformation
0628: .put(
0629: MOBLOG_IMAGE_URL,
0630: baseurl
0631: + mailbox
0632: .getUrlPrefix()
0633: + outputFilename
0634: + "."
0635: + extension);
0636: moblogImages
0637: .add(moblogImageInformation);
0638: } else if (attachmentMimeTypes
0639: .containsKey(type)) {
0640: if (_logger
0641: .isDebugEnabled()) {
0642: _logger
0643: .debug("Creating attachment of type: "
0644: + type);
0645: }
0646: String outputFilename = BlojsomUtils
0647: .digestString(bp
0648: .getFileName()
0649: + "-"
0650: + new Date()
0651: .getTime());
0652: String extension = BlojsomUtils
0653: .getFileExtension(bp
0654: .getFileName());
0655: if (BlojsomUtils
0656: .checkNullOrBlank(extension)) {
0657: extension = "";
0658: }
0659:
0660: if (_logger
0661: .isDebugEnabled()) {
0662: _logger
0663: .debug("Writing to: "
0664: + mailbox
0665: .getOutputDirectory()
0666: + File.separator
0667: + outputFilename
0668: + "."
0669: + extension);
0670: }
0671: MoblogPluginUtils
0672: .saveFile(
0673: mailbox
0674: .getOutputDirectory()
0675: + File.separator
0676: + outputFilename,
0677: "."
0678: + extension,
0679: bp
0680: .getInputStream());
0681:
0682: String baseurl = mailbox
0683: .getBlogBaseURL();
0684: Map moblogAttachmentInformation = new HashMap();
0685: moblogAttachmentInformation
0686: .put(
0687: MOBLOG_ATTACHMENT,
0688: bp
0689: .getFileName());
0690: moblogAttachmentInformation
0691: .put(
0692: MOBLOG_ATTACHMENT_URL,
0693: baseurl
0694: + mailbox
0695: .getUrlPrefix()
0696: + outputFilename
0697: + "."
0698: + extension);
0699: moblogAttachments
0700: .add(moblogAttachmentInformation);
0701: } else if (textMimeTypes
0702: .containsKey(type)) {
0703: if ((isMultipartAlternative && (TEXT_HTML_MIME_TYPE
0704: .equals(type)))
0705: || !isMultipartAlternative) {
0706: if (_logger
0707: .isDebugEnabled()) {
0708: _logger
0709: .debug("Using text part of type: "
0710: + type);
0711: }
0712: InputStream is = bp
0713: .getInputStream();
0714:
0715: BufferedReader reader = new BufferedReader(
0716: new InputStreamReader(
0717: is,
0718: BlojsomConstants.UTF8));
0719: String this Line;
0720:
0721: while ((this Line = reader
0722: .readLine()) != null) {
0723: description
0724: .append(this Line);
0725: description
0726: .append(BlojsomConstants.LINE_SEPARATOR);
0727: }
0728:
0729: reader.close();
0730: if (pattern != null) {
0731: Matcher matcher = pattern
0732: .matcher(description);
0733: if (!matcher.find()
0734: && !matcher
0735: .matches()) {
0736: moblogContext
0737: .put(
0738: MOBLOG_BODY_TEXT,
0739: description
0740: .toString());
0741: }
0742: } else {
0743: moblogContext
0744: .put(
0745: MOBLOG_BODY_TEXT,
0746: description
0747: .toString());
0748: }
0749: }
0750: } else {
0751: if (_logger.isInfoEnabled()) {
0752: _logger
0753: .info("Unknown mimetype for multipart: "
0754: + type);
0755: }
0756: }
0757: }
0758: } else {
0759: if (_logger.isDebugEnabled()) {
0760: _logger
0761: .debug("Body part has no defined mime type. Skipping.");
0762: }
0763: }
0764: }
0765: } else {
0766: // Check for the message being one of the defined text mime types if it's not a multipart
0767: Map textMimeTypes = mailbox
0768: .getTextMimeTypes();
0769: String mimeType = email.getContentType();
0770: if (mimeType != null) {
0771: mimeType = sanitizeContentType(mimeType);
0772: }
0773:
0774: if ((mimeType != null)
0775: && (textMimeTypes
0776: .containsKey(mimeType))) {
0777: InputStream is = email.getInputStream();
0778:
0779: BufferedReader reader = new BufferedReader(
0780: new InputStreamReader(is,
0781: BlojsomConstants.UTF8));
0782: String this Line;
0783:
0784: while ((this Line = reader.readLine()) != null) {
0785: description.append(this Line);
0786: description
0787: .append(BlojsomConstants.LINE_SEPARATOR);
0788: }
0789:
0790: reader.close();
0791: if (pattern != null) {
0792: Matcher matcher = pattern
0793: .matcher(description);
0794: if (!matcher.find()
0795: && !matcher.matches()) {
0796: moblogContext.put(
0797: MOBLOG_BODY_TEXT,
0798: description.toString());
0799: }
0800: } else {
0801: moblogContext.put(MOBLOG_BODY_TEXT,
0802: description.toString());
0803: }
0804: } else {
0805: if (_logger.isInfoEnabled()) {
0806: _logger.info("Unknown mimetype: "
0807: + mimeType);
0808: }
0809: }
0810: }
0811:
0812: // Process subject to change category for moblog post
0813: boolean categoryInSubject = false;
0814: String categoryFromSubject = null;
0815: if (subject.startsWith("[")) {
0816: int startIndex = subject.indexOf("[");
0817: if (startIndex != -1) {
0818: int closingIndex = subject.indexOf("]",
0819: startIndex);
0820: if (closingIndex != -1) {
0821: categoryFromSubject = subject
0822: .substring(startIndex + 1,
0823: closingIndex);
0824: subject = subject
0825: .substring(closingIndex + 1);
0826: categoryFromSubject = BlojsomUtils
0827: .normalize(categoryFromSubject);
0828: if (!categoryFromSubject
0829: .startsWith("/")) {
0830: categoryFromSubject = "/"
0831: + categoryFromSubject;
0832: }
0833: if (!categoryFromSubject
0834: .endsWith("/")) {
0835: categoryFromSubject += "/";
0836: }
0837: categoryInSubject = true;
0838: if (_logger.isInfoEnabled()) {
0839: _logger.info("Using category ["
0840: + categoryFromSubject
0841: + "] for entry: "
0842: + subject);
0843: }
0844: }
0845: }
0846: }
0847:
0848: String categoryID = categoryInSubject ? categoryFromSubject
0849: : mailbox.getCategoryId();
0850:
0851: moblogContext.put(MOBLOG_SUBJECT, subject);
0852: moblogContext.put(MOBLOG_IMAGES, moblogImages);
0853: moblogContext.put(MOBLOG_ATTACHMENTS,
0854: moblogAttachments);
0855:
0856: try {
0857: Blog blog = _fetcher.loadBlog(mailbox
0858: .getBlogId());
0859: String moblogText = mergeTemplate(
0860: MOBLOG_ENTRY_TEMPLATE, blog,
0861: moblogContext);
0862:
0863: Entry entry;
0864: entry = _fetcher.newEntry();
0865:
0866: entry.setBlogCategoryId(Integer
0867: .valueOf(categoryID));
0868: entry.setBlogId(mailbox.getId());
0869: entry.setDate(new Date());
0870: entry.setDescription(moblogText);
0871: entry.setTitle(subject);
0872: entry
0873: .setStatus(BlojsomMetaDataConstants.PUBLISHED_STATUS);
0874:
0875: Map entryMetaData = new HashMap();
0876: entryMetaData
0877: .put(
0878: BlojsomMetaDataConstants.BLOG_ENTRY_METADATA_AUTHOR_EXT,
0879: from);
0880: entry.setMetaData(entryMetaData);
0881:
0882: _fetcher.saveEntry(blog, entry);
0883:
0884: msgs[msgNum].setFlag(Flags.Flag.DELETED,
0885: true);
0886:
0887: _eventBroadcaster
0888: .broadcastEvent(new EntryAddedEvent(
0889: this , new Date(), entry,
0890: blog));
0891: } catch (FetcherException e) {
0892: if (_logger.isErrorEnabled()) {
0893: _logger.error(e);
0894: }
0895: }
0896: }
0897: }
0898:
0899: // Delete the messages
0900: try {
0901: if (folder != null) {
0902: folder.close(true);
0903: }
0904:
0905: if (store != null) {
0906: store.close();
0907: }
0908: } catch (MessagingException e) {
0909: if (_logger.isErrorEnabled()) {
0910: _logger.error(e);
0911: }
0912: }
0913: } catch (ConnectException e) {
0914: if (_logger.isErrorEnabled()) {
0915: _logger.error(e);
0916: }
0917: } catch (NoSuchProviderException e) {
0918: if (_logger.isErrorEnabled()) {
0919: _logger.error(e);
0920: }
0921: } catch (MessagingException e) {
0922: if (_logger.isErrorEnabled()) {
0923: _logger.error(e);
0924: }
0925: } catch (IOException e) {
0926: if (_logger.isErrorEnabled()) {
0927: _logger.error(e);
0928: }
0929: } finally {
0930: try {
0931: if (folder != null && folder.isOpen()) {
0932: folder.close(true);
0933: }
0934:
0935: if (store != null) {
0936: store.close();
0937: }
0938: } catch (MessagingException e) {
0939: if (_logger.isErrorEnabled()) {
0940: _logger.error(e);
0941: }
0942: }
0943: }
0944: }
0945:
0946: /**
0947: * Process the moblog mailboxes for each user
0948: */
0949: public void run() {
0950: try {
0951: while (!_finished) {
0952: if (_logger.isDebugEnabled()) {
0953: _logger
0954: .debug("Moblog plugin waking up and looking for new messages");
0955: }
0956:
0957: String[] blogIDs = _fetcher.loadBlogIDs();
0958: for (int i = 0; i < blogIDs.length; i++) {
0959: String blogID = blogIDs[i];
0960: Blog blog = _fetcher.loadBlog(blogID);
0961:
0962: Mailbox mailbox = MoblogPluginUtils
0963: .readMailboxSettingsForBlog(
0964: _servletConfig, blog);
0965: if (mailbox != null) {
0966: if (mailbox.isEnabled()) {
0967: if (_logger.isDebugEnabled()) {
0968: _logger.debug("Checking mailbox: "
0969: + mailbox.getUserId()
0970: + " for blog: "
0971: + mailbox.getBlogId());
0972: }
0973: processMailbox(mailbox);
0974: }
0975: }
0976: }
0977:
0978: if (_logger.isDebugEnabled()) {
0979: _logger
0980: .debug("Moblog plugin off to take a nap");
0981: }
0982: sleep(_pollTime * 1000);
0983: }
0984: } catch (InterruptedException e) {
0985: if (_logger.isErrorEnabled()) {
0986: _logger.error(e);
0987: }
0988: } catch (FetcherException e) {
0989: if (_logger.isErrorEnabled()) {
0990: _logger.error(e);
0991: }
0992: }
0993: }
0994:
0995: /**
0996: * Check to see that the sender is an authorized user to moblog
0997: *
0998: * @param mailbox Mailbox for user
0999: * @param fromAddress E-mail address of sender
1000: * @return <code>true</code> if the from address is specified as a valid poster to the moblog,
1001: * <code>false</code> otherwise
1002: */
1003: private boolean checkSender(Mailbox mailbox, String fromAddress) {
1004: boolean result = false;
1005: Map authorizedAddresses = mailbox.getAuthorizedAddresses();
1006:
1007: if (authorizedAddresses.containsKey(fromAddress)) {
1008: result = true;
1009: }
1010:
1011: return result;
1012: }
1013: }
1014:
1015: /**
1016: * Return a content type up to the first ; character
1017: *
1018: * @param contentType Content type
1019: * @return Content type without any trailing information after a ;
1020: */
1021: protected String sanitizeContentType(String contentType) {
1022: int semicolonIndex = contentType.indexOf(";");
1023: if (semicolonIndex != -1) {
1024: contentType = contentType.substring(0, semicolonIndex);
1025: }
1026:
1027: return contentType.toLowerCase();
1028: }
1029: }
|