summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/imap/src/nsImapUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/imap/src/nsImapUtils.cpp')
-rw-r--r--comm/mailnews/imap/src/nsImapUtils.cpp336
1 files changed, 336 insertions, 0 deletions
diff --git a/comm/mailnews/imap/src/nsImapUtils.cpp b/comm/mailnews/imap/src/nsImapUtils.cpp
new file mode 100644
index 0000000000..9a5811511c
--- /dev/null
+++ b/comm/mailnews/imap/src/nsImapUtils.cpp
@@ -0,0 +1,336 @@
+/* -*- 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 "msgCore.h"
+#include "nsImapUtils.h"
+#include "nsCOMPtr.h"
+#include "prsystem.h"
+#include "prprf.h"
+#include "nsNetCID.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, nsCString& folderURI,
+ uint32_t* key, char** part) {
+ 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 (part && keyEndSeparator != -1) {
+ int32_t partPos = uriStr.Find("part=", keyEndSeparator);
+ if (partPos != -1) {
+ *part = ToNewCString(Substring(uriStr, keyEndSeparator));
+ }
+ }
+ }
+ return NS_OK;
+}
+
+nsresult nsBuildImapMessageURI(const char* baseURI, uint32_t 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() {
+ 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);
+}