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.
157
2004/03/
04
12:
58:16
grubba
Exp $
+
// $Id: module.pike,v 1.
158
2004/03/
15
17:
12:
41
mast
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:597:
report_debug("query_property(): Unimplemented property:%O\n", prop_name); #endif /* DAV_DEBUG */ // 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 // multistatus XML element, with a response XML element which // contains a 404 (Not Found) status value. return Roxen.http_status (Protocols.HTTP.HTTP_NOT_FOUND, "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@}.
-
//!
-
//! @param context
-
//! The value returned by @[patch_property_start()].
-
//!
-
//! @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 the live properties they can
-
//! handle and call the inherited implementation for all others.
-
//! Setting of dead properties should be done through overloading of
-
//! @[set_dead_property()]. This way, the live properties handled on
-
//! 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
-
//! "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 path, string prop_name,
-
string|array(Parser.XML.Tree.Node) value,
-
RequestID id, mixed context)
-
{
-
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,
-
"Attempt to set read-only property.");
-
}
-
return set_dead_property(path, prop_name, value, id, context);
-
}
-
-
//! Attempt to set dead property @[prop_name] for @[path] to @[value].
-
//!
-
//! @param context
-
//! The value returned by @[patch_property_start()].
-
//!
-
//! @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.
-
//!
-
//! @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 path, string prop_name,
-
array(Parser.XML.Tree.Node) value,
-
RequestID id, mixed context)
-
{
-
return Roxen.http_status (Protocols.HTTP.HTTP_METHOD_INVALID,
-
"Setting of dead properties is not supported.");
-
}
-
-
//! Attempt to remove the property @[prop_name] for @[path].
-
//!
-
//! @param context
-
//! The value returned by @[patch_property_start()].
-
//!
-
//! @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, mixed context)
-
{
-
switch(prop_name) {
-
case "http://apache.org/dav/props/executable":
-
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,
-
"Attempt to remove a read-only property.");
-
}
-
// 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.
Roxen.git/server/base_server/module.pike:797:
if (!st) { st = stat_file(path, id); if (!st) { return; } } mapping(string:mixed) ret = find_properties(path, mode, result, id, filt, st); if (ret) {
-
result->add_response(path, XMLStatusNode(ret->error));
+
result->add_response(path, XMLStatusNode(ret->error
, ret->rettext
));
if (ret->rettext) { Parser.XML.Tree.ElementNode descr = Parser.XML.Tree.ElementNode ("DAV:responsedescription", ([])); descr->add_child (Parser.XML.Tree.TextNode (ret->rettext)); result->add_response (path, descr); } return; } if ((depth <= 0) || !st->isdir) return; depth--;
Roxen.git/server/base_server/module.pike:821:
} return; } // 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].
+
//!
Start
property
patching for @[path].
//! //! @returns //! @mixed //! @type zero
-
//! File not found.
@[patch_property_unroll()]
will
-
//!
not
be
called
in
this
case
.
+
//! File not found.
+
//!
@type PatchPropertyContext
+
//!
A
context that will
be
used
to
carry
out the patching
.
//! @type mapping //! Return code. No patching will be performed.
-
//! @type mixed
-
//! A context to be passed to @[set_property()],
-
//! @[remove_property()], @[patch_property_commit()]
-
//! and @[patch_property_unroll()].
+
//! @endmixed
-
mixed patch_property_start(string path, RequestID id)
+
mapping(string:
mixed
)|PatchPropertyContext
+
patch_property_start(string path, RequestID id)
{
-
return
!!
stat_file(path, id);
+
return stat_file(path, id)
&& PatchPropertyContext (path, 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,
mixed
context)
+
class
PatchPropertyContext
(optional
string
path,
optional
RequestID
id)
+
//!
Context
used
for
property
patching.
@[patch_property_start]
should
+
//!
return
an
object compatible with this class when
property
patching
+
//!
should
be
carried
out.
{
-
+
+
//! Attempt to set property @[prop_name] 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_commit()] is called, or unrolled when
+
//! @[patch_unroll()] is called.
+
//!
+
//! @note
+
//! Overloaded variants should only set the live properties they
+
//! can handle and call the inherited implementation for all
+
//! others. Setting of dead properties should be done through
+
//! overloading of @[set_dead_property()]. This way, the live
+
//! properties handled on 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
+
//! "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)
+
{
+
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,
+
"Attempt to set read-only property.");
}
-
+
return set_dead_property(prop_name, value);
+
}
-
//!
Patching
of the
properties
for
@[
path
]
succeeded.
-
void
patch_property_
commit
(string
path
,
RequestID
id,
mixed
context
)
+
//!
Attempt
to set dead property @[prop_name] 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_commit()
]
is called, or unrolled when
+
//! @[
patch_
unroll()] is called.
+
//!
+
//! @note
+
//! This function is called as a fallback by @[set_
property
()].
+
//!
+
//! @note
+
//! 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)
{
-
+
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
+
//! @[patch_commit()] is called, or unrolled when
+
//! @[patch_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 prop_name)
+
{
+
switch(prop_name) {
+
case "http://apache.org/dav/props/executable":
+
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,
+
"Attempt to remove a read-only property.");
+
}
+
// RFC 2518 12.13.1:
+
// Specifying the removal of a property that does not exist
+
// is not an error.
+
return 0;
+
}
+
+
//! Patching of one or more properties failed, i.e. at least one
+
//! call to @[set_property] or @[remove_property] returned a mapping
+
//! containing an error. Restore the state to what it was when
+
//! @[patch_property_start()] was called.
+
void patch_unroll()
+
{
+
}
+
+
//! Patching of the properties succeeded.
+
//!
+
//! @returns
+
//! Returns a result mapping. May return @expr{0@} (zero) on
+
//! success. If an error is returned, it's taken as a general
+
//! failure to patch any property (just like returning a mapping
+
//! from @[patch_property_start]). @[patch_unroll] will not be
+
//! called in that case.
+
mapping(string:mixed) patch_commit()
+
{
+
return 0;
+
}
+
}
+
mapping(string:mixed) patch_properties(string path, array(PatchPropertyCommand) instructions, MultiStatus result, RequestID id) {
-
mixed context = patch_property_start(path, id);
+
mapping(string:
mixed
)|PatchPropertyContext
context =
+
patch_property_start(path, id);
-
if (!
context
|| mappingp
(context)) {
+
if (!
objectp
(context)) {
return context; }
-
array(mapping(string:mixed)) results;
+
array(mapping(string:mixed)) results
= instructions->execute(context)
;
-
mixed err = catch {
-
results = instructions->execute(path, this_object(), id, context);
-
};
-
if (err) {
-
patch_property_unroll(path, id, context);
-
throw (err);
-
} 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_status (Protocols.HTTP.DAV_FAILED_DEP, "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,
context);
-
}
else {
+
context->
patch_unroll(
);
+
}
+
+
else {
+
mapping(string:mixed) ret =
context
->patch_commit(
);
+
if
(ret && ret->error >= 300)
+
return ret;
+
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, context
);
+
if (ret && ret->rettext) {
+
Parser.XML.Tree.ElementNode descr =
+
Parser.XML.Tree.ElementNode ("DAV:responsedescription", ([]));
+
descr->add
_
child (Parser.XML.Tree.TextNode (ret->rettext));
+
result->add
_
response
(path,
descr
);
} }
-
+
}
+
return 0; }
-
//! Convenience variant of @[
set
_
property
] that sets a single
-
//! property
: The default implementation calls
-
//!
@[patch_property_start], @[set_property], @[patch_property_unroll]
-
//!
and
@[patch_property_commit]
as
appropriate
.
+
//! Convenience variant of @[
patch
_
properties
] that sets a single
+
//! property
.
+
//!
+
//!
@returns
+
//!
Returns
a mapping on any error, zero otherwise
.
mapping(string:mixed) set_single_property (string path, string prop_name, string|array(Parser.XML.Tree.Node) value, RequestID id) {
-
mixed context = patch_property_start(path, id);
-
mapping
(
string:mixed
) result = set_property (
path,
prop_name, value
, id, context
);
+
mapping(string:
mixed
)
result;
+
mapping(string:mixed)|PatchPropertyContext
context =
+
patch_property_start(path, id);
+
+
if
(
!objectp (context
)
)
+
result =
context;
+
else {
+
result = context->
set_property (prop_name, value);
if (result && result->error >= 300)
-
patch_
property_
unroll
(
path, id, context
);
+
context->
patch_unroll();
else
-
patch_
property_
commit (
path,
id,
context
)
;
+
result = context->
patch_commit
();
+
}
+
+
if
(
mappingp
(result)
&& result->error >= 300
)
return result;
-
+
else
+
return 0;
}
-
//! Convenience variant of @[
remove
_
property
] that removes a single
-
//! property
: The default implementation calls
-
//!
@[patch_property_start], @[remove_property],
-
//!
@[patch_property_unroll] and @[patch_property_commit] as
-
//!
appropriate
.
+
//! Convenience variant of @[
patch
_
properties
] that removes a single
+
//! property
.
+
//!
+
//!
@returns
+
//!
Returns a mapping on any error, zero otherwise
.
mapping(string:mixed) remove_single_property (string path, string prop_name, RequestID id) {
-
mixed context = patch_property_start(path, id);
-
mapping
(
string:mixed
) result = remove_property (
path,
prop_name
, id, context
);
+
mapping(string:
mixed
)
result;
+
mapping(string:mixed)|PatchPropertyContext
context =
+
patch_property_start(path, id);
+
+
if
(
!objectp (context
)
)
+
result =
context;
+
else {
+
result = context->
remove_property (prop_name);
if (result && result->error >= 300)
-
patch_
property_
unroll
(
path, id, context
);
+
context->
patch_unroll();
else
-
patch_
property_
commit (
path,
id,
context
)
;
+
result = context->
patch_commit
();
+
}
+
+
if
(
mappingp
(result)
&& result->error >= 300
)
return result;
-
+
else
+
return 0;
} mapping(string:mixed)|int(-1..0)|Stdio.File find_file(string path, RequestID id); //! Delete the file specified by @[path]. //! //! @note //! Should return a 204 status on success. //!