remove dav.js (gone) and remote.js (resources now in resource.js)
authorDan Mills <thunder@mozilla.com>
Mon, 03 Nov 2008 14:38:34 -0800
changeset 45008 77e1ecde0bce679a57160867f87dae333e7c1483
parent 45007 66741433c68b8cb6a4e5433c09f35e509d775456
child 45009 27c03786692474758e27debb781e2b3ce40fafbb
push idunknown
push userunknown
push dateunknown
remove dav.js (gone) and remote.js (resources now in resource.js)
services/sync/modules/dav.js
services/sync/modules/remote.js
deleted file mode 100644
--- a/services/sync/modules/dav.js
+++ /dev/null
@@ -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);
-  }
-};
deleted file mode 100644
--- a/services/sync/modules/remote.js
+++ /dev/null
@@ -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);
-  }
-};