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