1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2016 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==NULL) { php_error_docref(NULL, 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	PHP_INTERBASE_VERSION,
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
463#ifdef ZTS
464ZEND_TSRMLS_CACHE_DEFINE()
465#endif
466ZEND_GET_MODULE(ibase)
467#endif
468
469/* True globals, no need for thread safety */
470int le_link, le_plink, le_trans;
471
472/* }}} */
473
474/* error handling ---------------------------- */
475
476/* {{{ proto string ibase_errmsg(void)
477   Return error message */
478PHP_FUNCTION(ibase_errmsg)
479{
480	if (zend_parse_parameters_none() == FAILURE) {
481		return;
482	}
483
484	if (IBG(sql_code) != 0) {
485		RETURN_STRING(IBG(errmsg));
486	}
487
488	RETURN_FALSE;
489}
490/* }}} */
491
492/* {{{ proto int ibase_errcode(void)
493   Return error code */
494PHP_FUNCTION(ibase_errcode)
495{
496	if (zend_parse_parameters_none() == FAILURE) {
497		return;
498	}
499
500	if (IBG(sql_code) != 0) {
501		RETURN_LONG(IBG(sql_code));
502	}
503	RETURN_FALSE;
504}
505/* }}} */
506
507/* print interbase error and save it for ibase_errmsg() */
508void _php_ibase_error(void) /* {{{ */
509{
510	char *s = IBG(errmsg);
511	const ISC_STATUS *statusp = IB_STATUS;
512
513	IBG(sql_code) = isc_sqlcode(IB_STATUS);
514
515	while ((s - IBG(errmsg)) < MAX_ERRMSG && fb_interpret(s, MAX_ERRMSG - strlen(IBG(errmsg)) - 1, &statusp)) {
516		strcat(IBG(errmsg), " ");
517		s = IBG(errmsg) + strlen(IBG(errmsg));
518	}
519
520	php_error_docref(NULL, E_WARNING, "%s", IBG(errmsg));
521}
522/* }}} */
523
524/* print php interbase module error and save it for ibase_errmsg() */
525void _php_ibase_module_error(char *msg, ...) /* {{{ */
526{
527	va_list ap;
528
529	va_start(ap, msg);
530
531	/* vsnprintf NUL terminates the buf and writes at most n-1 chars+NUL */
532	vsnprintf(IBG(errmsg), MAX_ERRMSG, msg, ap);
533	va_end(ap);
534
535	IBG(sql_code) = -999; /* no SQL error */
536
537	php_error_docref(NULL, E_WARNING, "%s", IBG(errmsg));
538}
539/* }}} */
540
541/* {{{ internal macros, functions and structures */
542typedef struct {
543	isc_db_handle *db_ptr;
544	zend_long tpb_len;
545	char *tpb_ptr;
546} ISC_TEB;
547
548/* }}} */
549
550/* Fill ib_link and trans with the correct database link and transaction. */
551void _php_ibase_get_link_trans(INTERNAL_FUNCTION_PARAMETERS, /* {{{ */
552	zval *link_id, ibase_db_link **ib_link, ibase_trans **trans)
553{
554	IBDEBUG("Transaction or database link?");
555	if (Z_RES_P(link_id)->type == le_trans) {
556		/* Transaction resource: make sure it refers to one link only, then
557		   fetch it; database link is stored in ib_trans->db_link[]. */
558		IBDEBUG("Type is le_trans");
559		*trans = (ibase_trans *)zend_fetch_resource_ex(link_id, LE_TRANS, le_trans);
560		if ((*trans)->link_cnt > 1) {
561			_php_ibase_module_error("Link id is ambiguous: transaction spans multiple connections."
562				);
563			return;
564		}
565		*ib_link = (*trans)->db_link[0];
566		return;
567	}
568	IBDEBUG("Type is le_[p]link or id not found");
569	/* Database link resource, use default transaction. */
570	*trans = NULL;
571	*ib_link = (ibase_db_link *)zend_fetch_resource2_ex(link_id, LE_LINK, le_link, le_plink);
572}
573/* }}} */
574
575/* destructors ---------------------- */
576
577static void _php_ibase_commit_link(ibase_db_link *link) /* {{{ */
578{
579	unsigned short i = 0, j;
580	ibase_tr_list *l;
581	ibase_event *e;
582	IBDEBUG("Checking transactions to close...");
583
584	for (l = link->tr_list; l != NULL; ++i) {
585		ibase_tr_list *p = l;
586		if (p->trans != 0) {
587			if (i == 0) {
588				if (p->trans->handle != 0) {
589					IBDEBUG("Committing default transaction...");
590					if (isc_commit_transaction(IB_STATUS, &p->trans->handle)) {
591						_php_ibase_error();
592					}
593				}
594				efree(p->trans); /* default transaction is not a registered resource: clean up */
595			} else {
596				if (p->trans->handle != 0) {
597					/* non-default trans might have been rolled back by other call of this dtor */
598					IBDEBUG("Rolling back other transactions...");
599					if (isc_rollback_transaction(IB_STATUS, &p->trans->handle)) {
600						_php_ibase_error();
601					}
602				}
603				/* set this link pointer to NULL in the transaction */
604				for (j = 0; j < p->trans->link_cnt; ++j) {
605					if (p->trans->db_link[j] == link) {
606						p->trans->db_link[j] = NULL;
607						break;
608					}
609				}
610			}
611		}
612		l = l->next;
613		efree(p);
614	}
615	link->tr_list = NULL;
616
617	for (e = link->event_head; e; e = e->event_next) {
618		_php_ibase_free_event(e);
619		e->link = NULL;
620	}
621}
622
623/* }}} */
624
625static void php_ibase_commit_link_rsrc(zend_resource *rsrc) /* {{{ */
626{
627	ibase_db_link *link = (ibase_db_link *) rsrc->ptr;
628
629	_php_ibase_commit_link(link);
630}
631/* }}} */
632
633static void _php_ibase_close_link(zend_resource *rsrc) /* {{{ */
634{
635	ibase_db_link *link = (ibase_db_link *) rsrc->ptr;
636
637	_php_ibase_commit_link(link);
638	if (link->handle != 0) {
639		IBDEBUG("Closing normal link...");
640		isc_detach_database(IB_STATUS, &link->handle);
641	}
642	IBG(num_links)--;
643	efree(link);
644}
645/* }}} */
646
647static void _php_ibase_close_plink(zend_resource *rsrc) /* {{{ */
648{
649	ibase_db_link *link = (ibase_db_link *) rsrc->ptr;
650
651	_php_ibase_commit_link(link);
652	IBDEBUG("Closing permanent link...");
653	if (link->handle != 0) {
654		isc_detach_database(IB_STATUS, &link->handle);
655	}
656	IBG(num_persistent)--;
657	IBG(num_links)--;
658	free(link);
659}
660/* }}} */
661
662static void _php_ibase_free_trans(zend_resource *rsrc) /* {{{ */
663{
664	ibase_trans *trans = (ibase_trans *)rsrc->ptr;
665	unsigned short i;
666
667	IBDEBUG("Cleaning up transaction resource...");
668	if (trans->handle != 0) {
669		IBDEBUG("Rolling back unhandled transaction...");
670		if (isc_rollback_transaction(IB_STATUS, &trans->handle)) {
671			_php_ibase_error();
672		}
673	}
674
675	/* now remove this transaction from all the connection-transaction lists */
676	for (i = 0; i < trans->link_cnt; ++i) {
677		if (trans->db_link[i] != NULL) {
678			ibase_tr_list **l;
679			for (l = &trans->db_link[i]->tr_list; *l != NULL; l = &(*l)->next) {
680				if ( (*l)->trans == trans) {
681					ibase_tr_list *p = *l;
682					*l = p->next;
683					efree(p);
684					break;
685				}
686			}
687		}
688	}
689	efree(trans);
690}
691/* }}} */
692
693/* TODO this function should be part of either Zend or PHP API */
694static PHP_INI_DISP(php_ibase_password_displayer_cb)
695{
696
697	if ((type == PHP_INI_DISPLAY_ORIG && ini_entry->orig_value)
698			|| (type == PHP_INI_DISPLAY_ACTIVE && ini_entry->value)) {
699		PUTS("********");
700	} else if (!sapi_module.phpinfo_as_text) {
701		PUTS("<i>no value</i>");
702	} else {
703		PUTS("no value");
704	}
705}
706
707/* {{{ startup, shutdown and info functions */
708PHP_INI_BEGIN()
709	PHP_INI_ENTRY_EX("ibase.allow_persistent", "1", PHP_INI_SYSTEM, NULL, zend_ini_boolean_displayer_cb)
710	PHP_INI_ENTRY_EX("ibase.max_persistent", "-1", PHP_INI_SYSTEM, NULL, display_link_numbers)
711	PHP_INI_ENTRY_EX("ibase.max_links", "-1", PHP_INI_SYSTEM, NULL, display_link_numbers)
712	PHP_INI_ENTRY("ibase.default_db", NULL, PHP_INI_SYSTEM, NULL)
713	PHP_INI_ENTRY("ibase.default_user", NULL, PHP_INI_ALL, NULL)
714	PHP_INI_ENTRY_EX("ibase.default_password", NULL, PHP_INI_ALL, NULL, php_ibase_password_displayer_cb)
715	PHP_INI_ENTRY("ibase.default_charset", NULL, PHP_INI_ALL, NULL)
716	PHP_INI_ENTRY("ibase.timestampformat", IB_DEF_DATE_FMT " " IB_DEF_TIME_FMT, PHP_INI_ALL, NULL)
717	PHP_INI_ENTRY("ibase.dateformat", IB_DEF_DATE_FMT, PHP_INI_ALL, NULL)
718	PHP_INI_ENTRY("ibase.timeformat", IB_DEF_TIME_FMT, PHP_INI_ALL, NULL)
719PHP_INI_END()
720
721static PHP_GINIT_FUNCTION(ibase)
722{
723#if defined(COMPILE_DL_INTERBASE) && defined(ZTS)
724	ZEND_TSRMLS_CACHE_UPDATE();
725#endif
726	ibase_globals->num_persistent = ibase_globals->num_links = 0;
727	ibase_globals->sql_code = *ibase_globals->errmsg = 0;
728	ibase_globals->default_link = NULL;
729}
730
731PHP_MINIT_FUNCTION(ibase)
732{
733	REGISTER_INI_ENTRIES();
734
735	le_link = zend_register_list_destructors_ex(_php_ibase_close_link, NULL, LE_LINK, module_number);
736	le_plink = zend_register_list_destructors_ex(php_ibase_commit_link_rsrc, _php_ibase_close_plink, LE_PLINK, module_number);
737	le_trans = zend_register_list_destructors_ex(_php_ibase_free_trans, NULL, LE_TRANS, module_number);
738
739	REGISTER_LONG_CONSTANT("IBASE_DEFAULT", PHP_IBASE_DEFAULT, CONST_PERSISTENT);
740	REGISTER_LONG_CONSTANT("IBASE_CREATE", PHP_IBASE_CREATE, CONST_PERSISTENT);
741	REGISTER_LONG_CONSTANT("IBASE_TEXT", PHP_IBASE_FETCH_BLOBS, CONST_PERSISTENT); /* deprecated, for BC only */
742	REGISTER_LONG_CONSTANT("IBASE_FETCH_BLOBS", PHP_IBASE_FETCH_BLOBS, CONST_PERSISTENT);
743	REGISTER_LONG_CONSTANT("IBASE_FETCH_ARRAYS", PHP_IBASE_FETCH_ARRAYS, CONST_PERSISTENT);
744	REGISTER_LONG_CONSTANT("IBASE_UNIXTIME", PHP_IBASE_UNIXTIME, CONST_PERSISTENT);
745
746	/* transactions */
747	REGISTER_LONG_CONSTANT("IBASE_WRITE", PHP_IBASE_WRITE, CONST_PERSISTENT);
748	REGISTER_LONG_CONSTANT("IBASE_READ", PHP_IBASE_READ, CONST_PERSISTENT);
749	REGISTER_LONG_CONSTANT("IBASE_COMMITTED", PHP_IBASE_COMMITTED, CONST_PERSISTENT);
750	REGISTER_LONG_CONSTANT("IBASE_CONSISTENCY", PHP_IBASE_CONSISTENCY, CONST_PERSISTENT);
751	REGISTER_LONG_CONSTANT("IBASE_CONCURRENCY", PHP_IBASE_CONCURRENCY, CONST_PERSISTENT);
752	REGISTER_LONG_CONSTANT("IBASE_REC_VERSION", PHP_IBASE_REC_VERSION, CONST_PERSISTENT);
753	REGISTER_LONG_CONSTANT("IBASE_REC_NO_VERSION", PHP_IBASE_REC_NO_VERSION, CONST_PERSISTENT);
754	REGISTER_LONG_CONSTANT("IBASE_NOWAIT", PHP_IBASE_NOWAIT, CONST_PERSISTENT);
755	REGISTER_LONG_CONSTANT("IBASE_WAIT", PHP_IBASE_WAIT, CONST_PERSISTENT);
756
757	php_ibase_query_minit(INIT_FUNC_ARGS_PASSTHRU);
758	php_ibase_blobs_minit(INIT_FUNC_ARGS_PASSTHRU);
759	php_ibase_events_minit(INIT_FUNC_ARGS_PASSTHRU);
760	php_ibase_service_minit(INIT_FUNC_ARGS_PASSTHRU);
761
762	return SUCCESS;
763}
764
765PHP_MSHUTDOWN_FUNCTION(ibase)
766{
767#ifndef PHP_WIN32
768	/**
769	 * When the Interbase client API library libgds.so is first loaded, it registers a call to
770	 * gds__cleanup() with atexit(), in order to clean up after itself when the process exits.
771	 * This means that the library is called at process shutdown, and cannot be unloaded beforehand.
772	 * PHP tries to unload modules after every request [dl()'ed modules], and right before the
773	 * process shuts down [modules loaded from php.ini]. This results in a segfault for this module.
774	 * By NULLing the dlopen() handle in the module entry, Zend omits the call to dlclose(),
775	 * ensuring that the module will remain present until the process exits. However, the functions
776	 * and classes exported by the module will not be available until the module is 'reloaded'.
777	 * When reloaded, dlopen() will return the handle of the already loaded module. The module will
778	 * be unloaded automatically when the process exits.
779	 */
780	zend_module_entry *ibase_entry;
781	if ((ibase_entry = zend_hash_str_find_ptr(&module_registry, ibase_module_entry.name,
782			strlen(ibase_module_entry.name))) != NULL) {
783		ibase_entry->handle = 0;
784	}
785#endif
786	UNREGISTER_INI_ENTRIES();
787	return SUCCESS;
788}
789
790PHP_RSHUTDOWN_FUNCTION(ibase)
791{
792	IBG(num_links) = IBG(num_persistent);
793	IBG(default_link)= NULL;
794
795	RESET_ERRMSG;
796
797	return SUCCESS;
798}
799
800PHP_MINFO_FUNCTION(ibase)
801{
802	char tmp[64], *s;
803
804	php_info_print_table_start();
805	php_info_print_table_row(2, "Firebird/InterBase Support",
806#ifdef COMPILE_DL_INTERBASE
807		"dynamic");
808#else
809		"static");
810#endif
811
812#ifdef FB_API_VER
813	snprintf( (s = tmp), sizeof(tmp), "Firebird API version %d", FB_API_VER);
814#elif (SQLDA_CURRENT_VERSION > 1)
815	s =  "Interbase 7.0 and up";
816#endif
817	php_info_print_table_row(2, "Compile-time Client Library Version", s);
818
819#if defined(__GNUC__) || defined(PHP_WIN32)
820	do {
821		info_func_t info_func = NULL;
822#ifdef __GNUC__
823		info_func = (info_func_t)dlsym(RTLD_DEFAULT, "isc_get_client_version");
824#else
825		HMODULE l = GetModuleHandle("fbclient");
826
827		if (!l && !(l = GetModuleHandle("gds32"))) {
828			break;
829		}
830		info_func = (info_func_t)GetProcAddress(l, "isc_get_client_version");
831#endif
832		if (info_func) {
833			info_func(s = tmp);
834		}
835		php_info_print_table_row(2, "Run-time Client Library Version", s);
836	} while (0);
837#endif
838	php_info_print_table_end();
839
840	DISPLAY_INI_ENTRIES();
841
842}
843/* }}} */
844
845enum connect_args { DB = 0, USER = 1, PASS = 2, CSET = 3, ROLE = 4, BUF = 0, DLECT = 1, SYNC = 2 };
846
847static char const dpb_args[] = {
848	0, isc_dpb_user_name, isc_dpb_password, isc_dpb_lc_ctype, isc_dpb_sql_role_name, 0
849};
850
851int _php_ibase_attach_db(char **args, size_t *len, zend_long *largs, isc_db_handle *db) /* {{{ */
852{
853	short i, dpb_len, buf_len = 257-2;  /* version byte at the front, and a null at the end */
854	char dpb_buffer[257] = { isc_dpb_version1, 0 }, *dpb;
855
856	dpb = dpb_buffer + 1;
857
858	for (i = 0; i < sizeof(dpb_args); ++i) {
859		if (dpb_args[i] && args[i] && len[i] && buf_len > 0) {
860			dpb_len = slprintf(dpb, buf_len, "%c%c%s", dpb_args[i],(unsigned char)len[i],args[i]);
861			dpb += dpb_len;
862			buf_len -= dpb_len;
863		}
864	}
865	if (largs[BUF] && buf_len > 0) {
866		dpb_len = slprintf(dpb, buf_len, "%c\2%c%c", isc_dpb_num_buffers,
867			(char)(largs[BUF] >> 8), (char)(largs[BUF] & 0xff));
868		dpb += dpb_len;
869		buf_len -= dpb_len;
870	}
871	if (largs[SYNC] && buf_len > 0) {
872		dpb_len = slprintf(dpb, buf_len, "%c\1%c", isc_dpb_force_write, largs[SYNC] == isc_spb_prp_wm_sync ? 1 : 0);
873		dpb += dpb_len;
874		buf_len -= dpb_len;
875	}
876	if (isc_attach_database(IB_STATUS, (short)len[DB], args[DB], db, (short)(dpb-dpb_buffer), dpb_buffer)) {
877		_php_ibase_error();
878		return FAILURE;
879	}
880	return SUCCESS;
881}
882/* }}} */
883
884static void _php_ibase_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) /* {{{ */
885{
886	char *c, hash[16], *args[] = { NULL, NULL, NULL, NULL, NULL };
887	int i;
888	size_t len[] = { 0, 0, 0, 0, 0 };
889	zend_long largs[] = { 0, 0, 0 };
890	PHP_MD5_CTX hash_context;
891	zend_resource new_index_ptr, *le;
892	isc_db_handle db_handle = 0;
893	ibase_db_link *ib_link;
894
895	RESET_ERRMSG;
896
897	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|ssssllsl",
898			&args[DB], &len[DB], &args[USER], &len[USER], &args[PASS], &len[PASS],
899			&args[CSET], &len[CSET], &largs[BUF], &largs[DLECT], &args[ROLE], &len[ROLE],
900			&largs[SYNC])) {
901		RETURN_FALSE;
902	}
903
904	/* restrict to the server/db in the .ini if in safe mode */
905	if ((!len[DB] || PG(sql_safe_mode)) && (c = INI_STR("ibase.default_db"))) {
906		args[DB] = c;
907		len[DB] = strlen(c);
908	}
909	if (!len[USER] && (c = INI_STR("ibase.default_user"))) {
910		args[USER] = c;
911		len[USER] = strlen(c);
912	}
913	if (!len[PASS] && (c = INI_STR("ibase.default_password"))) {
914		args[PASS] = c;
915		len[PASS] = strlen(c);
916	}
917	if (!len[CSET] && (c = INI_STR("ibase.default_charset"))) {
918		args[CSET] = c;
919		len[CSET] = strlen(c);
920	}
921
922	/* don't want usernames and passwords floating around */
923	PHP_MD5Init(&hash_context);
924	for (i = 0; i < sizeof(args)/sizeof(char*); ++i) {
925		PHP_MD5Update(&hash_context,args[i],len[i]);
926	}
927	for (i = 0; i < sizeof(largs)/sizeof(zend_long); ++i) {
928		PHP_MD5Update(&hash_context,(char*)&largs[i],sizeof(zend_long));
929	}
930	PHP_MD5Final((unsigned char*)hash, &hash_context);
931
932	/* try to reuse a connection */
933	if ((le = zend_hash_str_find_ptr(&EG(regular_list), hash, sizeof(hash)-1)) != NULL) {
934		zend_resource *xlink;
935
936		if (le->type != le_index_ptr) {
937			RETURN_FALSE;
938		}
939
940		xlink = (zend_resource*) le->ptr;
941		if ((!persistent && xlink->type == le_link) || xlink->type == le_plink) {
942			if (IBG(default_link)) {
943				zend_list_close(IBG(default_link));
944			}
945			xlink->gc.refcount++;
946			xlink->gc.refcount++;
947			IBG(default_link) = xlink;
948			RETVAL_RES(xlink);
949		} else {
950			zend_hash_str_del(&EG(regular_list), hash, sizeof(hash)-1);
951		}
952	}
953
954	/* ... or a persistent one */
955	do {
956		zend_long l;
957		static char info[] = { isc_info_base_level, isc_info_end };
958		char result[8];
959		ISC_STATUS status[20];
960
961		if ((le = zend_hash_str_find_ptr(&EG(persistent_list), hash, sizeof(hash)-1)) != NULL) {
962			if (le->type != le_plink) {
963				RETURN_FALSE;
964			}
965			/* check if connection has timed out */
966			ib_link = (ibase_db_link *) le->ptr;
967			if (!isc_database_info(status, &ib_link->handle, sizeof(info), info, sizeof(result), result)) {
968				RETVAL_RES(zend_register_resource(ib_link, le_plink));
969				break;
970			}
971			zend_hash_str_del(&EG(persistent_list), hash, sizeof(hash)-1);
972		}
973
974		/* no link found, so we have to open one */
975
976		if ((l = INI_INT("ibase.max_links")) != -1 && IBG(num_links) >= l) {
977			_php_ibase_module_error("Too many open links (%ld)", IBG(num_links));
978			RETURN_FALSE;
979		}
980
981		/* create the ib_link */
982		if (FAILURE == _php_ibase_attach_db(args, len, largs, &db_handle)) {
983			RETURN_FALSE;
984		}
985
986		/* use non-persistent if allowed number of persistent links is exceeded */
987		if (!persistent || ((l = INI_INT("ibase.max_persistent") != -1) && IBG(num_persistent) >= l)) {
988			ib_link = (ibase_db_link *) emalloc(sizeof(ibase_db_link));
989			RETVAL_RES(zend_register_resource(ib_link, le_link));
990		} else {
991			zend_resource new_le;
992
993			ib_link = (ibase_db_link *) malloc(sizeof(ibase_db_link));
994			if (!ib_link) {
995				RETURN_FALSE;
996			}
997
998			/* hash it up */
999			new_le.type = le_plink;
1000			new_le.ptr = ib_link;
1001			if (zend_hash_str_update_mem(&EG(persistent_list), hash, sizeof(hash)-1,
1002					(void *) &new_le, sizeof(zend_resource)) == NULL) {
1003				free(ib_link);
1004				RETURN_FALSE;
1005			}
1006			RETVAL_RES(zend_register_resource(ib_link, le_plink));
1007			++IBG(num_persistent);
1008		}
1009		ib_link->handle = db_handle;
1010		ib_link->dialect = largs[DLECT] ? (unsigned short)largs[DLECT] : SQL_DIALECT_CURRENT;
1011		ib_link->tr_list = NULL;
1012		ib_link->event_head = NULL;
1013
1014		++IBG(num_links);
1015	} while (0);
1016
1017	/* add it to the hash */
1018	new_index_ptr.ptr = (void *) Z_RES_P(return_value);
1019	new_index_ptr.type = le_index_ptr;
1020	if (zend_hash_str_update_mem(&EG(regular_list), hash, sizeof(hash)-1,
1021			(void *) &new_index_ptr, sizeof(zend_resource)) == NULL) {
1022		RETURN_FALSE;
1023	}
1024	if (IBG(default_link)) {
1025		zend_list_delete(IBG(default_link));
1026	}
1027	IBG(default_link) = Z_RES_P(return_value);
1028	Z_TRY_ADDREF_P(return_value);
1029	Z_TRY_ADDREF_P(return_value);
1030}
1031/* }}} */
1032
1033/* {{{ proto resource ibase_connect(string database [, string username [, string password [, string charset [, int buffers [, int dialect [, string role]]]]]])
1034   Open a connection to an InterBase database */
1035PHP_FUNCTION(ibase_connect)
1036{
1037	_php_ibase_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1038}
1039/* }}} */
1040
1041/* {{{ proto resource ibase_pconnect(string database [, string username [, string password [, string charset [, int buffers [, int dialect [, string role]]]]]])
1042   Open a persistent connection to an InterBase database */
1043PHP_FUNCTION(ibase_pconnect)
1044{
1045	_php_ibase_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INI_INT("ibase.allow_persistent"));
1046}
1047/* }}} */
1048
1049/* {{{ proto bool ibase_close([resource link_identifier])
1050   Close an InterBase connection */
1051PHP_FUNCTION(ibase_close)
1052{
1053	zval *link_arg = NULL;
1054	zend_resource *link_res;
1055
1056	RESET_ERRMSG;
1057
1058	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|r", &link_arg) == FAILURE) {
1059		return;
1060	}
1061
1062	if (ZEND_NUM_ARGS() == 0) {
1063		link_res = IBG(default_link);
1064		CHECK_LINK(link_res);
1065		IBG(default_link) = NULL;
1066	} else {
1067		link_res = Z_RES_P(link_arg);
1068	}
1069
1070	/* we have at least 3 additional references to this resource ??? */
1071	if (GC_REFCOUNT(link_res) < 4) {
1072		zend_list_close(link_res);
1073	} else {
1074		zend_list_delete(link_res);
1075	}
1076	RETURN_TRUE;
1077}
1078/* }}} */
1079
1080/* {{{ proto bool ibase_drop_db([resource link_identifier])
1081   Drop an InterBase database */
1082PHP_FUNCTION(ibase_drop_db)
1083{
1084	zval *link_arg = NULL;
1085	ibase_db_link *ib_link;
1086	ibase_tr_list *l;
1087	zend_resource *link_res;
1088
1089	RESET_ERRMSG;
1090
1091	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|r", &link_arg) == FAILURE) {
1092		return;
1093	}
1094
1095	if (ZEND_NUM_ARGS() == 0) {
1096		link_res = IBG(default_link);
1097		CHECK_LINK(link_res);
1098		IBG(default_link) = NULL;
1099	} else {
1100		link_res = Z_RES_P(link_arg);
1101	}
1102
1103	ib_link = (ibase_db_link *)zend_fetch_resource2(link_res, LE_LINK, le_link, le_plink);
1104
1105	if (!ib_link) {
1106		RETURN_FALSE;
1107	}
1108
1109	if (isc_drop_database(IB_STATUS, &ib_link->handle)) {
1110		_php_ibase_error();
1111		RETURN_FALSE;
1112	}
1113
1114	/* isc_drop_database() doesn't invalidate the transaction handles */
1115	for (l = ib_link->tr_list; l != NULL; l = l->next) {
1116		if (l->trans != NULL) l->trans->handle = 0;
1117	}
1118
1119	zend_list_delete(link_res);
1120
1121	RETURN_TRUE;
1122}
1123/* }}} */
1124
1125/* {{{ proto resource ibase_trans([int trans_args [, resource link_identifier [, ... ], int trans_args [, resource link_identifier [, ... ]] [, ...]]])
1126   Start a transaction over one or several databases */
1127
1128#define TPB_MAX_SIZE (8*sizeof(char))
1129
1130PHP_FUNCTION(ibase_trans)
1131{
1132	unsigned short i, link_cnt = 0, tpb_len = 0;
1133	int argn;
1134	char last_tpb[TPB_MAX_SIZE];
1135	ibase_db_link **ib_link = NULL;
1136	ibase_trans *ib_trans;
1137	isc_tr_handle tr_handle = 0;
1138	ISC_STATUS result;
1139
1140	RESET_ERRMSG;
1141
1142	argn = ZEND_NUM_ARGS();
1143
1144	/* (1+argn) is an upper bound for the number of links this trans connects to */
1145	ib_link = (ibase_db_link **) safe_emalloc(sizeof(ibase_db_link *),1+argn,0);
1146
1147	if (argn > 0) {
1148		zend_long trans_argl = 0;
1149		char *tpb;
1150		ISC_TEB *teb;
1151		zval *args = NULL;
1152
1153		if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argn) == FAILURE) {
1154			efree(ib_link);
1155			RETURN_FALSE;
1156		}
1157
1158		teb = (ISC_TEB *) safe_emalloc(sizeof(ISC_TEB),argn,0);
1159		tpb = (char *) safe_emalloc(TPB_MAX_SIZE,argn,0);
1160
1161		/* enumerate all the arguments: assume every non-resource argument
1162		   specifies modifiers for the link ids that follow it */
1163		for (i = 0; i < argn; ++i) {
1164
1165			if (Z_TYPE(args[i]) == IS_RESOURCE) {
1166
1167				if ((ib_link[link_cnt] = (ibase_db_link *)zend_fetch_resource2_ex(&args[i], LE_LINK, le_link, le_plink)) == NULL) {
1168					efree(teb);
1169					efree(tpb);
1170					efree(ib_link);
1171					RETURN_FALSE;
1172				}
1173
1174				/* copy the most recent modifier string into tbp[] */
1175				memcpy(&tpb[TPB_MAX_SIZE * link_cnt], last_tpb, TPB_MAX_SIZE);
1176
1177				/* add a database handle to the TEB with the most recently specified set of modifiers */
1178				teb[link_cnt].db_ptr = &ib_link[link_cnt]->handle;
1179				teb[link_cnt].tpb_len = tpb_len;
1180				teb[link_cnt].tpb_ptr = &tpb[TPB_MAX_SIZE * link_cnt];
1181
1182				++link_cnt;
1183
1184			} else {
1185
1186				tpb_len = 0;
1187
1188				convert_to_long_ex(&args[i]);
1189				trans_argl = Z_LVAL(args[i]);
1190
1191				if (trans_argl != PHP_IBASE_DEFAULT) {
1192					last_tpb[tpb_len++] = isc_tpb_version3;
1193
1194					/* access mode */
1195					if (PHP_IBASE_READ == (trans_argl & PHP_IBASE_READ)) {
1196						last_tpb[tpb_len++] = isc_tpb_read;
1197					} else if (PHP_IBASE_WRITE == (trans_argl & PHP_IBASE_WRITE)) {
1198						last_tpb[tpb_len++] = isc_tpb_write;
1199					}
1200
1201					/* isolation level */
1202					if (PHP_IBASE_COMMITTED == (trans_argl & PHP_IBASE_COMMITTED)) {
1203						last_tpb[tpb_len++] = isc_tpb_read_committed;
1204						if (PHP_IBASE_REC_VERSION == (trans_argl & PHP_IBASE_REC_VERSION)) {
1205							last_tpb[tpb_len++] = isc_tpb_rec_version;
1206						} else if (PHP_IBASE_REC_NO_VERSION == (trans_argl & PHP_IBASE_REC_NO_VERSION)) {
1207							last_tpb[tpb_len++] = isc_tpb_no_rec_version;
1208						}
1209					} else if (PHP_IBASE_CONSISTENCY == (trans_argl & PHP_IBASE_CONSISTENCY)) {
1210						last_tpb[tpb_len++] = isc_tpb_consistency;
1211					} else if (PHP_IBASE_CONCURRENCY == (trans_argl & PHP_IBASE_CONCURRENCY)) {
1212						last_tpb[tpb_len++] = isc_tpb_concurrency;
1213					}
1214
1215					/* lock resolution */
1216					if (PHP_IBASE_NOWAIT == (trans_argl & PHP_IBASE_NOWAIT)) {
1217						last_tpb[tpb_len++] = isc_tpb_nowait;
1218					} else if (PHP_IBASE_WAIT == (trans_argl & PHP_IBASE_WAIT)) {
1219						last_tpb[tpb_len++] = isc_tpb_wait;
1220					}
1221				}
1222			}
1223		}
1224
1225		if (link_cnt > 0) {
1226			result = isc_start_multiple(IB_STATUS, &tr_handle, link_cnt, teb);
1227		}
1228
1229		efree(tpb);
1230		efree(teb);
1231	}
1232
1233	if (link_cnt == 0) {
1234		link_cnt = 1;
1235		if ((ib_link[0] = (ibase_db_link *)zend_fetch_resource2(IBG(default_link), LE_LINK, le_link, le_plink)) == NULL) {
1236			efree(ib_link);
1237			RETURN_FALSE;
1238		}
1239		result = isc_start_transaction(IB_STATUS, &tr_handle, 1, &ib_link[0]->handle, tpb_len, last_tpb);
1240	}
1241
1242	/* start the transaction */
1243	if (result) {
1244		_php_ibase_error();
1245		efree(ib_link);
1246		RETURN_FALSE;
1247	}
1248
1249	/* register the transaction in our own data structures */
1250	ib_trans = (ibase_trans *) safe_emalloc(link_cnt-1, sizeof(ibase_db_link *), sizeof(ibase_trans));
1251	ib_trans->handle = tr_handle;
1252	ib_trans->link_cnt = link_cnt;
1253	ib_trans->affected_rows = 0;
1254	for (i = 0; i < link_cnt; ++i) {
1255		ibase_tr_list **l;
1256		ib_trans->db_link[i] = ib_link[i];
1257
1258		/* the first item in the connection-transaction list is reserved for the default transaction */
1259		if (ib_link[i]->tr_list == NULL) {
1260			ib_link[i]->tr_list = (ibase_tr_list *) emalloc(sizeof(ibase_tr_list));
1261			ib_link[i]->tr_list->trans = NULL;
1262			ib_link[i]->tr_list->next = NULL;
1263		}
1264
1265		/* link the transaction into the connection-transaction list */
1266		for (l = &ib_link[i]->tr_list; *l != NULL; l = &(*l)->next);
1267		*l = (ibase_tr_list *) emalloc(sizeof(ibase_tr_list));
1268		(*l)->trans = ib_trans;
1269		(*l)->next = NULL;
1270	}
1271	efree(ib_link);
1272	RETVAL_RES(zend_register_resource(ib_trans, le_trans));
1273	Z_TRY_ADDREF_P(return_value);
1274}
1275/* }}} */
1276
1277int _php_ibase_def_trans(ibase_db_link *ib_link, ibase_trans **trans) /* {{{ */
1278{
1279	if (ib_link == NULL) {
1280		php_error_docref(NULL, E_WARNING, "Invalid database link");
1281		return FAILURE;
1282	}
1283
1284	/* the first item in the connection-transaction list is reserved for the default transaction */
1285	if (ib_link->tr_list == NULL) {
1286		ib_link->tr_list = (ibase_tr_list *) emalloc(sizeof(ibase_tr_list));
1287		ib_link->tr_list->trans = NULL;
1288		ib_link->tr_list->next = NULL;
1289	}
1290
1291	if (*trans == NULL) {
1292		ibase_trans *tr = ib_link->tr_list->trans;
1293
1294		if (tr == NULL) {
1295			tr = (ibase_trans *) emalloc(sizeof(ibase_trans));
1296			tr->handle = 0;
1297			tr->link_cnt = 1;
1298			tr->affected_rows = 0;
1299			tr->db_link[0] = ib_link;
1300			ib_link->tr_list->trans = tr;
1301		}
1302		if (tr->handle == 0) {
1303			if (isc_start_transaction(IB_STATUS, &tr->handle, 1, &ib_link->handle, 0, NULL)) {
1304				_php_ibase_error();
1305				return FAILURE;
1306			}
1307		}
1308		*trans = tr;
1309	}
1310	return SUCCESS;
1311}
1312/* }}} */
1313
1314static void _php_ibase_trans_end(INTERNAL_FUNCTION_PARAMETERS, int commit) /* {{{ */
1315{
1316	ibase_trans *trans = NULL;
1317	int res_id = 0;
1318	ISC_STATUS result;
1319	ibase_db_link *ib_link;
1320	zval *arg = NULL;
1321
1322	RESET_ERRMSG;
1323
1324	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|r", &arg) == FAILURE) {
1325		return;
1326	}
1327
1328	if (ZEND_NUM_ARGS() == 0) {
1329		ib_link = (ibase_db_link *)zend_fetch_resource2(IBG(default_link), LE_LINK, le_link, le_plink);
1330		if (ib_link->tr_list == NULL || ib_link->tr_list->trans == NULL) {
1331			/* this link doesn't have a default transaction */
1332			_php_ibase_module_error("Default link has no default transaction");
1333			RETURN_FALSE;
1334		}
1335		trans = ib_link->tr_list->trans;
1336	} else {
1337		/* one id was passed, could be db or trans id */
1338		if (Z_RES_P(arg)->type == le_trans) {
1339			trans = (ibase_trans *)zend_fetch_resource_ex(arg, LE_TRANS, le_trans);
1340			res_id = Z_RES_P(arg)->handle;
1341		} else {
1342			ib_link = (ibase_db_link *)zend_fetch_resource2_ex(arg, LE_LINK, le_link, le_plink);
1343
1344			if (ib_link->tr_list == NULL || ib_link->tr_list->trans == NULL) {
1345				/* this link doesn't have a default transaction */
1346				_php_ibase_module_error("Link has no default transaction");
1347				RETURN_FALSE;
1348			}
1349			trans = ib_link->tr_list->trans;
1350		}
1351	}
1352
1353	switch (commit) {
1354		default: /* == case ROLLBACK: */
1355			result = isc_rollback_transaction(IB_STATUS, &trans->handle);
1356			break;
1357		case COMMIT:
1358			result = isc_commit_transaction(IB_STATUS, &trans->handle);
1359			break;
1360		case (ROLLBACK | RETAIN):
1361			result = isc_rollback_retaining(IB_STATUS, &trans->handle);
1362			break;
1363		case (COMMIT | RETAIN):
1364			result = isc_commit_retaining(IB_STATUS, &trans->handle);
1365			break;
1366	}
1367
1368	if (result) {
1369		_php_ibase_error();
1370		RETURN_FALSE;
1371	}
1372
1373	/* Don't try to destroy implicitly opened transaction from list... */
1374	if ((commit & RETAIN) == 0 && res_id != 0) {
1375		zend_list_delete(Z_RES_P(arg));
1376	}
1377	RETURN_TRUE;
1378}
1379/* }}} */
1380
1381/* {{{ proto bool ibase_commit( resource link_identifier )
1382   Commit transaction */
1383PHP_FUNCTION(ibase_commit)
1384{
1385	_php_ibase_trans_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, COMMIT);
1386}
1387/* }}} */
1388
1389/* {{{ proto bool ibase_rollback( resource link_identifier )
1390   Rollback transaction */
1391PHP_FUNCTION(ibase_rollback)
1392{
1393	_php_ibase_trans_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, ROLLBACK);
1394}
1395/* }}} */
1396
1397/* {{{ proto bool ibase_commit_ret( resource link_identifier )
1398   Commit transaction and retain the transaction context */
1399PHP_FUNCTION(ibase_commit_ret)
1400{
1401	_php_ibase_trans_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, COMMIT | RETAIN);
1402}
1403/* }}} */
1404
1405/* {{{ proto bool ibase_rollback_ret( resource link_identifier )
1406   Rollback transaction and retain the transaction context */
1407PHP_FUNCTION(ibase_rollback_ret)
1408{
1409	_php_ibase_trans_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, ROLLBACK | RETAIN);
1410}
1411/* }}} */
1412
1413/* {{{ proto int ibase_gen_id(string generator [, int increment [, resource link_identifier ]])
1414   Increments the named generator and returns its new value */
1415PHP_FUNCTION(ibase_gen_id)
1416{
1417	zval *link = NULL;
1418	char query[128], *generator;
1419	size_t gen_len;
1420	zend_long inc = 1;
1421	ibase_db_link *ib_link;
1422	ibase_trans *trans = NULL;
1423	XSQLDA out_sqlda;
1424	ISC_INT64 result;
1425
1426	RESET_ERRMSG;
1427
1428	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s|lr", &generator, &gen_len,
1429			&inc, &link)) {
1430		RETURN_FALSE;
1431	}
1432
1433	if (gen_len > 31) {
1434		php_error_docref(NULL, E_WARNING, "Invalid generator name");
1435		RETURN_FALSE;
1436	}
1437
1438	PHP_IBASE_LINK_TRANS(link, ib_link, trans);
1439
1440	snprintf(query, sizeof(query), "SELECT GEN_ID(%s,%ld) FROM rdb$database", generator, inc);
1441
1442	/* allocate a minimal descriptor area */
1443	out_sqlda.sqln = out_sqlda.sqld = 1;
1444	out_sqlda.version = SQLDA_CURRENT_VERSION;
1445
1446	/* allocate the field for the result */
1447	out_sqlda.sqlvar[0].sqltype = SQL_INT64;
1448	out_sqlda.sqlvar[0].sqlscale = 0;
1449	out_sqlda.sqlvar[0].sqllen = sizeof(result);
1450	out_sqlda.sqlvar[0].sqldata = (void*) &result;
1451
1452	/* execute the query */
1453	if (isc_dsql_exec_immed2(IB_STATUS, &ib_link->handle, &trans->handle, 0, query,
1454			SQL_DIALECT_CURRENT, NULL, &out_sqlda)) {
1455		_php_ibase_error();
1456		RETURN_FALSE;
1457	}
1458
1459	/* don't return the generator value as a string unless it doesn't fit in a long */
1460#if SIZEOF_ZEND_LONG < 8
1461	if (result < ZEND_LONG_MIN || result > ZEND_LONG_MAX) {
1462		char *res;
1463		int l;
1464
1465		l = spprintf(&res, 0, "%" LL_MASK "d", result);
1466		RETURN_STRINGL(res, l);
1467	}
1468#endif
1469	RETURN_LONG((zend_long)result);
1470}
1471
1472/* }}} */
1473
1474#endif /* HAVE_IBASE */
1475
1476/*
1477 * Local variables:
1478 * tab-width: 4
1479 * c-basic-offset: 4
1480 * End:
1481 * vim600: sw=4 ts=4 fdm=marker
1482 * vim<600: sw=4 ts=4
1483 */
1484