stream_wrappers.inc 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009
  1. <?php
  2. /**
  3. * @file
  4. * Drupal stream wrapper interface.
  5. *
  6. * Provides a Drupal interface and classes to implement PHP stream wrappers for
  7. * public, private, and temporary files.
  8. *
  9. * A stream wrapper is an abstraction of a file system that allows Drupal to
  10. * use the same set of methods to access both local files and remote resources.
  11. *
  12. * Note that PHP 5.2 fopen() only supports URIs of the form "scheme://target"
  13. * despite the fact that according to RFC 3986 a URI's scheme component
  14. * delimiter is in general just ":", not "://". Because of this PHP limitation
  15. * and for consistency Drupal will only accept URIs of form "scheme://target".
  16. *
  17. * @see http://www.faqs.org/rfcs/rfc3986.html
  18. * @see http://bugs.php.net/bug.php?id=47070
  19. */
  20. /**
  21. * Stream wrapper bit flags that are the basis for composite types.
  22. *
  23. * Note that 0x0002 is skipped, because it was the value of a constant that has
  24. * since been removed.
  25. */
  26. /**
  27. * Stream wrapper bit flag -- a filter that matches all wrappers.
  28. */
  29. define('STREAM_WRAPPERS_ALL', 0x0000);
  30. /**
  31. * Stream wrapper bit flag -- refers to a local file system location.
  32. */
  33. define('STREAM_WRAPPERS_LOCAL', 0x0001);
  34. /**
  35. * Stream wrapper bit flag -- wrapper is readable (almost always true).
  36. */
  37. define('STREAM_WRAPPERS_READ', 0x0004);
  38. /**
  39. * Stream wrapper bit flag -- wrapper is writeable.
  40. */
  41. define('STREAM_WRAPPERS_WRITE', 0x0008);
  42. /**
  43. * Stream wrapper bit flag -- exposed in the UI and potentially web accessible.
  44. */
  45. define('STREAM_WRAPPERS_VISIBLE', 0x0010);
  46. /**
  47. * Composite stream wrapper bit flags that are usually used as the types.
  48. */
  49. /**
  50. * Stream wrapper type flag -- not visible in the UI or accessible via web,
  51. * but readable and writable. E.g. the temporary directory for uploads.
  52. */
  53. define('STREAM_WRAPPERS_HIDDEN', STREAM_WRAPPERS_READ | STREAM_WRAPPERS_WRITE);
  54. /**
  55. * Stream wrapper type flag -- hidden, readable and writeable using local files.
  56. */
  57. define('STREAM_WRAPPERS_LOCAL_HIDDEN', STREAM_WRAPPERS_LOCAL | STREAM_WRAPPERS_HIDDEN);
  58. /**
  59. * Stream wrapper type flag -- visible, readable and writeable.
  60. */
  61. define('STREAM_WRAPPERS_WRITE_VISIBLE', STREAM_WRAPPERS_READ | STREAM_WRAPPERS_WRITE | STREAM_WRAPPERS_VISIBLE);
  62. /**
  63. * Stream wrapper type flag -- visible and read-only.
  64. */
  65. define('STREAM_WRAPPERS_READ_VISIBLE', STREAM_WRAPPERS_READ | STREAM_WRAPPERS_VISIBLE);
  66. /**
  67. * Stream wrapper type flag -- the default when 'type' is omitted from
  68. * hook_stream_wrappers(). This does not include STREAM_WRAPPERS_LOCAL,
  69. * because PHP grants a greater trust level to local files (for example, they
  70. * can be used in an "include" statement, regardless of the "allow_url_include"
  71. * setting), so stream wrappers need to explicitly opt-in to this.
  72. */
  73. define('STREAM_WRAPPERS_NORMAL', STREAM_WRAPPERS_WRITE_VISIBLE);
  74. /**
  75. * Stream wrapper type flag -- visible, readable and writeable using local files.
  76. */
  77. define('STREAM_WRAPPERS_LOCAL_NORMAL', STREAM_WRAPPERS_LOCAL | STREAM_WRAPPERS_NORMAL);
  78. /**
  79. * Generic PHP stream wrapper interface.
  80. *
  81. * @see http://www.php.net/manual/class.streamwrapper.php
  82. */
  83. interface StreamWrapperInterface {
  84. public function stream_open($uri, $mode, $options, &$opened_url);
  85. public function stream_close();
  86. public function stream_lock($operation);
  87. public function stream_read($count);
  88. public function stream_write($data);
  89. public function stream_eof();
  90. public function stream_seek($offset, $whence);
  91. public function stream_flush();
  92. public function stream_tell();
  93. public function stream_stat();
  94. public function unlink($uri);
  95. public function rename($from_uri, $to_uri);
  96. public function mkdir($uri, $mode, $options);
  97. public function rmdir($uri, $options);
  98. public function url_stat($uri, $flags);
  99. public function dir_opendir($uri, $options);
  100. public function dir_readdir();
  101. public function dir_rewinddir();
  102. public function dir_closedir();
  103. }
  104. /**
  105. * Drupal stream wrapper extension.
  106. *
  107. * Extend the StreamWrapperInterface with methods expected by Drupal stream
  108. * wrapper classes.
  109. */
  110. interface DrupalStreamWrapperInterface extends StreamWrapperInterface {
  111. /**
  112. * Set the absolute stream resource URI.
  113. *
  114. * This allows you to set the URI. Generally is only called by the factory
  115. * method.
  116. *
  117. * @param $uri
  118. * A string containing the URI that should be used for this instance.
  119. */
  120. public function setUri($uri);
  121. /**
  122. * Returns the stream resource URI.
  123. *
  124. * @return
  125. * Returns the current URI of the instance.
  126. */
  127. public function getUri();
  128. /**
  129. * Returns a web accessible URL for the resource.
  130. *
  131. * This function should return a URL that can be embedded in a web page
  132. * and accessed from a browser. For example, the external URL of
  133. * "youtube://xIpLd0WQKCY" might be
  134. * "http://www.youtube.com/watch?v=xIpLd0WQKCY".
  135. *
  136. * @return
  137. * Returns a string containing a web accessible URL for the resource.
  138. */
  139. public function getExternalUrl();
  140. /**
  141. * Returns the MIME type of the resource.
  142. *
  143. * @param $uri
  144. * The URI, path, or filename.
  145. * @param $mapping
  146. * An optional map of extensions to their mimetypes, in the form:
  147. * - 'mimetypes': a list of mimetypes, keyed by an identifier,
  148. * - 'extensions': the mapping itself, an associative array in which
  149. * the key is the extension and the value is the mimetype identifier.
  150. *
  151. * @return
  152. * Returns a string containing the MIME type of the resource.
  153. */
  154. public static function getMimeType($uri, $mapping = NULL);
  155. /**
  156. * Changes permissions of the resource.
  157. *
  158. * PHP lacks this functionality and it is not part of the official stream
  159. * wrapper interface. This is a custom implementation for Drupal.
  160. *
  161. * @param $mode
  162. * Integer value for the permissions. Consult PHP chmod() documentation
  163. * for more information.
  164. *
  165. * @return
  166. * Returns TRUE on success or FALSE on failure.
  167. */
  168. public function chmod($mode);
  169. /**
  170. * Returns canonical, absolute path of the resource.
  171. *
  172. * Implementation placeholder. PHP's realpath() does not support stream
  173. * wrappers. We provide this as a default so that individual wrappers may
  174. * implement their own solutions.
  175. *
  176. * @return
  177. * Returns a string with absolute pathname on success (implemented
  178. * by core wrappers), or FALSE on failure or if the registered
  179. * wrapper does not provide an implementation.
  180. */
  181. public function realpath();
  182. /**
  183. * Gets the name of the directory from a given path.
  184. *
  185. * This method is usually accessed through drupal_dirname(), which wraps
  186. * around the normal PHP dirname() function, which does not support stream
  187. * wrappers.
  188. *
  189. * @param $uri
  190. * An optional URI.
  191. *
  192. * @return
  193. * A string containing the directory name, or FALSE if not applicable.
  194. *
  195. * @see drupal_dirname()
  196. */
  197. public function dirname($uri = NULL);
  198. }
  199. /**
  200. * Drupal stream wrapper base class for local files.
  201. *
  202. * This class provides a complete stream wrapper implementation. URIs such as
  203. * "public://example.txt" are expanded to a normal filesystem path such as
  204. * "sites/default/files/example.txt" and then PHP filesystem functions are
  205. * invoked.
  206. *
  207. * DrupalLocalStreamWrapper implementations need to implement at least the
  208. * getDirectoryPath() and getExternalUrl() methods.
  209. */
  210. abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface {
  211. /**
  212. * Stream context resource.
  213. *
  214. * @var Resource
  215. */
  216. public $context;
  217. /**
  218. * A generic resource handle.
  219. *
  220. * @var Resource
  221. */
  222. public $handle = NULL;
  223. /**
  224. * Instance URI (stream).
  225. *
  226. * A stream is referenced as "scheme://target".
  227. *
  228. * @var String
  229. */
  230. protected $uri;
  231. /**
  232. * Gets the path that the wrapper is responsible for.
  233. * @TODO: Review this method name in D8 per http://drupal.org/node/701358
  234. *
  235. * @return
  236. * String specifying the path.
  237. */
  238. abstract function getDirectoryPath();
  239. /**
  240. * Base implementation of setUri().
  241. */
  242. function setUri($uri) {
  243. $this->uri = $uri;
  244. }
  245. /**
  246. * Base implementation of getUri().
  247. */
  248. function getUri() {
  249. return $this->uri;
  250. }
  251. /**
  252. * Returns the local writable target of the resource within the stream.
  253. *
  254. * This function should be used in place of calls to realpath() or similar
  255. * functions when attempting to determine the location of a file. While
  256. * functions like realpath() may return the location of a read-only file, this
  257. * method may return a URI or path suitable for writing that is completely
  258. * separate from the URI used for reading.
  259. *
  260. * @param $uri
  261. * Optional URI.
  262. *
  263. * @return
  264. * Returns a string representing a location suitable for writing of a file,
  265. * or FALSE if unable to write to the file such as with read-only streams.
  266. */
  267. protected function getTarget($uri = NULL) {
  268. if (!isset($uri)) {
  269. $uri = $this->uri;
  270. }
  271. list($scheme, $target) = explode('://', $uri, 2);
  272. // Remove erroneous leading or trailing, forward-slashes and backslashes.
  273. return trim($target, '\/');
  274. }
  275. /**
  276. * Base implementation of getMimeType().
  277. */
  278. static function getMimeType($uri, $mapping = NULL) {
  279. if (!isset($mapping)) {
  280. // The default file map, defined in file.mimetypes.inc is quite big.
  281. // We only load it when necessary.
  282. include_once DRUPAL_ROOT . '/includes/file.mimetypes.inc';
  283. $mapping = file_mimetype_mapping();
  284. }
  285. $extension = '';
  286. $file_parts = explode('.', drupal_basename($uri));
  287. // Remove the first part: a full filename should not match an extension.
  288. array_shift($file_parts);
  289. // Iterate over the file parts, trying to find a match.
  290. // For my.awesome.image.jpeg, we try:
  291. // - jpeg
  292. // - image.jpeg, and
  293. // - awesome.image.jpeg
  294. while ($additional_part = array_pop($file_parts)) {
  295. $extension = strtolower($additional_part . ($extension ? '.' . $extension : ''));
  296. if (isset($mapping['extensions'][$extension])) {
  297. return $mapping['mimetypes'][$mapping['extensions'][$extension]];
  298. }
  299. }
  300. return 'application/octet-stream';
  301. }
  302. /**
  303. * Base implementation of chmod().
  304. */
  305. function chmod($mode) {
  306. $output = @chmod($this->getLocalPath(), $mode);
  307. // We are modifying the underlying file here, so we have to clear the stat
  308. // cache so that PHP understands that URI has changed too.
  309. clearstatcache();
  310. return $output;
  311. }
  312. /**
  313. * Base implementation of realpath().
  314. */
  315. function realpath() {
  316. return $this->getLocalPath();
  317. }
  318. /**
  319. * Returns the canonical absolute path of the URI, if possible.
  320. *
  321. * @param string $uri
  322. * (optional) The stream wrapper URI to be converted to a canonical
  323. * absolute path. This may point to a directory or another type of file.
  324. *
  325. * @return string|false
  326. * If $uri is not set, returns the canonical absolute path of the URI
  327. * previously set by the DrupalStreamWrapperInterface::setUri() function.
  328. * If $uri is set and valid for this class, returns its canonical absolute
  329. * path, as determined by the realpath() function. If $uri is set but not
  330. * valid, returns FALSE.
  331. */
  332. protected function getLocalPath($uri = NULL) {
  333. if (!isset($uri)) {
  334. $uri = $this->uri;
  335. }
  336. $path = $this->getDirectoryPath() . '/' . $this->getTarget($uri);
  337. $realpath = realpath($path);
  338. if (!$realpath) {
  339. // This file does not yet exist.
  340. $realpath = realpath(dirname($path)) . '/' . drupal_basename($path);
  341. }
  342. $directory = realpath($this->getDirectoryPath());
  343. if (!$realpath || !$directory || strpos($realpath, $directory) !== 0) {
  344. return FALSE;
  345. }
  346. return $realpath;
  347. }
  348. /**
  349. * Support for fopen(), file_get_contents(), file_put_contents() etc.
  350. *
  351. * @param $uri
  352. * A string containing the URI to the file to open.
  353. * @param $mode
  354. * The file mode ("r", "wb" etc.).
  355. * @param $options
  356. * A bit mask of STREAM_USE_PATH and STREAM_REPORT_ERRORS.
  357. * @param $opened_path
  358. * A string containing the path actually opened.
  359. *
  360. * @return
  361. * Returns TRUE if file was opened successfully.
  362. *
  363. * @see http://php.net/manual/streamwrapper.stream-open.php
  364. */
  365. public function stream_open($uri, $mode, $options, &$opened_path) {
  366. $this->uri = $uri;
  367. $path = $this->getLocalPath();
  368. if ($path === FALSE) {
  369. if ($options & STREAM_REPORT_ERRORS) {
  370. trigger_error('stream_open() filename cannot be empty', E_USER_WARNING);
  371. }
  372. return FALSE;
  373. }
  374. $this->handle = ($options & STREAM_REPORT_ERRORS) ? fopen($path, $mode) : @fopen($path, $mode);
  375. if ((bool) $this->handle && $options & STREAM_USE_PATH) {
  376. $opened_path = $path;
  377. }
  378. return (bool) $this->handle;
  379. }
  380. /**
  381. * Support for flock().
  382. *
  383. * @param $operation
  384. * One of the following:
  385. * - LOCK_SH to acquire a shared lock (reader).
  386. * - LOCK_EX to acquire an exclusive lock (writer).
  387. * - LOCK_UN to release a lock (shared or exclusive).
  388. * - LOCK_NB if you don't want flock() to block while locking (not
  389. * supported on Windows).
  390. *
  391. * @return
  392. * Always returns TRUE at the present time.
  393. *
  394. * @see http://php.net/manual/streamwrapper.stream-lock.php
  395. */
  396. public function stream_lock($operation) {
  397. if (in_array($operation, array(LOCK_SH, LOCK_EX, LOCK_UN, LOCK_NB))) {
  398. return flock($this->handle, $operation);
  399. }
  400. return TRUE;
  401. }
  402. /**
  403. * Support for fread(), file_get_contents() etc.
  404. *
  405. * @param $count
  406. * Maximum number of bytes to be read.
  407. *
  408. * @return
  409. * The string that was read, or FALSE in case of an error.
  410. *
  411. * @see http://php.net/manual/streamwrapper.stream-read.php
  412. */
  413. public function stream_read($count) {
  414. return fread($this->handle, $count);
  415. }
  416. /**
  417. * Support for fwrite(), file_put_contents() etc.
  418. *
  419. * @param $data
  420. * The string to be written.
  421. *
  422. * @return
  423. * The number of bytes written (integer).
  424. *
  425. * @see http://php.net/manual/streamwrapper.stream-write.php
  426. */
  427. public function stream_write($data) {
  428. return fwrite($this->handle, $data);
  429. }
  430. /**
  431. * Support for feof().
  432. *
  433. * @return
  434. * TRUE if end-of-file has been reached.
  435. *
  436. * @see http://php.net/manual/streamwrapper.stream-eof.php
  437. */
  438. public function stream_eof() {
  439. return feof($this->handle);
  440. }
  441. /**
  442. * Support for fseek().
  443. *
  444. * @param $offset
  445. * The byte offset to got to.
  446. * @param $whence
  447. * SEEK_SET, SEEK_CUR, or SEEK_END.
  448. *
  449. * @return
  450. * TRUE on success.
  451. *
  452. * @see http://php.net/manual/streamwrapper.stream-seek.php
  453. */
  454. public function stream_seek($offset, $whence) {
  455. // fseek returns 0 on success and -1 on a failure.
  456. // stream_seek 1 on success and 0 on a failure.
  457. return !fseek($this->handle, $offset, $whence);
  458. }
  459. /**
  460. * Support for fflush().
  461. *
  462. * @return
  463. * TRUE if data was successfully stored (or there was no data to store).
  464. *
  465. * @see http://php.net/manual/streamwrapper.stream-flush.php
  466. */
  467. public function stream_flush() {
  468. return fflush($this->handle);
  469. }
  470. /**
  471. * Support for ftell().
  472. *
  473. * @return
  474. * The current offset in bytes from the beginning of file.
  475. *
  476. * @see http://php.net/manual/streamwrapper.stream-tell.php
  477. */
  478. public function stream_tell() {
  479. return ftell($this->handle);
  480. }
  481. /**
  482. * Support for fstat().
  483. *
  484. * @return
  485. * An array with file status, or FALSE in case of an error - see fstat()
  486. * for a description of this array.
  487. *
  488. * @see http://php.net/manual/streamwrapper.stream-stat.php
  489. */
  490. public function stream_stat() {
  491. return fstat($this->handle);
  492. }
  493. /**
  494. * Support for fclose().
  495. *
  496. * @return
  497. * TRUE if stream was successfully closed.
  498. *
  499. * @see http://php.net/manual/streamwrapper.stream-close.php
  500. */
  501. public function stream_close() {
  502. return fclose($this->handle);
  503. }
  504. /**
  505. * Sets metadata on the stream.
  506. *
  507. * WARNING: Do not call this method directly! It will be called internally by
  508. * PHP itself when one of the following functions is called on a stream URL:
  509. *
  510. * @param string $uri
  511. * A string containing the URI to the file to set metadata on.
  512. * @param int $option
  513. * One of:
  514. * - STREAM_META_TOUCH: The method was called in response to touch().
  515. * - STREAM_META_OWNER_NAME: The method was called in response to chown()
  516. * with string parameter.
  517. * - STREAM_META_OWNER: The method was called in response to chown().
  518. * - STREAM_META_GROUP_NAME: The method was called in response to chgrp().
  519. * - STREAM_META_GROUP: The method was called in response to chgrp().
  520. * - STREAM_META_ACCESS: The method was called in response to chmod().
  521. * @param mixed $value
  522. * If option is:
  523. * - STREAM_META_TOUCH: Array consisting of two arguments of the touch()
  524. * function.
  525. * - STREAM_META_OWNER_NAME or STREAM_META_GROUP_NAME: The name of the owner
  526. * user/group as string.
  527. * - STREAM_META_OWNER or STREAM_META_GROUP: The value of the owner
  528. * user/group as integer.
  529. * - STREAM_META_ACCESS: The argument of the chmod() as integer.
  530. *
  531. * @return bool
  532. * Returns TRUE on success or FALSE on failure. If $option is not
  533. * implemented, FALSE should be returned.
  534. *
  535. * @see touch()
  536. * @see chmod()
  537. * @see chown()
  538. * @see chgrp()
  539. * @link http://php.net/manual/streamwrapper.stream-metadata.php
  540. */
  541. public function stream_metadata($uri, $option, $value) {
  542. $target = $this->getLocalPath($uri);
  543. $return = FALSE;
  544. switch ($option) {
  545. case STREAM_META_TOUCH:
  546. if (!empty($value)) {
  547. $return = touch($target, $value[0], $value[1]);
  548. }
  549. else {
  550. $return = touch($target);
  551. }
  552. break;
  553. case STREAM_META_OWNER_NAME:
  554. case STREAM_META_OWNER:
  555. $return = chown($target, $value);
  556. break;
  557. case STREAM_META_GROUP_NAME:
  558. case STREAM_META_GROUP:
  559. $return = chgrp($target, $value);
  560. break;
  561. case STREAM_META_ACCESS:
  562. $return = chmod($target, $value);
  563. break;
  564. }
  565. if ($return) {
  566. // For convenience clear the file status cache of the underlying file,
  567. // since metadata operations are often followed by file status checks.
  568. clearstatcache(TRUE, $target);
  569. }
  570. return $return;
  571. }
  572. /**
  573. * Truncate stream.
  574. *
  575. * Will respond to truncation; e.g., through ftruncate().
  576. *
  577. * @param int $new_size
  578. * The new size.
  579. *
  580. * @return bool
  581. * TRUE on success, FALSE otherwise.
  582. */
  583. public function stream_truncate($new_size) {
  584. return ftruncate($this->handle, $new_size);
  585. }
  586. /**
  587. * Retrieve the underlying stream resource.
  588. *
  589. * This method is called in response to stream_select().
  590. *
  591. * @param int $cast_as
  592. * Can be STREAM_CAST_FOR_SELECT when stream_select() is calling
  593. * stream_cast() or STREAM_CAST_AS_STREAM when stream_cast() is called for
  594. * other uses.
  595. *
  596. * @return resource|false
  597. * The underlying stream resource or FALSE if stream_select() is not
  598. * supported.
  599. *
  600. * @see stream_select()
  601. * @link http://php.net/manual/streamwrapper.stream-cast.php
  602. */
  603. public function stream_cast($cast_as) {
  604. return $this->handle ? $this->handle : FALSE;
  605. }
  606. /**
  607. * Change stream options.
  608. *
  609. * This method is called to set options on the stream.
  610. *
  611. * Since Windows systems do not allow it and it is not needed for most use
  612. * cases anyway, this method is not supported on local files and will trigger
  613. * an error and return false. If needed, custom subclasses can provide
  614. * OS-specific implementations for advanced use cases.
  615. *
  616. * @param int $option
  617. * One of:
  618. * - STREAM_OPTION_BLOCKING: The method was called in response to
  619. * stream_set_blocking().
  620. * - STREAM_OPTION_READ_TIMEOUT: The method was called in response to
  621. * stream_set_timeout().
  622. * - STREAM_OPTION_WRITE_BUFFER: The method was called in response to
  623. * stream_set_write_buffer().
  624. * @param int $arg1
  625. * If option is:
  626. * - STREAM_OPTION_BLOCKING: The requested blocking mode:
  627. * - 1 means blocking.
  628. * - 0 means not blocking.
  629. * - STREAM_OPTION_READ_TIMEOUT: The timeout in seconds.
  630. * - STREAM_OPTION_WRITE_BUFFER: The buffer mode, STREAM_BUFFER_NONE or
  631. * STREAM_BUFFER_FULL.
  632. * @param int $arg2
  633. * If option is:
  634. * - STREAM_OPTION_BLOCKING: This option is not set.
  635. * - STREAM_OPTION_READ_TIMEOUT: The timeout in microseconds.
  636. * - STREAM_OPTION_WRITE_BUFFER: The requested buffer size.
  637. *
  638. * @return bool
  639. * TRUE on success, FALSE otherwise. If $option is not implemented, FALSE
  640. * should be returned.
  641. */
  642. public function stream_set_option($option, $arg1, $arg2) {
  643. trigger_error('stream_set_option() not supported for local file based stream wrappers', E_USER_WARNING);
  644. return FALSE;
  645. }
  646. /**
  647. * Support for unlink().
  648. *
  649. * @param $uri
  650. * A string containing the URI to the resource to delete.
  651. *
  652. * @return
  653. * TRUE if resource was successfully deleted.
  654. *
  655. * @see http://php.net/manual/streamwrapper.unlink.php
  656. */
  657. public function unlink($uri) {
  658. $this->uri = $uri;
  659. return drupal_unlink($this->getLocalPath());
  660. }
  661. /**
  662. * Support for rename().
  663. *
  664. * @param $from_uri,
  665. * The URI to the file to rename.
  666. * @param $to_uri
  667. * The new URI for file.
  668. *
  669. * @return
  670. * TRUE if file was successfully renamed.
  671. *
  672. * @see http://php.net/manual/streamwrapper.rename.php
  673. */
  674. public function rename($from_uri, $to_uri) {
  675. return rename($this->getLocalPath($from_uri), $this->getLocalPath($to_uri));
  676. }
  677. /**
  678. * Gets the name of the directory from a given path.
  679. *
  680. * This method is usually accessed through drupal_dirname(), which wraps
  681. * around the PHP dirname() function because it does not support stream
  682. * wrappers.
  683. *
  684. * @param $uri
  685. * A URI or path.
  686. *
  687. * @return
  688. * A string containing the directory name.
  689. *
  690. * @see drupal_dirname()
  691. */
  692. public function dirname($uri = NULL) {
  693. list($scheme, $target) = explode('://', $uri, 2);
  694. $target = $this->getTarget($uri);
  695. $dirname = dirname($target);
  696. if ($dirname == '.') {
  697. $dirname = '';
  698. }
  699. return $scheme . '://' . $dirname;
  700. }
  701. /**
  702. * Support for mkdir().
  703. *
  704. * @param $uri
  705. * A string containing the URI to the directory to create.
  706. * @param $mode
  707. * Permission flags - see mkdir().
  708. * @param $options
  709. * A bit mask of STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE.
  710. *
  711. * @return
  712. * TRUE if directory was successfully created.
  713. *
  714. * @see http://php.net/manual/streamwrapper.mkdir.php
  715. */
  716. public function mkdir($uri, $mode, $options) {
  717. $this->uri = $uri;
  718. $recursive = (bool) ($options & STREAM_MKDIR_RECURSIVE);
  719. if ($recursive) {
  720. // $this->getLocalPath() fails if $uri has multiple levels of directories
  721. // that do not yet exist.
  722. $localpath = $this->getDirectoryPath() . '/' . $this->getTarget($uri);
  723. }
  724. else {
  725. $localpath = $this->getLocalPath($uri);
  726. }
  727. if ($options & STREAM_REPORT_ERRORS) {
  728. return drupal_mkdir($localpath, $mode, $recursive);
  729. }
  730. else {
  731. return @drupal_mkdir($localpath, $mode, $recursive);
  732. }
  733. }
  734. /**
  735. * Support for rmdir().
  736. *
  737. * @param $uri
  738. * A string containing the URI to the directory to delete.
  739. * @param $options
  740. * A bit mask of STREAM_REPORT_ERRORS.
  741. *
  742. * @return
  743. * TRUE if directory was successfully removed.
  744. *
  745. * @see http://php.net/manual/streamwrapper.rmdir.php
  746. */
  747. public function rmdir($uri, $options) {
  748. $this->uri = $uri;
  749. if ($options & STREAM_REPORT_ERRORS) {
  750. return drupal_rmdir($this->getLocalPath());
  751. }
  752. else {
  753. return @drupal_rmdir($this->getLocalPath());
  754. }
  755. }
  756. /**
  757. * Support for stat().
  758. *
  759. * @param $uri
  760. * A string containing the URI to get information about.
  761. * @param $flags
  762. * A bit mask of STREAM_URL_STAT_LINK and STREAM_URL_STAT_QUIET.
  763. *
  764. * @return
  765. * An array with file status, or FALSE in case of an error - see fstat()
  766. * for a description of this array.
  767. *
  768. * @see http://php.net/manual/streamwrapper.url-stat.php
  769. */
  770. public function url_stat($uri, $flags) {
  771. $this->uri = $uri;
  772. $path = $this->getLocalPath();
  773. // Suppress warnings if requested or if the file or directory does not
  774. // exist. This is consistent with PHP's plain filesystem stream wrapper.
  775. if ($flags & STREAM_URL_STAT_QUIET || !file_exists($path)) {
  776. return @stat($path);
  777. }
  778. else {
  779. return stat($path);
  780. }
  781. }
  782. /**
  783. * Support for opendir().
  784. *
  785. * @param $uri
  786. * A string containing the URI to the directory to open.
  787. * @param $options
  788. * Unknown (parameter is not documented in PHP Manual).
  789. *
  790. * @return
  791. * TRUE on success.
  792. *
  793. * @see http://php.net/manual/streamwrapper.dir-opendir.php
  794. */
  795. public function dir_opendir($uri, $options) {
  796. $this->uri = $uri;
  797. $this->handle = opendir($this->getLocalPath());
  798. return (bool) $this->handle;
  799. }
  800. /**
  801. * Support for readdir().
  802. *
  803. * @return
  804. * The next filename, or FALSE if there are no more files in the directory.
  805. *
  806. * @see http://php.net/manual/streamwrapper.dir-readdir.php
  807. */
  808. public function dir_readdir() {
  809. return readdir($this->handle);
  810. }
  811. /**
  812. * Support for rewinddir().
  813. *
  814. * @return
  815. * TRUE on success.
  816. *
  817. * @see http://php.net/manual/streamwrapper.dir-rewinddir.php
  818. */
  819. public function dir_rewinddir() {
  820. rewinddir($this->handle);
  821. // We do not really have a way to signal a failure as rewinddir() does not
  822. // have a return value and there is no way to read a directory handler
  823. // without advancing to the next file.
  824. return TRUE;
  825. }
  826. /**
  827. * Support for closedir().
  828. *
  829. * @return
  830. * TRUE on success.
  831. *
  832. * @see http://php.net/manual/streamwrapper.dir-closedir.php
  833. */
  834. public function dir_closedir() {
  835. closedir($this->handle);
  836. // We do not really have a way to signal a failure as closedir() does not
  837. // have a return value.
  838. return TRUE;
  839. }
  840. }
  841. /**
  842. * Drupal public (public://) stream wrapper class.
  843. *
  844. * Provides support for storing publicly accessible files with the Drupal file
  845. * interface.
  846. */
  847. class DrupalPublicStreamWrapper extends DrupalLocalStreamWrapper {
  848. /**
  849. * Implements abstract public function getDirectoryPath()
  850. */
  851. public function getDirectoryPath() {
  852. return variable_get('file_public_path', conf_path() . '/files');
  853. }
  854. /**
  855. * Overrides getExternalUrl().
  856. *
  857. * Return the HTML URI of a public file.
  858. */
  859. function getExternalUrl() {
  860. $path = str_replace('\\', '/', $this->getTarget());
  861. return $GLOBALS['base_url'] . '/' . self::getDirectoryPath() . '/' . drupal_encode_path($path);
  862. }
  863. /**
  864. * {@inheritdoc}
  865. */
  866. protected function getLocalPath($uri = NULL) {
  867. $path = parent::getLocalPath($uri);
  868. if (variable_get('sa_core_2022_012_override', FALSE)) {
  869. return $path;
  870. }
  871. $private_path = variable_get('file_private_path', FALSE);
  872. if ($private_path) {
  873. $private_path = realpath($private_path);
  874. if ($private_path && strpos($path, $private_path) === 0) {
  875. return FALSE;
  876. }
  877. }
  878. return $path;
  879. }
  880. }
  881. /**
  882. * Drupal private (private://) stream wrapper class.
  883. *
  884. * Provides support for storing privately accessible files with the Drupal file
  885. * interface.
  886. */
  887. class DrupalPrivateStreamWrapper extends DrupalLocalStreamWrapper {
  888. /**
  889. * Implements abstract public function getDirectoryPath()
  890. */
  891. public function getDirectoryPath() {
  892. return variable_get('file_private_path', '');
  893. }
  894. /**
  895. * Overrides getExternalUrl().
  896. *
  897. * Return the HTML URI of a private file.
  898. */
  899. function getExternalUrl() {
  900. $path = str_replace('\\', '/', $this->getTarget());
  901. return url('system/files/' . $path, array('absolute' => TRUE));
  902. }
  903. }
  904. /**
  905. * Drupal temporary (temporary://) stream wrapper class.
  906. *
  907. * Provides support for storing temporarily accessible files with the Drupal
  908. * file interface.
  909. *
  910. * Extends DrupalPublicStreamWrapper.
  911. */
  912. class DrupalTemporaryStreamWrapper extends DrupalLocalStreamWrapper {
  913. /**
  914. * Implements abstract public function getDirectoryPath()
  915. */
  916. public function getDirectoryPath() {
  917. return variable_get('file_temporary_path', file_directory_temp());
  918. }
  919. /**
  920. * Overrides getExternalUrl().
  921. */
  922. public function getExternalUrl() {
  923. $path = str_replace('\\', '/', $this->getTarget());
  924. return url('system/temporary/' . $path, array('absolute' => TRUE));
  925. }
  926. }