File: cloudFileAccounts.js

package info (click to toggle)
thunderbird 1%3A52.9.1-1~deb9u1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 1,709,148 kB
  • sloc: cpp: 5,081,792; ansic: 2,051,951; python: 458,733; java: 241,615; xml: 193,600; asm: 178,649; sh: 81,881; makefile: 24,702; perl: 16,874; objc: 4,389; yacc: 1,816; ada: 1,697; lex: 1,257; pascal: 1,251; cs: 879; exp: 499; php: 436; lisp: 258; awk: 152; sed: 51; ruby: 47; csh: 27
file content (297 lines) | stat: -rw-r--r-- 9,451 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
/* 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/. */

this.EXPORTED_SYMBOLS = ["cloudFileAccounts"];

var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;

var CATEGORY = "cloud-files";
var PREF_ROOT = "mail.cloud_files.";
var ACCOUNT_ROOT = PREF_ROOT + "accounts.";

// The following constants are used to query and insert entries
// into the nsILoginManager.
var PWDMGR_HOST = "chrome://messenger/cloudfile";
var PWDMGR_REALM = "BigFiles Auth Token";

Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource:///modules/iteratorUtils.jsm");

var cloudFileAccounts = {
  get kTokenRealm() {
    return PWDMGR_REALM;
  },

  get _accountKeys() {
    let accountKeySet = {};
    let branch = Services.prefs.getBranch(ACCOUNT_ROOT);
    let children = branch.getChildList("", {});
    for (let child of children) {
      let subbranch = child.substr(0, child.indexOf("."));
      accountKeySet[subbranch] = 1;
    }

    // TODO: sort by ordinal
    return Object.keys(accountKeySet);
  },

  _getInitedProviderForType: function(aAccountKey, aType) {
    let provider = this.getProviderForType(aType);
    if (provider) {
      try {
        provider.init(aAccountKey);
      } catch (e) {
        Components.utils.reportError(e);
        provider = null;
      }
    }
    return provider;
  },

  _createUniqueAccountKey: function() {
    // Pick a unique account key (TODO: this is a dumb way to do it, probably)
    let existingKeys = this._accountKeys;
    for (let n = 1; ; n++) {

      if (!existingKeys.includes("account" + n))
        return "account" + n;
    }
  },

  /**
   * Ensure that we have the account key for an account. If we already have the
   * key, just return it. If we have the nsIMsgCloudFileProvider, get the key
   * from it.
   *
   * @param aKeyOrAccount the key or the account object
   * @return the account key
   */
  _ensureKey: function(aKeyOrAccount) {
    if (typeof aKeyOrAccount == "string")
      return aKeyOrAccount;
    else if ("accountKey" in aKeyOrAccount)
      return aKeyOrAccount.accountKey;
    else
      throw new Error("string or nsIMsgCloudFileProvider expected");
  },

  getProviderForType: function(aType) {
    try {
      let className = categoryManager.getCategoryEntry(CATEGORY, aType);
      let provider = Cc[className].createInstance(Ci.nsIMsgCloudFileProvider);
      return provider;
    } catch (e) {
      if (e.result != Cr.NS_ERROR_NOT_AVAILABLE) {
        // If a provider is not available we swallow the error message.
        // Otherwise at least notify, so developers can fix things.
        Cu.reportError("Getting provider for type=" + aType + " FAILED; " + e);
      }
    }
    return null;
  },

  // aExtraPrefs are prefs specific to an account provider.
  createAccount: function(aType, aRequestObserver, aExtraPrefs) {
    let key = this._createUniqueAccountKey();

    try {
      Services.prefs
              .setCharPref(ACCOUNT_ROOT + key + ".type", aType);

      if (aExtraPrefs !== undefined)
        this._processExtraPrefs(key, aExtraPrefs);

      let provider = this._getInitedProviderForType(key, aType);
      if (provider)
        provider.createExistingAccount(aRequestObserver);

      return provider;
    }
    catch(e) {
      Services.prefs.deleteBranch(ACCOUNT_ROOT + key);
      throw e;
    }
  },

  // Set provider-specific prefs
  _processExtraPrefs: function CFA__processExtraPrefs(aAccountKey,
                                                      aExtraPrefs) {
    const kFuncMap = {
      "int": "setIntPref",
      "bool": "setBoolPref",
      "char": "setCharPref",
    };

    for (let prefKey in aExtraPrefs) {
      let type = aExtraPrefs[prefKey].type;
      let value = aExtraPrefs[prefKey].value;

      if (!(type in kFuncMap)) {
        Components.utils.reportError("Did not recognize type: " + type);
        continue;
      }

      let func = kFuncMap[type];
      Services.prefs[func](ACCOUNT_ROOT + aAccountKey + "." + prefKey,
                           value);
    }
  },

  enumerateProviders: function*() {
    for (let entry in fixIterator(categoryManager.enumerateCategory(CATEGORY),
                                  Ci.nsISupportsCString)) {
      let provider = this.getProviderForType(entry.data);
      yield [entry.data, provider];
    }
  },

  getAccount: function(aKey) {
    let type = Services.prefs.QueryInterface(Ci.nsIPrefBranch)
                       .getCharPref(ACCOUNT_ROOT + aKey + ".type");
    return this._getInitedProviderForType(aKey, type);
  },

  removeAccount: function(aKeyOrAccount) {
    let key = this._ensureKey(aKeyOrAccount);

    let type = Services.prefs.QueryInterface(Ci.nsIPrefBranch)
                       .deleteBranch(ACCOUNT_ROOT + key);

    // Destroy any secret tokens for this accountKey.
    let logins = Services.logins
                         .findLogins({}, PWDMGR_HOST, null, "");
    for (let login of logins) {
      if (login.username == key)
        Services.logins.removeLogin(login);
    }
  },

  get accounts() {
    return this._accountKeys.filter(key => this.getAccount(key) != null).
      map(key => this.getAccount(key));
  },

  getAccountsForType: function CFA_getAccountsForType(aType) {
    let result = [];

    for (let accountKey of this._accountKeys) {
      let type = Services.prefs.getCharPref(ACCOUNT_ROOT + accountKey + ".type");
      if (type === aType)
        result.push(this.getAccount(accountKey));
    }

    return result;
  },

  addAccountDialog: function CFA_addAccountDialog() {
    let params = {accountKey: null};
    Services.wm
            .getMostRecentWindow(null)
            .openDialog("chrome://messenger/content/cloudfile/"
                        + "addAccountDialog.xul",
                        "", "chrome, dialog, modal, resizable=yes",
                        params).focus();
    return params.accountKey;
  },

  getDisplayName: function(aKeyOrAccount) {
    try {
      let key = this._ensureKey(aKeyOrAccount);
      return Services.prefs.getCharPref(ACCOUNT_ROOT +
                                        key + ".displayName");
    } catch(e) {
      // If no display name has been set, we return the empty string.
      Components.utils.reportError(e);
      return "";
    }
  },

  setDisplayName: function(aKeyOrAccount, aDisplayName) {
    let key = this._ensureKey(aKeyOrAccount);
    Services.prefs.setCharPref(ACCOUNT_ROOT + key +
                               ".displayName", aDisplayName);
  },

  /**
   * Retrieve a secret value, like an authorization token, for an account.
   *
   * @param aKeyOrAccount an nsIMsgCloudFileProvider, or an accountKey
   *                      for a provider.
   * @param aRealm a human-readable string describing what exactly
   *               was being stored. Should match the realm used when setting
   *               the value.
   */
  getSecretValue: function(aKeyOrAccount, aRealm) {
    let key = this._ensureKey(aKeyOrAccount);

    let loginInfo = this._getLoginInfoForKey(key, aRealm);

    if (loginInfo)
      return loginInfo.password;

    return null;
  },

  /**
   * Store a secret value, like an authorization token, for an account
   * in nsILoginManager.
   *
   * @param aKeyOrAccount an nsIMsgCloudFileProvider, or an accountKey
   *                      for a provider.
   * @param aRealm a human-readable string describing what exactly
   *               is being stored here. To reduce magic strings, you can use
   *               cloudFileAccounts.kTokenRealm for simple auth tokens, and
   *               anything else for custom secret values.
   * @param aToken The token to be saved.  If this is set to null or the
   *               empty string, then the entry for this key will be removed.
   */
  setSecretValue: function(aKeyOrAccount, aRealm, aToken) {
    let key = this._ensureKey(aKeyOrAccount);
    let loginInfo = this._getLoginInfoForKey(key, aRealm);

    if (!aToken) {
      if (!loginInfo)
        return;

      Services.logins.removeLogin(loginInfo);
      return;
    }

    let newLoginInfo = Cc["@mozilla.org/login-manager/loginInfo;1"]
                       .createInstance(Ci.nsILoginInfo);
    newLoginInfo.init(PWDMGR_HOST, null, aRealm, key,
                      aToken, "", "");

    if (loginInfo)
      Services.logins.modifyLogin(loginInfo, newLoginInfo);
    else
      Services.logins.addLogin(newLoginInfo);
  },

  /**
   * Searches the nsILoginManager for an nsILoginInfo for BigFiles with
   * the username set to aKey, and the realm set to aRealm.
   *
   * @param aKey a key for an nsIMsgCloudFileProvider that we're searching
   *             for login info for.
   * @param aRealm the realm that the login info was stored under.
   */
  _getLoginInfoForKey: function(aKey, aRealm) {
    let logins = Services.logins
                         .findLogins({}, PWDMGR_HOST, null, aRealm);
    for (let login of logins) {
      if (login.username == aKey)
        return login;
    }
    return null;
  },
};

XPCOMUtils.defineLazyServiceGetter(this, "categoryManager",
                                   "@mozilla.org/categorymanager;1",
                                   "nsICategoryManager");