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