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