1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2014 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Author: Andrei Zmievski <andrei@php.net>                             |
16   +----------------------------------------------------------------------+
17 */
18
19/* $Id$ */
20
21#ifdef HAVE_CONFIG_H
22#include "config.h"
23#endif
24
25#include "php.h"
26
27#if HAVE_WDDX
28
29#include "ext/xml/expat_compat.h"
30#include "php_wddx.h"
31#include "php_wddx_api.h"
32
33#define PHP_XML_INTERNAL
34#include "ext/xml/php_xml.h"
35#include "ext/standard/php_incomplete_class.h"
36#include "ext/standard/base64.h"
37#include "ext/standard/info.h"
38#include "zend_smart_str.h"
39#include "ext/standard/html.h"
40#include "ext/standard/php_string.h"
41#include "ext/date/php_date.h"
42#include "zend_globals.h"
43
44#define WDDX_BUF_LEN            256
45#define PHP_CLASS_NAME_VAR      "php_class_name"
46
47#define EL_ARRAY                "array"
48#define EL_BINARY               "binary"
49#define EL_BOOLEAN              "boolean"
50#define EL_CHAR                 "char"
51#define EL_CHAR_CODE            "code"
52#define EL_NULL                 "null"
53#define EL_NUMBER               "number"
54#define EL_PACKET               "wddxPacket"
55#define EL_STRING               "string"
56#define EL_STRUCT               "struct"
57#define EL_VALUE                "value"
58#define EL_VAR                  "var"
59#define EL_NAME                 "name"
60#define EL_VERSION              "version"
61#define EL_RECORDSET            "recordset"
62#define EL_FIELD                "field"
63#define EL_DATETIME             "dateTime"
64
65#define php_wddx_deserialize(a,b) \
66    php_wddx_deserialize_ex((a)->value.str.val, (a)->value.str.len, (b))
67
68#define SET_STACK_VARNAME                           \
69        if (stack->varname) {                       \
70            ent.varname = estrdup(stack->varname);  \
71            efree(stack->varname);                  \
72            stack->varname = NULL;                  \
73        } else                                      \
74            ent.varname = NULL;                     \
75
76static int le_wddx;
77
78typedef struct {
79    zval data;
80    enum {
81        ST_ARRAY,
82        ST_BOOLEAN,
83        ST_NULL,
84        ST_NUMBER,
85        ST_STRING,
86        ST_BINARY,
87        ST_STRUCT,
88        ST_RECORDSET,
89        ST_FIELD,
90        ST_DATETIME
91    } type;
92    char *varname;
93} st_entry;
94
95typedef struct {
96    int top, max;
97    char *varname;
98    zend_bool done;
99    void **elements;
100} wddx_stack;
101
102
103static void php_wddx_process_data(void *user_data, const XML_Char *s, int len);
104
105/* {{{ arginfo */
106ZEND_BEGIN_ARG_INFO_EX(arginfo_wddx_serialize_value, 0, 0, 1)
107    ZEND_ARG_INFO(0, var)
108    ZEND_ARG_INFO(0, comment)
109ZEND_END_ARG_INFO()
110
111ZEND_BEGIN_ARG_INFO_EX(arginfo_wddx_serialize_vars, 0, 0, 1)
112    ZEND_ARG_VARIADIC_INFO(0, var_names)
113ZEND_END_ARG_INFO()
114
115ZEND_BEGIN_ARG_INFO_EX(arginfo_wddx_serialize_start, 0, 0, 0)
116    ZEND_ARG_INFO(0, comment)
117ZEND_END_ARG_INFO()
118
119ZEND_BEGIN_ARG_INFO_EX(arginfo_wddx_packet_end, 0, 0, 1)
120    ZEND_ARG_INFO(0, packet_id)
121ZEND_END_ARG_INFO()
122
123ZEND_BEGIN_ARG_INFO_EX(arginfo_wddx_add_vars, 0, 0, 2)
124    ZEND_ARG_INFO(0, packet_id)
125    ZEND_ARG_VARIADIC_INFO(0, var_names)
126ZEND_END_ARG_INFO()
127
128ZEND_BEGIN_ARG_INFO_EX(arginfo_wddx_deserialize, 0, 0, 1)
129    ZEND_ARG_INFO(0, packet)
130ZEND_END_ARG_INFO()
131/* }}} */
132
133/* {{{ wddx_functions[]
134 */
135const zend_function_entry wddx_functions[] = {
136    PHP_FE(wddx_serialize_value, arginfo_wddx_serialize_value)
137    PHP_FE(wddx_serialize_vars, arginfo_wddx_serialize_vars)
138    PHP_FE(wddx_packet_start,   arginfo_wddx_serialize_start)
139    PHP_FE(wddx_packet_end,     arginfo_wddx_packet_end)
140    PHP_FE(wddx_add_vars,       arginfo_wddx_add_vars)
141    PHP_FE(wddx_deserialize,    arginfo_wddx_deserialize)
142    PHP_FE_END
143};
144/* }}} */
145
146PHP_MINIT_FUNCTION(wddx);
147PHP_MINFO_FUNCTION(wddx);
148
149/* {{{ dynamically loadable module stuff */
150#ifdef COMPILE_DL_WDDX
151ZEND_GET_MODULE(wddx)
152#endif /* COMPILE_DL_WDDX */
153/* }}} */
154
155/* {{{ wddx_module_entry
156 */
157zend_module_entry wddx_module_entry = {
158    STANDARD_MODULE_HEADER,
159    "wddx",
160    wddx_functions,
161    PHP_MINIT(wddx),
162    NULL,
163    NULL,
164    NULL,
165    PHP_MINFO(wddx),
166    NO_VERSION_YET,
167    STANDARD_MODULE_PROPERTIES
168};
169/* }}} */
170
171/* {{{ wddx_stack_init
172 */
173static int wddx_stack_init(wddx_stack *stack)
174{
175    stack->top = 0;
176    stack->elements = (void **) safe_emalloc(sizeof(void **), STACK_BLOCK_SIZE, 0);
177    stack->max = STACK_BLOCK_SIZE;
178    stack->varname = NULL;
179    stack->done = 0;
180
181    return SUCCESS;
182}
183/* }}} */
184
185/* {{{ wddx_stack_push
186 */
187static int wddx_stack_push(wddx_stack *stack, void *element, int size)
188{
189    if (stack->top >= stack->max) {     /* we need to allocate more memory */
190        stack->elements = (void **) erealloc(stack->elements,
191                   (sizeof(void **) * (stack->max += STACK_BLOCK_SIZE)));
192    }
193    stack->elements[stack->top] = (void *) emalloc(size);
194    memcpy(stack->elements[stack->top], element, size);
195    return stack->top++;
196}
197/* }}} */
198
199/* {{{ wddx_stack_top
200 */
201static int wddx_stack_top(wddx_stack *stack, void **element)
202{
203    if (stack->top > 0) {
204        *element = stack->elements[stack->top - 1];
205        return SUCCESS;
206    } else {
207        *element = NULL;
208        return FAILURE;
209    }
210}
211/* }}} */
212
213/* {{{ wddx_stack_is_empty
214 */
215static int wddx_stack_is_empty(wddx_stack *stack)
216{
217    if (stack->top == 0) {
218        return 1;
219    } else {
220        return 0;
221    }
222}
223/* }}} */
224
225/* {{{ wddx_stack_destroy
226 */
227static int wddx_stack_destroy(wddx_stack *stack)
228{
229    register int i;
230
231    if (stack->elements) {
232        for (i = 0; i < stack->top; i++) {
233            zval_ptr_dtor(&((st_entry *)stack->elements[i])->data);
234            if (((st_entry *)stack->elements[i])->varname) {
235                efree(((st_entry *)stack->elements[i])->varname);
236            }
237            efree(stack->elements[i]);
238        }
239        efree(stack->elements);
240    }
241    return SUCCESS;
242}
243/* }}} */
244
245/* {{{ release_wddx_packet_rsrc
246 */
247static void release_wddx_packet_rsrc(zend_resource *rsrc TSRMLS_DC)
248{
249    smart_str *str = (smart_str *)rsrc->ptr;
250    smart_str_free(str);
251    efree(str);
252}
253/* }}} */
254
255#include "ext/session/php_session.h"
256
257#if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION)
258/* {{{ PS_SERIALIZER_ENCODE_FUNC
259 */
260PS_SERIALIZER_ENCODE_FUNC(wddx)
261{
262    wddx_packet *packet;
263    zend_string *str;
264    PS_ENCODE_VARS;
265
266    packet = php_wddx_constructor();
267
268    php_wddx_packet_start(packet, NULL, 0);
269    php_wddx_add_chunk_static(packet, WDDX_STRUCT_S);
270
271    PS_ENCODE_LOOP(
272        php_wddx_serialize_var(packet, struc, key TSRMLS_CC);
273    );
274
275    php_wddx_add_chunk_static(packet, WDDX_STRUCT_E);
276    php_wddx_packet_end(packet);
277    smart_str_0(packet);
278    str = zend_string_copy(packet->s);
279    php_wddx_destructor(packet);
280
281    return str;
282}
283/* }}} */
284
285/* {{{ PS_SERIALIZER_DECODE_FUNC
286 */
287PS_SERIALIZER_DECODE_FUNC(wddx)
288{
289    zval retval;
290    zval *ent;
291    zend_string *key;
292    zend_ulong idx;
293    int ret;
294
295    if (vallen == 0) {
296        return SUCCESS;
297    }
298
299    ZVAL_UNDEF(&retval);
300    if ((ret = php_wddx_deserialize_ex(val, vallen, &retval)) == SUCCESS) {
301        ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(retval), idx, key, ent) {
302            if (key == NULL) {
303                key = zend_long_to_str(idx);
304            } else {
305                zend_string_addref(key);
306            }
307            if (php_set_session_var(key, ent, NULL TSRMLS_CC)) {
308                if (Z_REFCOUNTED_P(ent)) Z_ADDREF_P(ent);
309            }
310            PS_ADD_VAR(key);
311            zend_string_release(key);
312        } ZEND_HASH_FOREACH_END();
313    }
314
315    zval_ptr_dtor(&retval);
316
317    return ret;
318}
319/* }}} */
320#endif
321
322/* {{{ PHP_MINIT_FUNCTION
323 */
324PHP_MINIT_FUNCTION(wddx)
325{
326    le_wddx = zend_register_list_destructors_ex(release_wddx_packet_rsrc, NULL, "wddx", module_number);
327
328#if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION)
329    php_session_register_serializer("wddx",
330                                    PS_SERIALIZER_ENCODE_NAME(wddx),
331                                    PS_SERIALIZER_DECODE_NAME(wddx));
332#endif
333
334    return SUCCESS;
335}
336/* }}} */
337
338/* {{{ PHP_MINFO_FUNCTION
339 */
340PHP_MINFO_FUNCTION(wddx)
341{
342    php_info_print_table_start();
343#if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION)
344    php_info_print_table_header(2, "WDDX Support", "enabled" );
345    php_info_print_table_row(2, "WDDX Session Serializer", "enabled" );
346#else
347    php_info_print_table_row(2, "WDDX Support", "enabled" );
348#endif
349    php_info_print_table_end();
350}
351/* }}} */
352
353/* {{{ php_wddx_packet_start
354 */
355void php_wddx_packet_start(wddx_packet *packet, char *comment, size_t comment_len)
356{
357    php_wddx_add_chunk_static(packet, WDDX_PACKET_S);
358    if (comment) {
359        php_wddx_add_chunk_static(packet, WDDX_HEADER_S);
360        php_wddx_add_chunk_static(packet, WDDX_COMMENT_S);
361        php_wddx_add_chunk_ex(packet, comment, comment_len);
362        php_wddx_add_chunk_static(packet, WDDX_COMMENT_E);
363        php_wddx_add_chunk_static(packet, WDDX_HEADER_E);
364    } else {
365        php_wddx_add_chunk_static(packet, WDDX_HEADER);
366    }
367    php_wddx_add_chunk_static(packet, WDDX_DATA_S);
368}
369/* }}} */
370
371/* {{{ php_wddx_packet_end
372 */
373void php_wddx_packet_end(wddx_packet *packet)
374{
375    php_wddx_add_chunk_static(packet, WDDX_DATA_E);
376    php_wddx_add_chunk_static(packet, WDDX_PACKET_E);
377}
378/* }}} */
379
380#define FLUSH_BUF()                               \
381    if (l > 0) {                                  \
382        php_wddx_add_chunk_ex(packet, buf, l);    \
383        l = 0;                                    \
384    }
385
386/* {{{ php_wddx_serialize_string
387 */
388static void php_wddx_serialize_string(wddx_packet *packet, zval *var TSRMLS_DC)
389{
390    php_wddx_add_chunk_static(packet, WDDX_STRING_S);
391
392    if (Z_STRLEN_P(var) > 0) {
393        zend_string *buf;
394
395        buf = php_escape_html_entities(Z_STRVAL_P(var), Z_STRLEN_P(var), 0, ENT_QUOTES, NULL TSRMLS_CC);
396
397        php_wddx_add_chunk_ex(packet, buf->val, buf->len);
398
399        zend_string_release(buf);
400    }
401    php_wddx_add_chunk_static(packet, WDDX_STRING_E);
402}
403/* }}} */
404
405/* {{{ php_wddx_serialize_number
406 */
407static void php_wddx_serialize_number(wddx_packet *packet, zval *var)
408{
409    char tmp_buf[WDDX_BUF_LEN];
410    zval tmp;
411
412    ZVAL_DUP(&tmp, var);
413    convert_to_string(&tmp);
414    snprintf(tmp_buf, sizeof(tmp_buf), WDDX_NUMBER, Z_STRVAL(tmp));
415    zval_ptr_dtor(&tmp);
416
417    php_wddx_add_chunk(packet, tmp_buf);
418}
419/* }}} */
420
421/* {{{ php_wddx_serialize_boolean
422 */
423static void php_wddx_serialize_boolean(wddx_packet *packet, zval *var)
424{
425    php_wddx_add_chunk(packet, Z_TYPE_P(var) == IS_TRUE ? WDDX_BOOLEAN_TRUE : WDDX_BOOLEAN_FALSE);
426}
427/* }}} */
428
429/* {{{ php_wddx_serialize_unset
430 */
431static void php_wddx_serialize_unset(wddx_packet *packet)
432{
433    php_wddx_add_chunk_static(packet, WDDX_NULL);
434}
435/* }}} */
436
437/* {{{ php_wddx_serialize_object
438 */
439static void php_wddx_serialize_object(wddx_packet *packet, zval *obj)
440{
441/* OBJECTS_FIXME */
442    zval *ent, fname, *varname;
443    zval retval;
444    zend_string *key;
445    zend_ulong idx;
446    char tmp_buf[WDDX_BUF_LEN];
447    HashTable *objhash, *sleephash;
448    TSRMLS_FETCH();
449
450    ZVAL_STRING(&fname, "__sleep");
451    /*
452     * We try to call __sleep() method on object. It's supposed to return an
453     * array of property names to be serialized.
454     */
455    if (call_user_function_ex(CG(function_table), obj, &fname, &retval, 0, 0, 1, NULL TSRMLS_CC) == SUCCESS) {
456        if (!Z_ISUNDEF(retval) && (sleephash = HASH_OF(&retval))) {
457            PHP_CLASS_ATTRIBUTES;
458
459            PHP_SET_CLASS_ATTRIBUTES(obj);
460
461            php_wddx_add_chunk_static(packet, WDDX_STRUCT_S);
462            snprintf(tmp_buf, WDDX_BUF_LEN, WDDX_VAR_S, PHP_CLASS_NAME_VAR);
463            php_wddx_add_chunk(packet, tmp_buf);
464            php_wddx_add_chunk_static(packet, WDDX_STRING_S);
465            php_wddx_add_chunk_ex(packet, class_name->val, class_name->len);
466            php_wddx_add_chunk_static(packet, WDDX_STRING_E);
467            php_wddx_add_chunk_static(packet, WDDX_VAR_E);
468
469            PHP_CLEANUP_CLASS_ATTRIBUTES();
470
471            objhash = HASH_OF(obj);
472
473            ZEND_HASH_FOREACH_VAL(sleephash, varname) {
474                if (Z_TYPE_P(varname) != IS_STRING) {
475                    php_error_docref(NULL TSRMLS_CC, E_NOTICE, "__sleep should return an array only containing the names of instance-variables to serialize.");
476                    continue;
477                }
478
479                if ((ent = zend_hash_find(objhash, Z_STR_P(varname))) != NULL) {
480                    php_wddx_serialize_var(packet, ent, Z_STR_P(varname) TSRMLS_CC);
481                }
482            } ZEND_HASH_FOREACH_END();
483
484            php_wddx_add_chunk_static(packet, WDDX_STRUCT_E);
485        }
486    } else {
487        PHP_CLASS_ATTRIBUTES;
488
489        PHP_SET_CLASS_ATTRIBUTES(obj);
490
491        php_wddx_add_chunk_static(packet, WDDX_STRUCT_S);
492        snprintf(tmp_buf, WDDX_BUF_LEN, WDDX_VAR_S, PHP_CLASS_NAME_VAR);
493        php_wddx_add_chunk(packet, tmp_buf);
494        php_wddx_add_chunk_static(packet, WDDX_STRING_S);
495        php_wddx_add_chunk_ex(packet, class_name->val, class_name->len);
496        php_wddx_add_chunk_static(packet, WDDX_STRING_E);
497        php_wddx_add_chunk_static(packet, WDDX_VAR_E);
498
499        PHP_CLEANUP_CLASS_ATTRIBUTES();
500
501        objhash = HASH_OF(obj);
502        ZEND_HASH_FOREACH_KEY_VAL(objhash, idx, key, ent) {
503            if (ent == obj) {
504                continue;
505            }
506            if (key) {
507                const char *class_name, *prop_name;
508                size_t prop_name_len;
509                zend_string *tmp;
510
511                zend_unmangle_property_name_ex(key, &class_name, &prop_name, &prop_name_len);
512                tmp = zend_string_init(prop_name, prop_name_len, 0);
513                php_wddx_serialize_var(packet, ent, tmp TSRMLS_CC);
514                zend_string_release(tmp);
515            } else {
516                key = zend_long_to_str(idx);
517                php_wddx_serialize_var(packet, ent, key TSRMLS_CC);
518                zend_string_release(key);
519            }
520        } ZEND_HASH_FOREACH_END();
521        php_wddx_add_chunk_static(packet, WDDX_STRUCT_E);
522    }
523
524    zval_ptr_dtor(&fname);
525    zval_ptr_dtor(&retval);
526}
527/* }}} */
528
529/* {{{ php_wddx_serialize_array
530 */
531static void php_wddx_serialize_array(wddx_packet *packet, zval *arr)
532{
533    zval *ent;
534    zend_string *key;
535    int is_struct = 0;
536    zend_ulong idx;
537    HashTable *target_hash;
538    char tmp_buf[WDDX_BUF_LEN];
539    zend_ulong ind = 0;
540    TSRMLS_FETCH();
541
542    target_hash = HASH_OF(arr);
543    ZEND_HASH_FOREACH_KEY(target_hash, idx, key) {
544        if (key) {
545            is_struct = 1;
546            break;
547        }
548
549        if (idx != ind) {
550            is_struct = 1;
551            break;
552        }
553        ind++;
554    } ZEND_HASH_FOREACH_END();
555
556    if (is_struct) {
557        php_wddx_add_chunk_static(packet, WDDX_STRUCT_S);
558    } else {
559        snprintf(tmp_buf, sizeof(tmp_buf), WDDX_ARRAY_S, zend_hash_num_elements(target_hash));
560        php_wddx_add_chunk(packet, tmp_buf);
561    }
562
563    ZEND_HASH_FOREACH_KEY_VAL(target_hash, idx, key, ent) {
564        if (ent == arr) {
565            continue;
566        }
567
568        if (is_struct) {
569            if (key) {
570                php_wddx_serialize_var(packet, ent, key TSRMLS_CC);
571            } else {
572                key = zend_long_to_str(idx);
573                php_wddx_serialize_var(packet, ent, key TSRMLS_CC);
574                zend_string_release(key);
575            }
576        } else {
577            php_wddx_serialize_var(packet, ent, NULL TSRMLS_CC);
578        }
579    } ZEND_HASH_FOREACH_END();
580
581    if (is_struct) {
582        php_wddx_add_chunk_static(packet, WDDX_STRUCT_E);
583    } else {
584        php_wddx_add_chunk_static(packet, WDDX_ARRAY_E);
585    }
586}
587/* }}} */
588
589/* {{{ php_wddx_serialize_var
590 */
591void php_wddx_serialize_var(wddx_packet *packet, zval *var, zend_string *name TSRMLS_DC)
592{
593    HashTable *ht;
594
595    if (name) {
596        char *tmp_buf;
597        zend_string *name_esc;
598
599        name_esc = php_escape_html_entities(name->val, name->len, 0, ENT_QUOTES, NULL TSRMLS_CC);
600        tmp_buf = emalloc(name_esc->len + sizeof(WDDX_VAR_S));
601        snprintf(tmp_buf, name_esc->len + sizeof(WDDX_VAR_S), WDDX_VAR_S, name_esc->val);
602        php_wddx_add_chunk(packet, tmp_buf);
603        efree(tmp_buf);
604        zend_string_release(name_esc);
605    }
606
607    if (Z_TYPE_P(var) == IS_INDIRECT) {
608        var = Z_INDIRECT_P(var);
609    }
610    ZVAL_DEREF(var);
611    switch (Z_TYPE_P(var)) {
612        case IS_STRING:
613            php_wddx_serialize_string(packet, var TSRMLS_CC);
614            break;
615
616        case IS_LONG:
617        case IS_DOUBLE:
618            php_wddx_serialize_number(packet, var);
619            break;
620
621        case IS_TRUE:
622        case IS_FALSE:
623            php_wddx_serialize_boolean(packet, var);
624            break;
625
626        case IS_NULL:
627            php_wddx_serialize_unset(packet);
628            break;
629
630        case IS_ARRAY:
631            ht = Z_ARRVAL_P(var);
632            if (ht->u.v.nApplyCount > 1) {
633                php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "WDDX doesn't support circular references");
634                return;
635            }
636            if (ZEND_HASH_APPLY_PROTECTION(ht)) {
637                ht->u.v.nApplyCount++;
638            }
639            php_wddx_serialize_array(packet, var);
640            if (ZEND_HASH_APPLY_PROTECTION(ht)) {
641                ht->u.v.nApplyCount--;
642            }
643            break;
644
645        case IS_OBJECT:
646            ht = Z_OBJPROP_P(var);
647            if (ht->u.v.nApplyCount > 1) {
648                php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "WDDX doesn't support circular references");
649                return;
650            }
651            ht->u.v.nApplyCount++;
652            php_wddx_serialize_object(packet, var);
653            ht->u.v.nApplyCount--;
654            break;
655    }
656
657    if (name) {
658        php_wddx_add_chunk_static(packet, WDDX_VAR_E);
659    }
660}
661/* }}} */
662
663/* {{{ php_wddx_add_var
664 */
665static void php_wddx_add_var(wddx_packet *packet, zval *name_var)
666{
667    zval *val;
668    HashTable *target_hash;
669    TSRMLS_FETCH();
670
671    if (Z_TYPE_P(name_var) == IS_STRING) {
672        zend_array *symbol_table = zend_rebuild_symbol_table(TSRMLS_C);
673        if ((val = zend_hash_find(&symbol_table->ht, Z_STR_P(name_var))) != NULL) {
674            if (Z_TYPE_P(val) == IS_INDIRECT) {
675                val = Z_INDIRECT_P(val);
676            }
677            php_wddx_serialize_var(packet, val, Z_STR_P(name_var) TSRMLS_CC);
678        }
679    } else if (Z_TYPE_P(name_var) == IS_ARRAY || Z_TYPE_P(name_var) == IS_OBJECT)   {
680        int is_array = Z_TYPE_P(name_var) == IS_ARRAY;
681
682        target_hash = HASH_OF(name_var);
683
684        if (is_array && target_hash->u.v.nApplyCount > 1) {
685            php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
686            return;
687        }
688
689        if (Z_IMMUTABLE_P(name_var)) {
690            ZEND_HASH_FOREACH_VAL(target_hash, val) {
691                php_wddx_add_var(packet, val);
692            } ZEND_HASH_FOREACH_END();
693        } else {
694            ZEND_HASH_FOREACH_VAL(target_hash, val) {
695                if (is_array) {
696                    target_hash->u.v.nApplyCount++;
697                }
698
699                ZVAL_DEREF(val);
700                php_wddx_add_var(packet, val);
701
702                if (is_array) {
703                    target_hash->u.v.nApplyCount--;
704                }
705            } ZEND_HASH_FOREACH_END();
706        }
707    }
708}
709/* }}} */
710
711/* {{{ php_wddx_push_element
712 */
713static void php_wddx_push_element(void *user_data, const XML_Char *name, const XML_Char **atts)
714{
715    st_entry ent;
716    wddx_stack *stack = (wddx_stack *)user_data;
717    TSRMLS_FETCH();
718    if (!strcmp((char *)name, EL_PACKET)) {
719        int i;
720
721        if (atts) for (i=0; atts[i]; i++) {
722            if (!strcmp((char *)atts[i], EL_VERSION)) {
723                /* nothing for now */
724            }
725        }
726    } else if (!strcmp((char *)name, EL_STRING)) {
727        ent.type = ST_STRING;
728        SET_STACK_VARNAME;
729
730        ZVAL_STR(&ent.data, STR_EMPTY_ALLOC());
731        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
732    } else if (!strcmp((char *)name, EL_BINARY)) {
733        ent.type = ST_BINARY;
734        SET_STACK_VARNAME;
735
736        ZVAL_STR(&ent.data, STR_EMPTY_ALLOC());
737        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
738    } else if (!strcmp((char *)name, EL_CHAR)) {
739        int i;
740
741        if (atts) for (i = 0; atts[i]; i++) {
742            if (!strcmp((char *)atts[i], EL_CHAR_CODE) && atts[++i] && atts[i][0]) {
743                char tmp_buf[2];
744
745                snprintf(tmp_buf, sizeof(tmp_buf), "%c", (char)strtol((char *)atts[i], NULL, 16));
746                php_wddx_process_data(user_data, tmp_buf, strlen(tmp_buf));
747                break;
748            }
749        }
750    } else if (!strcmp((char *)name, EL_NUMBER)) {
751        ent.type = ST_NUMBER;
752        SET_STACK_VARNAME;
753
754        ZVAL_LONG(&ent.data, 0);
755        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
756    } else if (!strcmp((char *)name, EL_BOOLEAN)) {
757        int i;
758
759        if (atts) for (i = 0; atts[i]; i++) {
760            if (!strcmp((char *)atts[i], EL_VALUE) && atts[++i] && atts[i][0]) {
761                ent.type = ST_BOOLEAN;
762                SET_STACK_VARNAME;
763
764                ZVAL_TRUE(&ent.data);
765                wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
766                php_wddx_process_data(user_data, atts[i], strlen((char *)atts[i]));
767                break;
768            }
769        }
770    } else if (!strcmp((char *)name, EL_NULL)) {
771        ent.type = ST_NULL;
772        SET_STACK_VARNAME;
773
774        ZVAL_NULL(&ent.data);
775
776        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
777    } else if (!strcmp((char *)name, EL_ARRAY)) {
778        ent.type = ST_ARRAY;
779        SET_STACK_VARNAME;
780
781        array_init(&ent.data);
782        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
783    } else if (!strcmp((char *)name, EL_STRUCT)) {
784        ent.type = ST_STRUCT;
785        SET_STACK_VARNAME;
786        array_init(&ent.data);
787        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
788    } else if (!strcmp((char *)name, EL_VAR)) {
789        int i;
790
791        if (atts) for (i = 0; atts[i]; i++) {
792            if (!strcmp((char *)atts[i], EL_NAME) && atts[++i] && atts[i][0]) {
793                stack->varname = estrdup(atts[i]);
794                break;
795            }
796        }
797    } else if (!strcmp((char *)name, EL_RECORDSET)) {
798        int i;
799
800        ent.type = ST_RECORDSET;
801        SET_STACK_VARNAME;
802        array_init(&ent.data);
803
804        if (atts) for (i = 0; atts[i]; i++) {
805            if (!strcmp((char *)atts[i], "fieldNames") && atts[++i] && atts[i][0]) {
806                zval tmp;
807                char *key;
808                char *p1, *p2, *endp;
809
810                endp = (char *)atts[i] + strlen(atts[i]);
811                p1 = (char *)atts[i];
812                while ((p2 = php_memnstr(p1, ",", sizeof(",")-1, endp)) != NULL) {
813                    key = estrndup(p1, p2 - p1);
814                    array_init(&tmp);
815                    add_assoc_zval_ex(&ent.data, key, p2 - p1, &tmp);
816                    p1 = p2 + sizeof(",")-1;
817                    efree(key);
818                }
819
820                if (p1 <= endp) {
821                    array_init(&tmp);
822                    add_assoc_zval_ex(&ent.data, p1, endp - p1, &tmp);
823                }
824
825                break;
826            }
827        }
828
829        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
830    } else if (!strcmp((char *)name, EL_FIELD)) {
831        int i;
832        st_entry ent;
833
834        ent.type = ST_FIELD;
835        ent.varname = NULL;
836        ZVAL_UNDEF(&ent.data);
837
838        if (atts) for (i = 0; atts[i]; i++) {
839            if (!strcmp((char *)atts[i], EL_NAME) && atts[++i] && atts[i][0]) {
840                st_entry *recordset;
841                zval *field;
842
843                if (wddx_stack_top(stack, (void**)&recordset) == SUCCESS &&
844                    recordset->type == ST_RECORDSET &&
845                    (field = zend_hash_str_find(Z_ARRVAL(recordset->data), (char*)atts[i], strlen(atts[i]))) != NULL) {
846                    ZVAL_COPY_VALUE(&ent.data, field);
847                }
848
849                break;
850            }
851        }
852
853        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
854    } else if (!strcmp((char *)name, EL_DATETIME)) {
855        ent.type = ST_DATETIME;
856        SET_STACK_VARNAME;
857
858        ZVAL_LONG(&ent.data, 0);
859        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
860    }
861}
862/* }}} */
863
864/* {{{ php_wddx_pop_element
865 */
866static void php_wddx_pop_element(void *user_data, const XML_Char *name)
867{
868    st_entry            *ent1, *ent2;
869    wddx_stack          *stack = (wddx_stack *)user_data;
870    HashTable           *target_hash;
871    zend_class_entry    *pce;
872    zval                obj;
873    TSRMLS_FETCH();
874
875/* OBJECTS_FIXME */
876    if (stack->top == 0) {
877        return;
878    }
879
880    if (!strcmp((char *)name, EL_STRING) || !strcmp((char *)name, EL_NUMBER) ||
881        !strcmp((char *)name, EL_BOOLEAN) || !strcmp((char *)name, EL_NULL) ||
882        !strcmp((char *)name, EL_ARRAY) || !strcmp((char *)name, EL_STRUCT) ||
883        !strcmp((char *)name, EL_RECORDSET) || !strcmp((char *)name, EL_BINARY) ||
884        !strcmp((char *)name, EL_DATETIME)) {
885        wddx_stack_top(stack, (void**)&ent1);
886
887        if (!strcmp((char *)name, EL_BINARY)) {
888            zend_string *new_str;
889
890            new_str = php_base64_decode(Z_STRVAL(ent1->data), Z_STRLEN(ent1->data));
891            zval_ptr_dtor(&ent1->data);
892            ZVAL_STR(&ent1->data, new_str);
893        }
894
895        /* Call __wakeup() method on the object. */
896        if (Z_TYPE(ent1->data) == IS_OBJECT) {
897            zval fname, retval;
898
899            ZVAL_STRING(&fname, "__wakeup");
900
901            call_user_function_ex(NULL, &ent1->data, &fname, &retval, 0, 0, 0, NULL TSRMLS_CC);
902
903            zval_ptr_dtor(&fname);
904            zval_ptr_dtor(&retval);
905        }
906
907        if (stack->top > 1) {
908            stack->top--;
909            wddx_stack_top(stack, (void**)&ent2);
910
911            /* if non-existent field */
912            if (ent2->type == ST_FIELD && Z_ISUNDEF(ent2->data)) {
913                zval_ptr_dtor(&ent1->data);
914                efree(ent1);
915                return;
916            }
917
918            if (Z_TYPE(ent2->data) == IS_ARRAY || Z_TYPE(ent2->data) == IS_OBJECT) {
919                target_hash = HASH_OF(&ent2->data);
920
921                if (ent1->varname) {
922                    if (!strcmp(ent1->varname, PHP_CLASS_NAME_VAR) &&
923                        Z_TYPE(ent1->data) == IS_STRING && Z_STRLEN(ent1->data)) {
924                        zend_bool incomplete_class = 0;
925
926                        zend_str_tolower(Z_STRVAL(ent1->data), Z_STRLEN(ent1->data));
927                        zend_string_forget_hash_val(Z_STR(ent1->data));
928                        if ((pce = zend_hash_find_ptr(EG(class_table), Z_STR(ent1->data))) == NULL) {
929                            incomplete_class = 1;
930                            pce = PHP_IC_ENTRY;
931                        }
932
933                        /* Initialize target object */
934                        object_init_ex(&obj, pce);
935
936                        /* Merge current hashtable with object's default properties */
937                        zend_hash_merge(Z_OBJPROP(obj),
938                                        Z_ARRVAL(ent2->data),
939                                        zval_add_ref, 0);
940
941                        if (incomplete_class) {
942                            php_store_class_name(&obj, Z_STRVAL(ent1->data), Z_STRLEN(ent1->data));
943                        }
944
945                        /* Clean up old array entry */
946                        zval_ptr_dtor(&ent2->data);
947
948                        /* Set stack entry to point to the newly created object */
949                        ZVAL_COPY_VALUE(&ent2->data, &obj);
950
951                        /* Clean up class name var entry */
952                        zval_ptr_dtor(&ent1->data);
953                    } else if (Z_TYPE(ent2->data) == IS_OBJECT) {
954                        zend_class_entry *old_scope = EG(scope);
955
956                        EG(scope) = Z_OBJCE(ent2->data);
957                        add_property_zval(&ent2->data, ent1->varname, &ent1->data);
958                        if Z_REFCOUNTED(ent1->data) Z_DELREF(ent1->data);
959                        EG(scope) = old_scope;
960                    } else {
961                        zend_symtable_str_update(target_hash, ent1->varname, strlen(ent1->varname), &ent1->data);
962                    }
963                    efree(ent1->varname);
964                } else  {
965                    zend_hash_next_index_insert(target_hash, &ent1->data);
966                }
967            }
968            efree(ent1);
969        } else {
970            stack->done = 1;
971        }
972    } else if (!strcmp((char *)name, EL_VAR) && stack->varname) {
973        efree(stack->varname);
974    } else if (!strcmp((char *)name, EL_FIELD)) {
975        st_entry *ent;
976        wddx_stack_top(stack, (void **)&ent);
977        efree(ent);
978        stack->top--;
979    }
980}
981/* }}} */
982
983/* {{{ php_wddx_process_data
984 */
985static void php_wddx_process_data(void *user_data, const XML_Char *s, int len)
986{
987    st_entry *ent;
988    wddx_stack *stack = (wddx_stack *)user_data;
989    TSRMLS_FETCH();
990
991    if (!wddx_stack_is_empty(stack) && !stack->done) {
992        wddx_stack_top(stack, (void**)&ent);
993        switch (ent->type) {
994            case ST_BINARY:
995            case ST_STRING:
996                if (Z_STRLEN(ent->data) == 0) {
997                    zval_ptr_dtor(&ent->data);
998                    ZVAL_STRINGL(&ent->data, (char *)s, len);
999                } else {
1000                    Z_STR(ent->data) = zend_string_realloc(Z_STR(ent->data), Z_STRLEN(ent->data) + len, 0);
1001                    memcpy(Z_STRVAL(ent->data) + Z_STRLEN(ent->data) - len, (char *)s, len);
1002                    Z_STRVAL(ent->data)[Z_STRLEN(ent->data)] = '\0';
1003                }
1004                break;
1005            case ST_NUMBER:
1006                ZVAL_STRINGL(&ent->data, (char *)s, len);
1007                convert_scalar_to_number(&ent->data TSRMLS_CC);
1008                break;
1009
1010            case ST_BOOLEAN:
1011                if (!strcmp((char *)s, "true")) {
1012                    Z_LVAL(ent->data) = 1;
1013                } else if (!strcmp((char *)s, "false")) {
1014                    Z_LVAL(ent->data) = 0;
1015                } else {
1016                    stack->top--;
1017                    zval_ptr_dtor(&ent->data);
1018                    if (ent->varname)
1019                        efree(ent->varname);
1020                    efree(ent);
1021                }
1022                break;
1023
1024            case ST_DATETIME: {
1025                char *tmp;
1026
1027                tmp = emalloc(len + 1);
1028                memcpy(tmp, (char *)s, len);
1029                tmp[len] = '\0';
1030
1031                Z_LVAL(ent->data) = php_parse_date(tmp, NULL);
1032                /* date out of range < 1969 or > 2038 */
1033                if (Z_LVAL(ent->data) == -1) {
1034                    ZVAL_STRINGL(&ent->data, (char *)s, len);
1035                }
1036                efree(tmp);
1037            }
1038                break;
1039
1040            default:
1041                break;
1042        }
1043    }
1044}
1045/* }}} */
1046
1047/* {{{ php_wddx_deserialize_ex
1048 */
1049int php_wddx_deserialize_ex(const char *value, size_t vallen, zval *return_value)
1050{
1051    wddx_stack stack;
1052    XML_Parser parser;
1053    st_entry *ent;
1054    int retval;
1055
1056    wddx_stack_init(&stack);
1057    parser = XML_ParserCreate("UTF-8");
1058
1059    XML_SetUserData(parser, &stack);
1060    XML_SetElementHandler(parser, php_wddx_push_element, php_wddx_pop_element);
1061    XML_SetCharacterDataHandler(parser, php_wddx_process_data);
1062
1063    /* XXX value should be parsed in the loop to exhaust size_t */
1064    XML_Parse(parser, value, (int)vallen, 1);
1065
1066    XML_ParserFree(parser);
1067
1068    if (stack.top == 1) {
1069        wddx_stack_top(&stack, (void**)&ent);
1070        ZVAL_COPY(return_value, &ent->data);
1071        retval = SUCCESS;
1072    } else {
1073        retval = FAILURE;
1074    }
1075
1076    wddx_stack_destroy(&stack);
1077
1078    return retval;
1079}
1080/* }}} */
1081
1082/* {{{ proto string wddx_serialize_value(mixed var [, string comment])
1083   Creates a new packet and serializes the given value */
1084PHP_FUNCTION(wddx_serialize_value)
1085{
1086    zval *var;
1087    char *comment = NULL;
1088    size_t comment_len = 0;
1089    wddx_packet *packet;
1090
1091    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|s", &var, &comment, &comment_len) == FAILURE) {
1092        return;
1093    }
1094
1095    packet = php_wddx_constructor();
1096
1097    php_wddx_packet_start(packet, comment, comment_len);
1098    php_wddx_serialize_var(packet, var, NULL TSRMLS_CC);
1099    php_wddx_packet_end(packet);
1100    smart_str_0(packet);
1101
1102    RETVAL_STR(zend_string_copy(packet->s));
1103    php_wddx_destructor(packet);
1104}
1105/* }}} */
1106
1107/* {{{ proto string wddx_serialize_vars(mixed var_name [, mixed ...])
1108   Creates a new packet and serializes given variables into a struct */
1109PHP_FUNCTION(wddx_serialize_vars)
1110{
1111    int num_args, i;
1112    wddx_packet *packet;
1113    zval *args = NULL;
1114
1115    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &num_args) == FAILURE) {
1116        return;
1117    }
1118
1119    packet = php_wddx_constructor();
1120
1121    php_wddx_packet_start(packet, NULL, 0);
1122    php_wddx_add_chunk_static(packet, WDDX_STRUCT_S);
1123
1124    for (i=0; i<num_args; i++) {
1125        zval *arg;
1126        if (!Z_ISREF(args[i])) {
1127            arg = &args[i];
1128        } else {
1129            arg = Z_REFVAL(args[i]);
1130        }
1131        if (Z_TYPE_P(arg) != IS_ARRAY && Z_TYPE_P(arg) != IS_OBJECT) {
1132            convert_to_string_ex(arg);
1133        }
1134        php_wddx_add_var(packet, arg);
1135    }
1136
1137    php_wddx_add_chunk_static(packet, WDDX_STRUCT_E);
1138    php_wddx_packet_end(packet);
1139    smart_str_0(packet);
1140
1141    RETVAL_STR(zend_string_copy(packet->s));
1142    php_wddx_destructor(packet);
1143}
1144/* }}} */
1145
1146/* {{{ php_wddx_constructor
1147 */
1148wddx_packet *php_wddx_constructor(void)
1149{
1150    smart_str *packet;
1151
1152    packet = ecalloc(1, sizeof(smart_str));
1153
1154    return packet;
1155}
1156/* }}} */
1157
1158/* {{{ php_wddx_destructor
1159 */
1160void php_wddx_destructor(wddx_packet *packet)
1161{
1162    smart_str_free(packet);
1163    efree(packet);
1164}
1165/* }}} */
1166
1167/* {{{ proto resource wddx_packet_start([string comment])
1168   Starts a WDDX packet with optional comment and returns the packet id */
1169PHP_FUNCTION(wddx_packet_start)
1170{
1171    char *comment = NULL;
1172    size_t comment_len = 0;
1173    wddx_packet *packet;
1174
1175    comment = NULL;
1176
1177    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &comment, &comment_len) == FAILURE) {
1178        return;
1179    }
1180
1181    packet = php_wddx_constructor();
1182
1183    php_wddx_packet_start(packet, comment, comment_len);
1184    php_wddx_add_chunk_static(packet, WDDX_STRUCT_S);
1185
1186    ZEND_REGISTER_RESOURCE(return_value, packet, le_wddx);
1187}
1188/* }}} */
1189
1190/* {{{ proto string wddx_packet_end(resource packet_id)
1191   Ends specified WDDX packet and returns the string containing the packet */
1192PHP_FUNCTION(wddx_packet_end)
1193{
1194    zval *packet_id;
1195    wddx_packet *packet = NULL;
1196
1197    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &packet_id) == FAILURE) {
1198        return;
1199    }
1200
1201    ZEND_FETCH_RESOURCE(packet, wddx_packet *, packet_id, -1, "WDDX packet ID", le_wddx);
1202
1203    php_wddx_add_chunk_static(packet, WDDX_STRUCT_E);
1204
1205    php_wddx_packet_end(packet);
1206    smart_str_0(packet);
1207
1208    RETVAL_STR(zend_string_copy(packet->s));
1209
1210    zend_list_close(Z_RES_P(packet_id));
1211}
1212/* }}} */
1213
1214/* {{{ proto int wddx_add_vars(resource packet_id,  mixed var_names [, mixed ...])
1215   Serializes given variables and adds them to packet given by packet_id */
1216PHP_FUNCTION(wddx_add_vars)
1217{
1218    int num_args, i;
1219    zval *args = NULL;
1220    zval *packet_id;
1221    wddx_packet *packet = NULL;
1222
1223    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r+", &packet_id, &args, &num_args) == FAILURE) {
1224        return;
1225    }
1226
1227    if (!ZEND_FETCH_RESOURCE_NO_RETURN(packet, wddx_packet *, packet_id, -1, "WDDX packet ID", le_wddx)) {
1228        RETURN_FALSE;
1229    }
1230
1231    if (!packet) {
1232        RETURN_FALSE;
1233    }
1234
1235    for (i=0; i<num_args; i++) {
1236        zval *arg;
1237        if (!Z_ISREF(args[i])) {
1238            arg = &args[i];
1239        } else {
1240            arg = Z_REFVAL(args[i]);
1241        }
1242        if (Z_TYPE_P(arg) != IS_ARRAY && Z_TYPE_P(arg) != IS_OBJECT) {
1243            convert_to_string_ex(arg);
1244        }
1245        php_wddx_add_var(packet, arg);
1246    }
1247
1248    RETURN_TRUE;
1249}
1250/* }}} */
1251
1252/* {{{ proto mixed wddx_deserialize(mixed packet)
1253   Deserializes given packet and returns a PHP value */
1254PHP_FUNCTION(wddx_deserialize)
1255{
1256    zval *packet;
1257    php_stream *stream = NULL;
1258    zend_string *payload = NULL;
1259
1260    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &packet) == FAILURE) {
1261        return;
1262    }
1263
1264    if (Z_TYPE_P(packet) == IS_STRING) {
1265        payload = Z_STR_P(packet);
1266    } else if (Z_TYPE_P(packet) == IS_RESOURCE) {
1267        php_stream_from_zval(stream, packet);
1268        if (stream) {
1269            payload = php_stream_copy_to_mem(stream, PHP_STREAM_COPY_ALL, 0);
1270        }
1271    } else {
1272        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expecting parameter 1 to be a string or a stream");
1273        return;
1274    }
1275
1276    if (payload == NULL) {
1277        return;
1278    }
1279
1280    php_wddx_deserialize_ex(payload->val, payload->len, return_value);
1281
1282    if (stream) {
1283        efree(payload);
1284    }
1285}
1286/* }}} */
1287
1288#endif /* HAVE_LIBEXPAT */
1289
1290/*
1291 * Local variables:
1292 * tab-width: 4
1293 * c-basic-offset: 4
1294 * End:
1295 * vim600: sw=4 ts=4 fdm=marker
1296 * vim<600: sw=4 ts=4
1297 */
1298