1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2015 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   | Authors: Ard Biesheuvel <a.k.biesheuvel@its.tudelft.nl>              |
16   +----------------------------------------------------------------------+
17 */
18
19#ifdef HAVE_CONFIG_H
20#include "config.h"
21#endif
22
23#include "php.h"
24
25#if HAVE_IBASE
26
27#include "php_interbase.h"
28#include "php_ibase_includes.h"
29
30#define BLOB_CLOSE      1
31#define BLOB_CANCEL     2
32
33static int le_blob;
34
35static void _php_ibase_free_blob(zend_resource *rsrc) /* {{{ */
36{
37    ibase_blob *ib_blob = (ibase_blob *)rsrc->ptr;
38
39    if (ib_blob->bl_handle != NULL) { /* blob open*/
40        if (isc_cancel_blob(IB_STATUS, &ib_blob->bl_handle)) {
41            _php_ibase_module_error("You can lose data. Close any blob after reading from or "
42                "writing to it. Use ibase_blob_close() before calling ibase_close()");
43        }
44    }
45    efree(ib_blob);
46}
47/* }}} */
48
49void php_ibase_blobs_minit(INIT_FUNC_ARGS) /* {{{ */
50{
51    le_blob = zend_register_list_destructors_ex(_php_ibase_free_blob, NULL,
52        "interbase blob", module_number);
53}
54/* }}} */
55
56int _php_ibase_string_to_quad(char const *id, ISC_QUAD *qd) /* {{{ */
57{
58    /* shortcut for most common case */
59    if (sizeof(ISC_QUAD) == sizeof(ISC_UINT64)) {
60        return sscanf(id, BLOB_ID_MASK, (ISC_UINT64 *) qd);
61    } else {
62        ISC_UINT64 res;
63        if (sscanf(id, BLOB_ID_MASK, &res)) {
64            qd->gds_quad_high = (ISC_LONG) (res >> 0x20);
65            qd->gds_quad_low = (ISC_LONG) (res & 0xFFFFFFFF);
66            return 1;
67        }
68        return 0;
69    }
70}
71/* }}} */
72
73zend_string *_php_ibase_quad_to_string(ISC_QUAD const qd) /* {{{ */
74{
75    /* shortcut for most common case */
76    if (sizeof(ISC_QUAD) == sizeof(ISC_UINT64)) {
77        return strpprintf(BLOB_ID_LEN+1, "0x%0*" LL_MASK "x", 16, *(ISC_UINT64*)(void *) &qd);
78    } else {
79        ISC_UINT64 res = ((ISC_UINT64) qd.gds_quad_high << 0x20) | qd.gds_quad_low;
80        return strpprintf(BLOB_ID_LEN+1, "0x%0*" LL_MASK "x", 16, res);
81    }
82}
83/* }}} */
84
85typedef struct { /* {{{ */
86    ISC_LONG  max_segment;      /* Length of longest segment */
87    ISC_LONG  num_segments;     /* Total number of segments */
88    ISC_LONG  total_length;     /* Total length of blob */
89    int       bl_stream;        /* blob is stream ? */
90/* }}} */
91} IBASE_BLOBINFO;
92
93int _php_ibase_blob_get(zval *return_value, ibase_blob *ib_blob, unsigned long max_len) /* {{{ */
94{
95    if (ib_blob->bl_qd.gds_quad_high || ib_blob->bl_qd.gds_quad_low) { /*not null ?*/
96
97        ISC_STATUS stat;
98        zend_string *bl_data;
99        unsigned long cur_len;
100        unsigned short seg_len;
101
102        bl_data = zend_string_alloc(max_len, 0);
103
104        for (cur_len = stat = 0; (stat == 0 || stat == isc_segment) && cur_len < max_len; cur_len += seg_len) {
105
106            unsigned short chunk_size = (max_len-cur_len) > USHRT_MAX ? USHRT_MAX
107                : (unsigned short)(max_len-cur_len);
108
109            stat = isc_get_segment(IB_STATUS, &ib_blob->bl_handle, &seg_len, chunk_size, &ZSTR_VAL(bl_data)[cur_len]);
110        }
111
112        if (IB_STATUS[0] == 1 && (stat != 0 && stat != isc_segstr_eof && stat != isc_segment)) {
113            zend_string_free(bl_data);
114            _php_ibase_error();
115            return FAILURE;
116        }
117        ZSTR_VAL(bl_data)[cur_len] = '\0';
118        ZSTR_LEN(bl_data) = cur_len;
119        RETVAL_NEW_STR(bl_data);
120    } else { /* null blob */
121        RETVAL_EMPTY_STRING(); /* empty string */
122    }
123    return SUCCESS;
124}
125/* }}} */
126
127int _php_ibase_blob_add(zval *string_arg, ibase_blob *ib_blob) /* {{{ */
128{
129    unsigned long put_cnt = 0, rem_cnt;
130    unsigned short chunk_size;
131
132    convert_to_string_ex(string_arg);
133
134    for (rem_cnt = Z_STRLEN_P(string_arg); rem_cnt > 0; rem_cnt -= chunk_size)  {
135
136        chunk_size = rem_cnt > USHRT_MAX ? USHRT_MAX : (unsigned short)rem_cnt;
137
138        if (isc_put_segment(IB_STATUS, &ib_blob->bl_handle, chunk_size, &Z_STRVAL_P(string_arg)[put_cnt] )) {
139            _php_ibase_error();
140            return FAILURE;
141        }
142        put_cnt += chunk_size;
143    }
144    return SUCCESS;
145}
146/* }}} */
147
148static int _php_ibase_blob_info(isc_blob_handle bl_handle, IBASE_BLOBINFO *bl_info) /* {{{ */
149{
150    static char bl_items[] = {
151        isc_info_blob_num_segments,
152        isc_info_blob_max_segment,
153        isc_info_blob_total_length,
154        isc_info_blob_type
155    };
156
157    char bl_inf[sizeof(long)*8], *p;
158
159    bl_info->max_segment = 0;
160    bl_info->num_segments = 0;
161    bl_info->total_length = 0;
162    bl_info->bl_stream = 0;
163
164    if (isc_blob_info(IB_STATUS, &bl_handle, sizeof(bl_items), bl_items, sizeof(bl_inf), bl_inf)) {
165        _php_ibase_error();
166        return FAILURE;
167    }
168
169    for (p = bl_inf; *p != isc_info_end && p < bl_inf + sizeof(bl_inf);) {
170        unsigned short item_len;
171        int item = *p++;
172
173        item_len = (short) isc_vax_integer(p, 2);
174        p += 2;
175        switch (item) {
176            case isc_info_blob_num_segments:
177                bl_info->num_segments = isc_vax_integer(p, item_len);
178                break;
179            case isc_info_blob_max_segment:
180                bl_info->max_segment = isc_vax_integer(p, item_len);
181                break;
182            case isc_info_blob_total_length:
183                bl_info->total_length = isc_vax_integer(p, item_len);
184                break;
185            case isc_info_blob_type:
186                bl_info->bl_stream = isc_vax_integer(p, item_len);
187                break;
188            case isc_info_end:
189                break;
190            case isc_info_truncated:
191            case isc_info_error:  /* hmm. don't think so...*/
192                _php_ibase_module_error("PHP module internal error");
193                return FAILURE;
194        } /* switch */
195        p += item_len;
196    } /* for */
197    return SUCCESS;
198}
199/* }}} */
200
201/* {{{ proto resource ibase_blob_create([resource link_identifier])
202   Create blob for adding data */
203PHP_FUNCTION(ibase_blob_create)
204{
205    zval *link = NULL;
206    ibase_db_link *ib_link;
207    ibase_trans *trans = NULL;
208    ibase_blob *ib_blob;
209
210    RESET_ERRMSG;
211
212    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|r", &link)) {
213        RETURN_FALSE;
214    }
215
216    PHP_IBASE_LINK_TRANS(link, ib_link, trans);
217
218    ib_blob = (ibase_blob *) emalloc(sizeof(ibase_blob));
219    ib_blob->bl_handle = NULL;
220    ib_blob->type = BLOB_INPUT;
221
222    if (isc_create_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob->bl_handle, &ib_blob->bl_qd)) {
223        _php_ibase_error();
224        efree(ib_blob);
225        RETURN_FALSE;
226    }
227
228    RETVAL_RES(zend_register_resource(ib_blob, le_blob));
229    Z_TRY_ADDREF_P(return_value);
230}
231/* }}} */
232
233/* {{{ proto resource ibase_blob_open([ resource link_identifier, ] string blob_id)
234   Open blob for retrieving data parts */
235PHP_FUNCTION(ibase_blob_open)
236{
237    char *blob_id;
238    size_t blob_id_len;
239    zval *link = NULL;
240    ibase_db_link *ib_link;
241    ibase_trans *trans = NULL;
242    ibase_blob *ib_blob;
243
244    RESET_ERRMSG;
245
246    switch (ZEND_NUM_ARGS()) {
247        default:
248            WRONG_PARAM_COUNT;
249        case 1:
250            if (FAILURE == zend_parse_parameters(1, "s", &blob_id, &blob_id_len)) {
251                RETURN_FALSE;
252            }
253            break;
254        case 2:
255            if (FAILURE == zend_parse_parameters(2, "rs", &link, &blob_id, &blob_id_len)) {
256                RETURN_FALSE;
257            }
258            break;
259    }
260
261    PHP_IBASE_LINK_TRANS(link, ib_link, trans);
262
263    ib_blob = (ibase_blob *) emalloc(sizeof(ibase_blob));
264    ib_blob->bl_handle = NULL;
265    ib_blob->type = BLOB_OUTPUT;
266
267    do {
268        if (! _php_ibase_string_to_quad(blob_id, &ib_blob->bl_qd)) {
269            _php_ibase_module_error("String is not a BLOB ID");
270            break;
271        }
272
273        if (isc_open_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob->bl_handle,
274                &ib_blob->bl_qd)) {
275            _php_ibase_error();
276            break;
277        }
278
279        RETVAL_RES(zend_register_resource(ib_blob, le_blob));
280        Z_TRY_ADDREF_P(return_value);
281        return;
282
283    } while (0);
284
285    efree(ib_blob);
286    RETURN_FALSE;
287}
288/* }}} */
289
290/* {{{ proto bool ibase_blob_add(resource blob_handle, string data)
291   Add data into created blob */
292PHP_FUNCTION(ibase_blob_add)
293{
294    zval *blob_arg, *string_arg;
295    ibase_blob *ib_blob;
296
297    RESET_ERRMSG;
298
299    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &blob_arg, &string_arg)) {
300        return;
301    }
302
303    ib_blob = (ibase_blob *)zend_fetch_resource_ex(blob_arg, "Interbase blob", le_blob);
304
305    if (ib_blob->type != BLOB_INPUT) {
306        _php_ibase_module_error("BLOB is not open for input");
307        RETURN_FALSE;
308    }
309
310    if (_php_ibase_blob_add(string_arg, ib_blob) != SUCCESS) {
311        RETURN_FALSE;
312    }
313}
314/* }}} */
315
316/* {{{ proto string ibase_blob_get(resource blob_handle, int len)
317   Get len bytes data from open blob */
318PHP_FUNCTION(ibase_blob_get)
319{
320    zval *blob_arg;
321    unsigned long len_arg;
322    ibase_blob *ib_blob;
323
324    RESET_ERRMSG;
325
326    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &blob_arg, &len_arg)) {
327        return;
328    }
329
330    ib_blob = (ibase_blob *)zend_fetch_resource_ex(blob_arg, "Interbase blob", le_blob);
331
332    if (ib_blob->type != BLOB_OUTPUT) {
333        _php_ibase_module_error("BLOB is not open for output");
334        RETURN_FALSE;
335    }
336
337    if (_php_ibase_blob_get(return_value, ib_blob, len_arg) != SUCCESS) {
338        RETURN_FALSE;
339    }
340}
341/* }}} */
342
343static void _php_ibase_blob_end(INTERNAL_FUNCTION_PARAMETERS, int bl_end) /* {{{ */
344{
345    zval *blob_arg;
346    ibase_blob *ib_blob;
347
348    RESET_ERRMSG;
349
350    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "r", &blob_arg)) {
351        return;
352    }
353
354    ib_blob = (ibase_blob *)zend_fetch_resource_ex(blob_arg, "Interbase blob", le_blob);
355
356    if (bl_end == BLOB_CLOSE) { /* return id here */
357
358        if (ib_blob->bl_qd.gds_quad_high || ib_blob->bl_qd.gds_quad_low) { /*not null ?*/
359            if (isc_close_blob(IB_STATUS, &ib_blob->bl_handle)) {
360                _php_ibase_error();
361                RETURN_FALSE;
362            }
363        }
364        ib_blob->bl_handle = NULL;
365
366        RETVAL_NEW_STR(_php_ibase_quad_to_string(ib_blob->bl_qd));
367    } else { /* discard created blob */
368        if (isc_cancel_blob(IB_STATUS, &ib_blob->bl_handle)) {
369            _php_ibase_error();
370            RETURN_FALSE;
371        }
372        ib_blob->bl_handle = NULL;
373        RETVAL_TRUE;
374    }
375    zend_list_delete(Z_RES_P(blob_arg));
376}
377/* }}} */
378
379/* {{{ proto string ibase_blob_close(resource blob_handle)
380   Close blob */
381PHP_FUNCTION(ibase_blob_close)
382{
383    _php_ibase_blob_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, BLOB_CLOSE);
384}
385/* }}} */
386
387/* {{{ proto bool ibase_blob_cancel(resource blob_handle)
388   Cancel creating blob */
389PHP_FUNCTION(ibase_blob_cancel)
390{
391    _php_ibase_blob_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, BLOB_CANCEL);
392}
393/* }}} */
394
395/* {{{ proto array ibase_blob_info([ resource link_identifier, ] string blob_id)
396   Return blob length and other useful info */
397PHP_FUNCTION(ibase_blob_info)
398{
399    char *blob_id;
400    size_t blob_id_len;
401    zval *link = NULL;
402    ibase_db_link *ib_link;
403    ibase_trans *trans = NULL;
404    ibase_blob ib_blob = { NULL, BLOB_INPUT };
405    IBASE_BLOBINFO bl_info;
406
407    RESET_ERRMSG;
408
409    switch (ZEND_NUM_ARGS()) {
410        default:
411            WRONG_PARAM_COUNT;
412        case 1:
413            if (FAILURE == zend_parse_parameters(1, "s", &blob_id, &blob_id_len)) {
414                RETURN_FALSE;
415            }
416            break;
417        case 2:
418            if (FAILURE == zend_parse_parameters(2, "rs", &link, &blob_id, &blob_id_len)) {
419                RETURN_FALSE;
420            }
421            break;
422    }
423
424    PHP_IBASE_LINK_TRANS(link, ib_link, trans);
425
426    if (! _php_ibase_string_to_quad(blob_id, &ib_blob.bl_qd)) {
427        _php_ibase_module_error("Unrecognized BLOB ID");
428        RETURN_FALSE;
429    }
430
431    if (ib_blob.bl_qd.gds_quad_high || ib_blob.bl_qd.gds_quad_low) { /* not null ? */
432        if (isc_open_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob.bl_handle,
433                &ib_blob.bl_qd)) {
434            _php_ibase_error();
435            RETURN_FALSE;
436        }
437
438        if (_php_ibase_blob_info(ib_blob.bl_handle, &bl_info)) {
439            RETURN_FALSE;
440        }
441        if (isc_close_blob(IB_STATUS, &ib_blob.bl_handle)) {
442            _php_ibase_error();
443            RETURN_FALSE;
444        }
445    } else { /* null blob, all values to zero */
446        bl_info.max_segment = 0;
447        bl_info.num_segments = 0;
448        bl_info.total_length = 0;
449        bl_info.bl_stream = 0;
450    }
451
452    array_init(return_value);
453
454    add_index_long(return_value, 0, bl_info.total_length);
455    add_assoc_long(return_value, "length", bl_info.total_length);
456
457    add_index_long(return_value, 1, bl_info.num_segments);
458    add_assoc_long(return_value, "numseg", bl_info.num_segments);
459
460    add_index_long(return_value, 2, bl_info.max_segment);
461    add_assoc_long(return_value, "maxseg", bl_info.max_segment);
462
463    add_index_bool(return_value, 3, bl_info.bl_stream);
464    add_assoc_bool(return_value, "stream", bl_info.bl_stream);
465
466    add_index_bool(return_value, 4, (!ib_blob.bl_qd.gds_quad_high && !ib_blob.bl_qd.gds_quad_low));
467    add_assoc_bool(return_value, "isnull", (!ib_blob.bl_qd.gds_quad_high && !ib_blob.bl_qd.gds_quad_low));
468}
469/* }}} */
470
471/* {{{ proto bool ibase_blob_echo([ resource link_identifier, ] string blob_id)
472   Output blob contents to browser */
473PHP_FUNCTION(ibase_blob_echo)
474{
475    char *blob_id;
476    size_t blob_id_len;
477    zval *link = NULL;
478    ibase_db_link *ib_link;
479    ibase_trans *trans = NULL;
480    ibase_blob ib_blob_id = { NULL, BLOB_OUTPUT  };
481    char bl_data[IBASE_BLOB_SEG];
482    unsigned short seg_len;
483
484    RESET_ERRMSG;
485
486    switch (ZEND_NUM_ARGS()) {
487        default:
488            WRONG_PARAM_COUNT;
489        case 1:
490            if (FAILURE == zend_parse_parameters(1, "s", &blob_id, &blob_id_len)) {
491                RETURN_FALSE;
492            }
493            break;
494        case 2:
495            if (FAILURE == zend_parse_parameters(2, "rs", &link, &blob_id, &blob_id_len)) {
496                RETURN_FALSE;
497            }
498            break;
499    }
500
501    PHP_IBASE_LINK_TRANS(link, ib_link, trans);
502
503    if (! _php_ibase_string_to_quad(blob_id, &ib_blob_id.bl_qd)) {
504        _php_ibase_module_error("Unrecognized BLOB ID");
505        RETURN_FALSE;
506    }
507
508    do {
509        if (isc_open_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob_id.bl_handle,
510                &ib_blob_id.bl_qd)) {
511            break;
512        }
513
514        while (!isc_get_segment(IB_STATUS, &ib_blob_id.bl_handle, &seg_len, sizeof(bl_data), bl_data)
515                || IB_STATUS[1] == isc_segment) {
516            PHPWRITE(bl_data, seg_len);
517        }
518
519        if (IB_STATUS[0] && (IB_STATUS[1] != isc_segstr_eof)) {
520            break;
521        }
522
523        if (isc_close_blob(IB_STATUS, &ib_blob_id.bl_handle)) {
524            break;
525        }
526        RETURN_TRUE;
527    } while (0);
528
529    _php_ibase_error();
530    RETURN_FALSE;
531}
532/* }}} */
533
534/* {{{ proto string ibase_blob_import([ resource link_identifier, ] resource file)
535   Create blob, copy file in it, and close it */
536PHP_FUNCTION(ibase_blob_import)
537{
538    zval *link = NULL, *file;
539    int size;
540    unsigned short b;
541    ibase_blob ib_blob = { NULL, 0 };
542    ibase_db_link *ib_link;
543    ibase_trans *trans = NULL;
544    char bl_data[IBASE_BLOB_SEG];
545    php_stream *stream;
546
547    RESET_ERRMSG;
548
549    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "r|r",
550            (ZEND_NUM_ARGS()-1) ? &link : &file, &file)) {
551        RETURN_FALSE;
552    }
553
554    PHP_IBASE_LINK_TRANS(link, ib_link, trans);
555
556    php_stream_from_zval(stream, file);
557
558    do {
559        if (isc_create_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob.bl_handle,
560                &ib_blob.bl_qd)) {
561            break;
562        }
563
564        for (size = 0; (b = php_stream_read(stream, bl_data, sizeof(bl_data))); size += b) {
565            if (isc_put_segment(IB_STATUS, &ib_blob.bl_handle, b, bl_data)) {
566                break;
567            }
568        }
569
570        if (isc_close_blob(IB_STATUS, &ib_blob.bl_handle)) {
571            break;
572        }
573        RETURN_NEW_STR(_php_ibase_quad_to_string(ib_blob.bl_qd));
574    } while (0);
575
576    _php_ibase_error();
577    RETURN_FALSE;
578}
579/* }}} */
580
581#endif /* HAVE_IBASE */
582
583/*
584 * Local variables:
585 * tab-width: 4
586 * c-basic-offset: 4
587 * End:
588 * vim600: sw=4 ts=4 fdm=marker
589 * vim<600: sw=4 ts=4
590 */
591