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   | 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 TSRMLS_DC) /* {{{ */
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()" TSRMLS_CC);
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
73char *_php_ibase_quad_to_string(ISC_QUAD const qd) /* {{{ */
74{
75    char *result;
76
77    /* shortcut for most common case */
78    if (sizeof(ISC_QUAD) == sizeof(ISC_UINT64)) {
79        spprintf(&result, BLOB_ID_LEN+1, "0x%0*" LL_MASK "x", 16, *(ISC_UINT64*)(void *) &qd);
80    } else {
81        ISC_UINT64 res = ((ISC_UINT64) qd.gds_quad_high << 0x20) | qd.gds_quad_low;
82        spprintf(&result, BLOB_ID_LEN+1, "0x%0*" LL_MASK "x", 16, res);
83    }
84    return result;
85}
86/* }}} */
87
88typedef struct { /* {{{ */
89    ISC_LONG  max_segment;      /* Length of longest segment */
90    ISC_LONG  num_segments;     /* Total number of segments */
91    ISC_LONG  total_length;     /* Total length of blob */
92    int       bl_stream;        /* blob is stream ? */
93/* }}} */
94} IBASE_BLOBINFO;
95
96int _php_ibase_blob_get(zval *return_value, ibase_blob *ib_blob, unsigned long max_len TSRMLS_DC) /* {{{ */
97{
98    if (ib_blob->bl_qd.gds_quad_high || ib_blob->bl_qd.gds_quad_low) { /*not null ?*/
99
100        ISC_STATUS stat;
101        char *bl_data;
102        unsigned long cur_len;
103        unsigned short seg_len;
104
105        bl_data = safe_emalloc(1, max_len, 1);
106
107        for (cur_len = stat = 0; (stat == 0 || stat == isc_segment) && cur_len < max_len; cur_len += seg_len) {
108
109            unsigned short chunk_size = (max_len-cur_len) > USHRT_MAX ? USHRT_MAX
110                : (unsigned short)(max_len-cur_len);
111
112            stat = isc_get_segment(IB_STATUS, &ib_blob->bl_handle, &seg_len, chunk_size, &bl_data[cur_len]);
113        }
114
115        bl_data[cur_len] = '\0';
116        if (IB_STATUS[0] == 1 && (stat != 0 && stat != isc_segstr_eof && stat != isc_segment)) {
117            efree(bl_data);
118            _php_ibase_error(TSRMLS_C);
119            return FAILURE;
120        }
121        // TODO: avoid double reallocation???
122        RETVAL_STRINGL(bl_data, cur_len);
123        efree(bl_data);
124    } else { /* null blob */
125        RETVAL_EMPTY_STRING(); /* 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_P(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_P(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    Z_ADDREF_P(return_value);
234}
235/* }}} */
236
237/* {{{ proto resource ibase_blob_open([ resource link_identifier, ] string blob_id)
238   Open blob for retrieving data parts */
239PHP_FUNCTION(ibase_blob_open)
240{
241    char *blob_id;
242    size_t blob_id_len;
243    zval *link = NULL;
244    ibase_db_link *ib_link;
245    ibase_trans *trans = NULL;
246    ibase_blob *ib_blob;
247
248    RESET_ERRMSG;
249
250    switch (ZEND_NUM_ARGS()) {
251        default:
252            WRONG_PARAM_COUNT;
253        case 1:
254            if (FAILURE == zend_parse_parameters(1 TSRMLS_CC, "s", &blob_id, &blob_id_len)) {
255                RETURN_FALSE;
256            }
257            break;
258        case 2:
259            if (FAILURE == zend_parse_parameters(2 TSRMLS_CC, "rs", &link, &blob_id, &blob_id_len)) {
260                RETURN_FALSE;
261            }
262            break;
263    }
264
265    PHP_IBASE_LINK_TRANS(link, ib_link, trans);
266
267    ib_blob = (ibase_blob *) emalloc(sizeof(ibase_blob));
268    ib_blob->bl_handle = NULL;
269    ib_blob->type = BLOB_OUTPUT;
270
271    do {
272        if (! _php_ibase_string_to_quad(blob_id, &ib_blob->bl_qd)) {
273            _php_ibase_module_error("String is not a BLOB ID" TSRMLS_CC);
274            break;
275        }
276
277        if (isc_open_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob->bl_handle,
278                &ib_blob->bl_qd)) {
279            _php_ibase_error(TSRMLS_C);
280            break;
281        }
282
283        ZEND_REGISTER_RESOURCE(return_value, ib_blob, le_blob);
284        Z_ADDREF_P(return_value);
285        return;
286
287    } while (0);
288
289    efree(ib_blob);
290    RETURN_FALSE;
291}
292/* }}} */
293
294/* {{{ proto bool ibase_blob_add(resource blob_handle, string data)
295   Add data into created blob */
296PHP_FUNCTION(ibase_blob_add)
297{
298    zval *blob_arg, *string_arg;
299    ibase_blob *ib_blob;
300
301    RESET_ERRMSG;
302
303    if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &blob_arg, &string_arg) == FAILURE) {
304        WRONG_PARAM_COUNT;
305    }
306
307    ZEND_FETCH_RESOURCE(ib_blob, ibase_blob *, blob_arg, -1, "Interbase blob", le_blob);
308
309    if (ib_blob->type != BLOB_INPUT) {
310        _php_ibase_module_error("BLOB is not open for input" TSRMLS_CC);
311        RETURN_FALSE;
312    }
313
314    if (_php_ibase_blob_add(string_arg, ib_blob TSRMLS_CC) != SUCCESS) {
315        RETURN_FALSE;
316    }
317}
318/* }}} */
319
320/* {{{ proto string ibase_blob_get(resource blob_handle, int len)
321   Get len bytes data from open blob */
322PHP_FUNCTION(ibase_blob_get)
323{
324    zval *blob_arg, *len_arg;
325    ibase_blob *ib_blob;
326
327    RESET_ERRMSG;
328
329    if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &blob_arg, &len_arg) == FAILURE) {
330        WRONG_PARAM_COUNT;
331    }
332
333    ZEND_FETCH_RESOURCE(ib_blob, ibase_blob *, blob_arg, -1, "Interbase blob", le_blob);
334
335    if (ib_blob->type != BLOB_OUTPUT) {
336        _php_ibase_module_error("BLOB is not open for output" TSRMLS_CC);
337        RETURN_FALSE;
338    }
339
340    convert_to_long_ex(len_arg);
341
342    if (_php_ibase_blob_get(return_value, ib_blob, Z_LVAL_P(len_arg) TSRMLS_CC) != SUCCESS) {
343        RETURN_FALSE;
344    }
345}
346/* }}} */
347
348static void _php_ibase_blob_end(INTERNAL_FUNCTION_PARAMETERS, int bl_end) /* {{{ */
349{
350    zval *blob_arg;
351    ibase_blob *ib_blob;
352    char *s;
353
354    RESET_ERRMSG;
355
356    if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &blob_arg) == FAILURE) {
357        WRONG_PARAM_COUNT;
358    }
359
360    ZEND_FETCH_RESOURCE(ib_blob, ibase_blob *, blob_arg, -1, "Interbase blob", le_blob);
361
362    if (bl_end == BLOB_CLOSE) { /* return id here */
363
364        if (ib_blob->bl_qd.gds_quad_high || ib_blob->bl_qd.gds_quad_low) { /*not null ?*/
365            if (isc_close_blob(IB_STATUS, &ib_blob->bl_handle)) {
366                _php_ibase_error(TSRMLS_C);
367                RETURN_FALSE;
368            }
369        }
370        ib_blob->bl_handle = NULL;
371
372        s = _php_ibase_quad_to_string(ib_blob->bl_qd);
373        // TODO: avoid double reallocation???
374        RETVAL_STRINGL(s, BLOB_ID_LEN);
375        efree(s);
376    } else { /* discard created blob */
377        if (isc_cancel_blob(IB_STATUS, &ib_blob->bl_handle)) {
378            _php_ibase_error(TSRMLS_C);
379            RETURN_FALSE;
380        }
381        ib_blob->bl_handle = NULL;
382        RETVAL_TRUE;
383    }
384    zend_list_delete(Z_RES_P(blob_arg));
385}
386/* }}} */
387
388/* {{{ proto string ibase_blob_close(resource blob_handle)
389   Close blob */
390PHP_FUNCTION(ibase_blob_close)
391{
392    _php_ibase_blob_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, BLOB_CLOSE);
393}
394/* }}} */
395
396/* {{{ proto bool ibase_blob_cancel(resource blob_handle)
397   Cancel creating blob */
398PHP_FUNCTION(ibase_blob_cancel)
399{
400    _php_ibase_blob_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, BLOB_CANCEL);
401}
402/* }}} */
403
404/* {{{ proto array ibase_blob_info([ resource link_identifier, ] string blob_id)
405   Return blob length and other useful info */
406PHP_FUNCTION(ibase_blob_info)
407{
408    char *blob_id;
409    size_t blob_id_len;
410    zval *link = NULL;
411    ibase_db_link *ib_link;
412    ibase_trans *trans = NULL;
413    ibase_blob ib_blob = { NULL, BLOB_INPUT };
414    IBASE_BLOBINFO bl_info;
415
416    RESET_ERRMSG;
417
418    switch (ZEND_NUM_ARGS()) {
419        default:
420            WRONG_PARAM_COUNT;
421        case 1:
422            if (FAILURE == zend_parse_parameters(1 TSRMLS_CC, "s", &blob_id, &blob_id_len)) {
423                RETURN_FALSE;
424            }
425            break;
426        case 2:
427            if (FAILURE == zend_parse_parameters(2 TSRMLS_CC, "rs", &link, &blob_id, &blob_id_len)) {
428                RETURN_FALSE;
429            }
430            break;
431    }
432
433    PHP_IBASE_LINK_TRANS(link, ib_link, trans);
434
435    if (! _php_ibase_string_to_quad(blob_id, &ib_blob.bl_qd)) {
436        _php_ibase_module_error("Unrecognized BLOB ID" TSRMLS_CC);
437        RETURN_FALSE;
438    }
439
440    if (ib_blob.bl_qd.gds_quad_high || ib_blob.bl_qd.gds_quad_low) { /* not null ? */
441        if (isc_open_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob.bl_handle,
442                &ib_blob.bl_qd)) {
443            _php_ibase_error(TSRMLS_C);
444            RETURN_FALSE;
445        }
446
447        if (_php_ibase_blob_info(ib_blob.bl_handle, &bl_info TSRMLS_CC)) {
448            RETURN_FALSE;
449        }
450        if (isc_close_blob(IB_STATUS, &ib_blob.bl_handle)) {
451            _php_ibase_error(TSRMLS_C);
452            RETURN_FALSE;
453        }
454    } else { /* null blob, all values to zero */
455        bl_info.max_segment = 0;
456        bl_info.num_segments = 0;
457        bl_info.total_length = 0;
458        bl_info.bl_stream = 0;
459    }
460
461    array_init(return_value);
462
463    add_index_long(return_value, 0, bl_info.total_length);
464    add_assoc_long(return_value, "length", bl_info.total_length);
465
466    add_index_long(return_value, 1, bl_info.num_segments);
467    add_assoc_long(return_value, "numseg", bl_info.num_segments);
468
469    add_index_long(return_value, 2, bl_info.max_segment);
470    add_assoc_long(return_value, "maxseg", bl_info.max_segment);
471
472    add_index_bool(return_value, 3, bl_info.bl_stream);
473    add_assoc_bool(return_value, "stream", bl_info.bl_stream);
474
475    add_index_bool(return_value, 4, (!ib_blob.bl_qd.gds_quad_high && !ib_blob.bl_qd.gds_quad_low));
476    add_assoc_bool(return_value, "isnull", (!ib_blob.bl_qd.gds_quad_high && !ib_blob.bl_qd.gds_quad_low));
477}
478/* }}} */
479
480/* {{{ proto bool ibase_blob_echo([ resource link_identifier, ] string blob_id)
481   Output blob contents to browser */
482PHP_FUNCTION(ibase_blob_echo)
483{
484    char *blob_id;
485    size_t blob_id_len;
486    zval *link = NULL;
487    ibase_db_link *ib_link;
488    ibase_trans *trans = NULL;
489    ibase_blob ib_blob_id = { NULL, BLOB_OUTPUT  };
490    char bl_data[IBASE_BLOB_SEG];
491    unsigned short seg_len;
492
493    RESET_ERRMSG;
494
495    switch (ZEND_NUM_ARGS()) {
496        default:
497            WRONG_PARAM_COUNT;
498        case 1:
499            if (FAILURE == zend_parse_parameters(1 TSRMLS_CC, "s", &blob_id, &blob_id_len)) {
500                RETURN_FALSE;
501            }
502            break;
503        case 2:
504            if (FAILURE == zend_parse_parameters(2 TSRMLS_CC, "rs", &link, &blob_id, &blob_id_len)) {
505                RETURN_FALSE;
506            }
507            break;
508    }
509
510    PHP_IBASE_LINK_TRANS(link, ib_link, trans);
511
512    if (! _php_ibase_string_to_quad(blob_id, &ib_blob_id.bl_qd)) {
513        _php_ibase_module_error("Unrecognized BLOB ID" TSRMLS_CC);
514        RETURN_FALSE;
515    }
516
517    do {
518        if (isc_open_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob_id.bl_handle,
519                &ib_blob_id.bl_qd)) {
520            break;
521        }
522
523        while (!isc_get_segment(IB_STATUS, &ib_blob_id.bl_handle, &seg_len, sizeof(bl_data), bl_data)
524                || IB_STATUS[1] == isc_segment) {
525            PHPWRITE(bl_data, seg_len);
526        }
527
528        if (IB_STATUS[0] && (IB_STATUS[1] != isc_segstr_eof)) {
529            break;
530        }
531
532        if (isc_close_blob(IB_STATUS, &ib_blob_id.bl_handle)) {
533            break;
534        }
535        RETURN_TRUE;
536    } while (0);
537
538    _php_ibase_error(TSRMLS_C);
539    RETURN_FALSE;
540}
541/* }}} */
542
543/* {{{ proto string ibase_blob_import([ resource link_identifier, ] resource file)
544   Create blob, copy file in it, and close it */
545PHP_FUNCTION(ibase_blob_import)
546{
547    zval *link = NULL, *file;
548    int size;
549    unsigned short b;
550    ibase_blob ib_blob = { NULL, 0 };
551    ibase_db_link *ib_link;
552    ibase_trans *trans = NULL;
553    char bl_data[IBASE_BLOB_SEG];
554    php_stream *stream;
555    char *s;
556
557    RESET_ERRMSG;
558
559    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|r",
560            (ZEND_NUM_ARGS()-1) ? &link : &file, &file)) {
561        RETURN_FALSE;
562    }
563
564    PHP_IBASE_LINK_TRANS(link, ib_link, trans);
565
566    php_stream_from_zval(stream, file);
567
568    do {
569        if (isc_create_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob.bl_handle,
570                &ib_blob.bl_qd)) {
571            break;
572        }
573
574        for (size = 0; (b = php_stream_read(stream, bl_data, sizeof(bl_data))); size += b) {
575            if (isc_put_segment(IB_STATUS, &ib_blob.bl_handle, b, bl_data)) {
576                break;
577            }
578        }
579
580        if (isc_close_blob(IB_STATUS, &ib_blob.bl_handle)) {
581            break;
582        }
583        s = _php_ibase_quad_to_string(ib_blob.bl_qd);
584        // TODO: avoid double reallocation???
585        RETVAL_STRINGL(s, BLOB_ID_LEN);
586        efree(s);
587        return;
588    } while (0);
589
590    _php_ibase_error(TSRMLS_C);
591    RETURN_FALSE;
592}
593/* }}} */
594
595#endif /* HAVE_IBASE */
596
597/*
598 * Local variables:
599 * tab-width: 4
600 * c-basic-offset: 4
601 * End:
602 * vim600: sw=4 ts=4 fdm=marker
603 * vim<600: sw=4 ts=4
604 */
605