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