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_rsrc_list_entry *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        RETVAL_STRINGL(bl_data, cur_len, 0);
122    } else { /* null blob */
123        RETVAL_STRING("", 1); /* empty string */
124    }
125    return SUCCESS;
126}
127/* }}} */
128
129int _php_ibase_blob_add(zval **string_arg, ibase_blob *ib_blob TSRMLS_DC) /* {{{ */
130{
131    unsigned long put_cnt = 0, rem_cnt;
132    unsigned short chunk_size;
133
134    convert_to_string_ex(string_arg);
135
136    for (rem_cnt = Z_STRLEN_PP(string_arg); rem_cnt > 0; rem_cnt -= chunk_size)  {
137
138        chunk_size = rem_cnt > USHRT_MAX ? USHRT_MAX : (unsigned short)rem_cnt;
139
140        if (isc_put_segment(IB_STATUS, &ib_blob->bl_handle, chunk_size, &Z_STRVAL_PP(string_arg)[put_cnt] )) {
141            _php_ibase_error(TSRMLS_C);
142            return FAILURE;
143        }
144        put_cnt += chunk_size;
145    }
146    return SUCCESS;
147}
148/* }}} */
149
150static int _php_ibase_blob_info(isc_blob_handle bl_handle, IBASE_BLOBINFO *bl_info TSRMLS_DC) /* {{{ */
151{
152    static char bl_items[] = {
153        isc_info_blob_num_segments,
154        isc_info_blob_max_segment,
155        isc_info_blob_total_length,
156        isc_info_blob_type
157    };
158
159    char bl_inf[sizeof(long)*8], *p;
160
161    bl_info->max_segment = 0;
162    bl_info->num_segments = 0;
163    bl_info->total_length = 0;
164    bl_info->bl_stream = 0;
165
166    if (isc_blob_info(IB_STATUS, &bl_handle, sizeof(bl_items), bl_items, sizeof(bl_inf), bl_inf)) {
167        _php_ibase_error(TSRMLS_C);
168        return FAILURE;
169    }
170
171    for (p = bl_inf; *p != isc_info_end && p < bl_inf + sizeof(bl_inf);) {
172        unsigned short item_len;
173        int item = *p++;
174
175        item_len = (short) isc_vax_integer(p, 2);
176        p += 2;
177        switch (item) {
178            case isc_info_blob_num_segments:
179                bl_info->num_segments = isc_vax_integer(p, item_len);
180                break;
181            case isc_info_blob_max_segment:
182                bl_info->max_segment = isc_vax_integer(p, item_len);
183                break;
184            case isc_info_blob_total_length:
185                bl_info->total_length = isc_vax_integer(p, item_len);
186                break;
187            case isc_info_blob_type:
188                bl_info->bl_stream = isc_vax_integer(p, item_len);
189                break;
190            case isc_info_end:
191                break;
192            case isc_info_truncated:
193            case isc_info_error:  /* hmm. don't think so...*/
194                _php_ibase_module_error("PHP module internal error" TSRMLS_CC);
195                return FAILURE;
196        } /* switch */
197        p += item_len;
198    } /* for */
199    return SUCCESS;
200}
201/* }}} */
202
203/* {{{ proto resource ibase_blob_create([resource link_identifier])
204   Create blob for adding data */
205PHP_FUNCTION(ibase_blob_create)
206{
207    zval *link = NULL;
208    ibase_db_link *ib_link;
209    ibase_trans *trans = NULL;
210    ibase_blob *ib_blob;
211
212    RESET_ERRMSG;
213
214    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &link)) {
215        RETURN_FALSE;
216    }
217
218    PHP_IBASE_LINK_TRANS(link, ib_link, trans);
219
220    ib_blob = (ibase_blob *) emalloc(sizeof(ibase_blob));
221    ib_blob->bl_handle = NULL;
222    ib_blob->type = BLOB_INPUT;
223
224    if (isc_create_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob->bl_handle, &ib_blob->bl_qd)) {
225        _php_ibase_error(TSRMLS_C);
226        efree(ib_blob);
227        RETURN_FALSE;
228    }
229
230    ZEND_REGISTER_RESOURCE(return_value, ib_blob, le_blob);
231}
232/* }}} */
233
234/* {{{ proto resource ibase_blob_open([ resource link_identifier, ] string blob_id)
235   Open blob for retrieving data parts */
236PHP_FUNCTION(ibase_blob_open)
237{
238    char *blob_id;
239    int blob_id_len;
240    zval *link = NULL;
241    ibase_db_link *ib_link;
242    ibase_trans *trans = NULL;
243    ibase_blob *ib_blob;
244
245    RESET_ERRMSG;
246
247    switch (ZEND_NUM_ARGS()) {
248        default:
249            WRONG_PARAM_COUNT;
250        case 1:
251            if (FAILURE == zend_parse_parameters(1 TSRMLS_CC, "s", &blob_id, &blob_id_len)) {
252                RETURN_FALSE;
253            }
254            break;
255        case 2:
256            if (FAILURE == zend_parse_parameters(2 TSRMLS_CC, "rs", &link, &blob_id, &blob_id_len)) {
257                RETURN_FALSE;
258            }
259            break;
260    }
261
262    PHP_IBASE_LINK_TRANS(link, ib_link, trans);
263
264    ib_blob = (ibase_blob *) emalloc(sizeof(ibase_blob));
265    ib_blob->bl_handle = NULL;
266    ib_blob->type = BLOB_OUTPUT;
267
268    do {
269        if (! _php_ibase_string_to_quad(blob_id, &ib_blob->bl_qd)) {
270            _php_ibase_module_error("String is not a BLOB ID" TSRMLS_CC);
271            break;
272        }
273
274        if (isc_open_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob->bl_handle,
275                &ib_blob->bl_qd)) {
276            _php_ibase_error(TSRMLS_C);
277            break;
278        }
279
280        ZEND_REGISTER_RESOURCE(return_value, ib_blob, le_blob);
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 (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &blob_arg, &string_arg) == FAILURE) {
300        WRONG_PARAM_COUNT;
301    }
302
303    ZEND_FETCH_RESOURCE(ib_blob, ibase_blob *, blob_arg, -1, "Interbase blob", le_blob);
304
305    if (ib_blob->type != BLOB_INPUT) {
306        _php_ibase_module_error("BLOB is not open for input" TSRMLS_CC);
307        RETURN_FALSE;
308    }
309
310    if (_php_ibase_blob_add(string_arg, ib_blob TSRMLS_CC) != 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, **len_arg;
321    ibase_blob *ib_blob;
322
323    RESET_ERRMSG;
324
325    if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &blob_arg, &len_arg) == FAILURE) {
326        WRONG_PARAM_COUNT;
327    }
328
329    ZEND_FETCH_RESOURCE(ib_blob, ibase_blob *, blob_arg, -1, "Interbase blob", le_blob);
330
331    if (ib_blob->type != BLOB_OUTPUT) {
332        _php_ibase_module_error("BLOB is not open for output" TSRMLS_CC);
333        RETURN_FALSE;
334    }
335
336    convert_to_long_ex(len_arg);
337
338    if (_php_ibase_blob_get(return_value, ib_blob, Z_LVAL_PP(len_arg) TSRMLS_CC) != SUCCESS) {
339        RETURN_FALSE;
340    }
341}
342/* }}} */
343
344static void _php_ibase_blob_end(INTERNAL_FUNCTION_PARAMETERS, int bl_end) /* {{{ */
345{
346    zval **blob_arg;
347    ibase_blob *ib_blob;
348
349    RESET_ERRMSG;
350
351    if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &blob_arg) == FAILURE) {
352        WRONG_PARAM_COUNT;
353    }
354
355    ZEND_FETCH_RESOURCE(ib_blob, ibase_blob *, blob_arg, -1, "Interbase blob", le_blob);
356
357    if (bl_end == BLOB_CLOSE) { /* return id here */
358
359        if (ib_blob->bl_qd.gds_quad_high || ib_blob->bl_qd.gds_quad_low) { /*not null ?*/
360            if (isc_close_blob(IB_STATUS, &ib_blob->bl_handle)) {
361                _php_ibase_error(TSRMLS_C);
362                RETURN_FALSE;
363            }
364        }
365        ib_blob->bl_handle = NULL;
366
367        RETVAL_STRINGL(_php_ibase_quad_to_string(ib_blob->bl_qd), BLOB_ID_LEN, 0);
368    } else { /* discard created blob */
369        if (isc_cancel_blob(IB_STATUS, &ib_blob->bl_handle)) {
370            _php_ibase_error(TSRMLS_C);
371            RETURN_FALSE;
372        }
373        ib_blob->bl_handle = NULL;
374        RETVAL_TRUE;
375    }
376    zend_list_delete(Z_LVAL_PP(blob_arg));
377}
378/* }}} */
379
380/* {{{ proto string ibase_blob_close(resource blob_handle)
381   Close blob */
382PHP_FUNCTION(ibase_blob_close)
383{
384    _php_ibase_blob_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, BLOB_CLOSE);
385}
386/* }}} */
387
388/* {{{ proto bool ibase_blob_cancel(resource blob_handle)
389   Cancel creating blob */
390PHP_FUNCTION(ibase_blob_cancel)
391{
392    _php_ibase_blob_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, BLOB_CANCEL);
393}
394/* }}} */
395
396/* {{{ proto array ibase_blob_info([ resource link_identifier, ] string blob_id)
397   Return blob length and other useful info */
398PHP_FUNCTION(ibase_blob_info)
399{
400    char *blob_id;
401    int blob_id_len;
402    zval *link = NULL;
403    ibase_db_link *ib_link;
404    ibase_trans *trans = NULL;
405    ibase_blob ib_blob = { NULL, BLOB_INPUT };
406    IBASE_BLOBINFO bl_info;
407
408    RESET_ERRMSG;
409
410    switch (ZEND_NUM_ARGS()) {
411        default:
412            WRONG_PARAM_COUNT;
413        case 1:
414            if (FAILURE == zend_parse_parameters(1 TSRMLS_CC, "s", &blob_id, &blob_id_len)) {
415                RETURN_FALSE;
416            }
417            break;
418        case 2:
419            if (FAILURE == zend_parse_parameters(2 TSRMLS_CC, "rs", &link, &blob_id, &blob_id_len)) {
420                RETURN_FALSE;
421            }
422            break;
423    }
424
425    PHP_IBASE_LINK_TRANS(link, ib_link, trans);
426
427    if (! _php_ibase_string_to_quad(blob_id, &ib_blob.bl_qd)) {
428        _php_ibase_module_error("Unrecognized BLOB ID" TSRMLS_CC);
429        RETURN_FALSE;
430    }
431
432    if (ib_blob.bl_qd.gds_quad_high || ib_blob.bl_qd.gds_quad_low) { /* not null ? */
433        if (isc_open_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob.bl_handle,
434                &ib_blob.bl_qd)) {
435            _php_ibase_error(TSRMLS_C);
436            RETURN_FALSE;
437        }
438
439        if (_php_ibase_blob_info(ib_blob.bl_handle, &bl_info TSRMLS_CC)) {
440            RETURN_FALSE;
441        }
442        if (isc_close_blob(IB_STATUS, &ib_blob.bl_handle)) {
443            _php_ibase_error(TSRMLS_C);
444            RETURN_FALSE;
445        }
446    } else { /* null blob, all values to zero */
447        bl_info.max_segment = 0;
448        bl_info.num_segments = 0;
449        bl_info.total_length = 0;
450        bl_info.bl_stream = 0;
451    }
452
453    array_init(return_value);
454
455    add_index_long(return_value, 0, bl_info.total_length);
456    add_assoc_long(return_value, "length", bl_info.total_length);
457
458    add_index_long(return_value, 1, bl_info.num_segments);
459    add_assoc_long(return_value, "numseg", bl_info.num_segments);
460
461    add_index_long(return_value, 2, bl_info.max_segment);
462    add_assoc_long(return_value, "maxseg", bl_info.max_segment);
463
464    add_index_bool(return_value, 3, bl_info.bl_stream);
465    add_assoc_bool(return_value, "stream", bl_info.bl_stream);
466
467    add_index_bool(return_value, 4, (!ib_blob.bl_qd.gds_quad_high && !ib_blob.bl_qd.gds_quad_low));
468    add_assoc_bool(return_value, "isnull", (!ib_blob.bl_qd.gds_quad_high && !ib_blob.bl_qd.gds_quad_low));
469}
470/* }}} */
471
472/* {{{ proto bool ibase_blob_echo([ resource link_identifier, ] string blob_id)
473   Output blob contents to browser */
474PHP_FUNCTION(ibase_blob_echo)
475{
476    char *blob_id;
477    int blob_id_len;
478    zval *link = NULL;
479    ibase_db_link *ib_link;
480    ibase_trans *trans = NULL;
481    ibase_blob ib_blob_id = { NULL, BLOB_OUTPUT  };
482    char bl_data[IBASE_BLOB_SEG];
483    unsigned short seg_len;
484
485    RESET_ERRMSG;
486
487    switch (ZEND_NUM_ARGS()) {
488        default:
489            WRONG_PARAM_COUNT;
490        case 1:
491            if (FAILURE == zend_parse_parameters(1 TSRMLS_CC, "s", &blob_id, &blob_id_len)) {
492                RETURN_FALSE;
493            }
494            break;
495        case 2:
496            if (FAILURE == zend_parse_parameters(2 TSRMLS_CC, "rs", &link, &blob_id, &blob_id_len)) {
497                RETURN_FALSE;
498            }
499            break;
500    }
501
502    PHP_IBASE_LINK_TRANS(link, ib_link, trans);
503
504    if (! _php_ibase_string_to_quad(blob_id, &ib_blob_id.bl_qd)) {
505        _php_ibase_module_error("Unrecognized BLOB ID" TSRMLS_CC);
506        RETURN_FALSE;
507    }
508
509    do {
510        if (isc_open_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob_id.bl_handle,
511                &ib_blob_id.bl_qd)) {
512            break;
513        }
514
515        while (!isc_get_segment(IB_STATUS, &ib_blob_id.bl_handle, &seg_len, sizeof(bl_data), bl_data)
516                || IB_STATUS[1] == isc_segment) {
517            PHPWRITE(bl_data, seg_len);
518        }
519
520        if (IB_STATUS[0] && (IB_STATUS[1] != isc_segstr_eof)) {
521            break;
522        }
523
524        if (isc_close_blob(IB_STATUS, &ib_blob_id.bl_handle)) {
525            break;
526        }
527        RETURN_TRUE;
528    } while (0);
529
530    _php_ibase_error(TSRMLS_C);
531    RETURN_FALSE;
532}
533/* }}} */
534
535/* {{{ proto string ibase_blob_import([ resource link_identifier, ] resource file)
536   Create blob, copy file in it, and close it */
537PHP_FUNCTION(ibase_blob_import)
538{
539    zval *link = NULL, *file;
540    int size;
541    unsigned short b;
542    ibase_blob ib_blob = { NULL, 0 };
543    ibase_db_link *ib_link;
544    ibase_trans *trans = NULL;
545    char bl_data[IBASE_BLOB_SEG];
546    php_stream *stream;
547
548    RESET_ERRMSG;
549
550    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|r",
551            (ZEND_NUM_ARGS()-1) ? &link : &file, &file)) {
552        RETURN_FALSE;
553    }
554
555    PHP_IBASE_LINK_TRANS(link, ib_link, trans);
556
557    php_stream_from_zval(stream, &file);
558
559    do {
560        if (isc_create_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob.bl_handle,
561                &ib_blob.bl_qd)) {
562            break;
563        }
564
565        for (size = 0; (b = php_stream_read(stream, bl_data, sizeof(bl_data))); size += b) {
566            if (isc_put_segment(IB_STATUS, &ib_blob.bl_handle, b, bl_data)) {
567                break;
568            }
569        }
570
571        if (isc_close_blob(IB_STATUS, &ib_blob.bl_handle)) {
572            break;
573        }
574        RETURN_STRINGL( _php_ibase_quad_to_string(ib_blob.bl_qd), BLOB_ID_LEN, 0);
575    } while (0);
576
577    _php_ibase_error(TSRMLS_C);
578    RETURN_FALSE;
579}
580/* }}} */
581
582#endif /* HAVE_IBASE */
583
584/*
585 * Local variables:
586 * tab-width: 4
587 * c-basic-offset: 4
588 * End:
589 * vim600: sw=4 ts=4 fdm=marker
590 * vim<600: sw=4 ts=4
591 */
592