Bug 121647 POP/IMAP server passwords are inappropriately forgotten. r=bienvenu,sr=Neil,ui-review=clarkbw
authorMark Banner <bugzilla@standard8.plus.com>
Thu, 02 Jul 2009 08:29:32 +0100
changeset 2990 1d71913d2b496ae605602315ce144b5723c95300
parent 2989 5afa025fbe2baf41cea7ec41b3f7ae3833cd1bf4
child 2991 f9f606a5f2bd6c0363caefcdb4c18ac20291cc2f
push idunknown
push userunknown
push dateunknown
reviewersbienvenu, Neil
bugs121647
Bug 121647 POP/IMAP server passwords are inappropriately forgotten. r=bienvenu,sr=Neil,ui-review=clarkbw
mail/locales/en-US/chrome/messenger/imapMsgs.properties
mailnews/imap/public/nsIImapServerSink.idl
mailnews/imap/src/nsImapIncomingServer.cpp
mailnews/imap/src/nsImapIncomingServer.h
mailnews/imap/src/nsImapProtocol.cpp
mailnews/imap/src/nsImapStringBundle.h
suite/locales/en-US/chrome/mailnews/imapMsgs.properties
--- a/mail/locales/en-US/chrome/messenger/imapMsgs.properties
+++ b/mail/locales/en-US/chrome/messenger/imapMsgs.properties
@@ -30,16 +30,23 @@
 # use your version of this file under the terms of the MPL, indicate your
 # decision by deleting the provisions above and replace them with the notice
 # and other provisions required by the GPL or the LGPL. If you do not delete
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
+imapLoginFailedTitle=Login Failed
+# LOCALIZATION NOTE (imapLoginFailed): Insert "%S" in your translation where
+# you wish to display the hostname of the server to which login failed.
+imapLoginFailed=Login to server %S failed.
+imapLoginFailedRetryButton=&Retry
+imapLoginFailedEnterNewPasswordButton=&Enter New Password
+
 #
 # The following are used by the imap code to display progress/status/error messages
 #
 
 # Status - opening folder
 ## @name IMAP_STATUS_SELECTING_MAILBOX
 ## @loc None
 5000=Opening folder %S…
@@ -142,20 +149,16 @@ 5030=Getting Server Configuration Info…
 ## @name IMAP_GETTING_MAILBOX_INFO
 ## @loc None
 5031=Getting Mailbox Configuration Info…
 
 ## @name IMAP_EMPTY_MIME_PART
 ## @loc None
 5032=This body part will be downloaded on demand.
 
-## @name IMAP_LOGIN_FAILED
-## @loc None
-5035=Login to server %S failed.
-
 ## @name IMAP_RECEIVING_MESSAGE_HEADERS_OF
 ## @loc None
 # LOCALIZATION NOTE (Error 5036): Do not translate the word "%S" or "%lu" below.
 # Place the word %S in your translation where the name of the server should appear.
 # Place the word %lu where the number of headers should appear.
 5036=%S Downloading message header %lu of %lu
 
 ## @name IMAP_RECEIVING_MESSAGE_FLAGS_OF
--- a/mailnews/imap/public/nsIImapServerSink.idl
+++ b/mailnews/imap/public/nsIImapServerSink.idl
@@ -39,17 +39,21 @@
 #include "MailNewsTypes2.idl"
 
 interface nsIMsgWindow;
 interface nsIMsgMailNewsUrl;
 interface nsIImapProtocol;
 interface nsIImapUrl;
 interface nsIImapMockChannel;
 
-[scriptable, uuid(a5f8db71-35a5-4143-b59b-abd067138912)]
+/**
+ * nsIImapServerSink is designed to be used as a proxy to the application's UI
+ * thread from the running IMAP threads.
+ */
+[scriptable, uuid(ef0a5ddc-9478-4fe5-be41-8ec1079ce9d9)]
 interface nsIImapServerSink : nsISupports {
   /**
    * Check if the given folder path is a possible IMAP mailbox.
    * @param folderPath folder path to check
    * @param hierarchyDelimiter IMAP hierarchy delimiter in canonical format,
    *                           i.e., hierarchy delimiter has been replaced
    *                           with '/'
    * @param boxFlags IMAP folder flags (for subscription, namespaces etc.)
@@ -87,19 +91,53 @@ interface nsIImapServerSink : nsISupport
   void retryUrl(in nsIImapUrl imapUrl, in nsIImapMockChannel channel);
 
   /**
    * If previous URL failed, this gives server chance to abort URLs with same
    * mock channel.
    */
   void abortQueuedUrls();
   AString getImapStringByID(in long msgId);
-  AString formatStringWithHostNameByID(in long msgId);
-  void fEAlert(in AString alertSring, in nsIMsgMailNewsUrl url);
-  void fEAlertFromServer(in ACString alertSring, in nsIMsgMailNewsUrl url);
+
+  /**
+   * Alerts the user that the login to the IMAP server failed. Asks whether the
+   * connection should: retry, cancel, or request a new password.
+   *
+   * @param aMsgWindow The message window associated with this action (cannot
+   *                   be null).
+   * @return           The button pressed. 0 for retry, 1 for cancel,
+   *                   2 for enter a new password.
+   */
+  PRInt32 promptLoginFailed(in nsIMsgWindow aMsgWindow);
+
+  /**
+   * Alerts the user with the given string (FE = 'Front End').
+   *
+   * @param aAlertSring  The string to alert the user with.
+   * @param aUrl         The running url.
+   */
+  void fEAlert(in AString aAlertSring, in nsIMsgMailNewsUrl aUrl);
+
+  /**
+   * Alerts the user with a localized string. It will attempt to fill in
+   * the hostname into the string if necessary.
+   *
+   * @param aMsgId  The id of the string to present to the user..
+   * @param aUrl    The running url.
+   */
+  void fEAlertWithID(in long aMsgId, in nsIMsgMailNewsUrl aUrl);
+
+  /**
+   * Takes a response from the server and prepends it with IMAP_SERVER_SAID 
+   *
+   * @param aServerString  The string to alert the user with.
+   * @param url            The running url.
+   */
+  void fEAlertFromServer(in ACString aServerString, in nsIMsgMailNewsUrl aUrl);
+
   void commitNamespaces();
   void promptForPassword(out ACString promptString, in nsIMsgWindow msgWindow);
   attribute boolean userAuthenticated;
   void setMailServerUrls(in ACString manageMailAccount, in ACString manageLists, in ACString manageFilters);
 
   readonly attribute ACString arbitraryHeaders;
   void forgetPassword();
 
--- a/mailnews/imap/src/nsImapIncomingServer.cpp
+++ b/mailnews/imap/src/nsImapIncomingServer.cpp
@@ -1851,28 +1851,100 @@ void nsImapIncomingServer::UnsubscribeFr
       MSG_UrlQueue::AddUrlToPane(unsubscribeUrl, NULL, pane);
       XP_FREE(unsubscribeUrl);
     }
     UnsubscribeFromAllDescendants(currentChild);	// unsubscribe from its children
   }
 }
 #endif // 0
 
+NS_IMETHODIMP
+nsImapIncomingServer::PromptLoginFailed(nsIMsgWindow *aMsgWindow,
+                                        PRInt32 *aResult)
+{
+  NS_ENSURE_ARG_POINTER(aMsgWindow);
+  nsCOMPtr<nsIPrompt> dialog;
+  aMsgWindow->GetPromptDialog(getter_AddRefs(dialog));
+
+  nsresult rv;
+
+  // If we haven't got one, use the default.
+  if (!dialog)
+  {
+    nsCOMPtr<nsIWindowWatcher> wwatch =
+      do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = wwatch->GetNewPrompter(0, getter_AddRefs(dialog));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  rv = GetStringBundle();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCAutoString hostName;
+  GetRealHostName(hostName);
+
+  nsString message(
+    GetFormattedStringFromName(NS_LITERAL_STRING("imapLoginFailed"),
+                               NS_ConvertUTF8toUTF16(hostName)));
+
+  PRBool dummyValue;
+  return dialog->ConfirmEx(
+    GetImapStringByName(NS_LITERAL_STRING("imapLoginFailedTitle")).get(),
+    message.get(),
+    (nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_0) +
+    (nsIPrompt::BUTTON_TITLE_CANCEL * nsIPrompt::BUTTON_POS_1) +
+    (nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2),
+    GetImapStringByName(NS_LITERAL_STRING("imapLoginFailedRetryButton")).get(),
+    nsnull,
+    GetImapStringByName(NS_LITERAL_STRING("imapLoginFailedEnterNewPasswordButton")).get(),
+    nsnull, &dummyValue, aResult);
+}
 
 NS_IMETHODIMP
 nsImapIncomingServer::FEAlert(const nsAString& aString, nsIMsgMailNewsUrl *aUrl)
 {
   nsresult rv;
   nsCOMPtr <nsIMsgMailSession> mailSession =
     do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return mailSession->AlertUser(aString, aUrl);
 }
 
+NS_IMETHODIMP
+nsImapIncomingServer::FEAlertWithID(PRInt32 aMsgId, nsIMsgMailNewsUrl *aUrl)
+{
+  GetStringBundle();
+
+  nsString message;
+
+  if (m_stringBundle)
+  {
+    nsCAutoString hostName;
+    nsresult rv = GetRealHostName(hostName);
+    if (NS_SUCCEEDED(rv))
+    {
+      NS_ConvertASCIItoUTF16 hostStr(hostName);
+      const PRUnichar *params[] = { hostStr.get() };
+      rv = m_stringBundle->FormatStringFromID(aMsgId, params, 1,
+                                              getter_Copies(message));
+      if (NS_SUCCEEDED(rv))
+        return FEAlert(message, aUrl);
+    }
+  }
+
+  // Error condition
+  message.AssignLiteral("String ID ");
+  message.AppendInt(aMsgId);
+  FEAlert(message, aUrl);
+  return NS_OK;
+}
+
 NS_IMETHODIMP  nsImapIncomingServer::FEAlertFromServer(const nsACString& aString,
                                                        nsIMsgMailNewsUrl *aUrl)
 {
   NS_ENSURE_TRUE(!aString.IsEmpty(), NS_OK);
   
   nsresult rv;
   nsCOMPtr <nsIMsgMailSession> mailSession =
     do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
@@ -1915,17 +1987,18 @@ nsresult nsImapIncomingServer::GetString
     static const char propertyURL[] = IMAP_MSGS_URL;
     nsCOMPtr<nsIStringBundleService> sBundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &res);
     if (NS_SUCCEEDED(res) && (nsnull != sBundleService))
       res = sBundleService->CreateBundle(propertyURL, getter_AddRefs(m_stringBundle));
   }
   return (m_stringBundle) ? NS_OK : res;
 }
 
-NS_IMETHODIMP  nsImapIncomingServer::GetImapStringByID(PRInt32 aMsgId, nsAString& aString)
+NS_IMETHODIMP
+nsImapIncomingServer::GetImapStringByID(PRInt32 aMsgId, nsAString& aString)
 {
   nsresult res = NS_OK;
   GetStringBundle();
   if (m_stringBundle)
   {
     res = m_stringBundle->GetStringFromID(aMsgId, getter_Copies(aString));
     if (NS_SUCCEEDED(res))
       return res;
@@ -1933,39 +2006,34 @@ NS_IMETHODIMP  nsImapIncomingServer::Get
   aString.AssignLiteral("String ID ");
   // mscott: FIX ME
   nsString tmpIntStr;
   tmpIntStr.AppendInt(aMsgId);
   aString.Append(tmpIntStr);
   return NS_OK;
 }
 
-NS_IMETHODIMP  nsImapIncomingServer::FormatStringWithHostNameByID(PRInt32 aMsgId, nsAString& aString)
+nsString
+nsImapIncomingServer::GetImapStringByName(const nsString &aName)
 {
-  nsresult res = NS_OK;
+  nsString result;
+
   GetStringBundle();
+
   if (m_stringBundle)
   {
-    nsCAutoString hostName;
-    res = GetRealHostName(hostName);
-    if (NS_SUCCEEDED(res))
-    {
-      NS_ConvertASCIItoUTF16 hostStr (hostName);
-      const PRUnichar *params[] = { hostStr.get() };
-      res = m_stringBundle->FormatStringFromID(aMsgId, params, 1, getter_Copies(aString));
-      if (NS_SUCCEEDED(res))
-        return res;
-    }
+    nsresult rv = m_stringBundle->GetStringFromName(aName.get(),
+                                                    getter_Copies(result));
+    if (NS_SUCCEEDED(rv))
+      return result;
   }
-  aString.AssignLiteral("String ID ");
-  // mscott: FIX ME!!!
-  nsString tmpIntStr;
-  tmpIntStr.AppendInt(aMsgId);
-  aString.Append(tmpIntStr);
-  return NS_OK;
+
+  result.AssignLiteral("Failed to get string named ");
+  result.Append(aName);
+  return result;
 }
 
 nsresult nsImapIncomingServer::ResetFoldersToUnverified(nsIMsgFolder *parentFolder)
 {
   nsresult rv = NS_OK;
   if (!parentFolder)
   {
     nsCOMPtr<nsIMsgFolder> rootFolder;
@@ -2820,16 +2888,38 @@ nsImapIncomingServer::GetFormattedString
 
     rv = m_stringBundle->FormatStringFromID(aID,
                                 formatStrings, 1,
                                 getter_Copies(aResult));
   }
   return rv;
 }
 
+nsString
+nsImapIncomingServer::GetFormattedStringFromName(const nsString &aName,
+                                                 const nsString &aValue)
+{
+  nsString result;
+  GetStringBundle();
+  if (m_stringBundle)
+  {
+    const PRUnichar *formatStrings[] = { aValue.get() };
+
+    nsresult rv = m_stringBundle->FormatStringFromName(aName.get(),
+                                                       formatStrings, 1,
+                                                       getter_Copies(result));
+    if (NS_SUCCEEDED(rv))
+      return result;
+  }
+
+  result.AssignLiteral("Failed to format string named ");
+  result.Append(aName);
+  return result;
+}
+
 nsresult
 nsImapIncomingServer::GetPrefForServerAttribute(const char *prefSuffix, PRBool *prefValue)
 {
   // Any caller of this function must initialize prefValue with a default value
   // as this code will not set prefValue when the pref does not exist and return
   // NS_OK anyway
 
   if (!mPrefBranch)
--- a/mailnews/imap/src/nsImapIncomingServer.h
+++ b/mailnews/imap/src/nsImapIncomingServer.h
@@ -109,30 +109,34 @@ protected:
   void GetUnverifiedSubFolders(nsIMsgFolder *parentFolder,
                                nsCOMArray<nsIMsgImapMailFolder> &aFoldersArray);
   void GetUnverifiedFolders(nsCOMArray<nsIMsgImapMailFolder> &aFolderArray);
   nsresult DeleteNonVerifiedFolders(nsIMsgFolder *parentFolder);
   PRBool NoDescendentsAreVerified(nsIMsgFolder *parentFolder);
   PRBool AllDescendentsAreNoSelect(nsIMsgFolder *parentFolder);
 
   nsresult GetStringBundle();
+  nsString GetImapStringByName(const nsString &aName);
+
   void     GetPFCName(nsACString&); 
   nsresult GetPFCForStringId(PRBool createIfMissing, PRInt32 stringId, nsIMsgFolder **aFolder);
 private:
   nsresult SubscribeToFolder(const PRUnichar *aName, PRBool subscribe);
   nsresult GetImapConnection (nsIEventTarget* aEventTarget,
                                    nsIImapUrl* aImapUrl,
                                    nsIImapProtocol** aImapConnection);
   nsresult CreateProtocolInstance(nsIEventTarget *aEventTarget,
                                            nsIImapProtocol ** aImapConnection);
   nsresult CreateHostSpecificPrefName(const char *prefPrefix, nsCAutoString &prefName);
 
   nsresult DoomUrlIfChannelHasError(nsIImapUrl *aImapUrl, PRBool *urlDoomed);
   PRBool ConnectionTimeOut(nsIImapProtocol* aImapConnection);
   nsresult GetFormattedStringFromID(const nsAString& aValue, PRInt32 aID, nsAString& aResult);
+  nsString GetFormattedStringFromName(const nsString &aName,
+                                      const nsString &aValue);
   nsresult GetPrefForServerAttribute(const char *prefSuffix, PRBool *prefValue);
   PRBool CheckSpecialFolder(nsIRDFService *rdf, nsCString &folderUri,
                             PRUint32 folderFlag, nsCString &existingUri);
 
   nsCOMArray<nsIImapProtocol> m_connectionCache;
   nsCOMArray<nsIImapUrl> m_urlQueue;
   nsCOMPtr<nsIStringBundle>	m_stringBundle;
   nsCOMArray<nsIMsgFolder> m_subscribeFolders; // used to keep folder resources around while subscribe UI is up.
--- a/mailnews/imap/src/nsImapProtocol.cpp
+++ b/mailnews/imap/src/nsImapProtocol.cpp
@@ -4887,21 +4887,17 @@ nsImapProtocol::AlertUserEventUsingId(PR
   {
     PRBool suppressErrorMsg = PR_FALSE;
 
     nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningUrl);
     if (mailnewsUrl)
       mailnewsUrl->GetSuppressErrorMsgs(&suppressErrorMsg);
 
     if (!suppressErrorMsg)
-    {
-      nsString progressString;
-      m_imapServerSink->FormatStringWithHostNameByID(aMessageId, progressString);
-      m_imapServerSink->FEAlert(progressString, mailnewsUrl);
-    }
+      m_imapServerSink->FEAlertWithID(aMessageId, mailnewsUrl);
   }
 }
 
 void
 nsImapProtocol::AlertUserEvent(const char * message)
 {
   if (m_imapServerSink)
   {
@@ -7889,16 +7885,17 @@ nsresult nsImapProtocol::GetPassword(nsC
   }
   m_lastPasswordSent = password;
   return NS_OK;
 }
 
 PRBool nsImapProtocol::TryToLogon()
 {
   PRInt32 logonTries = 0;
+  const PRInt32 maxLogonTries = 4;
   PRBool loginSucceeded = PR_FALSE;
   PRBool clientSucceeded = PR_TRUE;
   nsCAutoString password;
   nsCAutoString userName;
   nsresult rv = NS_OK;
 
   // get the password and user name for the current incoming server...
   nsCOMPtr<nsIMsgIncomingServer> server = do_QueryReferent(m_server);
@@ -7990,34 +7987,56 @@ PRBool nsImapProtocol::TryToLogon()
       {
         rv = GetPassword(password);
         if (NS_FAILED(rv)) break;
         InsecureLogin(userName.get(), password);
       }
 
       if (!clientSucceeded || !GetServerStateParser().LastCommandSuccessful())
       {
+        if (!DeathSignalReceived() && clientSucceeded)
+        {
           // login failed!
           // if we failed because of an interrupt, then do not bother the user
           // similarly - if we failed due to a local error, don't bug them
-          if (m_imapServerSink && !DeathSignalReceived() && clientSucceeded)
+          if (m_imapServerSink)
           {
             nsCOMPtr<nsIMsgWindow> msgWindow;
             GetMsgWindow(getter_AddRefs(msgWindow));
             // if there's no msg window, don't forget the password
             if (msgWindow)
-              rv = m_imapServerSink->ForgetPassword();
+            {
+              PRInt32 buttonPressed = 0;
+              rv = m_imapServerSink->PromptLoginFailed(msgWindow,
+                                                       &buttonPressed);
+              if (NS_SUCCEEDED(rv))
+              {
+                if (buttonPressed == 2)
+                {
+                  // Change password was pressed. For now, forget the
+                  // stored password and we'll prompt for a new one next time
+                  // around.
+                  m_imapServerSink->ForgetPassword();
+                  // Reset logon tries to allow the user to have some failed
+                  // attempts even if they have already pressed retry a few
+                  // times.
+                  logonTries = 0;
+                }
+                else if (buttonPressed == 1)
+                  // Cancel button pressed, abort quickly by exiting the loop
+                  // this time.
+                  logonTries = maxLogonTries;
+              }
+            }
           }
-          if (!DeathSignalReceived() && clientSucceeded)
-          {
-            AlertUserEventUsingId(IMAP_LOGIN_FAILED);
-            m_hostSessionList->SetPasswordForHost(GetImapServerKey(), nsnull);
-            m_currentBiffState = nsIMsgFolder::nsMsgBiffState_Unknown;
-            SendSetBiffIndicatorEvent(m_currentBiffState);
-            password.Truncate();
+
+          m_hostSessionList->SetPasswordForHost(GetImapServerKey(), nsnull);
+          m_currentBiffState = nsIMsgFolder::nsMsgBiffState_Unknown;
+          SendSetBiffIndicatorEvent(m_currentBiffState);
+          password.Truncate();
         } // if we didn't receive the death signal...
       } // if login failed
       else  // login succeeded
       {
         PRBool passwordAlreadyVerified;
         rv = m_hostSessionList->SetPasswordForHost(GetImapServerKey(), password.get());
         rv = m_hostSessionList->GetPasswordVerifiedOnline(GetImapServerKey(), passwordAlreadyVerified);
         if (NS_SUCCEEDED(rv) && !passwordAlreadyVerified)
@@ -8056,17 +8075,17 @@ PRBool nsImapProtocol::TryToLogon()
 #ifdef UNREADY_CODE
         SetCurrentEntryStatus(-1);
         SetConnectionStatus(-1);        // stop netlib
 #endif
         break;
       }
   }
 
-  while (!loginSucceeded && ++logonTries < 4);
+  while (!loginSucceeded && ++logonTries < maxLogonTries);
 
   if (!loginSucceeded)
   {
     m_currentBiffState = nsIMsgFolder::nsMsgBiffState_Unknown;
     SendSetBiffIndicatorEvent(m_currentBiffState);
     HandleCurrentUrlError();
     SetConnectionStatus(-1);        // stop netlib
   }
--- a/mailnews/imap/src/nsImapStringBundle.h
+++ b/mailnews/imap/src/nsImapStringBundle.h
@@ -62,17 +62,16 @@ PR_END_EXTERN_C
 #define	IMAP_STATUS_CHECK_COMPAT                                  5012
 #define	IMAP_STATUS_SENDING_LOGIN                                  5013
 #define	IMAP_STATUS_SENDING_AUTH_LOGIN                                  5014
 #define	IMAP_DOWNLOADING_MESSAGE                                  5015
 #define	IMAP_GETTING_ACL_FOR_FOLDER                                  5029
 #define	IMAP_GETTING_SERVER_INFO                                  5030
 #define	IMAP_GETTING_MAILBOX_INFO                                  5031
 #define	IMAP_EMPTY_MIME_PART                                  5032
-#define	IMAP_LOGIN_FAILED                                  5035
 #define	IMAP_RECEIVING_MESSAGE_HEADERS_OF                                  5036
 #define	IMAP_RECEIVING_MESSAGE_FLAGS_OF                                  5037
 #define	IMAP_DELETING_MESSAGES                                  5038
 #define	IMAP_DELETING_MESSAGE                   5039
 #define	IMAP_MOVING_MESSAGES_TO                 5040
 #define	IMAP_MOVING_MESSAGE_TO                  5041
 #define	IMAP_COPYING_MESSAGES_TO                5042
 #define	IMAP_COPYING_MESSAGE_TO                 5043
--- a/suite/locales/en-US/chrome/mailnews/imapMsgs.properties
+++ b/suite/locales/en-US/chrome/mailnews/imapMsgs.properties
@@ -30,16 +30,23 @@
 # use your version of this file under the terms of the MPL, indicate your
 # decision by deleting the provisions above and replace them with the notice
 # and other provisions required by the GPL or the LGPL. If you do not delete
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
+imapLoginFailedTitle=Login Failed
+# LOCALIZATION NOTE (imapLoginFailed): Insert "%S" in your translation where
+# you wish to display the hostname of the server to which login failed.
+imapLoginFailed=Login to server %S failed.
+imapLoginFailedRetryButton=&Retry
+imapLoginFailedEnterNewPasswordButton=&Enter New Password
+
 #
 # The following are used by the imap code to display progress/status/error messages
 #
 
 # Status - opening folder
 ## @name IMAP_STATUS_SELECTING_MAILBOX
 ## @loc None
 5000=Opening folder %S…
@@ -142,20 +149,16 @@ 5030=Getting Server Configuration Info…
 ## @name IMAP_GETTING_MAILBOX_INFO
 ## @loc None
 5031=Getting Mailbox Configuration Info…
 
 ## @name IMAP_EMPTY_MIME_PART
 ## @loc None
 5032=This body part will be downloaded on demand.
 
-## @name IMAP_LOGIN_FAILED
-## @loc None
-5035=Login to server %S failed.
-
 ## @name IMAP_RECEIVING_MESSAGE_HEADERS_OF
 ## @loc None
 # LOCALIZATION NOTE (Error 5036): Do not translate the word "%S" or "%lu" below.
 # Place the word %S in your translation where the name of the server should appear.
 # Place the word %lu where the number of headers should appear.
 5036=%S Downloading message header %lu of %lu
 
 ## @name IMAP_RECEIVING_MESSAGE_FLAGS_OF