Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / addressbook / backends / ldap / e-book-backend-ldap.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /* e-book-backend-ldap.c - LDAP contact backend.
4  *
5  * Copyright (C) 2005 Novell, Inc.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2 of the GNU Lesser General Public
9  * License as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  * Authors: Chris Toshok <toshok@ximian.com>
22  *          Hans Petter Jansson <hpj@novell.com>
23  */
24
25 #define DEBUG
26
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>  
29 #endif
30
31 #include <stdlib.h>
32 #include <fcntl.h>
33 #include <string.h>
34
35 #include <glib.h>
36
37 #ifndef G_OS_WIN32
38 #ifdef DEBUG
39 #define LDAP_DEBUG
40 #define LDAP_DEBUG_ADD
41 #define LDAP_DEBUG_MODIFY
42 #endif
43 #include <ldap.h>
44 #ifdef DEBUG
45 #undef LDAP_DEBUG
46 #endif
47 #else
48 #define interface windows_interface
49 #include <windows.h>
50 #undef interface
51 #include <winldap.h>
52 #include "openldap-extract.h"
53 #endif
54
55 #define d(x) x
56
57 #ifndef G_OS_WIN32
58
59 #if LDAP_VENDOR_VERSION > 20000
60 #define OPENLDAP2
61 #else
62 #define OPENLDAP1
63 #endif
64
65 #ifdef OPENLDAP2
66 #ifndef SUNLDAP
67 #include <ldap_schema.h>
68 #endif
69 #endif
70
71 #ifdef SUNLDAP
72 #include "openldap-extract.h"
73 #endif
74
75
76 #endif
77
78 #include <sys/time.h>
79
80 #include <glib/gi18n-lib.h>
81 #include "libedataserver/e-sexp.h"
82 #include <libebook/e-contact.h>
83
84 #include <libedata-book/e-book-backend-sexp.h>
85 #include <libedata-book/e-data-book.h>
86 #include <libedata-book/e-data-book-view.h>
87 #include <libedata-book/e-book-backend-cache.h>
88 #include <libedata-book/e-book-backend-summary.h>
89 #include "e-book-backend-ldap.h"
90
91 /* this is broken currently, don't enable it */
92 /*#define ENABLE_SASL_BINDS*/
93
94 typedef enum {
95         E_BOOK_BACKEND_LDAP_TLS_NO,
96         E_BOOK_BACKEND_LDAP_TLS_ALWAYS,
97         E_BOOK_BACKEND_LDAP_TLS_WHEN_POSSIBLE,
98 } EBookBackendLDAPUseTLS;
99
100 /* interval for our poll_ldap timeout */
101 #define LDAP_POLL_INTERVAL 20 
102
103 /* timeout for ldap_result */
104 #define LDAP_RESULT_TIMEOUT_MILLIS 10
105
106 #define TV_TO_MILLIS(timeval) ((timeval).tv_sec * 1000 + (timeval).tv_usec / 1000)
107
108 /* the objectClasses we need */
109 #define TOP                  "top"
110 #define PERSON               "person"
111 #define ORGANIZATIONALPERSON "organizationalPerson"
112 #define INETORGPERSON        "inetOrgPerson"
113 #define CALENTRY             "calEntry"
114 #define EVOLUTIONPERSON      "evolutionPerson"
115 #define GROUPOFNAMES         "groupOfNames"
116
117 static gboolean enable_debug = FALSE;
118
119 static gchar *query_prop_to_ldap(gchar *query_prop);
120 static gchar *e_book_backend_ldap_build_query (EBookBackendLDAP *bl, const char *query);
121
122 static EBookBackendClass *e_book_backend_ldap_parent_class;
123 typedef struct LDAPOp LDAPOp;
124
125
126 struct _EBookBackendLDAPPrivate {
127         gboolean connected;
128
129         gchar    *ldap_host;   /* the hostname of the server */
130         int      ldap_port;    /* the port of the server */
131         char     *schema_dn;   /* the base dn for schema information */
132         gchar    *ldap_rootdn; /* the base dn of our searches */
133         int      ldap_scope;   /* the scope used for searches */
134         gchar   *ldap_search_filter;
135         int      ldap_limit;   /* the search limit */
136         int      ldap_timeout; /* the search timeout */
137
138         gchar   *auth_dn;
139         gchar   *auth_passwd;
140
141         gboolean ldap_v3;      /* TRUE if the server supports protocol
142                                   revision 3 (necessary for TLS) */
143         gboolean starttls;     /* TRUE if the *library* supports
144                                   starttls.  will be false if openssl
145                                   was not built into openldap. */
146         EBookBackendLDAPUseTLS use_tls;
147
148         LDAP     *ldap;
149
150         GList    *supported_fields;
151         GList    *supported_auth_methods;
152
153         EBookBackendCache *cache;
154
155         /* whether or not there's support for the objectclass we need
156            to store all our additional fields */
157         gboolean evolutionPersonSupported;
158         gboolean calEntrySupported;
159         gboolean evolutionPersonChecked;
160         gboolean marked_for_offline;
161         
162         int mode;
163         /* our operations */
164         GStaticRecMutex op_hash_mutex;
165         GHashTable *id_to_op;
166         int active_ops;
167         int poll_timeout;
168
169         /* summary file related */
170         char *summary_file_name;
171         gboolean is_summary_ready;
172         EBookBackendSummary *summary;
173
174         /* for future use */
175         void *reserved1;
176         void *reserved2;
177         void *reserved3;
178         void *reserved4;
179 };
180
181 typedef void (*LDAPOpHandler)(LDAPOp *op, LDAPMessage *res);
182 typedef void (*LDAPOpDtor)(LDAPOp *op);
183
184 struct LDAPOp {
185         LDAPOpHandler  handler;
186         LDAPOpDtor     dtor;
187         EBookBackend  *backend;
188         EDataBook     *book;
189         EDataBookView *view;
190         guint32        opid; /* the libebook operation id */
191         int            id;   /* the ldap msg id */
192 };
193
194 static GStaticRecMutex eds_ldap_handler_lock = G_STATIC_REC_MUTEX_INIT;
195
196 static void     ldap_op_add (LDAPOp *op, EBookBackend *backend, EDataBook *book,
197                              EDataBookView *view, int opid, int msgid, LDAPOpHandler handler, LDAPOpDtor dtor);
198 static void     ldap_op_finished (LDAPOp *op);
199
200 static gboolean poll_ldap (EBookBackendLDAP *bl);
201
202 static EContact *build_contact_from_entry (EBookBackendLDAP *bl, LDAPMessage *e, GList **existing_objectclasses);
203
204 static void email_populate (EContact *contact, char **values);
205 static struct berval** email_ber (EContact *contact);
206 static gboolean email_compare (EContact *contact1, EContact *contact2);
207
208 static void member_populate (EContact *contact, char **values);
209 static struct berval** member_ber (EContact *contact);
210 static gboolean member_compare (EContact *contact1, EContact *contact2);
211
212 static void homephone_populate (EContact *contact, char **values);
213 static struct berval** homephone_ber (EContact *contact);
214 static gboolean homephone_compare (EContact *contact1, EContact *contact2);
215
216 static void business_populate (EContact *contact, char **values);
217 static struct berval** business_ber (EContact *contact);
218 static gboolean business_compare (EContact *contact1, EContact *contact2);
219
220 static void anniversary_populate (EContact *contact, char **values);
221 static struct berval** anniversary_ber (EContact *contact);
222 static gboolean anniversary_compare (EContact *contact1, EContact *contact2);
223
224 static void birthday_populate (EContact *contact, char **values);
225 static struct berval** birthday_ber (EContact *contact);
226 static gboolean birthday_compare (EContact *contact1, EContact *contact2);
227
228 static void category_populate (EContact *contact, char **values);
229 static struct berval** category_ber (EContact *contact);
230 static gboolean category_compare (EContact *contact1, EContact *contact2);
231
232 static void home_address_populate(EContact * card, char **values);
233 static struct berval **home_address_ber(EContact * card);
234 static gboolean home_address_compare(EContact * ecard1, EContact * ecard2);
235
236 static void work_address_populate(EContact * card, char **values);
237 static struct berval **work_address_ber(EContact * card);
238 static gboolean work_address_compare(EContact * ecard1, EContact * ecard2);
239
240 static void other_address_populate(EContact * card, char **values);
241 static struct berval **other_address_ber(EContact * card);
242 static gboolean other_address_compare(EContact * ecard1, EContact * ecard2);
243
244 static void work_city_populate(EContact * card, char **values);
245 static void work_state_populate(EContact * card, char **values);
246 static void work_po_populate(EContact * card, char **values);
247 static void work_zip_populate(EContact * card, char **values);
248 static void work_country_populate(EContact * card, char **values);
249 static void home_city_populate(EContact * card, char **values);
250 static void home_state_populate(EContact * card, char **values);
251 static void home_zip_populate(EContact * card, char **values);
252 static void home_country_populate(EContact * card, char **values);
253
254 static void photo_populate (EContact *contact, struct berval **ber_values);
255 static struct berval **photo_ber (EContact * contact);
256 static gboolean photo_compare(EContact * ecard1, EContact * ecard2);
257
258 static void cert_populate (EContact *contact, struct berval **ber_values);
259
260 static struct prop_info {
261         EContactField field_id;
262         char *ldap_attr;
263 #define PROP_TYPE_STRING    0x01
264 #define PROP_TYPE_COMPLEX   0x02
265 #define PROP_TYPE_BINARY    0x04
266 #define PROP_DN             0x08
267 #define PROP_EVOLVE         0x10
268 #define PROP_WRITE_ONLY     0x20
269 #define PROP_TYPE_GROUP     0x40
270         int prop_type;
271
272         /* the remaining items are only used for the TYPE_COMPLEX props */
273
274         /* used when reading from the ldap server populates EContact with the values in **values. */
275         void (*populate_contact_func)(EContact *contact, char **values);
276         /* used when writing to an ldap server.  returns a NULL terminated array of berval*'s */
277         struct berval** (*ber_func)(EContact *contact);
278         /* used to compare list attributes */
279         gboolean (*compare_func)(EContact *contact1, EContact *contact2);
280
281         void (*binary_populate_contact_func)(EContact *contact, struct berval **ber_values);
282
283 } prop_info[] = {
284
285 #define BINARY_PROP(fid,a,ctor,ber,cmp) {fid, a, PROP_TYPE_BINARY, NULL, ber, cmp, ctor}
286 #define COMPLEX_PROP(fid,a,ctor,ber,cmp) {fid, a, PROP_TYPE_COMPLEX, ctor, ber, cmp}
287 #define E_COMPLEX_PROP(fid,a,ctor,ber,cmp) {fid, a, PROP_TYPE_COMPLEX | PROP_EVOLVE, ctor, ber, cmp}
288 #define STRING_PROP(fid,a) {fid, a, PROP_TYPE_STRING}
289 #define WRITE_ONLY_STRING_PROP(fid,a) {fid, a, PROP_TYPE_STRING | PROP_WRITE_ONLY}
290 #define E_STRING_PROP(fid,a) {fid, a, PROP_TYPE_STRING | PROP_EVOLVE}
291 #define GROUP_PROP(fid,a,ctor,ber,cmp) {fid, a, PROP_TYPE_GROUP, ctor, ber, cmp}
292 #define ADDRESS_STRING_PROP(fid,a, ctor) {fid, a, PROP_TYPE_COMPLEX, ctor}
293
294
295         /* name fields */
296         STRING_PROP (E_CONTACT_FULL_NAME,   "cn" ),
297         WRITE_ONLY_STRING_PROP (E_CONTACT_FAMILY_NAME, "sn" ),
298
299         /* email addresses */
300         COMPLEX_PROP   (E_CONTACT_EMAIL, "mail", email_populate, email_ber, email_compare),
301         GROUP_PROP   (E_CONTACT_EMAIL, "member", member_populate, member_ber, member_compare),
302
303         /* phone numbers */
304         E_STRING_PROP (E_CONTACT_PHONE_PRIMARY,      "primaryPhone"),
305         COMPLEX_PROP  (E_CONTACT_PHONE_BUSINESS,     "telephoneNumber", business_populate, business_ber, business_compare),
306         COMPLEX_PROP  (E_CONTACT_PHONE_HOME,         "homePhone", homephone_populate, homephone_ber, homephone_compare),
307         STRING_PROP   (E_CONTACT_PHONE_MOBILE,       "mobile"),
308         E_STRING_PROP (E_CONTACT_PHONE_CAR,          "carPhone"),
309         STRING_PROP   (E_CONTACT_PHONE_BUSINESS_FAX, "facsimileTelephoneNumber"), 
310         E_STRING_PROP (E_CONTACT_PHONE_HOME_FAX,     "homeFacsimileTelephoneNumber"), 
311         E_STRING_PROP (E_CONTACT_PHONE_OTHER,        "otherPhone"), 
312         E_STRING_PROP (E_CONTACT_PHONE_OTHER_FAX,    "otherFacsimileTelephoneNumber"), 
313         STRING_PROP   (E_CONTACT_PHONE_ISDN,         "internationaliSDNNumber"), 
314         STRING_PROP   (E_CONTACT_PHONE_PAGER,        "pager"),
315         E_STRING_PROP (E_CONTACT_PHONE_RADIO,        "radio"),
316         E_STRING_PROP (E_CONTACT_PHONE_TELEX,        "telex"),
317         E_STRING_PROP (E_CONTACT_PHONE_ASSISTANT,    "assistantPhone"),
318         E_STRING_PROP (E_CONTACT_PHONE_COMPANY,      "companyPhone"),
319         E_STRING_PROP (E_CONTACT_PHONE_CALLBACK,     "callbackPhone"),
320         E_STRING_PROP (E_CONTACT_PHONE_TTYTDD,       "tty"),
321
322         /* org information */
323         STRING_PROP   (E_CONTACT_ORG,       "o"),
324         STRING_PROP   (E_CONTACT_ORG_UNIT,  "ou"),
325         STRING_PROP   (E_CONTACT_OFFICE,    "roomNumber"),
326         STRING_PROP   (E_CONTACT_TITLE,     "title"),
327         E_STRING_PROP (E_CONTACT_ROLE,      "businessRole"), 
328         E_STRING_PROP (E_CONTACT_MANAGER,   "managerName"), 
329         E_STRING_PROP (E_CONTACT_ASSISTANT, "assistantName"), 
330
331         /* addresses */
332         COMPLEX_PROP  (E_CONTACT_ADDRESS_LABEL_WORK, "postalAddress", work_address_populate, work_address_ber, work_address_compare),
333         ADDRESS_STRING_PROP(E_CONTACT_ADDRESS_WORK, "l", work_city_populate),
334         ADDRESS_STRING_PROP(E_CONTACT_ADDRESS_WORK, "st", work_state_populate),
335         ADDRESS_STRING_PROP(E_CONTACT_ADDRESS_WORK, "postofficebox", work_po_populate),
336         ADDRESS_STRING_PROP(E_CONTACT_ADDRESS_WORK, "postalcode", work_zip_populate),
337         ADDRESS_STRING_PROP(E_CONTACT_ADDRESS_WORK, "c", work_country_populate),
338
339         COMPLEX_PROP  (E_CONTACT_ADDRESS_LABEL_HOME, "homePostalAddress", home_address_populate, home_address_ber, home_address_compare),
340         ADDRESS_STRING_PROP(E_CONTACT_ADDRESS_HOME, "mozillaHomeLocalityName", home_city_populate),
341         ADDRESS_STRING_PROP(E_CONTACT_ADDRESS_HOME, "mozillaHomeState", home_state_populate),
342         ADDRESS_STRING_PROP(E_CONTACT_ADDRESS_HOME, "mozillaHomePostalCode", home_zip_populate),
343         ADDRESS_STRING_PROP(E_CONTACT_ADDRESS_HOME, "mozillaHomeCountryName", home_country_populate),
344
345         E_COMPLEX_PROP(E_CONTACT_ADDRESS_LABEL_OTHER, "otherPostalAddress", other_address_populate, other_address_ber, other_address_compare),
346
347         /* photos */
348         BINARY_PROP  (E_CONTACT_PHOTO,       "jpegPhoto", photo_populate, photo_ber, photo_compare),
349
350         /* certificate foo. */
351         BINARY_PROP  (E_CONTACT_X509_CERT,   "userCertificate", cert_populate, NULL/*XXX*/, NULL/*XXX*/),
352 #if 0
353         /* hm, which do we use?  the inetOrgPerson schema says that
354            userSMIMECertificate should be used in favor of
355            userCertificate for S/MIME applications. */
356         BINARY_PROP  (E_CONTACT_X509_CERT,   "userSMIMECertificate", cert_populate, NULL/*XXX*/, NULL/*XXX*/),
357 #endif
358
359         /* misc fields */
360         STRING_PROP    (E_CONTACT_HOMEPAGE_URL,  "labeledURI"),
361         /* map nickname to displayName */
362         STRING_PROP    (E_CONTACT_NICKNAME,    "displayName"),
363         E_STRING_PROP  (E_CONTACT_SPOUSE,      "spouseName"), 
364         E_STRING_PROP  (E_CONTACT_NOTE,        "note"), 
365         E_COMPLEX_PROP (E_CONTACT_ANNIVERSARY, "anniversary", anniversary_populate, anniversary_ber, anniversary_compare), 
366         E_COMPLEX_PROP (E_CONTACT_BIRTH_DATE,  "birthDate", birthday_populate, birthday_ber, birthday_compare), 
367         E_STRING_PROP  (E_CONTACT_MAILER,      "mailer"), 
368
369         E_STRING_PROP  (E_CONTACT_FILE_AS,     "fileAs"),
370
371         E_COMPLEX_PROP (E_CONTACT_CATEGORY_LIST,  "category", category_populate, category_ber, category_compare),
372
373         STRING_PROP (E_CONTACT_CALENDAR_URI,   "calCalURI"),
374         STRING_PROP (E_CONTACT_FREEBUSY_URL,   "calFBURL"),
375         STRING_PROP (E_CONTACT_ICS_CALENDAR,   "icsCalendar"),
376
377 #undef E_STRING_PROP
378 #undef STRING_PROP
379 #undef E_COMPLEX_PROP
380 #undef COMPLEX_PROP
381 #undef GROUP_PROP
382 };
383
384 static int num_prop_infos = sizeof(prop_info) / sizeof(prop_info[0]);
385
386 #if 0
387 static void
388 remove_view (int msgid, LDAPOp *op, EDataBookView *view)
389 {
390         if (op->view == view)
391                 op->view = NULL;
392 }
393
394 static void
395 view_destroy(gpointer data, GObject *where_object_was)
396 {
397         EDataBook           *book = (EDataBook *)data;
398         EBookBackendLDAP    *bl;
399         EIterator         *iter;
400
401         d(printf ("view_destroy (%p)\n", where_object_was));
402
403         bl = E_BOOK_BACKEND_LDAP(e_data_book_get_backend(book));
404
405         iter = e_list_get_iterator (bl->priv->book_views);
406
407         while (e_iterator_is_valid (iter)) {
408                 EBookBackendLDAPBookView *view = (EBookBackendLDAPBookView*)e_iterator_get (iter);
409
410                 if (view->book_view == (EDataBookView*)where_object_was) {
411                         GNOME_Evolution_Addressbook_Book    corba_book;
412                         CORBA_Environment ev;
413
414                         /* if we have an active search, interrupt it */
415                         if (view->search_op) {
416                                 ldap_op_finished (view->search_op);
417                         }
418                         /* and remove us as the view for any other
419                            operations that might be using us to spew
420                            status messages to the gui */
421                         g_static_rec_mutex_lock (&bl->priv->op_hash_mutex);
422                         g_hash_table_foreach (bl->priv->id_to_op, (GHFunc)remove_view, view->book_view);
423                         g_static_rec_mutex_unlock (&bl->priv->op_hash_mutex);
424
425                         /* free up the view structure */
426                         g_free (view->search);
427                         g_free (view);
428
429                         /* and remove it from our list */
430                         e_iterator_delete (iter);
431
432                         /* unref the book now */
433                         corba_book = bonobo_object_corba_objref(BONOBO_OBJECT(book));
434
435                         CORBA_exception_init(&ev);
436
437                         GNOME_Evolution_Addressbook_Book_unref(corba_book, &ev);
438         
439                         if (ev._major != CORBA_NO_EXCEPTION) {
440                                 g_warning("view_destroy: Exception unreffing "
441                                           "corba book.\n");
442                         }
443
444                         CORBA_exception_free(&ev);
445                         break;
446                 }
447
448                 e_iterator_next (iter);
449         }
450
451         g_object_unref (iter);
452
453 }
454 #endif
455
456 static void
457 book_view_notify_status (EDataBookView *view, const char *status)
458 {
459         if (!view)
460                 return;
461         e_data_book_view_notify_status_message (view, status);
462 }
463
464 static EDataBookView*
465 find_book_view (EBookBackendLDAP *bl)
466 {
467         EList *views = e_book_backend_get_book_views (E_BOOK_BACKEND (bl));
468         EIterator *iter = e_list_get_iterator (views);
469         EDataBookView *rv = NULL;
470
471         if (e_iterator_is_valid (iter)) {
472                 /* just always use the first book view */
473                 EDataBookView *v = (EDataBookView*)e_iterator_get(iter);
474                 if (v)
475                         rv = v;
476         }
477
478         g_object_unref (iter);
479         g_object_unref (views);
480
481         return rv;
482 }
483
484 static void
485 add_to_supported_fields (EBookBackendLDAP *bl, char **attrs, GHashTable *attr_hash)
486 {
487         int i;
488         for (i = 0; attrs[i]; i ++) {
489                 char *query_prop = g_hash_table_lookup (attr_hash, attrs[i]);
490
491                 if (query_prop) {
492                         bl->priv->supported_fields = g_list_append (bl->priv->supported_fields, g_strdup (query_prop));
493
494                         /* handle the list attributes here */
495                         if (!strcmp (query_prop, e_contact_field_name (E_CONTACT_EMAIL))) {
496                                 bl->priv->supported_fields = g_list_append (bl->priv->supported_fields, g_strdup (e_contact_field_name (E_CONTACT_EMAIL_1)));
497                                 bl->priv->supported_fields = g_list_append (bl->priv->supported_fields, g_strdup (e_contact_field_name (E_CONTACT_EMAIL_2)));
498                                 bl->priv->supported_fields = g_list_append (bl->priv->supported_fields, g_strdup (e_contact_field_name (E_CONTACT_EMAIL_3)));
499                                 bl->priv->supported_fields = g_list_append (bl->priv->supported_fields, g_strdup (e_contact_field_name (E_CONTACT_EMAIL_4)));
500                         }
501                         else if (!strcmp (query_prop, e_contact_field_name (E_CONTACT_PHONE_BUSINESS))) {
502                                 bl->priv->supported_fields = g_list_append (bl->priv->supported_fields, g_strdup (e_contact_field_name (E_CONTACT_PHONE_BUSINESS_2)));
503                         }
504                         else if (!strcmp (query_prop, e_contact_field_name (E_CONTACT_PHONE_HOME))) {
505                                 bl->priv->supported_fields = g_list_append (bl->priv->supported_fields, g_strdup (e_contact_field_name(E_CONTACT_PHONE_HOME_2)));
506                         }
507                         else if (!strcmp (query_prop, e_contact_field_name (E_CONTACT_CATEGORY_LIST) )) {
508                                 bl->priv->supported_fields = g_list_append (bl->priv->supported_fields, g_strdup (e_contact_field_name (E_CONTACT_CATEGORIES)));
509                         }
510                         
511                 }
512         }
513 }
514
515 static void
516 add_oc_attributes_to_supported_fields (EBookBackendLDAP *bl, LDAPObjectClass *oc)
517 {
518         int i;
519         GHashTable *attr_hash = g_hash_table_new (g_str_hash, g_str_equal);
520
521         for (i = 0; i < num_prop_infos; i ++)
522                 g_hash_table_insert (attr_hash, prop_info[i].ldap_attr, (char*)e_contact_field_name (prop_info[i].field_id));
523
524         if (oc->oc_at_oids_must)
525                 add_to_supported_fields (bl, oc->oc_at_oids_must, attr_hash);
526
527         if (oc->oc_at_oids_may)
528                 add_to_supported_fields (bl, oc->oc_at_oids_may, attr_hash);
529
530         g_hash_table_destroy (attr_hash);
531 }
532
533 static void
534 check_schema_support (EBookBackendLDAP *bl)
535 {
536         char *attrs[2];
537         LDAPMessage *resp;
538         LDAP *ldap;
539         struct timeval timeout;
540
541         ldap = bl->priv->ldap;
542         if (!ldap) {
543                 return;
544         }
545
546         if (!bl->priv->schema_dn)
547                 return;
548
549         bl->priv->evolutionPersonChecked = TRUE;
550
551         attrs[0] = "objectClasses";
552         attrs[1] = NULL;
553
554         timeout.tv_sec = 30;
555         timeout.tv_usec = 0;
556
557         g_static_rec_mutex_lock (&eds_ldap_handler_lock);
558         if (ldap_search_ext_s (ldap, bl->priv->schema_dn, LDAP_SCOPE_BASE,
559                                "(objectClass=subschema)", attrs, 0,
560                                NULL, NULL, &timeout, LDAP_NO_LIMIT, &resp) == LDAP_SUCCESS) {
561                 char **values;
562
563                 values = ldap_get_values (ldap, resp, "objectClasses");
564                 g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
565
566                 if (values) {
567                         int i;
568                         for (i = 0; values[i]; i ++) {
569                                 int j;
570                                 int code;
571                                 const char *err;
572                                 LDAPObjectClass *oc = ldap_str2objectclass (values[i], &code, &err, 0);
573
574                                 if (!oc)
575                                         continue;
576
577                                 for (j = 0; oc->oc_names[j]; j++)
578                                         if (!g_ascii_strcasecmp (oc->oc_names[j], EVOLUTIONPERSON)) {
579                                                 g_print ("support found on ldap server for objectclass evolutionPerson\n");
580                                                 bl->priv->evolutionPersonSupported = TRUE;
581
582                                                 add_oc_attributes_to_supported_fields (bl, oc);
583                                         }
584                                         else if (!g_ascii_strcasecmp (oc->oc_names[j], CALENTRY)) {
585                                                 g_print ("support found on ldap server for objectclass calEntry\n");
586                                                 bl->priv->calEntrySupported = TRUE;
587                                                 add_oc_attributes_to_supported_fields (bl, oc);
588                                         }
589                                         else if (!g_ascii_strcasecmp (oc->oc_names[j], INETORGPERSON)
590                                                  || !g_ascii_strcasecmp (oc->oc_names[j], ORGANIZATIONALPERSON)
591                                                  || !g_ascii_strcasecmp (oc->oc_names[j], PERSON)
592                                                  || !g_ascii_strcasecmp (oc->oc_names[j], GROUPOFNAMES)) {
593                                                 add_oc_attributes_to_supported_fields (bl, oc);
594                                         }
595
596                                 ldap_objectclass_free (oc);
597                         }
598
599                         ldap_value_free (values);
600                 }
601                 else {
602                         /* the reason for this is so that if the user
603                            ends up authenticating to the ldap server,
604                            we will requery for the subschema values.
605                            This makes it a bit more robust in the face
606                            of draconian acl's that keep subschema
607                            reads from working until the user is
608                            authed. */
609                         if (!e_book_backend_is_writable (E_BOOK_BACKEND (bl))) {
610                                 g_warning ("subschema read returned nothing before successful auth");
611                                 bl->priv->evolutionPersonChecked = FALSE;
612                         }
613                         else {
614                                 g_warning ("subschema read returned nothing after successful auth");
615                         }
616                 }
617
618                 ldap_msgfree (resp);
619         }
620         else {
621                 g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
622         }
623 }
624
625
626 #ifndef SUNLDAP
627 static void
628 get_ldap_library_info (void)
629 {
630         LDAPAPIInfo info;
631         LDAP *ldap;
632
633         ldap = ldap_init (NULL, 0);
634         if (ldap == NULL) {
635                 g_warning ("couldn't create LDAP* for getting at the client lib api info");
636                 return;
637         }
638
639         info.ldapai_info_version = LDAP_API_INFO_VERSION;
640
641         if (LDAP_OPT_SUCCESS != ldap_get_option (ldap, LDAP_OPT_API_INFO, &info)) {
642                 g_warning ("couldn't get ldap api info");
643         }
644         else {
645                 int i;
646                 g_message ("libldap vendor/version: %s %2d.%02d.%02d",
647                            info.ldapai_vendor_name,
648                            info.ldapai_vendor_version / 10000,
649                            (info.ldapai_vendor_version % 10000) / 1000,
650                            info.ldapai_vendor_version % 1000);
651
652                 g_message ("library extensions present:");
653                 /* yuck.  we have to free these? */
654                 for (i = 0; info.ldapai_extensions[i]; i++) {
655                         char *extension = info.ldapai_extensions[i];
656                         g_message (extension);
657                         ldap_memfree (extension);
658                 }
659                 ldap_memfree (info.ldapai_extensions);
660                 ldap_memfree (info.ldapai_vendor_name);
661         }
662
663         ldap_unbind (ldap);
664 }
665 #endif
666
667 static int
668 query_ldap_root_dse (EBookBackendLDAP *bl)
669 {
670 #define MAX_DSE_ATTRS 20
671         LDAP *ldap;
672         LDAPMessage *resp;
673         int ldap_error = LDAP_OTHER;
674         char *attrs[MAX_DSE_ATTRS], **values;
675         int i = 0;
676         struct timeval timeout;
677
678         g_static_rec_mutex_lock (&eds_ldap_handler_lock);
679         ldap = bl->priv->ldap;
680         if (!ldap) {
681                 g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
682                 return ldap_error;
683         }
684         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
685
686         attrs[i++] = "supportedControl";
687         attrs[i++] = "supportedExtension";
688         attrs[i++] = "supportedFeatures";
689         attrs[i++] = "supportedSASLMechanisms";
690         attrs[i++] = "supportedLDAPVersion";
691         attrs[i++] = "subschemaSubentry"; /* OpenLDAP's dn for schema information */
692         attrs[i++] = "schemaNamingContext"; /* Active directory's dn for schema information */
693         attrs[i] = NULL;
694
695         timeout.tv_sec = 30;
696         timeout.tv_usec = 0;
697
698         g_static_rec_mutex_lock (&eds_ldap_handler_lock);
699         ldap_error = ldap_search_ext_s (ldap,
700                                         LDAP_ROOT_DSE, LDAP_SCOPE_BASE,
701                                         "(objectclass=*)",
702                                         attrs, 0, NULL, NULL, &timeout, LDAP_NO_LIMIT, &resp);
703         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
704         if (ldap_error != LDAP_SUCCESS) {
705                 g_warning ("could not perform query on Root DSE (ldap_error 0x%02x)", ldap_error);
706                 return ldap_error;
707         }
708
709         g_static_rec_mutex_lock (&eds_ldap_handler_lock);
710         values = ldap_get_values (ldap, resp, "supportedControl");
711         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
712         if (values) {
713                 for (i = 0; values[i]; i++)
714                         g_message ("supported server control: %s", values[i]);
715                 ldap_value_free (values);
716         }
717
718         g_static_rec_mutex_lock (&eds_ldap_handler_lock);
719         values = ldap_get_values (ldap, resp, "supportedExtension");
720         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
721         if (values) {
722                 for (i = 0; values[i]; i++) {
723                         g_message ("supported server extension: %s", values[i]);
724                         if (!strcmp (values[i], LDAP_EXOP_START_TLS)) {
725                                 g_message ("server reports LDAP_EXOP_START_TLS");
726                         }
727                 }
728                 ldap_value_free (values);
729         }
730
731         g_static_rec_mutex_lock (&eds_ldap_handler_lock);
732         values = ldap_get_values (ldap, resp, "supportedSASLMechanisms");
733         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
734         if (values) {
735                 char *auth_method;
736                 if (bl->priv->supported_auth_methods) {
737                         g_list_foreach (bl->priv->supported_auth_methods, (GFunc)g_free, NULL);
738                         g_list_free (bl->priv->supported_auth_methods);
739                 }
740                 bl->priv->supported_auth_methods = NULL;
741
742                 auth_method = g_strdup_printf ("ldap/simple-binddn|%s", _("Using Distinguished Name (DN)"));
743                 bl->priv->supported_auth_methods = g_list_append (bl->priv->supported_auth_methods, auth_method);
744
745                 auth_method = g_strdup_printf ("ldap/simple-email|%s", _("Using Email Address"));
746                 bl->priv->supported_auth_methods = g_list_append (bl->priv->supported_auth_methods, auth_method);
747
748                 for (i = 0; values[i]; i++) {
749                         auth_method = g_strdup_printf ("sasl/%s|%s", values[i], values[i]);
750                         bl->priv->supported_auth_methods = g_list_append (bl->priv->supported_auth_methods, auth_method);
751                         g_message ("supported SASL mechanism: %s", values[i]);
752                 }
753                 ldap_value_free (values);
754         }
755
756         g_static_rec_mutex_lock (&eds_ldap_handler_lock);
757         values = ldap_get_values (ldap, resp, "subschemaSubentry");
758         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
759         if (!values || !values[0]) {
760                 if (values) ldap_value_free (values);
761                 g_static_rec_mutex_lock (&eds_ldap_handler_lock);
762                 values = ldap_get_values (ldap, resp, "schemaNamingContext");
763                 g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
764         }
765         if (values && values[0]) {
766                 g_free (bl->priv->schema_dn);
767                 bl->priv->schema_dn = g_strdup (values[0]);
768         }
769         else {
770                 g_warning ("could not determine location of schema information on LDAP server");
771         }
772         if (values)
773                 ldap_value_free (values);
774
775         ldap_msgfree (resp);
776
777         return LDAP_SUCCESS;
778 }
779
780 static GNOME_Evolution_Addressbook_CallStatus
781 e_book_backend_ldap_connect (EBookBackendLDAP *bl)
782 {
783         EBookBackendLDAPPrivate *blpriv = bl->priv;
784         int protocol_version = LDAP_VERSION3;
785         GTimeVal start, end;
786         unsigned long diff;
787 #ifdef SUNLDAP
788         int ldap_flag;
789 #endif
790
791         if (enable_debug) {
792                 printf ("e_book_backend_ldap_connect ... \n");
793                 g_get_current_time (&start);
794         }
795
796         /* close connection first if it's open first */
797         g_static_rec_mutex_lock (&eds_ldap_handler_lock);
798         if (blpriv->ldap) {
799                 ldap_unbind (blpriv->ldap);
800         }
801         
802 #ifdef SUNLDAP
803         if (bl->priv->use_tls != E_BOOK_BACKEND_LDAP_TLS_NO) {
804                 char *evolution_dir_path = 
805                         g_build_path ("/", g_get_home_dir (), ".evolution", NULL);
806                 ldap_flag = ldapssl_client_init (evolution_dir_path, NULL);
807                 g_free (evolution_dir_path);
808         }
809 #endif
810                         
811         blpriv->ldap = ldap_init (blpriv->ldap_host, blpriv->ldap_port);
812
813 #if defined (DEBUG) && defined (LDAP_OPT_DEBUG_LEVEL)
814         {
815                 int debug_level = 4;
816                 ldap_set_option (blpriv->ldap, LDAP_OPT_DEBUG_LEVEL, &debug_level);
817         }
818 #endif
819         if (NULL != blpriv->ldap) {
820                 int ldap_error;
821
822                 ldap_error = ldap_set_option (blpriv->ldap, LDAP_OPT_PROTOCOL_VERSION, &protocol_version);
823                 if (LDAP_SUCCESS != ldap_error) {
824                         g_warning ("failed to set protocol version to LDAPv3");
825                         bl->priv->ldap_v3 = FALSE;
826                 }
827                 else
828                         bl->priv->ldap_v3 = TRUE;
829
830                 if (bl->priv->use_tls != E_BOOK_BACKEND_LDAP_TLS_NO) {
831                         int tls_level;
832
833                         if (!bl->priv->ldap_v3 && bl->priv->use_tls == E_BOOK_BACKEND_LDAP_TLS_ALWAYS) {
834                                 g_message ("TLS not available (fatal version), v3 protocol could not be established (ldap_error 0x%02x)", ldap_error);
835                                 ldap_unbind (blpriv->ldap);
836                                 blpriv->ldap = NULL;
837                                 g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
838                                 return GNOME_Evolution_Addressbook_TLSNotAvailable;
839                         }
840
841                         if (bl->priv->ldap_port == LDAPS_PORT && bl->priv->use_tls == E_BOOK_BACKEND_LDAP_TLS_ALWAYS) {
842 #ifdef SUNLDAP
843                                 if (ldap_flag >= 0) {
844                                         ldap_error = ldapssl_install_routines (blpriv->ldap);
845                                 } else
846                                         ldap_error = LDAP_NOT_SUPPORTED;
847                         
848                                 if (LDAP_SUCCESS == ldap_error) {
849                                         ldap_error = ldap_set_option (blpriv->ldap, LDAP_OPT_SSL, LDAP_OPT_ON );
850                                         ldap_set_option(blpriv->ldap, LDAP_OPT_RECONNECT, LDAP_OPT_ON );
851                                 }
852 #else
853 #if defined (LDAP_OPT_X_TLS_HARD) && defined (LDAP_OPT_X_TLS)
854                                 tls_level = LDAP_OPT_X_TLS_HARD;
855                                 ldap_set_option (blpriv->ldap, LDAP_OPT_X_TLS, &tls_level);
856 #elif defined (G_OS_WIN32)
857                                 tls_level = LDAP_OPT_ON;
858                                 ldap_set_option (blpriv->ldap, LDAP_OPT_SSL, &tls_level);
859 #else
860                                 g_message ("TLS option not available");
861 #endif
862 #endif
863                         }
864                         else if (bl->priv->use_tls) {
865 #ifdef SUNLDAP
866                                 if (ldap_flag >= 0) {
867                                         ldap_error = ldapssl_install_routines (blpriv->ldap);
868                                 } else
869                                         ldap_error = LDAP_NOT_SUPPORTED;
870                                 
871                                 if (LDAP_SUCCESS == ldap_error) {
872                                         ldap_error = ldap_set_option (blpriv->ldap, LDAP_OPT_SSL, LDAP_OPT_ON );
873                                         ldap_set_option(blpriv->ldap, LDAP_OPT_RECONNECT, LDAP_OPT_ON );
874                                 }
875 #else
876                                 ldap_error = ldap_start_tls_s (blpriv->ldap, NULL, NULL);
877 #endif
878                                 if (LDAP_SUCCESS != ldap_error) {
879                                         if (bl->priv->use_tls == E_BOOK_BACKEND_LDAP_TLS_ALWAYS) {
880                                                 g_message ("TLS not available (fatal version), (ldap_error 0x%02x)", ldap_error);
881                                                 ldap_unbind (blpriv->ldap);
882                                                 blpriv->ldap = NULL;
883                                                 g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
884                                                 return GNOME_Evolution_Addressbook_TLSNotAvailable;
885                                         }
886                                         else {
887                                                 g_message ("TLS not available (ldap_error 0x%02x)", ldap_error);
888                                         }
889                                 }
890                                 else
891                                         g_message ("TLS active");
892                         }
893                 }
894
895                 /* bind anonymously initially, we'll actually
896                    authenticate the user properly later (in
897                    authenticate_user) if they've selected
898                    authentication */
899                 ldap_error = ldap_simple_bind_s (blpriv->ldap, NULL, NULL);
900                 if (ldap_error == LDAP_PROTOCOL_ERROR) {
901                         g_warning ("failed to bind using v3.  trying v2.");
902                         /* server doesn't support v3 binds, so let's
903                            drop it down to v2 and try again. */
904                         bl->priv->ldap_v3 = FALSE;
905                         
906                         protocol_version = LDAP_VERSION2;
907                         ldap_set_option (blpriv->ldap, LDAP_OPT_PROTOCOL_VERSION, &protocol_version);
908
909                         ldap_error = ldap_simple_bind_s (blpriv->ldap, NULL, NULL);
910                 }
911
912                 if (ldap_error == LDAP_PROTOCOL_ERROR) {
913                         g_warning ("failed to bind using either v3 or v2 binds.");
914                         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
915                         return GNOME_Evolution_Addressbook_OtherError;
916                 }
917                 else if (ldap_error == LDAP_SERVER_DOWN) {
918                         /* we only want this to be fatal if the server is down. */
919                         g_warning ("failed to bind anonymously while connecting (ldap_error 0x%02x)", ldap_error);
920                         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
921                         return GNOME_Evolution_Addressbook_RepositoryOffline;
922                 }
923
924                 g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
925                 ldap_error = query_ldap_root_dse (bl);
926                 /* query_ldap_root_dse will cause the actual
927                    connect(), so any tcpip problems will show up
928                    here */
929
930                 /* we can't just check for LDAP_SUCCESS here since in
931                    older servers (namely openldap1.x servers), there's
932                    not a root DSE at all, so the query will fail with
933                    LDAP_NO_SUCH_OBJECT, and GWIA's LDAP server (which
934                    is v2 based and doesn't have a root dse) seems to
935                    fail with LDAP_PARTIAL_RESULTS. */
936                 if (ldap_error == LDAP_SUCCESS 
937                     || ldap_error == LDAP_PARTIAL_RESULTS
938                     || LDAP_NAME_ERROR (ldap_error)) {
939                         blpriv->connected = TRUE;
940
941                         /* check to see if evolutionPerson is supported, if we can (me
942                            might not be able to if we can't authenticate.  if we
943                            can't, try again in auth_user.) */
944                         if (!bl->priv->evolutionPersonChecked)
945                                 check_schema_support (bl);
946
947                         e_book_backend_set_is_loaded (E_BOOK_BACKEND (bl), TRUE);
948
949                         if (enable_debug) {
950                                 printf ("e_book_backend_ldap_connect ... success \n");
951                                 g_get_current_time (&end);
952                                 diff = end.tv_sec * 1000 + end.tv_usec/1000;
953                                 diff -= start.tv_sec * 1000 + start.tv_usec/1000;
954                                 printf("e_book_backend_ldap_connect took %ld.%03ld seconds\n",
955                                         diff/1000,diff%1000);
956                         }
957                         return GNOME_Evolution_Addressbook_Success;
958                 }
959                 else
960                         g_warning ("Failed to perform root dse query anonymously, (ldap_error 0x%02x)", ldap_error);
961         }
962         else {
963                 g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
964         }
965
966         g_warning ("e_book_backend_ldap_connect failed for "
967                    "'ldap://%s:%d/%s'\n",
968                    blpriv->ldap_host,
969                    blpriv->ldap_port,
970                    blpriv->ldap_rootdn ? blpriv->ldap_rootdn : "");
971         blpriv->connected = FALSE;
972         return GNOME_Evolution_Addressbook_RepositoryOffline;
973 }
974
975 static gboolean
976 e_book_backend_ldap_reconnect (EBookBackendLDAP *bl, EDataBookView *book_view, int ldap_status)
977 {
978         GTimeVal start, end;
979         unsigned long diff;
980
981         if (enable_debug) {
982                 printf ("e_book_backend_ldap_reconnect ... \n");
983                 g_get_current_time (&start);
984         }
985
986         if (!bl->priv->ldap) {
987                 if (enable_debug)
988                         printf ("e_book_backend_ldap_reconnect ... ldap handler is NULL\n");
989                 return FALSE;
990         }
991
992         /* we need to reconnect if we were previously connected */
993         if (bl->priv->connected && ldap_status == LDAP_SERVER_DOWN) {
994                 GNOME_Evolution_Addressbook_CallStatus status;
995                 int ldap_error = LDAP_SUCCESS;
996
997                 if (book_view)
998                         book_view_notify_status (book_view, _("Reconnecting to LDAP server..."));
999
1000                 status = e_book_backend_ldap_connect (bl);
1001
1002                 if (status != GNOME_Evolution_Addressbook_Success) {
1003                         if (book_view)
1004                                 book_view_notify_status (book_view, "");
1005                         if (enable_debug)
1006                                 printf ("e_book_backend_ldap_reconnect ... failed (server down?)\n");
1007                         return FALSE;
1008                 }
1009
1010                 if (bl->priv->auth_dn) {
1011                         g_static_rec_mutex_lock (&eds_ldap_handler_lock);
1012                         ldap_error = ldap_simple_bind_s(bl->priv->ldap,
1013                                                         bl->priv->auth_dn,
1014                                                         bl->priv->auth_passwd);
1015                         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
1016                 }
1017                 if (book_view)
1018                         book_view_notify_status (book_view, "");
1019
1020                 if (enable_debug) {
1021                         printf ("e_book_backend_ldap_reconnect ... returning %d\n", ldap_error);
1022                         g_get_current_time (&end);
1023                         diff = end.tv_sec * 1000 + end.tv_usec/1000;
1024                         diff -= start.tv_sec * 1000 + start.tv_usec/1000;
1025                         printf("e_book_backend_ldap_reconnect took %ld.%03ld seconds\n",
1026                                 diff/1000,diff%1000);
1027                 }
1028
1029                 return (ldap_error == LDAP_SUCCESS);
1030         }
1031         else {
1032                 return FALSE;
1033         }
1034 }
1035
1036 static void
1037 ldap_op_add (LDAPOp *op, EBookBackend *backend,
1038              EDataBook *book, EDataBookView *view,
1039              int opid, 
1040              int msgid,
1041              LDAPOpHandler handler, LDAPOpDtor dtor)
1042 {
1043         EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (backend);
1044
1045         op->backend = backend;
1046         op->book = book;
1047         op->view = view;
1048         op->opid = opid;
1049         op->id = msgid;
1050         op->handler = handler;
1051         op->dtor = dtor;
1052
1053         g_static_rec_mutex_lock (&bl->priv->op_hash_mutex);
1054         if (g_hash_table_lookup (bl->priv->id_to_op, &op->id)) {
1055                 g_warning ("conflicting ldap msgid's");
1056         }
1057
1058         g_hash_table_insert (bl->priv->id_to_op,
1059                              &op->id, op);
1060
1061         bl->priv->active_ops ++;
1062
1063         if (bl->priv->poll_timeout == -1) {
1064                 bl->priv->poll_timeout = g_timeout_add (LDAP_POLL_INTERVAL,
1065                                                         (GSourceFunc) poll_ldap,
1066                                                         bl);
1067         }
1068         g_static_rec_mutex_unlock (&bl->priv->op_hash_mutex);
1069 }
1070
1071 static void
1072 ldap_op_finished (LDAPOp *op)
1073 {
1074         EBookBackend *backend = op->backend;
1075         EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (backend);
1076
1077         g_static_rec_mutex_lock (&bl->priv->op_hash_mutex);
1078         g_hash_table_remove (bl->priv->id_to_op, &op->id);
1079
1080         /* should handle errors here */
1081         g_static_rec_mutex_lock (&eds_ldap_handler_lock);
1082         if (bl->priv->ldap)
1083                 ldap_abandon (bl->priv->ldap, op->id);
1084         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
1085         
1086         if (op->dtor)
1087                 op->dtor (op);
1088
1089         bl->priv->active_ops--;
1090
1091         if (bl->priv->active_ops == 0) {
1092                 if (bl->priv->poll_timeout != -1)
1093                         g_source_remove (bl->priv->poll_timeout);
1094                 bl->priv->poll_timeout = -1;
1095         }
1096         g_static_rec_mutex_unlock (&bl->priv->op_hash_mutex);
1097 }
1098
1099 static void
1100 ldap_op_change_id (LDAPOp *op, int msg_id)
1101 {
1102         EBookBackend *backend = op->backend;
1103         EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (backend);
1104
1105         g_static_rec_mutex_lock (&bl->priv->op_hash_mutex);
1106         g_hash_table_remove (bl->priv->id_to_op, &op->id);
1107
1108         op->id = msg_id;
1109
1110         g_hash_table_insert (bl->priv->id_to_op,
1111                              &op->id, op);
1112         g_static_rec_mutex_unlock (&bl->priv->op_hash_mutex);
1113 }
1114
1115 static int
1116 ldap_error_to_response (int ldap_error)
1117 {
1118         if (ldap_error == LDAP_SUCCESS)
1119                 return GNOME_Evolution_Addressbook_Success;
1120         else if (ldap_error == LDAP_INVALID_DN_SYNTAX)
1121                 return GNOME_Evolution_Addressbook_OtherError;
1122         else if (LDAP_NAME_ERROR (ldap_error))
1123                 return GNOME_Evolution_Addressbook_ContactNotFound;
1124         else if (ldap_error == LDAP_INSUFFICIENT_ACCESS)
1125                 return GNOME_Evolution_Addressbook_PermissionDenied;
1126         else if (ldap_error == LDAP_SERVER_DOWN)
1127                 return GNOME_Evolution_Addressbook_RepositoryOffline;
1128         else if (ldap_error == LDAP_ALREADY_EXISTS)
1129                 return GNOME_Evolution_Addressbook_ContactIdAlreadyExists;
1130         else
1131                 return GNOME_Evolution_Addressbook_OtherError;
1132 }
1133
1134 \f
1135 static char *
1136 create_dn_from_contact (EContact *contact, const char *root_dn)
1137 {
1138         char *cn, *cn_part = NULL;
1139         char *dn;
1140
1141         cn = e_contact_get (contact, E_CONTACT_FULL_NAME);
1142         if (cn) {
1143                 if (strchr (cn, ',')) {
1144                         /* need to escape commas */
1145                         char *new_cn = g_malloc0 (strlen (cn) * 3 + 1);
1146                         int i, j;
1147
1148                         for (i = 0, j = 0; i < strlen (cn); i ++) {
1149                                 if (cn[i] == ',') {
1150                                         sprintf (new_cn + j, "%%%02X", cn[i]);
1151                                         j += 3;
1152                                 }
1153                                 else {
1154                                         new_cn[j++] = cn[i];
1155                                 }
1156                         }
1157                         cn_part = g_strdup_printf ("cn=%s", new_cn);
1158                         g_free (new_cn);
1159                 }
1160                 else {
1161                         cn_part = g_strdup_printf ("cn=%s", cn);
1162                 }
1163         }
1164         else {
1165                 cn_part = g_strdup ("");
1166         }
1167
1168         dn = g_strdup_printf ("%s%s%s", cn_part,
1169                               (root_dn && strlen(root_dn)) ? "," : "",
1170                               (root_dn && strlen(root_dn)) ? root_dn: "");
1171
1172         g_free (cn_part);
1173
1174         g_print ("generated dn: %s\n", dn);
1175
1176         return dn;
1177 }
1178
1179 static void
1180 free_mods (GPtrArray *mods)
1181 {
1182         int i = 0;
1183         LDAPMod *mod;
1184
1185         while ((mod = g_ptr_array_index (mods, i++))) {
1186                 int j;
1187                 g_free (mod->mod_type);
1188
1189                 if (mod->mod_op & LDAP_MOD_BVALUES && mod->mod_bvalues) {
1190                         for (j = 0; mod->mod_bvalues[j]; j++) {
1191                                 g_free (mod->mod_bvalues[j]->bv_val);
1192                                 g_free (mod->mod_bvalues[j]);
1193                         }
1194                 }
1195                 else if (mod->mod_values) {
1196                         for (j = 0; mod->mod_values[j]; j++)
1197                                 g_free (mod->mod_values[j]);
1198                 }
1199                 g_free (mod);
1200         }
1201
1202         g_ptr_array_free (mods, TRUE);
1203 }
1204
1205 static GPtrArray*
1206 build_mods_from_contacts (EBookBackendLDAP *bl, EContact *current, EContact *new, gboolean *new_dn_needed)
1207 {
1208         gboolean adding = (current == NULL), is_list = FALSE;
1209         GPtrArray *result = g_ptr_array_new();
1210         int i;
1211
1212         if (new_dn_needed)
1213                 *new_dn_needed = FALSE;
1214         if (e_contact_get (new, E_CONTACT_IS_LIST))
1215                 is_list = TRUE;
1216
1217         /* we walk down the list of properties we can deal with (that
1218          big table at the top of the file) */
1219
1220         for (i = 0; i < num_prop_infos; i ++) {
1221                 gboolean include;
1222                 gboolean new_prop_present = FALSE;
1223                 gboolean current_prop_present = FALSE;
1224                 struct berval** new_prop_bers = NULL;
1225                 char *new_prop = NULL;
1226                 char *current_prop = NULL;
1227
1228                 /* XXX if it's an evolutionPerson prop and the ldap
1229                    server doesn't support that objectclass, skip it. */
1230                 if (prop_info[i].prop_type & PROP_EVOLVE ) {
1231                         if (!bl->priv->evolutionPersonSupported)
1232                                 continue;
1233                         if (is_list)
1234                                 continue;
1235                 }
1236                 if (((prop_info[i].prop_type & PROP_TYPE_COMPLEX) ||
1237                      (prop_info[i].prop_type & PROP_TYPE_BINARY))  && is_list) {
1238                         continue;
1239                 }
1240
1241                 /* get the value for the new contact, and compare it to
1242                    the value in the current contact to see if we should
1243                    update it -- if adding is TRUE, short circuit the
1244                    check. */
1245                 if (prop_info[i].prop_type & PROP_TYPE_STRING) {
1246                         new_prop = e_contact_get (new, prop_info[i].field_id);
1247                         new_prop_present = (new_prop != NULL);
1248                 }
1249                 else {
1250                         new_prop_bers = prop_info[i].ber_func ? prop_info[i].ber_func (new) : NULL;
1251                         new_prop_present = (new_prop_bers != NULL);
1252                 }
1253
1254                 /* need to set INCLUDE to true if the field needs to
1255                    show up in the ldap modify request */
1256                 if (adding) {
1257                         /* if we're creating a new contact, include it if the
1258                            field is there at all */
1259                         if (prop_info[i].prop_type & PROP_TYPE_STRING)
1260                                 include = (new_prop_present && *new_prop); /* empty strings cause problems */
1261                         else
1262                                 include = new_prop_present;
1263                 }
1264                 else {
1265                         /* if we're modifying an existing contact,
1266                            include it if the current field value is
1267                            different than the new one, if it didn't
1268                            exist previously, or if it's been
1269                            removed. */
1270                         if (prop_info[i].prop_type & PROP_TYPE_STRING) {
1271                                 current_prop = e_contact_get (current, prop_info[i].field_id);
1272                                 current_prop_present = (current_prop != NULL);
1273
1274                                 if (new_prop && current_prop)
1275                                         include = *new_prop && strcmp (new_prop, current_prop);
1276                                 else
1277                                         include = (new_prop != current_prop) && (!new_prop || *new_prop); /* empty strings cause problems */
1278                         }
1279                         else {
1280                                 int j;
1281                                 struct berval **current_prop_bers = prop_info[i].ber_func ? prop_info[i].ber_func (current) : NULL;
1282
1283                                 current_prop_present = (current_prop_bers != NULL);
1284
1285                                 /* free up the current_prop_bers */
1286                                 if (current_prop_bers) {
1287                                         for (j = 0; current_prop_bers[j]; j++) {
1288                                                 g_free (current_prop_bers[j]->bv_val);
1289                                                 g_free (current_prop_bers[j]);
1290                                         }
1291                                         g_free (current_prop_bers);
1292                                 }
1293
1294                                 include = prop_info[i].compare_func ? !prop_info[i].compare_func (new, current) : FALSE;
1295                         }
1296                 }
1297
1298                 if (include) {
1299                         LDAPMod *mod = g_new (LDAPMod, 1);
1300
1301                         /* the included attribute has changed - we
1302                            need to update the dn if it's one of the
1303                            attributes we compute the dn from. */
1304                         if (new_dn_needed)
1305                                 *new_dn_needed |= prop_info[i].prop_type & PROP_DN;
1306
1307                         if (adding) {
1308                                 mod->mod_op = LDAP_MOD_ADD;
1309                         }
1310                         else {
1311                                 if (!new_prop_present)
1312                                         mod->mod_op = LDAP_MOD_DELETE;
1313                                 else if (!current_prop_present)
1314                                         mod->mod_op = LDAP_MOD_ADD;
1315                                 else
1316                                         mod->mod_op = LDAP_MOD_REPLACE;
1317                         }
1318                         
1319                         mod->mod_type = g_strdup (prop_info[i].ldap_attr);
1320
1321                         if (prop_info[i].prop_type & PROP_TYPE_STRING) {
1322                                 mod->mod_values = g_new (char*, 2);
1323                                 mod->mod_values[0] = new_prop;
1324                                 mod->mod_values[1] = NULL;
1325                         }
1326                         else { /* PROP_TYPE_COMPLEX/PROP_TYPE_GROUP */
1327                                 mod->mod_op |= LDAP_MOD_BVALUES;
1328                                 mod->mod_bvalues = new_prop_bers;
1329                         }
1330
1331                         g_ptr_array_add (result, mod);
1332                 }
1333         }
1334
1335         /* NULL terminate the list of modifications */
1336         g_ptr_array_add (result, NULL);
1337         return result;
1338 }
1339
1340 static void
1341 add_objectclass_mod (EBookBackendLDAP *bl, GPtrArray *mod_array, GList *existing_objectclasses, gboolean is_list)
1342 {
1343 #define FIND_INSERT(oc) \
1344         if (!g_list_find_custom (existing_objectclasses, (oc), (GCompareFunc)g_ascii_strcasecmp)) \
1345                  g_ptr_array_add (objectclasses, g_strdup ((oc)))
1346 #define INSERT(oc) \
1347                  g_ptr_array_add (objectclasses, g_strdup ((oc)))
1348
1349         LDAPMod *objectclass_mod;
1350         GPtrArray *objectclasses = g_ptr_array_new();
1351
1352         if (existing_objectclasses) {
1353                 objectclass_mod = g_new (LDAPMod, 1);
1354                 objectclass_mod->mod_op = LDAP_MOD_ADD;
1355                 objectclass_mod->mod_type = g_strdup ("objectClass");
1356
1357                 /* yes, this is a linear search for each of our
1358                    objectclasses, but really, how many objectclasses
1359                    are there going to be in any sane ldap entry? */
1360                 FIND_INSERT (TOP);
1361                 if (is_list) {
1362                         FIND_INSERT (GROUPOFNAMES);
1363                 }
1364                 else {
1365                         FIND_INSERT (PERSON);
1366                         FIND_INSERT (ORGANIZATIONALPERSON);
1367                         FIND_INSERT (INETORGPERSON);
1368                         if (bl->priv->calEntrySupported)
1369                                 FIND_INSERT (CALENTRY);
1370                         if (bl->priv->evolutionPersonSupported)
1371                                 FIND_INSERT (EVOLUTIONPERSON);
1372                 }
1373
1374                 if (objectclasses->len) {
1375                         g_ptr_array_add (objectclasses, NULL);
1376                         objectclass_mod->mod_values = (char**)objectclasses->pdata;
1377                         g_ptr_array_add (mod_array, objectclass_mod);
1378                         g_ptr_array_free (objectclasses, FALSE);
1379                 }
1380                 else {
1381                         g_ptr_array_free (objectclasses, TRUE);
1382                         g_free (objectclass_mod->mod_type);
1383                         g_free (objectclass_mod);
1384                 }
1385         }
1386         else {
1387                 objectclass_mod = g_new (LDAPMod, 1);
1388                 objectclass_mod->mod_op = LDAP_MOD_ADD;
1389                 objectclass_mod->mod_type = g_strdup ("objectClass");
1390
1391                 INSERT(TOP);
1392                 if (is_list) {
1393                         INSERT(GROUPOFNAMES);
1394                 }
1395                 else {
1396                         INSERT(PERSON);
1397                         INSERT(ORGANIZATIONALPERSON);
1398                         INSERT(INETORGPERSON);
1399                         if (bl->priv->calEntrySupported)
1400                                 INSERT(CALENTRY);
1401                         if (bl->priv->evolutionPersonSupported)
1402                                 INSERT(EVOLUTIONPERSON);
1403                 }
1404                 g_ptr_array_add (objectclasses, NULL);
1405                 objectclass_mod->mod_values = (char**)objectclasses->pdata;
1406                 g_ptr_array_add (mod_array, objectclass_mod);
1407                 g_ptr_array_free (objectclasses, FALSE);
1408         }
1409 }
1410
1411 typedef struct {
1412         LDAPOp op;
1413         char *dn;
1414         EContact *new_contact;
1415 } LDAPCreateOp;
1416
1417 static void
1418 create_contact_handler (LDAPOp *op, LDAPMessage *res)
1419 {
1420         LDAPCreateOp *create_op = (LDAPCreateOp*)op;
1421         EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (op->backend);
1422         LDAP *ldap;
1423         char *ldap_error_msg;
1424         int ldap_error;
1425         int response;
1426
1427         ldap = bl->priv->ldap;
1428         if (!ldap) {
1429                 e_data_book_respond_create (op->book,
1430                                             op->opid,
1431                                             GNOME_Evolution_Addressbook_OtherError,
1432                                             NULL);
1433                 ldap_op_finished (op);
1434                 return;
1435         }
1436
1437         if (LDAP_RES_ADD != ldap_msgtype (res)) {
1438                 g_warning ("incorrect msg type %d passed to create_contact_handler", ldap_msgtype (res));
1439                 e_data_book_respond_create (op->book,
1440                                             op->opid,
1441                                             GNOME_Evolution_Addressbook_OtherError,
1442                                             NULL);
1443                 ldap_op_finished (op);
1444                 return;
1445         }
1446
1447         g_static_rec_mutex_lock (&eds_ldap_handler_lock);
1448         ldap_parse_result (ldap, res, &ldap_error,
1449                            NULL, &ldap_error_msg, NULL, NULL, 0);
1450         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
1451         if (ldap_error != LDAP_SUCCESS) {
1452                 g_warning ("create_contact_handler: %02X (%s), additional info: %s",
1453                            ldap_error,
1454                            ldap_err2string (ldap_error), ldap_error_msg);
1455         } else {
1456                 if (bl->priv->cache)
1457                         e_book_backend_cache_add_contact (bl->priv->cache, create_op->new_contact);
1458         }
1459         ldap_memfree (ldap_error_msg);
1460
1461         /* and lastly respond */
1462         response = ldap_error_to_response (ldap_error);
1463         e_data_book_respond_create (op->book,
1464                                     op->opid,
1465                                     response,
1466                                     create_op->new_contact);
1467
1468         ldap_op_finished (op);
1469 }
1470
1471 static void
1472 create_contact_dtor (LDAPOp *op)
1473 {
1474         LDAPCreateOp *create_op = (LDAPCreateOp*)op;
1475
1476         g_free (create_op->dn);
1477         g_object_unref (create_op->new_contact);
1478         g_free (create_op);
1479 }
1480
1481 static void
1482 e_book_backend_ldap_create_contact (EBookBackend *backend,
1483                                     EDataBook    *book,
1484                                     guint32       opid,
1485                                     const char   *vcard)
1486 {
1487         LDAPCreateOp *create_op = g_new (LDAPCreateOp, 1);
1488         EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (backend);
1489         EDataBookView *book_view;
1490         int create_contact_msgid;
1491         int response;
1492         int err;
1493         GPtrArray *mod_array;
1494         LDAPMod **ldap_mods;
1495         LDAP *ldap;
1496
1497         
1498         switch (bl->priv->mode) {
1499
1500         case GNOME_Evolution_Addressbook_MODE_LOCAL :
1501                 e_data_book_respond_create(book, opid, GNOME_Evolution_Addressbook_RepositoryOffline, NULL);
1502                 return;
1503         case GNOME_Evolution_Addressbook_MODE_REMOTE : 
1504
1505                 g_static_rec_mutex_lock (&eds_ldap_handler_lock);
1506                 if (!bl->priv->ldap) {
1507                         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
1508                         e_data_book_respond_create (book, opid, GNOME_Evolution_Addressbook_OtherError, NULL);
1509                         return;
1510                 }
1511                 g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
1512
1513                 book_view = find_book_view (bl);
1514
1515                 printf ("Create Contact: vcard = %s\n", vcard);
1516                 
1517                 create_op->new_contact = e_contact_new_from_vcard (vcard);
1518         
1519                 create_op->dn = create_dn_from_contact (create_op->new_contact, bl->priv->ldap_rootdn);
1520                 e_contact_set (create_op->new_contact, E_CONTACT_UID, create_op->dn);
1521                 
1522                 /* build our mods */
1523                 mod_array = build_mods_from_contacts (bl, NULL, create_op->new_contact, NULL);
1524                 
1525 #if 0
1526                 if (!mod_array) {
1527                         /* there's an illegal field in there.  report
1528                            UnsupportedAttribute back */
1529                         e_data_book_respond_create (book,
1530                                                     GNOME_Evolution_Addressbook_BookListener_UnsupportedField,
1531                                                     NULL);
1532                         
1533                         g_free (create_op->dn);
1534                         g_object_unref (create_op->new_contact);
1535                         g_free (create_op);
1536                         return;
1537                 }
1538 #endif
1539                 
1540                 /* remove the NULL at the end */
1541                 g_ptr_array_remove (mod_array, NULL);
1542                 
1543                 /* add our objectclass(es) */
1544                 if (e_contact_get (create_op->new_contact, E_CONTACT_IS_LIST))
1545                         add_objectclass_mod (bl, mod_array, NULL, TRUE);
1546                 else
1547                         add_objectclass_mod (bl, mod_array, NULL, FALSE);
1548                 
1549                 /* then put the NULL back */
1550                 g_ptr_array_add (mod_array, NULL);
1551                 
1552 #ifdef LDAP_DEBUG_ADD
1553                 {
1554                         int i;
1555                         printf ("Sending the following to the server as ADD\n");
1556                         
1557                         for (i = 0; g_ptr_array_index(mod_array, i); i ++) {
1558                                 LDAPMod *mod = g_ptr_array_index(mod_array, i);
1559                                 if (mod->mod_op & LDAP_MOD_DELETE)
1560                                         printf ("del ");
1561                                 else if (mod->mod_op & LDAP_MOD_REPLACE)
1562                                         printf ("rep ");
1563                                 else
1564                                         printf ("add ");
1565                                 if (mod->mod_op & LDAP_MOD_BVALUES)
1566                                         printf ("ber ");
1567                                 else
1568                                         printf ("    ");
1569                         
1570                                 printf (" %s:\n", mod->mod_type);
1571                         
1572                                 if (mod->mod_op & LDAP_MOD_BVALUES) {
1573                                         int j;
1574                                         for (j = 0; mod->mod_bvalues[j] && mod->mod_bvalues[j]->bv_val; j++)
1575                                                 printf ("\t\t'%s'\n", mod->mod_bvalues[j]->bv_val);
1576                                 }
1577                                 else {
1578                                         int j;
1579                                 
1580                                         for (j = 0; mod->mod_values[j]; j++)
1581                                                 printf ("\t\t'%s'\n", mod->mod_values[j]);
1582                                 }
1583                         }
1584                 }
1585 #endif
1586         
1587                 ldap_mods = (LDAPMod**)mod_array->pdata;
1588
1589                 ldap = bl->priv->ldap;
1590                 
1591                 do {
1592                         book_view_notify_status (book_view, _("Adding contact to LDAP server..."));
1593                         g_static_rec_mutex_lock (&eds_ldap_handler_lock);       
1594                         err = ldap_add_ext (ldap, create_op->dn, ldap_mods,
1595                                             NULL, NULL, &create_contact_msgid);
1596                         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
1597                 
1598                 } while (e_book_backend_ldap_reconnect (bl, book_view, err));
1599         
1600                 /* and clean up */
1601                 free_mods (mod_array);
1602         
1603                 if (LDAP_SUCCESS != err) {
1604                         response = ldap_error_to_response (err);
1605                         e_data_book_respond_create (create_op->op.book,
1606                                                     opid,
1607                                                     response,
1608                                                     NULL);
1609                         create_contact_dtor ((LDAPOp*)create_op);
1610                         return;
1611                 }
1612                 else {
1613                         g_print ("ldap_add_ext returned %d\n", err);
1614                         ldap_op_add ((LDAPOp*)create_op, backend, book,
1615                                      book_view, opid, create_contact_msgid,
1616                                      create_contact_handler, create_contact_dtor);
1617                 }
1618         }
1619 }
1620         
1621
1622 typedef struct {
1623         LDAPOp op;
1624         char *id;
1625 } LDAPRemoveOp;
1626
1627 static void
1628 remove_contact_handler (LDAPOp *op, LDAPMessage *res)
1629 {
1630         LDAPRemoveOp *remove_op = (LDAPRemoveOp*)op;
1631         EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (op->backend);
1632         char *ldap_error_msg;
1633         int ldap_error;
1634         GList *ids = NULL;
1635
1636         if (!bl->priv->ldap) {
1637                 e_data_book_respond_remove_contacts (op->book, op->opid, GNOME_Evolution_Addressbook_OtherError, NULL);
1638                 ldap_op_finished (op);
1639                 return;
1640         }
1641
1642         if (LDAP_RES_DELETE != ldap_msgtype (res)) {
1643                 g_warning ("incorrect msg type %d passed to remove_contact_handler", ldap_msgtype (res));
1644                 e_data_book_respond_remove_contacts (op->book,
1645                                                      op->opid,
1646                                                      GNOME_Evolution_Addressbook_OtherError,
1647                                                      NULL);
1648                 ldap_op_finished (op);
1649                 return;
1650         }
1651
1652         g_static_rec_mutex_lock (&eds_ldap_handler_lock);
1653         ldap_parse_result (bl->priv->ldap, res, &ldap_error,
1654                            NULL, &ldap_error_msg, NULL, NULL, 0);
1655         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
1656         if (ldap_error != LDAP_SUCCESS) {
1657                 g_warning ("remove_contact_handler: %02X (%s), additional info: %s",
1658                            ldap_error,
1659                            ldap_err2string (ldap_error), ldap_error_msg);
1660         } else {
1661                 /* Remove from cache too */
1662                 if (bl->priv->cache)
1663                         e_book_backend_cache_remove_contact (bl->priv->cache, remove_op->id);
1664         }
1665
1666         ldap_memfree (ldap_error_msg);
1667
1668         ids = g_list_append (ids, remove_op->id);
1669         e_data_book_respond_remove_contacts (remove_op->op.book,
1670                                              op->opid,
1671                                              ldap_error_to_response (ldap_error),
1672                                              ids);
1673         g_list_free (ids);
1674 }
1675
1676 static void
1677 remove_contact_dtor (LDAPOp *op)
1678 {
1679         LDAPRemoveOp *remove_op = (LDAPRemoveOp*)op;
1680
1681         g_free (remove_op->id);
1682         g_free (remove_op);
1683 }
1684
1685 static void
1686 e_book_backend_ldap_remove_contacts (EBookBackend *backend,
1687                                      EDataBook    *book,
1688                                      guint32       opid,
1689                                      GList        *ids)
1690 {
1691         LDAPRemoveOp *remove_op = g_new (LDAPRemoveOp, 1);
1692         EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (backend);
1693         EDataBookView *book_view;
1694         int remove_msgid;
1695         int ldap_error;
1696
1697         switch (bl->priv->mode) {
1698
1699         case GNOME_Evolution_Addressbook_MODE_LOCAL :
1700                 e_data_book_respond_remove_contacts (book, opid, GNOME_Evolution_Addressbook_RepositoryOffline, NULL);
1701                 g_free (remove_op);
1702                 return;
1703         case GNOME_Evolution_Addressbook_MODE_REMOTE : 
1704                 if (!bl->priv->ldap) {
1705                         e_data_book_respond_remove_contacts (book, opid, GNOME_Evolution_Addressbook_OtherError, NULL);
1706                         g_free (remove_op);
1707                         return;
1708                 }
1709
1710                 book_view = find_book_view (bl);
1711
1712                 /*
1713                 ** since we didn't pass "bulk-removes" in our static
1714                 ** capabilities, we should only get 1 length lists here, so
1715                 ** the id we're deleting is the first and only id in the list.
1716                 */
1717                 remove_op->id = g_strdup (ids->data);
1718                 
1719                 do {
1720                         book_view_notify_status (book_view, _("Removing contact from LDAP server..."));
1721                 
1722                         g_static_rec_mutex_lock (&eds_ldap_handler_lock);       
1723                         ldap_error = ldap_delete_ext (bl->priv->ldap,
1724                                                       remove_op->id,
1725                                                       NULL, NULL, &remove_msgid);
1726                         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
1727                 } while (e_book_backend_ldap_reconnect (bl, book_view, ldap_error));
1728                 
1729                 if (ldap_error != LDAP_SUCCESS) {
1730                         e_data_book_respond_remove_contacts (remove_op->op.book,
1731                                                              opid,
1732                                                              ldap_error_to_response (ldap_error),
1733                                                              NULL);
1734                         remove_contact_dtor ((LDAPOp*)remove_op);
1735                         return;
1736                 }
1737                 else {
1738                         g_print ("ldap_delete_ext returned %d\n", ldap_error);
1739                         ldap_op_add ((LDAPOp*)remove_op, backend, book,
1740                                      book_view, opid, remove_msgid,
1741                                      remove_contact_handler, remove_contact_dtor);
1742                 }
1743                 break;
1744         }
1745         
1746 }
1747         
1748 /*
1749 ** MODIFY
1750 **
1751 ** The modification request is actually composed of 2 separate
1752 ** requests.  Since we need to get a list of theexisting objectclasses
1753 ** used by the ldap server for the entry, and since the UI only sends
1754 ** us the current contact, we need to query the ldap server for the
1755 ** existing contact.
1756 **
1757 */
1758
1759 typedef struct {
1760         LDAPOp op;
1761         const char *id; /* the id of the contact we're modifying */
1762         EContact *current_contact;
1763         EContact *contact;
1764         GList *existing_objectclasses;
1765 } LDAPModifyOp;
1766
1767 static void
1768 modify_contact_modify_handler (LDAPOp *op, LDAPMessage *res)
1769 {
1770         LDAPModifyOp *modify_op = (LDAPModifyOp*)op;
1771         EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (op->backend);
1772         LDAP *ldap;
1773         char *ldap_error_msg;
1774         int ldap_error;
1775
1776         ldap = bl->priv->ldap;
1777         if (!ldap) {
1778                 e_data_book_respond_modify (op->book,
1779                                             op->opid,
1780                                             GNOME_Evolution_Addressbook_OtherError,
1781                                             NULL);
1782                 ldap_op_finished (op);
1783                 return;
1784         }
1785
1786         if (LDAP_RES_MODIFY != ldap_msgtype (res)) {
1787                 g_warning ("incorrect msg type %d passed to modify_contact_handler", ldap_msgtype (res));
1788                 e_data_book_respond_modify (op->book,
1789                                             op->opid,
1790                                             GNOME_Evolution_Addressbook_OtherError,
1791                                             NULL);
1792                 ldap_op_finished (op);
1793                 return;
1794         }
1795
1796         g_static_rec_mutex_lock (&eds_ldap_handler_lock);
1797         ldap_parse_result (ldap, res, &ldap_error,
1798                            NULL, &ldap_error_msg, NULL, NULL, 0);
1799         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
1800         if (ldap_error != LDAP_SUCCESS) {
1801                 g_warning ("modify_contact_handler: %02X (%s), additional info: %s",
1802                            ldap_error,
1803                            ldap_err2string (ldap_error), ldap_error_msg);
1804         } else {
1805                 if (bl->priv->cache)
1806                         e_book_backend_cache_add_contact (bl->priv->cache, modify_op->contact);
1807         }
1808         ldap_memfree (ldap_error_msg);
1809
1810         /* and lastly respond */
1811         e_data_book_respond_modify (op->book,
1812                                     op->opid,
1813                                     ldap_error_to_response (ldap_error),
1814                                     modify_op->contact);
1815         ldap_op_finished (op);
1816 }
1817
1818 static void
1819 modify_contact_search_handler (LDAPOp *op, LDAPMessage *res)
1820 {
1821         LDAPModifyOp *modify_op = (LDAPModifyOp*)op;
1822         EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (op->backend);
1823         LDAP *ldap;
1824         int msg_type;
1825
1826         ldap = bl->priv->ldap;
1827         if (!ldap) {
1828                 e_data_book_respond_modify (op->book, op->opid,
1829                                             GNOME_Evolution_Addressbook_OtherError, NULL);
1830                 ldap_op_finished (op);
1831                 return;
1832         }
1833
1834         /* if it's successful, we should get called with a
1835            RES_SEARCH_ENTRY and a RES_SEARCH_RESULT.  if it's
1836            unsuccessful, we should only see a RES_SEARCH_RESULT */
1837
1838         msg_type = ldap_msgtype (res);
1839         if (msg_type == LDAP_RES_SEARCH_ENTRY) {
1840                 LDAPMessage *e = ldap_first_entry(ldap, res);
1841
1842                 if (!e) {
1843                         g_warning ("uh, this shouldn't happen");
1844                         e_data_book_respond_modify (op->book,
1845                                                     op->opid,
1846                                                     GNOME_Evolution_Addressbook_OtherError,
1847                                                     NULL);
1848                         ldap_op_finished (op);
1849                         return;
1850                 }
1851
1852                 modify_op->current_contact = build_contact_from_entry (bl, e,
1853                                                                        &modify_op->existing_objectclasses);
1854         }
1855         else if (msg_type == LDAP_RES_SEARCH_RESULT) {
1856                 char *ldap_error_msg;
1857                 int ldap_error;
1858                 LDAPMod **ldap_mods;
1859                 GPtrArray *mod_array;
1860                 gboolean differences;
1861                 gboolean need_new_dn;
1862                 int modify_contact_msgid;
1863
1864                 /* grab the result code, and set up the actual modify
1865                    if it was successful */
1866                 g_static_rec_mutex_lock (&eds_ldap_handler_lock);
1867                 ldap_parse_result (bl->priv->ldap, res, &ldap_error,
1868                                    NULL, &ldap_error_msg, NULL, NULL, 0);
1869                 g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
1870                 if (ldap_error != LDAP_SUCCESS) {
1871                         g_warning ("modify_contact_search_handler: %02X (%s), additional info: %s",
1872                                    ldap_error,
1873                                    ldap_err2string (ldap_error), ldap_error_msg);
1874                 }
1875                 ldap_memfree (ldap_error_msg);
1876
1877                 if (ldap_error != LDAP_SUCCESS) {
1878                         /* more here i'm sure */
1879                         e_data_book_respond_modify (op->book,
1880                                                     op->opid,
1881                                                     ldap_error_to_response (ldap_error),
1882                                                     NULL);
1883                         ldap_op_finished (op);
1884                         return;
1885                 }
1886
1887                 /* build our mods */
1888                 mod_array = build_mods_from_contacts (bl, modify_op->current_contact, modify_op->contact, &need_new_dn);
1889                 differences = mod_array->len > 0;
1890
1891                 if (differences) {
1892                         /* remove the NULL at the end */
1893                         g_ptr_array_remove (mod_array, NULL);
1894
1895                         /* add our objectclass(es), making sure
1896                            evolutionPerson is there if it's supported */
1897                         if (e_contact_get (modify_op->current_contact, E_CONTACT_IS_LIST))
1898                                 add_objectclass_mod (bl, mod_array, modify_op->existing_objectclasses, TRUE);
1899                         else
1900                                 add_objectclass_mod (bl, mod_array, modify_op->existing_objectclasses, FALSE);
1901
1902                         /* then put the NULL back */
1903                         g_ptr_array_add (mod_array, NULL);
1904
1905                         ldap_mods = (LDAPMod**)mod_array->pdata;
1906
1907 #ifdef LDAP_DEBUG_MODIFY
1908                         {
1909                                 int i;
1910                                 printf ("Sending the following to the server as MOD\n");
1911                         
1912                                 for (i = 0; g_ptr_array_index(mod_array, i); i ++) {
1913                                         LDAPMod *mod = g_ptr_array_index(mod_array, i);
1914                                         if (mod->mod_op & LDAP_MOD_DELETE)
1915                                                 printf ("del ");
1916                                         else if (mod->mod_op & LDAP_MOD_REPLACE)
1917                                                 printf ("rep ");
1918                                         else
1919                                                 printf ("add ");
1920                                         if (mod->mod_op & LDAP_MOD_BVALUES)
1921                                                 printf ("ber ");
1922                                         else
1923                                                 printf ("    ");
1924                         
1925                                         printf (" %s:\n", mod->mod_type);
1926                         
1927                                         if (mod->mod_op & LDAP_MOD_BVALUES) {
1928                                                 int j;
1929                                                 for (j = 0; mod->mod_bvalues[j] && mod->mod_bvalues[j]->bv_val; j++)
1930                                                         printf ("\t\t'%s'\n", mod->mod_bvalues[j]->bv_val);
1931                                         }
1932                                         else {
1933                                                 int j;
1934                                                 for (j = 0; mod->mod_values[j]; j++)
1935                                                         printf ("\t\t'%s'\n", mod->mod_values[j]);
1936                                         }
1937                                 }
1938                         }
1939 #endif
1940                         /* actually perform the ldap modify */
1941                         g_static_rec_mutex_lock (&eds_ldap_handler_lock);
1942                         ldap_error = ldap_modify_ext (ldap, modify_op->id, ldap_mods,
1943                                                       NULL, NULL, &modify_contact_msgid);
1944                         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
1945
1946                         if (ldap_error == LDAP_SUCCESS) {
1947                                 op->handler = modify_contact_modify_handler;
1948                                 ldap_op_change_id ((LDAPOp*)modify_op,
1949                                                    modify_contact_msgid);
1950                         }
1951                         else {
1952                                 g_warning ("ldap_modify_ext returned %d\n", ldap_error);
1953                                 e_data_book_respond_modify (op->book,
1954                                                             op->opid,
1955                                                             ldap_error_to_response (ldap_error),
1956                                                             NULL);
1957                                 ldap_op_finished (op);
1958                                 free_mods (mod_array);
1959                                 return;
1960                         }
1961                 }
1962                 
1963                 /* and clean up */
1964                 free_mods (mod_array);
1965         }
1966         else {
1967                 g_warning ("unhandled result type %d returned", msg_type);
1968                 e_data_book_respond_modify (op->book,
1969                                             op->opid,
1970                                             GNOME_Evolution_Addressbook_OtherError,
1971                                             NULL);
1972                 ldap_op_finished (op);
1973         }
1974 }
1975
1976 static void
1977 modify_contact_dtor (LDAPOp *op)
1978 {
1979         LDAPModifyOp *modify_op = (LDAPModifyOp*)op;
1980
1981         g_list_foreach (modify_op->existing_objectclasses, (GFunc)g_free, NULL);
1982         g_list_free (modify_op->existing_objectclasses);
1983         if (modify_op->current_contact)
1984                 g_object_unref (modify_op->current_contact);
1985         if (modify_op->contact)
1986                 g_object_unref (modify_op->contact);
1987         g_free (modify_op);
1988 }
1989
1990 static void
1991 e_book_backend_ldap_modify_contact (EBookBackend *backend,
1992                                     EDataBook    *book,
1993                                     guint32       opid,
1994                                     const char   *vcard)
1995 {
1996         LDAPModifyOp *modify_op = g_new0 (LDAPModifyOp, 1);
1997         EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (backend);
1998         int ldap_error;
1999         LDAP *ldap;
2000         int modify_contact_msgid;
2001         EDataBookView *book_view;
2002
2003
2004         switch (bl->priv->mode) {
2005
2006         case GNOME_Evolution_Addressbook_MODE_LOCAL :
2007                 e_data_book_respond_modify(book, opid, GNOME_Evolution_Addressbook_RepositoryOffline, NULL);
2008                 return;
2009         case GNOME_Evolution_Addressbook_MODE_REMOTE :
2010                 if (!bl->priv->ldap) {
2011                         e_data_book_respond_modify (book, opid, GNOME_Evolution_Addressbook_OtherError, NULL);
2012                         g_free (modify_op);
2013                         return;
2014                 }
2015
2016                 book_view = find_book_view (bl);
2017
2018                 printf ("Modify Contact: vcard = %s\n", vcard);
2019                 modify_op->contact = e_contact_new_from_vcard (vcard);
2020                 modify_op->id = e_contact_get_const (modify_op->contact, E_CONTACT_UID);
2021
2022                 ldap = bl->priv->ldap;
2023
2024                 do {
2025                         book_view_notify_status (book_view, _("Modifying contact from LDAP server..."));
2026
2027                         g_static_rec_mutex_lock (&eds_ldap_handler_lock);
2028                         ldap_error = ldap_search_ext (ldap, modify_op->id,
2029                                                       LDAP_SCOPE_BASE,
2030                                                       "(objectclass=*)",
2031                                                       NULL, 0, NULL, NULL,
2032                                                       NULL, /* XXX timeout */
2033                                                       1, &modify_contact_msgid);
2034                         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
2035
2036                 } while (e_book_backend_ldap_reconnect (bl, book_view, ldap_error));
2037
2038                 if (ldap_error == LDAP_SUCCESS) {
2039                         ldap_op_add ((LDAPOp*)modify_op, backend, book,
2040                                      book_view, opid, modify_contact_msgid,
2041                                      modify_contact_search_handler, modify_contact_dtor);
2042                 }
2043                 else {
2044                         g_warning ("ldap_search_ext returned %d\n", ldap_error);
2045                         e_data_book_respond_modify (book,
2046                                                     opid,
2047                                                     GNOME_Evolution_Addressbook_OtherError,
2048                                                     NULL);
2049                         modify_contact_dtor ((LDAPOp*)modify_op);
2050                 }
2051         }
2052 }
2053
2054
2055 typedef struct {
2056         LDAPOp op;
2057 } LDAPGetContactOp;
2058
2059 static void
2060 get_contact_handler (LDAPOp *op, LDAPMessage *res)
2061 {
2062         EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (op->backend);
2063         int msg_type;
2064         GTimeVal start, end;
2065         unsigned long diff;
2066
2067         if (enable_debug) {
2068                 printf ("get_contact_handler ... \n");
2069                 g_get_current_time (&start);
2070         }
2071
2072         if (!bl->priv->ldap) {
2073                 e_data_book_respond_get_contact (op->book, op->opid, GNOME_Evolution_Addressbook_OtherError, "");
2074                 ldap_op_finished (op);
2075                 if (enable_debug)
2076                         printf ("get_contact_handler... ldap handler is NULL \n");
2077                 return;
2078         }
2079
2080         /* the msg_type will be either SEARCH_ENTRY (if we're
2081            successful) or SEARCH_RESULT (if we're not), so we finish
2082            the op after either */
2083         msg_type = ldap_msgtype (res);
2084         if (msg_type == LDAP_RES_SEARCH_ENTRY) {
2085                 LDAPMessage *e = ldap_first_entry (bl->priv->ldap, res);
2086
2087                 EContact *contact;
2088                 char *vcard;
2089
2090                 if (!e) {
2091                         g_warning ("uh, this shouldn't happen");
2092                         e_data_book_respond_get_contact (op->book,
2093                                                          op->opid,
2094                                                          GNOME_Evolution_Addressbook_OtherError,
2095                                                          "");
2096                         ldap_op_finished (op);
2097                         return;
2098                 }
2099
2100                 contact = build_contact_from_entry (bl, e, NULL);
2101
2102                 vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
2103                 e_data_book_respond_get_contact (op->book,
2104                                                  op->opid,
2105                                                  GNOME_Evolution_Addressbook_Success,
2106                                                  vcard);
2107                 g_free (vcard);
2108                 g_object_unref (contact);
2109                 ldap_op_finished (op);
2110
2111                 if (enable_debug) {
2112                         g_get_current_time (&end);
2113                         diff = end.tv_sec * 1000 + end.tv_usec/1000;
2114                         diff -= start.tv_sec * 1000 + start.tv_usec/1000;
2115                         printf ("get_contact_handler took %ld.%03ld seconds \n",
2116                                  diff/1000, diff%1000);
2117                 }
2118         }
2119         else if (msg_type == LDAP_RES_SEARCH_RESULT) {
2120                 char *ldap_error_msg;
2121                 int ldap_error;
2122
2123                 g_static_rec_mutex_lock (&eds_ldap_handler_lock);
2124                 ldap_parse_result (bl->priv->ldap, res, &ldap_error,
2125                                    NULL, &ldap_error_msg, NULL, NULL, 0);
2126                 g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
2127                 if (ldap_error != LDAP_SUCCESS) {
2128                         g_warning ("get_contact_handler: %02X (%s), additional info: %s",
2129                                    ldap_error,
2130                                    ldap_err2string (ldap_error), ldap_error_msg);
2131                 }
2132                 ldap_memfree (ldap_error_msg);
2133
2134                 e_data_book_respond_get_contact (op->book, 
2135                                                  op->opid,
2136                                                  ldap_error_to_response (ldap_error),
2137                                                  "");
2138                 ldap_op_finished (op);
2139         }
2140         else {
2141                 g_warning ("unhandled result type %d returned", msg_type);
2142                 e_data_book_respond_get_contact (op->book,
2143                                                  op->opid,
2144                                                  GNOME_Evolution_Addressbook_OtherError,
2145                                                  "");
2146                 ldap_op_finished (op);
2147         }
2148
2149 }
2150
2151 static void
2152 get_contact_dtor (LDAPOp *op)
2153 {
2154         LDAPGetContactOp *get_contact_op = (LDAPGetContactOp*)op;
2155
2156         g_free (get_contact_op);
2157 }
2158
2159 static void
2160 e_book_backend_ldap_get_contact (EBookBackend *backend,
2161                                  EDataBook    *book,
2162                                  guint32       opid,
2163                                  const char   *id)
2164 {
2165         LDAPGetContactOp *get_contact_op;
2166         EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (backend);
2167         LDAP *ldap;
2168         int get_contact_msgid;
2169         EDataBookView *book_view;
2170         int ldap_error;
2171         GTimeVal start, end;
2172         unsigned long diff;
2173
2174         switch (bl->priv->mode) {
2175
2176         case GNOME_Evolution_Addressbook_MODE_LOCAL :
2177                 if (bl->priv->marked_for_offline && bl->priv->cache) {
2178                         EContact *contact = e_book_backend_cache_get_contact (bl->priv->cache, id);
2179                         gchar *vcard_str;
2180
2181                         if (!contact) {
2182                                 e_data_book_respond_get_contact (book, opid, GNOME_Evolution_Addressbook_OtherError, "");
2183                                 return;
2184                         }
2185
2186                         vcard_str = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
2187
2188                         e_data_book_respond_get_contact (book,
2189                                                          opid,
2190                                                          GNOME_Evolution_Addressbook_Success,
2191                                                          vcard_str);
2192                         g_free (vcard_str);
2193                         g_object_unref (contact);
2194                         return;
2195                 }
2196
2197                 e_data_book_respond_get_contact(book, opid, GNOME_Evolution_Addressbook_RepositoryOffline, "");
2198                 return;
2199
2200         case GNOME_Evolution_Addressbook_MODE_REMOTE : 
2201
2202                 if (enable_debug) {
2203                         printf("e_book_backend_ldap_get_contact ... \n");
2204                         g_get_current_time (&start);
2205                 }
2206                 ldap = bl->priv->ldap;
2207
2208                 if (!ldap) {
2209                         e_data_book_respond_get_contact (book, opid, GNOME_Evolution_Addressbook_OtherError, "");
2210                         if (enable_debug)
2211                                 printf("e_book_backend_ldap_get_contact ... ldap handler is NULL\n");
2212                         return;
2213                 }
2214
2215                 get_contact_op = g_new0 (LDAPGetContactOp, 1);
2216                 book_view = find_book_view (bl);
2217
2218                 do {    
2219                         g_static_rec_mutex_lock (&eds_ldap_handler_lock);
2220                         ldap_error = ldap_search_ext (ldap, id,
2221                                                       LDAP_SCOPE_BASE,
2222                                                       "(objectclass=*)",
2223                                                       NULL, 0, NULL, NULL,
2224                                                       NULL, /* XXX timeout */
2225                                                       1, &get_contact_msgid);
2226                         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
2227                 } while (e_book_backend_ldap_reconnect (bl, book_view, ldap_error));
2228
2229                 if (ldap_error == LDAP_SUCCESS) {
2230                         ldap_op_add ((LDAPOp*)get_contact_op, backend, book,
2231                                      book_view, opid, get_contact_msgid,
2232                                      get_contact_handler, get_contact_dtor);
2233
2234                         if (enable_debug) {
2235                                 printf("e_book_backend_ldap_get_contact invoked get_contact_handler\n");
2236                                 g_get_current_time (&end);
2237                                 diff = end.tv_sec * 1000 + end.tv_usec/1000;
2238                                 diff -= start.tv_sec * 1000 + start.tv_usec/1000;
2239                                 printf("and took %ld.%03ld seconds\n",
2240                                                         diff/1000, diff%1000);
2241                         }
2242                 }
2243                 else {
2244                         e_data_book_respond_get_contact (book,
2245                                                          opid,
2246                                                          ldap_error_to_response (ldap_error),
2247                                                          "");
2248                         get_contact_dtor ((LDAPOp*)get_contact_op);
2249                 }
2250         }
2251 }
2252
2253
2254 typedef struct {
2255         LDAPOp op;
2256         GList *contacts;
2257 } LDAPGetContactListOp;
2258
2259 static void
2260 contact_list_handler (LDAPOp *op, LDAPMessage *res)
2261 {
2262         LDAPGetContactListOp *contact_list_op = (LDAPGetContactListOp*)op;
2263         EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (op->backend);
2264         LDAP *ldap;
2265         LDAPMessage *e;
2266         int msg_type;
2267         GTimeVal start, end;
2268         unsigned long diff;
2269
2270         if (enable_debug) {
2271                 printf ("contact_list_handler ...\n");
2272                 g_get_current_time (&start);
2273         }
2274         ldap = bl->priv->ldap;
2275         if (!ldap) {
2276                 e_data_book_respond_get_contact_list (op->book, op->opid, GNOME_Evolution_Addressbook_OtherError, NULL);
2277                 ldap_op_finished (op);
2278                 if (enable_debug)
2279                         printf ("contact_list_handler ... ldap handler is NULL \n");
2280                 return;
2281         }
2282
2283         msg_type = ldap_msgtype (res);
2284         if (msg_type == LDAP_RES_SEARCH_ENTRY) {
2285                 e = ldap_first_entry (ldap, res);
2286
2287                 while (NULL != e) {
2288                         EContact *contact;
2289                         char *vcard;
2290
2291                         contact = build_contact_from_entry (bl, e, NULL);
2292
2293                         vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
2294
2295                         printf ("vcard = %s\n", vcard);
2296  
2297                         contact_list_op->contacts = g_list_append (contact_list_op->contacts,
2298                                                                    vcard);
2299
2300                         g_object_unref (contact);
2301
2302                         e = ldap_next_entry(ldap, e);
2303                 }
2304         }
2305         else if (msg_type == LDAP_RES_SEARCH_RESULT) {
2306                 char *ldap_error_msg;
2307                 int ldap_error;
2308
2309                 g_static_rec_mutex_lock (&eds_ldap_handler_lock);
2310                 ldap_parse_result (ldap, res, &ldap_error,
2311                                    NULL, &ldap_error_msg, NULL, NULL, 0);
2312                 g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
2313                 if (ldap_error != LDAP_SUCCESS) {
2314                         g_warning ("contact_list_handler: %02X (%s), additional info: %s",
2315                                    ldap_error,
2316                                    ldap_err2string (ldap_error), ldap_error_msg);
2317                 }
2318                 ldap_memfree (ldap_error_msg);
2319
2320                 g_warning ("search returned %d\n", ldap_error);
2321
2322                 if (ldap_error == LDAP_TIMELIMIT_EXCEEDED)
2323                         e_data_book_respond_get_contact_list (op->book,
2324                                                               op->opid,
2325                                                               GNOME_Evolution_Addressbook_SearchTimeLimitExceeded,
2326                                                               contact_list_op->contacts);
2327                 else if (ldap_error == LDAP_SIZELIMIT_EXCEEDED)
2328                         e_data_book_respond_get_contact_list (op->book,
2329                                                               op->opid,
2330                                                               GNOME_Evolution_Addressbook_SearchSizeLimitExceeded,
2331                                                               contact_list_op->contacts);
2332                 else if (ldap_error == LDAP_SUCCESS)
2333                         e_data_book_respond_get_contact_list (op->book,
2334                                                               op->opid,
2335                                                               GNOME_Evolution_Addressbook_Success,
2336                                                               contact_list_op->contacts);
2337                 else
2338                         e_data_book_respond_get_contact_list (op->book,
2339                                                               op->opid,
2340                                                               GNOME_Evolution_Addressbook_OtherError,
2341                                                               contact_list_op->contacts);
2342
2343                 ldap_op_finished (op);
2344                 if (enable_debug) {
2345                         printf ("contact_list_handler success ");
2346                         g_get_current_time (&end);
2347                         diff = end.tv_sec * 1000 + end.tv_usec/1000;
2348                         diff -= start.tv_sec * 1000 + start.tv_usec/1000;
2349                         printf("and took %ld.%03ld seconds\n", diff/1000, diff%1000);
2350                 }
2351         }
2352         else {
2353                 g_warning ("unhandled search result type %d returned", msg_type);
2354                 e_data_book_respond_get_contact_list (op->book,
2355                                                       op->opid,
2356                                                       GNOME_Evolution_Addressbook_OtherError,
2357                                                       NULL);
2358                 ldap_op_finished (op);
2359         }
2360 }
2361
2362 static void
2363 contact_list_dtor (LDAPOp *op)
2364 {
2365         LDAPGetContactListOp *contact_list_op = (LDAPGetContactListOp*)op;
2366
2367         g_free (contact_list_op);
2368 }
2369
2370
2371 static void
2372 e_book_backend_ldap_get_contact_list (EBookBackend *backend,
2373                                       EDataBook    *book,
2374                                       guint32       opid,
2375                                       const char   *query)
2376 {
2377         LDAPGetContactListOp *contact_list_op;
2378         EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (backend);
2379         LDAP *ldap;
2380         int contact_list_msgid;
2381         EDataBookView *book_view;
2382         int ldap_error;
2383         char *ldap_query;
2384         GTimeVal start, end;
2385         unsigned long diff;
2386
2387         if (enable_debug) {
2388                 printf ("e_book_backend_ldap_get_contact_list ... \n");
2389                 g_get_current_time (&start);
2390         }
2391
2392         switch (bl->priv->mode) {
2393                 
2394         case GNOME_Evolution_Addressbook_MODE_LOCAL :
2395                 if (bl->priv->marked_for_offline && bl->priv->cache) {
2396                         GList *contacts;
2397                         GList *vcard_strings = NULL;
2398                         GList *l;
2399
2400                         contacts = e_book_backend_cache_get_contacts (bl->priv->cache, query);
2401
2402                         for (l = contacts; l; l = g_list_next (l)) {
2403                                 EContact *contact = l->data;
2404                                 vcard_strings = g_list_prepend (vcard_strings, e_vcard_to_string (E_VCARD (contact),
2405                                                                 EVC_FORMAT_VCARD_30));
2406                                 g_object_unref (contact);
2407                         }
2408
2409                         g_list_free (contacts);
2410                         e_data_book_respond_get_contact_list (book, opid, GNOME_Evolution_Addressbook_Success, vcard_strings);
2411                         return;
2412                 }
2413                 
2414                 e_data_book_respond_get_contact_list (book, opid, GNOME_Evolution_Addressbook_RepositoryOffline,
2415                                                       NULL);
2416                 return;
2417                 
2418         case GNOME_Evolution_Addressbook_MODE_REMOTE:
2419                 ldap = bl->priv->ldap;
2420
2421                 if (!ldap) {
2422                         e_data_book_respond_get_contact_list (book, opid, GNOME_Evolution_Addressbook_OtherError, NULL);
2423                         if (enable_debug)
2424                                 printf ("e_book_backend_ldap_get_contact_list... ldap handler is NULL\n");
2425                         return;
2426                 }
2427
2428                 contact_list_op = g_new0 (LDAPGetContactListOp, 1);
2429                 book_view = find_book_view (bl);
2430
2431                 ldap_query = e_book_backend_ldap_build_query (bl, query);
2432
2433                 printf ("getting contact list with filter: %s\n", ldap_query);
2434
2435                 do {    
2436                         g_static_rec_mutex_lock (&eds_ldap_handler_lock);
2437                         ldap_error = ldap_search_ext (ldap,
2438                                                       bl->priv->ldap_rootdn,
2439                                                       bl->priv->ldap_scope,
2440                                                       ldap_query,
2441                                                       NULL, 0, NULL, NULL,
2442                                                       NULL, /* XXX timeout */
2443                                                       LDAP_NO_LIMIT, &contact_list_msgid);
2444                         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
2445                 } while (e_book_backend_ldap_reconnect (bl, book_view, ldap_error));
2446
2447                 g_free (ldap_query);
2448
2449                 if (ldap_error == LDAP_SUCCESS) {
2450                         ldap_op_add ((LDAPOp*)contact_list_op, backend, book,
2451                                      book_view, opid, contact_list_msgid,
2452                                      contact_list_handler, contact_list_dtor);
2453                         if (enable_debug) {
2454                                 g_get_current_time (&end);
2455
2456                                 diff = end.tv_sec * 1000 + end.tv_usec/1000;
2457                                 diff -= start.tv_sec * 1000 + start.tv_usec/1000;
2458
2459                                 printf ("e_book_backend_ldap_get_contact_list invoked contact_list_handler ");
2460                                 printf ("and took %ld.%03ld seconds\n", diff/1000, diff%1000);
2461                         }
2462                 }
2463                 else {
2464                         e_data_book_respond_get_contact_list (book,
2465                                                               opid,
2466                                                               ldap_error_to_response (ldap_error),
2467                                                               NULL);
2468                         contact_list_dtor ((LDAPOp*)contact_list_op);
2469                 }
2470         }
2471 }
2472
2473 static EContactField email_ids[4] = {
2474         E_CONTACT_EMAIL_1,
2475         E_CONTACT_EMAIL_2,
2476         E_CONTACT_EMAIL_3,
2477         E_CONTACT_EMAIL_4
2478 };
2479
2480 /* List property functions */
2481 static void
2482 email_populate(EContact *contact, char **values)
2483 {
2484         int i;
2485         for (i = 0; values[i] && i < 4; i ++)
2486                 e_contact_set (contact, email_ids[i], values[i]);
2487 }
2488
2489 static struct berval**
2490 email_ber(EContact *contact)
2491 {
2492         struct berval** result;
2493         const char *emails[4];
2494         int i, j, num = 0;
2495
2496         if (e_contact_get (contact, E_CONTACT_IS_LIST))
2497                 return NULL;
2498
2499         for (i = 0; i < 4; i ++) {
2500                 emails[i] = e_contact_get (contact, email_ids[i]);
2501                 if (emails[i])
2502                         num++;
2503         }
2504
2505         if (num == 0)
2506                 return NULL;
2507
2508         result = g_new (struct berval*, num + 1);
2509
2510         for (i = 0; i < num; i ++)
2511                 result[i] = g_new (struct berval, 1);
2512
2513         j = 0;
2514         for (i = 0; i < 4; i ++) {
2515                 if (emails[i]) {
2516                         result[j]->bv_val = g_strdup (emails[i]);
2517                         result[j++]->bv_len = strlen (emails[i]);
2518                 }
2519         }
2520
2521         result[num] = NULL;
2522
2523         return result;
2524 }
2525
2526 static gboolean
2527 email_compare (EContact *contact1, EContact *contact2)
2528 {
2529         const char *email1, *email2;
2530         int i;
2531         /*
2532         if (e_contact_get (contact1, E_CONTACT_IS_LIST))
2533                 return TRUE;
2534         */
2535
2536         for (i = 0; i < 4; i ++) {
2537                 gboolean equal;
2538                 email1 = e_contact_get_const (contact1, email_ids[i]);
2539                 email2 = e_contact_get_const (contact2, email_ids[i]);
2540
2541                 if (email1 && email2)
2542                         equal = !strcmp (email1, email2);
2543                 else
2544                         equal = (!!email1 == !!email2);
2545
2546                 if (!equal)
2547                         return equal;
2548         }
2549
2550         return TRUE;
2551 }
2552
2553
2554 static void
2555 member_populate (EContact *contact, char **values)
2556 {
2557         int i;
2558         gchar **member_info;
2559
2560         for (i = 0; values[i]; i ++) {
2561                 EVCardAttribute *attr;
2562
2563                 member_info = g_strsplit (values [i], ";", -1);
2564
2565                 attr = e_vcard_attribute_new (NULL, EVC_EMAIL);
2566                 e_vcard_attribute_add_param_with_value (attr, e_vcard_attribute_param_new (EVC_X_DEST_EMAIL), member_info [0]);
2567                 e_vcard_attribute_add_param_with_value (attr, e_vcard_attribute_param_new (EVC_X_DEST_CONTACT_UID), member_info [1]);
2568                 if (member_info [2])
2569                         e_vcard_attribute_add_param_with_value (attr, e_vcard_attribute_param_new (EVC_X_DEST_NAME), member_info [2]);
2570                 e_vcard_attribute_add_value (attr, member_info [0]);
2571                 e_vcard_add_attribute (E_VCARD (contact), attr);
2572         }
2573 }
2574
2575 static struct berval**
2576 member_ber (EContact *contact)
2577 {
2578         struct berval** result;
2579         GList *members, *l, *p;
2580         int i=0, num = 0;
2581         char *dn;
2582
2583         if (!(e_contact_get (contact, E_CONTACT_IS_LIST)))
2584                 return NULL;
2585
2586         members = e_contact_get_attributes (contact, E_CONTACT_EMAIL);
2587         num = g_list_length (members);
2588         if (num == 0)
2589                 return NULL;
2590
2591         result = g_new (struct berval*, num + 1);
2592
2593         for (l = members; l != NULL; l = g_list_next (l)) {
2594                 EVCardAttribute *attr = l->data;
2595                 dn = NULL;
2596         
2597                 for (p = e_vcard_attribute_get_params (attr); p; p = p->next) {
2598                         EVCardAttributeParam *param = p->data;
2599                         const char *param_name = e_vcard_attribute_param_get_name (param);
2600                         
2601                         if (!g_ascii_strcasecmp (param_name, EVC_X_DEST_CONTACT_UID)) {
2602                                 GList *v = e_vcard_attribute_param_get_values (param);
2603                                 dn = v ? v->data : NULL;
2604                                 if (dn) {
2605                                         result[i] = g_new (struct berval, 1);
2606                                         result[i]->bv_val = g_strdup (dn);
2607                                         result[i]->bv_len = strlen (dn);
2608                                         i++;
2609                                 }
2610                         }
2611                 }
2612         }
2613         result[i] = NULL;
2614         return result;
2615 }
2616
2617 static gboolean
2618 member_compare (EContact *contact_new, EContact *contact_current)
2619 {
2620         GList *members_new, *members_cur, *l1, *l2, *p_new, *p_cur;
2621         int len1 = 0, len2 = 0;
2622         char *list_name1, *list_name2;
2623         gboolean equal;
2624
2625         if (!(e_contact_get (contact_new, E_CONTACT_IS_LIST)))
2626                 return TRUE;
2627         if (!(e_contact_get (contact_current, E_CONTACT_IS_LIST)))
2628                 return TRUE;
2629
2630         list_name1 = e_contact_get (contact_new, E_CONTACT_FULL_NAME);
2631         list_name2 = e_contact_get (contact_current, E_CONTACT_FULL_NAME);
2632         if (list_name1 && list_name2)
2633                 equal = !strcmp (list_name1, list_name2);
2634         else
2635                 equal = (!!list_name1 == !!list_name2);
2636
2637         if (!equal)
2638                 return equal;
2639
2640         members_new = e_contact_get_attributes (contact_new, E_CONTACT_EMAIL);
2641         len1 = g_list_length (members_new);
2642         members_cur = e_contact_get_attributes (contact_current, E_CONTACT_EMAIL);
2643         len2 = g_list_length (members_cur);
2644         if (len1 != len2)
2645                 return FALSE;
2646
2647         for (l1 = members_new; l1 != NULL; l1 = g_list_next (l1)) {
2648                 EVCardAttribute *attr_new = l1->data;
2649                 char *dn_new = NULL;
2650         
2651                 for (p_new = e_vcard_attribute_get_params (attr_new); p_new; p_new = p_new->next) {
2652                         EVCardAttributeParam *param = p_new->data;
2653                         const char *param_name1 = e_vcard_attribute_param_get_name (param);
2654                         
2655                         if (!g_ascii_strcasecmp (param_name1, EVC_X_DEST_CONTACT_UID)) {
2656                                 gboolean found = FALSE;
2657                                 GList *v = e_vcard_attribute_param_get_values (param);
2658                                 dn_new = v ? v->data : NULL;
2659                                 if (dn_new) {
2660                                         for (l2 = members_cur; l2 != NULL; l2 = g_list_next (l2)) {
2661                                                 EVCardAttribute *attr_cur = l2->data;
2662                                                 char *dn_cur = NULL;
2663
2664                                                 for (p_cur = e_vcard_attribute_get_params (attr_cur); p_cur; p_cur = p_cur->next) {
2665                                                         EVCardAttributeParam *param2 = p_cur->data;
2666                                                         const char *param_name2 = e_vcard_attribute_param_get_name (param2);
2667
2668                                                         if (!g_ascii_strcasecmp (param_name2, EVC_X_DEST_CONTACT_UID)) {
2669                                                                 GList *v2 = e_vcard_attribute_param_get_values (param2);
2670                                                                 dn_cur = v2 ? v2->data : NULL;
2671                                                         
2672                                                                 if (dn_cur) {
2673                                                                         if (!g_ascii_strcasecmp (dn_new, dn_cur)) {
2674                                                                                 found = TRUE;
2675                                                                                 members_cur = g_list_remove (members_cur, attr_cur);
2676                                                                                 goto next_member;
2677                                                                         }
2678                                                                 }
2679                                                         }
2680                                                 }
2681                                         }
2682                                         if (!found) {
2683                                                 return FALSE;
2684                                         }
2685                                 }
2686                         }
2687                 }
2688                 next_member: 
2689                 continue;
2690         }
2691         return TRUE;
2692 }
2693
2694 static void
2695 homephone_populate(EContact *contact, char **values)
2696 {
2697         if (values[0]) {
2698                 e_contact_set (contact, E_CONTACT_PHONE_HOME, values[0]);
2699                 if (values[1])
2700                         e_contact_set (contact, E_CONTACT_PHONE_HOME_2, values[1]);
2701         }
2702 }
2703
2704 static struct berval**
2705 homephone_ber(EContact *contact)
2706 {
2707         struct berval** result;
2708         const char *homephones[3];
2709         int i, j, num;
2710
2711         num = 0;
2712         if ((homephones[0] = e_contact_get (contact, E_CONTACT_PHONE_HOME)))
2713                 num++;
2714         if ((homephones[1] = e_contact_get (contact, E_CONTACT_PHONE_HOME_2)))
2715                 num++;
2716
2717         if (num == 0)
2718                 return NULL;
2719
2720         result = g_new (struct berval*, num + 1);
2721
2722         for (i = 0; i < num; i ++)
2723                 result[i] = g_new (struct berval, 1);
2724
2725         j = 0;
2726         for (i = 0; i < 2; i ++) {
2727                 if (homephones[i]) {
2728                         result[j]->bv_val = g_strdup (homephones[i]);
2729                         result[j++]->bv_len = strlen (homephones[i]);
2730                 }
2731         }
2732
2733         result[num] = NULL;
2734
2735         return result;
2736 }
2737
2738 static gboolean
2739 homephone_compare (EContact *contact1, EContact *contact2)
2740 {
2741         int phone_ids[2] = { E_CONTACT_PHONE_HOME, E_CONTACT_PHONE_HOME_2 };
2742         const char *phone1, *phone2;
2743         int i;
2744
2745         for (i = 0; i < 2; i ++) {
2746                 gboolean equal;
2747                 phone1 = e_contact_get (contact1, phone_ids[i]);
2748                 phone2 = e_contact_get (contact2, phone_ids[i]);
2749
2750                 if (phone1 && phone2)
2751                         equal = !strcmp (phone1, phone2);
2752                 else
2753                         equal = (!!phone1 == !!phone2);
2754
2755                 if (!equal)
2756                         return equal;
2757         }
2758
2759         return TRUE;
2760 }
2761
2762 static void
2763 business_populate(EContact *contact, char **values)
2764 {
2765         if (values[0]) {
2766                 e_contact_set (contact, E_CONTACT_PHONE_BUSINESS, values[0]);
2767                 if (values[1])
2768                         e_contact_set (contact, E_CONTACT_PHONE_BUSINESS_2, values[1]);
2769         }
2770 }
2771
2772 static struct berval**
2773 business_ber(EContact *contact)
2774 {
2775         struct berval** result;
2776         const char *business_phones[3];
2777         int i, j, num;
2778
2779         num = 0;
2780         if ((business_phones[0] = e_contact_get (contact, E_CONTACT_PHONE_BUSINESS)))
2781                 num++;
2782         if ((business_phones[1] = e_contact_get (contact, E_CONTACT_PHONE_BUSINESS_2)))
2783                 num++;
2784
2785         if (num == 0)
2786                 return NULL;
2787
2788         result = g_new (struct berval*, num + 1);
2789
2790         for (i = 0; i < num; i ++)
2791                 result[i] = g_new (struct berval, 1);
2792
2793         j = 0;
2794         for (i = 0; i < 2; i ++) {
2795                 if (business_phones[i]) {
2796                         result[j]->bv_val = g_strdup (business_phones[i]);
2797                         result[j++]->bv_len = strlen (business_phones[i]);
2798                 }
2799         }
2800
2801         result[num] = NULL;
2802
2803         return result;
2804 }
2805
2806 static gboolean
2807 business_compare (EContact *contact1, EContact *contact2)
2808 {
2809         int phone_ids[2] = { E_CONTACT_PHONE_BUSINESS, E_CONTACT_PHONE_BUSINESS_2 };
2810         const char *phone1, *phone2;
2811         int i;
2812
2813         for (i = 0; i < 2; i ++) {
2814                 gboolean equal;
2815                 phone1 = e_contact_get (contact1, phone_ids[i]);
2816                 phone2 = e_contact_get (contact2, phone_ids[i]);
2817
2818                 if (phone1 && phone2)
2819                         equal = !strcmp (phone1, phone2);
2820                 else
2821                         equal = (!!phone1 == !!phone2);
2822
2823                 if (!equal)
2824                         return equal;
2825         }
2826
2827         return TRUE;
2828 }
2829
2830 static void
2831 anniversary_populate (EContact *contact, char **values)
2832 {
2833         if (values[0]) {
2834                 EContactDate *dt = e_contact_date_from_string (values[0]);
2835                 e_contact_set (contact, E_CONTACT_ANNIVERSARY, dt);
2836                 e_contact_date_free (dt);
2837         }
2838 }
2839
2840 static struct berval**
2841 anniversary_ber (EContact *contact)
2842 {
2843         EContactDate *dt;
2844         struct berval** result = NULL;
2845
2846         dt = e_contact_get (contact, E_CONTACT_ANNIVERSARY);
2847
2848         if (dt) {
2849                 char *anniversary;
2850
2851                 anniversary = e_contact_date_to_string (dt);
2852
2853                 result = g_new (struct berval*, 2);
2854                 result[0] = g_new (struct berval, 1);
2855                 result[0]->bv_val = anniversary;
2856                 result[0]->bv_len = strlen (anniversary);
2857
2858                 result[1] = NULL;
2859
2860                 e_contact_date_free (dt);
2861         }
2862
2863         return result;
2864 }
2865
2866 static gboolean
2867 anniversary_compare (EContact *contact1, EContact *contact2)
2868 {
2869         EContactDate *dt1, *dt2;
2870         gboolean equal;
2871
2872         dt1 = e_contact_get (contact1, E_CONTACT_ANNIVERSARY);
2873         dt2 = e_contact_get (contact2, E_CONTACT_ANNIVERSARY);
2874
2875         equal = e_contact_date_equal (dt1, dt2);
2876
2877         e_contact_date_free (dt1);
2878         e_contact_date_free (dt2);
2879
2880         return equal;
2881 }
2882
2883 static void
2884 birthday_populate (EContact *contact, char **values)
2885 {
2886         if (values[0]) {
2887                 EContactDate *dt = e_contact_date_from_string (values[0]);
2888                 e_contact_set (contact, E_CONTACT_BIRTH_DATE, dt);
2889                 e_contact_date_free (dt);
2890         }
2891 }
2892
2893 static struct berval**
2894 birthday_ber (EContact *contact)
2895 {
2896         EContactDate *dt;
2897         struct berval** result = NULL;
2898
2899         dt = e_contact_get (contact, E_CONTACT_BIRTH_DATE);
2900         if (dt) {
2901                 char *birthday;
2902
2903                 birthday = e_contact_date_to_string (dt);
2904
2905                 result = g_new (struct berval*, 2);
2906                 result[0] = g_new (struct berval, 1);
2907                 result[0]->bv_val = birthday;
2908                 result[0]->bv_len = strlen (birthday);
2909
2910                 result[1] = NULL;
2911
2912                 e_contact_date_free (dt);
2913         }
2914
2915         return result;
2916 }
2917
2918 static gboolean
2919 birthday_compare (EContact *contact1, EContact *contact2)
2920 {
2921         EContactDate *dt1, *dt2;
2922         gboolean equal;
2923
2924         dt1 = e_contact_get (contact1, E_CONTACT_BIRTH_DATE);
2925         dt2 = e_contact_get (contact2, E_CONTACT_BIRTH_DATE);
2926
2927         equal = e_contact_date_equal (dt1, dt2);
2928
2929         e_contact_date_free (dt1);
2930         e_contact_date_free (dt2);
2931
2932         return equal;
2933 }
2934
2935 static void
2936 category_populate (EContact *contact, char **values)
2937 {
2938         int i;
2939         GList *categories = NULL;
2940
2941         for (i = 0; values[i]; i++)
2942                 categories = g_list_append (categories, g_strdup (values[i]));
2943
2944         e_contact_set (contact, E_CONTACT_CATEGORY_LIST, categories);
2945
2946         g_list_foreach (categories, (GFunc)g_free, NULL);
2947         g_list_free (categories);
2948 }
2949
2950 static struct berval**
2951 category_ber (EContact *contact)
2952 {
2953         struct berval** result = NULL;
2954         GList *categories;
2955         const char *category_string;
2956
2957         category_string = e_contact_get (contact, E_CONTACT_CATEGORIES);
2958         if (!category_string || !*category_string)
2959                 return NULL;
2960
2961         categories = e_contact_get (contact, E_CONTACT_CATEGORY_LIST);
2962
2963         if (g_list_length (categories) != 0) {
2964                 int i;
2965                 GList *iter;
2966                 result = g_new0 (struct berval*, g_list_length (categories) + 1);
2967
2968                 for (iter = categories, i = 0; iter; iter = iter->next) {
2969                         char *category = iter->data;
2970
2971                         if (category && *category) {
2972                                 result[i] = g_new (struct berval, 1);
2973                                 result[i]->bv_val = g_strdup (category);
2974                                 result[i]->bv_len = strlen (category);
2975                                 i++;
2976                         }
2977                 }
2978         }
2979
2980         g_list_foreach (categories, (GFunc)g_free, NULL);
2981         g_list_free (categories);
2982         return result;
2983 }
2984
2985 static gboolean
2986 category_compare (EContact *contact1, EContact *contact2)
2987 {
2988         const char *categories1, *categories2;
2989         gboolean equal;
2990
2991         categories1 = e_contact_get_const (contact1, E_CONTACT_CATEGORIES);
2992         categories2 = e_contact_get_const (contact2, E_CONTACT_CATEGORIES);
2993
2994         if (categories1 && categories2)
2995                 equal = !strcmp (categories1, categories2);
2996         else
2997                 equal = (categories1 == categories2);
2998
2999         return equal;
3000 }
3001
3002 static EContactAddress * getormakeEContactAddress(EContact * card, EContactField field)
3003 {
3004     EContactAddress *contact_addr = e_contact_get(card, field);
3005     if (!contact_addr)
3006         contact_addr = g_new0(EContactAddress, 1);
3007     return contact_addr;
3008 }
3009
3010
3011
3012 static void
3013 address_populate(EContact * card, char **values, EContactField field, EContactField other_field)
3014 {
3015         if (values[0]) {
3016                 EContactAddress *contact_addr;
3017                 char *temp = g_strdup(values[0]);
3018                 char *i;
3019                 for (i = temp; *i != '\0'; i++) {
3020                         if (*i == '$') {
3021                                 *i = '\n';
3022                         }
3023                 }
3024                 e_contact_set(card, field, temp);
3025
3026                 contact_addr = getormakeEContactAddress(card, other_field);
3027                 contact_addr->street = g_strdup (temp);
3028                 e_contact_set (card, other_field, contact_addr);
3029                 e_contact_address_free (contact_addr);
3030
3031                 g_free(temp);
3032         }
3033 }
3034
3035 static void
3036 work_city_populate(EContact * card, char **values)
3037 {
3038         EContactAddress *contact_addr = getormakeEContactAddress(card, E_CONTACT_ADDRESS_WORK);
3039         contact_addr->locality = g_strdup (values[0]);
3040         e_contact_set (card, E_CONTACT_ADDRESS_WORK, contact_addr);
3041         e_contact_address_free (contact_addr);
3042 }
3043
3044 static void
3045 work_state_populate(EContact * card, char **values)
3046 {
3047         EContactAddress *contact_addr = getormakeEContactAddress(card, E_CONTACT_ADDRESS_WORK);
3048         contact_addr->region = g_strdup (values[0]);
3049         e_contact_set (card, E_CONTACT_ADDRESS_WORK, contact_addr);
3050         e_contact_address_free (contact_addr);
3051 }
3052
3053 static void
3054 work_po_populate(EContact * card, char **values)
3055 {
3056         EContactAddress *contact_addr = getormakeEContactAddress(card, E_CONTACT_ADDRESS_WORK);
3057         contact_addr->po = g_strdup (values[0]);
3058         e_contact_set (card, E_CONTACT_ADDRESS_WORK, contact_addr);
3059         e_contact_address_free (contact_addr);
3060 }
3061
3062 static void
3063 work_zip_populate(EContact * card, char **values)
3064 {
3065         EContactAddress *contact_addr = getormakeEContactAddress(card, E_CONTACT_ADDRESS_WORK);
3066         contact_addr->code = g_strdup (values[0]);
3067         e_contact_set (card, E_CONTACT_ADDRESS_WORK, contact_addr);
3068         e_contact_address_free (contact_addr);
3069 }
3070
3071 static void
3072 work_country_populate(EContact * card, char **values)
3073 {
3074         EContactAddress *contact_addr = getormakeEContactAddress(card, E_CONTACT_ADDRESS_WORK);
3075         contact_addr->country = g_strdup (values[0]);
3076         e_contact_set (card, E_CONTACT_ADDRESS_WORK, contact_addr);
3077         e_contact_address_free (contact_addr);
3078 }
3079
3080 static void
3081 home_city_populate(EContact * card, char **values)
3082 {
3083         EContactAddress *contact_addr = getormakeEContactAddress(card, E_CONTACT_ADDRESS_HOME);
3084         contact_addr->locality = g_strdup (values[0]);
3085         e_contact_set (card, E_CONTACT_ADDRESS_HOME, contact_addr);
3086         e_contact_address_free (contact_addr);
3087 }
3088
3089 static void
3090 home_state_populate(EContact * card, char **values)
3091 {
3092         EContactAddress *contact_addr = getormakeEContactAddress(card, E_CONTACT_ADDRESS_HOME);
3093         contact_addr->region = g_strdup (values[0]);
3094         e_contact_set (card, E_CONTACT_ADDRESS_HOME, contact_addr);
3095         e_contact_address_free (contact_addr);
3096 }
3097
3098 static void
3099 home_zip_populate(EContact * card, char **values)
3100 {
3101         EContactAddress *contact_addr = getormakeEContactAddress(card, E_CONTACT_ADDRESS_HOME);
3102         contact_addr->code = g_strdup (values[0]);
3103         e_contact_set (card, E_CONTACT_ADDRESS_HOME, contact_addr);
3104         e_contact_address_free (contact_addr);
3105 }
3106
3107 static void
3108 home_country_populate(EContact * card, char **values)
3109 {
3110         EContactAddress *contact_addr = getormakeEContactAddress(card, E_CONTACT_ADDRESS_HOME);
3111         contact_addr->country = g_strdup (values[0]);
3112         e_contact_set (card, E_CONTACT_ADDRESS_HOME, contact_addr);
3113         e_contact_address_free (contact_addr);
3114 }
3115
3116 static void
3117 home_address_populate(EContact * card, char **values)
3118 {
3119         address_populate(card, values, E_CONTACT_ADDRESS_LABEL_HOME, E_CONTACT_ADDRESS_HOME);
3120 }
3121
3122 static void
3123 work_address_populate(EContact * card, char **values)
3124 {
3125         address_populate(card, values, E_CONTACT_ADDRESS_LABEL_WORK, E_CONTACT_ADDRESS_WORK);
3126 }
3127
3128 static void
3129 other_address_populate(EContact * card, char **values)
3130 {
3131         address_populate(card, values, E_CONTACT_ADDRESS_LABEL_OTHER, E_CONTACT_ADDRESS_OTHER);
3132 }
3133
3134 static struct berval **
3135 address_ber(EContact * card, EContactField field)
3136 {
3137         struct berval **result = NULL;
3138         char *address, *i;
3139
3140         address = e_contact_get(card, field);
3141         if (address) {
3142                 for (i = address; *i != '\0'; i++) {
3143                         if (*i == '\n') {
3144                                 *i = '$';
3145                         }
3146                 }
3147
3148                 result = g_new(struct berval *, 2);
3149                 result[0] = g_new(struct berval, 1);
3150                 result[0]->bv_val = address;
3151                 result[0]->bv_len = strlen(address);
3152
3153                 result[1] = NULL;
3154         }
3155         return result;
3156 }
3157
3158 static struct berval **
3159 home_address_ber(EContact * card)
3160 {
3161         return address_ber(card, E_CONTACT_ADDRESS_LABEL_HOME);
3162 }
3163
3164 static struct berval **
3165 work_address_ber(EContact * card)
3166 {
3167         return address_ber(card, E_CONTACT_ADDRESS_LABEL_WORK);
3168 }
3169
3170 static struct berval **
3171 other_address_ber(EContact * card)
3172 {
3173         return address_ber(card, E_CONTACT_ADDRESS_LABEL_OTHER);
3174 }
3175
3176 static gboolean
3177 address_compare(EContact * ecard1, EContact * ecard2, EContactField field)
3178 {
3179         const char *address1, *address2;
3180
3181         gboolean equal;
3182         address1 = e_contact_get_const(ecard1, field);
3183         address2 = e_contact_get_const(ecard2, field);
3184
3185         if (address1 && address2)
3186                 equal = !strcmp(address1, address2);
3187         else
3188                 equal = (!!address1 == !!address2);
3189
3190         return equal;
3191 }
3192
3193 static gboolean
3194 home_address_compare(EContact * ecard1, EContact * ecard2)
3195 {
3196         return address_compare(ecard1, ecard2, E_CONTACT_ADDRESS_LABEL_HOME);
3197 }
3198
3199 static gboolean
3200 work_address_compare(EContact * ecard1, EContact * ecard2)
3201 {
3202         return address_compare(ecard1, ecard2, E_CONTACT_ADDRESS_LABEL_WORK);
3203 }
3204
3205 static gboolean
3206 other_address_compare(EContact * ecard1, EContact * ecard2)
3207 {
3208         return address_compare(ecard1, ecard2, E_CONTACT_ADDRESS_LABEL_OTHER);
3209 }
3210
3211 static void
3212 photo_populate (EContact *contact, struct berval **ber_values)
3213 {
3214         if (ber_values && ber_values[0]) {
3215                 EContactPhoto photo;
3216                 photo.type = E_CONTACT_PHOTO_TYPE_INLINED;
3217                 photo.data.inlined.mime_type = NULL;
3218                 photo.data.inlined.data = (guchar*)ber_values[0]->bv_val;
3219                 photo.data.inlined.length = ber_values[0]->bv_len;
3220
3221                 e_contact_set (contact, E_CONTACT_PHOTO, &photo);
3222         }
3223 }
3224
3225 static struct berval **
3226 photo_ber (EContact *contact)
3227 {
3228         struct berval **result = NULL;
3229         EContactPhoto *photo;
3230
3231         photo = e_contact_get(contact, E_CONTACT_PHOTO);
3232         if (photo && photo->type == E_CONTACT_PHOTO_TYPE_INLINED) {
3233
3234                 result = g_new(struct berval *, 2);
3235                 result[0] = g_new(struct berval, 1);
3236                 result[0]->bv_len = photo->data.inlined.length;
3237                 result[0]->bv_val = g_malloc (photo->data.inlined.length);
3238                 memcpy (result[0]->bv_val, photo->data.inlined.data, photo->data.inlined.length);
3239                 e_contact_photo_free (photo);
3240
3241                 result[1] = NULL;
3242         }
3243
3244         return result;
3245 }
3246
3247 static gboolean
3248 photo_compare(EContact * ecard1, EContact * ecard2)
3249 {
3250         EContactPhoto *photo1, *photo2;
3251         gboolean equal;
3252
3253         photo1 = e_contact_get(ecard1, E_CONTACT_PHOTO);
3254         photo2 = e_contact_get(ecard2, E_CONTACT_PHOTO);
3255
3256
3257         if (photo1 && photo2) {
3258                 if (photo1->type == photo2->type && photo1->type == E_CONTACT_PHOTO_TYPE_INLINED) {
3259                         equal = ((photo1->data.inlined.length == photo2->data.inlined.length)
3260                                  && !memcmp (photo1->data.inlined.data, photo2->data.inlined.data, photo1->data.inlined.length));
3261                 } else if (photo1->type == photo2->type && photo1->type == E_CONTACT_PHOTO_TYPE_URI) {
3262
3263                         equal = !strcmp (photo1->data.uri, photo2->data.uri);
3264                 } else {
3265                         equal = FALSE;
3266                 }
3267                         
3268         }
3269         else {
3270                 equal = (!!photo1 == !!photo2);
3271         }
3272
3273         if (photo1)
3274                 e_contact_photo_free (photo1);
3275         if (photo2)
3276                 e_contact_photo_free (photo2);
3277
3278         return equal;
3279 }
3280
3281 static void
3282 cert_populate (EContact *contact, struct berval **ber_values)
3283 {
3284         if (ber_values && ber_values[0]) {
3285                 EContactCert cert;
3286                 cert.data = ber_values[0]->bv_val;
3287                 cert.length = ber_values[0]->bv_len;
3288
3289                 e_contact_set (contact, E_CONTACT_X509_CERT, &cert);
3290         }
3291 }
3292
3293 typedef struct {
3294         GList *list;
3295         EBookBackendLDAP *bl;
3296 } EBookBackendLDAPSExpData;
3297
3298 #define IS_RFC2254_CHAR(c) ((c) == '*' || (c) =='\\' || (c) == '(' || (c) == ')' || (c) == '\0')
3299 static char *
3300 rfc2254_escape(char *str)
3301 {
3302         int i;
3303         int len = strlen(str);
3304         int newlen = 0;
3305
3306         for (i = 0; i < len; i ++) {
3307                 if (IS_RFC2254_CHAR(str[i]))
3308                         newlen += 3;
3309                 else
3310                         newlen ++;
3311         }
3312
3313         if (len == newlen) {
3314                 return g_strdup (str);
3315         }
3316         else {
3317                 char *newstr = g_malloc0 (newlen + 1);
3318                 int j = 0;
3319                 for (i = 0; i < len; i ++) {
3320                         if (IS_RFC2254_CHAR(str[i])) {
3321                                 sprintf (newstr + j, "\\%02x", str[i]);
3322                                 j+= 3;
3323                         }
3324                         else {
3325                                 newstr[j++] = str[i];
3326                         }
3327                 }
3328                 return newstr;
3329         }
3330 }
3331
3332 static ESExpResult *
3333 func_and(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
3334 {
3335         EBookBackendLDAPSExpData *ldap_data = data;
3336         ESExpResult *r;
3337         char ** strings;
3338
3339         if (argc > 0) {
3340                 int i;
3341
3342                 strings = g_new0(char*, argc+3);
3343                 strings[0] = g_strdup ("(&");
3344                 strings[argc+3 - 2] = g_strdup (")");
3345                 
3346                 for (i = 0; i < argc; i ++) {
3347                         GList *list_head = ldap_data->list;
3348                         if (!list_head)
3349                                 break;
3350                         strings[argc - i] = list_head->data;
3351                         ldap_data->list = g_list_remove_link(list_head, list_head);
3352                         g_list_free_1(list_head);
3353                 }
3354
3355                 ldap_data->list = g_list_prepend(ldap_data->list, g_strjoinv(" ", strings));
3356
3357                 for (i = 0 ; i < argc + 2; i ++)
3358                         g_free (strings[i]);
3359
3360                 g_free (strings);
3361         }
3362
3363         r = e_sexp_result_new(f, ESEXP_RES_BOOL);
3364         r->value.bool = FALSE;
3365
3366         return r;
3367 }
3368
3369 static ESExpResult *
3370 func_or(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
3371 {
3372         EBookBackendLDAPSExpData *ldap_data = data;
3373         ESExpResult *r;
3374         char ** strings;
3375
3376         if (argc > 0) {
3377                 int i;
3378
3379                 strings = g_new0(char*, argc+3);
3380                 strings[0] = g_strdup ("(|");
3381                 strings[argc+3 - 2] = g_strdup (")");
3382
3383                 for (i = 0; i < argc; i ++) {
3384                         GList *list_head = ldap_data->list;
3385                         if (!list_head)
3386                                 break;
3387                         strings[argc - i] = list_head->data;
3388                         ldap_data->list = g_list_remove_link(list_head, list_head);
3389                         g_list_free_1(list_head);
3390                 }
3391
3392                 ldap_data->list = g_list_prepend(ldap_data->list, g_strjoinv(" ", strings));
3393
3394                 for (i = 0 ; i < argc + 2; i ++)
3395                         g_free (strings[i]);
3396
3397                 g_free (strings);
3398         }
3399
3400         r = e_sexp_result_new(f, ESEXP_RES_BOOL);
3401         r->value.bool = FALSE;
3402
3403         return r;
3404 }
3405
3406 static ESExpResult *
3407 func_not(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
3408 {
3409         EBookBackendLDAPSExpData *ldap_data = data;
3410         ESExpResult *r;
3411
3412         /* just replace the head of the list with the NOT of it. */
3413         if (argc > 0) {
3414                 char *term = ldap_data->list->data;
3415                 ldap_data->list->data = g_strdup_printf("(!%s)", term);
3416                 g_free (term);
3417         }
3418
3419         r = e_sexp_result_new(f, ESEXP_RES_BOOL);
3420         r->value.bool = FALSE;
3421
3422         return r;
3423 }
3424
3425 static ESExpResult *
3426 func_contains(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
3427 {
3428         EBookBackendLDAPSExpData *ldap_data = data;
3429         ESExpResult *r;
3430
3431         if (argc == 2
3432             && argv[0]->type == ESEXP_RES_STRING
3433             && argv[1]->type == ESEXP_RES_STRING) {
3434                 char *propname = argv[0]->value.string;
3435                 char *str = rfc2254_escape(argv[1]->value.string);
3436                 gboolean one_star = FALSE;
3437
3438                 if (strlen(str) == 0)
3439                         one_star = TRUE;
3440
3441                 if (!strcmp (propname, "x-evolution-any-field")) {
3442                         int i;
3443                         int query_length;
3444                         char *big_query;
3445                         char *match_str;
3446                         if (one_star) {
3447                                 /* ignore NULL query */
3448                                 r = e_sexp_result_new (f, ESEXP_RES_BOOL);
3449                                 r->value.bool = FALSE;
3450                                 return r;
3451                         }
3452
3453                         match_str = g_strdup_printf ("=*%s*)", str);
3454
3455                         query_length = 3; /* strlen ("(|") + strlen (")") */
3456
3457                         for (i = 0; i < num_prop_infos; i ++) {
3458                                 query_length += 1 /* strlen ("(") */ + strlen(prop_info[i].ldap_attr) + strlen (match_str);
3459                         }
3460
3461                         big_query = g_malloc0(query_length + 1);
3462                         strcat (big_query, "(|");
3463                         for (i = 0; i < num_prop_infos; i ++) {
3464                                 strcat (big_query, "(");
3465                                 strcat (big_query, prop_info[i].ldap_attr);
3466                                 strcat (big_query, match_str);
3467                         }
3468                         strcat (big_query, ")");
3469
3470                         ldap_data->list = g_list_prepend(ldap_data->list, big_query);
3471
3472                         g_free (match_str);
3473                 }
3474                 else {
3475                         char *ldap_attr = query_prop_to_ldap(propname);
3476
3477                         if (ldap_attr)
3478                                 ldap_data->list = g_list_prepend(ldap_data->list,
3479                                                                  g_strdup_printf("(%s=*%s%s)",
3480                                                                                  ldap_attr,
3481                                                                                  str,
3482                                                                                  one_star ? "" : "*"));
3483                 }
3484
3485                 g_free (str);
3486         }
3487
3488         r = e_sexp_result_new(f, ESEXP_RES_BOOL);
3489         r->value.bool = FALSE;
3490
3491         return r;
3492 }
3493
3494 static ESExpResult *
3495 func_is(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
3496 {
3497         EBookBackendLDAPSExpData *ldap_data = data;
3498         ESExpResult *r;
3499
3500         if (argc == 2
3501             && argv[0]->type == ESEXP_RES_STRING
3502             && argv[1]->type == ESEXP_RES_STRING) {
3503                 char *propname = argv[0]->value.string;
3504                 char *str = rfc2254_escape(argv[1]->value.string);
3505                 char *ldap_attr = query_prop_to_ldap(propname);
3506
3507                 if (ldap_attr)
3508                         ldap_data->list = g_list_prepend(ldap_data->list,
3509                                                          g_strdup_printf("(%s=%s)",
3510                                                                          ldap_attr, str));
3511                 else {
3512                         g_warning ("unknown query property\n");
3513                         /* we want something that'll always be false */
3514                         ldap_data->list = g_list_prepend(ldap_data->list,
3515                                                          g_strdup("objectClass=MyBarnIsBiggerThanYourBarn"));
3516                 }
3517
3518                 g_free (str);
3519         }
3520
3521         r = e_sexp_result_new(f, ESEXP_RES_BOOL);
3522         r->value.bool = FALSE;
3523
3524         return r;
3525 }
3526
3527 static ESExpResult *
3528 func_beginswith(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
3529 {
3530         EBookBackendLDAPSExpData *ldap_data = data;
3531         ESExpResult *r;
3532
3533         if (argc == 2
3534             && argv[0]->type == ESEXP_RES_STRING
3535             && argv[1]->type == ESEXP_RES_STRING) {
3536                 char *propname = argv[0]->value.string;
3537                 char *str = rfc2254_escape(argv[1]->value.string);
3538                 char *ldap_attr = query_prop_to_ldap(propname);
3539
3540                 if (strlen (str) == 0) {
3541                         r = e_sexp_result_new (f, ESEXP_RES_BOOL);
3542                         r->value.bool = FALSE;
3543                         return r;
3544                 }
3545
3546                 /* insert hack for fileAs queries, since we need to do
3547                    the right thing if the server supports them or not,
3548                    and for entries that have no fileAs attribute. */
3549                 if (ldap_attr) {
3550                         if (!strcmp (propname, "full_name")) {
3551                                 ldap_data->list = g_list_prepend(ldap_data->list,
3552                                                                g_strdup_printf(
3553                                                                        "(|(cn=%s*)(sn=%s*))",
3554                                                                        str, str));
3555                         }
3556                         else if (!strcmp (ldap_attr, "fileAs")) {
3557                                 if (ldap_data->bl->priv->evolutionPersonSupported)
3558                                         ldap_data->list = g_list_prepend(ldap_data->list,
3559                                                                  g_strdup_printf("(|(fileAs=%s*)(&(!(fileAs=*))(sn=%s*)))",
3560                                                                                  str, str));
3561                                 else 
3562                                         ldap_data->list = g_list_prepend(ldap_data->list,
3563                                                                          g_strdup_printf("(sn=%s*)", str));
3564                         }
3565                         else {
3566                                 ldap_data->list = g_list_prepend(ldap_data->list,
3567                                                                  g_strdup_printf("(%s=%s*)",
3568                                                                                  ldap_attr,
3569                                                                                  str));
3570                         }
3571                 }
3572
3573                 g_free (str);
3574         }
3575
3576         r = e_sexp_result_new(f, ESEXP_RES_BOOL);
3577         r->value.bool = FALSE;
3578
3579         return r;
3580 }
3581
3582 static ESExpResult *
3583 func_endswith(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
3584 {
3585         EBookBackendLDAPSExpData *ldap_data = data;
3586         ESExpResult *r;
3587
3588         if (argc == 2
3589             && argv[0]->type == ESEXP_RES_STRING
3590             && argv[1]->type == ESEXP_RES_STRING) {
3591                 char *propname = argv[0]->value.string;
3592                 char *str = rfc2254_escape(argv[1]->value.string);
3593                 char *ldap_attr = query_prop_to_ldap(propname);
3594
3595                 if (ldap_attr)
3596                         ldap_data->list = g_list_prepend(ldap_data->list,
3597                                                          g_strdup_printf("(%s=*%s)",
3598                                                                          ldap_attr,
3599                                                                          str));
3600                 g_free (str);
3601         }
3602
3603         r = e_sexp_result_new(f, ESEXP_RES_BOOL);
3604         r->value.bool = FALSE;
3605
3606         return r;
3607 }
3608
3609 static ESExpResult *
3610 func_exists(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
3611 {
3612         EBookBackendLDAPSExpData *ldap_data = data;
3613         ESExpResult *r;
3614
3615         if (argc == 1
3616             && argv[0]->type == ESEXP_RES_STRING) {
3617                 char *propname = argv[0]->value.string;
3618
3619                 if (!strcmp (propname, "x-evolution-any-field")) {
3620                         int i;
3621                         int query_length;
3622                         char *big_query;
3623                         char *match_str;
3624
3625                         match_str = g_strdup("=*)");
3626
3627                         query_length = 3; /* strlen ("(|") + strlen (")") */
3628
3629                         for (i = 0; i < num_prop_infos; i ++) {
3630                                 query_length += 1 /* strlen ("(") */ + strlen(prop_info[i].ldap_attr) + strlen (match_str);
3631                         }
3632
3633                         big_query = g_malloc0(query_length + 1);
3634                         strcat (big_query, "(|");
3635                         for (i = 0; i < num_prop_infos; i ++) {
3636                                 strcat (big_query, "(");
3637                                 strcat (big_query, prop_info[i].ldap_attr);
3638                                 strcat (big_query, match_str);
3639                         }
3640                         strcat (big_query, ")");
3641
3642                         ldap_data->list = g_list_prepend(ldap_data->list, big_query);
3643
3644                         g_free (match_str);
3645                 }
3646                 else {
3647                         char *ldap_attr = query_prop_to_ldap(propname);
3648
3649                         if (ldap_attr)
3650                                 ldap_data->list = g_list_prepend(ldap_data->list,
3651                                                                  g_strdup_printf("(%s=*)", ldap_attr));
3652                 }
3653         }
3654
3655         r = e_sexp_result_new(f, ESEXP_RES_BOOL);
3656         r->value.bool = FALSE;
3657
3658         return r;
3659 }
3660
3661 /* 'builtin' functions */
3662 static struct {
3663         char *name;
3664         ESExpFunc *func;
3665         int type;               /* set to 1 if a function can perform shortcut evaluation, or
3666                                    doesn't execute everything, 0 otherwise */
3667 } symbols[] = {
3668         { "and", func_and, 0 },
3669         { "or", func_or, 0 },
3670         { "not", func_not, 0 },
3671         { "contains", func_contains, 0 },
3672         { "is", func_is, 0 },
3673         { "beginswith", func_beginswith, 0 },
3674         { "endswith", func_endswith, 0 },
3675         { "exists", func_exists, 0 },
3676 };
3677
3678 static gchar *
3679 e_book_backend_ldap_build_query (EBookBackendLDAP *bl, const char *query)
3680 {
3681         ESExp *sexp;
3682         ESExpResult *r;
3683         gchar *retval;
3684         EBookBackendLDAPSExpData data;
3685         int i;
3686         char **strings;
3687
3688         data.list = NULL;
3689         data.bl = bl;
3690
3691         sexp = e_sexp_new();
3692
3693         for(i=0;i<sizeof(symbols)/sizeof(symbols[0]);i++) {
3694                 if (symbols[i].type == 1) {
3695                         e_sexp_add_ifunction(sexp, 0, symbols[i].name,
3696                                              (ESExpIFunc *)symbols[i].func, &data);
3697                 } else {
3698                         e_sexp_add_function(sexp, 0, symbols[i].name,
3699                                             symbols[i].func, &data);
3700                 }
3701         }
3702
3703         e_sexp_input_text(sexp, query, strlen(query));
3704         e_sexp_parse(sexp);
3705
3706         r = e_sexp_eval(sexp);
3707
3708         e_sexp_result_free(sexp, r);
3709         e_sexp_unref (sexp);
3710
3711         if (data.list) {
3712                 if (data.list->next) {
3713                         g_warning ("conversion to ldap query string failed");
3714                         retval = NULL;
3715                         g_list_foreach (data.list, (GFunc)g_free, NULL);
3716                 }
3717                 else {
3718                         if (bl->priv->ldap_search_filter && *bl->priv->ldap_search_filter) {
3719                                 strings = g_new0(char*, 5);
3720                                 strings[0] = g_strdup ("(&");
3721                                 strings[1] = g_strdup_printf ("%s", bl->priv->ldap_search_filter);
3722                                 strings[2] = data.list->data;
3723                                 strings[3] = g_strdup (")");
3724                                 retval =  g_strjoinv (" ", strings);
3725                                 for (i = 0 ; i < 4; i ++)
3726                                         g_free (strings[i]);
3727                                 g_free (strings);
3728                         }
3729                         else {
3730                                 retval = g_strdup (data.list->data);
3731                         }
3732                 }
3733         }
3734         else {
3735                 g_warning ("conversion to ldap query string failed");
3736                 retval = NULL;
3737         }
3738
3739         g_list_free (data.list);
3740         return retval;
3741 }
3742
3743 static gchar *
3744 query_prop_to_ldap(gchar *query_prop)
3745 {
3746         int i;
3747
3748         for (i = 0; i < num_prop_infos; i ++)
3749                 if (!strcmp (query_prop, e_contact_field_name (prop_info[i].field_id)))
3750                         return prop_info[i].ldap_attr;
3751
3752         return NULL;
3753 }
3754
3755 typedef struct {
3756         LDAPOp op;
3757         EDataBookView *view;
3758
3759         /* used to detect problems with start/stop_book_view racing */
3760         gboolean aborted;
3761         /* used by search_handler to only send the status messages once */
3762         gboolean notified_receiving_results;
3763 } LDAPSearchOp;
3764
3765 static EContact *
3766 build_contact_from_entry (EBookBackendLDAP *bl,
3767                           LDAPMessage *e, 
3768                           GList **existing_objectclasses)
3769 {
3770         EContact *contact = e_contact_new ();
3771         char *dn;
3772         char *attr;
3773         BerElement *ber = NULL;
3774         LDAP *ldap;
3775
3776         ldap = bl->priv->ldap;
3777         dn = ldap_get_dn (ldap, e);
3778         e_contact_set (contact, E_CONTACT_UID, dn);
3779         ldap_memfree (dn);
3780
3781         for (attr = ldap_first_attribute (ldap, e, &ber); attr;
3782              attr = ldap_next_attribute (ldap, e, ber)) {
3783                 int i;
3784                 struct prop_info *info = NULL;
3785                 char **values;
3786
3787                 printf ("attr = %s \n", attr);
3788                 if (!g_ascii_strcasecmp (attr, "objectclass")) {
3789                         values = ldap_get_values (ldap, e, attr);
3790                         for (i = 0; values[i]; i ++) {
3791                                 printf ("value = %s\n", values[i]);
3792                                 if (!g_ascii_strcasecmp (values[i], "groupOfNames")) {
3793                                         e_contact_set (contact, E_CONTACT_IS_LIST, GINT_TO_POINTER (TRUE));
3794                                         e_contact_set (contact, E_CONTACT_LIST_SHOW_ADDRESSES, GINT_TO_POINTER (TRUE));
3795                                 }
3796                                 if (existing_objectclasses) 
3797                                         *existing_objectclasses = g_list_append (*existing_objectclasses, g_strdup (values[i]));
3798                         }
3799                         ldap_value_free (values);
3800                 }
3801                 else {
3802                         for (i = 0; i < num_prop_infos; i ++)
3803                                 if (!g_ascii_strcasecmp (attr, prop_info[i].ldap_attr)) {
3804                                         info = &prop_info[i];
3805                                         break;
3806                                 }
3807
3808                         printf ("info = %p\n", info);
3809
3810                         if (info) {
3811                                 if (info->prop_type & PROP_WRITE_ONLY)
3812                                         continue;
3813
3814                                 if (info->prop_type & PROP_TYPE_BINARY) {
3815                                         struct berval **ber_values = ldap_get_values_len (ldap, e, attr);
3816
3817                                         if (ber_values) {
3818                                                 info->binary_populate_contact_func (contact, ber_values);
3819
3820                                                 ldap_value_free_len (ber_values);
3821                                         }
3822                                 }
3823                                 else {
3824                                         values = ldap_get_values (ldap, e, attr);
3825
3826                                         if (values) {
3827                                                 if (info->prop_type & PROP_TYPE_STRING) {
3828                                                         printf ("value = %s\n", values[0]);
3829                                                         /* if it's a normal property just set the string */
3830                                                         if (values[0])
3831                                                                 e_contact_set (contact, info->field_id, values[0]);
3832                                                 }
3833                                                 else if (info->prop_type & PROP_TYPE_COMPLEX) {
3834                                                         /* if it's a list call the contact-populate function,
3835                                                            which calls g_object_set to set the property */
3836                                                         info->populate_contact_func(contact,
3837                                                                                     values);
3838                                                 }
3839                                                 else if (info->prop_type & PROP_TYPE_GROUP) {
3840                                                         char *grpattrs[3];
3841                                                         int j, view_limit = -1, ldap_error, count;
3842                                                         EDataBookView *book_view;
3843                                                         LDAPMessage *result;
3844                                                         char **email_values, **cn_values, **member_info;
3845
3846                                                         grpattrs[0] = "cn";
3847                                                         grpattrs[1] = "mail";
3848                                                         grpattrs[2] = NULL;
3849                                                         /* search for member attributes */
3850                                                         /* get the e-mail id for each member and add them to the list */
3851                 
3852                                                         book_view = find_book_view (bl);        
3853                                                         if (book_view)
3854                                                                 view_limit = e_data_book_view_get_max_results (book_view);
3855                                                         if (view_limit == -1 || view_limit > bl->priv->ldap_limit)
3856                                                                 view_limit = bl->priv->ldap_limit;
3857
3858                                                         count = ldap_count_values (values);
3859                                                         member_info = g_new0 (gchar *, count+1);
3860
3861                                                         for (j = 0; values[j] ; j ++) {
3862                                                                 /* get the email id for the given dn */
3863                                                                 /* set base to DN and scope to base */
3864                                                                 printf ("value (dn) = %s \n", values [j]);
3865                                                                 do {
3866                                                                         if ((ldap_error = ldap_search_ext_s (bl->priv->ldap,
3867                                                                                                         values[j],
3868                                                                                                         LDAP_SCOPE_BASE,
3869                                                                                                         NULL,
3870                                                                                                         grpattrs, 0, 
3871                                                                                                         NULL,
3872                                                                                                         NULL,
3873                                                                                                         NULL,
3874                                                                                                         view_limit,
3875                                                                                                         &result)) == LDAP_SUCCESS) {
3876                                                                                 /* find the e-mail ids of members */
3877                                                                                 cn_values = ldap_get_values (ldap, result, "cn");
3878                                                                                 email_values = ldap_get_values (ldap, result, "mail");
3879
3880                                                                                 if (email_values) {
3881                                                                                         printf ("email = %s \n", email_values[0]);
3882                                                                                         *(member_info + j) = 
3883                                                                                                 g_strdup_printf ("%s;%s;", 
3884                                                                                                                  email_values[0], values[j]);
3885                                                                                         ldap_value_free (email_values);
3886                                                                                 }
3887                                                                                 if (cn_values) {
3888                                                                                         printf ("cn = %s \n", cn_values[0]);
3889                                                                                         *(member_info + j) = 
3890                                                                                                 g_strconcat (*(member_info + j), 
3891                                                                                                              cn_values [0], NULL);
3892                                                                                         ldap_value_free (cn_values);
3893                                                                                 }
3894                                                                         }
3895                                                                 }
3896                                                                 while (e_book_backend_ldap_reconnect (bl, book_view, ldap_error));
3897                         
3898                                                                 if (ldap_error != LDAP_SUCCESS) {
3899                                                                         book_view_notify_status (book_view, 
3900                                                                                                  ldap_err2string(ldap_error));
3901                                                                         continue;
3902                                                                 }
3903                                                         }
3904                                                         /* call populate function */    
3905                                                         info->populate_contact_func (contact, member_info); 
3906                                                 
3907                                                         for (j = 0; j < count; j++) {
3908                                                                 g_free (*(member_info + j));
3909                                                         }
3910                                                         g_free (member_info);   
3911                                                 }
3912
3913                                                 ldap_value_free (values);
3914                                         }
3915                                 }
3916                         }
3917                 }
3918
3919                 ldap_memfree (attr);
3920         }
3921
3922         if (ber)
3923                 ber_free (ber, 0);
3924
3925         return contact;
3926 }
3927
3928 static gboolean
3929 poll_ldap (EBookBackendLDAP *bl)
3930 {
3931         LDAP           *ldap;
3932         int            rc;
3933         LDAPMessage    *res;
3934         struct timeval timeout;
3935         const char *ldap_timeout_string;
3936
3937         ldap = bl->priv->ldap;
3938         if (!ldap) {
3939                 bl->priv->poll_timeout = -1;
3940                 return FALSE;
3941         }
3942
3943         if (!bl->priv->active_ops) {
3944                 g_warning ("poll_ldap being called for backend with no active operations");
3945                 bl->priv->poll_timeout = -1;
3946                 return FALSE;
3947         }
3948
3949         timeout.tv_sec = 0;
3950         ldap_timeout_string = g_getenv ("LDAP_TIMEOUT");
3951         if (ldap_timeout_string) {
3952                 timeout.tv_usec = g_ascii_strtod (ldap_timeout_string, NULL) * 1000;
3953         }
3954         else
3955                 timeout.tv_usec = LDAP_RESULT_TIMEOUT_MILLIS * 1000;
3956
3957         rc = ldap_result (ldap, LDAP_RES_ANY, 0, &timeout, &res);
3958         if (rc != 0) {/* rc == 0 means timeout exceeded */
3959                 if (rc == -1) {
3960                         EDataBookView *book_view = find_book_view (bl);
3961                         g_warning ("ldap_result returned -1, restarting ops");
3962
3963                         e_book_backend_ldap_reconnect (bl, book_view, LDAP_SERVER_DOWN);
3964 #if 0
3965                         if (bl->priv->connected)
3966                                 restart_ops (bl);
3967 #endif
3968                 }
3969                 else {
3970                         int msgid = ldap_msgid (res);
3971                         LDAPOp *op;
3972
3973                         g_static_rec_mutex_lock (&bl->priv->op_hash_mutex);
3974                         op = g_hash_table_lookup (bl->priv->id_to_op, &msgid);
3975
3976                         d(printf ("looked up msgid %d, got op %p\n", msgid, op));
3977
3978                         if (op && op->handler)
3979                                 op->handler (op, res);
3980                         else
3981                                 g_warning ("unknown operation, msgid = %d", msgid);
3982
3983                         /* XXX should the call to op->handler be
3984                            protected by the lock? */
3985                         g_static_rec_mutex_unlock (&bl->priv->op_hash_mutex);
3986
3987                         ldap_msgfree(res);
3988                 }
3989         }
3990
3991         return TRUE;
3992 }
3993
3994 static void
3995 ldap_search_handler (LDAPOp *op, LDAPMessage *res)
3996 {
3997         LDAPSearchOp *search_op = (LDAPSearchOp*)op;
3998         EDataBookView *view = search_op->view;
3999         EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (op->backend);
4000         LDAP *ldap;
4001         LDAPMessage *e;
4002         int msg_type;
4003         GTimeVal start, end;
4004         unsigned long diff;
4005
4006         d(printf ("ldap_search_handler (%p)\n", view));
4007         if (enable_debug)
4008                 g_get_current_time(&start);
4009
4010         ldap = bl->priv->ldap;
4011         if (!ldap) {
4012                 e_data_book_view_notify_complete (view, GNOME_Evolution_Addressbook_OtherError);
4013                 ldap_op_finished (op);
4014                 if (enable_debug)
4015                         printf ("ldap_search_handler... ldap handler is NULL \n");
4016                 return;
4017         }
4018
4019         if (!search_op->notified_receiving_results) {
4020                 search_op->notified_receiving_results = TRUE;
4021                 book_view_notify_status (op->view, _("Receiving LDAP search results..."));
4022         }
4023
4024         msg_type = ldap_msgtype (res);
4025         if (msg_type == LDAP_RES_SEARCH_ENTRY) {
4026                 e = ldap_first_entry (ldap, res);
4027
4028                 while (NULL != e) {
4029                         EContact *contact = build_contact_from_entry (bl, e, NULL);
4030
4031                         e_data_book_view_notify_update (view, contact);
4032                         g_object_unref (contact);
4033
4034                         e = ldap_next_entry(ldap, e);
4035                 }
4036         }
4037         else if (msg_type == LDAP_RES_SEARCH_RESULT) {
4038                 char *ldap_error_msg;
4039                 int ldap_error;
4040
4041                 g_static_rec_mutex_lock (&eds_ldap_handler_lock);
4042                 ldap_parse_result (ldap, res, &ldap_error,
4043                                    NULL, &ldap_error_msg, NULL, NULL, 0);
4044                 g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
4045                 if (ldap_error != LDAP_SUCCESS) {
4046                         g_warning ("ldap_search_handler: %02X (%s), additional info: %s",
4047                                    ldap_error,
4048                                    ldap_err2string (ldap_error), ldap_error_msg);
4049                 }
4050                 ldap_memfree (ldap_error_msg);
4051
4052                 if (ldap_error == LDAP_TIMELIMIT_EXCEEDED)
4053                         e_data_book_view_notify_complete (view, GNOME_Evolution_Addressbook_SearchTimeLimitExceeded);
4054                 else if (ldap_error == LDAP_SIZELIMIT_EXCEEDED)
4055                         e_data_book_view_notify_complete (view, GNOME_Evolution_Addressbook_SearchSizeLimitExceeded);
4056                 else if (ldap_error == LDAP_SUCCESS)
4057                         e_data_book_view_notify_complete (view, GNOME_Evolution_Addressbook_Success);
4058                 else
4059                         e_data_book_view_notify_complete (view, GNOME_Evolution_Addressbook_OtherError);
4060
4061                 ldap_op_finished (op);
4062                 if (enable_debug) {
4063                         g_get_current_time (&end);
4064                         diff = end.tv_sec * 1000 + end.tv_usec/1000;
4065                         diff -= start.tv_sec * 1000 + start.tv_usec/1000;
4066                         printf ("ldap_search_handler... completed with error code %d  ", ldap_error);
4067                         printf ("and took %ld.%03ld seconds\n", diff/1000, diff%1000);
4068                 }
4069         }
4070         else {
4071                 g_warning ("unhandled search result type %d returned", msg_type);
4072                 //e_data_book_view_notify_complete (view, GNOME_Evolution_Addressbook_OtherError);
4073                 e_data_book_view_notify_complete (view, GNOME_Evolution_Addressbook_InvalidQuery);
4074                 ldap_op_finished (op);
4075         }
4076 }
4077
4078 static void
4079 ldap_search_dtor (LDAPOp *op)
4080 {
4081         LDAPSearchOp *search_op = (LDAPSearchOp*) op;
4082
4083         d(printf ("ldap_search_dtor (%p)\n", search_op->view));
4084
4085         /* unhook us from our EDataBookView */
4086         g_object_set_data (G_OBJECT (search_op->view), "EBookBackendLDAP.BookView::search_op", NULL);
4087
4088         bonobo_object_unref (search_op->view);
4089
4090         if (!search_op->aborted)
4091                 g_free (search_op);
4092 }
4093
4094 static void
4095 e_book_backend_ldap_search (EBookBackendLDAP *bl,
4096                             EDataBook        *book,
4097                             EDataBookView    *view)
4098 {
4099         char *ldap_query;
4100         GList *contacts;
4101         GList *l;
4102         GTimeVal start, end;
4103         unsigned long diff;
4104
4105         if (enable_debug) {
4106                 printf ("e_book_backend_ldap_search ... \n");
4107                 g_get_current_time (&start);
4108         }
4109
4110         switch (bl->priv->mode) {
4111         case GNOME_Evolution_Addressbook_MODE_LOCAL :
4112                 if (!(bl->priv->marked_for_offline && bl->priv->cache)) {
4113                         e_data_book_view_notify_complete (view, GNOME_Evolution_Addressbook_RepositoryOffline);
4114                         return;
4115                 }
4116
4117                 contacts = e_book_backend_cache_get_contacts (bl->priv->cache,
4118                                                               e_data_book_view_get_card_query (view));
4119
4120                 for (l = contacts; l; l = g_list_next (l)) {
4121                         EContact *contact = l->data;
4122                         e_data_book_view_notify_update (view, contact);
4123                         g_object_unref (contact);
4124                 }
4125
4126                 g_list_free (contacts);
4127
4128                 e_data_book_view_notify_complete (view, GNOME_Evolution_Addressbook_Success);
4129                 return;
4130                 
4131         case GNOME_Evolution_Addressbook_MODE_REMOTE :
4132                 ldap_query = e_book_backend_ldap_build_query (bl, e_data_book_view_get_card_query (view));
4133
4134                 if (ldap_query != NULL && bl->priv->ldap) {
4135                         LDAP *ldap;
4136                         int ldap_err;
4137                         int search_msgid;
4138                         int view_limit;
4139
4140                         view_limit = e_data_book_view_get_max_results (view);
4141                         if (view_limit == -1 || view_limit > bl->priv->ldap_limit)
4142                                 view_limit = bl->priv->ldap_limit;
4143
4144                         printf ("searching server using filter: %s (expecting max %d results)\n", ldap_query,
4145                                 view_limit);
4146
4147                         ldap = bl->priv->ldap;
4148
4149                         do {
4150                                 book_view_notify_status (view, _("Searching..."));
4151
4152                                 ldap_err = ldap_search_ext (ldap, bl->priv->ldap_rootdn,
4153                                                             bl->priv->ldap_scope,
4154                                                             ldap_query,
4155                                                             NULL, 0,
4156                                                             NULL, /* XXX */
4157                                                             NULL, /* XXX */
4158                                                             NULL, /* XXX timeout */
4159                                                             view_limit, &search_msgid);
4160                         } while (e_book_backend_ldap_reconnect (bl, view, ldap_err));
4161
4162                         g_free (ldap_query);
4163
4164                         if (ldap_err != LDAP_SUCCESS) {
4165                                 book_view_notify_status (view, ldap_err2string(ldap_err));
4166                                 return;
4167                         }
4168                         else if (search_msgid == -1) {
4169                                 book_view_notify_status (view,
4170                                                          _("Error performing search"));
4171                                 return;
4172                         }
4173                         else {
4174                                 LDAPSearchOp *op = g_new0 (LDAPSearchOp, 1);
4175
4176                                 d(printf ("adding search_op (%p, %d)\n", view, search_msgid));
4177
4178                                 op->view = view;
4179                                 op->aborted = FALSE;
4180                                 bonobo_object_ref (view);
4181
4182                                 ldap_op_add ((LDAPOp*)op, E_BOOK_BACKEND(bl), book, view, 
4183                                              0, search_msgid,
4184                                              ldap_search_handler, ldap_search_dtor);
4185
4186                                 if (enable_debug) {
4187                                         printf ("e_book_backend_ldap_search invoked ldap_search_handler ");
4188                                         g_get_current_time (&end);
4189                                         diff = end.tv_sec * 1000 + end.tv_usec/1000;
4190                                         diff -= start.tv_sec * 1000 + start.tv_usec/1000;
4191                                         printf("and took  %ld.%03ld seconds\n", diff/1000,diff%1000);
4192                                 }
4193
4194                                 g_object_set_data (G_OBJECT (view), "EBookBackendLDAP.BookView::search_op", op);
4195                         }
4196                         return;
4197                 }
4198                 else {
4199                         /*
4200                         e_data_book_view_notify_complete (view,
4201                                                           GNOME_Evolution_Addressbook_InvalidQuery);
4202                         */
4203                         /* Ignore NULL query */
4204                         e_data_book_view_notify_complete (view,
4205                                                           GNOME_Evolution_Addressbook_Success);
4206                         return;
4207                 }
4208         }
4209 }
4210
4211 static void
4212 e_book_backend_ldap_start_book_view (EBookBackend  *backend,
4213                                      EDataBookView *view)
4214 {
4215         EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (backend);
4216
4217         d(printf ("start_book_view (%p)\n", view));
4218
4219         /* we start at 1 so the user sees stuff as it appears and we
4220            aren't left waiting for more cards to show up, if the
4221            connection is slow. */
4222         e_data_book_view_set_thresholds (view, 1, 3000);
4223
4224         e_book_backend_ldap_search (bl, NULL /* XXX ugh */, view);
4225 }
4226
4227 static void
4228 e_book_backend_ldap_stop_book_view (EBookBackend  *backend,
4229                                     EDataBookView *view)
4230 {
4231         LDAPSearchOp *op;
4232
4233         d(printf ("stop_book_view (%p)\n", view));
4234
4235         op = g_object_get_data (G_OBJECT (view), "EBookBackendLDAP.BookView::search_op");
4236         if (op) {
4237                 op->aborted = TRUE;
4238                 ldap_op_finished ((LDAPOp*)op);
4239                 g_free (op);
4240         }
4241 }
4242
4243 static void
4244 e_book_backend_ldap_get_changes (EBookBackend *backend,
4245                                  EDataBook    *book,
4246                                  guint32       opid,
4247                                  const char   *change_id)
4248 {
4249         /* FIXME: implement */
4250 }
4251
4252 #define LDAP_SIMPLE_PREFIX "ldap/simple-"
4253 #define SASL_PREFIX "sasl/"
4254
4255 static void
4256 generate_cache_handler (LDAPOp *op, LDAPMessage *res)
4257 {
4258         LDAPGetContactListOp *contact_list_op = (LDAPGetContactListOp *) op;
4259         EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (op->backend);
4260         LDAP *ldap;
4261         LDAPMessage *e;
4262         gint msg_type;
4263         EDataBookView *book_view;
4264         GTimeVal start, end;
4265         unsigned long diff;
4266
4267         if (enable_debug) {
4268                 printf ("generate_cache_handler ... \n");
4269                 g_get_current_time (&start);
4270         }
4271         ldap = bl->priv->ldap;
4272         if (!ldap) {
4273                 ldap_op_finished (op);
4274                 if (enable_debug)
4275                         printf ("generate_cache_handler ... ldap handler is NULL \n");
4276                 return;
4277         }
4278
4279         book_view = find_book_view (bl);
4280
4281         msg_type = ldap_msgtype (res);
4282         if (msg_type == LDAP_RES_SEARCH_ENTRY) {
4283                 g_static_rec_mutex_lock (&eds_ldap_handler_lock);
4284                 e = ldap_first_entry(ldap, res);
4285                 g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
4286
4287                 while (e != NULL) {
4288                         EContact *contact = build_contact_from_entry (bl, e, NULL);
4289
4290                         contact_list_op->contacts = g_list_prepend (contact_list_op->contacts, contact);
4291
4292                         g_static_rec_mutex_lock (&eds_ldap_handler_lock);
4293                         e = ldap_next_entry(ldap, e);
4294                         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
4295                 }
4296         } else {
4297                 GList *l;
4298                 int contact_num = 0;
4299                 char *status_msg;
4300                 
4301                 e_file_cache_clean (E_FILE_CACHE (bl->priv->cache));
4302
4303                 e_file_cache_freeze_changes (E_FILE_CACHE (bl->priv->cache));
4304                 for (l = contact_list_op->contacts; l; l = g_list_next (l)) {
4305                         EContact *contact = l->data;
4306
4307                         contact_num++;
4308                         if (book_view) {
4309                                 status_msg = g_strdup_printf (_("Downloading contacts (%d)... "),
4310                                                                  contact_num);
4311                                 e_data_book_view_notify_status_message (book_view, status_msg);
4312                                 g_free (status_msg); 
4313                         }
4314                         e_book_backend_cache_add_contact (bl->priv->cache, contact);
4315                 }
4316                 e_book_backend_cache_set_populated (bl->priv->cache);
4317                 e_file_cache_thaw_changes (E_FILE_CACHE (bl->priv->cache));
4318                 if (book_view)
4319                         e_data_book_view_notify_complete (book_view,
4320                                                           GNOME_Evolution_Addressbook_Success);
4321                 ldap_op_finished (op);
4322                 if (enable_debug) {
4323                         g_get_current_time (&end);
4324                         diff = end.tv_sec * 1000 + end.tv_usec/1000;
4325                         diff -= start.tv_sec * 1000 + start.tv_usec/1000;
4326                         printf ("generate_cache_handler ... completed in %ld.%03ld seconds\n",
4327                                                                 diff/1000,diff%1000);
4328                 }
4329         }
4330 }
4331
4332 static void
4333 generate_cache_dtor (LDAPOp *op)
4334 {
4335         LDAPGetContactListOp *contact_list_op = (LDAPGetContactListOp *) op;
4336         GList *l;
4337
4338         for (l = contact_list_op->contacts; l; l = g_list_next (l)) {
4339                 g_object_unref (l->data);
4340         }
4341
4342         g_list_free (contact_list_op->contacts);
4343         g_free (contact_list_op);
4344 }
4345
4346 static void
4347 generate_cache (EBookBackendLDAP *book_backend_ldap)
4348 {
4349         LDAPGetContactListOp *contact_list_op = g_new0 (LDAPGetContactListOp, 1);
4350         EBookBackendLDAPPrivate *priv;
4351         gchar *ldap_query;
4352         gint contact_list_msgid;
4353         gint ldap_error;
4354         GTimeVal start, end;
4355         unsigned long diff;
4356         
4357         if (enable_debug) {
4358                 printf ("generating offline cache ... \n");
4359                 g_get_current_time (&start);
4360         }
4361
4362         priv = book_backend_ldap->priv;
4363
4364         if (!priv->ldap) {
4365                 g_free (contact_list_op);
4366                 if (enable_debug)
4367                         printf ("generating offline cache failed ... ldap handler is NULL\n");
4368                 return;
4369         }
4370
4371         ldap_query = e_book_backend_ldap_build_query (book_backend_ldap, 
4372                                                       "(beginswith \"file_as\" \"\")");
4373
4374         do {
4375                 ldap_error = ldap_search_ext (priv->ldap,
4376                                               priv->ldap_rootdn,
4377                                               priv->ldap_scope,
4378                                               ldap_query,
4379                                               NULL, 0, NULL, NULL,
4380                                               NULL, /* XXX timeout */
4381                                               LDAP_NO_LIMIT, &contact_list_msgid);
4382         } while (e_book_backend_ldap_reconnect (book_backend_ldap, NULL, ldap_error));
4383
4384         g_free (ldap_query);
4385
4386         if (ldap_error == LDAP_SUCCESS) {
4387                 ldap_op_add ((LDAPOp*) contact_list_op, (EBookBackend *) book_backend_ldap, NULL /* book */,
4388                              NULL /* book_view */, 0 /* opid */, contact_list_msgid,
4389                              generate_cache_handler, generate_cache_dtor);
4390                 if (enable_debug) {
4391                         printf ("generating offline cache invoked generate_cache_handler ");
4392                         g_get_current_time (&end);
4393                         diff = end.tv_sec * 1000 + end.tv_usec/1000;
4394                         diff -= start.tv_sec * 1000 + start.tv_usec/1000;
4395                         printf("and took %ld.%03ld seconds\n", diff/1000, diff%1000);
4396                 }
4397         } else {
4398                 generate_cache_dtor ((LDAPOp *) contact_list_op);
4399         }
4400 }
4401
4402 static void
4403 e_book_backend_ldap_authenticate_user (EBookBackend *backend,
4404                                        EDataBook    *book,
4405                                        guint32       opid,
4406                                        const char   *user,
4407                                        const char   *passwd,
4408                                        const char   *auth_method)
4409 {
4410         EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (backend);
4411         int ldap_error;
4412         int status;
4413         char *dn = NULL;
4414
4415         if (enable_debug)
4416                 printf ("e_book_backend_ldap_authenticate_user ... \n");
4417
4418         if (bl->priv->mode == GNOME_Evolution_Addressbook_MODE_LOCAL) {
4419                 e_book_backend_notify_writable (backend, FALSE);
4420                 e_book_backend_notify_connection_status (backend, FALSE);
4421                 e_data_book_respond_authenticate_user (book,
4422                                                        opid,
4423                                                        GNOME_Evolution_Addressbook_Success);
4424                 return;
4425         }
4426
4427         if (!bl->priv->connected || !bl->priv->ldap) {
4428
4429                 status = e_book_backend_ldap_connect (bl);
4430                 if (status != GNOME_Evolution_Addressbook_Success) {
4431                         e_data_book_respond_authenticate_user (book,
4432                                                                opid, status);
4433                         return ;
4434                 }
4435                                                        
4436         }
4437
4438         if (!g_ascii_strncasecmp (auth_method, LDAP_SIMPLE_PREFIX, strlen (LDAP_SIMPLE_PREFIX))) {
4439
4440                 if (!strcmp (auth_method, "ldap/simple-email")) {
4441                         LDAPMessage    *res, *e;
4442                         char *query = g_strdup_printf ("(mail=%s)", user);
4443
4444                         g_static_rec_mutex_lock (&eds_ldap_handler_lock);
4445                         ldap_error = ldap_search_s (bl->priv->ldap,
4446                                                     bl->priv->ldap_rootdn,
4447                                                     bl->priv->ldap_scope,
4448                                                     query,
4449                                                     NULL, 0, &res);
4450                         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
4451                         g_free (query);
4452
4453                         if (ldap_error == LDAP_SUCCESS) {
4454                                 char *entry_dn;
4455
4456                                 g_static_rec_mutex_lock (&eds_ldap_handler_lock);
4457                                 e = ldap_first_entry (bl->priv->ldap, res);
4458                                 g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
4459                                 if (!e) {
4460                                         g_warning ("Failed to get the DN for %s", user);
4461                                         ldap_msgfree (res);
4462                                         e_data_book_respond_authenticate_user (book,
4463                                                                                opid,
4464                                                                                GNOME_Evolution_Addressbook_AuthenticationFailed);
4465                                         return;
4466                                 }
4467
4468                                 g_static_rec_mutex_lock (&eds_ldap_handler_lock);
4469                                 entry_dn = ldap_get_dn (bl->priv->ldap, e);
4470                                 g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
4471                                 dn = g_strdup(entry_dn);
4472
4473                                 ldap_memfree (entry_dn);
4474                                 ldap_msgfree (res);
4475                         }
4476                         else {
4477                                 e_data_book_respond_authenticate_user (book,
4478                                                                        opid,
4479                                                                        GNOME_Evolution_Addressbook_PermissionDenied);
4480                                 return;
4481                         }
4482                 }
4483                 else if (!strcmp (auth_method, "ldap/simple-binddn")) {
4484                         dn = g_strdup (user);
4485                 }
4486
4487                 /* now authenticate against the DN we were either supplied or queried for */
4488                 printf ("simple auth as %s\n", dn);
4489                 g_static_rec_mutex_lock (&eds_ldap_handler_lock);
4490                 ldap_error = ldap_simple_bind_s(bl->priv->ldap,
4491                                                 dn,
4492                                                 passwd);
4493                 g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
4494                 /* Some ldap servers are returning (ex active directory ones) LDAP_SERVER_DOWN
4495                  * when we try to do an ldap operation  after being  idle 
4496                  * for some time. This error is handled by poll_ldap in case of search operations
4497                  * We need to handle it explicitly for this bind call. We call reconnect so that
4498                  * we get a fresh ldap handle Fixes #67541 */
4499
4500                 if (ldap_error == LDAP_SERVER_DOWN) {
4501                         EDataBookView *view = find_book_view (bl);
4502
4503                         if (e_book_backend_ldap_reconnect (bl, view, ldap_error)) {
4504                                 ldap_error = LDAP_SUCCESS;
4505                         }
4506
4507                 }
4508                                 
4509                 e_data_book_respond_authenticate_user (book,
4510                                                        opid,
4511                                                        ldap_error_to_response (ldap_error));
4512         }
4513 #ifdef ENABLE_SASL_BINDS
4514         else if (!g_ascii_strncasecmp (auth_method, SASL_PREFIX, strlen (SASL_PREFIX))) {
4515                 g_print ("sasl bind (mech = %s) as %s", auth_method + strlen (SASL_PREFIX), user);
4516                 g_static_rec_mutex_lock (&eds_ldap_handler_lock);
4517                 ldap_error = ldap_sasl_bind_s (bl->priv->ldap,
4518                                                NULL,
4519                                                auth_method + strlen (SASL_PREFIX),
4520                                                passwd,
4521                                                NULL,
4522                                                NULL,
4523                                                NULL);
4524                 g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
4525
4526                 if (ldap_error == LDAP_NOT_SUPPORTED)
4527                         e_data_book_respond_authenticate_user (book,
4528                                                                opid,
4529                                                                GNOME_Evolution_Addressbook_UnsupportedAuthenticationMethod);
4530                 else
4531                         e_data_book_respond_authenticate_user (book,
4532                                                                opid,
4533                                                                ldap_error_to_response (ldap_error));
4534         }
4535 #endif
4536         else {
4537                 e_data_book_respond_authenticate_user (book,
4538                                                        opid,
4539                                                        GNOME_Evolution_Addressbook_UnsupportedAuthenticationMethod);
4540                 return;
4541         }
4542
4543         if (ldap_error == LDAP_SUCCESS) {
4544                 bl->priv->auth_dn = dn;
4545                 bl->priv->auth_passwd = g_strdup (passwd);
4546
4547                 e_book_backend_set_is_writable (backend, TRUE);
4548
4549                 /* force a requery on the root dse since some ldap
4550                    servers are set up such that they don't report
4551                    anything (including the schema DN) until the user
4552                    is authenticated */
4553                 if (!bl->priv->evolutionPersonChecked) {
4554                         ldap_error = query_ldap_root_dse (bl);
4555
4556                         if (LDAP_SUCCESS == ldap_error) {
4557                                 if (!bl->priv->evolutionPersonChecked)
4558                                         check_schema_support (bl);
4559                         }
4560                         else
4561                                 g_warning ("Failed to perform root dse query after authenticating, (ldap_error 0x%02x)", ldap_error);
4562                 }
4563
4564                 e_data_book_report_writable (book, TRUE);
4565
4566                 if (bl->priv->marked_for_offline && bl->priv->cache)
4567                         generate_cache (bl);
4568         }
4569 }
4570
4571 static void
4572 e_book_backend_ldap_get_required_fields (EBookBackend *backend,
4573                                           EDataBook    *book,
4574                                           guint32       opid)
4575
4576 {
4577         GList *fields = NULL;
4578         
4579         
4580         /*FIMEME we should look at mandatory attributs in the schema
4581           and return all those fields here */
4582         fields = g_list_append (fields, (char *)e_contact_field_name (E_CONTACT_FILE_AS));
4583         fields = g_list_append (fields, (char *)e_contact_field_name (E_CONTACT_FULL_NAME));
4584         fields = g_list_append (fields, (char *)e_contact_field_name (E_CONTACT_FAMILY_NAME));
4585         
4586         
4587         e_data_book_respond_get_required_fields (book,
4588                                                   opid,
4589                                                   GNOME_Evolution_Addressbook_Success,
4590                                                   fields);
4591         g_list_free (fields);
4592 }
4593
4594
4595 static void
4596 e_book_backend_ldap_get_supported_fields (EBookBackend *backend,
4597                                           EDataBook    *book,
4598                                           guint32       opid)
4599
4600 {
4601         EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (backend);
4602
4603         e_data_book_respond_get_supported_fields (book,
4604                                                   opid,
4605                                                   GNOME_Evolution_Addressbook_Success,
4606                                                   bl->priv->supported_fields);
4607 }
4608
4609 static void
4610 e_book_backend_ldap_get_supported_auth_methods (EBookBackend *backend,
4611                                                 EDataBook    *book,
4612                                                 guint32       opid)
4613
4614 {
4615         EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (backend);
4616
4617         e_data_book_respond_get_supported_auth_methods (book,
4618                                                         opid,
4619                                                         GNOME_Evolution_Addressbook_Success,
4620                                                         bl->priv->supported_auth_methods);
4621 }
4622
4623 static void
4624 ldap_cancel_op(void *key, void *value, void *data)
4625 {
4626         EBookBackendLDAP *bl = data;
4627         LDAPOp *op = value;
4628
4629         /* ignore errors, its only best effort? */
4630         g_static_rec_mutex_lock (&eds_ldap_handler_lock);
4631         if (bl->priv->ldap)
4632                 ldap_abandon (bl->priv->ldap, op->id);
4633         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
4634 }
4635
4636 static GNOME_Evolution_Addressbook_CallStatus
4637 e_book_backend_ldap_cancel_operation (EBookBackend *backend, EDataBook *book)
4638 {
4639         EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (backend);
4640
4641         g_static_rec_mutex_lock (&bl->priv->op_hash_mutex);
4642         g_hash_table_foreach (bl->priv->id_to_op, ldap_cancel_op, bl);
4643         g_static_rec_mutex_unlock (&bl->priv->op_hash_mutex);
4644
4645         return GNOME_Evolution_Addressbook_Success;
4646 }
4647
4648 static GNOME_Evolution_Addressbook_CallStatus
4649 e_book_backend_ldap_load_source (EBookBackend             *backend,
4650                                  ESource                  *source,
4651                                  gboolean                  only_if_exists)
4652 {
4653         EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (backend);
4654         LDAPURLDesc    *lud;
4655         int ldap_error;
4656         int limit = 100;
4657         int timeout = 60; /* 1 minute */
4658         gchar *uri;
4659         const char *str;
4660         const char *offline;
4661         gint result;
4662
4663         g_assert (bl->priv->connected == FALSE);
4664
4665         if (enable_debug)
4666                 printf ("e_book_backend_ldap_load_source ... \n");
4667
4668         uri = e_source_get_uri (source);
4669
4670         offline = e_source_get_property (source, "offline_sync");
4671         if (offline  &&   g_str_equal (offline, "1"))
4672                 bl->priv->marked_for_offline = TRUE;
4673         str = e_source_get_property (source, "limit");
4674         if (str)
4675                 limit = atoi (str);
4676
4677         str = e_source_get_property (source, "ssl");
4678         if (str) {
4679                 if (!strcmp (str, "always"))
4680                         bl->priv->use_tls = E_BOOK_BACKEND_LDAP_TLS_ALWAYS;
4681                 else if (!strcmp (str, "whenever_possible"))
4682                         bl->priv->use_tls = E_BOOK_BACKEND_LDAP_TLS_WHEN_POSSIBLE;
4683                 else if (strcmp (str, "never"))
4684                         g_warning ("Unhandled value for 'ssl', not using it.");
4685         }
4686         else
4687                 bl->priv->use_tls = E_BOOK_BACKEND_LDAP_TLS_NO;
4688
4689         str = e_source_get_property (source, "timeout");
4690         if (str)
4691                 timeout = atoi (str);
4692
4693         ldap_error = ldap_url_parse ((char*) uri, &lud);
4694
4695         if (ldap_error == LDAP_SUCCESS) {
4696                 bl->priv->ldap_host = g_strdup(lud->lud_host);
4697                 bl->priv->ldap_port = lud->lud_port;
4698                 /* if a port wasn't specified, default to LDAP_PORT */
4699                 if (bl->priv->ldap_port == 0)
4700                         bl->priv->ldap_port = LDAP_PORT;
4701                 bl->priv->ldap_rootdn = g_strdup(lud->lud_dn);
4702                 /* in case of migration, filter will be set to NULL and hence the search will fail */
4703                 if (lud->lud_filter)
4704                         bl->priv->ldap_search_filter = g_strdup (lud->lud_filter);
4705                 bl->priv->ldap_limit = limit;
4706                 bl->priv->ldap_timeout = timeout;
4707                 bl->priv->ldap_scope = lud->lud_scope;
4708
4709                 ldap_free_urldesc(lud);
4710         } else {
4711                 if (enable_debug)
4712                         printf ("e_book_backend_ldap_load_source ... failed to parse the ldap URI %s\n", uri);
4713                 g_free (uri);
4714                 return GNOME_Evolution_Addressbook_OtherError;
4715         }
4716
4717         if (bl->priv->cache) {
4718                 g_object_unref (bl->priv->cache);
4719                 bl->priv->cache = NULL;
4720         }
4721
4722         bl->priv->cache = e_book_backend_cache_new (uri);
4723         g_free (uri);
4724
4725         if (bl->priv->mode == GNOME_Evolution_Addressbook_MODE_LOCAL) {
4726                 /* Offline */
4727
4728                 e_book_backend_set_is_loaded (backend, TRUE);
4729                 e_book_backend_set_is_writable (backend, FALSE);
4730                 e_book_backend_notify_writable (backend, FALSE);
4731                 e_book_backend_notify_connection_status (backend, FALSE);
4732
4733                 if (!bl->priv->marked_for_offline)
4734                         return GNOME_Evolution_Addressbook_OfflineUnavailable;
4735
4736 #if 0
4737                 if (!e_book_backend_cache_is_populated (bl->priv->cache))
4738                         return GNOME_Evolution_Addressbook_OfflineUnavailable;
4739 #endif
4740
4741                 return GNOME_Evolution_Addressbook_Success;
4742         }
4743         else 
4744                 e_book_backend_notify_connection_status (backend, TRUE);
4745
4746         /* Online */
4747
4748         result = e_book_backend_ldap_connect (bl);
4749         if (result != GNOME_Evolution_Addressbook_Success) {
4750                 if (enable_debug)
4751                         printf ("e_book_backend_ldap_load_source ... failed to connect to server \n");
4752                 return result;
4753         }
4754
4755         if (bl->priv->marked_for_offline)
4756                 generate_cache (bl);
4757
4758         return result;
4759 }
4760
4761 static void
4762 e_book_backend_ldap_remove (EBookBackend *backend, EDataBook *book, guint32 opid)
4763 {
4764         /* if we ever add caching, we'll remove it here, but for now,
4765            just report back Success */
4766
4767         e_data_book_respond_remove (book, opid, GNOME_Evolution_Addressbook_Success);
4768 }
4769
4770 static char*
4771 e_book_backend_ldap_get_static_capabilities (EBookBackend *backend)
4772 {
4773         return g_strdup("net,anon-access,do-initial-query,contact-lists");
4774 }
4775
4776 #if 0
4777 static void
4778 stop_views (EBookBackend *backend)
4779 {
4780         EList     *book_views;
4781         EIterator *iter;
4782
4783         book_views = e_book_backend_get_book_views (backend);
4784         iter = e_list_get_iterator (book_views);
4785
4786         while (e_iterator_is_valid (iter)) {
4787                 EDataBookView *data_book_view = (EDataBookView *) e_iterator_get (iter);
4788                 e_book_backend_ldap_stop_book_view (backend, data_book_view);
4789                 e_iterator_next (iter);
4790         }
4791
4792         g_object_unref (iter);
4793         g_object_unref (book_views);
4794 }
4795
4796 static void
4797 start_views (EBookBackend *backend)
4798 {
4799         EList     *book_views;
4800         EIterator *iter;
4801
4802         book_views = e_book_backend_get_book_views (backend);
4803         iter = e_list_get_iterator (book_views);
4804
4805         while (e_iterator_is_valid (iter)) {
4806                 EDataBookView *data_book_view = (EDataBookView *) e_iterator_get (iter);
4807                 e_book_backend_ldap_start_book_view (backend, data_book_view);
4808                 e_iterator_next (iter);
4809         }
4810
4811         g_object_unref (iter);
4812         g_object_unref (book_views);
4813 }
4814 #endif
4815
4816 static void 
4817 e_book_backend_ldap_set_mode (EBookBackend *backend, int mode)
4818 {
4819         EBookBackendLDAP *bl = E_BOOK_BACKEND_LDAP (backend);
4820
4821         if (bl->priv->mode == mode)
4822                 return;
4823
4824         bl->priv->mode = mode;
4825 #if 0
4826         stop_views (backend);
4827 #endif
4828
4829         /* Cancel all running operations */
4830         e_book_backend_ldap_cancel_operation (backend, NULL);
4831
4832         if (mode == GNOME_Evolution_Addressbook_MODE_LOCAL) {
4833                 /* Go offline */
4834
4835                 e_book_backend_set_is_writable (backend, FALSE);
4836                 e_book_backend_notify_writable (backend, FALSE);
4837                 e_book_backend_notify_connection_status (backend, FALSE);
4838
4839 #if 0
4840                 if (bl->priv->ldap) {
4841                         ldap_unbind (bl->priv->ldap);
4842                         bl->priv->ldap = NULL;
4843                 }
4844 #endif
4845
4846                 bl->priv->connected = FALSE;
4847
4848 #if 0
4849                 if (e_book_backend_is_loaded (backend))
4850                         start_views (backend);
4851 #endif
4852         }
4853         else if (mode == GNOME_Evolution_Addressbook_MODE_REMOTE) {
4854                 /* Go online */
4855         
4856                 e_book_backend_set_is_writable (backend, TRUE);
4857                 e_book_backend_notify_writable (backend, TRUE);
4858                 e_book_backend_notify_connection_status (backend, TRUE);
4859
4860                 if (e_book_backend_is_loaded (backend)) {
4861                         e_book_backend_ldap_connect (bl);
4862                         e_book_backend_notify_auth_required (backend);
4863
4864 #if 0
4865                         start_views (backend);
4866 #endif
4867
4868                         if (bl->priv->marked_for_offline && bl->priv->cache)
4869                                 generate_cache (bl);
4870                 }
4871         }
4872 }
4873
4874 static gboolean
4875 e_book_backend_ldap_construct (EBookBackendLDAP *backend)
4876 {
4877         g_assert (backend != NULL);
4878         g_assert (E_IS_BOOK_BACKEND_LDAP (backend));
4879
4880         if (! e_book_backend_construct (E_BOOK_BACKEND (backend)))
4881                 return FALSE;
4882
4883         return TRUE;
4884 }
4885
4886 /**
4887  * e_book_backend_ldap_new:
4888  */
4889 EBookBackend *
4890 e_book_backend_ldap_new (void)
4891 {
4892         EBookBackendLDAP *backend;
4893
4894         backend = g_object_new (E_TYPE_BOOK_BACKEND_LDAP, NULL);
4895
4896         if (! e_book_backend_ldap_construct (backend)) {
4897                 g_object_unref (backend);
4898                 return NULL;
4899         }
4900
4901         return E_BOOK_BACKEND (backend);
4902 }
4903
4904 static gboolean
4905 call_dtor (int msgid, LDAPOp *op, gpointer data)
4906 {
4907         EBookBackendLDAP *bl;
4908
4909         bl = E_BOOK_BACKEND_LDAP (op->backend);
4910
4911         g_static_rec_mutex_lock (&eds_ldap_handler_lock);
4912         if (bl->priv->ldap)
4913                 ldap_abandon (bl->priv->ldap, op->id);
4914         g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
4915
4916         op->dtor (op);
4917
4918         return TRUE;
4919 }
4920
4921 static void
4922 e_book_backend_ldap_dispose (GObject *object)
4923 {
4924         EBookBackendLDAP *bl;
4925
4926         bl = E_BOOK_BACKEND_LDAP (object);
4927
4928         if (bl->priv) {
4929                 g_static_rec_mutex_lock (&bl->priv->op_hash_mutex);
4930                 g_hash_table_foreach_remove (bl->priv->id_to_op, (GHRFunc)call_dtor, NULL);
4931                 g_hash_table_destroy (bl->priv->id_to_op);
4932                 g_static_rec_mutex_unlock (&bl->priv->op_hash_mutex);
4933                 g_static_rec_mutex_free (&bl->priv->op_hash_mutex);
4934
4935                 g_static_rec_mutex_lock (&eds_ldap_handler_lock);       
4936                 if (bl->priv->ldap)
4937                         ldap_unbind (bl->priv->ldap);
4938                 g_static_rec_mutex_unlock (&eds_ldap_handler_lock);
4939
4940                 if (bl->priv->poll_timeout != -1) {
4941                         g_source_remove (bl->priv->poll_timeout);
4942                 }
4943
4944                 if (bl->priv->supported_fields) {
4945                         g_list_foreach (bl->priv->supported_fields, (GFunc)g_free, NULL);
4946                         g_list_free (bl->priv->supported_fields);
4947                 }
4948
4949                 if (bl->priv->supported_auth_methods) {
4950                         g_list_foreach (bl->priv->supported_auth_methods, (GFunc)g_free, NULL);
4951                         g_list_free (bl->priv->supported_auth_methods);
4952                 }
4953                 if (bl->priv->summary_file_name) {
4954                         g_free (bl->priv->summary_file_name);
4955                         bl->priv->summary_file_name = NULL;
4956                 }
4957                 if (bl->priv->summary) {
4958                         e_book_backend_summary_save (bl->priv->summary);
4959                         g_object_unref (bl->priv->summary);
4960                         bl->priv->summary = NULL;
4961                 }
4962
4963                 g_free (bl->priv->ldap_host);
4964                 g_free (bl->priv->ldap_rootdn);
4965                 g_free (bl->priv->ldap_search_filter);
4966                 g_free (bl->priv->schema_dn);
4967                 g_free (bl->priv);
4968                 bl->priv = NULL;
4969         }
4970
4971         if (G_OBJECT_CLASS (e_book_backend_ldap_parent_class)->dispose)
4972                 G_OBJECT_CLASS (e_book_backend_ldap_parent_class)->dispose (object);
4973 }
4974
4975 static void
4976 e_book_backend_ldap_class_init (EBookBackendLDAPClass *klass)
4977 {
4978         GObjectClass  *object_class = G_OBJECT_CLASS (klass);
4979         EBookBackendClass *parent_class;
4980
4981 #ifndef SUNLDAP
4982         /* get client side information (extensions present in the library) */
4983         get_ldap_library_info ();
4984 #endif
4985         e_book_backend_ldap_parent_class = g_type_class_peek_parent (klass);
4986
4987         parent_class = E_BOOK_BACKEND_CLASS (klass);
4988
4989         /* Set the virtual methods. */
4990         parent_class->load_source             = e_book_backend_ldap_load_source;
4991         parent_class->remove                  = e_book_backend_ldap_remove;
4992         parent_class->get_static_capabilities = e_book_backend_ldap_get_static_capabilities;
4993
4994         parent_class->create_contact          = e_book_backend_ldap_create_contact;
4995         parent_class->remove_contacts         = e_book_backend_ldap_remove_contacts;
4996         parent_class->modify_contact          = e_book_backend_ldap_modify_contact;
4997         parent_class->get_contact             = e_book_backend_ldap_get_contact;
4998         parent_class->get_contact_list        = e_book_backend_ldap_get_contact_list;
4999         parent_class->start_book_view         = e_book_backend_ldap_start_book_view;
5000         parent_class->stop_book_view          = e_book_backend_ldap_stop_book_view;
5001         parent_class->get_changes             = e_book_backend_ldap_get_changes;
5002         parent_class->authenticate_user       = e_book_backend_ldap_authenticate_user;
5003         parent_class->get_required_fields    = e_book_backend_ldap_get_required_fields;
5004         parent_class->get_supported_fields    = e_book_backend_ldap_get_supported_fields;
5005         parent_class->get_supported_auth_methods = e_book_backend_ldap_get_supported_auth_methods;
5006         parent_class->cancel_operation        = e_book_backend_ldap_cancel_operation;
5007         parent_class->set_mode                = e_book_backend_ldap_set_mode;
5008         
5009         object_class->dispose = e_book_backend_ldap_dispose;
5010 }
5011
5012 static void
5013 e_book_backend_ldap_init (EBookBackendLDAP *backend)
5014 {
5015         EBookBackendLDAPPrivate *priv;
5016
5017         priv                   = g_new0 (EBookBackendLDAPPrivate, 1);
5018
5019         priv->supported_fields       = NULL;
5020         priv->supported_auth_methods = NULL;
5021         priv->ldap_limit             = 100;
5022         priv->id_to_op               = g_hash_table_new (g_int_hash, g_int_equal);
5023         priv->poll_timeout           = -1;
5024         priv->marked_for_offline     = FALSE;
5025         priv->mode                   = GNOME_Evolution_Addressbook_MODE_REMOTE;
5026         priv->is_summary_ready       = FALSE;
5027         priv->reserved1              = NULL;
5028         priv->reserved2              = NULL;
5029         priv->reserved3              = NULL;
5030         priv->reserved4              = NULL;
5031         g_static_rec_mutex_init (&priv->op_hash_mutex);
5032
5033         backend->priv = priv;
5034
5035         if (g_getenv ("LDAP_DEBUG"))
5036                 enable_debug = TRUE;
5037 }
5038
5039 /**
5040  * e_book_backend_ldap_get_type:
5041  */
5042 GType
5043 e_book_backend_ldap_get_type (void)
5044 {
5045         static GType type = 0;
5046
5047         if (! type) {
5048                 GTypeInfo info = {
5049                         sizeof (EBookBackendLDAPClass),
5050                         NULL, /* base_class_init */
5051                         NULL, /* base_class_finalize */
5052                         (GClassInitFunc)  e_book_backend_ldap_class_init,
5053                         NULL, /* class_finalize */
5054                         NULL, /* class_data */
5055                         sizeof (EBookBackendLDAP),
5056                         0,    /* n_preallocs */
5057                         (GInstanceInitFunc) e_book_backend_ldap_init
5058                 };
5059
5060                 type = g_type_register_static (E_TYPE_BOOK_BACKEND, "EBookBackendLDAP", &info, 0);
5061         }
5062
5063         return type;
5064 }