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: Sascha Schumann <sascha@schumann.cx>                         |
16  +----------------------------------------------------------------------+
17*/
18
19/* $Id$ */
20
21#include "php.h"
22#include "ext/standard/php_var.h"
23#include "php_incomplete_class.h"
24
25/* {{{ reference-handling for unserializer: var_* */
26#define VAR_ENTRIES_MAX 1024
27#define VAR_ENTRIES_DBG 0
28
29typedef struct {
30    zval *data[VAR_ENTRIES_MAX];
31    zend_long used_slots;
32    void *next;
33} var_entries;
34
35typedef struct {
36    zval data[VAR_ENTRIES_MAX];
37    zend_long used_slots;
38    void *next;
39} var_dtor_entries;
40
41static inline void var_push(php_unserialize_data_t *var_hashx, zval *rval)
42{
43    var_entries *var_hash = (*var_hashx)->last;
44#if VAR_ENTRIES_DBG
45    fprintf(stderr, "var_push(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
46#endif
47
48    if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) {
49        var_hash = emalloc(sizeof(var_entries));
50        var_hash->used_slots = 0;
51        var_hash->next = 0;
52
53        if (!(*var_hashx)->first) {
54            (*var_hashx)->first = var_hash;
55        } else {
56            ((var_entries *) (*var_hashx)->last)->next = var_hash;
57        }
58
59        (*var_hashx)->last = var_hash;
60    }
61
62    var_hash->data[var_hash->used_slots++] = rval;
63}
64
65PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval *rval)
66{
67    var_dtor_entries *var_hash = (*var_hashx)->last_dtor;
68#if VAR_ENTRIES_DBG
69    fprintf(stderr, "var_push_dtor(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
70#endif
71
72    if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) {
73        var_hash = emalloc(sizeof(var_dtor_entries));
74        var_hash->used_slots = 0;
75        var_hash->next = 0;
76
77        if (!(*var_hashx)->first_dtor) {
78            (*var_hashx)->first_dtor = var_hash;
79        } else {
80            ((var_entries *) (*var_hashx)->last_dtor)->next = var_hash;
81        }
82
83        (*var_hashx)->last_dtor = var_hash;
84    }
85
86    ZVAL_COPY(&var_hash->data[var_hash->used_slots], rval);
87    var_hash->used_slots++;
88}
89
90//???
91#if 0
92PHPAPI void var_push_dtor_no_addref(php_unserialize_data_t *var_hashx, zval *rval)
93{
94    var_dtor_entries *var_hash = (*var_hashx)->last_dtor;
95#if VAR_ENTRIES_DBG
96    fprintf(stderr, "var_push_dtor_no_addref(%ld): %d (%d)\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval), Z_REFCOUNT_PP(rval));
97#endif
98
99    if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) {
100        var_hash = emalloc(sizeof(var_dtor_entries));
101        var_hash->used_slots = 0;
102        var_hash->next = 0;
103
104        if (!(*var_hashx)->first_dtor) {
105            (*var_hashx)->first_dtor = var_hash;
106        } else {
107            ((var_entries *) (*var_hashx)->last_dtor)->next = var_hash;
108        }
109
110        (*var_hashx)->last_dtor = var_hash;
111    }
112
113    ZVAL_COPY_VALUE(&var_hash->data[var_hash->used_slots], rval);
114    var_hash->used_slots++;
115}
116#endif
117
118PHPAPI void var_replace(php_unserialize_data_t *var_hashx, zval *ozval, zval *nzval)
119{
120    zend_long i;
121    var_entries *var_hash = (*var_hashx)->first;
122#if VAR_ENTRIES_DBG
123    fprintf(stderr, "var_replace(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(nzval));
124#endif
125
126    while (var_hash) {
127        for (i = 0; i < var_hash->used_slots; i++) {
128            if (var_hash->data[i] == ozval) {
129                var_hash->data[i] = nzval;
130                /* do not break here */
131            }
132        }
133        var_hash = var_hash->next;
134    }
135}
136
137static zval *var_access(php_unserialize_data_t *var_hashx, zend_long id)
138{
139    var_entries *var_hash = (*var_hashx)->first;
140#if VAR_ENTRIES_DBG
141    fprintf(stderr, "var_access(%ld): %ld\n", var_hash?var_hash->used_slots:-1L, id);
142#endif
143
144    while (id >= VAR_ENTRIES_MAX && var_hash && var_hash->used_slots == VAR_ENTRIES_MAX) {
145        var_hash = var_hash->next;
146        id -= VAR_ENTRIES_MAX;
147    }
148
149    if (!var_hash) return NULL;
150
151    if (id < 0 || id >= var_hash->used_slots) return NULL;
152
153    return var_hash->data[id];
154}
155
156PHPAPI void var_destroy(php_unserialize_data_t *var_hashx)
157{
158    void *next;
159    zend_long i;
160    var_entries *var_hash = (*var_hashx)->first;
161    var_dtor_entries *var_dtor_hash = (*var_hashx)->first_dtor;
162#if VAR_ENTRIES_DBG
163    fprintf(stderr, "var_destroy(%ld)\n", var_hash?var_hash->used_slots:-1L);
164#endif
165
166    while (var_hash) {
167        next = var_hash->next;
168        efree(var_hash);
169        var_hash = next;
170    }
171
172    while (var_dtor_hash) {
173        for (i = 0; i < var_dtor_hash->used_slots; i++) {
174            zval_ptr_dtor(&var_dtor_hash->data[i]);
175        }
176        next = var_dtor_hash->next;
177        efree(var_dtor_hash);
178        var_dtor_hash = next;
179    }
180}
181
182/* }}} */
183
184static zend_string *unserialize_str(const unsigned char **p, size_t len, size_t maxlen)
185{
186    size_t i, j;
187    zend_string *str = zend_string_alloc(len, 0);
188    unsigned char *end = *(unsigned char **)p+maxlen;
189
190    if (end < *p) {
191        zend_string_free(str);
192        return NULL;
193    }
194
195    for (i = 0; i < len; i++) {
196        if (*p >= end) {
197            zend_string_free(str);
198            return NULL;
199        }
200        if (**p != '\\') {
201            str->val[i] = (char)**p;
202        } else {
203            unsigned char ch = 0;
204
205            for (j = 0; j < 2; j++) {
206                (*p)++;
207                if (**p >= '0' && **p <= '9') {
208                    ch = (ch << 4) + (**p -'0');
209                } else if (**p >= 'a' && **p <= 'f') {
210                    ch = (ch << 4) + (**p -'a'+10);
211                } else if (**p >= 'A' && **p <= 'F') {
212                    ch = (ch << 4) + (**p -'A'+10);
213                } else {
214                    zend_string_free(str);
215                    return NULL;
216                }
217            }
218            str->val[i] = (char)ch;
219        }
220        (*p)++;
221    }
222    str->val[i] = 0;
223    str->len = i;
224    return str;
225}
226
227static inline int unserialize_allowed_class(zend_string *class_name, HashTable *classes)
228{
229    zend_string *lcname;
230    int res;
231    ALLOCA_FLAG(use_heap)
232
233    if(classes == NULL) {
234        return 1;
235    }
236    if(!zend_hash_num_elements(classes)) {
237        return 0;
238    }
239
240    STR_ALLOCA_ALLOC(lcname, class_name->len, use_heap);
241    zend_str_tolower_copy(lcname->val, class_name->val, class_name->len);
242    res = zend_hash_exists(classes, lcname);
243    STR_ALLOCA_FREE(lcname, use_heap);
244    return res;
245}
246
247#define YYFILL(n) do { } while (0)
248#define YYCTYPE unsigned char
249#define YYCURSOR cursor
250#define YYLIMIT limit
251#define YYMARKER marker
252
253
254/*!re2c
255uiv = [+]? [0-9]+;
256iv = [+-]? [0-9]+;
257nv = [+-]? ([0-9]* "." [0-9]+|[0-9]+ "." [0-9]*);
258nvexp = (iv | nv) [eE] [+-]? iv;
259any = [\000-\377];
260object = [OC];
261*/
262
263
264
265static inline zend_long parse_iv2(const unsigned char *p, const unsigned char **q)
266{
267    char cursor;
268    zend_long result = 0;
269    int neg = 0;
270
271    switch (*p) {
272        case '-':
273            neg++;
274            /* fall-through */
275        case '+':
276            p++;
277    }
278
279    while (1) {
280        cursor = (char)*p;
281        if (cursor >= '0' && cursor <= '9') {
282            result = result * 10 + (size_t)(cursor - (unsigned char)'0');
283        } else {
284            break;
285        }
286        p++;
287    }
288    if (q) *q = p;
289    if (neg) return -result;
290    return result;
291}
292
293static inline zend_long parse_iv(const unsigned char *p)
294{
295    return parse_iv2(p, NULL);
296}
297
298/* no need to check for length - re2c already did */
299static inline size_t parse_uiv(const unsigned char *p)
300{
301    unsigned char cursor;
302    size_t result = 0;
303
304    if (*p == '+') {
305        p++;
306    }
307
308    while (1) {
309        cursor = *p;
310        if (cursor >= '0' && cursor <= '9') {
311            result = result * 10 + (size_t)(cursor - (unsigned char)'0');
312        } else {
313            break;
314        }
315        p++;
316    }
317    return result;
318}
319
320#define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash, HashTable *classes TSRMLS_DC
321#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash, classes TSRMLS_CC
322
323static inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, int objprops)
324{
325    while (elements-- > 0) {
326        zval key, *data, d, *old_data;
327
328        ZVAL_UNDEF(&key);
329
330        if (!php_var_unserialize_ex(&key, p, max, NULL, classes TSRMLS_CC)) {
331            zval_dtor(&key);
332            return 0;
333        }
334
335        if (Z_TYPE(key) != IS_LONG && Z_TYPE(key) != IS_STRING) {
336            zval_dtor(&key);
337            return 0;
338        }
339
340        data = NULL;
341        ZVAL_UNDEF(&d);
342
343        if (!objprops) {
344            switch (Z_TYPE(key)) {
345            case IS_LONG:
346                if ((old_data = zend_hash_index_find(ht, Z_LVAL(key))) != NULL) {
347                    //??? update hash
348                    var_push_dtor(var_hash, old_data);
349                }
350                data = zend_hash_index_update(ht, Z_LVAL(key), &d);
351                break;
352            case IS_STRING:
353                if ((old_data = zend_symtable_find(ht, Z_STR(key))) != NULL) {
354                    //??? update hash
355                    var_push_dtor(var_hash, old_data);
356                }
357                data = zend_symtable_update(ht, Z_STR(key), &d);
358                break;
359            }
360        } else {
361            /* object properties should include no integers */
362            convert_to_string(&key);
363//???
364#if 1
365            data = zend_hash_update_ind(ht, Z_STR(key), &d);
366#else
367            if ((data = zend_hash_find(ht, Z_STR(key))) != NULL) {
368                if (Z_TYPE_P(data) == IS_INDIRECT) {
369                    data = Z_INDIRECT_P(data);
370                }
371                zval_ptr_dtor(data);
372//???               var_push_dtor(var_hash, data);
373                ZVAL_UNDEF(data);
374            } else {
375                data = zend_hash_update(ht, Z_STR(key), &d);
376            }
377#endif
378        }
379
380        zval_dtor(&key);
381
382        if (!php_var_unserialize_ex(data, p, max, var_hash, classes TSRMLS_CC)) {
383            return 0;
384        }
385
386        if (elements && *(*p-1) != ';' && *(*p-1) != '}') {
387            (*p)--;
388            return 0;
389        }
390    }
391
392    return 1;
393}
394
395static inline int finish_nested_data(UNSERIALIZE_PARAMETER)
396{
397    if (*((*p)++) == '}')
398        return 1;
399
400#if SOMETHING_NEW_MIGHT_LEAD_TO_CRASH_ENABLE_IF_YOU_ARE_BRAVE
401    zval_ptr_dtor(rval);
402#endif
403    return 0;
404}
405
406static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce)
407{
408    zend_long datalen;
409
410    datalen = parse_iv2((*p) + 2, p);
411
412    (*p) += 2;
413
414    if (datalen < 0 || (max - (*p)) <= datalen) {
415        zend_error(E_WARNING, "Insufficient data for unserializing - %pd required, %pd present", datalen, (zend_long)(max - (*p)));
416        return 0;
417    }
418
419    if (ce->unserialize == NULL) {
420        zend_error(E_WARNING, "Class %s has no unserializer", ce->name->val);
421        object_init_ex(rval, ce);
422    } else if (ce->unserialize(rval, ce, (const unsigned char*)*p, datalen, (zend_unserialize_data *)var_hash TSRMLS_CC) != SUCCESS) {
423        return 0;
424    }
425
426    (*p) += datalen;
427
428    return finish_nested_data(UNSERIALIZE_PASSTHRU);
429}
430
431static inline zend_long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce)
432{
433    zend_long elements;
434
435    elements = parse_iv2((*p) + 2, p);
436
437    (*p) += 2;
438
439    if (ce->serialize == NULL) {
440        object_init_ex(rval, ce);
441    } else {
442        /* If this class implements Serializable, it should not land here but in object_custom(). The passed string
443        obviously doesn't descend from the regular serializer. */
444        zend_error(E_WARNING, "Erroneous data format for unserializing '%s'", ce->name->val);
445        return 0;
446    }
447
448    return elements;
449}
450
451#ifdef PHP_WIN32
452# pragma optimize("", off)
453#endif
454static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements)
455{
456    zval retval;
457    zval fname;
458
459    if (Z_TYPE_P(rval) != IS_OBJECT) {
460        return 0;
461    }
462
463    //??? TODO: resize before
464    if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_OBJPROP_P(rval), elements, 1)) {
465        return 0;
466    }
467
468    ZVAL_DEREF(rval);
469    if (Z_OBJCE_P(rval) != PHP_IC_ENTRY &&
470        zend_hash_str_exists(&Z_OBJCE_P(rval)->function_table, "__wakeup", sizeof("__wakeup")-1)) {
471        ZVAL_STRINGL(&fname, "__wakeup", sizeof("__wakeup") - 1);
472        BG(serialize_lock)++;
473        call_user_function_ex(CG(function_table), rval, &fname, &retval, 0, 0, 1, NULL TSRMLS_CC);
474        BG(serialize_lock)--;
475        zval_dtor(&fname);
476        zval_dtor(&retval);
477    }
478
479    if (EG(exception)) {
480        return 0;
481    }
482
483    return finish_nested_data(UNSERIALIZE_PASSTHRU);
484
485}
486#ifdef PHP_WIN32
487# pragma optimize("", on)
488#endif
489
490PHPAPI int php_var_unserialize(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash TSRMLS_DC)
491{
492    HashTable *classes = NULL;
493    return php_var_unserialize_ex(UNSERIALIZE_PASSTHRU);
494}
495
496
497PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER)
498{
499    const unsigned char *cursor, *limit, *marker, *start;
500    zval *rval_ref;
501
502    limit = max;
503    cursor = *p;
504
505    if (YYCURSOR >= YYLIMIT) {
506        return 0;
507    }
508
509    if (var_hash && (*p)[0] != 'R') {
510        var_push(var_hash, rval);
511    }
512
513    start = cursor;
514
515/*!re2c
516
517"R:" iv ";"     {
518    zend_long id;
519
520    *p = YYCURSOR;
521    if (!var_hash) return 0;
522
523    id = parse_iv(start + 2) - 1;
524    if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) {
525        return 0;
526    }
527
528    zval_ptr_dtor(rval);
529    if (Z_ISREF_P(rval_ref)) {
530        ZVAL_COPY(rval, rval_ref);
531    } else {
532        ZVAL_NEW_REF(rval_ref, rval_ref);
533        ZVAL_COPY(rval, rval_ref);
534    }
535
536    return 1;
537}
538
539"r:" iv ";"     {
540    zend_long id;
541
542    *p = YYCURSOR;
543    if (!var_hash) return 0;
544
545    id = parse_iv(start + 2) - 1;
546    if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) {
547        return 0;
548    }
549
550//???
551//???   if (rval == rval_ref) return 0;
552
553//???   if (!ZVAL_IS_UNDEF(rval)) {
554//???       var_push_dtor_no_addref(var_hash, rval);
555//???   }
556    ZVAL_COPY(rval, rval_ref);
557//???   Z_UNSET_ISREF_PP(rval);
558
559    return 1;
560}
561
562"N;"    {
563    *p = YYCURSOR;
564    ZVAL_NULL(rval);
565    return 1;
566}
567
568"b:" [01] ";"   {
569    *p = YYCURSOR;
570    ZVAL_BOOL(rval, parse_iv(start + 2));
571    return 1;
572}
573
574"i:" iv ";" {
575#if SIZEOF_ZEND_LONG == 4
576    int digits = YYCURSOR - start - 3;
577
578    if (start[2] == '-' || start[2] == '+') {
579        digits--;
580    }
581
582    /* Use double for large zend_long values that were serialized on a 64-bit system */
583    if (digits >= MAX_LENGTH_OF_LONG - 1) {
584        if (digits == MAX_LENGTH_OF_LONG - 1) {
585            int cmp = strncmp((char*)YYCURSOR - MAX_LENGTH_OF_LONG, long_min_digits, MAX_LENGTH_OF_LONG - 1);
586
587            if (!(cmp < 0 || (cmp == 0 && start[2] == '-'))) {
588                goto use_double;
589            }
590        } else {
591            goto use_double;
592        }
593    }
594#endif
595    *p = YYCURSOR;
596    ZVAL_LONG(rval, parse_iv(start + 2));
597    return 1;
598}
599
600"d:" ("NAN" | "-"? "INF") ";"   {
601    *p = YYCURSOR;
602
603    if (!strncmp((char*)start + 2, "NAN", 3)) {
604        ZVAL_DOUBLE(rval, php_get_nan());
605    } else if (!strncmp((char*)start + 2, "INF", 3)) {
606        ZVAL_DOUBLE(rval, php_get_inf());
607    } else if (!strncmp((char*)start + 2, "-INF", 4)) {
608        ZVAL_DOUBLE(rval, -php_get_inf());
609    } else {
610        ZVAL_NULL(rval);
611    }
612
613    return 1;
614}
615
616"d:" (iv | nv | nvexp) ";"  {
617#if SIZEOF_ZEND_LONG == 4
618use_double:
619#endif
620    *p = YYCURSOR;
621    ZVAL_DOUBLE(rval, zend_strtod((const char *)start + 2, NULL));
622    return 1;
623}
624
625"s:" uiv ":" ["]    {
626    size_t len, maxlen;
627    char *str;
628
629    len = parse_uiv(start + 2);
630    maxlen = max - YYCURSOR;
631    if (maxlen < len) {
632        *p = start + 2;
633        return 0;
634    }
635
636    str = (char*)YYCURSOR;
637
638    YYCURSOR += len;
639
640    if (*(YYCURSOR) != '"') {
641        *p = YYCURSOR;
642        return 0;
643    }
644
645    YYCURSOR += 2;
646    *p = YYCURSOR;
647
648    ZVAL_STRINGL(rval, str, len);
649    return 1;
650}
651
652"S:" uiv ":" ["]    {
653    size_t len, maxlen;
654    zend_string *str;
655
656    len = parse_uiv(start + 2);
657    maxlen = max - YYCURSOR;
658    if (maxlen < len) {
659        *p = start + 2;
660        return 0;
661    }
662
663    if ((str = unserialize_str(&YYCURSOR, len, maxlen)) == NULL) {
664        return 0;
665    }
666
667    if (*(YYCURSOR) != '"') {
668        zend_string_free(str);
669        *p = YYCURSOR;
670        return 0;
671    }
672
673    YYCURSOR += 2;
674    *p = YYCURSOR;
675
676    ZVAL_STR(rval, str);
677    return 1;
678}
679
680"a:" uiv ":" "{" {
681    zend_long elements = parse_iv(start + 2);
682    /* use iv() not uiv() in order to check data range */
683    *p = YYCURSOR;
684
685    if (elements < 0) {
686        return 0;
687    }
688
689    array_init_size(rval, elements);
690//??? we can't convert from packed to hash during unserialization, because
691//??? reference to some zvals might be keept in var_hash (to support references)
692    zend_hash_real_init(Z_ARRVAL_P(rval), 0);
693
694    if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_P(rval), elements, 0)) {
695        return 0;
696    }
697
698    return finish_nested_data(UNSERIALIZE_PASSTHRU);
699}
700
701"o:" iv ":" ["] {
702
703//???   INIT_PZVAL(rval);
704
705    return object_common2(UNSERIALIZE_PASSTHRU,
706            object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR));
707}
708
709object ":" uiv ":" ["]  {
710    size_t len, len2, len3, maxlen;
711    zend_long elements;
712    char *str;
713    zend_string *class_name;
714    zend_class_entry *ce;
715    int incomplete_class = 0;
716
717    int custom_object = 0;
718
719    zval user_func;
720    zval retval;
721    zval args[1];
722
723    if (*start == 'C') {
724        custom_object = 1;
725    }
726
727//???   INIT_PZVAL(rval);
728    len2 = len = parse_uiv(start + 2);
729    maxlen = max - YYCURSOR;
730    if (maxlen < len || len == 0) {
731        *p = start + 2;
732        return 0;
733    }
734
735    str = (char*)YYCURSOR;
736
737    YYCURSOR += len;
738
739    if (*(YYCURSOR) != '"') {
740        *p = YYCURSOR;
741        return 0;
742    }
743    if (*(YYCURSOR+1) != ':') {
744        *p = YYCURSOR+1;
745        return 0;
746    }
747
748    len3 = strspn(str, "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\177\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377\\");
749    if (len3 != len)
750    {
751        *p = YYCURSOR + len3 - len;
752        return 0;
753    }
754
755    class_name = zend_string_init(str, len, 0);
756
757    do {
758        if(!unserialize_allowed_class(class_name, classes)) {
759            incomplete_class = 1;
760            ce = PHP_IC_ENTRY;
761            break;
762        }
763
764        /* Try to find class directly */
765        BG(serialize_lock)++;
766        ce = zend_lookup_class(class_name TSRMLS_CC);
767        if (ce) {
768            BG(serialize_lock)--;
769            if (EG(exception)) {
770                zend_string_release(class_name);
771                return 0;
772            }
773            break;
774        }
775        BG(serialize_lock)--;
776
777        if (EG(exception)) {
778            zend_string_release(class_name);
779            return 0;
780        }
781
782        /* Check for unserialize callback */
783        if ((PG(unserialize_callback_func) == NULL) || (PG(unserialize_callback_func)[0] == '\0')) {
784            incomplete_class = 1;
785            ce = PHP_IC_ENTRY;
786            break;
787        }
788
789        /* Call unserialize callback */
790        ZVAL_STRING(&user_func, PG(unserialize_callback_func));
791
792        ZVAL_STR_COPY(&args[0], class_name);
793        BG(serialize_lock)++;
794        if (call_user_function_ex(CG(function_table), NULL, &user_func, &retval, 1, args, 0, NULL TSRMLS_CC) != SUCCESS) {
795            BG(serialize_lock)--;
796            if (EG(exception)) {
797                zend_string_release(class_name);
798                zval_ptr_dtor(&user_func);
799                zval_ptr_dtor(&args[0]);
800                return 0;
801            }
802            php_error_docref(NULL TSRMLS_CC, E_WARNING, "defined (%s) but not found", Z_STRVAL(user_func));
803            incomplete_class = 1;
804            ce = PHP_IC_ENTRY;
805            zval_ptr_dtor(&user_func);
806            zval_ptr_dtor(&args[0]);
807            break;
808        }
809        BG(serialize_lock)--;
810        zval_ptr_dtor(&retval);
811        if (EG(exception)) {
812            zend_string_release(class_name);
813            zval_ptr_dtor(&user_func);
814            zval_ptr_dtor(&args[0]);
815            return 0;
816        }
817
818        /* The callback function may have defined the class */
819        if ((ce = zend_lookup_class(class_name TSRMLS_CC)) == NULL) {
820            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Function %s() hasn't defined the class it was called for", Z_STRVAL(user_func));
821            incomplete_class = 1;
822            ce = PHP_IC_ENTRY;
823        }
824
825        zval_ptr_dtor(&user_func);
826        zval_ptr_dtor(&args[0]);
827        break;
828    } while (1);
829
830    *p = YYCURSOR;
831
832    if (custom_object) {
833        int ret;
834
835        ret = object_custom(UNSERIALIZE_PASSTHRU, ce);
836
837        if (ret && incomplete_class) {
838            php_store_class_name(rval, class_name->val, len2);
839        }
840        zend_string_release(class_name);
841        return ret;
842    }
843
844    elements = object_common1(UNSERIALIZE_PASSTHRU, ce);
845
846    if (incomplete_class) {
847        php_store_class_name(rval, class_name->val, len2);
848    }
849    zend_string_release(class_name);
850
851    return object_common2(UNSERIALIZE_PASSTHRU, elements);
852}
853
854"}" {
855    /* this is the case where we have less data than planned */
856    php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unexpected end of serialized data");
857    return 0; /* not sure if it should be 0 or 1 here? */
858}
859
860any { return 0; }
861
862*/
863
864    return 0;
865}
866