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

/main/streams/streams.c

http://github.com/infusion/PHP
C | 2198 lines | 1598 code | 360 blank | 240 comment | 512 complexity | 0565eea8f249f3c67e700517e726691e MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-2.1, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 5 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2011 The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Authors: Wez Furlong <wez@thebrainroom.com> |
  16. | Borrowed code from: |
  17. | Rasmus Lerdorf <rasmus@lerdorf.on.ca> |
  18. | Jim Winstead <jimw@php.net> |
  19. +----------------------------------------------------------------------+
  20. */
  21. /* $Id: streams.c 307922 2011-02-01 18:10:35Z cataphract $ */
  22. #define _GNU_SOURCE
  23. #include "php.h"
  24. #include "php_globals.h"
  25. #include "php_network.h"
  26. #include "php_open_temporary_file.h"
  27. #include "ext/standard/file.h"
  28. #include "ext/standard/basic_functions.h" /* for BG(mmap_file) (not strictly required) */
  29. #include "ext/standard/php_string.h" /* for php_memnstr, used by php_stream_get_record() */
  30. #include <stddef.h>
  31. #include <fcntl.h>
  32. #include "php_streams_int.h"
  33. /* {{{ resource and registration code */
  34. /* Global wrapper hash, copied to FG(stream_wrappers) on registration of volatile wrapper */
  35. static HashTable url_stream_wrappers_hash;
  36. static int le_stream = FAILURE; /* true global */
  37. static int le_pstream = FAILURE; /* true global */
  38. static int le_stream_filter = FAILURE; /* true global */
  39. PHPAPI int php_file_le_stream(void)
  40. {
  41. return le_stream;
  42. }
  43. PHPAPI int php_file_le_pstream(void)
  44. {
  45. return le_pstream;
  46. }
  47. PHPAPI int php_file_le_stream_filter(void)
  48. {
  49. return le_stream_filter;
  50. }
  51. PHPAPI HashTable *_php_stream_get_url_stream_wrappers_hash(TSRMLS_D)
  52. {
  53. return (FG(stream_wrappers) ? FG(stream_wrappers) : &url_stream_wrappers_hash);
  54. }
  55. PHPAPI HashTable *php_stream_get_url_stream_wrappers_hash_global(void)
  56. {
  57. return &url_stream_wrappers_hash;
  58. }
  59. static int _php_stream_release_context(zend_rsrc_list_entry *le, void *pContext TSRMLS_DC)
  60. {
  61. if (le->ptr == pContext) {
  62. return --le->refcount == 0;
  63. }
  64. return 0;
  65. }
  66. static int forget_persistent_resource_id_numbers(zend_rsrc_list_entry *rsrc TSRMLS_DC)
  67. {
  68. php_stream *stream;
  69. if (Z_TYPE_P(rsrc) != le_pstream) {
  70. return 0;
  71. }
  72. stream = (php_stream*)rsrc->ptr;
  73. #if STREAM_DEBUG
  74. fprintf(stderr, "forget_persistent: %s:%p\n", stream->ops->label, stream);
  75. #endif
  76. stream->rsrc_id = FAILURE;
  77. if (stream->context) {
  78. zend_hash_apply_with_argument(&EG(regular_list),
  79. (apply_func_arg_t) _php_stream_release_context,
  80. stream->context TSRMLS_CC);
  81. stream->context = NULL;
  82. }
  83. return 0;
  84. }
  85. PHP_RSHUTDOWN_FUNCTION(streams)
  86. {
  87. zend_hash_apply(&EG(persistent_list), (apply_func_t)forget_persistent_resource_id_numbers TSRMLS_CC);
  88. return SUCCESS;
  89. }
  90. PHPAPI int php_stream_from_persistent_id(const char *persistent_id, php_stream **stream TSRMLS_DC)
  91. {
  92. zend_rsrc_list_entry *le;
  93. if (zend_hash_find(&EG(persistent_list), (char*)persistent_id, strlen(persistent_id)+1, (void*) &le) == SUCCESS) {
  94. if (Z_TYPE_P(le) == le_pstream) {
  95. if (stream) {
  96. *stream = (php_stream*)le->ptr;
  97. le->refcount++;
  98. (*stream)->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, *stream, le_pstream);
  99. }
  100. return PHP_STREAM_PERSISTENT_SUCCESS;
  101. }
  102. return PHP_STREAM_PERSISTENT_FAILURE;
  103. }
  104. return PHP_STREAM_PERSISTENT_NOT_EXIST;
  105. }
  106. /* }}} */
  107. /* {{{ wrapper error reporting */
  108. void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, const char *path, const char *caption TSRMLS_DC)
  109. {
  110. char *tmp = estrdup(path);
  111. char *msg;
  112. int free_msg = 0;
  113. if (wrapper) {
  114. if (wrapper->err_count > 0) {
  115. int i;
  116. size_t l;
  117. int brlen;
  118. char *br;
  119. if (PG(html_errors)) {
  120. brlen = 7;
  121. br = "<br />\n";
  122. } else {
  123. brlen = 1;
  124. br = "\n";
  125. }
  126. for (i = 0, l = 0; i < wrapper->err_count; i++) {
  127. l += strlen(wrapper->err_stack[i]);
  128. if (i < wrapper->err_count - 1) {
  129. l += brlen;
  130. }
  131. }
  132. msg = emalloc(l + 1);
  133. msg[0] = '\0';
  134. for (i = 0; i < wrapper->err_count; i++) {
  135. strcat(msg, wrapper->err_stack[i]);
  136. if (i < wrapper->err_count - 1) {
  137. strcat(msg, br);
  138. }
  139. }
  140. free_msg = 1;
  141. } else {
  142. if (wrapper == &php_plain_files_wrapper) {
  143. msg = strerror(errno);
  144. } else {
  145. msg = "operation failed";
  146. }
  147. }
  148. } else {
  149. msg = "no suitable wrapper could be found";
  150. }
  151. php_strip_url_passwd(tmp);
  152. php_error_docref1(NULL TSRMLS_CC, tmp, E_WARNING, "%s: %s", caption, msg);
  153. efree(tmp);
  154. if (free_msg) {
  155. efree(msg);
  156. }
  157. }
  158. void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper TSRMLS_DC)
  159. {
  160. if (wrapper) {
  161. /* tidy up the error stack */
  162. int i;
  163. for (i = 0; i < wrapper->err_count; i++) {
  164. efree(wrapper->err_stack[i]);
  165. }
  166. if (wrapper->err_stack) {
  167. efree(wrapper->err_stack);
  168. }
  169. wrapper->err_stack = NULL;
  170. wrapper->err_count = 0;
  171. }
  172. }
  173. PHPAPI void php_stream_wrapper_log_error(php_stream_wrapper *wrapper, int options TSRMLS_DC, const char *fmt, ...)
  174. {
  175. va_list args;
  176. char *buffer = NULL;
  177. va_start(args, fmt);
  178. vspprintf(&buffer, 0, fmt, args);
  179. va_end(args);
  180. if (options & REPORT_ERRORS || wrapper == NULL) {
  181. php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", buffer);
  182. efree(buffer);
  183. } else {
  184. /* append to stack */
  185. wrapper->err_stack = erealloc(wrapper->err_stack, (wrapper->err_count + 1) * sizeof(char *));
  186. if (wrapper->err_stack) {
  187. wrapper->err_stack[wrapper->err_count++] = buffer;
  188. }
  189. }
  190. }
  191. /* }}} */
  192. /* allocate a new stream for a particular ops */
  193. PHPAPI php_stream *_php_stream_alloc(php_stream_ops *ops, void *abstract, const char *persistent_id, const char *mode STREAMS_DC TSRMLS_DC) /* {{{ */
  194. {
  195. php_stream *ret;
  196. ret = (php_stream*) pemalloc_rel_orig(sizeof(php_stream), persistent_id ? 1 : 0);
  197. memset(ret, 0, sizeof(php_stream));
  198. ret->readfilters.stream = ret;
  199. ret->writefilters.stream = ret;
  200. #if STREAM_DEBUG
  201. fprintf(stderr, "stream_alloc: %s:%p persistent=%s\n", ops->label, ret, persistent_id);
  202. #endif
  203. ret->ops = ops;
  204. ret->abstract = abstract;
  205. ret->is_persistent = persistent_id ? 1 : 0;
  206. ret->chunk_size = FG(def_chunk_size);
  207. #if ZEND_DEBUG
  208. ret->open_filename = __zend_orig_filename ? __zend_orig_filename : __zend_filename;
  209. ret->open_lineno = __zend_orig_lineno ? __zend_orig_lineno : __zend_lineno;
  210. #endif
  211. if (FG(auto_detect_line_endings)) {
  212. ret->flags |= PHP_STREAM_FLAG_DETECT_EOL;
  213. }
  214. if (persistent_id) {
  215. zend_rsrc_list_entry le;
  216. Z_TYPE(le) = le_pstream;
  217. le.ptr = ret;
  218. le.refcount = 0;
  219. if (FAILURE == zend_hash_update(&EG(persistent_list), (char *)persistent_id,
  220. strlen(persistent_id) + 1,
  221. (void *)&le, sizeof(le), NULL)) {
  222. pefree(ret, 1);
  223. return NULL;
  224. }
  225. }
  226. ret->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, ret, persistent_id ? le_pstream : le_stream);
  227. strlcpy(ret->mode, mode, sizeof(ret->mode));
  228. return ret;
  229. }
  230. /* }}} */
  231. static int _php_stream_free_persistent(zend_rsrc_list_entry *le, void *pStream TSRMLS_DC)
  232. {
  233. return le->ptr == pStream;
  234. }
  235. PHPAPI int _php_stream_free(php_stream *stream, int close_options TSRMLS_DC) /* {{{ */
  236. {
  237. int ret = 1;
  238. int remove_rsrc = 1;
  239. int preserve_handle = close_options & PHP_STREAM_FREE_PRESERVE_HANDLE ? 1 : 0;
  240. int release_cast = 1;
  241. php_stream_context *context = stream->context;
  242. if (stream->flags & PHP_STREAM_FLAG_NO_CLOSE) {
  243. preserve_handle = 1;
  244. }
  245. #if STREAM_DEBUG
  246. fprintf(stderr, "stream_free: %s:%p[%s] in_free=%d opts=%08x\n", stream->ops->label, stream, stream->orig_path, stream->in_free, close_options);
  247. #endif
  248. /* recursion protection */
  249. if (stream->in_free) {
  250. return 1;
  251. }
  252. stream->in_free++;
  253. /* if we are releasing the stream only (and preserving the underlying handle),
  254. * we need to do things a little differently.
  255. * We are only ever called like this when the stream is cast to a FILE*
  256. * for include (or other similar) purposes.
  257. * */
  258. if (preserve_handle) {
  259. if (stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE) {
  260. /* If the stream was fopencookied, we must NOT touch anything
  261. * here, as the cookied stream relies on it all.
  262. * Instead, mark the stream as OK to auto-clean */
  263. php_stream_auto_cleanup(stream);
  264. stream->in_free--;
  265. return 0;
  266. }
  267. /* otherwise, make sure that we don't close the FILE* from a cast */
  268. release_cast = 0;
  269. }
  270. #if STREAM_DEBUG
  271. fprintf(stderr, "stream_free: %s:%p[%s] preserve_handle=%d release_cast=%d remove_rsrc=%d\n",
  272. stream->ops->label, stream, stream->orig_path, preserve_handle, release_cast, remove_rsrc);
  273. #endif
  274. /* make sure everything is saved */
  275. _php_stream_flush(stream, 1 TSRMLS_CC);
  276. /* If not called from the resource dtor, remove the stream from the resource list. */
  277. if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) == 0 && remove_rsrc) {
  278. zend_list_delete(stream->rsrc_id);
  279. }
  280. /* Remove stream from any context link list */
  281. if (stream->context && stream->context->links) {
  282. php_stream_context_del_link(stream->context, stream);
  283. }
  284. if (close_options & PHP_STREAM_FREE_CALL_DTOR) {
  285. if (release_cast && stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE) {
  286. /* calling fclose on an fopencookied stream will ultimately
  287. call this very same function. If we were called via fclose,
  288. the cookie_closer unsets the fclose_stdiocast flags, so
  289. we can be sure that we only reach here when PHP code calls
  290. php_stream_free.
  291. Lets let the cookie code clean it all up.
  292. */
  293. stream->in_free = 0;
  294. return fclose(stream->stdiocast);
  295. }
  296. ret = stream->ops->close(stream, preserve_handle ? 0 : 1 TSRMLS_CC);
  297. stream->abstract = NULL;
  298. /* tidy up any FILE* that might have been fdopened */
  299. if (release_cast && stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FDOPEN && stream->stdiocast) {
  300. fclose(stream->stdiocast);
  301. stream->stdiocast = NULL;
  302. stream->fclose_stdiocast = PHP_STREAM_FCLOSE_NONE;
  303. }
  304. }
  305. if (close_options & PHP_STREAM_FREE_RELEASE_STREAM) {
  306. while (stream->readfilters.head) {
  307. php_stream_filter_remove(stream->readfilters.head, 1 TSRMLS_CC);
  308. }
  309. while (stream->writefilters.head) {
  310. php_stream_filter_remove(stream->writefilters.head, 1 TSRMLS_CC);
  311. }
  312. if (stream->wrapper && stream->wrapper->wops && stream->wrapper->wops->stream_closer) {
  313. stream->wrapper->wops->stream_closer(stream->wrapper, stream TSRMLS_CC);
  314. stream->wrapper = NULL;
  315. }
  316. if (stream->wrapperdata) {
  317. zval_ptr_dtor(&stream->wrapperdata);
  318. stream->wrapperdata = NULL;
  319. }
  320. if (stream->readbuf) {
  321. pefree(stream->readbuf, stream->is_persistent);
  322. stream->readbuf = NULL;
  323. }
  324. if (stream->is_persistent && (close_options & PHP_STREAM_FREE_PERSISTENT)) {
  325. /* we don't work with *stream but need its value for comparison */
  326. zend_hash_apply_with_argument(&EG(persistent_list), (apply_func_arg_t) _php_stream_free_persistent, stream TSRMLS_CC);
  327. }
  328. #if ZEND_DEBUG
  329. if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) && (stream->__exposed == 0) && (EG(error_reporting) & E_WARNING)) {
  330. /* it leaked: Lets deliberately NOT pefree it so that the memory manager shows it
  331. * as leaked; it will log a warning, but lets help it out and display what kind
  332. * of stream it was. */
  333. char *leakinfo;
  334. spprintf(&leakinfo, 0, __FILE__ "(%d) : Stream of type '%s' %p (path:%s) was not closed\n", __LINE__, stream->ops->label, stream, stream->orig_path);
  335. if (stream->orig_path) {
  336. pefree(stream->orig_path, stream->is_persistent);
  337. stream->orig_path = NULL;
  338. }
  339. # if defined(PHP_WIN32)
  340. OutputDebugString(leakinfo);
  341. # else
  342. fprintf(stderr, "%s", leakinfo);
  343. # endif
  344. efree(leakinfo);
  345. } else {
  346. if (stream->orig_path) {
  347. pefree(stream->orig_path, stream->is_persistent);
  348. stream->orig_path = NULL;
  349. }
  350. pefree(stream, stream->is_persistent);
  351. }
  352. #else
  353. if (stream->orig_path) {
  354. pefree(stream->orig_path, stream->is_persistent);
  355. stream->orig_path = NULL;
  356. }
  357. pefree(stream, stream->is_persistent);
  358. #endif
  359. }
  360. if (context) {
  361. zend_list_delete(context->rsrc_id);
  362. }
  363. return ret;
  364. }
  365. /* }}} */
  366. /* {{{ generic stream operations */
  367. static void php_stream_fill_read_buffer(php_stream *stream, size_t size TSRMLS_DC)
  368. {
  369. /* allocate/fill the buffer */
  370. if (stream->readfilters.head) {
  371. char *chunk_buf;
  372. int err_flag = 0;
  373. php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL };
  374. php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out, *brig_swap;
  375. /* Invalidate the existing cache, otherwise reads can fail, see note in
  376. main/streams/filter.c::_php_stream_filter_append */
  377. stream->writepos = stream->readpos = 0;
  378. /* allocate a buffer for reading chunks */
  379. chunk_buf = emalloc(stream->chunk_size);
  380. while (!stream->eof && !err_flag && (stream->writepos - stream->readpos < (off_t)size)) {
  381. size_t justread = 0;
  382. int flags;
  383. php_stream_bucket *bucket;
  384. php_stream_filter_status_t status = PSFS_ERR_FATAL;
  385. php_stream_filter *filter;
  386. /* read a chunk into a bucket */
  387. justread = stream->ops->read(stream, chunk_buf, stream->chunk_size TSRMLS_CC);
  388. if (justread && justread != (size_t)-1) {
  389. bucket = php_stream_bucket_new(stream, chunk_buf, justread, 0, 0 TSRMLS_CC);
  390. /* after this call, bucket is owned by the brigade */
  391. php_stream_bucket_append(brig_inp, bucket TSRMLS_CC);
  392. flags = PSFS_FLAG_NORMAL;
  393. } else {
  394. flags = stream->eof ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC;
  395. }
  396. /* wind the handle... */
  397. for (filter = stream->readfilters.head; filter; filter = filter->next) {
  398. status = filter->fops->filter(stream, filter, brig_inp, brig_outp, NULL, flags TSRMLS_CC);
  399. if (status != PSFS_PASS_ON) {
  400. break;
  401. }
  402. /* brig_out becomes brig_in.
  403. * brig_in will always be empty here, as the filter MUST attach any un-consumed buckets
  404. * to its own brigade */
  405. brig_swap = brig_inp;
  406. brig_inp = brig_outp;
  407. brig_outp = brig_swap;
  408. memset(brig_outp, 0, sizeof(*brig_outp));
  409. }
  410. switch (status) {
  411. case PSFS_PASS_ON:
  412. /* we get here when the last filter in the chain has data to pass on.
  413. * in this situation, we are passing the brig_in brigade into the
  414. * stream read buffer */
  415. while (brig_inp->head) {
  416. bucket = brig_inp->head;
  417. /* grow buffer to hold this bucket
  418. * TODO: this can fail for persistent streams */
  419. if (stream->readbuflen - stream->writepos < bucket->buflen) {
  420. stream->readbuflen += bucket->buflen;
  421. stream->readbuf = perealloc(stream->readbuf, stream->readbuflen,
  422. stream->is_persistent);
  423. }
  424. memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen);
  425. stream->writepos += bucket->buflen;
  426. php_stream_bucket_unlink(bucket TSRMLS_CC);
  427. php_stream_bucket_delref(bucket TSRMLS_CC);
  428. }
  429. break;
  430. case PSFS_FEED_ME:
  431. /* when a filter needs feeding, there is no brig_out to deal with.
  432. * we simply continue the loop; if the caller needs more data,
  433. * we will read again, otherwise out job is done here */
  434. if (justread == 0) {
  435. /* there is no data */
  436. err_flag = 1;
  437. break;
  438. }
  439. continue;
  440. case PSFS_ERR_FATAL:
  441. /* some fatal error. Theoretically, the stream is borked, so all
  442. * further reads should fail. */
  443. err_flag = 1;
  444. break;
  445. }
  446. if (justread == 0 || justread == (size_t)-1) {
  447. break;
  448. }
  449. }
  450. efree(chunk_buf);
  451. } else {
  452. /* is there enough data in the buffer ? */
  453. if (stream->writepos - stream->readpos < (off_t)size) {
  454. size_t justread = 0;
  455. /* reduce buffer memory consumption if possible, to avoid a realloc */
  456. if (stream->readbuf && stream->readbuflen - stream->writepos < stream->chunk_size) {
  457. memmove(stream->readbuf, stream->readbuf + stream->readpos, stream->readbuflen - stream->readpos);
  458. stream->writepos -= stream->readpos;
  459. stream->readpos = 0;
  460. }
  461. /* grow the buffer if required
  462. * TODO: this can fail for persistent streams */
  463. if (stream->readbuflen - stream->writepos < stream->chunk_size) {
  464. stream->readbuflen += stream->chunk_size;
  465. stream->readbuf = perealloc(stream->readbuf, stream->readbuflen,
  466. stream->is_persistent);
  467. }
  468. justread = stream->ops->read(stream, stream->readbuf + stream->writepos,
  469. stream->readbuflen - stream->writepos
  470. TSRMLS_CC);
  471. if (justread != (size_t)-1) {
  472. stream->writepos += justread;
  473. }
  474. }
  475. }
  476. }
  477. PHPAPI size_t _php_stream_read(php_stream *stream, char *buf, size_t size TSRMLS_DC)
  478. {
  479. size_t toread = 0, didread = 0;
  480. while (size > 0) {
  481. /* take from the read buffer first.
  482. * It is possible that a buffered stream was switched to non-buffered, so we
  483. * drain the remainder of the buffer before using the "raw" read mode for
  484. * the excess */
  485. if (stream->writepos > stream->readpos) {
  486. toread = stream->writepos - stream->readpos;
  487. if (toread > size) {
  488. toread = size;
  489. }
  490. memcpy(buf, stream->readbuf + stream->readpos, toread);
  491. stream->readpos += toread;
  492. size -= toread;
  493. buf += toread;
  494. didread += toread;
  495. }
  496. /* ignore eof here; the underlying state might have changed */
  497. if (size == 0) {
  498. break;
  499. }
  500. if (!stream->readfilters.head && (stream->flags & PHP_STREAM_FLAG_NO_BUFFER || stream->chunk_size == 1)) {
  501. toread = stream->ops->read(stream, buf, size TSRMLS_CC);
  502. } else {
  503. php_stream_fill_read_buffer(stream, size TSRMLS_CC);
  504. toread = stream->writepos - stream->readpos;
  505. if (toread > size) {
  506. toread = size;
  507. }
  508. if (toread > 0) {
  509. memcpy(buf, stream->readbuf + stream->readpos, toread);
  510. stream->readpos += toread;
  511. }
  512. }
  513. if (toread > 0) {
  514. didread += toread;
  515. buf += toread;
  516. size -= toread;
  517. } else {
  518. /* EOF, or temporary end of data (for non-blocking mode). */
  519. break;
  520. }
  521. /* just break anyway, to avoid greedy read */
  522. if (stream->wrapper != &php_plain_files_wrapper) {
  523. break;
  524. }
  525. }
  526. if (didread > 0) {
  527. stream->position += didread;
  528. }
  529. return didread;
  530. }
  531. PHPAPI int _php_stream_eof(php_stream *stream TSRMLS_DC)
  532. {
  533. /* if there is data in the buffer, it's not EOF */
  534. if (stream->writepos - stream->readpos > 0) {
  535. return 0;
  536. }
  537. /* use the configured timeout when checking eof */
  538. if (!stream->eof && PHP_STREAM_OPTION_RETURN_ERR ==
  539. php_stream_set_option(stream, PHP_STREAM_OPTION_CHECK_LIVENESS,
  540. 0, NULL)) {
  541. stream->eof = 1;
  542. }
  543. return stream->eof;
  544. }
  545. PHPAPI int _php_stream_putc(php_stream *stream, int c TSRMLS_DC)
  546. {
  547. unsigned char buf = c;
  548. if (php_stream_write(stream, &buf, 1) > 0) {
  549. return 1;
  550. }
  551. return EOF;
  552. }
  553. PHPAPI int _php_stream_getc(php_stream *stream TSRMLS_DC)
  554. {
  555. char buf;
  556. if (php_stream_read(stream, &buf, 1) > 0) {
  557. return buf & 0xff;
  558. }
  559. return EOF;
  560. }
  561. PHPAPI int _php_stream_puts(php_stream *stream, char *buf TSRMLS_DC)
  562. {
  563. int len;
  564. char newline[2] = "\n"; /* is this OK for Win? */
  565. len = strlen(buf);
  566. if (len > 0 && php_stream_write(stream, buf, len) && php_stream_write(stream, newline, 1)) {
  567. return 1;
  568. }
  569. return 0;
  570. }
  571. PHPAPI int _php_stream_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
  572. {
  573. memset(ssb, 0, sizeof(*ssb));
  574. /* if the stream was wrapped, allow the wrapper to stat it */
  575. if (stream->wrapper && stream->wrapper->wops->stream_stat != NULL) {
  576. return stream->wrapper->wops->stream_stat(stream->wrapper, stream, ssb TSRMLS_CC);
  577. }
  578. /* if the stream doesn't directly support stat-ing, return with failure.
  579. * We could try and emulate this by casting to a FD and fstat-ing it,
  580. * but since the fd might not represent the actual underlying content
  581. * this would give bogus results. */
  582. if (stream->ops->stat == NULL) {
  583. return -1;
  584. }
  585. return (stream->ops->stat)(stream, ssb TSRMLS_CC);
  586. }
  587. PHPAPI char *php_stream_locate_eol(php_stream *stream, char *buf, size_t buf_len TSRMLS_DC)
  588. {
  589. size_t avail;
  590. char *cr, *lf, *eol = NULL;
  591. char *readptr;
  592. if (!buf) {
  593. readptr = stream->readbuf + stream->readpos;
  594. avail = stream->writepos - stream->readpos;
  595. } else {
  596. readptr = buf;
  597. avail = buf_len;
  598. }
  599. /* Look for EOL */
  600. if (stream->flags & PHP_STREAM_FLAG_DETECT_EOL) {
  601. cr = memchr(readptr, '\r', avail);
  602. lf = memchr(readptr, '\n', avail);
  603. if (cr && lf != cr + 1 && !(lf && lf < cr)) {
  604. /* mac */
  605. stream->flags ^= PHP_STREAM_FLAG_DETECT_EOL;
  606. stream->flags |= PHP_STREAM_FLAG_EOL_MAC;
  607. eol = cr;
  608. } else if ((cr && lf && cr == lf - 1) || (lf)) {
  609. /* dos or unix endings */
  610. stream->flags ^= PHP_STREAM_FLAG_DETECT_EOL;
  611. eol = lf;
  612. }
  613. } else if (stream->flags & PHP_STREAM_FLAG_EOL_MAC) {
  614. eol = memchr(readptr, '\r', avail);
  615. } else {
  616. /* unix (and dos) line endings */
  617. eol = memchr(readptr, '\n', avail);
  618. }
  619. return eol;
  620. }
  621. /* If buf == NULL, the buffer will be allocated automatically and will be of an
  622. * appropriate length to hold the line, regardless of the line length, memory
  623. * permitting */
  624. PHPAPI char *_php_stream_get_line(php_stream *stream, char *buf, size_t maxlen,
  625. size_t *returned_len TSRMLS_DC)
  626. {
  627. size_t avail = 0;
  628. size_t current_buf_size = 0;
  629. size_t total_copied = 0;
  630. int grow_mode = 0;
  631. char *bufstart = buf;
  632. if (buf == NULL) {
  633. grow_mode = 1;
  634. } else if (maxlen == 0) {
  635. return NULL;
  636. }
  637. /*
  638. * If the underlying stream operations block when no new data is readable,
  639. * we need to take extra precautions.
  640. *
  641. * If there is buffered data available, we check for a EOL. If it exists,
  642. * we pass the data immediately back to the caller. This saves a call
  643. * to the read implementation and will not block where blocking
  644. * is not necessary at all.
  645. *
  646. * If the stream buffer contains more data than the caller requested,
  647. * we can also avoid that costly step and simply return that data.
  648. */
  649. for (;;) {
  650. avail = stream->writepos - stream->readpos;
  651. if (avail > 0) {
  652. size_t cpysz = 0;
  653. char *readptr;
  654. char *eol;
  655. int done = 0;
  656. readptr = stream->readbuf + stream->readpos;
  657. eol = php_stream_locate_eol(stream, NULL, 0 TSRMLS_CC);
  658. if (eol) {
  659. cpysz = eol - readptr + 1;
  660. done = 1;
  661. } else {
  662. cpysz = avail;
  663. }
  664. if (grow_mode) {
  665. /* allow room for a NUL. If this realloc is really a realloc
  666. * (ie: second time around), we get an extra byte. In most
  667. * cases, with the default chunk size of 8K, we will only
  668. * incur that overhead once. When people have lines longer
  669. * than 8K, we waste 1 byte per additional 8K or so.
  670. * That seems acceptable to me, to avoid making this code
  671. * hard to follow */
  672. bufstart = erealloc(bufstart, current_buf_size + cpysz + 1);
  673. current_buf_size += cpysz + 1;
  674. buf = bufstart + total_copied;
  675. } else {
  676. if (cpysz >= maxlen - 1) {
  677. cpysz = maxlen - 1;
  678. done = 1;
  679. }
  680. }
  681. memcpy(buf, readptr, cpysz);
  682. stream->position += cpysz;
  683. stream->readpos += cpysz;
  684. buf += cpysz;
  685. maxlen -= cpysz;
  686. total_copied += cpysz;
  687. if (done) {
  688. break;
  689. }
  690. } else if (stream->eof) {
  691. break;
  692. } else {
  693. /* XXX: Should be fine to always read chunk_size */
  694. size_t toread;
  695. if (grow_mode) {
  696. toread = stream->chunk_size;
  697. } else {
  698. toread = maxlen - 1;
  699. if (toread > stream->chunk_size) {
  700. toread = stream->chunk_size;
  701. }
  702. }
  703. php_stream_fill_read_buffer(stream, toread TSRMLS_CC);
  704. if (stream->writepos - stream->readpos == 0) {
  705. break;
  706. }
  707. }
  708. }
  709. if (total_copied == 0) {
  710. if (grow_mode) {
  711. assert(bufstart == NULL);
  712. }
  713. return NULL;
  714. }
  715. buf[0] = '\0';
  716. if (returned_len) {
  717. *returned_len = total_copied;
  718. }
  719. return bufstart;
  720. }
  721. PHPAPI char *php_stream_get_record(php_stream *stream, size_t maxlen, size_t *returned_len, char *delim, size_t delim_len TSRMLS_DC)
  722. {
  723. char *e, *buf;
  724. size_t toread, len;
  725. int skip = 0;
  726. len = stream->writepos - stream->readpos;
  727. /* make sure the stream read buffer has maxlen bytes */
  728. while (len < maxlen) {
  729. size_t just_read;
  730. toread = MIN(maxlen - len, stream->chunk_size);
  731. php_stream_fill_read_buffer(stream, len + toread TSRMLS_CC);
  732. just_read = (stream->writepos - stream->readpos) - len;
  733. len += just_read;
  734. /* read operation have less data than request; assume the stream is
  735. * temporarily or permanently out of data */
  736. if (just_read < toread) {
  737. break;
  738. }
  739. }
  740. if (delim_len == 0 || !delim) {
  741. toread = maxlen;
  742. } else {
  743. size_t seek_len;
  744. /* set the maximum number of bytes we're allowed to read from buffer */
  745. seek_len = stream->writepos - stream->readpos;
  746. if (seek_len > maxlen) {
  747. seek_len = maxlen;
  748. }
  749. if (delim_len == 1) {
  750. e = memchr(stream->readbuf + stream->readpos, *delim, seek_len);
  751. } else {
  752. e = php_memnstr(stream->readbuf + stream->readpos, delim, delim_len, (stream->readbuf + stream->readpos + seek_len));
  753. }
  754. if (!e) {
  755. /* return with error if the delimiter string was not found, we
  756. * could not completely fill the read buffer with maxlen bytes
  757. * and we don't know we've reached end of file. Added with
  758. * non-blocking streams in mind, where this situation is frequent */
  759. if (seek_len < maxlen && !stream->eof) {
  760. return NULL;
  761. }
  762. toread = maxlen;
  763. } else {
  764. toread = e - (char *) stream->readbuf - stream->readpos;
  765. /* we found the delimiter, so advance the read pointer past it */
  766. skip = 1;
  767. }
  768. }
  769. if (toread > maxlen && maxlen > 0) {
  770. toread = maxlen;
  771. }
  772. buf = emalloc(toread + 1);
  773. *returned_len = php_stream_read(stream, buf, toread);
  774. if (skip) {
  775. stream->readpos += delim_len;
  776. stream->position += delim_len;
  777. }
  778. buf[*returned_len] = '\0';
  779. return buf;
  780. }
  781. /* Writes a buffer directly to a stream, using multiple of the chunk size */
  782. static size_t _php_stream_write_buffer(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
  783. {
  784. size_t didwrite = 0, towrite, justwrote;
  785. /* if we have a seekable stream we need to ensure that data is written at the
  786. * current stream->position. This means invalidating the read buffer and then
  787. * performing a low-level seek */
  788. if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0 && stream->readpos != stream->writepos) {
  789. stream->readpos = stream->writepos = 0;
  790. stream->ops->seek(stream, stream->position, SEEK_SET, &stream->position TSRMLS_CC);
  791. }
  792. while (count > 0) {
  793. towrite = count;
  794. if (towrite > stream->chunk_size)
  795. towrite = stream->chunk_size;
  796. justwrote = stream->ops->write(stream, buf, towrite TSRMLS_CC);
  797. /* convert justwrote to an integer, since normally it is unsigned */
  798. if ((int)justwrote > 0) {
  799. buf += justwrote;
  800. count -= justwrote;
  801. didwrite += justwrote;
  802. /* Only screw with the buffer if we can seek, otherwise we lose data
  803. * buffered from fifos and sockets */
  804. if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) {
  805. stream->position += justwrote;
  806. }
  807. } else {
  808. break;
  809. }
  810. }
  811. return didwrite;
  812. }
  813. /* push some data through the write filter chain.
  814. * buf may be NULL, if flags are set to indicate a flush.
  815. * This may trigger a real write to the stream.
  816. * Returns the number of bytes consumed from buf by the first filter in the chain.
  817. * */
  818. static size_t _php_stream_write_filtered(php_stream *stream, const char *buf, size_t count, int flags TSRMLS_DC)
  819. {
  820. size_t consumed = 0;
  821. php_stream_bucket *bucket;
  822. php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL };
  823. php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out, *brig_swap;
  824. php_stream_filter_status_t status = PSFS_ERR_FATAL;
  825. php_stream_filter *filter;
  826. if (buf) {
  827. bucket = php_stream_bucket_new(stream, (char *)buf, count, 0, 0 TSRMLS_CC);
  828. php_stream_bucket_append(&brig_in, bucket TSRMLS_CC);
  829. }
  830. for (filter = stream->writefilters.head; filter; filter = filter->next) {
  831. /* for our return value, we are interested in the number of bytes consumed from
  832. * the first filter in the chain */
  833. status = filter->fops->filter(stream, filter, brig_inp, brig_outp,
  834. filter == stream->writefilters.head ? &consumed : NULL, flags TSRMLS_CC);
  835. if (status != PSFS_PASS_ON) {
  836. break;
  837. }
  838. /* brig_out becomes brig_in.
  839. * brig_in will always be empty here, as the filter MUST attach any un-consumed buckets
  840. * to its own brigade */
  841. brig_swap = brig_inp;
  842. brig_inp = brig_outp;
  843. brig_outp = brig_swap;
  844. memset(brig_outp, 0, sizeof(*brig_outp));
  845. }
  846. switch (status) {
  847. case PSFS_PASS_ON:
  848. /* filter chain generated some output; push it through to the
  849. * underlying stream */
  850. while (brig_inp->head) {
  851. bucket = brig_inp->head;
  852. _php_stream_write_buffer(stream, bucket->buf, bucket->buflen TSRMLS_CC);
  853. /* Potential error situation - eg: no space on device. Perhaps we should keep this brigade
  854. * hanging around and try to write it later.
  855. * At the moment, we just drop it on the floor
  856. * */
  857. php_stream_bucket_unlink(bucket TSRMLS_CC);
  858. php_stream_bucket_delref(bucket TSRMLS_CC);
  859. }
  860. break;
  861. case PSFS_FEED_ME:
  862. /* need more data before we can push data through to the stream */
  863. break;
  864. case PSFS_ERR_FATAL:
  865. /* some fatal error. Theoretically, the stream is borked, so all
  866. * further writes should fail. */
  867. break;
  868. }
  869. return consumed;
  870. }
  871. PHPAPI int _php_stream_flush(php_stream *stream, int closing TSRMLS_DC)
  872. {
  873. int ret = 0;
  874. if (stream->writefilters.head) {
  875. _php_stream_write_filtered(stream, NULL, 0, closing ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC TSRMLS_CC);
  876. }
  877. if (stream->ops->flush) {
  878. ret = stream->ops->flush(stream TSRMLS_CC);
  879. }
  880. return ret;
  881. }
  882. PHPAPI size_t _php_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
  883. {
  884. if (buf == NULL || count == 0 || stream->ops->write == NULL) {
  885. return 0;
  886. }
  887. if (stream->writefilters.head) {
  888. return _php_stream_write_filtered(stream, buf, count, PSFS_FLAG_NORMAL TSRMLS_CC);
  889. } else {
  890. return _php_stream_write_buffer(stream, buf, count TSRMLS_CC);
  891. }
  892. }
  893. PHPAPI size_t _php_stream_printf(php_stream *stream TSRMLS_DC, const char *fmt, ...)
  894. {
  895. size_t count;
  896. char *buf;
  897. va_list ap;
  898. va_start(ap, fmt);
  899. count = vspprintf(&buf, 0, fmt, ap);
  900. va_end(ap);
  901. if (!buf) {
  902. return 0; /* error condition */
  903. }
  904. count = php_stream_write(stream, buf, count);
  905. efree(buf);
  906. return count;
  907. }
  908. PHPAPI off_t _php_stream_tell(php_stream *stream TSRMLS_DC)
  909. {
  910. return stream->position;
  911. }
  912. PHPAPI int _php_stream_seek(php_stream *stream, off_t offset, int whence TSRMLS_DC)
  913. {
  914. if (stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE) {
  915. /* flush to commit data written to the fopencookie FILE* */
  916. fflush(stream->stdiocast);
  917. }
  918. /* handle the case where we are in the buffer */
  919. if ((stream->flags & PHP_STREAM_FLAG_NO_BUFFER) == 0) {
  920. switch(whence) {
  921. case SEEK_CUR:
  922. if (offset > 0 && offset <= stream->writepos - stream->readpos) {
  923. stream->readpos += offset; /* if offset = ..., then readpos = writepos */
  924. stream->position += offset;
  925. stream->eof = 0;
  926. return 0;
  927. }
  928. break;
  929. case SEEK_SET:
  930. if (offset > stream->position &&
  931. offset <= stream->position + stream->writepos - stream->readpos) {
  932. stream->readpos += offset - stream->position;
  933. stream->position = offset;
  934. stream->eof = 0;
  935. return 0;
  936. }
  937. break;
  938. }
  939. }
  940. if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) {
  941. int ret;
  942. if (stream->writefilters.head) {
  943. _php_stream_flush(stream, 0 TSRMLS_CC);
  944. }
  945. switch(whence) {
  946. case SEEK_CUR:
  947. offset = stream->position + offset;
  948. whence = SEEK_SET;
  949. break;
  950. }
  951. ret = stream->ops->seek(stream, offset, whence, &stream->position TSRMLS_CC);
  952. if (((stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) || ret == 0) {
  953. if (ret == 0) {
  954. stream->eof = 0;
  955. }
  956. /* invalidate the buffer contents */
  957. stream->readpos = stream->writepos = 0;
  958. return ret;
  959. }
  960. /* else the stream has decided that it can't support seeking after all;
  961. * fall through to attempt emulation */
  962. }
  963. /* emulate forward moving seeks with reads */
  964. if (whence == SEEK_CUR && offset > 0) {
  965. char tmp[1024];
  966. size_t didread;
  967. while(offset > 0) {
  968. if ((didread = php_stream_read(stream, tmp, MIN(offset, sizeof(tmp)))) == 0) {
  969. return -1;
  970. }
  971. offset -= didread;
  972. }
  973. stream->eof = 0;
  974. return 0;
  975. }
  976. php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream does not support seeking");
  977. return -1;
  978. }
  979. PHPAPI int _php_stream_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
  980. {
  981. int ret = PHP_STREAM_OPTION_RETURN_NOTIMPL;
  982. if (stream->ops->set_option) {
  983. ret = stream->ops->set_option(stream, option, value, ptrparam TSRMLS_CC);
  984. }
  985. if (ret == PHP_STREAM_OPTION_RETURN_NOTIMPL) {
  986. switch(option) {
  987. case PHP_STREAM_OPTION_SET_CHUNK_SIZE:
  988. ret = stream->chunk_size;
  989. stream->chunk_size = value;
  990. return ret;
  991. case PHP_STREAM_OPTION_READ_BUFFER:
  992. /* try to match the buffer mode as best we can */
  993. if (value == PHP_STREAM_BUFFER_NONE) {
  994. stream->flags |= PHP_STREAM_FLAG_NO_BUFFER;
  995. } else if (stream->flags & PHP_STREAM_FLAG_NO_BUFFER) {
  996. stream->flags ^= PHP_STREAM_FLAG_NO_BUFFER;
  997. }
  998. ret = PHP_STREAM_OPTION_RETURN_OK;
  999. break;
  1000. default:
  1001. ;
  1002. }
  1003. }
  1004. return ret;
  1005. }
  1006. PHPAPI int _php_stream_truncate_set_size(php_stream *stream, size_t newsize TSRMLS_DC)
  1007. {
  1008. return php_stream_set_option(stream, PHP_STREAM_OPTION_TRUNCATE_API, PHP_STREAM_TRUNCATE_SET_SIZE, &newsize);
  1009. }
  1010. PHPAPI size_t _php_stream_passthru(php_stream * stream STREAMS_DC TSRMLS_DC)
  1011. {
  1012. size_t bcount = 0;
  1013. char buf[8192];
  1014. int b;
  1015. if (php_stream_mmap_possible(stream)) {
  1016. char *p;
  1017. size_t mapped;
  1018. p = php_stream_mmap_range(stream, php_stream_tell(stream), PHP_STREAM_MMAP_ALL, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped);
  1019. if (p) {
  1020. PHPWRITE(p, mapped);
  1021. php_stream_mmap_unmap_ex(stream, mapped);
  1022. return mapped;
  1023. }
  1024. }
  1025. while ((b = php_stream_read(stream, buf, sizeof(buf))) > 0) {
  1026. PHPWRITE(buf, b);
  1027. bcount += b;
  1028. }
  1029. return bcount;
  1030. }
  1031. PHPAPI size_t _php_stream_copy_to_mem(php_stream *src, char **buf, size_t maxlen, int persistent STREAMS_DC TSRMLS_DC)
  1032. {
  1033. size_t ret = 0;
  1034. char *ptr;
  1035. size_t len = 0, max_len;
  1036. int step = CHUNK_SIZE;
  1037. int min_room = CHUNK_SIZE / 4;
  1038. php_stream_statbuf ssbuf;
  1039. if (maxlen == 0) {
  1040. return 0;
  1041. }
  1042. if (maxlen == PHP_STREAM_COPY_ALL) {
  1043. maxlen = 0;
  1044. }
  1045. if (maxlen > 0) {
  1046. ptr = *buf = pemalloc_rel_orig(maxlen + 1, persistent);
  1047. while ((len < maxlen) && !php_stream_eof(src)) {
  1048. ret = php_stream_read(src, ptr, maxlen - len);
  1049. len += ret;
  1050. ptr += ret;
  1051. }
  1052. *ptr = '\0';
  1053. return len;
  1054. }
  1055. /* avoid many reallocs by allocating a good sized chunk to begin with, if
  1056. * we can. Note that the stream may be filtered, in which case the stat
  1057. * result may be inaccurate, as the filter may inflate or deflate the
  1058. * number of bytes that we can read. In order to avoid an upsize followed
  1059. * by a downsize of the buffer, overestimate by the step size (which is
  1060. * 2K). */
  1061. if (php_stream_stat(src, &ssbuf) == 0 && ssbuf.sb.st_size > 0) {
  1062. max_len = ssbuf.sb.st_size + step;
  1063. } else {
  1064. max_len = step;
  1065. }
  1066. ptr = *buf = pemalloc_rel_orig(max_len, persistent);
  1067. while((ret = php_stream_read(src, ptr, max_len - len))) {
  1068. len += ret;
  1069. if (len + min_room >= max_len) {
  1070. *buf = perealloc_rel_orig(*buf, max_len + step, persistent);
  1071. max_len += step;
  1072. ptr = *buf + len;
  1073. } else {
  1074. ptr += ret;
  1075. }
  1076. }
  1077. if (len) {
  1078. *buf = perealloc_rel_orig(*buf, len + 1, persistent);
  1079. (*buf)[len] = '\0';
  1080. } else {
  1081. pefree(*buf, persistent);
  1082. *buf = NULL;
  1083. }
  1084. return len;
  1085. }
  1086. /* Returns SUCCESS/FAILURE and sets *len to the number of bytes moved */
  1087. PHPAPI size_t _php_stream_copy_to_stream_ex(php_stream *src, php_stream *dest, size_t maxlen, size_t *len STREAMS_DC TSRMLS_DC)
  1088. {
  1089. char buf[CHUNK_SIZE];
  1090. size_t readchunk;
  1091. size_t haveread = 0;
  1092. size_t didread;
  1093. size_t dummy;
  1094. php_stream_statbuf ssbuf;
  1095. if (!len) {
  1096. len = &dummy;
  1097. }
  1098. if (maxlen == 0) {
  1099. *len = 0;
  1100. return SUCCESS;
  1101. }
  1102. if (maxlen == PHP_STREAM_COPY_ALL) {
  1103. maxlen = 0;
  1104. }
  1105. if (php_stream_stat(src, &ssbuf) == 0) {
  1106. if (ssbuf.sb.st_size == 0
  1107. #ifdef S_ISREG
  1108. && S_ISREG(ssbuf.sb.st_mode)
  1109. #endif
  1110. ) {
  1111. *len = 0;
  1112. return SUCCESS;
  1113. }
  1114. }
  1115. if (php_stream_mmap_possible(src)) {
  1116. char *p;
  1117. size_t mapped;
  1118. p = php_stream_mmap_range(src, php_stream_tell(src), maxlen, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped);
  1119. if (p) {
  1120. mapped = php_stream_write(dest, p, mapped);
  1121. php_stream_mmap_unmap_ex(src, mapped);
  1122. *len = mapped;
  1123. /* we've got at least 1 byte to read.
  1124. * less than 1 is an error */
  1125. if (mapped > 0) {
  1126. return SUCCESS;
  1127. }
  1128. return FAILURE;
  1129. }
  1130. }
  1131. while(1) {
  1132. readchunk = sizeof(buf);
  1133. if (maxlen && (maxlen - haveread) < readchunk) {
  1134. readchunk = maxlen - haveread;
  1135. }
  1136. didread = php_stream_read(src, buf, readchunk);
  1137. if (didread) {
  1138. /* extra paranoid */
  1139. size_t didwrite, towrite;
  1140. char *writeptr;
  1141. towrite = didread;
  1142. writeptr = buf;
  1143. haveread += didread;
  1144. while(towrite) {
  1145. didwrite = php_stream_write(dest, writeptr, towrite);
  1146. if (didwrite == 0) {
  1147. *len = haveread - (didread - towrite);
  1148. return FAILURE;
  1149. }
  1150. towrite -= didwrite;
  1151. writeptr += didwrite;
  1152. }
  1153. } else {
  1154. break;
  1155. }
  1156. if (maxlen - haveread == 0) {
  1157. break;
  1158. }
  1159. }
  1160. *len = haveread;
  1161. /* we've got at least 1 byte to read.
  1162. * less than 1 is an error */
  1163. if (haveread > 0 || src->eof) {
  1164. return SUCCESS;
  1165. }
  1166. return FAILURE;
  1167. }
  1168. /* Returns the number of bytes moved.
  1169. * Returns 1 when source len is 0.
  1170. * Deprecated in favor of php_stream_copy_to_stream_ex() */
  1171. ZEND_ATTRIBUTE_DEPRECATED
  1172. PHPAPI size_t _php_stream_copy_to_stream(php_stream *src, php_stream *dest, size_t maxlen STREAMS_DC TSRMLS_DC)
  1173. {
  1174. size_t len;
  1175. int ret = _php_stream_copy_to_stream_ex(src, dest, maxlen, &len STREAMS_REL_CC TSRMLS_CC);
  1176. if (ret == SUCCESS && len == 0 && maxlen != 0) {
  1177. return 1;
  1178. }
  1179. return len;
  1180. }
  1181. /* }}} */
  1182. /* {{{ wrapper init and registration */
  1183. static void stream_resource_regular_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
  1184. {
  1185. php_stream *stream = (php_stream*)rsrc->ptr;
  1186. /* set the return value for pclose */
  1187. FG(pclose_ret) = php_stream_free(stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR);
  1188. }
  1189. static void stream_resource_persistent_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
  1190. {
  1191. php_stream *stream = (php_stream*)rsrc->ptr;
  1192. FG(pclose_ret) = php_stream_free(stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR);
  1193. }
  1194. void php_shutdown_stream_hashes(TSRMLS_D)
  1195. {
  1196. if (FG(stream_wrappers)) {
  1197. zend_hash_destroy(FG(stream_wrappers));
  1198. efree(FG(stream_wrappers));
  1199. FG(stream_wrappers) = NULL;
  1200. }
  1201. if (FG(stream_filters)) {
  1202. zend_hash_destroy(FG(stream_filters));
  1203. efree(FG(stream_filters));
  1204. FG(stream_filters) = NULL;
  1205. }
  1206. }
  1207. int php_init_stream_wrappers(int module_number TSRMLS_DC)
  1208. {
  1209. le_stream = zend_register_list_destructors_ex(stream_resource_regular_dtor, NULL, "stream", module_number);
  1210. le_pstream = zend_register_list_destructors_ex(NULL, stream_resource_persistent_dtor, "persistent stream", module_number);
  1211. /* Filters are cleaned up by the streams they're attached to */
  1212. le_stream_filter = zend_register_list_destructors_ex(NULL, NULL, "stream filter", module_number);
  1213. return (
  1214. zend_hash_init(&url_stream_wrappers_hash, 0, NULL, NULL, 1) == SUCCESS
  1215. &&
  1216. zend_hash_init(php_get_stream_filters_hash_global(), 0, NULL, NULL, 1) == SUCCESS
  1217. &&
  1218. zend_hash_init(php_stream_xport_get_hash(), 0, NULL, NULL, 1) == SUCCESS
  1219. &&
  1220. php_stream_xport_register("tcp", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS
  1221. &&
  1222. php_stream_xport_register("udp", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS
  1223. #if defined(AF_UNIX) && !(defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE))
  1224. &&
  1225. php_stream_xport_register("unix", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS
  1226. &&
  1227. php_stream_xport_register("udg", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS
  1228. #endif
  1229. ) ? SUCCESS : FAILURE;
  1230. }
  1231. int php_shutdown_stream_wrappers(int module_number TSRMLS_DC)
  1232. {
  1233. zend_hash_destroy(&url_stream_wrappers_hash);
  1234. zend_hash_destroy(php_get_stream_filters_hash_global());
  1235. zend_hash_destroy(php_stream_xport_get_hash());
  1236. return SUCCESS;
  1237. }
  1238. /* Validate protocol scheme names during registration
  1239. * Must conform to /^[a-zA-Z0-9+.-]+$/
  1240. */
  1241. static inline int php_stream_wrapper_scheme_validate(char *protocol, int protocol_len)
  1242. {
  1243. int i;
  1244. for(i = 0; i < protocol_len; i++) {
  1245. if (!isalnum((int)protocol[i]) &&
  1246. protocol[i] != '+' &&
  1247. protocol[i] != '-' &&
  1248. protocol[i] != '.') {
  1249. return FAILURE;
  1250. }
  1251. }
  1252. return SUCCESS;
  1253. }
  1254. /* API for registering GLOBAL wrappers */
  1255. PHPAPI int php_register_url_stream_wrapper(char *protocol, php_stream_wrapper *wrapper TSRMLS_DC)
  1256. {
  1257. int protocol_len = strlen(protocol);
  1258. if (php_stream_wrapper_scheme_validate(protocol, protocol_len) == FAILURE) {
  1259. return FAILURE;
  1260. }
  1261. return zend_hash_add(&url_stream_wrappers_hash, protocol, protocol_len + 1, &wrapper, sizeof(wrapper), NULL);
  1262. }
  1263. PHPAPI int php_unregister_url_stream_wrapper(char *protocol TSRMLS_DC)
  1264. {
  1265. return zend_hash_del(&url_stream_wrappers_hash, protocol, strlen(protocol) + 1);
  1266. }
  1267. static void clone_wrapper_hash(TSRMLS_D)
  1268. {
  1269. php_stream_wrapper *tmp;
  1270. ALLOC_HASHTABLE(FG(stream_wrappers));
  1271. zend_hash_init(FG(stream_wrappers), zend_hash_num_elements(&url_stream_wrappers_hash), NULL, NULL, 1);
  1272. zend_hash_copy(FG(stream_wrappers), &url_stream_wrappers_hash, NULL, &tmp, sizeof(tmp));
  1273. }
  1274. /* API for registering VOLATILE wrappers */
  1275. PHPAPI int php_register_url_stream_wrapper_volatile(char *protocol, php_stream_wrapper *wrapper TSRMLS_DC)
  1276. {
  1277. int protocol_len = strlen(protocol);
  1278. if (php_stream_wrapper_scheme_validate(protocol, protocol_len) == FAILURE) {
  1279. return FAILURE;
  1280. }
  1281. if (!FG(stream_wrappers)) {
  1282. clone_wrapper_hash(TSRMLS_C);
  1283. }
  1284. return zend_hash_add(FG(stream_wrappers), protocol, protocol_len + 1, &wrapper, sizeof(wrapper), NULL);
  1285. }
  1286. PHPAPI int php_unregister_url_stream_wrapper_volatile(char *protocol TSRMLS_DC)
  1287. {
  1288. if (!FG(stream_wrappers)) {
  1289. clone_wrapper_hash(TSRMLS_C);
  1290. }
  1291. return zend_hash_del(FG(stream_wrappers), protocol, strlen(protocol) + 1);
  1292. }
  1293. /* }}} */
  1294. /* {{{ php_stream_locate_url_wrapper */
  1295. PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, char **path_for_open, int options TSRMLS_DC)
  1296. {
  1297. HashTable *wrapper_hash = (FG(stream_wrappers) ? FG(stream_wrappers) : &url_stream_wrappers_hash);
  1298. php_stream_wrapper **wrapperpp = NULL;
  1299. const char *p, *protocol = NULL;
  1300. int n = 0;
  1301. if (path_for_open) {
  1302. *path_for_open = (char*)path;
  1303. }
  1304. if (options & IGNORE_URL) {
  1305. return (options & STREAM_LOCATE_WRAPPERS_ONLY) ? NULL : &php_plain_files_wrapper;
  1306. }
  1307. for (p = path; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++) {
  1308. n++;
  1309. }
  1310. if ((*p == ':') && (n > 1) && (!strncmp("//", p+1, 2) || (n == 4 && !memcmp("data:", path, 5)))) {
  1311. protocol = path;
  1312. } else if (n == 5 && strncasecmp(path, "zlib:", 5) == 0) {
  1313. /* BC with older php scripts and zlib wrapper */
  1314. protocol = "compress.zlib";
  1315. n = 13;
  1316. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Use of \"zlib:\" wrapper is deprecated; please use \"compress.zlib://\" instead");
  1317. }
  1318. if (protocol) {
  1319. char *tmp = estrndup(protocol, n);
  1320. if (FAILURE == zend_hash_find(wrapper_hash, (char*)tmp, n + 1, (void**)&wrapperpp)) {
  1321. php_strtolower(tmp, n);
  1322. if (FAILURE == zend_hash_find(wrapper_hash, (char*)tmp, n + 1, (void**)&wrapperpp)) {
  1323. char wrapper_name[32];
  1324. if (n >= sizeof(wrapper_name)) {
  1325. n = sizeof(wrapper_name) - 1;
  1326. }
  1327. PHP_STRLCPY(wrapper_name, protocol, sizeof(wrapper_name), n);
  1328. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to find the wrapper \"%s\" - did you forget to enable it when you configured PHP?", wrapper_name);
  1329. wrapperpp = NULL;
  1330. protocol = NULL;
  1331. }
  1332. }
  1333. efree(tmp);
  1334. }
  1335. /* TODO: curl based streams probably support file:// properly */
  1336. if (!protocol || !strncasecmp(protocol, "file", n)) {
  1337. /* fall back on regular file access */
  1338. php_stream_wrapper *plain_files_wrapper = &php_plain_files_wrapper;
  1339. if (protocol) {
  1340. int localhost = 0;
  1341. if (!strncasecmp(path, "file://localhost/", 17)) {
  1342. localhost = 1;
  1343. }
  1344. #ifdef PHP_WIN32
  1345. if (localhost == 0 && path[n+3] != '\0' && path[n+3] != '/' && path[n+4] != ':') {
  1346. #else
  1347. if (localhost == 0 && path[n+3] != '\0' && path[n+3] != '/') {
  1348. #endif
  1349. if (options & REPORT_ERRORS) {
  1350. php_error_docref(NULL TSRMLS_CC, E_WARNING, "remote host file access not supported, %s", path);
  1351. }
  1352. return NULL;
  1353. }
  1354. if (path_for_open) {
  1355. /* skip past protocol and :/, but handle windows correctly */
  1356. *path_for_open = (char*)path + n + 1;
  1357. if (localhost == 1) {
  1358. (*path_for_open) += 11;
  1359. }
  1360. while (*(++*path_for_open)=='/');
  1361. #ifdef PHP_WIN32
  1362. if (*(*path_for_open + 1) != ':')
  1363. #endif
  1364. (*path_for_open)--;
  1365. }
  1366. }
  1367. if (options & STREAM_LOCATE_WRAPPERS_ONLY) {
  1368. return NULL;
  1369. }
  1370. if (FG(stream_wrappers)) {
  1371. /* The file:// wrapper may have been disabled/overridden */
  1372. if (wrapperpp) {
  1373. /* It was found so go ahead and provide it */
  1374. return *wrapperpp;
  1375. }
  1376. /* Check again, the original check might have not known the protocol name */
  1377. if (zend_hash_find(wrapper_hash, "file", sizeof("file"), (void**)&wrapperpp) == SUCCESS) {
  1378. return *wrapperpp;
  1379. }
  1380. if (options & REPORT_ERRORS) {
  1381. php_error_docref(NULL TSRMLS_CC, E_WARNING, "file:// wrapper is disabled in the server configuration");
  1382. }
  1383. return NULL;
  1384. }
  1385. return plain_files_wrapper;
  1386. }
  1387. if (wrapperpp && (*wrapperpp)->is_url &&
  1388. (options & STREAM_DISABLE_URL_PROTECTION) == 0 &&
  1389. (!PG(allow_url_fopen) ||
  1390. (((options & STREAM_OPEN_FOR_INCLUDE) ||
  1391. PG(in_user_include)) && !PG(allow_url_include)))) {
  1392. if (options & REPORT_ERRORS) {
  1393. /* protocol[n] probably isn't '\0' */
  1394. char *protocol_dup = estrndup(protocol, n);
  1395. if (!PG(allow_url_fopen)) {
  1396. php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s:// wrapper is disabled in the server configuration by allow_url_fopen=0", protocol_dup);
  1397. } else {
  1398. php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s:// wrapper is disabled in the server configuration by allow_url_include=0", protocol_dup);
  1399. }
  1400. efree(protocol_dup);
  1401. }
  1402. return NULL;
  1403. }
  1404. return *wrapperpp;
  1405. }
  1406. /* }}} */
  1407. /* {{{ _php_stream_mkdir
  1408. */
  1409. PHPAPI int _php_stream_mkdir(char *path, int mode, int options, php_stream_context *context TSRMLS_DC)
  1410. {
  1411. php_stream_wrapper *wrapper = NULL;
  1412. wrapper = php_stream_locate_url_wrapper(path, NULL, ENFORCE_SAFE_MODE TSRMLS_CC);
  1413. if (!wrapper || !wrapper->wops || !wrapper->wops->stream_mkdir) {
  1414. return 0;
  1415. }
  1416. return wrapper->wops->stream_mkdir(wrapper, path, mode, options, context TSRMLS_CC);
  1417. }
  1418. /* }}} */
  1419. /* {{{ _php_stream_rmdir
  1420. */
  1421. PHPAPI int _php_stream_rmdir(char *path, int options, php_stream_context *context TSRMLS_DC)
  1422. {
  1423. php_stream_wrapper *wrapper = NULL;
  1424. wrapper = php_stream_locate_url_wrapper(path, NULL, ENFORCE_SAFE_MODE TSRMLS_CC);
  1425. if (!wrapper || !wrapper->wops || !wrapper->wops->stream_rmdir) {
  1426. return 0;
  1427. }
  1428. return wrapper->wops->stream_rmdir(wrapper, path, options, context TSRMLS_CC);
  1429. }
  1430. /* }}} */
  1431. /* {{{ _php_stream_stat_path */
  1432. PHPAPI int _php_stream_stat_path(char *path, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC)
  1433. {
  1434. php_stream_wrapper *wrapper = NULL;
  1435. char *path_to_open = path;
  1436. int ret;
  1437. /* Try to hit the cache first */
  1438. if (flags & PHP_STREAM_URL_STAT_LINK) {
  1439. if (BG(CurrentLStatFile) && strcmp(path, BG(CurrentLStatFile)) == 0) {
  1440. memcpy(ssb, &BG(lssb), sizeof(php_stream_statbuf));
  1441. return 0;
  1442. }
  1443. } else {
  1444. if (BG(CurrentStatFile) && strcmp(path, BG(CurrentStatFile)) == 0) {
  1445. memcpy(ssb, &BG(ssb), sizeof(php_stream_statbuf));
  1446. return 0;
  1447. }
  1448. }
  1449. wrapper = php_stream_locate_url_wrapper(path, &path_to_open, ENFORCE_SAFE_MODE TSRMLS_CC);
  1450. if (wrapper && wrapper->wops->url_stat) {
  1451. ret = wrapper->wops->url_stat(wrapper, path_to_open, flags, ssb, context TSRMLS_CC);
  1452. if (ret == 0) {
  1453. /* Drop into cache */
  1454. if (flags & PHP_STREAM_URL_STAT_LINK) {
  1455. if (BG(CurrentLStatFile)) {
  1456. efree(BG(CurrentLStatFile));
  1457. }
  1458. BG(CurrentLStatFile) = estrdup(path);
  1459. memcpy(&BG(lssb), ssb, sizeof(php_stream_statbuf));
  1460. } else {
  1461. if (BG(CurrentStatFile)) {
  1462. efree(BG(CurrentStatFile));
  1463. }
  1464. BG(CurrentStatFile) = estrdup(path);
  1465. memcpy(&BG(ssb), ssb, sizeof(php_stream_statbuf));
  1466. }
  1467. }
  1468. return ret;
  1469. }
  1470. return -1;
  1471. }
  1472. /* }}} */
  1473. /* {{{ php_stream_opendir */
  1474. PHPAPI php_stream *_php_stream_opendir(char *path, int options,
  1475. php_stream_context *context STREAMS_DC TSRMLS_DC)
  1476. {
  1477. php_stream *stream = NULL;
  1478. php_stream_wrapper *wrapper = NULL;
  1479. char *path_to_open;
  1480. if (!path || !*path) {
  1481. return NULL;
  1482. }
  1483. path_to_open = path;
  1484. wrapper = php_stream_locate_url_wrapper(path, &path_to_open, options TSRMLS_CC);
  1485. if (wrapper && wrapper->wops->dir_opener) {
  1486. stream = wrapper->wops->dir_opener(wrapper,
  1487. path_to_open, "r", options ^ REPORT_ERRORS, NULL,
  1488. context STREAMS_REL_CC TSRMLS_CC);
  1489. if (stream) {
  1490. stream->wrapper = wrapper;
  1491. stream->flags |= PHP_STREAM_FLAG_NO_BUFFER | PHP_STREAM_FLAG_IS_DIR;
  1492. }
  1493. } else if (wrapper) {
  1494. php_stream_wrapper_log_error(wrapper, options ^ REPORT_ERRORS TSRMLS_CC, "not implemented");
  1495. }
  1496. if (stream == NULL && (options & REPORT_ERRORS)) {
  1497. php_stream_display_wrapper_errors(wrapper, path, "failed to open dir" TSRMLS_CC);
  1498. }
  1499. php_stream_tidy_wrapper_error_log(wrapper TSRMLS_CC);
  1500. return stream;
  1501. }
  1502. /* }}} */
  1503. /* {{{ _php_stream_readdir */
  1504. PHPAPI php_st

Large files files are truncated, but you can click here to view the full file