From e7f5c26fb98eeddf41f4f58386115907a881de4a Mon Sep 17 00:00:00 2001 From: "sspitzer%netscape.com" Date: Thu, 31 May 2001 05:26:08 +0000 Subject: [PATCH] fix for #82821. fix several problems (including an infinite loop problem during folder discovery) cause by long folder names on platforms with filename limitations. r/sr=mscott,bienvneu,a=leaf/chofmann --- mailnews/base/util/nsMsgDBFolder.cpp | 99 +++++++++----------------- mailnews/base/util/nsMsgDBFolder.h | 2 +- mailnews/base/util/nsMsgUtils.cpp | 61 +++++++++------- mailnews/imap/src/nsImapMailFolder.cpp | 43 +++++------ 4 files changed, 86 insertions(+), 119 deletions(-) diff --git a/mailnews/base/util/nsMsgDBFolder.cpp b/mailnews/base/util/nsMsgDBFolder.cpp index 07ce7130724d..576c789f1799 100644 --- a/mailnews/base/util/nsMsgDBFolder.cpp +++ b/mailnews/base/util/nsMsgDBFolder.cpp @@ -38,15 +38,7 @@ #include "nsITransport.h" #include "nsIFileTransportService.h" #include "nsIMsgFolderCompactor.h" -#if defined(XP_OS2) -#define MAX_FILE_LENGTH_WITHOUT_EXTENSION 8 -#elif defined(XP_MAC) -#define MAX_FILE_LENGTH_WITHOUT_EXTENSION 26 -#elif defined(XP_WIN32) -#define MAX_FILE_LENGTH_WITHOUT_EXTENSION 256 -#else -#define MAX_FILE_LENGTH_WITHOUT_EXTENSION 32000 -#endif +#include "nsMsgUtils.h" static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID); @@ -580,69 +572,44 @@ nsresult nsMsgDBFolder::GetOfflineStoreOutputStream(nsIOutputStream **outputStre return rv; } +// XXX todo +// move these to a common location and remove all the hard coded ".msf" +#define SUMMARY_SUFFIX ".msf" +#define SUMMARY_SUFFIX_LEN 4 + // path coming in is the root path without the leaf name, // on the way out, it's the whole path. -nsresult nsMsgDBFolder::CreatePlatformLeafNameForDisk(const char *userLeafName, nsFileSpec &path, char **resultName) +nsresult nsMsgDBFolder::CreateFileSpecForDB(const char *userLeafName, nsFileSpec &path, nsIFileSpec **dbFileSpec) { -#if 0 - const int charLimit = MAX_FILE_LENGTH_WITHOUT_EXTENSION; // set on platform specific basis -#endif -#if defined(XP_MAC) - nsCAutoString illegalChars(":"); -#elif defined(XP_OS2) - nsCAutoString illegalChars("\"/\\[]:;=,|?<>*$. "); -#elif defined(XP_WIN32) - nsCAutoString illegalChars("\"/\\[]:;=,|?<>*$"); -#else // UNIX (what about beos?) - nsCAutoString illegalChars; -#endif + NS_ENSURE_ARG_POINTER(dbFileSpec); + NS_ENSURE_ARG_POINTER(userLeafName); - if (!resultName || !userLeafName) - return NS_ERROR_NULL_POINTER; - *resultName = nsnull; + nsCAutoString proposedDBName(userLeafName); + NS_MsgHashIfNecessary(proposedDBName); - // mangledLeaf is the new leaf name. - // If userLeafName (a) contains all legal characters - // (b) is within the valid length for the given platform - // (c) does not already exist on the disk - // then we simply return nsCRT::strdup(userLeafName) - // Otherwise we mangle it - // mangledPath is the entire path to the newly mangled leaf name - nsCAutoString mangledLeaf(userLeafName); + // (note, the caller of this will be using the dbFileSpec to call db->Open() + // will turn the path into summary spec, and append the ".msf" extension) + // + // we want db->Open() to create a new summary file + // so we have to jump through some hoops to make sure the .msf it will + // create is unique. now that we've got the "safe" proposedDBName, + // we append ".msf" to see if the file exists. if so, we make the name + // unique and then string off the ".msf" so that we pass the right thing + // into Open(). this isn't ideal, since this is not atomic + // but it will make do. + proposedDBName+= SUMMARY_SUFFIX; + path += proposedDBName.get(); + if (path.Exists()) + { + path.MakeUnique(); + proposedDBName = path.GetLeafName(); + } + // now, take the ".msf" off + proposedDBName.Truncate(proposedDBName.Length() - SUMMARY_SUFFIX_LEN); + path.SetLeafName(proposedDBName); - PRInt32 illegalCharacterIndex = mangledLeaf.FindCharInSet(illegalChars); - - if (illegalCharacterIndex == kNotFound) - { - path += (const char *) mangledLeaf; - if (!path.Exists()) - { - // if there are no illegal characters - // and the file doesn't already exist, then don't do anything to the string - // Note that this might be truncated to charLength, but if so, the file still - // does not exist, so we are OK. - *resultName = mangledLeaf.ToNewCString(); - return (*resultName) ? NS_OK : NS_ERROR_OUT_OF_MEMORY; - } - } - else - { - - // First, replace all illegal characters with '_' - mangledLeaf.ReplaceChar(illegalChars, '_'); - - path += (const char *) mangledLeaf; - } - // if we are here, then any of the following may apply: - // (a) there were illegal characters - // (b) the file already existed - - // Now, we have to loop until we find a filename that doesn't already - // exist on the disk - path.SetLeafName(mangledLeaf.get()); - path.MakeUnique(); - *resultName = path.GetLeafName(); - return NS_OK; + NS_NewFileSpecWithSpec(path, dbFileSpec); + return NS_OK; } NS_IMETHODIMP diff --git a/mailnews/base/util/nsMsgDBFolder.h b/mailnews/base/util/nsMsgDBFolder.h index c6a045fb69ef..0ec9b60d79f6 100644 --- a/mailnews/base/util/nsMsgDBFolder.h +++ b/mailnews/base/util/nsMsgDBFolder.h @@ -103,7 +103,7 @@ protected: nsresult CheckWithNewMessagesStatus(PRBool messageAdded); nsresult OnKeyAddedOrDeleted(nsMsgKey aKeyChanged, nsMsgKey aParentKey , PRInt32 aFlags, nsIDBChangeListener * aInstigator, PRBool added, PRBool doFlat, PRBool doThread); - nsresult CreatePlatformLeafNameForDisk(const char *userLeafName, nsFileSpec &baseDir, char **resultName); + nsresult CreateFileSpecForDB(const char *userLeafName, nsFileSpec &baseDir, nsIFileSpec **dbFileSpec); nsresult GetFolderCacheKey(nsIFileSpec **aFileSpec); nsresult GetFolderCacheElemFromFileSpec(nsIFileSpec *fileSpec, nsIMsgFolderCacheElement **cacheElement); diff --git a/mailnews/base/util/nsMsgUtils.cpp b/mailnews/base/util/nsMsgUtils.cpp index 3bf0f83997d5..9fadeb5c950b 100644 --- a/mailnews/base/util/nsMsgUtils.cpp +++ b/mailnews/base/util/nsMsgUtils.cpp @@ -41,10 +41,6 @@ static NS_DEFINE_CID(kImapUrlCID, NS_IMAPURL_CID); static NS_DEFINE_CID(kCMailboxUrl, NS_MAILBOXURL_CID); static NS_DEFINE_CID(kCNntpUrlCID, NS_NNTPURL_CID); -#if defined(DEBUG_sspitzer_) || defined(DEBUG_seth_) -#define DEBUG_NS_MsgHashIfNecessary 1 -#endif - nsresult GetMessageServiceContractIDForURI(const char *uri, nsString &contractID) { @@ -236,38 +232,51 @@ static PRUint32 StringHash(const char *ubuf) nsresult NS_MsgHashIfNecessary(nsCAutoString &name) { #if defined(XP_MAC) + nsCAutoString illegalChars(":"); const PRUint32 MAX_LEN = 25; -#elif defined(XP_UNIX) || defined(XP_PC) || defined(XP_BEOS) +#elif defined(XP_UNIX) || defined(XP_BEOS) + nsCAutoString illegalChars; // is this correct for BEOS? const PRUint32 MAX_LEN = 55; +#elif defined(XP_WIN32) + nsCAutoString illegalChars("\"/\\[]:;=,|?<>*$"); + const PRUint32 MAX_LEN = 55; +#elif defined(XP_OS2) + nsCAutoString illegalChars("\"/\\[]:;=,|?<>*$. "); + const PRUint32 MAX_LEN = 8; #else #error need_to_define_your_max_filename_length #endif nsCAutoString str(name); -#ifdef DEBUG_NS_MsgHashIfNecessary - printf("in: %s\n",str.get()); -#endif - - // Given a name, use either that name, if it fits on our - // filesystem, or a hashified version of it, if the name is too - // long to fit. + // Given a filename, make it safe for filesystem + // certain filenames require hashing because they + // are too long or contain illegal characters + PRInt32 illegalCharacterIndex = str.FindCharInSet(illegalChars); char hashedname[MAX_LEN + 1]; - PRBool needshash = PL_strlen(str.get()) > MAX_LEN; -#if defined(XP_WIN16) || defined(XP_OS2) - if (!needshash) { - needshash = PL_strchr(str.get(), '.') != nsnull || - PL_strchr(str.get(), ':') != nsnull; - } -#endif - PL_strncpy(hashedname, str.get(), MAX_LEN + 1); - if (needshash) { - PR_snprintf(hashedname + MAX_LEN - 8, 9, "%08lx", + if (illegalCharacterIndex == kNotFound) + { + // no illegal chars, it's just too long + // keep the initial part of the string, but hash to make it fit + if (str.Length() > MAX_LEN) + { + PL_strncpy(hashedname, str.get(), MAX_LEN + 1); + PR_snprintf(hashedname + MAX_LEN - 8, 9, "%08lx", (unsigned long) StringHash(str.get())); + name = hashedname; + } + } + else + { + // found illegal chars, hash the whole thing + // if we do substitution, then hash, two strings + // could hash to the same value. + // for example, on mac: "foo__bar", "foo:_bar", "foo::bar" + // would map to "foo_bar". this way, all three will map to + // different values + PR_snprintf(hashedname, 9, "%08lx", + (unsigned long) StringHash(str.get())); + name = hashedname; } - name = hashedname; -#ifdef DEBUG_NS_MsgHashIfNecessary - printf("out: %s\n",hashedname); -#endif return NS_OK; } diff --git a/mailnews/imap/src/nsImapMailFolder.cpp b/mailnews/imap/src/nsImapMailFolder.cpp index 785217d77620..88f6ca971b65 100644 --- a/mailnews/imap/src/nsImapMailFolder.cpp +++ b/mailnews/imap/src/nsImapMailFolder.cpp @@ -721,7 +721,14 @@ NS_IMETHODIMP nsImapMailFolder::CreateClientSubfolderInfo(const char *folderName nsCAutoString uri (mURI); parentName.Right(leafName, leafName.Length() - folderStart - 1); parentName.Truncate(folderStart); - path += parentName; + + // the parentName might be too long or have some illegal chars + // so we make it safe + nsCAutoString safeParentName; + safeParentName.AssignWithConversion(parentName); + NS_MsgHashIfNecessary(safeParentName); + path += safeParentName; + rv = CreateDirectoryForFolder(path); if (NS_FAILED(rv)) return rv; uri.Append('/'); @@ -748,22 +755,13 @@ NS_IMETHODIMP nsImapMailFolder::CreateClientSubfolderInfo(const char *folderName rv = nsComponentManager::CreateInstance(kCMailDB, nsnull, NS_GET_IID(nsIMsgDatabase), (void **) getter_AddRefs(mailDBFactory)); if (NS_SUCCEEDED(rv) && mailDBFactory) { - nsCOMPtr unusedDB; + nsCOMPtr unusedDB; nsCOMPtr dbFileSpec; - nsXPIDLCString uniqueLeafName; - nsCAutoString proposedDBName(folderName); - proposedDBName += ".msf"; + // warning, path will be changed + rv = CreateFileSpecForDB(folderName, path, getter_AddRefs(dbFileSpec)); + NS_ENSURE_SUCCESS(rv,rv); - rv = CreatePlatformLeafNameForDisk(proposedDBName, path, getter_Copies(uniqueLeafName)); - - // take off the ".msf" on the end. - proposedDBName = uniqueLeafName; - proposedDBName.Truncate(proposedDBName.Length() - 4); - - path.SetLeafName(proposedDBName); - - NS_NewFileSpecWithSpec(path, getter_AddRefs(dbFileSpec)); rv = mailDBFactory->Open(dbFileSpec, PR_TRUE, PR_TRUE, (nsIMsgDatabase **) getter_AddRefs(unusedDB)); if (NS_SUCCEEDED(rv) && unusedDB) @@ -5490,20 +5488,13 @@ NS_IMETHODIMP nsImapMailFolder::RenameClient( nsIMsgFolder *msgFolder, const cha nsCOMPtr unusedDB; nsCOMPtr dbFileSpec; - nsXPIDLCString uniqueLeafName; - nsCAutoString proposedDBName(newLeafName.ToNewCString()); - //nsCAutoString proposedDBName(folderName); - proposedDBName += ".msf"; + nsCAutoString proposedDBName; + proposedDBName.AssignWithConversion(newLeafName); - rv = CreatePlatformLeafNameForDisk(proposedDBName, path, getter_Copies(uniqueLeafName)); + // warning, path will be changed + rv = CreateFileSpecForDB(proposedDBName.get(), path, getter_AddRefs(dbFileSpec)); + NS_ENSURE_SUCCESS(rv,rv); - // take off the ".msf" on the end. - proposedDBName = uniqueLeafName; - proposedDBName.Truncate(proposedDBName.Length() - 4); - - path.SetLeafName(proposedDBName); - - NS_NewFileSpecWithSpec(path, getter_AddRefs(dbFileSpec)); // it's OK to use Open and not OpenFolderDB here, since we don't use the DB. rv = mailDBFactory->Open(dbFileSpec, PR_TRUE, PR_TRUE, (nsIMsgDatabase **) getter_AddRefs(unusedDB));