in mailnews/search/src/nsMsgSearchAdapter.cpp [214:539]
nsresult nsMsgSearchAdapter::EncodeImapTerm(nsIMsgSearchTerm* term,
bool reallyDredd,
const char16_t* srcCharset,
const char16_t* destCharset,
char** ppOutTerm) {
NS_ENSURE_ARG_POINTER(term);
NS_ENSURE_ARG_POINTER(ppOutTerm);
nsresult err = NS_OK;
bool useNot = false;
bool useQuotes = false;
bool ignoreValue = false;
nsAutoCString arbitraryHeader;
const char* whichMnemonic = nullptr;
const char* orHeaderMnemonic = nullptr;
*ppOutTerm = nullptr;
nsCOMPtr<nsIMsgSearchValue> searchValue;
nsresult rv = term->GetValue(getter_AddRefs(searchValue));
NS_ENSURE_SUCCESS(rv, rv);
nsMsgSearchOpValue op;
term->GetOp(&op);
if (op == nsMsgSearchOp::DoesntContain || op == nsMsgSearchOp::Isnt)
useNot = true;
nsMsgSearchAttribValue attrib;
term->GetAttrib(&attrib);
switch (attrib) {
case nsMsgSearchAttrib::ToOrCC:
orHeaderMnemonic = m_kImapCC;
// fall through to case nsMsgSearchAttrib::To:
[[fallthrough]];
case nsMsgSearchAttrib::To:
whichMnemonic = m_kImapTo;
break;
case nsMsgSearchAttrib::CC:
whichMnemonic = m_kImapCC;
break;
case nsMsgSearchAttrib::Sender:
whichMnemonic = m_kImapFrom;
break;
case nsMsgSearchAttrib::Subject:
whichMnemonic = m_kImapSubject;
break;
case nsMsgSearchAttrib::Body:
whichMnemonic = m_kImapBody;
break;
case nsMsgSearchAttrib::AgeInDays: // added for searching online for age in
// days...
// for AgeInDays, we are actually going to perform a search by date, so
// convert the operations for age to the IMAP mnemonics that we would use
// for date!
{
// If we have a future date, the > and < are reversed.
// e.g. ageInDays > 2 means more than 2 days old ("date before X")
// whereas
// ageInDays > -2 should be more than 2 days in the future ("date
// after X")
int32_t ageInDays;
searchValue->GetAge(&ageInDays);
bool dateInFuture = (ageInDays < 0);
switch (op) {
case nsMsgSearchOp::IsGreaterThan:
whichMnemonic = (!dateInFuture) ? m_kImapBefore : m_kImapSince;
break;
case nsMsgSearchOp::IsLessThan:
whichMnemonic = (!dateInFuture) ? m_kImapSince : m_kImapBefore;
break;
case nsMsgSearchOp::Is:
whichMnemonic = m_kImapSentOn;
break;
default:
NS_ASSERTION(false, "invalid search operator");
return NS_ERROR_INVALID_ARG;
}
}
break;
case nsMsgSearchAttrib::Size:
switch (op) {
case nsMsgSearchOp::IsGreaterThan:
whichMnemonic = m_kImapSizeLarger;
break;
case nsMsgSearchOp::IsLessThan:
whichMnemonic = m_kImapSizeSmaller;
break;
default:
NS_ASSERTION(false, "invalid search operator");
return NS_ERROR_INVALID_ARG;
}
break;
case nsMsgSearchAttrib::Date:
switch (op) {
case nsMsgSearchOp::IsBefore:
whichMnemonic = m_kImapBefore;
break;
case nsMsgSearchOp::IsAfter:
whichMnemonic = m_kImapSince;
break;
case nsMsgSearchOp::Isnt: /* we've already added the "Not" so just
process it like it was a date is search */
case nsMsgSearchOp::Is:
whichMnemonic = m_kImapSentOn;
break;
default:
NS_ASSERTION(false, "invalid search operator");
return NS_ERROR_INVALID_ARG;
}
break;
case nsMsgSearchAttrib::AnyText:
whichMnemonic = m_kImapAnyText;
break;
case nsMsgSearchAttrib::Keywords:
whichMnemonic = m_kImapKeyword;
break;
case nsMsgSearchAttrib::MsgStatus:
useNot = false; // bizarrely, NOT SEEN is wrong, but UNSEEN is right.
ignoreValue = true; // the mnemonic is all we need
uint32_t status;
searchValue->GetStatus(&status);
switch (status) {
case nsMsgMessageFlags::Read:
whichMnemonic =
op == nsMsgSearchOp::Is ? m_kImapSeen : m_kImapNotSeen;
break;
case nsMsgMessageFlags::Replied:
whichMnemonic =
op == nsMsgSearchOp::Is ? m_kImapAnswered : m_kImapNotAnswered;
break;
case nsMsgMessageFlags::New:
whichMnemonic = op == nsMsgSearchOp::Is ? m_kImapNew : m_kImapNotNew;
break;
case nsMsgMessageFlags::Marked:
whichMnemonic =
op == nsMsgSearchOp::Is ? m_kImapFlagged : m_kImapNotFlagged;
break;
default:
NS_ASSERTION(false, "invalid search operator");
return NS_ERROR_INVALID_ARG;
}
break;
default:
if (attrib > nsMsgSearchAttrib::OtherHeader &&
attrib < nsMsgSearchAttrib::kNumMsgSearchAttributes) {
nsCString arbitraryHeaderTerm;
term->GetArbitraryHeader(arbitraryHeaderTerm);
if (!arbitraryHeaderTerm.IsEmpty()) {
arbitraryHeader.AssignLiteral(" \"");
arbitraryHeader.Append(arbitraryHeaderTerm);
arbitraryHeader.AppendLiteral("\" ");
whichMnemonic = arbitraryHeader.get();
} else
return NS_ERROR_FAILURE;
} else {
NS_ASSERTION(false, "invalid search operator");
return NS_ERROR_INVALID_ARG;
}
}
char* value = nullptr;
char dateBuf[100];
dateBuf[0] = '\0';
bool valueWasAllocated = false;
if (attrib == nsMsgSearchAttrib::Date) {
// note that there used to be code here that encoded an RFC822 date for imap
// searches. The IMAP RFC 2060 is misleading to the point that it looks like
// it requires an RFC822 date but really it expects dd-mmm-yyyy, like dredd,
// and refers to the RFC822 date only in that the dd-mmm-yyyy date will
// match the RFC822 date within the message.
PRTime adjustedDate;
searchValue->GetDate(&adjustedDate);
if (whichMnemonic == m_kImapSince) {
// it looks like the IMAP server searches on Since includes the date in
// question... our UI presents Is, IsGreater and IsLessThan. For the
// IsGreater case (m_kImapSince) we need to adjust the date so we get
// greater than and not greater than or equal to which is what the IMAP
// server wants to search on won't work on Mac.
adjustedDate += PR_USEC_PER_DAY;
}
PRExplodedTime exploded;
PR_ExplodeTime(adjustedDate, PR_LocalTimeParameters, &exploded);
PR_FormatTimeUSEnglish(dateBuf, sizeof(dateBuf), "%d-%b-%Y", &exploded);
// strftime (dateBuf, sizeof(dateBuf), "%d-%b-%Y", localtime (/*
// &term->m_value.u.date */ &adjustedDate));
value = dateBuf;
} else {
if (attrib == nsMsgSearchAttrib::AgeInDays) {
// okay, take the current date, subtract off the age in days, then do an
// appropriate Date search on the resulting day.
int32_t ageInDays;
searchValue->GetAge(&ageInDays);
PRTime now = PR_Now();
PRTime matchDay = now - ageInDays * PR_USEC_PER_DAY;
PRExplodedTime exploded;
PR_ExplodeTime(matchDay, PR_LocalTimeParameters, &exploded);
PR_FormatTimeUSEnglish(dateBuf, sizeof(dateBuf), "%d-%b-%Y", &exploded);
// strftime (dateBuf, sizeof(dateBuf), "%d-%b-%Y", localtime
// (&matchDay));
value = dateBuf;
} else if (attrib == nsMsgSearchAttrib::Size) {
uint32_t sizeValue;
nsAutoCString searchTermValue;
searchValue->GetSize(&sizeValue);
// Multiply by 1024 to get into kb resolution
sizeValue *= 1024;
// Ensure that greater than is really greater than
// in kb resolution.
if (op == nsMsgSearchOp::IsGreaterThan) sizeValue += 1024;
searchTermValue.AppendInt(sizeValue);
value = ToNewCString(searchTermValue);
valueWasAllocated = true;
} else
if (IS_STRING_ATTRIBUTE(attrib)) {
char16_t*
convertedValue; // = reallyDredd ? MSG_EscapeSearchUrl
// (term->m_value.u.string) :
// msg_EscapeImapSearchProtocol(term->m_value.u.string);
nsString searchTermValue;
searchValue->GetStr(searchTermValue);
// Ugly switch for Korean mail/news charsets.
// We want to do this here because here is where
// we know what charset we want to use.
#ifdef DOING_CHARSET
if (reallyDredd)
dest_csid = INTL_DefaultNewsCharSetID(dest_csid);
else
dest_csid = INTL_DefaultMailCharSetID(dest_csid);
#endif
// do all sorts of crazy escaping
convertedValue = reallyDredd
? EscapeSearchUrl(searchTermValue.get())
: EscapeImapSearchProtocol(searchTermValue.get());
useQuotes =
((!reallyDredd ||
(nsDependentString(convertedValue).FindChar(char16_t(' ')) !=
-1)) &&
(attrib != nsMsgSearchAttrib::Keywords));
// now convert to char* and escape quoted_specials
nsAutoCString valueStr;
nsresult rv = nsMsgI18NConvertFromUnicode(
NS_LossyConvertUTF16toASCII(destCharset),
nsDependentString(convertedValue), valueStr);
if (NS_SUCCEEDED(rv)) {
const char* vptr = valueStr.get();
// max escaped length is one extra character for every character in the
// cmd.
mozilla::UniquePtr<char[]> newValue =
mozilla::MakeUnique<char[]>(2 * strlen(vptr) + 1);
if (newValue) {
char* p = newValue.get();
while (1) {
char ch = *vptr++;
if (!ch) break;
if ((useQuotes ? ch == '"' : 0) || ch == '\\') *p++ = '\\';
*p++ = ch;
}
*p = '\0';
value = strdup(newValue.get()); // realloc down to smaller size
}
} else
value = strdup("");
free(convertedValue);
valueWasAllocated = true;
}
}
// this should be rewritten to use nsCString
int subLen = (value ? strlen(value) : 0) + (useNot ? strlen(m_kImapNot) : 0) +
strlen(m_kImapHeader);
int len =
strlen(whichMnemonic) + subLen + (useQuotes ? 2 : 0) +
(orHeaderMnemonic
? (subLen + strlen(m_kImapOr) + strlen(orHeaderMnemonic) + 2 /*""*/)
: 0) +
10; // add slough for imap string literals
char* encoding = new char[len];
if (encoding) {
encoding[0] = '\0';
// Remember: if ToOrCC and useNot then the expression becomes NOT To AND Not
// CC as opposed to (NOT TO) || (NOT CC)
if (orHeaderMnemonic && !useNot) PL_strcat(encoding, m_kImapOr);
if (useNot) PL_strcat(encoding, m_kImapNot);
if (!arbitraryHeader.IsEmpty()) PL_strcat(encoding, m_kImapHeader);
PL_strcat(encoding, whichMnemonic);
if (!ignoreValue)
err = EncodeImapValue(encoding, value, useQuotes, reallyDredd);
if (orHeaderMnemonic) {
if (useNot) PL_strcat(encoding, m_kImapNot);
PL_strcat(encoding, m_kImapHeader);
PL_strcat(encoding, orHeaderMnemonic);
if (!ignoreValue)
err = EncodeImapValue(encoding, value, useQuotes, reallyDredd);
}
// kmcentee, don't let the encoding end with whitespace,
// this throws off later url STRCMP
if (*encoding && *(encoding + strlen(encoding) - 1) == ' ')
*(encoding + strlen(encoding) - 1) = '\0';
}
if (value && valueWasAllocated) free(value);
*ppOutTerm = encoding;
return err;
}