root/main/streams/memory.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. php_stream_memory_write
  2. php_stream_memory_read
  3. php_stream_memory_close
  4. php_stream_memory_flush
  5. php_stream_memory_seek
  6. php_stream_memory_cast
  7. php_stream_memory_stat
  8. php_stream_memory_set_option
  9. _php_stream_memory_create
  10. _php_stream_memory_open
  11. _php_stream_memory_get_buffer
  12. php_stream_temp_write
  13. php_stream_temp_read
  14. php_stream_temp_close
  15. php_stream_temp_flush
  16. php_stream_temp_seek
  17. php_stream_temp_cast
  18. php_stream_temp_stat
  19. php_stream_temp_set_option
  20. _php_stream_temp_create_ex
  21. _php_stream_temp_create
  22. _php_stream_temp_open
  23. php_stream_url_wrap_rfc2397

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

/* [<][>][^][v][top][bottom][index][help] */