Bug 1366591, Bug 28211 - When save to Sent (or Drafts or Templates) fails, save to subfolder of local folders. r=jorgk,aceman
authorGene Smith <gds@chartertn.net>
Mon, 10 Jul 2017 02:17:02 -0400
changeset 52560 a6a1279292d055c75b9554b01dab78b1cd9f27de
parent 52557 7e89569fe183633ed9519198d15833e5b1bb07ae
child 52561 ed7a7b25e03336eae054bc161efc8be6ec2d02cd
child 52563 873ddce7e8e6f9b9272789154752207cb6556652
push id4665
push userfoss@franjaru.com
push dateTue, 11 Jul 2017 15:22:51 +0000
treeherdertry-comm-central@0ed46f1af7c2 [default view] [failures only]
reviewersjorgk, aceman
bugs1366591, 28211
Bug 1366591, Bug 28211 - When save to Sent (or Drafts or Templates) fails, save to subfolder of local folders. r=jorgk,aceman When save to Sent, Drafts or Templates folders fails, e.g., due to IMAP connection issues, prompts now occurs to save to <local folder account>/Sent-<account>, <local folder account>/Drafts-<account> or <local folder account>/Templates-<account>, where <account> is the account name having the network issue. Also supports sending by right-click on Outbox after messages composed and saved with "Send Later".
mail/components/compose/content/MsgComposeCommands.js
mail/locales/en-US/chrome/messenger/messengercompose/composeMsgs.properties
mailnews/compose/src/nsMsgSend.cpp
mailnews/compose/src/nsMsgSend.h
suite/locales/en-US/chrome/mailnews/compose/composeMsgs.properties
suite/mailnews/compose/MsgComposeCommands.js
--- a/mail/components/compose/content/MsgComposeCommands.js
+++ b/mail/components/compose/content/MsgComposeCommands.js
@@ -3884,17 +3884,17 @@ function ComposeCanClose()
     // call window.focus, since we need to pop up a dialog
     // and therefore need to be visible (to prevent user confusion)
     window.focus();
     let draftFolderURI = gCurrentIdentity.draftFolder;
     let draftFolderName = MailUtils.getFolderForURI(draftFolderURI).prettyName;
     let result = Services.prompt
                          .confirmEx(window,
                                     getComposeBundle().getString("saveDlogTitle"),
-                                    getComposeBundle().getFormattedString("saveDlogMessages",[draftFolderName]),
+                                    getComposeBundle().getFormattedString("saveDlogMessages2", [draftFolderName]),
                                     (Services.prompt.BUTTON_TITLE_SAVE * Services.prompt.BUTTON_POS_0) +
                                     (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1) +
                                     (Services.prompt.BUTTON_TITLE_DONT_SAVE * Services.prompt.BUTTON_POS_2),
                                     null, null, null,
                                     null, {value:0});
     switch (result)
     {
       case 0: //Save
--- a/mail/locales/en-US/chrome/messenger/messengercompose/composeMsgs.properties
+++ b/mail/locales/en-US/chrome/messenger/messengercompose/composeMsgs.properties
@@ -108,18 +108,20 @@ smtpAuthMechNotSupported=The Outgoing se
 smtpAuthenticationNotSupported=Unable to authenticate to Outgoing server (SMTP) %S. It does not support authentication (SMTP-AUTH) but you have chosen to use authentication. Please change the 'Authentication method' to 'None' in the 'Account Settings | Outgoing Server (SMTP)' or contact your email service provider for instructions.
 
 # LOCALIZATION NOTE (errorIllegalLocalPart): %s is an email address with an illegal localpart
 errorIllegalLocalPart=There are non-ASCII characters in the local part of the recipient address %s. This is not yet supported. Please change this address and try again.
 
 ## Strings used for the save message dialog shown when the user closes a message compose window
 saveDlogTitle=Save Message
 
-## LOCALIZATION NOTE (SaveDlogMessages): %1$S is the folder name
-saveDlogMessages=Message has not been sent. Do you want to save the message in your drafts folder (%1$S)?
+## LOCALIZATION NOTE (saveDlogMessages2): Do not translate the words %1$S and \n.
+## %1$S is replaced by the folder name configured for saving drafts (typically the "Drafts" folder).
+## Translate "Write" to match the translation of item "windowTitlePrefix" below.
+saveDlogMessages2=Message has not been sent or saved in your drafts folder (%1$S).\n"Save" copies the message to your drafts folder (%1$S) and closes the Write window.\n"Don't Save" closes the Write window without saving a draft.\n"Cancel" allows you continue writing without saving a draft.
 
 ## generics string
 defaultSubject=(no subject)
 chooseFileToAttach=Attach File(s)
 genericFailureExplanation=Please verify that your account settings are correct and try again.
 
 ## LOCALIZATION NOTE (undisclosedRecipients): this string must use only US_ASCII characters
 undisclosedRecipients=undisclosed-recipients
@@ -326,22 +328,44 @@ smtpEnterPasswordPrompt=Enter your passw
 ## and %2$S where the user name should appear.
 smtpEnterPasswordPromptWithUsername=Enter your password for %2$S on %1$S:
 smtpEnterPasswordPromptTitle=Outgoing server (SMTP) Password Required
 
 # LOCALIZATION NOTE (removeAttachmentMsgs): Semi-colon list of plural forms.
 # See: http://developer.mozilla.org/en/Localization_and_Plurals
 removeAttachmentMsgs=Remove Attachment;Remove Attachments
 
-## LOCALIZATION NOTE(errorSavingMsg): Do not translate the word %S. It
-## will be replaced with the name of the folder the message is being saved to.
-errorSavingMsg=There was an error saving the message to %S. Retry?
+## LOCALIZATION NOTE(promptToSaveSentLocally): Do not translate the stings %1$S, %2$S, %3$S and \n.
+## %2$S will be replaced with the account name. $1$S will be replaced by the folder name
+## configured to contain saved sent messages (typically the "Sent" folder).
+## %3$S will be replaced with the local folders account name (typically "Local Folders").
+## Translate "Write" to match the translation of item "windowTitlePrefix" above.
+promptToSaveSentLocally=Your message was sent but not saved to your sent folder (%1$S) probably because of network errors.\n"Retry" attempts the save again.\n"Save" copies the message to %3$S/%1$S-%2$S and closes the Write window if it is present.\n"Cancel" does not save the sent message and closes the Write window if it is present.
 errorFilteringMsg=Your message has been sent and saved, but there was an error while running message filters on it.
 errorCloudFileAuth.title=Authentication Error
 
+## LOCALIZATION NOTE(promptToSaveDraftLocally): Do not translate the stings %1$S, %2$S, %3$S and \n.
+## %2$S will be replaced with the account name. $1$S will be replaced by the folder name
+## configured to contain saved draft messages (typically the "Drafts" folder).
+## %3$S will be replaced with the local folders account name (typically "Local Folders").
+promptToSaveDraftLocally=Your draft message was not saved to your drafts folder (%1$S) probably because of network errors.\n"Retry" attempts to save again.\n"Save" copies the message to %3$S/%1$S-%2$S and you can continue writing.\n"Cancel" allows you to continue writing without saving your draft.
+buttonLabelRetry=Retry
+
+## LOCALIZATION NOTE(promptToSaveTemplateLocally): Do not translate the stings %1$S, %2$S, %3$S and \n.
+## %2$S will be replaced with the account name. $1$S will be replaced by the folder name
+## configured to contain saved templates (typically the "Templates" folder).
+## %3$S will be replaced with the local folders account name (typically "Local Folders").
+promptToSaveTemplateLocally=Your template was not saved to your templates folder (%1$S) probably because of network errors.\n"Retry" attempts to save again.\n"Save" copies the message to %3$S/%1$S-%2$S and you can continue writing.\n"Cancel" allows you to continue writing without saving your template.
+
+## LOCALIZATION NOTE(saveToLocalFoldersFailed): Message appears after normal
+## save fails (e.g., to Sent) and save to Local Folders also fails. This could
+## occur if network is down and filesystem problems are present such as disk
+## full, permission issues or hardware failure.
+saveToLocalFoldersFailed=Unable to save your message to local folders. Possibly out of file storage space.
+
 ## LOCALIZATION NOTE(errorCloudFileAuth.message):
 ## %1$S is the name of the online storage service against which the authentication failed.
 errorCloudFileAuth.message=Unable to authenticate to %1$S.
 errorCloudFileUpload.title=Upload Error
 
 ## LOCALIZATION NOTE(errorCloudFileUpload.message):
 ## %1$S is the name of the online storage service against which the uploading failed.
 ## %2$S is the name of the file that failed to upload.
--- a/mailnews/compose/src/nsMsgSend.cpp
+++ b/mailnews/compose/src/nsMsgSend.cpp
@@ -3621,41 +3621,62 @@ nsMsgComposeAndSend::DeliverAsMailExit(n
 
 NS_IMETHODIMP
 nsMsgComposeAndSend::DeliverAsNewsExit(nsIURI *aUrl, nsresult aExitCode)
 {
   DoDeliveryExitProcessing(aUrl, aExitCode, mSendMailAlso);
   return NS_OK;
 }
 
-bool nsMsgComposeAndSend::CanSaveMessagesToFolder(const char *folderURL)
+nsresult
+nsMsgComposeAndSend::GetIncomingServer(const char *folderURL, nsIMsgIncomingServer **aServer)
 {
   nsresult rv;
   nsCOMPtr<nsIRDFService> rdf(do_GetService("@mozilla.org/rdf/rdf-service;1", &rv));
   if (NS_FAILED(rv))
-    return false;
+    return rv;
 
   nsCOMPtr<nsIRDFResource> resource;
   rv = rdf->GetResource(nsDependentCString(folderURL), getter_AddRefs(resource));
   if (NS_FAILED(rv))
-    return false;
+    return rv;
 
   nsCOMPtr <nsIMsgFolder> thisFolder;
   thisFolder = do_QueryInterface(resource, &rv);
   if (NS_FAILED(rv) || !thisFolder)
-    return false;
+    return rv;
 
   nsCOMPtr<nsIMsgIncomingServer> server;
   rv = thisFolder->GetServer(getter_AddRefs(server));
-  if (NS_FAILED(rv) || !server)
+  if (NS_FAILED(rv))
+    return rv;
+  if (!server)
+    return NS_ERROR_NULL_POINTER;
+
+  server.forget(aServer);
+  return NS_OK;
+}
+
+bool
+nsMsgComposeAndSend::CanSaveMessagesToFolder(const char *folderURL)
+{
+  bool canSave = false;
+  // Get pointer to server.
+  nsCOMPtr<nsIMsgIncomingServer> server;
+  nsresult rv = GetIncomingServer(folderURL, getter_AddRefs(server));
+  if (NS_FAILED(rv))
     return false;
 
   // See if we are allowed to save/file msgs to this folder.
-  bool canSave;
-  rv = server->GetCanFileMessagesOnServer(&canSave);
+  if (server)
+  {
+    rv = server->GetCanFileMessagesOnServer(&canSave);
+    if (NS_FAILED(rv))
+      canSave = false;
+  }
   return canSave;
 }
 
 //
 // Now, start the appropriate copy operation.
 //
 nsresult
 nsMsgComposeAndSend::DoFcc()
@@ -3827,36 +3848,123 @@ nsMsgComposeAndSend::NotifyListenerOnSto
     nsresult rv;
     nsCOMPtr<nsIStringBundleService> bundleService =
       mozilla::services::GetStringBundleService();
     NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
     nsCOMPtr<nsIStringBundle> bundle;
     rv = bundleService->CreateBundle("chrome://messenger/locale/messengercompose/composeMsgs.properties", getter_AddRefs(bundle));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    nsString msg;
-    const char16_t *formatStrings[] = { mSavedToFolderName.get() };
-
-    rv = bundle->FormatStringFromName(u"errorSavingMsg",
-                                      formatStrings, 1,
-                                      getter_Copies(msg));
+    // Obtain account name for local folders.
+    nsString localFoldersAccountName;
+    nsCOMPtr<nsIMsgAccountManager> accountManager =
+      do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
     if (NS_SUCCEEDED(rv))
     {
-      bool retry = false;
-      nsMsgAskBooleanQuestionByString(prompt, msg.get(), &retry, nullptr);
-      if (retry)
+      nsCOMPtr<nsIMsgIncomingServer> server;
+      rv = accountManager->GetLocalFoldersServer(getter_AddRefs(server));
+      if (NS_SUCCEEDED(rv))
+        rv = server->GetPrettyName(localFoldersAccountName);
+    }
+    if (NS_FAILED(rv) || localFoldersAccountName.IsEmpty())
+    {
+      // Unable to obtain localFoldersAccountName.
+      Fail(NS_OK, nullptr, &aStatus);
+      return NS_ERROR_FAILURE;
+    }
+
+    // Get the user account name where "save to" failed.
+    nsString accountName;
+    const char* fcc = mCompFields->GetFcc();
+    if (fcc && *fcc)
+    {
+      nsCOMPtr<nsIMsgIncomingServer> server;
+      rv = GetIncomingServer(fcc, getter_AddRefs(server));
+      if (NS_SUCCEEDED(rv) && server)
+        rv = server->GetPrettyName(accountName);
+    }
+    else
+      rv = NS_ERROR_FAILURE;
+    if (NS_FAILED(rv) || accountName.IsEmpty())
+    {
+      // Unable to obtain accountName.
+      Fail(NS_OK, nullptr, &aStatus);
+      return NS_ERROR_FAILURE;
+    }
+
+    const char16_t *formatStrings[] = { mSavedToFolderName.get(), accountName.get(),
+                                        localFoldersAccountName.get() };
+
+    nsString msg;
+    switch (m_deliver_mode)
+    {
+      case nsMsgDeliverNow:
+      case nsMsgSendUnsent:
+        rv = bundle->FormatStringFromName(u"promptToSaveSentLocally",
+                                          formatStrings, 3,
+                                          getter_Copies(msg));
+        break;
+      case nsMsgSaveAsDraft:
+        rv = bundle->FormatStringFromName(u"promptToSaveDraftLocally",
+                                          formatStrings, 3,
+                                          getter_Copies(msg));
+        break;
+      case nsMsgSaveAsTemplate:
+        rv = bundle->FormatStringFromName(u"promptToSaveTemplateLocally",
+                                          formatStrings, 3,
+                                          getter_Copies(msg));
+        break;
+      default:
+        rv = NS_ERROR_UNEXPECTED;
+    }
+    NS_ENSURE_SUCCESS(rv, rv);
+    int32_t buttonPressed = 0;
+    bool showCheckBox = false;
+    uint32_t buttonFlags = (nsIPrompt::BUTTON_POS_0 * nsIPrompt::BUTTON_TITLE_IS_STRING) +
+                           (nsIPrompt::BUTTON_POS_1 * nsIPrompt::BUTTON_TITLE_CANCEL) +
+                           (nsIPrompt::BUTTON_POS_2 * nsIPrompt::BUTTON_TITLE_SAVE);
+    nsString dialogTitle, buttonLabelRetry;
+    bundle->GetStringFromName(u"SaveDialogTitle", getter_Copies(dialogTitle));
+    bundle->GetStringFromName(u"buttonLabelRetry", getter_Copies(buttonLabelRetry));
+    prompt->ConfirmEx(dialogTitle.get(), msg.get(), buttonFlags, buttonLabelRetry.get(),
+                      nullptr, nullptr, nullptr, &showCheckBox, &buttonPressed);
+    if (buttonPressed == 0)
+    {
+      // retry button clicked
+      mSendProgress = nullptr; // this was canceled, so we need to clear it.
+      return SendToMagicFolder(m_deliver_mode);
+    }
+
+    bool saveLocally = (buttonPressed == 2);
+
+    if (saveLocally)
+    {
+      // Try to save to Local Folders/<account name>.
+      // Pass in "nsMsgDeliverNow" so draft saves too. Also, fcc pointer
+      // is nullptr to tell function to save to local folders and not the
+      // configured fcc.
+      rv = MimeDoFCC(mTempFile,
+                     nsIMsgSend::nsMsgDeliverNow,
+                     mCompFields->GetBcc(),
+                     nullptr,
+                     mCompFields->GetNewspostUrl());
+
+      if (NS_FAILED(rv))
       {
-        mSendProgress = nullptr; // this was cancelled, so we need to clear it.
-        return SendToMagicFolder(m_deliver_mode);
+        // Save to Local Folders failed. Inform the user.
+        nsCOMPtr<nsIPrompt> prompt;
+        GetDefaultPrompt(getter_AddRefs(prompt));
+        nsMsgDisplayMessageByName(prompt, u"saveToLocalFoldersFailed");
       }
     }
 
-    // We failed, and the user decided not to retry. So we're just going to
-    // fail out. However, give Fail a success code so that it doesn't prompt
-    // the user a second time as they already know about the failure.
+    // Failure detected when user saved to default folder and the user did not
+    // retry; instead the user saved to Local Folders or canceled the save. So
+    // just call Fail() with a success code so that it doesn't prompt the user
+    // again since the user already knows about the failure and has reacted.
     Fail(NS_OK, nullptr, &aStatus);
   }
 
   if (NS_SUCCEEDED(aStatus) &&
       !mPerformingSecondFCC && m_messageKey != nsMsgKey_None &&
       (m_deliver_mode == nsMsgDeliverNow || m_deliver_mode == nsMsgSendUnsent))
   {
     nsresult rv = FilterSentMessage();
@@ -4210,31 +4318,23 @@ nsMsgComposeAndSend::MimeDoFCC(nsIFile  
                                const char       *bcc_header,
                                const char       *fcc_header,
                                const char       *news_url)
 {
   nsresult      status = NS_OK;
   char          *ibuffer = nullptr;
   uint32_t      n;
   bool          folderIsLocal = true;
-  nsCString     turi;
+  nsCString     tmpUri;
   char16_t     *printfString = nullptr;
   nsString msg;
   nsCOMPtr<nsIMsgFolder> folder;
 
-  // Before continuing, just check the user has not cancel the operation
   if (mSendProgress)
-  {
-    bool canceled = false;
-    mSendProgress->GetProcessCanceledByUser(&canceled);
-    if (canceled)
-      return NS_ERROR_ABORT;
-    else
-      mSendProgress->OnProgressChange(nullptr, nullptr, 0, 0, 0, -1);
-  }
+    mSendProgress->OnProgressChange(nullptr, nullptr, 0, 0, 0, -1);
 
   //
   // Ok, this is here to keep track of this for 2 copy operations...
   //
   if (mCopyFile)
   {
     mCopyFile2 = mCopyFile;
     mCopyFile = nullptr;
@@ -4291,33 +4391,113 @@ nsMsgComposeAndSend::MimeDoFCC(nsIFile  
   // First, we we need to put a Berkeley "From - " delimiter at the head of
   // the file for parsing...
   //
 
   if (fcc_header && *fcc_header)
     GetExistingFolder(nsDependentCString(fcc_header), getter_AddRefs(folder));
 
   if ((mode == nsMsgDeliverNow || mode == nsMsgSendUnsent) && folder)
-    turi = fcc_header;
+  {
+    tmpUri = fcc_header;
+  }
+  else if (!fcc_header)
+  {
+    // Set fcc_header to a special folder in Local Folders "account" since can't
+    // save to Sent mbox, typically because imap connection is down. This
+    // folder is created if it doesn't yet exist.
+
+    nsCString folder;
+    // First, in folder string, obtain the uri for the local folders account
+    // which is typically "mailbox://nobody@Local%20Folders/"
+    nsCOMPtr<nsIMsgAccountManager> accountManager =
+      do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+    if (NS_SUCCEEDED(rv) && accountManager)
+    {
+      nsCOMPtr<nsIMsgIncomingServer> server;
+      rv = accountManager->GetLocalFoldersServer(getter_AddRefs(server));
+      if (NS_SUCCEEDED(rv) && server)
+      {
+        nsCOMPtr<nsIMsgFolder> rootFolder;
+        rv = server->GetRootMsgFolder(getter_AddRefs(rootFolder));
+        if (NS_SUCCEEDED(rv) && rootFolder)
+        {
+          rv = rootFolder->GetURI(folder);
+          folder.Append("/");
+        }
+      }
+    }
+    if (NS_FAILED(rv) || folder.IsEmpty())
+    {
+      status = NS_ERROR_FAILURE;
+      goto FAIL;
+    }
+
+    // Now append the special folder name folder to the local folder uri.
+    switch (m_deliver_mode)
+    {
+      case nsMsgDeliverNow:
+      case nsMsgSendUnsent:
+      case nsMsgSaveAsDraft:
+      case nsMsgSaveAsTemplate:
+        // Typically, this appends "Sent-", "Drafts-" or "Templates-" to folder
+        // and then has the account name appended, e.g., .../Sent-MyImapAccount.
+        folder.Append(NS_ConvertUTF16toUTF8(mSavedToFolderName));
+        folder.Append("-");
+        break;
+      default:
+        status = NS_ERROR_FAILURE;
+        goto FAIL;
+    }
+
+    // Get the account name where the "save to" failed.
+    nsString accountName;
+    const char* fcc = mCompFields->GetFcc();
+    if (fcc && *fcc)
+    {
+      nsCOMPtr<nsIMsgIncomingServer> server;
+      rv = GetIncomingServer(fcc, getter_AddRefs(server));
+      if (NS_SUCCEEDED(rv) && server)
+      {
+        rv = server->GetPrettyName(accountName);
+      }
+    }
+    else
+      rv = NS_ERROR_FAILURE;
+    if (NS_FAILED(rv) || accountName.IsEmpty())
+    {
+      status = NS_ERROR_FAILURE;
+      goto FAIL;
+    }
+
+    // Now append the imap account name (escaped) to the folder uri.
+    nsCString escAccountName;
+    MsgEscapeString(NS_ConvertUTF16toUTF8(accountName), nsINetUtil::ESCAPE_URL_PATH, escAccountName);
+    folder += escAccountName;
+    fcc_header = ToNewCString(folder);
+    tmpUri = fcc_header;
+  }
   else
-    GetFolderURIFromUserPrefs(mode, mUserIdentity, turi);
-  status = MessageFolderIsLocal(mUserIdentity, mode, turi.get(), &folderIsLocal);
+  {
+    GetFolderURIFromUserPrefs(mode, mUserIdentity, tmpUri);
+  }
+  status = MessageFolderIsLocal(mUserIdentity, mode, tmpUri.get(), &folderIsLocal);
   if (NS_FAILED(status))
     goto FAIL;
 
   // Tell the user we are copying the message...
   mComposeBundle->GetStringFromName(u"copyMessageStart",
                                     getter_Copies(msg));
   if (!msg.IsEmpty())
   {
     nsCOMPtr<nsIRDFService> rdfService = do_GetService(kRDFServiceCID);
     if (rdfService)
     {
       nsCOMPtr<nsIRDFResource> res;
-      rdfService->GetResource(turi, getter_AddRefs(res));
+      rdfService->GetResource(tmpUri, getter_AddRefs(res));
       nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(res);
       if (folder)
         folder->GetName(mSavedToFolderName);
     }
     if (!mSavedToFolderName.IsEmpty())
       printfString = nsTextFormatter::smprintf(msg.get(), mSavedToFolderName.get());
     else
       printfString = nsTextFormatter::smprintf(msg.get(), "?");
@@ -4639,17 +4819,17 @@ FAIL:
 
   // When we get here, we have to see if we have been successful so far.
   // If we have, then we should start up the async copy service operation.
   // If we weren't successful, then we should just return the error and
   // bail out.
   if (NS_SUCCEEDED(status))
   {
     // If we are here, time to start the async copy service operation!
-    status = StartMessageCopyOperation(mCopyFile, mode, turi);
+    status = StartMessageCopyOperation(mCopyFile, mode, tmpUri);
   }
   return status;
 }
 
 //
 // This is pretty much a wrapper to the functionality that lives in the
 // nsMsgCopy class
 //
--- a/mailnews/compose/src/nsMsgSend.h
+++ b/mailnews/compose/src/nsMsgSend.h
@@ -128,16 +128,17 @@
 #include "nsWeakReference.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDOMWindow.h"
 #include "nsIMsgComposeSecure.h"
 #include "nsAutoPtr.h"
 #include "nsMsgAttachmentData.h"
 #include "nsIMsgFilterService.h"
 #include "nsIMsgOperationListener.h"
+#include "nsMsgIncomingServer.h"
 
 //
 // Some necessary defines...
 //
 #define MIME_BUFFER_SIZE      4096 // must be greater than 1000
                                    // SMTP (RFC821) limit
 // Maximum number of bytes we allow in a line before we force
 // encoding to base64 if not already QR-encoded or of type message/rfc822.
@@ -193,16 +194,19 @@ public:
   nsresult    DoFcc();
   nsresult    StartMessageCopyOperation(nsIFile          *aFileSpec,
                                         nsMsgDeliverMode mode,
                                         const nsCString& dest_uri);
 
 
   nsresult SendToMagicFolder(nsMsgDeliverMode flag);
 
+  // For the folderURL return the corresponding pointer to the incoming server.
+  nsresult GetIncomingServer(const char *folderURL, nsIMsgIncomingServer **aServer);
+
   // Check to see if it's ok to save msgs to the configured folder.
   bool CanSaveMessagesToFolder(const char *folderURL);
 
   //
   // FCC operations...
   //
   nsresult    MimeDoFCC (nsIFile *input_file,
     nsMsgDeliverMode mode,
--- a/suite/locales/en-US/chrome/mailnews/compose/composeMsgs.properties
+++ b/suite/locales/en-US/chrome/mailnews/compose/composeMsgs.properties
@@ -107,18 +107,21 @@ smtpAuthMechNotSupported=The Outgoing se
 # LOCALIZATION NOTE (smtpAuthenticationNotSupported): %S is the server hostname
 smtpAuthenticationNotSupported=Unable to authenticate to Outgoing server (SMTP) %S. It does not support authentication (SMTP-AUTH) but you have chosen to use authentication. Please change the 'Authentication method' to 'None' in 'Account Settings | Outgoing server (SMTP)' or contact your email service provider for instructions.
 
 # LOCALIZATION NOTE (errorIllegalLocalPart): %s is an email address with an illegal localpart
 errorIllegalLocalPart=There are non-ASCII characters in the local part of the recipient address %s. This is not yet supported. Please change this address and try again.
 
 ## Strings used for the save message dialog shown when the user closes a message compose window
 saveDlogTitle=Save Message
-## LOCALIZATION NOTE (SaveDlogMessages): %1$S is the folder name
-saveDlogMessages=Message has not been sent. Do you want to save the message in your drafts folder (%1$S)?
+
+## LOCALIZATION NOTE (saveDlogMessages2): Do not translate the words %1$S and \n.
+## %1$S is replaced by the folder name configured for saving drafts (typically the "Drafts" folder).
+## Translate "Compose" to match the translation of item "windowTitlePrefix" below.
+saveDlogMessages2=Message has not been sent or saved in your drafts folder (%1$S).\n"Save" copies the message to your drafts folder (%1$S) and closes the Compose window.\n"Don't Save" closes the Compose window without saving a draft.\n"Cancel" allows you continue writing without saving a draft.
 
 ## generics string
 defaultSubject=(no subject)
 chooseFileToAttach=Attach File(s)
 
 ##
 windowTitlePrefix=Compose:
 
@@ -233,12 +236,34 @@ renameAttachmentMessage=New attachment n
 smtpEnterPasswordPrompt=Enter your password for %S:
 
 ## LOCALIZATION NOTE(smtpEnterPasswordPromptWithUsername): Do not translate the
 ## words %1$S and %2$S. Place the word %1$S where the host name should appear,
 ## and %2$S where the user name should appear.
 smtpEnterPasswordPromptWithUsername=Enter your password for %2$S on %1$S:
 smtpEnterPasswordPromptTitle=Outgoing server (SMTP) Server Password Required
 
-## LOCALIZATION NOTE(errorSavingMsg): Do not translate the word %S. It
-## will be replaced with the name of the folder the message is being saved to.
-errorSavingMsg=There was an error saving the message to %S. Retry?
+## LOCALIZATION NOTE(promptToSaveSentLocally): Do not translate the stings %1$S, %2$S, %3$S and \n.
+## %2$S will be replaced with the account name. $1$S will be replaced by the folder name
+## configured to contain saved sent messages (typically the "Sent" folder).
+## %3$S will be replaced with the local folders account name (typically "Local Folders").
+## Translate "Compose" to match the translation of item "windowTitlePrefix" above.
+promptToSaveSentLocally=Your message was sent but not saved to your sent folder (%1$S) probably because of network errors.\n"Retry" attempts the save again.\n"Save" copies the message to %3$S/%1$S-%2$S and closes the Compose window if it is present.\n"Cancel" does not save the sent message and closes the Compose window if it is present.
 errorFilteringMsg=Your message has been sent and saved, but there was an error while running message filters on it.
+
+## LOCALIZATION NOTE(promptToSaveDraftLocally): Do not translate the stings %1$S, %2$S, %3$S and \n.
+## %2$S will be replaced with the account name. $1$S will be replaced by the folder name
+## configured to contain saved draft messages (typically the "Drafts" folder).
+## %3$S will be replaced with the local folders account name (typically "Local Folders").
+promptToSaveDraftLocally=Your draft message was not saved to your drafts folder (%1$S) probably because of network errors.\n"Retry" attempts to save again.\n"Save" copies the message to %3$S/%1$S-%2$S and you can continue writing.\n"Cancel" allows you to continue writing without saving your draft.
+buttonLabelRetry=Retry
+
+## LOCALIZATION NOTE(promptToSaveTemplateLocally): Do not translate the stings %1$S, %2$S, %3$S and \n.
+## %2$S will be replaced with the account name. $1$S will be replaced by the folder name
+## configured to contain saved templates (typically the "Templates" folder).
+## %3$S will be replaced with the local folders account name (typically "Local Folders").
+promptToSaveTemplateLocally=Your template was not saved to your templates folder (%1$S) probably because of network errors.\n"Retry" attempts to save again.\n"Save" copies the message to %3$S/%1$S-%2$S and you can continue writing.\n"Cancel" allows you to continue writing without saving your template.
+
+## LOCALIZATION NOTE(saveToLocalFoldersFailed): Message appears after normal
+## save fails (e.g., to Sent) and save to Local Folders also fails. This could
+## occur if network is down and filesystem problems are present such as disk
+## full, permission issues or hardware failure.
+saveToLocalFoldersFailed=Unable to save your message to local folders. Possibly out of file storage space.
--- a/suite/mailnews/compose/MsgComposeCommands.js
+++ b/suite/mailnews/compose/MsgComposeCommands.js
@@ -2141,17 +2141,17 @@ function ComposeCanClose()
   {
     // call window.focus, since we need to pop up a dialog
     // and therefore need to be visible (to prevent user confusion)
     window.focus();
     let draftFolderURI = gCurrentIdentity.draftFolder;
     let draftFolderName = MailUtils.getFolderForURI(draftFolderURI).prettyName;
     switch (Services.prompt.confirmEx(window,
               sComposeMsgsBundle.getString("saveDlogTitle"),
-              sComposeMsgsBundle.getFormattedString("saveDlogMessages",[draftFolderName]),
+              sComposeMsgsBundle.getFormattedString("saveDlogMessages2", [draftFolderName]),
               (Services.prompt.BUTTON_TITLE_SAVE * Services.prompt.BUTTON_POS_0) +
               (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1) +
               (Services.prompt.BUTTON_TITLE_DONT_SAVE * Services.prompt.BUTTON_POS_2),
               null, null, null, null, {value:0}))
     {
       case 0: //Save
         // we can close immediately if we already autosaved the draft
         if (!gContentChanged && !gMsgCompose.bodyModified)