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