1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2017 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 static int le_event;
31
_php_ibase_event_free(char * event_buf,char * result_buf)32 static void _php_ibase_event_free(char *event_buf, char *result_buf) /* {{{ */
33 {
34 isc_free(event_buf);
35 isc_free(result_buf);
36 }
37 /* }}} */
38
_php_ibase_free_event(ibase_event * event)39 void _php_ibase_free_event(ibase_event *event) /* {{{ */
40 {
41 unsigned short i;
42
43 event->state = DEAD;
44
45 if (event->link != NULL) {
46 ibase_event **node;
47
48 zend_list_delete(event->link_res);
49 if (event->link->handle != 0 &&
50 isc_cancel_events(IB_STATUS, &event->link->handle, &event->event_id)) {
51 _php_ibase_error();
52 }
53
54 /* delete this event from the link struct */
55 for (node = &event->link->event_head; *node != event; node = &(*node)->event_next);
56 *node = event->event_next;
57 }
58
59 if (Z_TYPE(event->callback) != IS_UNDEF) {
60 zval_dtor(&event->callback);
61 ZVAL_UNDEF(&event->callback);
62
63 _php_ibase_event_free(event->event_buffer,event->result_buffer);
64
65 for (i = 0; i < event->event_count; ++i) {
66 if (event->events[i]) {
67 efree(event->events[i]);
68 }
69 }
70 efree(event->events);
71 }
72 }
73 /* }}} */
74
_php_ibase_free_event_rsrc(zend_resource * rsrc)75 static void _php_ibase_free_event_rsrc(zend_resource *rsrc) /* {{{ */
76 {
77 ibase_event *e = (ibase_event *) rsrc->ptr;
78
79 _php_ibase_free_event(e);
80
81 efree(e);
82 }
83 /* }}} */
84
php_ibase_events_minit(INIT_FUNC_ARGS)85 void php_ibase_events_minit(INIT_FUNC_ARGS) /* {{{ */
86 {
87 le_event = zend_register_list_destructors_ex(_php_ibase_free_event_rsrc, NULL,
88 "interbase event", module_number);
89 }
90 /* }}} */
91
_php_ibase_event_block(ibase_db_link * ib_link,unsigned short count,char ** events,unsigned short * l,char ** event_buf,char ** result_buf)92 static void _php_ibase_event_block(ibase_db_link *ib_link, unsigned short count, /* {{{ */
93 char **events, unsigned short *l, char **event_buf, char **result_buf)
94 {
95 ISC_STATUS dummy_result[20];
96 ISC_ULONG dummy_count[15];
97
98 /**
99 * Unfortunately, there's no clean and portable way in C to pass arguments to
100 * a variadic function if you don't know the number of arguments at compile time.
101 * (And even if there were a way, the Interbase API doesn't provide a version of
102 * this function that takes a va_list as an argument)
103 *
104 * In this case, the number of arguments is limited to 18 by the underlying API,
105 * so we can work around it.
106 */
107
108 *l = (unsigned short) isc_event_block(event_buf, result_buf, count, events[0],
109 events[1], events[2], events[3], events[4], events[5], events[6], events[7],
110 events[8], events[9], events[10], events[11], events[12], events[13], events[14]);
111
112 /**
113 * Currently, this is the only way to correctly initialize an event buffer.
114 * This is clearly something that should be fixed, cause the semantics of
115 * isc_wait_for_event() indicate that it blocks until an event occurs.
116 * If the Firebird people ever fix this, these lines should be removed,
117 * otherwise, events will have to fire twice before ibase_wait_event() returns.
118 */
119
120 isc_wait_for_event(dummy_result, &ib_link->handle, *l, *event_buf, *result_buf);
121 isc_event_counts(dummy_count, *l, *event_buf, *result_buf);
122 }
123 /* }}} */
124
125 /* {{{ proto string ibase_wait_event([resource link_identifier,] string event [, string event [, ...]])
126 Waits for any one of the passed Interbase events to be posted by the database, and returns its name */
PHP_FUNCTION(ibase_wait_event)127 PHP_FUNCTION(ibase_wait_event)
128 {
129 zval *args;
130 ibase_db_link *ib_link;
131 int num_args;
132 char *event_buffer, *result_buffer, *events[15];
133 unsigned short i = 0, event_count = 0, buffer_size;
134 ISC_ULONG occurred_event[15];
135
136 RESET_ERRMSG;
137
138 /* no more than 15 events */
139 if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 16) {
140 WRONG_PARAM_COUNT;
141 }
142
143 if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &num_args) == FAILURE) {
144 return;
145 }
146
147 if (Z_TYPE(args[0]) == IS_RESOURCE) {
148 if ((ib_link = (ibase_db_link *)zend_fetch_resource2_ex(&args[0], "InterBase link", le_link, le_plink)) == NULL) {
149 RETURN_FALSE;
150 }
151 i = 1;
152 } else {
153 if (ZEND_NUM_ARGS() > 15) {
154 WRONG_PARAM_COUNT;
155 }
156 if ((ib_link = (ibase_db_link *)zend_fetch_resource2(IBG(default_link), "InterBase link", le_link, le_plink)) == NULL) {
157 RETURN_FALSE;
158 }
159 }
160
161 for (; i < ZEND_NUM_ARGS(); ++i) {
162 convert_to_string_ex(&args[i]);
163 events[event_count++] = Z_STRVAL(args[i]);
164 }
165
166 /* fills the required data structure with information about the events */
167 _php_ibase_event_block(ib_link, event_count, events, &buffer_size, &event_buffer, &result_buffer);
168
169 /* now block until an event occurs */
170 if (isc_wait_for_event(IB_STATUS, &ib_link->handle, buffer_size, event_buffer, result_buffer)) {
171 _php_ibase_error();
172 _php_ibase_event_free(event_buffer,result_buffer);
173 RETURN_FALSE;
174 }
175
176 /* find out which event occurred */
177 isc_event_counts(occurred_event, buffer_size, event_buffer, result_buffer);
178 for (i = 0; i < event_count; ++i) {
179 if (occurred_event[i]) {
180 zend_string *result = zend_string_init(events[i], strlen(events[i]), 0);
181 _php_ibase_event_free(event_buffer,result_buffer);
182 RETURN_STR(result);
183 }
184 }
185
186 /* If we reach this line, isc_wait_for_event() did return, but we don't know
187 which event fired. */
188 _php_ibase_event_free(event_buffer,result_buffer);
189 RETURN_FALSE;
190 }
191 /* }}} */
192
193 #if FB_API_VER >= 20
194 #define PHP_ISC_CALLBACK ISC_EVENT_CALLBACK
_php_ibase_callback(ibase_event * event,ISC_USHORT buffer_size,ISC_UCHAR * result_buf)195 static ISC_EVENT_CALLBACK _php_ibase_callback(ibase_event *event, /* {{{ */
196 ISC_USHORT buffer_size, ISC_UCHAR *result_buf)
197 #else
198 #define PHP_ISC_CALLBACK isc_callback
199 static isc_callback _php_ibase_callback(ibase_event *event, /* {{{ */
200 unsigned short buffer_size, char *result_buf)
201 #endif
202 {
203 /* this function is called asynchronously by the Interbase client library. */
204 TSRMLS_FETCH_FROM_CTX(event->thread_ctx);
205
206 /**
207 * The callback function is called when the event is first registered and when the event
208 * is cancelled. I consider this is a bug. By clearing event->callback first and setting
209 * it to -1 later, we make sure nothing happens if no event was actually posted.
210 */
211 switch (event->state) {
212 unsigned short i;
213 ISC_ULONG occurred_event[15];
214 zval return_value, args[2];
215
216 default: /* == DEAD */
217 break;
218 case ACTIVE:
219 /* copy the updated results into the result buffer */
220 memcpy(event->result_buffer, result_buf, buffer_size);
221
222 ZVAL_RES(&args[1], event->link_res);
223
224 /* find out which event occurred */
225 isc_event_counts(occurred_event, buffer_size, event->event_buffer, event->result_buffer);
226 for (i = 0; i < event->event_count; ++i) {
227 if (occurred_event[i]) {
228 ZVAL_STRING(&args[0], event->events[i]);
229 //efree(event->events[i]);
230 break;
231 }
232 }
233
234 /* call the callback provided by the user */
235 if (SUCCESS != call_user_function(EG(function_table), NULL,
236 &event->callback, &return_value, 2, args)) {
237 _php_ibase_module_error("Error calling callback %s", Z_STRVAL(event->callback));
238 break;
239 }
240
241 if (Z_TYPE(return_value) == IS_FALSE) {
242 event->state = DEAD;
243 break;
244 }
245 case NEW:
246 /* re-register the event */
247 if (isc_que_events(IB_STATUS, &event->link->handle, &event->event_id, buffer_size,
248 event->event_buffer,(PHP_ISC_CALLBACK)_php_ibase_callback, (void *)event)) {
249
250 _php_ibase_error();
251 }
252 event->state = ACTIVE;
253 }
254 return 0;
255 }
256 /* }}} */
257
258 /* {{{ proto resource ibase_set_event_handler([resource link_identifier,] callback handler, string event [, string event [, ...]])
259 Register the callback for handling each of the named events */
PHP_FUNCTION(ibase_set_event_handler)260 PHP_FUNCTION(ibase_set_event_handler)
261 {
262 /**
263 * The callback passed to this function should take an event name (string) and a
264 * link resource id (int) as arguments. The value returned from the function is
265 * used to determine if the event handler should remain set.
266 */
267 zend_string *cb_name;
268 zval *args, *cb_arg;
269 ibase_db_link *ib_link;
270 ibase_event *event;
271 unsigned short i = 1, buffer_size;
272 int num_args;
273 zend_resource *link_res;
274
275 RESET_ERRMSG;
276
277 /* Minimum and maximum number of arguments allowed */
278 if (ZEND_NUM_ARGS() < 2 || ZEND_NUM_ARGS() > 17) {
279 WRONG_PARAM_COUNT;
280 }
281
282 if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &num_args) == FAILURE) {
283 return;
284 }
285
286 /* get a working link */
287 if (Z_TYPE(args[0]) != IS_STRING) {
288 /* resource, callback, event_1 [, ... event_15]
289 * No more than 15 events
290 */
291 if (ZEND_NUM_ARGS() < 3 || ZEND_NUM_ARGS() > 17) {
292 WRONG_PARAM_COUNT;
293 }
294
295 cb_arg = &args[1];
296 i = 2;
297
298 if ((ib_link = (ibase_db_link *)zend_fetch_resource2_ex(&args[0], "InterBase link", le_link, le_plink)) == NULL) {
299 RETURN_FALSE;
300 }
301
302 link_res = Z_RES(args[0]);
303
304 } else {
305 /* callback, event_1 [, ... event_15]
306 * No more than 15 events
307 */
308 if (ZEND_NUM_ARGS() < 2 || ZEND_NUM_ARGS() > 16) {
309 WRONG_PARAM_COUNT;
310 }
311
312 cb_arg = &args[0];
313
314 if ((ib_link = (ibase_db_link *)zend_fetch_resource2(IBG(default_link), "InterBase link", le_link, le_plink)) == NULL) {
315 RETURN_FALSE;
316 }
317 link_res = IBG(default_link);
318 }
319
320 /* get the callback */
321 if (!zend_is_callable(cb_arg, 0, &cb_name)) {
322 _php_ibase_module_error("Callback argument %s is not a callable function", ZSTR_VAL(cb_name));
323 zend_string_release(cb_name);
324 RETURN_FALSE;
325 }
326 zend_string_release(cb_name);
327
328 /* allocate the event resource */
329 event = (ibase_event *) safe_emalloc(sizeof(ibase_event), 1, 0);
330 TSRMLS_SET_CTX(event->thread_ctx);
331 event->link_res = link_res;
332 GC_REFCOUNT(link_res)++;
333 event->link = ib_link;
334 event->event_count = 0;
335 event->state = NEW;
336 event->events = (char **) safe_emalloc(sizeof(char *), 15, 0);
337
338 ZVAL_DUP(&event->callback, cb_arg);
339
340 for (; i < 15; ++i) {
341 if (i < ZEND_NUM_ARGS()) {
342 convert_to_string_ex(&args[i]);
343 event->events[event->event_count++] = estrdup(Z_STRVAL(args[i]));
344 } else {
345 event->events[i] = NULL;
346 }
347 }
348
349 /* fills the required data structure with information about the events */
350 _php_ibase_event_block(ib_link, event->event_count, event->events,
351 &buffer_size, &event->event_buffer, &event->result_buffer);
352
353 /* now register the events with the Interbase API */
354 if (isc_que_events(IB_STATUS, &ib_link->handle, &event->event_id, buffer_size,
355 event->event_buffer,(PHP_ISC_CALLBACK)_php_ibase_callback, (void *)event)) {
356
357 _php_ibase_error();
358 efree(event);
359 RETURN_FALSE;
360 }
361
362 event->event_next = ib_link->event_head;
363 ib_link->event_head = event;
364
365 RETVAL_RES(zend_register_resource(event, le_event));
366 Z_TRY_ADDREF_P(return_value);
367 }
368 /* }}} */
369
370 /* {{{ proto bool ibase_free_event_handler(resource event)
371 Frees the event handler set by ibase_set_event_handler() */
PHP_FUNCTION(ibase_free_event_handler)372 PHP_FUNCTION(ibase_free_event_handler)
373 {
374 zval *event_arg;
375
376 RESET_ERRMSG;
377
378 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "r", &event_arg)) {
379 ibase_event *event;
380
381 event = (ibase_event *)zend_fetch_resource_ex(event_arg, "Interbase event", le_event);
382
383 event->state = DEAD;
384
385 zend_list_delete(Z_RES_P(event_arg));
386 RETURN_TRUE;
387 } else {
388 RETURN_FALSE;
389 }
390 }
391 /* }}} */
392
393 #endif /* HAVE_IBASE */
394
395 /*
396 * Local variables:
397 * tab-width: 4
398 * c-basic-offset: 4
399 * End:
400 * vim600: sw=4 ts=4 fdm=marker
401 * vim<600: sw=4 ts=4
402 */
403