1
/* Copyright (C) 2000-2006 Thomas Bopp, Thorsten Hampel, Ludger Merkens
3
* This program is free software; you can redistribute it and/or modify
4
* it under the terms of the GNU General Public License as published by
5
* the Free Software Foundation; either version 2 of the License, or
6
* (at your option) any later version.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License
14
* along with this program; if not, write to the Free Software
15
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
* $Id: webdav.pike,v 1.3 2006/05/17 20:41:49 exodusd Exp $
20
constant cvs_version="$Id: webdav.pike,v 1.3 2006/05/17 20:41:49 exodusd Exp $";
28
#include <attributes.h>
32
//#define WEBDAV_DEBUG
35
#define DAV_WERR(s, args...) werror(s+"\n", args)
37
#define DAV_WERR(s, args...)
44
inherit WebdavHandler;
49
mapping null_ressources = ([ ]);
51
void create(object dav, object fp) {
55
string url_name(string fname) {
56
return replace_uml(fname);
59
static string generate_token(object ctx) {
63
id = ctx->get_object_id();
65
string token = sprintf("%08x", random(time()));
66
string ttoken = sprintf("%08x", time()) + sprintf("%08x", random(time())) + sprintf("%08x", random(time()));
68
return "opaquelocktoken:" + token + "-" +
69
ttoken[0..3] + "-" + ttoken[4..7] + "-" + ttoken[8..11] + "-" +
73
string lock(mixed ctx, string fname, mapping lock_data) {
76
if ( !lock_data->token ) {
77
token = generate_token(ctx);
78
lock_data->token = token;
81
token = lock_data->token;
84
mapping ldata = ctx->query_attribute(OBJ_LOCK) || ([ ]);
85
ldata[token] = lock_data;
86
ctx->set_attribute(OBJ_LOCK, ldata);
89
mapping ldata = null_ressources[fname];
90
ldata[token] = lock_data;
91
null_ressources[fname] = ldata | ([ "isnull": 1, ]) ;
93
return lock_data->token;
95
void unlock(mixed ctx, string fname, void|string token)
97
if ( !stringp(token) )
98
ctx->set_attribute(OBJ_LOCK, 0);
100
mapping ldata = ctx->query_attribute(OBJ_LOCK);
101
if ( mappingp(ldata) ) {
102
m_delete(ldata, token);
103
ctx->set_attribute(OBJ_LOCK, ldata);
107
mapping get_locks(mixed ctx, string fname) {
109
return null_ressources[fname];
110
return ctx->query_attribute(OBJ_LOCK) || ([ ]);
112
mapping is_locked(mixed ctx, string fname, void|string gottoken) {
115
ldata = null_ressources[fname] || ([ ]);
117
ldata = ctx->query_attribute(OBJ_LOCK) || ([ ]);
118
DAV_WERR("is_locked(%O, %s)", ctx, fname);
120
foreach(indices(ldata), string token) {
121
mapping lockdata = ldata[token];
122
if ( mappingp(lockdata) ) {
124
if ( stringp(lockdata["timeout"]) ) {
125
sscanf(lockdata["timeout"], "Second-%d", timeout);
127
if ( lockdata->locktime > 0 && (time() - lockdata->locktime) < timeout )
129
DAV_WERR("active lock found...");
130
if ( !stringp(gottoken) || lockdata->token == gottoken )
135
if ( objectp(ctx) ) {
136
object env = ctx->get_environment();
137
ldata = is_locked(env, "", gottoken);
138
if ( mappingp(ldata) )
141
if ( !stringp(gottoken) ) {
143
ctx->set_attribute(OBJ_LOCK, 0);
145
null_ressources[fname] = 0;
149
string get_user_href() {
150
return _Server->get_server_name() + "/~" + this_user()->get_user_name();
153
string get_etag(mixed ctx) {
154
return ctx->get_etag();
157
object get_object_id() { return _dav->get_object_id(); }
158
object this() { return _dav->get_user_object(); }
161
static object __webdavHandler;
163
int check_lock(object obj, mapping vars, void|string fname)
165
if ( !objectp(obj) && !stringp(fname) )
167
if ( !stringp(fname) )
169
string token = get_opaquelocktoken(__request->request_headers->if);
170
if ( stringp(token) ) {
171
if ( mappingp(__webdavHandler->is_locked(obj, fname, token)) )
174
mapping res = __webdavHandler->is_locked(obj, fname);
175
DAV_WERR("Checking lock (current token=%O) locked=%O", token, res);
179
mapping handle_OPTIONS(object obj, mapping variables)
181
mapping result = ::handle_OPTIONS(obj, variables);
182
result->extra_heads += ([
183
"MS-Author-Via": "DAV",
194
mapping handle_LOCK(object obj, mapping variables)
196
if ( this_user() == USER("guest") )
197
return response_noaccess(obj, variables);
198
obj = _fp->path_to_object(__request->not_query, 1);
200
return lock(__request->not_query, __request->request_headers,
202
__webdavHandler, obj);
205
mapping handle_UNLOCK(object obj, mapping variables)
207
if ( this_user() == USER("guest") )
208
return response_noaccess(obj, variables);
209
obj = _fp->path_to_object(__request->not_query, 1);
211
return unlock(__request->not_query, __request->request_headers,
213
__webdavHandler, obj);
217
static bool move_and_rename(object obj, string name)
219
string fname, dname, sname;
221
sname = _Server->get_server_name();
222
sscanf(name, "%*s://" + _Server->get_server_name() + "%s", name);
224
if ( name[-1] == '/' )
225
name = dirname(name);
227
fname = basename(name);
228
dname = dirname(name);
229
object target = _fp->path_to_object(dname);
230
if ( !objectp(target) ) {
231
DAV_WERR("No Target directory found at %s", dname);
234
if ( strlen(fname) > 0 )
235
obj->set_attribute(OBJ_NAME, fname);
240
mapping|void handle_MOVE(object obj, mapping variables)
242
string destination = __request->request_headers->destination;
243
string overwrite = __request->request_headers->overwrite;
245
if ( !check_lock(obj, variables) )
246
return low_answer(423, "Locked");
248
if ( !stringp(overwrite) )
250
__request->misc->overwrite = overwrite;
251
__request->misc->destination = resolve_destination(
252
destination, __request->request_headers->host);
255
// create copy variables before calling filesystem module
256
if ( mappingp(__request->misc->destination) )
257
return __request->misc->destination;
258
else if ( stringp(__request->misc->destination) )
259
__request->misc["new-uri"] = __request->misc->destination;
260
DAV_WERR("Handling move:misc=\n"+sprintf("%O\n", __request->misc));
262
destination = __request->misc["new-uri"];
263
if ( catch(destination = url_to_string(destination)) )
264
FATAL("Failed to convert destination %s", destination);
266
object dest = _fp->path_to_object(destination);
267
if ( objectp(dest) ) {
268
if ( __request->misc->overwrite == "F" ) {
269
DAV_WERR("overwritting failed !");
270
return low_answer(412, "Pre-Condition Failed");
273
if ( !check_lock(dest, variables) )
274
return low_answer(423, "Locked");
279
if ( !move_and_rename(obj, destination) )
280
return low_answer(409, "conflict");
281
return low_answer(res, "moved");
284
mapping|void handle_MKCOL(object obj, mapping variables)
286
if ( strlen(__request->body_raw) > 0 )
287
return low_answer(415, "unsupported type");
288
// todo: read the body ?!
289
mapping result = ::handle_MKDIR(obj, variables);
290
if ( mappingp(result) && (result->error == 200 || !result->error) )
291
return low_answer(201, "Created");
295
mapping|void handle_COPY(object obj, mapping variables)
297
string destination = __request->request_headers->destination;
298
string overwrite = __request->request_headers->overwrite;
302
if ( !stringp(overwrite) )
304
__request->misc->overwrite = overwrite;
305
__request->misc->destination = resolve_destination(
306
destination, __request->request_headers->host);
307
if ( mappingp(__request->misc->destination) )
308
return __request->misc->destination;
310
mixed result = ([ ]); // should now how to copy handle_http();
313
duplicate = _fp->path_to_object(__request->misc->destination);
315
DAV_WERR("Handling COPY:misc=\n"+sprintf("%O\n", __request->misc));
316
DAV_WERR("Found dest resource = %s !",
317
(objectp(duplicate) ? "yes" : "no"));
319
if ( objectp(duplicate) ) {
320
if ( __request->misc->overwrite == "F" ) {
321
DAV_WERR("overwritting failed !");
322
return low_answer(412, "conflict");
325
if ( !check_lock(duplicate, variables) )
326
return low_answer(423, "Locked");
333
if ( obj->get_object_class() & CLASS_CONTAINER )
334
duplicate = obj->duplicate(true);
336
duplicate= obj->duplicate();
339
if ( !move_and_rename(duplicate, __request->misc->destination) )
340
return low_answer(409, "conflict");
341
duplicate->set_attribute(OBJ_LOCK, 0);
342
return low_answer(res, "copied");
345
FATAL("Resource could not be found !");
346
return low_answer(404, "not found");
350
mapping|void handle_PROPPATCH(object obj, mapping variables)
352
obj = _fp->path_to_object(__request->not_query, 1);
354
if ( !check_lock(obj, variables) )
355
return low_answer(423, "Locked");
357
return proppatch(__request->not_query, __request->request_headers,
358
__request->body_raw, __webdavHandler, obj);
361
mapping handle_DELETE(object obj, mapping vars)
364
if ( !check_lock(obj, vars) )
365
return low_answer(423, "Locked");
366
return ::handle_DELETE(obj, vars);
369
mapping handle_PUT(object obj, mapping vars)
371
string fname = __request->not_query;
373
obj = _fp->path_to_object(__request->not_query, 1);
376
if ( !check_lock(obj, vars, fname) )
377
return low_answer(423, "Locked");
379
mapping result = ::handle_PUT(obj, vars);
381
mapping locks = __webdavHandler->get_locks(0, fname);
382
if ( mappingp(locks) && sizeof(locks) > 0 ) {
383
// locked null resources
384
object fp = vars->fp;
386
fp = get_module("filepath:tree");
387
obj = fp->path_to_object(fname);
388
if ( objectp(obj) ) {
389
if ( !mappingp(obj->query_attribute(OBJ_LOCK)) )
390
obj->set_attribute(OBJ_LOCK, locks);
395
FATAL("While setting lock for previous null resource: %O", err);
400
mapping|void handle_PROPFIND(object obj, mapping variables)
402
isWebDAV = 1; // heuristics ;-)
404
obj = _fp->path_to_object(__request->not_query, 1);
407
return low_answer(404, "not found");
409
return propfind(__request->not_query, __request->request_headers,
410
__request->body_raw, __webdavHandler, obj);
413
mixed get_property(object obj, Property property)
417
if ( !objectp(property) )
418
error("No property found, null-pointer !");
419
DAV_WERR("Get property %s, val=%O, ns=%O", property->get_name(),obj->query_attribute(property->get_name()), property->describe_namespace());
420
string pname = property->get_ns_name();
423
switch( property->get_name() ) {
425
return obj->query_attribute(OBJ_NAME);
427
return obj->get_identifier();
430
mixed res = obj->query_attribute(pname);
432
return replace(res, ({ "<", ">" }), ({ "<", ">" }));
436
int set_property(object obj, Property property, mapping namespaces)
438
string val = property->get_value();
439
string xmlns = property->describe_namespace();
440
DAV_WERR("Set property %s", property->_sprintf());
442
obj->set_attribute(property->get_ns_name(), val);
446
string resolve_redirect(object link)
448
object res = link->get_link_object();
449
return _fp->object_to_filename(res);
452
object get_context(object ctx, string f)
455
return ctx->get_object_byname(f);
459
int is_link(object ctx)
461
if ( objectp(ctx) && ctx->get_object_class() & CLASS_LINK )
467
call_command(string cmd, object obj, mapping variables)
469
mapping result = ([ ]);
471
// overwritten - must not forward requests without trailing /
472
DAV_WERR("RAW: %s", __request->raw);
474
function call = this_object()["handle_"+cmd];
475
if ( functionp(call) ) {
476
result = call(obj, variables);
480
result->data = "Not implemented";
482
if ( mappingp(result) ) {
483
if ( stringp(result->data) && strlen(result->data) > 0 &&
484
!result->encoding && !result->type )
487
result->encoding = "utf-8";
488
result->type = "text/xml";
493
DAV_WERR("DAV: %s %s %f seconds", cmd, __request->not_query, f);
497
void create(object fp, bool admin_port)
499
::create(fp, admin_port);
500
__webdavHandler = steamDAV(this_object(), fp);
501
__webdavHandler->get_directory = fp->get_directory;
502
__webdavHandler->stat_file = fp->stat_file;
503
__webdavHandler->set_property = set_property;
504
__webdavHandler->get_property = get_property;
505
__webdavHandler->resolve_redirect = resolve_redirect;
506
__webdavHandler->get_context = get_context;
507
__webdavHandler->is_link = is_link;
510
void respond(object req, mapping result)
512
DAV_WERR("Respond: %O", result);
513
if ( stringp(result->data) )
514
result->length = strlen(result->data);
516
::respond(req, result);
519
string get_identifier()
527
string get_socket_name() { if ( is_dav() ) return "webdav"; else return "http"; }