Bug 675448 - "Save as ..." multiple mails fails because of long pathnames and doesn't notice the user. r=rkent
authorMagnus Melin <mkmelin+mozilla@iki.fi>
Fri, 30 Jan 2015 21:37:40 +0200
changeset 21765 a55857b15908ca8f322e87f107dbf53ce9f8a69c
parent 21764 74916ecc3d77bfd9520338828d22cd7afaa810c6
child 21766 1aa67131558dac13f885b4fdb9abe80d041ff526
push id1326
push usermbanner@mozilla.com
push dateMon, 30 Mar 2015 20:10:12 +0000
treeherdercomm-beta@69663dd6f687 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrkent
bugs675448
Bug 675448 - "Save as ..." multiple mails fails because of long pathnames and doesn't notice the user. r=rkent
mailnews/base/src/nsMessenger.cpp
mailnews/base/src/nsMessenger.h
--- a/mailnews/base/src/nsMessenger.cpp
+++ b/mailnews/base/src/nsMessenger.cpp
@@ -945,16 +945,52 @@ enum MESSENGER_SAVEAS_FILE_TYPE
  HTML_FILE_TYPE = 1,
  TEXT_FILE_TYPE = 2,
  ANY_FILE_TYPE = 3
 };
 #define HTML_FILE_EXTENSION ".htm"
 #define HTML_FILE_EXTENSION2 ".html"
 #define TEXT_FILE_EXTENSION ".txt"
 
+/**
+ * Adjust the file name, removing characters from the middle of the name if
+ * the name would otherwise be too long - too long for what file systems
+ * usually support.
+ */
+nsresult nsMessenger::AdjustFileIfNameTooLong(nsIFile* aFile)
+{
+  NS_ENSURE_ARG_POINTER(aFile);
+  nsAutoString path;
+  nsresult rv = aFile->GetPath(path);
+  NS_ENSURE_SUCCESS(rv, rv);
+  // Most common file systems have a max filename length of 255. On windows, the
+  // total path length is (at least for all practical purposees) limited to 255.
+  // Let's just don't allow paths longer than that elsewhere either for
+  // simplicity.
+  uint32_t MAX = 255;
+  if (path.Length() > MAX) {
+    nsAutoString leafName;
+    rv = aFile->GetLeafName(leafName);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    uint32_t pathLengthUpToLeaf = path.Length() - leafName.Length();
+    if (pathLengthUpToLeaf >= MAX - 8) { // want at least 8 chars for name
+      return NS_ERROR_FILE_NAME_TOO_LONG;
+    }
+    uint32_t x = MAX - pathLengthUpToLeaf; // x = max leaf size
+    nsAutoString truncatedLeaf;
+    truncatedLeaf.Append(Substring(leafName, 0, x/2));
+    truncatedLeaf.AppendLiteral("...");
+    truncatedLeaf.Append(Substring(leafName, leafName.Length() - x/2 + 3,
+                                   leafName.Length()));
+    rv = aFile->SetLeafName(truncatedLeaf);
+  }
+  return rv;
+}
+
 NS_IMETHODIMP
 nsMessenger::SaveAs(const nsACString& aURI, bool aAsFile,
                     nsIMsgIdentity *aIdentity, const nsAString& aMsgFilename,
                     bool aBypassFilePicker)
 {
   nsCOMPtr<nsIMsgMessageService> messageService;
   nsCOMPtr<nsIUrlListener> urlListener;
   nsSaveMsgListener *saveListener = nullptr;
@@ -988,17 +1024,25 @@ nsMessenger::SaveAs(const nsACString& aU
                                NS_LITERAL_STRING(HTML_FILE_EXTENSION),
                                nsCaseInsensitiveStringComparator())) ||
                (StringEndsWith(aMsgFilename,
                                NS_LITERAL_STRING(HTML_FILE_EXTENSION2),
                                nsCaseInsensitiveStringComparator())))
         saveAsFileType = HTML_FILE_TYPE;
       else
         saveAsFileType = EML_FILE_TYPE;
-    } 
+    }
+
+    rv = AdjustFileIfNameTooLong(saveAsFile);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = PromptIfFileExists(saveAsFile);
+    if (NS_FAILED(rv)) {
+      goto done;
+    }
 
     // After saveListener goes out of scope, the listener will be owned by
     // whoever the listener is registered with, usually a URL.
     nsRefPtr<nsSaveMsgListener> saveListener = new nsSaveMsgListener(saveAsFile, this, nullptr);
     if (!saveListener) {
       rv = NS_ERROR_OUT_OF_MEMORY;
       goto done;
     }
@@ -1312,16 +1356,19 @@ nsMessenger::SaveMessages(uint32_t aCoun
     nsCOMPtr<nsIFile> saveToFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = saveToFile->InitWithFile(saveDir);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = saveToFile->Append(nsDependentString(aFilenameArray[i]));
     NS_ENSURE_SUCCESS(rv, rv);
 
+    rv = AdjustFileIfNameTooLong(saveToFile);
+    NS_ENSURE_SUCCESS(rv, rv);
+
     rv = PromptIfFileExists(saveToFile);
     if (NS_FAILED(rv))
       continue;
 
     nsCOMPtr<nsIMsgMessageService> messageService;
     nsCOMPtr<nsIUrlListener> urlListener;
 
     rv = GetMessageServiceFromURI(nsDependentCString(aMessageUriArray[i]),
@@ -1347,16 +1394,21 @@ nsMessenger::SaveMessages(uint32_t aCoun
       return rv;
     }
 
     // Ok, now save the message.
     rv = messageService->SaveMessageToDisk(aMessageUriArray[i],
                                            saveToFile, false,
                                            urlListener, nullptr,
                                            true, mMsgWindow);
+    if (NS_FAILED(rv)) {
+      NS_IF_RELEASE(saveListener);
+      Alert("saveMessageFailed");
+      return rv;
+    }
   }
   return rv;
 }
 
 nsresult
 nsMessenger::Alert(const char *stringName)
 {
   nsresult rv = NS_OK;
--- a/mailnews/base/src/nsMessenger.h
+++ b/mailnews/base/src/nsMessenger.h
@@ -61,16 +61,18 @@ protected:
   nsresult InitStringBundle();
   nsresult PromptIfDeleteAttachments(bool saveFirst, uint32_t count, const char **displayNameArray);
 
 private:
   nsresult GetLastSaveDirectory(nsIFile **aLastSaveAsDir);
   // if aLocalFile is a dir, we use it.  otherwise, we use the parent of aLocalFile.
   nsresult SetLastSaveDirectory(nsIFile *aLocalFile);
 
+  nsresult AdjustFileIfNameTooLong(nsIFile* aFile);
+
   nsresult GetSaveAsFile(const nsAString& aMsgFilename, int32_t *aSaveAsFileType,
                          nsIFile **aSaveAsFile);
 
   nsresult GetSaveToDir(nsIFile **aSaveToDir);
 
   nsString mId;
   nsCOMPtr<nsITransactionManager> mTxnMgr;