home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 February / PCWorld_2008-02_cd.bin / temacd / songbird / Songbird_0.4_windows-i686.exe / xulrunner / components / nsLoginManagerPrompter.js < prev    next >
Text File  |  2007-12-07  |  27KB  |  739 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is mozilla.org code.
  15.  *
  16.  * The Initial Developer of the Original Code is Mozilla Corporation.
  17.  * Portions created by the Initial Developer are Copyright (C) 2007
  18.  * the Initial Developer. All Rights Reserved.
  19.  *
  20.  * Contributor(s):
  21.  *  Justin Dolske <dolske@mozilla.com> (original author)
  22.  *
  23.  * Alternatively, the contents of this file may be used under the terms of
  24.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  25.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  26.  * in which case the provisions of the GPL or the LGPL are applicable instead
  27.  * of those above. If you wish to allow use of your version of this file only
  28.  * under the terms of either the GPL or the LGPL, and not to allow others to
  29.  * use your version of this file under the terms of the MPL, indicate your
  30.  * decision by deleting the provisions above and replace them with the notice
  31.  * and other provisions required by the GPL or the LGPL. If you do not delete
  32.  * the provisions above, a recipient may use your version of this file under
  33.  * the terms of any one of the MPL, the GPL or the LGPL.
  34.  *
  35.  * ***** END LICENSE BLOCK ***** */
  36.  
  37.  
  38. const Cc = Components.classes;
  39. const Ci = Components.interfaces;
  40.  
  41. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  42.  
  43. /*
  44.  * LoginManagerPromptFactory
  45.  *
  46.  * Implements nsIPromptFactory
  47.  *
  48.  * Invoked by NS_NewAuthPrompter2()
  49.  * [embedding/components/windowwatcher/src/nsPrompt.cpp]
  50.  */
  51. function LoginManagerPromptFactory() {}
  52.  
  53. LoginManagerPromptFactory.prototype = {
  54.  
  55.     classDescription : "LoginManagerPromptFactory",
  56.     contractID : "@mozilla.org/passwordmanager/authpromptfactory;1",
  57.     classID : Components.ID("{749e62f4-60ae-4569-a8a2-de78b649660e}"),
  58.     QueryInterface : XPCOMUtils.generateQI([Ci.nsIPromptFactory]),
  59.  
  60.     getPrompt : function (aWindow, aIID) {
  61.         var prompt = new LoginManagerPrompter().QueryInterface(aIID);
  62.         prompt.init(aWindow);
  63.         return prompt;
  64.     }
  65. }; // end of LoginManagerPromptFactory implementation
  66.  
  67.  
  68.  
  69.  
  70. /* ==================== LoginManagerPrompter ==================== */
  71.  
  72.  
  73.  
  74.  
  75. /*
  76.  * LoginManagerPrompter
  77.  *
  78.  * Implements nsIAuthPrompt2 and nsILoginManagerPrompter.
  79.  * nsIAuthPrompt2 usage is invoked by a channel for protocol-based
  80.  * authentication (eg HTTP Authenticate, FTP login). nsILoginManagerPrompter
  81.  * is invoked by Login Manager for saving/changing a login.
  82.  */
  83. function LoginManagerPrompter() {}
  84.  
  85. LoginManagerPrompter.prototype = {
  86.  
  87.     classDescription : "LoginManagerPrompter",
  88.     contractID : "@mozilla.org/login-manager/prompter;1",
  89.     classID : Components.ID("{8aa66d77-1bbb-45a6-991e-b8f47751c291}"),
  90.     QueryInterface : XPCOMUtils.generateQI(
  91.                         [Ci.nsIAuthPrompt2, Ci.nsILoginManagerPrompter]),
  92.  
  93.     _window        : null,
  94.     _debug         : false, // mirrors signon.debug
  95.  
  96.     __pwmgr : null, // Password Manager service
  97.     get _pwmgr() {
  98.         if (!this.__pwmgr)
  99.             this.__pwmgr = Cc["@mozilla.org/login-manager;1"].
  100.                            getService(Ci.nsILoginManager);
  101.         return this.__pwmgr;
  102.     },
  103.  
  104.     __logService : null, // Console logging service, used for debugging.
  105.     get _logService() {
  106.         if (!this.__logService)
  107.             this.__logService = Cc["@mozilla.org/consoleservice;1"].
  108.                                 getService(Ci.nsIConsoleService);
  109.         return this.__logService;
  110.     },
  111.  
  112.     __promptService : null, // Prompt service for user interaction
  113.     get _promptService() {
  114.         if (!this.__promptService)
  115.             this.__promptService =
  116.                 Cc["@mozilla.org/embedcomp/prompt-service;1"].
  117.                 getService(Ci.nsIPromptService2);
  118.         return this.__promptService;
  119.     },
  120.  
  121.  
  122.     __strBundle : null, // String bundle for L10N
  123.     get _strBundle() {
  124.         if (!this.__strBundle) {
  125.             var bunService = Cc["@mozilla.org/intl/stringbundle;1"].
  126.                              getService(Ci.nsIStringBundleService);
  127.             this.__strBundle = bunService.createBundle(
  128.                         "chrome://passwordmgr/locale/passwordmgr.properties");
  129.             if (!this.__strBundle)
  130.                 throw "String bundle for Login Manager not present!";
  131.         }
  132.  
  133.         return this.__strBundle;
  134.     },
  135.  
  136.  
  137.     __brandBundle : null, // String bundle for L10N
  138.     get _brandBundle() {
  139.         if (!this.__brandBundle) {
  140.             var bunService = Cc["@mozilla.org/intl/stringbundle;1"].
  141.                              getService(Ci.nsIStringBundleService);
  142.             this.__brandBundle = bunService.createBundle(
  143.                         "chrome://branding/locale/brand.properties");
  144.             if (!this.__brandBundle)
  145.                 throw "Branding string bundle not present!";
  146.         }
  147.  
  148.         return this.__brandBundle;
  149.     },
  150.  
  151.  
  152.     /*
  153.      * log
  154.      *
  155.      * Internal function for logging debug messages to the Error Console window.
  156.      */
  157.     log : function (message) {
  158.         if (!this._debug)
  159.             return;
  160.  
  161.         dump("Pwmgr Prompter: " + message + "\n");
  162.         this._logService.logStringMessage("Pwmgr Prompter: " + message);
  163.     },
  164.  
  165.  
  166.  
  167.  
  168.     /* ---------- nsIAuthPrompt2 prompts ---------- */
  169.  
  170.  
  171.  
  172.  
  173.     /*
  174.      * promptAuth
  175.      *
  176.      * Implementation of nsIAuthPrompt2.
  177.      *
  178.      * nsIChannel aChannel
  179.      * int        aLevel
  180.      * nsIAuthInformation aAuthInfo
  181.      */
  182.     promptAuth : function (aChannel, aLevel, aAuthInfo) {
  183.         var selectedLogin = null;
  184.         var checkbox = { value : false };
  185.         var checkboxLabel = null;
  186.         var epicfail = false;
  187.  
  188.         try {
  189.  
  190.             this.log("===== promptAuth called =====");
  191.  
  192.             // If the user submits a login but it fails, we need to remove the
  193.             // notification bar that was displayed. Conveniently, the user will
  194.             // be prompted for authentication again, which brings us here.
  195.             var notifyBox = this._getNotifyBox();
  196.             if (notifyBox)
  197.                 this._removeSaveLoginNotification(notifyBox);
  198.  
  199.             var [hostname, httpRealm] = this._getAuthTarget(aChannel, aAuthInfo);
  200.  
  201.  
  202.             // Looks for existing logins to prefill the prompt with.
  203.             var foundLogins = this._pwmgr.findLogins({},
  204.                                         hostname, null, httpRealm);
  205.  
  206.             // XXX Can't select from multiple accounts yet. (bug 227632)
  207.             if (foundLogins.length > 0) {
  208.                 selectedLogin = foundLogins[0];
  209.                 this._SetAuthInfo(aAuthInfo, selectedLogin.username,
  210.                                              selectedLogin.password);
  211.                 checkbox.value = true;
  212.             }
  213.  
  214.             var canRememberLogin = this._pwmgr.getLoginSavingEnabled(hostname);
  215.         
  216.             // if checkboxLabel is null, the checkbox won't be shown at all.
  217.             if (canRememberLogin && !notifyBox)
  218.                 checkboxLabel = this._getLocalizedString("rememberPassword");
  219.         } catch (e) {
  220.             // Ignore any errors and display the prompt anyway.
  221.             epicfail = true;
  222.             Components.utils.reportError("LoginManagerPrompter: " +
  223.                 "Epic fail in promptAuth: " + e + "\n");
  224.         }
  225.  
  226.         var ok = this._promptService.promptAuth(this._window, aChannel,
  227.                                 aLevel, aAuthInfo, checkboxLabel, checkbox);
  228.         if (epicfail)
  229.             return ok;
  230.  
  231.         try {
  232.             // If there's a notification box, use it to allow the user to
  233.             // determine if the login should be saved. If there isn't a
  234.             // notification box, only save the login if the user set the
  235.             // checkbox to do so.
  236.             var rememberLogin = notifyBox ? canRememberLogin : checkbox.value;
  237.  
  238.             if (ok && rememberLogin) {
  239.                 var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
  240.                                createInstance(Ci.nsILoginInfo);
  241.                 newLogin.init(hostname, null, httpRealm,
  242.                               aAuthInfo.username, aAuthInfo.password,
  243.                               "", "");
  244.  
  245.                 // If we didn't find an existing login, or if the username
  246.                 // changed, save as a new login.
  247.                 if (!selectedLogin ||
  248.                     aAuthInfo.username != selectedLogin.username) {
  249.  
  250.                     // add as new
  251.                     this.log("New login seen for " + aAuthInfo.username +
  252.                              " @ " + hostname + " (" + httpRealm + ")");
  253.                     if (notifyBox)
  254.                         this._showSaveLoginNotification(notifyBox, newLogin);
  255.                     else
  256.                         this._pwmgr.addLogin(newLogin);
  257.  
  258.                 } else if (selectedLogin &&
  259.                            aAuthInfo.password != selectedLogin.password) {
  260.  
  261.                     this.log("Updating password for " + aAuthInfo.username +
  262.                              " @ " + hostname + " (" + httpRealm + ")");
  263.                     // update password
  264.                     this._pwmgr.modifyLogin(foundLogins[0], newLogin);
  265.  
  266.                 } else {
  267.                     this.log("Login unchanged, no further action needed.");
  268.                     return ok;
  269.                 }
  270.             }
  271.         } catch (e) {
  272.             Components.utils.reportError("LoginManagerPrompter: " +
  273.                 "Fail2 in promptAuth: " + e + "\n");
  274.         }
  275.  
  276.         return ok;
  277.     },
  278.  
  279.     asyncPromptAuth : function () {
  280.         return NS_ERROR_NOT_IMPLEMENTED;
  281.     },
  282.  
  283.  
  284.  
  285.  
  286.     /* ---------- nsILoginManagerPrompter prompts ---------- */
  287.  
  288.  
  289.  
  290.  
  291.     /*
  292.      * init
  293.      *
  294.      */
  295.     init : function (aWindow) {
  296.         this._window = aWindow;
  297.  
  298.         var prefBranch = Cc["@mozilla.org/preferences-service;1"].
  299.                          getService(Ci.nsIPrefService).getBranch("signon.");
  300.         this._debug = prefBranch.getBoolPref("debug");
  301.         this.log("===== initialized =====");
  302.     },
  303.  
  304.  
  305.     /*
  306.      * promptToSavePassword
  307.      *
  308.      */
  309.     promptToSavePassword : function (aLogin) {
  310.         var notifyBox = this._getNotifyBox();
  311.  
  312.         if (notifyBox)
  313.             this._showSaveLoginNotification(notifyBox, aLogin);
  314.         else
  315.             this._showSaveLoginDialog(aLogin);
  316.     },
  317.  
  318.  
  319.     /*
  320.      * _showSaveLoginNotification
  321.      *
  322.      * Displays a notification bar (rather than a popup), to allow the user to
  323.      * save the specified login. This allows the user to see the results of
  324.      * their login, and only save a login which they know worked.
  325.      *
  326.      */
  327.     _showSaveLoginNotification : function (aNotifyBox, aLogin) {
  328.  
  329.         // Ugh. We can't use the strings from the popup window, because they
  330.         // have the access key marked in the string (eg "Mo&zilla"), along
  331.         // with some weird rules for handling access keys that do not occur
  332.         // in the string, for L10N. See commonDialog.js's setLabelForNode().
  333.         var neverButtonText =
  334.               this._getLocalizedString("notifyBarNeverForSiteButtonText");
  335.         var neverButtonAccessKey =
  336.               this._getLocalizedString("notifyBarNeverForSiteButtonAccessKey");
  337.         var rememberButtonText =
  338.               this._getLocalizedString("notifyBarRememberButtonText");
  339.         var rememberButtonAccessKey =
  340.               this._getLocalizedString("notifyBarRememberButtonAccessKey");
  341.  
  342.         var brandShortName =
  343.               this._brandBundle.GetStringFromName("brandShortName");
  344.         var notificationText  = this._getLocalizedString(
  345.                                         "savePasswordText", [brandShortName]);
  346.  
  347.         // The callbacks in |buttons| have a closure to access the variables
  348.         // in scope here; set one to |this._pwmgr| so we can get back to pwmgr
  349.         // without a getService() call.
  350.         var pwmgr = this._pwmgr;
  351.  
  352.  
  353.         var buttons = [
  354.             // "Remember" button
  355.             {
  356.                 label:     rememberButtonText,
  357.                 accessKey: rememberButtonAccessKey,
  358.                 popup:     null,
  359.                 callback: function(aNotificationBar, aButton) {
  360.                     pwmgr.addLogin(aLogin);
  361.                 }
  362.             },
  363.  
  364.             // "Never for this site" button
  365.             {
  366.                 label:     neverButtonText,
  367.                 accessKey: neverButtonAccessKey,
  368.                 popup:     null,
  369.                 callback: function(aNotificationBar, aButton) {
  370.                     pwmgr.setLoginSavingEnabled(aLogin.hostname, false);
  371.                 }
  372.             }
  373.  
  374.             // "Not now" button not needed, as notification bar isn't modal.
  375.         ];
  376.  
  377.  
  378.         var oldBar = aNotifyBox.getNotificationWithValue("password-save");
  379.         const priority = aNotifyBox.PRIORITY_INFO_MEDIUM;
  380.  
  381.         this.log("Adding new save-password notification bar");
  382.         var newBar = aNotifyBox.appendNotification(
  383.                                 notificationText, "password-save",
  384.                                 null, priority, buttons);
  385.  
  386.         // The page we're going to hasn't loaded yet, so we want to persist
  387.         // across the first location change.
  388.         newBar.ignoreFirstLocationChange = true;
  389.  
  390.         // Sites like Gmail perform a funky redirect dance before you end up
  391.         // at the post-authentication page. I don't see a good way to
  392.         // heuristically determine when to ignore such location changes, so
  393.         // we'll try ignoring location changes based on a time interval.
  394.         var now = Date.now() / 1000;
  395.         newBar.ignoreLocationChangeTimeout = now + 10; // 10 seconds
  396.  
  397.         if (oldBar) {
  398.             this.log("(...and removing old save-password notification bar)");
  399.             aNotifyBox.removeNotification(oldBar);
  400.         }
  401.     },
  402.  
  403.  
  404.     /*
  405.      * _removeSaveLoginNotification
  406.      *
  407.      */
  408.     _removeSaveLoginNotification : function (aNotifyBox) {
  409.  
  410.         var oldBar = aNotifyBox.getNotificationWithValue("password-save");
  411.  
  412.         if (oldBar) {
  413.             this.log("Removing save-password notification bar.");
  414.             aNotifyBox.removeNotification(oldBar);
  415.         }
  416.     },
  417.  
  418.  
  419.     /*
  420.      * _showSaveLoginDialog
  421.      *
  422.      * Called when we detect a new login in a form submission,
  423.      * asks the user what to do.
  424.      *
  425.      */
  426.     _showSaveLoginDialog : function (aLogin) {
  427.         const buttonFlags = Ci.nsIPrompt.BUTTON_POS_1_DEFAULT +
  428.             (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) +
  429.             (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_1) +
  430.             (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_2);
  431.  
  432.         var brandShortName =
  433.                 this._brandBundle.GetStringFromName("brandShortName");
  434.  
  435.         var dialogText         = this._getLocalizedString(
  436.                                         "savePasswordText", [brandShortName]);
  437.         var dialogTitle        = this._getLocalizedString(
  438.                                         "savePasswordTitle");
  439.         var neverButtonText    = this._getLocalizedString(
  440.                                         "neverForSiteButtonText");
  441.         var rememberButtonText = this._getLocalizedString(
  442.                                         "rememberButtonText");
  443.         var notNowButtonText   = this._getLocalizedString(
  444.                                         "notNowButtonText");
  445.  
  446.         this.log("Prompting user to save/ignore login");
  447.         var userChoice = this._promptService.confirmEx(this._window,
  448.                                             dialogTitle, dialogText,
  449.                                             buttonFlags, rememberButtonText,
  450.                                             notNowButtonText, neverButtonText,
  451.                                             null, {});
  452.         //  Returns:
  453.         //   0 - Save the login
  454.         //   1 - Ignore the login this time
  455.         //   2 - Never save logins for this site
  456.         if (userChoice == 2) {
  457.             this.log("Disabling " + aLogin.hostname + " logins by request.");
  458.             this._pwmgr.setLoginSavingEnabled(aLogin.hostname, false);
  459.         } else if (userChoice == 0) {
  460.             this.log("Saving login for " + aLogin.hostname);
  461.             this._pwmgr.addLogin(aLogin);
  462.         } else {
  463.             // userChoice == 1 --> just ignore the login.
  464.             this.log("Ignoring login.");
  465.         }
  466.     },
  467.  
  468.  
  469.     /*
  470.      * promptToChangePassword
  471.      *
  472.      * Called when we think we detect a password change for an existing
  473.      * login, when the form being submitted contains multiple password
  474.      * fields.
  475.      *
  476.      */
  477.     promptToChangePassword : function (aOldLogin, aNewLogin) {
  478.         const buttonFlags = Ci.nsIPrompt.STD_YES_NO_BUTTONS;
  479.  
  480.         var dialogText;
  481.         if (aOldLogin.username)
  482.             dialogText  = this._getLocalizedString(
  483.                                     "passwordChangeText",
  484.                                     [aOldLogin.username]);
  485.         else
  486.             dialogText  = this._getLocalizedString(
  487.                                     "passwordChangeTextNoUser");
  488.  
  489.         var dialogTitle = this._getLocalizedString(
  490.                                     "passwordChangeTitle");
  491.  
  492.         // returns 0 for yes, 1 for no.
  493.         var ok = !this._promptService.confirmEx(this._window,
  494.                                 dialogTitle, dialogText, buttonFlags,
  495.                                 null, null, null,
  496.                                 null, {});
  497.         if (ok) {
  498.             this.log("Updating password for user " + aOldLogin.username);
  499.             this._pwmgr.modifyLogin(aOldLogin, aNewLogin);
  500.         }
  501.     },
  502.  
  503.  
  504.     /*
  505.      * promptToChangePasswordWithUsernames
  506.      *
  507.      * Called when we detect a password change in a form submission, but we
  508.      * don't know which existing login (username) it's for. Asks the user
  509.      * to select a username and confirm the password change.
  510.      *
  511.      * Note: The caller doesn't know the username for aNewLogin, so this
  512.      *       function fills in .username and .usernameField with the values
  513.      *       from the login selected by the user.
  514.      * 
  515.      * Note; XPCOM stupidity: |count| is just |logins.length|.
  516.      */
  517.     promptToChangePasswordWithUsernames : function (logins, count, aNewLogin) {
  518.         const buttonFlags = Ci.nsIPrompt.STD_YES_NO_BUTTONS;
  519.  
  520.         var usernames = logins.map(function (l) l.username);
  521.         var dialogText  = this._getLocalizedString("userSelectText");
  522.         var dialogTitle = this._getLocalizedString("passwordChangeTitle");
  523.         var selectedIndex = { value: null };
  524.  
  525.         // If user selects ok, outparam.value is set to the index
  526.         // of the selected username.
  527.         var ok = this._promptService.select(this._window,
  528.                                 dialogTitle, dialogText,
  529.                                 usernames.length, usernames,
  530.                                 selectedIndex);
  531.         if (ok) {
  532.             // Now that we know which login to change the password for,
  533.             // update the missing username info in the aNewLogin.
  534.  
  535.             var selectedLogin = logins[selectedIndex.value];
  536.  
  537.             this.log("Updating password for user " + selectedLogin.username);
  538.  
  539.             aNewLogin.username      = selectedLogin.username;
  540.             aNewLogin.usernameField = selectedLogin.usernameField;
  541.  
  542.             this._pwmgr.modifyLogin(selectedLogin, aNewLogin);
  543.         }
  544.     },
  545.  
  546.  
  547.  
  548.  
  549.     /* ---------- Internal Methods ---------- */
  550.  
  551.  
  552.  
  553.  
  554.     /*
  555.      * _getNotifyBox
  556.      *
  557.      * Returns the notification box to this prompter, or null if there isn't
  558.      * a notification box available.
  559.      */
  560.     _getNotifyBox : function () {
  561.         try {
  562.             // Get topmost window, in case we're in a frame.
  563.             var notifyWindow = this._window.top
  564.  
  565.             // Some sites pop up a temporary login window, when disappears
  566.             // upon submission of credentials. We want to put the notification
  567.             // bar in the opener window if this seems to be happening.
  568.             if (notifyWindow.opener) {
  569.                 var chromeWin = notifyWindow
  570.                                     .QueryInterface(Ci.nsIInterfaceRequestor)
  571.                                     .getInterface(Ci.nsIWebNavigation)
  572.                                     .QueryInterface(Ci.nsIDocShellTreeItem)
  573.                                     .rootTreeItem
  574.                                     .QueryInterface(Ci.nsIInterfaceRequestor)
  575.                                     .getInterface(Ci.nsIDOMWindow);
  576.                 var chromeDoc = chromeWin.document.documentElement;
  577.  
  578.                 // Check to see if the current window was opened with
  579.                 // chrome disabled, and if so use the opener window.
  580.                 if (chromeDoc.getAttribute("chromehidden")) {
  581.                     this.log("Using opener window for notification bar.");
  582.                     notifyWindow = notifyWindow.opener;
  583.                 }
  584.             }
  585.  
  586.  
  587.             // Find the <browser> which contains notifyWindow, by looking
  588.             // through all the open windows and all the <browsers> in each.
  589.             var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
  590.                      getService(Ci.nsIWindowMediator);
  591.             var enumerator = wm.getEnumerator("navigator:browser");
  592.             var tabbrowser = null;
  593.             var foundBrowser = null;
  594.  
  595.             while (!foundBrowser && enumerator.hasMoreElements()) {
  596.                 var win = enumerator.getNext();
  597.                 tabbrowser = win.getBrowser(); 
  598.                 foundBrowser = tabbrowser.getBrowserForDocument(
  599.                                                   notifyWindow.document);
  600.             }
  601.  
  602.             // Return the notificationBox associated with the browser.
  603.             if (foundBrowser)
  604.                 return tabbrowser.getNotificationBox(foundBrowser)
  605.  
  606.         } catch (e) {
  607.             // If any errors happen, just assume no notification box.
  608.             this.log("No notification box available: " + e)
  609.         }
  610.  
  611.         return null;
  612.     },
  613.  
  614.  
  615.     /*
  616.      * _getLocalisedString
  617.      *
  618.      * Can be called as:
  619.      *   _getLocalisedString("key1");
  620.      *   _getLocalizedString("key2", ["arg1"]);
  621.      *   _getLocalizedString("key3", ["arg1", "arg2"]);
  622.      *   (etc)
  623.      *
  624.      * Returns the localized string for the specified key,
  625.      * formatted if required.
  626.      *
  627.      */ 
  628.     _getLocalizedString : function (key, formatArgs) {
  629.         if (formatArgs)
  630.             return this._strBundle.formatStringFromName(
  631.                                         key, formatArgs, formatArgs.length);
  632.         else
  633.             return this._strBundle.GetStringFromName(key);
  634.     },
  635.  
  636.  
  637.     /*
  638.      * _getFormattedHostname
  639.      *
  640.      * Returns the hostname to use in a nsILoginInfo object (for example,
  641.      * "http://example.com").
  642.      */
  643.     _getFormattedHostname : function (aURI) {
  644.         var scheme = aURI.scheme;
  645.  
  646.         var hostname = scheme + "://" + aURI.host;
  647.  
  648.         // If the URI explicitly specified a port, only include it when
  649.         // it's not the default. (We never want "http://foo.com:80")
  650.         port = aURI.port;
  651.         if (port != -1) {
  652.             var ioService = Cc["@mozilla.org/network/io-service;1"].
  653.                             getService(Ci.nsIIOService);
  654.             var handler = ioService.getProtocolHandler(scheme);
  655.             if (port != handler.defaultPort)
  656.                 hostname += ":" + port;
  657.         }
  658.  
  659.         return hostname;
  660.     },
  661.  
  662.  
  663.     /*
  664.      * _getAuthTarget
  665.      *
  666.      * Returns the hostname and realm for which authentication is being
  667.      * requested, in the format expected to be used with nsILoginInfo.
  668.      */
  669.     _getAuthTarget : function (aChannel, aAuthInfo) {
  670.         var hostname, realm;
  671.  
  672.         // If our proxy is demanding authentication, don't use the
  673.         // channel's actual destination.
  674.         if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) {
  675.             this.log("getAuthTarget is for proxy auth");
  676.             if (!(aChannel instanceof Ci.nsIProxiedChannel))
  677.                 throw "proxy auth needs nsIProxiedChannel";
  678.  
  679.             var info = aChannel.proxyInfo;
  680.             if (!info)
  681.                 throw "proxy auth needs nsIProxyInfo";
  682.  
  683.             // Proxies don't have a scheme, but we'll use "moz-proxy://"
  684.             // so that it's more obvious what the login is for.
  685.             var idnService = Cc["@mozilla.org/network/idn-service;1"].
  686.                              getService(Ci.nsIIDNService);
  687.             hostname = "moz-proxy://" +
  688.                         idnService.convertUTF8toACE(info.host) +
  689.                         ":" + info.port;
  690.             realm = aAuthInfo.realm;
  691.             if (!realm)
  692.                 realm = hostname;
  693.  
  694.             return [hostname, realm];
  695.         }
  696.  
  697.         hostname = this._getFormattedHostname(aChannel.URI);
  698.  
  699.         // If a HTTP WWW-Authenticate header specified a realm, that value
  700.         // will be available here. If it wasn't set or wasn't HTTP, we'll use
  701.         // the formatted hostname instead.
  702.         realm = aAuthInfo.realm;
  703.         if (!realm)
  704.             realm = hostname;
  705.  
  706.         return [hostname, realm];
  707.     },
  708.  
  709.     /**
  710.      * Given a username (possibly in DOMAIN\user form) and password, parses the
  711.      * domain out of the username if necessary and sets domain, username and
  712.      * password on the auth information object.
  713.      */
  714.     _SetAuthInfo : function (aAuthInfo, username, password) {
  715.         var flags = aAuthInfo.flags;
  716.         if (flags & Ci.nsIAuthInformation.NEED_DOMAIN) {
  717.             // Domain is separated from username by a backslash
  718.             var idx = username.indexOf("\\");
  719.             if (idx == -1) {
  720.                 aAuthInfo.username = username;
  721.             } else {
  722.                 aAuthInfo.domain   =  username.substring(0, idx);
  723.                 aAuthInfo.username =  username.substring(idx+1);
  724.             }
  725.         } else {
  726.             aAuthInfo.username = username;
  727.         }
  728.         aAuthInfo.password = password;
  729.     }
  730.  
  731. }; // end of LoginManagerPrompter implementation
  732.  
  733. var component = [LoginManagerPromptFactory, LoginManagerPrompter];
  734. function NSGetModule(compMgr, fileSpec) {
  735.     return XPCOMUtils.generateModule(component);
  736. }
  737.  
  738.  
  739.