diff --git a/coreapi/ldap/ldapprovider.c b/coreapi/ldap/ldapprovider.c index dcd4b22e8..d71989726 100644 --- a/coreapi/ldap/ldapprovider.c +++ b/coreapi/ldap/ldapprovider.c @@ -15,8 +15,10 @@ */ #include "ldapprovider.h" +#include "linphonecore.h" #include "linphonecore_utils.h" #include "lpconfig.h" + #include @@ -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 } }; diff --git a/gtk/linphone.h b/gtk/linphone.h index 67fa27ebf..2b2eba531 100644 --- a/gtk/linphone.h +++ b/gtk/linphone.h @@ -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); diff --git a/gtk/loginframe.c b/gtk/loginframe.c index 50160a85a..2aa55d9dc 100644 --- a/gtk/loginframe.c +++ b/gtk/loginframe.c @@ -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){ diff --git a/gtk/main.c b/gtk/main.c index d8a8a2ede..9bab5b833 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -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(); }