forked from mirrors/linphone-iphone
830 lines
25 KiB
C
830 lines
25 KiB
C
/*
|
|
* 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 "linphone/ldapprovider.h"
|
|
#include "private.h"
|
|
#include "linphone/lpconfig.h"
|
|
#include "contact_providers_priv.h"
|
|
#include "mediastreamer2/mscommon.h"
|
|
#include <belle-sip/dict.h>
|
|
|
|
#ifdef BUILD_LDAP
|
|
#include <ldap.h>
|
|
#include <sasl/sasl.h>
|
|
|
|
|
|
#define MAX_RUNNING_REQUESTS 10
|
|
#define FILTER_MAX_SIZE 512
|
|
|
|
struct LDAPFriendData {
|
|
char* name;
|
|
char* sip;
|
|
};
|
|
|
|
struct _LinphoneLDAPContactProvider
|
|
{
|
|
LinphoneContactProvider base;
|
|
LinphoneDictionary* config;
|
|
|
|
LDAP* ld;
|
|
bctbx_list_t* requests;
|
|
unsigned int req_count;
|
|
|
|
// bind transaction
|
|
bool_t connected;
|
|
ms_thread_t bind_thread;
|
|
|
|
// config
|
|
int use_tls;
|
|
const char* auth_method;
|
|
const char* username;
|
|
const char* password;
|
|
const char* server;
|
|
const char* bind_dn;
|
|
|
|
const char* sasl_authname;
|
|
const char* sasl_realm;
|
|
|
|
const char* base_object;
|
|
const char* sip_attr;
|
|
const char* name_attr;
|
|
const char* filter;
|
|
|
|
char** attributes;
|
|
|
|
int timeout;
|
|
int deref_aliases;
|
|
int max_results;
|
|
|
|
};
|
|
|
|
struct _LinphoneLDAPContactSearch
|
|
{
|
|
LinphoneContactSearch base;
|
|
LDAP* ld;
|
|
int msgid;
|
|
char* filter;
|
|
bool_t complete;
|
|
bctbx_list_t* found_entries;
|
|
unsigned int found_count;
|
|
};
|
|
|
|
|
|
/* *************************
|
|
* LinphoneLDAPContactSearch
|
|
* *************************/
|
|
|
|
LinphoneLDAPContactSearch* linphone_ldap_contact_search_create(LinphoneLDAPContactProvider* cp, const char* predicate, ContactSearchCallback cb, void* cb_data)
|
|
{
|
|
LinphoneLDAPContactSearch* search = belle_sip_object_new(LinphoneLDAPContactSearch);
|
|
LinphoneContactSearch* base = LINPHONE_CONTACT_SEARCH(search);
|
|
|
|
linphone_contact_search_init(base, predicate, cb, cb_data);
|
|
|
|
search->ld = cp->ld;
|
|
|
|
search->filter = ms_malloc(FILTER_MAX_SIZE);
|
|
snprintf(search->filter, FILTER_MAX_SIZE-1, cp->filter, predicate);
|
|
search->filter[FILTER_MAX_SIZE-1] = 0;
|
|
|
|
return search;
|
|
}
|
|
|
|
void linphone_ldap_contact_search_destroy_friend( void* entry )
|
|
{
|
|
linphone_friend_destroy((LinphoneFriend*)entry);
|
|
}
|
|
|
|
unsigned int linphone_ldap_contact_search_result_count(LinphoneLDAPContactSearch* obj)
|
|
{
|
|
return obj->found_count;
|
|
}
|
|
|
|
static void linphone_ldap_contact_search_destroy( LinphoneLDAPContactSearch* obj )
|
|
{
|
|
//ms_message("~LinphoneLDAPContactSearch(%p)", obj);
|
|
bctbx_list_for_each(obj->found_entries, linphone_ldap_contact_search_destroy_friend);
|
|
obj->found_entries = bctbx_list_free(obj->found_entries);
|
|
if( obj->filter ) ms_free(obj->filter);
|
|
}
|
|
|
|
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneLDAPContactSearch);
|
|
BELLE_SIP_INSTANCIATE_VPTR(LinphoneLDAPContactSearch,LinphoneContactSearch,
|
|
(belle_sip_object_destroy_t)linphone_ldap_contact_search_destroy,
|
|
NULL,
|
|
NULL,
|
|
TRUE
|
|
);
|
|
|
|
|
|
/* ***************************
|
|
* LinphoneLDAPContactProvider
|
|
* ***************************/
|
|
|
|
static inline LinphoneLDAPContactSearch* linphone_ldap_contact_provider_request_search( LinphoneLDAPContactProvider* obj, int msgid );
|
|
static unsigned int linphone_ldap_contact_provider_cancel_search(LinphoneContactProvider* obj, LinphoneContactSearch *req);
|
|
static void linphone_ldap_contact_provider_conf_destroy(LinphoneLDAPContactProvider* obj );
|
|
static bool_t linphone_ldap_contact_provider_iterate(void *data);
|
|
static int linphone_ldap_contact_provider_bind_interact(LDAP *ld, unsigned flags, void *defaults, void *sasl_interact);
|
|
static int linphone_ldap_contact_provider_perform_search( LinphoneLDAPContactProvider* obj, LinphoneLDAPContactSearch* req);
|
|
|
|
static void linphone_ldap_contact_provider_destroy_request_cb(void *req)
|
|
{
|
|
belle_sip_object_unref(req);
|
|
}
|
|
|
|
static void linphone_ldap_contact_provider_destroy( LinphoneLDAPContactProvider* obj )
|
|
{
|
|
//ms_message("linphone_ldap_contact_provider_destroy");
|
|
linphone_core_remove_iterate_hook(LINPHONE_CONTACT_PROVIDER(obj)->lc, linphone_ldap_contact_provider_iterate,obj);
|
|
|
|
// clean pending requests
|
|
bctbx_list_for_each(obj->requests, linphone_ldap_contact_provider_destroy_request_cb);
|
|
|
|
if (obj->ld) ldap_unbind_ext(obj->ld, NULL, NULL);
|
|
obj->ld = NULL;
|
|
|
|
if( obj->config ) linphone_dictionary_unref(obj->config);
|
|
|
|
linphone_ldap_contact_provider_conf_destroy(obj);
|
|
}
|
|
|
|
static int linphone_ldap_contact_provider_complete_contact( LinphoneLDAPContactProvider* obj, struct LDAPFriendData* lf, const char* attr_name, const char* attr_value)
|
|
{
|
|
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
|
|
if( lf->name && lf->sip )
|
|
return 1;
|
|
else
|
|
return 0;
|
|
|
|
}
|
|
|
|
static void linphone_ldap_contact_provider_handle_search_result( LinphoneLDAPContactProvider* obj, LinphoneLDAPContactSearch* req, LDAPMessage* message )
|
|
{
|
|
int msgtype = ldap_msgtype(message);
|
|
|
|
switch(msgtype){
|
|
|
|
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};
|
|
bool_t contact_complete = FALSE;
|
|
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 )
|
|
{
|
|
contact_complete = linphone_ldap_contact_provider_complete_contact(obj, &ldap_data, attr, (*it)->bv_val);
|
|
if( contact_complete ) break;
|
|
|
|
it++;
|
|
}
|
|
|
|
if( values ) ldap_value_free_len(values);
|
|
ldap_memfree(attr);
|
|
|
|
if( contact_complete ) break;
|
|
|
|
attr = ldap_next_attribute(obj->ld, entry, ber);
|
|
}
|
|
|
|
if( contact_complete ) {
|
|
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 = bctbx_list_append(req->found_entries, lf);
|
|
req->found_count++;
|
|
//ms_message("Added friend %s / %s", ldap_data.name, ldap_data.sip);
|
|
ms_free(ldap_data.sip);
|
|
ms_free(ldap_data.name);
|
|
linphone_address_unref(la);
|
|
}
|
|
}
|
|
|
|
if( ber ) ber_free(ber, 0);
|
|
|
|
entry = ldap_next_entry(obj->ld, entry);
|
|
}
|
|
}
|
|
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;
|
|
|
|
|
|
default: ms_message("[LDAP] Unhandled message type %x", msgtype); break;
|
|
}
|
|
}
|
|
|
|
static bool_t linphone_ldap_contact_provider_iterate(void *data)
|
|
{
|
|
LinphoneLDAPContactProvider* obj = LINPHONE_LDAP_CONTACT_PROVIDER(data);
|
|
if( obj->ld && obj->connected && (obj->req_count > 0) ){
|
|
|
|
// never block
|
|
struct timeval timeout = {0,0};
|
|
LDAPMessage* results = NULL;
|
|
|
|
int ret = ldap_result(obj->ld, LDAP_RES_ANY, LDAP_MSG_ONE, &timeout, &results);
|
|
|
|
switch( ret ){
|
|
case -1:
|
|
{
|
|
ms_warning("Error in ldap_result : returned -1 (req_count %d): %s", obj->req_count, ldap_err2string(errno));
|
|
break;
|
|
}
|
|
case 0: break; // nothing to do
|
|
|
|
case LDAP_RES_BIND:
|
|
{
|
|
ms_error("iterate: unexpected LDAP_RES_BIND");
|
|
break;
|
|
}
|
|
case LDAP_RES_EXTENDED:
|
|
case LDAP_RES_SEARCH_ENTRY:
|
|
case LDAP_RES_SEARCH_REFERENCE:
|
|
case LDAP_RES_INTERMEDIATE:
|
|
case LDAP_RES_SEARCH_RESULT:
|
|
{
|
|
LDAPMessage* message = ldap_first_message(obj->ld, results);
|
|
LinphoneLDAPContactSearch* req = linphone_ldap_contact_provider_request_search(obj, ldap_msgid(message));
|
|
while( message != NULL ){
|
|
linphone_ldap_contact_provider_handle_search_result(obj, req, message );
|
|
message = ldap_next_message(obj->ld, message);
|
|
}
|
|
if( req && ret == LDAP_RES_SEARCH_RESULT)
|
|
linphone_ldap_contact_provider_cancel_search(
|
|
LINPHONE_CONTACT_PROVIDER(obj),
|
|
LINPHONE_CONTACT_SEARCH(req));
|
|
break;
|
|
}
|
|
case LDAP_RES_MODIFY:
|
|
case LDAP_RES_ADD:
|
|
case LDAP_RES_DELETE:
|
|
case LDAP_RES_MODDN:
|
|
case LDAP_RES_COMPARE:
|
|
default:
|
|
ms_message("Unhandled LDAP result %x", ret);
|
|
break;
|
|
}
|
|
|
|
if( results )
|
|
ldap_msgfree(results);
|
|
}
|
|
|
|
if( obj->ld && obj->connected ){
|
|
// check for pending searches
|
|
unsigned int i;
|
|
|
|
for( i=0; i<obj->req_count; i++){
|
|
LinphoneLDAPContactSearch* search = (LinphoneLDAPContactSearch*)bctbx_list_nth_data( obj->requests, i );
|
|
if( search && search->msgid == 0){
|
|
int ret;
|
|
ms_message("Found pending search %p (for %s), launching...", search, search->filter);
|
|
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));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
static char* required_config_keys[] = {
|
|
// connection
|
|
"server",
|
|
"use_tls",
|
|
"auth_method",
|
|
"username",
|
|
"password",
|
|
"bind_dn",
|
|
"sasl_authname",
|
|
"sasl_realm",
|
|
|
|
// search
|
|
"base_object",
|
|
"filter",
|
|
"name_attribute",
|
|
"sip_attribute",
|
|
"attributes",
|
|
|
|
// misc
|
|
"timeout",
|
|
"max_results",
|
|
"deref_aliases",
|
|
NULL
|
|
};
|
|
|
|
static bool_t linphone_ldap_contact_provider_valid_config(const LinphoneDictionary* dict)
|
|
{
|
|
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;
|
|
config_name++;
|
|
}
|
|
return valid;
|
|
}
|
|
|
|
static void linphone_ldap_contact_provider_loadconfig(LinphoneLDAPContactProvider* obj, const LinphoneDictionary* dict)
|
|
{
|
|
char* attributes_list, *saveptr, *attr;
|
|
unsigned int attr_count = 0, attr_idx = 0, i;
|
|
|
|
if( !linphone_ldap_contact_provider_valid_config(dict) ) return;
|
|
|
|
// free any pre-existing attributes values
|
|
linphone_ldap_contact_provider_conf_destroy(obj);
|
|
if( obj->config ) linphone_dictionary_unref(obj->config);
|
|
|
|
// clone new config into the dictionary
|
|
obj->config = linphone_dictionary_ref(linphone_dictionary_clone(dict));
|
|
|
|
#if 0 // until sasl auth is set up, force anonymous auth.
|
|
linphone_dictionary_set_string(obj->config, "auth_method", "ANONYMOUS");
|
|
#endif
|
|
|
|
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->auth_method = linphone_dictionary_get_string(obj->config, "auth_method", "ANONYMOUS");
|
|
obj->username = linphone_dictionary_get_string(obj->config, "username", "");
|
|
obj->password = linphone_dictionary_get_string(obj->config, "password", "");
|
|
obj->bind_dn = linphone_dictionary_get_string(obj->config, "bind_dn", "");
|
|
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://localhost");
|
|
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");
|
|
obj->sasl_authname = linphone_dictionary_get_string(obj->config, "sasl_authname", "");
|
|
obj->sasl_realm = linphone_dictionary_get_string(obj->config, "sasl_realm", "");
|
|
|
|
/*
|
|
* parse the attributes list
|
|
*/
|
|
attributes_list = ms_strdup(
|
|
linphone_dictionary_get_string(obj->config,
|
|
"attributes",
|
|
"telephoneNumber,givenName,sn,mobile,homePhone")
|
|
);
|
|
|
|
// count attributes:
|
|
for( i=0; attributes_list[i]; i++) {
|
|
if( attributes_list[i] == ',') attr_count++;
|
|
}
|
|
|
|
// 1 more for the first attr without ',', the other for the null-finished list
|
|
obj->attributes = ms_malloc0((attr_count+2) * sizeof(char*));
|
|
|
|
attr = strtok_r( attributes_list, ",", &saveptr );
|
|
while( attr != NULL ){
|
|
obj->attributes[attr_idx] = ms_strdup(attr);
|
|
attr_idx++;
|
|
attr = strtok_r(NULL, ",", &saveptr);
|
|
}
|
|
if( attr_idx != attr_count+1) ms_error("Invalid attribute number!!! %d expected, got %d", attr_count+1, attr_idx);
|
|
|
|
ms_free(attributes_list);
|
|
}
|
|
|
|
static int linphone_ldap_contact_provider_bind_interact(LDAP *ld,
|
|
unsigned flags,
|
|
void *defaults,
|
|
void *sasl_interact)
|
|
{
|
|
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);
|
|
|
|
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 -> %s", obj->sasl_realm);
|
|
dflt = obj->sasl_realm;
|
|
break;
|
|
case SASL_CB_AUTHNAME:
|
|
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;
|
|
break;
|
|
case SASL_CB_PASS:
|
|
ms_message("* SASL_CB_PASS (hidden)");
|
|
dflt = obj->password;
|
|
break;
|
|
default:
|
|
ms_message("SASL interact asked for unknown id %lx\n",interact->id);
|
|
}
|
|
interact->result = (dflt && *dflt) ? dflt : (const char*)"";
|
|
interact->len = strlen( (const char*)interact->result );
|
|
|
|
interact++;
|
|
}
|
|
return LDAP_SUCCESS;
|
|
}
|
|
|
|
static void* ldap_bind_thread_func( void*arg)
|
|
{
|
|
LinphoneLDAPContactProvider* obj = linphone_ldap_contact_provider_ref(arg);
|
|
const char* auth_mechanism = obj->auth_method;
|
|
int ret;
|
|
|
|
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);
|
|
}
|
|
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;
|
|
} else {
|
|
int err;
|
|
ldap_get_option(obj->ld, LDAP_OPT_RESULT_CODE, &err);
|
|
ms_error("ldap_sasl_bind error returned %x, err %x (%s), auth_method: %s",
|
|
ret, err, ldap_err2string(err), auth_mechanism );
|
|
}
|
|
|
|
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);
|
|
return 0;
|
|
}
|
|
|
|
unsigned int linphone_ldap_contact_provider_get_max_result(const LinphoneLDAPContactProvider* obj)
|
|
{
|
|
return obj->max_results;
|
|
}
|
|
|
|
static void linphone_ldap_contact_provider_config_dump_cb(const char*key, void* value, void* userdata)
|
|
{
|
|
ms_message("- %s -> %s", key, (const char* )value);
|
|
}
|
|
|
|
LinphoneLDAPContactProvider*linphone_ldap_contact_provider_create(LinphoneCore* lc, const LinphoneDictionary* config)
|
|
{
|
|
LinphoneLDAPContactProvider* obj = belle_sip_object_new(LinphoneLDAPContactProvider);
|
|
int proto_version = LDAP_VERSION3;
|
|
|
|
linphone_contact_provider_init((LinphoneContactProvider*)obj, lc);
|
|
|
|
ms_message( "Constructed Contact provider '%s'", BELLE_SIP_OBJECT_VPTR(obj,LinphoneContactProvider)->name);
|
|
|
|
if( !linphone_ldap_contact_provider_valid_config(config) ) {
|
|
ms_error( "Invalid configuration for LDAP, aborting creation");
|
|
belle_sip_object_unref(obj);
|
|
obj = NULL;
|
|
} else {
|
|
int ret;
|
|
linphone_dictionary_foreach( config, linphone_ldap_contact_provider_config_dump_cb, 0 );
|
|
linphone_ldap_contact_provider_loadconfig(obj, config);
|
|
|
|
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 {
|
|
// 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
|
|
//ldap_set_option( obj->ld, LDAP_OPT_CONNECT_ASYNC, LDAP_OPT_ON);
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
static inline LinphoneLDAPContactSearch* linphone_ldap_contact_provider_request_search( LinphoneLDAPContactProvider* obj, int msgid )
|
|
{
|
|
LinphoneLDAPContactSearch dummy = {};
|
|
bctbx_list_t* list_entry;
|
|
dummy.msgid = msgid;
|
|
|
|
list_entry = bctbx_list_find_custom(obj->requests, linphone_ldap_request_entry_compare_weak, &dummy);
|
|
if( list_entry ) return list_entry->data;
|
|
else return NULL;
|
|
}
|
|
|
|
static unsigned int linphone_ldap_contact_provider_cancel_search(LinphoneContactProvider* obj, LinphoneContactSearch *req)
|
|
{
|
|
LinphoneLDAPContactSearch* ldap_req = LINPHONE_LDAP_CONTACT_SEARCH(req);
|
|
LinphoneLDAPContactProvider* ldap_cp = LINPHONE_LDAP_CONTACT_PROVIDER(obj);
|
|
int ret = 1;
|
|
|
|
bctbx_list_t* list_entry = bctbx_list_find_custom(ldap_cp->requests, linphone_ldap_request_entry_compare_strong, req);
|
|
if( list_entry ) {
|
|
ms_message("Delete search %p", req);
|
|
ldap_cp->requests = bctbx_list_erase_link(ldap_cp->requests, list_entry);
|
|
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;
|
|
}
|
|
|
|
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 ){
|
|
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 );
|
|
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;
|
|
}
|
|
|
|
static LinphoneLDAPContactSearch* linphone_ldap_contact_provider_begin_search ( LinphoneLDAPContactProvider* obj,
|
|
const char* predicate,
|
|
ContactSearchCallback cb,
|
|
void* cb_data )
|
|
{
|
|
bool_t connected = obj->connected;
|
|
LinphoneLDAPContactSearch* request;
|
|
|
|
// if we're not yet connected, bind
|
|
if( !connected ) {
|
|
if( !obj->bind_thread ) linphone_ldap_contact_provider_bind(obj);
|
|
}
|
|
|
|
request = linphone_ldap_contact_search_create( obj, predicate, cb, cb_data );
|
|
|
|
if( connected ){
|
|
int ret = linphone_ldap_contact_provider_perform_search(obj, request);
|
|
ms_message ( "Created search %d for '%s', msgid %d, @%p", obj->req_count, predicate, request->msgid, request );
|
|
if( ret != LDAP_SUCCESS ){
|
|
belle_sip_object_unref(request);
|
|
request = NULL;
|
|
}
|
|
} else {
|
|
ms_message("Delayed search, wait for connection");
|
|
}
|
|
|
|
if( request != NULL ) {
|
|
obj->requests = bctbx_list_append ( obj->requests, request );
|
|
obj->req_count++;
|
|
}
|
|
|
|
return request;
|
|
}
|
|
|
|
|
|
static int linphone_ldap_contact_provider_marshal(LinphoneLDAPContactProvider* obj, char* buff, size_t buff_size, size_t *offset)
|
|
{
|
|
belle_sip_error_code error = BELLE_SIP_OK;
|
|
char **attr;
|
|
|
|
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"
|
|
"tls: %d \n"
|
|
"auth: %s \n"
|
|
"user: %s \n"
|
|
"pass: %s \n"
|
|
"server: %s \n"
|
|
"base: %s \n"
|
|
"filter: %s \n"
|
|
"timeout: %d \n"
|
|
"deref: %d \n"
|
|
"max_res: %d \n"
|
|
"sip_attr:%s \n"
|
|
"name_attr:%s \n"
|
|
"attrs:\n",
|
|
obj->use_tls, obj->auth_method,
|
|
obj->username, obj->password, obj->server,
|
|
obj->base_object, obj->filter,
|
|
obj->timeout, obj->deref_aliases,
|
|
obj->max_results,
|
|
obj->sip_attr, obj->name_attr);
|
|
if(error!= BELLE_SIP_OK) return error;
|
|
|
|
attr = obj->attributes;
|
|
while( *attr ){
|
|
error = belle_sip_snprintf(buff, buff_size, offset, "- %s\n", *attr);
|
|
if(error!= BELLE_SIP_OK) return error;
|
|
else attr++;
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
int linphone_ldap_contact_provider_available()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneLDAPContactProvider);
|
|
|
|
BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(LinphoneLDAPContactProvider)
|
|
{
|
|
{
|
|
BELLE_SIP_VPTR_INIT(LinphoneLDAPContactProvider,LinphoneContactProvider,TRUE),
|
|
(belle_sip_object_destroy_t)linphone_ldap_contact_provider_destroy,
|
|
NULL,
|
|
(belle_sip_object_marshal_t)linphone_ldap_contact_provider_marshal
|
|
},
|
|
"LDAP",
|
|
(LinphoneContactProviderStartSearchMethod)linphone_ldap_contact_provider_begin_search,
|
|
(LinphoneContactProviderCancelSearchMethod)linphone_ldap_contact_provider_cancel_search
|
|
}
|
|
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; }
|
|
|
|
int linphone_ldap_contact_provider_available(){ return 0; }
|
|
|
|
|
|
#endif /* BUILD_LDAP */
|
|
|