ldapprovider.c 23.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 *  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.
 */

#include "ldapprovider.h"
18
#include "linphonecore.h"
19 20
#include "linphonecore_utils.h"
#include "lpconfig.h"
21
#include <belle-sip/dict.h>
22

23
#include <ldap.h>
24
#include <sasl/sasl.h>
25 26 27 28 29


#define MAX_RUNNING_REQUESTS 10
#define FILTER_MAX_SIZE      512

Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
30 31 32 33 34 35
typedef enum {
	ANONYMOUS,
	PLAIN,
	SASL
} LDAPAuthMethod;

36 37 38 39 40
struct LDAPFriendData {
	char* name;
	char* sip;
};

41 42
struct _LinphoneLDAPContactProvider
{
43
	LinphoneContactProvider base;
44 45
	LinphoneDictionary* config;

Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
46
	LDAP*   ld;
47
	MSList* requests;
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
48
	uint    req_count;
49

Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
50
	// bind transaction
51 52
	int bind_msgid;
	const char* auth_mechanism;
53
	bool_t connected;
54 55 56

	// config
	int use_tls;
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
57
	LDAPAuthMethod auth_method;
58 59 60
	const char*  username;
	const char*  password;
	const char*  server;
61

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

	char**       attributes;
68

69 70 71 72 73
	int    timeout;
	int    deref_aliases;
	int    max_results;
};

Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
74 75
struct _LinphoneLDAPContactSearch
{
76 77 78 79
	LinphoneContactSearch base;
	LDAP*   ld;
	int     msgid;
	char*   filter;
80 81
	bool_t  complete;
	MSList* found_entries;
82
	unsigned int found_count;
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
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);
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
94 95
	struct timeval timeout = { cp->timeout, 0 };

96
	linphone_contact_search_init(base, predicate, cb, cb_data);
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
97 98 99

	search->ld = cp->ld;

100 101 102
	search->filter = ms_malloc(FILTER_MAX_SIZE);
	snprintf(search->filter, FILTER_MAX_SIZE-1, cp->filter, predicate);
	search->filter[FILTER_MAX_SIZE-1] = 0;
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
103

104 105
	ms_message("Calling ldap_search_ext with predicate '%s' on base %s", search->filter, cp->base_object);

Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
106 107 108 109 110 111 112 113 114 115 116
	int ret = ldap_search_ext(search->ld,
					cp->base_object, // base from which to start
					LDAP_SCOPE_SUBTREE,
					search->filter, // search predicate
					cp->attributes, // which attributes to get
					0,              // 0 = get attrs AND value, 1 = get attrs only
					NULL,
					NULL,
					&timeout,       // server timeout for the search
					cp->max_results,// max result number
					&search->msgid );
117

Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
118 119 120 121
	if( ret != LDAP_SUCCESS ){
		ms_error("Error ldap_search_ext returned %d (%s)", ret, ldap_err2string(ret));
		belle_sip_object_unref(search);
		return NULL;
122 123
	} else {
		ms_message("LinphoneLDAPContactSearch created @%p : msgid %d", search, search->msgid);
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
124
	}
125 126 127
	return search;
}

128 129 130 131 132
void linphone_ldap_contact_search_destroy_friend( void* entry )
{
	linphone_friend_destroy((LinphoneFriend*)entry);
}

133 134 135 136 137 138
unsigned int linphone_ldap_contact_search_result_count(LinphoneLDAPContactSearch* obj)
{
	return obj->found_count;
}


139
static void linphone_ldap_contact_search_destroy( LinphoneLDAPContactSearch* obj )
140
{
141
	//ms_message("~LinphoneLDAPContactSearch(%p)", obj);
142 143
	ms_list_for_each(obj->found_entries, linphone_ldap_contact_search_destroy_friend);
	obj->found_entries = ms_list_free(obj->found_entries);
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
144
	if( obj->filter ) ms_free(obj->filter);
145 146 147 148
}

BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneLDAPContactSearch);
BELLE_SIP_INSTANCIATE_VPTR(LinphoneLDAPContactSearch,LinphoneContactSearch,
149
						   (belle_sip_object_destroy_t)linphone_ldap_contact_search_destroy,
150 151 152 153 154 155 156 157 158
						   NULL,
						   NULL,
						   TRUE
);


/* ***************************
 * LinphoneLDAPContactProvider
 * ***************************/
159 160

static inline LinphoneLDAPContactSearch* linphone_ldap_contact_provider_request_search( LinphoneLDAPContactProvider* obj, int msgid );
161 162
static unsigned int linphone_ldap_contact_provider_cancel_search(LinphoneContactProvider* obj, LinphoneContactSearch *req);
static void linphone_ldap_contact_provider_conf_destroy(LinphoneLDAPContactProvider* obj );
163
static bool_t linphone_ldap_contact_provider_iterate(void *data);
164 165
static int linphone_ldap_contact_provider_bind_interact(LDAP *ld, unsigned flags, void *defaults, void *sasl_interact);

166 167

/* Authentication methods */
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
struct AuthMethodDescription{
	LDAPAuthMethod method;
	const char* description;
};

static struct AuthMethodDescription ldap_auth_method_description[] = {
	{ANONYMOUS, "anonymous"},
	{PLAIN,     "plain"},
	{SASL,      "sasl"},
	{0,         NULL}
};

static LDAPAuthMethod linphone_ldap_contact_provider_auth_method( const char* description )
{
	struct AuthMethodDescription* desc = ldap_auth_method_description;
	while( desc && desc->description ){
		if( strcmp(description, desc->description) == 0)
			return desc->method;
		desc++;
	}
	return ANONYMOUS;
}
190

191
static void linphone_ldap_contact_provider_destroy_request_cb(void *req)
192 193 194 195
{
	belle_sip_object_unref(req);
}

196 197
static void linphone_ldap_contact_provider_destroy( LinphoneLDAPContactProvider* obj )
{
198
	//ms_message("linphone_ldap_contact_provider_destroy");
199 200
	linphone_core_remove_iterate_hook(LINPHONE_CONTACT_PROVIDER(obj)->lc, linphone_ldap_contact_provider_iterate,obj);

201
	// clean pending requests
202
	ms_list_for_each(obj->requests, linphone_ldap_contact_provider_destroy_request_cb);
203

204 205
	if (obj->ld) ldap_unbind_ext(obj->ld, NULL, NULL);
	obj->ld = NULL;
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
206

207 208
	if( obj->config ) linphone_dictionary_unref(obj->config);

209
	linphone_ldap_contact_provider_conf_destroy(obj);
210 211
}

212
static int linphone_ldap_contact_provider_parse_bind_results( LinphoneLDAPContactProvider* obj, LDAPMessage* results )
213
{
214 215 216 217
	int ret;
	if( obj->auth_method == ANONYMOUS ) {
		ms_message("ANONYMOUS BIND OK");
		ret = LDAP_SUCCESS;
218
	} else {
219
		ms_message("Advanced BIND follow-up");
220 221
		ret = ldap_sasl_interactive_bind(obj->ld,
										 NULL, // dn, should be NULL
222
										 "DIGEST-MD5", // TODO: use defined auth
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
										 NULL,NULL, // server and client controls
										 LDAP_SASL_QUIET, // never prompt, only use callback
										 linphone_ldap_contact_provider_bind_interact, // callback to call when info is needed
										 obj, // private data
										 results, // result, to pass later on when a ldap_result() comes
										 &obj->auth_mechanism,
										 &obj->bind_msgid );
		if( ret != LDAP_SUCCESS){
			ms_error("ldap_parse_sasl_bind_result failed(%d)", ret);
		}
	}

	if( ret == LDAP_SUCCESS ){
		obj->connected  = TRUE;
		obj->bind_msgid = 0;
238
	}
239

240
	return ret;
241

242 243
}

244
static int linphone_ldap_contact_provider_complete_contact( LinphoneLDAPContactProvider* obj, struct LDAPFriendData* lf, const char* attr_name, const char* attr_value)
245 246 247 248 249 250 251 252
{
	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
253 254 255 256
	if( lf->name && lf->sip )
		return 1;
	else
		return 0;
257 258 259

}

260
static void linphone_ldap_contact_provider_handle_search_result( LinphoneLDAPContactProvider* obj, LinphoneLDAPContactSearch* req, LDAPMessage* message )
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
261
{
262
	int msgtype = ldap_msgtype(message);
263

264 265
	switch(msgtype){

266 267 268 269 270 271 272 273 274
	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};
275
			bool_t contact_complete = FALSE;
276 277 278 279 280 281 282 283 284
			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 )
				{
285 286
					contact_complete = linphone_ldap_contact_provider_complete_contact(obj, &ldap_data, attr, (*it)->bv_val);
					if( contact_complete ) break;
287 288

					it++;
289 290
				}

291 292 293
				if( values ) ldap_value_free_len(values);
				ldap_memfree(attr);

294
				if( contact_complete ) break;
295

296
				attr = ldap_next_attribute(obj->ld, entry, ber);
297
			}
298

299
			if( contact_complete ) {
300 301 302 303 304 305
				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);
					req->found_entries = ms_list_append(req->found_entries, lf);
306
					req->found_count++;
307
					//ms_message("Added friend %s / %s", ldap_data.name, ldap_data.sip);
308 309
					ms_free(ldap_data.sip);
					ms_free(ldap_data.name);
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
310
					linphone_address_destroy(la);
311 312 313 314 315 316
				}
			}

			if( ber ) ber_free(ber, 0);

			entry = ldap_next_entry(obj->ld, entry);
317
		}
318 319 320 321 322 323 324 325 326 327 328
	}
	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;

329

330
	default: ms_message("[LDAP] Unhandled message type %x", msgtype); break;
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
331 332 333
	}
}

334 335 336
static bool_t linphone_ldap_contact_provider_iterate(void *data)
{
	LinphoneLDAPContactProvider* obj = LINPHONE_LDAP_CONTACT_PROVIDER(data);
337
	if( obj->ld && ((obj->req_count > 0) || (obj->bind_msgid != 0) )){
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
338

339 340 341 342
		// never block
		struct timeval timeout = {0,0};
		LDAPMessage* results = NULL;

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

345
		switch( ret ){
346 347
		case -1:
		{
348
			ms_warning("Error in ldap_result : returned -1 (req_count %d, bind_msgid %d): %s", obj->req_count, obj->bind_msgid, ldap_err2string(errno));
349 350 351
			break;
		}
		case 0: break; // nothing to do
352 353

		case LDAP_RES_BIND:
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
354 355 356 357
		{
			ms_message("iterate: LDAP_RES_BIND");
			if( ldap_msgid( results ) != obj->bind_msgid ) {
				ms_error("Bad msgid");
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
358
			} else {
359
				linphone_ldap_contact_provider_parse_bind_results( obj, results );
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
360 361 362
			}
			break;
		}
363
		case LDAP_RES_EXTENDED:
364 365
		case LDAP_RES_SEARCH_ENTRY:
		case LDAP_RES_SEARCH_REFERENCE:
366
		case LDAP_RES_INTERMEDIATE:
367
		case LDAP_RES_SEARCH_RESULT:
368 369
		{
			LDAPMessage* message = ldap_first_message(obj->ld, results);
370
			LinphoneLDAPContactSearch* req = linphone_ldap_contact_provider_request_search(obj, ldap_msgid(message));
371
			while( message != NULL ){
372
				linphone_ldap_contact_provider_handle_search_result(obj, req, message );
373 374
				message = ldap_next_message(obj->ld, message);
			}
375
			if( req && ret == LDAP_RES_SEARCH_RESULT) linphone_ldap_contact_provider_cancel_search(LINPHONE_CONTACT_PROVIDER(obj), LINPHONE_CONTACT_SEARCH(req));
376 377
			break;
		}
378 379 380 381 382 383
		case LDAP_RES_MODIFY:
		case LDAP_RES_ADD:
		case LDAP_RES_DELETE:
		case LDAP_RES_MODDN:
		case LDAP_RES_COMPARE:
		default:
384
			ms_message("Unhandled LDAP result %x", ret);
385 386
			break;
		}
387 388 389

		if( results )
			ldap_msgfree(results);
390 391 392 393
	}
	return TRUE;
}

394 395 396 397 398 399 400 401 402 403 404
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);
	}
}

405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
static char* required_config_keys[] = {
	// connection
	"server",
	"use_tls",
	"auth_method",
	"username",
	"password",

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

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

427
static bool_t linphone_ldap_contact_provider_valid_config(const LinphoneDictionary* dict)
428
{
429 430 431 432 433 434 435 436 437
	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;
438
		config_name++;
439
	}
440 441 442
	return valid;
}

443
static void linphone_ldap_contact_provider_loadconfig(LinphoneLDAPContactProvider* obj, const LinphoneDictionary* dict)
444 445 446
{
	char* attributes_list, *saveptr, *attr;
	unsigned int attr_count = 0, attr_idx = 0, i;
447

448
	if( !linphone_ldap_contact_provider_valid_config(dict) ) return;
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
449

450
	// free any pre-existing attributes values
451
	linphone_ldap_contact_provider_conf_destroy(obj);
452 453 454
	if( obj->config ) linphone_dictionary_unref(obj->config);

	// clone new config into the dictionary
455
	obj->config = linphone_dictionary_ref(linphone_dictionary_clone(dict));
456 457 458 459 460 461 462 463 464 465 466 467

	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);
	obj->username      = linphone_dictionary_get_string(obj->config, "username",       "");
	obj->password      = linphone_dictionary_get_string(obj->config, "password",       "");
	obj->base_object   = linphone_dictionary_get_string(obj->config, "base_object",    "dc=example,dc=com");
	obj->server        = linphone_dictionary_get_string(obj->config, "server",         "ldap://192.168.0.230:10389");
	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");
468

469 470 471
	/*
	 * Get authentication method
	 */
472
	obj->auth_method = linphone_ldap_contact_provider_auth_method(
473 474
				linphone_dictionary_get_string(obj->config, "auth_method", "anonymous")
				);
475

Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
476 477 478
	/*
	 * parse the attributes list
	 */
479 480 481 482 483
	attributes_list = ms_strdup(
				linphone_dictionary_get_string(obj->config,
											   "attributes",
											   "telephoneNumber,givenName,sn,mobile,homePhone")
				);
484 485

	// count attributes:
486
	for( i=0; attributes_list[i]; i++) {
487 488
		if( attributes_list[i] == ',') attr_count++;
	}
489

Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
490
	// 1 more for the first attr without ',', the other for the null-finished list
491
	obj->attributes = ms_malloc0((attr_count+2) * sizeof(char*));
492 493 494 495 496 497 498

	attr = strtok_r( attributes_list, ",", &saveptr );
	while( attr != NULL ){
		obj->attributes[attr_idx] = ms_strdup(attr);
		attr_idx++;
		attr = strtok_r(NULL, ",", &saveptr);
	}
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
499
	if( attr_idx != attr_count+1) ms_error("Invalid attribute number!!! %d expected, got %d", attr_count+1, attr_idx);
500

Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
501
	ms_free(attributes_list);
502 503
}

504 505 506 507
static int linphone_ldap_contact_provider_bind_interact(LDAP *ld,
														unsigned flags,
														void *defaults,
														void *sasl_interact)
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
508
{
509 510 511 512
	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);
513

514 515 516 517 518 519 520 521 522 523
	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:
			ms_message("* SASL_CB_GETREALM");
			dflt=NULL;
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
524
		break;
525 526 527 528 529 530 531 532
		case SASL_CB_USER:
		case SASL_CB_AUTHNAME:
			ms_message("* SASL_CB_AUTHNAME -> %s", obj->username);
			dflt=obj->username;
		break;
		case SASL_CB_PASS:
			ms_message("* SASL_CB_PASS -> %s", obj->password);
			dflt=obj->password;
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
533
		break;
534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
		default:
			ms_message("my_sasl_interact asked for unknown %lx\n",interact->id);
		}
		interact->result = (dflt && *dflt) ? dflt : (const char*)"";
		interact->len = strlen( (const char*)interact->result );

		interact++;
	}
	return LDAP_SUCCESS;
}

static int linphone_ldap_contact_provider_bind( LinphoneLDAPContactProvider* obj )
{
	int ret;
	const char* auth_mechanism = linphone_dictionary_get_string(obj->config, "auth_method", "anonymous");
	LDAPAuthMethod method = obj->auth_method;

	if( method == ANONYMOUS ){
		// for anonymous authentication, use a simple sasl_bind
		struct berval creds = {strlen(obj->password), ms_strdup(obj->password)};
		ret = ldap_sasl_bind(obj->ld, obj->base_object, NULL, &creds, NULL, NULL, &obj->bind_msgid);
		if(creds.bv_val) ms_free(creds.bv_val);
	} else {
		ret = ldap_sasl_interactive_bind(obj->ld,
										 NULL, // dn, should be NULL
										 "SIMPLE",//"DIGEST-MD5",
										 NULL,NULL, // server and client controls
										 LDAP_SASL_QUIET, // never prompt, only use callback
										 linphone_ldap_contact_provider_bind_interact, // callback to call when info is needed
										 obj, // private data
										 NULL, // result, to pass later on when a ldap_result() comes
										 &obj->auth_mechanism,
										 &obj->bind_msgid );
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
567
	}
568 569 570 571 572 573 574 575
	if( ret == LDAP_SUCCESS || ret == LDAP_SASL_BIND_IN_PROGRESS ) {
		if( ret == LDAP_SASL_BIND_IN_PROGRESS) ms_message("BIND_IN_PROGRESS");
		ms_message("LDAP bind request sent, auth: %s, msgid %x", obj->auth_mechanism?obj->auth_mechanism:"-", obj->bind_msgid);
	} else {
		int err;
		ldap_get_option(obj->ld, LDAP_OPT_RESULT_CODE, &err);
		ms_error("ldap_sasl_bind error returned %d, err %d (%s), auth_method: %s",
				 ret, err, ldap_err2string(err), auth_mechanism );
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
576
	}
577

Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
578 579
	return 0;
}
580

581 582 583 584 585
unsigned int linphone_ldap_contact_provider_get_max_result(const LinphoneLDAPContactProvider* obj)
{
	return obj->max_results;
}

586 587 588 589 590
static void linphone_ldap_contact_provider_config_dump_cb(const char*key, void* value, void* userdata)
{
	ms_message("- %s -> %s", key, (const char* )value);
}

591
LinphoneLDAPContactProvider*linphone_ldap_contact_provider_create(LinphoneCore* lc, const LinphoneDictionary* config)
592 593 594
{
	LinphoneLDAPContactProvider* obj = belle_sip_object_new(LinphoneLDAPContactProvider);
	int proto_version = LDAP_VERSION3;
595 596

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

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

600 601
	if( !linphone_ldap_contact_provider_valid_config(config) ) {
		ms_error( "Invalid configuration for LDAP, aborting creation");
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
602
		belle_sip_object_unref(obj);
603
		obj = NULL;
604
	} else {
605
		linphone_dictionary_foreach( config, linphone_ldap_contact_provider_config_dump_cb, 0 );
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621
		linphone_ldap_contact_provider_loadconfig(obj, config);

		int ret = ldap_initialize(&(obj->ld),obj->server);

		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 {
			// 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);
		}
622 623 624 625
	}
	return obj;
}

626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
/**
 * 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);
}

648
static inline LinphoneLDAPContactSearch* linphone_ldap_contact_provider_request_search( LinphoneLDAPContactProvider* obj, int msgid )
649
{
650 651
	LinphoneLDAPContactSearch dummy = {};
	dummy.msgid = msgid;
652 653 654 655 656 657

	MSList* list_entry = ms_list_find_custom(obj->requests, linphone_ldap_request_entry_compare_weak, &dummy);
	if( list_entry ) return list_entry->data;
	else return NULL;
}

658
static unsigned int linphone_ldap_contact_provider_cancel_search(LinphoneContactProvider* obj, LinphoneContactSearch *req)
659 660 661 662 663 664 665
{
	LinphoneLDAPContactSearch*  ldap_req = LINPHONE_LDAP_CONTACT_SEARCH(req);
	LinphoneLDAPContactProvider* ldap_cp = LINPHONE_LDAP_CONTACT_PROVIDER(obj);
	int ret = 1;

	MSList* list_entry = ms_list_find_custom(ldap_cp->requests, linphone_ldap_request_entry_compare_strong, req);
	if( list_entry ) {
666
		ldap_cp->requests = ms_list_remove_link(ldap_cp->requests, list_entry);
667 668 669 670 671 672 673 674 675
		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;
}

676
static LinphoneLDAPContactSearch* linphone_ldap_contact_provider_begin_search ( LinphoneLDAPContactProvider* obj,
677 678 679 680
		const char* predicate,
		ContactSearchCallback cb,
		void* cb_data )
{
681 682 683
	// if we're not yet connected, bind
	if( !obj->connected ) linphone_ldap_contact_provider_bind(obj);

684 685 686 687 688 689 690 691 692 693 694 695
	LinphoneLDAPContactSearch* request = linphone_ldap_contact_search_create ( obj, predicate, cb, cb_data );

	if ( request != NULL ) {
		ms_message ( "Created search %d for '%s', msgid %d, @%p", obj->req_count, predicate, request->msgid, request );

		obj->requests  = ms_list_append ( obj->requests, request );
		obj->req_count++;
	}
	return request;
}


696
static int linphone_ldap_contact_provider_marshal(LinphoneLDAPContactProvider* obj, char* buff, size_t buff_size, size_t *offset)
697 698 699 700 701 702 703 704 705 706 707 708 709 710
{
	belle_sip_error_code error = BELLE_SIP_OK;

	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, "bind_msgid:%d,\n", obj->bind_msgid);
	if(error!= BELLE_SIP_OK) return error;

	error = belle_sip_snprintf(buff, buff_size, offset,
							   "CONFIG:\n"
711 712 713 714 715 716 717
							   "tls: %d \n"
							   "auth: %d \n"
							   "user: %s \n"
							   "pass: %s \n"
							   "server: %s \n"
							   "base: %s \n"
							   "filter: %s \n"
718
							   "timeout: %d \n"
719
							   "deref: %d \n"
720
							   "max_res: %d \n"
721 722 723
							   "sip_attr:%s \n"
							   "name_attr:%s \n"
							   "attrs:\n",
724 725 726 727
							   obj->use_tls, obj->auth_method,
							   obj->username, obj->password, obj->server,
							   obj->base_object, obj->filter,
							   obj->timeout, obj->deref_aliases,
728 729
							   obj->max_results,
							   obj->sip_attr, obj->name_attr);
730 731 732 733
	if(error!= BELLE_SIP_OK) return error;

	char **attr = obj->attributes;
	while( *attr ){
734
		error = belle_sip_snprintf(buff, buff_size, offset, "- %s\n", *attr);
735 736 737 738 739 740 741 742
		if(error!= BELLE_SIP_OK) return error;
		else attr++;
	}

	return error;

}

Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
743 744 745 746 747 748 749 750 751 752

BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneLDAPContactProvider);

BELLE_SIP_INSTANCIATE_CUSTOM_VPTR(LinphoneLDAPContactProvider)=
{
	{
		{
			BELLE_SIP_VPTR_INIT(LinphoneLDAPContactProvider,LinphoneContactProvider,TRUE),
			(belle_sip_object_destroy_t)linphone_ldap_contact_provider_destroy,
			NULL,
753
			(belle_sip_object_marshal_t)linphone_ldap_contact_provider_marshal
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
754 755
		},
		"LDAP",
756
		(LinphoneContactProviderStartSearchMethod)linphone_ldap_contact_provider_begin_search,
757
		(LinphoneContactProviderCancelSearchMethod)linphone_ldap_contact_provider_cancel_search
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
758 759
	}
};
760