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

/main/streams/memory.c

http://github.com/infusion/PHP
C | 762 lines | 554 code | 114 blank | 94 comment | 123 complexity | a8053a1475b25f89c1a715c2be669eff MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-2.1, BSD-3-Clause
  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. | Author: Marcus Boerger <helly@php.net> |
  16. +----------------------------------------------------------------------+
  17. */
  18. /* $Id: memory.c 306939 2011-01-01 02:19:59Z felipe $ */
  19. #define _GNU_SOURCE
  20. #include "php.h"
  21. PHPAPI int php_url_decode(char *str, int len);
  22. PHPAPI unsigned char *php_base64_decode(const unsigned char *str, int length, int *ret_length);
  23. /* Memory streams use a dynamic memory buffer to emulate a stream.
  24. * You can use php_stream_memory_open to create a readonly stream
  25. * from an existing memory buffer.
  26. */
  27. /* Temp streams are streams that uses memory streams as long their
  28. * size is less than a given memory amount. When a write operation
  29. * exceeds that limit the content is written to a temporary file.
  30. */
  31. /* {{{ ------- MEMORY stream implementation -------*/
  32. typedef struct {
  33. char *data;
  34. size_t fpos;
  35. size_t fsize;
  36. size_t smax;
  37. int mode;
  38. php_stream **owner_ptr;
  39. } php_stream_memory_data;
  40. /* {{{ */
  41. static size_t php_stream_memory_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
  42. {
  43. php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract;
  44. assert(ms != NULL);
  45. if (ms->mode & TEMP_STREAM_READONLY) {
  46. return 0;
  47. }
  48. if (ms->fpos + count > ms->fsize) {
  49. char *tmp;
  50. if (!ms->data) {
  51. tmp = emalloc(ms->fpos + count);
  52. } else {
  53. tmp = erealloc(ms->data, ms->fpos + count);
  54. }
  55. if (!tmp) {
  56. count = ms->fsize - ms->fpos + 1;
  57. } else {
  58. ms->data = tmp;
  59. ms->fsize = ms->fpos + count;
  60. }
  61. }
  62. if (!ms->data)
  63. count = 0;
  64. if (count) {
  65. assert(buf!= NULL);
  66. memcpy(ms->data+ms->fpos, (char*)buf, count);
  67. ms->fpos += count;
  68. }
  69. return count;
  70. }
  71. /* }}} */
  72. /* {{{ */
  73. static size_t php_stream_memory_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
  74. {
  75. php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract;
  76. assert(ms != NULL);
  77. if (ms->fpos + count >= ms->fsize) {
  78. count = ms->fsize - ms->fpos;
  79. stream->eof = 1;
  80. }
  81. if (count) {
  82. assert(ms->data!= NULL);
  83. assert(buf!= NULL);
  84. memcpy(buf, ms->data+ms->fpos, count);
  85. ms->fpos += count;
  86. }
  87. return count;
  88. }
  89. /* }}} */
  90. /* {{{ */
  91. static int php_stream_memory_close(php_stream *stream, int close_handle TSRMLS_DC)
  92. {
  93. php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract;
  94. assert(ms != NULL);
  95. if (ms->data && close_handle && ms->mode != TEMP_STREAM_READONLY) {
  96. efree(ms->data);
  97. }
  98. if (ms->owner_ptr) {
  99. *ms->owner_ptr = NULL;
  100. }
  101. efree(ms);
  102. return 0;
  103. }
  104. /* }}} */
  105. /* {{{ */
  106. static int php_stream_memory_flush(php_stream *stream TSRMLS_DC)
  107. {
  108. /* nothing to do here */
  109. return 0;
  110. }
  111. /* }}} */
  112. /* {{{ */
  113. static int php_stream_memory_seek(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC)
  114. {
  115. php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract;
  116. assert(ms != NULL);
  117. switch(whence) {
  118. case SEEK_CUR:
  119. if (offset < 0) {
  120. if (ms->fpos < (size_t)(-offset)) {
  121. ms->fpos = 0;
  122. *newoffs = -1;
  123. return -1;
  124. } else {
  125. ms->fpos = ms->fpos + offset;
  126. *newoffs = ms->fpos;
  127. stream->eof = 0;
  128. return 0;
  129. }
  130. } else {
  131. if (ms->fpos + (size_t)(offset) > ms->fsize) {
  132. ms->fpos = ms->fsize;
  133. *newoffs = -1;
  134. return -1;
  135. } else {
  136. ms->fpos = ms->fpos + offset;
  137. *newoffs = ms->fpos;
  138. stream->eof = 0;
  139. return 0;
  140. }
  141. }
  142. case SEEK_SET:
  143. if (ms->fsize < (size_t)(offset)) {
  144. ms->fpos = ms->fsize;
  145. *newoffs = -1;
  146. return -1;
  147. } else {
  148. ms->fpos = offset;
  149. *newoffs = ms->fpos;
  150. stream->eof = 0;
  151. return 0;
  152. }
  153. case SEEK_END:
  154. if (offset > 0) {
  155. ms->fpos = ms->fsize;
  156. *newoffs = -1;
  157. return -1;
  158. } else if (ms->fsize < (size_t)(-offset)) {
  159. ms->fpos = 0;
  160. *newoffs = -1;
  161. return -1;
  162. } else {
  163. ms->fpos = ms->fsize + offset;
  164. *newoffs = ms->fpos;
  165. stream->eof = 0;
  166. return 0;
  167. }
  168. default:
  169. *newoffs = ms->fpos;
  170. return -1;
  171. }
  172. }
  173. /* }}} */
  174. /* {{{ */
  175. static int php_stream_memory_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
  176. {
  177. return FAILURE;
  178. }
  179. /* }}} */
  180. static int php_stream_memory_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) /* {{{ */
  181. {
  182. time_t timestamp = 0;
  183. php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract;
  184. assert(ms != NULL);
  185. memset(ssb, 0, sizeof(php_stream_statbuf));
  186. /* read-only across the board */
  187. ssb->sb.st_mode = ms->mode & TEMP_STREAM_READONLY ? 0444 : 0666;
  188. ssb->sb.st_size = ms->fsize;
  189. ssb->sb.st_mode |= S_IFREG; /* regular file */
  190. #ifdef NETWARE
  191. ssb->sb.st_mtime.tv_sec = timestamp;
  192. ssb->sb.st_atime.tv_sec = timestamp;
  193. ssb->sb.st_ctime.tv_sec = timestamp;
  194. #else
  195. ssb->sb.st_mtime = timestamp;
  196. ssb->sb.st_atime = timestamp;
  197. ssb->sb.st_ctime = timestamp;
  198. #endif
  199. ssb->sb.st_nlink = 1;
  200. ssb->sb.st_rdev = -1;
  201. /* this is only for APC, so use /dev/null device - no chance of conflict there! */
  202. ssb->sb.st_dev = 0xC;
  203. /* generate unique inode number for alias/filename, so no phars will conflict */
  204. ssb->sb.st_ino = 0;
  205. #ifndef PHP_WIN32
  206. ssb->sb.st_blksize = -1;
  207. #endif
  208. #if !defined(PHP_WIN32) && !defined(__BEOS__)
  209. ssb->sb.st_blocks = -1;
  210. #endif
  211. return 0;
  212. }
  213. /* }}} */
  214. static int php_stream_memory_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) /* {{{ */
  215. {
  216. php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract;
  217. size_t newsize;
  218. switch(option) {
  219. case PHP_STREAM_OPTION_TRUNCATE_API:
  220. switch (value) {
  221. case PHP_STREAM_TRUNCATE_SUPPORTED:
  222. return PHP_STREAM_OPTION_RETURN_OK;
  223. case PHP_STREAM_TRUNCATE_SET_SIZE:
  224. if (ms->mode & TEMP_STREAM_READONLY) {
  225. return PHP_STREAM_OPTION_RETURN_ERR;
  226. }
  227. newsize = *(size_t*)ptrparam;
  228. if (newsize <= ms->fsize) {
  229. if (newsize < ms->fpos) {
  230. ms->fpos = newsize;
  231. }
  232. } else {
  233. ms->data = erealloc(ms->data, newsize);
  234. memset(ms->data+ms->fsize, 0, newsize - ms->fsize);
  235. ms->fsize = newsize;
  236. }
  237. ms->fsize = newsize;
  238. return PHP_STREAM_OPTION_RETURN_OK;
  239. }
  240. default:
  241. return PHP_STREAM_OPTION_RETURN_NOTIMPL;
  242. }
  243. }
  244. /* }}} */
  245. PHPAPI php_stream_ops php_stream_memory_ops = {
  246. php_stream_memory_write, php_stream_memory_read,
  247. php_stream_memory_close, php_stream_memory_flush,
  248. "MEMORY",
  249. php_stream_memory_seek,
  250. php_stream_memory_cast,
  251. php_stream_memory_stat,
  252. php_stream_memory_set_option
  253. };
  254. /* {{{ */
  255. PHPAPI php_stream *_php_stream_memory_create(int mode STREAMS_DC TSRMLS_DC)
  256. {
  257. php_stream_memory_data *self;
  258. php_stream *stream;
  259. self = emalloc(sizeof(*self));
  260. self->data = NULL;
  261. self->fpos = 0;
  262. self->fsize = 0;
  263. self->smax = ~0u;
  264. self->mode = mode;
  265. self->owner_ptr = NULL;
  266. stream = php_stream_alloc_rel(&php_stream_memory_ops, self, 0, mode & TEMP_STREAM_READONLY ? "rb" : "w+b");
  267. stream->flags |= PHP_STREAM_FLAG_NO_BUFFER;
  268. return stream;
  269. }
  270. /* }}} */
  271. /* {{{ */
  272. PHPAPI php_stream *_php_stream_memory_open(int mode, char *buf, size_t length STREAMS_DC TSRMLS_DC)
  273. {
  274. php_stream *stream;
  275. php_stream_memory_data *ms;
  276. if ((stream = php_stream_memory_create_rel(mode)) != NULL) {
  277. ms = (php_stream_memory_data*)stream->abstract;
  278. if (mode == TEMP_STREAM_READONLY || mode == TEMP_STREAM_TAKE_BUFFER) {
  279. /* use the buffer directly */
  280. ms->data = buf;
  281. ms->fsize = length;
  282. } else {
  283. if (length) {
  284. assert(buf != NULL);
  285. php_stream_write(stream, buf, length);
  286. }
  287. }
  288. }
  289. return stream;
  290. }
  291. /* }}} */
  292. /* {{{ */
  293. PHPAPI char *_php_stream_memory_get_buffer(php_stream *stream, size_t *length STREAMS_DC TSRMLS_DC)
  294. {
  295. php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract;
  296. assert(ms != NULL);
  297. assert(length != 0);
  298. *length = ms->fsize;
  299. return ms->data;
  300. }
  301. /* }}} */
  302. /* }}} */
  303. /* {{{ ------- TEMP stream implementation -------*/
  304. typedef struct {
  305. php_stream *innerstream;
  306. size_t smax;
  307. int mode;
  308. zval* meta;
  309. } php_stream_temp_data;
  310. /* {{{ */
  311. static size_t php_stream_temp_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
  312. {
  313. php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
  314. assert(ts != NULL);
  315. if (!ts->innerstream) {
  316. return -1;
  317. }
  318. if (php_stream_is(ts->innerstream, PHP_STREAM_IS_MEMORY)) {
  319. size_t memsize;
  320. char *membuf = php_stream_memory_get_buffer(ts->innerstream, &memsize);
  321. if (memsize + count >= ts->smax) {
  322. php_stream *file = php_stream_fopen_tmpfile();
  323. php_stream_write(file, membuf, memsize);
  324. php_stream_close(ts->innerstream);
  325. ts->innerstream = file;
  326. }
  327. }
  328. return php_stream_write(ts->innerstream, buf, count);
  329. }
  330. /* }}} */
  331. /* {{{ */
  332. static size_t php_stream_temp_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
  333. {
  334. php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
  335. size_t got;
  336. assert(ts != NULL);
  337. if (!ts->innerstream) {
  338. return -1;
  339. }
  340. got = php_stream_read(ts->innerstream, buf, count);
  341. stream->eof = ts->innerstream->eof;
  342. return got;
  343. }
  344. /* }}} */
  345. /* {{{ */
  346. static int php_stream_temp_close(php_stream *stream, int close_handle TSRMLS_DC)
  347. {
  348. php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
  349. int ret;
  350. assert(ts != NULL);
  351. if (ts->innerstream) {
  352. ret = php_stream_free(ts->innerstream, PHP_STREAM_FREE_CLOSE | (close_handle ? 0 : PHP_STREAM_FREE_PRESERVE_HANDLE));
  353. } else {
  354. ret = 0;
  355. }
  356. if (ts->meta) {
  357. zval_ptr_dtor(&ts->meta);
  358. }
  359. efree(ts);
  360. return ret;
  361. }
  362. /* }}} */
  363. /* {{{ */
  364. static int php_stream_temp_flush(php_stream *stream TSRMLS_DC)
  365. {
  366. php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
  367. assert(ts != NULL);
  368. return ts->innerstream ? php_stream_flush(ts->innerstream) : -1;
  369. }
  370. /* }}} */
  371. /* {{{ */
  372. static int php_stream_temp_seek(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC)
  373. {
  374. php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
  375. int ret;
  376. assert(ts != NULL);
  377. if (!ts->innerstream) {
  378. *newoffs = -1;
  379. return -1;
  380. }
  381. ret = php_stream_seek(ts->innerstream, offset, whence);
  382. *newoffs = php_stream_tell(ts->innerstream);
  383. stream->eof = ts->innerstream->eof;
  384. return ret;
  385. }
  386. /* }}} */
  387. /* {{{ */
  388. static int php_stream_temp_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
  389. {
  390. php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
  391. php_stream *file;
  392. size_t memsize;
  393. char *membuf;
  394. off_t pos;
  395. assert(ts != NULL);
  396. if (!ts->innerstream) {
  397. return FAILURE;
  398. }
  399. if (php_stream_is(ts->innerstream, PHP_STREAM_IS_STDIO)) {
  400. return php_stream_cast(ts->innerstream, castas, ret, 0);
  401. }
  402. /* we are still using a memory based backing. If they are if we can be
  403. * a FILE*, say yes because we can perform the conversion.
  404. * If they actually want to perform the conversion, we need to switch
  405. * the memory stream to a tmpfile stream */
  406. if (ret == NULL && castas == PHP_STREAM_AS_STDIO) {
  407. return SUCCESS;
  408. }
  409. /* say "no" to other stream forms */
  410. if (ret == NULL) {
  411. return FAILURE;
  412. }
  413. /* perform the conversion and then pass the request on to the innerstream */
  414. membuf = php_stream_memory_get_buffer(ts->innerstream, &memsize);
  415. file = php_stream_fopen_tmpfile();
  416. php_stream_write(file, membuf, memsize);
  417. pos = php_stream_tell(ts->innerstream);
  418. php_stream_close(ts->innerstream);
  419. ts->innerstream = file;
  420. php_stream_seek(ts->innerstream, pos, SEEK_SET);
  421. return php_stream_cast(ts->innerstream, castas, ret, 1);
  422. }
  423. /* }}} */
  424. static int php_stream_temp_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) /* {{{ */
  425. {
  426. php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
  427. if (!ts || !ts->innerstream) {
  428. return -1;
  429. }
  430. return php_stream_stat(ts->innerstream, ssb);
  431. }
  432. /* }}} */
  433. static int php_stream_temp_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) /* {{{ */
  434. {
  435. php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
  436. switch(option) {
  437. case PHP_STREAM_OPTION_META_DATA_API:
  438. if (ts->meta) {
  439. zend_hash_copy(Z_ARRVAL_P((zval*)ptrparam), Z_ARRVAL_P(ts->meta), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval*));
  440. }
  441. return PHP_STREAM_OPTION_RETURN_OK;
  442. default:
  443. if (ts->innerstream) {
  444. return php_stream_set_option(ts->innerstream, option, value, ptrparam);
  445. }
  446. return PHP_STREAM_OPTION_RETURN_NOTIMPL;
  447. }
  448. }
  449. /* }}} */
  450. PHPAPI php_stream_ops php_stream_temp_ops = {
  451. php_stream_temp_write, php_stream_temp_read,
  452. php_stream_temp_close, php_stream_temp_flush,
  453. "TEMP",
  454. php_stream_temp_seek,
  455. php_stream_temp_cast,
  456. php_stream_temp_stat,
  457. php_stream_temp_set_option
  458. };
  459. /* }}} */
  460. /* {{{ _php_stream_temp_create */
  461. PHPAPI php_stream *_php_stream_temp_create(int mode, size_t max_memory_usage STREAMS_DC TSRMLS_DC)
  462. {
  463. php_stream_temp_data *self;
  464. php_stream *stream;
  465. self = ecalloc(1, sizeof(*self));
  466. self->smax = max_memory_usage;
  467. self->mode = mode;
  468. self->meta = NULL;
  469. stream = php_stream_alloc_rel(&php_stream_temp_ops, self, 0, mode & TEMP_STREAM_READONLY ? "rb" : "w+b");
  470. stream->flags |= PHP_STREAM_FLAG_NO_BUFFER;
  471. self->innerstream = php_stream_memory_create_rel(mode);
  472. php_stream_auto_cleanup(self->innerstream); /* do not warn if innerstream is GC'ed before stream */
  473. ((php_stream_memory_data*)self->innerstream->abstract)->owner_ptr = &self->innerstream;
  474. return stream;
  475. }
  476. /* }}} */
  477. /* {{{ _php_stream_temp_open */
  478. PHPAPI php_stream *_php_stream_temp_open(int mode, size_t max_memory_usage, char *buf, size_t length STREAMS_DC TSRMLS_DC)
  479. {
  480. php_stream *stream;
  481. php_stream_temp_data *ts;
  482. off_t newoffs;
  483. if ((stream = php_stream_temp_create_rel(mode, max_memory_usage)) != NULL) {
  484. if (length) {
  485. assert(buf != NULL);
  486. php_stream_temp_write(stream, buf, length TSRMLS_CC);
  487. php_stream_temp_seek(stream, 0, SEEK_SET, &newoffs TSRMLS_CC);
  488. }
  489. ts = (php_stream_temp_data*)stream->abstract;
  490. assert(ts != NULL);
  491. ts->mode = mode;
  492. }
  493. return stream;
  494. }
  495. /* }}} */
  496. PHPAPI php_stream_ops php_stream_rfc2397_ops = {
  497. php_stream_temp_write, php_stream_temp_read,
  498. php_stream_temp_close, php_stream_temp_flush,
  499. "RFC2397",
  500. php_stream_temp_seek,
  501. php_stream_temp_cast,
  502. php_stream_temp_stat,
  503. php_stream_temp_set_option
  504. };
  505. static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) /* {{{ */
  506. {
  507. php_stream *stream;
  508. php_stream_temp_data *ts;
  509. char *comma, *semi, *sep, *key;
  510. size_t mlen, dlen, plen, vlen;
  511. off_t newoffs;
  512. zval *meta = NULL;
  513. int base64 = 0, ilen;
  514. if (memcmp(path, "data:", 5)) {
  515. return NULL;
  516. }
  517. path += 5;
  518. dlen = strlen(path);
  519. if (dlen >= 2 && path[0] == '/' && path[1] == '/') {
  520. dlen -= 2;
  521. path += 2;
  522. }
  523. if ((comma = memchr(path, ',', dlen)) == NULL) {
  524. php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: no comma in URL");
  525. return NULL;
  526. }
  527. if (comma != path) {
  528. /* meta info */
  529. mlen = comma - path;
  530. dlen -= mlen;
  531. semi = memchr(path, ';', mlen);
  532. sep = memchr(path, '/', mlen);
  533. if (!semi && !sep) {
  534. php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: illegal media type");
  535. return NULL;
  536. }
  537. MAKE_STD_ZVAL(meta);
  538. array_init(meta);
  539. if (!semi) { /* there is only a mime type */
  540. add_assoc_stringl(meta, "mediatype", path, mlen, 1);
  541. mlen = 0;
  542. } else if (sep && sep < semi) { /* there is a mime type */
  543. plen = semi - path;
  544. add_assoc_stringl(meta, "mediatype", path, plen, 1);
  545. mlen -= plen;
  546. path += plen;
  547. } else if (semi != path || mlen != sizeof(";base64")-1 || memcmp(path, ";base64", sizeof(";base64")-1)) { /* must be error since parameters are only allowed after mediatype */
  548. zval_ptr_dtor(&meta);
  549. php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: illegal media type");
  550. return NULL;
  551. }
  552. /* get parameters and potentially ';base64' */
  553. while(semi && (semi == path)) {
  554. path++;
  555. mlen--;
  556. sep = memchr(path, '=', mlen);
  557. semi = memchr(path, ';', mlen);
  558. if (!sep || (semi && semi < sep)) { /* must be ';base64' or failure */
  559. if (mlen != sizeof("base64")-1 || memcmp(path, "base64", sizeof("base64")-1)) {
  560. /* must be error since parameters are only allowed after mediatype and we have no '=' sign */
  561. zval_ptr_dtor(&meta);
  562. php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: illegal parameter");
  563. return NULL;
  564. }
  565. base64 = 1;
  566. mlen -= sizeof("base64") - 1;
  567. path += sizeof("base64") - 1;
  568. break;
  569. }
  570. /* found parameter ... the heart of cs ppl lies in +1/-1 or was it +2 this time? */
  571. plen = sep - path;
  572. vlen = (semi ? semi - sep : mlen - plen) - 1 /* '=' */;
  573. key = estrndup(path, plen);
  574. add_assoc_stringl_ex(meta, key, plen + 1, sep + 1, vlen, 1);
  575. efree(key);
  576. plen += vlen + 1;
  577. mlen -= plen;
  578. path += plen;
  579. }
  580. if (mlen) {
  581. zval_ptr_dtor(&meta);
  582. php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: illegal URL");
  583. return NULL;
  584. }
  585. } else {
  586. MAKE_STD_ZVAL(meta);
  587. array_init(meta);
  588. }
  589. add_assoc_bool(meta, "base64", base64);
  590. /* skip ',' */
  591. comma++;
  592. dlen--;
  593. if (base64) {
  594. comma = (char*)php_base64_decode((const unsigned char *)comma, dlen, &ilen);
  595. if (!comma) {
  596. zval_ptr_dtor(&meta);
  597. php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: unable to decode");
  598. return NULL;
  599. }
  600. } else {
  601. comma = estrndup(comma, dlen);
  602. ilen = dlen = php_url_decode(comma, dlen);
  603. }
  604. if ((stream = php_stream_temp_create_rel(0, ~0u)) != NULL) {
  605. /* store data */
  606. php_stream_temp_write(stream, comma, ilen TSRMLS_CC);
  607. php_stream_temp_seek(stream, 0, SEEK_SET, &newoffs TSRMLS_CC);
  608. /* set special stream stuff (enforce exact mode) */
  609. vlen = strlen(mode);
  610. if (vlen >= sizeof(stream->mode)) {
  611. vlen = sizeof(stream->mode) - 1;
  612. }
  613. memcpy(stream->mode, mode, vlen);
  614. stream->mode[vlen] = '\0';
  615. stream->ops = &php_stream_rfc2397_ops;
  616. ts = (php_stream_temp_data*)stream->abstract;
  617. assert(ts != NULL);
  618. ts->mode = mode && mode[0] == 'r' && mode[1] != '+' ? TEMP_STREAM_READONLY : 0;
  619. ts->meta = meta;
  620. }
  621. efree(comma);
  622. return stream;
  623. }
  624. PHPAPI php_stream_wrapper_ops php_stream_rfc2397_wops = {
  625. php_stream_url_wrap_rfc2397,
  626. NULL, /* close */
  627. NULL, /* fstat */
  628. NULL, /* stat */
  629. NULL, /* opendir */
  630. "RFC2397",
  631. NULL, /* unlink */
  632. NULL, /* rename */
  633. NULL, /* mkdir */
  634. NULL /* rmdir */
  635. };
  636. PHPAPI php_stream_wrapper php_stream_rfc2397_wrapper = {
  637. &php_stream_rfc2397_wops,
  638. NULL,
  639. 1, /* is_url */
  640. };
  641. /*
  642. * Local variables:
  643. * tab-width: 4
  644. * c-basic-offset: 4
  645. * End:
  646. * vim600: noet sw=4 ts=4 fdm=marker
  647. * vim<600: noet sw=4 ts=4
  648. */