Back to index

php5  5.3.10
memory.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Author: Marcus Boerger <helly@php.net>                               |
00016    +----------------------------------------------------------------------+
00017  */
00018 
00019 /* $Id: memory.c 321634 2012-01-01 13:15:04Z felipe $ */
00020 
00021 #define _GNU_SOURCE
00022 #include "php.h"
00023 
00024 PHPAPI int php_url_decode(char *str, int len);
00025 PHPAPI unsigned char *php_base64_decode(const unsigned char *str, int length, int *ret_length);
00026 
00027 /* Memory streams use a dynamic memory buffer to emulate a stream.
00028  * You can use php_stream_memory_open to create a readonly stream
00029  * from an existing memory buffer.
00030  */
00031 
00032 /* Temp streams are streams that uses memory streams as long their
00033  * size is less than a given memory amount. When a write operation
00034  * exceeds that limit the content is written to a temporary file.
00035  */
00036 
00037 /* {{{ ------- MEMORY stream implementation -------*/
00038 
00039 typedef struct {
00040        char        *data;
00041        size_t      fpos;
00042        size_t      fsize;
00043        size_t      smax;
00044        int                  mode;
00045        php_stream  **owner_ptr;
00046 } php_stream_memory_data;
00047 
00048 
00049 /* {{{ */
00050 static size_t php_stream_memory_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
00051 {
00052        php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract;
00053        assert(ms != NULL);
00054 
00055        if (ms->mode & TEMP_STREAM_READONLY) {
00056               return 0;
00057        }
00058        if (ms->fpos + count > ms->fsize) {
00059               char *tmp;
00060 
00061               if (!ms->data) {
00062                      tmp = emalloc(ms->fpos + count);
00063               } else {
00064                      tmp = erealloc(ms->data, ms->fpos + count);
00065               }
00066               if (!tmp) {
00067                      count = ms->fsize - ms->fpos + 1;
00068               } else {
00069                      ms->data = tmp;
00070                      ms->fsize = ms->fpos + count;
00071               }
00072        }
00073        if (!ms->data)
00074               count = 0;
00075        if (count) {
00076               assert(buf!= NULL);
00077               memcpy(ms->data+ms->fpos, (char*)buf, count);
00078               ms->fpos += count;
00079        }
00080        return count;
00081 }
00082 /* }}} */
00083 
00084 
00085 /* {{{ */
00086 static size_t php_stream_memory_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
00087 {
00088        php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract;
00089        assert(ms != NULL);
00090 
00091        if (ms->fpos + count >= ms->fsize) {
00092               count = ms->fsize - ms->fpos;
00093               stream->eof = 1;
00094        }
00095        if (count) {
00096               assert(ms->data!= NULL);
00097               assert(buf!= NULL);
00098               memcpy(buf, ms->data+ms->fpos, count);
00099               ms->fpos += count;
00100        }
00101        return count;
00102 }
00103 /* }}} */
00104 
00105 
00106 /* {{{ */
00107 static int php_stream_memory_close(php_stream *stream, int close_handle TSRMLS_DC)
00108 {
00109        php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract;
00110        assert(ms != NULL);
00111 
00112        if (ms->data && close_handle && ms->mode != TEMP_STREAM_READONLY) {
00113               efree(ms->data);
00114        }
00115        if (ms->owner_ptr) {
00116               *ms->owner_ptr = NULL;
00117        }
00118        efree(ms);
00119        return 0;
00120 }
00121 /* }}} */
00122 
00123 
00124 /* {{{ */
00125 static int php_stream_memory_flush(php_stream *stream TSRMLS_DC)
00126 {
00127        /* nothing to do here */
00128        return 0;
00129 }
00130 /* }}} */
00131 
00132 
00133 /* {{{ */
00134 static int php_stream_memory_seek(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC)
00135 {
00136        php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract;
00137        assert(ms != NULL);
00138 
00139        switch(whence) {
00140               case SEEK_CUR:
00141                      if (offset < 0) {
00142                             if (ms->fpos < (size_t)(-offset)) {
00143                                    ms->fpos = 0;
00144                                    *newoffs = -1;
00145                                    return -1;
00146                             } else {
00147                                    ms->fpos = ms->fpos + offset;
00148                                    *newoffs = ms->fpos;
00149                                    stream->eof = 0;
00150                                    return 0;
00151                             }
00152                      } else {
00153                             if (ms->fpos + (size_t)(offset) > ms->fsize) {
00154                                    ms->fpos = ms->fsize;
00155                                    *newoffs = -1;
00156                                    return -1;
00157                             } else {
00158                                    ms->fpos = ms->fpos + offset;
00159                                    *newoffs = ms->fpos;
00160                                    stream->eof = 0;
00161                                    return 0;
00162                             }
00163                      }
00164               case SEEK_SET:
00165                      if (ms->fsize < (size_t)(offset)) {
00166                             ms->fpos = ms->fsize;
00167                             *newoffs = -1;
00168                             return -1;
00169                      } else {
00170                             ms->fpos = offset;
00171                             *newoffs = ms->fpos;
00172                             stream->eof = 0;
00173                             return 0;
00174                      }
00175               case SEEK_END:
00176                      if (offset > 0) {
00177                             ms->fpos = ms->fsize;
00178                             *newoffs = -1;
00179                             return -1;
00180                      } else if (ms->fsize < (size_t)(-offset)) {
00181                             ms->fpos = 0;
00182                             *newoffs = -1;
00183                             return -1;
00184                      } else {
00185                             ms->fpos = ms->fsize + offset;
00186                             *newoffs = ms->fpos;
00187                             stream->eof = 0;
00188                             return 0;
00189                      }
00190               default:
00191                      *newoffs = ms->fpos;
00192                      return -1;
00193        }
00194 }
00195 /* }}} */
00196 
00197 /* {{{ */
00198 static int php_stream_memory_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
00199 {
00200        return FAILURE;
00201 }
00202 /* }}} */
00203 
00204 static int php_stream_memory_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) /* {{{ */
00205 {
00206        time_t timestamp = 0;
00207        php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract;
00208        assert(ms != NULL);
00209 
00210        memset(ssb, 0, sizeof(php_stream_statbuf));
00211        /* read-only across the board */
00212        
00213        ssb->sb.st_mode = ms->mode & TEMP_STREAM_READONLY ? 0444 : 0666;
00214 
00215        ssb->sb.st_size = ms->fsize;
00216        ssb->sb.st_mode |= S_IFREG; /* regular file */
00217 
00218 #ifdef NETWARE
00219        ssb->sb.st_mtime.tv_sec = timestamp;
00220        ssb->sb.st_atime.tv_sec = timestamp;
00221        ssb->sb.st_ctime.tv_sec = timestamp;
00222 #else
00223        ssb->sb.st_mtime = timestamp;
00224        ssb->sb.st_atime = timestamp;
00225        ssb->sb.st_ctime = timestamp;
00226 #endif
00227 
00228        ssb->sb.st_nlink = 1;
00229        ssb->sb.st_rdev = -1;
00230        /* this is only for APC, so use /dev/null device - no chance of conflict there! */
00231        ssb->sb.st_dev = 0xC;
00232        /* generate unique inode number for alias/filename, so no phars will conflict */
00233        ssb->sb.st_ino = 0;
00234 
00235 #ifndef PHP_WIN32
00236        ssb->sb.st_blksize = -1;
00237 #endif
00238 
00239 #if !defined(PHP_WIN32) && !defined(__BEOS__)
00240        ssb->sb.st_blocks = -1;
00241 #endif
00242 
00243        return 0;
00244 }
00245 /* }}} */
00246 
00247 static int php_stream_memory_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) /* {{{ */
00248 {
00249        php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract;
00250        size_t newsize;
00251        
00252        switch(option) {
00253               case PHP_STREAM_OPTION_TRUNCATE_API:
00254                      switch (value) {
00255                             case PHP_STREAM_TRUNCATE_SUPPORTED:
00256                                    return PHP_STREAM_OPTION_RETURN_OK;
00257 
00258                             case PHP_STREAM_TRUNCATE_SET_SIZE:
00259                                    if (ms->mode & TEMP_STREAM_READONLY) {
00260                                           return PHP_STREAM_OPTION_RETURN_ERR;
00261                                    }
00262                                    newsize = *(size_t*)ptrparam;
00263                                    if (newsize <= ms->fsize) {
00264                                           if (newsize < ms->fpos) {
00265                                                  ms->fpos = newsize;
00266                                           }
00267                                    } else {
00268                                           ms->data = erealloc(ms->data, newsize);
00269                                           memset(ms->data+ms->fsize, 0, newsize - ms->fsize);
00270                                           ms->fsize = newsize;
00271                                    }
00272                                    ms->fsize = newsize;
00273                                    return PHP_STREAM_OPTION_RETURN_OK;
00274                      }
00275               default:
00276                      return PHP_STREAM_OPTION_RETURN_NOTIMPL;
00277        }
00278 }
00279 /* }}} */
00280        
00281 PHPAPI php_stream_ops       php_stream_memory_ops = {
00282        php_stream_memory_write, php_stream_memory_read,
00283        php_stream_memory_close, php_stream_memory_flush,
00284        "MEMORY",
00285        php_stream_memory_seek,
00286        php_stream_memory_cast,
00287        php_stream_memory_stat,
00288        php_stream_memory_set_option
00289 };
00290 
00291 
00292 /* {{{ */
00293 PHPAPI php_stream *_php_stream_memory_create(int mode STREAMS_DC TSRMLS_DC)
00294 {
00295        php_stream_memory_data *self;
00296        php_stream *stream;
00297 
00298        self = emalloc(sizeof(*self));
00299        self->data = NULL;
00300        self->fpos = 0;
00301        self->fsize = 0;
00302        self->smax = ~0u;
00303        self->mode = mode;
00304        self->owner_ptr = NULL;
00305        
00306        stream = php_stream_alloc_rel(&php_stream_memory_ops, self, 0, mode & TEMP_STREAM_READONLY ? "rb" : "w+b");
00307        stream->flags |= PHP_STREAM_FLAG_NO_BUFFER;
00308        return stream;
00309 }
00310 /* }}} */
00311 
00312 
00313 /* {{{ */
00314 PHPAPI php_stream *_php_stream_memory_open(int mode, char *buf, size_t length STREAMS_DC TSRMLS_DC)
00315 {
00316        php_stream *stream;
00317        php_stream_memory_data *ms;
00318 
00319        if ((stream = php_stream_memory_create_rel(mode)) != NULL) {
00320               ms = (php_stream_memory_data*)stream->abstract;
00321               
00322               if (mode == TEMP_STREAM_READONLY || mode == TEMP_STREAM_TAKE_BUFFER) {
00323                      /* use the buffer directly */
00324                      ms->data = buf;
00325                      ms->fsize = length;
00326               } else {
00327                      if (length) {
00328                             assert(buf != NULL);
00329                             php_stream_write(stream, buf, length);
00330                      }
00331               }
00332        }
00333        return stream;
00334 }
00335 /* }}} */
00336 
00337 
00338 /* {{{ */
00339 PHPAPI char *_php_stream_memory_get_buffer(php_stream *stream, size_t *length STREAMS_DC TSRMLS_DC)
00340 {
00341        php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract;
00342 
00343        assert(ms != NULL);
00344        assert(length != 0);
00345 
00346        *length = ms->fsize;
00347        return ms->data;
00348 }
00349 /* }}} */
00350 
00351 /* }}} */
00352 
00353 /* {{{ ------- TEMP stream implementation -------*/
00354 
00355 typedef struct {
00356        php_stream  *innerstream;
00357        size_t      smax;
00358        int                  mode;
00359        zval*       meta;
00360 } php_stream_temp_data;
00361 
00362 
00363 /* {{{ */
00364 static size_t php_stream_temp_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
00365 {
00366        php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
00367        assert(ts != NULL);
00368 
00369        if (!ts->innerstream) {
00370               return -1;
00371        }
00372        if (php_stream_is(ts->innerstream, PHP_STREAM_IS_MEMORY)) {
00373               size_t memsize;
00374               char *membuf = php_stream_memory_get_buffer(ts->innerstream, &memsize);
00375 
00376               if (memsize + count >= ts->smax) {
00377                      php_stream *file = php_stream_fopen_tmpfile();
00378                      php_stream_write(file, membuf, memsize);
00379                      php_stream_close(ts->innerstream);
00380                      ts->innerstream = file;
00381               }
00382        }
00383        return php_stream_write(ts->innerstream, buf, count);
00384 }
00385 /* }}} */
00386 
00387 
00388 /* {{{ */
00389 static size_t php_stream_temp_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
00390 {
00391        php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
00392        size_t got;
00393 
00394        assert(ts != NULL);
00395 
00396        if (!ts->innerstream) {
00397               return -1;
00398        }
00399        
00400        got = php_stream_read(ts->innerstream, buf, count);
00401        
00402        stream->eof = ts->innerstream->eof;
00403        
00404        return got;
00405 }
00406 /* }}} */
00407 
00408 
00409 /* {{{ */
00410 static int php_stream_temp_close(php_stream *stream, int close_handle TSRMLS_DC)
00411 {
00412        php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
00413        int ret;
00414 
00415        assert(ts != NULL);
00416 
00417        if (ts->innerstream) {
00418               ret = php_stream_free(ts->innerstream, PHP_STREAM_FREE_CLOSE | (close_handle ? 0 : PHP_STREAM_FREE_PRESERVE_HANDLE));
00419        } else {
00420               ret = 0;
00421        }
00422        
00423        if (ts->meta) {
00424               zval_ptr_dtor(&ts->meta);
00425        }
00426 
00427        efree(ts);
00428 
00429        return ret;
00430 }
00431 /* }}} */
00432 
00433 
00434 /* {{{ */
00435 static int php_stream_temp_flush(php_stream *stream TSRMLS_DC)
00436 {
00437        php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
00438        assert(ts != NULL);
00439 
00440        return ts->innerstream ? php_stream_flush(ts->innerstream) : -1;
00441 }
00442 /* }}} */
00443 
00444 
00445 /* {{{ */
00446 static int php_stream_temp_seek(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC)
00447 {
00448        php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
00449        int ret;
00450 
00451        assert(ts != NULL);
00452 
00453        if (!ts->innerstream) {
00454               *newoffs = -1;
00455               return -1;
00456        }
00457        ret = php_stream_seek(ts->innerstream, offset, whence);
00458        *newoffs = php_stream_tell(ts->innerstream);
00459        stream->eof = ts->innerstream->eof;
00460        
00461        return ret;
00462 }
00463 /* }}} */
00464 
00465 /* {{{ */
00466 static int php_stream_temp_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
00467 {
00468        php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
00469        php_stream *file;
00470        size_t memsize;
00471        char *membuf;
00472        off_t pos;
00473 
00474        assert(ts != NULL);
00475 
00476        if (!ts->innerstream) {
00477               return FAILURE;
00478        }
00479        if (php_stream_is(ts->innerstream, PHP_STREAM_IS_STDIO)) {
00480               return php_stream_cast(ts->innerstream, castas, ret, 0);
00481        }
00482 
00483        /* we are still using a memory based backing. If they are if we can be
00484         * a FILE*, say yes because we can perform the conversion.
00485         * If they actually want to perform the conversion, we need to switch
00486         * the memory stream to a tmpfile stream */
00487 
00488        if (ret == NULL && castas == PHP_STREAM_AS_STDIO) {
00489               return SUCCESS;
00490        }
00491 
00492        /* say "no" to other stream forms */
00493        if (ret == NULL) {
00494               return FAILURE;
00495        }
00496 
00497        /* perform the conversion and then pass the request on to the innerstream */
00498        membuf = php_stream_memory_get_buffer(ts->innerstream, &memsize);
00499        file = php_stream_fopen_tmpfile();
00500        php_stream_write(file, membuf, memsize);
00501        pos = php_stream_tell(ts->innerstream);
00502 
00503        php_stream_close(ts->innerstream);
00504        ts->innerstream = file;
00505        php_stream_seek(ts->innerstream, pos, SEEK_SET);
00506 
00507        return php_stream_cast(ts->innerstream, castas, ret, 1);
00508 }
00509 /* }}} */
00510 
00511 static int php_stream_temp_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) /* {{{ */
00512 {
00513        php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
00514 
00515        if (!ts || !ts->innerstream) {
00516               return -1;
00517        }
00518        return php_stream_stat(ts->innerstream, ssb);
00519 }
00520 /* }}} */
00521 
00522 static int php_stream_temp_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) /* {{{ */
00523 {
00524        php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
00525        
00526        switch(option) {
00527               case PHP_STREAM_OPTION_META_DATA_API:
00528                      if (ts->meta) {
00529                             zend_hash_copy(Z_ARRVAL_P((zval*)ptrparam), Z_ARRVAL_P(ts->meta), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval*));
00530                      }
00531                      return PHP_STREAM_OPTION_RETURN_OK;
00532               default:
00533                      if (ts->innerstream) {
00534                             return php_stream_set_option(ts->innerstream, option, value, ptrparam);
00535                      }
00536                      return PHP_STREAM_OPTION_RETURN_NOTIMPL;
00537        }
00538 }
00539 /* }}} */
00540 
00541 PHPAPI php_stream_ops       php_stream_temp_ops = {
00542        php_stream_temp_write, php_stream_temp_read,
00543        php_stream_temp_close, php_stream_temp_flush,
00544        "TEMP",
00545        php_stream_temp_seek,
00546        php_stream_temp_cast,
00547        php_stream_temp_stat,
00548        php_stream_temp_set_option
00549 };
00550 
00551 /* }}} */
00552 
00553 /* {{{ _php_stream_temp_create */
00554 PHPAPI php_stream *_php_stream_temp_create(int mode, size_t max_memory_usage STREAMS_DC TSRMLS_DC)
00555 {
00556        php_stream_temp_data *self;
00557        php_stream *stream;
00558 
00559        self = ecalloc(1, sizeof(*self));
00560        self->smax = max_memory_usage;
00561        self->mode = mode;
00562        self->meta = NULL;
00563        stream = php_stream_alloc_rel(&php_stream_temp_ops, self, 0, mode & TEMP_STREAM_READONLY ? "rb" : "w+b");
00564        stream->flags |= PHP_STREAM_FLAG_NO_BUFFER;
00565        self->innerstream = php_stream_memory_create_rel(mode);
00566        php_stream_auto_cleanup(self->innerstream); /* do not warn if innerstream is GC'ed before stream */
00567        ((php_stream_memory_data*)self->innerstream->abstract)->owner_ptr = &self->innerstream;
00568 
00569        return stream;
00570 }
00571 /* }}} */
00572 
00573 
00574 /* {{{ _php_stream_temp_open */
00575 PHPAPI php_stream *_php_stream_temp_open(int mode, size_t max_memory_usage, char *buf, size_t length STREAMS_DC TSRMLS_DC)
00576 {
00577        php_stream *stream;
00578        php_stream_temp_data *ts;
00579        off_t newoffs;
00580 
00581        if ((stream = php_stream_temp_create_rel(mode, max_memory_usage)) != NULL) {
00582               if (length) {
00583                      assert(buf != NULL);
00584                      php_stream_temp_write(stream, buf, length TSRMLS_CC);
00585                      php_stream_temp_seek(stream, 0, SEEK_SET, &newoffs TSRMLS_CC);
00586               }
00587               ts = (php_stream_temp_data*)stream->abstract;
00588               assert(ts != NULL);
00589               ts->mode = mode;
00590        }
00591        return stream;
00592 }
00593 /* }}} */
00594 
00595 PHPAPI php_stream_ops php_stream_rfc2397_ops = {
00596        php_stream_temp_write, php_stream_temp_read,
00597        php_stream_temp_close, php_stream_temp_flush,
00598        "RFC2397",
00599        php_stream_temp_seek,
00600        php_stream_temp_cast,
00601        php_stream_temp_stat,
00602        php_stream_temp_set_option
00603 };
00604 
00605 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) /* {{{ */
00606 {
00607        php_stream *stream;
00608        php_stream_temp_data *ts;
00609        char *comma, *semi, *sep, *key;
00610        size_t mlen, dlen, plen, vlen;
00611        off_t newoffs;
00612        zval *meta = NULL;
00613        int base64 = 0, ilen;
00614 
00615        if (memcmp(path, "data:", 5)) {
00616               return NULL;
00617        }
00618 
00619        path += 5;
00620        dlen = strlen(path);
00621 
00622        if (dlen >= 2 && path[0] == '/' && path[1] == '/') {
00623               dlen -= 2;
00624               path += 2;
00625        }
00626 
00627        if ((comma = memchr(path, ',', dlen)) == NULL) {
00628               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: no comma in URL");
00629               return NULL;
00630        }
00631 
00632        if (comma != path) {
00633               /* meta info */
00634               mlen = comma - path;
00635               dlen -= mlen;
00636               semi = memchr(path, ';', mlen);
00637               sep = memchr(path, '/', mlen);
00638               
00639               if (!semi && !sep) {
00640                      php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: illegal media type");
00641                      return NULL;
00642               }
00643 
00644               MAKE_STD_ZVAL(meta);
00645               array_init(meta);
00646               if (!semi) { /* there is only a mime type */
00647                      add_assoc_stringl(meta, "mediatype", path, mlen, 1);
00648                      mlen = 0;
00649               } else if (sep && sep < semi) { /* there is a mime type */
00650                      plen = semi - path;
00651                      add_assoc_stringl(meta, "mediatype", path, plen, 1);
00652                      mlen -= plen;
00653                      path += plen;
00654               } else if (semi != path || mlen != sizeof(";base64")-1 || memcmp(path, ";base64", sizeof(";base64")-1)) { /* must be error since parameters are only allowed after mediatype */
00655                      zval_ptr_dtor(&meta);
00656                      php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: illegal media type");
00657                      return NULL;
00658               }
00659               /* get parameters and potentially ';base64' */
00660               while(semi && (semi == path)) {
00661                      path++;
00662                      mlen--;
00663                      sep = memchr(path, '=', mlen);
00664                      semi = memchr(path, ';', mlen);
00665                      if (!sep || (semi && semi < sep)) { /* must be ';base64' or failure */
00666                             if (mlen != sizeof("base64")-1 || memcmp(path, "base64", sizeof("base64")-1)) {
00667                                    /* must be error since parameters are only allowed after mediatype and we have no '=' sign */
00668                                    zval_ptr_dtor(&meta);
00669                                    php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: illegal parameter");
00670                                    return NULL;
00671                             }
00672                             base64 = 1;
00673                             mlen -= sizeof("base64") - 1;
00674                             path += sizeof("base64") - 1;
00675                             break;
00676                      }
00677                      /* found parameter ... the heart of cs ppl lies in +1/-1 or was it +2 this time? */
00678                      plen = sep - path;
00679                      vlen = (semi ? semi - sep : mlen - plen) - 1 /* '=' */;
00680                      key = estrndup(path, plen);
00681                      add_assoc_stringl_ex(meta, key, plen + 1, sep + 1, vlen, 1);
00682                      efree(key);
00683                      plen += vlen + 1;
00684                      mlen -= plen;
00685                      path += plen;
00686               }
00687               if (mlen) {
00688                      zval_ptr_dtor(&meta);
00689                      php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: illegal URL");
00690                      return NULL;
00691               }
00692        } else {
00693               MAKE_STD_ZVAL(meta);
00694               array_init(meta);
00695        }
00696        add_assoc_bool(meta, "base64", base64);
00697 
00698        /* skip ',' */
00699        comma++;
00700        dlen--;
00701 
00702        if (base64) {
00703               comma = (char*)php_base64_decode((const unsigned char *)comma, dlen, &ilen);
00704               if (!comma) {
00705                      zval_ptr_dtor(&meta);
00706                      php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: unable to decode");
00707                      return NULL;
00708               }
00709        } else {
00710               comma = estrndup(comma, dlen);
00711               ilen = dlen = php_url_decode(comma, dlen);
00712        }
00713 
00714        if ((stream = php_stream_temp_create_rel(0, ~0u)) != NULL) {
00715               /* store data */
00716               php_stream_temp_write(stream, comma, ilen TSRMLS_CC);
00717               php_stream_temp_seek(stream, 0, SEEK_SET, &newoffs TSRMLS_CC);
00718               /* set special stream stuff (enforce exact mode) */
00719               vlen = strlen(mode);
00720               if (vlen >= sizeof(stream->mode)) {
00721                      vlen = sizeof(stream->mode) - 1;
00722               }
00723               memcpy(stream->mode, mode, vlen);
00724               stream->mode[vlen] = '\0';
00725               stream->ops = &php_stream_rfc2397_ops;
00726               ts = (php_stream_temp_data*)stream->abstract;
00727               assert(ts != NULL);
00728               ts->mode = mode && mode[0] == 'r' && mode[1] != '+' ? TEMP_STREAM_READONLY : 0;
00729               ts->meta = meta;
00730        }
00731        efree(comma);
00732 
00733        return stream;
00734 }
00735 
00736 PHPAPI php_stream_wrapper_ops php_stream_rfc2397_wops = {
00737        php_stream_url_wrap_rfc2397,
00738        NULL, /* close */
00739        NULL, /* fstat */
00740        NULL, /* stat */
00741        NULL, /* opendir */
00742        "RFC2397",
00743        NULL, /* unlink */
00744        NULL, /* rename */
00745        NULL, /* mkdir */
00746        NULL  /* rmdir */
00747 };
00748 
00749 PHPAPI php_stream_wrapper php_stream_rfc2397_wrapper =  {
00750        &php_stream_rfc2397_wops,
00751        NULL,
00752        1, /* is_url */
00753 };
00754 
00755 /*
00756  * Local variables:
00757  * tab-width: 4
00758  * c-basic-offset: 4
00759  * End:
00760  * vim600: noet sw=4 ts=4 fdm=marker
00761  * vim<600: noet sw=4 ts=4
00762  */