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