diff options
Diffstat (limited to 'toolkit/components/passwordmgr/LoginImport.jsm')
-rw-r--r-- | toolkit/components/passwordmgr/LoginImport.jsm | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/toolkit/components/passwordmgr/LoginImport.jsm b/toolkit/components/passwordmgr/LoginImport.jsm new file mode 100644 index 000000000..a1d5c988a --- /dev/null +++ b/toolkit/components/passwordmgr/LoginImport.jsm @@ -0,0 +1,173 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Provides an object that has a method to import login-related data from the + * previous SQLite storage format. + */ + +"use strict"; + +this.EXPORTED_SYMBOLS = [ + "LoginImport", +]; + +// Globals + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; +const Cr = Components.results; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "OS", + "resource://gre/modules/osfile.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Sqlite", + "resource://gre/modules/Sqlite.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", + "resource://gre/modules/NetUtil.jsm"); + +// LoginImport + +/** + * Provides an object that has a method to import login-related data from the + * previous SQLite storage format. + * + * @param aStore + * LoginStore object where imported data will be added. + * @param aPath + * String containing the file path of the SQLite login database. + */ +this.LoginImport = function (aStore, aPath) { + this.store = aStore; + this.path = aPath; +}; + +this.LoginImport.prototype = { + /** + * LoginStore object where imported data will be added. + */ + store: null, + + /** + * String containing the file path of the SQLite login database. + */ + path: null, + + /** + * Imports login-related data from the previous SQLite storage format. + */ + import: Task.async(function* () { + // We currently migrate data directly from the database to the JSON store at + // first run, then we set a preference to prevent repeating the import. + // Thus, merging with existing data is not a use case we support. This + // restriction might be removed to support re-importing passwords set by an + // old version by flipping the import preference and restarting. + if (this.store.data.logins.length > 0 || + this.store.data.disabledHosts.length > 0) { + throw new Error("Unable to import saved passwords because some data " + + "has already been imported or saved."); + } + + // When a timestamp is not specified, we will use the same reference time. + let referenceTimeMs = Date.now(); + + let connection = yield Sqlite.openConnection({ path: this.path }); + try { + let schemaVersion = yield connection.getSchemaVersion(); + + // We support importing database schema versions from 3 onwards. + // Version 3 was implemented in bug 316084 (Firefox 3.6, March 2009). + // Version 4 was implemented in bug 465636 (Firefox 4, March 2010). + // Version 5 was implemented in bug 718817 (Firefox 13, February 2012). + if (schemaVersion < 3) { + throw new Error("Unable to import saved passwords because " + + "the existing profile is too old."); + } + + let rows = yield connection.execute("SELECT * FROM moz_logins"); + for (let row of rows) { + try { + let hostname = row.getResultByName("hostname"); + let httpRealm = row.getResultByName("httpRealm"); + let formSubmitURL = row.getResultByName("formSubmitURL"); + let usernameField = row.getResultByName("usernameField"); + let passwordField = row.getResultByName("passwordField"); + let encryptedUsername = row.getResultByName("encryptedUsername"); + let encryptedPassword = row.getResultByName("encryptedPassword"); + + // The "guid" field was introduced in schema version 2, and the + // "enctype" field was introduced in schema version 3. We don't + // support upgrading from older versions of the database. + let guid = row.getResultByName("guid"); + let encType = row.getResultByName("encType"); + + // The time and count fields were introduced in schema version 4. + let timeCreated = null; + let timeLastUsed = null; + let timePasswordChanged = null; + let timesUsed = null; + try { + timeCreated = row.getResultByName("timeCreated"); + timeLastUsed = row.getResultByName("timeLastUsed"); + timePasswordChanged = row.getResultByName("timePasswordChanged"); + timesUsed = row.getResultByName("timesUsed"); + } catch (ex) { } + + // These columns may be null either because they were not present in + // the database or because the record was created on a new schema + // version by an old application version. + if (!timeCreated) { + timeCreated = referenceTimeMs; + } + if (!timeLastUsed) { + timeLastUsed = referenceTimeMs; + } + if (!timePasswordChanged) { + timePasswordChanged = referenceTimeMs; + } + if (!timesUsed) { + timesUsed = 1; + } + + this.store.data.logins.push({ + id: this.store.data.nextId++, + hostname: hostname, + httpRealm: httpRealm, + formSubmitURL: formSubmitURL, + usernameField: usernameField, + passwordField: passwordField, + encryptedUsername: encryptedUsername, + encryptedPassword: encryptedPassword, + guid: guid, + encType: encType, + timeCreated: timeCreated, + timeLastUsed: timeLastUsed, + timePasswordChanged: timePasswordChanged, + timesUsed: timesUsed, + }); + } catch (ex) { + Cu.reportError("Error importing login: " + ex); + } + } + + rows = yield connection.execute("SELECT * FROM moz_disabledHosts"); + for (let row of rows) { + try { + let hostname = row.getResultByName("hostname"); + + this.store.data.disabledHosts.push(hostname); + } catch (ex) { + Cu.reportError("Error importing disabled host: " + ex); + } + } + } finally { + yield connection.close(); + } + }), +}; |