Roxen.git / server / etc / modules / PropertySet.pike

version» Context lines:

Roxen.git/server/etc/modules/PropertySet.pike:1:   //! Contains methods for querying and setting of properties for a   //! resource.   //!   //! This default implementation takes care of the most important RFC   //! 2518 properties for ordinary files and directories in read-only   //! mode.   //!   //! Objects of this class are usually created through   //! @[RoxenModule()->query_properties()].    + #include <roxen.h> +    #ifdef DAV_DEBUG   #define DAV_WERROR(X...) werror(X)   #else /* !DAV_DEBUG */   #define DAV_WERROR(X...)   #endif /* DAV_DEBUG */    - //! Path for which these properties apply. + //! Filesystem-relative path for which these properties apply.   string path;    - //! Status information about @[path] as returned by @[stat_file()]. - Stat st; + //! Absolute path for which these properties apply. + string abs_path;      //! The current request.   RequestID id;      //! Create a new property set.   //!   //! Usually called via @[query_properties()]. - static void create(string path, Stat st, RequestID id) + static void create(string path, string abs_path, RequestID id)   {    global::path = path; -  global::st = st; +  global::abs_path = abs_path;    global::id = id; -  +  +  ASSERT_IF_DEBUG(has_prefix(abs_path, "/") && has_suffix(abs_path, path));   }    -  + //! @decl static void destroy(); + //!   //! Destruction callback.   //!   //! Note that this function must unroll any uncommitted   //! property changes. - static void destroy() - { - } +     -  + //! Return an @[Stdio.Stat] object for the resource. Its main use is + //! to tell collections (i.e. directories) from non-collections. + Stat get_stat(); +    //! Called by the default @[query_property] implementation to get the   //! response headers a GET or HEAD request on @[path] would yield.   //! It's used to fill in the properties that should reflect various   //! response headers.   mapping(string:string) get_response_headers();    -  + // Simulate an import of useful stuff from Parser.XML.Tree. + static constant SimpleNode = Parser.XML.Tree.SimpleNode; + static constant SimpleElementNode = Parser.XML.Tree.SimpleElementNode; +    private constant all_properties_common = (<    "DAV:getcontentlength",    "DAV:getcontenttype",    "DAV:displayname",    "DAV:resourcetype",    "DAV:supportedlock",    "DAV:iscollection",    "DAV:isfolder", -  +  "DAV:lockdiscovery", +  "DAV:supportedlock",   >);      private constant all_properties_file = all_properties_common + (<    "http://apache.org/dav/props/executable",   >);      private constant all_properties_dir = all_properties_common;      //! Returns a multiset with the names of all supported properties.   //!
Roxen.git/server/etc/modules/PropertySet.pike:89:   //!   //! @value "DAV:getcontenttype"   //! RFC2518 13.5   //!   //! @value "DAV:getetag"   //! RFC2518 13.6   //!   //! @value "DAV:getlastmodified"   //! RFC2518 13.7   //! + //! @value "DAV:lockdiscovery" + //! RFC2518 13.8 + //!   //! @value "DAV:resourcetype"   //! RFC2518 13.9   //!   //! @value "DAV:supportedlock"   //! RFC2518 13.11   //!   //! @value "DAV:defaultdocument"   //! @tt{draft-hopmann-collection-props-00@} 1.3   //!   //! Specifies the default document for a collection.
Roxen.git/server/etc/modules/PropertySet.pike:223:   //! Unknown definition.   //! @endstring   //!   //! Also, the MS DAV client requires a type argument to be able to   //! parse date/time fields correctly, even when they are formatted   //! according to the standard. @[XMLPropStatNode.add_property] has   //! special cases for this for @tt{DAV:creationdate@} and   //! @tt{DAV:getlastmodified@}.   multiset(string) query_all_properties()   { +  Stat st = get_stat(); +  if (st) {    multiset(string) props = -  (st->isreg ? all_properties_file : all_properties_dir) + (<>); +  (get_stat()->isreg ? all_properties_file : all_properties_dir) + (<>);       // This isn't necessary for the Content-Length and Content-Type    // headers since RequestID.make_response_headers always sets those.    mapping(string:string) hdrs = get_response_headers();    if (hdrs["Content-Language"]) props["DAV:getcontentlanguage"] = 1;    if (hdrs->ETag) props["DAV:getetag"] = 1;    if (hdrs["Last-Modified"]) props["DAV:getlastmodified"] = 1; -  +     return props;    } -  +  // Null resource. +  return all_properties_common + (<>); + }      //! Returns the value of the specified property, or an error code   //! mapping.   //!   //! The default implementation takes care of the most important RFC   //! 2518 properties.   //!   //! @note   //! Returning a string is shorthand for returning an array   //! with a single text node. - string|array(Parser.XML.Tree.Node)|mapping(string:mixed) + string|array(SimpleNode)|mapping(string:mixed)    query_property(string prop_name)   {    switch(prop_name) {   #if 0    // We don't really have any idea of the creation time in a unix    // style file system.    case "DAV:creationdate": // RFC2518 13.1 -  int t = st->ctime; -  if (t > st->atime) t = st->atime; -  if (t > st->mtime) t = st->mtime; +  Stdio.Stat stat = get_stat(); +  if (stat) { +  int t = stat->ctime; +  if (t > stat->atime) t = stat->atime; +  if (t > stat->mtime) t = stat->mtime;    return Roxen.iso8601_date_time(t); // MS kludge. -  +  } +  break;   #endif       case "DAV:displayname": // RFC2518 13.2    if ((path == "") || (path == "/")) return "/";    if (path[-1] == '/') return basename(path[..sizeof(path)-2]);    return basename(path);       case "DAV:getcontentlanguage":// RFC2518 13.3    return get_response_headers()["Content-Language"];   
Roxen.git/server/etc/modules/PropertySet.pike:279:       case "DAV:getcontenttype": // RFC2518 13.5    return get_response_headers()["Content-Type"];       case "DAV:getetag": // RFC2518 13.6    return get_response_headers()->ETag;       case "DAV:getlastmodified": // RFC2518 13.7    return get_response_headers()["Last-Modified"];    +  case "DAV:lockdiscovery": // RFC2518 13.8 +  return indices(id->conf->find_locks(abs_path, 0, 0, id))->get_xml(); +     case "DAV:resourcetype": // RFC2518 13.9 -  if (st->isdir) { +  if ((get_stat()||([]))->isdir) {    return ({ -  Parser.XML.Tree.ElementNode("DAV:collection", ([])), // 12.2 +  SimpleElementNode("DAV:collection", ([])), // 12.2    });    }    return 0;       case "DAV:supportedlock": // RFC2518 13.11 -  return ""; +  { +  return ({ +  SimpleElementNode("DAV:lockentry", ([]))-> +  add_child(SimpleElementNode("DAV:lockscope", ([]))-> +  add_child(SimpleElementNode("DAV:exclusive", ([]))))-> +  add_child(SimpleElementNode("DAV:locktype", ([]))-> +  add_child(SimpleElementNode("DAV:write", ([])))),    -  +  SimpleElementNode("DAV:lockentry", ([]))-> +  add_child(SimpleElementNode("DAV:lockscope", ([]))-> +  add_child(SimpleElementNode("DAV:shared", ([]))))-> +  add_child(SimpleElementNode("DAV:locktype", ([]))-> +  add_child(SimpleElementNode("DAV:write", ([])))), +  }); +  }    case "http://apache.org/dav/props/executable":    // http://www.webdav.org/mod_dav/:    //    // Name: executable    // Namespace: http://apache.org/dav/props/    // Purpose: Describes the executable status of the resource.    // Value: "T" | "F" (case is significant)    // Description: This property is defined by mod_dav's default    // repository, the "filesystem" repository. It    // corresponds to the "executable" permission flag    // in most filesystems.    //    // This property is not defined on collections. -  if (st->isreg) { -  if (st->mode & 0111) return "T"; +  Stdio.Stat stat = get_stat(); +  if (stat && stat->isreg) { +  if (stat->mode & 0111) return "T";    return "F";    }    break;      #if 0    // Need more interaction with directory listing modules to handle    // this.    case "DAV:defaultdocument": // draft-hopmann-collection-props-00 1.3    return "";   
Roxen.git/server/etc/modules/PropertySet.pike:326: Inside #if 0
   case "DAV:ishidden": // draft-hopmann-collection-props-00 1.6    return "0";       // Absence means not a structured document.    case "DAV:isstructureddocument": // draft-hopmann-collection-props-00 1.7    return "0";   #endif       case "DAV:iscollection": // draft-ietf-dasl-protocol-00 5.18    case "DAV:isfolder": // draft-hopmann-collection-props-00 1.5 -  if (st->isdir) { +  if ((get_stat()||([]))->isdir) {    return "1";    }    return "0";      #if 0    // The following are properties in the DAV namespace    // that Microsoft has stolen.    case "DAV:isreadonly": // MS -  if (!(st->mode & 0222)) { +  if (!((get_stat()||(["mode":0222]))->mode & 0222)) {    return "1";    }    return "0";    case "DAV:isroot": // MS    if (path == "") return "1";    return "0";    case "DAV:lastaccessed": // MS -  return Roxen.iso8601_date_time(st->atime); +  return Roxen.iso8601_date_time((get_stat()||([]))->atime);    case "DAV:href": // MS -  return sprintf("%s://%s%s%s%s", -  id->port_obj->prot_name, -  id->misc->host || id->port_obj->ip || -  gethostname(), -  (id->port_obj->port == id->port_obj->port)? -  "":(":"+(string)id->port_obj->port), -  id->port_obj->path||"", -  combine_path(query_location(), path)); +  return id->url_base() + abs_path[1..];    case "DAV:contentclass": // MS    return "";    case "DAV:parentname": // MS    return "";    case "DAV:name": // MS -  return combine_path(query_location(), path); +  return abs_path;   #endif /* 0 */       default:    break;    }       DAV_WERROR("query_property(): Unimplemented property:%O\n", prop_name);    // RFC 2518 8.1:    // A request to retrieve the value of a property which does not    // exist is an error and MUST be noted, if the response uses a
Roxen.git/server/etc/modules/PropertySet.pike:443:   //! any level in the inherit hierachy take precedence over dead   //! properties.   //!   //! @note   //! RFC 2518: Live property - A property whose semantics and syntax   //! are enforced by the server. For example, the live   //! @tt{"getcontentlength"@} property has its value, the length of the   //! entity returned by a GET request, automatically calculated by   //! the server.   mapping(string:mixed) set_property(string prop_name, -  string|array(Parser.XML.Tree.Node) value) +  string|array(SimpleNode) value)   {    switch(prop_name) {    case "http://apache.org/dav/props/executable":    // FIXME: Could probably be implemented R/W.    // FALL_THROUGH    case "DAV:displayname": // 13.2    case "DAV:getcontentlength": // 13.4    case "DAV:getcontenttype": // 13.5    case "DAV:getlastmodified": // 13.7    return Roxen.http_status (Protocols.HTTP.HTTP_CONFLICT,
Roxen.git/server/etc/modules/PropertySet.pike:484:   //! The default implementation currently does not support setting   //! of dead properties, and will return an error code.   //!   //! @note   //! RFC 2518: Dead Property - A property whose semantics and syntax   //! are not enforced by the server. The server only records the   //! value of a dead property; the client is responsible for   //! maintaining the consistency of the syntax and semantics of a   //! dead property.   mapping(string:mixed) set_dead_property(string prop_name, -  array(Parser.XML.Tree.Node) value) +  array(SimpleNode) value)   {    return Roxen.http_status (Protocols.HTTP.HTTP_METHOD_INVALID,    "Setting of dead properties is not supported.");   }      //! Attempt to remove the property @[prop_name].   //!   //! @note   //! Actual removal of the property should be done first   //! when @[commit()] is called, or unrolled
Roxen.git/server/etc/modules/PropertySet.pike:522:    }    // RFC 2518 12.13.1:    // Specifying the removal of a property that does not exist    // is not an error.    return 0;   }      //! RFC 2518 PROPFIND implementation for a single resource (i.e. not   //! recursive).   //! - //! @param path - //! @[query_location()]-relative path. +    //! @param mode   //! Query mode. Currently one of   //! @string mode   //! @value "DAV:propname"   //! Query names of supported properties.   //! @value "DAV:allprop"   //! Query all properties and their values.   //! @value "DAV:prop"   //! Query properties specified by @[filt] and their values.   //! @endstring   //! @param result - //! Result object. + //! @[MultiStatus.Prefixed] object to collect the results. It has + //! the path to the file system as implicit prefix.   //! @param filt   //! Optional multiset of requested properties. If this parameter   //! is @expr{0@} (zero) then all available properties are requested.   mapping(string:mixed) find_properties(string mode, -  MultiStatus result, +  MultiStatus.Prefixed result,    multiset(string)|void filt)   {    switch(mode) {    case "DAV:propname": -  foreach(query_all_properties(); string prop_name;) { +  filt = query_all_properties(); +  foreach(filt; string prop_name;) {    result->add_property(path, prop_name, "");    } -  return 0; +  break;    case "DAV:allprop":    if (filt) {    // Used in http://sapportals.com/xmlns/cm/webdavinclude case.    // (draft-reschke-webdav-allprop-include-04).    filt |= query_all_properties();    } else {    filt = query_all_properties();    }    // FALL_THROUGH    case "DAV:prop": -  foreach(indices(filt), string prop_name) { +  foreach(filt; string prop_name;) {    result->add_property(path, prop_name,    query_property(prop_name));    } -  +  break; +  default: +  // FIXME: Unsupported DAV operation.    return 0;    } -  // FIXME: Unsupported DAV operation. +  +  if (filt["http://apache.org/dav/props/executable"]) +  // Not really necessary. +  result->add_namespace ("http://apache.org/dav/props/");    return 0;   }