ldapprovider.c 25.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

Ghislain MARY's avatar
Ghislain MARY committed
17
#include "linphone/ldapprovider.h"
Simon Morlat's avatar
Simon Morlat committed
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 30 31


#define MAX_RUNNING_REQUESTS 10
#define FILTER_MAX_SIZE      512

32 33 34 35 36
struct LDAPFriendData {
	char* name;
	char* sip;
};

37 38
struct _LinphoneLDAPContactProvider
{
39
	LinphoneContactProvider base;
40 41
	LinphoneDictionary* config;

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 51

	// config
	int use_tls;
52
	const char*  auth_method;
53 54 55
	const char*  username;
	const char*  password;
	const char*  server;
56 57 58 59
	const char*  bind_dn;

	const char*  sasl_authname;
	const char*  sasl_realm;
60

61 62 63 64 65 66
	const char*  base_object;
	const char*  sip_attr;
	const char*  name_attr;
	const char*  filter;

	char**       attributes;
67

68 69 70
	int    timeout;
	int    deref_aliases;
	int    max_results;
71

72 73
};

74 75
struct _LinphoneLDAPContactSearch
{
76 77 78 79
	LinphoneContactSearch base;
	LDAP*   ld;
	int     msgid;
	char*   filter;
80
	bool_t  complete;
81
	bctbx_list_t* found_entries;
82
	unsigned int found_count;
83 84 85
};


86 87 88 89
/* *************************
 * LinphoneLDAPContactSearch
 * *************************/

90
LinphoneLDAPContactSearch* linphone_ldap_contact_search_create(LinphoneLDAPContactProvider* cp, const char* predicate, ContactSearchCallback cb, void* cb_data)
91 92 93
{
	LinphoneLDAPContactSearch* search = belle_sip_object_new(LinphoneLDAPContactSearch);
	LinphoneContactSearch* base = LINPHONE_CONTACT_SEARCH(search);
94

95
	linphone_contact_search_init(base, predicate, cb, cb_data);
96 97 98

	search->ld = cp->ld;

99 100 101
	search->filter = ms_malloc(FILTER_MAX_SIZE);
	snprintf(search->filter, FILTER_MAX_SIZE-1, cp->filter, predicate);
	search->filter[FILTER_MAX_SIZE-1] = 0;
102

103 104 105
	return search;
}

106 107 108 109 110
void linphone_ldap_contact_search_destroy_friend( void* entry )
{
	linphone_friend_destroy((LinphoneFriend*)entry);
}

111 112 113 114 115
unsigned int linphone_ldap_contact_search_result_count(LinphoneLDAPContactSearch* obj)
{
	return obj->found_count;
}

116
static void linphone_ldap_contact_search_destroy( LinphoneLDAPContactSearch* obj )
117
{
118
	//ms_message("~LinphoneLDAPContactSearch(%p)", obj);
119 120
	bctbx_list_for_each(obj->found_entries, linphone_ldap_contact_search_destroy_friend);
	obj->found_entries = bctbx_list_free(obj->found_entries);
121
	if( obj->filter ) ms_free(obj->filter);
122 123 124 125
}

BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneLDAPContactSearch);
BELLE_SIP_INSTANCIATE_VPTR(LinphoneLDAPContactSearch,LinphoneContactSearch,
126
						   (belle_sip_object_destroy_t)linphone_ldap_contact_search_destroy,
127 128 129 130 131 132 133 134 135
						   NULL,
						   NULL,
						   TRUE
);


/* ***************************
 * LinphoneLDAPContactProvider
 * ***************************/
136 137

static inline LinphoneLDAPContactSearch* linphone_ldap_contact_provider_request_search( LinphoneLDAPContactProvider* obj, int msgid );
138 139
static unsigned int linphone_ldap_contact_provider_cancel_search(LinphoneContactProvider* obj, LinphoneContactSearch *req);
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

144
static void linphone_ldap_contact_provider_destroy_request_cb(void *req)
145 146 147 148
{
	belle_sip_object_unref(req);
}

149 150
static void linphone_ldap_contact_provider_destroy( LinphoneLDAPContactProvider* obj )
{
151
	//ms_message("linphone_ldap_contact_provider_destroy");
152 153
	linphone_core_remove_iterate_hook(LINPHONE_CONTACT_PROVIDER(obj)->lc, linphone_ldap_contact_provider_iterate,obj);

154
	// clean pending requests
155
	bctbx_list_for_each(obj->requests, linphone_ldap_contact_provider_destroy_request_cb);
156

157 158
	if (obj->ld) ldap_unbind_ext(obj->ld, NULL, NULL);
	obj->ld = NULL;
159

160 161
	if( obj->config ) linphone_dictionary_unref(obj->config);

162
	linphone_ldap_contact_provider_conf_destroy(obj);
163 164
}

165
static int linphone_ldap_contact_provider_complete_contact( LinphoneLDAPContactProvider* obj, struct LDAPFriendData* lf, const char* attr_name, const char* attr_value)
166 167 168 169 170 171 172 173
{
	if( strcmp(attr_name, obj->name_attr ) == 0 ){
		lf->name = ms_strdup(attr_value);
	} else if( strcmp(attr_name, obj->sip_attr) == 0 ) {
		lf->sip = ms_strdup(attr_value);
	}

	// return 1 if the structure has enough data to create a linphone friend
174 175 176 177
	if( lf->name && lf->sip )
		return 1;
	else
		return 0;
178 179 180

}

181
static void linphone_ldap_contact_provider_handle_search_result( LinphoneLDAPContactProvider* obj, LinphoneLDAPContactSearch* req, LDAPMessage* message )
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
182
{
183
	int msgtype = ldap_msgtype(message);
184

185 186
	switch(msgtype){

187 188 189 190 191 192 193 194 195
	case LDAP_RES_SEARCH_ENTRY:
	case LDAP_RES_EXTENDED:
	{
		LDAPMessage *entry = ldap_first_entry(obj->ld, message);
		LinphoneCore*   lc = LINPHONE_CONTACT_PROVIDER(obj)->lc;

		while( entry != NULL ){

			struct LDAPFriendData ldap_data = {0};
196
			bool_t contact_complete = FALSE;
197 198 199 200 201 202 203 204 205
			BerElement*  ber = NULL;
			char*       attr = ldap_first_attribute(obj->ld, entry, &ber);

			while( attr ){
				struct berval** values = ldap_get_values_len(obj->ld, entry, attr);
				struct berval**     it = values;

				while( values && *it && (*it)->bv_val && (*it)->bv_len )
				{
206 207
					contact_complete = linphone_ldap_contact_provider_complete_contact(obj, &ldap_data, attr, (*it)->bv_val);
					if( contact_complete ) break;
208 209

					it++;
210 211
				}

212 213 214
				if( values ) ldap_value_free_len(values);
				ldap_memfree(attr);

215
				if( contact_complete ) break;
216

217
				attr = ldap_next_attribute(obj->ld, entry, ber);
218
			}
219

220
			if( contact_complete ) {
221 222 223 224 225
				LinphoneAddress* la = linphone_core_interpret_url(lc, ldap_data.sip);
				if( la ){
					LinphoneFriend* lf = linphone_core_create_friend(lc);
					linphone_friend_set_address(lf, la);
					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 230
					ms_free(ldap_data.sip);
					ms_free(ldap_data.name);
Simon Morlat's avatar
Simon Morlat committed
231
					linphone_address_unref(la);
232 233 234 235 236 237
				}
			}

			if( ber ) ber_free(ber, 0);

			entry = ldap_next_entry(obj->ld, entry);
238
		}
239 240 241 242 243 244 245 246 247 248 249
	}
	break;

	case LDAP_RES_SEARCH_RESULT:
	{
		// this one is received when a request is finished
		req->complete = TRUE;
		linphone_contact_search_invoke_cb(LINPHONE_CONTACT_SEARCH(req), req->found_entries);
	}
	break;

250

251
	default: ms_message("[LDAP] Unhandled message type %x", msgtype); break;
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
252 253 254
	}
}

255 256 257
static bool_t linphone_ldap_contact_provider_iterate(void *data)
{
	LinphoneLDAPContactProvider* obj = LINPHONE_LDAP_CONTACT_PROVIDER(data);
258
	if( obj->ld && obj->connected && (obj->req_count > 0) ){
259

260 261 262 263
		// never block
		struct timeval timeout = {0,0};
		LDAPMessage* results = NULL;

264
		int ret = ldap_result(obj->ld, LDAP_RES_ANY, LDAP_MSG_ONE, &timeout, &results);
265

266
		switch( ret ){
267 268
		case -1:
		{
269
			ms_warning("Error in ldap_result : returned -1 (req_count %d): %s", obj->req_count, ldap_err2string(errno));
270 271 272
			break;
		}
		case 0: break; // nothing to do
273 274

		case LDAP_RES_BIND:
275
		{
276
			ms_error("iterate: unexpected LDAP_RES_BIND");
277 278
			break;
		}
279
		case LDAP_RES_EXTENDED:
280 281
		case LDAP_RES_SEARCH_ENTRY:
		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 290
				message = ldap_next_message(obj->ld, message);
			}
291 292 293 294
			if( req && ret == LDAP_RES_SEARCH_RESULT)
				linphone_ldap_contact_provider_cancel_search(
							LINPHONE_CONTACT_PROVIDER(obj),
							LINPHONE_CONTACT_SEARCH(req));
295 296
			break;
		}
297 298 299 300 301 302
		case LDAP_RES_MODIFY:
		case LDAP_RES_ADD:
		case LDAP_RES_DELETE:
		case LDAP_RES_MODDN:
		case LDAP_RES_COMPARE:
		default:
303
			ms_message("Unhandled LDAP result %x", ret);
304 305
			break;
		}
306 307 308

		if( results )
			ldap_msgfree(results);
309
	}
310 311 312

	if( obj->ld && obj->connected ){
		// 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 321 322 323 324 325
				ret = linphone_ldap_contact_provider_perform_search(obj, search);
				if( ret != LDAP_SUCCESS ){
					linphone_ldap_contact_provider_cancel_search(
								LINPHONE_CONTACT_PROVIDER(obj),
								LINPHONE_CONTACT_SEARCH(search));
				}
326 327 328 329
			}
		}
	}

330 331 332
	return TRUE;
}

333 334 335 336 337 338 339 340 341 342 343
static void linphone_ldap_contact_provider_conf_destroy(LinphoneLDAPContactProvider* obj )
{
	if(obj->attributes){
		int i=0;
		for( ; obj->attributes[i]; i++){
			ms_free(obj->attributes[i]);
		}
		ms_free(obj->attributes);
	}
}

344 345 346 347 348 349 350
static char* required_config_keys[] = {
	// connection
	"server",
	"use_tls",
	"auth_method",
	"username",
	"password",
351 352 353
	"bind_dn",
	"sasl_authname",
	"sasl_realm",
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368

	// search
	"base_object",
	"filter",
	"name_attribute",
	"sip_attribute",
	"attributes",

	// misc
	"timeout",
	"max_results",
	"deref_aliases",
	NULL
};

369
static bool_t linphone_ldap_contact_provider_valid_config(const LinphoneDictionary* dict)
370
{
371 372 373 374 375 376 377 378 379
	char** config_name = required_config_keys;

	bool_t valid = TRUE;
	bool_t has_key;

	while(*config_name ){
		has_key = linphone_dictionary_haskey(dict, *config_name);
		if( !has_key ) ms_error("Missing LDAP config value for '%s'", *config_name);
		valid &= has_key;
380
		config_name++;
381
	}
382 383 384
	return valid;
}

385
static void linphone_ldap_contact_provider_loadconfig(LinphoneLDAPContactProvider* obj, const LinphoneDictionary* dict)
386 387 388
{
	char* attributes_list, *saveptr, *attr;
	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 395 396
	if( obj->config ) linphone_dictionary_unref(obj->config);

	// clone new config into the dictionary
397
	obj->config = linphone_dictionary_ref(linphone_dictionary_clone(dict));
398

399 400 401 402
#if 0 // until sasl auth is set up, force anonymous auth.
	linphone_dictionary_set_string(obj->config, "auth_method", "ANONYMOUS");
#endif

403 404 405 406
	obj->use_tls       = linphone_dictionary_get_int(obj->config, "use_tls",       0);
	obj->timeout       = linphone_dictionary_get_int(obj->config, "timeout",       10);
	obj->deref_aliases = linphone_dictionary_get_int(obj->config, "deref_aliases", 0);
	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 409
	obj->username      = linphone_dictionary_get_string(obj->config, "username",       "");
	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 414 415
	obj->filter        = linphone_dictionary_get_string(obj->config, "filter",         "uid=*%s*");
	obj->name_attr     = linphone_dictionary_get_string(obj->config, "name_attribute", "givenName");
	obj->sip_attr      = linphone_dictionary_get_string(obj->config, "sip_attribute",  "mobile");
416 417
	obj->sasl_authname = linphone_dictionary_get_string(obj->config, "sasl_authname",  "");
	obj->sasl_realm    = linphone_dictionary_get_string(obj->config, "sasl_realm",  "");
418

419 420 421
	/*
	 * parse the attributes list
	 */
422 423 424 425 426
	attributes_list = ms_strdup(
				linphone_dictionary_get_string(obj->config,
											   "attributes",
											   "telephoneNumber,givenName,sn,mobile,homePhone")
				);
427 428

	// count attributes:
429
	for( i=0; attributes_list[i]; i++) {
430 431
		if( attributes_list[i] == ',') attr_count++;
	}
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 437 438 439 440 441

	attr = strtok_r( attributes_list, ",", &saveptr );
	while( attr != NULL ){
		obj->attributes[attr_idx] = ms_strdup(attr);
		attr_idx++;
		attr = strtok_r(NULL, ",", &saveptr);
	}
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
}

447 448 449 450
static int linphone_ldap_contact_provider_bind_interact(LDAP *ld,
														unsigned flags,
														void *defaults,
														void *sasl_interact)
451
{
452 453 454 455
	sasl_interact_t *interact = (sasl_interact_t*)sasl_interact;
	LinphoneLDAPContactProvider* obj = LINPHONE_LDAP_CONTACT_PROVIDER(defaults);
	ms_message("bind_interact called: ld %p, flags %x, default %p, interact %p",
			   ld, flags, defaults, sasl_interact);
456

457 458 459 460 461 462 463 464
	if( ld == NULL ) return LDAP_PARAM_ERROR;

	while( interact->id != SASL_CB_LIST_END ) {

		const char *dflt = interact->defresult;

		switch( interact->id ) {
		case SASL_CB_GETREALM:
465 466
			ms_message("* SASL_CB_GETREALM -> %s", obj->sasl_realm);
			dflt = obj->sasl_realm;
467
		break;
468
		case SASL_CB_AUTHNAME:
469 470 471 472 473 474
			ms_message("* SASL_CB_AUTHNAME -> %s", obj->sasl_authname);
			dflt = obj->sasl_authname;
		break;
		case SASL_CB_USER:
			ms_message("* SASL_CB_USER -> %s", obj->username);
			dflt = obj->username;
475 476
		break;
		case SASL_CB_PASS:
477 478
			ms_message("* SASL_CB_PASS (hidden)");
			dflt = obj->password;
479
		break;
480
		default:
481
			ms_message("SASL interact asked for unknown id %lx\n",interact->id);
482 483 484 485 486 487 488 489 490
		}
		interact->result = (dflt && *dflt) ? dflt : (const char*)"";
		interact->len = strlen( (const char*)interact->result );

		interact++;
	}
	return LDAP_SUCCESS;
}

491
static void* ldap_bind_thread_func( void*arg)
492
{
493 494
	LinphoneLDAPContactProvider* obj = linphone_ldap_contact_provider_ref(arg);
	const char* auth_mechanism = obj->auth_method;
495
	int ret;
496 497 498 499 500 501 502 503 504 505 506 507 508 509

	if( (strcmp(auth_mechanism, "ANONYMOUS") == 0) || (strcmp(auth_mechanism, "SIMPLE") == 0) )
	{
		struct berval passwd = { strlen(obj->password), ms_strdup(obj->password)};
		auth_mechanism = LDAP_SASL_SIMPLE;
		ret = ldap_sasl_bind_s(obj->ld,
							   obj->bind_dn,
							   auth_mechanism,
							   &passwd,
							   NULL,
							   NULL,
							   NULL);

		ms_free(passwd.bv_val);
510
	}
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526
	else
	{

		ms_message("LDAP interactive bind");
		ret = ldap_sasl_interactive_bind_s(obj->ld,
										   obj->bind_dn,
										   auth_mechanism,
										   NULL,NULL,
										   LDAP_SASL_QUIET,
										   linphone_ldap_contact_provider_bind_interact,
										   obj);
	}

	if( ret == LDAP_SUCCESS ) {
		ms_message("LDAP bind OK");
		obj->connected = 1;
527 528 529
	} else {
		int err;
		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 535 536 537 538 539 540 541 542
	obj->bind_thread = 0;
	linphone_ldap_contact_provider_unref(obj);
	return (void*)0;
}

static int linphone_ldap_contact_provider_bind( LinphoneLDAPContactProvider* obj )
{
	// perform the bind in an alternate thread, so that we don't stall the main loop
	ms_thread_create(&obj->bind_thread, NULL, ldap_bind_thread_func, obj);
543 544
	return 0;
}
545

546 547 548 549 550
unsigned int linphone_ldap_contact_provider_get_max_result(const LinphoneLDAPContactProvider* obj)
{
	return obj->max_results;
}

551 552 553 554 555
static void linphone_ldap_contact_provider_config_dump_cb(const char*key, void* value, void* userdata)
{
	ms_message("- %s -> %s", key, (const char* )value);
}

556
LinphoneLDAPContactProvider*linphone_ldap_contact_provider_create(LinphoneCore* lc, const LinphoneDictionary* config)
557 558 559
{
	LinphoneLDAPContactProvider* obj = belle_sip_object_new(LinphoneLDAPContactProvider);
	int proto_version = LDAP_VERSION3;
560 561

	linphone_contact_provider_init((LinphoneContactProvider*)obj, lc);
562

563 564
	ms_message( "Constructed Contact provider '%s'", BELLE_SIP_OBJECT_VPTR(obj,LinphoneContactProvider)->name);

565 566
	if( !linphone_ldap_contact_provider_valid_config(config) ) {
		ms_error( "Invalid configuration for LDAP, aborting creation");
567
		belle_sip_object_unref(obj);
568
		obj = NULL;
569
	} else {
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
570
		int ret;
571
		linphone_dictionary_foreach( config, linphone_ldap_contact_provider_config_dump_cb, 0 );
572 573
		linphone_ldap_contact_provider_loadconfig(obj, config);

Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
574
		ret = ldap_initialize(&(obj->ld),obj->server);
575 576 577 578 579 580 581 582 583 584

		if( ret != LDAP_SUCCESS ){
			ms_error( "Problem initializing ldap on url '%s': %s", obj->server, ldap_err2string(ret));
			belle_sip_object_unref(obj);
			obj = NULL;
		} else if( (ret = ldap_set_option(obj->ld, LDAP_OPT_PROTOCOL_VERSION, &proto_version)) != LDAP_SUCCESS ){
			ms_error( "Problem setting protocol version %d: %s", proto_version, ldap_err2string(ret));
			belle_sip_object_unref(obj);
			obj = NULL;
		} else {
585 586
			// prevents blocking calls to bind() when the server is invalid, but this is not working for now..
			// 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 590 591
			// register our hook into iterate so that LDAP can do its magic asynchronously.
			linphone_core_add_iterate_hook(lc, linphone_ldap_contact_provider_iterate, obj);
		}
592 593 594 595
	}
	return obj;
}

596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
/**
 * Search an LDAP request in the list of current LDAP requests to serve, only taking
 * the msgid as a key to search.
 */
static int linphone_ldap_request_entry_compare_weak(const void*a, const void* b)
{
	const LinphoneLDAPContactSearch* ra = (const LinphoneLDAPContactSearch*)a;
	const LinphoneLDAPContactSearch* rb = (const LinphoneLDAPContactSearch*)b;
	return !(ra->msgid == rb->msgid); // 0 if equal
}

/**
 * Search an LDAP request in the list of current LDAP requests to serve, with strong search
 * comparing both msgid and request pointer
 */
static int linphone_ldap_request_entry_compare_strong(const void*a, const void* b)
{
	const LinphoneLDAPContactSearch* ra = (const LinphoneLDAPContactSearch*)a;
	const LinphoneLDAPContactSearch* rb = (const LinphoneLDAPContactSearch*)b;
	return !(ra->msgid == rb->msgid) && !(ra == rb);
}

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 626 627 628
	if( list_entry ) return list_entry->data;
	else return NULL;
}

629
static unsigned int linphone_ldap_contact_provider_cancel_search(LinphoneContactProvider* obj, LinphoneContactSearch *req)
630 631 632 633 634
{
	LinphoneLDAPContactSearch*  ldap_req = LINPHONE_LDAP_CONTACT_SEARCH(req);
	LinphoneLDAPContactProvider* ldap_cp = LINPHONE_LDAP_CONTACT_PROVIDER(obj);
	int ret = 1;

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);
Simon Morlat's avatar
Simon Morlat committed
638
		ldap_cp->requests = bctbx_list_erase_link(ldap_cp->requests, list_entry);
639 640 641 642 643 644 645 646 647
		ldap_cp->req_count--;
		ret = 0; // return OK if we found it in the monitored requests
	} else {
		ms_warning("Couldn't find ldap request %p (id %d) in monitoring.", ldap_req, ldap_req->msgid);
	}
	belle_sip_object_unref(req); // unref request even if not found
	return ret;
}

648 649 650 651 652 653
static int linphone_ldap_contact_provider_perform_search( LinphoneLDAPContactProvider* obj, LinphoneLDAPContactSearch* req)
{
	int ret = -1;
	struct timeval timeout = { obj->timeout, 0 };

	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 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678
		ret = ldap_search_ext(obj->ld,
						obj->base_object,// base from which to start
						LDAP_SCOPE_SUBTREE,
						req->filter,     // search predicate
						obj->attributes, // which attributes to get
						0,               // 0 = get attrs AND value, 1 = get attrs only
						NULL,
						NULL,
						&timeout,        // server timeout for the search
						obj->max_results,// max result number
						&req->msgid );

		if( ret != LDAP_SUCCESS ){
			ms_error("Error ldap_search_ext returned %d (%s)", ret, ldap_err2string(ret));
		} else {
			ms_message("LinphoneLDAPContactSearch created @%p : msgid %d", req, req->msgid);
		}

	} else {
		ms_warning( "LDAP Search already performed for %s, msgid %d", req->filter, req->msgid);
	}
	return ret;
}

679
static LinphoneLDAPContactSearch* linphone_ldap_contact_provider_begin_search ( LinphoneLDAPContactProvider* obj,
680 681 682 683
		const char* predicate,
		ContactSearchCallback cb,
		void* cb_data )
{
684
	bool_t connected = obj->connected;
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
685
	LinphoneLDAPContactSearch* request;
686

687
	// if we're not yet connected, bind
688 689 690
	if( !connected ) {
		if( !obj->bind_thread ) linphone_ldap_contact_provider_bind(obj);
	}
691

Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
692
	request = linphone_ldap_contact_search_create( obj, predicate, cb, cb_data );
693

694 695
	if( connected ){
		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 698 699 700 701 702 703
		if( ret != LDAP_SUCCESS ){
			belle_sip_object_unref(request);
			request = NULL;
		}
	} else {
		ms_message("Delayed search, wait for connection");
	}
704

705
	if( request != NULL ) {
706
		obj->requests = bctbx_list_append ( obj->requests, request );
707 708
		obj->req_count++;
	}
709

710 711 712 713
	return request;
}


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;
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
717
	char **attr;
718 719 720 721 722 723 724 725 726

	error = belle_sip_snprintf(buff, buff_size, offset, "ld:%p,\n", obj->ld);
	if(error!= BELLE_SIP_OK) return error;

	error = belle_sip_snprintf(buff, buff_size, offset, "req_count:%d,\n", obj->req_count);
	if(error!= BELLE_SIP_OK) return error;

	error = belle_sip_snprintf(buff, buff_size, offset,
							   "CONFIG:\n"
727
							   "tls: %d \n"
728
							   "auth: %s \n"
729 730 731 732 733
							   "user: %s \n"
							   "pass: %s \n"
							   "server: %s \n"
							   "base: %s \n"
							   "filter: %s \n"
734
							   "timeout: %d \n"
735
							   "deref: %d \n"
736
							   "max_res: %d \n"
737 738 739
							   "sip_attr:%s \n"
							   "name_attr:%s \n"
							   "attrs:\n",
740 741 742 743
							   obj->use_tls, obj->auth_method,
							   obj->username, obj->password, obj->server,
							   obj->base_object, obj->filter,
							   obj->timeout, obj->deref_aliases,
744 745
							   obj->max_results,
							   obj->sip_attr, obj->name_attr);
746 747
	if(error!= BELLE_SIP_OK) return error;

Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
748
	attr = obj->attributes;
749
	while( *attr ){
750
		error = belle_sip_snprintf(buff, buff_size, offset, "- %s\n", *attr);
751 752 753 754 755 756 757 758
		if(error!= BELLE_SIP_OK) return error;
		else attr++;
	}

	return error;

}

759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779
LinphoneLDAPContactProvider*linphone_ldap_contact_provider_ref(void* obj)
{
	return linphone_ldap_contact_provider_cast(belle_sip_object_ref(obj));
}


void linphone_ldap_contact_provider_unref(void* obj)
{
	belle_sip_object_unref(obj);
}

inline LinphoneLDAPContactSearch*linphone_ldap_contact_search_cast(void* obj)
{
	return BELLE_SIP_CAST(obj, LinphoneLDAPContactSearch);
}


LinphoneLDAPContactProvider* linphone_ldap_contact_provider_cast(void* obj)
{
	return BELLE_SIP_CAST(obj, LinphoneLDAPContactProvider);
}
780

781 782 783 784 785
int linphone_ldap_contact_provider_available()
{
	return 1;
}

786 787
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneLDAPContactProvider);

788
BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(LinphoneLDAPContactProvider)
789 790 791 792 793
	{
		{
			BELLE_SIP_VPTR_INIT(LinphoneLDAPContactProvider,LinphoneContactProvider,TRUE),
			(belle_sip_object_destroy_t)linphone_ldap_contact_provider_destroy,
			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 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824
BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END

#else

/* Stubbed implementation */

LinphoneLDAPContactSearch* linphone_ldap_contact_search_create(LinphoneLDAPContactProvider* ld,
															   const char* predicate,
															   ContactSearchCallback cb,
															   void* cb_data)
{
	return NULL;
}

unsigned int linphone_ldap_contact_search_result_count(LinphoneLDAPContactSearch* obj){ return 0; }
LinphoneLDAPContactSearch* linphone_ldap_contact_search_cast( void* obj ){ return NULL; }


/* LinphoneLDAPContactProvider */

LinphoneLDAPContactProvider* linphone_ldap_contact_provider_create(LinphoneCore* lc, const LinphoneDictionary* config){ return NULL; }
unsigned int                 linphone_ldap_contact_provider_get_max_result(const LinphoneLDAPContactProvider* obj){ return 0; }
LinphoneLDAPContactProvider* linphone_ldap_contact_provider_ref( void* obj ){ return NULL; }
void                         linphone_ldap_contact_provider_unref( void* obj ){  }
LinphoneLDAPContactProvider* linphone_ldap_contact_provider_cast( void* obj ){ return NULL; }
825

826 827
int linphone_ldap_contact_provider_available(){	return 0; }

828

829
#endif /* BUILD_LDAP */
830