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