summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/imap/src/nsImapNamespace.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/imap/src/nsImapNamespace.cpp')
-rw-r--r--comm/mailnews/imap/src/nsImapNamespace.cpp513
1 files changed, 513 insertions, 0 deletions
diff --git a/comm/mailnews/imap/src/nsImapNamespace.cpp b/comm/mailnews/imap/src/nsImapNamespace.cpp
new file mode 100644
index 0000000000..4fd2dcf7db
--- /dev/null
+++ b/comm/mailnews/imap/src/nsImapNamespace.cpp
@@ -0,0 +1,513 @@
+/* -*- 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" // for pre-compiled headers
+
+#include "nsImapCore.h"
+#include "nsImapNamespace.h"
+#include "../public/nsIImapHostSessionList.h"
+#include "nsImapUrl.h"
+#include "nsString.h"
+#include "nsServiceManagerUtils.h"
+
+//////////////////// nsImapNamespace
+////////////////////////////////////////////////////////////////
+
+#define NS_IIMAPHOSTSESSIONLIST_CID \
+ { \
+ 0x479ce8fc, 0xe725, 0x11d2, { \
+ 0xa5, 0x05, 0x00, 0x60, 0xb0, 0xfc, 0x04, 0xb7 \
+ } \
+ }
+static NS_DEFINE_CID(kCImapHostSessionListCID, NS_IIMAPHOSTSESSIONLIST_CID);
+
+nsImapNamespace::nsImapNamespace(EIMAPNamespaceType type, const char* prefix,
+ char delimiter, bool from_prefs) {
+ m_namespaceType = type;
+ m_prefix = PL_strdup(prefix);
+ m_fromPrefs = from_prefs;
+
+ m_delimiter = delimiter;
+ m_delimiterFilledIn =
+ !m_fromPrefs; // if it's from the prefs, we can't be sure about the
+ // delimiter until we list it.
+}
+
+nsImapNamespace::~nsImapNamespace() { PR_FREEIF(m_prefix); }
+
+void nsImapNamespace::SetDelimiter(char delimiter, bool delimiterFilledIn) {
+ m_delimiter = delimiter;
+ m_delimiterFilledIn = delimiterFilledIn;
+}
+
+// returns -1 if this box is not part of this namespace,
+// or the length of the prefix if it is part of this namespace
+int nsImapNamespace::MailboxMatchesNamespace(const char* boxname) {
+ if (!boxname) return -1;
+
+ // If the namespace is part of the boxname
+ if (!m_prefix || !*m_prefix) return 0;
+
+ if (PL_strstr(boxname, m_prefix) == boxname) return PL_strlen(m_prefix);
+
+ // If the boxname is part of the prefix
+ // (Used for matching Personal mailbox with Personal/ namespace, etc.)
+ if (PL_strstr(m_prefix, boxname) == m_prefix) return PL_strlen(boxname);
+ return -1;
+}
+
+nsImapNamespaceList* nsImapNamespaceList::CreatensImapNamespaceList() {
+ nsImapNamespaceList* rv = new nsImapNamespaceList();
+ return rv;
+}
+
+nsImapNamespaceList::nsImapNamespaceList() {}
+
+int nsImapNamespaceList::GetNumberOfNamespaces() {
+ return m_NamespaceList.Length();
+}
+
+nsresult nsImapNamespaceList::InitFromString(const char* nameSpaceString,
+ EIMAPNamespaceType nstype) {
+ nsresult rv = NS_OK;
+ if (nameSpaceString) {
+ int numNamespaces = UnserializeNamespaces(nameSpaceString, nullptr, 0);
+ char** prefixes = (char**)PR_CALLOC(numNamespaces * sizeof(char*));
+ if (prefixes) {
+ int len = UnserializeNamespaces(nameSpaceString, prefixes, numNamespaces);
+ for (int i = 0; i < len; i++) {
+ char* thisns = prefixes[i];
+ char delimiter = '/'; // a guess
+ if (PL_strlen(thisns) >= 1) delimiter = thisns[PL_strlen(thisns) - 1];
+ nsImapNamespace* ns =
+ new nsImapNamespace(nstype, thisns, delimiter, true);
+ if (ns) AddNewNamespace(ns);
+ PR_FREEIF(thisns);
+ }
+ PR_Free(prefixes);
+ }
+ }
+
+ return rv;
+}
+
+nsresult nsImapNamespaceList::OutputToString(nsCString& string) {
+ nsresult rv = NS_OK;
+ return rv;
+}
+
+int nsImapNamespaceList::GetNumberOfNamespaces(EIMAPNamespaceType type) {
+ int nodeIndex = 0, count = 0;
+ for (nodeIndex = m_NamespaceList.Length() - 1; nodeIndex >= 0; nodeIndex--) {
+ nsImapNamespace* nspace = m_NamespaceList.ElementAt(nodeIndex);
+ if (nspace->GetType() == type) {
+ count++;
+ }
+ }
+ return count;
+}
+
+int nsImapNamespaceList::AddNewNamespace(nsImapNamespace* ns) {
+ // If the namespace is from the NAMESPACE response, then we should see if
+ // there are any namespaces previously set by the preferences, or the default
+ // namespace. If so, remove these.
+
+ if (!ns->GetIsNamespaceFromPrefs()) {
+ int nodeIndex;
+ // iterate backwards because we delete elements
+ for (nodeIndex = m_NamespaceList.Length() - 1; nodeIndex >= 0;
+ nodeIndex--) {
+ nsImapNamespace* nspace = m_NamespaceList.ElementAt(nodeIndex);
+ // if we find existing namespace(s) that matches the
+ // new one, we'll just remove the old ones and let the
+ // new one get added when we've finished checking for
+ // matching namespaces or namespaces that came from prefs.
+ if (nspace && (nspace->GetIsNamespaceFromPrefs() ||
+ (!PL_strcmp(ns->GetPrefix(), nspace->GetPrefix()) &&
+ ns->GetType() == nspace->GetType() &&
+ ns->GetDelimiter() == nspace->GetDelimiter()))) {
+ m_NamespaceList.RemoveElementAt(nodeIndex);
+ delete nspace;
+ }
+ }
+ }
+
+ // Add the new namespace to the list. This must come after the removing code,
+ // or else we could never add the initial kDefaultNamespace type to the list.
+ m_NamespaceList.AppendElement(ns);
+
+ return 0;
+}
+
+// chrisf - later, fix this to know the real concept of "default" namespace of a
+// given type
+nsImapNamespace* nsImapNamespaceList::GetDefaultNamespaceOfType(
+ EIMAPNamespaceType type) {
+ nsImapNamespace *rv = 0, *firstOfType = 0;
+
+ int nodeIndex, count = m_NamespaceList.Length();
+ for (nodeIndex = 0; nodeIndex < count && !rv; nodeIndex++) {
+ nsImapNamespace* ns = m_NamespaceList.ElementAt(nodeIndex);
+ if (ns->GetType() == type) {
+ if (!firstOfType) firstOfType = ns;
+ if (!(*(ns->GetPrefix()))) {
+ // This namespace's prefix is ""
+ // Therefore it is the default
+ rv = ns;
+ }
+ }
+ }
+ if (!rv) rv = firstOfType;
+ return rv;
+}
+
+nsImapNamespaceList::~nsImapNamespaceList() {
+ ClearNamespaces(true, true, true);
+}
+
+// ClearNamespaces removes and deletes the namespaces specified, and if there
+// are no namespaces left,
+void nsImapNamespaceList::ClearNamespaces(bool deleteFromPrefsNamespaces,
+ bool deleteServerAdvertisedNamespaces,
+ bool reallyDelete) {
+ int nodeIndex;
+
+ // iterate backwards because we delete elements
+ for (nodeIndex = m_NamespaceList.Length() - 1; nodeIndex >= 0; nodeIndex--) {
+ nsImapNamespace* ns = m_NamespaceList.ElementAt(nodeIndex);
+ if (ns->GetIsNamespaceFromPrefs()) {
+ if (deleteFromPrefsNamespaces) {
+ m_NamespaceList.RemoveElementAt(nodeIndex);
+ if (reallyDelete) delete ns;
+ }
+ } else if (deleteServerAdvertisedNamespaces) {
+ m_NamespaceList.RemoveElementAt(nodeIndex);
+ if (reallyDelete) delete ns;
+ }
+ }
+}
+
+nsImapNamespace* nsImapNamespaceList::GetNamespaceNumber(int nodeIndex) {
+ NS_ASSERTION(nodeIndex >= 0 && nodeIndex < GetNumberOfNamespaces(),
+ "invalid IMAP namespace node index");
+ if (nodeIndex < 0) nodeIndex = 0;
+
+ // XXX really could be just ElementAt; that's why we have the assertion
+ return m_NamespaceList.SafeElementAt(nodeIndex);
+}
+
+nsImapNamespace* nsImapNamespaceList::GetNamespaceNumber(
+ int nodeIndex, EIMAPNamespaceType type) {
+ int nodeCount, count = 0;
+ for (nodeCount = m_NamespaceList.Length() - 1; nodeCount >= 0; nodeCount--) {
+ nsImapNamespace* nspace = m_NamespaceList.ElementAt(nodeCount);
+ if (nspace->GetType() == type) {
+ count++;
+ if (count == nodeIndex) return nspace;
+ }
+ }
+ return nullptr;
+}
+
+nsImapNamespace* nsImapNamespaceList::GetNamespaceForMailbox(
+ const char* boxname) {
+ // We want to find the LONGEST substring that matches the beginning of this
+ // mailbox's path. This accounts for nested namespaces (i.e. "Public/" and
+ // "Public/Users/")
+
+ // Also, we want to match the namespace's mailbox to that namespace also:
+ // The Personal box will match the Personal/ namespace, etc.
+
+ // these lists shouldn't be too long (99% chance there won't be more than 3 or
+ // 4) so just do a linear search
+
+ int lengthMatched = -1;
+ int currentMatchedLength = -1;
+ nsImapNamespace* rv = nullptr;
+ int nodeIndex = 0;
+
+ if (!PL_strcasecmp(boxname, "INBOX"))
+ return GetDefaultNamespaceOfType(kPersonalNamespace);
+
+ for (nodeIndex = m_NamespaceList.Length() - 1; nodeIndex >= 0; nodeIndex--) {
+ nsImapNamespace* nspace = m_NamespaceList.ElementAt(nodeIndex);
+ currentMatchedLength = nspace->MailboxMatchesNamespace(boxname);
+ if (currentMatchedLength > lengthMatched) {
+ rv = nspace;
+ lengthMatched = currentMatchedLength;
+ }
+ }
+
+ return rv;
+}
+
+#define SERIALIZER_SEPARATORS ","
+
+/**
+ * If len is one, copies the first element of prefixes into
+ * serializedNamespaces. If len > 1, copies len strings from prefixes into
+ * serializedNamespaces as a comma-separated list of quoted strings.
+ */
+nsresult nsImapNamespaceList::SerializeNamespaces(
+ char** prefixes, int len, nsCString& serializedNamespaces) {
+ if (len <= 0) return NS_OK;
+
+ if (len == 1) {
+ serializedNamespaces.Assign(prefixes[0]);
+ return NS_OK;
+ }
+
+ for (int i = 0; i < len; i++) {
+ if (i > 0) serializedNamespaces.Append(',');
+
+ serializedNamespaces.Append('"');
+ serializedNamespaces.Append(prefixes[i]);
+ serializedNamespaces.Append('"');
+ }
+ return NS_OK;
+}
+
+/* str is the string which needs to be unserialized.
+ If prefixes is NULL, simply returns the number of namespaces in str. (len is
+ ignored) If prefixes is not NULL, it should be an array of length len which
+ is to be filled in with newly-allocated string. Returns the number of
+ strings filled in.
+*/
+int nsImapNamespaceList::UnserializeNamespaces(const char* str, char** prefixes,
+ int len) {
+ if (!str) return 0;
+ if (!prefixes) {
+ if (str[0] != '"') return 1;
+
+ int count = 0;
+ char* ourstr = PL_strdup(str);
+ char* origOurStr = ourstr;
+ if (ourstr) {
+ char* token = NS_strtok(SERIALIZER_SEPARATORS, &ourstr);
+ while (token != nullptr) {
+ token = NS_strtok(SERIALIZER_SEPARATORS, &ourstr);
+ count++;
+ }
+ PR_Free(origOurStr);
+ }
+ return count;
+ }
+
+ if ((str[0] != '"') && (len >= 1)) {
+ prefixes[0] = PL_strdup(str);
+ return 1;
+ }
+
+ int count = 0;
+ char* ourstr = PL_strdup(str);
+ char* origOurStr = ourstr;
+ if (ourstr) {
+ char* token = NS_strtok(SERIALIZER_SEPARATORS, &ourstr);
+ while ((count < len) && (token != nullptr)) {
+ char *current = PL_strdup(token), *where = current;
+ if (where[0] == '"') where++;
+ if (where[PL_strlen(where) - 1] == '"') where[PL_strlen(where) - 1] = 0;
+ prefixes[count] = PL_strdup(where);
+ PR_FREEIF(current);
+ token = NS_strtok(SERIALIZER_SEPARATORS, &ourstr);
+ count++;
+ }
+ PR_Free(origOurStr);
+ }
+ return count;
+}
+
+// static
+nsCString nsImapNamespaceList::AllocateCanonicalFolderName(
+ const char* onlineFolderName, char delimiter) {
+ nsCString canonicalPath;
+ if (delimiter) {
+ char* tmp =
+ nsImapUrl::ReplaceCharsInCopiedString(onlineFolderName, delimiter, '/');
+ canonicalPath.Assign(tmp);
+ PR_Free(tmp);
+ } else {
+ canonicalPath.Assign(onlineFolderName);
+ }
+ canonicalPath.ReplaceSubstring("\\/", "/");
+ return canonicalPath;
+}
+
+nsImapNamespace* nsImapNamespaceList::GetNamespaceForFolder(
+ const char* hostName, const char* canonicalFolderName, char delimiter) {
+ if (!hostName || !canonicalFolderName) return nullptr;
+
+ nsImapNamespace* resultNamespace = nullptr;
+ nsresult rv;
+ char* convertedFolderName = nsImapNamespaceList::AllocateServerFolderName(
+ canonicalFolderName, delimiter);
+
+ if (convertedFolderName) {
+ nsCOMPtr<nsIImapHostSessionList> hostSessionList =
+ do_GetService(kCImapHostSessionListCID, &rv);
+ if (NS_FAILED(rv)) return nullptr;
+ hostSessionList->GetNamespaceForMailboxForHost(
+ hostName, convertedFolderName, resultNamespace);
+ PR_Free(convertedFolderName);
+ } else {
+ NS_ASSERTION(false, "couldn't get converted folder name");
+ }
+
+ return resultNamespace;
+}
+
+/* static */
+char* nsImapNamespaceList::AllocateServerFolderName(
+ const char* canonicalFolderName, char delimiter) {
+ if (delimiter)
+ return nsImapUrl::ReplaceCharsInCopiedString(canonicalFolderName, '/',
+ delimiter);
+ return NS_xstrdup(canonicalFolderName);
+}
+
+/*
+ GetFolderOwnerNameFromPath takes as inputs a folder name
+ in canonical form, and a namespace for that folder.
+ The namespace MUST be of type kOtherUsersNamespace, hence the folder MUST be
+ owned by another user. This function extracts the folder owner's name from
+ the canonical name of the folder, and returns the owner's name.
+*/
+/* static */
+nsCString nsImapNamespaceList::GetFolderOwnerNameFromPath(
+ nsImapNamespace* namespaceForFolder, const char* canonicalFolderName) {
+ if (!namespaceForFolder || !canonicalFolderName) {
+ NS_ERROR("null namespace or canonical folder name");
+ return ""_ns;
+ }
+
+ // Convert the canonical path to the online path.
+ nsAutoCString convertedFolderName(canonicalFolderName);
+ char delimiter = namespaceForFolder->GetDelimiter();
+ if (delimiter) {
+ convertedFolderName.ReplaceChar('/', delimiter);
+ }
+
+ // Trim off the prefix.
+ uint32_t prefixLen =
+ nsDependentCString(namespaceForFolder->GetPrefix()).Length();
+ if (convertedFolderName.Length() <= prefixLen) {
+ NS_ERROR("server folder name invalid");
+ return ""_ns;
+ }
+
+ // Trim off anything after the owner name.
+ nsCString owner(Substring(convertedFolderName, prefixLen));
+ int32_t i = owner.FindChar(delimiter);
+ if (i != kNotFound) {
+ owner.Truncate(i);
+ }
+ return owner;
+}
+
+/*
+GetFolderIsNamespace returns TRUE if the given folder is the folder representing
+a namespace.
+*/
+
+bool nsImapNamespaceList::GetFolderIsNamespace(
+ const char* hostName, const char* canonicalFolderName, char delimiter,
+ nsImapNamespace* namespaceForFolder) {
+ NS_ASSERTION(namespaceForFolder, "null namespace");
+
+ bool rv = false;
+
+ const char* prefix = namespaceForFolder->GetPrefix();
+ NS_ASSERTION(prefix, "namespace has no prefix");
+ if (!prefix || !*prefix) // empty namespace prefix
+ return false;
+
+ char* convertedFolderName =
+ AllocateServerFolderName(canonicalFolderName, delimiter);
+ if (convertedFolderName) {
+ bool lastCharIsDelimiter = (prefix[strlen(prefix) - 1] == delimiter);
+
+ if (lastCharIsDelimiter) {
+ rv = ((strncmp(convertedFolderName, prefix,
+ strlen(convertedFolderName)) == 0) &&
+ (strlen(convertedFolderName) == strlen(prefix) - 1));
+ } else {
+ rv = (strcmp(convertedFolderName, prefix) == 0);
+ }
+
+ PR_Free(convertedFolderName);
+ } else {
+ NS_ASSERTION(false, "couldn't allocate server folder name");
+ }
+
+ return rv;
+}
+
+/*
+ SuggestHierarchySeparatorForNamespace takes a namespace from libmsg
+ and a hierarchy delimiter. If the namespace has not been filled in from
+ online NAMESPACE command yet, it fills in the suggested delimiter to be
+ used from then on (until it is overridden by an online response).
+*/
+
+void nsImapNamespaceList::SuggestHierarchySeparatorForNamespace(
+ nsImapNamespace* namespaceForFolder, char delimiterFromFolder) {
+ NS_ASSERTION(namespaceForFolder, "need namespace");
+ if (namespaceForFolder && !namespaceForFolder->GetIsDelimiterFilledIn())
+ namespaceForFolder->SetDelimiter(delimiterFromFolder, false);
+}
+
+/*
+ GenerateFullFolderNameWithDefaultNamespace takes a folder name in canonical
+ form, converts to online form and calculates the full online server name
+ including the namespace prefix of the default namespace of the
+ given type, in the form: PR_smprintf("%s%s", prefix, onlineServerName) if
+ there is a NULL owner PR_smprintf("%s%s%c%s", prefix, owner, delimiter,
+ onlineServerName) if there is an owner. It then converts this back to
+ canonical form and returns it.
+ It returns empty string if there is no namespace of the given type.
+ If nsUsed is not passed in as NULL, then *nsUsed is filled in and returned; it
+ is the namespace used for generating the folder name.
+*/
+nsCString nsImapNamespaceList::GenerateFullFolderNameWithDefaultNamespace(
+ const char* hostName, const char* canonicalFolderName, const char* owner,
+ EIMAPNamespaceType nsType, nsImapNamespace** nsUsed) {
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIImapHostSessionList> hostSession =
+ do_GetService(kCImapHostSessionListCID, &rv);
+ NS_ENSURE_SUCCESS(rv, ""_ns);
+ nsImapNamespace* ns;
+ nsCString fullFolderName;
+ rv = hostSession->GetDefaultNamespaceOfTypeForHost(hostName, nsType, ns);
+ NS_ENSURE_SUCCESS(rv, ""_ns);
+ if (ns) {
+ if (nsUsed) *nsUsed = ns;
+ const char* prefix = ns->GetPrefix();
+ char* convertedFolderName =
+ AllocateServerFolderName(canonicalFolderName, ns->GetDelimiter());
+ if (convertedFolderName) {
+ char* convertedReturnName = nullptr;
+ if (owner) {
+ convertedReturnName = PR_smprintf(
+ "%s%s%c%s", prefix, owner, ns->GetDelimiter(), convertedFolderName);
+ } else {
+ convertedReturnName = PR_smprintf("%s%s", prefix, convertedFolderName);
+ }
+
+ if (convertedReturnName) {
+ fullFolderName = AllocateCanonicalFolderName(convertedReturnName,
+ ns->GetDelimiter());
+ PR_Free(convertedReturnName);
+ }
+ PR_Free(convertedFolderName);
+ } else {
+ NS_ASSERTION(false, "couldn't allocate server folder name");
+ }
+ } else {
+ // Could not find other users namespace on the given host
+ NS_WARNING("couldn't find namespace for given host");
+ }
+ return fullFolderName;
+}