* src/common/session.c
[claws.git] / src / addrcache.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2001-2002 Match Grun
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * 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
31 #define ID_TIME_OFFSET             998000000
32 #define ADDRCACHE_MAX_SEARCH_COUNT 1000
33
34 static int _nextCacheID__ = 0;
35
36 /*
37  * Generate next cache ID.
38  */
39 static int addrcache_next_cache_id() {
40         int retVal;
41
42         if( _nextCacheID__ == 0 ) {
43                 _nextCacheID__ = 1;
44         }
45         retVal = _nextCacheID__;
46         ++_nextCacheID__;
47         return retVal;
48 }
49
50 /*
51 * Create new address cache.
52 */
53 AddressCache *addrcache_create(void) {
54         AddressCache *cache;
55         gint t;
56
57         cache = g_new0( AddressCache, 1 );
58         cache->itemHash = g_hash_table_new( g_str_hash, g_str_equal );
59         cache->cacheID = g_strdup_printf( "%d", addrcache_next_cache_id() );
60
61         cache->dataRead = FALSE;
62         cache->modified = FALSE;
63         cache->dirtyFlag = FALSE;
64         cache->accessFlag = FALSE;
65         cache->name = NULL;
66         cache->modifyTime = 0;
67
68         /* Generate the next ID using system time */
69         cache->nextID = 1;
70         t = time( NULL );
71         if( t > 0 ) {
72                 cache->nextID = t - ID_TIME_OFFSET;
73         }
74
75         cache->tempList = NULL;
76         cache->rootFolder = addritem_create_item_folder();
77         cache->rootFolder->isRoot = TRUE;
78         ADDRITEM_PARENT(cache->rootFolder) = NULL;
79
80         cache->searchIndex = NULL;
81
82         return cache;
83 }
84
85 /*
86 * Properties.
87 */
88 ItemFolder *addrcache_get_root_folder( AddressCache *cache ) {
89         g_return_val_if_fail( cache != NULL, NULL );
90         return cache->rootFolder;
91 }
92 GList *addrcache_get_list_folder( AddressCache *cache ) {
93         g_return_val_if_fail( cache != NULL, NULL );
94         return cache->rootFolder->listFolder;
95 }
96 GList *addrcache_get_list_person( AddressCache *cache ) {
97         g_return_val_if_fail( cache != NULL, NULL );
98         return cache->rootFolder->listPerson;
99 }
100 gboolean addrcache_get_dirty( AddressCache *cache ) {
101         g_return_val_if_fail( cache != NULL, FALSE );
102         return cache->dirtyFlag;
103 }
104 void addrcache_set_dirty( AddressCache *cache, const gboolean value ) {
105         g_return_if_fail( cache != NULL );
106         cache->dirtyFlag = value;
107 }
108 gboolean addrcache_get_modified( AddressCache *cache ) {
109         g_return_val_if_fail( cache != NULL, FALSE );
110         return cache->modified;
111 }
112 void addrcache_set_modified( AddressCache *cache, const gboolean value ) {
113         g_return_if_fail( cache != NULL );
114         cache->modified = value;
115 }
116 gboolean addrcache_get_read_flag( AddressCache *cache ) {
117         g_return_val_if_fail( cache != NULL, FALSE );
118         return cache->dataRead;
119 }
120 void addrcache_set_read_flag( AddressCache *cache, const gboolean value ) {
121         g_return_if_fail( cache != NULL );
122         cache->dataRead = value;
123 }
124 gboolean addrcache_get_accessed( AddressCache *cache ) {
125         g_return_val_if_fail( cache != NULL, FALSE );
126         return cache->accessFlag;
127 }
128 void addrcache_set_accessed( AddressCache *cache, const gboolean value ) {
129         g_return_if_fail( cache != NULL );
130         cache->accessFlag = value;
131 }
132 gchar *addrcache_get_name( AddressCache *cache ) {
133         g_return_val_if_fail( cache != NULL, NULL );
134         return cache->name;
135 }
136 void addrcache_set_name( AddressCache *cache, const gchar *value ) {
137         g_return_if_fail( cache != NULL );
138         cache->name = mgu_replace_string( cache->name, value );
139         g_strstrip( cache->name );
140         cache->dirtyFlag = TRUE;
141 }
142
143 /*
144 * Generate next ID.
145 */
146 void addrcache_next_id( AddressCache *cache ) {
147         g_return_if_fail( cache != NULL );
148         cache->nextID++;
149 }
150
151 /*
152 * Refresh internal variables. This can be used force a reload.
153 */
154 void addrcache_refresh( AddressCache *cache ) {
155         cache->dataRead = FALSE;
156         cache->modified = TRUE;
157         cache->accessFlag = FALSE;
158         cache->modifyTime = 0;
159 }
160
161 /*
162 * Free hash table visitor function.
163 */
164 static gint addrcache_free_item_vis( gpointer key, gpointer value, gpointer data ) {
165         AddrItemObject *obj = ( AddrItemObject * ) value;
166
167         if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) {
168                 addritem_free_item_person( ( ItemPerson * ) obj );
169         }
170         else if( ADDRITEM_TYPE(obj) == ITEMTYPE_EMAIL ) {
171                 addritem_free_item_email( ( ItemEMail * ) obj );
172         }
173         else if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) {
174                 addritem_free_item_group( ( ItemGroup * ) obj );
175         }
176         else if( ADDRITEM_TYPE(obj) == ITEMTYPE_FOLDER ) {
177                 addritem_free_item_folder( ( ItemFolder * ) obj );
178         }
179         key = NULL;
180         value = NULL;
181         return TRUE;
182 }
183
184 /*
185 * Free hash table of address cache items.
186 */
187 static void addrcache_free_item_hash( GHashTable *table ) {
188         g_return_if_fail( table != NULL );
189         g_hash_table_freeze( table );
190         g_hash_table_foreach_remove( table, addrcache_free_item_vis, NULL );
191         g_hash_table_thaw( table );
192 }
193
194 /*
195 * Free up folders and groups.
196 */
197 static void addrcache_free_all_folders( ItemFolder *parent ) {
198         GList *node;
199
200         if( parent == NULL ) return;
201
202         node = parent->listFolder;
203         while( node ) {
204                 ItemFolder *folder = node->data;
205                 addrcache_free_all_folders( folder );
206                 node->data = NULL;
207                 node = g_list_next( node );
208         }
209         g_list_free( parent->listPerson );
210         g_list_free( parent->listGroup );
211         g_list_free( parent->listFolder );
212         parent->listPerson = NULL;
213         parent->listGroup = NULL;
214         parent->listFolder = NULL;
215 }
216
217 /*
218 * Clear the address cache.
219 */
220 void addrcache_clear( AddressCache *cache ) {
221         g_return_if_fail( cache != NULL );
222
223         /* Clear completion index */
224         addrcindex_clear( cache->searchIndex );
225
226         /* Free up folders and hash table */
227         addrcache_free_all_folders( cache->rootFolder );
228         addrcache_free_item_hash( cache->itemHash );
229         g_hash_table_destroy( cache->itemHash );
230         cache->itemHash = NULL;
231         ADDRITEM_PARENT(cache->rootFolder) = NULL;
232         addritem_free_item_folder( cache->rootFolder );
233         cache->rootFolder = NULL;
234         if( cache->tempList ) g_list_free( cache->tempList );
235         cache->tempList = NULL;
236
237         /* Reset to initial state */
238         cache->itemHash = g_hash_table_new( g_str_hash, g_str_equal );
239         cache->rootFolder = addritem_create_item_folder();
240         cache->rootFolder->isRoot = TRUE;
241         ADDRITEM_PARENT(cache->rootFolder) = NULL;
242
243         addrcache_refresh( cache );
244 }
245
246 /*
247 * Free address cache.
248 */
249 void addrcache_free( AddressCache *cache ) {
250         g_return_if_fail( cache != NULL );
251
252         /* Free completion index */
253         addrcindex_free( cache->searchIndex );
254         cache->searchIndex = NULL;
255
256         cache->dirtyFlag = FALSE;
257         addrcache_free_all_folders( cache->rootFolder );
258         addrcache_free_item_hash( cache->itemHash );
259         g_hash_table_destroy( cache->itemHash );
260         cache->itemHash = NULL;
261         ADDRITEM_PARENT(cache->rootFolder) = NULL;
262         addritem_free_item_folder( cache->rootFolder );
263         cache->rootFolder = NULL;
264         g_list_free( cache->tempList );
265         cache->tempList = NULL;
266         g_free( cache->cacheID );
267         cache->cacheID = NULL;
268         g_free( cache->name );
269         cache->name = NULL;
270
271         g_free( cache );
272 }
273
274 /*
275 * Check whether file has changed by comparing with cache.
276 * return: TRUE if file has changed.
277 */
278 gboolean addrcache_check_file( AddressCache *cache, gchar *path ) {
279         gboolean retVal;
280         struct stat filestat;
281         retVal = TRUE;
282         if( path ) {
283                 if( 0 == lstat( path, &filestat ) ) {
284                         if( filestat.st_mtime == cache->modifyTime ) retVal = FALSE;
285                 }
286         }
287         return retVal;
288 }
289
290 /*
291 * Save file time to cache.
292 * return: TRUE if time marked.
293 */
294 gboolean addrcache_mark_file( AddressCache *cache, gchar *path ) {
295         gboolean retVal = FALSE;
296         struct stat filestat;
297         if( path ) {
298                 if( 0 == lstat( path, &filestat ) ) {
299                         cache->modifyTime = filestat.st_mtime;
300                         retVal = TRUE;
301                 }
302         }
303         return retVal;
304 }
305
306 /*
307 * Print list of items.
308 */
309 void addrcache_print_item_list( GList *list, FILE *stream ) {
310         GList *node = list;
311         while( node ) {
312                 AddrItemObject *obj = node->data;
313                 if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) {
314                         addritem_print_item_person( ( ItemPerson * ) obj, stream );
315                 }
316                 else if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) {
317                         addritem_print_item_group( ( ItemGroup * ) obj, stream );
318                 }
319                 else if( ADDRITEM_TYPE(obj) == ITEMTYPE_FOLDER ) {
320                         addritem_print_item_folder( ( ItemFolder * ) obj, stream );
321                 }
322                 node = g_list_next( node );
323         }
324         fprintf( stream, "\t---\n" );
325 }
326
327 /*
328 * Print item hash table visitor function.
329 */
330 static void addrcache_print_item_vis( gpointer key, gpointer value, gpointer data ) {
331         AddrItemObject *obj = ( AddrItemObject * ) value;
332         FILE *stream = ( FILE * ) data;
333         if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) {
334                 addritem_print_item_person( ( ItemPerson * ) obj, stream );
335         }
336         else if( ADDRITEM_TYPE(obj) == ITEMTYPE_EMAIL ) {
337                 printf( "addrcache: print email\n" );
338         }
339         else if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) {
340                 addritem_print_item_group( ( ItemGroup * ) obj, stream );
341         }
342         else if( ADDRITEM_TYPE(obj) == ITEMTYPE_FOLDER ) {
343                 addritem_print_item_folder( ( ItemFolder * ) obj, stream );
344         }
345 }
346
347 /*
348 * Dump entire address cache hash table contents.
349 */
350 void addrcache_print( AddressCache *cache, FILE *stream ) {
351         g_return_if_fail( cache != NULL );
352         fprintf( stream, "AddressCache:\n" );
353         fprintf( stream, "cache id : %s\n",  cache->cacheID );
354         fprintf( stream, "next id  : %d\n",  cache->nextID );
355         fprintf( stream, "name     : %s\n",  cache->name );
356         fprintf( stream, "mod time : %ld\n", cache->modifyTime );
357         fprintf( stream, "modified : %s\n",  cache->modified ? "yes" : "no" );
358         fprintf( stream, "data read: %s\n",  cache->dataRead ? "yes" : "no" );
359 }
360
361 /*
362 * Dump entire address cache hash table contents.
363 */
364 void addrcache_dump_hash( AddressCache *cache, FILE *stream ) {
365         g_return_if_fail( cache != NULL );
366         addrcache_print( cache, stream );
367         g_hash_table_foreach( cache->itemHash, addrcache_print_item_vis, stream );
368 }
369
370 /*
371  * Allocate ID for person.
372  */
373 void addrcache_id_person( AddressCache *cache, ItemPerson *person ) {
374         g_return_if_fail( cache != NULL );
375         g_return_if_fail( person != NULL );
376         if( ADDRITEM_ID(person) ) return;
377         addrcache_next_id( cache );
378         ADDRITEM_ID(person) = g_strdup_printf( "%d", cache->nextID );
379 }
380
381 /*
382  * Allocate ID for group.
383  */
384 void addrcache_id_group( AddressCache *cache, ItemGroup *group ) {
385         g_return_if_fail( cache != NULL );
386         g_return_if_fail( group != NULL );
387         if( ADDRITEM_ID(group) ) return;
388         addrcache_next_id( cache );
389         ADDRITEM_ID(group) = g_strdup_printf( "%d", cache->nextID );
390 }
391
392 /*
393  * Allocate ID for folder.
394  */
395 void addrcache_id_folder( AddressCache *cache, ItemFolder *folder ) {
396         g_return_if_fail( cache != NULL );
397         g_return_if_fail( folder != NULL );
398         if( ADDRITEM_ID(folder) ) return;
399         addrcache_next_id( cache );
400         ADDRITEM_ID(folder) = g_strdup_printf( "%d", cache->nextID );
401 }
402
403 /*
404  * Allocate ID for email address.
405  */
406 void addrcache_id_email( AddressCache *cache, ItemEMail *email ) {
407         g_return_if_fail( cache != NULL );
408         g_return_if_fail( email != NULL );
409         if( ADDRITEM_ID(email) ) return;
410         addrcache_next_id( cache );
411         ADDRITEM_ID(email) = g_strdup_printf( "%d", cache->nextID );
412 }
413
414 /*
415  * Allocate ID for user attribute.
416  */
417 void addrcache_id_attribute( AddressCache *cache, UserAttribute *attrib ) {
418         g_return_if_fail( cache != NULL );
419         g_return_if_fail( attrib != NULL );
420         if( attrib->uid ) return;
421         addrcache_next_id( cache );
422         attrib->uid = g_strdup_printf( "%d", cache->nextID );
423 }
424
425 /*
426 * Add person to hash table.
427 * return: TRUE if item added.
428 */
429 gboolean addrcache_hash_add_person( AddressCache *cache, ItemPerson *person ) {
430         if( g_hash_table_lookup( cache->itemHash, ADDRITEM_ID(person) ) ) {
431                 return FALSE;
432         }
433         g_hash_table_insert( cache->itemHash, ADDRITEM_ID(person), person );
434         return TRUE;
435 }
436
437 /*
438 * Add email to hash table.
439 * return: TRUE if item added.
440 */
441 gboolean addrcache_hash_add_email( AddressCache *cache, ItemEMail *email ) {
442         if( g_hash_table_lookup( cache->itemHash, ADDRITEM_ID(email) ) ) {
443                 return FALSE;
444         }
445         g_hash_table_insert( cache->itemHash, ADDRITEM_ID(email), email );
446         return TRUE;
447 }
448
449 /*
450 * Add group to hash table.
451 * return: TRUE if item added.
452 */
453 gboolean addrcache_hash_add_group( AddressCache *cache, ItemGroup *group ) {
454         g_return_val_if_fail( cache != NULL, FALSE );
455         g_return_val_if_fail( group != NULL, FALSE );
456
457         if( g_hash_table_lookup( cache->itemHash, ADDRITEM_ID(group) ) ) {
458                 return FALSE;
459         }
460         g_hash_table_insert( cache->itemHash, ADDRITEM_ID(group), group );
461         return TRUE;
462 }
463
464 /*
465 * Add folder to hash table.
466 * return: TRUE if item added.
467 */
468 gboolean addrcache_hash_add_folder( AddressCache *cache, ItemFolder *folder ) {
469         g_return_val_if_fail( cache != NULL, FALSE );
470         g_return_val_if_fail( folder != NULL, FALSE );
471
472         if( g_hash_table_lookup( cache->itemHash, ADDRITEM_ID(folder) ) ) {
473                 return FALSE;
474         }
475         g_hash_table_insert( cache->itemHash, ADDRITEM_ID(folder), folder );
476         return TRUE;
477 }
478
479 /*
480 * Add person to specified folder in cache.
481 */
482 gboolean addrcache_folder_add_person( AddressCache *cache, ItemFolder *folder, ItemPerson *item ) {
483         gboolean retVal = FALSE;
484
485         g_return_val_if_fail( cache != NULL, FALSE );
486         g_return_val_if_fail( folder != NULL, FALSE );
487         g_return_val_if_fail( item != NULL, FALSE );
488
489         retVal = addrcache_hash_add_person( cache, item );
490         if( retVal ) {
491                 addritem_folder_add_person( folder, item );
492                 cache->dirtyFlag = TRUE;
493         }
494         return retVal;
495 }
496
497 /*
498 * Add folder to specified folder in cache.
499 */
500 gboolean addrcache_folder_add_folder( AddressCache *cache, ItemFolder *folder, ItemFolder *item ) {
501         gboolean retVal = FALSE;
502
503         g_return_val_if_fail( cache != NULL, FALSE );
504         g_return_val_if_fail( folder != NULL, FALSE );
505         g_return_val_if_fail( item != NULL, FALSE );
506
507         retVal = addrcache_hash_add_folder( cache, item );
508         if( retVal ) {
509                 addritem_folder_add_folder( folder, item );
510                 cache->dirtyFlag = TRUE;
511         }
512         return TRUE;
513 }
514
515 /*
516 * Add folder to specified folder in cache.
517 */
518 gboolean addrcache_folder_add_group( AddressCache *cache, ItemFolder *folder, ItemGroup *item ) {
519         gboolean retVal = FALSE;
520
521         g_return_val_if_fail( cache != NULL, FALSE );
522         g_return_val_if_fail( folder != NULL, FALSE );
523         g_return_val_if_fail( item != NULL, FALSE );
524
525         retVal = addrcache_hash_add_group( cache, item );
526         if( retVal ) {
527                 addritem_folder_add_group( folder, item );
528                 cache->dirtyFlag = TRUE;
529         }
530         return retVal;
531 }
532
533 /*
534 * Add person to address cache.
535 * return: TRUE if item added.
536 */
537 gboolean addrcache_add_person( AddressCache *cache, ItemPerson *person ) {
538         gboolean retVal = FALSE;
539
540         g_return_val_if_fail( cache != NULL, FALSE );
541         g_return_val_if_fail( person != NULL, FALSE );
542
543         retVal = addrcache_hash_add_person( cache, person );
544         if( retVal ) {
545                 addritem_folder_add_person( cache->rootFolder, person );
546                 cache->dirtyFlag = TRUE;
547         }
548         return retVal;
549 }
550
551 /*
552 * Add EMail address to person.
553 * return: TRUE if item added.
554 */
555 gboolean addrcache_person_add_email( AddressCache *cache, ItemPerson *person, ItemEMail *email ) {
556         gboolean retVal = FALSE;
557
558         g_return_val_if_fail( cache != NULL, FALSE );
559         g_return_val_if_fail( person != NULL, FALSE );
560         g_return_val_if_fail( email != NULL, FALSE );
561
562         retVal = addrcache_hash_add_email( cache, email );
563         if( retVal ) {
564                 addritem_person_add_email( person, email );
565                 cache->dirtyFlag = TRUE;
566         }
567         return retVal;
568 }
569
570 /*
571 * Add group to address cache.
572 * return: TRUE if item added.
573 */
574 gboolean addrcache_add_group( AddressCache *cache, ItemGroup *group ) {
575         gboolean retVal = FALSE;
576
577         g_return_val_if_fail( cache != NULL, FALSE );
578         g_return_val_if_fail( group != NULL, FALSE );
579
580         retVal = addrcache_hash_add_group( cache, group );
581         if( retVal ) {
582                 addritem_folder_add_group( cache->rootFolder, group );
583                 cache->dirtyFlag = TRUE;
584         }
585         return retVal;
586 }
587
588 /*
589 * Add EMail address to person.
590 * return: TRUE if item added.
591 */
592 gboolean addrcache_group_add_email( AddressCache *cache, ItemGroup *group, ItemEMail *email ) {
593         g_return_val_if_fail( cache != NULL, FALSE );
594         g_return_val_if_fail( group != NULL, FALSE );
595         g_return_val_if_fail( email != NULL, FALSE );
596
597         addritem_group_add_email( group, email );
598         cache->dirtyFlag = TRUE;
599         return TRUE;
600 }
601
602 /*
603 * Add folder to address cache.
604 * return: TRUE if item added.
605 */
606 gboolean addrcache_add_folder( AddressCache *cache, ItemFolder *folder ) {
607         gboolean retVal = FALSE;
608
609         g_return_val_if_fail( cache != NULL, FALSE );
610         g_return_val_if_fail( folder != NULL, FALSE );
611
612         retVal = addrcache_hash_add_folder( cache, folder );
613         if( retVal ) {
614                 addritem_folder_add_folder( cache->rootFolder, folder );
615                 cache->dirtyFlag = TRUE;
616         }
617         return retVal;
618 }
619
620 /*
621 * Move person to destination folder.
622 * Enter: cache  Cache.
623 *        person Person to move.
624 *        target Target folder.
625 */
626 void addrcache_folder_move_person(
627         AddressCache *cache, ItemPerson *person, ItemFolder *target )
628 {
629         ItemFolder *parent;
630
631         g_return_if_fail( cache != NULL );
632         g_return_if_fail( person != NULL );
633
634         parent = ( ItemFolder * ) ADDRITEM_PARENT(person);
635         if( ! parent ) parent = cache->rootFolder;
636         parent->listPerson = g_list_remove( parent->listPerson, person );
637         target->listPerson = g_list_append( target->listPerson, person );
638         ADDRITEM_PARENT(person) = ADDRITEM_OBJECT(target);
639         cache->dirtyFlag = TRUE;
640 }
641
642 /*
643 * Move group to destination folder.
644 * Enter: cache  Cache.
645 *        group  Group to move.
646 *        target Target folder.
647 */
648 void addrcache_folder_move_group(
649         AddressCache *cache, ItemGroup *group, ItemFolder *target )
650 {
651         ItemFolder *parent;
652
653         g_return_if_fail( cache != NULL );
654         g_return_if_fail( group != NULL );
655
656         parent = ( ItemFolder * ) ADDRITEM_PARENT(group);
657         if( ! parent ) parent = cache->rootFolder;
658         parent->listGroup = g_list_remove( parent->listGroup, group );
659         target->listGroup = g_list_append( target->listGroup, group );
660         ADDRITEM_PARENT(group) = ADDRITEM_OBJECT(target);
661         cache->dirtyFlag = TRUE;
662 }
663
664 /*
665 * Move folder to destination folder.
666 * Enter: cache  Cache.
667 *        folder Folder to move.
668 *        target Target folder.
669 */
670 void addrcache_folder_move_folder(
671         AddressCache *cache, ItemFolder *folder, ItemFolder *target )
672 {
673         ItemFolder *parent;
674
675         g_return_if_fail( cache != NULL );
676         g_return_if_fail( folder != NULL );
677
678         parent = ( ItemFolder * ) ADDRITEM_PARENT(folder);
679         if( ! parent ) parent = cache->rootFolder;
680         parent->listFolder = g_list_remove( parent->listFolder, folder );
681         target->listFolder = g_list_append( target->listFolder, folder );
682         ADDRITEM_PARENT(folder) = ADDRITEM_OBJECT(target);
683         cache->dirtyFlag = TRUE;
684 }
685
686 /*
687 * Return pointer to object (either person or group) for specified ID.
688 * param: uid Object ID.
689 * return: Object, or NULL if not found.
690 */
691 AddrItemObject *addrcache_get_object( AddressCache *cache, const gchar *uid ) {
692         AddrItemObject *obj = NULL;
693         gchar *uidH;
694
695         g_return_val_if_fail( cache != NULL, NULL );
696
697         if( uid == NULL || *uid == '\0' ) return NULL;
698         obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid );
699         if( obj ) {
700                 /* Check for matching UID */
701                 uidH = ADDRITEM_ID(obj);
702                 if( uidH ) {
703                         if( strcmp( uidH, uid ) == 0 ) return obj;
704                 }
705         }
706         return NULL;
707 }
708
709 /*
710 * Return pointer for specified object ID.
711 * param: uid Object ID.
712 * return: Person object, or NULL if not found.
713 */
714 ItemPerson *addrcache_get_person( AddressCache *cache, const gchar *uid ) {
715         ItemPerson *person = NULL;
716         AddrItemObject *obj = addrcache_get_object( cache, uid );
717
718         if( obj ) {
719                 if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) {
720                         person = ( ItemPerson * ) obj;
721                 }
722         }
723         return person;
724 }
725
726 /*
727 * Return pointer for specified object ID.
728 * param: uid group ID.
729 * return: Group object, or NULL if not found.
730 */
731 ItemGroup *addrcache_get_group( AddressCache *cache, const gchar *uid ) {
732         ItemGroup *group = NULL;
733         AddrItemObject *obj = addrcache_get_object( cache, uid );
734
735         if( obj ) {
736                 if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) {
737                         group = ( ItemGroup * ) obj;
738                 }
739         }
740         return group;
741 }
742
743 /*
744 * Find email address in address cache.
745 * param: eid    EMail ID.
746 * return: email object for specified object ID and email ID, or NULL if not found.
747 */
748 ItemEMail *addrcache_get_email( AddressCache *cache, const gchar *eid ) {
749         ItemEMail *email = NULL;
750         AddrItemObject *obj = addrcache_get_object( cache, eid );
751
752         if( obj ) {
753                 if( ADDRITEM_TYPE(obj) == ITEMTYPE_EMAIL ) {
754                         email = ( ItemEMail * ) obj;
755                 }
756         }
757         return email;
758 }
759
760 /*
761 * Remove attribute from person.
762 * param: uid    Object ID for person.
763 *        aid    Attribute ID.
764 * return: UserAttribute object, or NULL if not found. Note that object should still be freed.
765 */
766 UserAttribute *addrcache_person_remove_attrib_id( AddressCache *cache, const gchar *uid, const gchar *aid ) {
767         UserAttribute *attrib = NULL;
768         ItemPerson *person;
769
770         if( aid == NULL || *aid == '\0' ) return NULL;
771
772         person = addrcache_get_person( cache, uid );
773         if( person ) {
774                 attrib = addritem_person_remove_attrib_id( person, aid );
775                 cache->dirtyFlag = TRUE;
776         }
777         return attrib;
778 }
779
780 /*
781 * Remove attribute from person.
782 * param: person Person.
783 *        attrib Attribute to remove.
784 * return: UserAttribute object. Note that object should still be freed.
785 */
786 UserAttribute *addrcache_person_remove_attribute( AddressCache *cache, ItemPerson *person, UserAttribute *attrib ) {
787         UserAttribute *found = NULL;
788
789         g_return_val_if_fail( cache != NULL, NULL );
790
791         if( person && attrib ) {
792                 found = addritem_person_remove_attribute( person, attrib );
793                 cache->dirtyFlag = TRUE;
794         }
795         return found;
796 }
797
798 /*
799 * Remove group from address cache.
800 * param: group  Group to remove.
801 * return: Group, or NULL if not found. Note that object should still be freed.
802 */
803 ItemGroup *addrcache_remove_group( AddressCache *cache, ItemGroup *group ) {
804         AddrItemObject *obj = NULL;
805
806         g_return_val_if_fail( cache != NULL, NULL );
807
808         if( group ) {
809                 gchar *uid = ADDRITEM_ID(group);
810                 if( uid == NULL || *uid == '\0' ) return NULL;
811                 obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid );
812                 if( obj ) {
813                         ItemFolder *parent = ( ItemFolder * ) ADDRITEM_PARENT(group);
814                         if( ! parent ) parent = cache->rootFolder;
815
816                         /* Remove group from parent's list and hash table */
817                         parent->listGroup = g_list_remove( parent->listGroup, obj );
818                         g_hash_table_remove( cache->itemHash, uid );
819                         cache->dirtyFlag = TRUE;
820                         return group;
821                 }
822         }
823         return NULL;
824 }
825
826 /*
827 * Remove specified email from address cache. Note that object is only
828 * removed from cache and not parent objects.
829 * param: email  EMail to remove.
830 * return: EMail, or NULL if not found. Note that object should still be freed.
831 */
832 ItemEMail *addrcache_remove_email( AddressCache *cache, ItemEMail *email ) {
833         AddrItemObject *obj = NULL;
834
835         g_return_val_if_fail( cache != NULL, NULL );
836
837         if( email ) {
838                 gchar *eid = ADDRITEM_ID(email);
839                 if( eid == NULL || *eid == '\0' ) return NULL;
840                 obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, eid );
841                 if( obj ) {
842                         if( ADDRITEM_TYPE(obj) == ITEMTYPE_EMAIL ) {
843                                 /* Remove email addresses from hash table. */
844                                 g_hash_table_remove( cache->itemHash, eid );
845                                 cache->dirtyFlag = TRUE;
846                                 return email;
847                         }
848                 }
849         }
850         return NULL;
851 }
852
853 /*
854 * Hash table visitor function to remove email from group.
855 */
856 static void addrcache_allgrp_rem_email_vis( gpointer key, gpointer value, gpointer data ) {
857         AddrItemObject *obj = ( AddrItemObject * ) value;
858         ItemEMail *email = ( ItemEMail * ) data;
859
860         if( ! email ) return;
861         if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) {
862                 ItemGroup *group = ( ItemGroup * ) value;
863                 if( group ) {
864                         /* Remove each email address that belongs to the person from the list */
865                         group->listEMail = g_list_remove( group->listEMail, email );
866                 }
867         }
868 }
869
870 /*
871 * Remove specified person from address cache.
872 * param: person Person to remove.
873 * return: Person, or NULL if not found. Note that object should still be freed.
874 */
875 ItemPerson *addrcache_remove_person( AddressCache *cache, ItemPerson *person ) {
876         AddrItemObject *obj = NULL;
877         gchar *uid;
878
879         g_return_val_if_fail( cache != NULL, NULL );
880
881         if( person ) {
882                 uid = ADDRITEM_ID(person);
883                 if( uid == NULL || *uid == '\0' ) return NULL;
884                 obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid );
885                 if( obj ) {
886                         if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) {
887                                 ItemFolder *parent;
888                                 GList *node;
889
890                                 /* Remove all email addresses for person */
891                                 /* from groups and from hash table */
892                                 node = person->listEMail;
893                                 while( node ) {
894                                         ItemEMail *email;
895                                         gchar *eid;
896
897                                         email = node->data;
898                                         g_hash_table_foreach( cache->itemHash,
899                                                 addrcache_allgrp_rem_email_vis, email );
900                                         eid = ADDRITEM_ID( email );
901                                         g_hash_table_remove( cache->itemHash, eid );
902                                         node = g_list_next( node );
903                                 }
904
905                                 /* Remove person from owning folder */
906                                 parent = ( ItemFolder * ) ADDRITEM_PARENT(person);
907                                 if( ! parent ) parent = cache->rootFolder;
908                                 parent->listPerson = g_list_remove( parent->listPerson, person );
909                                 g_hash_table_remove( cache->itemHash, uid );
910                                 cache->dirtyFlag = TRUE;
911                                 return person;
912                         }
913                 }
914         }
915         return NULL;
916 }
917
918 /*
919 * Remove email address in address cache for specified person.
920 * param: person Person.
921 *        email  EMail to remove.
922 * return: EMail object, or NULL if not found. Note that object should still be freed.
923 */
924 ItemEMail *addrcache_person_remove_email( AddressCache *cache, ItemPerson *person, ItemEMail *email ) {
925         ItemEMail *found = NULL;
926
927         g_return_val_if_fail( cache != NULL, NULL );
928
929         if( person && email ) {
930                 found = addritem_person_remove_email( person, email );
931                 if( found ) {
932                         /* Remove email from all groups. */
933                         g_hash_table_foreach( cache->itemHash, addrcache_allgrp_rem_email_vis, email );
934
935                         /* Remove email from person's address list */
936                         if( person->listEMail ) {
937                                 person->listEMail = g_list_remove( person->listEMail, email );
938                         }
939                         /* Unlink reference to person. */
940                         ADDRITEM_PARENT(email) = NULL;
941                         cache->dirtyFlag = TRUE;
942                 }
943         }
944         return found;
945 }
946
947 /*
948 * Move email address in address cache to new person. If member of group, address
949 * remains in group.
950 * param: cache  Cache.
951 *        email  EMail to remove.
952 *        target Target person.
953 * return: EMail object, or NULL if not found. Note that object should still be freed.
954 */
955 void addrcache_person_move_email(
956         AddressCache *cache, ItemEMail *email, ItemPerson *target )
957 {
958         ItemPerson *person;
959
960         g_return_if_fail( cache != NULL );
961
962         if( email == NULL ) return;
963         if( target == NULL ) return;
964
965         person = ( ItemPerson * ) ADDRITEM_PARENT(email);
966         if( person ) {
967                 ItemEMail *found;
968                 found = addritem_person_remove_email( person, email );
969                 if( found ) {
970                         /*
971                         if( person->listEMail ) {
972                                 person->listEMail = g_list_remove( person->listEMail, found );
973                                 addritem_person_add_email( target, found );
974                                 cache->dirtyFlag = TRUE;
975                         }
976                         */
977                         addritem_person_add_email( target, found );
978                         cache->dirtyFlag = TRUE;
979                 }
980         }
981 }
982
983 /*
984 * Return link list of address items for root level folder. Note that the list contains
985 * references to items and should be g_free() when done. Do *NOT* attempt to use the
986 * addrcache_free_xxx() functions... this will destroy the address cache data!
987 * Return: List of items, or NULL if none.
988 */
989 GList *addrcache_folder_get_address_list( AddressCache *cache, ItemFolder *folder ) {
990         GList *list = NULL;
991         GList *node = NULL;
992         ItemFolder *f = folder;
993
994         g_return_val_if_fail( cache != NULL, NULL );
995
996         if( ! f ) f = cache->rootFolder;
997         node = f->listPerson;
998         while( node ) {
999                 list = g_list_append( list, node->data );
1000                 node = g_list_next( node );
1001         }
1002         node = f->listGroup;
1003         while( node ) {
1004                 list = g_list_append( list, node->data );
1005                 node = g_list_next( node );
1006         }
1007         return list;
1008 }
1009
1010 /*
1011 * Return link list of persons for specified folder. Note that the list contains
1012 * references to items and should be g_free() when done. Do *NOT* attempt to use the
1013 * addrcache_free_xxx() functions... this will destroy the address cache data!
1014 * Return: List of items, or NULL if none.
1015 */
1016 GList *addrcache_folder_get_person_list( AddressCache *cache, ItemFolder *folder ) {
1017         ItemFolder *f = folder;
1018
1019         g_return_val_if_fail( cache != NULL, NULL );
1020
1021         if( ! f ) f = cache->rootFolder;
1022         return addritem_folder_get_person_list( f );
1023 }
1024
1025 /*
1026 * Return link list of group items for specified folder. Note that the list contains
1027 * references to items and should be g_free() when done. Do *NOT* attempt to use the
1028 * addrcache_free_xxx() functions... this will destroy the address cache data!
1029 * Return: List of items, or NULL if none.
1030 */
1031 GList *addrcache_folder_get_group_list( AddressCache *cache, ItemFolder *folder ) {
1032         ItemFolder *f = folder;
1033
1034         g_return_val_if_fail( cache != NULL, NULL );
1035
1036         if( ! f ) f = cache->rootFolder;
1037         return addritem_folder_get_group_list( f );
1038 }
1039
1040 /*
1041 * Return link list of folder items for specified folder. Note that the list contains
1042 * references to items and should be g_free() when done. Do *NOT* attempt to used the
1043 * addrcache_free_xxx() functions... this will destroy the address cache data!
1044 * Return: List of items, or NULL if none.
1045 */
1046 GList *addrcache_folder_get_folder_list( AddressCache *cache, ItemFolder *folder ) {
1047         GList *node = NULL;
1048         GList *list = NULL;
1049         ItemFolder *f = folder;
1050
1051         g_return_val_if_fail( cache != NULL, NULL );
1052
1053         if( ! f ) f = cache->rootFolder;
1054         node = f->listFolder;
1055         while( node ) {
1056                 list = g_list_append( list, node->data );
1057                 node = g_list_next( node );
1058         }
1059         return list;
1060 }
1061
1062 /*
1063 * Return link list of address items for root level folder. Note that the list contains
1064 * references to items and should be g_free() when done. Do *NOT* attempt to used the
1065 * addrcache_free_xxx() functions... this will destroy the address cache data!
1066 * Return: List of items, or NULL if none.
1067 */
1068 GList *addrcache_get_address_list( AddressCache *cache ) {
1069         g_return_val_if_fail( cache != NULL, NULL );
1070         return addrcache_folder_get_address_list( cache, cache->rootFolder );
1071 }
1072
1073 /*
1074 * Return link list of persons for root level folder. Note that the list contains
1075 * references to items and should be g_free() when done. Do *NOT* attempt to used the
1076 * addrcache_free_xxx() functions... this will destroy the address cache data!
1077 * Return: List of items, or NULL if none.
1078 */
1079 GList *addrcache_get_person_list( AddressCache *cache ) {
1080         g_return_val_if_fail( cache != NULL, NULL );
1081         return addritem_folder_get_person_list( cache->rootFolder );
1082 }
1083
1084 /*
1085 * Return link list of group items in root level folder. Note that the list contains
1086 * references to items and should be g_free() when done. Do *NOT* attempt to used the
1087 * addrcache_free_xxx() functions... this will destroy the address cache data!
1088 * Return: List of items, or NULL if none.
1089 */
1090 GList *addrcache_get_group_list( AddressCache *cache ) {
1091         g_return_val_if_fail( cache != NULL, NULL );
1092         return cache->rootFolder->listGroup;
1093 }
1094
1095 /*
1096 * Return link list of folder items in root level folder. Note that the list contains
1097 * references to items and should be g_free() when done. Do *NOT* attempt to used the
1098 * addrcache_free_xxx() functions... this will destroy the address cache data!
1099 * Return: List of items, or NULL if none.
1100 */
1101 GList *addrcache_get_folder_list( AddressCache *cache ) {
1102         g_return_val_if_fail( cache != NULL, NULL );
1103         return cache->rootFolder->listFolder;
1104 }
1105
1106 /*
1107 * Group visitor function.
1108 */
1109 static void addrcache_get_grp_person_vis( gpointer key, gpointer value, gpointer data ) {
1110         AddrItemObject *obj = ( AddrItemObject * ) value;
1111
1112         if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) {
1113                 AddressCache *cache = data;
1114                 ItemGroup *group = ( ItemGroup * ) obj;
1115                 ItemPerson *person = ( ItemPerson * ) cache->tempList->data;
1116                 GList *node = group->listEMail;
1117                 while( node ) {
1118                         ItemEMail *email = ( ItemEMail * ) node->data;
1119                         if( ADDRITEM_PARENT(email) == ADDRITEM_OBJECT(person) ) {
1120                                 if( ! g_list_find( cache->tempList, group ) ) {
1121                                         cache->tempList = g_list_append( cache->tempList, group );
1122                                 }
1123                         }
1124                         node = g_list_next( node );
1125                 }
1126         }
1127 }
1128
1129 /*
1130 * Return linked list of groups which contain a reference to specified person's email
1131 * address.
1132 */
1133 GList *addrcache_get_group_for_person( AddressCache *cache, ItemPerson *person ) {
1134         GList *list = NULL;
1135
1136         g_return_val_if_fail( cache != NULL, NULL );
1137
1138         cache->tempList = NULL;
1139         cache->tempList = g_list_append( cache->tempList, person );
1140         g_hash_table_foreach( cache->itemHash, addrcache_get_grp_person_vis, cache );
1141         cache->tempList = g_list_remove( cache->tempList, person );
1142         list = cache->tempList;
1143         cache->tempList = NULL;
1144         return list;
1145 }
1146
1147 /*
1148 * Find root folder for specified folder.
1149 * Enter: folder Folder to search.
1150 * Return: root folder, or NULL if not found.
1151 */
1152 ItemFolder *addrcache_find_root_folder( ItemFolder *folder ) {
1153         ItemFolder *item = folder;
1154         gint count = 0;
1155
1156         while( item ) {
1157                 if( item->isRoot ) break;
1158                 if( ++count > ADDRCACHE_MAX_SEARCH_COUNT ) {
1159                         item = NULL;
1160                         break;
1161                 }
1162                 item = ( ItemFolder * ) ADDRITEM_PARENT(folder);
1163         }
1164         return item;
1165 }
1166
1167 /*
1168 * Get all person visitor function.
1169 */
1170 static void addrcache_get_all_persons_vis( gpointer key, gpointer value, gpointer data ) {
1171         AddrItemObject *obj = ( AddrItemObject * ) value;
1172
1173         if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) {
1174                 AddressCache *cache = data;
1175                 cache->tempList = g_list_append( cache->tempList, obj );
1176         }
1177 }
1178
1179 /*
1180 * Return link list of all persons in address cache.  Note that the list contains
1181 * references to items. Do *NOT* attempt to use the addrcache_free_xxx() functions...
1182 * this will destroy the address cache data!
1183 * Return: List of items, or NULL if none.
1184 */
1185 GList *addrcache_get_all_persons( AddressCache *cache ) {
1186         GList *list = NULL;
1187
1188         g_return_val_if_fail( cache != NULL, NULL );
1189
1190         cache->tempList = NULL;
1191         g_hash_table_foreach( cache->itemHash, addrcache_get_all_persons_vis, cache );
1192         list = cache->tempList;
1193         cache->tempList = NULL;
1194         return list;
1195 }
1196
1197 /*
1198 * Get all groups visitor function.
1199 */
1200 static void addrcache_get_all_groups_vis( gpointer key, gpointer value, gpointer data ) {
1201         AddrItemObject *obj = ( AddrItemObject * ) value;
1202
1203         if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) {
1204                 AddressCache *cache = data;
1205                 cache->tempList = g_list_append( cache->tempList, obj );
1206         }
1207 }
1208
1209 /*
1210 * Return link list of all groups in address cache.  Note that the list contains
1211 * references to items. Do *NOT* attempt to use the addrcache_free_xxx() functions...
1212 * this will destroy the address cache data!
1213 * Return: List of items, or NULL if none.
1214 */
1215 GList *addrcache_get_all_groups( AddressCache *cache ) {
1216         GList *list = NULL;
1217
1218         g_return_val_if_fail( cache != NULL, NULL );
1219
1220         cache->tempList = NULL;
1221         g_hash_table_foreach( cache->itemHash, addrcache_get_all_groups_vis, cache );
1222         list = cache->tempList;
1223         cache->tempList = NULL;
1224         return list;
1225 }
1226
1227 /*
1228 * Remove folder from cache. Children are re-parented to parent folder.
1229 * param: folder Folder to remove.
1230 * return: Folder, or NULL if not found. Note that object should still be freed.
1231 */
1232 ItemFolder *addrcache_remove_folder( AddressCache *cache, ItemFolder *folder ) {
1233         AddrItemObject *obj = NULL;
1234
1235         g_return_val_if_fail( cache != NULL, NULL );
1236
1237         if( folder ) {
1238                 gchar *uid = ADDRITEM_ID(folder);
1239                 if( uid == NULL || *uid == '\0' ) return NULL;
1240                 obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid );
1241                 if( obj ) {
1242                         ItemFolder *parent = ( ItemFolder * ) ADDRITEM_PARENT(folder);
1243                         GList *node;
1244                         AddrItemObject *aio;
1245                         if( ! parent ) parent = cache->rootFolder;
1246
1247                         /* Re-parent children in folder */
1248                         node = folder->listFolder;
1249                         while( node ) {
1250                                 aio = ( AddrItemObject * ) node->data;
1251                                 parent->listFolder = g_list_append( parent->listFolder, aio );
1252                                 aio->parent = ADDRITEM_OBJECT(parent);
1253                                 node = g_list_next( node );
1254                         }
1255                         node = folder->listPerson;
1256                         while( node ) {
1257                                 aio = ( AddrItemObject * ) node->data;
1258                                 parent->listPerson = g_list_append( parent->listPerson, aio );
1259                                 aio->parent = ADDRITEM_OBJECT(parent);
1260                                 node = g_list_next( node );
1261                         }
1262                         node = folder->listGroup;
1263                         while( node ) {
1264                                 aio = ( AddrItemObject * ) node->data;
1265                                 parent->listGroup = g_list_append( parent->listGroup, aio );
1266                                 aio->parent = ADDRITEM_OBJECT(parent);
1267                                 node = g_list_next( node );
1268                         }
1269
1270                         /* Remove folder from parent's list and hash table */
1271                         parent->listFolder = g_list_remove( parent->listFolder, folder );
1272                         ADDRITEM_PARENT(folder) = NULL;
1273                         g_hash_table_remove( cache->itemHash, uid );
1274                         cache->dirtyFlag = TRUE;
1275                         return folder;
1276                 }
1277         }
1278         return NULL;
1279 }
1280
1281 /*
1282 * Remove folder from cache. Children are deleted.
1283 * param: folder Folder to remove.
1284 * return: Folder, or NULL if not found. Note that object should still be freed.
1285 */
1286 ItemFolder *addrcache_remove_folder_delete( AddressCache *cache, ItemFolder *folder ) {
1287         AddrItemObject *obj = NULL;
1288
1289         g_return_val_if_fail( cache != NULL, NULL );
1290
1291         if( folder ) {
1292                 gchar *uid = ADDRITEM_ID(folder);
1293                 if( uid == NULL || *uid == '\0' ) return NULL;
1294                 obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid );
1295                 if( obj ) {
1296                         ItemFolder *parent = ( ItemFolder * ) ADDRITEM_PARENT(folder);
1297                         if( ! parent ) parent = cache->rootFolder;
1298
1299                         /* Remove groups */
1300                         while( folder->listGroup ) {
1301                                 ItemGroup *item = ( ItemGroup * ) folder->listGroup->data;
1302                                 item = addrcache_remove_group( cache, item );
1303                                 if( item ) {
1304                                         addritem_free_item_group( item );
1305                                         item = NULL;
1306                                 }
1307                         }
1308
1309                         while( folder->listPerson ) {
1310                                 ItemPerson *item = ( ItemPerson * ) folder->listPerson->data;
1311                                 item = addrcache_remove_person( cache, item );
1312                                 if( item ) {
1313                                         addritem_free_item_person( item );
1314                                         item = NULL;
1315                                 }
1316                         }
1317
1318                         /* Recursive deletion of folder */
1319                         while( folder->listFolder ) {
1320                                 ItemFolder *item = ( ItemFolder * ) folder->listFolder->data;
1321                                 item = addrcache_remove_folder_delete( cache, item );
1322                                 if( item ) {
1323                                         addritem_free_item_folder( item );
1324                                         item = NULL;
1325                                 }
1326                         }
1327
1328                         /* Remove folder from parent's list and hash table */
1329                         parent->listFolder = g_list_remove( parent->listFolder, folder );
1330                         ADDRITEM_PARENT(folder) = NULL;
1331                         g_hash_table_remove( cache->itemHash, uid );
1332                         cache->dirtyFlag = TRUE;
1333                         return folder;
1334                 }
1335         }
1336         return NULL;
1337 }
1338
1339 /*
1340 * Add person and address data to cache.
1341 * Enter: cache     Cache.
1342 *        folder    Folder where to add person, or NULL for root folder.
1343 *        name      Common name.
1344 *        address   EMail address.
1345 *        remarks   Remarks.
1346 * Return: Person added. Do not *NOT* to use the addrbook_free_xxx() functions...
1347 * this will destroy the address book data.
1348 */
1349 ItemPerson *addrcache_add_contact( AddressCache *cache, ItemFolder *folder, const gchar *name,
1350                 const gchar *address, const gchar *remarks )
1351 {
1352         ItemPerson *person = NULL;
1353         ItemEMail *email = NULL;
1354         ItemFolder *f = folder;
1355
1356         g_return_val_if_fail( cache != NULL, NULL );
1357
1358         if( ! f ) f = cache->rootFolder;
1359
1360         /* Create person object */
1361         person = addritem_create_item_person();
1362         addritem_person_set_common_name( person, name );
1363         addrcache_id_person( cache, person );
1364         addrcache_folder_add_person( cache, f, person );
1365
1366         /* Create email object */
1367         email = addritem_create_item_email();
1368         addritem_email_set_address( email, address );
1369         addritem_email_set_remarks( email, remarks );
1370         addrcache_id_email( cache, email );
1371         addritem_person_add_email( person, email );
1372         cache->dirtyFlag = TRUE;
1373
1374         return person;
1375 }
1376
1377 /**
1378  * Clear address completion index.
1379  * \param cache Cache.
1380  */
1381 void addrcache_clear_index( AddressCache *cache ) {
1382         g_return_if_fail( cache != NULL );
1383         addrcindex_clear( cache->searchIndex );
1384 }
1385
1386 /**
1387  * Control creation of an address completion index.
1388  * \param cache Cache.
1389  * \param value Set to <i>TRUE</i> to create an index, or <i>FALSE</i> to
1390  *              destroy index.
1391  */
1392 void addrcache_use_index( AddressCache *cache, gboolean value ) {
1393         g_return_if_fail( cache != NULL );
1394
1395         if( value ) {
1396                 if( cache->searchIndex ) {
1397                         addrcindex_clear( cache->searchIndex );
1398                 }
1399                 else {
1400                         cache->searchIndex = addrcindex_create();
1401                 }
1402         }
1403         else {
1404                 addrcindex_free( cache->searchIndex );
1405                 cache->searchIndex = NULL;
1406         }
1407 }
1408
1409 /*
1410  * Load completion callback function.
1411  */
1412 static void addrcache_load_index_cb( gpointer key, gpointer value, gpointer data ) {
1413         AddrItemObject *obj = ( AddrItemObject * ) value;
1414
1415         if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) {
1416                 ItemPerson *person = ( ItemPerson * ) obj;
1417                 AddrCacheIndex *index = data;
1418                 addrcindex_add_person( index, person );
1419         }
1420 }
1421
1422 /**
1423  * Rebuild address completion index with all persons in cache.
1424  * \param cache Cache.
1425  */
1426 void addrcache_build_index( AddressCache *cache ) {
1427         g_return_if_fail( cache != NULL );
1428
1429         if( cache->searchIndex == NULL ) return;
1430
1431         /* Clear index */
1432         addrcindex_clear( cache->searchIndex );
1433
1434         /* Now load up */       
1435         g_hash_table_foreach(
1436                 cache->itemHash, addrcache_load_index_cb, cache->searchIndex );
1437         addrcindex_validate( cache->searchIndex );
1438
1439         /* addrcindex_print( cache->searchIndex, stdout ); */
1440 }
1441
1442 /**
1443  * Invalidate the address cache. This will cause index to be rebuilt.
1444  * \param cache Cache.
1445  */
1446 void addrcache_invalidate( AddressCache *cache ) {
1447         g_return_if_fail( cache != NULL );
1448
1449         if( cache->searchIndex == NULL ) return;
1450         addrcindex_invalidate( cache->searchIndex );
1451 }
1452
1453 /*
1454 * End of Source.
1455 */
1456
1457