Permalink
Browse files

fixed sending Etag header in PUT response, fixed VCard update handlin…

…g and group VCards
  • Loading branch information...
1 parent 0558ed8 commit dd9b53e034e7dbb7afd82bab135c816ba02f4613 @mikedeboer committed Feb 20, 2013
View
4 lib/CardDAV/card.js
@@ -88,8 +88,10 @@ var jsCardDAV_Card = module.exports = jsDAV_File.extend(jsCardDAV_iCard, jsDAVAC
* @param string cardData
* @return string|null
*/
- put: function(cardData, callback) {
+ put: function(cardData, enc, callback) {
var self = this;
+ if (Buffer.isBuffer(cardData))
+ cardData = cardData.toString("utf8");
this.carddavBackend.updateCard(this.addressBookInfo.id, this.cardData.uri, cardData, function(err, etag) {
if (err)
return callback(err);
View
36 lib/CardDAV/plugin.js
@@ -194,13 +194,13 @@ var jsCardDAV_Plugin = module.exports = jsDAV_Plugin.extend({
if (node.hasFeature(jsCardDAV_UserAddressBooks)) {
var meCardProp = "{http://calendarserver.org/ns/}me-card";
if (requestedProperties[meCardProp]) {
- self.handler.getProperties(node.getOwner(), ["{http://sabredav.org/ns}vcard-url"], function(err, props) {
+ self.handler.getProperties(node.getOwner(), ["{http://ajax.org/2005/aml}vcard-url"], function(err, props) {
if (err)
return e.next(err);
- if (props["{http://sabredav.org/ns}vcard-url"]) {
+ if (props["{http://ajax.org/2005/aml}vcard-url"]) {
returnedProperties["200"][meCardProp] = jsDAV_Property_Href.new(
- props["{http://sabredav.org/ns}vcard-url"]
+ props["{http://ajax.org/2005/aml}vcard-url"]
);
delete requestedProperties[meCardProp];
}
@@ -244,7 +244,7 @@ var jsCardDAV_Plugin = module.exports = jsDAV_Plugin.extend({
return e.stop();
}
- this.server.updateProperties(node.getOwner(), {"{http://sabredav.org/ns}vcard-url": value}, function(err, innerResult) {
+ this.server.updateProperties(node.getOwner(), {"{http://ajax.org/2005/aml}vcard-url": value}, function(err, innerResult) {
if (err)
return e.next(err);
@@ -339,18 +339,24 @@ var jsCardDAV_Plugin = module.exports = jsDAV_Plugin.extend({
* @param resource data
* @return void
*/
- beforeWriteContent: function(e, path, node, data) {
+ beforeWriteContent: function(e, path, node) {
if (!node.hasFeature(jsCardDAV_iCard))
return e.next();
- try {
- this.validateVCard(data);
- }
- catch (ex) {
- return e.next(ex);
- }
-
- e.next();
+ var self = this;
+ this.handler.getRequestBody("utf8", null, false, function(err, data) {
+ if (err)
+ return e.next(err);
+
+ try {
+ self.validateVCard(data);
+ }
+ catch (ex) {
+ return e.next(ex);
+ }
+
+ e.next();
+ });
},
/**
@@ -685,7 +691,7 @@ var jsCardDAV_Plugin = module.exports = jsDAV_Plugin.extend({
*/
afterGetProperties: function(e, uri, properties) {
// If the request was made using the SOGO connector, we must rewrite
- // the content-type property. By default SabreDAV will send back
+ // the content-type property. By default jsDAV will send back
// text/x-vcard; charset=utf-8, but for SOGO we must strip that last
// part.
if (!properties["200"]["{DAV:}getcontenttype"])
@@ -695,7 +701,7 @@ var jsCardDAV_Plugin = module.exports = jsDAV_Plugin.extend({
return e.next();
if (properties["200"]["{DAV:}getcontenttype"].indexOf("text/x-vcard") === 0)
- properties[200]["{DAV:}getcontenttype"] = "text/x-vcard";
+ properties["200"]["{DAV:}getcontenttype"] = "text/x-vcard";
e.next();
},
View
2 lib/DAV/backends/dropbox/tree.js
@@ -121,7 +121,7 @@ var jsDAV_Tree_Dropbox = module.exports = jsDAV_Tree.extend({
this.client.mv(source, destination, function(status, res) {
if (isError(status))
return cbfsmove(res.error);
- cbfsmove();
+ cbfsmove(null, source, destination);
});
},
});
View
4 lib/DAV/backends/fs/tree.js
@@ -116,6 +116,8 @@ var jsDAV_Tree_Filesystem = module.exports = jsDAV_Tree.extend({
return cbfsmove(new Exc.Forbidden("You are not allowed to move to " +
this.stripSandbox(destination)));
}
- Fs.rename(source, destination, cbfsmove);
+ Fs.rename(source, destination, function(err) {
+ cbfsmove(err, source, destination);
+ });
}
});
View
2 lib/DAV/backends/ftp/tree.js
@@ -203,7 +203,7 @@ var jsDAV_Tree_Ftp = module.exports = jsDAV_Tree.extend({
if (node.hasFeature(jsDav_iCollection))
repathChildren(source, destination);
- next();
+ next(null, source, destination);
});
function repathChildren(oldParentPath, newParentPath) {
View
8 lib/DAV/backends/sftp/tree.js
@@ -117,9 +117,11 @@ var jsDAV_Tree_Sftp = module.exports = jsDAV_Tree.extend({
* @return void
*/
move: function(source, destination, cbfsmove) {
- this.sftp.rename(this.getRealPath(source), this.getRealPath(destination), function(err) {
- Util.log(err, "error");
- cbfsmove(err);
+ var source = this.getRealPath(source);
+ var destination = this.getRealPath(destination);
+ this.sftp.rename(source, destination, function(err) {
+ Util.log(err, "error");
+ cbfsmove(err, source, destination);
});
}
});
View
115 lib/DAV/handler.js
@@ -332,6 +332,34 @@ jsDAV_Handler.STATUS_MAP = {
cbgetnodefp(null, node);
});
};
+
+ /**
+ * This method is called with every tree update
+ *
+ * Examples of tree updates are:
+ * * node deletions
+ * * node creations
+ * * copy
+ * * move
+ * * renaming nodes
+ *
+ * If Tree classes implement a form of caching, this will allow
+ * them to make sure caches will be expired.
+ *
+ * If a path is passed, it is assumed that the entire subtree is dirty
+ *
+ * @param string path
+ * @return void
+ */
+ this.markDirty = function(path) {
+ // We don't care enough about sub-paths
+ // flushing the entire cache
+ path = Util.trim(path, "/");
+ for (var nodePath in this.nodeCache) {
+ if (nodePath.indexOf(path) === 0)
+ delete this.nodeCache[nodePath];
+ }
+ };
/**
* HTTP OPTIONS
@@ -677,6 +705,8 @@ jsDAV_Handler.STATUS_MAP = {
node["delete"](function(err) {
if (!Util.empty(err))
return self.handleError(err);
+
+ self.markDirty(uri);
self.httpResponse.writeHead(204, {"content-length": "0"});
self.httpResponse.end();
self.dispatchEvent("afterDelete", uri);
@@ -885,7 +915,7 @@ jsDAV_Handler.STATUS_MAP = {
if (!node.hasFeature(jsDAV_iFile))
return self.handleError(new Exc.Conflict("PUT is not allowed on non-files."));
- self.dispatchEvent("beforeWriteContent", uri, function(stop) {
+ self.dispatchEvent("beforeWriteContent", uri, node, function(stop) {
if (stop === true)
return;
@@ -901,11 +931,14 @@ jsDAV_Handler.STATUS_MAP = {
});
}
- function afterPut(err) {
+ function afterPut(err, etag) {
if (!Util.empty(err))
return self.handleError(err);
- self.httpResponse.writeHead(200, {"content-length": "0"});
+ var headers = {"content-length": "0"};
+ if (etag)
+ headers.etag = etag;
+ self.httpResponse.writeHead(200, headers);
self.httpResponse.end();
self.dispatchEvent("afterWriteContent", uri);
}
@@ -1017,11 +1050,11 @@ jsDAV_Handler.STATUS_MAP = {
this.getCopyAndMoveInfo(function(err, moveInfo) {
if (!Util.empty(err))
return self.handleError(err);
- if (moveInfo["destinationExists"]) {
- self.dispatchEvent("beforeUnbind", moveInfo["destination"], function(stop) {
+ if (moveInfo.destinationExists) {
+ self.dispatchEvent("beforeUnbind", moveInfo.destination, function(stop) {
if (stop === true)
return false;
- moveInfo["destinationNode"]["delete"](function(err) {
+ moveInfo.destinationNode["delete"](function(err) {
if (!Util.empty(err))
return self.handleError(err);
afterDelete();
@@ -1033,23 +1066,26 @@ jsDAV_Handler.STATUS_MAP = {
}
function afterDelete() {
- self.dispatchEvent("beforeUnbind", moveInfo["source"], function(stop) {
+ self.dispatchEvent("beforeUnbind", moveInfo.source, function(stop) {
if (stop === true)
return false;
- self.dispatchEvent("beforeBind", moveInfo["destination"], function(stop) {
+ self.dispatchEvent("beforeBind", moveInfo.destination, function(stop) {
if (stop === true)
return false;
- self.server.tree.move(moveInfo["source"], moveInfo["destination"], function(err) {
+ self.server.tree.move(moveInfo.source, moveInfo.destination, function(err, sourceDir, destinationDir) {
if (!Util.empty(err))
return self.handleError(err);
- self.dispatchEvent("afterBind", moveInfo["destination"],
- Path.join(self.server.tree.basePath, moveInfo["destination"]));
+ self.markDirty(moveInfo.source);
+ self.markDirty(moveInfo.destination);
+
+ self.dispatchEvent("afterBind", moveInfo.destination,
+ Path.join(self.server.tree.basePath, moveInfo.destination));
// If a resource was overwritten we should send a 204, otherwise a 201
- self.httpResponse.writeHead(moveInfo["destinationExists"] ? 204 : 201,
+ self.httpResponse.writeHead(moveInfo.destinationExists ? 204 : 201,
{"content-length": "0"});
self.httpResponse.end();
- self.dispatchEvent("afterMove", moveInfo["destination"]);
+ self.dispatchEvent("afterMove", moveInfo.destination);
});
});
});
@@ -1071,11 +1107,11 @@ jsDAV_Handler.STATUS_MAP = {
this.getCopyAndMoveInfo(function(err, copyInfo) {
if (!Util.empty(err))
return self.handleError(err);
- if (copyInfo["destinationExists"]) {
- self.dispatchEvent("beforeUnbind", copyInfo["destination"], function(stop) {
+ if (copyInfo.destinationExists) {
+ self.dispatchEvent("beforeUnbind", copyInfo.destination, function(stop) {
if (stop === true)
return false;
- copyInfo["destinationNode"]["delete"](function(err) {
+ copyInfo.destinationNode["delete"](function(err) {
if (!Util.empty(err))
return self.handleError(err);
afterDelete();
@@ -1087,20 +1123,23 @@ jsDAV_Handler.STATUS_MAP = {
}
function afterDelete() {
- self.dispatchEvent("beforeBind", copyInfo["destination"], function(stop) {
+ self.dispatchEvent("beforeBind", copyInfo.destination, function(stop) {
if (stop === true)
return false;
- self.server.tree.copy(copyInfo["source"], copyInfo["destination"], function(err) {
+ self.server.tree.copy(copyInfo.source, copyInfo.destination, function(err) {
if (!Util.empty(err))
return self.handleError(err);
- self.dispatchEvent("afterBind", copyInfo["destination"],
- Path.join(self.server.tree.basePath, copyInfo["destination"]));
+
+ self.markDirty(copyInfo.destination);
+
+ self.dispatchEvent("afterBind", copyInfo.destination,
+ Path.join(self.server.tree.basePath, copyInfo.destination));
// If a resource was overwritten we should send a 204, otherwise a 201
- self.httpResponse.writeHead(copyInfo["destinationExists"] ? 204 : 201,
+ self.httpResponse.writeHead(copyInfo.destinationExists ? 204 : 201,
{"Content-Length": "0"});
self.httpResponse.end();
- self.dispatchEvent("afterCopy", copyInfo["destination"]);
+ self.dispatchEvent("afterCopy", copyInfo.destination);
});
});
}
@@ -1383,16 +1422,34 @@ jsDAV_Handler.STATUS_MAP = {
node = n;
// Only need to check entity tags if they are not *
if (ifMatch !== "*") {
- // The Etag is surrounded by double-quotes, so those must be
- // stripped.
- ifMatch = Util.trim(ifMatch, '"');
node.getETag(function(err, etag) {
if (err)
return cbprecond(err);
- if (etag !== ifMatch) {
+ var haveMatch = false;
+ // There can be multiple etags
+ ifMatch = ifMatch.split(",");
+ var ifMatchItem;
+ for (var i = 0, l = ifMatch.length; i < l; ++i) {
+ // Stripping any extra spaces
+ ifMatchItem = Util.trim(ifMatch[i], " ");
+
+ if (etag === ifMatchItem) {
+ haveMatch = true;
+ break;
+ }
+ else {
+ // Evolution has a bug where it sometimes prepends the "
+ // with a \. This is our workaround.
+ if (ifMatchItem.replace('\\"','"') === etag) {
+ haveMatch = true;
+ break;
+ }
+ }
+ }
+ if (!haveMatch) {
return cbprecond(new Exc.PreconditionFailed(
- "An If-Match header was specified, but the ETag did not match",
+ "An If-Match header was specified, but none of the specified the ETags matched",
"If-Match")
);
}
@@ -2229,7 +2286,7 @@ jsDAV_Handler.STATUS_MAP = {
if (!Util.empty(err))
return cbcreatefile(err);
- self.server.tree.markDirty(dir + "/" + name);
+ self.markDirty(dir + "/" + name);
self.dispatchEvent("afterBind", uri, Path.join(self.server.tree.basePath, uri));
cbcreatefile();
}
@@ -2342,7 +2399,7 @@ jsDAV_Handler.STATUS_MAP = {
}
function onDone() {
- self.server.tree.markDirty(parentUri);
+ self.markDirty(parentUri);
self.dispatchEvent("afterBind", uri, Path.join(parent.path, newName));
cbcreatecoll();
}
View
2 lib/DAV/interfaces/iFile.js
@@ -24,7 +24,7 @@ var jsDAV_iFile = module.exports = Base.extend({
* @param resource data
* @return void
*/
- put: function(data, callback) { callback(Exc.notImplementedYet()); },
+ put: function(data, enc, callback) { callback(Exc.notImplementedYet()); },
/**
* Returns the data
View
50 lib/DAV/objectTree.js
@@ -23,9 +23,6 @@ var Exc = require("./../shared/exceptions");
var jsDAV_ObjectTree = module.exports = jsDAV_Tree.extend({
initialize: function(rootNode) {
this.rootNode = rootNode;
- // we keep a cache dual to the one found in jsDAV_Handler, because of
- // possibly heavy-duty object-store (database) ops internally.
- this.cache = {};
},
/**
@@ -37,9 +34,6 @@ var jsDAV_ObjectTree = module.exports = jsDAV_Tree.extend({
getNodeForPath: function(path, cbgetnodepath) {
path = Util.trim(path, "/");
- // Did we cache it earlier?
- if (this.cache[path])
- return cbgetnodepath(null, this.cache[path]);
// Is it the root node?
if (!path || !path.length)
return cbgetnodepath(null, this.rootNode);
@@ -52,24 +46,17 @@ var jsDAV_ObjectTree = module.exports = jsDAV_Tree.extend({
// If there was no parent, we must simply ask it from the root node.
if (parentName === "") {
- this.rootNode.getChild(baseName, afterGetChild);
+ this.rootNode.getChild(baseName, cbgetnodepath);
}
else {
// Otherwise, we recursively grab the parent and ask him/her.
this.getNodeForPath(parentName, function(err, parent) {
if (!parent || !parent.hasFeature(jsDAV_iCollection))
return cbgetnodepath(new Exc.NotFound("Could not find node at path: " + path));
- parent.getChild(baseName, afterGetChild);
+ parent.getChild(baseName, cbgetnodepath);
});
}
-
- function afterGetChild(err, node) {
- if (err)
- return cbgetnodepath(err);
- self.cache[path] = node;
- cbgetnodepath(null, node);
- }
},
/**
@@ -87,40 +74,9 @@ var jsDAV_ObjectTree = module.exports = jsDAV_Tree.extend({
node.getChildren(function(err, children) {
if (err)
return cbgetchildren(err);
-
- children.forEach(function(child) {
- self.cache[Util.trim(path, "/") + "/" + child.getName()] = child;
- });
+
cbgetchildren(null, children);
});
});
- },
-
- /**
- * This method is called with every tree update
- *
- * Examples of tree updates are:
- * * node deletions
- * * node creations
- * * copy
- * * move
- * * renaming nodes
- *
- * If Tree classes implement a form of caching, this will allow
- * them to make sure caches will be expired.
- *
- * If a path is passed, it is assumed that the entire subtree is dirty
- *
- * @param string path
- * @return void
- */
- markDirty: function(path) {
- // We don't care enough about sub-paths
- // flushing the entire cache
- path = Util.trim(path, "/");
- for (var nodePath in this.cache) {
- if (nodePath.indexOf(path) === 0)
- delete this.cache[nodePath];
- }
}
});
View
2 lib/DAV/plugins/locks.js
@@ -336,7 +336,7 @@ var jsDAV_Locks_Plugin = module.exports = jsDAV_ServerPlugin.extend({
function afterNode() {
// We need to call the beforeWriteContent event for RFC3744
- self.handler.dispatchEvent("beforeWriteContent", [uri]);
+ self.handler.dispatchEvent("beforeWriteContent", [uri, node]);
self.lockNode(uri, lockInfo, function(err) {
if (err)
View
2 lib/DAV/server.js
@@ -132,7 +132,7 @@ function Server(options) {
+ "Argument must either be an instance of jsDAV_Tree, jsDAV_iNode, "
+ "a valid path to a location on the local filesystem or null", "error");
}
- root = jsDAV_SimpleCollection.new("root");
+ root = jsDAV_SimpleCollection.new("root");
this.tree = jsDAV_ObjectTree.new(root, options);
}
View
23 lib/DAV/tree.js
@@ -109,7 +109,6 @@ var jsDAV_Tree = module.exports = Base.extend({
self.copyNode(sourceNode, destinationParent, destinationName, function(err) {
if (err)
return cbcopytree(err);
- self.markDirty(destinationDir);
cbcopytree();
});
});
@@ -154,8 +153,6 @@ var jsDAV_Tree = module.exports = Base.extend({
function onDone(err) {
if (err)
return cbmovetree(err);
- self.markDirty(sourceDir);
- self.markDirty(destinationDir);
cbmovetree();
}
},
@@ -194,26 +191,6 @@ var jsDAV_Tree = module.exports = Base.extend({
node.getChildren(cbtreechildren);
});
},
-
- /**
- * This method is called with every tree update
- *
- * Examples of tree updates are:
- * * node deletions
- * * node creations
- * * copy
- * * move
- * * renaming nodes
- *
- * If Tree classes implement a form of caching, this will allow
- * them to make sure caches will be expired.
- *
- * If a path is passed, it is assumed that the entire subtree is dirty
- *
- * @param string path
- * @return void
- */
- markDirty: function(path) {},
/**
* copyNode
View
2 lib/DAVACL/abstractPrincipalCollection.js
@@ -74,7 +74,7 @@ var jsDAVACL_AbstractPrincipalCollection = module.exports = jsDAV_Collection.ext
* @param Function callback
* @return jsDAV_iPrincipal
*/
- getChildForPrincipal: function(principalInfo, callback) {},
+ getChildForPrincipal: function(principalInfo, callback) { callback(Exc.notImplementedYet()); },
/**
* Returns the name of this collection.
View
1 lib/VObject/component/VAlarm.js
@@ -8,7 +8,6 @@
"use strict";
var jsVObject_Component = require("./../component");
-console.dir(jsVObject_Component);
var jsVObject_DateTimeParser = require("./../dateTimeParser");
/**
View
4 lib/shared/asyncEvents.js
@@ -66,11 +66,11 @@ var _slice = Array.prototype.slice;
}
}).end(function(err) {
// everything except TRUE as an argument is an error
- if (jsDAV.debugMode && err) {
+ if (jsDAV.debugMode && !!err) {
if (err === true)
Util.log("event propagation '" + eventName + "' stopped", "info");
else
- Util.log("argument after event '" + eventName + "': " + err, "error");
+ Util.log("argument after event '" + eventName + "': {" + typeof err + "} " + err, "error");
}
cbdispatch(err);
});

0 comments on commit dd9b53e

Please sign in to comment.