mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-24 18:55:30 +00:00
remove dav.js (gone) and remote.js (resources now in resource.js)
This commit is contained in:
parent
542bee77cd
commit
2ffe3b8d8f
@ -1,493 +0,0 @@
|
||||
/* ***** 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 Bookmarks Sync.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dan Mills <thunder@mozilla.com>
|
||||
*
|
||||
* 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 EXPORTED_SYMBOLS = ['DAV', 'DAVCollection'];
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://weave/log4moz.js");
|
||||
Cu.import("resource://weave/constants.js");
|
||||
Cu.import("resource://weave/util.js");
|
||||
Cu.import("resource://weave/identity.js");
|
||||
Cu.import("resource://weave/async.js");
|
||||
|
||||
Function.prototype.async = Async.sugar;
|
||||
|
||||
Utils.lazy(this, 'DAV', DAVCollection);
|
||||
|
||||
let DAVLocks = {default: null};
|
||||
|
||||
/*
|
||||
* DAV object
|
||||
* Abstracts the raw DAV commands
|
||||
*/
|
||||
|
||||
function DAVCollection(baseURL, defaultPrefix) {
|
||||
this.baseURL = baseURL;
|
||||
this.defaultPrefix = defaultPrefix;
|
||||
this._identity = 'DAV:default';
|
||||
this._log = Log4Moz.Service.getLogger("Service.DAV");
|
||||
this._log.level =
|
||||
Log4Moz.Level[Utils.prefs.getCharPref("log.logger.service.dav")];
|
||||
}
|
||||
DAVCollection.prototype = {
|
||||
|
||||
__dp: null,
|
||||
get _dp() {
|
||||
if (!this.__dp)
|
||||
this.__dp = Cc["@mozilla.org/xmlextras/domparser;1"].
|
||||
createInstance(Ci.nsIDOMParser);
|
||||
return this.__dp;
|
||||
},
|
||||
|
||||
get identity() { return this._identity; },
|
||||
set identity(value) { this._identity = value; },
|
||||
|
||||
get baseURL() {
|
||||
return this._baseURL;
|
||||
},
|
||||
set baseURL(value) {
|
||||
if (value && value[value.length-1] != '/')
|
||||
value = value + '/';
|
||||
this._baseURL = value;
|
||||
},
|
||||
|
||||
get defaultPrefix() {
|
||||
return this._defaultPrefix;
|
||||
},
|
||||
set defaultPrefix(value) {
|
||||
if (value && value[value.length-1] != '/')
|
||||
value = value + '/';
|
||||
if (value && value[0] == '/')
|
||||
value = value.slice(1);
|
||||
if (!value)
|
||||
value = '';
|
||||
this._defaultPrefix = value;
|
||||
},
|
||||
|
||||
get locked() {
|
||||
return !this._allowLock || (DAVLocks['default'] &&
|
||||
DAVLocks['default'].token);
|
||||
},
|
||||
|
||||
_allowLock: true,
|
||||
get allowLock() this._allowLock,
|
||||
set allowLock(value) {
|
||||
this._allowLock = value;
|
||||
},
|
||||
|
||||
_makeRequest: function DC__makeRequest(op, path, headers, data) {
|
||||
let self = yield;
|
||||
let ret;
|
||||
|
||||
this._log.debug(op + " request for " + (path? path : 'root folder'));
|
||||
|
||||
if (!path || path[0] != '/')
|
||||
path = this._defaultPrefix + path; // if relative: prepend default prefix
|
||||
else
|
||||
path = path.slice(1); // if absolute: remove leading slash
|
||||
// path at this point should have no leading slash.
|
||||
|
||||
if (this._lastProgress)
|
||||
throw "Request already in progress";
|
||||
else
|
||||
this._lastProgress = Date.now();
|
||||
|
||||
let xhrCb = self.cb;
|
||||
let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
||||
createInstance(Ci.nsIXMLHttpRequest);
|
||||
|
||||
// check for stalled connections
|
||||
let listener = new Utils.EventListener(this._timeoutCb(request, xhrCb));
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
timer.initWithCallback(listener, CONNECTION_TIMEOUT,
|
||||
timer.TYPE_REPEATING_SLACK);
|
||||
|
||||
request.onload = xhrCb;
|
||||
request.onerror = xhrCb;
|
||||
request.onprogress =Utils.bind2(this, this._onProgress);
|
||||
request.mozBackgroundRequest = true;
|
||||
request.open(op, this._baseURL + path, true);
|
||||
|
||||
|
||||
// Force cache validation
|
||||
let channel = request.channel;
|
||||
channel = channel.QueryInterface(Ci.nsIRequest);
|
||||
let loadFlags = channel.loadFlags;
|
||||
loadFlags |= Ci.nsIRequest.VALIDATE_ALWAYS;
|
||||
channel.loadFlags = loadFlags;
|
||||
|
||||
let key;
|
||||
for (key in headers) {
|
||||
if (key == 'Authorization')
|
||||
this._log.trace("HTTP Header " + key + ": ***** (suppressed)");
|
||||
else
|
||||
this._log.trace("HTTP Header " + key + ": " + headers[key]);
|
||||
request.setRequestHeader(key, headers[key]);
|
||||
}
|
||||
|
||||
let event = yield request.send(data);
|
||||
|
||||
timer.cancel();
|
||||
this._lastProgress = null;
|
||||
self.done(event.target);
|
||||
},
|
||||
|
||||
_onProgress: function DC__onProgress(event) {
|
||||
this._lastProgress = Date.now();
|
||||
},
|
||||
|
||||
_timeoutCb: function DC__timeoutCb(request, callback) {
|
||||
return function() {
|
||||
if (Date.now() - this._lastProgress > CONNECTION_TIMEOUT) {
|
||||
this._log.warn("Connection timed out");
|
||||
request.abort();
|
||||
callback({target:{status:-1}});
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
get _defaultHeaders() {
|
||||
let h = {'Content-type': 'text/plain'},
|
||||
id = ID.get(this.identity),
|
||||
lock = DAVLocks['default'];
|
||||
if (id)
|
||||
h['Authorization'] = 'Basic ' + btoa(id.username + ":" + id.password);
|
||||
if (lock)
|
||||
h['If'] = "<" + lock.URL + "> (<" + lock.token + ">)";
|
||||
return h;
|
||||
},
|
||||
|
||||
// mkdir -p
|
||||
_mkcol: function DC__mkcol(path) {
|
||||
let self = yield;
|
||||
let ok = true;
|
||||
|
||||
try {
|
||||
let components = path.split('/');
|
||||
let path2 = '';
|
||||
|
||||
for (let i = 0; i < components.length; i++) {
|
||||
|
||||
// trailing slashes will cause an empty path component at the end
|
||||
if (components[i] == '')
|
||||
continue;
|
||||
|
||||
path2 = path2 + components[i];
|
||||
|
||||
// check if it exists first
|
||||
this._makeRequest.async(this, self.cb, "GET", path2 + "/", this._defaultHeaders);
|
||||
let ret = yield;
|
||||
if (ret.status != 404) {
|
||||
this._log.trace("Skipping creation of path " + path2 +
|
||||
" (got status " + ret.status + ")");
|
||||
} else {
|
||||
this._log.debug("Creating path: " + path2);
|
||||
this._makeRequest.async(this, self.cb, "MKCOL", path2,
|
||||
this._defaultHeaders);
|
||||
ret = yield;
|
||||
|
||||
if (ret.status != 201) {
|
||||
this._log.debug(ret.responseText);
|
||||
throw 'request failed: ' + ret.status;
|
||||
}
|
||||
}
|
||||
|
||||
// add slash *after* the request, trailing slashes cause a 412!
|
||||
path2 = path2 + "/";
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
this._log.error("Could not create directory on server");
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e) +
|
||||
" - " + (e.location? e.location : ""));
|
||||
ok = false;
|
||||
}
|
||||
|
||||
self.done(ok);
|
||||
},
|
||||
|
||||
GET: function DC_GET(path, onComplete) {
|
||||
return this._makeRequest.async(this, onComplete, "GET", path,
|
||||
this._defaultHeaders);
|
||||
},
|
||||
|
||||
POST: function DC_POST(path, data, onComplete) {
|
||||
return this._makeRequest.async(this, onComplete, "POST", path,
|
||||
this._defaultHeaders, data);
|
||||
},
|
||||
|
||||
formPost: function DC_formPOST(path, data, onComplete) {
|
||||
let headers = {'Content-type': 'application/x-www-form-urlencoded'};
|
||||
headers.__proto__ = this._defaultHeaders;
|
||||
|
||||
return this._makeRequest.async(this, onComplete, "POST", path,
|
||||
headers, data);
|
||||
},
|
||||
|
||||
PUT: function DC_PUT(path, data, onComplete) {
|
||||
return this._makeRequest.async(this, onComplete, "PUT", path,
|
||||
this._defaultHeaders, data);
|
||||
},
|
||||
|
||||
DELETE: function DC_DELETE(path, onComplete) {
|
||||
return this._makeRequest.async(this, onComplete, "DELETE", path,
|
||||
this._defaultHeaders);
|
||||
},
|
||||
|
||||
MKCOL: function DC_MKCOL(path, onComplete) {
|
||||
return this._mkcol.async(this, onComplete, path);
|
||||
},
|
||||
|
||||
PROPFIND: function DC_PROPFIND(path, data, onComplete) {
|
||||
let headers = {'Content-type': 'text/xml; charset="utf-8"',
|
||||
'Depth': '0'};
|
||||
headers.__proto__ = this._defaultHeaders;
|
||||
return this._makeRequest.async(this, onComplete, "PROPFIND", path,
|
||||
headers, data);
|
||||
},
|
||||
|
||||
LOCK: function DC_LOCK(path, data, onComplete) {
|
||||
let headers = {'Content-type': 'text/xml; charset="utf-8"',
|
||||
'Depth': 'infinity',
|
||||
'Timeout': 'Second-600'};
|
||||
headers.__proto__ = this._defaultHeaders;
|
||||
return this._makeRequest.async(this, onComplete, "LOCK", path, headers, data);
|
||||
},
|
||||
|
||||
UNLOCK: function DC_UNLOCK(path, onComplete) {
|
||||
let headers = {'Lock-Token': '<' + DAVLocks['default'].token + '>'};
|
||||
headers.__proto__ = this._defaultHeaders;
|
||||
return this._makeRequest.async(this, onComplete, "UNLOCK", path, headers);
|
||||
},
|
||||
|
||||
// Get all files
|
||||
listFiles: function DC_listFiles(path) {
|
||||
let self = yield;
|
||||
|
||||
if (!path)
|
||||
path = "";
|
||||
|
||||
let headers = {'Content-type': 'text/xml; charset="utf-8"',
|
||||
'Depth': '1'};
|
||||
headers.__proto__ = this._defaultHeaders;
|
||||
|
||||
this._makeRequest.async(this, self.cb, "PROPFIND", path, headers,
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
|
||||
"<D:propfind xmlns:D='DAV:'><D:prop/></D:propfind>");
|
||||
let resp = yield;
|
||||
Utils.ensureStatus(resp.status, "propfind failed");
|
||||
|
||||
let ret = [];
|
||||
try {
|
||||
let elts = Utils.xpath(resp.responseXML, '//D:href');
|
||||
// FIXME: shouldn't depend on the first one being the root
|
||||
let root = elts.iterateNext();
|
||||
root = root.textContent;
|
||||
let elt;
|
||||
while (elt = elts.iterateNext())
|
||||
ret.push(elt.textContent.replace(root, ''));
|
||||
} catch (e) {}
|
||||
|
||||
self.done(ret);
|
||||
},
|
||||
|
||||
// Login / Logout
|
||||
|
||||
checkLogin: function DC_checkLogin(username, password) {
|
||||
let self = yield;
|
||||
|
||||
this._log.debug("checkLogin called for user " + username);
|
||||
|
||||
let headers = {
|
||||
'Content-type' : 'text/plain',
|
||||
'Authorization' : 'Basic ' + btoa(username + ":" + password)
|
||||
};
|
||||
let lock = DAVLocks['default'];
|
||||
if (lock)
|
||||
headers['If'] = "<" + lock.URL + "> (<" + lock.token + ">)";
|
||||
|
||||
// Make a call to make sure it's working
|
||||
this._makeRequest.async(this, self.cb, "GET", "", headers);
|
||||
let resp = yield;
|
||||
|
||||
this._log.debug("checkLogin got response status " + resp.status);
|
||||
self.done(resp.status);
|
||||
},
|
||||
|
||||
// Locking
|
||||
|
||||
_getActiveLock: function DC__getActiveLock() {
|
||||
let self = yield;
|
||||
let ret = null;
|
||||
|
||||
this._log.debug("Getting active lock token");
|
||||
this.PROPFIND("lock",
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
|
||||
"<D:propfind xmlns:D='DAV:'>" +
|
||||
" <D:prop><D:lockdiscovery/></D:prop>" +
|
||||
"</D:propfind>", self.cb);
|
||||
let resp = yield;
|
||||
|
||||
if (resp.status < 200 || resp.status >= 300) {
|
||||
self.done(false);
|
||||
yield;
|
||||
}
|
||||
|
||||
let tokens = Utils.xpath(resp.responseXML, '//D:locktoken/D:href');
|
||||
let token = tokens.iterateNext();
|
||||
if (token)
|
||||
ret = token.textContent;
|
||||
|
||||
if (ret)
|
||||
this._log.trace("Found an active lock token");
|
||||
else
|
||||
this._log.trace("No active lock token found");
|
||||
self.done({URL: this._baseURL, token: ret});
|
||||
},
|
||||
|
||||
lock: function DC_lock() {
|
||||
let self = yield;
|
||||
let resp;
|
||||
|
||||
try {
|
||||
this._log.trace("Acquiring lock");
|
||||
|
||||
if (this.locked) {
|
||||
this._log.debug("Lock called, but we are already locked");
|
||||
return;
|
||||
}
|
||||
this._allowLock = false;
|
||||
|
||||
resp = yield this.LOCK("lock",
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" +
|
||||
"<D:lockinfo xmlns:D=\"DAV:\">\n" +
|
||||
" <D:locktype><D:write/></D:locktype>\n" +
|
||||
" <D:lockscope><D:exclusive/></D:lockscope>\n" +
|
||||
"</D:lockinfo>", self.cb);
|
||||
if (!Utils.checkStatus(resp.status))
|
||||
return;
|
||||
|
||||
let tokens = Utils.xpath(resp.responseXML, '//D:locktoken/D:href');
|
||||
let token = tokens.iterateNext();
|
||||
if (token) {
|
||||
DAVLocks['default'] = {
|
||||
URL: this._baseURL,
|
||||
token: token.textContent
|
||||
};
|
||||
}
|
||||
|
||||
if (DAVLocks['default']) {
|
||||
this._log.trace("Lock acquired");
|
||||
self.done(DAVLocks['default']);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
this._log.error("Could not acquire lock");
|
||||
if (resp.responseText)
|
||||
this._log.error("Server response to LOCK:\n" + resp.responseText);
|
||||
throw e;
|
||||
|
||||
} finally {
|
||||
this._allowLock = true;
|
||||
}
|
||||
},
|
||||
|
||||
unlock: function DC_unlock() {
|
||||
let self = yield;
|
||||
|
||||
this._log.trace("Releasing lock");
|
||||
|
||||
if (!this.locked) {
|
||||
this._log.debug("Unlock called, but we don't hold a token right now");
|
||||
self.done(true);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let resp = yield this.UNLOCK("lock", self.cb);
|
||||
|
||||
if (Utils.checkStatus(resp.status)) {
|
||||
this._log.trace("Lock released");
|
||||
self.done(true);
|
||||
} else {
|
||||
this._log.trace("Failed to release lock");
|
||||
self.done(false);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
throw e;
|
||||
|
||||
} finally {
|
||||
// Do this unconditionally, since code that calls unlock() doesn't
|
||||
// really have much of an option if unlock fails. The only thing
|
||||
// to do is wait for it to time out (and hope it didn't really
|
||||
// fail)
|
||||
if (DAVLocks['default'])
|
||||
delete DAVLocks['default'];
|
||||
}
|
||||
},
|
||||
|
||||
forceUnlock: function DC_forceUnlock() {
|
||||
let self = yield;
|
||||
let unlocked = true;
|
||||
|
||||
this._log.debug("Forcibly releasing any server locks");
|
||||
|
||||
this._getActiveLock.async(this, self.cb);
|
||||
DAVLocks['default'] = yield;
|
||||
|
||||
if (!DAVLocks['default']) {
|
||||
this._log.debug("No server lock found");
|
||||
self.done(true);
|
||||
yield;
|
||||
}
|
||||
|
||||
this._log.trace("Server lock found, unlocking");
|
||||
this.unlock.async(this, self.cb);
|
||||
unlocked = yield;
|
||||
|
||||
if (unlocked)
|
||||
this._log.trace("Lock released");
|
||||
else
|
||||
this._log.trace("No lock released");
|
||||
self.done(unlocked);
|
||||
}
|
||||
};
|
@ -1,724 +0,0 @@
|
||||
/* ***** 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 Bookmarks Sync.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dan Mills <thunder@mozilla.com>
|
||||
*
|
||||
* 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 EXPORTED_SYMBOLS = ['Resource', 'JsonFilter', 'CryptoFilter',
|
||||
'Keychain', 'RemoteStore'];
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://weave/log4moz.js");
|
||||
Cu.import("resource://weave/constants.js");
|
||||
Cu.import("resource://weave/util.js");
|
||||
Cu.import("resource://weave/crypto.js");
|
||||
Cu.import("resource://weave/async.js");
|
||||
Cu.import("resource://weave/identity.js");
|
||||
Cu.import("resource://weave/dav.js");
|
||||
Cu.import("resource://weave/stores.js");
|
||||
|
||||
Function.prototype.async = Async.sugar;
|
||||
|
||||
|
||||
function RequestException(resource, action, request) {
|
||||
this._resource = resource;
|
||||
this._action = action;
|
||||
this._request = request;
|
||||
this.location = Components.stack.caller;
|
||||
}
|
||||
RequestException.prototype = {
|
||||
get resource() { return this._resource; },
|
||||
get action() { return this._action; },
|
||||
get request() { return this._request; },
|
||||
get status() { return this._request.status; },
|
||||
toString: function ReqEx_toString() {
|
||||
return "Could not " + this._action + " resource " + this._resource.path +
|
||||
" (" + this._request.status + ")";
|
||||
}
|
||||
};
|
||||
|
||||
function Resource(path) {
|
||||
this._init(path);
|
||||
}
|
||||
Resource.prototype = {
|
||||
get identity() { return this._identity; },
|
||||
set identity(value) { this._identity = value; },
|
||||
|
||||
get dav() { return this._dav; },
|
||||
set dav(value) { this._dav = value; },
|
||||
|
||||
get path() { return this._path; },
|
||||
set path(value) {
|
||||
this._dirty = true;
|
||||
this._path = value;
|
||||
},
|
||||
|
||||
get data() { return this._data; },
|
||||
set data(value) {
|
||||
this._dirty = true;
|
||||
this._data = value;
|
||||
},
|
||||
|
||||
__os: null,
|
||||
get _os() {
|
||||
if (!this.__os)
|
||||
this.__os = Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(Ci.nsIObserverService);
|
||||
return this.__os;
|
||||
},
|
||||
|
||||
get lastRequest() { return this._lastRequest; },
|
||||
get downloaded() { return this._downloaded; },
|
||||
get dirty() { return this._dirty; },
|
||||
|
||||
pushFilter: function Res_pushFilter(filter) {
|
||||
this._filters.push(filter);
|
||||
},
|
||||
|
||||
popFilter: function Res_popFilter() {
|
||||
return this._filters.pop();
|
||||
},
|
||||
|
||||
clearFilters: function Res_clearFilters() {
|
||||
this._filters = [];
|
||||
},
|
||||
|
||||
_init: function Res__init(path) {
|
||||
this._identity = null; // unused
|
||||
this._dav = null; // unused
|
||||
this._path = path;
|
||||
this._data = null;
|
||||
this._downloaded = false;
|
||||
this._dirty = false;
|
||||
this._filters = [];
|
||||
this._lastRequest = null;
|
||||
this._log = Log4Moz.Service.getLogger("Service.Resource");
|
||||
},
|
||||
|
||||
// note: this is unused, and it's not clear whether it's useful or not
|
||||
_sync: function Res__sync() {
|
||||
let self = yield;
|
||||
let ret;
|
||||
|
||||
// If we've set the locally stored value, upload it. If we
|
||||
// haven't, and we haven't yet downloaded this resource, then get
|
||||
// it. Otherwise do nothing (don't try to get it every time)
|
||||
|
||||
if (this.dirty) {
|
||||
this.put(self.cb);
|
||||
ret = yield;
|
||||
|
||||
} else if (!this.downloaded) {
|
||||
this.get(self.cb);
|
||||
ret = yield;
|
||||
}
|
||||
|
||||
self.done(ret);
|
||||
},
|
||||
sync: function Res_sync(onComplete) {
|
||||
this._sync.async(this, onComplete);
|
||||
},
|
||||
|
||||
_request: function Res__request(action, data) {
|
||||
let self = yield;
|
||||
let listener, timer;
|
||||
let iter = 0;
|
||||
|
||||
if ("PUT" == action) {
|
||||
for each (let filter in this._filters) {
|
||||
data = yield filter.beforePUT.async(filter, self.cb, data);
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
switch (action) {
|
||||
case "GET":
|
||||
DAV.GET(this.path, self.cb);
|
||||
break;
|
||||
case "PUT":
|
||||
DAV.PUT(this.path, data, self.cb);
|
||||
break;
|
||||
case "DELETE":
|
||||
DAV.DELETE(this.path, self.cb);
|
||||
break;
|
||||
default:
|
||||
throw "Unknown request action for Resource";
|
||||
}
|
||||
this._lastRequest = yield;
|
||||
|
||||
if (action == "DELETE" &&
|
||||
Utils.checkStatus(this._lastRequest.status, null, [[200,300],404])) {
|
||||
this._dirty = false;
|
||||
this._data = null;
|
||||
break;
|
||||
|
||||
} else if (Utils.checkStatus(this._lastRequest.status)) {
|
||||
this._log.debug(action + " request successful");
|
||||
this._dirty = false;
|
||||
if (action == "GET")
|
||||
this._data = this._lastRequest.responseText;
|
||||
//else if (action == "PUT")
|
||||
// this._data = data; // wrong! (because of filters)
|
||||
break;
|
||||
|
||||
} else if (action == "GET" && this._lastRequest.status == 404) {
|
||||
throw new RequestException(this, action, this._lastRequest);
|
||||
|
||||
} else if (iter >= 10) {
|
||||
// iter too big? bail
|
||||
throw new RequestException(this, action, this._lastRequest);
|
||||
|
||||
} else {
|
||||
// wait for a bit and try again
|
||||
if (!timer) {
|
||||
listener = new Utils.EventListener(self.cb);
|
||||
timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
}
|
||||
yield timer.initWithCallback(listener, iter * iter * 1000,
|
||||
timer.TYPE_ONE_SHOT);
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
if ("GET" == action) {
|
||||
let filters = this._filters.slice(); // reverse() mutates, so we copy
|
||||
for each (let filter in filters.reverse()) {
|
||||
this._data = yield filter.afterGET.async(filter, self.cb, this._data);
|
||||
}
|
||||
}
|
||||
|
||||
self.done(this._data);
|
||||
},
|
||||
|
||||
get: function Res_get(onComplete) {
|
||||
this._request.async(this, onComplete, "GET");
|
||||
},
|
||||
|
||||
put: function Res_put(onComplete, data) {
|
||||
if ("undefined" == typeof(data))
|
||||
data = this._data;
|
||||
this._request.async(this, onComplete, "PUT", data);
|
||||
},
|
||||
|
||||
delete: function Res_delete(onComplete) {
|
||||
this._request.async(this, onComplete, "DELETE");
|
||||
}
|
||||
};
|
||||
|
||||
function ResourceSet(basePath) {
|
||||
this._init(basePath);
|
||||
}
|
||||
ResourceSet.prototype = {
|
||||
__proto__: new Resource(),
|
||||
_init: function ResSet__init(basePath) {
|
||||
this.__proto__.__proto__._init.call(this);
|
||||
this._basePath = basePath;
|
||||
this._log = Log4Moz.Service.getLogger("Service.ResourceSet");
|
||||
},
|
||||
|
||||
_hack: function ResSet__hack(action, id, data) {
|
||||
let self = yield;
|
||||
let savedData = this._data;
|
||||
|
||||
if ("PUT" == action)
|
||||
this._data = data;
|
||||
|
||||
this._path = this._basePath + id;
|
||||
yield this._request.async(this, self.cb, action, data);
|
||||
|
||||
let newData = this._data;
|
||||
this._data = savedData;
|
||||
if (this._data == null)
|
||||
this._data = {};
|
||||
this._data[id] = newData;
|
||||
|
||||
self.done(this._data[id]);
|
||||
},
|
||||
|
||||
get: function ResSet_get(onComplete, id) {
|
||||
this._hack.async(this, onComplete, "GET", id);
|
||||
},
|
||||
|
||||
put: function ResSet_put(onComplete, id, data) {
|
||||
this._hack.async(this, onComplete, "PUT", id, data);
|
||||
},
|
||||
|
||||
delete: function ResSet_delete(onComplete, id) {
|
||||
this._hack.async(this, onComplete, "DELETE", id);
|
||||
}
|
||||
};
|
||||
|
||||
function ResourceFilter() {
|
||||
this._log = Log4Moz.Service.getLogger("Service.ResourceFilter");
|
||||
}
|
||||
ResourceFilter.prototype = {
|
||||
beforePUT: function ResFilter_beforePUT(data) {
|
||||
let self = yield;
|
||||
this._log.debug("Doing absolutely nothing")
|
||||
self.done(data);
|
||||
},
|
||||
afterGET: function ResFilter_afterGET(data) {
|
||||
let self = yield;
|
||||
this._log.debug("Doing absolutely nothing")
|
||||
self.done(data);
|
||||
}
|
||||
};
|
||||
|
||||
function JsonFilter() {
|
||||
this._log = Log4Moz.Service.getLogger("Service.JsonFilter");
|
||||
}
|
||||
JsonFilter.prototype = {
|
||||
__proto__: new ResourceFilter(),
|
||||
|
||||
__os: null,
|
||||
get _os() {
|
||||
if (!this.__os)
|
||||
this.__os = Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(Ci.nsIObserverService);
|
||||
return this.__os;
|
||||
},
|
||||
|
||||
get _json() {
|
||||
let json = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
|
||||
this.__defineGetter__("_json", function() json);
|
||||
return this._json;
|
||||
},
|
||||
|
||||
beforePUT: function JsonFilter_beforePUT(data) {
|
||||
let self = yield;
|
||||
this._log.debug("Encoding data as JSON");
|
||||
this._os.notifyObservers(null, "weave:service:sync:status", "stats.encoding-json");
|
||||
self.done(this._json.encode(data));
|
||||
},
|
||||
|
||||
afterGET: function JsonFilter_afterGET(data) {
|
||||
let self = yield;
|
||||
this._log.debug("Decoding JSON data");
|
||||
this._os.notifyObservers(null, "weave:service:sync:status", "stats.decoding-json");
|
||||
self.done(this._json.decode(data));
|
||||
}
|
||||
};
|
||||
|
||||
function CryptoFilter(identity) {
|
||||
this._identity = identity;
|
||||
this._log = Log4Moz.Service.getLogger("Service.CryptoFilter");
|
||||
}
|
||||
CryptoFilter.prototype = {
|
||||
__proto__: new ResourceFilter(),
|
||||
|
||||
get _os() {
|
||||
let os = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
this.__defineGetter__("_os", function() os);
|
||||
return os;
|
||||
},
|
||||
|
||||
beforePUT: function CryptoFilter_beforePUT(data) {
|
||||
let self = yield;
|
||||
this._log.debug("Encrypting data");
|
||||
this._os.notifyObservers(null, "weave:service:sync:status", "status.encrypting");
|
||||
let ret = yield Crypto.encryptData.async(Crypto, self.cb, data, this._identity);
|
||||
self.done(ret);
|
||||
},
|
||||
|
||||
afterGET: function CryptoFilter_afterGET(data) {
|
||||
let self = yield;
|
||||
this._log.debug("Decrypting data");
|
||||
this._os.notifyObservers(null, "weave:service:sync:status", "status.decrypting");
|
||||
let ret = yield Crypto.decryptData.async(Crypto, self.cb, data, this._identity);
|
||||
self.done(ret);
|
||||
}
|
||||
};
|
||||
|
||||
function Keychain(prefix) {
|
||||
this._init(prefix);
|
||||
}
|
||||
Keychain.prototype = {
|
||||
__proto__: new Resource(),
|
||||
_init: function Keychain__init(prefix) {
|
||||
this.__proto__.__proto__._init.call(this, prefix + "keys.json");
|
||||
this.pushFilter(new JsonFilter());
|
||||
},
|
||||
_initialize: function Keychain__initialize(identity) {
|
||||
let self = yield;
|
||||
let wrappedSymkey;
|
||||
|
||||
if ("none" != Utils.prefs.getCharPref("encryption")) {
|
||||
this._os.notifyObservers(null, "weave:service:sync:status", "status.generating-random-key");
|
||||
|
||||
yield Crypto.randomKeyGen.async(Crypto, self.cb, identity);
|
||||
|
||||
// Wrap (encrypt) this key with the user's public key.
|
||||
let idRSA = ID.get('WeaveCryptoID');
|
||||
this._os.notifyObservers(null, "weave:service:sync:status", "status.encrypting-key");
|
||||
wrappedSymkey = yield Crypto.wrapKey.async(Crypto, self.cb,
|
||||
identity.bulkKey, idRSA);
|
||||
}
|
||||
|
||||
let keys = {ring: {}, bulkIV: identity.bulkIV};
|
||||
this._os.notifyObservers(null, "weave:service:sync:status", "status.uploading-key");
|
||||
keys.ring[identity.username] = wrappedSymkey;
|
||||
yield this.put(self.cb, keys);
|
||||
},
|
||||
initialize: function Keychain_initialize(onComplete, identity) {
|
||||
this._initialize.async(this, onComplete, identity);
|
||||
},
|
||||
_getKeyAndIV: function Keychain__getKeyAndIV(identity) {
|
||||
let self = yield;
|
||||
|
||||
this._os.notifyObservers(null, "weave:service:sync:status", "status.downloading-keyring");
|
||||
|
||||
yield this.get(self.cb);
|
||||
|
||||
if (!this.data || !this.data.ring || !this.data.ring[identity.username])
|
||||
throw "Keyring does not contain a key for this user";
|
||||
|
||||
// Unwrap (decrypt) the key with the user's private key.
|
||||
this._os.notifyObservers(null, "weave:service:sync:status", "status.decrypting-key");
|
||||
let idRSA = ID.get('WeaveCryptoID');
|
||||
let symkey = yield Crypto.unwrapKey.async(Crypto, self.cb,
|
||||
this.data.ring[identity.username], idRSA);
|
||||
let iv = this.data.bulkIV;
|
||||
|
||||
identity.bulkKey = symkey;
|
||||
identity.bulkIV = iv;
|
||||
},
|
||||
_setKey: function KeyChain__setKey(bulkID, newID) {
|
||||
/* FIXME!: It's possible that the keyring is changed on the server
|
||||
after we do a GET. Then we're just uploading this new local keyring,
|
||||
thereby losing any changes made on the server keyring since this GET.
|
||||
|
||||
Also, if this.data was not instantiated properly (i.e. you're
|
||||
using KeyChain directly instead of getting it from the engine),
|
||||
you run the risk of wiping the server-side keychain.
|
||||
*/
|
||||
let self = yield;
|
||||
|
||||
this.get(self.cb);
|
||||
yield;
|
||||
|
||||
let wrappedKey = yield Crypto.wrapKey.async(Crypto, self.cb,
|
||||
bulkID.bulkKey, newID);
|
||||
this.data.ring[newID.username] = wrappedKey;
|
||||
this.put(self.cb, this.data);
|
||||
yield;
|
||||
},
|
||||
getKeyAndIV: function Keychain_getKeyAndIV(onComplete, identity) {
|
||||
this._getKeyAndIV.async(this, onComplete, identity);
|
||||
},
|
||||
setKey: function Keychain_setKey(onComplete, bulkID, newID) {
|
||||
this._setKey.async(this, onComplete, bulkID, newID);
|
||||
}
|
||||
};
|
||||
|
||||
function RemoteStore(engine) {
|
||||
this._engine = engine;
|
||||
this._log = Log4Moz.Service.getLogger("Service.RemoteStore");
|
||||
}
|
||||
RemoteStore.prototype = {
|
||||
get serverPrefix() this._engine.serverPrefix,
|
||||
get engineId() this._engine.engineId,
|
||||
|
||||
__os: null,
|
||||
get _os() {
|
||||
if (!this.__os)
|
||||
this.__os = Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(Ci.nsIObserverService);
|
||||
return this.__os;
|
||||
},
|
||||
|
||||
get status() {
|
||||
let status = new Resource(this.serverPrefix + "status.json");
|
||||
status.pushFilter(new JsonFilter());
|
||||
this.__defineGetter__("status", function() status);
|
||||
return status;
|
||||
},
|
||||
|
||||
get keys() {
|
||||
let keys = new Keychain(this.serverPrefix);
|
||||
this.__defineGetter__("keys", function() keys);
|
||||
return keys;
|
||||
},
|
||||
|
||||
get _snapshot() {
|
||||
let snapshot = new Resource(this.serverPrefix + "snapshot.json");
|
||||
snapshot.pushFilter(new JsonFilter());
|
||||
snapshot.pushFilter(new CryptoFilter(this._engine.engineId));
|
||||
this.__defineGetter__("_snapshot", function() snapshot);
|
||||
return snapshot;
|
||||
},
|
||||
|
||||
get _deltas() {
|
||||
let deltas = new ResourceSet(this.serverPrefix + "deltas/");
|
||||
deltas.pushFilter(new JsonFilter());
|
||||
deltas.pushFilter(new CryptoFilter(this._engine.engineId));
|
||||
this.__defineGetter__("_deltas", function() deltas);
|
||||
return deltas;
|
||||
},
|
||||
|
||||
_openSession: function RStore__openSession(lastSyncSnap) {
|
||||
let self = yield;
|
||||
|
||||
if (!this.serverPrefix || !this.engineId)
|
||||
throw "Cannot initialize RemoteStore: engine has no server prefix or crypto ID";
|
||||
|
||||
this.status.data = null;
|
||||
this.keys.data = null;
|
||||
this._snapshot.data = null;
|
||||
this._deltas.data = null;
|
||||
this._lastSyncSnap = lastSyncSnap;
|
||||
|
||||
let ret = yield DAV.MKCOL(this.serverPrefix + "deltas", self.cb);
|
||||
if (!ret)
|
||||
throw "Could not create remote folder";
|
||||
|
||||
this._log.debug("Downloading status file");
|
||||
this._os.notifyObservers(null, "weave:service:sync:status", "status.downloading-status");
|
||||
|
||||
yield this.status.get(self.cb);
|
||||
this._log.debug("Downloading status file... done");
|
||||
|
||||
// Bail out if the server has a newer format version than we can parse
|
||||
if (this.status.data.formatVersion > ENGINE_STORAGE_FORMAT_VERSION) {
|
||||
this._log.error("Server uses storage format v" +
|
||||
this.status.data.formatVersion +
|
||||
", this client understands up to v" +
|
||||
ENGINE_STORAGE_FORMAT_VERSION);
|
||||
throw "Incompatible remote store format";
|
||||
}
|
||||
|
||||
if (this.status.data.GUID != lastSyncSnap.GUID) {
|
||||
this._log.trace("Remote GUID: " + this.status.data.GUID);
|
||||
this._log.trace("Local GUID: " + lastSyncSnap.GUID);
|
||||
this._log.debug("Server wipe since last sync, resetting last sync snapshot");
|
||||
lastSyncSnap.wipe();
|
||||
lastSyncSnap.GUID = this.status.data.GUID;
|
||||
// yield this._store.resetGUIDs(self.cb); // XXX not sure if this is really needed (and it needs to be done from the engine if so)
|
||||
}
|
||||
|
||||
this._log.info("Last sync snapshot version: " + lastSyncSnap.version);
|
||||
this._log.info("Server maxVersion: " + this.status.data.maxVersion);
|
||||
|
||||
if ("none" != Utils.prefs.getCharPref("encryption"))
|
||||
yield this.keys.getKeyAndIV(self.cb, this.engineId);
|
||||
},
|
||||
openSession: function RStore_openSession(onComplete, lastSyncSnap) {
|
||||
this._openSession.async(this, onComplete, lastSyncSnap);
|
||||
},
|
||||
|
||||
closeSession: function RStore_closeSession() {
|
||||
this.status.data = null;
|
||||
this.keys.data = null;
|
||||
this._snapshot.data = null;
|
||||
this._deltas.data = null;
|
||||
this._lastSyncSnap = null;
|
||||
},
|
||||
|
||||
// Does a fresh upload of the given snapshot to a new store
|
||||
// FIXME: add 'metadata' arg here like appendDelta's
|
||||
_initialize: function RStore__initialize(snapshot) {
|
||||
let self = yield;
|
||||
|
||||
yield this.keys.initialize(self.cb, this.engineId);
|
||||
this._os.notifyObservers(null, "weave:service:sync:status", "status.uploading-snapshot");
|
||||
yield this._snapshot.put(self.cb, snapshot.data);
|
||||
|
||||
let c = 0;
|
||||
for (GUID in snapshot.data)
|
||||
c++;
|
||||
|
||||
this._os.notifyObservers(null, "weave:service:sync:status", "status.uploading-status");
|
||||
yield this.status.put(self.cb,
|
||||
{GUID: snapshot.GUID,
|
||||
formatVersion: ENGINE_STORAGE_FORMAT_VERSION,
|
||||
snapVersion: snapshot.version,
|
||||
maxVersion: snapshot.version,
|
||||
snapEncryption: Crypto.defaultAlgorithm,
|
||||
deltasEncryption: Crypto.defaultAlgorithm,
|
||||
itemCount: c});
|
||||
this._log.info("Full upload to server successful");
|
||||
},
|
||||
initialize: function RStore_initialize(onComplete, snapshot) {
|
||||
this._initialize.async(this, onComplete, snapshot);
|
||||
},
|
||||
|
||||
// Removes server files - you may want to run initialize() after this
|
||||
// FIXME: might want to do a PROPFIND instead (to catch all deltas in one go)
|
||||
_wipe: function Engine__wipe() {
|
||||
let self = yield;
|
||||
this._log.debug("Deleting remote store data");
|
||||
yield this.status.delete(self.cb);
|
||||
yield this.keys.delete(self.cb);
|
||||
yield this._snapshot.delete(self.cb);
|
||||
//yield this._deltas.delete(self.cb);
|
||||
this._log.debug("Server files deleted");
|
||||
},
|
||||
wipe: function Engine_wipe(onComplete) {
|
||||
this._wipe.async(this, onComplete)
|
||||
},
|
||||
|
||||
// Gets the latest server snapshot by downloading all server files
|
||||
// (snapshot + deltas)
|
||||
_getLatestFromScratch: function RStore__getLatestFromScratch() {
|
||||
let self = yield;
|
||||
|
||||
this._log.info("Downloading all server data from scratch");
|
||||
this._os.notifyObservers(null, "weave:service:sync:status", "status.downloading-snapshot");
|
||||
|
||||
let snap = new SnapshotStore();
|
||||
snap.data = yield this._snapshot.get(self.cb);
|
||||
|
||||
this._os.notifyObservers(null, "weave:service:sync:status", "status.downloading-deltas");
|
||||
let status = this.status.data;
|
||||
for (let id = status.snapVersion + 1; id <= status.maxVersion; id++) {
|
||||
let delta = yield this._deltas.get(self.cb, id);
|
||||
yield snap.applyCommands.async(snap, self.cb, delta);
|
||||
}
|
||||
|
||||
self.done(snap.data);
|
||||
},
|
||||
|
||||
// Gets the latest server snapshot by downloading only the necessary
|
||||
// deltas from the given snapshot (but may fall back to a full download)
|
||||
_getLatestFromSnap: function RStore__getLatestFromSnap() {
|
||||
let self = yield;
|
||||
let deltas, snap = new SnapshotStore();
|
||||
snap.version = this.status.data.maxVersion;
|
||||
|
||||
if (!this._lastSyncSnap ||
|
||||
this._lastSyncSnap.version < this.status.data.snapVersion) {
|
||||
this._log.trace("Getting latest from scratch (last sync snap too old)");
|
||||
snap.data = yield this._getLatestFromScratch.async(this, self.cb);
|
||||
self.done(snap.data);
|
||||
return;
|
||||
|
||||
} else if (this._lastSyncSnap.version >= this.status.data.snapVersion &&
|
||||
this._lastSyncSnap.version < this.status.data.maxVersion) {
|
||||
this._log.debug("Using last sync snapshot as starting point for server snapshot");
|
||||
snap.data = Utils.deepCopy(this._lastSyncSnap.data);
|
||||
this._log.info("Downloading server deltas");
|
||||
this._os.notifyObservers(null, "weave:service:sync:status", "status.downloading-deltas");
|
||||
deltas = [];
|
||||
let min = this._lastSyncSnap.version + 1;
|
||||
let max = this.status.data.maxVersion;
|
||||
for (let id = min; id <= max; id++) {
|
||||
let delta = yield this._deltas.get(self.cb, id);
|
||||
deltas.push(delta);
|
||||
}
|
||||
|
||||
} else if (this._lastSyncSnap.version == this.status.data.maxVersion) {
|
||||
this._log.debug("Using last sync snapshot as server snapshot (snap version == max version)");
|
||||
this._log.trace("Local snapshot version == server maxVersion");
|
||||
snap.data = Utils.deepCopy(this._lastSyncSnap.data);
|
||||
deltas = [];
|
||||
|
||||
} else { // this._lastSyncSnap.version > this.status.data.maxVersion
|
||||
this._log.error("Server snapshot is older than local snapshot");
|
||||
throw "Server snapshot is older than local snapshot";
|
||||
}
|
||||
|
||||
try {
|
||||
for (var i = 0; i < deltas.length; i++) {
|
||||
yield snap.applyCommands.async(snap, self.cb, deltas[i]);
|
||||
}
|
||||
} catch (e) {
|
||||
this._log.warn("Error applying remote deltas to saved snapshot, attempting a full download");
|
||||
this._log.debug("Exception: " + Utils.exceptionStr(e));
|
||||
this._log.trace("Stack:\n" + Utils.stackTrace(e));
|
||||
snap.data = yield this._getLatestFromScratch.async(this, self.cb);
|
||||
}
|
||||
|
||||
self.done(snap.data);
|
||||
},
|
||||
|
||||
// get the latest server snapshot. If a snapshot is given, try to
|
||||
// download only the necessary deltas to get to the latest
|
||||
_wrap: function RStore__wrap() {
|
||||
let self = yield;
|
||||
let ret = yield this._getLatestFromSnap.async(this, self.cb);
|
||||
self.done(ret);
|
||||
},
|
||||
wrap: function RStore_wrap(onComplete) {
|
||||
this._wrap.async(this, onComplete);
|
||||
},
|
||||
|
||||
// Adds a new set of changes (a delta) to this store
|
||||
_appendDelta: function RStore__appendDelta(snapshot, delta, metadata) {
|
||||
let self = yield;
|
||||
|
||||
if (metadata) {
|
||||
for (let key in metadata)
|
||||
this.status.data[key] = metadata[key];
|
||||
}
|
||||
|
||||
let c = 0;
|
||||
for (item in snapshot.data)
|
||||
c++;
|
||||
this.status.data.itemCount = c;
|
||||
|
||||
let id = ++this.status.data.maxVersion;
|
||||
|
||||
// upload the delta even if we upload a new snapshot, so other clients
|
||||
// can be spared of a full re-download
|
||||
this._os.notifyObservers(null, "weave:service:sync:status",
|
||||
"status.uploading-deltas");
|
||||
yield this._deltas.put(self.cb, id, delta);
|
||||
|
||||
// if we have more than KEEP_DELTAS, then upload a new snapshot
|
||||
// this allows us to remove old deltas
|
||||
if ((id - this.status.data.snapVersion) > KEEP_DELTAS) {
|
||||
this._os.notifyObservers(null, "weave:service:sync:status",
|
||||
"status.uploading-snapshot");
|
||||
yield this._snapshot.put(self.cb, snapshot.data);
|
||||
this.status.data.snapVersion = id;
|
||||
}
|
||||
|
||||
// XXX we could define another constant here
|
||||
// (e.g. KEEP_MAX_DELTAS) to define when to actually start
|
||||
// deleting deltas from the server. However, we can do this more
|
||||
// efficiently server-side
|
||||
|
||||
// finally, upload a new status file
|
||||
this._os.notifyObservers(null, "weave:service:sync:status",
|
||||
"status.uploading-status");
|
||||
yield this.status.put(self.cb);
|
||||
},
|
||||
appendDelta: function RStore_appendDelta(onComplete, snapshot, delta, metadata) {
|
||||
this._appendDelta.async(this, onComplete, snapshot, delta, metadata);
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user