@Override public int getUnreadMessageCount() throws MessagingException { checkOpen(); try { int unreadMessageCount = 0; List<ImapResponse> responses = mConnection.executeSimpleCommand( String.format( ImapConstants.STATUS + " \"%s\" (" + ImapConstants.UNSEEN + ")", ImapStore.encodeFolderName(mName, mStore.mPathPrefix))); // S: * STATUS mboxname (MESSAGES 231 UIDNEXT 44292) for (ImapResponse response : responses) { if (response.isDataResponse(0, ImapConstants.STATUS)) { unreadMessageCount = response .getListOrEmpty(2) .getKeyedStringOrEmpty(ImapConstants.UNSEEN) .getNumberOrZero(); } } return unreadMessageCount; } catch (IOException ioe) { throw ioExceptionHandler(mConnection, ioe); } finally { destroyResponses(); } }
@Override public boolean create(FolderType type) throws MessagingException { /* * This method needs to operate in the unselected mode as well as the selected mode * so we must get the connection ourselves if it's not there. We are specifically * not calling checkOpen() since we don't care if the folder is open. */ ImapConnection connection = null; synchronized (this) { if (mConnection == null) { connection = mStore.getConnection(); } else { connection = mConnection; } } try { connection.executeSimpleCommand( String.format( ImapConstants.CREATE + " \"%s\"", ImapStore.encodeFolderName(mName, mStore.mPathPrefix))); return true; } catch (MessagingException me) { return false; } catch (IOException ioe) { throw ioExceptionHandler(connection, ioe); } finally { connection.destroyResponses(); if (mConnection == null) { mStore.poolConnection(connection); } } }
/** * Selects the folder for use. Before performing any operations on this folder, it must be * selected. */ private void doSelect() throws IOException, MessagingException { List<ImapResponse> responses = mConnection.executeSimpleCommand( String.format( ImapConstants.SELECT + " \"%s\"", ImapStore.encodeFolderName(mName, mStore.mPathPrefix))); // Assume the folder is opened read-write; unless we are notified otherwise mMode = OpenMode.READ_WRITE; int messageCount = -1; for (ImapResponse response : responses) { if (response.isDataResponse(1, ImapConstants.EXISTS)) { messageCount = response.getStringOrEmpty(0).getNumberOrZero(); } else if (response.isOk()) { final ImapString responseCode = response.getResponseCodeOrEmpty(); if (responseCode.is(ImapConstants.READ_ONLY)) { mMode = OpenMode.READ_ONLY; } else if (responseCode.is(ImapConstants.READ_WRITE)) { mMode = OpenMode.READ_WRITE; } } else if (response.isTagged()) { // Not OK throw new MessagingException( "Can't open mailbox: " + response.getStatusResponseTextOrEmpty()); } } if (messageCount == -1) { throw new MessagingException("Did not find message count during select"); } mMessageCount = messageCount; mExists = true; }
@Override public boolean exists() throws MessagingException { if (mExists) { return true; } /* * This method needs to operate in the unselected mode as well as the selected mode * so we must get the connection ourselves if it's not there. We are specifically * not calling checkOpen() since we don't care if the folder is open. */ ImapConnection connection = null; synchronized (this) { if (mConnection == null) { connection = mStore.getConnection(); } else { connection = mConnection; } } try { connection.executeSimpleCommand( String.format( ImapConstants.STATUS + " \"%s\" (" + ImapConstants.UIDVALIDITY + ")", ImapStore.encodeFolderName(mName, mStore.mPathPrefix))); mExists = true; return true; } catch (MessagingException me) { // Treat IOERROR messaging exception as IOException if (me.getExceptionType() == MessagingException.IOERROR) { throw me; } return false; } catch (IOException ioe) { throw ioExceptionHandler(connection, ioe); } finally { connection.destroyResponses(); if (mConnection == null) { mStore.poolConnection(connection); } } }
/** * Appends the given messages to the selected folder. This implementation also determines the new * UID of the given message on the IMAP server and sets the Message's UID to the new server UID. */ @Override public void appendMessages(Message[] messages) throws MessagingException { checkOpen(); try { for (Message message : messages) { // Create output count CountingOutputStream out = new CountingOutputStream(); EOLConvertingOutputStream eolOut = new EOLConvertingOutputStream(out); message.writeTo(eolOut); eolOut.flush(); // Create flag list (most often this will be "\SEEN") String flagList = ""; Flag[] flags = message.getFlags(); if (flags.length > 0) { StringBuilder sb = new StringBuilder(); for (int i = 0, count = flags.length; i < count; i++) { Flag flag = flags[i]; if (flag == Flag.SEEN) { sb.append(" " + ImapConstants.FLAG_SEEN); } else if (flag == Flag.FLAGGED) { sb.append(" " + ImapConstants.FLAG_FLAGGED); } } if (sb.length() > 0) { flagList = sb.substring(1); } } mConnection.sendCommand( String.format( ImapConstants.APPEND + " \"%s\" (%s) {%d}", ImapStore.encodeFolderName(mName, mStore.mPathPrefix), flagList, out.getCount()), false); ImapResponse response; do { response = mConnection.readResponse(); if (response.isContinuationRequest()) { eolOut = new EOLConvertingOutputStream(mConnection.mTransport.getOutputStream()); message.writeTo(eolOut); eolOut.write('\r'); eolOut.write('\n'); eolOut.flush(); } else if (!response.isTagged()) { handleUntaggedResponse(response); } } while (!response.isTagged()); // TODO Why not check the response? /* * Try to recover the UID of the message from an APPENDUID response. * e.g. 11 OK [APPENDUID 2 238268] APPEND completed */ final ImapList appendList = response.getListOrEmpty(1); if ((appendList.size() >= 3) && appendList.is(0, ImapConstants.APPENDUID)) { String serverUid = appendList.getStringOrEmpty(2).getString(); if (!TextUtils.isEmpty(serverUid)) { message.setUid(serverUid); continue; } } /* * Try to find the UID of the message we just appended using the * Message-ID header. If there are more than one response, take the * last one, as it's most likely the newest (the one we just uploaded). */ String messageId = message.getMessageId(); if (messageId == null || messageId.length() == 0) { continue; } // Most servers don't care about parenthesis in the search query [and, some // fail to work if they are used] String[] uids = searchForUids(String.format("HEADER MESSAGE-ID %s", messageId)); if (uids.length > 0) { message.setUid(uids[0]); } // However, there's at least one server [AOL] that fails to work unless there // are parenthesis, so, try this as a last resort uids = searchForUids(String.format("(HEADER MESSAGE-ID %s)", messageId)); if (uids.length > 0) { message.setUid(uids[0]); } } } catch (IOException ioe) { throw ioExceptionHandler(mConnection, ioe); } finally { destroyResponses(); } }
@Override public void copyMessages(Message[] messages, Folder folder, MessageUpdateCallbacks callbacks) throws MessagingException { checkOpen(); try { List<ImapResponse> responseList = mConnection.executeSimpleCommand( String.format( ImapConstants.UID_COPY + " %s \"%s\"", ImapStore.joinMessageUids(messages), ImapStore.encodeFolderName(folder.getName(), mStore.mPathPrefix))); // Build a message map for faster UID matching HashMap<String, Message> messageMap = new HashMap<String, Message>(); boolean handledUidPlus = false; for (Message m : messages) { messageMap.put(m.getUid(), m); } // Process response to get the new UIDs for (ImapResponse response : responseList) { // All "BAD" responses are bad. Only "NO", tagged responses are bad. if (response.isBad() || (response.isNo() && response.isTagged())) { String responseText = response.getStatusResponseTextOrEmpty().getString(); throw new MessagingException(responseText); } // Skip untagged responses; they're just status if (!response.isTagged()) { continue; } // No callback provided to report of UID changes; nothing more to do here // NOTE: We check this here to catch any server errors if (callbacks == null) { continue; } ImapList copyResponse = response.getListOrEmpty(1); String responseCode = copyResponse.getStringOrEmpty(0).getString(); if (ImapConstants.COPYUID.equals(responseCode)) { handledUidPlus = true; String origIdSet = copyResponse.getStringOrEmpty(2).getString(); String newIdSet = copyResponse.getStringOrEmpty(3).getString(); String[] origIdArray = ImapUtility.getImapSequenceValues(origIdSet); String[] newIdArray = ImapUtility.getImapSequenceValues(newIdSet); // There has to be a 1:1 mapping between old and new IDs if (origIdArray.length != newIdArray.length) { throw new MessagingException( "Set length mis-match; orig IDs \"" + origIdSet + "\" new IDs \"" + newIdSet + "\""); } for (int i = 0; i < origIdArray.length; i++) { final String id = origIdArray[i]; final Message m = messageMap.get(id); if (m != null) { callbacks.onMessageUidChange(m, newIdArray[i]); } } } } // If the server doesn't support UIDPLUS, try a different way to get the new UID(s) if (callbacks != null && !handledUidPlus) { ImapFolder newFolder = (ImapFolder) folder; try { // Temporarily select the destination folder newFolder.open(OpenMode.READ_WRITE); // Do the search(es) ... for (Message m : messages) { String searchString = "HEADER Message-Id \"" + m.getMessageId() + "\""; String[] newIdArray = newFolder.searchForUids(searchString); if (newIdArray.length == 1) { callbacks.onMessageUidChange(m, newIdArray[0]); } } } catch (MessagingException e) { // Log, but, don't abort; failures here don't need to be propagated Log.d(Logging.LOG_TAG, "Failed to find message", e); } finally { newFolder.close(false); } // Re-select the original folder doSelect(); } } catch (IOException ioe) { throw ioExceptionHandler(mConnection, ioe); } finally { destroyResponses(); } }