5496fd2003-09-17Henrik Grubbström (Grubba) // Protocol support for RFC 2518 //
dfc1ad2004-05-13Henrik Grubbström (Grubba) // $Id: webdav.pike,v 1.29 2004/05/13 13:39:39 grubba Exp $
5496fd2003-09-17Henrik Grubbström (Grubba) // // 2003-09-17 Henrik Grubbström inherit "module"; #include <module.h> #include <request_trace.h>
dfc1ad2004-05-13Henrik Grubbström (Grubba) constant cvs_version = "$Id: webdav.pike,v 1.29 2004/05/13 13:39:39 grubba Exp $";
5496fd2003-09-17Henrik Grubbström (Grubba) constant thread_safe = 1; constant module_name = "DAV: Protocol support"; constant module_type = MODULE_FIRST; constant module_doc = "Adds support for various HTTP extensions defined " "in <a href='http://rfc.roxen.com/2518'>RFC 2518 (WEBDAV)</a>, such as " "<b>PROPFIND</b> and <b>PROPPATCH</b>.";
ca04922003-12-22Henrik Grubbström (Grubba) #ifdef DAV_DEBUG #define DAV_WERROR(X...) werror(X) #else /* !DAV_DEBUG */ #define DAV_WERROR(X...) #endif /* DAV_DEBUG */
5496fd2003-09-17Henrik Grubbström (Grubba) Configuration conf; void start(int q, Configuration c) { conf = c; }
cf09072004-04-29Henrik Grubbström (Grubba) mapping(string:mixed)|int(-1..0) first_try(RequestID id)
5496fd2003-09-17Henrik Grubbström (Grubba) { switch(id->method) { case "OPTIONS": return ([ "type":"text/html", "data":"", "extra_heads":([
a378422004-03-01Henrik Grubbström (Grubba)  "Allow":"CHMOD,COPY,DELETE,GET,HEAD,MKCOL,MKDIR,MOVE,"
5496fd2003-09-17Henrik Grubbström (Grubba)  "MV,PING,POST,PROPFIND,PROPPATCH,PUT,OPTIONS",
a378422004-03-01Henrik Grubbström (Grubba)  "Public":"CHMOD,COPY,DELETE,GET,HEAD,MKCOL,MKDIR,MOVE,"
5496fd2003-09-17Henrik Grubbström (Grubba)  "MV,PING,POST,PROPFIND,PROPPATCH,PUT,OPTIONS", "Accept-Ranges":"bytes",
1795852004-05-08Henrik Grubbström (Grubba)  "DAV":"1,2",
5496fd2003-09-17Henrik Grubbström (Grubba)  ]), ]);
cf09072004-04-29Henrik Grubbström (Grubba)  case "LOCK": case "UNLOCK":
a378422004-03-01Henrik Grubbström (Grubba)  case "COPY":
63a38c2004-03-03Henrik Grubbström (Grubba)  case "DELETE":
5496fd2003-09-17Henrik Grubbström (Grubba)  case "PROPFIND": case "PROPPATCH": // These need to be special cased, since they are recursive. return handle_webdav(id); } return 0; }
a8e2b32004-05-07Henrik Grubbström (Grubba) static constant SimpleNode = Parser.XML.Tree.SimpleNode; static constant SimpleRootNode = Parser.XML.Tree.SimpleRootNode; static constant SimpleHeaderNode = Parser.XML.Tree.SimpleHeaderNode; static constant SimpleElementNode = Parser.XML.Tree.SimpleElementNode;
5496fd2003-09-17Henrik Grubbström (Grubba) //! Implements PROPPATCH <DAV:set/>. class PatchPropertySetCmd {
d5fb852004-03-01Henrik Grubbström (Grubba)  constant command="DAV:set";
5496fd2003-09-17Henrik Grubbström (Grubba)  string property_name;
a8e2b32004-05-07Henrik Grubbström (Grubba)  string|array(SimpleNode) value; static void create(SimpleNode prop_node)
5496fd2003-09-17Henrik Grubbström (Grubba)  { property_name = prop_node->get_full_name(); value = prop_node->get_children(); if ((sizeof(value) == 1) && (value[0]->get_node_type() == Parser.XML.Tree.XML_TEXT)) { // Special case for a single text node. value = value[0]->get_text(); } }
df04242004-03-16Henrik Grubbström (Grubba)  mapping(string:mixed) execute(PropertySet context)
5496fd2003-09-17Henrik Grubbström (Grubba)  {
a2a4372004-03-23Martin Stjernholm #ifdef REQUEST_TRACE RequestID id = context->id; SIMPLE_TRACE_ENTER (0, "Setting property %O to %O", property_name, value); #endif mapping(string:mixed) res = context->set_property(property_name, value);
fb6c432004-03-24Anders Johansson #ifdef REQUEST_TRACE
a2a4372004-03-23Martin Stjernholm  SIMPLE_TRACE_LEAVE (res ? sprintf ("Got status %d: %O", res->error, res->rettext) : "");
fb6c432004-03-24Anders Johansson #endif
a2a4372004-03-23Martin Stjernholm  return res;
5496fd2003-09-17Henrik Grubbström (Grubba)  } } //! Implements PROPPATCH <DAV:remove/>.
d5fb852004-03-01Henrik Grubbström (Grubba) class PatchPropertyRemoveCmd(string property_name)
5496fd2003-09-17Henrik Grubbström (Grubba) {
d5fb852004-03-01Henrik Grubbström (Grubba)  constant command="DAV:remove";
0cb4c42004-03-15Martin Stjernholm 
df04242004-03-16Henrik Grubbström (Grubba)  mapping(string:mixed) execute(PropertySet context)
5496fd2003-09-17Henrik Grubbström (Grubba)  {
a2a4372004-03-23Martin Stjernholm #ifdef REQUEST_TRACE RequestID id = context->id; SIMPLE_TRACE_ENTER (0, "Removing property %O", property_name); #endif mapping(string:mixed) res = context->remove_property(property_name);
fb6c432004-03-24Anders Johansson #ifdef REQUEST_TRACE
a2a4372004-03-23Martin Stjernholm  SIMPLE_TRACE_LEAVE (res ? sprintf ("Got status %d: %O", res->error, res->rettext) : "");
fb6c432004-03-24Anders Johansson #endif
a2a4372004-03-23Martin Stjernholm  return res;
5496fd2003-09-17Henrik Grubbström (Grubba)  } } //! Handle WEBDAV requests.
cf09072004-04-29Henrik Grubbström (Grubba) mapping(string:mixed)|int(-1..0) handle_webdav(RequestID id)
5496fd2003-09-17Henrik Grubbström (Grubba) {
a8e2b32004-05-07Henrik Grubbström (Grubba)  SimpleNode xml_data;
5496fd2003-09-17Henrik Grubbström (Grubba)  TRACE_ENTER("Handle WEBDAV request...", 0); if (catch { xml_data = id->get_xml_data(); }) { // RFC 2518 8: // If a server receives ill-formed XML in a request it MUST reject // the entire request with a 400 (Bad Request). TRACE_LEAVE("Malformed XML.");
a2a4372004-03-23Martin Stjernholm  return Roxen.http_status(400, "Malformed XML data.");
5496fd2003-09-17Henrik Grubbström (Grubba)  }
cf09072004-04-29Henrik Grubbström (Grubba)  if (!(< "LOCK", "UNLOCK", "COPY", "DELETE", "PROPFIND", "PROPPATCH">)[id->method]) {
5496fd2003-09-17Henrik Grubbström (Grubba)  TRACE_LEAVE("Not implemented.");
a2a4372004-03-23Martin Stjernholm  return Roxen.http_status(501, "Not implemented.");
5496fd2003-09-17Henrik Grubbström (Grubba)  }
a378422004-03-01Henrik Grubbström (Grubba)  // RFC 2518 9.2: // The Depth header is only supported if a method's definition // explicitly provides for such support. int depth; if ((<"PROPFIND", "COPY", "MOVE", "DELETE", "LOCK">)[id->method]) { depth = ([ "0":0, "1":1, "infinity":0x7fffffff, 0:0x7fffffff ]) [id->request_headers->depth && String.trim_whites(id->request_headers->depth)];
63a38c2004-03-03Henrik Grubbström (Grubba)  if (zero_type(depth)) { TRACE_LEAVE(sprintf("Bad depth header: %O.", id->request_headers->depth));
a2a4372004-03-23Martin Stjernholm  return Roxen.http_status(400, "Unsupported depth.");
63a38c2004-03-03Henrik Grubbström (Grubba)  }
a378422004-03-01Henrik Grubbström (Grubba)  } else if (id->request_headers->depth) { // Depth header not supported in this case. }
5496fd2003-09-17Henrik Grubbström (Grubba)  // Function to call for matching location modules. // // Arguments: // string path // int d // RoxenModule module // RequestID id // mixed ... extras
1108142004-05-10Martin Stjernholm  function(string,string,int,RoxenModule,RequestID,
c2025d2004-03-03Henrik Grubbström (Grubba)  mixed ...:mapping(string:mixed)) recur_func;
5496fd2003-09-17Henrik Grubbström (Grubba)  array(mixed) extras = ({});
63a38c2004-03-03Henrik Grubbström (Grubba)  mapping(string:mixed) empty_result;
5496fd2003-09-17Henrik Grubbström (Grubba)  switch(id->method) {
cf09072004-04-29Henrik Grubbström (Grubba)  case "LOCK":
103d3f2004-04-30Henrik Grubbström (Grubba)  DAVLock lock;
cf09072004-04-29Henrik Grubbström (Grubba)  if (!xml_data) { // Refresh.
42e8992004-05-07Martin Stjernholm  int/*LockFlag*/|DAVLock state =
3563702004-05-03Henrik Grubbström (Grubba)  id->conf->check_locks(id->not_query, 0, id);
103d3f2004-04-30Henrik Grubbström (Grubba)  if (intp(state)) { if (state) {
c280752004-05-04Henrik Grubbström (Grubba)  TRACE_LEAVE("LOCK: Refresh: locked by other user.");
103d3f2004-04-30Henrik Grubbström (Grubba)  return Roxen.http_status(423, "Locked by other user"); } else {
c280752004-05-04Henrik Grubbström (Grubba)  TRACE_LEAVE("LOCK: Refresh: Lock not found.");
103d3f2004-04-30Henrik Grubbström (Grubba)  return Roxen.http_status(424, "Couldn't refresh missing lock."); } } id->conf->refresh_lock(lock = state, id);
cf09072004-04-29Henrik Grubbström (Grubba)  } else { // New lock.
a8e2b32004-05-07Henrik Grubbström (Grubba)  SimpleNode lock_info_node =
cf09072004-04-29Henrik Grubbström (Grubba)  xml_data->get_first_element("DAV:lockinfo", 1); if (!lock_info_node) {
c280752004-05-04Henrik Grubbström (Grubba)  TRACE_LEAVE("LOCK: No DAV:lockinfo.");
103d3f2004-04-30Henrik Grubbström (Grubba)  return Roxen.http_status(422, "Missing DAV:lockinfo.");
cf09072004-04-29Henrik Grubbström (Grubba)  }
a8e2b32004-05-07Henrik Grubbström (Grubba)  SimpleNode lock_scope_node =
cf09072004-04-29Henrik Grubbström (Grubba)  lock_info_node->get_first_element("DAV:lockscope", 1); if (!lock_scope_node) {
c280752004-05-04Henrik Grubbström (Grubba)  TRACE_LEAVE("LOCK: No DAV:lockscope.");
103d3f2004-04-30Henrik Grubbström (Grubba)  return Roxen.http_status(422, "Missing DAV:lockscope.");
cf09072004-04-29Henrik Grubbström (Grubba)  } string lockscope; if (lock_scope_node->get_first_element("DAV:exclusive", 1)) { lockscope = "DAV:exclusive"; } if (lock_scope_node->get_first_element("DAV:shared", 1)) { if (lockscope) {
c280752004-05-04Henrik Grubbström (Grubba)  TRACE_LEAVE("LOCK: Both DAV:exclusive and DAV:shared.");
103d3f2004-04-30Henrik Grubbström (Grubba)  return Roxen.http_status(422, "Both DAV:exclusive and DAV:shared.");
cf09072004-04-29Henrik Grubbström (Grubba)  } lockscope = "DAV:shared"; } if (!lockscope) {
c280752004-05-04Henrik Grubbström (Grubba)  TRACE_LEAVE("LOCK: Both DAV:lockscope.");
103d3f2004-04-30Henrik Grubbström (Grubba)  return Roxen.http_status(422, "Unsupported DAV:lockscope.");
cf09072004-04-29Henrik Grubbström (Grubba)  }
a8e2b32004-05-07Henrik Grubbström (Grubba)  SimpleNode lock_type_node =
cf09072004-04-29Henrik Grubbström (Grubba)  lock_info_node->get_first_element("DAV:locktype", 1); if (!lock_type_node) {
c280752004-05-04Henrik Grubbström (Grubba)  TRACE_LEAVE("LOCK: Both DAV:locktype.");
103d3f2004-04-30Henrik Grubbström (Grubba)  return Roxen.http_status(422, "Missing DAV:locktype.");
cf09072004-04-29Henrik Grubbström (Grubba)  } if (!lock_type_node->get_first_element("DAV:write", 1)) { // We only support DAV:write locks.
c280752004-05-04Henrik Grubbström (Grubba)  TRACE_LEAVE("LOCK: No DAV:write.");
103d3f2004-04-30Henrik Grubbström (Grubba)  return Roxen.http_status(422, "Missing DAV:write.");
cf09072004-04-29Henrik Grubbström (Grubba)  } string locktype = "DAV:write";
bd95642004-05-06Martin Stjernholm 
a8e2b32004-05-07Henrik Grubbström (Grubba)  array(SimpleNode) owner; if (SimpleNode owner_node = lock_info_node->get_first_element("DAV:owner", 1))
bd95642004-05-06Martin Stjernholm  owner = owner_node->get_children();
103d3f2004-04-30Henrik Grubbström (Grubba)  // Parameters OK, try to create a lock.
c280752004-05-04Henrik Grubbström (Grubba)  TRACE_ENTER(sprintf("LOCK: Creating a %s%s lock on %O.", depth?"recursive ":"", lockscope, id->not_query), this);
103d3f2004-04-30Henrik Grubbström (Grubba)  mapping(string:mixed)|DAVLock new_lock = id->conf->lock_file(id->not_query, depth != 0,
3563702004-05-03Henrik Grubbström (Grubba)  lockscope, "DAV:write",
bd95642004-05-06Martin Stjernholm  owner,
103d3f2004-04-30Henrik Grubbström (Grubba)  id); if (mappingp(new_lock)) { // Error // FIXME: Should probably generate a MultiStatus response. // cf RFC 2518 8.10.10.
c280752004-05-04Henrik Grubbström (Grubba)  TRACE_LEAVE("Failed."); TRACE_LEAVE("Returning failure code.");
103d3f2004-04-30Henrik Grubbström (Grubba)  return new_lock; }
c280752004-05-04Henrik Grubbström (Grubba)  TRACE_LEAVE("Ok.");
103d3f2004-04-30Henrik Grubbström (Grubba)  lock = new_lock;
cf09072004-04-29Henrik Grubbström (Grubba)  }
a8e2b32004-05-07Henrik Grubbström (Grubba)  string xml = SimpleRootNode()-> add_child(SimpleHeaderNode((["version":"1.0", "encoding":"utf-8"])))-> add_child(SimpleElementNode("DAV:prop", ([ "xmlns:DAV":"DAV:", "xmlns:MS": "urn:schemas-microsoft-com:datatypes" ]))-> add_child(SimpleElementNode("DAV:lockdiscovery", ([]))-> add_child(lock->get_xml())))->render_xml();
c280752004-05-04Henrik Grubbström (Grubba)  TRACE_LEAVE("Returning XML.");
103d3f2004-04-30Henrik Grubbström (Grubba)  return ([ "error":200, "data":xml, "len":sizeof(xml), "type":"text/xml; charset=\"utf-8\"",
c057eb2004-05-06Henrik Grubbström (Grubba)  "extra_heads":([ "Lock-Token":lock->locktoken ]),
103d3f2004-04-30Henrik Grubbström (Grubba)  ]);
cf09072004-04-29Henrik Grubbström (Grubba)  case "UNLOCK":
6b34222004-05-04Henrik Grubbström (Grubba)  string locktoken; if (!(locktoken = id->request_headers["lock-token"])) {
c280752004-05-04Henrik Grubbström (Grubba)  TRACE_LEAVE("UNLOCK: No lock-token.");
6b34222004-05-04Henrik Grubbström (Grubba)  return Roxen.http_status(400, "UNLOCK: Missing lock-token header."); } // The lock-token header is a Coded-URL. sscanf(locktoken, "<%s>", locktoken); if (!objectp(lock = id->conf->check_locks(id->not_query, 0, id))) {
c280752004-05-04Henrik Grubbström (Grubba)  TRACE_LEAVE(sprintf("UNLOCK: Lock-token %O not found.", locktoken));
6b34222004-05-04Henrik Grubbström (Grubba)  return Roxen.http_status(403, "UNLOCK: Lock not found."); }
6761032004-05-10Henrik Grubbström (Grubba)  if (lock->locktoken != locktoken) { SIMPLE_TRACE_LEAVE("UNLOCK: Locktoken mismatch: %O != %O.\n", locktoken, lock->locktoken); return Roxen.http_status(423, "Invalid locktoken."); }
c280752004-05-04Henrik Grubbström (Grubba)  mapping res = id->conf->unlock_file(id->not_query, lock, id); if (res) { TRACE_LEAVE(sprintf("UNLOCK: Unlocking of %O failed.", locktoken)); return res; } TRACE_LEAVE(sprintf("UNLOCK: Unlocked %O successfully.", locktoken)); return Roxen.http_status(204, "Ok.");
a378422004-03-01Henrik Grubbström (Grubba)  case "COPY": if (!id->request_headers->destination) {
c280752004-05-04Henrik Grubbström (Grubba)  TRACE_LEAVE("COPY: No destination header.");
a2a4372004-03-23Martin Stjernholm  return Roxen.http_status(400, "COPY: Missing destination header.");
a378422004-03-01Henrik Grubbström (Grubba)  }
dfc1ad2004-05-13Henrik Grubbström (Grubba)  PropertyBehavior propertybehavior = (<>); // default
a378422004-03-01Henrik Grubbström (Grubba)  if (xml_data) { // Mapping from href to behavior. // @int // @value -1 // omit // @value 0 // default
1795852004-05-08Henrik Grubbström (Grubba)  // If default also check the entry for href @expr{0@} (zero).
a378422004-03-01Henrik Grubbström (Grubba)  // @value 1 // keepalive // @endint
a8e2b32004-05-07Henrik Grubbström (Grubba)  SimpleNode prop_behav_node =
a378422004-03-01Henrik Grubbström (Grubba)  xml_data->get_first_element("DAV:propertybehavior", 1); if (!prop_behav_node) {
c280752004-05-04Henrik Grubbström (Grubba)  TRACE_LEAVE("COPY: No DAV:propertybehavior.");
a2a4372004-03-23Martin Stjernholm  return Roxen.http_status(400, "Missing DAV:propertybehavior.");
a378422004-03-01Henrik Grubbström (Grubba)  } /* Valid children of <DAV:propertybehavior> are * <DAV:keepalive> (12.12.1) * or * <DAV:omit> (12.12.2) */
a8e2b32004-05-07Henrik Grubbström (Grubba)  foreach(prop_behav_node->get_children(), SimpleNode n) {
a378422004-03-01Henrik Grubbström (Grubba)  switch(n->get_full_name()) { case "DAV:omit":
dfc1ad2004-05-13Henrik Grubbström (Grubba)  if (!multisetp(propertybehavior) || sizeof(propertybehavior)) {
a2a4372004-03-23Martin Stjernholm  return Roxen.http_status(400, "Conflicting DAV:propertybehavior.");
a378422004-03-01Henrik Grubbström (Grubba)  }
dfc1ad2004-05-13Henrik Grubbström (Grubba)  propertybehavior = 0;
a378422004-03-01Henrik Grubbström (Grubba)  break; case "DAV:keepalive":
dfc1ad2004-05-13Henrik Grubbström (Grubba)  if (!multisetp(propertybehavior) || sizeof(propertybehavior)) { return Roxen.http_status(400, "Conflicting DAV:propertybehavior."); }
a8e2b32004-05-07Henrik Grubbström (Grubba)  foreach(n->get_children(), SimpleNode href) {
a378422004-03-01Henrik Grubbström (Grubba)  if (href->get_full_name == "DAV:href") {
dfc1ad2004-05-13Henrik Grubbström (Grubba)  if (!multisetp(propertybehavior)) { TRACE_LEAVE("COPY: Conflicting DAV:propertybehaviour."); return Roxen.http_status(400, "Conflicting DAV:propertybehavior."); }
a378422004-03-01Henrik Grubbström (Grubba)  propertybehavior[href->value_of_node()] = 1; } else if (href->mNodeType == Parser.XML.Tree.XML_TEXT) { if (href->get_text() != "*"){
c280752004-05-04Henrik Grubbström (Grubba)  TRACE_LEAVE("COPY: Syntax error in DAV:keepalive.");
dfc1ad2004-05-13Henrik Grubbström (Grubba)  return Roxen.http_status(400, "Syntax error in DAV:keepalive.");
a378422004-03-01Henrik Grubbström (Grubba)  }
dfc1ad2004-05-13Henrik Grubbström (Grubba)  if (!multisetp(propertybehavior) || sizeof(propertybehavior)) {
c280752004-05-04Henrik Grubbström (Grubba)  TRACE_LEAVE("COPY: Conflicting DAV:propertybehaviour.");
dfc1ad2004-05-13Henrik Grubbström (Grubba)  return Roxen.http_status(400, "Conflicting DAV:propertybehavior.");
a378422004-03-01Henrik Grubbström (Grubba)  }
dfc1ad2004-05-13Henrik Grubbström (Grubba)  propertybehavior = 1;
a378422004-03-01Henrik Grubbström (Grubba)  } } break; } } }
545f5f2004-05-10Henrik Grubbström (Grubba)  extras = ({ id->misc["new-uri"], propertybehavior,
aaed922004-05-13Martin Stjernholm  // RFC 2518 9.6: If the overwrite header is not // included in a COPY or MOVE request then the // resource [sic] MUST treat the request as if it has // an overwrite header of value "T". !id->request_headers->overwrite || id->request_headers->overwrite=="T",
545f5f2004-05-10Henrik Grubbström (Grubba)  });
a378422004-03-01Henrik Grubbström (Grubba) 
1795852004-05-08Henrik Grubbström (Grubba)  recur_func = lambda(string source, string loc, int d, RoxenModule module,
1108142004-05-10Martin Stjernholm  RequestID id, string destination,
dfc1ad2004-05-13Henrik Grubbström (Grubba)  PropertyBehavior behavior,
aaed922004-05-13Martin Stjernholm  Overwrite overwrite) {
1795852004-05-08Henrik Grubbström (Grubba)  if (!has_prefix(destination, loc)) { // FIXME: Destination in other filesystem. return 0; } // Convert destination to module location relative. destination = destination[sizeof(loc)..];
d5432b2004-05-10Henrik Grubbström (Grubba)  mapping res = module->recurse_copy_files(source, destination, d,
1108142004-05-10Martin Stjernholm  behavior, overwrite, id);
d5432b2004-05-10Henrik Grubbström (Grubba)  if (res && ((res->error == 201) || (res->error == 204))) { empty_result = res; return 0; } return res;
a378422004-03-01Henrik Grubbström (Grubba)  };
d5432b2004-05-10Henrik Grubbström (Grubba)  empty_result = Roxen.http_status(404);
a378422004-03-01Henrik Grubbström (Grubba)  break;
63a38c2004-03-03Henrik Grubbström (Grubba)  case "DELETE":
1795852004-05-08Henrik Grubbström (Grubba)  recur_func = lambda(string path, string ignored, int d, RoxenModule module,
1108142004-05-10Martin Stjernholm  RequestID id) { mapping res = module->recurse_delete_files(path, id);
6761032004-05-10Henrik Grubbström (Grubba)  if (res && res->error < 300) {
1795852004-05-08Henrik Grubbström (Grubba)  // Succeed in deleting some file(s).
6761032004-05-10Henrik Grubbström (Grubba)  empty_result = res; return 0;
1795852004-05-08Henrik Grubbström (Grubba)  }
6761032004-05-10Henrik Grubbström (Grubba)  return res;
63a38c2004-03-03Henrik Grubbström (Grubba)  };
1795852004-05-08Henrik Grubbström (Grubba)  // The multi status will be empty if everything went well, // or if the file didn't exist. empty_result = Roxen.http_status(404);
63a38c2004-03-03Henrik Grubbström (Grubba)  break;
5496fd2003-09-17Henrik Grubbström (Grubba)  case "PROPFIND": // Get meta data. if (xml_data) {
a8e2b32004-05-07Henrik Grubbström (Grubba)  SimpleNode propfind = xml_data->get_first_element("DAV:propfind", 1);
5496fd2003-09-17Henrik Grubbström (Grubba)  if (!propfind) {
951dce2004-05-10Henrik Grubbström (Grubba)  TRACE_LEAVE("PROPFIND: No DAV:propfind.");
a2a4372004-03-23Martin Stjernholm  return Roxen.http_status(400, "Missing DAV:propfind.");
5496fd2003-09-17Henrik Grubbström (Grubba)  } /* Valid children of <DAV:propfind> are * <DAV:propname /> * or * <DAV:allprop /> * or * <DAV:prop>{propertylist}*</DAV:prop> */
a8e2b32004-05-07Henrik Grubbström (Grubba)  foreach(propfind->get_children(), SimpleNode prop) {
5496fd2003-09-17Henrik Grubbström (Grubba)  switch(prop->get_full_name()) { case "DAV:propname":
c280752004-05-04Henrik Grubbström (Grubba)  if (recur_func) { TRACE_LEAVE("PROPFIND: Conflicting DAV:propfind.");
a2a4372004-03-23Martin Stjernholm  return Roxen.http_status(400, "Bad DAV request (23.3.2.1).");
c280752004-05-04Henrik Grubbström (Grubba)  }
a378422004-03-01Henrik Grubbström (Grubba)  recur_func = lambda(string path, string ignored, int d,
1108142004-05-10Martin Stjernholm  RoxenModule module, RequestID id) {
c2025d2004-03-03Henrik Grubbström (Grubba)  module->recurse_find_properties(path, "DAV:propname",
1108142004-05-10Martin Stjernholm  d, id);
c2025d2004-03-03Henrik Grubbström (Grubba)  return 0;
5496fd2003-09-17Henrik Grubbström (Grubba)  }; break; case "DAV:allprop":
c280752004-05-04Henrik Grubbström (Grubba)  if (recur_func) { TRACE_LEAVE("PROPFIND: Conflicting DAV:propfind.");
a2a4372004-03-23Martin Stjernholm  return Roxen.http_status(400, "Bad DAV request (23.3.2.1).");
c280752004-05-04Henrik Grubbström (Grubba)  }
a378422004-03-01Henrik Grubbström (Grubba)  recur_func = lambda(string path, string ignored, int d,
1108142004-05-10Martin Stjernholm  RoxenModule module, RequestID id,
5496fd2003-09-17Henrik Grubbström (Grubba)  multiset(string)|void filt) {
c2025d2004-03-03Henrik Grubbström (Grubba)  module->recurse_find_properties(path, "DAV:allprop",
1108142004-05-10Martin Stjernholm  d, id,
c2025d2004-03-03Henrik Grubbström (Grubba)  filt); return 0;
5496fd2003-09-17Henrik Grubbström (Grubba)  }; break; case "DAV:prop":
c280752004-05-04Henrik Grubbström (Grubba)  if (recur_func) { TRACE_LEAVE("PROPFIND: Conflicting DAV:propfind.");
a2a4372004-03-23Martin Stjernholm  return Roxen.http_status(400, "Bad DAV request (23.3.2.1).");
c280752004-05-04Henrik Grubbström (Grubba)  }
a378422004-03-01Henrik Grubbström (Grubba)  recur_func = lambda(string path, string ignored, int d,
1108142004-05-10Martin Stjernholm  RoxenModule module, RequestID id,
5496fd2003-09-17Henrik Grubbström (Grubba)  multiset(string) filt) {
c2025d2004-03-03Henrik Grubbström (Grubba)  module->recurse_find_properties(path, "DAV:prop",
1108142004-05-10Martin Stjernholm  d, id,
c2025d2004-03-03Henrik Grubbström (Grubba)  filt); return 0;
5496fd2003-09-17Henrik Grubbström (Grubba)  }; // FALL_THROUGH case "http://sapportals.com/xmlns/cm/webdavinclude": // Support for draft-reschke-webdav-allprop-include-04 // FIXME: Should we check that // http://sapportals.com/xmlns/cm/webdavinclude only // occurrs in the DAV:allprop case? multiset(string) props = (multiset(string))(prop->get_children()->get_full_name() - ({ "" })); if (sizeof(extras)) { extras[0] |= props; } else { extras = ({ props }); } break; default: break; } } } else { // RFC 2518 8.1: // A client may choose not to submit a request body. An empty // PROPFIND request body MUST be treated as a request for the // names and values of all properties.
a378422004-03-01Henrik Grubbström (Grubba)  recur_func = lambda(string path, string ignored, int d,
1108142004-05-10Martin Stjernholm  RoxenModule module, RequestID id) {
c2025d2004-03-03Henrik Grubbström (Grubba)  module->recurse_find_properties(path, "DAV:allprop",
1108142004-05-10Martin Stjernholm  d, id);
c2025d2004-03-03Henrik Grubbström (Grubba)  return 0;
5496fd2003-09-17Henrik Grubbström (Grubba)  }; } break; case "PROPPATCH": // Set/delete meta data.
a8e2b32004-05-07Henrik Grubbström (Grubba)  SimpleNode propupdate = xml_data->get_first_element("DAV:propertyupdate", 1);
5496fd2003-09-17Henrik Grubbström (Grubba)  if (!propupdate) {
c280752004-05-04Henrik Grubbström (Grubba)  TRACE_LEAVE("PROPPATCH: No DAV:propertyupdate.");
a2a4372004-03-23Martin Stjernholm  return Roxen.http_status(400, "Missing DAV:propertyupdate.");
5496fd2003-09-17Henrik Grubbström (Grubba)  } /* Valid children of <DAV:propertyupdate> are any combinations of * <DAV:set><DAV:prop>{propertylist}*</DAV:prop></DAV:set> * and * <DAV:remove><DAV:prop>{propertylist}*</DAV:prop></DAV:remove> * * RFC 2518 8.2: * Instruction processing MUST occur in the order instructions * are received (i.e., from top to bottom). */ array(PatchPropertyCommand) instructions = ({});
a8e2b32004-05-07Henrik Grubbström (Grubba)  foreach(propupdate->get_children(), SimpleNode cmd) {
5496fd2003-09-17Henrik Grubbström (Grubba)  switch(cmd->get_full_name()) { case "DAV:set": case "DAV:remove":
a8e2b32004-05-07Henrik Grubbström (Grubba)  SimpleNode prop = cmd->get_first_element("DAV:prop", 1);
5496fd2003-09-17Henrik Grubbström (Grubba)  if (!prop) {
c280752004-05-04Henrik Grubbström (Grubba)  TRACE_LEAVE("PROPPATCH: No DAV:prop.");
a2a4372004-03-23Martin Stjernholm  return Roxen.http_status(400, "Bad DAV request (no properties specified).");
5496fd2003-09-17Henrik Grubbström (Grubba)  } if (cmd->get_full_name() == "DAV:set") { instructions += map(prop->get_children(), PatchPropertySetCmd); } else { // FIXME: Should we verify that the properties are empty? instructions += map(prop->get_children()->get_full_name(), PatchPropertyRemoveCmd); } break; default: // FIXME: Should we complain here? break; } } if (!sizeof(instructions)) {
c280752004-05-04Henrik Grubbström (Grubba)  TRACE_LEAVE("PROPPATCH: No instructions.");
a2a4372004-03-23Martin Stjernholm  return Roxen.http_status(400, "Bad DAV request (23.3.2.2).");
5496fd2003-09-17Henrik Grubbström (Grubba)  }
a378422004-03-01Henrik Grubbström (Grubba)  recur_func = lambda(string path, string ignored, int d, RoxenModule module,
1108142004-05-10Martin Stjernholm  RequestID id,
5496fd2003-09-17Henrik Grubbström (Grubba)  array(PatchPropertyCommand) instructions) {
a378422004-03-01Henrik Grubbström (Grubba)  // NOTE: RFC 2518 does not support depth header // with PROPPATCH, thus no recursion wrapper.
1108142004-05-10Martin Stjernholm  return module->patch_properties(path, instructions, id);
5496fd2003-09-17Henrik Grubbström (Grubba)  }; extras = ({ instructions }); break; default: break; } if (!recur_func) { TRACE_LEAVE("Bad DAV request.");
a2a4372004-03-23Martin Stjernholm  return Roxen.http_status(400, "Bad DAV request (23.3.2.2).");
5496fd2003-09-17Henrik Grubbström (Grubba)  } // FIXME: Security, DoS, etc...
9c5c122004-05-05Martin Stjernholm  int start_ms_size = id->multi_status_size();
5496fd2003-09-17Henrik Grubbström (Grubba)  string href = id->not_query; string href_prefix = combine_path(href, "./"); foreach(conf->location_modules(), [string loc, function fun]) { int d = depth;
a378422004-03-01Henrik Grubbström (Grubba)  string path; // Module location relative path.
1795852004-05-08Henrik Grubbström (Grubba)  SIMPLE_TRACE_ENTER(function_object(fun), "Trying module mounted at %O...", loc);
a378422004-03-01Henrik Grubbström (Grubba)  if (has_prefix(href, loc) || (loc == href+"/")) {
5496fd2003-09-17Henrik Grubbström (Grubba)  // href = loc + path. path = href[sizeof(loc)..]; } else if (d && has_prefix(loc, href_prefix) &&
1795852004-05-08Henrik Grubbström (Grubba)  ((d -= sizeof((loc[sizeof(href_prefix)..])/"/")) >= 0)) { // loc = href_prefix + ...
5496fd2003-09-17Henrik Grubbström (Grubba)  // && recursion. path = ""; } else { TRACE_LEAVE("Miss"); continue; } #ifdef MODULE_LEVEL_SECURITY int|mapping security_ret; if(security_ret = conf->check_security(fun, id)) { if (mappingp(security_ret)) {
a378422004-03-01Henrik Grubbström (Grubba)  // FIXME: What if we've already added some stuff in result?
5496fd2003-09-17Henrik Grubbström (Grubba)  TRACE_LEAVE("Security check return."); TRACE_LEAVE("Need authentication.");
9c5c122004-05-05Martin Stjernholm  recur_func = 0; // Avoid garbage.
5496fd2003-09-17Henrik Grubbström (Grubba)  return security_ret; } else { TRACE_LEAVE("Not allowed."); continue; } } #endif RoxenModule c = function_object(fun); TRACE_ENTER("Performing the work...", c);
9c5c122004-05-05Martin Stjernholm  ASSERT_IF_DEBUG (has_prefix (loc, "/"));
1108142004-05-10Martin Stjernholm  mapping(string:mixed) ret = recur_func(path, loc, d, c, id, @extras);
c2025d2004-03-03Henrik Grubbström (Grubba)  if (ret) { TRACE_LEAVE("Short circuit return."); TRACE_LEAVE("Done."); TRACE_LEAVE("DAV request done.");
9c5c122004-05-05Martin Stjernholm  recur_func = 0; // Avoid garbage.
c2025d2004-03-03Henrik Grubbström (Grubba)  return ret; }
5496fd2003-09-17Henrik Grubbström (Grubba)  TRACE_LEAVE("Done."); TRACE_LEAVE("Done."); } TRACE_LEAVE("DAV request done.");
9c5c122004-05-05Martin Stjernholm  recur_func = 0; // Avoid garbage. if (id->multi_status_size() == start_ms_size) {
a2a4372004-03-23Martin Stjernholm  return empty_result || Roxen.http_status(404);
63a38c2004-03-03Henrik Grubbström (Grubba)  }
9c5c122004-05-05Martin Stjernholm  return ([]);
5496fd2003-09-17Henrik Grubbström (Grubba) }