diff options
-rw-r--r-- | ChangeLog | 16 | ||||
-rw-r--r-- | addressbook/backend/pas/Makefile.am | 5 | ||||
-rw-r--r-- | addressbook/backend/pas/pas-backend-ldap.c | 578 | ||||
-rw-r--r-- | configure.in | 15 | ||||
-rw-r--r-- | wombat/Makefile.am | 2 | ||||
-rw-r--r-- | wombat/wombat.c | 8 |
6 files changed, 538 insertions, 86 deletions
@@ -1,3 +1,19 @@ +2000-04-11 Chris Toshok <toshok@helixcode.com> + + * wombat/Makefile.am (wombat_LDADD): add LDAP_LIBS here. + + * configure.in: check for -lldap and -llber and if both are + present include ldap support in the pas/wombat. + + * addressbook/backend/pas/Makefile.am (libpas_la_SOURCES): include + pas-backend.ldap.c if ENABLE_LDAP. + + * addressbook/backend/pas/pas-backend-ldap.c: get searching + working (converting between the sexp and ldap stuff.) + + * wombat/wombat.c (setup_pas): register the ldap pas backend if + HAVE_LDAP is defined. + 2000-04-11 Christopher James Lahey <clahey@helixcode.com> * configure.in: Changed AC_DEFUN to AC_DEFINE. diff --git a/addressbook/backend/pas/Makefile.am b/addressbook/backend/pas/Makefile.am index dc2c4b5beb..dfaf7aae54 100644 --- a/addressbook/backend/pas/Makefile.am +++ b/addressbook/backend/pas/Makefile.am @@ -34,12 +34,17 @@ gnome_libs = \ $(GNOMEGNORBA_LIBS) \ $(INTLLIBS) +if ENABLE_LDAP +LDAP_BACKEND = pas-backend-ldap.c +endif + libpas_la_SOURCES = \ $(CORBA_SOURCE) \ pas-book-factory.c \ pas-book-view.c \ pas-book.c \ pas-backend-file.c \ + $(LDAP_BACKEND) \ pas-backend.c \ pas-card-cursor.c diff --git a/addressbook/backend/pas/pas-backend-ldap.c b/addressbook/backend/pas/pas-backend-ldap.c index 0f95aa5484..c80fbd388b 100644 --- a/addressbook/backend/pas/pas-backend-ldap.c +++ b/addressbook/backend/pas/pas-backend-ldap.c @@ -17,13 +17,20 @@ #include "pas-book.h" #include "pas-card-cursor.h" +#include <e-sexp.h> +#include <e-card.h> + static PASBackendClass *pas_backend_ldap_parent_class; typedef struct _PASBackendLDAPCursorPrivate PASBackendLDAPCursorPrivate; +typedef struct _PASBackendLDAPBookView PASBackendLDAPBookView; struct _PASBackendLDAPPrivate { gboolean connected; GList *clients; LDAP *ldap; + gchar *ldap_host; + int ldap_port; + GList *book_views; }; struct _PASBackendLDAPCursorPrivate { @@ -31,7 +38,12 @@ struct _PASBackendLDAPCursorPrivate { PASBook *book; GList *elements; - int num_elements; + long num_elements; +}; + +struct _PASBackendLDAPBookView { + PASBookView *book_view; + gchar *search; }; static long @@ -74,48 +86,78 @@ cursor_destroy(GtkObject *object, gpointer data) g_free(cursor_data); } -static char * -pas_backend_ldap_create_unique_id (char *vcard) +static void +view_destroy(GtkObject *object, gpointer data) { - /* use a 32 counter and the 32 bit timestamp to make an id. - it's doubtful 2^32 id's will be created in a second, so we - should be okay. */ - static guint c = 0; - return g_strdup_printf ("pas-id-%08lX%08X", time(NULL), c++); + CORBA_Environment ev; + Evolution_Book corba_book; + PASBook *book = (PASBook *)data; + PASBackendLDAP *bl; + GList *list; + + bl = PAS_BACKEND_LDAP(pas_book_get_backend(book)); + for (list = bl->priv->book_views; list; list = g_list_next(list)) { + PASBackendLDAPBookView *view = list->data; + if (view->book_view == PAS_BOOK_VIEW(object)) { + g_free (view->search); + g_free (view); + bl->priv->book_views = g_list_remove_link(bl->priv->book_views, list); + g_list_free_1(list); + break; + } + } + + corba_book = bonobo_object_corba_objref(BONOBO_OBJECT(book)); + + CORBA_exception_init(&ev); + + Evolution_Book_unref(corba_book, &ev); + + if (ev._major != CORBA_NO_EXCEPTION) { + g_warning("view_destroy: Exception unreffing " + "corba book.\n"); + } + + CORBA_exception_free(&ev); } static void -pas_backend_ldap_process_create_card (PASBackend *backend, - PASBook *book, - PASRequest *req) +pas_backend_ldap_ensure_connected (PASBackendLDAP *bl) { - PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); LDAP *ldap = bl->priv->ldap; int ldap_error; - char *id; - id = pas_backend_ldap_create_unique_id (req->vcard); + /* the connection has gone down, or wasn't ever opened */ + if (ldap == NULL || + (ldap_error = ldap_simple_bind_s(ldap, NULL /*binddn*/, NULL /*passwd*/)) != LDAP_SUCCESS) { - /* XXX use ldap_add_s */ + /* close connection first if it's open first */ + if (ldap) + ldap_unbind (ldap); - if (LDAP_SUCCESS == ldap_error) { - pas_book_notify_add(book, id); + bl->priv->ldap = ldap_open (bl->priv->ldap_host, bl->priv->ldap_port); + if (NULL != bl->priv->ldap) + bl->priv->connected = TRUE; + else + g_warning ("pas_backend_ldap_ensure_connected failed for 'ldap://%s:%d/' (error %s)\n", + bl->priv->ldap_host, bl->priv->ldap_port, ldap_err2string(ldap_error)); - pas_book_respond_create ( - book, - Evolution_BookListener_Success, - id); + ldap_simple_bind_s(bl->priv->ldap, NULL /*binddn*/, NULL /*passwd*/); } - else { - /* XXX need a different call status for this case, i - think */ - pas_book_respond_create ( +} + +static void +pas_backend_ldap_process_create_card (PASBackend *backend, + PASBook *book, + PASRequest *req) +{ + g_warning ("pas_backend_ldap_process_create_card not implemented\n"); + + pas_book_respond_create ( book, Evolution_BookListener_CardNotFound, ""); - } - g_free (id); g_free (req->vcard); } @@ -124,24 +166,11 @@ pas_backend_ldap_process_remove_card (PASBackend *backend, PASBook *book, PASRequest *req) { - PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); - LDAP *ldap = bl->priv->ldap; - int ldap_error; - - /* XXX use ldap_delete_s */ - - if (LDAP_SUCCESS == ldap_error) { - pas_book_notify_remove (book, req->id); + g_warning ("pas_backend_ldap_process_remove_card not implemented\n"); - pas_book_respond_remove ( - book, - Evolution_BookListener_Success); - } - else { - pas_book_respond_remove ( + pas_book_respond_remove ( book, Evolution_BookListener_CardNotFound); - } g_free (req->id); } @@ -156,7 +185,9 @@ pas_backend_ldap_build_all_cards_list(PASBackend *backend, LDAPMessage *res, *e; - if (ldap_search_s (ldap, NULL, LDAP_SCOPE_ONELEVEL, + if (ldap_search_s (ldap, + NULL, + LDAP_SCOPE_ONELEVEL, "(objectclass=*)", NULL, 0, &res) == -1) { ldap_perror (ldap, "ldap_search"); @@ -191,33 +222,18 @@ pas_backend_ldap_process_modify_card (PASBackend *backend, PASBook *book, PASRequest *req) { - PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); - LDAP *ldap = bl->priv->ldap; - int ldap_error; - - /* XXX use ldap_modify_s */ - - if (LDAP_SUCCESS == ldap_error) { + g_warning ("pas_backend_ldap_process_modify_card not implemented\n"); - pas_book_notify_change (book, req->id); - - pas_book_respond_modify ( - book, - Evolution_BookListener_Success); - } - else { - pas_book_respond_modify ( + pas_book_respond_modify ( book, Evolution_BookListener_CardNotFound); - } - g_free (req->vcard); } static void -pas_backend_ldap_process_get_all_cards (PASBackend *backend, - PASBook *book, - PASRequest *req) +pas_backend_ldap_process_get_cursor (PASBackend *backend, + PASBook *book, + PASRequest *req) { CORBA_Environment ev; PASBackendLDAPCursorPrivate *cursor_data; @@ -238,7 +254,7 @@ pas_backend_ldap_process_get_all_cards (PASBackend *backend, Evolution_Book_ref(corba_book, &ev); if (ev._major != CORBA_NO_EXCEPTION) { - g_warning("pas_backend_file_process_get_all_cards: Exception reffing " + g_warning("pas_backend_file_process_get_cursor: Exception reffing " "corba book.\n"); } @@ -259,6 +275,406 @@ pas_backend_ldap_process_get_all_cards (PASBackend *backend, cursor); } +static gchar * +map_e_card_prop_to_ldap(gchar *e_card_prop) +{ + if (!strcmp(e_card_prop, "full_name")) return "cn"; + else if (!strcmp(e_card_prop, "email")) return "mail"; + else return NULL; +} + +static gchar * +map_ldap_to_e_card_prop(gchar *ldap_attr) +{ + if (!strcmp(ldap_attr, "cn")) return "full_name"; + else return NULL; +} + +static ESExpResult * +func_and(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data) +{ + GList **list = data; + ESExpResult *r; + char ** strings; + + if (argc > 0) { + int i; + + strings = g_new(char*, argc+3); + strings[0] = g_strdup ("(&"); + strings[argc+3 - 2] = g_strdup (")"); + strings[argc+3 - 1] = NULL; + + for (i = 0; i < argc; i ++) { + GList *list_head = *list; + strings[argc - i] = (*list)->data; + *list = g_list_remove_link(*list, *list); + g_list_free_1(list_head); + } + + *list = g_list_prepend(*list, g_strjoinv(" ", strings)); + + for (i = 0 ; i < argc + 2; i ++) + g_free (strings[i]); + + g_free (strings); + } + + r = e_sexp_result_new(ESEXP_RES_BOOL); + r->value.bool = FALSE; + + return r; +} + +static ESExpResult * +func_or(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data) +{ + GList **list = data; + ESExpResult *r; + char ** strings; + + if (argc > 0) { + int i; + + strings = g_new(char*, argc+3); + strings[0] = g_strdup ("(|"); + strings[argc+3 - 2] = g_strdup (")"); + strings[argc+3 - 1] = NULL; + for (i = 0; i < argc; i ++) { + GList *list_head = *list; + strings[argc - i] = (*list)->data; + *list = g_list_remove_link(*list, *list); + g_list_free_1(list_head); + } + + *list = g_list_prepend(*list, g_strjoinv(" ", strings)); + + for (i = 0 ; i < argc + 2; i ++) + g_free (strings[i]); + + g_free (strings); + } + + r = e_sexp_result_new(ESEXP_RES_BOOL); + r->value.bool = FALSE; + + return r; +} + +static ESExpResult * +func_not(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data) +{ + GList **list = data; + ESExpResult *r; + + /* just replace the head of the list with the NOT of it. */ + if (argc > 0) { + char *term = (*list)->data; + (*list)->data = g_strdup_printf("(!%s)", term); + g_free (term); + } + + r = e_sexp_result_new(ESEXP_RES_BOOL); + r->value.bool = FALSE; + + return r; +} + +static ESExpResult * +func_contains(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data) +{ + GList **list = data; + ESExpResult *r; + + if (argc == 2 + && argv[0]->type == ESEXP_RES_STRING + && argv[1]->type == ESEXP_RES_STRING) { + char *propname = argv[0]->value.string; + char *str = argv[1]->value.string; + char *ldap_attr = map_e_card_prop_to_ldap(propname); + gboolean one_star = FALSE; + + if (strlen(str) == 0) + one_star = TRUE; + + if (ldap_attr) + *list = g_list_prepend(*list, + g_strdup_printf("(%s=*%s%s)", + ldap_attr, + str, + one_star ? "" : "*")); + } + + r = e_sexp_result_new(ESEXP_RES_BOOL); + r->value.bool = FALSE; + + return r; +} + +static ESExpResult * +func_is(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data) +{ + GList **list = data; + ESExpResult *r; + + if (argc == 2 + && argv[0]->type == ESEXP_RES_STRING + && argv[1]->type == ESEXP_RES_STRING) { + char *propname = argv[0]->value.string; + char *str = argv[1]->value.string; + char *ldap_attr = map_e_card_prop_to_ldap(propname); + + if (ldap_attr) + *list = g_list_prepend(*list, + g_strdup_printf("(%s=%s)", + ldap_attr, str)); + } + + r = e_sexp_result_new(ESEXP_RES_BOOL); + r->value.bool = FALSE; + + return r; +} + +static ESExpResult * +func_beginswith(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data) +{ + GList **list = data; + ESExpResult *r; + + if (argc == 2 + && argv[0]->type == ESEXP_RES_STRING + && argv[1]->type == ESEXP_RES_STRING) { + char *propname = argv[0]->value.string; + char *str = argv[1]->value.string; + char *ldap_attr = map_e_card_prop_to_ldap(propname); + gboolean one_star = FALSE; + + if (strlen(str) == 0) + one_star = TRUE; + + if (ldap_attr) + *list = g_list_prepend(*list, + g_strdup_printf("(%s=%s*)", + ldap_attr, + str)); + } + + r = e_sexp_result_new(ESEXP_RES_BOOL); + r->value.bool = FALSE; + + return r; +} + +static ESExpResult * +func_endswith(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data) +{ + GList **list = data; + ESExpResult *r; + + if (argc == 2 + && argv[0]->type == ESEXP_RES_STRING + && argv[1]->type == ESEXP_RES_STRING) { + char *propname = argv[0]->value.string; + char *str = argv[1]->value.string; + char *ldap_attr = map_e_card_prop_to_ldap(propname); + gboolean one_star = FALSE; + + if (strlen(str) == 0) + one_star = TRUE; + + if (ldap_attr) + *list = g_list_prepend(*list, + g_strdup_printf("(%s=*%s)", + ldap_attr, + str)); + } + + r = e_sexp_result_new(ESEXP_RES_BOOL); + r->value.bool = FALSE; + + return r; +} + +/* 'builtin' functions */ +static struct { + char *name; + ESExpFunc *func; + int type; /* set to 1 if a function can perform shortcut evaluation, or + doesn't execute everything, 0 otherwise */ +} symbols[] = { + { "and", func_and, 0 }, + { "or", func_or, 0 }, + { "not", func_not, 0 }, + { "contains", func_contains, 0 }, + { "is", func_is, 0 }, + { "beginswith", func_beginswith, 0 }, + { "endswith", func_endswith, 0 }, +}; + +static gchar * +pas_backend_ldap_build_query (gchar *query) +{ + ESExp *sexp; + ESExpResult *r; + gchar *retval; + GList *list = NULL; + int i; + + sexp = e_sexp_new(); + + for(i=0;i<sizeof(symbols)/sizeof(symbols[0]);i++) { + if (symbols[i].type == 1) { + e_sexp_add_ifunction(sexp, 0, symbols[i].name, + (ESExpIFunc *)symbols[i].func, &list); + } else { + e_sexp_add_function(sexp, 0, symbols[i].name, + symbols[i].func, &list); + } + } + + e_sexp_input_text(sexp, query, strlen(query)); + e_sexp_parse(sexp); + + r = e_sexp_eval(sexp); + + gtk_object_unref(GTK_OBJECT(sexp)); + e_sexp_result_free(r); + + if (list->next) { + g_warning ("conversion to ldap query string failed"); + retval = NULL; + g_list_foreach (list, (GFunc)g_free, NULL); + } + else { + retval = list->data; + } + + g_list_free (list); + return retval; +} + +static void +pas_backend_ldap_search (PASBackendLDAP *bl, + PASBook *book, + PASBackendLDAPBookView *view) +{ + char *ldap_query = pas_backend_ldap_build_query(view->search); + GList *cards = NULL; + + if (ldap_query != NULL) { + LDAP *ldap = bl->priv->ldap; + int ldap_error; + LDAPMessage *res, *e; + + pas_backend_ldap_ensure_connected(bl); + + if ((ldap_error = ldap_search_s (ldap, + NULL, + LDAP_SCOPE_ONELEVEL, + ldap_query, + NULL, 0, &res)) != LDAP_SUCCESS) { + ldap_perror (ldap, "ldap_search"); + } + + e = ldap_first_entry(ldap, res); + + while (NULL != e) { + ECard *card = E_CARD(gtk_type_new(e_card_get_type())); + char *dn = ldap_get_dn(ldap, e); + char *attr, *prop; + BerElement *ber = NULL; + + e_card_set_id (card, dn); + + /* XXX needs a bit of work here */ + for (attr = ldap_first_attribute (ldap, e, &ber); attr; + attr = ldap_next_attribute (ldap, e, ber)) { + prop = map_ldap_to_e_card_prop (attr); + + if (prop) { + char **values; + values = ldap_get_values (ldap, e, attr); + + gtk_object_set(GTK_OBJECT(card), prop, values[0], NULL); + + ldap_value_free (values); + } + } + + if (ber) + ber_free (ber, 0); + + cards = g_list_append(cards, e_card_get_vcard(card)); + + gtk_object_unref (GTK_OBJECT(card)); + + e = ldap_next_entry(ldap, e); + } + + ldap_msgfree(res); + + if (cards) { + pas_book_view_notify_add (view->book_view, cards); + + /* + ** It's fine to do this now since the data has been handed off. + */ + g_list_foreach (cards, (GFunc)g_free, NULL); + g_list_free (cards); + } + + } +} + +static void +pas_backend_ldap_process_get_book_view (PASBackend *backend, + PASBook *book, + PASRequest *req) +{ + PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); + CORBA_Environment ev; + int db_error = 0; + Evolution_Book corba_book; + PASBookView *book_view; + PASBackendLDAPBookView *view; + + g_return_if_fail (req->listener != NULL); + + corba_book = bonobo_object_corba_objref(BONOBO_OBJECT(book)); + + CORBA_exception_init(&ev); + + Evolution_Book_ref(corba_book, &ev); + + if (ev._major != CORBA_NO_EXCEPTION) { + g_warning("pas_backend_file_process_get_book_view: Exception reffing " + "corba book.\n"); + } + + CORBA_exception_free(&ev); + + book_view = pas_book_view_new (req->listener); + + gtk_signal_connect(GTK_OBJECT(book_view), "destroy", + GTK_SIGNAL_FUNC(view_destroy), book); + + pas_book_respond_get_book_view (book, + (book_view != NULL + ? Evolution_BookListener_Success + : Evolution_BookListener_CardNotFound /* XXX */), + book_view); + + view = g_new(PASBackendLDAPBookView, 1); + view->book_view = book_view; + view->search = g_strdup(req->search); + + bl->priv->book_views = g_list_prepend(bl->priv->book_views, view); + + pas_backend_ldap_search (bl, book, view); + +} + static void pas_backend_ldap_process_check_connection (PASBackend *backend, PASBook *book, @@ -297,9 +713,13 @@ pas_backend_ldap_process_client_requests (PASBook *book) case CheckConnection: pas_backend_ldap_process_check_connection (backend, book, req); break; - - case GetAllCards: - pas_backend_ldap_process_get_all_cards (backend, book, req); + + case GetCursor: + pas_backend_ldap_process_get_cursor (backend, book, req); + break; + + case GetBookView: + pas_backend_ldap_process_get_book_view (backend, book, req); break; } @@ -347,33 +767,19 @@ pas_backend_ldap_load_uri (PASBackend *backend, g_assert (bl->priv->connected == FALSE); -#if 0 ldap_error = ldap_url_parse (uri, &lud); if (LDAP_SUCCESS == ldap_error) { - bl->priv->ldap = ldap_open (lud->lud_host, lud->lud_port); - if (NULL != bl->priv->ldap) - bl->priv->connected = TRUE; - else - g_warning ("pas_backend_ldap_load_uri failed for '%s' (error %s)\n", - uri, ldap_err2string(ldap_error)); + bl->priv->ldap_host = g_strdup(lud->lud_host); + bl->priv->ldap_port = lud->lud_port; ldap_free_urldesc(lud); + + pas_backend_ldap_ensure_connected(bl); } else { g_warning ("pas_backend_ldap_load_uri failed for '%s' (error %s)\n", uri, ldap_err2string(ldap_error)); } -#else - bl->priv->ldap = ldap_init ("ldap.bigfoot.com", 389); - if (NULL != bl->priv->ldap) - bl->priv->connected = TRUE; - else - g_warning ("pas_backend_ldap_load_uri failed for '%s' (error %s)\n", - uri, ldap_err2string(ldap_error)); - - ldap_bind_s(bl->priv->ldap, NULL /*binddn*/, NULL /*passwd*/, LDAP_AUTH_SIMPLE); - -#endif } static void @@ -453,6 +859,8 @@ pas_backend_ldap_new (void) return NULL; } + backend->priv->ldap = NULL; + return PAS_BACKEND (backend); } diff --git a/configure.in b/configure.in index d057649280..8dbd3190c0 100644 --- a/configure.in +++ b/configure.in @@ -89,6 +89,21 @@ dnl ************************************************** AC_CHECK_HEADERS(db_185.h) dnl ************************************************** +dnl * ldap related stuff. +dnl ************************************************** +AC_CHECK_LIB(lber, ber_get_tag, LDAP_LIBS="-llber") +if test $ac_cv_lib_lber_ber_get_tag = yes; then +AC_CHECK_LIB(ldap, ldap_open, LDAP_LIBS="-lldap $LDAP_LIBS", ,-llber) +fi +if test $ac_cv_lib_ldap_ldap_open = yes; then + AC_DEFINE(HAVE_LDAP) + AC_SUBST(LDAP_LIBS) +else + AC_MSG_WARN(no ldap support present) +fi +AM_CONDITIONAL(ENABLE_LDAP, test $ac_cv_lib_ldap_ldap_open = yes) + +dnl ************************************************** dnl * Posix thread support dnl ************************************************** diff --git a/wombat/Makefile.am b/wombat/Makefile.am index 6ab086d667..002ce7d355 100644 --- a/wombat/Makefile.am +++ b/wombat/Makefile.am @@ -25,7 +25,7 @@ wombat_LDADD = \ $(top_builddir)/calendar/cal-util/libcalutil.la \ $(top_builddir)/libversit/libversit.la \ $(top_builddir)/e-util/libeutil.la \ - -lgnomevfs + -lgnomevfs $(LDAP_LIBS) wombat_LDFLAGS = `gnome-config --libs gnorba` diff --git a/wombat/wombat.c b/wombat/wombat.c index 5e4ad25173..989df9b484 100644 --- a/wombat/wombat.c +++ b/wombat/wombat.c @@ -9,6 +9,9 @@ #include <bonobo.h> #include <pas-book-factory.h> #include <pas-backend-file.h> +#ifdef HAVE_LDAP +#include <pas-backend-ldap.h> +#endif #include <libgnomevfs/gnome-vfs-init.h> #include <libgnorba/gnorba.h> #include <cal-factory.h> @@ -27,6 +30,11 @@ setup_pas (int argc, char **argv) pas_book_factory_register_backend ( factory, "file", pas_backend_file_new); +#ifdef HAVE_LDAP + pas_book_factory_register_backend ( + factory, "ldap", pas_backend_ldap_new); +#endif + pas_book_factory_activate (factory); } |