1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2013 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: Jouni Ahto <jouni.ahto@exdec.fi>                            |
16   |          Andrew Avdeev <andy@rsc.mv.ru>                              |
17   |          Ard Biesheuvel <a.k.biesheuvel@ewi.tudelft.nl>              |
18   +----------------------------------------------------------------------+
19 */
20
21/* $Id$ */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#define _GNU_SOURCE
28
29#include "php.h"
30
31#if HAVE_IBASE
32
33#include "php_ini.h"
34#include "ext/standard/php_standard.h"
35#include "ext/standard/md5.h"
36#include "php_interbase.h"
37#include "php_ibase_includes.h"
38#include "SAPI.h"
39
40#include <time.h>
41
42#define ROLLBACK        0
43#define COMMIT          1
44#define RETAIN          2
45
46#define CHECK_LINK(link) { if (link==-1) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "A link to the server could not be established"); RETURN_FALSE; } }
47
48ZEND_DECLARE_MODULE_GLOBALS(ibase)
49static PHP_GINIT_FUNCTION(ibase);
50
51/* {{{ arginfo */
52ZEND_BEGIN_ARG_INFO(arginfo_ibase_errmsg, 0)
53ZEND_END_ARG_INFO()
54
55ZEND_BEGIN_ARG_INFO(arginfo_ibase_errcode, 0)
56ZEND_END_ARG_INFO()
57
58ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_connect, 0, 0, 1)
59    ZEND_ARG_INFO(0, database)
60    ZEND_ARG_INFO(0, username)
61    ZEND_ARG_INFO(0, password)
62    ZEND_ARG_INFO(0, charset)
63    ZEND_ARG_INFO(0, buffers)
64    ZEND_ARG_INFO(0, dialect)
65    ZEND_ARG_INFO(0, role)
66ZEND_END_ARG_INFO()
67
68ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_pconnect, 0, 0, 1)
69    ZEND_ARG_INFO(0, database)
70    ZEND_ARG_INFO(0, username)
71    ZEND_ARG_INFO(0, password)
72    ZEND_ARG_INFO(0, charset)
73    ZEND_ARG_INFO(0, buffers)
74    ZEND_ARG_INFO(0, dialect)
75    ZEND_ARG_INFO(0, role)
76ZEND_END_ARG_INFO()
77
78ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_close, 0, 0, 0)
79    ZEND_ARG_INFO(0, link_identifier)
80ZEND_END_ARG_INFO()
81
82ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_drop_db, 0, 0, 0)
83    ZEND_ARG_INFO(0, link_identifier)
84ZEND_END_ARG_INFO()
85
86ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_trans, 0, 0, 0)
87    ZEND_ARG_INFO(0, trans_args)
88    ZEND_ARG_INFO(0, link_identifier)
89    ZEND_ARG_INFO(0, trans_args)
90    ZEND_ARG_INFO(0, link_identifier)
91ZEND_END_ARG_INFO()
92
93ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_commit, 0, 0, 1)
94    ZEND_ARG_INFO(0, link_identifier)
95ZEND_END_ARG_INFO()
96
97ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_rollback, 0, 0, 1)
98    ZEND_ARG_INFO(0, link_identifier)
99ZEND_END_ARG_INFO()
100
101ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_commit_ret, 0, 0, 1)
102    ZEND_ARG_INFO(0, link_identifier)
103ZEND_END_ARG_INFO()
104
105ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_rollback_ret, 0, 0, 1)
106    ZEND_ARG_INFO(0, link_identifier)
107ZEND_END_ARG_INFO()
108
109ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_gen_id, 0, 0, 1)
110    ZEND_ARG_INFO(0, generator)
111    ZEND_ARG_INFO(0, increment)
112    ZEND_ARG_INFO(0, link_identifier)
113ZEND_END_ARG_INFO()
114
115ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_blob_create, 0, 0, 0)
116    ZEND_ARG_INFO(0, link_identifier)
117ZEND_END_ARG_INFO()
118
119ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_blob_open, 0, 0, 0)
120    ZEND_ARG_INFO(0, link_identifier)
121    ZEND_ARG_INFO(0, blob_id)
122ZEND_END_ARG_INFO()
123
124ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_blob_add, 0, 0, 2)
125    ZEND_ARG_INFO(0, blob_handle)
126    ZEND_ARG_INFO(0, data)
127ZEND_END_ARG_INFO()
128
129ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_blob_get, 0, 0, 2)
130    ZEND_ARG_INFO(0, blob_handle)
131    ZEND_ARG_INFO(0, len)
132ZEND_END_ARG_INFO()
133
134ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_blob_close, 0, 0, 1)
135    ZEND_ARG_INFO(0, blob_handle)
136ZEND_END_ARG_INFO()
137
138ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_blob_cancel, 0, 0, 1)
139    ZEND_ARG_INFO(0, blob_handle)
140ZEND_END_ARG_INFO()
141
142ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_blob_info, 0, 0, 0)
143    ZEND_ARG_INFO(0, link_identifier)
144    ZEND_ARG_INFO(0, blob_id)
145ZEND_END_ARG_INFO()
146
147ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_blob_echo, 0, 0, 0)
148    ZEND_ARG_INFO(0, link_identifier)
149    ZEND_ARG_INFO(0, blob_id)
150ZEND_END_ARG_INFO()
151
152ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_blob_import, 0, 0, 0)
153    ZEND_ARG_INFO(0, link_identifier)
154    ZEND_ARG_INFO(0, file)
155ZEND_END_ARG_INFO()
156
157ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_query, 0, 0, 0)
158    ZEND_ARG_INFO(0, link_identifier)
159    ZEND_ARG_INFO(0, link_identifier)
160    ZEND_ARG_INFO(0, query)
161    ZEND_ARG_INFO(0, bind_arg)
162    ZEND_ARG_INFO(0, bind_arg)
163ZEND_END_ARG_INFO()
164
165ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_affected_rows, 0, 0, 0)
166    ZEND_ARG_INFO(0, link_identifier)
167ZEND_END_ARG_INFO()
168
169#if abies_0
170ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_num_rows, 0, 0, 1)
171    ZEND_ARG_INFO(0, result_identifier)
172ZEND_END_ARG_INFO()
173#endif
174
175ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_fetch_row, 0, 0, 1)
176    ZEND_ARG_INFO(0, result)
177    ZEND_ARG_INFO(0, fetch_flags)
178ZEND_END_ARG_INFO()
179
180ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_fetch_assoc, 0, 0, 1)
181    ZEND_ARG_INFO(0, result)
182    ZEND_ARG_INFO(0, fetch_flags)
183ZEND_END_ARG_INFO()
184
185ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_fetch_object, 0, 0, 1)
186    ZEND_ARG_INFO(0, result)
187    ZEND_ARG_INFO(0, fetch_flags)
188ZEND_END_ARG_INFO()
189
190ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_name_result, 0, 0, 2)
191    ZEND_ARG_INFO(0, result)
192    ZEND_ARG_INFO(0, name)
193ZEND_END_ARG_INFO()
194
195ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_free_result, 0, 0, 1)
196    ZEND_ARG_INFO(0, result)
197ZEND_END_ARG_INFO()
198
199ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_prepare, 0, 0, 0)
200    ZEND_ARG_INFO(0, link_identifier)
201    ZEND_ARG_INFO(0, query)
202ZEND_END_ARG_INFO()
203
204ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_execute, 0, 0, 1)
205    ZEND_ARG_INFO(0, query)
206    ZEND_ARG_INFO(0, bind_arg)
207    ZEND_ARG_INFO(0, bind_arg)
208ZEND_END_ARG_INFO()
209
210ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_free_query, 0, 0, 1)
211    ZEND_ARG_INFO(0, query)
212ZEND_END_ARG_INFO()
213
214ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_num_fields, 0, 0, 1)
215    ZEND_ARG_INFO(0, query_result)
216ZEND_END_ARG_INFO()
217
218ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_field_info, 0, 0, 2)
219    ZEND_ARG_INFO(0, query_result)
220    ZEND_ARG_INFO(0, field_number)
221ZEND_END_ARG_INFO()
222
223ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_num_params, 0, 0, 1)
224    ZEND_ARG_INFO(0, query)
225ZEND_END_ARG_INFO()
226
227ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_param_info, 0, 0, 2)
228    ZEND_ARG_INFO(0, query)
229    ZEND_ARG_INFO(0, field_number)
230ZEND_END_ARG_INFO()
231
232ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_add_user, 0, 0, 3)
233    ZEND_ARG_INFO(0, service_handle)
234    ZEND_ARG_INFO(0, user_name)
235    ZEND_ARG_INFO(0, password)
236    ZEND_ARG_INFO(0, first_name)
237    ZEND_ARG_INFO(0, middle_name)
238    ZEND_ARG_INFO(0, last_name)
239ZEND_END_ARG_INFO()
240
241ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_modify_user, 0, 0, 3)
242    ZEND_ARG_INFO(0, service_handle)
243    ZEND_ARG_INFO(0, user_name)
244    ZEND_ARG_INFO(0, password)
245    ZEND_ARG_INFO(0, first_name)
246    ZEND_ARG_INFO(0, middle_name)
247    ZEND_ARG_INFO(0, last_name)
248ZEND_END_ARG_INFO()
249
250ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_delete_user, 0, 0, 3)
251    ZEND_ARG_INFO(0, service_handle)
252    ZEND_ARG_INFO(0, user_name)
253    ZEND_ARG_INFO(0, password)
254    ZEND_ARG_INFO(0, first_name)
255    ZEND_ARG_INFO(0, middle_name)
256    ZEND_ARG_INFO(0, last_name)
257ZEND_END_ARG_INFO()
258
259ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_service_attach, 0, 0, 3)
260    ZEND_ARG_INFO(0, host)
261    ZEND_ARG_INFO(0, dba_username)
262    ZEND_ARG_INFO(0, dba_password)
263ZEND_END_ARG_INFO()
264
265ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_service_detach, 0, 0, 1)
266    ZEND_ARG_INFO(0, service_handle)
267ZEND_END_ARG_INFO()
268
269ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_backup, 0, 0, 3)
270    ZEND_ARG_INFO(0, service_handle)
271    ZEND_ARG_INFO(0, source_db)
272    ZEND_ARG_INFO(0, dest_file)
273    ZEND_ARG_INFO(0, options)
274    ZEND_ARG_INFO(0, verbose)
275ZEND_END_ARG_INFO()
276
277ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_restore, 0, 0, 3)
278    ZEND_ARG_INFO(0, service_handle)
279    ZEND_ARG_INFO(0, source_file)
280    ZEND_ARG_INFO(0, dest_db)
281    ZEND_ARG_INFO(0, options)
282    ZEND_ARG_INFO(0, verbose)
283ZEND_END_ARG_INFO()
284
285ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_maintain_db, 0, 0, 3)
286    ZEND_ARG_INFO(0, service_handle)
287    ZEND_ARG_INFO(0, db)
288    ZEND_ARG_INFO(0, action)
289    ZEND_ARG_INFO(0, argument)
290ZEND_END_ARG_INFO()
291
292ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_db_info, 0, 0, 3)
293    ZEND_ARG_INFO(0, service_handle)
294    ZEND_ARG_INFO(0, db)
295    ZEND_ARG_INFO(0, action)
296    ZEND_ARG_INFO(0, argument)
297ZEND_END_ARG_INFO()
298
299ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_server_info, 0, 0, 2)
300    ZEND_ARG_INFO(0, service_handle)
301    ZEND_ARG_INFO(0, action)
302ZEND_END_ARG_INFO()
303
304ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_wait_event, 0, 0, 1)
305    ZEND_ARG_INFO(0, link_identifier)
306    ZEND_ARG_INFO(0, event)
307    ZEND_ARG_INFO(0, event2)
308ZEND_END_ARG_INFO()
309
310ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_set_event_handler, 0, 0, 2)
311    ZEND_ARG_INFO(0, link_identifier)
312    ZEND_ARG_INFO(0, handler)
313    ZEND_ARG_INFO(0, event)
314    ZEND_ARG_INFO(0, event2)
315ZEND_END_ARG_INFO()
316
317ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_free_event_handler, 0, 0, 1)
318    ZEND_ARG_INFO(0, event)
319ZEND_END_ARG_INFO()
320/* }}} */
321
322/* {{{ extension definition structures */
323const zend_function_entry ibase_functions[] = {
324    PHP_FE(ibase_connect,       arginfo_ibase_connect)
325    PHP_FE(ibase_pconnect,      arginfo_ibase_pconnect)
326    PHP_FE(ibase_close,         arginfo_ibase_close)
327    PHP_FE(ibase_drop_db,       arginfo_ibase_drop_db)
328    PHP_FE(ibase_query,         arginfo_ibase_query)
329    PHP_FE(ibase_fetch_row,     arginfo_ibase_fetch_row)
330    PHP_FE(ibase_fetch_assoc,   arginfo_ibase_fetch_assoc)
331    PHP_FE(ibase_fetch_object,  arginfo_ibase_fetch_object)
332    PHP_FE(ibase_free_result,   arginfo_ibase_free_result)
333    PHP_FE(ibase_name_result,   arginfo_ibase_name_result)
334    PHP_FE(ibase_prepare,       arginfo_ibase_prepare)
335    PHP_FE(ibase_execute,       arginfo_ibase_execute)
336    PHP_FE(ibase_free_query,    arginfo_ibase_free_query)
337    PHP_FE(ibase_gen_id,        arginfo_ibase_gen_id)
338    PHP_FE(ibase_num_fields,    arginfo_ibase_num_fields)
339    PHP_FE(ibase_num_params,    arginfo_ibase_num_params)
340#if abies_0
341    PHP_FE(ibase_num_rows,      arginfo_ibase_num_rows)
342#endif
343    PHP_FE(ibase_affected_rows, arginfo_ibase_affected_rows)
344    PHP_FE(ibase_field_info,    arginfo_ibase_field_info)
345    PHP_FE(ibase_param_info,    arginfo_ibase_param_info)
346
347    PHP_FE(ibase_trans,         arginfo_ibase_trans)
348    PHP_FE(ibase_commit,        arginfo_ibase_commit)
349    PHP_FE(ibase_rollback,      arginfo_ibase_rollback)
350    PHP_FE(ibase_commit_ret,    arginfo_ibase_commit_ret)
351    PHP_FE(ibase_rollback_ret,  arginfo_ibase_rollback_ret)
352
353    PHP_FE(ibase_blob_info,     arginfo_ibase_blob_info)
354    PHP_FE(ibase_blob_create,   arginfo_ibase_blob_create)
355    PHP_FE(ibase_blob_add,      arginfo_ibase_blob_add)
356    PHP_FE(ibase_blob_cancel,   arginfo_ibase_blob_cancel)
357    PHP_FE(ibase_blob_close,    arginfo_ibase_blob_close)
358    PHP_FE(ibase_blob_open,     arginfo_ibase_blob_open)
359    PHP_FE(ibase_blob_get,      arginfo_ibase_blob_get)
360    PHP_FE(ibase_blob_echo,     arginfo_ibase_blob_echo)
361    PHP_FE(ibase_blob_import,   arginfo_ibase_blob_import)
362    PHP_FE(ibase_errmsg,        arginfo_ibase_errmsg)
363    PHP_FE(ibase_errcode,       arginfo_ibase_errcode)
364
365    PHP_FE(ibase_add_user,      arginfo_ibase_add_user)
366    PHP_FE(ibase_modify_user,   arginfo_ibase_modify_user)
367    PHP_FE(ibase_delete_user,   arginfo_ibase_delete_user)
368
369    PHP_FE(ibase_service_attach, arginfo_ibase_service_attach)
370    PHP_FE(ibase_service_detach, arginfo_ibase_service_detach)
371    PHP_FE(ibase_backup,        arginfo_ibase_backup)
372    PHP_FE(ibase_restore,       arginfo_ibase_restore)
373    PHP_FE(ibase_maintain_db,   arginfo_ibase_maintain_db)
374    PHP_FE(ibase_db_info,       arginfo_ibase_db_info)
375    PHP_FE(ibase_server_info,   arginfo_ibase_server_info)
376
377    PHP_FE(ibase_wait_event,            arginfo_ibase_wait_event)
378    PHP_FE(ibase_set_event_handler,     arginfo_ibase_set_event_handler)
379    PHP_FE(ibase_free_event_handler,    arginfo_ibase_free_event_handler)
380
381    /**
382    * These aliases are provided in order to maintain forward compatibility. As Firebird
383    * and InterBase are developed independently, functionality might be different between
384    * the two branches in future versions.
385    * Firebird users should use the aliases, so future InterBase-specific changes will
386    * not affect their code
387    */
388    PHP_FALIAS(fbird_connect,       ibase_connect,      arginfo_ibase_connect)
389    PHP_FALIAS(fbird_pconnect,      ibase_pconnect,     arginfo_ibase_pconnect)
390    PHP_FALIAS(fbird_close,         ibase_close,        arginfo_ibase_close)
391    PHP_FALIAS(fbird_drop_db,       ibase_drop_db,      arginfo_ibase_drop_db)
392    PHP_FALIAS(fbird_query,         ibase_query,        arginfo_ibase_query)
393    PHP_FALIAS(fbird_fetch_row,     ibase_fetch_row,    arginfo_ibase_fetch_row)
394    PHP_FALIAS(fbird_fetch_assoc,   ibase_fetch_assoc,  arginfo_ibase_fetch_assoc)
395    PHP_FALIAS(fbird_fetch_object,  ibase_fetch_object, arginfo_ibase_fetch_object)
396    PHP_FALIAS(fbird_free_result,   ibase_free_result,  arginfo_ibase_free_result)
397    PHP_FALIAS(fbird_name_result,   ibase_name_result,  arginfo_ibase_name_result)
398    PHP_FALIAS(fbird_prepare,       ibase_prepare,      arginfo_ibase_prepare)
399    PHP_FALIAS(fbird_execute,       ibase_execute,      arginfo_ibase_execute)
400    PHP_FALIAS(fbird_free_query,    ibase_free_query,   arginfo_ibase_free_query)
401    PHP_FALIAS(fbird_gen_id,        ibase_gen_id,       arginfo_ibase_gen_id)
402    PHP_FALIAS(fbird_num_fields,    ibase_num_fields,   arginfo_ibase_num_fields)
403    PHP_FALIAS(fbird_num_params,    ibase_num_params,   arginfo_ibase_num_params)
404#if abies_0
405    PHP_FALIAS(fbird_num_rows,      ibase_num_rows,     arginfo_ibase_num_rows)
406#endif
407    PHP_FALIAS(fbird_affected_rows, ibase_affected_rows, arginfo_ibase_affected_rows)
408    PHP_FALIAS(fbird_field_info,    ibase_field_info,   arginfo_ibase_field_info)
409    PHP_FALIAS(fbird_param_info,    ibase_param_info,   arginfo_ibase_param_info)
410
411    PHP_FALIAS(fbird_trans,         ibase_trans,        arginfo_ibase_trans)
412    PHP_FALIAS(fbird_commit,        ibase_commit,       arginfo_ibase_commit)
413    PHP_FALIAS(fbird_rollback,      ibase_rollback,     arginfo_ibase_rollback)
414    PHP_FALIAS(fbird_commit_ret,    ibase_commit_ret,   arginfo_ibase_commit_ret)
415    PHP_FALIAS(fbird_rollback_ret,  ibase_rollback_ret, arginfo_ibase_rollback_ret)
416
417    PHP_FALIAS(fbird_blob_info,     ibase_blob_info,    arginfo_ibase_blob_info)
418    PHP_FALIAS(fbird_blob_create,   ibase_blob_create,  arginfo_ibase_blob_create)
419    PHP_FALIAS(fbird_blob_add,      ibase_blob_add,     arginfo_ibase_blob_add)
420    PHP_FALIAS(fbird_blob_cancel,   ibase_blob_cancel,  arginfo_ibase_blob_cancel)
421    PHP_FALIAS(fbird_blob_close,    ibase_blob_close,   arginfo_ibase_blob_close)
422    PHP_FALIAS(fbird_blob_open,     ibase_blob_open,    arginfo_ibase_blob_open)
423    PHP_FALIAS(fbird_blob_get,      ibase_blob_get,     arginfo_ibase_blob_get)
424    PHP_FALIAS(fbird_blob_echo,     ibase_blob_echo,    arginfo_ibase_blob_echo)
425    PHP_FALIAS(fbird_blob_import,   ibase_blob_import,  arginfo_ibase_blob_import)
426    PHP_FALIAS(fbird_errmsg,        ibase_errmsg,       arginfo_ibase_errmsg)
427    PHP_FALIAS(fbird_errcode,       ibase_errcode,      arginfo_ibase_errcode)
428
429    PHP_FALIAS(fbird_add_user,      ibase_add_user,     arginfo_ibase_add_user)
430    PHP_FALIAS(fbird_modify_user,   ibase_modify_user,  arginfo_ibase_modify_user)
431    PHP_FALIAS(fbird_delete_user,   ibase_delete_user,  arginfo_ibase_delete_user)
432
433    PHP_FALIAS(fbird_service_attach,    ibase_service_attach, arginfo_ibase_service_attach)
434    PHP_FALIAS(fbird_service_detach,    ibase_service_detach, arginfo_ibase_service_detach)
435    PHP_FALIAS(fbird_backup,        ibase_backup,       arginfo_ibase_backup)
436    PHP_FALIAS(fbird_restore,       ibase_restore,      arginfo_ibase_restore)
437    PHP_FALIAS(fbird_maintain_db,   ibase_maintain_db,  arginfo_ibase_maintain_db)
438    PHP_FALIAS(fbird_db_info,       ibase_db_info,      arginfo_ibase_db_info)
439    PHP_FALIAS(fbird_server_info,   ibase_server_info,  arginfo_ibase_server_info)
440
441    PHP_FALIAS(fbird_wait_event,    ibase_wait_event,   arginfo_ibase_wait_event)
442    PHP_FALIAS(fbird_set_event_handler, ibase_set_event_handler,    arginfo_ibase_set_event_handler)
443    PHP_FALIAS(fbird_free_event_handler,    ibase_free_event_handler, arginfo_ibase_free_event_handler)
444    PHP_FE_END
445};
446
447zend_module_entry ibase_module_entry = {
448    STANDARD_MODULE_HEADER,
449    "interbase",
450    ibase_functions,
451    PHP_MINIT(ibase),
452    PHP_MSHUTDOWN(ibase),
453    NULL,
454    PHP_RSHUTDOWN(ibase),
455    PHP_MINFO(ibase),
456    NO_VERSION_YET,
457    PHP_MODULE_GLOBALS(ibase),
458    PHP_GINIT(ibase),
459    NULL,
460    NULL,
461    STANDARD_MODULE_PROPERTIES_EX
462};
463
464#ifdef COMPILE_DL_INTERBASE
465ZEND_GET_MODULE(ibase)
466#endif
467
468/* True globals, no need for thread safety */
469int le_link, le_plink, le_trans;
470
471/* }}} */
472
473/* error handling ---------------------------- */
474
475/* {{{ proto string ibase_errmsg(void)
476   Return error message */
477PHP_FUNCTION(ibase_errmsg)
478{
479    if (zend_parse_parameters_none() == FAILURE) {
480        return;
481    }
482
483    if (IBG(sql_code) != 0) {
484        RETURN_STRING(IBG(errmsg), 1);
485    }
486
487    RETURN_FALSE;
488}
489/* }}} */
490
491/* {{{ proto int ibase_errcode(void)
492   Return error code */
493PHP_FUNCTION(ibase_errcode)
494{
495    if (zend_parse_parameters_none() == FAILURE) {
496        return;
497    }
498
499    if (IBG(sql_code) != 0) {
500        RETURN_LONG(IBG(sql_code));
501    }
502    RETURN_FALSE;
503}
504/* }}} */
505
506/* print interbase error and save it for ibase_errmsg() */
507void _php_ibase_error(TSRMLS_D) /* {{{ */
508{
509    char *s = IBG(errmsg);
510    ISC_STATUS *statusp = IB_STATUS;
511
512    IBG(sql_code) = isc_sqlcode(IB_STATUS);
513
514    while ((s - IBG(errmsg)) < MAX_ERRMSG - (IBASE_MSGSIZE + 2) && isc_interprete(s, &statusp)) {
515        strcat(IBG(errmsg), " ");
516        s = IBG(errmsg) + strlen(IBG(errmsg));
517    }
518
519    php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", IBG(errmsg));
520}
521/* }}} */
522
523/* print php interbase module error and save it for ibase_errmsg() */
524void _php_ibase_module_error(char *msg TSRMLS_DC, ...) /* {{{ */
525{
526    va_list ap;
527
528#ifdef ZTS
529    va_start(ap, TSRMLS_C);
530#else
531    va_start(ap, msg);
532#endif
533
534    /* vsnprintf NUL terminates the buf and writes at most n-1 chars+NUL */
535    vsnprintf(IBG(errmsg), MAX_ERRMSG, msg, ap);
536    va_end(ap);
537
538    IBG(sql_code) = -999; /* no SQL error */
539
540    php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", IBG(errmsg));
541}
542/* }}} */
543
544/* {{{ internal macros, functions and structures */
545typedef struct {
546    isc_db_handle *db_ptr;
547    long tpb_len;
548    char *tpb_ptr;
549} ISC_TEB;
550
551/* }}} */
552
553/* Fill ib_link and trans with the correct database link and transaction. */
554void _php_ibase_get_link_trans(INTERNAL_FUNCTION_PARAMETERS, /* {{{ */
555    zval **link_id, ibase_db_link **ib_link, ibase_trans **trans)
556{
557    int type;
558
559    IBDEBUG("Transaction or database link?");
560    if (zend_list_find(Z_LVAL_PP(link_id), &type)) {
561        if (type == le_trans) {
562            /* Transaction resource: make sure it refers to one link only, then
563               fetch it; database link is stored in ib_trans->db_link[]. */
564            IBDEBUG("Type is le_trans");
565            ZEND_FETCH_RESOURCE(*trans, ibase_trans *, link_id, -1, LE_TRANS, le_trans);
566            if ((*trans)->link_cnt > 1) {
567                _php_ibase_module_error("Link id is ambiguous: transaction spans multiple connections."
568                    TSRMLS_CC);
569                return;
570            }
571            *ib_link = (*trans)->db_link[0];
572            return;
573        }
574    }
575    IBDEBUG("Type is le_[p]link or id not found");
576    /* Database link resource, use default transaction. */
577    *trans = NULL;
578    ZEND_FETCH_RESOURCE2(*ib_link, ibase_db_link *, link_id, -1, LE_LINK, le_link, le_plink);
579}
580/* }}} */
581
582/* destructors ---------------------- */
583
584static void _php_ibase_commit_link(ibase_db_link *link TSRMLS_DC) /* {{{ */
585{
586    unsigned short i = 0, j;
587    ibase_tr_list *l;
588    ibase_event *e;
589    IBDEBUG("Checking transactions to close...");
590
591    for (l = link->tr_list; l != NULL; ++i) {
592        ibase_tr_list *p = l;
593        if (p->trans != NULL) {
594            if (i == 0) {
595                if (p->trans->handle != NULL) {
596                    IBDEBUG("Committing default transaction...");
597                    if (isc_commit_transaction(IB_STATUS, &p->trans->handle)) {
598                        _php_ibase_error(TSRMLS_C);
599                    }
600                }
601                efree(p->trans); /* default transaction is not a registered resource: clean up */
602            } else {
603                if (p->trans->handle != NULL) {
604                    /* non-default trans might have been rolled back by other call of this dtor */
605                    IBDEBUG("Rolling back other transactions...");
606                    if (isc_rollback_transaction(IB_STATUS, &p->trans->handle)) {
607                        _php_ibase_error(TSRMLS_C);
608                    }
609                }
610                /* set this link pointer to NULL in the transaction */
611                for (j = 0; j < p->trans->link_cnt; ++j) {
612                    if (p->trans->db_link[j] == link) {
613                        p->trans->db_link[j] = NULL;
614                        break;
615                    }
616                }
617            }
618        }
619        l = l->next;
620        efree(p);
621    }
622    link->tr_list = NULL;
623
624    for (e = link->event_head; e; e = e->event_next) {
625        _php_ibase_free_event(e TSRMLS_CC);
626        e->link = NULL;
627    }
628}
629
630/* }}} */
631
632static void php_ibase_commit_link_rsrc(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
633{
634    ibase_db_link *link = (ibase_db_link *) rsrc->ptr;
635
636    _php_ibase_commit_link(link TSRMLS_CC);
637}
638/* }}} */
639
640static void _php_ibase_close_link(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
641{
642    ibase_db_link *link = (ibase_db_link *) rsrc->ptr;
643
644    _php_ibase_commit_link(link TSRMLS_CC);
645    if (link->handle != NULL) {
646        IBDEBUG("Closing normal link...");
647        isc_detach_database(IB_STATUS, &link->handle);
648    }
649    IBG(num_links)--;
650    efree(link);
651}
652/* }}} */
653
654static void _php_ibase_close_plink(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
655{
656    ibase_db_link *link = (ibase_db_link *) rsrc->ptr;
657
658    _php_ibase_commit_link(link TSRMLS_CC);
659    IBDEBUG("Closing permanent link...");
660    if (link->handle != NULL) {
661        isc_detach_database(IB_STATUS, &link->handle);
662    }
663    IBG(num_persistent)--;
664    IBG(num_links)--;
665    free(link);
666}
667/* }}} */
668
669static void _php_ibase_free_trans(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
670{
671    ibase_trans *trans = (ibase_trans *)rsrc->ptr;
672    unsigned short i;
673
674    IBDEBUG("Cleaning up transaction resource...");
675    if (trans->handle != NULL) {
676        IBDEBUG("Rolling back unhandled transaction...");
677        if (isc_rollback_transaction(IB_STATUS, &trans->handle)) {
678            _php_ibase_error(TSRMLS_C);
679        }
680    }
681
682    /* now remove this transaction from all the connection-transaction lists */
683    for (i = 0; i < trans->link_cnt; ++i) {
684        if (trans->db_link[i] != NULL) {
685            ibase_tr_list **l;
686            for (l = &trans->db_link[i]->tr_list; *l != NULL; l = &(*l)->next) {
687                if ( (*l)->trans == trans) {
688                    ibase_tr_list *p = *l;
689                    *l = p->next;
690                    efree(p);
691                    break;
692                }
693            }
694        }
695    }
696    efree(trans);
697}
698/* }}} */
699
700/* TODO this function should be part of either Zend or PHP API */
701static PHP_INI_DISP(php_ibase_password_displayer_cb)
702{
703    TSRMLS_FETCH();
704
705    if ((type == PHP_INI_DISPLAY_ORIG && ini_entry->orig_value)
706            || (type == PHP_INI_DISPLAY_ACTIVE && ini_entry->value)) {
707        PUTS("********");
708    } else if (!sapi_module.phpinfo_as_text) {
709        PUTS("<i>no value</i>");
710    } else {
711        PUTS("no value");
712    }
713}
714
715/* {{{ startup, shutdown and info functions */
716PHP_INI_BEGIN()
717    PHP_INI_ENTRY_EX("ibase.allow_persistent", "1", PHP_INI_SYSTEM, NULL, zend_ini_boolean_displayer_cb)
718    PHP_INI_ENTRY_EX("ibase.max_persistent", "-1", PHP_INI_SYSTEM, NULL, display_link_numbers)
719    PHP_INI_ENTRY_EX("ibase.max_links", "-1", PHP_INI_SYSTEM, NULL, display_link_numbers)
720    PHP_INI_ENTRY("ibase.default_db", NULL, PHP_INI_SYSTEM, NULL)
721    PHP_INI_ENTRY("ibase.default_user", NULL, PHP_INI_ALL, NULL)
722    PHP_INI_ENTRY_EX("ibase.default_password", NULL, PHP_INI_ALL, NULL, php_ibase_password_displayer_cb)
723    PHP_INI_ENTRY("ibase.default_charset", NULL, PHP_INI_ALL, NULL)
724    PHP_INI_ENTRY("ibase.timestampformat", IB_DEF_DATE_FMT " " IB_DEF_TIME_FMT, PHP_INI_ALL, NULL)
725    PHP_INI_ENTRY("ibase.dateformat", IB_DEF_DATE_FMT, PHP_INI_ALL, NULL)
726    PHP_INI_ENTRY("ibase.timeformat", IB_DEF_TIME_FMT, PHP_INI_ALL, NULL)
727PHP_INI_END()
728
729static PHP_GINIT_FUNCTION(ibase)
730{
731    ibase_globals->num_persistent = ibase_globals->num_links = 0;
732    ibase_globals->sql_code = *ibase_globals->errmsg = 0;
733    ibase_globals->default_link = -1;
734}
735
736PHP_MINIT_FUNCTION(ibase)
737{
738    REGISTER_INI_ENTRIES();
739
740    le_link = zend_register_list_destructors_ex(_php_ibase_close_link, NULL, LE_LINK, module_number);
741    le_plink = zend_register_list_destructors_ex(php_ibase_commit_link_rsrc, _php_ibase_close_plink, LE_PLINK, module_number);
742    le_trans = zend_register_list_destructors_ex(_php_ibase_free_trans, NULL, LE_TRANS, module_number);
743
744    REGISTER_LONG_CONSTANT("IBASE_DEFAULT", PHP_IBASE_DEFAULT, CONST_PERSISTENT);
745    REGISTER_LONG_CONSTANT("IBASE_CREATE", PHP_IBASE_CREATE, CONST_PERSISTENT);
746    REGISTER_LONG_CONSTANT("IBASE_TEXT", PHP_IBASE_FETCH_BLOBS, CONST_PERSISTENT); /* deprecated, for BC only */
747    REGISTER_LONG_CONSTANT("IBASE_FETCH_BLOBS", PHP_IBASE_FETCH_BLOBS, CONST_PERSISTENT);
748    REGISTER_LONG_CONSTANT("IBASE_FETCH_ARRAYS", PHP_IBASE_FETCH_ARRAYS, CONST_PERSISTENT);
749    REGISTER_LONG_CONSTANT("IBASE_UNIXTIME", PHP_IBASE_UNIXTIME, CONST_PERSISTENT);
750
751    /* transactions */
752    REGISTER_LONG_CONSTANT("IBASE_WRITE", PHP_IBASE_WRITE, CONST_PERSISTENT);
753    REGISTER_LONG_CONSTANT("IBASE_READ", PHP_IBASE_READ, CONST_PERSISTENT);
754    REGISTER_LONG_CONSTANT("IBASE_COMMITTED", PHP_IBASE_COMMITTED, CONST_PERSISTENT);
755    REGISTER_LONG_CONSTANT("IBASE_CONSISTENCY", PHP_IBASE_CONSISTENCY, CONST_PERSISTENT);
756    REGISTER_LONG_CONSTANT("IBASE_CONCURRENCY", PHP_IBASE_CONCURRENCY, CONST_PERSISTENT);
757    REGISTER_LONG_CONSTANT("IBASE_REC_VERSION", PHP_IBASE_REC_VERSION, CONST_PERSISTENT);
758    REGISTER_LONG_CONSTANT("IBASE_REC_NO_VERSION", PHP_IBASE_REC_NO_VERSION, CONST_PERSISTENT);
759    REGISTER_LONG_CONSTANT("IBASE_NOWAIT", PHP_IBASE_NOWAIT, CONST_PERSISTENT);
760    REGISTER_LONG_CONSTANT("IBASE_WAIT", PHP_IBASE_WAIT, CONST_PERSISTENT);
761
762    php_ibase_query_minit(INIT_FUNC_ARGS_PASSTHRU);
763    php_ibase_blobs_minit(INIT_FUNC_ARGS_PASSTHRU);
764    php_ibase_events_minit(INIT_FUNC_ARGS_PASSTHRU);
765    php_ibase_service_minit(INIT_FUNC_ARGS_PASSTHRU);
766
767    return SUCCESS;
768}
769
770PHP_MSHUTDOWN_FUNCTION(ibase)
771{
772#ifndef PHP_WIN32
773    /**
774     * When the Interbase client API library libgds.so is first loaded, it registers a call to
775     * gds__cleanup() with atexit(), in order to clean up after itself when the process exits.
776     * This means that the library is called at process shutdown, and cannot be unloaded beforehand.
777     * PHP tries to unload modules after every request [dl()'ed modules], and right before the
778     * process shuts down [modules loaded from php.ini]. This results in a segfault for this module.
779     * By NULLing the dlopen() handle in the module entry, Zend omits the call to dlclose(),
780     * ensuring that the module will remain present until the process exits. However, the functions
781     * and classes exported by the module will not be available until the module is 'reloaded'.
782     * When reloaded, dlopen() will return the handle of the already loaded module. The module will
783     * be unloaded automatically when the process exits.
784     */
785    zend_module_entry *ibase_entry;
786    if (SUCCESS == zend_hash_find(&module_registry, ibase_module_entry.name,
787            strlen(ibase_module_entry.name) +1, (void*) &ibase_entry)) {
788        ibase_entry->handle = NULL;
789    }
790#endif
791    UNREGISTER_INI_ENTRIES();
792    return SUCCESS;
793}
794
795PHP_RSHUTDOWN_FUNCTION(ibase)
796{
797    IBG(num_links) = IBG(num_persistent);
798    IBG(default_link)= -1;
799
800    RESET_ERRMSG;
801
802    return SUCCESS;
803}
804
805PHP_MINFO_FUNCTION(ibase)
806{
807    char tmp[64], *s;
808
809    php_info_print_table_start();
810    php_info_print_table_row(2, "Firebird/InterBase Support",
811#ifdef COMPILE_DL_INTERBASE
812        "dynamic");
813#else
814        "static");
815#endif
816
817#ifdef FB_API_VER
818    snprintf( (s = tmp), sizeof(tmp), "Firebird API version %d", FB_API_VER);
819#elif (SQLDA_CURRENT_VERSION > 1)
820    s =  "Interbase 7.0 and up";
821#elif !defined(DSC_null)
822    s = "Interbase 6";
823#else
824    s = "Firebird 1.0";
825#endif
826    php_info_print_table_row(2, "Compile-time Client Library Version", s);
827
828#if defined(__GNUC__) || defined(PHP_WIN32)
829    do {
830        info_func_t info_func = NULL;
831#ifdef __GNUC__
832        info_func = (info_func_t)dlsym(RTLD_DEFAULT, "isc_get_client_version");
833#else
834        HMODULE l = GetModuleHandle("fbclient");
835
836        if (!l && !(l = GetModuleHandle("gds32"))) {
837            break;
838        }
839        info_func = (info_func_t)GetProcAddress(l, "isc_get_client_version");
840#endif
841        if (info_func) {
842            info_func(s = tmp);
843        } else {
844            s = "Firebird 1.0/Interbase 6";
845        }
846        php_info_print_table_row(2, "Run-time Client Library Version", s);
847    } while (0);
848#endif
849    php_info_print_table_end();
850
851    DISPLAY_INI_ENTRIES();
852
853}
854/* }}} */
855
856enum connect_args { DB = 0, USER = 1, PASS = 2, CSET = 3, ROLE = 4, BUF = 0, DLECT = 1, SYNC = 2 };
857
858static char const dpb_args[] = {
859    0, isc_dpb_user_name, isc_dpb_password, isc_dpb_lc_ctype, isc_dpb_sql_role_name, 0
860};
861
862int _php_ibase_attach_db(char **args, int *len, long *largs, isc_db_handle *db TSRMLS_DC)
863{
864    short i, dpb_len, buf_len = 257-2;  /* version byte at the front, and a null at the end */
865    char dpb_buffer[257] = { isc_dpb_version1, 0 }, *dpb;
866
867    dpb = dpb_buffer + 1;
868
869    for (i = 0; i < sizeof(dpb_args); ++i) {
870        if (dpb_args[i] && args[i] && len[i] && buf_len > 0) {
871            dpb_len = slprintf(dpb, buf_len, "%c%c%s", dpb_args[i],(unsigned char)len[i],args[i]);
872            dpb += dpb_len;
873            buf_len -= dpb_len;
874        }
875    }
876    if (largs[BUF] && buf_len > 0) {
877        dpb_len = slprintf(dpb, buf_len, "%c\2%c%c", isc_dpb_num_buffers,
878            (char)(largs[BUF] >> 8), (char)(largs[BUF] & 0xff));
879        dpb += dpb_len;
880        buf_len -= dpb_len;
881    }
882    if (largs[SYNC] && buf_len > 0) {
883        dpb_len = slprintf(dpb, buf_len, "%c\1%c", isc_dpb_force_write, largs[SYNC] == isc_spb_prp_wm_sync ? 1 : 0);
884        dpb += dpb_len;
885        buf_len -= dpb_len;
886    }
887    if (isc_attach_database(IB_STATUS, (short)len[DB], args[DB], db, (short)(dpb-dpb_buffer), dpb_buffer)) {
888        _php_ibase_error(TSRMLS_C);
889        return FAILURE;
890    }
891    return SUCCESS;
892}
893/* }}} */
894
895static void _php_ibase_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) /* {{{ */
896{
897    char *c, hash[16], *args[] = { NULL, NULL, NULL, NULL, NULL };
898    int i, len[] = { 0, 0, 0, 0, 0 };
899    long largs[] = { 0, 0, 0 };
900    PHP_MD5_CTX hash_context;
901    zend_rsrc_list_entry new_index_ptr, *le;
902    isc_db_handle db_handle = NULL;
903    ibase_db_link *ib_link;
904
905    RESET_ERRMSG;
906
907    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ssssllsl",
908            &args[DB], &len[DB], &args[USER], &len[USER], &args[PASS], &len[PASS],
909            &args[CSET], &len[CSET], &largs[BUF], &largs[DLECT], &args[ROLE], &len[ROLE],
910            &largs[SYNC])) {
911        RETURN_FALSE;
912    }
913
914    /* restrict to the server/db in the .ini if in safe mode */
915    if ((!len[DB] || PG(sql_safe_mode)) && (c = INI_STR("ibase.default_db"))) {
916        args[DB] = c;
917        len[DB] = strlen(c);
918    }
919    if (!len[USER] && (c = INI_STR("ibase.default_user"))) {
920        args[USER] = c;
921        len[USER] = strlen(c);
922    }
923    if (!len[PASS] && (c = INI_STR("ibase.default_password"))) {
924        args[PASS] = c;
925        len[PASS] = strlen(c);
926    }
927    if (!len[CSET] && (c = INI_STR("ibase.default_charset"))) {
928        args[CSET] = c;
929        len[CSET] = strlen(c);
930    }
931
932    /* don't want usernames and passwords floating around */
933    PHP_MD5Init(&hash_context);
934    for (i = 0; i < sizeof(args)/sizeof(char*); ++i) {
935        PHP_MD5Update(&hash_context,args[i],len[i]);
936    }
937    for (i = 0; i < sizeof(largs)/sizeof(long); ++i) {
938        PHP_MD5Update(&hash_context,(char*)&largs[i],sizeof(long));
939    }
940    PHP_MD5Final(hash, &hash_context);
941
942    /* try to reuse a connection */
943    if (SUCCESS == zend_hash_find(&EG(regular_list), hash, sizeof(hash), (void *) &le)) {
944        long xlink;
945        int type;
946
947        if (Z_TYPE_P(le) != le_index_ptr) {
948            RETURN_FALSE;
949        }
950
951        xlink = (long) le->ptr;
952        if (zend_list_find(xlink, &type) && ((!persistent && type == le_link) || type == le_plink)) {
953            zend_list_addref(xlink);
954            RETURN_RESOURCE(IBG(default_link) = xlink);
955        } else {
956            zend_hash_del(&EG(regular_list), hash, sizeof(hash));
957        }
958    }
959
960    /* ... or a persistent one */
961    switch (zend_hash_find(&EG(persistent_list), hash, sizeof(hash), (void *) &le)) {
962        long l;
963
964        static char info[] = { isc_info_base_level, isc_info_end };
965        char result[8];
966        ISC_STATUS status[20];
967
968    case SUCCESS:
969
970        if (Z_TYPE_P(le) != le_plink) {
971            RETURN_FALSE;
972        }
973        /* check if connection has timed out */
974        ib_link = (ibase_db_link *) le->ptr;
975        if (!isc_database_info(status, &ib_link->handle, sizeof(info), info, sizeof(result), result)) {
976            ZEND_REGISTER_RESOURCE(return_value, ib_link, le_plink);
977            break;
978        }
979        zend_hash_del(&EG(persistent_list), hash, sizeof(hash));
980
981    default:
982
983        /* no link found, so we have to open one */
984
985        if ((l = INI_INT("ibase.max_links")) != -1 && IBG(num_links) >= l) {
986            _php_ibase_module_error("Too many open links (%ld)" TSRMLS_CC, IBG(num_links));
987            RETURN_FALSE;
988        }
989
990        /* create the ib_link */
991        if (FAILURE == _php_ibase_attach_db(args, len, largs, &db_handle TSRMLS_CC)) {
992            RETURN_FALSE;
993        }
994
995        /* use non-persistent if allowed number of persistent links is exceeded */
996        if (!persistent || ((l = INI_INT("ibase.max_persistent") != -1) && IBG(num_persistent) >= l)) {
997            ib_link = (ibase_db_link *) emalloc(sizeof(ibase_db_link));
998            ZEND_REGISTER_RESOURCE(return_value, ib_link, le_link);
999        } else {
1000            zend_rsrc_list_entry new_le;
1001
1002            ib_link = (ibase_db_link *) malloc(sizeof(ibase_db_link));
1003            if (!ib_link) {
1004                RETURN_FALSE;
1005            }
1006
1007            /* hash it up */
1008            Z_TYPE(new_le) = le_plink;
1009            new_le.ptr = ib_link;
1010            if (FAILURE == zend_hash_update(&EG(persistent_list), hash, sizeof(hash),
1011                    (void *) &new_le, sizeof(zend_rsrc_list_entry), NULL)) {
1012                free(ib_link);
1013                RETURN_FALSE;
1014            }
1015            ZEND_REGISTER_RESOURCE(return_value, ib_link, le_plink);
1016            ++IBG(num_persistent);
1017        }
1018        ib_link->handle = db_handle;
1019        ib_link->dialect = largs[DLECT] ? (unsigned short)largs[DLECT] : SQL_DIALECT_CURRENT;
1020        ib_link->tr_list = NULL;
1021        ib_link->event_head = NULL;
1022
1023        ++IBG(num_links);
1024    }
1025
1026    /* add it to the hash */
1027    new_index_ptr.ptr = (void *) Z_LVAL_P(return_value);
1028    Z_TYPE(new_index_ptr) = le_index_ptr;
1029    if (FAILURE == zend_hash_update(&EG(regular_list), hash, sizeof(hash),
1030            (void *) &new_index_ptr, sizeof(zend_rsrc_list_entry), NULL)) {
1031        RETURN_FALSE;
1032    }
1033    zend_list_addref(IBG(default_link) = Z_LVAL_P(return_value));
1034}
1035/* }}} */
1036
1037/* {{{ proto resource ibase_connect(string database [, string username [, string password [, string charset [, int buffers [, int dialect [, string role]]]]]])
1038   Open a connection to an InterBase database */
1039PHP_FUNCTION(ibase_connect)
1040{
1041    _php_ibase_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1042}
1043/* }}} */
1044
1045/* {{{ proto resource ibase_pconnect(string database [, string username [, string password [, string charset [, int buffers [, int dialect [, string role]]]]]])
1046   Open a persistent connection to an InterBase database */
1047PHP_FUNCTION(ibase_pconnect)
1048{
1049    _php_ibase_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INI_INT("ibase.allow_persistent"));
1050}
1051/* }}} */
1052
1053/* {{{ proto bool ibase_close([resource link_identifier])
1054   Close an InterBase connection */
1055PHP_FUNCTION(ibase_close)
1056{
1057    zval *link_arg = NULL;
1058    ibase_db_link *ib_link;
1059    int link_id;
1060
1061    RESET_ERRMSG;
1062
1063    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &link_arg) == FAILURE) {
1064        return;
1065    }
1066
1067    if (ZEND_NUM_ARGS() == 0) {
1068        link_id = IBG(default_link);
1069        CHECK_LINK(link_id);
1070        IBG(default_link) = -1;
1071    } else {
1072        link_id = Z_RESVAL_P(link_arg);
1073    }
1074
1075    ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, &link_arg, link_id, LE_LINK, le_link, le_plink);
1076    zend_list_delete(link_id);
1077    RETURN_TRUE;
1078}
1079/* }}} */
1080
1081/* {{{ proto bool ibase_drop_db([resource link_identifier])
1082   Drop an InterBase database */
1083PHP_FUNCTION(ibase_drop_db)
1084{
1085    zval *link_arg = NULL;
1086    ibase_db_link *ib_link;
1087    ibase_tr_list *l;
1088    int link_id;
1089
1090    RESET_ERRMSG;
1091
1092    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &link_arg) == FAILURE) {
1093        return;
1094    }
1095
1096    if (ZEND_NUM_ARGS() == 0) {
1097        link_id = IBG(default_link);
1098        CHECK_LINK(link_id);
1099        IBG(default_link) = -1;
1100    } else {
1101        link_id = Z_RESVAL_P(link_arg);
1102    }
1103
1104    ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, &link_arg, link_id, LE_LINK, le_link, le_plink);
1105
1106    if (isc_drop_database(IB_STATUS, &ib_link->handle)) {
1107        _php_ibase_error(TSRMLS_C);
1108        RETURN_FALSE;
1109    }
1110
1111    /* isc_drop_database() doesn't invalidate the transaction handles */
1112    for (l = ib_link->tr_list; l != NULL; l = l->next) {
1113        if (l->trans != NULL) l->trans->handle = NULL;
1114    }
1115
1116    zend_list_delete(link_id);
1117    RETURN_TRUE;
1118}
1119/* }}} */
1120
1121/* {{{ proto resource ibase_trans([int trans_args [, resource link_identifier [, ... ], int trans_args [, resource link_identifier [, ... ]] [, ...]]])
1122   Start a transaction over one or several databases */
1123
1124#define TPB_MAX_SIZE (8*sizeof(char))
1125
1126PHP_FUNCTION(ibase_trans)
1127{
1128    unsigned short i, link_cnt = 0, tpb_len = 0;
1129    int argn;
1130    char last_tpb[TPB_MAX_SIZE];
1131    ibase_db_link **ib_link = NULL;
1132    ibase_trans *ib_trans;
1133    isc_tr_handle tr_handle = NULL;
1134    ISC_STATUS result;
1135
1136    RESET_ERRMSG;
1137
1138    argn = ZEND_NUM_ARGS();
1139
1140    /* (1+argn) is an upper bound for the number of links this trans connects to */
1141    ib_link = (ibase_db_link **) safe_emalloc(sizeof(ibase_db_link *),1+argn,0);
1142
1143    if (argn > 0) {
1144        long trans_argl = 0;
1145        char *tpb;
1146        ISC_TEB *teb;
1147        zval ***args = NULL;
1148
1149        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argn) == FAILURE) {
1150            efree(args);
1151            efree(ib_link);
1152            RETURN_FALSE;
1153        }
1154
1155        teb = (ISC_TEB *) safe_emalloc(sizeof(ISC_TEB),argn,0);
1156        tpb = (char *) safe_emalloc(TPB_MAX_SIZE,argn,0);
1157
1158        /* enumerate all the arguments: assume every non-resource argument
1159           specifies modifiers for the link ids that follow it */
1160        for (i = 0; i < argn; ++i) {
1161
1162            if (Z_TYPE_PP(args[i]) == IS_RESOURCE) {
1163
1164                if (!ZEND_FETCH_RESOURCE2_NO_RETURN(ib_link[link_cnt], ibase_db_link *, args[i], -1, LE_LINK, le_link, le_plink)) {
1165                    efree(teb);
1166                    efree(tpb);
1167                    efree(ib_link);
1168                    efree(args);
1169                    RETURN_FALSE;
1170                }
1171
1172                /* copy the most recent modifier string into tbp[] */
1173                memcpy(&tpb[TPB_MAX_SIZE * link_cnt], last_tpb, TPB_MAX_SIZE);
1174
1175                /* add a database handle to the TEB with the most recently specified set of modifiers */
1176                teb[link_cnt].db_ptr = &ib_link[link_cnt]->handle;
1177                teb[link_cnt].tpb_len = tpb_len;
1178                teb[link_cnt].tpb_ptr = &tpb[TPB_MAX_SIZE * link_cnt];
1179
1180                ++link_cnt;
1181
1182            } else {
1183
1184                tpb_len = 0;
1185
1186                convert_to_long_ex(args[i]);
1187                trans_argl = Z_LVAL_PP(args[i]);
1188
1189                if (trans_argl != PHP_IBASE_DEFAULT) {
1190                    last_tpb[tpb_len++] = isc_tpb_version3;
1191
1192                    /* access mode */
1193                    if (PHP_IBASE_READ == (trans_argl & PHP_IBASE_READ)) {
1194                        last_tpb[tpb_len++] = isc_tpb_read;
1195                    } else if (PHP_IBASE_WRITE == (trans_argl & PHP_IBASE_WRITE)) {
1196                        last_tpb[tpb_len++] = isc_tpb_write;
1197                    }
1198
1199                    /* isolation level */
1200                    if (PHP_IBASE_COMMITTED == (trans_argl & PHP_IBASE_COMMITTED)) {
1201                        last_tpb[tpb_len++] = isc_tpb_read_committed;
1202                        if (PHP_IBASE_REC_VERSION == (trans_argl & PHP_IBASE_REC_VERSION)) {
1203                            last_tpb[tpb_len++] = isc_tpb_rec_version;
1204                        } else if (PHP_IBASE_REC_NO_VERSION == (trans_argl & PHP_IBASE_REC_NO_VERSION)) {
1205                            last_tpb[tpb_len++] = isc_tpb_no_rec_version;
1206                        }
1207                    } else if (PHP_IBASE_CONSISTENCY == (trans_argl & PHP_IBASE_CONSISTENCY)) {
1208                        last_tpb[tpb_len++] = isc_tpb_consistency;
1209                    } else if (PHP_IBASE_CONCURRENCY == (trans_argl & PHP_IBASE_CONCURRENCY)) {
1210                        last_tpb[tpb_len++] = isc_tpb_concurrency;
1211                    }
1212
1213                    /* lock resolution */
1214                    if (PHP_IBASE_NOWAIT == (trans_argl & PHP_IBASE_NOWAIT)) {
1215                        last_tpb[tpb_len++] = isc_tpb_nowait;
1216                    } else if (PHP_IBASE_WAIT == (trans_argl & PHP_IBASE_WAIT)) {
1217                        last_tpb[tpb_len++] = isc_tpb_wait;
1218                    }
1219                }
1220            }
1221        }
1222
1223        if (link_cnt > 0) {
1224            result = isc_start_multiple(IB_STATUS, &tr_handle, link_cnt, teb);
1225        }
1226
1227        efree(args);
1228        efree(tpb);
1229        efree(teb);
1230    }
1231
1232    if (link_cnt == 0) {
1233        link_cnt = 1;
1234        if (!ZEND_FETCH_RESOURCE2_NO_RETURN(ib_link[0], ibase_db_link *, NULL, IBG(default_link), LE_LINK, le_link, le_plink)) {
1235            efree(ib_link);
1236            RETURN_FALSE;
1237        }
1238        result = isc_start_transaction(IB_STATUS, &tr_handle, 1, &ib_link[0]->handle, tpb_len, last_tpb);
1239    }
1240
1241    /* start the transaction */
1242    if (result) {
1243        _php_ibase_error(TSRMLS_C);
1244        efree(ib_link);
1245        RETURN_FALSE;
1246    }
1247
1248    /* register the transaction in our own data structures */
1249    ib_trans = (ibase_trans *) safe_emalloc(link_cnt-1, sizeof(ibase_db_link *), sizeof(ibase_trans));
1250    ib_trans->handle = tr_handle;
1251    ib_trans->link_cnt = link_cnt;
1252    ib_trans->affected_rows = 0;
1253    for (i = 0; i < link_cnt; ++i) {
1254        ibase_tr_list **l;
1255        ib_trans->db_link[i] = ib_link[i];
1256
1257        /* the first item in the connection-transaction list is reserved for the default transaction */
1258        if (ib_link[i]->tr_list == NULL) {
1259            ib_link[i]->tr_list = (ibase_tr_list *) emalloc(sizeof(ibase_tr_list));
1260            ib_link[i]->tr_list->trans = NULL;
1261            ib_link[i]->tr_list->next = NULL;
1262        }
1263
1264        /* link the transaction into the connection-transaction list */
1265        for (l = &ib_link[i]->tr_list; *l != NULL; l = &(*l)->next);
1266        *l = (ibase_tr_list *) emalloc(sizeof(ibase_tr_list));
1267        (*l)->trans = ib_trans;
1268        (*l)->next = NULL;
1269    }
1270    efree(ib_link);
1271    ZEND_REGISTER_RESOURCE(return_value, ib_trans, le_trans);
1272}
1273/* }}} */
1274
1275int _php_ibase_def_trans(ibase_db_link *ib_link, ibase_trans **trans TSRMLS_DC) /* {{{ */
1276{
1277    if (ib_link == NULL) {
1278        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid database link");
1279        return FAILURE;
1280    }
1281
1282    /* the first item in the connection-transaction list is reserved for the default transaction */
1283    if (ib_link->tr_list == NULL) {
1284        ib_link->tr_list = (ibase_tr_list *) emalloc(sizeof(ibase_tr_list));
1285        ib_link->tr_list->trans = NULL;
1286        ib_link->tr_list->next = NULL;
1287    }
1288
1289    if (*trans == NULL) {
1290        ibase_trans *tr = ib_link->tr_list->trans;
1291
1292        if (tr == NULL) {
1293            tr = (ibase_trans *) emalloc(sizeof(ibase_trans));
1294            tr->handle = NULL;
1295            tr->link_cnt = 1;
1296            tr->affected_rows = 0;
1297            tr->db_link[0] = ib_link;
1298            ib_link->tr_list->trans = tr;
1299        }
1300        if (tr->handle == NULL) {
1301            if (isc_start_transaction(IB_STATUS, &tr->handle, 1, &ib_link->handle, 0, NULL)) {
1302                _php_ibase_error(TSRMLS_C);
1303                return FAILURE;
1304            }
1305        }
1306        *trans = tr;
1307    }
1308    return SUCCESS;
1309}
1310/* }}} */
1311
1312static void _php_ibase_trans_end(INTERNAL_FUNCTION_PARAMETERS, int commit) /* {{{ */
1313{
1314    ibase_trans *trans = NULL;
1315    int res_id = 0;
1316    ISC_STATUS result;
1317    ibase_db_link *ib_link;
1318    zval *arg = NULL;
1319    int type;
1320
1321    RESET_ERRMSG;
1322
1323    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &arg) == FAILURE) {
1324        return;
1325    }
1326
1327    if (ZEND_NUM_ARGS() == 0) {
1328        ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, NULL, IBG(default_link), LE_LINK, le_link, le_plink);
1329        if (ib_link->tr_list == NULL || ib_link->tr_list->trans == NULL) {
1330            /* this link doesn't have a default transaction */
1331            _php_ibase_module_error("Default link has no default transaction" TSRMLS_CC);
1332            RETURN_FALSE;
1333        }
1334        trans = ib_link->tr_list->trans;
1335    } else {
1336        /* one id was passed, could be db or trans id */
1337        if (zend_list_find(Z_RESVAL_P(arg), &type) && type == le_trans) {
1338            ZEND_FETCH_RESOURCE(trans, ibase_trans *, &arg, -1, LE_TRANS, le_trans);
1339            res_id = Z_RESVAL_P(arg);
1340        } else {
1341            ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, &arg, -1, LE_LINK, le_link, le_plink);
1342
1343            if (ib_link->tr_list == NULL || ib_link->tr_list->trans == NULL) {
1344                /* this link doesn't have a default transaction */
1345                _php_ibase_module_error("Link has no default transaction" TSRMLS_CC);
1346                RETURN_FALSE;
1347            }
1348            trans = ib_link->tr_list->trans;
1349        }
1350    }
1351
1352    switch (commit) {
1353        default: /* == case ROLLBACK: */
1354            result = isc_rollback_transaction(IB_STATUS, &trans->handle);
1355            break;
1356        case COMMIT:
1357            result = isc_commit_transaction(IB_STATUS, &trans->handle);
1358            break;
1359        case (ROLLBACK | RETAIN):
1360            result = isc_rollback_retaining(IB_STATUS, &trans->handle);
1361            break;
1362        case (COMMIT | RETAIN):
1363            result = isc_commit_retaining(IB_STATUS, &trans->handle);
1364            break;
1365    }
1366
1367    if (result) {
1368        _php_ibase_error(TSRMLS_C);
1369        RETURN_FALSE;
1370    }
1371
1372    /* Don't try to destroy implicitly opened transaction from list... */
1373    if ((commit & RETAIN) == 0 && res_id != 0) {
1374        zend_list_delete(res_id);
1375    }
1376    RETURN_TRUE;
1377}
1378/* }}} */
1379
1380/* {{{ proto bool ibase_commit( resource link_identifier )
1381   Commit transaction */
1382PHP_FUNCTION(ibase_commit)
1383{
1384    _php_ibase_trans_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, COMMIT);
1385}
1386/* }}} */
1387
1388/* {{{ proto bool ibase_rollback( resource link_identifier )
1389   Rollback transaction */
1390PHP_FUNCTION(ibase_rollback)
1391{
1392    _php_ibase_trans_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, ROLLBACK);
1393}
1394/* }}} */
1395
1396/* {{{ proto bool ibase_commit_ret( resource link_identifier )
1397   Commit transaction and retain the transaction context */
1398PHP_FUNCTION(ibase_commit_ret)
1399{
1400    _php_ibase_trans_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, COMMIT | RETAIN);
1401}
1402/* }}} */
1403
1404/* {{{ proto bool ibase_rollback_ret( resource link_identifier )
1405   Rollback transaction and retain the transaction context */
1406PHP_FUNCTION(ibase_rollback_ret)
1407{
1408    _php_ibase_trans_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, ROLLBACK | RETAIN);
1409}
1410/* }}} */
1411
1412/* {{{ proto int ibase_gen_id(string generator [, int increment [, resource link_identifier ]])
1413   Increments the named generator and returns its new value */
1414PHP_FUNCTION(ibase_gen_id)
1415{
1416    zval *link = NULL;
1417    char query[128], *generator;
1418    int gen_len;
1419    long inc = 1;
1420    ibase_db_link *ib_link;
1421    ibase_trans *trans = NULL;
1422    XSQLDA out_sqlda;
1423    ISC_INT64 result;
1424
1425    RESET_ERRMSG;
1426
1427    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lr", &generator, &gen_len,
1428            &inc, &link)) {
1429        RETURN_FALSE;
1430    }
1431
1432    if (gen_len > 31) {
1433        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid generator name");
1434        RETURN_FALSE;
1435    }
1436
1437    PHP_IBASE_LINK_TRANS(link, ib_link, trans);
1438
1439    snprintf(query, sizeof(query), "SELECT GEN_ID(%s,%ld) FROM rdb$database", generator, inc);
1440
1441    /* allocate a minimal descriptor area */
1442    out_sqlda.sqln = out_sqlda.sqld = 1;
1443    out_sqlda.version = SQLDA_CURRENT_VERSION;
1444
1445    /* allocate the field for the result */
1446    out_sqlda.sqlvar[0].sqltype = SQL_INT64;
1447    out_sqlda.sqlvar[0].sqlscale = 0;
1448    out_sqlda.sqlvar[0].sqllen = sizeof(result);
1449    out_sqlda.sqlvar[0].sqldata = (void*) &result;
1450
1451    /* execute the query */
1452    if (isc_dsql_exec_immed2(IB_STATUS, &ib_link->handle, &trans->handle, 0, query,
1453            SQL_DIALECT_CURRENT, NULL, &out_sqlda)) {
1454        _php_ibase_error(TSRMLS_C);
1455        RETURN_FALSE;
1456    }
1457
1458    /* don't return the generator value as a string unless it doesn't fit in a long */
1459#if SIZEOF_LONG < 8
1460    if (result < LONG_MIN || result > LONG_MAX) {
1461        char *res;
1462        int l;
1463
1464        l = spprintf(&res, 0, "%" LL_MASK "d", result);
1465        RETURN_STRINGL(res, l, 0);
1466    }
1467#endif
1468    RETURN_LONG((long)result);
1469}
1470
1471/* }}} */
1472
1473#endif /* HAVE_IBASE */
1474
1475/*
1476 * Local variables:
1477 * tab-width: 4
1478 * c-basic-offset: 4
1479 * End:
1480 * vim600: sw=4 ts=4 fdm=marker
1481 * vim<600: sw=4 ts=4
1482 */
1483