talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

addrcache.c (30019B)


      1 /*
      2  * Claws Mail -- a GTK based, lightweight, and fast e-mail client
      3  * Copyright (C) 2001-2012 Match Grun and the Claws Mail team
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License as published by
      7  * the Free Software Foundation; either version 3 of the License, or
      8  * (at your option) any later version.
      9  *
     10  * This program is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  * GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License
     16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
     17  *
     18  */
     19 
     20 /*
     21  * Functions to maintain address cache.
     22  */
     23 
     24 #include <stdio.h>
     25 #include <string.h>
     26 #include <sys/stat.h>
     27 
     28 #include "mgutils.h"
     29 #include "addrcache.h"
     30 #include "utils.h"
     31 
     32 #define ID_TIME_OFFSET             998000000
     33 #define ADDRCACHE_MAX_SEARCH_COUNT 1000
     34 
     35 static int _nextCacheID__ = 0;
     36 
     37 /*
     38  * Generate next cache ID.
     39  */
     40 static int addrcache_next_cache_id() {
     41 	int retVal;
     42 
     43 	if( _nextCacheID__ == 0 ) {
     44 		_nextCacheID__ = 1;
     45 	}
     46 	retVal = _nextCacheID__;
     47 	++_nextCacheID__;
     48 	return retVal;
     49 }
     50 
     51 /*
     52 * Create new address cache.
     53 */
     54 AddressCache *addrcache_create() {
     55 	AddressCache *cache;
     56 	gint t;
     57 
     58 	cache = g_new0( AddressCache, 1 );
     59 	cache->itemHash = g_hash_table_new( g_str_hash, g_str_equal );
     60 	cache->cacheID = g_strdup_printf( "%d", addrcache_next_cache_id() );
     61 
     62 	cache->dataRead = FALSE;
     63 	cache->modified = FALSE;
     64 	cache->dirtyFlag = FALSE;
     65 	cache->accessFlag = FALSE;
     66 	cache->name = NULL;
     67 	cache->modifyTime = 0;
     68 
     69 	/* Generate the next ID using system time */
     70 	cache->nextID = 1;
     71 	t = time( NULL );
     72 	if( t > 0 ) {
     73 		cache->nextID = t - ID_TIME_OFFSET;
     74 	}
     75 
     76 	cache->tempList = NULL;
     77 	cache->rootFolder = addritem_create_item_folder();
     78 	cache->rootFolder->isRoot = TRUE;
     79 	ADDRITEM_PARENT(cache->rootFolder) = NULL;
     80 	return cache;
     81 }
     82 
     83 /*
     84 * Properties.
     85 */
     86 ItemFolder *addrcache_get_root_folder( AddressCache *cache ) {
     87 	cm_return_val_if_fail( cache != NULL, NULL );
     88 	return cache->rootFolder;
     89 }
     90 GList *addrcache_get_list_folder( AddressCache *cache ) {
     91 	cm_return_val_if_fail( cache != NULL, NULL );
     92 	return cache->rootFolder->listFolder;
     93 }
     94 GList *addrcache_get_list_person( AddressCache *cache ) {
     95 	cm_return_val_if_fail( cache != NULL, NULL );
     96 	return cache->rootFolder->listPerson;
     97 }
     98 gboolean addrcache_get_dirty( AddressCache *cache ) {
     99 	cm_return_val_if_fail( cache != NULL, FALSE );
    100 	return cache->dirtyFlag;
    101 }
    102 void addrcache_set_dirty( AddressCache *cache, const gboolean value ) {
    103 	cm_return_if_fail( cache != NULL );
    104 	cache->dirtyFlag = value;
    105 }
    106 gchar *addrcache_get_name( AddressCache *cache ) {
    107 	cm_return_val_if_fail( cache != NULL, NULL );
    108 	return cache->name;
    109 }
    110 void addrcache_set_name( AddressCache *cache, const gchar *value ) {
    111 	cm_return_if_fail( cache != NULL );
    112 	cache->name = mgu_replace_string( cache->name, value );
    113 	g_strstrip( cache->name );
    114 	cache->dirtyFlag = TRUE;
    115 }
    116 
    117 /*
    118 * Generate next ID.
    119 */
    120 static void addrcache_next_id( AddressCache *cache ) {
    121 	cm_return_if_fail( cache != NULL );
    122 	cache->nextID++;
    123 }
    124 
    125 /*
    126 * Refresh internal variables. This can be used force a reload.
    127 */
    128 void addrcache_refresh( AddressCache *cache ) {
    129 	cache->dataRead = FALSE;
    130 	cache->modified = TRUE;
    131 	cache->accessFlag = FALSE;
    132 	cache->modifyTime = 0;
    133 }
    134 
    135 /*
    136 * Free hash table visitor function.
    137 */
    138 static gint addrcache_free_item_vis( gpointer key, gpointer value, gpointer data ) {
    139 	AddrItemObject *obj = ( AddrItemObject * ) value;
    140 
    141 	if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) {
    142 		addritem_free_item_person( ( ItemPerson * ) obj );
    143 	}
    144 	else if( ADDRITEM_TYPE(obj) == ITEMTYPE_EMAIL ) {
    145 		addritem_free_item_email( ( ItemEMail * ) obj );
    146 	}
    147 	else if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) {
    148 		addritem_free_item_group( ( ItemGroup * ) obj );
    149 	}
    150 	else if( ADDRITEM_TYPE(obj) == ITEMTYPE_FOLDER ) {
    151 		addritem_free_item_folder( ( ItemFolder * ) obj );
    152 	}
    153 	key = NULL;
    154 	value = NULL;
    155 	return TRUE;
    156 }
    157 
    158 /*
    159 * Free hash table of address cache items.
    160 */
    161 static void addrcache_free_item_hash( GHashTable *table ) {
    162 	cm_return_if_fail( table != NULL );
    163 	g_hash_table_foreach_remove( table, addrcache_free_item_vis, NULL );
    164 }
    165 
    166 /*
    167 * Free up folders and groups.
    168 */
    169 static void addrcache_free_all_folders( ItemFolder *parent ) {
    170 	GList *node;
    171 
    172 	if( parent == NULL ) return;
    173 
    174 	node = parent->listFolder;
    175 	while( node ) {
    176 		ItemFolder *folder = node->data;
    177 		addrcache_free_all_folders( folder );
    178 		node->data = NULL;
    179 		node = g_list_next( node );
    180 	}
    181 	g_list_free( parent->listPerson );
    182 	g_list_free( parent->listGroup );
    183 	g_list_free( parent->listFolder );
    184 	parent->listPerson = NULL;
    185 	parent->listGroup = NULL;
    186 	parent->listFolder = NULL;
    187 }
    188 
    189 /*
    190 * Clear the address cache.
    191 */
    192 void addrcache_clear( AddressCache *cache ) {
    193 	cm_return_if_fail( cache != NULL );
    194 
    195 	/* g_print( "...addrcache_clear :%s:\n", cache->name ); */
    196 	/* Free up folders and hash table */
    197 	addrcache_free_all_folders( cache->rootFolder );
    198 	addrcache_free_item_hash( cache->itemHash );
    199 	g_hash_table_destroy( cache->itemHash );
    200 	cache->itemHash = NULL;
    201 	ADDRITEM_PARENT(cache->rootFolder) = NULL;
    202 	addritem_free_item_folder( cache->rootFolder );
    203 	cache->rootFolder = NULL;
    204 	if( cache->tempList ) g_list_free( cache->tempList );
    205 	cache->tempList = NULL;
    206 
    207 	/* Reset to initial state */
    208 	cache->itemHash = g_hash_table_new( g_str_hash, g_str_equal );
    209 	cache->rootFolder = addritem_create_item_folder();
    210 	cache->rootFolder->isRoot = TRUE;
    211 	ADDRITEM_PARENT(cache->rootFolder) = NULL;
    212 
    213 	addrcache_refresh( cache );
    214 }
    215 
    216 /*
    217 * Free address cache.
    218 */
    219 void addrcache_free( AddressCache *cache ) {
    220 	cm_return_if_fail( cache != NULL );
    221 
    222 	cache->dirtyFlag = FALSE;
    223 	addrcache_free_all_folders( cache->rootFolder );
    224 	addrcache_free_item_hash( cache->itemHash );
    225 	g_hash_table_destroy( cache->itemHash );
    226 	cache->itemHash = NULL;
    227 	ADDRITEM_PARENT(cache->rootFolder) = NULL;
    228 	addritem_free_item_folder( cache->rootFolder );
    229 	cache->rootFolder = NULL;
    230 	g_list_free( cache->tempList );
    231 	cache->tempList = NULL;
    232 	g_free( cache->cacheID );
    233 	cache->cacheID = NULL;
    234 	g_free( cache->name );
    235 	cache->name = NULL;
    236 	g_free( cache );
    237 }
    238 
    239 /*
    240 * Check whether file has changed by comparing with cache.
    241 * return: TRUE if file has changed.
    242 */
    243 gboolean addrcache_check_file( AddressCache *cache, gchar *path ) {
    244 	gboolean retVal;
    245 	GStatBuf filestat;
    246 	retVal = TRUE;
    247 	if( path ) {
    248 		if( 0 == g_stat( path, &filestat ) ) {
    249 			if( filestat.st_mtime == cache->modifyTime ) retVal = FALSE;
    250 		}
    251 	}
    252 	return retVal;
    253 }
    254 
    255 /*
    256 * Save file time to cache.
    257 * return: TRUE if time marked.
    258 */
    259 gboolean addrcache_mark_file( AddressCache *cache, gchar *path ) {
    260 	gboolean retVal = FALSE;
    261 	GStatBuf filestat;
    262 	if( path ) {
    263 		if( 0 == g_stat( path, &filestat ) ) {
    264 			cache->modifyTime = filestat.st_mtime;
    265 			retVal = TRUE;
    266 		}
    267 	}
    268 	return retVal;
    269 }
    270 
    271 /*
    272 * Dump entire address cache hash table contents.
    273 */
    274 void addrcache_print( AddressCache *cache, FILE *stream ) {
    275 	cm_return_if_fail( cache != NULL );
    276 	fprintf( stream, "AddressCache:\n" );
    277 	fprintf( stream, "cache id : %s\n",  cache->cacheID );
    278 	fprintf( stream, "next id  : %d\n",  cache->nextID );
    279 	fprintf( stream, "name     : %s\n",  cache->name );
    280 	fprintf( stream, "mod time : %ld\n", (long int)cache->modifyTime );
    281 	fprintf( stream, "modified : %s\n",  cache->modified ? "yes" : "no" );
    282 	fprintf( stream, "data read: %s\n",  cache->dataRead ? "yes" : "no" );
    283 }
    284 
    285 /*
    286  * Allocate ID for person.
    287  */
    288 void addrcache_id_person( AddressCache *cache, ItemPerson *person ) {
    289 	cm_return_if_fail( cache != NULL );
    290 	cm_return_if_fail( person != NULL );
    291 	if( ADDRITEM_ID(person) ) return;
    292 	addrcache_next_id( cache );
    293 	ADDRITEM_ID(person) = g_strdup_printf( "%d", cache->nextID );
    294 }
    295 
    296 /*
    297  * Allocate ID for group.
    298  */
    299 void addrcache_id_group( AddressCache *cache, ItemGroup *group ) {
    300 	cm_return_if_fail( cache != NULL );
    301 	cm_return_if_fail( group != NULL );
    302 	if( ADDRITEM_ID(group) ) return;
    303 	addrcache_next_id( cache );
    304 	ADDRITEM_ID(group) = g_strdup_printf( "%d", cache->nextID );
    305 }
    306 
    307 /*
    308  * Allocate ID for folder.
    309  */
    310 void addrcache_id_folder( AddressCache *cache, ItemFolder *folder ) {
    311 	cm_return_if_fail( cache != NULL );
    312 	cm_return_if_fail( folder != NULL );
    313 	if( ADDRITEM_ID(folder) ) return;
    314 	addrcache_next_id( cache );
    315 	ADDRITEM_ID(folder) = g_strdup_printf( "%d", cache->nextID );
    316 }
    317 
    318 /*
    319  * Allocate ID for email address.
    320  */
    321 void addrcache_id_email( AddressCache *cache, ItemEMail *email ) {
    322 	cm_return_if_fail( cache != NULL );
    323 	cm_return_if_fail( email != NULL );
    324 	if( ADDRITEM_ID(email) ) return;
    325 	addrcache_next_id( cache );
    326 	ADDRITEM_ID(email) = g_strdup_printf( "%d", cache->nextID );
    327 }
    328 
    329 /*
    330  * Allocate ID for user attribute.
    331  */
    332 void addrcache_id_attribute( AddressCache *cache, UserAttribute *attrib ) {
    333 	cm_return_if_fail( cache != NULL );
    334 	cm_return_if_fail( attrib != NULL );
    335 	if( attrib->uid ) return;
    336 	addrcache_next_id( cache );
    337 	attrib->uid = g_strdup_printf( "%d", cache->nextID );
    338 }
    339 
    340 /*
    341 * Add person to hash table.
    342 * return: TRUE if item added.
    343 */
    344 gboolean addrcache_hash_add_person( AddressCache *cache, ItemPerson *person ) {
    345 	if( g_hash_table_lookup( cache->itemHash, ADDRITEM_ID(person) ) ) {
    346 		return FALSE;
    347 	}
    348 	g_hash_table_insert( cache->itemHash, ADDRITEM_ID(person), person );
    349 	return TRUE;
    350 }
    351 
    352 /*
    353 * Add email to hash table.
    354 * return: TRUE if item added.
    355 */
    356 static gboolean addrcache_hash_add_email( AddressCache *cache, ItemEMail *email ) {
    357 	if( g_hash_table_lookup( cache->itemHash, ADDRITEM_ID(email) ) ) {
    358 		return FALSE;
    359 	}
    360 	g_hash_table_insert( cache->itemHash, ADDRITEM_ID(email), email );
    361 	return TRUE;
    362 }
    363 
    364 /*
    365 * Add group to hash table.
    366 * return: TRUE if item added.
    367 */
    368 gboolean addrcache_hash_add_group( AddressCache *cache, ItemGroup *group ) {
    369 	cm_return_val_if_fail( cache != NULL, FALSE );
    370 	cm_return_val_if_fail( group != NULL, FALSE );
    371 
    372 	if( g_hash_table_lookup( cache->itemHash, ADDRITEM_ID(group) ) ) {
    373 		return FALSE;
    374 	}
    375 	g_hash_table_insert( cache->itemHash, ADDRITEM_ID(group), group );
    376 	return TRUE;
    377 }
    378 
    379 /*
    380 * Add folder to hash table.
    381 * return: TRUE if item added.
    382 */
    383 gboolean addrcache_hash_add_folder( AddressCache *cache, ItemFolder *folder ) {
    384 	cm_return_val_if_fail( cache != NULL, FALSE );
    385 	cm_return_val_if_fail( folder != NULL, FALSE );
    386 
    387 	if( g_hash_table_lookup( cache->itemHash, ADDRITEM_ID(folder) ) ) {
    388 		return FALSE;
    389 	}
    390 	g_hash_table_insert( cache->itemHash, ADDRITEM_ID(folder), folder );
    391 	return TRUE;
    392 }
    393 
    394 /*
    395 * Add person to specified folder in cache.
    396 */
    397 gboolean addrcache_folder_add_person( AddressCache *cache, ItemFolder *folder, ItemPerson *item ) {
    398 	gboolean retVal = FALSE;
    399 
    400 	cm_return_val_if_fail( cache != NULL, FALSE );
    401 	cm_return_val_if_fail( folder != NULL, FALSE );
    402 	cm_return_val_if_fail( item != NULL, FALSE );
    403 
    404 	retVal = addrcache_hash_add_person( cache, item );
    405 	if( retVal ) {
    406 		addritem_folder_add_person( folder, item );
    407 		cache->dirtyFlag = TRUE;
    408 	}
    409 	return retVal;
    410 }
    411 
    412 /*
    413 * Add folder to specified folder in cache.
    414 */
    415 gboolean addrcache_folder_add_folder( AddressCache *cache, ItemFolder *folder, ItemFolder *item ) {
    416 	gboolean retVal = FALSE;
    417 
    418 	cm_return_val_if_fail( cache != NULL, FALSE );
    419 	cm_return_val_if_fail( folder != NULL, FALSE );
    420 	cm_return_val_if_fail( item != NULL, FALSE );
    421 
    422 	retVal = addrcache_hash_add_folder( cache, item );
    423 	if( retVal ) {
    424 		addritem_folder_add_folder( folder, item );
    425 		cache->dirtyFlag = TRUE;
    426 	}
    427 	return TRUE;
    428 }
    429 
    430 /*
    431 * Add folder to specified folder in cache.
    432 */
    433 gboolean addrcache_folder_add_group( AddressCache *cache, ItemFolder *folder, ItemGroup *item ) {
    434 	gboolean retVal = FALSE;
    435 
    436 	cm_return_val_if_fail( cache != NULL, FALSE );
    437 	cm_return_val_if_fail( folder != NULL, FALSE );
    438 	cm_return_val_if_fail( item != NULL, FALSE );
    439 
    440 	retVal = addrcache_hash_add_group( cache, item );
    441 	if( retVal ) {
    442 		addritem_folder_add_group( folder, item );
    443 		cache->dirtyFlag = TRUE;
    444 	}
    445 	return retVal;
    446 }
    447 
    448 /*
    449 * Add person to address cache.
    450 * return: TRUE if item added.
    451 */
    452 gboolean addrcache_add_person( AddressCache *cache, ItemPerson *person ) {
    453 	gboolean retVal = FALSE;
    454 
    455 	cm_return_val_if_fail( cache != NULL, FALSE );
    456 	cm_return_val_if_fail( person != NULL, FALSE );
    457 
    458 	retVal = addrcache_hash_add_person( cache, person );
    459 	if( retVal ) {
    460 		addritem_folder_add_person( cache->rootFolder, person );
    461 		cache->dirtyFlag = TRUE;
    462 	}
    463 	return retVal;
    464 }
    465 
    466 /*
    467 * Add EMail address to person.
    468 * return: TRUE if item added.
    469 */
    470 gboolean addrcache_person_add_email( AddressCache *cache, ItemPerson *person, ItemEMail *email ) {
    471 	gboolean retVal = FALSE;
    472 
    473 	cm_return_val_if_fail( cache != NULL, FALSE );
    474 	cm_return_val_if_fail( person != NULL, FALSE );
    475 	cm_return_val_if_fail( email != NULL, FALSE );
    476 
    477 	retVal = addrcache_hash_add_email( cache, email );
    478 	if( retVal ) {
    479 		addritem_person_add_email( person, email );
    480 		cache->dirtyFlag = TRUE;
    481 	}
    482 	return retVal;
    483 }
    484 
    485 /*
    486 * Add group to address cache.
    487 * return: TRUE if item added.
    488 */
    489 gboolean addrcache_add_group( AddressCache *cache, ItemGroup *group ) {
    490 	gboolean retVal = FALSE;
    491 
    492 	cm_return_val_if_fail( cache != NULL, FALSE );
    493 	cm_return_val_if_fail( group != NULL, FALSE );
    494 
    495 	retVal = addrcache_hash_add_group( cache, group );
    496 	if( retVal ) {
    497 		addritem_folder_add_group( cache->rootFolder, group );
    498 		cache->dirtyFlag = TRUE;
    499 	}
    500 	return retVal;
    501 }
    502 
    503 /*
    504 * Add EMail address to person.
    505 * return: TRUE if item added.
    506 */
    507 gboolean addrcache_group_add_email( AddressCache *cache, ItemGroup *group, ItemEMail *email ) {
    508 	cm_return_val_if_fail( cache != NULL, FALSE );
    509 	cm_return_val_if_fail( group != NULL, FALSE );
    510 	cm_return_val_if_fail( email != NULL, FALSE );
    511 
    512 	addritem_group_add_email( group, email );
    513 	cache->dirtyFlag = TRUE;
    514 	return TRUE;
    515 }
    516 
    517 /*
    518 * Add folder to address cache.
    519 * return: TRUE if item added.
    520 */
    521 gboolean addrcache_add_folder( AddressCache *cache, ItemFolder *folder ) {
    522 	gboolean retVal = FALSE;
    523 
    524 	cm_return_val_if_fail( cache != NULL, FALSE );
    525 	cm_return_val_if_fail( folder != NULL, FALSE );
    526 
    527 	retVal = addrcache_hash_add_folder( cache, folder );
    528 	if( retVal ) {
    529 		addritem_folder_add_folder( cache->rootFolder, folder );
    530 		cache->dirtyFlag = TRUE;
    531 	}
    532 	return retVal;
    533 }
    534 
    535 /*
    536 * Move person to destination folder.
    537 * Enter: cache  Cache.
    538 *        person Person to move.
    539 *        target Target folder.
    540 */
    541 void addrcache_folder_move_person(
    542 	AddressCache *cache, ItemPerson *person, ItemFolder *target )
    543 {
    544 	ItemFolder *parent;
    545 
    546 	cm_return_if_fail( cache != NULL );
    547 	cm_return_if_fail( person != NULL );
    548 
    549 	parent = ( ItemFolder * ) ADDRITEM_PARENT(person);
    550 	if( ! parent ) parent = cache->rootFolder;
    551 	parent->listPerson = g_list_remove( parent->listPerson, person );
    552 	target->listPerson = g_list_append( target->listPerson, person );
    553 	ADDRITEM_PARENT(person) = ADDRITEM_OBJECT(target);
    554 	cache->dirtyFlag = TRUE;
    555 }
    556 
    557 /*
    558 * Move group to destination folder.
    559 * Enter: cache  Cache.
    560 *        group  Group to move.
    561 *        target Target folder.
    562 */
    563 void addrcache_folder_move_group(
    564 	AddressCache *cache, ItemGroup *group, ItemFolder *target )
    565 {
    566 	ItemFolder *parent;
    567 
    568 	cm_return_if_fail( cache != NULL );
    569 	cm_return_if_fail( group != NULL );
    570 
    571 	parent = ( ItemFolder * ) ADDRITEM_PARENT(group);
    572 	if( ! parent ) parent = cache->rootFolder;
    573 	parent->listGroup = g_list_remove( parent->listGroup, group );
    574 	target->listGroup = g_list_append( target->listGroup, group );
    575 	ADDRITEM_PARENT(group) = ADDRITEM_OBJECT(target);
    576 	cache->dirtyFlag = TRUE;
    577 }
    578 
    579 /*
    580 * Move folder to destination folder.
    581 * Enter: cache  Cache.
    582 *        folder Folder to move.
    583 *        target Target folder.
    584 */
    585 void addrcache_folder_move_folder(
    586 	AddressCache *cache, ItemFolder *folder, ItemFolder *target )
    587 {
    588 	ItemFolder *parent;
    589 
    590 	cm_return_if_fail( cache != NULL );
    591 	cm_return_if_fail( folder != NULL );
    592 
    593 	parent = ( ItemFolder * ) ADDRITEM_PARENT(folder);
    594 	if( ! parent ) parent = cache->rootFolder;
    595 	parent->listFolder = g_list_remove( parent->listFolder, folder );
    596 	target->listFolder = g_list_append( target->listFolder, folder );
    597 	ADDRITEM_PARENT(folder) = ADDRITEM_OBJECT(target);
    598 	cache->dirtyFlag = TRUE;
    599 }
    600 
    601 /*
    602 * Return pointer to object (either person or group) for specified ID.
    603 * param: uid Object ID.
    604 * return: Object, or NULL if not found.
    605 */
    606 AddrItemObject *addrcache_get_object( AddressCache *cache, const gchar *uid ) {
    607 	AddrItemObject *obj = NULL;
    608 	gchar *uidH;
    609 
    610 	cm_return_val_if_fail( cache != NULL, NULL );
    611 
    612 	if( uid == NULL || *uid == '\0' ) return NULL;
    613 	obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid );
    614 	if( obj ) {
    615 		/* Check for matching UID */
    616 		uidH = ADDRITEM_ID(obj);
    617 		if( uidH ) {
    618 			if( strcmp( uidH, uid ) == 0 ) return obj;
    619 		}
    620 	}
    621 	return NULL;
    622 }
    623 
    624 /*
    625 * Find email address in address cache.
    626 * param: eid	EMail ID.
    627 * return: email object for specified object ID and email ID, or NULL if not found.
    628 */
    629 ItemEMail *addrcache_get_email( AddressCache *cache, const gchar *eid ) {
    630 	ItemEMail *email = NULL;
    631 	AddrItemObject *obj = addrcache_get_object( cache, eid );
    632 
    633 	if( obj ) {
    634 		if( ADDRITEM_TYPE(obj) == ITEMTYPE_EMAIL ) {
    635 			email = ( ItemEMail * ) obj;
    636 		}
    637 	}
    638 	return email;
    639 }
    640 
    641 /*
    642 * Remove group from address cache.
    643 * param: group	Group to remove.
    644 * return: Group, or NULL if not found. Note that object should still be freed.
    645 */
    646 ItemGroup *addrcache_remove_group( AddressCache *cache, ItemGroup *group ) {
    647 	AddrItemObject *obj = NULL;
    648 
    649 	cm_return_val_if_fail( cache != NULL, NULL );
    650 
    651 	if( group ) {
    652 		gchar *uid = ADDRITEM_ID(group);
    653 		if( uid == NULL || *uid == '\0' ) return NULL;
    654 		obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid );
    655 		if( obj ) {
    656 			ItemFolder *parent = ( ItemFolder * ) ADDRITEM_PARENT(group);
    657 			if( ! parent ) parent = cache->rootFolder;
    658 
    659 			/* Remove group from parent's list and hash table */
    660 			parent->listGroup = g_list_remove( parent->listGroup, obj );
    661 			g_hash_table_remove( cache->itemHash, uid );
    662 			cache->dirtyFlag = TRUE;
    663 			return group;
    664 		}
    665 	}
    666 	return NULL;
    667 }
    668 
    669 /*
    670 * Remove specified email from address cache. Note that object is only
    671 * removed from cache and not parent objects.
    672 * param: email	EMail to remove.
    673 * return: EMail, or NULL if not found. Note that object should still be freed.
    674 */
    675 ItemEMail *addrcache_remove_email( AddressCache *cache, ItemEMail *email ) {
    676 	AddrItemObject *obj = NULL;
    677 
    678 	cm_return_val_if_fail( cache != NULL, NULL );
    679 
    680 	if( email ) {
    681 		gchar *eid = ADDRITEM_ID(email);
    682 		if( eid == NULL || *eid == '\0' ) return NULL;
    683 		obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, eid );
    684 		if( obj ) {
    685 			if( ADDRITEM_TYPE(obj) == ITEMTYPE_EMAIL ) {
    686 				/* Remove email addresses from hash table. */
    687 				g_hash_table_remove( cache->itemHash, eid );
    688 				cache->dirtyFlag = TRUE;
    689 				return email;
    690 			}
    691 		}
    692 	}
    693 	return NULL;
    694 }
    695 
    696 /*
    697 * Hash table visitor function to remove email from group.
    698 */
    699 static void addrcache_allgrp_rem_email_vis( gpointer key, gpointer value, gpointer data ) {
    700 	AddrItemObject *obj = ( AddrItemObject * ) value;
    701 	ItemEMail *email = ( ItemEMail * ) data;
    702 
    703 	if( ! email ) return;
    704 	if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) {
    705 		ItemGroup *group = ( ItemGroup * ) value;
    706 		if( group ) {
    707 			/* Remove each email address that belongs to the person from the list */
    708 			group->listEMail = g_list_remove( group->listEMail, email );
    709 		}
    710 	}
    711 }
    712 
    713 /*
    714 * Remove specified person from address cache.
    715 * param: person	Person to remove.
    716 * return: Person, or NULL if not found. Note that object should still be freed.
    717 */
    718 ItemPerson *addrcache_remove_person( AddressCache *cache, ItemPerson *person ) {
    719 	AddrItemObject *obj = NULL;
    720 	gchar *uid;
    721 
    722 	cm_return_val_if_fail( cache != NULL, NULL );
    723 
    724 	if( person ) {
    725 		uid = ADDRITEM_ID(person);
    726 		if( uid == NULL || *uid == '\0' ) return NULL;
    727 		obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid );
    728 		if( obj ) {
    729 			if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) {
    730 				ItemFolder *parent;
    731 				GList *node;
    732 
    733 				/* Remove all email addresses for person */
    734 				/* from groups and from hash table */
    735 				node = person->listEMail;
    736 				while( node ) {
    737 					ItemEMail *email;
    738 					gchar *eid;
    739 
    740 					email = node->data;
    741 					g_hash_table_foreach( cache->itemHash,
    742 						addrcache_allgrp_rem_email_vis, email );
    743 					eid = ADDRITEM_ID( email );
    744 					g_hash_table_remove( cache->itemHash, eid );
    745 					node = g_list_next( node );
    746 				}
    747 
    748 				/* Remove person from owning folder */
    749 				parent = ( ItemFolder * ) ADDRITEM_PARENT(person);
    750 				if( ! parent ) parent = cache->rootFolder;
    751 				parent->listPerson = g_list_remove( parent->listPerson, person );
    752 				g_hash_table_remove( cache->itemHash, uid );
    753 				cache->dirtyFlag = TRUE;
    754 				return person;
    755 			}
    756 		}
    757 	}
    758 	return NULL;
    759 }
    760 
    761 /*
    762 * Remove email address in address cache for specified person.
    763 * param: person	Person.
    764 *        email	EMail to remove.
    765 * return: EMail object, or NULL if not found. Note that object should still be freed.
    766 */
    767 ItemEMail *addrcache_person_remove_email( AddressCache *cache, ItemPerson *person, ItemEMail *email ) {
    768 	ItemEMail *found = NULL;
    769 
    770 	cm_return_val_if_fail( cache != NULL, NULL );
    771 
    772 	if( person && email ) {
    773 		found = addritem_person_remove_email( person, email );
    774 		if( found ) {
    775 			/* Remove email from all groups. */
    776 			g_hash_table_foreach( cache->itemHash, addrcache_allgrp_rem_email_vis, email );
    777 
    778 			/* Remove email from person's address list */
    779 			if( person->listEMail ) {
    780 				person->listEMail = g_list_remove( person->listEMail, email );
    781 			}
    782 			/* Unlink reference to person. */
    783 			ADDRITEM_PARENT(email) = NULL;
    784 			cache->dirtyFlag = TRUE;
    785 		}
    786 	}
    787 	return found;
    788 }
    789 
    790 /*
    791 * Group visitor function.
    792 */
    793 static void addrcache_get_grp_person_vis( gpointer key, gpointer value, gpointer data ) {
    794 	AddrItemObject *obj = ( AddrItemObject * ) value;
    795 
    796 	if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) {
    797 		AddressCache *cache = data;
    798 		ItemGroup *group = ( ItemGroup * ) obj;
    799 		ItemPerson *person = ( ItemPerson * ) cache->tempList->data;
    800 		GList *node = group->listEMail;
    801 		while( node ) {
    802 			ItemEMail *email = ( ItemEMail * ) node->data;
    803 			if( ADDRITEM_PARENT(email) == ADDRITEM_OBJECT(person) ) {
    804 				if( ! g_list_find( cache->tempList, group ) ) {
    805 					cache->tempList = g_list_append( cache->tempList, group );
    806 				}
    807 			}
    808 			node = g_list_next( node );
    809 		}
    810 	}
    811 }
    812 
    813 /*
    814 * Return linked list of groups which contain a reference to specified person's email
    815 * address.
    816 */
    817 GList *addrcache_get_group_for_person( AddressCache *cache, ItemPerson *person ) {
    818 	GList *list = NULL;
    819 
    820 	cm_return_val_if_fail( cache != NULL, NULL );
    821 
    822 	cache->tempList = NULL;
    823 	cache->tempList = g_list_append( cache->tempList, person );
    824 	g_hash_table_foreach( cache->itemHash, addrcache_get_grp_person_vis, cache );
    825 	cache->tempList = g_list_remove( cache->tempList, person );
    826 	list = cache->tempList;
    827 	cache->tempList = NULL;
    828 	return list;
    829 }
    830 
    831 /*
    832 * Get all person visitor function.
    833 */
    834 static void addrcache_get_all_persons_vis( gpointer key, gpointer value, gpointer data ) {
    835 	AddrItemObject *obj = ( AddrItemObject * ) value;
    836 
    837 	if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) {
    838 		AddressCache *cache = data;
    839 		cache->tempList = g_list_append( cache->tempList, obj );
    840 	}
    841 }
    842 
    843 /*
    844 * Return link list of all persons in address cache.  Note that the list contains
    845 * references to items. Do *NOT* attempt to use the addrcache_free_xxx() functions...
    846 * this will destroy the address cache data!
    847 * Return: List of items, or NULL if none.
    848 */
    849 GList *addrcache_get_all_persons( AddressCache *cache ) {
    850 	GList *list = NULL;
    851 
    852 	cm_return_val_if_fail( cache != NULL, NULL );
    853 
    854 	cache->tempList = NULL;
    855 	g_hash_table_foreach( cache->itemHash, addrcache_get_all_persons_vis, cache );
    856 	list = cache->tempList;
    857 	cache->tempList = NULL;
    858 	return list;
    859 }
    860 
    861 /*
    862 * Get all groups visitor function.
    863 */
    864 static void addrcache_get_all_groups_vis( gpointer key, gpointer value, gpointer data ) {
    865 	AddrItemObject *obj = ( AddrItemObject * ) value;
    866 
    867 	if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) {
    868 		AddressCache *cache = data;
    869 		cache->tempList = g_list_append( cache->tempList, obj );
    870 	}
    871 }
    872 
    873 /*
    874 * Return link list of all groups in address cache.  Note that the list contains
    875 * references to items. Do *NOT* attempt to use the addrcache_free_xxx() functions...
    876 * this will destroy the address cache data!
    877 * Return: List of items, or NULL if none.
    878 */
    879 GList *addrcache_get_all_groups( AddressCache *cache ) {
    880 	GList *list = NULL;
    881 
    882 	cm_return_val_if_fail( cache != NULL, NULL );
    883 
    884 	cache->tempList = NULL;
    885 	g_hash_table_foreach( cache->itemHash, addrcache_get_all_groups_vis, cache );
    886 	list = cache->tempList;
    887 	cache->tempList = NULL;
    888 	return list;
    889 }
    890 
    891 /*
    892 * Remove folder from cache. Children are re-parented to parent folder.
    893 * param: folder Folder to remove.
    894 * return: Folder, or NULL if not found. Note that object should still be freed.
    895 */
    896 ItemFolder *addrcache_remove_folder( AddressCache *cache, ItemFolder *folder ) {
    897 	AddrItemObject *obj = NULL;
    898 
    899 	cm_return_val_if_fail( cache != NULL, NULL );
    900 
    901 	if( folder ) {
    902 		gchar *uid = ADDRITEM_ID(folder);
    903 		if( uid == NULL || *uid == '\0' ) return NULL;
    904 		obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid );
    905 		if( obj ) {
    906 			ItemFolder *parent = ( ItemFolder * ) ADDRITEM_PARENT(folder);
    907 			GList *node;
    908 			AddrItemObject *aio;
    909 			if( ! parent ) parent = cache->rootFolder;
    910 
    911 			/* Re-parent children in folder */
    912 			node = folder->listFolder;
    913 			while( node ) {
    914 				aio = ( AddrItemObject * ) node->data;
    915 				parent->listFolder = g_list_append( parent->listFolder, aio );
    916 				aio->parent = ADDRITEM_OBJECT(parent);
    917 				node = g_list_next( node );
    918 			}
    919 			node = folder->listPerson;
    920 			while( node ) {
    921 				aio = ( AddrItemObject * ) node->data;
    922 				parent->listPerson = g_list_append( parent->listPerson, aio );
    923 				aio->parent = ADDRITEM_OBJECT(parent);
    924 				node = g_list_next( node );
    925 			}
    926 			node = folder->listGroup;
    927 			while( node ) {
    928 				aio = ( AddrItemObject * ) node->data;
    929 				parent->listGroup = g_list_append( parent->listGroup, aio );
    930 				aio->parent = ADDRITEM_OBJECT(parent);
    931 				node = g_list_next( node );
    932 			}
    933 
    934 			/* Remove folder from parent's list and hash table */
    935 			parent->listFolder = g_list_remove( parent->listFolder, folder );
    936 			ADDRITEM_PARENT(folder) = NULL;
    937 			g_hash_table_remove( cache->itemHash, uid );
    938 			cache->dirtyFlag = TRUE;
    939 			return folder;
    940 		}
    941 	}
    942 	return NULL;
    943 }
    944 
    945 /*
    946 * Remove folder from cache. Children are deleted.
    947 * param: folder Folder to remove.
    948 * return: Folder, or NULL if not found. Note that object should still be freed.
    949 */
    950 ItemFolder *addrcache_remove_folder_delete( AddressCache *cache, ItemFolder *folder ) {
    951 	AddrItemObject *obj = NULL;
    952 
    953 	cm_return_val_if_fail( cache != NULL, NULL );
    954 
    955 	if( folder ) {
    956 		gchar *uid = ADDRITEM_ID(folder);
    957 		if( uid == NULL || *uid == '\0' ) return NULL;
    958 		obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid );
    959 		if( obj ) {
    960 			ItemFolder *parent = ( ItemFolder * ) ADDRITEM_PARENT(folder);
    961 			if( ! parent ) parent = cache->rootFolder;
    962 
    963 			/* Remove groups */
    964 			while( folder->listGroup ) {
    965 				ItemGroup *item = ( ItemGroup * ) folder->listGroup->data;
    966 				item = addrcache_remove_group( cache, item );
    967 				if( item ) {
    968 					addritem_free_item_group( item );
    969 					item = NULL;
    970 				}
    971 			}
    972 
    973 			while( folder->listPerson ) {
    974 				ItemPerson *item = ( ItemPerson * ) folder->listPerson->data;
    975 				item = addrcache_remove_person( cache, item );
    976 				if( item ) {
    977 					addritem_free_item_person( item );
    978 					item = NULL;
    979 				}
    980 			}
    981 
    982 			/* Recursive deletion of folder */
    983 			while( folder->listFolder ) {
    984 				ItemFolder *item = ( ItemFolder * ) folder->listFolder->data;
    985 				item = addrcache_remove_folder_delete( cache, item );
    986 				if( item ) {
    987 					addritem_free_item_folder( item );
    988 					item = NULL;
    989 				}
    990 			}
    991 
    992 			/* Remove folder from parent's list and hash table */
    993 			parent->listFolder = g_list_remove( parent->listFolder, folder );
    994 			ADDRITEM_PARENT(folder) = NULL;
    995 			g_hash_table_remove( cache->itemHash, uid );
    996 			cache->dirtyFlag = TRUE;
    997 			return folder;
    998 		}
    999 	}
   1000 	return NULL;
   1001 }
   1002 
   1003 /**
   1004  * Add person and address data to cache.
   1005  * \param cache     Cache.
   1006  * \param folder    Folder where to add person, or NULL for root folder.
   1007  * \param name      Common name.
   1008  * \param address   EMail address.
   1009  * \param remarks   Remarks.
   1010  * \return Person added. Do not *NOT* to use the
   1011  *         <code>addrbook_free_xxx()</code> functions...; this will destroy
   1012  *         the address book data.
   1013  */
   1014 ItemPerson *addrcache_add_contact(
   1015 		AddressCache *cache, ItemFolder *folder, const gchar *name,
   1016 		const gchar *address, const gchar *remarks )
   1017 {
   1018 	ItemPerson *person = NULL;
   1019 	ItemEMail *email = NULL;
   1020 	ItemFolder *f = folder;
   1021 
   1022 	cm_return_val_if_fail( cache != NULL, NULL );
   1023 
   1024 	if( ! f ) f = cache->rootFolder;
   1025 
   1026 	/* Create person object */
   1027 	person = addritem_create_item_person();
   1028 	addritem_person_set_common_name( person, name );
   1029 	addrcache_id_person( cache, person );
   1030 	addrcache_folder_add_person( cache, f, person );
   1031 
   1032 	/* Create email object */
   1033 	email = addritem_create_item_email();
   1034 	addritem_email_set_address( email, address );
   1035 	addritem_email_set_remarks( email, remarks );
   1036 	addrcache_id_email( cache, email );
   1037 	addritem_person_add_email( person, email );
   1038 	cache->dirtyFlag = TRUE;
   1039 
   1040 	return person;
   1041 }
   1042 
   1043 /**
   1044  * Create a new folder and add to address cache.
   1045  * \param  cache  Address cache.
   1046  * \param  folder Parent folder where to add folder, or <i>NULL</i> for
   1047  *                root folder.
   1048  * \return Folder that was created. This should <b>*NOT*</b> be
   1049  *         <code>g_free()</code> when done.
   1050  */
   1051 ItemFolder *addrcache_add_new_folder( AddressCache *cache, ItemFolder *parent )
   1052 {
   1053 	ItemFolder *folder;
   1054 	ItemFolder *p = parent;
   1055 
   1056 	cm_return_val_if_fail( cache != NULL, NULL );
   1057 
   1058 	if( !p ) p = cache->rootFolder;
   1059 	folder = addritem_create_item_folder();
   1060 	addrcache_id_folder( cache, folder );
   1061 	if( addrcache_hash_add_folder( cache, folder ) ) {
   1062 		p->listFolder = g_list_append( p->listFolder, folder );
   1063 		ADDRITEM_PARENT(folder) = ADDRITEM_OBJECT(p);
   1064 		addrcache_set_dirty( cache, TRUE );
   1065 	}
   1066 	else {
   1067 		addritem_free_item_folder( folder );
   1068 		folder = NULL;
   1069 	}
   1070 	return folder;
   1071 }