Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsImapUtils.h"
#include "nsCOMPtr.h"
#include "prsystem.h"
#include "prprf.h"
#include "nsMsgUtils.h"
#include "nsImapFlagAndUidState.h"
#include "nsImapNamespace.h"
#include "nsIImapFlagAndUidState.h"
nsresult nsImapURI2FullName(const char* rootURI, const char* hostName,
const char* uriStr, char** name) {
nsAutoCString uri(uriStr);
nsAutoCString fullName;
if (uri.Find(rootURI) != 0) return NS_ERROR_FAILURE;
fullName = Substring(uri, strlen(rootURI));
uri = fullName;
int32_t hostStart = uri.Find(hostName);
if (hostStart <= 0) return NS_ERROR_FAILURE;
fullName = Substring(uri, hostStart);
uri = fullName;
int32_t hostEnd = uri.FindChar('/');
if (hostEnd <= 0) return NS_ERROR_FAILURE;
fullName = Substring(uri, hostEnd + 1);
if (fullName.IsEmpty()) return NS_ERROR_FAILURE;
*name = ToNewCString(fullName);
return NS_OK;
}
/* parses ImapMessageURI */
nsresult nsParseImapMessageURI(const nsACString& uri, nsACString& folderURI,
nsMsgKey* key, nsACString& mimePart) {
if (!key) return NS_ERROR_NULL_POINTER;
const nsPromiseFlatCString& uriStr = PromiseFlatCString(uri);
int32_t folderEnd = -1;
// imap-message uri's can have imap:// url strings tacked on the end,
// e.g., when opening/saving attachments. We don't want to look for '#'
// in that part of the uri, if the attachment name contains '#',
// so check for that here.
if (StringBeginsWith(uriStr, "imap-message"_ns)) {
folderEnd = uriStr.Find("imap://");
}
int32_t keySeparator = uriStr.RFindChar('#', folderEnd);
if (keySeparator != -1) {
int32_t keyEndSeparator = MsgFindCharInSet(uriStr, "/?&", keySeparator);
nsAutoString folderPath;
folderURI = StringHead(uriStr, keySeparator);
folderURI.Cut(4, 8); // cut out the _message part of imap-message:
// folder uri's don't have fully escaped usernames.
int32_t atPos = folderURI.FindChar('@');
if (atPos != -1) {
nsCString unescapedName, escapedName;
int32_t userNamePos = folderURI.Find("//") + 2;
uint32_t origUserNameLen = atPos - userNamePos;
if (NS_SUCCEEDED(MsgUnescapeString(
Substring(folderURI, userNamePos, origUserNameLen), 0,
unescapedName))) {
// Re-escape the username, matching the way we do it in uris, not the
// way necko escapes urls. See nsMsgIncomingServer::GetServerURI.
MsgEscapeString(unescapedName, nsINetUtil::ESCAPE_XALPHAS, escapedName);
folderURI.Replace(userNamePos, origUserNameLen, escapedName);
}
}
nsAutoCString keyStr;
if (keyEndSeparator != -1) {
keyStr = Substring(uriStr, keySeparator + 1,
keyEndSeparator - (keySeparator + 1));
} else {
keyStr = Substring(uriStr, keySeparator + 1);
}
*key = strtoul(keyStr.get(), nullptr, 10);
if (keyEndSeparator != -1) {
int32_t partPos = uriStr.Find("part=", keyEndSeparator);
if (partPos != -1) {
mimePart = Substring(uriStr, keyEndSeparator);
}
}
}
return NS_OK;
}
nsresult nsBuildImapMessageURI(const char* baseURI, nsMsgKey key,
nsACString& uri) {
uri.Append(baseURI);
uri.Append('#');
uri.AppendInt(key);
return NS_OK;
}
nsresult nsCreateImapBaseMessageURI(const nsACString& baseURI,
nsCString& baseMessageURI) {
nsAutoCString tailURI(baseURI);
// chop off imap:/
if (tailURI.Find(kImapRootURI) == 0) tailURI.Cut(0, PL_strlen(kImapRootURI));
baseMessageURI = kImapMessageRootURI;
baseMessageURI += tailURI;
return NS_OK;
}
// nsImapMailboxSpec definition
NS_IMPL_ISUPPORTS(nsImapMailboxSpec, nsIMailboxSpec)
nsImapMailboxSpec::nsImapMailboxSpec() {
mConnection = nullptr;
mFolder_UIDVALIDITY = 0;
mHighestModSeq = 0;
mNumOfMessages = 0;
mNumOfUnseenMessages = 0;
mNumOfRecentMessages = 0;
mNextUID = 0;
mBoxFlags = 0;
mSupportedUserFlags = 0;
mHierarchySeparator = '\0';
mFolderSelected = false;
mDiscoveredFromLsub = false;
mOnlineVerified = false;
mNamespaceForFolder = nullptr;
}
nsImapMailboxSpec::~nsImapMailboxSpec() {}
NS_IMPL_GETSET(nsImapMailboxSpec, Folder_UIDVALIDITY, int32_t,
mFolder_UIDVALIDITY)
NS_IMPL_GETSET(nsImapMailboxSpec, HighestModSeq, uint64_t, mHighestModSeq)
NS_IMPL_GETSET(nsImapMailboxSpec, NumMessages, int32_t, mNumOfMessages)
NS_IMPL_GETSET(nsImapMailboxSpec, NumUnseenMessages, int32_t,
mNumOfUnseenMessages)
NS_IMPL_GETSET(nsImapMailboxSpec, NumRecentMessages, int32_t,
mNumOfRecentMessages)
NS_IMPL_GETSET(nsImapMailboxSpec, NextUID, int32_t, mNextUID)
NS_IMPL_GETSET(nsImapMailboxSpec, HierarchyDelimiter, char, mHierarchySeparator)
NS_IMPL_GETSET(nsImapMailboxSpec, FolderSelected, bool, mFolderSelected)
NS_IMPL_GETSET(nsImapMailboxSpec, DiscoveredFromLsub, bool, mDiscoveredFromLsub)
NS_IMPL_GETSET(nsImapMailboxSpec, OnlineVerified, bool, mOnlineVerified)
NS_IMPL_GETSET(nsImapMailboxSpec, SupportedUserFlags, uint32_t,
mSupportedUserFlags)
NS_IMPL_GETSET(nsImapMailboxSpec, Box_flags, uint32_t, mBoxFlags)
NS_IMPL_GETSET(nsImapMailboxSpec, NamespaceForFolder, nsImapNamespace*,
mNamespaceForFolder)
NS_IMETHODIMP nsImapMailboxSpec::GetAllocatedPathName(
nsACString& aAllocatedPathName) {
aAllocatedPathName = mAllocatedPathName;
return NS_OK;
}
NS_IMETHODIMP nsImapMailboxSpec::SetAllocatedPathName(
const nsACString& aAllocatedPathName) {
mAllocatedPathName = aAllocatedPathName;
return NS_OK;
}
NS_IMETHODIMP nsImapMailboxSpec::GetUnicharPathName(
nsAString& aUnicharPathName) {
aUnicharPathName = mUnicharPathName;
return NS_OK;
}
NS_IMETHODIMP nsImapMailboxSpec::SetUnicharPathName(
const nsAString& aUnicharPathName) {
mUnicharPathName = aUnicharPathName;
return NS_OK;
}
NS_IMETHODIMP nsImapMailboxSpec::GetHostName(nsACString& aHostName) {
aHostName = mHostName;
return NS_OK;
}
NS_IMETHODIMP nsImapMailboxSpec::SetHostName(const nsACString& aHostName) {
mHostName = aHostName;
return NS_OK;
}
NS_IMETHODIMP nsImapMailboxSpec::GetFlagState(
nsIImapFlagAndUidState** aFlagState) {
NS_ENSURE_ARG_POINTER(aFlagState);
NS_IF_ADDREF(*aFlagState = mFlagState);
return NS_OK;
}
NS_IMETHODIMP nsImapMailboxSpec::SetFlagState(
nsIImapFlagAndUidState* aFlagState) {
NS_ENSURE_ARG_POINTER(aFlagState);
mFlagState = aFlagState;
return NS_OK;
}
nsImapMailboxSpec& nsImapMailboxSpec::operator=(
const nsImapMailboxSpec& aCopy) {
mFolder_UIDVALIDITY = aCopy.mFolder_UIDVALIDITY;
mHighestModSeq = aCopy.mHighestModSeq;
mNumOfMessages = aCopy.mNumOfMessages;
mNumOfUnseenMessages = aCopy.mNumOfUnseenMessages;
mNumOfRecentMessages = aCopy.mNumOfRecentMessages;
mBoxFlags = aCopy.mBoxFlags;
mSupportedUserFlags = aCopy.mSupportedUserFlags;
mAllocatedPathName.Assign(aCopy.mAllocatedPathName);
mUnicharPathName.Assign(aCopy.mUnicharPathName);
mHostName.Assign(aCopy.mHostName);
mFlagState = aCopy.mFlagState;
mNamespaceForFolder = aCopy.mNamespaceForFolder;
mFolderSelected = aCopy.mFolderSelected;
mDiscoveredFromLsub = aCopy.mDiscoveredFromLsub;
mOnlineVerified = aCopy.mOnlineVerified;
return *this;
}
// use the flagState to determine if the gaps in the msgUids correspond to gaps
// in the mailbox, in which case we can still use ranges. If flagState is null,
// we won't do this.
void AllocateImapUidString(const uint32_t* msgUids, uint32_t& msgCount,
nsImapFlagAndUidState* flagState,
nsCString& returnString) {
uint32_t startSequence = (msgCount > 0) ? msgUids[0] : 0xFFFFFFFF;
uint32_t curSequenceEnd = startSequence;
uint32_t total = msgCount;
int32_t curFlagStateIndex = -1;
// a partial fetch flag state doesn't help us, so don't use it.
if (flagState && flagState->GetPartialUIDFetch()) flagState = nullptr;
for (uint32_t keyIndex = 0; keyIndex < total; keyIndex++) {
uint32_t curKey = msgUids[keyIndex];
uint32_t nextKey =
(keyIndex + 1 < total) ? msgUids[keyIndex + 1] : 0xFFFFFFFF;
bool lastKey = (nextKey == 0xFFFFFFFF);
if (lastKey) curSequenceEnd = curKey;
if (!lastKey) {
if (nextKey == curSequenceEnd + 1) {
curSequenceEnd = nextKey;
curFlagStateIndex++;
continue;
}
if (flagState) {
if (curFlagStateIndex == -1) {
bool foundIt;
flagState->GetMessageFlagsFromUID(curSequenceEnd, &foundIt,
&curFlagStateIndex);
if (!foundIt) {
NS_WARNING("flag state missing key");
// The start of this sequence is missing from flag state, so move
// on to the next key.
curFlagStateIndex = -1;
curSequenceEnd = startSequence = nextKey;
continue;
}
}
curFlagStateIndex++;
uint32_t nextUidInFlagState;
nsresult rv =
flagState->GetUidOfMessage(curFlagStateIndex, &nextUidInFlagState);
if (NS_SUCCEEDED(rv) && nextUidInFlagState == nextKey) {
curSequenceEnd = nextKey;
continue;
}
}
}
if (curSequenceEnd > startSequence) {
returnString.AppendInt((int64_t)startSequence);
returnString += ':';
returnString.AppendInt((int64_t)curSequenceEnd);
startSequence = nextKey;
curSequenceEnd = startSequence;
curFlagStateIndex = -1;
} else {
startSequence = nextKey;
curSequenceEnd = startSequence;
returnString.AppendInt((int64_t)msgUids[keyIndex]);
curFlagStateIndex = -1;
}
// check if we've generated too long a string - if there's no flag state,
// it means we just need to go ahead and generate a too long string
// because the calling code won't handle breaking up the strings.
if (flagState && returnString.Length() > 950) {
msgCount = keyIndex;
break;
}
// If we are not the last item then we need to add the comma
// but it's important we do it here, after the length check
if (!lastKey) returnString += ',';
}
}
void ParseUidString(const char* uidString, nsTArray<nsMsgKey>& keys) {
// This is in the form <id>,<id>, or <id1>:<id2>
if (!uidString) return;
char curChar = *uidString;
bool isRange = false;
uint32_t curToken;
uint32_t saveStartToken = 0;
for (const char* curCharPtr = uidString; curChar && *curCharPtr;) {
const char* currentKeyToken = curCharPtr;
curChar = *curCharPtr;
while (curChar != ':' && curChar != ',' && curChar != '\0') {
curChar = *curCharPtr++;
}
// we don't need to null terminate currentKeyToken because strtoul
// stops at non-numeric chars.
curToken = strtoul(currentKeyToken, nullptr, 10);
if (isRange) {
while (saveStartToken < curToken) keys.AppendElement(saveStartToken++);
}
keys.AppendElement(curToken);
isRange = (curChar == ':');
if (isRange) saveStartToken = curToken + 1;
}
}
void AppendUid(nsCString& msgIds, uint32_t uid) {
char buf[20];
PR_snprintf(buf, sizeof(buf), "%u", uid);
msgIds.Append(buf);
}