Roxen.git
/
server
/
base_server
/
module.pike
version
»
Context lines:
10
20
40
80
file
none
3
Roxen.git/server/base_server/module.pike:1:
// This file is part of Roxen WebServer. // Copyright © 1996 - 2001, Roxen IS.
-
// $Id: module.pike,v 1.
133
2003/
04
/
23
12:
32
:
00
mast
Exp $
+
// $Id: module.pike,v 1.
134
2003/
06
/
02
12:
06
:
31
grubba
Exp $
#include <module_constants.h> #include <module.h> #include <request_trace.h> constant __pragma_save_parent__ = 1; inherit "basic_defvar"; mapping(string:array(int)) error_log=([]);
Roxen.git/server/base_server/module.pike:280:
TRACE_LEAVE("OK"); } else { TRACE_LEAVE("No stat info"); } } TRACE_LEAVE(""); return(res); }
+
// ISO 8601 Date and Time
+
// RFC 2518 23.2
+
// No fraction, UTC only.
+
static string iso8601_date_time(int ts)
+
{
+
mapping(string:int) gmt = gmtime(ts);
+
return sprintf("%04d-%02d-%02dT%02d:%02d:%02dZ",
+
1900 + gmt->year, gmt->mon, gmt->mday,
+
gmt->hour, gmt->min, gmt->sec);
+
}
+
+
//! Returns a multiset with the names off all supported properties.
+
multiset(string) query_all_properties(string path, RequestID id)
+
{
+
Stat st = stat_file(path, id);
+
if (!st) return (<>);
+
multiset(string) res = (<
+
"DAV:creationdate", // 13.1
+
"DAV:displayname", // 13.2
+
"DAV:getlastmodified", // 13.7
+
>);
+
if (st->isreg) {
+
res += (<
+
"DAV:getcontentlength", // 13.4
+
"DAV:getcontenttype", // 13.5
+
>);
+
}
+
return res;
+
}
+
+
//! Returns the value of the specified property, or an error code
+
//! mapping.
+
//!
+
//! @note
+
//! Returning a string is shorthand for returning an array
+
//! with a single text node.
+
string|array(Parser.XML.Tree.Node)|mapping(string:mixed)
+
query_property(string path, string prop_name, RequestID id)
+
{
+
Stat st = stat_file(path, id);
+
if (!st) return 0; // FIXME: No such file.
+
switch(prop_name) {
+
case "DAV:creationdate": // 13.1
+
return iso8601_date_time(st->ctime);
+
case "DAV:displayname": // 13.2
+
return combine_path(query_location(), path);
+
case "DAV:getcontentlength": // 13.4
+
if (st->isreg) {
+
return (string)st->size;
+
}
+
break;
+
case "DAV:getcontenttype": // 13.5
+
if (st->isreg) {
+
return id->conf->
+
type_from_filename(path, 0,
+
lower_case(Roxen.extension(path, id)));
+
}
+
break;
+
case "DAV:getlastmodified": // 13.7
+
return iso8601_date_time(st->mtime);
+
default:
+
break;
+
}
+
return 0; // FIXME: No such property.
+
}
+
+
//! Attempt to set property @[prop_name] for @[path] to @[value].
+
//!
+
//! @param value
+
//! Value to set the node to.
+
//! The case of an array of a single text node is special cased,
+
//! and is sent as a @expr{string@}.
+
//!
+
//! @returns
+
//! Returns a result mapping. May return @expr{0@} (zero) on success.
+
//!
+
//! @note
+
//! Actual changing of the property should be done first
+
//! when @[patch_property_commit()] is called, or unrolled
+
//! when @[patch_property_unroll()] is called.
+
//!
+
//! @note
+
//! Overloaded variants should only set live properties;
+
//! setting of dead properties should be done throuh
+
//! overloading of @[set_dead_property()].
+
mapping(string:mixed) set_property(string path, string prop_name,
+
string|array(Parser.XML.Tree.Node) value,
+
RequestID id)
+
{
+
switch(prop_name) {
+
case "DAV:creationdate": // 13.1
+
case "DAV:displayname": // 13.2
+
case "DAV:getcontentlength": // 13.4
+
case "DAV:getcontenttype": // 13.5
+
case "DAV:getlastmodified": // 13.7
+
return Roxen.http_low_answer(409,
+
"Attempt to set read-only property.");
+
}
+
return set_dead_property(path, prop_name, value, id);
+
}
+
+
//! Attempt to set dead property @[prop_name] for @[path] to @[value].
+
//!
+
//! @returns
+
//! Returns a result mapping. May return @expr{0@} (zero) on success.
+
//!
+
//! @note
+
//! Actual changing of the property should be done first
+
//! when @[patch_property_commit()] is called, or unrolled
+
//! when @[patch_property_unroll()] is called.
+
//!
+
//! @note
+
//! This function is called as a fallback by @[set_property()]
+
//! if all else fails.
+
//!
+
//! @note
+
//! The default implementation currently does not support setting
+
//! of dead properties, and will return an error code.
+
mapping(string:mixed) set_dead_property(string path, string prop_name,
+
array(Parser.XML.Tree.Node) value,
+
RequestID id)
+
{
+
return Roxen.http_low_answer(405,
+
"Setting of dead properties is not supported.");
+
}
+
+
//! Attempt to remove the property @[prop_name] for @[path].
+
//!
+
//! @note
+
//! Actual removal of the property should be done first
+
//! when @[patch_property_commit()] is called, or unrolled
+
//! when @[patch_property_unroll()] is called.
+
//!
+
//! @returns
+
//! Returns a result mapping. May return @expr{0@} (zero) on success.
+
//!
+
//! @note
+
//! The default implementation does not support deletion.
+
mapping(string:mixed) remove_property(string path, string prop_name,
+
RequestID id)
+
{
+
switch(prop_name) {
+
case "DAV:creationdate": // 13.1
+
case "DAV:displayname": // 13.2
+
case "DAV:getcontentlength": // 13.4
+
case "DAV:getcontenttype": // 13.5
+
case "DAV:getlastmodified": // 13.7
+
return Roxen.http_low_answer(409,
+
"Attempt to remove a read-only property.");
+
}
+
return Roxen.http_low_answer(404,
+
"Attempt to remove an unknown property.");
+
}
+
+
//! Default implementation of some RFC 2518 properties.
+
//!
+
//! @param path
+
//! @[query_location()]-relative path.
+
//! @param mode
+
//! Query-mode. Currently one of
+
//! @string mode
+
//! @value "DAV:propname"
+
//! Query after names of supported properties.
+
//! @value "DAV:allprop"
+
//! Query after all properties and their values.
+
//! @value "DAV:prop"
+
//! Query after properties specified by @[filt] and
+
//! their values.
+
//! @endstring
+
//! @param result
+
//! Result object.
+
//! @param id
+
//! Id of the current request.
+
//! @param filt
+
//! Optional multiset of requested properties. If this parameter
+
//! is @expr{0@} (zero) then all available properties are requested.
+
//!
+
//! @note
+
//! id->not_query() does not necessarily contain the same value as @[path].
+
void find_properties(string path, string mode, MultiStatus result,
+
RequestID id, multiset(string)|void filt)
+
{
+
Stat st = stat_file(path, id);
+
if (!st) return;
+
+
switch(mode) {
+
case "DAV:propname":
+
foreach(indices(query_all_properties(path, id)), string prop_name) {
+
result->add_property(path, prop_name, "");
+
}
+
return;
+
case "DAV:allprop":
+
filt = query_all_properties(path, id);
+
// FALL_THROUGH
+
case "DAV:prop":
+
foreach(indices(filt), string prop_name) {
+
result->add_property(path, prop_name,
+
query_property(path, prop_name, id));
+
}
+
return;
+
}
+
// FIXME: Unsupported DAV operation.
+
return;
+
}
+
+
void recurse_find_properties(string path, string mode, int depth,
+
MultiStatus result,
+
RequestID id, multiset(string)|void filt)
+
{
+
Stat st = stat_file(path, id);
+
if (!st) return;
+
+
find_properties(path, mode, result, id, filt);
+
if ((depth <= 0) || !st->isdir) return;
+
depth--;
+
foreach(find_dir(path, id), string filename) {
+
recurse_find_properties(combine_path(path, filename), mode, depth,
+
result, id, filt);
+
}
+
}
+
+
// RFC 2518 8.2
+
// Instructions MUST either all be executed or none executed.
+
// Thus if any error occurs during procesing all executed
+
// instructions MUST be undone and a proper error result
+
// returned.
+
+
//! Signal start of patching of properties for @[path].
+
void patch_property_start(string path, RequestID id)
+
{
+
}
+
+
//! Patching of the properties for @[path] failed.
+
//! Restore the state to what it was when @[patch_property_start()]
+
//! was called.
+
void patch_property_unroll(string path, RequestID id)
+
{
+
}
+
+
//! Patching of the properties for @[path] succeeded.
+
void patch_property_commit(string path, RequestID id)
+
{
+
}
+
+
void patch_properties(string path, array(PatchPropertyCommand) instructions,
+
MultiStatus result, RequestID id)
+
{
+
patch_property_start(path, id);
+
+
array(mapping(string:mixed)) results;
+
+
mixed err = catch {
+
results = instructions->execute(path, this_object(), id);
+
};
+
if (err) {
+
report_debug("patch_properties() failed:\n"
+
"%s\n",
+
describe_backtrace(err));
+
mapping(string:mixed) answer =
+
Roxen.http_low_answer(500, "Internal Server Error.");
+
foreach(instructions, PatchPropertyCommand instr) {
+
result->add_property(path, instr->property_name, answer);
+
}
+
patch_property_unroll(path, id);
+
} else {
+
int any_failed;
+
foreach(results, mapping(string:mixed) answer) {
+
if (any_failed = (answer && (answer->error >= 300))) {
+
break;
+
}
+
}
+
if (any_failed) {
+
// Unroll and fail any succeeded items.
+
int i;
+
mapping(string:mixed) answer =
+
Roxen.http_low_answer(424, "Failed dependency.");
+
for(i = 0; i < sizeof(results); i++) {
+
if (!results[i] || results[i]->error < 300) {
+
result->add_property(path, instructions[i]->property_name,
+
answer);
+
} else {
+
result->add_property(path, instructions[i]->property_name,
+
results[i]);
+
}
+
}
+
patch_property_unroll(path, id);
+
} else {
+
int i;
+
for(i = 0; i < sizeof(results); i++) {
+
result->add_property(path, instructions[i]->property_name,
+
results[i]);
+
}
+
patch_property_commit(path, id);
+
}
+
}
+
}
+
+
void recurse_patch_properties(string path, int depth,
+
array(PatchPropertyCommand) instructions,
+
MultiStatus result, RequestID id)
+
{
+
Stat st = stat_file(path, id);
+
+
patch_properties(path, instructions, result, id);
+
if (!st || (depth <= 0) || !st->isdir) return;
+
depth--;
+
foreach(find_dir(path, id), string filename) {
+
recurse_patch_properties(combine_path(path, filename), depth,
+
instructions, result, id);
+
}
+
}
+
string real_file(string f, RequestID id){} void add_api_function( string name, function f, void|array(string) types) { _api_functions[name] = ({ f, types }); } mapping api_functions() { return _api_functions;