diff --git a/configure.ac b/configure.ac index 5c6700d5f..19087a244 100644 --- a/configure.ac +++ b/configure.ac @@ -165,6 +165,24 @@ AC_ARG_ENABLE(x11, [enable_x11=true] ) +dnl conditional build of LDAP support +AC_ARG_ENABLE(ldap, + [AS_HELP_STRING([--disable-ldap], [Disable LDAP support (default=no)])], + [case "${enableval}" in + yes) enable_ldap=true ;; + no) enable_ldap=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --disable-ldap) ;; + esac], + [enable_ldap=true] +) + +AM_CONDITIONAL(BUILD_LDAP, test x$enable_ldap != xfalse) +if test "$enable_ldap" = "true"; then + AC_CHECK_LIB(ldap,ldap_initialize, LDAP_LIBS="-lldap") + AC_SUBST(LDAP_LIBS) + AC_DEFINE(BUILD_LDAP,1,[Defined if LDAP build option enabled]) +fi + dnl conditionnal build of console interface. AC_ARG_ENABLE(console_ui, [AS_HELP_STRING([--enable-console_ui=[yes/no]], [Turn on or off compilation of console interface (default=yes)])], @@ -864,6 +882,7 @@ printf "* %-30s %s\n" "Tools" $build_tools printf "* %-30s %s\n" "Message storage" $enable_msg_storage printf "* %-30s %s\n" "zRTP encryption (GPLv3)" $zrtp printf "* %-30s %s\n" "uPnP support" $build_upnp +printf "* %-30s %s\n" "LDAP support" $enable_ldap if test "$enable_tunnel" = "true" ; then printf "* %-30s %s\n" "Tunnel support" "true" diff --git a/console/Makefile.am b/console/Makefile.am index 84e0c94cd..3a975e9c4 100644 --- a/console/Makefile.am +++ b/console/Makefile.am @@ -24,11 +24,12 @@ bin_PROGRAMS+=linphoned endif linphonec_SOURCES=linphonec.c linphonec.h commands.c -linphonec_CFLAGS=$(COMMON_CFLAGS) $(CONSOLE_FLAGS) +linphonec_CFLAGS=$(COMMON_CFLAGS) $(CONSOLE_FLAGS) $(BELLESIP_CFLAGS) linphonec_LDADD=$(top_builddir)/coreapi/liblinphone.la \ $(READLINE_LIBS) \ $(SQLITE3_LIBS) \ - $(X11_LIBS) + $(X11_LIBS) \ + $(BELLESIP_LIBS) if BUILD_WIN32 #special build of linphonec to detach from the windows console diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am index ec0250fa8..7d728492a 100644 --- a/coreapi/Makefile.am +++ b/coreapi/Makefile.am @@ -48,11 +48,17 @@ liblinphone_la_SOURCES=\ message_storage.c \ info.c \ event.c event.h \ + contactprovider.c contactprovider.h \ + dict.c \ $(GITVERSION_FILE) if BUILD_UPNP liblinphone_la_SOURCES+=upnp.c upnp.h endif + +if BUILD_LDAP +liblinphone_la_SOURCES+= ldap/ldapprovider.c ldap/ldapprovider.h +endif liblinphone_la_SOURCES+= bellesip_sal/sal_address_impl.c \ bellesip_sal/sal_impl.c bellesip_sal/sal_impl.h \ @@ -104,7 +110,8 @@ liblinphone_la_LIBADD= \ $(TUNNEL_LIBS) \ $(LIBSOUP_LIBS) \ $(SQLITE3_LIBS) \ - $(LIBXML2_LIBS) + $(LIBXML2_LIBS) \ + $(LDAP_LIBS) if ENABLE_TESTS diff --git a/coreapi/contactprovider.c b/coreapi/contactprovider.c new file mode 100644 index 000000000..96a941b1f --- /dev/null +++ b/coreapi/contactprovider.c @@ -0,0 +1,101 @@ +/* + * 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 "contactprovider.h" +#include + +/* LinphoneContactSearchRequest + */ + +void linphone_contact_search_init(LinphoneContactSearch* obj, + const char* predicate, + ContactSearchCallback cb, + void* cb_data) +{ + static unsigned int request_id_counter = 1; + obj->id = request_id_counter++; // unique id + obj->predicate = ms_strdup(predicate?predicate:""); + obj->cb = cb; + obj->data = cb_data; + ms_message("LinphoneContactSearch@%p(id:%d, pred:%s, cb:%p, data:%p)", + obj, obj->id, obj->predicate, obj->cb, obj->data); +} + +static void linphone_contact_search_destroy( LinphoneContactSearch* req) { + ms_message( "~LinphoneContactSearch(%p)", req); + if( req->predicate ) ms_free(req->predicate); +} + +ContactSearchID linphone_contact_search_get_id(LinphoneContactSearch* obj) +{ + return obj->id; +} + +const char*linphone_contact_search_get_predicate(LinphoneContactSearch* obj) +{ + return obj->predicate; +} + +void linphone_contact_search_invoke_cb(LinphoneContactSearch* req, MSList* friends) +{ + if( req->cb ) req->cb(req, friends, req->data); +} + +int linphone_contact_search_compare(const void* a, const void* b) { + LinphoneContactSearch *ra=((LinphoneContactSearch*)a); + LinphoneContactSearch *rb=((LinphoneContactSearch*)b); + return !(ra->id == rb->id); // return 0 if id is equal, 1 otherwise +} + +BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneContactSearch); + +BELLE_SIP_INSTANCIATE_VPTR(LinphoneContactSearch,belle_sip_object_t, + (belle_sip_object_destroy_t)linphone_contact_search_destroy, + NULL, // clone + NULL, // marshal + FALSE +); + +/* + * LinphoneContactProvider + */ + + +void linphone_contact_provider_init(LinphoneContactProvider* obj, LinphoneCore* lc){ + obj->lc = lc; +} + +static void contact_provider_destroy(LinphoneContactProvider* obj){ + (void)obj; +} + + +BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneContactProvider); +BELLE_SIP_INSTANCIATE_CUSTOM_VPTR(LinphoneContactProvider)= +{ + { + BELLE_SIP_VPTR_INIT(LinphoneContactProvider,belle_sip_object_t,TRUE), + (belle_sip_object_destroy_t) contact_provider_destroy, + NULL,/*no clone*/ + NULL,/*no marshal*/ + }, + "", + // Pure virtual + NULL, /* begin_search -> pure virtual */ + NULL /* cancel_search -> pure virtual */ +}; + + diff --git a/coreapi/contactprovider.h b/coreapi/contactprovider.h new file mode 100644 index 000000000..857bd1a8d --- /dev/null +++ b/coreapi/contactprovider.h @@ -0,0 +1,63 @@ +/* + * 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 +#include "linphonecore.h" + +/* LinphoneContactSearchRequest */ + +struct _LinphoneContactSearch{ + belle_sip_object_t base; + ContactSearchID id; + char* predicate; + ContactSearchCallback cb; + void* data; +}; + +#define LINPHONE_CONTACT_SEARCH(obj) BELLE_SIP_CAST(obj,LinphoneContactSearch) +BELLE_SIP_DECLARE_VPTR(LinphoneContactSearch) + + +void linphone_contact_search_init(LinphoneContactSearch* obj, const char* predicate, ContactSearchCallback cb, void* cb_data); +ContactSearchID linphone_contact_search_get_id(LinphoneContactSearch* obj); +const char* linphone_contact_search_get_predicate(LinphoneContactSearch* obj); +void linphone_contact_search_invoke_cb(LinphoneContactSearch* req, MSList* friends); + + +/* LinphoneContactProvider */ + +struct _LinphoneContactProvider { + belle_sip_object_t base; + LinphoneCore* lc; +}; + +typedef struct _LinphoneContactProvider LinphoneContactProvider; +typedef LinphoneContactSearch* (*LinphoneContactProviderStartSearchMethod)( LinphoneContactProvider* thiz, const char* predicate, ContactSearchCallback cb, void* data ); +typedef unsigned int (*LinphoneContactProviderCancelSearchMethod)( LinphoneContactProvider* thiz, LinphoneContactSearch *request ); +#define LINPHONE_CONTACT_PROVIDER(obj) BELLE_SIP_CAST(obj,LinphoneContactProvider) + +BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(LinphoneContactProvider,belle_sip_object_t) + const char* name; /*!< Name of the contact provider (LDAP, Google, ...) */ + + /* pure virtual methods: inheriting objects must implement these */ + LinphoneContactProviderStartSearchMethod begin_search; + LinphoneContactProviderCancelSearchMethod cancel_search; +BELLE_SIP_DECLARE_CUSTOM_VPTR_END + + +void linphone_contact_provider_init(LinphoneContactProvider* obj, LinphoneCore* lc); +LinphoneCore* linphone_contact_provider_get_core(LinphoneContactProvider* obj); +const char* linphone_contact_provider_get_name(LinphoneContactProvider* obj); diff --git a/coreapi/dict.c b/coreapi/dict.c new file mode 100644 index 000000000..4535e1a27 --- /dev/null +++ b/coreapi/dict.c @@ -0,0 +1,159 @@ +/* +linphone +Copyright (C) 2009 Simon MORLAT (simon.morlat@linphone.org) + +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 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 "linphonecore.h" +#include "lpconfig.h" +#include "private.h" + +#include +#include +#include + + +/** + * @addtogroup linphone_dict + * @{ +**/ + + +LinphoneDictionary* linphone_dictionary_new() +{ + return belle_sip_dict_create(); +} + +LinphoneDictionary* linphone_dictionary_clone(const LinphoneDictionary* src) +{ + LinphoneDictionary* cloned = linphone_dictionary_new(); + if( cloned ){ + belle_sip_dict_clone(src, cloned); + } + return cloned; +} + +LinphoneDictionary* linphone_dictionary_ref(LinphoneDictionary* obj) +{ + return BELLE_SIP_DICT(belle_sip_object_ref(obj)); +} + +void linphone_dictionary_unref(LinphoneDictionary *obj) +{ + belle_sip_object_unref(obj); +} + +void linphone_dictionary_set_int(LinphoneDictionary* obj, const char* key, int value) +{ + belle_sip_dict_set_int(obj, key, value); +} + +int linphone_dictionary_get_int(LinphoneDictionary* obj, const char* key, int default_value) +{ + return belle_sip_dict_get_int(obj, key, default_value); +} + +void linphone_dictionary_set_string(LinphoneDictionary* obj, const char* key, const char*value) +{ + belle_sip_dict_set_string(obj, key, value); +} + +const char* linphone_dictionary_get_string(LinphoneDictionary* obj, const char* key, const char* default_value) +{ + return belle_sip_dict_get_string(obj, key, default_value); +} + +void linphone_dictionary_set_int64(LinphoneDictionary* obj, const char* key, int64_t value) +{ + belle_sip_dict_set_int64(obj, key, value); +} + +int64_t linphone_dictionary_get_int64(LinphoneDictionary* obj, const char* key, int64_t default_value) +{ + return belle_sip_dict_get_int64(obj, key, default_value); +} + +int linphone_dictionary_remove(LinphoneDictionary* obj, const char* key) +{ + return belle_sip_dict_remove(obj, key); +} + +void linphone_dictionary_clear(LinphoneDictionary* obj) +{ + belle_sip_dict_clear(obj); +} + +int linphone_dictionary_haskey(const LinphoneDictionary* obj, const char* key) +{ + return belle_sip_dict_haskey(obj, key); +} + +void linphone_dictionary_foreach(const LinphoneDictionary* obj, void (*apply_func)(const char*, void*, void*), void* userdata) +{ + return belle_sip_dict_foreach(obj, apply_func, userdata); +} + +struct lp_config_to_dict { + const char* section; + const LpConfig* config; + LinphoneDictionary* dict; +}; + +static void lp_config_section_to_dict_cb(const char*key, struct lp_config_to_dict* userdata) +{ + const char* value = lp_config_get_string(userdata->config, userdata->section, key, ""); + linphone_dictionary_set_string(userdata->dict, key, value); +} + +LinphoneDictionary* lp_config_section_to_dict(const LpConfig* lpconfig, const char* section) +{ + LinphoneDictionary* dict = NULL; + struct lp_config_to_dict fd; + fd.config = lpconfig; + fd.section = section; + + dict = linphone_dictionary_new(); + fd.dict = dict; + + lp_config_for_each_entry(lpconfig, section, + (void (*)(const char*, void*))lp_config_section_to_dict_cb, + &fd); + + return dict; +} + +struct lp_config_from_dict { + const char* section; + LpConfig* config; +}; + +static void lp_config_dict_dump_cb( const char* key, void* value, void* userdata) +{ + struct lp_config_from_dict* fd= (struct lp_config_from_dict*)userdata; + lp_config_set_string(fd->config, fd->section, key, (const char*)value); +} + +void lp_config_load_dict_to_section(LpConfig* lpconfig, const char* section, const LinphoneDictionary* dict) +{ + struct lp_config_from_dict pvdata = { section, lpconfig }; + linphone_dictionary_foreach(dict,lp_config_dict_dump_cb, &pvdata); +} + + + +/** + * @} +**/ diff --git a/coreapi/help/Makefile.am b/coreapi/help/Makefile.am index 9d35b40a4..9d3c2475a 100644 --- a/coreapi/help/Makefile.am +++ b/coreapi/help/Makefile.am @@ -53,7 +53,8 @@ LINPHONE_TUTOS=$(helloworld_SOURCES) helloworld_LDADD=$(top_builddir)/coreapi/liblinphone.la \ $(MEDIASTREAMER_LIBS) \ - $(ORTP_LIBS) + $(ORTP_LIBS) \ + $(BELLESIP_LIBS) registration_SOURCES=registration.c LINPHONE_TUTOS+=$(registration_SOURCES) @@ -85,7 +86,8 @@ AM_CFLAGS=\ -DLOG_DOMAIN=\"LinphoneCore\" \ $(IPV6_CFLAGS) \ -DORTP_INET6 \ - $(VIDEO_CFLAGS) + $(VIDEO_CFLAGS) \ + $(BELLESIP_CFLAGS) tutodir=$(datadir)/tutorials/linphone diff --git a/coreapi/ldap/ldapprovider.c b/coreapi/ldap/ldapprovider.c new file mode 100644 index 000000000..35a7638a0 --- /dev/null +++ b/coreapi/ldap/ldapprovider.c @@ -0,0 +1,773 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "ldapprovider.h" +#include "linphonecore.h" +#include "linphonecore_utils.h" +#include "lpconfig.h" +#include + +#include +#include + + +#define MAX_RUNNING_REQUESTS 10 +#define FILTER_MAX_SIZE 512 + +typedef enum { + ANONYMOUS, + PLAIN, + SASL +} LDAPAuthMethod; + +struct LDAPFriendData { + char* name; + char* sip; +}; + +struct _LinphoneLDAPContactProvider +{ + LinphoneContactProvider base; + LinphoneDictionary* config; + + LDAP* ld; + MSList* requests; + uint req_count; + + // bind transaction + int bind_msgid; + const char* auth_mechanism; + bool_t connected; + + // config + int use_tls; + LDAPAuthMethod auth_method; + const char* username; + const char* password; + const char* server; + + 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; + MSList* 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); + struct timeval timeout = { cp->timeout, 0 }; + + 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; + + ms_message("Calling ldap_search_ext with predicate '%s' on base %s", search->filter, cp->base_object); + + int ret = ldap_search_ext(search->ld, + cp->base_object, // base from which to start + LDAP_SCOPE_SUBTREE, + search->filter, // search predicate + cp->attributes, // which attributes to get + 0, // 0 = get attrs AND value, 1 = get attrs only + NULL, + NULL, + &timeout, // server timeout for the search + cp->max_results,// max result number + &search->msgid ); + + if( ret != LDAP_SUCCESS ){ + ms_error("Error ldap_search_ext returned %d (%s)", ret, ldap_err2string(ret)); + belle_sip_object_unref(search); + return NULL; + } else { + ms_message("LinphoneLDAPContactSearch created @%p : msgid %d", search, search->msgid); + } + 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); + ms_list_for_each(obj->found_entries, linphone_ldap_contact_search_destroy_friend); + obj->found_entries = ms_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); + + +/* Authentication methods */ +struct AuthMethodDescription{ + LDAPAuthMethod method; + const char* description; +}; + +static struct AuthMethodDescription ldap_auth_method_description[] = { + {ANONYMOUS, "anonymous"}, + {PLAIN, "plain"}, + {SASL, "sasl"}, + {0, NULL} +}; + +static LDAPAuthMethod linphone_ldap_contact_provider_auth_method( const char* description ) +{ + struct AuthMethodDescription* desc = ldap_auth_method_description; + while( desc && desc->description ){ + if( strcmp(description, desc->description) == 0) + return desc->method; + desc++; + } + return ANONYMOUS; +} + +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 + ms_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_parse_bind_results( LinphoneLDAPContactProvider* obj, LDAPMessage* results ) +{ + int ret; + if( obj->auth_method == ANONYMOUS ) { + ms_message("ANONYMOUS BIND OK"); + ret = LDAP_SUCCESS; + } else { + ms_message("COMPLICATED BIND follow-up"); + ret = ldap_sasl_interactive_bind(obj->ld, + NULL, // dn, should be NULL + "DIGEST-MD5", + NULL,NULL, // server and client controls + LDAP_SASL_QUIET, // never prompt, only use callback + linphone_ldap_contact_provider_bind_interact, // callback to call when info is needed + obj, // private data + results, // result, to pass later on when a ldap_result() comes + &obj->auth_mechanism, + &obj->bind_msgid ); + if( ret != LDAP_SUCCESS){ + ms_error("ldap_parse_sasl_bind_result failed(%d)", ret); + } + } + + if( ret == LDAP_SUCCESS ){ + obj->connected = TRUE; + obj->bind_msgid = 0; + } + + return ret; + +} + +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); + char* dn = ldap_get_dn(obj->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); + + 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 = ms_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_destroy(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->req_count > 0) || (obj->bind_msgid != 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); + + if( ret != 0 && ret != -1) ms_message("ldap_result %x", ret); + + switch( ret ){ + case -1: + { + ms_warning("Error in ldap_result : returned -1 (req_count %d, bind_msgid %d): %s", obj->req_count, obj->bind_msgid, ldap_err2string(errno)); + break; + } + case 0: break; // nothing to do + + case LDAP_RES_BIND: + { + ms_message("iterate: LDAP_RES_BIND"); + if( ldap_msgid( results ) != obj->bind_msgid ) { + ms_error("Bad msgid"); + } else { + linphone_ldap_contact_provider_parse_bind_results( obj, results ); + } + 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 ){ + //ms_message("Message @%p:id %d / type %x / associated request: %p", message, ldap_msgid(message), ldap_msgtype(message), req); + 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); + } + 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", + + // 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)); + + obj->use_tls = linphone_dictionary_get_int(obj->config, "use_tls", 0); + obj->timeout = linphone_dictionary_get_int(obj->config, "timeout", 10); + obj->deref_aliases = linphone_dictionary_get_int(obj->config, "deref_aliases", 0); + obj->max_results = linphone_dictionary_get_int(obj->config, "max_results", 50); + obj->username = linphone_dictionary_get_string(obj->config, "username", ""); + obj->password = linphone_dictionary_get_string(obj->config, "password", ""); + obj->base_object = linphone_dictionary_get_string(obj->config, "base_object", "dc=example,dc=com"); + obj->server = linphone_dictionary_get_string(obj->config, "server", "ldap://192.168.0.230:10389"); + obj->filter = linphone_dictionary_get_string(obj->config, "filter", "uid=*%s*"); + obj->name_attr = linphone_dictionary_get_string(obj->config, "name_attribute", "givenName"); + obj->sip_attr = linphone_dictionary_get_string(obj->config, "sip_attribute", "mobile"); + + /* + * Get authentication method + */ + obj->auth_method = linphone_ldap_contact_provider_auth_method( + linphone_dictionary_get_string(obj->config, "auth_method", "anonymous") + ); + + /* + * 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"); + dflt=NULL; + break; + case SASL_CB_USER: + case SASL_CB_AUTHNAME: + ms_message("* SASL_CB_AUTHNAME -> %s", obj->username); + dflt=obj->username; + break; + case SASL_CB_PASS: + ms_message("* SASL_CB_PASS -> %s", obj->password); + dflt=obj->password; + break; + default: + ms_message("my_sasl_interact asked for unknown %lx\n",interact->id); + } + interact->result = (dflt && *dflt) ? dflt : (const char*)""; + interact->len = strlen( (const char*)interact->result ); + + interact++; + } + return LDAP_SUCCESS; +} + +static int linphone_ldap_contact_provider_bind( LinphoneLDAPContactProvider* obj ) +{ + int ret; + const char* auth_mechanism = linphone_dictionary_get_string(obj->config, "auth_method", "anonymous"); + LDAPAuthMethod method = obj->auth_method; + + if( method == ANONYMOUS ){ + // for anonymous authentication, use a simple sasl_bind + struct berval creds = {strlen(obj->password), ms_strdup(obj->password)}; + ret = ldap_sasl_bind(obj->ld, obj->base_object, NULL, &creds, NULL, NULL, &obj->bind_msgid); + if(creds.bv_val) ms_free(creds.bv_val); + } else { + ret = ldap_sasl_interactive_bind(obj->ld, + NULL, // dn, should be NULL + "SIMPLE",//"DIGEST-MD5", + NULL,NULL, // server and client controls + LDAP_SASL_QUIET, // never prompt, only use callback + linphone_ldap_contact_provider_bind_interact, // callback to call when info is needed + obj, // private data + NULL, // result, to pass later on when a ldap_result() comes + &obj->auth_mechanism, + &obj->bind_msgid ); + } + if( ret == LDAP_SUCCESS || ret == LDAP_SASL_BIND_IN_PROGRESS ) { + if( ret == LDAP_SASL_BIND_IN_PROGRESS) ms_message("BIND_IN_PROGRESS"); + ms_message("LDAP bind request sent, auth: %s, msgid %x", obj->auth_mechanism?obj->auth_mechanism:"-", obj->bind_msgid); + } else { + int err; + ldap_get_option(obj->ld, LDAP_OPT_RESULT_CODE, &err); + ms_error("ldap_sasl_bind error returned %d, err %d (%s), auth_method: %s", + ret, err, ldap_err2string(err), auth_mechanism ); + } + + 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 { + linphone_dictionary_foreach( config, linphone_ldap_contact_provider_config_dump_cb, 0 ); + linphone_ldap_contact_provider_loadconfig(obj, config); + + int ret = ldap_initialize(&(obj->ld),obj->server); + + if( ret != LDAP_SUCCESS ){ + ms_error( "Problem initializing ldap on url '%s': %s", obj->server, ldap_err2string(ret)); + belle_sip_object_unref(obj); + obj = NULL; + } else if( (ret = ldap_set_option(obj->ld, LDAP_OPT_PROTOCOL_VERSION, &proto_version)) != LDAP_SUCCESS ){ + ms_error( "Problem setting protocol version %d: %s", proto_version, ldap_err2string(ret)); + belle_sip_object_unref(obj); + obj = NULL; + } else { + // register our hook into iterate so that LDAP can do its magic asynchronously. + //linphone_ldap_contact_provider_bind(obj); + 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 = {}; + dummy.msgid = msgid; + + MSList* list_entry = ms_list_find_custom(obj->requests, linphone_ldap_request_entry_compare_weak, &dummy); + if( list_entry ) return list_entry->data; + else return NULL; +} + +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; + + 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_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 LinphoneLDAPContactSearch* linphone_ldap_contact_provider_begin_search ( LinphoneLDAPContactProvider* obj, + const char* predicate, + ContactSearchCallback cb, + void* cb_data ) +{ + // if we're not yet connected, bind + if( !obj->connected ) linphone_ldap_contact_provider_bind(obj); + + LinphoneLDAPContactSearch* request = linphone_ldap_contact_search_create ( obj, predicate, cb, cb_data ); + + if ( request != NULL ) { + ms_message ( "Created search %d for '%s', msgid %d, @%p", obj->req_count, predicate, request->msgid, request ); + + obj->requests = ms_list_append ( obj->requests, request ); + obj->req_count++; + } + return request; +} + + +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; + + error = belle_sip_snprintf(buff, buff_size, offset, "ld:%p,\n", obj->ld); + if(error!= BELLE_SIP_OK) return error; + + error = belle_sip_snprintf(buff, buff_size, offset, "req_count:%d,\n", obj->req_count); + if(error!= BELLE_SIP_OK) return error; + + error = belle_sip_snprintf(buff, buff_size, offset, "bind_msgid:%d,\n", obj->bind_msgid); + if(error!= BELLE_SIP_OK) return error; + + error = belle_sip_snprintf(buff, buff_size, offset, + "CONFIG:\n" + "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" + "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; + + char **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; + +} + + +BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneLDAPContactProvider); + +BELLE_SIP_INSTANCIATE_CUSTOM_VPTR(LinphoneLDAPContactProvider)= +{ + { + { + BELLE_SIP_VPTR_INIT(LinphoneLDAPContactProvider,LinphoneContactProvider,TRUE), + (belle_sip_object_destroy_t)linphone_ldap_contact_provider_destroy, + NULL, + (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 + } +}; + diff --git a/coreapi/ldap/ldapprovider.h b/coreapi/ldap/ldapprovider.h new file mode 100644 index 000000000..73878ee7c --- /dev/null +++ b/coreapi/ldap/ldapprovider.h @@ -0,0 +1,45 @@ +/* + * 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 "contactprovider.h" + +#include + +typedef struct _LinphoneLDAPContactProvider LinphoneLDAPContactProvider; + +/* LinphoneLDAPContactSearch */ +typedef struct _LinphoneLDAPContactSearch LinphoneLDAPContactSearch; + +#define LINPHONE_LDAP_CONTACT_SEARCH(obj) BELLE_SIP_CAST(obj,LinphoneLDAPContactSearch) +BELLE_SIP_DECLARE_VPTR(LinphoneLDAPContactSearch) + +LinphoneLDAPContactSearch* linphone_ldap_contact_search_create(LinphoneLDAPContactProvider* ld, + const char* predicate, + ContactSearchCallback cb, + void* cb_data); + +unsigned int linphone_ldap_contact_search_result_count(LinphoneLDAPContactSearch* obj); + + +/* LinphoneLDAPContactProvider */ + +#define LINPHONE_LDAP_CONTACT_PROVIDER(obj) BELLE_SIP_CAST(obj,LinphoneLDAPContactProvider) + +BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(LinphoneLDAPContactProvider,LinphoneContactProvider) +BELLE_SIP_DECLARE_CUSTOM_VPTR_END + +LinphoneLDAPContactProvider* linphone_ldap_contact_provider_create(LinphoneCore* lc, const LinphoneDictionary* config); +unsigned int linphone_ldap_contact_provider_get_max_result(const LinphoneLDAPContactProvider* obj); diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index bc256c466..6741073b1 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1350,6 +1350,7 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta lc->tunnel=linphone_core_tunnel_new(lc); if (lc->tunnel) linphone_tunnel_configure(lc->tunnel); #endif + if (lc->vtable.display_status) lc->vtable.display_status(lc,_("Ready")); lc->auto_net_state_mon=lc->sip_conf.auto_net_state_mon; @@ -5674,16 +5675,23 @@ static void linphone_core_uninit(LinphoneCore *lc) } #endif //BUILD_UPNP +#ifdef BUILD_LDAP + if( lc->ldap != NULL ) { + belle_sip_object_unref(lc->ldap); + lc->ldap = NULL; + } +#endif + if (lp_config_needs_commit(lc->config)) lp_config_sync(lc->config); lp_config_destroy(lc->config); lc->config = NULL; /* Mark the config as NULL to block further calls */ ms_list_for_each(lc->call_logs,(void (*)(void*))linphone_call_log_destroy); lc->call_logs=ms_list_free(lc->call_logs); - + ms_list_for_each(lc->last_recv_msg_ids,ms_free); lc->last_recv_msg_ids=ms_list_free(lc->last_recv_msg_ids); - + // Free struct variable if(lc->zrtp_secrets_cache != NULL) { ms_free(lc->zrtp_secrets_cache); @@ -6287,5 +6295,3 @@ void linphone_core_set_chat_database_path(LinphoneCore *lc, const char *path){ linphone_core_message_storage_init(lc); } } - - diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index ae7998db6..1e61cd37f 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -32,6 +32,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "lpconfig.h" +#include +#include + #define LINPHONE_IPADDR_SIZE 64 #define LINPHONE_HOSTNAME_SIZE 128 @@ -118,6 +121,8 @@ typedef enum _LinphoneTransportType LinphoneTransportType; */ typedef struct SalAddress LinphoneAddress; +typedef struct belle_sip_dict LinphoneDictionary; + /** * The LinphoneContent struct holds data that can be embedded in a signaling message. * @ingroup misc @@ -181,6 +186,37 @@ typedef enum _LinphoneReason LinphoneReason; **/ const char *linphone_reason_to_string(LinphoneReason err); + +/* linphone dictionary */ +LINPHONE_PUBLIC LinphoneDictionary* linphone_dictionary_new(); +LinphoneDictionary * linphone_dictionary_clone(const LinphoneDictionary* src); +LinphoneDictionary * linphone_dictionary_ref(LinphoneDictionary* obj); +void linphone_dictionary_unref(LinphoneDictionary* obj); +LINPHONE_PUBLIC void linphone_dictionary_set_int(LinphoneDictionary* obj, const char* key, int value); +LINPHONE_PUBLIC int linphone_dictionary_get_int(LinphoneDictionary* obj, const char* key, int default_value); +LINPHONE_PUBLIC void linphone_dictionary_set_string(LinphoneDictionary* obj, const char* key, const char*value); +LINPHONE_PUBLIC const char* linphone_dictionary_get_string(LinphoneDictionary* obj, const char* key, const char* default_value); +LINPHONE_PUBLIC void linphone_dictionary_set_int64(LinphoneDictionary* obj, const char* key, int64_t value); +LINPHONE_PUBLIC int64_t linphone_dictionary_get_int64(LinphoneDictionary* obj, const char* key, int64_t default_value); +LINPHONE_PUBLIC int linphone_dictionary_remove(LinphoneDictionary* obj, const char* key); +LINPHONE_PUBLIC void linphone_dictionary_clear(LinphoneDictionary* obj); +LINPHONE_PUBLIC int linphone_dictionary_haskey(const LinphoneDictionary* obj, const char* key); +LINPHONE_PUBLIC void linphone_dictionary_foreach( const LinphoneDictionary* obj, void (*apply_func)(const char*key, void* value, void* userdata), void* userdata); +/** + * Converts a config section into a dictionary. + * @return a #LinphoneDictionary with all the keys from a section, or NULL if the section doesn't exist + * @ingroup misc + */ +LinphoneDictionary* lp_config_section_to_dict( const LpConfig* lpconfig, const char* section ); + +/** + * Loads a dictionary into a section of the lpconfig. If the section doesn't exist it is created. + * Overwrites existing keys, creates non-existing keys. + * @ingroup misc + */ +void lp_config_load_dict_to_section( LpConfig* lpconfig, const char* section, const LinphoneDictionary* dict); + + #ifdef IN_LINPHONE #include "linphonefriend.h" #include "event.h" @@ -402,7 +438,7 @@ typedef enum _LinphonePrivacy { * **/ LinphonePrivacyCritical=0x10, - + /** * Special keyword to use privacy as defined either globally or by proxy using linphone_proxy_config_set_privacy() */ @@ -442,7 +478,7 @@ LINPHONE_PUBLIC LinphoneInfoMessage *linphone_info_message_copy(const LinphoneIn * @ingroup media_parameters **/ struct _LinphoneVideoPolicy{ - bool_t automatically_initiate; /** Can be created by linphone_chat_room_create_message(). */ typedef struct _LinphoneChatMessage LinphoneChatMessage; - + /** * A chat room is the place where text messages are exchanged. *
Can be created by linphone_core_create_chat_room(). @@ -945,7 +981,7 @@ LINPHONE_PUBLIC int linphone_chat_room_get_unread_messages_count(LinphoneChatRoo LINPHONE_PUBLIC LinphoneCore* linphone_chat_room_get_lc(LinphoneChatRoom *cr); LINPHONE_PUBLIC void linphone_chat_room_set_user_data(LinphoneChatRoom *cr, void * ud); LINPHONE_PUBLIC void * linphone_chat_room_get_user_data(LinphoneChatRoom *cr); -LINPHONE_PUBLIC MSList* linphone_core_get_chat_rooms(LinphoneCore *lc); +LINPHONE_PUBLIC MSList* linphone_core_get_chat_rooms(LinphoneCore *lc); LINPHONE_PUBLIC unsigned int linphone_chat_message_store(LinphoneChatMessage *msg); LINPHONE_PUBLIC const char* linphone_chat_message_state_to_string(const LinphoneChatMessageState state); @@ -1023,23 +1059,23 @@ typedef void (*LinphoneCoreCallEncryptionChangedCb)(LinphoneCore *lc, LinphoneCa * Registration state notification callback prototype * */ typedef void (*LinphoneCoreRegistrationStateChangedCb)(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState cstate, const char *message); -/** Callback prototype - * @deprecated +/** Callback prototype + * @deprecated */ typedef void (*ShowInterfaceCb)(LinphoneCore *lc); -/** Callback prototype - * @deprecated +/** Callback prototype + * @deprecated */ typedef void (*DisplayStatusCb)(LinphoneCore *lc, const char *message); -/** Callback prototype - * @deprecated +/** Callback prototype + * @deprecated */ typedef void (*DisplayMessageCb)(LinphoneCore *lc, const char *message); -/** Callback prototype - * @deprecated +/** Callback prototype + * @deprecated */ typedef void (*DisplayUrlCb)(LinphoneCore *lc, const char *message, const char *url); -/** Callback prototype +/** Callback prototype */ typedef void (*LinphoneCoreCbFunc)(LinphoneCore *lc,void * user_data); /** @@ -1057,7 +1093,7 @@ typedef void (*LinphoneCoreNotifyPresenceReceivedCb)(LinphoneCore *lc, LinphoneF * Callback prototype */ typedef void (*LinphoneCoreNewSubscriptionRequestedCb)(LinphoneCore *lc, LinphoneFriend *lf, const char *url); -/** +/** * Callback for requesting authentication information to application or user. * @param lc the LinphoneCore * @param realm the realm (domain) on which authentication is required. @@ -1066,7 +1102,7 @@ typedef void (*LinphoneCoreNewSubscriptionRequestedCb)(LinphoneCore *lc, Linphon */ typedef void (*LinphoneCoreAuthInfoRequestedCb)(LinphoneCore *lc, const char *realm, const char *username, const char *domain); -/** +/** * Callback to notify a new call-log entry has been added. * This is done typically when a call terminates. * @param lc the LinphoneCore @@ -1093,8 +1129,8 @@ typedef void (*LinphoneCoreTextMessageReceivedCb)(LinphoneCore *lc, LinphoneChat * @param LinphoneChatMessage incoming message */ typedef void (*LinphoneCoreMessageReceivedCb)(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMessage *message); - -/** + +/** * Callback for being notified of DTMFs received. * @param lc the linphone core * @param call the call that received the dtmf @@ -1114,7 +1150,7 @@ typedef void (*LinphoneCoreBuddyInfoUpdatedCb)(LinphoneCore *lc, LinphoneFriend */ typedef void (*LinphoneCoreTransferStateChangedCb)(LinphoneCore *lc, LinphoneCall *transfered, LinphoneCallState new_call_state); -/** +/** * Callback for receiving quality statistics for calls. * @param lc the LinphoneCore * @param call the call @@ -1122,11 +1158,11 @@ typedef void (*LinphoneCoreTransferStateChangedCb)(LinphoneCore *lc, LinphoneCal */ typedef void (*LinphoneCoreCallStatsUpdatedCb)(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallStats *stats); -/** +/** * Callback prototype for receiving info messages. * @param lc the LinphoneCore * @param call the call whose info message belongs to. - * @param msg the info message. + * @param msg the info message. */ typedef void (*LinphoneCoreInfoReceivedCb)(LinphoneCore *lc, LinphoneCall *call, const LinphoneInfoMessage *msg); @@ -1431,7 +1467,7 @@ LINPHONE_PUBLIC int linphone_core_enable_payload_type(LinphoneCore *lc, PayloadT * @param rate can be #LINPHONE_FIND_PAYLOAD_IGNORE_RATE * @param channels number of channels, can be #LINPHONE_FIND_PAYLOAD_IGNORE_CHANNELS * @return Returns NULL if not found. - */ + */ LINPHONE_PUBLIC PayloadType* linphone_core_find_payload_type(LinphoneCore* lc, const char* type, int rate, int channels) ; LINPHONE_PUBLIC int linphone_core_get_payload_type_number(LinphoneCore *lc, const PayloadType *pt); @@ -1447,7 +1483,7 @@ LINPHONE_PUBLIC bool_t linphone_core_check_payload_type_usability(LinphoneCore * * @ingroup proxy */ LINPHONE_PUBLIC LinphoneProxyConfig * linphone_core_create_proxy_config(LinphoneCore *lc); - + LINPHONE_PUBLIC int linphone_core_add_proxy_config(LinphoneCore *lc, LinphoneProxyConfig *config); LINPHONE_PUBLIC void linphone_core_clear_proxy_config(LinphoneCore *lc); @@ -1604,28 +1640,28 @@ LINPHONE_PUBLIC const char * linphone_core_get_stun_server(const LinphoneCore *l * @ingroup network_parameters * Return the availability of uPnP. * - * @return true if uPnP is available otherwise return false. + * @return true if uPnP is available otherwise return false. */ bool_t linphone_core_upnp_available(); /** * @ingroup network_parameters - * Return the internal state of uPnP. + * Return the internal state of uPnP. * * @param lc #LinphoneCore - * @return an LinphoneUpnpState. + * @return an LinphoneUpnpState. */ LinphoneUpnpState linphone_core_get_upnp_state(const LinphoneCore *lc); /** * @ingroup network_parameters - * Return the external ip address of router. + * Return the external ip address of router. * In some cases the uPnP can have an external ip address but not a usable uPnP - * (state different of Ok). + * (state different of Ok). * * @param lc #LinphoneCore * @return a null terminated string containing the external ip address. If the - * the external ip address is not available return null. + * the external ip address is not available return null. */ const char * linphone_core_get_upnp_external_ipaddress(const LinphoneCore *lc); @@ -2167,6 +2203,23 @@ LINPHONE_PUBLIC const char *linphone_core_get_video_display_filter(LinphoneCore LINPHONE_PUBLIC void linphone_core_set_video_display_filter(LinphoneCore *lc, const char *filtername); +/** Contact Providers + */ +BELLE_SIP_DECLARE_TYPES_BEGIN(linphone,10000) +BELLE_SIP_TYPE_ID(LinphoneContactSearch), +BELLE_SIP_TYPE_ID(LinphoneContactProvider), +BELLE_SIP_TYPE_ID(LinphoneLDAPContactProvider), +BELLE_SIP_TYPE_ID(LinphoneLDAPContactSearch) +BELLE_SIP_DECLARE_TYPES_END + +typedef unsigned int ContactSearchID; + +struct _LinphoneContactSearch; +typedef struct _LinphoneContactSearch LinphoneContactSearch; + +typedef void (*ContactSearchCallback)( LinphoneContactSearch* id, MSList* friends, void* data ); + + #ifdef __cplusplus } #endif diff --git a/coreapi/private.h b/coreapi/private.h index 69ee5b61e..8f13716e1 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -74,6 +74,10 @@ extern "C" { #endif #endif +#ifdef BUILD_LDAP +#include "ldap/ldapprovider.h" +#endif + struct _LinphoneCallParams{ LinphoneCall *referer; /*in case this call creation is consecutive to an incoming transfer, this points to the original call */ int audio_bw; /* bandwidth limit for audio stream */ @@ -656,6 +660,10 @@ struct _LinphoneCore #ifdef BUILD_UPNP UpnpContext *upnp; #endif //BUILD_UPNP + +#ifdef BUILD_LDAP + LinphoneLDAPContactProvider* ldap; +#endif //BUILD_LDAP }; diff --git a/gtk/Makefile.am b/gtk/Makefile.am index b80527de0..2f13d8b49 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -55,7 +55,7 @@ linphone_SOURCES+= \ endif linphone_LDADD= $(top_builddir)/coreapi/liblinphone.la \ - $(LIBGTK_LIBS) $(NOTIFY1_LIBS) $(NOTIFY4_LIBS) $(LIBGTKMAC_LIBS) $(INTLLIBS) $(SQLITE3_LIBS) + $(LIBGTK_LIBS) $(NOTIFY1_LIBS) $(NOTIFY4_LIBS) $(LIBGTKMAC_LIBS) $(INTLLIBS) $(SQLITE3_LIBS) $(BELLESIP_LIBS) if BUILD_WIN32 @@ -77,7 +77,7 @@ endif AM_CFLAGS= -DIN_LINPHONE -I$(top_srcdir)/coreapi/ \ $(MEDIASTREAMER_CFLAGS) \ - $(ORTP_CFLAGS) \ + $(ORTP_CFLAGS) $(BELLESIP_CFLAGS) \ $(STRICT_OPTIONS) $(LIBGTK_CFLAGS) $(LIBGTKMAC_CFLAGS) $(IPV6_CFLAGS) \ $(TUNNEL_CFLAGS) \ $(SQLITE3_CFLAGS) diff --git a/gtk/linphone.h b/gtk/linphone.h index 67fa27ebf..c77a02e6d 100644 --- a/gtk/linphone.h +++ b/gtk/linphone.h @@ -28,6 +28,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif #include "linphonecore.h" +#ifdef BUILD_LDAP +#include "ldap/ldapprovider.h" +#endif + + #ifdef ENABLE_NLS # include # undef _ @@ -46,6 +51,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); @@ -63,6 +73,8 @@ void linphone_gtk_show_assistant(void); void linphone_gtk_close_assistant(void); LinphoneCore *linphone_gtk_get_core(void); +LinphoneLDAPContactProvider* linphone_gtk_get_ldap(void); +void linphone_gtk_set_ldap(LinphoneLDAPContactProvider* ldap); GtkWidget *linphone_gtk_get_main_window(); void linphone_gtk_display_something(GtkMessageType type,const gchar *message); void linphone_gtk_start_call(GtkWidget *button); diff --git a/gtk/loginframe.c b/gtk/loginframe.c index 45da5480f..ef4ae9e05 100644 --- a/gtk/loginframe.c +++ b/gtk/loginframe.c @@ -20,6 +20,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "linphone.h" void linphone_gtk_login_frame_connect_clicked(GtkWidget *button); +void test_button_clicked_cb(GtkWidget *button); void linphone_gtk_exit_login_frame(void); enum { diff --git a/gtk/main.c b/gtk/main.c index d8a8a2ede..db9568cb0 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -50,6 +50,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. const char *this_program_ident_string="linphone_ident_string=" LINPHONE_VERSION; static LinphoneCore *the_core=NULL; +static LinphoneLDAPContactProvider* ldap_provider = NULL; static GtkWidget *the_ui=NULL; static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState rs, const char *msg); @@ -69,7 +70,7 @@ void linphone_gtk_save_main_window_position(GtkWindow* mw, GdkEvent *event, gpoi static gboolean linphone_gtk_auto_answer(LinphoneCall *call); void linphone_gtk_status_icon_set_blinking(gboolean val); void _linphone_gtk_enable_video(gboolean val); - +void linphone_gtk_on_uribar_changed(GtkEditable *uribar, gpointer user_data); #ifndef HAVE_GTK_OSX static gint main_window_x=0; @@ -231,6 +232,23 @@ static const char *linphone_gtk_get_factory_config_file(){ return _factory_config_file; } +LinphoneLDAPContactProvider* linphone_gtk_get_ldap(void){ +#ifdef BUILD_LDAP + return ldap_provider; +#else + return NULL; +#endif +} + +void linphone_gtk_set_ldap(LinphoneLDAPContactProvider* ldap) +{ + if( ldap_provider ) + belle_sip_object_unref(ldap_provider); + + ldap_provider = ldap ? LINPHONE_LDAP_CONTACT_PROVIDER(belle_sip_object_ref( ldap )) + : NULL; +} + static void linphone_gtk_init_liblinphone(const char *config_file, const char *factory_config_file, const char *db_file) { LinphoneCoreVTable vtable={0}; @@ -256,7 +274,16 @@ static void linphone_gtk_init_liblinphone(const char *config_file, the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL); //lp_config_set_int(linphone_core_get_config(the_core), "sip", "store_auth_info", 0); - + + +#ifdef BUILD_LDAP + if( lp_config_has_section(linphone_core_get_config(the_core),"ldap") ){ + LpConfig* cfg = linphone_core_get_config(the_core); + LinphoneDictionary* ldap_cfg = lp_config_section_to_dict(cfg, "ldap"); + linphone_gtk_set_ldap( linphone_ldap_contact_provider_create(the_core, ldap_cfg) ); + } +#endif + linphone_core_set_user_agent(the_core,"Linphone", LINPHONE_VERSION); linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL); linphone_core_set_zrtp_secrets_file(the_core,secrets_file); @@ -279,7 +306,7 @@ GtkWidget *linphone_gtk_get_main_window(){ } void linphone_gtk_destroy_main_window() { - linphone_gtk_destroy_window(the_ui); + linphone_gtk_destroy_window(the_ui); the_ui = NULL; } @@ -629,12 +656,31 @@ 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); + + tmp = g_utf8_casefold(address,-1); + if (tmp){ + if (strstr(tmp,key)) + ret=TRUE; + g_free(tmp); + } + + if( address) + g_free(address); + + 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,14 +688,18 @@ 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_popup_completion(gep, TRUE); + gtk_entry_completion_set_match_func(gep,uribar_completion_matchfunc, NULL, NULL); + gtk_entry_completion_set_minimum_key_length(gep,3); gtk_entry_set_completion(uribar,gep); + g_signal_connect (G_OBJECT (uribar), "changed", G_CALLBACK(linphone_gtk_on_uribar_changed), NULL); } static void save_uri_history(){ @@ -697,10 +747,130 @@ 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(); } +void on_contact_provider_search_results( LinphoneContactSearch* req, MSList* friends, void* data ) +{ + GtkTreeIter iter; + GtkEntry* uribar = GTK_ENTRY(data); + GtkEntryCompletion* compl = gtk_entry_get_completion(uribar); + GtkTreeModel* model = gtk_entry_completion_get_model(compl); + GtkListStore* list = GTK_LIST_STORE(model); + gboolean valid; + + // clear completion list from previous non-history entries + valid = gtk_tree_model_get_iter_first(model,&iter); + while(valid) + { + char* url; + int type; + gtk_tree_model_get(model,&iter, 0,&url, 1,&type, -1); + + if (type != COMPLETION_HISTORY) { + valid = gtk_list_store_remove(list, &iter); + } else { + valid = gtk_tree_model_iter_next(model,&iter); + } + + if( url ) g_free(url); + if( !valid ) break; + } + + // add new non-history related matches + 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("[LDAP]Insert match: %s", 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(compl); + // save the number of LDAP results to better decide if new results should be fetched when search predicate gets bigger + gtk_object_set_data(GTK_OBJECT(uribar), "ldap_res_cout", + GINT_TO_POINTER( + linphone_ldap_contact_search_result_count(LINPHONE_LDAP_CONTACT_SEARCH(req)) + ) + ); + + // Gtk bug? we need to emit a "changed" signal so that the completion appears if + // the list of results was previously empty + g_signal_handlers_block_by_func(uribar, linphone_gtk_on_uribar_changed, NULL); + g_signal_emit_by_name(uribar, "changed"); + g_signal_handlers_unblock_by_func(uribar, linphone_gtk_on_uribar_changed, NULL); +} + +struct CompletionTimeout { + guint timeout_id; +}; + +static gboolean launch_contact_provider_search(void *userdata) +{ + LinphoneLDAPContactProvider* ldap = linphone_gtk_get_ldap(); + GtkWidget* uribar = GTK_WIDGET(userdata); + const gchar* predicate = gtk_entry_get_text(GTK_ENTRY(uribar)); + gchar* previous_search = gtk_object_get_data(GTK_OBJECT(uribar), "previous_search"); + unsigned int prev_res_count = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(uribar), "ldap_res_cout")); + + if( ldap && strlen(predicate) >= 3 ){ // don't search too small predicates + unsigned int max_res_count = linphone_ldap_contact_provider_get_max_result(ldap); + + if( previous_search && + (strstr(predicate, previous_search) == predicate) && // last search contained results from this one + (prev_res_count != max_res_count) ){ // and we didn't reach the max result limit + + ms_message("Don't launch search on already searched data (current: %s, old search: %s), (%d/%d results)", + predicate, previous_search, prev_res_count, max_res_count); + return FALSE; + } + + // save current search + if( previous_search ) ms_free(previous_search); + gtk_object_set_data(GTK_OBJECT(uribar), "previous_search", ms_strdup(predicate)); + + ms_message("launch_contact_provider_search"); + LinphoneContactSearch* search = + BELLE_SIP_OBJECT_VPTR(ldap,LinphoneContactProvider)->begin_search( + LINPHONE_CONTACT_PROVIDER(ldap), + predicate, + on_contact_provider_search_results, + uribar); + + if(search) + belle_sip_object_ref(search); + } + return FALSE; +} + +void linphone_gtk_on_uribar_changed(GtkEditable *uribar, gpointer user_data) +{ +#ifdef BUILD_LDAP + gchar* text = gtk_editable_get_chars(uribar, 0,-1); + gint timeout = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(uribar), "complete_timeout")); + if( text ) g_free(text); + + if( timeout != 0 ) { + g_source_remove(timeout); + } + + timeout = g_timeout_add_seconds(1,(GSourceFunc)launch_contact_provider_search, uribar); + + gtk_object_set_data(GTK_OBJECT(uribar),"complete_timeout", GINT_TO_POINTER(timeout) ); +#endif +} + bool_t linphone_gtk_video_enabled(void){ const LinphoneVideoPolicy *vpol=linphone_core_get_video_policy(linphone_gtk_get_core()); return vpol->automatically_accept && vpol->automatically_initiate; @@ -1873,6 +2043,9 @@ static void linphone_gtk_quit(void){ g_source_remove_by_user_data(linphone_gtk_get_core()); #ifdef BUILD_WIZARD linphone_gtk_close_assistant(); +#endif +#ifdef BUILD_LDAP + linphone_gtk_set_ldap(NULL); #endif linphone_gtk_uninit_instance(); linphone_gtk_destroy_log_window(); diff --git a/gtk/main.ui b/gtk/main.ui index ae1335c5d..05461b6fc 100644 --- a/gtk/main.ui +++ b/gtk/main.ui @@ -1109,6 +1109,12 @@ 3 + + + + + + False @@ -1547,8 +1553,8 @@ - True True + True True False none @@ -1762,6 +1768,9 @@ 0 + + + True diff --git a/gtk/parameters.ui b/gtk/parameters.ui index 4cb435eb2..21464d9e4 100644 --- a/gtk/parameters.ui +++ b/gtk/parameters.ui @@ -9,6 +9,12 @@ 1 10 + + 500 + 50 + 1 + 10 + 1 65535 @@ -54,6 +60,12 @@ 1 10 + + 100 + 10 + 1 + 10 + 65535 2 @@ -81,6 +93,23 @@ 10 + + + + + + + + anonymous + + + GSSAPI + + + SASL + + + @@ -2428,6 +2457,540 @@ False + + + True + False + + + True + False + 0 + none + + + True + False + 12 + + + True + False + 5 + 2 + + + True + False + 1 + Server address: + + + + + True + False + Authentication method: + + + 1 + 2 + + + + + True + False + Username: + + + 2 + 3 + + + + + True + False + Password: + + + 3 + 4 + + + + + Use TLS Connection + True + True + False + False + True + + + 1 + 2 + 4 + 5 + + + + + True + True + + False + False + True + True + + + 1 + 2 + + + + + True + True + + False + False + True + True + + + 1 + 2 + 2 + 3 + + + + + True + True + + True + False + False + True + True + + + 1 + 2 + 3 + 4 + + + + + True + False + liststore2 + 0 + 0 + 0 + + + + 0 + + + + + 1 + 2 + 1 + 2 + + + + + + + + + True + False + <b>Connection</b> + True + + + + + True + True + 0 + + + + + True + False + 0 + none + + + True + False + 12 + + + True + False + 5 + 2 + + + True + False + Base object: + + + + + True + False + Filter (%s for name): + + + 1 + 2 + + + + + True + False + Name Attribute: + + + 2 + 3 + + + + + True + False + SIP address attribute: + + + 3 + 4 + + + + + True + False + Attributes to query: + + + 4 + 5 + + + + + True + True + + False + False + True + True + + + 1 + 2 + + + + + True + True + + False + False + True + True + + + 1 + 2 + 1 + 2 + + + + + True + True + + False + False + True + True + + + 1 + 2 + 2 + 3 + + + + + True + True + + False + False + True + True + + + 1 + 2 + 3 + 4 + + + + + True + True + + False + False + True + True + + + 1 + 2 + 4 + 5 + + + + + + + + + True + False + <b>Search</b> + True + + + + + True + True + 1 + + + + + True + False + 0 + none + + + True + False + 12 + + + True + False + 3 + 2 + + + True + False + Timeout for search: + + + + + True + False + Max results: + + + 1 + 2 + + + + + True + True + + False + False + True + True + adjustment9 + + + 1 + 2 + + + + + True + True + 3 + + False + False + True + True + adjustment10 + True + + + 1 + 2 + 1 + 2 + + + + + Follow Aliases + True + True + False + False + True + + + 1 + 2 + 2 + 3 + + + + + + + + + + + + True + False + <b>Miscellaneous</b> + True + + + + + True + True + 2 + + + + + True + False + 2 + + + + + + Save + True + True + True + False + + + + False + False + end + 1 + + + + + Reset + True + True + True + False + + + + False + False + end + 2 + + + + + False + True + 3 + + + + + 5 + + + + + True + False + + + True + False + gtk-disconnect + + + True + True + 0 + + + + + True + False + LDAP + + + True + True + 1 + + + + + 5 + False + + True diff --git a/gtk/propertybox.c b/gtk/propertybox.c index 8579fc2f4..20c0b01cd 100644 --- a/gtk/propertybox.c +++ b/gtk/propertybox.c @@ -19,6 +19,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "linphone.h" #include "linphone_tunnel.h" +#include "lpconfig.h" typedef enum { CAP_IGNORE, @@ -59,6 +60,131 @@ static void linphone_gtk_fill_combo_box(GtkWidget *combo, const char **devices, gtk_combo_box_set_active(GTK_COMBO_BOX(combo),active); } +static void linphone_gtk_ldap_load_settings(GtkWidget* param) +{ + GtkWidget *mw = linphone_gtk_get_main_window(); + GtkWidget *pb = (GtkWidget *) g_object_get_data(G_OBJECT(mw), "parameters"); + LpConfig* config = linphone_core_get_config(linphone_gtk_get_core()); + LinphoneDictionary* ldap_conf = lp_config_section_to_dict(config,"ldap"); + GtkEntry* entry; + GtkToggleButton* toggle; + GtkSpinButton* spin; + + + toggle = GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"ldap_use_tls")); + gtk_toggle_button_set_active(toggle, linphone_dictionary_get_int(ldap_conf,"use_tls", 0) ); + + + entry = GTK_ENTRY(linphone_gtk_get_widget(pb,"ldap_server")); + gtk_entry_set_text(entry, linphone_dictionary_get_string(ldap_conf,"server", "ldap://example.com") ); + + entry = GTK_ENTRY(linphone_gtk_get_widget(pb,"ldap_username")); + gtk_entry_set_text(entry, linphone_dictionary_get_string(ldap_conf,"username", "") ); + + entry = GTK_ENTRY(linphone_gtk_get_widget(pb,"ldap_password")); + gtk_entry_set_text(entry, linphone_dictionary_get_string(ldap_conf,"password", "") ); + + // TODO + // GtkComboBox* cbox = GTK_COMBO_BOX(linphone_gtk_get_widget(pb,"ldap_auth_method")); + // gtk_combo_box_set_active(entry, linphone_dictionary_get_string(ldap_conf,"auth_method", "anonymous") ); + + entry = GTK_ENTRY(linphone_gtk_get_widget(pb,"ldap_base_object")); + gtk_entry_set_text(entry, linphone_dictionary_get_string(ldap_conf,"base_object", "dc=example,dc=com") ); + + entry = GTK_ENTRY(linphone_gtk_get_widget(pb,"ldap_filter")); + gtk_entry_set_text(entry, linphone_dictionary_get_string(ldap_conf,"filter", "uid=*%s*") ); + + entry = GTK_ENTRY(linphone_gtk_get_widget(pb,"ldap_name_attribute")); + gtk_entry_set_text(entry, linphone_dictionary_get_string(ldap_conf,"name_attribute", "cn") ); + + entry = GTK_ENTRY(linphone_gtk_get_widget(pb,"ldap_sip_attribute")); + gtk_entry_set_text(entry, linphone_dictionary_get_string(ldap_conf,"sip_attribute", "mobile") ); + + entry = GTK_ENTRY(linphone_gtk_get_widget(pb,"ldap_attributes")); + gtk_entry_set_text(entry, linphone_dictionary_get_string(ldap_conf,"attributes", "cn,givenName,sn,mobile,homePhone") ); + + + toggle = GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"ldap_deref_aliases")); + gtk_toggle_button_set_active(toggle, linphone_dictionary_get_int(ldap_conf,"deref_aliases", 0) ); + + spin = GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb,"ldap_max_results")); + gtk_spin_button_set_value(spin, linphone_dictionary_get_int(ldap_conf,"max_results", 50) ); + + spin = GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb,"ldap_timeout")); + gtk_spin_button_set_value(spin, linphone_dictionary_get_int(ldap_conf,"timeout", 10) ); + +} + +void linphone_gtk_ldap_reset(GtkWidget *tabmgr) +{ + GtkWidget *mw = linphone_gtk_get_main_window(); + GtkWidget *pb = (GtkWidget *) g_object_get_data(G_OBJECT(mw), "parameters"); + ms_message("RESET LDAP"); + linphone_gtk_ldap_load_settings(pb); +} + +void linphone_gtk_ldap_save(GtkWidget *tabmgr) +{ + LinphoneCore *lc = linphone_gtk_get_core(); + LpConfig* conf = linphone_core_get_config(lc); + LinphoneDictionary* dict = linphone_dictionary_new(); + + GtkWidget *mw = linphone_gtk_get_main_window(); + GtkWidget *pb = (GtkWidget *) g_object_get_data(G_OBJECT(mw), "parameters"); + GtkEntry* entry; + GtkToggleButton* toggle; + GtkSpinButton* spin; + + ms_message("SAVE LDAP"); + + toggle = GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"ldap_use_tls")); + linphone_dictionary_set_int(dict, "use_tls", gtk_toggle_button_get_active(toggle)); + + + entry = GTK_ENTRY(linphone_gtk_get_widget(pb,"ldap_server")); + linphone_dictionary_set_string(dict, "server", gtk_entry_get_text(entry)); + + entry = GTK_ENTRY(linphone_gtk_get_widget(pb,"ldap_username")); + linphone_dictionary_set_string(dict, "username", gtk_entry_get_text(entry)); + + entry = GTK_ENTRY(linphone_gtk_get_widget(pb,"ldap_password")); + linphone_dictionary_set_string(dict, "password", gtk_entry_get_text(entry)); + + + GtkComboBox* cbox = GTK_COMBO_BOX(linphone_gtk_get_widget(pb,"ldap_auth_method")); + linphone_dictionary_set_string(dict, "auth_method", gtk_combo_box_get_active_text(cbox)); + + entry = GTK_ENTRY(linphone_gtk_get_widget(pb,"ldap_base_object")); + linphone_dictionary_set_string(dict, "base_object", gtk_entry_get_text(entry)); + + entry = GTK_ENTRY(linphone_gtk_get_widget(pb,"ldap_filter")); + linphone_dictionary_set_string(dict, "filter", gtk_entry_get_text(entry)); + + entry = GTK_ENTRY(linphone_gtk_get_widget(pb,"ldap_name_attribute")); + linphone_dictionary_set_string(dict, "name_attribute", gtk_entry_get_text(entry)); + + entry = GTK_ENTRY(linphone_gtk_get_widget(pb,"ldap_sip_attribute")); + linphone_dictionary_set_string(dict, "sip_attribute", gtk_entry_get_text(entry)); + + entry = GTK_ENTRY(linphone_gtk_get_widget(pb,"ldap_attributes")); + linphone_dictionary_set_string(dict, "attributes", gtk_entry_get_text(entry)); + + toggle = GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"ldap_deref_aliases")); + linphone_dictionary_set_int(dict, "deref_aliases", gtk_toggle_button_get_active(toggle)); + + spin = GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb,"ldap_max_results")); + linphone_dictionary_set_int(dict, "max_results", gtk_spin_button_get_value(spin) ); + + spin = GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb,"ldap_timeout")); + linphone_dictionary_set_int(dict, "timeout", gtk_spin_button_get_value(spin) ); + + ms_message("Create LDAP from config"); + // create new LDAP according to the validated config + linphone_gtk_set_ldap( linphone_ldap_contact_provider_create(lc, dict) ); + // save the config to linphonerc: + lp_config_load_dict_to_section(conf, "ldap", dict); +} + void linphone_gtk_fill_video_sizes(GtkWidget *combo){ const MSVideoSizeDef *def=linphone_core_get_supported_video_sizes(linphone_gtk_get_core());; int i,active=0; @@ -1301,6 +1427,15 @@ void linphone_gtk_show_parameters(void){ gtk_widget_set_visible(GTK_WIDGET(linphone_gtk_get_widget(pb,"tunnel_label")), TRUE); } + /* LDAP CONFIG */ +#ifdef BUILD_LDAP + linphone_gtk_ldap_load_settings(pb); +#else + // hide the LDAP tab + GtkNotebook* notebook = GTK_NOTEBOOK(linphone_gtk_get_widget(pb, "notebook1")); + gtk_notebook_remove_page(notebook,5); +#endif + gtk_widget_show(pb); } diff --git a/tester/Makefile.am b/tester/Makefile.am index e81c85d97..4388ac483 100644 --- a/tester/Makefile.am +++ b/tester/Makefile.am @@ -23,11 +23,11 @@ liblinphone_tester_SOURCES= liblinphone_tester.c liblinphone_tester.h\ AM_CPPFLAGS=-I$(top_srcdir)/include -I$(top_srcdir)/coreapi -LDADD=$(top_builddir)/coreapi/liblinphone.la +LDADD=$(top_builddir)/coreapi/liblinphone.la $(BELLESIP_LIBS) AM_LDFLAGS=$(CUNIT_LIBS) -AM_CFLAGS=$(STRICT_OPTIONS) -DIN_LINPHONE $(ORTP_CFLAGS) $(MEDIASTREAMER_CFLAGS) $(CUNIT_CFLAGS) +AM_CFLAGS=$(STRICT_OPTIONS) -DIN_LINPHONE $(ORTP_CFLAGS) $(MEDIASTREAMER_CFLAGS) $(CUNIT_CFLAGS) $(BELLESIP_CFLAGS) test: liblinphone_tester ./liblinphone_tester --config $(abs_srcdir) diff --git a/tester/call_tester.c b/tester/call_tester.c index b166cb86e..615886606 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -1053,7 +1053,6 @@ static void early_media_call_forking(void) { linphone_core_use_files (marie1->lc,TRUE); linphone_core_set_play_file(marie1->lc,ringbackpath); - linphone_core_enable_video(marie2->lc,TRUE,TRUE); linphone_core_set_video_policy(marie2->lc,&pol); linphone_core_set_audio_port_range(marie2->lc,40200,40300);