diff options
Diffstat (limited to 'addressbook/backend/pas/pas-backend-ldap.c')
-rw-r--r-- | addressbook/backend/pas/pas-backend-ldap.c | 3555 |
1 files changed, 0 insertions, 3555 deletions
diff --git a/addressbook/backend/pas/pas-backend-ldap.c b/addressbook/backend/pas/pas-backend-ldap.c deleted file mode 100644 index 1e8677e559..0000000000 --- a/addressbook/backend/pas/pas-backend-ldap.c +++ /dev/null @@ -1,3555 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Author: - * Chris Toshok (toshok@ximian.com) - * - * Copyright 2000, Ximian, Inc. - */ - -#define DEBUG - -#include "config.h" -#include <fcntl.h> -#include <string.h> - -#ifdef DEBUG -#define LDAP_DEBUG -#define LDAP_DEBUG_ADD -#endif -#include <ldap.h> -#ifdef DEBUG -#undef LDAP_DEBUG -#endif - -#if LDAP_VENDOR_VERSION > 20000 -#define OPENLDAP2 -#else -#define OPENLDAP1 -#endif - -#ifdef OPENLDAP2 -#include <ldap_schema.h> -#endif - -#include <sys/time.h> - -#include <e-util/e-sexp.h> -#include <ebook/e-card-simple.h> -#include <libgnome/gnome-i18n.h> - -#include "pas-backend-ldap.h" -#include "pas-backend-card-sexp.h" -#include "pas-book.h" -#include "pas-card-cursor.h" - -#include <stdlib.h> - -/* this is broken currently, don't enable it */ -/*#define ENABLE_SASL_BINDS*/ - -typedef enum { - PAS_BACKEND_LDAP_TLS_NO, - PAS_BACKEND_LDAP_TLS_ALWAYS, - PAS_BACKEND_LDAP_TLS_WHEN_POSSIBLE, -} PASBackendLDAPUseTLS; - -/* interval for our poll_ldap timeout */ -#define LDAP_POLL_INTERVAL 20 - -/* timeout for ldap_result */ -#define LDAP_RESULT_TIMEOUT_MILLIS 10 - -/* smart grouping stuff */ -#define GROUPING_INITIAL_SIZE 1 -#define GROUPING_MAXIMUM_SIZE 200 - -/* the next two are in milliseconds */ -#define GROUPING_MINIMUM_WAIT 0 /* we never send updates faster than this, to avoid totally spamming the UI */ -#define GROUPING_MAXIMUM_WAIT 250 /* we always send updates (if there are pending cards) when we hit this */ - -#define TV_TO_MILLIS(timeval) ((timeval).tv_sec * 1000 + (timeval).tv_usec / 1000) - -/* the objectClasses we need */ -#define TOP "top" -#define PERSON "person" -#define ORGANIZATIONALPERSON "organizationalPerson" -#define INETORGPERSON "inetOrgPerson" -#define CALENTRY "calEntry" -#define EVOLUTIONPERSON "evolutionPerson" - -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; - gboolean connected; - - 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 */ - int ldap_timeout; /* the search timeout */ - - gchar *auth_dn; - gchar *auth_passwd; - - gboolean ldap_v3; /* TRUE if the server supports protocol - revision 3 (necessary for TLS) */ - gboolean starttls; /* TRUE if the *library* supports - starttls. will be false if openssl - was not built into openldap. */ - PASBackendLDAPUseTLS use_tls; - - EList *book_views; - - LDAP *ldap; - - EList *supported_fields; - EList *supported_auth_methods; - - /* whether or not there's support for the objectclass we need - to store all our additional fields */ - gboolean evolutionPersonSupported; - gboolean calEntrySupported; - gboolean evolutionPersonChecked; - - /* our operations */ - GHashTable *id_to_op; - int active_ops; - int poll_timeout; -}; - -struct _PASBackendLDAPCursorPrivate { - PASBackend *backend; - PASBook *book; - - GList *elements; - long num_elements; -}; - -struct _PASBackendLDAPBookView { - PASBookView *book_view; - PASBackendLDAPPrivate *blpriv; - gchar *search; - PASBackendCardSExp *card_sexp; - int limit; - - LDAPOp *search_op; -}; - -typedef void (*LDAPOpHandler)(LDAPOp *op, LDAPMessage *res); -typedef void (*LDAPOpDtor)(LDAPOp *op); - -struct LDAPOp { - LDAPOpHandler handler; - LDAPOpDtor dtor; - PASBackend *backend; - PASBook *book; - PASBookView *view; - int id; -}; - -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); -struct berval** email_ber (ECardSimple *card); -static gboolean email_compare (ECardSimple *ecard1, ECardSimple *ecard2); - -static void homephone_populate (ECardSimple *card, char **values); -struct berval** homephone_ber (ECardSimple *card); -static gboolean homephone_compare (ECardSimple *ecard1, ECardSimple *ecard2); - -static void business_populate (ECardSimple *card, char **values); -struct berval** business_ber (ECardSimple *card); -static gboolean business_compare (ECardSimple *ecard1, ECardSimple *ecard2); - -static void anniversary_populate (ECardSimple *card, char **values); -struct berval** anniversary_ber (ECardSimple *card); -static gboolean anniversary_compare (ECardSimple *ecard1, ECardSimple *ecard2); - -static void birthday_populate (ECardSimple *card, char **values); -struct berval** birthday_ber (ECardSimple *card); -static gboolean birthday_compare (ECardSimple *ecard1, ECardSimple *ecard2); - -static void category_populate (ECardSimple *card, char **values); -struct berval** category_ber (ECardSimple *card); -static gboolean category_compare (ECardSimple *ecard1, ECardSimple *ecard2); - -struct prop_info { - ECardSimpleField field_id; - char *query_prop; - char *ldap_attr; -#define PROP_TYPE_STRING 0x01 -#define PROP_TYPE_COMPLEX 0x02 -#define PROP_DN 0x04 -#define PROP_EVOLVE 0x08 - int prop_type; - - /* the remaining items are only used for the TYPE_COMPLEX props */ - - /* used when reading from the ldap server populates ECard with the values in **values. */ - void (*populate_ecard_func)(ECardSimple *card, char **values); - /* used when writing to an ldap server. returns a NULL terminated array of berval*'s */ - struct berval** (*ber_func)(ECardSimple *card); - /* used to compare list attributes */ - gboolean (*compare_func)(ECardSimple *card1, ECardSimple *card2); - -} prop_info[] = { - -#define COMPLEX_PROP(fid,q,a,ctor,ber,cmp) {fid, q, a, PROP_TYPE_COMPLEX, ctor, ber, cmp} -#define E_COMPLEX_PROP(fid,q,a,ctor,ber,cmp) {fid, q, a, PROP_TYPE_COMPLEX | PROP_EVOLVE, ctor, ber, cmp} -#define STRING_PROP(fid,q,a) {fid, q, a, PROP_TYPE_STRING} -#define E_STRING_PROP(fid,q,a) {fid, q, a, PROP_TYPE_STRING | PROP_EVOLVE} - - - /* name fields */ - STRING_PROP (E_CARD_SIMPLE_FIELD_FULL_NAME, "full_name", "cn" ), - STRING_PROP (E_CARD_SIMPLE_FIELD_FAMILY_NAME, "family_name", "sn" ), - - /* email addresses */ - COMPLEX_PROP (E_CARD_SIMPLE_FIELD_EMAIL, "email", "mail", email_populate, email_ber, email_compare), - - /* phone numbers */ - E_STRING_PROP (E_CARD_SIMPLE_FIELD_PHONE_PRIMARY, "primary_phone", "primaryPhone"), - COMPLEX_PROP (E_CARD_SIMPLE_FIELD_PHONE_BUSINESS, "business_phone", "telephoneNumber", business_populate, business_ber, business_compare), - COMPLEX_PROP (E_CARD_SIMPLE_FIELD_PHONE_HOME, "home_phone", "homePhone", homephone_populate, homephone_ber, homephone_compare), - STRING_PROP (E_CARD_SIMPLE_FIELD_PHONE_MOBILE, "mobile_phone", "mobile"), - E_STRING_PROP (E_CARD_SIMPLE_FIELD_PHONE_CAR, "car_phone", "carPhone"), - STRING_PROP (E_CARD_SIMPLE_FIELD_PHONE_BUSINESS_FAX, "business_fax", "facsimileTelephoneNumber"), - E_STRING_PROP (E_CARD_SIMPLE_FIELD_PHONE_HOME_FAX, "home_fax", "homeFacsimileTelephoneNumber"), - E_STRING_PROP (E_CARD_SIMPLE_FIELD_PHONE_OTHER, "other_phone", "otherPhone"), - E_STRING_PROP (E_CARD_SIMPLE_FIELD_PHONE_OTHER_FAX, "other_fax", "otherFacsimileTelephoneNumber"), - STRING_PROP (E_CARD_SIMPLE_FIELD_PHONE_ISDN, "isdn", "internationaliSDNNumber"), - STRING_PROP (E_CARD_SIMPLE_FIELD_PHONE_PAGER, "pager", "pager"), - E_STRING_PROP (E_CARD_SIMPLE_FIELD_PHONE_RADIO, "radio", "radio"), - E_STRING_PROP (E_CARD_SIMPLE_FIELD_PHONE_TELEX, "telex", "telex"), - E_STRING_PROP (E_CARD_SIMPLE_FIELD_PHONE_ASSISTANT, "assistant_phone", "assistantPhone"), - E_STRING_PROP (E_CARD_SIMPLE_FIELD_PHONE_COMPANY, "company_phone", "companyPhone"), - E_STRING_PROP (E_CARD_SIMPLE_FIELD_PHONE_CALLBACK, "callback_phone", "callbackPhone"), - E_STRING_PROP (E_CARD_SIMPLE_FIELD_PHONE_TTYTDD, "tty", "tty"), - - /* org information */ - STRING_PROP (E_CARD_SIMPLE_FIELD_ORG, "org", "o"), - STRING_PROP (E_CARD_SIMPLE_FIELD_ORG_UNIT, "org_unit", "ou"), - STRING_PROP (E_CARD_SIMPLE_FIELD_OFFICE, "office", "roomNumber"), - STRING_PROP (E_CARD_SIMPLE_FIELD_TITLE, "title", "title"), - E_STRING_PROP (E_CARD_SIMPLE_FIELD_ROLE, "role", "businessRole"), - E_STRING_PROP (E_CARD_SIMPLE_FIELD_MANAGER, "manager", "managerName"), - E_STRING_PROP (E_CARD_SIMPLE_FIELD_ASSISTANT, "assistant", "assistantName"), - - /* addresses */ - STRING_PROP (E_CARD_SIMPLE_FIELD_ADDRESS_BUSINESS, "business_address", "postalAddress"), - STRING_PROP (E_CARD_SIMPLE_FIELD_ADDRESS_HOME, "home_address", "homePostalAddress"), - E_STRING_PROP (E_CARD_SIMPLE_FIELD_ADDRESS_OTHER, "other_address", "otherPostalAddress"), - - /* misc fields */ - STRING_PROP (E_CARD_SIMPLE_FIELD_URL, "url", "labeledURI"), - /* map nickname to displayName */ - STRING_PROP (E_CARD_SIMPLE_FIELD_NICKNAME, "nickname", "displayName"), - E_STRING_PROP (E_CARD_SIMPLE_FIELD_SPOUSE, "spouse", "spouseName"), - E_STRING_PROP (E_CARD_SIMPLE_FIELD_NOTE, "note", "note"), - E_COMPLEX_PROP (E_CARD_SIMPLE_FIELD_ANNIVERSARY, "anniversary", "anniversary", anniversary_populate, anniversary_ber, anniversary_compare), - E_COMPLEX_PROP (E_CARD_SIMPLE_FIELD_BIRTH_DATE, "birth_date", "birthDate", birthday_populate, birthday_ber, birthday_compare), - E_STRING_PROP (E_CARD_SIMPLE_FIELD_MAILER, "mailer", "mailer"), - - E_STRING_PROP (E_CARD_SIMPLE_FIELD_FILE_AS, "file_as", "fileAs"), - E_COMPLEX_PROP (E_CARD_SIMPLE_FIELD_CATEGORIES, "categories", "category", category_populate, category_ber, category_compare), - - STRING_PROP (E_CARD_SIMPLE_FIELD_CALURI, "caluri", "calCalURI"), - STRING_PROP (E_CARD_SIMPLE_FIELD_FBURL, "fburl", "calFBURL"), - STRING_PROP (E_CARD_SIMPLE_FIELD_ICSCALENDAR, "icscalendar", "icsCalendar"), - -/* E_CARD_SIMPLE_FIELD_NAME_OR_ORG, */ - - -#undef E_STRING_PROP -#undef STRING_PROP -#undef E_COMPLEX_PROP -#undef COMPLEX_PROP -}; - -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(gpointer data, GObject *where_object_was) -{ - PASBook *book = (PASBook *)data; - PASBackendLDAP *bl; - EIterator *iter; - - bl = PAS_BACKEND_LDAP(pas_book_get_backend(book)); - - iter = e_list_get_iterator (bl->priv->book_views); - - while (e_iterator_is_valid (iter)) { - PASBackendLDAPBookView *view = (PASBackendLDAPBookView*)e_iterator_get (iter); - - if (view->book_view == (PASBookView*)where_object_was) { - GNOME_Evolution_Addressbook_Book corba_book; - CORBA_Environment ev; - - /* 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); - - /* free up the view structure */ - g_free (view->search); - g_object_unref (view->card_sexp); - g_free (view); - - /* and remove it from our list */ - e_iterator_delete (iter); - - /* unref the book now */ - corba_book = bonobo_object_corba_objref(BONOBO_OBJECT(book)); - - CORBA_exception_init(&ev); - - GNOME_Evolution_Addressbook_Book_unref(corba_book, &ev); - - if (ev._major != CORBA_NO_EXCEPTION) { - g_warning("view_destroy: Exception unreffing " - "corba book.\n"); - } - - CORBA_exception_free(&ev); - break; - } - - e_iterator_next (iter); - } - - g_object_unref (iter); - -} - -static void -book_view_notify_status (PASBookView *view, const char *status) -{ - if (!view) - return; - pas_book_view_notify_status_message (view, status); -} - -static PASBookView* -find_book_view (PASBackendLDAP *bl) -{ - EIterator *iter = e_list_get_iterator (bl->priv->book_views); - PASBookView *rv = NULL; - - if (e_iterator_is_valid (iter)) { - /* just always use the first book view */ - PASBackendLDAPBookView *v = (PASBackendLDAPBookView*)e_iterator_get(iter); - if (v) - rv = v->book_view; - } - - g_object_unref (iter); - - return rv; -} - -static void -add_to_supported_fields (PASBackendLDAP *bl, char **attrs, GHashTable *attr_hash) -{ - int i; - for (i = 0; attrs[i]; i ++) { - char *query_prop = g_hash_table_lookup (attr_hash, attrs[i]); - - if (query_prop) { - e_list_append (bl->priv->supported_fields, query_prop); - - /* handle the list attributes here */ - if (!strcmp (query_prop, "email")) { - e_list_append (bl->priv->supported_fields, "email_2"); - e_list_append (bl->priv->supported_fields, "email_3"); - } - else if (!strcmp (query_prop, "business_phone")) { - e_list_append (bl->priv->supported_fields, "business_phone_2"); - } - else if (!strcmp (query_prop, "home_phone")) { - e_list_append (bl->priv->supported_fields, "home_phone_2"); - } - } - } -} - -static void -add_oc_attributes_to_supported_fields (PASBackendLDAP *bl, LDAPObjectClass *oc) -{ - int i; - GHashTable *attr_hash = g_hash_table_new (g_str_hash, g_str_equal); - - for (i = 0; i < num_prop_infos; i ++) - g_hash_table_insert (attr_hash, prop_info[i].ldap_attr, prop_info[i].query_prop); - - if (oc->oc_at_oids_must) - add_to_supported_fields (bl, oc->oc_at_oids_must, attr_hash); - - if (oc->oc_at_oids_may) - add_to_supported_fields (bl, oc->oc_at_oids_may, attr_hash); - - g_hash_table_destroy (attr_hash); -} - -static void -check_schema_support (PASBackendLDAP *bl) -{ - char *attrs[2]; - LDAPMessage *resp; - LDAP *ldap = bl->priv->ldap; - struct timeval timeout; - - if (!bl->priv->schema_dn) - return; - - bl->priv->evolutionPersonChecked = TRUE; - - attrs[0] = "objectClasses"; - attrs[1] = NULL; - - timeout.tv_sec = 30; - timeout.tv_usec = 0; - - if (ldap_search_ext_s (ldap, bl->priv->schema_dn, LDAP_SCOPE_BASE, - "(objectClass=subschema)", attrs, 0, - NULL, NULL, &timeout, LDAP_NO_LIMIT, &resp) == LDAP_SUCCESS) { - char **values; - - values = ldap_get_values (ldap, resp, "objectClasses"); - - if (values) { - int i; - for (i = 0; values[i]; i ++) { - int j; - int code; - const char *err; - LDAPObjectClass *oc = ldap_str2objectclass (values[i], &code, &err, 0); - - if (!oc) - continue; - - for (j = 0; oc->oc_names[j]; j++) - if (!g_ascii_strcasecmp (oc->oc_names[j], EVOLUTIONPERSON)) { - g_print ("support found on ldap server for objectclass evolutionPerson\n"); - bl->priv->evolutionPersonSupported = TRUE; - - add_oc_attributes_to_supported_fields (bl, oc); - } - else if (!g_ascii_strcasecmp (oc->oc_names[j], CALENTRY)) { - g_print ("support found on ldap server for objectclass calEntry\n"); - bl->priv->calEntrySupported = TRUE; - add_oc_attributes_to_supported_fields (bl, oc); - } - else if (!g_ascii_strcasecmp (oc->oc_names[j], INETORGPERSON) - || !g_ascii_strcasecmp (oc->oc_names[j], ORGANIZATIONALPERSON) - || !g_ascii_strcasecmp (oc->oc_names[j], PERSON)) { - add_oc_attributes_to_supported_fields (bl, oc); - } - - ldap_objectclass_free (oc); - } - - ldap_value_free (values); - } - else { - /* the reason for this is so that if the user - ends up authenticating to the ldap server, - we will requery for the subschema values. - This makes it a bit more robust in the face - of draconian acl's that keep subschema - reads from working until the user is - authed. */ - if (!pas_backend_is_writable (PAS_BACKEND (bl))) { - g_warning ("subschema read returned nothing before successful auth"); - bl->priv->evolutionPersonChecked = FALSE; - } - else { - g_warning ("subschema read returned nothing after successful auth"); - } - } - - ldap_msgfree (resp); - } -} - -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:"); - /* yuck. we have to free these? */ - for (i = 0; info.ldapai_extensions[i]; i++) { - char *extension = info.ldapai_extensions[i]; - g_message (extension); - ldap_memfree (extension); - } - ldap_memfree (info.ldapai_extensions); - ldap_memfree (info.ldapai_vendor_name); - } - - ldap_unbind_ext_s (ldap, NULL, NULL); -} - -static int -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 = 30; - timeout.tv_usec = 0; - - ldap_error = ldap_search_ext_s (ldap, - LDAP_ROOT_DSE, LDAP_SCOPE_BASE, - "(objectclass=*)", - attrs, 0, NULL, NULL, &timeout, LDAP_NO_LIMIT, &resp); - if (ldap_error != LDAP_SUCCESS) { - g_warning ("could not perform query on Root DSE (ldap_error 0x%02x)", ldap_error); - return ldap_error; - } - - 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) { - char *auth_method; - if (bl->priv->supported_auth_methods) - g_object_unref (bl->priv->supported_auth_methods); - bl->priv->supported_auth_methods = e_list_new ((EListCopyFunc)g_strdup, (EListFreeFunc)g_free, NULL); - - auth_method = g_strdup_printf ("ldap/simple-binddn|%s", _("Using Distinguished Name (DN)")); - e_list_append (bl->priv->supported_auth_methods, auth_method); - g_free (auth_method); - - auth_method = g_strdup_printf ("ldap/simple-email|%s", _("Using Email Address")); - e_list_append (bl->priv->supported_auth_methods, auth_method); - g_free (auth_method); - - for (i = 0; values[i]; i++) { - auth_method = g_strdup_printf ("sasl/%s|%s", values[i], values[i]); - e_list_append (bl->priv->supported_auth_methods, auth_method); - g_free (auth_method); - g_message ("supported SASL mechanism: %s", values[i]); - } - ldap_value_free (values); - } - - - values = ldap_get_values (ldap, resp, "subschemaSubentry"); - if (!values || !values[0]) { - if (values) ldap_value_free (values); - values = ldap_get_values (ldap, resp, "schemaNamingContext"); - } - if (values && values[0]) { - g_free (bl->priv->schema_dn); - 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); - - ldap_msgfree (resp); - - return LDAP_SUCCESS; -} - -static GNOME_Evolution_Addressbook_BookListener_CallStatus -pas_backend_ldap_connect (PASBackendLDAP *bl) -{ - PASBackendLDAPPrivate *blpriv = bl->priv; - - /* close connection first if it's open first */ - if (blpriv->ldap) - ldap_unbind_ext (blpriv->ldap, NULL, NULL); - - blpriv->ldap = ldap_init (blpriv->ldap_host, blpriv->ldap_port); -#if defined (DEBUG) && defined (LDAP_OPT_DEBUG_LEVEL) - { - int debug_level = 4; - ldap_set_option (blpriv->ldap, LDAP_OPT_DEBUG_LEVEL, &debug_level); - } -#endif - - if (NULL != blpriv->ldap) { - int ldap_error; - - if (bl->priv->use_tls != PAS_BACKEND_LDAP_TLS_NO) { - 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; - } - else - bl->priv->ldap_v3 = TRUE; - - if (!bl->priv->ldap_v3 && bl->priv->use_tls == PAS_BACKEND_LDAP_TLS_ALWAYS) { - g_message ("TLS not available (fatal version), v3 protocol could not be established (ldap_error 0x%02x)", ldap_error); - ldap_unbind (blpriv->ldap); - blpriv->ldap = NULL; - return GNOME_Evolution_Addressbook_BookListener_TLSNotAvailable; - } - - if (bl->priv->ldap_port == LDAPS_PORT && bl->priv->use_tls == PAS_BACKEND_LDAP_TLS_ALWAYS) { - int tls_level = LDAP_OPT_X_TLS_HARD; - ldap_set_option (blpriv->ldap, LDAP_OPT_X_TLS, &tls_level); - } - else if (bl->priv->use_tls) { - ldap_error = ldap_start_tls_s (blpriv->ldap, NULL, NULL); - if (LDAP_SUCCESS != ldap_error) { - if (bl->priv->use_tls == PAS_BACKEND_LDAP_TLS_ALWAYS) { - g_message ("TLS not available (fatal version), (ldap_error 0x%02x)", ldap_error); - ldap_unbind (blpriv->ldap); - blpriv->ldap = NULL; - return GNOME_Evolution_Addressbook_BookListener_TLSNotAvailable; - } - else { - g_message ("TLS not available (ldap_error 0x%02x)", ldap_error); - } - } - else - g_message ("TLS active"); - } - } - - /* bind anonymously initially, we'll actually - authenticate the user properly later (in - authenticate_user) if they've selected - authentication */ - ldap_error = ldap_simple_bind_s (blpriv->ldap, NULL, NULL); - if (ldap_error == LDAP_SERVER_DOWN) { - /* we only want this to be fatal if the server is down. */ - g_warning ("failed to bind anonymously while connecting (ldap_error 0x%02x)", ldap_error); - return GNOME_Evolution_Addressbook_BookListener_RepositoryOffline; - } - - ldap_error = query_ldap_root_dse (bl); - /* query_ldap_root_dse will cause the actual - connect(), so any tcpip problems will show up - here */ - - /* we can't just check for LDAP_SUCCESS here since in - older servers (namely openldap1.x servers), there's - not a root DSE at all, so the query will fail with - LDAP_NO_SUCH_OBJECT. */ - if (ldap_error == LDAP_SUCCESS || LDAP_NAME_ERROR (ldap_error)) { - blpriv->connected = TRUE; - - /* check to see if evolutionPerson is supported, if we can (me - might not be able to if we can't authenticate. if we - can't, try again in auth_user.) */ - if (!bl->priv->evolutionPersonChecked) - check_schema_support (bl); - - pas_backend_set_is_loaded (PAS_BACKEND (bl), TRUE); - return GNOME_Evolution_Addressbook_BookListener_Success; - } - else - g_warning ("Failed to perform root dse query anonymously, (ldap_error 0x%02x)", ldap_error); - } - - 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; - return GNOME_Evolution_Addressbook_BookListener_RepositoryOffline; -} - -static gboolean -pas_backend_ldap_reconnect (PASBackendLDAP *bl, PASBookView *book_view, int ldap_status) -{ - /* we need to reconnect if we were previously connected */ - if (bl->priv->connected && ldap_status == LDAP_SERVER_DOWN) { - GNOME_Evolution_Addressbook_BookListener_CallStatus status; - int ldap_error = LDAP_SUCCESS; - - book_view_notify_status (book_view, _("Reconnecting to LDAP server...")); - - status = pas_backend_ldap_connect (bl); - - if (status != GNOME_Evolution_Addressbook_BookListener_Success) { - book_view_notify_status (book_view, ""); - return FALSE; - } - - if (bl->priv->auth_dn) - ldap_error = ldap_simple_bind_s(bl->priv->ldap, - bl->priv->auth_dn, - bl->priv->auth_passwd); - book_view_notify_status (book_view, ""); - return (ldap_error == LDAP_SUCCESS); - } - else { - return FALSE; - } -} - -static void -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; - - if (g_hash_table_lookup (bl->priv->id_to_op, &op->id)) { - g_warning ("conflicting ldap msgid's"); - } - - g_hash_table_insert (bl->priv->id_to_op, - &op->id, op); - - bl->priv->active_ops ++; - - if (bl->priv->poll_timeout == -1) - bl->priv->poll_timeout = g_timeout_add (LDAP_POLL_INTERVAL, - (GSourceFunc) poll_ldap, - bl); -} - -static void -ldap_op_finished (LDAPOp *op) -{ - PASBackend *backend = op->backend; - PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); - - g_hash_table_remove (bl->priv->id_to_op, &op->id); - - /* should handle errors here */ - ldap_abandon (bl->priv->ldap, op->id); - - op->dtor (op); - - 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_change_id (LDAPOp *op, int msg_id) -{ - PASBackend *backend = op->backend; - PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); - - g_hash_table_remove (bl->priv->id_to_op, &op->id); - - op->id = msg_id; - - g_hash_table_insert (bl->priv->id_to_op, - &op->id, op); -} - -static int -ldap_error_to_response (int ldap_error) -{ - if (ldap_error == LDAP_SUCCESS) - return GNOME_Evolution_Addressbook_BookListener_Success; - else if (LDAP_NAME_ERROR (ldap_error)) - return GNOME_Evolution_Addressbook_BookListener_CardNotFound; - else if (ldap_error == LDAP_INSUFFICIENT_ACCESS) - return GNOME_Evolution_Addressbook_BookListener_PermissionDenied; - else if (ldap_error == LDAP_SERVER_DOWN) - return GNOME_Evolution_Addressbook_BookListener_RepositoryOffline; - else if (ldap_error == LDAP_ALREADY_EXISTS) - return GNOME_Evolution_Addressbook_BookListener_CardIdAlreadyExists; - else - return GNOME_Evolution_Addressbook_BookListener_OtherError; -} - - -static char * -create_dn_from_ecard (ECardSimple *card, const char *root_dn) -{ - char *cn, *cn_part = NULL; - char *dn; - - cn = e_card_simple_get (card, E_CARD_SIMPLE_FIELD_FULL_NAME); - if (cn) { - if (strchr (cn, ',')) { - /* need to escape commas */ - char *new_cn = g_malloc0 (strlen (cn) * 3 + 1); - int i, j; - - for (i = 0, j = 0; i < strlen (cn); i ++) { - if (cn[i] == ',') { - sprintf (new_cn + j, "%%%02X", cn[i]); - j += 3; - } - else { - new_cn[j++] = cn[i]; - } - } - cn_part = g_strdup_printf ("cn=%s", new_cn); - g_free (new_cn); - } - else { - cn_part = g_strdup_printf ("cn=%s", cn); - } - } - else { - cn_part = g_strdup (""); - } - - dn = g_strdup_printf ("%s%s%s", cn_part, - (root_dn && strlen(root_dn)) ? "," : "", - (root_dn && strlen(root_dn)) ? root_dn: ""); - - g_free (cn_part); - - g_print ("generated dn: %s\n", dn); - - return dn; -} - -static void -free_mods (GPtrArray *mods) -{ - int i = 0; - LDAPMod *mod; - - while ((mod = g_ptr_array_index (mods, i++))) { - int j; - g_free (mod->mod_type); - - if (mod->mod_op & LDAP_MOD_BVALUES) { - for (j = 0; mod->mod_bvalues[j]; j++) { - g_free (mod->mod_bvalues[j]->bv_val); - g_free (mod->mod_bvalues[j]); - } - } - else { - for (j = 0; mod->mod_values[j]; j++) - g_free (mod->mod_values[j]); - } - g_free (mod); - } - - g_ptr_array_free (mods, TRUE); -} - -static GPtrArray* -build_mods_from_ecards (PASBackendLDAP *bl, ECardSimple *current, ECardSimple *new, gboolean *new_dn_needed) -{ - gboolean adding = (current == NULL); - GPtrArray *result = g_ptr_array_new(); - int i; - - if (new_dn_needed) - *new_dn_needed = FALSE; - - /* we walk down the list of properties we can deal with (that - big table at the top of the file) */ - - for (i = 0; i < num_prop_infos; i ++) { - gboolean include; - gboolean new_prop_present = FALSE; - gboolean current_prop_present = FALSE; - struct berval** new_prop_bers = NULL; - char *new_prop = NULL; - char *current_prop = NULL; - - /* XXX if it's an evolutionPerson prop and the ldap - server doesn't support that objectclass, skip it. */ - if (prop_info[i].prop_type & PROP_EVOLVE && !bl->priv->evolutionPersonSupported) - continue; - - /* get the value for the new card, and compare it to - the value in the current card to see if we should - update it -- if adding is TRUE, short circuit the - check. */ - if (prop_info[i].prop_type & PROP_TYPE_STRING) { - new_prop = e_card_simple_get (new, prop_info[i].field_id); - new_prop_present = (new_prop != NULL); - } - else { - new_prop_bers = prop_info[i].ber_func (new); - new_prop_present = (new_prop_bers != NULL); - } - - /* need to set INCLUDE to true if the field needs to - show up in the ldap modify request */ - if (adding) { - /* if we're creating a new card, include it if the - field is there at all */ - if (prop_info[i].prop_type & PROP_TYPE_STRING) - include = (new_prop_present && *new_prop); /* empty strings cause problems */ - else - include = new_prop_present; - } - else { - /* if we're modifying an existing card, - include it if the current field value is - different than the new one, if it didn't - exist previously, or if it's been - removed. */ - if (prop_info[i].prop_type & PROP_TYPE_STRING) { - current_prop = e_card_simple_get (current, prop_info[i].field_id); - current_prop_present = (current_prop != NULL); - - if (new_prop && current_prop) - include = *new_prop && strcmp (new_prop, current_prop); - else - include = (!!new_prop != !!current_prop); - } - else { - int j; - struct berval **current_prop_bers = prop_info[i].ber_func (current); - - current_prop_present = (current_prop_bers != NULL); - - /* free up the current_prop_bers */ - if (current_prop_bers) { - for (j = 0; current_prop_bers[j]; j++) { - g_free (current_prop_bers[j]->bv_val); - g_free (current_prop_bers[j]); - } - g_free (current_prop_bers); - } - - include = !prop_info[i].compare_func (new, current); - } - } - - if (include) { - LDAPMod *mod = g_new (LDAPMod, 1); - - /* the included attribute has changed - we - need to update the dn if it's one of the - attributes we compute the dn from. */ - if (new_dn_needed) - *new_dn_needed |= prop_info[i].prop_type & PROP_DN; - - if (adding) { - mod->mod_op = LDAP_MOD_ADD; - } - else { - if (!new_prop_present) - mod->mod_op = LDAP_MOD_DELETE; - else if (!current_prop_present) - mod->mod_op = LDAP_MOD_ADD; - else - mod->mod_op = LDAP_MOD_REPLACE; - } - - mod->mod_type = g_strdup (prop_info[i].ldap_attr); - - if (prop_info[i].prop_type & PROP_TYPE_STRING) { - mod->mod_values = g_new (char*, 2); - mod->mod_values[0] = new_prop; - mod->mod_values[1] = NULL; - } - else { /* PROP_TYPE_COMPLEX */ - mod->mod_op |= LDAP_MOD_BVALUES; - mod->mod_bvalues = new_prop_bers; - } - - g_ptr_array_add (result, mod); - } - - } - - /* NULL terminate the list of modifications */ - g_ptr_array_add (result, NULL); - - return result; -} - -static void -add_objectclass_mod (PASBackendLDAP *bl, GPtrArray *mod_array, GList *existing_objectclasses) -{ -#define FIND_INSERT(oc) \ - if (!g_list_find_custom (existing_objectclasses, (oc), (GCompareFunc)g_ascii_strcasecmp)) \ - g_ptr_array_add (objectclasses, g_strdup ((oc))) -#define INSERT(oc) \ - g_ptr_array_add (objectclasses, g_strdup ((oc))) - - LDAPMod *objectclass_mod; - GPtrArray *objectclasses = g_ptr_array_new(); - - if (existing_objectclasses) { - objectclass_mod = g_new (LDAPMod, 1); - objectclass_mod->mod_op = LDAP_MOD_ADD; - objectclass_mod->mod_type = g_strdup ("objectClass"); - - /* yes, this is a linear search for each of our - objectclasses, but really, how many objectclasses - are there going to be in any sane ldap entry? */ - FIND_INSERT (TOP); - FIND_INSERT (PERSON); - FIND_INSERT (ORGANIZATIONALPERSON); - FIND_INSERT (INETORGPERSON); - if (bl->priv->calEntrySupported) - FIND_INSERT (CALENTRY); - if (bl->priv->evolutionPersonSupported) - FIND_INSERT (EVOLUTIONPERSON); - - if (objectclasses->len) { - g_ptr_array_add (objectclasses, NULL); - objectclass_mod->mod_values = (char**)objectclasses->pdata; - g_ptr_array_add (mod_array, objectclass_mod); - g_ptr_array_free (objectclasses, FALSE); - } - else { - g_ptr_array_free (objectclasses, TRUE); - g_free (objectclass_mod->mod_type); - g_free (objectclass_mod); - } - - } - else { - objectclass_mod = g_new (LDAPMod, 1); - objectclass_mod->mod_op = LDAP_MOD_ADD; - objectclass_mod->mod_type = g_strdup ("objectClass"); - - INSERT(TOP); - INSERT(PERSON); - INSERT(ORGANIZATIONALPERSON); - INSERT(INETORGPERSON); - if (bl->priv->calEntrySupported) - INSERT(CALENTRY); - if (bl->priv->evolutionPersonSupported) - INSERT(EVOLUTIONPERSON); - g_ptr_array_add (objectclasses, NULL); - objectclass_mod->mod_values = (char**)objectclasses->pdata; - g_ptr_array_add (mod_array, objectclass_mod); - g_ptr_array_free (objectclasses, FALSE); - } -} - -typedef struct { - LDAPOp op; - char *dn; - ECardSimple *new_card; -} LDAPCreateOp; - -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 */ - EIterator *iter; - - iter = e_list_get_iterator (bl->priv->book_views); - while (e_iterator_is_valid (iter)) { - CORBA_Environment ev; - gboolean match; - PASBackendLDAPBookView *view = (PASBackendLDAPBookView*)e_iterator_get (iter); - char *new_vcard; - - CORBA_exception_init(&ev); - - bonobo_object_dup_ref(bonobo_object_corba_objref(BONOBO_OBJECT(view->book_view)), &ev); - - new_vcard = e_card_simple_get_vcard_assume_utf8 (create_op->new_card); - - match = pas_backend_card_sexp_match_vcard (view->card_sexp, - new_vcard); - if (match) { - pas_book_view_notify_add_1 (view->book_view, - new_vcard); - } - pas_book_view_notify_complete (view->book_view, GNOME_Evolution_Addressbook_BookViewListener_Success); - - g_free (new_vcard); - - bonobo_object_release_unref(bonobo_object_corba_objref(BONOBO_OBJECT(view->book_view)), &ev); - - e_iterator_next (iter); - } - g_object_unref (iter); - } - else { - ldap_perror (ldap, "create_card"); - } - - if (op->view) - pas_book_view_notify_complete (op->view, GNOME_Evolution_Addressbook_BookViewListener_Success); - - /* 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); - g_object_unref (create_op->new_card); - g_free (create_op); -} - -static void -pas_backend_ldap_process_create_card (PASBackend *backend, - PASBook *book, - PASCreateCardRequest *req) -{ - LDAPCreateOp *create_op = g_new (LDAPCreateOp, 1); - PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); - PASBookView *book_view; - int create_card_msgid; - ECard *new_ecard; - int response; - int err; - GPtrArray *mod_array; - LDAPMod **ldap_mods; - LDAP *ldap; - - book_view = find_book_view (bl); - - printf ("vcard = %s\n", req->vcard); - - new_ecard = e_card_new (req->vcard); - create_op->new_card = e_card_simple_new (new_ecard); - - 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, create_op->new_card, NULL); - -#if 0 - if (!mod_array) { - /* there's an illegal field in there. report - UnsupportedAttribute back */ - pas_book_respond_create (book, - GNOME_Evolution_Addressbook_BookListener_UnsupportedField, - create_op->dn); - - g_free (create_op->dn); - g_object_unref (create_op->new_card); - g_free (create_op); - return; - } -#endif - - /* remove the NULL at the end */ - g_ptr_array_remove (mod_array, NULL); - - /* add our objectclass(es) */ - add_objectclass_mod (bl, mod_array, NULL); - - /* then put the NULL back */ - g_ptr_array_add (mod_array, NULL); - -#ifdef LDAP_DEBUG_ADD - { - int i; - printf ("Sending the following to the server as ADD\n"); - - for (i = 0; g_ptr_array_index(mod_array, i); i ++) { - LDAPMod *mod = g_ptr_array_index(mod_array, i); - if (mod->mod_op & LDAP_MOD_DELETE) - printf ("del "); - else if (mod->mod_op & LDAP_MOD_REPLACE) - printf ("rep "); - else - printf ("add "); - if (mod->mod_op & LDAP_MOD_BVALUES) - printf ("ber "); - else - printf (" "); - - printf (" %s:\n", mod->mod_type); - - if (mod->mod_op & LDAP_MOD_BVALUES) { - int j; - for (j = 0; mod->mod_bvalues[j] && mod->mod_bvalues[j]->bv_val; j++) - printf ("\t\t'%s'\n", mod->mod_bvalues[j]->bv_val); - } - else { - int j; - - for (j = 0; mod->mod_values[j]; j++) - printf ("\t\t'%s'\n", mod->mod_values[j]); - } - } - } -#endif - - ldap_mods = (LDAPMod**)mod_array->pdata; - - do { - book_view_notify_status (book_view, _("Adding card to LDAP server...")); - - err = ldap_add_ext (ldap, create_op->dn, ldap_mods, - NULL, NULL, &create_card_msgid); - - } while (pas_backend_ldap_reconnect (bl, book_view, err)); - - /* 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; - } - - ldap_parse_result (bl->priv->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 */ - EIterator *iter = e_list_get_iterator (bl->priv->book_views); - - while (e_iterator_is_valid (iter)) { - CORBA_Environment ev; - PASBackendLDAPBookView *view = (PASBackendLDAPBookView*)e_iterator_get (iter); - - CORBA_exception_init(&ev); - - bonobo_object_dup_ref(bonobo_object_corba_objref(BONOBO_OBJECT(view->book_view)), &ev); - - pas_book_view_notify_remove_1 (view->book_view, remove_op->id); - - bonobo_object_release_unref(bonobo_object_corba_objref(BONOBO_OBJECT(view->book_view)), &ev); - - e_iterator_next (iter); - } - g_object_unref (iter); - } - else { - 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, GNOME_Evolution_Addressbook_BookViewListener_Success); -} - -static void -remove_card_dtor (LDAPOp *op) -{ - LDAPRemoveOp *remove_op = (LDAPRemoveOp*)op; - - g_free (remove_op->id); - g_free (remove_op); -} - -static void -pas_backend_ldap_process_remove_cards (PASBackend *backend, - PASBook *book, - PASRemoveCardsRequest *req) -{ - LDAPRemoveOp *remove_op = g_new (LDAPRemoveOp, 1); - PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); - PASBookView *book_view; - int remove_msgid; - int ldap_error; - - book_view = find_book_view (bl); - - /* - ** since we didn't pass "bulk-removes" in our static - ** capabilities, we should only get 1 length lists here, so - ** the id we're deleting is the first and only id in the list. - */ - remove_op->id = g_strdup (req->ids->data); - - do { - book_view_notify_status (book_view, _("Removing card from LDAP server...")); - - ldap_error = ldap_delete_ext (bl->priv->ldap, - remove_op->id, - NULL, NULL, &remove_msgid); - } while (pas_backend_ldap_reconnect (bl, book_view, ldap_error)); - - 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; - 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 void -modify_card_modify_handler (LDAPOp *op, LDAPMessage *res) -{ - LDAPModifyOp *modify_op = (LDAPModifyOp*)op; - PASBackendLDAP *bl = PAS_BACKEND_LDAP (op->backend); - LDAP *ldap = bl->priv->ldap; - int ldap_error; - - 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; - } - - ldap_parse_result (ldap, res, &ldap_error, - NULL, NULL, NULL, NULL, 0); - - if (ldap_error == LDAP_SUCCESS) { - /* the card was modified, let's let the views know about it */ - EIterator *iter = e_list_get_iterator (bl->priv->book_views); - while (e_iterator_is_valid (iter)) { - CORBA_Environment ev; - gboolean old_match, new_match; - PASBackendLDAPBookView *view = (PASBackendLDAPBookView*)e_iterator_get (iter); - - 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, - 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_1 (view->book_view, e_card_simple_get_id (modify_op->card)); - pas_book_view_notify_complete (view->book_view, GNOME_Evolution_Addressbook_BookViewListener_Success); - - bonobo_object_release_unref(bonobo_object_corba_objref(BONOBO_OBJECT(view->book_view)), &ev); - - e_iterator_next (iter); - } - g_object_unref (iter); - } - 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; - } - - 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 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; - - /* 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 (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; - } - - /* 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 -modify_card_dtor (LDAPOp *op) -{ - LDAPModifyOp *modify_op = (LDAPModifyOp*)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) - g_object_unref (modify_op->current_card); - g_free (modify_op->vcard); - if (modify_op->card) - g_object_unref (modify_op->card); - g_free (modify_op); -} - -static void -pas_backend_ldap_process_modify_card (PASBackend *backend, - PASBook *book, - PASModifyCardRequest *req) -{ - 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; - - book_view = find_book_view (bl); - - modify_op->vcard = g_strdup (req->vcard); - new_ecard = e_card_new (modify_op->vcard); - modify_op->card = e_card_simple_new (new_ecard); - g_object_unref (new_ecard); - modify_op->id = e_card_simple_get_id(modify_op->card); - - ldap = bl->priv->ldap; - - book_view_notify_status (book_view, _("Modifying card from LDAP server...")); - - do { - book_view_notify_status (book_view, _("Modifying card from LDAP server...")); - - ldap_error = ldap_search_ext (ldap, modify_op->id, - LDAP_SCOPE_BASE, - "(objectclass=*)", - NULL, 0, NULL, NULL, - NULL, /* XXX timeout */ - 1, &modify_card_msgid); - - } while (pas_backend_ldap_reconnect (bl, book_view, ldap_error)); - - 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; -} LDAPGetVCardOp; - -static void -get_vcard_handler (LDAPOp *op, LDAPMessage *res) -{ - 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; - } - - 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); - g_object_unref (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_warning ("unhandled result type %d returned", msg_type); - pas_book_respond_get_vcard (op->book, GNOME_Evolution_Addressbook_BookListener_OtherError, - ""); - ldap_op_finished (op); - } - -} - -static void -get_vcard_dtor (LDAPOp *op) -{ - LDAPGetVCardOp *get_vcard_op = (LDAPGetVCardOp*)op; - - g_free (get_vcard_op); -} - -static void -pas_backend_ldap_process_get_vcard (PASBackend *backend, - PASBook *book, - PASGetVCardRequest *req) -{ - 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; - int ldap_error; - - book_view = find_book_view (bl); - - do { - ldap_error = ldap_search_ext (ldap, req->id, - LDAP_SCOPE_BASE, - "(objectclass=*)", - NULL, 0, NULL, NULL, - NULL, /* XXX timeout */ - 1, &get_vcard_msgid); - } while (pas_backend_ldap_reconnect (bl, book_view, ldap_error)); - - 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, - ldap_error_to_response (ldap_error), - ""); - get_vcard_dtor ((LDAPOp*)get_vcard_op); - } -} - - -typedef struct { - LDAPOp op; - PASBackendLDAPCursorPrivate *cursor_data; - gboolean responded; /* if FALSE, we need to free cursor_data in the dtor */ -} 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(gpointer data, GObject *where_object_was) -{ - PASBackendLDAPCursorPrivate *cursor_data = (PASBackendLDAPCursorPrivate *) data; - - if (cursor_data->book) { - CORBA_Environment ev; - GNOME_Evolution_Addressbook_Book corba_book; - - corba_book = bonobo_object_corba_objref(BONOBO_OBJECT(cursor_data->book)); - - 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"); - } - - 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 -get_cursor_handler (LDAPOp *op, LDAPMessage *res) -{ - LDAPGetCursorOp *cursor_op = (LDAPGetCursorOp*)op; - PASBackendLDAP *bl = PAS_BACKEND_LDAP (op->backend); - LDAP *ldap = bl->priv->ldap; - int msg_type; - - msg_type = ldap_msgtype (res); - if (msg_type == LDAP_RES_SEARCH_ENTRY) { - LDAPMessage *e; - - 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); - g_object_unref (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); - - if (ldap_error == LDAP_SUCCESS) { - cursor = pas_card_cursor_new(get_length, - get_nth, - cursor_op->cursor_data); - - g_object_weak_ref (G_OBJECT (cursor), cursor_destroy, cursor_op->cursor_data); - - cursor_op->responded = TRUE; - } - - pas_book_respond_get_cursor (cursor_op->cursor_data->book, - ldap_error_to_response (ldap_error), - cursor); - - 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 void -get_cursor_dtor (LDAPOp *op) -{ - LDAPGetCursorOp *cursor_op = (LDAPGetCursorOp*)op; - - if (!cursor_op->responded) { - cursor_destroy (cursor_op->cursor_data, NULL); - } - - g_free (op); -} - -static void -pas_backend_ldap_process_get_cursor (PASBackend *backend, - PASBook *book, - PASGetCursorRequest *req) -{ - PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); - LDAP *ldap = bl->priv->ldap; - int ldap_error; - int get_cursor_msgid; - LDAPGetCursorOp *cursor_op; - PASBookView *book_view; - - book_view = find_book_view (bl); - - cursor_op = g_new0 (LDAPGetCursorOp, 1); - cursor_op->cursor_data = g_new0 (PASBackendLDAPCursorPrivate, 1); - - do { - ldap_error = ldap_search_ext (ldap, - bl->priv->ldap_rootdn, - bl->priv->ldap_scope, - "(objectclass=*)", - NULL, 0, - NULL, NULL, NULL, /* timeout */ - 0, &get_cursor_msgid); - } while (pas_backend_ldap_reconnect (bl, book_view, ldap_error)); - - if (ldap_error == LDAP_SUCCESS) { - CORBA_Environment ev; - GNOME_Evolution_Addressbook_Book corba_book; - - corba_book = bonobo_object_corba_objref(BONOBO_OBJECT(book)); - - CORBA_exception_init(&ev); - - 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); - } -} - - -/* List property functions */ -static void -email_populate(ECardSimple *card, char **values) -{ - int i; - - for (i = 0; values[i] && i < 3; i ++) { - e_card_simple_set_email (card, i, values[i]); - } -} - -struct berval** -email_ber(ECardSimple *card) -{ - struct berval** result; - const char *emails[3]; - int i, j, num; - - num = 0; - for (i = 0; i < 3; i ++) { - emails[i] = e_card_simple_get_email (card, E_CARD_SIMPLE_EMAIL_ID_EMAIL + i); - if (emails[i]) - num++; - } - - if (num == 0) - return NULL; - - result = g_new (struct berval*, num + 1); - - for (i = 0; i < num; i ++) - result[i] = g_new (struct berval, 1); - - j = 0; - for (i = 0; i < 3; i ++) { - if (emails[i]) { - result[j]->bv_val = g_strdup (emails[i]); - result[j++]->bv_len = strlen (emails[i]); - } - } - - result[num] = NULL; - - return result; -} - -static gboolean -email_compare (ECardSimple *ecard1, ECardSimple *ecard2) -{ - const char *email1, *email2; - int i; - - for (i = 0; i < 3; i ++) { - gboolean equal; - email1 = e_card_simple_get_email (ecard1, E_CARD_SIMPLE_EMAIL_ID_EMAIL + i); - email2 = e_card_simple_get_email (ecard2, E_CARD_SIMPLE_EMAIL_ID_EMAIL + i); - - if (email1 && email2) - equal = !strcmp (email1, email2); - else - equal = (!!email1 == !!email2); - - if (!equal) - return equal; - } - - return TRUE; -} - -static void -homephone_populate(ECardSimple *card, char **values) -{ - if (values[0]) { - e_card_simple_set (card, E_CARD_SIMPLE_FIELD_PHONE_HOME, values[0]); - if (values[1]) - e_card_simple_set (card, E_CARD_SIMPLE_FIELD_PHONE_HOME_2, values[1]); - } -} - -struct berval** -homephone_ber(ECardSimple *card) -{ - struct berval** result; - const char *homephones[3]; - int i, j, num; - - num = 0; - if ((homephones[0] = e_card_simple_get (card, E_CARD_SIMPLE_FIELD_PHONE_HOME))) - num++; - if ((homephones[1] = e_card_simple_get (card, E_CARD_SIMPLE_FIELD_PHONE_HOME_2))) - num++; - - if (num == 0) - return NULL; - - result = g_new (struct berval*, num + 1); - - for (i = 0; i < num; i ++) - result[i] = g_new (struct berval, 1); - - j = 0; - for (i = 0; i < 2; i ++) { - if (homephones[i]) { - result[j]->bv_val = g_strdup (homephones[i]); - result[j++]->bv_len = strlen (homephones[i]); - } - } - - result[num] = NULL; - - return result; -} - -static gboolean -homephone_compare (ECardSimple *ecard1, ECardSimple *ecard2) -{ - int phone_ids[2] = { E_CARD_SIMPLE_FIELD_PHONE_HOME, E_CARD_SIMPLE_FIELD_PHONE_HOME_2 }; - const char *phone1, *phone2; - int i; - - for (i = 0; i < 2; i ++) { - gboolean equal; - phone1 = e_card_simple_get (ecard1, phone_ids[i]); - phone2 = e_card_simple_get (ecard2, phone_ids[i]); - - if (phone1 && phone2) - equal = !strcmp (phone1, phone2); - else - equal = (!!phone1 == !!phone2); - - if (!equal) - return equal; - } - - return TRUE; -} - -static void -business_populate(ECardSimple *card, char **values) -{ - if (values[0]) { - e_card_simple_set (card, E_CARD_SIMPLE_FIELD_PHONE_BUSINESS, values[0]); - if (values[1]) - e_card_simple_set (card, E_CARD_SIMPLE_FIELD_PHONE_BUSINESS_2, values[1]); - } -} - -struct berval** -business_ber(ECardSimple *card) -{ - struct berval** result; - const char *business_phones[3]; - int i, j, num; - - num = 0; - if ((business_phones[0] = e_card_simple_get (card, E_CARD_SIMPLE_FIELD_PHONE_BUSINESS))) - num++; - if ((business_phones[1] = e_card_simple_get (card, E_CARD_SIMPLE_FIELD_PHONE_BUSINESS_2))) - num++; - - if (num == 0) - return NULL; - - result = g_new (struct berval*, num + 1); - - for (i = 0; i < num; i ++) - result[i] = g_new (struct berval, 1); - - j = 0; - for (i = 0; i < 2; i ++) { - if (business_phones[i]) { - result[j]->bv_val = g_strdup (business_phones[i]); - result[j++]->bv_len = strlen (business_phones[i]); - } - } - - result[num] = NULL; - - return result; -} - -static gboolean -business_compare (ECardSimple *ecard1, ECardSimple *ecard2) -{ - int phone_ids[2] = { E_CARD_SIMPLE_FIELD_PHONE_BUSINESS, E_CARD_SIMPLE_FIELD_PHONE_BUSINESS_2 }; - const char *phone1, *phone2; - int i; - - for (i = 0; i < 2; i ++) { - gboolean equal; - phone1 = e_card_simple_get (ecard1, phone_ids[i]); - phone2 = e_card_simple_get (ecard2, phone_ids[i]); - - if (phone1 && phone2) - equal = !strcmp (phone1, phone2); - else - equal = (!!phone1 == !!phone2); - - if (!equal) - return equal; - } - - return TRUE; -} - -static void -anniversary_populate (ECardSimple *card, char **values) -{ - if (values[0]) { - ECardDate dt = e_card_date_from_string (values[0]); - g_object_set (card->card, - "anniversary", &dt, - NULL); - } -} - -struct berval** -anniversary_ber (ECardSimple *card) -{ - ECardDate *dt; - struct berval** result = NULL; - - g_object_get (card->card, - "anniversary", &dt, - NULL); - - if (dt) { - char *anniversary; - - anniversary = e_card_date_to_string (dt); - - result = g_new (struct berval*, 2); - result[0] = g_new (struct berval, 1); - result[0]->bv_val = anniversary; - result[0]->bv_len = strlen (anniversary); - - result[1] = NULL; - } - - return result; -} - -static gboolean -anniversary_compare (ECardSimple *ecard1, ECardSimple *ecard2) -{ - ECardDate *dt; - char *date1 = NULL, *date2 = NULL; - gboolean equal; - - g_object_get (ecard1->card, - "anniversary", &dt, - NULL); - if (dt) - date1 = e_card_date_to_string (dt); - - g_object_get (ecard2->card, - "anniversary", &dt, - NULL); - if (dt) - date2 = e_card_date_to_string (dt); - - if (date1 && date2) - equal = !strcmp (date1, date2); - else - equal = (!!date1 == !!date2); - - g_free (date1); - g_free (date2); - - return equal; -} - -static void -birthday_populate (ECardSimple *card, char **values) -{ - if (values[0]) { - ECardDate dt = e_card_date_from_string (values[0]); - g_object_set (card->card, - "birth_date", &dt, - NULL); - } -} - -struct berval** -birthday_ber (ECardSimple *card) -{ - ECardDate *dt; - struct berval** result = NULL; - - g_object_get (card->card, - "birth_date", &dt, - NULL); - - if (dt) { - char *birthday; - - birthday = e_card_date_to_string (dt); - - result = g_new (struct berval*, 2); - result[0] = g_new (struct berval, 1); - result[0]->bv_val = birthday; - result[0]->bv_len = strlen (birthday); - - result[1] = NULL; - } - - return result; -} - -static gboolean -birthday_compare (ECardSimple *ecard1, ECardSimple *ecard2) -{ - ECardDate *dt; - char *date1 = NULL, *date2 = NULL; - gboolean equal; - - g_object_get (ecard1->card, - "birth_date", &dt, - NULL); - if (dt) - date1 = e_card_date_to_string (dt); - - g_object_get (ecard2->card, - "birth_date", &dt, - NULL); - if (dt) - date2 = e_card_date_to_string (dt); - - if (date1 && date2) - equal = !strcmp (date1, date2); - else - equal = (!!date1 == !!date2); - - g_free (date1); - g_free (date2); - - return equal; -} - -static void -category_populate (ECardSimple *card, char **values) -{ - int i; - ECard *ecard; - EList *categories; - - g_object_get (card, - "card", &ecard, - NULL); - - categories = e_list_new((EListCopyFunc) g_strdup, - (EListFreeFunc) g_free, - NULL); - - for (i = 0; values[i]; i++) - e_list_append (categories, values[i]); - - g_object_set (ecard, - "category_list", categories, - NULL); - - g_object_unref (categories); - - e_card_simple_sync_card (card); - g_object_unref (ecard); -} - -struct berval** -category_ber (ECardSimple *card) -{ - struct berval** result = NULL; - EList *categories; - EIterator *iterator; - ECard *ecard; - int i; - - g_object_get (card, - "card", &ecard, - NULL); - - g_object_get (ecard, - "category_list", &categories, - NULL); - - if (e_list_length (categories) != 0) { - result = g_new0 (struct berval*, e_list_length (categories) + 1); - - for (iterator = e_list_get_iterator(categories), i = 0; e_iterator_is_valid (iterator); - e_iterator_next (iterator), i++) { - const char *category = e_iterator_get (iterator); - - result[i] = g_new (struct berval, 1); - result[i]->bv_val = g_strdup (category); - result[i]->bv_len = strlen (category); - } - - g_object_unref (iterator); - } - - g_object_unref (categories); - g_object_unref (ecard); - return result; -} - -static gboolean -category_compare (ECardSimple *ecard1, ECardSimple *ecard2) -{ - char *categories1, *categories2; - gboolean equal; - - categories1 = e_card_simple_get (ecard1, E_CARD_SIMPLE_FIELD_CATEGORIES); - categories2 = e_card_simple_get (ecard2, E_CARD_SIMPLE_FIELD_CATEGORIES); - - equal = !strcmp (categories1, categories2); - - g_free (categories1); - g_free (categories2); - - return equal; -} - -typedef struct { - GList *list; - PASBackendLDAP *bl; -} PASBackendLDAPSExpData; - -#define IS_RFC2254_CHAR(c) ((c) == '*' || (c) =='\\' || (c) == '(' || (c) == ')' || (c) == '\0') -static char * -rfc2254_escape(char *str) -{ - int i; - int len = strlen(str); - int newlen = 0; - - for (i = 0; i < len; i ++) { - if (IS_RFC2254_CHAR(str[i])) - newlen += 3; - else - newlen ++; - } - - if (len == newlen) { - return g_strdup (str); - } - else { - char *newstr = g_malloc0 (newlen + 1); - int j = 0; - for (i = 0; i < len; i ++) { - if (IS_RFC2254_CHAR(str[i])) { - sprintf (newstr + j, "\\%02x", str[i]); - j+= 3; - } - else { - newstr[j++] = str[i]; - } - } - return newstr; - } -} - -static ESExpResult * -func_and(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data) -{ - PASBackendLDAPSExpData *ldap_data = data; - ESExpResult *r; - char ** strings; - - if (argc > 0) { - int i; - - strings = g_new0(char*, argc+3); - strings[0] = g_strdup ("(&"); - strings[argc+3 - 2] = g_strdup (")"); - - for (i = 0; i < argc; i ++) { - GList *list_head = ldap_data->list; - if (!list_head) - break; - strings[argc - i] = list_head->data; - ldap_data->list = g_list_remove_link(list_head, list_head); - g_list_free_1(list_head); - } - - ldap_data->list = g_list_prepend(ldap_data->list, g_strjoinv(" ", strings)); - - for (i = 0 ; i < argc + 2; i ++) - g_free (strings[i]); - - g_free (strings); - } - - r = e_sexp_result_new(f, ESEXP_RES_BOOL); - r->value.bool = FALSE; - - return r; -} - -static ESExpResult * -func_or(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data) -{ - PASBackendLDAPSExpData *ldap_data = data; - ESExpResult *r; - char ** strings; - - if (argc > 0) { - int i; - - strings = g_new0(char*, argc+3); - strings[0] = g_strdup ("(|"); - strings[argc+3 - 2] = g_strdup (")"); - - for (i = 0; i < argc; i ++) { - GList *list_head = ldap_data->list; - if (!list_head) - break; - strings[argc - i] = list_head->data; - ldap_data->list = g_list_remove_link(list_head, list_head); - g_list_free_1(list_head); - } - - ldap_data->list = g_list_prepend(ldap_data->list, g_strjoinv(" ", strings)); - - for (i = 0 ; i < argc + 2; i ++) - g_free (strings[i]); - - g_free (strings); - } - - r = e_sexp_result_new(f, ESEXP_RES_BOOL); - r->value.bool = FALSE; - - return r; -} - -static ESExpResult * -func_not(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data) -{ - PASBackendLDAPSExpData *ldap_data = data; - ESExpResult *r; - - /* just replace the head of the list with the NOT of it. */ - if (argc > 0) { - char *term = ldap_data->list->data; - ldap_data->list->data = g_strdup_printf("(!%s)", term); - g_free (term); - } - - r = e_sexp_result_new(f, ESEXP_RES_BOOL); - r->value.bool = FALSE; - - return r; -} - -static ESExpResult * -func_contains(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data) -{ - PASBackendLDAPSExpData *ldap_data = data; - ESExpResult *r; - - if (argc == 2 - && argv[0]->type == ESEXP_RES_STRING - && argv[1]->type == ESEXP_RES_STRING) { - char *propname = argv[0]->value.string; - char *str = rfc2254_escape(argv[1]->value.string); - gboolean one_star = FALSE; - - if (strlen(str) == 0) - one_star = TRUE; - - if (!strcmp (propname, "x-evolution-any-field")) { - int i; - int query_length; - char *big_query; - char *match_str; - - match_str = g_strdup_printf("=*%s%s)", - str, one_star ? "" : "*"); - - query_length = 3; /* strlen ("(|") + strlen (")") */ - - for (i = 0; i < num_prop_infos; i ++) { - query_length += 1 /* strlen ("(") */ + strlen(prop_info[i].ldap_attr) + strlen (match_str); - } - - big_query = g_malloc0(query_length + 1); - strcat (big_query, "(|"); - for (i = 0; i < num_prop_infos; i ++) { - strcat (big_query, "("); - strcat (big_query, prop_info[i].ldap_attr); - strcat (big_query, match_str); - } - strcat (big_query, ")"); - - ldap_data->list = g_list_prepend(ldap_data->list, big_query); - - g_free (match_str); - } - else { - char *ldap_attr = query_prop_to_ldap(propname); - - if (ldap_attr) - ldap_data->list = g_list_prepend(ldap_data->list, - g_strdup_printf("(%s=*%s%s)", - ldap_attr, - str, - one_star ? "" : "*")); - } - - g_free (str); - } - - r = e_sexp_result_new(f, ESEXP_RES_BOOL); - r->value.bool = FALSE; - - return r; -} - -static ESExpResult * -func_is(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data) -{ - PASBackendLDAPSExpData *ldap_data = data; - ESExpResult *r; - - if (argc == 2 - && argv[0]->type == ESEXP_RES_STRING - && argv[1]->type == ESEXP_RES_STRING) { - char *propname = argv[0]->value.string; - char *str = rfc2254_escape(argv[1]->value.string); - char *ldap_attr = query_prop_to_ldap(propname); - - if (ldap_attr) - ldap_data->list = g_list_prepend(ldap_data->list, - g_strdup_printf("(%s=%s)", - ldap_attr, str)); - else { - g_warning ("unknown query property\n"); - /* we want something that'll always be false */ - ldap_data->list = g_list_prepend(ldap_data->list, - g_strdup("objectClass=MyBarnIsBiggerThanYourBarn")); - } - - g_free (str); - } - - r = e_sexp_result_new(f, ESEXP_RES_BOOL); - r->value.bool = FALSE; - - return r; -} - -static ESExpResult * -func_beginswith(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data) -{ - PASBackendLDAPSExpData *ldap_data = data; - ESExpResult *r; - - if (argc == 2 - && argv[0]->type == ESEXP_RES_STRING - && argv[1]->type == ESEXP_RES_STRING) { - char *propname = argv[0]->value.string; - char *str = rfc2254_escape(argv[1]->value.string); - char *ldap_attr = query_prop_to_ldap(propname); - - /* insert hack for fileAs queries, since we need to do - the right thing if the server supports them or not, - and for entries that have no fileAs attribute. */ - if (ldap_attr) { - if (!strcmp (propname, "full_name")) { - ldap_data->list = g_list_prepend(ldap_data->list, - g_strdup_printf( - "(|(cn=%s*)(sn=%s*))", - str, str)); - } - else if (!strcmp (ldap_attr, "fileAs")) { - if (ldap_data->bl->priv->evolutionPersonSupported) - ldap_data->list = g_list_prepend(ldap_data->list, - g_strdup_printf("(|(fileAs=%s*)(&(!(fileAs=*))(sn=%s*)))", - str, str)); - else - ldap_data->list = g_list_prepend(ldap_data->list, - g_strdup_printf("(sn=%s*)", str)); - } - else { - ldap_data->list = g_list_prepend(ldap_data->list, - g_strdup_printf("(%s=%s*)", - ldap_attr, - str)); - } - } - - g_free (str); - } - - r = e_sexp_result_new(f, ESEXP_RES_BOOL); - r->value.bool = FALSE; - - return r; -} - -static ESExpResult * -func_endswith(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data) -{ - PASBackendLDAPSExpData *ldap_data = data; - ESExpResult *r; - - if (argc == 2 - && argv[0]->type == ESEXP_RES_STRING - && argv[1]->type == ESEXP_RES_STRING) { - char *propname = argv[0]->value.string; - char *str = rfc2254_escape(argv[1]->value.string); - char *ldap_attr = query_prop_to_ldap(propname); - - if (ldap_attr) - ldap_data->list = g_list_prepend(ldap_data->list, - g_strdup_printf("(%s=*%s)", - ldap_attr, - str)); - g_free (str); - } - - r = e_sexp_result_new(f, ESEXP_RES_BOOL); - r->value.bool = FALSE; - - return r; -} - -/* 'builtin' functions */ -static struct { - char *name; - ESExpFunc *func; - int type; /* set to 1 if a function can perform shortcut evaluation, or - doesn't execute everything, 0 otherwise */ -} symbols[] = { - { "and", func_and, 0 }, - { "or", func_or, 0 }, - { "not", func_not, 0 }, - { "contains", func_contains, 0 }, - { "is", func_is, 0 }, - { "beginswith", func_beginswith, 0 }, - { "endswith", func_endswith, 0 }, -}; - -static gchar * -pas_backend_ldap_build_query (PASBackendLDAP *bl, gchar *query) -{ - ESExp *sexp; - ESExpResult *r; - gchar *retval; - PASBackendLDAPSExpData data; - int i; - - data.list = NULL; - data.bl = bl; - - sexp = e_sexp_new(); - - for(i=0;i<sizeof(symbols)/sizeof(symbols[0]);i++) { - if (symbols[i].type == 1) { - e_sexp_add_ifunction(sexp, 0, symbols[i].name, - (ESExpIFunc *)symbols[i].func, &data); - } else { - e_sexp_add_function(sexp, 0, symbols[i].name, - symbols[i].func, &data); - } - } - - e_sexp_input_text(sexp, query, strlen(query)); - e_sexp_parse(sexp); - - r = e_sexp_eval(sexp); - - e_sexp_result_free(sexp, r); - e_sexp_unref (sexp); - - if (data.list) { - if (data.list->next) { - g_warning ("conversion to ldap query string failed"); - retval = NULL; - g_list_foreach (data.list, (GFunc)g_free, NULL); - } - else { - retval = data.list->data; - } - } - else { - g_warning ("conversion to ldap query string failed"); - retval = NULL; - } - - g_list_free (data.list); - return retval; -} - -static gchar * -query_prop_to_ldap(gchar *query_prop) -{ - int i; - - for (i = 0; i < num_prop_infos; i ++) - if (!strcmp (query_prop, prop_info[i].query_prop)) - return prop_info[i].ldap_attr; - - return NULL; -} - - -typedef struct { - LDAPOp op; - 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 * -build_card_from_entry (LDAP *ldap, LDAPMessage *e, GList **existing_objectclasses) -{ - ECard *ecard = e_card_new (""); - ECardSimple *card = e_card_simple_new (ecard); - char *dn; - char *attr; - BerElement *ber = NULL; - - dn = ldap_get_dn(ldap, e); - e_card_simple_set_id (card, dn); - ldap_memfree (dn); - - for (attr = ldap_first_attribute (ldap, e, &ber); attr; - attr = ldap_next_attribute (ldap, e, ber)) { - int i; - struct prop_info *info = NULL; - char **values; - - if (existing_objectclasses && !g_ascii_strcasecmp (attr, "objectclass")) { - values = ldap_get_values (ldap, e, attr); - for (i = 0; values[i]; i ++) - *existing_objectclasses = g_list_append (*existing_objectclasses, g_strdup (values[i])); - - ldap_value_free (values); - } - else { - for (i = 0; i < num_prop_infos; i ++) - if (!g_ascii_strcasecmp (attr, prop_info[i].ldap_attr)) { - info = &prop_info[i]; - break; - } - - if (info) { - values = ldap_get_values (ldap, e, attr); - - if (values) { - if (info->prop_type & PROP_TYPE_STRING) { - /* if it's a normal property just set the string */ - if (values[0]) - e_card_simple_set (card, info->field_id, values[0]); - - } - else if (info->prop_type & PROP_TYPE_COMPLEX) { - /* if it's a list call the ecard-populate function, - which calls g_object_set to set the property */ - info->populate_ecard_func(card, - values); - } - - ldap_value_free (values); - } - } - } - - ldap_memfree (attr); - } - - if (ber) - ber_free (ber, 0); - - e_card_simple_sync_card (card); - - g_object_unref (ecard); - - return card; -} - -static gboolean -poll_ldap (PASBackendLDAP *bl) -{ - LDAP *ldap = bl->priv->ldap; - int rc; - LDAPMessage *res; - GTimeVal cur_time; - glong cur_millis; - struct timeval timeout; - EIterator *iter; - - if (!bl->priv->active_ops) { - g_warning ("poll_ldap being called for backend with no active operations"); - return FALSE; - } - - timeout.tv_sec = 0; - timeout.tv_usec = LDAP_RESULT_TIMEOUT_MILLIS * 1000; - - rc = ldap_result (ldap, LDAP_RES_ANY, 0, &timeout, &res); - if (rc != 0) {/* rc == 0 means timeout exceeded */ - if (rc == -1) { - PASBookView *book_view = find_book_view (bl); - g_warning ("ldap_result returned -1, restarting ops"); - - pas_backend_ldap_reconnect (bl, book_view, LDAP_SERVER_DOWN); -#if 0 - if (bl->priv->connected) - restart_ops (bl); -#endif - } - else { - int msgid = ldap_msgid (res); - LDAPOp *op; - - op = g_hash_table_lookup (bl->priv->id_to_op, &msgid); - - if (op) - op->handler (op, res); - else - g_warning ("unknown operation, msgid = %d", msgid); - - ldap_msgfree(res); - } - } - - g_get_current_time (&cur_time); - cur_millis = TV_TO_MILLIS (cur_time); - - iter = e_list_get_iterator (bl->priv->book_views); - while (e_iterator_is_valid (iter)) { - PASBackendLDAPBookView *view = (PASBackendLDAPBookView *)e_iterator_get (iter); - if (view->search_op) { - bonobo_object_dup_ref(bonobo_object_corba_objref(BONOBO_OBJECT(view->book_view)), NULL); - - ldap_search_op_timeout (view->search_op, cur_millis); - - bonobo_object_release_unref(bonobo_object_corba_objref(BONOBO_OBJECT(view->book_view)), NULL); - } - e_iterator_next (iter); - } - g_object_unref (iter); - - 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); - g_list_free (search_op->pending_adds); - 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 - search_op->grouping_time_start > GROUPING_MINIMUM_WAIT) { - - if (search_op->num_pending_adds >= search_op->target_pending_adds) - send_pending_adds (search_op); - - if (cur_millis - search_op->grouping_time_start > GROUPING_MAXIMUM_WAIT) { - GTimeVal new_start; - - 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", - search_op->num_sent_this_time, - search_op->num_sent_last_time, - search_op->target_pending_adds); -#endif - g_get_current_time (&new_start); - 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; - } - } -} - -static void -ldap_search_handler (LDAPOp *op, LDAPMessage *res) -{ - LDAPSearchOp *search_op = (LDAPSearchOp*)op; - PASBackendLDAPBookView *view = search_op->view; - PASBackendLDAP *bl = PAS_BACKEND_LDAP (op->backend); - LDAP *ldap = bl->priv->ldap; - LDAPMessage *e; - int msg_type; - - bonobo_object_dup_ref(bonobo_object_corba_objref(BONOBO_OBJECT(view->book_view)), NULL); - - if (!search_op->notified_receiving_results) { - search_op->notified_receiving_results = TRUE; - book_view_notify_status (op->view, _("Receiving LDAP search results...")); - } - - msg_type = ldap_msgtype (res); - if (msg_type == LDAP_RES_SEARCH_ENTRY) { - e = ldap_first_entry(ldap, res); - - while (NULL != e) { - ECardSimple *card = build_card_from_entry (ldap, e, NULL); - - search_op->pending_adds = g_list_append (search_op->pending_adds, - e_card_simple_get_vcard_assume_utf8 (card)); - search_op->num_pending_adds ++; - - g_object_unref (card); - - e = ldap_next_entry(ldap, e); - } - } - else if (msg_type == LDAP_RES_SEARCH_RESULT) { - int ldap_error; - - ldap_parse_result (ldap, res, &ldap_error, - NULL, NULL, NULL, NULL, 0); - - g_warning ("search returned %d\n", ldap_error); - - /* the entry that marks the end of our search */ - if (search_op->num_pending_adds) - send_pending_adds (search_op); - - if (ldap_error == LDAP_TIMELIMIT_EXCEEDED) - pas_book_view_notify_complete (search_op->op.view, GNOME_Evolution_Addressbook_BookViewListener_SearchTimeLimitExceeded); - else if (ldap_error == LDAP_SIZELIMIT_EXCEEDED) - pas_book_view_notify_complete (search_op->op.view, GNOME_Evolution_Addressbook_BookViewListener_SearchSizeLimitExceeded); - else if (ldap_error == LDAP_SUCCESS) - pas_book_view_notify_complete (search_op->op.view, GNOME_Evolution_Addressbook_BookViewListener_Success); - else - pas_book_view_notify_complete (search_op->op.view, GNOME_Evolution_Addressbook_BookViewListener_OtherError); - - ldap_op_finished (op); - } - else { - 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, GNOME_Evolution_Addressbook_BookViewListener_OtherError); - ldap_op_finished (op); - } - - - bonobo_object_release_unref(bonobo_object_corba_objref(BONOBO_OBJECT(view->book_view)), NULL); -} - -static void -ldap_search_dtor (LDAPOp *op) -{ - LDAPSearchOp *search_op = (LDAPSearchOp*) op; - - /* unhook us from our PASBackendLDAPBookView */ - if (search_op->view) - search_op->view->search_op = NULL; - - g_list_foreach (search_op->pending_adds, (GFunc)g_free, NULL); - g_list_free (search_op->pending_adds); - search_op->pending_adds = NULL; - search_op->num_pending_adds = 0; - - g_free (search_op); -} - -static void -pas_backend_ldap_search (PASBackendLDAP *bl, - PASBook *book, - PASBackendLDAPBookView *view) -{ - char *ldap_query; - - 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; - - printf ("searching server using filter: %s\n", ldap_query); - - do { - book_view_notify_status (view->book_view, _("Searching...")); - - 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 */ - view->limit, &search_msgid); - } while (pas_backend_ldap_reconnect (bl, view->book_view, ldap_err)); - - g_free (ldap_query); - - if (ldap_err != LDAP_SUCCESS) { - book_view_notify_status (view->book_view, ldap_err2string(ldap_err)); - return; - } - else if (search_msgid == -1) { - book_view_notify_status (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); - - 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 { - pas_book_view_notify_complete (view->book_view, - GNOME_Evolution_Addressbook_BookViewListener_InvalidQuery); - return; - } - -} - -static void -ldap_get_view (PASBackend *backend, - PASBook *book, - const char *search, - GNOME_Evolution_Addressbook_BookViewListener listener, - int limit) -{ - PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); - PASBookView *book_view; - PASBackendLDAPBookView *view; - - book_view = pas_book_view_new (listener); - - bonobo_object_ref(BONOBO_OBJECT(book)); - g_object_weak_ref (G_OBJECT (book_view), view_destroy, book); - - view = g_new0(PASBackendLDAPBookView, 1); - view->book_view = book_view; - view->search = g_strdup(search); - view->card_sexp = pas_backend_card_sexp_new (view->search); - view->blpriv = bl->priv; - view->limit = limit; - - e_list_append(bl->priv->book_views, view); - - pas_book_respond_get_book_view (book, - (book_view != NULL - ? GNOME_Evolution_Addressbook_BookListener_Success - : GNOME_Evolution_Addressbook_BookListener_CardNotFound /* XXX */), - book_view); - - pas_backend_ldap_search (bl, book, view); - - bonobo_object_unref (BONOBO_OBJECT (book_view)); -} - -static void -pas_backend_ldap_process_get_book_view (PASBackend *backend, - PASBook *book, - PASGetBookViewRequest *req) -{ - PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); - - ldap_get_view (backend, book, req->search, req->listener, - bl->priv->ldap_limit); -} - -static void -pas_backend_ldap_process_get_completion_view (PASBackend *backend, - PASBook *book, - PASGetCompletionViewRequest *req) -{ - PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); - - ldap_get_view (backend, book, req->search, req->listener, - MIN (bl->priv->ldap_limit, 100)); -} - -static void -pas_backend_ldap_process_get_changes (PASBackend *backend, - PASBook *book, - PASGetChangesRequest *req) -{ - /* FIXME: implement */ -} - -static void -pas_backend_ldap_process_check_connection (PASBackend *backend, - PASBook *book, - PASCheckConnectionRequest *req) -{ - PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); - - pas_book_report_connection (book, bl->priv->connected); -} - -#define LDAP_SIMPLE_PREFIX "ldap/simple-" -#define SASL_PREFIX "sasl/" - -static void -pas_backend_ldap_process_authenticate_user (PASBackend *backend, - PASBook *book, - PASAuthenticateUserRequest *req) -{ - PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); - int ldap_error; - char *dn = NULL; - - if (!strncasecmp (req->auth_method, LDAP_SIMPLE_PREFIX, strlen (LDAP_SIMPLE_PREFIX))) { - - if (!strcmp (req->auth_method, "ldap/simple-email")) { - LDAPMessage *res, *e; - char *query = g_strdup_printf ("(mail=%s)", req->user); - - ldap_error = ldap_search_s (bl->priv->ldap, - bl->priv->ldap_rootdn, - bl->priv->ldap_scope, - query, - NULL, 0, &res); - g_free (query); - - if (ldap_error == LDAP_SUCCESS) { - char *entry_dn; - - e = ldap_first_entry (bl->priv->ldap, res); - - entry_dn = ldap_get_dn (bl->priv->ldap, e); - dn = g_strdup(entry_dn); - - ldap_memfree (entry_dn); - ldap_msgfree (res); - } - else { - pas_book_respond_authenticate_user (book, - GNOME_Evolution_Addressbook_BookListener_PermissionDenied); - return; - } - } - else if (!strcmp (req->auth_method, "ldap/simple-binddn")) { - dn = g_strdup (req->user); - } - - /* now authenticate against the DN we were either supplied or queried for */ - printf ("simple auth as %s\n", dn); - ldap_error = ldap_simple_bind_s(bl->priv->ldap, - dn, - req->passwd); - - pas_book_respond_authenticate_user (book, - ldap_error_to_response (ldap_error)); - } -#ifdef ENABLE_SASL_BINDS - else if (!strncasecmp (req->auth_method, SASL_PREFIX, strlen (SASL_PREFIX))) { - g_print ("sasl bind (mech = %s) as %s", req->auth_method + strlen (SASL_PREFIX), req->user); - ldap_error = ldap_sasl_bind_s (bl->priv->ldap, - NULL, - req->auth_method + strlen (SASL_PREFIX), - req->passwd, - NULL, - NULL, - NULL); - - if (ldap_error == LDAP_NOT_SUPPORTED) - pas_book_respond_authenticate_user (book, - GNOME_Evolution_Addressbook_BookListener_UnsupportedAuthenticationMethod); - else - pas_book_respond_authenticate_user (book, - ldap_error_to_response (ldap_error)); - } -#endif - else { - pas_book_respond_authenticate_user (book, - GNOME_Evolution_Addressbook_BookListener_UnsupportedAuthenticationMethod); - return; - } - - if (ldap_error == LDAP_SUCCESS) { - bl->priv->auth_dn = dn; - bl->priv->auth_passwd = g_strdup (req->passwd); - - pas_backend_set_is_writable (backend, TRUE); - - /* force a requery on the root dse since some ldap - servers are set up such that they don't report - anything (including the schema DN) until the user - is authenticated */ - if (!bl->priv->evolutionPersonChecked) { - ldap_error = query_ldap_root_dse (bl); - - if (LDAP_SUCCESS == ldap_error) { - if (!bl->priv->evolutionPersonChecked) - check_schema_support (bl); - } - else - g_warning ("Failed to perform root dse query after authenticating, (ldap_error 0x%02x)", ldap_error); - } - - pas_book_report_writable (book, TRUE); - } - -} - -static void -pas_backend_ldap_process_get_supported_fields (PASBackend *backend, - PASBook *book, - PASGetSupportedFieldsRequest *req) - -{ - PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); - - pas_book_respond_get_supported_fields (book, - GNOME_Evolution_Addressbook_BookListener_Success, - bl->priv->supported_fields); -} - -static void -pas_backend_ldap_process_get_supported_auth_methods (PASBackend *backend, - PASBook *book, - PASGetSupportedAuthMethodsRequest *req) - -{ - PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); - - pas_book_respond_get_supported_auth_methods (book, - GNOME_Evolution_Addressbook_BookListener_Success, - bl->priv->supported_auth_methods); -} - -static GNOME_Evolution_Addressbook_BookListener_CallStatus -pas_backend_ldap_load_uri (PASBackend *backend, - const char *uri) -{ - PASBackendLDAP *bl = PAS_BACKEND_LDAP (backend); - LDAPURLDesc *lud; - int ldap_error; - char **attributes; - int i; - int limit = 100; - int timeout = 60; /* 1 minute */ - - g_assert (bl->priv->connected == FALSE); - - attributes = g_strsplit (uri, ";", 0); - - if (attributes[0] == NULL) - return FALSE; - - for (i = 1; attributes[i]; i++) { - char *equals; - char *value; - int key_length; - equals = strchr (attributes[i], '='); - if (equals) { - key_length = equals - attributes[i]; - value = equals + 1; - } else { - key_length = strlen (attributes[i]); - value = NULL; - } - - if (key_length == strlen("limit") && !strncmp (attributes[i], "limit", key_length)) { - if (value) - limit = atoi(value); - } - else if (key_length == strlen("ssl") && !strncmp (attributes[i], "ssl", key_length)) { - if (value) { - if (!strncmp (value, "always", 6)) { - bl->priv->use_tls = PAS_BACKEND_LDAP_TLS_ALWAYS; - } - else if (!strncmp (value, "whenever_possible", 3)) { - bl->priv->use_tls = PAS_BACKEND_LDAP_TLS_WHEN_POSSIBLE; - } - else if (strncmp (value, "never", 5)) { - g_warning ("unhandled value for use_tls, not using it"); - } - } - else { - bl->priv->use_tls = PAS_BACKEND_LDAP_TLS_WHEN_POSSIBLE; - } - } - else if (key_length == strlen("timeout") && !strncmp (attributes[i], "timeout", key_length)) { - if (value) - timeout = atoi (value); - } - } - - ldap_error = ldap_url_parse ((char*)attributes[0], &lud); - g_strfreev (attributes); - - if (ldap_error == LDAP_SUCCESS) { - g_free(bl->priv->uri); - bl->priv->uri = g_strdup (uri); - bl->priv->ldap_host = g_strdup(lud->lud_host); - bl->priv->ldap_port = lud->lud_port; - /* if a port wasn't specified, default to LDAP_PORT */ - if (bl->priv->ldap_port == 0) - bl->priv->ldap_port = LDAP_PORT; - bl->priv->ldap_rootdn = g_strdup(lud->lud_dn); - bl->priv->ldap_limit = limit; - bl->priv->ldap_timeout = timeout; - bl->priv->ldap_scope = lud->lud_scope; - - ldap_free_urldesc(lud); - - return pas_backend_ldap_connect (bl); - } else - return GNOME_Evolution_Addressbook_BookListener_OtherError; -} - -/* Get_uri handler for the addressbook LDAP backend */ -static const char * -pas_backend_ldap_get_uri (PASBackend *backend) -{ - PASBackendLDAP *bl; - - bl = PAS_BACKEND_LDAP (backend); - return bl->priv->uri; -} - -static char* -pas_backend_ldap_get_static_capabilities (PASBackend *backend) -{ - return g_strdup("net"); -} - -static gboolean -pas_backend_ldap_construct (PASBackendLDAP *backend) -{ - g_assert (backend != NULL); - g_assert (PAS_IS_BACKEND_LDAP (backend)); - - if (! pas_backend_construct (PAS_BACKEND (backend))) - return FALSE; - - return TRUE; -} - -/** - * pas_backend_ldap_new: - */ -PASBackend * -pas_backend_ldap_new (void) -{ - PASBackendLDAP *backend; - - backend = g_object_new (PAS_TYPE_BACKEND_LDAP, NULL); - - if (! pas_backend_ldap_construct (backend)) { - g_object_unref (backend); - - return NULL; - } - - return PAS_BACKEND (backend); -} - -static gboolean -call_dtor (int msgid, LDAPOp *op, gpointer data) -{ - ldap_abandon (PAS_BACKEND_LDAP(op->backend)->priv->ldap, op->id); - - op->dtor (op); - - return TRUE; -} - -static void -pas_backend_ldap_dispose (GObject *object) -{ - PASBackendLDAP *bl; - - bl = PAS_BACKEND_LDAP (object); - - if (bl->priv) { - 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) { - printf ("removing timeout\n"); - g_source_remove (bl->priv->poll_timeout); - } - - g_object_unref (bl->priv->book_views); - - if (bl->priv->supported_fields) - g_object_unref (bl->priv->supported_fields); - - if (bl->priv->supported_auth_methods) - g_object_unref (bl->priv->supported_auth_methods); - - g_free (bl->priv->uri); - - g_free (bl->priv); - bl->priv = NULL; - } - - if (G_OBJECT_CLASS (pas_backend_ldap_parent_class)->dispose) - G_OBJECT_CLASS (pas_backend_ldap_parent_class)->dispose (object); -} - -static void -pas_backend_ldap_class_init (PASBackendLDAPClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - PASBackendClass *parent_class; - - /* get client side information (extensions present in the library) */ - get_ldap_library_info (); - - pas_backend_ldap_parent_class = g_type_class_peek_parent (klass); - - parent_class = PAS_BACKEND_CLASS (klass); - - /* Set the virtual methods. */ - parent_class->load_uri = pas_backend_ldap_load_uri; - parent_class->get_uri = pas_backend_ldap_get_uri; - parent_class->get_static_capabilities = pas_backend_ldap_get_static_capabilities; - - parent_class->create_card = pas_backend_ldap_process_create_card; - parent_class->remove_cards = pas_backend_ldap_process_remove_cards; - parent_class->modify_card = pas_backend_ldap_process_modify_card; - parent_class->check_connection = pas_backend_ldap_process_check_connection; - parent_class->get_vcard = pas_backend_ldap_process_get_vcard; - parent_class->get_cursor = pas_backend_ldap_process_get_cursor; - parent_class->get_book_view = pas_backend_ldap_process_get_book_view; - parent_class->get_completion_view = pas_backend_ldap_process_get_completion_view; - parent_class->get_changes = pas_backend_ldap_process_get_changes; - parent_class->authenticate_user = pas_backend_ldap_process_authenticate_user; - parent_class->get_supported_fields = pas_backend_ldap_process_get_supported_fields; - parent_class->get_supported_auth_methods = pas_backend_ldap_process_get_supported_auth_methods; - - object_class->dispose = pas_backend_ldap_dispose; -} - -static void -pas_backend_ldap_init (PASBackendLDAP *backend) -{ - PASBackendLDAPPrivate *priv; - - priv = g_new0 (PASBackendLDAPPrivate, 1); - - priv->supported_fields = e_list_new ((EListCopyFunc)g_strdup, (EListFreeFunc)g_free, NULL); - priv->ldap_limit = 100; - priv->id_to_op = g_hash_table_new (g_int_hash, g_int_equal); - priv->poll_timeout = -1; - priv->book_views = e_list_new (NULL, NULL, NULL); - - backend->priv = priv; -} - -/** - * pas_backend_ldap_get_type: - */ -GType -pas_backend_ldap_get_type (void) -{ - static GType type = 0; - - if (! type) { - GTypeInfo info = { - sizeof (PASBackendLDAPClass), - NULL, /* base_class_init */ - NULL, /* base_class_finalize */ - (GClassInitFunc) pas_backend_ldap_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (PASBackendLDAP), - 0, /* n_preallocs */ - (GInstanceInitFunc) pas_backend_ldap_init - }; - - type = g_type_register_static (PAS_TYPE_BACKEND, "PASBackendLDAP", &info, 0); - } - - return type; -} |