From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- comm/mailnews/imap/src/nsImapUtils.cpp | 336 +++++++++++++++++++++++++++++++++ 1 file changed, 336 insertions(+) create mode 100644 comm/mailnews/imap/src/nsImapUtils.cpp (limited to 'comm/mailnews/imap/src/nsImapUtils.cpp') 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& keys) { + // This is in the form ,, or : + 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); +} -- cgit v1.2.3