/[drupal]/contributions/modules/webdav/webdav.module
ViewVC logotype

Contents of /contributions/modules/webdav/webdav.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.37 - (show annotations) (download) (as text)
Mon May 18 22:26:43 2009 UTC (6 months, 1 week ago) by ulhume
Branch: MAIN
CVS Tags: DRUPAL-6--1-0-RC8, HEAD
Changes since 1.36: +2 -2 lines
File MIME type: text/x-php
#391596
1 <?php
2
3 /* This file is part of "WebDav for Drupal Module".
4 * Copyright 2009, arNuméral
5 * Author : Yoran Brault
6 * eMail : yoran.brault@bad_arnumeral.fr (remove bad_ before sending an email)
7 * Site : http://www.arnumeral.fr/node/5
8 *
9 * "WebDav for Drupal Module" is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2.1 of
12 * the License, or (at your option) any later version.
13 *
14 * "WebDav for Drupal Module" is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public
20 * License along with "Broken Anchor for Node comments Module"; if not, write to the Free
21 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23 */
24
25
26 require_once "server.inc";
27
28 define('WEBDAV_ROOT_MENU', 'webdav');
29 define('WEBDAV_VERSION', '6.x-1.0-rc7');
30
31
32
33 /* permissions */
34 define('WEBDAV_PERMS_WEBDAV_ACCESS', "WebDAV access");
35
36
37 define('WEBDAV_STATUS_OK',"200 OK");
38 define('WEBDAV_STATUS_CREATED',"201 Created");
39 define('WEBDAV_STATUS_NO_CONTENT',"204 No Content");
40 define('WEBDAV_STATUS_NOT_FOUND', "404 Not Found");
41 define('WEBDAV_STATUS_FORBIDDEN', "403 Forbidden");
42 define('WEBDAV_STATUS_INTERNAL_SERVER_ERROR',"500 Internal Server Error");
43 define('WEBDAV_STATUS_NOT_IMPLEMENTED',"501 Not Implemented");
44 define('WEBDAV_STATUS_CONFLICT',"409 Conflict");
45 define('WEBDAV_STATUS_MOVED_PERMANENTLY', '301 Moved Permanently');
46 define('WEBDAV_STATUS_UNAUTHORIZED', '401 Unauthorized');
47
48 define('WEBDAV_CONTENT_XML',0);
49 define('WEBDAV_CONTENT_DRUPAL',1);
50 define('WEBDAV_CONTENT_BINARY',2);
51
52
53
54 function webdav_parse_path($path) {
55 $path=trim($path,"/");
56 if (strlen($path)>0) {
57 $route=explode("/",$path);
58 } else {
59 $route=array();
60 }
61 array_unshift($route, "root");
62 return $route;
63 }
64
65 /**
66 * Session is basicaly a wrapper arround
67 * and exploded path called as 'arguments'
68 *
69 * @param $path ressource path
70 * @return session array
71 */
72 function webdav_session_create($path) {
73 $tmp=webdav_parse_path($path);
74
75 // Decode the path as it comes from real URL
76 for($i=0; $i < count($tmp); $i++) {
77 if ($i!=1) {
78 $route[]=webdav_url_decode($tmp[$i]);
79 }
80 }
81
82 // build the context
83 return (object)array(
84 'path'=>$path,
85 'route'=>$route,
86 'error'=>false,
87 );
88 }
89
90 function webdav_url_decode($source) {
91 $result=rawurldecode($source);
92 $result=strtr($result,array("[NB]"=>"#"));
93 return $result;
94 }
95
96 function webdav_url_encode($source) {
97 $result=rawurlencode($source);
98 $result=strtr($result,array("#"=>"[NB]"));
99 if (webdav_client_is_gnome()) {
100 // GVFS don't like to have encoded version for those characters...
101 $result=strtr($result,array("%27"=>"'", "%2C"=>",", ";"=>"%59"));
102 }
103 return $result;
104 }
105
106 function webdav_mime_type($file_path)
107 {
108 $mtype = '';
109 if (function_exists('mime_content_type')){
110 $mtype = mime_content_type($file_path);
111 }
112 else if (function_exists('finfo_file')){
113 $finfo = finfo_open(FILEINFO_MIME);
114 $mtype = finfo_file($finfo, $file_path);
115 finfo_close($finfo);
116 }
117 if ($mtype == ''){
118 $mtype = "application/force-download";
119 }
120 return $mtype;
121 }
122
123 function webdav_user_authenticate($form_values = array()) {
124 global $user;
125
126 // Name and pass keys are required.
127 if (!empty($form_values['name']) && !empty($form_values['pass']) &&
128 $account = user_load(array('name' => $form_values['name'], 'pass' => trim($form_values['pass']), 'status' => 1))) {
129 $user = $account;
130 webdav_user_authenticate_finalize($form_values);
131 return $user;
132 }
133 }
134
135 /**
136 * This is a copy of original Drupal Core function.
137 *
138 * @param unknown_type $edit
139 */
140 function webdav_user_authenticate_finalize(&$edit) {
141 global $user;
142
143 /**
144 * We don't want to be logged like this as there is no such a thing as
145 * WebDAV session. So each query would log something here...
146 */
147 // watchdog('user', 'Session opened for %name.', array('%name' => $user->name));
148 // // Update the user table timestamp noting user has logged in.
149 // // This is also used to invalidate one-time login links.
150 // $user->login = time();
151 // db_query("UPDATE {users} SET login = %d WHERE uid = %d", $user->login, $user->uid);
152
153 /*
154 * We Don't need to regenarate session as we just don't use this. Cookie will be clean
155 * anyway in webdav_handler
156 */
157 // Regenerate the session ID to prevent against session fixation attacks.
158 // sess_regenerate();
159 user_module_invoke('login', $edit, $user);
160 }
161
162 function webdav_check_collection_perms($handler) {
163 if ($handler===false) {
164 return true;
165 }
166
167 // If no access defined, we juste drop any attempt to access
168 if (!$handler['access']) {
169 if (webdav_is_debug()) webdav_debug("No access perms defined for '".webdav_format_route($handler['route'])."' collection.");
170 webdav_session_set_status(WEBDAV_STATUS_FORBIDDEN);
171 return false;
172 }
173
174 // Lets get authentication stuff
175 $auth_type = isset($_SERVER["AUTH_TYPE"])? $_SERVER["AUTH_TYPE"]: null;
176 $user_name = isset($_SERVER["PHP_AUTH_USER"])? $_SERVER["PHP_AUTH_USER"]: null;
177 $password = isset($_SERVER["PHP_AUTH_PW"])? $_SERVER["PHP_AUTH_PW"]: null;
178
179 // We are still anonymous, so let's authentify if we can
180 global $user;
181 if ($user->uid==0 && $user_name && $password) {
182 if (webdav_is_debug()) webdav_debug("Checking user:".webdav_format_var($user_name)." password:".webdav_format_var($password, false));
183 $user= webdav_user_authenticate(array('name'=>$user_name, 'pass'=>$password));
184 if (user_access(WEBDAV_PERMS_WEBDAV_ACCESS, $user) == TRUE) {
185 if (webdav_is_debug()) webdav_debug("Login succeed for ".$user_name);
186 return true;
187 }
188
189 if (dav_is_debug) webdav_debug("Login failed for ".$user_name);
190 return false;
191 }
192
193 // From this point we are authentified if we can, anonymous else
194 // but webdav resource can be accessed by anonymous, so let's try
195 $ok=true;
196 foreach ($handler['access'] as $access) {
197 if (!user_access($access)) {
198 webdav_session_set_status(WEBDAV_STATUS_FORBIDDEN);
199 $ok=false;
200 break;
201 }
202 }
203 if ($ok) {
204 return true;
205 }
206
207 // Nothing works, so we need authentication
208 $site_name=variable_get('site_name', null);
209 if ($site_name) {
210 $site_name=" for '".$site_name."'";
211 }
212 $http_auth_realm = "Drupal WebDav Access"; // for ".$site_name;
213 if (dav_is_debug) webdav_debug(" Need authentication, for ".$http_auth_realm);
214
215 // RFC2518 says we must use Digest instead of Basic
216 // but Microsoft Clients do not support Digest
217 // and we don't support NTLM and Kerberos
218 // so we are stuck with Basic here
219 header('WWW-Authenticate: Basic realm="'.$http_auth_realm.'"');
220 header("HTTP/1.1 401 Unauthorized", true);
221 header("X-WebDAV-Status: 401 Unauthorized", true);
222 webdav_session_close(WEBDAV_STATUS_UNAUTHORIZED);
223 exit();
224 }
225
226 /**
227 * Implementation of hook_menu
228 */
229 function webdav_menu() {
230
231 // Webdav Query callback
232 $items = array (
233 WEBDAV_ROOT_MENU => array (
234 'page callback' => 'webdav_handler',
235 'page arguments' => array (1),
236 'type' => MENU_CALLBACK,
237 // We should do this in order not to let drupal handle authentication. See check_auth.
238 'access arguments' => array ('access content'),
239 ),
240
241 // Webdav server settings
242 'admin/settings/webdav' => array (
243 'title' => 'Server',
244 'description' => 'Configure WebDAV server settings.',
245 'page callback' => 'drupal_get_form',
246 'page arguments' => array ('webdav_admin_settings'),
247 'access arguments' => array ('administer site configuration'),
248 'file' => 'webdav.admin.inc',
249 ),
250
251 // Main settings tab (for other modules to hook on)
252 'admin/settings/webdav/main' => array(
253 'title' => 'Server settings',
254 'type' => MENU_DEFAULT_LOCAL_TASK,
255 'weight' => -10,
256 ),
257 );
258 return $items;
259 }
260
261
262 /**
263 * hook_perm implementation.
264 *
265 * @return permission array.
266 */
267 function webdav_perm() {
268
269 return array (
270 WEBDAV_PERMS_WEBDAV_ACCESS,
271 );
272 }
273
274 /**
275 * redirect to the right collection URL is ending slash is mission
276 *
277 * @param $item the corresponding item
278 */
279 function webdav_redirect_wrong_collection($item) {
280 if (!webdav_redirect()) return;
281 // redirection if leading / is missing for a folder
282 $path=webdav_session('path');
283 if ($item['type']=='collection' && !webdav_is_slashied($path)) {
284 if (webdav_is_debug()) {
285 webdav_debug("Collection URL is mission ending slash, sending redirection...");
286 }
287 webdav_session_close(WEBDAV_STATUS_MOVED_PERMANENTLY);
288 drupal_goto(trim($path,'/').'/', NULL, NULL, 301);
289 exit;
290 }
291 }
292
293 /**
294 * Entry point for any DAV request
295 */
296 function webdav_handler() {
297 // We don't handle POST and this arrives when someone is connecting via Drupal
298 if ($_SERVER['REQUEST_METHOD']=='POST') return "";
299
300 // Default output type for WebDAV is XML
301 webdav_set_output_type(WEBDAV_CONTENT_XML);;
302 global $session_time_start;
303 $session_time_start = microtime(true);
304
305
306 // Everything from now is buffered
307 ob_start();
308 ob_implicit_flush(0);
309
310 // Lets mess u with paths...
311 $_SERVER['SCRIPT_NAME'] = ""; // In order to be modified by autopath system & co.
312 $_SERVER['PATH_INFO'] = $_GET['q'];
313
314 // PATH_INFO should always start with /
315 if (strlen($_SERVER['PATH_INFO'])==0 || $_SERVER['PATH_INFO'][0]!='/') {
316 $_SERVER['PATH_INFO'] = "/".$_SERVER['PATH_INFO'];
317 }
318
319 // we need our last slash if we had it...
320 if ( webdav_is_slashied($_SERVER['REQUEST_URI']) &&
321 !webdav_is_slashied($_SERVER['PATH_INFO']) ) {
322 $_SERVER['PATH_INFO'].="/";
323 }
324
325
326 // Debugging stuff
327 $depth=$_SERVER['HTTP_DEPTH']==''?'infinite':$_SERVER['HTTP_DEPTH'];
328 if (webdav_is_debug()) {
329 error_log('');
330 error_log('******************************************************');
331 error_log('* RM:'.$_SERVER['REQUEST_METHOD']." DP:".$depth." SN:".$_SERVER['SCRIPT_NAME']." PI:".$_SERVER['PATH_INFO']);
332 if (webdav_is_debug(2)) {
333 error_log('* RU:'.$_SERVER['REQUEST_URI']. " DQ:?q=".$_GET['q']);
334 }
335 error_log('* WD:'.WEBDAV_VERSION.' WS:'.$_SERVER['SERVER_SOFTWARE']." PHP:".PHP_VERSION." GW:".PHP_SAPI." OS:".PHP_OS);
336 error_log('* UA:'.$_SERVER['HTTP_USER_AGENT']);
337 error_log('******************************************************');
338 } else {
339 // error_log('WebDAV '.WEBDAV_VERSION." : ".$_SERVER['REQUEST_METHOD']." [".$depth.", ".$_SERVER['HTTP_USER_AGENT']."] : ".$_SERVER['PATH_INFO']);
340 }
341
342
343 // Call server request
344 $server = new DrupalDavServer();
345 $server->ServeRequest();
346 // If we are not in webdav pure mode (binary or drupal embeded, we should handle errors)
347 if (webdav_get_output_type()!=WEBDAV_CONTENT_XML && webdav_session_failed()) {
348 error_log("Error in embeded mode : ".webdav_session('status'));
349 if (webdav_session('status')==WEBDAV_STATUS_NOT_FOUND) {
350 drupal_not_found();
351 } else {
352 drupal_access_denied();
353 }
354 exit;
355 }
356
357 // If this is drupal embeded content we should act as a kind menu handler
358 if (webdav_get_output_type()==WEBDAV_CONTENT_DRUPAL) {
359 $contents = ob_get_contents();
360 ob_end_clean();
361 return $contents;
362 }
363
364 // We don't need this anymore
365 // session_destroy();
366 header("Set-Cookie:",TRUE); // Someone ? a better way to really clean-up header ?
367 header("Expires:",TRUE);
368 header("Last-Modified:",TRUE);
369 header("Cache-Control:",TRUE);
370 header("Cache-Control:",TRUE);
371 //header("ETag: $etag");
372
373 // select the right encoding for our content
374 if( (webdav_get_output_type()==WEBDAV_CONTENT_BINARY) || !webdav_http_compression() || headers_sent() || $_SERVER['REQUEST_METHOD']=="OPTIONS" || !function_exists("gzencode")){
375 $encoding = false;
376 }elseif( strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'x-gzip') !== false ){
377 $encoding = 'x-gzip';
378 }elseif( strpos($_SERVER['HTTP_ACCEPT_ENCODING'],'gzip') !== false ){
379 $encoding = 'gzip';
380 }else{
381 $encoding = false;
382 }
383
384 $contents = ob_get_contents();
385 if (webdav_get_output_type()!=WEBDAV_CONTENT_BINARY ) {
386 if (webdav_is_debug(3)) webdav_debug($contents);
387 }
388 $original_size=round(strlen($contents)/1024);
389 ob_end_clean();
390 if( $encoding ){
391 header('Content-Encoding: '.$encoding);
392 $contents = gzencode($contents, 9);
393 if (webdav_is_debug()) {
394 $final_size=round(strlen($contents)/1024);
395 if ($final_size==0) {
396 $ratio=0;
397 } else {
398 $ratio=round($original_size/$final_size);
399 }
400 webdav_debug("Sending compressed data from ".$original_size."ko to ".$final_size."ko (Ratio:".$ratio.")");
401 }
402 print($contents);
403 }else{
404 print $contents;
405 }
406 exit();
407 }
408
409 /**
410 * Slashify - make sure path ends in a slash
411 *
412 * @param string directory path
413 * @returns string directory path wiht trailing slash
414 */
415 function webdav_slashify($path) {
416 if ($path[strlen($path)-1] != '/') {
417 $path = $path."/";
418 }
419 return $path;
420 }
421
422 function webdav_unslashify($path) {
423 if ($path[strlen($path)-1] == '/') {
424 $path = substr($path,0,strlen($path)-1);
425 }
426 return $path;
427 }
428
429 function webdav_is_slashied($path) {
430 return ($path[strlen($path)-1] == '/');
431 }
432
433 function _webdav_has_last_slash($path) {
434 return ($path[strlen($path)-1] == '/');
435 }
436
437 function webdav_route_to_path($route) {
438 for ($i=0; $i<count($route); $i++) {
439 $route[$i]=webdav_url_encode($route[$i]);
440 }
441 return implode("/",$route);
442 }
443
444 /**
445 * Enumerate items for a collection using hook_dav_items
446 *
447 * @param $handler
448 * @return members
449 */
450 function webdav_members($handler) {
451 if (webdav_cache_routes() && $handler['cached'] && $data=cache_get('webdav_'.$handler['id'])) {
452 return $data->data;
453 }
454 if (webdav_is_debug()) webdav_debug("Getting members for ".webdav_format_route($handler['route'])." collection." );
455 $members = webdav_operation($handler, "members");
456 if (webdav_is_debug()) webdav_debug("We found ".count($members)." members.");
457 if (webdav_cache_routes() && $handler['cached']) {
458 cache_set('webdav_'.$handler['id'],$members, 'cache',CACHE_TEMPORARY);
459 }
460 return $members;
461 }
462
463
464
465
466
467
468 /**
469 * Invoke a hook on a collection owner.
470 *
471 * @param $handler target collection
472 * @param $operation
473 * @return hook result, null if nothing returned
474 */
475 function webdav_operation() {
476 $args=func_get_args();
477 $handler=array_shift($args);
478 $operation=array_shift($args);
479
480 if (webdav_is_debug()) webdav_debug("Invoking callbacks for '".$operation."' on ".webdav_format_route($handler['route_pattern']));
481
482 $single=$operation!='members';
483 $functions=array();
484 foreach($handler['definitions'] as $definition) {
485 if (isset($definition[$operation.' callback'])) {
486 if (function_exists($definition[$operation.' callback'])) {
487 $functions[]=$definition[$operation.' callback'];
488 }
489 }
490 }
491
492 if (count($functions)==0) {
493 if (webdav_is_debug(2)) {
494 webdav_debug("No callback defined for '".$operation."' with ".webdav_format_route($handler['route_pattern']));
495 }
496 webdav_session_set_status(WEBDAV_STATUS_NOT_IMPLEMENTED);
497 return;
498 }
499
500 if ($single && count($functions)>1) {
501 webdav_session_set_status(WEBDAV_STATUS_INTERNAL_SERVER_ERROR);
502 return;
503 }
504
505 $parameters=array();
506 array_unshift($args, $handler);
507 foreach ($args as $arg) {
508 if (is_array($arg['route'])) { // this is a handler !!
509 foreach ($arg['parameters'] as $parameter) {
510 $parameters[]=$parameter;
511 }
512 } else {
513 $parameters[]=$arg;
514 }
515 }
516
517 $result=array();
518 foreach ($functions as $function) {
519 $function_result= call_user_func_array($function, $parameters);
520 if (webdav_is_debug(2)) {
521 webdav_debug(" -> ".$function."(".webdav_format_parameters($parameters).") : ".webdav_format_var($function_result, false));
522 }
523 if ($single) {
524 return $function_result;
525 }
526 if (isset($function_result) && is_array($function_result)) {
527 $result = array_merge_recursive($result, $function_result);
528 }
529 }
530 return $result;
531 }
532
533
534
535
536 /**
537 * Implementation of hook_cron().
538 */
539 function webdav_cron() {
540 if (webdav_is_debug()) webdav_debug("CRON: cleaning webdav expired locks");
541 db_query("DELETE FROM {webdav_locks} WHERE expires < %d", time());
542 }
543
544 /**
545 * Implementation of hook_help().
546 */
547 function webdav_help($path, $arg = NULL) {
548 switch ($path) {
549 case 'admin/settings/dav':
550 return '<p>' . t('') . '</p>';
551 }
552 }
553
554 /**
555 * Implementation of hook_theme()
556 */
557 function webdav_theme() {
558 return array(
559 'webdav_listing' => array(
560 'arguments' => array('root' => NULL, 'items' => NULL),
561 'template' => 'webdav_listing',
562 ),
563 'webdav_page' => array(
564 'arguments' => array('root' => NULL, 'items' => NULL, 'listing'=>NULL),
565 'template' => 'webdav_page',
566 ),
567 );
568 }
569
570 /**
571 * Check if debug logging is enabled.
572 *
573 * @return true if enabled.
574 */
575 function webdav_http_compression() {
576 return variable_get("webdav_compression", true);
577 }
578
579 /**
580 * Look up an item from a collection. If no 'hook_webdav_item' can help us, we enumerate
581 * items from parents and search for its name.
582 *
583 * @param $handler handler used to search the item
584 * @return a resource
585 */
586 function webdav_resource($handler) {
587 if ($handler==false) {
588 return false;
589 }
590 // First we try to find a hook for item look up
591 if (webdav_is_debug()) webdav_debug("Searching ".webdav_format_route($handler['route'])." resource.");
592
593 // A very special case... the root resource
594 // TODO use a 'resource callback' is path handler for this.
595 $route=$handler['route'];
596 if (count($route)==1 && $route[0]=='root') {
597 return array (
598 'id' => '',
599 'type' => 'collection',
600 );
601 }
602
603 $item=false;
604 // We get parent name
605 $resource_id = array_pop($route);
606
607 if (webdav_is_debug()) webdav_debug("Searching '".$resource_id."' in ".webdav_format_route($route)." members.");
608 $parent_handler=webdav_route_resolve($route);
609 if ($parent_handler===false) {
610 return false;
611 }
612 $members = webdav_members($parent_handler);
613
614 foreach ($members as & $_item) {
615 if ($_item['id'] == $resource_id) {
616 $item=$_item;
617 break;
618 }
619 }
620
621 if (webdav_is_debug()) {
622 if ($item) {
623 webdav_debug("Lookup result is a '".$item['type']."' called '".$item['id']."'");
624 } else {
625 $item=false;
626 webdav_debug("No resource found...");
627 }
628 }
629 return $item;
630 }
631
632
633 /**
634 * Timeout for locks expriration.
635 *
636 * @return seconds
637 */
638
639 function webdav_lock_timeout() {
640 return variable_get("webdav_lock_timeout", 500);
641 }
642
643 /**
644 * Resolve a given arguments list according to a specific collection.
645 *
646 * @param $source route to resolve
647 * @param $path collection path
648 * @return If collection is resoled, an array of wildcards=>according argument; false else.
649 */
650 function webdav_is_route_matching($source, $handler) {
651 $route=$handler['route'];
652 if (webdav_is_debug(3)) webdav_debug(" + Checking ".webdav_format_route($route).' route handler.');
653 $result=array();
654 $match=true;
655
656 // Special case for "match all" ending pattern. We normalize it in order to
657 // get extra data and carry on with standard matching method
658 if (preg_match('/^%%([a-z_]+)$/', $route[count($route)-1], $matches)) {
659 array_pop($route); // we don't nee this any more
660 if (count($source)<(count($route)-1)) { // this will never happen...
661 return false;
662 }
663 $count=count($source)-count($route);
664 if ($count>0) { // we have data to store
665 $tmp=array();
666 for ($i=0; $i<$count; $i++) {
667 array_unshift($tmp, array_pop($source));
668 }
669 $result[$matches[1]]=implode("/",$tmp);
670 } else {
671 $result[$matches[1]]="/";
672 }
673 }
674
675 if (count($route)==count($source)) {
676 for ($i=0; $i < count($route); $i++) {
677 if (preg_match('/^%([a-z_]+)$/', $route[$i], $matches)) {
678 $result[$matches[1]]=$source[$i];
679 } elseif ($route[$i]!=$source[$i]) {
680 $match=false;
681 break;
682 }
683 }
684 if ($match) {
685 return $result;
686 }
687 }
688 return false;
689 }
690
691
692 function webdav_dump_structure($array, $ident="") {
693 foreach ($array as $key=>$value) {
694 $result.=$ident;
695 if (!is_numeric($key)) $result.="'".$key."' => ";
696 if (is_array($value)) {
697 $result.="array (\n".webdav_dump_structure($value, $ident." ");
698 $result.=$ident."),\n";
699 } elseif (is_object($value)) {
700 $result.="object,\n";
701 } else {
702 if (is_string($value)) {
703 $result.="'".$value."'";
704 } else {
705 $result.=$value;
706 }
707 $result.=",\n";
708 }
709 }
710 return $result;
711 }
712 function webdav_need_route_handlers() {
713 global $webdav_need_route_handlers;
714
715 if (empty($webdav_need_route_handlers)) {
716 if(webdav_cache_routes() && $data=cache_get('webdav_routes', 'cache', CACHE_PERMANENT)) {
717 $webdav_need_route_handlers=$data->data;
718 return $webdav_need_route_handlers;
719 }
720 if (webdav_is_debug()) webdav_debug(" Route handlers cache is empty : let's find all the mutants");
721 $by_path=array();
722 foreach (module_implements('webdav_definitions') as $module) {
723 $tmp=module_invoke($module,'webdav_definitions');
724 foreach ($tmp as $path=>$definition) {
725 $path=trim($path,"/");
726 $by_path[$path]['definitions'][]=$definition;
727
728 // we move owner data to root definition
729 // FIXME we can have conflict here...
730 foreach (array('module', 'access', 'arguments', 'cached') as $field) {
731 if (!isset($by_path[$path][$field])) {
732 if ($field=='module' && !isset($definition[$field])) {
733 $definition[$field]=$module;
734 }
735 if (!is_null($definition[$field])) {
736 $by_path[$path][$field]=$definition[$field];
737 unset($definition[$field]);
738 }
739 }
740 }
741 if (!isset($by_path[$path]['route'])) {
742 $by_path[$path]['route']=webdav_parse_path($path);
743 }
744 }
745 }
746 uasort($by_path, _webdav_collection_sorter); //
747 $webdav_need_route_handlers=array_values($by_path);
748 if (webdav_is_debug(2)) {
749 foreach ($webdav_need_route_handlers as $handler) {
750 webdav_debug(" + ".webdav_format_route($handler['route']));
751 if (webdav_is_debug(3)) {
752 foreach($handler["definitions"] as $definition) {
753 foreach ($definition as $key=>$value) {
754 webdav_debug(" - ".$key." = > ".webdav_format_var($value));
755 }
756 }
757 }
758 }
759 }
760 if (webdav_is_debug(2)) webdav_debug(" End of route handlers cache building");
761 }
762 if (webdav_cache_routes()) {
763 cache_set('webdav_routes',$webdav_need_route_handlers,'cache',CACHE_PERMANENT);
764 }
765 return $webdav_need_route_handlers;
766 }
767
768 function _webdav_collection_sorter($a, $b) {
769 return count($a['route'])-count($b['route']);
770 }
771
772 /**
773 * Try to resolve collection arguments with known collections as given by hook_webdav_collections.
774 *
775 * @param $route route to resolve
776 * @return if a route is found, an array containing route handler, else false.
777 */
778 function webdav_route_resolve($route, $level=0) {
779 for ($i=0; $i < $level; $i++) {
780 array_pop($route);
781 }
782 $handler_id=implode("_",$route);
783 global $webdav_resolved_routes;
784 if (isset($webdav_resolved_routes[$handler_id])) {
785 $result=$webdav_resolved_routes[$handler_id];
786 if (webdav_is_debug()) webdav_debug("Route resolved using cache : ".webdav_format_var($result));
787 return $result;
788 }
789
790 if (webdav_is_debug()) webdav_debug("Resolving ".webdav_format_route($route)."...");
791
792 $matches=array();
793 foreach (webdav_need_route_handlers() as $handler) {
794 $parameters = webdav_is_route_matching($route, $handler);
795 if ($parameters !== false) {
796 $match=$handler;
797 $match['parameters']=$parameters;
798 $match['route']=$route;
799 $match['route_pattern']=$handler['route'];
800 $matches[]=$match;
801 }
802 }
803
804 usort($matches, webdav_best_matches);
805
806 // search for the better match
807 if (webdav_is_debug(2)) {
808 webdav_debug("Resolved routes are (first is best) :");
809 foreach ($matches as $match) {
810 webdav_debug(" * ".webdav_format_route($match['route_pattern'])." : ".count($match['parameters']));
811 }
812 }
813
814 if (count($matches)>0) {
815 $result=$matches[0];
816 $parameters=array();
817 if (count($result['arguments'])>0) {
818 foreach ($result['arguments'] as $index) {
819 $index++; // we skip root
820 $item=$result['route_pattern'][$index];
821 $value=$result['route'][$index];
822 if ($item[0]=='%') {
823 $item=trim($item, '%');
824 $value=$result['parameters'][$item];
825 $loader=$result['module'].'_'.$item."_load";
826 if (function_exists($loader)) {
827 if (webdav_is_debug(2)) webdav_debug(" -> invoking ".$loader.'('.webdav_format_parameters(array($value)).")");
828 $loader_result = $loader($value);
829 if ($loader_result===false) {
830 if (webdav_is_debug()) webdav_debug("Error while loading '".$value."' with ".$loader);
831 return false;
832 } else {
833 $value=$loader_result;
834 }
835 }
836 }
837 $parameters[]=$value;
838 }
839 }
840 $result['parameters']=$parameters;
841 if (webdav_is_debug()) {
842 webdav_debug("Found a ".webdav_format_route($handler['route'])." handler owned by '".$result['module']."'");
843 if (webdav_is_debug(2)) {
844 foreach ($result['parameters'] as $key=>$value) {
845 webdav_debug(" * arguments['".$key."'] => ".webdav_format_var($value));
846 }
847 }
848 }
849 $result['id']=$handler_id;
850 $webdav_resolved_routes[$handler_id]=$result;
851 return $result;
852 }
853
854 if (webdav_is_debug()) {
855 webdav_debug("We didn't found any handler.");
856 }
857 $webdav_resolved_routes[$handler_id]=false;
858 return false;
859 }
860
861 function webdav_best_matches($a, $b) {
862 return count($a['parameters'])-count($b['parameters']);
863 }
864
865 /**
866 * output a debug message (syslog).
867 *
868 * @param $message message
869 */
870 function webdav_debug($message) {
871 $lines=explode("\n", $message);
872 foreach ($lines as $line) {
873 error_log($line);
874 }
875 }
876
877
878
879 /**
880 * Show closing session query, ainly used to log access in watchdog .
881 */
882 function webdav_debug_close_query(&$session){
883 global $session_time_start;
884 $time = round(1000*(microtime(true) - $session_time_start));
885 if (webdav_is_debug() ) {
886 webdav_debug("Closing session with ".(webdav_session_failed()?"error":"status")." : ".(empty($session->status)?"OK":$session->status));
887 webdav_debug('Request served in ' .$time." ms");
888 } else {
889 error_log($_SERVER['REQUEST_METHOD']." [".($_SERVER['HTTP_DEPTH']==''?'infinite':$_SERVER['HTTP_DEPTH'])."] ".$_SERVER['PATH_INFO']." : ".(empty($session->status)?"OK":$session->status)." (".$time." ms)");
890 }
891 if (webdav_watch_dog()) {
892 if ($_SERVER['REQUEST_METHOD']!="PROPFIND" && $_SERVER['REQUEST_METHOD']!="OPTIONS") {
893 watchdog('webdav', "WebDAV access to @query method<br/>status:@status", array(
894 '@query' => $_SERVER['REQUEST_METHOD'],
895 '@status' => $session->status,
896 )
897 ,webdav_session_failed()?WATCHDOG_ERROR:WATCHDOG_NOTICE);
898 }
899 }
900 }
901
902 /**
903 * Check if watchdog logging is enabled.
904 *
905 * @return true if enabled.
906 */
907 function webdav_watch_dog() {
908 return variable_get("webdav_watch_dog", false);
909 }
910
911 /**
912 * Check if watchdog logging is enabled.
913 *
914 * @return true if enabled.
915 */
916 function webdav_debug_level() {
917 return variable_get("webdav_debug_level", 1);
918 }
919 function webdav_format_route($array) {
920 $result="";
921 $iCount=0;
922 foreach ($array as $item) {
923 if ($result!="") {
924 $result.=", ";
925 }
926 $result.=$item;
927 $iCount++;
928 }
929 return "{ ".$result." }";
930 }
931
932 function webdav_format_var($var,$expand=true) {
933 if (is_object($var)) {
934 return 'OBJECT';
935 } elseif (is_string($var)) {
936 return !$expand?"STRING[".strlen($var)."]":"'".$var."'";
937 } elseif (is_bool($var)) {
938 return $var?"TRUE":"FALSE";
939 }elseif (is_array($var)) {
940 return "ARRAY[".count($var)."]";
941 }
942 return $var;
943 }
944
945 function webdav_format_parameters($array) {
946 $result="";
947 $iCount=0;
948 foreach ($array as $item) {
949 if ($result!="") {
950 $result.=", ";
951 }
952 $result.=webdav_format_var($item);
953 $iCount++;
954 }
955 return $result;
956 }
957 /**
958 * Check if debug logging is enabled.
959 *
960 * @return true if enabled.
961 */
962 function webdav_is_debug($level=1) {
963 return (webdav_debug_level()>=$level);
964 }
965
966
967 function webdav_set_output_type($type) {
968 global $webdav_drupal_content;
969 $webdav_drupal_content=$type;
970 if (webdav_is_debug(2)) webdav_debug("Selecting a new output type : ".$type);
971 }
972
973 function webdav_get_output_type() {
974 global $webdav_drupal_content;
975 return $webdav_drupal_content;
976 }
977
978 /**
979 * Originaly a file.inc Drupal function modified.
980 *
981 * @param unknown_type $filename
982 * @param unknown_type $mapping
983 */
984 function webdav_file_get_mimetype($filename, $mapping = NULL) {
985 if (!is_array($mapping)) {
986 $mapping = variable_get('mime_extension_mapping', array(
987 'ez' => 'application/andrew-inset',
988 'atom' => 'application/atom',
989 'atomcat' => 'application/atomcat+xml',
990 'atomsrv' => 'application/atomserv+xml',
991 'cap|pcap' => 'application/cap',
992 'cu' => 'application/cu-seeme',
993 'tsp' => 'application/dsptype',
994 'spl' => 'application/x-futuresplash',
995 'hta' => 'application/hta',
996 'jar' => 'application/java-archive',
997 'ser' => 'application/java-serialized-object',
998 'class' => 'application/java-vm',
999 'hqx' => 'application/mac-binhex40',
1000 'cpt' => 'image/x-corelphotopaint',
1001 'nb' => 'application/mathematica',
1002 'mdb' => 'application/msaccess',
1003 'doc|dot' => 'application/msword',
1004 'bin' => 'application/octet-stream',
1005 'oda' => 'application/oda',
1006 'ogg|ogx' => 'application/ogg',
1007 'pdf' => 'application/pdf',
1008 'key' => 'application/pgp-keys',
1009 'pgp' => 'application/pgp-signature',
1010 'prf' => 'application/pics-rules',
1011 'ps|ai|eps' => 'application/postscript',
1012 'rar' => 'application/rar',
1013 'rdf' => 'application/rdf+xml',
1014 'rss' => 'application/rss+xml',
1015 'rtf' => 'application/rtf',
1016 'smi|smil' => 'application/smil',
1017 'wpd' => 'application/wordperfect',
1018 'wp5' => 'application/wordperfect5.1',
1019 'xhtml|xht' => 'application/xhtml+xml',
1020 'xml|xsl' => 'application/xml',
1021 'zip' => 'application/zip',
1022 'cdy' => 'application/vnd.cinderella',
1023 'kml' => 'application/vnd.google-earth.kml+xml',
1024 'kmz' => 'application/vnd.google-earth.kmz',
1025 'xul' => 'application/vnd.mozilla.xul+xml',
1026 'xls|xlb|xlt' => 'application/vnd.ms-excel',
1027 'cat' => 'application/vnd.ms-pki.seccat',
1028 'stl' => 'application/vnd.ms-pki.stl',
1029 'ppt|pps' => 'application/vnd.ms-powerpoint',
1030 'odc' => 'application/vnd.oasis.opendocument.chart',
1031 'odb' => 'application/vnd.oasis.opendocument.database',
1032 'odf' => 'application/vnd.oasis.opendocument.formula',
1033 'odg' => 'application/vnd.oasis.opendocument.graphics',
1034 'otg' => 'application/vnd.oasis.opendocument.graphics-template',
1035 'odi' => 'application/vnd.oasis.opendocument.image',
1036 'odp' => 'application/vnd.oasis.opendocument.presentation',
1037 'otp' => 'application/vnd.oasis.opendocument.presentation-template',
1038 'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
1039 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template',
1040 'odt' => 'application/vnd.oasis.opendocument.text',
1041 'odm' => 'application/vnd.oasis.opendocument.text-master',
1042 'ott' => 'application/vnd.oasis.opendocument.text-template',
1043 'oth' => 'application/vnd.oasis.opendocument.text-web',
1044 'docm' => 'application/vnd.ms-word.document.macroEnabled.12',
1045 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
1046 'dotm' => 'application/vnd.ms-word.template.macroEnabled.12',
1047 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
1048 'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
1049 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
1050 'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
1051 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
1052 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
1053 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
1054 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
1055 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
1056 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
1057 'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
1058 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
1059 'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12',
1060 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
1061 'cod' => 'application/vnd.rim.cod',
1062 'mmf' => 'application/vnd.smaf',
1063 'sdc' => 'application/vnd.stardivision.calc',
1064 'sds' => 'application/vnd.stardivision.chart',
1065 'sda' => 'application/vnd.stardivision.draw',
1066 'sdd' => 'application/vnd.stardivision.impress',
1067 'sdf' => 'application/vnd.stardivision.math',
1068 'sdw' => 'application/vnd.stardivision.writer',
1069 'sgl' => 'application/vnd.stardivision.writer-global',
1070 'sxc' => 'application/vnd.sun.xml.calc',
1071 'stc' => 'application/vnd.sun.xml.calc.template',
1072 'sxd' => 'application/vnd.sun.xml.draw',
1073 'std' => 'application/vnd.sun.xml.draw.template',
1074 'sxi' => 'application/vnd.sun.xml.impress',
1075 'sti' => 'application/vnd.sun.xml.impress.template',
1076 'sxm' => 'application/vnd.sun.xml.math',
1077 'sxw' => 'application/vnd.sun.xml.writer',
1078 'sxg' => 'application/vnd.sun.xml.writer.global',
1079 'stw' => 'application/vnd.sun.xml.writer.template',
1080 'sis' => 'application/vnd.symbian.install',
1081 'vsd' => 'application/vnd.visio',
1082 'wbxml' => 'application/vnd.wap.wbxml',
1083 'wmlc' => 'application/vnd.wap.wmlc',
1084 'wmlsc' => 'application/vnd.wap.wmlscriptc',
1085 'wk' => 'application/x-123',
1086 '7z' => 'application/x-7z-compressed',
1087 'abw' => 'application/x-abiword',
1088 'dmg' => 'application/x-apple-diskimage',
1089 'bcpio' => 'application/x-bcpio',
1090 'torrent' => 'application/x-bittorrent',
1091 'cab' => 'application/x-cab',
1092 'cbr' => 'application/x-cbr',
1093 'cbz' => 'application/x-cbz',
1094 'cdf' => 'application/x-cdf',
1095 'vcd' => 'application/x-cdlink',
1096 'pgn' => 'application/x-chess-pgn',
1097 'cpio' => 'application/x-cpio',
1098 'csh' => 'text/x-csh',
1099 'deb|udeb' => 'application/x-debian-package',
1100 'dcr|dir|dxr' => 'application/x-director',
1101 'dms' => 'application/x-dms',
1102 'wad' => 'application/x-doom',
1103 'dvi' => 'application/x-dvi',
1104 'rhtml' => 'application/x-httpd-eruby',
1105 'flac' => 'application/x-flac',
1106 'pfa|pfb|gsf|pcf|pcf.Z' => 'application/x-font',
1107 'mm' => 'application/x-freemind',
1108 'gnumeric' => 'application/x-gnumeric',
1109 'sgf' => 'application/x-go-sgf',
1110 'gcf' => 'application/x-graphing-calculator',
1111 'gtar|tgz|taz' => 'application/x-compressed-tar', // YB
1112 'bz2'=>'application/x-bzip',
1113 'hdf' => 'application/x-hdf',
1114 'phtml|pht|php' => 'application/x-httpd-php',
1115 'phps' => 'application/x-httpd-php-source',
1116 'php3' => 'application/x-httpd-php3',
1117 'php3p' => 'application/x-httpd-php3-preprocessed',
1118 'php4' => 'application/x-httpd-php4',
1119 'ica' => 'application/x-ica',
1120 'ins|isp' => 'application/x-internet-signup',
1121 'iii' => 'application/x-iphone',
1122 'iso' => 'application/x-iso9660-image',
1123 'jnlp' => 'application/x-java-jnlp-file',
1124 'js' => 'application/x-javascript',
1125 'jmz' => 'application/x-jmol',
1126 'chrt' => 'application/x-kchart',
1127 'kil' => 'application/x-killustrator',
1128 'skp|skd|skt|skm' => 'application/x-koan',
1129 'kpr|kpt' => 'application/x-kpresenter',
1130 'ksp' => 'application/x-kspread',
1131 'kwd|kwt' => 'application/x-kword',
1132 'latex' => 'application/x-latex',
1133 'lha' => 'application/x-lha',
1134 'lyx' => 'application/x-lyx',
1135 'lzh' => 'application/x-lzh',
1136 'lzx' => 'application/x-lzx',
1137 'frm|maker|frame|fm|fb|book|fbdoc' => 'application/x-maker',
1138 'mif' => 'application/x-mif',
1139 'wmd' => 'application/x-ms-wmd',
1140 'wmz' => 'application/x-ms-wmz',
1141 'com|exe|bat|dll' => 'application/x-msdos-program',
1142 'msi' => 'application/x-msi',
1143 'nc' => 'application/x-netcdf',
1144 'pac' => 'application/x-ns-proxy-autoconfig',
1145 'nwc' => 'application/x-nwc',
1146 'o' => 'application/x-object',
1147 'oza' => 'application/x-oz-application',
1148 'p7r' => 'application/x-pkcs7-certreqresp',
1149 'crl' => 'application/x-pkcs7-crl',
1150 'pyc|pyo' => 'application/x-python-code',
1151 'qtl' => 'application/x-quicktimeplayer',
1152 'rpm' => 'application/x-redhat-package-manager',
1153 'sh' => 'text/x-sh',
1154 'shar' => 'application/x-shar',
1155 'swf|swfl' => 'application/x-shockwave-flash',
1156 'sit|sitx' => 'application/x-stuffit',
1157 'sv4cpio' => 'application/x-sv4cpio',
1158 'sv4crc' => 'application/x-sv4crc',
1159 'tar' => 'application/x-tar',
1160 'tcl' => 'application/x-tcl',
1161 'gf' => 'application/x-tex-gf',
1162 'pk' => 'application/x-tex-pk',
1163 'texinfo|texi' => 'application/x-texinfo',
1164 '~|%|bak|old|sik' => 'application/x-trash',
1165 't|tr|roff' => 'application/x-troff',
1166 'man' => 'application/x-troff-man',
1167 'me' => 'application/x-troff-me',
1168 'ms' => 'application/x-troff-ms',
1169 'ustar' => 'application/x-ustar',
1170 'src' => 'application/x-wais-source',
1171 'wz' => 'application/x-wingz',
1172 'crt' => 'application/x-x509-ca-cert',
1173 'xcf' => 'application/x-xcf',
1174 'fig' => 'application/x-xfig',
1175 'xpi' => 'application/x-xpinstall',
1176 'au|snd' => 'audio/basic',
1177 'mid|midi|kar' => 'audio/midi',
1178 'mpga|mpega|mp2|mp3|m4a' => 'audio/mpeg',