sync with sylpheed 0.6.2cvs6
[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_val_if_fail( cache != NULL, NULL );
68         return cache->rootFolder;
69 }
70 GList *addrcache_get_list_folder( AddressCache *cache ) {
71         g_return_val_if_fail( cache != NULL, NULL );
72         return cache->rootFolder->listFolder;
73 }
74 GList *addrcache_get_list_person( AddressCache *cache ) {
75         g_return_val_if_fail( cache != NULL, 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 : %ld\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_val_if_fail( cache != NULL, FALSE );
351         g_return_val_if_fail( group != NULL, FALSE );
352
353         if( g_hash_table_lookup( cache->itemHash, ADDRITEM_ID(group) ) ) {
354                 return FALSE;
355         }
356         g_hash_table_insert( cache->itemHash, ADDRITEM_ID(group), group );
357         return TRUE;
358 }
359
360 /*
361 * Add folder to hash table.
362 * return: TRUE if item added.
363 */
364 gboolean addrcache_hash_add_folder( AddressCache *cache, ItemFolder *folder ) {
365         g_return_val_if_fail( cache != NULL, FALSE );
366         g_return_val_if_fail( folder != NULL, FALSE );
367
368         if( g_hash_table_lookup( cache->itemHash, ADDRITEM_ID(folder) ) ) {
369                 return FALSE;
370         }
371         g_hash_table_insert( cache->itemHash, ADDRITEM_ID(folder), folder );
372         return TRUE;
373 }
374
375 /*
376 * Add person to specified folder in cache.
377 */
378 gboolean addrcache_folder_add_person( AddressCache *cache, ItemFolder *folder, ItemPerson *item ) {
379         gboolean retVal = FALSE;
380
381         g_return_val_if_fail( cache != NULL, FALSE );
382         g_return_val_if_fail( folder != NULL, FALSE );
383         g_return_val_if_fail( item != NULL, FALSE );
384
385         retVal = addrcache_hash_add_person( cache, item );
386         if( retVal ) {
387                 addritem_folder_add_person( folder, item );
388         }
389         return retVal;
390 }
391
392 /*
393 * Add folder to specified folder in cache.
394 */
395 gboolean addrcache_folder_add_folder( AddressCache *cache, ItemFolder *folder, ItemFolder *item ) {
396         gboolean retVal = FALSE;
397
398         g_return_val_if_fail( cache != NULL, FALSE );
399         g_return_val_if_fail( folder != NULL, FALSE );
400         g_return_val_if_fail( item != NULL, FALSE );
401
402         retVal = addrcache_hash_add_folder( cache, item );
403         if( retVal ) {
404                 addritem_folder_add_folder( folder, item );
405         }
406         return TRUE;
407 }
408
409 /*
410 * Add folder to specified folder in cache.
411 */
412 gboolean addrcache_folder_add_group( AddressCache *cache, ItemFolder *folder, ItemGroup *item ) {
413         gboolean retVal = FALSE;
414
415         g_return_val_if_fail( cache != NULL, FALSE );
416         g_return_val_if_fail( folder != NULL, FALSE );
417         g_return_val_if_fail( item != NULL, FALSE );
418
419         retVal = addrcache_hash_add_group( cache, item );
420         if( retVal ) {
421                 addritem_folder_add_group( folder, item );
422         }
423         return retVal;
424 }
425
426 /*
427 * Add person to address cache.
428 * return: TRUE if item added.
429 */
430 gboolean addrcache_add_person( AddressCache *cache, ItemPerson *person ) {
431         gboolean retVal = FALSE;
432
433         g_return_val_if_fail( cache != NULL, FALSE );
434         g_return_val_if_fail( person != NULL, FALSE );
435
436         retVal = addrcache_hash_add_person( cache, person );
437         if( retVal ) {
438                 addritem_folder_add_person( cache->rootFolder, person );
439         }
440         return retVal;
441 }
442
443 /*
444 * Add EMail address to person.
445 * return: TRUE if item added.
446 */
447 gboolean addrcache_person_add_email( AddressCache *cache, ItemPerson *person, ItemEMail *email ) {
448         g_return_val_if_fail( cache != NULL, FALSE );
449         g_return_val_if_fail( person != NULL, FALSE );
450         g_return_val_if_fail( email != NULL, FALSE );
451
452         addritem_person_add_email( person, email );
453         return TRUE;
454 }
455
456 /*
457 * Add group to address cache.
458 * return: TRUE if item added.
459 */
460 gboolean addrcache_add_group( AddressCache *cache, ItemGroup *group ) {
461         gboolean retVal = FALSE;
462
463         g_return_val_if_fail( cache != NULL, FALSE );
464         g_return_val_if_fail( group != NULL, FALSE );
465
466         retVal = addrcache_hash_add_group( cache, group );
467         if( retVal ) {
468                 addritem_folder_add_group( cache->rootFolder, group );
469         }
470         return retVal;
471 }
472
473 /*
474 * Add EMail address to person.
475 * return: TRUE if item added.
476 */
477 gboolean addrcache_group_add_email( AddressCache *cache, ItemGroup *group, ItemEMail *email ) {
478         g_return_val_if_fail( cache != NULL, FALSE );
479         g_return_val_if_fail( group != NULL, FALSE );
480         g_return_val_if_fail( email != NULL, FALSE );
481
482         addritem_group_add_email( group, email );
483         return TRUE;
484 }
485
486 /*
487 * Add folder to address cache.
488 * return: TRUE if item added.
489 */
490 gboolean addrcache_add_folder( AddressCache *cache, ItemFolder *folder ) {
491         gboolean retVal = FALSE;
492
493         g_return_val_if_fail( cache != NULL, FALSE );
494         g_return_val_if_fail( folder != NULL, FALSE );
495
496         retVal = addrcache_hash_add_folder( cache, folder );
497         if( retVal ) {
498                 addritem_folder_add_folder( cache->rootFolder, folder );
499         }
500         return retVal;
501 }
502
503 /*
504 * Return pointer to object (either person or group) for specified ID.
505 * param: uid Object ID.
506 * return: Object, or NULL if not found.
507 */
508 AddrItemObject *addrcache_get_object( AddressCache *cache, const gchar *uid ) {
509         AddrItemObject *obj = NULL;
510         gchar *uidH;
511
512         g_return_val_if_fail( cache != NULL, NULL );
513
514         if( uid == NULL || *uid == '\0' ) return NULL;
515         obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid );
516         if( obj ) {
517                 // Check for matching UID
518                 uidH = ADDRITEM_ID(obj);
519                 if( uidH ) {
520                         if( strcmp( uidH, uid ) == 0 ) return obj;
521                 }
522         }
523         return NULL;
524 }
525
526 /*
527 * Return pointer for specified object ID.
528 * param: uid Object ID.
529 * return: Person object, or NULL if not found.
530 */
531 ItemPerson *addrcache_get_person( AddressCache *cache, const gchar *uid ) {
532         ItemPerson *person = NULL;
533         AddrItemObject *obj = addrcache_get_object( cache, uid );
534
535         if( obj ) {
536                 if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) {
537                         person = ( ItemPerson * ) obj;
538                 }
539         }
540         return person;
541 }
542
543 /*
544 * Return pointer for specified object ID.
545 * param: uid group ID.
546 * return: Group object, or NULL if not found.
547 */
548 ItemGroup *addrcache_get_group( AddressCache *cache, const gchar *uid ) {
549         ItemGroup *group = NULL;
550         AddrItemObject *obj = addrcache_get_object( cache, uid );
551
552         if( obj ) {
553                 if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) {
554                         group = ( ItemGroup * ) obj;
555                 }
556         }
557         return group;
558 }
559
560 /*
561 * Find email address in address cache.
562 * param: uid    Object ID for person.
563 *        eid    EMail ID.
564 * return: email object for specified object ID and email ID, or NULL if not found.
565 */
566 ItemEMail *addrcache_get_email( AddressCache *cache, const gchar *uid, const gchar *eid ) {
567         AddrItemObject *objP;
568
569         if( eid == NULL || *eid == '\0' ) return NULL;
570
571         objP = addrcache_get_object( cache, uid );
572         if( objP ) {
573                 if( ADDRITEM_TYPE(objP) == ITEMTYPE_PERSON ) {
574                         // Sequential search through email addresses
575                         ItemPerson *person = ( ItemPerson * ) objP;
576                         GList *nodeMail = person->listEMail;
577                         while( nodeMail ) {
578                                 AddrItemObject *objE = nodeMail->data;
579                                 gchar *ide = ADDRITEM_ID(objE);
580                                 if( ide ) {
581                                         if( strcmp( ide, eid ) == 0 ) {
582                                                 return ( ItemEMail * ) objE;
583                                         }
584                                 }
585                                 nodeMail = g_list_next( nodeMail );
586                         }
587                 }
588         }
589         return NULL;
590 }
591
592 /*
593 * Remove attribute from person.
594 * param: uid    Object ID for person.
595 *        aid    Attribute ID.
596 * return: UserAttribute object, or NULL if not found. Note that object should still be freed.
597 */
598 UserAttribute *addrcache_person_remove_attrib_id( AddressCache *cache, const gchar *uid, const gchar *aid ) {
599         UserAttribute *attrib = NULL;
600         ItemPerson *person;
601
602         if( aid == NULL || *aid == '\0' ) return NULL;
603
604         person = addrcache_get_person( cache, uid );
605         if( person ) {
606                 attrib = addritem_person_remove_attrib_id( person, aid );
607         }
608         return attrib;
609 }
610
611 /*
612 * Remove attribute from person.
613 * param: person Person.
614 *        attrib Attribute to remove.
615 * return: UserAttribute object. Note that object should still be freed.
616 */
617 UserAttribute *addrcache_person_remove_attribute( AddressCache *cache, ItemPerson *person, UserAttribute *attrib ) {
618         UserAttribute *found = NULL;
619
620         g_return_val_if_fail( cache != NULL, NULL );
621
622         if( person && attrib ) {
623                 found = addritem_person_remove_attribute( person, attrib );
624         }
625         return found;
626 }
627
628 /*
629 * Remove group from address cache for specified ID.
630 * param: uid Object ID.
631 * return: Group, or NULL if not found. Note that object should still be freed.
632 */
633 ItemGroup *addrcache_remove_group_id( AddressCache *cache, const gchar *uid ) {
634         AddrItemObject *obj = NULL;
635
636         g_return_val_if_fail( cache != NULL, NULL );
637
638         if( uid == NULL || *uid == '\0' ) return NULL;
639         obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid );
640         if( obj ) {
641                 if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) {
642                         ItemGroup *group = ( ItemGroup * ) obj;
643                         ItemFolder *parent = ( ItemFolder * ) ADDRITEM_PARENT(group);
644                         if( ! parent ) parent = cache->rootFolder;
645                         // Remove group from parent's list and hash table
646                         parent->listGroup = g_list_remove( parent->listGroup, group );
647                         g_hash_table_remove( cache->itemHash, uid );
648                         return ( ItemGroup * ) obj;
649                 }
650         }
651         return NULL;
652 }
653
654 /*
655 * Remove group from address cache.
656 * param: group  Group to remove.
657 * return: Group, or NULL if not found. Note that object should still be freed.
658 */
659 ItemGroup *addrcache_remove_group( AddressCache *cache, ItemGroup *group ) {
660         AddrItemObject *obj = NULL;
661
662         g_return_val_if_fail( cache != NULL, NULL );
663
664         if( group ) {
665                 gchar *uid = ADDRITEM_ID(group);
666                 if( uid == NULL || *uid == '\0' ) return NULL;
667                 obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid );
668                 if( obj ) {
669                         ItemFolder *parent = ( ItemFolder * ) ADDRITEM_PARENT(group);
670                         if( ! parent ) parent = cache->rootFolder;
671
672                         // Remove group from parent's list and hash table
673                         parent->listGroup = g_list_remove( parent->listGroup, obj );
674                         g_hash_table_remove( cache->itemHash, uid );
675                         return group;
676                 }
677         }
678         return NULL;
679 }
680
681 /*
682 * Remove person's email address from all groups in folder.
683 */
684 static void addrcache_foldergrp_rem_person( ItemFolder *folder, ItemPerson *person ) {
685         GList *nodeGrp = folder->listGroup;
686
687         while( nodeGrp ) {
688                 ItemGroup *group = nodeGrp->data;
689                 if( group ) {
690                         // Remove each email address that belongs to the person from the list
691                         GList *node = person->listEMail;
692                         while( node ) {
693                                 group->listEMail = g_list_remove( group->listEMail, node->data );
694                                 node = g_list_next( node );
695                         }
696                 }
697                 nodeGrp = g_list_next( nodeGrp );
698         }
699 }
700
701 /*
702 * Remove person from address cache for specified ID. Note that person still retains
703 * their EMail addresses. Also, links to these email addresses will be severed from
704 * the group.
705 * param: uid Object ID.
706 * return: Person, or NULL if not found. Note that object should still be freed.
707 */
708 ItemPerson *addrcache_remove_person_id( AddressCache *cache, const gchar *uid ) {
709         AddrItemObject *obj = NULL;
710
711         g_return_val_if_fail( cache != NULL, NULL );
712
713         if( uid == NULL || *uid == '\0' ) return NULL;
714         obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid );
715         if( obj ) {
716                 if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) {
717                         // Remove person's email addresses from all groups where
718                         // referenced and from hash table.
719                         ItemPerson *person = ( ItemPerson * ) obj;
720                         ItemFolder *parent = ( ItemFolder * ) ADDRITEM_PARENT(person);
721                         if( ! parent ) parent = cache->rootFolder;
722                         // Remove emails from groups, remove from parent's list
723                         // and hash table
724                         addrcache_foldergrp_rem_person( parent, person );
725                         parent->listPerson = g_list_remove( parent->listPerson, person );
726                         g_hash_table_remove( cache->itemHash, uid );
727                         return person;
728                 }
729         }
730         return NULL;
731 }
732
733 /*
734 * Remove specified person from address cache.
735 * param: person Person to remove.
736 * return: Person, or NULL if not found. Note that object should still be freed.
737 */
738 ItemPerson *addrcache_remove_person( AddressCache *cache, ItemPerson *person ) {
739         AddrItemObject *obj = NULL;
740
741         g_return_val_if_fail( cache != NULL, NULL );
742
743         if( person ) {
744                 gchar *uid = ADDRITEM_ID(person);
745                 if( uid == NULL || *uid == '\0' ) return NULL;
746                 obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid );
747                 if( obj ) {
748                         if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) {
749                                 // Remove person's email addresses from all groups where
750                                 // referenced and from hash table.
751                                 ItemFolder *parent = ( ItemFolder * ) ADDRITEM_PARENT(person);
752                                 if( ! parent ) parent = cache->rootFolder;
753                                 addrcache_foldergrp_rem_person( parent, person );
754                                 parent->listPerson = g_list_remove( parent->listPerson, person );
755                                 g_hash_table_remove( cache->itemHash, uid );
756                                 return person;
757                         }
758                 }
759         }
760         return NULL;
761 }
762
763 /*
764 * Remove email from group item hash table visitor function.
765 */
766 static void addrcache_allgrp_rem_email_vis( gpointer key, gpointer value, gpointer data ) {
767         AddrItemObject *obj = ( AddrItemObject * ) value;
768         ItemEMail *email = ( ItemEMail * ) data;
769
770         if( !email ) return;
771         if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) {
772                 ItemGroup *group = ( ItemGroup * ) value;
773                 if( group ) {
774                         // Remove each email address that belongs to the person from the list
775                         group->listEMail = g_list_remove( group->listEMail, email );
776                 }
777         }
778 }
779
780 /*
781 * Remove email address in address cache for specified ID.
782 * param: uid    Object ID for person.
783 *        eid    EMail ID.
784 * return: EMail object, or NULL if not found. Note that object should still be freed.
785 */
786 ItemEMail *addrcache_person_remove_email_id( AddressCache *cache, const gchar *uid, const gchar *eid ) {
787         ItemEMail *email = NULL;
788         ItemPerson *person;
789
790         if( eid == NULL || *eid == '\0' ) return NULL;
791
792         person = addrcache_get_person( cache, uid );
793         if( person ) {
794                 email = addritem_person_remove_email_id( person, eid );
795                 if( email ) {
796                         // Remove email from all groups.
797                         g_hash_table_foreach( cache->itemHash, addrcache_allgrp_rem_email_vis, email );
798
799                         // Remove email from person's address list
800                         if( person->listEMail ) {
801                                 person->listEMail = g_list_remove( person->listEMail, email );
802                         }
803                         // Unlink reference to person.
804                         ADDRITEM_PARENT(email) = NULL;
805                 }
806         }
807         return email;
808 }
809
810 /*
811 * Remove email address in address cache for specified person.
812 * param: person Person.
813 *        email  EMail to remove.
814 * return: EMail object, or NULL if not found. Note that object should still be freed.
815 */
816 ItemEMail *addrcache_person_remove_email( AddressCache *cache, ItemPerson *person, ItemEMail *email ) {
817         ItemEMail *found = NULL;
818
819         g_return_val_if_fail( cache != NULL, NULL );
820
821         if( person && email ) {
822                 found = addritem_person_remove_email( person, email );
823                 if( found ) {
824                         // Remove email from all groups.
825                         g_hash_table_foreach( cache->itemHash, addrcache_allgrp_rem_email_vis, email );
826
827                         // Remove email from person's address list
828                         if( person->listEMail ) {
829                                 person->listEMail = g_list_remove( person->listEMail, email );
830                         }
831                         // Unlink reference to person.
832                         ADDRITEM_PARENT(email) = NULL;
833                 }
834         }
835         return found;
836 }
837
838 /*
839 * Return link list of address items for root level folder. Note that the list contains
840 * references to items and should be g_free() when done. Do *NOT* attempt to use the
841 * addrcache_free_xxx() functions... this will destroy the address cache data!
842 * Return: List of items, or NULL if none.
843 */
844 GList *addrcache_folder_get_address_list( AddressCache *cache, ItemFolder *folder ) {
845         GList *list = NULL;
846         GList *node = NULL;
847         ItemFolder *f = folder;
848
849         g_return_val_if_fail( cache != NULL, NULL );
850
851         if( ! f ) f = cache->rootFolder;
852         node = f->listPerson;
853         while( node ) {
854                 list = g_list_append( list, node->data );
855                 node = g_list_next( node );
856         }
857         node = f->listGroup;
858         while( node ) {
859                 list = g_list_append( list, node->data );
860                 node = g_list_next( node );
861         }
862         return list;
863 }
864
865 /*
866 * Return link list of persons for specified folder. Note that the list contains
867 * references to items and should be g_free() when done. Do *NOT* attempt to use the
868 * addrcache_free_xxx() functions... this will destroy the address cache data!
869 * Return: List of items, or NULL if none.
870 */
871 GList *addrcache_folder_get_person_list( AddressCache *cache, ItemFolder *folder ) {
872         ItemFolder *f = folder;
873
874         g_return_val_if_fail( cache != NULL, NULL );
875
876         if( ! f ) f = cache->rootFolder;
877         return addritem_folder_get_person_list( f );
878 }
879
880 /*
881 * Return link list of group items for specified folder. Note that the list contains
882 * references to items and should be g_free() when done. Do *NOT* attempt to use the
883 * addrcache_free_xxx() functions... this will destroy the address cache data!
884 * Return: List of items, or NULL if none.
885 */
886 GList *addrcache_folder_get_group_list( AddressCache *cache, ItemFolder *folder ) {
887         ItemFolder *f = folder;
888
889         g_return_val_if_fail( cache != NULL, NULL );
890
891         if( ! f ) f = cache->rootFolder;
892         return addritem_folder_get_group_list( f );
893 }
894
895 /*
896 * Return link list of folder items for specified folder. Note that the list contains
897 * references to items and should be g_free() when done. Do *NOT* attempt to used the
898 * addrcache_free_xxx() functions... this will destroy the address cache data!
899 * Return: List of items, or NULL if none.
900 */
901 GList *addrcache_folder_get_folder_list( AddressCache *cache, ItemFolder *folder ) {
902         GList *node = NULL;
903         GList *list = NULL;
904         ItemFolder *f = folder;
905
906         g_return_val_if_fail( cache != NULL, NULL );
907
908         if( ! f ) f = cache->rootFolder;
909         node = f->listFolder;
910         while( node ) {
911                 list = g_list_append( list, node->data );
912                 node = g_list_next( node );
913         }
914         return list;
915 }
916
917 /*
918 * Return link list of address items for root level folder. Note that the list contains
919 * references to items and should be g_free() when done. Do *NOT* attempt to used the
920 * addrcache_free_xxx() functions... this will destroy the address cache data!
921 * Return: List of items, or NULL if none.
922 */
923 GList *addrcache_get_address_list( AddressCache *cache ) {
924         g_return_val_if_fail( cache != NULL, NULL );
925         return addrcache_folder_get_address_list( cache, cache->rootFolder );
926 }
927
928 /*
929 * Return link list of persons for root level folder. Note that the list contains
930 * references to items and should be g_free() when done. Do *NOT* attempt to used the
931 * addrcache_free_xxx() functions... this will destroy the address cache data!
932 * Return: List of items, or NULL if none.
933 */
934 GList *addrcache_get_person_list( AddressCache *cache ) {
935         g_return_val_if_fail( cache != NULL, NULL );
936         return addritem_folder_get_person_list( cache->rootFolder );
937 }
938
939 /*
940 * Return link list of group items in root level folder. Note that the list contains
941 * references to items and should be g_free() when done. Do *NOT* attempt to used the
942 * addrcache_free_xxx() functions... this will destroy the address cache data!
943 * Return: List of items, or NULL if none.
944 */
945 GList *addrcache_get_group_list( AddressCache *cache ) {
946         g_return_val_if_fail( cache != NULL, NULL );
947         return cache->rootFolder->listGroup;
948 }
949
950 /*
951 * Return link list of folder items in root level folder. Note that the list contains
952 * references to items and should be g_free() when done. Do *NOT* attempt to used the
953 * addrcache_free_xxx() functions... this will destroy the address cache data!
954 * Return: List of items, or NULL if none.
955 */
956 GList *addrcache_get_folder_list( AddressCache *cache ) {
957         g_return_val_if_fail( cache != NULL, NULL );
958         return cache->rootFolder->listFolder;
959 }
960
961 /*
962 * Group visitor function.
963 */
964 static void addrcache_get_grp_person_vis( gpointer key, gpointer value, gpointer data ) {
965         AddrItemObject *obj = ( AddrItemObject * ) value;
966
967         if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) {
968                 AddressCache *cache = data;
969                 ItemGroup *group = ( ItemGroup * ) obj;
970                 ItemPerson *person = ( ItemPerson * ) cache->tempList->data;
971                 GList *node = group->listEMail;
972                 while( node ) {
973                         ItemEMail *email = ( ItemEMail * ) node->data;
974                         if( ADDRITEM_PARENT(email) == ADDRITEM_OBJECT(person) ) {
975                                 if( ! g_list_find( cache->tempList, group ) ) {
976                                         cache->tempList = g_list_append( cache->tempList, group );
977                                 }
978                         }
979                         node = g_list_next( node );
980                 }
981         }
982 }
983
984 /*
985 * Return link list of groups which contain a reference to specified person's email
986 * address.
987 */
988 GList *addrcache_get_group_for_person( AddressCache *cache, ItemPerson *person ) {
989         GList *list = NULL;
990
991         g_return_val_if_fail( cache != NULL, NULL );
992
993         cache->tempList = NULL;
994         cache->tempList = g_list_append( cache->tempList, person );
995         g_hash_table_foreach( cache->itemHash, addrcache_get_grp_person_vis, cache );
996         cache->tempList = g_list_remove( cache->tempList, person );
997         list = cache->tempList;
998         cache->tempList = NULL;
999         return list;
1000 }
1001
1002 /*
1003 * Find root folder for specified folder.
1004 * Enter: folder Folder to search.
1005 * Return: root folder, or NULL if not found.
1006 */
1007 ItemFolder *addrcache_find_root_folder( ItemFolder *folder ) {
1008         ItemFolder *item = folder;
1009         gint count = 0;
1010
1011         while( item ) {
1012                 if( item->isRoot ) break;
1013                 if( ++count > ADDRCACHE_MAX_SEARCH_COUNT ) {
1014                         item = NULL;
1015                         break;
1016                 }
1017                 item = ( ItemFolder * ) ADDRITEM_PARENT(folder);
1018         }
1019         return item;
1020 }
1021
1022 /*
1023 * Get all persons visitor function.
1024 */
1025 static void addrcache_get_all_persons_vis( gpointer key, gpointer value, gpointer data ) {
1026         AddrItemObject *obj = ( AddrItemObject * ) value;
1027
1028         if( ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON ) {
1029                 AddressCache *cache = data;
1030                 cache->tempList = g_list_append( cache->tempList, obj );
1031         }
1032 }
1033
1034 /*
1035 * Return link list of all persons in address cache.  Note that the list contains
1036 * references to items. Do *NOT* attempt to use the addrcache_free_xxx() functions...
1037 * this will destroy the address cache data!
1038 * Return: List of items, or NULL if none.
1039 */
1040 GList *addrcache_get_all_persons( AddressCache *cache ) {
1041         GList *list = NULL;
1042
1043         g_return_val_if_fail( cache != NULL, NULL );
1044
1045         cache->tempList = NULL;
1046         g_hash_table_foreach( cache->itemHash, addrcache_get_all_persons_vis, cache );
1047         list = cache->tempList;
1048         cache->tempList = NULL;
1049         return list;
1050 }
1051
1052 /*
1053 * Get all groups visitor function.
1054 */
1055 static void addrcache_get_all_groups_vis( gpointer key, gpointer value, gpointer data ) {
1056         AddrItemObject *obj = ( AddrItemObject * ) value;
1057
1058         if( ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP ) {
1059                 AddressCache *cache = data;
1060                 cache->tempList = g_list_append( cache->tempList, obj );
1061         }
1062 }
1063
1064 /*
1065 * Return link list of all groups in address cache.  Note that the list contains
1066 * references to items. Do *NOT* attempt to use the addrcache_free_xxx() functions...
1067 * this will destroy the address cache data!
1068 * Return: List of items, or NULL if none.
1069 */
1070 GList *addrcache_get_all_groups( AddressCache *cache ) {
1071         GList *list = NULL;
1072
1073         g_return_val_if_fail( cache != NULL, NULL );
1074
1075         cache->tempList = NULL;
1076         g_hash_table_foreach( cache->itemHash, addrcache_get_all_groups_vis, cache );
1077         list = cache->tempList;
1078         cache->tempList = NULL;
1079         return list;
1080 }
1081
1082 /*
1083 * Remove folder from cache. Children are re-parented to parent folder.
1084 * param: folder Folder to remove.
1085 * return: Folder, or NULL if not found. Note that object should still be freed.
1086 */
1087 ItemFolder *addrcache_remove_folder( AddressCache *cache, ItemFolder *folder ) {
1088         AddrItemObject *obj = NULL;
1089
1090         g_return_val_if_fail( cache != NULL, NULL );
1091
1092         if( folder ) {
1093                 gchar *uid = ADDRITEM_ID(folder);
1094                 if( uid == NULL || *uid == '\0' ) return NULL;
1095                 obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid );
1096                 if( obj ) {
1097                         ItemFolder *parent = ( ItemFolder * ) ADDRITEM_PARENT(folder);
1098                         GList *node;
1099                         AddrItemObject *aio;
1100                         if( ! parent ) parent = cache->rootFolder;
1101
1102                         // Re-parent children in folder
1103                         node = folder->listFolder;
1104                         while( node ) {
1105                                 aio = ( AddrItemObject * ) node->data;
1106                                 parent->listFolder = g_list_append( parent->listFolder, aio );
1107                                 aio->parent = ADDRITEM_OBJECT(parent);
1108                                 node = g_list_next( node );
1109                         }
1110                         node = folder->listPerson;
1111                         while( node ) {
1112                                 aio = ( AddrItemObject * ) node->data;
1113                                 parent->listPerson = g_list_append( parent->listPerson, aio );
1114                                 aio->parent = ADDRITEM_OBJECT(parent);
1115                                 node = g_list_next( node );
1116                         }
1117                         node = folder->listGroup;
1118                         while( node ) {
1119                                 aio = ( AddrItemObject * ) node->data;
1120                                 parent->listGroup = g_list_append( parent->listGroup, aio );
1121                                 aio->parent = ADDRITEM_OBJECT(parent);
1122                                 node = g_list_next( node );
1123                         }
1124
1125                         // Remove folder from parent's list and hash table
1126                         parent->listFolder = g_list_remove( parent->listFolder, folder );
1127                         ADDRITEM_PARENT(folder) = NULL;
1128                         g_hash_table_remove( cache->itemHash, uid );
1129                         return folder;
1130                 }
1131         }
1132         return NULL;
1133 }
1134
1135 /*
1136 * Remove folder from cache. Children are deleted.
1137 * param: folder Folder to remove.
1138 * return: Folder, or NULL if not found. Note that object should still be freed.
1139 */
1140 ItemFolder *addrcache_remove_folder_delete( AddressCache *cache, ItemFolder *folder ) {
1141         AddrItemObject *obj = NULL;
1142
1143         g_return_val_if_fail( cache != NULL, NULL );
1144
1145         if( folder ) {
1146                 gchar *uid = ADDRITEM_ID(folder);
1147                 if( uid == NULL || *uid == '\0' ) return NULL;
1148                 obj = ( AddrItemObject * ) g_hash_table_lookup( cache->itemHash, uid );
1149                 if( obj ) {
1150                         ItemFolder *parent = ( ItemFolder * ) ADDRITEM_PARENT(folder);
1151                         if( ! parent ) parent = cache->rootFolder;
1152
1153                         // Remove groups
1154                         while( folder->listGroup ) {
1155                                 ItemGroup *item = ( ItemGroup * ) folder->listGroup->data;
1156                                 item = addrcache_remove_group( cache, item );
1157                                 if( item ) {
1158                                         addritem_free_item_group( item );
1159                                         item = NULL;
1160                                 }
1161                         }
1162
1163                         while( folder->listPerson ) {
1164                                 ItemPerson *item = ( ItemPerson * ) folder->listPerson->data;
1165                                 item = addrcache_remove_person( cache, item );
1166                                 if( item ) {
1167                                         addritem_free_item_person( item );
1168                                         item = NULL;
1169                                 }
1170                         }
1171
1172                         // Recursive deletion of folder
1173                         while( folder->listFolder ) {
1174                                 ItemFolder *item = ( ItemFolder * ) folder->listFolder->data;
1175                                 item = addrcache_remove_folder_delete( cache, item );
1176                                 if( item ) {
1177                                         addritem_free_item_folder( item );
1178                                         item = NULL;
1179                                 }
1180                         }
1181
1182                         // Remove folder from parent's list and hash table
1183                         parent->listFolder = g_list_remove( parent->listFolder, folder );
1184                         ADDRITEM_PARENT(folder) = NULL;
1185                         g_hash_table_remove( cache->itemHash, uid );
1186                         return folder;
1187                 }
1188         }
1189         return NULL;
1190 }
1191
1192 /*
1193 * Add person and address data to cache.
1194 * Enter: cache     Cache.
1195 *        folder    Folder where to add person, or NULL for root folder.
1196 *        name      Common name.
1197 *        address   EMail address.
1198 *        remarks   Remarks.
1199 * Return: Person added. Do not *NOT* to use the addrbook_free_xxx() functions...
1200 * this will destroy the address book data.
1201 */
1202 ItemPerson *addrcache_add_contact( AddressCache *cache, ItemFolder *folder, const gchar *name,
1203                 const gchar *address, const gchar *remarks )
1204 {
1205         ItemPerson *person = NULL;
1206         ItemEMail *email = NULL;
1207         ItemFolder *f = folder;
1208
1209         g_return_val_if_fail( cache != NULL, NULL );
1210
1211         if( ! f ) f = cache->rootFolder;
1212
1213         // Create person object
1214         person = addritem_create_item_person();
1215         addritem_person_set_common_name( person, name );
1216         addrcache_id_person( cache, person );
1217         addrcache_folder_add_person( cache, f, person );
1218
1219         // Create email object
1220         email = addritem_create_item_email();
1221         addritem_email_set_address( email, address );
1222         addritem_email_set_remarks( email, remarks );
1223         addrcache_id_email( cache, email );
1224         addritem_person_add_email( person, email );
1225
1226         return person;
1227 }
1228
1229 /*
1230 * End of Source.
1231 */