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