Source code

Revision control

Other Tools

1
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
2
/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
/**
8
* Provides an object that has a method to import login-related data from the
9
* previous SQLite storage format.
10
*/
11
12
"use strict";
13
14
const EXPORTED_SYMBOLS = ["LoginImport"];
15
16
// Globals
17
18
ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
19
ChromeUtils.defineModuleGetter(
20
this,
21
"Sqlite",
23
);
24
ChromeUtils.defineModuleGetter(
25
this,
26
"NetUtil",
28
);
29
30
// LoginImport
31
32
/**
33
* Provides an object that has a method to import login-related data from the
34
* previous SQLite storage format.
35
*
36
* @param aStore
37
* LoginStore object where imported data will be added.
38
* @param aPath
39
* String containing the file path of the SQLite login database.
40
*/
41
this.LoginImport = function(aStore, aPath) {
42
this.store = aStore;
43
this.path = aPath;
44
};
45
46
this.LoginImport.prototype = {
47
/**
48
* LoginStore object where imported data will be added.
49
*/
50
store: null,
51
52
/**
53
* String containing the file path of the SQLite login database.
54
*/
55
path: null,
56
57
/**
58
* Imports login-related data from the previous SQLite storage format.
59
*/
60
async import() {
61
// We currently migrate data directly from the database to the JSON store at
62
// first run, then we set a preference to prevent repeating the import.
63
// Thus, merging with existing data is not a use case we support. This
64
// restriction might be removed to support re-importing passwords set by an
65
// old version by flipping the import preference and restarting.
66
if (this.store.data.logins.length) {
67
throw new Error(
68
"Unable to import saved passwords because some data " +
69
"has already been imported or saved."
70
);
71
}
72
73
// When a timestamp is not specified, we will use the same reference time.
74
let referenceTimeMs = Date.now();
75
76
let connection = await Sqlite.openConnection({ path: this.path });
77
try {
78
let schemaVersion = await connection.getSchemaVersion();
79
80
// We support importing database schema versions from 3 onwards.
81
// Version 3 was implemented in bug 316084 (Firefox 3.6, March 2009).
82
// Version 4 was implemented in bug 465636 (Firefox 4, March 2010).
83
// Version 5 was implemented in bug 718817 (Firefox 13, February 2012).
84
if (schemaVersion < 3) {
85
throw new Error(
86
"Unable to import saved passwords because " +
87
"the existing profile is too old."
88
);
89
}
90
91
let rows = await connection.execute("SELECT * FROM moz_logins");
92
for (let row of rows) {
93
try {
94
let hostname = row.getResultByName("hostname");
95
let httpRealm = row.getResultByName("httpRealm");
96
let formSubmitURL = row.getResultByName("formSubmitURL");
97
let usernameField = row.getResultByName("usernameField");
98
let passwordField = row.getResultByName("passwordField");
99
let encryptedUsername = row.getResultByName("encryptedUsername");
100
let encryptedPassword = row.getResultByName("encryptedPassword");
101
102
// The "guid" field was introduced in schema version 2, and the
103
// "enctype" field was introduced in schema version 3. We don't
104
// support upgrading from older versions of the database.
105
let guid = row.getResultByName("guid");
106
let encType = row.getResultByName("encType");
107
108
// The time and count fields were introduced in schema version 4.
109
let timeCreated = null;
110
let timeLastUsed = null;
111
let timePasswordChanged = null;
112
let timesUsed = null;
113
try {
114
timeCreated = row.getResultByName("timeCreated");
115
timeLastUsed = row.getResultByName("timeLastUsed");
116
timePasswordChanged = row.getResultByName("timePasswordChanged");
117
timesUsed = row.getResultByName("timesUsed");
118
} catch (ex) {}
119
120
// These columns may be null either because they were not present in
121
// the database or because the record was created on a new schema
122
// version by an old application version.
123
if (!timeCreated) {
124
timeCreated = referenceTimeMs;
125
}
126
if (!timeLastUsed) {
127
timeLastUsed = referenceTimeMs;
128
}
129
if (!timePasswordChanged) {
130
timePasswordChanged = referenceTimeMs;
131
}
132
if (!timesUsed) {
133
timesUsed = 1;
134
}
135
136
this.store.data.logins.push({
137
id: this.store.data.nextId++,
138
hostname,
139
httpRealm,
140
formSubmitURL,
141
usernameField,
142
passwordField,
143
encryptedUsername,
144
encryptedPassword,
145
guid,
146
encType,
147
timeCreated,
148
timeLastUsed,
149
timePasswordChanged,
150
timesUsed,
151
});
152
} catch (ex) {
153
Cu.reportError("Error importing login: " + ex);
154
}
155
}
156
} finally {
157
await connection.close();
158
}
159
},
160
};