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 2010/08/18 20:32:45 astra Exp $
20 constant cvs_version="$Id: webdav.pike,v 1.3 2010/08/18 20:32:45 astra 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 string lock(mixed ctx, string fname, mapping lock_data) {
62 if ( !lock_data->token ) {
63 token = Locking.generate_token(ctx);
64 lock_data->token = token;
67 token = lock_data->token;
70 mapping ldata = ctx->query_attribute(OBJ_LOCK) || ([ ]);
71 ldata[token] = lock_data;
72 ctx->set_attribute(OBJ_LOCK, ldata);
75 mapping ldata = null_ressources[fname] || ([ ]);
76 ldata[token] = lock_data;
77 null_ressources[fname] = ldata | ([ "isnull": 1, ]) ;
79 return lock_data->token;
82 void unlock(mixed ctx, string fname, void|string token)
84 if ( !stringp(token) )
85 ctx->set_attribute(OBJ_LOCK, 0);
87 mapping ldata = ctx->query_attribute(OBJ_LOCK);
88 if ( mappingp(ldata) ) {
89 m_delete(ldata, token);
90 ctx->set_attribute(OBJ_LOCK, ldata);
95 mapping get_locks(mixed ctx, string fname) {
97 return null_ressources[fname];
98 return ctx->query_attribute(OBJ_LOCK) || ([ ]);
101 mapping is_locked(mixed ctx, string fname, void|string gottoken) {
104 ldata = null_ressources[fname] || ([ ]);
106 ldata = ctx->query_attribute(OBJ_LOCK) || ([ ]);
107 DAV_WERR("is_locked(%O, %s, %O)", ctx, fname, gottoken);
108 DAV_WERR("locks = %O\n", ldata);
109 foreach(indices(ldata), string token) {
110 mapping lockdata = ldata[token];
111 if ( mappingp(lockdata) ) {
113 if ( stringp(lockdata["timeout"]) ) {
114 sscanf(lockdata["timeout"], "Second-%d", timeout);
116 if ( lockdata->locktime > 0 && (time() - lockdata->locktime) < timeout )
118 DAV_WERR("active lock found...");
119 if ( !stringp(gottoken) || lockdata->token == gottoken )
124 if ( objectp(ctx) ) {
125 object env = ctx->get_environment();
126 ldata = is_locked(env, "", gottoken);
127 if ( mappingp(ldata) )
130 if ( !stringp(gottoken) ) {
132 catch(ctx->set_attribute(OBJ_LOCK, 0));
134 null_ressources[fname] = 0;
138 string get_user_href() {
139 return _Server->get_server_name() + "/~" + this_user()->get_user_name();
142 string get_etag(mixed ctx) {
143 return ctx->get_etag();
146 object get_object_id() { return _dav->get_object_id(); }
147 object this() { return _dav->get_user_object(); }
150 static object __webdavHandler;
152 int check_lock(object obj, mapping vars, void|string fname)
154 if ( !objectp(obj) && !stringp(fname) )
156 if ( !stringp(fname) )
158 string token = get_opaquelocktoken(__request->request_headers->if);
159 if ( stringp(token) ) {
160 if ( mappingp(__webdavHandler->is_locked(obj, fname, token)) )
163 mapping res = __webdavHandler->is_locked(obj, fname);
164 DAV_WERR("Checking lock (current token=%O) locked=%O", token, res);
168 int check_precondition(object obj)
170 int res = ::check_precondition(obj);
174 array ifheader = webdavlib.parse_if_header(__request->request_headers->if);
175 if ( sizeof(ifheader) == 0 )
178 foreach ( ifheader, mapping list) {
179 object condobj = obj;
180 string path = list->resource || __request->not_query;
181 if ( stringp(list->resource) ) {
182 condobj = _fp->path_to_object(list->resource);
184 if ( stringp(list->state) ) {
186 if ( search(list->state, "opaquelocktoken") >= 0 ) {
187 mapping islocked =__webdavHandler->is_locked(condobj,path,list->state);
188 if ( objectp(__webdavHandler) && !mappingp(islocked) ) {
189 continue; // match needs resource to be locked
192 else if ( list->state == "DAV:no-lock" ) {
193 mapping locks = __webdavHandler->get_locks(condobj, path);
198 if ( stringp(list->entity) )
199 if ( objectp(condobj) && list->entity != condobj->get_etag() )
203 else if ( stringp(list->entity) ) {
204 if ( objectp(condobj) && list->entity != condobj->get_etag() )
212 mapping handle_OPTIONS(object obj, mapping variables)
214 mapping result = ::handle_OPTIONS(obj, variables);
215 result->extra_heads += ([
216 "MS-Author-Via": "DAV",
227 mapping handle_LOCK(object obj, mapping variables)
229 if ( this_user() == USER("guest") )
230 return response_noaccess(obj, variables);
232 obj = _fp->path_to_object(__request->not_query, 1);
233 if ( !check_precondition(obj) )
234 return low_answer(412, "Precondition Failed");
236 return lock(__request->not_query, __request->request_headers,
238 __webdavHandler, obj);
241 mapping handle_UNLOCK(object obj, mapping variables)
243 if ( this_user() == USER("guest") )
244 return response_noaccess(obj, variables);
245 obj = _fp->path_to_object(__request->not_query, 1);
246 if ( !check_precondition(obj) )
247 return low_answer(412, "Precondition Failed");
249 return unlock(__request->not_query, __request->request_headers,
251 __webdavHandler, obj);
255 static bool move_and_rename(object obj, string name)
257 string fname, dname, sname;
259 sname = _Server->get_server_name();
260 sscanf(name, "%*s://" + _Server->get_server_name() + "%s", name);
262 if ( name[-1] == '/' )
263 name = dirname(name);
265 fname = basename(name);
266 dname = dirname(name);
269 target = obj->get_environment();
271 target = _fp->path_to_object(dname);
274 if ( !objectp(target) ) {
275 DAV_WERR("No Target directory found at %s", dname);
278 if ( strlen(fname) > 0 )
279 obj->set_attribute(OBJ_NAME, fname);
284 mapping|void handle_MOVE(object obj, mapping variables)
286 string destination = __request->request_headers->destination;
287 string overwrite = __request->request_headers->overwrite;
289 if ( !check_precondition(obj) )
290 return low_answer(412, "Precondition Failed");
292 if ( !check_lock(obj, variables) )
293 return low_answer(423, "Locked");
296 return response_notfound(__request->not_query, variables);
298 if ( !stringp(overwrite) )
300 __request->misc->overwrite = overwrite;
301 __request->misc->destination = resolve_destination(
302 destination, __request->request_headers->host);
305 // create copy variables before calling filesystem module
306 if ( mappingp(__request->misc->destination) )
307 return __request->misc->destination;
308 else if ( stringp(__request->misc->destination) )
309 __request->misc["new-uri"] = __request->misc->destination;
310 DAV_WERR("Handling move:misc=\n"+sprintf("%O\n", __request->misc));
312 destination = __request->misc["new-uri"];
313 if ( catch(destination = url_to_string(destination)) )
314 FATAL("Failed to convert destination %s", destination);
316 object dest = _fp->path_to_object(destination);
317 if ( objectp(dest) ) {
318 if ( __request->misc->overwrite == "F" ) {
319 DAV_WERR("overwritting failed !");
320 return low_answer(412, "Pre-Condition Failed");
323 if ( !check_lock(dest, variables) )
324 return low_answer(423, "Locked");
329 if ( !move_and_rename(obj, destination) )
330 return low_answer(409, "conflict");
331 return low_answer(res, "moved");
334 mapping|void handle_MKCOL(object obj, mapping variables)
336 if ( strlen(__request->body_raw) > 0 )
337 return low_answer(415, "unsupported type");
338 // todo: read the body ?!
339 if ( !check_precondition(obj) )
340 return low_answer(412, "Precondition Failed");
341 mapping result = ::handle_MKDIR(obj, variables);
342 if ( mappingp(result) && (result->error == 200 || !result->error) )
343 return low_answer(201, "Created");
347 mapping|void handle_COPY(object obj, mapping variables)
349 string destination = __request->request_headers->destination;
350 string overwrite = __request->request_headers->overwrite;
352 if ( !check_precondition(obj) )
353 return low_answer(412, "Precondition Failed");
355 if ( !stringp(overwrite) )
357 __request->misc->overwrite = overwrite;
358 __request->misc->destination = resolve_destination(
359 destination, __request->request_headers->host);
360 if ( mappingp(__request->misc->destination) )
361 return __request->misc->destination;
363 mixed result = ([ ]); // should now how to copy handle_http();
366 duplicate = _fp->path_to_object(__request->misc->destination);
368 DAV_WERR("Handling COPY:misc=\n"+sprintf("%O\n", __request->misc));
369 DAV_WERR("Found dest resource = %s !",
370 (objectp(duplicate) ? "yes" : "no"));
372 if ( objectp(duplicate) ) {
373 if ( __request->misc->overwrite == "F" ) {
374 DAV_WERR("overwritting failed !");
375 return low_answer(412, "conflict");
378 if ( !check_lock(duplicate, variables) )
379 return low_answer(423, "Locked");
386 if ( obj->get_object_class() & CLASS_CONTAINER )
387 duplicate = obj->duplicate(true);
389 duplicate= obj->duplicate();
392 if ( !move_and_rename(duplicate, __request->misc->destination) )
393 return low_answer(409, "conflict");
394 duplicate->set_attribute(OBJ_LOCK, 0);
395 return low_answer(res, "copied");
398 FATAL("Resource could not be found !");
399 return low_answer(404, "not found");
403 mapping|void handle_PROPPATCH(object obj, mapping variables)
405 obj = _fp->path_to_object(__request->not_query, 1);
407 if ( !check_lock(obj, variables) )
408 return low_answer(423, "Locked");
410 if ( !check_precondition(obj) )
411 return low_answer(412, "Precondition Failed");
413 return proppatch(__request->not_query, __request->request_headers,
414 __request->body_raw, __webdavHandler, obj);
417 mapping handle_DELETE(object obj, mapping vars)
420 if ( !check_lock(obj, vars) )
421 return low_answer(423, "Locked");
422 return ::handle_DELETE(obj, vars);
425 mapping handle_PUT(object obj, mapping vars)
427 string fname = __request->not_query;
429 obj = _fp->path_to_object(__request->not_query, 1);
432 if ( !check_lock(obj, vars, fname) )
433 return low_answer(423, "Locked");
435 if ( !check_precondition(obj) )
436 return low_answer(412, "Precondition Failed");
438 mapping result = ::handle_PUT(obj, vars);
440 mapping locks = __webdavHandler->get_locks(0, fname);
441 if ( mappingp(locks) && sizeof(locks) > 0 ) {
442 // locked null resources
443 object fp = vars->fp;
445 fp = get_module("filepath:tree");
446 obj = fp->path_to_object(fname);
447 if ( objectp(obj) ) {
448 if ( !mappingp(obj->query_attribute(OBJ_LOCK)) )
449 obj->set_attribute(OBJ_LOCK, locks);
454 FATAL("While setting lock for previous null resource: %O", err);
459 mapping|void handle_PROPFIND(object obj, mapping variables)
461 isWebDAV = 1; // heuristics ;-)
463 obj = _fp->path_to_object(__request->not_query, 1);
466 return low_answer(404, "not found");
468 return propfind(__request->not_query, __request->request_headers,
469 __request->body_raw, __webdavHandler, obj);
472 mixed get_property(object obj, Property property)
476 if ( !objectp(property) )
477 error("No property found, null-pointer !");
478 DAV_WERR("Get property %s, val=%O, ns=%O", property->get_name(),obj->query_attribute(property->get_name()), property->describe_namespace());
479 string pname = property->get_ns_name();
482 switch( property->get_name() ) {
484 return obj->query_attribute(OBJ_NAME);
486 return obj->get_identifier();
489 mixed res = obj->query_attribute(pname);
491 return replace(res, ({ "<", ">" }), ({ "<", ">" }));
495 int set_property(object obj, Property property, mapping namespaces)
497 string val = property->get_value();
498 string xmlns = property->describe_namespace();
499 DAV_WERR("Set property %s", property->_sprintf());
501 obj->set_attribute(property->get_ns_name(), val);
505 string resolve_redirect(object link)
507 object res = link->get_link_object();
508 return _fp->object_to_filename(res);
511 object get_context(object ctx, string f)
514 return ctx->get_object_byname(f);
518 int is_link(object ctx)
520 if ( objectp(ctx) && ctx->get_object_class() & CLASS_LINK )
526 call_command(string cmd, object obj, mapping variables)
528 mapping result = ([ ]);
530 // overwritten - must not forward requests without trailing /
531 DAV_WERR("RAW: %s", __request->raw);
533 function call = this_object()["handle_"+cmd];
534 if ( functionp(call) ) {
535 result = call(obj, variables);
539 result->data = "Not implemented";
541 if ( mappingp(result) ) {
542 if ( stringp(result->data) && strlen(result->data) > 0 &&
543 !result->encoding && !result->type )
546 result->encoding = "utf-8";
547 result->type = "text/xml";
552 DAV_WERR("DAV: %s %s %f seconds", cmd, __request->not_query, f);
556 void create(object fp, bool admin_port)
558 ::create(fp, admin_port);
559 __webdavHandler = steamDAV(this_object(), fp);
560 __webdavHandler->get_directory = fp->get_directory;
561 __webdavHandler->stat_file = fp->stat_file;
562 __webdavHandler->set_property = set_property;
563 __webdavHandler->get_property = get_property;
564 __webdavHandler->resolve_redirect = resolve_redirect;
565 __webdavHandler->get_context = get_context;
566 __webdavHandler->is_link = is_link;
569 void respond(object req, mapping result)
571 DAV_WERR("Respond: %O", result);
572 if ( stringp(result->data) )
573 result->length = strlen(result->data);
575 ::respond(req, result);
578 string get_identifier()