This commit is contained in:
Dan Mills 2007-11-13 21:37:20 -08:00
Родитель de10e7a246
Коммит cd9b1836db
4 изменённых файлов: 222 добавлений и 101 удалений

Просмотреть файл

@ -48,7 +48,7 @@ const MODE_TRUNCATE = 0x20;
const PERMS_FILE = 0644;
const PERMS_DIRECTORY = 0755;
const STORAGE_FORMAT_VERSION = 0;
const STORAGE_FORMAT_VERSION = 1;
const ONE_BYTE = 1;
const ONE_KILOBYTE = 1024 * ONE_BYTE;
@ -173,12 +173,11 @@ BookmarksSyncService.prototype = {
return branch.setCharPref("browser.places.sync.username", value);
},
get password() {
_lmGet: function BSS__lmGet(realm) {
// fixme: make a request and get the realm
let password;
let lm = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
let logins = lm.findLogins({}, makeURI(this._serverURL).hostPort, null,
'services.mozilla.com - proxy');
let logins = lm.findLogins({}, 'chrome://sync', null, realm);
for (let i = 0; i < logins.length; i++) {
if (logins[i].username == this.username) {
@ -188,27 +187,43 @@ BookmarksSyncService.prototype = {
}
return password;
},
set password(value) {
_lmSet: function BSS__lmSet(realm, password) {
// cleanup any existing passwords
let uri = makeURI(this._serverURL);
let lm = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
let logins = lm.findLogins({}, uri.hostPort, null,
'services.mozilla.com - proxy');
let logins = lm.findLogins({}, 'chrome://sync', null, realm);
for(let i = 0; i < logins.length; i++) {
lm.removeLogin(logins[i]);
}
if (!value)
if (!password)
return;
// save the new one
let nsLoginInfo = new Components.Constructor(
"@mozilla.org/login-manager/loginInfo;1", Ci.nsILoginInfo, "init");
let login = new nsLoginInfo(uri.hostPort, null, 'services.mozilla.com - proxy',
this.username, value, null, null);
let login = new nsLoginInfo('chrome://sync', null, realm,
this.username, password, null, null);
lm.addLogin(login);
},
get password() {
return this._lmGet('Mozilla Services Password');
},
set password(value) {
this._lmSet('Mozilla Services Password', value);
},
_passphrase: null,
get passphrase() {
if (this._passphrase === null)
return this._lmGet('Mozilla Services Encryption Passphrase');
return this._passphrase;
},
set passphrase(value) {
this._lmSet('Mozilla Services Encryption Passphrase', value);
},
get userPath() {
this._log.info("Hashing username " + this.username);
@ -240,6 +255,29 @@ BookmarksSyncService.prototype = {
return null;
},
_encryptionChanged: false,
get encryption() {
let branch = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefBranch);
return branch.getCharPref("browser.places.sync.encryption");
},
set encryption(value) {
switch (value) {
case "XXXTEA":
case "none":
let branch = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefBranch);
let cur = branch.getCharPref("browser.places.sync.encryption");
if (value != cur) {
this._encryptionChanged = true;
branch.setCharPref("browser.places.sync.encryption", value);
}
break;
default:
throw "Invalid encryption value: " + value;
}
},
_init: function BSS__init() {
this._initLogs();
this._log.info("Bookmarks Sync Service Initializing");
@ -1171,29 +1209,49 @@ BookmarksSyncService.prototype = {
server.deltas.push(serverDelta);
gen = this._dav.PUT("bookmarks-deltas.json",
this._mungeCommands(server.deltas), cont);
let deltasPut = yield;
gen.close();
if (server.formatVersion != STORAGE_FORMAT_VERSION ||
this._encryptionChanged) {
gen = this._fullUpload.async(this, cont);
let status = yield;
gen.close();
if (!status)
this._log.error("Could not upload files to server"); // eep?
// FIXME: need to watch out for the storage format version changing,
// in that case we'll have to re-upload all the files, not just these
gen = this._dav.PUT("bookmarks-status.json",
uneval({GUID: this._snapshotGUID,
formatVersion: STORAGE_FORMAT_VERSION,
snapVersion: server.snapVersion,
maxVersion: this._snapshotVersion}), cont);
let statusPut = yield;
gen.close();
if (deltasPut.status >= 200 && deltasPut.status < 300 &&
statusPut.status >= 200 && statusPut.status < 300) {
this._log.info("Successfully updated deltas and status on server");
this._saveSnapshot();
} else {
// FIXME: revert snapshot here? - can't, we already applied
// updates locally! - need to save and retry
this._log.error("Could not update deltas on server");
let data;
if (this.encryption == "none") {
data = this._mungeCommands(server.deltas);
} else if (this.encryption == "XXXTEA") {
this._log.debug("Encrypting snapshot");
data = this._encrypter.encrypt(uneval(server.deltas), this.passphrase);
this._log.debug("Done encrypting snapshot");
} else {
this._log.error("Unknown encryption scheme: " + this.encryption);
throw 'close generator';
}
gen = this._dav.PUT("bookmarks-deltas.json", data, cont);
let deltasPut = yield;
gen.close();
gen = this._dav.PUT("bookmarks-status.json",
uneval({GUID: this._snapshotGUID,
formatVersion: STORAGE_FORMAT_VERSION,
snapVersion: server.snapVersion,
maxVersion: this._snapshotVersion,
snapEncryption: server.snapEncryption,
deltasEncryption: this.encryption}), cont);
let statusPut = yield;
gen.close();
if (deltasPut.status >= 200 && deltasPut.status < 300 &&
statusPut.status >= 200 && statusPut.status < 300) {
this._log.info("Successfully updated deltas and status on server");
this._saveSnapshot();
} else {
// FIXME: revert snapshot here? - can't, we already applied
// updates locally! - need to save and retry
this._log.error("Could not update deltas on server");
}
}
}
@ -1254,6 +1312,38 @@ BookmarksSyncService.prototype = {
}
},
// NOTE: this method can throw 'close generator'
_checkStatus: function BSS__checkStatus(code, msg) {
if (code >= 200 && code < 300)
return;
this._log.error(msg + " Error code: " + code);
throw 'close generator';
},
// NOTE: this method can throw 'close generator'
_decrypt: function BSS__decrypt(alg, data) {
let out;
switch (alg) {
case "XXXTEA":
try {
this._log.debug("Decrypting data");
out = eval(this._encrypter.decrypt(data, this.passphrase));
this._log.debug("Done decrypting data");
} catch (e) {
this._log.error("Could not decrypt server snapshot");
throw 'close generator';
}
break;
case "none":
out = eval(data);
break;
default:
this._log.error("Unknown encryption algorithm: " + alg);
throw 'close generator';
}
return out;
},
/* Get the deltas/combined updates from the server
* Returns:
* status:
@ -1266,6 +1356,10 @@ BookmarksSyncService.prototype = {
* the latest version on the server
* snapVersion:
* the version of the current snapshot on the server (deltas not applied)
* snapEncryption:
* encryption algorithm currently used on the server-stored snapshot
* deltasEncryption:
* encryption algorithm currently used on the server-stored deltas
* snapshot:
* full snapshot of the latest server version (deltas applied)
* deltas:
@ -1278,6 +1372,7 @@ BookmarksSyncService.prototype = {
let cont = yield;
let ret = {status: -1,
formatVersion: null, maxVersion: null, snapVersion: null,
snapEncryption: null, deltasEncryption: null,
snapshot: null, deltas: null, updates: null};
try {
@ -1301,6 +1396,11 @@ BookmarksSyncService.prototype = {
generatorDone(this, onComplete, ret)
throw 'close generator';
}
if (status.formatVersion == 0) {
ret.snapEncryption = status.snapEncryption = "none";
ret.deltasEncryption = status.deltasEncryption = "none";
}
if (status.GUID != this._snapshotGUID) {
this._log.info("Remote/local sync GUIDs do not match. " +
@ -1319,25 +1419,15 @@ BookmarksSyncService.prototype = {
gen = this._dav.GET("bookmarks-snapshot.json", cont);
resp = yield;
gen.close()
this._checkStatus(resp.status, "Could not download snapshot.");
snap = this._decrypt(status.snapEncryption, resp.responseText);
if (resp.status < 200 || resp.status >= 300) {
this._log.error("Could not download server snapshot");
generatorDone(this, onComplete, ret)
throw 'close generator';
}
snap = eval(resp.responseText);
this._log.info("Downloading server deltas");
gen = this._dav.GET("bookmarks-deltas.json", cont);
resp = yield;
gen.close();
if (resp.status < 200 || resp.status >= 300) {
this._log.error("Could not download server deltas");
generatorDone(this, onComplete, ret)
throw 'close generator';
}
allDeltas = eval(resp.responseText);
this._checkStatus(resp.status, "Could not download deltas.");
allDeltas = this._decrypt(status.deltasEncryption, resp.responseText);
deltas = eval(uneval(allDeltas));
} else if (this._snapshotVersion >= status.snapVersion &&
@ -1348,13 +1438,8 @@ BookmarksSyncService.prototype = {
gen = this._dav.GET("bookmarks-deltas.json", cont);
resp = yield;
gen.close();
if (resp.status < 200 || resp.status >= 300) {
this._log.error("Could not download server deltas");
generatorDone(this, onComplete, ret)
throw 'close generator';
}
allDeltas = eval(resp.responseText);
this._checkStatus(resp.status, "Could not download deltas.");
allDeltas = this._decrypt(status.deltasEncryption, resp.responseText);
deltas = allDeltas.slice(this._snapshotVersion - status.snapVersion);
} else if (this._snapshotVersion == status.maxVersion) {
@ -1365,18 +1450,12 @@ BookmarksSyncService.prototype = {
gen = this._dav.GET("bookmarks-deltas.json", cont);
resp = yield;
gen.close();
if (resp.status < 200 || resp.status >= 300) {
this._log.error("Could not download server deltas");
generatorDone(this, onComplete, ret)
throw 'close generator';
}
allDeltas = eval(resp.responseText);
this._checkStatus(resp.status, "Could not download deltas.");
allDeltas = this._decrypt(status.deltasEncryption, resp.responseText);
deltas = [];
} else { // this._snapshotVersion > status.maxVersion
this._log.error("Server snapshot is older than local snapshot");
generatorDone(this, onComplete, ret)
throw 'close generator';
}
@ -1388,6 +1467,8 @@ BookmarksSyncService.prototype = {
ret.formatVersion = status.formatVersion;
ret.maxVersion = status.maxVersion;
ret.snapVersion = status.snapVersion;
ret.snapEncryption = status.snapEncryption;
ret.deltasEncryption = status.deltasEncryption;
ret.snapshot = snap;
ret.deltas = allDeltas;
gen = this._detectUpdates.async(this, cont, this._snapshot, snap);
@ -1401,44 +1482,12 @@ BookmarksSyncService.prototype = {
this._snapshot = this._getBookmarks();
this._snapshotVersion = 0;
this._snapshotGUID = null; // in case there are other snapshots out there
gen = this._dav.PUT("bookmarks-snapshot.json",
this._mungeNodes(this._snapshot), cont);
resp = yield;
gen.close();
if (resp.status < 200 || resp.status >= 300) {
this._log.error("Could not upload snapshot to server, error code: " +
resp.status);
generatorDone(this, onComplete, ret)
throw 'close generator';
}
gen = this._dav.PUT("bookmarks-deltas.json", uneval([]), cont);
resp = yield;
gen = this._fullUpload.async(this, cont);
let uploadStatus = yield;
gen.close();
if (resp.status < 200 || resp.status >= 300) {
this._log.error("Could not upload deltas to server, error code: " +
resp.status);
generatorDone(this, onComplete, ret)
if (!uploadStatus)
throw 'close generator';
}
gen = this._dav.PUT("bookmarks-status.json",
uneval({GUID: this._snapshotGUID,
formatVersion: STORAGE_FORMAT_VERSION,
snapVersion: this._snapshotVersion,
maxVersion: this._snapshotVersion}), cont);
resp = yield;
gen.close();
if (resp.status < 200 || resp.status >= 300) {
this._log.error("Could not upload status file to server, error code: " +
resp.status);
generatorDone(this, onComplete, ret)
throw 'close generator';
}
this._log.info("Initial upload to server successful");
this._saveSnapshot();
@ -1447,6 +1496,8 @@ BookmarksSyncService.prototype = {
ret.formatVersion = STORAGE_FORMAT_VERSION;
ret.maxVersion = this._snapshotVersion;
ret.snapVersion = this._snapshotVersion;
ret.snapEncryption = this.encryption;
ret.deltasEncryption = this.encryption;
ret.snapshot = eval(uneval(this._snapshot));
ret.deltas = [];
ret.updates = [];
@ -1469,6 +1520,57 @@ BookmarksSyncService.prototype = {
this._log.warn("generator not properly closed");
},
_fullUpload: function BSS__fullUpload(onComplete) {
let cont = yield;
let ret = false;
try {
let data;
if (this.encryption == "none") {
data = this._mungeNodes(this._snapshot);
} else if (this.encryption == "XXXTEA") {
this._log.debug("Encrypting snapshot");
data = this._encrypter.encrypt(uneval(this._snapshot), this.passphrase);
this._log.debug("Done encrypting snapshot");
} else {
this._log.error("Unknown encryption scheme: " + this.encryption);
throw 'close generator';
}
let gen = this._dav.PUT("bookmarks-snapshot.json", data, cont);
resp = yield;
gen.close();
this._checkStatus(resp.status, "Could not upload snapshot.");
gen = this._dav.PUT("bookmarks-deltas.json", uneval([]), cont);
resp = yield;
gen.close();
this._checkStatus(resp.status, "Could not upload deltas.");
gen = this._dav.PUT("bookmarks-status.json",
uneval({GUID: this._snapshotGUID,
formatVersion: STORAGE_FORMAT_VERSION,
snapVersion: this._snapshotVersion,
maxVersion: this._snapshotVersion,
snapEncryption: this.encryption,
deltasEncryption: "none"}), cont);
resp = yield;
gen.close();
this._checkStatus(resp.status, "Could not upload status file.");
this._log.info("Full upload to server successful");
ret = true;
} catch (e) {
if (e != 'close generator')
this._log.error("Exception caught: " + e.message);
} finally {
generatorDone(this, onComplete, ret)
yield; // onComplete is responsible for closing the generator
}
this._log.warn("generator not properly closed");
},
_onLogin: function BSS__onLogin(success) {
this._loginGen.close();
this._loginGen = null;
@ -1477,6 +1579,7 @@ BookmarksSyncService.prototype = {
} else {
this._os.notifyObservers(null, "bookmarks-sync:login-error", "");
}
this._passphrase = null;
this._loggingIn = false;
},
@ -1655,7 +1758,7 @@ BookmarksSyncService.prototype = {
this._syncGen = this._doSync.async(this, callback);
},
login: function BSS_login(password) {
login: function BSS_login(password, passphrase) {
if (this._loggingIn) {
this._log.warn("Login requested, but already logging in");
return;
@ -1675,10 +1778,14 @@ BookmarksSyncService.prototype = {
this._os.notifyObservers(null, "bookmarks-sync:login-error", "");
return;
}
this._loggingIn = true;
// cache passphrase. if null, we'll try to get it from the pw manager
this._passphrase = passphrase;
this._dav.baseURL = this._serverURL + "user/" + this.userPath + "/";
this._log.info("Using server URL: " + this._dav.baseURL);
this._loggingIn = true;
let callback = bind2(this, this._onLogin);
this._loginGen = this._dav.login.async(this._dav, callback,
this.username, password);
@ -1687,6 +1794,7 @@ BookmarksSyncService.prototype = {
logout: function BSS_logout() {
this._log.info("Logging out");
this._dav.logout();
this._passphrase = null;
this._os.notifyObservers(null, "bookmarks-sync:logout", "");
},

Просмотреть файл

@ -53,10 +53,22 @@ interface IBookmarksSyncService : nsISupports
readonly attribute AString currentUser;
/**
* Log into the server. Pre-requisite for sync().
* password is optional when the password is saved in the password manager
* Encryption algorithm to use. May be 'xxxtea' or 'none'.
*/
void login(in AString password);
attribute AString enryption;
/**
* The stored encryption passphrase
*/
attribute AString passphrase;
/**
* Log into the server. Pre-requisite for sync().
* password and/or passphrase may be null, and then they will be
* fetched from the password manager. If they are not given or
* found, login will fail.
*/
void login(in AString password, in AString passphrase);
/**
* Log out of the server.

Двоичные данные
services/sync/IBookmarksSyncService.xpt

Двоичный файл не отображается.

Просмотреть файл

@ -6,3 +6,4 @@ pref("browser.places.sync.enabled", true);
pref("browser.places.sync.bookmarks", true);
pref("browser.places.sync.schedule", 1);
pref("browser.places.sync.lastsync", "0");
pref("browser.places.sync.encryption", "XXXTEA");