1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2016 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 != 0) { /* 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, zend_ulong 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		zend_ulong 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	zend_ulong 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(zend_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 = 0;
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 = 0;
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(), "rz", &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	zend_ulong 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 = 0;
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 = 0;
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 = { 0, 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 = { 0, 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 = { 0, 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