Roxen.git / server / modules / misc / webdav.pike

version» Context lines:

Roxen.git/server/modules/misc/webdav.pike:16:   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>.";      #ifdef DAV_DEBUG   #define DAV_WERROR(X...) werror(X)   #else /* !DAV_DEBUG */   #define DAV_WERROR(X...)   #endif /* DAV_DEBUG */    + #ifdef IF_HEADER_DEBUG + #define IF_HDR_MSG(X...) werror (X) + #else + #define IF_HDR_MSG(X...) + #endif +    // Stuff from base_server/configuration.pike:   #ifdef THREADS   #define LOCK(X) key=id->conf->_lock(X)   #define UNLOCK() do{key=0;}while(0)   #else   #define LOCK(X)   #define UNLOCK()   #endif      
Roxen.git/server/modules/misc/webdav.pike:45:    "Maximum number of seconds a WebDAV lock should be valid for. "    "Negative disables the timeout header. "    "Zero enables infinite locks. " );   }      void start(int q, Configuration c)   {    conf = c;   }    + mapping(string:mixed)|int(-1..0) low_check_if_header(string path, +  array(array(array(string))) condition, +  RequestID id) + { +  SIMPLE_TRACE_ENTER(this, "Checking \"If\" header for %O", path); +  +  string|int(-1..0) etag; +  mapping(string:DAVLock) locks; +  +  next_condition: +  foreach(condition, array(array(string)) sub_cond) { +  SIMPLE_TRACE_ENTER(this, +  "Trying condition ( %{%s:%O %})...", sub_cond); +  int negate; +  foreach(sub_cond, array(string) token) { +  switch(token[0]) { +  case "not": +  negate = !negate; +  break; +  case "etag": +  if (!etag) { +  // Get the etag for this resource (if any). +  // FIXME: We only support straight strings as etag properties. +  if (!stringp(etag = id->conf->query_property(path, +  "DAV:getetag", id))) { +  etag = -1; +  } +  IF_HDR_MSG("IF: Etag: %O.\n", etag); +  } +  if (etag != token[1]) { +  // No etag available for this resource, or mismatch. +  if (!negate) { +  TRACE_LEAVE("Etag mismatch."); +  continue next_condition; +  } +  } else if (negate) { +  // Etag match with negated expression. +  TRACE_LEAVE("Matched negated etag."); +  continue next_condition; +  } +  negate = 0; +  break; +  case "key": +  if (!locks) { +  locks = id->conf->find_locks(path, 0, 0, id); +  } +  if (negate) { +  if (locks[token[1]]) { +  TRACE_LEAVE("Matched negated lock."); +  continue next_condition; // Fail. +  } +  } else if (!locks[token[1]]) { +  // Lock mismatch. +  SIMPLE_TRACE_LEAVE("Lock mismatch for key %O.", token[1]); +  continue next_condition; // Fail. +  } +  negate = 0; +  break; +  default: +  SIMPLE_TRACE_LEAVE("Unsupported condition: %s: %O.", +  token[0], token[1]); +  continue next_condition; // Fail. +  } +  } +  // Found matching sub-condition. +  TRACE_LEAVE("Found match."); +  TRACE_LEAVE("Ok%s."); +  return 0; +  } +  +  TRACE_LEAVE("Precondition failed."); +  return Roxen.http_status(Protocols.HTTP.HTTP_PRECOND_FAILED); + } +  + //! Checks that all preconditions specified by any if-header hold true. + //! + //! @returns + //! Returns @expr{0@} (zero) if all conditions hold true, and typically + //! a @[Protocols.HTTP.HTTP_PRECOND_FAILED] error status if not. + mapping(string:mixed)|int(-1..0) check_if_header(RequestID id) + { +  mapping(string:array(array(array(string)))) if_data = +  id->get_if_data(); +  if (!if_data) return 0; +  +  foreach(if_data; string path; array(array(array(string))) condition) { +  if (!path) continue; +  mapping(string:mixed)|int(-1..0) res = +  low_check_if_header(path, condition, id); +  if (res) return res; +  } +  +  return 0; + } +    mapping(string:mixed)|int(-1..0) first_try(RequestID id)   { -  +  if (id->misc->internal_get) return 0; +  +  mapping(string:mixed)|int(-1..0) res = check_if_header(id); +  if (res) { +  return res; +  } +     switch(id->method) {    case "OPTIONS":    return ([ "type":"text/html",    "data":"",    "extra_heads":([    "Allow":"CHMOD,COPY,DELETE,GET,HEAD,MKCOL,MKDIR,MOVE,"    "MV,PING,POST,PROPFIND,PROPPATCH,PUT,OPTIONS",    "Public":"CHMOD,COPY,DELETE,GET,HEAD,MKCOL,MKDIR,MOVE,"    "MV,PING,POST,PROPFIND,PROPPATCH,PUT,OPTIONS",    "Accept-Ranges":"bytes",
Roxen.git/server/modules/misc/webdav.pike:399:    "extra_heads":([ "Lock-Token":lock->locktoken ]),    ]);    case "UNLOCK":    string locktoken;    if (!(locktoken = id->request_headers["lock-token"])) {    TRACE_LEAVE("UNLOCK: No lock-token.");    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))) { +  string path = id->not_query; +  if (!has_suffix(path, "/")) path += "/"; +  mapping(string:DAVLock) locks = id->conf->find_locks(path, 0, 1, id); +  if (!(lock = locks[locktoken])) {    TRACE_LEAVE(sprintf("UNLOCK: Lock-token %O not found.", locktoken));    return Roxen.http_status(403, "UNLOCK: Lock not found.");    } -  if (lock->locktoken != locktoken) { -  SIMPLE_TRACE_LEAVE("UNLOCK: Locktoken mismatch: %O != %O.\n", -  locktoken, lock->locktoken); +  if (lock->path != path) { +  SIMPLE_TRACE_LEAVE("UNLOCK: Lock doesn't match path: %O != %O.\n", +  path, lock->path);    return Roxen.http_status(423, "Invalid locktoken.");    } -  mapping res = id->conf->unlock_file(id->not_query, lock, id); +  mapping res = id->conf->unlock_file(path, 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.");       case "COPY":    case "MOVE":    if (!id->request_headers->destination) {
Roxen.git/server/modules/misc/webdav.pike:531:    // RFC 4918 9.9:    // The MOVE operation on a non-collection resource is    // the logical equivalent of a copy (COPY), followed    // by consistency maintenance processing, followed by    // a delete of the source, where all three actions are    // performed in a single operation.       // The above seems to imply that RFC 4918 9.6 applies    // to MOVE. We thus need to destroy any locks rooted    // on the moved resource. -  multiset(DAVLock) sub_locks = +  mapping(string:DAVLock) sub_locks =    module->find_locks(source, -1, 0, id); -  foreach(sub_locks||(<>);DAVLock lock;) { +  foreach(sub_locks||([]);;DAVLock lock) {    SIMPLE_TRACE_ENTER(module,    "MOVE: Unlocking %O...", lock);    mapping fail =    id->conf->unlock_file(lock->path, lock, id);    if (fail) {    TRACE_LEAVE("MOVE: Unlock failed.");    } else {    TRACE_LEAVE("MOVE: Unlock ok.");    }    }
Roxen.git/server/modules/misc/webdav.pike:561:    };    empty_result = Roxen.http_status(404);    break;    case "DELETE":    recur_func = lambda(string path, string ignored, int d, RoxenModule module,    RequestID id) {    mapping res = module->recurse_delete_files(path, id);    if (res && res->error < 300) {    // Succeed in deleting some file(s).    empty_result = res; -  -  // RFC 4918 9.6: -  // A server processing a successful DELETE request: -  // -  // MUST destroy locks rooted on the deleted resource -  multiset(DAVLock) sub_locks = -  module->find_locks(path, -1, 0, id); -  foreach(sub_locks||(<>);DAVLock lock;) { -  SIMPLE_TRACE_ENTER(module, -  "DELETE: Unlocking %O...", lock); -  mapping fail = -  id->conf->unlock_file(lock->path, lock, id); -  if (fail) { -  TRACE_LEAVE("DELETE: Unlock failed."); -  } else { -  TRACE_LEAVE("DELETE: Unlock ok."); -  } -  } +     return 0;    }    return res;    };    // The multi status will be empty if everything went well,    // or if the file didn't exist.    empty_result = Roxen.http_status(404);    break;    case "PROPFIND": // Get meta data.    if (xml_data) {