speed up of marking messages as read
[claws.git] / src / addrbook.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2001-2002 Match Grun and the Sylpheed-Claws team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 /* General functions for accessing address book files */
21
22 #include <glib.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <dirent.h>
26 #include <sys/stat.h>
27 #include <math.h>
28 #include <setjmp.h>
29
30 #include "xml.h"
31 #include "mgutils.h"
32 #include "addritem.h"
33 #include "addrcache.h"
34 #include "addrbook.h"
35 #include "adbookbase.h"
36
37 #ifndef DEV_STANDALONE
38 #include "prefs_gtk.h"
39 #include "codeconv.h"
40 #endif
41
42 #define ADDRBOOK_MAX_SEARCH_COUNT 1000
43 #define ADDRBOOK_PREFIX           "addrbook-"
44 #define ADDRBOOK_SUFFIX           ".xml"
45 #define FILE_NUMDIGITS            6
46
47 #define ID_TIME_OFFSET            998000000
48
49 /* Create new address book */
50 AddressBookFile *addrbook_create_book()
51 {
52         AddressBookFile *book;
53
54         book = g_new0(AddressBookFile, 1);
55         book->type = ADBOOKTYPE_BOOK;
56         book->addressCache = addrcache_create();
57         book->retVal = MGU_SUCCESS;
58         book->path = NULL;
59         book->fileName = NULL;
60         book->maxValue = 0;
61         book->tempList = NULL;
62         book->tempHash = NULL;
63         book->addressCache->modified = TRUE;
64         return book;
65 }
66
67 /* Specify name to be used */
68 void addrbook_set_name(AddressBookFile *book, const gchar *value)
69 {
70         g_return_if_fail(book != NULL);
71         addrcache_set_name(book->addressCache, value);
72 }
73
74 gchar *addrbook_get_name(AddressBookFile *book)
75 {
76         g_return_val_if_fail(book != NULL, NULL);
77         return addrcache_get_name(book->addressCache);
78 }
79
80 void addrbook_set_path(AddressBookFile *book, const gchar *value)
81 {
82         g_return_if_fail(book != NULL);
83         book->path = mgu_replace_string(book->path, value);
84         addrcache_set_dirty(book->addressCache, TRUE);
85 }
86
87 void addrbook_set_file(AddressBookFile *book, const gchar *value)
88 {
89         g_return_if_fail(book != NULL);
90         book->fileName = mgu_replace_string(book->fileName, value);
91         addrcache_set_dirty(book->addressCache, TRUE);
92 }
93
94 gboolean addrbook_get_modified(AddressBookFile *book)
95 {
96         g_return_val_if_fail(book != NULL, FALSE);
97         return book->addressCache->modified;
98 }
99
100 void addrbook_set_modified(AddressBookFile *book, const gboolean value)
101 {
102         g_return_if_fail(book != NULL);
103         book->addressCache->modified = value;
104 }
105
106 gboolean addrbook_get_accessed(AddressBookFile *book)
107 {
108         g_return_val_if_fail(book != NULL, FALSE);
109         return book->addressCache->accessFlag;
110 }
111
112 void addrbook_set_accessed(AddressBookFile *book, const gboolean value)
113 {
114         g_return_if_fail(book != NULL);
115         book->addressCache->accessFlag = value;
116 }
117
118 gboolean addrbook_get_read_flag(AddressBookFile *book)
119 {
120         g_return_val_if_fail(book != NULL, FALSE);
121         return book->addressCache->dataRead;
122 }
123
124 void addrbook_set_read_flag(AddressBookFile *book, const gboolean value)
125 {
126         g_return_if_fail(book != NULL);
127         book->addressCache->dataRead = value;
128 }
129
130 gint addrbook_get_status(AddressBookFile *book)
131 {
132         g_return_val_if_fail(book != NULL, -1);
133         return book->retVal;
134 }
135
136 ItemFolder *addrbook_get_root_folder(AddressBookFile *book)
137 {
138         g_return_val_if_fail(book != NULL, NULL);
139         return addrcache_get_root_folder(book->addressCache);
140 }
141
142 GList *addrbook_get_list_folder(AddressBookFile *book)
143 {
144         g_return_val_if_fail(book != NULL, NULL);
145         return addrcache_get_list_folder(book->addressCache);
146 }
147
148 GList *addrbook_get_list_person(AddressBookFile *book)
149 {
150         g_return_val_if_fail(book != NULL, NULL);
151         return addrcache_get_list_person(book->addressCache);
152 }
153
154 gboolean addrbook_get_dirty(AddressBookFile *book)
155 {
156         g_return_val_if_fail(book != NULL, FALSE);
157         return addrcache_get_dirty(book->addressCache);
158 }
159
160 void addrbook_set_dirty(AddressBookFile *book, const gboolean value)
161 {
162         g_return_if_fail(book != NULL);
163         addrcache_set_dirty(book->addressCache, value);
164 }
165
166 /* Empty address book */
167 void addrbook_empty_book(AddressBookFile *book)
168 {
169         g_return_if_fail(book != NULL);
170
171         /* Free up internal objects */
172         addrcache_clear(book->addressCache);
173         addrcache_set_dirty(book->addressCache, FALSE);
174         g_list_free(book->tempList);
175
176         /* Reset to initial state */
177         book->tempList = NULL;
178         book->tempHash = NULL;
179         book->addressCache->dataRead = FALSE;
180         book->addressCache->modified = FALSE;
181         book->addressCache->accessFlag = FALSE;
182         book->retVal = MGU_SUCCESS;
183 }
184
185 /* Free address book */
186 void addrbook_free_book(AddressBookFile *book)
187 {
188         g_return_if_fail(book != NULL);
189
190         /* Clear cache */
191         addrcache_clear(book->addressCache);
192         addrcache_free(book->addressCache);
193
194         /* Free up internal objects */
195         g_free(book->path);
196         g_free(book->fileName);
197         g_list_free(book->tempList);
198
199         book->path = NULL;
200         book->fileName = NULL;
201         book->maxValue = 0;
202         book->tempList = NULL;
203         book->tempHash = NULL;
204
205         book->type = ADBOOKTYPE_NONE;
206         book->addressCache = NULL;
207         book->retVal = MGU_SUCCESS;
208
209         g_free(book);
210 }
211
212 /* Print list of items */
213 void addrbook_print_item_list(GList *list, FILE *stream)
214 {
215         GList *node = list;
216
217         while(node) {
218                 AddrItemObject *obj = node->data;
219                 if (ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON) 
220                         addritem_print_item_person((ItemPerson *) obj, stream); 
221                 else if (ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP)
222                         addritem_print_item_group((ItemGroup *) obj, stream); 
223                 else if (ADDRITEM_TYPE(obj) == ITEMTYPE_FOLDER) 
224                         addritem_print_item_folder((ItemFolder *) obj, stream);
225                 node = g_list_next(node);
226         }
227         fprintf(stream, "\t---\n");
228 }
229
230 /* Print address book */
231 void addrbook_print_book(AddressBookFile *book, FILE *stream)
232 {
233         g_return_if_fail(book != NULL);
234
235         fprintf(stream, "AddressBook:\n");
236         fprintf(stream, "\t  path : '%s'\n", book->path);
237         fprintf(stream, "\t  file : '%s'\n", book->fileName);
238         fprintf(stream, "\tstatus : %d\n",   book->retVal );
239         addrcache_print(book->addressCache, stream);
240 }
241
242 /* Dump entire address book traversing folders */
243 void addrbook_dump_book(AddressBookFile *book, FILE *stream)
244 {
245         ItemFolder *folder;
246
247         g_return_if_fail(book != NULL);
248
249         addrbook_print_book(book, stream);
250         folder = book->addressCache->rootFolder;
251         addritem_print_item_folder(folder, stream);
252 }
253
254 /* Remove group from address book.
255    param: group Group to remove.
256    return: Group, or NULL if not found. 
257    Note that object should still be freed */
258 ItemGroup *addrbook_remove_group(AddressBookFile *book, ItemGroup *group)
259 {
260         g_return_val_if_fail(book != NULL, NULL);
261         return addrcache_remove_group(book->addressCache, group);
262 }
263
264 /* Remove specified person from address book.
265    param: person        Person to remove.
266   return: Person, or NULL if not found. 
267   Note that object should still be freed */
268 ItemPerson *addrbook_remove_person(AddressBookFile *book, ItemPerson *person)
269 {
270         g_return_val_if_fail(book != NULL, NULL);
271         return addrcache_remove_person(book->addressCache, person);
272 }
273
274 /* Remove email address in address book for specified person.
275    param: person        Person.
276           email         EMail to remove.
277   return: EMail object, or NULL if not found. 
278   Note that object should still be freed */
279 ItemEMail *addrbook_person_remove_email(AddressBookFile *book,
280                                         ItemPerson *person, ItemEMail *email)
281 {
282         g_return_val_if_fail(book != NULL, NULL);
283         return addrcache_person_remove_email(book->addressCache, person, email);
284 }
285
286 /* **********************************************************************
287 * Read/Write XML data file...
288 * ===========================
289 * Notes:
290 * 1)    The address book is structured as follows:
291 *
292 *               address-book
293 *                       person
294 *                               address-list
295 *                                       address
296 *                               attribute-list
297 *                                       attribute
298 *                       group
299 *                               member-list
300 *                                       member
301 *                       folder
302 *                               item-list
303 *                                       item
304 *
305 * 2)    This sequence of elements was chosen so that the most important
306 *       elements (person and their email addresses) appear first.
307 *
308 * 3)    Groups then appear. When groups are loaded, person's email
309 *       addresses have already been loaded and can be found.
310 *
311 * 4)    Finally folders are loaded. Any forward and backward references
312 *       to folders, groups and persons in the folders are resolved after
313 *       loading.
314 *
315 * ***********************************************************************
316 */
317
318 /* Element tag names */
319 #define AB_ELTAG_ADDRESS         "address"
320 #define AB_ELTAG_ATTRIBUTE       "attribute"
321 #define AB_ELTAG_ATTRIBUTE_LIST  "attribute-list"
322 #define AB_ELTAG_ADDRESS_LIST    "address-list"
323 #define AB_ELTAG_MEMBER          "member"
324 #define AB_ELTAG_MEMBER_LIST     "member-list"
325 #define AB_ELTAG_ITEM            "item"
326 #define AB_ELTAG_ITEM_LIST       "item-list"
327 #define AB_ELTAG_ADDRESS_BOOK    "address-book"
328 #define AB_ELTAG_PERSON          "person"
329 #define AB_ELTAG_GROUP           "group"
330 #define AB_ELTAG_FOLDER          "folder"
331
332 /* Attribute tag names */
333 #define AB_ATTAG_TYPE            "type"
334 #define AB_ATTAG_UID             "uid"
335 #define AB_ATTAG_NAME            "name"
336 #define AB_ATTAG_REMARKS         "remarks"
337 #define AB_ATTAG_FIRST_NAME      "first-name"
338 #define AB_ATTAG_LAST_NAME       "last-name"
339 #define AB_ATTAG_NICK_NAME       "nick-name"
340 #define AB_ATTAG_COMMON_NAME     "cn"
341 #define AB_ATTAG_ALIAS           "alias"
342 #define AB_ATTAG_EMAIL           "email"
343 #define AB_ATTAG_EID             "eid"
344 #define AB_ATTAG_PID             "pid"
345
346 /* Attribute values */
347 #define AB_ATTAG_VAL_PERSON      "person"
348 #define AB_ATTAG_VAL_GROUP       "group"
349 #define AB_ATTAG_VAL_FOLDER      "folder"
350
351 /* Parse address item for person */
352 static void addrbook_parse_address(AddressBookFile *book, XMLFile *file, 
353                                    ItemPerson *person)
354 {
355         GList *attr;
356         gchar *name, *value;
357         ItemEMail *email = NULL;
358
359         attr = xml_get_current_tag_attr(file);
360         while (attr) {
361                 name = ((XMLAttr *)attr->data)->name;
362                 value = ((XMLAttr *)attr->data)->value;
363                 if (!email)
364                         email = addritem_create_item_email();
365                 if (strcmp(name, AB_ATTAG_UID) == 0)
366                         ADDRITEM_ID(email) = g_strdup(value);
367                 else if (strcmp(name, AB_ATTAG_ALIAS) == 0)
368                         ADDRITEM_NAME(email) = g_strdup(value);
369                 else if (strcmp(name, AB_ATTAG_EMAIL) == 0)
370                         email->address = g_strdup(value);
371                 else if (strcmp(name, AB_ATTAG_REMARKS) == 0)
372                         email->remarks = g_strdup(value);
373                 attr = g_list_next(attr);
374         }
375         if (email) {
376                 if (person) {
377                         addrcache_person_add_email(book->addressCache, person,
378                                                    email);
379                 }
380                 else {
381                         addritem_free_item_email(email);
382                         email = NULL;
383                 }
384         }
385 }
386
387 /* Parse email address list */
388 static void addrbook_parse_addr_list(AddressBookFile *book, XMLFile *file, 
389                                      ItemPerson *person)
390 {
391         GList *attr;
392         guint prev_level;
393
394         for (;;) {
395                 prev_level = file->level;
396                 if (xml_parse_next_tag(file)) {
397                         longjmp(book->jumper, 1);
398                 }
399                 if (file->level < prev_level) return;
400                 if (xml_compare_tag(file, AB_ELTAG_ADDRESS)) {
401                         attr = xml_get_current_tag_attr(file);
402                         addrbook_parse_address(book, file, person);
403                         addrbook_parse_addr_list(book, file, person);
404                 }
405         }
406 }
407
408 /* Parse attibute for person */
409 static void addrbook_parse_attribute(XMLFile *file, ItemPerson *person)
410 {
411         GList *attr;
412         gchar *name, *value;
413         gchar *element;
414         UserAttribute *uAttr = NULL;
415
416         attr = xml_get_current_tag_attr(file);
417         while (attr) {
418                 name = ((XMLAttr *)attr->data)->name;
419                 value = ((XMLAttr *)attr->data)->value;
420                 if (!uAttr) uAttr = addritem_create_attribute();
421                 if (strcmp(name, AB_ATTAG_UID) == 0)
422                         addritem_attrib_set_id(uAttr, value);
423                 else if (strcmp(name, AB_ATTAG_NAME) == 0) 
424                         addritem_attrib_set_name(uAttr, value);
425                 attr = g_list_next(attr);
426         }
427
428         element = xml_get_element(file);
429         addritem_attrib_set_value(uAttr, element);
430
431         if (uAttr) {
432                 if (person) {
433                         addritem_person_add_attribute(person, uAttr);
434                 }
435                 else {
436                         addritem_free_attribute(uAttr);
437                         uAttr = NULL;
438                 }
439         }
440 }
441
442 /* Parse attribute list */
443 static void addrbook_parse_attr_list(AddressBookFile *book, XMLFile *file, 
444                                      ItemPerson *person)
445 {
446         GList *attr;
447         guint prev_level;
448
449         for (;;) {
450                 prev_level = file->level;
451                 if (xml_parse_next_tag(file)) {
452                         longjmp( book->jumper, 1 );
453                 }
454                 if (file->level < prev_level) return;
455                 if (xml_compare_tag(file, AB_ELTAG_ATTRIBUTE)) {
456                         attr = xml_get_current_tag_attr(file);
457                         addrbook_parse_attribute(file, person);
458                         addrbook_parse_attr_list(book, file, person);
459                 }
460         }
461 }
462
463 /* Parse person */
464 static void addrbook_parse_person(AddressBookFile *book, XMLFile *file)
465 {
466         GList *attr;
467         gchar *name, *value;
468         ItemPerson *person = NULL;
469
470         attr = xml_get_current_tag_attr(file);
471         while (attr) {
472                 name = ((XMLAttr *)attr->data)->name;
473                 value = ((XMLAttr *)attr->data)->value;
474                 if (!person) 
475                         person = addritem_create_item_person();
476                 if (strcmp(name, AB_ATTAG_UID) == 0)
477                         ADDRITEM_ID(person) = g_strdup(value);
478                 else if (strcmp(name, AB_ATTAG_FIRST_NAME) == 0)
479                         person->firstName = g_strdup(value);
480                 else if (strcmp(name, AB_ATTAG_LAST_NAME) == 0)
481                         person->lastName = g_strdup(value);
482                 else if (strcmp(name, AB_ATTAG_NICK_NAME) == 0)
483                         person->nickName = g_strdup(value);
484                 else if (strcmp(name, AB_ATTAG_COMMON_NAME) == 0)
485                         ADDRITEM_NAME(person) = g_strdup(value);
486                 attr = g_list_next(attr);
487         }
488         if (xml_parse_next_tag(file)) { /* Consume closing tag */
489                 longjmp(book->jumper, 1);
490         }
491         if (xml_compare_tag(file, AB_ELTAG_ADDRESS_LIST)) {
492                 addrbook_parse_addr_list(book, file, person);
493                 if (person) {
494                         addrcache_hash_add_person(book->addressCache, person);
495                 }
496         }
497         if (xml_parse_next_tag(file)) { /* Consume closing tag */
498                 longjmp(book->jumper, 1);
499         }
500         if (xml_compare_tag(file, AB_ELTAG_ATTRIBUTE_LIST)) {
501                 addrbook_parse_attr_list(book, file, person);
502         }
503 }
504
505 /* Parse group member */
506 static void addrbook_parse_member(AddressBookFile *book, XMLFile *file, 
507                                   ItemGroup *group)
508 {
509         GList *attr;
510         gchar *name, *value;
511         gchar *pid = NULL, *eid = NULL;
512         ItemEMail *email = NULL;
513
514         attr = xml_get_current_tag_attr(file);
515         while (attr) {
516                 name = ((XMLAttr *)attr->data)->name;
517                 value = ((XMLAttr *)attr->data)->value;
518                 if (strcmp(name, AB_ATTAG_PID) == 0)
519                         pid = g_strdup(value);
520                 else if (strcmp(name, AB_ATTAG_EID) == 0)
521                         eid = g_strdup(value);
522                 attr = g_list_next(attr);
523         }
524         /* email = addrcache_get_email( book->addressCache, pid, eid ); */
525         email = addrcache_get_email(book->addressCache, eid);
526         if (email) {
527                 if (group) {
528                         addrcache_group_add_email(book->addressCache, group, 
529                                                   email);
530                 }
531                 else {
532                         addritem_free_item_email(email);
533                         email = NULL;
534                 }
535         }
536 }
537
538 /* Parse group member list */
539 static void addrbook_parse_member_list(AddressBookFile *book, XMLFile *file, 
540                                        ItemGroup *group)
541 {
542         GList *attr;
543         guint prev_level;
544
545         for (;;) {
546                 prev_level = file->level;
547                 if (xml_parse_next_tag(file)) {
548                         longjmp(book->jumper, 1);
549                 }
550                 if (file->level < prev_level)
551                         return;
552                 if (xml_compare_tag(file, AB_ELTAG_MEMBER)) {
553                         attr = xml_get_current_tag_attr(file);
554                         addrbook_parse_member(book, file, group);
555                         addrbook_parse_member_list(book, file, group);
556                 }
557                 else {
558                         attr = xml_get_current_tag_attr(file);
559                 }
560         }
561 }
562
563 /* Parse group */
564 static void addrbook_parse_group(AddressBookFile *book, XMLFile *file)
565 {
566         GList *attr;
567         gchar *name, *value;
568         ItemGroup *group = NULL;
569
570         attr = xml_get_current_tag_attr(file);
571         while (attr) {
572                 name = ((XMLAttr *)attr->data)->name;
573                 value = ((XMLAttr *)attr->data)->value;
574                 if (!group) 
575                         group = addritem_create_item_group();
576                 if (strcmp(name, AB_ATTAG_UID) == 0) 
577                         ADDRITEM_ID(group) = g_strdup(value);
578                 else if (strcmp(name, AB_ATTAG_NAME) == 0)
579                         ADDRITEM_NAME(group) = g_strdup(value);
580                 else if (strcmp(name, AB_ATTAG_REMARKS) == 0)
581                         group->remarks = g_strdup(value);
582                 attr = g_list_next(attr);
583         }
584         if (xml_parse_next_tag(file)) { /* Consume closing tag */
585                 longjmp(book->jumper, 1);
586         }
587         if (xml_compare_tag(file, AB_ELTAG_MEMBER_LIST)) {
588                 if (group) {
589                         addrcache_hash_add_group(book->addressCache, group);
590                 }
591                 addrbook_parse_member_list(book, file, group);
592         }
593 }
594
595 /* Parse folder item */
596 static void addrbook_parse_folder_item(AddressBookFile *book, XMLFile *file, 
597                                        ItemFolder *folder)
598 {
599         GList *attr;
600         gchar *name, *value;
601         gchar *uid = NULL;
602
603         attr = xml_get_current_tag_attr(file);
604         while (attr) {
605                 name = ((XMLAttr *)attr->data)->name;
606                 value = ((XMLAttr *)attr->data)->value;
607                 if (strcmp(name, AB_ATTAG_UID) == 0) {
608                         uid = g_strdup(value);
609                 }
610                 attr = g_list_next(attr);
611         }
612         if (folder) {
613                 if (uid) {
614                         folder->listItems = g_list_append(folder->listItems, uid);
615                 }
616         }
617 }
618
619 /* Parse folder item list */
620 static void addrbook_parse_folder_list(AddressBookFile *book, XMLFile *file,
621                                        ItemFolder *folder)
622 {
623         GList *attr;
624         guint prev_level;
625
626         for (;;) {
627                 prev_level = file->level;
628                 if (xml_parse_next_tag(file)) {
629                         longjmp(book->jumper, 1);
630                 }
631                 if (file->level < prev_level)
632                         return;
633                 if (xml_compare_tag(file, AB_ELTAG_ITEM)) {
634                         attr = xml_get_current_tag_attr(file);
635                         addrbook_parse_folder_item(book, file, folder);
636                         addrbook_parse_folder_list(book, file, folder);
637                 }
638                 else {
639                         attr = xml_get_current_tag_attr(file);
640                 }
641         }
642 }
643
644 /* Parse folder */
645 static void addrbook_parse_folder(AddressBookFile *book, XMLFile *file) 
646 {
647         GList *attr;
648         gchar *name, *value;
649         ItemFolder *folder = NULL;
650
651         attr = xml_get_current_tag_attr(file);
652         while (attr) {
653                 name = ((XMLAttr *)attr->data)->name;
654                 value = ((XMLAttr *)attr->data)->value;
655                 if (!folder)
656                         folder = addritem_create_item_folder();
657                 if (strcmp(name, AB_ATTAG_UID) == 0)
658                         ADDRITEM_ID(folder) = g_strdup(value);
659                 else if (strcmp(name, AB_ATTAG_NAME) == 0)
660                         ADDRITEM_NAME(folder) = g_strdup(value);
661                 else if (strcmp(name, AB_ATTAG_REMARKS) == 0)
662                         folder->remarks = g_strdup(value);
663                 attr = g_list_next(attr);
664         }
665         if (xml_parse_next_tag(file)) { /* Consume closing tag */
666                 longjmp(book->jumper, 1);
667         }
668         if (xml_compare_tag(file, AB_ELTAG_ITEM_LIST)) {
669                 if (folder) {
670                         if (addrcache_hash_add_folder(book->addressCache, 
671                                                       folder)) {
672                                 book->tempList = g_list_append(book->tempList, 
673                                                                folder);
674                                 /* We will resolve folder later */
675                                 ADDRITEM_PARENT(folder) = NULL;
676                         }
677                 }
678                 addrbook_parse_folder_list(book, file, folder);
679         }
680 }
681
682 /* Parse address book.
683    Return: TRUE if data read successfully, FALSE if error reading data */
684 static gboolean addrbook_read_tree(AddressBookFile *book, XMLFile *file)
685 {
686         gboolean retVal;
687         GList *attr;
688         gchar *name, *value;
689
690         book->retVal = MGU_BAD_FORMAT;
691         if (xml_get_dtd(file))
692                 return FALSE;
693         if (xml_parse_next_tag(file))
694                 longjmp(book->jumper, 1);
695         if (!xml_compare_tag(file, AB_ELTAG_ADDRESS_BOOK))
696                 return FALSE;
697
698         attr = xml_get_current_tag_attr(file);
699         while (attr) {
700                 name = ((XMLAttr *)attr->data)->name;
701                 value = ((XMLAttr *)attr->data)->value;
702                 if (strcmp( name, AB_ATTAG_NAME) == 0)
703                         addrbook_set_name( book, value );
704                 attr = g_list_next( attr );
705         }
706
707         retVal = TRUE;
708         for (;;) {
709                 if (!file->level)
710                         break;
711                 /* Get next item tag (person, group or folder) */
712                 if (xml_parse_next_tag(file))
713                         longjmp( book->jumper, 1 );
714                         
715                 if (xml_compare_tag(file, AB_ELTAG_PERSON))
716                         addrbook_parse_person(book, file);
717                 else if (xml_compare_tag(file, AB_ELTAG_GROUP))
718                         addrbook_parse_group(book, file);
719                 else if (xml_compare_tag(file, AB_ELTAG_FOLDER))
720                         addrbook_parse_folder(book, file);
721         }
722         if (retVal) book->retVal = MGU_SUCCESS;
723                 return retVal;
724 }
725
726 /* Resolve folder items visitor function */
727 static void addrbook_res_items_vis(gpointer key, gpointer value, gpointer data)
728 {
729         AddressBookFile *book = data;
730         AddrItemObject *obj = (AddrItemObject *) value;
731         ItemFolder *rootFolder = book->addressCache->rootFolder;
732         if (obj->parent == NULL) {
733                 if (ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON) {
734                         rootFolder->listPerson = g_list_append(rootFolder->listPerson,
735                                                                obj);
736                         ADDRITEM_PARENT(obj) = ADDRITEM_OBJECT(rootFolder);
737                 }
738                 else if (ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP) {
739                         rootFolder->listGroup = g_list_append(rootFolder->listGroup,
740                                                               obj);
741                         ADDRITEM_PARENT(obj) = ADDRITEM_OBJECT(rootFolder);
742                 }
743         }
744 }
745
746 /* Resolve folder items. Lists of UID's are replaced with pointers to 
747    data items */
748 static void addrbook_resolve_folder_items(AddressBookFile *book)
749 {
750         GList *nodeFolder = NULL;
751         GList *listRemove = NULL;
752         GList *node = NULL;
753         ItemFolder *rootFolder = book->addressCache->rootFolder;
754         nodeFolder = book->tempList;
755         
756         while (nodeFolder) {
757                 ItemFolder *folder = nodeFolder->data;
758                 listRemove = NULL;
759                 node = folder->listItems;
760                 while (node) {
761                         gchar *uid = node->data;
762                         AddrItemObject *aio = addrcache_get_object(book->addressCache, 
763                                                                    uid);
764                         if (aio) {
765                                 if (aio->type == ITEMTYPE_FOLDER) {
766                                         ItemFolder *item = (ItemFolder *) aio;
767                                         folder->listFolder = g_list_append(folder->listFolder, item);
768                                         ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder);
769                                         addrcache_hash_add_folder(book->addressCache, folder);
770                                 }
771                                 else if (aio->type == ITEMTYPE_PERSON) {
772                                         ItemPerson *item = (ItemPerson *) aio;
773                                         folder->listPerson = g_list_append(folder->listPerson, item);
774                                         ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder);
775                                 }
776                                 else if (aio->type == ITEMTYPE_GROUP) {
777                                         ItemGroup *item = (ItemGroup *) aio;
778                                         folder->listGroup = g_list_append(folder->listGroup, item);
779                                         ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder);
780                                 }
781                                 /* Replace data with pointer to item */
782                                 g_free(uid);
783                                 node->data = aio;
784                         }
785                         else { /* Not found, append to remove list. */
786                                 listRemove = g_list_append(listRemove, uid);
787                         }
788                         node = g_list_next(node);
789                 }
790                 rootFolder->listFolder = g_list_append(rootFolder->listFolder, 
791                                                        folder);
792                 /* Process remove list */
793                 node = listRemove;
794                 while (node) {
795                         gchar *uid = node->data;
796                         folder->listItems = g_list_remove(folder->listItems,
797                                                           uid);
798                         g_free(uid);
799                         node = g_list_next(node);
800                 }
801                 g_list_free(listRemove);
802                 nodeFolder = g_list_next(nodeFolder);
803         }
804         /* Remove folders with parents. */
805         listRemove = NULL;
806         node = rootFolder->listFolder;
807         while (node) {
808                 ItemFolder *folder = (ItemFolder *) node->data;
809                 if (ADDRITEM_PARENT(folder))
810                         /* Remove folders with parents */
811                         listRemove = g_list_append(listRemove, folder);
812                 else /* Add to root folder */
813                         ADDRITEM_PARENT(folder) = ADDRITEM_OBJECT(book->addressCache->rootFolder);
814
815                 node = g_list_next( node );
816         }
817         /* Process remove list */
818         node = listRemove;
819         while (node) {
820                 rootFolder->listFolder = g_list_remove(rootFolder->listFolder, 
821                                                        node->data);
822                 node = g_list_next(node);
823         }
824         g_list_free(listRemove);
825
826         /* Move all unparented persons and groups into root folder */
827         g_hash_table_foreach(book->addressCache->itemHash, 
828                              addrbook_res_items_vis, book);
829
830         /* Free up some more */
831         nodeFolder = book->tempList;
832         while (nodeFolder) {
833                 ItemFolder *folder = nodeFolder->data;
834                 g_list_free(folder->listItems);
835                 folder->listItems = NULL;
836                 nodeFolder = g_list_next(nodeFolder);
837         }
838         g_list_free(book->tempList);
839         book->tempList = NULL;
840 }
841
842 /* Read address book file */
843 gint addrbook_read_data(AddressBookFile *book)
844 {
845         XMLFile *file = NULL;
846         gchar *fileSpec = NULL;
847
848         g_return_val_if_fail(book != NULL, -1);
849
850         /*
851         printf( "...addrbook_read_data :%s:\t:%s:\n", book->fileName,
852                 addrcache_get_name( book->addressCache ) );
853         */
854
855         fileSpec = g_strconcat(book->path, G_DIR_SEPARATOR_S, 
856                                book->fileName, NULL);
857         book->retVal = MGU_OPEN_FILE;
858         addrcache_clear(book->addressCache);
859         book->addressCache->modified = FALSE;
860         book->addressCache->accessFlag = FALSE;
861         file = xml_open_file(fileSpec);
862         g_free(fileSpec);
863         if (file) {
864                 book->tempList = NULL;
865                 /* Trap for parsing errors. */
866                 if (setjmp( book->jumper)) {
867                         xml_close_file(file);
868                         return book->retVal;
869                 }
870                 addrbook_read_tree(book, file);
871                 xml_close_file(file);
872                 /* Resolve folder items */
873                 addrbook_resolve_folder_items(book);
874                 book->tempList = NULL;
875                 book->addressCache->modified = FALSE;
876                 book->addressCache->dataRead = TRUE;
877                 addrcache_set_dirty(book->addressCache, FALSE);
878         }
879         return book->retVal;
880 }
881
882 static void addrbook_write_elem_s(FILE *fp, gint lvl, gchar *name)
883 {
884         gint i;
885         for (i = 0; i < lvl; i++) 
886                 fputs("  ", fp);
887         fputs("<", fp);
888         fputs(name, fp);
889 }
890
891 static void addrbook_write_elem_e(FILE *fp, gint lvl, gchar *name)
892 {
893         gint i;
894         for(i = 0; i < lvl; i++)
895                 fputs("  ", fp);
896         fputs("</", fp);
897         fputs(name, fp);
898         fputs(">\n", fp);
899 }
900
901 static void addrbook_write_attr(FILE *fp, gchar *name, gchar *value)
902 {
903         fputs(" ", fp);
904         fputs(name, fp);
905         fputs("=\"", fp);
906         xml_file_put_escape_str(fp, value);
907         fputs("\"", fp);
908 }
909
910 /* Write file hash table visitor function */
911 static void addrbook_write_item_person_vis(gpointer key, gpointer value, 
912                                            gpointer data)
913 {
914         AddrItemObject *obj = (AddrItemObject *) value;
915         FILE *fp = (FILE *) data;
916         GList *node;
917
918         if (!obj)
919                 return;
920         if (ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON) {
921                 ItemPerson *person = (ItemPerson *) value;
922                 if (person) {
923                         addrbook_write_elem_s(fp, 1, AB_ELTAG_PERSON);
924                         addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(person));
925                         addrbook_write_attr(fp, AB_ATTAG_FIRST_NAME, person->firstName);
926                         addrbook_write_attr(fp, AB_ATTAG_LAST_NAME, person->lastName);
927                         addrbook_write_attr(fp, AB_ATTAG_NICK_NAME, person->nickName);
928                         addrbook_write_attr(fp, AB_ATTAG_COMMON_NAME, ADDRITEM_NAME(person));
929                         fputs(" >\n", fp);
930
931                         /* Output email addresses */
932                         addrbook_write_elem_s(fp, 2, AB_ELTAG_ADDRESS_LIST);
933                         fputs(">\n", fp);
934                         node = person->listEMail;
935                         while (node) {
936                                 ItemEMail *email = node->data;
937                                 addrbook_write_elem_s(fp, 3, AB_ELTAG_ADDRESS);
938                                 addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(email));
939                                 addrbook_write_attr(fp, AB_ATTAG_ALIAS, ADDRITEM_NAME(email));
940                                 addrbook_write_attr(fp, AB_ATTAG_EMAIL, email->address);
941                                 addrbook_write_attr(fp, AB_ATTAG_REMARKS, email->remarks);
942                                 fputs(" />\n", fp);
943                                 node = g_list_next(node);
944                         }
945                         addrbook_write_elem_e(fp, 2, AB_ELTAG_ADDRESS_LIST);
946
947                         /* Output user attributes */
948                         addrbook_write_elem_s(fp, 2, AB_ELTAG_ATTRIBUTE_LIST);
949                         fputs(">\n", fp);
950                         node = person->listAttrib;
951                         while (node) {
952                                 UserAttribute *attrib = node->data;
953                                 addrbook_write_elem_s(fp, 3, AB_ELTAG_ATTRIBUTE);
954                                 addrbook_write_attr(fp, AB_ATTAG_UID, attrib->uid);
955                                 addrbook_write_attr(fp, AB_ATTAG_NAME, attrib->name);
956                                 fputs(" >", fp);
957                                 xml_file_put_escape_str(fp, attrib->value);
958                                 addrbook_write_elem_e(fp, 0, AB_ELTAG_ATTRIBUTE);
959                                 node = g_list_next(node);
960                         }
961                         addrbook_write_elem_e(fp, 2, AB_ELTAG_ATTRIBUTE_LIST);
962                         addrbook_write_elem_e(fp, 1, AB_ELTAG_PERSON);
963                 }
964         }
965 }
966
967 /* Write file hash table visitor function */
968 static void addrbook_write_item_group_vis(gpointer key, gpointer value, 
969                                           gpointer data)
970 {
971         AddrItemObject *obj = (AddrItemObject *) value;
972         FILE *fp = (FILE *) data;
973         GList *node;
974
975         if (!obj)
976                 return;
977         if (ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP) {
978                 ItemGroup *group = (ItemGroup *) value;
979                 if (group) {
980                         addrbook_write_elem_s(fp, 1, AB_ELTAG_GROUP);
981                         addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(group));
982                         addrbook_write_attr(fp, AB_ATTAG_NAME, ADDRITEM_NAME(group));
983                         addrbook_write_attr(fp, AB_ATTAG_REMARKS, group->remarks);
984                         fputs(" >\n", fp);
985
986                         /* Output email address links */
987                         addrbook_write_elem_s(fp, 2, AB_ELTAG_MEMBER_LIST);
988                         fputs(">\n", fp);
989                         node = group->listEMail;
990                         while (node) {
991                                 ItemEMail *email = node->data;
992                                 ItemPerson *person = (ItemPerson *) ADDRITEM_PARENT(email);
993                                 addrbook_write_elem_s(fp, 3, AB_ELTAG_MEMBER);
994                                 addrbook_write_attr(fp, AB_ATTAG_PID, ADDRITEM_ID(person));
995                                 addrbook_write_attr(fp, AB_ATTAG_EID, ADDRITEM_ID(email));
996                                 fputs(" />\n", fp);
997                                 node = g_list_next(node);
998                         }
999                         addrbook_write_elem_e(fp, 2, AB_ELTAG_MEMBER_LIST);
1000                         addrbook_write_elem_e(fp, 1, AB_ELTAG_GROUP);
1001                 }
1002         }
1003 }
1004
1005 /* Write file hash table visitor function */
1006 static void addrbook_write_item_folder_vis(gpointer key, gpointer value, 
1007                                            gpointer data)
1008 {
1009         AddrItemObject *obj = (AddrItemObject *) value;
1010         FILE *fp = (FILE *) data;
1011         GList *node;
1012
1013         if (!obj)
1014                 return;
1015         if (ADDRITEM_TYPE(obj) == ITEMTYPE_FOLDER) {
1016                 ItemFolder *folder = (ItemFolder *) value;
1017                 if (folder) {
1018                         addrbook_write_elem_s(fp, 1, AB_ELTAG_FOLDER);
1019                         addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(folder));
1020                         addrbook_write_attr(fp, AB_ATTAG_NAME, ADDRITEM_NAME(folder));
1021                         addrbook_write_attr(fp, AB_ATTAG_REMARKS, folder->remarks);
1022                         fputs(" >\n", fp);
1023                         addrbook_write_elem_s(fp, 2, AB_ELTAG_ITEM_LIST);
1024                         fputs(">\n", fp);
1025
1026                         /* Output persons */
1027                         node = folder->listPerson;
1028                         while (node) {
1029                                 ItemPerson *item = node->data;
1030                                 addrbook_write_elem_s(fp, 3, AB_ELTAG_ITEM);
1031                                 addrbook_write_attr(fp, AB_ATTAG_TYPE,  AB_ATTAG_VAL_PERSON);
1032                                 addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(item));
1033                                 fputs(" />\n", fp);
1034                                 node = g_list_next(node);
1035                         }
1036
1037                         /* Output groups */
1038                         node = folder->listGroup;
1039                         while (node) {
1040                                 ItemGroup *item = node->data;
1041                                 addrbook_write_elem_s(fp, 3, AB_ELTAG_ITEM);
1042                                 addrbook_write_attr(fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_GROUP);
1043                                 addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(item));
1044                                 fputs(" />\n", fp);
1045                                 node = g_list_next(node);
1046                         }
1047
1048                         /* Output folders */
1049                         node = folder->listFolder;
1050                         while (node) {
1051                                 ItemFolder *item = node->data;
1052                                 addrbook_write_elem_s(fp, 3, AB_ELTAG_ITEM);
1053                                 addrbook_write_attr(fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_FOLDER);
1054                                 addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(item));
1055                                 fputs(" />\n", fp);
1056                                 node = g_list_next(node);
1057                         }
1058                         addrbook_write_elem_e(fp, 2, AB_ELTAG_ITEM_LIST);
1059                         addrbook_write_elem_e(fp, 1, AB_ELTAG_FOLDER);
1060                 }
1061         }
1062 }
1063
1064 /* Output address book data to specified file.
1065   return: Status code */
1066 gint addrbook_write_to(AddressBookFile *book, gchar *newFile)
1067 {
1068         FILE *fp;
1069         gchar *fileSpec;
1070 #ifndef DEV_STANDALONE
1071         PrefFile *pfile;
1072 #endif
1073
1074         g_return_val_if_fail(book != NULL, -1);
1075         g_return_val_if_fail(newFile != NULL, -1);
1076
1077         fileSpec = g_strconcat(book->path, G_DIR_SEPARATOR_S, newFile, NULL);
1078
1079         book->retVal = MGU_OPEN_FILE;
1080 #ifdef DEV_STANDALONE
1081         fp = fopen(fileSpec, "wb");
1082         g_free(fileSpec);
1083         if (fp) {
1084                 fputs("<?xml version=\"1.0\" ?>\n", fp);
1085 #else
1086         pfile = prefs_write_open(fileSpec);
1087         g_free(fileSpec);
1088         if (pfile) {
1089                 fp = pfile->fp;
1090                 fprintf(fp, "<?xml version=\"1.0\" encoding=\"%s\" ?>\n",
1091                         conv_get_current_charset_str());
1092 #endif
1093                 addrbook_write_elem_s(fp, 0, AB_ELTAG_ADDRESS_BOOK);
1094                 addrbook_write_attr(fp, AB_ATTAG_NAME,
1095                                     addrcache_get_name(book->addressCache));
1096                 fputs(" >\n", fp);
1097
1098                 /* Output all persons */
1099                 g_hash_table_foreach(book->addressCache->itemHash, 
1100                                      addrbook_write_item_person_vis, fp);
1101
1102                 /* Output all groups */
1103                 g_hash_table_foreach(book->addressCache->itemHash, 
1104                                      addrbook_write_item_group_vis, fp);
1105
1106                 /* Output all folders */
1107                 g_hash_table_foreach(book->addressCache->itemHash, 
1108                                      addrbook_write_item_folder_vis, fp);
1109
1110                 addrbook_write_elem_e(fp, 0, AB_ELTAG_ADDRESS_BOOK);
1111                 book->retVal = MGU_SUCCESS;
1112 #ifdef DEV_STANDALONE
1113                 fclose(fp);
1114 #else
1115                 if (prefs_file_close( pfile ) < 0)
1116                         book->retVal = MGU_ERROR_WRITE;
1117 #endif
1118         }
1119
1120         fileSpec = NULL;
1121         return book->retVal;
1122 }
1123
1124 /* Output address book data to original file.
1125    return: Status code */
1126 gint addrbook_save_data(AddressBookFile *book)
1127 {
1128         g_return_val_if_fail(book != NULL, -1);
1129
1130         book->retVal = MGU_NO_FILE;
1131         if (book->fileName == NULL || *book->fileName == '\0') 
1132                 return book->retVal;
1133         if (book->path == NULL || *book->path == '\0')
1134                 return book->retVal;
1135
1136         addrbook_write_to(book, book->fileName);
1137         if (book->retVal == MGU_SUCCESS)
1138                 addrcache_set_dirty(book->addressCache, FALSE);
1139         return book->retVal;
1140 }
1141
1142 /* **********************************************************************
1143    Address book edit interface functions...
1144    ***********************************************************************
1145   Move person's email item.
1146   param: book       Address book.
1147          person     Person.
1148          itemMove   Item to move.
1149          itemTarget Target item before which to move item */
1150 ItemEMail *addrbook_move_email_before(AddressBookFile *book, ItemPerson *person,
1151                                       ItemEMail *itemMove, ItemEMail *itemTarget)
1152 {
1153         ItemEMail *email = NULL;
1154
1155         g_return_val_if_fail(book != NULL, NULL);
1156
1157         email = addritem_move_email_before(person, itemMove, itemTarget);
1158         if (email) 
1159                 addrcache_set_dirty(book->addressCache, TRUE);
1160         return email;
1161 }
1162
1163 /* Move person's email item.
1164    param: book       Address book.
1165           person     Person.
1166          itemMove   Item to move.
1167          itemTarget Target item after which to move item */
1168 ItemEMail *addrbook_move_email_after(AddressBookFile *book, ItemPerson *person,
1169                                      ItemEMail *itemMove, ItemEMail *itemTarget)
1170 {
1171         ItemEMail *email = NULL;
1172
1173         g_return_val_if_fail(book != NULL, NULL);
1174
1175         email = addritem_move_email_after(person, itemMove, itemTarget);
1176         if (email)
1177                 addrcache_set_dirty(book->addressCache, TRUE);
1178         return email;
1179 }
1180
1181 /* Hash table visitor function for deletion of hashtable entries */
1182 static gboolean addrbook_free_simple_hash_vis(gpointer *key, gpointer *value, 
1183                                               gpointer *data)
1184 {
1185         g_free(key);
1186         key = NULL;
1187         value = NULL;
1188         return TRUE;
1189 }
1190
1191 /* Update address book email list for specified person.
1192    Enter: book      Address book.
1193           person    Person to update.
1194           listEMail List of new email addresses.
1195   Note: The existing email addresses are replaced with the new addresses. Any references
1196   to old addresses in the groups are re-linked to the new addresses. All old addresses
1197   linked to the person are removed */
1198 void addrbook_update_address_list(AddressBookFile *book, ItemPerson *person, 
1199                                   GList *listEMail)
1200 {
1201         GList *node;
1202         GList *listDelete;
1203         GList *listGroup;
1204
1205         g_return_if_fail(book != NULL);
1206         g_return_if_fail(person != NULL);
1207
1208         /* Get groups where person's existing email addresses are listed */
1209         listGroup = addrcache_get_group_for_person(book->addressCache, person);
1210         if (listGroup) {
1211                 GHashTable *hashEMail;
1212                 GHashTable *hashEMailAlias;
1213                 GList *nodeGrp;
1214
1215                 /* Load hash table with new address entries */
1216                 hashEMail = g_hash_table_new(g_str_hash, g_str_equal);
1217                 hashEMailAlias = g_hash_table_new(g_str_hash, g_str_equal);
1218                 node = listEMail;
1219                 while (node) {
1220                         ItemEMail *email = node->data;
1221                         gchar *addr = g_strdup(email->address);
1222                         gchar *alias = email->obj.name ;
1223                         g_strdown(addr);
1224                         if (!g_hash_table_lookup(hashEMail, addr)) {
1225                                 g_hash_table_insert(hashEMail, addr, email);
1226                         }
1227                         if (*alias != '\0' && ! g_hash_table_lookup(hashEMailAlias,
1228                             alias)) 
1229                                 g_hash_table_insert(hashEMailAlias, alias, email);
1230
1231                         node = g_list_next(node);
1232                 }
1233
1234                 /* Re-parent new addresses to existing groups, where email address match. */
1235                 nodeGrp = listGroup;
1236                 while (nodeGrp) {
1237                         ItemGroup *group = (ItemGroup *) nodeGrp->data;
1238                         GList *groupEMail = group->listEMail;
1239                         GList *nodeGrpEM;
1240                         GList *listRemove = NULL;
1241
1242                         /* Process each email item linked to group */
1243                         nodeGrpEM = groupEMail;
1244                         while (nodeGrpEM) {
1245                                 ItemEMail *emailGrp = (ItemEMail *) nodeGrpEM->data;
1246
1247                                 if (ADDRITEM_PARENT(emailGrp) == ADDRITEM_OBJECT(person)) {
1248                                         /* Found an email address for this person */
1249                                         ItemEMail *emailNew = NULL;
1250                                         gchar *addr = g_strdup(emailGrp->address);
1251                                         gchar *alias = emailGrp->obj.name;
1252                                         g_strdown(addr);
1253                                         emailNew = (ItemEMail *)
1254                                                 g_hash_table_lookup(hashEMail, addr);
1255                                         g_free( addr );
1256                                         /* If no match by e-mail, try to match by e-mail alias */
1257                                         if (!emailNew && *alias != '\0') {
1258                                                 emailNew = (ItemEMail *)
1259                                                         g_hash_table_lookup(hashEMailAlias, alias);
1260                                         }
1261                                         
1262                                         if (emailNew)
1263                                                 /* Point to this entry */
1264                                                 nodeGrpEM->data = emailNew;
1265                                         else if (g_hash_table_size(hashEMail)==1)
1266                                                 /* If the person has just one e-mail address, then 
1267                                                    change e-mail address in group list */
1268                                                 nodeGrpEM->data = listEMail->data;
1269                                         else 
1270                                                 /* Mark for removal */
1271                                                 listRemove = g_list_append(listRemove, emailGrp);
1272                                 }
1273                                 /* Move on to next email link */
1274                                 nodeGrpEM = g_list_next(nodeGrpEM);
1275                         }
1276
1277                         /* Process all removed links in current group */
1278                         nodeGrpEM = listRemove;
1279                         while (nodeGrpEM) {
1280                                 ItemEMail *emailGrp = nodeGrpEM->data;
1281                                 groupEMail = g_list_remove(groupEMail, emailGrp);
1282                                 nodeGrpEM = g_list_next(nodeGrpEM);
1283                         }
1284
1285                         g_list_free(listRemove);
1286
1287                         /* Move on to next group */
1288                         nodeGrp = g_list_next(nodeGrp);
1289
1290                 }
1291                 /* Clear hash table */
1292                 g_hash_table_foreach_remove(hashEMail, (GHRFunc) 
1293                                             addrbook_free_simple_hash_vis, NULL);
1294                 g_hash_table_destroy(hashEMail);
1295                 hashEMail = NULL;
1296                 g_hash_table_destroy(hashEMailAlias);
1297                 hashEMailAlias = NULL;
1298                 g_list_free(listGroup);
1299                 listGroup = NULL;
1300         }
1301         /* Remove old addresses from person and cache */
1302         listDelete = NULL;
1303         node = person->listEMail;
1304         while (node) {
1305                 ItemEMail *email = node->data;
1306
1307                 if (addrcache_person_remove_email(book->addressCache, person, email))
1308                         addrcache_remove_email(book->addressCache, email);
1309
1310                 listDelete = g_list_append(listDelete, email);
1311                 node = person->listEMail;
1312         }
1313         /* Add new address entries */
1314         node = listEMail;
1315         while (node) {
1316                 ItemEMail *email = node->data;
1317
1318                 if (ADDRITEM_ID(email) == NULL)
1319                         /* Allocate an ID for new address */
1320                         addrcache_id_email(book->addressCache, email);
1321                         
1322                 addrcache_person_add_email( book->addressCache, person, email );
1323                 node = g_list_next( node );
1324         }
1325
1326         addrcache_set_dirty(book->addressCache, TRUE);
1327
1328         /* Free up memory */
1329         g_list_free(listEMail);
1330         listEMail = NULL;
1331
1332         node = listDelete;
1333         while (node) {
1334                 ItemEMail *email = node->data;
1335
1336                 addritem_free_item_email(email);
1337                 node = g_list_next(node);
1338         }
1339         g_list_free(listDelete);
1340         listDelete = NULL;
1341
1342 }
1343
1344 /* Add person and address data to address book.
1345    Enter: book      Address book.
1346           folder    Folder where to add person, or NULL for root folder.
1347           listEMail New list of email addresses.
1348    Return: Person added.
1349    Note: A new person is created with specified list of email addresses. All objects inserted
1350    into address book */
1351 ItemPerson *addrbook_add_address_list(AddressBookFile *book, ItemFolder *folder,
1352                                       GList *listEMail)
1353 {
1354         ItemPerson *person;
1355         ItemFolder *f = folder;
1356         GList *node;
1357
1358         g_return_val_if_fail(book != NULL, NULL);
1359
1360         if (!f) 
1361                 f = book->addressCache->rootFolder;
1362         person = addritem_create_item_person();
1363         addrcache_id_person(book->addressCache, person);
1364         addrcache_folder_add_person(book->addressCache, f, person);
1365
1366         node = listEMail;
1367         while (node) {
1368                 ItemEMail *email = node->data;
1369                 if (ADDRITEM_ID(email) == NULL)
1370                         addrcache_id_email(book->addressCache, email);
1371
1372                 addrcache_person_add_email(book->addressCache, person, email);
1373                 node = g_list_next(node);
1374         }
1375         return person;
1376 }
1377
1378 /* Build available email list visitor function */
1379 static void addrbook_build_avail_email_vis(gpointer key, gpointer value, 
1380                                            gpointer data)
1381 {
1382         AddrItemObject *obj = (AddrItemObject *) value;
1383
1384         if (ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON) {
1385                 AddressBookFile *book = data;
1386                 ItemPerson *person = (ItemPerson *) obj;
1387                 GList *node = person->listEMail;
1388                 while (node) {
1389                         ItemEMail *email = node->data;
1390                         /* gchar *newKey = g_strdup( ADDRITEM_ID(email) ); */
1391
1392                         if (!g_hash_table_lookup(book->tempHash,
1393                                                  ADDRITEM_ID(email)))
1394                                 book->tempList = g_list_append(book->tempList, email);
1395
1396                         node = g_list_next(node);
1397                 }
1398         }
1399 }
1400
1401 /* Return link list of available email items (which have not already been linked to
1402    groups). Note that the list contains references to items and should be g_free()
1403    when done. Do *NOT* attempt to used the addrbook_free_xxx() functions... this will
1404    destroy the addressbook data!
1405    Return: List of items, or NULL if none */
1406 GList *addrbook_get_available_email_list(AddressBookFile *book, ItemGroup *group)
1407 {
1408         GList *list = NULL;
1409         GHashTable *table;
1410
1411         g_return_val_if_fail(book != NULL, NULL);
1412
1413         /* Load hash table with group email entries */
1414         table = g_hash_table_new(g_str_hash, g_str_equal);
1415         if (group) {
1416                 list = group->listEMail;
1417                 while (list) {
1418                         ItemEMail *email = list->data;
1419                         g_hash_table_insert(table, ADDRITEM_ID(email), email);
1420                         list = g_list_next(list);
1421                 }
1422         }
1423
1424         /* Build list of available email addresses which exclude those already in groups */
1425         book->tempList = NULL;
1426         book->tempHash = table;
1427         g_hash_table_foreach(book->addressCache->itemHash, 
1428                              addrbook_build_avail_email_vis, book);
1429         list = book->tempList;
1430         book->tempList = NULL;
1431         book->tempHash = NULL;
1432
1433         /* Clear hash table */
1434         g_hash_table_destroy(table);
1435         table = NULL;
1436
1437         return list;
1438 }
1439
1440 /* Update address book email list for specified group.
1441    Enter: book      Address book.
1442           group     group to update.
1443           listEMail New list of email addresses. This should *NOT* be g_free() when done.
1444   Note: The existing email addresses are replaced with the new addresses. Any references
1445   to old addresses in the groups are re-linked to the new addresses. All old addresses
1446   linked to the person are removed */
1447 void addrbook_update_group_list(AddressBookFile *book, ItemGroup *group, 
1448                                 GList *listEMail)
1449 {
1450         GList *oldData;
1451
1452         g_return_if_fail(book != NULL);
1453         g_return_if_fail(group != NULL);
1454
1455         addrcache_set_dirty(book->addressCache, TRUE);
1456
1457         /* Remember old list */
1458         oldData = group->listEMail;
1459         group->listEMail = listEMail;
1460         mgu_clear_list(oldData);
1461         oldData = NULL;
1462 }
1463
1464 /* Add group and email list to address book.
1465    Enter: book      Address book.
1466           folder    Parent folder, or NULL for root folder.
1467           listEMail New list of email addresses. This should *NOT* be g_free() when done.
1468    Return: Group object.
1469    Note: The existing email addresses are replaced with the new addresses. Any references
1470    to old addresses in the groups are re-linked to the new addresses. All old addresses
1471    linked to the person are removed */
1472 ItemGroup *addrbook_add_group_list(AddressBookFile *book, ItemFolder *folder,
1473                                    GList *listEMail)
1474 {
1475         ItemGroup *group = NULL;
1476         ItemFolder *f = folder;
1477
1478         g_return_val_if_fail(book != NULL, NULL);
1479
1480         if (!f)
1481                 f = book->addressCache->rootFolder;
1482         group = addritem_create_item_group();
1483         addrcache_id_group(book->addressCache, group);
1484         addrcache_folder_add_group(book->addressCache, f, group);
1485         group->listEMail = listEMail;
1486         return group;
1487 }
1488
1489 /* Add new folder to address book.
1490    Enter: book   Address book.
1491           parent        Parent folder.
1492    Return: Folder that was added. This should *NOT* be g_free() when done */
1493 ItemFolder *addrbook_add_new_folder(AddressBookFile *book, ItemFolder *parent)
1494 {
1495         ItemFolder *folder = NULL;
1496         ItemFolder *p = parent;
1497
1498         g_return_val_if_fail(book != NULL, NULL);
1499
1500         if (!p)
1501                 p = book->addressCache->rootFolder;
1502         folder = addritem_create_item_folder();
1503         addrcache_id_folder(book->addressCache, folder);
1504         if (addrcache_hash_add_folder(book->addressCache, folder)) {
1505                 p->listFolder = g_list_append(p->listFolder, folder);
1506                 ADDRITEM_PARENT(folder) = ADDRITEM_OBJECT(p);
1507                 addrcache_set_dirty(book->addressCache, TRUE);
1508         }
1509         else {
1510                 addritem_free_item_folder(folder);
1511                 folder = NULL;
1512         }
1513         return folder;
1514 }
1515
1516 /* Update address book attribute list for specified person.
1517    Enter: book       Address book.
1518           person     Person to update.
1519           listAttrib New list of attributes.
1520    Note: The existing email addresses are replaced with the new addresses. All old attributes
1521    linked to the person are removed */
1522 void addrbook_update_attrib_list(AddressBookFile *book, ItemPerson *person,
1523                                  GList *listAttrib)
1524 {
1525         GList *node;
1526         GList *oldData;
1527
1528         g_return_if_fail(book != NULL);
1529         g_return_if_fail(person != NULL);
1530
1531         /* Remember old list */
1532         oldData = person->listAttrib;
1533
1534         /* Attach new address list to person. */
1535         node = listAttrib;
1536         while (node) {
1537                 UserAttribute *attrib = node->data;
1538                 if (attrib->uid == NULL) {
1539                         /* Allocate an ID */
1540                         addrcache_id_attribute(book->addressCache, attrib);
1541                 }
1542                 node = g_list_next(node);
1543         }
1544         person->listAttrib = listAttrib;
1545         addrcache_set_dirty(book->addressCache, TRUE);
1546
1547         /* Free up old data */
1548         addritem_free_list_attribute(oldData);
1549         oldData = NULL;
1550 }
1551
1552 /*
1553 * Add attribute data for person to address book.
1554 * Enter: book       Address book.
1555 *        person     New person object.
1556 *        listAttrib New list of attributes.
1557 * Note: Only attributes are inserted into address book.
1558 */
1559 void addrbook_add_attrib_list( AddressBookFile *book, ItemPerson *person, GList *listAttrib ) {
1560         GList *node;
1561
1562         g_return_if_fail( book != NULL );
1563         g_return_if_fail( person != NULL );
1564
1565         node = listAttrib;
1566         while( node ) {
1567                 UserAttribute *attrib = node->data;
1568                 if( attrib->uid == NULL ) {
1569                         addrcache_id_attribute( book->addressCache, attrib );
1570                 }
1571                 addritem_person_add_attribute( person, attrib );
1572                 node = g_list_next( node );
1573         }
1574         addrcache_set_dirty( book->addressCache, TRUE );
1575 }
1576
1577 /* Return address book file for specified object.
1578    Enter: aio Book item object.
1579    Return: Address book, or NULL if not found */
1580 AddressBookFile *addrbook_item_get_bookfile(AddrItemObject *aio)
1581 {
1582         AddressBookFile *book = NULL;
1583
1584         if (aio) {
1585                 ItemFolder *parent = NULL;
1586                 ItemFolder *root = NULL;
1587                 if (aio->type == ITEMTYPE_EMAIL) {
1588                         ItemPerson *person = (ItemPerson *)ADDRITEM_PARENT(aio);
1589                         if( person ) 
1590                                 parent = (ItemFolder *)ADDRITEM_PARENT(person);
1591                 }
1592                 else {
1593                         parent = (ItemFolder *)ADDRITEM_PARENT(aio);
1594                 }
1595                 if (parent) 
1596                         root = addrcache_find_root_folder(parent);
1597                 if (root)
1598                         book = (AddressBookFile *)ADDRITEM_PARENT(root);
1599         }
1600         return book;
1601 }
1602
1603 /* Remove folder from address book. Children are re-parented to parent folder.
1604    param: folder Folder to remove.
1605    return: Folder, or NULL if not found. Note that object should still be freed */
1606 ItemFolder *addrbook_remove_folder(AddressBookFile *book, ItemFolder *folder)
1607 {
1608         ItemFolder *f;
1609
1610         g_return_val_if_fail(book != NULL, NULL);
1611
1612         f = addrcache_remove_folder(book->addressCache, folder);
1613         return f;
1614 }
1615
1616 /* Remove folder from address book. Children are deleted.
1617    param: folder Folder to remove.
1618    return: Folder, or NULL if not found. Note that object should still be freed */
1619 ItemFolder *addrbook_remove_folder_delete(AddressBookFile *book, 
1620                                           ItemFolder *folder)
1621 {
1622         ItemFolder *f;
1623
1624         g_return_val_if_fail(book != NULL, NULL);
1625
1626         f = addrcache_remove_folder_delete(book->addressCache, folder);
1627         return f;
1628 }
1629
1630 #define WORK_BUFLEN     1024
1631 #define ADDRBOOK_DIGITS "0123456789"
1632
1633 /* Return list of existing address book files.
1634    Enter: book Address book file.
1635    Return: File list */
1636 GList *addrbook_get_bookfile_list(AddressBookFile *book) {
1637         gchar *adbookdir;
1638         DIR *dp;
1639         struct dirent *entry;
1640         struct stat statbuf;
1641         gchar buf[WORK_BUFLEN];
1642         gchar numbuf[WORK_BUFLEN];
1643         gint len, lenpre, lensuf, lennum;
1644         long int val, maxval;
1645         GList *fileList = NULL;
1646
1647         g_return_val_if_fail(book != NULL, NULL);
1648
1649         if (book->path == NULL || *book->path == '\0') {
1650                 book->retVal = MGU_NO_PATH;
1651                 return NULL;
1652         }
1653
1654         strcpy(buf, book->path);
1655         len = strlen(buf);
1656         if (len > 0) {
1657                 if (buf[len-1] != G_DIR_SEPARATOR) {
1658                         buf[len] = G_DIR_SEPARATOR;
1659                         buf[++len] = '\0';
1660                 }
1661         }
1662
1663         adbookdir = g_strdup(buf);
1664         strcat(buf, ADDRBOOK_PREFIX);
1665
1666         if ((dp = opendir(adbookdir)) == NULL) {
1667                 book->retVal = MGU_OPEN_DIRECTORY;
1668                 g_free(adbookdir);
1669                 return NULL;
1670         }
1671
1672         lenpre = strlen(ADDRBOOK_PREFIX);
1673         lensuf = strlen(ADDRBOOK_SUFFIX);
1674         lennum = FILE_NUMDIGITS + lenpre;
1675         maxval = -1;
1676
1677         while ((entry = readdir(dp)) != NULL) {
1678                 gchar *endptr = NULL;
1679                 gint i;
1680                 gboolean flg;
1681
1682                 strcpy(buf, adbookdir);
1683                 strcat(buf, entry->d_name);
1684                 stat(buf, &statbuf);
1685                 if (S_IFREG & statbuf.st_mode) {
1686                         if (strncmp(entry->d_name, ADDRBOOK_PREFIX, lenpre) == 0) {
1687                                 if (strncmp((entry->d_name) + lennum, ADDRBOOK_SUFFIX, lensuf) == 0) {
1688                                         strncpy(numbuf, (entry->d_name) + lenpre, FILE_NUMDIGITS);
1689                                         numbuf[FILE_NUMDIGITS] = '\0';
1690                                         flg = TRUE;
1691                                         for(i = 0; i < FILE_NUMDIGITS; i++) {
1692                                                 if(!strchr(ADDRBOOK_DIGITS, numbuf[i])) {
1693                                                         flg = FALSE;
1694                                                         break;
1695                                                 }
1696                                         }
1697                                         if (flg) {
1698                                                 /* Get value */
1699                                                 val = strtol(numbuf, &endptr, 10);
1700                                                 if (endptr  && val > -1) {
1701                                                         if (val > maxval) maxval = val;
1702                                                         fileList = g_list_append(fileList, g_strdup(entry->d_name));
1703                                                 }
1704                                         }
1705                                 }
1706                         }
1707                 }
1708         }
1709         closedir(dp);
1710         g_free(adbookdir);
1711
1712         book->maxValue = maxval; 
1713         book->retVal = MGU_SUCCESS;
1714         return fileList;
1715 }
1716
1717 /* Return file name for specified file number.
1718    Enter:  fileNum File number.
1719    Return: File name, or NULL if file number too large. Should be g_free() when done */
1720 gchar *addrbook_gen_new_file_name(gint fileNum) {
1721         gchar fmt[30];
1722         gchar buf[WORK_BUFLEN];
1723         gint n = fileNum;
1724         long int nmax;
1725
1726         if (n < 1) 
1727                 n = 1;
1728         nmax = -1 + (long int) pow(10, FILE_NUMDIGITS);
1729         if (fileNum > nmax)
1730                 return NULL;
1731         g_snprintf(fmt, sizeof(fmt), "%%s%%0%dd%%s", FILE_NUMDIGITS);
1732         g_snprintf(buf, sizeof(buf), fmt, ADDRBOOK_PREFIX, n, ADDRBOOK_SUFFIX);
1733         return g_strdup(buf);
1734 }
1735
1736 /* **********************************************************************
1737    Address book test functions...
1738    **********************************************************************
1739 */
1740
1741 /*
1742 * Test email address list.
1743 */
1744 static void addrbook_chkparse_addr_list( AddressBookFile *book, XMLFile *file ){
1745         guint prev_level;
1746         GList *attr;
1747
1748         for (;;) {
1749                 prev_level = file->level;
1750                 if (xml_parse_next_tag(file))
1751                         longjmp(book->jumper, 1);
1752                 if (file->level < prev_level)
1753                         return;
1754                 attr = xml_get_current_tag_attr(file);
1755                 /* addrbook_show_attribs( attr ); */
1756                 if (xml_compare_tag(file, AB_ELTAG_ADDRESS))
1757                         addrbook_chkparse_addr_list(book, file);
1758         }
1759 }
1760
1761 /* Test user attributes for person */
1762 static void addrbook_chkparse_attribute(AddressBookFile *book, XMLFile *file)
1763 {
1764         GList *attr;
1765         gchar *element;
1766
1767         attr = xml_get_current_tag_attr(file);
1768         /* addrbook_show_attribs( attr ); */
1769         element = xml_get_element(file);
1770         /* printf( "\t\tattrib value : %s\n", element ); */
1771 }
1772
1773 /* Test attribute list */
1774 static void addrbook_chkparse_attr_list(AddressBookFile *book, XMLFile *file)
1775 {
1776         guint prev_level;
1777
1778         for (;;) {
1779                 prev_level = file->level;
1780                 if (xml_parse_next_tag(file))
1781                         longjmp(book->jumper, 1);
1782                 if (file->level < prev_level) 
1783                         return;
1784                 if (xml_compare_tag(file, AB_ELTAG_ATTRIBUTE)) {
1785                         addrbook_chkparse_attribute(book, file);
1786                         addrbook_chkparse_attr_list(book, file);
1787                 }
1788         }
1789 }
1790
1791 /* Test person */
1792 static void addrbook_chkparse_person(AddressBookFile *book, XMLFile *file)
1793 {
1794         GList *attr;
1795
1796         attr = xml_get_current_tag_attr(file);
1797         /* addrbook_show_attribs( attr ); */
1798         if (xml_parse_next_tag(file)) /* Consume closing tag */
1799                 longjmp(book->jumper, 1);
1800
1801         if (xml_compare_tag(file, AB_ELTAG_ADDRESS_LIST))
1802                 addrbook_chkparse_addr_list(book, file);
1803
1804         if (xml_parse_next_tag(file))   /* Consume closing tag */
1805                 longjmp(book->jumper, 1);
1806
1807         if (xml_compare_tag(file, AB_ELTAG_ATTRIBUTE_LIST))
1808                 addrbook_chkparse_attr_list(book, file);
1809 }
1810
1811 /* Test group member list */
1812 static void addrbook_chkparse_member_list(AddressBookFile *book, XMLFile *file)
1813 {
1814         GList *attr;
1815         guint prev_level;
1816
1817         for (;;) {
1818                 prev_level = file->level;
1819                 if (xml_parse_next_tag(file)) 
1820                         longjmp(book->jumper, 1);
1821
1822                 if (file->level < prev_level)
1823                         return;
1824                         
1825                 if (xml_compare_tag(file, AB_ELTAG_MEMBER)) {
1826                         attr = xml_get_current_tag_attr(file);
1827                         /* addrbook_show_attribs( attr ); */
1828                         addrbook_chkparse_member_list(book, file);
1829                 }
1830                 else {
1831                         attr = xml_get_current_tag_attr(file);
1832                         /* addrbook_show_attribs( attr ); */
1833                 }
1834         }
1835 }
1836
1837 /* Test group */
1838 static void addrbook_chkparse_group(AddressBookFile *book, XMLFile *file)
1839 {
1840         GList *attr;
1841
1842         attr = xml_get_current_tag_attr(file);
1843         /* addrbook_show_attribs( attr ); */
1844         if (xml_parse_next_tag(file))   /* Consume closing tag */
1845                 longjmp(book->jumper, 1);
1846
1847         if (xml_compare_tag(file, AB_ELTAG_MEMBER_LIST))
1848                 addrbook_chkparse_member_list(book, file);
1849 }
1850
1851 /* Test folder item list */
1852 static void addrbook_chkparse_folder_list(AddressBookFile *book, XMLFile *file)
1853 {
1854         GList *attr;
1855         guint prev_level;
1856
1857         for (;;) {
1858                 prev_level = file->level;
1859                 if (xml_parse_next_tag(file))
1860                         longjmp(book->jumper, 1);
1861
1862                 if (file->level < prev_level)
1863                         return;
1864                         
1865                 if (xml_compare_tag(file, AB_ELTAG_ITEM)) {
1866                         attr = xml_get_current_tag_attr(file);
1867                         /* addrbook_show_attribs( attr ); */
1868                         addrbook_chkparse_folder_list(book, file);
1869                 }
1870                 else {
1871                         attr = xml_get_current_tag_attr(file);
1872                         /* addrbook_show_attribs( attr ); */
1873                 }
1874         }
1875 }
1876
1877 /* Test folder */
1878 static void addrbook_chkparse_folder(AddressBookFile *book, XMLFile *file)
1879 {
1880         GList *attr;
1881
1882         attr = xml_get_current_tag_attr(file);
1883         /* addrbook_show_attribs( attr ); */
1884         if (xml_parse_next_tag(file))   /* Consume closing tag */
1885                 longjmp(book->jumper, 1);
1886
1887         if (xml_compare_tag(file, AB_ELTAG_ITEM_LIST))
1888                 addrbook_chkparse_folder_list(book, file);
1889 }
1890
1891 /* Test address book */
1892 static gboolean addrbook_chkread_tree(AddressBookFile *book, XMLFile *file)
1893 {
1894         GList *attr;
1895         gboolean retVal;
1896
1897         if (xml_get_dtd(file))
1898                 return FALSE;
1899
1900         if (xml_parse_next_tag(file))
1901                 return FALSE;
1902
1903         if (!xml_compare_tag(file, AB_ELTAG_ADDRESS_BOOK))
1904                 return FALSE;
1905
1906         attr = xml_get_current_tag_attr(file);
1907         /* addrbook_show_attribs( attr ); */
1908
1909         retVal = TRUE;
1910         for (;;) {
1911                 if (!file->level) 
1912                         break;
1913                 /* Get item tag */
1914                 if (xml_parse_next_tag(file))
1915                         longjmp(book->jumper, 1);
1916                         
1917                 /* Get next tag (person, group or folder) */
1918                 if (xml_compare_tag(file, AB_ELTAG_PERSON))
1919                         addrbook_chkparse_person( book, file );
1920                 else if (xml_compare_tag(file, AB_ELTAG_GROUP))
1921                         addrbook_chkparse_group(book, file);
1922                 else if (xml_compare_tag(file, AB_ELTAG_FOLDER))
1923                         addrbook_chkparse_folder(book, file);
1924         }
1925         return retVal;
1926 }
1927
1928 /* Test address book file by parsing contents.
1929    Enter: book     Address book file to check.
1930           fileName File name to check.
1931    Return: MGU_SUCCESS if file appears to be valid format */
1932 gint addrbook_test_read_file(AddressBookFile *book, gchar *fileName)
1933 {
1934         XMLFile *file = NULL;
1935         gchar *fileSpec = NULL;
1936
1937         g_return_val_if_fail(book != NULL, -1);
1938
1939         fileSpec = g_strconcat(book->path, G_DIR_SEPARATOR_S, fileName, NULL);
1940         book->retVal = MGU_OPEN_FILE;
1941         file = xml_open_file(fileSpec);
1942         g_free(fileSpec);
1943         if (file) {
1944                 book->retVal = MGU_BAD_FORMAT;
1945                 if (setjmp(book->jumper)) {
1946                         /* printf( "Caught Ya!!!\n" ); */
1947                         xml_close_file(file);
1948                         return book->retVal;
1949                 }
1950                 if (addrbook_chkread_tree(book, file))
1951                         book->retVal = MGU_SUCCESS;
1952
1953                 xml_close_file( file );
1954         }
1955         return book->retVal;
1956 }
1957
1958 /* Return link list of all persons in address book.  Note that the list contains
1959    references to items. Do *NOT* attempt to use the addrbook_free_xxx() functions...
1960    this will destroy the addressbook data!
1961    Return: List of items, or NULL if none */
1962 GList *addrbook_get_all_persons(AddressBookFile *book)
1963 {
1964         g_return_val_if_fail(book != NULL, NULL);
1965         return addrcache_get_all_persons(book->addressCache);
1966 }
1967
1968 /* Add person and address data to address book.
1969    Enter: book      Address book.
1970           folder    Folder where to add person, or NULL for root folder.
1971           name      Common name.
1972           address   EMail address.
1973           remarks   Remarks.
1974    Return: Person added. Do not *NOT* to use the addrbook_free_xxx() functions...
1975    this will destroy the address book data */
1976 ItemPerson *addrbook_add_contact(AddressBookFile *book, ItemFolder *folder, 
1977                                  const gchar *name,const gchar *address, 
1978                                  const gchar *remarks)
1979 {
1980         g_return_val_if_fail(book != NULL, NULL);
1981         return addrcache_add_contact(book->addressCache, folder, name, address, 
1982                                      remarks);
1983 }
1984
1985 /* Return file name for next address book file.
1986    Enter:  book Address book.
1987    Return: File name, or NULL if could not create. This should be g_free()
1988            when done */
1989 gchar *addrbook_guess_next_file(AddressBookFile *book)
1990 {
1991         gchar *newFile = NULL;
1992         GList *fileList = NULL;
1993         gint fileNum = 1;
1994         fileList = addrbook_get_bookfile_list(book);
1995         if (fileList)
1996                 fileNum = 1 + book->maxValue;
1997         
1998         newFile = addrbook_gen_new_file_name(fileNum);
1999         g_list_free(fileList);
2000         fileList = NULL;
2001         return newFile;
2002 }