You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2381 lines
60 KiB

24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
24 years ago
25 years ago
24 years ago
25 years ago
24 years ago
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 4 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2002 The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 2.02 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available at through the world-wide-web at |
  10. | http://www.php.net/license/2_02.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: |
  16. | Wez Furlong (wez@thebrainroom.com) |
  17. | Borrowed code from: |
  18. | Rasmus Lerdorf <rasmus@lerdorf.on.ca> |
  19. | Jim Winstead <jimw@php.net> |
  20. +----------------------------------------------------------------------+
  21. */
  22. /* $Id$ */
  23. #define _GNU_SOURCE
  24. #include "php.h"
  25. #include "php_globals.h"
  26. #include "php_network.h"
  27. #include "php_open_temporary_file.h"
  28. #include "ext/standard/file.h"
  29. #include "ext/standard/basic_functions.h" /* for BG(mmap_file) (not strictly required) */
  30. #ifdef HAVE_SYS_MMAN_H
  31. #include <sys/mman.h>
  32. #endif
  33. #include <stddef.h>
  34. #include <fcntl.h>
  35. #ifndef MAP_FAILED
  36. #define MAP_FAILED ((void *) -1)
  37. #endif
  38. #define CHUNK_SIZE 8192
  39. #ifdef PHP_WIN32
  40. #define EWOULDBLOCK WSAEWOULDBLOCK
  41. #endif
  42. #define STREAM_DEBUG 0
  43. #define STREAM_WRAPPER_PLAIN_FILES ((php_stream_wrapper*)-1)
  44. /* {{{ some macros to help track leaks */
  45. #if ZEND_DEBUG
  46. #define emalloc_rel_orig(size) \
  47. ( __php_stream_call_depth == 0 \
  48. ? _emalloc((size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_RELAY_CC) \
  49. : _emalloc((size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC) )
  50. #define erealloc_rel_orig(ptr, size) \
  51. ( __php_stream_call_depth == 0 \
  52. ? _erealloc((ptr), (size), 0 ZEND_FILE_LINE_CC ZEND_FILE_LINE_RELAY_CC) \
  53. : _erealloc((ptr), (size), 0 ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC) )
  54. #define pemalloc_rel_orig(size, persistent) ((persistent) ? malloc((size)) : emalloc_rel_orig((size)))
  55. #define perealloc_rel_orig(ptr, size, persistent) ((persistent) ? realloc((ptr), (size)) : erealloc_rel_orig((ptr), (size)))
  56. #else
  57. # define pemalloc_rel_orig(size, persistent) pemalloc((size), (persistent))
  58. # define perealloc_rel_orig(ptr, size, persistent) perealloc((ptr), (size), (persistent))
  59. # define emalloc_rel_orig(size) emalloc((size))
  60. #endif
  61. /* }}} */
  62. static HashTable url_stream_wrappers_hash;
  63. static int le_stream = FAILURE; /* true global */
  64. static int le_pstream = FAILURE; /* true global */
  65. PHPAPI int php_file_le_stream(void)
  66. {
  67. return le_stream;
  68. }
  69. PHPAPI int php_file_le_pstream(void)
  70. {
  71. return le_pstream;
  72. }
  73. static int forget_persistent_resource_id_numbers(zend_rsrc_list_entry *rsrc TSRMLS_DC)
  74. {
  75. php_stream *stream;
  76. if (Z_TYPE_P(rsrc) != le_pstream)
  77. return 0;
  78. stream = (php_stream*)rsrc->ptr;
  79. #if STREAM_DEBUG
  80. fprintf(stderr, "forget_persistent: %s:%p\n", stream->ops->label, stream);
  81. #endif
  82. stream->rsrc_id = FAILURE;
  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. 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. if ((*stream)->rsrc_id == FAILURE) {
  98. /* first access this request; give it a valid id */
  99. (*stream)->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, *stream, le_pstream);
  100. }
  101. }
  102. return PHP_STREAM_PERSISTENT_SUCCESS;
  103. }
  104. return PHP_STREAM_PERSISTENT_FAILURE;
  105. }
  106. return PHP_STREAM_PERSISTENT_NOT_EXIST;
  107. }
  108. static void 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. msg = emalloc(l + 1);
  132. msg[0] = '\0';
  133. for (i = 0; i < wrapper->err_count; i++) {
  134. strcat(msg, wrapper->err_stack[i]);
  135. if (i < wrapper->err_count - 1)
  136. strcat(msg, br);
  137. }
  138. free_msg = 1;
  139. } else {
  140. msg = strerror(errno);
  141. }
  142. } else {
  143. msg = "no suitable wrapper could be found";
  144. }
  145. php_strip_url_passwd(tmp);
  146. php_error_docref1(NULL TSRMLS_CC, tmp, E_WARNING, "%s: %s", caption, msg);
  147. efree(tmp);
  148. if (free_msg)
  149. efree(msg);
  150. }
  151. static void tidy_wrapper_error_log(php_stream_wrapper *wrapper TSRMLS_DC)
  152. {
  153. if (wrapper) {
  154. /* tidy up the error stack */
  155. int i;
  156. for (i = 0; i < wrapper->err_count; i++)
  157. efree(wrapper->err_stack[i]);
  158. if (wrapper->err_stack)
  159. efree(wrapper->err_stack);
  160. wrapper->err_stack = NULL;
  161. wrapper->err_count = 0;
  162. }
  163. }
  164. /* allocate a new stream for a particular ops */
  165. PHPAPI php_stream *_php_stream_alloc(php_stream_ops *ops, void *abstract, const char *persistent_id, const char *mode STREAMS_DC TSRMLS_DC) /* {{{ */
  166. {
  167. php_stream *ret;
  168. ret = (php_stream*) pemalloc_rel_orig(sizeof(php_stream), persistent_id ? 1 : 0);
  169. memset(ret, 0, sizeof(php_stream));
  170. #if STREAM_DEBUG
  171. fprintf(stderr, "stream_alloc: %s:%p persistent=%s\n", ops->label, ret, persistent_id);
  172. #endif
  173. ret->ops = ops;
  174. ret->abstract = abstract;
  175. ret->is_persistent = persistent_id ? 1 : 0;
  176. ret->chunk_size = FG(def_chunk_size);
  177. if (FG(auto_detect_line_endings))
  178. ret->flags |= PHP_STREAM_FLAG_DETECT_EOL;
  179. if (persistent_id) {
  180. list_entry le;
  181. Z_TYPE(le) = le_pstream;
  182. le.ptr = ret;
  183. if (FAILURE == zend_hash_update(&EG(persistent_list), (char *)persistent_id,
  184. strlen(persistent_id) + 1,
  185. (void *)&le, sizeof(list_entry), NULL)) {
  186. pefree(ret, 1);
  187. return NULL;
  188. }
  189. }
  190. ret->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, ret, persistent_id ? le_pstream : le_stream);
  191. strlcpy(ret->mode, mode, sizeof(ret->mode));
  192. return ret;
  193. }
  194. /* }}} */
  195. PHPAPI int _php_stream_free(php_stream *stream, int close_options TSRMLS_DC) /* {{{ */
  196. {
  197. int ret = 1;
  198. #if STREAM_DEBUG
  199. 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);
  200. #endif
  201. if (stream->in_free)
  202. return 1;
  203. stream->in_free++;
  204. _php_stream_flush(stream, 1 TSRMLS_CC);
  205. if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) == 0) {
  206. /* Remove entry from the resource list */
  207. zend_list_delete(stream->rsrc_id);
  208. }
  209. if (close_options & PHP_STREAM_FREE_CALL_DTOR) {
  210. if (stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE) {
  211. /* calling fclose on an fopencookied stream will ultimately
  212. call this very same function. If we were called via fclose,
  213. the cookie_closer unsets the fclose_stdiocast flags, so
  214. we can be sure that we only reach here when PHP code calls
  215. php_stream_free.
  216. Lets let the cookie code clean it all up.
  217. */
  218. stream->in_free = 0;
  219. return fclose(stream->stdiocast);
  220. }
  221. ret = stream->ops->close(stream, close_options & PHP_STREAM_FREE_PRESERVE_HANDLE ? 0 : 1 TSRMLS_CC);
  222. stream->abstract = NULL;
  223. /* tidy up any FILE* that might have been fdopened */
  224. if (stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FDOPEN && stream->stdiocast) {
  225. fclose(stream->stdiocast);
  226. stream->stdiocast = NULL;
  227. }
  228. }
  229. if (close_options & PHP_STREAM_FREE_RELEASE_STREAM) {
  230. while (stream->filterhead) {
  231. php_stream_filter_remove_head(stream, 1);
  232. }
  233. if (stream->wrapper && stream->wrapper->wops && stream->wrapper->wops->stream_closer) {
  234. stream->wrapper->wops->stream_closer(stream->wrapper, stream TSRMLS_CC);
  235. stream->wrapper = NULL;
  236. }
  237. if (stream->wrapperdata) {
  238. zval_ptr_dtor(&stream->wrapperdata);
  239. stream->wrapperdata = NULL;
  240. }
  241. if (stream->readbuf) {
  242. pefree(stream->readbuf, stream->is_persistent);
  243. stream->readbuf = NULL;
  244. }
  245. #if ZEND_DEBUG
  246. if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) && (stream->__exposed == 0) && (EG(error_reporting) & E_WARNING)) {
  247. /* it leaked: Lets deliberately NOT pefree it so that the memory manager shows it
  248. * as leaked; it will log a warning, but lets help it out and display what kind
  249. * of stream it was. */
  250. char leakbuf[512];
  251. snprintf(leakbuf, sizeof(leakbuf), __FILE__ "(%d) : Stream of type '%s' 0x%08X (path:%s) was not closed\n", __LINE__, stream->ops->label, (unsigned int)stream, stream->__orig_path);
  252. STR_FREE(stream->__orig_path);
  253. # if defined(PHP_WIN32)
  254. OutputDebugString(leakbuf);
  255. # else
  256. fprintf(stderr, leakbuf);
  257. # endif
  258. } else {
  259. STR_FREE(stream->__orig_path);
  260. pefree(stream, stream->is_persistent);
  261. }
  262. #else
  263. pefree(stream, stream->is_persistent);
  264. #endif
  265. }
  266. return ret;
  267. }
  268. /* }}} */
  269. /* {{{ filter API */
  270. static HashTable stream_filters_hash;
  271. PHPAPI int php_stream_filter_register_factory(const char *filterpattern, php_stream_filter_factory *factory TSRMLS_DC)
  272. {
  273. return zend_hash_add(&stream_filters_hash, (char*)filterpattern, strlen(filterpattern), factory, sizeof(*factory), NULL);
  274. }
  275. PHPAPI int php_stream_filter_unregister_factory(const char *filterpattern TSRMLS_DC)
  276. {
  277. return zend_hash_del(&stream_filters_hash, (char*)filterpattern, strlen(filterpattern));
  278. }
  279. /* We allow very simple pattern matching for filter factories:
  280. * if "charset.utf-8/sjis" is requested, we search first for an exact
  281. * match. If that fails, we try "charset.*".
  282. * This means that we don't need to clog up the hashtable with a zillion
  283. * charsets (for example) but still be able to provide them all as filters */
  284. PHPAPI php_stream_filter *php_stream_filter_create(const char *filtername, const char *filterparams, int filterparamslen, int persistent TSRMLS_DC)
  285. {
  286. php_stream_filter_factory *factory;
  287. php_stream_filter *filter = NULL;
  288. int n;
  289. char *period;
  290. n = strlen(filtername);
  291. if (SUCCESS == zend_hash_find(&stream_filters_hash, (char*)filtername, n, (void**)&factory)) {
  292. filter = factory->create_filter(filtername, filterparams, filterparamslen, persistent TSRMLS_CC);
  293. } else if ((period = strchr(filtername, '.'))) {
  294. /* try a wildcard */
  295. char wildname[128];
  296. PHP_STRLCPY(wildname, filtername, sizeof(wildname) - 1, period-filtername + 1);
  297. strcat(wildname, "*");
  298. if (SUCCESS == zend_hash_find(&stream_filters_hash, wildname, strlen(wildname), (void**)&factory)) {
  299. filter = factory->create_filter(filtername, filterparams, filterparamslen, persistent TSRMLS_CC);
  300. }
  301. }
  302. if (filter == NULL) {
  303. /* TODO: these need correct docrefs */
  304. if (factory == NULL)
  305. php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to locate filter \"%s\"", filtername);
  306. else
  307. php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to create or locate filter \"%s\"", filtername);
  308. }
  309. return filter;
  310. }
  311. PHPAPI php_stream_filter *_php_stream_filter_alloc(php_stream_filter_ops *fops, void *abstract, int persistent STREAMS_DC TSRMLS_DC)
  312. {
  313. php_stream_filter *filter;
  314. filter = (php_stream_filter*) pemalloc_rel_orig(sizeof(php_stream_filter), persistent);
  315. memset(filter, 0, sizeof(php_stream_filter));
  316. filter->fops = fops;
  317. filter->abstract = abstract;
  318. filter->is_persistent = persistent;
  319. return filter;
  320. }
  321. PHPAPI void php_stream_filter_free(php_stream_filter *filter TSRMLS_DC)
  322. {
  323. if (filter->fops->dtor)
  324. filter->fops->dtor(filter TSRMLS_CC);
  325. pefree(filter, filter->is_persistent);
  326. }
  327. PHPAPI void php_stream_filter_prepend(php_stream *stream, php_stream_filter *filter)
  328. {
  329. filter->next = stream->filterhead;
  330. filter->prev = NULL;
  331. if (stream->filterhead) {
  332. stream->filterhead->prev = filter;
  333. } else {
  334. stream->filtertail = filter;
  335. }
  336. stream->filterhead = filter;
  337. filter->stream = stream;
  338. }
  339. PHPAPI void php_stream_filter_append(php_stream *stream, php_stream_filter *filter)
  340. {
  341. filter->prev = stream->filtertail;
  342. filter->next = NULL;
  343. if (stream->filtertail) {
  344. stream->filtertail->next = filter;
  345. } else {
  346. stream->filterhead = filter;
  347. }
  348. stream->filtertail = filter;
  349. filter->stream = stream;
  350. }
  351. PHPAPI php_stream_filter *php_stream_filter_remove(php_stream *stream, php_stream_filter *filter, int call_dtor TSRMLS_DC)
  352. {
  353. assert(stream == filter->stream);
  354. if (filter->prev) {
  355. filter->prev->next = filter->next;
  356. } else {
  357. stream->filterhead = filter->next;
  358. }
  359. if (filter->next) {
  360. filter->next->prev = filter->prev;
  361. } else {
  362. stream->filtertail = filter->prev;
  363. }
  364. if (call_dtor) {
  365. php_stream_filter_free(filter TSRMLS_CC);
  366. return NULL;
  367. }
  368. return filter;
  369. }
  370. /* }}} */
  371. /* {{{ generic stream operations */
  372. static void php_stream_fill_read_buffer(php_stream *stream, size_t size TSRMLS_DC)
  373. {
  374. /* allocate/fill the buffer */
  375. /* is there enough data in the buffer ? */
  376. if (stream->writepos - stream->readpos < (off_t)size) {
  377. size_t justread = 0;
  378. if (stream->eof)
  379. return;
  380. /* no; so lets fetch more data */
  381. /* reduce buffer memory consumption if possible, to avoid a realloc */
  382. if (stream->readbuf && stream->readbuflen - stream->writepos < stream->chunk_size) {
  383. memmove(stream->readbuf, stream->readbuf + stream->readpos, stream->readbuflen - stream->readpos);
  384. stream->writepos -= stream->readpos;
  385. stream->readpos = 0;
  386. }
  387. /* grow the buffer if required */
  388. if (stream->readbuflen - stream->writepos < stream->chunk_size) {
  389. stream->readbuflen += stream->chunk_size;
  390. stream->readbuf = perealloc(stream->readbuf, stream->readbuflen,
  391. stream->is_persistent);
  392. }
  393. if (stream->filterhead) {
  394. justread = stream->filterhead->fops->read(stream, stream->filterhead,
  395. stream->readbuf + stream->writepos,
  396. stream->readbuflen - stream->writepos
  397. TSRMLS_CC);
  398. } else {
  399. justread = stream->ops->read(stream, stream->readbuf + stream->writepos,
  400. stream->readbuflen - stream->writepos
  401. TSRMLS_CC);
  402. }
  403. if (justread != (size_t)-1) {
  404. stream->writepos += justread;
  405. }
  406. }
  407. }
  408. PHPAPI size_t _php_stream_read(php_stream *stream, char *buf, size_t size TSRMLS_DC)
  409. {
  410. size_t toread = 0, didread = 0;
  411. while (size > 0) {
  412. /* take from the read buffer first.
  413. * It is possible that a buffered stream was switched to non-buffered, so we
  414. * drain the remainder of the buffer before using the "raw" read mode for
  415. * the excess */
  416. if (stream->writepos > stream->readpos) {
  417. toread = stream->writepos - stream->readpos;
  418. if (toread > size)
  419. toread = size;
  420. memcpy(buf, stream->readbuf + stream->readpos, toread);
  421. stream->readpos += toread;
  422. size -= toread;
  423. buf += toread;
  424. didread += toread;
  425. }
  426. if (size == 0 || stream->eof) {
  427. break;
  428. }
  429. if (stream->flags & PHP_STREAM_FLAG_NO_BUFFER || stream->chunk_size == 1) {
  430. if (stream->filterhead) {
  431. toread = stream->filterhead->fops->read(stream, stream->filterhead,
  432. buf, size
  433. TSRMLS_CC);
  434. } else {
  435. toread = stream->ops->read(stream, buf, size TSRMLS_CC);
  436. }
  437. } else {
  438. php_stream_fill_read_buffer(stream, size TSRMLS_CC);
  439. toread = stream->writepos - stream->readpos;
  440. if (toread > size)
  441. toread = size;
  442. if (toread > 0) {
  443. memcpy(buf, stream->readbuf + stream->readpos, toread);
  444. stream->readpos += toread;
  445. }
  446. }
  447. if (toread > 0) {
  448. didread += toread;
  449. buf += toread;
  450. size -= toread;
  451. } else {
  452. /* EOF, or temporary end of data (for non-blocking mode). */
  453. break;
  454. }
  455. }
  456. if (didread > 0)
  457. stream->position += didread;
  458. return didread;
  459. }
  460. PHPAPI int _php_stream_eof(php_stream *stream TSRMLS_DC)
  461. {
  462. /* if there is data in the buffer, it's not EOF */
  463. if (stream->writepos - stream->readpos > 0)
  464. return 0;
  465. return stream->eof;
  466. }
  467. PHPAPI int _php_stream_putc(php_stream *stream, int c TSRMLS_DC)
  468. {
  469. unsigned char buf = c;
  470. if (php_stream_write(stream, &buf, 1) > 0) {
  471. return 1;
  472. }
  473. return EOF;
  474. }
  475. PHPAPI int _php_stream_getc(php_stream *stream TSRMLS_DC)
  476. {
  477. char buf;
  478. if (php_stream_read(stream, &buf, 1) > 0) {
  479. return buf & 0xff;
  480. }
  481. return EOF;
  482. }
  483. PHPAPI int _php_stream_puts(php_stream *stream, char *buf TSRMLS_DC)
  484. {
  485. int len;
  486. char newline[2] = "\n"; /* is this OK for Win? */
  487. len = strlen(buf);
  488. if (len > 0 && php_stream_write(stream, buf, len) && php_stream_write(stream, newline, 1)) {
  489. return 1;
  490. }
  491. return 0;
  492. }
  493. PHPAPI int _php_stream_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
  494. {
  495. memset(ssb, 0, sizeof(*ssb));
  496. /* if the stream was wrapped, allow the wrapper to stat it */
  497. if (stream->wrapper && stream->wrapper->wops->stream_stat != NULL) {
  498. return stream->wrapper->wops->stream_stat(stream->wrapper, stream, ssb TSRMLS_CC);
  499. }
  500. /* if the stream doesn't directly support stat-ing, return with failure.
  501. * We could try and emulate this by casting to a FD and fstat-ing it,
  502. * but since the fd might not represent the actual underlying content
  503. * this would give bogus results. */
  504. if (stream->ops->stat == NULL) {
  505. return -1;
  506. }
  507. return stream->ops->stat(stream, ssb TSRMLS_CC);
  508. }
  509. PHPAPI char *php_stream_locate_eol(php_stream *stream, char *buf, size_t buf_len TSRMLS_DC)
  510. {
  511. size_t avail;
  512. char *cr, *lf, *eol = NULL;
  513. char *readptr;
  514. if (!buf) {
  515. readptr = stream->readbuf + stream->readpos;
  516. avail = stream->writepos - stream->readpos;
  517. } else {
  518. readptr = buf;
  519. avail = buf_len;
  520. }
  521. /* Look for EOL */
  522. if (stream->flags & PHP_STREAM_FLAG_DETECT_EOL) {
  523. cr = memchr(readptr, '\r', avail);
  524. lf = memchr(readptr, '\n', avail);
  525. if (cr && lf != cr + 1 && !(lf && lf < cr)) {
  526. /* mac */
  527. stream->flags ^= PHP_STREAM_FLAG_DETECT_EOL;
  528. stream->flags |= PHP_STREAM_FLAG_EOL_MAC;
  529. eol = cr;
  530. } else if ((cr && lf && cr == lf - 1) || (lf)) {
  531. /* dos or unix endings */
  532. stream->flags ^= PHP_STREAM_FLAG_DETECT_EOL;
  533. eol = lf;
  534. }
  535. } else if (stream->flags & PHP_STREAM_FLAG_EOL_MAC) {
  536. eol = memchr(readptr, '\r', avail);
  537. } else {
  538. /* unix (and dos) line endings */
  539. eol = memchr(readptr, '\n', avail);
  540. }
  541. return eol;
  542. }
  543. /* If buf == NULL, the buffer will be allocated automatically and will be of an
  544. * appropriate length to hold the line, regardless of the line length, memory
  545. * permitting */
  546. PHPAPI char *_php_stream_get_line(php_stream *stream, char *buf, size_t maxlen,
  547. size_t *returned_len TSRMLS_DC)
  548. {
  549. size_t avail = 0;
  550. size_t current_buf_size = 0;
  551. size_t total_copied = 0;
  552. int grow_mode = 0;
  553. char *bufstart = buf;
  554. if (buf == NULL)
  555. grow_mode = 1;
  556. else if (maxlen == 0)
  557. return NULL;
  558. /*
  559. * If the underlying stream operations block when no new data is readable,
  560. * we need to take extra precautions.
  561. *
  562. * If there is buffered data available, we check for a EOL. If it exists,
  563. * we pass the data immediately back to the caller. This saves a call
  564. * to the read implementation and will not block where blocking
  565. * is not necessary at all.
  566. *
  567. * If the stream buffer contains more data than the caller requested,
  568. * we can also avoid that costly step and simply return that data.
  569. */
  570. for (;;) {
  571. avail = stream->writepos - stream->readpos;
  572. if (avail > 0) {
  573. size_t cpysz = 0;
  574. char *readptr;
  575. char *eol;
  576. int done = 0;
  577. readptr = stream->readbuf + stream->readpos;
  578. eol = php_stream_locate_eol(stream, NULL, 0 TSRMLS_CC);
  579. if (eol) {
  580. cpysz = eol - readptr + 1;
  581. done = 1;
  582. } else {
  583. cpysz = avail;
  584. }
  585. if (grow_mode) {
  586. /* allow room for a NUL. If this realloc is really a realloc
  587. * (ie: second time around), we get an extra byte. In most
  588. * cases, with the default chunk size of 8K, we will only
  589. * incur that overhead once. When people have lines longer
  590. * than 8K, we waste 1 byte per additional 8K or so.
  591. * That seems acceptable to me, to avoid making this code
  592. * hard to follow */
  593. bufstart = erealloc(bufstart, current_buf_size + cpysz + 1);
  594. current_buf_size += cpysz + 1;
  595. buf = bufstart + total_copied;
  596. } else {
  597. if (cpysz >= maxlen - 1) {
  598. cpysz = maxlen - 1;
  599. done = 1;
  600. }
  601. }
  602. memcpy(buf, readptr, cpysz);
  603. stream->position += cpysz;
  604. stream->readpos += cpysz;
  605. buf += cpysz;
  606. maxlen -= cpysz;
  607. total_copied += cpysz;
  608. if (done) {
  609. break;
  610. }
  611. } else if (stream->eof) {
  612. break;
  613. } else {
  614. /* XXX: Should be fine to always read chunk_size */
  615. size_t toread;
  616. if (grow_mode) {
  617. toread = stream->chunk_size;
  618. } else {
  619. toread = maxlen - 1;
  620. if (toread > stream->chunk_size)
  621. toread = stream->chunk_size;
  622. }
  623. php_stream_fill_read_buffer(stream, toread TSRMLS_CC);
  624. if (stream->writepos - stream->readpos == 0) {
  625. break;
  626. }
  627. }
  628. }
  629. if (total_copied == 0) {
  630. if (grow_mode) {
  631. assert(bufstart == NULL);
  632. }
  633. return NULL;
  634. }
  635. buf[0] = '\0';
  636. if (returned_len)
  637. *returned_len = total_copied;
  638. return bufstart;
  639. }
  640. PHPAPI int _php_stream_flush(php_stream *stream, int closing TSRMLS_DC)
  641. {
  642. int ret = 0;
  643. if (stream->filterhead)
  644. stream->filterhead->fops->flush(stream, stream->filterhead, closing TSRMLS_CC);
  645. if (stream->ops->flush) {
  646. ret = stream->ops->flush(stream TSRMLS_CC);
  647. }
  648. return ret;
  649. }
  650. PHPAPI size_t _php_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
  651. {
  652. size_t didwrite = 0, towrite, justwrote;
  653. assert(stream);
  654. if (buf == NULL || count == 0 || stream->ops->write == NULL)
  655. return 0;
  656. while (count > 0) {
  657. towrite = count;
  658. if (towrite > stream->chunk_size)
  659. towrite = stream->chunk_size;
  660. if (stream->filterhead) {
  661. justwrote = stream->filterhead->fops->write(stream, stream->filterhead, buf, towrite TSRMLS_CC);
  662. } else {
  663. justwrote = stream->ops->write(stream, buf, towrite TSRMLS_CC);
  664. }
  665. if (justwrote > 0) {
  666. stream->position += justwrote;
  667. buf += justwrote;
  668. count -= justwrote;
  669. didwrite += justwrote;
  670. /* FIXME: invalidate the whole readbuffer */
  671. stream->writepos = 0;
  672. stream->readpos = 0;
  673. } else {
  674. break;
  675. }
  676. }
  677. return didwrite;
  678. }
  679. PHPAPI off_t _php_stream_tell(php_stream *stream TSRMLS_DC)
  680. {
  681. return stream->position;
  682. }
  683. PHPAPI int _php_stream_seek(php_stream *stream, off_t offset, int whence TSRMLS_DC)
  684. {
  685. /* not moving anywhere */
  686. if ((offset == 0 && whence == SEEK_CUR) || (offset == stream->position && whence == SEEK_SET))
  687. return 0;
  688. /* handle the case where we are in the buffer */
  689. if ((stream->flags & PHP_STREAM_FLAG_NO_BUFFER) == 0) {
  690. switch(whence) {
  691. case SEEK_CUR:
  692. if (offset > 0 && offset < stream->writepos - stream->readpos) {
  693. stream->readpos += offset;
  694. stream->position += offset;
  695. stream->eof = 0;
  696. return 0;
  697. }
  698. break;
  699. case SEEK_SET:
  700. if (offset > stream->position &&
  701. offset < stream->position + stream->writepos - stream->readpos) {
  702. stream->readpos += offset - stream->position;
  703. stream->position = offset;
  704. stream->eof = 0;
  705. return 0;
  706. }
  707. break;
  708. }
  709. }
  710. /* invalidate the buffer contents */
  711. stream->readpos = stream->writepos = 0;
  712. if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) {
  713. int ret;
  714. if (stream->filterhead)
  715. stream->filterhead->fops->flush(stream, stream->filterhead, 0 TSRMLS_CC);
  716. switch(whence) {
  717. case SEEK_CUR:
  718. offset = stream->position + offset;
  719. whence = SEEK_SET;
  720. break;
  721. }
  722. ret = stream->ops->seek(stream, offset, whence, &stream->position TSRMLS_CC);
  723. if (((stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) || ret == 0) {
  724. if (ret == 0)
  725. stream->eof = 0;
  726. return ret;
  727. }
  728. /* else the stream has decided that it can't support seeking after all;
  729. * fall through to attempt emulation */
  730. }
  731. /* emulate forward moving seeks with reads */
  732. if (whence == SEEK_CUR && offset > 0) {
  733. char tmp[1024];
  734. while(offset >= sizeof(tmp)) {
  735. if (php_stream_read(stream, tmp, sizeof(tmp)) == 0)
  736. return -1;
  737. offset -= sizeof(tmp);
  738. }
  739. if (offset) {
  740. if (php_stream_read(stream, tmp, offset) == 0)
  741. return -1;
  742. }
  743. stream->eof = 0;
  744. return 0;
  745. }
  746. php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream does not support seeking");
  747. return -1;
  748. }
  749. PHPAPI int _php_stream_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
  750. {
  751. int ret = PHP_STREAM_OPTION_RETURN_NOTIMPL;
  752. if (stream->ops->set_option) {
  753. ret = stream->ops->set_option(stream, option, value, ptrparam TSRMLS_CC);
  754. }
  755. if (ret == PHP_STREAM_OPTION_RETURN_NOTIMPL) {
  756. switch(option) {
  757. case PHP_STREAM_OPTION_SET_CHUNK_SIZE:
  758. ret = stream->chunk_size;
  759. stream->chunk_size = value;
  760. return ret;
  761. case PHP_STREAM_OPTION_READ_BUFFER:
  762. /* try to match the buffer mode as best we can */
  763. if (value == PHP_STREAM_BUFFER_NONE) {
  764. stream->flags |= PHP_STREAM_FLAG_NO_BUFFER;
  765. } else {
  766. stream->flags ^= PHP_STREAM_FLAG_NO_BUFFER;
  767. }
  768. ret = PHP_STREAM_OPTION_RETURN_OK;
  769. break;
  770. default:
  771. ret = PHP_STREAM_OPTION_RETURN_ERR;
  772. }
  773. }
  774. return ret;
  775. }
  776. PHPAPI size_t _php_stream_passthru(php_stream * stream STREAMS_DC TSRMLS_DC)
  777. {
  778. size_t bcount = 0;
  779. int ready = 0;
  780. char buf[8192];
  781. #ifdef HAVE_MMAP
  782. int fd;
  783. #endif
  784. #ifdef HAVE_MMAP
  785. if (!php_stream_is(stream, PHP_STREAM_IS_SOCKET)
  786. && stream->filterhead == NULL
  787. && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD, (void*)&fd, 0))
  788. {
  789. struct stat sbuf;
  790. off_t off;
  791. void *p;
  792. size_t len;
  793. fstat(fd, &sbuf);
  794. if (sbuf.st_size > sizeof(buf)) {
  795. off = php_stream_tell(stream);
  796. len = sbuf.st_size - off;
  797. p = mmap(0, len, PROT_READ, MAP_SHARED, fd, off);
  798. if (p != (void *) MAP_FAILED) {
  799. BG(mmap_file) = p;
  800. BG(mmap_len) = len;
  801. PHPWRITE(p, len);
  802. BG(mmap_file) = NULL;
  803. munmap(p, len);
  804. bcount += len;
  805. ready = 1;
  806. }
  807. }
  808. }
  809. #endif
  810. if(!ready) {
  811. int b;
  812. while ((b = php_stream_read(stream, buf, sizeof(buf))) > 0) {
  813. PHPWRITE(buf, b);
  814. bcount += b;
  815. }
  816. }
  817. return bcount;
  818. }
  819. PHPAPI size_t _php_stream_copy_to_mem(php_stream *src, char **buf, size_t maxlen, int persistent STREAMS_DC TSRMLS_DC)
  820. {
  821. size_t ret = 0;
  822. char *ptr;
  823. size_t len = 0, max_len;
  824. int step = CHUNK_SIZE;
  825. int min_room = CHUNK_SIZE / 4;
  826. #if HAVE_MMAP
  827. int srcfd;
  828. #endif
  829. if (buf)
  830. *buf = NULL;
  831. if (maxlen == 0)
  832. return 0;
  833. if (maxlen == PHP_STREAM_COPY_ALL)
  834. maxlen = 0;
  835. #if HAVE_MMAP
  836. /* try and optimize the case where we are copying from the start of a plain file.
  837. * We could probably make this work in more situations, but I don't trust the stdio
  838. * buffering layer.
  839. * */
  840. if ( php_stream_is(src, PHP_STREAM_IS_STDIO) &&
  841. src->filterhead == NULL &&
  842. php_stream_tell(src) == 0 &&
  843. SUCCESS == php_stream_cast(src, PHP_STREAM_AS_FD, (void**)&srcfd, 0))
  844. {
  845. struct stat sbuf;
  846. if (fstat(srcfd, &sbuf) == 0) {
  847. void *srcfile;
  848. #if STREAM_DEBUG
  849. fprintf(stderr, "mmap attempt: maxlen=%d filesize=%ld\n", maxlen, sbuf.st_size);
  850. #endif
  851. if (maxlen > sbuf.st_size || maxlen == 0)
  852. maxlen = sbuf.st_size;
  853. #if STREAM_DEBUG
  854. fprintf(stderr, "mmap attempt: will map maxlen=%d\n", maxlen);
  855. #endif
  856. srcfile = mmap(NULL, maxlen, PROT_READ, MAP_SHARED, srcfd, 0);
  857. if (srcfile != (void*)MAP_FAILED) {
  858. *buf = pemalloc_rel_orig(maxlen + 1, persistent);
  859. if (*buf) {
  860. memcpy(*buf, srcfile, maxlen);
  861. (*buf)[maxlen] = '\0';
  862. ret = maxlen;
  863. }
  864. munmap(srcfile, maxlen);
  865. return ret;
  866. }
  867. }
  868. /* fall through - we might be able to copy in smaller chunks */
  869. }
  870. #endif
  871. ptr = *buf = pemalloc_rel_orig(step, persistent);
  872. max_len = step;
  873. while((ret = php_stream_read(src, ptr, max_len - len))) {
  874. len += ret;
  875. if (len + min_room >= max_len) {
  876. *buf = perealloc_rel_orig(*buf, max_len + step, persistent);
  877. max_len += step;
  878. ptr = *buf + len;
  879. }
  880. }
  881. if (len) {
  882. *buf = perealloc_rel_orig(*buf, len + 1, persistent);
  883. (*buf)[len] = '\0';
  884. } else {
  885. pefree(*buf, persistent);
  886. *buf = NULL;
  887. }
  888. return len;
  889. }
  890. PHPAPI size_t _php_stream_copy_to_stream(php_stream *src, php_stream *dest, size_t maxlen STREAMS_DC TSRMLS_DC)
  891. {
  892. char buf[CHUNK_SIZE];
  893. size_t readchunk;
  894. size_t haveread = 0;
  895. size_t didread;
  896. #if HAVE_MMAP
  897. int srcfd;
  898. #endif
  899. if (maxlen == 0)
  900. return 0;
  901. if (maxlen == PHP_STREAM_COPY_ALL)
  902. maxlen = 0;
  903. #if HAVE_MMAP
  904. /* try and optimize the case where we are copying from the start of a plain file.
  905. * We could probably make this work in more situations, but I don't trust the stdio
  906. * buffering layer.
  907. * */
  908. if ( php_stream_is(src, PHP_STREAM_IS_STDIO) &&
  909. src->filterhead == NULL &&
  910. php_stream_tell(src) == 0 &&
  911. SUCCESS == php_stream_cast(src, PHP_STREAM_AS_FD, (void**)&srcfd, 0))
  912. {
  913. struct stat sbuf;
  914. if (fstat(srcfd, &sbuf) == 0) {
  915. void *srcfile;
  916. /* in the event that the source file is 0 bytes, return 1 to indicate success
  917. * because opening the file to write had already created a copy */
  918. if(sbuf.st_size ==0)
  919. return 1;
  920. if (maxlen > sbuf.st_size || maxlen == 0)
  921. maxlen = sbuf.st_size;
  922. srcfile = mmap(NULL, maxlen, PROT_READ, MAP_SHARED, srcfd, 0);
  923. if (srcfile != (void*)MAP_FAILED) {
  924. haveread = php_stream_write(dest, srcfile, maxlen);
  925. munmap(srcfile, maxlen);
  926. return haveread;
  927. }
  928. }
  929. /* fall through - we might be able to copy in smaller chunks */
  930. }
  931. #endif
  932. while(1) {
  933. readchunk = sizeof(buf);
  934. if (maxlen && (maxlen - haveread) < readchunk)
  935. readchunk = maxlen - haveread;
  936. didread = php_stream_read(src, buf, readchunk);
  937. if (didread) {
  938. /* extra paranoid */
  939. size_t didwrite, towrite;
  940. char *writeptr;
  941. towrite = didread;
  942. writeptr = buf;
  943. haveread += didread;
  944. while(towrite) {
  945. didwrite = php_stream_write(dest, writeptr, towrite);
  946. if (didwrite == 0)
  947. return 0; /* error */
  948. towrite -= didwrite;
  949. writeptr += didwrite;
  950. }
  951. } else {
  952. if (maxlen == 0) {
  953. return haveread;
  954. } else {
  955. return 0; /* error */
  956. }
  957. }
  958. if (maxlen - haveread == 0) {
  959. break;
  960. }
  961. }
  962. return haveread;
  963. }
  964. /* }}} */
  965. /* {{{ ------- STDIO stream implementation -------*/
  966. typedef struct {
  967. FILE *file;
  968. int fd; /* underlying file descriptor */
  969. int is_process_pipe; /* use pclose instead of fclose */
  970. int is_pipe; /* don't try and seek */
  971. #if HAVE_FLUSHIO
  972. char last_op;
  973. #endif
  974. } php_stdio_stream_data;
  975. PHPAPI php_stream *_php_stream_fopen_temporary_file(const char *dir, const char *pfx, char **opened_path STREAMS_DC TSRMLS_DC)
  976. {
  977. FILE *fp = php_open_temporary_file(dir, pfx, opened_path TSRMLS_CC);
  978. if (fp) {
  979. php_stream *stream = php_stream_fopen_from_file_rel(fp, "wb");
  980. if (stream) {
  981. return stream;
  982. }
  983. fclose(fp);
  984. php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to allocate stream");
  985. return NULL;
  986. }
  987. return NULL;
  988. }
  989. PHPAPI php_stream *_php_stream_fopen_tmpfile(int dummy STREAMS_DC TSRMLS_DC)
  990. {
  991. FILE *fp;
  992. php_stream *stream;
  993. fp = tmpfile();
  994. if (fp == NULL) {
  995. php_error_docref(NULL TSRMLS_CC, E_WARNING, "tmpfile(): %s", strerror(errno));
  996. return NULL;
  997. }
  998. stream = php_stream_fopen_from_file_rel(fp, "r+");
  999. if (stream == NULL) {
  1000. php_error_docref(NULL TSRMLS_CC, E_WARNING, "tmpfile(): %s", strerror(errno));
  1001. fclose(fp);
  1002. return NULL;
  1003. }
  1004. return stream;
  1005. }
  1006. PHPAPI php_stream *_php_stream_fopen_from_file(FILE *file, const char *mode STREAMS_DC TSRMLS_DC)
  1007. {
  1008. php_stdio_stream_data *self;
  1009. self = emalloc_rel_orig(sizeof(*self));
  1010. self->file = file;
  1011. self->is_pipe = 0;
  1012. self->is_process_pipe = 0;
  1013. self->fd = fileno(file);
  1014. #ifdef S_ISFIFO
  1015. /* detect if this is a pipe */
  1016. if (self->fd >= 0) {
  1017. struct stat sb;
  1018. self->is_pipe = (fstat(self->fd, &sb) == 0 && S_ISFIFO(sb.st_mode)) ? 1 : 0;
  1019. }
  1020. #endif
  1021. return php_stream_alloc_rel(&php_stream_stdio_ops, self, 0, mode);
  1022. }
  1023. PHPAPI php_stream *_php_stream_fopen_from_pipe(FILE *file, const char *mode STREAMS_DC TSRMLS_DC)
  1024. {
  1025. php_stdio_stream_data *self;
  1026. self = emalloc_rel_orig(sizeof(*self));
  1027. self->file = file;
  1028. self->is_pipe = 1;
  1029. self->is_process_pipe = 1;
  1030. self->fd = fileno(file);
  1031. return php_stream_alloc_rel(&php_stream_stdio_ops, self, 0, mode);
  1032. }
  1033. static size_t php_stdiop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
  1034. {
  1035. php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
  1036. assert(data != NULL);
  1037. if (data->fd >= 0) {
  1038. int bytes_written = write(data->fd, buf, count);
  1039. if (bytes_written < 0) return 0;
  1040. return (size_t) bytes_written;
  1041. } else {
  1042. #if HAVE_FLUSHIO
  1043. if (!data->is_pipe && data->last_op == 'r') {
  1044. fseek(data->file, 0, SEEK_CUR);
  1045. }
  1046. data->last_op = 'w';
  1047. #endif
  1048. return fwrite(buf, 1, count, data->file);
  1049. }
  1050. }
  1051. static size_t php_stdiop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
  1052. {
  1053. php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
  1054. size_t ret;
  1055. assert(data != NULL);
  1056. if (data->fd >= 0) {
  1057. ret = read(data->fd, buf, count);
  1058. if (ret == 0 || (ret == -1 && errno != EWOULDBLOCK))
  1059. stream->eof = 1;
  1060. } else {
  1061. #if HAVE_FLUSHIO
  1062. if (!data->is_pipe && data->last_op == 'w')
  1063. fseek(data->file, 0, SEEK_CUR);
  1064. data->last_op = 'r';
  1065. #endif
  1066. ret = fread(buf, 1, count, data->file);
  1067. if (feof(data->file))
  1068. stream->eof = 1;
  1069. }
  1070. return ret;
  1071. }
  1072. static int php_stdiop_close(php_stream *stream, int close_handle TSRMLS_DC)
  1073. {
  1074. int ret;
  1075. php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
  1076. assert(data != NULL);
  1077. if (close_handle) {
  1078. if (data->is_process_pipe) {
  1079. ret = pclose(data->file);
  1080. } else {
  1081. ret = fclose(data->file);
  1082. }
  1083. } else {
  1084. ret = 0;
  1085. }
  1086. /* STDIO streams are never persistent! */
  1087. efree(data);
  1088. return ret;
  1089. }
  1090. static int php_stdiop_flush(php_stream *stream TSRMLS_DC)
  1091. {
  1092. php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
  1093. assert(data != NULL);
  1094. /*
  1095. * stdio buffers data in user land. By calling fflush(3), this
  1096. * data is send to the kernel using write(2). fsync'ing is
  1097. * something completely different.
  1098. */
  1099. if (data->fd < 0) {
  1100. return fflush(data->file);
  1101. }
  1102. return 0;
  1103. }
  1104. static int php_stdiop_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC)
  1105. {
  1106. php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
  1107. int ret;
  1108. assert(data != NULL);
  1109. if (data->is_pipe) {
  1110. php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot seek on a pipe");
  1111. return -1;
  1112. }
  1113. if (data->fd >= 0) {
  1114. off_t result;
  1115. result = lseek(data->fd, offset, whence);
  1116. if (result == (off_t)-1)
  1117. return -1;
  1118. *newoffset = result;
  1119. return 0;
  1120. } else {
  1121. ret = fseek(data->file, offset, whence);
  1122. *newoffset = ftell(data->file);
  1123. return ret;
  1124. }
  1125. }
  1126. static int php_stdiop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
  1127. {
  1128. int fd;
  1129. php_stdio_stream_data *data = (php_stdio_stream_data*) stream->abstract;
  1130. assert(data != NULL);
  1131. /* as soon as someone touches the stdio layer, buffering may ensue,
  1132. * so we need to stop using the fd directly in that case */
  1133. switch (castas) {
  1134. case PHP_STREAM_AS_STDIO:
  1135. if (ret) {
  1136. *ret = data->file;
  1137. data->fd = -1;
  1138. }
  1139. return SUCCESS;
  1140. case PHP_STREAM_AS_FD:
  1141. /* fetch the fileno rather than using data->fd, since we may
  1142. * have zeroed that member if someone requested the FILE*
  1143. * first (see above case) */
  1144. fd = fileno(data->file);
  1145. if (fd < 0) {
  1146. return FAILURE;
  1147. }
  1148. if (ret) {
  1149. fflush(data->file);
  1150. *ret = (void*)fd;
  1151. }
  1152. return SUCCESS;
  1153. default:
  1154. return FAILURE;
  1155. }
  1156. }
  1157. static int php_stdiop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
  1158. {
  1159. int fd;
  1160. php_stdio_stream_data *data = (php_stdio_stream_data*) stream->abstract;
  1161. assert(data != NULL);
  1162. fd = fileno(data->file);
  1163. return fstat(fd, &ssb->sb);
  1164. }
  1165. static int php_stdiop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
  1166. {
  1167. php_stdio_stream_data *data = (php_stdio_stream_data*) stream->abstract;
  1168. size_t size;
  1169. int fd;
  1170. #ifdef O_NONBLOCK
  1171. /* FIXME: make this work for win32 */
  1172. int flags;
  1173. int oldval;
  1174. #endif
  1175. switch(option) {
  1176. case PHP_STREAM_OPTION_BLOCKING:
  1177. fd = fileno(data->file);
  1178. if (fd == -1)
  1179. return -1;
  1180. #ifdef O_NONBLOCK
  1181. flags = fcntl(fd, F_GETFL, 0);
  1182. oldval = (flags & O_NONBLOCK) ? 0 : 1;
  1183. if (value)
  1184. flags ^= O_NONBLOCK;
  1185. else
  1186. flags |= O_NONBLOCK;
  1187. if (-1 == fcntl(fd, F_SETFL, flags))
  1188. return -1;
  1189. return oldval;
  1190. #else
  1191. return -1; /* not yet implemented */
  1192. #endif
  1193. case PHP_STREAM_OPTION_WRITE_BUFFER:
  1194. if (ptrparam)
  1195. size = *(size_t *)ptrparam;
  1196. else
  1197. size = BUFSIZ;
  1198. switch(value) {
  1199. case PHP_STREAM_BUFFER_NONE:
  1200. stream->flags |= PHP_STREAM_FLAG_NO_BUFFER;
  1201. return setvbuf(data->file, NULL, _IONBF, 0);
  1202. case PHP_STREAM_BUFFER_LINE:
  1203. stream->flags ^= PHP_STREAM_FLAG_NO_BUFFER;
  1204. return setvbuf(data->file, NULL, _IOLBF, size);
  1205. case PHP_STREAM_BUFFER_FULL:
  1206. stream->flags ^= PHP_STREAM_FLAG_NO_BUFFER;
  1207. return setvbuf(data->file, NULL, _IOFBF, size);
  1208. default:
  1209. return -1;
  1210. }
  1211. break;
  1212. default:
  1213. return -1;
  1214. }
  1215. }
  1216. PHPAPI php_stream_ops php_stream_stdio_ops = {
  1217. php_stdiop_write, php_stdiop_read,
  1218. php_stdiop_close, php_stdiop_flush,
  1219. "STDIO",
  1220. php_stdiop_seek,
  1221. php_stdiop_cast,
  1222. php_stdiop_stat,
  1223. php_stdiop_set_option
  1224. };
  1225. /* }}} */
  1226. /* {{{ php_stream_fopen_with_path */
  1227. PHPAPI php_stream *_php_stream_fopen_with_path(char *filename, char *mode, char *path, char **opened_path, int options STREAMS_DC TSRMLS_DC)
  1228. {
  1229. /* code ripped off from fopen_wrappers.c */
  1230. char *pathbuf, *ptr, *end;
  1231. char *exec_fname;
  1232. char trypath[MAXPATHLEN];
  1233. struct stat sb;
  1234. php_stream *stream;
  1235. int path_length;
  1236. int filename_length;
  1237. int exec_fname_length;
  1238. if (opened_path) {
  1239. *opened_path = NULL;
  1240. }
  1241. if(!filename) {
  1242. return NULL;
  1243. }
  1244. filename_length = strlen(filename);
  1245. /* Relative path open */
  1246. if (*filename == '.' && (*(filename+1) == '/' || *(filename+1) == '.')) {
  1247. /* further checks, we could have ....... filenames */
  1248. ptr = filename + 1;
  1249. if (*ptr == '.') {
  1250. while (*(++ptr) == '.');
  1251. if (*ptr != '/') { /* not a relative path after all */
  1252. goto not_relative_path;
  1253. }
  1254. }
  1255. if (php_check_open_basedir(filename TSRMLS_CC)) {
  1256. return NULL;
  1257. }
  1258. if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) {
  1259. return NULL;
  1260. }
  1261. return php_stream_fopen_rel(filename, mode, opened_path, options);
  1262. }
  1263. /*
  1264. * files in safe_mode_include_dir (or subdir) are excluded from
  1265. * safe mode GID/UID checks
  1266. */
  1267. not_relative_path:
  1268. /* Absolute path open */
  1269. if (IS_ABSOLUTE_PATH(filename, filename_length)) {
  1270. if (php_check_open_basedir(filename TSRMLS_CC)) {
  1271. return NULL;
  1272. }
  1273. if ((php_check_safe_mode_include_dir(filename TSRMLS_CC)) == 0)
  1274. /* filename is in safe_mode_include_dir (or subdir) */
  1275. return php_stream_fopen_rel(filename, mode, opened_path, options);
  1276. if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM)))
  1277. return NULL;
  1278. return php_stream_fopen_rel(filename, mode, opened_path, options);
  1279. }
  1280. if (!path || (path && !*path)) {
  1281. if (php_check_open_basedir(path TSRMLS_CC)) {
  1282. return NULL;
  1283. }
  1284. if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) {
  1285. return NULL;
  1286. }
  1287. return php_stream_fopen_rel(filename, mode, opened_path, options);
  1288. }
  1289. /* check in provided path */
  1290. /* append the calling scripts' current working directory
  1291. * as a fall back case
  1292. */
  1293. if (zend_is_executing(TSRMLS_C)) {
  1294. exec_fname = zend_get_executed_filename(TSRMLS_C);
  1295. exec_fname_length = strlen(exec_fname);
  1296. path_length = strlen(path);
  1297. while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length]));
  1298. if ((exec_fname && exec_fname[0] == '[')
  1299. || exec_fname_length<=0) {
  1300. /* [no active file] or no path */
  1301. pathbuf = estrdup(path);
  1302. } else {
  1303. pathbuf = (char *) emalloc(exec_fname_length + path_length +1 +1);
  1304. memcpy(pathbuf, path, path_length);
  1305. pathbuf[path_length] = DEFAULT_DIR_SEPARATOR;
  1306. memcpy(pathbuf+path_length+1, exec_fname, exec_fname_length);
  1307. pathbuf[path_length + exec_fname_length +1] = '\0';
  1308. }
  1309. } else {
  1310. pathbuf = estrdup(path);
  1311. }
  1312. ptr = pathbuf;
  1313. while (ptr && *ptr) {
  1314. end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
  1315. if (end != NULL) {
  1316. *end = '\0';
  1317. end++;
  1318. }
  1319. snprintf(trypath, MAXPATHLEN, "%s/%s", ptr, filename);
  1320. if (PG(safe_mode)) {
  1321. if (VCWD_STAT(trypath, &sb) == 0) {
  1322. /* file exists ... check permission */
  1323. if (php_check_open_basedir(trypath TSRMLS_CC)) {
  1324. stream = NULL;
  1325. } else if ((php_check_safe_mode_include_dir(trypath TSRMLS_CC) == 0) ||
  1326. php_checkuid(trypath, mode, CHECKUID_CHECK_MODE_PARAM)) {
  1327. /* UID ok, or trypath is in safe_mode_include_dir */
  1328. stream = php_stream_fopen_rel(trypath, mode, opened_path, options);
  1329. } else {
  1330. stream = NULL;
  1331. }
  1332. efree(pathbuf);
  1333. return stream;
  1334. }
  1335. }
  1336. stream = php_stream_fopen_rel(trypath, mode, opened_path, options);
  1337. if (stream) {
  1338. efree(pathbuf);
  1339. return stream;
  1340. }
  1341. ptr = end;
  1342. } /* end provided path */
  1343. efree(pathbuf);
  1344. return NULL;
  1345. }
  1346. /* }}} */
  1347. #ifndef S_ISREG
  1348. #define S_ISREG(mode) (((mode)&S_IFMT) == S_IFREG)
  1349. #endif
  1350. /* {{{ php_stream_fopen */
  1351. PHPAPI php_stream *_php_stream_fopen(const char *filename, const char *mode, char **opened_path, int options STREAMS_DC TSRMLS_DC)
  1352. {
  1353. FILE *fp;
  1354. char *realpath = NULL;
  1355. struct stat st;
  1356. php_stream *ret;
  1357. realpath = expand_filepath(filename, NULL TSRMLS_CC);
  1358. fp = fopen(realpath, mode);
  1359. if (fp) {
  1360. /* sanity checks for include/require */
  1361. if (options & STREAM_OPEN_FOR_INCLUDE && (fstat(fileno(fp), &st) == -1 || !S_ISREG(st.st_mode))) {
  1362. goto err;
  1363. }
  1364. ret = php_stream_fopen_from_file_rel(fp, mode);
  1365. if (ret) {
  1366. if (opened_path) {
  1367. *opened_path = realpath;
  1368. realpath = NULL;
  1369. }
  1370. if (realpath)
  1371. efree(realpath);
  1372. return ret;
  1373. }
  1374. err:
  1375. fclose(fp);
  1376. }
  1377. efree(realpath);
  1378. return NULL;
  1379. }
  1380. /* }}} */
  1381. /* {{{ STDIO with fopencookie */
  1382. #if HAVE_FOPENCOOKIE
  1383. static ssize_t stream_cookie_reader(void *cookie, char *buffer, size_t size)
  1384. {
  1385. ssize_t ret;
  1386. TSRMLS_FETCH();
  1387. ret = php_stream_read(((php_stream *)cookie), buffer, size);
  1388. return ret;
  1389. }
  1390. static ssize_t stream_cookie_writer(void *cookie, const char *buffer, size_t size)
  1391. {
  1392. TSRMLS_FETCH();
  1393. return php_stream_write(((php_stream *)cookie), (char *)buffer, size);
  1394. }
  1395. #ifdef COOKIE_SEEKER_USES_FPOS_T
  1396. static int stream_cookie_seeker(void *cookie, fpos_t *position, int whence)
  1397. {
  1398. TSRMLS_FETCH();
  1399. *position = php_stream_seek((php_stream *)cookie, *position, whence);
  1400. if (*position == -1)
  1401. return -1;
  1402. return 0;
  1403. }
  1404. #else
  1405. static int stream_cookie_seeker(void *cookie, off_t position, int whence)
  1406. {
  1407. TSRMLS_FETCH();
  1408. return php_stream_seek((php_stream *)cookie, position, whence);
  1409. }
  1410. #endif
  1411. static int stream_cookie_closer(void *cookie)
  1412. {
  1413. php_stream *stream = (php_stream*)cookie;
  1414. TSRMLS_FETCH();
  1415. /* prevent recursion */
  1416. stream->fclose_stdiocast = PHP_STREAM_FCLOSE_NONE;
  1417. return php_stream_close(stream);
  1418. }
  1419. static COOKIE_IO_FUNCTIONS_T stream_cookie_functions =
  1420. {
  1421. stream_cookie_reader, stream_cookie_writer,
  1422. stream_cookie_seeker, stream_cookie_closer
  1423. };
  1424. #else
  1425. /* TODO: use socketpair() to emulate fopencookie, as suggested by Hartmut ? */
  1426. #endif
  1427. /* }}} */
  1428. /* {{{ php_stream_cast */
  1429. PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show_err TSRMLS_DC)
  1430. {
  1431. int flags = castas & PHP_STREAM_CAST_MASK;
  1432. castas &= ~PHP_STREAM_CAST_MASK;
  1433. /* synchronize our buffer (if possible) */
  1434. if (ret) {
  1435. php_stream_flush(stream);
  1436. if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) {
  1437. off_t dummy;
  1438. stream->ops->seek(stream, stream->position, SEEK_SET, &dummy TSRMLS_CC);
  1439. stream->readpos = stream->writepos = 0;
  1440. }
  1441. }
  1442. /* filtered streams can only be cast as stdio, and only when fopencookie is present */
  1443. if (castas == PHP_STREAM_AS_STDIO) {
  1444. if (stream->stdiocast) {
  1445. if (ret) {
  1446. *ret = stream->stdiocast;
  1447. }
  1448. goto exit_success;
  1449. }
  1450. /* if the stream is a stdio stream let's give it a chance to respond
  1451. * first, to avoid doubling up the layers of stdio with an fopencookie */
  1452. if (php_stream_is(stream, PHP_STREAM_IS_STDIO) &&
  1453. stream->ops->cast &&
  1454. stream->filterhead == NULL &&
  1455. stream->ops->cast(stream, castas, ret TSRMLS_CC) == SUCCESS)
  1456. {
  1457. goto exit_success;
  1458. }
  1459. #if HAVE_FOPENCOOKIE
  1460. /* if just checking, say yes we can be a FILE*, but don't actually create it yet */
  1461. if (ret == NULL)
  1462. goto exit_success;
  1463. *ret = fopencookie(stream, stream->mode, stream_cookie_functions);
  1464. if (*ret != NULL) {
  1465. off_t pos;
  1466. stream->fclose_stdiocast = PHP_STREAM_FCLOSE_FOPENCOOKIE;
  1467. /* If the stream position is not at the start, we need to force
  1468. * the stdio layer to believe it's real location. */
  1469. pos = php_stream_tell(stream);
  1470. if (pos > 0)
  1471. fseek(*ret, pos, SEEK_SET);
  1472. goto exit_success;
  1473. }
  1474. /* must be either:
  1475. a) programmer error
  1476. b) no memory
  1477. -> lets bail
  1478. */
  1479. php_error_docref(NULL TSRMLS_CC, E_ERROR, "fopencookie failed");
  1480. return FAILURE;
  1481. #endif
  1482. }
  1483. if (stream->filterhead) {
  1484. php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot cast a filtered stream on this system");
  1485. return FAILURE;
  1486. }
  1487. if (stream->ops->cast && stream->ops->cast(stream, castas, ret TSRMLS_CC) == SUCCESS)
  1488. goto exit_success;
  1489. if (show_err) {
  1490. /* these names depend on the values of the PHP_STREAM_AS_XXX defines in php_streams.h */
  1491. static const char *cast_names[3] = {
  1492. "STDIO FILE*", "File Descriptor", "Socket Descriptor"
  1493. };
  1494. php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot represent a stream of type %s as a %s",
  1495. stream->ops->label,
  1496. cast_names[castas]
  1497. );
  1498. }
  1499. return FAILURE;
  1500. exit_success:
  1501. if ((stream->writepos - stream->readpos) > 0 &&
  1502. stream->fclose_stdiocast != PHP_STREAM_FCLOSE_FOPENCOOKIE &&
  1503. (flags & PHP_STREAM_CAST_INTERNAL) == 0) {
  1504. /* the data we have buffered will be lost to the third party library that
  1505. * will be accessing the stream. Emit a warning so that the end-user will
  1506. * know that they should try something else */
  1507. php_error_docref(NULL TSRMLS_CC, E_WARNING,
  1508. "%d bytes of buffered data lost during conversion to FILE*!",
  1509. stream->writepos - stream->readpos);
  1510. }
  1511. if (castas == PHP_STREAM_AS_STDIO && ret)
  1512. stream->stdiocast = *ret;
  1513. if (flags & PHP_STREAM_CAST_RELEASE) {
  1514. /* Something other than php_stream_close will be closing
  1515. * the underlying handle, so we should free the stream handle/data
  1516. * here now. The stream may not be freed immediately (in the case
  1517. * of fopencookie), but the caller should still not touch their
  1518. * original stream pointer in any case. */
  1519. if (stream->fclose_stdiocast != PHP_STREAM_FCLOSE_FOPENCOOKIE) {
  1520. /* ask the implementation to release resources other than
  1521. * the underlying handle */
  1522. php_stream_free(stream, PHP_STREAM_FREE_PRESERVE_HANDLE | PHP_STREAM_FREE_CLOSE);
  1523. }
  1524. }
  1525. return SUCCESS;
  1526. }
  1527. /* }}} */
  1528. /* {{{ wrapper init and registration */
  1529. static void stream_resource_regular_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
  1530. {
  1531. php_stream *stream = (php_stream*)rsrc->ptr;
  1532. /* set the return value for pclose */
  1533. FG(pclose_ret) = php_stream_free(stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR);
  1534. }
  1535. static void stream_resource_persistent_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
  1536. {
  1537. php_stream *stream = (php_stream*)rsrc->ptr;
  1538. FG(pclose_ret) = php_stream_free(stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR);
  1539. }
  1540. int php_init_stream_wrappers(int module_number TSRMLS_DC)
  1541. {
  1542. le_stream = zend_register_list_destructors_ex(stream_resource_regular_dtor, NULL, "stream", module_number);
  1543. le_pstream = zend_register_list_destructors_ex(NULL, stream_resource_persistent_dtor, "persistent stream", module_number);
  1544. return (
  1545. zend_hash_init(&url_stream_wrappers_hash, 0, NULL, NULL, 1) == SUCCESS
  1546. &&
  1547. zend_hash_init(&stream_filters_hash, 0, NULL, NULL, 1) == SUCCESS
  1548. ) ? SUCCESS : FAILURE;
  1549. }
  1550. int php_shutdown_stream_wrappers(int module_number TSRMLS_DC)
  1551. {
  1552. zend_hash_destroy(&url_stream_wrappers_hash);
  1553. zend_hash_destroy(&stream_filters_hash);
  1554. return SUCCESS;
  1555. }
  1556. PHPAPI int php_register_url_stream_wrapper(char *protocol, php_stream_wrapper *wrapper TSRMLS_DC)
  1557. {
  1558. return zend_hash_add(&url_stream_wrappers_hash, protocol, strlen(protocol), wrapper, sizeof(*wrapper), NULL);
  1559. }
  1560. PHPAPI int php_unregister_url_stream_wrapper(char *protocol TSRMLS_DC)
  1561. {
  1562. return zend_hash_del(&url_stream_wrappers_hash, protocol, strlen(protocol));
  1563. }
  1564. /* }}} */
  1565. static size_t php_plain_files_dirstream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
  1566. {
  1567. DIR *dir = (DIR*)stream->abstract;
  1568. /* avoid libc5 readdir problems */
  1569. char entry[sizeof(struct dirent)+MAXPATHLEN];
  1570. struct dirent *result = (struct dirent *)&entry;
  1571. php_stream_dirent *ent = (php_stream_dirent*)buf;
  1572. /* avoid problems if someone mis-uses the stream */
  1573. if (count != sizeof(php_stream_dirent))
  1574. return 0;
  1575. if (php_readdir_r(dir, (struct dirent *)entry, &result) == 0 && result) {
  1576. PHP_STRLCPY(ent->d_name, result->d_name, sizeof(ent->d_name), strlen(result->d_name));
  1577. return sizeof(php_stream_dirent);
  1578. }
  1579. return 0;
  1580. }
  1581. static int php_plain_files_dirstream_close(php_stream *stream, int close_handle TSRMLS_DC)
  1582. {
  1583. return closedir((DIR *)stream->abstract);
  1584. }
  1585. static int php_plain_files_dirstream_rewind(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC)
  1586. {
  1587. rewinddir((DIR *)stream->abstract);
  1588. return 0;
  1589. }
  1590. static php_stream_ops php_plain_files_dirstream_ops = {
  1591. NULL, php_plain_files_dirstream_read,
  1592. php_plain_files_dirstream_close, NULL,
  1593. "dir",
  1594. php_plain_files_dirstream_rewind,
  1595. NULL, /* cast */
  1596. NULL, /* stat */
  1597. NULL /* set_option */
  1598. };
  1599. static php_stream *php_plain_files_dir_opener(php_stream_wrapper *wrapper, char *path, char *mode,
  1600. int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
  1601. {
  1602. DIR *dir = NULL;
  1603. php_stream *stream = NULL;
  1604. if (php_check_open_basedir(path TSRMLS_CC)) {
  1605. return NULL;
  1606. }
  1607. if (PG(safe_mode) &&(!php_checkuid(path, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
  1608. return NULL;
  1609. }
  1610. dir = VCWD_OPENDIR(path);
  1611. #ifdef PHP_WIN32
  1612. if (dir && dir->finished) {
  1613. closedir(dir);
  1614. dir = NULL;
  1615. }
  1616. #endif
  1617. if (dir) {
  1618. stream = php_stream_alloc(&php_plain_files_dirstream_ops, dir, 0, mode);
  1619. if (stream == NULL)
  1620. closedir(dir);
  1621. }
  1622. return stream;
  1623. }
  1624. static php_stream *php_plain_files_stream_opener(php_stream_wrapper *wrapper, char *path, char *mode,
  1625. int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
  1626. {
  1627. if ((options & USE_PATH) && PG(include_path) != NULL) {
  1628. return php_stream_fopen_with_path_rel(path, mode, PG(include_path), opened_path, options);
  1629. }
  1630. if (php_check_open_basedir(path TSRMLS_CC)) {
  1631. return NULL;
  1632. }
  1633. if ((options & ENFORCE_SAFE_MODE) && PG(safe_mode) && (!php_checkuid(path, mode, CHECKUID_CHECK_MODE_PARAM)))
  1634. return NULL;
  1635. return php_stream_fopen_rel(path, mode, opened_path, options);
  1636. }
  1637. static int php_plain_files_url_stater(php_stream_wrapper *wrapper, char *url, php_stream_statbuf *ssb TSRMLS_DC)
  1638. {
  1639. return VCWD_STAT(url, &ssb->sb);
  1640. }
  1641. static php_stream_wrapper_ops php_plain_files_wrapper_ops = {
  1642. php_plain_files_stream_opener,
  1643. NULL,
  1644. NULL,
  1645. php_plain_files_url_stater,
  1646. php_plain_files_dir_opener
  1647. };
  1648. static php_stream_wrapper php_plain_files_wrapper = {
  1649. &php_plain_files_wrapper_ops,
  1650. NULL,
  1651. 0
  1652. };
  1653. PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, char **path_for_open, int options TSRMLS_DC)
  1654. {
  1655. php_stream_wrapper *wrapper = NULL;
  1656. const char *p, *protocol = NULL;
  1657. int n = 0;
  1658. if (path_for_open)
  1659. *path_for_open = (char*)path;
  1660. if (options & IGNORE_URL)
  1661. return (options & STREAM_LOCATE_WRAPPERS_ONLY) ? NULL : &php_plain_files_wrapper;
  1662. for (p = path; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++) {
  1663. n++;
  1664. }
  1665. if ((*p == ':') && (n > 1) && !strncmp("://", p, 3)) {
  1666. protocol = path;
  1667. } else if (strncasecmp(path, "zlib:", 5) == 0) {
  1668. /* BC with older php scripts and zlib wrapper */
  1669. protocol = "compress.zlib";
  1670. n = 13;
  1671. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Use of \"zlib:\" wrapper is deprecated; please use \"compress.zlib://\" instead.");
  1672. }
  1673. if (protocol) {
  1674. if (FAILURE == zend_hash_find(&url_stream_wrappers_hash, (char*)protocol, n, (void**)&wrapper)) {
  1675. char wrapper_name[32];
  1676. if (n >= sizeof(wrapper_name))
  1677. n = sizeof(wrapper_name) - 1;
  1678. PHP_STRLCPY(wrapper_name, protocol, sizeof(wrapper_name), n);
  1679. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unable to find the wrapper \"%s\" - did you forget to enable it when you configured PHP?",
  1680. wrapper_name);
  1681. wrapper = NULL;
  1682. protocol = NULL;
  1683. }
  1684. }
  1685. /* TODO: curl based streams probably support file:// properly */
  1686. if (!protocol || !strncasecmp(protocol, "file", n)) {
  1687. if (protocol && path[n+1] == '/' && path[n+2] == '/') {
  1688. if (options & REPORT_ERRORS)
  1689. php_error_docref(NULL TSRMLS_CC, E_WARNING, "remote host file access not supported, %s", path);
  1690. return NULL;
  1691. }
  1692. if (protocol && path_for_open)
  1693. *path_for_open = (char*)path + n + 1;
  1694. /* fall back on regular file access */
  1695. return (options & STREAM_LOCATE_WRAPPERS_ONLY) ? NULL : &php_plain_files_wrapper;
  1696. }
  1697. if (wrapper && wrapper->is_url && !PG(allow_url_fopen)) {
  1698. if (options & REPORT_ERRORS)
  1699. php_error_docref(NULL TSRMLS_CC, E_WARNING, "URL file-access is disabled in the server configuration");
  1700. return NULL;
  1701. }
  1702. return wrapper;
  1703. }
  1704. PHPAPI int _php_stream_stat_path(char *path, php_stream_statbuf *ssb TSRMLS_DC)
  1705. {
  1706. php_stream_wrapper *wrapper = NULL;
  1707. char *path_to_open = path;
  1708. wrapper = php_stream_locate_url_wrapper(path, &path_to_open, ENFORCE_SAFE_MODE TSRMLS_CC);
  1709. if (wrapper && wrapper->wops->url_stat) {
  1710. return wrapper->wops->url_stat(wrapper, path_to_open, ssb TSRMLS_CC);
  1711. }
  1712. return -1;
  1713. }
  1714. /* {{{ php_stream_opendir */
  1715. PHPAPI php_stream *_php_stream_opendir(char *path, int options,
  1716. php_stream_context *context STREAMS_DC TSRMLS_DC)
  1717. {
  1718. php_stream *stream = NULL;
  1719. php_stream_wrapper *wrapper = NULL;
  1720. char *path_to_open;
  1721. if (!path || !*path)
  1722. return NULL;
  1723. path_to_open = path;
  1724. wrapper = php_stream_locate_url_wrapper(path, &path_to_open, options TSRMLS_CC);
  1725. if (wrapper && wrapper->wops->dir_opener) {
  1726. stream = wrapper->wops->dir_opener(wrapper,
  1727. path_to_open, "r", options ^ REPORT_ERRORS, NULL,
  1728. context STREAMS_REL_CC TSRMLS_CC);
  1729. if (stream) {
  1730. stream->wrapper = wrapper;
  1731. stream->flags |= PHP_STREAM_FLAG_NO_BUFFER;
  1732. }
  1733. } else if (wrapper) {
  1734. php_stream_wrapper_log_error(wrapper, options ^ REPORT_ERRORS TSRMLS_CC, "not implemented");
  1735. }
  1736. if (stream == NULL && (options & REPORT_ERRORS)) {
  1737. display_wrapper_errors(wrapper, path, "failed to open dir" TSRMLS_CC);
  1738. }
  1739. tidy_wrapper_error_log(wrapper TSRMLS_CC);
  1740. return stream;
  1741. }
  1742. /* }}} */
  1743. PHPAPI php_stream_dirent *_php_stream_readdir(php_stream *dirstream, php_stream_dirent *ent TSRMLS_DC)
  1744. {
  1745. if (sizeof(php_stream_dirent) == php_stream_read(dirstream, (char*)ent, sizeof(php_stream_dirent)))
  1746. return ent;
  1747. return NULL;
  1748. }
  1749. PHPAPI void php_stream_wrapper_log_error(php_stream_wrapper *wrapper, int options TSRMLS_DC, const char *fmt, ...)
  1750. {
  1751. va_list args;
  1752. char *buffer = NULL;
  1753. va_start(args, fmt);
  1754. vspprintf(&buffer, 0, fmt, args);
  1755. va_end(args);
  1756. if (options & REPORT_ERRORS || wrapper == NULL) {
  1757. php_error_docref(NULL TSRMLS_CC, E_WARNING, buffer);
  1758. efree(buffer);
  1759. } else {
  1760. /* append to stack */
  1761. wrapper->err_stack = erealloc(wrapper->err_stack, (wrapper->err_count + 1) * sizeof(char *));
  1762. if (wrapper->err_stack)
  1763. wrapper->err_stack[wrapper->err_count++] = buffer;
  1764. }
  1765. }
  1766. /* {{{ php_stream_open_wrapper_ex */
  1767. PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int options,
  1768. char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
  1769. {
  1770. php_stream *stream = NULL;
  1771. php_stream_wrapper *wrapper = NULL;
  1772. char *path_to_open;
  1773. #if ZEND_DEBUG
  1774. char *copy_of_path = NULL;
  1775. #endif
  1776. if (opened_path)
  1777. *opened_path = NULL;
  1778. if (!path || !*path)
  1779. return NULL;
  1780. path_to_open = path;
  1781. wrapper = php_stream_locate_url_wrapper(path, &path_to_open, options TSRMLS_CC);
  1782. if (wrapper) {
  1783. /* prepare error stack */
  1784. wrapper->err_count = 0;
  1785. wrapper->err_stack = NULL;
  1786. stream = wrapper->wops->stream_opener(wrapper,
  1787. path_to_open, mode, options ^ REPORT_ERRORS,
  1788. opened_path, context STREAMS_REL_CC TSRMLS_CC);
  1789. if (stream)
  1790. stream->wrapper = wrapper;
  1791. }
  1792. #if ZEND_DEBUG
  1793. if (stream) {
  1794. copy_of_path = estrdup(path);
  1795. stream->__orig_path = copy_of_path;
  1796. }
  1797. #endif
  1798. if (stream != NULL && (options & STREAM_MUST_SEEK)) {
  1799. php_stream *newstream;
  1800. switch(php_stream_make_seekable_rel(stream, &newstream,
  1801. (options & STREAM_WILL_CAST)
  1802. ? PHP_STREAM_PREFER_STDIO : PHP_STREAM_NO_PREFERENCE)) {
  1803. case PHP_STREAM_UNCHANGED:
  1804. return stream;
  1805. case PHP_STREAM_RELEASED:
  1806. #if ZEND_DEBUG
  1807. newstream->__orig_path = copy_of_path;
  1808. #endif
  1809. return newstream;
  1810. default:
  1811. php_stream_close(stream);
  1812. stream = NULL;
  1813. if (options & REPORT_ERRORS) {
  1814. char *tmp = estrdup(path);
  1815. php_strip_url_passwd(tmp);
  1816. php_error_docref1(NULL TSRMLS_CC, tmp, E_WARNING, "could not make seekable - %s",
  1817. tmp, strerror(errno));
  1818. efree(tmp);
  1819. options ^= REPORT_ERRORS;
  1820. }
  1821. }
  1822. }
  1823. if (stream == NULL && (options & REPORT_ERRORS)) {
  1824. display_wrapper_errors(wrapper, path, "failed to create stream" TSRMLS_CC);
  1825. }
  1826. tidy_wrapper_error_log(wrapper TSRMLS_CC);
  1827. #if ZEND_DEBUG
  1828. if (stream == NULL && copy_of_path != NULL)
  1829. efree(copy_of_path);
  1830. #endif
  1831. return stream;
  1832. }
  1833. /* }}} */
  1834. /* {{{ php_stream_open_wrapper_as_file */
  1835. PHPAPI FILE * _php_stream_open_wrapper_as_file(char *path, char *mode, int options, char **opened_path STREAMS_DC TSRMLS_DC)
  1836. {
  1837. FILE *fp = NULL;
  1838. php_stream *stream = NULL;
  1839. stream = php_stream_open_wrapper_rel(path, mode, options|STREAM_WILL_CAST, opened_path);
  1840. if (stream == NULL)
  1841. return NULL;
  1842. if (php_stream_cast(stream, PHP_STREAM_AS_STDIO|PHP_STREAM_CAST_TRY_HARD|PHP_STREAM_CAST_RELEASE,
  1843. (void**)&fp, REPORT_ERRORS) == FAILURE)
  1844. {
  1845. php_stream_close(stream);
  1846. if (opened_path && *opened_path)
  1847. efree(*opened_path);
  1848. return NULL;
  1849. }
  1850. return fp;
  1851. }
  1852. /* }}} */
  1853. /* {{{ php_stream_make_seekable */
  1854. PHPAPI int _php_stream_make_seekable(php_stream *origstream, php_stream **newstream, int flags STREAMS_DC TSRMLS_DC)
  1855. {
  1856. assert(newstream != NULL);
  1857. *newstream = NULL;
  1858. if (origstream->ops->seek != NULL) {
  1859. *newstream = origstream;
  1860. return PHP_STREAM_UNCHANGED;
  1861. }
  1862. /* Use a tmpfile and copy the old streams contents into it */
  1863. if (flags & PHP_STREAM_PREFER_STDIO)
  1864. *newstream = php_stream_fopen_tmpfile();
  1865. else
  1866. *newstream = php_stream_temp_new();
  1867. if (*newstream == NULL)
  1868. return PHP_STREAM_FAILED;
  1869. if (php_stream_copy_to_stream(origstream, *newstream, PHP_STREAM_COPY_ALL) == 0) {
  1870. php_stream_close(*newstream);
  1871. *newstream = NULL;
  1872. return PHP_STREAM_CRITICAL;
  1873. }
  1874. php_stream_close(origstream);
  1875. php_stream_seek(*newstream, 0, SEEK_SET);
  1876. return PHP_STREAM_RELEASED;
  1877. }
  1878. /* }}} */
  1879. PHPAPI php_stream_context *php_stream_context_set(php_stream *stream, php_stream_context *context)
  1880. {
  1881. php_stream_context *oldcontext = stream->context;
  1882. stream->context = context;
  1883. return oldcontext;
  1884. }
  1885. PHPAPI void php_stream_notification_notify(php_stream_context *context, int notifycode, int severity,
  1886. char *xmsg, int xcode, size_t bytes_sofar, size_t bytes_max, void * ptr TSRMLS_DC)
  1887. {
  1888. if (context && context->notifier)
  1889. context->notifier->func(context, notifycode, severity, xmsg, xcode, bytes_sofar, bytes_max, ptr TSRMLS_CC);
  1890. }
  1891. PHPAPI void php_stream_context_free(php_stream_context *context)
  1892. {
  1893. zval_ptr_dtor(&context->options);
  1894. efree(context);
  1895. }
  1896. PHPAPI php_stream_context *php_stream_context_alloc(void)
  1897. {
  1898. php_stream_context *context;
  1899. context = ecalloc(1, sizeof(php_stream_context));
  1900. MAKE_STD_ZVAL(context->options);
  1901. array_init(context->options);
  1902. return context;
  1903. }
  1904. PHPAPI php_stream_notifier *php_stream_notification_alloc(void)
  1905. {
  1906. return ecalloc(1, sizeof(php_stream_notifier));
  1907. }
  1908. PHPAPI void php_stream_notification_free(php_stream_notifier *notifier)
  1909. {
  1910. efree(notifier);
  1911. }
  1912. PHPAPI int php_stream_context_get_option(php_stream_context *context,
  1913. const char *wrappername, const char *optionname, zval ***optionvalue)
  1914. {
  1915. zval **wrapperhash;
  1916. if (FAILURE == zend_hash_find(Z_ARRVAL_P(context->options), (char*)wrappername, strlen(wrappername)+1, (void**)&wrapperhash))
  1917. return FAILURE;
  1918. return zend_hash_find(Z_ARRVAL_PP(wrapperhash), (char*)optionname, strlen(optionname)+1, (void**)optionvalue);
  1919. }
  1920. PHPAPI int php_stream_context_set_option(php_stream_context *context,
  1921. const char *wrappername, const char *optionname, zval *optionvalue)
  1922. {
  1923. zval **wrapperhash;
  1924. zval *category;
  1925. if (FAILURE == zend_hash_find(Z_ARRVAL_P(context->options), (char*)wrappername, strlen(wrappername)+1, (void**)&wrapperhash)) {
  1926. MAKE_STD_ZVAL(category);
  1927. array_init(category);
  1928. if (FAILURE == zend_hash_update(Z_ARRVAL_P(context->options), (char*)wrappername, strlen(wrappername)+1, (void**)&category, sizeof(zval *), NULL))
  1929. return FAILURE;
  1930. ZVAL_ADDREF(optionvalue);
  1931. wrapperhash = &category;
  1932. }
  1933. return zend_hash_update(Z_ARRVAL_PP(wrapperhash), (char*)optionname, strlen(optionname)+1, (void**)&optionvalue, sizeof(zval *), NULL);
  1934. }
  1935. PHPAPI HashTable *php_stream_get_url_stream_wrappers_hash()
  1936. {
  1937. return &url_stream_wrappers_hash;
  1938. }
  1939. /*
  1940. * Local variables:
  1941. * tab-width: 4
  1942. * c-basic-offset: 4
  1943. * End:
  1944. * vim600: noet sw=4 ts=4 fdm=marker
  1945. * vim<600: noet sw=4 ts=4
  1946. */