private void indexMessage(int msgNum, Pop3Message message) { if (K9MailLib.isDebug() && DEBUG_PROTOCOL_POP3) { Log.d(LOG_TAG, "Adding index for UID " + message.getUid() + " to msgNum " + msgNum); } mMsgNumToMsgMap.put(msgNum, message); mUidToMsgMap.put(message.getUid(), message); mUidToMsgNumMap.put(message.getUid(), msgNum); }
@Override public List<Pop3Message> getMessages( int start, int end, Date earliestDate, MessageRetrievalListener<Pop3Message> listener) throws MessagingException { if (start < 1 || end < 1 || end < start) { throw new MessagingException( String.format(Locale.US, "Invalid message set %d %d", start, end)); } try { indexMsgNums(start, end); } catch (IOException ioe) { throw new MessagingException("getMessages", ioe); } List<Pop3Message> messages = new ArrayList<Pop3Message>(); int i = 0; for (int msgNum = start; msgNum <= end; msgNum++) { Pop3Message message = mMsgNumToMsgMap.get(msgNum); if (message == null) { /* * There could be gaps in the message numbers or malformed * responses which lead to "gaps" in mMsgNumToMsgMap. * * See issue 2252 */ continue; } if (listener != null) { listener.messageStarted(message.getUid(), i++, (end - start) + 1); } messages.add(message); if (listener != null) { listener.messageFinished(message, i++, (end - start) + 1); } } return messages; }
/** * Fetches the body of the given message, limiting the downloaded data to the specified number * of lines if possible. * * <p>If lines is -1 the entire message is fetched. This is implemented with RETR for lines = -1 * or TOP for any other value. If the server does not support TOP, RETR is used instead. */ private void fetchBody(Pop3Message message, int lines) throws IOException, MessagingException { String response = null; // Try hard to use the TOP command if we're not asked to download the whole message. if (lines != -1 && (!mTopNotSupported || mCapabilities.top)) { try { if (K9MailLib.isDebug() && DEBUG_PROTOCOL_POP3 && !mCapabilities.top) { Log.d( LOG_TAG, "This server doesn't support the CAPA command. " + "Checking to see if the TOP command is supported nevertheless."); } response = executeSimpleCommand( String.format( Locale.US, TOP_COMMAND + " %d %d", mUidToMsgNumMap.get(message.getUid()), lines)); // TOP command is supported. Remember this for the next time. mCapabilities.top = true; } catch (Pop3ErrorResponse e) { if (mCapabilities.top) { // The TOP command should be supported but something went wrong. throw e; } else { if (K9MailLib.isDebug() && DEBUG_PROTOCOL_POP3) { Log.d( LOG_TAG, "The server really doesn't support the TOP " + "command. Using RETR instead."); } // Don't try to use the TOP command again. mTopNotSupported = true; } } } if (response == null) { executeSimpleCommand( String.format(Locale.US, RETR_COMMAND + " %d", mUidToMsgNumMap.get(message.getUid()))); } try { message.parse(new Pop3ResponseInputStream(mIn)); // TODO: if we've received fewer lines than requested we also have the complete message. if (lines == -1 || !mCapabilities.top) { message.setFlag(Flag.X_DOWNLOADED_FULL, true); } } catch (MessagingException me) { /* * If we're only downloading headers it's possible * we'll get a broken MIME message which we're not * real worried about. If we've downloaded the body * and can't parse it we need to let the user know. */ if (lines == -1) { throw me; } } }
private void fetchEnvelope( List<Pop3Message> messages, MessageRetrievalListener<Pop3Message> listener) throws IOException, MessagingException { int unsizedMessages = 0; for (Message message : messages) { if (message.getSize() == -1) { unsizedMessages++; } } if (unsizedMessages == 0) { return; } if (unsizedMessages < 50 && mMessageCount > 5000) { /* * In extreme cases we'll do a command per message instead of a bulk request * to hopefully save some time and bandwidth. */ for (int i = 0, count = messages.size(); i < count; i++) { Pop3Message message = messages.get(i); if (listener != null) { listener.messageStarted(message.getUid(), i, count); } String response = executeSimpleCommand( String.format( Locale.US, LIST_COMMAND + " %d", mUidToMsgNumMap.get(message.getUid()))); String[] listParts = response.split(" "); // int msgNum = Integer.parseInt(listParts[1]); int msgSize = Integer.parseInt(listParts[2]); message.setSize(msgSize); if (listener != null) { listener.messageFinished(message, i, count); } } } else { Set<String> msgUidIndex = new HashSet<String>(); for (Message message : messages) { msgUidIndex.add(message.getUid()); } int i = 0, count = messages.size(); String response = executeSimpleCommand(LIST_COMMAND); while ((response = readLine()) != null) { if (response.equals(".")) { break; } String[] listParts = response.split(" "); int msgNum = Integer.parseInt(listParts[0]); int msgSize = Integer.parseInt(listParts[1]); Pop3Message pop3Message = mMsgNumToMsgMap.get(msgNum); if (pop3Message != null && msgUidIndex.contains(pop3Message.getUid())) { if (listener != null) { listener.messageStarted(pop3Message.getUid(), i, count); } pop3Message.setSize(msgSize); if (listener != null) { listener.messageFinished(pop3Message, i, count); } i++; } } } }
/** * Fetch the items contained in the FetchProfile into the given set of Messages in as efficient * a manner as possible. * * @param messages * @param fp * @throws MessagingException */ @Override public void fetch( List<Pop3Message> messages, FetchProfile fp, MessageRetrievalListener<Pop3Message> listener) throws MessagingException { if (messages == null || messages.isEmpty()) { return; } List<String> uids = new ArrayList<String>(); for (Message message : messages) { uids.add(message.getUid()); } try { indexUids(uids); } catch (IOException ioe) { throw new MessagingException("fetch", ioe); } try { if (fp.contains(FetchProfile.Item.ENVELOPE)) { /* * We pass the listener only if there are other things to do in the * FetchProfile. Since fetchEnvelop works in bulk and eveything else * works one at a time if we let fetchEnvelope send events the * event would get sent twice. */ fetchEnvelope(messages, fp.size() == 1 ? listener : null); } } catch (IOException ioe) { throw new MessagingException("fetch", ioe); } for (int i = 0, count = messages.size(); i < count; i++) { Pop3Message pop3Message = messages.get(i); try { if (listener != null && !fp.contains(FetchProfile.Item.ENVELOPE)) { listener.messageStarted(pop3Message.getUid(), i, count); } if (fp.contains(FetchProfile.Item.BODY)) { fetchBody(pop3Message, -1); } else if (fp.contains(FetchProfile.Item.BODY_SANE)) { /* * To convert the suggested download size we take the size * divided by the maximum line size (76). */ if (mStoreConfig.getMaximumAutoDownloadMessageSize() > 0) { fetchBody(pop3Message, (mStoreConfig.getMaximumAutoDownloadMessageSize() / 76)); } else { fetchBody(pop3Message, -1); } } else if (fp.contains(FetchProfile.Item.STRUCTURE)) { /* * If the user is requesting STRUCTURE we are required to set the body * to null since we do not support the function. */ pop3Message.setBody(null); } if (listener != null && !(fp.contains(FetchProfile.Item.ENVELOPE) && fp.size() == 1)) { listener.messageFinished(pop3Message, i, count); } } catch (IOException ioe) { throw new MessagingException("Unable to fetch message", ioe); } } }