/* * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client * Copyright (C) 2001 Match Grun * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * General functions for accessing address book files. */ #include #include #include #include #include #include #include #include "xml.h" #include "mgutils.h" #include "addritem.h" #include "addrcache.h" #include "addrbook.h" #include "adbookbase.h" #ifndef DEV_STANDALONE #include "prefs.h" #include "codeconv.h" #endif #define ADDRBOOK_MAX_SEARCH_COUNT 1000 #define ADDRBOOK_PREFIX "addrbook-" #define ADDRBOOK_SUFFIX ".xml" #define FILE_NUMDIGITS 6 #define ID_TIME_OFFSET 998000000 /* * Create new address book. */ AddressBookFile *addrbook_create_book() { AddressBookFile *book; book = g_new0( AddressBookFile, 1 ); book->type = ADBOOKTYPE_BOOK; book->addressCache = addrcache_create(); book->retVal = MGU_SUCCESS; book->path = NULL; book->fileName = NULL; book->maxValue = 0; book->tempList = NULL; book->tempHash = NULL; book->addressCache->modified = TRUE; return book; } /* * Specify name to be used. */ void addrbook_set_name( AddressBookFile *book, const gchar *value ) { g_return_if_fail( book != NULL ); addrcache_set_name( book->addressCache, value ); } gchar *addrbook_get_name( AddressBookFile *book ) { g_return_val_if_fail( book != NULL, NULL ); return addrcache_get_name( book->addressCache ); } void addrbook_set_path( AddressBookFile *book, const gchar *value ) { g_return_if_fail( book != NULL ); book->path = mgu_replace_string( book->path, value ); addrcache_set_dirty( book->addressCache, TRUE ); } void addrbook_set_file( AddressBookFile *book, const gchar *value ) { g_return_if_fail( book != NULL ); book->fileName = mgu_replace_string( book->fileName, value ); addrcache_set_dirty( book->addressCache, TRUE ); } gboolean addrbook_get_modified( AddressBookFile *book ) { g_return_val_if_fail( book != NULL, FALSE ); return book->addressCache->modified; } void addrbook_set_modified( AddressBookFile *book, const gboolean value ) { g_return_if_fail( book != NULL ); book->addressCache->modified = value; } gboolean addrbook_get_accessed( AddressBookFile *book ) { g_return_val_if_fail( book != NULL, FALSE ); return book->addressCache->accessFlag; } void addrbook_set_accessed( AddressBookFile *book, const gboolean value ) { g_return_if_fail( book != NULL ); book->addressCache->accessFlag = value; } gboolean addrbook_get_read_flag( AddressBookFile *book ) { g_return_val_if_fail( book != NULL, FALSE ); return book->addressCache->dataRead; } void addrbook_set_read_flag( AddressBookFile *book, const gboolean value ) { g_return_if_fail( book != NULL ); book->addressCache->dataRead = value; } gint addrbook_get_status( AddressBookFile *book ) { g_return_val_if_fail( book != NULL, -1 ); return book->retVal; } ItemFolder *addrbook_get_root_folder( AddressBookFile *book ) { g_return_val_if_fail( book != NULL, NULL ); return addrcache_get_root_folder( book->addressCache ); } GList *addrbook_get_list_folder( AddressBookFile *book ) { g_return_val_if_fail( book != NULL, NULL ); return addrcache_get_list_folder( book->addressCache ); } GList *addrbook_get_list_person( AddressBookFile *book ) { g_return_val_if_fail( book != NULL, NULL ); return addrcache_get_list_person( book->addressCache ); } gboolean addrbook_get_dirty( AddressBookFile *book ) { g_return_val_if_fail( book != NULL, FALSE ); return addrcache_get_dirty( book->addressCache ); } void addrbook_set_dirty( AddressBookFile *book, const gboolean value ) { g_return_if_fail( book != NULL ); addrcache_set_dirty( book->addressCache, value ); } /* * Empty address book. */ void addrbook_empty_book( AddressBookFile *book ) { g_return_if_fail( book != NULL ); /* Free up internal objects */ addrcache_clear( book->addressCache ); addrcache_set_dirty( book->addressCache, FALSE ); g_list_free( book->tempList ); /* Reset to initial state */ book->tempList = NULL; book->tempHash = NULL; book->addressCache->dataRead = FALSE; book->addressCache->modified = FALSE; book->addressCache->accessFlag = FALSE; book->retVal = MGU_SUCCESS; } /* * Free address book. */ void addrbook_free_book( AddressBookFile *book ) { g_return_if_fail( book != NULL ); /* Clear cache */ addrcache_clear( book->addressCache ); addrcache_free( book->addressCache ); /* Free up internal objects */ g_free( book->path ); g_free( book->fileName ); g_list_free( book->tempList ); book->path = NULL; book->fileName = NULL; book->maxValue = 0; book->tempList = NULL; book->tempHash = NULL; book->type = ADBOOKTYPE_NONE; book->addressCache = NULL; book->retVal = MGU_SUCCESS; g_free( book ); } /* * Print list of items. */ void addrbook_print_item_list( GList *list, FILE *stream ) { GList *node = list; while( node ) { AddrItemObject *obj = node->data; if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) { addritem_print_item_person( ( ItemPerson * ) obj, stream ); } else if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) { addritem_print_item_group( ( ItemGroup * ) obj, stream ); } else if( ADDRITEM_TYPE(obj) == ITEMTYPE_FOLDER ) { addritem_print_item_folder( ( ItemFolder * ) obj, stream ); } node = g_list_next( node ); } fprintf( stream, "\t---\n" ); } /* * Print address book. */ void addrbook_print_book( AddressBookFile *book, FILE *stream ) { g_return_if_fail( book != NULL ); fprintf( stream, "AddressBook:\n" ); fprintf( stream, "\tpath : '%s'\n", book->path ); fprintf( stream, "\tfile : '%s'\n", book->fileName ); fprintf( stream, "\tstatus: %d : '%s'\n", book->retVal, mgu_error2string( book->retVal ) ); addrcache_print( book->addressCache, stream ); } /* * Dump entire address book traversing folders. */ void addrbook_dump_book( AddressBookFile *book, FILE *stream ) { ItemFolder *folder; g_return_if_fail( book != NULL ); addrbook_print_book( book, stream ); folder = book->addressCache->rootFolder; addritem_print_item_folder( folder, stream ); } /* * Remove group from address book. * param: group Group to remove. * return: Group, or NULL if not found. Note that object should still be freed. */ ItemGroup *addrbook_remove_group( AddressBookFile *book, ItemGroup *group ) { g_return_val_if_fail( book != NULL, NULL ); return addrcache_remove_group( book->addressCache, group ); } /* * Remove specified person from address book. * param: person Person to remove. * return: Person, or NULL if not found. Note that object should still be freed. */ ItemPerson *addrbook_remove_person( AddressBookFile *book, ItemPerson *person ) { g_return_val_if_fail( book != NULL, NULL ); return addrcache_remove_person( book->addressCache, person ); } /* * Remove email address in address book for specified person. * param: person Person. * email EMail to remove. * return: EMail object, or NULL if not found. Note that object should still be freed. */ ItemEMail *addrbook_person_remove_email( AddressBookFile *book, ItemPerson *person, ItemEMail *email ) { g_return_val_if_fail( book != NULL, NULL ); return addrcache_person_remove_email( book->addressCache, person, email ); } /* ********************************************************************** * Read/Write XML data file... * =========================== * Notes: * 1) The address book is structured as follows: * * address-book * person * address-list * address * attribute-list * attribute * group * member-list * member * folder * item-list * item * * 2) This sequence of elements was chosen so that the most important * elements (person and their email addresses) appear first. * * 3) Groups then appear. When groups are loaded, person's email * addresses have already been loaded and can be found. * * 4) Finally folders are loaded. Any forward and backward references * to folders, groups and persons in the folders are resolved after * loading. * * *********************************************************************** */ /* Element tag names */ #define AB_ELTAG_ADDRESS "address" #define AB_ELTAG_ATTRIBUTE "attribute" #define AB_ELTAG_ATTRIBUTE_LIST "attribute-list" #define AB_ELTAG_ADDRESS_LIST "address-list" #define AB_ELTAG_MEMBER "member" #define AB_ELTAG_MEMBER_LIST "member-list" #define AB_ELTAG_ITEM "item" #define AB_ELTAG_ITEM_LIST "item-list" #define AB_ELTAG_ADDRESS_BOOK "address-book" #define AB_ELTAG_PERSON "person" #define AB_ELTAG_GROUP "group" #define AB_ELTAG_FOLDER "folder" /* Attribute tag names */ #define AB_ATTAG_TYPE "type" #define AB_ATTAG_UID "uid" #define AB_ATTAG_NAME "name" #define AB_ATTAG_REMARKS "remarks" #define AB_ATTAG_FIRST_NAME "first-name" #define AB_ATTAG_LAST_NAME "last-name" #define AB_ATTAG_NICK_NAME "nick-name" #define AB_ATTAG_COMMON_NAME "cn" #define AB_ATTAG_ALIAS "alias" #define AB_ATTAG_EMAIL "email" #define AB_ATTAG_EID "eid" #define AB_ATTAG_PID "pid" /* Attribute values */ #define AB_ATTAG_VAL_PERSON "person" #define AB_ATTAG_VAL_GROUP "group" #define AB_ATTAG_VAL_FOLDER "folder" /* * Parse address item for person. */ static void addrbook_parse_address( AddressBookFile *book, XMLFile *file, ItemPerson *person ) { GList *attr; gchar *name, *value; ItemEMail *email = NULL; attr = xml_get_current_tag_attr(file); while( attr ) { name = ((XMLAttr *)attr->data)->name; value = ((XMLAttr *)attr->data)->value; if( ! email ) email = addritem_create_item_email(); if( strcmp( name, AB_ATTAG_UID ) == 0 ) { ADDRITEM_ID(email) = g_strdup( value ); } else if( strcmp( name, AB_ATTAG_ALIAS ) == 0 ) { ADDRITEM_NAME(email) = g_strdup( value ); } else if( strcmp( name, AB_ATTAG_EMAIL ) == 0 ) { email->address = g_strdup( value ); } else if( strcmp( name, AB_ATTAG_REMARKS ) == 0 ) { email->remarks = g_strdup( value ); } attr = g_list_next( attr ); } if( email ) { if( person ) { addrcache_person_add_email( book->addressCache, person, email ); } else { addritem_free_item_email( email ); email = NULL; } } } /* * Parse email address list. */ static void addrbook_parse_addr_list( AddressBookFile *book, XMLFile *file, ItemPerson *person ){ GList *attr; guint prev_level; for (;;) { prev_level = file->level; if( xml_parse_next_tag( file ) ) { longjmp( book->jumper, 1 ); } if (file->level < prev_level) return; if( xml_compare_tag( file, AB_ELTAG_ADDRESS ) ) { attr = xml_get_current_tag_attr(file); addrbook_parse_address( book, file, person ); addrbook_parse_addr_list( book, file, person ); } } } /* * Parse attibute for person. */ static void addrbook_parse_attribute( XMLFile *file, ItemPerson *person ) { GList *attr; gchar *name, *value; gchar *element; UserAttribute *uAttr = NULL; attr = xml_get_current_tag_attr(file); while( attr ) { name = ((XMLAttr *)attr->data)->name; value = ((XMLAttr *)attr->data)->value; if( ! uAttr ) uAttr = addritem_create_attribute(); if( strcmp( name, AB_ATTAG_UID ) == 0 ) { addritem_attrib_set_id( uAttr, value ); } else if( strcmp( name, AB_ATTAG_NAME ) == 0 ) { addritem_attrib_set_name( uAttr, value ); } attr = g_list_next( attr ); } element = xml_get_element( file ); addritem_attrib_set_value( uAttr, element ); if( uAttr ) { if( person ) { addritem_person_add_attribute( person, uAttr ); } else { addritem_free_attribute( uAttr ); uAttr = NULL; } } } /* * Parse attribute list. */ static void addrbook_parse_attr_list( AddressBookFile *book, XMLFile *file, ItemPerson *person ){ GList *attr; guint prev_level; for (;;) { prev_level = file->level; if( xml_parse_next_tag( file ) ) { longjmp( book->jumper, 1 ); } if (file->level < prev_level) return; if( xml_compare_tag( file, AB_ELTAG_ATTRIBUTE ) ) { attr = xml_get_current_tag_attr(file); addrbook_parse_attribute( file, person ); addrbook_parse_attr_list( book, file, person ); } } } /* * Parse person. */ static void addrbook_parse_person( AddressBookFile *book, XMLFile *file ) { GList *attr; gchar *name, *value; ItemPerson *person = NULL; attr = xml_get_current_tag_attr(file); while( attr ) { name = ((XMLAttr *)attr->data)->name; value = ((XMLAttr *)attr->data)->value; if( ! person ) person = addritem_create_item_person(); if( strcmp( name, AB_ATTAG_UID ) == 0 ) { ADDRITEM_ID(person) = g_strdup( value ); } else if( strcmp( name, AB_ATTAG_FIRST_NAME ) == 0 ) { person->firstName = g_strdup( value ); } else if( strcmp( name, AB_ATTAG_LAST_NAME ) == 0 ) { person->lastName = g_strdup( value ); } else if( strcmp( name, AB_ATTAG_NICK_NAME ) == 0 ) { person->nickName = g_strdup( value ); } else if( strcmp( name, AB_ATTAG_COMMON_NAME ) == 0 ) { ADDRITEM_NAME(person) = g_strdup( value ); } attr = g_list_next( attr ); } if( xml_parse_next_tag( file ) ) { /* Consume closing tag */ longjmp( book->jumper, 1 ); } if( xml_compare_tag( file, AB_ELTAG_ADDRESS_LIST ) ) { addrbook_parse_addr_list( book, file, person ); if( person ) { addrcache_hash_add_person( book->addressCache, person ); } } if( xml_parse_next_tag( file ) ) { /* Consume closing tag */ longjmp( book->jumper, 1 ); } if( xml_compare_tag( file, AB_ELTAG_ATTRIBUTE_LIST ) ) { addrbook_parse_attr_list( book, file, person ); } } /* * Parse group member. */ static void addrbook_parse_member( AddressBookFile *book, XMLFile *file, ItemGroup *group ) { GList *attr; gchar *name, *value; gchar *pid = NULL, *eid = NULL; ItemEMail *email = NULL; attr = xml_get_current_tag_attr(file); while( attr ) { name = ((XMLAttr *)attr->data)->name; value = ((XMLAttr *)attr->data)->value; if( strcmp( name, AB_ATTAG_PID ) == 0 ) { pid = g_strdup( value ); } else if( strcmp( name, AB_ATTAG_EID ) == 0 ) { eid = g_strdup( value ); } attr = g_list_next( attr ); } /* email = addrcache_get_email( book->addressCache, pid, eid ); */ email = addrcache_get_email( book->addressCache, eid ); if( email ) { if( group ) { addrcache_group_add_email( book->addressCache, group, email ); } else { addritem_free_item_email( email ); email = NULL; } } } /* * Parse group member list. */ static void addrbook_parse_member_list( AddressBookFile *book, XMLFile *file, ItemGroup *group ){ GList *attr; guint prev_level; for (;;) { prev_level = file->level; if( xml_parse_next_tag( file ) ) { longjmp( book->jumper, 1 ); } if (file->level < prev_level) return; if( xml_compare_tag( file, AB_ELTAG_MEMBER ) ) { attr = xml_get_current_tag_attr(file); addrbook_parse_member( book, file, group ); addrbook_parse_member_list( book, file, group ); } else { attr = xml_get_current_tag_attr( file ); } } } /* * Parse group. */ static void addrbook_parse_group( AddressBookFile *book, XMLFile *file ) { GList *attr; gchar *name, *value; ItemGroup *group = NULL; attr = xml_get_current_tag_attr(file); while( attr ) { name = ((XMLAttr *)attr->data)->name; value = ((XMLAttr *)attr->data)->value; if( ! group ) group = addritem_create_item_group(); if( strcmp( name, AB_ATTAG_UID ) == 0 ) { ADDRITEM_ID(group) = g_strdup( value ); } else if( strcmp( name, AB_ATTAG_NAME ) == 0 ) { ADDRITEM_NAME(group) = g_strdup( value ); } else if( strcmp( name, AB_ATTAG_REMARKS ) == 0 ) { group->remarks = g_strdup( value ); } attr = g_list_next( attr ); } if( xml_parse_next_tag( file ) ) { /* Consume closing tag */ longjmp( book->jumper, 1 ); } if( xml_compare_tag( file, AB_ELTAG_MEMBER_LIST ) ) { if( group ) { addrcache_hash_add_group( book->addressCache, group ); } addrbook_parse_member_list( book, file, group ); } } /* * Parse folder item. */ static void addrbook_parse_folder_item( AddressBookFile *book, XMLFile *file, ItemFolder *folder ) { GList *attr; gchar *name, *value; gchar *uid = NULL; attr = xml_get_current_tag_attr(file); while( attr ) { name = ((XMLAttr *)attr->data)->name; value = ((XMLAttr *)attr->data)->value; if( strcmp( name, AB_ATTAG_UID ) == 0 ) { uid = g_strdup( value ); } attr = g_list_next( attr ); } if( folder ) { if( uid ) { folder->listItems = g_list_append( folder->listItems, uid ); } } } /* * Parse folder item list. */ static void addrbook_parse_folder_list( AddressBookFile *book, XMLFile *file, ItemFolder *folder ){ GList *attr; guint prev_level; for (;;) { prev_level = file->level; if( xml_parse_next_tag( file ) ) { longjmp( book->jumper, 1 ); } if (file->level < prev_level) return; if( xml_compare_tag( file, AB_ELTAG_ITEM ) ) { attr = xml_get_current_tag_attr(file); addrbook_parse_folder_item( book, file, folder ); addrbook_parse_folder_list( book, file, folder ); } else { attr = xml_get_current_tag_attr( file ); } } } /* * Parse folder. */ static void addrbook_parse_folder( AddressBookFile *book, XMLFile *file ) { GList *attr; gchar *name, *value; ItemFolder *folder = NULL; attr = xml_get_current_tag_attr(file); while( attr ) { name = ((XMLAttr *)attr->data)->name; value = ((XMLAttr *)attr->data)->value; if( ! folder ) { folder = addritem_create_item_folder(); } if( strcmp( name, AB_ATTAG_UID ) == 0 ) { ADDRITEM_ID(folder) = g_strdup( value ); } else if( strcmp( name, AB_ATTAG_NAME ) == 0 ) { ADDRITEM_NAME(folder) = g_strdup( value ); } else if( strcmp( name, AB_ATTAG_REMARKS ) == 0 ) { folder->remarks = g_strdup( value ); } attr = g_list_next( attr ); } if( xml_parse_next_tag( file ) ) { /* Consume closing tag */ longjmp( book->jumper, 1 ); } if( xml_compare_tag( file, AB_ELTAG_ITEM_LIST ) ) { if( folder ) { if( addrcache_hash_add_folder( book->addressCache, folder ) ) { book->tempList = g_list_append( book->tempList, folder ); ADDRITEM_PARENT(folder) = NULL; /* We will resolve folder later */ } } addrbook_parse_folder_list( book, file, folder ); } } /* * Parse address book. * Return: TRUE if data read successfully, FALSE if error reading data. */ static gboolean addrbook_read_tree( AddressBookFile *book, XMLFile *file ) { gboolean retVal; GList *attr; gchar *name, *value; book->retVal = MGU_BAD_FORMAT; if( xml_get_dtd( file ) ) { return FALSE; } if( xml_parse_next_tag( file ) ) { longjmp( book->jumper, 1 ); } if( ! xml_compare_tag( file, AB_ELTAG_ADDRESS_BOOK ) ) { return FALSE; } attr = xml_get_current_tag_attr(file); while( attr ) { name = ((XMLAttr *)attr->data)->name; value = ((XMLAttr *)attr->data)->value; if( strcmp( name, AB_ATTAG_NAME ) == 0 ) { addrbook_set_name( book, value ); } attr = g_list_next( attr ); } retVal = TRUE; for (;;) { if (! file->level ) break; /* Get next item tag (person, group or folder) */ if( xml_parse_next_tag( file ) ) { longjmp( book->jumper, 1 ); } if( xml_compare_tag( file, AB_ELTAG_PERSON ) ) { addrbook_parse_person( book, file ); } else if( xml_compare_tag( file, AB_ELTAG_GROUP ) ) { addrbook_parse_group( book, file ); } else if( xml_compare_tag( file, AB_ELTAG_FOLDER ) ) { addrbook_parse_folder( book, file ); } } if( retVal ) book->retVal = MGU_SUCCESS; return retVal; } /* * Resolve folder items visitor function. */ static void addrbook_res_items_vis( gpointer key, gpointer value, gpointer data ) { AddressBookFile *book = data; AddrItemObject *obj = ( AddrItemObject * ) value; ItemFolder *rootFolder = book->addressCache->rootFolder; if( obj->parent == NULL ) { if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) { rootFolder->listPerson = g_list_append( rootFolder->listPerson, obj ); ADDRITEM_PARENT(obj) = ADDRITEM_OBJECT(rootFolder); } else if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) { rootFolder->listGroup = g_list_append( rootFolder->listGroup, obj ); ADDRITEM_PARENT(obj) = ADDRITEM_OBJECT(rootFolder); } } } /* * Resolve folder items. Lists of UID's are replaced with pointers to data items. */ static void addrbook_resolve_folder_items( AddressBookFile *book ) { GList *nodeFolder = NULL; GList *listRemove = NULL; GList *node = NULL; ItemFolder *rootFolder = book->addressCache->rootFolder; nodeFolder = book->tempList; while( nodeFolder ) { ItemFolder *folder = nodeFolder->data; listRemove = NULL; node = folder->listItems; while( node ) { gchar *uid = node->data; AddrItemObject *aio = addrcache_get_object( book->addressCache, uid ); if( aio ) { if( aio->type == ITEMTYPE_FOLDER ) { ItemFolder *item = ( ItemFolder * ) aio; folder->listFolder = g_list_append( folder->listFolder, item ); ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder); addrcache_hash_add_folder( book->addressCache, folder ); } else if( aio->type == ITEMTYPE_PERSON ) { ItemPerson *item = ( ItemPerson * ) aio; folder->listPerson = g_list_append( folder->listPerson, item ); ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder); } else if( aio->type == ITEMTYPE_GROUP ) { ItemGroup *item = ( ItemGroup * ) aio; folder->listGroup = g_list_append( folder->listGroup, item ); ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder); } /* Replace data with pointer to item */ g_free( uid ); node->data = aio; } else { /* Not found, append to remove list. */ listRemove = g_list_append( listRemove, uid ); } node = g_list_next( node ); } rootFolder->listFolder = g_list_append( rootFolder->listFolder, folder ); /* Process remove list */ node = listRemove; while( node ) { gchar *uid = node->data; folder->listItems = g_list_remove( folder->listItems, uid ); g_free( uid ); node = g_list_next( node ); } g_list_free( listRemove ); nodeFolder = g_list_next( nodeFolder ); } /* Remove folders with parents. */ listRemove = NULL; node = rootFolder->listFolder; while( node ) { ItemFolder *folder = ( ItemFolder * ) node->data; if( ADDRITEM_PARENT(folder) ) { /* Remove folders with parents */ listRemove = g_list_append( listRemove, folder ); } else { /* Add to root folder */ ADDRITEM_PARENT(folder) = ADDRITEM_OBJECT(book->addressCache->rootFolder); } node = g_list_next( node ); } /* Process remove list */ node = listRemove; while( node ) { rootFolder->listFolder = g_list_remove( rootFolder->listFolder, node->data ); node = g_list_next( node ); } g_list_free( listRemove ); /* Move all unparented persons and groups into root folder */ g_hash_table_foreach( book->addressCache->itemHash, addrbook_res_items_vis, book ); /* Free up some more */ nodeFolder = book->tempList; while( nodeFolder ) { ItemFolder *folder = nodeFolder->data; g_list_free( folder->listItems ); folder->listItems = NULL; nodeFolder = g_list_next( nodeFolder ); } g_list_free( book->tempList ); book->tempList = NULL; } /* * Read address book file. */ gint addrbook_read_data( AddressBookFile *book ) { XMLFile *file = NULL; gchar *fileSpec = NULL; g_return_val_if_fail( book != NULL, -1 ); /* printf( "...addrbook_read_data :%s:\t:%s:\n", book->fileName, addrcache_get_name( book->addressCache ) ); */ fileSpec = g_strconcat( book->path, G_DIR_SEPARATOR_S, book->fileName, NULL ); book->retVal = MGU_OPEN_FILE; addrcache_clear( book->addressCache ); book->addressCache->modified = FALSE; book->addressCache->accessFlag = FALSE; file = xml_open_file( fileSpec ); g_free( fileSpec ); if( file ) { book->tempList = NULL; /* Trap for parsing errors. */ if( setjmp( book->jumper ) ) { xml_close_file( file ); return book->retVal; } addrbook_read_tree( book, file ); xml_close_file( file ); /* Resolve folder items */ addrbook_resolve_folder_items( book ); book->tempList = NULL; book->addressCache->modified = FALSE; book->addressCache->dataRead = TRUE; addrcache_set_dirty( book->addressCache, FALSE ); } return book->retVal; } static void addrbook_write_elem_s( FILE *fp, gint lvl, gchar *name ) { gint i; for( i = 0; i < lvl; i++ ) fputs( " ", fp ); fputs( "<", fp ); fputs( name, fp ); } static void addrbook_write_elem_e( FILE *fp, gint lvl, gchar *name ) { gint i; for( i = 0; i < lvl; i++ ) fputs( " ", fp ); fputs( "\n", fp ); } static void addrbook_write_attr( FILE *fp, gchar *name, gchar *value ) { fputs( " ", fp ); fputs( name, fp ); fputs( "=\"", fp ); xml_file_put_escape_str( fp, value ); fputs( "\"", fp ); } /* * Write file hash table visitor function. */ static void addrbook_write_item_person_vis( gpointer key, gpointer value, gpointer data ) { AddrItemObject *obj = ( AddrItemObject * ) value; FILE *fp = ( FILE * ) data; GList *node; if( ! obj ) return; if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) { ItemPerson *person = ( ItemPerson * ) value; if( person ) { addrbook_write_elem_s( fp, 1, AB_ELTAG_PERSON ); addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(person) ); addrbook_write_attr( fp, AB_ATTAG_FIRST_NAME, person->firstName ); addrbook_write_attr( fp, AB_ATTAG_LAST_NAME, person->lastName ); addrbook_write_attr( fp, AB_ATTAG_NICK_NAME, person->nickName ); addrbook_write_attr( fp, AB_ATTAG_COMMON_NAME, ADDRITEM_NAME(person) ); fputs( " >\n", fp); /* Output email addresses */ addrbook_write_elem_s( fp, 2, AB_ELTAG_ADDRESS_LIST ); fputs( ">\n", fp ); node = person->listEMail; while ( node ) { ItemEMail *email = node->data; addrbook_write_elem_s( fp, 3, AB_ELTAG_ADDRESS ); addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(email) ); addrbook_write_attr( fp, AB_ATTAG_ALIAS, ADDRITEM_NAME(email) ); addrbook_write_attr( fp, AB_ATTAG_EMAIL, email->address ); addrbook_write_attr( fp, AB_ATTAG_REMARKS, email->remarks ); fputs( " />\n", fp); node = g_list_next( node ); } addrbook_write_elem_e( fp, 2, AB_ELTAG_ADDRESS_LIST ); /* Output user attributes */ addrbook_write_elem_s( fp, 2, AB_ELTAG_ATTRIBUTE_LIST ); fputs( ">\n", fp ); node = person->listAttrib; while ( node ) { UserAttribute *attrib = node->data; addrbook_write_elem_s( fp, 3, AB_ELTAG_ATTRIBUTE ); addrbook_write_attr( fp, AB_ATTAG_UID, attrib->uid ); addrbook_write_attr( fp, AB_ATTAG_NAME, attrib->name ); fputs( " >", fp); xml_file_put_escape_str( fp, attrib->value ); addrbook_write_elem_e( fp, 0, AB_ELTAG_ATTRIBUTE ); node = g_list_next( node ); } addrbook_write_elem_e( fp, 2, AB_ELTAG_ATTRIBUTE_LIST ); addrbook_write_elem_e( fp, 1, AB_ELTAG_PERSON ); } } } /* * Write file hash table visitor function. */ static void addrbook_write_item_group_vis( gpointer key, gpointer value, gpointer data ) { AddrItemObject *obj = ( AddrItemObject * ) value; FILE *fp = ( FILE * ) data; GList *node; if( ! obj ) return; if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) { ItemGroup *group = ( ItemGroup * ) value; if( group ) { addrbook_write_elem_s( fp, 1, AB_ELTAG_GROUP ); addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(group) ); addrbook_write_attr( fp, AB_ATTAG_NAME, ADDRITEM_NAME(group) ); addrbook_write_attr( fp, AB_ATTAG_REMARKS, group->remarks ); fputs( " >\n", fp ); /* Output email address links */ addrbook_write_elem_s( fp, 2, AB_ELTAG_MEMBER_LIST ); fputs( ">\n", fp ); node = group->listEMail; while ( node ) { ItemEMail *email = node->data; ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(email); addrbook_write_elem_s( fp, 3, AB_ELTAG_MEMBER ); addrbook_write_attr( fp, AB_ATTAG_PID, ADDRITEM_ID(person) ); addrbook_write_attr( fp, AB_ATTAG_EID, ADDRITEM_ID(email) ); fputs( " />\n", fp ); node = g_list_next( node ); } addrbook_write_elem_e( fp, 2, AB_ELTAG_MEMBER_LIST ); addrbook_write_elem_e( fp, 1, AB_ELTAG_GROUP ); } } } /* * Write file hash table visitor function. */ static void addrbook_write_item_folder_vis( gpointer key, gpointer value, gpointer data ) { AddrItemObject *obj = ( AddrItemObject * ) value; FILE *fp = ( FILE * ) data; GList *node; if( ! obj ) return; if( ADDRITEM_TYPE(obj) == ITEMTYPE_FOLDER ) { ItemFolder *folder = ( ItemFolder * ) value; if( folder ) { addrbook_write_elem_s( fp, 1, AB_ELTAG_FOLDER ); addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(folder) ); addrbook_write_attr( fp, AB_ATTAG_NAME, ADDRITEM_NAME(folder) ); addrbook_write_attr( fp, AB_ATTAG_REMARKS, folder->remarks ); fputs( " >\n", fp ); addrbook_write_elem_s( fp, 2, AB_ELTAG_ITEM_LIST ); fputs( ">\n", fp ); /* Output persons */ node = folder->listPerson; while ( node ) { ItemPerson *item = node->data; addrbook_write_elem_s( fp, 3, AB_ELTAG_ITEM ); addrbook_write_attr( fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_PERSON ); addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(item ) ); fputs( " />\n", fp ); node = g_list_next( node ); } /* Output groups */ node = folder->listGroup; while ( node ) { ItemGroup *item = node->data; addrbook_write_elem_s( fp, 3, AB_ELTAG_ITEM ); addrbook_write_attr( fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_GROUP ); addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(item ) ); fputs( " />\n", fp ); node = g_list_next( node ); } /* Output folders */ node = folder->listFolder; while ( node ) { ItemFolder *item = node->data; addrbook_write_elem_s( fp, 3, AB_ELTAG_ITEM ); addrbook_write_attr( fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_FOLDER ); addrbook_write_attr( fp, AB_ATTAG_UID, ADDRITEM_ID(item ) ); fputs( " />\n", fp ); node = g_list_next( node ); } addrbook_write_elem_e( fp, 2, AB_ELTAG_ITEM_LIST ); addrbook_write_elem_e( fp, 1, AB_ELTAG_FOLDER ); } } } /* * Output address book data to specified file. * return: Status code. */ gint addrbook_write_to( AddressBookFile *book, gchar *newFile ) { FILE *fp; gchar *fileSpec; #ifndef DEV_STANDALONE PrefFile *pfile; #endif g_return_val_if_fail( book != NULL, -1 ); g_return_val_if_fail( newFile != NULL, -1 ); fileSpec = g_strconcat( book->path, G_DIR_SEPARATOR_S, newFile, NULL ); book->retVal = MGU_OPEN_FILE; #ifdef DEV_STANDALONE fp = fopen( fileSpec, "wb" ); g_free( fileSpec ); if( fp ) { fputs( "\n", fp ); #else pfile = prefs_write_open( fileSpec ); g_free( fileSpec ); if( pfile ) { fp = pfile->fp; fprintf( fp, "\n", conv_get_current_charset_str() ); #endif addrbook_write_elem_s( fp, 0, AB_ELTAG_ADDRESS_BOOK ); addrbook_write_attr( fp, AB_ATTAG_NAME, addrcache_get_name( book->addressCache ) ); fputs( " >\n", fp ); /* Output all persons */ g_hash_table_foreach( book->addressCache->itemHash, addrbook_write_item_person_vis, fp ); /* Output all groups */ g_hash_table_foreach( book->addressCache->itemHash, addrbook_write_item_group_vis, fp ); /* Output all folders */ g_hash_table_foreach( book->addressCache->itemHash, addrbook_write_item_folder_vis, fp ); addrbook_write_elem_e( fp, 0, AB_ELTAG_ADDRESS_BOOK ); book->retVal = MGU_SUCCESS; #ifdef DEV_STANDALONE fclose( fp ); #else if( prefs_write_close( pfile ) < 0 ) { book->retVal = MGU_ERROR_WRITE; } #endif } fileSpec = NULL; return book->retVal; } /* * Output address book data to original file. * return: Status code. */ gint addrbook_save_data( AddressBookFile *book ) { g_return_val_if_fail( book != NULL, -1 ); book->retVal = MGU_NO_FILE; if( book->fileName == NULL || *book->fileName == '\0' ) return book->retVal; if( book->path == NULL || *book->path == '\0' ) return book->retVal; addrbook_write_to( book, book->fileName ); if( book->retVal == MGU_SUCCESS ) { addrcache_set_dirty( book->addressCache, FALSE ); } return book->retVal; } /* ********************************************************************** * Address book edit interface functions... * *********************************************************************** */ /* * Move person's email item. * param: book Address book. * person Person. * itemMove Item to move. * itemTarget Target item before which to move item. */ ItemEMail *addrbook_move_email_before( AddressBookFile *book, ItemPerson *person, ItemEMail *itemMove, ItemEMail *itemTarget ) { ItemEMail *email = NULL; g_return_val_if_fail( book != NULL, NULL ); email = addritem_move_email_before( person, itemMove, itemTarget ); if( email ) { addrcache_set_dirty( book->addressCache, TRUE ); } return email; } /* * Move person's email item. * param: book Address book. * person Person. * itemMove Item to move. * itemTarget Target item after which to move item. */ ItemEMail *addrbook_move_email_after( AddressBookFile *book, ItemPerson *person, ItemEMail *itemMove, ItemEMail *itemTarget ) { ItemEMail *email = NULL; g_return_val_if_fail( book != NULL, NULL ); email = addritem_move_email_after( person, itemMove, itemTarget ); if( email ) { addrcache_set_dirty( book->addressCache, TRUE ); } return email; } /* * Hash table visitor function for deletion of hashtable entries. */ static gboolean addrbook_free_simple_hash_vis( gpointer *key, gpointer *value, gpointer *data ) { g_free( key ); key = NULL; value = NULL; return TRUE; } /* * Update address book email list for specified person. * Enter: book Address book. * person Person to update. * listEMail List of new email addresses. * Note: The existing email addresses are replaced with the new addresses. Any references * to old addresses in the groups are re-linked to the new addresses. All old addresses * linked to the person are removed. */ void addrbook_update_address_list( AddressBookFile *book, ItemPerson *person, GList *listEMail ) { GList *node; GList *listDelete; GList *listGroup; g_return_if_fail( book != NULL ); g_return_if_fail( person != NULL ); /* Get groups where person's existing email addresses are listed */ listGroup = addrcache_get_group_for_person( book->addressCache, person ); if( listGroup ) { GHashTable *hashEMail; GList *nodeGrp; /* Load hash table with new address entries */ hashEMail = g_hash_table_new( g_str_hash, g_str_equal ); node = listEMail; while( node ) { ItemEMail *email = node->data; gchar *addr = g_strdup( email->address ); g_strdown( addr ); if( ! g_hash_table_lookup( hashEMail, addr ) ) { g_hash_table_insert( hashEMail, addr, email ); } node = g_list_next( node ); } /* Re-parent new addresses to existing groups, where email address match. */ nodeGrp = listGroup; while( nodeGrp ) { ItemGroup *group = ( ItemGroup * ) nodeGrp->data; GList *groupEMail = group->listEMail; GList *nodeGrpEM; GList *listRemove = NULL; /* Process each email item linked to group */ nodeGrpEM = groupEMail; while( nodeGrpEM ) { ItemEMail *emailGrp = ( ItemEMail * ) nodeGrpEM->data; if( ADDRITEM_PARENT(emailGrp) == ADDRITEM_OBJECT(person) ) { /* Found an email address for this person */ ItemEMail *emailNew = NULL; gchar *addr = g_strdup( emailGrp->address ); g_strdown( addr ); emailNew = ( ItemEMail * ) g_hash_table_lookup( hashEMail, addr ); g_free( addr ); if( emailNew ) { /* Point to this entry */ nodeGrpEM->data = emailNew; } else { /* Mark for removal */ listRemove = g_list_append( listRemove, emailGrp ); } } /* Move on to next email link */ nodeGrpEM = g_list_next( nodeGrpEM ); } /* Process all removed links in current group */ nodeGrpEM = listRemove; while( nodeGrpEM ) { ItemEMail *emailGrp = nodeGrpEM->data; groupEMail = g_list_remove( groupEMail, emailGrp ); nodeGrpEM = g_list_next( nodeGrpEM ); } g_list_free( listRemove ); /* Move on to next group */ nodeGrp = g_list_next( nodeGrp ); } /* Clear hash table */ g_hash_table_foreach_remove( hashEMail, ( GHRFunc ) addrbook_free_simple_hash_vis, NULL ); g_hash_table_destroy( hashEMail ); hashEMail = NULL; g_list_free( listGroup ); listGroup = NULL; } /* Remove old addresses from person and cache */ listDelete = NULL; node = person->listEMail; while( node ) { ItemEMail *email = node->data; if( addrcache_person_remove_email( book->addressCache, person, email ) ) { addrcache_remove_email( book->addressCache, email ); } listDelete = g_list_append( listDelete, email ); node = person->listEMail; } /* Add new address entries */ node = listEMail; while( node ) { ItemEMail *email = node->data; if( ADDRITEM_ID(email) == NULL ) { /* Allocate an ID for new address */ addrcache_id_email( book->addressCache, email ); } addrcache_person_add_email( book->addressCache, person, email ); node = g_list_next( node ); } addrcache_set_dirty( book->addressCache, TRUE ); /* Free up memory */ g_list_free( listEMail ); listEMail = NULL; node = listDelete; while( node ) { ItemEMail *email = node->data; addritem_free_item_email( email ); node = g_list_next( node ); } g_list_free( listDelete ); listDelete = NULL; } /* * Add person and address data to address book. * Enter: book Address book. * folder Folder where to add person, or NULL for root folder. * listEMail New list of email addresses. * Return: Person added. * Note: A new person is created with specified list of email addresses. All objects inserted * into address book. */ ItemPerson *addrbook_add_address_list( AddressBookFile *book, ItemFolder *folder, GList *listEMail ) { ItemPerson *person; ItemFolder *f = folder; GList *node; g_return_val_if_fail( book != NULL, NULL ); if( ! f ) f = book->addressCache->rootFolder; person = addritem_create_item_person(); addrcache_id_person( book->addressCache, person ); addrcache_folder_add_person( book->addressCache, f, person ); node = listEMail; while( node ) { ItemEMail *email = node->data; if( ADDRITEM_ID(email) == NULL ) { addrcache_id_email( book->addressCache, email ); } addrcache_person_add_email( book->addressCache, person, email ); node = g_list_next( node ); } return person; } /* * Build available email list visitor function. */ static void addrbook_build_avail_email_vis( gpointer key, gpointer value, gpointer data ) { AddrItemObject *obj = ( AddrItemObject * ) value; if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) { AddressBookFile *book = data; ItemPerson *person = ( ItemPerson * ) obj; GList *node = person->listEMail; while( node ) { ItemEMail *email = node->data; /* gchar *newKey = g_strdup( ADDRITEM_ID(email) ); */ if( ! g_hash_table_lookup( book->tempHash, ADDRITEM_ID(email) ) ) { book->tempList = g_list_append( book->tempList, email ); } node = g_list_next( node ); } } } /* * Return link list of available email items (which have not already been linked to * groups). Note that the list contains references to items and should be g_free() * when done. Do *NOT* attempt to used the addrbook_free_xxx() functions... this will * destroy the addressbook data! * Return: List of items, or NULL if none. */ GList *addrbook_get_available_email_list( AddressBookFile *book, ItemGroup *group ) { GList *list = NULL; GHashTable *table; g_return_val_if_fail( book != NULL, NULL ); /* Load hash table with group email entries */ table = g_hash_table_new( g_str_hash, g_str_equal ); if( group ) { list = group->listEMail; while( list ) { ItemEMail *email = list->data; g_hash_table_insert( table, ADDRITEM_ID(email), email ); list = g_list_next( list ); } } /* Build list of available email addresses which exclude those already in groups */ book->tempList = NULL; book->tempHash = table; g_hash_table_foreach( book->addressCache->itemHash, addrbook_build_avail_email_vis, book ); list = book->tempList; book->tempList = NULL; book->tempHash = NULL; /* Clear hash table */ g_hash_table_destroy( table ); table = NULL; return list; } /* * Update address book email list for specified group. * Enter: book Address book. * group group to update. * listEMail New list of email addresses. This should *NOT* be g_free() when done. * Note: The existing email addresses are replaced with the new addresses. Any references * to old addresses in the groups are re-linked to the new addresses. All old addresses * linked to the person are removed. */ void addrbook_update_group_list( AddressBookFile *book, ItemGroup *group, GList *listEMail ) { GList *oldData; g_return_if_fail( book != NULL ); g_return_if_fail( group != NULL ); addrcache_set_dirty( book->addressCache, TRUE ); /* Remember old list */ oldData = group->listEMail; group->listEMail = listEMail; mgu_clear_list( oldData ); oldData = NULL; } /* * Add group and email list to address book. * Enter: book Address book. * folder Parent folder, or NULL for root folder. * listEMail New list of email addresses. This should *NOT* be g_free() when done. * Return: Group object. * Note: The existing email addresses are replaced with the new addresses. Any references * to old addresses in the groups are re-linked to the new addresses. All old addresses * linked to the person are removed. */ ItemGroup *addrbook_add_group_list( AddressBookFile *book, ItemFolder *folder, GList *listEMail ) { ItemGroup *group = NULL; ItemFolder *f = folder; g_return_val_if_fail( book != NULL, NULL ); if( ! f ) f = book->addressCache->rootFolder; group = addritem_create_item_group(); addrcache_id_group( book->addressCache, group ); addrcache_folder_add_group( book->addressCache, f, group ); group->listEMail = listEMail; return group; } /* * Add new folder to address book. * Enter: book Address book. * parent Parent folder. * Return: Folder that was added. This should *NOT* be g_free() when done. */ ItemFolder *addrbook_add_new_folder( AddressBookFile *book, ItemFolder *parent ) { ItemFolder *folder = NULL; ItemFolder *p = parent; g_return_val_if_fail( book != NULL, NULL ); if( ! p ) p = book->addressCache->rootFolder; folder = addritem_create_item_folder(); addrcache_id_folder( book->addressCache, folder ); if( addrcache_hash_add_folder( book->addressCache, folder ) ) { p->listFolder = g_list_append( p->listFolder, folder ); ADDRITEM_PARENT(folder) = ADDRITEM_OBJECT(p); addrcache_set_dirty( book->addressCache, TRUE ); } else { addritem_free_item_folder( folder ); folder = NULL; } return folder; } /* * Update address book attribute list for specified person. * Enter: book Address book. * person Person to update. * listAttrib New list of attributes. * Note: The existing email addresses are replaced with the new addresses. All old attributes * linked to the person are removed. */ void addrbook_update_attrib_list( AddressBookFile *book, ItemPerson *person, GList *listAttrib ) { GList *node; GList *oldData; g_return_if_fail( book != NULL ); g_return_if_fail( person != NULL ); /* Remember old list */ oldData = person->listAttrib; /* Attach new address list to person. */ node = listAttrib; while( node ) { UserAttribute *attrib = node->data; if( attrib->uid == NULL ) { /* Allocate an ID */ addrcache_id_attribute( book->addressCache, attrib ); } node = g_list_next( node ); } person->listAttrib = listAttrib; addrcache_set_dirty( book->addressCache, TRUE ); /* Free up old data */ addritem_free_list_attribute( oldData ); oldData = NULL; } /* * Add attribute data for person to address book. * Enter: book Address book. * person New person object. * listAttrib New list of attributes. * Note: Only attributes are inserted into address book. */ void addrbook_add_attrib_list( AddressBookFile *book, ItemPerson *person, GList *listAttrib ) { GList *node; g_return_if_fail( book != NULL ); g_return_if_fail( person != NULL ); node = listAttrib; while( node ) { UserAttribute *attrib = node->data; if( attrib->uid == NULL ) { addrcache_id_attribute( book->addressCache, attrib ); } addritem_person_add_attribute( person, attrib ); node = g_list_next( node ); } addrcache_set_dirty( book->addressCache, TRUE ); } /* * Return address book file for specified object. * Enter: aio Book item object. * Return: Address book, or NULL if not found. */ AddressBookFile *addrbook_item_get_bookfile( AddrItemObject *aio ) { AddressBookFile *book = NULL; if( aio ) { ItemFolder *parent = NULL; ItemFolder *root = NULL; if( aio->type == ITEMTYPE_EMAIL ) { ItemPerson *person = ( ItemPerson * ) ADDRITEM_PARENT(aio); if( person ) { parent = ( ItemFolder * ) ADDRITEM_PARENT(person); } } else { parent = ( ItemFolder * ) ADDRITEM_PARENT(aio); } if( parent ) { root = addrcache_find_root_folder( parent ); } if( root ) { book = ( AddressBookFile * ) ADDRITEM_PARENT(root); } } return book; } /* * Remove folder from address book. Children are re-parented to parent folder. * param: folder Folder to remove. * return: Folder, or NULL if not found. Note that object should still be freed. */ ItemFolder *addrbook_remove_folder( AddressBookFile *book, ItemFolder *folder ) { ItemFolder *f; g_return_val_if_fail( book != NULL, NULL ); f = addrcache_remove_folder( book->addressCache, folder ); return f; } /* * Remove folder from address book. Children are deleted. * param: folder Folder to remove. * return: Folder, or NULL if not found. Note that object should still be freed. */ ItemFolder *addrbook_remove_folder_delete( AddressBookFile *book, ItemFolder *folder ) { ItemFolder *f; g_return_val_if_fail( book != NULL, NULL ); f = addrcache_remove_folder_delete( book->addressCache, folder ); return f; } #define WORK_BUFLEN 1024 #define ADDRBOOK_DIGITS "0123456789" /* * Return list of existing address book files. * Enter: book Address book file. * Return: File list. */ GList *addrbook_get_bookfile_list( AddressBookFile *book ) { gchar *adbookdir; DIR *dp; struct dirent *entry; struct stat statbuf; gchar buf[ WORK_BUFLEN ]; gchar numbuf[ WORK_BUFLEN ]; gint len, lenpre, lensuf, lennum; long int val, maxval; GList *fileList = NULL; g_return_val_if_fail( book != NULL, NULL ); if( book->path == NULL || *book->path == '\0' ) { book->retVal = MGU_NO_PATH; return NULL; } strcpy( buf, book->path ); len = strlen( buf ); if( len > 0 ) { if( buf[ len-1 ] != G_DIR_SEPARATOR ) { buf[ len ] = G_DIR_SEPARATOR; buf[ ++len ] = '\0'; } } adbookdir = g_strdup( buf ); strcat( buf, ADDRBOOK_PREFIX ); if( ( dp = opendir( adbookdir ) ) == NULL ) { book->retVal = MGU_OPEN_DIRECTORY; g_free( adbookdir ); return NULL; } lenpre = strlen( ADDRBOOK_PREFIX ); lensuf = strlen( ADDRBOOK_SUFFIX ); lennum = FILE_NUMDIGITS + lenpre; maxval = -1; while( ( entry = readdir( dp ) ) != NULL ) { gchar *endptr = NULL; gint i; gboolean flg; strcpy( buf, adbookdir ); strcat( buf, entry->d_name ); stat( buf, &statbuf ); if( S_IFREG & statbuf.st_mode ) { if( strncmp( entry->d_name, ADDRBOOK_PREFIX, lenpre ) == 0 ) { if( strncmp( (entry->d_name) + lennum, ADDRBOOK_SUFFIX, lensuf ) == 0 ) { strncpy( numbuf, (entry->d_name) + lenpre, FILE_NUMDIGITS ); numbuf[ FILE_NUMDIGITS ] = '\0'; flg = TRUE; for( i = 0; i < FILE_NUMDIGITS; i++ ) { if( ! strchr( ADDRBOOK_DIGITS, numbuf[i] ) ) { flg = FALSE; break; } } if( flg ) { /* Get value */ val = strtol( numbuf, &endptr, 10 ); if( endptr && val > -1 ) { if( val > maxval ) maxval = val; fileList = g_list_append( fileList, g_strdup( entry->d_name ) ); } } } } } } closedir( dp ); g_free( adbookdir ); book->maxValue = maxval; book->retVal = MGU_SUCCESS; return fileList; } /* * Return file name for specified file number. * Enter: fileNum File number. * Return: File name, or NULL if file number too large. Should be g_free() when done. */ gchar *addrbook_gen_new_file_name( gint fileNum ) { gchar fmt[ 30 ]; gchar buf[ WORK_BUFLEN ]; gint n = fileNum; long int nmax; if( n < 1 ) n = 1; nmax = -1 + (long int) pow( 10, FILE_NUMDIGITS ); if( fileNum > nmax ) return NULL; g_snprintf( fmt, sizeof(fmt), "%%s%%0%dd%%s", FILE_NUMDIGITS ); g_snprintf( buf, sizeof(buf), fmt, ADDRBOOK_PREFIX, n, ADDRBOOK_SUFFIX ); return g_strdup( buf ); } /* ********************************************************************** * Address book test functions... * *********************************************************************** */ /* * Test email address list. */ static void addrbook_chkparse_addr_list( AddressBookFile *book, XMLFile *file ){ guint prev_level; GList *attr; for (;;) { prev_level = file->level; if( xml_parse_next_tag( file ) ) { longjmp( book->jumper, 1 ); } if (file->level < prev_level) return; attr = xml_get_current_tag_attr(file); /* addrbook_show_attribs( attr ); */ if( xml_compare_tag( file, AB_ELTAG_ADDRESS ) ) { addrbook_chkparse_addr_list( book, file ); } } } /* * Test user attributes for person. */ static void addrbook_chkparse_attribute( AddressBookFile *book, XMLFile *file ) { GList *attr; gchar *element; attr = xml_get_current_tag_attr(file); /* addrbook_show_attribs( attr ); */ element = xml_get_element( file ); /* printf( "\t\tattrib value : %s\n", element ); */ } /* * Test attribute list. */ static void addrbook_chkparse_attr_list( AddressBookFile *book, XMLFile *file ){ guint prev_level; for (;;) { prev_level = file->level; if( xml_parse_next_tag( file ) ) { longjmp( book->jumper, 1 ); } if (file->level < prev_level) return; if( xml_compare_tag( file, AB_ELTAG_ATTRIBUTE ) ) { addrbook_chkparse_attribute( book, file ); addrbook_chkparse_attr_list( book, file ); } } } /* * Test person. */ static void addrbook_chkparse_person( AddressBookFile *book, XMLFile *file ) { GList *attr; attr = xml_get_current_tag_attr(file); /* addrbook_show_attribs( attr ); */ if( xml_parse_next_tag( file ) ) { /* Consume closing tag */ longjmp( book->jumper, 1 ); } if( xml_compare_tag( file, AB_ELTAG_ADDRESS_LIST ) ) { addrbook_chkparse_addr_list( book, file ); } if( xml_parse_next_tag( file ) ) { /* Consume closing tag */ longjmp( book->jumper, 1 ); } if( xml_compare_tag( file, AB_ELTAG_ATTRIBUTE_LIST ) ) { addrbook_chkparse_attr_list( book, file ); } } /* * Test group member list. */ static void addrbook_chkparse_member_list( AddressBookFile *book, XMLFile *file ){ GList *attr; guint prev_level; for (;;) { prev_level = file->level; if( xml_parse_next_tag( file ) ) { longjmp( book->jumper, 1 ); } if (file->level < prev_level) return; if( xml_compare_tag( file, AB_ELTAG_MEMBER ) ) { attr = xml_get_current_tag_attr(file); /* addrbook_show_attribs( attr ); */ addrbook_chkparse_member_list( book, file ); } else { attr = xml_get_current_tag_attr( file ); /* addrbook_show_attribs( attr ); */ } } } /* * Test group. */ static void addrbook_chkparse_group( AddressBookFile *book, XMLFile *file ) { GList *attr; attr = xml_get_current_tag_attr(file); /* addrbook_show_attribs( attr ); */ if( xml_parse_next_tag( file ) ) { /* Consume closing tag */ longjmp( book->jumper, 1 ); } if( xml_compare_tag( file, AB_ELTAG_MEMBER_LIST ) ) { addrbook_chkparse_member_list( book, file ); } } /* * Test folder item list. */ static void addrbook_chkparse_folder_list( AddressBookFile *book, XMLFile *file ){ GList *attr; guint prev_level; for (;;) { prev_level = file->level; if( xml_parse_next_tag( file ) ) { longjmp( book->jumper, 1 ); } if (file->level < prev_level) return; if( xml_compare_tag( file, AB_ELTAG_ITEM ) ) { attr = xml_get_current_tag_attr(file); /* addrbook_show_attribs( attr ); */ addrbook_chkparse_folder_list( book, file ); } else { attr = xml_get_current_tag_attr( file ); /* addrbook_show_attribs( attr ); */ } } } /* * Test folder. */ static void addrbook_chkparse_folder( AddressBookFile *book, XMLFile *file ) { GList *attr; attr = xml_get_current_tag_attr(file); /* addrbook_show_attribs( attr ); */ if( xml_parse_next_tag( file ) ) { /* Consume closing tag */ longjmp( book->jumper, 1 ); } if( xml_compare_tag( file, AB_ELTAG_ITEM_LIST ) ) { addrbook_chkparse_folder_list( book, file ); } } /* * Test address book. */ static gboolean addrbook_chkread_tree( AddressBookFile *book, XMLFile *file ) { GList *attr; gboolean retVal; if( xml_get_dtd( file ) ) { return FALSE; } if( xml_parse_next_tag( file ) ) { return FALSE; } if( ! xml_compare_tag( file, AB_ELTAG_ADDRESS_BOOK ) ) { return FALSE; } attr = xml_get_current_tag_attr(file); /* addrbook_show_attribs( attr ); */ retVal = TRUE; for (;;) { if (! file->level ) break; /* Get item tag */ if( xml_parse_next_tag( file ) ) { longjmp( book->jumper, 1 ); } /* Get next tag (person, group or folder) */ if( xml_compare_tag( file, AB_ELTAG_PERSON ) ) { addrbook_chkparse_person( book, file ); } else if( xml_compare_tag( file, AB_ELTAG_GROUP ) ) { addrbook_chkparse_group( book, file ); } else if( xml_compare_tag( file, AB_ELTAG_FOLDER ) ) { addrbook_chkparse_folder( book, file ); } } return retVal; } /* * Test address book file by parsing contents. * Enter: book Address book file to check. * fileName File name to check. * Return: MGU_SUCCESS if file appears to be valid format. */ gint addrbook_test_read_file( AddressBookFile *book, gchar *fileName ) { XMLFile *file = NULL; gchar *fileSpec = NULL; g_return_val_if_fail( book != NULL, -1 ); fileSpec = g_strconcat( book->path, G_DIR_SEPARATOR_S, fileName, NULL ); book->retVal = MGU_OPEN_FILE; file = xml_open_file( fileSpec ); g_free( fileSpec ); if( file ) { book->retVal = MGU_BAD_FORMAT; if( setjmp( book->jumper ) ) { /* printf( "Caught Ya!!!\n" ); */ xml_close_file( file ); return book->retVal; } if( addrbook_chkread_tree( book, file ) ) { book->retVal = MGU_SUCCESS; } xml_close_file( file ); } return book->retVal; } /* * Return link list of all persons in address book. Note that the list contains * references to items. Do *NOT* attempt to use the addrbook_free_xxx() functions... * this will destroy the addressbook data! * Return: List of items, or NULL if none. */ GList *addrbook_get_all_persons( AddressBookFile *book ) { g_return_val_if_fail( book != NULL, NULL ); return addrcache_get_all_persons( book->addressCache ); } /* * Add person and address data to address book. * Enter: book Address book. * folder Folder where to add person, or NULL for root folder. * name Common name. * address EMail address. * remarks Remarks. * Return: Person added. Do not *NOT* to use the addrbook_free_xxx() functions... * this will destroy the address book data. */ ItemPerson *addrbook_add_contact( AddressBookFile *book, ItemFolder *folder, const gchar *name, const gchar *address, const gchar *remarks ) { g_return_val_if_fail( book != NULL, NULL ); return addrcache_add_contact( book->addressCache, folder, name, address, remarks ); } /* * Return file name for next address book file. * Enter: book Address book. * Return: File name, or NULL if could not create. This should be g_free() * when done. */ gchar *addrbook_guess_next_file( AddressBookFile *book ) { gchar *newFile = NULL; GList *fileList = NULL; gint fileNum = 1; fileList = addrbook_get_bookfile_list( book ); if( fileList ) { fileNum = 1 + book->maxValue; } newFile = addrbook_gen_new_file_name( fileNum ); g_list_free( fileList ); fileList = NULL; return newFile; } /* * End of Source. */