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