Back to index

php5  5.3.10
filters.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    | Authors:                                                             |
00016    | Wez Furlong (wez@thebrainroom.com)                                   |
00017    | Sara Golemon (pollita@php.net)                                       |
00018    | Moriyoshi Koizumi (moriyoshi@php.net)                                |
00019    | Marcus Boerger (helly@php.net)                                       |
00020    +----------------------------------------------------------------------+
00021 */
00022 
00023 /* $Id: filters.c 321634 2012-01-01 13:15:04Z felipe $ */
00024 
00025 #include "php.h"
00026 #include "php_globals.h"
00027 #include "ext/standard/basic_functions.h"
00028 #include "ext/standard/file.h"
00029 #include "ext/standard/php_string.h"
00030 #include "ext/standard/php_smart_str.h"
00031 
00032 /* {{{ rot13 stream filter implementation */
00033 static char rot13_from[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
00034 static char rot13_to[] = "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM";
00035 
00036 static php_stream_filter_status_t strfilter_rot13_filter(
00037        php_stream *stream,
00038        php_stream_filter *thisfilter,
00039        php_stream_bucket_brigade *buckets_in,
00040        php_stream_bucket_brigade *buckets_out,
00041        size_t *bytes_consumed,
00042        int flags
00043        TSRMLS_DC)
00044 {
00045        php_stream_bucket *bucket;
00046        size_t consumed = 0;
00047 
00048        while (buckets_in->head) {
00049               bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC);
00050               
00051               php_strtr(bucket->buf, bucket->buflen, rot13_from, rot13_to, 52);
00052               consumed += bucket->buflen;
00053               
00054               php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
00055        }
00056 
00057        if (bytes_consumed) {
00058               *bytes_consumed = consumed;
00059        }
00060        
00061        return PSFS_PASS_ON;
00062 }
00063 
00064 static php_stream_filter_ops strfilter_rot13_ops = {
00065        strfilter_rot13_filter,
00066        NULL,
00067        "string.rot13"
00068 };
00069 
00070 static php_stream_filter *strfilter_rot13_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
00071 {
00072        return php_stream_filter_alloc(&strfilter_rot13_ops, NULL, persistent);
00073 }
00074 
00075 static php_stream_filter_factory strfilter_rot13_factory = {
00076        strfilter_rot13_create
00077 };
00078 /* }}} */
00079 
00080 /* {{{ string.toupper / string.tolower stream filter implementation */
00081 static char lowercase[] = "abcdefghijklmnopqrstuvwxyz";
00082 static char uppercase[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
00083 
00084 static php_stream_filter_status_t strfilter_toupper_filter(
00085        php_stream *stream,
00086        php_stream_filter *thisfilter,
00087        php_stream_bucket_brigade *buckets_in,
00088        php_stream_bucket_brigade *buckets_out,
00089        size_t *bytes_consumed,
00090        int flags
00091        TSRMLS_DC)
00092 {
00093        php_stream_bucket *bucket;
00094        size_t consumed = 0;
00095 
00096        while (buckets_in->head) {
00097               bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC);
00098               
00099               php_strtr(bucket->buf, bucket->buflen, lowercase, uppercase, 26);
00100               consumed += bucket->buflen;
00101               
00102               php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
00103        }
00104 
00105        if (bytes_consumed) {
00106               *bytes_consumed = consumed;
00107        }
00108 
00109        return PSFS_PASS_ON;
00110 }
00111 
00112 static php_stream_filter_status_t strfilter_tolower_filter(
00113        php_stream *stream,
00114        php_stream_filter *thisfilter,
00115        php_stream_bucket_brigade *buckets_in,
00116        php_stream_bucket_brigade *buckets_out,
00117        size_t *bytes_consumed,
00118        int flags
00119        TSRMLS_DC)
00120 {
00121        php_stream_bucket *bucket;
00122        size_t consumed = 0;
00123 
00124        while (buckets_in->head) {
00125               bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC);
00126               
00127               php_strtr(bucket->buf, bucket->buflen, uppercase, lowercase, 26);
00128               consumed += bucket->buflen;
00129               
00130               php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
00131        }
00132 
00133        if (bytes_consumed) {
00134               *bytes_consumed = consumed;
00135        }
00136 
00137        return PSFS_PASS_ON;
00138 }
00139 
00140 static php_stream_filter_ops strfilter_toupper_ops = {
00141        strfilter_toupper_filter,
00142        NULL,
00143        "string.toupper"
00144 };
00145 
00146 static php_stream_filter_ops strfilter_tolower_ops = {
00147        strfilter_tolower_filter,
00148        NULL,
00149        "string.tolower"
00150 };
00151 
00152 static php_stream_filter *strfilter_toupper_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
00153 {
00154        return php_stream_filter_alloc(&strfilter_toupper_ops, NULL, persistent);
00155 }
00156 
00157 static php_stream_filter *strfilter_tolower_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
00158 {
00159        return php_stream_filter_alloc(&strfilter_tolower_ops, NULL, persistent);
00160 }
00161 
00162 static php_stream_filter_factory strfilter_toupper_factory = {
00163        strfilter_toupper_create
00164 };
00165 
00166 static php_stream_filter_factory strfilter_tolower_factory = {
00167        strfilter_tolower_create
00168 };
00169 /* }}} */
00170 
00171 /* {{{ strip_tags filter implementation */
00172 typedef struct _php_strip_tags_filter {
00173        const char *allowed_tags;
00174        int allowed_tags_len;
00175        int state;
00176        int persistent;
00177 } php_strip_tags_filter;
00178 
00179 static int php_strip_tags_filter_ctor(php_strip_tags_filter *inst, const char *allowed_tags, int allowed_tags_len, int persistent)
00180 {
00181        if (allowed_tags != NULL) {
00182               if (NULL == (inst->allowed_tags = pemalloc(allowed_tags_len, persistent))) {
00183                      return FAILURE;
00184               }
00185               memcpy((char *)inst->allowed_tags, allowed_tags, allowed_tags_len);
00186               inst->allowed_tags_len = allowed_tags_len; 
00187        } else {
00188               inst->allowed_tags = NULL;
00189        }
00190        inst->state = 0;
00191        inst->persistent = persistent;
00192 
00193        return SUCCESS;
00194 }      
00195 
00196 static void php_strip_tags_filter_dtor(php_strip_tags_filter *inst)
00197 {
00198        if (inst->allowed_tags != NULL) {
00199               pefree((void *)inst->allowed_tags, inst->persistent);
00200        }
00201 }
00202 
00203 static php_stream_filter_status_t strfilter_strip_tags_filter(
00204        php_stream *stream,
00205        php_stream_filter *thisfilter,
00206        php_stream_bucket_brigade *buckets_in,
00207        php_stream_bucket_brigade *buckets_out,
00208        size_t *bytes_consumed,
00209        int flags
00210        TSRMLS_DC)
00211 {
00212        php_stream_bucket *bucket;
00213        size_t consumed = 0;
00214        php_strip_tags_filter *inst = (php_strip_tags_filter *) thisfilter->abstract;
00215 
00216        while (buckets_in->head) {
00217               bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC);
00218               consumed = bucket->buflen;
00219               
00220               bucket->buflen = php_strip_tags(bucket->buf, bucket->buflen, &(inst->state), (char *)inst->allowed_tags, inst->allowed_tags_len);
00221        
00222               php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
00223        }
00224 
00225        if (bytes_consumed) {
00226               *bytes_consumed = consumed;
00227        }
00228        
00229        return PSFS_PASS_ON;
00230 }
00231 
00232 static void strfilter_strip_tags_dtor(php_stream_filter *thisfilter TSRMLS_DC)
00233 {
00234        assert(thisfilter->abstract != NULL);
00235 
00236        php_strip_tags_filter_dtor((php_strip_tags_filter *)thisfilter->abstract);
00237 
00238        pefree(thisfilter->abstract, ((php_strip_tags_filter *)thisfilter->abstract)->persistent);
00239 }
00240 
00241 static php_stream_filter_ops strfilter_strip_tags_ops = {
00242        strfilter_strip_tags_filter,
00243        strfilter_strip_tags_dtor,
00244        "string.strip_tags"
00245 };
00246 
00247 static php_stream_filter *strfilter_strip_tags_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
00248 {
00249        php_strip_tags_filter *inst;
00250        smart_str tags_ss = { 0, 0, 0 };
00251        
00252        inst = pemalloc(sizeof(php_strip_tags_filter), persistent);
00253 
00254        if (inst == NULL) { /* it's possible pemalloc returns NULL
00255                                              instead of causing it to bail out */
00256               return NULL;
00257        }
00258        
00259        if (filterparams != NULL) {
00260               if (Z_TYPE_P(filterparams) == IS_ARRAY) {
00261                      HashPosition pos;
00262                      zval **tmp;
00263 
00264                      zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(filterparams), &pos);
00265                      while (zend_hash_get_current_data_ex(Z_ARRVAL_P(filterparams), (void **) &tmp, &pos) == SUCCESS) {
00266                             convert_to_string_ex(tmp);
00267                             smart_str_appendc(&tags_ss, '<');
00268                             smart_str_appendl(&tags_ss, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
00269                             smart_str_appendc(&tags_ss, '>');
00270                             zend_hash_move_forward_ex(Z_ARRVAL_P(filterparams), &pos);
00271                      }
00272                      smart_str_0(&tags_ss);
00273               } else {
00274                      /* FIXME: convert_to_* may clutter zvals and lead it into segfault ? */
00275                      convert_to_string_ex(&filterparams);
00276 
00277                      tags_ss.c = Z_STRVAL_P(filterparams);
00278                      tags_ss.len = Z_STRLEN_P(filterparams);
00279                      tags_ss.a = 0;
00280               }
00281        }
00282 
00283        if (php_strip_tags_filter_ctor(inst, tags_ss.c, tags_ss.len, persistent) != SUCCESS) {
00284               if (tags_ss.a != 0) {
00285                      STR_FREE(tags_ss.c);
00286               }
00287               pefree(inst, persistent);
00288               return NULL;
00289        }
00290 
00291        if (tags_ss.a != 0) {
00292               STR_FREE(tags_ss.c);
00293        }
00294 
00295        return php_stream_filter_alloc(&strfilter_strip_tags_ops, inst, persistent);
00296 }
00297 
00298 static php_stream_filter_factory strfilter_strip_tags_factory = {
00299        strfilter_strip_tags_create
00300 };
00301 
00302 /* }}} */
00303 
00304 /* {{{ base64 / quoted_printable stream filter implementation */
00305 
00306 typedef enum _php_conv_err_t {
00307        PHP_CONV_ERR_SUCCESS = SUCCESS,
00308        PHP_CONV_ERR_UNKNOWN,
00309        PHP_CONV_ERR_TOO_BIG,
00310        PHP_CONV_ERR_INVALID_SEQ,
00311        PHP_CONV_ERR_UNEXPECTED_EOS,
00312        PHP_CONV_ERR_EXISTS,
00313        PHP_CONV_ERR_MORE,
00314        PHP_CONV_ERR_ALLOC,
00315        PHP_CONV_ERR_NOT_FOUND
00316 } php_conv_err_t;
00317 
00318 typedef struct _php_conv php_conv;
00319 
00320 typedef php_conv_err_t (*php_conv_convert_func)(php_conv *, const char **, size_t *, char **, size_t *);
00321 typedef void (*php_conv_dtor_func)(php_conv *);
00322 
00323 struct _php_conv {
00324        php_conv_convert_func convert_op;
00325        php_conv_dtor_func dtor;
00326 };
00327 
00328 #define php_conv_convert(a, b, c, d, e) ((php_conv *)(a))->convert_op((php_conv *)(a), (b), (c), (d), (e))
00329 #define php_conv_dtor(a) ((php_conv *)a)->dtor((a))
00330 
00331 /* {{{ php_conv_base64_encode */
00332 typedef struct _php_conv_base64_encode {
00333        php_conv _super;
00334 
00335        unsigned char erem[3];
00336        size_t erem_len;
00337        unsigned int line_ccnt;
00338        unsigned int line_len;
00339        const char *lbchars;
00340        int lbchars_dup;
00341        size_t lbchars_len;
00342        int persistent;
00343 } php_conv_base64_encode;
00344 
00345 static php_conv_err_t php_conv_base64_encode_convert(php_conv_base64_encode *inst, const char **in_p, size_t *in_left, char **out_p, size_t *out_left);
00346 static void php_conv_base64_encode_dtor(php_conv_base64_encode *inst);
00347 
00348 static unsigned char b64_tbl_enc[256] = {
00349        'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
00350        'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
00351        'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
00352        'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/',
00353        'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
00354        'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
00355        'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
00356        'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/',
00357        'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
00358        'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
00359        'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
00360        'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/',
00361        'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
00362        'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
00363        'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
00364        'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
00365 };
00366 
00367 static php_conv_err_t php_conv_base64_encode_ctor(php_conv_base64_encode *inst, unsigned int line_len, const char *lbchars, size_t lbchars_len, int lbchars_dup, int persistent)
00368 {
00369        inst->_super.convert_op = (php_conv_convert_func) php_conv_base64_encode_convert;
00370        inst->_super.dtor = (php_conv_dtor_func) php_conv_base64_encode_dtor;
00371        inst->erem_len = 0;
00372        inst->line_ccnt = line_len;
00373        inst->line_len = line_len;
00374        if (lbchars != NULL) {
00375               inst->lbchars = (lbchars_dup ? pestrdup(lbchars, persistent) : lbchars);
00376               inst->lbchars_len = lbchars_len;
00377        } else {
00378               inst->lbchars = NULL;
00379        }
00380        inst->lbchars_dup = lbchars_dup;
00381        inst->persistent = persistent;
00382        return PHP_CONV_ERR_SUCCESS;
00383 }
00384 
00385 static void php_conv_base64_encode_dtor(php_conv_base64_encode *inst)
00386 {
00387        assert(inst != NULL);
00388        if (inst->lbchars_dup && inst->lbchars != NULL) {
00389               pefree((void *)inst->lbchars, inst->persistent);
00390        }
00391 }
00392 
00393 static php_conv_err_t php_conv_base64_encode_flush(php_conv_base64_encode *inst, const char **in_pp, size_t *in_left_p, char **out_pp, size_t *out_left_p)
00394 {
00395        volatile php_conv_err_t err = PHP_CONV_ERR_SUCCESS;
00396        register unsigned char *pd;
00397        register size_t ocnt;
00398        unsigned int line_ccnt;
00399 
00400        pd = (unsigned char *)(*out_pp);
00401        ocnt = *out_left_p;
00402        line_ccnt = inst->line_ccnt;
00403 
00404        switch (inst->erem_len) {
00405               case 0:
00406                      /* do nothing */
00407                      break;
00408 
00409               case 1:
00410                      if (line_ccnt < 4 && inst->lbchars != NULL) {
00411                             if (ocnt < inst->lbchars_len) {
00412                                    return PHP_CONV_ERR_TOO_BIG;
00413                             }
00414                             memcpy(pd, inst->lbchars, inst->lbchars_len);
00415                             pd += inst->lbchars_len;
00416                             ocnt -= inst->lbchars_len;
00417                             line_ccnt = inst->line_len;
00418                      }
00419                      if (ocnt < 4) {
00420                             err = PHP_CONV_ERR_TOO_BIG;
00421                             goto out;
00422                      }
00423                      *(pd++) = b64_tbl_enc[(inst->erem[0] >> 2)];
00424                      *(pd++) = b64_tbl_enc[(unsigned char)(inst->erem[0] << 4)];
00425                      *(pd++) = '=';
00426                      *(pd++) = '=';
00427                      inst->erem_len = 0;
00428                      ocnt -= 4;
00429                      line_ccnt -= 4;
00430                      break;
00431 
00432               case 2: 
00433                      if (line_ccnt < 4 && inst->lbchars != NULL) {
00434                             if (ocnt < inst->lbchars_len) {
00435                                    return PHP_CONV_ERR_TOO_BIG;
00436                             }
00437                             memcpy(pd, inst->lbchars, inst->lbchars_len);
00438                             pd += inst->lbchars_len;
00439                             ocnt -= inst->lbchars_len;
00440                             line_ccnt = inst->line_len;
00441                      }
00442                      if (ocnt < 4) {
00443                             err = PHP_CONV_ERR_TOO_BIG;
00444                             goto out;
00445                      }
00446                      *(pd++) = b64_tbl_enc[(inst->erem[0] >> 2)];
00447                      *(pd++) = b64_tbl_enc[(unsigned char)(inst->erem[0] << 4) | (inst->erem[1] >> 4)];
00448                      *(pd++) = b64_tbl_enc[(unsigned char)(inst->erem[1] << 2)];
00449                      *(pd++) = '=';
00450                      inst->erem_len = 0;
00451                      ocnt -=4;
00452                      line_ccnt -= 4;
00453                      break;
00454 
00455               default:
00456                      /* should not happen... */
00457                      err = PHP_CONV_ERR_UNKNOWN;
00458                      break;
00459        }
00460 out:
00461        *out_pp = (char *)pd;
00462        *out_left_p = ocnt;
00463        inst->line_ccnt = line_ccnt;
00464        return err;
00465 }
00466 
00467 static php_conv_err_t php_conv_base64_encode_convert(php_conv_base64_encode *inst, const char **in_pp, size_t *in_left_p, char **out_pp, size_t *out_left_p)
00468 {
00469        volatile php_conv_err_t err = PHP_CONV_ERR_SUCCESS;
00470        register size_t ocnt, icnt;
00471        register unsigned char *ps, *pd;
00472        register unsigned int line_ccnt;
00473 
00474        if (in_pp == NULL || in_left_p == NULL) { 
00475               return php_conv_base64_encode_flush(inst, in_pp, in_left_p, out_pp, out_left_p);
00476        }
00477 
00478        pd = (unsigned char *)(*out_pp);
00479        ocnt = *out_left_p;
00480        ps = (unsigned char *)(*in_pp);
00481        icnt = *in_left_p;
00482        line_ccnt = inst->line_ccnt;
00483 
00484        /* consume the remainder first */
00485        switch (inst->erem_len) {
00486               case 1:
00487                      if (icnt >= 2) {
00488                             if (line_ccnt < 4 && inst->lbchars != NULL) {
00489                                    if (ocnt < inst->lbchars_len) {
00490                                           return PHP_CONV_ERR_TOO_BIG;
00491                                    }
00492                                    memcpy(pd, inst->lbchars, inst->lbchars_len);
00493                                    pd += inst->lbchars_len;
00494                                    ocnt -= inst->lbchars_len;
00495                                    line_ccnt = inst->line_len;
00496                             }
00497                             if (ocnt < 4) {
00498                                    err = PHP_CONV_ERR_TOO_BIG;
00499                                    goto out;
00500                             }
00501                             *(pd++) = b64_tbl_enc[(inst->erem[0] >> 2)];
00502                             *(pd++) = b64_tbl_enc[(unsigned char)(inst->erem[0] << 4) | (ps[0] >> 4)];
00503                             *(pd++) = b64_tbl_enc[(unsigned char)(ps[0] << 2) | (ps[1] >> 6)];
00504                             *(pd++) = b64_tbl_enc[ps[1]];
00505                             ocnt -= 4;
00506                             ps += 2;
00507                             icnt -= 2;
00508                             inst->erem_len = 0;
00509                             line_ccnt -= 4;
00510                      }
00511                      break;
00512 
00513               case 2: 
00514                      if (icnt >= 1) {
00515                             if (inst->line_ccnt < 4 && inst->lbchars != NULL) {
00516                                    if (ocnt < inst->lbchars_len) {
00517                                           return PHP_CONV_ERR_TOO_BIG;
00518                                    }
00519                                    memcpy(pd, inst->lbchars, inst->lbchars_len);
00520                                    pd += inst->lbchars_len;
00521                                    ocnt -= inst->lbchars_len;
00522                                    line_ccnt = inst->line_len;
00523                             }
00524                             if (ocnt < 4) {
00525                                    err = PHP_CONV_ERR_TOO_BIG;
00526                                    goto out;
00527                             }
00528                             *(pd++) = b64_tbl_enc[(inst->erem[0] >> 2)];
00529                             *(pd++) = b64_tbl_enc[(unsigned char)(inst->erem[0] << 4) | (inst->erem[1] >> 4)];
00530                             *(pd++) = b64_tbl_enc[(unsigned char)(inst->erem[1] << 2) | (ps[0] >> 6)];
00531                             *(pd++) = b64_tbl_enc[ps[0]];
00532                             ocnt -= 4;
00533                             ps += 1;
00534                             icnt -= 1;
00535                             inst->erem_len = 0;
00536                             line_ccnt -= 4;
00537                      }
00538                      break;
00539        }
00540 
00541        while (icnt >= 3) {
00542               if (line_ccnt < 4 && inst->lbchars != NULL) {
00543                      if (ocnt < inst->lbchars_len) {
00544                             err = PHP_CONV_ERR_TOO_BIG;
00545                             goto out;
00546                      }
00547                      memcpy(pd, inst->lbchars, inst->lbchars_len);
00548                      pd += inst->lbchars_len;
00549                      ocnt -= inst->lbchars_len;
00550                      line_ccnt = inst->line_len;
00551               }
00552               if (ocnt < 4) {
00553                      err = PHP_CONV_ERR_TOO_BIG;
00554                      goto out;
00555               }
00556               *(pd++) = b64_tbl_enc[ps[0] >> 2];
00557               *(pd++) = b64_tbl_enc[(unsigned char)(ps[0] << 4) | (ps[1] >> 4)];
00558               *(pd++) = b64_tbl_enc[(unsigned char)(ps[1] << 2) | (ps[2] >> 6)];
00559               *(pd++) = b64_tbl_enc[ps[2]];
00560 
00561               ps += 3;
00562               icnt -=3;
00563               ocnt -= 4;
00564               line_ccnt -= 4;
00565        }
00566        for (;icnt > 0; icnt--) {
00567               inst->erem[inst->erem_len++] = *(ps++);
00568        }
00569 
00570 out:
00571        *in_pp = (const char *)ps;
00572        *in_left_p = icnt;
00573        *out_pp = (char *)pd;
00574        *out_left_p = ocnt;
00575        inst->line_ccnt = line_ccnt;
00576 
00577        return err;
00578 }
00579 
00580 /* }}} */
00581 
00582 /* {{{ php_conv_base64_decode */
00583 typedef struct _php_conv_base64_decode {
00584        php_conv _super;
00585 
00586        unsigned int urem;
00587        unsigned int urem_nbits;
00588        unsigned int ustat;
00589        int eos;
00590 } php_conv_base64_decode;
00591 
00592 static php_conv_err_t php_conv_base64_decode_convert(php_conv_base64_decode *inst, const char **in_p, size_t *in_left, char **out_p, size_t *out_left);
00593 static void php_conv_base64_decode_dtor(php_conv_base64_decode *inst);
00594 
00595 static unsigned int b64_tbl_dec[256] = {
00596        64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
00597        64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
00598        64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
00599        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64,128, 64, 64,
00600        64,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
00601        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
00602        64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
00603        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
00604        64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
00605        64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
00606        64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
00607        64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
00608        64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
00609        64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
00610        64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
00611        64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
00612 };
00613 
00614 static int php_conv_base64_decode_ctor(php_conv_base64_decode *inst)
00615 {
00616        inst->_super.convert_op = (php_conv_convert_func) php_conv_base64_decode_convert;
00617        inst->_super.dtor = (php_conv_dtor_func) php_conv_base64_decode_dtor;
00618 
00619        inst->urem = 0;
00620        inst->urem_nbits = 0;
00621        inst->ustat = 0;
00622        inst->eos = 0;
00623        return SUCCESS;
00624 }
00625 
00626 static void php_conv_base64_decode_dtor(php_conv_base64_decode *inst)
00627 {
00628        /* do nothing */
00629 }
00630 
00631 #define bmask(a) (0xffff >> (16 - a))
00632 static php_conv_err_t php_conv_base64_decode_convert(php_conv_base64_decode *inst, const char **in_pp, size_t *in_left_p, char **out_pp, size_t *out_left_p)
00633 {
00634        php_conv_err_t err;
00635 
00636        unsigned int urem, urem_nbits;
00637        unsigned int pack, pack_bcnt;
00638        unsigned char *ps, *pd;
00639        size_t icnt, ocnt;
00640        unsigned int ustat;
00641 
00642        static const unsigned int nbitsof_pack = 8;
00643 
00644        if (in_pp == NULL || in_left_p == NULL) {
00645               if (inst->eos || inst->urem_nbits == 0) { 
00646                      return PHP_CONV_ERR_SUCCESS;
00647               }
00648               return PHP_CONV_ERR_UNEXPECTED_EOS;
00649        }
00650 
00651        err = PHP_CONV_ERR_SUCCESS;
00652 
00653        ps = (unsigned char *)*in_pp;
00654        pd = (unsigned char *)*out_pp;
00655        icnt = *in_left_p;
00656        ocnt = *out_left_p;
00657 
00658        urem = inst->urem;
00659        urem_nbits = inst->urem_nbits;
00660        ustat = inst->ustat;
00661 
00662        pack = 0;
00663        pack_bcnt = nbitsof_pack;
00664 
00665        for (;;) {
00666               if (pack_bcnt >= urem_nbits) {
00667                      pack_bcnt -= urem_nbits;
00668                      pack |= (urem << pack_bcnt);
00669                      urem_nbits = 0;
00670               } else {
00671                      urem_nbits -= pack_bcnt;
00672                      pack |= (urem >> urem_nbits);
00673                      urem &= bmask(urem_nbits);
00674                      pack_bcnt = 0;
00675               }
00676               if (pack_bcnt > 0) {
00677                      unsigned int i;
00678 
00679                      if (icnt < 1) {
00680                             break;
00681                      }
00682 
00683                      i = b64_tbl_dec[(unsigned int)*(ps++)];
00684                      icnt--;
00685                      ustat |= i & 0x80;
00686 
00687                      if (!(i & 0xc0)) {
00688                             if (ustat) {
00689                                    err = PHP_CONV_ERR_INVALID_SEQ;
00690                                    break;
00691                             }
00692                             if (6 <= pack_bcnt) {
00693                                    pack_bcnt -= 6;
00694                                    pack |= (i << pack_bcnt);
00695                                    urem = 0;
00696                             } else {
00697                                    urem_nbits = 6 - pack_bcnt;
00698                                    pack |= (i >> urem_nbits);
00699                                    urem = i & bmask(urem_nbits);
00700                                    pack_bcnt = 0;
00701                             }
00702                      } else if (ustat) {
00703                             if (pack_bcnt == 8 || pack_bcnt == 2) {
00704                                    err = PHP_CONV_ERR_INVALID_SEQ;
00705                                    break;
00706                             }
00707                             inst->eos = 1;
00708                      }
00709               }
00710               if ((pack_bcnt | ustat) == 0) {
00711                      if (ocnt < 1) {
00712                             err = PHP_CONV_ERR_TOO_BIG;
00713                             break;
00714                      }
00715                      *(pd++) = pack;
00716                      ocnt--;
00717                      pack = 0;
00718                      pack_bcnt = nbitsof_pack;
00719               }
00720        }
00721 
00722        if (urem_nbits >= pack_bcnt) {
00723               urem |= (pack << (urem_nbits - pack_bcnt));
00724               urem_nbits += (nbitsof_pack - pack_bcnt);
00725        } else {
00726               urem |= (pack >> (pack_bcnt - urem_nbits));
00727               urem_nbits += (nbitsof_pack - pack_bcnt);
00728        }
00729 
00730        inst->urem = urem;
00731        inst->urem_nbits = urem_nbits;
00732        inst->ustat = ustat;
00733 
00734        *in_pp = (const char *)ps;
00735        *in_left_p = icnt;
00736        *out_pp = (char *)pd;
00737        *out_left_p = ocnt;
00738 
00739        return err;
00740 }
00741 #undef bmask
00742 /* }}} */
00743 
00744 /* {{{ php_conv_qprint_encode */
00745 typedef struct _php_conv_qprint_encode {
00746        php_conv _super;
00747 
00748        int opts;
00749        unsigned int line_ccnt;
00750        unsigned int line_len;
00751        const char *lbchars;
00752        int lbchars_dup;
00753        size_t lbchars_len;
00754        int persistent;
00755        unsigned int lb_ptr;
00756        unsigned int lb_cnt;
00757 } php_conv_qprint_encode;
00758 
00759 #define PHP_CONV_QPRINT_OPT_BINARY             0x00000001
00760 #define PHP_CONV_QPRINT_OPT_FORCE_ENCODE_FIRST 0x00000002
00761 
00762 static void php_conv_qprint_encode_dtor(php_conv_qprint_encode *inst);
00763 static php_conv_err_t php_conv_qprint_encode_convert(php_conv_qprint_encode *inst, const char **in_pp, size_t *in_left_p, char **out_pp, size_t *out_left_p);
00764 
00765 static void php_conv_qprint_encode_dtor(php_conv_qprint_encode *inst)
00766 {
00767        assert(inst != NULL);
00768        if (inst->lbchars_dup && inst->lbchars != NULL) {
00769               pefree((void *)inst->lbchars, inst->persistent);
00770        }
00771 }
00772 
00773 #define NEXT_CHAR(ps, icnt, lb_ptr, lb_cnt, lbchars) \
00774        ((lb_ptr) < (lb_cnt) ? (lbchars)[(lb_ptr)] : *(ps)) 
00775 
00776 #define CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt) \
00777        if ((lb_ptr) < (lb_cnt)) { \
00778               (lb_ptr)++; \
00779        } else { \
00780               (lb_cnt) = (lb_ptr) = 0; \
00781               --(icnt); \
00782               (ps)++; \
00783        }
00784 
00785 static php_conv_err_t php_conv_qprint_encode_convert(php_conv_qprint_encode *inst, const char **in_pp, size_t *in_left_p, char **out_pp, size_t *out_left_p)
00786 {
00787        php_conv_err_t err = PHP_CONV_ERR_SUCCESS;
00788        unsigned char *ps, *pd;
00789        size_t icnt, ocnt;
00790        unsigned int c;
00791        unsigned int line_ccnt;
00792        unsigned int lb_ptr;
00793        unsigned int lb_cnt;
00794        int opts;
00795        static char qp_digits[] = "0123456789ABCDEF";
00796 
00797        line_ccnt = inst->line_ccnt;
00798        opts = inst->opts;
00799        lb_ptr = inst->lb_ptr;
00800        lb_cnt = inst->lb_cnt;
00801 
00802        if ((in_pp == NULL || in_left_p == NULL) && (lb_ptr >=lb_cnt)) {
00803               return PHP_CONV_ERR_SUCCESS;
00804        }
00805 
00806        ps = (unsigned char *)(*in_pp);
00807        icnt = *in_left_p;
00808        pd = (unsigned char *)(*out_pp);
00809        ocnt = *out_left_p;
00810 
00811        for (;;) {
00812               if (!(opts & PHP_CONV_QPRINT_OPT_BINARY) && inst->lbchars != NULL && inst->lbchars_len > 0) {
00813                      /* look ahead for the line break chars to make a right decision
00814                       * how to consume incoming characters */
00815 
00816                      if (icnt > 0 && *ps == inst->lbchars[lb_cnt]) {
00817                             lb_cnt++;
00818 
00819                             if (lb_cnt >= inst->lbchars_len) {
00820                                    unsigned int i;
00821 
00822                                    if (ocnt < lb_cnt) {
00823                                           lb_cnt--;
00824                                           err = PHP_CONV_ERR_TOO_BIG;
00825                                           break;
00826                                    }
00827 
00828                                    for (i = 0; i < lb_cnt; i++) {
00829                                           *(pd++) = inst->lbchars[i];
00830                                           ocnt--;
00831                                    }
00832                                    line_ccnt = inst->line_len;
00833                                    lb_ptr = lb_cnt = 0;
00834                             }
00835                             ps++, icnt--;
00836                             continue;
00837                      }
00838               }
00839 
00840               if (lb_ptr >= lb_cnt && icnt <= 0) {
00841                      break;
00842               } 
00843 
00844               c = NEXT_CHAR(ps, icnt, lb_ptr, lb_cnt, inst->lbchars);
00845 
00846               if (!(opts & PHP_CONV_QPRINT_OPT_BINARY) && (c == '\t' || c == ' ')) {
00847                      if (line_ccnt < 2 && inst->lbchars != NULL) {
00848                             if (ocnt < inst->lbchars_len + 1) {
00849                                    err = PHP_CONV_ERR_TOO_BIG;
00850                                    break;
00851                             }
00852 
00853                             *(pd++) = '=';
00854                             ocnt--;
00855                             line_ccnt--;
00856 
00857                             memcpy(pd, inst->lbchars, inst->lbchars_len);
00858                             pd += inst->lbchars_len;
00859                             ocnt -= inst->lbchars_len;
00860                             line_ccnt = inst->line_len;
00861                      } else {
00862                             if (ocnt < 1) {
00863                                    err = PHP_CONV_ERR_TOO_BIG;
00864                                    break;
00865                             }
00866                             *(pd++) = c;
00867                             ocnt--;
00868                             line_ccnt--;
00869                             CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt);
00870                      }
00871               } else if ((!(opts & PHP_CONV_QPRINT_OPT_FORCE_ENCODE_FIRST) || line_ccnt < inst->line_len) && ((c >= 33 && c <= 60) || (c >= 62 && c <= 126))) { 
00872                      if (line_ccnt < 2 && inst->lbchars != NULL) {
00873                             if (ocnt < inst->lbchars_len + 1) {
00874                                    err = PHP_CONV_ERR_TOO_BIG;
00875                                    break;
00876                             }
00877                             *(pd++) = '=';
00878                             ocnt--;
00879                             line_ccnt--;
00880 
00881                             memcpy(pd, inst->lbchars, inst->lbchars_len);
00882                             pd += inst->lbchars_len;
00883                             ocnt -= inst->lbchars_len;
00884                             line_ccnt = inst->line_len;
00885                      }
00886                      if (ocnt < 1) {
00887                             err = PHP_CONV_ERR_TOO_BIG;
00888                             break;
00889                      }
00890                      *(pd++) = c;
00891                      ocnt--;
00892                      line_ccnt--;
00893                      CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt);
00894               } else {
00895                      if (line_ccnt < 4) {
00896                             if (ocnt < inst->lbchars_len + 1) {
00897                                    err = PHP_CONV_ERR_TOO_BIG;
00898                                    break;
00899                             }
00900                             *(pd++) = '=';
00901                             ocnt--;
00902                             line_ccnt--;
00903 
00904                             memcpy(pd, inst->lbchars, inst->lbchars_len);
00905                             pd += inst->lbchars_len;
00906                             ocnt -= inst->lbchars_len;
00907                             line_ccnt = inst->line_len;
00908                      }
00909                      if (ocnt < 3) {
00910                             err = PHP_CONV_ERR_TOO_BIG;
00911                             break;
00912                      }
00913                      *(pd++) = '=';
00914                      *(pd++) = qp_digits[(c >> 4)];
00915                      *(pd++) = qp_digits[(c & 0x0f)]; 
00916                      ocnt -= 3;
00917                      line_ccnt -= 3;
00918                      CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt);
00919               }
00920        }
00921 
00922        *in_pp = (const char *)ps;
00923        *in_left_p = icnt;
00924        *out_pp = (char *)pd;
00925        *out_left_p = ocnt; 
00926        inst->line_ccnt = line_ccnt;
00927        inst->lb_ptr = lb_ptr;
00928        inst->lb_cnt = lb_cnt;
00929        return err;
00930 }
00931 #undef NEXT_CHAR
00932 #undef CONSUME_CHAR
00933 
00934 static php_conv_err_t php_conv_qprint_encode_ctor(php_conv_qprint_encode *inst, unsigned int line_len, const char *lbchars, size_t lbchars_len, int lbchars_dup, int opts, int persistent)
00935 {
00936        if (line_len < 4 && lbchars != NULL) {
00937               return PHP_CONV_ERR_TOO_BIG;
00938        }
00939        inst->_super.convert_op = (php_conv_convert_func) php_conv_qprint_encode_convert;
00940        inst->_super.dtor = (php_conv_dtor_func) php_conv_qprint_encode_dtor;
00941        inst->line_ccnt = line_len;
00942        inst->line_len = line_len;
00943        if (lbchars != NULL) {
00944               inst->lbchars = (lbchars_dup ? pestrdup(lbchars, persistent) : lbchars);
00945               inst->lbchars_len = lbchars_len;
00946        } else {
00947               inst->lbchars = NULL;
00948        }
00949        inst->lbchars_dup = lbchars_dup;
00950        inst->persistent = persistent;
00951        inst->opts = opts;
00952        inst->lb_cnt = inst->lb_ptr = 0;
00953        return PHP_CONV_ERR_SUCCESS;
00954 }
00955 /* }}} */
00956 
00957 /* {{{ php_conv_qprint_decode */
00958 typedef struct _php_conv_qprint_decode {
00959        php_conv _super;
00960 
00961        int scan_stat;
00962        unsigned int next_char;
00963        const char *lbchars;
00964        int lbchars_dup;
00965        size_t lbchars_len;
00966        int persistent;
00967        unsigned int lb_ptr;
00968        unsigned int lb_cnt; 
00969 } php_conv_qprint_decode;
00970 
00971 static void php_conv_qprint_decode_dtor(php_conv_qprint_decode *inst)
00972 {
00973        assert(inst != NULL);
00974        if (inst->lbchars_dup && inst->lbchars != NULL) {
00975               pefree((void *)inst->lbchars, inst->persistent);
00976        }
00977 }
00978 
00979 static php_conv_err_t php_conv_qprint_decode_convert(php_conv_qprint_decode *inst, const char **in_pp, size_t *in_left_p, char **out_pp, size_t *out_left_p)
00980 {
00981        php_conv_err_t err = PHP_CONV_ERR_SUCCESS;
00982        size_t icnt, ocnt;
00983        unsigned char *ps, *pd;
00984        unsigned int scan_stat;
00985        unsigned int next_char;
00986        unsigned int lb_ptr, lb_cnt;
00987 
00988        lb_ptr = inst->lb_ptr;
00989        lb_cnt = inst->lb_cnt;
00990 
00991        if ((in_pp == NULL || in_left_p == NULL) && lb_cnt == lb_ptr) {
00992               if (inst->scan_stat != 0) {
00993                      return PHP_CONV_ERR_UNEXPECTED_EOS;
00994               }
00995               return PHP_CONV_ERR_SUCCESS;
00996        }
00997 
00998        ps = (unsigned char *)(*in_pp);
00999        icnt = *in_left_p;
01000        pd = (unsigned char *)(*out_pp);
01001        ocnt = *out_left_p;
01002        scan_stat = inst->scan_stat;
01003        next_char = inst->next_char;
01004 
01005        for (;;) {
01006               switch (scan_stat) {
01007                      case 0: {
01008                             if (icnt <= 0) {
01009                                    goto out;
01010                             }
01011                             if (*ps == '=') {
01012                                    scan_stat = 1;
01013                             } else {
01014                                    if (ocnt < 1) {
01015                                           err = PHP_CONV_ERR_TOO_BIG;
01016                                           goto out;
01017                                    }
01018                                    *(pd++) = *ps;
01019                                    ocnt--;
01020                             }
01021                             ps++, icnt--;
01022                      } break;
01023 
01024                      case 1: {
01025                             if (icnt <= 0) {
01026                                    goto out;
01027                             }
01028                             if (*ps == ' ' || *ps == '\t') {
01029                                    scan_stat = 4;
01030                                    ps++, icnt--;
01031                                    break;
01032                             } else if (!inst->lbchars && lb_cnt == 0 && *ps == '\r') {
01033                                    /* auto-detect line endings, looks like network line ending \r\n (could be mac \r) */
01034                                    lb_cnt++;
01035                                    scan_stat = 5;
01036                                    ps++, icnt--;
01037                                    break;
01038                             } else if (!inst->lbchars && lb_cnt == 0 && *ps == '\n') {
01039                                    /* auto-detect line endings, looks like unix-lineendings, not to spec, but it is seem in the wild, a lot */
01040                                    lb_cnt = lb_ptr = 0;
01041                                    scan_stat = 0;
01042                                    ps++, icnt--;
01043                                    break;
01044                             } else if (lb_cnt < inst->lbchars_len &&
01045                                                  *ps == (unsigned char)inst->lbchars[lb_cnt]) {
01046                                    lb_cnt++;
01047                                    scan_stat = 5;
01048                                    ps++, icnt--;
01049                                    break;
01050                             }
01051                      } /* break is missing intentionally */
01052 
01053                      case 2: {     
01054                             if (icnt <= 0) {
01055                                    goto out;
01056                             }
01057 
01058                             if (!isxdigit((int) *ps)) {
01059                                    err = PHP_CONV_ERR_INVALID_SEQ;
01060                                    goto out;
01061                             }
01062                             next_char = (next_char << 4) | (*ps >= 'A' ? *ps - 0x37 : *ps - 0x30);
01063                             scan_stat++;
01064                             ps++, icnt--;
01065                             if (scan_stat != 3) {
01066                                    break;
01067                             }
01068                      } /* break is missing intentionally */
01069 
01070                      case 3: {
01071                             if (ocnt < 1) {
01072                                    err = PHP_CONV_ERR_TOO_BIG;
01073                                    goto out;
01074                             }
01075                             *(pd++) = next_char;
01076                             ocnt--;
01077                             scan_stat = 0;
01078                      } break;
01079 
01080                      case 4: {
01081                             if (icnt <= 0) {
01082                                    goto out;
01083                             }
01084                             if (lb_cnt < inst->lbchars_len &&
01085                                    *ps == (unsigned char)inst->lbchars[lb_cnt]) {
01086                                    lb_cnt++;
01087                                    scan_stat = 5;
01088                             }
01089                             if (*ps != '\t' && *ps != ' ') {
01090                                    err = PHP_CONV_ERR_INVALID_SEQ;
01091                                    goto out;
01092                             }
01093                             ps++, icnt--;
01094                      } break;
01095 
01096                      case 5: {
01097                             if (!inst->lbchars && lb_cnt == 1 && *ps == '\n') {
01098                                    /* auto-detect soft line breaks, found network line break */
01099                                    lb_cnt = lb_ptr = 0;
01100                                    scan_stat = 0;
01101                                    ps++, icnt--; /* consume \n */
01102                             } else if (!inst->lbchars && lb_cnt > 0) {
01103                                    /* auto-detect soft line breaks, found mac line break */
01104                                    lb_cnt = lb_ptr = 0;
01105                                    scan_stat = 0;
01106                             } else if (lb_cnt >= inst->lbchars_len) {
01107                                    /* soft line break */
01108                                    lb_cnt = lb_ptr = 0;
01109                                    scan_stat = 0;
01110                             } else if (icnt > 0) {
01111                                    if (*ps == (unsigned char)inst->lbchars[lb_cnt]) {
01112                                           lb_cnt++;
01113                                           ps++, icnt--;
01114                                    } else {
01115                                           scan_stat = 6; /* no break for short-cut */
01116                                    }
01117                             } else {
01118                                    goto out;
01119                             }
01120                      } break;
01121 
01122                      case 6: {
01123                             if (lb_ptr < lb_cnt) {
01124                                    if (ocnt < 1) {
01125                                           err = PHP_CONV_ERR_TOO_BIG;
01126                                           goto out;
01127                                    }
01128                                    *(pd++) = inst->lbchars[lb_ptr++];
01129                                    ocnt--;
01130                             } else {
01131                                    scan_stat = 0;
01132                                    lb_cnt = lb_ptr = 0;
01133                             }
01134                      } break;
01135               }
01136        }
01137 out:
01138        *in_pp = (const char *)ps;
01139        *in_left_p = icnt;
01140        *out_pp = (char *)pd;
01141        *out_left_p = ocnt;
01142        inst->scan_stat = scan_stat;
01143        inst->lb_ptr = lb_ptr;
01144        inst->lb_cnt = lb_cnt;
01145        inst->next_char = next_char;
01146 
01147        return err;
01148 }
01149 static php_conv_err_t php_conv_qprint_decode_ctor(php_conv_qprint_decode *inst, const char *lbchars, size_t lbchars_len, int lbchars_dup, int persistent)
01150 {
01151        inst->_super.convert_op = (php_conv_convert_func) php_conv_qprint_decode_convert;
01152        inst->_super.dtor = (php_conv_dtor_func) php_conv_qprint_decode_dtor;
01153        inst->scan_stat = 0;
01154        inst->next_char = 0;
01155        inst->lb_ptr = inst->lb_cnt = 0;
01156        if (lbchars != NULL) {
01157               inst->lbchars = (lbchars_dup ? pestrdup(lbchars, persistent) : lbchars);
01158               inst->lbchars_len = lbchars_len;
01159        } else {
01160               inst->lbchars = NULL;
01161               inst->lbchars_len = 0;
01162        }
01163        inst->lbchars_dup = lbchars_dup;
01164        inst->persistent = persistent;
01165        return PHP_CONV_ERR_SUCCESS;
01166 }
01167 /* }}} */
01168 
01169 typedef struct _php_convert_filter {
01170        php_conv *cd;
01171        int persistent;
01172        char *filtername;
01173        char stub[128];
01174        size_t stub_len;
01175 } php_convert_filter;
01176 
01177 #define PHP_CONV_BASE64_ENCODE 1
01178 #define PHP_CONV_BASE64_DECODE 2
01179 #define PHP_CONV_QPRINT_ENCODE 3 
01180 #define PHP_CONV_QPRINT_DECODE 4
01181 
01182 static php_conv_err_t php_conv_get_string_prop_ex(const HashTable *ht, char **pretval, size_t *pretval_len, char *field_name, size_t field_name_len, int persistent)
01183 {
01184        zval **tmpval;
01185 
01186        *pretval = NULL;
01187        *pretval_len = 0;
01188  
01189        if (zend_hash_find((HashTable *)ht, field_name, field_name_len, (void **)&tmpval) == SUCCESS) {
01190               if (Z_TYPE_PP(tmpval) != IS_STRING) {
01191                      zval zt = **tmpval;
01192 
01193                      convert_to_string(&zt);
01194 
01195                      if (NULL == (*pretval = pemalloc(Z_STRLEN(zt) + 1, persistent))) {
01196                             return PHP_CONV_ERR_ALLOC;
01197                      }
01198 
01199                      *pretval_len = Z_STRLEN(zt);
01200                      memcpy(*pretval, Z_STRVAL(zt), Z_STRLEN(zt) + 1);
01201                      zval_dtor(&zt);
01202               } else {
01203                      if (NULL == (*pretval = pemalloc(Z_STRLEN_PP(tmpval) + 1, persistent))) {
01204                             return PHP_CONV_ERR_ALLOC;
01205                      }
01206                      *pretval_len = Z_STRLEN_PP(tmpval);
01207                      memcpy(*pretval, Z_STRVAL_PP(tmpval), Z_STRLEN_PP(tmpval) + 1);
01208               }
01209        } else {
01210               return PHP_CONV_ERR_NOT_FOUND;
01211        }
01212        return PHP_CONV_ERR_SUCCESS;
01213 }
01214 
01215 #if IT_WAS_USED
01216 static php_conv_err_t php_conv_get_long_prop_ex(const HashTable *ht, long *pretval, char *field_name, size_t field_name_len)
01217 {
01218        zval **tmpval;
01219 
01220        *pretval = 0;
01221 
01222        if (zend_hash_find((HashTable *)ht, field_name, field_name_len, (void **)&tmpval) == SUCCESS) {
01223               zval tmp, *ztval = *tmpval;
01224 
01225               if (Z_TYPE_PP(tmpval) != IS_LONG) {
01226                      tmp = *ztval;
01227                      zval_copy_ctor(&tmp);
01228                      convert_to_long(&tmp);
01229                      ztval = &tmp;
01230               }
01231               *pretval = Z_LVAL_P(ztval);
01232        } else {
01233               return PHP_CONV_ERR_NOT_FOUND;
01234        } 
01235        return PHP_CONV_ERR_SUCCESS;
01236 }
01237 #endif
01238 
01239 static php_conv_err_t php_conv_get_ulong_prop_ex(const HashTable *ht, unsigned long *pretval, char *field_name, size_t field_name_len)
01240 {
01241        zval **tmpval;
01242 
01243        *pretval = 0;
01244 
01245        if (zend_hash_find((HashTable *)ht, field_name, field_name_len, (void **)&tmpval) == SUCCESS) {
01246               zval tmp, *ztval = *tmpval;
01247 
01248               if (Z_TYPE_PP(tmpval) != IS_LONG) {
01249                      tmp = *ztval;
01250                      zval_copy_ctor(&tmp);
01251                      convert_to_long(&tmp);
01252                      ztval = &tmp;
01253               }
01254               if (Z_LVAL_P(ztval) < 0) {
01255                      *pretval = 0;
01256               } else {
01257                      *pretval = Z_LVAL_P(ztval);
01258               }
01259        } else {
01260               return PHP_CONV_ERR_NOT_FOUND;
01261        } 
01262        return PHP_CONV_ERR_SUCCESS;
01263 }
01264 
01265 static php_conv_err_t php_conv_get_bool_prop_ex(const HashTable *ht, int *pretval, char *field_name, size_t field_name_len)
01266 {
01267        zval **tmpval;
01268 
01269        *pretval = 0;
01270 
01271        if (zend_hash_find((HashTable *)ht, field_name, field_name_len, (void **)&tmpval) == SUCCESS) {
01272               zval tmp, *ztval = *tmpval;
01273 
01274               if (Z_TYPE_PP(tmpval) != IS_BOOL) {
01275                      tmp = *ztval;
01276                      zval_copy_ctor(&tmp);
01277                      convert_to_boolean(&tmp);
01278                      ztval = &tmp;
01279               }
01280               *pretval = Z_BVAL_P(ztval);
01281        } else {
01282               return PHP_CONV_ERR_NOT_FOUND;
01283        } 
01284        return PHP_CONV_ERR_SUCCESS;
01285 }
01286 
01287 
01288 #if IT_WAS_USED
01289 static int php_conv_get_int_prop_ex(const HashTable *ht, int *pretval, char *field_name, size_t field_name_len)
01290 {
01291        long l;
01292        php_conv_err_t err;
01293 
01294        *pretval = 0;
01295 
01296        if ((err = php_conv_get_long_prop_ex(ht, &l, field_name, field_name_len)) == PHP_CONV_ERR_SUCCESS) {
01297               *pretval = l;
01298        }
01299        return err;
01300 }
01301 #endif
01302 
01303 static int php_conv_get_uint_prop_ex(const HashTable *ht, unsigned int *pretval, char *field_name, size_t field_name_len)
01304 {
01305        long l;
01306        php_conv_err_t err;
01307 
01308        *pretval = 0;
01309 
01310        if ((err = php_conv_get_ulong_prop_ex(ht, &l, field_name, field_name_len)) == PHP_CONV_ERR_SUCCESS) {
01311               *pretval = l;
01312        }
01313        return err;
01314 }
01315 
01316 #define GET_STR_PROP(ht, var, var_len, fldname, persistent) \
01317        php_conv_get_string_prop_ex(ht, &var, &var_len, fldname, sizeof(fldname), persistent) 
01318 
01319 #define GET_INT_PROP(ht, var, fldname) \
01320        php_conv_get_int_prop_ex(ht, &var, fldname, sizeof(fldname))
01321 
01322 #define GET_UINT_PROP(ht, var, fldname) \
01323        php_conv_get_uint_prop_ex(ht, &var, fldname, sizeof(fldname))
01324 
01325 #define GET_BOOL_PROP(ht, var, fldname) \
01326        php_conv_get_bool_prop_ex(ht, &var, fldname, sizeof(fldname))
01327 
01328 static php_conv *php_conv_open(int conv_mode, const HashTable *options, int persistent)
01329 {
01330        /* FIXME: I'll have to replace this ugly code by something neat
01331           (factories?) in the near future. */ 
01332        php_conv *retval = NULL;
01333 
01334        switch (conv_mode) {
01335               case PHP_CONV_BASE64_ENCODE: {
01336                      unsigned int line_len = 0;
01337                      char *lbchars = NULL;
01338                      size_t lbchars_len;
01339 
01340                      if (options != NULL) {
01341                             GET_STR_PROP(options, lbchars, lbchars_len, "line-break-chars", 0);
01342                             GET_UINT_PROP(options, line_len, "line-length");
01343                             if (line_len < 4) {
01344                                    if (lbchars != NULL) {
01345                                           pefree(lbchars, 0);
01346                                    }
01347                                    lbchars = NULL;
01348                             } else {
01349                                    if (lbchars == NULL) {
01350                                           lbchars = pestrdup("\r\n", 0);
01351                                           lbchars_len = 2;
01352                                    }
01353                             }
01354                      }
01355                      retval = pemalloc(sizeof(php_conv_base64_encode), persistent);
01356                      if (lbchars != NULL) {
01357                             if (php_conv_base64_encode_ctor((php_conv_base64_encode *)retval, line_len, lbchars, lbchars_len, 1, persistent)) {
01358                                    if (lbchars != NULL) {
01359                                           pefree(lbchars, 0);
01360                                    }
01361                                    goto out_failure;
01362                             }
01363                             pefree(lbchars, 0);
01364                      } else {
01365                             if (php_conv_base64_encode_ctor((php_conv_base64_encode *)retval, 0, NULL, 0, 0, persistent)) {
01366                                    goto out_failure;
01367                             }
01368                      }
01369               } break;
01370 
01371               case PHP_CONV_BASE64_DECODE:
01372                      retval = pemalloc(sizeof(php_conv_base64_decode), persistent);
01373                      if (php_conv_base64_decode_ctor((php_conv_base64_decode *)retval)) {
01374                             goto out_failure;
01375                      }
01376                      break;
01377 
01378               case PHP_CONV_QPRINT_ENCODE: {
01379                      unsigned int line_len = 0;
01380                      char *lbchars = NULL;
01381                      size_t lbchars_len;
01382                      int opts = 0;
01383 
01384                      if (options != NULL) {
01385                             int opt_binary = 0;
01386                             int opt_force_encode_first = 0;
01387 
01388                             GET_STR_PROP(options, lbchars, lbchars_len, "line-break-chars", 0);
01389                             GET_UINT_PROP(options, line_len, "line-length");
01390                             GET_BOOL_PROP(options, opt_binary, "binary"); 
01391                             GET_BOOL_PROP(options, opt_force_encode_first, "force-encode-first"); 
01392 
01393                             if (line_len < 4) {
01394                                    if (lbchars != NULL) {
01395                                           pefree(lbchars, 0);
01396                                    }
01397                                    lbchars = NULL;
01398                             } else {
01399                                    if (lbchars == NULL) {
01400                                           lbchars = pestrdup("\r\n", 0);
01401                                           lbchars_len = 2;
01402                                    }
01403                             }
01404                             opts |= (opt_binary ? PHP_CONV_QPRINT_OPT_BINARY : 0);
01405                             opts |= (opt_force_encode_first ? PHP_CONV_QPRINT_OPT_FORCE_ENCODE_FIRST : 0);
01406                      }
01407                      retval = pemalloc(sizeof(php_conv_qprint_encode), persistent);
01408                      if (lbchars != NULL) {
01409                             if (php_conv_qprint_encode_ctor((php_conv_qprint_encode *)retval, line_len, lbchars, lbchars_len, 1, opts, persistent)) {
01410                                    pefree(lbchars, 0);
01411                                    goto out_failure;
01412                             }
01413                             pefree(lbchars, 0);
01414                      } else {
01415                             if (php_conv_qprint_encode_ctor((php_conv_qprint_encode *)retval, 0, NULL, 0, 0, opts, persistent)) {
01416                                    goto out_failure;
01417                             }
01418                      }
01419               } break;
01420        
01421               case PHP_CONV_QPRINT_DECODE: {
01422                      char *lbchars = NULL;
01423                      size_t lbchars_len;
01424 
01425                      if (options != NULL) {
01426                             /* If line-break-chars are not specified, filter will attempt to detect line endings (\r, \n, or \r\n) */
01427                             GET_STR_PROP(options, lbchars, lbchars_len, "line-break-chars", 0);
01428                      }
01429 
01430                      retval = pemalloc(sizeof(php_conv_qprint_decode), persistent);
01431                      if (lbchars != NULL) {
01432                             if (php_conv_qprint_decode_ctor((php_conv_qprint_decode *)retval, lbchars, lbchars_len, 1, persistent)) {
01433                                    pefree(lbchars, 0);
01434                                    goto out_failure;
01435                             }
01436                             pefree(lbchars, 0);
01437                      } else {
01438                             if (php_conv_qprint_decode_ctor((php_conv_qprint_decode *)retval, NULL, 0, 0, persistent)) {
01439                                    goto out_failure;
01440                             }
01441                      }
01442               } break;
01443 
01444               default:
01445                      retval = NULL;
01446                      break;
01447        }
01448        return retval;
01449 
01450 out_failure:
01451        if (retval != NULL) {
01452               pefree(retval, persistent);
01453        }
01454        return NULL;  
01455 }
01456 
01457 #undef GET_STR_PROP
01458 #undef GET_INT_PROP
01459 #undef GET_UINT_PROP
01460 #undef GET_BOOL_PROP
01461 
01462 static int php_convert_filter_ctor(php_convert_filter *inst,
01463        int conv_mode, HashTable *conv_opts,
01464        const char *filtername, int persistent)
01465 {
01466        inst->persistent = persistent;
01467        inst->filtername = pestrdup(filtername, persistent);
01468        inst->stub_len = 0;
01469 
01470        if ((inst->cd = php_conv_open(conv_mode, conv_opts, persistent)) == NULL) {
01471               goto out_failure;
01472        }
01473 
01474        return SUCCESS;
01475 
01476 out_failure:
01477        if (inst->cd != NULL) {
01478               php_conv_dtor(inst->cd);
01479               pefree(inst->cd, persistent);
01480        }
01481        if (inst->filtername != NULL) {
01482               pefree(inst->filtername, persistent);
01483        }
01484        return FAILURE;
01485 }
01486 
01487 static void php_convert_filter_dtor(php_convert_filter *inst)
01488 {
01489        if (inst->cd != NULL) {
01490               php_conv_dtor(inst->cd);
01491               pefree(inst->cd, inst->persistent);
01492        }
01493 
01494        if (inst->filtername != NULL) {
01495               pefree(inst->filtername, inst->persistent);
01496        }
01497 }
01498 
01499 /* {{{ strfilter_convert_append_bucket */
01500 static int strfilter_convert_append_bucket(
01501               php_convert_filter *inst,
01502               php_stream *stream, php_stream_filter *filter,
01503               php_stream_bucket_brigade *buckets_out,
01504               const char *ps, size_t buf_len, size_t *consumed,
01505               int persistent TSRMLS_DC)
01506 {
01507        php_conv_err_t err;
01508        php_stream_bucket *new_bucket;
01509        char *out_buf = NULL;
01510        size_t out_buf_size;
01511        char *pd;
01512        const char *pt;
01513        size_t ocnt, icnt, tcnt;
01514        size_t initial_out_buf_size;
01515        
01516        if (ps == NULL) {
01517               initial_out_buf_size = 64;
01518               icnt = 1;
01519        } else {
01520               initial_out_buf_size = buf_len;
01521               icnt = buf_len;
01522        }
01523 
01524        out_buf_size = ocnt = initial_out_buf_size; 
01525        if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) {
01526               return FAILURE;
01527        }
01528 
01529        pd = out_buf;
01530 
01531        if (inst->stub_len > 0) {
01532               pt = inst->stub;
01533               tcnt = inst->stub_len;
01534 
01535               while (tcnt > 0) {
01536                      err = php_conv_convert(inst->cd, &pt, &tcnt, &pd, &ocnt);
01537 
01538                      switch (err) {
01539                             case PHP_CONV_ERR_INVALID_SEQ:
01540                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): invalid byte sequence", inst->filtername);
01541                                    goto out_failure;
01542 
01543                             case PHP_CONV_ERR_MORE:
01544                                    if (ps != NULL) {
01545                                           if (icnt > 0) {
01546                                                  if (inst->stub_len >= sizeof(inst->stub)) {
01547                                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): insufficient buffer", inst->filtername);
01548                                                         goto out_failure;
01549                                                  }
01550                                                  inst->stub[inst->stub_len++] = *(ps++);
01551                                                  icnt--;
01552                                                  pt = inst->stub;
01553                                                  tcnt = inst->stub_len;
01554                                           } else {
01555                                                  tcnt = 0;
01556                                                  break;
01557                                           }
01558                                    }
01559                                    break;
01560 
01561                             case PHP_CONV_ERR_UNEXPECTED_EOS:
01562                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): unexpected end of stream", inst->filtername);
01563                                    goto out_failure;
01564 
01565                             case PHP_CONV_ERR_TOO_BIG: {
01566                                    char *new_out_buf;
01567                                    size_t new_out_buf_size;
01568 
01569                                    new_out_buf_size = out_buf_size << 1;
01570 
01571                                    if (new_out_buf_size < out_buf_size) {
01572                                           /* whoa! no bigger buckets are sold anywhere... */
01573                                           if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
01574                                                  goto out_failure;
01575                                           }
01576 
01577                                           php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
01578 
01579                                           out_buf_size = ocnt = initial_out_buf_size;
01580                                           if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) {
01581                                                  return FAILURE;
01582                                           }
01583                                           pd = out_buf;
01584                                    } else {
01585                                           if (NULL == (new_out_buf = perealloc(out_buf, new_out_buf_size, persistent))) {
01586                                                  if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
01587                                                         goto out_failure;
01588                                                  }
01589 
01590                                                  php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
01591                                                  return FAILURE;
01592                                           }
01593 
01594                                           pd = new_out_buf + (pd - out_buf);
01595                                           ocnt += (new_out_buf_size - out_buf_size);
01596                                           out_buf = new_out_buf;
01597                                           out_buf_size = new_out_buf_size;
01598                                    }
01599                             } break;
01600 
01601                             case PHP_CONV_ERR_UNKNOWN:
01602                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): unknown error", inst->filtername);
01603                                    goto out_failure;
01604 
01605                             default:
01606                                    break;
01607                      }
01608               }
01609               memmove(inst->stub, pt, tcnt);
01610               inst->stub_len = tcnt;
01611        }
01612 
01613        while (icnt > 0) {
01614               err = ((ps == NULL ? php_conv_convert(inst->cd, NULL, NULL, &pd, &ocnt):
01615                             php_conv_convert(inst->cd, &ps, &icnt, &pd, &ocnt)));
01616               switch (err) {
01617                      case PHP_CONV_ERR_INVALID_SEQ:
01618                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): invalid byte sequence", inst->filtername);
01619                             goto out_failure;
01620 
01621                      case PHP_CONV_ERR_MORE:
01622                             if (ps != NULL) {
01623                                    if (icnt > sizeof(inst->stub)) {
01624                                           php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): insufficient buffer", inst->filtername);
01625                                           goto out_failure;
01626                                    }
01627                                    memcpy(inst->stub, ps, icnt);
01628                                    inst->stub_len = icnt;
01629                                    ps += icnt;
01630                                    icnt = 0;
01631                             } else {
01632                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): unexpected octet values", inst->filtername);
01633                                    goto out_failure;
01634                             }
01635                             break;
01636 
01637                      case PHP_CONV_ERR_TOO_BIG: {
01638                             char *new_out_buf;
01639                             size_t new_out_buf_size;
01640 
01641                             new_out_buf_size = out_buf_size << 1;
01642 
01643                             if (new_out_buf_size < out_buf_size) {
01644                                    /* whoa! no bigger buckets are sold anywhere... */
01645                                    if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
01646                                           goto out_failure;
01647                                    }
01648 
01649                                    php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
01650 
01651                                    out_buf_size = ocnt = initial_out_buf_size;
01652                                    if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) {
01653                                           return FAILURE;
01654                                    }
01655                                    pd = out_buf;
01656                             } else {
01657                                    if (NULL == (new_out_buf = perealloc(out_buf, new_out_buf_size, persistent))) {
01658                                           if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
01659                                                  goto out_failure;
01660                                           }
01661 
01662                                           php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
01663                                           return FAILURE;
01664                                    }
01665                                    pd = new_out_buf + (pd - out_buf);
01666                                    ocnt += (new_out_buf_size - out_buf_size);
01667                                    out_buf = new_out_buf;
01668                                    out_buf_size = new_out_buf_size;
01669                             }
01670                      } break;
01671 
01672                      case PHP_CONV_ERR_UNKNOWN:
01673                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): unknown error", inst->filtername);
01674                             goto out_failure;
01675 
01676                      default:
01677                             if (ps == NULL) {
01678                                    icnt = 0;
01679                             }
01680                             break;
01681               }
01682        }
01683 
01684        if (out_buf_size - ocnt > 0) {
01685               if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
01686                      goto out_failure;
01687               }
01688               php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
01689        } else {
01690               pefree(out_buf, persistent);
01691        }
01692        *consumed += buf_len - icnt;
01693 
01694        return SUCCESS;
01695 
01696 out_failure:
01697        pefree(out_buf, persistent);
01698        return FAILURE;
01699 }
01700 /* }}} */
01701 
01702 static php_stream_filter_status_t strfilter_convert_filter(
01703        php_stream *stream,
01704        php_stream_filter *thisfilter,
01705        php_stream_bucket_brigade *buckets_in,
01706        php_stream_bucket_brigade *buckets_out,
01707        size_t *bytes_consumed,
01708        int flags
01709        TSRMLS_DC)
01710 {
01711        php_stream_bucket *bucket = NULL;
01712        size_t consumed = 0;
01713        php_convert_filter *inst = (php_convert_filter *)thisfilter->abstract;
01714 
01715        while (buckets_in->head != NULL) {
01716               bucket = buckets_in->head;
01717 
01718               php_stream_bucket_unlink(bucket TSRMLS_CC);
01719 
01720               if (strfilter_convert_append_bucket(inst, stream, thisfilter,
01721                             buckets_out, bucket->buf, bucket->buflen, &consumed,
01722                             php_stream_is_persistent(stream) TSRMLS_CC) != SUCCESS) {
01723                      goto out_failure;
01724               }
01725 
01726               php_stream_bucket_delref(bucket TSRMLS_CC);
01727        }
01728 
01729        if (flags != PSFS_FLAG_NORMAL) {
01730               if (strfilter_convert_append_bucket(inst, stream, thisfilter,
01731                             buckets_out, NULL, 0, &consumed,
01732                             php_stream_is_persistent(stream) TSRMLS_CC) != SUCCESS) {
01733                      goto out_failure;
01734               }
01735        }
01736 
01737        if (bytes_consumed) {
01738               *bytes_consumed = consumed;
01739        }
01740 
01741        return PSFS_PASS_ON;
01742 
01743 out_failure:
01744        if (bucket != NULL) {
01745               php_stream_bucket_delref(bucket TSRMLS_CC);
01746        }
01747        return PSFS_ERR_FATAL;
01748 }
01749 
01750 static void strfilter_convert_dtor(php_stream_filter *thisfilter TSRMLS_DC)
01751 {
01752        assert(thisfilter->abstract != NULL);
01753 
01754        php_convert_filter_dtor((php_convert_filter *)thisfilter->abstract);
01755        pefree(thisfilter->abstract, ((php_convert_filter *)thisfilter->abstract)->persistent);
01756 }
01757 
01758 static php_stream_filter_ops strfilter_convert_ops = {
01759        strfilter_convert_filter,
01760        strfilter_convert_dtor,
01761        "convert.*"
01762 };
01763 
01764 static php_stream_filter *strfilter_convert_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
01765 {
01766        php_convert_filter *inst;
01767        php_stream_filter *retval = NULL;
01768 
01769        char *dot;
01770        int conv_mode = 0;
01771 
01772        if (filterparams != NULL && Z_TYPE_P(filterparams) != IS_ARRAY) {
01773               php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): invalid filter parameter", filtername);
01774               return NULL;
01775        }
01776 
01777        if ((dot = strchr(filtername, '.')) == NULL) {
01778               return NULL;
01779        }
01780        ++dot;
01781 
01782        inst = pemalloc(sizeof(php_convert_filter), persistent);
01783 
01784        if (strcasecmp(dot, "base64-encode") == 0) {
01785               conv_mode = PHP_CONV_BASE64_ENCODE;
01786        } else if (strcasecmp(dot, "base64-decode") == 0) {
01787               conv_mode = PHP_CONV_BASE64_DECODE;
01788        } else if (strcasecmp(dot, "quoted-printable-encode") == 0) {
01789               conv_mode = PHP_CONV_QPRINT_ENCODE;
01790        } else if (strcasecmp(dot, "quoted-printable-decode") == 0) {
01791               conv_mode = PHP_CONV_QPRINT_DECODE;
01792        }
01793        
01794        if (php_convert_filter_ctor(inst, conv_mode,
01795               (filterparams != NULL ? Z_ARRVAL_P(filterparams) : NULL),
01796               filtername, persistent) != SUCCESS) {
01797               goto out;
01798        }      
01799 
01800        retval = php_stream_filter_alloc(&strfilter_convert_ops, inst, persistent);
01801 out:
01802        if (retval == NULL) {
01803               pefree(inst, persistent);
01804        }
01805 
01806        return retval;
01807 }
01808 
01809 static php_stream_filter_factory strfilter_convert_factory = {
01810        strfilter_convert_create
01811 };
01812 /* }}} */
01813 
01814 /* {{{ consumed filter implementation */
01815 typedef struct _php_consumed_filter_data {
01816        int persistent;
01817        size_t consumed;
01818        off_t offset;
01819 } php_consumed_filter_data;
01820 
01821 static php_stream_filter_status_t consumed_filter_filter(
01822        php_stream *stream,
01823        php_stream_filter *thisfilter,
01824        php_stream_bucket_brigade *buckets_in,
01825        php_stream_bucket_brigade *buckets_out,
01826        size_t *bytes_consumed,
01827        int flags
01828        TSRMLS_DC)
01829 {
01830        php_consumed_filter_data *data = (php_consumed_filter_data *)(thisfilter->abstract);
01831        php_stream_bucket *bucket;
01832        size_t consumed = 0;
01833 
01834        if (data->offset == ~0) {
01835               data->offset = php_stream_tell(stream);
01836        }
01837        while ((bucket = buckets_in->head) != NULL) {
01838               php_stream_bucket_unlink(bucket TSRMLS_CC);
01839               consumed += bucket->buflen;
01840               php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
01841        }
01842        if (bytes_consumed) {
01843               *bytes_consumed = consumed;
01844        }
01845        if (flags & PSFS_FLAG_FLUSH_CLOSE) {
01846               php_stream_seek(stream, data->offset + data->consumed, SEEK_SET);
01847        }
01848        data->consumed += consumed;
01849        
01850        return PSFS_PASS_ON;
01851 }
01852 
01853 static void consumed_filter_dtor(php_stream_filter *thisfilter TSRMLS_DC)
01854 {
01855        if (thisfilter && thisfilter->abstract) {
01856               php_consumed_filter_data *data = (php_consumed_filter_data*)thisfilter->abstract;
01857               pefree(data, data->persistent);
01858        }
01859 }
01860 
01861 static php_stream_filter_ops consumed_filter_ops = {
01862        consumed_filter_filter,
01863        consumed_filter_dtor,
01864        "consumed"
01865 };
01866 
01867 static php_stream_filter *consumed_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
01868 {
01869        php_stream_filter_ops *fops = NULL;
01870        php_consumed_filter_data *data;
01871 
01872        if (strcasecmp(filtername, "consumed")) {
01873               return NULL;
01874        }
01875 
01876        /* Create this filter */
01877        data = pecalloc(1, sizeof(php_consumed_filter_data), persistent);
01878        if (!data) {
01879               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zd bytes", sizeof(php_consumed_filter_data));
01880               return NULL;
01881        }
01882        data->persistent = persistent;
01883        data->consumed = 0;
01884        data->offset = ~0;
01885        fops = &consumed_filter_ops;
01886 
01887        return php_stream_filter_alloc(fops, data, persistent);
01888 }
01889 
01890 php_stream_filter_factory consumed_filter_factory = {
01891        consumed_filter_create
01892 };
01893 
01894 /* }}} */
01895 
01896 /* {{{ chunked filter implementation */
01897 typedef enum _php_chunked_filter_state {
01898        CHUNK_SIZE_START,
01899        CHUNK_SIZE,
01900        CHUNK_SIZE_EXT,
01901        CHUNK_SIZE_CR,
01902        CHUNK_SIZE_LF,
01903        CHUNK_BODY,
01904        CHUNK_BODY_CR,
01905        CHUNK_BODY_LF,
01906        CHUNK_TRAILER,
01907        CHUNK_ERROR
01908 } php_chunked_filter_state;
01909 
01910 typedef struct _php_chunked_filter_data {
01911        php_chunked_filter_state state;
01912        size_t chunk_size;
01913        int persistent;
01914 } php_chunked_filter_data;
01915 
01916 static int php_dechunk(char *buf, int len, php_chunked_filter_data *data)
01917 {
01918        char *p = buf;
01919        char *end = p + len;
01920        char *out = buf;
01921        int out_len = 0;
01922 
01923        while (p < end) {
01924               switch (data->state) {
01925                      case CHUNK_SIZE_START:
01926                             data->chunk_size = 0;
01927                      case CHUNK_SIZE:
01928                             while (p < end) {
01929                                    if (*p >= '0' && *p <= '9') {
01930                                           data->chunk_size = (data->chunk_size * 16) + (*p - '0');
01931                                    } else if (*p >= 'A' && *p <= 'F') {
01932                                           data->chunk_size = (data->chunk_size * 16) + (*p - 'A' + 10);
01933                                    } else if (*p >= 'a' && *p <= 'f') {
01934                                           data->chunk_size = (data->chunk_size * 16) + (*p - 'a' + 10);
01935                                    } else if (data->state == CHUNK_SIZE_START) {
01936                                           data->state = CHUNK_ERROR;
01937                                           break;
01938                                    } else {
01939                                           data->state = CHUNK_SIZE_EXT;
01940                                           break;
01941                                    }
01942                                    data->state = CHUNK_SIZE;
01943                                    p++;
01944                             }
01945                             if (data->state == CHUNK_ERROR) {
01946                                    continue;
01947                             } else if (p == end) {
01948                                    return out_len;
01949                             }
01950                      case CHUNK_SIZE_EXT:
01951                             /* skip extension */
01952                             while (p < end && *p != '\r' && *p != '\n') {
01953                                    p++;
01954                             }
01955                             if (p == end) {
01956                                    return out_len;
01957                             }
01958                      case CHUNK_SIZE_CR:
01959                             if (*p == '\r') {
01960                                    p++;
01961                                    if (p == end) {
01962                                           data->state = CHUNK_SIZE_LF;
01963                                           return out_len;
01964                                    }
01965                             }
01966                      case CHUNK_SIZE_LF:
01967                             if (*p == '\n') {
01968                                    p++;
01969                                    if (data->chunk_size == 0) {
01970                                           /* last chunk */
01971                                           data->state = CHUNK_TRAILER;
01972                                           continue;
01973                                    } else if (p == end) {
01974                                           data->state = CHUNK_BODY;
01975                                           return out_len;
01976                                    }
01977                             } else {
01978                                    data->state = CHUNK_ERROR;
01979                                    continue;
01980                             }
01981                      case CHUNK_BODY:
01982                             if ((size_t) (end - p) >= data->chunk_size) {
01983                                    if (p != out) {
01984                                           memmove(out, p, data->chunk_size);
01985                                    }
01986                                    out += data->chunk_size;
01987                                    out_len += data->chunk_size;
01988                                    p += data->chunk_size;
01989                                    if (p == end) {
01990                                           data->state = CHUNK_BODY_CR;
01991                                           return out_len;
01992                                    }
01993                             } else {
01994                                    if (p != out) {
01995                                           memmove(out, p, end - p);
01996                                    }
01997                                    data->chunk_size -= end - p;
01998                                    data->state=CHUNK_BODY;
01999                                    out_len += end - p;
02000                                    return out_len;
02001                             }
02002                      case CHUNK_BODY_CR:
02003                             if (*p == '\r') {
02004                                    p++;
02005                                    if (p == end) {
02006                                           data->state = CHUNK_BODY_LF;
02007                                           return out_len;
02008                                    }
02009                             }
02010                      case CHUNK_BODY_LF:
02011                             if (*p == '\n') {
02012                                    p++;
02013                                    data->state = CHUNK_SIZE_START;
02014                                    continue;
02015                             } else {
02016                                    data->state = CHUNK_ERROR;
02017                                    continue;
02018                             }
02019                      case CHUNK_TRAILER:
02020                             /* ignore trailer */
02021                             p = end;
02022                             continue;
02023                      case CHUNK_ERROR:
02024                             if (p != out) {
02025                                    memmove(out, p, end - p);
02026                             }
02027                             out_len += end - p;
02028                             return out_len;      
02029               }
02030        }
02031        return out_len;
02032 }
02033 
02034 static php_stream_filter_status_t php_chunked_filter(
02035        php_stream *stream,
02036        php_stream_filter *thisfilter,
02037        php_stream_bucket_brigade *buckets_in,
02038        php_stream_bucket_brigade *buckets_out,
02039        size_t *bytes_consumed,
02040        int flags
02041        TSRMLS_DC)
02042 {
02043        php_stream_bucket *bucket;
02044        size_t consumed = 0;
02045        php_chunked_filter_data *data = (php_chunked_filter_data *) thisfilter->abstract;
02046 
02047        while (buckets_in->head) {
02048               bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC);
02049               consumed += bucket->buflen;
02050               bucket->buflen = php_dechunk(bucket->buf, bucket->buflen, data);      
02051               php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
02052        }
02053 
02054        if (bytes_consumed) {
02055               *bytes_consumed = consumed;
02056        }
02057        
02058        return PSFS_PASS_ON;
02059 }
02060 
02061 static void php_chunked_dtor(php_stream_filter *thisfilter TSRMLS_DC)
02062 {
02063        if (thisfilter && thisfilter->abstract) {
02064               php_chunked_filter_data *data = (php_chunked_filter_data *) thisfilter->abstract;
02065               pefree(data, data->persistent);
02066        }
02067 }
02068 
02069 static php_stream_filter_ops chunked_filter_ops = {
02070        php_chunked_filter,
02071        php_chunked_dtor,
02072        "dechunk"
02073 };
02074 
02075 static php_stream_filter *chunked_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
02076 {
02077        php_stream_filter_ops *fops = NULL;
02078        php_chunked_filter_data *data;
02079 
02080        if (strcasecmp(filtername, "dechunk")) {
02081               return NULL;
02082        }
02083 
02084        /* Create this filter */
02085        data = (php_chunked_filter_data *)pecalloc(1, sizeof(php_chunked_filter_data), persistent);
02086        if (!data) {
02087               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zd bytes", sizeof(php_chunked_filter_data));
02088               return NULL;
02089        }
02090        data->state = CHUNK_SIZE_START;
02091        data->chunk_size = 0;
02092        data->persistent = persistent;
02093        fops = &chunked_filter_ops;
02094 
02095        return php_stream_filter_alloc(fops, data, persistent);
02096 }
02097 
02098 static php_stream_filter_factory chunked_filter_factory = {
02099        chunked_filter_create
02100 };
02101 /* }}} */
02102 
02103 static const struct {
02104        php_stream_filter_ops *ops;
02105        php_stream_filter_factory *factory;
02106 } standard_filters[] = {
02107        { &strfilter_rot13_ops, &strfilter_rot13_factory },
02108        { &strfilter_toupper_ops, &strfilter_toupper_factory },
02109        { &strfilter_tolower_ops, &strfilter_tolower_factory },
02110        { &strfilter_strip_tags_ops, &strfilter_strip_tags_factory },
02111        { &strfilter_convert_ops, &strfilter_convert_factory },
02112        { &consumed_filter_ops, &consumed_filter_factory },
02113        { &chunked_filter_ops, &chunked_filter_factory },
02114        /* additional filters to go here */
02115        { NULL, NULL }
02116 };
02117 
02118 /* {{{ filter MINIT and MSHUTDOWN */
02119 PHP_MINIT_FUNCTION(standard_filters)
02120 {
02121        int i;
02122 
02123        for (i = 0; standard_filters[i].ops; i++) {
02124               if (FAILURE == php_stream_filter_register_factory(
02125                                    standard_filters[i].ops->label,
02126                                    standard_filters[i].factory
02127                                    TSRMLS_CC)) {
02128                      return FAILURE;
02129               }
02130        }
02131        return SUCCESS;
02132 }
02133 
02134 PHP_MSHUTDOWN_FUNCTION(standard_filters)
02135 {
02136        int i;
02137 
02138        for (i = 0; standard_filters[i].ops; i++) {
02139               php_stream_filter_unregister_factory(standard_filters[i].ops->label TSRMLS_CC);
02140        }
02141        return SUCCESS;
02142 }
02143 /* }}} */
02144 
02145 /*
02146  * Local variables:
02147  * tab-width: 4
02148  * c-basic-offset: 4
02149  * End:
02150  * vim600: sw=4 ts=4 fdm=marker
02151  * vim<600: sw=4 ts=4
02152  */