Parent Directory
|
Revision Log
|
Revision Graph
#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', |