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