From da6e1ea98dcf2614ea71ea5dfb88ef63e194c8f6 Mon Sep 17 00:00:00 2001 From: Chris Toshok Date: Fri, 8 Mar 2002 00:51:11 +0000 Subject: track union/struct change. (pas_book_queue_remove_card): same. 2002-03-07 Chris Toshok * backend/pas/pas-book.c (pas_book_queue_create_card): track union/struct change. (pas_book_queue_remove_card): same. (pas_book_queue_modify_card): same. (pas_book_queue_get_cursor): same. (pas_book_queue_get_vcard): same. (pas_book_queue_authenticate_user): same. (pas_book_queue_get_book_view): same. (pas_book_queue_get_changes): same. (pas_book_free_request): new function - free everything we need to for each type of request. (pas_book_destroy): call pas_book_free_request here instead of just freeing 3 elements of the old struct. yay plugging memleaks. * backend/pas/pas-book.h: make PASRequest a union and split out members into structs, so it's a little clearer which fields are used by which requests. Also, add prototype for pas_book_free_request so backends can just free everything at once (usually in their requests_queued signal func.) * backend/pas/pas-backend-file.c (pas_backend_file_process_create_card): track struct/union change. (pas_backend_file_process_remove_card): same. (pas_backend_file_process_modify_card): same. (pas_backend_file_build_cards_list): same. (pas_backend_file_process_get_vcard): same. (pas_backend_file_process_get_cursor): same. (pas_backend_file_process_get_book_view): same. (pas_backend_file_process_get_changes): same. (pas_backend_file_process_check_connection): same. (pas_backend_file_process_authenticate_user): same. (pas_backend_file_process_get_supported_fields): same. (pas_backend_file_process_client_requests): case the union to the specific struct and pass it to the process_* functions. also, call pas_book_free_request here, instead of relying on each of the functions to free their stuff. svn path=/trunk/; revision=15987 --- addressbook/ChangeLog | 39 + addressbook/backend/pas/pas-backend-file.c | 65 +- addressbook/backend/pas/pas-backend-ldap.c | 1618 ++++++++++++++++------------ addressbook/backend/pas/pas-book.c | 87 +- addressbook/backend/pas/pas-book.h | 74 +- 5 files changed, 1153 insertions(+), 730 deletions(-) (limited to 'addressbook') diff --git a/addressbook/ChangeLog b/addressbook/ChangeLog index e15535e17c..310d5add60 100644 --- a/addressbook/ChangeLog +++ b/addressbook/ChangeLog @@ -1,3 +1,42 @@ +2002-03-07 Chris Toshok + + * backend/pas/pas-book.c (pas_book_queue_create_card): track + union/struct change. + (pas_book_queue_remove_card): same. + (pas_book_queue_modify_card): same. + (pas_book_queue_get_cursor): same. + (pas_book_queue_get_vcard): same. + (pas_book_queue_authenticate_user): same. + (pas_book_queue_get_book_view): same. + (pas_book_queue_get_changes): same. + (pas_book_free_request): new function - free everything we need to + for each type of request. + (pas_book_destroy): call pas_book_free_request here instead of + just freeing 3 elements of the old struct. yay plugging memleaks. + + * backend/pas/pas-book.h: make PASRequest a union and split out + members into structs, so it's a little clearer which fields are + used by which requests. Also, add prototype for + pas_book_free_request so backends can just free everything at once + (usually in their requests_queued signal func.) + + * backend/pas/pas-backend-file.c + (pas_backend_file_process_create_card): track struct/union change. + (pas_backend_file_process_remove_card): same. + (pas_backend_file_process_modify_card): same. + (pas_backend_file_build_cards_list): same. + (pas_backend_file_process_get_vcard): same. + (pas_backend_file_process_get_cursor): same. + (pas_backend_file_process_get_book_view): same. + (pas_backend_file_process_get_changes): same. + (pas_backend_file_process_check_connection): same. + (pas_backend_file_process_authenticate_user): same. + (pas_backend_file_process_get_supported_fields): same. + (pas_backend_file_process_client_requests): case the union to the + specific struct and pass it to the process_* functions. also, + call pas_book_free_request here, instead of relying on each of the + functions to free their stuff. + 2002-03-07 Dan Winship * gui/component/addressbook-storage.c diff --git a/addressbook/backend/pas/pas-backend-file.c b/addressbook/backend/pas/pas-backend-file.c index 057012ddaa..acca404ad8 100644 --- a/addressbook/backend/pas/pas-backend-file.c +++ b/addressbook/backend/pas/pas-backend-file.c @@ -535,7 +535,7 @@ do_create(PASBackend *backend, static void pas_backend_file_process_create_card (PASBackend *backend, PASBook *book, - PASRequest *req) + PASCreateCardRequest *req) { char *id; char *vcard; @@ -577,7 +577,7 @@ pas_backend_file_process_create_card (PASBackend *backend, static void pas_backend_file_process_remove_card (PASBackend *backend, PASBook *book, - PASRequest *req) + PASRemoveCardRequest *req) { PASBackendFile *bf = PAS_BACKEND_FILE (backend); DB *db = bf->priv->file_db; @@ -636,7 +636,7 @@ pas_backend_file_process_remove_card (PASBackend *backend, static void pas_backend_file_process_modify_card (PASBackend *backend, PASBook *book, - PASRequest *req) + PASModifyCardRequest *req) { PASBackendFile *bf = PAS_BACKEND_FILE (backend); DB *db = bf->priv->file_db; @@ -671,7 +671,6 @@ pas_backend_file_process_modify_card (PASBackend *backend, pas_book_respond_modify ( book, GNOME_Evolution_Addressbook_BookListener_CardNotFound); - g_free (req->id); return; } old_vcard_string = g_strdup(vcard_dbt.data); @@ -728,8 +727,8 @@ pas_backend_file_process_modify_card (PASBackend *backend, static void pas_backend_file_build_cards_list(PASBackend *backend, - PASBackendFileCursorPrivate *cursor_data, - char *search) + PASBackendFileCursorPrivate *cursor_data, + char *search) { PASBackendFile *bf = PAS_BACKEND_FILE (backend); DB *db = bf->priv->file_db; @@ -788,7 +787,7 @@ pas_backend_file_build_cards_list(PASBackend *backend, static void pas_backend_file_process_get_vcard (PASBackend *backend, PASBook *book, - PASRequest *req) + PASGetVCardRequest *req) { PASBackendFile *bf; DB *db; @@ -821,7 +820,7 @@ pas_backend_file_process_get_vcard (PASBackend *backend, static void pas_backend_file_process_get_cursor (PASBackend *backend, PASBook *book, - PASRequest *req) + PASGetCursorRequest *req) { /* PASBackendFile *bf = PAS_BACKEND_FILE (backend); @@ -871,10 +870,9 @@ pas_backend_file_process_get_cursor (PASBackend *backend, static void pas_backend_file_process_get_book_view (PASBackend *backend, PASBook *book, - PASRequest *req) + PASGetBookViewRequest *req) { PASBackendFile *bf = PAS_BACKEND_FILE (backend); - CORBA_Environment ev; PASBookView *book_view; PASBackendFileBookView view; EIterator *iterator; @@ -889,7 +887,7 @@ pas_backend_file_process_get_book_view (PASBackend *backend, GTK_SIGNAL_FUNC(view_destroy), book); view.book_view = book_view; - view.search = req->search; + view.search = g_strdup (req->search); view.card_sexp = NULL; view.change_id = NULL; view.change_context = NULL; @@ -906,25 +904,12 @@ pas_backend_file_process_get_book_view (PASBackend *backend, e_iterator_last(iterator); pas_backend_file_search (bf, book, e_iterator_get(iterator)); gtk_object_unref(GTK_OBJECT(iterator)); - - g_free(req->search); - CORBA_exception_init(&ev); - - bonobo_object_unref (BONOBO_OBJECT (book_view)); - bonobo_object_release_unref (req->listener, &ev); - - if (ev._major != CORBA_NO_EXCEPTION) { - g_warning("pas_backend_file_process_get_book_view: Exception unreffing " - "listener.\n"); - } - - CORBA_exception_free(&ev); } static void pas_backend_file_process_get_changes (PASBackend *backend, PASBook *book, - PASRequest *req) + PASGetChangesRequest *req) { PASBackendFile *bf = PAS_BACKEND_FILE (backend); CORBA_Environment ev; @@ -967,7 +952,7 @@ pas_backend_file_process_get_changes (PASBackend *backend, pas_backend_file_changes (bf, book, e_iterator_get(iterator)); gtk_object_unref(GTK_OBJECT(iterator)); - g_free(req->search); + g_free(req->change_id); CORBA_exception_init(&ev); bonobo_object_release_unref (req->listener, &ev); @@ -982,7 +967,7 @@ pas_backend_file_process_get_changes (PASBackend *backend, static void pas_backend_file_process_check_connection (PASBackend *backend, PASBook *book, - PASRequest *req) + PASCheckConnectionRequest *req) { PASBackendFile *bf = PAS_BACKEND_FILE (backend); @@ -1000,7 +985,7 @@ pas_backend_file_extract_path_from_uri (const char *uri) static void pas_backend_file_process_authenticate_user (PASBackend *backend, PASBook *book, - PASRequest *req) + PASAuthenticateUserRequest *req) { pas_book_respond_authenticate_user (book, GNOME_Evolution_Addressbook_BookListener_Success); @@ -1009,7 +994,7 @@ pas_backend_file_process_authenticate_user (PASBackend *backend, static void pas_backend_file_process_get_supported_fields (PASBackend *backend, PASBook *book, - PASRequest *req) + PASGetSupportedFieldsRequest *req) { EList *fields = e_list_new ((EListCopyFunc)g_strdup, (EListFreeFunc)g_free, NULL); ECardSimple *simple; @@ -1047,47 +1032,47 @@ pas_backend_file_process_client_requests (PASBook *book) switch (req->op) { case CreateCard: - pas_backend_file_process_create_card (backend, book, req); + pas_backend_file_process_create_card (backend, book, (PASCreateCardRequest*)req); break; case RemoveCard: - pas_backend_file_process_remove_card (backend, book, req); + pas_backend_file_process_remove_card (backend, book, (PASRemoveCardRequest*)req); break; case ModifyCard: - pas_backend_file_process_modify_card (backend, book, req); + pas_backend_file_process_modify_card (backend, book, (PASModifyCardRequest*)req); break; case CheckConnection: - pas_backend_file_process_check_connection (backend, book, req); + pas_backend_file_process_check_connection (backend, book, (PASCheckConnectionRequest*)req); break; case GetVCard: - pas_backend_file_process_get_vcard (backend, book, req); + pas_backend_file_process_get_vcard (backend, book, (PASGetVCardRequest*)req); break; case GetCursor: - pas_backend_file_process_get_cursor (backend, book, req); + pas_backend_file_process_get_cursor (backend, book, (PASGetCursorRequest*)req); break; case GetBookView: - pas_backend_file_process_get_book_view (backend, book, req); + pas_backend_file_process_get_book_view (backend, book, (PASGetBookViewRequest*)req); break; case GetChanges: - pas_backend_file_process_get_changes (backend, book, req); + pas_backend_file_process_get_changes (backend, book, (PASGetChangesRequest*)req); break; case AuthenticateUser: - pas_backend_file_process_authenticate_user (backend, book, req); + pas_backend_file_process_authenticate_user (backend, book, (PASAuthenticateUserRequest*)req); break; case GetSupportedFields: - pas_backend_file_process_get_supported_fields (backend, book, req); + pas_backend_file_process_get_supported_fields (backend, book, (PASGetSupportedFieldsRequest*)req); break; } - g_free (req); + pas_book_free_request (req); } static void diff --git a/addressbook/backend/pas/pas-backend-ldap.c b/addressbook/backend/pas/pas-backend-ldap.c index 6a98246257..aebe05d98d 100644 --- a/addressbook/backend/pas/pas-backend-ldap.c +++ b/addressbook/backend/pas/pas-backend-ldap.c @@ -10,8 +10,6 @@ #include "config.h" #include -#include -#include #include #include @@ -33,7 +31,7 @@ #endif #ifdef OPENLDAP2 -#include "ldap_schema.h" +#include #endif #include @@ -49,8 +47,6 @@ #include -#define LDAP_MAX_SEARCH_RESPONSES 100 - /* interval for our poll_ldap timeout */ #define LDAP_POLL_INTERVAL 20 @@ -85,11 +81,16 @@ struct _PASBackendLDAPPrivate { char *uri; gboolean connected; GList *clients; - gchar *ldap_host; - gchar *ldap_rootdn; - int ldap_port; - int ldap_scope; - int ldap_limit; + + gchar *ldap_host; /* the hostname of the server */ + int ldap_port; /* the port of the server */ + char *schema_dn; /* the base dn for schema information */ + gchar *ldap_rootdn; /* the base dn of our searches */ + int ldap_scope; /* the scope used for searches */ + int ldap_limit; /* the search limit */ + + gboolean ldap_v3; /* TRUE if the server supports protocol + revision 3 (necessary for TLS) */ GList *book_views; LDAP *ldap; @@ -99,14 +100,14 @@ struct _PASBackendLDAPPrivate { /* whether or not there's support for the objectclass we need to store all our additional fields */ gboolean evolutionPersonSupported; - gboolean evolutionPersonChecked; + gboolean schema_checked; gboolean writable; - /* whether or not there's a request in process on our LDAP* */ - LDAPOp *current_op; - GList *pending_ops; - int op_idle; + /* our operations */ + GHashTable *id_to_op; + int active_ops; + int poll_timeout; }; struct _PASBackendLDAPCursorPrivate { @@ -122,25 +123,12 @@ struct _PASBackendLDAPBookView { PASBackendLDAPPrivate *blpriv; gchar *search; PASBackendCardSExp *card_sexp; - int search_timeout; - int search_msgid; - LDAPOp *search_op; - /* grouping stuff */ - - GList *pending_adds; /* the cards we're sending */ - int num_pending_adds; /* the number waiting to be sent */ - int target_pending_adds; /* the cutoff that forces a flush to the client, if it happens before the timeout */ - int num_sent_this_time; /* the number of cards we sent to the client before the most recent timeout */ - int num_sent_last_time; /* the number of cards we sent to the client before the previous timeout */ - glong grouping_time_start; - - /* used by poll_ldap to only send the status messages once */ - gboolean notified_receiving_results; + LDAPOp *search_op; }; -typedef gboolean (*LDAPOpHandler)(PASBackend *backend, LDAPOp *op); -typedef void (*LDAPOpDtor)(PASBackend *backend, LDAPOp *op); +typedef void (*LDAPOpHandler)(LDAPOp *op, LDAPMessage *res); +typedef void (*LDAPOpDtor)(LDAPOp *op); struct LDAPOp { LDAPOpHandler handler; @@ -148,15 +136,17 @@ struct LDAPOp { PASBackend *backend; PASBook *book; PASBookView *view; + int id; }; -static void ldap_op_init (LDAPOp *op, PASBackend *backend, PASBook *book, PASBookView *view, 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_add (LDAPOp *op, PASBackend *backend, PASBook *book, + PASBookView *view, int id, LDAPOpHandler handler, LDAPOpDtor dtor); static void ldap_op_finished (LDAPOp *op); +static void ldap_search_op_timeout (LDAPOp *op, glong cur_millis); + +static gboolean poll_ldap (PASBackendLDAP *bl); + static ECardSimple *build_card_from_entry (LDAP *ldap, LDAPMessage *e, GList **existing_objectclasses); static void email_populate (ECardSimple *card, char **values); @@ -273,6 +263,13 @@ struct prop_info { static int num_prop_infos = sizeof(prop_info) / sizeof(prop_info[0]); +static void +remove_view (int msgid, LDAPOp *op, PASBookView *view) +{ + if (op->view == view) + op->view = NULL; +} + static void view_destroy(GtkObject *object, gpointer data) { @@ -286,26 +283,13 @@ 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_timeout != 0) { - /* we have a search running on the - ldap connection. remove the timeout - handler and anbandon the msg id */ - g_source_remove(view->search_timeout); - if (view->search_msgid != -1) - 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); - } - } + /* if we have an active search, interrupt it */ + if (view->search_op) + ldap_op_finished (view->search_op); + /* and remove us as the view for any other + operations that might be using us to spew + status messages to the gui */ + g_hash_table_foreach (bl->priv->id_to_op, (GHFunc)remove_view, view->book_view); g_free (view->search); gtk_object_unref (GTK_OBJECT (view->card_sexp)); g_free (view); @@ -379,12 +363,15 @@ check_schema_support (PASBackendLDAP *bl) LDAPMessage *resp; LDAP *ldap = bl->priv->ldap; - bl->priv->evolutionPersonChecked = TRUE; + if (!bl->priv->schema_dn) + return; + + bl->priv->schema_checked = TRUE; attrs[0] = "objectClasses"; attrs[1] = NULL; - if (ldap_search_ext_s (ldap, "cn=Subschema", LDAP_SCOPE_BASE, + if (ldap_search_ext_s (ldap, bl->priv->schema_dn, LDAP_SCOPE_BASE, "(objectClass=subschema)", attrs, 0, NULL, NULL, NULL, 0, &resp) == LDAP_SUCCESS) { char **values; @@ -421,6 +408,120 @@ check_schema_support (PASBackendLDAP *bl) } } +static void +get_ldap_library_info () +{ + LDAPAPIInfo info; + LDAP *ldap; + + if (LDAP_SUCCESS != ldap_create (&ldap)) { + g_warning ("couldn't create LDAP* for getting at the client lib api info"); + return; + } + + info.ldapai_info_version = LDAP_API_INFO_VERSION; + + if (LDAP_OPT_SUCCESS != ldap_get_option (ldap, LDAP_OPT_API_INFO, &info)) { + g_warning ("couldn't get ldap api info"); + } + else { + int i; + g_message ("libldap vendor/version: %s %2d.%02d.%02d", + info.ldapai_vendor_name, + info.ldapai_vendor_version / 10000, + (info.ldapai_vendor_version % 10000) / 1000, + info.ldapai_vendor_version % 1000); + + g_message ("extensions present:"); + for (i = 0; info.ldapai_extensions[i]; i++) { + char *extension = info.ldapai_extensions[i]; + g_message (extension); + } + } + + ldap_unbind_ext_s (ldap, NULL, NULL); +} + +static void +query_ldap_root_dse (PASBackendLDAP *bl) +{ +#define MAX_DSE_ATTRS 20 + LDAP *ldap = bl->priv->ldap; + LDAPMessage *resp; + int ldap_error; + char *attrs[MAX_DSE_ATTRS], **values; + int i = 0; + struct timeval timeout; + + attrs[i++] = "supportedControl"; + attrs[i++] = "supportedExtension"; + attrs[i++] = "supportedFeatures"; + attrs[i++] = "supportedSASLMechanisms"; + attrs[i++] = "supportedLDAPVersion"; + attrs[i++] = "subschemaSubentry"; /* OpenLDAP's dn for schema information */ + attrs[i++] = "schemaNamingContext"; /* Active directory's dn for schema information */ + attrs[i] = NULL; + + timeout.tv_sec = 0; + timeout.tv_usec = LDAP_RESULT_TIMEOUT_MILLIS * 1000; + + ldap_error = ldap_search_ext_s (ldap, + LDAP_ROOT_DSE, LDAP_SCOPE_BASE, + "(objectclass=*)", + attrs, 0, NULL, NULL, NULL, 0, &resp); + if (ldap_error != LDAP_SUCCESS) { + g_warning ("could not perform query on Root DSE"); + return; + } + + values = ldap_get_values (ldap, resp, "supportedControl"); + if (values) { + for (i = 0; values[i]; i++) + g_message ("supported server control: %s", values[i]); + ldap_value_free (values); + } + + values = ldap_get_values (ldap, resp, "supportedExtension"); + if (values) { + for (i = 0; values[i]; i++) { + g_message ("supported server extension: %s", values[i]); + if (!strcmp (values[i], LDAP_EXOP_START_TLS)) { + g_message ("server reports LDAP_EXOP_START_TLS"); + } + } + ldap_value_free (values); + } + + values = ldap_get_values (ldap, resp, "supportedSASLMechanisms"); + if (values) { + for (i = 0; values[i]; i++) + g_message ("supported SASL mechanism: %s", values[i]); + ldap_value_free (values); + } + + + values = ldap_get_values (ldap, resp, "supportedLDAPVersion"); + if (values) { + for (i = 0; values[i]; i++) + if (LDAP_VERSION3 == atoi (values[i])) + bl->priv->ldap_v3 = TRUE; + ldap_value_free (values); + } + + values = ldap_get_values (ldap, resp, "subschemaSubentry"); + if (!values || !values[0]) + values = ldap_get_values (ldap, resp, "schemaNamingContext"); + if (values || values[0]) { + bl->priv->schema_dn = g_strdup (values[0]); + } + else { + g_warning ("could not determine location of schema information on LDAP server"); + } + if (values) + ldap_value_free (values); + +} + static void pas_backend_ldap_connect (PASBackendLDAP *bl) { @@ -433,14 +534,42 @@ pas_backend_ldap_connect (PASBackendLDAP *bl) blpriv->ldap = ldap_init (blpriv->ldap_host, blpriv->ldap_port); #ifdef DEBUG { - int debug_level = ~0; + int debug_level = 4; ldap_set_option (blpriv->ldap, LDAP_OPT_DEBUG_LEVEL, &debug_level); } #endif if (NULL != blpriv->ldap) { - ldap_simple_bind_s(blpriv->ldap, - NULL /*binddn*/, NULL /*passwd*/); + int ldap_error; + query_ldap_root_dse (bl); + + if (bl->priv->ldap_v3) { + int protocol_version = LDAP_VERSION3; + ldap_error = ldap_set_option (blpriv->ldap, LDAP_OPT_PROTOCOL_VERSION, &protocol_version); + if (LDAP_OPT_SUCCESS != ldap_error) { + g_warning ("failed to set protocol version to LDAPv3"); + bl->priv->ldap_v3 = FALSE; + } + } + +#if notyet + if (TRUE /* the user wants to use TLS */) { + if (bl->priv->ldap_v3 /* the server supports v3 */) { + ldap_error = ldap_start_tls_s (blpriv->ldap, NULL, NULL); + if (LDAP_SUCCESS != ldap_error) { + g_warning ("ldap_start_tls_s failed with ldap_error 0x%2x (%s)", + ldap_error, + ldap_err2string (ldap_error)); + } + else + g_message ("TLS active"); + } + else { + g_warning ("user wants to use TLS, but server doesn't support LDAPv3"); + } + } +#endif + blpriv->connected = TRUE; /* check to see if evolutionPerson is supported, if we can (me @@ -456,141 +585,67 @@ pas_backend_ldap_connect (PASBackendLDAP *bl) blpriv->ldap_rootdn ? blpriv->ldap_rootdn : ""); blpriv->connected = FALSE; } - -} - -static ECardSimple * -search_for_dn_with_objectclasses (PASBackendLDAP *bl, const char *dn, GList **existing_objectclasses) -{ - LDAP *ldap = bl->priv->ldap; - LDAPMessage *res, *e; - ECardSimple *result = NULL; - - if (ldap_search_s (ldap, - dn, - LDAP_SCOPE_BASE, - "(objectclass=*)", - NULL, 0, &res) == LDAP_SUCCESS) { - e = ldap_first_entry (ldap, res); - while (NULL != e) { - if (!strcmp (ldap_get_dn (ldap, e), dn)) { - printf ("found it\n"); - result = build_card_from_entry (ldap, e, existing_objectclasses); - break; - } - e = ldap_next_entry (ldap, e); - } - - ldap_msgfree(res); - } - - return result; -} - -static ECardSimple * -search_for_dn (PASBackendLDAP *bl, const char *dn) -{ - return search_for_dn_with_objectclasses (bl, dn, NULL); } static void -ldap_op_init (LDAPOp *op, PASBackend *backend, - PASBook *book, PASBookView *view, - LDAPOpHandler handler, LDAPOpDtor dtor) +ldap_op_add (LDAPOp *op, PASBackend *backend, + PASBook *book, PASBookView *view, + int id, + LDAPOpHandler handler, LDAPOpDtor dtor) { + PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); + op->backend = backend; op->book = book; op->view = view; + op->id = id; 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; + g_hash_table_insert (bl->priv->id_to_op, + &op->id, op); - if (!bl->priv->connected) { - if (op->view) - pas_book_view_notify_status_message (op->view, _("Connecting to LDAP server...")); - pas_backend_ldap_connect(bl); - } - - if (bl->priv->connected) { - if (op->handler (backend, op)) - ldap_op_finished (op); - } - else { - if (op->view) { - pas_book_view_notify_status_message (op->view, _("Unable to connect to LDAP server.")); - pas_book_view_notify_complete (op->view); - } + bl->priv->active_ops ++; - ldap_op_finished (op); - } + if (bl->priv->poll_timeout == -1) + bl->priv->poll_timeout = g_timeout_add (LDAP_POLL_INTERVAL, + (GSourceFunc) poll_ldap, + bl); } 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. */ - if (op->view) - pas_book_view_notify_status_message (op->view, _("Waiting for connection to LDAP server...")); - 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) +ldap_op_finished (LDAPOp *op) { + PASBackend *backend = op->backend; PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); - bl->priv->op_idle = 0; - - ldap_op_process_current (backend); + g_hash_table_remove (bl->priv->id_to_op, &op->id); - return FALSE; -} - -static void -ldap_op_restart (LDAPOp *op) -{ - PASBackend *backend = op->backend; - PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); + /* should handle errors here */ + ldap_abandon (bl->priv->ldap, op->id); - g_return_if_fail (op == bl->priv->current_op); + op->dtor (op); - bl->priv->op_idle = g_idle_add((GSourceFunc)ldap_op_process_on_idle, backend); + bl->priv->active_ops--; + if (bl->priv->active_ops == 0) { + if (bl->priv->poll_timeout != -1) + g_source_remove (bl->priv->poll_timeout); + bl->priv->poll_timeout = -1; + } } static void -ldap_op_finished (LDAPOp *op) +ldap_op_change_id (LDAPOp *op, int msg_id) { PASBackend *backend = op->backend; PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); - g_return_if_fail (op == bl->priv->current_op); - - op->dtor (backend, op); + g_hash_table_remove (bl->priv->id_to_op, &op->id); - 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); + op->id = msg_id; - bl->priv->op_idle = g_idle_add((GSourceFunc)ldap_op_process_on_idle, backend); - } - else { - bl->priv->current_op = NULL; - } + g_hash_table_insert (bl->priv->id_to_op, + &op->id, op); } static int @@ -867,49 +922,128 @@ add_objectclass_mod (PASBackendLDAP *bl, GPtrArray *mod_array, GList *existing_o typedef struct { LDAPOp op; - char *vcard; + char *dn; + ECardSimple *new_card; } LDAPCreateOp; -static gboolean -create_card_handler (PASBackend *backend, LDAPOp *op) +static void +create_card_handler (LDAPOp *op, LDAPMessage *res) { LDAPCreateOp *create_op = (LDAPCreateOp*)op; + PASBackendLDAP *bl = PAS_BACKEND_LDAP (op->backend); + LDAP *ldap = bl->priv->ldap; + int ldap_error; + int response; + + if (LDAP_RES_ADD != ldap_msgtype (res)) { + g_warning ("incorrect msg type %d passed to create_card_handler", ldap_msgtype (res)); + pas_book_respond_create (op->book, + GNOME_Evolution_Addressbook_BookListener_OtherError, + create_op->dn); + ldap_op_finished (op); + return; + } + + ldap_parse_result (ldap, res, &ldap_error, + NULL, NULL, NULL, NULL, 0); + + if (ldap_error == LDAP_SUCCESS) { + /* the card was created, let's let the views know about it */ + GList *l; + for (l = bl->priv->book_views; l; l = l->next) { + CORBA_Environment ev; + gboolean match; + PASBackendLDAPBookView *view = l->data; + + CORBA_exception_init(&ev); + + bonobo_object_dup_ref(bonobo_object_corba_objref(BONOBO_OBJECT(view->book_view)), &ev); + + match = pas_backend_card_sexp_match_vcard (view->card_sexp, + e_card_simple_get_vcard_assume_utf8 (create_op->new_card)); + if (match) { + char *vcard = e_card_simple_get_vcard_assume_utf8 (create_op->new_card); + pas_book_view_notify_add_1 (view->book_view, + vcard); + g_free (vcard); + } + pas_book_view_notify_complete (view->book_view); + + bonobo_object_release_unref(bonobo_object_corba_objref(BONOBO_OBJECT(view->book_view)), &ev); + } + } + else { + ldap_perror (ldap, "create_card"); + } + + if (op->view) + pas_book_view_notify_complete (op->view); + + /* and lastly respond */ + response = ldap_error_to_response (ldap_error); + pas_book_respond_create (op->book, + response, + create_op->dn); + + ldap_op_finished (op); +} + +static void +create_card_dtor (LDAPOp *op) +{ + LDAPCreateOp *create_op = (LDAPCreateOp*)op; + + g_free (create_op->dn); + gtk_object_unref (GTK_OBJECT (create_op->new_card)); + g_free (create_op); +} + +static void +pas_backend_ldap_process_create_card (PASBackend *backend, + PASBook *book, + PASRequest *req) +{ + LDAPCreateOp *create_op = g_new (LDAPCreateOp, 1); PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); + PASBookView *book_view = NULL; + int create_card_msgid; ECard *new_ecard; - ECardSimple *new_card; - char *dn; int response; - int ldap_error; + int err; GPtrArray *mod_array; LDAPMod **ldap_mods; LDAP *ldap; - printf ("vcard = %s\n", create_op->vcard); + if (bl->priv->book_views) { + PASBackendLDAPBookView *v = bl->priv->book_views->data; + book_view = v->book_view; + } + + printf ("vcard = %s\n", req->create.vcard); - new_ecard = e_card_new (create_op->vcard); - new_card = e_card_simple_new (new_ecard); + new_ecard = e_card_new (req->create.vcard); + create_op->new_card = e_card_simple_new (new_ecard); - dn = create_dn_from_ecard (new_card, bl->priv->ldap_rootdn); - e_card_simple_set_id (new_card, dn); /* for the notification code below */ + create_op->dn = create_dn_from_ecard (create_op->new_card, bl->priv->ldap_rootdn); + e_card_simple_set_id (create_op->new_card, create_op->dn); /* for the notification code below */ ldap = bl->priv->ldap; /* build our mods */ - mod_array = build_mods_from_ecards (bl, NULL, new_card, NULL); + mod_array = build_mods_from_ecards (bl, NULL, create_op->new_card, NULL); #if 0 if (!mod_array) { /* there's an illegal field in there. report UnsupportedAttribute back */ - g_free (dn); - - gtk_object_unref (GTK_OBJECT(new_card)); - - pas_book_respond_create (create_op->op.book, + pas_book_respond_create (book, GNOME_Evolution_Addressbook_BookListener_UnsupportedField, - dn); + create_op->dn); - return TRUE; + g_free (create_op->dn); + gtk_object_unref (GTK_OBJECT(create_op->new_card)); + g_free (create_op); + return; } #endif @@ -959,368 +1093,472 @@ create_card_handler (PASBackend *backend, LDAPOp *op) ldap_mods = (LDAPMod**)mod_array->pdata; - if (op->view) - pas_book_view_notify_status_message (op->view, _("Adding card to LDAP server...")); + if (book_view) + pas_book_view_notify_status_message (book_view, _("Adding card to LDAP server...")); + + err = ldap_add_ext (ldap, create_op->dn, ldap_mods, + NULL, NULL, &create_card_msgid); + + /* and clean up */ + free_mods (mod_array); + + if (LDAP_SUCCESS != err) { + response = ldap_error_to_response (err); + pas_book_respond_create (create_op->op.book, + response, + create_op->dn); + create_card_dtor ((LDAPOp*)create_op); + return; + } + else { + g_print ("ldap_add_ext returned %d\n", err); + ldap_op_add ((LDAPOp*)create_op, backend, book, + book_view, create_card_msgid, + create_card_handler, create_card_dtor); + } +} + + +typedef struct { + LDAPOp op; + char *id; +} LDAPRemoveOp; + +static void +remove_card_handler (LDAPOp *op, LDAPMessage *res) +{ + LDAPRemoveOp *remove_op = (LDAPRemoveOp*)op; + PASBackendLDAP *bl = PAS_BACKEND_LDAP (op->backend); + int ldap_error; + + if (LDAP_RES_DELETE != ldap_msgtype (res)) { + g_warning ("incorrect msg type %d passed to remove_card_handler", ldap_msgtype (res)); + pas_book_respond_remove (op->book, + GNOME_Evolution_Addressbook_BookListener_OtherError); + ldap_op_finished (op); + return; + } - /* actually perform the ldap add */ - ldap_error = ldap_add_s (ldap, dn, ldap_mods); + ldap_parse_result (bl->priv->ldap, res, &ldap_error, + NULL, NULL, NULL, NULL, 0); if (ldap_error == LDAP_SUCCESS) { - /* the card was created, let's let the views know about it */ + /* the card was removed, let's let the views know about it */ GList *l; for (l = bl->priv->book_views; l; l = l->next) { CORBA_Environment ev; - gboolean match; PASBackendLDAPBookView *view = l->data; CORBA_exception_init(&ev); bonobo_object_dup_ref(bonobo_object_corba_objref(BONOBO_OBJECT(view->book_view)), &ev); - match = pas_backend_card_sexp_match_vcard (view->card_sexp, - e_card_simple_get_vcard_assume_utf8 (new_card)); - if (match) - pas_book_view_notify_add_1 (view->book_view, e_card_simple_get_vcard_assume_utf8 (new_card)); - pas_book_view_notify_complete (view->book_view); + pas_book_view_notify_remove (view->book_view, remove_op->id); bonobo_object_release_unref(bonobo_object_corba_objref(BONOBO_OBJECT(view->book_view)), &ev); } } else { - ldap_perror (ldap, "ldap_add_s"); + ldap_perror (bl->priv->ldap, "remove_card"); } + pas_book_respond_remove (remove_op->op.book, + ldap_error_to_response (ldap_error)); + if (op->view) pas_book_view_notify_complete (op->view); - - /* and clean up */ - free_mods (mod_array); - g_free (dn); - - gtk_object_unref (GTK_OBJECT(new_card)); - - /* and lastly respond */ - response = ldap_error_to_response (ldap_error); - pas_book_respond_create (create_op->op.book, - response, - dn); - - /* we're synchronous */ - return TRUE; } static void -create_card_dtor (PASBackend *backend, LDAPOp *op) +remove_card_dtor (LDAPOp *op) { - LDAPCreateOp *create_op = (LDAPCreateOp*)op; - - if (op->view) - bonobo_object_release_unref(bonobo_object_corba_objref(BONOBO_OBJECT(op->view)), NULL); + LDAPRemoveOp *remove_op = (LDAPRemoveOp*)op; - g_free (create_op->vcard); - g_free (create_op); + g_free (remove_op->id); + g_free (remove_op); } static void -pas_backend_ldap_process_create_card (PASBackend *backend, +pas_backend_ldap_process_remove_card (PASBackend *backend, PASBook *book, PASRequest *req) { - LDAPCreateOp *create_op = g_new (LDAPCreateOp, 1); + LDAPRemoveOp *remove_op = g_new (LDAPRemoveOp, 1); PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); PASBookView *book_view = NULL; + int remove_msgid; + int ldap_error; if (bl->priv->book_views) { PASBackendLDAPBookView *v = bl->priv->book_views->data; book_view = v->book_view; - bonobo_object_dup_ref(bonobo_object_corba_objref(BONOBO_OBJECT(book_view)), NULL); } - ldap_op_init ((LDAPOp*)create_op, backend, book, - book_view, - create_card_handler, create_card_dtor); + remove_op->id = g_strdup (req->remove.id); + + if (book_view) + pas_book_view_notify_status_message (book_view, _("Removing card from LDAP server...")); - create_op->vcard = req->vcard; + ldap_error = ldap_delete_ext (bl->priv->ldap, + remove_op->id, + NULL, NULL, &remove_msgid); - ldap_op_process ((LDAPOp*)create_op); + if (ldap_error != LDAP_SUCCESS) { + pas_book_respond_remove (remove_op->op.book, + ldap_error_to_response (ldap_error)); + remove_card_dtor ((LDAPOp*)remove_op); + return; + } + else { + g_print ("ldap_delete_ext returned %d\n", ldap_error); + ldap_op_add ((LDAPOp*)remove_op, backend, book, + book_view, remove_msgid, + remove_card_handler, remove_card_dtor); + } } +/* +** MODIFY +** +** The modification request is actually composed of 2 separate +** requests. Since we need to get a list of theexisting objectclasses +** used by the ldap server for the entry, and since the UI only sends +** us the current card, we need to query the ldap server for the +** existing card. +** +*/ + typedef struct { LDAPOp op; - char *id; -} LDAPRemoveOp; + const char *id; /* the id of the card we're modifying */ + char *current_vcard; /* current in the LDAP db */ + ECardSimple *current_card; + char *vcard; /* the VCard we want to store */ + ECardSimple *card; + GList *existing_objectclasses; +} LDAPModifyOp; -static gboolean -remove_card_handler (PASBackend *backend, LDAPOp *op) +static void +modify_card_modify_handler (LDAPOp *op, LDAPMessage *res) { - LDAPRemoveOp *remove_op = (LDAPRemoveOp*)op; - PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); - int response; + LDAPModifyOp *modify_op = (LDAPModifyOp*)op; + PASBackendLDAP *bl = PAS_BACKEND_LDAP (op->backend); + LDAP *ldap = bl->priv->ldap; int ldap_error; - ECardSimple *simple; - - if (op->view) - pas_book_view_notify_status_message (op->view, _("Removing card from LDAP server...")); - simple = search_for_dn (bl, remove_op->id); + if (LDAP_RES_MODIFY != ldap_msgtype (res)) { + g_warning ("incorrect msg type %d passed to modify_card_handler", ldap_msgtype (res)); + pas_book_respond_modify (op->book, + GNOME_Evolution_Addressbook_BookListener_OtherError); + ldap_op_finished (op); + return; + } - if (simple) { - ldap_error = ldap_delete_s (bl->priv->ldap, remove_op->id); + ldap_parse_result (ldap, res, &ldap_error, + NULL, NULL, NULL, NULL, 0); - if (ldap_error == LDAP_SUCCESS) { - /* the card was removed, let's let the views know about it */ - GList *l; - for (l = bl->priv->book_views; l; l = l->next) { - CORBA_Environment ev; - gboolean match; - PASBackendLDAPBookView *view = l->data; + if (ldap_error == LDAP_SUCCESS) { + /* the card was modified, let's let the views know about it */ + GList *l; + for (l = bl->priv->book_views; l; l = l->next) { + CORBA_Environment ev; + gboolean old_match, new_match; + PASBackendLDAPBookView *view = l->data; - CORBA_exception_init(&ev); + CORBA_exception_init(&ev); - bonobo_object_dup_ref(bonobo_object_corba_objref(BONOBO_OBJECT(view->book_view)), &ev); + bonobo_object_dup_ref(bonobo_object_corba_objref(BONOBO_OBJECT(view->book_view)), &ev); - match = pas_backend_card_sexp_match_vcard (view->card_sexp, - e_card_simple_get_vcard_assume_utf8 (simple)); - if (match) - pas_book_view_notify_remove (view->book_view, remove_op->id); - pas_book_view_notify_complete (view->book_view); + old_match = pas_backend_card_sexp_match_vcard (view->card_sexp, + modify_op->current_vcard); + new_match = pas_backend_card_sexp_match_vcard (view->card_sexp, + modify_op->vcard); + if (old_match && new_match) + pas_book_view_notify_change_1 (view->book_view, modify_op->vcard); + else if (new_match) + pas_book_view_notify_add_1 (view->book_view, modify_op->vcard); + else /* if (old_match) */ + pas_book_view_notify_remove (view->book_view, e_card_simple_get_id (modify_op->card)); + pas_book_view_notify_complete (view->book_view); - bonobo_object_release_unref(bonobo_object_corba_objref(BONOBO_OBJECT(view->book_view)), &ev); - } + bonobo_object_release_unref(bonobo_object_corba_objref(BONOBO_OBJECT(view->book_view)), &ev); } - else { - ldap_perror (bl->priv->ldap, "ldap_delete_s"); + } + else { + ldap_perror (ldap, "ldap_modify_s"); + } + + /* and lastly respond */ + pas_book_respond_modify (op->book, + ldap_error_to_response (ldap_error)); + ldap_op_finished (op); +} + +static void +modify_card_search_handler (LDAPOp *op, LDAPMessage *res) +{ + LDAPModifyOp *modify_op = (LDAPModifyOp*)op; + PASBackendLDAP *bl = PAS_BACKEND_LDAP (op->backend); + LDAP *ldap = bl->priv->ldap; + int msg_type; + + /* if it's successful, we should get called with a + RES_SEARCH_ENTRY and a RES_SEARCH_RESULT. if it's + unsuccessful, we should only see a RES_SEARCH_RESULT */ + + msg_type = ldap_msgtype (res); + if (msg_type == LDAP_RES_SEARCH_ENTRY) { + LDAPMessage *e = ldap_first_entry(ldap, res); + + if (!e) { + g_warning ("uh, this shouldn't happen"); + pas_book_respond_modify (op->book, + GNOME_Evolution_Addressbook_BookListener_OtherError); + ldap_op_finished (op); + return; } - response = ldap_error_to_response (ldap_error); + modify_op->current_card = build_card_from_entry (ldap, e, + &modify_op->existing_objectclasses); + modify_op->current_vcard = e_card_simple_get_vcard_assume_utf8 (modify_op->current_card); } - else - response = GNOME_Evolution_Addressbook_BookListener_CardNotFound; + else if (msg_type == LDAP_RES_SEARCH_RESULT) { + int ldap_error; + LDAPMod **ldap_mods; + GPtrArray *mod_array; + gboolean differences; + gboolean need_new_dn; + int modify_card_msgid; - pas_book_respond_remove (remove_op->op.book, - response); + /* grab the result code, and set up the actual modify + if it was successful */ + ldap_parse_result (bl->priv->ldap, res, &ldap_error, + NULL, NULL, NULL, NULL, 0); - if (op->view) - pas_book_view_notify_complete (op->view); + if (ldap_error != LDAP_SUCCESS) { + /* more here i'm sure */ + pas_book_respond_modify (op->book, + ldap_error_to_response (ldap_error)); + ldap_op_finished (op); + return; + } - /* we're synchronous */ - return TRUE; + /* build our mods */ + mod_array = build_mods_from_ecards (bl, modify_op->current_card, modify_op->card, &need_new_dn); + differences = mod_array->len > 0; + + if (differences) { + /* remove the NULL at the end */ + g_ptr_array_remove (mod_array, NULL); + + /* add our objectclass(es), making sure + evolutionPerson is there if it's supported */ + add_objectclass_mod (bl, mod_array, modify_op->existing_objectclasses); + + /* then put the NULL back */ + g_ptr_array_add (mod_array, NULL); + + ldap_mods = (LDAPMod**)mod_array->pdata; + + /* actually perform the ldap modify */ + ldap_error = ldap_modify_ext (ldap, modify_op->id, ldap_mods, + NULL, NULL, &modify_card_msgid); + + if (ldap_error == LDAP_SUCCESS) { + op->handler = modify_card_modify_handler; + ldap_op_change_id ((LDAPOp*)modify_op, + modify_card_msgid); + } + else { + g_warning ("ldap_modify_ext returned %d\n", ldap_error); + pas_book_respond_modify (op->book, + ldap_error_to_response (ldap_error)); + ldap_op_finished (op); + return; + } + } + + /* and clean up */ + free_mods (mod_array); + } + else { + g_warning ("unhandled result type %d returned", msg_type); + pas_book_respond_modify (op->book, + GNOME_Evolution_Addressbook_BookListener_OtherError); + ldap_op_finished (op); + } } static void -remove_card_dtor (PASBackend *backend, LDAPOp *op) +modify_card_dtor (LDAPOp *op) { - LDAPRemoveOp *remove_op = (LDAPRemoveOp*)op; - - if (op->view) - bonobo_object_release_unref(bonobo_object_corba_objref(BONOBO_OBJECT(op->view)), NULL); + LDAPModifyOp *modify_op = (LDAPModifyOp*)op; - g_free (remove_op->id); - g_free (remove_op); + g_list_foreach (modify_op->existing_objectclasses, (GFunc)g_free, NULL); + g_list_free (modify_op->existing_objectclasses); + g_free (modify_op->current_vcard); + if (modify_op->current_card) + gtk_object_unref (GTK_OBJECT (modify_op->current_card)); + g_free (modify_op->vcard); + if (modify_op->card) + gtk_object_unref (GTK_OBJECT (modify_op->card)); + g_free (modify_op); } static void -pas_backend_ldap_process_remove_card (PASBackend *backend, +pas_backend_ldap_process_modify_card (PASBackend *backend, PASBook *book, PASRequest *req) { - LDAPRemoveOp *remove_op = g_new (LDAPRemoveOp, 1); + LDAPModifyOp *modify_op = g_new0 (LDAPModifyOp, 1); PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); + ECard *new_ecard; + int ldap_error; + LDAP *ldap; + int modify_card_msgid; PASBookView *book_view = NULL; if (bl->priv->book_views) { PASBackendLDAPBookView *v = bl->priv->book_views->data; book_view = v->book_view; - bonobo_object_dup_ref(bonobo_object_corba_objref(BONOBO_OBJECT(book_view)), NULL); } - ldap_op_init ((LDAPOp*)remove_op, backend, book, - book_view, - remove_card_handler, remove_card_dtor); + modify_op->vcard = g_strdup (req->modify.vcard); + new_ecard = e_card_new (modify_op->vcard); + modify_op->card = e_card_simple_new (new_ecard); + gtk_object_unref (GTK_OBJECT (new_ecard)); + modify_op->id = e_card_simple_get_id(modify_op->card); + + ldap = bl->priv->ldap; + + if (book_view) + pas_book_view_notify_status_message (book_view, _("Modifying card from LDAP server...")); - remove_op->id = req->id; + ldap_error = ldap_search_ext (ldap, modify_op->id, + LDAP_SCOPE_BASE, + "(objectclass=*)", + NULL, 0, NULL, NULL, + NULL, /* XXX timeout */ + 1, &modify_card_msgid); - ldap_op_process ((LDAPOp*)remove_op); + if (ldap_error == LDAP_SUCCESS) { + ldap_op_add ((LDAPOp*)modify_op, backend, book, + book_view, modify_card_msgid, + modify_card_search_handler, modify_card_dtor); + } + else { + g_warning ("ldap_search_ext returned %d\n", ldap_error); + pas_book_respond_modify (book, + GNOME_Evolution_Addressbook_BookListener_OtherError); + modify_card_dtor ((LDAPOp*)modify_op); + } } typedef struct { LDAPOp op; - char *vcard; -} LDAPModifyOp; +} LDAPGetVCardOp; -static gboolean -modify_card_handler (PASBackend *backend, LDAPOp *op) +static void +get_vcard_handler (LDAPOp *op, LDAPMessage *res) { - LDAPModifyOp *modify_op = (LDAPModifyOp*)op; - PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); - ECard *new_ecard; - const char *id; - int response; - int ldap_error = LDAP_SUCCESS; - GPtrArray *mod_array; - LDAPMod **ldap_mods; - LDAP *ldap; - ECardSimple *current_card; - GList *existing_objectclasses = NULL; - - new_ecard = e_card_new (modify_op->vcard); - id = e_card_get_id(new_ecard); - - ldap = bl->priv->ldap; - - if (op->view) - pas_book_view_notify_status_message (op->view, _("Modifying card from LDAP server...")); - - current_card = search_for_dn_with_objectclasses (bl, id, &existing_objectclasses); - - if (current_card) { - ECardSimple *new_card = e_card_simple_new (new_ecard); - gboolean need_new_dn; - - /* build our mods */ - mod_array = build_mods_from_ecards (bl, current_card, new_card, &need_new_dn); - if (mod_array->len > 0) { - - /* remove the NULL at the end */ - g_ptr_array_remove (mod_array, NULL); - - /* add our objectclass(es), making sure - evolutionPerson is there if it's supported */ - add_objectclass_mod (bl, mod_array, existing_objectclasses); - - /* then put the NULL back */ - g_ptr_array_add (mod_array, NULL); - - ldap_mods = (LDAPMod**)mod_array->pdata; - - /* actually perform the ldap modify */ - ldap_error = ldap_modify_ext_s (ldap, id, ldap_mods, NULL, NULL); - if (ldap_error == LDAP_SUCCESS) { - - /* the card was modified, let's let the views know about it */ - GList *l; - for (l = bl->priv->book_views; l; l = l->next) { - CORBA_Environment ev; - gboolean old_match, new_match; - PASBackendLDAPBookView *view = l->data; - - CORBA_exception_init(&ev); - - bonobo_object_dup_ref(bonobo_object_corba_objref(BONOBO_OBJECT(view->book_view)), &ev); - - old_match = pas_backend_card_sexp_match_vcard (view->card_sexp, - e_card_simple_get_vcard_assume_utf8 (current_card)); - new_match = pas_backend_card_sexp_match_vcard (view->card_sexp, - modify_op->vcard); - if (old_match && new_match) - pas_book_view_notify_change_1 (view->book_view, modify_op->vcard); - else if (new_match) - pas_book_view_notify_add_1 (view->book_view, modify_op->vcard); - else /* if (old_match) */ - pas_book_view_notify_remove (view->book_view, e_card_simple_get_id (new_card)); - pas_book_view_notify_complete (view->book_view); - - bonobo_object_release_unref(bonobo_object_corba_objref(BONOBO_OBJECT(view->book_view)), &ev); - } - } - else { - ldap_perror (ldap, "ldap_modify_s"); - } - } - else { - g_print ("modify list empty. no modification sent\n"); + PASBackendLDAP *bl = PAS_BACKEND_LDAP (op->backend); + int msg_type; + + /* the msg_type will be either SEARCH_ENTRY (if we're + successful) or SEARCH_RESULT (if we're not), so we finish + the op after either */ + msg_type = ldap_msgtype (res); + if (msg_type == LDAP_RES_SEARCH_ENTRY) { + LDAPMessage *e = ldap_first_entry(bl->priv->ldap, res); + ECardSimple *simple; + char *vcard; + + if (!e) { + g_warning ("uh, this shouldn't happen"); + pas_book_respond_get_vcard (op->book, + GNOME_Evolution_Addressbook_BookListener_OtherError, + ""); + ldap_op_finished (op); + return; } - /* and clean up */ - free_mods (mod_array); - g_list_foreach (existing_objectclasses, (GFunc)g_free, NULL); - g_list_free (existing_objectclasses); - gtk_object_unref (GTK_OBJECT(new_card)); - gtk_object_unref (GTK_OBJECT(current_card)); + simple = build_card_from_entry (bl->priv->ldap, e, NULL); + vcard = e_card_simple_get_vcard_assume_utf8 (simple); + pas_book_respond_get_vcard (op->book, + GNOME_Evolution_Addressbook_BookListener_Success, + vcard); + g_free (vcard); + gtk_object_unref (GTK_OBJECT (simple)); + ldap_op_finished (op); + } + else if (msg_type == LDAP_RES_SEARCH_RESULT) { + int ldap_error; + ldap_parse_result (bl->priv->ldap, res, &ldap_error, + NULL, NULL, NULL, NULL, 0); + pas_book_respond_get_vcard (op->book, ldap_error_to_response (ldap_error), ""); + ldap_op_finished (op); } else { - g_print ("didn't find original card\n"); + g_warning ("unhandled result type %d returned", msg_type); + pas_book_respond_get_vcard (op->book, GNOME_Evolution_Addressbook_BookListener_OtherError, + ""); + ldap_op_finished (op); } - response = ldap_error_to_response (ldap_error); - pas_book_respond_modify (modify_op->op.book, - response); - - if (op->view) - pas_book_view_notify_complete (op->view); - - /* we're synchronous */ - return TRUE; } static void -modify_card_dtor (PASBackend *backend, LDAPOp *op) +get_vcard_dtor (LDAPOp *op) { - LDAPModifyOp *modify_op = (LDAPModifyOp*)op; + LDAPGetVCardOp *get_vcard_op = (LDAPGetVCardOp*)op; - if (op->view) - bonobo_object_release_unref(bonobo_object_corba_objref(BONOBO_OBJECT(op->view)), NULL); - - g_free (modify_op->vcard); - g_free (modify_op); + g_free (get_vcard_op); } static void -pas_backend_ldap_process_modify_card (PASBackend *backend, - PASBook *book, - PASRequest *req) +pas_backend_ldap_process_get_vcard (PASBackend *backend, + PASBook *book, + PASRequest *req) { - LDAPModifyOp *modify_op = g_new (LDAPModifyOp, 1); + LDAPGetVCardOp *get_vcard_op = g_new0 (LDAPGetVCardOp, 1); PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); + LDAP *ldap = bl->priv->ldap; + int get_vcard_msgid; PASBookView *book_view = NULL; + int ldap_error; if (bl->priv->book_views) { PASBackendLDAPBookView *v = bl->priv->book_views->data; book_view = v->book_view; - bonobo_object_dup_ref(bonobo_object_corba_objref(BONOBO_OBJECT(book_view)), NULL); } + + ldap_error = ldap_search_ext (ldap, req->get_vcard.id, + LDAP_SCOPE_BASE, + "(objectclass=*)", + NULL, 0, NULL, NULL, + NULL, /* XXX timeout */ + 1, &get_vcard_msgid); - ldap_op_init ((LDAPOp*)modify_op, backend, book, - book_view, - modify_card_handler, modify_card_dtor); - - modify_op->vcard = req->vcard; - - ldap_op_process ((LDAPOp*)modify_op); -} - - -static void -pas_backend_ldap_process_get_vcard (PASBackend *backend, - PASBook *book, - PASRequest *req) -{ - PASBackendLDAP *bl; - ECardSimple *simple; - - bl = PAS_BACKEND_LDAP (pas_book_get_backend (book)); - - simple = search_for_dn (bl, req->id); - - if (simple) { - pas_book_respond_get_vcard (book, - GNOME_Evolution_Addressbook_BookListener_Success, - e_card_simple_get_vcard_assume_utf8 (simple)); - gtk_object_unref (GTK_OBJECT (simple)); + if (ldap_error == LDAP_SUCCESS) { + ldap_op_add ((LDAPOp*)get_vcard_op, backend, book, + book_view, get_vcard_msgid, + get_vcard_handler, get_vcard_dtor); } else { pas_book_respond_get_vcard (book, - GNOME_Evolution_Addressbook_BookListener_CardNotFound, + ldap_error_to_response (ldap_error), ""); + get_vcard_dtor ((LDAPOp*)get_vcard_op); } } typedef struct { LDAPOp op; - PASBook *book; + PASBackendLDAPCursorPrivate *cursor_data; + gboolean responded; /* if FALSE, we need to free cursor_data in the dtor */ } LDAPGetCursorOp; static long @@ -1344,22 +1582,25 @@ get_nth(PASCardCursor *cursor, long n, gpointer data) static void cursor_destroy(GtkObject *object, gpointer data) { - CORBA_Environment ev; - GNOME_Evolution_Addressbook_Book corba_book; PASBackendLDAPCursorPrivate *cursor_data = (PASBackendLDAPCursorPrivate *) data; - corba_book = bonobo_object_corba_objref(BONOBO_OBJECT(cursor_data->book)); + if (cursor_data->book) { + CORBA_Environment ev; + GNOME_Evolution_Addressbook_Book corba_book; - CORBA_exception_init(&ev); + corba_book = bonobo_object_corba_objref(BONOBO_OBJECT(cursor_data->book)); - GNOME_Evolution_Addressbook_Book_unref(corba_book, &ev); + CORBA_exception_init(&ev); + + GNOME_Evolution_Addressbook_Book_unref(corba_book, &ev); - if (ev._major != CORBA_NO_EXCEPTION) { - g_warning("cursor_destroy: Exception unreffing " - "corba book.\n"); - } + if (ev._major != CORBA_NO_EXCEPTION) { + g_warning("cursor_destroy: Exception unreffing " + "corba book.\n"); + } - CORBA_exception_free(&ev); + CORBA_exception_free(&ev); + } /* free the ldap specific cursor information */ g_list_foreach (cursor_data->elements, (GFunc)g_free, NULL); @@ -1369,97 +1610,72 @@ cursor_destroy(GtkObject *object, gpointer data) } static void -pas_backend_ldap_build_all_cards_list(PASBackend *backend, - PASBackendLDAPCursorPrivate *cursor_data) +get_cursor_handler (LDAPOp *op, LDAPMessage *res) { - 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, - bl->priv->ldap_scope, - "(objectclass=*)", - NULL, 0, &res)) != LDAP_SUCCESS) { - g_warning ("ldap error '%s' in " - "pas_backend_ldap_build_all_cards_list\n", - ldap_err2string(ldap_error)); - } + LDAPGetCursorOp *cursor_op = (LDAPGetCursorOp*)op; + PASBackendLDAP *bl = PAS_BACKEND_LDAP (op->backend); + LDAP *ldap = bl->priv->ldap; + int msg_type; - cursor_data->elements = NULL; + msg_type = ldap_msgtype (res); + if (msg_type == LDAP_RES_SEARCH_ENTRY) { + LDAPMessage *e; - cursor_data->num_elements = ldap_count_entries (ldap, res); + e = ldap_first_entry (ldap, res); + while (e) { + ECardSimple *simple; + + simple = build_card_from_entry (ldap, e, NULL); + if (simple) { + char *vcard = e_card_simple_get_vcard_assume_utf8 (simple); + cursor_op->cursor_data->num_elements ++; + cursor_op->cursor_data->elements = g_list_prepend (cursor_op->cursor_data->elements, + vcard); + gtk_object_unref (GTK_OBJECT (simple)); + } + } + } + else if (msg_type == LDAP_RES_SEARCH_RESULT) { + PASCardCursor *cursor = CORBA_OBJECT_NIL; + int ldap_error; + ldap_parse_result (bl->priv->ldap, res, &ldap_error, + NULL, NULL, NULL, NULL, 0); - e = ldap_first_entry(ldap, res); + if (ldap_error == LDAP_SUCCESS) { + cursor = pas_card_cursor_new(get_length, + get_nth, + cursor_op->cursor_data); - while (NULL != e) { + gtk_signal_connect(GTK_OBJECT(cursor), "destroy", + GTK_SIGNAL_FUNC(cursor_destroy), cursor_op->cursor_data); - /* for now just make a list of the dn's */ -#if 0 - for ( a = ldap_first_attribute( ldap, e, &ber ); a != NULL; - a = ldap_next_attribute( ldap, e, ber ) ) { + cursor_op->responded = TRUE; } -#else - cursor_data->elements = g_list_prepend(cursor_data->elements, - g_strdup(ldap_get_dn(ldap, e))); -#endif - e = ldap_next_entry(ldap, e); - } + pas_book_respond_get_cursor (cursor_op->cursor_data->book, + ldap_error_to_response (ldap_error), + cursor); - ldap_msgfree(res); + ldap_op_finished (op); + } + else { + g_warning ("unhandled result type %d returned", msg_type); + pas_book_respond_get_cursor (op->book, + GNOME_Evolution_Addressbook_BookListener_OtherError, + CORBA_OBJECT_NIL); + ldap_op_finished (op); + } } - -static gboolean -get_cursor_handler (PASBackend *backend, LDAPOp *op) +static void +get_cursor_dtor (LDAPOp *op) { LDAPGetCursorOp *cursor_op = (LDAPGetCursorOp*)op; - CORBA_Environment ev; - int ldap_error = 0; - PASCardCursor *cursor; - GNOME_Evolution_Addressbook_Book corba_book; - PASBackendLDAPCursorPrivate *cursor_data; - PASBook *book = cursor_op->book; - - cursor_data = g_new(PASBackendLDAPCursorPrivate, 1); - cursor_data->backend = backend; - cursor_data->book = book; - - pas_backend_ldap_build_all_cards_list(backend, cursor_data); - - corba_book = bonobo_object_corba_objref(BONOBO_OBJECT(book)); - - CORBA_exception_init(&ev); - GNOME_Evolution_Addressbook_Book_ref(corba_book, &ev); - - if (ev._major != CORBA_NO_EXCEPTION) { - g_warning("pas_backend_file_process_get_cursor: Exception reffing " - "corba book.\n"); + if (!cursor_op->responded) { + cursor_destroy (NULL, cursor_op->cursor_data); } - CORBA_exception_free(&ev); - - cursor = pas_card_cursor_new(get_length, - get_nth, - cursor_data); - - gtk_signal_connect(GTK_OBJECT(cursor), "destroy", - GTK_SIGNAL_FUNC(cursor_destroy), cursor_data); - - pas_book_respond_get_cursor (book, - ldap_error_to_response (ldap_error), - cursor); - - /* we're synchronous */ - return TRUE; -} - -static void -get_cursor_dtor (PASBackend *backend, LDAPOp *op) -{ g_free (op); } @@ -1468,11 +1684,53 @@ pas_backend_ldap_process_get_cursor (PASBackend *backend, PASBook *book, PASRequest *req) { - LDAPGetCursorOp *op = g_new (LDAPGetCursorOp, 1); + PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); + LDAP *ldap = bl->priv->ldap; + int ldap_error; + int get_cursor_msgid; + LDAPGetCursorOp *cursor_op; + + cursor_op = g_new0 (LDAPGetCursorOp, 1); + cursor_op->cursor_data = g_new0 (PASBackendLDAPCursorPrivate, 1); + + ldap_error = ldap_search_ext (ldap, + bl->priv->ldap_rootdn, + bl->priv->ldap_scope, + "(objectclass=*)", + NULL, 0, + NULL, NULL, NULL, /* timeout */ + bl->priv->ldap_limit, &get_cursor_msgid); + + if (ldap_error == LDAP_SUCCESS) { + CORBA_Environment ev; + GNOME_Evolution_Addressbook_Book corba_book; + + corba_book = bonobo_object_corba_objref(BONOBO_OBJECT(book)); - ldap_op_init ((LDAPOp*)op, backend, book, NULL, get_cursor_handler, get_cursor_dtor); + CORBA_exception_init(&ev); - ldap_op_process ((LDAPOp*)op); + GNOME_Evolution_Addressbook_Book_ref(corba_book, &ev); + + cursor_op->cursor_data->backend = backend; + cursor_op->cursor_data->book = book; + + if (ev._major != CORBA_NO_EXCEPTION) { + g_warning("pas_backend_ldap_process_get_cursor: Exception reffing " + "corba book.\n"); + } + + CORBA_exception_free(&ev); + + + ldap_op_add ((LDAPOp*)cursor_op, backend, book, + NULL, get_cursor_msgid, get_cursor_handler, get_cursor_dtor); + } + else { + pas_book_respond_get_cursor (book, + ldap_error_to_response (ldap_error), + CORBA_OBJECT_NIL); + get_cursor_dtor ((LDAPOp*)cursor_op); + } } @@ -2191,9 +2449,18 @@ query_prop_to_ldap(gchar *query_prop) typedef struct { LDAPOp op; - char *ldap_query; - PASBackendLDAP *bl; PASBackendLDAPBookView *view; + + /* grouping stuff */ + GList *pending_adds; /* the cards we're sending */ + int num_pending_adds; /* the number waiting to be sent */ + int target_pending_adds; /* the cutoff that forces a flush to the client, if it happens before the timeout */ + int num_sent_this_time; /* the number of cards we sent to the client before the most recent timeout */ + int num_sent_last_time; /* the number of cards we sent to the client before the previous timeout */ + glong grouping_time_start; + + /* used by search_handler to only send the status messages once */ + gboolean notified_receiving_results; } LDAPSearchOp; static ECardSimple * @@ -2263,25 +2530,13 @@ build_card_from_entry (LDAP *ldap, LDAPMessage *e, GList **existing_objectclasse return card; } -static void -send_pending_adds (PASBackendLDAPBookView *view) -{ - view->num_sent_this_time += view->num_pending_adds; - pas_book_view_notify_add (view->book_view, view->pending_adds); - g_list_foreach (view->pending_adds, (GFunc)g_free, NULL); - view->pending_adds = NULL; - view->num_pending_adds = 0; -} - static gboolean -poll_ldap (LDAPSearchOp *op) +poll_ldap (PASBackendLDAP *bl) { - PASBackendLDAPBookView *view = op->view; - PASBackendLDAP *bl = op->bl; + GList *list; LDAP *ldap = bl->priv->ldap; int rc; - LDAPMessage *res, *e; - static int received = 0; + LDAPMessage *res; GTimeVal cur_time; glong cur_millis; struct timeval timeout; @@ -2289,13 +2544,9 @@ poll_ldap (LDAPSearchOp *op) timeout.tv_sec = 0; timeout.tv_usec = LDAP_RESULT_TIMEOUT_MILLIS * 1000; - if (!view->notified_receiving_results) { - view->notified_receiving_results = TRUE; - pas_book_view_notify_status_message (view->book_view, _("Receiving LDAP search results...")); - } - - rc = ldap_result (ldap, view->search_msgid, 0, &timeout, &res); + rc = ldap_result (ldap, LDAP_RES_ANY, 0, &timeout, &res); if (rc != 0) {/* rc == 0 means timeout exceeded */ +#if 0 if (rc == -1 && received == 0) { pas_book_view_notify_status_message (view->book_view, _("Restarting search.")); /* connection went down and we never got any. */ @@ -2305,139 +2556,134 @@ poll_ldap (LDAPSearchOp *op) ldap_op_restart ((LDAPOp*)op); return FALSE; } - - if (rc != LDAP_RES_SEARCH_ENTRY) { - view->search_timeout = 0; - if (view->num_pending_adds) - send_pending_adds (view); - pas_book_view_notify_complete (view->book_view); - ldap_op_finished ((LDAPOp*)op); - received = 0; - return FALSE; +#endif + if (rc == -1) { + g_warning ("ldap_result returned -1"); } + else { + int msgid = ldap_msgid (res); + LDAPOp *op; - received = 1; - - e = ldap_first_entry(ldap, res); - - while (NULL != e) { - ECardSimple *card = build_card_from_entry (ldap, e, NULL); - - view->pending_adds = g_list_append (view->pending_adds, - e_card_simple_get_vcard_assume_utf8 (card)); - view->num_pending_adds ++; + op = g_hash_table_lookup (bl->priv->id_to_op, &msgid); + if (!op) + g_error ("unknown operation, msgid = %d", msgid); - gtk_object_unref (GTK_OBJECT(card)); + op->handler (op, res); - e = ldap_next_entry(ldap, e); + ldap_msgfree(res); } - - ldap_msgfree(res); } g_get_current_time (&cur_time); cur_millis = TV_TO_MILLIS (cur_time); + + for (list = bl->priv->book_views; list; list = g_list_next(list)) { + PASBackendLDAPBookView *view = list->data; + if (view->search_op) + ldap_search_op_timeout (view->search_op, cur_millis); + } + + return TRUE; +} + +static void +send_pending_adds (LDAPSearchOp *search_op) +{ + search_op->num_sent_this_time += search_op->num_pending_adds; + pas_book_view_notify_add (search_op->op.view, search_op->pending_adds); + g_list_foreach (search_op->pending_adds, (GFunc)g_free, NULL); + search_op->pending_adds = NULL; + search_op->num_pending_adds = 0; +} + +static void +ldap_search_op_timeout (LDAPOp *op, glong cur_millis) +{ + LDAPSearchOp *search_op = (LDAPSearchOp*)op; - if (cur_millis - view->grouping_time_start > GROUPING_MINIMUM_WAIT) { + if (cur_millis - search_op->grouping_time_start > GROUPING_MINIMUM_WAIT) { - if (view->num_pending_adds >= view->target_pending_adds) - send_pending_adds (view); + if (search_op->num_pending_adds >= search_op->target_pending_adds) + send_pending_adds (search_op); - if (cur_millis - view->grouping_time_start > GROUPING_MAXIMUM_WAIT) { + if (cur_millis - search_op->grouping_time_start > GROUPING_MAXIMUM_WAIT) { GTimeVal new_start; - if (view->num_pending_adds) - send_pending_adds (view); - view->target_pending_adds = MIN (GROUPING_MAXIMUM_SIZE, - (view->num_sent_this_time + view->num_sent_last_time) / 2); - view->target_pending_adds = MAX (view->target_pending_adds, 1); + if (search_op->num_pending_adds) + send_pending_adds (search_op); + search_op->target_pending_adds = MIN (GROUPING_MAXIMUM_SIZE, + (search_op->num_sent_this_time + search_op->num_sent_last_time) / 2); + search_op->target_pending_adds = MAX (search_op->target_pending_adds, 1); #ifdef PERFORMANCE_SPEW printf ("num sent this time %d, last time %d, target pending adds set to %d\n", - view->num_sent_this_time, - view->num_sent_last_time, - view->target_pending_adds); + search_op->num_sent_this_time, + search_op->num_sent_last_time, + search_op->target_pending_adds); #endif g_get_current_time (&new_start); - view->grouping_time_start = TV_TO_MILLIS (new_start); - view->num_sent_last_time = view->num_sent_this_time; - view->num_sent_this_time = 0; + search_op->grouping_time_start = TV_TO_MILLIS (new_start); + search_op->num_sent_last_time = search_op->num_sent_this_time; + search_op->num_sent_this_time = 0; } } - - return TRUE; } -static gboolean -ldap_search_handler (PASBackend *backend, LDAPOp *op) +static void +ldap_search_handler (LDAPOp *op, LDAPMessage *res) { - LDAPSearchOp *search_op = (LDAPSearchOp*) op; - - if (op->view) - pas_book_view_notify_status_message (op->view, _("Searching...")); - - /* 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(PAS_BACKEND_LDAP (backend), search_op->view->search); + LDAPSearchOp *search_op = (LDAPSearchOp*)op; + PASBackendLDAP *bl = PAS_BACKEND_LDAP (op->backend); + LDAP *ldap = bl->priv->ldap; + LDAPMessage *e; + int msg_type; - if (search_op->ldap_query != NULL) { - PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); - PASBackendLDAPBookView *view = search_op->view; - LDAP *ldap = bl->priv->ldap; - int ldap_err; - GTimeVal search_start; + if (!search_op->notified_receiving_results) { + search_op->notified_receiving_results = TRUE; + pas_book_view_notify_status_message (op->view, _("Receiving LDAP search results...")); + } - view->pending_adds = NULL; - view->num_pending_adds = 0; - view->target_pending_adds = GROUPING_INITIAL_SIZE; + msg_type = ldap_msgtype (res); + if (msg_type == LDAP_RES_SEARCH_ENTRY) { + e = ldap_first_entry(ldap, res); - g_get_current_time (&search_start); - view->grouping_time_start = TV_TO_MILLIS (search_start); - view->num_sent_last_time = 0; - view->num_sent_this_time = 0; - view->notified_receiving_results = FALSE; + while (NULL != e) { + ECardSimple *card = build_card_from_entry (ldap, e, NULL); - ldap_err = ldap_search_ext (ldap, bl->priv->ldap_rootdn, - bl->priv->ldap_scope, - search_op->ldap_query, - NULL, 0, - NULL, /* XXX */ - NULL, /* XXX */ - NULL, - bl->priv->ldap_limit, &view->search_msgid); + search_op->pending_adds = g_list_append (search_op->pending_adds, + e_card_simple_get_vcard_assume_utf8 (card)); + search_op->num_pending_adds ++; - if (ldap_err != LDAP_SUCCESS) { - pas_book_view_notify_status_message (view->book_view, ldap_err2string(ldap_err)); - return TRUE; /* act synchronous in this case */ - } + gtk_object_unref (GTK_OBJECT(card)); - if (view->search_msgid == -1) { - pas_book_view_notify_status_message (view->book_view, ldap_err2string(ldap_err)); - return TRUE; /* act synchronous in this case */ - } - else { - view->search_timeout = g_timeout_add (LDAP_POLL_INTERVAL, - (GSourceFunc) poll_ldap, - search_op); + e = ldap_next_entry(ldap, e); } - - /* we're async */ - return FALSE; + } + else if (msg_type == LDAP_RES_SEARCH_RESULT) { + /* the entry that marks the end of our search */ + if (search_op->num_pending_adds) + send_pending_adds (search_op); + pas_book_view_notify_complete (search_op->op.view); + ldap_op_finished (op); } 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; + g_warning ("unhandled search result type %d returned", msg_type); + if (search_op->num_pending_adds) + send_pending_adds (search_op); + pas_book_view_notify_complete (search_op->op.view); + ldap_op_finished (op); } } static void -ldap_search_dtor (PASBackend *backend, LDAPOp *op) +ldap_search_dtor (LDAPOp *op) { LDAPSearchOp *search_op = (LDAPSearchOp*) op; - g_free (search_op->ldap_query); + /* unhook us from our PASBackendLDAPBookView */ + if (search_op->view) + search_op->view->search_op = NULL; + g_free (search_op); } @@ -2446,19 +2692,64 @@ pas_backend_ldap_search (PASBackendLDAP *bl, PASBook *book, PASBackendLDAPBookView *view) { - LDAPSearchOp *op = g_new (LDAPSearchOp, 1); + LDAPSearchOp *search_op = g_new0 (LDAPSearchOp, 1); + char *ldap_query; - ldap_op_init ((LDAPOp*)op, PAS_BACKEND(bl), book, view->book_view, ldap_search_handler, ldap_search_dtor); + pas_book_view_notify_status_message (view->book_view, _("Searching...")); - op->ldap_query = NULL; - op->view = view; - op->bl = bl; + ldap_query = pas_backend_ldap_build_query(bl, view->search); + + if (ldap_query != NULL) { + LDAP *ldap = bl->priv->ldap; + int ldap_err; + GTimeVal search_start; + int search_msgid; + + ldap_err = ldap_search_ext (ldap, bl->priv->ldap_rootdn, + bl->priv->ldap_scope, + ldap_query, + NULL, 0, + NULL, /* XXX */ + NULL, /* XXX */ + NULL, /* XXX timeout */ + bl->priv->ldap_limit, &search_msgid); + + g_free (ldap_query); + + if (ldap_err != LDAP_SUCCESS) { + pas_book_view_notify_status_message (view->book_view, ldap_err2string(ldap_err)); + return; + } + else if (search_msgid == -1) { + pas_book_view_notify_status_message (view->book_view, + _("Error performing search")); + return; + } + else { + LDAPSearchOp *op = g_new0 (LDAPSearchOp, 1); + + op->target_pending_adds = GROUPING_INITIAL_SIZE; + + g_get_current_time (&search_start); + op->grouping_time_start = TV_TO_MILLIS (search_start); - /* keep track of the search op so we can delete it from the - list if the view is destroyed */ - view->search_op = (LDAPOp*)op; + op->view = view; + + view->search_op = (LDAPOp*)op; + + ldap_op_add ((LDAPOp*)op, PAS_BACKEND(bl), book, view->book_view, + search_msgid, + ldap_search_handler, ldap_search_dtor); + + } + return; + } + else { + g_warning ("LDAP problem converting search query %s\n", search_op->view->search); + pas_book_view_notify_status_message (view->book_view, _("Could not parse query string")); + return; + } - ldap_op_process ((LDAPOp*)op); } static void @@ -2467,13 +2758,12 @@ pas_backend_ldap_process_get_book_view (PASBackend *backend, PASRequest *req) { PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); - CORBA_Environment ev; PASBookView *book_view; PASBackendLDAPBookView *view; - g_return_if_fail (req->listener != NULL); + g_return_if_fail (req->get_book_view.listener != NULL); - book_view = pas_book_view_new (req->listener); + book_view = pas_book_view_new (req->get_book_view.listener); bonobo_object_ref(BONOBO_OBJECT(book)); gtk_signal_connect(GTK_OBJECT(book_view), "destroy", @@ -2481,7 +2771,7 @@ pas_backend_ldap_process_get_book_view (PASBackend *backend, view = g_new0(PASBackendLDAPBookView, 1); view->book_view = book_view; - view->search = g_strdup(req->search); + view->search = g_strdup(req->get_book_view.search); view->card_sexp = pas_backend_card_sexp_new (view->search); view->blpriv = bl->priv; @@ -2495,18 +2785,7 @@ pas_backend_ldap_process_get_book_view (PASBackend *backend, pas_backend_ldap_search (bl, book, view); - g_free (req->search); - CORBA_exception_init(&ev); - bonobo_object_unref (BONOBO_OBJECT (book_view)); - bonobo_object_release_unref(req->listener, &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); - } static void @@ -2528,9 +2807,9 @@ pas_backend_ldap_process_authenticate_user (PASBackend *backend, int ldap_error; char *dn = NULL; - if (!strcmp (req->auth_method, "ldap/simple-email")) { + if (!strcmp (req->auth_user.auth_method, "ldap/simple-email")) { LDAPMessage *res, *e; - char *query = g_strdup_printf ("(mail=%s)", req->user); + char *query = g_strdup_printf ("(mail=%s)", req->auth_user.user); ldap_error = ldap_search_s (bl->priv->ldap, bl->priv->ldap_rootdn, @@ -2550,15 +2829,15 @@ pas_backend_ldap_process_authenticate_user (PASBackend *backend, return; } } - else { - dn = g_strdup (req->user); + else if (!strcmp (req->auth_user.auth_method, "ldap/simple-binddn")) { + dn = g_strdup (req->auth_user.user); } /* now authenticate against the DN we were either supplied or queried for */ printf ("authenticating as %s\n", dn); ldap_error = ldap_simple_bind_s(bl->priv->ldap, dn, - req->passwd); + req->auth_user.passwd); g_free (dn); pas_book_respond_authenticate_user (book, @@ -2566,7 +2845,7 @@ pas_backend_ldap_process_authenticate_user (PASBackend *backend, bl->priv->writable = (ldap_error == LDAP_SUCCESS); - if (!bl->priv->evolutionPersonChecked) + if (!bl->priv->schema_checked) check_schema_support (bl); pas_book_report_writable (book, bl->priv->writable); @@ -2639,7 +2918,7 @@ pas_backend_ldap_process_client_requests (PASBook *book) break; } - g_free (req); + pas_book_free_request (req); } static void @@ -2845,10 +3124,14 @@ pas_backend_ldap_new (void) return PAS_BACKEND (backend); } -static void -call_dtor (LDAPOp *op, gpointer data) +static gboolean +call_dtor (int msgid, LDAPOp *op, gpointer data) { - op->dtor (op->backend, op); + ldap_abandon (PAS_BACKEND_LDAP(op->backend)->priv->ldap, op->id); + + op->dtor (op); + + return TRUE; } static void @@ -2858,8 +3141,11 @@ pas_backend_ldap_destroy (GtkObject *object) bl = PAS_BACKEND_LDAP (object); - g_list_foreach (bl->priv->pending_ops, (GFunc)call_dtor, NULL); - g_list_free (bl->priv->pending_ops); + g_hash_table_foreach_remove (bl->priv->id_to_op, (GHRFunc)call_dtor, NULL); + g_hash_table_destroy (bl->priv->id_to_op); + + if (bl->priv->poll_timeout != -1) + g_source_remove (bl->priv->poll_timeout); if (bl->priv->supported_fields) gtk_object_unref (GTK_OBJECT (bl->priv->supported_fields)); @@ -2875,6 +3161,9 @@ pas_backend_ldap_class_init (PASBackendLDAPClass *klass) GtkObjectClass *object_class = (GtkObjectClass *) klass; PASBackendClass *parent_class; + /* get client side information (extensions present in the library) */ + get_ldap_library_info (); + pas_backend_ldap_parent_class = gtk_type_class (pas_backend_get_type ()); parent_class = PAS_BACKEND_CLASS (klass); @@ -2897,8 +3186,9 @@ pas_backend_ldap_init (PASBackendLDAP *backend) priv = g_new0 (PASBackendLDAPPrivate, 1); priv->supported_fields = e_list_new ((EListCopyFunc)g_strdup, (EListFreeFunc)g_free, NULL); - priv->ldap_limit = 100; - + priv->ldap_limit = 100; + priv->id_to_op = g_hash_table_new (g_int_hash, g_int_equal); + priv->poll_timeout = -1; backend->priv = priv; } diff --git a/addressbook/backend/pas/pas-book.c b/addressbook/backend/pas/pas-book.c index b0b35b2c0f..9bb2cd348e 100644 --- a/addressbook/backend/pas/pas-book.c +++ b/addressbook/backend/pas/pas-book.c @@ -74,7 +74,7 @@ pas_book_queue_create_card (PASBook *book, const char *vcard) req = g_new0 (PASRequest, 1); req->op = CreateCard; - req->vcard = g_strdup (vcard); + req->create.vcard = g_strdup (vcard); pas_book_queue_request (book, req); } @@ -86,7 +86,7 @@ pas_book_queue_remove_card (PASBook *book, const char *id) req = g_new0 (PASRequest, 1); req->op = RemoveCard; - req->id = g_strdup (id); + req->remove.id = g_strdup (id); pas_book_queue_request (book, req); } @@ -98,7 +98,7 @@ pas_book_queue_modify_card (PASBook *book, const char *vcard) req = g_new0 (PASRequest, 1); req->op = ModifyCard; - req->vcard = g_strdup (vcard); + req->modify.vcard = g_strdup (vcard); pas_book_queue_request (book, req); } @@ -110,7 +110,7 @@ pas_book_queue_get_cursor (PASBook *book, const char *search) req = g_new0 (PASRequest, 1); req->op = GetCursor; - req->search = g_strdup(search); + req->get_cursor.search = g_strdup(search); pas_book_queue_request (book, req); } @@ -122,7 +122,7 @@ pas_book_queue_get_vcard (PASBook *book, const char *id) req = g_new0 (PASRequest, 1); req->op = GetVCard; - req->id = g_strdup(id); + req->get_vcard.id = g_strdup(id); pas_book_queue_request (book, req); } @@ -135,9 +135,9 @@ pas_book_queue_authenticate_user (PASBook *book, req = g_new0 (PASRequest, 1); req->op = AuthenticateUser; - req->user = g_strdup(user); - req->passwd = g_strdup(passwd); - req->auth_method = g_strdup(auth_method); + req->auth_user.user = g_strdup(user); + req->auth_user.passwd = g_strdup(passwd); + req->auth_user.auth_method = g_strdup(auth_method); pas_book_queue_request (book, req); } @@ -162,11 +162,11 @@ pas_book_queue_get_book_view (PASBook *book, const GNOME_Evolution_Addressbook_B req = g_new0 (PASRequest, 1); req->op = GetBookView; - req->search = g_strdup(search); + req->get_book_view.search = g_strdup(search); CORBA_exception_init (&ev); - req->listener = bonobo_object_dup_ref(listener, &ev); + req->get_book_view.listener = bonobo_object_dup_ref(listener, &ev); if (ev._major != CORBA_NO_EXCEPTION) { g_warning ("pas_book_queue_get_book_view: Exception " @@ -186,11 +186,11 @@ pas_book_queue_get_changes (PASBook *book, const GNOME_Evolution_Addressbook_Boo req = g_new0 (PASRequest, 1); req->op = GetChanges; - req->change_id= g_strdup(change_id); + req->get_changes.change_id= g_strdup(change_id); CORBA_exception_init (&ev); - req->listener = bonobo_object_dup_ref(listener, &ev); + req->get_changes.listener = bonobo_object_dup_ref(listener, &ev); if (ev._major != CORBA_NO_EXCEPTION) { g_warning ("pas_book_queue_get_changes: Exception " @@ -766,6 +766,63 @@ pas_book_new (PASBackend *backend, return book; } +void +pas_book_free_request (PASRequest *req) +{ + CORBA_Environment ev; + switch (req->op) { + case CreateCard: + g_free (req->create.id); + g_free (req->create.vcard); + break; + case RemoveCard: + g_free (req->remove.id); + break; + case ModifyCard: + g_free (req->modify.vcard); + break; + case GetVCard: + g_free (req->get_vcard.id); + break; + case GetCursor: + g_free (req->get_cursor.search); + break; + case GetBookView: + g_free (req->get_book_view.search); + CORBA_exception_init (&ev); + bonobo_object_release_unref (req->get_book_view.listener, &ev); + + if (ev._major != CORBA_NO_EXCEPTION) + g_message ("pas_book_free_request(GetBookView): could not release the listener"); + + CORBA_exception_free (&ev); + break; + case GetChanges: + g_free (req->get_changes.change_id); + CORBA_exception_init (&ev); + bonobo_object_release_unref (req->get_changes.listener, &ev); + + if (ev._major != CORBA_NO_EXCEPTION) + g_message ("pas_book_free_request(GetChanges): could not release the listener"); + + CORBA_exception_free (&ev); + break; + case CheckConnection: + /* nothing to free */ + break; + case AuthenticateUser: + g_free (req->auth_user.user); + g_free (req->auth_user.passwd); + g_free (req->auth_user.auth_method); + break; + case GetSupportedFields: + /* nothing to free */ + break; + } + + g_free (req); +} + static void pas_book_destroy (GtkObject *object) { @@ -774,11 +831,7 @@ pas_book_destroy (GtkObject *object) CORBA_Environment ev; for (l = book->priv->request_queue; l != NULL; l = l->next) { - PASRequest *req = l->data; - - g_free (req->id); - g_free (req->vcard); - g_free (req); + pas_book_free_request ((PASRequest *)l->data); } g_list_free (book->priv->request_queue); diff --git a/addressbook/backend/pas/pas-book.h b/addressbook/backend/pas/pas-book.h index 8afba26b45..a6f2fda3cc 100644 --- a/addressbook/backend/pas/pas-book.h +++ b/addressbook/backend/pas/pas-book.h @@ -38,16 +38,71 @@ typedef enum { } PASOperation; typedef struct { - PASOperation op; - char *id; - char *vcard; - char *search; - char *change_id; - char *user; - char *passwd; - char *auth_method; + PASOperation op; + char *id; + char *vcard; +} PASCreateCardRequest; + +typedef struct { + PASOperation op; + char *id; +} PASRemoveCardRequest; + +typedef struct { + PASOperation op; + char *vcard; +} PASModifyCardRequest; + +typedef struct { + PASOperation op; + char *id; +} PASGetVCardRequest; + +typedef struct { + PASOperation op; + char *search; +} PASGetCursorRequest; + +typedef struct { + PASOperation op; + char *search; GNOME_Evolution_Addressbook_BookViewListener listener; - GNOME_Evolution_Addressbook_stringlist fields; +} PASGetBookViewRequest; + +typedef struct { + PASOperation op; + char *change_id; + GNOME_Evolution_Addressbook_BookViewListener listener; +} PASGetChangesRequest; + +typedef struct { + PASOperation op; +} PASCheckConnectionRequest; + +typedef struct { + PASOperation op; + char *user; + char *passwd; + char *auth_method; +} PASAuthenticateUserRequest; + +typedef struct { + PASOperation op; +} PASGetSupportedFieldsRequest; + +typedef union { + PASOperation op; + + PASCreateCardRequest create; + PASRemoveCardRequest remove; + PASModifyCardRequest modify; + PASGetVCardRequest get_vcard; + PASGetCursorRequest get_cursor; + PASGetBookViewRequest get_book_view; + PASGetChangesRequest get_changes; + PASCheckConnectionRequest check_connection; + PASAuthenticateUserRequest auth_user; + PASGetSupportedFieldsRequest get_supported_fields; } PASRequest; struct _PASBook { @@ -71,6 +126,7 @@ PASBackend *pas_book_get_backend (PASBook GNOME_Evolution_Addressbook_BookListener pas_book_get_listener (PASBook *book); int pas_book_check_pending (PASBook *book); PASRequest *pas_book_pop_request (PASBook *book); +void pas_book_free_request (PASRequest *request); void pas_book_respond_open (PASBook *book, GNOME_Evolution_Addressbook_BookListener_CallStatus status); void pas_book_respond_create (PASBook *book, -- cgit v1.2.3