xref: /PHP-7.4/ext/ldap/ldap.c (revision fef5a9e5)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) The PHP Group                                          |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Amitay Isaacs  <amitay@w-o-i.com>                           |
16    |          Eric Warnke    <ericw@albany.edu>                           |
17    |          Rasmus Lerdorf <rasmus@php.net>                             |
18    |          Gerrit Thomson <334647@swin.edu.au>                         |
19    |          Jani Taskinen  <sniper@iki.fi>                              |
20    |          Stig Venaas    <venaas@uninett.no>                          |
21    |          Doug Goldstein <cardoe@cardoe.com>                          |
22    |          Côme Chilliet  <mcmic@php.net>                              |
23    | PHP 4.0 updates:  Zeev Suraski <zeev@php.net>                        |
24    +----------------------------------------------------------------------+
25  */
26 
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30 
31 #include "php.h"
32 #include "php_ini.h"
33 
34 #include <stddef.h>
35 
36 #include "ext/standard/dl.h"
37 #include "php_ldap.h"
38 
39 #ifdef PHP_WIN32
40 #include <string.h>
41 #include "config.w32.h"
42 #define strdup _strdup
43 #undef WINDOWS
44 #undef strcasecmp
45 #undef strncasecmp
46 #define WINSOCK 1
47 #define __STDC__ 1
48 #endif
49 
50 #include "ext/standard/php_string.h"
51 #include "ext/standard/info.h"
52 
53 #ifdef HAVE_LDAP_SASL
54 #include <sasl/sasl.h>
55 #endif
56 
57 #define PHP_LDAP_ESCAPE_FILTER 0x01
58 #define PHP_LDAP_ESCAPE_DN     0x02
59 
60 #if defined(LDAP_CONTROL_PAGEDRESULTS) && !defined(HAVE_LDAP_CONTROL_FIND)
ldap_control_find(const char * oid,LDAPControl ** ctrls,LDAPControl *** nextctrlp)61 LDAPControl *ldap_control_find( const char *oid, LDAPControl **ctrls, LDAPControl ***nextctrlp)
62 {
63 	assert(nextctrlp == NULL);
64 	return ldap_find_control(oid, ctrls);
65 }
66 #endif
67 
68 #if !defined(LDAP_API_FEATURE_X_OPENLDAP)
ldap_memvfree(void ** v)69 void ldap_memvfree(void **v)
70 {
71 	ldap_value_free((char **)v);
72 }
73 #endif
74 
75 typedef struct {
76 	LDAP *link;
77 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC)
78 	zval rebindproc;
79 #endif
80 } ldap_linkdata;
81 
82 typedef struct {
83 	LDAPMessage *data;
84 	BerElement  *ber;
85 	zval         res;
86 } ldap_resultentry;
87 
88 ZEND_DECLARE_MODULE_GLOBALS(ldap)
89 static PHP_GINIT_FUNCTION(ldap);
90 
91 static int le_link, le_result, le_result_entry;
92 
93 #ifdef COMPILE_DL_LDAP
94 #ifdef ZTS
95 ZEND_TSRMLS_CACHE_DEFINE()
96 #endif
ZEND_GET_MODULE(ldap)97 ZEND_GET_MODULE(ldap)
98 #endif
99 
100 static void _close_ldap_link(zend_resource *rsrc) /* {{{ */
101 {
102 	ldap_linkdata *ld = (ldap_linkdata *)rsrc->ptr;
103 
104 	/* We use ldap_destroy rather than ldap_unbind here, because ldap_unbind
105 	 * will skip the destructor entirely if a critical client control is set. */
106 	ldap_destroy(ld->link);
107 
108 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC)
109 	zval_ptr_dtor(&ld->rebindproc);
110 #endif
111 
112 	efree(ld);
113 	LDAPG(num_links)--;
114 }
115 /* }}} */
116 
_free_ldap_result(zend_resource * rsrc)117 static void _free_ldap_result(zend_resource *rsrc) /* {{{ */
118 {
119 	LDAPMessage *result = (LDAPMessage *)rsrc->ptr;
120 	ldap_msgfree(result);
121 }
122 /* }}} */
123 
_free_ldap_result_entry(zend_resource * rsrc)124 static void _free_ldap_result_entry(zend_resource *rsrc) /* {{{ */
125 {
126 	ldap_resultentry *entry = (ldap_resultentry *)rsrc->ptr;
127 
128 	if (entry->ber != NULL) {
129 		ber_free(entry->ber, 0);
130 		entry->ber = NULL;
131 	}
132 	zval_ptr_dtor(&entry->res);
133 	efree(entry);
134 }
135 /* }}} */
136 
137 /* {{{ Parse controls from and to arrays */
_php_ldap_control_to_array(LDAP * ld,LDAPControl * ctrl,zval * array,int request)138 static void _php_ldap_control_to_array(LDAP *ld, LDAPControl* ctrl, zval* array, int request)
139 {
140 	array_init(array);
141 
142 	add_assoc_string(array, "oid", ctrl->ldctl_oid);
143 	if (request) {
144 		/* iscritical field only makes sense in request controls (which may be obtained by ldap_get_option) */
145 		add_assoc_bool(array, "iscritical", (ctrl->ldctl_iscritical != 0));
146 	}
147 
148 	/* If it is a known oid, parse to values */
149 	if (strcmp(ctrl->ldctl_oid, LDAP_CONTROL_PASSWORDPOLICYRESPONSE) == 0) {
150 		int expire = 0, grace = 0, rc;
151 		LDAPPasswordPolicyError pperr;
152 		zval value;
153 
154 		rc = ldap_parse_passwordpolicy_control(ld, ctrl, &expire, &grace, &pperr);
155 		if ( rc == LDAP_SUCCESS ) {
156 			array_init(&value);
157 			add_assoc_long(&value, "expire", expire);
158 			add_assoc_long(&value, "grace", grace);
159 
160 			if ( pperr != PP_noError ) {
161 				add_assoc_long(&value, "error", pperr);
162 			}
163 			add_assoc_zval(array, "value", &value);
164 		} else {
165 			add_assoc_null(array, "value");
166 		}
167 	} else if (strcmp(ctrl->ldctl_oid, LDAP_CONTROL_PAGEDRESULTS) == 0) {
168 		int lestimated, rc;
169 		struct berval lcookie = { 0L, NULL };
170 		zval value;
171 
172 		if (ctrl->ldctl_value.bv_len) {
173 			/* ldap_parse_pageresponse_control() allocates lcookie.bv_val */
174 			rc = ldap_parse_pageresponse_control(ld, ctrl, &lestimated, &lcookie);
175 		} else {
176 			/* ldap_parse_pageresponse_control will crash if value is empty */
177 			rc = -1;
178 		}
179 
180 		if ( rc == LDAP_SUCCESS ) {
181 			array_init(&value);
182 			add_assoc_long(&value, "size", lestimated);
183 			add_assoc_stringl(&value, "cookie", lcookie.bv_val, lcookie.bv_len);
184 			add_assoc_zval(array, "value", &value);
185 		} else {
186 			add_assoc_null(array, "value");
187 		}
188 
189 		if (lcookie.bv_val) {
190 			ldap_memfree(lcookie.bv_val);
191 		}
192 	} else if ((strcmp(ctrl->ldctl_oid, LDAP_CONTROL_PRE_READ) == 0) || (strcmp(ctrl->ldctl_oid, LDAP_CONTROL_POST_READ) == 0)) {
193 		BerElement *ber;
194 		struct berval bv;
195 
196 		ber = ber_init(&ctrl->ldctl_value);
197 		if (ber == NULL) {
198 			add_assoc_null(array, "value");
199 		} else if (ber_scanf(ber, "{m{" /*}}*/, &bv) == LBER_ERROR) {
200 			add_assoc_null(array, "value");
201 		} else {
202 			zval value;
203 
204 			array_init(&value);
205 			add_assoc_stringl(&value, "dn", bv.bv_val, bv.bv_len);
206 
207 			while (ber_scanf(ber, "{m" /*}*/, &bv) != LBER_ERROR) {
208 				int	 i;
209 				BerVarray vals = NULL;
210 				zval tmp;
211 
212 				if (ber_scanf(ber, "[W]", &vals) == LBER_ERROR || vals == NULL)
213 				{
214 					break;
215 				}
216 
217 				array_init(&tmp);
218 				for (i = 0; vals[i].bv_val != NULL; i++) {
219 					add_next_index_stringl(&tmp, vals[i].bv_val, vals[i].bv_len);
220 				}
221 				add_assoc_zval(&value, bv.bv_val, &tmp);
222 
223 				ber_bvarray_free(vals);
224 			}
225 			add_assoc_zval(array, "value", &value);
226 		}
227 
228 		if (ber != NULL) {
229 			ber_free(ber, 1);
230 		}
231 	} else if (strcmp(ctrl->ldctl_oid, LDAP_CONTROL_SORTRESPONSE) == 0) {
232 		zval value;
233 		int errcode, rc;
234 		char* attribute;
235 
236 		if (ctrl->ldctl_value.bv_len) {
237 			rc = ldap_parse_sortresponse_control(ld, ctrl, &errcode, &attribute);
238 		} else {
239 			rc = -1;
240 		}
241 		if ( rc == LDAP_SUCCESS ) {
242 			array_init(&value);
243 			add_assoc_long(&value, "errcode", errcode);
244 			if (attribute) {
245 				add_assoc_string(&value, "attribute", attribute);
246 				ldap_memfree(attribute);
247 			}
248 			add_assoc_zval(array, "value", &value);
249 		} else {
250 			add_assoc_null(array, "value");
251 		}
252 	} else if (strcmp(ctrl->ldctl_oid, LDAP_CONTROL_VLVRESPONSE) == 0) {
253 		int target, count, errcode, rc;
254 		struct berval *context;
255 		zval value;
256 
257 		if (ctrl->ldctl_value.bv_len) {
258 			rc = ldap_parse_vlvresponse_control(ld, ctrl, &target, &count, &context, &errcode);
259 		} else {
260 			rc = -1;
261 		}
262 		if ( rc == LDAP_SUCCESS ) {
263 			array_init(&value);
264 			add_assoc_long(&value, "target", target);
265 			add_assoc_long(&value, "count", count);
266 			add_assoc_long(&value, "errcode", errcode);
267 			if ( context && (context->bv_len >= 0) ) {
268 				add_assoc_stringl(&value, "context", context->bv_val, context->bv_len);
269 			}
270 			add_assoc_zval(array, "value", &value);
271 			ber_bvfree(context);
272 		} else {
273 			add_assoc_null(array, "value");
274 		}
275 	} else {
276 		if (ctrl->ldctl_value.bv_len) {
277 			add_assoc_stringl(array, "value", ctrl->ldctl_value.bv_val, ctrl->ldctl_value.bv_len);
278 		} else {
279 			add_assoc_null(array, "value");
280 		}
281 	}
282 }
283 
_php_ldap_control_from_array(LDAP * ld,LDAPControl ** ctrl,zval * array)284 static int _php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, zval* array)
285 {
286 	zval* val;
287 	zend_string *control_oid;
288 	int control_iscritical = 0, rc = LDAP_SUCCESS;
289 	char** ldap_attrs = NULL;
290 	LDAPSortKey** sort_keys = NULL;
291 	zend_string *tmpstring = NULL, **tmpstrings1 = NULL, **tmpstrings2 = NULL;
292 	size_t num_tmpstrings1 = 0, num_tmpstrings2 = 0;
293 
294 	if ((val = zend_hash_str_find(Z_ARRVAL_P(array), "oid", sizeof("oid") - 1)) == NULL) {
295 		php_error_docref(NULL, E_WARNING, "Control must have an oid key");
296 		return -1;
297 	}
298 
299 	control_oid = zval_get_string(val);
300 	if (EG(exception)) {
301 		return -1;
302 	}
303 
304 	if ((val = zend_hash_str_find(Z_ARRVAL_P(array), "iscritical", sizeof("iscritical") - 1)) != NULL) {
305 		control_iscritical = zend_is_true(val);
306 	} else {
307 		control_iscritical = 0;
308 	}
309 
310 	BerElement *ber = NULL;
311 	struct berval control_value = { 0L, NULL };
312 	int control_value_alloc = 0;
313 
314 	if ((val = zend_hash_str_find(Z_ARRVAL_P(array), "value", sizeof("value") - 1)) != NULL) {
315 		if (Z_TYPE_P(val) != IS_ARRAY) {
316 			tmpstring = zval_get_string(val);
317 			if (EG(exception)) {
318 				rc = -1;
319 				goto failure;
320 			}
321 			control_value.bv_val = ZSTR_VAL(tmpstring);
322 			control_value.bv_len = ZSTR_LEN(tmpstring);
323 		} else if (strcmp(ZSTR_VAL(control_oid), LDAP_CONTROL_PAGEDRESULTS) == 0) {
324 			zval* tmp;
325 			int pagesize = 1;
326 			struct berval cookie = { 0L, NULL };
327 			if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "size", sizeof("size") - 1)) != NULL) {
328 				pagesize = zval_get_long(tmp);
329 			}
330 			if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "cookie", sizeof("cookie") - 1)) != NULL) {
331 				tmpstring = zval_get_string(tmp);
332 				if (EG(exception)) {
333 					rc = -1;
334 					goto failure;
335 				}
336 				cookie.bv_val = ZSTR_VAL(tmpstring);
337 				cookie.bv_len = ZSTR_LEN(tmpstring);
338 			}
339 			/* ldap_create_page_control_value() allocates memory for control_value.bv_val */
340 			control_value_alloc = 1;
341 			rc = ldap_create_page_control_value(ld, pagesize, &cookie, &control_value);
342 			if (rc != LDAP_SUCCESS) {
343 				php_error_docref(NULL, E_WARNING, "Failed to create paged result control value: %s (%d)", ldap_err2string(rc), rc);
344 			}
345 		} else if (strcmp(ZSTR_VAL(control_oid), LDAP_CONTROL_ASSERT) == 0) {
346 			zval* tmp;
347 			zend_string* assert;
348 			if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "filter", sizeof("filter") - 1)) == NULL) {
349 				rc = -1;
350 				php_error_docref(NULL, E_WARNING, "Filter missing from assert control value array");
351 			} else {
352 				assert = zval_get_string(tmp);
353 				if (EG(exception)) {
354 					rc = -1;
355 					goto failure;
356 				}
357 				/* ldap_create_assertion_control_value does not reset ld_errno, we need to do it ourselves
358 					 See http://www.openldap.org/its/index.cgi/Incoming?id=8674 */
359 				int success = LDAP_SUCCESS;
360 				ldap_set_option(ld, LDAP_OPT_RESULT_CODE, &success);
361 				/* ldap_create_assertion_control_value() allocates memory for control_value.bv_val */
362 				control_value_alloc = 1;
363 				rc = ldap_create_assertion_control_value(ld, ZSTR_VAL(assert), &control_value);
364 				if (rc != LDAP_SUCCESS) {
365 					php_error_docref(NULL, E_WARNING, "Failed to create assert control value: %s (%d)", ldap_err2string(rc), rc);
366 				}
367 				zend_string_release(assert);
368 			}
369 		} else if (strcmp(ZSTR_VAL(control_oid), LDAP_CONTROL_VALUESRETURNFILTER) == 0) {
370 			zval* tmp;
371 			if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "filter", sizeof("filter") - 1)) == NULL) {
372 				rc = -1;
373 				php_error_docref(NULL, E_WARNING, "Filter missing from control value array");
374 			} else {
375 				ber = ber_alloc_t(LBER_USE_DER);
376 				if (ber == NULL) {
377 					rc = -1;
378 					php_error_docref(NULL, E_WARNING, "Failed to allocate control value");
379 				} else {
380 					tmpstring = zval_get_string(tmp);
381 					if (EG(exception)) {
382 						rc = -1;
383 						goto failure;
384 					}
385 					if (ldap_put_vrFilter(ber, ZSTR_VAL(tmpstring)) == -1) {
386 						rc = -1;
387 						php_error_docref(NULL, E_WARNING, "Failed to create control value: Bad ValuesReturnFilter: %s", ZSTR_VAL(tmpstring));
388 					} else if (ber_flatten2(ber, &control_value, control_value_alloc) == -1) {
389 						rc = -1;
390 					}
391 				}
392 			}
393 		} else if ((strcmp(ZSTR_VAL(control_oid), LDAP_CONTROL_PRE_READ) == 0) || (strcmp(ZSTR_VAL(control_oid), LDAP_CONTROL_POST_READ) == 0)) {
394 			zval* tmp;
395 			if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "attrs", sizeof("attrs") - 1)) == NULL) {
396 				rc = -1;
397 				php_error_docref(NULL, E_WARNING, "Attributes list missing from control value array");
398 			} else {
399 				ber = ber_alloc_t(LBER_USE_DER);
400 
401 				if (ber == NULL) {
402 					rc = -1;
403 					php_error_docref(NULL, E_WARNING, "Failed to allocate control value");
404 				} else {
405 					int num_attribs, i;
406 					zval* attr;
407 
408 					num_attribs = zend_hash_num_elements(Z_ARRVAL_P(tmp));
409 					ldap_attrs = safe_emalloc((num_attribs+1), sizeof(char *), 0);
410 					tmpstrings1 = safe_emalloc(num_attribs, sizeof(zend_string*), 0);
411 					num_tmpstrings1 = 0;
412 
413 					for (i = 0; i<num_attribs; i++) {
414 						if ((attr = zend_hash_index_find(Z_ARRVAL_P(tmp), i)) == NULL) {
415 							rc = -1;
416 							php_error_docref(NULL, E_WARNING, "Failed to encode attribute list");
417 							goto failure;
418 						}
419 
420 						tmpstrings1[num_tmpstrings1] = zval_get_string(attr);
421 						if (EG(exception)) {
422 							rc = -1;
423 							goto failure;
424 						}
425 						ldap_attrs[i] = ZSTR_VAL(tmpstrings1[num_tmpstrings1]);
426 						++num_tmpstrings1;
427 					}
428 					ldap_attrs[num_attribs] = NULL;
429 
430 					ber_init2( ber, NULL, LBER_USE_DER );
431 
432 					if (ber_printf(ber, "{v}", ldap_attrs) == -1) {
433 						rc = -1;
434 						php_error_docref(NULL, E_WARNING, "Failed to encode attribute list");
435 					} else {
436 						int err;
437 						err = ber_flatten2(ber, &control_value, control_value_alloc);
438 						if (err < 0) {
439 							rc = -1;
440 							php_error_docref(NULL, E_WARNING, "Failed to encode control value (%d)", err);
441 						}
442 					}
443 				}
444 			}
445 		} else if (strcmp(ZSTR_VAL(control_oid), LDAP_CONTROL_SORTREQUEST) == 0) {
446 			int num_keys, i;
447 			zval *sortkey, *tmp;
448 
449 			num_keys = zend_hash_num_elements(Z_ARRVAL_P(val));
450 			sort_keys = safe_emalloc((num_keys+1), sizeof(LDAPSortKey*), 0);
451 			tmpstrings1 = safe_emalloc(num_keys, sizeof(zend_string*), 0);
452 			tmpstrings2 = safe_emalloc(num_keys, sizeof(zend_string*), 0);
453 			num_tmpstrings1 = 0;
454 			num_tmpstrings2 = 0;
455 
456 			for (i = 0; i<num_keys; i++) {
457 				if ((sortkey = zend_hash_index_find(Z_ARRVAL_P(val), i)) == NULL) {
458 					rc = -1;
459 					php_error_docref(NULL, E_WARNING, "Failed to encode sort keys list");
460 					goto failure;
461 				}
462 
463 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(sortkey), "attr", sizeof("attr") - 1)) == NULL) {
464 					rc = -1;
465 					php_error_docref(NULL, E_WARNING, "Sort key list missing field");
466 					goto failure;
467 				}
468 				sort_keys[i] = emalloc(sizeof(LDAPSortKey));
469 				tmpstrings1[num_tmpstrings1] = zval_get_string(tmp);
470 				if (EG(exception)) {
471 					rc = -1;
472 					goto failure;
473 				}
474 				sort_keys[i]->attributeType = ZSTR_VAL(tmpstrings1[num_tmpstrings1]);
475 				++num_tmpstrings1;
476 
477 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(sortkey), "oid", sizeof("oid") - 1)) != NULL) {
478 					tmpstrings2[num_tmpstrings2] = zval_get_string(tmp);
479 					if (EG(exception)) {
480 						rc = -1;
481 						goto failure;
482 					}
483 					sort_keys[i]->orderingRule = ZSTR_VAL(tmpstrings2[num_tmpstrings2]);
484 					++num_tmpstrings2;
485 				} else {
486 					sort_keys[i]->orderingRule = NULL;
487 				}
488 
489 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(sortkey), "reverse", sizeof("reverse") - 1)) != NULL) {
490 					sort_keys[i]->reverseOrder = zend_is_true(tmp);
491 				} else {
492 					sort_keys[i]->reverseOrder = 0;
493 				}
494 			}
495 			sort_keys[num_keys] = NULL;
496 			/* ldap_create_sort_control_value() allocates memory for control_value.bv_val */
497 			control_value_alloc = 1;
498 			rc = ldap_create_sort_control_value(ld, sort_keys, &control_value);
499 			if (rc != LDAP_SUCCESS) {
500 				php_error_docref(NULL, E_WARNING, "Failed to create sort control value: %s (%d)", ldap_err2string(rc), rc);
501 			}
502 		} else if (strcmp(ZSTR_VAL(control_oid), LDAP_CONTROL_VLVREQUEST) == 0) {
503 			zval* tmp;
504 			LDAPVLVInfo vlvInfo;
505 			struct berval attrValue;
506 			struct berval context;
507 
508 			if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "before", sizeof("before") - 1)) != NULL) {
509 				vlvInfo.ldvlv_before_count = zval_get_long(tmp);
510 			} else {
511 				rc = -1;
512 				php_error_docref(NULL, E_WARNING, "Before key missing from array value for VLV control");
513 				goto failure;
514 			}
515 
516 			if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "after", sizeof("after") - 1)) != NULL) {
517 				vlvInfo.ldvlv_after_count = zval_get_long(tmp);
518 			} else {
519 				rc = -1;
520 				php_error_docref(NULL, E_WARNING, "After key missing from array value for VLV control");
521 				goto failure;
522 			}
523 
524 			if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "attrvalue", sizeof("attrvalue") - 1)) != NULL) {
525 				tmpstring = zval_get_string(tmp);
526 				if (EG(exception)) {
527 					rc = -1;
528 					goto failure;
529 				}
530 				attrValue.bv_val = ZSTR_VAL(tmpstring);
531 				attrValue.bv_len = ZSTR_LEN(tmpstring);
532 				vlvInfo.ldvlv_attrvalue = &attrValue;
533 			} else if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "offset", sizeof("offset") - 1)) != NULL) {
534 				vlvInfo.ldvlv_attrvalue = NULL;
535 				vlvInfo.ldvlv_offset = zval_get_long(tmp);
536 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "count", sizeof("count") - 1)) != NULL) {
537 					vlvInfo.ldvlv_count = zval_get_long(tmp);
538 				} else {
539 					rc = -1;
540 					php_error_docref(NULL, E_WARNING, "Count key missing from array value for VLV control");
541 					goto failure;
542 				}
543 			} else {
544 				rc = -1;
545 				php_error_docref(NULL, E_WARNING, "Missing either attrvalue or offset key from array value for VLV control");
546 				goto failure;
547 			}
548 
549 			if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "context", sizeof("context") - 1)) != NULL) {
550 				tmpstring = zval_get_string(tmp);
551 				if (EG(exception)) {
552 					rc = -1;
553 					goto failure;
554 				}
555 				context.bv_val = ZSTR_VAL(tmpstring);
556 				context.bv_len = ZSTR_LEN(tmpstring);
557 				vlvInfo.ldvlv_context = &context;
558 			} else {
559 				vlvInfo.ldvlv_context = NULL;
560 			}
561 
562 			/* ldap_create_vlv_control_value() allocates memory for control_value.bv_val */
563 			control_value_alloc = 1;
564 			rc = ldap_create_vlv_control_value(ld, &vlvInfo, &control_value);
565 			if (rc != LDAP_SUCCESS) {
566 				php_error_docref(NULL, E_WARNING, "Failed to create VLV control value: %s (%d)", ldap_err2string(rc), rc);
567 			}
568 		} else {
569 			php_error_docref(NULL, E_WARNING, "Control OID %s does not expect an array as value", ZSTR_VAL(control_oid));
570 			rc = -1;
571 		}
572 	}
573 
574 	if (rc == LDAP_SUCCESS) {
575 		rc = ldap_control_create(ZSTR_VAL(control_oid), control_iscritical, &control_value, 1, ctrl);
576 	}
577 
578 failure:
579 	zend_string_release(control_oid);
580 	if (tmpstring != NULL) {
581 		zend_string_release(tmpstring);
582 	}
583 	if (tmpstrings1 != NULL) {
584 		int i;
585 		for (i = 0; i < num_tmpstrings1; ++i) {
586 			zend_string_release(tmpstrings1[i]);
587 		}
588 		efree(tmpstrings1);
589 	}
590 	if (tmpstrings2 != NULL) {
591 		int i;
592 		for (i = 0; i < num_tmpstrings2; ++i) {
593 			zend_string_release(tmpstrings2[i]);
594 		}
595 		efree(tmpstrings2);
596 	}
597 	if (control_value.bv_val != NULL && control_value_alloc != 0) {
598 		ber_memfree(control_value.bv_val);
599 	}
600 	if (ber != NULL) {
601 		ber_free(ber, 1);
602 	}
603 	if (ldap_attrs != NULL) {
604 		efree(ldap_attrs);
605 	}
606 	if (sort_keys != NULL) {
607 		LDAPSortKey** sortp = sort_keys;
608 		while (*sortp) {
609 			efree(*sortp);
610 			sortp++;
611 		}
612 		efree(sort_keys);
613 		sort_keys = NULL;
614 	}
615 
616 	if (rc == LDAP_SUCCESS) {
617 		return LDAP_SUCCESS;
618 	}
619 
620 	/* Failed */
621 	*ctrl = NULL;
622 	return -1;
623 }
624 
_php_ldap_controls_to_array(LDAP * ld,LDAPControl ** ctrls,zval * array,int request)625 static void _php_ldap_controls_to_array(LDAP *ld, LDAPControl** ctrls, zval* array, int request)
626 {
627 	zval tmp1;
628 	LDAPControl **ctrlp;
629 
630 	array = zend_try_array_init(array);
631 	if (!array) {
632 		return;
633 	}
634 
635 	if (ctrls == NULL) {
636 		return;
637 	}
638 	ctrlp = ctrls;
639 	while (*ctrlp != NULL) {
640 		_php_ldap_control_to_array(ld, *ctrlp, &tmp1, request);
641 		add_assoc_zval(array, (*ctrlp)->ldctl_oid, &tmp1);
642 		ctrlp++;
643 	}
644 	ldap_controls_free(ctrls);
645 }
646 
_php_ldap_controls_from_array(LDAP * ld,zval * array)647 static LDAPControl** _php_ldap_controls_from_array(LDAP *ld, zval* array)
648 {
649 	int ncontrols;
650 	LDAPControl** ctrlp, **ctrls = NULL;
651 	zval* ctrlarray;
652 	int error = 0;
653 
654 	ncontrols = zend_hash_num_elements(Z_ARRVAL_P(array));
655 	ctrls = safe_emalloc((1 + ncontrols), sizeof(*ctrls), 0);
656 	*ctrls = NULL;
657 	ctrlp = ctrls;
658 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), ctrlarray) {
659 		if (Z_TYPE_P(ctrlarray) != IS_ARRAY) {
660 			php_error_docref(NULL, E_WARNING, "The array value must contain only arrays, where each array is a control");
661 			error = 1;
662 			break;
663 		}
664 
665 		if (_php_ldap_control_from_array(ld, ctrlp, ctrlarray) == LDAP_SUCCESS) {
666 			++ctrlp;
667 		} else {
668 			error = 1;
669 			break;
670 		}
671 
672 		*ctrlp = NULL;
673 	} ZEND_HASH_FOREACH_END();
674 
675 	if (error) {
676 		ctrlp = ctrls;
677 		while (*ctrlp) {
678 			ldap_control_free(*ctrlp);
679 			ctrlp++;
680 		}
681 		efree(ctrls);
682 		ctrls = NULL;
683 	}
684 
685 	return ctrls;
686 }
687 
_php_ldap_controls_free(LDAPControl *** ctrls)688 static void _php_ldap_controls_free (LDAPControl*** ctrls)
689 {
690 	LDAPControl **ctrlp;
691 
692 	if (*ctrls) {
693 		ctrlp = *ctrls;
694 		while (*ctrlp) {
695 			ldap_control_free(*ctrlp);
696 			ctrlp++;
697 		}
698 		efree(*ctrls);
699 		*ctrls = NULL;
700 	}
701 }
702 /* }}} */
703 
704 /* {{{ PHP_INI_BEGIN
705  */
706 PHP_INI_BEGIN()
707 	STD_PHP_INI_ENTRY_EX("ldap.max_links", "-1", PHP_INI_SYSTEM, OnUpdateLong, max_links, zend_ldap_globals, ldap_globals, display_link_numbers)
PHP_INI_END()708 PHP_INI_END()
709 /* }}} */
710 
711 /* {{{ PHP_GINIT_FUNCTION
712  */
713 static PHP_GINIT_FUNCTION(ldap)
714 {
715 #if defined(COMPILE_DL_LDAP) && defined(ZTS)
716 	ZEND_TSRMLS_CACHE_UPDATE();
717 #endif
718 	ldap_globals->num_links = 0;
719 }
720 /* }}} */
721 
722 /* {{{ PHP_MINIT_FUNCTION
723  */
PHP_MINIT_FUNCTION(ldap)724 PHP_MINIT_FUNCTION(ldap)
725 {
726 	REGISTER_INI_ENTRIES();
727 
728 	/* Constants to be used with deref-parameter in php_ldap_do_search() */
729 	REGISTER_LONG_CONSTANT("LDAP_DEREF_NEVER", LDAP_DEREF_NEVER, CONST_PERSISTENT | CONST_CS);
730 	REGISTER_LONG_CONSTANT("LDAP_DEREF_SEARCHING", LDAP_DEREF_SEARCHING, CONST_PERSISTENT | CONST_CS);
731 	REGISTER_LONG_CONSTANT("LDAP_DEREF_FINDING", LDAP_DEREF_FINDING, CONST_PERSISTENT | CONST_CS);
732 	REGISTER_LONG_CONSTANT("LDAP_DEREF_ALWAYS", LDAP_DEREF_ALWAYS, CONST_PERSISTENT | CONST_CS);
733 
734 	/* Constants to be used with ldap_modify_batch() */
735 	REGISTER_LONG_CONSTANT("LDAP_MODIFY_BATCH_ADD", LDAP_MODIFY_BATCH_ADD, CONST_PERSISTENT | CONST_CS);
736 	REGISTER_LONG_CONSTANT("LDAP_MODIFY_BATCH_REMOVE", LDAP_MODIFY_BATCH_REMOVE, CONST_PERSISTENT | CONST_CS);
737 	REGISTER_LONG_CONSTANT("LDAP_MODIFY_BATCH_REMOVE_ALL", LDAP_MODIFY_BATCH_REMOVE_ALL, CONST_PERSISTENT | CONST_CS);
738 	REGISTER_LONG_CONSTANT("LDAP_MODIFY_BATCH_REPLACE", LDAP_MODIFY_BATCH_REPLACE, CONST_PERSISTENT | CONST_CS);
739 	REGISTER_STRING_CONSTANT("LDAP_MODIFY_BATCH_ATTRIB", LDAP_MODIFY_BATCH_ATTRIB, CONST_PERSISTENT | CONST_CS);
740 	REGISTER_STRING_CONSTANT("LDAP_MODIFY_BATCH_MODTYPE", LDAP_MODIFY_BATCH_MODTYPE, CONST_PERSISTENT | CONST_CS);
741 	REGISTER_STRING_CONSTANT("LDAP_MODIFY_BATCH_VALUES", LDAP_MODIFY_BATCH_VALUES, CONST_PERSISTENT | CONST_CS);
742 
743 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP
744 	/* LDAP options */
745 	REGISTER_LONG_CONSTANT("LDAP_OPT_DEREF", LDAP_OPT_DEREF, CONST_PERSISTENT | CONST_CS);
746 	REGISTER_LONG_CONSTANT("LDAP_OPT_SIZELIMIT", LDAP_OPT_SIZELIMIT, CONST_PERSISTENT | CONST_CS);
747 	REGISTER_LONG_CONSTANT("LDAP_OPT_TIMELIMIT", LDAP_OPT_TIMELIMIT, CONST_PERSISTENT | CONST_CS);
748 #ifdef LDAP_OPT_NETWORK_TIMEOUT
749 	REGISTER_LONG_CONSTANT("LDAP_OPT_NETWORK_TIMEOUT", LDAP_OPT_NETWORK_TIMEOUT, CONST_PERSISTENT | CONST_CS);
750 #elif defined (LDAP_X_OPT_CONNECT_TIMEOUT)
751 	REGISTER_LONG_CONSTANT("LDAP_OPT_NETWORK_TIMEOUT", LDAP_X_OPT_CONNECT_TIMEOUT, CONST_PERSISTENT | CONST_CS);
752 #endif
753 #ifdef LDAP_OPT_TIMEOUT
754 	REGISTER_LONG_CONSTANT("LDAP_OPT_TIMEOUT", LDAP_OPT_TIMEOUT, CONST_PERSISTENT | CONST_CS);
755 #endif
756 	REGISTER_LONG_CONSTANT("LDAP_OPT_PROTOCOL_VERSION", LDAP_OPT_PROTOCOL_VERSION, CONST_PERSISTENT | CONST_CS);
757 	REGISTER_LONG_CONSTANT("LDAP_OPT_ERROR_NUMBER", LDAP_OPT_ERROR_NUMBER, CONST_PERSISTENT | CONST_CS);
758 	REGISTER_LONG_CONSTANT("LDAP_OPT_REFERRALS", LDAP_OPT_REFERRALS, CONST_PERSISTENT | CONST_CS);
759 #ifdef LDAP_OPT_RESTART
760 	REGISTER_LONG_CONSTANT("LDAP_OPT_RESTART", LDAP_OPT_RESTART, CONST_PERSISTENT | CONST_CS);
761 #endif
762 #ifdef LDAP_OPT_HOST_NAME
763 	REGISTER_LONG_CONSTANT("LDAP_OPT_HOST_NAME", LDAP_OPT_HOST_NAME, CONST_PERSISTENT | CONST_CS);
764 #endif
765 	REGISTER_LONG_CONSTANT("LDAP_OPT_ERROR_STRING", LDAP_OPT_ERROR_STRING, CONST_PERSISTENT | CONST_CS);
766 #ifdef LDAP_OPT_MATCHED_DN
767 	REGISTER_LONG_CONSTANT("LDAP_OPT_MATCHED_DN", LDAP_OPT_MATCHED_DN, CONST_PERSISTENT | CONST_CS);
768 #endif
769 	REGISTER_LONG_CONSTANT("LDAP_OPT_SERVER_CONTROLS", LDAP_OPT_SERVER_CONTROLS, CONST_PERSISTENT | CONST_CS);
770 	REGISTER_LONG_CONSTANT("LDAP_OPT_CLIENT_CONTROLS", LDAP_OPT_CLIENT_CONTROLS, CONST_PERSISTENT | CONST_CS);
771 #endif
772 #ifdef LDAP_OPT_DEBUG_LEVEL
773 	REGISTER_LONG_CONSTANT("LDAP_OPT_DEBUG_LEVEL", LDAP_OPT_DEBUG_LEVEL, CONST_PERSISTENT | CONST_CS);
774 #endif
775 
776 #ifdef LDAP_OPT_DIAGNOSTIC_MESSAGE
777 	REGISTER_LONG_CONSTANT("LDAP_OPT_DIAGNOSTIC_MESSAGE", LDAP_OPT_DIAGNOSTIC_MESSAGE, CONST_PERSISTENT | CONST_CS);
778 #endif
779 
780 #ifdef HAVE_LDAP_SASL
781 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_MECH", LDAP_OPT_X_SASL_MECH, CONST_PERSISTENT | CONST_CS);
782 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_REALM", LDAP_OPT_X_SASL_REALM, CONST_PERSISTENT | CONST_CS);
783 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_AUTHCID", LDAP_OPT_X_SASL_AUTHCID, CONST_PERSISTENT | CONST_CS);
784 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_AUTHZID", LDAP_OPT_X_SASL_AUTHZID, CONST_PERSISTENT | CONST_CS);
785 #endif
786 #ifdef LDAP_OPT_X_SASL_NOCANON
787 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_NOCANON", LDAP_OPT_X_SASL_NOCANON, CONST_PERSISTENT | CONST_CS);
788 #endif
789 #ifdef LDAP_OPT_X_SASL_USERNAME
790 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_USERNAME", LDAP_OPT_X_SASL_USERNAME, CONST_PERSISTENT | CONST_CS);
791 #endif
792 
793 #ifdef ORALDAP
794 	REGISTER_LONG_CONSTANT("GSLC_SSL_NO_AUTH", GSLC_SSL_NO_AUTH, CONST_PERSISTENT | CONST_CS);
795 	REGISTER_LONG_CONSTANT("GSLC_SSL_ONEWAY_AUTH", GSLC_SSL_ONEWAY_AUTH, CONST_PERSISTENT | CONST_CS);
796 	REGISTER_LONG_CONSTANT("GSLC_SSL_TWOWAY_AUTH", GSLC_SSL_TWOWAY_AUTH, CONST_PERSISTENT | CONST_CS);
797 #endif
798 
799 #if (LDAP_API_VERSION > 2000)
800 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_REQUIRE_CERT", LDAP_OPT_X_TLS_REQUIRE_CERT, CONST_PERSISTENT | CONST_CS);
801 
802 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_NEVER", LDAP_OPT_X_TLS_NEVER, CONST_PERSISTENT | CONST_CS);
803 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_HARD", LDAP_OPT_X_TLS_HARD, CONST_PERSISTENT | CONST_CS);
804 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_DEMAND", LDAP_OPT_X_TLS_DEMAND, CONST_PERSISTENT | CONST_CS);
805 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_ALLOW", LDAP_OPT_X_TLS_ALLOW, CONST_PERSISTENT | CONST_CS);
806 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_TRY", LDAP_OPT_X_TLS_TRY, CONST_PERSISTENT | CONST_CS);
807 
808 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CACERTDIR", LDAP_OPT_X_TLS_CACERTDIR, CONST_PERSISTENT | CONST_CS);
809 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CACERTFILE", LDAP_OPT_X_TLS_CACERTFILE, CONST_PERSISTENT | CONST_CS);
810 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CERTFILE", LDAP_OPT_X_TLS_CERTFILE, CONST_PERSISTENT | CONST_CS);
811 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CIPHER_SUITE", LDAP_OPT_X_TLS_CIPHER_SUITE, CONST_PERSISTENT | CONST_CS);
812 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_KEYFILE", LDAP_OPT_X_TLS_KEYFILE, CONST_PERSISTENT | CONST_CS);
813 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_RANDOM_FILE", LDAP_OPT_X_TLS_RANDOM_FILE, CONST_PERSISTENT | CONST_CS);
814 #endif
815 
816 #ifdef LDAP_OPT_X_TLS_CRLCHECK
817 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CRLCHECK", LDAP_OPT_X_TLS_CRLCHECK, CONST_PERSISTENT | CONST_CS);
818 
819 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CRL_NONE", LDAP_OPT_X_TLS_CRL_NONE, CONST_PERSISTENT | CONST_CS);
820 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CRL_PEER", LDAP_OPT_X_TLS_CRL_PEER, CONST_PERSISTENT | CONST_CS);
821 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CRL_ALL", LDAP_OPT_X_TLS_CRL_ALL, CONST_PERSISTENT | CONST_CS);
822 #endif
823 
824 #ifdef LDAP_OPT_X_TLS_DHFILE
825 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_DHFILE", LDAP_OPT_X_TLS_DHFILE, CONST_PERSISTENT | CONST_CS);
826 #endif
827 
828 #ifdef LDAP_OPT_X_TLS_CRLFILE
829 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CRLFILE", LDAP_OPT_X_TLS_CRLFILE, CONST_PERSISTENT | CONST_CS);
830 #endif
831 
832 #ifdef LDAP_OPT_X_TLS_PROTOCOL_MIN
833 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_MIN", LDAP_OPT_X_TLS_PROTOCOL_MIN, CONST_PERSISTENT | CONST_CS);
834 
835 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_SSL2", LDAP_OPT_X_TLS_PROTOCOL_SSL2, CONST_PERSISTENT | CONST_CS);
836 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_SSL3", LDAP_OPT_X_TLS_PROTOCOL_SSL3, CONST_PERSISTENT | CONST_CS);
837 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_TLS1_0", LDAP_OPT_X_TLS_PROTOCOL_TLS1_0, CONST_PERSISTENT | CONST_CS);
838 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_TLS1_1", LDAP_OPT_X_TLS_PROTOCOL_TLS1_1, CONST_PERSISTENT | CONST_CS);
839 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_TLS1_2", LDAP_OPT_X_TLS_PROTOCOL_TLS1_2, CONST_PERSISTENT | CONST_CS);
840 #endif
841 
842 #ifdef LDAP_OPT_X_TLS_PACKAGE
843 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PACKAGE", LDAP_OPT_X_TLS_PACKAGE, CONST_PERSISTENT | CONST_CS);
844 #endif
845 
846 #ifdef LDAP_OPT_X_KEEPALIVE_IDLE
847 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_KEEPALIVE_IDLE", LDAP_OPT_X_KEEPALIVE_IDLE, CONST_PERSISTENT | CONST_CS);
848 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_KEEPALIVE_PROBES", LDAP_OPT_X_KEEPALIVE_PROBES, CONST_PERSISTENT | CONST_CS);
849 	REGISTER_LONG_CONSTANT("LDAP_OPT_X_KEEPALIVE_INTERVAL", LDAP_OPT_X_KEEPALIVE_INTERVAL, CONST_PERSISTENT | CONST_CS);
850 #endif
851 
852 	REGISTER_LONG_CONSTANT("LDAP_ESCAPE_FILTER", PHP_LDAP_ESCAPE_FILTER, CONST_PERSISTENT | CONST_CS);
853 	REGISTER_LONG_CONSTANT("LDAP_ESCAPE_DN", PHP_LDAP_ESCAPE_DN, CONST_PERSISTENT | CONST_CS);
854 
855 #ifdef HAVE_LDAP_EXTENDED_OPERATION_S
856 	REGISTER_STRING_CONSTANT("LDAP_EXOP_START_TLS", LDAP_EXOP_START_TLS, CONST_PERSISTENT | CONST_CS);
857 	REGISTER_STRING_CONSTANT("LDAP_EXOP_MODIFY_PASSWD", LDAP_EXOP_MODIFY_PASSWD, CONST_PERSISTENT | CONST_CS);
858 	REGISTER_STRING_CONSTANT("LDAP_EXOP_REFRESH", LDAP_EXOP_REFRESH, CONST_PERSISTENT | CONST_CS);
859 	REGISTER_STRING_CONSTANT("LDAP_EXOP_WHO_AM_I", LDAP_EXOP_WHO_AM_I, CONST_PERSISTENT | CONST_CS);
860 	REGISTER_STRING_CONSTANT("LDAP_EXOP_TURN", LDAP_EXOP_TURN, CONST_PERSISTENT | CONST_CS);
861 #endif
862 
863 /* LDAP Controls */
864 /*	standard track controls */
865 #ifdef LDAP_CONTROL_MANAGEDSAIT
866 	/* RFC 3296 */
867 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_MANAGEDSAIT", LDAP_CONTROL_MANAGEDSAIT, CONST_PERSISTENT | CONST_CS);
868 #endif
869 #ifdef LDAP_CONTROL_PROXY_AUTHZ
870 	/* RFC 4370 */
871 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_PROXY_AUTHZ", LDAP_CONTROL_PROXY_AUTHZ, CONST_PERSISTENT | CONST_CS);
872 #endif
873 #ifdef LDAP_CONTROL_SUBENTRIES
874 	/* RFC 3672 */
875 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_SUBENTRIES", LDAP_CONTROL_SUBENTRIES, CONST_PERSISTENT | CONST_CS);
876 #endif
877 #ifdef LDAP_CONTROL_VALUESRETURNFILTER
878 	/* RFC 3876 */
879 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_VALUESRETURNFILTER", LDAP_CONTROL_VALUESRETURNFILTER, CONST_PERSISTENT | CONST_CS);
880 #endif
881 #ifdef LDAP_CONTROL_ASSERT
882 	/* RFC 4528 */
883 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_ASSERT", LDAP_CONTROL_ASSERT, CONST_PERSISTENT | CONST_CS);
884 	/* RFC 4527 */
885 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_PRE_READ", LDAP_CONTROL_PRE_READ, CONST_PERSISTENT | CONST_CS);
886 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_POST_READ", LDAP_CONTROL_POST_READ, CONST_PERSISTENT | CONST_CS);
887 #endif
888 #ifdef LDAP_CONTROL_SORTREQUEST
889 	/* RFC 2891 */
890 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_SORTREQUEST", LDAP_CONTROL_SORTREQUEST, CONST_PERSISTENT | CONST_CS);
891 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_SORTRESPONSE", LDAP_CONTROL_SORTRESPONSE, CONST_PERSISTENT | CONST_CS);
892 #endif
893 /*	non-standard track controls */
894 #ifdef LDAP_CONTROL_PAGEDRESULTS
895 	/* RFC 2696 */
896 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_PAGEDRESULTS", LDAP_CONTROL_PAGEDRESULTS, CONST_PERSISTENT | CONST_CS);
897 #endif
898 #ifdef LDAP_CONTROL_AUTHZID_REQUEST
899 	/* RFC 3829 */
900 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_AUTHZID_REQUEST", LDAP_CONTROL_AUTHZID_REQUEST, CONST_PERSISTENT | CONST_CS);
901 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_AUTHZID_RESPONSE", LDAP_CONTROL_AUTHZID_RESPONSE, CONST_PERSISTENT | CONST_CS);
902 #endif
903 #ifdef LDAP_CONTROL_SYNC
904 	/* LDAP Content Synchronization Operation -- RFC 4533 */
905 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_SYNC", LDAP_CONTROL_SYNC, CONST_PERSISTENT | CONST_CS);
906 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_SYNC_STATE", LDAP_CONTROL_SYNC_STATE, CONST_PERSISTENT | CONST_CS);
907 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_SYNC_DONE", LDAP_CONTROL_SYNC_DONE, CONST_PERSISTENT | CONST_CS);
908 #endif
909 #ifdef LDAP_CONTROL_DONTUSECOPY
910 	/* LDAP Don't Use Copy Control (RFC 6171) */
911 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_DONTUSECOPY", LDAP_CONTROL_DONTUSECOPY, CONST_PERSISTENT | CONST_CS);
912 #endif
913 #ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
914 	/* Password policy Controls */
915 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_PASSWORDPOLICYREQUEST", LDAP_CONTROL_PASSWORDPOLICYREQUEST, CONST_PERSISTENT | CONST_CS);
916 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_PASSWORDPOLICYRESPONSE", LDAP_CONTROL_PASSWORDPOLICYRESPONSE, CONST_PERSISTENT | CONST_CS);
917 #endif
918 #ifdef LDAP_CONTROL_X_INCREMENTAL_VALUES
919 	/* MS Active Directory controls */
920 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_INCREMENTAL_VALUES", LDAP_CONTROL_X_INCREMENTAL_VALUES, CONST_PERSISTENT | CONST_CS);
921 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_DOMAIN_SCOPE", LDAP_CONTROL_X_DOMAIN_SCOPE, CONST_PERSISTENT | CONST_CS);
922 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_PERMISSIVE_MODIFY", LDAP_CONTROL_X_PERMISSIVE_MODIFY, CONST_PERSISTENT | CONST_CS);
923 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_SEARCH_OPTIONS", LDAP_CONTROL_X_SEARCH_OPTIONS, CONST_PERSISTENT | CONST_CS);
924 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_TREE_DELETE", LDAP_CONTROL_X_TREE_DELETE, CONST_PERSISTENT | CONST_CS);
925 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_EXTENDED_DN", LDAP_CONTROL_X_EXTENDED_DN, CONST_PERSISTENT | CONST_CS);
926 #endif
927 #ifdef LDAP_CONTROL_VLVREQUEST
928 	/* LDAP VLV */
929 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_VLVREQUEST", LDAP_CONTROL_VLVREQUEST, CONST_PERSISTENT | CONST_CS);
930 	REGISTER_STRING_CONSTANT("LDAP_CONTROL_VLVRESPONSE", LDAP_CONTROL_VLVRESPONSE, CONST_PERSISTENT | CONST_CS);
931 #endif
932 
933 	le_link = zend_register_list_destructors_ex(_close_ldap_link, NULL, "ldap link", module_number);
934 	le_result = zend_register_list_destructors_ex(_free_ldap_result, NULL, "ldap result", module_number);
935 	le_result_entry = zend_register_list_destructors_ex(_free_ldap_result_entry, NULL, "ldap result entry", module_number);
936 
937 	ldap_module_entry.type = type;
938 
939 	return SUCCESS;
940 }
941 /* }}} */
942 
943 /* {{{ PHP_MSHUTDOWN_FUNCTION
944  */
PHP_MSHUTDOWN_FUNCTION(ldap)945 PHP_MSHUTDOWN_FUNCTION(ldap)
946 {
947 	UNREGISTER_INI_ENTRIES();
948 	return SUCCESS;
949 }
950 /* }}} */
951 
952 /* {{{ PHP_MINFO_FUNCTION
953  */
PHP_MINFO_FUNCTION(ldap)954 PHP_MINFO_FUNCTION(ldap)
955 {
956 	char tmp[32];
957 
958 	php_info_print_table_start();
959 	php_info_print_table_row(2, "LDAP Support", "enabled");
960 
961 	if (LDAPG(max_links) == -1) {
962 		snprintf(tmp, 31, ZEND_LONG_FMT "/unlimited", LDAPG(num_links));
963 	} else {
964 		snprintf(tmp, 31, ZEND_LONG_FMT "/" ZEND_LONG_FMT, LDAPG(num_links), LDAPG(max_links));
965 	}
966 	php_info_print_table_row(2, "Total Links", tmp);
967 
968 #ifdef LDAP_API_VERSION
969 	snprintf(tmp, 31, "%d", LDAP_API_VERSION);
970 	php_info_print_table_row(2, "API Version", tmp);
971 #endif
972 
973 #ifdef LDAP_VENDOR_NAME
974 	php_info_print_table_row(2, "Vendor Name", LDAP_VENDOR_NAME);
975 #endif
976 
977 #ifdef LDAP_VENDOR_VERSION
978 	snprintf(tmp, 31, "%d", LDAP_VENDOR_VERSION);
979 	php_info_print_table_row(2, "Vendor Version", tmp);
980 #endif
981 
982 #ifdef HAVE_LDAP_SASL
983 	php_info_print_table_row(2, "SASL Support", "Enabled");
984 #endif
985 
986 	php_info_print_table_end();
987 	DISPLAY_INI_ENTRIES();
988 }
989 /* }}} */
990 
991 /* {{{ proto resource ldap_connect([string host [, int port [, string wallet [, string wallet_passwd [, int authmode]]]]])
992    Connect to an LDAP server */
PHP_FUNCTION(ldap_connect)993 PHP_FUNCTION(ldap_connect)
994 {
995 	char *host = NULL;
996 	size_t hostlen = 0;
997 	zend_long port = LDAP_PORT;
998 #ifdef HAVE_ORALDAP
999 	char *wallet = NULL, *walletpasswd = NULL;
1000 	size_t walletlen = 0, walletpasswdlen = 0;
1001 	zend_long authmode = GSLC_SSL_NO_AUTH;
1002 	int ssl=0;
1003 #endif
1004 	ldap_linkdata *ld;
1005 	LDAP *ldap = NULL;
1006 
1007 #ifdef HAVE_ORALDAP
1008 	if (ZEND_NUM_ARGS() == 3 || ZEND_NUM_ARGS() == 4) {
1009 		WRONG_PARAM_COUNT;
1010 	}
1011 
1012 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|slssl", &host, &hostlen, &port, &wallet, &walletlen, &walletpasswd, &walletpasswdlen, &authmode) != SUCCESS) {
1013 		RETURN_FALSE;
1014 	}
1015 
1016 	if (ZEND_NUM_ARGS() == 5) {
1017 		ssl = 1;
1018 	}
1019 #else
1020 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|sl", &host, &hostlen, &port) != SUCCESS) {
1021 		RETURN_FALSE;
1022 	}
1023 #endif
1024 
1025 	if (LDAPG(max_links) != -1 && LDAPG(num_links) >= LDAPG(max_links)) {
1026 		php_error_docref(NULL, E_WARNING, "Too many open links (" ZEND_LONG_FMT ")", LDAPG(num_links));
1027 		RETURN_FALSE;
1028 	}
1029 
1030 	ld = ecalloc(1, sizeof(ldap_linkdata));
1031 
1032 	{
1033 		int rc = LDAP_SUCCESS;
1034 		char	*url = host;
1035 		if (url && !ldap_is_ldap_url(url)) {
1036 			size_t urllen = hostlen + sizeof( "ldap://:65535" );
1037 
1038 			if (port <= 0 || port > 65535) {
1039 				efree(ld);
1040 				php_error_docref(NULL, E_WARNING, "invalid port number: " ZEND_LONG_FMT, port);
1041 				RETURN_FALSE;
1042 			}
1043 
1044 			url = emalloc(urllen);
1045 			snprintf( url, urllen, "ldap://%s:" ZEND_LONG_FMT, host, port );
1046 		}
1047 
1048 #ifdef LDAP_API_FEATURE_X_OPENLDAP
1049 		/* ldap_init() is deprecated, use ldap_initialize() instead.
1050 		 */
1051 		rc = ldap_initialize(&ldap, url);
1052 #else /* ! LDAP_API_FEATURE_X_OPENLDAP */
1053 		/* ldap_init does not support URLs.
1054 		 * We must try the original host and port information.
1055 		 */
1056 		ldap = ldap_init(host, port);
1057 		if (ldap == NULL) {
1058 			efree(ld);
1059 			php_error_docref(NULL, E_WARNING, "Could not create session handle");
1060 			RETURN_FALSE;
1061 		}
1062 #endif /* ! LDAP_API_FEATURE_X_OPENLDAP */
1063 		if (url != host) {
1064 			efree(url);
1065 		}
1066 		if (rc != LDAP_SUCCESS) {
1067 			efree(ld);
1068 			php_error_docref(NULL, E_WARNING, "Could not create session handle: %s", ldap_err2string(rc));
1069 			RETURN_FALSE;
1070 		}
1071 	}
1072 
1073 	if (ldap == NULL) {
1074 		efree(ld);
1075 		RETURN_FALSE;
1076 	} else {
1077 #ifdef HAVE_ORALDAP
1078 		if (ssl) {
1079 			if (ldap_init_SSL(&ldap->ld_sb, wallet, walletpasswd, authmode)) {
1080 				efree(ld);
1081 				php_error_docref(NULL, E_WARNING, "SSL init failed");
1082 				RETURN_FALSE;
1083 			}
1084 		}
1085 #endif
1086 		LDAPG(num_links)++;
1087 		ld->link = ldap;
1088 		RETURN_RES(zend_register_resource(ld, le_link));
1089 	}
1090 
1091 }
1092 /* }}} */
1093 
1094 /* {{{ _get_lderrno
1095  */
_get_lderrno(LDAP * ldap)1096 static int _get_lderrno(LDAP *ldap)
1097 {
1098 #if LDAP_API_VERSION > 2000 || HAVE_ORALDAP
1099 	int lderr;
1100 
1101 	/* New versions of OpenLDAP do it this way */
1102 	ldap_get_option(ldap, LDAP_OPT_ERROR_NUMBER, &lderr);
1103 	return lderr;
1104 #else
1105 	return ldap->ld_errno;
1106 #endif
1107 }
1108 /* }}} */
1109 
1110 /* {{{ _set_lderrno
1111  */
_set_lderrno(LDAP * ldap,int lderr)1112 static void _set_lderrno(LDAP *ldap, int lderr)
1113 {
1114 #if LDAP_API_VERSION > 2000 || HAVE_ORALDAP
1115 	/* New versions of OpenLDAP do it this way */
1116 	ldap_set_option(ldap, LDAP_OPT_ERROR_NUMBER, &lderr);
1117 #else
1118 	ldap->ld_errno = lderr;
1119 #endif
1120 }
1121 /* }}} */
1122 
1123 /* {{{ proto bool ldap_bind(resource link [, string dn [, string password]])
1124    Bind to LDAP directory */
PHP_FUNCTION(ldap_bind)1125 PHP_FUNCTION(ldap_bind)
1126 {
1127 	zval *link;
1128 	char *ldap_bind_dn = NULL, *ldap_bind_pw = NULL;
1129 	size_t ldap_bind_dnlen, ldap_bind_pwlen;
1130 	ldap_linkdata *ld;
1131 	int rc;
1132 
1133 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|ss", &link, &ldap_bind_dn, &ldap_bind_dnlen, &ldap_bind_pw, &ldap_bind_pwlen) != SUCCESS) {
1134 		RETURN_FALSE;
1135 	}
1136 
1137 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
1138 		RETURN_FALSE;
1139 	}
1140 
1141 	if (ldap_bind_dn != NULL && memchr(ldap_bind_dn, '\0', ldap_bind_dnlen) != NULL) {
1142 		_set_lderrno(ld->link, LDAP_INVALID_CREDENTIALS);
1143 		php_error_docref(NULL, E_WARNING, "DN contains a null byte");
1144 		RETURN_FALSE;
1145 	}
1146 
1147 	if (ldap_bind_pw != NULL && memchr(ldap_bind_pw, '\0', ldap_bind_pwlen) != NULL) {
1148 		_set_lderrno(ld->link, LDAP_INVALID_CREDENTIALS);
1149 		php_error_docref(NULL, E_WARNING, "Password contains a null byte");
1150 		RETURN_FALSE;
1151 	}
1152 
1153 	{
1154 #ifdef LDAP_API_FEATURE_X_OPENLDAP
1155 		/* ldap_simple_bind_s() is deprecated, use ldap_sasl_bind_s() instead.
1156 		 */
1157 		struct berval   cred;
1158 
1159 		cred.bv_val = ldap_bind_pw;
1160 		cred.bv_len = ldap_bind_pw ? ldap_bind_pwlen : 0;
1161 		rc = ldap_sasl_bind_s(ld->link, ldap_bind_dn, LDAP_SASL_SIMPLE, &cred,
1162 				NULL, NULL,     /* no controls right now */
1163 				NULL);	  /* we don't care about the server's credentials */
1164 #else /* ! LDAP_API_FEATURE_X_OPENLDAP */
1165 		rc = ldap_simple_bind_s(ld->link, ldap_bind_dn, ldap_bind_pw);
1166 #endif /* ! LDAP_API_FEATURE_X_OPENLDAP */
1167 	}
1168 	if ( rc != LDAP_SUCCESS) {
1169 		php_error_docref(NULL, E_WARNING, "Unable to bind to server: %s", ldap_err2string(rc));
1170 		RETURN_FALSE;
1171 	} else {
1172 		RETURN_TRUE;
1173 	}
1174 }
1175 /* }}} */
1176 
1177 /* {{{ proto resource ldap_bind_ext(resource link [, string dn [, string password [, serverctrls]]])
1178    Bind to LDAP directory */
PHP_FUNCTION(ldap_bind_ext)1179 PHP_FUNCTION(ldap_bind_ext)
1180 {
1181 	zval *serverctrls = NULL;
1182 	zval *link;
1183 	char *ldap_bind_dn = NULL, *ldap_bind_pw = NULL;
1184 	size_t ldap_bind_dnlen, ldap_bind_pwlen;
1185 	ldap_linkdata *ld;
1186 	LDAPControl **lserverctrls = NULL;
1187 	LDAPMessage *ldap_res;
1188 	int rc;
1189 
1190 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|ssa", &link, &ldap_bind_dn, &ldap_bind_dnlen, &ldap_bind_pw, &ldap_bind_pwlen, &serverctrls) != SUCCESS) {
1191 		RETURN_FALSE;
1192 	}
1193 
1194 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
1195 		RETURN_FALSE;
1196 	}
1197 
1198 	if (ldap_bind_dn != NULL && memchr(ldap_bind_dn, '\0', ldap_bind_dnlen) != NULL) {
1199 		_set_lderrno(ld->link, LDAP_INVALID_CREDENTIALS);
1200 		php_error_docref(NULL, E_WARNING, "DN contains a null byte");
1201 		RETURN_FALSE;
1202 	}
1203 
1204 	if (ldap_bind_pw != NULL && memchr(ldap_bind_pw, '\0', ldap_bind_pwlen) != NULL) {
1205 		_set_lderrno(ld->link, LDAP_INVALID_CREDENTIALS);
1206 		php_error_docref(NULL, E_WARNING, "Password contains a null byte");
1207 		RETURN_FALSE;
1208 	}
1209 
1210 	if (serverctrls) {
1211 		lserverctrls = _php_ldap_controls_from_array(ld->link, serverctrls);
1212 		if (lserverctrls == NULL) {
1213 			RETVAL_FALSE;
1214 			goto cleanup;
1215 		}
1216 	}
1217 
1218 	{
1219 		/* ldap_simple_bind() is deprecated, use ldap_sasl_bind() instead */
1220 		struct berval   cred;
1221 		int msgid;
1222 
1223 		cred.bv_val = ldap_bind_pw;
1224 		cred.bv_len = ldap_bind_pw ? ldap_bind_pwlen : 0;
1225 		/* asynchronous call */
1226 		rc = ldap_sasl_bind(ld->link, ldap_bind_dn, LDAP_SASL_SIMPLE, &cred,
1227 				lserverctrls, NULL, &msgid);
1228 		if (rc != LDAP_SUCCESS ) {
1229 			php_error_docref(NULL, E_WARNING, "Unable to bind to server: %s (%d)", ldap_err2string(rc), rc);
1230 			RETVAL_FALSE;
1231 			goto cleanup;
1232 		}
1233 
1234 		rc = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res);
1235 		if (rc == -1) {
1236 			php_error_docref(NULL, E_WARNING, "Bind operation failed");
1237 			RETVAL_FALSE;
1238 			goto cleanup;
1239 		}
1240 
1241 		/* return a PHP control object */
1242 		RETVAL_RES(zend_register_resource(ldap_res, le_result));
1243 	}
1244 
1245 cleanup:
1246 	if (lserverctrls) {
1247 		_php_ldap_controls_free(&lserverctrls);
1248 	}
1249 
1250 	return;
1251 }
1252 /* }}} */
1253 
1254 #ifdef HAVE_LDAP_SASL
1255 typedef struct {
1256 	char *mech;
1257 	char *realm;
1258 	char *authcid;
1259 	char *passwd;
1260 	char *authzid;
1261 } php_ldap_bictx;
1262 
1263 /* {{{ _php_sasl_setdefs
1264  */
_php_sasl_setdefs(LDAP * ld,char * sasl_mech,char * sasl_realm,char * sasl_authc_id,char * passwd,char * sasl_authz_id)1265 static php_ldap_bictx *_php_sasl_setdefs(LDAP *ld, char *sasl_mech, char *sasl_realm, char *sasl_authc_id, char *passwd, char *sasl_authz_id)
1266 {
1267 	php_ldap_bictx *ctx;
1268 
1269 	ctx = ber_memalloc(sizeof(php_ldap_bictx));
1270 	ctx->mech    = (sasl_mech) ? ber_strdup(sasl_mech) : NULL;
1271 	ctx->realm   = (sasl_realm) ? ber_strdup(sasl_realm) : NULL;
1272 	ctx->authcid = (sasl_authc_id) ? ber_strdup(sasl_authc_id) : NULL;
1273 	ctx->passwd  = (passwd) ? ber_strdup(passwd) : NULL;
1274 	ctx->authzid = (sasl_authz_id) ? ber_strdup(sasl_authz_id) : NULL;
1275 
1276 	if (ctx->mech == NULL) {
1277 		ldap_get_option(ld, LDAP_OPT_X_SASL_MECH, &ctx->mech);
1278 	}
1279 	if (ctx->realm == NULL) {
1280 		ldap_get_option(ld, LDAP_OPT_X_SASL_REALM, &ctx->realm);
1281 	}
1282 	if (ctx->authcid == NULL) {
1283 		ldap_get_option(ld, LDAP_OPT_X_SASL_AUTHCID, &ctx->authcid);
1284 	}
1285 	if (ctx->authzid == NULL) {
1286 		ldap_get_option(ld, LDAP_OPT_X_SASL_AUTHZID, &ctx->authzid);
1287 	}
1288 
1289 	return ctx;
1290 }
1291 /* }}} */
1292 
1293 /* {{{ _php_sasl_freedefs
1294  */
_php_sasl_freedefs(php_ldap_bictx * ctx)1295 static void _php_sasl_freedefs(php_ldap_bictx *ctx)
1296 {
1297 	if (ctx->mech) ber_memfree(ctx->mech);
1298 	if (ctx->realm) ber_memfree(ctx->realm);
1299 	if (ctx->authcid) ber_memfree(ctx->authcid);
1300 	if (ctx->passwd) ber_memfree(ctx->passwd);
1301 	if (ctx->authzid) ber_memfree(ctx->authzid);
1302 	ber_memfree(ctx);
1303 }
1304 /* }}} */
1305 
1306 /* {{{ _php_sasl_interact
1307    Internal interact function for SASL */
_php_sasl_interact(LDAP * ld,unsigned flags,void * defaults,void * in)1308 static int _php_sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *in)
1309 {
1310 	sasl_interact_t *interact = in;
1311 	const char *p;
1312 	php_ldap_bictx *ctx = defaults;
1313 
1314 	for (;interact->id != SASL_CB_LIST_END;interact++) {
1315 		p = NULL;
1316 		switch(interact->id) {
1317 			case SASL_CB_GETREALM:
1318 				p = ctx->realm;
1319 				break;
1320 			case SASL_CB_AUTHNAME:
1321 				p = ctx->authcid;
1322 				break;
1323 			case SASL_CB_USER:
1324 				p = ctx->authzid;
1325 				break;
1326 			case SASL_CB_PASS:
1327 				p = ctx->passwd;
1328 				break;
1329 		}
1330 		if (p) {
1331 			interact->result = p;
1332 			interact->len = strlen(interact->result);
1333 		}
1334 	}
1335 	return LDAP_SUCCESS;
1336 }
1337 /* }}} */
1338 
1339 /* {{{ proto bool ldap_sasl_bind(resource link [, string binddn [, string password [, string sasl_mech [, string sasl_realm [, string sasl_authc_id [, string sasl_authz_id [, string props]]]]]]])
1340    Bind to LDAP directory using SASL */
PHP_FUNCTION(ldap_sasl_bind)1341 PHP_FUNCTION(ldap_sasl_bind)
1342 {
1343 	zval *link;
1344 	ldap_linkdata *ld;
1345 	char *binddn = NULL;
1346 	char *passwd = NULL;
1347 	char *sasl_mech = NULL;
1348 	char *sasl_realm = NULL;
1349 	char *sasl_authz_id = NULL;
1350 	char *sasl_authc_id = NULL;
1351 	char *props = NULL;
1352 	size_t rc, dn_len, passwd_len, mech_len, realm_len, authc_id_len, authz_id_len, props_len;
1353 	php_ldap_bictx *ctx;
1354 
1355 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|sssssss", &link, &binddn, &dn_len, &passwd, &passwd_len, &sasl_mech, &mech_len, &sasl_realm, &realm_len, &sasl_authc_id, &authc_id_len, &sasl_authz_id, &authz_id_len, &props, &props_len) != SUCCESS) {
1356 		RETURN_FALSE;
1357 	}
1358 
1359 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
1360 		RETURN_FALSE;
1361 	}
1362 
1363 	ctx = _php_sasl_setdefs(ld->link, sasl_mech, sasl_realm, sasl_authc_id, passwd, sasl_authz_id);
1364 
1365 	if (props) {
1366 		ldap_set_option(ld->link, LDAP_OPT_X_SASL_SECPROPS, props);
1367 	}
1368 
1369 	rc = ldap_sasl_interactive_bind_s(ld->link, binddn, ctx->mech, NULL, NULL, LDAP_SASL_QUIET, _php_sasl_interact, ctx);
1370 	if (rc != LDAP_SUCCESS) {
1371 		php_error_docref(NULL, E_WARNING, "Unable to bind to server: %s", ldap_err2string(rc));
1372 		RETVAL_FALSE;
1373 	} else {
1374 		RETVAL_TRUE;
1375 	}
1376 	_php_sasl_freedefs(ctx);
1377 }
1378 /* }}} */
1379 #endif /* HAVE_LDAP_SASL */
1380 
1381 /* {{{ proto bool ldap_unbind(resource link)
1382    Unbind from LDAP directory */
PHP_FUNCTION(ldap_unbind)1383 PHP_FUNCTION(ldap_unbind)
1384 {
1385 	zval *link;
1386 	ldap_linkdata *ld;
1387 
1388 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &link) != SUCCESS) {
1389 		RETURN_FALSE;
1390 	}
1391 
1392 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
1393 		RETURN_FALSE;
1394 	}
1395 
1396 	zend_list_close(Z_RES_P(link));
1397 	RETURN_TRUE;
1398 }
1399 /* }}} */
1400 
1401 /* {{{ php_set_opts
1402  */
php_set_opts(LDAP * ldap,int sizelimit,int timelimit,int deref,int * old_sizelimit,int * old_timelimit,int * old_deref)1403 static void php_set_opts(LDAP *ldap, int sizelimit, int timelimit, int deref, int *old_sizelimit, int *old_timelimit, int *old_deref)
1404 {
1405 	/* sizelimit */
1406 	if (sizelimit > -1) {
1407 #if (LDAP_API_VERSION >= 2004) || HAVE_ORALDAP
1408 		ldap_get_option(ldap, LDAP_OPT_SIZELIMIT, old_sizelimit);
1409 		ldap_set_option(ldap, LDAP_OPT_SIZELIMIT, &sizelimit);
1410 #else
1411 		*old_sizelimit = ldap->ld_sizelimit;
1412 		ldap->ld_sizelimit = sizelimit;
1413 #endif
1414 	}
1415 
1416 	/* timelimit */
1417 	if (timelimit > -1) {
1418 #if (LDAP_API_VERSION >= 2004) || HAVE_ORALDAP
1419 		ldap_get_option(ldap, LDAP_OPT_TIMELIMIT, old_timelimit);
1420 		ldap_set_option(ldap, LDAP_OPT_TIMELIMIT, &timelimit);
1421 #else
1422 		*old_timelimit = ldap->ld_timelimit;
1423 		ldap->ld_timelimit = timelimit;
1424 #endif
1425 	}
1426 
1427 	/* deref */
1428 	if (deref > -1) {
1429 #if (LDAP_API_VERSION >= 2004) || HAVE_ORALDAP
1430 		ldap_get_option(ldap, LDAP_OPT_DEREF, old_deref);
1431 		ldap_set_option(ldap, LDAP_OPT_DEREF, &deref);
1432 #else
1433 		*old_deref = ldap->ld_deref;
1434 		ldap->ld_deref = deref;
1435 #endif
1436 	}
1437 }
1438 /* }}} */
1439 
1440 /* {{{ php_ldap_do_search
1441  */
php_ldap_do_search(INTERNAL_FUNCTION_PARAMETERS,int scope)1442 static void php_ldap_do_search(INTERNAL_FUNCTION_PARAMETERS, int scope)
1443 {
1444 	zval *link, *base_dn, *filter, *attrs = NULL, *attr, *serverctrls = NULL;
1445 	zend_long attrsonly, sizelimit, timelimit, deref;
1446 	zend_string *ldap_filter = NULL, *ldap_base_dn = NULL;
1447 	char **ldap_attrs = NULL;
1448 	ldap_linkdata *ld = NULL;
1449 	LDAPMessage *ldap_res = NULL;
1450 	LDAPControl **lserverctrls = NULL;
1451 	int ldap_attrsonly = 0, ldap_sizelimit = -1, ldap_timelimit = -1, ldap_deref = -1;
1452 	int old_ldap_sizelimit = -1, old_ldap_timelimit = -1, old_ldap_deref = -1;
1453 	int num_attribs = 0, ret = 1, i, errno, argcount = ZEND_NUM_ARGS();
1454 
1455 	if (zend_parse_parameters(argcount, "zzz|a/lllla/", &link, &base_dn, &filter, &attrs, &attrsonly,
1456 		&sizelimit, &timelimit, &deref, &serverctrls) == FAILURE) {
1457 		return;
1458 	}
1459 
1460 	/* Reverse -> fall through */
1461 	switch (argcount) {
1462 		case 9:
1463 		case 8:
1464 			ldap_deref = deref;
1465 		case 7:
1466 			ldap_timelimit = timelimit;
1467 		case 6:
1468 			ldap_sizelimit = sizelimit;
1469 		case 5:
1470 			ldap_attrsonly = attrsonly;
1471 		case 4:
1472 			num_attribs = zend_hash_num_elements(Z_ARRVAL_P(attrs));
1473 			ldap_attrs = safe_emalloc((num_attribs+1), sizeof(char *), 0);
1474 
1475 			for (i = 0; i<num_attribs; i++) {
1476 				if ((attr = zend_hash_index_find(Z_ARRVAL_P(attrs), i)) == NULL) {
1477 					php_error_docref(NULL, E_WARNING, "Array initialization wrong");
1478 					ret = 0;
1479 					goto cleanup;
1480 				}
1481 
1482 				convert_to_string(attr);
1483 				if (EG(exception)) {
1484 					ret = 0;
1485 					goto cleanup;
1486 				}
1487 				ldap_attrs[i] = Z_STRVAL_P(attr);
1488 			}
1489 			ldap_attrs[num_attribs] = NULL;
1490 		default:
1491 			break;
1492 	}
1493 
1494 	/* parallel search? */
1495 	if (Z_TYPE_P(link) == IS_ARRAY) {
1496 		int i, nlinks, nbases, nfilters, *rcs;
1497 		ldap_linkdata **lds;
1498 		zval *entry, resource;
1499 
1500 		nlinks = zend_hash_num_elements(Z_ARRVAL_P(link));
1501 		if (nlinks == 0) {
1502 			php_error_docref(NULL, E_WARNING, "No links in link array");
1503 			ret = 0;
1504 			goto cleanup;
1505 		}
1506 
1507 		if (Z_TYPE_P(base_dn) == IS_ARRAY) {
1508 			nbases = zend_hash_num_elements(Z_ARRVAL_P(base_dn));
1509 			if (nbases != nlinks) {
1510 				php_error_docref(NULL, E_WARNING, "Base must either be a string, or an array with the same number of elements as the links array");
1511 				ret = 0;
1512 				goto cleanup;
1513 			}
1514 			zend_hash_internal_pointer_reset(Z_ARRVAL_P(base_dn));
1515 		} else {
1516 			nbases = 0; /* this means string, not array */
1517 			ldap_base_dn = zval_get_string(base_dn);
1518 			if (EG(exception)) {
1519 				ret = 0;
1520 				goto cleanup;
1521 			}
1522 		}
1523 
1524 		if (Z_TYPE_P(filter) == IS_ARRAY) {
1525 			nfilters = zend_hash_num_elements(Z_ARRVAL_P(filter));
1526 			if (nfilters != nlinks) {
1527 				php_error_docref(NULL, E_WARNING, "Filter must either be a string, or an array with the same number of elements as the links array");
1528 				ret = 0;
1529 				goto cleanup;
1530 			}
1531 			zend_hash_internal_pointer_reset(Z_ARRVAL_P(filter));
1532 		} else {
1533 			nfilters = 0; /* this means string, not array */
1534 			ldap_filter = zval_get_string(filter);
1535 			if (EG(exception)) {
1536 				ret = 0;
1537 				goto cleanup;
1538 			}
1539 		}
1540 
1541 		lds = safe_emalloc(nlinks, sizeof(ldap_linkdata), 0);
1542 		rcs = safe_emalloc(nlinks, sizeof(*rcs), 0);
1543 
1544 		zend_hash_internal_pointer_reset(Z_ARRVAL_P(link));
1545 		for (i=0; i<nlinks; i++) {
1546 			entry = zend_hash_get_current_data(Z_ARRVAL_P(link));
1547 
1548 			ld = (ldap_linkdata *) zend_fetch_resource_ex(entry, "ldap link", le_link);
1549 			if (ld == NULL) {
1550 				ret = 0;
1551 				goto cleanup_parallel;
1552 			}
1553 			if (nbases != 0) { /* base_dn an array? */
1554 				entry = zend_hash_get_current_data(Z_ARRVAL_P(base_dn));
1555 				zend_hash_move_forward(Z_ARRVAL_P(base_dn));
1556 				ldap_base_dn = zval_get_string(entry);
1557 				if (EG(exception)) {
1558 					ret = 0;
1559 					goto cleanup_parallel;
1560 				}
1561 			}
1562 			if (nfilters != 0) { /* filter an array? */
1563 				entry = zend_hash_get_current_data(Z_ARRVAL_P(filter));
1564 				zend_hash_move_forward(Z_ARRVAL_P(filter));
1565 				ldap_filter = zval_get_string(entry);
1566 				if (EG(exception)) {
1567 					ret = 0;
1568 					goto cleanup_parallel;
1569 				}
1570 			}
1571 
1572 			if (argcount > 8) {
1573 				/* We have to parse controls again for each link as they use it */
1574 				_php_ldap_controls_free(&lserverctrls);
1575 				lserverctrls = _php_ldap_controls_from_array(ld->link, serverctrls);
1576 				if (lserverctrls == NULL) {
1577 					rcs[i] = -1;
1578 					continue;
1579 				}
1580 			}
1581 
1582 			php_set_opts(ld->link, ldap_sizelimit, ldap_timelimit, ldap_deref, &old_ldap_sizelimit, &old_ldap_timelimit, &old_ldap_deref);
1583 
1584 			/* Run the actual search */
1585 			ldap_search_ext(ld->link, ZSTR_VAL(ldap_base_dn), scope, ZSTR_VAL(ldap_filter), ldap_attrs, ldap_attrsonly, lserverctrls, NULL, NULL, ldap_sizelimit, &rcs[i]);
1586 			lds[i] = ld;
1587 			zend_hash_move_forward(Z_ARRVAL_P(link));
1588 		}
1589 
1590 		array_init(return_value);
1591 
1592 		/* Collect results from the searches */
1593 		for (i=0; i<nlinks; i++) {
1594 			if (rcs[i] != -1) {
1595 				rcs[i] = ldap_result(lds[i]->link, LDAP_RES_ANY, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res);
1596 			}
1597 			if (rcs[i] != -1) {
1598 				ZVAL_RES(&resource, zend_register_resource(ldap_res, le_result));
1599 				add_next_index_zval(return_value, &resource);
1600 			} else {
1601 				add_next_index_bool(return_value, 0);
1602 			}
1603 		}
1604 
1605 cleanup_parallel:
1606 		efree(lds);
1607 		efree(rcs);
1608 	} else {
1609 		ldap_filter = zval_get_string(filter);
1610 		if (EG(exception)) {
1611 			ret = 0;
1612 			goto cleanup;
1613 		}
1614 
1615 		ldap_base_dn = zval_get_string(base_dn);
1616 		if (EG(exception)) {
1617 			ret = 0;
1618 			goto cleanup;
1619 		}
1620 
1621 		ld = (ldap_linkdata *) zend_fetch_resource_ex(link, "ldap link", le_link);
1622 		if (ld == NULL) {
1623 			ret = 0;
1624 			goto cleanup;
1625 		}
1626 
1627 		if (argcount > 8) {
1628 			lserverctrls = _php_ldap_controls_from_array(ld->link, serverctrls);
1629 			if (lserverctrls == NULL) {
1630 				ret = 0;
1631 				goto cleanup;
1632 			}
1633 		}
1634 
1635 		php_set_opts(ld->link, ldap_sizelimit, ldap_timelimit, ldap_deref, &old_ldap_sizelimit, &old_ldap_timelimit, &old_ldap_deref);
1636 
1637 		/* Run the actual search */
1638 		errno = ldap_search_ext_s(ld->link, ZSTR_VAL(ldap_base_dn), scope, ZSTR_VAL(ldap_filter), ldap_attrs, ldap_attrsonly, lserverctrls, NULL, NULL, ldap_sizelimit, &ldap_res);
1639 
1640 		if (errno != LDAP_SUCCESS
1641 			&& errno != LDAP_SIZELIMIT_EXCEEDED
1642 #ifdef LDAP_ADMINLIMIT_EXCEEDED
1643 			&& errno != LDAP_ADMINLIMIT_EXCEEDED
1644 #endif
1645 #ifdef LDAP_REFERRAL
1646 			&& errno != LDAP_REFERRAL
1647 #endif
1648 		) {
1649 			/* ldap_res should be freed regardless of return value of ldap_search_ext_s()
1650 			 * see: https://linux.die.net/man/3/ldap_search_ext_s */
1651 			if (ldap_res != NULL) {
1652 				ldap_msgfree(ldap_res);
1653 			}
1654 			php_error_docref(NULL, E_WARNING, "Search: %s", ldap_err2string(errno));
1655 			ret = 0;
1656 		} else {
1657 			if (errno == LDAP_SIZELIMIT_EXCEEDED) {
1658 				php_error_docref(NULL, E_WARNING, "Partial search results returned: Sizelimit exceeded");
1659 			}
1660 #ifdef LDAP_ADMINLIMIT_EXCEEDED
1661 			else if (errno == LDAP_ADMINLIMIT_EXCEEDED) {
1662 				php_error_docref(NULL, E_WARNING, "Partial search results returned: Adminlimit exceeded");
1663 			}
1664 #endif
1665 
1666 			RETVAL_RES(zend_register_resource(ldap_res, le_result));
1667 		}
1668 	}
1669 
1670 cleanup:
1671 	if (ld) {
1672 		/* Restoring previous options */
1673 		php_set_opts(ld->link, old_ldap_sizelimit, old_ldap_timelimit, old_ldap_deref, &ldap_sizelimit, &ldap_timelimit, &ldap_deref);
1674 	}
1675 	if (ldap_filter) {
1676 		zend_string_release(ldap_filter);
1677 	}
1678 	if (ldap_base_dn) {
1679 		zend_string_release(ldap_base_dn);
1680 	}
1681 	if (ldap_attrs != NULL) {
1682 		efree(ldap_attrs);
1683 	}
1684 	if (!ret) {
1685 		RETVAL_BOOL(ret);
1686 	}
1687 	if (lserverctrls) {
1688 		_php_ldap_controls_free(&lserverctrls);
1689 	}
1690 }
1691 /* }}} */
1692 
1693 /* {{{ proto resource ldap_read(resource|array link, string base_dn, string filter [, array attrs [, int attrsonly [, int sizelimit [, int timelimit [, int deref [, array servercontrols]]]]]])
1694    Read an entry */
PHP_FUNCTION(ldap_read)1695 PHP_FUNCTION(ldap_read)
1696 {
1697 	php_ldap_do_search(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_SCOPE_BASE);
1698 }
1699 /* }}} */
1700 
1701 /* {{{ proto resource ldap_list(resource|array link, string base_dn, string filter [, array attrs [, int attrsonly [, int sizelimit [, int timelimit [, int deref [, array servercontrols]]]]]])
1702    Single-level search */
PHP_FUNCTION(ldap_list)1703 PHP_FUNCTION(ldap_list)
1704 {
1705 	php_ldap_do_search(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_SCOPE_ONELEVEL);
1706 }
1707 /* }}} */
1708 
1709 /* {{{ proto resource ldap_search(resource|array link, string base_dn, string filter [, array attrs [, int attrsonly [, int sizelimit [, int timelimit [, int deref [, array servercontrols]]]]]])
1710    Search LDAP tree under base_dn */
PHP_FUNCTION(ldap_search)1711 PHP_FUNCTION(ldap_search)
1712 {
1713 	php_ldap_do_search(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_SCOPE_SUBTREE);
1714 }
1715 /* }}} */
1716 
1717 /* {{{ proto bool ldap_free_result(resource result)
1718    Free result memory */
PHP_FUNCTION(ldap_free_result)1719 PHP_FUNCTION(ldap_free_result)
1720 {
1721 	zval *result;
1722 	LDAPMessage *ldap_result;
1723 
1724 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &result) != SUCCESS) {
1725 		return;
1726 	}
1727 
1728 	if ((ldap_result = (LDAPMessage *)zend_fetch_resource(Z_RES_P(result), "ldap result", le_result)) == NULL) {
1729 		RETURN_FALSE;
1730 	}
1731 
1732 	zend_list_close(Z_RES_P(result));  /* Delete list entry */
1733 	RETVAL_TRUE;
1734 }
1735 /* }}} */
1736 
1737 /* {{{ proto int ldap_count_entries(resource link, resource result)
1738    Count the number of entries in a search result */
PHP_FUNCTION(ldap_count_entries)1739 PHP_FUNCTION(ldap_count_entries)
1740 {
1741 	zval *link, *result;
1742 	ldap_linkdata *ld;
1743 	LDAPMessage *ldap_result;
1744 
1745 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &link, &result) != SUCCESS) {
1746 		return;
1747 	}
1748 
1749 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
1750 		RETURN_FALSE;
1751 	}
1752 
1753 	if ((ldap_result = (LDAPMessage *)zend_fetch_resource(Z_RES_P(result), "ldap result", le_result)) == NULL) {
1754 		RETURN_FALSE;
1755 	}
1756 
1757 	RETURN_LONG(ldap_count_entries(ld->link, ldap_result));
1758 }
1759 /* }}} */
1760 
1761 /* {{{ proto resource ldap_first_entry(resource link, resource result)
1762    Return first result id */
PHP_FUNCTION(ldap_first_entry)1763 PHP_FUNCTION(ldap_first_entry)
1764 {
1765 	zval *link, *result;
1766 	ldap_linkdata *ld;
1767 	ldap_resultentry *resultentry;
1768 	LDAPMessage *ldap_result, *entry;
1769 
1770 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &link, &result) != SUCCESS) {
1771 		return;
1772 	}
1773 
1774 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
1775 		RETURN_FALSE;
1776 	}
1777 
1778 	if ((ldap_result = (LDAPMessage *)zend_fetch_resource(Z_RES_P(result), "ldap result", le_result)) == NULL) {
1779 		RETURN_FALSE;
1780 	}
1781 
1782 	if ((entry = ldap_first_entry(ld->link, ldap_result)) == NULL) {
1783 		RETVAL_FALSE;
1784 	} else {
1785 		resultentry = emalloc(sizeof(ldap_resultentry));
1786 		RETVAL_RES(zend_register_resource(resultentry, le_result_entry));
1787 		ZVAL_COPY(&resultentry->res, result);
1788 		resultentry->data = entry;
1789 		resultentry->ber = NULL;
1790 	}
1791 }
1792 /* }}} */
1793 
1794 /* {{{ proto resource ldap_next_entry(resource link, resource result_entry)
1795    Get next result entry */
PHP_FUNCTION(ldap_next_entry)1796 PHP_FUNCTION(ldap_next_entry)
1797 {
1798 	zval *link, *result_entry;
1799 	ldap_linkdata *ld;
1800 	ldap_resultentry *resultentry, *resultentry_next;
1801 	LDAPMessage *entry_next;
1802 
1803 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &link, &result_entry) != SUCCESS) {
1804 		return;
1805 	}
1806 
1807 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
1808 		RETURN_FALSE;
1809 	}
1810 	if ((resultentry = (ldap_resultentry *)zend_fetch_resource(Z_RES_P(result_entry), "ldap result entry", le_result_entry)) == NULL) {
1811 		RETURN_FALSE;
1812 	}
1813 
1814 	if ((entry_next = ldap_next_entry(ld->link, resultentry->data)) == NULL) {
1815 		RETVAL_FALSE;
1816 	} else {
1817 		resultentry_next = emalloc(sizeof(ldap_resultentry));
1818 		RETVAL_RES(zend_register_resource(resultentry_next, le_result_entry));
1819 		ZVAL_COPY(&resultentry_next->res, &resultentry->res);
1820 		resultentry_next->data = entry_next;
1821 		resultentry_next->ber = NULL;
1822 	}
1823 }
1824 /* }}} */
1825 
1826 /* {{{ proto array ldap_get_entries(resource link, resource result)
1827    Get all result entries */
PHP_FUNCTION(ldap_get_entries)1828 PHP_FUNCTION(ldap_get_entries)
1829 {
1830 	zval *link, *result;
1831 	LDAPMessage *ldap_result, *ldap_result_entry;
1832 	zval tmp1, tmp2;
1833 	ldap_linkdata *ld;
1834 	LDAP *ldap;
1835 	int num_entries, num_attrib, num_values, i;
1836 	BerElement *ber;
1837 	char *attribute;
1838 	size_t attr_len;
1839 	struct berval **ldap_value;
1840 	char *dn;
1841 
1842 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &link, &result) != SUCCESS) {
1843 		return;
1844 	}
1845 
1846 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
1847 		RETURN_FALSE;
1848 	}
1849 	if ((ldap_result = (LDAPMessage *)zend_fetch_resource(Z_RES_P(result), "ldap result", le_result)) == NULL) {
1850 		RETURN_FALSE;
1851 	}
1852 
1853 	ldap = ld->link;
1854 	num_entries = ldap_count_entries(ldap, ldap_result);
1855 
1856 	array_init(return_value);
1857 	add_assoc_long(return_value, "count", num_entries);
1858 
1859 	if (num_entries == 0) {
1860 		return;
1861 	}
1862 
1863 	ldap_result_entry = ldap_first_entry(ldap, ldap_result);
1864 	if (ldap_result_entry == NULL) {
1865 		zend_array_destroy(Z_ARR_P(return_value));
1866 		RETURN_FALSE;
1867 	}
1868 
1869 	num_entries = 0;
1870 	while (ldap_result_entry != NULL) {
1871 		array_init(&tmp1);
1872 
1873 		num_attrib = 0;
1874 		attribute = ldap_first_attribute(ldap, ldap_result_entry, &ber);
1875 
1876 		while (attribute != NULL) {
1877 			ldap_value = ldap_get_values_len(ldap, ldap_result_entry, attribute);
1878 			num_values = ldap_count_values_len(ldap_value);
1879 
1880 			array_init(&tmp2);
1881 			add_assoc_long(&tmp2, "count", num_values);
1882 			for (i = 0; i < num_values; i++) {
1883 				add_index_stringl(&tmp2, i, ldap_value[i]->bv_val, ldap_value[i]->bv_len);
1884 			}
1885 			ldap_value_free_len(ldap_value);
1886 
1887 			attr_len = strlen(attribute);
1888 			zend_hash_str_update(Z_ARRVAL(tmp1), php_strtolower(attribute, attr_len), attr_len, &tmp2);
1889 			add_index_string(&tmp1, num_attrib, attribute);
1890 
1891 			num_attrib++;
1892 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP || WINDOWS
1893 			ldap_memfree(attribute);
1894 #endif
1895 			attribute = ldap_next_attribute(ldap, ldap_result_entry, ber);
1896 		}
1897 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP || WINDOWS
1898 		if (ber != NULL) {
1899 			ber_free(ber, 0);
1900 		}
1901 #endif
1902 
1903 		add_assoc_long(&tmp1, "count", num_attrib);
1904 		dn = ldap_get_dn(ldap, ldap_result_entry);
1905 		if (dn) {
1906 			add_assoc_string(&tmp1, "dn", dn);
1907 		} else {
1908 			add_assoc_null(&tmp1, "dn");
1909 		}
1910 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP || WINDOWS
1911 		ldap_memfree(dn);
1912 #else
1913 		free(dn);
1914 #endif
1915 
1916 		zend_hash_index_update(Z_ARRVAL_P(return_value), num_entries, &tmp1);
1917 
1918 		num_entries++;
1919 		ldap_result_entry = ldap_next_entry(ldap, ldap_result_entry);
1920 	}
1921 
1922 	add_assoc_long(return_value, "count", num_entries);
1923 
1924 }
1925 /* }}} */
1926 
1927 /* {{{ proto string ldap_first_attribute(resource link, resource result_entry)
1928    Return first attribute */
PHP_FUNCTION(ldap_first_attribute)1929 PHP_FUNCTION(ldap_first_attribute)
1930 {
1931 	zval *link, *result_entry;
1932 	ldap_linkdata *ld;
1933 	ldap_resultentry *resultentry;
1934 	char *attribute;
1935 	zend_long dummy_ber;
1936 
1937 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr|l", &link, &result_entry, &dummy_ber) != SUCCESS) {
1938 		return;
1939 	}
1940 
1941 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
1942 		RETURN_FALSE;
1943 	}
1944 
1945 	if ((resultentry = (ldap_resultentry *)zend_fetch_resource(Z_RES_P(result_entry), "ldap result entry", le_result_entry)) == NULL) {
1946 		RETURN_FALSE;
1947 	}
1948 
1949 	if ((attribute = ldap_first_attribute(ld->link, resultentry->data, &resultentry->ber)) == NULL) {
1950 		RETURN_FALSE;
1951 	} else {
1952 		RETVAL_STRING(attribute);
1953 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP || WINDOWS
1954 		ldap_memfree(attribute);
1955 #endif
1956 	}
1957 }
1958 /* }}} */
1959 
1960 /* {{{ proto string ldap_next_attribute(resource link, resource result_entry)
1961    Get the next attribute in result */
PHP_FUNCTION(ldap_next_attribute)1962 PHP_FUNCTION(ldap_next_attribute)
1963 {
1964 	zval *link, *result_entry;
1965 	ldap_linkdata *ld;
1966 	ldap_resultentry *resultentry;
1967 	char *attribute;
1968 	zend_long dummy_ber;
1969 
1970 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr|l", &link, &result_entry, &dummy_ber) != SUCCESS) {
1971 		return;
1972 	}
1973 
1974 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
1975 		RETURN_FALSE;
1976 	}
1977 
1978 	if ((resultentry = (ldap_resultentry *)zend_fetch_resource(Z_RES_P(result_entry), "ldap result entry", le_result_entry)) == NULL) {
1979 		RETURN_FALSE;
1980 	}
1981 
1982 	if (resultentry->ber == NULL) {
1983 		php_error_docref(NULL, E_WARNING, "called before calling ldap_first_attribute() or no attributes found in result entry");
1984 		RETURN_FALSE;
1985 	}
1986 
1987 	if ((attribute = ldap_next_attribute(ld->link, resultentry->data, resultentry->ber)) == NULL) {
1988 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP || WINDOWS
1989 		if (resultentry->ber != NULL) {
1990 			ber_free(resultentry->ber, 0);
1991 			resultentry->ber = NULL;
1992 		}
1993 #endif
1994 		RETURN_FALSE;
1995 	} else {
1996 		RETVAL_STRING(attribute);
1997 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP || WINDOWS
1998 		ldap_memfree(attribute);
1999 #endif
2000 	}
2001 }
2002 /* }}} */
2003 
2004 /* {{{ proto array ldap_get_attributes(resource link, resource result_entry)
2005    Get attributes from a search result entry */
PHP_FUNCTION(ldap_get_attributes)2006 PHP_FUNCTION(ldap_get_attributes)
2007 {
2008 	zval *link, *result_entry;
2009 	zval tmp;
2010 	ldap_linkdata *ld;
2011 	ldap_resultentry *resultentry;
2012 	char *attribute;
2013 	struct berval **ldap_value;
2014 	int i, num_values, num_attrib;
2015 	BerElement *ber;
2016 
2017 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &link, &result_entry) != SUCCESS) {
2018 		return;
2019 	}
2020 
2021 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
2022 		RETURN_FALSE;
2023 	}
2024 
2025 	if ((resultentry = (ldap_resultentry *)zend_fetch_resource(Z_RES_P(result_entry), "ldap result entry", le_result_entry)) == NULL) {
2026 		RETURN_FALSE;
2027 	}
2028 
2029 	array_init(return_value);
2030 	num_attrib = 0;
2031 
2032 	attribute = ldap_first_attribute(ld->link, resultentry->data, &ber);
2033 	while (attribute != NULL) {
2034 		ldap_value = ldap_get_values_len(ld->link, resultentry->data, attribute);
2035 		num_values = ldap_count_values_len(ldap_value);
2036 
2037 		array_init(&tmp);
2038 		add_assoc_long(&tmp, "count", num_values);
2039 		for (i = 0; i < num_values; i++) {
2040 			add_index_stringl(&tmp, i, ldap_value[i]->bv_val, ldap_value[i]->bv_len);
2041 		}
2042 		ldap_value_free_len(ldap_value);
2043 
2044 		zend_hash_str_update(Z_ARRVAL_P(return_value), attribute, strlen(attribute), &tmp);
2045 		add_index_string(return_value, num_attrib, attribute);
2046 
2047 		num_attrib++;
2048 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP || WINDOWS
2049 		ldap_memfree(attribute);
2050 #endif
2051 		attribute = ldap_next_attribute(ld->link, resultentry->data, ber);
2052 	}
2053 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP || WINDOWS
2054 	if (ber != NULL) {
2055 		ber_free(ber, 0);
2056 	}
2057 #endif
2058 
2059 	add_assoc_long(return_value, "count", num_attrib);
2060 }
2061 /* }}} */
2062 
2063 /* {{{ proto array ldap_get_values_len(resource link, resource result_entry, string attribute)
2064    Get all values with lengths from a result entry */
PHP_FUNCTION(ldap_get_values_len)2065 PHP_FUNCTION(ldap_get_values_len)
2066 {
2067 	zval *link, *result_entry;
2068 	ldap_linkdata *ld;
2069 	ldap_resultentry *resultentry;
2070 	char *attr;
2071 	struct berval **ldap_value_len;
2072 	int i, num_values;
2073 	size_t attr_len;
2074 
2075 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rrs", &link, &result_entry, &attr, &attr_len) != SUCCESS) {
2076 		return;
2077 	}
2078 
2079 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
2080 		RETURN_FALSE;
2081 	}
2082 
2083 	if ((resultentry = (ldap_resultentry *)zend_fetch_resource(Z_RES_P(result_entry), "ldap result entry", le_result_entry)) == NULL) {
2084 		RETURN_FALSE;
2085 	}
2086 
2087 	if ((ldap_value_len = ldap_get_values_len(ld->link, resultentry->data, attr)) == NULL) {
2088 		php_error_docref(NULL, E_WARNING, "Cannot get the value(s) of attribute %s", ldap_err2string(_get_lderrno(ld->link)));
2089 		RETURN_FALSE;
2090 	}
2091 
2092 	num_values = ldap_count_values_len(ldap_value_len);
2093 	array_init(return_value);
2094 
2095 	for (i=0; i<num_values; i++) {
2096 		add_next_index_stringl(return_value, ldap_value_len[i]->bv_val, ldap_value_len[i]->bv_len);
2097 	}
2098 
2099 	add_assoc_long(return_value, "count", num_values);
2100 	ldap_value_free_len(ldap_value_len);
2101 
2102 }
2103 /* }}} */
2104 
2105 /* {{{ proto string ldap_get_dn(resource link, resource result_entry)
2106    Get the DN of a result entry */
PHP_FUNCTION(ldap_get_dn)2107 PHP_FUNCTION(ldap_get_dn)
2108 {
2109 	zval *link, *result_entry;
2110 	ldap_linkdata *ld;
2111 	ldap_resultentry *resultentry;
2112 	char *text;
2113 
2114 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &link, &result_entry) != SUCCESS) {
2115 		return;
2116 	}
2117 
2118 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
2119 		RETURN_FALSE;
2120 	}
2121 
2122 	if ((resultentry = (ldap_resultentry *)zend_fetch_resource(Z_RES_P(result_entry), "ldap result entry", le_result_entry)) == NULL) {
2123 		RETURN_FALSE;
2124 	}
2125 
2126 	text = ldap_get_dn(ld->link, resultentry->data);
2127 	if (text != NULL) {
2128 		RETVAL_STRING(text);
2129 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP || WINDOWS
2130 		ldap_memfree(text);
2131 #else
2132 		free(text);
2133 #endif
2134 	} else {
2135 		RETURN_FALSE;
2136 	}
2137 }
2138 /* }}} */
2139 
2140 /* {{{ proto array ldap_explode_dn(string dn, int with_attrib)
2141    Splits DN into its component parts */
PHP_FUNCTION(ldap_explode_dn)2142 PHP_FUNCTION(ldap_explode_dn)
2143 {
2144 	zend_long with_attrib;
2145 	char *dn, **ldap_value;
2146 	int i, count;
2147 	size_t dn_len;
2148 
2149 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl", &dn, &dn_len, &with_attrib) != SUCCESS) {
2150 		return;
2151 	}
2152 
2153 	if (!(ldap_value = ldap_explode_dn(dn, with_attrib))) {
2154 		/* Invalid parameters were passed to ldap_explode_dn */
2155 		RETURN_FALSE;
2156 	}
2157 
2158 	i=0;
2159 	while (ldap_value[i] != NULL) i++;
2160 	count = i;
2161 
2162 	array_init(return_value);
2163 
2164 	add_assoc_long(return_value, "count", count);
2165 	for (i = 0; i<count; i++) {
2166 		add_index_string(return_value, i, ldap_value[i]);
2167 	}
2168 
2169 	ldap_memvfree((void **)ldap_value);
2170 }
2171 /* }}} */
2172 
2173 /* {{{ proto string ldap_dn2ufn(string dn)
2174    Convert DN to User Friendly Naming format */
PHP_FUNCTION(ldap_dn2ufn)2175 PHP_FUNCTION(ldap_dn2ufn)
2176 {
2177 	char *dn, *ufn;
2178 	size_t dn_len;
2179 
2180 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &dn, &dn_len) != SUCCESS) {
2181 		return;
2182 	}
2183 
2184 	ufn = ldap_dn2ufn(dn);
2185 
2186 	if (ufn != NULL) {
2187 		RETVAL_STRING(ufn);
2188 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP || WINDOWS
2189 		ldap_memfree(ufn);
2190 #endif
2191 	} else {
2192 		RETURN_FALSE;
2193 	}
2194 }
2195 /* }}} */
2196 
2197 
2198 /* added to fix use of ldap_modify_add for doing an ldap_add, gerrit thomson. */
2199 #define PHP_LD_FULL_ADD 0xff
2200 /* {{{ php_ldap_do_modify
2201  */
php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS,int oper,int ext)2202 static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper, int ext)
2203 {
2204 	zval *serverctrls = NULL;
2205 	zval *link, *entry, *value, *ivalue;
2206 	ldap_linkdata *ld;
2207 	char *dn;
2208 	LDAPMod **ldap_mods;
2209 	LDAPControl **lserverctrls = NULL;
2210 	LDAPMessage *ldap_res;
2211 	int i, j, num_attribs, num_values, msgid;
2212 	size_t dn_len;
2213 	int *num_berval;
2214 	zend_string *attribute;
2215 	zend_ulong index;
2216 	int is_full_add=0; /* flag for full add operation so ldap_mod_add can be put back into oper, gerrit THomson */
2217 
2218 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsa/|a", &link, &dn, &dn_len, &entry, &serverctrls) != SUCCESS) {
2219 		return;
2220 	}
2221 
2222 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
2223 		RETURN_FALSE;
2224 	}
2225 
2226 	num_attribs = zend_hash_num_elements(Z_ARRVAL_P(entry));
2227 	ldap_mods = safe_emalloc((num_attribs+1), sizeof(LDAPMod *), 0);
2228 	num_berval = safe_emalloc(num_attribs, sizeof(int), 0);
2229 	zend_hash_internal_pointer_reset(Z_ARRVAL_P(entry));
2230 
2231 	/* added by gerrit thomson to fix ldap_add using ldap_mod_add */
2232 	if (oper == PHP_LD_FULL_ADD) {
2233 		oper = LDAP_MOD_ADD;
2234 		is_full_add = 1;
2235 	}
2236 	/* end additional , gerrit thomson */
2237 
2238 	for (i = 0; i < num_attribs; i++) {
2239 		ldap_mods[i] = emalloc(sizeof(LDAPMod));
2240 		ldap_mods[i]->mod_op = oper | LDAP_MOD_BVALUES;
2241 		ldap_mods[i]->mod_type = NULL;
2242 
2243 		if (zend_hash_get_current_key(Z_ARRVAL_P(entry), &attribute, &index) == HASH_KEY_IS_STRING) {
2244 			ldap_mods[i]->mod_type = estrndup(ZSTR_VAL(attribute), ZSTR_LEN(attribute));
2245 		} else {
2246 			php_error_docref(NULL, E_WARNING, "Unknown attribute in the data");
2247 			/* Free allocated memory */
2248 			while (i >= 0) {
2249 				if (ldap_mods[i]->mod_type) {
2250 					efree(ldap_mods[i]->mod_type);
2251 				}
2252 				efree(ldap_mods[i]);
2253 				i--;
2254 			}
2255 			efree(num_berval);
2256 			efree(ldap_mods);
2257 			RETURN_FALSE;
2258 		}
2259 
2260 		value = zend_hash_get_current_data(Z_ARRVAL_P(entry));
2261 
2262 		ZVAL_DEREF(value);
2263 		if (Z_TYPE_P(value) != IS_ARRAY) {
2264 			num_values = 1;
2265 		} else {
2266 			SEPARATE_ARRAY(value);
2267 			num_values = zend_hash_num_elements(Z_ARRVAL_P(value));
2268 		}
2269 
2270 		num_berval[i] = num_values;
2271 		ldap_mods[i]->mod_bvalues = safe_emalloc((num_values + 1), sizeof(struct berval *), 0);
2272 
2273 /* allow for arrays with one element, no allowance for arrays with none but probably not required, gerrit thomson. */
2274 		if ((num_values == 1) && (Z_TYPE_P(value) != IS_ARRAY)) {
2275 			convert_to_string(value);
2276 			if (EG(exception)) {
2277 				RETVAL_FALSE;
2278 				goto cleanup;
2279 			}
2280 			ldap_mods[i]->mod_bvalues[0] = (struct berval *) emalloc (sizeof(struct berval));
2281 			ldap_mods[i]->mod_bvalues[0]->bv_val = Z_STRVAL_P(value);
2282 			ldap_mods[i]->mod_bvalues[0]->bv_len = Z_STRLEN_P(value);
2283 		} else {
2284 			for (j = 0; j < num_values; j++) {
2285 				if ((ivalue = zend_hash_index_find(Z_ARRVAL_P(value), j)) == NULL) {
2286 					php_error_docref(NULL, E_WARNING, "Value array must have consecutive indices 0, 1, ...");
2287 					num_berval[i] = j;
2288 					num_attribs = i + 1;
2289 					RETVAL_FALSE;
2290 					goto cleanup;
2291 				}
2292 				convert_to_string(ivalue);
2293 				if (EG(exception)) {
2294 					RETVAL_FALSE;
2295 					goto cleanup;
2296 				}
2297 				ldap_mods[i]->mod_bvalues[j] = (struct berval *) emalloc (sizeof(struct berval));
2298 				ldap_mods[i]->mod_bvalues[j]->bv_val = Z_STRVAL_P(ivalue);
2299 				ldap_mods[i]->mod_bvalues[j]->bv_len = Z_STRLEN_P(ivalue);
2300 			}
2301 		}
2302 		ldap_mods[i]->mod_bvalues[num_values] = NULL;
2303 		zend_hash_move_forward(Z_ARRVAL_P(entry));
2304 	}
2305 	ldap_mods[num_attribs] = NULL;
2306 
2307 	if (serverctrls) {
2308 		lserverctrls = _php_ldap_controls_from_array(ld->link, serverctrls);
2309 		if (lserverctrls == NULL) {
2310 			RETVAL_FALSE;
2311 			goto cleanup;
2312 		}
2313 	}
2314 
2315 /* check flag to see if do_mod was called to perform full add , gerrit thomson */
2316 	if (is_full_add == 1) {
2317 		if (ext) {
2318 			i = ldap_add_ext(ld->link, dn, ldap_mods, lserverctrls, NULL, &msgid);
2319 		} else {
2320 			i = ldap_add_ext_s(ld->link, dn, ldap_mods, lserverctrls, NULL);
2321 		}
2322 		if (i != LDAP_SUCCESS) {
2323 			php_error_docref(NULL, E_WARNING, "Add: %s", ldap_err2string(i));
2324 			RETVAL_FALSE;
2325 		} else if (ext) {
2326 			i = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res);
2327 			if (i == -1) {
2328 				php_error_docref(NULL, E_WARNING, "Add operation failed");
2329 				RETVAL_FALSE;
2330 				goto cleanup;
2331 			}
2332 
2333 			/* return a PHP control object */
2334 			RETVAL_RES(zend_register_resource(ldap_res, le_result));
2335 		} else RETVAL_TRUE;
2336 	} else {
2337 		if (ext) {
2338 			i = ldap_modify_ext(ld->link, dn, ldap_mods, lserverctrls, NULL, &msgid);
2339 		} else {
2340 			i = ldap_modify_ext_s(ld->link, dn, ldap_mods, lserverctrls, NULL);
2341 		}
2342 		if (i != LDAP_SUCCESS) {
2343 			php_error_docref(NULL, E_WARNING, "Modify: %s", ldap_err2string(i));
2344 			RETVAL_FALSE;
2345 		} else if (ext) {
2346 			i = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res);
2347 			if (i == -1) {
2348 				php_error_docref(NULL, E_WARNING, "Modify operation failed");
2349 				RETVAL_FALSE;
2350 				goto cleanup;
2351 			}
2352 
2353 			/* return a PHP control object */
2354 			RETVAL_RES(zend_register_resource(ldap_res, le_result));
2355 		} else RETVAL_TRUE;
2356 	}
2357 
2358 cleanup:
2359 	for (i = 0; i < num_attribs; i++) {
2360 		efree(ldap_mods[i]->mod_type);
2361 		for (j = 0; j < num_berval[i]; j++) {
2362 			efree(ldap_mods[i]->mod_bvalues[j]);
2363 		}
2364 		efree(ldap_mods[i]->mod_bvalues);
2365 		efree(ldap_mods[i]);
2366 	}
2367 	efree(num_berval);
2368 	efree(ldap_mods);
2369 
2370 	if (lserverctrls) {
2371 		_php_ldap_controls_free(&lserverctrls);
2372 	}
2373 
2374 	return;
2375 }
2376 /* }}} */
2377 
2378 /* {{{ proto bool ldap_add(resource link, string dn, array entry [, array servercontrols])
2379    Add entries to LDAP directory */
PHP_FUNCTION(ldap_add)2380 PHP_FUNCTION(ldap_add)
2381 {
2382 	/* use a newly define parameter into the do_modify so ldap_mod_add can be used the way it is supposed to be used , Gerrit THomson */
2383 	php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_LD_FULL_ADD, 0);
2384 }
2385 /* }}} */
2386 
2387 /* {{{ proto resource ldap_add_ext(resource link, string dn, array entry [, array servercontrols])
2388    Add entries to LDAP directory */
PHP_FUNCTION(ldap_add_ext)2389 PHP_FUNCTION(ldap_add_ext)
2390 {
2391 	php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_LD_FULL_ADD, 1);
2392 }
2393 /* }}} */
2394 
2395 /* three functions for attribute base modifications, gerrit Thomson */
2396 
2397 /* {{{ proto bool ldap_mod_replace(resource link, string dn, array entry [, array servercontrols])
2398    Replace attribute values with new ones */
PHP_FUNCTION(ldap_mod_replace)2399 PHP_FUNCTION(ldap_mod_replace)
2400 {
2401 	php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_REPLACE, 0);
2402 }
2403 /* }}} */
2404 
2405 /* {{{ proto resource ldap_mod_replace_ext(resource link, string dn, array entry [, array servercontrols])
2406    Replace attribute values with new ones */
PHP_FUNCTION(ldap_mod_replace_ext)2407 PHP_FUNCTION(ldap_mod_replace_ext)
2408 {
2409 	php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_REPLACE, 1);
2410 }
2411 /* }}} */
2412 
2413 /* {{{ proto bool ldap_mod_add(resource link, string dn, array entry [, array servercontrols])
2414    Add attribute values to current */
PHP_FUNCTION(ldap_mod_add)2415 PHP_FUNCTION(ldap_mod_add)
2416 {
2417 	php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_ADD, 0);
2418 }
2419 /* }}} */
2420 
2421 /* {{{ proto resource ldap_mod_add(resource link, string dn, array entry [, array servercontrols])
2422    Add attribute values to current */
PHP_FUNCTION(ldap_mod_add_ext)2423 PHP_FUNCTION(ldap_mod_add_ext)
2424 {
2425 	php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_ADD, 1);
2426 }
2427 /* }}} */
2428 
2429 /* {{{ proto bool ldap_mod_del(resource link, string dn, array entry [, array servercontrols])
2430    Delete attribute values */
PHP_FUNCTION(ldap_mod_del)2431 PHP_FUNCTION(ldap_mod_del)
2432 {
2433 	php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_DELETE, 0);
2434 }
2435 /* }}} */
2436 
2437 /* {{{ proto resource ldap_mod_del_ext(resource link, string dn, array entry [, array servercontrols])
2438    Delete attribute values */
PHP_FUNCTION(ldap_mod_del_ext)2439 PHP_FUNCTION(ldap_mod_del_ext)
2440 {
2441 	php_ldap_do_modify(INTERNAL_FUNCTION_PARAM_PASSTHRU, LDAP_MOD_DELETE, 1);
2442 }
2443 /* }}} */
2444 
2445 /* {{{ php_ldap_do_delete
2446  */
php_ldap_do_delete(INTERNAL_FUNCTION_PARAMETERS,int ext)2447 static void php_ldap_do_delete(INTERNAL_FUNCTION_PARAMETERS, int ext)
2448 {
2449 	zval *serverctrls = NULL;
2450 	zval *link;
2451 	ldap_linkdata *ld;
2452 	LDAPControl **lserverctrls = NULL;
2453 	LDAPMessage *ldap_res;
2454 	char *dn;
2455 	int rc, msgid;
2456 	size_t dn_len;
2457 
2458 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs|a", &link, &dn, &dn_len, &serverctrls) != SUCCESS) {
2459 		return;
2460 	}
2461 
2462 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
2463 		RETURN_FALSE;
2464 	}
2465 
2466 	if (serverctrls) {
2467 		lserverctrls = _php_ldap_controls_from_array(ld->link, serverctrls);
2468 		if (lserverctrls == NULL) {
2469 			RETVAL_FALSE;
2470 			goto cleanup;
2471 		}
2472 	}
2473 
2474 	if (ext) {
2475 		rc = ldap_delete_ext(ld->link, dn, lserverctrls, NULL, &msgid);
2476 	} else {
2477 		rc = ldap_delete_ext_s(ld->link, dn, lserverctrls, NULL);
2478 	}
2479 	if (rc != LDAP_SUCCESS) {
2480 		php_error_docref(NULL, E_WARNING, "Delete: %s", ldap_err2string(rc));
2481 		RETVAL_FALSE;
2482 		goto cleanup;
2483 	} else if (ext) {
2484 		rc = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res);
2485 		if (rc == -1) {
2486 			php_error_docref(NULL, E_WARNING, "Delete operation failed");
2487 			RETVAL_FALSE;
2488 			goto cleanup;
2489 		}
2490 
2491 		/* return a PHP control object */
2492 		RETVAL_RES(zend_register_resource(ldap_res, le_result));
2493 	} else {
2494 		RETVAL_TRUE;
2495 	}
2496 
2497 cleanup:
2498 	if (lserverctrls) {
2499 		_php_ldap_controls_free(&lserverctrls);
2500 	}
2501 
2502 	return;
2503 }
2504 /* }}} */
2505 
2506 /* {{{ proto bool ldap_delete(resource link, string dn [, array servercontrols])
2507    Delete an entry from a directory */
PHP_FUNCTION(ldap_delete)2508 PHP_FUNCTION(ldap_delete)
2509 {
2510 	php_ldap_do_delete(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
2511 }
2512 /* }}} */
2513 
2514 /* {{{ proto resource ldap_delete_ext(resource link, string dn [, array servercontrols])
2515    Delete an entry from a directory */
PHP_FUNCTION(ldap_delete_ext)2516 PHP_FUNCTION(ldap_delete_ext)
2517 {
2518 	php_ldap_do_delete(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
2519 }
2520 /* }}} */
2521 
2522 /* {{{ _ldap_str_equal_to_const
2523  */
_ldap_str_equal_to_const(const char * str,size_t str_len,const char * cstr)2524 static size_t _ldap_str_equal_to_const(const char *str, size_t str_len, const char *cstr)
2525 {
2526 	size_t i;
2527 
2528 	if (strlen(cstr) != str_len)
2529 		return 0;
2530 
2531 	for (i = 0; i < str_len; ++i) {
2532 		if (str[i] != cstr[i]) {
2533 			return 0;
2534 		}
2535 	}
2536 
2537 	return 1;
2538 }
2539 /* }}} */
2540 
2541 /* {{{ _ldap_strlen_max
2542  */
_ldap_strlen_max(const char * str,size_t max_len)2543 static size_t _ldap_strlen_max(const char *str, size_t max_len)
2544 {
2545 	size_t i;
2546 
2547 	for (i = 0; i < max_len; ++i) {
2548 		if (str[i] == '\0') {
2549 			return i;
2550 		}
2551 	}
2552 
2553 	return max_len;
2554 }
2555 /* }}} */
2556 
2557 /* {{{ _ldap_hash_fetch
2558  */
_ldap_hash_fetch(zval * hashTbl,const char * key,zval ** out)2559 static void _ldap_hash_fetch(zval *hashTbl, const char *key, zval **out)
2560 {
2561 	*out = zend_hash_str_find(Z_ARRVAL_P(hashTbl), key, strlen(key));
2562 }
2563 /* }}} */
2564 
2565 /* {{{ proto bool ldap_modify_batch(resource link, string dn, array modifs [, array servercontrols])
2566    Perform multiple modifications as part of one operation */
PHP_FUNCTION(ldap_modify_batch)2567 PHP_FUNCTION(ldap_modify_batch)
2568 {
2569 	zval *serverctrls = NULL;
2570 	ldap_linkdata *ld;
2571 	zval *link, *mods, *mod, *modinfo;
2572 	zend_string *modval;
2573 	zval *attrib, *modtype, *vals;
2574 	zval *fetched;
2575 	char *dn;
2576 	size_t dn_len;
2577 	int i, j, k;
2578 	int num_mods, num_modprops, num_modvals;
2579 	LDAPMod **ldap_mods;
2580 	LDAPControl **lserverctrls = NULL;
2581 	uint32_t oper;
2582 
2583 	/*
2584 	$mods = array(
2585 		array(
2586 			"attrib" => "unicodePwd",
2587 			"modtype" => LDAP_MODIFY_BATCH_REMOVE,
2588 			"values" => array($oldpw)
2589 		),
2590 		array(
2591 			"attrib" => "unicodePwd",
2592 			"modtype" => LDAP_MODIFY_BATCH_ADD,
2593 			"values" => array($newpw)
2594 		),
2595 		array(
2596 			"attrib" => "userPrincipalName",
2597 			"modtype" => LDAP_MODIFY_BATCH_REPLACE,
2598 			"values" => array("janitor@corp.contoso.com")
2599 		),
2600 		array(
2601 			"attrib" => "userCert",
2602 			"modtype" => LDAP_MODIFY_BATCH_REMOVE_ALL
2603 		)
2604 	);
2605 	*/
2606 
2607 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsa/|a", &link, &dn, &dn_len, &mods, &serverctrls) != SUCCESS) {
2608 		return;
2609 	}
2610 
2611 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
2612 		RETURN_FALSE;
2613 	}
2614 
2615 	/* perform validation */
2616 	{
2617 		zend_string *modkey;
2618 		zend_long modtype;
2619 
2620 		/* to store the wrongly-typed keys */
2621 		zend_ulong tmpUlong;
2622 
2623 		/* make sure the DN contains no NUL bytes */
2624 		if (_ldap_strlen_max(dn, dn_len) != dn_len) {
2625 			php_error_docref(NULL, E_WARNING, "DN must not contain NUL bytes");
2626 			RETURN_FALSE;
2627 		}
2628 
2629 		/* make sure the top level is a normal array */
2630 		zend_hash_internal_pointer_reset(Z_ARRVAL_P(mods));
2631 		if (zend_hash_get_current_key_type(Z_ARRVAL_P(mods)) != HASH_KEY_IS_LONG) {
2632 			php_error_docref(NULL, E_WARNING, "Modifications array must not be string-indexed");
2633 			RETURN_FALSE;
2634 		}
2635 
2636 		num_mods = zend_hash_num_elements(Z_ARRVAL_P(mods));
2637 
2638 		for (i = 0; i < num_mods; i++) {
2639 			/* is the numbering consecutive? */
2640 			if ((fetched = zend_hash_index_find(Z_ARRVAL_P(mods), i)) == NULL) {
2641 				php_error_docref(NULL, E_WARNING, "Modifications array must have consecutive indices 0, 1, ...");
2642 				RETURN_FALSE;
2643 			}
2644 			mod = fetched;
2645 
2646 			/* is it an array? */
2647 			if (Z_TYPE_P(mod) != IS_ARRAY) {
2648 				php_error_docref(NULL, E_WARNING, "Each entry of modifications array must be an array itself");
2649 				RETURN_FALSE;
2650 			}
2651 
2652 			SEPARATE_ARRAY(mod);
2653 			/* for the modification hashtable... */
2654 			zend_hash_internal_pointer_reset(Z_ARRVAL_P(mod));
2655 			num_modprops = zend_hash_num_elements(Z_ARRVAL_P(mod));
2656 
2657 			for (j = 0; j < num_modprops; j++) {
2658 				/* are the keys strings? */
2659 				if (zend_hash_get_current_key(Z_ARRVAL_P(mod), &modkey, &tmpUlong) != HASH_KEY_IS_STRING) {
2660 					php_error_docref(NULL, E_WARNING, "Each entry of modifications array must be string-indexed");
2661 					RETURN_FALSE;
2662 				}
2663 
2664 				/* is this a valid entry? */
2665 				if (
2666 					!_ldap_str_equal_to_const(ZSTR_VAL(modkey), ZSTR_LEN(modkey), LDAP_MODIFY_BATCH_ATTRIB) &&
2667 					!_ldap_str_equal_to_const(ZSTR_VAL(modkey), ZSTR_LEN(modkey), LDAP_MODIFY_BATCH_MODTYPE) &&
2668 					!_ldap_str_equal_to_const(ZSTR_VAL(modkey), ZSTR_LEN(modkey), LDAP_MODIFY_BATCH_VALUES)
2669 				) {
2670 					php_error_docref(NULL, E_WARNING, "The only allowed keys in entries of the modifications array are '" LDAP_MODIFY_BATCH_ATTRIB "', '" LDAP_MODIFY_BATCH_MODTYPE "' and '" LDAP_MODIFY_BATCH_VALUES "'");
2671 					RETURN_FALSE;
2672 				}
2673 
2674 				fetched = zend_hash_get_current_data(Z_ARRVAL_P(mod));
2675 				modinfo = fetched;
2676 
2677 				/* does the value type match the key? */
2678 				if (_ldap_str_equal_to_const(ZSTR_VAL(modkey), ZSTR_LEN(modkey), LDAP_MODIFY_BATCH_ATTRIB)) {
2679 					if (Z_TYPE_P(modinfo) != IS_STRING) {
2680 						php_error_docref(NULL, E_WARNING, "A '" LDAP_MODIFY_BATCH_ATTRIB "' value must be a string");
2681 						RETURN_FALSE;
2682 					}
2683 
2684 					if (Z_STRLEN_P(modinfo) != _ldap_strlen_max(Z_STRVAL_P(modinfo), Z_STRLEN_P(modinfo))) {
2685 						php_error_docref(NULL, E_WARNING, "A '" LDAP_MODIFY_BATCH_ATTRIB "' value must not contain NUL bytes");
2686 						RETURN_FALSE;
2687 					}
2688 				}
2689 				else if (_ldap_str_equal_to_const(ZSTR_VAL(modkey), ZSTR_LEN(modkey), LDAP_MODIFY_BATCH_MODTYPE)) {
2690 					if (Z_TYPE_P(modinfo) != IS_LONG) {
2691 						php_error_docref(NULL, E_WARNING, "A '" LDAP_MODIFY_BATCH_MODTYPE "' value must be a long");
2692 						RETURN_FALSE;
2693 					}
2694 
2695 					/* is the value in range? */
2696 					modtype = Z_LVAL_P(modinfo);
2697 					if (
2698 						modtype != LDAP_MODIFY_BATCH_ADD &&
2699 						modtype != LDAP_MODIFY_BATCH_REMOVE &&
2700 						modtype != LDAP_MODIFY_BATCH_REPLACE &&
2701 						modtype != LDAP_MODIFY_BATCH_REMOVE_ALL
2702 					) {
2703 						php_error_docref(NULL, E_WARNING, "The '" LDAP_MODIFY_BATCH_MODTYPE "' value must match one of the LDAP_MODIFY_BATCH_* constants");
2704 						RETURN_FALSE;
2705 					}
2706 
2707 					/* if it's REMOVE_ALL, there must not be a values array; otherwise, there must */
2708 					if (modtype == LDAP_MODIFY_BATCH_REMOVE_ALL) {
2709 						if (zend_hash_str_exists(Z_ARRVAL_P(mod), LDAP_MODIFY_BATCH_VALUES, strlen(LDAP_MODIFY_BATCH_VALUES))) {
2710 							php_error_docref(NULL, E_WARNING, "If '" LDAP_MODIFY_BATCH_MODTYPE "' is LDAP_MODIFY_BATCH_REMOVE_ALL, a '" LDAP_MODIFY_BATCH_VALUES "' array must not be provided");
2711 							RETURN_FALSE;
2712 						}
2713 					}
2714 					else {
2715 						if (!zend_hash_str_exists(Z_ARRVAL_P(mod), LDAP_MODIFY_BATCH_VALUES, strlen(LDAP_MODIFY_BATCH_VALUES))) {
2716 							php_error_docref(NULL, E_WARNING, "If '" LDAP_MODIFY_BATCH_MODTYPE "' is not LDAP_MODIFY_BATCH_REMOVE_ALL, a '" LDAP_MODIFY_BATCH_VALUES "' array must be provided");
2717 							RETURN_FALSE;
2718 						}
2719 					}
2720 				}
2721 				else if (_ldap_str_equal_to_const(ZSTR_VAL(modkey), ZSTR_LEN(modkey), LDAP_MODIFY_BATCH_VALUES)) {
2722 					if (Z_TYPE_P(modinfo) != IS_ARRAY) {
2723 						php_error_docref(NULL, E_WARNING, "A '" LDAP_MODIFY_BATCH_VALUES "' value must be an array");
2724 						RETURN_FALSE;
2725 					}
2726 
2727 					SEPARATE_ARRAY(modinfo);
2728 					/* is the array not empty? */
2729 					zend_hash_internal_pointer_reset(Z_ARRVAL_P(modinfo));
2730 					num_modvals = zend_hash_num_elements(Z_ARRVAL_P(modinfo));
2731 					if (num_modvals == 0) {
2732 						php_error_docref(NULL, E_WARNING, "A '" LDAP_MODIFY_BATCH_VALUES "' array must have at least one element");
2733 						RETURN_FALSE;
2734 					}
2735 
2736 					/* are its keys integers? */
2737 					if (zend_hash_get_current_key_type(Z_ARRVAL_P(modinfo)) != HASH_KEY_IS_LONG) {
2738 						php_error_docref(NULL, E_WARNING, "A '" LDAP_MODIFY_BATCH_VALUES "' array must not be string-indexed");
2739 						RETURN_FALSE;
2740 					}
2741 
2742 					/* are the keys consecutive? */
2743 					for (k = 0; k < num_modvals; k++) {
2744 						if ((fetched = zend_hash_index_find(Z_ARRVAL_P(modinfo), k)) == NULL) {
2745 							php_error_docref(NULL, E_WARNING, "A '" LDAP_MODIFY_BATCH_VALUES "' array must have consecutive indices 0, 1, ...");
2746 							RETURN_FALSE;
2747 						}
2748 					}
2749 				}
2750 
2751 				zend_hash_move_forward(Z_ARRVAL_P(mod));
2752 			}
2753 		}
2754 	}
2755 	/* validation was successful */
2756 
2757 	/* allocate array of modifications */
2758 	ldap_mods = safe_emalloc((num_mods+1), sizeof(LDAPMod *), 0);
2759 
2760 	/* for each modification */
2761 	for (i = 0; i < num_mods; i++) {
2762 		/* allocate the modification struct */
2763 		ldap_mods[i] = safe_emalloc(1, sizeof(LDAPMod), 0);
2764 
2765 		/* fetch the relevant data */
2766 		fetched = zend_hash_index_find(Z_ARRVAL_P(mods), i);
2767 		mod = fetched;
2768 
2769 		_ldap_hash_fetch(mod, LDAP_MODIFY_BATCH_ATTRIB, &attrib);
2770 		_ldap_hash_fetch(mod, LDAP_MODIFY_BATCH_MODTYPE, &modtype);
2771 		_ldap_hash_fetch(mod, LDAP_MODIFY_BATCH_VALUES, &vals);
2772 
2773 		/* map the modification type */
2774 		switch (Z_LVAL_P(modtype)) {
2775 			case LDAP_MODIFY_BATCH_ADD:
2776 				oper = LDAP_MOD_ADD;
2777 				break;
2778 			case LDAP_MODIFY_BATCH_REMOVE:
2779 			case LDAP_MODIFY_BATCH_REMOVE_ALL:
2780 				oper = LDAP_MOD_DELETE;
2781 				break;
2782 			case LDAP_MODIFY_BATCH_REPLACE:
2783 				oper = LDAP_MOD_REPLACE;
2784 				break;
2785 			default:
2786 				zend_throw_error(NULL, "Unknown and uncaught modification type.");
2787 				RETVAL_FALSE;
2788 				efree(ldap_mods[i]);
2789 				num_mods = i;
2790 				goto cleanup;
2791 		}
2792 
2793 		/* fill in the basic info */
2794 		ldap_mods[i]->mod_op = oper | LDAP_MOD_BVALUES;
2795 		ldap_mods[i]->mod_type = estrndup(Z_STRVAL_P(attrib), Z_STRLEN_P(attrib));
2796 
2797 		if (Z_LVAL_P(modtype) == LDAP_MODIFY_BATCH_REMOVE_ALL) {
2798 			/* no values */
2799 			ldap_mods[i]->mod_bvalues = NULL;
2800 		}
2801 		else {
2802 			/* allocate space for the values as part of this modification */
2803 			num_modvals = zend_hash_num_elements(Z_ARRVAL_P(vals));
2804 			ldap_mods[i]->mod_bvalues = safe_emalloc((num_modvals+1), sizeof(struct berval *), 0);
2805 
2806 			/* for each value */
2807 			for (j = 0; j < num_modvals; j++) {
2808 				/* fetch it */
2809 				fetched = zend_hash_index_find(Z_ARRVAL_P(vals), j);
2810 				modval = zval_get_string(fetched);
2811 				if (EG(exception)) {
2812 					RETVAL_FALSE;
2813 					ldap_mods[i]->mod_bvalues[j] = NULL;
2814 					num_mods = i + 1;
2815 					goto cleanup;
2816 				}
2817 
2818 				/* allocate the data struct */
2819 				ldap_mods[i]->mod_bvalues[j] = safe_emalloc(1, sizeof(struct berval), 0);
2820 
2821 				/* fill it */
2822 				ldap_mods[i]->mod_bvalues[j]->bv_len = ZSTR_LEN(modval);
2823 				ldap_mods[i]->mod_bvalues[j]->bv_val = estrndup(ZSTR_VAL(modval), ZSTR_LEN(modval));
2824 				zend_string_release(modval);
2825 			}
2826 
2827 			/* NULL-terminate values */
2828 			ldap_mods[i]->mod_bvalues[num_modvals] = NULL;
2829 		}
2830 	}
2831 
2832 	/* NULL-terminate modifications */
2833 	ldap_mods[num_mods] = NULL;
2834 
2835 	if (serverctrls) {
2836 		lserverctrls = _php_ldap_controls_from_array(ld->link, serverctrls);
2837 		if (lserverctrls == NULL) {
2838 			RETVAL_FALSE;
2839 			goto cleanup;
2840 		}
2841 	}
2842 
2843 	/* perform (finally) */
2844 	if ((i = ldap_modify_ext_s(ld->link, dn, ldap_mods, lserverctrls, NULL)) != LDAP_SUCCESS) {
2845 		php_error_docref(NULL, E_WARNING, "Batch Modify: %s", ldap_err2string(i));
2846 		RETVAL_FALSE;
2847 	} else RETVAL_TRUE;
2848 
2849 	/* clean up */
2850 	cleanup: {
2851 		for (i = 0; i < num_mods; i++) {
2852 			/* attribute */
2853 			efree(ldap_mods[i]->mod_type);
2854 
2855 			if (ldap_mods[i]->mod_bvalues != NULL) {
2856 				/* each BER value */
2857 				for (j = 0; ldap_mods[i]->mod_bvalues[j] != NULL; j++) {
2858 					/* free the data bytes */
2859 					efree(ldap_mods[i]->mod_bvalues[j]->bv_val);
2860 
2861 					/* free the bvalue struct */
2862 					efree(ldap_mods[i]->mod_bvalues[j]);
2863 				}
2864 
2865 				/* the BER value array */
2866 				efree(ldap_mods[i]->mod_bvalues);
2867 			}
2868 
2869 			/* the modification */
2870 			efree(ldap_mods[i]);
2871 		}
2872 
2873 		/* the modifications array */
2874 		efree(ldap_mods);
2875 
2876 		if (lserverctrls) {
2877 			_php_ldap_controls_free(&lserverctrls);
2878 		}
2879 	}
2880 }
2881 /* }}} */
2882 
2883 /* {{{ proto int ldap_errno(resource link)
2884    Get the current ldap error number */
PHP_FUNCTION(ldap_errno)2885 PHP_FUNCTION(ldap_errno)
2886 {
2887 	zval *link;
2888 	ldap_linkdata *ld;
2889 
2890 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &link) != SUCCESS) {
2891 		return;
2892 	}
2893 
2894 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
2895 		RETURN_FALSE;
2896 	}
2897 
2898 	RETURN_LONG(_get_lderrno(ld->link));
2899 }
2900 /* }}} */
2901 
2902 /* {{{ proto string ldap_err2str(int errno)
2903    Convert error number to error string */
PHP_FUNCTION(ldap_err2str)2904 PHP_FUNCTION(ldap_err2str)
2905 {
2906 	zend_long perrno;
2907 
2908 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &perrno) != SUCCESS) {
2909 		return;
2910 	}
2911 
2912 	RETURN_STRING(ldap_err2string(perrno));
2913 }
2914 /* }}} */
2915 
2916 /* {{{ proto string ldap_error(resource link)
2917    Get the current ldap error string */
PHP_FUNCTION(ldap_error)2918 PHP_FUNCTION(ldap_error)
2919 {
2920 	zval *link;
2921 	ldap_linkdata *ld;
2922 	int ld_errno;
2923 
2924 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &link) != SUCCESS) {
2925 		return;
2926 	}
2927 
2928 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
2929 		RETURN_FALSE;
2930 	}
2931 
2932 	ld_errno = _get_lderrno(ld->link);
2933 
2934 	RETURN_STRING(ldap_err2string(ld_errno));
2935 }
2936 /* }}} */
2937 
2938 /* {{{ proto bool ldap_compare(resource link, string dn, string attr, string value)
2939    Determine if an entry has a specific value for one of its attributes */
PHP_FUNCTION(ldap_compare)2940 PHP_FUNCTION(ldap_compare)
2941 {
2942 	zval *serverctrls = NULL;
2943 	zval *link;
2944 	char *dn, *attr, *value;
2945 	size_t dn_len, attr_len, value_len;
2946 	ldap_linkdata *ld;
2947 	LDAPControl **lserverctrls = NULL;
2948 	int errno;
2949 	struct berval lvalue;
2950 
2951 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsss|a", &link, &dn, &dn_len, &attr, &attr_len, &value, &value_len, &serverctrls) != SUCCESS) {
2952 		return;
2953 	}
2954 
2955 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
2956 		RETURN_FALSE;
2957 	}
2958 
2959 	if (serverctrls) {
2960 		lserverctrls = _php_ldap_controls_from_array(ld->link, serverctrls);
2961 		if (lserverctrls == NULL) {
2962 			RETVAL_FALSE;
2963 			goto cleanup;
2964 		}
2965 	}
2966 
2967 	lvalue.bv_val = value;
2968 	lvalue.bv_len = value_len;
2969 
2970 	errno = ldap_compare_ext_s(ld->link, dn, attr, &lvalue, lserverctrls, NULL);
2971 
2972 	switch (errno) {
2973 		case LDAP_COMPARE_TRUE:
2974 			RETVAL_TRUE;
2975 			break;
2976 
2977 		case LDAP_COMPARE_FALSE:
2978 			RETVAL_FALSE;
2979 			break;
2980 
2981 		default:
2982 			php_error_docref(NULL, E_WARNING, "Compare: %s", ldap_err2string(errno));
2983 			RETVAL_LONG(-1);
2984 	}
2985 
2986 cleanup:
2987 	if (lserverctrls) {
2988 		_php_ldap_controls_free(&lserverctrls);
2989 	}
2990 
2991 	return;
2992 }
2993 /* }}} */
2994 
2995 /* {{{ proto bool ldap_sort(resource link, resource result, string sortfilter)
2996    Sort LDAP result entries */
PHP_FUNCTION(ldap_sort)2997 PHP_FUNCTION(ldap_sort)
2998 {
2999 	zval *link, *result;
3000 	ldap_linkdata *ld;
3001 	char *sortfilter;
3002 	size_t sflen;
3003 	zend_resource *le;
3004 
3005 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rrs", &link, &result, &sortfilter, &sflen) != SUCCESS) {
3006 		RETURN_FALSE;
3007 	}
3008 
3009 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
3010 		RETURN_FALSE;
3011 	}
3012 
3013 	le = Z_RES_P(result);
3014 	if (le->type != le_result) {
3015 		php_error_docref(NULL, E_WARNING, "Supplied resource is not a valid ldap result resource");
3016 		RETURN_FALSE;
3017 	}
3018 
3019 	if (ldap_sort_entries(ld->link, (LDAPMessage **) &le->ptr, sflen ? sortfilter : NULL, strcmp) != LDAP_SUCCESS) {
3020 		php_error_docref(NULL, E_WARNING, "%s", ldap_err2string(errno));
3021 		RETURN_FALSE;
3022 	}
3023 
3024 	RETURN_TRUE;
3025 }
3026 /* }}} */
3027 
3028 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP
3029 /* {{{ proto bool ldap_get_option(resource link, int option, mixed retval)
3030    Get the current value of various session-wide parameters */
PHP_FUNCTION(ldap_get_option)3031 PHP_FUNCTION(ldap_get_option)
3032 {
3033 	zval *link, *retval;
3034 	ldap_linkdata *ld;
3035 	zend_long option;
3036 
3037 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlz", &link, &option, &retval) != SUCCESS) {
3038 		return;
3039 	}
3040 
3041 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
3042 		RETURN_FALSE;
3043 	}
3044 
3045 	switch (option) {
3046 	/* options with int value */
3047 	case LDAP_OPT_DEREF:
3048 	case LDAP_OPT_SIZELIMIT:
3049 	case LDAP_OPT_TIMELIMIT:
3050 	case LDAP_OPT_PROTOCOL_VERSION:
3051 	case LDAP_OPT_ERROR_NUMBER:
3052 	case LDAP_OPT_REFERRALS:
3053 #ifdef LDAP_OPT_RESTART
3054 	case LDAP_OPT_RESTART:
3055 #endif
3056 #ifdef LDAP_OPT_X_SASL_NOCANON
3057 	case LDAP_OPT_X_SASL_NOCANON:
3058 #endif
3059 #ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
3060 	case LDAP_OPT_X_TLS_REQUIRE_CERT:
3061 #endif
3062 #ifdef LDAP_OPT_X_TLS_CRLCHECK
3063 	case LDAP_OPT_X_TLS_CRLCHECK:
3064 #endif
3065 #ifdef LDAP_OPT_X_TLS_PROTOCOL_MIN
3066 	case LDAP_OPT_X_TLS_PROTOCOL_MIN:
3067 #endif
3068 #ifdef LDAP_OPT_X_KEEPALIVE_IDLE
3069 	case LDAP_OPT_X_KEEPALIVE_IDLE:
3070 	case LDAP_OPT_X_KEEPALIVE_PROBES:
3071 	case LDAP_OPT_X_KEEPALIVE_INTERVAL:
3072 #endif
3073 		{
3074 			int val;
3075 
3076 			if (ldap_get_option(ld->link, option, &val)) {
3077 				RETURN_FALSE;
3078 			}
3079 			ZEND_TRY_ASSIGN_REF_LONG(retval, val);
3080 		} break;
3081 #ifdef LDAP_OPT_NETWORK_TIMEOUT
3082 	case LDAP_OPT_NETWORK_TIMEOUT:
3083 		{
3084 			struct timeval *timeout = NULL;
3085 
3086 			if (ldap_get_option(ld->link, LDAP_OPT_NETWORK_TIMEOUT, (void *) &timeout)) {
3087 				if (timeout) {
3088 					ldap_memfree(timeout);
3089 				}
3090 				RETURN_FALSE;
3091 			}
3092 			if (!timeout) {
3093 				RETURN_FALSE;
3094 			}
3095 			ZEND_TRY_ASSIGN_REF_LONG(retval, timeout->tv_sec);
3096 			ldap_memfree(timeout);
3097 		} break;
3098 #elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
3099 	case LDAP_X_OPT_CONNECT_TIMEOUT:
3100 		{
3101 			int timeout;
3102 
3103 			if (ldap_get_option(ld->link, LDAP_X_OPT_CONNECT_TIMEOUT, &timeout)) {
3104 				RETURN_FALSE;
3105 			}
3106 			ZEND_TRY_ASSIGN_REF_LONG(retval, (timeout / 1000));
3107 		} break;
3108 #endif
3109 #ifdef LDAP_OPT_TIMEOUT
3110 	case LDAP_OPT_TIMEOUT:
3111 		{
3112 			struct timeval *timeout = NULL;
3113 
3114 			if (ldap_get_option(ld->link, LDAP_OPT_TIMEOUT, (void *) &timeout)) {
3115 				if (timeout) {
3116 					ldap_memfree(timeout);
3117 				}
3118 				RETURN_FALSE;
3119 			}
3120 			if (!timeout) {
3121 				RETURN_FALSE;
3122 			}
3123 			ZEND_TRY_ASSIGN_REF_LONG(retval, timeout->tv_sec);
3124 			ldap_memfree(timeout);
3125 		} break;
3126 #endif
3127 	/* options with string value */
3128 	case LDAP_OPT_ERROR_STRING:
3129 #ifdef LDAP_OPT_HOST_NAME
3130 	case LDAP_OPT_HOST_NAME:
3131 #endif
3132 #ifdef HAVE_LDAP_SASL
3133 	case LDAP_OPT_X_SASL_MECH:
3134 	case LDAP_OPT_X_SASL_REALM:
3135 	case LDAP_OPT_X_SASL_AUTHCID:
3136 	case LDAP_OPT_X_SASL_AUTHZID:
3137 #endif
3138 #ifdef LDAP_OPT_X_SASL_USERNAME
3139 	case LDAP_OPT_X_SASL_USERNAME:
3140 #endif
3141 #if (LDAP_API_VERSION > 2000)
3142 	case LDAP_OPT_X_TLS_CACERTDIR:
3143 	case LDAP_OPT_X_TLS_CACERTFILE:
3144 	case LDAP_OPT_X_TLS_CERTFILE:
3145 	case LDAP_OPT_X_TLS_CIPHER_SUITE:
3146 	case LDAP_OPT_X_TLS_KEYFILE:
3147 	case LDAP_OPT_X_TLS_RANDOM_FILE:
3148 #endif
3149 #ifdef LDAP_OPT_X_TLS_PACKAGE
3150 	case LDAP_OPT_X_TLS_PACKAGE:
3151 #endif
3152 #ifdef LDAP_OPT_X_TLS_CRLFILE
3153 	case LDAP_OPT_X_TLS_CRLFILE:
3154 #endif
3155 #ifdef LDAP_OPT_X_TLS_DHFILE
3156 	case LDAP_OPT_X_TLS_DHFILE:
3157 #endif
3158 #ifdef LDAP_OPT_MATCHED_DN
3159 	case LDAP_OPT_MATCHED_DN:
3160 #endif
3161 		{
3162 			char *val = NULL;
3163 
3164 			if (ldap_get_option(ld->link, option, &val) || val == NULL || *val == '\0') {
3165 				if (val) {
3166 					ldap_memfree(val);
3167 				}
3168 				RETURN_FALSE;
3169 			}
3170 			ZEND_TRY_ASSIGN_REF_STRING(retval, val);
3171 			ldap_memfree(val);
3172 		} break;
3173 	case LDAP_OPT_SERVER_CONTROLS:
3174 	case LDAP_OPT_CLIENT_CONTROLS:
3175 		{
3176 			LDAPControl **ctrls = NULL;
3177 
3178 			if (ldap_get_option(ld->link, option, &ctrls) || ctrls == NULL) {
3179 				if (ctrls) {
3180 					ldap_memfree(ctrls);
3181 				}
3182 				RETURN_FALSE;
3183 			}
3184 			_php_ldap_controls_to_array(ld->link, ctrls, retval, 1);
3185 		} break;
3186 /* options not implemented
3187 	case LDAP_OPT_API_INFO:
3188 	case LDAP_OPT_API_FEATURE_INFO:
3189 */
3190 	default:
3191 		RETURN_FALSE;
3192 	}
3193 	RETURN_TRUE;
3194 }
3195 /* }}} */
3196 
3197 /* {{{ proto bool ldap_set_option(resource link, int option, mixed newval)
3198    Set the value of various session-wide parameters */
PHP_FUNCTION(ldap_set_option)3199 PHP_FUNCTION(ldap_set_option)
3200 {
3201 	zval *link, *newval;
3202 	ldap_linkdata *ld;
3203 	LDAP *ldap;
3204 	zend_long option;
3205 
3206 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zlz", &link, &option, &newval) != SUCCESS) {
3207 		return;
3208 	}
3209 
3210 	if (Z_TYPE_P(link) == IS_NULL) {
3211 		ldap = NULL;
3212 	} else {
3213 		if ((ld = (ldap_linkdata *)zend_fetch_resource_ex(link, "ldap link", le_link)) == NULL) {
3214 			RETURN_FALSE;
3215 		}
3216 		ldap = ld->link;
3217 	}
3218 
3219 	switch (option) {
3220 	/* options with int value */
3221 	case LDAP_OPT_DEREF:
3222 	case LDAP_OPT_SIZELIMIT:
3223 	case LDAP_OPT_TIMELIMIT:
3224 	case LDAP_OPT_PROTOCOL_VERSION:
3225 	case LDAP_OPT_ERROR_NUMBER:
3226 #ifdef LDAP_OPT_DEBUG_LEVEL
3227 	case LDAP_OPT_DEBUG_LEVEL:
3228 #endif
3229 #ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
3230 	case LDAP_OPT_X_TLS_REQUIRE_CERT:
3231 #endif
3232 #ifdef LDAP_OPT_X_TLS_CRLCHECK
3233 	case LDAP_OPT_X_TLS_CRLCHECK:
3234 #endif
3235 #ifdef LDAP_OPT_X_TLS_PROTOCOL_MIN
3236 	case LDAP_OPT_X_TLS_PROTOCOL_MIN:
3237 #endif
3238 #ifdef LDAP_OPT_X_KEEPALIVE_IDLE
3239 	case LDAP_OPT_X_KEEPALIVE_IDLE:
3240 	case LDAP_OPT_X_KEEPALIVE_PROBES:
3241 	case LDAP_OPT_X_KEEPALIVE_INTERVAL:
3242 #endif
3243 		{
3244 			int val;
3245 
3246 			convert_to_long_ex(newval);
3247 			if (ZEND_LONG_EXCEEDS_INT(Z_LVAL_P(newval))) {
3248 				php_error_docref(NULL, E_WARNING, "Option value is too big");
3249 				RETURN_FALSE;
3250 			}
3251 			val = (int)Z_LVAL_P(newval);
3252 			if (ldap_set_option(ldap, option, &val)) {
3253 				RETURN_FALSE;
3254 			}
3255 		} break;
3256 #ifdef LDAP_OPT_NETWORK_TIMEOUT
3257 	case LDAP_OPT_NETWORK_TIMEOUT:
3258 		{
3259 			struct timeval timeout;
3260 
3261 			convert_to_long_ex(newval);
3262 			timeout.tv_sec = Z_LVAL_P(newval);
3263 			timeout.tv_usec = 0;
3264 			if (ldap_set_option(ldap, LDAP_OPT_NETWORK_TIMEOUT, (void *) &timeout)) {
3265 				RETURN_FALSE;
3266 			}
3267 		} break;
3268 #elif defined(LDAP_X_OPT_CONNECT_TIMEOUT)
3269 	case LDAP_X_OPT_CONNECT_TIMEOUT:
3270 		{
3271 			int timeout;
3272 
3273 			convert_to_long_ex(newval);
3274 			timeout = 1000 * Z_LVAL_P(newval); /* Convert to milliseconds */
3275 			if (ldap_set_option(ldap, LDAP_X_OPT_CONNECT_TIMEOUT, &timeout)) {
3276 				RETURN_FALSE;
3277 			}
3278 		} break;
3279 #endif
3280 #ifdef LDAP_OPT_TIMEOUT
3281 	case LDAP_OPT_TIMEOUT:
3282 		{
3283 			struct timeval timeout;
3284 
3285 			convert_to_long_ex(newval);
3286 			timeout.tv_sec = Z_LVAL_P(newval);
3287 			timeout.tv_usec = 0;
3288 			if (ldap_set_option(ldap, LDAP_OPT_TIMEOUT, (void *) &timeout)) {
3289 				RETURN_FALSE;
3290 			}
3291 		} break;
3292 #endif
3293 		/* options with string value */
3294 	case LDAP_OPT_ERROR_STRING:
3295 #ifdef LDAP_OPT_HOST_NAME
3296 	case LDAP_OPT_HOST_NAME:
3297 #endif
3298 #ifdef HAVE_LDAP_SASL
3299 	case LDAP_OPT_X_SASL_MECH:
3300 	case LDAP_OPT_X_SASL_REALM:
3301 	case LDAP_OPT_X_SASL_AUTHCID:
3302 	case LDAP_OPT_X_SASL_AUTHZID:
3303 #endif
3304 #if (LDAP_API_VERSION > 2000)
3305 	case LDAP_OPT_X_TLS_CACERTDIR:
3306 	case LDAP_OPT_X_TLS_CACERTFILE:
3307 	case LDAP_OPT_X_TLS_CERTFILE:
3308 	case LDAP_OPT_X_TLS_CIPHER_SUITE:
3309 	case LDAP_OPT_X_TLS_KEYFILE:
3310 	case LDAP_OPT_X_TLS_RANDOM_FILE:
3311 #endif
3312 #ifdef LDAP_OPT_X_TLS_CRLFILE
3313 	case LDAP_OPT_X_TLS_CRLFILE:
3314 #endif
3315 #ifdef LDAP_OPT_X_TLS_DHFILE
3316 	case LDAP_OPT_X_TLS_DHFILE:
3317 #endif
3318 #ifdef LDAP_OPT_MATCHED_DN
3319 	case LDAP_OPT_MATCHED_DN:
3320 #endif
3321 		{
3322 			zend_string *val;
3323 			val = zval_get_string(newval);
3324 			if (EG(exception)) {
3325 				RETURN_FALSE;
3326 			}
3327 			if (ldap_set_option(ldap, option, ZSTR_VAL(val))) {
3328 				zend_string_release(val);
3329 				RETURN_FALSE;
3330 			}
3331 			zend_string_release(val);
3332 		} break;
3333 		/* options with boolean value */
3334 	case LDAP_OPT_REFERRALS:
3335 #ifdef LDAP_OPT_RESTART
3336 	case LDAP_OPT_RESTART:
3337 #endif
3338 #ifdef LDAP_OPT_X_SASL_NOCANON
3339 	case LDAP_OPT_X_SASL_NOCANON:
3340 #endif
3341 		{
3342 			void *val;
3343 			val = zend_is_true(newval) ? LDAP_OPT_ON : LDAP_OPT_OFF;
3344 			if (ldap_set_option(ldap, option, val)) {
3345 				RETURN_FALSE;
3346 			}
3347 		} break;
3348 		/* options with control list value */
3349 	case LDAP_OPT_SERVER_CONTROLS:
3350 	case LDAP_OPT_CLIENT_CONTROLS:
3351 		{
3352 			LDAPControl **ctrls;
3353 			int rc;
3354 
3355 			if (Z_TYPE_P(newval) != IS_ARRAY) {
3356 				php_error_docref(NULL, E_WARNING, "Expected array value for this option");
3357 				RETURN_FALSE;
3358 			}
3359 
3360 			ctrls = _php_ldap_controls_from_array(ldap, newval);
3361 
3362 			if (ctrls == NULL) {
3363 				RETURN_FALSE;
3364 			} else {
3365 				rc = ldap_set_option(ldap, option, ctrls);
3366 				_php_ldap_controls_free(&ctrls);
3367 				if (rc != LDAP_SUCCESS) {
3368 					RETURN_FALSE;
3369 				}
3370 			}
3371 		} break;
3372 	default:
3373 		RETURN_FALSE;
3374 	}
3375 	RETURN_TRUE;
3376 }
3377 /* }}} */
3378 
3379 #ifdef HAVE_LDAP_PARSE_RESULT
3380 /* {{{ proto bool ldap_parse_result(resource link, resource result, int &errcode [, string &matcheddn [, string &errmsg [, array &referrals [, array &controls]]]])
3381    Extract information from result */
PHP_FUNCTION(ldap_parse_result)3382 PHP_FUNCTION(ldap_parse_result)
3383 {
3384 	zval *link, *result, *errcode, *matcheddn, *errmsg, *referrals, *serverctrls;
3385 	ldap_linkdata *ld;
3386 	LDAPMessage *ldap_result;
3387 	LDAPControl **lserverctrls = NULL;
3388 	char **lreferrals, **refp;
3389 	char *lmatcheddn, *lerrmsg;
3390 	int rc, lerrcode, myargcount = ZEND_NUM_ARGS();
3391 
3392 	if (zend_parse_parameters(myargcount, "rrz|zzzz", &link, &result, &errcode, &matcheddn, &errmsg, &referrals, &serverctrls) != SUCCESS) {
3393 		return;
3394 	}
3395 
3396 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
3397 		RETURN_FALSE;
3398 	}
3399 
3400 	if ((ldap_result = (LDAPMessage *)zend_fetch_resource(Z_RES_P(result), "ldap result", le_result)) == NULL) {
3401 		RETURN_FALSE;
3402 	}
3403 
3404 	rc = ldap_parse_result(ld->link, ldap_result, &lerrcode,
3405 				myargcount > 3 ? &lmatcheddn : NULL,
3406 				myargcount > 4 ? &lerrmsg : NULL,
3407 				myargcount > 5 ? &lreferrals : NULL,
3408 				myargcount > 6 ? &lserverctrls : NULL,
3409 				0);
3410 	if (rc != LDAP_SUCCESS) {
3411 		php_error_docref(NULL, E_WARNING, "Unable to parse result: %s", ldap_err2string(rc));
3412 		RETURN_FALSE;
3413 	}
3414 
3415 	ZEND_TRY_ASSIGN_REF_LONG(errcode, lerrcode);
3416 
3417 	/* Reverse -> fall through */
3418 	switch (myargcount) {
3419 		case 7:
3420 			_php_ldap_controls_to_array(ld->link, lserverctrls, serverctrls, 0);
3421 		case 6:
3422 			referrals = zend_try_array_init(referrals);
3423 			if (!referrals) {
3424 				return;
3425 			}
3426 			if (lreferrals != NULL) {
3427 				refp = lreferrals;
3428 				while (*refp) {
3429 					add_next_index_string(referrals, *refp);
3430 					refp++;
3431 				}
3432 				ldap_memvfree((void**)lreferrals);
3433 			}
3434 		case 5:
3435 			if (lerrmsg == NULL) {
3436 				ZEND_TRY_ASSIGN_REF_EMPTY_STRING(errmsg);
3437 			} else {
3438 				ZEND_TRY_ASSIGN_REF_STRING(errmsg, lerrmsg);
3439 				ldap_memfree(lerrmsg);
3440 			}
3441 		case 4:
3442 			if (lmatcheddn == NULL) {
3443 				ZEND_TRY_ASSIGN_REF_EMPTY_STRING(matcheddn);
3444 			} else {
3445 				ZEND_TRY_ASSIGN_REF_STRING(matcheddn, lmatcheddn);
3446 				ldap_memfree(lmatcheddn);
3447 			}
3448 	}
3449 	RETURN_TRUE;
3450 }
3451 /* }}} */
3452 #endif
3453 
3454 /* {{{ Extended operation response parsing, Pierangelo Masarati */
3455 #ifdef HAVE_LDAP_PARSE_EXTENDED_RESULT
3456 /* {{{ proto bool ldap_parse_exop(resource link, resource result [, string &retdata [, string &retoid]])
3457    Extract information from extended operation result */
PHP_FUNCTION(ldap_parse_exop)3458 PHP_FUNCTION(ldap_parse_exop)
3459 {
3460 	zval *link, *result, *retdata, *retoid;
3461 	ldap_linkdata *ld;
3462 	LDAPMessage *ldap_result;
3463 	char *lretoid;
3464 	struct berval *lretdata;
3465 	int rc, myargcount = ZEND_NUM_ARGS();
3466 
3467 	if (zend_parse_parameters(myargcount, "rr|zz", &link, &result, &retdata, &retoid) != SUCCESS) {
3468 		WRONG_PARAM_COUNT;
3469 	}
3470 
3471 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
3472 		RETURN_FALSE;
3473 	}
3474 
3475 	if ((ldap_result = (LDAPMessage *)zend_fetch_resource(Z_RES_P(result), "ldap result", le_result)) == NULL) {
3476 		RETURN_FALSE;
3477 	}
3478 
3479 	rc = ldap_parse_extended_result(ld->link, ldap_result,
3480 				myargcount > 3 ? &lretoid: NULL,
3481 				myargcount > 2 ? &lretdata: NULL,
3482 				0);
3483 	if (rc != LDAP_SUCCESS) {
3484 		php_error_docref(NULL, E_WARNING, "Unable to parse extended operation result: %s", ldap_err2string(rc));
3485 		RETURN_FALSE;
3486 	}
3487 
3488 	/* Reverse -> fall through */
3489 	switch (myargcount) {
3490 		case 4:
3491 			if (lretoid == NULL) {
3492 				ZEND_TRY_ASSIGN_REF_EMPTY_STRING(retoid);
3493 			} else {
3494 				ZEND_TRY_ASSIGN_REF_STRING(retoid, lretoid);
3495 				ldap_memfree(lretoid);
3496 			}
3497 		case 3:
3498 			/* use arg #3 as the data returned by the server */
3499 			if (lretdata == NULL) {
3500 				ZEND_TRY_ASSIGN_REF_EMPTY_STRING(retdata);
3501 			} else {
3502 				ZEND_TRY_ASSIGN_REF_STRINGL(retdata, lretdata->bv_val, lretdata->bv_len);
3503 				ldap_memfree(lretdata->bv_val);
3504 				ldap_memfree(lretdata);
3505 			}
3506 	}
3507 	RETURN_TRUE;
3508 }
3509 /* }}} */
3510 #endif
3511 /* }}} */
3512 
3513 /* {{{ proto resource ldap_first_reference(resource link, resource result)
3514    Return first reference */
PHP_FUNCTION(ldap_first_reference)3515 PHP_FUNCTION(ldap_first_reference)
3516 {
3517 	zval *link, *result;
3518 	ldap_linkdata *ld;
3519 	ldap_resultentry *resultentry;
3520 	LDAPMessage *ldap_result, *entry;
3521 
3522 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &link, &result) != SUCCESS) {
3523 		return;
3524 	}
3525 
3526 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
3527 		RETURN_FALSE;
3528 	}
3529 
3530 	if ((ldap_result = (LDAPMessage *)zend_fetch_resource(Z_RES_P(result), "ldap result", le_result)) == NULL) {
3531 		RETURN_FALSE;
3532 	}
3533 
3534 	if ((entry = ldap_first_reference(ld->link, ldap_result)) == NULL) {
3535 		RETVAL_FALSE;
3536 	} else {
3537 		resultentry = emalloc(sizeof(ldap_resultentry));
3538 		RETVAL_RES(zend_register_resource(resultentry, le_result_entry));
3539 		ZVAL_COPY(&resultentry->res, result);
3540 		resultentry->data = entry;
3541 		resultentry->ber = NULL;
3542 	}
3543 }
3544 /* }}} */
3545 
3546 /* {{{ proto resource ldap_next_reference(resource link, resource reference_entry)
3547    Get next reference */
PHP_FUNCTION(ldap_next_reference)3548 PHP_FUNCTION(ldap_next_reference)
3549 {
3550 	zval *link, *result_entry;
3551 	ldap_linkdata *ld;
3552 	ldap_resultentry *resultentry, *resultentry_next;
3553 	LDAPMessage *entry_next;
3554 
3555 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &link, &result_entry) != SUCCESS) {
3556 		return;
3557 	}
3558 
3559 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
3560 		RETURN_FALSE;
3561 	}
3562 
3563 	if ((resultentry = (ldap_resultentry *)zend_fetch_resource(Z_RES_P(result_entry), "ldap result entry", le_result_entry)) == NULL) {
3564 		RETURN_FALSE;
3565 	}
3566 
3567 	if ((entry_next = ldap_next_reference(ld->link, resultentry->data)) == NULL) {
3568 		RETVAL_FALSE;
3569 	} else {
3570 		resultentry_next = emalloc(sizeof(ldap_resultentry));
3571 		RETVAL_RES(zend_register_resource(resultentry_next, le_result_entry));
3572 		ZVAL_COPY(&resultentry_next->res, &resultentry->res);
3573 		resultentry_next->data = entry_next;
3574 		resultentry_next->ber = NULL;
3575 	}
3576 }
3577 /* }}} */
3578 
3579 #ifdef HAVE_LDAP_PARSE_REFERENCE
3580 /* {{{ proto bool ldap_parse_reference(resource link, resource reference_entry, array &referrals)
3581    Extract information from reference entry */
PHP_FUNCTION(ldap_parse_reference)3582 PHP_FUNCTION(ldap_parse_reference)
3583 {
3584 	zval *link, *result_entry, *referrals;
3585 	ldap_linkdata *ld;
3586 	ldap_resultentry *resultentry;
3587 	char **lreferrals, **refp;
3588 
3589 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rrz", &link, &result_entry, &referrals) != SUCCESS) {
3590 		return;
3591 	}
3592 
3593 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
3594 		RETURN_FALSE;
3595 	}
3596 
3597 	if ((resultentry = (ldap_resultentry *)zend_fetch_resource(Z_RES_P(result_entry), "ldap result entry", le_result_entry)) == NULL) {
3598 		RETURN_FALSE;
3599 	}
3600 
3601 	if (ldap_parse_reference(ld->link, resultentry->data, &lreferrals, NULL /* &serverctrls */, 0) != LDAP_SUCCESS) {
3602 		RETURN_FALSE;
3603 	}
3604 
3605 	referrals = zend_try_array_init(referrals);
3606 	if (!referrals) {
3607 		return;
3608 	}
3609 
3610 	if (lreferrals != NULL) {
3611 		refp = lreferrals;
3612 		while (*refp) {
3613 			add_next_index_string(referrals, *refp);
3614 			refp++;
3615 		}
3616 		ldap_memvfree((void**)lreferrals);
3617 	}
3618 	RETURN_TRUE;
3619 }
3620 /* }}} */
3621 #endif
3622 
3623 /* {{{ php_ldap_do_rename
3624  */
php_ldap_do_rename(INTERNAL_FUNCTION_PARAMETERS,int ext)3625 static void php_ldap_do_rename(INTERNAL_FUNCTION_PARAMETERS, int ext)
3626 {
3627 	zval *serverctrls = NULL;
3628 	zval *link;
3629 	ldap_linkdata *ld;
3630 	LDAPControl **lserverctrls = NULL;
3631 	LDAPMessage *ldap_res;
3632 	int rc, msgid;
3633 	char *dn, *newrdn, *newparent;
3634 	size_t dn_len, newrdn_len, newparent_len;
3635 	zend_bool deleteoldrdn;
3636 
3637 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsssb|a", &link, &dn, &dn_len, &newrdn, &newrdn_len, &newparent, &newparent_len, &deleteoldrdn, &serverctrls) != SUCCESS) {
3638 		return;
3639 	}
3640 
3641 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
3642 		RETURN_FALSE;
3643 	}
3644 
3645 	if (newparent_len == 0) {
3646 		newparent = NULL;
3647 	}
3648 
3649 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP
3650 	if (serverctrls) {
3651 		lserverctrls = _php_ldap_controls_from_array(ld->link, serverctrls);
3652 		if (lserverctrls == NULL) {
3653 			RETVAL_FALSE;
3654 			goto cleanup;
3655 		}
3656 	}
3657 
3658 	if (ext) {
3659 		rc = ldap_rename(ld->link, dn, newrdn, newparent, deleteoldrdn, lserverctrls, NULL, &msgid);
3660 	} else {
3661 		rc = ldap_rename_s(ld->link, dn, newrdn, newparent, deleteoldrdn, lserverctrls, NULL);
3662 	}
3663 #else
3664 	if (newparent_len != 0) {
3665 		php_error_docref(NULL, E_WARNING, "You are using old LDAP API, newparent must be the empty string, can only modify RDN");
3666 		RETURN_FALSE;
3667 	}
3668 	if (serverctrls) {
3669 		php_error_docref(NULL, E_WARNING, "You are using old LDAP API, controls are not supported");
3670 		RETURN_FALSE;
3671 	}
3672 	if (ext) {
3673 		php_error_docref(NULL, E_WARNING, "You are using old LDAP API, ldap_rename_ext is not supported");
3674 		RETURN_FALSE;
3675 	}
3676 /* could support old APIs but need check for ldap_modrdn2()/ldap_modrdn() */
3677 	rc = ldap_modrdn2_s(ld->link, dn, newrdn, deleteoldrdn);
3678 #endif
3679 
3680 	if (rc != LDAP_SUCCESS) {
3681 		RETVAL_FALSE;
3682 	} else if (ext) {
3683 		rc = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res);
3684 		if (rc == -1) {
3685 			php_error_docref(NULL, E_WARNING, "Rename operation failed");
3686 			RETVAL_FALSE;
3687 			goto cleanup;
3688 		}
3689 
3690 		/* return a PHP control object */
3691 		RETVAL_RES(zend_register_resource(ldap_res, le_result));
3692 	} else {
3693 		RETVAL_TRUE;
3694 	}
3695 
3696 cleanup:
3697 	if (lserverctrls) {
3698 		_php_ldap_controls_free(&lserverctrls);
3699 	}
3700 
3701 	return;
3702 }
3703 /* }}} */
3704 
3705 /* {{{ proto bool ldap_rename(resource link, string dn, string newrdn, string newparent, bool deleteoldrdn [, array servercontrols])
3706    Modify the name of an entry */
PHP_FUNCTION(ldap_rename)3707 PHP_FUNCTION(ldap_rename)
3708 {
3709 	php_ldap_do_rename(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
3710 }
3711 /* }}} */
3712 
3713 /* {{{ proto resource ldap_rename_ext(resource link, string dn, string newrdn, string newparent, bool deleteoldrdn [, array servercontrols])
3714    Modify the name of an entry */
PHP_FUNCTION(ldap_rename_ext)3715 PHP_FUNCTION(ldap_rename_ext)
3716 {
3717 	php_ldap_do_rename(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
3718 }
3719 /* }}} */
3720 
3721 #ifdef HAVE_LDAP_START_TLS_S
3722 /* {{{ proto bool ldap_start_tls(resource link)
3723    Start TLS */
PHP_FUNCTION(ldap_start_tls)3724 PHP_FUNCTION(ldap_start_tls)
3725 {
3726 	zval *link;
3727 	ldap_linkdata *ld;
3728 	int rc, protocol = LDAP_VERSION3;
3729 
3730 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &link) != SUCCESS) {
3731 		return;
3732 	}
3733 
3734 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
3735 		RETURN_FALSE;
3736 	}
3737 
3738 	if (((rc = ldap_set_option(ld->link, LDAP_OPT_PROTOCOL_VERSION, &protocol)) != LDAP_SUCCESS) ||
3739 		((rc = ldap_start_tls_s(ld->link, NULL, NULL)) != LDAP_SUCCESS)
3740 	) {
3741 		php_error_docref(NULL, E_WARNING,"Unable to start TLS: %s", ldap_err2string(rc));
3742 		RETURN_FALSE;
3743 	} else {
3744 		RETURN_TRUE;
3745 	}
3746 }
3747 /* }}} */
3748 #endif
3749 #endif /* (LDAP_API_VERSION > 2000) || HAVE_ORALDAP */
3750 
3751 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC)
3752 /* {{{ _ldap_rebind_proc()
3753 */
_ldap_rebind_proc(LDAP * ldap,const char * url,ber_tag_t req,ber_int_t msgid,void * params)3754 int _ldap_rebind_proc(LDAP *ldap, const char *url, ber_tag_t req, ber_int_t msgid, void *params)
3755 {
3756 	ldap_linkdata *ld;
3757 	int retval;
3758 	zval cb_args[2];
3759 	zval cb_retval;
3760 	zval *cb_link = (zval *) params;
3761 
3762 	ld = (ldap_linkdata *) zend_fetch_resource_ex(cb_link, "ldap link", le_link);
3763 
3764 	/* link exists and callback set? */
3765 	if (ld == NULL || Z_ISUNDEF(ld->rebindproc)) {
3766 		php_error_docref(NULL, E_WARNING, "Link not found or no callback set");
3767 		return LDAP_OTHER;
3768 	}
3769 
3770 	/* callback */
3771 	ZVAL_COPY_VALUE(&cb_args[0], cb_link);
3772 	ZVAL_STRING(&cb_args[1], url);
3773 	if (call_user_function_ex(EG(function_table), NULL, &ld->rebindproc, &cb_retval, 2, cb_args, 0, NULL) == SUCCESS && !Z_ISUNDEF(cb_retval)) {
3774 		retval = zval_get_long(&cb_retval);
3775 		zval_ptr_dtor(&cb_retval);
3776 	} else {
3777 		php_error_docref(NULL, E_WARNING, "rebind_proc PHP callback failed");
3778 		retval = LDAP_OTHER;
3779 	}
3780 	zval_ptr_dtor(&cb_args[1]);
3781 	return retval;
3782 }
3783 /* }}} */
3784 
3785 /* {{{ proto bool ldap_set_rebind_proc(resource link, string callback)
3786    Set a callback function to do re-binds on referral chasing. */
PHP_FUNCTION(ldap_set_rebind_proc)3787 PHP_FUNCTION(ldap_set_rebind_proc)
3788 {
3789 	zval *link, *callback;
3790 	ldap_linkdata *ld;
3791 
3792 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rz", &link, &callback) != SUCCESS) {
3793 		RETURN_FALSE;
3794 	}
3795 
3796 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
3797 		RETURN_FALSE;
3798 	}
3799 
3800 	if (Z_TYPE_P(callback) == IS_STRING && Z_STRLEN_P(callback) == 0) {
3801 		/* unregister rebind procedure */
3802 		if (!Z_ISUNDEF(ld->rebindproc)) {
3803 			zval_ptr_dtor(&ld->rebindproc);
3804 			ZVAL_UNDEF(&ld->rebindproc);
3805 			ldap_set_rebind_proc(ld->link, NULL, NULL);
3806 		}
3807 		RETURN_TRUE;
3808 	}
3809 
3810 	/* callable? */
3811 	if (!zend_is_callable(callback, 0, NULL)) {
3812 		zend_string *callback_name = zend_get_callable_name(callback);
3813 		php_error_docref(NULL, E_WARNING, "Two arguments expected for '%s' to be a valid callback", ZSTR_VAL(callback_name));
3814 		zend_string_release_ex(callback_name, 0);
3815 		RETURN_FALSE;
3816 	}
3817 
3818 	/* register rebind procedure */
3819 	if (Z_ISUNDEF(ld->rebindproc)) {
3820 		ldap_set_rebind_proc(ld->link, _ldap_rebind_proc, (void *) link);
3821 	} else {
3822 		zval_ptr_dtor(&ld->rebindproc);
3823 	}
3824 
3825 	ZVAL_COPY(&ld->rebindproc, callback);
3826 	RETURN_TRUE;
3827 }
3828 /* }}} */
3829 #endif
3830 
php_ldap_do_escape(const zend_bool * map,const char * value,size_t valuelen,zend_long flags)3831 static zend_string* php_ldap_do_escape(const zend_bool *map, const char *value, size_t valuelen, zend_long flags)
3832 {
3833 	char hex[] = "0123456789abcdef";
3834 	size_t i, p = 0;
3835 	size_t len = 0;
3836 	zend_string *ret;
3837 
3838 	for (i = 0; i < valuelen; i++) {
3839 		len += (map[(unsigned char) value[i]]) ? 3 : 1;
3840 	}
3841 	/* Per RFC 4514, a leading and trailing space must be escaped */
3842 	if ((flags & PHP_LDAP_ESCAPE_DN) && (value[0] == ' ')) {
3843 		len += 2;
3844 	}
3845 	if ((flags & PHP_LDAP_ESCAPE_DN) && ((valuelen > 1) && (value[valuelen - 1] == ' '))) {
3846 		len += 2;
3847 	}
3848 
3849 	ret =  zend_string_alloc(len, 0);
3850 
3851 	for (i = 0; i < valuelen; i++) {
3852 		unsigned char v = (unsigned char) value[i];
3853 
3854 		if (map[v] || ((flags & PHP_LDAP_ESCAPE_DN) && ((i == 0) || (i + 1 == valuelen)) && (v == ' '))) {
3855 			ZSTR_VAL(ret)[p++] = '\\';
3856 			ZSTR_VAL(ret)[p++] = hex[v >> 4];
3857 			ZSTR_VAL(ret)[p++] = hex[v & 0x0f];
3858 		} else {
3859 			ZSTR_VAL(ret)[p++] = v;
3860 		}
3861 	}
3862 
3863 	ZSTR_VAL(ret)[p] = '\0';
3864 	ZSTR_LEN(ret) = p;
3865 	return ret;
3866 }
3867 
php_ldap_escape_map_set_chars(zend_bool * map,const char * chars,const size_t charslen,char escape)3868 static void php_ldap_escape_map_set_chars(zend_bool *map, const char *chars, const size_t charslen, char escape)
3869 {
3870 	size_t i = 0;
3871 	while (i < charslen) {
3872 		map[(unsigned char) chars[i++]] = escape;
3873 	}
3874 }
3875 
PHP_FUNCTION(ldap_escape)3876 PHP_FUNCTION(ldap_escape)
3877 {
3878 	char *value, *ignores;
3879 	size_t valuelen = 0, ignoreslen = 0;
3880 	int i;
3881 	zend_long flags = 0;
3882 	zend_bool map[256] = {0}, havecharlist = 0;
3883 
3884 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|sl", &value, &valuelen, &ignores, &ignoreslen, &flags) != SUCCESS) {
3885 		return;
3886 	}
3887 
3888 	if (!valuelen) {
3889 		RETURN_EMPTY_STRING();
3890 	}
3891 
3892 	if (flags & PHP_LDAP_ESCAPE_FILTER) {
3893 		havecharlist = 1;
3894 		php_ldap_escape_map_set_chars(map, "\\*()\0", sizeof("\\*()\0") - 1, 1);
3895 	}
3896 
3897 	if (flags & PHP_LDAP_ESCAPE_DN) {
3898 		havecharlist = 1;
3899 		php_ldap_escape_map_set_chars(map, "\\,=+<>;\"#\r", sizeof("\\,=+<>;\"#\r") - 1, 1);
3900 	}
3901 
3902 	if (!havecharlist) {
3903 		for (i = 0; i < 256; i++) {
3904 			map[i] = 1;
3905 		}
3906 	}
3907 
3908 	if (ignoreslen) {
3909 		php_ldap_escape_map_set_chars(map, ignores, ignoreslen, 0);
3910 	}
3911 
3912 	RETURN_NEW_STR(php_ldap_do_escape(map, value, valuelen, flags));
3913 }
3914 
3915 #ifdef STR_TRANSLATION
3916 /* {{{ php_ldap_do_translate
3917  */
php_ldap_do_translate(INTERNAL_FUNCTION_PARAMETERS,int way)3918 static void php_ldap_do_translate(INTERNAL_FUNCTION_PARAMETERS, int way)
3919 {
3920 	char *value;
3921 	size_t value_len;
3922 	int result;
3923 
3924 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &value, &value_len) != SUCCESS) {
3925 		return;
3926 	}
3927 
3928 	if (value_len == 0) {
3929 		RETURN_FALSE;
3930 	}
3931 
3932 	if (way == 1) {
3933 		result = ldap_8859_to_t61(&value, &value_len, 0);
3934 	} else {
3935 		result = ldap_t61_to_8859(&value, &value_len, 0);
3936 	}
3937 
3938 	if (result == LDAP_SUCCESS) {
3939 		RETVAL_STRINGL(value, value_len);
3940 		free(value);
3941 	} else {
3942 		php_error_docref(NULL, E_WARNING, "Conversion from iso-8859-1 to t61 failed: %s", ldap_err2string(result));
3943 		RETVAL_FALSE;
3944 	}
3945 }
3946 /* }}} */
3947 
3948 /* {{{ proto string ldap_t61_to_8859(string value)
3949    Translate t61 characters to 8859 characters */
PHP_FUNCTION(ldap_t61_to_8859)3950 PHP_FUNCTION(ldap_t61_to_8859)
3951 {
3952 	php_ldap_do_translate(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
3953 }
3954 /* }}} */
3955 
3956 /* {{{ proto string ldap_8859_to_t61(string value)
3957    Translate 8859 characters to t61 characters */
PHP_FUNCTION(ldap_8859_to_t61)3958 PHP_FUNCTION(ldap_8859_to_t61)
3959 {
3960 	php_ldap_do_translate(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
3961 }
3962 /* }}} */
3963 #endif
3964 
3965 #ifdef LDAP_CONTROL_PAGEDRESULTS
3966 /* {{{ proto mixed ldap_control_paged_result(resource link, int pagesize [, bool iscritical [, string cookie]])
3967    Inject paged results control*/
PHP_FUNCTION(ldap_control_paged_result)3968 PHP_FUNCTION(ldap_control_paged_result)
3969 {
3970 	zend_long pagesize;
3971 	zend_bool iscritical;
3972 	zval *link;
3973 	char *cookie = NULL;
3974 	size_t cookie_len = 0;
3975 	struct berval lcookie = { 0L, NULL };
3976 	ldap_linkdata *ld;
3977 	LDAP *ldap;
3978 	BerElement *ber = NULL;
3979 	LDAPControl	ctrl, *ctrlsp[2];
3980 	int rc, myargcount = ZEND_NUM_ARGS();
3981 
3982 	if (zend_parse_parameters(myargcount, "rl|bs", &link, &pagesize, &iscritical, &cookie, &cookie_len) != SUCCESS) {
3983 		return;
3984 	}
3985 
3986 	if (Z_TYPE_P(link) == IS_NULL) {
3987 		ldap = NULL;
3988 	} else {
3989 		if ((ld = (ldap_linkdata *)zend_fetch_resource_ex(link, "ldap link", le_link)) == NULL) {
3990 			RETURN_FALSE;
3991 		}
3992 		ldap = ld->link;
3993 	}
3994 
3995 	ber = ber_alloc_t(LBER_USE_DER);
3996 	if (ber == NULL) {
3997 		php_error_docref(NULL, E_WARNING, "Unable to alloc BER encoding resources for paged results control");
3998 		RETURN_FALSE;
3999 	}
4000 
4001 	ctrl.ldctl_iscritical = 0;
4002 
4003 	switch (myargcount) {
4004 		case 4:
4005 			lcookie.bv_val = cookie;
4006 			lcookie.bv_len = cookie_len;
4007 			/* fallthru */
4008 		case 3:
4009 			ctrl.ldctl_iscritical = (int)iscritical;
4010 			/* fallthru */
4011 	}
4012 
4013 	if (ber_printf(ber, "{iO}", (int)pagesize, &lcookie) == LBER_ERROR) {
4014 		php_error_docref(NULL, E_WARNING, "Unable to BER printf paged results control");
4015 		RETVAL_FALSE;
4016 		goto lcpr_error_out;
4017 	}
4018 	rc = ber_flatten2(ber, &ctrl.ldctl_value, 0);
4019 	if (rc == LBER_ERROR) {
4020 		php_error_docref(NULL, E_WARNING, "Unable to BER encode paged results control");
4021 		RETVAL_FALSE;
4022 		goto lcpr_error_out;
4023 	}
4024 
4025 	ctrl.ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
4026 
4027 	if (ldap) {
4028 		/* directly set the option */
4029 		ctrlsp[0] = &ctrl;
4030 		ctrlsp[1] = NULL;
4031 
4032 		rc = ldap_set_option(ldap, LDAP_OPT_SERVER_CONTROLS, ctrlsp);
4033 		if (rc != LDAP_SUCCESS) {
4034 			php_error_docref(NULL, E_WARNING, "Unable to set paged results control: %s (%d)", ldap_err2string(rc), rc);
4035 			RETVAL_FALSE;
4036 			goto lcpr_error_out;
4037 		}
4038 		RETVAL_TRUE;
4039 	} else {
4040 		/* return a PHP control object */
4041 		array_init(return_value);
4042 
4043 		add_assoc_string(return_value, "oid", ctrl.ldctl_oid);
4044 		if (ctrl.ldctl_value.bv_len) {
4045 			add_assoc_stringl(return_value, "value", ctrl.ldctl_value.bv_val, ctrl.ldctl_value.bv_len);
4046 		}
4047 		if (ctrl.ldctl_iscritical) {
4048 			add_assoc_bool(return_value, "iscritical", ctrl.ldctl_iscritical);
4049 		}
4050 	}
4051 
4052 lcpr_error_out:
4053 	if (ber != NULL) {
4054 		ber_free(ber, 1);
4055 	}
4056 	return;
4057 }
4058 /* }}} */
4059 
4060 /* {{{ proto bool ldap_control_paged_result_response(resource link, resource result [, string &cookie [, int &estimated]])
4061    Extract paged results control response */
PHP_FUNCTION(ldap_control_paged_result_response)4062 PHP_FUNCTION(ldap_control_paged_result_response)
4063 {
4064 	zval *link, *result, *cookie, *estimated;
4065 	struct berval lcookie;
4066 	int lestimated;
4067 	ldap_linkdata *ld;
4068 	LDAPMessage *ldap_result;
4069 	LDAPControl **lserverctrls, *lctrl;
4070 	BerElement *ber;
4071 	ber_tag_t tag;
4072 	int rc, lerrcode, myargcount = ZEND_NUM_ARGS();
4073 
4074 	if (zend_parse_parameters(myargcount, "rr|zz", &link, &result, &cookie, &estimated) != SUCCESS) {
4075 		return;
4076 	}
4077 
4078 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
4079 		RETURN_FALSE;
4080 	}
4081 
4082 	if ((ldap_result = (LDAPMessage *)zend_fetch_resource(Z_RES_P(result), "ldap result", le_result)) == NULL) {
4083 		RETURN_FALSE;
4084 	}
4085 
4086 	rc = ldap_parse_result(ld->link,
4087 				ldap_result,
4088 				&lerrcode,
4089 				NULL,		/* matcheddn */
4090 				NULL,		/* errmsg */
4091 				NULL,		/* referrals */
4092 				&lserverctrls,
4093 				0);
4094 
4095 	if (rc != LDAP_SUCCESS) {
4096 		php_error_docref(NULL, E_WARNING, "Unable to parse result: %s (%d)", ldap_err2string(rc), rc);
4097 		RETURN_FALSE;
4098 	}
4099 
4100 	if (lerrcode != LDAP_SUCCESS) {
4101 		php_error_docref(NULL, E_WARNING, "Result is: %s (%d)", ldap_err2string(lerrcode), lerrcode);
4102 		RETURN_FALSE;
4103 	}
4104 
4105 	if (lserverctrls == NULL) {
4106 		php_error_docref(NULL, E_WARNING, "No server controls in result");
4107 		RETURN_FALSE;
4108 	}
4109 
4110 	lctrl = ldap_control_find(LDAP_CONTROL_PAGEDRESULTS, lserverctrls, NULL);
4111 	if (lctrl == NULL) {
4112 		ldap_controls_free(lserverctrls);
4113 		php_error_docref(NULL, E_WARNING, "No paged results control response in result");
4114 		RETURN_FALSE;
4115 	}
4116 
4117 	ber = ber_init(&lctrl->ldctl_value);
4118 	if (ber == NULL) {
4119 		ldap_controls_free(lserverctrls);
4120 		php_error_docref(NULL, E_WARNING, "Unable to alloc BER decoding resources for paged results control response");
4121 		RETURN_FALSE;
4122 	}
4123 
4124 	tag = ber_scanf(ber, "{io}", &lestimated, &lcookie);
4125 	(void)ber_free(ber, 1);
4126 
4127 	if (tag == LBER_ERROR) {
4128 		ldap_controls_free(lserverctrls);
4129 		php_error_docref(NULL, E_WARNING, "Unable to decode paged results control response");
4130 		RETURN_FALSE;
4131 	}
4132 
4133 	if (lestimated < 0) {
4134 		ldap_controls_free(lserverctrls);
4135 		php_error_docref(NULL, E_WARNING, "Invalid paged results control response value");
4136 		RETURN_FALSE;
4137 	}
4138 
4139 	ldap_controls_free(lserverctrls);
4140 	if (myargcount == 4) {
4141 		ZEND_TRY_ASSIGN_REF_LONG(estimated, lestimated);
4142 	}
4143 
4144  	if (lcookie.bv_len == 0) {
4145 		ZEND_TRY_ASSIGN_REF_EMPTY_STRING(cookie);
4146  	} else {
4147 		ZEND_TRY_ASSIGN_REF_STRINGL(cookie, lcookie.bv_val, lcookie.bv_len);
4148  	}
4149  	ldap_memfree(lcookie.bv_val);
4150 
4151 	RETURN_TRUE;
4152 }
4153 /* }}} */
4154 #endif
4155 
4156 /* {{{ Extended operations, Pierangelo Masarati */
4157 #ifdef HAVE_LDAP_EXTENDED_OPERATION_S
4158 /* {{{ proto resource ldap_exop(resource link, string reqoid [, string reqdata [, array servercontrols [, string &retdata [, string &retoid]]]])
4159    Extended operation */
PHP_FUNCTION(ldap_exop)4160 PHP_FUNCTION(ldap_exop)
4161 {
4162 	zval *serverctrls = NULL;
4163 	zval *link, *retdata = NULL, *retoid = NULL;
4164 	char *lretoid = NULL;
4165 	zend_string *reqoid, *reqdata = NULL;
4166 	struct berval lreqdata, *lretdata = NULL;
4167 	ldap_linkdata *ld;
4168 	LDAPMessage *ldap_res;
4169 	LDAPControl **lserverctrls = NULL;
4170 	int rc, msgid;
4171 
4172 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rS|S!a!zz", &link, &reqoid, &reqdata, &serverctrls, &retdata, &retoid) != SUCCESS) {
4173 		return;
4174 	}
4175 
4176 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
4177 		RETURN_FALSE;
4178 	}
4179 
4180 	if (reqdata) {
4181 		lreqdata.bv_val = ZSTR_VAL(reqdata);
4182 		lreqdata.bv_len = ZSTR_LEN(reqdata);
4183 	} else {
4184 		lreqdata.bv_len = 0;
4185 	}
4186 
4187 	if (serverctrls) {
4188 		lserverctrls = _php_ldap_controls_from_array(ld->link, serverctrls);
4189 		if (lserverctrls == NULL) {
4190 			RETVAL_FALSE;
4191 			goto cleanup;
4192 		}
4193 	}
4194 
4195 	if (retdata) {
4196 		/* synchronous call */
4197 		rc = ldap_extended_operation_s(ld->link, ZSTR_VAL(reqoid),
4198 			lreqdata.bv_len > 0 ? &lreqdata: NULL,
4199 			lserverctrls,
4200 			NULL,
4201 			retoid ? &lretoid : NULL,
4202 			&lretdata );
4203 		if (rc != LDAP_SUCCESS ) {
4204 			php_error_docref(NULL, E_WARNING, "Extended operation %s failed: %s (%d)", ZSTR_VAL(reqoid), ldap_err2string(rc), rc);
4205 			RETVAL_FALSE;
4206 			goto cleanup;
4207 		}
4208 
4209 		if (retoid) {
4210 			if (lretoid) {
4211 				ZEND_TRY_ASSIGN_REF_STRING(retoid, lretoid);
4212 				ldap_memfree(lretoid);
4213 			} else {
4214 				ZEND_TRY_ASSIGN_REF_EMPTY_STRING(retoid);
4215 			}
4216 		}
4217 
4218 		if (lretdata) {
4219 			ZEND_TRY_ASSIGN_REF_STRINGL(retdata, lretdata->bv_val, lretdata->bv_len);
4220 			ldap_memfree(lretdata->bv_val);
4221 			ldap_memfree(lretdata);
4222 		} else {
4223 			ZEND_TRY_ASSIGN_REF_EMPTY_STRING(retdata);
4224 		}
4225 
4226 		RETVAL_TRUE;
4227 		goto cleanup;
4228 	}
4229 
4230 	/* asynchronous call */
4231 	rc = ldap_extended_operation(ld->link, ZSTR_VAL(reqoid),
4232 		lreqdata.bv_len > 0 ? &lreqdata: NULL,
4233 		lserverctrls,
4234 		NULL,
4235 		&msgid);
4236 	if (rc != LDAP_SUCCESS ) {
4237 		php_error_docref(NULL, E_WARNING, "Extended operation %s failed: %s (%d)", ZSTR_VAL(reqoid), ldap_err2string(rc), rc);
4238 		RETVAL_FALSE;
4239 		goto cleanup;
4240 	}
4241 
4242 	rc = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res);
4243 	if (rc == -1) {
4244 		php_error_docref(NULL, E_WARNING, "Extended operation %s failed", ZSTR_VAL(reqoid));
4245 		RETVAL_FALSE;
4246 		goto cleanup;
4247 	}
4248 
4249 	/* return a PHP control object */
4250 	RETVAL_RES(zend_register_resource(ldap_res, le_result));
4251 
4252 	cleanup:
4253 	if (lserverctrls) {
4254 		_php_ldap_controls_free(&lserverctrls);
4255 	}
4256 }
4257 /* }}} */
4258 #endif
4259 
4260 #ifdef HAVE_LDAP_PASSWD
4261 /* {{{ proto bool|string ldap_exop_passwd(resource link [, string user [, string oldpw [, string newpw [, array ctrls]]]])
4262    Passwd modify extended operation */
PHP_FUNCTION(ldap_exop_passwd)4263 PHP_FUNCTION(ldap_exop_passwd)
4264 {
4265 	zval *link, *serverctrls;
4266 	struct berval luser = { 0L, NULL };
4267 	struct berval loldpw = { 0L, NULL };
4268 	struct berval lnewpw = { 0L, NULL };
4269 	struct berval lgenpasswd = { 0L, NULL };
4270 	LDAPControl *ctrl, **lserverctrls = NULL, *requestctrls[2] = { NULL, NULL };
4271 	LDAPMessage* ldap_res = NULL;
4272 	ldap_linkdata *ld;
4273 	int rc, myargcount = ZEND_NUM_ARGS(), msgid, err;
4274 	char* errmsg = NULL;
4275 
4276 	if (zend_parse_parameters(myargcount, "r|sssz/", &link, &luser.bv_val, &luser.bv_len, &loldpw.bv_val, &loldpw.bv_len, &lnewpw.bv_val, &lnewpw.bv_len, &serverctrls) == FAILURE) {
4277 		return;
4278 	}
4279 
4280 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
4281 		RETVAL_FALSE;
4282 		goto cleanup;
4283 	}
4284 
4285 	switch (myargcount) {
4286 		case 5:
4287 			/* ldap_create_passwordpolicy_control() allocates ctrl */
4288 			if (ldap_create_passwordpolicy_control(ld->link, &ctrl) == LDAP_SUCCESS) {
4289 				requestctrls[0] = ctrl;
4290 			}
4291 	}
4292 
4293 	/* asynchronous call to get result and controls */
4294 	rc = ldap_passwd(ld->link, &luser,
4295 		loldpw.bv_len > 0 ? &loldpw : NULL,
4296 		lnewpw.bv_len > 0 ? &lnewpw : NULL,
4297 		requestctrls,
4298 		NULL, &msgid);
4299 
4300 	if (requestctrls[0] != NULL) {
4301 		ldap_control_free(requestctrls[0]);
4302 	}
4303 
4304 	if (rc != LDAP_SUCCESS ) {
4305 		php_error_docref(NULL, E_WARNING, "Passwd modify extended operation failed: %s (%d)", ldap_err2string(rc), rc);
4306 		RETVAL_FALSE;
4307 		goto cleanup;
4308 	}
4309 
4310 	rc = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res);
4311 	if ((rc < 0) || !ldap_res) {
4312 		rc = _get_lderrno(ld->link);
4313 		php_error_docref(NULL, E_WARNING, "Passwd modify extended operation failed: %s (%d)", ldap_err2string(rc), rc);
4314 		RETVAL_FALSE;
4315 		goto cleanup;
4316 	}
4317 
4318 	rc = ldap_parse_passwd(ld->link, ldap_res, &lgenpasswd);
4319 	if( rc != LDAP_SUCCESS ) {
4320 		php_error_docref(NULL, E_WARNING, "Passwd modify extended operation failed: %s (%d)", ldap_err2string(rc), rc);
4321 		RETVAL_FALSE;
4322 		goto cleanup;
4323 	}
4324 
4325 	rc = ldap_parse_result(ld->link, ldap_res, &err, NULL, &errmsg, NULL, (myargcount > 4 ? &lserverctrls : NULL), 0);
4326 	if( rc != LDAP_SUCCESS ) {
4327 		php_error_docref(NULL, E_WARNING, "Passwd modify extended operation failed: %s (%d)", ldap_err2string(rc), rc);
4328 		RETVAL_FALSE;
4329 		goto cleanup;
4330 	}
4331 
4332 	if (myargcount > 4) {
4333 		_php_ldap_controls_to_array(ld->link, lserverctrls, serverctrls, 0);
4334 	}
4335 
4336 	/* return */
4337 	if (lnewpw.bv_len == 0) {
4338 		if (lgenpasswd.bv_len == 0) {
4339 			RETVAL_EMPTY_STRING();
4340 		} else {
4341 			RETVAL_STRINGL(lgenpasswd.bv_val, lgenpasswd.bv_len);
4342 		}
4343 	} else if (err == LDAP_SUCCESS) {
4344 		RETVAL_TRUE;
4345 	} else {
4346 		php_error_docref(NULL, E_WARNING, "Passwd modify extended operation failed: %s (%d)", (errmsg ? errmsg : ldap_err2string(err)), err);
4347 		RETVAL_FALSE;
4348 	}
4349 
4350 cleanup:
4351 	if (lgenpasswd.bv_val != NULL) {
4352 		ldap_memfree(lgenpasswd.bv_val);
4353 	}
4354 	if (ldap_res != NULL) {
4355 		ldap_msgfree(ldap_res);
4356 	}
4357 	if (errmsg != NULL) {
4358 		ldap_memfree(errmsg);
4359 	}
4360 }
4361 /* }}} */
4362 #endif
4363 
4364 #ifdef HAVE_LDAP_WHOAMI_S
4365 /* {{{ proto bool|string ldap_exop_whoami(resource link)
4366    Whoami extended operation */
PHP_FUNCTION(ldap_exop_whoami)4367 PHP_FUNCTION(ldap_exop_whoami)
4368 {
4369 	zval *link;
4370 	struct berval *lauthzid;
4371 	ldap_linkdata *ld;
4372 	int rc;
4373 
4374 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &link) == FAILURE) {
4375 		WRONG_PARAM_COUNT;
4376 	}
4377 
4378 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
4379 		RETURN_FALSE;
4380 	}
4381 
4382 	/* synchronous call */
4383 	rc = ldap_whoami_s(ld->link, &lauthzid, NULL, NULL);
4384 	if (rc != LDAP_SUCCESS ) {
4385 		php_error_docref(NULL, E_WARNING, "Whoami extended operation failed: %s (%d)", ldap_err2string(rc), rc);
4386 		RETURN_FALSE;
4387 	}
4388 
4389 	if (lauthzid == NULL) {
4390 		RETVAL_EMPTY_STRING();
4391 	} else {
4392 		RETVAL_STRINGL(lauthzid->bv_val, lauthzid->bv_len);
4393 		ldap_memfree(lauthzid->bv_val);
4394 		ldap_memfree(lauthzid);
4395 	}
4396 }
4397 /* }}} */
4398 #endif
4399 
4400 #ifdef HAVE_LDAP_REFRESH_S
4401 /* {{{ proto bool|int ldap_exop_refresh(resource link , string dn , int ttl)
4402    DDS refresh extended operation */
PHP_FUNCTION(ldap_exop_refresh)4403 PHP_FUNCTION(ldap_exop_refresh)
4404 {
4405 	zval *link, *ttl;
4406 	struct berval ldn;
4407 	ber_int_t lttl;
4408 	ber_int_t newttl;
4409 	ldap_linkdata *ld;
4410 	int rc;
4411 
4412 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsz", &link, &ldn.bv_val, &ldn.bv_len, &ttl) != SUCCESS) {
4413 		WRONG_PARAM_COUNT;
4414 	}
4415 
4416 	if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
4417 		RETURN_FALSE;
4418 	}
4419 
4420 	lttl = (ber_int_t)zval_get_long(ttl);
4421 
4422 	rc = ldap_refresh_s(ld->link, &ldn, lttl, &newttl, NULL, NULL);
4423 	if (rc != LDAP_SUCCESS ) {
4424 		php_error_docref(NULL, E_WARNING, "Refresh extended operation failed: %s (%d)", ldap_err2string(rc), rc);
4425 		RETURN_FALSE;
4426 	}
4427 
4428 	RETURN_LONG(newttl);
4429 }
4430 /* }}} */
4431 #endif
4432 
4433 /* }}} */
4434 
4435 /* {{{ arginfo */
4436 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_connect, 0, 0, 0)
4437 	ZEND_ARG_INFO(0, hostname)
4438 	ZEND_ARG_INFO(0, port)
4439 #ifdef HAVE_ORALDAP
4440 	ZEND_ARG_INFO(0, wallet)
4441 	ZEND_ARG_INFO(0, wallet_passwd)
4442 	ZEND_ARG_INFO(0, authmode)
4443 #endif
4444 ZEND_END_ARG_INFO()
4445 
4446 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_resource, 0, 0, 1)
4447 	ZEND_ARG_INFO(0, link_identifier)
4448 ZEND_END_ARG_INFO()
4449 
4450 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_bind, 0, 0, 1)
4451 	ZEND_ARG_INFO(0, link_identifier)
4452 	ZEND_ARG_INFO(0, bind_rdn)
4453 	ZEND_ARG_INFO(0, bind_password)
4454 ZEND_END_ARG_INFO()
4455 
4456 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_bind_ext, 0, 0, 1)
4457 	ZEND_ARG_INFO(0, link_identifier)
4458 	ZEND_ARG_INFO(0, bind_rdn)
4459 	ZEND_ARG_INFO(0, bind_password)
4460 	ZEND_ARG_INFO(0, servercontrols)
4461 ZEND_END_ARG_INFO()
4462 
4463 #ifdef HAVE_LDAP_SASL
4464 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_sasl_bind, 0, 0, 1)
4465 	ZEND_ARG_INFO(0, link)
4466 	ZEND_ARG_INFO(0, binddn)
4467 	ZEND_ARG_INFO(0, password)
4468 	ZEND_ARG_INFO(0, sasl_mech)
4469 	ZEND_ARG_INFO(0, sasl_realm)
4470 	ZEND_ARG_INFO(0, sasl_authz_id)
4471 	ZEND_ARG_INFO(0, props)
4472 ZEND_END_ARG_INFO()
4473 #endif
4474 
4475 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_read, 0, 0, 3)
4476 	ZEND_ARG_INFO(0, link_identifier)
4477 	ZEND_ARG_INFO(0, base_dn)
4478 	ZEND_ARG_INFO(0, filter)
4479 	ZEND_ARG_INFO(0, attributes)
4480 	ZEND_ARG_INFO(0, attrsonly)
4481 	ZEND_ARG_INFO(0, sizelimit)
4482 	ZEND_ARG_INFO(0, timelimit)
4483 	ZEND_ARG_INFO(0, deref)
4484 	ZEND_ARG_INFO(0, servercontrols)
4485 ZEND_END_ARG_INFO()
4486 
4487 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_list, 0, 0, 3)
4488 	ZEND_ARG_INFO(0, link_identifier)
4489 	ZEND_ARG_INFO(0, base_dn)
4490 	ZEND_ARG_INFO(0, filter)
4491 	ZEND_ARG_INFO(0, attributes)
4492 	ZEND_ARG_INFO(0, attrsonly)
4493 	ZEND_ARG_INFO(0, sizelimit)
4494 	ZEND_ARG_INFO(0, timelimit)
4495 	ZEND_ARG_INFO(0, deref)
4496 	ZEND_ARG_INFO(0, servercontrols)
4497 ZEND_END_ARG_INFO()
4498 
4499 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_search, 0, 0, 3)
4500 	ZEND_ARG_INFO(0, link_identifier)
4501 	ZEND_ARG_INFO(0, base_dn)
4502 	ZEND_ARG_INFO(0, filter)
4503 	ZEND_ARG_INFO(0, attributes)
4504 	ZEND_ARG_INFO(0, attrsonly)
4505 	ZEND_ARG_INFO(0, sizelimit)
4506 	ZEND_ARG_INFO(0, timelimit)
4507 	ZEND_ARG_INFO(0, deref)
4508 	ZEND_ARG_INFO(0, servercontrols)
4509 ZEND_END_ARG_INFO()
4510 
4511 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_count_entries, 0, 0, 2)
4512 	ZEND_ARG_INFO(0, link_identifier)
4513 	ZEND_ARG_INFO(0, result_identifier)
4514 ZEND_END_ARG_INFO()
4515 
4516 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_first_entry, 0, 0, 2)
4517 	ZEND_ARG_INFO(0, link_identifier)
4518 	ZEND_ARG_INFO(0, result_identifier)
4519 ZEND_END_ARG_INFO()
4520 
4521 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_next_entry, 0, 0, 2)
4522 	ZEND_ARG_INFO(0, link_identifier)
4523 	ZEND_ARG_INFO(0, result_identifier)
4524 ZEND_END_ARG_INFO()
4525 
4526 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_get_entries, 0, 0, 2)
4527 	ZEND_ARG_INFO(0, link_identifier)
4528 	ZEND_ARG_INFO(0, result_identifier)
4529 ZEND_END_ARG_INFO()
4530 
4531 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_first_attribute, 0, 0, 2)
4532 	ZEND_ARG_INFO(0, link_identifier)
4533 	ZEND_ARG_INFO(0, result_entry_identifier)
4534 ZEND_END_ARG_INFO()
4535 
4536 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_next_attribute, 0, 0, 2)
4537 	ZEND_ARG_INFO(0, link_identifier)
4538 	ZEND_ARG_INFO(0, result_entry_identifier)
4539 ZEND_END_ARG_INFO()
4540 
4541 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_get_attributes, 0, 0, 2)
4542 	ZEND_ARG_INFO(0, link_identifier)
4543 	ZEND_ARG_INFO(0, result_entry_identifier)
4544 ZEND_END_ARG_INFO()
4545 
4546 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_get_values, 0, 0, 3)
4547 	ZEND_ARG_INFO(0, link_identifier)
4548 	ZEND_ARG_INFO(0, result_entry_identifier)
4549 	ZEND_ARG_INFO(0, attribute)
4550 ZEND_END_ARG_INFO()
4551 
4552 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_get_values_len, 0, 0, 3)
4553 	ZEND_ARG_INFO(0, link_identifier)
4554 	ZEND_ARG_INFO(0, result_entry_identifier)
4555 	ZEND_ARG_INFO(0, attribute)
4556 ZEND_END_ARG_INFO()
4557 
4558 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_get_dn, 0, 0, 2)
4559 	ZEND_ARG_INFO(0, link_identifier)
4560 	ZEND_ARG_INFO(0, result_entry_identifier)
4561 ZEND_END_ARG_INFO()
4562 
4563 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_explode_dn, 0, 0, 2)
4564 	ZEND_ARG_INFO(0, dn)
4565 	ZEND_ARG_INFO(0, with_attrib)
4566 ZEND_END_ARG_INFO()
4567 
4568 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_dn2ufn, 0, 0, 1)
4569 	ZEND_ARG_INFO(0, dn)
4570 ZEND_END_ARG_INFO()
4571 
4572 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_add, 0, 0, 3)
4573 	ZEND_ARG_INFO(0, link_identifier)
4574 	ZEND_ARG_INFO(0, dn)
4575 	ZEND_ARG_INFO(0, entry)
4576 	ZEND_ARG_INFO(0, servercontrols)
4577 ZEND_END_ARG_INFO()
4578 
4579 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_add_ext, 0, 0, 3)
4580 	ZEND_ARG_INFO(0, link_identifier)
4581 	ZEND_ARG_INFO(0, dn)
4582 	ZEND_ARG_INFO(0, entry)
4583 	ZEND_ARG_INFO(0, servercontrols)
4584 ZEND_END_ARG_INFO()
4585 
4586 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_delete, 0, 0, 2)
4587 	ZEND_ARG_INFO(0, link_identifier)
4588 	ZEND_ARG_INFO(0, dn)
4589 	ZEND_ARG_INFO(0, servercontrols)
4590 ZEND_END_ARG_INFO()
4591 
4592 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_delete_ext, 0, 0, 2)
4593 	ZEND_ARG_INFO(0, link_identifier)
4594 	ZEND_ARG_INFO(0, dn)
4595 	ZEND_ARG_INFO(0, servercontrols)
4596 ZEND_END_ARG_INFO()
4597 
4598 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_modify, 0, 0, 3)
4599 	ZEND_ARG_INFO(0, link_identifier)
4600 	ZEND_ARG_INFO(0, dn)
4601 	ZEND_ARG_INFO(0, entry)
4602 	ZEND_ARG_INFO(0, servercontrols)
4603 ZEND_END_ARG_INFO()
4604 
4605 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_modify_batch, 0, 0, 3)
4606 	ZEND_ARG_INFO(0, link_identifier)
4607 	ZEND_ARG_INFO(0, dn)
4608 	ZEND_ARG_ARRAY_INFO(0, modifications_info, 0)
4609 	ZEND_ARG_INFO(0, servercontrols)
4610 ZEND_END_ARG_INFO()
4611 
4612 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_mod_add, 0, 0, 3)
4613 	ZEND_ARG_INFO(0, link_identifier)
4614 	ZEND_ARG_INFO(0, dn)
4615 	ZEND_ARG_INFO(0, entry)
4616 	ZEND_ARG_INFO(0, servercontrols)
4617 ZEND_END_ARG_INFO()
4618 
4619 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_mod_add_ext, 0, 0, 3)
4620 	ZEND_ARG_INFO(0, link_identifier)
4621 	ZEND_ARG_INFO(0, dn)
4622 	ZEND_ARG_INFO(0, entry)
4623 	ZEND_ARG_INFO(0, servercontrols)
4624 ZEND_END_ARG_INFO()
4625 
4626 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_mod_replace, 0, 0, 3)
4627 	ZEND_ARG_INFO(0, link_identifier)
4628 	ZEND_ARG_INFO(0, dn)
4629 	ZEND_ARG_INFO(0, entry)
4630 	ZEND_ARG_INFO(0, servercontrols)
4631 ZEND_END_ARG_INFO()
4632 
4633 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_mod_replace_ext, 0, 0, 3)
4634 	ZEND_ARG_INFO(0, link_identifier)
4635 	ZEND_ARG_INFO(0, dn)
4636 	ZEND_ARG_INFO(0, entry)
4637 	ZEND_ARG_INFO(0, servercontrols)
4638 ZEND_END_ARG_INFO()
4639 
4640 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_mod_del, 0, 0, 3)
4641 	ZEND_ARG_INFO(0, link_identifier)
4642 	ZEND_ARG_INFO(0, dn)
4643 	ZEND_ARG_INFO(0, entry)
4644 	ZEND_ARG_INFO(0, servercontrols)
4645 ZEND_END_ARG_INFO()
4646 
4647 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_mod_del_ext, 0, 0, 3)
4648 	ZEND_ARG_INFO(0, link_identifier)
4649 	ZEND_ARG_INFO(0, dn)
4650 	ZEND_ARG_INFO(0, entry)
4651 	ZEND_ARG_INFO(0, servercontrols)
4652 ZEND_END_ARG_INFO()
4653 
4654 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_err2str, 0, 0, 1)
4655 	ZEND_ARG_INFO(0, errno)
4656 ZEND_END_ARG_INFO()
4657 
4658 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_compare, 0, 0, 4)
4659 	ZEND_ARG_INFO(0, link_identifier)
4660 	ZEND_ARG_INFO(0, dn)
4661 	ZEND_ARG_INFO(0, attribute)
4662 	ZEND_ARG_INFO(0, value)
4663 	ZEND_ARG_INFO(0, servercontrols)
4664 ZEND_END_ARG_INFO()
4665 
4666 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_sort, 0, 0, 3)
4667 	ZEND_ARG_INFO(0, link)
4668 	ZEND_ARG_INFO(0, result)
4669 	ZEND_ARG_INFO(0, sortfilter)
4670 ZEND_END_ARG_INFO()
4671 
4672 #ifdef LDAP_CONTROL_PAGEDRESULTS
4673 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_control_paged_result, 0, 0, 2)
4674 	ZEND_ARG_INFO(0, link)
4675 	ZEND_ARG_INFO(0, pagesize)
4676 	ZEND_ARG_INFO(0, iscritical)
4677 	ZEND_ARG_INFO(0, cookie)
4678 ZEND_END_ARG_INFO();
4679 
4680 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_control_paged_result_response, 0, 0, 2)
4681 	ZEND_ARG_INFO(0, link)
4682 	ZEND_ARG_INFO(0, result)
4683 	ZEND_ARG_INFO(1, cookie)
4684 	ZEND_ARG_INFO(1, estimated)
4685 ZEND_END_ARG_INFO();
4686 #endif
4687 
4688 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP
4689 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_rename, 0, 0, 5)
4690 	ZEND_ARG_INFO(0, link_identifier)
4691 	ZEND_ARG_INFO(0, dn)
4692 	ZEND_ARG_INFO(0, newrdn)
4693 	ZEND_ARG_INFO(0, newparent)
4694 	ZEND_ARG_INFO(0, deleteoldrdn)
4695 	ZEND_ARG_INFO(0, servercontrols)
4696 ZEND_END_ARG_INFO()
4697 
4698 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_rename_ext, 0, 0, 5)
4699 	ZEND_ARG_INFO(0, link_identifier)
4700 	ZEND_ARG_INFO(0, dn)
4701 	ZEND_ARG_INFO(0, newrdn)
4702 	ZEND_ARG_INFO(0, newparent)
4703 	ZEND_ARG_INFO(0, deleteoldrdn)
4704 	ZEND_ARG_INFO(0, servercontrols)
4705 ZEND_END_ARG_INFO()
4706 
4707 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_get_option, 0, 0, 3)
4708 	ZEND_ARG_INFO(0, link_identifier)
4709 	ZEND_ARG_INFO(0, option)
4710 	ZEND_ARG_INFO(1, retval)
4711 ZEND_END_ARG_INFO()
4712 
4713 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_set_option, 0, 0, 3)
4714 	ZEND_ARG_INFO(0, link_identifier)
4715 	ZEND_ARG_INFO(0, option)
4716 	ZEND_ARG_INFO(0, newval)
4717 ZEND_END_ARG_INFO()
4718 
4719 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_first_reference, 0, 0, 2)
4720 	ZEND_ARG_INFO(0, link)
4721 	ZEND_ARG_INFO(0, result)
4722 ZEND_END_ARG_INFO()
4723 
4724 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_next_reference, 0, 0, 2)
4725 	ZEND_ARG_INFO(0, link)
4726 	ZEND_ARG_INFO(0, entry)
4727 ZEND_END_ARG_INFO()
4728 
4729 #ifdef HAVE_LDAP_PARSE_REFERENCE
4730 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_parse_reference, 0, 0, 3)
4731 	ZEND_ARG_INFO(0, link)
4732 	ZEND_ARG_INFO(0, entry)
4733 	ZEND_ARG_INFO(1, referrals)
4734 ZEND_END_ARG_INFO()
4735 #endif
4736 
4737 
4738 #ifdef HAVE_LDAP_PARSE_RESULT
4739 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_parse_result, 0, 0, 3)
4740 	ZEND_ARG_INFO(0, link)
4741 	ZEND_ARG_INFO(0, result)
4742 	ZEND_ARG_INFO(1, errcode)
4743 	ZEND_ARG_INFO(1, matcheddn)
4744 	ZEND_ARG_INFO(1, errmsg)
4745 	ZEND_ARG_INFO(1, referrals)
4746 	ZEND_ARG_INFO(1, serverctrls)
4747 ZEND_END_ARG_INFO()
4748 #endif
4749 #endif
4750 
4751 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC)
4752 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_set_rebind_proc, 0, 0, 2)
4753 	ZEND_ARG_INFO(0, link)
4754 	ZEND_ARG_INFO(0, callback)
4755 ZEND_END_ARG_INFO()
4756 #endif
4757 
4758 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_escape, 0, 0, 1)
4759 	ZEND_ARG_INFO(0, value)
4760 	ZEND_ARG_INFO(0, ignore)
4761 	ZEND_ARG_INFO(0, flags)
4762 ZEND_END_ARG_INFO()
4763 
4764 #ifdef STR_TRANSLATION
4765 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_t61_to_8859, 0, 0, 1)
4766 	ZEND_ARG_INFO(0, value)
4767 ZEND_END_ARG_INFO()
4768 
4769 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_8859_to_t61, 0, 0, 1)
4770 	ZEND_ARG_INFO(0, value)
4771 ZEND_END_ARG_INFO()
4772 #endif
4773 
4774 #ifdef HAVE_LDAP_EXTENDED_OPERATION_S
4775 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_exop, 0, 0, 2)
4776 	ZEND_ARG_INFO(0, link)
4777 	ZEND_ARG_INFO(0, reqoid)
4778 	ZEND_ARG_INFO(0, reqdata)
4779 	ZEND_ARG_INFO(0, servercontrols)
4780 	ZEND_ARG_INFO(1, retdata)
4781 	ZEND_ARG_INFO(1, retoid)
4782 ZEND_END_ARG_INFO()
4783 #endif
4784 
4785 #ifdef HAVE_LDAP_PASSWD
4786 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_exop_passwd, 0, 0, 1)
4787 	ZEND_ARG_INFO(0, link)
4788 	ZEND_ARG_INFO(0, user)
4789 	ZEND_ARG_INFO(0, oldpw)
4790 	ZEND_ARG_INFO(0, newpw)
4791 	ZEND_ARG_INFO(1, serverctrls)
4792 ZEND_END_ARG_INFO()
4793 #endif
4794 
4795 #ifdef HAVE_LDAP_WHOAMI_S
4796 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_exop_whoami, 0, 0, 1)
4797 	ZEND_ARG_INFO(0, link)
4798 ZEND_END_ARG_INFO()
4799 #endif
4800 
4801 #ifdef HAVE_LDAP_REFRESH_S
4802 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_exop_refresh, 0, 0, 3)
4803 	ZEND_ARG_INFO(0, link)
4804 	ZEND_ARG_INFO(0, dn)
4805 	ZEND_ARG_INFO(0, ttl)
4806 ZEND_END_ARG_INFO()
4807 #endif
4808 
4809 #ifdef HAVE_LDAP_PARSE_EXTENDED_RESULT
4810 ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_parse_exop, 0, 0, 4)
4811 	ZEND_ARG_INFO(0, link)
4812 	ZEND_ARG_INFO(0, result)
4813 	ZEND_ARG_INFO(1, retdata)
4814 	ZEND_ARG_INFO(1, retoid)
4815 ZEND_END_ARG_INFO()
4816 #endif
4817 /* }}} */
4818 
4819 /*
4820 	This is just a small subset of the functionality provided by the LDAP library. All the
4821 	operations are synchronous. Referrals are not handled automatically.
4822 */
4823 /* {{{ ldap_functions[]
4824  */
4825 static const zend_function_entry ldap_functions[] = {
4826 	PHP_FE(ldap_connect,								arginfo_ldap_connect)
4827 	PHP_FALIAS(ldap_close,		ldap_unbind,			arginfo_ldap_resource)
4828 	PHP_FE(ldap_bind,									arginfo_ldap_bind)
4829 	PHP_FE(ldap_bind_ext,								arginfo_ldap_bind_ext)
4830 #ifdef HAVE_LDAP_SASL
4831 	PHP_FE(ldap_sasl_bind,								arginfo_ldap_sasl_bind)
4832 #endif
4833 	PHP_FE(ldap_unbind,									arginfo_ldap_resource)
4834 	PHP_FE(ldap_read,									arginfo_ldap_read)
4835 	PHP_FE(ldap_list,									arginfo_ldap_list)
4836 	PHP_FE(ldap_search,									arginfo_ldap_search)
4837 	PHP_FE(ldap_free_result,							arginfo_ldap_resource)
4838 	PHP_FE(ldap_count_entries,							arginfo_ldap_count_entries)
4839 	PHP_FE(ldap_first_entry,							arginfo_ldap_first_entry)
4840 	PHP_FE(ldap_next_entry,								arginfo_ldap_next_entry)
4841 	PHP_FE(ldap_get_entries,							arginfo_ldap_get_entries)
4842 	PHP_FE(ldap_first_attribute,						arginfo_ldap_first_attribute)
4843 	PHP_FE(ldap_next_attribute,							arginfo_ldap_next_attribute)
4844 	PHP_FE(ldap_get_attributes,							arginfo_ldap_get_attributes)
4845 	PHP_FALIAS(ldap_get_values,	ldap_get_values_len,	arginfo_ldap_get_values)
4846 	PHP_FE(ldap_get_values_len,							arginfo_ldap_get_values_len)
4847 	PHP_FE(ldap_get_dn,									arginfo_ldap_get_dn)
4848 	PHP_FE(ldap_explode_dn,								arginfo_ldap_explode_dn)
4849 	PHP_FE(ldap_dn2ufn,									arginfo_ldap_dn2ufn)
4850 	PHP_FE(ldap_add,									arginfo_ldap_add)
4851 	PHP_FE(ldap_add_ext,								arginfo_ldap_add_ext)
4852 	PHP_FE(ldap_delete,									arginfo_ldap_delete)
4853 	PHP_FE(ldap_delete_ext,								arginfo_ldap_delete_ext)
4854 	PHP_FE(ldap_modify_batch,							arginfo_ldap_modify_batch)
4855 	PHP_FALIAS(ldap_modify,		ldap_mod_replace,		arginfo_ldap_modify)
4856 
4857 /* additional functions for attribute based modifications, Gerrit Thomson */
4858 	PHP_FE(ldap_mod_add,								arginfo_ldap_mod_add)
4859 	PHP_FE(ldap_mod_add_ext,							arginfo_ldap_mod_add_ext)
4860 	PHP_FE(ldap_mod_replace,							arginfo_ldap_mod_replace)
4861 	PHP_FE(ldap_mod_replace_ext,						arginfo_ldap_mod_replace_ext)
4862 	PHP_FE(ldap_mod_del,								arginfo_ldap_mod_del)
4863 	PHP_FE(ldap_mod_del_ext,							arginfo_ldap_mod_del_ext)
4864 /* end gjt mod */
4865 
4866 	PHP_FE(ldap_errno,									arginfo_ldap_resource)
4867 	PHP_FE(ldap_err2str,								arginfo_ldap_err2str)
4868 	PHP_FE(ldap_error,									arginfo_ldap_resource)
4869 	PHP_FE(ldap_compare,								arginfo_ldap_compare)
4870 	PHP_DEP_FE(ldap_sort,									arginfo_ldap_sort)
4871 
4872 #if (LDAP_API_VERSION > 2000) || HAVE_ORALDAP
4873 	PHP_FE(ldap_rename,									arginfo_ldap_rename)
4874 	PHP_FE(ldap_rename_ext,								arginfo_ldap_rename_ext)
4875 	PHP_FE(ldap_get_option,								arginfo_ldap_get_option)
4876 	PHP_FE(ldap_set_option,								arginfo_ldap_set_option)
4877 	PHP_FE(ldap_first_reference,						arginfo_ldap_first_reference)
4878 	PHP_FE(ldap_next_reference,							arginfo_ldap_next_reference)
4879 #ifdef HAVE_LDAP_PARSE_REFERENCE
4880 	PHP_FE(ldap_parse_reference,						arginfo_ldap_parse_reference)
4881 #endif
4882 #ifdef HAVE_LDAP_PARSE_RESULT
4883 	PHP_FE(ldap_parse_result,							arginfo_ldap_parse_result)
4884 #endif
4885 #ifdef HAVE_LDAP_START_TLS_S
4886 	PHP_FE(ldap_start_tls,								arginfo_ldap_resource)
4887 #endif
4888 #ifdef HAVE_LDAP_EXTENDED_OPERATION_S
4889 	PHP_FE(ldap_exop,									arginfo_ldap_exop)
4890 #endif
4891 #ifdef HAVE_LDAP_PASSWD
4892 	PHP_FE(ldap_exop_passwd,							arginfo_ldap_exop_passwd)
4893 #endif
4894 #ifdef HAVE_LDAP_WHOAMI_S
4895 	PHP_FE(ldap_exop_whoami,							arginfo_ldap_exop_whoami)
4896 #endif
4897 #ifdef HAVE_LDAP_REFRESH_S
4898 	PHP_FE(ldap_exop_refresh,							arginfo_ldap_exop_refresh)
4899 #endif
4900 #ifdef HAVE_LDAP_PARSE_EXTENDED_RESULT
4901 	PHP_FE(ldap_parse_exop,								arginfo_ldap_parse_exop)
4902 #endif
4903 #endif
4904 
4905 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC)
4906 	PHP_FE(ldap_set_rebind_proc,						arginfo_ldap_set_rebind_proc)
4907 #endif
4908 
4909 	PHP_FE(ldap_escape,									arginfo_ldap_escape)
4910 
4911 #ifdef STR_TRANSLATION
4912 	PHP_FE(ldap_t61_to_8859,							arginfo_ldap_t61_to_8859)
4913 	PHP_FE(ldap_8859_to_t61,							arginfo_ldap_8859_to_t61)
4914 #endif
4915 
4916 #ifdef LDAP_CONTROL_PAGEDRESULTS
4917 	PHP_DEP_FE(ldap_control_paged_result,				arginfo_ldap_control_paged_result)
4918 	PHP_DEP_FE(ldap_control_paged_result_response,		arginfo_ldap_control_paged_result_response)
4919 #endif
4920 	PHP_FE_END
4921 };
4922 /* }}} */
4923 
4924 zend_module_entry ldap_module_entry = { /* {{{ */
4925 	STANDARD_MODULE_HEADER,
4926 	"ldap",
4927 	ldap_functions,
4928 	PHP_MINIT(ldap),
4929 	PHP_MSHUTDOWN(ldap),
4930 	NULL,
4931 	NULL,
4932 	PHP_MINFO(ldap),
4933 	PHP_LDAP_VERSION,
4934 	PHP_MODULE_GLOBALS(ldap),
4935 	PHP_GINIT(ldap),
4936 	NULL,
4937 	NULL,
4938 	STANDARD_MODULE_PROPERTIES_EX
4939 };
4940 /* }}} */
4941