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 7                                                        |
   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 #include "ext/standard/base64.h"
  24 
  25 PHPAPI size_t php_url_decode(char *str, size_t len);
  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)
  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)
  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)
 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)
 126 {
 127         /* nothing to do here */
 128         return 0;
 129 }
 130 /* }}} */
 131 
 132 
 133 /* {{{ */
 134 static int php_stream_memory_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs)
 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)
 199 {
 200         return FAILURE;
 201 }
 202 /* }}} */
 203 
 204 static int php_stream_memory_stat(php_stream *stream, php_stream_statbuf *ssb) /* {{{ */
 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) /* {{{ */
 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)
 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)
 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)
 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)
 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, 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)
 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)
 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         zval_ptr_dtor(&ts->meta);
 429 
 430         if (ts->tmpdir) {
 431                 efree(ts->tmpdir);
 432         }
 433 
 434         efree(ts);
 435 
 436         return ret;
 437 }
 438 /* }}} */
 439 
 440 
 441 /* {{{ */
 442 static int php_stream_temp_flush(php_stream *stream)
 443 {
 444         php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
 445         assert(ts != NULL);
 446 
 447         return ts->innerstream ? php_stream_flush(ts->innerstream) : -1;
 448 }
 449 /* }}} */
 450 
 451 
 452 /* {{{ */
 453 static int php_stream_temp_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs)
 454 {
 455         php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
 456         int ret;
 457 
 458         assert(ts != NULL);
 459 
 460         if (!ts->innerstream) {
 461                 *newoffs = -1;
 462                 return -1;
 463         }
 464         ret = php_stream_seek(ts->innerstream, offset, whence);
 465         *newoffs = php_stream_tell(ts->innerstream);
 466         stream->eof = ts->innerstream->eof;
 467 
 468         return ret;
 469 }
 470 /* }}} */
 471 
 472 /* {{{ */
 473 static int php_stream_temp_cast(php_stream *stream, int castas, void **ret)
 474 {
 475         php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
 476         php_stream *file;
 477         size_t memsize;
 478         char *membuf;
 479         zend_off_t pos;
 480 
 481         assert(ts != NULL);
 482 
 483         if (!ts->innerstream) {
 484                 return FAILURE;
 485         }
 486         if (php_stream_is(ts->innerstream, PHP_STREAM_IS_STDIO)) {
 487                 return php_stream_cast(ts->innerstream, castas, ret, 0);
 488         }
 489 
 490         /* we are still using a memory based backing. If they are if we can be
 491          * a FILE*, say yes because we can perform the conversion.
 492          * If they actually want to perform the conversion, we need to switch
 493          * the memory stream to a tmpfile stream */
 494 
 495         if (ret == NULL && castas == PHP_STREAM_AS_STDIO) {
 496                 return SUCCESS;
 497         }
 498 
 499         /* say "no" to other stream forms */
 500         if (ret == NULL) {
 501                 return FAILURE;
 502         }
 503 
 504         /* perform the conversion and then pass the request on to the innerstream */
 505         membuf = php_stream_memory_get_buffer(ts->innerstream, &memsize);
 506         file = php_stream_fopen_tmpfile();
 507         php_stream_write(file, membuf, memsize);
 508         pos = php_stream_tell(ts->innerstream);
 509 
 510         php_stream_free_enclosed(ts->innerstream, PHP_STREAM_FREE_CLOSE);
 511         ts->innerstream = file;
 512         php_stream_encloses(stream, ts->innerstream);
 513         php_stream_seek(ts->innerstream, pos, SEEK_SET);
 514 
 515         return php_stream_cast(ts->innerstream, castas, ret, 1);
 516 }
 517 /* }}} */
 518 
 519 static int php_stream_temp_stat(php_stream *stream, php_stream_statbuf *ssb) /* {{{ */
 520 {
 521         php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
 522 
 523         if (!ts || !ts->innerstream) {
 524                 return -1;
 525         }
 526         return php_stream_stat(ts->innerstream, ssb);
 527 }
 528 /* }}} */
 529 
 530 static int php_stream_temp_set_option(php_stream *stream, int option, int value, void *ptrparam) /* {{{ */
 531 {
 532         php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
 533 
 534         switch(option) {
 535                 case PHP_STREAM_OPTION_META_DATA_API:
 536                         if (Z_TYPE(ts->meta) != IS_UNDEF) {
 537                                 zend_hash_copy(Z_ARRVAL_P((zval*)ptrparam), Z_ARRVAL(ts->meta), zval_add_ref);
 538                         }
 539                         return PHP_STREAM_OPTION_RETURN_OK;
 540                 default:
 541                         if (ts->innerstream) {
 542                                 return php_stream_set_option(ts->innerstream, option, value, ptrparam);
 543                         }
 544                         return PHP_STREAM_OPTION_RETURN_NOTIMPL;
 545         }
 546 }
 547 /* }}} */
 548 
 549 PHPAPI php_stream_ops   php_stream_temp_ops = {
 550         php_stream_temp_write, php_stream_temp_read,
 551         php_stream_temp_close, php_stream_temp_flush,
 552         "TEMP",
 553         php_stream_temp_seek,
 554         php_stream_temp_cast,
 555         php_stream_temp_stat,
 556         php_stream_temp_set_option
 557 };
 558 
 559 /* }}} */
 560 
 561 /* {{{ _php_stream_temp_create_ex */
 562 PHPAPI php_stream *_php_stream_temp_create_ex(int mode, size_t max_memory_usage, const char *tmpdir STREAMS_DC)
 563 {
 564         php_stream_temp_data *self;
 565         php_stream *stream;
 566 
 567         self = ecalloc(1, sizeof(*self));
 568         self->smax = max_memory_usage;
 569         self->mode = mode;
 570         ZVAL_UNDEF(&self->meta);
 571         if (tmpdir) {
 572                 self->tmpdir = estrdup(tmpdir);
 573         }
 574         stream = php_stream_alloc_rel(&php_stream_temp_ops, self, 0, mode & TEMP_STREAM_READONLY ? "rb" : "w+b");
 575         stream->flags |= PHP_STREAM_FLAG_NO_BUFFER;
 576         self->innerstream = php_stream_memory_create_rel(mode);
 577         php_stream_encloses(stream, self->innerstream);
 578 
 579         return stream;
 580 }
 581 /* }}} */
 582 
 583 /* {{{ _php_stream_temp_create */
 584 PHPAPI php_stream *_php_stream_temp_create(int mode, size_t max_memory_usage STREAMS_DC)
 585 {
 586         return php_stream_temp_create_ex(mode, max_memory_usage, NULL);
 587 }
 588 /* }}} */
 589 
 590 /* {{{ _php_stream_temp_open */
 591 PHPAPI php_stream *_php_stream_temp_open(int mode, size_t max_memory_usage, char *buf, size_t length STREAMS_DC)
 592 {
 593         php_stream *stream;
 594         php_stream_temp_data *ts;
 595         zend_off_t newoffs;
 596 
 597         if ((stream = php_stream_temp_create_rel(mode, max_memory_usage)) != NULL) {
 598                 if (length) {
 599                         assert(buf != NULL);
 600                         php_stream_temp_write(stream, buf, length);
 601                         php_stream_temp_seek(stream, 0, SEEK_SET, &newoffs);
 602                 }
 603                 ts = (php_stream_temp_data*)stream->abstract;
 604                 assert(ts != NULL);
 605                 ts->mode = mode;
 606         }
 607         return stream;
 608 }
 609 /* }}} */
 610 
 611 PHPAPI php_stream_ops php_stream_rfc2397_ops = {
 612         php_stream_temp_write, php_stream_temp_read,
 613         php_stream_temp_close, php_stream_temp_flush,
 614         "RFC2397",
 615         php_stream_temp_seek,
 616         php_stream_temp_cast,
 617         php_stream_temp_stat,
 618         php_stream_temp_set_option
 619 };
 620 
 621 static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, const char *path,
 622                                                                                                 const char *mode, int options, zend_string **opened_path,
 623                                                                                                 php_stream_context *context STREAMS_DC) /* {{{ */
 624 {
 625         php_stream *stream;
 626         php_stream_temp_data *ts;
 627         char *comma, *semi, *sep, *key;
 628         size_t mlen, dlen, plen, vlen;
 629         zend_off_t newoffs;
 630         zval meta;
 631         int base64 = 0, ilen;
 632         zend_string *base64_comma = NULL;
 633 
 634         ZVAL_NULL(&meta);
 635         if (memcmp(path, "data:", 5)) {
 636                 return NULL;
 637         }
 638 
 639         path += 5;
 640         dlen = strlen(path);
 641 
 642         if (dlen >= 2 && path[0] == '/' && path[1] == '/') {
 643                 dlen -= 2;
 644                 path += 2;
 645         }
 646 
 647         if ((comma = memchr(path, ',', dlen)) == NULL) {
 648                 php_stream_wrapper_log_error(wrapper, options, "rfc2397: no comma in URL");
 649                 return NULL;
 650         }
 651 
 652         if (comma != path) {
 653                 /* meta info */
 654                 mlen = comma - path;
 655                 dlen -= mlen;
 656                 semi = memchr(path, ';', mlen);
 657                 sep = memchr(path, '/', mlen);
 658 
 659                 if (!semi && !sep) {
 660                         php_stream_wrapper_log_error(wrapper, options, "rfc2397: illegal media type");
 661                         return NULL;
 662                 }
 663 
 664                 array_init(&meta);
 665                 if (!semi) { /* there is only a mime type */
 666                         add_assoc_stringl(&meta, "mediatype", (char *) path, mlen);
 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);
 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, "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, "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, sep + 1, vlen);
 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, "rfc2397: illegal URL");
 711                         return NULL;
 712                 }
 713         } else {
 714                 array_init(&meta);
 715         }
 716         add_assoc_bool(&meta, "base64", base64);
 717 
 718         /* skip ',' */
 719         comma++;
 720         dlen--;
 721 
 722         if (base64) {
 723                 base64_comma = php_base64_decode((const unsigned char *)comma, dlen);
 724                 if (!base64_comma) {
 725                         zval_ptr_dtor(&meta);
 726                         php_stream_wrapper_log_error(wrapper, options, "rfc2397: unable to decode");
 727                         return NULL;
 728                 }
 729                 comma = ZSTR_VAL(base64_comma);
 730                 ilen = (int)ZSTR_LEN(base64_comma);
 731         } else {
 732                 comma = estrndup(comma, dlen);
 733                 dlen = php_url_decode(comma, dlen);
 734                 ilen = (int)dlen;
 735         }
 736 
 737         if ((stream = php_stream_temp_create_rel(0, ~0u)) != NULL) {
 738                 /* store data */
 739                 php_stream_temp_write(stream, comma, ilen);
 740                 php_stream_temp_seek(stream, 0, SEEK_SET, &newoffs);
 741                 /* set special stream stuff (enforce exact mode) */
 742                 vlen = strlen(mode);
 743                 if (vlen >= sizeof(stream->mode)) {
 744                         vlen = sizeof(stream->mode) - 1;
 745                 }
 746                 memcpy(stream->mode, mode, vlen);
 747                 stream->mode[vlen] = '\0';
 748                 stream->ops = &php_stream_rfc2397_ops;
 749                 ts = (php_stream_temp_data*)stream->abstract;
 750                 assert(ts != NULL);
 751                 ts->mode = mode && mode[0] == 'r' && mode[1] != '+' ? TEMP_STREAM_READONLY : 0;
 752                 ZVAL_COPY_VALUE(&ts->meta, &meta);
 753         }
 754         if (base64_comma) {
 755                 zend_string_free(base64_comma);
 756         } else {
 757                 efree(comma);
 758         }
 759 
 760         return stream;
 761 }
 762 
 763 PHPAPI php_stream_wrapper_ops php_stream_rfc2397_wops = {
 764         php_stream_url_wrap_rfc2397,
 765         NULL, /* close */
 766         NULL, /* fstat */
 767         NULL, /* stat */
 768         NULL, /* opendir */
 769         "RFC2397",
 770         NULL, /* unlink */
 771         NULL, /* rename */
 772         NULL, /* mkdir */
 773         NULL  /* rmdir */
 774 };
 775 
 776 PHPAPI php_stream_wrapper php_stream_rfc2397_wrapper =  {
 777         &php_stream_rfc2397_wops,
 778         NULL,
 779         1, /* is_url */
 780 };
 781 
 782 /*
 783  * Local variables:
 784  * tab-width: 4
 785  * c-basic-offset: 4
 786  * End:
 787  * vim600: noet sw=4 ts=4 fdm=marker
 788  * vim<600: noet sw=4 ts=4
 789  */

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