1 /*
2  *  This program is free software; you can redistribute it and/or modify
3  *  it under the terms of the GNU General Public License as published by
4  *  the Free Software Foundation; either version 2 of the License, or
5  *  (at your option) any later version.
6  *
7  *  This program is distributed in the hope that it will be useful,
8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *  GNU Library General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License
13  *  along with this program; if not, write to the Free Software
14  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15  */
16 
17 #include "linphone/ldapprovider.h"
18 #include "private.h"
19 #include "linphone/lpconfig.h"
20 #include "contact_providers_priv.h"
21 #include "mediastreamer2/mscommon.h"
22 #include <belle-sip/dict.h>
23 
24 #ifdef BUILD_LDAP
25 #include <ldap.h>
26 #include <sasl/sasl.h>
27 
28 
29 #define MAX_RUNNING_REQUESTS 10
30 #define FILTER_MAX_SIZE      512
31 
32 struct LDAPFriendData {
33 	char* name;
34 	char* sip;
35 };
36 
37 struct _LinphoneLDAPContactProvider
38 {
39 	LinphoneContactProvider base;
40 	LinphoneDictionary* config;
41 
42 	LDAP*   ld;
43 	bctbx_list_t* requests;
44 	unsigned int    req_count;
45 
46 	// bind transaction
47 	bool_t connected;
48 	ms_thread_t bind_thread;
49 
50 	// config
51 	int use_tls;
52 	const char*  auth_method;
53 	const char*  username;
54 	const char*  password;
55 	const char*  server;
56 	const char*  bind_dn;
57 
58 	const char*  sasl_authname;
59 	const char*  sasl_realm;
60 
61 	const char*  base_object;
62 	const char*  sip_attr;
63 	const char*  name_attr;
64 	const char*  filter;
65 
66 	char**       attributes;
67 
68 	int    timeout;
69 	int    deref_aliases;
70 	int    max_results;
71 
72 };
73 
74 struct _LinphoneLDAPContactSearch
75 {
76 	LinphoneContactSearch base;
77 	LDAP*   ld;
78 	int     msgid;
79 	char*   filter;
80 	bool_t  complete;
81 	bctbx_list_t* found_entries;
82 	unsigned int found_count;
83 };
84 
85 
86 /* *************************
87  * LinphoneLDAPContactSearch
88  * *************************/
89 
linphone_ldap_contact_search_create(LinphoneLDAPContactProvider * cp,const char * predicate,ContactSearchCallback cb,void * cb_data)90 LinphoneLDAPContactSearch* linphone_ldap_contact_search_create(LinphoneLDAPContactProvider* cp, const char* predicate, ContactSearchCallback cb, void* cb_data)
91 {
92 	LinphoneLDAPContactSearch* search = belle_sip_object_new(LinphoneLDAPContactSearch);
93 	LinphoneContactSearch* base = LINPHONE_CONTACT_SEARCH(search);
94 
95 	linphone_contact_search_init(base, predicate, cb, cb_data);
96 
97 	search->ld = cp->ld;
98 
99 	search->filter = ms_malloc(FILTER_MAX_SIZE);
100 	snprintf(search->filter, FILTER_MAX_SIZE-1, cp->filter, predicate);
101 	search->filter[FILTER_MAX_SIZE-1] = 0;
102 
103 	return search;
104 }
105 
linphone_ldap_contact_search_destroy_friend(void * entry)106 void linphone_ldap_contact_search_destroy_friend( void* entry )
107 {
108 	linphone_friend_destroy((LinphoneFriend*)entry);
109 }
110 
linphone_ldap_contact_search_result_count(LinphoneLDAPContactSearch * obj)111 unsigned int linphone_ldap_contact_search_result_count(LinphoneLDAPContactSearch* obj)
112 {
113 	return obj->found_count;
114 }
115 
linphone_ldap_contact_search_destroy(LinphoneLDAPContactSearch * obj)116 static void linphone_ldap_contact_search_destroy( LinphoneLDAPContactSearch* obj )
117 {
118 	//ms_message("~LinphoneLDAPContactSearch(%p)", obj);
119 	bctbx_list_for_each(obj->found_entries, linphone_ldap_contact_search_destroy_friend);
120 	obj->found_entries = bctbx_list_free(obj->found_entries);
121 	if( obj->filter ) ms_free(obj->filter);
122 }
123 
124 BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneLDAPContactSearch);
125 BELLE_SIP_INSTANCIATE_VPTR(LinphoneLDAPContactSearch,LinphoneContactSearch,
126 						   (belle_sip_object_destroy_t)linphone_ldap_contact_search_destroy,
127 						   NULL,
128 						   NULL,
129 						   TRUE
130 );
131 
132 
133 /* ***************************
134  * LinphoneLDAPContactProvider
135  * ***************************/
136 
137 static inline LinphoneLDAPContactSearch* linphone_ldap_contact_provider_request_search( LinphoneLDAPContactProvider* obj, int msgid );
138 static unsigned int linphone_ldap_contact_provider_cancel_search(LinphoneContactProvider* obj, LinphoneContactSearch *req);
139 static void linphone_ldap_contact_provider_conf_destroy(LinphoneLDAPContactProvider* obj );
140 static bool_t linphone_ldap_contact_provider_iterate(void *data);
141 static int linphone_ldap_contact_provider_bind_interact(LDAP *ld, unsigned flags, void *defaults, void *sasl_interact);
142 static int linphone_ldap_contact_provider_perform_search( LinphoneLDAPContactProvider* obj, LinphoneLDAPContactSearch* req);
143 
linphone_ldap_contact_provider_destroy_request_cb(void * req)144 static void linphone_ldap_contact_provider_destroy_request_cb(void *req)
145 {
146 	belle_sip_object_unref(req);
147 }
148 
linphone_ldap_contact_provider_destroy(LinphoneLDAPContactProvider * obj)149 static void linphone_ldap_contact_provider_destroy( LinphoneLDAPContactProvider* obj )
150 {
151 	//ms_message("linphone_ldap_contact_provider_destroy");
152 	linphone_core_remove_iterate_hook(LINPHONE_CONTACT_PROVIDER(obj)->lc, linphone_ldap_contact_provider_iterate,obj);
153 
154 	// clean pending requests
155 	bctbx_list_for_each(obj->requests, linphone_ldap_contact_provider_destroy_request_cb);
156 
157 	if (obj->ld) ldap_unbind_ext(obj->ld, NULL, NULL);
158 	obj->ld = NULL;
159 
160 	if( obj->config ) linphone_dictionary_unref(obj->config);
161 
162 	linphone_ldap_contact_provider_conf_destroy(obj);
163 }
164 
linphone_ldap_contact_provider_complete_contact(LinphoneLDAPContactProvider * obj,struct LDAPFriendData * lf,const char * attr_name,const char * attr_value)165 static int linphone_ldap_contact_provider_complete_contact( LinphoneLDAPContactProvider* obj, struct LDAPFriendData* lf, const char* attr_name, const char* attr_value)
166 {
167 	if( strcmp(attr_name, obj->name_attr ) == 0 ){
168 		lf->name = ms_strdup(attr_value);
169 	} else if( strcmp(attr_name, obj->sip_attr) == 0 ) {
170 		lf->sip = ms_strdup(attr_value);
171 	}
172 
173 	// return 1 if the structure has enough data to create a linphone friend
174 	if( lf->name && lf->sip )
175 		return 1;
176 	else
177 		return 0;
178 
179 }
180 
linphone_ldap_contact_provider_handle_search_result(LinphoneLDAPContactProvider * obj,LinphoneLDAPContactSearch * req,LDAPMessage * message)181 static void linphone_ldap_contact_provider_handle_search_result( LinphoneLDAPContactProvider* obj, LinphoneLDAPContactSearch* req, LDAPMessage* message )
182 {
183 	int msgtype = ldap_msgtype(message);
184 
185 	switch(msgtype){
186 
187 	case LDAP_RES_SEARCH_ENTRY:
188 	case LDAP_RES_EXTENDED:
189 	{
190 		LDAPMessage *entry = ldap_first_entry(obj->ld, message);
191 		LinphoneCore*   lc = LINPHONE_CONTACT_PROVIDER(obj)->lc;
192 
193 		while( entry != NULL ){
194 
195 			struct LDAPFriendData ldap_data = {0};
196 			bool_t contact_complete = FALSE;
197 			BerElement*  ber = NULL;
198 			char*       attr = ldap_first_attribute(obj->ld, entry, &ber);
199 
200 			while( attr ){
201 				struct berval** values = ldap_get_values_len(obj->ld, entry, attr);
202 				struct berval**     it = values;
203 
204 				while( values && *it && (*it)->bv_val && (*it)->bv_len )
205 				{
206 					contact_complete = linphone_ldap_contact_provider_complete_contact(obj, &ldap_data, attr, (*it)->bv_val);
207 					if( contact_complete ) break;
208 
209 					it++;
210 				}
211 
212 				if( values ) ldap_value_free_len(values);
213 				ldap_memfree(attr);
214 
215 				if( contact_complete ) break;
216 
217 				attr = ldap_next_attribute(obj->ld, entry, ber);
218 			}
219 
220 			if( contact_complete ) {
221 				LinphoneAddress* la = linphone_core_interpret_url(lc, ldap_data.sip);
222 				if( la ){
223 					LinphoneFriend* lf = linphone_core_create_friend(lc);
224 					linphone_friend_set_address(lf, la);
225 					linphone_friend_set_name(lf, ldap_data.name);
226 					req->found_entries = bctbx_list_append(req->found_entries, lf);
227 					req->found_count++;
228 					//ms_message("Added friend %s / %s", ldap_data.name, ldap_data.sip);
229 					ms_free(ldap_data.sip);
230 					ms_free(ldap_data.name);
231 					linphone_address_unref(la);
232 				}
233 			}
234 
235 			if( ber ) ber_free(ber, 0);
236 
237 			entry = ldap_next_entry(obj->ld, entry);
238 		}
239 	}
240 	break;
241 
242 	case LDAP_RES_SEARCH_RESULT:
243 	{
244 		// this one is received when a request is finished
245 		req->complete = TRUE;
246 		linphone_contact_search_invoke_cb(LINPHONE_CONTACT_SEARCH(req), req->found_entries);
247 	}
248 	break;
249 
250 
251 	default: ms_message("[LDAP] Unhandled message type %x", msgtype); break;
252 	}
253 }
254 
linphone_ldap_contact_provider_iterate(void * data)255 static bool_t linphone_ldap_contact_provider_iterate(void *data)
256 {
257 	LinphoneLDAPContactProvider* obj = LINPHONE_LDAP_CONTACT_PROVIDER(data);
258 	if( obj->ld && obj->connected && (obj->req_count > 0) ){
259 
260 		// never block
261 		struct timeval timeout = {0,0};
262 		LDAPMessage* results = NULL;
263 
264 		int ret = ldap_result(obj->ld, LDAP_RES_ANY, LDAP_MSG_ONE, &timeout, &results);
265 
266 		switch( ret ){
267 		case -1:
268 		{
269 			ms_warning("Error in ldap_result : returned -1 (req_count %d): %s", obj->req_count, ldap_err2string(errno));
270 			break;
271 		}
272 		case 0: break; // nothing to do
273 
274 		case LDAP_RES_BIND:
275 		{
276 			ms_error("iterate: unexpected LDAP_RES_BIND");
277 			break;
278 		}
279 		case LDAP_RES_EXTENDED:
280 		case LDAP_RES_SEARCH_ENTRY:
281 		case LDAP_RES_SEARCH_REFERENCE:
282 		case LDAP_RES_INTERMEDIATE:
283 		case LDAP_RES_SEARCH_RESULT:
284 		{
285 			LDAPMessage* message = ldap_first_message(obj->ld, results);
286 			LinphoneLDAPContactSearch* req = linphone_ldap_contact_provider_request_search(obj, ldap_msgid(message));
287 			while( message != NULL ){
288 				linphone_ldap_contact_provider_handle_search_result(obj, req, message );
289 				message = ldap_next_message(obj->ld, message);
290 			}
291 			if( req && ret == LDAP_RES_SEARCH_RESULT)
292 				linphone_ldap_contact_provider_cancel_search(
293 							LINPHONE_CONTACT_PROVIDER(obj),
294 							LINPHONE_CONTACT_SEARCH(req));
295 			break;
296 		}
297 		case LDAP_RES_MODIFY:
298 		case LDAP_RES_ADD:
299 		case LDAP_RES_DELETE:
300 		case LDAP_RES_MODDN:
301 		case LDAP_RES_COMPARE:
302 		default:
303 			ms_message("Unhandled LDAP result %x", ret);
304 			break;
305 		}
306 
307 		if( results )
308 			ldap_msgfree(results);
309 	}
310 
311 	if( obj->ld && obj->connected ){
312 		// check for pending searches
313 		unsigned int i;
314 
315 		for( i=0; i<obj->req_count; i++){
316 			LinphoneLDAPContactSearch* search = (LinphoneLDAPContactSearch*)bctbx_list_nth_data( obj->requests, i );
317 			if( search && search->msgid == 0){
318 				int ret;
319 				ms_message("Found pending search %p (for %s), launching...", search, search->filter);
320 				ret = linphone_ldap_contact_provider_perform_search(obj, search);
321 				if( ret != LDAP_SUCCESS ){
322 					linphone_ldap_contact_provider_cancel_search(
323 								LINPHONE_CONTACT_PROVIDER(obj),
324 								LINPHONE_CONTACT_SEARCH(search));
325 				}
326 			}
327 		}
328 	}
329 
330 	return TRUE;
331 }
332 
linphone_ldap_contact_provider_conf_destroy(LinphoneLDAPContactProvider * obj)333 static void linphone_ldap_contact_provider_conf_destroy(LinphoneLDAPContactProvider* obj )
334 {
335 	if(obj->attributes){
336 		int i=0;
337 		for( ; obj->attributes[i]; i++){
338 			ms_free(obj->attributes[i]);
339 		}
340 		ms_free(obj->attributes);
341 	}
342 }
343 
344 static char* required_config_keys[] = {
345 	// connection
346 	"server",
347 	"use_tls",
348 	"auth_method",
349 	"username",
350 	"password",
351 	"bind_dn",
352 	"sasl_authname",
353 	"sasl_realm",
354 
355 	// search
356 	"base_object",
357 	"filter",
358 	"name_attribute",
359 	"sip_attribute",
360 	"attributes",
361 
362 	// misc
363 	"timeout",
364 	"max_results",
365 	"deref_aliases",
366 	NULL
367 };
368 
linphone_ldap_contact_provider_valid_config(const LinphoneDictionary * dict)369 static bool_t linphone_ldap_contact_provider_valid_config(const LinphoneDictionary* dict)
370 {
371 	char** config_name = required_config_keys;
372 
373 	bool_t valid = TRUE;
374 	bool_t has_key;
375 
376 	while(*config_name ){
377 		has_key = linphone_dictionary_haskey(dict, *config_name);
378 		if( !has_key ) ms_error("Missing LDAP config value for '%s'", *config_name);
379 		valid &= has_key;
380 		config_name++;
381 	}
382 	return valid;
383 }
384 
linphone_ldap_contact_provider_loadconfig(LinphoneLDAPContactProvider * obj,const LinphoneDictionary * dict)385 static void linphone_ldap_contact_provider_loadconfig(LinphoneLDAPContactProvider* obj, const LinphoneDictionary* dict)
386 {
387 	char* attributes_list, *saveptr, *attr;
388 	unsigned int attr_count = 0, attr_idx = 0, i;
389 
390 	if( !linphone_ldap_contact_provider_valid_config(dict) ) return;
391 
392 	// free any pre-existing attributes values
393 	linphone_ldap_contact_provider_conf_destroy(obj);
394 	if( obj->config ) linphone_dictionary_unref(obj->config);
395 
396 	// clone new config into the dictionary
397 	obj->config = linphone_dictionary_ref(linphone_dictionary_clone(dict));
398 
399 #if 0 // until sasl auth is set up, force anonymous auth.
400 	linphone_dictionary_set_string(obj->config, "auth_method", "ANONYMOUS");
401 #endif
402 
403 	obj->use_tls       = linphone_dictionary_get_int(obj->config, "use_tls",       0);
404 	obj->timeout       = linphone_dictionary_get_int(obj->config, "timeout",       10);
405 	obj->deref_aliases = linphone_dictionary_get_int(obj->config, "deref_aliases", 0);
406 	obj->max_results   = linphone_dictionary_get_int(obj->config, "max_results",   50);
407 	obj->auth_method   = linphone_dictionary_get_string(obj->config, "auth_method",    "ANONYMOUS");
408 	obj->username      = linphone_dictionary_get_string(obj->config, "username",       "");
409 	obj->password      = linphone_dictionary_get_string(obj->config, "password",       "");
410 	obj->bind_dn       = linphone_dictionary_get_string(obj->config, "bind_dn",        "");
411 	obj->base_object   = linphone_dictionary_get_string(obj->config, "base_object",    "dc=example,dc=com");
412 	obj->server        = linphone_dictionary_get_string(obj->config, "server",         "ldap://localhost");
413 	obj->filter        = linphone_dictionary_get_string(obj->config, "filter",         "uid=*%s*");
414 	obj->name_attr     = linphone_dictionary_get_string(obj->config, "name_attribute", "givenName");
415 	obj->sip_attr      = linphone_dictionary_get_string(obj->config, "sip_attribute",  "mobile");
416 	obj->sasl_authname = linphone_dictionary_get_string(obj->config, "sasl_authname",  "");
417 	obj->sasl_realm    = linphone_dictionary_get_string(obj->config, "sasl_realm",  "");
418 
419 	/*
420 	 * parse the attributes list
421 	 */
422 	attributes_list = ms_strdup(
423 				linphone_dictionary_get_string(obj->config,
424 											   "attributes",
425 											   "telephoneNumber,givenName,sn,mobile,homePhone")
426 				);
427 
428 	// count attributes:
429 	for( i=0; attributes_list[i]; i++) {
430 		if( attributes_list[i] == ',') attr_count++;
431 	}
432 
433 	// 1 more for the first attr without ',', the other for the null-finished list
434 	obj->attributes = ms_malloc0((attr_count+2) * sizeof(char*));
435 
436 	attr = strtok_r( attributes_list, ",", &saveptr );
437 	while( attr != NULL ){
438 		obj->attributes[attr_idx] = ms_strdup(attr);
439 		attr_idx++;
440 		attr = strtok_r(NULL, ",", &saveptr);
441 	}
442 	if( attr_idx != attr_count+1) ms_error("Invalid attribute number!!! %d expected, got %d", attr_count+1, attr_idx);
443 
444 	ms_free(attributes_list);
445 }
446 
linphone_ldap_contact_provider_bind_interact(LDAP * ld,unsigned flags,void * defaults,void * sasl_interact)447 static int linphone_ldap_contact_provider_bind_interact(LDAP *ld,
448 														unsigned flags,
449 														void *defaults,
450 														void *sasl_interact)
451 {
452 	sasl_interact_t *interact = (sasl_interact_t*)sasl_interact;
453 	LinphoneLDAPContactProvider* obj = LINPHONE_LDAP_CONTACT_PROVIDER(defaults);
454 	ms_message("bind_interact called: ld %p, flags %x, default %p, interact %p",
455 			   ld, flags, defaults, sasl_interact);
456 
457 	if( ld == NULL ) return LDAP_PARAM_ERROR;
458 
459 	while( interact->id != SASL_CB_LIST_END ) {
460 
461 		const char *dflt = interact->defresult;
462 
463 		switch( interact->id ) {
464 		case SASL_CB_GETREALM:
465 			ms_message("* SASL_CB_GETREALM -> %s", obj->sasl_realm);
466 			dflt = obj->sasl_realm;
467 		break;
468 		case SASL_CB_AUTHNAME:
469 			ms_message("* SASL_CB_AUTHNAME -> %s", obj->sasl_authname);
470 			dflt = obj->sasl_authname;
471 		break;
472 		case SASL_CB_USER:
473 			ms_message("* SASL_CB_USER -> %s", obj->username);
474 			dflt = obj->username;
475 		break;
476 		case SASL_CB_PASS:
477 			ms_message("* SASL_CB_PASS (hidden)");
478 			dflt = obj->password;
479 		break;
480 		default:
481 			ms_message("SASL interact asked for unknown id %lx\n",interact->id);
482 		}
483 		interact->result = (dflt && *dflt) ? dflt : (const char*)"";
484 		interact->len = strlen( (const char*)interact->result );
485 
486 		interact++;
487 	}
488 	return LDAP_SUCCESS;
489 }
490 
ldap_bind_thread_func(void * arg)491 static void* ldap_bind_thread_func( void*arg)
492 {
493 	LinphoneLDAPContactProvider* obj = linphone_ldap_contact_provider_ref(arg);
494 	const char* auth_mechanism = obj->auth_method;
495 	int ret;
496 
497 	if( (strcmp(auth_mechanism, "ANONYMOUS") == 0) || (strcmp(auth_mechanism, "SIMPLE") == 0) )
498 	{
499 		struct berval passwd = { strlen(obj->password), ms_strdup(obj->password)};
500 		auth_mechanism = LDAP_SASL_SIMPLE;
501 		ret = ldap_sasl_bind_s(obj->ld,
502 							   obj->bind_dn,
503 							   auth_mechanism,
504 							   &passwd,
505 							   NULL,
506 							   NULL,
507 							   NULL);
508 
509 		ms_free(passwd.bv_val);
510 	}
511 	else
512 	{
513 
514 		ms_message("LDAP interactive bind");
515 		ret = ldap_sasl_interactive_bind_s(obj->ld,
516 										   obj->bind_dn,
517 										   auth_mechanism,
518 										   NULL,NULL,
519 										   LDAP_SASL_QUIET,
520 										   linphone_ldap_contact_provider_bind_interact,
521 										   obj);
522 	}
523 
524 	if( ret == LDAP_SUCCESS ) {
525 		ms_message("LDAP bind OK");
526 		obj->connected = 1;
527 	} else {
528 		int err;
529 		ldap_get_option(obj->ld, LDAP_OPT_RESULT_CODE, &err);
530 		ms_error("ldap_sasl_bind error returned %x, err %x (%s), auth_method: %s",
531 				 ret, err, ldap_err2string(err), auth_mechanism );
532 	}
533 
534 	obj->bind_thread = 0;
535 	linphone_ldap_contact_provider_unref(obj);
536 	return (void*)0;
537 }
538 
linphone_ldap_contact_provider_bind(LinphoneLDAPContactProvider * obj)539 static int linphone_ldap_contact_provider_bind( LinphoneLDAPContactProvider* obj )
540 {
541 	// perform the bind in an alternate thread, so that we don't stall the main loop
542 	ms_thread_create(&obj->bind_thread, NULL, ldap_bind_thread_func, obj);
543 	return 0;
544 }
545 
linphone_ldap_contact_provider_get_max_result(const LinphoneLDAPContactProvider * obj)546 unsigned int linphone_ldap_contact_provider_get_max_result(const LinphoneLDAPContactProvider* obj)
547 {
548 	return obj->max_results;
549 }
550 
linphone_ldap_contact_provider_config_dump_cb(const char * key,void * value,void * userdata)551 static void linphone_ldap_contact_provider_config_dump_cb(const char*key, void* value, void* userdata)
552 {
553 	ms_message("- %s -> %s", key, (const char* )value);
554 }
555 
linphone_ldap_contact_provider_create(LinphoneCore * lc,const LinphoneDictionary * config)556 LinphoneLDAPContactProvider*linphone_ldap_contact_provider_create(LinphoneCore* lc, const LinphoneDictionary* config)
557 {
558 	LinphoneLDAPContactProvider* obj = belle_sip_object_new(LinphoneLDAPContactProvider);
559 	int proto_version = LDAP_VERSION3;
560 
561 	linphone_contact_provider_init((LinphoneContactProvider*)obj, lc);
562 
563 	ms_message( "Constructed Contact provider '%s'", BELLE_SIP_OBJECT_VPTR(obj,LinphoneContactProvider)->name);
564 
565 	if( !linphone_ldap_contact_provider_valid_config(config) ) {
566 		ms_error( "Invalid configuration for LDAP, aborting creation");
567 		belle_sip_object_unref(obj);
568 		obj = NULL;
569 	} else {
570 		int ret;
571 		linphone_dictionary_foreach( config, linphone_ldap_contact_provider_config_dump_cb, 0 );
572 		linphone_ldap_contact_provider_loadconfig(obj, config);
573 
574 		ret = ldap_initialize(&(obj->ld),obj->server);
575 
576 		if( ret != LDAP_SUCCESS ){
577 			ms_error( "Problem initializing ldap on url '%s': %s", obj->server, ldap_err2string(ret));
578 			belle_sip_object_unref(obj);
579 			obj = NULL;
580 		} else if( (ret = ldap_set_option(obj->ld, LDAP_OPT_PROTOCOL_VERSION, &proto_version)) != LDAP_SUCCESS ){
581 			ms_error( "Problem setting protocol version %d: %s", proto_version, ldap_err2string(ret));
582 			belle_sip_object_unref(obj);
583 			obj = NULL;
584 		} else {
585 			// prevents blocking calls to bind() when the server is invalid, but this is not working for now..
586 			// see bug https://bugzilla.mozilla.org/show_bug.cgi?id=79509
587 			//ldap_set_option( obj->ld, LDAP_OPT_CONNECT_ASYNC, LDAP_OPT_ON);
588 
589 			// register our hook into iterate so that LDAP can do its magic asynchronously.
590 			linphone_core_add_iterate_hook(lc, linphone_ldap_contact_provider_iterate, obj);
591 		}
592 	}
593 	return obj;
594 }
595 
596 /**
597  * Search an LDAP request in the list of current LDAP requests to serve, only taking
598  * the msgid as a key to search.
599  */
linphone_ldap_request_entry_compare_weak(const void * a,const void * b)600 static int linphone_ldap_request_entry_compare_weak(const void*a, const void* b)
601 {
602 	const LinphoneLDAPContactSearch* ra = (const LinphoneLDAPContactSearch*)a;
603 	const LinphoneLDAPContactSearch* rb = (const LinphoneLDAPContactSearch*)b;
604 	return !(ra->msgid == rb->msgid); // 0 if equal
605 }
606 
607 /**
608  * Search an LDAP request in the list of current LDAP requests to serve, with strong search
609  * comparing both msgid and request pointer
610  */
linphone_ldap_request_entry_compare_strong(const void * a,const void * b)611 static int linphone_ldap_request_entry_compare_strong(const void*a, const void* b)
612 {
613 	const LinphoneLDAPContactSearch* ra = (const LinphoneLDAPContactSearch*)a;
614 	const LinphoneLDAPContactSearch* rb = (const LinphoneLDAPContactSearch*)b;
615 	return !(ra->msgid == rb->msgid) && !(ra == rb);
616 }
617 
linphone_ldap_contact_provider_request_search(LinphoneLDAPContactProvider * obj,int msgid)618 static inline LinphoneLDAPContactSearch* linphone_ldap_contact_provider_request_search( LinphoneLDAPContactProvider* obj, int msgid )
619 {
620 	LinphoneLDAPContactSearch dummy = {};
621 	bctbx_list_t* list_entry;
622 	dummy.msgid = msgid;
623 
624 	list_entry = bctbx_list_find_custom(obj->requests, linphone_ldap_request_entry_compare_weak, &dummy);
625 	if( list_entry ) return list_entry->data;
626 	else return NULL;
627 }
628 
linphone_ldap_contact_provider_cancel_search(LinphoneContactProvider * obj,LinphoneContactSearch * req)629 static unsigned int linphone_ldap_contact_provider_cancel_search(LinphoneContactProvider* obj, LinphoneContactSearch *req)
630 {
631 	LinphoneLDAPContactSearch*  ldap_req = LINPHONE_LDAP_CONTACT_SEARCH(req);
632 	LinphoneLDAPContactProvider* ldap_cp = LINPHONE_LDAP_CONTACT_PROVIDER(obj);
633 	int ret = 1;
634 
635 	bctbx_list_t* list_entry = bctbx_list_find_custom(ldap_cp->requests, linphone_ldap_request_entry_compare_strong, req);
636 	if( list_entry ) {
637 		ms_message("Delete search %p", req);
638 		ldap_cp->requests = bctbx_list_erase_link(ldap_cp->requests, list_entry);
639 		ldap_cp->req_count--;
640 		ret = 0; // return OK if we found it in the monitored requests
641 	} else {
642 		ms_warning("Couldn't find ldap request %p (id %d) in monitoring.", ldap_req, ldap_req->msgid);
643 	}
644 	belle_sip_object_unref(req); // unref request even if not found
645 	return ret;
646 }
647 
linphone_ldap_contact_provider_perform_search(LinphoneLDAPContactProvider * obj,LinphoneLDAPContactSearch * req)648 static int linphone_ldap_contact_provider_perform_search( LinphoneLDAPContactProvider* obj, LinphoneLDAPContactSearch* req)
649 {
650 	int ret = -1;
651 	struct timeval timeout = { obj->timeout, 0 };
652 
653 	if( req->msgid == 0 ){
654 		ms_message ( "Calling ldap_search_ext with predicate '%s' on base '%s', ld %p, attrs '%s', maxres = %d", req->filter, obj->base_object, obj->ld, obj->attributes[0], obj->max_results );
655 		ret = ldap_search_ext(obj->ld,
656 						obj->base_object,// base from which to start
657 						LDAP_SCOPE_SUBTREE,
658 						req->filter,     // search predicate
659 						obj->attributes, // which attributes to get
660 						0,               // 0 = get attrs AND value, 1 = get attrs only
661 						NULL,
662 						NULL,
663 						&timeout,        // server timeout for the search
664 						obj->max_results,// max result number
665 						&req->msgid );
666 
667 		if( ret != LDAP_SUCCESS ){
668 			ms_error("Error ldap_search_ext returned %d (%s)", ret, ldap_err2string(ret));
669 		} else {
670 			ms_message("LinphoneLDAPContactSearch created @%p : msgid %d", req, req->msgid);
671 		}
672 
673 	} else {
674 		ms_warning( "LDAP Search already performed for %s, msgid %d", req->filter, req->msgid);
675 	}
676 	return ret;
677 }
678 
linphone_ldap_contact_provider_begin_search(LinphoneLDAPContactProvider * obj,const char * predicate,ContactSearchCallback cb,void * cb_data)679 static LinphoneLDAPContactSearch* linphone_ldap_contact_provider_begin_search ( LinphoneLDAPContactProvider* obj,
680 		const char* predicate,
681 		ContactSearchCallback cb,
682 		void* cb_data )
683 {
684 	bool_t connected = obj->connected;
685 	LinphoneLDAPContactSearch* request;
686 
687 	// if we're not yet connected, bind
688 	if( !connected ) {
689 		if( !obj->bind_thread ) linphone_ldap_contact_provider_bind(obj);
690 	}
691 
692 	request = linphone_ldap_contact_search_create( obj, predicate, cb, cb_data );
693 
694 	if( connected ){
695 		int ret = linphone_ldap_contact_provider_perform_search(obj, request);
696 		ms_message ( "Created search %d for '%s', msgid %d, @%p", obj->req_count, predicate, request->msgid, request );
697 		if( ret != LDAP_SUCCESS ){
698 			belle_sip_object_unref(request);
699 			request = NULL;
700 		}
701 	} else {
702 		ms_message("Delayed search, wait for connection");
703 	}
704 
705 	if( request != NULL ) {
706 		obj->requests = bctbx_list_append ( obj->requests, request );
707 		obj->req_count++;
708 	}
709 
710 	return request;
711 }
712 
713 
linphone_ldap_contact_provider_marshal(LinphoneLDAPContactProvider * obj,char * buff,size_t buff_size,size_t * offset)714 static int linphone_ldap_contact_provider_marshal(LinphoneLDAPContactProvider* obj, char* buff, size_t buff_size, size_t *offset)
715 {
716 	belle_sip_error_code error = BELLE_SIP_OK;
717 	char **attr;
718 
719 	error = belle_sip_snprintf(buff, buff_size, offset, "ld:%p,\n", obj->ld);
720 	if(error!= BELLE_SIP_OK) return error;
721 
722 	error = belle_sip_snprintf(buff, buff_size, offset, "req_count:%d,\n", obj->req_count);
723 	if(error!= BELLE_SIP_OK) return error;
724 
725 	error = belle_sip_snprintf(buff, buff_size, offset,
726 							   "CONFIG:\n"
727 							   "tls: %d \n"
728 							   "auth: %s \n"
729 							   "user: %s \n"
730 							   "pass: %s \n"
731 							   "server: %s \n"
732 							   "base: %s \n"
733 							   "filter: %s \n"
734 							   "timeout: %d \n"
735 							   "deref: %d \n"
736 							   "max_res: %d \n"
737 							   "sip_attr:%s \n"
738 							   "name_attr:%s \n"
739 							   "attrs:\n",
740 							   obj->use_tls, obj->auth_method,
741 							   obj->username, obj->password, obj->server,
742 							   obj->base_object, obj->filter,
743 							   obj->timeout, obj->deref_aliases,
744 							   obj->max_results,
745 							   obj->sip_attr, obj->name_attr);
746 	if(error!= BELLE_SIP_OK) return error;
747 
748 	attr = obj->attributes;
749 	while( *attr ){
750 		error = belle_sip_snprintf(buff, buff_size, offset, "- %s\n", *attr);
751 		if(error!= BELLE_SIP_OK) return error;
752 		else attr++;
753 	}
754 
755 	return error;
756 
757 }
758 
linphone_ldap_contact_provider_ref(void * obj)759 LinphoneLDAPContactProvider*linphone_ldap_contact_provider_ref(void* obj)
760 {
761 	return linphone_ldap_contact_provider_cast(belle_sip_object_ref(obj));
762 }
763 
764 
linphone_ldap_contact_provider_unref(void * obj)765 void linphone_ldap_contact_provider_unref(void* obj)
766 {
767 	belle_sip_object_unref(obj);
768 }
769 
linphone_ldap_contact_search_cast(void * obj)770 inline LinphoneLDAPContactSearch*linphone_ldap_contact_search_cast(void* obj)
771 {
772 	return BELLE_SIP_CAST(obj, LinphoneLDAPContactSearch);
773 }
774 
775 
linphone_ldap_contact_provider_cast(void * obj)776 LinphoneLDAPContactProvider* linphone_ldap_contact_provider_cast(void* obj)
777 {
778 	return BELLE_SIP_CAST(obj, LinphoneLDAPContactProvider);
779 }
780 
linphone_ldap_contact_provider_available()781 int linphone_ldap_contact_provider_available()
782 {
783 	return 1;
784 }
785 
786 BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneLDAPContactProvider);
787 
BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(LinphoneLDAPContactProvider)788 BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(LinphoneLDAPContactProvider)
789 	{
790 		{
791 			BELLE_SIP_VPTR_INIT(LinphoneLDAPContactProvider,LinphoneContactProvider,TRUE),
792 			(belle_sip_object_destroy_t)linphone_ldap_contact_provider_destroy,
793 			NULL,
794 			(belle_sip_object_marshal_t)linphone_ldap_contact_provider_marshal
795 		},
796 		"LDAP",
797 		(LinphoneContactProviderStartSearchMethod)linphone_ldap_contact_provider_begin_search,
798 		(LinphoneContactProviderCancelSearchMethod)linphone_ldap_contact_provider_cancel_search
799 	}
800 BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END
801 
802 #else
803 
804 /* Stubbed implementation */
805 
806 LinphoneLDAPContactSearch* linphone_ldap_contact_search_create(LinphoneLDAPContactProvider* ld,
807 															   const char* predicate,
808 															   ContactSearchCallback cb,
809 															   void* cb_data)
810 {
811 	return NULL;
812 }
813 
814 unsigned int linphone_ldap_contact_search_result_count(LinphoneLDAPContactSearch* obj){ return 0; }
815 LinphoneLDAPContactSearch* linphone_ldap_contact_search_cast( void* obj ){ return NULL; }
816 
817 
818 /* LinphoneLDAPContactProvider */
819 
820 LinphoneLDAPContactProvider* linphone_ldap_contact_provider_create(LinphoneCore* lc, const LinphoneDictionary* config){ return NULL; }
821 unsigned int                 linphone_ldap_contact_provider_get_max_result(const LinphoneLDAPContactProvider* obj){ return 0; }
822 LinphoneLDAPContactProvider* linphone_ldap_contact_provider_ref( void* obj ){ return NULL; }
823 void                         linphone_ldap_contact_provider_unref( void* obj ){  }
824 LinphoneLDAPContactProvider* linphone_ldap_contact_provider_cast( void* obj ){ return NULL; }
825 
826 int linphone_ldap_contact_provider_available(){	return 0; }
827 
828 
829 #endif /* BUILD_LDAP */
830 
831