home *** CD-ROM | disk | FTP | other *** search
/ PC World 2007 December / PCWorld_2007-12_cd.bin / audio-video / songbird / Songbird_0.3_windows-i686.exe / xulrunner / components / nsLoginManagerPrompter.js < prev    next >
Text File  |  2007-09-21  |  26KB  |  738 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,
  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.  
  187.         this.log("===== promptAuth called =====");
  188.  
  189.         // If the user submits a login but it fails, we need to remove the
  190.         // notification bar that was displayed. Conveniently, the user will be
  191.         // prompted for authentication again, which brings us here.
  192.         // XXX this isn't right if there are multiple logins on a page (eg,
  193.         // 2 images from different http realms). That seems like an edge case
  194.         // that we're probably not handling right anyway.
  195.         var notifyBox = this._getNotifyBox();
  196.         if (notifyBox)
  197.             this._removeSaveLoginNotification(notifyBox);
  198.  
  199.         var hostname, httpRealm;
  200.         [hostname, httpRealm] = this._GetAuthKey(aChannel, aAuthInfo);
  201.  
  202.  
  203.         // Looks for existing logins to prefill the prompt with.
  204.         var foundLogins = this._pwmgr.findLogins({},
  205.                                         hostname, null, httpRealm);
  206.  
  207.         // XXX Like the original code, we can't deal with multiple
  208.         // account selection. (bug 227632)
  209.         if (foundLogins.length > 0) {
  210.             selectedLogin = foundLogins[0];
  211.             this._SetAuthInfo(aAuthInfo, selectedLogin.username,
  212.                                          selectedLogin.password);
  213.             checkbox.value = true;
  214.         }
  215.  
  216.         var canRememberLogin = this._pwmgr.getLoginSavingEnabled(hostname);
  217.         
  218.         // if checkboxLabel is null, the checkbox won't be shown at all.
  219.         if (canRememberLogin && !notifyBox)
  220.             checkboxLabel = this._getLocalizedString("rememberPassword");
  221.  
  222.         var ok = this._promptService.promptAuth(this._window, aChannel,
  223.                                 aLevel, aAuthInfo, checkboxLabel, checkbox);
  224.  
  225.         // If there's a notification box, use it to allow the user to
  226.         // determine if the login should be saved. If there isn't a
  227.         // notification box, only save the login if the user set the checkbox
  228.         // to do so.
  229.         var rememberLogin = notifyBox ? canRememberLogin : checkbox.value;
  230.  
  231.         if (ok && rememberLogin) {
  232.             var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"]
  233.                                 .createInstance(Ci.nsILoginInfo);
  234.             newLogin.init(hostname, null, httpRealm,
  235.                             aAuthInfo.username, aAuthInfo.password,
  236.                             "", "");
  237.  
  238.             // If we didn't find an existing login, or if the username
  239.             // changed, save as a new login.
  240.             if (!selectedLogin ||
  241.                 aAuthInfo.username != selectedLogin.username) {
  242.  
  243.                 // add as new
  244.                 this.log("New login seen for " + aAuthInfo.username +
  245.                          " @ " + hostname + " (" + httpRealm + ")");
  246.                 if (notifyBox)
  247.                     this._showSaveLoginNotification(notifyBox, newLogin);
  248.                 else
  249.                     this._pwmgr.addLogin(newLogin);
  250.  
  251.             } else if (selectedLogin &&
  252.                        aAuthInfo.password != selectedLogin.password) {
  253.  
  254.                 this.log("Updating password for " + aAuthInfo.username +
  255.                          " @ " + hostname + " (" + httpRealm + ")");
  256.                 // update password
  257.                 this._pwmgr.modifyLogin(foundLogins[0], newLogin);
  258.  
  259.             } else {
  260.                 this.log("Login unchanged, no further action needed.");
  261.                 return ok;
  262.             }
  263.         }
  264.  
  265.         return ok;
  266.     },
  267.  
  268.     asyncPromptAuth : function () {
  269.         return NS_ERROR_NOT_IMPLEMENTED;
  270.     },
  271.  
  272.  
  273.  
  274.  
  275.     /* ---------- nsILoginManagerPrompter prompts ---------- */
  276.  
  277.  
  278.  
  279.  
  280.     /*
  281.      * init
  282.      *
  283.      */
  284.     init : function (aWindow) {
  285.         this._window = aWindow;
  286.         this.log("===== initialized =====");
  287.     },
  288.  
  289.  
  290.     /*
  291.      * promptToSavePassword
  292.      *
  293.      */
  294.     promptToSavePassword : function (aLogin) {
  295.         var notifyBox = this._getNotifyBox();
  296.  
  297.         if (notifyBox)
  298.             this._showSaveLoginNotification(notifyBox, aLogin);
  299.         else
  300.             this._showSaveLoginDialog(aLogin);
  301.     },
  302.  
  303.  
  304.     /*
  305.      * _showSaveLoginNotification
  306.      *
  307.      * Displays a notification bar (rather than a popup), to allow the user to
  308.      * save the specified login. This allows the user to see the results of
  309.      * their login, and only save a login which they know worked.
  310.      *
  311.      */
  312.     _showSaveLoginNotification : function (aNotifyBox, aLogin) {
  313.  
  314.         // Ugh. We can't use the strings from the popup window, because they
  315.         // have the access key marked in the string (eg "Mo&zilla"), along
  316.         // with some weird rules for handling access keys that do not occur
  317.         // in the string, for L10N. See commonDialog.js's setLabelForNode().
  318.         var neverButtonText =
  319.               this._getLocalizedString("notifyBarNeverForSiteButtonText");
  320.         var neverButtonAccessKey =
  321.               this._getLocalizedString("notifyBarNeverForSiteButtonAccessKey");
  322.         var rememberButtonText =
  323.               this._getLocalizedString("notifyBarRememberButtonText");
  324.         var rememberButtonAccessKey =
  325.               this._getLocalizedString("notifyBarRememberButtonAccessKey");
  326.  
  327.         var brandShortName =
  328.               this._brandBundle.GetStringFromName("brandShortName");
  329.         var notificationText  = this._getLocalizedString(
  330.                                         "savePasswordText", [brandShortName]);
  331.  
  332.         // The callbacks in |buttons| have a closure to access the variables
  333.         // in scope here; set one to |this._pwmgr| so we can get back to pwmgr
  334.         // without a getService() call.
  335.         var pwmgr = this._pwmgr;
  336.  
  337.  
  338.         var buttons = [
  339.             // "Remember" button
  340.             {
  341.                 label:     rememberButtonText,
  342.                 accessKey: rememberButtonAccessKey,
  343.                 popup:     null,
  344.                 callback: function(aNotificationBar, aButton) {
  345.                     pwmgr.addLogin(aLogin);
  346.                 }
  347.             },
  348.  
  349.             // "Never for this site" button
  350.             {
  351.                 label:     neverButtonText,
  352.                 accessKey: neverButtonAccessKey,
  353.                 popup:     null,
  354.                 callback: function(aNotificationBar, aButton) {
  355.                     pwmgr.setLoginSavingEnabled(aLogin.hostname, false);
  356.                 }
  357.             }
  358.  
  359.             // "Not now" button not needed, as notification bar isn't modal.
  360.         ];
  361.  
  362.  
  363.         var oldBar = aNotifyBox.getNotificationWithValue("password-save");
  364.         const priority = aNotifyBox.PRIORITY_INFO_MEDIUM;
  365.  
  366.         this.log("Adding new save-password notification bar");
  367.         var newBar = aNotifyBox.appendNotification(
  368.                                 notificationText, "password-save",
  369.                                 null, priority, buttons);
  370.  
  371.         // The page we're going to hasn't loaded yet, so we want to persist
  372.         // across the first location change.
  373.         newBar.ignoreFirstLocationChange = true;
  374.  
  375.         // Sites like Gmail perform a funky redirect dance before you end up
  376.         // at the post-authentication page. I don't see a good way to
  377.         // heuristically determine when to ignore such location changes, so
  378.         // we'll try ignoring location changes based on a time interval.
  379.         var now = Date.now() / 1000;
  380.         newBar.ignoreLocationChangeTimeout = now + 10; // 10 seconds
  381.  
  382.         if (oldBar) {
  383.             this.log("(...and removing old save-password notification bar)");
  384.             aNotifyBox.removeNotification(oldBar);
  385.         }
  386.     },
  387.  
  388.  
  389.     /*
  390.      * _removeSaveLoginNotification
  391.      *
  392.      */
  393.     _removeSaveLoginNotification : function (aNotifyBox) {
  394.  
  395.         var oldBar = aNotifyBox.getNotificationWithValue("password-save");
  396.  
  397.         if (oldBar) {
  398.             this.log("Removing save-password notification bar.");
  399.             aNotifyBox.removeNotification(oldBar);
  400.         }
  401.     },
  402.  
  403.  
  404.     /*
  405.      * _showSaveLoginDialog
  406.      *
  407.      * Called when we detect a new login in a form submission,
  408.      * asks the user what to do.
  409.      *
  410.      */
  411.     _showSaveLoginDialog : function (aLogin) {
  412.         const buttonFlags = Ci.nsIPrompt.BUTTON_POS_1_DEFAULT +
  413.             (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) +
  414.             (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_1) +
  415.             (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_2);
  416.  
  417.         var brandShortName =
  418.                 this._brandBundle.GetStringFromName("brandShortName");
  419.  
  420.         var dialogText         = this._getLocalizedString(
  421.                                         "savePasswordText", [brandShortName]);
  422.         var dialogTitle        = this._getLocalizedString(
  423.                                         "savePasswordTitle");
  424.         var neverButtonText    = this._getLocalizedString(
  425.                                         "neverForSiteButtonText");
  426.         var rememberButtonText = this._getLocalizedString(
  427.                                         "rememberButtonText");
  428.         var notNowButtonText   = this._getLocalizedString(
  429.                                         "notNowButtonText");
  430.  
  431.         this.log("Prompting user to save/ignore login");
  432.         var userChoice = this._promptService.confirmEx(this._window,
  433.                                             dialogTitle, dialogText,
  434.                                             buttonFlags, rememberButtonText,
  435.                                             notNowButtonText, neverButtonText,
  436.                                             null, {});
  437.         //  Returns:
  438.         //   0 - Save the login
  439.         //   1 - Ignore the login this time
  440.         //   2 - Never save logins for this site
  441.         if (userChoice == 2) {
  442.             this.log("Disabling " + aLogin.hostname + " logins by request.");
  443.             this._pwmgr.setLoginSavingEnabled(aLogin.hostname, false);
  444.         } else if (userChoice == 0) {
  445.             this.log("Saving login for " + aLogin.hostname);
  446.             this._pwmgr.addLogin(aLogin.formLogin);
  447.         } else {
  448.             // userChoice == 1 --> just ignore the login.
  449.             this.log("Ignoring login.");
  450.         }
  451.     },
  452.  
  453.  
  454.     /*
  455.      * promptToChangePassword
  456.      *
  457.      * Called when we think we detect a password change for an existing
  458.      * login, when the form being submitted contains multiple password
  459.      * fields.
  460.      *
  461.      */
  462.     promptToChangePassword : function (aOldLogin, aNewLogin) {
  463.         const buttonFlags = Ci.nsIPrompt.STD_YES_NO_BUTTONS;
  464.  
  465.         var dialogText;
  466.         if (aOldLogin.username)
  467.             dialogText  = this._getLocalizedString(
  468.                                     "passwordChangeText",
  469.                                     [aOldLogin.username]);
  470.         else
  471.             dialogText  = this._getLocalizedString(
  472.                                     "passwordChangeTextNoUser");
  473.  
  474.         var dialogTitle = this._getLocalizedString(
  475.                                     "passwordChangeTitle");
  476.  
  477.         // returns 0 for yes, 1 for no.
  478.         var ok = !this._promptService.confirmEx(this._window,
  479.                                 dialogTitle, dialogText, buttonFlags,
  480.                                 null, null, null,
  481.                                 null, {});
  482.         if (ok) {
  483.             this.log("Updating password for user " + aOldLogin.username);
  484.             this._pwmgr.modifyLogin(aOldLogin, aNewLogin);
  485.         }
  486.     },
  487.  
  488.  
  489.     /*
  490.      * promptToChangePasswordWithUsernames
  491.      *
  492.      * Called when we detect a password change in a form submission, but we
  493.      * don't know which existing login (username) it's for. Asks the user
  494.      * to select a username and confirm the password change.
  495.      *
  496.      * Note: The caller doesn't know the username for aNewLogin, so this
  497.      *       function fills in .username and .usernameField with the values
  498.      *       from the login selected by the user.
  499.      * 
  500.      * Note; XPCOM stupidity: |count| is just |logins.length|.
  501.      */
  502.     promptToChangePasswordWithUsernames : function (logins, count, aNewLogin) {
  503.         const buttonFlags = Ci.nsIPrompt.STD_YES_NO_BUTTONS;
  504.  
  505.         var usernames = logins.map(function (l) l.username);
  506.         var dialogText  = this._getLocalizedString("userSelectText");
  507.         var dialogTitle = this._getLocalizedString("passwordChangeTitle");
  508.         var selectedIndex = { value: null };
  509.  
  510.         // If user selects ok, outparam.value is set to the index
  511.         // of the selected username.
  512.         var ok = this._promptService.select(this._window,
  513.                                 dialogTitle, dialogText,
  514.                                 usernames.length, usernames,
  515.                                 selectedIndex);
  516.         if (ok) {
  517.             // Now that we know which login to change the password for,
  518.             // update the missing username info in the aNewLogin.
  519.  
  520.             var selectedLogin = logins[selectedIndex.value];
  521.  
  522.             this.log("Updating password for user " + selectedLogin.username);
  523.  
  524.             aNewLogin.username      = selectedLogin.username;
  525.             aNewLogin.usernameField = selectedLogin.usernameField;
  526.  
  527.             this._pwmgr.modifyLogin(selectedLogin, aNewLogin);
  528.         }
  529.     },
  530.  
  531.  
  532.  
  533.  
  534.     /* ---------- Internal Methods ---------- */
  535.  
  536.  
  537.  
  538.  
  539.     /*
  540.      * _getNotifyBox
  541.      *
  542.      * Returns the notification box to this prompter, or null if there isn't
  543.      * a notification box available.
  544.      */
  545.     _getNotifyBox : function () {
  546.         try {
  547.             // Get topmost window, in case we're in a frame.
  548.             var notifyWindow = this._window.top
  549.  
  550.             // Some sites pop up a temporary login window, when disappears
  551.             // upon submission of credentials. We want to put the notification
  552.             // bar in the opener window if this seems to be happening.
  553.             if (notifyWindow.opener) {
  554.                 var chromeWin = notifyWindow
  555.                                     .QueryInterface(Ci.nsIInterfaceRequestor)
  556.                                     .getInterface(Ci.nsIWebNavigation)
  557.                                     .QueryInterface(Ci.nsIDocShellTreeItem)
  558.                                     .rootTreeItem
  559.                                     .QueryInterface(Ci.nsIInterfaceRequestor)
  560.                                     .getInterface(Ci.nsIDOMWindow);
  561.                 var chromeDoc = chromeWin.document.documentElement;
  562.  
  563.                 // Check to see if the current window was opened with
  564.                 // chrome disabled, and if so use the opener window.
  565.                 if (chromeDoc.getAttribute("chromehidden")) {
  566.                     this.log("Using opener window for notification bar.");
  567.                     notifyWindow = notifyWindow.opener;
  568.                 }
  569.             }
  570.  
  571.  
  572.             // Find the <browser> which contains notifyWindow, by looking
  573.             // through all the open windows and all the <browsers> in each.
  574.             var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
  575.                      getService(Ci.nsIWindowMediator);
  576.             var enumerator = wm.getEnumerator("navigator:browser");
  577.             var tabbrowser = null;
  578.             var foundBrowser = null;
  579.  
  580.             while (!foundBrowser && enumerator.hasMoreElements()) {
  581.                 var win = enumerator.getNext();
  582.                 tabbrowser = win.getBrowser(); 
  583.                 foundBrowser = tabbrowser.getBrowserForDocument(
  584.                                                   notifyWindow.document);
  585.             }
  586.  
  587.             // Return the notificationBox associated with the browser.
  588.             if (foundBrowser)
  589.                 return tabbrowser.getNotificationBox(foundBrowser)
  590.  
  591.         } catch (e) {
  592.             // If any errors happen, just assume no notification box.
  593.             this.log("No notification box available: " + e)
  594.         }
  595.  
  596.         return null;
  597.     },
  598.  
  599.  
  600.     /*
  601.      * _getLocalisedString
  602.      *
  603.      * Can be called as:
  604.      *   _getLocalisedString("key1");
  605.      *   _getLocalizedString("key2", ["arg1"]);
  606.      *   _getLocalizedString("key3", ["arg1", "arg2"]);
  607.      *   (etc)
  608.      *
  609.      * Returns the localized string for the specified key,
  610.      * formatted if required.
  611.      *
  612.      */ 
  613.     _getLocalizedString : function (key, formatArgs) {
  614.         if (formatArgs)
  615.             return this._strBundle.formatStringFromName(
  616.                                         key, formatArgs, formatArgs.length);
  617.         else
  618.             return this._strBundle.GetStringFromName(key);
  619.     },
  620.  
  621.  
  622.     // From /netwerk/base/public/nsNetUtil.h....
  623.     /**
  624.      * This function is a helper to get a protocol's default port if the
  625.      * URI does not specify a port explicitly. Returns -1 if protocol has no
  626.      * concept of ports or if there was an error getting the port.
  627.      */
  628.     _GetRealPort : function (aURI) {
  629.         var port = aURI.port;
  630.  
  631.         if (port != -1)
  632.             return port; // explicitly specified
  633.  
  634.         // Otherwise, we have to get the default port from the protocol handler
  635.         // Need the scheme first
  636.         var scheme = aURI.scheme;
  637.  
  638.         var ioService = Cc["@mozilla.org/network/io-service;1"].
  639.                         getService(Ci.nsIIOService);
  640.  
  641.         var handler = ioService.getProtocolHandler(scheme);
  642.         port = handler.defaultPort;
  643.  
  644.         return port;
  645.     },
  646.  
  647.  
  648.     // From: /embedding/components/windowwatcher/public/nsPromptUtils.h
  649.     // Args: nsIChannel, nsIAuthInformation, boolean, string, int
  650.     _GetAuthHostPort : function (aChannel, aAuthInfo) {
  651.  
  652.         // Have to distinguish proxy auth and host auth here...
  653.         var flags = aAuthInfo.flags;
  654.  
  655.         if (flags & (Ci.nsIAuthInformation.AUTH_PROXY)) {
  656.             // TODO: untested...
  657.             var proxied = aChannel.QueryInterface(Ci.nsIProxiedChannel);
  658.             if (!proxied)
  659.                 throw "proxy auth needs nsIProxiedChannel";
  660.  
  661.             var info = proxied.proxyInfo;
  662.             if (!info)
  663.                 throw "proxy auth needs nsIProxyInfo";
  664.  
  665.             var idnhost = info.host;
  666.             var port    = info.port;
  667.  
  668.             var idnService = Cc["@mozilla.org/network/idn-service;1"].
  669.                              getService(Ci.nsIIDNService);
  670.             host = idnService.convertUTF8toACE(idnhost);
  671.         } else {
  672.             var host = aChannel.URI.host;
  673.             var port = this._GetRealPort(aChannel.URI);
  674.         }
  675.  
  676.         return [host, port];
  677.     },
  678.  
  679.  
  680.     // From: /embedding/components/windowwatcher/public/nsPromptUtils.h
  681.     // Args: nsIChannel, nsIAuthInformation
  682.     _GetAuthKey : function (aChannel, aAuthInfo) {
  683.         var key = "";
  684.         // HTTP does this differently from other protocols
  685.         var http = aChannel.QueryInterface(Ci.nsIHttpChannel);
  686.         if (!http) {
  687.             key = aChannel.URI.prePath;
  688.             this.log("_GetAuthKey: got http channel, key is: " + key);
  689.             return key;
  690.         }
  691.  
  692.         var [host, port] = this._GetAuthHostPort(aChannel, aAuthInfo);
  693.  
  694.         var realm = aAuthInfo.realm;
  695.  
  696.         key += host;
  697.         key += ':';
  698.         key += port;
  699.  
  700.         this.log("_GetAuthKey got host: " + key + " and realm: " + realm);
  701.  
  702.         return [key, realm];
  703.     },
  704.  
  705.  
  706.     // From: /embedding/components/windowwatcher/public/nsPromptUtils.h
  707.     // Args: nsIAuthInformation, string, string
  708.     /**
  709.      * Given a username (possibly in DOMAIN\user form) and password, parses the
  710.      * domain out of the username if necessary and sets domain, username and
  711.      * password on the auth information object.
  712.      */
  713.     _SetAuthInfo : function (aAuthInfo, username, password) {
  714.         var flags = aAuthInfo.flags;
  715.         if (flags & Ci.nsIAuthInformation.NEED_DOMAIN) {
  716.             // Domain is separated from username by a backslash
  717.             var idx = username.indexOf("\\");
  718.             if (idx == -1) {
  719.                 aAuthInfo.username = username;
  720.             } else {
  721.                 aAuthInfo.domain   =  username.substring(0, idx);
  722.                 aAuthInfo.username =  username.substring(idx+1);
  723.             }
  724.         } else {
  725.             aAuthInfo.username = username;
  726.         }
  727.         aAuthInfo.password = password;
  728.     }
  729.  
  730. }; // end of LoginManagerPrompter implementation
  731.  
  732. var component = [LoginManagerPromptFactory, LoginManagerPrompter];
  733. function NSGetModule(compMgr, fileSpec) {
  734.     return XPCOMUtils.generateModule(component);
  735. }
  736.  
  737.  
  738.