/* -*- 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 id = row.getResultByName("id"); let hostname = row.getResultByName("hostname"); this.store.data.disabledHosts.push(hostname); } catch (ex) { Cu.reportError("Error importing disabled host: " + ex); } } } finally { yield connection.close(); } }), };