private void connect() throws ServiceException { if (!connection.isClosed()) return; try { connection.connect(); try { connection.login(dataSource.getDecryptedPassword()); } catch (CommandFailedException e) { throw new LoginException(e.getError()); } } catch (Exception e) { connection.close(); throw ServiceException.FAILURE("Unable to connect to POP3 server: " + dataSource, e); } }
@Override public synchronized void test() throws ServiceException { validateDataSource(); try { connect(); if (dataSource.leaveOnServer() && !hasUIDL()) { throw RemoteServiceException.POP3_UIDL_REQUIRED(); } connection.quit(); } catch (IOException e) { throw ServiceException.FAILURE("Unable to connect to POP3 server: " + dataSource, e); } finally { connection.close(); } }
private boolean hasUIDL() throws IOException { if (connection.hasCapability(Pop3Capabilities.UIDL)) { return true; } // Additional check for servers that don't support CAPA // (i.e. Hotmail) but do support UIDL. try { if (connection.getMessageCount() > 0) { // Only need to test UIDL on first message connection.getMessageUid(1); } else { connection.getMessageUids(); } } catch (CommandFailedException e) { return false; } return true; }
private void fetchAndRetainMessages() throws Exception { String[] uids = connection.getMessageUids(); Set<String> existingUids = PopMessage.getMatchingUids(dataSource, uids); int count = uids.length - existingUids.size(); LOG.info("Found %d new message(s) on remote server", count); if (count == 0) { return; // No new messages } IOExceptionHandler.getInstance().resetSyncCounter(mbox); boolean checkForSelfPopping = true; for (int msgno = uids.length; msgno > 0; --msgno) { String uid = uids[msgno - 1]; if (!existingUids.contains(uid)) { if (checkForSelfPopping) { // Only check new messages else could match one we previously synced. if (poppingSelf(uid)) { throw ServiceException.INVALID_REQUEST( "User attempted to import messages from his own mailbox", null); } checkForSelfPopping = false; // Only need to check one message } LOG.debug("Fetching message with uid %s", uid); IOExceptionHandler.getInstance().trackSyncItem(mbox, msgno); // Don't allow filtering to a mountpoint when retaining the // message. We don't have a local id, so we can't keep track // of it in the data_source_item table. try { fetchAndAddMessage(msgno, connection.getMessageSize(msgno), uid, false); } catch (Exception e) { if (IOExceptionHandler.getInstance().isRecoverable(mbox, msgno, "pop sync fail", e)) { // skip it; will be retried on every subsequent sync continue; } throw e; } } } IOExceptionHandler.getInstance().checkpointIOExceptionRate(mbox); }
@Override public synchronized void importData(List<Integer> folderIds, boolean fullSync) throws ServiceException { validateDataSource(); connect(); try { if (connection.getMessageCount() > 0) { if (dataSource.leaveOnServer()) { fetchAndRetainMessages(); } else { fetchAndDeleteMessages(); } } connection.quit(); } catch (ServiceException e) { throw e; } catch (Exception e) { throw ServiceException.FAILURE("Synchronization of POP3 folder failed", e); } finally { connection.close(); } }
private void fetchAndDeleteMessages() throws Exception { Integer sizes[] = connection.getMessageSizes(); LOG.info("Found %d new message(s) on remote server", sizes.length); IOExceptionHandler.getInstance().resetSyncCounter(mbox); for (int msgno = sizes.length; msgno > 0; --msgno) { LOG.debug("Fetching message number %d", msgno); IOExceptionHandler.getInstance().trackSyncItem(mbox, msgno); try { fetchAndAddMessage(msgno, sizes[msgno - 1], null, true); } catch (Exception e) { if (IOExceptionHandler.getInstance().isRecoverable(mbox, msgno, "pop sync fail", e)) { // don't delete msg; if we end up aborting we want it still to exist // skip it; will be retried on every subsequent sync continue; } throw e; } checkIsEnabled(); connection.deleteMessage(msgno); } IOExceptionHandler.getInstance().checkpointIOExceptionRate(mbox); }
private void fetchAndAddMessage(int msgno, int size, String uid, boolean allowFilterToMountpoint) throws ServiceException, IOException { ContentInputStream cis = null; MessageContent mc = null; checkIsEnabled(); try { cis = connection.getMessage(msgno); mc = MessageContent.read(cis, size); ParsedMessage pm = mc.getParsedMessage(null, indexAttachments); if (pm == null) { LOG.warn("Empty message body for UID %d. Must be ignored.", uid); return; } Message msg = null; // bug 47796: Set received date to sent date if available otherwise use current time try { Date sentDate = pm.getMimeMessage().getSentDate(); if (sentDate == null) { LOG.warn( "null sent date; probably due to parse error. Date header value: [%s]", pm.getMimeMessage().getHeader("Date", null)); } pm.setReceivedDate(sentDate != null ? sentDate.getTime() : System.currentTimeMillis()); } catch (MessagingException e) { LOG.warn( "unable to get sent date from parsed message due to exception, must use current time", e); pm.setReceivedDate(System.currentTimeMillis()); } DeliveryContext dc = mc.getDeliveryContext(); if (isOffline()) { msg = addMessage(null, pm, size, dataSource.getFolderId(), Flag.BITMASK_UNREAD, dc); } else { Integer localId = getFirstLocalId( RuleManager.applyRulesToIncomingMessage( null, mbox, pm, size, dataSource.getEmailAddress(), dc, dataSource.getFolderId(), true, allowFilterToMountpoint)); if (localId != null) { msg = mbox.getMessageById(null, localId); } } if (msg != null && uid != null) { PopMessage msgTracker = new PopMessage(dataSource, msg.getId(), uid); msgTracker.add(); } } catch (CommandFailedException e) { LOG.warn("Error fetching message number %d: %s", msgno, e.getMessage()); } finally { if (cis != null) { try { cis.close(); } catch (ParseException pe) { LOG.error( "ParseException while closing ContentInputStream. Assuming cis is effectively closed", pe); } } if (mc != null) { mc.cleanup(); } } }