void nsImapServerResponseParser::msg_fetch()

in mailnews/imap/src/nsImapServerResponseParser.cpp [910:1226]


void nsImapServerResponseParser::msg_fetch() {
  bool bNeedEndMessageDownload = false;

  // we have not seen a uid response or flags for this fetch, yet
  fCurrentResponseUID = 0;
  fCurrentLineContainedFlagInfo = false;
  fSizeOfMostRecentMessage = 0;
  // show any incremental progress, for instance, for header downloading
  fServerConnection.ShowProgress();

  fNextToken++;  // eat the '(' character

  // some of these productions are ignored for now
  while (ContinueParse() && (*fNextToken != ')')) {
    if (!PL_strcasecmp(fNextToken, "FLAGS")) {
      if (fCurrentResponseUID == 0)
        fFlagState->GetUidOfMessage(fFetchResponseIndex - 1,
                                    &fCurrentResponseUID);

      AdvanceToNextToken();
      if (ContinueParse()) flags();

      if (ContinueParse()) {  // eat the closing ')'
        fNextToken++;
        // there may be another ')' to close out
        // msg_fetch.  If there is then don't advance
        if (*fNextToken != ')') AdvanceToNextToken();
      }
    } else if (!PL_strcasecmp(fNextToken, "UID")) {
      AdvanceToNextToken();
      if (ContinueParse()) {
        fCurrentResponseUID = strtoul(fNextToken, nullptr, 10);
        if (fCurrentResponseUID > fHighestRecordedUID)
          fHighestRecordedUID = fCurrentResponseUID;
        // size came before UID
        if (fSizeOfMostRecentMessage)
          fReceivedHeaderOrSizeForUID = CurrentResponseUID();
        // if this token ends in ')', then it is the last token
        // else we advance
        char lastTokenChar = *(fNextToken + strlen(fNextToken) - 1);
        if (lastTokenChar == ')')
          fNextToken += strlen(fNextToken) - 1;
        else if (lastTokenChar < '0' || lastTokenChar > '9') {
          // GIANT HACK
          // this is a corrupt uid - see if it's pre 5.08 Zimbra omitting
          // a space between the UID and MODSEQ
          if (strlen(fNextToken) > 6 &&
              !strcmp("MODSEQ", fNextToken + strlen(fNextToken) - 6))
            fNextToken += strlen(fNextToken) - 6;
        } else
          AdvanceToNextToken();
      }
    } else if (!PL_strcasecmp(fNextToken, "MODSEQ")) {
      AdvanceToNextToken();
      if (ContinueParse()) {
        fNextToken++;  // eat '('
        uint64_t modSeq = ParseUint64Str(fNextToken);
        if (modSeq > fHighestModSeq) fHighestModSeq = modSeq;

        if (PL_strcasestr(fNextToken, ")")) {
          // eat token chars until we get the ')'
          fNextToken = strchr(fNextToken, ')');
          if (fNextToken) {
            fNextToken++;
            if (*fNextToken != ')') AdvanceToNextToken();
          } else
            SetSyntaxError(true);
        } else {
          SetSyntaxError(true);
        }
      }
    } else if (!PL_strcasecmp(fNextToken, "RFC822") ||
               !PL_strcasecmp(fNextToken, "RFC822.HEADER") ||
               !PL_strncasecmp(fNextToken, "BODY[HEADER", 11) ||
               !PL_strncasecmp(fNextToken, "BODY[]", 6) ||
               !PL_strcasecmp(fNextToken, "RFC822.TEXT") ||
               (!PL_strncasecmp(fNextToken, "BODY[", 5) &&
                PL_strstr(fNextToken, "HEADER"))) {
      if (fCurrentResponseUID == 0)
        fFlagState->GetUidOfMessage(fFetchResponseIndex - 1,
                                    &fCurrentResponseUID);

      if (!PL_strcasecmp(fNextToken, "RFC822.HEADER") ||
          !PL_strcasecmp(fNextToken, "BODY[HEADER]")) {
        // all of this message's headers
        AdvanceToNextToken();
        fDownloadingHeaders = true;
        BeginMessageDownload(MESSAGE_RFC822);  // initialize header parser
        bNeedEndMessageDownload = false;
        if (ContinueParse()) msg_fetch_headers(nullptr);
      } else if (!PL_strncasecmp(fNextToken, "BODY[HEADER.FIELDS", 19)) {
        fDownloadingHeaders = true;
        BeginMessageDownload(MESSAGE_RFC822);  // initialize header parser
        // specific message header fields
        while (ContinueParse() && fNextToken[strlen(fNextToken) - 1] != ']')
          AdvanceToNextToken();
        if (ContinueParse()) {
          bNeedEndMessageDownload = false;
          AdvanceToNextToken();
          if (ContinueParse()) msg_fetch_headers(nullptr);
        }
      } else {
        char* whereHeader = PL_strstr(fNextToken, "HEADER");
        if (whereHeader) {
          const char* startPartNum = fNextToken + 5;
          if (whereHeader > startPartNum) {
            int32_t partLength =
                whereHeader - startPartNum - 1;  //-1 for the dot!
            char* partNum = (char*)PR_CALLOC((partLength + 1) * sizeof(char));
            if (partNum) {
              PL_strncpy(partNum, startPartNum, partLength);
              if (ContinueParse()) {
                if (PL_strstr(fNextToken, "FIELDS")) {
                  while (ContinueParse() &&
                         fNextToken[strlen(fNextToken) - 1] != ']')
                    AdvanceToNextToken();
                }
                if (ContinueParse()) {
                  AdvanceToNextToken();
                  if (ContinueParse()) msg_fetch_headers(partNum);
                }
              }
              PR_Free(partNum);
            }
          } else
            SetSyntaxError(true);
        } else {
          fDownloadingHeaders = false;

          bool chunk = false;
          int32_t origin = 0;
          if (!PL_strncasecmp(fNextToken, "BODY[]<", 7)) {
            char* tokenCopy = 0;
            tokenCopy = PL_strdup(fNextToken);
            if (tokenCopy) {
              char* originString =
                  tokenCopy + 7;  // where the byte number starts
              char* closeBracket = PL_strchr(tokenCopy, '>');
              if (closeBracket && originString && *originString) {
                *closeBracket = 0;
                origin = atoi(originString);
                chunk = true;
              }
              PR_Free(tokenCopy);
            }
          }

          AdvanceToNextToken();
          if (ContinueParse()) {
            msg_fetch_content(chunk, origin, MESSAGE_RFC822);
          }
        }
      }
    } else if (!PL_strcasecmp(fNextToken, "RFC822.SIZE")) {
      AdvanceToNextToken();
      if (ContinueParse()) {
        bool sendEndMsgDownload =
            (GetDownloadingHeaders() &&
             fReceivedHeaderOrSizeForUID == CurrentResponseUID());
        fSizeOfMostRecentMessage = strtoul(fNextToken, nullptr, 10);
        fReceivedHeaderOrSizeForUID = CurrentResponseUID();
        if (sendEndMsgDownload) {
          fServerConnection.NormalMessageEndDownload();
          fReceivedHeaderOrSizeForUID = nsMsgKey_None;
        }

        if (fSizeOfMostRecentMessage == 0 && CurrentResponseUID()) {
          // on no, bogus Netscape 2.0 mail server bug
          if (!fZeroLengthMessageUidString.IsEmpty())
            fZeroLengthMessageUidString += ",";

          fZeroLengthMessageUidString.AppendInt(CurrentResponseUID());
        }

        // if this token ends in ')', then it is the last token
        // else we advance
        if (*(fNextToken + strlen(fNextToken) - 1) == ')')
          fNextToken += strlen(fNextToken) - 1;
        else
          AdvanceToNextToken();
      }
    } else if (!PL_strcasecmp(fNextToken, "XSENDER")) {
      PR_FREEIF(fXSenderInfo);
      AdvanceToNextToken();
      if (!fNextToken)
        SetSyntaxError(true);
      else {
        fXSenderInfo = CreateAstring();
        AdvanceToNextToken();
      }
    } else if (!PL_strcasecmp(fNextToken, "X-GM-MSGID")) {
      AdvanceToNextToken();
      if (!fNextToken)
        SetSyntaxError(true);
      else {
        fMsgID = CreateAtom();
        AdvanceToNextToken();
        nsCString msgIDValue;
        msgIDValue.Assign(fMsgID);
        if (fCurrentResponseUID == 0)
          fFlagState->GetUidOfMessage(fFetchResponseIndex - 1,
                                      &fCurrentResponseUID);
        fFlagState->SetCustomAttribute(fCurrentResponseUID, "X-GM-MSGID"_ns,
                                       msgIDValue);
        PR_FREEIF(fMsgID);
      }
    } else if (!PL_strcasecmp(fNextToken, "X-GM-THRID")) {
      AdvanceToNextToken();
      if (!fNextToken)
        SetSyntaxError(true);
      else {
        fThreadID = CreateAtom();
        AdvanceToNextToken();
        nsCString threadIDValue;
        threadIDValue.Assign(fThreadID);
        if (fCurrentResponseUID == 0)
          fFlagState->GetUidOfMessage(fFetchResponseIndex - 1,
                                      &fCurrentResponseUID);
        fFlagState->SetCustomAttribute(fCurrentResponseUID, "X-GM-THRID"_ns,
                                       threadIDValue);
        PR_FREEIF(fThreadID);
      }
    } else if (!PL_strcasecmp(fNextToken, "X-GM-LABELS")) {
      AdvanceToNextToken();
      if (!fNextToken)
        SetSyntaxError(true);
      else {
        fLabels = CreateParenGroup();
        nsCString labelsValue;
        labelsValue.Assign(fLabels);
        labelsValue.Cut(0, 1);
        labelsValue.Cut(labelsValue.Length() - 1, 1);
        if (fCurrentResponseUID == 0)
          fFlagState->GetUidOfMessage(fFetchResponseIndex - 1,
                                      &fCurrentResponseUID);
        fFlagState->SetCustomAttribute(fCurrentResponseUID, "X-GM-LABELS"_ns,
                                       labelsValue);
        PR_FREEIF(fLabels);
      }
    }

    // I only fetch RFC822 so I should never see these BODY responses
    else if (!PL_strcasecmp(fNextToken, "BODY"))
      skip_to_CRLF();  // I never ask for this
    else if (!PL_strncasecmp(fNextToken, "BODY[TEXT", 9)) {
      // This parses the "preview" response (first 2048 bytes of body text).
      mime_part_data();  // Note: TEXT is not an actual mime part.
    } else if (!PL_strcasecmp(fNextToken, "ENVELOPE")) {
      fDownloadingHeaders = true;
      bNeedEndMessageDownload = true;
      BeginMessageDownload(MESSAGE_RFC822);
      envelope_data();
    } else if (!PL_strcasecmp(fNextToken, "INTERNALDATE")) {
      fDownloadingHeaders =
          true;  // we only request internal date while downloading headers
      if (!bNeedEndMessageDownload) BeginMessageDownload(MESSAGE_RFC822);
      bNeedEndMessageDownload = true;
      internal_date();
    } else {
      nsImapAction imapAction;
      if (!fServerConnection.GetCurrentUrl()) return;
      fServerConnection.GetCurrentUrl()->GetImapAction(&imapAction);
      nsAutoCString userDefinedFetchAttribute;
      fServerConnection.GetCurrentUrl()->GetCustomAttributeToFetch(
          userDefinedFetchAttribute);
      if ((imapAction == nsIImapUrl::nsImapUserDefinedFetchAttribute &&
           !strcmp(userDefinedFetchAttribute.get(), fNextToken)) ||
          imapAction == nsIImapUrl::nsImapUserDefinedMsgCommand) {
        AdvanceToNextToken();
        char* fetchResult;
        if (fNextToken[0] == '(')
          // look through the tokens until we find the closing ')'
          // we can have a result like the following:
          // ((A B) (C D) (E F))
          fetchResult = CreateParenGroup();
        else {
          fetchResult = CreateAstring();
          AdvanceToNextToken();
        }
        if (imapAction == nsIImapUrl::nsImapUserDefinedFetchAttribute)
          fServerConnection.GetCurrentUrl()->SetCustomAttributeResult(
              nsDependentCString(fetchResult));
        if (imapAction == nsIImapUrl::nsImapUserDefinedMsgCommand)
          fServerConnection.GetCurrentUrl()->SetCustomCommandResult(
              nsDependentCString(fetchResult));
        PR_Free(fetchResult);
      } else
        SetSyntaxError(true);
    }
  }

  if (ContinueParse()) {
    if (CurrentResponseUID() && CurrentResponseUID() != nsMsgKey_None &&
        fCurrentLineContainedFlagInfo && fFlagState) {
      fFlagState->AddUidFlagPair(CurrentResponseUID(), fSavedFlagInfo,
                                 fFetchResponseIndex - 1);
      for (uint32_t i = 0; i < fCustomFlags.Length(); i++)
        fFlagState->AddUidCustomFlagPair(CurrentResponseUID(),
                                         fCustomFlags[i].get());
      fCustomFlags.Clear();
    }

    if (fFetchingAllFlags)
      fCurrentLineContainedFlagInfo =
          false;  // do not fire if in PostProcessEndOfLine

    AdvanceToNextToken();  // eat the ')' ending token
    // should be at end of line
    if (bNeedEndMessageDownload) {
      if (ContinueParse()) {
        // complete the message download
        fServerConnection.NormalMessageEndDownload();
      } else
        fServerConnection.AbortMessageDownLoad();
    }
  }
}