1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5/* This is a JavaScript module (JSM) to be imported via
6 * Components.utils.import() and acts as a singleton. Only the following
7 * listed symbols will exposed on import, and only when and where imported.
8 */
9
10var EXPORTED_SYMBOLS = [
11  "Address",
12  "CreditCard",
13  "DumpAddresses",
14  "DumpCreditCards",
15];
16
17const { Logger } = ChromeUtils.import("resource://tps/logger.jsm");
18
19ChromeUtils.defineModuleGetter(
20  this,
21  "formAutofillStorage",
22  "resource://formautofill/FormAutofillStorage.jsm"
23);
24
25ChromeUtils.defineModuleGetter(
26  this,
27  "OSKeyStore",
28  "resource://gre/modules/OSKeyStore.jsm"
29);
30
31class FormAutofillBase {
32  constructor(props, subStorageName, fields) {
33    this._subStorageName = subStorageName;
34    this._fields = fields;
35
36    this.props = {};
37    this.updateProps = null;
38    if ("changes" in props) {
39      this.updateProps = props.changes;
40    }
41    for (const field of this._fields) {
42      this.props[field] = field in props ? props[field] : null;
43    }
44  }
45
46  async getStorage() {
47    await formAutofillStorage.initialize();
48    return formAutofillStorage[this._subStorageName];
49  }
50
51  async Create() {
52    const storage = await this.getStorage();
53    await storage.add(this.props);
54  }
55
56  async Find() {
57    const storage = await this.getStorage();
58    return storage._data.find(entry =>
59      this._fields.every(field => entry[field] === this.props[field])
60    );
61  }
62
63  async Update() {
64    const storage = await this.getStorage();
65    const { guid } = await this.Find();
66    await storage.update(guid, this.updateProps, true);
67  }
68
69  async Remove() {
70    const storage = await this.getStorage();
71    const { guid } = await this.Find();
72    storage.remove(guid);
73  }
74}
75
76async function DumpStorage(subStorageName) {
77  await formAutofillStorage.initialize();
78  Logger.logInfo(`\ndumping ${subStorageName} list\n`, true);
79  const entries = formAutofillStorage[subStorageName]._data;
80  for (const entry of entries) {
81    Logger.logInfo(JSON.stringify(entry), true);
82  }
83  Logger.logInfo(`\n\nend ${subStorageName} list\n`, true);
84}
85
86const ADDRESS_FIELDS = [
87  "given-name",
88  "additional-name",
89  "family-name",
90  "organization",
91  "street-address",
92  "address-level2",
93  "address-level1",
94  "postal-code",
95  "country",
96  "tel",
97  "email",
98];
99
100class Address extends FormAutofillBase {
101  constructor(props) {
102    super(props, "addresses", ADDRESS_FIELDS);
103  }
104}
105
106async function DumpAddresses() {
107  await DumpStorage("addresses");
108}
109
110const CREDIT_CARD_FIELDS = [
111  "cc-name",
112  "cc-number",
113  "cc-exp-month",
114  "cc-exp-year",
115];
116
117class CreditCard extends FormAutofillBase {
118  constructor(props) {
119    super(props, "creditCards", CREDIT_CARD_FIELDS);
120  }
121
122  async Find() {
123    const storage = await this.getStorage();
124    await Promise.all(
125      storage._data.map(
126        async entry =>
127          (entry["cc-number"] = await OSKeyStore.decrypt(
128            entry["cc-number-encrypted"]
129          ))
130      )
131    );
132    return storage._data.find(entry => {
133      return this._fields.every(field => entry[field] === this.props[field]);
134    });
135  }
136}
137
138async function DumpCreditCards() {
139  await DumpStorage("creditCards");
140}
141