2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2001 Match Grun
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.
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.
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.
21 * General functions for accessing external address book files.
34 #include "addrcache.h"
37 #ifndef DEV_STANDALONE
41 #define ADDRBOOK_MAX_SEARCH_COUNT 1000
42 #define ADDRBOOK_PREFIX "addrbook-"
43 #define ADDRBOOK_SUFFIX ".xml"
44 #define FILE_NUMDIGITS 6
46 #define ID_TIME_OFFSET 998000000
48 * Create new address book.
50 AddressBookFile *addrbook_create_book() {
51 AddressBookFile *book;
53 book = g_new0( AddressBookFile, 1 );
56 book->fileName = NULL;
57 book->retVal = MGU_SUCCESS;
58 book->addressCache = addrcache_create();
60 book->tempList = NULL;
61 book->readFlag = FALSE;
62 book->dirtyFlag = FALSE;
63 book->modifyFlag = TRUE;
64 book->accessFlag = FALSE;
65 book->tempHash = NULL;
70 * Specify name to be used.
72 void addrbook_set_name( AddressBookFile *book, const gchar *value ) {
73 g_return_if_fail( book != NULL );
74 book->name = mgu_replace_string( book->name, value );
76 void addrbook_set_path( AddressBookFile *book, const gchar *value ) {
77 g_return_if_fail( book != NULL );
78 book->path = mgu_replace_string( book->path, value );
79 book->dirtyFlag = TRUE;
81 void addrbook_set_file( AddressBookFile *book, const gchar *value ) {
82 g_return_if_fail( book != NULL );
83 book->fileName = mgu_replace_string( book->fileName, value );
84 book->dirtyFlag = TRUE;
86 void addrbook_set_accessed( AddressBookFile *book, const gboolean value ) {
87 g_return_if_fail( book != NULL );
88 book->accessFlag = value;
90 gboolean addrbook_get_modified( AddressBookFile *book ) {
91 g_return_if_fail( book != NULL );
92 return book->modifyFlag;
94 gboolean addrbook_get_accessed( AddressBookFile *book ) {
95 g_return_if_fail( book != NULL );
96 return book->accessFlag;
98 gboolean addrbook_get_read_flag( AddressBookFile *book ) {
99 g_return_if_fail( book != NULL );
100 return book->readFlag;
102 gint addrbook_get_status( AddressBookFile *book ) {
103 g_return_if_fail( book != NULL );
106 ItemFolder *addrbook_get_root_folder( AddressBookFile *book ) {
107 g_return_if_fail( book != NULL );
108 return addrcache_get_root_folder( book->addressCache );
110 GList *addrbook_get_list_folder( AddressBookFile *book ) {
111 g_return_if_fail( book != NULL );
112 return addrcache_get_list_folder( book->addressCache );
114 GList *addrbook_get_list_person( AddressBookFile *book ) {
115 g_return_if_fail( book != NULL );
116 return addrcache_get_list_person( book->addressCache );
118 gchar *addrbook_get_name( AddressBookFile *book ) {
119 g_return_if_fail( book != NULL );
123 static gint addrcache_free_item_vis( gpointer key, gpointer value, gpointer data ) {
124 AddrItemObject *obj = ( AddrItemObject * ) value;
125 if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) {
126 addritem_free_item_person( ( ItemPerson * ) obj );
128 else if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) {
129 addritem_free_item_group( ( ItemGroup * ) obj );
131 else if( ADDRITEM_TYPE(obj) == ITEMTYPE_FOLDER ) {
132 addritem_free_item_folder( ( ItemFolder * ) obj );
140 * Free hash table of address book items.
142 static void addrcache_free_item_hash( GHashTable *table ) {
143 g_return_if_fail( table != NULL );
144 g_hash_table_freeze( table );
145 g_hash_table_foreach_remove( table, addrcache_free_item_vis, NULL );
146 g_hash_table_thaw( table );
147 g_hash_table_destroy( table );
151 * Empty address book.
153 void addrbook_empty_book( AddressBookFile *book ) {
154 g_return_if_fail( book != NULL );
156 // Free up folders and hash table
157 addrcache_clear( book->addressCache );
159 g_list_free( book->tempList );
160 book->tempList = NULL;
162 // Reset to initial state
163 book->retVal = MGU_SUCCESS;
164 book->tempHash = NULL;
165 book->readFlag = FALSE;
166 book->dirtyFlag = FALSE;
167 book->modifyFlag = FALSE;
168 book->accessFlag = FALSE;
174 void addrbook_free_book( AddressBookFile *book ) {
175 g_return_if_fail( book != NULL );
177 g_free( book->name );
178 g_free( book->path );
179 g_free( book->fileName );
182 book->fileName = NULL;
184 // Free up folders and hash table
185 addrcache_free( book->addressCache );
186 book->addressCache = NULL;
188 g_list_free( book->tempList );
189 book->tempList = NULL;
191 book->retVal = MGU_SUCCESS;
192 book->tempHash = NULL;
193 book->readFlag = FALSE;
194 book->dirtyFlag = FALSE;
195 book->modifyFlag = FALSE;
196 book->accessFlag = FALSE;
202 * Print list of items.
204 void addrbook_print_item_list( GList *list, FILE *stream ) {
207 AddrItemObject *obj = node->data;
208 if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) {
209 addritem_print_item_person( ( ItemPerson * ) obj, stream );
211 else if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) {
212 addritem_print_item_group( ( ItemGroup * ) obj, stream );
214 else if( ADDRITEM_TYPE(obj) == ITEMTYPE_FOLDER ) {
215 addritem_print_item_folder( ( ItemFolder * ) obj, stream );
217 node = g_list_next( node );
219 fprintf( stream, "\t---\n" );
223 * Print address book.
225 void addrbook_print_book( AddressBookFile *book, FILE *stream ) {
226 g_return_if_fail( book != NULL );
227 fprintf( stream, "AddressBook:\n" );
228 fprintf( stream, "\tname : '%s'\n", book->name );
229 fprintf( stream, "\tpath : '%s'\n", book->path );
230 fprintf( stream, "\tfile : '%s'\n", book->fileName );
231 fprintf( stream, "\tstatus: %d : '%s'\n", book->retVal, mgu_error2string( book->retVal ) );
232 addrcache_print( book->addressCache, stream );
236 * Dump entire address book traversing folders.
238 void addrbook_dump_book( AddressBookFile *book, FILE *stream ) {
241 g_return_if_fail( book != NULL );
242 addrbook_print_book( book, stream );
243 folder = book->addressCache->rootFolder;
244 addritem_print_item_folder( folder, stream );
248 * Remove group from address book.
249 * param: group Group to remove.
250 * return: Group, or NULL if not found. Note that object should still be freed.
252 ItemGroup *addrbook_remove_group( AddressBookFile *book, ItemGroup *group ) {
254 g_return_if_fail( book != NULL );
255 item = addrcache_remove_group( book->addressCache, group );
256 if( item ) book->dirtyFlag = TRUE;
261 * Remove specified person from address book.
262 * param: person Person to remove.
263 * return: Person, or NULL if not found. Note that object should still be freed.
265 ItemPerson *addrbook_remove_person( AddressBookFile *book, ItemPerson *person ) {
267 g_return_if_fail( book != NULL );
268 item = addrcache_remove_person( book->addressCache, person );
269 if( item ) book->dirtyFlag = TRUE;
274 * Remove email address in address book for specified person.
275 * param: person Person.
276 * email EMail to remove.
277 * return: EMail object, or NULL if not found. Note that object should still be freed.
279 ItemEMail *addrbook_person_remove_email( AddressBookFile *book, ItemPerson *person, ItemEMail *email ) {
281 g_return_if_fail( book != NULL );
282 item = addrcache_person_remove_email( book->addressCache, person, email );
283 if( item ); book->dirtyFlag = TRUE;
287 /* **********************************************************************
288 * Read/Write XML data file...
289 * ===========================
291 * 1) The address book is structured as follows:
306 * 2) This sequence of elements was chosen so that the most important
307 * elements (person and their email addresses) appear first.
309 * 3) Groups then appear. When groups are loaded, person's email
310 * addresses have already been loaded and can be found.
312 * 4) Finally folders are loaded. Any forward and backward references
313 * to folders, groups and persons in the folders are resolved after
316 * ***********************************************************************
320 #define AB_ELTAG_ADDRESS "address"
321 #define AB_ELTAG_ATTRIBUTE "attribute"
322 #define AB_ELTAG_ATTRIBUTE_LIST "attribute-list"
323 #define AB_ELTAG_ADDRESS_LIST "address-list"
324 #define AB_ELTAG_MEMBER "member"
325 #define AB_ELTAG_MEMBER_LIST "member-list"
326 #define AB_ELTAG_ITEM "item"
327 #define AB_ELTAG_ITEM_LIST "item-list"
328 #define AB_ELTAG_ADDRESS_BOOK "address-book"
329 #define AB_ELTAG_PERSON "person"
330 #define AB_ELTAG_GROUP "group"
331 #define AB_ELTAG_FOLDER "folder"
333 // Attribute tag names
334 #define AB_ATTAG_TYPE "type"
335 #define AB_ATTAG_UID "uid"
336 #define AB_ATTAG_NAME "name"
337 #define AB_ATTAG_REMARKS "remarks"
338 #define AB_ATTAG_FIRST_NAME "first-name"
339 #define AB_ATTAG_LAST_NAME "last-name"
340 #define AB_ATTAG_NICK_NAME "nick-name"
341 #define AB_ATTAG_COMMON_NAME "cn"
342 #define AB_ATTAG_ALIAS "alias"
343 #define AB_ATTAG_EMAIL "email"
344 #define AB_ATTAG_EID "eid"
345 #define AB_ATTAG_PID "pid"
348 #define AB_ATTAG_VAL_PERSON "person"
349 #define AB_ATTAG_VAL_GROUP "group"
350 #define AB_ATTAG_VAL_FOLDER "folder"
353 * Parse address item for person.
355 static void addrbook_parse_address( AddressBookFile *book, XMLFile *file, ItemPerson *person ) {
358 ItemEMail *email = NULL;
360 attr = xml_get_current_tag_attr(file);
362 name = ((XMLAttr *)attr->data)->name;
363 value = ((XMLAttr *)attr->data)->value;
364 if( ! email ) email = addritem_create_item_email();
365 if( strcmp( name, AB_ATTAG_UID ) == 0 ) {
366 ADDRITEM_ID(email) = g_strdup( value );
368 else if( strcmp( name, AB_ATTAG_ALIAS ) == 0 ) {
369 ADDRITEM_NAME(email) = g_strdup( value );
371 else if( strcmp( name, AB_ATTAG_EMAIL ) == 0 ) {
372 email->address = g_strdup( value );
374 else if( strcmp( name, AB_ATTAG_REMARKS ) == 0 ) {
375 email->remarks = g_strdup( value );
377 attr = g_list_next( attr );
381 addrcache_person_add_email( book->addressCache, person, email );
384 addritem_free_item_email( email );
391 * Parse email address list.
393 static void addrbook_parse_addr_list( AddressBookFile *book, XMLFile *file, ItemPerson *person ){
398 prev_level = file->level;
399 if( xml_parse_next_tag( file ) ) {
400 longjmp( book->jumper, 1 );
402 if (file->level < prev_level) return;
403 if( xml_compare_tag( file, AB_ELTAG_ADDRESS ) ) {
404 attr = xml_get_current_tag_attr(file);
405 addrbook_parse_address( book, file, person );
406 addrbook_parse_addr_list( book, file, person );
412 * Parse attibute for person.
414 static void addrbook_parse_attribute( XMLFile *file, ItemPerson *person ) {
418 UserAttribute *uAttr = NULL;
420 attr = xml_get_current_tag_attr(file);
422 name = ((XMLAttr *)attr->data)->name;
423 value = ((XMLAttr *)attr->data)->value;
424 if( ! uAttr ) uAttr = addritem_create_attribute();
425 if( strcmp( name, AB_ATTAG_UID ) == 0 ) {
426 addritem_attrib_set_id( uAttr, value );
428 else if( strcmp( name, AB_ATTAG_NAME ) == 0 ) {
429 addritem_attrib_set_name( uAttr, value );
431 attr = g_list_next( attr );
434 element = xml_get_element( file );
435 addritem_attrib_set_value( uAttr, element );
439 addritem_person_add_attribute( person, uAttr );
442 addritem_free_attribute( uAttr );
449 * Parse attribute list.
451 static void addrbook_parse_attr_list( AddressBookFile *book, XMLFile *file, ItemPerson *person ){
456 prev_level = file->level;
457 if( xml_parse_next_tag( file ) ) {
458 longjmp( book->jumper, 1 );
460 if (file->level < prev_level) return;
461 if( xml_compare_tag( file, AB_ELTAG_ATTRIBUTE ) ) {
462 attr = xml_get_current_tag_attr(file);
463 addrbook_parse_attribute( file, person );
464 addrbook_parse_attr_list( book, file, person );
472 static void addrbook_parse_person( AddressBookFile *book, XMLFile *file ) {
475 ItemPerson *person = NULL;
477 attr = xml_get_current_tag_attr(file);
479 name = ((XMLAttr *)attr->data)->name;
480 value = ((XMLAttr *)attr->data)->value;
481 if( ! person ) person = addritem_create_item_person();
482 if( strcmp( name, AB_ATTAG_UID ) == 0 ) {
483 ADDRITEM_ID(person) = g_strdup( value );
485 else if( strcmp( name, AB_ATTAG_FIRST_NAME ) == 0 ) {
486 person->firstName = g_strdup( value );
488 else if( strcmp( name, AB_ATTAG_LAST_NAME ) == 0 ) {
489 person->lastName = g_strdup( value );
491 else if( strcmp( name, AB_ATTAG_NICK_NAME ) == 0 ) {
492 person->nickName = g_strdup( value );
494 else if( strcmp( name, AB_ATTAG_COMMON_NAME ) == 0 ) {
495 ADDRITEM_NAME(person) = g_strdup( value );
497 attr = g_list_next( attr );
499 if( xml_parse_next_tag( file ) ) { // Consume closing tag
500 longjmp( book->jumper, 1 );
502 if( xml_compare_tag( file, AB_ELTAG_ADDRESS_LIST ) ) {
503 addrbook_parse_addr_list( book, file, person );
505 addrcache_hash_add_person( book->addressCache, person );
508 if( xml_parse_next_tag( file ) ) { // Consume closing tag
509 longjmp( book->jumper, 1 );
511 if( xml_compare_tag( file, AB_ELTAG_ATTRIBUTE_LIST ) ) {
512 addrbook_parse_attr_list( book, file, person );
517 * Parse group member.
519 static void addrbook_parse_member( AddressBookFile *book, XMLFile *file, ItemGroup *group ) {
522 gchar *pid = NULL, *eid = NULL;
523 ItemEMail *email = NULL;
525 attr = xml_get_current_tag_attr(file);
527 name = ((XMLAttr *)attr->data)->name;
528 value = ((XMLAttr *)attr->data)->value;
529 if( strcmp( name, AB_ATTAG_PID ) == 0 ) {
530 pid = g_strdup( value );
532 else if( strcmp( name, AB_ATTAG_EID ) == 0 ) {
533 eid = g_strdup( value );
535 attr = g_list_next( attr );
537 email = addrcache_get_email( book->addressCache, pid, eid );
540 addrcache_group_add_email( book->addressCache, group, email );
543 addritem_free_item_email( email );
550 * Parse group member list.
552 static void addrbook_parse_member_list( AddressBookFile *book, XMLFile *file, ItemGroup *group ){
558 prev_level = file->level;
559 if( xml_parse_next_tag( file ) ) {
560 longjmp( book->jumper, 1 );
562 if (file->level < prev_level) return;
563 if( xml_compare_tag( file, AB_ELTAG_MEMBER ) ) {
564 attr = xml_get_current_tag_attr(file);
565 addrbook_parse_member( book, file, group );
566 addrbook_parse_member_list( book, file, group );
569 attr = xml_get_current_tag_attr( file );
577 static void addrbook_parse_group( AddressBookFile *book, XMLFile *file ) {
580 ItemGroup *group = NULL;
582 attr = xml_get_current_tag_attr(file);
584 name = ((XMLAttr *)attr->data)->name;
585 value = ((XMLAttr *)attr->data)->value;
586 if( ! group ) group = addritem_create_item_group();
587 if( strcmp( name, AB_ATTAG_UID ) == 0 ) {
588 ADDRITEM_ID(group) = g_strdup( value );
590 else if( strcmp( name, AB_ATTAG_NAME ) == 0 ) {
591 ADDRITEM_NAME(group) = g_strdup( value );
593 else if( strcmp( name, AB_ATTAG_REMARKS ) == 0 ) {
594 group->remarks = g_strdup( value );
596 attr = g_list_next( attr );
598 if( xml_parse_next_tag( file ) ) { // Consume closing tag
599 longjmp( book->jumper, 1 );
601 if( xml_compare_tag( file, AB_ELTAG_MEMBER_LIST ) ) {
603 addrcache_hash_add_group( book->addressCache, group );
605 addrbook_parse_member_list( book, file, group );
612 static void addrbook_parse_folder_item( AddressBookFile *book, XMLFile *file, ItemFolder *folder ) {
617 attr = xml_get_current_tag_attr(file);
619 name = ((XMLAttr *)attr->data)->name;
620 value = ((XMLAttr *)attr->data)->value;
621 if( strcmp( name, AB_ATTAG_UID ) == 0 ) {
622 uid = g_strdup( value );
624 attr = g_list_next( attr );
628 folder->listItems = g_list_append( folder->listItems, uid );
634 * Parse folder item list.
636 static void addrbook_parse_folder_list( AddressBookFile *book, XMLFile *file, ItemFolder *folder ){
642 prev_level = file->level;
643 if( xml_parse_next_tag( file ) ) {
644 longjmp( book->jumper, 1 );
646 if (file->level < prev_level) return;
647 if( xml_compare_tag( file, AB_ELTAG_ITEM ) ) {
648 attr = xml_get_current_tag_attr(file);
649 addrbook_parse_folder_item( book, file, folder );
650 addrbook_parse_folder_list( book, file, folder );
653 attr = xml_get_current_tag_attr( file );
661 static void addrbook_parse_folder( AddressBookFile *book, XMLFile *file ) {
664 ItemFolder *folder = NULL;
666 attr = xml_get_current_tag_attr(file);
668 name = ((XMLAttr *)attr->data)->name;
669 value = ((XMLAttr *)attr->data)->value;
671 folder = addritem_create_item_folder();
673 if( strcmp( name, AB_ATTAG_UID ) == 0 ) {
674 ADDRITEM_ID(folder) = g_strdup( value );
676 else if( strcmp( name, AB_ATTAG_NAME ) == 0 ) {
677 ADDRITEM_NAME(folder) = g_strdup( value );
679 else if( strcmp( name, AB_ATTAG_REMARKS ) == 0 ) {
680 folder->remarks = g_strdup( value );
682 attr = g_list_next( attr );
684 if( xml_parse_next_tag( file ) ) { // Consume closing tag
685 longjmp( book->jumper, 1 );
687 if( xml_compare_tag( file, AB_ELTAG_ITEM_LIST ) ) {
689 if( addrcache_hash_add_folder( book->addressCache, folder ) ) {
690 book->tempList = g_list_append( book->tempList, folder );
691 ADDRITEM_PARENT(folder) = NULL; // We will resolve folder later
694 addrbook_parse_folder_list( book, file, folder );
699 * Parse address book.
700 * Return: TRUE if data read successfully, FALSE if error reading data.
702 static gboolean addrbook_read_tree( AddressBookFile *book, XMLFile *file ) {
707 book->retVal = MGU_BAD_FORMAT;
708 if( xml_get_dtd( file ) ) {
711 if( xml_parse_next_tag( file ) ) {
712 longjmp( book->jumper, 1 );
714 if( ! xml_compare_tag( file, AB_ELTAG_ADDRESS_BOOK ) ) {
718 attr = xml_get_current_tag_attr(file);
720 name = ((XMLAttr *)attr->data)->name;
721 value = ((XMLAttr *)attr->data)->value;
722 if( strcmp( name, AB_ATTAG_NAME ) == 0 ) {
723 addrbook_set_name( book, value );
725 attr = g_list_next( attr );
730 if (! file->level ) break;
731 // Get next item tag (person, group or folder)
732 if( xml_parse_next_tag( file ) ) {
733 longjmp( book->jumper, 1 );
735 if( xml_compare_tag( file, AB_ELTAG_PERSON ) ) {
736 addrbook_parse_person( book, file );
738 else if( xml_compare_tag( file, AB_ELTAG_GROUP ) ) {
739 addrbook_parse_group( book, file );
741 else if( xml_compare_tag( file, AB_ELTAG_FOLDER ) ) {
742 addrbook_parse_folder( book, file );
745 if( retVal ) book->retVal = MGU_SUCCESS;
750 * Resolve folder items visitor function.
752 static void addrbook_res_items_vis( gpointer key, gpointer value, gpointer data ) {
753 AddressBookFile *book = data;
754 AddrItemObject *obj = ( AddrItemObject * ) value;
755 ItemFolder *rootFolder = book->addressCache->rootFolder;
756 if( obj->parent == NULL ) {
757 if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) {
758 rootFolder->listPerson = g_list_append( rootFolder->listPerson, obj );
759 ADDRITEM_PARENT(obj) = ADDRITEM_OBJECT(rootFolder);
761 else if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) {
762 rootFolder->listGroup = g_list_append( rootFolder->listGroup, obj );
763 ADDRITEM_PARENT(obj) = ADDRITEM_OBJECT(rootFolder);
769 * Resolve folder items. Lists of UID's are replaced with pointers to data items.
771 static void addrbook_resolve_folder_items( AddressBookFile *book ) {
772 GList *nodeFolder = NULL;
773 GList *listRemove = NULL;
775 ItemFolder *rootFolder = book->addressCache->rootFolder;
776 nodeFolder = book->tempList;
777 while( nodeFolder ) {
778 ItemFolder *folder = nodeFolder->data;
780 node = folder->listItems;
782 gchar *uid = node->data;
783 AddrItemObject *aio = addrcache_get_object( book->addressCache, uid );
785 if( aio->type == ITEMTYPE_FOLDER ) {
786 ItemFolder *item = ( ItemFolder * ) aio;
787 folder->listFolder = g_list_append( folder->listFolder, item );
788 ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder);
789 addrcache_hash_add_folder( book->addressCache, folder );
791 else if( aio->type == ITEMTYPE_PERSON ) {
792 ItemPerson *item = ( ItemPerson * ) aio;
793 folder->listPerson = g_list_append( folder->listPerson, item );
794 ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder);
796 else if( aio->type == ITEMTYPE_GROUP ) {
797 ItemGroup *item = ( ItemGroup * ) aio;
798 folder->listGroup = g_list_append( folder->listGroup, item );
799 ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder);
801 // Replace data with pointer to item
806 // Not found, append to remove list.
807 listRemove = g_list_append( listRemove, uid );
809 node = g_list_next( node );
811 rootFolder->listFolder = g_list_append( rootFolder->listFolder, folder );
813 // Process remove list
816 gchar *uid = node->data;
817 folder->listItems = g_list_remove( folder->listItems, uid );
819 node = g_list_next( node );
821 g_list_free( listRemove );
822 nodeFolder = g_list_next( nodeFolder );
825 // Remove folders with parents.
827 node = rootFolder->listFolder;
829 ItemFolder *folder = ( ItemFolder * ) node->data;
830 if( ADDRITEM_PARENT(folder) ) {
831 // Remove folders with parents
832 listRemove = g_list_append( listRemove, folder );
835 // Add to root folder
836 ADDRITEM_PARENT(folder) = ADDRITEM_OBJECT(book->addressCache->rootFolder);
838 node = g_list_next( node );
841 // Process remove list
844 rootFolder->listFolder = g_list_remove( rootFolder->listFolder, node->data );
845 node = g_list_next( node );
847 g_list_free( listRemove );
849 // Move all unparented persons and groups into root folder
850 g_hash_table_foreach( book->addressCache->itemHash, addrbook_res_items_vis, book );
853 nodeFolder = book->tempList;
854 while( nodeFolder ) {
855 ItemFolder *folder = nodeFolder->data;
856 g_list_free( folder->listItems );
857 folder->listItems = NULL;
858 nodeFolder = g_list_next( nodeFolder );
860 g_list_free( book->tempList );
861 book->tempList = NULL;
866 * Read address book file.
868 gint addrbook_read_data( AddressBookFile *book ) {
869 XMLFile *file = NULL;
870 gchar *fileSpec = NULL;
871 g_return_if_fail( book != NULL );
873 fileSpec = g_strconcat( book->path, G_DIR_SEPARATOR_S, book->fileName, NULL );
874 book->retVal = MGU_OPEN_FILE;
875 book->accessFlag = FALSE;
876 book->modifyFlag = FALSE;
877 file = xml_open_file( fileSpec );
880 book->tempList = NULL;
882 // Trap for parsing errors.
883 if( setjmp( book->jumper ) ) {
884 xml_close_file( file );
887 addrbook_read_tree( book, file );
888 xml_close_file( file );
890 // Resolve folder items
891 addrbook_resolve_folder_items( book );
892 book->tempList = NULL;
893 book->readFlag = TRUE;
894 book->dirtyFlag = FALSE;
899 static void addrbook_write_elem_s( FILE *fp, gint lvl, gchar *name ) {
901 for( i = 0; i < lvl; i++ ) fputs( " ", fp );
906 static void addrbook_write_elem_e( FILE *fp, gint lvl, gchar *name ) {
908 for( i = 0; i < lvl; i++ ) fputs( " ", fp );
914 static void addrbook_write_attr( FILE *fp, gchar *name, gchar *value ) {
918 xml_file_put_escape_str( fp, value );
923 * Write file hash table visitor function.
925 static void addrbook_write_item_person_vis( gpointer key, gpointer value, gpointer data ) {
926 AddrItemObject *obj = ( AddrItemObject * ) value;
927 FILE *fp = ( FILE * ) data;
930 if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) {
931 ItemPerson *person = ( ItemPerson * ) value;
933 addrbook_write_elem_s( fp, 1, AB_ELTAG_PERSON );
934 addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(person) );
935 addrbook_write_attr( fp, AB_ATTAG_FIRST_NAME, person->firstName );
936 addrbook_write_attr( fp, AB_ATTAG_LAST_NAME, person->lastName );
937 addrbook_write_attr( fp, AB_ATTAG_NICK_NAME, person->nickName );
938 addrbook_write_attr( fp, AB_ATTAG_COMMON_NAME, ADDRITEM_NAME(person) );
941 // Output email addresses
942 addrbook_write_elem_s( fp, 2, AB_ELTAG_ADDRESS_LIST );
944 node = person->listEMail;
946 ItemEMail *email = node->data;
947 addrbook_write_elem_s( fp, 3, AB_ELTAG_ADDRESS );
948 addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(email) );
949 addrbook_write_attr( fp, AB_ATTAG_ALIAS, ADDRITEM_NAME(email) );
950 addrbook_write_attr( fp, AB_ATTAG_EMAIL, email->address );
951 addrbook_write_attr( fp, AB_ATTAG_REMARKS, email->remarks );
953 node = g_list_next( node );
955 addrbook_write_elem_e( fp, 2, AB_ELTAG_ADDRESS_LIST );
957 // Output user attributes
958 addrbook_write_elem_s( fp, 2, AB_ELTAG_ATTRIBUTE_LIST );
960 node = person->listAttrib;
962 UserAttribute *attrib = node->data;
963 addrbook_write_elem_s( fp, 3, AB_ELTAG_ATTRIBUTE );
964 addrbook_write_attr( fp, AB_ATTAG_UID, attrib->uid );
965 addrbook_write_attr( fp, AB_ATTAG_NAME, attrib->name );
967 xml_file_put_escape_str( fp, attrib->value );
968 addrbook_write_elem_e( fp, 0, AB_ELTAG_ATTRIBUTE );
969 node = g_list_next( node );
971 addrbook_write_elem_e( fp, 2, AB_ELTAG_ATTRIBUTE_LIST );
972 addrbook_write_elem_e( fp, 1, AB_ELTAG_PERSON );
978 * Output all groups in folder.
980 static void addrbook_write_folder_group( ItemFolder *parent, FILE *fp ) {
981 GList *nodeGrp = parent->listGroup;
984 ItemGroup *group = nodeGrp->data;
986 addrbook_write_elem_s( fp, 1, AB_ELTAG_GROUP );
987 addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(group) );
988 addrbook_write_attr( fp, AB_ATTAG_NAME, ADDRITEM_NAME(group) );
989 addrbook_write_attr( fp, AB_ATTAG_REMARKS, group->remarks );
992 // Output email address links
993 addrbook_write_elem_s( fp, 2, AB_ELTAG_MEMBER_LIST );
995 node = group->listEMail;
997 ItemEMail *email = node->data;
998 ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(email);
999 addrbook_write_elem_s( fp, 3, AB_ELTAG_MEMBER );
1000 addrbook_write_attr( fp, AB_ATTAG_PID, ADDRITEM_ID(person) );
1001 addrbook_write_attr( fp, AB_ATTAG_EID, ADDRITEM_ID(email) );
1002 fputs( " />\n", fp );
1003 node = g_list_next( node );
1005 addrbook_write_elem_e( fp, 2, AB_ELTAG_MEMBER_LIST );
1006 addrbook_write_elem_e( fp, 1, AB_ELTAG_GROUP );
1008 nodeGrp = g_list_next( nodeGrp );
1010 node = parent->listFolder;
1012 ItemFolder *folder = node->data;
1013 addrbook_write_folder_group( folder, fp );
1014 node = g_list_next( node );
1019 * Write file hash table visitor function.
1021 static void addrbook_write_item_group_vis( gpointer key, gpointer value, gpointer data ) {
1022 AddrItemObject *obj = ( AddrItemObject * ) value;
1023 FILE *fp = ( FILE * ) data;
1026 if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) {
1027 ItemGroup *group = ( ItemGroup * ) value;
1029 addrbook_write_elem_s( fp, 1, AB_ELTAG_GROUP );
1030 addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(group) );
1031 addrbook_write_attr( fp, AB_ATTAG_NAME, ADDRITEM_NAME(group) );
1032 addrbook_write_attr( fp, AB_ATTAG_REMARKS, group->remarks );
1033 fputs( " >\n", fp );
1035 // Output email address links
1036 addrbook_write_elem_s( fp, 2, AB_ELTAG_MEMBER_LIST );
1038 node = group->listEMail;
1040 ItemEMail *email = node->data;
1041 ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(email);
1042 addrbook_write_elem_s( fp, 3, AB_ELTAG_MEMBER );
1043 addrbook_write_attr( fp, AB_ATTAG_PID, ADDRITEM_ID(person) );
1044 addrbook_write_attr( fp, AB_ATTAG_EID, ADDRITEM_ID(email) );
1045 fputs( " />\n", fp );
1046 node = g_list_next( node );
1048 addrbook_write_elem_e( fp, 2, AB_ELTAG_MEMBER_LIST );
1049 addrbook_write_elem_e( fp, 1, AB_ELTAG_GROUP );
1055 * Output all folders in folder.
1057 static void addrbook_write_folder_folder( ItemFolder *parent, FILE *fp ) {
1058 GList *nodeFold = parent->listFolder;
1061 ItemFolder *folder = nodeFold->data;
1062 addrbook_write_folder_folder( folder, fp );
1064 addrbook_write_elem_s( fp, 1, AB_ELTAG_FOLDER );
1065 addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(folder) );
1066 addrbook_write_attr( fp, AB_ATTAG_NAME, ADDRITEM_NAME(folder) );
1067 addrbook_write_attr( fp, AB_ATTAG_REMARKS, folder->remarks );
1068 fputs( " >\n", fp );
1069 addrbook_write_elem_s( fp, 2, AB_ELTAG_ITEM_LIST );
1073 node = folder->listPerson;
1075 ItemPerson *item = node->data;
1076 addrbook_write_elem_s( fp, 2, AB_ELTAG_ITEM );
1077 addrbook_write_attr( fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_PERSON );
1078 addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(item ) );
1079 fputs( " />\n", fp );
1080 node = g_list_next( node );
1084 node = folder->listGroup;
1086 ItemGroup *item = node->data;
1087 addrbook_write_elem_s( fp, 2, AB_ELTAG_ITEM );
1088 addrbook_write_attr( fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_GROUP );
1089 addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(item ) );
1090 fputs( " />\n", fp );
1091 node = g_list_next( node );
1095 node = folder->listFolder;
1097 ItemFolder *item = node->data;
1098 addrbook_write_elem_s( fp, 2, AB_ELTAG_ITEM );
1099 addrbook_write_attr( fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_FOLDER );
1100 addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(item ) );
1101 fputs( " />\n", fp );
1102 node = g_list_next( node );
1104 addrbook_write_elem_e( fp, 2, AB_ELTAG_ITEM_LIST );
1105 addrbook_write_elem_e( fp, 1, AB_ELTAG_FOLDER );
1107 nodeFold = g_list_next( nodeFold );
1112 * Write file hash table visitor function.
1114 static void addrbook_write_item_folder_vis( gpointer key, gpointer value, gpointer data ) {
1115 AddrItemObject *obj = ( AddrItemObject * ) value;
1116 FILE *fp = ( FILE * ) data;
1119 if( ADDRITEM_TYPE(obj) == ITEMTYPE_FOLDER ) {
1120 ItemFolder *folder = ( ItemFolder * ) value;
1122 addrbook_write_elem_s( fp, 1, AB_ELTAG_FOLDER );
1123 addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(folder) );
1124 addrbook_write_attr( fp, AB_ATTAG_NAME, ADDRITEM_NAME(folder) );
1125 addrbook_write_attr( fp, AB_ATTAG_REMARKS, folder->remarks );
1126 fputs( " >\n", fp );
1127 addrbook_write_elem_s( fp, 2, AB_ELTAG_ITEM_LIST );
1131 node = folder->listPerson;
1133 ItemPerson *item = node->data;
1134 addrbook_write_elem_s( fp, 3, AB_ELTAG_ITEM );
1135 addrbook_write_attr( fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_PERSON );
1136 addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(item ) );
1137 fputs( " />\n", fp );
1138 node = g_list_next( node );
1142 node = folder->listGroup;
1144 ItemGroup *item = node->data;
1145 addrbook_write_elem_s( fp, 3, AB_ELTAG_ITEM );
1146 addrbook_write_attr( fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_GROUP );
1147 addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(item ) );
1148 fputs( " />\n", fp );
1149 node = g_list_next( node );
1153 node = folder->listFolder;
1155 ItemFolder *item = node->data;
1156 addrbook_write_elem_s( fp, 3, AB_ELTAG_ITEM );
1157 addrbook_write_attr( fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_FOLDER );
1158 addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(item ) );
1159 fputs( " />\n", fp );
1160 node = g_list_next( node );
1162 addrbook_write_elem_e( fp, 2, AB_ELTAG_ITEM_LIST );
1163 addrbook_write_elem_e( fp, 1, AB_ELTAG_FOLDER );
1169 * Output address book data to specified file.
1170 * return: Status code.
1172 gint addrbook_write_to( AddressBookFile *book, gchar *newFile ) {
1175 #ifndef DEV_STANDALONE
1179 g_return_if_fail( book != NULL );
1180 g_return_if_fail( newFile != NULL );
1182 fileSpec = g_strconcat( book->path, G_DIR_SEPARATOR_S, newFile, NULL );
1184 book->retVal = MGU_OPEN_FILE;
1185 #ifdef DEV_STANDALONE
1186 fp = fopen( fileSpec, "w" );
1189 fputs( "<?xml version=\"1.0\" ?>\n", fp );
1191 pfile = prefs_write_open( fileSpec );
1195 fprintf( fp, "<?xml version=\"1.0\" encoding=\"%s\" ?>\n",
1196 conv_get_current_charset_str() );
1198 addrbook_write_elem_s( fp, 0, AB_ELTAG_ADDRESS_BOOK );
1199 addrbook_write_attr( fp, AB_ATTAG_NAME, book->name );
1200 fputs( " >\n", fp );
1202 // Output all persons
1203 g_hash_table_foreach( book->addressCache->itemHash, addrbook_write_item_person_vis, fp );
1205 // Output all groups
1206 g_hash_table_foreach( book->addressCache->itemHash, addrbook_write_item_group_vis, fp );
1207 // addrbook_write_folder_group( book->rootFolder, fp );
1209 // Output all folders
1210 g_hash_table_foreach( book->addressCache->itemHash, addrbook_write_item_folder_vis, fp );
1211 // addrbook_write_folder_folder( book->rootFolder, fp );
1213 addrbook_write_elem_e( fp, 0, AB_ELTAG_ADDRESS_BOOK );
1214 book->retVal = MGU_SUCCESS;
1215 #ifdef DEV_STANDALONE
1218 if( prefs_write_close( pfile ) < 0 ) {
1219 book->retVal = MGU_ERROR_WRITE;
1225 return book->retVal;
1229 * Output address book data to original file.
1230 * return: Status code.
1232 gint addrbook_save_data( AddressBookFile *book ) {
1233 g_return_if_fail( book != NULL );
1235 book->retVal = MGU_NO_FILE;
1236 if( book->fileName == NULL || *book->fileName == '\0' ) return book->retVal;
1237 if( book->path == NULL || *book->path == '\0' ) return book->retVal;
1239 addrbook_write_to( book, book->fileName );
1240 if( book->retVal == MGU_SUCCESS ) {
1241 book->dirtyFlag = FALSE;
1243 return book->retVal;
1246 /* **********************************************************************
1247 * Address book edit interface functions...
1248 * ***********************************************************************
1252 * Move person's email item.
1253 * param: book Address book.
1255 * itemMove Item to move.
1256 * itemTarget Target item before which to move item.
1258 ItemEMail *addrbook_move_email_before( AddressBookFile *book, ItemPerson *person,
1259 ItemEMail *itemMove, ItemEMail *itemTarget )
1261 ItemEMail *email = NULL;
1262 g_return_if_fail( book != NULL );
1263 email = addritem_move_email_before( person, itemMove, itemTarget );
1265 book->dirtyFlag = TRUE;
1271 * Move person's email item.
1272 * param: book Address book.
1274 * itemMove Item to move.
1275 * itemTarget Target item after which to move item.
1277 ItemEMail *addrbook_move_email_after( AddressBookFile *book, ItemPerson *person,
1278 ItemEMail *itemMove, ItemEMail *itemTarget )
1280 ItemEMail *email = NULL;
1281 g_return_if_fail( book != NULL );
1282 email = addritem_move_email_after( person, itemMove, itemTarget );
1284 book->dirtyFlag = TRUE;
1290 * Hash table visitor function.
1292 static gboolean addrbook_free_simple_hash_vis( gpointer *key, gpointer *value, gpointer *data ) {
1300 * Update address book email list for specified person.
1301 * Enter: book Address book.
1302 * person Person to update.
1303 * listEMail New list of email addresses.
1304 * Note: The existing email addresses are replaced with the new addresses. Any references
1305 * to old addresses in the groups are re-linked to the new addresses. All old addresses
1306 * linked to the person are removed.
1308 void addrbook_update_address_list( AddressBookFile *book, ItemPerson *person, GList *listEMail ) {
1313 g_return_if_fail( book != NULL );
1314 g_return_if_fail( person != NULL );
1316 // Remember old list
1317 oldData = person->listEMail;
1319 // Attach new address list to person.
1322 ItemEMail *email = node->data;
1323 if( ADDRITEM_ID(email) == NULL ) {
1325 addrcache_id_email( book->addressCache, email );
1327 ADDRITEM_PARENT(email) = ADDRITEM_OBJECT(person);
1328 node = g_list_next( node );
1330 person->listEMail = listEMail;
1332 // Get groups where person's email is listed
1333 listGroup = addrcache_get_group_for_person( book->addressCache, person );
1335 GHashTable *hashEMail;
1338 // Load hash table with new address entries
1339 hashEMail = g_hash_table_new( g_str_hash, g_str_equal );
1342 ItemEMail *email = node->data;
1343 gchar *addr = g_strdup( email->address );
1345 if( ! g_hash_table_lookup( hashEMail, addr ) ) {
1346 g_hash_table_insert( hashEMail, addr, email );
1348 node = g_list_next( node );
1351 // Re-parent new addresses to existing groups, where email address match.
1352 nodeGrp = listGroup;
1354 ItemGroup *group = ( ItemGroup * ) nodeGrp->data;
1355 GList *groupEMail = group->listEMail;
1357 GList *listRemove = NULL;
1359 // Process each email item linked to group
1360 nodeGrpEM = groupEMail;
1361 while( nodeGrpEM ) {
1362 ItemEMail *emailGrp = ( ItemEMail * ) nodeGrpEM->data;
1363 if( ADDRITEM_PARENT(emailGrp) == ADDRITEM_OBJECT(person) ) {
1364 // Found an email address for this person
1365 ItemEMail *emailNew = NULL;
1366 gchar *addr = g_strdup( emailGrp->address );
1368 emailNew = ( ItemEMail * ) g_hash_table_lookup( hashEMail, addr );
1371 // Point to this entry
1372 nodeGrpEM->data = emailNew;
1376 listRemove = g_list_append( listRemove, emailGrp );
1379 // Move on to next email link
1380 nodeGrpEM = g_list_next( nodeGrpEM );
1383 // Process all removed links in current group
1384 nodeGrpEM = listRemove;
1385 while( nodeGrpEM ) {
1386 ItemEMail *emailGrp = nodeGrpEM->data;
1387 groupEMail = g_list_remove( groupEMail, emailGrp );
1388 nodeGrpEM = g_list_next( nodeGrpEM );
1391 // Move on to next group
1392 nodeGrp = g_list_next( nodeGrp );
1397 g_hash_table_foreach_remove( hashEMail, ( GHRFunc ) addrbook_free_simple_hash_vis, NULL );
1398 g_hash_table_destroy( hashEMail );
1400 g_list_free( listGroup );
1405 addritem_free_list_email( oldData );
1407 book->dirtyFlag = TRUE;
1412 * Add person and address data to address book.
1413 * Enter: book Address book.
1414 * folder Folder where to add person, or NULL for root folder.
1415 * listEMail New list of email addresses.
1416 * Return: Person added.
1417 * Note: A new person is created with specified list of email addresses. All objects inserted
1418 * into address book.
1420 ItemPerson *addrbook_add_address_list( AddressBookFile *book, ItemFolder *folder, GList *listEMail ) {
1422 ItemFolder *f = folder;
1425 g_return_if_fail( book != NULL );
1427 if( ! f ) f = book->addressCache->rootFolder;
1428 person = addritem_create_item_person();
1429 addrcache_id_person( book->addressCache, person );
1430 addrcache_folder_add_person( book->addressCache, f, person );
1434 ItemEMail *email = node->data;
1435 if( ADDRITEM_ID(email) == NULL ) {
1436 addrcache_id_email( book->addressCache, email );
1438 addrcache_person_add_email( book->addressCache, person, email );
1439 node = g_list_next( node );
1441 book->dirtyFlag = TRUE;
1446 * Load hash table visitor function.
1448 static void addrbook_load_hash_table_email_vis( gpointer key, gpointer value, gpointer data ) {
1449 AddrItemObject *obj = ( AddrItemObject * ) value;
1450 if( ADDRITEM_TYPE(obj) == ITEMTYPE_EMAIL ) {
1451 GHashTable *table = ( GHashTable * ) data;
1452 gchar *newKey = g_strdup( key );
1453 ItemEMail *email = ( ItemEMail * ) obj;
1454 if( ! g_hash_table_lookup( table, newKey ) ) {
1455 g_hash_table_insert( table, newKey, email );
1461 * Load hash table with links to email addresses.
1463 static void addrbook_load_hash_table_email( AddressBookFile *book, GHashTable *table ) {
1464 g_return_if_fail( book != NULL );
1465 g_return_if_fail( table != NULL );
1466 g_hash_table_foreach( book->addressCache->itemHash, addrbook_load_hash_table_email_vis, table );
1470 * Build available email list visitor function.
1472 static void addrbook_build_avail_email_vis( gpointer key, gpointer value, gpointer data ) {
1473 AddrItemObject *obj = ( AddrItemObject * ) value;
1474 if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) {
1475 AddressBookFile *book = data;
1476 ItemPerson *person = ( ItemPerson * ) obj;
1477 GList *node = person->listEMail;
1479 ItemEMail *email = node->data;
1480 gchar *newKey = g_strdup( ADDRITEM_ID(email) );
1481 if( ! g_hash_table_lookup( book->tempHash, ADDRITEM_ID(email) ) ) {
1482 book->tempList = g_list_append( book->tempList, email );
1484 node = g_list_next( node );
1490 * Return link list of available email items (which have not already been linked to
1491 * groups). Note that the list contains references to items and should be g_free()
1492 * when done. Do *NOT* attempt to used the addrbook_free_xxx() functions... this will
1493 * destroy the addressbook data!
1494 * Return: List of items, or NULL if none.
1496 GList *addrbook_get_available_email_list( AddressBookFile *book, ItemGroup *group ) {
1500 g_return_if_fail( book != NULL );
1502 // Load hash table with group email entries
1503 table = g_hash_table_new( g_str_hash, g_str_equal );
1505 list = group->listEMail;
1507 ItemEMail *email = list->data;
1508 g_hash_table_insert( table, ADDRITEM_ID(email), email );
1509 list = g_list_next( list );
1513 // Build list of available email addresses which exclude those already in groups
1514 book->tempList = NULL;
1515 book->tempHash = table;
1516 g_hash_table_foreach( book->addressCache->itemHash, addrbook_build_avail_email_vis, book );
1517 list = book->tempList;
1518 book->tempList = NULL;
1519 book->tempHash = NULL;
1522 g_hash_table_destroy( table );
1529 * Update address book email list for specified group.
1530 * Enter: book Address book.
1531 * group group to update.
1532 * listEMail New list of email addresses. This should *NOT* be g_free() when done.
1533 * Note: The existing email addresses are replaced with the new addresses. Any references
1534 * to old addresses in the groups are re-linked to the new addresses. All old addresses
1535 * linked to the person are removed.
1537 void addrbook_update_group_list( AddressBookFile *book, ItemGroup *group, GList *listEMail ) {
1540 g_return_if_fail( book != NULL );
1541 g_return_if_fail( group != NULL );
1543 // Remember old list
1544 oldData = group->listEMail;
1545 group->listEMail = listEMail;
1546 mgu_clear_list( oldData );
1548 book->dirtyFlag = TRUE;
1552 * Add group and email list to address book.
1553 * Enter: book Address book.
1554 * folder Parent folder, or NULL for root folder.
1555 * listEMail New list of email addresses. This should *NOT* be g_free() when done.
1556 * Return: Group object.
1557 * Note: The existing email addresses are replaced with the new addresses. Any references
1558 * to old addresses in the groups are re-linked to the new addresses. All old addresses
1559 * linked to the person are removed.
1561 ItemGroup *addrbook_add_group_list( AddressBookFile *book, ItemFolder *folder, GList *listEMail ) {
1562 ItemGroup *group = NULL;
1563 ItemFolder *f = folder;
1564 g_return_if_fail( book != NULL );
1566 if( ! f ) f = book->addressCache->rootFolder;
1567 group = addritem_create_item_group();
1568 addrcache_id_group( book->addressCache, group );
1569 addrcache_folder_add_group( book->addressCache, f, group );
1570 group->listEMail = listEMail;
1571 book->dirtyFlag = TRUE;
1576 * Add new folder to address book.
1577 * Enter: book Address book.
1578 * parent Parent folder.
1579 * Return: Folder that was added. This should *NOT* be g_free() when done.
1581 ItemFolder *addrbook_add_new_folder( AddressBookFile *book, ItemFolder *parent ) {
1582 ItemFolder *folder = NULL;
1583 ItemFolder *p = parent;
1584 g_return_if_fail( book != NULL );
1586 if( ! p ) p = book->addressCache->rootFolder;
1587 folder = addritem_create_item_folder();
1588 addrcache_id_folder( book->addressCache, folder );
1589 if( addrcache_hash_add_folder( book->addressCache, folder ) ) {
1590 p->listFolder = g_list_append( p->listFolder, folder );
1591 ADDRITEM_PARENT(folder) = ADDRITEM_OBJECT(p);
1592 book->dirtyFlag = TRUE;
1595 addritem_free_item_folder( folder );
1602 * Update address book attribute list for specified person.
1603 * Enter: book Address book.
1604 * person Person to update.
1605 * listAttrib New list of attributes.
1606 * Note: The existing email addresses are replaced with the new addresses. All old attributes
1607 * linked to the person are removed.
1609 void addrbook_update_attrib_list( AddressBookFile *book, ItemPerson *person, GList *listAttrib ) {
1613 g_return_if_fail( book != NULL );
1614 g_return_if_fail( person != NULL );
1616 // Remember old list
1617 oldData = person->listAttrib;
1619 // Attach new address list to person.
1622 UserAttribute *attrib = node->data;
1623 if( attrib->uid == NULL ) {
1625 addrcache_id_attribute( book->addressCache, attrib );
1627 node = g_list_next( node );
1629 person->listAttrib = listAttrib;
1632 addritem_free_list_attribute( oldData );
1634 book->dirtyFlag = TRUE;
1639 * Add attribute data for person to address book.
1640 * Enter: book Address book.
1641 * person New person object.
1642 * listAttrib New list of attributes.
1643 * Note: Only attributes are inserted into address book.
1645 void addrbook_add_attrib_list( AddressBookFile *book, ItemPerson *person, GList *listAttrib ) {
1647 g_return_if_fail( book != NULL );
1648 g_return_if_fail( person != NULL );
1652 UserAttribute *attrib = node->data;
1653 if( attrib->uid == NULL ) {
1654 addrcache_id_attribute( book->addressCache, attrib );
1656 addritem_person_add_attribute( person, attrib );
1657 node = g_list_next( node );
1659 book->dirtyFlag = TRUE;
1663 * Return address book file for specified object.
1664 * Enter: aio Book item object.
1665 * Return: Address book, or NULL if not found.
1667 AddressBookFile *addrbook_item_get_bookfile( AddrItemObject *aio ) {
1668 AddressBookFile *book = NULL;
1670 ItemFolder *parent = NULL;
1671 ItemFolder *root = NULL;
1672 if( aio->type == ITEMTYPE_EMAIL ) {
1673 ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(aio);
1675 parent = ( ItemFolder * ) ADDRITEM_PARENT(person);
1679 parent = ( ItemFolder * ) ADDRITEM_PARENT(aio);
1682 root = addrcache_find_root_folder( parent );
1685 book = ( AddressBookFile * ) ADDRITEM_PARENT(root);
1692 * Remove folder from address book. Children are re-parented to parent folder.
1693 * param: folder Folder to remove.
1694 * return: Folder, or NULL if not found. Note that object should still be freed.
1696 ItemFolder *addrbook_remove_folder( AddressBookFile *book, ItemFolder *folder ) {
1698 g_return_if_fail( book != NULL );
1699 f = addrcache_remove_folder( book->addressCache, folder );
1700 if( f ) book->dirtyFlag = TRUE;
1705 * Remove folder from address book. Children are deleted.
1706 * param: folder Folder to remove.
1707 * return: Folder, or NULL if not found. Note that object should still be freed.
1709 ItemFolder *addrbook_remove_folder_delete( AddressBookFile *book, ItemFolder *folder ) {
1711 g_return_if_fail( book != NULL );
1712 f = addrcache_remove_folder_delete( book->addressCache, folder );
1713 if( f ) book->dirtyFlag = TRUE;
1717 #define WORK_BUFLEN 1024
1718 #define ADDRBOOK_DIGITS "0123456789"
1721 * Return list of existing address book files.
1722 * Enter: book Address book file.
1723 * Return: File list.
1725 GList *addrbook_get_bookfile_list( AddressBookFile *book ) {
1728 struct dirent *entry;
1729 struct stat statbuf;
1730 gchar buf[ WORK_BUFLEN ];
1731 gchar numbuf[ WORK_BUFLEN ];
1732 gint len, lenpre, lensuf, lennum;
1733 long int val, maxval;
1734 GList *fileList = NULL;
1736 g_return_if_fail( book != NULL );
1738 if( book->path == NULL || *book->path == '\0' ) {
1739 book->retVal = MGU_NO_PATH;
1743 strcpy( buf, book->path );
1744 len = strlen( buf );
1746 if( buf[ len-1 ] != G_DIR_SEPARATOR ) {
1747 buf[ len ] = G_DIR_SEPARATOR;
1748 buf[ ++len ] = '\0';
1752 adbookdir = g_strdup( buf );
1753 strcat( buf, ADDRBOOK_PREFIX );
1755 if( ( dp = opendir( adbookdir ) ) == NULL ) {
1756 book->retVal = MGU_OPEN_DIRECTORY;
1757 g_free( adbookdir );
1761 lenpre = strlen( ADDRBOOK_PREFIX );
1762 lensuf = strlen( ADDRBOOK_SUFFIX );
1763 lennum = FILE_NUMDIGITS + lenpre;
1766 while( ( entry = readdir( dp ) ) != NULL ) {
1767 gchar **endptr = NULL;
1771 strcpy( buf, adbookdir );
1772 strcat( buf, entry->d_name );
1773 stat( buf, &statbuf );
1774 if( S_IFREG & statbuf.st_mode ) {
1775 if( strncmp( entry->d_name, ADDRBOOK_PREFIX, lenpre ) == 0 ) {
1776 if( strncmp( (entry->d_name) + lennum, ADDRBOOK_SUFFIX, lensuf ) == 0 ) {
1777 strncpy( numbuf, (entry->d_name) + lenpre, FILE_NUMDIGITS );
1779 for( i = 0; i < FILE_NUMDIGITS; i++ ) {
1780 if( ! strchr( ADDRBOOK_DIGITS, numbuf[i] ) ) {
1787 val = strtol( numbuf, &endptr, 10 );
1788 if( endptr && val > -1 ) {
1789 if( val > maxval ) maxval = val;
1790 fileList = g_list_append( fileList, g_strdup( entry->d_name ) );
1798 g_free( adbookdir );
1800 book->maxValue = maxval;
1801 book->retVal = MGU_SUCCESS;
1806 * Return file name for specified file number.
1807 * Enter: fileNum File number.
1808 * Return: File name, or NULL if file number too large. Should be g_free() when done.
1810 gchar *addrbook_gen_new_file_name( gint fileNum ) {
1812 gchar buf[ WORK_BUFLEN ];
1817 nmax = -1 + (long int) pow( 10, FILE_NUMDIGITS );
1818 if( fileNum > nmax ) return NULL;
1819 sprintf( fmt, "%%s%%0%dd%%s", FILE_NUMDIGITS );
1820 sprintf( buf, fmt, ADDRBOOK_PREFIX, n, ADDRBOOK_SUFFIX );
1821 return g_strdup( buf );
1824 /* **********************************************************************
1825 * Address book test functions...
1826 * ***********************************************************************
1829 static void addrbook_show_attribs( GList *attr ) {
1831 gchar *name = ((XMLAttr *)attr->data)->name;
1832 gchar *value = ((XMLAttr *)attr->data)->value;
1833 printf( "\tn/v = %s : %s\n", name, value );
1834 attr = g_list_next( attr );
1836 printf( "\t---\n" );
1840 * Test email address list.
1842 static void addrbook_chkparse_addr_list( AddressBookFile *book, XMLFile *file ){
1847 prev_level = file->level;
1848 if( xml_parse_next_tag( file ) ) {
1849 longjmp( book->jumper, 1 );
1851 if (file->level < prev_level) return;
1852 attr = xml_get_current_tag_attr(file);
1853 // addrbook_show_attribs( attr );
1854 if( xml_compare_tag( file, AB_ELTAG_ADDRESS ) ) {
1855 addrbook_chkparse_addr_list( book, file );
1861 * Test user attributes for person.
1863 static void addrbook_chkparse_attribute( AddressBookFile *book, XMLFile *file ) {
1867 attr = xml_get_current_tag_attr(file);
1868 // addrbook_show_attribs( attr );
1869 element = xml_get_element( file );
1870 // printf( "\t\tattrib value : %s\n", element );
1874 * Test attribute list.
1876 static void addrbook_chkparse_attr_list( AddressBookFile *book, XMLFile *file ){
1880 prev_level = file->level;
1881 if( xml_parse_next_tag( file ) ) {
1882 longjmp( book->jumper, 1 );
1884 if (file->level < prev_level) return;
1885 if( xml_compare_tag( file, AB_ELTAG_ATTRIBUTE ) ) {
1886 addrbook_chkparse_attribute( book, file );
1887 addrbook_chkparse_attr_list( book, file );
1895 static void addrbook_chkparse_person( AddressBookFile *book, XMLFile *file ) {
1897 gchar *name, *value;
1899 attr = xml_get_current_tag_attr(file);
1900 // addrbook_show_attribs( attr );
1901 if( xml_parse_next_tag( file ) ) { // Consume closing tag
1902 longjmp( book->jumper, 1 );
1904 if( xml_compare_tag( file, AB_ELTAG_ADDRESS_LIST ) ) {
1905 addrbook_chkparse_addr_list( book, file );
1907 if( xml_parse_next_tag( file ) ) { // Consume closing tag
1908 longjmp( book->jumper, 1 );
1910 if( xml_compare_tag( file, AB_ELTAG_ATTRIBUTE_LIST ) ) {
1911 addrbook_chkparse_attr_list( book, file );
1916 * Test group member list.
1918 static void addrbook_chkparse_member_list( AddressBookFile *book, XMLFile *file ){
1923 prev_level = file->level;
1924 if( xml_parse_next_tag( file ) ) {
1925 longjmp( book->jumper, 1 );
1927 if (file->level < prev_level) return;
1928 if( xml_compare_tag( file, AB_ELTAG_MEMBER ) ) {
1929 attr = xml_get_current_tag_attr(file);
1930 // addrbook_show_attribs( attr );
1931 addrbook_chkparse_member_list( book, file );
1934 attr = xml_get_current_tag_attr( file );
1935 // addrbook_show_attribs( attr );
1943 static void addrbook_chkparse_group( AddressBookFile *book, XMLFile *file ) {
1945 gchar *name, *value;
1947 attr = xml_get_current_tag_attr(file);
1948 // addrbook_show_attribs( attr );
1949 if( xml_parse_next_tag( file ) ) { // Consume closing tag
1950 longjmp( book->jumper, 1 );
1952 if( xml_compare_tag( file, AB_ELTAG_MEMBER_LIST ) ) {
1953 addrbook_chkparse_member_list( book, file );
1958 * Test folder item list.
1960 static void addrbook_chkparse_folder_list( AddressBookFile *book, XMLFile *file ){
1965 prev_level = file->level;
1966 if( xml_parse_next_tag( file ) ) {
1967 longjmp( book->jumper, 1 );
1969 if (file->level < prev_level) return;
1970 if( xml_compare_tag( file, AB_ELTAG_ITEM ) ) {
1971 attr = xml_get_current_tag_attr(file);
1972 // addrbook_show_attribs( attr );
1973 addrbook_chkparse_folder_list( book, file );
1976 attr = xml_get_current_tag_attr( file );
1977 // addrbook_show_attribs( attr );
1985 static void addrbook_chkparse_folder( AddressBookFile *book, XMLFile *file ) {
1987 gchar *name, *value;
1989 attr = xml_get_current_tag_attr(file);
1990 // addrbook_show_attribs( attr );
1991 if( xml_parse_next_tag( file ) ) { // Consume closing tag
1992 longjmp( book->jumper, 1 );
1994 if( xml_compare_tag( file, AB_ELTAG_ITEM_LIST ) ) {
1995 addrbook_chkparse_folder_list( book, file );
2000 * Test address book.
2002 static gboolean addrbook_chkread_tree( AddressBookFile *book, XMLFile *file ) {
2004 gchar *name, *value;
2007 if( xml_get_dtd( file ) ) {
2010 if( xml_parse_next_tag( file ) ) {
2014 if( ! xml_compare_tag( file, AB_ELTAG_ADDRESS_BOOK ) ) {
2018 attr = xml_get_current_tag_attr(file);
2019 // addrbook_show_attribs( attr );
2023 if (! file->level ) break;
2025 if( xml_parse_next_tag( file ) ) {
2026 longjmp( book->jumper, 1 );
2028 // Get next tag (person, group or folder)
2029 if( xml_parse_next_tag( file ) ) {
2030 longjmp( book->jumper, 1 );
2032 if( xml_compare_tag( file, AB_ELTAG_PERSON ) ) {
2033 addrbook_chkparse_person( book, file );
2035 else if( xml_compare_tag( file, AB_ELTAG_GROUP ) ) {
2036 addrbook_chkparse_group( book, file );
2038 else if( xml_compare_tag( file, AB_ELTAG_FOLDER ) ) {
2039 addrbook_chkparse_folder( book, file );
2042 // Item not recognized
2050 * Test address book file by parsing contents.
2051 * Enter: book Address book file to check.
2052 * fileName File name to check.
2053 * Return: MGU_SUCCESS if file appears to be valid format.
2055 gint addrbook_test_read_file( AddressBookFile *book, gchar *fileName ) {
2056 XMLFile *file = NULL;
2057 gchar *fileSpec = NULL;
2059 g_return_if_fail( book != NULL );
2061 fileSpec = g_strconcat( book->path, G_DIR_SEPARATOR_S, fileName, NULL );
2062 book->retVal = MGU_OPEN_FILE;
2063 file = xml_open_file( fileSpec );
2066 book->retVal = MGU_BAD_FORMAT;
2067 if( setjmp( book->jumper ) ) {
2068 // printf( "Caught Ya!!!\n" );
2069 xml_close_file( file );
2070 return book->retVal;
2072 if( addrbook_chkread_tree( book, file ) ) {
2073 book->retVal = MGU_SUCCESS;
2075 xml_close_file( file );
2077 return book->retVal;
2081 * Return link list of all persons in address book. Note that the list contains
2082 * references to items. Do *NOT* attempt to use the addrbook_free_xxx() functions...
2083 * this will destroy the addressbook data!
2084 * Return: List of items, or NULL if none.
2086 GList *addrbook_get_all_persons( AddressBookFile *book ) {
2087 g_return_if_fail( book != NULL );
2088 return addrcache_get_all_persons( book->addressCache );
2092 * Add person and address data to address book.
2093 * Enter: book Address book.
2094 * folder Folder where to add person, or NULL for root folder.
2096 * address EMail address.
2098 * Return: Person added. Do not *NOT* to use the addrbook_free_xxx() functions...
2099 * this will destroy the address book data.
2101 ItemPerson *addrbook_add_contact( AddressBookFile *book, ItemFolder *folder, const gchar *name,
2102 const gchar *address, const gchar *remarks )
2104 ItemPerson *person = NULL;
2105 g_return_if_fail( book != NULL );
2106 person = addrcache_add_contact( book->addressCache, folder, name, address, remarks );
2107 if( person ) book->dirtyFlag = TRUE;