webdav.php 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. <?php
  2. /**
  3. * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
  4. * This file is licensed under the Affero General Public License version 3 or
  5. * later.
  6. * See the COPYING-README file.
  7. */
  8. namespace OC\Files\Storage;
  9. class DAV extends \OC\Files\Storage\Common{
  10. private $password;
  11. private $user;
  12. private $host;
  13. private $secure;
  14. private $root;
  15. private $ready;
  16. /**
  17. * @var \Sabre_DAV_Client
  18. */
  19. private $client;
  20. private static $tempFiles=array();
  21. public function __construct($params) {
  22. if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
  23. $host = $params['host'];
  24. //remove leading http[s], will be generated in createBaseUri()
  25. if (substr($host, 0, 8) == "https://") $host = substr($host, 8);
  26. else if (substr($host, 0, 7) == "http://") $host = substr($host, 7);
  27. $this->host=$host;
  28. $this->user=$params['user'];
  29. $this->password=$params['password'];
  30. if (isset($params['secure'])) {
  31. if (is_string($params['secure'])) {
  32. $this->secure = ($params['secure'] === 'true');
  33. } else {
  34. $this->secure = (bool)$params['secure'];
  35. }
  36. } else {
  37. $this->secure = false;
  38. }
  39. $this->root=isset($params['root'])?$params['root']:'/';
  40. if ( ! $this->root || $this->root[0]!='/') {
  41. $this->root='/'.$this->root;
  42. }
  43. if (substr($this->root, -1, 1)!='/') {
  44. $this->root.='/';
  45. }
  46. } else {
  47. throw new \Exception();
  48. }
  49. }
  50. private function init(){
  51. if($this->ready) {
  52. return;
  53. }
  54. $this->ready = true;
  55. $settings = array(
  56. 'baseUri' => $this->createBaseUri(),
  57. 'userName' => $this->user,
  58. 'password' => $this->password,
  59. );
  60. $this->client = new \Sabre_DAV_Client($settings);
  61. $caview = \OCP\Files::getStorage('files_external');
  62. if ($caview) {
  63. $certPath=\OCP\Config::getSystemValue('datadirectory').$caview->getAbsolutePath("").'rootcerts.crt';
  64. if (file_exists($certPath)) {
  65. $this->client->addTrustedCertificates($certPath);
  66. }
  67. }
  68. }
  69. public function getId(){
  70. return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
  71. }
  72. private function createBaseUri() {
  73. $baseUri='http';
  74. if ($this->secure) {
  75. $baseUri.='s';
  76. }
  77. $baseUri.='://'.$this->host.$this->root;
  78. return $baseUri;
  79. }
  80. public function mkdir($path) {
  81. $this->init();
  82. $path=$this->cleanPath($path);
  83. return $this->simpleResponse('MKCOL', $path, null, 201);
  84. }
  85. public function rmdir($path) {
  86. $this->init();
  87. $path=$this->cleanPath($path);
  88. return $this->simpleResponse('DELETE', $path, null, 204);
  89. }
  90. public function opendir($path) {
  91. $this->init();
  92. $path=$this->cleanPath($path);
  93. try {
  94. $response=$this->client->propfind($path, array(), 1);
  95. $id=md5('webdav'.$this->root.$path);
  96. $content = array();
  97. $files=array_keys($response);
  98. array_shift($files);//the first entry is the current directory
  99. foreach ($files as $file) {
  100. $file = urldecode(basename($file));
  101. $content[]=$file;
  102. }
  103. \OC\Files\Stream\Dir::register($id, $content);
  104. return opendir('fakedir://'.$id);
  105. } catch(\Exception $e) {
  106. return false;
  107. }
  108. }
  109. public function filetype($path) {
  110. $this->init();
  111. $path=$this->cleanPath($path);
  112. try {
  113. $response=$this->client->propfind($path, array('{DAV:}resourcetype'));
  114. $responseType=$response["{DAV:}resourcetype"]->resourceType;
  115. return (count($responseType)>0 and $responseType[0]=="{DAV:}collection")?'dir':'file';
  116. } catch(\Exception $e) {
  117. error_log($e->getMessage());
  118. \OCP\Util::writeLog("webdav client", \OCP\Util::sanitizeHTML($e->getMessage()), \OCP\Util::ERROR);
  119. return false;
  120. }
  121. }
  122. public function isReadable($path) {
  123. return true;//not properly supported
  124. }
  125. public function isUpdatable($path) {
  126. return true;//not properly supported
  127. }
  128. public function file_exists($path) {
  129. $this->init();
  130. $path=$this->cleanPath($path);
  131. try {
  132. $this->client->propfind($path, array('{DAV:}resourcetype'));
  133. return true;//no 404 exception
  134. } catch(\Exception $e) {
  135. return false;
  136. }
  137. }
  138. public function unlink($path) {
  139. $this->init();
  140. return $this->simpleResponse('DELETE', $path, null, 204);
  141. }
  142. public function fopen($path, $mode) {
  143. $this->init();
  144. $path=$this->cleanPath($path);
  145. switch($mode) {
  146. case 'r':
  147. case 'rb':
  148. if ( ! $this->file_exists($path)) {
  149. return false;
  150. }
  151. //straight up curl instead of sabredav here, sabredav put's the entire get result in memory
  152. $curl = curl_init();
  153. $fp = fopen('php://temp', 'r+');
  154. curl_setopt($curl, CURLOPT_USERPWD, $this->user.':'.$this->password);
  155. curl_setopt($curl, CURLOPT_URL, $this->createBaseUri().$path);
  156. curl_setopt($curl, CURLOPT_FILE, $fp);
  157. curl_exec ($curl);
  158. curl_close ($curl);
  159. rewind($fp);
  160. return $fp;
  161. case 'w':
  162. case 'wb':
  163. case 'a':
  164. case 'ab':
  165. case 'r+':
  166. case 'w+':
  167. case 'wb+':
  168. case 'a+':
  169. case 'x':
  170. case 'x+':
  171. case 'c':
  172. case 'c+':
  173. //emulate these
  174. if (strrpos($path, '.')!==false) {
  175. $ext=substr($path, strrpos($path, '.'));
  176. } else {
  177. $ext='';
  178. }
  179. $tmpFile = \OCP\Files::tmpFile($ext);
  180. \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack'));
  181. if($this->file_exists($path)) {
  182. $this->getFile($path, $tmpFile);
  183. }
  184. self::$tempFiles[$tmpFile]=$path;
  185. return fopen('close://'.$tmpFile, $mode);
  186. }
  187. }
  188. public function writeBack($tmpFile) {
  189. if (isset(self::$tempFiles[$tmpFile])) {
  190. $this->uploadFile($tmpFile, self::$tempFiles[$tmpFile]);
  191. unlink($tmpFile);
  192. }
  193. }
  194. public function free_space($path) {
  195. $this->init();
  196. $path=$this->cleanPath($path);
  197. try {
  198. $response=$this->client->propfind($path, array('{DAV:}quota-available-bytes'));
  199. if (isset($response['{DAV:}quota-available-bytes'])) {
  200. return (int)$response['{DAV:}quota-available-bytes'];
  201. } else {
  202. return 0;
  203. }
  204. } catch(\Exception $e) {
  205. return \OC\Files\FREE_SPACE_UNKNOWN;
  206. }
  207. }
  208. public function touch($path, $mtime=null) {
  209. $this->init();
  210. if (is_null($mtime)) {
  211. $mtime=time();
  212. }
  213. $path=$this->cleanPath($path);
  214. // if file exists, update the mtime, else create a new empty file
  215. if ($this->file_exists($path)) {
  216. $this->client->proppatch($path, array('{DAV:}lastmodified' => $mtime));
  217. } else {
  218. $this->file_put_contents($path, '');
  219. }
  220. }
  221. public function getFile($path, $target) {
  222. $this->init();
  223. $source=$this->fopen($path, 'r');
  224. file_put_contents($target, $source);
  225. }
  226. public function uploadFile($path, $target) {
  227. $this->init();
  228. $source=fopen($path, 'r');
  229. $curl = curl_init();
  230. curl_setopt($curl, CURLOPT_USERPWD, $this->user.':'.$this->password);
  231. curl_setopt($curl, CURLOPT_URL, $this->createBaseUri().$target);
  232. curl_setopt($curl, CURLOPT_BINARYTRANSFER, true);
  233. curl_setopt($curl, CURLOPT_INFILE, $source); // file pointer
  234. curl_setopt($curl, CURLOPT_INFILESIZE, filesize($path));
  235. curl_setopt($curl, CURLOPT_PUT, true);
  236. curl_exec ($curl);
  237. curl_close ($curl);
  238. }
  239. public function rename($path1, $path2) {
  240. $this->init();
  241. $path1=$this->cleanPath($path1);
  242. $path2=$this->root.$this->cleanPath($path2);
  243. try {
  244. $this->client->request('MOVE', $path1, null, array('Destination'=>$path2));
  245. return true;
  246. } catch(\Exception $e) {
  247. return false;
  248. }
  249. }
  250. public function copy($path1, $path2) {
  251. $this->init();
  252. $path1=$this->cleanPath($path1);
  253. $path2=$this->root.$this->cleanPath($path2);
  254. try {
  255. $this->client->request('COPY', $path1, null, array('Destination'=>$path2));
  256. return true;
  257. } catch(\Exception $e) {
  258. return false;
  259. }
  260. }
  261. public function stat($path) {
  262. $this->init();
  263. $path=$this->cleanPath($path);
  264. try {
  265. $response=$this->client->propfind($path, array('{DAV:}getlastmodified', '{DAV:}getcontentlength'));
  266. return array(
  267. 'mtime'=>strtotime($response['{DAV:}getlastmodified']),
  268. 'size'=>(int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
  269. );
  270. } catch(\Exception $e) {
  271. return array();
  272. }
  273. }
  274. public function getMimeType($path) {
  275. $this->init();
  276. $path=$this->cleanPath($path);
  277. try {
  278. $response=$this->client->propfind($path, array('{DAV:}getcontenttype', '{DAV:}resourcetype'));
  279. $responseType=$response["{DAV:}resourcetype"]->resourceType;
  280. $type=(count($responseType)>0 and $responseType[0]=="{DAV:}collection")?'dir':'file';
  281. if ($type=='dir') {
  282. return 'httpd/unix-directory';
  283. } elseif (isset($response['{DAV:}getcontenttype'])) {
  284. return $response['{DAV:}getcontenttype'];
  285. } else {
  286. return false;
  287. }
  288. } catch(\Exception $e) {
  289. return false;
  290. }
  291. }
  292. public function cleanPath($path) {
  293. if ( ! $path || $path[0]=='/') {
  294. return substr($path, 1);
  295. } else {
  296. return $path;
  297. }
  298. }
  299. private function simpleResponse($method, $path, $body, $expected) {
  300. $path=$this->cleanPath($path);
  301. try {
  302. $response=$this->client->request($method, $path, $body);
  303. return $response['statusCode']==$expected;
  304. } catch(\Exception $e) {
  305. return false;
  306. }
  307. }
  308. }