001: package org.claros.commons.mail.protocols;
002:
003: import java.security.Security;
004: import java.util.ArrayList;
005: import java.util.Collections;
006: import java.util.HashMap;
007: import java.util.Map;
008: import java.util.Properties;
009:
010: import javax.mail.AuthenticationFailedException;
011: import javax.mail.FetchProfile;
012: import javax.mail.Flags;
013: import javax.mail.Folder;
014: import javax.mail.Message;
015: import javax.mail.MessagingException;
016: import javax.mail.NoSuchProviderException;
017: import javax.mail.Session;
018: import javax.mail.Store;
019:
020: import org.apache.commons.logging.Log;
021: import org.apache.commons.logging.LogFactory;
022: import org.claros.commons.auth.models.AuthProfile;
023: import org.claros.commons.exception.SystemException;
024: import org.claros.commons.mail.exception.ConnectionException;
025: import org.claros.commons.mail.exception.MailboxActionException;
026: import org.claros.commons.mail.exception.ProtocolNotAvailableException;
027: import org.claros.commons.mail.exception.ServerDownException;
028: import org.claros.commons.mail.models.ConnectionMetaHandler;
029: import org.claros.commons.mail.models.ConnectionProfile;
030: import org.claros.commons.mail.models.EmailHeader;
031: import org.claros.commons.mail.utility.Constants;
032: import org.claros.commons.mail.utility.Utility;
033: import org.claros.commons.utility.Formatter;
034:
035: /**
036: * @author Umut Gokbayrak
037: */
038: public class Pop3ProtocolImpl implements Protocol {
039: private static Log log = LogFactory.getLog(Pop3ProtocolImpl.class);
040: private ConnectionProfile profile;
041: private AuthProfile auth;
042: private ConnectionMetaHandler handler;
043: private static Map pop3Folders = Collections
044: .synchronizedMap(new HashMap());
045:
046: /**
047: *
048: * @param profile
049: * @param auth
050: * @param handler
051: */
052: Pop3ProtocolImpl(ConnectionProfile profile, AuthProfile auth,
053: ConnectionMetaHandler handler) {
054: this .profile = profile;
055: this .auth = auth;
056: this .handler = handler;
057: }
058:
059: /* (non-Javadoc)
060: * @see org.claros.commons.mail.protocols.FetchProtocol#connect(int)
061: */
062: public ConnectionMetaHandler connect(int connectType)
063: throws SystemException, ConnectionException,
064: ServerDownException {
065: try {
066: try {
067: disconnect();
068: try {
069: Thread.sleep(2000);
070: } catch (Exception k) {
071: }
072: } catch (Exception k) {
073: }
074:
075: if (handler == null || !handler.getStore().isConnected()) {
076: Properties props = new Properties();
077:
078: if (log.isDebugEnabled()) {
079: props.setProperty("mail.debug", "true");
080: System.setProperty("javax.net.debug", "all");
081: }
082:
083: if (profile.getFetchSSL() != null
084: && profile.getFetchSSL().toLowerCase().equals(
085: "true")) {
086: Security
087: .addProvider(new com.sun.net.ssl.internal.ssl.Provider());
088:
089: Security
090: .setProperty("ssl.SocketFactory.provider",
091: "org.claros.commons.mail.protocols.DummySSLSocketFactory");
092: props.setProperty("mail.store.protocol", "pop3");
093: props.setProperty("mail.pop3.host", profile
094: .getFetchServer());
095: props.setProperty("mail.pop3.port", profile
096: .getFetchPort());
097:
098: props
099: .setProperty(
100: "mail.pop3.socketFactory.class",
101: "org.claros.commons.mail.protocols.DummySSLSocketFactory");
102: props
103: .setProperty(
104: "mail.pop3.socketFactory.fallback",
105: "false");
106: props.setProperty("mail.pop3.port", profile
107: .getFetchPort());
108: props.setProperty("mail.pop3.socketFactory.port",
109: profile.getFetchPort());
110: }
111:
112: Session session = Session.getInstance(props);
113: log.debug("session instance initiated");
114: handler = new ConnectionMetaHandler();
115: handler.setStore(session
116: .getStore(profile.getProtocol()));
117: log.debug("session store set");
118: handler.getStore().connect(profile.getFetchServer(),
119: profile.getIFetchPort(), auth.getUsername(),
120: auth.getPassword());
121: log.debug("Store has been connected... Successful");
122: handler.setMbox(handler.getStore().getDefaultFolder());
123: handler.setMbox(handler.getMbox().getFolder(
124: Constants.FOLDER_INBOX(profile)));
125: log.debug("Got mailbox");
126: handler.getMbox().open(connectType);
127: log.debug("Mailbox open");
128:
129: // storing the folder in map
130: pop3Folders.put(auth.getUsername(), handler.getMbox());
131:
132: handler.setTotalMessagesCount(handler.getMbox()
133: .getMessageCount());
134: log.debug("Message Count:"
135: + handler.getTotalMessagesCount());
136: }
137: } catch (AuthenticationFailedException e) {
138: log
139: .debug(
140: "Pop3 Mailbox was busy with another session and there is a read write lock. A few minutes later when the lock is released everything will be fine.",
141: e);
142: } catch (NoSuchProviderException e) {
143: log.fatal(profile.getProtocol()
144: + " provider could not be found.");
145: throw new SystemException(e);
146: } catch (MessagingException e) {
147: log.error("Connection could not be established.");
148: throw new ConnectionException(e);
149: } catch (Exception e) {
150: e.printStackTrace();
151: }
152: return handler;
153: }
154:
155: /* (non-Javadoc)
156: * @see org.claros.commons.mail.protocols.FetchProtocol#deleteMessages(int[])
157: */
158: public ConnectionMetaHandler deleteMessages(int[] messageIds)
159: throws MailboxActionException, SystemException,
160: ConnectionException {
161: Folder fold = null;
162: try {
163: fold = getFolder();
164: if (messageIds != null && messageIds.length > 0) {
165: for (int i = 0; i < messageIds.length; i++) {
166: Message msg = fold.getMessage(messageIds[i]);
167: msg.setFlag(Flags.Flag.DELETED, true);
168: }
169: }
170: // fold.expunge();
171: disconnect();
172: connect(Constants.CONNECTION_READ_WRITE);
173: return handler;
174: } catch (Exception e) {
175: pop3Folders.put(auth.getUsername(), null);
176: log.error("Could not delete message ids: " + messageIds, e);
177: throw new MailboxActionException(e);
178: }
179: }
180:
181: /**
182: * Fetches all e-mail headers from the server, with appropriate
183: * fields already set.
184: * @param handler
185: * @return ArrayList of MessageHeaders
186: * @throws ConnectionException
187: */
188: public ArrayList fetchAllHeaders() throws SystemException,
189: ConnectionException {
190: ArrayList headers = null;
191: Folder fold = null;
192: try {
193: fold = getFolder();
194: closeFolder(fold);
195: fold = getFolder();
196:
197: headers = new ArrayList();
198: EmailHeader header = null;
199:
200: Message[] msgs = fold.getMessages();
201: FetchProfile fp = new FetchProfile();
202: fp.add(FetchProfile.Item.ENVELOPE);
203: fp.add(FetchProfile.Item.FLAGS);
204: fp.add(FetchProfile.Item.CONTENT_INFO);
205: fp.add("Size");
206: fp.add("Date");
207: fold.fetch(msgs, fp);
208:
209: Message msg = null;
210: for (int i = 0; i < msgs.length; i++) {
211: try {
212: header = new EmailHeader();
213: msg = msgs[i];
214:
215: header
216: .setMultipart((msg
217: .isMimeType("multipart/*")) ? true
218: : false);
219: header.setMessageId(i + 1);
220: header.setFrom(msg.getFrom());
221: header.setTo(msg
222: .getRecipients(Message.RecipientType.TO));
223: header.setCc(msg
224: .getRecipients(Message.RecipientType.CC));
225: header.setBcc(msg
226: .getRecipients(Message.RecipientType.BCC));
227: header.setDate(msg.getSentDate());
228: header.setReplyTo(msg.getReplyTo());
229: header.setSize(msg.getSize());
230: header.setSubject(msg.getSubject());
231:
232: // now set the human readables.
233: header.setDateShown(Formatter.formatDate(header
234: .getDate(), "dd.MM.yyyy HH:mm"));
235: header.setFromShown(Utility
236: .addressArrToString(header.getFrom()));
237: header.setToShown(Utility.addressArrToString(header
238: .getTo()));
239: header.setCcShown(Utility.addressArrToString(header
240: .getCc()));
241: header.setSizeShown(Utility
242: .sizeToHumanReadable(header.getSize()));
243:
244: // it is time to add it to the arraylist
245: headers.add(header);
246: } catch (MessagingException e1) {
247: log
248: .debug(
249: "Could not parse headers of e-mail. Message might be defuncted or illegal formatted.",
250: e1);
251: }
252: }
253: } catch (Exception e) {
254: log
255: .error(
256: "Could not fetch message headers. Is mbox connection still alive???",
257: e);
258: throw new ConnectionException(e);
259: }
260: return headers;
261: }
262:
263: /**
264: * Fetches and returns message headers as message objects.
265: * @return
266: * @throws SystemException
267: * @throws ConnectionException
268: */
269: public ArrayList fetchAllHeadersAsMessages()
270: throws SystemException, ConnectionException {
271: ArrayList headers = null;
272: Folder fold = null;
273: try {
274: fold = getFolder();
275: closeFolder(fold);
276: fold = getFolder();
277: headers = new ArrayList();
278:
279: Message[] msgs = fold.getMessages();
280: FetchProfile fp = new FetchProfile();
281: fp.add(FetchProfile.Item.ENVELOPE);
282: fp.add(FetchProfile.Item.FLAGS);
283: fp.add(FetchProfile.Item.CONTENT_INFO);
284: fp.add("Size");
285: fp.add("Date");
286: fold.fetch(msgs, fp);
287:
288: Message msg = null;
289: for (int i = 0; i < msgs.length; i++) {
290: msg = msgs[i];
291: headers.add(msg);
292: }
293: } catch (Exception e) {
294: log
295: .error(
296: "Could not fetch message headers. Is mbox connection still alive???",
297: e);
298: throw new ConnectionException(e);
299: }
300: return headers;
301: }
302:
303: public Message getMessage(int messageId)
304: throws MailboxActionException, SystemException,
305: ConnectionException, Exception {
306: Message msg = null;
307: Folder fold = null;
308: try {
309: try {
310: fold = getFolder();
311: msg = fold.getMessage(messageId);
312: } catch (Exception e) {
313: log
314: .error(
315: "Could not fetch message body from remote server.",
316: e);
317: throw new MailboxActionException(e);
318: }
319: } catch (Exception e) {
320: throw e;
321: }
322: return msg;
323: }
324:
325: /**
326: * Disconnects the previously opened data connection if
327: * the connection is still alive.
328: * @param handler
329: */
330: public void disconnect() {
331: try {
332: Folder fold = (Folder) pop3Folders.get(auth.getUsername());
333: if (fold != null) {
334: fold.close(true);
335: }
336: try {
337: if (handler.getMbox() != null) {
338: handler.getMbox().close(true);
339: }
340: } catch (Exception e) {
341: }
342:
343: try {
344: if (handler.getStore() != null) {
345: handler.getStore().close();
346: }
347: } catch (Exception e) {
348: }
349: } catch (Exception e) {
350: }
351: pop3Folders.put(auth.getUsername(), null);
352: }
353:
354: public void emptyFolder() throws Exception {
355: Folder f = getFolder();
356:
357: try {
358: Message msgs[] = f.getMessages();
359: FetchProfile fp = new FetchProfile();
360: fp.add(FetchProfile.Item.ENVELOPE);
361: f.fetch(msgs, fp);
362:
363: int ids[] = new int[msgs.length];
364: for (int i = 0; i < msgs.length; i++) {
365: ids[i] = msgs[i].getMessageNumber();
366: }
367: if (ids.length > 0) {
368: deleteMessages(ids);
369: }
370: } catch (Exception e) {
371: log.warn("Could not delete all messages");
372: }
373: }
374:
375: /**
376: *
377: * @return
378: * @throws Exception
379: */
380: public synchronized Folder getFolder() throws Exception {
381: String folder = Constants.FOLDER_INBOX(profile);
382:
383: Folder fold = (Folder) pop3Folders.get(auth.getUsername());
384: if (fold != null && fold.isOpen()) {
385: return fold;
386: } else {
387: if (folder != null && handler != null) {
388: Store store = handler.getStore();
389: if (store == null || !store.isConnected()) {
390: log.debug("Connection is closed. Restoring it...");
391: handler = connect(Constants.CONNECTION_READ_WRITE);
392: log.debug("Connection re-established");
393: }
394: fold = handler.getStore().getFolder(folder);
395: if (!fold.isOpen()) {
396: log.debug("Folder :" + folder
397: + " is closed. Opening again.");
398: fold.open(Constants.CONNECTION_READ_WRITE);
399: log.debug("Folder is open again.");
400:
401: pop3Folders.put(auth.getUsername(), fold);
402: }
403: }
404: }
405: return fold;
406: }
407:
408: /**
409: *
410: * @throws Exception
411: */
412: /*
413: public void connectIfNeeded() throws Exception {
414: Folder myFold = null;
415: String folder = Constants.FOLDER_INBOX(profile);
416:
417: if (folder != null && handler != null) {
418: Store store = handler.getStore();
419: if (store == null || !store.isConnected()) {
420: log.debug("Connection is closed. Restoring it...");
421: handler = connect(Constants.CONNECTION_READ_WRITE);
422: log.debug("Connection re-established");
423: }
424: myFold = handler.getStore().getFolder(folder);
425: if (!myFold.isOpen() && myFold.exists()) {
426: try {
427: myFold.close(true);
428: } catch (Exception z) {}
429:
430: try {
431: log.debug("Folder :" + folder + " is closed. Opening again.");
432: myFold.open(Constants.CONNECTION_READ_WRITE);
433: log.debug("Folder is open again.");
434: } catch (Exception m) {
435: m.printStackTrace();
436: }
437: }
438: }
439: }
440: */
441:
442: /**
443: *
444: * @param f
445: */
446: public void closeFolder(Folder f) {
447: if (f != null) {
448: try {
449: if (f.isOpen()) {
450: f.close(true);
451: log.info("Folder: " + f.getName()
452: + " was open and now closed.");
453: } else {
454: log.info("Folder: " + f.getName()
455: + " was already closed.");
456: }
457: } catch (MessagingException e) {
458: log.info("Error while closing folder: " + f.getName(),
459: e);
460: }
461: }
462: pop3Folders.put(auth.getUsername(), null);
463: }
464:
465: /**
466: * @return
467: */
468: public int getUnreadMessageCount() throws Exception {
469: Folder f = getFolder();
470:
471: if (f.exists()) {
472: return f.getUnreadMessageCount();
473: }
474: return 0;
475: }
476:
477: /**
478: * @return
479: */
480: public int getTotalMessageCount() throws Exception {
481: Folder f = getFolder();
482: if (f.exists()) {
483: return f.getMessageCount();
484: }
485: return 0;
486: }
487:
488: public void flagAsDeleted(int[] ids) throws Exception {
489: deleteMessages(ids);
490: }
491:
492: public ArrayList getHeadersSortedList(String sortCriteriaRaw,
493: String sortDirectionRaw)
494: throws ProtocolNotAvailableException {
495: profile.setSupportSort(false);
496: throw new ProtocolNotAvailableException();
497: }
498:
499: /**
500: *
501: */
502: public ConnectionProfile getProfile() {
503: return profile;
504: }
505:
506: /**
507: *
508: */
509: public ArrayList fetchHeaders(int[] msgs) throws SystemException,
510: ConnectionException {
511: throw new SystemException(
512: "this method is not available with the pop3 protocol");
513: }
514:
515: }
|