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