WebDav.class.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  1. <?php
  2. class WebDav{
  3. public $root,$folder,$logs,$version = 1,$user,$lockfile,$ignoreFiles = array();
  4. public $on = array(
  5. 'login' => null,
  6. 'log' => null,
  7. 'edit' => null,
  8. 'move' => null,
  9. 'delete' => null,
  10. 'list' => null,
  11. 'properties' => null,
  12. 'copy' => null
  13. );
  14. public $do = array(
  15. 'edit' => null,
  16. 'move' => null,
  17. 'delete' => null,
  18. 'list' => null,
  19. 'copy' => null
  20. );
  21. public static function logPath() {
  22. return __DIR__.SLASH.'logs';
  23. }
  24. function start(){
  25. $server = $_SERVER;
  26. $method = strtoupper($server['REQUEST_METHOD']);
  27. $headers = getallheaders();
  28. $body = file_get_contents("php://input");
  29. $requestUri = explode('/',$server['REQUEST_URI']);
  30. $requestUri = array_filter($requestUri);
  31. $requestUri = '/'.implode('/',$requestUri);
  32. $server['REQUEST_URI'] = $requestUri;
  33. $log = PHP_EOL.PHP_EOL.'---------- '.$method.' --------------';
  34. $log .= PHP_EOL.'Fichier : '.$requestUri;
  35. foreach($headers as $key => $header){
  36. $log .= PHP_EOL."\t - ".$key.' : '.$header;
  37. }
  38. $log .= PHP_EOL."\t === ".'Requête : '.PHP_EOL. $body;
  39. $this->logit($log);
  40. if(!file_exists(self::logPath()))
  41. mkdir(self::logPath(),0755,true);
  42. if($method!='OPTIONS' && isset($this->do['login']) ){
  43. try{
  44. $function = $this->do['login'];
  45. $user = isset($server['PHP_AUTH_USER']) ? $server['PHP_AUTH_USER']: '';
  46. $password = isset($server['PHP_AUTH_PW']) ? $server['PHP_AUTH_PW']: '';
  47. $header = apache_request_headers();
  48. if(isset($header['Authorization']) && $header['Authorization']!=''){
  49. $authorization = explode(' ',$header['Authorization']);
  50. if(count($authorization)==2){
  51. $credentials = explode(':',base64_decode($authorization[1]));
  52. if(isset($credentials[0])) $user = $credentials[0];
  53. if(isset($credentials[1])) $password = $credentials[1];
  54. }
  55. }
  56. $user = $user =='' ? 'anonymous': $user;
  57. $password = $password =='' ? 'anonymous': $password;
  58. $this->user = $function($user,$password);
  59. }catch(Exception $e){
  60. $this->logit('REQUEST URI : '.$requestUri.PHP_EOL.'REQUEST HEADERS : '.json_encode($headers));
  61. $this->logit('Error '.self::headerFromCode(401).' for -'. $user.' :'.$e->getCode().' '.$e->getMessage() );
  62. $this->logit(json_encode($_SERVER,JSON_PRETTY_PRINT));
  63. $header = apache_request_headers();
  64. $header = $this->logit(json_encode($header,JSON_PRETTY_PRINT));
  65. $header = $this->logit(json_encode($_REQUEST,JSON_PRETTY_PRINT));
  66. self::emit_header($e->getCode());
  67. if($e->getCode()==401)
  68. header('WWW-Authenticate: Basic realm="Software Webdav gate"');
  69. echo $e->getMessage();
  70. exit();
  71. }
  72. }
  73. try{
  74. switch($method) {
  75. //Returns a list of properties of a file, a directory of a directory and its files.
  76. case 'PROPFIND':
  77. $log= "\t === ".'Réponse : '.PHP_EOL;
  78. try{
  79. $depth = isset($headers['Depth']) ? $headers['Depth'] : 0;
  80. if(in_array(mt_basename($server['REQUEST_URI']), $this->ignoreFiles)){
  81. $this->logit('File in blacklist ignored'.PHP_EOL);
  82. return self::emit_header(404);
  83. }
  84. $scanned = $this->folder.$this->get_path($server);
  85. $files = array();
  86. if(isset($this->do['list']) ){
  87. $function = $this->do['list'];
  88. $files = $function($scanned,$depth);
  89. }
  90. $locks = $this->lock();
  91. $xml = '';
  92. foreach ($files as $i=>$file) {
  93. $url = $requestUri.'/';
  94. //Si on est pas sur le premier dossier/fichier d'origine, c'est qu'on est sur un enfant du dossier, on concatene son url au dossier
  95. if($i!=0) $url .= rawurlencode($file['label']);
  96. $xml .= self::xml_file($url,$file,$locks);
  97. }
  98. $stream = '<D:multistatus xmlns:D="DAV:">'.$xml.'</D:multistatus>';
  99. self::emit_header(207);
  100. header('DAV: '.$this->version);
  101. header('Content-Type: text/xml');
  102. echo $stream;
  103. $log.= "\t".'DAV: '.$this->version.PHP_EOL;
  104. $log.= "\t\t".'Content-Type: text/xml'.PHP_EOL;
  105. $log.= "\t\t".$stream.PHP_EOL;
  106. $this->logit($log);
  107. }catch(Exception $e){
  108. $log.= "\t".$e->getCode().' : '.$e->getMessage().PHP_EOL;
  109. $this->logit($log);
  110. self::emit_header($e->getCode());
  111. }
  112. break;
  113. //Récuperation d'un fichier (ou dossier ?)
  114. case 'GET':
  115. $log= "\t === ".'Réponse : '.PHP_EOL;
  116. try{
  117. $file = $this->folder.$this->get_path($server);
  118. $this->logit('Download '.$file);
  119. if(!isset($this->do['download']) ) throw new Exception("Not implemented", 501);
  120. $function = $this->do['download'];
  121. $stream = $function($file);
  122. self::emit_header(200);
  123. header('Content-Length: '.strlen($stream));
  124. echo $stream;
  125. $log.= "\t".'- 200 OK'.PHP_EOL;
  126. $log.= "\t".'- Content-Length: '.strlen($stream);
  127. $log.= "\t\t".$stream.PHP_EOL;
  128. $this->logit($log);
  129. }catch(Exception $e){
  130. $log.= "\t".'- '.$e->getCode().' '.$e->getMessage().PHP_EOL;
  131. $this->logit($log);
  132. self::emit_header($e->getCode());
  133. header('Content-Type: text/xml');
  134. }
  135. break;
  136. //Envoi d'un fichier
  137. case 'PUT':
  138. $log= "\t === ".'Réponse : '.PHP_EOL;
  139. try{
  140. $target = $this->folder.$this->get_path($server);
  141. $this->logit('PUT '.$target);
  142. if(!isset($this->do['edit']) ) throw new Exception("Not implemented", 501);
  143. $function = $this->do['edit'];
  144. $function($target, $body,'file');
  145. self::emit_header(201);
  146. $log.= "\t".'- 201 Created'.PHP_EOL;
  147. $this->logit($log);
  148. }catch(Exception $e){
  149. $log.= "\t".'- '.$e->getCode().' '.$e->getMessage().PHP_EOL;
  150. $this->logit($log);
  151. self::emit_header($e->getCode());
  152. }
  153. break;
  154. //Suppression dossier/fichier
  155. case 'DELETE':
  156. $log= "\t === ".'Réponse : '.PHP_EOL;
  157. try{
  158. $path = $this->folder.$this->get_path($server);
  159. if(!isset($this->do['delete']) ) throw new Exception("Error Processing Request", 501);
  160. $function = $this->do['delete'];
  161. $function($path);
  162. self::emit_header(200);
  163. }catch(Exception $e){
  164. $log.= "\t".'- '.$e->getCode().' '.$e->getMessage().PHP_EOL;
  165. $this->logit($log);
  166. self::emit_header($e->getCode());
  167. }
  168. break;
  169. //Déplacement/renommage dossier/fichier
  170. case 'MOVE':
  171. $log= "\t === ".'Réponse : '.PHP_EOL;
  172. try{
  173. $target = $this->folder.$this->get_path($server);
  174. $toUrl = rawurldecode($server['HTTP_DESTINATION']);
  175. $toUrl = parse_url($toUrl, PHP_URL_PATH);
  176. $this->logit('Move to '.$toUrl);
  177. $to = $this->folder.str_replace('/',SLASH,utf8_decode(str_replace($this->root,'',$toUrl)));
  178. $to = substr($to, -1,1) == SLASH ? substr($to, 0,strlen($to)-1): $to;
  179. $this->logit('Move '.$target.' to '.$to.' (url :'.$toUrl.')');
  180. if(!isset($this->do['move']) ) throw new Exception("Not implemented", 501);
  181. $function = $this->do['move'];
  182. $function($target,$to);
  183. self::emit_header(200);
  184. }catch(Exception $e){
  185. $log.= "\t".'- '.$e->getCode().' '.$e->getMessage().PHP_EOL;
  186. $this->logit($log);
  187. self::emit_header($e->getCode());
  188. }
  189. break;
  190. //Creation de dossier
  191. case 'MKCOL':
  192. $log= "\t === ".'Réponse : '.PHP_EOL;
  193. try{
  194. $path = $this->folder.$this->get_path($server);
  195. $this->logit('Create folder '.$path);
  196. if(isset($this->do['edit']) ){
  197. $function = $this->do['edit'];
  198. $function($path, '','create','directory');
  199. self::emit_header(200);
  200. break;
  201. }
  202. self::emit_header(200);
  203. mkdir($path,0755,true);
  204. }catch(Exception $e){
  205. $log.= "\t".'- '.$e->getCode().' '.$e->getMessage().PHP_EOL;
  206. $this->logit($log);
  207. self::emit_header($e->getCode());
  208. }
  209. break;
  210. //The OPTIONS method allows an http client to find out what HTTP methods are supported on a specific url.
  211. case 'OPTIONS':
  212. $log= "\t === ".'Réponse : '.PHP_EOL;
  213. //header('Allows: options get head post delete trace propfind proppatch copy mkcol put lock unlock');
  214. if (ob_get_length()) ob_end_clean();
  215. header('Allows: options get head delete propfind copy mkcol put lock unlock proppatch');
  216. $log.= "\t".'- Allows: options get head delete propfind copy mkcol put lock unlock proppatch'.PHP_EOL;
  217. $this->logit($log);
  218. break;
  219. case 'HEAD':
  220. $log= "\t === ".'Réponse : '.PHP_EOL;
  221. self::emit_header(200);
  222. $log.= "\t".'- 200 Ok'.PHP_EOL;
  223. $this->logit($log);
  224. break;
  225. case 'POST':
  226. $log= "\t === ".'Réponse : '.PHP_EOL;
  227. self::emit_header(501);
  228. $log.= "\t".'- 501 Not implemented'.PHP_EOL;
  229. $this->logit($log);
  230. break;
  231. case 'TRACE':
  232. $log= "\t === ".'Réponse : '.PHP_EOL;
  233. self::emit_header(501);
  234. $log.= "\t".'- 501 Not implemented'.PHP_EOL;
  235. $this->logit($log);
  236. break;
  237. //Updates properties of a resource or collection.
  238. case 'PROPPATCH':
  239. $log= "\t === ".'Réponse : '.PHP_EOL;
  240. $properties = array();
  241. $namespaces = array();
  242. $ns = '';
  243. preg_match_all('|xmlns:([^\=]*)="([^"]*)"|iUs', $body, $matches,PREG_SET_ORDER);
  244. foreach ($matches as $match) {
  245. if(count($match)!=3) continue;
  246. $namespaces[$match[1]] = $match[2];
  247. $ns.=' xmlns:'.$match[1].' = "'.$match[2].'" ';
  248. }
  249. preg_match('|<D:prop>(.*)<\/D:prop>|iUs', $body, $matches);
  250. if(isset($matches[1])){
  251. preg_match_all('|<([^:]*):([^\>]*)>([^\<]*)<\/\1:\2>|iUs', $matches[1], $matches,PREG_SET_ORDER);
  252. foreach ($matches as $match) {
  253. if(count($match)==4)
  254. $properties[$match[2]] = array('ns'=>$match[1],'value'=>$match[3],'key'=>$match[2]);
  255. }
  256. }
  257. $this->logit('Set properties '.json_encode($properties,JSON_PRETTY_PRINT));
  258. if(isset($this->on['properties']) ){
  259. self::emit_header(200);
  260. $function = $this->do['properties'];
  261. $function($properties,$namespaces);
  262. break;
  263. }
  264. $response = '<?xml version="1.0" encoding="utf-8"?>
  265. <d:multistatus xmlns:d="DAV:" '.$ns.'>
  266. <d:response>
  267. <d:href>/</d:href>
  268. <d:propstat>
  269. <d:status>HTTP/1.1 200 OK</d:status>
  270. <d:prop>
  271. ';
  272. foreach ($properties as $key => $value) {
  273. $response .= '<'.$value['ns'].':'.$value['key'].' />';
  274. }
  275. $response .= '
  276. </d:prop>
  277. </d:propstat>
  278. </d:response>
  279. </d:multistatus>';
  280. $this->logit('[RESPONSE]'.PHP_EOL.$response);
  281. self::emit_header(207);
  282. header('Content-Type: text/xml');
  283. echo $response;
  284. $log.= "\t".'- 207 Multi status'.PHP_EOL;
  285. $log.= "\t".'- Content-Type: text/xml'.PHP_EOL;
  286. $log.= "\t\t".$response.PHP_EOL;
  287. $this->logit($log);
  288. break;
  289. //Copie d'un élement vers un nouvel emplacement
  290. case 'COPY':
  291. $log= "\t === ".'Réponse : '.PHP_EOL;
  292. try{
  293. $target = $this->folder.$this->get_path($server);
  294. $toUrl = rawurldecode($server['HTTP_DESTINATION']);
  295. $toUrl = parse_url($toUrl, PHP_URL_PATH);
  296. $to = $this->folder.str_replace('/',SLASH,utf8_decode(str_replace($this->root,'',$toUrl)));
  297. $this->logit('Copy '.$target.' to '.$to.' (url :'.$toUrl.')');
  298. if(!isset($this->do['copy'])) throw new Exception("Not implemented", 501);
  299. $function = $this->do['copy'];
  300. $function($target,$to);
  301. self::emit_header(200);
  302. $log.= "\t".'- 200 OK'.PHP_EOL;
  303. $this->logit($log);
  304. }catch(Exception $e){
  305. $log.= "\t".'- '.$e->getCode().' '.$e->getMessage().PHP_EOL;
  306. $this->logit($log);
  307. self::emit_header($e->getCode());
  308. }
  309. break;
  310. //Verouillage d'un élement
  311. case 'LOCK':
  312. $log= "\t === ".'Réponse : '.PHP_EOL;
  313. try{
  314. $target = $this->folder.$this->get_path($server);
  315. $response = 200;
  316. $locks = $this->lock();
  317. //Renouvellement de lock ou demande par un autre user
  318. if(isset($locks[base64_encode($requestUri)])){
  319. $lock = $locks[base64_encode($requestUri)];
  320. if($lock['owner']!=$this->user->login) {
  321. $response = 423;
  322. }else{
  323. $properties = array(
  324. 'owner'=>$this->user->login,
  325. 'created'=>time(),
  326. 'depth'=>isset($headers['Depth']) ? $headers['Depth'] : 'Infinity',
  327. 'timeout'=>isset($headers['Timeout']) ? $headers['Timeout'] : 'Second-3600',
  328. );
  329. $lock = $this->lock($requestUri,$properties);
  330. }
  331. //Création d'un nouveau lock
  332. }else{
  333. $properties = array(
  334. 'owner'=>$this->user->login,
  335. 'created'=>time(),
  336. 'depth'=>isset($headers['Depth']) ? $headers['Depth'] : 'Infinity',
  337. 'timeout'=>isset($headers['Timeout']) ? $headers['Timeout'] : 'Second-3600',
  338. );
  339. $lock = $this->lock($requestUri,$properties);
  340. }
  341. self::emit_header($response);
  342. header('Lock-Token: <'.$lock['token'].'>');
  343. $requestUri = str_replace('&','%26',$requestUri);
  344. $xml = '<?xml version="1.0" encoding="utf-8" ?>
  345. <D:prop xmlns:D="DAV:">
  346. <D:lockdiscovery>
  347. <D:activelock>
  348. <D:locktype><D:write/></D:locktype>
  349. <D:lockscope><D:exclusive/></D:lockscope>
  350. <D:depth>'.$lock['depth'].'</D:depth>
  351. <D:owner>
  352. <D:href>'.$lock['owner'].'</D:href>
  353. </D:owner>
  354. <D:timeout>'.$lock['timeout'].'</D:timeout>
  355. <D:locktoken>
  356. <D:href>'.$lock['token'].'</D:href>
  357. </D:locktoken>
  358. <D:lockroot>
  359. <D:href>'.$requestUri.'</D:href>
  360. </D:lockroot>
  361. </D:activelock>
  362. </D:lockdiscovery>
  363. </D:prop>';
  364. echo $xml;
  365. $log.= "\t".'- '.$response.' '.self::headerFromCode($response).PHP_EOL;
  366. $log.= "\t".'- Lock-Token: <'.$lock['token'].'>'.PHP_EOL;
  367. $log.= "\t\t".$xml.PHP_EOL;
  368. $this->logit($log);
  369. }catch(Exception $e){
  370. $log.= "\t".'- '.$e->getCode().' '.$e->getMessage().PHP_EOL;
  371. $this->logit($log);
  372. self::emit_header($e->getCode());
  373. }
  374. break;
  375. //Déverouillage d'un élement
  376. case 'UNLOCK':
  377. $log= "\t === ".'Réponse : '.PHP_EOL;
  378. try{
  379. $target = $this->folder.$this->get_path($server);
  380. $token = isset($headers['Lock-Token']) ? $headers['Lock-Token'] : 'no-token';
  381. $token = preg_replace('|[\<\>]|is','',$token);
  382. $locks = $this->lock();
  383. if(isset($locks[base64_encode($requestUri)])){
  384. if($locks[base64_encode($requestUri)]['token'] == $token){
  385. $this->lock($requestUri,false);
  386. }
  387. }
  388. self::emit_header(204);
  389. $log.= "\t".'- 204 No Content'.PHP_EOL;
  390. $this->logit($log);
  391. }catch(Exception $e){
  392. $log.= "\t".'- '.$e->getCode().' '.$e->getMessage().PHP_EOL;
  393. $this->logit($log);
  394. self::emit_header($e->getCode());
  395. }
  396. break;
  397. }
  398. }catch(Exception $e){
  399. self::emit_header(500);
  400. }
  401. exit();
  402. }
  403. //Méthode de gestion du manifest des locks fichiers (ajout, supression, listing)
  404. // Si properties est a false : supression d'un verrou pour 'file'
  405. // Si file + properties est renseigné : Création d'un verrou pour 'file' avec ces propriétés
  406. // Si file et properties sont a vide : on retourne juste la liste des verrou non expirés
  407. public function lock($file=null,$properties = array()){
  408. if(!file_exists($this->lockfile)) file_put_contents($this->lockfile,json_encode(array()));
  409. $locks = json_decode(file_get_contents($this->lockfile),true);
  410. foreach($locks as $key=>$lock){
  411. $timeoutInfos = explode('-',$lock['timeout']);
  412. //Calcul du délais d'expiration d'un verrou
  413. $secondNumber = 60;
  414. if(count($timeoutInfos)==2){
  415. list($unity,$number) = $timeoutInfos;
  416. $secondNumber = $number;
  417. if($unity == 'Minut') $secondNumber *= 60;
  418. if($unity == 'Hour') $secondNumber *= 3600;
  419. if($unity == 'Day') $secondNumber *= 86400;
  420. }
  421. //Supression du verrou fichier si expiré
  422. if(time() <= ($secondNumber + $lock['created'])) continue;
  423. unset($locks[$key]);
  424. }
  425. //Si aucun parametre n'est spécifié, on retourne la liste des verrous
  426. if($file==null) return $locks;
  427. //Si les properties sont a false, on supprime le verrou
  428. if($properties===false){
  429. unset($locks[base64_encode($file)]);
  430. }else{
  431. //Si les properties du verrou sont renseigné, on le save
  432. $properties['token'] = isset($properties['token']) && $properties['token'] != '' ? $properties['token'] : 'fr.sys1:'.sha1($file);
  433. $locks[base64_encode($file)] = $properties;
  434. }
  435. file_put_contents($this->lockfile, json_encode($locks));
  436. return !$properties ? '': $locks[base64_encode($file)];
  437. }
  438. /*
  439. create_time : timestamp de création
  440. update_time : timestamp dernière modification
  441. length : taille du dossier ou du fichier
  442. type : directory/file
  443. */
  444. public static function xml_file($url,$fileInfos,$locks){
  445. if(!isset($fileInfos['create_time'])) $fileInfos['create_time'] = time();
  446. if(!isset($fileInfos['update_time'])) $fileInfos['update_time'] = time();
  447. if(!isset($fileInfos['length'])) $fileInfos['length'] = 0;
  448. $fileInfos['create_time'] = $fileInfos['create_time'] - (3600*2);
  449. $fileInfos['update_time'] = $fileInfos['update_time'] - (3600*2);
  450. $fileInfos['update_time'] = date('D, j M Y H:i:s', $fileInfos['update_time']).' GMT';
  451. $fileInfos['create_time'] = date('D, j M Y H:i:s', $fileInfos['create_time']).' GMT';
  452. $url = str_replace('&','%26',$url);
  453. $xml = '<D:response>
  454. <D:href>'.$url.'</D:href>';
  455. $xml .= '<D:propstat>
  456. <D:prop>';
  457. if($fileInfos['type']=='directory'){
  458. $xml .='<D:resourcetype>
  459. <D:collection/>
  460. </D:resourcetype>';
  461. }else{
  462. $xml .='<D:resourcetype/>';
  463. $xml .='<D:supportedlock>
  464. <D:lockentry>
  465. <D:lockscope><D:exclusive/></D:lockscope>
  466. <D:locktype><D:write/></D:locktype>
  467. </D:lockentry>
  468. </D:supportedlock>';
  469. }
  470. if(isset($locks[base64_encode($url)])){
  471. $lock = $locks[base64_encode($url)];
  472. //Envoi du verrou au client si un verrou est en place
  473. $xml .='<D:lockdiscovery>
  474. <D:activelock>
  475. <D:locktype><D:write/></D:locktype>
  476. <D:lockscope><D:exclusive/></D:lockscope>
  477. <D:depth>'.$lock['depth'].'</D:depth>
  478. <D:owner>'.$lock['owner'].'</D:owner>
  479. <D:timeout>'.$lock['timeout'].'</D:timeout>
  480. <D:locktoken>
  481. <D:href>'.$lock['token'].'</D:href>
  482. </D:locktoken>
  483. </D:activelock>
  484. </D:lockdiscovery>';
  485. }
  486. if(isset($fileInfos['length']))
  487. $xml.= '<D:getcontentlength>'.$fileInfos['length'].'</D:getcontentlength>';
  488. $xml .='
  489. <D:getetag>"'.sha1($fileInfos['update_time']).'"</D:getetag>
  490. <D:getlastmodified>'.$fileInfos['update_time'].'</D:getlastmodified>
  491. <D:creationdate>'.$fileInfos['create_time'].'</D:creationdate>
  492. </D:prop>
  493. <D:status>HTTP/1.1 200 OK</D:status>
  494. </D:propstat>';
  495. if(!isset($fileInfos['length'])){
  496. $xml .= '<D:propstat>
  497. <D:prop>
  498. <D:getcontentlength/>
  499. </D:prop>
  500. <D:status>HTTP/1.1 404 Not Found</D:status>
  501. </D:propstat>';
  502. }
  503. $xml .= '</D:response>';
  504. return $xml;
  505. }
  506. function get_path($server){
  507. $path = $this->get_parameter($server['REQUEST_URI']);
  508. $path = rawurldecode(utf8_decode($path));
  509. return str_replace('/',SLASH,$path);
  510. }
  511. public function get_parameter($parameter){
  512. $path = rawurldecode($parameter);
  513. $path = str_replace('\\', '/', $path);
  514. $path = str_replace('//', '/', $path);
  515. $path = substr($path,strlen($this->root));
  516. if(substr($path, -1)=='/') $path = substr($path, 0,-1);
  517. return $path;
  518. }
  519. function logit($message){
  520. if(isset($this->on['log']) ){
  521. $function = $this->on['log'];
  522. $function($message);
  523. }
  524. if($this->logs=='') return;
  525. file_put_contents($this->logs, $message.PHP_EOL,FILE_APPEND);
  526. }
  527. public static function emit_header($code){
  528. if (ob_get_length()) ob_end_clean();
  529. header(self::headerFromCode($code));
  530. }
  531. public static function headerFromCode($code){
  532. $codes = array(
  533. 200 => 'HTTP/1.1 200 Ok',
  534. 201 => 'HTTP/1.1 201 Created',
  535. 204 => 'HTTP/1.1 204 No Content',
  536. 207 => 'HTTP/1.1 207 Multi-Status',
  537. 401 => 'HTTP/1.0 401 Unauthorized',
  538. 403 => 'HTTP/1.1 403 Forbidden',
  539. 404 => 'HTTP/1.1 404 Not Found',
  540. 406 => 'HTTP/1.1 406 Not acceptable',
  541. 409 => 'HTTP/1.1 409 Conflict',
  542. 415 => 'HTTP/1.1 415 Unsupported Media Type',
  543. 423 => 'HTTP/1.1 423 Locked',
  544. 500 => 'HTTP/1.1 500 Internal Server Error',
  545. 501 => 'HTTP/1.1 501 Method not implemented',
  546. );
  547. return isset($codes[$code]) ? $codes[$code] : $codes[500];
  548. }
  549. }
  550. ?>