1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
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 "ext/standard/php_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    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    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                zend_string *tmp;
509
510                zend_unmangle_property_name(key->val, key->len, &class_name, &prop_name);
511                tmp = zend_string_init(prop_name, strlen(prop_name), 0);
512                php_wddx_serialize_var(packet, ent, tmp TSRMLS_CC);
513                zend_string_release(tmp);
514            } else {
515                key = zend_long_to_str(idx);
516                php_wddx_serialize_var(packet, ent, key TSRMLS_CC);
517                zend_string_release(key);
518            }
519        } ZEND_HASH_FOREACH_END();
520        php_wddx_add_chunk_static(packet, WDDX_STRUCT_E);
521    }
522
523    zval_ptr_dtor(&fname);
524    zval_ptr_dtor(&retval);
525}
526/* }}} */
527
528/* {{{ php_wddx_serialize_array
529 */
530static void php_wddx_serialize_array(wddx_packet *packet, zval *arr)
531{
532    zval *ent;
533    zend_string *key;
534    int is_struct = 0;
535    ulong idx;
536    HashTable *target_hash;
537    char tmp_buf[WDDX_BUF_LEN];
538    ulong ind = 0;
539    TSRMLS_FETCH();
540
541    target_hash = HASH_OF(arr);
542    ZEND_HASH_FOREACH_KEY(target_hash, idx, key) {
543        if (key) {
544            is_struct = 1;
545            break;
546        }
547
548        if (idx != ind) {
549            is_struct = 1;
550            break;
551        }
552        ind++;
553    } ZEND_HASH_FOREACH_END();
554
555    if (is_struct) {
556        php_wddx_add_chunk_static(packet, WDDX_STRUCT_S);
557    } else {
558        snprintf(tmp_buf, sizeof(tmp_buf), WDDX_ARRAY_S, zend_hash_num_elements(target_hash));
559        php_wddx_add_chunk(packet, tmp_buf);
560    }
561
562    ZEND_HASH_FOREACH_KEY_VAL(target_hash, idx, key, ent) {
563        if (ent == arr) {
564            continue;
565        }
566
567        if (is_struct) {
568            if (key) {
569                php_wddx_serialize_var(packet, ent, key TSRMLS_CC);
570            } else {
571                key = zend_long_to_str(idx);
572                php_wddx_serialize_var(packet, ent, key TSRMLS_CC);
573                zend_string_release(key);
574            }
575        } else {
576            php_wddx_serialize_var(packet, ent, NULL TSRMLS_CC);
577        }
578    } ZEND_HASH_FOREACH_END();
579
580    if (is_struct) {
581        php_wddx_add_chunk_static(packet, WDDX_STRUCT_E);
582    } else {
583        php_wddx_add_chunk_static(packet, WDDX_ARRAY_E);
584    }
585}
586/* }}} */
587
588/* {{{ php_wddx_serialize_var
589 */
590void php_wddx_serialize_var(wddx_packet *packet, zval *var, zend_string *name TSRMLS_DC)
591{
592    HashTable *ht;
593
594    if (name) {
595        char *tmp_buf;
596        zend_string *name_esc;
597
598        name_esc = php_escape_html_entities(name->val, name->len, 0, ENT_QUOTES, NULL TSRMLS_CC);
599        tmp_buf = emalloc(name_esc->len + sizeof(WDDX_VAR_S));
600        snprintf(tmp_buf, name_esc->len + sizeof(WDDX_VAR_S), WDDX_VAR_S, name_esc->val);
601        php_wddx_add_chunk(packet, tmp_buf);
602        efree(tmp_buf);
603        zend_string_release(name_esc);
604    }
605
606    if (Z_TYPE_P(var) == IS_INDIRECT) {
607        var = Z_INDIRECT_P(var);
608    }
609    ZVAL_DEREF(var);
610    switch (Z_TYPE_P(var)) {
611        case IS_STRING:
612            php_wddx_serialize_string(packet, var TSRMLS_CC);
613            break;
614
615        case IS_LONG:
616        case IS_DOUBLE:
617            php_wddx_serialize_number(packet, var);
618            break;
619
620        case IS_TRUE:
621        case IS_FALSE:
622            php_wddx_serialize_boolean(packet, var);
623            break;
624
625        case IS_NULL:
626            php_wddx_serialize_unset(packet);
627            break;
628
629        case IS_ARRAY:
630            ht = Z_ARRVAL_P(var);
631            if (ht->u.v.nApplyCount > 1) {
632                php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "WDDX doesn't support circular references");
633                return;
634            }
635            if (ZEND_HASH_APPLY_PROTECTION(ht)) {
636                ht->u.v.nApplyCount++;
637            }
638            php_wddx_serialize_array(packet, var);
639            if (ZEND_HASH_APPLY_PROTECTION(ht)) {
640                ht->u.v.nApplyCount--;
641            }
642            break;
643
644        case IS_OBJECT:
645            ht = Z_OBJPROP_P(var);
646            if (ht->u.v.nApplyCount > 1) {
647                php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "WDDX doesn't support circular references");
648                return;
649            }
650            ht->u.v.nApplyCount++;
651            php_wddx_serialize_object(packet, var);
652            ht->u.v.nApplyCount--;
653            break;
654    }
655
656    if (name) {
657        php_wddx_add_chunk_static(packet, WDDX_VAR_E);
658    }
659}
660/* }}} */
661
662/* {{{ php_wddx_add_var
663 */
664static void php_wddx_add_var(wddx_packet *packet, zval *name_var)
665{
666    zval *val;
667    HashTable *target_hash;
668    TSRMLS_FETCH();
669
670    if (Z_TYPE_P(name_var) == IS_STRING) {
671        zend_array *symbol_table = zend_rebuild_symbol_table(TSRMLS_C);
672        if ((val = zend_hash_find(&symbol_table->ht, Z_STR_P(name_var))) != NULL) {
673            if (Z_TYPE_P(val) == IS_INDIRECT) {
674                val = Z_INDIRECT_P(val);
675            }
676            php_wddx_serialize_var(packet, val, Z_STR_P(name_var) TSRMLS_CC);
677        }
678    } else if (Z_TYPE_P(name_var) == IS_ARRAY || Z_TYPE_P(name_var) == IS_OBJECT)   {
679        int is_array = Z_TYPE_P(name_var) == IS_ARRAY;
680
681        target_hash = HASH_OF(name_var);
682
683        if (is_array && target_hash->u.v.nApplyCount > 1) {
684            php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
685            return;
686        }
687
688        if (Z_IMMUTABLE_P(name_var)) {
689            ZEND_HASH_FOREACH_VAL(target_hash, val) {
690                php_wddx_add_var(packet, val);
691            } ZEND_HASH_FOREACH_END();
692        } else {
693            ZEND_HASH_FOREACH_VAL(target_hash, val) {
694                if (is_array) {
695                    target_hash->u.v.nApplyCount++;
696                }
697
698                ZVAL_DEREF(val);
699                php_wddx_add_var(packet, val);
700
701                if (is_array) {
702                    target_hash->u.v.nApplyCount--;
703                }
704            } ZEND_HASH_FOREACH_END();
705        }
706    }
707}
708/* }}} */
709
710/* {{{ php_wddx_push_element
711 */
712static void php_wddx_push_element(void *user_data, const XML_Char *name, const XML_Char **atts)
713{
714    st_entry ent;
715    wddx_stack *stack = (wddx_stack *)user_data;
716    TSRMLS_FETCH();
717    if (!strcmp((char *)name, EL_PACKET)) {
718        int i;
719
720        if (atts) for (i=0; atts[i]; i++) {
721            if (!strcmp((char *)atts[i], EL_VERSION)) {
722                /* nothing for now */
723            }
724        }
725    } else if (!strcmp((char *)name, EL_STRING)) {
726        ent.type = ST_STRING;
727        SET_STACK_VARNAME;
728
729        ZVAL_STR(&ent.data, STR_EMPTY_ALLOC());
730        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
731    } else if (!strcmp((char *)name, EL_BINARY)) {
732        ent.type = ST_BINARY;
733        SET_STACK_VARNAME;
734
735        ZVAL_STR(&ent.data, STR_EMPTY_ALLOC());
736        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
737    } else if (!strcmp((char *)name, EL_CHAR)) {
738        int i;
739
740        if (atts) for (i = 0; atts[i]; i++) {
741            if (!strcmp((char *)atts[i], EL_CHAR_CODE) && atts[++i] && atts[i][0]) {
742                char tmp_buf[2];
743
744                snprintf(tmp_buf, sizeof(tmp_buf), "%c", (char)strtol((char *)atts[i], NULL, 16));
745                php_wddx_process_data(user_data, tmp_buf, strlen(tmp_buf));
746                break;
747            }
748        }
749    } else if (!strcmp((char *)name, EL_NUMBER)) {
750        ent.type = ST_NUMBER;
751        SET_STACK_VARNAME;
752
753        ZVAL_LONG(&ent.data, 0);
754        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
755    } else if (!strcmp((char *)name, EL_BOOLEAN)) {
756        int i;
757
758        if (atts) for (i = 0; atts[i]; i++) {
759            if (!strcmp((char *)atts[i], EL_VALUE) && atts[++i] && atts[i][0]) {
760                ent.type = ST_BOOLEAN;
761                SET_STACK_VARNAME;
762
763                ZVAL_TRUE(&ent.data);
764                wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
765                php_wddx_process_data(user_data, atts[i], strlen((char *)atts[i]));
766                break;
767            }
768        }
769    } else if (!strcmp((char *)name, EL_NULL)) {
770        ent.type = ST_NULL;
771        SET_STACK_VARNAME;
772
773        ZVAL_NULL(&ent.data);
774
775        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
776    } else if (!strcmp((char *)name, EL_ARRAY)) {
777        ent.type = ST_ARRAY;
778        SET_STACK_VARNAME;
779
780        array_init(&ent.data);
781        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
782    } else if (!strcmp((char *)name, EL_STRUCT)) {
783        ent.type = ST_STRUCT;
784        SET_STACK_VARNAME;
785        array_init(&ent.data);
786        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
787    } else if (!strcmp((char *)name, EL_VAR)) {
788        int i;
789
790        if (atts) for (i = 0; atts[i]; i++) {
791            if (!strcmp((char *)atts[i], EL_NAME) && atts[++i] && atts[i][0]) {
792                stack->varname = estrdup(atts[i]);
793                break;
794            }
795        }
796    } else if (!strcmp((char *)name, EL_RECORDSET)) {
797        int i;
798
799        ent.type = ST_RECORDSET;
800        SET_STACK_VARNAME;
801        array_init(&ent.data);
802
803        if (atts) for (i = 0; atts[i]; i++) {
804            if (!strcmp((char *)atts[i], "fieldNames") && atts[++i] && atts[i][0]) {
805                zval tmp;
806                char *key;
807                char *p1, *p2, *endp;
808
809                endp = (char *)atts[i] + strlen(atts[i]);
810                p1 = (char *)atts[i];
811                while ((p2 = php_memnstr(p1, ",", sizeof(",")-1, endp)) != NULL) {
812                    key = estrndup(p1, p2 - p1);
813                    array_init(&tmp);
814                    add_assoc_zval_ex(&ent.data, key, p2 - p1, &tmp);
815                    p1 = p2 + sizeof(",")-1;
816                    efree(key);
817                }
818
819                if (p1 <= endp) {
820                    array_init(&tmp);
821                    add_assoc_zval_ex(&ent.data, p1, endp - p1, &tmp);
822                }
823
824                break;
825            }
826        }
827
828        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
829    } else if (!strcmp((char *)name, EL_FIELD)) {
830        int i;
831        st_entry ent;
832
833        ent.type = ST_FIELD;
834        ent.varname = NULL;
835        ZVAL_UNDEF(&ent.data);
836
837        if (atts) for (i = 0; atts[i]; i++) {
838            if (!strcmp((char *)atts[i], EL_NAME) && atts[++i] && atts[i][0]) {
839                st_entry *recordset;
840                zval *field;
841
842                if (wddx_stack_top(stack, (void**)&recordset) == SUCCESS &&
843                    recordset->type == ST_RECORDSET &&
844                    (field = zend_hash_str_find(Z_ARRVAL(recordset->data), (char*)atts[i], strlen(atts[i]))) != NULL) {
845                    ZVAL_COPY_VALUE(&ent.data, field);
846                }
847
848                break;
849            }
850        }
851
852        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
853    } else if (!strcmp((char *)name, EL_DATETIME)) {
854        ent.type = ST_DATETIME;
855        SET_STACK_VARNAME;
856
857        ZVAL_LONG(&ent.data, 0);
858        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
859    }
860}
861/* }}} */
862
863/* {{{ php_wddx_pop_element
864 */
865static void php_wddx_pop_element(void *user_data, const XML_Char *name)
866{
867    st_entry            *ent1, *ent2;
868    wddx_stack          *stack = (wddx_stack *)user_data;
869    HashTable           *target_hash;
870    zend_class_entry    *pce;
871    zval                obj;
872    TSRMLS_FETCH();
873
874/* OBJECTS_FIXME */
875    if (stack->top == 0) {
876        return;
877    }
878
879    if (!strcmp((char *)name, EL_STRING) || !strcmp((char *)name, EL_NUMBER) ||
880        !strcmp((char *)name, EL_BOOLEAN) || !strcmp((char *)name, EL_NULL) ||
881        !strcmp((char *)name, EL_ARRAY) || !strcmp((char *)name, EL_STRUCT) ||
882        !strcmp((char *)name, EL_RECORDSET) || !strcmp((char *)name, EL_BINARY) ||
883        !strcmp((char *)name, EL_DATETIME)) {
884        wddx_stack_top(stack, (void**)&ent1);
885
886        if (!strcmp((char *)name, EL_BINARY)) {
887            zend_string *new_str;
888
889            new_str = php_base64_decode(Z_STRVAL(ent1->data), Z_STRLEN(ent1->data));
890            zval_ptr_dtor(&ent1->data);
891            ZVAL_STR(&ent1->data, new_str);
892        }
893
894        /* Call __wakeup() method on the object. */
895        if (Z_TYPE(ent1->data) == IS_OBJECT) {
896            zval fname, retval;
897
898            ZVAL_STRING(&fname, "__wakeup");
899
900            call_user_function_ex(NULL, &ent1->data, &fname, &retval, 0, 0, 0, NULL TSRMLS_CC);
901
902            zval_ptr_dtor(&fname);
903            zval_ptr_dtor(&retval);
904        }
905
906        if (stack->top > 1) {
907            stack->top--;
908            wddx_stack_top(stack, (void**)&ent2);
909
910            /* if non-existent field */
911            if (ent2->type == ST_FIELD && Z_ISUNDEF(ent2->data)) {
912                zval_ptr_dtor(&ent1->data);
913                efree(ent1);
914                return;
915            }
916
917            if (Z_TYPE(ent2->data) == IS_ARRAY || Z_TYPE(ent2->data) == IS_OBJECT) {
918                target_hash = HASH_OF(&ent2->data);
919
920                if (ent1->varname) {
921                    if (!strcmp(ent1->varname, PHP_CLASS_NAME_VAR) &&
922                        Z_TYPE(ent1->data) == IS_STRING && Z_STRLEN(ent1->data)) {
923                        zend_bool incomplete_class = 0;
924
925                        zend_str_tolower(Z_STRVAL(ent1->data), Z_STRLEN(ent1->data));
926                        zend_string_forget_hash_val(Z_STR(ent1->data));
927                        if ((pce = zend_hash_find_ptr(EG(class_table), Z_STR(ent1->data))) == NULL) {
928                            incomplete_class = 1;
929                            pce = PHP_IC_ENTRY;
930                        }
931
932                        /* Initialize target object */
933                        object_init_ex(&obj, pce);
934
935                        /* Merge current hashtable with object's default properties */
936                        zend_hash_merge(Z_OBJPROP(obj),
937                                        Z_ARRVAL(ent2->data),
938                                        zval_add_ref, 0);
939
940                        if (incomplete_class) {
941                            php_store_class_name(&obj, Z_STRVAL(ent1->data), Z_STRLEN(ent1->data));
942                        }
943
944                        /* Clean up old array entry */
945                        zval_ptr_dtor(&ent2->data);
946
947                        /* Set stack entry to point to the newly created object */
948                        ZVAL_COPY_VALUE(&ent2->data, &obj);
949
950                        /* Clean up class name var entry */
951                        zval_ptr_dtor(&ent1->data);
952                    } else if (Z_TYPE(ent2->data) == IS_OBJECT) {
953                        zend_class_entry *old_scope = EG(scope);
954
955                        EG(scope) = Z_OBJCE(ent2->data);
956                        add_property_zval(&ent2->data, ent1->varname, &ent1->data);
957                        if Z_REFCOUNTED(ent1->data) Z_DELREF(ent1->data);
958                        EG(scope) = old_scope;
959                    } else {
960                        zend_symtable_str_update(target_hash, ent1->varname, strlen(ent1->varname), &ent1->data);
961                    }
962                    efree(ent1->varname);
963                } else  {
964                    zend_hash_next_index_insert(target_hash, &ent1->data);
965                }
966            }
967            efree(ent1);
968        } else {
969            stack->done = 1;
970        }
971    } else if (!strcmp((char *)name, EL_VAR) && stack->varname) {
972        efree(stack->varname);
973    } else if (!strcmp((char *)name, EL_FIELD)) {
974        st_entry *ent;
975        wddx_stack_top(stack, (void **)&ent);
976        efree(ent);
977        stack->top--;
978    }
979}
980/* }}} */
981
982/* {{{ php_wddx_process_data
983 */
984static void php_wddx_process_data(void *user_data, const XML_Char *s, int len)
985{
986    st_entry *ent;
987    wddx_stack *stack = (wddx_stack *)user_data;
988    TSRMLS_FETCH();
989
990    if (!wddx_stack_is_empty(stack) && !stack->done) {
991        wddx_stack_top(stack, (void**)&ent);
992        switch (ent->type) {
993            case ST_BINARY:
994            case ST_STRING:
995                if (Z_STRLEN(ent->data) == 0) {
996                    zval_ptr_dtor(&ent->data);
997                    ZVAL_STRINGL(&ent->data, (char *)s, len);
998                } else {
999                    Z_STR(ent->data) = zend_string_realloc(Z_STR(ent->data), Z_STRLEN(ent->data) + len, 0);
1000                    memcpy(Z_STRVAL(ent->data) + Z_STRLEN(ent->data) - len, (char *)s, len);
1001                    Z_STRVAL(ent->data)[Z_STRLEN(ent->data)] = '\0';
1002                }
1003                break;
1004            case ST_NUMBER:
1005                ZVAL_STRINGL(&ent->data, (char *)s, len);
1006                convert_scalar_to_number(&ent->data TSRMLS_CC);
1007                break;
1008
1009            case ST_BOOLEAN:
1010                if (!strcmp((char *)s, "true")) {
1011                    Z_LVAL(ent->data) = 1;
1012                } else if (!strcmp((char *)s, "false")) {
1013                    Z_LVAL(ent->data) = 0;
1014                } else {
1015                    stack->top--;
1016                    zval_ptr_dtor(&ent->data);
1017                    if (ent->varname)
1018                        efree(ent->varname);
1019                    efree(ent);
1020                }
1021                break;
1022
1023            case ST_DATETIME: {
1024                char *tmp;
1025
1026                tmp = emalloc(len + 1);
1027                memcpy(tmp, (char *)s, len);
1028                tmp[len] = '\0';
1029
1030                Z_LVAL(ent->data) = php_parse_date(tmp, NULL);
1031                /* date out of range < 1969 or > 2038 */
1032                if (Z_LVAL(ent->data) == -1) {
1033                    ZVAL_STRINGL(&ent->data, (char *)s, len);
1034                }
1035                efree(tmp);
1036            }
1037                break;
1038
1039            default:
1040                break;
1041        }
1042    }
1043}
1044/* }}} */
1045
1046/* {{{ php_wddx_deserialize_ex
1047 */
1048int php_wddx_deserialize_ex(char *value, int vallen, zval *return_value)
1049{
1050    wddx_stack stack;
1051    XML_Parser parser;
1052    st_entry *ent;
1053    int retval;
1054
1055    wddx_stack_init(&stack);
1056    parser = XML_ParserCreate("UTF-8");
1057
1058    XML_SetUserData(parser, &stack);
1059    XML_SetElementHandler(parser, php_wddx_push_element, php_wddx_pop_element);
1060    XML_SetCharacterDataHandler(parser, php_wddx_process_data);
1061
1062    XML_Parse(parser, value, vallen, 1);
1063
1064    XML_ParserFree(parser);
1065
1066    if (stack.top == 1) {
1067        wddx_stack_top(&stack, (void**)&ent);
1068        ZVAL_COPY(return_value, &ent->data);
1069        retval = SUCCESS;
1070    } else {
1071        retval = FAILURE;
1072    }
1073
1074    wddx_stack_destroy(&stack);
1075
1076    return retval;
1077}
1078/* }}} */
1079
1080/* {{{ proto string wddx_serialize_value(mixed var [, string comment])
1081   Creates a new packet and serializes the given value */
1082PHP_FUNCTION(wddx_serialize_value)
1083{
1084    zval *var;
1085    char *comment = NULL;
1086    size_t comment_len = 0;
1087    wddx_packet *packet;
1088
1089    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|s", &var, &comment, &comment_len) == FAILURE) {
1090        return;
1091    }
1092
1093    packet = php_wddx_constructor();
1094
1095    php_wddx_packet_start(packet, comment, comment_len);
1096    php_wddx_serialize_var(packet, var, NULL TSRMLS_CC);
1097    php_wddx_packet_end(packet);
1098    smart_str_0(packet);
1099
1100    RETVAL_STR(zend_string_copy(packet->s));
1101    php_wddx_destructor(packet);
1102}
1103/* }}} */
1104
1105/* {{{ proto string wddx_serialize_vars(mixed var_name [, mixed ...])
1106   Creates a new packet and serializes given variables into a struct */
1107PHP_FUNCTION(wddx_serialize_vars)
1108{
1109    int num_args, i;
1110    wddx_packet *packet;
1111    zval *args = NULL;
1112
1113    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &num_args) == FAILURE) {
1114        return;
1115    }
1116
1117    packet = php_wddx_constructor();
1118
1119    php_wddx_packet_start(packet, NULL, 0);
1120    php_wddx_add_chunk_static(packet, WDDX_STRUCT_S);
1121
1122    for (i=0; i<num_args; i++) {
1123        zval *arg;
1124        if (!Z_ISREF(args[i])) {
1125            arg = &args[i];
1126        } else {
1127            arg = Z_REFVAL(args[i]);
1128        }
1129        if (Z_TYPE_P(arg) != IS_ARRAY && Z_TYPE_P(arg) != IS_OBJECT) {
1130            convert_to_string_ex(arg);
1131        }
1132        php_wddx_add_var(packet, arg);
1133    }
1134
1135    php_wddx_add_chunk_static(packet, WDDX_STRUCT_E);
1136    php_wddx_packet_end(packet);
1137    smart_str_0(packet);
1138
1139    RETVAL_STR(zend_string_copy(packet->s));
1140    php_wddx_destructor(packet);
1141}
1142/* }}} */
1143
1144/* {{{ php_wddx_constructor
1145 */
1146wddx_packet *php_wddx_constructor(void)
1147{
1148    smart_str *packet;
1149
1150    packet = ecalloc(1, sizeof(smart_str));
1151
1152    return packet;
1153}
1154/* }}} */
1155
1156/* {{{ php_wddx_destructor
1157 */
1158void php_wddx_destructor(wddx_packet *packet)
1159{
1160    smart_str_free(packet);
1161    efree(packet);
1162}
1163/* }}} */
1164
1165/* {{{ proto resource wddx_packet_start([string comment])
1166   Starts a WDDX packet with optional comment and returns the packet id */
1167PHP_FUNCTION(wddx_packet_start)
1168{
1169    char *comment = NULL;
1170    size_t comment_len = 0;
1171    wddx_packet *packet;
1172
1173    comment = NULL;
1174
1175    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &comment, &comment_len) == FAILURE) {
1176        return;
1177    }
1178
1179    packet = php_wddx_constructor();
1180
1181    php_wddx_packet_start(packet, comment, comment_len);
1182    php_wddx_add_chunk_static(packet, WDDX_STRUCT_S);
1183
1184    ZEND_REGISTER_RESOURCE(return_value, packet, le_wddx);
1185}
1186/* }}} */
1187
1188/* {{{ proto string wddx_packet_end(resource packet_id)
1189   Ends specified WDDX packet and returns the string containing the packet */
1190PHP_FUNCTION(wddx_packet_end)
1191{
1192    zval *packet_id;
1193    wddx_packet *packet = NULL;
1194
1195    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &packet_id) == FAILURE) {
1196        return;
1197    }
1198
1199    ZEND_FETCH_RESOURCE(packet, wddx_packet *, packet_id, -1, "WDDX packet ID", le_wddx);
1200
1201    php_wddx_add_chunk_static(packet, WDDX_STRUCT_E);
1202
1203    php_wddx_packet_end(packet);
1204    smart_str_0(packet);
1205
1206    RETVAL_STR(zend_string_copy(packet->s));
1207
1208    zend_list_close(Z_RES_P(packet_id));
1209}
1210/* }}} */
1211
1212/* {{{ proto int wddx_add_vars(resource packet_id,  mixed var_names [, mixed ...])
1213   Serializes given variables and adds them to packet given by packet_id */
1214PHP_FUNCTION(wddx_add_vars)
1215{
1216    int num_args, i;
1217    zval *args = NULL;
1218    zval *packet_id;
1219    wddx_packet *packet = NULL;
1220
1221    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r+", &packet_id, &args, &num_args) == FAILURE) {
1222        return;
1223    }
1224
1225    if (!ZEND_FETCH_RESOURCE_NO_RETURN(packet, wddx_packet *, packet_id, -1, "WDDX packet ID", le_wddx)) {
1226        RETURN_FALSE;
1227    }
1228
1229    if (!packet) {
1230        RETURN_FALSE;
1231    }
1232
1233    for (i=0; i<num_args; i++) {
1234        zval *arg;
1235        if (!Z_ISREF(args[i])) {
1236            arg = &args[i];
1237        } else {
1238            arg = Z_REFVAL(args[i]);
1239        }
1240        if (Z_TYPE_P(arg) != IS_ARRAY && Z_TYPE_P(arg) != IS_OBJECT) {
1241            convert_to_string_ex(arg);
1242        }
1243        php_wddx_add_var(packet, arg);
1244    }
1245
1246    RETURN_TRUE;
1247}
1248/* }}} */
1249
1250/* {{{ proto mixed wddx_deserialize(mixed packet)
1251   Deserializes given packet and returns a PHP value */
1252PHP_FUNCTION(wddx_deserialize)
1253{
1254    zval *packet;
1255    php_stream *stream = NULL;
1256    zend_string *payload = NULL;
1257
1258    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &packet) == FAILURE) {
1259        return;
1260    }
1261
1262    if (Z_TYPE_P(packet) == IS_STRING) {
1263        payload = Z_STR_P(packet);
1264    } else if (Z_TYPE_P(packet) == IS_RESOURCE) {
1265        php_stream_from_zval(stream, packet);
1266        if (stream) {
1267            payload = php_stream_copy_to_mem(stream, PHP_STREAM_COPY_ALL, 0);
1268        }
1269    } else {
1270        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expecting parameter 1 to be a string or a stream");
1271        return;
1272    }
1273
1274    if (payload == NULL) {
1275        return;
1276    }
1277
1278    php_wddx_deserialize_ex(payload->val, payload->len, return_value);
1279
1280    if (stream) {
1281        efree(payload);
1282    }
1283}
1284/* }}} */
1285
1286#endif /* HAVE_LIBEXPAT */
1287
1288/*
1289 * Local variables:
1290 * tab-width: 4
1291 * c-basic-offset: 4
1292 * End:
1293 * vim600: sw=4 ts=4 fdm=marker
1294 * vim<600: sw=4 ts=4
1295 */
1296