PageRenderTime 26ms CodeModel.GetById 9ms RepoModel.GetById 1ms app.codeStats 0ms

/python/php/sdk/google/appengine/ext/cloud_storage_streams/CloudStorageStreamWrapper.php

http://googleappengine.googlecode.com/
PHP | 380 lines | 237 code | 44 blank | 99 comment | 35 complexity | 111e6adb064d7a43b1a746cd8ececaa1 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, Apache-2.0, BSD-3-Clause, GPL-2.0, LGPL-2.1, MIT
  1. <?php
  2. /**
  3. * Copyright 2007 Google Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  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 implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /**
  18. * A user space stream wrapper for reading and writing to Google Cloud Storage.
  19. *
  20. * See: http://www.php.net/manual/en/class.streamwrapper.php
  21. *
  22. */
  23. namespace google\appengine\ext\cloud_storage_streams;
  24. use google\appengine\api\cloud_storage\CloudStorageTools;
  25. use google\appengine\util\ArrayUtil;
  26. /**
  27. * Allowed stream_context options.
  28. * "anonymous": Boolean, if set then OAuth tokens will not be generated.
  29. * "acl": The ACL to apply when creating an object.
  30. * "Content-Type": The content type of the object being written.
  31. */
  32. final class CloudStorageStreamWrapper {
  33. // The client instance that we're using to communicate with GS.
  34. private $client;
  35. // Must be public according to PHP documents - We capture the contents when
  36. // constructing objects.
  37. public $context;
  38. const STREAM_OPEN_FOR_INCLUDE = 0x80;
  39. private static $valid_read_modes = ['r', 'rb', 'rt'];
  40. private static $valid_write_modes = ['w', 'wb', 'wt'];
  41. /**
  42. * Constructs a new stream wrapper.
  43. */
  44. public function __construct() {
  45. }
  46. /**
  47. * Destructs an existing stream wrapper.
  48. */
  49. public function __destruct() {
  50. }
  51. /**
  52. * Close an open directory handle.
  53. */
  54. public function dir_closedir() {
  55. assert(isset($this->client));
  56. $this->client->close();
  57. $this->client = null;
  58. }
  59. /**
  60. * Open a directory handle.
  61. */
  62. public function dir_opendir($path, $options) {
  63. if (!CloudStorageTools::parseFilename($path, $bucket, $object)) {
  64. trigger_error(sprintf("Invalid Google Cloud Storage path: %s", $path),
  65. E_USER_ERROR);
  66. return false;
  67. }
  68. // Assume opening root directory if no object name is specified in path.
  69. if (!isset($object)) {
  70. $object = "/";
  71. }
  72. $this->client = new CloudStorageDirectoryClient($bucket,
  73. $object,
  74. $this->context);
  75. return $this->client->initialise();
  76. }
  77. /**
  78. * Read entry from the directory handle.
  79. *
  80. * @return string representing the next filename, of false if there is no
  81. * next file.
  82. */
  83. public function dir_readdir() {
  84. assert(isset($this->client));
  85. return $this->client->dir_readdir();
  86. }
  87. /**
  88. * Reset the output returned from dir_readdir.
  89. *
  90. * @return bool true if the stream can be rewound, false otherwise.
  91. */
  92. public function dir_rewinddir() {
  93. assert(isset($this->client));
  94. return $this->client->dir_rewinddir();
  95. }
  96. public function mkdir($path, $mode, $options) {
  97. if (!CloudStorageTools::parseFilename($path, $bucket, $object) ||
  98. !isset($object)) {
  99. if (($options | STREAM_REPORT_ERRORS) != 0) {
  100. trigger_error(sprintf("Invalid Google Cloud Storage path: %s", $path),
  101. E_USER_ERROR);
  102. }
  103. return false;
  104. }
  105. $client = new CloudStorageDirectoryClient($bucket,
  106. $object,
  107. $this->context);
  108. return $client->mkdir($options);
  109. }
  110. public function rmdir($path, $options) {
  111. if (!CloudStorageTools::parseFilename($path, $bucket, $object) ||
  112. !isset($object)) {
  113. if (($options | STREAM_REPORT_ERRORS) != 0) {
  114. trigger_error(sprintf("Invalid Google Cloud Storage path: %s", $path),
  115. E_USER_ERROR);
  116. }
  117. return false;
  118. }
  119. $client = new CloudStorageDirectoryClient($bucket,
  120. $object,
  121. $this->context);
  122. return $client->rmdir($options);
  123. }
  124. /**
  125. * Rename a cloud storage object.
  126. *
  127. * @return TRUE if the object was renamed, FALSE otherwise
  128. */
  129. public function rename($from, $to) {
  130. if (!CloudStorageTools::parseFilename($from, $from_bucket, $from_object) ||
  131. !isset($from_object)) {
  132. trigger_error(sprintf("Invalid Google Cloud Storage path: %s", $from),
  133. E_USER_ERROR);
  134. return false;
  135. }
  136. if (!CloudStorageTools::parseFilename($to, $to_bucket, $to_object) ||
  137. !isset($to_object)) {
  138. trigger_error(sprintf("Invalid Google Cloud Storage path: %s", $to),
  139. E_USER_ERROR);
  140. return false;
  141. }
  142. // If the file being renamed is an uploaded file being moved to an allowed
  143. // include bucket trigger a warning.
  144. $allowed_buckets = $this->getAllowedBuckets();
  145. foreach ($_FILES as $file) {
  146. if ($file['tmp_name'] == $from) {
  147. foreach ($allowed_buckets as $allowed_bucket) {
  148. // 5th character indicates start of bucket since it ignores 'gs://'.
  149. if (strpos($to, $allowed_bucket) === 5) {
  150. trigger_error(sprintf('Moving uploaded file (%s) to an allowed ' .
  151. 'include bucket (%s) which may be ' .
  152. 'vulnerable to local file inclusion (LFI).',
  153. $from, $allowed_bucket),
  154. E_USER_WARNING);
  155. break 2;
  156. }
  157. }
  158. }
  159. }
  160. $client = new CloudStorageRenameClient($from_bucket,
  161. $from_object,
  162. $to_bucket,
  163. $to_object,
  164. $this->context);
  165. return $client->rename();
  166. }
  167. /**
  168. * Retrieve the underlaying resource of the stream, called in response to
  169. * stream_select().
  170. *
  171. * As GS streams have no underlying resource, we can only return false
  172. */
  173. public function stream_cast() {
  174. return false;
  175. }
  176. /**
  177. * All resources that were locked, or allocated, by the wrapper should be
  178. * released.
  179. *
  180. * No value is returned.
  181. */
  182. public function stream_close() {
  183. assert(isset($this->client));
  184. $this->client->close();
  185. $this->client = null;
  186. }
  187. /**
  188. * Tests for end-of-file on a file pointer.
  189. *
  190. * @return TRUE if the read/write position is at the end of the stream and if
  191. * no more data is available to be read, or FALSE otherwise
  192. */
  193. public function stream_eof() {
  194. assert(isset($this->client));
  195. return $this->client->eof();
  196. }
  197. /**
  198. * Flushes the output.
  199. *
  200. * @return TRUE if the cached data was successfully stored (or if there was
  201. * no data to store), or FALSE if the data could not be stored.
  202. */
  203. public function stream_flush() {
  204. assert(isset($this->client));
  205. return $this->client->flush();
  206. }
  207. public function stream_metadata($path, $option, $value) {
  208. return false;
  209. }
  210. public function stream_open($path, $mode, $options, &$opened_path) {
  211. if (!CloudStorageTools::parseFilename($path, $bucket, $object) ||
  212. !isset($object)) {
  213. if (($options & STREAM_REPORT_ERRORS) != 0) {
  214. trigger_error(sprintf("Invalid Google Cloud Storage path: %s", $path),
  215. E_USER_ERROR);
  216. }
  217. return false;
  218. }
  219. if (($options & self::STREAM_OPEN_FOR_INCLUDE) != 0) {
  220. $allowed_buckets = $this->getAllowedBuckets();
  221. $include_allowed = false;
  222. foreach ($allowed_buckets as $bucket_name) {
  223. // Check if the allowed bucket includes a path restriction and if so
  224. // separate the path from the bucket name.
  225. if (strpos($bucket_name, '/') !== false) {
  226. list($bucket_name, $object_path) = explode('/', $bucket_name, 2);
  227. }
  228. if ($bucket_name === $bucket) {
  229. // If a path restriction is set then ensure that the object either
  230. // starts with or is equal to the path.
  231. $include_allowed = !isset($object_path) ||
  232. (isset($object) && strpos($object, $object_path) === 1);
  233. break;
  234. }
  235. }
  236. if (!$include_allowed) {
  237. if (($options & STREAM_REPORT_ERRORS) != 0) {
  238. trigger_error(
  239. sprintf("Not allowed to include/require from bucket '%s'",
  240. $bucket),
  241. E_USER_ERROR);
  242. }
  243. return false;
  244. }
  245. }
  246. if (in_array($mode, self::$valid_read_modes)) {
  247. $this->client = new CloudStorageReadClient($bucket,
  248. $object,
  249. $this->context);
  250. } else if (in_array($mode, self::$valid_write_modes)) {
  251. $this->client = new CloudStorageWriteClient($bucket,
  252. $object,
  253. $this->context);
  254. } else {
  255. if (($options & STREAM_REPORT_ERRORS) != 0) {
  256. trigger_error(sprintf("Invalid mode: %s", $mode), E_USER_ERROR);
  257. }
  258. return false;
  259. }
  260. return $this->client->initialize();
  261. }
  262. /**
  263. * Read from a stream, return string of bytes.
  264. */
  265. public function stream_read($count) {
  266. assert(isset($this->client));
  267. return $this->client->read($count);
  268. }
  269. public function stream_seek($offset, $whence) {
  270. assert(isset($this->client));
  271. return $this->client->seek($offset, $whence);
  272. }
  273. public function stream_set_option($option, $arg1, $arg2) {
  274. assert(isset($this->client));
  275. return false;
  276. }
  277. public function stream_stat() {
  278. assert(isset($this->client));
  279. return $this->client->stat();
  280. }
  281. public function stream_tell() {
  282. assert(isset($this->client));
  283. return $this->client->tell();
  284. }
  285. /**
  286. * Return the number of bytes written.
  287. */
  288. public function stream_write($data) {
  289. assert(isset($this->client));
  290. return $this->client->write($data);
  291. }
  292. /**
  293. * Deletes a file. Called in response to unlink($filename).
  294. */
  295. public function unlink($path) {
  296. if (!CloudStorageTools::parseFilename($path, $bucket, $object) ||
  297. !isset($object)) {
  298. trigger_error(sprintf("Invalid Google Cloud Storage path: %s", $path),
  299. E_USER_ERROR);
  300. return false;
  301. }
  302. $this->client = new CloudStorageDeleteClient($bucket,
  303. $object,
  304. $this->context);
  305. return $this->client->delete();
  306. }
  307. public function url_stat($path, $flags) {
  308. if (!CloudStorageTools::parseFilename($path, $bucket, $object)) {
  309. if (($flags & STREAM_URL_STAT_QUIET) != 0) {
  310. trigger_error(sprintf("Invalid Google Cloud Storage path: %s", $path),
  311. E_USER_ERROR);
  312. return false;
  313. }
  314. }
  315. $client = new CloudStorageUrlStatClient($bucket,
  316. $object,
  317. $this->context,
  318. $flags);
  319. return $client->stat();
  320. }
  321. private function getAllowedBuckets() {
  322. static $allowed_buckets;
  323. if (!isset($allowed_buckets)) {
  324. $allowed_buckets = explode(',', GAE_INCLUDE_GS_BUCKETS);
  325. $allowed_buckets = array_map('trim', $allowed_buckets);
  326. $allowed_bukcets = array_filter($allowed_buckets);
  327. }
  328. return $allowed_buckets;
  329. }
  330. public function getMetaData() {
  331. return $this->client->getMetaData();
  332. }
  333. public function getContentType() {
  334. return $this->client->getContentType();
  335. }
  336. }