memory.c 20 KB

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