PageRenderTime 54ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/inc/bx/popoon/components/readers/webdav/bx.php

https://github.com/chregu/fluxcms
PHP | 414 lines | 216 code | 69 blank | 129 comment | 59 complexity | 01386e5f68819db188875e86d80a0ece MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, Apache-2.0, LGPL-2.1
  1. <?php
  2. // +----------------------------------------------------------------------+
  3. // | Bx |
  4. // +----------------------------------------------------------------------+
  5. // | Copyright (c) 2001-2007 Liip AG |
  6. // +----------------------------------------------------------------------+
  7. // | Licensed under the Apache License, Version 2.0 (the "License"); |
  8. // | you may not use this file except in compliance with the License. |
  9. // | You may obtain a copy of the License at |
  10. // | http://www.apache.org/licenses/LICENSE-2.0 |
  11. // | Unless required by applicable law or agreed to in writing, software |
  12. // | distributed under the License is distributed on an "AS IS" BASIS, |
  13. // | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
  14. // | implied. See the License for the specific language governing |
  15. // | permissions and limitations under the License. |
  16. // +----------------------------------------------------------------------+
  17. // | Author: Liip AG <devel@liip.ch> |
  18. // +----------------------------------------------------------------------+
  19. /**
  20. * Filesystem access using WebDAV
  21. *
  22. * @access public
  23. */
  24. class HTTP_WebDAV_Server_bx extends HTTP_WebDAV_Server_Filesystem_MDB2
  25. {
  26. // request resource
  27. private $_bxResource=null;
  28. /**
  29. * Serve a webdav request
  30. *
  31. * @access public
  32. * @param string
  33. */
  34. function ServeRequest($base = false)
  35. {
  36. // set root directory, defaults to webserver document root if not set
  37. if ($base) {
  38. $this->base = realpath($base); // TODO throw if not a directory
  39. } else if(!$this->base) {
  40. $this->base = $_SERVER['DOCUMENT_ROOT'];
  41. }
  42. $this->db = $GLOBALS['POOL']->db;
  43. if (MDB2::isError($this->db)) {
  44. throw new PopoonDBException( $this->db);
  45. }
  46. //mysql_select_db($this->db_name) or die(mysql_error());
  47. // TODO throw on connection problems
  48. $old = file_get_contents(BX_DATA_DIR.$this->_urldecode(!empty($_SERVER["PATH_INFO"]) ? $_SERVER["PATH_INFO"] : "/"));
  49. // let the base class do all the work
  50. parent::ServeRequest();
  51. // trigger onSave() handler
  52. $method = strtolower($_SERVER["REQUEST_METHOD"]);
  53. if ($method == 'put' && (int) $this->_http_status >=200 && (int) $this->_http_status <205) {
  54. if ($this->_bxResource && method_exists($this->_bxResource, 'onSave')) {
  55. $this->_bxResource->onSave($old);
  56. }
  57. }
  58. }
  59. /**
  60. * GET method handler
  61. *
  62. * @param array parameter passing array
  63. * @return bool true on success
  64. */
  65. function GET(&$options)
  66. {
  67. // get absolute fs path to requested resource
  68. $fulluri = $options["path"];
  69. $mode = "output";
  70. // get language and strip it from fulluri
  71. list($fulluri, $lang) = bx_collections::getLanguage($fulluri);
  72. $GLOBALS['POOL']->config->setOutputLanguage($lang);
  73. $parts = bx_collections::getCollectionAndFileParts($fulluri,$mode);
  74. $collection = $parts['coll'] ;
  75. $filename = $parts['name'];
  76. $ext = $parts['ext'];
  77. $id = $filename.".".$ext;
  78. if($collection === FALSE) {
  79. throw new BxPageNotFoundException($fulluri);
  80. return false;
  81. } else {
  82. $fspath = $collection->getContentUriById($id,true);
  83. }
  84. // sanity check
  85. if (!file_exists($fspath)) {
  86. throw new BxPageNotFoundException($fulluri);
  87. return false;
  88. }
  89. // detect resource type
  90. $options['mimetype'] = $this->_mimetype($fspath);
  91. // detect modification time
  92. // see rfc2518, section 13.7
  93. // some clients seem to treat this as a reverse rule
  94. // requiering a Last-Modified header if the getlastmodified header was set
  95. $options['mtime'] = filemtime($fspath);
  96. // detect resource size
  97. // no need to check result here, it is handled by the base class
  98. $options['stream'] = fopen($fspath, "r");
  99. $fstat = fstat( $options['stream'] );
  100. if ($fstat['size'] > 0) {
  101. $options['size'] = $fstat['size'];
  102. }
  103. return true;
  104. }
  105. function _mimetype($fspath) {
  106. $mimetype= popoon_helpers_mimetypes::getFromFileLocation($fspath);
  107. return $mimetype."; charset=utf-8" ;
  108. }
  109. /**
  110. * PROPFIND method handler
  111. *
  112. * @param array general parameter passing array
  113. * @param array return array for file properties
  114. * @return bool true on success
  115. */
  116. function PROPFIND(&$options, &$files)
  117. {
  118. // get absolute fs path to requested resource
  119. $fspath = $this->base . $options["path"];
  120. $path = "";
  121. if (strpos($options['path'],".") === false && substr($options['path'],-1) != "/") {
  122. $options['path'] .= "/";
  123. }
  124. // bx_helpers_debug::dump_errorlog($options);
  125. $p = bx_collections::getCollectionAndFileParts($options['path'],"output");
  126. $coll = $p['coll'];
  127. // prepare property array
  128. $files["files"] = array();
  129. // if we have a filename, then it's not a request for a collection, but a sub resource of this collection
  130. if ($coll && !($coll->mock)) {
  131. $files["files"][] = $this->fileinfo($path, $coll);
  132. } else {
  133. return false;
  134. }
  135. // information for contained resources requested?
  136. if (!empty($options["depth"]) && ($coll instanceof bx_collection)) {
  137. // TODO check for is_dir() first?
  138. // make sure path ends with '/'
  139. if (substr($options["path"],-1) !== "/") {
  140. $options["path"] .= "/";
  141. }
  142. foreach ($coll->getChildren($p['rawname']) as $child) {
  143. $files["files"][] = $this->fileinfo($options["path"],$child);
  144. }
  145. }
  146. // ok, all done
  147. //bx_helpers_debug::dump_errorlog($files);
  148. return true;
  149. }
  150. /**
  151. * PROPPATCH method handler
  152. *
  153. * @param array general parameter passing array
  154. * @return bool true on success
  155. */
  156. function proppatch(&$options)
  157. {
  158. global $prefs, $tab;
  159. $msg = "";
  160. $path = $options["path"];
  161. $dir = dirname($path)."/";
  162. $base = basename($path);
  163. foreach($options["props"] as $key => $prop) {
  164. if($prop['ns'] == "DAV:") {
  165. $options["props"][$key][$status] = "403 Forbidden";
  166. } else {
  167. //FIXME.. here we should call the res->setProperty
  168. bx_resourcemanager::setProperty($options['path'],$prop['name'],$prop['val'], $prop['ns']);
  169. }
  170. }
  171. return "";
  172. }
  173. /**
  174. * MKCOL method handler
  175. *
  176. * @param array general parameter passing array
  177. * @return bool true on success
  178. */
  179. function MKCOL($options)
  180. {
  181. $path = $options["path"];
  182. //$path = "/s";
  183. $parent = dirname($path);
  184. if ($parent != "/"){
  185. $parent .= '/';
  186. }
  187. $name = basename($path);
  188. $coll = bx_collections::getCollection($parent);
  189. $coll->makeCollection($name);
  190. /*if(!file_exists($parent)) {
  191. return "409 Conflict";
  192. }
  193. if(!is_dir($parent)) {
  194. return "403 Forbidden";
  195. }
  196. if( file_exists($parent."/".$name) ) {
  197. return "405 Method not allowed";
  198. }
  199. */
  200. if(!empty($_SERVER["CONTENT_LENGTH"])) { // no body parsing yet
  201. return "415 Unsupported media type";
  202. }
  203. /*
  204. if(!$stat) {
  205. return "403 Forbidden";
  206. }
  207. */
  208. return ("201 Created");
  209. }
  210. function fileinfo($path, $res = NULL)
  211. {
  212. $path = $path.$res->getLocalName();
  213. $path = str_replace("/./","/",$path);
  214. // create result array
  215. $info = array();
  216. $info["path"] = $path;
  217. $info["props"] = array();
  218. // no special beautified displayname here ...
  219. $info["props"][] = $this->mkprop("displayname", $res->getDisplayName());
  220. // creation and modification time
  221. $info["props"][] = $this->mkprop("creationdate", $res->getCreationDate());
  222. $info["props"][] = $this->mkprop("getlastmodified", $res->getLastModified());
  223. // type and size (caller already made sure that path exists)
  224. if ($res->getMimetype() == "httpd/unix-directory") {
  225. // directory (WebDAV collection)
  226. $info["props"][] = $this->mkprop("resourcetype", "collection");
  227. $info["props"][] = $this->mkprop("getcontenttype", "httpd/unix-directory");
  228. // $info["props"][] = $this->mkprop("getcontentlength", 0);
  229. } else {
  230. // plain file (WebDAV resource)
  231. $info["props"][] = $this->mkprop("resourcetype", "");
  232. $info["props"][] = $this->mkprop("getcontenttype", $res->getMimetype());
  233. $info["props"][] = $this->mkprop("getcontentlength",$res->getContentLength()); //filesize($fspath));
  234. }
  235. foreach( $res->getAllProperties() as $row) {
  236. $info["props"][] = $this->mkprop($row["namespace"], $row["name"], $row["value"]);
  237. }
  238. return $info;
  239. }
  240. /**
  241. * PUT method handler
  242. *
  243. * @param array parameter passing array
  244. * @return bool true on success
  245. */
  246. function PUT(&$options)
  247. {
  248. /* include_once("popoon/streams/bx.php");
  249. stream_wrapper_register("bx", "bxStream");
  250. */
  251. $fspath = $options["path"];
  252. /*
  253. if(!@is_dir(dirname($fspath))) {
  254. return "409 Conflict";
  255. }
  256. */
  257. //$streamtype = $this->getStreamType($options["path"]);
  258. $p = bx_collections::getCollectionAndFileParts($options["path"],"output");
  259. $fspath = $p['coll']->getContentUriById($p['rawname']);
  260. $options["new"] = ! file_exists($fspath);
  261. //$options["new"] = false;
  262. if ($p['coll']) {
  263. $this->_bxResource = $p['coll']->getChildResourceById($p['rawname']);
  264. if($options['new']) {
  265. $this->_bxResource->create();
  266. }
  267. }
  268. $fp = fopen($fspath, "w");
  269. if(!$fp) {
  270. error_log("Could not open $fspath for write");
  271. }
  272. return $fp;
  273. }
  274. /**
  275. * COPY method handler
  276. *
  277. * @param array general parameter passing array
  278. * @return bool true on success
  279. */
  280. function copy($options, $del=false)
  281. {
  282. // TODO Property updates still broken (Litmus should detect this?)
  283. if(!empty($_SERVER["CONTENT_LENGTH"])) { // no body parsing yet
  284. return "415 Unsupported media type";
  285. }
  286. // no copying to different WebDAV Servers yet
  287. if(isset($options["dest_url"])) {
  288. return "502 bad gateway";
  289. }
  290. $source = $this->base .$options["path"];
  291. if(!file_exists($source)) return "404 Not found";
  292. $dest = $this->base . $options["dest"];
  293. $new = !file_exists($dest);
  294. $existing_col = false;
  295. if(!$new) {
  296. if($del && is_dir($dest)) {
  297. if(!$options["overwrite"]) {
  298. return "412 precondition failed";
  299. }
  300. $dest .= basename($source);
  301. if(file_exists($dest.basename($source))) {
  302. $options["dest"] .= basename($source);
  303. } else {
  304. $new = true;
  305. $existing_col = true;
  306. }
  307. }
  308. }
  309. if(!$new) {
  310. if($options["overwrite"]) {
  311. $stat = $this->delete(array("path" => $options["dest"]));
  312. if($stat[0] != "2") return $stat;
  313. } else {
  314. return "412 precondition failed";
  315. }
  316. }
  317. if (is_dir($source)) {
  318. // RFC 2518 Section 9.2, last paragraph
  319. if ($options["depth"] != "infinity") {
  320. error_log("---- ".$options["depth"]);
  321. return "400 Bad request";
  322. }
  323. system(escapeshellcmd("cp -R ".escapeshellarg($source) ." " . escapeshellarg($dest)));
  324. if($del) {
  325. system(escapeshellcmd("rm -rf ".escapeshellarg($source)) );
  326. }
  327. } else {
  328. if($del) {
  329. @unlink($dest);
  330. $query = "DELETE FROM properties WHERE path = '$options[dest]'";
  331. $this->db->query($query);
  332. rename($source, $dest);
  333. $query = "UPDATE properties SET path = '$options[dest]' WHERE path = '$options[path]'";
  334. $this->db->query($query);
  335. } else {
  336. if(substr($dest,-1)=="/") $dest = substr($dest,0,-1);
  337. copy($source, $dest);
  338. }
  339. }
  340. return ($new && !$existing_col) ? "201 Created" : "204 No Content";
  341. }
  342. }
  343. ?>