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