/lib/Sabre/DAV/TemporaryFileFilterPlugin.php

https://github.com/agilastic/sabre-dav · PHP · 297 lines · 111 code · 52 blank · 134 comment · 10 complexity · 2e0a6ce4503097910e62486b7a0cd993 MD5 · raw file

  1. <?php
  2. namespace Sabre\DAV;
  3. use
  4. Sabre\HTTP\URLUtil,
  5. Sabre\HTTP\RequestInterface,
  6. Sabre\HTTP\ResponseInterface;
  7. /**
  8. * Temporary File Filter Plugin
  9. *
  10. * The purpose of this filter is to intercept some of the garbage files
  11. * operation systems and applications tend to generate when mounting
  12. * a WebDAV share as a disk.
  13. *
  14. * It will intercept these files and place them in a separate directory.
  15. * these files are not deleted automatically, so it is adviceable to
  16. * delete these after they are not accessed for 24 hours.
  17. *
  18. * Currently it supports:
  19. * * OS/X style resource forks and .DS_Store
  20. * * desktop.ini and Thumbs.db (windows)
  21. * * .*.swp (vim temporary files)
  22. * * .dat.* (smultron temporary files)
  23. *
  24. * Additional patterns can be added, by adding on to the
  25. * temporaryFilePatterns property.
  26. *
  27. * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
  28. * @author Evert Pot (http://evertpot.com/)
  29. * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
  30. */
  31. class TemporaryFileFilterPlugin extends ServerPlugin {
  32. /**
  33. * This is the list of patterns we intercept.
  34. * If new patterns are added, they must be valid patterns for preg_match.
  35. *
  36. * @var array
  37. */
  38. public $temporaryFilePatterns = array(
  39. '/^\._(.*)$/', // OS/X resource forks
  40. '/^.DS_Store$/', // OS/X custom folder settings
  41. '/^desktop.ini$/', // Windows custom folder settings
  42. '/^Thumbs.db$/', // Windows thumbnail cache
  43. '/^.(.*).swp$/', // ViM temporary files
  44. '/^\.dat(.*)$/', // Smultron seems to create these
  45. '/^~lock.(.*)#$/', // Windows 7 lockfiles
  46. );
  47. /**
  48. * A reference to the main Server class
  49. *
  50. * @var Sabre\DAV\Server
  51. */
  52. protected $server;
  53. /**
  54. * This is the directory where this plugin
  55. * will store it's files.
  56. *
  57. * @var string
  58. */
  59. private $dataDir;
  60. /**
  61. * Creates the plugin.
  62. *
  63. * Make sure you specify a directory for your files. If you don't, we
  64. * will use PHP's directory for session-storage instead, and you might
  65. * not want that.
  66. *
  67. * @param string|null $dataDir
  68. */
  69. public function __construct($dataDir = null) {
  70. if (!$dataDir) $dataDir = ini_get('session.save_path').'/sabredav/';
  71. if (!is_dir($dataDir)) mkdir($dataDir);
  72. $this->dataDir = $dataDir;
  73. }
  74. /**
  75. * Initialize the plugin
  76. *
  77. * This is called automatically be the Server class after this plugin is
  78. * added with Sabre\DAV\Server::addPlugin()
  79. *
  80. * @param Server $server
  81. * @return void
  82. */
  83. public function initialize(Server $server) {
  84. $this->server = $server;
  85. $server->on('beforeMethod', [$this,'beforeMethod']);
  86. $server->on('beforeCreateFile',[$this,'beforeCreateFile']);
  87. }
  88. /**
  89. * This method is called before any HTTP method handler
  90. *
  91. * This method intercepts any GET, DELETE, PUT and PROPFIND calls to
  92. * filenames that are known to match the 'temporary file' regex.
  93. *
  94. * @param RequestInterface $request
  95. * @param ResponseInterface $response
  96. * @return bool
  97. */
  98. public function beforeMethod(RequestInterface $request, ResponseInterface $response) {
  99. if (!$tempLocation = $this->isTempFile($request->getPath()))
  100. return true;
  101. switch($request->getMethod()) {
  102. case 'GET' :
  103. return $this->httpGet($request, $response, $tempLocation);
  104. case 'PUT' :
  105. return $this->httpPut($request, $response, $tempLocation);
  106. case 'PROPFIND' :
  107. return $this->httpPropfind($request, $response, $tempLocation);
  108. case 'DELETE' :
  109. return $this->httpDelete($request, $response, $tempLocation);
  110. }
  111. return true;
  112. }
  113. /**
  114. * This method is invoked if some subsystem creates a new file.
  115. *
  116. * This is used to deal with HTTP LOCK requests which create a new
  117. * file.
  118. *
  119. * @param string $uri
  120. * @param resource $data
  121. * @return bool
  122. */
  123. public function beforeCreateFile($uri,$data) {
  124. if ($tempPath = $this->isTempFile($uri)) {
  125. $hR = $this->server->httpResponse;
  126. $hR->setHeader('X-Sabre-Temp','true');
  127. file_put_contents($tempPath,$data);
  128. return false;
  129. }
  130. return true;
  131. }
  132. /**
  133. * This method will check if the url matches the temporary file pattern
  134. * if it does, it will return an path based on $this->dataDir for the
  135. * temporary file storage.
  136. *
  137. * @param string $path
  138. * @return boolean|string
  139. */
  140. protected function isTempFile($path) {
  141. // We're only interested in the basename.
  142. list(, $tempPath) = URLUtil::splitPath($path);
  143. foreach($this->temporaryFilePatterns as $tempFile) {
  144. if (preg_match($tempFile,$tempPath)) {
  145. return $this->getDataDir() . '/sabredav_' . md5($path) . '.tempfile';
  146. }
  147. }
  148. return false;
  149. }
  150. /**
  151. * This method handles the GET method for temporary files.
  152. * If the file doesn't exist, it will return false which will kick in
  153. * the regular system for the GET method.
  154. *
  155. * @param RequestInterface $request
  156. * @param ResponseInterface $hR
  157. * @param string $tempLocation
  158. * @return bool
  159. */
  160. public function httpGet(RequestInterface $request, ResponseInterface $hR, $tempLocation) {
  161. if (!file_exists($tempLocation)) return true;
  162. $hR->setHeader('Content-Type','application/octet-stream');
  163. $hR->setHeader('Content-Length',filesize($tempLocation));
  164. $hR->setHeader('X-Sabre-Temp','true');
  165. $hR->setStatus(200);
  166. $hR->setBody(fopen($tempLocation,'r'));
  167. return false;
  168. }
  169. /**
  170. * This method handles the PUT method.
  171. *
  172. * @param RequestInterface $request
  173. * @param ResponseInterface $hR
  174. * @param string $tempLocation
  175. * @return bool
  176. */
  177. public function httpPut(RequestInterface $request, ResponseInterface $hR, $tempLocation) {
  178. $hR->setHeader('X-Sabre-Temp','true');
  179. $newFile = !file_exists($tempLocation);
  180. if (!$newFile && ($this->server->httpRequest->getHeader('If-None-Match'))) {
  181. throw new Exception\PreconditionFailed('The resource already exists, and an If-None-Match header was supplied');
  182. }
  183. file_put_contents($tempLocation,$this->server->httpRequest->getBody());
  184. $hR->setStatus($newFile?201:200);
  185. return false;
  186. }
  187. /**
  188. * This method handles the DELETE method.
  189. *
  190. * If the file didn't exist, it will return false, which will make the
  191. * standard HTTP DELETE handler kick in.
  192. *
  193. * @param RequestInterface $request
  194. * @param ResponseInterface $hR
  195. * @param string $tempLocation
  196. * @return bool
  197. */
  198. public function httpDelete(RequestInterface $request, ResponseInterface $hR, $tempLocation) {
  199. if (!file_exists($tempLocation)) return true;
  200. unlink($tempLocation);
  201. $hR->setHeader('X-Sabre-Temp','true');
  202. $hR->setStatus(204);
  203. return false;
  204. }
  205. /**
  206. * This method handles the PROPFIND method.
  207. *
  208. * It's a very lazy method, it won't bother checking the request body
  209. * for which properties were requested, and just sends back a default
  210. * set of properties.
  211. *
  212. * @param RequestInterface $request
  213. * @param ResponseInterface $hR
  214. * @param string $tempLocation
  215. * @return bool
  216. */
  217. public function httpPropfind(RequestInterface $request, ResponseInterface $hR, $tempLocation) {
  218. if (!file_exists($tempLocation)) return true;
  219. $hR->setHeader('X-Sabre-Temp','true');
  220. $hR->setStatus(207);
  221. $hR->setHeader('Content-Type','application/xml; charset=utf-8');
  222. $this->server->parsePropFindRequest($request->getBody($asString = true));
  223. $properties = [
  224. 'href' => $request->getPath(),
  225. 200 => [
  226. '{DAV:}getlastmodified' => new Property\GetLastModified(filemtime($tempLocation)),
  227. '{DAV:}getcontentlength' => filesize($tempLocation),
  228. '{DAV:}resourcetype' => new Property\ResourceType(null),
  229. '{'.Server::NS_SABREDAV.'}tempFile' => true,
  230. ],
  231. ];
  232. $data = $this->server->generateMultiStatus([$properties]);
  233. $hR->setBody($data);
  234. return false;
  235. }
  236. /**
  237. * This method returns the directory where the temporary files should be stored.
  238. *
  239. * @return string
  240. */
  241. protected function getDataDir()
  242. {
  243. return $this->dataDir;
  244. }
  245. }