xref: /PHP-7.2/ext/interbase/ibase_blobs.c (revision 7a7ec01a)
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 static int le_blob;
34 
_php_ibase_free_blob(zend_resource * rsrc)35 static 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 
php_ibase_blobs_minit(INIT_FUNC_ARGS)49 void 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 
_php_ibase_string_to_quad(char const * id,ISC_QUAD * qd)56 int _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 
_php_ibase_quad_to_string(ISC_QUAD const qd)73 zend_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 
85 typedef 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 
_php_ibase_blob_get(zval * return_value,ibase_blob * ib_blob,zend_ulong max_len)93 int _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_safe_alloc(1, max_len, 0, 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 
_php_ibase_blob_add(zval * string_arg,ibase_blob * ib_blob)127 int _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 
_php_ibase_blob_info(isc_blob_handle bl_handle,IBASE_BLOBINFO * bl_info)148 static 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 */
PHP_FUNCTION(ibase_blob_create)203 PHP_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 */
PHP_FUNCTION(ibase_blob_open)235 PHP_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 */
PHP_FUNCTION(ibase_blob_add)292 PHP_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 */
PHP_FUNCTION(ibase_blob_get)318 PHP_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 
_php_ibase_blob_end(INTERNAL_FUNCTION_PARAMETERS,int bl_end)343 static 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 */
PHP_FUNCTION(ibase_blob_close)381 PHP_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 */
PHP_FUNCTION(ibase_blob_cancel)389 PHP_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 */
PHP_FUNCTION(ibase_blob_info)397 PHP_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 */
PHP_FUNCTION(ibase_blob_echo)473 PHP_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 */
PHP_FUNCTION(ibase_blob_import)536 PHP_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