mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
713 lines
22 KiB
JavaScript
713 lines
22 KiB
JavaScript
|
/* ***** BEGIN LICENSE BLOCK *****
|
||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||
|
*
|
||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||
|
* the License. You may obtain a copy of the License at
|
||
|
* http://www.mozilla.org/MPL/
|
||
|
*
|
||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
|
* for the specific language governing rights and limitations under the
|
||
|
* License.
|
||
|
*
|
||
|
* The Original Code is mozilla.org code.
|
||
|
*
|
||
|
* The Initial Developer of the Original Code is Mozilla Corporation.
|
||
|
* Portions created by the Initial Developer are Copyright (C) 2007
|
||
|
* the Initial Developer. All Rights Reserved.
|
||
|
*
|
||
|
* Contributor(s):
|
||
|
* Justin Dolske <dolske@mozilla.com> (original author)
|
||
|
*
|
||
|
* Alternatively, the contents of this file may be used under the terms of
|
||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||
|
* of those above. If you wish to allow use of your version of this file only
|
||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||
|
* use your version of this file under the terms of the MPL, indicate your
|
||
|
* decision by deleting the provisions above and replace them with the notice
|
||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||
|
* the provisions above, a recipient may use your version of this file under
|
||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||
|
*
|
||
|
* ***** END LICENSE BLOCK ***** */
|
||
|
|
||
|
|
||
|
const Cc = Components.classes;
|
||
|
const Ci = Components.interfaces;
|
||
|
|
||
|
function LoginManagerStorage_legacy() { };
|
||
|
|
||
|
LoginManagerStorage_legacy.prototype = {
|
||
|
|
||
|
QueryInterface : function (iid) {
|
||
|
const interfaces = [Ci.nsILoginManagerStorage, Ci.nsISupports];
|
||
|
if (!interfaces.some( function(v) { return iid.equals(v) } ))
|
||
|
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
__logService : null, // Console logging service, used for debugging.
|
||
|
get _logService() {
|
||
|
if (!this.__logService)
|
||
|
this.__logService = Cc["@mozilla.org/consoleservice;1"]
|
||
|
.getService(Ci.nsIConsoleService);
|
||
|
return this.__logService;
|
||
|
},
|
||
|
|
||
|
__decoderRing : null, // nsSecretDecoderRing service
|
||
|
get _decoderRing() {
|
||
|
if (!this.__decoderRing)
|
||
|
this.__decoderRing = Cc["@mozilla.org/security/sdr;1"]
|
||
|
.getService(Ci.nsISecretDecoderRing);
|
||
|
return this.__decoderRing;
|
||
|
},
|
||
|
|
||
|
_prefBranch : null, // Preferences service
|
||
|
|
||
|
_datafile : null, // name of datafile (usually "signons2.txt")
|
||
|
_datapath : null, // path to datafile (usually profile directory)
|
||
|
_debug : false, // mirrors signon.debug
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Core datastructures
|
||
|
*
|
||
|
* EG: _logins["http://site.com"][0].password
|
||
|
* EG: _disabledHosts["never.site.com"]
|
||
|
*/
|
||
|
_logins : null,
|
||
|
_disabledHosts : null,
|
||
|
|
||
|
|
||
|
/*
|
||
|
* log
|
||
|
*
|
||
|
* Internal function for logging debug messages to the Error Console.
|
||
|
*/
|
||
|
log : function (message) {
|
||
|
if (!this._debug)
|
||
|
return;
|
||
|
dump("PwMgr Storage: " + message + "\n");
|
||
|
this._logService.logStringMessage("PwMgr Storage: " + message);
|
||
|
},
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/* ==================== Public Methods ==================== */
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
initWithFile : function(aPath, aInputFilename, aOutputFilename) {
|
||
|
this._datapath = aPath;
|
||
|
this._datafile = aInputFilename;
|
||
|
|
||
|
this.init();
|
||
|
|
||
|
if (aOutputFilename && aOutputFilename.length) {
|
||
|
this._datafile = aOutputFilename;
|
||
|
this._writeFile(aPath, aOutputFilename);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/*
|
||
|
* init
|
||
|
*
|
||
|
* Initialize this storage component and load stored passwords from disk.
|
||
|
*/
|
||
|
init : function () {
|
||
|
this._logins = {};
|
||
|
this._disabledHosts = {};
|
||
|
|
||
|
// Connect to the correct preferences branch.
|
||
|
this._prefBranch = Cc["@mozilla.org/preferences-service;1"]
|
||
|
.getService(Ci.nsIPrefService);
|
||
|
this._prefBranch = this._prefBranch.getBranch("signon.");
|
||
|
this._prefBranch.QueryInterface(Ci.nsIPrefBranch2);
|
||
|
|
||
|
if (this._prefBranch.prefHasUserValue("debug"))
|
||
|
this._debug = this._prefBranch.getBoolPref("debug");
|
||
|
|
||
|
// Check to see if the internal PKCS#11 token has been initialized.
|
||
|
// If not, set a blank password.
|
||
|
var tokenDB = Cc["@mozilla.org/security/pk11tokendb;1"]
|
||
|
.getService(Ci.nsIPK11TokenDB);
|
||
|
|
||
|
var token = tokenDB.getInternalKeyToken();
|
||
|
if (token.needsUserInit) {
|
||
|
this.log("Initializing key3.db with default blank password.");
|
||
|
token.initPassword("");
|
||
|
}
|
||
|
|
||
|
// Get the location of the user's profile.
|
||
|
if (!this._datapath) {
|
||
|
var DIR_SERVICE = new Components.Constructor(
|
||
|
"@mozilla.org/file/directory_service;1", "nsIProperties");
|
||
|
this._datapath = (new DIR_SERVICE()).get("ProfD", Ci.nsIFile).path;
|
||
|
}
|
||
|
|
||
|
if (!this._datafile)
|
||
|
this._datafile = this._prefBranch.getCharPref("SignonFileName2");
|
||
|
|
||
|
var importFile = null;
|
||
|
if (!this._doesFileExist(this._datapath, this._datafile)) {
|
||
|
this.log("SignonFilename2 file does not exist. (file=" +
|
||
|
this._datafile + ") path=(" + this._datapath + ")");
|
||
|
|
||
|
// Try reading the old file
|
||
|
importFile = this._prefBranch.getCharPref("SignonFileName");
|
||
|
if (!this._doesFileExist(this._datapath, importFile)) {
|
||
|
this.log("SignonFilename1 file does not exist. (file=" +
|
||
|
importFile + ") path=(" + this._datapath + ")");
|
||
|
this.log("Creating new signons file...");
|
||
|
importFile = null;
|
||
|
this._writeFile(this._datapath, this._datafile);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Read in the stored login data.
|
||
|
if (importFile) {
|
||
|
this.log("Importing " + importFile);
|
||
|
this._readFile(this._datapath, importFile);
|
||
|
|
||
|
this._writeFile(this._datapath, this._datafile);
|
||
|
} else {
|
||
|
this._readFile(this._datapath, this._datafile);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
|
||
|
/*
|
||
|
* addLogin
|
||
|
*
|
||
|
*/
|
||
|
addLogin : function (login) {
|
||
|
var key = login.hostname;
|
||
|
|
||
|
// If first entry for key, create an Array to hold it's logins.
|
||
|
if (!this._logins[key])
|
||
|
this._logins[key] = [];
|
||
|
|
||
|
this._logins[key].push(login);
|
||
|
|
||
|
this._writeFile(this._datapath, this._datafile);
|
||
|
},
|
||
|
|
||
|
|
||
|
/*
|
||
|
* removeLogin
|
||
|
*
|
||
|
*/
|
||
|
removeLogin : function (login) {
|
||
|
var key = login.hostname;
|
||
|
var logins = this._logins[key];
|
||
|
|
||
|
if (!logins)
|
||
|
throw "No logins found for hostname (" + key + ")";
|
||
|
|
||
|
for (var i = 0; i < logins.length; i++) {
|
||
|
if (logins[i].equals(login)) {
|
||
|
logins.splice(i, 1); // delete that login from array.
|
||
|
break;
|
||
|
// Note that if there are duplicate entries, they'll
|
||
|
// have to be deleted one-by-one.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Did we delete all the logins for this host?
|
||
|
if (logins.length == 0)
|
||
|
delete this._logins[key];
|
||
|
|
||
|
this._writeFile(this._datapath, this._datafile);
|
||
|
},
|
||
|
|
||
|
|
||
|
/*
|
||
|
* modifyLogin
|
||
|
*
|
||
|
*/
|
||
|
modifyLogin : function (oldLogin, newLogin) {
|
||
|
this.removeLogin(oldLogin);
|
||
|
this.addLogin(newLogin);
|
||
|
},
|
||
|
|
||
|
|
||
|
/*
|
||
|
* getAllLogins
|
||
|
*
|
||
|
* Returns an array of nsAccountInfo.
|
||
|
*/
|
||
|
getAllLogins : function (count) {
|
||
|
var result = [];
|
||
|
|
||
|
// Each entry is an array -- append the array entries to |result|.
|
||
|
for each (var hostLogins in this._logins) {
|
||
|
result = result.concat(hostLogins);
|
||
|
}
|
||
|
|
||
|
count.value = result.length; // needed for XPCOM
|
||
|
return result;
|
||
|
},
|
||
|
|
||
|
|
||
|
/*
|
||
|
* clearAllLogins
|
||
|
*
|
||
|
* Clears all logins from storage.
|
||
|
*/
|
||
|
clearAllLogins : function () {
|
||
|
this._logins = {};
|
||
|
// Disabled hosts kept, as one presumably doesn't want to erase those.
|
||
|
|
||
|
this._writeFile(this._datapath, this._datafile);
|
||
|
},
|
||
|
|
||
|
|
||
|
/*
|
||
|
* getAllDisabledHosts
|
||
|
*
|
||
|
*/
|
||
|
getAllDisabledHosts : function (count) {
|
||
|
var result = [];
|
||
|
|
||
|
for (var hostname in this._disabledHosts) {
|
||
|
result.push(hostname);
|
||
|
}
|
||
|
|
||
|
count.value = result.length; // needed for XPCOM
|
||
|
return result;
|
||
|
},
|
||
|
|
||
|
|
||
|
/*
|
||
|
* getLoginSavingEnabled
|
||
|
*
|
||
|
*/
|
||
|
getLoginSavingEnabled : function (hostname) {
|
||
|
return !this._disabledHosts[hostname];
|
||
|
},
|
||
|
|
||
|
|
||
|
/*
|
||
|
* setLoginSavingEnabled
|
||
|
*
|
||
|
*/
|
||
|
setLoginSavingEnabled : function (hostname, enabled) {
|
||
|
if (enabled)
|
||
|
delete this._disabledHosts[hostname];
|
||
|
else
|
||
|
this._disabledHosts[hostname] = true;
|
||
|
|
||
|
this._writeFile(this._datapath, this._datafile);
|
||
|
},
|
||
|
|
||
|
|
||
|
/*
|
||
|
* findLogins
|
||
|
*
|
||
|
*/
|
||
|
findLogins : function (count, hostname, formSubmitURL, httpRealm) {
|
||
|
var hostLogins = this._logins[hostname];
|
||
|
if (hostLogins == null) {
|
||
|
count.value = 0;
|
||
|
return [];
|
||
|
}
|
||
|
|
||
|
var result = [];
|
||
|
|
||
|
for each (var login in hostLogins) {
|
||
|
|
||
|
// If looking for an HTTP login, make sure the httpRealms match.
|
||
|
if (httpRealm != login.httpRealm)
|
||
|
continue;
|
||
|
|
||
|
// If looking for a form login, make sure the action URLs match
|
||
|
// ...unless the stored login is blank (not null), which means
|
||
|
// login was stored before we started keeping the action URL.
|
||
|
if (formSubmitURL != login.formSubmitURL &&
|
||
|
login.formSubmitURL != "")
|
||
|
continue;
|
||
|
|
||
|
result.push(login);
|
||
|
}
|
||
|
|
||
|
count.value = result.length; // needed for XPCOM
|
||
|
return result;
|
||
|
},
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/* ==================== Internal Methods ==================== */
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* _readFile
|
||
|
*
|
||
|
*/
|
||
|
_readFile : function (pathname, filename) {
|
||
|
var oldFormat = false;
|
||
|
var writeOnFinish = false;
|
||
|
|
||
|
this.log("Reading passwords from " + pathname + "/" + filename);
|
||
|
|
||
|
var file = Cc["@mozilla.org/file/local;1"]
|
||
|
.createInstance(Ci.nsILocalFile);
|
||
|
file.initWithPath(pathname);
|
||
|
file.append(filename);
|
||
|
|
||
|
var inputStream = Cc["@mozilla.org/network/file-input-stream;1"]
|
||
|
.createInstance(Ci.nsIFileInputStream);
|
||
|
inputStream.init(file, 0x01, -1, null); // RD_ONLY, -1=default perm
|
||
|
var lineStream = inputStream.QueryInterface(Ci.nsILineInputStream);
|
||
|
var line = { value: "" };
|
||
|
|
||
|
const STATE = { HEADER : 0, REJECT : 1, REALM : 2,
|
||
|
USERFIELD : 3, USERVALUE : 4,
|
||
|
PASSFIELD : 5, PASSVALUE : 6, ACTIONURL : 7 };
|
||
|
var parseState = STATE.HEADER;
|
||
|
|
||
|
var nsLoginInfo = new Components.Constructor(
|
||
|
"@mozilla.org/login-manager/loginInfo;1", Ci.nsILoginInfo);
|
||
|
var processEntry = false;
|
||
|
|
||
|
do {
|
||
|
var hasMore = lineStream.readLine(line);
|
||
|
|
||
|
switch (parseState) {
|
||
|
// Check file header
|
||
|
case STATE.HEADER:
|
||
|
if (line.value == "#2c") {
|
||
|
oldFormat = true;
|
||
|
} else if (line.value != "#2d") {
|
||
|
this.log("invalid file header (" + line.value + ")");
|
||
|
throw "invalid file header in " + filename;
|
||
|
// We could disable later writing to file, so we
|
||
|
// don't clobber whatever it is. ...however, that
|
||
|
// would mean corrupt files are not self-healing.
|
||
|
return;
|
||
|
}
|
||
|
parseState++;
|
||
|
break;
|
||
|
|
||
|
// Line is a hostname for which passwords should never be saved.
|
||
|
case STATE.REJECT:
|
||
|
if (line.value == ".") {
|
||
|
parseState++;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
this._disabledHosts[line.value] = true;
|
||
|
|
||
|
break;
|
||
|
|
||
|
// Line is a hostname, saved login(s) will follow
|
||
|
case STATE.REALM:
|
||
|
var hostrealm = line.value;
|
||
|
|
||
|
// Format is "http://site.com", with "(some realm)"
|
||
|
// appended if it's a HTTP-Auth login.
|
||
|
const realmFormat = /^(.+?)( \(.*\))?$/; // XXX .* or .+?
|
||
|
var matches = realmFormat.exec(hostrealm);
|
||
|
|
||
|
var hostname, httpRealm;
|
||
|
if (matches && matches.length == 3) {
|
||
|
hostname = matches[1];
|
||
|
httpRealm = matches[2] ?
|
||
|
matches[2].slice(2, -1) : null;
|
||
|
} else {
|
||
|
if (hostrealm != "") {
|
||
|
// Uhoh. This shouldn't happen, but try to deal.
|
||
|
this.log("Error parsing host/realm: " + hostrealm);
|
||
|
}
|
||
|
hostname = hostrealm;
|
||
|
httpRealm = null;
|
||
|
}
|
||
|
|
||
|
parseState++;
|
||
|
break;
|
||
|
|
||
|
// Line is the HTML 'name' attribute for the username field
|
||
|
// (or "." to indicate end of hostrealm)
|
||
|
case STATE.USERFIELD:
|
||
|
if (line.value == ".") {
|
||
|
parseState = STATE.REALM;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
var entry = new nsLoginInfo();
|
||
|
entry.hostname = hostname;
|
||
|
entry.httpRealm = httpRealm;
|
||
|
|
||
|
entry.usernameField = line.value;
|
||
|
parseState++;
|
||
|
break;
|
||
|
|
||
|
// Line is a username
|
||
|
case STATE.USERVALUE:
|
||
|
entry.username = this._decrypt(line.value);
|
||
|
parseState++;
|
||
|
break;
|
||
|
|
||
|
// Line is the HTML 'name' attribute for the password field,
|
||
|
// with a leading '*' character
|
||
|
case STATE.PASSFIELD:
|
||
|
entry.passwordField = line.value.substr(1);
|
||
|
parseState++;
|
||
|
break;
|
||
|
|
||
|
// Line is a password
|
||
|
case STATE.PASSVALUE:
|
||
|
entry.password = this._decrypt(line.value);
|
||
|
if (oldFormat) {
|
||
|
entry.formSubmitURL = "";
|
||
|
processEntry = true;
|
||
|
parseState = STATE.USERFIELD;
|
||
|
} else {
|
||
|
parseState++;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
// Line is the action URL
|
||
|
case STATE.ACTIONURL:
|
||
|
entry.formSubmitURL = line.value;
|
||
|
processEntry = true;
|
||
|
parseState = STATE.USERFIELD;
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
if (processEntry) {
|
||
|
if (entry.username == "" && entry.password == "") {
|
||
|
// Discard bogus entry, and update the file when done.
|
||
|
writeOnFinish = true;
|
||
|
} else {
|
||
|
if (!this._logins[hostname])
|
||
|
this._logins[hostname] = [];
|
||
|
this._logins[hostname].push(entry);
|
||
|
}
|
||
|
entry = null;
|
||
|
processEntry = false;
|
||
|
}
|
||
|
} while (hasMore);
|
||
|
|
||
|
lineStream.close();
|
||
|
|
||
|
if (writeOnFinish)
|
||
|
this._writeFile(pathname, filename);
|
||
|
|
||
|
return;
|
||
|
},
|
||
|
|
||
|
|
||
|
/*
|
||
|
* _writeFile
|
||
|
*
|
||
|
*/
|
||
|
_writeFile : function (pathname, filename) {
|
||
|
function writeLine(data) {
|
||
|
data += "\r\n";
|
||
|
outputStream.write(data, data.length);
|
||
|
}
|
||
|
|
||
|
this.log("Writing passwords to " + pathname + "/" + filename);
|
||
|
|
||
|
var file = Cc["@mozilla.org/file/local;1"]
|
||
|
.createInstance(Ci.nsILocalFile);
|
||
|
file.initWithPath(pathname);
|
||
|
file.append(filename);
|
||
|
|
||
|
var outputStream = Cc["@mozilla.org/network/safe-file-output-stream;1"]
|
||
|
.createInstance(Ci.nsIFileOutputStream);
|
||
|
outputStream.QueryInterface(Ci.nsISafeOutputStream);
|
||
|
|
||
|
// WR_ONLY|CREAT|TRUNC
|
||
|
outputStream.init(file, 0x02 | 0x08 | 0x20, 0600, null);
|
||
|
|
||
|
// write file version header
|
||
|
writeLine("#2d");
|
||
|
|
||
|
// write disabled logins list
|
||
|
for (var hostname in this._disabledHosts) {
|
||
|
writeLine(hostname);
|
||
|
}
|
||
|
|
||
|
// write end-of-reject-list marker
|
||
|
writeLine(".");
|
||
|
|
||
|
for (var hostname in this._logins) {
|
||
|
function sortByRealm(a,b) {
|
||
|
a = a.httpRealm;
|
||
|
b = b.httpRealm;
|
||
|
|
||
|
if (!a && !b)
|
||
|
return 0;
|
||
|
|
||
|
if (!a || a < b)
|
||
|
return -1;
|
||
|
|
||
|
if (!b || b > a)
|
||
|
return 1;
|
||
|
|
||
|
return 0; // a==b, neither is null
|
||
|
}
|
||
|
|
||
|
// Sort logins by httpRealm. This allows us to group multiple
|
||
|
// logins for the same realm together.
|
||
|
this._logins[hostname].sort(sortByRealm);
|
||
|
|
||
|
|
||
|
// write each login known for the host
|
||
|
var lastRealm = null;
|
||
|
var firstEntry = true;
|
||
|
for each (var login in this._logins[hostname]) {
|
||
|
|
||
|
// If this login is for a new realm, start a new entry.
|
||
|
if (login.httpRealm != lastRealm || firstEntry) {
|
||
|
// end previous entry, if needed.
|
||
|
if (!firstEntry)
|
||
|
writeLine(".");
|
||
|
|
||
|
var hostrealm = login.hostname;
|
||
|
if (login.httpRealm)
|
||
|
hostrealm += " (" + login.httpRealm + ")";
|
||
|
|
||
|
writeLine(hostrealm);
|
||
|
}
|
||
|
|
||
|
firstEntry = false;
|
||
|
|
||
|
var encUsername = this._encrypt(login.username);
|
||
|
var encPassword = this._encrypt(login.password);
|
||
|
|
||
|
writeLine((login.usernameField ? login.usernameField : ""));
|
||
|
writeLine(encUsername);
|
||
|
writeLine("*" +
|
||
|
(login.passwordField ? login.passwordField : ""));
|
||
|
writeLine(encPassword);
|
||
|
writeLine((login.formSubmitURL ? login.formSubmitURL : ""));
|
||
|
|
||
|
lastRealm = login.httpRealm;
|
||
|
}
|
||
|
|
||
|
// write end-of-host marker
|
||
|
writeLine(".");
|
||
|
}
|
||
|
|
||
|
// [if there were no hosts, no end-of-host marker (".") needed]
|
||
|
|
||
|
outputStream.finish();
|
||
|
},
|
||
|
|
||
|
|
||
|
/*
|
||
|
* _encrypt
|
||
|
*
|
||
|
*/
|
||
|
_encrypt : function (plainText) {
|
||
|
return this._decoderRing.encryptString(plainText);
|
||
|
},
|
||
|
|
||
|
|
||
|
/*
|
||
|
* decrypt
|
||
|
*
|
||
|
*/
|
||
|
_decrypt : function (cipherText) {
|
||
|
var plainText = null;
|
||
|
|
||
|
if (cipherText.charAt(0) == '~') {
|
||
|
// The pre-Mozilla-1.0 file format obscured entries by
|
||
|
// base64-encoding them. These entries are signaled by a leading
|
||
|
// '~' character.
|
||
|
// This file format is no longer supported.
|
||
|
|
||
|
throw "!!!!! base64 encoded passwords not supported !!!!!";
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
plainText = this._decoderRing.decryptString(cipherText);
|
||
|
} catch (e) {
|
||
|
this.log("Failed to decrypt string: " + cipherText);
|
||
|
}
|
||
|
|
||
|
return plainText;
|
||
|
},
|
||
|
|
||
|
|
||
|
/*
|
||
|
* _doesFileExist
|
||
|
*
|
||
|
*/
|
||
|
_doesFileExist : function (filepath, filename) {
|
||
|
var file = Cc["@mozilla.org/file/local;1"]
|
||
|
.createInstance(Ci.nsILocalFile);
|
||
|
file.initWithPath(filepath);
|
||
|
file.append(filename);
|
||
|
|
||
|
return file.exists();
|
||
|
}
|
||
|
}; // end of nsLoginManagerStorage_legacy implementation
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// Boilerplate code for component registration...
|
||
|
var gModule = {
|
||
|
registerSelf: function(componentManager, fileSpec, location, type) {
|
||
|
componentManager = componentManager.QueryInterface(
|
||
|
Ci.nsIComponentRegistrar);
|
||
|
for each (var obj in this._objects)
|
||
|
componentManager.registerFactoryLocation(obj.CID,
|
||
|
obj.className, obj.contractID,
|
||
|
fileSpec, location, type);
|
||
|
},
|
||
|
|
||
|
unregisterSelf: function (componentManager, location, type) {
|
||
|
for each (var obj in this._objects)
|
||
|
componentManager.unregisterFactoryLocation(obj.CID, location);
|
||
|
},
|
||
|
|
||
|
getClassObject: function(componentManager, cid, iid) {
|
||
|
if (!iid.equals(Ci.nsIFactory))
|
||
|
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||
|
|
||
|
for (var key in this._objects) {
|
||
|
if (cid.equals(this._objects[key].CID))
|
||
|
return this._objects[key].factory;
|
||
|
}
|
||
|
|
||
|
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||
|
},
|
||
|
|
||
|
_objects: {
|
||
|
service: {
|
||
|
CID : Components.ID("{e09e4ca6-276b-4bb4-8b71-0635a3a2a007}"),
|
||
|
contractID : "@mozilla.org/login-manager/storage/legacy;1",
|
||
|
className : "LoginManagerStorage_legacy",
|
||
|
factory : aFactory = {
|
||
|
createInstance: function(aOuter, aIID) {
|
||
|
if (aOuter != null)
|
||
|
throw Components.results.NS_ERROR_NO_AGGREGATION;
|
||
|
var svc = new LoginManagerStorage_legacy();
|
||
|
return svc.QueryInterface(aIID);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
canUnload: function(componentManager) {
|
||
|
return true;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
function NSGetModule(compMgr, fileSpec) {
|
||
|
return gModule;
|
||
|
}
|