xref: /PHP-8.0/ext/oci8/oci8.c (revision c435e677)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | http://www.php.net/license/3_01.txt                                  |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Authors: Stig Sæther Bakken <ssb@php.net>                            |
14    |          Thies C. Arntzen <thies@thieso.net>                         |
15    |          Maxim Maletsky <maxim@maxim.cx>                             |
16    |                                                                      |
17    | Collection support by Andy Sautins <asautins@veripost.net>           |
18    | Temporary LOB support by David Benson <dbenson@mancala.com>          |
19    | ZTS per process OCIPLogon by Harald Radi <harald.radi@nme.at>        |
20    |                                                                      |
21    | Redesigned by: Antony Dovgal <antony@zend.com>                       |
22    |                Andi Gutmans <andi@php.net>                           |
23    |                Wez Furlong <wez@omniti.com>                          |
24    +----------------------------------------------------------------------+
25 */
26 
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30 
31 #include "php.h"
32 #include "ext/standard/info.h"
33 #include "php_ini.h"
34 #include "zend_smart_str.h"
35 
36 #ifdef HAVE_OCI8
37 
38 /* PHP 5.2 is the minimum supported version for OCI8 2.0 */
39 #if PHP_MAJOR_VERSION < 5 || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION <= 1)
40 #error Use PHP OCI8 1.4 for your version of PHP
41 #elif PHP_MAJOR_VERSION < 7
42 /* PHP 7 is the minimum supported version for OCI8 2.1 */
43 #error Use PHP OCI8 2.0 for your version of PHP
44 #elif PHP_MAJOR_VERSION < 8
45 /* PHP 8 is the minimum supported version for OCI8 3.0 */
46 #error Use PHP OCI8 2.2 for your version of PHP
47 #endif
48 
49 #include "php_oci8.h"
50 #include "php_oci8_int.h"
51 #include "zend_hash.h"
52 
53 ZEND_DECLARE_MODULE_GLOBALS(oci)
54 static PHP_GINIT_FUNCTION(oci);
55 static PHP_GSHUTDOWN_FUNCTION(oci);
56 
57 /* Allow PHP 5.3 branch to be used in PECL for 5.x compatible builds */
58 #ifndef Z_ADDREF_P
59 #define Z_ADDREF_P(x) ZVAL_ADDREF(x)
60 #endif
61 
62 /* For a user friendly message about environment setup */
63 #if defined(PHP_WIN32)
64 #define PHP_OCI8_LIB_PATH_MSG "PATH"
65 #elif defined(__APPLE__)
66 #define PHP_OCI8_LIB_PATH_MSG "DYLD_LIBRARY_PATH"
67 #elif defined(_AIX)
68 #define PHP_OCI8_LIB_PATH_MSG "LIBPATH"
69 #elif defined(__hpux)
70 #define PHP_OCI8_LIB_PATH_MSG "SHLIB_PATH"
71 #else
72 #define PHP_OCI8_LIB_PATH_MSG "LD_LIBRARY_PATH"
73 #endif
74 
75 /* True globals, no need for thread safety */
76 int le_connection;
77 int le_pconnection;
78 int le_statement;
79 int le_descriptor;
80 int le_psessionpool;
81 int le_collection;
82 
83 zend_class_entry *oci_lob_class_entry_ptr;
84 zend_class_entry *oci_coll_class_entry_ptr;
85 
86 #ifndef SQLT_BFILEE
87 #define SQLT_BFILEE 114
88 #endif
89 #ifndef SQLT_CFILEE
90 #define SQLT_CFILEE 115
91 #endif
92 
93 #ifdef ZTS
94 #define PHP_OCI_INIT_MODE (OCI_DEFAULT | OCI_OBJECT | OCI_THREADED | OCI_NO_MUTEX)
95 #else
96 #define PHP_OCI_INIT_MODE (OCI_DEFAULT | OCI_OBJECT)
97 #endif
98 
99 /* {{{ static protos */
100 static void php_oci_connection_list_dtor (zend_resource *);
101 static void php_oci_pconnection_list_dtor (zend_resource *);
102 static void php_oci_pconnection_list_np_dtor (zend_resource *);
103 static void php_oci_statement_list_dtor (zend_resource *);
104 static void php_oci_descriptor_list_dtor (zend_resource *);
105 static void php_oci_spool_list_dtor(zend_resource *entry);
106 static void php_oci_collection_list_dtor (zend_resource *);
107 
108 static int php_oci_persistent_helper(zval *zv);
109 static int php_oci_connection_ping(php_oci_connection *);
110 static int php_oci_connection_status(php_oci_connection *);
111 static int php_oci_connection_close(php_oci_connection *);
112 static void php_oci_spool_close(php_oci_spool *session_pool);
113 
114 static OCIEnv *php_oci_create_env(ub2 charsetid);
115 static int php_oci_create_session(php_oci_connection *connection, php_oci_spool *session_pool, char *dbname, int dbname_len, char *username, int username_len, char *password, int password_len, char *new_password, int new_password_len, int session_mode);
116 static int php_oci_old_create_session(php_oci_connection *connection, char *dbname, int dbname_len, char *username, int username_len, char *password, int password_len, char *new_password, int new_password_len, int session_mode);
117 static php_oci_spool *php_oci_get_spool(char *username, int username_len, char *password, int password_len, char *dbname, int dbname_len, int charsetid);
118 static php_oci_spool *php_oci_create_spool(char *username, int username_len, char *password, int password_len, char *dbname, int dbname_len, zend_string *hash_key, int charsetid);
119 static sword php_oci_ping_init(php_oci_connection *connection, OCIError *errh);
120 /* }}} */
121 
122 /* {{{ dynamically loadable module stuff */
123 #if defined(COMPILE_DL_OCI8) || defined(COMPILE_DL_OCI8_11G) || defined(COMPILE_DL_OCI8_12C) || defined(COMPILE_DL_OCI8_19)
124 ZEND_GET_MODULE(oci8)
125 #endif /* COMPILE_DL */
126 /* }}} */
127 
128 #include "oci8_arginfo.h"
129 
130 /* {{{ extension definition structures */
131 
132 zend_module_entry oci8_module_entry = {
133 	STANDARD_MODULE_HEADER,
134 	"oci8",				  /* extension name */
135 	ext_functions,	      /* extension function list */
136 	PHP_MINIT(oci),		  /* extension-wide startup function */
137 	PHP_MSHUTDOWN(oci),	  /* extension-wide shutdown function */
138 	PHP_RINIT(oci),		  /* per-request startup function */
139 	PHP_RSHUTDOWN(oci),	  /* per-request shutdown function */
140 	PHP_MINFO(oci),		  /* information function */
141 	PHP_OCI8_VERSION,
142 	PHP_MODULE_GLOBALS(oci),  /* globals descriptor */
143 	PHP_GINIT(oci),			  /* globals ctor */
144 	PHP_GSHUTDOWN(oci),		  /* globals dtor */
145 	NULL,					  /* post deactivate */
146 	STANDARD_MODULE_PROPERTIES_EX
147 };
148 /* }}} */
149 
150 /* {{{ PHP_INI */
151 PHP_INI_BEGIN()
152 	STD_PHP_INI_ENTRY(	"oci8.max_persistent",			"-1",	PHP_INI_SYSTEM,	OnUpdateLong,	max_persistent,			zend_oci_globals,	oci_globals)
153 	STD_PHP_INI_ENTRY(	"oci8.persistent_timeout",		"-1",	PHP_INI_SYSTEM,	OnUpdateLong,	persistent_timeout,		zend_oci_globals,	oci_globals)
154 	STD_PHP_INI_ENTRY(	"oci8.ping_interval",			"60",	PHP_INI_SYSTEM,	OnUpdateLong,	ping_interval,			zend_oci_globals,	oci_globals)
155 	STD_PHP_INI_BOOLEAN("oci8.privileged_connect",		"0",	PHP_INI_SYSTEM,	OnUpdateBool,		privileged_connect,		zend_oci_globals,	oci_globals)
156 	STD_PHP_INI_ENTRY(	"oci8.statement_cache_size",	"20",	PHP_INI_SYSTEM,	OnUpdateLong,	statement_cache_size,	zend_oci_globals,	oci_globals)
157 	STD_PHP_INI_ENTRY(	"oci8.default_prefetch",		"100",	PHP_INI_SYSTEM,	OnUpdateLong,	default_prefetch,		zend_oci_globals,	oci_globals)
158 	STD_PHP_INI_BOOLEAN("oci8.old_oci_close_semantics",	"0",	PHP_INI_SYSTEM,	OnUpdateBool,		old_oci_close_semantics,zend_oci_globals,	oci_globals)
159 #if (OCI_MAJOR_VERSION >= 11)
160 	STD_PHP_INI_ENTRY(	"oci8.connection_class",		"",		PHP_INI_ALL,	OnUpdateString,		connection_class,		zend_oci_globals,	oci_globals)
161 #endif
162 #if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2)))
163 	STD_PHP_INI_BOOLEAN("oci8.events",					"0",	PHP_INI_SYSTEM,	OnUpdateBool,		events,					zend_oci_globals,	oci_globals)
164 #endif
PHP_INI_END()165 PHP_INI_END()
166 /* }}} */
167 
168 /* {{{ startup, shutdown and info functions */
169 
170 /* {{{	php_oci_init_global_handles()
171  *
172  * Initialize global handles only when they are needed
173  */
174 static void php_oci_init_global_handles(void)
175 {
176 	sword errstatus;
177 	sb4   ora_error_code = 0;
178 	text  tmp_buf[PHP_OCI_ERRBUF_LEN];  /* Use traditional smaller size: non-PL/SQL errors should fit and it keeps the stack smaller */
179 
180 	errstatus = OCIEnvNlsCreate(&OCI_G(env), PHP_OCI_INIT_MODE, 0, NULL, NULL, NULL, 0, NULL, 0, 0);
181 
182 	if (errstatus == OCI_ERROR) {
183 #ifdef HAVE_OCI_INSTANT_CLIENT
184 		php_error_docref(NULL, E_WARNING, "OCIEnvNlsCreate() failed. There is something wrong with your system - please check that " PHP_OCI8_LIB_PATH_MSG " includes the directory with Oracle Instant Client libraries");
185 #else
186 		php_error_docref(NULL, E_WARNING, "OCIEnvNlsCreate() failed. There is something wrong with your system - please check that ORACLE_HOME and " PHP_OCI8_LIB_PATH_MSG " are set and point to the right directories");
187 #endif
188 		if (OCI_G(env)
189 			&& OCIErrorGet(OCI_G(env), (ub4)1, NULL, &ora_error_code, tmp_buf, (ub4)PHP_OCI_ERRBUF_LEN, (ub4)OCI_HTYPE_ENV) == OCI_SUCCESS
190 			&& *tmp_buf) {
191 			php_error_docref(NULL, E_WARNING, "%s", tmp_buf);
192 		}
193 
194 		OCI_G(env) = NULL;
195 		OCI_G(err) = NULL;
196 		return;
197 	}
198 
199 	errstatus = OCIHandleAlloc (OCI_G(env), (dvoid **)&OCI_G(err), OCI_HTYPE_ERROR, 0, NULL);
200 
201 	if (errstatus == OCI_SUCCESS) {
202 #if !defined(OCI_MAJOR_VERSION) || (OCI_MAJOR_VERSION < 11)
203 		/* This fixes PECL bug 15988 (sqlnet.ora not being read).  The
204 		 * root cause was fixed in Oracle 10.2.0.4 but there is no
205 		 * compile time method to check for that precise patch level,
206 		 * nor can it be guaranteed that runtime will use the same
207 		 * patch level the code was compiled with.  So, we do this
208 		 * code for all non 11g versions.
209 		 */
210 		OCICPool *cpoolh;
211 		ub4 cpoolmode = 0x80000000;	/* Pass invalid mode to OCIConnectionPoolCreate */
212 		PHP_OCI_CALL(OCIHandleAlloc, (OCI_G(env), (dvoid **) &cpoolh, OCI_HTYPE_CPOOL, (size_t) 0, (dvoid **) 0));
213 		PHP_OCI_CALL(OCIConnectionPoolCreate, (OCI_G(env), OCI_G(err), cpoolh, NULL, 0, NULL, 0, 0, 0, 0, NULL, 0, NULL, 0, cpoolmode));
214 		PHP_OCI_CALL(OCIConnectionPoolDestroy, (cpoolh, OCI_G(err), OCI_DEFAULT));
215 		PHP_OCI_CALL(OCIHandleFree, (cpoolh, OCI_HTYPE_CPOOL));
216 #endif
217 	} else {
218 		OCIErrorGet(OCI_G(env), (ub4)1, NULL, &ora_error_code, tmp_buf, (ub4)PHP_OCI_ERRBUF_LEN, (ub4)OCI_HTYPE_ERROR);
219 
220 		if (ora_error_code) {
221 			int tmp_buf_len = (int) strlen((char *)tmp_buf);
222 
223 			if (tmp_buf_len > 0 && tmp_buf[tmp_buf_len - 1] == '\n') {
224 				tmp_buf[tmp_buf_len - 1] = '\0';
225 			}
226 
227 			if (errstatus == OCI_SUCCESS_WITH_INFO) {
228 				php_error_docref(NULL, E_WARNING, "Initialization error: OCI_SUCCESS_WITH_INFO: %s", tmp_buf);
229 			} else {
230 				php_error_docref(NULL, E_WARNING, "Initialization error: OCI_ERROR: %s", tmp_buf);
231 
232 				OCIHandleFree((dvoid *) OCI_G(env), OCI_HTYPE_ENV);
233 
234 				OCI_G(env) = NULL;
235 				OCI_G(err) = NULL;
236 			}
237 		}
238 	}
239 }
240 /* }}} */
241 
242 /* {{{ PHP_GINIT_FUNCTION
243  *
244  * Zerofill globals during module init
245  */
PHP_GINIT_FUNCTION(oci)246 static PHP_GINIT_FUNCTION(oci)
247 {
248 	memset(oci_globals, 0, sizeof(zend_oci_globals));
249 }
250 /* }}} */
251 
252 /* {{{ PHP_GSHUTDOWN_FUNCTION
253  *
254  * Called for thread shutdown in ZTS, after module shutdown for non-ZTS
255  * Free global handles (if they were initialized before)
256  */
PHP_GSHUTDOWN_FUNCTION(oci)257 static PHP_GSHUTDOWN_FUNCTION(oci)
258 {
259 	if (oci_globals->err) {
260 		oci_globals->in_call = 1;
261 		OCIHandleFree((dvoid *) oci_globals->err, OCI_HTYPE_ERROR);
262 		oci_globals->in_call = 0;
263 		oci_globals->err = NULL;
264 	}
265 
266 	if (oci_globals->env) {
267 		oci_globals->in_call = 1;
268 		OCIHandleFree((dvoid *) oci_globals->env, OCI_HTYPE_ENV);
269 		oci_globals->in_call = 0;
270 		oci_globals->env = NULL;
271 	}
272 }
273 /* }}} */
274 
PHP_MINIT_FUNCTION(oci)275 PHP_MINIT_FUNCTION(oci)
276 {
277 	zend_class_entry oci_lob_class_entry;
278 	zend_class_entry oci_coll_class_entry;
279 
280 	REGISTER_INI_ENTRIES();
281 
282 	le_statement = zend_register_list_destructors_ex(php_oci_statement_list_dtor, NULL, "oci8 statement", module_number);
283 	le_connection = zend_register_list_destructors_ex(php_oci_connection_list_dtor, NULL, "oci8 connection", module_number);
284 	le_pconnection = zend_register_list_destructors_ex(php_oci_pconnection_list_np_dtor, php_oci_pconnection_list_dtor, "oci8 persistent connection", module_number);
285 	le_psessionpool = zend_register_list_destructors_ex(NULL, php_oci_spool_list_dtor, "oci8 persistent session pool", module_number);
286 	le_descriptor = zend_register_list_destructors_ex(php_oci_descriptor_list_dtor, NULL, "oci8 descriptor", module_number);
287 	le_collection = zend_register_list_destructors_ex(php_oci_collection_list_dtor, NULL, "oci8 collection", module_number);
288 
289 	INIT_CLASS_ENTRY(oci_lob_class_entry, "OCILob", class_OCILob_methods);
290 	INIT_CLASS_ENTRY(oci_coll_class_entry, "OCICollection", class_OCICollection_methods);
291 
292 	oci_lob_class_entry_ptr = zend_register_internal_class(&oci_lob_class_entry);
293 	oci_coll_class_entry_ptr = zend_register_internal_class(&oci_coll_class_entry);
294 
295 /* thies@thieso.net 990203 i do not think that we will need all of them - just in here for completeness for now! */
296 	REGISTER_LONG_CONSTANT("OCI_DEFAULT",OCI_DEFAULT, CONST_CS | CONST_PERSISTENT);
297 	REGISTER_LONG_CONSTANT("OCI_SYSOPER",OCI_SYSOPER, CONST_CS | CONST_PERSISTENT);
298 	REGISTER_LONG_CONSTANT("OCI_SYSDBA",OCI_SYSDBA, CONST_CS | CONST_PERSISTENT);
299 	REGISTER_LONG_CONSTANT("OCI_CRED_EXT",PHP_OCI_CRED_EXT, CONST_CS | CONST_PERSISTENT);
300 	REGISTER_LONG_CONSTANT("OCI_DESCRIBE_ONLY",OCI_DESCRIBE_ONLY, CONST_CS | CONST_PERSISTENT);
301 	REGISTER_LONG_CONSTANT("OCI_COMMIT_ON_SUCCESS",OCI_COMMIT_ON_SUCCESS, CONST_CS | CONST_PERSISTENT);
302 	REGISTER_LONG_CONSTANT("OCI_NO_AUTO_COMMIT",OCI_DEFAULT, CONST_CS | CONST_PERSISTENT);
303 	REGISTER_LONG_CONSTANT("OCI_EXACT_FETCH",OCI_EXACT_FETCH, CONST_CS | CONST_PERSISTENT);
304 
305 /* for $LOB->seek() */
306 	REGISTER_LONG_CONSTANT("OCI_SEEK_SET",PHP_OCI_SEEK_SET, CONST_CS | CONST_PERSISTENT);
307 	REGISTER_LONG_CONSTANT("OCI_SEEK_CUR",PHP_OCI_SEEK_CUR, CONST_CS | CONST_PERSISTENT);
308 	REGISTER_LONG_CONSTANT("OCI_SEEK_END",PHP_OCI_SEEK_END, CONST_CS | CONST_PERSISTENT);
309 
310 /*	for $LOB->flush() */
311 	REGISTER_LONG_CONSTANT("OCI_LOB_BUFFER_FREE",OCI_LOB_BUFFER_FREE, CONST_CS | CONST_PERSISTENT);
312 
313 /* for OCIBindByName (real "oci" names + short "php" names */
314 	REGISTER_LONG_CONSTANT("SQLT_BFILEE",SQLT_BFILEE, CONST_CS | CONST_PERSISTENT);
315 	REGISTER_LONG_CONSTANT("SQLT_CFILEE",SQLT_CFILEE, CONST_CS | CONST_PERSISTENT);
316 	REGISTER_LONG_CONSTANT("SQLT_CLOB",SQLT_CLOB, CONST_CS | CONST_PERSISTENT);
317 	REGISTER_LONG_CONSTANT("SQLT_BLOB",SQLT_BLOB, CONST_CS | CONST_PERSISTENT);
318 	REGISTER_LONG_CONSTANT("SQLT_RDD",SQLT_RDD, CONST_CS | CONST_PERSISTENT);
319 	REGISTER_LONG_CONSTANT("SQLT_INT",SQLT_INT, CONST_CS | CONST_PERSISTENT);
320 	REGISTER_LONG_CONSTANT("SQLT_NUM",SQLT_NUM, CONST_CS | CONST_PERSISTENT);
321 	REGISTER_LONG_CONSTANT("SQLT_RSET",SQLT_RSET, CONST_CS | CONST_PERSISTENT);
322 	REGISTER_LONG_CONSTANT("SQLT_AFC",SQLT_AFC, CONST_CS | CONST_PERSISTENT);
323 	REGISTER_LONG_CONSTANT("SQLT_CHR",SQLT_CHR, CONST_CS | CONST_PERSISTENT);
324 	REGISTER_LONG_CONSTANT("SQLT_VCS",SQLT_VCS, CONST_CS | CONST_PERSISTENT);
325 	REGISTER_LONG_CONSTANT("SQLT_AVC",SQLT_AVC, CONST_CS | CONST_PERSISTENT);
326 	REGISTER_LONG_CONSTANT("SQLT_STR",SQLT_STR, CONST_CS | CONST_PERSISTENT);
327 	REGISTER_LONG_CONSTANT("SQLT_LVC",SQLT_LVC, CONST_CS | CONST_PERSISTENT);
328 	REGISTER_LONG_CONSTANT("SQLT_FLT",SQLT_FLT, CONST_CS | CONST_PERSISTENT);
329 	REGISTER_LONG_CONSTANT("SQLT_UIN",SQLT_UIN, CONST_CS | CONST_PERSISTENT);
330 	REGISTER_LONG_CONSTANT("SQLT_LNG",SQLT_LNG, CONST_CS | CONST_PERSISTENT);
331 	REGISTER_LONG_CONSTANT("SQLT_LBI",SQLT_LBI, CONST_CS | CONST_PERSISTENT);
332 	REGISTER_LONG_CONSTANT("SQLT_BIN",SQLT_BIN, CONST_CS | CONST_PERSISTENT);
333 	REGISTER_LONG_CONSTANT("SQLT_ODT",SQLT_ODT, CONST_CS | CONST_PERSISTENT);
334 #if defined(HAVE_OCI_INSTANT_CLIENT) || (defined(OCI_MAJOR_VERSION) && OCI_MAJOR_VERSION >= 10)
335 	REGISTER_LONG_CONSTANT("SQLT_BDOUBLE",SQLT_BDOUBLE, CONST_CS | CONST_PERSISTENT);
336 	REGISTER_LONG_CONSTANT("SQLT_BFLOAT",SQLT_BFLOAT, CONST_CS | CONST_PERSISTENT);
337 #endif
338 #if defined(OCI_MAJOR_VERSION) && OCI_MAJOR_VERSION >= 12
339 	REGISTER_LONG_CONSTANT("SQLT_BOL",SQLT_BOL, CONST_CS | CONST_PERSISTENT);
340 #endif
341 
342 	REGISTER_LONG_CONSTANT("OCI_B_NTY",SQLT_NTY, CONST_CS | CONST_PERSISTENT);
343 	REGISTER_LONG_CONSTANT("SQLT_NTY",SQLT_NTY, CONST_CS | CONST_PERSISTENT);
344 	REGISTER_STRING_CONSTANT("OCI_SYSDATE","SYSDATE", CONST_CS | CONST_PERSISTENT);
345 
346 	REGISTER_LONG_CONSTANT("OCI_B_BFILE",SQLT_BFILEE, CONST_CS | CONST_PERSISTENT);
347 	REGISTER_LONG_CONSTANT("OCI_B_CFILEE",SQLT_CFILEE, CONST_CS | CONST_PERSISTENT);
348 	REGISTER_LONG_CONSTANT("OCI_B_CLOB",SQLT_CLOB, CONST_CS | CONST_PERSISTENT);
349 	REGISTER_LONG_CONSTANT("OCI_B_BLOB",SQLT_BLOB, CONST_CS | CONST_PERSISTENT);
350 	REGISTER_LONG_CONSTANT("OCI_B_ROWID",SQLT_RDD, CONST_CS | CONST_PERSISTENT);
351 	REGISTER_LONG_CONSTANT("OCI_B_CURSOR",SQLT_RSET, CONST_CS | CONST_PERSISTENT);
352 	REGISTER_LONG_CONSTANT("OCI_B_BIN",SQLT_BIN, CONST_CS | CONST_PERSISTENT);
353 	REGISTER_LONG_CONSTANT("OCI_B_INT",SQLT_INT, CONST_CS | CONST_PERSISTENT);
354 	REGISTER_LONG_CONSTANT("OCI_B_NUM",SQLT_NUM, CONST_CS | CONST_PERSISTENT);
355 #if defined(OCI_MAJOR_VERSION) && OCI_MAJOR_VERSION >= 12
356 	REGISTER_LONG_CONSTANT("OCI_B_BOL",SQLT_BOL, CONST_CS | CONST_PERSISTENT);
357 #endif
358 
359 /* for OCIFetchStatement */
360 	REGISTER_LONG_CONSTANT("OCI_FETCHSTATEMENT_BY_COLUMN", PHP_OCI_FETCHSTATEMENT_BY_COLUMN, CONST_CS | CONST_PERSISTENT);
361 	REGISTER_LONG_CONSTANT("OCI_FETCHSTATEMENT_BY_ROW", PHP_OCI_FETCHSTATEMENT_BY_ROW, CONST_CS | CONST_PERSISTENT);
362 
363 /* for OCIFetchInto & OCIResult */
364 	REGISTER_LONG_CONSTANT("OCI_ASSOC",PHP_OCI_ASSOC, CONST_CS | CONST_PERSISTENT);
365 	REGISTER_LONG_CONSTANT("OCI_NUM",PHP_OCI_NUM, CONST_CS | CONST_PERSISTENT);
366 	REGISTER_LONG_CONSTANT("OCI_BOTH",PHP_OCI_BOTH, CONST_CS | CONST_PERSISTENT);
367 	REGISTER_LONG_CONSTANT("OCI_RETURN_NULLS",PHP_OCI_RETURN_NULLS, CONST_CS | CONST_PERSISTENT);
368 	REGISTER_LONG_CONSTANT("OCI_RETURN_LOBS",PHP_OCI_RETURN_LOBS, CONST_CS | CONST_PERSISTENT);
369 
370 /* for OCINewDescriptor (real "oci" names + short "php" names */
371 	REGISTER_LONG_CONSTANT("OCI_DTYPE_FILE",OCI_DTYPE_FILE, CONST_CS | CONST_PERSISTENT);
372 	REGISTER_LONG_CONSTANT("OCI_DTYPE_LOB",OCI_DTYPE_LOB, CONST_CS | CONST_PERSISTENT);
373 	REGISTER_LONG_CONSTANT("OCI_DTYPE_ROWID",OCI_DTYPE_ROWID, CONST_CS | CONST_PERSISTENT);
374 
375 	REGISTER_LONG_CONSTANT("OCI_D_FILE",OCI_DTYPE_FILE, CONST_CS | CONST_PERSISTENT);
376 	REGISTER_LONG_CONSTANT("OCI_D_LOB",OCI_DTYPE_LOB, CONST_CS | CONST_PERSISTENT);
377 	REGISTER_LONG_CONSTANT("OCI_D_ROWID",OCI_DTYPE_ROWID, CONST_CS | CONST_PERSISTENT);
378 
379 /* for OCIWriteTemporaryLob */
380 	REGISTER_LONG_CONSTANT("OCI_TEMP_CLOB",OCI_TEMP_CLOB, CONST_CS | CONST_PERSISTENT);
381 	REGISTER_LONG_CONSTANT("OCI_TEMP_BLOB",OCI_TEMP_BLOB, CONST_CS | CONST_PERSISTENT);
382 
383 /* for Transparent Application Failover */
384 	REGISTER_LONG_CONSTANT("OCI_FO_END", OCI_FO_END, CONST_CS | CONST_PERSISTENT);
385 	REGISTER_LONG_CONSTANT("OCI_FO_ABORT", OCI_FO_ABORT, CONST_CS | CONST_PERSISTENT);
386 	REGISTER_LONG_CONSTANT("OCI_FO_REAUTH", OCI_FO_REAUTH, CONST_CS | CONST_PERSISTENT);
387 	REGISTER_LONG_CONSTANT("OCI_FO_BEGIN", OCI_FO_BEGIN, CONST_CS | CONST_PERSISTENT);
388 	REGISTER_LONG_CONSTANT("OCI_FO_ERROR", OCI_FO_ERROR, CONST_CS | CONST_PERSISTENT);
389 
390 	REGISTER_LONG_CONSTANT("OCI_FO_NONE", OCI_FO_NONE, CONST_CS | CONST_PERSISTENT);
391 	REGISTER_LONG_CONSTANT("OCI_FO_SESSION", OCI_FO_SESSION, CONST_CS | CONST_PERSISTENT);
392 	REGISTER_LONG_CONSTANT("OCI_FO_SELECT", OCI_FO_SELECT, CONST_CS | CONST_PERSISTENT);
393 	REGISTER_LONG_CONSTANT("OCI_FO_TXNAL", OCI_FO_TXNAL, CONST_CS | CONST_PERSISTENT);
394 
395 	REGISTER_LONG_CONSTANT("OCI_FO_RETRY", OCI_FO_RETRY, CONST_CS | CONST_PERSISTENT);
396 
397 	return SUCCESS;
398 }
399 
PHP_RINIT_FUNCTION(oci)400 PHP_RINIT_FUNCTION(oci)
401 {
402 	OCI_G(num_links) = OCI_G(num_persistent);
403 	OCI_G(errcode) = 0;
404 	OCI_G(edition) = NULL;
405 
406 	return SUCCESS;
407 }
408 
PHP_MSHUTDOWN_FUNCTION(oci)409 PHP_MSHUTDOWN_FUNCTION(oci)
410 {
411 	OCI_G(shutdown) = 1;
412 
413 	UNREGISTER_INI_ENTRIES();
414 
415 	return SUCCESS;
416 }
417 
PHP_RSHUTDOWN_FUNCTION(oci)418 PHP_RSHUTDOWN_FUNCTION(oci)
419 {
420 	/* Check persistent connections and do the necessary actions if needed. If persistent_helper is
421 	 * unable to process a pconnection because of a refcount, the processing would happen from
422 	 * np-destructor which is called when refcount goes to zero - php_oci_pconnection_list_np_dtor
423 	 */
424 	zend_hash_apply(&EG(persistent_list), php_oci_persistent_helper);
425 
426 	if (OCI_G(edition)) {
427 		efree(OCI_G(edition));
428 	}
429 
430 	return SUCCESS;
431 }
432 
PHP_MINFO_FUNCTION(oci)433 PHP_MINFO_FUNCTION(oci)
434 {
435 	char buf[32];
436 #if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2)))
437 	char ver[256];
438 #endif
439 
440 	php_info_print_table_start();
441 	php_info_print_table_row(2, "OCI8 Support", "enabled");
442 #if defined(HAVE_OCI8_DTRACE)
443 	php_info_print_table_row(2, "OCI8 DTrace Support", "enabled");
444 #else
445 	php_info_print_table_row(2, "OCI8 DTrace Support", "disabled");
446 #endif
447 	php_info_print_table_row(2, "OCI8 Version", PHP_OCI8_VERSION);
448 
449 #if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2)))
450 	php_oci_client_get_version(ver, sizeof(ver));
451 	php_info_print_table_row(2, "Oracle Run-time Client Library Version", ver);
452 #else
453 	php_info_print_table_row(2, "Oracle Run-time Client Library Version", "Unknown");
454 #endif
455 #if	defined(OCI_MAJOR_VERSION) && defined(OCI_MINOR_VERSION)
456 	snprintf(buf, sizeof(buf), "%d.%d", OCI_MAJOR_VERSION, OCI_MINOR_VERSION);
457 #elif defined(PHP_OCI8_ORACLE_VERSION)
458 	snprintf(buf, sizeof(buf), "%s", PHP_OCI8_ORACLE_VERSION);
459 #else
460 	snprintf(buf, sizeof(buf), "Unknown");
461 #endif
462 #if defined(HAVE_OCI_INSTANT_CLIENT)
463 	php_info_print_table_row(2, "Oracle Compile-time Instant Client Version", buf);
464 #else
465 	php_info_print_table_row(2, "Oracle Compile-time Version", buf);
466 #endif
467 
468 #if !defined(PHP_WIN32) && !defined(HAVE_OCI_INSTANT_CLIENT)
469 #if defined(PHP_OCI8_DEF_DIR)
470 	php_info_print_table_row(2, "Compile-time ORACLE_HOME", PHP_OCI8_DEF_DIR);
471 #endif
472 #if defined(PHP_OCI8_DEF_SHARED_LIBADD)
473 	php_info_print_table_row(2, "Libraries Used", PHP_OCI8_DEF_SHARED_LIBADD);
474 #endif
475 #endif
476 
477 
478 	php_info_print_table_end();
479 
480 	DISPLAY_INI_ENTRIES();
481 
482 	php_info_print_table_start();
483 	php_info_print_table_header(2, "Statistics", "");
484 	snprintf(buf, sizeof(buf), ZEND_LONG_FMT, OCI_G(num_persistent));
485 	php_info_print_table_row(2, "Active Persistent Connections", buf);
486 	snprintf(buf, sizeof(buf), ZEND_LONG_FMT, OCI_G(num_links));
487 	php_info_print_table_row(2, "Active Connections", buf);
488 	php_info_print_table_end();
489 }
490 /* }}} */
491 
492 /* {{{ list destructors */
493 
494 /* {{{ php_oci_connection_list_dtor()
495  *
496  * Non-persistent connection destructor
497  */
php_oci_connection_list_dtor(zend_resource * entry)498 static void php_oci_connection_list_dtor(zend_resource *entry)
499 {
500 	php_oci_connection *connection = (php_oci_connection *)entry->ptr;
501 
502 	if (connection) {
503 		php_oci_connection_close(connection);
504 		OCI_G(num_links)--;
505 	}
506 }
507 /* }}} */
508 
509 /* {{{ php_oci_pconnection_list_dtor()
510  *
511  * Persistent connection destructor
512  */
php_oci_pconnection_list_dtor(zend_resource * entry)513 static void php_oci_pconnection_list_dtor(zend_resource *entry)
514 {
515 	php_oci_connection *connection = (php_oci_connection *)entry->ptr;
516 
517 	if (connection) {
518 		php_oci_connection_close(connection);
519 		OCI_G(num_persistent)--;
520 		OCI_G(num_links)--;
521 	}
522 }
523 /* }}} */
524 
525 /* {{{ php_oci_pconnection_list_np_dtor()
526  *
527  * Non-Persistent destructor for persistent connection - This gets invoked when
528  * the refcount of this goes to zero in the regular list
529  */
php_oci_pconnection_list_np_dtor(zend_resource * entry)530 static void php_oci_pconnection_list_np_dtor(zend_resource *entry)
531 {
532 	php_oci_connection *connection = (php_oci_connection *)entry->ptr;
533 	zval *zvp;
534 	zend_resource *le;
535 
536 	/*
537 	 * We cannot get connection as NULL or as a stub in this function. This is the function that
538 	 * turns a pconnection to a stub
539 	 *
540 	 * If oci_password_change() changed the password of a persistent connection, close the
541 	 * connection and remove it from the persistent connection cache.  This means subsequent scripts
542 	 * will be prevented from being able to present the old (now invalid) password to a usable
543 	 * connection to the database; they must use the new password.
544 	 *
545 	 * Check for conditions that warrant removal of the hash entry
546 	 */
547 
548 	if (!connection->is_open ||
549 		connection->passwd_changed ||
550 		(PG(connection_status) & PHP_CONNECTION_TIMEOUT) ||
551 		OCI_G(in_call)) {
552 
553 		/* Remove the hash entry if present */
554 		if (connection->hash_key) {
555 			zvp = zend_hash_find(&EG(persistent_list), connection->hash_key);
556 			le = zvp ? Z_RES_P(zvp) : NULL;
557 			if (le != NULL && le->type == le_pconnection && le->ptr == connection) {
558 				zend_hash_del(&EG(persistent_list), connection->hash_key);
559 			}
560 			else {
561 				php_oci_connection_close(connection);
562 				OCI_G(num_persistent)--;
563 			}
564 		}
565 
566 #ifdef HAVE_OCI8_DTRACE
567 		if (DTRACE_OCI8_CONNECT_P_DTOR_CLOSE_ENABLED()) {
568 			DTRACE_OCI8_CONNECT_P_DTOR_CLOSE(connection);
569 		}
570 #endif /* HAVE_OCI8_DTRACE */
571 	} else {
572 		/*
573 		 * Release the connection to underlying pool.  We do this unconditionally so that
574 		 * out-of-scope pconnects are now consistent with oci_close and out-of-scope new connect
575 		 * semantics. With the PECL OCI 1.3.x extensions, we release pconnections when oci_close
576 		 * takes the refcount to zero.
577 		 *
578 		 * If oci_old_close_semantics is set, we artificially bump up the refcount and decremented
579 		 * only at request shutdown.
580 		 */
581 		php_oci_connection_release(connection);
582 
583 #ifdef HAVE_OCI8_DTRACE
584 		if (DTRACE_OCI8_CONNECT_P_DTOR_RELEASE_ENABLED()) {
585 			DTRACE_OCI8_CONNECT_P_DTOR_RELEASE(connection);
586 		}
587 #endif /* HAVE_OCI8_DTRACE */
588 	}
589 }
590 /* }}} */
591 
592 /* {{{ php_oci_statement_list_dtor()
593  *
594  * Statement destructor
595  */
php_oci_statement_list_dtor(zend_resource * entry)596 static void php_oci_statement_list_dtor(zend_resource *entry)
597 {
598 	php_oci_statement *statement = (php_oci_statement *)entry->ptr;
599 	php_oci_statement_free(statement);
600 }
601 /* }}} */
602 
603 /* {{{ php_oci_descriptor_list_dtor()
604  *
605  *	Descriptor destructor
606  */
php_oci_descriptor_list_dtor(zend_resource * entry)607 static void php_oci_descriptor_list_dtor(zend_resource *entry)
608 {
609 	php_oci_descriptor *descriptor = (php_oci_descriptor *)entry->ptr;
610 	php_oci_lob_free(descriptor);
611 }
612 /* }}} */
613 
614 /* {{{ php_oci_collection_list_dtor()
615  *
616  * Collection destructor
617  */
php_oci_collection_list_dtor(zend_resource * entry)618 static void php_oci_collection_list_dtor(zend_resource *entry)
619 {
620 	php_oci_collection *collection = (php_oci_collection *)entry->ptr;
621 	php_oci_collection_close(collection);
622 }
623 /* }}} */
624 
625 /* }}} */
626 
627 /* {{{ Hash Destructors */
628 
629 /* {{{ php_oci_define_hash_dtor()
630  *
631  * Define hash destructor
632  */
php_oci_define_hash_dtor(zval * data)633 void php_oci_define_hash_dtor(zval *data)
634 {
635 	php_oci_define *define = (php_oci_define *) Z_PTR_P(data);
636 
637 	if (define->name) {
638 		efree(define->name);
639 		define->name = NULL;
640 	}
641 
642 	zval_ptr_dtor(&define->val);
643 
644     efree(define);
645 }
646 /* }}} */
647 
648 /* {{{ php_oci_bind_hash_dtor()
649  *
650  * Bind hash destructor
651  */
php_oci_bind_hash_dtor(zval * data)652 void php_oci_bind_hash_dtor(zval *data)
653 {
654 	php_oci_bind *bind = (php_oci_bind *) Z_PTR_P(data);
655 
656 	if (!Z_ISUNDEF(bind->val)) {
657 		zval_ptr_dtor(&bind->val);
658 		ZVAL_UNDEF(&bind->val);
659 	}
660 
661 	if (bind->array.elements) {
662 		efree(bind->array.elements);
663 		bind->array.elements = NULL;
664 	}
665 
666 	if (bind->array.element_lengths) {
667 		efree(bind->array.element_lengths);
668 		bind->array.element_lengths = NULL;
669 	}
670 
671 	if (bind->array.indicators) {
672 		efree(bind->array.indicators);
673 		bind->array.indicators = NULL;
674 	}
675 
676 	efree(bind);
677 }
678 /* }}} */
679 
680 /* {{{ php_oci_column_hash_dtor()
681  *
682  * Column hash destructor
683  */
php_oci_column_hash_dtor(zval * data)684 void php_oci_column_hash_dtor(zval *data)
685 {
686 	php_oci_out_column *column = (php_oci_out_column *) Z_PTR_P(data);
687 
688 	if (column->stmtid) {
689 		zend_list_close(column->stmtid);
690 	}
691 
692 	if (column->descid) {
693 		if (GC_REFCOUNT(column->descid) == 1)
694 			zend_list_close(column->descid);
695 		else {
696 			GC_DELREF(column->descid);
697 		}
698 	}
699 
700 	if (column->data) {
701 		efree(column->data);
702 	}
703 
704 	if (column->name) {
705 		efree(column->name);
706 	}
707 
708 	efree(column);
709 }
710 /* }}} */
711 
712 /* {{{ php_oci_descriptor_flush_hash_dtor()
713  *
714  * Flush descriptors on commit
715  */
php_oci_descriptor_flush_hash_dtor(zval * data)716 void php_oci_descriptor_flush_hash_dtor(zval *data)
717 {
718 	php_oci_descriptor *descriptor = (php_oci_descriptor *) Z_PTR_P(data);
719 
720 	if (descriptor && descriptor->buffering == PHP_OCI_LOB_BUFFER_USED && (descriptor->type == OCI_DTYPE_LOB || descriptor->type == OCI_DTYPE_FILE)) {
721 		php_oci_lob_flush(descriptor, OCI_LOB_BUFFER_FREE);
722 		descriptor->buffering = PHP_OCI_LOB_BUFFER_ENABLED;
723 	}
724 	data = NULL;
725 }
726 /* }}} */
727 
728 /* }}} */
729 
730 /* {{{ php_oci_connection_descriptors_free()
731  *
732  * Free descriptors for a connection
733  */
php_oci_connection_descriptors_free(php_oci_connection * connection)734 void php_oci_connection_descriptors_free(php_oci_connection *connection)
735 {
736 	zend_hash_destroy(connection->descriptors);
737 	efree(connection->descriptors);
738 	connection->descriptors = NULL;
739 	connection->descriptor_count = 0;
740 }
741 /* }}} */
742 
743 /* {{{ php_oci_error()
744  *
745  * Fetch & print out error message if we get an error
746  * Returns an Oracle error number
747  */
php_oci_error(OCIError * err_p,sword errstatus)748 sb4 php_oci_error(OCIError *err_p, sword errstatus)
749 {
750 	text errbuf[PHP_OCI_ERRBUF_LEN];
751 	sb4 errcode = 0; /* Oracle error number */
752 
753 	switch (errstatus) {
754 		case OCI_SUCCESS:
755 			break;
756 		case OCI_SUCCESS_WITH_INFO:
757 			errcode = php_oci_fetch_errmsg(err_p, errbuf, sizeof(errbuf));
758 			if (errcode) {
759 				php_error_docref(NULL, E_WARNING, "OCI_SUCCESS_WITH_INFO: %s", errbuf);
760 			} else {
761 				php_error_docref(NULL, E_WARNING, "OCI_SUCCESS_WITH_INFO: failed to fetch error message");
762 			}
763 			break;
764 		case OCI_NEED_DATA:
765 			php_error_docref(NULL, E_WARNING, "OCI_NEED_DATA");
766 			break;
767 		case OCI_NO_DATA:
768 			errcode = php_oci_fetch_errmsg(err_p, errbuf, sizeof(errbuf));
769 			if (errcode) {
770 				php_error_docref(NULL, E_WARNING, "%s", errbuf);
771 			} else {
772 				php_error_docref(NULL, E_WARNING, "OCI_NO_DATA: failed to fetch error message");
773 			}
774 			break;
775 		case OCI_ERROR:
776 			errcode = php_oci_fetch_errmsg(err_p, errbuf, sizeof(errbuf));
777 			if (errcode) {
778 				php_error_docref(NULL, E_WARNING, "%s", errbuf);
779 			} else {
780 				php_error_docref(NULL, E_WARNING, "Failed to fetch error message");
781 			}
782 			break;
783 		case OCI_INVALID_HANDLE:
784 			php_error_docref(NULL, E_WARNING, "OCI_INVALID_HANDLE");
785 			break;
786 		case OCI_STILL_EXECUTING:
787 			php_error_docref(NULL, E_WARNING, "OCI_STILL_EXECUTING");
788 			break;
789 		case OCI_CONTINUE:
790 			php_error_docref(NULL, E_WARNING, "OCI_CONTINUE");
791 			break;
792 		default:
793 			php_error_docref(NULL, E_WARNING, "Unknown OCI error code: %d", errstatus);
794 			break;
795 	}
796 
797 #ifdef HAVE_OCI8_DTRACE
798 	if (DTRACE_OCI8_ERROR_ENABLED()) {
799 		DTRACE_OCI8_ERROR((int)errstatus, (long)errcode);
800 	}
801 #endif /* HAVE_OCI8_DTRACE */
802 
803 	return errcode;
804 }
805 /* }}} */
806 
807 /* {{{ php_oci_fetch_errmsg()
808  *
809  * Fetch error message into the buffer from the error handle provided
810  */
php_oci_fetch_errmsg(OCIError * error_handle,text * error_buf,size_t error_buf_size)811 sb4 php_oci_fetch_errmsg(OCIError *error_handle, text *error_buf, size_t error_buf_size)
812 {
813 	sb4 error_code = 0;
814 
815 	PHP_OCI_CALL(OCIErrorGet, (error_handle, (ub4)1, NULL, &error_code, error_buf, (ub4)error_buf_size, (ub4)OCI_HTYPE_ERROR));
816 
817 	if (error_code) {
818 		int err_buf_len = (int) strlen((char *)error_buf);
819 
820 		if (err_buf_len && error_buf[err_buf_len - 1] == '\n') {
821 			error_buf[err_buf_len - 1] = '\0';
822 		}
823 	}
824 	return error_code;
825 }
826 /* }}} */
827 
828 /* {{{ php_oci_fetch_sqltext_offset()
829  *
830  * Compute offset in the SQL statement
831  */
php_oci_fetch_sqltext_offset(php_oci_statement * statement,text ** sqltext,ub2 * error_offset)832 int php_oci_fetch_sqltext_offset(php_oci_statement *statement, text **sqltext, ub2 *error_offset)
833 {
834 	sword errstatus;
835 
836 	*sqltext = NULL;
837 	*error_offset = 0;
838 	PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)statement->stmt, OCI_HTYPE_STMT, (dvoid *) sqltext, (ub4 *)0, OCI_ATTR_STATEMENT, statement->err));
839 
840 	if (errstatus != OCI_SUCCESS) {
841 		statement->errcode = php_oci_error(statement->err, errstatus);
842 		PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
843 		return 1;
844 	}
845 
846 	PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)statement->stmt, OCI_HTYPE_STMT, (ub2 *)error_offset, (ub4 *)0, OCI_ATTR_PARSE_ERROR_OFFSET, statement->err));
847 
848 	if (errstatus != OCI_SUCCESS) {
849 		statement->errcode = php_oci_error(statement->err, errstatus);
850 		PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
851 		return 1;
852 	}
853 	return 0;
854 }
855 /* }}} */
856 
857 /* {{{ php_oci_do_connect()
858  *
859  * Connect wrapper
860  */
php_oci_do_connect(INTERNAL_FUNCTION_PARAMETERS,int persistent,int exclusive)861 void php_oci_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent, int exclusive)
862 {
863 	php_oci_connection *connection;
864 	char *username, *password;
865 	char *dbname = NULL, *charset = NULL;
866 	size_t username_len = 0, password_len = 0;
867 	size_t dbname_len = 0, charset_len = 0;
868 	zend_long session_mode = OCI_DEFAULT;
869 
870 	/* if a fourth parameter is handed over, it is the charset identifier (but is only used in Oracle 9i+) */
871 	ZEND_PARSE_PARAMETERS_START(2, 5)
872 		Z_PARAM_STRING(username, username_len)
873 		Z_PARAM_STRING(password, password_len)
874 		Z_PARAM_OPTIONAL
875 		Z_PARAM_STRING_OR_NULL(dbname, dbname_len)
876 		Z_PARAM_STRING(charset, charset_len)
877 		Z_PARAM_LONG(session_mode)
878 	ZEND_PARSE_PARAMETERS_END();
879 
880 #ifdef HAVE_OCI8_DTRACE
881 	if (DTRACE_OCI8_CONNECT_ENTRY_ENABLED()) {
882 		DTRACE_OCI8_CONNECT_ENTRY(username, dbname, charset, session_mode, persistent, exclusive);
883 	}
884 #endif /* HAVE_OCI8_DTRACE */
885 
886 	if (!charset_len) {
887 		charset = NULL;
888 	}
889 
890 	connection = php_oci_do_connect_ex(username, (int) username_len, password, (int) password_len, NULL, 0, dbname, (int) dbname_len, charset, session_mode, persistent, exclusive);
891 
892 #ifdef HAVE_OCI8_DTRACE
893 	if (DTRACE_OCI8_CONNECT_RETURN_ENABLED()) {
894 		DTRACE_OCI8_CONNECT_RETURN(connection);
895 	}
896 #endif /* HAVE_OCI8_DTRACE */
897 
898 
899 	if (!connection) {
900 		RETURN_FALSE;
901 	}
902 	RETURN_RES(connection->id);
903 
904 }
905 /* }}} */
906 
907 /* {{{ php_oci_do_connect_ex()
908  *
909  * The real connect function. Allocates all the resources needed, establishes the connection and
910  * returns the result handle (or NULL)
911  */
php_oci_do_connect_ex(char * username,int username_len,char * password,int password_len,char * new_password,int new_password_len,char * dbname,int dbname_len,char * charset,zend_long session_mode,int persistent,int exclusive)912 php_oci_connection *php_oci_do_connect_ex(char *username, int username_len, char *password, int password_len, char *new_password, int new_password_len, char *dbname, int dbname_len, char *charset, zend_long session_mode, int persistent, int exclusive)
913 {
914 	zval *zvp;
915 	zend_resource *le = NULL;
916 	zend_resource new_le;
917 	php_oci_connection *connection = NULL;
918 	smart_str hashed_details = {0};
919 	time_t timestamp;
920 	php_oci_spool *session_pool = NULL;
921 	zend_bool use_spool = 1;	   /* Default is to use client-side session pool */
922 	zend_bool ping_done = 0;
923 
924 	ub2 charsetid = 0;
925 	ub2 charsetid_nls_lang = 0;
926 
927 	if (session_mode & ~(OCI_SYSOPER | OCI_SYSDBA | PHP_OCI_CRED_EXT)) {
928 		php_error_docref(NULL, E_WARNING, "Invalid session mode specified (" ZEND_LONG_FMT ")", session_mode);
929 		return NULL;
930 	}
931 	if (session_mode & (OCI_SYSOPER | OCI_SYSDBA | PHP_OCI_CRED_EXT)) {
932 		if ((session_mode & OCI_SYSOPER) && (session_mode & OCI_SYSDBA)) {
933 			php_error_docref(NULL, E_WARNING, "OCI_SYSDBA and OCI_SYSOPER cannot be used together");
934 			return NULL;
935 		}
936 		if (session_mode & PHP_OCI_CRED_EXT) {
937 #ifdef PHP_WIN32
938 			/* Disable external authentication on Windows as Impersonation is not yet handled.
939 			 * TODO: Re-enable this once OCI provides capability.
940 			 */
941 			php_error_docref(NULL, E_WARNING, "External Authentication is not supported on Windows");
942 			return NULL;
943 #endif
944 			if (username_len != 1 || username[0] != '/' || password_len != 0) {
945 				php_error_docref(NULL, E_WARNING, "OCI_CRED_EXT can only be used with a username of \"/\" and a NULL password");
946 				return NULL;
947 			}
948 		}
949 		if (session_mode & (OCI_SYSOPER | OCI_SYSDBA)) {
950 			/* Increase security by not caching privileged oci_pconnect() connections. The
951 			 * connection becomes equivalent to oci_connect() or oci_new_connect().
952 			 */
953 			persistent = 0;
954 			if (!OCI_G(privileged_connect)) {
955 				php_error_docref(NULL, E_WARNING, "Privileged connect is disabled. Enable oci8.privileged_connect to be able to connect as SYSOPER or SYSDBA");
956 				return NULL;
957 			}
958 		}
959 	}
960 
961 	/* Initialize global handles if they weren't initialized before */
962 	if (OCI_G(env) == NULL) {
963 		php_oci_init_global_handles();
964 		if (OCI_G(env) == NULL) {
965 			return NULL;
966 		}
967 	}
968 
969 	/* We cannot use the new session create logic (OCISessionGet from
970 	 * client-side session pool) when privileged connect or password
971 	 * change is attempted or OCI_CRED_EXT mode is specified.
972 	 * TODO: Re-enable this when OCI provides support.
973 	 */
974 	if ((session_mode & (OCI_SYSOPER | OCI_SYSDBA | PHP_OCI_CRED_EXT)) || (new_password_len)) {
975 		use_spool = 0;
976 	}
977 
978 	smart_str_appendl_ex(&hashed_details, "oci8***", sizeof("oci8***") - 1, 0);
979 	smart_str_appendl_ex(&hashed_details, username, username_len, 0);
980 	smart_str_appendl_ex(&hashed_details, "**", sizeof("**") - 1, 0);
981 
982 	/* DRCP: connection_class is an attribute of a connection */
983 	if (OCI_G(connection_class)){
984 		smart_str_appendl_ex(&hashed_details, OCI_G(connection_class), strlen(OCI_G(connection_class)), 0);
985 	}
986 	smart_str_appendl_ex(&hashed_details, "**", sizeof("**") - 1, 0);
987 
988 	/* Add edition attribute to the hash */
989 	if (OCI_G(edition)){
990 		smart_str_appendl_ex(&hashed_details, OCI_G(edition), strlen(OCI_G(edition)), 0);
991 	}
992 	smart_str_appendl_ex(&hashed_details, "**", sizeof("**") - 1, 0);
993 
994 	if (password_len) {
995 		zend_ulong password_hash;
996 		password_hash = zend_hash_func(password, password_len);
997 		smart_str_append_unsigned_ex(&hashed_details, password_hash, 0);
998 	}
999 	smart_str_appendl_ex(&hashed_details, "**", sizeof("**") - 1, 0);
1000 
1001 	if (dbname) {
1002 		smart_str_appendl_ex(&hashed_details, dbname, dbname_len, 0);
1003 	}
1004 	smart_str_appendl_ex(&hashed_details, "**", sizeof("**") - 1, 0);
1005 
1006 	if (charset && *charset) {
1007 		PHP_OCI_CALL_RETURN(charsetid, OCINlsCharSetNameToId, (OCI_G(env), (CONST oratext *)charset));
1008 		if (!charsetid) {
1009 			php_error_docref(NULL, E_WARNING, "Invalid character set name: %s", charset);
1010 		} else {
1011 			smart_str_append_unsigned_ex(&hashed_details, charsetid, 0);
1012 		}
1013 	}
1014 
1015 	/* use NLS_LANG if no or invalid charset specified */
1016 	if (!charsetid) {
1017 		size_t rsize = 0;
1018 		sword result;
1019 
1020 		PHP_OCI_CALL_RETURN(result, OCINlsEnvironmentVariableGet, (&charsetid_nls_lang, 0, OCI_NLS_CHARSET_ID, 0, &rsize));
1021 		if (result != OCI_SUCCESS) {
1022 			charsetid_nls_lang = 0;
1023 		}
1024 		smart_str_append_unsigned_ex(&hashed_details, charsetid_nls_lang, 0);
1025 	}
1026 
1027 	timestamp = time(NULL);
1028 
1029 	smart_str_append_unsigned_ex(&hashed_details, session_mode, 0);
1030 	if (persistent) {
1031 		smart_str_appendl_ex(&hashed_details, "pc", sizeof("pc") - 1, 0);
1032 	}
1033 
1034 	smart_str_0(&hashed_details);
1035 
1036 	/* make it lowercase */
1037 	php_strtolower(ZSTR_VAL(hashed_details.s), ZSTR_LEN(hashed_details.s));
1038 
1039 	if (!exclusive && !new_password) {
1040 		zend_bool found = 0;
1041 
1042 		if (persistent && ((zvp = zend_hash_find(&EG(persistent_list), hashed_details.s))) != NULL) {
1043 			zend_resource *le = Z_RES_P(zvp);
1044 
1045 			found = 1;
1046 			/* found */
1047 			if (le->type == le_pconnection) {
1048 				connection = (php_oci_connection *)le->ptr;
1049 			}
1050 		} else if (!persistent && ((zvp = zend_hash_find(&EG(regular_list), hashed_details.s)) != NULL)) {
1051 			le = Z_RES_P(zvp);
1052 			found = 1;
1053 			if (le->type == le_index_ptr) {
1054 				zend_resource *ptr;
1055 
1056 				ptr = (zend_resource *) le->ptr;
1057 				if (ptr && (ptr->type == le_connection)) {
1058 					connection = (php_oci_connection *)ptr->ptr;
1059 				}
1060 			}
1061 		}
1062 
1063 #ifdef HAVE_OCI8_DTRACE
1064 		if (DTRACE_OCI8_CONNECT_LOOKUP_ENABLED()) {
1065 			DTRACE_OCI8_CONNECT_LOOKUP(connection, connection && connection->is_stub ? 1 : 0);
1066 		}
1067 #endif /* HAVE_OCI8_DTRACE */
1068 
1069 		/* If we got a pconnection stub, then 'load'(OCISessionGet) the real connection from its
1070 		 * private spool A connection is a stub if it is only a cached structure and the real
1071 		 * connection is released to its underlying private session pool.  We currently do not have
1072 		 * stub support for non-persistent conns.
1073 		 *
1074 		 * TODO: put in negative code for non-persistent stubs
1075 		 */
1076 		if (connection && connection->is_persistent && connection->is_stub) {
1077 			if (php_oci_create_session(connection, NULL, dbname, dbname_len, username, username_len, password, password_len, new_password, new_password_len, (int) session_mode)) {
1078 				smart_str_free(&hashed_details);
1079 				zend_hash_del(&EG(persistent_list), connection->hash_key);
1080 
1081 				return NULL;
1082 			}
1083 			/* We do the ping in php_oci_create_session, no need to ping again below */
1084 			ping_done = 1;
1085 		}
1086 
1087 		if (connection) {
1088 			if (connection->is_open) {
1089 				/* found an open connection. now ping it */
1090 				if (connection->is_persistent) {
1091 
1092 					/* Check connection liveness in the following order:
1093 					 * 1) always check OCI_ATTR_SERVER_STATUS
1094 					 * 2) see if it's time to ping it
1095 					 * 3) ping it if needed
1096 					 */
1097 					if (php_oci_connection_status(connection)) {
1098 						/* Only ping if:
1099 						 *
1100 						 * 1) next_ping > 0, which means that ping_interval is not -1 (aka "Off")
1101 						 *
1102 						 * 2) current_timestamp > next_ping, which means "it's time to check if it's
1103 						 * still alive"
1104 						 */
1105 						if (!ping_done && (*(connection->next_pingp) > 0) && (timestamp >= *(connection->next_pingp)) && !php_oci_connection_ping(connection)) {
1106 							/* server died */
1107 						} else {
1108 							php_oci_connection *tmp = (php_oci_connection *) NULL;
1109 							zval *tmp_val = (zval *) NULL;
1110 
1111 							/* okay, the connection is open and the server is still alive */
1112 							connection->used_this_request = 1;
1113 							if (connection->id) {
1114 								tmp_val = zend_hash_index_find(&EG(regular_list), connection->id->handle);
1115 								if ((tmp_val != NULL) && (Z_TYPE_P(tmp_val) == IS_RESOURCE)) {
1116 									tmp = Z_RES_VAL_P(tmp_val);
1117 								}
1118 
1119 								if ((tmp_val != NULL) && (tmp != NULL) &&
1120 									(ZSTR_LEN(tmp->hash_key) == ZSTR_LEN(hashed_details.s)) &&
1121 									(memcmp(ZSTR_VAL(tmp->hash_key), ZSTR_VAL(hashed_details.s),
1122 									 ZSTR_LEN(tmp->hash_key)) == 0)) {
1123 									connection = tmp;
1124 									GC_ADDREF(connection->id);
1125 								}
1126 							} else {
1127 								PHP_OCI_REGISTER_RESOURCE(connection, le_pconnection);
1128 								/* Persistent connections: For old close semantics we artificially
1129 								 * bump up the refcount to prevent the non-persistent destructor
1130 								 * from getting called until request shutdown. The refcount is
1131 								 * decremented in the persistent helper
1132 								 */
1133 								if (OCI_G(old_oci_close_semantics)) {
1134 									GC_ADDREF(connection->id);
1135 								}
1136 							}
1137 							smart_str_free(&hashed_details);
1138 							return connection;
1139 						}
1140 					}
1141 					/* server died */
1142 				} else {
1143 					/* we do not ping non-persistent connections */
1144 					smart_str_free(&hashed_details);
1145 					GC_ADDREF(connection->id);
1146 					return connection;
1147 				}
1148 			} /* is_open is true? */
1149 
1150 			/* Server died - connection not usable. The is_open=true can also fall through to here,
1151 			 * if ping fails
1152 			 */
1153 			if (persistent){
1154 
1155 				connection->is_open = 0;
1156 				connection->used_this_request = 1;
1157 
1158 				/* We have to do a hash_del but need to preserve the resource if there is a positive
1159 				 * refcount. Set the data pointer in the list entry to NULL
1160 				 */
1161 				if (connection == connection->id->ptr && le) {
1162 					le->ptr = NULL;
1163 				}
1164 
1165 				zend_hash_del(&EG(persistent_list), hashed_details.s);
1166 			} else {
1167 				/* We only remove the hash entry. The resource and the list entry with its pointer
1168 				 * to the resource are still intact
1169 				 */
1170 				zend_hash_del(&EG(regular_list), hashed_details.s);
1171 			}
1172 
1173 			connection = NULL;
1174 		} else if (found) {
1175 			/* found something, but it's not a connection, delete it */
1176 			if (persistent) {
1177 				zend_hash_del(&EG(persistent_list), hashed_details.s);
1178 			} else {
1179 				zend_hash_del(&EG(regular_list), hashed_details.s);
1180 			}
1181 		}
1182 	}
1183 
1184 	/* Check if we have reached max_persistent. If so, try to remove a few timed-out connections. As
1185 	 * a last resort, return a non-persistent connection.
1186 	 */
1187 	if (persistent) {
1188 		zend_bool alloc_non_persistent = 0;
1189 
1190 		if (OCI_G(max_persistent) != -1 && OCI_G(num_persistent) >= OCI_G(max_persistent)) {
1191 			/* try to find an idle connection and kill it */
1192 			zend_hash_apply(&EG(persistent_list), php_oci_persistent_helper);
1193 
1194 			if (OCI_G(max_persistent) != -1 && OCI_G(num_persistent) >= OCI_G(max_persistent)) {
1195 				/* all persistent connactions are in use, fallback to non-persistent connection creation */
1196 				php_error_docref(NULL, E_NOTICE, "Too many open persistent connections (" ZEND_LONG_FMT ")", OCI_G(num_persistent));
1197 				alloc_non_persistent = 1;
1198 			}
1199 		}
1200 
1201 		if (alloc_non_persistent) {
1202 			connection = (php_oci_connection *) ecalloc(1, sizeof(php_oci_connection));
1203 			connection->hash_key = zend_string_dup(hashed_details.s, 0);
1204 			connection->is_persistent = 0;
1205 			ZVAL_UNDEF(&connection->taf_callback);
1206 #ifdef HAVE_OCI8_DTRACE
1207 			connection->client_id = NULL;
1208 #endif
1209 		} else {
1210 			connection = (php_oci_connection *) calloc(1, sizeof(php_oci_connection));
1211 			if (connection == NULL) {
1212 				return NULL;
1213 			}
1214 			connection->hash_key = zend_string_dup(hashed_details.s, 1);
1215 			if (connection->hash_key == NULL) {
1216 				free(connection);
1217 				return NULL;
1218 			}
1219 			connection->is_persistent = 1;
1220 			ZVAL_UNDEF(&connection->taf_callback);
1221 #ifdef HAVE_OCI8_DTRACE
1222 			connection->client_id = NULL;
1223 #endif
1224 		}
1225 	} else {
1226 		connection = (php_oci_connection *) ecalloc(1, sizeof(php_oci_connection));
1227 		connection->hash_key = zend_string_dup(hashed_details.s, 0);
1228 		connection->is_persistent = 0;
1229 		ZVAL_UNDEF(&connection->taf_callback);
1230 #ifdef HAVE_OCI8_DTRACE
1231 		connection->client_id = NULL;
1232 #endif
1233 	}
1234 
1235 	/* {{{ Get the session pool that suits this connection request from the persistent list. This
1236 	 * step is only for non-persistent connections as persistent connections have private session
1237 	 * pools. Non-persistent conns use shared session pool to allow for optimizations such as
1238 	 * caching the physical connection (for DRCP) even when the non-persistent php connection is
1239 	 * destroyed.
1240 	 *
1241 	 * TODO: Unconditionally do this once OCI provides extended OCISessionGet capability
1242 	 */
1243 	if (use_spool && !connection->is_persistent) {
1244 		if ((session_pool = php_oci_get_spool(username, username_len, password, password_len, dbname, dbname_len, charsetid ? charsetid:charsetid_nls_lang))==NULL)
1245 		{
1246 			php_oci_connection_close(connection);
1247 			smart_str_free(&hashed_details);
1248 			return NULL;
1249 		}
1250 	}
1251 	/* }}} */
1252 
1253 	connection->idle_expiry = (OCI_G(persistent_timeout) > 0) ? (timestamp + OCI_G(persistent_timeout)) : 0;
1254 
1255 	/* Mark password as unchanged by PHP during the duration of the database session */
1256 	connection->passwd_changed = 0;
1257 
1258 	smart_str_free(&hashed_details);
1259 
1260 	if (charsetid) {
1261 		connection->charset = charsetid;
1262 	} else {
1263 		connection->charset = charsetid_nls_lang;
1264 	}
1265 
1266 	/* Old session creation semantics when session pool cannot be used Eg: privileged
1267 	 * connect/password change
1268 	 */
1269 	if (!use_spool) {
1270 		if (php_oci_old_create_session(connection, dbname, dbname_len, username, username_len, password, password_len, new_password, new_password_len, (int) session_mode)) {
1271 			php_oci_connection_close(connection);
1272 			return NULL;
1273 		}
1274 	} else {
1275 		/* create using the client-side session pool */
1276 		if (php_oci_create_session(connection, session_pool, dbname, dbname_len, username, username_len, password, password_len, new_password, new_password_len, (int) session_mode)) {
1277 			php_oci_connection_close(connection);
1278 			return NULL;
1279 		}
1280 	}
1281 
1282 	/* Mark it as open */
1283 	connection->is_open = 1;
1284 
1285 	/* add to the appropriate hash */
1286 	if (connection->is_persistent) {
1287 		connection->used_this_request = 1;
1288 		PHP_OCI_REGISTER_RESOURCE(connection, le_pconnection);
1289 
1290 		/* Persistent connections: For old close semantics we artificially bump up the refcount to
1291 		 * prevent the non-persistent destructor from getting called until request shutdown. The
1292 		 * refcount is decremented in the persistent helper
1293 		 */
1294 		if (OCI_G(old_oci_close_semantics)) {
1295 			GC_ADDREF(connection->id);
1296 		}
1297 		zend_register_persistent_resource_ex(connection->hash_key, connection, le_pconnection);
1298 		OCI_G(num_persistent)++;
1299 		OCI_G(num_links)++;
1300 	} else if (!exclusive) {
1301 		PHP_OCI_REGISTER_RESOURCE(connection, le_connection);
1302 		new_le.ptr = connection->id;
1303 		new_le.type = le_index_ptr;
1304 		zend_hash_update_mem(&EG(regular_list), connection->hash_key, (void *)&new_le, sizeof(zend_resource));
1305 		OCI_G(num_links)++;
1306 	} else {
1307 		PHP_OCI_REGISTER_RESOURCE(connection, le_connection);
1308 		OCI_G(num_links)++;
1309 	}
1310 
1311 #ifdef HAVE_OCI8_DTRACE
1312 	if (DTRACE_OCI8_CONNECT_TYPE_ENABLED()) {
1313 		DTRACE_OCI8_CONNECT_TYPE(connection->is_persistent ? 1 : 0, exclusive ? 1 : 0, connection, OCI_G(num_persistent), OCI_G(num_links));
1314 	}
1315 #endif /* HAVE_OCI8_DTRACE */
1316 
1317 	return connection;
1318 }
1319 /* }}} */
1320 
1321 /* {{{ php_oci_connection_ping()
1322  *
1323  * Ping connection. Uses OCIPing() or OCIServerVersion() depending on the Oracle Client version
1324  */
php_oci_connection_ping(php_oci_connection * connection)1325 static int php_oci_connection_ping(php_oci_connection *connection)
1326 {
1327 	sword errstatus;
1328 #if (!((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))))
1329 	char version[256];
1330 #endif
1331 
1332 	OCI_G(errcode) = 0;  		/* assume ping is successful */
1333 
1334 	/* Use OCIPing instead of OCIServerVersion. If OCIPing returns ORA-1010 (invalid OCI operation)
1335 	 * such as from Pre-10.1 servers, the error is still from the server and we would have
1336 	 * successfully performed a roundtrip and validated the connection. Use OCIServerVersion for
1337 	 * Pre-10.2 clients
1338 	 */
1339 #if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2)))	/* OCIPing available 10.2 onwards */
1340 	PHP_OCI_CALL_RETURN(errstatus, OCIPing, (connection->svc, OCI_G(err), OCI_DEFAULT));
1341 #else
1342 	/* use good old OCIServerVersion() */
1343 	PHP_OCI_CALL_RETURN(errstatus, OCIServerVersion, (connection->svc, OCI_G(err), (text *)version, sizeof(version), OCI_HTYPE_SVCCTX));
1344 #endif
1345 
1346 	if (errstatus == OCI_SUCCESS) {
1347 		return 1;
1348 	} else {
1349 		sb4 error_code = 0;
1350 		text tmp_buf[PHP_OCI_ERRBUF_LEN];
1351 
1352 		/* Treat ORA-1010 as a successful Ping */
1353 		OCIErrorGet(OCI_G(err), (ub4)1, NULL, &error_code, tmp_buf, (ub4)PHP_OCI_ERRBUF_LEN, (ub4)OCI_HTYPE_ERROR);
1354 		if (error_code == 1010) {
1355 			return 1;
1356 		}
1357 		OCI_G(errcode) = error_code;
1358 	}
1359 
1360 	return 0;
1361 }
1362 /* }}} */
1363 
1364 /* {{{ php_oci_connection_status()
1365  *
1366  * Check connection status (pre-ping check)
1367  */
php_oci_connection_status(php_oci_connection * connection)1368 static int php_oci_connection_status(php_oci_connection *connection)
1369 {
1370 	ub4 ss = OCI_SERVER_NOT_CONNECTED;
1371 	sword errstatus;
1372 
1373 	/* get OCI_ATTR_SERVER_STATUS */
1374 	PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)connection->server, OCI_HTYPE_SERVER, (dvoid *)&ss, (ub4 *)0, OCI_ATTR_SERVER_STATUS, OCI_G(err)));
1375 
1376 	if (errstatus == OCI_SUCCESS && ss == OCI_SERVER_NORMAL) {
1377 		return 1;
1378 	}
1379 
1380 	/* ignore errors here, just return failure */
1381 	return 0;
1382 }
1383 /* }}} */
1384 
1385 /* {{{ php_oci_connection_rollback()
1386  *
1387  * Rollback connection
1388  */
php_oci_connection_rollback(php_oci_connection * connection)1389 int php_oci_connection_rollback(php_oci_connection *connection)
1390 {
1391 	sword errstatus;
1392 
1393 	PHP_OCI_CALL_RETURN(errstatus, OCITransRollback, (connection->svc, connection->err, (ub4) 0));
1394 	connection->rb_on_disconnect = 0;
1395 
1396 	if (errstatus != OCI_SUCCESS) {
1397 		connection->errcode = php_oci_error(connection->err, errstatus);
1398 		PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
1399 		return 1;
1400 	}
1401 	connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
1402 	return 0;
1403 }
1404 /* }}} */
1405 
1406 /* {{{ php_oci_connection_commit()
1407  *
1408  * Commit connection
1409  */
php_oci_connection_commit(php_oci_connection * connection)1410 int php_oci_connection_commit(php_oci_connection *connection)
1411 {
1412 	sword errstatus;
1413 
1414 	PHP_OCI_CALL_RETURN(errstatus, OCITransCommit, (connection->svc, connection->err, (ub4) 0));
1415 	connection->rb_on_disconnect = 0;
1416 
1417 	if (errstatus != OCI_SUCCESS) {
1418 		connection->errcode = php_oci_error(connection->err, errstatus);
1419 		PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
1420 		return 1;
1421 	}
1422 	connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
1423 	return 0;
1424 }
1425 /* }}} */
1426 
1427 /* {{{ php_oci_connection_close()
1428  *
1429  * Close the connection and free all its resources
1430  */
php_oci_connection_close(php_oci_connection * connection)1431 static int php_oci_connection_close(php_oci_connection *connection)
1432 {
1433 	int result = 0;
1434 	zend_bool in_call_save = OCI_G(in_call);
1435 
1436 #ifdef HAVE_OCI8_DTRACE
1437 	if (DTRACE_OCI8_CONNECTION_CLOSE_ENABLED()) {
1438 		DTRACE_OCI8_CONNECTION_CLOSE(connection);
1439 	}
1440 #endif /* HAVE_OCI8_DTRACE */
1441 
1442 	if (!connection->is_stub) {
1443 		/* Release resources associated with connection */
1444 		php_oci_connection_release(connection);
1445 	}
1446 
1447 	if (!connection->using_spool && connection->svc) {
1448 		PHP_OCI_CALL(OCISessionEnd, (connection->svc, connection->err, connection->session, (ub4) 0));
1449 	}
1450 
1451 	if (connection->err) {
1452 		PHP_OCI_CALL(OCIHandleFree, ((dvoid *) connection->err, (ub4) OCI_HTYPE_ERROR));
1453 	}
1454 	if (connection->authinfo) {
1455 		PHP_OCI_CALL(OCIHandleFree, ((dvoid *) connection->authinfo, (ub4) OCI_HTYPE_AUTHINFO));
1456 	}
1457 
1458 	/* No Handlefrees for session pool connections */
1459 	if (!connection->using_spool) {
1460 		if (connection->session) {
1461 			PHP_OCI_CALL(OCIHandleFree, ((dvoid *) connection->session, OCI_HTYPE_SESSION));
1462 		}
1463 
1464 		if (connection->is_attached) {
1465 			PHP_OCI_CALL(OCIServerDetach, (connection->server, OCI_G(err), OCI_DEFAULT));
1466 		}
1467 
1468 		if (connection->svc) {
1469 			PHP_OCI_CALL(OCIHandleFree, ((dvoid *) connection->svc, (ub4) OCI_HTYPE_SVCCTX));
1470 		}
1471 
1472 		if (connection->server) {
1473 			PHP_OCI_CALL(OCIHandleFree, ((dvoid *) connection->server, (ub4) OCI_HTYPE_SERVER));
1474 		}
1475 
1476 		if (connection->env) {
1477 			PHP_OCI_CALL(OCIHandleFree, ((dvoid *) connection->env, OCI_HTYPE_ENV));
1478 		}
1479 	} else if (connection->private_spool) {
1480 	/* Keep this as the last member to be freed, as there are dependencies
1481 	 * (like env) on the session pool
1482 	 */
1483 		php_oci_spool_close(connection->private_spool);
1484 		connection->private_spool = NULL;
1485 	}
1486 
1487 	if (GC_REFCOUNT(connection->hash_key) >= 2) {
1488 		zend_hash_del(&EG(regular_list), connection->hash_key);
1489 	}
1490 
1491 	if (connection->hash_key) {
1492 		pefree(connection->hash_key, connection->is_persistent);
1493 		connection->hash_key = NULL;
1494 	}
1495 #ifdef HAVE_OCI8_DTRACE
1496 	if (connection->client_id) {
1497 		pefree(connection->client_id, connection->is_persistent);
1498 		connection->client_id = NULL;
1499 	}
1500 #endif /* HAVE_OCI8_DTRACE */
1501 
1502 	if (!Z_ISUNDEF(connection->taf_callback)) {
1503 		/* If it's NULL, then its value should be freed already */
1504 		if (!Z_ISNULL(connection->taf_callback)) {
1505 			zval_ptr_dtor(&connection->taf_callback);
1506 		}
1507 		ZVAL_UNDEF(&connection->taf_callback);
1508 	}
1509 
1510 	pefree(connection, connection->is_persistent);
1511 	connection = NULL;
1512 	OCI_G(in_call) = in_call_save;
1513 	return result;
1514 }
1515 /* }}} */
1516 
1517 /* {{{ php_oci_connection_release()
1518  *
1519  * Release the connection's resources. This involves freeing descriptors and rolling back
1520  * transactions, setting timeout-related parameters etc. For session-pool using connections, the
1521  * underlying connection is released to its session pool.
1522  */
php_oci_connection_release(php_oci_connection * connection)1523 int php_oci_connection_release(php_oci_connection *connection)
1524 {
1525 	int result = 0;
1526 	zend_bool in_call_save = OCI_G(in_call);
1527 	time_t timestamp = time(NULL);
1528 
1529 	if (connection->is_stub) {
1530 		return 0;
1531 	}
1532 
1533 	if (connection->descriptors) {
1534 		php_oci_connection_descriptors_free(connection);
1535 	}
1536 
1537 	if (connection->svc) {
1538 		/* rollback outstanding transactions */
1539 		if (connection->rb_on_disconnect) {
1540 			if (php_oci_connection_rollback(connection)) {
1541 				/* rollback failed */
1542 				result = 1;
1543 			}
1544 		}
1545 	}
1546 
1547 	if (OCI_G(persistent_timeout) > 0) {
1548 		connection->idle_expiry = timestamp + OCI_G(persistent_timeout);
1549 	}
1550 
1551 	/* We may have half-cooked connections to clean up */
1552 	if (connection->next_pingp) {
1553 		if (OCI_G(ping_interval) >= 0) {
1554 			*(connection->next_pingp) = timestamp + OCI_G(ping_interval);
1555 		} else {
1556 			/* ping_interval is -1 */
1557 			*(connection->next_pingp) = 0;
1558 		}
1559 	}
1560 
1561 	/* Release the session (stubs are filtered out at the beginning)*/
1562 	if (connection->using_spool) {
1563 		ub4 rlsMode = OCI_DEFAULT;
1564 
1565 		if (result) {
1566 			rlsMode |= OCI_SESSRLS_DROPSESS;
1567 		}
1568 
1569 		/* Sessions for non-persistent connections should be dropped.  For 11 and above, the session
1570 		 * pool has its own mechanism for doing so for purity NEW connections. We need to do so
1571 		 * explicitly for 10.2 and earlier.
1572 		 */
1573 #if (!(OCI_MAJOR_VERSION >= 11))
1574 		if (!connection->is_persistent) {
1575 			rlsMode |= OCI_SESSRLS_DROPSESS;
1576 		}
1577 #endif
1578 
1579 		if (connection->svc) {
1580 			PHP_OCI_CALL(OCISessionRelease, (connection->svc, connection->err, NULL,
1581 										 0, rlsMode));
1582 		}
1583 		/* It no longer has relation with the database session. However authinfo and env are
1584 		 * cached
1585 		 */
1586 		connection->svc = NULL;
1587 		connection->server = NULL;
1588 		connection->session = NULL;
1589 
1590 		connection->is_attached = connection->is_open = connection->rb_on_disconnect = connection->used_this_request = 0;
1591 		connection->is_stub = 1;
1592 
1593 		/* Cut the link between the connection structure and the time_t structure allocated within
1594 		 * the OCI session
1595 		 */
1596 		connection->next_pingp = NULL;
1597 #ifdef HAVE_OCI8_DTRACE
1598 		if (connection->client_id) {
1599 			pefree(connection->client_id, connection->is_persistent);
1600 			connection->client_id = NULL;
1601 		}
1602 #endif /* HAVE_OCI8_DTRACE */
1603 	}
1604 
1605 	/* Always set id to null, so next time a new resource is being registered. */
1606 	connection->id = NULL;
1607 
1608 	OCI_G(in_call) = in_call_save;
1609 	return result;
1610 }
1611 /* }}} */
1612 
1613 /* {{{ php_oci_password_change()
1614  *
1615  * Change password for the user with the username given
1616  */
php_oci_password_change(php_oci_connection * connection,char * user,int user_len,char * pass_old,int pass_old_len,char * pass_new,int pass_new_len)1617 int php_oci_password_change(php_oci_connection *connection, char *user, int user_len, char *pass_old, int pass_old_len, char *pass_new, int pass_new_len)
1618 {
1619 	sword errstatus;
1620 
1621 	PHP_OCI_CALL_RETURN(errstatus, OCIPasswordChange, (connection->svc, connection->err, (text *)user, user_len, (text *)pass_old, pass_old_len, (text *)pass_new, pass_new_len, OCI_DEFAULT));
1622 
1623 	if (errstatus != OCI_SUCCESS) {
1624 		connection->errcode = php_oci_error(connection->err, errstatus);
1625 		PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
1626 		return 1;
1627 	}
1628 	connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
1629 	connection->passwd_changed = 1;
1630 	return 0;
1631 }
1632 /* }}} */
1633 
1634 /* {{{ php_oci_client_get_version()
1635  *
1636  * Get Oracle client library version
1637  */
php_oci_client_get_version(char * version,size_t version_size)1638 void php_oci_client_get_version(char *version, size_t version_size)
1639 {
1640 #if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2)))	/* OCIClientVersion only available 10.2 onwards */
1641 	sword major_version = 0;
1642 	sword minor_version = 0;
1643 	sword update_num = 0;
1644 	sword patch_num = 0;
1645 	sword port_update_num = 0;
1646 
1647 	PHP_OCI_CALL(OCIClientVersion, (&major_version, &minor_version, &update_num, &patch_num, &port_update_num));
1648 	snprintf(version, version_size, "%d.%d.%d.%d.%d", major_version, minor_version, update_num, patch_num, port_update_num);
1649 #else
1650 	memcpy(version, "Unknown", sizeof("Unknown"));
1651 #endif
1652 }
1653 /* }}} */
1654 
1655 /* {{{ php_oci_server_get_version()
1656  *
1657  * Get Oracle server version
1658  */
php_oci_server_get_version(php_oci_connection * connection,char * version,size_t version_size)1659 int php_oci_server_get_version(php_oci_connection *connection, char *version, size_t version_size)
1660 {
1661 	sword errstatus;
1662 
1663 	PHP_OCI_CALL_RETURN(errstatus, OCIServerVersion, (connection->svc, connection->err, (text *)version, (ub4) version_size, OCI_HTYPE_SVCCTX));
1664 
1665 	if (errstatus != OCI_SUCCESS) {
1666 		connection->errcode = php_oci_error(connection->err, errstatus);
1667 		PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
1668 		return 1;
1669 	}
1670 
1671 	return 0;
1672 }
1673 /* }}} */
1674 
1675 /* {{{ php_oci_column_to_zval()
1676  *
1677  * Convert php_oci_out_column struct into zval
1678  */
php_oci_column_to_zval(php_oci_out_column * column,zval * value,int mode)1679 int php_oci_column_to_zval(php_oci_out_column *column, zval *value, int mode)
1680 {
1681 	php_oci_descriptor *descriptor;
1682 	ub4 lob_length;
1683 	int column_size;
1684 	char *lob_buffer = (char *)0;
1685 	int lob_fetch_status;
1686 
1687 	if (column->indicator == -1) { /* column is NULL */
1688 		ZVAL_NULL(value);
1689 		return 0;
1690 	}
1691 
1692 	if (column->is_cursor) { /* REFCURSOR -> simply return the statement id */
1693 		ZVAL_RES(value, column->stmtid);
1694 		GC_ADDREF(column->stmtid);
1695 	} else if (column->is_descr) {
1696 
1697 		if (column->data_type != SQLT_RDD) {
1698 
1699 			/* reset descriptor's length */
1700 			descriptor = (php_oci_descriptor *) column->descid->ptr;
1701 
1702 			if (!descriptor) {
1703 				php_error_docref(NULL, E_WARNING, "Unable to find LOB descriptor #%d", column->descid->handle);
1704 				return 1;
1705 			}
1706 
1707 			descriptor->lob_size = -1;
1708 			descriptor->lob_current_position = 0;
1709 			descriptor->buffering = 0;
1710 		}
1711 
1712 		if (column->data_type != SQLT_RDD && (mode & PHP_OCI_RETURN_LOBS)) {
1713 			/* PHP_OCI_RETURN_LOBS means that we want the content of the LOB back instead of the locator */
1714 
1715 			if (column->chunk_size)
1716 				descriptor->chunk_size = column->chunk_size;
1717 			lob_fetch_status = php_oci_lob_read(descriptor, -1, 0, &lob_buffer, &lob_length);
1718 			if (descriptor->chunk_size)  /* Cache the chunk_size to avoid recalling OCILobGetChunkSize */
1719 				column->chunk_size = descriptor->chunk_size;
1720 			php_oci_temp_lob_close(descriptor);
1721 			if (lob_fetch_status) {
1722 				ZVAL_FALSE(value);
1723 				return 1;
1724 			} else {
1725 				if (lob_length > 0) {
1726 					ZVAL_STRINGL(value, lob_buffer, lob_length);
1727 				} else {
1728 					ZVAL_EMPTY_STRING(value);
1729 				}
1730 				if (lob_buffer)
1731 					efree(lob_buffer);
1732 				return 0;
1733 			}
1734 		} else {
1735 			/* return the locator */
1736 			object_init_ex(value, oci_lob_class_entry_ptr);
1737 			add_property_resource(value, "descriptor", column->descid);
1738 			GC_ADDREF(column->descid);
1739 		}
1740 	} else {
1741 		switch (column->retcode) {
1742 			case 0:
1743 				/* intact value */
1744 				if (column->piecewise) {
1745 					column_size = column->retlen4;
1746 				} else {
1747 					column_size = column->retlen;
1748 				}
1749 				break;
1750 
1751 			default:
1752 				ZVAL_FALSE(value);
1753 				return 0;
1754 		}
1755 
1756 		ZVAL_STRINGL(value, column->data, column_size);
1757 	}
1758 	return 0;
1759 }
1760 /* }}} */
1761 
1762 
1763 /* {{{ php_oci_fetch_row()
1764  *
1765  * Fetch the next row from the given statement
1766  * Has logic for Oracle 12c Implicit Result Sets
1767  */
php_oci_fetch_row(INTERNAL_FUNCTION_PARAMETERS,int mode,int expected_args)1768 void php_oci_fetch_row (INTERNAL_FUNCTION_PARAMETERS, int mode, int expected_args)
1769 {
1770 	zval *z_statement, *array;
1771 	zval *placeholder = (zval*) NULL;
1772 /*	zend_array *temp_array = (zend_array *) NULL;*/
1773 	php_oci_statement *statement;		  /* statement that will be fetched from */
1774 #if (OCI_MAJOR_VERSION >= 12)
1775 	php_oci_statement *invokedstatement;  /* statement this function was invoked with */
1776 #endif /* OCI_MAJOR_VERSION */
1777 	php_oci_out_column *column;
1778 	ub4 nrows = 1;
1779 	int i;
1780 	zend_long fetch_mode = 0;
1781 
1782 	if (expected_args > 2) {
1783 		/* only for ocifetchinto BC */
1784 
1785 		ZEND_PARSE_PARAMETERS_START(2, 3)
1786 			Z_PARAM_RESOURCE(z_statement)
1787 			Z_PARAM_ZVAL(array)
1788 			Z_PARAM_OPTIONAL
1789 			Z_PARAM_LONG(fetch_mode)
1790 		ZEND_PARSE_PARAMETERS_END();
1791 
1792 		if (ZEND_NUM_ARGS() == 2) {
1793 			fetch_mode = mode;
1794 		}
1795 
1796 		if (Z_ISREF_P(array))
1797 			placeholder = Z_REFVAL_P(array);
1798 		else
1799 			placeholder = array;
1800 
1801 	} else if (expected_args == 2) {
1802 		/* only for oci_fetch_array() */
1803 
1804 		ZEND_PARSE_PARAMETERS_START(1, 2)
1805 			Z_PARAM_RESOURCE(z_statement)
1806 			Z_PARAM_OPTIONAL
1807 			Z_PARAM_LONG(fetch_mode)
1808 		ZEND_PARSE_PARAMETERS_END();
1809 
1810 		if (ZEND_NUM_ARGS() == 1) {
1811 			fetch_mode = mode;
1812 		}
1813 	} else {
1814 		/* for all oci_fetch_*() */
1815 
1816 		ZEND_PARSE_PARAMETERS_START(1, 1)
1817 			Z_PARAM_RESOURCE(z_statement)
1818 		ZEND_PARSE_PARAMETERS_END();
1819 
1820 		fetch_mode = mode;
1821 	}
1822 
1823 	if (!(fetch_mode & PHP_OCI_NUM) && !(fetch_mode & PHP_OCI_ASSOC)) {
1824 		/* none of the modes present, use the default one */
1825 		if (mode & PHP_OCI_ASSOC) {
1826 			fetch_mode |= PHP_OCI_ASSOC;
1827 		}
1828 		if (mode & PHP_OCI_NUM) {
1829 			fetch_mode |= PHP_OCI_NUM;
1830 		}
1831 	}
1832 
1833 #if (OCI_MAJOR_VERSION < 12)
1834 	PHP_OCI_ZVAL_TO_STATEMENT(z_statement, statement);
1835 
1836 	if (php_oci_statement_fetch(statement, nrows)) {
1837 		RETURN_FALSE;			/* end of fetch */
1838 	}
1839 #else /* OCI_MAJOR_VERSION */
1840 	PHP_OCI_ZVAL_TO_STATEMENT(z_statement, invokedstatement);
1841 
1842 	if (invokedstatement->impres_flag == PHP_OCI_IMPRES_NO_CHILDREN ||
1843         invokedstatement->impres_flag == PHP_OCI_IMPRES_IS_CHILD) {
1844 		/* Already know there are no Implicit Result Sets */
1845 	    statement = invokedstatement;
1846 	} else if (invokedstatement->impres_flag == PHP_OCI_IMPRES_HAS_CHILDREN) {
1847 		/* Previously saw an Implicit Result Set in an earlier invocation of php_oci_fetch_row */
1848 		statement = (php_oci_statement *)invokedstatement->impres_child_stmt;
1849 	} else {
1850 		sword errstatus;
1851 
1852 		/* Check for an Implicit Result Set on this statement handle */
1853 		PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)invokedstatement->stmt, OCI_HTYPE_STMT,
1854 						    (dvoid *) &invokedstatement->impres_count,
1855 						    (ub4 *)NULL, OCI_ATTR_IMPLICIT_RESULT_COUNT, invokedstatement->err));
1856 		if (errstatus) {
1857 			RETURN_FALSE;
1858 		}
1859 		if (invokedstatement->impres_count > 0) {
1860 			/* Make it so the fetch occurs on the first Implicit Result Set */
1861 			statement = php_oci_get_implicit_resultset(invokedstatement);
1862 			if (!statement || php_oci_statement_execute(statement, (ub4)OCI_DEFAULT))
1863 				RETURN_FALSE;
1864 			invokedstatement->impres_count--;
1865 			invokedstatement->impres_child_stmt = (struct php_oci_statement *)statement;
1866 			invokedstatement->impres_flag = PHP_OCI_IMPRES_HAS_CHILDREN;
1867 		} else {
1868 			statement = invokedstatement; /* didn't find Implicit Result Sets */
1869 			invokedstatement->impres_flag = PHP_OCI_IMPRES_NO_CHILDREN;  /* Don't bother checking again */
1870 		}
1871 	}
1872 
1873 	if (php_oci_statement_fetch(statement, nrows)) {
1874 		/* End of fetch */
1875 		if (invokedstatement->impres_count > 0) {
1876 			/* Check next Implicit Result Set */
1877 	        statement = php_oci_get_implicit_resultset(invokedstatement);
1878 			if (!statement || php_oci_statement_execute(statement, (ub4)OCI_DEFAULT))
1879 				RETURN_FALSE;
1880 			invokedstatement->impres_count--;
1881 			invokedstatement->impres_child_stmt = (struct php_oci_statement *)statement;
1882 			if (php_oci_statement_fetch(statement, nrows)) {
1883 				/* End of all fetches */
1884 	            RETURN_FALSE;
1885 			}
1886 		} else {
1887 			RETURN_FALSE;
1888 		}
1889     }
1890 #endif /* OCI_MAJOR_VERSION */
1891 
1892 	if (placeholder == NULL) {
1893 		placeholder = return_value;
1894 	} else {
1895 		zval_ptr_dtor(placeholder);
1896 	}
1897 
1898 	array_init(placeholder);
1899 
1900 	for (i = 0; i < statement->ncolumns; i++) {
1901 
1902 		column = php_oci_statement_get_column(statement, i + 1, NULL, 0);
1903 
1904 		if (column == NULL) {
1905 			continue;
1906 		}
1907 		if ((column->indicator == -1) && ((fetch_mode & PHP_OCI_RETURN_NULLS) == 0)) {
1908 			continue;
1909 		}
1910 
1911 		if (!(column->indicator == -1)) {
1912 			zval element;
1913 
1914 			php_oci_column_to_zval(column, &element, (int) fetch_mode);
1915 
1916 			if (fetch_mode & PHP_OCI_NUM || !(fetch_mode & PHP_OCI_ASSOC)) {
1917 				add_index_zval(placeholder, i, &element);
1918 			}
1919 			if (fetch_mode & PHP_OCI_ASSOC) {
1920 				if (fetch_mode & PHP_OCI_NUM) {
1921 					Z_TRY_ADDREF_P(&element);
1922 				}
1923 				add_assoc_zval(placeholder, column->name, &element);
1924 			}
1925 
1926 		} else {
1927 			if (fetch_mode & PHP_OCI_NUM || !(fetch_mode & PHP_OCI_ASSOC)) {
1928 				add_index_null(placeholder, i);
1929 			}
1930 			if (fetch_mode & PHP_OCI_ASSOC) {
1931 				add_assoc_null(placeholder, column->name);
1932 			}
1933 		}
1934 	}
1935 
1936 	if (expected_args > 2) {
1937 		RETURN_LONG(statement->ncolumns);
1938 	}
1939 }
1940 /* }}} */
1941 
1942 /* {{{ php_oci_persistent_helper()
1943  *
1944  * Helper function to close/rollback persistent connections at the end of request. A return value of
1945  * 1 indicates that the connection is to be destroyed
1946  */
php_oci_persistent_helper(zval * zv)1947 static int php_oci_persistent_helper(zval *zv)
1948 {
1949 	zend_resource *le = Z_RES_P(zv);
1950 	time_t timestamp;
1951 	php_oci_connection *connection;
1952 
1953 	timestamp = time(NULL);
1954 
1955 	/* Persistent connection stubs are also counted as they have private session pools */
1956 	if (le->type == le_pconnection) {
1957 		connection = (php_oci_connection *)le->ptr;
1958 
1959 		/* Remove TAF callback function as it's bound to current request */
1960 		if (connection->used_this_request && !Z_ISUNDEF(connection->taf_callback) && !Z_ISNULL(connection->taf_callback)) {
1961 			php_oci_unregister_taf_callback(connection);
1962 		}
1963 
1964 		if (!connection->used_this_request && OCI_G(persistent_timeout) != -1) {
1965 #ifdef HAVE_OCI8_DTRACE
1966 			if (DTRACE_OCI8_CONNECT_EXPIRY_ENABLED()) {
1967 				DTRACE_OCI8_CONNECT_EXPIRY(connection, connection->is_stub ? 1 : 0, (long)connection->idle_expiry, (long)timestamp);
1968 			}
1969 #endif /* HAVE_OCI8_DTRACE */
1970 			if (connection->idle_expiry < timestamp) {
1971 				/* connection has timed out */
1972 				return ZEND_HASH_APPLY_REMOVE;
1973 			}
1974 		}
1975 	}
1976 	return ZEND_HASH_APPLY_KEEP;
1977 }
1978 /* }}} */
1979 
1980 /* {{{ php_oci_create_spool()
1981  *
1982  *	 Create(alloc + Init) Session pool for the given dbname and charsetid
1983  */
php_oci_create_spool(char * username,int username_len,char * password,int password_len,char * dbname,int dbname_len,zend_string * hash_key,int charsetid)1984 static php_oci_spool *php_oci_create_spool(char *username, int username_len, char *password, int password_len, char *dbname, int dbname_len, zend_string *hash_key, int charsetid)
1985 {
1986 	php_oci_spool *session_pool = NULL;
1987 	zend_bool iserror = 0;
1988 	ub4 poolmode = OCI_DEFAULT;	/* Mode to be passed to OCISessionPoolCreate */
1989 	OCIAuthInfo *spoolAuth = NULL;
1990 	sword errstatus;
1991 
1992 	/* Allocate sessionpool out of persistent memory */
1993 	session_pool = (php_oci_spool *) calloc(1, sizeof(php_oci_spool));
1994 	if (session_pool == NULL) {
1995 		iserror = 1;
1996 		goto exit_create_spool;
1997 	}
1998 
1999 	/* Populate key if passed */
2000 	if (hash_key && (ZSTR_LEN(hash_key) > 0)) {
2001 		session_pool->spool_hash_key = zend_string_dup(hash_key, 1);
2002 		if (session_pool->spool_hash_key == NULL) {
2003 			iserror = 1;
2004 			goto exit_create_spool;
2005 		}
2006 	}
2007 
2008 	/* Create the session pool's env */
2009 	if (!(session_pool->env = php_oci_create_env(charsetid))) {
2010 		iserror = 1;
2011 		goto exit_create_spool;
2012 	}
2013 
2014 	/* Allocate the pool handle */
2015 	PHP_OCI_CALL_RETURN(errstatus, OCIHandleAlloc, (session_pool->env, (dvoid **) &session_pool->poolh, OCI_HTYPE_SPOOL, (size_t) 0, (dvoid **) 0));
2016 
2017 	if (errstatus != OCI_SUCCESS) {
2018 		OCI_G(errcode) = php_oci_error(OCI_G(err), errstatus);
2019 		iserror = 1;
2020 		goto exit_create_spool;
2021 	}
2022 
2023 	/* Allocate the session pool error handle - This only for use in the destructor, as there is a
2024 	 * generic bug which can free up the OCI_G(err) variable before destroying connections. We
2025 	 * cannot use this for other roundtrip calls as there is no way the user can access this error
2026 	 */
2027 	PHP_OCI_CALL_RETURN(errstatus, OCIHandleAlloc, ((dvoid *) session_pool->env, (dvoid **)&(session_pool->err), (ub4) OCI_HTYPE_ERROR,(size_t) 0, (dvoid **) 0));
2028 
2029 	if (errstatus != OCI_SUCCESS) {
2030 		OCI_G(errcode) = php_oci_error(OCI_G(err), errstatus);
2031 		iserror = 1;
2032 		goto exit_create_spool;
2033 	}
2034 
2035 /* Disable RLB as we mostly have single-connection pools */
2036 #if (OCI_MAJOR_VERSION > 10)
2037 	poolmode = OCI_SPC_NO_RLB | OCI_SPC_HOMOGENEOUS;
2038 #else
2039 	poolmode = OCI_SPC_HOMOGENEOUS;
2040 #endif
2041 
2042 #if ((OCI_MAJOR_VERSION > 11) || ((OCI_MAJOR_VERSION == 11) && (OCI_MINOR_VERSION >= 2)))
2043 	/* {{{ Allocate auth handle for session pool */
2044 	PHP_OCI_CALL_RETURN(errstatus, OCIHandleAlloc, (session_pool->env, (dvoid **)&(spoolAuth), OCI_HTYPE_AUTHINFO, 0, NULL));
2045 
2046 	if (errstatus != OCI_SUCCESS) {
2047 		OCI_G(errcode) = php_oci_error(OCI_G(err), errstatus);
2048 		iserror = 1;
2049 		goto exit_create_spool;
2050  	}
2051 	/* }}} */
2052 
2053 	/* {{{ Set the edition attribute on the auth handle */
2054 	if (OCI_G(edition)) {
2055 		PHP_OCI_CALL_RETURN(errstatus, OCIAttrSet, ((dvoid *) spoolAuth, (ub4) OCI_HTYPE_AUTHINFO, (dvoid *) OCI_G(edition), (ub4)(strlen(OCI_G(edition))), (ub4)OCI_ATTR_EDITION, OCI_G(err)));
2056 
2057 		if (errstatus != OCI_SUCCESS) {
2058 			OCI_G(errcode) = php_oci_error(OCI_G(err), errstatus);
2059 			iserror = 1;
2060 			goto exit_create_spool;
2061 		}
2062 	}
2063 	/* }}} */
2064 
2065 	/* {{{ Set the driver name attribute on the auth handle */
2066 	PHP_OCI_CALL_RETURN(errstatus, OCIAttrSet, ((dvoid *) spoolAuth, (ub4) OCI_HTYPE_AUTHINFO, (dvoid *) PHP_OCI8_DRIVER_NAME, (ub4) sizeof(PHP_OCI8_DRIVER_NAME)-1, (ub4) OCI_ATTR_DRIVER_NAME, OCI_G(err)));
2067 
2068 	if (errstatus != OCI_SUCCESS) {
2069 		OCI_G(errcode) = php_oci_error(OCI_G(err), errstatus);
2070 		iserror = 1;
2071 		goto exit_create_spool;
2072 	}
2073 	/* }}} */
2074 
2075 	/* {{{ Set the auth handle on the session pool */
2076 	PHP_OCI_CALL_RETURN(errstatus, OCIAttrSet, ((dvoid *) (session_pool->poolh),(ub4) OCI_HTYPE_SPOOL, (dvoid *) spoolAuth, (ub4)0, (ub4)OCI_ATTR_SPOOL_AUTH, OCI_G(err)));
2077 
2078 	if (errstatus != OCI_SUCCESS) {
2079 		OCI_G(errcode) = php_oci_error(OCI_G(err), errstatus);
2080 		iserror = 1;
2081 		goto exit_create_spool;
2082 	}
2083 	/* }}} */
2084 #endif
2085 
2086 	/* Create the homogeneous session pool - We have different session pools for every different
2087 	 * username, password, charset and dbname.
2088 	 */
2089 	PHP_OCI_CALL_RETURN(errstatus, OCISessionPoolCreate,(session_pool->env, OCI_G(err), session_pool->poolh, (OraText **)&session_pool->poolname, &session_pool->poolname_len, (OraText *)dbname, (ub4)dbname_len, 0, UB4MAXVAL, 1,(OraText *)username, (ub4)username_len, (OraText *)password,(ub4)password_len, poolmode));
2090 
2091 	if (errstatus != OCI_SUCCESS) {
2092 		OCI_G(errcode) = php_oci_error(OCI_G(err), errstatus);
2093 		iserror = 1;
2094 	}
2095 
2096 exit_create_spool:
2097 	if (iserror && session_pool) {
2098 		php_oci_spool_close(session_pool);
2099 		session_pool = NULL;
2100 	}
2101 
2102 	if (spoolAuth) {
2103 		PHP_OCI_CALL(OCIHandleFree, ((dvoid *) spoolAuth, (ub4) OCI_HTYPE_AUTHINFO));
2104 	}
2105 
2106 #ifdef HAVE_OCI8_DTRACE
2107 	if (DTRACE_OCI8_SESSPOOL_CREATE_ENABLED()) {
2108 		DTRACE_OCI8_SESSPOOL_CREATE(session_pool);
2109 	}
2110 #endif /* HAVE_OCI8_DTRACE */
2111 
2112 	return session_pool;
2113 }
2114 /* }}} */
2115 
2116 /* {{{ php_oci_get_spool()
2117  *
2118  * Get Session pool for the given dbname and charsetid from the persistent list. Function called for
2119  * non-persistent connections.
2120  */
php_oci_get_spool(char * username,int username_len,char * password,int password_len,char * dbname,int dbname_len,int charsetid)2121 static php_oci_spool *php_oci_get_spool(char *username, int username_len, char *password, int password_len, char *dbname, int dbname_len, int charsetid)
2122 {
2123 	smart_str spool_hashed_details = {0};
2124 	php_oci_spool *session_pool = NULL;
2125 	zend_resource *spool_out_le = NULL;
2126 	zend_bool iserror = 0;
2127 	zval *spool_out_zv = NULL;
2128 
2129 	/* {{{ Create the spool hash key */
2130 	smart_str_appendl_ex(&spool_hashed_details, "oci8spool***", sizeof("oci8spool***") - 1, 0);
2131 	smart_str_appendl_ex(&spool_hashed_details, username, username_len, 0);
2132 	smart_str_appendl_ex(&spool_hashed_details, "**", sizeof("**") - 1, 0);
2133 	/* Add edition attribute to the hash */
2134 	if (OCI_G(edition)){
2135 		smart_str_appendl_ex(&spool_hashed_details, OCI_G(edition), strlen(OCI_G(edition)), 0);
2136 	}
2137 	smart_str_appendl_ex(&spool_hashed_details, "**", sizeof("**") - 1, 0);
2138 	if (password_len) {
2139 		zend_ulong password_hash;
2140 		password_hash = zend_hash_func(password, password_len);
2141 		smart_str_append_unsigned_ex(&spool_hashed_details, password_hash, 0);
2142 	}
2143 	smart_str_appendl_ex(&spool_hashed_details, "**", sizeof("**") - 1, 0);
2144 
2145 	if (dbname_len) {
2146 		smart_str_appendl_ex(&spool_hashed_details, dbname, dbname_len, 0);
2147 	}
2148 	smart_str_appendl_ex(&spool_hashed_details, "**", sizeof("**") - 1, 0);
2149 
2150 	smart_str_append_unsigned_ex(&spool_hashed_details, charsetid, 0);
2151 
2152 	/* Session Pool Hash Key : oci8spool***username**edition**hashedpassword**dbname**charset */
2153 
2154 	smart_str_0(&spool_hashed_details);
2155 	php_strtolower(ZSTR_VAL(spool_hashed_details.s), ZSTR_LEN(spool_hashed_details.s));
2156 	/* }}} */
2157 
2158 	spool_out_zv = zend_hash_find(&EG(persistent_list), spool_hashed_details.s);
2159 	if (spool_out_zv != NULL) {
2160 		spool_out_le = Z_RES_P(spool_out_zv);
2161 	}
2162 
2163 	if (spool_out_le == NULL) {
2164 
2165 		session_pool = php_oci_create_spool(username, username_len, password, password_len, dbname, dbname_len, spool_hashed_details.s, charsetid);
2166 
2167 		if (session_pool == NULL) {
2168 			iserror = 1;
2169 			goto exit_get_spool;
2170 		}
2171 		zend_register_persistent_resource_ex(session_pool->spool_hash_key, session_pool, le_psessionpool);
2172 	} else if (spool_out_le->type == le_psessionpool &&
2173 		ZSTR_LEN(((php_oci_spool *)(spool_out_le->ptr))->spool_hash_key) == ZSTR_LEN(spool_hashed_details.s) &&
2174 		memcmp(ZSTR_VAL(((php_oci_spool *)(spool_out_le->ptr))->spool_hash_key), ZSTR_VAL(spool_hashed_details.s), ZSTR_LEN(spool_hashed_details.s)) == 0) {
2175 		/* retrieve the cached session pool */
2176 		session_pool = (php_oci_spool *)(spool_out_le->ptr);
2177 	}
2178 
2179 exit_get_spool:
2180 	smart_str_free(&spool_hashed_details);
2181 	if (iserror && session_pool) {
2182 		php_oci_spool_close(session_pool);
2183 		session_pool = NULL;
2184 	}
2185 
2186 	return session_pool;
2187 
2188 }
2189 /* }}} */
2190 
2191 /* {{{ php_oci_create_env()
2192  *
2193  * Create the OCI environment choosing the correct function for the OCI version
2194  */
php_oci_create_env(ub2 charsetid)2195 static OCIEnv *php_oci_create_env(ub2 charsetid)
2196 {
2197 	OCIEnv *retenv = NULL;
2198 
2199 	/* create an environment using the character set id */
2200 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIEnvNlsCreate, (&retenv, OCI_G(events) ? PHP_OCI_INIT_MODE | OCI_EVENTS : PHP_OCI_INIT_MODE, 0, NULL, NULL, NULL, 0, NULL, charsetid, charsetid));
2201 
2202 	if (OCI_G(errcode) != OCI_SUCCESS) {
2203 		sb4   ora_error_code = 0;
2204 		text  ora_msg_buf[PHP_OCI_ERRBUF_LEN];  /* Use traditional smaller size: non-PL/SQL errors should fit and it keeps the stack smaller */
2205 
2206 #ifdef HAVE_OCI_INSTANT_CLIENT
2207 		php_error_docref(NULL, E_WARNING, "OCIEnvNlsCreate() failed. There is something wrong with your system - please check that " PHP_OCI8_LIB_PATH_MSG " includes the directory with Oracle Instant Client libraries");
2208 #else
2209 		php_error_docref(NULL, E_WARNING, "OCIEnvNlsCreate() failed. There is something wrong with your system - please check that ORACLE_HOME and " PHP_OCI8_LIB_PATH_MSG " are set and point to the right directories");
2210 #endif
2211 		if (retenv
2212 			&& OCIErrorGet(retenv, (ub4)1, NULL, &ora_error_code, ora_msg_buf, (ub4)PHP_OCI_ERRBUF_LEN, (ub4)OCI_HTYPE_ENV) == OCI_SUCCESS
2213 			&& *ora_msg_buf) {
2214 			php_error_docref(NULL, E_WARNING, "%s", ora_msg_buf);
2215 		}
2216 
2217 		return NULL;
2218 	}
2219 	return retenv;
2220 }
2221 /* }}} */
2222 
2223 /* {{{ php_oci_old_create_session()
2224  *
2225  * This function is to be deprecated in future in favour of OCISessionGet which is used in
2226  * php_oci_do_connect_ex
2227  */
php_oci_old_create_session(php_oci_connection * connection,char * dbname,int dbname_len,char * username,int username_len,char * password,int password_len,char * new_password,int new_password_len,int session_mode)2228 static int php_oci_old_create_session(php_oci_connection *connection, char *dbname, int dbname_len, char *username, int username_len, char *password, int password_len, char *new_password, int new_password_len, int session_mode)
2229 {
2230 	ub4 statement_cache_size = 0;
2231 
2232 	if (OCI_G(statement_cache_size) > 0) {
2233 		if (OCI_G(statement_cache_size) > SB4MAXVAL)
2234 			statement_cache_size = (ub4) SB4MAXVAL;
2235 		else
2236 			statement_cache_size = (ub4) OCI_G(statement_cache_size);
2237 	}
2238 
2239 	/* Create the OCI environment separate for each connection */
2240 	if (!(connection->env = php_oci_create_env(connection->charset))) {
2241 		return 1;
2242 	}
2243 
2244 	/* {{{ Allocate our server handle */
2245 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIHandleAlloc, (connection->env, (dvoid **)&(connection->server), OCI_HTYPE_SERVER, 0, NULL));
2246 
2247 	if (OCI_G(errcode) != OCI_SUCCESS) {
2248 		php_oci_error(OCI_G(err), OCI_G(errcode));
2249 		return 1;
2250 	}
2251 	/* }}} */
2252 
2253 	/* {{{ Attach to the server */
2254 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIServerAttach, (connection->server, OCI_G(err), (text *)dbname, dbname_len, (ub4) OCI_DEFAULT));
2255 
2256 	if (OCI_G(errcode) != OCI_SUCCESS) {
2257 		php_oci_error(OCI_G(err), OCI_G(errcode));
2258 		return 1;
2259 	}
2260 	/* }}} */
2261 	connection->is_attached = 1;
2262 
2263 	/* {{{ Allocate our session handle */
2264 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIHandleAlloc, (connection->env, (dvoid **)&(connection->session), OCI_HTYPE_SESSION, 0, NULL));
2265 
2266 	if (OCI_G(errcode) != OCI_SUCCESS) {
2267 		php_oci_error(OCI_G(err), OCI_G(errcode));
2268 		return 1;
2269 	}
2270 	/* }}} */
2271 
2272 	/* {{{ Allocate our private error-handle */
2273 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIHandleAlloc, (connection->env, (dvoid **)&(connection->err), OCI_HTYPE_ERROR, 0, NULL));
2274 
2275 	if (OCI_G(errcode) != OCI_SUCCESS) {
2276 		php_oci_error(OCI_G(err), OCI_G(errcode));
2277 		return 1;
2278 	}
2279 	/* }}} */
2280 
2281 	/* {{{ Allocate our service-context */
2282 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIHandleAlloc, (connection->env, (dvoid **)&(connection->svc), OCI_HTYPE_SVCCTX, 0, NULL));
2283 
2284 	if (OCI_G(errcode) != OCI_SUCCESS) {
2285 		php_oci_error(OCI_G(err), OCI_G(errcode));
2286 		return 1;
2287 	}
2288 	/* }}} */
2289 
2290 	/* {{{ Set the username */
2291 	if (username) {
2292 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->session, (ub4) OCI_HTYPE_SESSION, (dvoid *) username, (ub4) username_len, (ub4) OCI_ATTR_USERNAME, OCI_G(err)));
2293 
2294 		if (OCI_G(errcode) != OCI_SUCCESS) {
2295 			php_oci_error(OCI_G(err), OCI_G(errcode));
2296 			return 1;
2297 		}
2298 	}
2299 	/* }}} */
2300 
2301 	/* {{{ Set the password */
2302 	if (password) {
2303 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->session, (ub4) OCI_HTYPE_SESSION, (dvoid *) password, (ub4) password_len, (ub4) OCI_ATTR_PASSWORD, OCI_G(err)));
2304 
2305 		if (OCI_G(errcode) != OCI_SUCCESS) {
2306 			php_oci_error(OCI_G(err), OCI_G(errcode));
2307 			return 1;
2308 		}
2309 	}
2310 	/* }}} */
2311 
2312 	/* {{{ Set the edition attribute on the session handle */
2313 #if ((OCI_MAJOR_VERSION > 11) || ((OCI_MAJOR_VERSION == 11) && (OCI_MINOR_VERSION >= 2)))
2314 	if (OCI_G(edition)) {
2315 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->session, (ub4) OCI_HTYPE_SESSION, (dvoid *) OCI_G(edition), (ub4) (strlen(OCI_G(edition))), (ub4) OCI_ATTR_EDITION, OCI_G(err)));
2316 
2317 		if (OCI_G(errcode) != OCI_SUCCESS) {
2318 			php_oci_error(OCI_G(err), OCI_G(errcode));
2319 			return 1;
2320 		}
2321 	}
2322 #endif
2323 /* }}} */
2324 
2325 	/* {{{ Set the driver name attribute on the session handle */
2326 #if (OCI_MAJOR_VERSION >= 11)
2327 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->session, (ub4) OCI_HTYPE_SESSION, (dvoid *) PHP_OCI8_DRIVER_NAME, (ub4) sizeof(PHP_OCI8_DRIVER_NAME)-1, (ub4) OCI_ATTR_DRIVER_NAME, OCI_G(err)));
2328 
2329 	if (OCI_G(errcode) != OCI_SUCCESS) {
2330 		php_oci_error(OCI_G(err), OCI_G(errcode));
2331 		return 1;
2332 	}
2333 #endif
2334 /* }}} */
2335 
2336 	/* {{{ Set the server handle in the service handle */
2337 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, (connection->svc, OCI_HTYPE_SVCCTX, connection->server, 0, OCI_ATTR_SERVER, OCI_G(err)));
2338 
2339 	if (OCI_G(errcode) != OCI_SUCCESS) {
2340 		php_oci_error(OCI_G(err), OCI_G(errcode));
2341 		return 1;
2342 	}
2343 	/* }}} */
2344 
2345 	/* {{{ Set the authentication handle in the service handle */
2346 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, (connection->svc, OCI_HTYPE_SVCCTX, connection->session, 0, OCI_ATTR_SESSION, OCI_G(err)));
2347 
2348 	if (OCI_G(errcode) != OCI_SUCCESS) {
2349 		php_oci_error(OCI_G(err), OCI_G(errcode));
2350 		return 1;
2351 	}
2352 	/* }}} */
2353 
2354 	if (new_password) {
2355 		/* {{{ Try to change password if new one was provided */
2356 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIPasswordChange, (connection->svc, OCI_G(err), (text *)username, username_len, (text *)password, password_len, (text *)new_password, new_password_len, OCI_AUTH));
2357 
2358 		if (OCI_G(errcode) != OCI_SUCCESS) {
2359 			php_oci_error(OCI_G(err), OCI_G(errcode));
2360 			return 1;
2361 		}
2362 
2363 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrGet, ((dvoid *)connection->svc, OCI_HTYPE_SVCCTX, (dvoid *)&(connection->session), (ub4 *)0, OCI_ATTR_SESSION, OCI_G(err)));
2364 
2365 		if (OCI_G(errcode) != OCI_SUCCESS) {
2366 			php_oci_error(OCI_G(err), OCI_G(errcode));
2367 			return 1;
2368 		}
2369 		/* }}} */
2370 	} else {
2371 		/* {{{ start the session */
2372 		ub4 cred_type = OCI_CRED_RDBMS;
2373 
2374 		/* Extract the overloaded session_mode parameter into valid Oracle credential and session mode values */
2375 		if (session_mode & PHP_OCI_CRED_EXT) {
2376 			cred_type = OCI_CRED_EXT;
2377 			session_mode ^= PHP_OCI_CRED_EXT;
2378 		}
2379 
2380 		session_mode |= OCI_STMT_CACHE;
2381 
2382 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCISessionBegin, (connection->svc, OCI_G(err), connection->session, (ub4) cred_type, (ub4) session_mode));
2383 
2384 		if (OCI_G(errcode) != OCI_SUCCESS) {
2385 			php_oci_error(OCI_G(err), OCI_G(errcode));
2386 			/* OCISessionBegin returns OCI_SUCCESS_WITH_INFO when
2387 			 * user's password has expired, but is still usable.
2388 			 */
2389 			if (OCI_G(errcode) != OCI_SUCCESS_WITH_INFO) {
2390 				return 1;
2391 			}
2392 		}
2393 		/* }}} */
2394 	}
2395 
2396 	/* Brand new connection: Init and update the next_ping in the connection */
2397 	if (php_oci_ping_init(connection, OCI_G(err)) != OCI_SUCCESS) {
2398 		php_oci_error(OCI_G(err), OCI_G(errcode));
2399 		return 1;
2400 	}
2401 
2402 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->svc, (ub4) OCI_HTYPE_SVCCTX, (ub4 *) &statement_cache_size, 0, (ub4) OCI_ATTR_STMTCACHESIZE, OCI_G(err)));
2403 
2404 	if (OCI_G(errcode) != OCI_SUCCESS) {
2405 		php_oci_error(OCI_G(err), OCI_G(errcode));
2406 		return 1;
2407 	}
2408 
2409 	/* Successfully created session */
2410 	return 0;
2411 }
2412 /* }}} */
2413 
2414 /* {{{ php_oci_create_session()
2415  *
2416  * Create session using client-side session pool - new norm
2417  */
php_oci_create_session(php_oci_connection * connection,php_oci_spool * session_pool,char * dbname,int dbname_len,char * username,int username_len,char * password,int password_len,char * new_password,int new_password_len,int session_mode)2418 static int php_oci_create_session(php_oci_connection *connection, php_oci_spool *session_pool, char *dbname, int dbname_len, char *username, int username_len, char *password, int password_len, char *new_password, int new_password_len, int session_mode)
2419 {
2420 	php_oci_spool *actual_spool = NULL;
2421 #if (OCI_MAJOR_VERSION > 10)
2422 	ub4 purity = -2;				/* Illegal value to initialize */
2423 #endif
2424 	time_t timestamp = time(NULL);
2425 	ub4 statement_cache_size = 0;
2426 
2427 	if (OCI_G(statement_cache_size) > 0) {
2428 		if (OCI_G(statement_cache_size) > SB4MAXVAL)
2429 			statement_cache_size = (ub4) SB4MAXVAL;
2430 		else
2431 			statement_cache_size = (ub4) OCI_G(statement_cache_size);
2432 	}
2433 
2434 	/* Persistent connections have private session pools */
2435 	if (connection->is_persistent && !connection->private_spool &&
2436 		!(connection->private_spool = php_oci_create_spool(username, username_len, password, password_len, dbname, dbname_len, NULL, connection->charset))) {
2437 			return 1;
2438 	}
2439 	actual_spool = (connection->is_persistent) ? (connection->private_spool) : (session_pool);
2440 
2441 	connection->env = actual_spool->env;
2442 
2443 	/* Do this upfront so that connection close on an error would know that this is a session pool
2444 	 * connection. Failure to do this would result in crashes in error scenarios
2445 	 */
2446 	if (!connection->using_spool) {
2447 		connection->using_spool = 1;
2448 	}
2449 
2450 #ifdef HAVE_OCI8_DTRACE
2451 	if (DTRACE_OCI8_SESSPOOL_TYPE_ENABLED()) {
2452 		DTRACE_OCI8_SESSPOOL_TYPE(session_pool ? 1 : 0, session_pool ? session_pool : connection->private_spool);
2453 	}
2454 #endif /* HAVE_OCI8_DTRACE */
2455 
2456 	/* The passed in "connection" can be a cached stub from plist or freshly created. In the former
2457 	 * case, we do not have to allocate any handles
2458 	 */
2459 
2460 	if (!connection->err) {
2461 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIHandleAlloc, (connection->env, (dvoid **)&(connection->err), OCI_HTYPE_ERROR, 0, NULL));
2462 
2463 		if (OCI_G(errcode) != OCI_SUCCESS) {
2464 			php_oci_error(OCI_G(err), OCI_G(errcode));
2465 			return 1;
2466 		}
2467 	}
2468 
2469 	/* {{{ Allocate and initialize the connection-private authinfo handle if not allocated yet */
2470 	if (!connection->authinfo) {
2471 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIHandleAlloc, (connection->env, (dvoid **)&(connection->authinfo), OCI_HTYPE_AUTHINFO, 0, NULL));
2472 
2473 		if (OCI_G(errcode) != OCI_SUCCESS) {
2474 			php_oci_error(OCI_G(err), OCI_G(errcode));
2475 			return 1;
2476 		}
2477 
2478 		/* Set the Connection class and purity if OCI client version >= 11g */
2479 #if (OCI_MAJOR_VERSION > 10)
2480 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->authinfo,(ub4) OCI_HTYPE_SESSION, (dvoid *) OCI_G(connection_class), (ub4)(strlen(OCI_G(connection_class))), (ub4)OCI_ATTR_CONNECTION_CLASS, OCI_G(err)));
2481 
2482 		if (OCI_G(errcode) != OCI_SUCCESS) {
2483 			php_oci_error(OCI_G(err), OCI_G(errcode));
2484 			return 1;
2485 		}
2486 
2487 		if (connection->is_persistent)
2488 			purity = OCI_ATTR_PURITY_SELF;
2489 		else
2490 			purity = OCI_ATTR_PURITY_NEW;
2491 
2492 		PHP_OCI_CALL_RETURN(OCI_G(errcode),OCIAttrSet, ((dvoid *) connection->authinfo,(ub4) OCI_HTYPE_AUTHINFO, (dvoid *) &purity, (ub4)0, (ub4)OCI_ATTR_PURITY, OCI_G(err)));
2493 
2494 		if (OCI_G(errcode) != OCI_SUCCESS) {
2495 			php_oci_error(OCI_G(err), OCI_G(errcode));
2496 			return 1;
2497 		}
2498 #endif
2499 	}
2500 	/* }}} */
2501 
2502 	/* {{{ Debug statements */
2503 #ifdef HAVE_OCI8_DTRACE
2504 	if (DTRACE_OCI8_SESSPOOL_STATS_ENABLED()) {
2505 		ub4 numfree = 0, numbusy = 0, numopen = 0;
2506 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrGet, ((dvoid *)actual_spool->poolh, OCI_HTYPE_SPOOL, (dvoid *)&numopen, (ub4 *)0, OCI_ATTR_SPOOL_OPEN_COUNT, OCI_G(err)));
2507 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrGet, ((dvoid *)actual_spool->poolh, OCI_HTYPE_SPOOL, (dvoid *)&numbusy, (ub4 *)0, OCI_ATTR_SPOOL_BUSY_COUNT, OCI_G(err)));
2508 		numfree = numopen - numbusy;	/* number of free connections in the pool */
2509 		DTRACE_OCI8_SESSPOOL_STATS(numfree, numbusy, numopen);
2510 	}
2511 #endif /* HAVE_OCI8_DTRACE */
2512 	/* }}} */
2513 
2514 		/* Ping loop: Ping and loop till we get a good connection. When a database instance goes
2515 		 * down, it can leave several bad connections that need to be flushed out before getting a
2516 		 * good one. In non-RAC, we always get a brand new connection at the end of the loop and in
2517 		 * RAC, we can get a good connection from a different instance before flushing out all bad
2518 		 * ones. We do not need to ping brand new connections.
2519 		 */
2520 	do {
2521 		/* Continue to use the global error handle as the connection is closed when an error occurs */
2522 		PHP_OCI_CALL_RETURN(OCI_G(errcode),OCISessionGet, (connection->env, OCI_G(err), &(connection->svc), (OCIAuthInfo *)connection->authinfo, (OraText *)actual_spool->poolname, (ub4)actual_spool->poolname_len, NULL, 0, NULL, NULL, NULL, OCI_SESSGET_SPOOL));
2523 
2524 		if (OCI_G(errcode) != OCI_SUCCESS) {
2525 			php_oci_error(OCI_G(err), OCI_G(errcode));
2526 
2527 			/* Session creation returns OCI_SUCCESS_WITH_INFO when user's password has expired, but
2528 			 * is still usable.
2529 			 */
2530 
2531 			if (OCI_G(errcode) != OCI_SUCCESS_WITH_INFO) {
2532 				return 1;
2533 			}
2534 		}
2535 
2536 		/* {{{ Populate the session and server fields of the connection */
2537 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrGet, ((dvoid *)connection->svc, OCI_HTYPE_SVCCTX, (dvoid *)&(connection->server), (ub4 *)0, OCI_ATTR_SERVER, OCI_G(err)));
2538 
2539 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrGet, ((dvoid *)connection->svc, OCI_HTYPE_SVCCTX, (dvoid *)&(connection->session), (ub4 *)0, OCI_ATTR_SESSION, OCI_G(err)));
2540 		/* }}} */
2541 
2542 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIContextGetValue, (connection->session, OCI_G(err), (ub1 *)"NEXT_PING", (ub1)sizeof("NEXT_PING"), (void **)&(connection->next_pingp)));
2543 		if (OCI_G(errcode) != OCI_SUCCESS) {
2544 			php_oci_error(OCI_G(err), OCI_G(errcode));
2545 			return 1;
2546 		}
2547 
2548 		if (!(connection->next_pingp)){
2549 			/* This is a brand new connection, we need not ping, but have to initialize ping */
2550 			if (php_oci_ping_init(connection, OCI_G(err)) != OCI_SUCCESS) {
2551 				php_oci_error(OCI_G(err), OCI_G(errcode));
2552 				return 1;
2553 			}
2554 		} else if ((*(connection->next_pingp) > 0) && (timestamp >= *(connection->next_pingp))) {
2555 			if (php_oci_connection_ping(connection)) {
2556 				/* Got a good connection - update next_ping and get out of ping loop */
2557 				*(connection->next_pingp) = timestamp + OCI_G(ping_interval);
2558 			} else {
2559 				/* Bad connection - remove from pool */
2560 				PHP_OCI_CALL(OCISessionRelease, (connection->svc, connection->err, NULL,0, (ub4) OCI_SESSRLS_DROPSESS));
2561 				connection->svc = NULL;
2562 				connection->server = NULL;
2563 				connection->session = NULL;
2564 			}
2565 		}	/* If ping applicable */
2566 	} while (!(connection->svc));
2567 
2568 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->svc, (ub4) OCI_HTYPE_SVCCTX, (ub4 *) &statement_cache_size, 0, (ub4) OCI_ATTR_STMTCACHESIZE, OCI_G(err)));
2569 
2570 	if (OCI_G(errcode) != OCI_SUCCESS) {
2571 		php_oci_error(OCI_G(err), OCI_G(errcode));
2572 		return 1;
2573 	}
2574 
2575 	/* Session is now taken from the session pool and attached and open */
2576 	connection->is_stub = 0;
2577 	connection->is_attached = connection->is_open = 1;
2578 
2579 	return 0;
2580 }
2581 /* }}} */
2582 
2583 /* {{{ php_oci_spool_list_dtor()
2584  *
2585  * Session pool destructor function
2586  */
php_oci_spool_list_dtor(zend_resource * entry)2587 static void php_oci_spool_list_dtor(zend_resource *entry)
2588 {
2589 	php_oci_spool *session_pool = (php_oci_spool *)entry->ptr;
2590 
2591 	if (session_pool) {
2592 		php_oci_spool_close(session_pool);
2593 	}
2594 
2595 	return;
2596 }
2597 /* }}} */
2598 
2599 /* {{{	php_oci_spool_close()
2600  *
2601  * Destroys the OCI Session Pool
2602  */
php_oci_spool_close(php_oci_spool * session_pool)2603 static void php_oci_spool_close(php_oci_spool *session_pool)
2604 {
2605 	if (session_pool->poolname_len) {
2606 		PHP_OCI_CALL(OCISessionPoolDestroy, ((dvoid *) session_pool->poolh,
2607 			(dvoid *) session_pool->err, OCI_SPD_FORCE));
2608 	}
2609 
2610 	if (session_pool->poolh) {
2611 		PHP_OCI_CALL(OCIHandleFree, ((dvoid *) session_pool->poolh, OCI_HTYPE_SPOOL));
2612 	}
2613 
2614 	if (session_pool->err) {
2615 		PHP_OCI_CALL(OCIHandleFree, ((dvoid *) session_pool->err, OCI_HTYPE_ERROR));
2616 	}
2617 
2618 	if (session_pool->env) {
2619 		PHP_OCI_CALL(OCIHandleFree, ((dvoid *) session_pool->env, OCI_HTYPE_ENV));
2620 	}
2621 
2622 	if (session_pool->spool_hash_key) {
2623 		free(session_pool->spool_hash_key);
2624 	}
2625 
2626 	free(session_pool);
2627 }
2628 /* }}} */
2629 
2630 /* {{{ php_oci_ping_init()
2631  *
2632  * Initializes the next_ping time as a context value in the connection.	 We now use
2633  * OCIContext{Get,Set}Value to store the next_ping because we need to support ping for
2634  * non-persistent DRCP connections
2635  */
php_oci_ping_init(php_oci_connection * connection,OCIError * errh)2636 static sword php_oci_ping_init(php_oci_connection *connection, OCIError *errh)
2637 {
2638 	time_t *next_pingp = NULL;
2639 
2640 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIContextGetValue, (connection->session, errh, (ub1 *)"NEXT_PING", (ub1)sizeof("NEXT_PING"), (void **)&next_pingp));
2641 	if (OCI_G(errcode) != OCI_SUCCESS) {
2642 		return OCI_G(errcode);
2643 	}
2644 
2645 	/* This must be a brand-new connection. Allocate memory for the ping */
2646 	if (!next_pingp) {
2647 		PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIMemoryAlloc, (connection->session, errh, (void **)&next_pingp, OCI_DURATION_SESSION, sizeof(time_t), OCI_MEMORY_CLEARED));
2648 		if (OCI_G(errcode) != OCI_SUCCESS) {
2649 			return OCI_G(errcode);
2650 		}
2651 	}
2652 
2653 	if (OCI_G(ping_interval) >= 0) {
2654 		time_t timestamp = time(NULL);
2655 		*next_pingp = timestamp + OCI_G(ping_interval);
2656 	} else {
2657 		*next_pingp = 0;
2658 	}
2659 
2660 	/* Set the new ping value into the connection */
2661 	PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIContextSetValue, (connection->session, errh, OCI_DURATION_SESSION, (ub1 *)"NEXT_PING", (ub1)sizeof("NEXT_PING"), next_pingp));
2662 	if (OCI_G(errcode) != OCI_SUCCESS) {
2663 		OCIMemoryFree(connection->session, errh, next_pingp);
2664 		return OCI_G(errcode);
2665 	}
2666 
2667 	/* Cache the pointer so we do not have to do OCIContextGetValue repeatedly */
2668 	connection->next_pingp = next_pingp;
2669 
2670 	return OCI_SUCCESS;
2671 }
2672 /* }}} */
2673 
2674 /* {{{ php_oci_dtrace_check_connection()
2675  *
2676  * DTrace output for connections that may have become invalid and marked for reopening
2677  */
php_oci_dtrace_check_connection(php_oci_connection * connection,sb4 errcode,ub4 serverStatus)2678 void php_oci_dtrace_check_connection(php_oci_connection *connection, sb4 errcode, ub4 serverStatus)
2679 {
2680 #ifdef HAVE_OCI8_DTRACE
2681 	if (DTRACE_OCI8_CHECK_CONNECTION_ENABLED()) {
2682 		DTRACE_OCI8_CHECK_CONNECTION(connection, connection->client_id, connection->is_open ? 1 : 0, (long)errcode, (unsigned long)serverStatus);
2683 	}
2684 #endif /* HAVE_OCI8_DTRACE */
2685 }
2686 /* }}} */
2687 
2688 #endif /* HAVE_OCI8 */
2689