Progress commit on LDAP:

- we now create minphone contacts based on what the LDAP query returns,
- added configuration for specifying which LDAP field corresponds to the SIP address and contact name
- fixed some leaks in LDAP data extraction
- modified the completion of the uribar to include LDAP results. We now insert them into the list when available.
This commit is contained in:
Guillaume BIENKOWSKI 2013-12-03 10:52:00 +01:00
parent c17165ac9d
commit 8aa375d637
4 changed files with 231 additions and 113 deletions

View file

@ -15,8 +15,10 @@
*/
#include "ldapprovider.h"
#include "linphonecore.h"
#include "linphonecore_utils.h"
#include "lpconfig.h"
#include <ldap.h>
@ -29,6 +31,11 @@ typedef enum {
SASL
} LDAPAuthMethod;
struct LDAPFriendData {
char* name;
char* sip;
};
struct _LinphoneLDAPContactProvider
{
LinphoneContactProvider base;
@ -38,7 +45,7 @@ struct _LinphoneLDAPContactProvider
// bind transaction
uint bind_msgid;
struct berval *servercreds;
bool_t connected;
// config
int use_tls;
@ -49,6 +56,9 @@ struct _LinphoneLDAPContactProvider
char* base_object;
char** attributes;
char* sip_attr;
char* name_attr;
char* filter;
int timeout;
int deref_aliases;
@ -61,7 +71,8 @@ struct _LinphoneLDAPContactSearch
LDAP* ld;
int msgid;
char* filter;
// MSList* found_entries;
bool_t complete;
MSList* found_entries;
};
@ -107,7 +118,7 @@ LinphoneLDAPContactSearch*linphone_ldap_contact_search_create(LinphoneLDAPContac
return search;
}
static void linphone_ldap_contact_destroy( LinphoneLDAPContactSearch* obj )
static void linphone_ldap_contact_search_destroy( LinphoneLDAPContactSearch* obj )
{
ms_message("~LinphoneLDAPContactSearch(%p)", obj);
if( obj->filter ) ms_free(obj->filter);
@ -115,7 +126,7 @@ static void linphone_ldap_contact_destroy( LinphoneLDAPContactSearch* obj )
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneLDAPContactSearch);
BELLE_SIP_INSTANCIATE_VPTR(LinphoneLDAPContactSearch,LinphoneContactSearch,
(belle_sip_object_destroy_t)linphone_ldap_contact_destroy,
(belle_sip_object_destroy_t)linphone_ldap_contact_search_destroy,
NULL,
NULL,
TRUE
@ -136,8 +147,10 @@ static struct AuthMethodDescription ldap_auth_method_description[] = {
{SASL, "sasl"},
{0, NULL}
};
static unsigned int linphone_ldap_cancel_search_from_msgid( LinphoneLDAPContactProvider* obj, int msgid );
static inline LinphoneLDAPContactSearch* linphone_ldap_request_entry_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 LDAPAuthMethod linphone_ldap_contact_provider_auth_method( const char* description )
{
@ -155,22 +168,6 @@ static void linphone_ldap_contact_provider_destroy_request(void *req, void *dumm
belle_sip_object_unref(req);
}
static void linphone_ldap_contact_provider_conf_destroy(LinphoneLDAPContactProvider* obj )
{
if(obj->username) ms_free(obj->username);
if(obj->password) ms_free(obj->password);
if(obj->base_object) ms_free(obj->base_object);
if(obj->filter) ms_free(obj->filter);
if(obj->attributes){
int i=0;
for( ; obj->attributes[i]; i++){
ms_free(obj->attributes[i]);
}
ms_free(obj->attributes);
}
}
static void linphone_ldap_contact_provider_destroy( LinphoneLDAPContactProvider* obj )
{
// clean pending requests
@ -188,52 +185,107 @@ static int linphone_ldap_parse_bind_results( LinphoneLDAPContactProvider* obj, L
int ret = ldap_parse_sasl_bind_result(obj->ld, results, NULL, 0);
if( ret != LDAP_SUCCESS ){
ms_error("ldap_parse_sasl_bind_result failed");
} else {
obj->connected = TRUE;
}
return ret;
}
static void linphone_ldap_handle_search_result( LinphoneLDAPContactProvider* cp, LinphoneLDAPContactSearch* req, LDAPMessage* message )
static int linphone_ldap_complete_contact( LinphoneLDAPContactProvider* obj, struct LDAPFriendData* lf, const char* attr_name, const char* attr_value)
{
if( strcmp(attr_name, obj->name_attr ) == 0 ){
ms_message("Got name attr: %s", attr_value);
lf->name = ms_strdup(attr_value);
} else if( strcmp(attr_name, obj->sip_attr) == 0 ) {
ms_message("Got sip attr: %s", attr_value);
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_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(cp->ld, message);
while( entry != NULL ){
BerElement* ber = NULL;
char* dn = ldap_get_dn(cp->ld, entry);
char* attr = ldap_first_attribute(cp->ld, entry, &ber);
if( dn ){
ms_message("search result: dn: %s", dn);
ldap_memfree(dn);
}
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( attr ){
struct berval** values = ldap_get_values_len(cp->ld, entry, attr);
struct berval** v = values;
while( *v && (*v)->bv_val && (*v)->bv_len )
{
ms_message("%s -> %s", attr, (*v)->bv_val);
v++;
}
while( entry != NULL ){
if( values ) ldap_value_free_len(values);
struct LDAPFriendData ldap_data = {0};
bool_t all_found = FALSE;
BerElement* ber = NULL;
char* attr = ldap_first_attribute(obj->ld, entry, &ber);
char* dn = ldap_get_dn(obj->ld, entry);
ldap_memfree(attr);
attr = ldap_next_attribute(cp->ld, entry, ber);
}
if( ber ) ber_free(ber, 0);
entry = ldap_next_entry(cp->ld, entry);
if( dn ){
ms_message("search result: dn: %s", dn);
ldap_memfree(dn);
}
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 )
{
ms_message("%s -> %s", attr, (*it)->bv_val);
all_found = linphone_ldap_complete_contact(obj, &ldap_data, attr, (*it)->bv_val);
if( all_found ) break;
it++;
}
if( values ) ldap_value_free_len(values);
ldap_memfree(attr);
if( all_found ) break;
attr = ldap_next_attribute(obj->ld, entry, ber);
}
if( all_found ) {
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);
ms_message("Added friend %s / %s", ldap_data.name, ldap_data.sip);
ms_free(ldap_data.sip);
ms_free(ldap_data.name);
}
}
if( ber ) ber_free(ber, 0);
entry = ldap_next_entry(obj->ld, entry);
}
break;
}
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("Unhandled message type %x", msgtype); break;
default: ms_message("Unhandled message type %x", msgtype); break;
}
}
@ -275,17 +327,14 @@ static bool_t linphone_ldap_contact_provider_iterate(void *data)
case LDAP_RES_SEARCH_REFERENCE:
case LDAP_RES_INTERMEDIATE:
{
bool_t associated_req_found = FALSE;
LDAPMessage* message = ldap_first_message(obj->ld, results);
LinphoneLDAPContactSearch* req = linphone_ldap_request_entry_search(obj, ldap_msgid(message));
while( message != NULL ){
LinphoneLDAPContactSearch* req = linphone_ldap_request_entry_search(obj, ldap_msgid(message));
ms_message("Message @%p:id %d / type %x / associated request: %p", message, ldap_msgid(message), ldap_msgtype(message), req);
linphone_ldap_handle_search_result(obj, req, message );
if( req ) associated_req_found = TRUE;
message = ldap_next_message(obj->ld, message);
}
if( associated_req_found)
linphone_ldap_cancel_search_from_msgid(obj, ldap_msgid(results));
if( req ) linphone_ldap_contact_provider_cancel_search(LINPHONE_CONTACT_PROVIDER(obj), LINPHONE_CONTACT_SEARCH(req));
break;
}
case LDAP_RES_MODIFY:
@ -304,21 +353,47 @@ static bool_t linphone_ldap_contact_provider_iterate(void *data)
return TRUE;
}
static void linphone_ldap_contact_provider_conf_destroy(LinphoneLDAPContactProvider* obj )
{
if(obj->username) ms_free(obj->username);
if(obj->password) ms_free(obj->password);
if(obj->base_object) ms_free(obj->base_object);
if(obj->filter) ms_free(obj->filter);
if(obj->sip_attr) ms_free(obj->sip_attr);
if(obj->name_attr) ms_free(obj->name_attr);
if(obj->attributes){
int i=0;
for( ; obj->attributes[i]; i++){
ms_free(obj->attributes[i]);
}
ms_free(obj->attributes);
}
}
static void linphone_ldap_contact_provider_loadconfig(LinphoneLDAPContactProvider* obj, LpConfig* config)
{
const char* section="ldap";
char* attributes_list, *saveptr, *attr;
unsigned int attr_count = 0, attr_idx = 0, i;
obj->use_tls = lp_config_get_int(config, section, "use_tls", 0);
obj->timeout = lp_config_get_int(config, section, "timeout", 10);
obj->deref_aliases = lp_config_get_int(config, section, "deref_aliases", 0);
obj->max_results = lp_config_get_int(config, section, "max_results", 50);
obj->auth_method = linphone_ldap_contact_provider_auth_method( lp_config_get_string(config, section, "auth_method", "anonymous"));
obj->use_tls = lp_config_get_int(config, section, "use_tls", 0);
obj->timeout = lp_config_get_int(config, section, "timeout", 10);
obj->deref_aliases = lp_config_get_int(config, section, "deref_aliases", 0);
obj->max_results = lp_config_get_int(config, section, "max_results", 50);
obj->auth_method = linphone_ldap_contact_provider_auth_method( lp_config_get_string(config, section, "auth_method", "anonymous"));
// free any pre-existing char* conf values
linphone_ldap_contact_provider_conf_destroy(obj);
obj->username = ms_strdup(lp_config_get_string(config, section, "username", ""));
obj->password = ms_strdup(lp_config_get_string(config, section, "password", ""));
obj->base_object = ms_strdup(lp_config_get_string(config, section, "base_object", "dc=example,dc=com"));
obj->server = ms_strdup(lp_config_get_string(config, section, "server", "ldap://localhost:10389"));
obj->filter = ms_strdup(lp_config_get_string(config, section, "filter", "uid=*%s*"));
obj->name_attr = ms_strdup(lp_config_get_string(config, section, "name_attribute", "givenName"));
obj->sip_attr = ms_strdup(lp_config_get_string(config, section, "sip_attribute", "mobile"));
/*
* parse the attributes list
*/
@ -327,12 +402,12 @@ static void linphone_ldap_contact_provider_loadconfig(LinphoneLDAPContactProvide
"telephoneNumber,givenName,sn,mobile,homePhone"));
// count attributes:
for( i=0; attributes_list[i]; i++)
{
for( i=0; attributes_list[i]; i++) {
if( attributes_list[i] == ',') attr_count++;
}
obj->attributes = ms_malloc0((attr_count+2) * sizeof(char*));
// 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 ){
@ -343,14 +418,6 @@ static void linphone_ldap_contact_provider_loadconfig(LinphoneLDAPContactProvide
if( attr_idx != attr_count+1) ms_error("Invalid attribute number!!! %d expected, got %d", attr_count+1, attr_idx);
ms_free(attributes_list);
obj->username = ms_strdup(lp_config_get_string(config, section, "username", ""));
obj->password = ms_strdup(lp_config_get_string(config, section, "password", ""));
obj->base_object = ms_strdup(lp_config_get_string(config, section, "base_object", "dc=example,dc=com"));
obj->server = ms_strdup(lp_config_get_string(config, section, "server", "ldap://localhost:10389"));
obj->filter = ms_strdup(lp_config_get_string(config, section, "filter", "uid=*%s*"));
}
static int linphone_ldap_contact_provider_bind( LinphoneLDAPContactProvider* obj )
@ -443,29 +510,7 @@ static inline LinphoneLDAPContactSearch* linphone_ldap_request_entry_search( Lin
else return NULL;
}
static unsigned int linphone_ldap_cancel_search_from_msgid( LinphoneLDAPContactProvider* obj, int msgid )
{
int ret = 1;
LinphoneLDAPContactSearch dummy = {
.msgid = msgid
};
MSList* list_entry = ms_list_find_custom(obj->requests, linphone_ldap_request_entry_compare_weak, &dummy);
ms_message("Found entry for msgid %d @%p", msgid, list_entry);
if( list_entry ) {
LinphoneLDAPContactSearch* entry = list_entry->data;
obj->requests = ms_list_remove_link(obj->requests, list_entry);
obj->req_count--;
belle_sip_object_unref(entry);
ret = 0; // return OK if we found it in the monitored requests
} else {
ms_warning("Couldn't find ldap request id %d in monitoring.", msgid);
}
return ret;
}
static unsigned int linphone_ldap_cancel_search(LinphoneContactProvider* obj, LinphoneContactSearch *req)
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);
@ -473,7 +518,7 @@ static unsigned int linphone_ldap_cancel_search(LinphoneContactProvider* obj, Li
MSList* list_entry = ms_list_find_custom(ldap_cp->requests, linphone_ldap_request_entry_compare_strong, req);
if( list_entry ) {
ldap_cp->requests = ms_list_remove(ldap_cp->requests, list_entry);
ldap_cp->requests = ms_list_remove_link(ldap_cp->requests, list_entry);
ldap_cp->req_count--;
ret = 0; // return OK if we found it in the monitored requests
} else {
@ -516,27 +561,30 @@ static int linphone_ldap_marshal(LinphoneLDAPContactProvider* obj, char* buff, s
error = belle_sip_snprintf(buff, buff_size, offset,
"CONFIG:\n"
"tls: %d \n"
"auth: %d \n"
"user: %s \n"
"pass: %s \n"
"server: %s \n"
"base: %s \n"
"filter: %s \n"
"tls: %d \n"
"auth: %d \n"
"user: %s \n"
"pass: %s \n"
"server: %s \n"
"base: %s \n"
"filter: %s \n"
"timeout: %d \n"
"deref: %d \n"
"deref: %d \n"
"max_res: %d \n"
"attrs: ",
"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->max_results,
obj->sip_attr, obj->name_attr);
if(error!= BELLE_SIP_OK) return error;
char **attr = obj->attributes;
while( *attr ){
error = belle_sip_snprintf(buff, buff_size, offset, "attr:%s\n", *attr);
error = belle_sip_snprintf(buff, buff_size, offset, "- %s\n", *attr);
if(error!= BELLE_SIP_OK) return error;
else attr++;
}
@ -559,7 +607,7 @@ BELLE_SIP_INSTANCIATE_CUSTOM_VPTR(LinphoneLDAPContactProvider)=
},
"LDAP",
(LinphoneContactProviderStartSearchMethod)linphone_ldap_begin_search,
(LinphoneContactProviderCancelSearchMethod)linphone_ldap_cancel_search
(LinphoneContactProviderCancelSearchMethod)linphone_ldap_contact_provider_cancel_search
}
};

View file

@ -46,6 +46,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define LINPHONE_VERSION LINPHONE_VERSION_DATE
#endif
enum {
COMPLETION_HISTORY,
COMPLETION_LDAP
};
GdkPixbuf * create_pixbuf(const gchar *filename);
GdkPixbufAnimation *create_pixbuf_animation(const gchar *filename);
void add_pixmap_directory(const gchar *directory);

View file

@ -169,7 +169,52 @@ void linphone_gtk_login_frame_connect_clicked(GtkWidget *button){
void test_cb( LinphoneContactSearch* req, MSList* friends, void* data )
{
ms_message("LDAP Search CB received ");
ms_message("LDAP Search CB received:");
GtkEntry* uribar = GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
GtkTreeModel* model = gtk_entry_completion_get_model(gtk_entry_get_completion(uribar));
GtkListStore* list = GTK_LIST_STORE(model);
GtkTreeIter iter;
// clear completion list from previous LDAP completion suggestions
if (!gtk_tree_model_get_iter_first(model,&iter)) return;
do {
int type;
char* url;
bool_t valid = TRUE;
gtk_tree_model_get(model,&iter,1,&type,0,&url,-1);
if (type == COMPLETION_LDAP) {
ms_message("Removing entry for %s", url?url:"NULL");
valid = gtk_list_store_remove(list, &iter);
} else {
ms_message("Keep entry for %s (type %d)", url?url:"NULL", type);
}
if( url ) g_free(url);
if( !valid ) break;
}while(gtk_tree_model_iter_next(model,&iter));
while( friends ){
LinphoneFriend* lf = friends->data;
if( lf ) {
const LinphoneAddress* la = linphone_friend_get_address(lf);
if( la ){
char *addr = linphone_address_as_string(la);
if( addr ){
ms_message("Match: name=%s, addr=%s", linphone_friend_get_name(lf), addr);
gtk_list_store_insert_with_values(list, &iter, -1,
0, addr,
1, COMPLETION_LDAP, -1);
ms_free(addr);
}
}
}
friends = friends->next;
}
gtk_entry_completion_complete(gtk_entry_get_completion(uribar));
}
@ -177,7 +222,10 @@ void test_btn_clicked_cb(GtkWidget *button)
{
ms_message("test_button_clicked_cb");
LinphoneCore* core = linphone_gtk_get_core();
linphone_core_ldap_launch_search(core, "mar", test_cb, (void*)0x12345678);
GtkWidget *uri_bar=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar");
const gchar* pred = gtk_entry_buffer_get_text(gtk_entry_get_buffer((GtkEntry*)uri_bar));
linphone_core_ldap_launch_search(core, pred, test_cb, (void*)0x12345678);
}
void linphone_gtk_internet_kind_changed(GtkWidget *combo){

View file

@ -629,12 +629,28 @@ static gboolean linphone_gtk_iterate(LinphoneCore *lc){
return TRUE;
}
static gboolean uribar_completion_matchfunc(GtkEntryCompletion *completion, const gchar *key, GtkTreeIter *iter, gpointer user_data){
char* address = NULL;
gboolean ret = FALSE;
gchar *tmp= NULL;
gtk_tree_model_get(gtk_entry_completion_get_model(completion),iter,0,&address,-1);
ms_message("In matchFunc(): key=%s, addr=%s",key,address);
tmp = g_utf8_casefold(address,-1);
if (tmp){
if (strstr(tmp,key))
ret=TRUE;
g_free(tmp);
}
return ret;
}
static void load_uri_history(){
GtkEntry *uribar=GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar"));
char key[20];
int i;
GtkEntryCompletion *gep=gtk_entry_completion_new();
GtkListStore *model=gtk_list_store_new(1,G_TYPE_STRING);
GtkListStore *model=gtk_list_store_new(2,G_TYPE_STRING,G_TYPE_INT);
for (i=0;;i++){
const char *uri;
snprintf(key,sizeof(key),"uri%i",i);
@ -642,13 +658,14 @@ static void load_uri_history(){
if (uri!=NULL) {
GtkTreeIter iter;
gtk_list_store_append(model,&iter);
gtk_list_store_set(model,&iter,0,uri,-1);
gtk_list_store_set(model,&iter,0,uri,1,COMPLETION_HISTORY,-1);
if (i==0) gtk_entry_set_text(uribar,uri);
}
else break;
}
gtk_entry_completion_set_model(gep,GTK_TREE_MODEL(model));
gtk_entry_completion_set_text_column(gep,0);
gtk_entry_completion_set_match_func(gep,uribar_completion_matchfunc, NULL, NULL);
gtk_entry_set_completion(uribar,gep);
}
@ -697,7 +714,7 @@ static void completion_add_text(GtkEntry *entry, const char *text){
}
/* and prepend it on top of the list */
gtk_list_store_prepend(GTK_LIST_STORE(model),&iter);
gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,-1);
gtk_list_store_set(GTK_LIST_STORE(model),&iter,0,text,1,COMPLETION_HISTORY,-1);
save_uri_history();
}