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