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