summaryrefslogtreecommitdiffstats
path: root/toolkit/components/passwordmgr/LoginImport.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/passwordmgr/LoginImport.jsm')
-rw-r--r--toolkit/components/passwordmgr/LoginImport.jsm173
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();
+ }
+ }),
+};