Bug 201750 - Combine the news username and password auth dialogs. r=Standard8, sr=bienvenu, ui-r=bwinton
authorJoshua Cranmer <Pidgeot18@gmail.com>
Tue, 14 Feb 2012 16:04:41 -0600
changeset 10838 a2e8883c30bf052ae00f0a4cdc19da69e24d2f90
parent 10837 8f6ef3a01d419cacaf029d430d6f147fe931247b
child 10839 9d43b85434a1dfe3f98c0c6c12bbe4746148e790
push id463
push userbugzilla@standard8.plus.com
push dateTue, 24 Apr 2012 17:34:51 +0000
treeherdercomm-beta@e53588e8f7b0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersStandard8, bienvenu, bwinton
bugs201750
Bug 201750 - Combine the news username and password auth dialogs. r=Standard8, sr=bienvenu, ui-r=bwinton
mail/locales/en-US/chrome/messenger/news.properties
mailnews/news/public/nsIMsgNewsFolder.idl
mailnews/news/src/nsNNTPProtocol.cpp
mailnews/news/src/nsNewsFolder.cpp
mailnews/news/src/nsNewsFolder.h
mailnews/news/src/nsNntpIncomingServer.cpp
mailnews/news/test/unit/test_nntpPassword3.js
suite/locales/en-US/chrome/mailnews/news.properties
--- a/mail/locales/en-US/chrome/messenger/news.properties
+++ b/mail/locales/en-US/chrome/messenger/news.properties
@@ -41,21 +41,22 @@ htmlNewsErrorTitle=Error!
 # LOCALIZATION NOTE ( htmlNewsError ): In the following item, translate only "Error!" and "newsgroup server responded:"
 htmlNewsError=<H1>Error!</H1>newsgroup server responded: 
 # LOCALIZATION NOTE ( articleExpired ): In the following item, translate only "Perhaps the article has expired"
 articleExpired=<B><P>Perhaps the article has expired</P></B>
 removeExpiredArtLinkText=Click here to remove all expired articles
 cancelDisallowed=This message does not appear to be from you.  You may only cancel your own posts, not those made by others.
 cancelConfirm=Are you sure you want to cancel this message?
 messageCancelled=Message cancelled.
-enterUsername=Please enter a username for news server access:
-enterUsernameTitle=News Server Username Required
-saveUsername=Use Password Manager to remember this value.
-enterPassword=Please enter a password for news server access:
-enterPasswordTitle=News Server Password Required
+enterUserPassTitle=News Server Username and Password Required
+# LOCALIZATION NOTE (enterUserPassServer): %S is the server being accessed
+enterUserPassServer=Please enter a username and password for %S:
+# LOCALIZATION NOTE (enterUserPassGroup): %1$S is a specific newsgroup to set
+# the password for; %2$S is the server from which the newsgroup is accessed
+enterUserPassGroup=Please enter a username and password for %1$S on %2$S:
 okButtonText=Download
 
 noNewMessages=There are no new messages on the server.
 # LOCALIZATION NOTE (newNewsgroupHeaders): %1$S is the number of the current
 # header being downloaded, %2$S is the number of headers to be downloaded, and
 #  %3$S is the newsgroup whose headers are being downloaded.
 newNewsgroupHeaders=Downloading %1$S of %2$S headers for %3$S
 # LOCALIZATION NOTE (newNewsgroupFilteringHeaders): %1$S is the name of the MIME
--- a/mailnews/news/public/nsIMsgNewsFolder.idl
+++ b/mailnews/news/public/nsIMsgNewsFolder.idl
@@ -42,41 +42,104 @@
 #include "nsTArray.h"
 %}
 
 interface nsIMsgWindow;
 interface nsINntpIncomingServer;
 
 [ref] native nsMsgKeyArrayRef(nsTArray<nsMsgKey>);
 
-[scriptable, uuid(86a38356-6ab0-4117-8269-674cbb4f264f)]
+[scriptable, uuid(3bf5c65d-71bd-4560-a337-435797d249a6)]
 interface nsIMsgNewsFolder : nsISupports {
-  attribute ACString groupUsername;
-  attribute ACString groupPassword;
-
   readonly attribute AString unicodeName;
   /**|rawName| is an 8-bit string to represent the name of a newsgroup used by 
    * a news server. It's offered for the convenience of callers so that they 
    * don't have to convert |unicodeName| to the server-side name when 
    * communicating with a news server.  It's US-ASCII except for some 
    * 'stand-alone' Chinese news servers that use GB2312 for newsgroup names 
    * violating RFC 1036. For those servers, it's GB2312. However, it can be any
    * other single and multibyte encoding in principle. The encoding of this 
    * string is stored in |nsINntpIncomingServer| because that's a server-wide
    * property.
    **/
   [noscript] readonly attribute ACString rawName;
   readonly attribute nsINntpIncomingServer nntpServer;
   attribute boolean saveArticleOffline;
-   
-  ACString getGroupPasswordWithUI(in AString aPromptString, in AString aPromptTitle, in nsIMsgWindow aMsgWindow);
-  ACString getGroupUsernameWithUI(in AString aPromptString, in AString aPromptTitle, in nsIMsgWindow aMsgWindow);
+
+  /**
+   * @name Authentication methods
+   * NNTP authentication is slightly wonky, due to edge cases that are not seen
+   * in other protocols. Authentication is not necessary; if authentication is
+   * used, it could be configured on a per-group basis or even require only a
+   * username and not a password.
+   *
+   * Since passwords could be per-group, it is necessary to refer to passwords
+   * using the methods on this interface and not nsIMsgIncomingServer. Passwords
+   * for the server as a whole are found via the root folder. If the server is
+   * configured to use single sign-on (the default), asking any group for its
+   * password will result in the server's password, otherwise, each group stores
+   * its password individually.
+   *
+   * Due to this setup, most of the password management functions on
+   * nsIMsgIncomingServer do not correctly work. The only one that would affect
+   * the passwords stored on folders correctly is forgetPassword; using any
+   * other on a news server would result in inconsistent state.
+   *
+   * Before requesting either the username or password for authentication, it is
+   * first necessary to call getAuthenticationCredentials. If the method returns
+   * true, then groupUsername and groupPassword are appropriately set up for
+   * necessary authentication; if not, then authentication must be stopped.
+   */
+  /// @{
 
-  void forgetGroupUsername();
-  void forgetGroupPassword();
+  /**
+   * Gets the authentication credentials, returning if the results are valid.
+   *
+   * If mustPrompt is true, then the user will always be asked for the
+   * credentials. Otherwise, if mayPrompt is true, then the user will be asked
+   * for credentials if there are no saved credentials. If mayPrompt is false,
+   * then no prompt will be shown, even if there are no saved credentials.
+   *
+   * If this method returns true, then groupUsername and groupPassword will
+   * contain non-empty results that could be used for authentication. If this
+   * method returns false, then the values of groupUsername and groupPassword
+   * will be cleared if they had previously been set. This could happen if
+   * mustPrompt were true and the user decided to cancel the authentication
+   * prompt.
+   *
+   * Note that this method will be executed synchronously; if an async prompt
+   * is wanted, it is the responsibility of the caller to manage it explicitly
+   * with nsIMsgAsyncPrompter.
+   */
+  bool getAuthenticationCredentials(in nsIMsgWindow aMsgWindow,
+    in bool mayPrompt, in bool mustPrompt);
+
+  /**
+   * Force migration of credentials from older versions of this codebase.
+   *
+   * This method is normally called during the creation of newsgroup folders,
+   * and it should not be necessary to call this method. This method also
+   * forcibly deletes the old credentials, so that passwords would not be
+   * leaked.
+   *
+   * It is expected that this method will be removed once it is decided that
+   * supporting migration from old versions of Thunderbird and SeaMonkey should
+   * not be supported.
+   */
+  void migrateLegacyCredentials();
+
+  /// The username that should be used for this group
+  attribute ACString groupUsername;
+
+  /// The password that should be used for this group
+  attribute ACString groupPassword;
+
+  /// Forgets saved authentication credentials permanently.
+  void forgetAuthenticationCredentials();
+  /// @}
   
   void moveFolder(in nsIMsgFolder aNewsgroupToMove, in nsIMsgFolder aRefNewsgroup, in PRInt32 aOrientation);
 
   nsIMsgFolder addNewsgroup(in AUTF8String newsgroupName, in ACString setStr);
 
   void setReadSetFromStr(in ACString setStr);
 
   readonly attribute ACString newsrcLine;
--- a/mailnews/news/src/nsNNTPProtocol.cpp
+++ b/mailnews/news/src/nsNNTPProtocol.cpp
@@ -2380,65 +2380,39 @@ PRInt32 nsNNTPProtocol::BeginAuthorizati
       rv = server->GetRootFolder(getter_AddRefs(rootFolder));
       if (NS_SUCCEEDED(rv) && rootFolder) {
         m_newsFolder = do_QueryInterface(rootFolder);
       }
     }
   }
 
   NS_ASSERTION(m_newsFolder, "no m_newsFolder");
-  nsCString cachedUsername;
+  if (!m_newsFolder)
+    return MK_NNTP_AUTH_FAILED;
+  
+  // Force-grab the authentication details...
+  nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
+  if (!m_msgWindow && mailnewsurl)
+    mailnewsurl->GetMsgWindow(getter_AddRefs(m_msgWindow));
+  bool validCredentials = false;
+  rv = m_newsFolder->GetAuthenticationCredentials(m_msgWindow, true, false,
+    &validCredentials);
+  if (NS_FAILED(rv) || !validCredentials)
+    return MK_NNTP_AUTH_FAILED;
+
   nsCString username;
-  if (m_newsFolder)
-    rv = m_newsFolder->GetGroupUsername(cachedUsername);
-
-  if (NS_FAILED(rv) || cachedUsername.IsEmpty()) {
-    rv = NS_OK;
-    NNTP_LOG_NOTE("ask for the news username");
-
-    nsString usernamePromptText;
-    GetNewsStringByName("enterUsername", getter_Copies(usernamePromptText));
-    nsString usernamePromptTitleText;
-    GetNewsStringByName("enterUsernameTitle",
-                        getter_Copies(usernamePromptTitleText));
-    if (m_newsFolder) {
-      if (!m_msgWindow) {
-        nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
-        if (mailnewsurl)
-          rv = mailnewsurl->GetMsgWindow(getter_AddRefs(m_msgWindow));
-      }
-
-      rv = m_newsFolder->GetGroupUsernameWithUI(usernamePromptText,
-                                                usernamePromptTitleText,
-                                                m_msgWindow, username);
-    }
-    else
-      return(MK_NNTP_AUTH_FAILED);
-
-    if (NS_FAILED(rv)) {
-      AlertError(MK_NNTP_AUTH_FAILED, "Aborted by user");
-      return(MK_NNTP_AUTH_FAILED);
-    }
-  } // !username
-
-  if (NS_FAILED(rv) || (username.IsEmpty() && cachedUsername.IsEmpty()))
-    return(MK_NNTP_AUTH_FAILED);
+  rv = m_newsFolder->GetGroupUsername(username);
+  if (NS_FAILED(rv) || username.IsEmpty())
+    return MK_NNTP_AUTH_FAILED;
 
   NS_MsgSACopy(&command, "AUTHINFO user ");
-  if (!cachedUsername.IsEmpty()) {
-    PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) use %s as the username",this, cachedUsername.get()));
-    NS_MsgSACat(&command, cachedUsername.get());
-  }
-  else {
-    PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) use %s as the username",this, username.get()));
-    NS_MsgSACat(&command, username.get());
-  }
+  PR_LOG(NNTP, PR_LOG_ALWAYS,("(%p) use %s as the username", this, username.get()));
+  NS_MsgSACat(&command, username.get());
   NS_MsgSACat(&command, CRLF);
 
-  nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
   if (mailnewsurl)
     status = SendData(mailnewsurl, command);
 
   PR_Free(command);
 
   m_nextState = NNTP_RESPONSE;
   m_nextStateAfterResponse = NNTP_AUTHORIZE_RESPONSE;
 
@@ -2447,17 +2421,16 @@ PRInt32 nsNNTPProtocol::BeginAuthorizati
   return status;
 }
 
 PRInt32 nsNNTPProtocol::AuthorizationResponse()
 {
   nsresult rv = NS_OK;
   PRInt32 status = 0;
 
-
   if (MK_NNTP_RESPONSE_AUTHINFO_OK == m_responseCode ||
     MK_NNTP_RESPONSE_AUTHINFO_SIMPLE_OK == m_responseCode)
   {
     /* successful login */
 #ifdef HAVE_NNTP_EXTENSIONS
     bool pushAuth;
     /* If we're here because the host demanded authentication before we
     * even sent a single command, then jump back to the beginning of everything
@@ -2480,69 +2453,28 @@ PRInt32 nsNNTPProtocol::AuthorizationRes
     else
       m_nextState = SEND_FIRST_NNTP_COMMAND;
 #endif /* HAVE_NNTP_EXTENSIONS */
 
     return(0);
   }
   else if (MK_NNTP_RESPONSE_AUTHINFO_CONT == m_responseCode)
   {
-    /* password required */
     char * command = 0;
+
+    // Since we had to have called BeginAuthorization to get here, we've already
+    // prompted for the authorization credentials. Just grab them without a
+    // further prompt.
     nsCString password;
-    nsCString cachedPassword;
-
-    NS_ASSERTION(m_newsFolder, "no newsFolder");
-    if (m_newsFolder)
-      rv = m_newsFolder->GetGroupPassword(cachedPassword);
-
-    if (NS_FAILED(rv) || cachedPassword.IsEmpty()) {
-      rv = NS_OK;
-      NNTP_LOG_NOTE("ask for the news password");
-
-      nsString passwordPromptText;
-      GetNewsStringByName("enterPassword", getter_Copies(passwordPromptText));
-      nsString passwordPromptTitleText;
-      GetNewsStringByName("enterPasswordTitle", getter_Copies(passwordPromptTitleText));
-
-      NS_ASSERTION(m_newsFolder, "no newsFolder");
-      if (m_newsFolder) {
-        if (!m_msgWindow) {
-          nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
-          if (mailnewsurl)
-            rv = mailnewsurl->GetMsgWindow(getter_AddRefs(m_msgWindow));
-        }
-
-        rv = m_newsFolder->GetGroupPasswordWithUI(passwordPromptText, passwordPromptTitleText,
-                                                  m_msgWindow, password);
-      }
-      else {
-        NNTP_LOG_NOTE("we don't know the folder");
-        NNTP_LOG_NOTE("this can happen if someone gives us just an article url");
-        return(MK_NNTP_AUTH_FAILED);
-      }
-
-      if (NS_FAILED(rv)) {
-        AlertError(MK_NNTP_AUTH_FAILED,"Aborted by user");
-        return(MK_NNTP_AUTH_FAILED);
-      }
-    }
-
-    if(NS_FAILED(rv) || (password.IsEmpty() && cachedPassword.IsEmpty()))
-      return(MK_NNTP_AUTH_FAILED);
+    rv = m_newsFolder->GetGroupPassword(password);
+    if (NS_FAILED(rv) || password.IsEmpty())
+      return MK_NNTP_AUTH_FAILED;
 
     NS_MsgSACopy(&command, "AUTHINFO pass ");
-    if (!cachedPassword.IsEmpty()) {
-      PR_LOG(NNTP,PR_LOG_ALWAYS,("(%p) use cached password", this));
-      NS_MsgSACat(&command, cachedPassword.get());
-    }
-    else {
-      // *don't log the password!* PR_LOG(NNTP,PR_LOG_ALWAYS,("use %s as the password",(const char *)password));
-      NS_MsgSACat(&command, password.get());
-    }
+    NS_MsgSACat(&command, password.get());
     NS_MsgSACat(&command, CRLF);
 
     nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningURL);
     if (mailnewsurl)
       status = SendData(mailnewsurl, command, true);
 
     PR_FREEIF(command);
 
@@ -3011,20 +2943,17 @@ void nsNNTPProtocol::HandleAuthenticatio
 
   /* login failed */
   AlertError(MK_NNTP_AUTH_FAILED, m_responseText);
 
   NS_ASSERTION(m_newsFolder, "no newsFolder");
   if (m_newsFolder)
   {
     if (!userHasAuthenticatedInThisSession)
-    {
-      (void) m_newsFolder->ForgetGroupUsername();
-      (void) m_newsFolder->ForgetGroupPassword();
-    }
+      m_newsFolder->ForgetAuthenticationCredentials();
     // we'll allow one failure before clearing out password,
     // but we need to handle the case where the password has
     // changed while the app is running, and
     // reprompt the user for the (potentially) new password.
     // So clear the userAuthenticated flag.
     m_nntpServer->SetUserAuthenticated(false);
   }
 }
--- a/mailnews/news/src/nsNewsFolder.cpp
+++ b/mailnews/news/src/nsNewsFolder.cpp
@@ -215,16 +215,19 @@ nsMsgNewsFolder::AddNewsgroup(const nsAC
   if (NS_FAILED(rv)) return rv;
 
   nsCOMPtr<nsIMsgNewsFolder> newsFolder(do_QueryInterface(res, &rv));
   if (NS_FAILED(rv)) return rv;
 
   // cache this for when we open the db
   rv = newsFolder->SetReadSetFromStr(setStr);
 
+  // I don't have a good time to do this, but this is as good as any...
+  newsFolder->MigrateLegacyCredentials();
+
   rv = folder->SetParent(this);
   NS_ENSURE_SUCCESS(rv,rv);
 
   // this what shows up in the UI
   rv = folder->SetName(nameUtf16);
   NS_ENSURE_SUCCESS(rv,rv);
 
   rv = folder->SetFlag(nsMsgFolderFlags::Newsgroup);
@@ -1083,27 +1086,18 @@ NS_IMETHODIMP nsMsgNewsFolder::GetGroupP
 }
 
 NS_IMETHODIMP nsMsgNewsFolder::SetGroupPassword(const nsACString& aGroupPassword)
 {
   mGroupPassword = aGroupPassword;
   return NS_OK;
 }
 
-nsresult nsMsgNewsFolder::CreateNewsgroupUsernameUrlForSignon(const nsACString& inUriStr, nsACString& result)
-{
-  return CreateNewsgroupUrlForSignon(inUriStr, "username", result);
-}
-
-nsresult nsMsgNewsFolder::CreateNewsgroupPasswordUrlForSignon(const nsACString& inUriStr, nsACString& result)
-{
-  return CreateNewsgroupUrlForSignon(inUriStr, "password", result);
-}
-
-nsresult nsMsgNewsFolder::CreateNewsgroupUrlForSignon(const nsACString& inUriStr, const char *ref, nsACString& result)
+nsresult nsMsgNewsFolder::CreateNewsgroupUrlForSignon(const char *ref,
+    nsAString &result)
 {
   nsresult rv;
   nsCOMPtr<nsIURL> url = do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv,rv);
 
   nsCOMPtr<nsIMsgIncomingServer> server;
   rv = GetServer(getter_AddRefs(server));
   if (NS_FAILED(rv)) return rv;
@@ -1121,17 +1115,17 @@ nsresult nsMsgNewsFolder::CreateNewsgrou
     rv = server->GetServerURI(serverURI);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = url->SetSpec(serverURI);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   else
   {
-    rv = url->SetSpec(inUriStr);
+    rv = url->SetSpec(mURI);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   PRInt32 port = 0;
   rv = url->GetPort(&port);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (port <= 0)
@@ -1149,112 +1143,282 @@ nsresult nsMsgNewsFolder::CreateNewsgrou
     // password manager "blanks" those out.
     if (socketType == nsMsgSocketType::SSL)
     {
       rv = url->SetPort(nsINntpUrl::DEFAULT_NNTPS_PORT);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
+  nsCString rawResult;
   if (ref)
   {
     rv = url->SetRef(nsDependentCString(ref));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    return url->GetSpec(result);
+    rv = url->GetSpec(rawResult);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
+  else
+  {
+    // If the url doesn't have a path, make sure we don't get a '/' on the end
+    // as that will confuse searching in password manager.
+    nsCString spec;
+    rv = url->GetSpec(spec);
+    NS_ENSURE_SUCCESS(rv, rv);
 
-  // If the url doesn't have a path, make sure we don't get a '/' on the end
-  // as that will confuse searching in password manager.
-  nsCString spec;
-  rv = url->GetSpec(spec);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (!spec.IsEmpty() && spec[spec.Length() - 1] == '/')
-    result = StringHead(spec, spec.Length() - 1);
-  else
-    result = spec;
-
+    if (!spec.IsEmpty() && spec[spec.Length() - 1] == '/')
+      rawResult = StringHead(spec, spec.Length() - 1);
+    else
+      rawResult = spec;
+  }
+  result = NS_ConvertASCIItoUTF16(rawResult);
   return NS_OK;
 }
 
-NS_IMETHODIMP nsMsgNewsFolder::ForgetGroupUsername()
+NS_IMETHODIMP
+nsMsgNewsFolder::MigrateLegacyCredentials()
 {
-  nsCString hostname;
-  nsresult rv = CreateNewsgroupUrlForSignon(mURI, nsnull, hostname);
+  // The original ways that authentication credentials were stored was rather
+  // complicated and messy. We used separate URLs as the "HTTP realm" field to
+  // permit prompting for username and password as separate dialogs. In this
+  // method, we check for this, and store them in the new unified credentials
+  // dialog.
+
+  // Create the URLs that the login manager needs
+  nsString signonUrl;
+  nsresult rv = CreateNewsgroupUrlForSignon(nsnull, signonUrl);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsString usernameUrl;
+  rv = CreateNewsgroupUrlForSignon("username", usernameUrl);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsString passwordUrl;
+  rv = CreateNewsgroupUrlForSignon("password", passwordUrl);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsILoginManager> loginMgr =
+    do_GetService(NS_LOGINMANAGER_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Grab out the saved username
+  PRUint32 count = 0;
+  nsILoginInfo **logins = nsnull;
+  rv = loginMgr->FindLogins(&count, signonUrl, EmptyString(), usernameUrl,
+    &logins);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ASSERTION(count <= 1, "Too many usernames?");
+
+  nsString username;
+  if (count > 0)
+  {
+    rv = logins[0]->GetPassword(username);
+    // Remove the saved login
+    loginMgr->RemoveLogin(logins[0]);
+  }
+
+  NS_FREE_XPCOM_ISUPPORTS_POINTER_ARRAY(count, logins);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Do the same things for the password
+  rv = loginMgr->FindLogins(&count, signonUrl, EmptyString(), passwordUrl,
+                            &logins);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ASSERTION(count <= 1, "Too many passwords?");
+
+  nsString password;
+  if (count > 0)
+  {
+    rv = logins[0]->GetPassword(password);
+    loginMgr->RemoveLogin(logins[0]);
+  }
+  NS_FREE_XPCOM_ISUPPORTS_POINTER_ARRAY(count, logins);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Make and add the new logon
+  nsCOMPtr<nsILoginInfo> newLogin = do_CreateInstance(NS_LOGININFO_CONTRACTID);
+  // We need to pass in JS equivalent to "null"; empty ("") isn't good enough
+  nsString voidString;
+  voidString.SetIsVoid(true);
+  newLogin->Init(signonUrl, voidString, signonUrl, username, password,
+    EmptyString(), EmptyString());
+  return loginMgr->AddLogin(newLogin);
+}
+
+NS_IMETHODIMP
+nsMsgNewsFolder::GetAuthenticationCredentials(nsIMsgWindow *aMsgWindow,
+    bool mayPrompt, bool mustPrompt, bool *validCredentials)
+{
+  // Not strictly necessary, but it would help consumers to realize that this is
+  // a rather nonsensical combination.
+  NS_ENSURE_FALSE(mustPrompt && !mayPrompt, NS_ERROR_INVALID_ARG);
+  NS_ENSURE_ARG_POINTER(validCredentials);
+
+  nsCOMPtr<nsIStringBundleService> bundleService =
+    mozilla::services::GetStringBundleService();
+  NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+
+  nsresult rv;
+  nsCOMPtr<nsIStringBundle> bundle;
+  rv = bundleService->CreateBundle(NEWS_MSGS_URL, getter_AddRefs(bundle));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsString signonUrl;
+  rv = CreateNewsgroupUrlForSignon(nsnull, signonUrl);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCString signonURL;
-  rv = CreateNewsgroupUsernameUrlForSignon(mURI, signonURL);
+  // If we don't have a username or password, try to load it via the login mgr.
+  // Do this even if mustPrompt is true, to prefill the dialog.
+  if (mGroupUsername.IsEmpty() || mGroupPassword.IsEmpty())
+  {
+    nsCOMPtr<nsILoginManager> loginMgr =
+      do_GetService(NS_LOGINMANAGER_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    PRUint32 numLogins = 0;
+    nsILoginInfo **logins = nsnull;
+    rv = loginMgr->FindLogins(&numLogins, signonUrl, EmptyString(), signonUrl,
+      &logins);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (numLogins > 0)
+    {
+      nsString uniUsername, uniPassword;
+      logins[0]->GetUsername(uniUsername);
+      logins[0]->GetPassword(uniPassword);
+      mGroupUsername = NS_LossyConvertUTF16toASCII(uniUsername);
+      mGroupPassword = NS_LossyConvertUTF16toASCII(uniPassword);
+
+      *validCredentials = true;
+    }
+    NS_FREE_XPCOM_ISUPPORTS_POINTER_ARRAY(numLogins, logins);
+  }
+
+  // Show the prompt if we need to
+  if (mustPrompt ||
+      (mayPrompt && (mGroupUsername.IsEmpty() || mGroupPassword.IsEmpty())))
+  {
+    nsCOMPtr<nsIAuthPrompt> dialog;
+    if (aMsgWindow)
+    {
+      nsCOMPtr<nsIDocShell> docShell;
+      rv = aMsgWindow->GetRootDocShell(getter_AddRefs(docShell));
+      NS_ENSURE_SUCCESS(rv, rv);
+      dialog = do_GetInterface(docShell, &rv);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+    else
+    {
+      nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
+      if (wwatch)
+        wwatch->GetNewAuthPrompter(0, getter_AddRefs(dialog));
+      if (!dialog) return NS_ERROR_FAILURE;
+    }
+
+    NS_ASSERTION(dialog, "We didn't get a net prompt");
+    if (dialog)
+    {
+      // Format the prompt text strings
+      nsString promptTitle, promptText;
+      bundle->GetStringFromName(NS_LITERAL_STRING("enterUserPassTitle").get(),
+        getter_Copies(promptTitle));
+
+      nsString serverName;
+      nsCOMPtr<nsIMsgIncomingServer> server;
+      rv = GetServer(getter_AddRefs(server));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      server->GetPrettyName(serverName);
+
+      nsCOMPtr<nsINntpIncomingServer> nntpServer;
+      rv = GetNntpServer(getter_AddRefs(nntpServer));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      bool singleSignon = true;
+      nntpServer->GetSingleSignon(&singleSignon);
+
+      const PRUnichar *params[2];
+      params[0] = mName.get();
+      params[1] = serverName.get();
+      if (singleSignon)
+        bundle->FormatStringFromName(
+          NS_LITERAL_STRING("enterUserPassServer").get(),
+          &params[1], 1, getter_Copies(promptText));
+      else
+        bundle->FormatStringFromName(
+          NS_LITERAL_STRING("enterUserPassGroup").get(),
+          params, 2, getter_Copies(promptText));
+
+      // Fill the signon url for the dialog
+      nsString signonURL;
+      rv = CreateNewsgroupUrlForSignon(nsnull, signonURL);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      // Prefill saved username/password
+      PRUnichar *uniGroupUsername = ToNewUnicode(
+        NS_ConvertASCIItoUTF16(mGroupUsername));
+      PRUnichar *uniGroupPassword = ToNewUnicode(
+        NS_ConvertASCIItoUTF16(mGroupPassword));
+
+      // Prompt for the dialog
+      rv = dialog->PromptUsernameAndPassword(promptTitle.get(),
+        promptText.get(), signonURL.get(),
+        nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY,
+        &uniGroupUsername, &uniGroupPassword, validCredentials);
+
+      nsAutoString uniPasswordAdopted, uniUsernameAdopted;
+      uniPasswordAdopted.Adopt(uniGroupPassword);
+      uniUsernameAdopted.Adopt(uniGroupUsername);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      // Only use the username/password if the user didn't cancel.
+      if (*validCredentials)
+      {
+        SetGroupUsername(NS_LossyConvertUTF16toASCII(uniUsernameAdopted));
+        SetGroupPassword(NS_LossyConvertUTF16toASCII(uniPasswordAdopted));
+      }
+      else
+      {
+        mGroupUsername.Truncate();
+        mGroupPassword.Truncate();
+      }
+    }
+  }
+  
+  *validCredentials = !(mGroupUsername.IsEmpty() || mGroupPassword.IsEmpty());
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgNewsFolder::ForgetAuthenticationCredentials()
+{
+  nsString signonUrl;
+  nsresult rv = CreateNewsgroupUrlForSignon(nsnull, signonUrl);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsILoginManager> loginMgr =
     do_GetService(NS_LOGINMANAGER_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   PRUint32 count;
   nsILoginInfo** logins;
 
-  // XXX we don't support multiple logins per news server, so just delete any
-  // we find.
-  rv = loginMgr->FindLogins(&count, NS_ConvertASCIItoUTF16(hostname),
-                            EmptyString(),
-                            NS_ConvertASCIItoUTF16(signonURL), &logins);
+  rv = loginMgr->FindLogins(&count, signonUrl, EmptyString(), signonUrl,
+    &logins);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // There should only be one-login stored for this url, however just in case
   // there isn't.
   for (PRUint32 i = 0; i < count; ++i)
-  {
-    // If this fails, just continue, we'll still want to remove the password
-    // from our local cache.
     loginMgr->RemoveLogin(logins[i]);
-  }
   NS_FREE_XPCOM_ISUPPORTS_POINTER_ARRAY(count, logins);
 
   return NS_OK;
 }
 
-NS_IMETHODIMP nsMsgNewsFolder::ForgetGroupPassword()
-{
-  nsCString hostname;
-  nsresult rv = CreateNewsgroupUrlForSignon(mURI, nsnull, hostname);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCString signonURL;
-  rv = CreateNewsgroupPasswordUrlForSignon(mURI, signonURL);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsILoginManager> loginMgr =
-    do_GetService(NS_LOGINMANAGER_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PRUint32 count;
-  nsILoginInfo** logins;
-
-  // XXX we don't support multiple logins per news server, so just delete any
-  // we find.
-  rv = loginMgr->FindLogins(&count, NS_ConvertASCIItoUTF16(hostname),
-                            EmptyString(),
-                            NS_ConvertASCIItoUTF16(signonURL), &logins);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // There should only be one-login stored for this url, however just in case
-  // there isn't.
-  for (PRUint32 i = 0; i < count; ++i)
-  {
-    // If this fails, just continue, we'll still want to remove the password
-    // from our local cache.
-    loginMgr->RemoveLogin(logins[i]);
-  }
-  NS_FREE_XPCOM_ISUPPORTS_POINTER_ARRAY(count, logins);
-
-  return SetGroupPassword(EmptyCString());
-}
-
 // change order of subfolders (newsgroups)
 // aOrientation = -1 ... aNewsgroupToMove aRefNewsgroup ...
 // aOrientation =  1 ... aRefNewsgroup aNewsgroupToMove ...
 NS_IMETHODIMP nsMsgNewsFolder::MoveFolder(nsIMsgFolder *aNewsgroupToMove, nsIMsgFolder *aRefNewsgroup, PRInt32 aOrientation)
 {
   // if folders are identical do nothing
   if (aNewsgroupToMove == aRefNewsgroup)
     return NS_OK;
@@ -1317,237 +1481,16 @@ NS_IMETHODIMP nsMsgNewsFolder::MoveFolde
   NS_ENSURE_SUCCESS(rv,rv);
 
   rv = nntpServer->WriteNewsrcFile();
   NS_ENSURE_SUCCESS(rv,rv);
 
   return rv;
 }
 
-NS_IMETHODIMP
-nsMsgNewsFolder::GetGroupPasswordWithUI(const nsAString& aPromptMessage,
-                                        const nsAString& aPromptTitle,
-                                        nsIMsgWindow* aMsgWindow,
-                                        nsACString& aGroupPassword)
-{
-  nsresult rv;
-
-  if (mGroupPassword.IsEmpty())
-  {
-    // prompt the user for the password
-    nsCOMPtr<nsIAuthPrompt> dialog;
-    if (aMsgWindow)
-    {
-      nsCOMPtr<nsIDocShell> docShell;
-      rv = aMsgWindow->GetRootDocShell(getter_AddRefs(docShell));
-      if (NS_FAILED(rv)) return rv;
-      dialog = do_GetInterface(docShell, &rv);
-      if (NS_FAILED(rv)) return rv;
-    }
-    else
-    {
-      nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
-      if (wwatch)
-        wwatch->GetNewAuthPrompter(0, getter_AddRefs(dialog));
-      if (!dialog) return NS_ERROR_FAILURE;
-    }
-
-    NS_ASSERTION(dialog,"we didn't get a net prompt");
-    if (dialog)
-    {
-      bool okayValue = true;
-
-      nsCString signonURL;
-      rv = CreateNewsgroupPasswordUrlForSignon(mURI, signonURL);
-      if (NS_FAILED(rv)) return rv;
-
-      PRUnichar *uniGroupPassword = nsnull;
-      if (!mPrevPassword.IsEmpty())
-        uniGroupPassword = ToNewUnicode(NS_ConvertASCIItoUTF16(mPrevPassword));
-
-      rv = dialog->PromptPassword(nsString(aPromptTitle).get(), nsString(aPromptMessage).get(),
-                                  NS_ConvertASCIItoUTF16(signonURL).get(),
-                                  nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY,
-                                  &uniGroupPassword, &okayValue);
-      nsAutoString uniPasswordAdopted;
-      uniPasswordAdopted.Adopt(uniGroupPassword);
-      if (NS_FAILED(rv)) return rv;
-
-      if (!okayValue) // if the user pressed cancel, just return NULL;
-      {
-        aGroupPassword.Truncate();
-        return rv;
-      }
-
-      // we got a password back...so remember it
-      rv = SetGroupPassword(NS_LossyConvertUTF16toASCII(uniPasswordAdopted));
-      if (NS_FAILED(rv)) return rv;
-
-    } // if we got a prompt dialog
-  } // if the password is empty
-
-  return GetGroupPassword(aGroupPassword);
-}
-
-NS_IMETHODIMP
-nsMsgNewsFolder::GetGroupUsernameWithUI(const nsAString& aPromptMessage,
-                                        const nsAString& aPromptTitle,
-                                        nsIMsgWindow* aMsgWindow,
-                                        nsACString& aGroupUsername)
-{
-  nsresult rv;
-
-  if (mGroupUsername.IsEmpty())
-  {
-    // prompt the user for the username
-    nsCOMPtr<nsIAuthPrompt> dialog;
-    if (aMsgWindow)
-    {
-      // prompt the user for the password
-      nsCOMPtr<nsIDocShell> docShell;
-      rv = aMsgWindow->GetRootDocShell(getter_AddRefs(docShell));
-      if (NS_FAILED(rv)) return rv;
-      dialog = do_GetInterface(docShell, &rv);
-    }
-    else
-    {
-      nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
-      if (wwatch)
-        wwatch->GetNewAuthPrompter(0, getter_AddRefs(dialog));
-    }
-    
-    NS_ENSURE_TRUE(dialog, NS_ERROR_FAILURE);
-
-    nsString uniGroupUsername;
-    bool okayValue = true;
-
-    nsCString signonURL;
-    rv = CreateNewsgroupUsernameUrlForSignon(mURI, signonURL);
-    if (NS_FAILED(rv))
-      return rv;
-
-    // This is the hostname without the #username or #password on the end.
-    nsCString hostnameURL;
-    rv = CreateNewsgroupUrlForSignon(mURI, nsnull, hostnameURL);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // Due to the way the new login manager doesn't want to let us save
-    // usernames in the password field, unfortunately the way mailnews has
-    // worked historically, means we need to do this.
-    // So we have to first check manully, then prompt, the save separately if
-    // we want to get a value.
-    nsCOMPtr<nsILoginManager> loginMgr =
-      do_GetService(NS_LOGINMANAGER_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    PRUint32 count = 0;
-    nsILoginInfo **logins = nsnull;
-    rv = loginMgr->FindLogins(&count, NS_ConvertASCIItoUTF16(hostnameURL),
-                              EmptyString(), NS_ConvertASCIItoUTF16(signonURL), &logins);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (count > 0)
-    {
-      nsString username;
-      rv = logins[0]->GetPassword(username);
-
-      NS_FREE_XPCOM_ISUPPORTS_POINTER_ARRAY(count, logins);
-
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      NS_LossyConvertUTF16toASCII result(username);
-
-      // Just use the first as that is all we should have
-      rv = SetGroupUsername(result);
-      if (NS_FAILED(rv))
-        return rv;
-
-      mPrevUsername = aGroupUsername = result;
-      return NS_OK;
-    }
-    NS_FREE_XPCOM_ISUPPORTS_POINTER_ARRAY(count, logins);
-
-
-    // No logins, so time to prompt. Toolkit password manager doesn't let us
-    // use nsIAuthPrompt.prompt in the way we'd like currently, and it doesn't
-    // let us store username-only passwords. So go direct to the dialog and
-    // cut out the middle-man.
-    nsCOMPtr<nsIPromptService> promptSvc =
-      do_GetService(NS_PROMPTSERVICE_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    bool saveUsername = false;
-
-    nsCOMPtr<nsIStringBundleService> bundleService =
-      mozilla::services::GetStringBundleService();
-    NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
-
-    nsCOMPtr<nsIStringBundle> bundle;
-    rv = bundleService->CreateBundle(NEWS_MSGS_URL, getter_AddRefs(bundle));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsString saveUsernameText;
-    rv = bundle->GetStringFromName(NS_LITERAL_STRING("saveUsername").get(),
-                                   getter_Copies(saveUsernameText));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = promptSvc->Prompt(nsnull, nsString(aPromptTitle).get(),
-                           nsString(aPromptMessage).get(),
-                           getter_Copies(uniGroupUsername),
-                           saveUsernameText.get(), &saveUsername,
-                           &okayValue);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!okayValue) // if the user pressed cancel, just return NULL;
-    {
-      aGroupUsername.Truncate();
-      return rv;
-    }
-
-    if (saveUsername)
-    {
-      nsCOMPtr<nsILoginInfo> login =
-        do_CreateInstance("@mozilla.org/login-manager/loginInfo;1", &rv);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      rv = login->SetHostname(NS_ConvertASCIItoUTF16(hostnameURL));
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      rv = login->SetHttpRealm(NS_ConvertASCIItoUTF16(signonURL));
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      rv = login->SetPassword(uniGroupUsername);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      // Apparently there is a small difference between empty strings and null
-      // strings, but setting these to empty works for us.
-      rv = login->SetUsername(EmptyString());
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      rv = login->SetUsernameField(EmptyString());
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      rv = login->SetPasswordField(EmptyString());
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      rv = loginMgr->AddLogin(login);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-
-    // we got a username back, remember it
-    rv = SetGroupUsername(NS_LossyConvertUTF16toASCII(uniGroupUsername));
-    if (NS_FAILED(rv)) return rv;
-
-  } // if the password is empty
-
-  rv = GetGroupUsername(aGroupUsername);
-  mPrevUsername = aGroupUsername;
-  return rv;
-}
-
 nsresult nsMsgNewsFolder::CreateBaseMessageURI(const nsACString& aURI)
 {
   return nsCreateNewsBaseMessageURI(nsCString(aURI).get(), mBaseMessageURI);
 }
 
 NS_IMETHODIMP
 nsMsgNewsFolder::GetNewsrcLine(nsACString& newsrcLine)
 {
--- a/mailnews/news/src/nsNewsFolder.h
+++ b/mailnews/news/src/nsNewsFolder.h
@@ -145,23 +145,24 @@ protected:
   nsMsgKeySet *mReadSet;
 
   nsCOMPtr<nsILocalFile> mNewsrcFilePath;
 
   // used for auth news
   nsCString mGroupUsername;
   nsCString mGroupPassword;
 
-  nsCString mPrevUsername;
-  nsCString mPrevPassword;
-
   // the name of the newsgroup.
   nsCString mRawName;
   PRInt32 mSortOrder;
 
 private:
-  nsresult CreateNewsgroupUsernameUrlForSignon(const nsACString& inUriStr, nsACString& result);
-  nsresult CreateNewsgroupPasswordUrlForSignon(const nsACString& inUriStr, nsACString& result);
-  nsresult CreateNewsgroupUrlForSignon(const nsACString& inUriStr, const char * ref, nsACString& result);
+  /**
+   * Constructs a signon url for use in login manager.
+   *
+   * @param ref    The URI ref (should be null unless working with legacy).
+   * @param result The result of the string
+   */
+  nsresult CreateNewsgroupUrlForSignon(const char *ref, nsAString &result);
   nsCOMPtr <nsIMsgFilterList> mFilterList;
 };
 
 #endif // nsMsgNewsFolder_h__
--- a/mailnews/news/src/nsNntpIncomingServer.cpp
+++ b/mailnews/news/src/nsNntpIncomingServer.cpp
@@ -1407,19 +1407,17 @@ nsNntpIncomingServer::ForgetPassword()
     rv = GetRootFolder(getter_AddRefs(rootFolder));
     NS_ENSURE_SUCCESS(rv,rv);
     if (!rootFolder) return NS_ERROR_FAILURE;
 
     nsCOMPtr <nsIMsgNewsFolder> newsFolder = do_QueryInterface(rootFolder, &rv);
     NS_ENSURE_SUCCESS(rv,rv);
     if (!newsFolder) return NS_ERROR_FAILURE;
 
-    rv = newsFolder->ForgetGroupUsername();
-    NS_ENSURE_SUCCESS(rv,rv);
-    rv = newsFolder->ForgetGroupPassword();
+    rv = newsFolder->ForgetAuthenticationCredentials();
     NS_ENSURE_SUCCESS(rv,rv);
 
     // clear password of all child folders
     nsCOMPtr<nsISimpleEnumerator> subFolders;
 
     rv = rootFolder->GetSubFolders(getter_AddRefs(subFolders));
     NS_ENSURE_SUCCESS(rv,rv);
 
@@ -1429,19 +1427,17 @@ nsNntpIncomingServer::ForgetPassword()
 
     while (NS_SUCCEEDED(subFolders->HasMoreElements(&moreFolders)) &&
            moreFolders) {
         nsCOMPtr<nsISupports> child;
         rv = subFolders->GetNext(getter_AddRefs(child));
         if (NS_SUCCEEDED(rv) && child) {
             newsFolder = do_QueryInterface(child, &rv);
             if (NS_SUCCEEDED(rv) && newsFolder) {
-                rv = newsFolder->ForgetGroupUsername();
-                if (NS_FAILED(rv)) return_rv = rv;
-                rv = newsFolder->ForgetGroupPassword();
+                rv = newsFolder->ForgetAuthenticationCredentials();
                 if (NS_FAILED(rv)) return_rv = rv;
             }
             else {
                 return_rv = NS_ERROR_FAILURE;
             }
         }
     }
 
--- a/mailnews/news/test/unit/test_nntpPassword3.js
+++ b/mailnews/news/test/unit/test_nntpPassword3.js
@@ -26,40 +26,30 @@ function run_test()
   loadLocalMailAccount();
 
   var acctMgr = Cc["@mozilla.org/messenger/account-manager;1"]
                   .getService(Ci.nsIMsgAccountManager);
 
   var incomingServer = acctMgr.createIncomingServer(null, kHostname,
                                                     kProtocol);
 
+  // Force move to new credentials
+  incomingServer.rootFolder.QueryInterface(Ci.nsIMsgNewsFolder)
+                           .migrateLegacyCredentials();
+
   var i;
   var count = {};
 
   // Test - Check there is a password to begin with...
-  var logins = loginMgr.findLogins(count, kServerUrl, null,
-                                   kServerUrl + "/#password");
+  var logins = loginMgr.findLogins(count, kServerUrl, null, kServerUrl);
 
   do_check_eq(count.value, 1);
+  do_check_eq(logins[0].username, kUsername);
   do_check_eq(logins[0].password, kPassword);
 
-  // ...and a username.
-  var logins = loginMgr.findLogins(count, kServerUrl, null,
-                                   kServerUrl + "/#username");
-
-  do_check_eq(count.value, 1);
-  do_check_eq(logins[0].password, kUsername);
-
   // Test - Remove the news password login via the incoming server
   incomingServer.forgetPassword();
 
-  logins = logins = loginMgr.findLogins(count, kServerUrl, null,
-                                        kServerUrl + "/#password");
+  logins = loginMgr.findLogins(count, kServerUrl, null, kServerUrl);
 
   // should be no passwords left...
   do_check_eq(count.value, 0);
-
-  logins = logins = loginMgr.findLogins(count, kServerUrl, null,
-                                        kServerUrl + "/#username");
-
-  // ...and no usernames left either.
-  do_check_eq(count.value, 0);
 }
--- a/suite/locales/en-US/chrome/mailnews/news.properties
+++ b/suite/locales/en-US/chrome/mailnews/news.properties
@@ -41,21 +41,22 @@ htmlNewsErrorTitle=Error!
 # LOCALIZATION NOTE ( htmlNewsError ): In the following item, translate only "Error!" and "newsgroup server responded:"
 htmlNewsError=<H1>Error!</H1>newsgroup server responded: 
 # LOCALIZATION NOTE ( articleExpired ): In the following item, translate only "Perhaps the article has expired"
 articleExpired=<B><P>Perhaps the article has expired</P></B>
 removeExpiredArtLinkText=Click here to remove all expired articles
 cancelDisallowed=This message does not appear to be from you.  You may only cancel your own posts, not those made by others.
 cancelConfirm=Are you sure you want to cancel this message?
 messageCancelled=Message cancelled.
-enterUsername=Please enter a username for news server access:
-enterUsernameTitle=News Server Username Required
-saveUsername=Use Password Manager to remember this value.
-enterPassword=Please enter a password for news server access:
-enterPasswordTitle=News Server Password Required
+enterUserPassTitle=News Server Username and Password Required
+# LOCALIZATION NOTE (enterUserPassServer): %S is the server being accessed
+enterUserPassServer=Please enter a username and password for %S:
+# LOCALIZATION NOTE (enterUserPassGroup): %1$S is a specific newsgroup to set
+# the password for; %2$S is the server from which the newsgroup is accessed
+enterUserPassGroup=Please enter a username and password for %1$S on %2$S:
 okButtonText=Download
 
 noNewMessages=There are no new messages on the server.
 # LOCALIZATION NOTE (newNewsgroupHeaders): %1$S is the number of the current
 # header being downloaded, %2$S is the number of headers to be downloaded, and
 # %3$S is the newsgroup whose headers are being downloaded.
 newNewsgroupHeaders=Downloading %1$S of %2$S headers on %3$S
 # LOCALIZATION NOTE (newNewsgroupFilteringHeaders): %1$S is the name of the MIME