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