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);
     }
   }
 }