diff options
-rw-r--r-- | addressbook/ChangeLog | 11 | ||||
-rw-r--r-- | addressbook/backend/pas/pas-backend-ldap.c | 608 |
2 files changed, 471 insertions, 148 deletions
diff --git a/addressbook/ChangeLog b/addressbook/ChangeLog index c2c3e98801..f7835575b2 100644 --- a/addressbook/ChangeLog +++ b/addressbook/ChangeLog @@ -1,3 +1,14 @@ +2000-09-15 Chris Toshok <toshok@helixcode.com> + + * backend/pas/pas-backend-ldap.c: split all the ldap operations + into 2 halves, a handler, and destructor, and create a structure + containing two function pointers and any data they need. this + allows us queue up pending operations (since the LDAP*'s are no + longer view specific. there's one per backend.) also, add + support for restarting async operations if the SERVER DOWN error + isn't communicated until sometime after the handler is called (as + is the case with the async search stuff.) + 2000-09-14 Dan Winship <danw@helixcode.com> * gui/component/addressbook-factory.c (main): Call unicode_init diff --git a/addressbook/backend/pas/pas-backend-ldap.c b/addressbook/backend/pas/pas-backend-ldap.c index 3968648972..08fe3ef773 100644 --- a/addressbook/backend/pas/pas-backend-ldap.c +++ b/addressbook/backend/pas/pas-backend-ldap.c @@ -27,6 +27,7 @@ static gchar *query_prop_to_ldap(gchar *query_prop); static PASBackendClass *pas_backend_ldap_parent_class; typedef struct _PASBackendLDAPCursorPrivate PASBackendLDAPCursorPrivate; typedef struct _PASBackendLDAPBookView PASBackendLDAPBookView; +typedef struct LDAPOp LDAPOp; struct _PASBackendLDAPPrivate { char *uri; @@ -37,6 +38,13 @@ struct _PASBackendLDAPPrivate { int ldap_port; int ldap_scope; GList *book_views; + + LDAP *ldap; + + /* whether or not there's a request in process on our LDAP* */ + LDAPOp *current_op; + GList *pending_ops; + int op_idle; }; struct _PASBackendLDAPCursorPrivate { @@ -50,51 +58,28 @@ struct _PASBackendLDAPCursorPrivate { struct _PASBackendLDAPBookView { PASBookView *book_view; PASBackendLDAPPrivate *blpriv; - LDAP *ldap; gchar *search; int search_idle; int search_msgid; + LDAPOp *search_op; }; -static long -get_length(PASCardCursor *cursor, gpointer data) -{ - PASBackendLDAPCursorPrivate *cursor_data = (PASBackendLDAPCursorPrivate *) data; +typedef gboolean (*LDAPOpHandler)(PASBackend *backend, LDAPOp *op); +typedef void (*LDAPOpDtor)(PASBackend *backend, LDAPOp *op); - return cursor_data->num_elements; -} - -static char * -get_nth(PASCardCursor *cursor, long n, gpointer data) -{ - return g_strdup(""); -} - -static void -cursor_destroy(GtkObject *object, gpointer data) -{ - CORBA_Environment ev; - Evolution_Book corba_book; - PASBackendLDAPCursorPrivate *cursor_data = (PASBackendLDAPCursorPrivate *) data; - - corba_book = bonobo_object_corba_objref(BONOBO_OBJECT(cursor_data->book)); - - CORBA_exception_init(&ev); - - Evolution_Book_unref(corba_book, &ev); - - if (ev._major != CORBA_NO_EXCEPTION) { - g_warning("cursor_destroy: Exception unreffing " - "corba book.\n"); - } - - CORBA_exception_free(&ev); - - /* free the ldap specific cursor information */ +struct LDAPOp { + LDAPOpHandler handler; + LDAPOpDtor dtor; + PASBackend *backend; + PASBook *book; +}; - - g_free(cursor_data); -} +static void ldap_op_init (LDAPOp *op, PASBackend *backend, PASBook *book, LDAPOpHandler handler, LDAPOpDtor dtor); +static void ldap_op_process_current (PASBackend *backend); +static void ldap_op_process (LDAPOp *op); +static void ldap_op_restart (LDAPOp *op); +static gboolean ldap_op_process_on_idle (PASBackend *backend); +static void ldap_op_finished (LDAPOp *op); static void view_destroy(GtkObject *object, gpointer data) @@ -109,10 +94,26 @@ view_destroy(GtkObject *object, gpointer data) for (list = bl->priv->book_views; list; list = g_list_next(list)) { PASBackendLDAPBookView *view = list->data; if (view->book_view == PAS_BOOK_VIEW(object)) { - if (view->search_idle != 0) + if (view->search_idle != 0) { + /* we have a search running on the + ldap connection. remove the idle + handler and anbandon the msg id */ g_source_remove(view->search_idle); - if (view->ldap) - ldap_unbind (view->ldap); + printf ("abandoning search id %d\n", view->search_msgid); + ldap_abandon (bl->priv->ldap, view->search_msgid); + + /* if the search op is the current op, + finish it. else, remove it from the + list and nuke it ourselves. */ + if (view->search_op == bl->priv->current_op) + ldap_op_finished (view->search_op); + else { + bl->priv->pending_ops = g_list_remove (bl->priv->pending_ops, + view->search_op); + view->search_op->dtor (view->search_op->backend, + view->search_op); + } + } g_free (view->search); g_free (view); bl->priv->book_views = g_list_remove_link(bl->priv->book_views, list); @@ -136,49 +137,194 @@ view_destroy(GtkObject *object, gpointer data) } static void -pas_backend_ldap_connect (PASBackendLDAPBookView *view) +pas_backend_ldap_connect (PASBackendLDAP *bl) { - LDAP *ldap = view->ldap; - PASBackendLDAPPrivate *bl = view->blpriv; - - /* the connection has gone down, or wasn't ever opened */ - if (ldap == NULL || - (ldap_simple_bind_s(ldap, NULL /*binddn*/, NULL /*passwd*/) != LDAP_SUCCESS)) { - - /* close connection first if it's open first */ - if (ldap) - ldap_unbind (ldap); - - view->ldap = ldap_open (bl->ldap_host, bl->ldap_port); - if (NULL != view->ldap) { - ldap_simple_bind_s(view->ldap, - NULL /*binddn*/, NULL /*passwd*/); - bl->connected = TRUE; - } - else { - g_warning ("pas_backend_ldap_connect failed for " - "'ldap://%s:%d/%s'\n", - bl->ldap_host, - bl->ldap_port, - bl->ldap_rootdn ? bl->ldap_rootdn : ""); - bl->connected = FALSE; - } + PASBackendLDAPPrivate *blpriv = bl->priv; + + printf ("connecting to LDAP server\n"); + /* close connection first if it's open first */ + if (blpriv->ldap) + ldap_unbind (blpriv->ldap); + + blpriv->ldap = ldap_open (blpriv->ldap_host, blpriv->ldap_port); + if (NULL != blpriv->ldap) { + ldap_simple_bind_s(blpriv->ldap, + NULL /*binddn*/, NULL /*passwd*/); + blpriv->connected = TRUE; + } + else { + g_warning ("pas_backend_ldap_connect failed for " + "'ldap://%s:%d/%s'\n", + blpriv->ldap_host, + blpriv->ldap_port, + blpriv->ldap_rootdn ? blpriv->ldap_rootdn : ""); + blpriv->connected = FALSE; + } +} + +static void +ldap_op_init (LDAPOp *op, PASBackend *backend, + PASBook *book, LDAPOpHandler handler, LDAPOpDtor dtor) +{ + op->backend = backend; + op->book = book; + op->handler = handler; + op->dtor = dtor; +} + +static void +ldap_op_process_current (PASBackend *backend) +{ + PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); + LDAPOp *op = bl->priv->current_op; + + if (!bl->priv->connected) + pas_backend_ldap_connect(bl); + + if (op->handler (backend, op)) + ldap_op_finished (op); +} + +static void +ldap_op_process (LDAPOp *op) +{ + PASBackendLDAP *bl = PAS_BACKEND_LDAP (op->backend); + + if (bl->priv->current_op) { + /* operation in progress. queue this op for later and return. */ + bl->priv->pending_ops = g_list_append (bl->priv->pending_ops, op); + } + else { + /* nothing going on, do this op now */ + bl->priv->current_op = op; + ldap_op_process_current (op->backend); + } +} + +static gboolean +ldap_op_process_on_idle (PASBackend *backend) +{ + PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); + + bl->priv->op_idle = 0; + + ldap_op_process_current (backend); + + return FALSE; +} + +static void +ldap_op_restart (LDAPOp *op) +{ + PASBackend *backend = op->backend; + PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); + + g_return_if_fail (op == bl->priv->current_op); + + bl->priv->op_idle = g_idle_add((GSourceFunc)ldap_op_process_on_idle, backend); +} + +static void +ldap_op_finished (LDAPOp *op) +{ + PASBackend *backend = op->backend; + PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); + + g_return_if_fail (op == bl->priv->current_op); + + op->dtor (backend, op); + + if (bl->priv->pending_ops) { + bl->priv->current_op = bl->priv->pending_ops->data; + bl->priv->pending_ops = g_list_remove_link (bl->priv->pending_ops, bl->priv->pending_ops); + + bl->priv->op_idle = g_idle_add((GSourceFunc)ldap_op_process_on_idle, backend); + } + else { + bl->priv->current_op = NULL; } } + +typedef struct { + LDAPOp op; + char *vcard; +} LDAPCreateOp; + +static gboolean +create_card_handler (PASBackend *backend, LDAPOp *op) +{ + /* LDAPCreateOp *create_op = (LDAPCreateOp*)op;*/ + + /* we're synchronous */ + return TRUE; +} + +static void +create_card_dtor (PASBackend *backend, LDAPOp *op) +{ + LDAPCreateOp *create_op = (LDAPCreateOp*)op; + + g_free (create_op->vcard); + g_free (create_op); +} + 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"); + LDAPCreateOp *create_op = g_new (LDAPCreateOp, 1); - pas_book_respond_create ( - book, - Evolution_BookListener_CardNotFound, - ""); + ldap_op_init ((LDAPOp*)create_op, backend, book, create_card_handler, create_card_dtor); - g_free (req->vcard); + ldap_op_process ((LDAPOp*)create_op); +} + + +typedef struct { + LDAPOp op; + char *id; +} LDAPRemoveOp; + +static gboolean +remove_card_handler (PASBackend *backend, LDAPOp *op) +{ + LDAPRemoveOp *remove_op = (LDAPRemoveOp*)op; + int response = Evolution_BookListener_Success; + +#if notyet + PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); + int ldap_error; + + ldap_error = ldap_delete_s (bl->priv->ldap, req->id); + + if (ldap_error != SUCCESS) { + if (NAME_ERROR (ldap_error)) + response = Evolution_BookListener_CardNotFound; + else if (ldap_error == LDAP_INSUFFICIENT_ACCESS) + response = Evolution_BookListener_PermissionDenied; + else if (ldap_error == ) + response = Evolution_BookListener_RepositoryOffline; + else + response = Evolution_BookListener_OtherError; + } +#endif + + pas_book_respond_remove (remove_op->op.book, + response); + + /* we're synchronous */ + return TRUE; +} + +static void +remove_card_dtor (PASBackend *backend, LDAPOp *op) +{ + LDAPRemoveOp *remove_op = (LDAPRemoveOp*)op; + + g_free (remove_op->id); + g_free (remove_op); } static void @@ -186,39 +332,126 @@ pas_backend_ldap_process_remove_card (PASBackend *backend, PASBook *book, PASRequest *req) { - g_warning ("pas_backend_ldap_process_remove_card not implemented\n"); + LDAPRemoveOp *remove_op = g_new (LDAPRemoveOp, 1); - pas_book_respond_remove ( - book, - Evolution_BookListener_CardNotFound); + ldap_op_init ((LDAPOp*)remove_op, backend, book, remove_card_handler, remove_card_dtor); + + remove_op->id = req->id; - g_free (req->id); + ldap_op_process ((LDAPOp*)remove_op); } -static void -pas_backend_ldap_build_all_cards_list(PASBackend *backend, - PASBackendLDAPCursorPrivate *cursor_data) + +typedef struct { + LDAPOp op; + char *vcard; +} LDAPModifyOp; + +static gboolean +modify_card_handler (PASBackend *backend, LDAPOp *op) { + LDAPModifyOp *modify_op = (LDAPModifyOp*)op; + +#if 0 PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); LDAP *ldap; int ldap_error; LDAPMessage *res, *e; - ldap = ldap_open (bl->priv->ldap_host, bl->priv->ldap_port); - if (NULL != ldap) { - ldap_simple_bind_s(ldap, NULL /*binddn*/, NULL /*passwd*/); - } - else { - g_warning ("pas_backend_ldap_build_all_cards_list: ldap_open failed for " - "'ldap://%s:%d/%s'\n", - bl->priv->ldap_host, - bl->priv->ldap_port, - bl->priv->ldap_rootdn ? bl->priv->ldap_rootdn : ""); - return; + ldap = bl->ldap; + + ldap_modify_s () +#endif + pas_book_respond_modify ( + modify_op->op.book, + Evolution_BookListener_CardNotFound); + + /* we're synchronous */ + return TRUE; +} + +static void +modify_card_dtor (PASBackend *backend, LDAPOp *op) +{ + LDAPModifyOp *modify_op = (LDAPModifyOp*)op; + + g_free (modify_op->vcard); + g_free (modify_op); +} + +static void +pas_backend_ldap_process_modify_card (PASBackend *backend, + PASBook *book, + PASRequest *req) +{ + LDAPModifyOp *modify_op = g_new (LDAPModifyOp, 1); + + ldap_op_init ((LDAPOp*)modify_op, backend, book, modify_card_handler, modify_card_dtor); + + modify_op->vcard = req->vcard; + + ldap_op_process ((LDAPOp*)modify_op); +} + + +typedef struct { + LDAPOp op; + PASBook *book; +} LDAPGetCursorOp; + +static long +get_length(PASCardCursor *cursor, gpointer data) +{ + PASBackendLDAPCursorPrivate *cursor_data = (PASBackendLDAPCursorPrivate *) data; + + return cursor_data->num_elements; +} + +static char * +get_nth(PASCardCursor *cursor, long n, gpointer data) +{ + PASBackendLDAPCursorPrivate *cursor_data = (PASBackendLDAPCursorPrivate *) data; + + g_return_val_if_fail (n < cursor_data->num_elements, NULL); + + return (char*)g_list_nth (cursor_data->elements, n); +} + +static void +cursor_destroy(GtkObject *object, gpointer data) +{ + CORBA_Environment ev; + Evolution_Book corba_book; + PASBackendLDAPCursorPrivate *cursor_data = (PASBackendLDAPCursorPrivate *) data; + + corba_book = bonobo_object_corba_objref(BONOBO_OBJECT(cursor_data->book)); + + CORBA_exception_init(&ev); + + Evolution_Book_unref(corba_book, &ev); + + if (ev._major != CORBA_NO_EXCEPTION) { + g_warning("cursor_destroy: Exception unreffing " + "corba book.\n"); } - ldap->ld_sizelimit = LDAP_MAX_SEARCH_RESPONSES; - ldap->ld_deref = LDAP_DEREF_ALWAYS; + CORBA_exception_free(&ev); + + /* free the ldap specific cursor information */ + g_list_foreach (cursor_data->elements, (GFunc)g_free, NULL); + g_list_free (cursor_data->elements); + + g_free(cursor_data); +} + +static void +pas_backend_ldap_build_all_cards_list(PASBackend *backend, + PASBackendLDAPCursorPrivate *cursor_data) +{ + PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); + LDAP *ldap = bl->priv->ldap; + int ldap_error; + LDAPMessage *res, *e; if ((ldap_error = ldap_search_s (ldap, bl->priv->ldap_rootdn, @@ -252,33 +485,19 @@ pas_backend_ldap_build_all_cards_list(PASBackend *backend, } ldap_msgfree(res); - - ldap_unbind (ldap); } -static void -pas_backend_ldap_process_modify_card (PASBackend *backend, - PASBook *book, - PASRequest *req) -{ - g_warning ("pas_backend_ldap_process_modify_card not implemented\n"); - - pas_book_respond_modify ( - book, - Evolution_BookListener_CardNotFound); - g_free (req->vcard); -} -static void -pas_backend_ldap_process_get_cursor (PASBackend *backend, - PASBook *book, - PASRequest *req) +static gboolean +get_cursor_handler (PASBackend *backend, LDAPOp *op) { + LDAPGetCursorOp *cursor_op = (LDAPGetCursorOp*)op; CORBA_Environment ev; - PASBackendLDAPCursorPrivate *cursor_data; int ldap_error = 0; PASCardCursor *cursor; Evolution_Book corba_book; + PASBackendLDAPCursorPrivate *cursor_data; + PASBook *book = cursor_op->book; cursor_data = g_new(PASBackendLDAPCursorPrivate, 1); cursor_data->backend = backend; @@ -312,6 +531,27 @@ pas_backend_ldap_process_get_cursor (PASBackend *backend, ? Evolution_BookListener_Success : Evolution_BookListener_CardNotFound), cursor); + + /* we're synchronous */ + return TRUE; +} + +static void +get_cursor_dtor (PASBackend *backend, LDAPOp *op) +{ + g_free (op); +} + +static void +pas_backend_ldap_process_get_cursor (PASBackend *backend, + PASBook *book, + PASRequest *req) +{ + LDAPGetCursorOp *op = g_new (LDAPGetCursorOp, 1); + + ldap_op_init ((LDAPOp*)op, backend, book, get_cursor_handler, get_cursor_dtor); + + ldap_op_process ((LDAPOp*)op); } static void @@ -661,24 +901,50 @@ query_prop_to_ldap(gchar *query_prop) return NULL; } + +typedef struct { + LDAPOp op; + char *ldap_query; + PASBackendLDAP *bl; + PASBackendLDAPBookView *view; +} LDAPSearchOp; + + static gboolean -poll_ldap (PASBackendLDAPBookView *view) +poll_ldap (LDAPSearchOp *op) { - LDAP *ldap; + PASBackendLDAPBookView *view = op->view; + PASBackendLDAP *bl = op->bl; + LDAP *ldap = bl->priv->ldap; int rc; LDAPMessage *res, *e; GList *cards = NULL; + static int received = 0; - ldap = view->ldap; - - if ((rc = ldap_result (ldap, view->search_msgid, 0, NULL, &res)) - != LDAP_RES_SEARCH_ENTRY) { + printf ("polling for ldap search result\n"); + + rc = ldap_result (ldap, view->search_msgid, 0, NULL, &res); + + if (rc == -1 && received == 0) { + printf ("restarting search\n"); + /* connection went down and we never got any. */ + bl->priv->connected = FALSE; + + /* this will reopen the connection */ + ldap_op_restart ((LDAPOp*)op); + return FALSE; + } + + if (rc != LDAP_RES_SEARCH_ENTRY) { view->search_idle = 0; pas_book_view_notify_complete (view->book_view); - ldap_unbind (ldap); - view->ldap = NULL; + ldap_op_finished ((LDAPOp*)op); + received = 0; + printf ("done (rc = %d)\n", rc); return FALSE; } + + received = 1; e = ldap_first_entry(ldap, res); @@ -748,41 +1014,74 @@ poll_ldap (PASBackendLDAPBookView *view) return TRUE; } +static gboolean +ldap_search_handler (PASBackend *backend, LDAPOp *op) +{ + LDAPSearchOp *search_op = (LDAPSearchOp*) op; + + printf ("doing ldap search\n"); + + /* it might not be NULL if we've been restarted */ + if (search_op->ldap_query == NULL) + search_op->ldap_query = pas_backend_ldap_build_query(search_op->view->search); + + if (search_op->ldap_query != NULL) { + PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); + PASBackendLDAPBookView *view = search_op->view; + LDAP *ldap = bl->priv->ldap; + + ldap->ld_sizelimit = LDAP_MAX_SEARCH_RESPONSES; + ldap->ld_deref = LDAP_DEREF_ALWAYS; + + if ((view->search_msgid = ldap_search (ldap, + bl->priv->ldap_rootdn, + bl->priv->ldap_scope, + search_op->ldap_query, + NULL, 0)) == -1) { + g_warning ("ldap error '%s' in pas_backend_ldap_search\n", ldap_err2string(ldap->ld_errno)); + } + else { + view->search_idle = g_idle_add((GSourceFunc)poll_ldap, search_op); + } + + /* we're async */ + return FALSE; + } + else { + /* error doing the conversion to an ldap query, let's + end this now by acting like we're synchronous. */ + g_warning ("LDAP problem converting search query %s\n", search_op->view->search); + return TRUE; + } +} + +static void +ldap_search_dtor (PASBackend *backend, LDAPOp *op) +{ + LDAPSearchOp *search_op = (LDAPSearchOp*) op; + + g_free (search_op->ldap_query); + g_free (search_op); +} + static void pas_backend_ldap_search (PASBackendLDAP *bl, PASBook *book, PASBackendLDAPBookView *view) { - char *ldap_query; + LDAPSearchOp *op = g_new (LDAPSearchOp, 1); - if (view->search_idle) - g_warning ("pas_backend_ldap_search called again with pending search on this view.\n"); + ldap_op_init ((LDAPOp*)op, PAS_BACKEND(bl), book, ldap_search_handler, ldap_search_dtor); - ldap_query = pas_backend_ldap_build_query(view->search); + op->ldap_query = NULL; + op->view = view; + op->bl = bl; - if (ldap_query != NULL) { - LDAP *ldap; + /* keep track of the search op so we can delete it from the + list if the view is destroyed */ + view->search_op = (LDAPOp*)op; - pas_backend_ldap_connect(view); - - ldap = view->ldap; - - if (ldap) { - ldap->ld_sizelimit = LDAP_MAX_SEARCH_RESPONSES; - ldap->ld_deref = LDAP_DEREF_ALWAYS; - - if ((view->search_msgid = ldap_search (ldap, - bl->priv->ldap_rootdn, - bl->priv->ldap_scope, - ldap_query, - NULL, 0)) == -1) { - g_warning ("ldap error '%s' in pas_backend_ldap_search\n", ldap_err2string(ldap->ld_errno)); - } - else { - view->search_idle = g_idle_add((GSourceFunc)poll_ldap, view); - } - } - } + ldap_op_process ((LDAPOp*)op); } static void @@ -950,7 +1249,11 @@ pas_backend_ldap_load_uri (PASBackend *backend, ldap_free_urldesc(lud); - return TRUE; + pas_backend_ldap_connect (bl); + if (bl->priv->ldap == NULL) + return FALSE; + else + return TRUE; } else return FALSE; } @@ -1081,12 +1384,21 @@ pas_backend_ldap_new (void) } static void +call_dtor (LDAPOp *op, gpointer data) +{ + op->dtor (op->backend, op); +} + +static void pas_backend_ldap_destroy (GtkObject *object) { PASBackendLDAP *bl; bl = PAS_BACKEND_LDAP (object); + g_list_foreach (bl->priv->pending_ops, (GFunc)call_dtor, NULL); + g_list_free (bl->priv->pending_ops); + g_free (bl->priv->uri); GTK_OBJECT_CLASS (pas_backend_ldap_parent_class)->destroy (object); |