Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / addressbook / backends / groupwise / e-book-backend-groupwise.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /* e-book-backend-groupwise.c - Groupwise 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: Sivaiah Nallagatla <snallagatla@novell.com>
22  */
23
24 #include <config.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/time.h>
31 #include <time.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include "db.h"
35
36 #include <glib.h>
37 #include <glib/gstdio.h>
38 #include <glib/gi18n-lib.h>
39
40 #include "libedataserver/e-sexp.h"
41 #include "libedataserver/e-data-server-util.h"
42 #include "libedataserver/e-db3-utils.h"
43 #include "libedataserver/e-flag.h"
44 #include "libedataserver/e-url.h" 
45 #include "libebook/e-contact.h"
46 #include "libedata-book/e-book-backend-sexp.h"
47 #include "libedata-book/e-data-book.h"
48 #include "libedata-book/e-data-book-view.h"
49 #include "libedata-book/e-book-backend-db-cache.h"
50 #include "libedata-book/e-book-backend-summary.h"
51 #include "e-book-backend-groupwise.h"
52
53 #include "e-gw-connection.h"
54 #include "e-gw-item.h"
55 #include "e-gw-filter.h"
56
57 static EBookBackendClass *e_book_backend_groupwise_parent_class;
58                                                                                                                              
59 struct _EBookBackendGroupwisePrivate {
60         EGwConnection *cnc; 
61         char *uri;
62         char *container_id;
63         char *book_name;
64         char *original_uri;
65         char *summary_file_name;
66         gboolean only_if_exists;
67         GHashTable *categories_by_id;
68         GHashTable *categories_by_name;
69         gboolean is_writable;
70         gboolean is_cache_ready;
71         gboolean is_summary_ready;
72         gboolean marked_for_offline;
73         char *use_ssl;
74         int mode;
75         int cache_timeout;
76         EBookBackendSummary *summary;
77         GMutex *update_cache_mutex;
78         GMutex *update_mutex;
79         DB     *file_db;
80         DB_ENV *env;
81         /* for future use */
82         void *reserved1;
83 };
84
85 static GStaticMutex global_env_lock = G_STATIC_MUTEX_INIT;
86 static struct {
87         int ref_count;
88         DB_ENV *env;
89 } global_env;
90
91 #define ELEMENT_TYPE_SIMPLE 0x01
92 #define ELEMENT_TYPE_COMPLEX 0x02 /* fields which require explicit functions to set values into EContact and EGwItem */
93 #define SUMMARY_FLUSH_TIMEOUT 5000
94
95 static gboolean enable_debug = FALSE;
96
97 static void populate_emails (EContact *contact, gpointer data);
98 static void set_emails_in_gw_item (EGwItem *item, gpointer data);
99 static void set_emails_changes (EGwItem *new_item, EGwItem *old_item);
100 static void populate_full_name (EContact *contact, gpointer data);
101 static void set_full_name_in_gw_item (EGwItem *item, gpointer data);
102 static void set_full_name_changes (EGwItem *new_item, EGwItem *old_item);
103 static void populate_contact_members (EContact *contact, gpointer data);
104 static void set_categories_changes (EGwItem *new_item, EGwItem *old_item);
105 static void populate_birth_date (EContact *contact, gpointer data);
106 static void set_birth_date_in_gw_item (EGwItem *item, gpointer data);
107 static void set_birth_date_changes  (EGwItem *new_item, EGwItem *old_item);
108 static void populate_address (EContact *contact, gpointer data);
109 static void set_address_in_gw_item (EGwItem *item, gpointer data);
110 static void set_address_changes (EGwItem *new_item, EGwItem *old_item);
111 static void populate_ims (EContact *contact, gpointer data);
112 static void set_ims_in_gw_item (EGwItem *item, gpointer data);
113 static void set_im_changes (EGwItem *new_item, EGwItem *old_item);
114 static void fill_contact_from_gw_item (EContact *contact, EGwItem *item, GHashTable *categories_by_ids);
115
116 static const struct field_element_mapping {
117         EContactField field_id;
118         int element_type;
119         char *element_name;
120         void (*populate_contact_func)(EContact *contact,    gpointer data);
121         void (*set_value_in_gw_item) (EGwItem *item, gpointer data);
122         void (*set_changes) (EGwItem *new_item, EGwItem *old_item);
123  
124 } mappings [] = { 
125   
126         { E_CONTACT_UID, ELEMENT_TYPE_SIMPLE, "id"},
127         { E_CONTACT_FILE_AS, ELEMENT_TYPE_SIMPLE, "name" },
128         { E_CONTACT_FULL_NAME, ELEMENT_TYPE_COMPLEX, "full_name", populate_full_name, set_full_name_in_gw_item, set_full_name_changes},
129         { E_CONTACT_BIRTH_DATE, ELEMENT_TYPE_COMPLEX, "birthday", populate_birth_date, set_birth_date_in_gw_item, set_birth_date_changes },
130         { E_CONTACT_HOMEPAGE_URL, ELEMENT_TYPE_SIMPLE, "website"},
131         { E_CONTACT_NOTE, ELEMENT_TYPE_SIMPLE, "comment"},
132         { E_CONTACT_PHONE_PRIMARY, ELEMENT_TYPE_SIMPLE , "default_phone"},
133         { E_CONTACT_PHONE_BUSINESS, ELEMENT_TYPE_SIMPLE, "phone_Office"},
134         { E_CONTACT_PHONE_HOME, ELEMENT_TYPE_SIMPLE, "phone_Home"},
135         { E_CONTACT_PHONE_MOBILE, ELEMENT_TYPE_SIMPLE, "phone_Mobile"},
136         { E_CONTACT_PHONE_BUSINESS_FAX, ELEMENT_TYPE_SIMPLE, "phone_Fax" },
137         { E_CONTACT_PHONE_PAGER, ELEMENT_TYPE_SIMPLE, "phone_Pager"},
138         { E_CONTACT_ORG, ELEMENT_TYPE_SIMPLE, "organization"},
139         { E_CONTACT_ORG_UNIT, ELEMENT_TYPE_SIMPLE, "department"},
140         { E_CONTACT_TITLE, ELEMENT_TYPE_SIMPLE, "title"},
141         { E_CONTACT_EMAIL, ELEMENT_TYPE_COMPLEX, "members", populate_contact_members, NULL, NULL},
142         { E_CONTACT_ADDRESS_HOME, ELEMENT_TYPE_COMPLEX, "Home", populate_address, set_address_in_gw_item, set_address_changes },
143         { E_CONTACT_IM_AIM, ELEMENT_TYPE_COMPLEX, "ims", populate_ims, set_ims_in_gw_item, set_im_changes },
144         { E_CONTACT_CATEGORIES, ELEMENT_TYPE_COMPLEX, "categories", NULL, NULL, set_categories_changes},
145         { E_CONTACT_EMAIL_1, ELEMENT_TYPE_COMPLEX, "email", populate_emails, set_emails_in_gw_item, set_emails_changes },
146         { E_CONTACT_REV, ELEMENT_TYPE_SIMPLE, "modified_time"},
147         { E_CONTACT_BOOK_URI, ELEMENT_TYPE_SIMPLE, "book_uri"}
148 }; 
149
150 static void
151 free_attr_list (GList *attr_list)
152 {
153         GList *l;
154                                                                                                                              
155         for (l = attr_list; l; l = g_list_next (l)) {
156                 EVCardAttribute *attr = l->data;
157                 e_vcard_attribute_free (attr);
158         }
159                                                                                                                              
160         g_list_free (attr_list);
161 }
162
163 static void 
164 populate_ims (EContact *contact, gpointer data)
165 {
166         GList *im_list;
167         GList *aim_list = NULL;
168         GList *icq_list = NULL;
169         GList *yahoo_list = NULL;
170         GList *gadugadu_list = NULL;
171         GList *msn_list = NULL;
172         GList *jabber_list = NULL;
173         GList *groupwise_list = NULL;
174         IMAddress *address;
175         EGwItem *item;
176   
177         item = E_GW_ITEM (data);
178         im_list = e_gw_item_get_im_list (item);
179
180         for (; im_list != NULL; im_list = g_list_next (im_list)) {
181                 EVCardAttribute *attr;
182                 GList **im_attr_list = NULL;
183                 int im_field_id = -1;
184
185                 address = (IMAddress *) (im_list->data);
186                 if (address->service == NULL) {
187                         continue;
188                 }
189                 
190                 if (g_str_equal (address->service, "icq")) {
191                         im_field_id = E_CONTACT_IM_ICQ;
192                         im_attr_list = &icq_list;
193                 }
194                 else if (g_str_equal (address->service, "aim")) {
195                         im_field_id = E_CONTACT_IM_AIM;
196                         im_attr_list = &aim_list;
197                 }
198                 else if ( g_str_equal (address->service, "msn")) {
199                         im_field_id = E_CONTACT_IM_MSN;
200                         im_attr_list = &msn_list;
201                 }
202                 else if (g_str_equal (address->service, "yahoo")) {
203                         im_field_id = E_CONTACT_IM_YAHOO;
204                         im_attr_list = &yahoo_list;
205                 }
206                 else if (g_str_equal (address->service, "gadu-gadu")) {
207                         im_field_id = E_CONTACT_IM_GADUGADU;
208                         im_attr_list = &gadugadu_list;
209                 }
210                 else if (g_str_equal (address->service, "jabber")) {
211                         im_field_id = E_CONTACT_IM_JABBER;
212                         im_attr_list = &jabber_list;
213                 }
214                         
215                 else if (g_str_equal (address->service, "nov")) {
216                         im_field_id = E_CONTACT_IM_GROUPWISE;
217                         im_attr_list = &groupwise_list;
218                 }
219                 if (im_field_id == -1)
220                         continue;
221
222                 attr = e_vcard_attribute_new ("", e_contact_vcard_attribute(im_field_id));
223                 e_vcard_attribute_add_param_with_value (attr, e_vcard_attribute_param_new (EVC_TYPE), "WORK");
224                 e_vcard_attribute_add_value (attr, address->address);
225                 *im_attr_list = g_list_append (*im_attr_list, attr);
226         }
227         
228         e_contact_set_attributes (contact, E_CONTACT_IM_AIM, aim_list);
229         e_contact_set_attributes (contact, E_CONTACT_IM_JABBER, jabber_list);
230         e_contact_set_attributes (contact, E_CONTACT_IM_ICQ, icq_list);
231         e_contact_set_attributes (contact, E_CONTACT_IM_YAHOO, yahoo_list);
232         e_contact_set_attributes (contact, E_CONTACT_IM_GADUGADU, gadugadu_list);
233         e_contact_set_attributes (contact, E_CONTACT_IM_MSN, msn_list);
234         e_contact_set_attributes (contact, E_CONTACT_IM_GROUPWISE, groupwise_list);
235         
236         free_attr_list (aim_list);
237         free_attr_list (jabber_list);
238         free_attr_list (icq_list);
239         free_attr_list (yahoo_list);
240         free_attr_list (gadugadu_list);
241         free_attr_list (msn_list);
242         free_attr_list (groupwise_list);
243 }
244
245 static void
246 append_ims_to_list (GList **im_list, EContact *contact,  char *service_name, EContactField field_id)
247 {
248         GList *list;
249         IMAddress *address;
250         list = e_contact_get (contact, field_id);
251         for (; list != NULL; list =  g_list_next (list)) {
252                 address = g_new0 (IMAddress , 1);
253                 address->service = g_strdup (service_name);
254                 address->address = list->data;
255                 *im_list = g_list_append (*im_list, address);
256         }
257         g_list_free (list);
258         
259 }
260
261 static void 
262 set_ims_in_gw_item (EGwItem *item, gpointer data)
263 {
264         EContact *contact;
265         GList *im_list = NULL;
266   
267         contact = E_CONTACT (data);
268   
269         append_ims_to_list (&im_list, contact, "aim", E_CONTACT_IM_AIM);
270         append_ims_to_list (&im_list, contact, "yahoo", E_CONTACT_IM_YAHOO);
271         append_ims_to_list (&im_list, contact, "gadu-gadu", E_CONTACT_IM_GADUGADU);
272         append_ims_to_list (&im_list, contact, "icq", E_CONTACT_IM_ICQ);
273         append_ims_to_list (&im_list, contact, "msn", E_CONTACT_IM_MSN);
274         append_ims_to_list (&im_list, contact, "jabber", E_CONTACT_IM_JABBER);
275         append_ims_to_list (&im_list, contact, "nov", E_CONTACT_IM_GROUPWISE);
276         if (im_list)
277                 e_gw_item_set_im_list (item, im_list);
278 }
279
280 static void
281 set_im_changes (EGwItem *new_item, EGwItem *old_item)
282 {
283         GList *old_ims;
284         GList *new_ims;
285         GList *added_ims = NULL;
286         GList *old_ims_copy;
287         GList *temp;
288         gboolean ims_matched;
289         IMAddress *im1, *im2;
290
291         old_ims = e_gw_item_get_im_list (old_item);
292         new_ims = e_gw_item_get_im_list (new_item);
293
294         if (old_ims && new_ims) {
295         
296                 old_ims_copy = g_list_copy (old_ims);
297                 for ( ; new_ims != NULL; new_ims = g_list_next (new_ims)) {
298                         
299                         im1 = new_ims->data;
300                         temp = old_ims;
301                         ims_matched = FALSE;
302                         for(; temp != NULL; temp = g_list_next (temp)) {
303                                 im2 = temp->data;
304                                 if (g_str_equal (im1->service, im2->service) && g_str_equal (im1->address, im2->address)) {
305                                         ims_matched = TRUE;
306                                         old_ims_copy = g_list_remove (old_ims_copy, im2);
307                                         break;
308                                 }
309                                 
310                         }
311                         if (! ims_matched)
312                                 added_ims = g_list_append (added_ims, im1);
313                 }
314                              
315                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_ADD, "ims", added_ims);
316                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_DELETE, "ims", old_ims_copy);
317
318         } else if (!new_ims && old_ims) {
319                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_DELETE, "ims", g_list_copy (old_ims));
320         } else if (new_ims && !old_ims) {
321                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_ADD, "ims", g_list_copy (new_ims));
322         }
323         
324 }
325
326 static void 
327 copy_postal_address_to_contact_address ( EContactAddress *contact_addr, PostalAddress *address)
328 {
329         contact_addr->address_format = NULL;
330         contact_addr->po = NULL;
331         contact_addr->street = g_strdup (address->street_address);
332         contact_addr->ext = g_strdup (address->location);
333         contact_addr->locality = g_strdup (address->city);
334         contact_addr->region = g_strdup (address->state);
335         contact_addr->code = g_strdup (address->postal_code);
336         contact_addr->country = g_strdup (address->country);
337 }
338
339 static void 
340 copy_contact_address_to_postal_address (PostalAddress *address, EContactAddress *contact_addr)
341 {
342         /* ugh, contact addr has null terminated strings instead of NULLs*/
343         address->street_address = (contact_addr->street && *contact_addr->street) ? g_strdup (contact_addr->street): NULL;
344         address->location = (contact_addr->ext && *contact_addr->ext) ? g_strdup (contact_addr->ext) : NULL;
345         address->city = (contact_addr->locality && *contact_addr->locality) ? g_strdup (contact_addr->locality) : NULL;
346         address->state = (contact_addr->region && *contact_addr->region) ?  g_strdup (contact_addr->region) : NULL;
347         address->postal_code = (contact_addr->code && *contact_addr->code ) ? g_strdup (contact_addr->code) : NULL;
348         address->country = (contact_addr->country && *(contact_addr->country)) ? g_strdup (contact_addr->country) : NULL;
349 }
350
351 static void 
352 populate_address (EContact *contact, gpointer data)
353 {
354         PostalAddress *address;
355         EGwItem *item;
356         EContactAddress *contact_addr;
357         
358         item = E_GW_ITEM (data);
359         
360         address = e_gw_item_get_address (item, "Home");
361         contact_addr = NULL;
362
363         if (address) {
364                 contact_addr = g_new0(EContactAddress, 1);
365                 copy_postal_address_to_contact_address (contact_addr, address);
366                 e_contact_set (contact, E_CONTACT_ADDRESS_HOME, contact_addr);
367                 e_contact_address_free (contact_addr);
368         }
369   
370         address = e_gw_item_get_address (item, "Office");
371         if (address) {
372                 contact_addr = g_new0(EContactAddress, 1);
373                 copy_postal_address_to_contact_address (contact_addr, address);
374                 e_contact_set (contact, E_CONTACT_ADDRESS_WORK, contact_addr);
375                 e_contact_address_free (contact_addr);
376         }
377 }
378
379 static void 
380 set_address_in_gw_item (EGwItem *item, gpointer data)
381 {
382         EContact *contact;
383         EContactAddress *contact_address;
384         PostalAddress *address;
385
386         contact = E_CONTACT (data);
387         
388         contact_address = e_contact_get (contact, E_CONTACT_ADDRESS_HOME);
389         if (contact_address) {
390                 address = g_new0(PostalAddress, 1);
391                 copy_contact_address_to_postal_address (address, contact_address);
392                 e_gw_item_set_address (item, "Home", address);
393                 e_contact_address_free (contact_address);
394         }
395                 
396         contact_address = e_contact_get (contact, E_CONTACT_ADDRESS_WORK);
397         if (contact_address) {
398                 address = g_new0(PostalAddress, 1);
399                 copy_contact_address_to_postal_address (address, contact_address);
400                 e_gw_item_set_address (item, "Office", address);
401                 e_contact_address_free (contact_address);
402         }
403 }
404
405 static PostalAddress *
406 copy_postal_address (PostalAddress *address)
407 {
408         PostalAddress *address_copy;
409
410         address_copy = g_new0(PostalAddress, 1);
411
412         address_copy->street_address = g_strdup (address->street_address);
413         address_copy->location = g_strdup (address->location);
414         address_copy->city = g_strdup (address->city);
415         address_copy->state = g_strdup (address->state);
416         address_copy->postal_code = g_strdup (address->postal_code);
417         address_copy->country = g_strdup (address->country);
418         return address_copy;
419 }
420
421 static void 
422 set_postal_address_change (EGwItem *new_item, EGwItem *old_item,  char *address_type)
423 {
424         PostalAddress *old_postal_address;
425         PostalAddress *new_postal_address;
426         PostalAddress *update_postal_address, *delete_postal_address;
427         char *s1, *s2;
428         update_postal_address = g_new0(PostalAddress, 1);
429         delete_postal_address = g_new0 (PostalAddress, 1);
430         
431         new_postal_address = e_gw_item_get_address (new_item,  address_type);
432         old_postal_address = e_gw_item_get_address (old_item, address_type);
433         if (new_postal_address && old_postal_address) {
434                 s1 = new_postal_address->street_address;
435                 s2 = old_postal_address->street_address;
436                 if (!s1 && s2)
437                         delete_postal_address->street_address = g_strdup(s2);
438                 else if (s1)
439                         update_postal_address->street_address = g_strdup(s1);
440                 
441                 s1 =  new_postal_address->location;
442                 s2 = old_postal_address->location;
443                 if (!s1 && s2)
444                         delete_postal_address->location = g_strdup(s2);
445                 else if (s1)
446                         update_postal_address->location = g_strdup(s1);
447
448                 s1 = new_postal_address->city;
449                 s2 = old_postal_address->city;
450                 if (!s1 && s2)
451                         delete_postal_address->city = g_strdup(s2);
452                 else if (s1)
453                         update_postal_address->city = g_strdup(s1);
454
455                 s1 =  new_postal_address->state;
456                 s2 = old_postal_address->state;
457                 if (!s1 && s2)
458                         delete_postal_address->state = g_strdup(s2);
459                 else if (s1)
460                         update_postal_address->state = g_strdup(s1);
461                 s1 =  new_postal_address->postal_code;
462                 s2 = old_postal_address->postal_code;
463                 if (!s1 && s2)
464                         delete_postal_address->postal_code = g_strdup(s2);
465                 else if (s1)
466                         update_postal_address->postal_code = g_strdup(s1);
467
468                 s1 =  new_postal_address->country;
469                 s2 =  old_postal_address->country;
470                 if (!s1 && s2)
471                         delete_postal_address->country = g_strdup(s2);
472                 else if (s1)
473                         update_postal_address->country = g_strdup(s1);
474
475                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_UPDATE, address_type, update_postal_address);
476                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_DELETE, address_type, delete_postal_address);
477                 
478         } else if (!new_postal_address && old_postal_address) {
479                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_DELETE, address_type, copy_postal_address(old_postal_address));
480         } else if (new_postal_address && !old_postal_address) {
481                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_ADD, address_type, copy_postal_address(new_postal_address));
482         }
483 }
484
485 static void 
486 set_address_changes (EGwItem *new_item , EGwItem *old_item)
487 {
488         set_postal_address_change (new_item, old_item, "Home");
489         set_postal_address_change (new_item, old_item, "Office");
490 }
491
492 static void 
493 populate_birth_date (EContact *contact, gpointer data)
494 {
495         EGwItem *item;
496         char *value ;
497         EContactDate *date;
498   
499         item = E_GW_ITEM (data);
500         value = e_gw_item_get_field_value (item, "birthday");
501         if (value) {
502                 date =  e_contact_date_from_string (value);
503                 e_contact_set (contact, E_CONTACT_BIRTH_DATE, date);
504                 e_contact_date_free (date);
505         }
506 }
507
508 static void 
509 set_birth_date_in_gw_item (EGwItem *item, gpointer data)
510 {
511         EContact *contact;
512         EContactDate *date;
513         char *date_string;
514         contact = E_CONTACT (data);
515         date = e_contact_get (contact, E_CONTACT_BIRTH_DATE);
516         if (date) {
517                 date_string = e_contact_date_to_string (date);
518                 e_gw_item_set_field_value (item, "birthday", date_string);
519                 e_contact_date_free (date);
520                 g_free (date_string);
521         }
522
523 }
524
525 static void 
526 set_birth_date_changes (EGwItem *new_item, EGwItem *old_item)
527 {
528         char *new_birthday;
529         char *old_birthday;
530
531         new_birthday = e_gw_item_get_field_value (new_item, "birthday");
532         old_birthday = e_gw_item_get_field_value (old_item, "birthday");
533         
534         if (new_birthday && old_birthday) {
535                 if (!g_str_equal (new_birthday, old_birthday))
536                         e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_UPDATE, "birthday", new_birthday);
537         }
538         else if (!new_birthday && old_birthday) {
539                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_DELETE, "birthday", old_birthday);
540         }
541         else if (new_birthday && !old_birthday) {
542                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_ADD, "birthday", new_birthday);
543         }
544 }
545
546 static const int email_fields[3] = {
547         E_CONTACT_EMAIL_1,
548         E_CONTACT_EMAIL_2,
549         E_CONTACT_EMAIL_3
550
551 };
552
553 static void 
554 populate_emails (EContact *contact, gpointer data)
555 {
556         GList *email_list;
557         EGwItem *item;
558         int i;
559
560         item = E_GW_ITEM (data);
561         email_list = e_gw_item_get_email_list(item);
562
563         for (i =0 ; i < 3 && email_list; i++, email_list = g_list_next (email_list)) {
564                 if (email_list->data) 
565                         e_contact_set (contact, email_fields[i], email_list->data);
566         }
567
568
569
570 static void 
571 set_emails_in_gw_item (EGwItem *item, gpointer data)
572 {
573         GList *email_list;
574         EContact *contact;
575         char *email;
576         int i;
577
578         contact = E_CONTACT (data);
579         email_list = NULL;
580         for (i =0 ; i < 3; i++) {
581                 email = e_contact_get (contact, email_fields[i]);
582                 if(email)
583                         email_list = g_list_append (email_list, g_strdup (email));
584         }
585         e_gw_item_set_email_list (item, email_list);
586 }  
587
588 static void 
589 compare_string_lists ( GList *old_list, GList *new_list, GList **additions, GList **deletions)
590 {
591         GList *temp, *old_list_copy;
592         gboolean strings_matched;
593         char *string1, *string2;
594         
595         if (old_list && new_list) {
596                 old_list_copy = g_list_copy (old_list);
597                 for ( ; new_list != NULL; new_list = g_list_next (new_list)) {
598                         
599                         string1 = new_list->data;
600                         temp = old_list;
601                         strings_matched = FALSE;
602                         for(; temp != NULL; temp = g_list_next (temp)) {
603                                 string2 = temp->data;
604                                 if ( g_str_equal (string1, string2)) {
605                                         strings_matched = TRUE;
606                                         old_list_copy = g_list_remove (old_list_copy, string2);
607                                         break;
608                                 }
609                         }
610                         if (!strings_matched)
611                                 *additions = g_list_append (*additions, string1);
612                 }
613                 *deletions = old_list_copy;
614         }
615         else if (!new_list && old_list) 
616                 *deletions = g_list_copy (old_list);
617         else if (new_list && !old_list)
618                 *additions = g_list_copy (new_list);
619 }
620  
621 static void 
622 set_emails_changes (EGwItem *new_item, EGwItem *old_item)
623 {
624         GList *old_email_list;
625         GList *new_email_list;
626         GList  *added_emails = NULL, *deleted_emails = NULL;
627
628         old_email_list = e_gw_item_get_email_list (old_item);
629         new_email_list = e_gw_item_get_email_list (new_item);
630         compare_string_lists (old_email_list, new_email_list, &added_emails, &deleted_emails);
631         if (added_emails)
632                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_ADD, "email", added_emails);
633         if (deleted_emails)
634                 e_gw_item_set_change (new_item,  E_GW_ITEM_CHANGE_TYPE_DELETE, "email", deleted_emails);
635 }
636  
637 static void 
638 populate_full_name (EContact *contact, gpointer data)
639 {
640         EGwItem *item;
641         FullName  *full_name ;
642         char *full_name_string;
643
644         item = E_GW_ITEM(data);
645         full_name = e_gw_item_get_full_name (item);
646         if (full_name) {
647                 full_name_string = g_strconcat ( (full_name->first_name == NULL) ? "\0" :    full_name->first_name, " ",
648                             (full_name->middle_name == NULL) ? "\0" : full_name->middle_name, " ",
649                             full_name->last_name == NULL ? "\0" : full_name->last_name, " ",
650                             (full_name->name_suffix == NULL ) ? "\0" : full_name->name_suffix, NULL);
651                 full_name_string = g_strstrip (full_name_string);
652                 if (!g_str_equal (full_name_string, "\0"))
653                         e_contact_set (contact, E_CONTACT_FULL_NAME, full_name_string);
654                 g_free (full_name_string);
655         }
656 }
657
658 static void 
659 set_full_name_in_gw_item (EGwItem *item, gpointer data)
660 {
661         EContact *contact;
662         char   *name;
663         EContactName *contact_name;
664         FullName *full_name;
665
666         contact = E_CONTACT (data);
667   
668         name = e_contact_get (contact, E_CONTACT_FULL_NAME);
669
670         if(name) {
671                 contact_name = e_contact_name_from_string (name);
672                 full_name = g_new0 (FullName, 1);
673                 if (contact_name && full_name) {
674                         full_name->name_prefix =  g_strdup (contact_name->prefixes);
675                         full_name->first_name =  g_strdup (contact_name->given);
676                         full_name->middle_name =  g_strdup (contact_name->additional);
677                         full_name->last_name =  g_strdup (contact_name->family);
678                         full_name->name_suffix = g_strdup (contact_name->suffixes);
679                         e_contact_name_free (contact_name);
680                 }
681                 e_gw_item_set_full_name (item, full_name);
682         }
683 }
684
685 static FullName *
686 copy_full_name (FullName *full_name)
687 {
688         FullName *full_name_copy = g_new0(FullName, 1);
689         full_name_copy->name_prefix = g_strdup (full_name->name_prefix);
690         full_name_copy->first_name =  g_strdup (full_name->first_name);
691         full_name_copy->middle_name = g_strdup (full_name->middle_name);
692         full_name_copy->last_name = g_strdup (full_name->last_name);
693         full_name_copy->name_suffix = g_strdup (full_name->name_suffix);
694         return full_name_copy;
695 }
696
697 static void 
698 set_full_name_changes (EGwItem *new_item, EGwItem *old_item)
699 {
700         FullName *old_full_name;
701         FullName *new_full_name;
702         FullName  *update_full_name, *delete_full_name;
703         char *s1, *s2;
704         update_full_name = g_new0(FullName, 1);
705         delete_full_name = g_new0 (FullName, 1);
706         
707         old_full_name = e_gw_item_get_full_name (old_item);
708         new_full_name = e_gw_item_get_full_name (new_item);
709         
710         if (old_full_name && new_full_name) {
711                 s1 = new_full_name->name_prefix;
712                 s2 = old_full_name->name_prefix;
713                 if(!s1 && s2)
714                         delete_full_name->name_prefix = g_strdup(s2);
715                 else if (s1)
716                         update_full_name->name_prefix = g_strdup(s1);
717                 s1 = new_full_name->first_name;
718                 s2  = old_full_name->first_name;
719                 if(!s1 && s2)
720                         delete_full_name->first_name = g_strdup(s2);
721                 else if (s1)
722                         update_full_name->first_name = g_strdup(s1);
723                 s1 = new_full_name->middle_name;
724                 s2  = old_full_name->middle_name;
725                 if(!s1 && s2)
726                         delete_full_name->middle_name = g_strdup(s2);
727                 else if (s1)
728                         update_full_name->middle_name = g_strdup(s1);
729                 
730                 s1 = new_full_name->last_name;
731                 s2 = old_full_name->last_name;
732                 if(!s1 && s2)
733                         delete_full_name->last_name = g_strdup(s2);
734                 else if (s1)
735                         update_full_name->last_name = g_strdup(s1);
736                 s1 = new_full_name->name_suffix;
737                 s2  = old_full_name->name_suffix;
738                 if(!s1 && s2)
739                         delete_full_name->name_suffix = g_strdup(s2);
740                 else if (s1)
741                         update_full_name->name_suffix = g_strdup(s1);
742                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_UPDATE,"full_name",  update_full_name);
743                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_DELETE,"full_name",  delete_full_name);
744         
745         } else if (!new_full_name && old_full_name) {
746                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_DELETE, "full_name", copy_full_name(old_full_name));
747         } else if (new_full_name && !old_full_name) {
748                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_ADD, "full_name", copy_full_name(new_full_name));
749         }
750 }
751
752 static void 
753 populate_contact_members (EContact *contact, gpointer data)
754 {
755         EGwItem *item;
756         GList *member_list;
757
758         item = E_GW_ITEM(data);
759         member_list = e_gw_item_get_member_list (item);
760
761         for (; member_list != NULL; member_list = g_list_next (member_list)) {
762                 EVCardAttribute *attr;
763                 EGroupMember *member;
764                 member = (EGroupMember *) member_list->data;
765
766                 attr = e_vcard_attribute_new (NULL, EVC_EMAIL);
767                 e_vcard_attribute_add_param_with_value (attr,
768                                                         e_vcard_attribute_param_new (EVC_X_DEST_CONTACT_UID),
769                                                         member->id);
770                 e_vcard_attribute_add_param_with_value (attr,
771                                                         e_vcard_attribute_param_new (EVC_X_DEST_EMAIL),
772                                                         member->email);
773                 if (member->name)
774                         e_vcard_attribute_add_param_with_value (attr,
775                                                         e_vcard_attribute_param_new (EVC_X_DEST_NAME),
776                                                         member->name);
777                 e_vcard_attribute_add_value (attr, member->email);
778                 e_vcard_add_attribute (E_VCARD (contact), attr);
779         }
780 }
781
782 static void
783 set_members_in_gw_item (EGwItem  *item, EContact *contact, EBookBackendGroupwise *egwb)
784 {
785         GList  *members, *temp, *items, *p, *emails_without_ids;
786         GList *group_members;
787         char *email;
788         EGwFilter *filter;
789         int status;
790         char *id;
791         EGwItem *temp_item;
792         int count = 0;
793         int element_type;
794         int i;
795         char *value;
796         EGroupMember *member;
797
798         members = e_contact_get_attributes (contact, E_CONTACT_EMAIL);
799         temp = members;
800         filter = e_gw_filter_new ();
801         group_members = NULL;
802         emails_without_ids = NULL;
803
804         for ( ;temp != NULL; temp = g_list_next (temp)) {
805                 EVCardAttribute *attr = temp->data;
806                 id = email = NULL;
807
808                 for (p = e_vcard_attribute_get_params (attr); p; p = p->next) {
809                         EVCardAttributeParam *param = p->data;
810                         const char *param_name = e_vcard_attribute_param_get_name (param);
811
812                         if (!g_ascii_strcasecmp (param_name,
813                                                  EVC_X_DEST_CONTACT_UID)) {
814                                 GList *v = e_vcard_attribute_param_get_values (param);
815                                 id = v ? v->data : NULL;
816                                 if (id) {
817                                         EGwItem *gw_item = NULL;
818                                         e_gw_connection_get_item (egwb->priv->cnc, egwb->priv->container_id,id, "name email", &gw_item);
819                                         if (!gw_item) {
820                                                 /* The item corresponding to this id is not found. This happens in case of
821                                                  * importing, in imported file the stored id is corresponding to the address
822                                                  * book from which the contact list was exported.
823                                                  */ 
824                                                 id = NULL;
825                                         }
826                                         else
827                                                 g_object_unref (gw_item);
828                                 }
829                         } else if (!g_ascii_strcasecmp (param_name,
830                                                         EVC_X_DEST_EMAIL)) {
831                                 GList *v = e_vcard_attribute_param_get_values (param);
832                                 email = v ? v->data : NULL;
833                         }
834                 }
835                 if (id) {
836                         member = g_new0 (EGroupMember , 1);
837                         member->id = g_strdup (id);
838                         group_members = g_list_append (group_members, member);
839                 } else if (email) {
840                         e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_EQUAL, "emailList/@primary", email);
841                         emails_without_ids = g_list_append (emails_without_ids, g_strdup (email));
842                         count++;
843                 }
844         }
845         e_gw_filter_group_conditions (filter, E_GW_FILTER_OP_OR, count);
846         items = NULL;
847
848         if (count)
849                 status = e_gw_connection_get_items (egwb->priv->cnc, egwb->priv->container_id, "name email default members", filter, &items);
850
851         for (; items != NULL; items = g_list_next (items )) {
852                 GList *emails;
853                 GList *ptr;
854
855                 temp_item = E_GW_ITEM (items->data);
856                 emails = e_gw_item_get_email_list (temp_item);
857                 if (emails_without_ids && (ptr = g_list_find_custom (emails_without_ids, emails->data, (GCompareFunc)strcasecmp ))) {
858                         emails_without_ids = g_list_remove_link (emails_without_ids, ptr);
859                         g_list_free (ptr);
860                         id = g_strdup (e_gw_item_get_id (temp_item));
861                         member = g_new0 (EGroupMember , 1);
862                         member->id = id;
863                         group_members = g_list_append (group_members, member);
864                 }
865                 g_object_unref (temp_item);
866         }
867        
868
869         /* In groupwise there is no way to put arbitrary members into a group. There's no 
870          * mechanism for a group to contain members that are not already present in a system 
871          * or personal addressbook as a contact, and so they cant be saved and will be lost.
872          * In order to save them we first need to create groupwise based contacts for these 
873          * arbitrary contacts and then add them as members to the group.
874          */
875         
876         temp = emails_without_ids ;
877         for (; temp != NULL; temp = g_list_next (temp)) {
878                 EContact *new_contact = e_contact_new ();
879                 EGwItem *new_item = e_gw_item_new_empty ();
880                 FullName *full_name;
881
882                 e_contact_set (new_contact,E_CONTACT_FULL_NAME, e_contact_name_from_string (strdup (temp->data))); 
883                 e_contact_set (new_contact, E_CONTACT_EMAIL_1, strdup (temp->data));
884                 e_contact_set (new_contact, E_CONTACT_IS_LIST, FALSE);
885                 e_gw_item_set_item_type (new_item, E_GW_ITEM_TYPE_CONTACT);
886                 e_gw_item_set_container_id (new_item, g_strdup(egwb->priv->container_id));
887                 full_name = g_new0 (FullName, 1);
888                 full_name->name_prefix = NULL;
889                 full_name->first_name = g_strdup(temp->data);
890                 full_name->middle_name = NULL;
891                 full_name->last_name = NULL;
892                 full_name->name_suffix = NULL;
893                 e_gw_item_set_full_name (new_item, full_name);
894                 
895                 for (i=0; i < G_N_ELEMENTS (mappings); i++) {
896                         element_type = mappings[i].element_type;
897                         if (element_type == ELEMENT_TYPE_SIMPLE) {
898                                 value = e_contact_get (new_contact, mappings[i].field_id);
899                                 if (value != NULL) {
900                                         e_gw_item_set_field_value (new_item, mappings[i].element_name, value);
901                                         g_free (value);
902                                 }
903                         }
904                         else if (element_type == ELEMENT_TYPE_COMPLEX) {
905                                 if (mappings[i].field_id == E_CONTACT_CATEGORIES) {
906                                         continue;
907                                 }
908                                 else if (mappings[i].field_id == E_CONTACT_EMAIL) {
909                                         if (e_contact_get (contact, E_CONTACT_IS_LIST))
910                                                 continue;
911                                 }
912                                 else if (mappings[i].field_id == E_CONTACT_FULL_NAME) {
913                                         continue;
914                                 }
915                                 else {
916                                         mappings[i].set_value_in_gw_item (new_item, new_contact);
917                                 }
918                         }
919                 
920                 }
921                 id = NULL;
922                 status = e_gw_connection_create_item (egwb->priv->cnc, new_item, &id);
923                 if (status == E_GW_CONNECTION_STATUS_INVALID_CONNECTION)
924                         status = e_gw_connection_create_item (egwb->priv->cnc, new_item, &id);
925                 
926                 if (status == E_GW_CONNECTION_STATUS_OK && id) {
927                         e_contact_set (new_contact, E_CONTACT_UID, id);
928                         e_book_backend_db_cache_add_contact (egwb->priv->file_db, new_contact);
929                         e_book_backend_summary_add_contact (egwb->priv->summary, new_contact);
930                         member = g_new0 (EGroupMember, 1);
931                         member->id = g_strdup (id);
932                         group_members = g_list_append (group_members, member);
933                         g_free (id);
934                 }
935                 g_object_unref (new_item);
936                 g_object_unref (new_contact);
937         }
938
939         g_list_foreach (members, (GFunc) e_vcard_attribute_free, NULL);
940         g_list_free (members);
941         g_list_foreach (emails_without_ids, (GFunc) g_free, NULL);
942         g_list_free (emails_without_ids);
943         g_list_free (items);
944         e_gw_item_set_member_list (item, group_members);
945 }
946
947 static void 
948 set_member_changes (EGwItem *new_item, EGwItem *old_item, EBookBackendGroupwise *egwb)
949 {
950         GList *old_members, *new_members ;
951         GList *old_ids,  *new_ids,  *additions, *deletions;
952
953         old_ids = new_ids = additions = deletions = NULL;
954         old_members = e_gw_item_get_member_list (old_item);
955         new_members = e_gw_item_get_member_list (new_item);
956
957         for ( ;old_members != NULL; old_members = g_list_next (old_members)) {
958                 EGroupMember *member;
959                 member = (EGroupMember *)old_members->data;
960                 old_ids = g_list_append (old_ids, member->id);
961         }
962         for ( ;new_members != NULL; new_members = g_list_next (new_members)) {
963                 EGroupMember *member;
964                 member = (EGroupMember *)new_members->data;
965                 new_ids = g_list_append (new_ids, member->id);
966         }
967         
968         compare_string_lists (old_ids, new_ids, &additions, &deletions);
969         if (additions) 
970                 e_gw_connection_add_members (egwb->priv->cnc, e_gw_item_get_id (old_item), additions);
971         if (deletions)
972                 e_gw_connection_remove_members (egwb->priv->cnc, e_gw_item_get_id (old_item), deletions);
973
974         g_list_free (new_ids);
975         g_list_free (old_ids);
976         g_list_free (additions);
977         g_list_free (deletions);
978 }
979
980 static void 
981 set_organization_in_gw_item (EGwItem *item, EContact *contact, EBookBackendGroupwise *egwb)
982 {
983         char *organization_name;
984         EGwItem *org_item, *temp_item;
985         EGwFilter *filter;
986         int status;
987         char *id;
988         GList *items;
989         
990         organization_name = e_contact_get (contact, E_CONTACT_ORG);
991         if (organization_name == NULL || strlen (organization_name) == 0)
992                 return;
993
994         filter = e_gw_filter_new ();
995         e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_EQUAL, "name", organization_name);
996         items = NULL;
997         status = e_gw_connection_get_items (egwb->priv->cnc, egwb->priv->container_id, NULL, filter, &items);
998         g_object_unref (filter);
999         id = NULL;
1000
1001         for (; items != NULL; items = g_list_next (items )) {
1002                 temp_item = E_GW_ITEM (items->data);
1003                 if (e_gw_item_get_item_type (temp_item) == E_GW_ITEM_TYPE_ORGANISATION) {
1004                         id = g_strdup (e_gw_item_get_id (temp_item));
1005                         for (;items != NULL; items = g_list_next (items))
1006                                 g_object_unref (items->data);
1007                         break;
1008                 }
1009                 g_object_unref (temp_item);
1010         }
1011         g_list_free (items);
1012
1013         if (id == NULL) {
1014                 org_item = e_gw_item_new_empty ();
1015                 e_gw_item_set_container_id (org_item, egwb->priv->container_id);
1016                 e_gw_item_set_field_value (org_item, "name", organization_name);
1017                 e_gw_item_set_item_type (org_item, E_GW_ITEM_TYPE_ORGANISATION);
1018                 status = e_gw_connection_create_item (egwb->priv->cnc, org_item, &id);
1019                 if ((status == E_GW_CONNECTION_STATUS_OK) && id) {
1020                         EContact *contact = e_contact_new ();
1021                         fill_contact_from_gw_item (contact, org_item, egwb->priv->categories_by_id);
1022                         e_contact_set (contact, E_CONTACT_UID, id);
1023                         e_contact_set (contact, E_CONTACT_FULL_NAME, organization_name);
1024                         /* book uri is always set outside fill_contact_from_gw_item() */
1025                         e_contact_set (contact, E_CONTACT_BOOK_URI, egwb->priv->original_uri);
1026                         e_book_backend_db_cache_add_contact (egwb->priv->file_db, contact);
1027                         e_book_backend_summary_add_contact (egwb->priv->summary, contact);
1028                         g_object_unref (contact);
1029                 }
1030                 g_object_unref (org_item);
1031                 if (status != E_GW_CONNECTION_STATUS_OK)
1032                         return;
1033         }
1034         if (id == NULL) 
1035                 return;
1036         e_gw_item_set_field_value (item, "organization_id", id);
1037         e_gw_item_set_field_value (item , "organization", organization_name);
1038 }
1039
1040 static void
1041 set_organization_changes_in_gw_item (EGwItem *new_item, EGwItem *old_item)
1042 {
1043         char *old_value;
1044         char *new_value;
1045         char *old_org_id;
1046         char *new_org_id;
1047
1048         old_value = e_gw_item_get_field_value (old_item, "organization");
1049         new_value = e_gw_item_get_field_value (new_item, "organization");
1050         old_org_id = e_gw_item_get_field_value (old_item, "organization_id");
1051         new_org_id = e_gw_item_get_field_value (new_item, "organization_id");
1052         if (new_value && old_value) {
1053                 if (!g_str_equal (new_value, old_value)) {
1054                         e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_UPDATE, "organization", new_value);
1055                         e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_UPDATE, "organization_id", new_org_id);
1056                 }
1057         } else if (!new_value  && old_value) {
1058                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_DELETE,"organization", old_value);
1059                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_DELETE, "organization_id", old_org_id);
1060         } else if (new_value && !old_value) {
1061                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_ADD, "organization", new_value);
1062                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_ADD, "organization_id", new_org_id);
1063         }
1064 }
1065
1066 static void 
1067 set_categories_in_gw_item (EGwItem *item, EContact *contact, EBookBackendGroupwise *egwb)
1068 {
1069         GHashTable *categories_by_name;
1070         GList *category_names,  *category_ids;
1071         char *id;
1072         int status;
1073
1074         categories_by_name = egwb->priv->categories_by_name;
1075         category_names = e_contact_get (contact, E_CONTACT_CATEGORY_LIST);
1076         category_ids = NULL;
1077         id = NULL;
1078         for (; category_names != NULL; category_names = g_list_next (category_names)) {
1079                 if (!category_names->data || strlen(category_names->data) == 0 )
1080                         continue;
1081                 id = g_hash_table_lookup (categories_by_name, category_names->data);
1082                 if (id) 
1083                         category_ids = g_list_append (category_ids, g_strdup (id));
1084                 else {
1085                         EGwItem *category_item;
1086
1087                         category_item = e_gw_item_new_empty();
1088                         e_gw_item_set_item_type (category_item,  E_GW_ITEM_TYPE_CATEGORY);
1089                         e_gw_item_set_category_name (category_item, category_names->data);
1090                         status = e_gw_connection_create_item (egwb->priv->cnc, category_item, &id);
1091                         if (status == E_GW_CONNECTION_STATUS_OK && id != NULL) {
1092                                 char **components = g_strsplit (id, "@", -1);
1093                                 char *temp_id = components[0];
1094                                                         
1095                                 g_hash_table_insert (categories_by_name, g_strdup (category_names->data), g_strdup(temp_id));
1096                                 g_hash_table_insert (egwb->priv->categories_by_id, g_strdup(temp_id), g_strdup (category_names->data));
1097                                 category_ids = g_list_append (category_ids, g_strdup(temp_id));
1098                                 g_free (id);
1099                                 g_strfreev(components);
1100                         }
1101                         g_object_unref (category_item);
1102                 }
1103         }
1104         e_gw_item_set_categories (item, category_ids);  
1105 }
1106
1107 static void 
1108 set_categories_changes (EGwItem *new_item, EGwItem *old_item)
1109 {
1110         GList *old_category_list;
1111         GList *new_category_list;
1112         GList *temp, *old_categories_copy, *added_categories = NULL;
1113         gboolean categories_matched;
1114         char *category1, *category2;
1115
1116         old_category_list = e_gw_item_get_categories (old_item);
1117         new_category_list = e_gw_item_get_categories (new_item);
1118
1119         if (old_category_list && new_category_list) {
1120                 old_categories_copy = g_list_copy (old_category_list);
1121
1122                 for ( ; new_category_list != NULL; new_category_list = g_list_next (new_category_list)) {
1123                         category1  = new_category_list->data;
1124                         temp = old_category_list;
1125                         categories_matched  = FALSE;
1126
1127                         for(; temp != NULL; temp = g_list_next (temp)) {
1128                                 category2 = temp->data;
1129                                 if ( g_str_equal (category1, category2)) {
1130                                         categories_matched = TRUE;
1131                                         old_categories_copy = g_list_remove (old_categories_copy, category2);
1132                                         break;
1133                                 }
1134                         }
1135                         if (!categories_matched)
1136                                 added_categories = g_list_append (added_categories, category1);
1137                 }
1138                 
1139                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_ADD, "categories", added_categories);
1140                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_DELETE, "categories", old_categories_copy);
1141         } else if (!new_category_list && old_category_list) {
1142                 e_gw_item_set_change (new_item,  E_GW_ITEM_CHANGE_TYPE_DELETE, "categories", old_category_list);
1143         } else if (new_category_list && !old_category_list) {
1144                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_ADD, "categories", new_category_list);
1145         }
1146 }
1147
1148 static void 
1149 fill_contact_from_gw_item (EContact *contact, EGwItem *item, GHashTable *categories_by_ids)
1150 {
1151         char* value;
1152         int element_type;
1153         int i;
1154         gboolean is_contact_list;
1155         
1156         is_contact_list = e_gw_item_get_item_type (item) == E_GW_ITEM_TYPE_GROUP ? TRUE: FALSE;
1157         e_contact_set (contact, E_CONTACT_IS_LIST, GINT_TO_POINTER (is_contact_list));
1158         if (is_contact_list)
1159                 e_contact_set (contact, E_CONTACT_LIST_SHOW_ADDRESSES, GINT_TO_POINTER (TRUE));
1160
1161         for ( i = 0; i < G_N_ELEMENTS (mappings); i++) {
1162                 element_type = mappings[i].element_type;
1163
1164                 if(element_type == ELEMENT_TYPE_SIMPLE) {
1165                         if (mappings[i].field_id != E_CONTACT_BOOK_URI) {
1166                                 value = e_gw_item_get_field_value (item, mappings[i].element_name);
1167                                 if(value != NULL)
1168                                         e_contact_set (contact, mappings[i].field_id, value);
1169                         }
1170                 } else if (element_type == ELEMENT_TYPE_COMPLEX) {
1171                         if (mappings[i].field_id == E_CONTACT_CATEGORIES) {
1172                                 GList *category_ids, *category_names;
1173                                 char *name;
1174
1175                                 category_names = NULL;
1176                                 category_ids = e_gw_item_get_categories (item);
1177                                 for (; category_ids; category_ids = g_list_next (category_ids)) {
1178                                         name = g_hash_table_lookup (categories_by_ids, category_ids->data);
1179                                         if (name)
1180                                                 category_names = g_list_append (category_names, name);
1181                                 }
1182                                 if (category_names) {
1183                                         e_contact_set (contact, E_CONTACT_CATEGORY_LIST, category_names);
1184                                         g_list_free (category_names);
1185                                 }
1186                         }
1187                         else      
1188                                 mappings[i].populate_contact_func(contact, item);
1189                 }
1190         }
1191 }
1192
1193 static void
1194 e_book_backend_groupwise_create_contact (EBookBackend *backend,
1195                                          EDataBook *book,
1196                                          guint32 opid,
1197                                          const char *vcard )
1198 {
1199         EContact *contact;
1200         EBookBackendGroupwise *egwb;
1201         char *id;
1202         int status;
1203         EGwItem *item;
1204         int element_type;
1205         char* value;
1206         int i;
1207
1208         if (enable_debug)
1209                 printf("\ne_book_backend_groupwise_create_contact...\n");
1210
1211         egwb = E_BOOK_BACKEND_GROUPWISE (backend);
1212
1213         switch (egwb->priv->mode) {
1214
1215         case GNOME_Evolution_Addressbook_MODE_LOCAL :
1216                 e_data_book_respond_create(book, opid, GNOME_Evolution_Addressbook_RepositoryOffline, NULL);
1217                 return;
1218            
1219         case  GNOME_Evolution_Addressbook_MODE_REMOTE : 
1220                 
1221                 if (egwb->priv->cnc == NULL) {
1222                         e_data_book_respond_create(book, opid, GNOME_Evolution_Addressbook_AuthenticationRequired, NULL);
1223                         return;
1224                 }
1225                 if (!egwb->priv->is_writable) {
1226                         e_data_book_respond_create(book, opid, GNOME_Evolution_Addressbook_PermissionDenied, NULL);
1227                         return;
1228                 }
1229                 contact = e_contact_new_from_vcard(vcard);
1230                 item = e_gw_item_new_empty ();
1231                 e_gw_item_set_item_type (item, e_contact_get (contact, E_CONTACT_IS_LIST) ? E_GW_ITEM_TYPE_GROUP :E_GW_ITEM_TYPE_CONTACT);
1232                 e_gw_item_set_container_id (item, g_strdup(egwb->priv->container_id));
1233                 
1234                 for (i = 0; i < G_N_ELEMENTS (mappings); i++) {
1235                         element_type = mappings[i].element_type;
1236                         if (element_type == ELEMENT_TYPE_SIMPLE)  {
1237                                 value =  e_contact_get(contact, mappings[i].field_id);
1238                                 if (mappings[i].field_id == E_CONTACT_ORG) {
1239                                         set_organization_in_gw_item (item, contact, egwb);
1240                                         continue;
1241                                 }
1242                                 if (value != NULL)
1243                                         e_gw_item_set_field_value (item, mappings[i].element_name, value);
1244                         } else if (element_type == ELEMENT_TYPE_COMPLEX) {
1245                                 if (mappings[i].field_id == E_CONTACT_CATEGORIES) {
1246                                         set_categories_in_gw_item (item, contact, egwb);
1247                                 }
1248                                 else if (mappings[i].field_id == E_CONTACT_EMAIL) {
1249                                         if (e_contact_get (contact, E_CONTACT_IS_LIST))
1250                                                 set_members_in_gw_item (item, contact, egwb);
1251                                 }
1252                                 else {
1253                                         mappings[i].set_value_in_gw_item (item, contact);
1254                                 }
1255                         }
1256                 }
1257                 id = NULL;
1258                 status = e_gw_connection_create_item (egwb->priv->cnc, item, &id);  
1259                 if (status == E_GW_CONNECTION_STATUS_INVALID_CONNECTION) 
1260                         status = e_gw_connection_create_item (egwb->priv->cnc, item, &id);  
1261
1262                 /* Make sure server has returned  an id for the created contact */
1263                 if (status == E_GW_CONNECTION_STATUS_OK && id) {
1264                         e_contact_set (contact, E_CONTACT_UID, id);
1265                         g_free (id);
1266                         e_book_backend_db_cache_add_contact (egwb->priv->file_db, contact);
1267                         egwb->priv->file_db->sync(egwb->priv->file_db, 0);
1268                         e_book_backend_summary_add_contact (egwb->priv->summary, contact);
1269                         e_data_book_respond_create(book, opid, GNOME_Evolution_Addressbook_Success, contact);
1270                         
1271                 }
1272                 else {
1273                         e_data_book_respond_create(book, opid, GNOME_Evolution_Addressbook_OtherError, NULL);
1274                 }
1275                 g_object_unref (item);
1276                 return;
1277         default:
1278                 break;
1279         }
1280 }
1281
1282 static void
1283 e_book_backend_groupwise_remove_contacts (EBookBackend *backend,
1284                                           EDataBook    *book,
1285                                           guint32 opid,
1286                                           GList *id_list)
1287 {
1288         char *id;
1289         EBookBackendGroupwise *ebgw;
1290         GList *deleted_ids = NULL;
1291
1292         if (enable_debug)
1293                 printf ("\ne_book_backend_groupwise_remove_contacts...\n");
1294
1295         ebgw = E_BOOK_BACKEND_GROUPWISE (backend);
1296         
1297         switch (ebgw->priv->mode) {
1298
1299         case GNOME_Evolution_Addressbook_MODE_LOCAL :
1300                 e_data_book_respond_remove_contacts (book, opid, GNOME_Evolution_Addressbook_RepositoryOffline, NULL);
1301                 return;
1302
1303         case GNOME_Evolution_Addressbook_MODE_REMOTE : 
1304                 if (ebgw->priv->cnc == NULL) {
1305                         e_data_book_respond_remove_contacts (book, opid, GNOME_Evolution_Addressbook_AuthenticationRequired, NULL);
1306                         return;
1307                 }
1308                 
1309                 if (!ebgw->priv->is_writable) {
1310                         e_data_book_respond_remove_contacts (book, opid, GNOME_Evolution_Addressbook_PermissionDenied, NULL);
1311                         return;
1312                 }
1313                 
1314                 for ( ; id_list != NULL; id_list = g_list_next (id_list)) {
1315                         id = (char*) id_list->data;
1316                         e_gw_connection_remove_item (ebgw->priv->cnc, ebgw->priv->container_id, id);
1317                         deleted_ids =  g_list_append (deleted_ids, id);
1318                         e_book_backend_db_cache_remove_contact (ebgw->priv->file_db, id);
1319                         e_book_backend_summary_remove_contact (ebgw->priv->summary, id);
1320                 }
1321                 ebgw->priv->file_db->sync(ebgw->priv->file_db, 0);
1322                 e_data_book_respond_remove_contacts (book, opid,
1323                                                      GNOME_Evolution_Addressbook_Success,  deleted_ids);
1324                 return;
1325         default :
1326                 break;
1327         }
1328 }
1329
1330 static void 
1331 set_changes_in_gw_item (EGwItem *new_item, EGwItem *old_item)
1332 {
1333         char* new_value;
1334         char *old_value;
1335         int element_type;
1336         int i;
1337
1338         g_return_if_fail (E_IS_GW_ITEM(new_item));
1339         g_return_if_fail (E_IS_GW_ITEM(old_item));
1340         
1341         for ( i = 0; i < G_N_ELEMENTS (mappings); i++) {
1342                 element_type = mappings[i].element_type;
1343                 if (element_type == ELEMENT_TYPE_SIMPLE) {
1344                         if (mappings[i].field_id == E_CONTACT_ORG) {
1345                                 set_organization_changes_in_gw_item (new_item, old_item);
1346                                 continue;
1347                         }
1348                 
1349                         new_value = e_gw_item_get_field_value (new_item, mappings[i].element_name);
1350                         old_value = e_gw_item_get_field_value (old_item, mappings[i].element_name);
1351                         if (new_value && old_value) {
1352                                 if (!g_str_equal (new_value, old_value))
1353                                         e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_UPDATE, mappings[i].element_name, new_value);
1354                         } else if (!new_value  && old_value) {
1355                                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_DELETE, mappings[i].element_name, old_value);
1356                         } else if (new_value && !old_value) {
1357                                 e_gw_item_set_change (new_item, E_GW_ITEM_CHANGE_TYPE_ADD, mappings[i].element_name, new_value);
1358                         }
1359                                                                 
1360                 } else if (element_type == ELEMENT_TYPE_COMPLEX) {
1361                         if (mappings[i].field_id != E_CONTACT_EMAIL)
1362                                 mappings[i].set_changes(new_item, old_item);
1363                 }
1364         }
1365 }
1366
1367 static void
1368 e_book_backend_groupwise_modify_contact (EBookBackend *backend,
1369                                          EDataBook    *book,
1370                                          guint32       opid,
1371                                          const char   *vcard)
1372 {       
1373         EContact *contact;
1374         EBookBackendGroupwise *egwb;
1375         char *id;
1376         int status;
1377         EGwItem *new_item;
1378         EGwItem *old_item;
1379         int element_type;
1380         char* value;
1381         char *new_org, *old_org;
1382         int i;
1383
1384         if (enable_debug)
1385                 printf ("\ne_book_backend_groupwise_modify_contact...\n");      
1386         egwb = E_BOOK_BACKEND_GROUPWISE (backend);
1387
1388         switch (egwb->priv->mode) {
1389
1390         case GNOME_Evolution_Addressbook_MODE_LOCAL :
1391                 e_data_book_respond_modify(book, opid, GNOME_Evolution_Addressbook_RepositoryOffline, NULL);
1392                 return;
1393         case GNOME_Evolution_Addressbook_MODE_REMOTE :
1394                         
1395                 if (egwb->priv->cnc == NULL) {
1396                         e_data_book_respond_modify (book, opid, GNOME_Evolution_Addressbook_AuthenticationRequired, NULL);
1397                         return;
1398                 }
1399                 if (!egwb->priv->is_writable) {
1400                         e_data_book_respond_modify (book, opid, GNOME_Evolution_Addressbook_PermissionDenied, NULL);
1401                         return;
1402                 }
1403                 contact = e_contact_new_from_vcard(vcard);
1404                 new_item = e_gw_item_new_empty ();
1405
1406                 for (i = 0; i < G_N_ELEMENTS (mappings); i++) {
1407                         element_type = mappings[i].element_type;
1408                         if (element_type == ELEMENT_TYPE_SIMPLE)  {
1409                                 value =  e_contact_get(contact, mappings[i].field_id);
1410                                 if (value &&  *value)
1411                                         e_gw_item_set_field_value (new_item, mappings[i].element_name, value);
1412                         } else if (element_type == ELEMENT_TYPE_COMPLEX) {
1413                                 if (mappings[i].field_id == E_CONTACT_CATEGORIES) 
1414                                         set_categories_in_gw_item (new_item, contact, egwb);
1415                                 else if (mappings[i].field_id == E_CONTACT_EMAIL) {
1416                                         if (e_contact_get (contact, E_CONTACT_IS_LIST))
1417                                                 set_members_in_gw_item (new_item, contact, egwb);
1418                                 }
1419                                 else
1420                                         mappings[i].set_value_in_gw_item (new_item, contact);
1421                         }
1422                 }
1423         
1424                 id = e_contact_get (contact, E_CONTACT_UID);
1425                 old_item = NULL;
1426                 status = e_gw_connection_get_item (egwb->priv->cnc, egwb->priv->container_id, id, NULL,  &old_item);
1427                 
1428                 if (old_item == NULL) {
1429                         e_data_book_respond_modify (book, opid, GNOME_Evolution_Addressbook_ContactNotFound, NULL);
1430                         return;
1431                 }
1432                 
1433                 if (status != E_GW_CONNECTION_STATUS_OK) {
1434                         e_data_book_respond_modify (book, opid, GNOME_Evolution_Addressbook_OtherError, NULL);
1435                         return;
1436                 }
1437                 
1438                 if (e_contact_get (contact, E_CONTACT_IS_LIST))
1439                         set_member_changes (new_item, old_item, egwb);
1440                 new_org = e_gw_item_get_field_value (new_item, "organization");
1441                 old_org = e_gw_item_get_field_value (old_item, "organization");
1442                 if (new_org && *new_org) {
1443                         
1444                         if ((old_org == NULL) || (old_org && strcmp (new_org, old_org)) != 0)
1445                                 set_organization_in_gw_item (new_item, contact, egwb);
1446                 }
1447                 
1448                 set_changes_in_gw_item (new_item, old_item);
1449                 
1450                 e_gw_item_set_item_type (new_item, e_gw_item_get_item_type (old_item));
1451                 status = e_gw_connection_modify_item (egwb->priv->cnc, id, new_item);
1452                 if (status == E_GW_CONNECTION_STATUS_OK) {
1453                         e_data_book_respond_modify (book, opid, GNOME_Evolution_Addressbook_Success, contact);
1454                         e_book_backend_db_cache_remove_contact (egwb->priv->file_db, id);
1455                         e_book_backend_summary_remove_contact (egwb->priv->summary, id);
1456                         e_book_backend_db_cache_add_contact (egwb->priv->file_db, contact);
1457                         egwb->priv->file_db->sync(egwb->priv->file_db, 0);
1458                         e_book_backend_summary_add_contact (egwb->priv->summary, contact);
1459                 }
1460                 else 
1461                         e_data_book_respond_modify (book, opid, GNOME_Evolution_Addressbook_OtherError, NULL);
1462                 g_object_unref (new_item);
1463                 g_object_ref (old_item);
1464                 g_object_unref (contact);
1465                 return;
1466         default :
1467                 break;
1468         }
1469 }
1470         
1471 static void
1472 e_book_backend_groupwise_get_contact (EBookBackend *backend,
1473                                       EDataBook    *book,
1474                                       guint32       opid,
1475                                       const char   *id)
1476 {
1477         EBookBackendGroupwise *gwb;
1478         int status ;
1479         EGwItem *item;
1480         EContact *contact;
1481         char *vcard;
1482
1483         if (enable_debug)
1484                 printf ("\ne_book_backend_groupwise_get_contact...\n"); 
1485
1486         gwb =  E_BOOK_BACKEND_GROUPWISE (backend);
1487
1488         switch (gwb->priv->mode) {
1489
1490         case GNOME_Evolution_Addressbook_MODE_LOCAL :
1491                 contact = e_book_backend_db_cache_get_contact (gwb->priv->file_db, id);
1492                 vcard =  e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
1493                 if (contact) {
1494                         e_data_book_respond_get_contact(book, opid, GNOME_Evolution_Addressbook_Success, vcard);
1495                         g_free (vcard);
1496                         g_object_unref (contact);
1497                 }
1498                 else {
1499                         e_data_book_respond_get_contact(book, opid, GNOME_Evolution_Addressbook_ContactNotFound, "");
1500                 }
1501                 return;
1502
1503         case GNOME_Evolution_Addressbook_MODE_REMOTE :  
1504                 if (gwb->priv->cnc == NULL) {
1505                         e_data_book_respond_get_contact (book, opid, GNOME_Evolution_Addressbook_OtherError, NULL);
1506                         return;
1507                 }
1508                 status = e_gw_connection_get_item (gwb->priv->cnc, gwb->priv->container_id, id,
1509                                                    "name email default members", &item);
1510                 if (status == E_GW_CONNECTION_STATUS_OK) {
1511                         if (item) {
1512                                 contact = e_contact_new ();
1513                                 fill_contact_from_gw_item (contact, item, gwb->priv->categories_by_id);
1514                                 e_contact_set (contact, E_CONTACT_BOOK_URI, gwb->priv->original_uri);
1515                                 vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
1516                                 e_data_book_respond_get_contact (book, opid, GNOME_Evolution_Addressbook_Success, vcard);
1517                                 g_free (vcard);
1518                                 g_object_unref (contact);
1519                                 g_object_unref (item);
1520                                 return;
1521                         }
1522                 }
1523                 e_data_book_respond_get_contact (book, opid, GNOME_Evolution_Addressbook_ContactNotFound, "");  
1524                 return;
1525         default :
1526                 break;
1527         }
1528 }
1529
1530 typedef struct {
1531         EGwFilter *filter;
1532         gboolean is_filter_valid;
1533         gboolean is_personal_book;
1534         int auto_completion; 
1535         char *search_string;
1536 } EBookBackendGroupwiseSExpData;
1537
1538 static ESExpResult *
1539 func_and(ESExp *f, int argc, ESExpResult **argv, void *data)
1540 {
1541         ESExpResult *r;
1542         EGwFilter *filter;
1543         EBookBackendGroupwiseSExpData *sexp_data;
1544
1545         sexp_data = (EBookBackendGroupwiseSExpData *) data;
1546         filter = E_GW_FILTER (sexp_data->filter);
1547         if (argc > 0)
1548                 e_gw_filter_group_conditions (filter, E_GW_FILTER_OP_AND, argc);
1549         r = e_sexp_result_new(f, ESEXP_RES_BOOL);
1550         r->value.bool = FALSE;
1551
1552         return r;
1553 }
1554
1555 static ESExpResult *
1556 func_or(ESExp *f, int argc, ESExpResult **argv, void *data)
1557 {
1558         ESExpResult *r;
1559         EGwFilter *filter;
1560         EBookBackendGroupwiseSExpData *sexp_data;
1561
1562         sexp_data = (EBookBackendGroupwiseSExpData *) data;
1563         filter = E_GW_FILTER (sexp_data->filter);
1564         if (argc > 0)
1565                  e_gw_filter_group_conditions (filter, E_GW_FILTER_OP_OR, argc);
1566         r = e_sexp_result_new(f, ESEXP_RES_BOOL);
1567         r->value.bool = FALSE;
1568
1569         return r;
1570 }
1571
1572 static ESExpResult *
1573 func_not(ESExp *f, int argc, ESExpResult **argv, void *data)
1574 {
1575         ESExpResult *r;
1576         EBookBackendGroupwiseSExpData *sexp_data;
1577
1578         sexp_data = (EBookBackendGroupwiseSExpData *) data;
1579         sexp_data->is_filter_valid = FALSE;
1580         r = e_sexp_result_new(f, ESEXP_RES_BOOL);
1581         r->value.bool = FALSE;
1582
1583         return r;
1584 }
1585
1586 static ESExpResult *
1587 func_contains (struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
1588 {
1589         ESExpResult *r;
1590         EGwFilter *filter;
1591         EBookBackendGroupwiseSExpData *sexp_data;
1592
1593         sexp_data = (EBookBackendGroupwiseSExpData *) data;
1594         filter = E_GW_FILTER (sexp_data->filter);
1595
1596         if (argc == 2
1597             && argv[0]->type == ESEXP_RES_STRING
1598             && argv[1]->type == ESEXP_RES_STRING) {
1599                 char *propname = argv[0]->value.string;
1600                 char *str = argv[1]->value.string;
1601                 char *gw_field_name;
1602
1603                 if (g_str_equal (propname, "x-evolution-any-field")) {
1604                         if (!sexp_data->is_personal_book && str && strlen(str) == 0) {
1605                                 /* ignore the NULL query */
1606                                 sexp_data->is_filter_valid = FALSE;
1607                                 r = e_sexp_result_new(f, ESEXP_RES_BOOL);
1608                                 r->value.bool = FALSE;
1609                                 return r;
1610                         }
1611                 }
1612                 gw_field_name = NULL;
1613                 if (g_str_equal (propname, "full_name"))
1614                         gw_field_name = "fullName";
1615                 else if (g_str_equal (propname, "email"))
1616                         gw_field_name = "emailList/email";
1617                 else if (g_str_equal (propname, "file_as") || g_str_equal (propname, "nickname"))
1618                          gw_field_name = "name";
1619                 
1620                 if (gw_field_name) {
1621                         if (g_str_equal (gw_field_name, "fullName")) {
1622                                 e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_CONTAINS, "fullName/firstName", str);  
1623                                 e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_CONTAINS, "fullName/lastName", str);
1624                                 if (sexp_data->is_personal_book) {
1625                                         e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_CONTAINS, "fullName/displayName", str);
1626                                         e_gw_filter_group_conditions (filter, E_GW_FILTER_OP_OR, 3);
1627                                 }
1628                                 else { 
1629                                         e_gw_filter_group_conditions (filter, E_GW_FILTER_OP_OR, 2);
1630                                 }
1631                         }
1632                         else { 
1633                                 e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_CONTAINS, gw_field_name, str);
1634                         }
1635                 }
1636                 else { 
1637                      sexp_data->is_filter_valid = FALSE;
1638                 }
1639         }
1640
1641         r = e_sexp_result_new(f, ESEXP_RES_BOOL);
1642         r->value.bool = FALSE;
1643
1644         return r;
1645 }
1646
1647 static ESExpResult *
1648 func_is(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
1649 {
1650         ESExpResult *r;
1651         EGwFilter *filter;
1652         EBookBackendGroupwiseSExpData *sexp_data;
1653
1654         sexp_data = (EBookBackendGroupwiseSExpData *) data;
1655         filter = E_GW_FILTER (sexp_data->filter);
1656
1657         if (argc == 2
1658             && argv[0]->type == ESEXP_RES_STRING
1659             && argv[1]->type == ESEXP_RES_STRING) {
1660                 char *propname = argv[0]->value.string;
1661                 char *str = argv[1]->value.string;
1662                 char *gw_field_name;
1663         
1664                 gw_field_name = NULL;
1665                 if (g_str_equal (propname, "full_name"))
1666                         gw_field_name = "fullName";
1667                 else if (g_str_equal (propname, "email"))
1668                         gw_field_name = "emailList/email";
1669                 else if (g_str_equal (propname, "file_as") || g_str_equal (propname, "nickname"))
1670                         gw_field_name = "name";
1671                 
1672                 if (gw_field_name) {
1673                         if (g_str_equal (gw_field_name, "fullName")) {
1674                                 e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_EQUAL, "fullName/firstName", str);     
1675                                 e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_EQUAL, "fullName/lastName", str);
1676                                 if (sexp_data->is_personal_book) {
1677                                         e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_EQUAL, "fullName/displayName", str);
1678                                         e_gw_filter_group_conditions (filter, E_GW_FILTER_OP_OR, 3);
1679                                 }
1680                                 else { 
1681                                         e_gw_filter_group_conditions (filter, E_GW_FILTER_OP_OR, 2);
1682                                 }
1683                         }
1684                         else {
1685                                 e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_EQUAL, gw_field_name, str);
1686                         }
1687                 }
1688                 else {
1689                      sexp_data->is_filter_valid = FALSE;
1690                 }
1691         }
1692
1693         r = e_sexp_result_new(f, ESEXP_RES_BOOL);
1694         r->value.bool = FALSE;
1695
1696         return r;
1697 }
1698
1699 #define BEGINS_WITH_NAME (1 << 0)
1700 #define BEGINS_WITH_EMAIL (1 << 1)
1701 #define BEGINS_WITH_FILE_AS (1 << 2)
1702 #define BEGINS_WITH_NICK_NAME (1 << 3)
1703 #define AUTO_COMPLETION_QUERY 15
1704
1705 static ESExpResult *
1706 func_beginswith(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
1707 {
1708         ESExpResult *r;
1709         EGwFilter *filter;
1710         EBookBackendGroupwiseSExpData *sexp_data;
1711
1712         sexp_data = (EBookBackendGroupwiseSExpData *) data;
1713         filter = E_GW_FILTER (sexp_data->filter);
1714
1715         if (argc == 2
1716             && argv[0]->type == ESEXP_RES_STRING
1717             && argv[1]->type == ESEXP_RES_STRING) {
1718                 char *propname = argv[0]->value.string;
1719                 char *str = argv[1]->value.string;
1720                 char *gw_field_name;
1721
1722                 if (!sexp_data->is_personal_book && str && strlen(str) == 0) {
1723                         /* ignore the NULL query */
1724                         sexp_data->is_filter_valid = FALSE;
1725                         r = e_sexp_result_new(f, ESEXP_RES_BOOL);
1726                         r->value.bool = FALSE;
1727                         return r;
1728                 }
1729
1730                 gw_field_name = NULL;
1731                 if (g_str_equal (propname, "full_name")) {
1732                         gw_field_name = "fullName";
1733                         sexp_data->auto_completion |= BEGINS_WITH_NAME;
1734                         sexp_data->search_string = g_strdup (str);
1735                 }
1736                 else if (g_str_equal (propname, "email")) {
1737                         gw_field_name = "emailList/email";
1738                         sexp_data->auto_completion |= BEGINS_WITH_EMAIL;
1739                 }
1740                 else if (g_str_equal (propname, "file_as")) { 
1741                          gw_field_name = "name";
1742                          sexp_data->auto_completion |= BEGINS_WITH_FILE_AS;
1743                 } else if (g_str_equal (propname, "nickname")) { 
1744                          gw_field_name = "name";
1745                          sexp_data->auto_completion |= BEGINS_WITH_NICK_NAME;
1746                 }
1747
1748                 if (gw_field_name) {
1749                         
1750                         if (g_str_equal (gw_field_name, "fullName")) {
1751                                 e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_BEGINS, "fullName/firstName", str);    
1752                                 e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_BEGINS, "fullName/lastName", str);
1753                                 if (sexp_data->is_personal_book) {
1754                                         e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_BEGINS, "fullName/displayName", str);
1755                                         e_gw_filter_group_conditions (filter, E_GW_FILTER_OP_OR, 3);
1756                                 }
1757                                 else { 
1758                                         e_gw_filter_group_conditions (filter, E_GW_FILTER_OP_OR, 2);
1759                                 }
1760                         }
1761                         else {
1762                                 e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_BEGINS, gw_field_name, str);
1763                         }
1764                 }
1765                 else {
1766                         sexp_data->is_filter_valid = FALSE;
1767                 }
1768         }
1769
1770         r = e_sexp_result_new(f, ESEXP_RES_BOOL);
1771         r->value.bool = FALSE;
1772
1773         return r;
1774 }
1775
1776 static ESExpResult *
1777 func_endswith(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
1778 {
1779         EBookBackendGroupwiseSExpData *sexp_data;
1780         ESExpResult *r;
1781
1782         sexp_data = (EBookBackendGroupwiseSExpData *) data;
1783         sexp_data->is_filter_valid = FALSE;
1784         
1785         r = e_sexp_result_new(f, ESEXP_RES_BOOL);
1786         
1787         r->value.bool = FALSE; 
1788
1789         return r;
1790 }
1791
1792 static ESExpResult *
1793 func_exists(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
1794 {
1795         ESExpResult *r;
1796         EGwFilter *filter;
1797         EBookBackendGroupwiseSExpData *sexp_data;
1798
1799         sexp_data = (EBookBackendGroupwiseSExpData *) data;
1800         filter = E_GW_FILTER (sexp_data->filter);
1801
1802         if (argc == 1
1803             && argv[0]->type == ESEXP_RES_STRING) {
1804                 char *propname = argv[0]->value.string;
1805                 char *str = argv[1]->value.string;
1806                 char *gw_field_name;
1807         
1808                 gw_field_name = NULL;
1809                 if (g_str_equal (propname, "full_name"))
1810                         gw_field_name = "fullName";
1811                 else if (g_str_equal (propname, "email"))
1812                         gw_field_name = "emailList/email";
1813                 else if (g_str_equal (propname, "file_as") || g_str_equal (propname, "nickname"))
1814                          gw_field_name = "name";
1815
1816                 if (gw_field_name) {
1817                         
1818                         if (g_str_equal (gw_field_name, "fullName")) {
1819                                 e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_EXISTS, "fullName/firstName", str);    
1820                                 e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_EXISTS, "fullName/lastName", str);
1821                                 if (sexp_data->is_personal_book) {
1822                                         e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_EXISTS, "fullName/displayName", str);
1823                                         e_gw_filter_group_conditions (filter, E_GW_FILTER_OP_OR, 3);
1824                                 }
1825                                 else { 
1826                                         e_gw_filter_group_conditions (filter, E_GW_FILTER_OP_OR, 2);
1827                                 }
1828                         }
1829                         else {
1830                                 e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_EXISTS, gw_field_name, str);
1831                         }
1832                 }
1833                 else { 
1834                         sexp_data->is_filter_valid = FALSE;
1835                 }
1836         }
1837
1838         r = e_sexp_result_new(f, ESEXP_RES_BOOL);
1839         r->value.bool = FALSE;
1840
1841         return r;
1842 }
1843
1844 /* 'builtin' functions */
1845 static const struct {
1846         char *name;
1847         ESExpFunc *func;
1848         int type;               /* set to 1 if a function can perform shortcut evaluation, or
1849                                    doesn't execute everything, 0 otherwise */
1850 } symbols[] = {
1851         { "and", func_and, 0 },
1852         { "or", func_or, 0 },
1853         { "not", func_not, 0 },
1854         { "contains", func_contains, 0 },
1855         { "is", func_is, 0 },
1856         { "beginswith", func_beginswith, 0 },
1857         { "endswith", func_endswith, 0 },
1858         { "exists", func_exists, 0 },
1859 };
1860
1861 static EGwFilter*
1862 e_book_backend_groupwise_build_gw_filter (EBookBackendGroupwise *ebgw, const char *query, gpointer is_auto_completion, char ** search_string)
1863 {
1864         ESExp *sexp;
1865         ESExpResult *r;
1866         EBookBackendGroupwiseSExpData *sexp_data;
1867         EGwFilter *filter;
1868         int i;
1869
1870         sexp = e_sexp_new();
1871         filter = e_gw_filter_new ();
1872         
1873         sexp_data = g_new0 (EBookBackendGroupwiseSExpData, 1);
1874         sexp_data->filter = filter;
1875         sexp_data->is_filter_valid = TRUE;
1876         sexp_data->is_personal_book = e_book_backend_is_writable ( E_BOOK_BACKEND (ebgw));
1877         sexp_data->auto_completion = 0;
1878         sexp_data->search_string = NULL;
1879
1880         for(i=0;i<sizeof(symbols)/sizeof(symbols[0]);i++) {
1881                 if (symbols[i].type == 1) {
1882                         e_sexp_add_ifunction(sexp, 0, symbols[i].name,
1883                                              (ESExpIFunc *)symbols[i].func, sexp_data);
1884                 } else {
1885                         e_sexp_add_function(sexp, 0, symbols[i].name,
1886                                             symbols[i].func, sexp_data);
1887                 }
1888         }
1889
1890         e_sexp_input_text(sexp, query, strlen(query));
1891         e_sexp_parse(sexp);
1892         r = e_sexp_eval(sexp);
1893         e_sexp_result_free(sexp, r);
1894         e_sexp_unref (sexp);
1895         
1896         if (sexp_data->is_filter_valid) {
1897                 if (sexp_data->auto_completion == AUTO_COMPLETION_QUERY)
1898                         *(gboolean *)is_auto_completion = TRUE;
1899                 if (search_string)
1900                         *search_string = sexp_data->search_string;
1901                 g_free (sexp_data);
1902                 return filter;
1903         }
1904         else {
1905                 g_object_unref (filter);
1906                 g_free (sexp_data);
1907                 return NULL;
1908         }
1909 }
1910
1911 static void
1912 e_book_backend_groupwise_get_contact_list (EBookBackend *backend,
1913                                            EDataBook    *book,
1914                                            guint32       opid,
1915                                            const char   *query )
1916 {
1917         GList *vcard_list;
1918         int status;
1919         GList *gw_items, *contacts = NULL, *temp;
1920         EContact *contact;
1921         EBookBackendGroupwise *egwb;
1922         gboolean match_needed;
1923         EBookBackendSExp *card_sexp = NULL;
1924         EGwFilter *filter = NULL;
1925         GPtrArray *ids;
1926         gboolean is_auto_completion;
1927
1928         egwb = E_BOOK_BACKEND_GROUPWISE (backend);
1929         vcard_list = NULL;
1930         gw_items = NULL;
1931
1932         if (enable_debug)
1933                 printf ("\ne_book_backend_groupwise_get_contact_list...\n");
1934
1935         switch (egwb->priv->mode) {
1936                 
1937         case GNOME_Evolution_Addressbook_MODE_LOCAL :
1938
1939                 if (!egwb->priv->file_db) {
1940                         e_data_book_respond_get_contact_list (book, opid, GNOME_Evolution_Addressbook_RepositoryOffline, NULL);
1941                         return;
1942                 }
1943                 
1944                 if (egwb->priv->is_summary_ready && 
1945                     e_book_backend_summary_is_summary_query (egwb->priv->summary, query)) {
1946                         int i;
1947                         ids = e_book_backend_summary_search (egwb->priv->summary, query);
1948                         for (i = 0; i < ids->len; i ++) {
1949                                 char *uid = g_ptr_array_index (ids, i);
1950
1951                                 EContact *contact = 
1952                                         e_book_backend_db_cache_get_contact (egwb->priv->file_db, uid);
1953                                 contacts = g_list_append (contacts, contact);   
1954                         }
1955                         g_ptr_array_free (ids, TRUE);
1956                 }
1957                 else
1958                         contacts = e_book_backend_db_cache_get_contacts (egwb->priv->file_db, query);
1959
1960                 temp = contacts;
1961                 for (; contacts != NULL; contacts = g_list_next(contacts)) { 
1962                         vcard_list = g_list_append (vcard_list, 
1963                                                     e_vcard_to_string (E_VCARD (contacts->data), 
1964                                                     EVC_FORMAT_VCARD_30));
1965                         g_object_unref (contacts->data);
1966                 }
1967                 e_data_book_respond_get_contact_list (book, opid, GNOME_Evolution_Addressbook_Success,
1968                                                       vcard_list);
1969                 if (temp)
1970                         g_list_free (temp);
1971                 return;
1972                 
1973         case GNOME_Evolution_Addressbook_MODE_REMOTE:
1974
1975                 if (egwb->priv->cnc == NULL) {
1976                         e_data_book_respond_get_contact_list (book, opid, GNOME_Evolution_Addressbook_AuthenticationRequired, NULL);
1977                         return;
1978                 }
1979
1980                 match_needed = TRUE;
1981                 card_sexp = e_book_backend_sexp_new (query);
1982                 if (!card_sexp) {
1983                         e_data_book_respond_get_contact_list (book, opid, GNOME_Evolution_Addressbook_InvalidQuery,
1984                                                       vcard_list);
1985                 }
1986                 
1987                 status = E_GW_CONNECTION_STATUS_OK;
1988                 if (egwb->priv->is_cache_ready ) {
1989                         if (egwb->priv->is_summary_ready && 
1990                             e_book_backend_summary_is_summary_query (egwb->priv->summary, query)) {
1991                                 ids = e_book_backend_summary_search (egwb->priv->summary, query);
1992
1993                                 if (!egwb->priv->is_writable) {
1994                                         int i;
1995                                         for (i = 0; i < ids->len; i ++) {
1996                                                 char *uid = g_ptr_array_index (ids, i);
1997                                                 contact = e_book_backend_db_cache_get_contact (egwb->priv->file_db, uid);
1998                                                 vcard_list = g_list_append (vcard_list,
1999                                                             e_vcard_to_string (E_VCARD (contact),
2000                                                             EVC_FORMAT_VCARD_30));
2001                                                 g_object_unref (contact);
2002                                         }
2003                                         g_ptr_array_free (ids, TRUE);
2004                                         ids->len = 0;
2005                                 }
2006                         }
2007                         else {
2008                                 ids = e_book_backend_db_cache_search (egwb->priv->file_db, query);
2009                         }
2010
2011                         if (ids->len > 0) {
2012                                 status = e_gw_connection_get_items_from_ids (egwb->priv->cnc, 
2013                                                                         egwb->priv->container_id, 
2014                                                                         "name email default members", 
2015                                                                         ids, &gw_items);
2016                                 if (status == E_GW_CONNECTION_STATUS_INVALID_CONNECTION)
2017                                 status = e_gw_connection_get_items_from_ids (egwb->priv->cnc, 
2018                                                                         egwb->priv->container_id, 
2019                                                                         "name email default members", 
2020                                                                         ids, &gw_items);
2021                         g_ptr_array_free (ids, TRUE);
2022                         }
2023                         match_needed = FALSE;
2024                 } else { 
2025                         if (strcmp (query, "(contains \"x-evolution-any-field\" \"\")") != 0)
2026                                 filter = e_book_backend_groupwise_build_gw_filter (egwb, 
2027                                                                                    query, 
2028                                                                                    &is_auto_completion, 
2029                                                                                    NULL);
2030                         if (filter)
2031                                 match_needed = FALSE;
2032                         status = e_gw_connection_get_items (egwb->priv->cnc, 
2033                                                             egwb->priv->container_id, 
2034                                                             "name email default members", 
2035                                                             filter, &gw_items);
2036                         if (status == E_GW_CONNECTION_STATUS_INVALID_CONNECTION)
2037                                 status = e_gw_connection_get_items (egwb->priv->cnc, 
2038                                                                     egwb->priv->container_id, 
2039                                                                     "name email default members", 
2040                                                                     filter, &gw_items);
2041                 }
2042                 
2043                 if (status != E_GW_CONNECTION_STATUS_OK) {
2044                         e_data_book_respond_get_contact_list (book, opid, GNOME_Evolution_Addressbook_OtherError,
2045                                                               NULL);
2046                         return;
2047                 }
2048                 for (; gw_items != NULL; gw_items = g_list_next(gw_items)) {
2049                         contact = e_contact_new ();
2050                         fill_contact_from_gw_item (contact, E_GW_ITEM (gw_items->data), egwb->priv->categories_by_id);
2051                         e_contact_set (contact, E_CONTACT_BOOK_URI, egwb->priv->original_uri);
2052                         if (match_needed &&  e_book_backend_sexp_match_contact (card_sexp, contact))
2053                                 vcard_list = g_list_append (vcard_list, 
2054                                                             e_vcard_to_string (E_VCARD (contact), 
2055                                                             EVC_FORMAT_VCARD_30));
2056                         else 
2057                                 vcard_list = g_list_append (vcard_list, 
2058                                                             e_vcard_to_string (E_VCARD (contact), 
2059                                                             EVC_FORMAT_VCARD_30));
2060                         g_object_unref (contact);
2061                         g_object_unref (gw_items->data);
2062                 }
2063                 if (gw_items)
2064                         g_list_free (gw_items);
2065                 e_data_book_respond_get_contact_list (book, opid, GNOME_Evolution_Addressbook_Success,
2066                                                       vcard_list);
2067                 if (filter)
2068                         g_object_unref (filter);
2069                 return;
2070         default :
2071                 break;
2072                 
2073         }
2074 }
2075         
2076 typedef struct {
2077         EBookBackendGroupwise *bg;
2078         GThread *thread;
2079         EFlag *running;
2080 } GroupwiseBackendSearchClosure;
2081
2082 static void
2083 closure_destroy (GroupwiseBackendSearchClosure *closure)
2084 {
2085         e_flag_free (closure->running);
2086         g_free (closure);
2087 }
2088
2089 static GroupwiseBackendSearchClosure*
2090 init_closure (EDataBookView *book_view, EBookBackendGroupwise *bg)
2091 {
2092         GroupwiseBackendSearchClosure *closure = g_new (GroupwiseBackendSearchClosure, 1);
2093
2094         closure->bg = bg;
2095         closure->thread = NULL;
2096         closure->running = e_flag_new ();
2097
2098         g_object_set_data_full (G_OBJECT (book_view), "EBookBackendGroupwise.BookView::closure",
2099                                 closure, (GDestroyNotify)closure_destroy);
2100
2101         return closure;
2102 }
2103
2104 static GroupwiseBackendSearchClosure*
2105 get_closure (EDataBookView *book_view)
2106 {
2107         return g_object_get_data (G_OBJECT (book_view), "EBookBackendGroupwise.BookView::closure");
2108 }
2109
2110 static void
2111 get_contacts_from_cache (EBookBackendGroupwise *ebgw, 
2112                          const char *query,
2113                          GPtrArray *ids,
2114                          EDataBookView *book_view, 
2115                          GroupwiseBackendSearchClosure *closure)
2116 {
2117         int i;
2118
2119         if (enable_debug)
2120                 printf ("\nread contacts from cache for the ids found in summary\n");
2121         for (i = 0; i < ids->len; i ++) {
2122                 char *uid;
2123                 EContact *contact; 
2124
2125                 if (!e_flag_is_set (closure->running))
2126                         break;
2127
2128                 uid = g_ptr_array_index (ids, i);
2129                 contact = e_book_backend_db_cache_get_contact (ebgw->priv->file_db, uid);
2130                 if (contact) {
2131                         e_data_book_view_notify_update (book_view, contact);
2132                         g_object_unref (contact);
2133                 }
2134         }
2135         if (e_flag_is_set (closure->running))
2136                 e_data_book_view_notify_complete (book_view, 
2137                                                   GNOME_Evolution_Addressbook_Success);
2138 }
2139  
2140 static gpointer
2141 book_view_thread (gpointer data)
2142 {
2143         int status, count = 0;
2144         GList *gw_items, *temp_list, *contacts;
2145         EContact *contact;
2146         EBookBackendGroupwise *gwb;
2147         const char *query = NULL;
2148         EGwFilter *filter = NULL;
2149         GPtrArray *ids = NULL;
2150         EDataBookView *book_view = data;
2151         GroupwiseBackendSearchClosure *closure = get_closure (book_view);
2152         char *view = NULL;
2153         gboolean is_auto_completion = FALSE;
2154         char *search_string = NULL;
2155         GTimeVal start, end;
2156         unsigned long diff;
2157
2158         gwb  = closure->bg;
2159         gw_items = NULL;
2160
2161         if (enable_debug)
2162                 printf ("start book view for %s \n", gwb->priv->book_name);
2163         bonobo_object_ref (book_view);
2164         e_flag_set (closure->running);
2165         
2166         query = e_data_book_view_get_card_query (book_view);
2167         if (enable_debug)
2168                 printf ("get view for query %s \n", query);
2169         switch (gwb->priv->mode) {
2170
2171         case GNOME_Evolution_Addressbook_MODE_LOCAL :
2172                 if (!gwb->priv->file_db) {
2173                         e_data_book_view_notify_complete (book_view, GNOME_Evolution_Addressbook_Success);
2174                         return NULL;
2175                 }
2176
2177                 if (gwb->priv->is_summary_ready && 
2178                     e_book_backend_summary_is_summary_query (gwb->priv->summary, query)) {
2179                         if (enable_debug)
2180                                 printf ("reading the uids from summary \n");
2181                         ids = e_book_backend_summary_search (gwb->priv->summary, query);
2182                         if (ids && ids->len > 0) {
2183                                 get_contacts_from_cache (gwb, query, ids, book_view, closure);
2184                                 g_ptr_array_free (ids, TRUE);
2185                         }
2186                         bonobo_object_unref (book_view);
2187                         return NULL;
2188                 }
2189
2190                 /* fall back to cache */
2191                 if (enable_debug)
2192                         printf ("summary not found, reading the uids from cache\n");
2193                 contacts = e_book_backend_db_cache_get_contacts (gwb->priv->file_db, query);
2194                 temp_list = contacts;
2195                 for (; contacts != NULL; contacts = g_list_next(contacts)) {
2196                         if (!e_flag_is_set (closure->running)) {
2197                                 for (;contacts != NULL; contacts = g_list_next (contacts))
2198                                         g_object_unref (contacts->data);
2199                                 break;
2200                         }
2201                         e_data_book_view_notify_update (book_view, E_CONTACT(contacts->data));
2202                         g_object_unref (contacts->data);
2203                 }
2204                 if (e_flag_is_set (closure->running))
2205                         e_data_book_view_notify_complete (book_view, GNOME_Evolution_Addressbook_Success);
2206                 if (temp_list)
2207                         g_list_free (temp_list);
2208                 bonobo_object_unref (book_view);
2209                 return NULL;
2210                 
2211         case GNOME_Evolution_Addressbook_MODE_REMOTE :
2212                 
2213                 if (gwb->priv->cnc == NULL) {
2214                         e_data_book_view_notify_complete (book_view, 
2215                                                           GNOME_Evolution_Addressbook_AuthenticationRequired);
2216                         bonobo_object_unref (book_view);
2217                         return NULL; 
2218                 }
2219
2220                 if (enable_debug)
2221                         g_get_current_time(&start);
2222
2223                 filter = e_book_backend_groupwise_build_gw_filter (gwb, query, &is_auto_completion, &search_string);
2224                 view = "name email default members";
2225                 if (is_auto_completion) 
2226                         view = "name email";
2227
2228                 if (search_string) {
2229                         /* groupwise server supports only name, rebuild the filter */
2230                         filter = e_gw_filter_new ();
2231                         e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_BEGINS, 
2232                                                           "fullName/lastName", search_string);
2233                         e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_BEGINS, 
2234                                                           "fullName/firstName", search_string);
2235                         e_gw_filter_group_conditions (filter, E_GW_FILTER_OP_OR, 2);
2236                         g_free (search_string);
2237                 }
2238
2239                 if (!gwb->priv->is_writable && !filter) {
2240                         e_data_book_view_notify_complete (book_view, GNOME_Evolution_Addressbook_Success);
2241                         bonobo_object_unref (book_view);
2242                         return NULL; 
2243                 }
2244                 else 
2245                         status =  E_GW_CONNECTION_STATUS_OK;
2246
2247                 /* Check if the data is found on summary */
2248                 if (gwb->priv->is_summary_ready && 
2249                     e_book_backend_summary_is_summary_query (gwb->priv->summary, query)) {
2250                         if (enable_debug)
2251                                 printf("reading the uids from summary file\n");
2252                         ids = e_book_backend_summary_search (gwb->priv->summary, query);
2253                 }
2254
2255                 /*
2256                  * Search for contact in cache, if not found, read from server 
2257                  */
2258
2259                 if (ids && ids->len > 0) {
2260                         if (enable_debug)
2261                                 printf ("number of matches found in summary %d\n", ids->len);
2262                         /* read from summary */
2263                         if (gwb->priv->is_cache_ready && !gwb->priv->is_writable) {
2264                                 /* read from cache, only for system address book, as we refresh
2265                                  * only system address book, periodically. 
2266                                  */     
2267                                 if (enable_debug)
2268                                         printf ("reading contacts from cache for the uids in summary \n");
2269                                 if (!is_auto_completion)
2270                                         e_data_book_view_notify_status_message (book_view, 
2271                                                                                 _("Searching..."));
2272                                 get_contacts_from_cache (gwb, query, ids, book_view, closure);
2273                                 g_ptr_array_free (ids, TRUE);
2274                                 bonobo_object_unref (book_view);
2275                                 if (enable_debug) {
2276                                         g_get_current_time(&end);
2277                                         diff = end.tv_sec * 1000 + end.tv_usec/1000;
2278                                         diff -= start.tv_sec * 1000 + start.tv_usec/1000;
2279                                         printf("reading contacts from cache took %ld.%03ld seconds\n",
2280                                                 diff/1000,diff%1000);
2281                                 }
2282                                 return NULL;
2283                         }
2284                         else {
2285                                 /* read from server for the ids */
2286                                 /* either autocompletion or search query and cache not ready */
2287                                 if (enable_debug)
2288                                         printf ("reading contacts from server for the uids in summary \n");
2289                                 if (!is_auto_completion)
2290                                         e_data_book_view_notify_status_message (book_view, 
2291                                                                                 _("Searching..."));
2292                                 status = e_gw_connection_get_items_from_ids (gwb->priv->cnc, 
2293                                                                              gwb->priv->container_id, 
2294                                                                              view, ids, &gw_items);
2295                                 if (status == E_GW_CONNECTION_STATUS_INVALID_CONNECTION)
2296                                         status = e_gw_connection_get_items_from_ids (gwb->priv->cnc, 
2297                                                                                      gwb->priv->container_id, 
2298                                                                                      view, ids, &gw_items);
2299                                 if (enable_debug && status == E_GW_CONNECTION_STATUS_OK)
2300                                         printf ("read contacts from server \n");
2301                         }
2302                         g_ptr_array_free (ids, TRUE);
2303                 }
2304                 else {
2305                         /* no summary information found, read from server */
2306                         if (enable_debug)
2307                                 printf ("summary not found, reading the contacts from server\n");
2308                         if (!is_auto_completion) {
2309                                 if (filter) 
2310                                         e_data_book_view_notify_status_message (book_view, _("Searching..."));
2311                                 else 
2312                                         e_data_book_view_notify_status_message (book_view, _("Loading..."));
2313                         }
2314                         status = e_gw_connection_get_items (gwb->priv->cnc, 
2315                                                             gwb->priv->container_id, 
2316                                                             view, filter, &gw_items);
2317                         if (status == E_GW_CONNECTION_STATUS_INVALID_CONNECTION)
2318                                 status = e_gw_connection_get_items (gwb->priv->cnc, 
2319                                                                     gwb->priv->container_id, 
2320                                                                     view, filter, &gw_items);
2321                 }
2322
2323                 if (status != E_GW_CONNECTION_STATUS_OK) {
2324                         e_data_book_view_notify_complete (book_view, GNOME_Evolution_Addressbook_OtherError);
2325                         bonobo_object_unref (book_view);
2326                         return NULL;
2327                 }
2328
2329                 temp_list = gw_items;
2330                 for (; gw_items != NULL; gw_items = g_list_next(gw_items)) { 
2331
2332                         if (!e_flag_is_set (closure->running)) {
2333                                 for (;gw_items != NULL; gw_items = g_list_next (gw_items))
2334                                         g_object_unref (gw_items->data);
2335                                 break;
2336                         }
2337
2338                         count ++;
2339                         contact = e_contact_new ();
2340                         fill_contact_from_gw_item (contact, 
2341                                                    E_GW_ITEM (gw_items->data), 
2342                                                    gwb->priv->categories_by_id);
2343                         e_contact_set (contact, E_CONTACT_BOOK_URI, gwb->priv->original_uri);
2344                         if (e_contact_get_const (contact, E_CONTACT_UID)) 
2345                                 e_data_book_view_notify_update (book_view, contact);
2346                         else 
2347                                 g_critical ("Id missing for item %s\n", (char *)e_contact_get_const (contact, E_CONTACT_FILE_AS));
2348                         g_object_unref(contact);
2349                         g_object_unref (gw_items->data);
2350                 }
2351                 if (temp_list)
2352                         g_list_free (temp_list);
2353                 if (e_flag_is_set (closure->running))
2354                         e_data_book_view_notify_complete (book_view, GNOME_Evolution_Addressbook_Success);
2355                 if (filter)
2356                         g_object_unref (filter);
2357                 bonobo_object_unref (book_view);
2358
2359                 if (enable_debug) {
2360                         g_get_current_time(&end);
2361                         diff = end.tv_sec * 1000 + end.tv_usec/1000;
2362                         diff -= start.tv_sec * 1000 + start.tv_usec/1000;
2363                         printf("reading %d contacts from server took %ld.%03ld seconds\n",
2364                                 count, diff/1000,diff%1000);
2365                 }
2366
2367                 return NULL;
2368         default :
2369                 break;
2370         }
2371         return NULL;
2372 }
2373
2374 static void
2375 e_book_backend_groupwise_start_book_view (EBookBackend  *backend,
2376                                      EDataBookView *book_view)
2377 {
2378         GroupwiseBackendSearchClosure *closure = init_closure (book_view, E_BOOK_BACKEND_GROUPWISE (backend));
2379
2380         if (enable_debug)
2381                 printf ("\ne_book_backend_groupwise_start_book_view...\n");
2382         closure->thread = g_thread_create (book_view_thread, book_view, FALSE, NULL);
2383         e_flag_wait (closure->running);
2384         
2385         /* at this point we know the book view thread is actually running */
2386 }
2387   
2388 static void
2389 e_book_backend_groupwise_stop_book_view (EBookBackend  *backend,
2390                                          EDataBookView *book_view)
2391 {
2392         GroupwiseBackendSearchClosure *closure = get_closure (book_view);
2393         
2394         if (enable_debug)
2395                 printf ("\ne_book_backend_groupwise_stop_book_view...\n");
2396         e_flag_clear (closure->running);
2397 }
2398
2399 static void
2400 e_book_backend_groupwise_get_changes (EBookBackend *backend,
2401                                       EDataBook    *book,
2402                                       guint32       opid,
2403                                       const char *change_id  )
2404 {
2405         if (enable_debug)
2406                 printf ("\ne_book_backend_groupwise_get_changes...\n");
2407
2408         /* FIXME : provide implmentation */
2409        
2410 }
2411
2412 static void
2413 book_view_notify_status (EDataBookView *view, const char *status)
2414 {
2415         if (!view)
2416                 return;
2417         e_data_book_view_notify_status_message (view, status);
2418 }
2419
2420 static EDataBookView *
2421 find_book_view (EBookBackendGroupwise *ebgw)
2422 {
2423         EList *views = e_book_backend_get_book_views (E_BOOK_BACKEND (ebgw));
2424         EIterator *iter;
2425         EDataBookView *rv = NULL;
2426
2427         if (!views)
2428                 return NULL;
2429
2430         iter = e_list_get_iterator (views);
2431
2432         if (!iter) {
2433                 g_object_unref (views);
2434                 return NULL;
2435         }
2436
2437         if (e_iterator_is_valid (iter)) {
2438                 /* just always use the first book view */
2439                 EDataBookView *v = (EDataBookView*)e_iterator_get(iter);
2440                 if (v)
2441                         rv = v;
2442         }
2443
2444         g_object_unref (iter);
2445         g_object_unref (views);
2446         
2447         return rv;
2448 }
2449
2450 static void 
2451 get_sequence_from_cache (DB *db,
2452                 gdouble *cache_first_sequence,
2453                 gdouble *cache_last_sequence,
2454                 gdouble *cache_last_po_rebuild_time)
2455 {
2456         DBT uid_dbt, vcard_dbt;
2457         int db_error;
2458
2459         string_to_dbt ("firstSequence", &uid_dbt);
2460         memset (&vcard_dbt, 0, sizeof(vcard_dbt));
2461         vcard_dbt.flags = DB_DBT_MALLOC;
2462
2463         db_error = db->get (db, NULL, &uid_dbt, &vcard_dbt, 0);
2464         if (db_error != 0) {
2465                 g_warning ("db->get failed with %d", db_error);
2466         }
2467         else {
2468                 *cache_first_sequence = strtod (g_strdup (vcard_dbt.data), NULL);
2469                 g_free (vcard_dbt.data);
2470         }
2471         
2472         string_to_dbt ("lastSequence", &uid_dbt);
2473         memset (&vcard_dbt, 0, sizeof(vcard_dbt));
2474         vcard_dbt.flags = DB_DBT_MALLOC;
2475
2476         db_error = db->get (db, NULL, &uid_dbt, &vcard_dbt, 0);
2477         if (db_error != 0) {
2478                 g_warning ("db->get failed with %d", db_error);
2479         }
2480         else {
2481                 *cache_last_sequence = strtod (g_strdup (vcard_dbt.data), NULL);
2482                 g_free (vcard_dbt.data);
2483         }
2484
2485         string_to_dbt ("lastTimePORebuild", &uid_dbt);
2486         memset (&vcard_dbt, 0, sizeof(vcard_dbt));
2487         vcard_dbt.flags = DB_DBT_MALLOC;
2488
2489         db_error = db->get (db, NULL, &uid_dbt, &vcard_dbt, 0);
2490         if (db_error != 0) {
2491                 g_warning ("db->get failed with %d", db_error);
2492         }
2493         else {
2494                 *cache_last_po_rebuild_time = strtod (g_strdup (vcard_dbt.data), NULL);
2495                 g_free (vcard_dbt.data);
2496         }
2497
2498         if (enable_debug) {
2499                 printf("Read sequences from cache\n");
2500                 printf("firstSequence:%lf, lastSequence:%lf, lastPoRebuildTime:%lf\n", *cache_first_sequence, *cache_last_sequence, *cache_last_po_rebuild_time);
2501         }
2502                 
2503 }
2504 static void
2505 add_sequence_to_cache (DB *db, 
2506                        gdouble first_sequence,
2507                        gdouble last_sequence,
2508                        gdouble last_po_rebuild_time)
2509 {
2510                 gchar *tmp;
2511                 DBT   uid_dbt, vcard_dbt;
2512                 int db_error;
2513
2514                 if (enable_debug) {
2515                         printf("Adding sequences to cache\n");
2516                         printf("firstSequence:%lf, lastSequence:%lf, lastPoRebuildTime:%lf\n", first_sequence, last_sequence, last_po_rebuild_time);
2517                 }
2518
2519                 string_to_dbt ("firstSequence",&uid_dbt );
2520                 tmp = g_strdup_printf("%lf", first_sequence);
2521                 string_to_dbt (tmp, &vcard_dbt);
2522
2523                 db_error = db->put (db, NULL, &uid_dbt, &vcard_dbt, 0);
2524
2525                 g_free (tmp);
2526
2527                 if (db_error != 0) {
2528                         g_warning ("db->put failed with %d", db_error);
2529                 }
2530
2531                 string_to_dbt ("lastSequence",&uid_dbt );
2532                 tmp = g_strdup_printf("%lf", last_sequence);
2533                 string_to_dbt (tmp, &vcard_dbt);
2534
2535                 db_error = db->put (db, NULL, &uid_dbt, &vcard_dbt, 0);
2536
2537                 g_free (tmp);
2538
2539                 if (db_error != 0) {
2540                         g_warning ("db->put failed with %d", db_error);
2541                 }
2542                 
2543                 string_to_dbt ("lastTimePORebuild",&uid_dbt );
2544                 tmp = g_strdup_printf("%lf", last_po_rebuild_time);
2545                 string_to_dbt (tmp, &vcard_dbt);
2546
2547                 db_error = db->put (db, NULL, &uid_dbt, &vcard_dbt, 0);
2548
2549                 g_free (tmp);
2550
2551                 if (db_error != 0) {
2552                         g_warning ("db->put failed with %d", db_error);
2553                 }
2554 }
2555
2556 #define CURSOR_ITEM_LIMIT 100
2557 /*
2558 static gpointer
2559 build_cache (EBookBackendGroupwise *ebgw)
2560 {
2561         int status, contact_num = 0;
2562         GList *gw_items = NULL;
2563         EContact *contact;
2564         EDataBookView *book_view;
2565         EBookBackendGroupwisePrivate *priv = ebgw->priv;
2566         char *status_msg;
2567         
2568         status = e_gw_connection_get_items (ebgw->priv->cnc, ebgw->priv->container_id, "name email default members", NULL, &gw_items);
2569         if (status != E_GW_CONNECTION_STATUS_OK) 
2570                 return FALSE;
2571         
2572
2573         for (; gw_items != NULL; gw_items = g_list_next(gw_items)) { 
2574                 contact_num++;
2575                 contact = e_contact_new ();
2576                 fill_contact_from_gw_item (contact, E_GW_ITEM (gw_items->data), ebgw->priv->categories_by_id);
2577                 e_book_backend_cache_add_contact (ebgw->priv->cache, contact);
2578                 if (book_view) {
2579                         status_msg = g_strdup_printf (_("Downloading contacts (%d)... "),
2580                                                          contact_num);
2581                         book_view_notify_status (book_view, status_msg);
2582                         g_free (status_msg);
2583                 }
2584                 g_object_unref(contact);
2585                 g_object_unref (gw_items->data);
2586                         
2587         }
2588                 
2589         e_book_backend_cache_set_populated (priv->cache);
2590         priv->is_cache_ready=TRUE;
2591         
2592         g_list_free (gw_items);
2593         
2594         return NULL;
2595 }*/
2596
2597
2598 /*FIXME using cursors for address book seems to be crashing server 
2599 till it gets fixed we will use get items. cursor implementation is below */
2600
2601 static gpointer
2602 build_cache (EBookBackendGroupwise *ebgw)
2603 {
2604         int status;
2605         GList *gw_items = NULL, *l;
2606         EContact *contact;
2607         int cursor, contact_num = 0;
2608         gboolean done = FALSE;
2609         EBookBackendGroupwisePrivate *priv = ebgw->priv;
2610         const char *position = E_GW_CURSOR_POSITION_START;
2611         EDataBookView *book_view;
2612         GroupwiseBackendSearchClosure *closure;
2613         char *status_msg;
2614         GTimeVal start, end;
2615         GTimeVal tstart, tend;
2616         unsigned long diff;
2617
2618         if(!ebgw)
2619                 return FALSE;
2620
2621         if (enable_debug) {
2622                 g_get_current_time(&start);
2623                 printf("Building the cache for %s \n", ebgw->priv->book_name);
2624         }
2625
2626         status = e_gw_connection_create_cursor (priv->cnc, priv->container_id, 
2627                                                 "name email default members", NULL, &cursor);
2628         if (status != E_GW_CONNECTION_STATUS_OK) {
2629                 if (enable_debug)
2630                         printf("No connection with the server \n");
2631                 return FALSE;
2632         }
2633
2634         book_view = find_book_view (ebgw);
2635         if (book_view) {
2636                 closure = get_closure (book_view);
2637                 bonobo_object_ref (book_view);
2638                 if (closure)
2639                         e_flag_set (closure->running);
2640         }
2641
2642         while (!done) {
2643
2644                 if (enable_debug) 
2645                         g_get_current_time(&tstart);
2646                 status = e_gw_connection_read_cursor (priv->cnc, priv->container_id, 
2647                                                       cursor, TRUE, CURSOR_ITEM_LIMIT, 
2648                                                       position, &gw_items);
2649                 if (enable_debug) {
2650                         g_get_current_time(&tend);
2651                         diff = tend.tv_sec * 1000 + tend.tv_usec/1000;
2652                         diff -= tstart.tv_sec * 1000 + tstart.tv_usec/1000;
2653                         printf("e_gw_connection_read_cursor took %ld.%03ld seconds for %d contacts\n", diff / 1000, diff % 1000, CURSOR_ITEM_LIMIT);
2654                 }
2655
2656                 for (l = gw_items; l != NULL; l = g_list_next (l)) { 
2657                         contact_num++;
2658
2659                         contact = e_contact_new ();
2660                         fill_contact_from_gw_item (contact, E_GW_ITEM (l->data), 
2661                                                    ebgw->priv->categories_by_id);
2662                         e_contact_set (contact, E_CONTACT_BOOK_URI, priv->original_uri);
2663                         e_book_backend_db_cache_add_contact (ebgw->priv->file_db, contact);
2664                         e_book_backend_summary_add_contact (ebgw->priv->summary, contact);
2665
2666                         /* Since we get contacts incrementally, 100 at a time, we can not
2667                          * calculate the percentage of cache update.
2668                          * Also we should be using "percent" in notify_progress() instead of
2669                          * forming the message like this.
2670                          */
2671                         if (book_view) {
2672                                 status_msg = g_strdup_printf (_("Downloading contacts (%d)... "),
2673                                                                  contact_num);
2674                                 book_view_notify_status (book_view, status_msg);
2675                                 g_free (status_msg);
2676                         }
2677
2678                         g_object_unref(contact);
2679                         g_object_unref (l->data);
2680                         
2681                 }
2682                 if (!gw_items) {
2683                         e_book_backend_db_cache_set_populated (ebgw->priv->file_db);
2684                         done = TRUE;
2685                         priv->is_cache_ready=TRUE;
2686                         priv->is_summary_ready = TRUE;
2687                 }
2688                 
2689                 g_list_free (gw_items);
2690                 gw_items = NULL;
2691                 position = E_GW_CURSOR_POSITION_CURRENT;
2692         }
2693
2694         ebgw->priv->file_db->sync(ebgw->priv->file_db, 0);
2695
2696         if (book_view) {
2697                 e_data_book_view_notify_complete (book_view,
2698                                                   GNOME_Evolution_Addressbook_Success);
2699                 bonobo_object_unref (book_view);
2700         }
2701
2702         e_gw_connection_destroy_cursor (priv->cnc, priv->container_id, cursor);
2703
2704         if (enable_debug) {
2705                 g_get_current_time(&end);
2706                 diff = end.tv_sec * 1000 + end.tv_usec/1000;
2707                 diff -= start.tv_sec * 1000 + start.tv_usec/1000;
2708                 printf("completed building cache for %s in %ld.%03ld seconds for %d contacts\n", 
2709                         priv->book_name, diff / 1000, diff % 1000, contact_num);
2710         }
2711         return NULL;
2712 }
2713
2714 static void
2715 build_summary (EBookBackendGroupwise *ebgw)
2716 {
2717         gchar *query_string;
2718         GList *contacts, *temp_list = NULL;
2719         GTimeVal start, end;
2720         unsigned long diff;
2721
2722         if (enable_debug) {
2723                 g_get_current_time(&start);
2724                 printf ("summary file not found or not up-to-date, building summary for %s\n", 
2725                         ebgw->priv->book_name);
2726         }
2727
2728         /* build summary from cache */
2729         query_string = g_strdup_printf ("(or (beginswith \"file_as\" \"\") "
2730                                         "    (beginswith \"full_name\" \"\") "
2731                                         "    (beginswith \"email\" \"\") "
2732                                         "    (beginswith \"nickname\" \"\"))");
2733         contacts = e_book_backend_db_cache_get_contacts (ebgw->priv->file_db, query_string);
2734         g_free (query_string);
2735         temp_list = contacts;
2736         for (; contacts != NULL; contacts = g_list_next(contacts)) {
2737                 e_book_backend_summary_add_contact (ebgw->priv->summary, contacts->data);
2738                 g_object_unref (contacts->data);
2739         }
2740         if (temp_list)
2741                 g_list_free (temp_list);
2742         ebgw->priv->is_summary_ready = TRUE;
2743         
2744         if (enable_debug) {
2745                 g_get_current_time(&end);
2746                 diff = end.tv_sec * 1000 + end.tv_usec/1000;
2747                 diff -= start.tv_sec * 1000 + start.tv_usec/1000;
2748                 printf("building summary for %s took %ld.%03ld seconds \n", 
2749                         ebgw->priv->book_name, diff / 1000, diff % 1000);
2750         }
2751 }
2752
2753 static gboolean
2754 update_cache (EBookBackendGroupwise *ebgw)
2755 {
2756         int status, contact_num = 0;
2757         GList *gw_items = NULL;
2758         EContact *contact;
2759         EGwFilter *filter;
2760         time_t mod_time;
2761         char cache_time_string[25], *status_msg;
2762         const struct tm *tm;
2763         struct stat buf;
2764         char *cache_file_name;
2765         EDataBookView *book_view;
2766         GroupwiseBackendSearchClosure *closure;
2767         GTimeVal start, end;
2768         unsigned long diff;
2769
2770         if (!ebgw)
2771                 return FALSE;
2772         
2773         g_mutex_lock (ebgw->priv->update_cache_mutex);
2774
2775         if (enable_debug) {
2776                 g_get_current_time(&start);
2777                 printf("updating cache for %s\n", ebgw->priv->book_name);
2778         }
2779
2780         book_view = find_book_view (ebgw);
2781         if (book_view) {
2782                 closure = get_closure (book_view);
2783                 bonobo_object_ref (book_view);
2784                 if (closure)
2785                         e_flag_set (closure->running);
2786         }
2787
2788         cache_file_name = e_book_backend_db_cache_get_filename(ebgw->priv->file_db);
2789         g_stat (cache_file_name, &buf);
2790         g_free (cache_file_name);
2791         mod_time = buf.st_mtime;
2792         tm = gmtime (&mod_time);
2793         strftime (cache_time_string, 100, "%Y-%m-%dT%H:%M:%SZ", tm);
2794
2795         if (e_book_backend_summary_load (ebgw->priv->summary) == FALSE || 
2796             e_book_backend_summary_is_up_to_date (ebgw->priv->summary, mod_time) == FALSE) {
2797                 /* build summary */
2798                  build_summary (ebgw);
2799         }
2800         
2801         filter = e_gw_filter_new ();
2802         e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_GREATERTHAN, 
2803                                           "modified", cache_time_string);
2804         status = e_gw_connection_get_items (ebgw->priv->cnc, ebgw->priv->container_id, 
2805                                             "name email default members", filter, &gw_items);
2806         if (status != E_GW_CONNECTION_STATUS_OK) {
2807                 if (book_view)
2808                         bonobo_object_unref (book_view);
2809                 if (enable_debug)
2810                         printf("No connection with the server \n");
2811                 g_mutex_unlock (ebgw->priv->update_cache_mutex);
2812                 return FALSE;
2813         }
2814         
2815         for (; gw_items != NULL; gw_items = g_list_next(gw_items)) { 
2816                 const char *id;
2817
2818                 contact = e_contact_new ();
2819                 fill_contact_from_gw_item (contact, E_GW_ITEM (gw_items->data), 
2820                                            ebgw->priv->categories_by_id);
2821                 
2822                 e_contact_set (contact, E_CONTACT_BOOK_URI, ebgw->priv->original_uri);
2823                 id =  e_contact_get_const (contact, E_CONTACT_UID);
2824
2825                 contact_num++;
2826                 if (book_view) {
2827                         status_msg = g_strdup_printf (_("Updating contacts cache (%d)... "),
2828                                                          contact_num);
2829                         book_view_notify_status (book_view, status_msg);
2830                         g_free (status_msg);
2831                 }
2832
2833                 if (e_book_backend_db_cache_check_contact (ebgw->priv->file_db, id)) {
2834                         e_book_backend_db_cache_add_contact (ebgw->priv->file_db, contact);
2835                         e_book_backend_summary_remove_contact (ebgw->priv->summary, id);
2836                         e_book_backend_summary_add_contact (ebgw->priv->summary, contact);
2837                 } else {
2838                         e_book_backend_db_cache_add_contact (ebgw->priv->file_db, contact);
2839                         e_book_backend_summary_add_contact (ebgw->priv->summary, contact);
2840                 }
2841                 
2842                 g_object_unref(contact);
2843                 g_object_unref (gw_items->data);
2844         }
2845         
2846         ebgw->priv->is_cache_ready = TRUE;
2847         ebgw->priv->is_summary_ready = TRUE;
2848         
2849         ebgw->priv->file_db->sync(ebgw->priv->file_db, 0);
2850
2851         if (book_view) {
2852                 e_data_book_view_notify_complete (book_view,
2853                                                   GNOME_Evolution_Addressbook_Success);
2854                 bonobo_object_unref (book_view);
2855         }
2856         g_object_unref (filter);
2857         g_list_free (gw_items);
2858
2859         if (enable_debug) {
2860                 g_get_current_time(&end);
2861                 diff = end.tv_sec * 1000 + end.tv_usec/1000;
2862                 diff -= start.tv_sec * 1000 + start.tv_usec/1000;
2863                 printf("updating the cache for %s complated in %ld.%03ld seconds for %d contacts\n", 
2864                         ebgw->priv->book_name, diff / 1000, diff % 1000, contact_num);
2865         }
2866         g_mutex_unlock (ebgw->priv->update_cache_mutex);
2867         return FALSE;
2868 }
2869
2870 static gboolean
2871 update_address_book_deltas (EBookBackendGroupwise *ebgw)
2872 {
2873         int status, contact_num = 0;
2874         gdouble server_first_sequence = -1, server_last_sequence = -1, server_last_po_rebuild_time = -1;
2875         gdouble cache_first_sequence = -1, cache_last_sequence = -1, cache_last_po_rebuild_time = -1;
2876         char *count, *sequence, *status_msg;
2877         gboolean sync_required = FALSE;
2878         GList *add_list = NULL, *delete_list = NULL;
2879         EContact *contact;
2880         EDataBookView *book_view;
2881         GroupwiseBackendSearchClosure *closure;
2882         EGwItem *item;
2883         EBookBackendGroupwisePrivate *priv;
2884
2885         GTimeVal start, end;
2886         unsigned long diff;
2887         char *cache_file_name;
2888         struct stat buf;
2889         time_t mod_time;
2890
2891
2892         if (!ebgw)
2893                 return FALSE;
2894
2895         priv = ebgw->priv;
2896
2897         g_mutex_lock (priv->update_mutex);
2898         
2899         if (enable_debug)
2900                 printf("\nupdating GroupWise system address book cache \n");
2901                 
2902         /* builds or updates the cache for system address book */       
2903         status = e_gw_connection_get_items_delta_info (priv->cnc,
2904                                                        ebgw->priv->container_id, 
2905                                                        &server_first_sequence,
2906                                                        &server_last_sequence, 
2907                                                        &server_last_po_rebuild_time);
2908         if (status != E_GW_CONNECTION_STATUS_OK) {
2909                 if (enable_debug)
2910                         printf("No connection with the server \n");
2911                 g_mutex_unlock (priv->update_mutex);
2912                 return FALSE;
2913         }
2914
2915         /* Check whether the sequence has been reset or not */
2916         if (server_first_sequence <= 0 || server_last_sequence <= 0) {
2917                 /* build the cache */
2918                 if (enable_debug)
2919                         printf ("sequence is reset, rebuilding cache...\n");
2920                 build_cache (ebgw);
2921                 add_sequence_to_cache (priv->file_db, server_first_sequence, 
2922                                        server_last_sequence, server_last_po_rebuild_time);
2923                 ebgw->priv->file_db->sync (ebgw->priv->file_db, 0);
2924                 g_mutex_unlock (priv->update_mutex);
2925                 return TRUE;
2926         }
2927
2928         /* Read the last sequence and last poa rebuild time from cache */
2929         get_sequence_from_cache(priv->file_db, &cache_first_sequence, &cache_last_sequence, &cache_last_po_rebuild_time);
2930
2931         /* check whether the all the sequences are available and also whether the PO is rebuilt */
2932         if (server_first_sequence > cache_last_sequence || cache_last_sequence == -1 || 
2933             server_last_po_rebuild_time != cache_last_po_rebuild_time) {
2934                 /* build the cache again and update the cache with the sequence information */
2935                 if (enable_debug)
2936                         printf ("either the sequences missing or PO is rebuilt...rebuilding the cache\n");
2937                 build_cache (ebgw);
2938                 add_sequence_to_cache (priv->file_db, server_first_sequence, 
2939                                        server_last_sequence, server_last_po_rebuild_time);
2940                 ebgw->priv->file_db->sync (ebgw->priv->file_db, 0);
2941                 g_mutex_unlock (priv->update_mutex);
2942                 return TRUE;
2943         }
2944
2945         if (enable_debug)
2946                 g_get_current_time(&start);
2947
2948         book_view = find_book_view (ebgw);
2949         if (book_view) {
2950                 closure = get_closure (book_view);
2951                 bonobo_object_ref (book_view);
2952                 if (closure)
2953                         e_flag_set (closure->running);
2954         }
2955
2956         /* update the cache */
2957         sequence = g_strdup_printf ("%lf", cache_last_sequence +1);
2958         count = g_strdup_printf ("%d", CURSOR_ITEM_LIMIT);
2959
2960         /* load summary file */
2961         cache_file_name = e_book_backend_db_cache_get_filename(ebgw->priv->file_db);
2962         g_stat (cache_file_name, &buf);
2963         g_free (cache_file_name);
2964         mod_time = buf.st_mtime;
2965         if (e_book_backend_summary_load (ebgw->priv->summary) == FALSE || 
2966             e_book_backend_summary_is_up_to_date (ebgw->priv->summary, mod_time) == FALSE) {
2967                 /* build summary */
2968                  build_summary (ebgw);
2969         }
2970
2971         if (cache_last_sequence != server_last_sequence) {
2972
2973                         if (enable_debug) {
2974                                 printf("cache_last_sequence:%lf, server_last_sequence:%lf\n", cache_last_sequence, server_last_sequence);
2975                                 printf("Calling get_items_delta\n");
2976                         }
2977                         e_gw_connection_get_items_delta (priv->cnc, 
2978                                                          ebgw->priv->container_id, 
2979                                                          "name email sync", count, 
2980                                                          sequence, 
2981                                                          &add_list, &delete_list);
2982
2983                         if (add_list == NULL && delete_list == NULL) {
2984                                 if (enable_debug)
2985                                         printf("sequence differs but no changes found !!!\n");
2986                                 add_sequence_to_cache (priv->file_db, server_first_sequence, 
2987                                        server_last_sequence, server_last_po_rebuild_time);
2988                                 g_mutex_unlock (priv->update_mutex);
2989                                 g_free (sequence);
2990                                 g_free (count);
2991                                 return TRUE;
2992                         }
2993                         sync_required = TRUE;
2994                         if (enable_debug) {
2995                                 printf("add_list size:%d\n", g_list_length(add_list));
2996                                 printf("delete_list size:%d\n", g_list_length(delete_list));
2997                         }
2998
2999                         for (; delete_list != NULL; delete_list = g_list_next(delete_list)) { 
3000                                 const char *id;
3001
3002                                 /* deleted from the server */
3003                                 contact = e_contact_new ();
3004                                 fill_contact_from_gw_item (contact, 
3005                                                            E_GW_ITEM (delete_list->data), 
3006                                                            ebgw->priv->categories_by_id);
3007                                 if (enable_debug)
3008                                         printf("contact email:%s, contact name:%s\n", (char *) e_contact_get(contact, E_CONTACT_EMAIL_1), (char *) e_contact_get(contact, E_CONTACT_GIVEN_NAME));
3009                                 e_contact_set (contact, 
3010                                                E_CONTACT_BOOK_URI, 
3011                                                priv->original_uri);
3012                                 id =  e_contact_get_const (contact, E_CONTACT_UID);
3013
3014                                 if (e_book_backend_db_cache_check_contact (ebgw->priv->file_db, id)) {
3015                                         contact_num++;
3016
3017                                         if (book_view) {
3018                                                 status_msg = g_strdup_printf (_("Updating contacts cache (%d)... "),
3019                                                                                  contact_num);
3020                                                 book_view_notify_status (book_view, status_msg);
3021                                                 g_free (status_msg);
3022                                         }
3023                                         e_book_backend_db_cache_remove_contact (ebgw->priv->file_db, id);
3024                                         e_book_backend_summary_remove_contact (ebgw->priv->summary, id);
3025                                 }
3026                                 g_object_unref(contact);
3027                                 g_object_unref (delete_list->data);
3028                         }
3029
3030                         for (; add_list != NULL; add_list = g_list_next(add_list)) { 
3031                                 const char *id;
3032
3033                                 /* newly added to server */
3034                                 contact = e_contact_new ();
3035                                 fill_contact_from_gw_item (contact, 
3036                                                            E_GW_ITEM (add_list->data), 
3037                                                            ebgw->priv->categories_by_id);
3038
3039                                 /* When a distribution list is modified the server sends me a delete and add response.
3040                                 But it doesnt send me the members, so i have to explicitly request the server for the members                                of the distribution list */
3041
3042                                 if (e_contact_get (contact, E_CONTACT_IS_LIST)) {
3043                                         if(enable_debug)
3044                                                 printf ("Contact List modified fetching the members of the contact list\n");
3045                                                         
3046                                         status = e_gw_connection_get_item (ebgw->priv->cnc, ebgw->priv->container_id, e_contact_get (contact, E_CONTACT_UID), "name email default members", &item);
3047                                         g_object_unref (contact);
3048                                         contact = e_contact_new ();
3049                                         fill_contact_from_gw_item (contact, item, ebgw->priv->categories_by_id);
3050                                         g_object_unref (item);
3051                                 }
3052
3053                                 if (enable_debug)
3054                                         printf("contact email:%s, contact name:%s\n", (char *)e_contact_get(contact, E_CONTACT_EMAIL_1),(char *) e_contact_get(contact, E_CONTACT_GIVEN_NAME));
3055                                 e_contact_set (contact, 
3056                                                E_CONTACT_BOOK_URI, 
3057                                                priv->original_uri);
3058                                 id =  e_contact_get_const (contact, E_CONTACT_UID);
3059
3060                                 contact_num++;
3061                                 if (book_view) {
3062                                         status_msg = g_strdup_printf (_("Updating contacts cache (%d)... "),
3063                                                                          contact_num);
3064                                         book_view_notify_status (book_view, status_msg);
3065                                         g_free (status_msg);
3066                                 }
3067                                 if (e_book_backend_db_cache_check_contact (ebgw->priv->file_db, id)) {
3068                                         if (enable_debug)
3069                                                 printf("contact already there\n");
3070                                         e_book_backend_summary_remove_contact (ebgw->priv->summary, id);
3071                                         e_book_backend_db_cache_add_contact (ebgw->priv->file_db, contact);
3072                                         e_book_backend_summary_add_contact (ebgw->priv->summary, contact);
3073                                 } else {
3074                                         if (enable_debug)
3075                                                 printf("contact not there\n");
3076                                         e_book_backend_db_cache_add_contact (ebgw->priv->file_db, contact);
3077                                         e_book_backend_summary_add_contact (ebgw->priv->summary, contact);
3078                                 }
3079
3080                                 g_object_unref(contact);
3081                                 g_object_unref (add_list->data);
3082                         }
3083                         cache_last_sequence += contact_num;
3084
3085                 /* cache is updated, now adding the sequence information to the cache */
3086
3087                 add_sequence_to_cache (priv->file_db, server_first_sequence, 
3088                                        server_last_sequence, server_last_po_rebuild_time);
3089
3090                 g_list_free (add_list);
3091                 g_list_free (delete_list);
3092         }
3093         
3094         g_free (sequence);
3095         g_free (count);
3096         ebgw->priv->is_cache_ready = TRUE;
3097         ebgw->priv->is_summary_ready = TRUE;
3098
3099         if (sync_required)
3100                 ebgw->priv->file_db->sync(ebgw->priv->file_db, 0);
3101
3102
3103         if (book_view) {
3104                 e_data_book_view_notify_complete (book_view,
3105                                                   GNOME_Evolution_Addressbook_Success);
3106                 bonobo_object_unref (book_view);
3107         }
3108
3109         if (enable_debug) {
3110                 g_get_current_time(&end);
3111                 diff = end.tv_sec * 1000 + end.tv_usec/1000;
3112                 diff -= start.tv_sec * 1000 + start.tv_usec/1000;
3113                 printf("updating GroupWise system address book cache took %ld.%03ld seconds for %d changes\n", 
3114                         diff / 1000, diff % 1000, contact_num);
3115         }
3116         g_mutex_unlock(priv->update_mutex);
3117
3118         return TRUE;
3119 }
3120
3121 static gboolean
3122 update_address_book_cache (gpointer ebgw)
3123 {
3124         GThread *thread;
3125         GError *error = NULL;
3126
3127         if (!ebgw)
3128                 return FALSE;
3129
3130         if (enable_debug)
3131                 printf("GroupWise system addressbook cache time out, updating.. \n");
3132
3133         thread = g_thread_create ((GThreadFunc) update_address_book_deltas, ebgw, FALSE, NULL);
3134         if (!thread) {
3135                 g_warning (G_STRLOC ": %s", error->message);
3136                 g_error_free (error);
3137         }
3138
3139         return TRUE;
3140 }
3141
3142
3143 #define CACHE_REFRESH_INTERVAL 600000
3144 static void
3145 e_book_backend_groupwise_authenticate_user (EBookBackend *backend,
3146                                             EDataBook    *book,
3147                                             guint32       opid,
3148                                             const char *user,
3149                                             const char *passwd,
3150                                             const char *auth_method)
3151 {
3152         EBookBackendGroupwise *ebgw;
3153         EBookBackendGroupwisePrivate *priv;
3154         char *id, *tmpfile;
3155         int status;
3156         char *http_uri;
3157         gboolean is_writable;
3158         const char *cache_refresh_interval_set;
3159         int cache_refresh_interval = CACHE_REFRESH_INTERVAL;
3160
3161         ebgw = E_BOOK_BACKEND_GROUPWISE (backend);
3162         priv = ebgw->priv;
3163
3164         if (enable_debug) {
3165                 printf ("authenticate user ............\n");
3166                 if(priv->book_name)
3167                         printf("book_name:%s\n", priv->book_name);
3168         }
3169
3170         
3171         switch (ebgw->priv->mode) {
3172         case GNOME_Evolution_Addressbook_MODE_LOCAL:
3173                 /* load summary file for offline use */
3174                 g_mkdir_with_parents (g_path_get_dirname (priv->summary_file_name), 0700);
3175                 priv->summary = e_book_backend_summary_new (priv->summary_file_name, 
3176                                                     SUMMARY_FLUSH_TIMEOUT);
3177                 e_book_backend_summary_load (priv->summary);
3178
3179                 e_book_backend_notify_writable (backend, FALSE);
3180                 e_book_backend_notify_connection_status (backend, FALSE); 
3181                 e_data_book_respond_authenticate_user (book, opid, GNOME_Evolution_Addressbook_Success); 
3182                 return;
3183                 
3184         case GNOME_Evolution_Addressbook_MODE_REMOTE:
3185                 
3186                 if (priv->cnc) { /*we have already authenticated to server */
3187                         printf("already authenticated\n");
3188                         e_data_book_respond_authenticate_user (book, opid, GNOME_Evolution_Addressbook_Success); 
3189                         return;
3190                 }
3191                 
3192                 priv->cnc = e_gw_connection_new (priv->uri, user, passwd);
3193                 if (!E_IS_GW_CONNECTION(priv->cnc) && priv->use_ssl && g_str_equal (priv->use_ssl, "when-possible")) {
3194                         http_uri = g_strconcat ("http://", priv->uri + 8, NULL);
3195                         priv->cnc = e_gw_connection_new (http_uri, user, passwd);
3196                         g_free (http_uri);
3197                 }
3198                 if (!E_IS_GW_CONNECTION(priv->cnc)) {
3199                         e_data_book_respond_authenticate_user (book, opid, GNOME_Evolution_Addressbook_AuthenticationFailed);
3200                         return;
3201                 }
3202                 
3203                 id = NULL;
3204                 is_writable = FALSE;
3205                 status = e_gw_connection_get_address_book_id (priv->cnc,  priv->book_name, &id, &is_writable); 
3206                 if (status == E_GW_CONNECTION_STATUS_INVALID_CONNECTION)
3207                         status = e_gw_connection_get_address_book_id (priv->cnc,  priv->book_name, &id, &is_writable); 
3208                 if (status == E_GW_CONNECTION_STATUS_OK) {
3209                         if ( (id == NULL) && !priv->only_if_exists ) {
3210                                 status = e_gw_connection_create_book (priv->cnc, priv->book_name,  &id);
3211                                 is_writable = TRUE;
3212                                 if (status != E_GW_CONNECTION_STATUS_OK ) {
3213                                         e_data_book_respond_authenticate_user (book, opid, GNOME_Evolution_Addressbook_OtherError);
3214                                         return;
3215                                 }
3216                         }
3217                 }
3218                 if (id != NULL) {
3219                         priv->container_id = g_strdup (id);
3220                         g_free(id);
3221                         e_book_backend_set_is_writable (backend, is_writable);
3222                         e_book_backend_notify_writable (backend, is_writable);
3223                         e_book_backend_notify_connection_status (backend, TRUE); 
3224                         priv->is_writable = is_writable;
3225                         e_gw_connection_get_categories (priv->cnc, &priv->categories_by_id, &priv->categories_by_name);
3226                         if (!e_gw_connection_get_version(priv->cnc))  
3227                                 e_data_book_respond_authenticate_user (book, opid, GNOME_Evolution_Addressbook_InvalidServerVersion);
3228                         else
3229                                 e_data_book_respond_authenticate_user (book, opid, GNOME_Evolution_Addressbook_Success); 
3230                 } else {
3231                         e_book_backend_set_is_loaded (backend, FALSE);
3232                         e_data_book_respond_authenticate_user (book, opid, GNOME_Evolution_Addressbook_NoSuchBook);
3233                 }
3234
3235                 /* initialize summary file */
3236                 tmpfile = g_path_get_dirname (priv->summary_file_name);
3237                 g_mkdir_with_parents (tmpfile, 0700);
3238                 g_free (tmpfile);
3239                 priv->summary = e_book_backend_summary_new (priv->summary_file_name, 
3240                                                             SUMMARY_FLUSH_TIMEOUT);
3241
3242                 if (!ebgw->priv->file_db) {
3243                                 e_data_book_respond_authenticate_user (book, opid, GNOME_Evolution_Addressbook_OtherError);
3244                                 return ;
3245                 }
3246                 if (e_book_backend_db_cache_is_populated (ebgw->priv->file_db)) {
3247                         if (enable_debug)
3248                                 printf("cache is populated\n");
3249                         if (priv->is_writable){
3250                                 if (enable_debug) {
3251                                         printf("is writable\n");
3252                                         printf("creating update_cache thread\n");
3253                                 }
3254                                 g_thread_create ((GThreadFunc) update_cache, ebgw, FALSE, NULL);
3255                         }
3256                         else if (priv->marked_for_offline) {
3257                                 GThread *t;
3258                                 if (enable_debug)
3259                                         printf("marked for offline\n");
3260                                 if (enable_debug)
3261                                         printf("creating update_address_book_deltas thread\n");
3262
3263                                 t = g_thread_create ((GThreadFunc) update_address_book_deltas, ebgw, TRUE, NULL);
3264
3265                                 /* spawn a thread to update the system address book cache 
3266                                  * at given intervals
3267                                  */
3268                                 cache_refresh_interval_set = g_getenv ("BOOK_CACHE_REFRESH_INTERVAL");
3269                                 if (cache_refresh_interval_set) {
3270                                         cache_refresh_interval = g_ascii_strtod (cache_refresh_interval_set, 
3271                                                                                 NULL); /* use this */
3272                                         cache_refresh_interval *= (60*1000);
3273                                 }
3274
3275                                 /* set the cache refresh time */
3276                                 g_thread_join (t);
3277                                 if (enable_debug)
3278                                         printf ("creating cache refresh thread for GW system book \n");
3279                                 priv->cache_timeout = g_timeout_add (cache_refresh_interval, 
3280                                                                      (GSourceFunc) update_address_book_cache,
3281                                                                      (gpointer)ebgw); 
3282                         }
3283                 }
3284                 else if (priv->is_writable) {  /* for personal books we always cache */
3285                         /* Personal address book and frequent contacts */
3286                         if (enable_debug) {
3287                                 printf("else if is _writable");
3288                                 printf("build_cahe thread");
3289                         }
3290                         g_thread_create ((GThreadFunc) build_cache, ebgw, FALSE, NULL);
3291                 }
3292                 else if(priv->marked_for_offline) { 
3293                         GThread *t;
3294                         if (enable_debug)
3295                                 printf("else if marked_for_offline\n");
3296                         /* System address book */
3297                         /* cache is not populated and book is not writable and marked for offline usage */
3298                         if (enable_debug)
3299                                 printf("creating update_address_book_deltas thread\n");
3300                         t = g_thread_create ((GThreadFunc) update_address_book_deltas, ebgw, TRUE, NULL);
3301                         g_thread_join (t);
3302                         /* set the cache refresh time */
3303                         if (enable_debug)
3304                                 printf ("creating cache refresh thread for GW system book \n");
3305                         priv->cache_timeout = g_timeout_add (cache_refresh_interval, 
3306                                                              (GSourceFunc) update_address_book_cache,
3307                                                              (gpointer)ebgw); 
3308                 }
3309                 return;
3310         default :
3311                 break;
3312         }
3313 }
3314
3315 static void
3316 e_book_backend_groupwise_get_required_fields (EBookBackend *backend,
3317                                                EDataBook    *book,
3318                                                guint32       opid)
3319 {
3320         GList *fields = NULL;
3321
3322         if (enable_debug)
3323                 printf ("\ne_book_backend_groupwise_get_required_fields...\n");
3324   
3325         fields = g_list_append (fields, (char *)e_contact_field_name (E_CONTACT_FILE_AS));
3326         e_data_book_respond_get_supported_fields (book, opid,
3327                                                   GNOME_Evolution_Addressbook_Success,
3328                                                   fields);
3329         g_list_free (fields);
3330  
3331 }
3332
3333 static void
3334 e_book_backend_groupwise_get_supported_fields (EBookBackend *backend,
3335                                                EDataBook    *book,
3336                                                guint32       opid)
3337 {
3338         GList *fields = NULL;
3339         int i;
3340
3341         if (enable_debug)
3342                 printf ("\ne_book_backend_groupwise_get_supported_fields...\n");
3343   
3344         for (i = 0; i < G_N_ELEMENTS (mappings) ; i ++)
3345                 fields = g_list_append (fields, g_strdup (e_contact_field_name (mappings[i].field_id)));
3346         fields = g_list_append (fields, g_strdup (e_contact_field_name (E_CONTACT_EMAIL_2)));
3347         fields = g_list_append (fields, g_strdup (e_contact_field_name (E_CONTACT_EMAIL_3)));
3348         fields = g_list_append (fields, g_strdup (e_contact_field_name (E_CONTACT_IM_ICQ)));
3349         fields = g_list_append (fields, g_strdup (e_contact_field_name (E_CONTACT_IM_YAHOO)));
3350         fields = g_list_append (fields, g_strdup (e_contact_field_name (E_CONTACT_IM_GADUGADU)));
3351         fields = g_list_append (fields, g_strdup (e_contact_field_name (E_CONTACT_IM_MSN)));
3352         fields = g_list_append (fields, g_strdup (e_contact_field_name (E_CONTACT_IM_JABBER)));
3353         fields = g_list_append (fields, g_strdup (e_contact_field_name (E_CONTACT_IM_GROUPWISE)));
3354         fields = g_list_append (fields, g_strdup (e_contact_field_name (E_CONTACT_ADDRESS_WORK)));
3355         e_data_book_respond_get_supported_fields (book, opid,
3356                                                   GNOME_Evolution_Addressbook_Success,
3357                                                   fields);
3358         g_list_free (fields);
3359 }
3360
3361 static GNOME_Evolution_Addressbook_CallStatus
3362 e_book_backend_groupwise_cancel_operation (EBookBackend *backend, EDataBook *book)
3363 {
3364         if (enable_debug)
3365                 printf ("\ne_book_backend_groupwise_cancel_operation...\n");
3366         return GNOME_Evolution_Addressbook_CouldNotCancel;
3367 }
3368
3369 static void
3370 #if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3
3371 file_errcall (const DB_ENV *env, const char *buf1, const char *buf2)
3372 #else
3373 file_errcall (const char *buf1, char *buf2)
3374 #endif
3375 {
3376         g_warning ("libdb error: %s", buf2);
3377 }
3378
3379 static GNOME_Evolution_Addressbook_CallStatus
3380 e_book_backend_groupwise_load_source (EBookBackend           *backend,
3381                                       ESource                *source,
3382                                       gboolean                only_if_exists)
3383 {
3384         EBookBackendGroupwise *ebgw;
3385         EBookBackendGroupwisePrivate *priv;
3386         char *dirname, *filename, *tmp;
3387         char *book_name;
3388         char *uri;
3389         char **tokens;
3390         const char *port;
3391         int db_error;
3392         DB *db;
3393         DB_ENV *env;
3394         EUri *parsed_uri;
3395         int i;
3396         const char *use_ssl;
3397         const char *offline;
3398
3399
3400         if (enable_debug)
3401                 printf("\ne_book_backend_groupwise_load_source.. \n");
3402         ebgw = E_BOOK_BACKEND_GROUPWISE (backend);
3403         priv = ebgw->priv;
3404         g_object_ref (source);
3405
3406         offline = e_source_get_property (source, "offline_sync");
3407         if (offline  && g_str_equal (offline, "1"))
3408                 priv->marked_for_offline = TRUE;
3409         
3410         if (priv->mode ==  GNOME_Evolution_Addressbook_MODE_LOCAL &&  !priv->marked_for_offline ) {
3411                 return GNOME_Evolution_Addressbook_OfflineUnavailable;
3412         }
3413         
3414         uri =  e_source_get_uri (source);
3415         priv->original_uri = g_strdup (uri);
3416         if(uri == NULL)
3417                 return  GNOME_Evolution_Addressbook_OtherError;
3418
3419         tokens = g_strsplit (uri, ";", 2);
3420         g_free (uri);
3421         if (tokens[0]) 
3422                 uri = g_strdup(tokens[0]);
3423         book_name = g_strdup (tokens[1]);
3424         if(book_name == NULL)
3425                 return  GNOME_Evolution_Addressbook_OtherError;
3426         g_strfreev (tokens);
3427         parsed_uri = e_uri_new (uri);
3428         port = e_source_get_property (source, "port");
3429         if (port == NULL)
3430                 port = "7191";
3431         use_ssl = e_source_get_property (source, "use_ssl");
3432         if (use_ssl && !g_str_equal (use_ssl, "never")) 
3433                 priv->uri = g_strconcat ("https://", parsed_uri->host,":", port, "/soap", NULL );
3434         else 
3435                 priv->uri = g_strconcat ("http://", parsed_uri->host,":", port, "/soap", NULL );
3436         priv->use_ssl = g_strdup (use_ssl);
3437         priv->only_if_exists = only_if_exists;
3438         
3439         priv->book_name = book_name;
3440         e_book_backend_set_is_loaded (E_BOOK_BACKEND (backend), TRUE);
3441         e_book_backend_set_is_writable (E_BOOK_BACKEND(backend), FALSE);  
3442         if (priv->mode == GNOME_Evolution_Addressbook_MODE_LOCAL) {
3443                 e_book_backend_notify_writable (backend, FALSE);
3444                 e_book_backend_notify_connection_status (backend, FALSE); 
3445         }
3446         else {
3447                 e_book_backend_notify_connection_status (backend, TRUE);
3448         }
3449         
3450         for (i = 0; i < strlen (uri); i++) {
3451                 switch (uri[i]) {
3452                 case ':' :
3453                 case '/' :
3454                         uri[i] = '_';
3455                 }
3456         }
3457
3458         if (priv->mode == GNOME_Evolution_Addressbook_MODE_LOCAL) 
3459                 if (!e_book_backend_db_cache_exists (priv->original_uri)) {
3460                         g_free (uri);
3461                         e_uri_free (parsed_uri);
3462                         return GNOME_Evolution_Addressbook_OfflineUnavailable;
3463         }
3464
3465         g_free (priv->summary_file_name);
3466         tmp = g_build_filename (g_get_home_dir(), ".evolution/addressbook" , uri, priv->book_name, NULL);
3467         priv->summary_file_name = g_strconcat (tmp, ".summary", NULL);
3468         g_free (tmp);
3469
3470         dirname = g_build_filename (g_get_home_dir(), ".evolution/cache/addressbook", uri, priv->book_name, NULL);
3471         filename = g_build_filename (dirname, "cache.db", NULL);
3472
3473         db_error = e_db3_utils_maybe_recover (filename);
3474         if (db_error !=0) {
3475                 g_warning ("db recovery failed with %d", db_error);
3476                 g_free (dirname);
3477                 g_free (filename);
3478                 return GNOME_Evolution_Addressbook_OtherError;
3479         }
3480         
3481         g_static_mutex_lock(&global_env_lock);
3482         if (global_env.ref_count > 0) {
3483                 env = global_env.env;
3484                 global_env.ref_count++;
3485         } 
3486         else {
3487                 db_error = db_env_create (&env, 0);
3488                 if (db_error != 0) {
3489                         g_warning ("db_env_create failed with %d", db_error);
3490                         g_static_mutex_unlock (&global_env_lock);
3491                         g_free (dirname);
3492                         g_free (filename);
3493                         return GNOME_Evolution_Addressbook_OtherError;
3494                 }
3495
3496                 db_error = env->open (env, NULL, DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE | DB_THREAD, 0);
3497                 if (db_error != 0) {
3498                         env->close(env, 0);
3499                         g_warning ("db_env_open failed with %d", db_error);
3500                         g_static_mutex_unlock(&global_env_lock);
3501                         g_free(dirname);
3502                         g_free(filename);
3503                         return GNOME_Evolution_Addressbook_OtherError;
3504                 }
3505
3506                 env->set_errcall (env, file_errcall);
3507
3508                 global_env.env = env;
3509                 global_env.ref_count = 1;
3510         }
3511         g_static_mutex_unlock(&global_env_lock);
3512
3513         ebgw->priv->env = env;
3514
3515         db_error = db_create (&db, env, 0);
3516         if (db_error != 0) {
3517                 g_warning ("db_create failed with %d", db_error);
3518                 g_free(dirname);
3519                 g_free(filename);
3520                 return GNOME_Evolution_Addressbook_OtherError;
3521         }
3522
3523         db_error = db->open (db, NULL, filename, NULL, DB_HASH, DB_THREAD, 0666);
3524
3525         if (db_error == DB_OLD_VERSION) {
3526                 db_error = e_db3_utils_upgrade_format (filename);
3527
3528                 if (db_error != 0) {
3529                         g_warning ("db format upgrade failed with %d", db_error);
3530                         g_free(filename);
3531                         g_free(dirname);
3532                         return GNOME_Evolution_Addressbook_OtherError;
3533                 }
3534                 
3535                 db_error = db->open (db, NULL, filename, NULL, DB_HASH, DB_THREAD, 0666);
3536         }
3537
3538         ebgw->priv->file_db = db;
3539
3540         if (db_error != 0) {
3541                 int rv;
3542
3543                 /* the databade didn't exist, so we create the 
3544                    directory then the .db */
3545                 rv = g_mkdir_with_parents (dirname, 0777);
3546                 if (rv == -1 && errno != EEXIST) {
3547                         g_warning ("failed to make directory %s: %s", dirname, strerror (errno));
3548                         g_free (dirname);
3549                         g_free (filename);
3550                         if (errno == EACCES || errno == EPERM)
3551                                 return GNOME_Evolution_Addressbook_PermissionDenied;
3552                         else
3553                                 return GNOME_Evolution_Addressbook_OtherError;
3554                 }
3555
3556                 db_error = db->open (db, NULL, filename, NULL, DB_HASH, DB_CREATE | DB_THREAD, 0666);
3557                 if (db_error != 0) {
3558                         g_warning ("db->open (...DB_CREATE...) failed with %d", db_error);
3559                 }
3560
3561         }
3562
3563         ebgw->priv->file_db = db;
3564
3565         if (db_error != 0 || ebgw->priv->file_db == NULL) {
3566                 ebgw->priv->file_db = NULL;
3567                 g_free(filename);
3568                 g_free(dirname);
3569                 return GNOME_Evolution_Addressbook_OtherError;
3570         }
3571         
3572         e_book_backend_db_cache_set_filename (ebgw->priv->file_db, filename);
3573         g_free(filename);
3574         g_free(dirname);
3575         g_free (uri);
3576         e_uri_free (parsed_uri);
3577
3578         /*if (enable_debug) {
3579                 printf ("summary file name = %s\ncache file name = %s \n", 
3580                          priv->summary_file_name, e_file_cache_get_filename (E_FILE_CACHE(priv->cache)));
3581         }*/
3582
3583         return GNOME_Evolution_Addressbook_Success;
3584 }
3585
3586 static void
3587 e_book_backend_groupwise_remove (EBookBackend *backend,
3588                                  EDataBook        *book,
3589                                  guint32           opid)
3590 {
3591         EBookBackendGroupwise *ebgw;
3592         int status;
3593   
3594         if (enable_debug)
3595                 printf ("\ne_book_backend_groupwise_remove...\n");
3596         ebgw = E_BOOK_BACKEND_GROUPWISE (backend);
3597         if (ebgw->priv->cnc == NULL) {
3598                 e_data_book_respond_remove (book,  opid,  GNOME_Evolution_Addressbook_AuthenticationRequired);
3599                 return;
3600         }
3601         if (!ebgw->priv->is_writable) {
3602                 e_data_book_respond_remove (book,  opid,  GNOME_Evolution_Addressbook_PermissionDenied);
3603                 return;
3604         }
3605         status = e_gw_connection_remove_item (ebgw->priv->cnc, NULL, ebgw->priv->container_id);
3606         if (status == E_GW_CONNECTION_STATUS_OK) 
3607                 e_data_book_respond_remove (book,  opid, GNOME_Evolution_Addressbook_Success);
3608         else
3609                 e_data_book_respond_remove (book,  opid, GNOME_Evolution_Addressbook_OtherError);
3610         g_unlink (e_book_backend_db_cache_get_filename(ebgw->priv->file_db));
3611 }
3612
3613 static char *
3614 e_book_backend_groupwise_get_static_capabilities (EBookBackend *backend)
3615 {
3616         EBookBackendGroupwise *ebgw;
3617
3618         if (enable_debug)
3619                 printf ("\ne_book_backend_groupwise_get_static_capabilities...\n");
3620         
3621         ebgw = E_BOOK_BACKEND_GROUPWISE (backend);
3622
3623         /* do-initialy-query is enabled for system address book also, so that we get the
3624          * book_view, which is needed for displaying cache update progress. 
3625          * and null query is handled for system address book. 
3626          */
3627         return g_strdup ("net,bulk-removes,do-initial-query,contact-lists");
3628 }
3629
3630 static void 
3631 e_book_backend_groupwise_get_supported_auth_methods (EBookBackend *backend, EDataBook *book, guint32 opid)
3632 {
3633         GList *auth_methods = NULL;
3634         char *auth_method;
3635         
3636         if (enable_debug)
3637                 printf ("\ne_book_backend_groupwise_get_supported_auth_methods...\n");
3638         auth_method =  g_strdup_printf ("plain/password");
3639         auth_methods = g_list_append (auth_methods, auth_method);
3640         e_data_book_respond_get_supported_auth_methods (book,
3641                                                         opid,
3642                                                         GNOME_Evolution_Addressbook_Success,
3643                                                         auth_methods);  
3644         g_free (auth_method);
3645         g_list_free (auth_methods);
3646 }
3647
3648 static void 
3649 e_book_backend_groupwise_set_mode (EBookBackend *backend, int mode)
3650 {
3651         EBookBackendGroupwise *bg;
3652         
3653         if (enable_debug)
3654                 printf ("\ne_book_backend_groupwise_set_mode...\n");
3655         bg = E_BOOK_BACKEND_GROUPWISE (backend);
3656         bg->priv->mode = mode;
3657         if (e_book_backend_is_loaded (backend)) {
3658                 if (mode == GNOME_Evolution_Addressbook_MODE_LOCAL) {
3659                         e_book_backend_notify_writable (backend, FALSE);
3660                         e_book_backend_notify_connection_status (backend, FALSE);
3661                         if (bg->priv->cnc) {
3662                                 g_object_unref (bg->priv->cnc);
3663                                 bg->priv->cnc=NULL;
3664                         }
3665                 }
3666                 else if (mode == GNOME_Evolution_Addressbook_MODE_REMOTE) {
3667                         if (bg->priv->is_writable)
3668                                 e_book_backend_notify_writable (backend, TRUE);
3669                         else 
3670                                 e_book_backend_notify_writable (backend, FALSE);
3671                         e_book_backend_notify_connection_status (backend, TRUE);
3672                         e_book_backend_notify_auth_required (backend);
3673                 }
3674         }
3675 }
3676
3677 /**
3678  * e_book_backend_groupwise_new:
3679  */
3680 EBookBackend *
3681 e_book_backend_groupwise_new (void)
3682 {
3683         EBookBackendGroupwise *backend;
3684
3685         if (enable_debug)
3686                 printf ("\ne_book_backend_groupwise_new...\n");
3687                                                                                                                              
3688         backend = g_object_new (E_TYPE_BOOK_BACKEND_GROUPWISE, NULL);
3689                                                                                                        
3690         return E_BOOK_BACKEND (backend);
3691 }
3692
3693 static void
3694 e_book_backend_groupwise_dispose (GObject *object)
3695 {
3696         EBookBackendGroupwise *bgw;
3697
3698         if (enable_debug)
3699                 printf ("\ne_book_backend_groupwise_dispose...\n");
3700                                                                                                                              
3701         bgw = E_BOOK_BACKEND_GROUPWISE (object);
3702                                                                                                                              
3703         if (bgw->priv) {
3704                 if (bgw->priv->file_db)
3705                         bgw->priv->file_db->close (bgw->priv->file_db, 0);
3706
3707                 g_static_mutex_lock(&global_env_lock);
3708                 global_env.ref_count--;
3709                 if (global_env.ref_count == 0) {
3710                         global_env.env->close (global_env.env, 0);
3711                         global_env.env = NULL;
3712                 }
3713                 g_static_mutex_unlock(&global_env_lock);
3714                 if (bgw->priv->uri) {
3715                         g_free (bgw->priv->uri);
3716                         bgw->priv->uri = NULL;
3717                 }
3718
3719                 if (bgw->priv->original_uri) {
3720                         g_free (bgw->priv->original_uri);
3721                         bgw->priv->original_uri = NULL;
3722                 }
3723
3724                 if (bgw->priv->cnc) {
3725                         g_object_unref (bgw->priv->cnc);
3726                         bgw->priv->cnc = NULL;
3727                 }
3728                 if (bgw->priv->container_id) {
3729                         g_free (bgw->priv->container_id);
3730                         bgw->priv->container_id = NULL;
3731                 }
3732                 if (bgw->priv->book_name) {
3733                         g_free (bgw->priv->book_name);
3734                         bgw->priv->book_name = NULL;
3735                 }
3736                 if (bgw->priv->summary_file_name) {
3737                         g_free (bgw->priv->summary_file_name);
3738                         bgw->priv->summary_file_name = NULL;
3739                 }
3740                 if (bgw->priv->summary) {
3741                         e_book_backend_summary_save(bgw->priv->summary);
3742                         g_object_unref (bgw->priv->summary);
3743                         bgw->priv->summary = NULL;
3744                 }
3745                 if (bgw->priv->use_ssl) {
3746                         g_free (bgw->priv->use_ssl);
3747                 }
3748                 if (bgw->priv->cache_timeout) {
3749                         g_source_remove (bgw->priv->cache_timeout);
3750                         bgw->priv->cache_timeout = 0;
3751                 }
3752                 if (bgw->priv->update_mutex)
3753                         g_mutex_free (bgw->priv->update_mutex);
3754                 if (bgw->priv->update_cache_mutex)
3755                         g_mutex_free (bgw->priv->update_cache_mutex);
3756                 
3757                 g_free (bgw->priv);
3758                 bgw->priv = NULL;
3759         }
3760                                                                                                                              
3761         G_OBJECT_CLASS (e_book_backend_groupwise_parent_class)->dispose (object);
3762 }
3763                                                                                                                             
3764 static void
3765 e_book_backend_groupwise_class_init (EBookBackendGroupwiseClass *klass)
3766 {
3767   
3768
3769         GObjectClass  *object_class = G_OBJECT_CLASS (klass);
3770         EBookBackendClass *parent_class;
3771
3772
3773         e_book_backend_groupwise_parent_class = g_type_class_peek_parent (klass);
3774
3775         parent_class = E_BOOK_BACKEND_CLASS (klass);
3776
3777         /* Set the virtual methods. */
3778         parent_class->load_source             = e_book_backend_groupwise_load_source;
3779         parent_class->get_static_capabilities = e_book_backend_groupwise_get_static_capabilities;
3780
3781         parent_class->create_contact          = e_book_backend_groupwise_create_contact;
3782         parent_class->remove_contacts         = e_book_backend_groupwise_remove_contacts;
3783         parent_class->modify_contact          = e_book_backend_groupwise_modify_contact;
3784         parent_class->get_contact             = e_book_backend_groupwise_get_contact;
3785         parent_class->get_contact_list        = e_book_backend_groupwise_get_contact_list;
3786         parent_class->start_book_view         = e_book_backend_groupwise_start_book_view;
3787         parent_class->stop_book_view          = e_book_backend_groupwise_stop_book_view;
3788         parent_class->get_changes             = e_book_backend_groupwise_get_changes;
3789         parent_class->authenticate_user       = e_book_backend_groupwise_authenticate_user;
3790         parent_class->get_required_fields     = e_book_backend_groupwise_get_required_fields;
3791         parent_class->get_supported_fields    = e_book_backend_groupwise_get_supported_fields;
3792         parent_class->get_supported_auth_methods = e_book_backend_groupwise_get_supported_auth_methods;
3793         parent_class->cancel_operation        = e_book_backend_groupwise_cancel_operation;
3794         parent_class->remove                  = e_book_backend_groupwise_remove;
3795         parent_class->set_mode                = e_book_backend_groupwise_set_mode;
3796         object_class->dispose                 = e_book_backend_groupwise_dispose;
3797 }
3798
3799 static void
3800 e_book_backend_groupwise_init (EBookBackendGroupwise *backend)
3801 {
3802         EBookBackendGroupwisePrivate *priv;
3803                                                                                                                              
3804         priv= g_new0 (EBookBackendGroupwisePrivate, 1);
3805         priv->is_writable = TRUE;
3806         priv->is_cache_ready = FALSE;
3807         priv->is_summary_ready = FALSE;
3808         priv->marked_for_offline = FALSE;
3809         priv->use_ssl = NULL;
3810         priv->cnc = NULL;
3811         priv->original_uri = NULL;
3812         priv->cache_timeout = 0;
3813         priv->update_mutex = g_mutex_new();
3814         priv->update_cache_mutex = g_mutex_new();
3815         priv->reserved1 = NULL;
3816         backend->priv = priv;
3817
3818         if (g_getenv ("GROUPWISE_DEBUG")) { 
3819                 if (atoi (g_getenv ("GROUPWISE_DEBUG")) == 2)
3820                         enable_debug = TRUE;
3821                 else
3822                         enable_debug = FALSE;
3823         }
3824 }
3825
3826
3827 /**
3828  * e_book_backend_groupwise_get_type:
3829  */
3830 GType
3831 e_book_backend_groupwise_get_type (void)
3832 {
3833         static GType type = 0;
3834                                                                                                                              
3835         if (! type) {
3836                 GTypeInfo info = {
3837                         sizeof (EBookBackendGroupwiseClass),
3838                         NULL, /* base_class_init */
3839                         NULL, /* base_class_finalize */
3840                         (GClassInitFunc)  e_book_backend_groupwise_class_init,
3841                         NULL, /* class_finalize */
3842                         NULL, /* class_data */
3843                         sizeof (EBookBackendGroupwise),
3844                         0,    /* n_preallocs */
3845                         (GInstanceInitFunc) e_book_backend_groupwise_init
3846                 };
3847                                                                                                                              
3848                 type = g_type_register_static (E_TYPE_BOOK_BACKEND, "EBookBackendGroupwise", &info, 0);
3849         }
3850                                                                                                                              
3851         return type;
3852 }