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