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