2010-02-03 [mir] 3.7.5cvs5
[claws.git] / src / addrindex.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2001-2009 Match Grun and the Claws Mail team
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 3 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, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 /*
21  * General functions for accessing address index file.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
28 #include "defs.h"
29
30 #include <glib.h>
31 #include <glib/gi18n.h>
32
33 #include "mgutils.h"
34 #include "addritem.h"
35 #include "addrcache.h"
36 #include "addrbook.h"
37 #include "addressbook.h"
38 #include "addrindex.h"
39 #include "xml.h"
40 #include "addrquery.h"
41 #include "addr_compl.h"
42 #include "utils.h"
43 #include "alertpanel.h"
44
45 #ifndef DEV_STANDALONE
46 #include "prefs_gtk.h"
47 #include "codeconv.h"
48 #endif
49
50 #include "vcard.h"
51
52 #ifdef USE_JPILOT
53 #include "jpilot.h"
54 #endif
55
56 #ifdef USE_LDAP
57 #include "ldapserver.h"
58 #include "ldapctrl.h"
59 #include "ldapquery.h"
60 #include "ldapupdate.h"
61 #include "ldaputil.h"
62 #endif
63
64 #define TAG_ADDRESS_INDEX    "addressbook"
65
66 #define TAG_IF_ADDRESS_BOOK  "book_list"
67 #define TAG_IF_VCARD         "vcard_list"
68 #define TAG_IF_JPILOT        "jpilot_list"
69 #define TAG_IF_LDAP          "ldap_list"
70
71 #define TAG_DS_ADDRESS_BOOK  "book"
72 #define TAG_DS_VCARD         "vcard"
73 #define TAG_DS_JPILOT        "jpilot"
74 #define TAG_DS_LDAP          "server"
75
76 /* XML Attribute names */
77 #define ATTAG_BOOK_NAME       "name"
78 #define ATTAG_BOOK_FILE       "file"
79
80 #define ATTAG_VCARD_NAME      "name"
81 #define ATTAG_VCARD_FILE      "file"
82
83 #define ATTAG_JPILOT_NAME     "name"
84 #define ATTAG_JPILOT_FILE     "file"
85 #define ATTAG_JPILOT_CUSTOM_1 "custom-1"
86 #define ATTAG_JPILOT_CUSTOM_2 "custom-2"
87 #define ATTAG_JPILOT_CUSTOM_3 "custom-3"
88 #define ATTAG_JPILOT_CUSTOM_4 "custom-4"
89 #define ATTAG_JPILOT_CUSTOM   "custom-"
90
91 #define ATTAG_LDAP_NAME       "name"
92 #define ATTAG_LDAP_HOST       "host"
93 #define ATTAG_LDAP_PORT       "port"
94 #define ATTAG_LDAP_BASE_DN    "base-dn"
95 #define ATTAG_LDAP_BIND_DN    "bind-dn"
96 #define ATTAG_LDAP_BIND_PASS  "bind-pass"
97 #define ATTAG_LDAP_CRITERIA   "criteria"
98 #define ATTAG_LDAP_MAX_ENTRY  "max-entry"
99 #define ATTAG_LDAP_TIMEOUT    "timeout"
100 #define ATTAG_LDAP_MAX_AGE    "max-age"
101 #define ATTAG_LDAP_DYN_SEARCH "dyn-search"
102 #define ATTAG_LDAP_MATCH_OPT  "match-opt"
103 #define ATTAG_LDAP_ENABLE_TLS "enable-tls"
104 #define ATTAG_LDAP_ENABLE_SSL "enable-ssl"
105
106 #define ELTAG_LDAP_ATTR_SRCH  "attribute"
107 #define ATTAG_LDAP_ATTR_NAME  "name"
108
109 /* Attribute values */
110 #define ATVAL_BOOLEAN_YES         "yes"
111 #define ATVAL_BOOLEAN_NO          "no"
112 #define ATVAL_LDAP_MATCH_BEGIN    "begin-with"
113 #define ATVAL_LDAP_MATCH_CONTAINS "contains"
114
115 /* New attributes */
116 #define ATTAG_LDAP_DEFAULT    "default"
117
118 #define DISP_NEW_COMMON       _("Common addresses")
119 #define DISP_NEW_PERSONAL     _("Personal addresses")
120
121 /* Old address book */
122 #define TAG_IF_OLD_COMMON     "common_address"
123 #define TAG_IF_OLD_PERSONAL   "personal_address"
124
125 #define DISP_OLD_COMMON       _("Common address")
126 #define DISP_OLD_PERSONAL     _("Personal address")
127
128 /**
129  * Singleton object.
130  */
131 static AddressIndex *_addressIndex_ = NULL;
132
133 /*
134  * Define attribute name-value pair.
135  */
136 typedef struct _AddressIfAttr AddressIfAttrib;
137 struct _AddressIfAttr {
138         gchar *name;
139         gchar *value;
140 };
141
142 static AddressDataSource *addrindex_create_datasource   ( AddressIfType ifType );
143
144 static GList *addrindex_ds_get_all_persons      ( AddressDataSource *ds );
145 static GList *addrindex_ds_get_all_groups       ( AddressDataSource *ds );
146 static AddressDataSource *addrindex_get_datasource      ( AddressIndex *addrIndex,
147                                                   const gchar *cacheID );
148 static AddressInterface *addrindex_get_interface        ( AddressIndex *addrIndex,
149                                                   AddressIfType ifType );
150 static gint addrindex_write_to                  ( AddressIndex *addrIndex,
151                                           const gchar *newFile );
152
153 /*
154  * Define DOM fragment.
155  */
156 typedef struct _AddressIfFrag AddressIfFragment;
157 struct _AddressIfFrag {
158         gchar *name;
159         GList *children;
160         GList *attributes;
161 };
162
163 /**
164  * Build interface with default values.
165  *
166  * \param type Interface type.
167  * \param name Interface name.
168  * \param tagIf XML tag name for interface in address index file.
169  * \param tagDS XML tag name for datasource in address index file.
170  * \return Address interface object.
171 */
172 static AddressInterface *addrindex_create_interface(
173                 gint type, gchar *name, gchar *tagIf, gchar *tagDS )
174 {
175         AddressInterface *iface = g_new0( AddressInterface, 1 );
176
177         ADDRITEM_TYPE(iface) = ITEMTYPE_INTERFACE;
178         ADDRITEM_ID(iface) = NULL;
179         ADDRITEM_NAME(iface) = g_strdup( name );
180         ADDRITEM_PARENT(iface) = NULL;
181         ADDRITEM_SUBTYPE(iface) = type;
182         iface->type = type;
183         iface->name = g_strdup( name );
184         iface->listTag = g_strdup( tagIf );
185         iface->itemTag = g_strdup( tagDS );
186         iface->legacyFlag = FALSE;
187         iface->haveLibrary = TRUE;
188         iface->useInterface = TRUE;
189         iface->readOnly      = TRUE;
190
191         /* Set callbacks to NULL values - override for each interface */
192         iface->getAccessFlag = NULL;
193         iface->getModifyFlag = NULL;
194         iface->getReadFlag   = NULL;
195         iface->getStatusCode = NULL;
196         iface->getReadData   = NULL;
197         iface->getRootFolder = NULL;
198         iface->getListFolder = NULL;
199         iface->getListPerson = NULL;
200         iface->getAllPersons = NULL;
201         iface->getAllGroups  = NULL;
202         iface->getName       = NULL;
203         iface->listSource = NULL;
204
205         /* Search stuff */
206         iface->externalQuery = FALSE;
207         iface->searchOrder = 0;         /* Ignored */
208         iface->startSearch = NULL;
209         iface->stopSearch = NULL;
210
211         return iface;
212 }
213
214 /**
215  * Build table of of all address book interfaces.
216  * \param addrIndex Address index object.
217  */
218 static void addrindex_build_if_list( AddressIndex *addrIndex ) {
219         AddressInterface *iface;
220
221         /* Create intrinsic XML address book interface */
222         iface = addrindex_create_interface(
223                         ADDR_IF_BOOK, "Address Book", TAG_IF_ADDRESS_BOOK,
224                         TAG_DS_ADDRESS_BOOK );
225         iface->readOnly      = FALSE;
226         iface->getModifyFlag = ( void * ) addrbook_get_modified;
227         iface->getAccessFlag = ( void * ) addrbook_get_accessed;
228         iface->getReadFlag   = ( void * ) addrbook_get_read_flag;
229         iface->getStatusCode = ( void * ) addrbook_get_status;
230         iface->getReadData   = ( void * ) addrbook_read_data;
231         iface->getRootFolder = ( void * ) addrbook_get_root_folder;
232         iface->getListFolder = ( void * ) addrbook_get_list_folder;
233         iface->getListPerson = ( void * ) addrbook_get_list_person;
234         iface->getAllPersons = ( void * ) addrbook_get_all_persons;
235         iface->getAllGroups  = ( void * ) addrbook_get_all_groups;
236         iface->getName       = ( void * ) addrbook_get_name;
237         iface->setAccessFlag = ( void * ) addrbook_set_accessed;
238         iface->searchOrder   = 0;
239
240         /* Add to list of interfaces in address book */ 
241         addrIndex->interfaceList =
242                 g_list_append( addrIndex->interfaceList, iface );
243         ADDRITEM_PARENT(iface) = ADDRITEM_OBJECT(addrIndex);
244
245         /* Create vCard interface */
246         iface = addrindex_create_interface(
247                         ADDR_IF_VCARD, "vCard", TAG_IF_VCARD, TAG_DS_VCARD );
248         iface->getModifyFlag = ( void * ) vcard_get_modified;
249         iface->getAccessFlag = ( void * ) vcard_get_accessed;
250         iface->getReadFlag   = ( void * ) vcard_get_read_flag;
251         iface->getStatusCode = ( void * ) vcard_get_status;
252         iface->getReadData   = ( void * ) vcard_read_data;
253         iface->getRootFolder = ( void * ) vcard_get_root_folder;
254         iface->getListFolder = ( void * ) vcard_get_list_folder;
255         iface->getListPerson = ( void * ) vcard_get_list_person;
256         iface->getAllPersons = ( void * ) vcard_get_all_persons;
257         iface->getName       = ( void * ) vcard_get_name;
258         iface->setAccessFlag = ( void * ) vcard_set_accessed;
259         iface->searchOrder   = 0;
260         addrIndex->interfaceList =
261                 g_list_append( addrIndex->interfaceList, iface );
262         ADDRITEM_PARENT(iface) = ADDRITEM_OBJECT(addrIndex);
263
264         /* Create JPilot interface */
265         iface = addrindex_create_interface(
266                         ADDR_IF_JPILOT, "J-Pilot", TAG_IF_JPILOT,
267                         TAG_DS_JPILOT );
268 #ifdef USE_JPILOT
269         iface->haveLibrary = jpilot_test_pilot_lib();
270         iface->useInterface = iface->haveLibrary;
271         iface->getModifyFlag = ( void * ) jpilot_get_modified;
272         iface->getAccessFlag = ( void * ) jpilot_get_accessed;
273         iface->getReadFlag   = ( void * ) jpilot_get_read_flag;
274         iface->getStatusCode = ( void * ) jpilot_get_status;
275         iface->getReadData   = ( void * ) jpilot_read_data;
276         iface->getRootFolder = ( void * ) jpilot_get_root_folder;
277         iface->getListFolder = ( void * ) jpilot_get_list_folder;
278         iface->getListPerson = ( void * ) jpilot_get_list_person;
279         iface->getAllPersons = ( void * ) jpilot_get_all_persons;
280         iface->getName       = ( void * ) jpilot_get_name;
281         iface->setAccessFlag = ( void * ) jpilot_set_accessed;
282         iface->searchOrder   = 0;
283 #else
284         iface->useInterface = FALSE;
285         iface->haveLibrary = FALSE;
286 #endif
287         addrIndex->interfaceList =
288                 g_list_append( addrIndex->interfaceList, iface );
289         ADDRITEM_PARENT(iface) = ADDRITEM_OBJECT(addrIndex);
290
291         /* Create LDAP interface */
292         iface = addrindex_create_interface(
293                         ADDR_IF_LDAP, "LDAP", TAG_IF_LDAP, TAG_DS_LDAP );
294 #ifdef USE_LDAP
295         iface->readOnly = FALSE;
296         /* iface->haveLibrary = ldapsvr_test_ldap_lib(); */
297         iface->haveLibrary = ldaputil_test_ldap_lib();
298         iface->useInterface = iface->haveLibrary;
299         iface->getModifyFlag = ( void * ) ldapsvr_get_modified;
300         iface->getAccessFlag = ( void * ) ldapsvr_get_accessed;
301         iface->getReadFlag   = ( void * ) ldapsvr_get_read_flag;
302         iface->getStatusCode = ( void * ) ldapsvr_get_status;
303         iface->getReadData   = ( void * ) ldapsvr_read_data;
304         iface->getRootFolder = ( void * ) ldapsvr_get_root_folder;
305         iface->getListFolder = ( void * ) ldapsvr_get_list_folder;
306         iface->getListPerson = ( void * ) ldapsvr_get_list_person;
307         iface->getName       = ( void * ) ldapsvr_get_name;
308         iface->setAccessFlag = ( void * ) ldapsvr_set_accessed;
309         iface->externalQuery = TRUE;
310         iface->searchOrder   = 1;
311 #else
312         iface->useInterface = FALSE;
313         iface->haveLibrary = FALSE;
314 #endif
315         addrIndex->interfaceList =
316                 g_list_append( addrIndex->interfaceList, iface );
317         ADDRITEM_PARENT(iface) = ADDRITEM_OBJECT(addrIndex);
318
319         /* Two old legacy data sources (pre 0.7.0) */
320         iface = addrindex_create_interface(
321                         ADDR_IF_COMMON, "Old Address - common",
322                         TAG_IF_OLD_COMMON, NULL );
323         iface->legacyFlag = TRUE;
324         addrIndex->interfaceList =
325                 g_list_append( addrIndex->interfaceList, iface );
326         ADDRITEM_PARENT(iface) = ADDRITEM_OBJECT(addrIndex);
327
328         iface = addrindex_create_interface(
329                         ADDR_IF_COMMON, "Old Address - personal",
330                         TAG_IF_OLD_PERSONAL, NULL );
331         iface->legacyFlag = TRUE;
332         addrIndex->interfaceList =
333                 g_list_append( addrIndex->interfaceList, iface );
334         ADDRITEM_PARENT(iface) = ADDRITEM_OBJECT(addrIndex);
335
336 }
337
338 /**
339  * Free DOM fragment.
340  * \param fragment Fragment to free.
341  */
342 static void addrindex_free_fragment( AddressIfFragment *fragment ) {
343         GList *node;
344
345         /* Free children */
346         node = fragment->children;
347         while( node ) {
348                 AddressIfFragment *child = node->data;
349                 addrindex_free_fragment( child );
350                 node->data = NULL;
351                 node = g_list_next( node );
352         }
353         g_list_free( fragment->children );
354
355         /* Free attributes */
356         node = fragment->attributes;
357         while( node ) {
358                 AddressIfAttrib *nv = node->data;
359                 g_free( nv->name );
360                 g_free( nv->value );
361                 g_free( nv );
362                 node->data = NULL;
363                 node = g_list_next( node );
364         }
365         g_list_free( fragment->attributes );
366
367         g_free( fragment->name );
368         fragment->name = NULL;
369         fragment->attributes = NULL;
370         fragment->children = NULL;
371
372         g_free( fragment );
373 }
374
375 /**
376  * Create a new data source.
377  * \param ifType Interface type to create.
378  * \return Initialized data source.
379  */
380 AddressDataSource *addrindex_create_datasource( AddressIfType ifType ) {
381         AddressDataSource *ds = g_new0( AddressDataSource, 1 );
382
383         ADDRITEM_TYPE(ds) = ITEMTYPE_DATASOURCE;
384         ADDRITEM_ID(ds) = NULL;
385         ADDRITEM_NAME(ds) = NULL;
386         ADDRITEM_PARENT(ds) = NULL;
387         ADDRITEM_SUBTYPE(ds) = 0;
388         ds->type = ifType;
389         ds->rawDataSource = NULL;
390         ds->interface = NULL;
391         return ds;
392 }
393
394 /**
395  * Free up data source.
396  * \param ds Data source to free.
397  */
398 void addrindex_free_datasource( AddressDataSource *ds ) {
399         AddressInterface *iface;
400
401         cm_return_if_fail( ds != NULL );
402
403         iface = ds->interface;
404         if( ds->rawDataSource != NULL ) {
405                 if( iface != NULL ) {
406                         if( iface->useInterface ) {
407                                 if( iface->type == ADDR_IF_BOOK ) {
408                                         AddressBookFile *abf = ds->rawDataSource;
409                                         addrbook_free_book( abf );
410                                 }
411                                 else if( iface->type == ADDR_IF_VCARD ) {
412                                         VCardFile *vcf = ds->rawDataSource;
413                                         vcard_free( vcf );
414                                 }
415 #ifdef USE_JPILOT
416                                 else if( iface->type == ADDR_IF_JPILOT ) {
417                                         JPilotFile *jpf = ds->rawDataSource;
418                                         jpilot_free( jpf );
419                                 }
420 #endif
421 #ifdef USE_LDAP
422                                 else if( iface->type == ADDR_IF_LDAP ) {
423                                         LdapServer *server = ds->rawDataSource;
424                                         ldapsvr_free( server );
425                                 }
426 #endif
427                                 else {
428                                 }
429                         }
430                         else {
431                                 AddressIfFragment *fragment = ds->rawDataSource;
432                                 addrindex_free_fragment( fragment );
433                         }
434                 }
435         }
436
437         ADDRITEM_TYPE(ds) = ITEMTYPE_NONE;
438         ADDRITEM_ID(ds) = NULL;
439         ADDRITEM_NAME(ds) = NULL;
440         ADDRITEM_PARENT(ds) = NULL;
441         ADDRITEM_SUBTYPE(ds) = 0;
442         ds->type = ADDR_IF_NONE;
443         ds->interface = NULL;
444         ds->rawDataSource = NULL;
445
446         g_free( ds );
447 }
448
449 /**
450  * Free up all data sources for specified interface.
451  * \param iface Address interface to process.
452  */
453 static void addrindex_free_all_datasources( AddressInterface *iface ) {
454         GList *node = iface->listSource;
455         while( node ) {
456                 AddressDataSource *ds = node->data;
457                 addrindex_free_datasource( ds );
458                 node->data = NULL;
459                 node = g_list_next( node );
460         }
461 }
462
463 /**
464  * Free up specified interface.
465  * \param iface Interface to process.
466  */
467 static void addrindex_free_interface( AddressInterface *iface ) {
468         /* Free up data sources */
469         addrindex_free_all_datasources( iface );
470         g_list_free( iface->listSource );
471
472         /* Free internal storage */
473         g_free( ADDRITEM_ID(iface) );
474         g_free( ADDRITEM_NAME(iface) );
475         g_free( iface->name );
476         g_free( iface->listTag );
477         g_free( iface->itemTag );
478
479         /* Clear all pointers */
480         ADDRITEM_TYPE(iface) = ITEMTYPE_NONE;
481         ADDRITEM_ID(iface) = NULL;
482         ADDRITEM_NAME(iface) = NULL;
483         ADDRITEM_PARENT(iface) = NULL;
484         ADDRITEM_SUBTYPE(iface) = 0;
485         iface->type = ADDR_IF_NONE;
486         iface->name = NULL;
487         iface->listTag = NULL;
488         iface->itemTag = NULL;
489         iface->legacyFlag = FALSE;
490         iface->useInterface = FALSE;
491         iface->haveLibrary = FALSE;
492         iface->listSource = NULL;
493
494         /* Search stuff */
495         iface->searchOrder = 0;
496         iface->startSearch = NULL;
497         iface->stopSearch = NULL;
498
499         g_free( iface );
500 }
501
502 /**
503  * Return cache ID for specified data source.
504  *
505  * \param  addrIndex Address index.
506  * \param  ds        Data source.
507  * \return ID or NULL if not found. This should be <code>g_free()</code>
508  *         when done.
509  */
510 gchar *addrindex_get_cache_id( AddressIndex *addrIndex, AddressDataSource *ds ) {
511         gchar *cacheID = NULL;
512         AddrBookBase *adbase;
513         AddressCache *cache;
514
515         cm_return_val_if_fail( addrIndex != NULL, NULL );
516         cm_return_val_if_fail( ds != NULL, NULL );
517
518         adbase = ( AddrBookBase * ) ds->rawDataSource;
519         if( adbase ) {
520                 cache = adbase->addressCache;
521                 if( cache ) {
522                         cacheID = g_strdup( cache->cacheID );
523                 }
524         }
525
526         return cacheID;
527 }
528
529 /**
530  * Return reference to data source for specified cacheID.
531  * \param addrIndex Address index.
532  * \param cacheID   ID.
533  * \return Data source, or NULL if not found.
534  */
535 static AddressDataSource *addrindex_get_datasource(
536                 AddressIndex *addrIndex, const gchar *cacheID )
537 {
538         cm_return_val_if_fail( addrIndex != NULL, NULL );
539         cm_return_val_if_fail( cacheID != NULL, NULL );
540         return ( AddressDataSource * ) g_hash_table_lookup( addrIndex->hashCache, cacheID );
541 }
542
543 /**
544  * Return reference to address cache for specified cacheID.
545  * \param addrIndex Address index.
546  * \param cacheID   ID.
547  * \return Address cache, or NULL if not found.
548  */
549 AddressCache *addrindex_get_cache( AddressIndex *addrIndex, const gchar *cacheID ) {
550         AddressDataSource *ds;
551         AddrBookBase *adbase;
552         AddressCache *cache;
553
554         cm_return_val_if_fail( addrIndex != NULL, NULL );
555         cm_return_val_if_fail( cacheID != NULL, NULL );
556
557         cache = NULL;
558         ds = addrindex_get_datasource( addrIndex, cacheID );
559         if( ds ) {
560                 adbase = ( AddrBookBase * ) ds->rawDataSource;
561                 cache = adbase->addressCache;
562         }
563         return cache;
564 }
565
566 /**
567  * Add data source into hash table.
568  * \param addrIndex Address index.
569  * \param ds        Data source.
570  */
571 static void addrindex_hash_add_cache(
572                 AddressIndex *addrIndex, AddressDataSource *ds )
573 {
574         gchar *cacheID;
575
576         cacheID = addrindex_get_cache_id( addrIndex, ds );
577         if( cacheID ) {
578                 g_hash_table_insert( addrIndex->hashCache, cacheID, ds );
579         }
580 }
581
582 /*
583  * Free hash table callback function.
584  */
585 static gboolean addrindex_free_cache_cb( gpointer key, gpointer value, gpointer data ) {
586         g_free( key );
587         key = NULL;
588         value = NULL;
589         return TRUE;
590 }
591
592 /*
593  * Free hash table of address cache items.
594  */
595 static void addrindex_free_cache_hash( GHashTable *table ) {
596         g_hash_table_foreach_remove( table, addrindex_free_cache_cb, NULL );
597         g_hash_table_destroy( table );
598 }
599
600 /**
601  * Remove data source from internal hashtable.
602  * \param addrIndex Address index.
603  * \param ds        Data source to remove.
604  */
605 static void addrindex_hash_remove_cache(
606                 AddressIndex *addrIndex, AddressDataSource *ds )
607 {
608         gchar *cacheID;
609
610         cacheID = addrindex_get_cache_id( addrIndex, ds );
611         if( cacheID ) {
612                 g_hash_table_remove( addrIndex->hashCache, cacheID );
613                 g_free( cacheID );
614                 cacheID = NULL;
615         }
616 }
617
618 /**
619  * Create a new address index. This is created as a singleton object.
620  * \return Initialized address index object.
621  */
622 AddressIndex *addrindex_create_index( void ) {
623         AddressIndex *index;
624
625         if( _addressIndex_ == NULL ) {
626                 index = g_new0( AddressIndex, 1 );
627                 ADDRITEM_TYPE(index) = ITEMTYPE_INDEX;
628                 ADDRITEM_ID(index) = NULL;
629                 ADDRITEM_NAME(index) = g_strdup( "Address Index" );
630                 ADDRITEM_PARENT(index) = NULL;
631                 ADDRITEM_SUBTYPE(index) = 0;
632                 index->filePath = NULL;
633                 index->fileName = NULL;
634                 index->retVal = MGU_SUCCESS;
635                 index->needsConversion = FALSE;
636                 index->wasConverted = FALSE;
637                 index->conversionError = FALSE;
638                 index->interfaceList = NULL;
639                 index->lastType = ADDR_IF_NONE;
640                 index->dirtyFlag = FALSE;
641                 index->hashCache = g_hash_table_new( g_str_hash, g_str_equal );
642                 index->loadedFlag = FALSE;
643                 index->searchOrder = NULL;
644                 addrindex_build_if_list( index );
645                 _addressIndex_ = index;
646         }
647         return _addressIndex_;
648 }
649
650 /**
651  * Property - Specify file path to address index file.
652  * \param addrIndex Address index.
653  * \param value Path to index file.
654  */
655 void addrindex_set_file_path( AddressIndex *addrIndex, const gchar *value ) {
656         cm_return_if_fail( addrIndex != NULL );
657         addrIndex->filePath = mgu_replace_string( addrIndex->filePath, value );
658 }
659
660 /**
661  * Property - Specify file name to address index file.
662  * \param addrIndex Address index.
663  * \param value File name.
664  */
665 void addrindex_set_file_name( AddressIndex *addrIndex, const gchar *value ) {
666         cm_return_if_fail( addrIndex != NULL );
667         addrIndex->fileName = mgu_replace_string( addrIndex->fileName, value );
668 }
669
670 /**
671  * Return list of address interfaces.
672  * \param addrIndex Address index.
673  * \return List of address interfaces.
674  */
675 GList *addrindex_get_interface_list( AddressIndex *addrIndex ) {
676         cm_return_val_if_fail( addrIndex != NULL, NULL );
677         return addrIndex->interfaceList;
678 }
679
680 /**
681  * Perform any other initialization of address index.
682  */
683 void addrindex_initialize( void ) {
684         qrymgr_initialize();
685         addrcompl_initialize();
686 }
687
688 /**
689  * Perform any other teardown of address index.
690  */
691 void addrindex_teardown( void ) {
692         addrcompl_teardown();
693         qrymgr_teardown();
694 }
695
696 /**
697  * Free up address index.
698  * \param addrIndex Address index.
699  */
700 void addrindex_free_index( AddressIndex *addrIndex ) {
701         GList *node;
702
703         cm_return_if_fail( addrIndex != NULL );
704
705         /* Search stuff */
706         g_list_free( addrIndex->searchOrder );
707         addrIndex->searchOrder = NULL;
708
709         /* Free internal storage */
710         g_free( ADDRITEM_ID(addrIndex) );
711         g_free( ADDRITEM_NAME(addrIndex) );
712         g_free( addrIndex->filePath );
713         g_free( addrIndex->fileName );
714
715         /* Clear pointers */    
716         ADDRITEM_TYPE(addrIndex) = ITEMTYPE_NONE;
717         ADDRITEM_ID(addrIndex) = NULL;
718         ADDRITEM_NAME(addrIndex) = NULL;
719         ADDRITEM_PARENT(addrIndex) = NULL;
720         ADDRITEM_SUBTYPE(addrIndex) = 0;
721         addrIndex->filePath = NULL;
722         addrIndex->fileName = NULL;
723         addrIndex->retVal = MGU_SUCCESS;
724         addrIndex->needsConversion = FALSE;
725         addrIndex->wasConverted = FALSE;
726         addrIndex->conversionError = FALSE;
727         addrIndex->lastType = ADDR_IF_NONE;
728         addrIndex->dirtyFlag = FALSE;
729
730         /* Free up interfaces */        
731         node = addrIndex->interfaceList;
732         while( node ) {
733                 AddressInterface *iface = node->data;
734                 addrindex_free_interface( iface );
735                 node = g_list_next( node );
736         }
737         g_list_free( addrIndex->interfaceList );
738         addrIndex->interfaceList = NULL;
739
740         /* Free up hash cache */
741         addrindex_free_cache_hash( addrIndex->hashCache );
742         addrIndex->hashCache = NULL;
743
744         addrIndex->loadedFlag = FALSE;
745
746         g_free( addrIndex );
747         addrIndex = NULL;
748         _addressIndex_ = NULL;
749 }
750
751 /**
752  * Print address index.
753  * \param addrIndex Address index.
754  * \parem stream    Stream to print.
755 */
756 void addrindex_print_index( AddressIndex *addrIndex, FILE *stream ) {
757         cm_return_if_fail( addrIndex != NULL );
758         fprintf( stream, "AddressIndex:\n" );
759         fprintf( stream, "\tfile path: '%s'\n", addrIndex->filePath );
760         fprintf( stream, "\tfile name: '%s'\n", addrIndex->fileName );
761         fprintf( stream, "\t   status: %d\n", addrIndex->retVal );
762         fprintf( stream, "\tconverted: '%s'\n",
763                         addrIndex->wasConverted ? "yes" : "no" );
764         fprintf( stream, "\tcvt error: '%s'\n",
765                         addrIndex->conversionError ? "yes" : "no" );
766         fprintf( stream, "\t---\n" );
767 }
768
769 /**
770  * Retrieve reference to address interface for specified interface type.
771  * \param  addrIndex Address index.
772  * \param  ifType Interface type.
773  * \return Address interface, or NULL if not found.
774  */
775 static AddressInterface *addrindex_get_interface(
776         AddressIndex *addrIndex, AddressIfType ifType )
777 {
778         AddressInterface *retVal = NULL;
779         GList *node;
780
781         cm_return_val_if_fail( addrIndex != NULL, NULL );
782
783         node = addrIndex->interfaceList;
784         while( node ) {
785                 AddressInterface *iface = node->data;
786                 node = g_list_next( node );
787                 if( iface->type == ifType ) {
788                         retVal = iface;
789                         break;
790                 }
791         }
792         return retVal;
793 }
794
795 /**
796  * Add raw data source to index. The raw data object (an AddressBookFile or
797  * VCardFile object, for example) should be supplied as the raw dataSource
798  * argument.
799  *
800  * \param  addrIndex Address index.
801  * \param ifType     Interface type to add.
802  * \param dataSource Actual raw data source to add. 
803  * \return Data source added, or NULL if invalid interface type.
804  */
805 AddressDataSource *addrindex_index_add_datasource(
806         AddressIndex *addrIndex, AddressIfType ifType, gpointer dataSource )
807 {
808         AddressInterface *iface;
809         AddressDataSource *ds = NULL;
810
811         cm_return_val_if_fail( addrIndex != NULL, NULL );
812         cm_return_val_if_fail( dataSource != NULL, NULL );
813
814         iface = addrindex_get_interface( addrIndex, ifType );
815         if( iface ) {
816                 ds = addrindex_create_datasource( ifType );
817                 ADDRITEM_PARENT(ds) = ADDRITEM_OBJECT(iface);
818                 ds->type = ifType;
819                 ds->rawDataSource = dataSource;
820                 ds->interface = iface;
821                 iface->listSource = g_list_append( iface->listSource, ds );
822                 addrIndex->dirtyFlag = TRUE;
823
824                 addrindex_hash_add_cache( addrIndex, ds );
825         }
826         return ds;
827 }
828
829 /**
830  * Remove specified data source from index.
831  * \param  addrIndex Address index.
832  * \param  dataSource Data source to add. 
833  * \return Reference to data source if removed, or NULL if data source was not
834  *         found in index. Note the this object must still be freed.
835  */
836 AddressDataSource *addrindex_index_remove_datasource(
837         AddressIndex *addrIndex, AddressDataSource *dataSource )
838 {
839         AddressDataSource *retVal = FALSE;
840         AddressInterface *iface;
841
842         cm_return_val_if_fail( addrIndex != NULL, NULL );
843         cm_return_val_if_fail( dataSource != NULL, NULL );
844
845         iface = addrindex_get_interface( addrIndex, dataSource->type );
846         if( iface ) {
847                 iface->listSource = g_list_remove( iface->listSource, dataSource );
848                 addrIndex->dirtyFlag = TRUE;
849                 dataSource->interface = NULL;
850
851                 /* Remove cache from hash table */
852                 addrindex_hash_remove_cache( addrIndex, dataSource );
853
854                 retVal = dataSource;
855         }
856         return retVal;
857 }
858
859 /**
860  * Retrieve a reference to address interface for specified interface type and
861  * XML interface tag name.
862  * \param  addrIndex Address index.
863  * \param  tag       XML interface tag name to match.
864  * \param  ifType    Interface type to match.
865  * \return Reference to address index, or NULL if not found in index.
866  */
867 static AddressInterface *addrindex_tag_get_interface(
868         AddressIndex *addrIndex, gchar *tag, AddressIfType ifType )
869 {
870         AddressInterface *retVal = NULL;
871         GList *node = addrIndex->interfaceList;
872
873         while( node ) {
874                 AddressInterface *iface = node->data;
875                 node = g_list_next( node );
876                 if( tag ) {
877                         if( strcmp( iface->listTag, tag ) == 0 ) {
878                                 retVal = iface;
879                                 break;
880                         }
881                 }
882                 else {
883                         if( iface->type == ifType ) {
884                                 retVal = iface;
885                                 break;
886                         }
887                 }
888         }
889         return retVal;
890 }
891
892 /**
893  * Retrieve a reference to address interface for specified interface type and
894  * XML datasource tag name.
895  * \param  addrIndex Address index.
896  * \param  ifType    Interface type to match.
897  * \param  tag       XML datasource tag name to match.
898  * \return Reference to address index, or NULL if not found in index.
899  */
900 static AddressInterface *addrindex_tag_get_datasource(
901         AddressIndex *addrIndex, AddressIfType ifType, gchar *tag )
902 {
903         AddressInterface *retVal = NULL;
904         GList *node = addrIndex->interfaceList;
905
906         while( node ) {
907                 AddressInterface *iface = node->data;
908                 node = g_list_next( node );
909                 if( iface->type == ifType && iface->itemTag ) {
910                         if( strcmp( iface->itemTag, tag ) == 0 ) {
911                                 retVal = iface;
912                                 break;
913                         }
914                 }
915         }
916         return retVal;
917 }
918
919 /* **********************************************************************
920 * Interface XML parsing functions.
921 * ***********************************************************************
922 */
923
924 /**
925  * Write start of XML element to file.
926  * \param fp   File.
927  * \param lvl  Indentation level.
928  * \param name Element name.
929  */
930 static int addrindex_write_elem_s( FILE *fp, const gint lvl, const gchar *name ) {
931         gint i;
932         for( i = 0; i < lvl; i++ ) 
933                 if (fputs( "  ", fp ) == EOF)
934                         return -1;
935         if (fputs( "<", fp ) == EOF)
936                 return -1;
937         if (fputs( name, fp ) == EOF)
938                 return -1;
939         return 0;
940 }
941
942 /**
943  * Write end of XML element to file.
944  * \param fp   File.
945  * \param lvl  Indentation level.
946  * \param name Element name.
947  */
948 static int addrindex_write_elem_e( FILE *fp, const gint lvl, const gchar *name ) {
949         gint i;
950         for( i = 0; i < lvl; i++ ) 
951                 if (fputs( "  ", fp ) == EOF)
952                         return -1;
953         if (fputs( "</", fp ) == EOF)
954                 return -1;
955         if (fputs( name, fp ) == EOF)
956                 return -1;
957         if (fputs( ">\n", fp ) == EOF)
958                 return -1;
959         return 0;
960 }
961
962 /**
963  * Write XML attribute to file.
964  * \param fp    File.
965  * \param name  Attribute name.
966  * \param value Attribute value.
967  */
968 static int addrindex_write_attr( FILE *fp, const gchar *name, const gchar *value ) {
969         if (fputs( " ", fp ) == EOF)
970                 return -1;
971         if (fputs( name, fp ) == EOF)
972                 return -1;
973         if (fputs( "=\"", fp ) == EOF)
974                 return -1;
975         if (xml_file_put_escape_str( fp, value ) < 0)
976                 return -1;
977         if (fputs( "\"", fp ) == EOF)
978                 return -1;
979         return 0;
980 }
981
982 #if !defined(USE_LDAP) || !defined(USE_JPILOT)
983 /**
984  * Return DOM fragment for current XML tag from file.
985  * \param  file XML file being processed.
986  * \return Fragment representing DOM fragment for configuration element.
987  */
988 static AddressIfFragment *addrindex_read_fragment( XMLFile *file ) {
989         AddressIfFragment *fragment;
990         AddressIfFragment *child;
991         AddressIfAttrib *nv;
992         XMLTag *xtag;
993         GList *list;
994         GList *attr;
995         gchar *name;
996         gchar *value;
997         guint prevLevel;
998         gint rc;
999
1000         /* g_print( "addrindex_read_fragment\n" ); */
1001
1002         prevLevel = file->level;
1003
1004         /* Get current tag name */
1005         xtag = xml_get_current_tag( file );
1006
1007         /* Create new fragment */
1008         fragment = g_new0( AddressIfFragment, 1 );
1009         fragment->name = g_strdup( xtag->tag );
1010         fragment->children = NULL;
1011         fragment->attributes = NULL;
1012
1013         /* Read attributes */
1014         list = NULL;
1015         attr = xml_get_current_tag_attr( file );
1016         while( attr ) {
1017                 name = ((XMLAttr *)attr->data)->name;
1018                 value = ((XMLAttr *)attr->data)->value;
1019                 nv = g_new0( AddressIfAttrib, 1 );
1020                 nv->name = g_strdup( name );
1021                 nv->value = g_strdup( value );
1022                 list = g_list_append( list, nv );
1023                 attr = g_list_next( attr );
1024         }
1025         fragment->attributes = list;
1026
1027         /* Now read the children */
1028         while( TRUE ) {
1029                 rc = xml_parse_next_tag( file );
1030                 if( rc != 0 ) {
1031                         /* End of file? */
1032                         break;
1033                 }
1034                 if( file->level < prevLevel ) {
1035                         /* We must be above level we start at */
1036                         break;
1037                 }
1038                 child = addrindex_read_fragment( file );
1039                 fragment->children = g_list_append( fragment->children, child );
1040         }
1041
1042         return fragment;
1043 }
1044
1045 /**
1046  * Write DOM fragment to file.
1047  * \param fp       File to write.
1048  * \param fragment DOM fragment for configuration element.
1049  * \param lvl      Indent level.
1050  */
1051 static int addrindex_write_fragment(
1052                 FILE *fp, const AddressIfFragment *fragment, const gint lvl )
1053 {
1054         GList *node;
1055
1056         if( fragment ) {
1057                 if (addrindex_write_elem_s( fp, lvl, fragment->name ) < 0)
1058                         return -1;
1059                 node = fragment->attributes;
1060                 while( node ) {
1061                         AddressIfAttrib *nv = node->data;
1062                         if (addrindex_write_attr( fp, nv->name, nv->value ) < 0)
1063                                 return -1;
1064                         node = g_list_next( node );
1065                 }
1066                 if( fragment->children ) {
1067                         if (fputs(" >\n", fp) == EOF)
1068                                 return -1;
1069
1070                         /* Output children */
1071                         node = fragment->children;
1072                         while( node ) {
1073                                 AddressIfFragment *child = node->data;
1074                                 if (addrindex_write_fragment( fp, child, 1+lvl ) < 0)
1075                                         return -1;
1076                                 node = g_list_next( node );
1077                         }
1078
1079                         /* Output closing tag */
1080                         if (addrindex_write_elem_e( fp, lvl, fragment->name ) < 0)
1081                                 return -1;
1082                 }
1083                 else {
1084                         if (fputs(" />\n", fp) == EOF)
1085                                 return -1;
1086                 }
1087         }
1088         
1089         return 0;
1090 }
1091 #endif
1092 /**
1093  * Read/parse address index file, creating a data source for a regular
1094  * intrinsic XML addressbook.
1095  * \param  file Address index file.
1096  * \return Data source.
1097  */
1098 static AddressDataSource *addrindex_parse_book( XMLFile *file ) {
1099         AddressDataSource *ds;
1100         AddressBookFile *abf;
1101         GList *attr;
1102
1103         ds = addrindex_create_datasource( ADDR_IF_BOOK );
1104         abf = addrbook_create_book();
1105         attr = xml_get_current_tag_attr( file );
1106         while( attr ) {
1107                 gchar *name = ((XMLAttr *)attr->data)->name;
1108                 gchar *value = ((XMLAttr *)attr->data)->value;
1109                 if( strcmp( name, ATTAG_BOOK_NAME ) == 0 ) {
1110                         addrbook_set_name( abf, value );
1111                 }
1112                 else if( strcmp( name, ATTAG_BOOK_FILE ) == 0) {
1113                         addrbook_set_file( abf, value );
1114                 }
1115                 attr = g_list_next( attr );
1116         }
1117         ds->rawDataSource = abf;
1118         return ds;
1119 }
1120
1121 static int addrindex_write_book( FILE *fp, AddressDataSource *ds, gint lvl ) {
1122         AddressBookFile *abf = ds->rawDataSource;
1123         if( abf ) {
1124                 if (addrindex_write_elem_s( fp, lvl, TAG_DS_ADDRESS_BOOK ) < 0)
1125                         return -1;
1126                 if (addrindex_write_attr( fp, ATTAG_BOOK_NAME, addrbook_get_name( abf ) ) < 0)
1127                         return -1;
1128                 if (addrindex_write_attr( fp, ATTAG_BOOK_FILE, abf->fileName ) < 0)
1129                         return -1;
1130                 if (fputs( " />\n", fp ) == EOF)
1131                         return -1;
1132         }
1133         return 0;
1134 }
1135
1136 static AddressDataSource *addrindex_parse_vcard( XMLFile *file ) {
1137         AddressDataSource *ds;
1138         VCardFile *vcf;
1139         GList *attr;
1140
1141         ds = addrindex_create_datasource( ADDR_IF_VCARD );
1142         vcf = vcard_create();
1143         attr = xml_get_current_tag_attr( file );
1144         while( attr ) {
1145                 gchar *name = ((XMLAttr *)attr->data)->name;
1146                 gchar *value = ((XMLAttr *)attr->data)->value;
1147                 if( strcmp( name, ATTAG_VCARD_NAME ) == 0 ) {
1148                         vcard_set_name( vcf, value );
1149                 }
1150                 else if( strcmp( name, ATTAG_VCARD_FILE ) == 0) {
1151                         vcard_set_file( vcf, value );
1152                 }
1153                 attr = g_list_next( attr );
1154         }
1155         ds->rawDataSource = vcf;
1156         return ds;
1157 }
1158
1159 static int addrindex_write_vcard( FILE *fp, AddressDataSource *ds, gint lvl ) {
1160         VCardFile *vcf = ds->rawDataSource;
1161         if( vcf ) {
1162                 if (addrindex_write_elem_s( fp, lvl, TAG_DS_VCARD ) < 0)
1163                         return -1;
1164                 if (addrindex_write_attr( fp, ATTAG_VCARD_NAME, vcard_get_name( vcf ) ) < 0)
1165                         return -1;
1166                 if (addrindex_write_attr( fp, ATTAG_VCARD_FILE, vcf->path ) < 0)
1167                         return -1;
1168                 if (fputs( " />\n", fp ) == EOF)
1169                         return -1;
1170         }
1171         return 0;
1172 }
1173
1174 #ifdef USE_JPILOT
1175 static AddressDataSource *addrindex_parse_jpilot( XMLFile *file ) {
1176         AddressDataSource *ds;
1177         JPilotFile *jpf;
1178         GList *attr;
1179
1180         ds = addrindex_create_datasource( ADDR_IF_JPILOT );
1181         jpf = jpilot_create();
1182         attr = xml_get_current_tag_attr( file );
1183         while( attr ) {
1184                 gchar *name = ((XMLAttr *)attr->data)->name;
1185                 gchar *value = ((XMLAttr *)attr->data)->value;
1186                 if( strcmp( name, ATTAG_JPILOT_NAME ) == 0 ) {
1187                         jpilot_set_name( jpf, value );
1188                 }
1189                 else if( strcmp( name, ATTAG_JPILOT_FILE ) == 0 ) {
1190                         jpilot_set_file( jpf, value );
1191                 }
1192                 else if( strcmp( name, ATTAG_JPILOT_CUSTOM_1 ) == 0 ) {
1193                         jpilot_add_custom_label( jpf, value );
1194                 }
1195                 else if( strcmp( name, ATTAG_JPILOT_CUSTOM_2 ) == 0 ) {
1196                         jpilot_add_custom_label( jpf, value );
1197                 }
1198                 else if( strcmp( name, ATTAG_JPILOT_CUSTOM_3 ) == 0 ) {
1199                         jpilot_add_custom_label( jpf, value );
1200                 }
1201                 else if( strcmp( name, ATTAG_JPILOT_CUSTOM_4 ) == 0 ) {
1202                         jpilot_add_custom_label( jpf, value );
1203                 }
1204                 attr = g_list_next( attr );
1205         }
1206         ds->rawDataSource = jpf;
1207         return ds;
1208 }
1209
1210 static int addrindex_write_jpilot( FILE *fp,AddressDataSource *ds, gint lvl ) {
1211         JPilotFile *jpf = ds->rawDataSource;
1212         if( jpf ) {
1213                 gint ind;
1214                 GList *node;
1215                 GList *customLbl = jpilot_get_custom_labels( jpf );
1216                 if (addrindex_write_elem_s( fp, lvl, TAG_DS_JPILOT ) < 0)
1217                         return -1;
1218                 if (addrindex_write_attr( fp, ATTAG_JPILOT_NAME, jpilot_get_name( jpf ) ) < 0)
1219                         return -1;
1220                 if (addrindex_write_attr( fp, ATTAG_JPILOT_FILE, jpf->path ) < 0)
1221                         return -1;
1222                 node = customLbl;
1223                 ind = 1;
1224                 while( node ) {
1225                         gchar name[256];
1226                         g_snprintf( name, sizeof(name), "%s%d",
1227                                     ATTAG_JPILOT_CUSTOM, ind );
1228                         if (addrindex_write_attr( fp, name, node->data ) < 0)
1229                                 return -1;
1230                         ind++;
1231                         node = g_list_next( node );
1232                 }
1233                 if (fputs( " />\n", fp ) == EOF)
1234                         return -1;
1235         }
1236         return 0;
1237 }
1238
1239 #else
1240 /*
1241  * Just read/write DOM fragments (preserve data found in file).
1242  */
1243 static AddressDataSource *addrindex_parse_jpilot( XMLFile *file ) {
1244         AddressDataSource *ds;
1245
1246         ds = addrindex_create_datasource( ADDR_IF_JPILOT );
1247         ds->rawDataSource = addrindex_read_fragment( file );
1248         return ds;
1249 }
1250
1251 static int addrindex_write_jpilot( FILE *fp, AddressDataSource *ds, gint lvl ) {
1252         AddressIfFragment *fragment = ds->rawDataSource;
1253         if( fragment ) {
1254                 if (addrindex_write_fragment( fp, fragment, lvl ) < 0)
1255                         return -1;
1256         }
1257         return 0;
1258 }
1259 #endif
1260
1261 #ifdef USE_LDAP
1262 /**
1263  * Parse LDAP criteria attribute data from XML file.
1264  * \param file Index file.
1265  * \param ctl  LDAP control object to populate.
1266  */
1267 static void addrindex_parse_ldap_attrlist( XMLFile *file, LdapControl *ctl ) {
1268         guint prevLevel;
1269         XMLTag *xtag;
1270         XMLTag *xtagPrev;
1271         gint rc;
1272         GList *attr;
1273         GList *list;
1274         GList *node;
1275
1276         if( file == NULL ) {
1277                 return;
1278         }
1279
1280         list = NULL;
1281         prevLevel = file->level;
1282         xtagPrev = xml_get_current_tag( file );
1283         while( TRUE ) {
1284                 rc = xml_parse_next_tag( file );
1285                 if( rc != 0 ) {
1286                         /* Terminate prematurely */
1287                         mgu_free_dlist( list );
1288                         list = NULL;
1289                         return;
1290                 }
1291                 if( file->level < prevLevel ) {
1292                         /* We must be above level we start at */
1293                         break;
1294                 }
1295
1296                 /* Get a tag (element) */
1297                 xtag = xml_get_current_tag( file );
1298                 if( strcmp( xtag->tag, ELTAG_LDAP_ATTR_SRCH ) == 0 ) {
1299                         /* LDAP criteria attribute */
1300                         attr = xml_get_current_tag_attr( file );
1301                         while( attr ) {
1302                                 gchar *name = ((XMLAttr *)attr->data)->name;
1303                                 gchar *value = ((XMLAttr *)attr->data)->value;
1304                                 if( strcmp( name, ATTAG_LDAP_ATTR_NAME ) == 0 ) {
1305                                         if( value && strlen( value ) > 0 ) {
1306                                                 list = g_list_append(
1307                                                         list, g_strdup( value ) );
1308                                         }
1309                                 }
1310                                 attr = g_list_next( attr );
1311                         }
1312                 }
1313                 else {
1314                         if( xtag != xtagPrev ) {
1315                                 /* Found a new tag */
1316                                 break;
1317                         }
1318                 }
1319                 xtag = xtagPrev;
1320         }
1321
1322         /* Build list of search attributes */
1323         ldapctl_criteria_list_clear( ctl );
1324         node = list;
1325         while( node ) {
1326                 ldapctl_criteria_list_add( ctl, node->data );
1327                 g_free( node->data );
1328                 node->data = NULL;
1329                 node = g_list_next( node );
1330         }
1331         g_list_free( list );
1332         list = NULL;
1333
1334 }
1335
1336 void ldapsvr_set_control( LdapServer *server, LdapControl *ctl );
1337 /**
1338  * Parse LDAP control data from XML file.
1339  * \param  file Index file.
1340  * \return Initialized data soruce object.
1341  */
1342 static AddressDataSource *addrindex_parse_ldap( XMLFile *file ) {
1343         AddressDataSource *ds;
1344         LdapServer *server;
1345         LdapControl *ctl;
1346         GList *attr;
1347         gchar *serverName = NULL;
1348         gchar *criteria = NULL;
1349         gboolean bDynSearch;
1350         gboolean bTLS, bSSL;
1351         gint iMatch;
1352
1353         /* g_print( "addrindex_parse_ldap\n" ); */
1354         /* Set up some defaults */
1355         bDynSearch = FALSE;
1356         bTLS = FALSE;
1357         bSSL = FALSE;
1358         iMatch = LDAPCTL_MATCH_BEGINWITH;
1359
1360         ds = addrindex_create_datasource( ADDR_IF_LDAP );
1361         ctl = ldapctl_create();
1362         attr = xml_get_current_tag_attr( file );
1363         while( attr ) {
1364                 gchar *name = ((XMLAttr *)attr->data)->name;
1365                 gchar *value = ((XMLAttr *)attr->data)->value;
1366                 gint ivalue = atoi( value );
1367
1368                 if( strcmp( name, ATTAG_LDAP_NAME ) == 0 ) {
1369                         g_free( serverName );
1370                         serverName = g_strdup( value );
1371                 }
1372                 else if( strcmp( name, ATTAG_LDAP_HOST ) == 0 ) {
1373                         ldapctl_set_host( ctl, value );
1374                 }
1375                 else if( strcmp( name, ATTAG_LDAP_PORT ) == 0 ) {
1376                         ldapctl_set_port( ctl, ivalue );
1377                 }
1378                 else if( strcmp( name, ATTAG_LDAP_BASE_DN ) == 0 ) {
1379                         ldapctl_set_base_dn( ctl, value );
1380                 }
1381                 else if( strcmp( name, ATTAG_LDAP_BIND_DN ) == 0 ) {
1382                         ldapctl_set_bind_dn( ctl, value );
1383                 }
1384                 else if( strcmp( name, ATTAG_LDAP_BIND_PASS ) == 0 ) {
1385                         ldapctl_set_bind_password( ctl, value, FALSE, FALSE );
1386                 }
1387                 else if( strcmp( name, ATTAG_LDAP_CRITERIA ) == 0 ) {
1388                         g_free( criteria );
1389                         criteria = g_strdup( value );
1390                         g_print("criteria %s\n", criteria);
1391                 }
1392                 else if( strcmp( name, ATTAG_LDAP_MAX_ENTRY ) == 0 ) {
1393                         ldapctl_set_max_entries( ctl, ivalue );
1394                 }
1395                 else if( strcmp( name, ATTAG_LDAP_TIMEOUT ) == 0 ) {
1396                         ldapctl_set_timeout( ctl, ivalue );
1397                 }
1398                 else if( strcmp( name, ATTAG_LDAP_MAX_AGE ) == 0 ) {
1399                         ldapctl_set_max_query_age( ctl, ivalue );
1400                 }
1401                 else if( strcmp( name, ATTAG_LDAP_DYN_SEARCH ) == 0 ) {
1402                         bDynSearch = FALSE;
1403                         if( strcmp( value, ATVAL_BOOLEAN_YES ) == 0 ) {
1404                                 bDynSearch = TRUE;
1405                         }
1406                 }
1407                 else if( strcmp( name, ATTAG_LDAP_MATCH_OPT ) == 0 ) {
1408                         iMatch = LDAPCTL_MATCH_BEGINWITH;
1409                         if( strcmp( value, ATVAL_LDAP_MATCH_CONTAINS ) == 0 ) {
1410                                 iMatch = LDAPCTL_MATCH_CONTAINS;
1411                         }
1412                 }
1413                 else if( strcmp( name, ATTAG_LDAP_ENABLE_TLS ) == 0 ) {
1414                         bTLS = FALSE;
1415                         if( strcmp( value, ATVAL_BOOLEAN_YES ) == 0 ) {
1416                                 bTLS = TRUE;
1417                         }
1418                 }
1419                 else if( strcmp( name, ATTAG_LDAP_ENABLE_SSL ) == 0 ) {
1420                         bSSL = FALSE;
1421                         if( strcmp( value, ATVAL_BOOLEAN_YES ) == 0 ) {
1422                                 bSSL = TRUE;
1423                         }
1424                 }
1425                 attr = g_list_next( attr );
1426         }
1427
1428         server = ldapsvr_create_noctl();
1429         ldapsvr_set_name( server, serverName );
1430         ldapsvr_set_search_flag( server, bDynSearch );
1431         ldapctl_set_matching_option( ctl, iMatch );
1432 #ifdef USE_LDAP_TLS
1433         ldapctl_set_tls( ctl, bTLS );
1434         ldapctl_set_ssl( ctl, bSSL );
1435 #endif
1436         g_free( serverName );
1437         ldapsvr_set_control( server, ctl );
1438         ds->rawDataSource = server;
1439
1440         addrindex_parse_ldap_attrlist( file, ctl );
1441         /*
1442          * If criteria have been specified and no attributes were listed, then
1443          * convert old style criteria into an attribute list. Any criteria will
1444          * be dropped when saving data.
1445          */
1446         if( criteria ) {
1447                 if( ! ldapctl_get_criteria_list( ctl ) ) {
1448                         ldapctl_parse_ldap_search( ctl, criteria );
1449                 }
1450                 g_free( criteria );
1451         }
1452         /* ldapsvr_print_data( server, stdout ); */
1453
1454         return ds;
1455 }
1456
1457 static int addrindex_write_ldap( FILE *fp, AddressDataSource *ds, gint lvl ) {
1458         LdapServer *server = ds->rawDataSource;
1459         LdapControl *ctl = NULL;
1460         GList *node;
1461         gchar value[256];
1462
1463         if( server ) {
1464                 ctl = server->control;
1465         }
1466         if( ctl == NULL ) return 0;
1467
1468         /* Output start element with attributes */
1469         if (addrindex_write_elem_s( fp, lvl, TAG_DS_LDAP ) < 0)
1470                 return -1;
1471         if (addrindex_write_attr( fp, ATTAG_LDAP_NAME, ldapsvr_get_name( server ) ) < 0)
1472                 return -1;
1473         if (addrindex_write_attr( fp, ATTAG_LDAP_HOST, ctl->hostName ) < 0)
1474                 return -1;
1475
1476         sprintf( value, "%d", ctl->port );      
1477         if (addrindex_write_attr( fp, ATTAG_LDAP_PORT, value ) < 0)
1478                 return -1;
1479
1480         if (addrindex_write_attr( fp, ATTAG_LDAP_BASE_DN, ctl->baseDN ) < 0)
1481                 return -1;
1482         if (addrindex_write_attr( fp, ATTAG_LDAP_BIND_DN, ctl->bindDN ) < 0)
1483                 return -1;
1484         if (addrindex_write_attr( fp, ATTAG_LDAP_BIND_PASS, ctl->bindPass ) < 0)
1485                 return -1;
1486
1487         sprintf( value, "%d", ctl->maxEntries );
1488         if (addrindex_write_attr( fp, ATTAG_LDAP_MAX_ENTRY, value ) < 0)
1489                 return -1;
1490         sprintf( value, "%d", ctl->timeOut );
1491         if (addrindex_write_attr( fp, ATTAG_LDAP_TIMEOUT, value ) < 0)
1492                 return -1;
1493         sprintf( value, "%d", ctl->maxQueryAge );
1494         if (addrindex_write_attr( fp, ATTAG_LDAP_MAX_AGE, value ) < 0)
1495                 return -1;
1496
1497         if (addrindex_write_attr( fp, ATTAG_LDAP_DYN_SEARCH,
1498                         server->searchFlag ?
1499                         ATVAL_BOOLEAN_YES : ATVAL_BOOLEAN_NO ) < 0)
1500                 return -1;
1501
1502         if (addrindex_write_attr( fp, ATTAG_LDAP_MATCH_OPT,
1503                 ( ctl->matchingOption == LDAPCTL_MATCH_CONTAINS ) ?
1504                 ATVAL_LDAP_MATCH_CONTAINS : ATVAL_LDAP_MATCH_BEGIN ) < 0)
1505                 return -1;
1506
1507         if (addrindex_write_attr( fp, ATTAG_LDAP_ENABLE_TLS,
1508                         ctl->enableTLS ?
1509                         ATVAL_BOOLEAN_YES : ATVAL_BOOLEAN_NO ) < 0)
1510                 return -1;
1511         if (addrindex_write_attr( fp, ATTAG_LDAP_ENABLE_SSL,
1512                         ctl->enableSSL ?
1513                         ATVAL_BOOLEAN_YES : ATVAL_BOOLEAN_NO ) < 0)
1514                 return -1;
1515
1516         if (fputs(" >\n", fp) == EOF)
1517                 return -1;
1518
1519         /* Output attributes */
1520         node = ldapctl_get_criteria_list( ctl );
1521         while( node ) {
1522                 if (addrindex_write_elem_s( fp, 1+lvl, ELTAG_LDAP_ATTR_SRCH ) < 0)
1523                         return -1;
1524                 if (addrindex_write_attr( fp, ATTAG_LDAP_ATTR_NAME, node->data ) < 0)
1525                         return -1;
1526                 if (fputs(" />\n", fp) == EOF)
1527                         return -1;
1528                 node = g_list_next( node );
1529         }
1530
1531         /* End of element */    
1532         if (addrindex_write_elem_e( fp, lvl, TAG_DS_LDAP ) < 0)
1533                 return -1;
1534         
1535         return 0;
1536 }
1537
1538 #else
1539 /*
1540  * Just read/write DOM fragments (preserve data found in file).
1541  */
1542 static AddressDataSource *addrindex_parse_ldap( XMLFile *file ) {
1543         AddressDataSource *ds;
1544
1545         ds = addrindex_create_datasource( ADDR_IF_LDAP );
1546         ds->rawDataSource = addrindex_read_fragment( file );
1547         return ds;
1548 }
1549
1550 static int addrindex_write_ldap( FILE *fp, AddressDataSource *ds, gint lvl ) {
1551         AddressIfFragment *fragment = ds->rawDataSource;
1552         if( fragment ) {
1553                 if (addrindex_write_fragment( fp, fragment, lvl ) < 0)
1554                         return -1;
1555         }
1556         return 0;
1557 }
1558 #endif
1559
1560 /* **********************************************************************
1561 * Address index I/O functions.
1562 * ***********************************************************************
1563 */
1564 /**
1565  * Read address index file, creating appropriate data sources for each address
1566  * index file entry.
1567  *
1568  * \param  addrIndex Address index.
1569  * \param  file Address index file.
1570  */
1571 static void addrindex_read_index( AddressIndex *addrIndex, XMLFile *file ) {
1572         guint prev_level;
1573         XMLTag *xtag;
1574         AddressInterface *iface = NULL, *dsIFace = NULL;
1575         AddressDataSource *ds;
1576         gint rc;
1577
1578         addrIndex->loadedFlag = FALSE;
1579         for (;;) {
1580                 prev_level = file->level;
1581                 rc = xml_parse_next_tag( file );
1582                 if( file->level == 0 ) return;
1583
1584                 xtag = xml_get_current_tag( file );
1585
1586                 iface = addrindex_tag_get_interface( addrIndex, xtag->tag, ADDR_IF_NONE );
1587                 if( iface ) {
1588                         addrIndex->lastType = iface->type;
1589                         if( iface->legacyFlag ) addrIndex->needsConversion = TRUE;
1590                 }
1591                 else {
1592                         dsIFace = addrindex_tag_get_datasource(
1593                                         addrIndex, addrIndex->lastType, xtag->tag );
1594                         if( dsIFace ) {
1595                                 /* Add data source to list */
1596                                 ds = NULL;
1597                                 if( addrIndex->lastType == ADDR_IF_BOOK ) {
1598                                         ds = addrindex_parse_book( file );
1599                                         if( ds->rawDataSource ) {
1600                                                 addrbook_set_path( ds->rawDataSource,
1601                                                         addrIndex->filePath );
1602                                         }
1603                                 }
1604                                 else if( addrIndex->lastType == ADDR_IF_VCARD ) {
1605                                         ds = addrindex_parse_vcard( file );
1606                                 }
1607                                 else if( addrIndex->lastType == ADDR_IF_JPILOT ) {
1608                                         ds = addrindex_parse_jpilot( file );
1609                                 }
1610                                 else if( addrIndex->lastType == ADDR_IF_LDAP ) {
1611                                         ds = addrindex_parse_ldap( file );
1612                                 }
1613                                 if( ds ) {
1614                                         ds->interface = dsIFace;
1615                                         addrindex_hash_add_cache( addrIndex, ds );
1616                                         dsIFace->listSource =
1617                                                 g_list_append( dsIFace->listSource, ds );
1618                                 }
1619                         }
1620                 }
1621         }
1622 }
1623
1624 /*
1625  * Search order sorting comparison function for building search order list.
1626  */
1627 static gint addrindex_search_order_compare( gconstpointer ptrA, gconstpointer ptrB ) {
1628         AddressInterface *ifaceA = ( AddressInterface * ) ptrA;
1629         AddressInterface *ifaceB = ( AddressInterface * ) ptrB;
1630
1631         return ifaceA->searchOrder - ifaceB->searchOrder;
1632 }
1633
1634 /**
1635  * Build list of data sources to process.
1636  * \param addrIndex Address index object.
1637  */
1638 static void addrindex_build_search_order( AddressIndex *addrIndex ) {
1639         GList *nodeIf;
1640
1641         /* Clear existing list */
1642         g_list_free( addrIndex->searchOrder );
1643         addrIndex->searchOrder = NULL;
1644
1645         /* Build new list */
1646         nodeIf = addrIndex->interfaceList;
1647         while( nodeIf ) {
1648                 AddressInterface *iface = nodeIf->data;
1649                 if( iface->useInterface ) {
1650                         if( iface->searchOrder > 0 ) {
1651                                 /* Add to search order list */
1652                                 addrIndex->searchOrder = g_list_insert_sorted(
1653                                         addrIndex->searchOrder, iface,
1654                                         addrindex_search_order_compare );
1655                         }
1656                 }
1657                 nodeIf = g_list_next( nodeIf );
1658         }
1659 }
1660
1661 static gint addrindex_read_file( AddressIndex *addrIndex ) {
1662         XMLFile *file = NULL;
1663         gchar *fileSpec = NULL;
1664
1665         cm_return_val_if_fail( addrIndex != NULL, -1 );
1666
1667         fileSpec = g_strconcat( addrIndex->filePath, G_DIR_SEPARATOR_S, addrIndex->fileName, NULL );
1668         addrIndex->retVal = MGU_NO_FILE;
1669         file = xml_open_file( fileSpec );
1670         g_free( fileSpec );
1671
1672         if( file == NULL ) {
1673                 /*
1674                 g_print( " file '%s' does not exist.\n", addrIndex->fileName );
1675                 */
1676                 return addrIndex->retVal;
1677         }
1678
1679         addrIndex->retVal = MGU_BAD_FORMAT;
1680         if( xml_get_dtd( file ) == 0 ) {
1681                 if( xml_parse_next_tag( file ) == 0 ) {
1682                         if( xml_compare_tag( file, TAG_ADDRESS_INDEX ) ) {
1683                                 addrindex_read_index( addrIndex, file );
1684                                 addrIndex->retVal = MGU_SUCCESS;
1685                         }
1686                 }
1687         }
1688         xml_close_file( file );
1689
1690         addrindex_build_search_order( addrIndex );
1691
1692         return addrIndex->retVal;
1693 }
1694
1695 static int addrindex_write_index( AddressIndex *addrIndex, FILE *fp ) {
1696         GList *nodeIF, *nodeDS;
1697         gint lvlList = 1;
1698         gint lvlItem = 1 + lvlList;
1699
1700         nodeIF = addrIndex->interfaceList;
1701         while( nodeIF ) {
1702                 AddressInterface *iface = nodeIF->data;
1703                 if( ! iface->legacyFlag ) {
1704                         nodeDS = iface->listSource;
1705                         if (addrindex_write_elem_s( fp, lvlList, iface->listTag ) < 0)
1706                                 return -1;
1707                         if (fputs( ">\n", fp ) == EOF)
1708                                 return -1;
1709                         while( nodeDS ) {
1710                                 AddressDataSource *ds = nodeDS->data;
1711                                 if( ds ) {
1712                                         if( iface->type == ADDR_IF_BOOK ) {
1713                                                 if (addrindex_write_book( fp, ds, lvlItem ) < 0)
1714                                                         return -1;
1715                                         }
1716                                         if( iface->type == ADDR_IF_VCARD ) {
1717                                                 if (addrindex_write_vcard( fp, ds, lvlItem ) < 0)
1718                                                         return -1;
1719                                         }
1720                                         if( iface->type == ADDR_IF_JPILOT ) {
1721                                                 if (addrindex_write_jpilot( fp, ds, lvlItem ) < 0)
1722                                                         return -1;
1723                                         }
1724                                         if( iface->type == ADDR_IF_LDAP ) {
1725                                                 if (addrindex_write_ldap( fp, ds, lvlItem ) < 0)
1726                                                         return -1;
1727                                         }
1728                                 }
1729                                 nodeDS = g_list_next( nodeDS );
1730                         }
1731                         if (addrindex_write_elem_e( fp, lvlList, iface->listTag ) < 0)
1732                                 return -1;
1733                 }
1734                 nodeIF = g_list_next( nodeIF );
1735         }
1736         return 0;
1737 }
1738
1739 /*
1740 * Write data to specified file.
1741 * Enter: addrIndex Address index object.
1742 *        newFile   New file name.
1743 * return: Status code, from addrIndex->retVal.
1744 * Note: File will be created in directory specified by addrIndex.
1745 */
1746 static gint addrindex_write_to( AddressIndex *addrIndex, const gchar *newFile ) {
1747         FILE *fp;
1748         gchar *fileSpec;
1749 #ifndef DEV_STANDALONE
1750         PrefFile *pfile;
1751 #endif
1752
1753         cm_return_val_if_fail( addrIndex != NULL, -1 );
1754
1755         fileSpec = g_strconcat( addrIndex->filePath, G_DIR_SEPARATOR_S, newFile, NULL );
1756         addrIndex->retVal = MGU_OPEN_FILE;
1757 #ifdef DEV_STANDALONE
1758         fp = g_fopen( fileSpec, "wb" );
1759         g_free( fileSpec );
1760         if( fp ) {
1761                 fputs( "<?xml version=\"1.0\" ?>\n", fp );
1762 #else
1763         pfile = prefs_write_open( fileSpec );
1764         g_free( fileSpec );
1765         if( pfile ) {
1766                 fp = pfile->fp;
1767                 if (fprintf( fp, "<?xml version=\"1.0\" encoding=\"%s\" ?>\n", CS_INTERNAL ) < 0)
1768                         goto fail;
1769 #endif
1770                 if (addrindex_write_elem_s( fp, 0, TAG_ADDRESS_INDEX ) < 0)
1771                         goto fail;
1772                 if (fputs( ">\n", fp ) == EOF)
1773                         goto fail;
1774
1775                 if (addrindex_write_index( addrIndex, fp ) < 0)
1776                         goto fail;
1777                 if (addrindex_write_elem_e( fp, 0, TAG_ADDRESS_INDEX ) < 0)
1778                         goto fail;
1779
1780                 addrIndex->retVal = MGU_SUCCESS;
1781 #ifdef DEV_STANDALONE
1782                 fclose( fp );
1783 #else
1784                 if( prefs_file_close( pfile ) < 0 ) {
1785                         addrIndex->retVal = MGU_ERROR_WRITE;
1786                 }
1787 #endif
1788         }
1789
1790         fileSpec = NULL;
1791         return addrIndex->retVal;
1792 fail:
1793         g_warning("error writing AB index\n");
1794         addrIndex->retVal = MGU_ERROR_WRITE;
1795         if (pfile)
1796                 prefs_file_close_revert( pfile );
1797         return addrIndex->retVal;
1798 }
1799
1800 /*
1801 * Save address index data to original file.
1802 * return: Status code, from addrIndex->retVal.
1803 */
1804 gint addrindex_save_data( AddressIndex *addrIndex ) {
1805 #ifdef USE_LDAP
1806         GList *nodeIf;
1807         GList *nodeDS;
1808 #endif
1809         
1810         cm_return_val_if_fail( addrIndex != NULL, -1 );
1811
1812 #ifdef USE_LDAP
1813         nodeIf = addrIndex->interfaceList;
1814         /* save LDAP interfaces */
1815         while ( nodeIf ) {
1816                 AddressInterface *iface = nodeIf->data;
1817                 if( iface->type == ADDR_IF_LDAP ) {
1818                         nodeDS = iface->listSource;
1819                         while( nodeDS ) {
1820                                 AddressDataSource *ds = nodeDS->data;
1821                                 LdapServer *abf = ds->rawDataSource;
1822                                 if( ldapsvr_get_read_flag( abf ) ) {
1823                                         if( ldapsvr_get_modified( abf ) ) {
1824                                                 ldapsvr_update_book( abf, NULL );
1825                                                 if( abf->retVal != LDAPRC_SUCCESS ) {
1826                                                         alertpanel( _("Address(es) update"),
1827                                                                 _("Update failed. Changes not written to Directory."),
1828                                                                 GTK_STOCK_CLOSE, NULL, NULL );
1829                                                 }
1830                                                 else {
1831                                                         abf->retVal = MGU_SUCCESS;
1832                                                         ldapsvr_set_modified( abf, FALSE );
1833                                                 }
1834                                         }
1835                                 }
1836                                 nodeDS = g_list_next( nodeDS );
1837                         }
1838                         break;
1839                 }
1840                 nodeIf = g_list_next( nodeIf );
1841         }
1842 #endif
1843         addrIndex->retVal = MGU_NO_FILE;
1844         if( addrIndex->fileName == NULL || *addrIndex->fileName == '\0' ) return addrIndex->retVal;
1845         if( addrIndex->filePath == NULL || *addrIndex->filePath == '\0' ) return addrIndex->retVal;
1846
1847         addrindex_write_to( addrIndex, addrIndex->fileName );
1848         if( addrIndex->retVal == MGU_SUCCESS ) {
1849                 addrIndex->dirtyFlag = FALSE;
1850         }
1851         return addrIndex->retVal;
1852 }
1853
1854 /*
1855 * Save all address book files which may have changed.
1856 * Return: Status code, set if there was a problem saving data.
1857 */
1858 gint addrindex_save_all_books( AddressIndex *addrIndex ) {
1859         gint retVal = MGU_SUCCESS;
1860         GList *nodeIf, *nodeDS;
1861
1862         nodeIf = addrIndex->interfaceList;
1863         while( nodeIf ) {
1864                 AddressInterface *iface = nodeIf->data;
1865                 if( iface->type == ADDR_IF_BOOK ) {
1866                         nodeDS = iface->listSource;
1867                         while( nodeDS ) {
1868                                 AddressDataSource *ds = nodeDS->data;
1869                                 AddressBookFile *abf = ds->rawDataSource;
1870                                 if( addrbook_get_dirty( abf ) ) {
1871                                         if( addrbook_get_read_flag( abf ) ) {
1872                                                 addrbook_save_data( abf );
1873                                                 if( abf->retVal != MGU_SUCCESS ) {
1874                                                         retVal = abf->retVal;
1875                                                 }
1876                                         }
1877                                 }
1878                                 nodeDS = g_list_next( nodeDS );
1879                         }
1880                         break;
1881                 }
1882                 nodeIf = g_list_next( nodeIf );
1883         }
1884         return retVal;
1885 }
1886
1887
1888 /* **********************************************************************
1889 * Address book conversion to new format.
1890 * ***********************************************************************
1891 */
1892
1893 #define ELTAG_IF_OLD_FOLDER   "folder"
1894 #define ELTAG_IF_OLD_GROUP    "group"
1895 #define ELTAG_IF_OLD_ITEM     "item"
1896 #define ELTAG_IF_OLD_NAME     "name"
1897 #define ELTAG_IF_OLD_ADDRESS  "address"
1898 #define ELTAG_IF_OLD_REMARKS  "remarks"
1899 #define ATTAG_IF_OLD_NAME     "name"
1900
1901 #define TEMPNODE_ROOT         0
1902 #define TEMPNODE_FOLDER       1
1903 #define TEMPNODE_GROUP        2
1904 #define TEMPNODE_ADDRESS      3
1905
1906 typedef struct _AddressCvt_Node AddressCvtNode;
1907 struct _AddressCvt_Node {
1908         gint  type;
1909         gchar *name;
1910         gchar *address;
1911         gchar *remarks;
1912         GList *list;
1913 };
1914
1915 /*
1916 * Parse current address item.
1917 */
1918 static AddressCvtNode *addrindex_parse_item( XMLFile *file ) {
1919         gchar *element;
1920         guint level;
1921         AddressCvtNode *nn;
1922
1923         nn = g_new0( AddressCvtNode, 1 );
1924         nn->type = TEMPNODE_ADDRESS;
1925         nn->list = NULL;
1926
1927         level = file->level;
1928
1929         for (;;) {
1930                 xml_parse_next_tag(file);
1931                 if (file->level < level) return nn;
1932
1933                 element = xml_get_element( file );
1934                 if( xml_compare_tag( file, ELTAG_IF_OLD_NAME ) ) {
1935                         nn->name = g_strdup( element );
1936                 }
1937                 if( xml_compare_tag( file, ELTAG_IF_OLD_ADDRESS ) ) {
1938                         nn->address = g_strdup( element );
1939                 }
1940                 if( xml_compare_tag( file, ELTAG_IF_OLD_REMARKS ) ) {
1941                         nn->remarks = g_strdup( element );
1942                 }
1943                 g_free(element);
1944                 xml_parse_next_tag(file);
1945         }
1946 }
1947
1948 /*
1949 * Create a temporary node below specified node.
1950 */
1951 static AddressCvtNode *addrindex_add_object( AddressCvtNode *node, gint type, gchar *name, gchar *addr, char *rem ) {
1952         AddressCvtNode *nn;
1953         nn = g_new0( AddressCvtNode, 1 );
1954         nn->type = type;
1955         nn->name = g_strdup( name );
1956         nn->remarks = g_strdup( rem );
1957         node->list = g_list_append( node->list, nn );
1958         return nn;
1959 }
1960
1961 /*
1962 * Process current temporary node.
1963 */
1964 static void addrindex_add_obj( XMLFile *file, AddressCvtNode *node ) {
1965         GList *attr;
1966         guint prev_level;
1967         AddressCvtNode *newNode = NULL;
1968         gchar *name;
1969         gchar *value;
1970
1971         for (;;) {
1972                 prev_level = file->level;
1973                 xml_parse_next_tag( file );
1974                 if (file->level < prev_level) return;
1975                 name = NULL;
1976                 value = NULL;
1977
1978                 if( xml_compare_tag( file, ELTAG_IF_OLD_GROUP ) ) {
1979                         attr = xml_get_current_tag_attr(file);
1980                         if (attr) {
1981                                 name = ((XMLAttr *)attr->data)->name;
1982                                 if( strcmp( name, ATTAG_IF_OLD_NAME ) == 0 ) {
1983                                         value = ((XMLAttr *)attr->data)->value;
1984                                 }
1985                         }
1986                         newNode = addrindex_add_object( node, TEMPNODE_GROUP, value, "", "" );
1987                         addrindex_add_obj( file, newNode );
1988
1989                 }
1990                 else if( xml_compare_tag( file, ELTAG_IF_OLD_FOLDER ) ) {
1991                         attr = xml_get_current_tag_attr(file);
1992                         if (attr) {
1993                                 name = ((XMLAttr *)attr->data)->name;
1994                                 if( strcmp( name, ATTAG_IF_OLD_NAME ) == 0 ) {
1995                                         value = ((XMLAttr *)attr->data)->value;
1996                                 }
1997                         }
1998                         newNode = addrindex_add_object( node, TEMPNODE_FOLDER, value, "", "" );
1999                         addrindex_add_obj( file, newNode );
2000                 }
2001                 else if( xml_compare_tag( file, ELTAG_IF_OLD_ITEM ) ) {
2002                         newNode = addrindex_parse_item( file );
2003                         node->list = g_list_append( node->list, newNode );
2004                 }
2005                 else {
2006                         /* g_print( "invalid: !!! \n" ); */
2007                         attr = xml_get_current_tag_attr( file );
2008                 }
2009         }
2010 }
2011
2012 /*
2013 * Consume all nodes below current tag.
2014 */
2015 static void addrindex_consume_tree( XMLFile *file ) {
2016         guint prev_level;
2017         gchar *element;
2018         GList *attr;
2019         XMLTag *xtag;
2020
2021         for (;;) {
2022                 prev_level = file->level;
2023                 xml_parse_next_tag( file );
2024                 if (file->level < prev_level) return;
2025
2026                 xtag = xml_get_current_tag( file );
2027                 /* g_print( "tag : %s\n", xtag->tag ); */
2028                 element = xml_get_element( file );
2029                 attr = xml_get_current_tag_attr( file );
2030                 /* show_attribs( attr ); */
2031                 /* g_print( "\ttag  value : %s :\n", element ); */
2032                 addrindex_consume_tree( file );
2033         }
2034 }
2035
2036 /*
2037 * Print temporary tree.
2038 */
2039 static void addrindex_print_node( AddressCvtNode *node, FILE *stream  ) {
2040         GList *list;
2041
2042         fprintf( stream, "Node:\ttype :%d:\n", node->type );
2043         fprintf( stream, "\tname :%s:\n", node->name );
2044         fprintf( stream, "\taddr :%s:\n", node->address );
2045         fprintf( stream, "\trems :%s:\n", node->remarks );
2046         if( node->list ) {
2047                 fprintf( stream, "\t--list----\n" );
2048         }
2049         list = node->list;
2050         while( list ) {
2051                 AddressCvtNode *lNode = list->data;
2052                 list = g_list_next( list );
2053                 addrindex_print_node( lNode, stream );
2054         }
2055         fprintf( stream, "\t==list-%d==\n", node->type );
2056 }
2057
2058 /*
2059 * Free up temporary tree.
2060 */
2061 static void addrindex_free_node( AddressCvtNode *node ) {
2062         GList *list = node->list;
2063
2064         while( list ) {
2065                 AddressCvtNode *lNode = list->data;
2066                 list = g_list_next( list );
2067                 addrindex_free_node( lNode );
2068         }
2069         node->type = TEMPNODE_ROOT;
2070         g_free( node->name );
2071         g_free( node->address );
2072         g_free( node->remarks );
2073         g_list_free( node->list );
2074         g_free( node );
2075 }
2076
2077 /*
2078 * Process address book for specified node.
2079 */
2080 static void addrindex_process_node(
2081                 AddressBookFile *abf, AddressCvtNode *node, ItemFolder *parent,
2082                 ItemGroup *parentGrp, ItemFolder *folderGrp )
2083 {
2084         GList *list;
2085         ItemFolder *itemFolder = NULL;
2086         ItemGroup *itemGParent = parentGrp;
2087         ItemFolder *itemGFolder = folderGrp;
2088         AddressCache *cache = abf->addressCache;
2089
2090         if( node->type == TEMPNODE_ROOT ) {
2091                 itemFolder = parent;
2092         }
2093         else if( node->type == TEMPNODE_FOLDER ) {
2094                 itemFolder = addritem_create_item_folder();
2095                 addritem_folder_set_name( itemFolder, node->name );
2096                 addrcache_id_folder( cache, itemFolder );
2097                 addrcache_folder_add_folder( cache, parent, itemFolder );
2098                 itemGFolder = NULL;
2099         }
2100         else if( node->type == TEMPNODE_GROUP ) {
2101                 ItemGroup *itemGroup;
2102                 gchar *fName;
2103
2104                 /* Create a folder for group */
2105                 fName = g_strdup_printf( "Cvt - %s", node->name );
2106                 itemGFolder = addritem_create_item_folder();
2107                 addritem_folder_set_name( itemGFolder, fName );
2108                 addrcache_id_folder( cache, itemGFolder );
2109                 addrcache_folder_add_folder( cache, parent, itemGFolder );
2110                 g_free( fName );
2111
2112                 /* Add group into folder */
2113                 itemGroup = addritem_create_item_group();
2114                 addritem_group_set_name( itemGroup, node->name );
2115                 addrcache_id_group( cache, itemGroup );
2116                 addrcache_folder_add_group( cache, itemGFolder, itemGroup );
2117                 itemGParent = itemGroup;
2118         }
2119         else if( node->type == TEMPNODE_ADDRESS ) {
2120                 ItemPerson *itemPerson;
2121                 ItemEMail *itemEMail;
2122
2123                 /* Create person and email objects */
2124                 itemPerson = addritem_create_item_person();
2125                 addritem_person_set_common_name( itemPerson, node->name );
2126                 addrcache_id_person( cache, itemPerson );
2127                 itemEMail = addritem_create_item_email();
2128                 addritem_email_set_address( itemEMail, node->address );
2129                 addritem_email_set_remarks( itemEMail, node->remarks );
2130                 addrcache_id_email( cache, itemEMail );
2131                 addrcache_person_add_email( cache, itemPerson, itemEMail );
2132
2133                 /* Add person into appropriate folder */
2134                 if( itemGFolder ) {
2135                         addrcache_folder_add_person( cache, itemGFolder, itemPerson );
2136                 }
2137                 else {
2138                         addrcache_folder_add_person( cache, parent, itemPerson );
2139                 }
2140
2141                 /* Add email address only into group */
2142                 if( parentGrp ) {
2143                         addrcache_group_add_email( cache, parentGrp, itemEMail );
2144                 }
2145         }
2146
2147         list = node->list;
2148         while( list ) {
2149                 AddressCvtNode *lNode = list->data;
2150                 list = g_list_next( list );
2151                 addrindex_process_node( abf, lNode, itemFolder, itemGParent, itemGFolder );
2152         }
2153 }
2154
2155 /*
2156 * Process address book to specified file number.
2157 */
2158 static gboolean addrindex_process_book( AddressIndex *addrIndex, XMLFile *file, gchar *displayName ) {
2159         gboolean retVal = FALSE;
2160         AddressBookFile *abf = NULL;
2161         AddressCvtNode *rootNode = NULL;
2162         gchar *newFile = NULL;
2163         GList *fileList = NULL;
2164         gint fileNum  = 0;
2165
2166         /* Setup root node */
2167         rootNode = g_new0( AddressCvtNode, 1 );
2168         rootNode->type = TEMPNODE_ROOT;
2169         rootNode->name = g_strdup( "root" );
2170         rootNode->list = NULL;
2171         addrindex_add_obj( file, rootNode );
2172         /* addrindex_print_node( rootNode, stdout ); */
2173
2174         /* Create new address book */
2175         abf = addrbook_create_book();
2176         addrbook_set_name( abf, displayName );
2177         addrbook_set_path( abf, addrIndex->filePath );
2178
2179         /* Determine next available file number */
2180         fileList = addrbook_get_bookfile_list( abf );
2181         if( fileList ) {
2182                 fileNum = 1 + abf->maxValue;
2183         }
2184         g_list_free( fileList );
2185         fileList = NULL;
2186
2187         newFile = addrbook_gen_new_file_name( fileNum );
2188         if( newFile ) {
2189                 addrbook_set_file( abf, newFile );
2190         }
2191
2192         addrindex_process_node( abf, rootNode, abf->addressCache->rootFolder, NULL, NULL );
2193
2194         /* addrbook_dump_book( abf, stdout ); */
2195         addrbook_save_data( abf );
2196         addrIndex->retVal = abf->retVal;
2197         if( abf->retVal == MGU_SUCCESS ) retVal = TRUE;
2198
2199         addrbook_free_book( abf );
2200         abf = NULL;
2201         addrindex_free_node( rootNode );
2202         rootNode = NULL;
2203
2204         /* Create entries in address index */
2205         if( retVal ) {
2206                 abf = addrbook_create_book();
2207                 addrbook_set_name( abf, displayName );
2208                 addrbook_set_path( abf, addrIndex->filePath );
2209                 addrbook_set_file( abf, newFile );
2210                 addrindex_index_add_datasource( addrIndex, ADDR_IF_BOOK, abf );
2211         }
2212
2213         return retVal;
2214 }
2215
2216 /*
2217 * Process tree converting data.
2218 */
2219 static void addrindex_convert_tree( AddressIndex *addrIndex, XMLFile *file ) {
2220         guint prev_level;
2221         gchar *element;
2222         GList *attr;
2223         XMLTag *xtag;
2224
2225         /* Process file */
2226         for (;;) {
2227                 prev_level = file->level;
2228                 xml_parse_next_tag( file );
2229                 if (file->level < prev_level) return;
2230
2231                 xtag = xml_get_current_tag( file );
2232                 /* g_print( "tag : %d : %s\n", prev_level, xtag->tag ); */
2233                 if( strcmp( xtag->tag, TAG_IF_OLD_COMMON ) == 0 ) {
2234                         if( addrindex_process_book( addrIndex, file, DISP_OLD_COMMON ) ) {
2235                                 addrIndex->needsConversion = FALSE;
2236                                 addrIndex->wasConverted = TRUE;
2237                                 continue;
2238                         }
2239                         return;
2240                 }
2241                 if( strcmp( xtag->tag, TAG_IF_OLD_PERSONAL ) == 0 ) {
2242                         if( addrindex_process_book( addrIndex, file, DISP_OLD_PERSONAL ) ) {
2243                                 addrIndex->needsConversion = FALSE;
2244                                 addrIndex->wasConverted = TRUE;
2245                                 continue;
2246                         }
2247                         return;
2248                 }
2249                 element = xml_get_element( file );
2250                 attr = xml_get_current_tag_attr( file );
2251                 /* show_attribs( attr ); */
2252                 /* g_print( "\ttag  value : %s :\n", element ); */
2253                 addrindex_consume_tree( file );
2254         }
2255 }
2256
2257 static gint addrindex_convert_data( AddressIndex *addrIndex ) {
2258         XMLFile *file = NULL;
2259         gchar *fileSpec;
2260
2261         fileSpec = g_strconcat( addrIndex->filePath, G_DIR_SEPARATOR_S, addrIndex->fileName, NULL );
2262         addrIndex->retVal = MGU_NO_FILE;
2263         file = xml_open_file( fileSpec );
2264         g_free( fileSpec );
2265
2266         if( file == NULL ) {
2267                 /* g_print( " file '%s' does not exist.\n", addrIndex->fileName ); */
2268                 return addrIndex->retVal;
2269         }
2270
2271         addrIndex->retVal = MGU_BAD_FORMAT;
2272         if( xml_get_dtd( file ) == 0 ) {
2273                 if( xml_parse_next_tag( file ) == 0 ) {
2274                         if( xml_compare_tag( file, TAG_ADDRESS_INDEX ) ) {
2275                                 addrindex_convert_tree( addrIndex, file );
2276                         }
2277                 }
2278         }
2279         xml_close_file( file );
2280         return addrIndex->retVal;
2281 }
2282
2283 /*
2284 * Create a new address book file.
2285 */
2286 static gboolean addrindex_create_new_book( AddressIndex *addrIndex, gchar *displayName ) {
2287         gboolean retVal = FALSE;
2288         AddressBookFile *abf = NULL;
2289         gchar *newFile = NULL;
2290         GList *fileList = NULL;
2291         gint fileNum = 0;
2292
2293         /* Create new address book */
2294         abf = addrbook_create_book();
2295         addrbook_set_name( abf, displayName );
2296         addrbook_set_path( abf, addrIndex->filePath );
2297
2298         /* Determine next available file number */
2299         fileList = addrbook_get_bookfile_list( abf );
2300         if( fileList ) {
2301                 fileNum = 1 + abf->maxValue;
2302         }
2303         g_list_free( fileList );
2304         fileList = NULL;
2305
2306         newFile = addrbook_gen_new_file_name( fileNum );
2307         if( newFile ) {
2308                 addrbook_set_file( abf, newFile );
2309         }
2310
2311         addrbook_save_data( abf );
2312         addrIndex->retVal = abf->retVal;
2313         if( abf->retVal == MGU_SUCCESS ) retVal = TRUE;
2314         addrbook_free_book( abf );
2315         abf = NULL;
2316
2317         /* Create entries in address index */
2318         if( retVal ) {
2319                 abf = addrbook_create_book();
2320                 addrbook_set_name( abf, displayName );
2321                 addrbook_set_path( abf, addrIndex->filePath );
2322                 addrbook_set_file( abf, newFile );
2323                 addrindex_index_add_datasource( addrIndex, ADDR_IF_BOOK, abf );
2324         }
2325
2326         return retVal;
2327 }
2328
2329 /*
2330 * Read data for address index performing a conversion if necesary.
2331 * Enter: addrIndex Address index object.
2332 * return: Status code, from addrIndex->retVal.
2333 * Note: New address book files will be created in directory specified by
2334 * addrIndex. Three files will be created, for the following:
2335 *       "Common addresses"
2336 *       "Personal addresses"
2337 *       "Gathered addresses" - a new address book.
2338 */
2339 gint addrindex_read_data( AddressIndex *addrIndex ) {
2340         cm_return_val_if_fail( addrIndex != NULL, -1 );
2341
2342         addrIndex->conversionError = FALSE;
2343         addrindex_read_file( addrIndex );
2344         if( addrIndex->retVal == MGU_SUCCESS ) {
2345                 if( addrIndex->needsConversion ) {
2346                         if( addrindex_convert_data( addrIndex ) == MGU_SUCCESS ) {
2347                                 addrIndex->conversionError = TRUE;
2348                         }
2349                         else {
2350                                 addrIndex->conversionError = TRUE;
2351                         }
2352                 }
2353                 addrIndex->dirtyFlag = TRUE;
2354         }
2355         return addrIndex->retVal;
2356 }
2357
2358 /*
2359 * Create new address books for a new address index.
2360 * Enter: addrIndex Address index object.
2361 * return: Status code, from addrIndex->retVal.
2362 * Note: New address book files will be created in directory specified by
2363 * addrIndex. Three files will be created, for the following:
2364 *       "Common addresses"
2365 *       "Personal addresses"
2366 *       "Gathered addresses" - a new address book.
2367 */
2368 gint addrindex_create_new_books( AddressIndex *addrIndex ) {
2369         gboolean flg;
2370
2371         cm_return_val_if_fail( addrIndex != NULL, -1 );
2372
2373         flg = addrindex_create_new_book( addrIndex, DISP_NEW_COMMON );
2374         if( flg ) {
2375                 flg = addrindex_create_new_book( addrIndex, DISP_NEW_PERSONAL );
2376                 addrIndex->dirtyFlag = TRUE;
2377         }
2378         return addrIndex->retVal;
2379 }
2380
2381 /* **********************************************************************
2382 * New interface stuff.
2383 * ***********************************************************************
2384 */
2385
2386 /*
2387  * Return modified flag for specified data source.
2388  */
2389 gboolean addrindex_ds_get_modify_flag( AddressDataSource *ds ) {
2390         gboolean retVal = FALSE;
2391         AddressInterface *iface;
2392
2393         if( ds == NULL ) return retVal;
2394         iface = ds->interface;
2395         if( iface == NULL ) return retVal;
2396         if( iface->getModifyFlag ) {
2397                 retVal = ( iface->getModifyFlag ) ( ds->rawDataSource );
2398         }
2399         return retVal;
2400 }
2401
2402 /*
2403  * Return accessed flag for specified data source.
2404  */
2405 gboolean addrindex_ds_get_access_flag( AddressDataSource *ds ) {
2406         gboolean retVal = FALSE;
2407         AddressInterface *iface;
2408
2409         if( ds == NULL ) return retVal;
2410         iface = ds->interface;
2411         if( iface == NULL ) return retVal;
2412         if( iface->getAccessFlag ) {
2413                 retVal = ( iface->getAccessFlag ) ( ds->rawDataSource );
2414         }
2415         return retVal;
2416 }
2417
2418 /*
2419  * Return data read flag for specified data source.
2420  */
2421 gboolean addrindex_ds_get_read_flag( AddressDataSource *ds ) {
2422         gboolean retVal = TRUE;
2423         AddressInterface *iface;
2424
2425         if( ds == NULL ) return retVal;
2426         iface = ds->interface;
2427         if( iface == NULL ) return retVal;
2428         if( iface->getReadFlag ) {
2429                 retVal = ( iface->getReadFlag ) ( ds->rawDataSource );
2430         }
2431         return retVal;
2432 }
2433
2434 /*
2435  * Return status code for specified data source.
2436  */
2437 gint addrindex_ds_get_status_code( AddressDataSource *ds ) {
2438         gint retVal = MGU_SUCCESS;
2439         AddressInterface *iface;
2440
2441         if( ds == NULL ) return retVal;
2442         iface = ds->interface;
2443         if( iface == NULL ) return retVal;
2444         if( iface->getStatusCode ) {
2445                 retVal = ( iface->getStatusCode ) ( ds->rawDataSource );
2446         }
2447         return retVal;
2448 }
2449
2450 /*
2451  * Return data read flag for specified data source.
2452  */
2453 gint addrindex_ds_read_data( AddressDataSource *ds ) {
2454         gint retVal = MGU_SUCCESS;
2455         AddressInterface *iface;
2456
2457         if( ds == NULL ) return retVal;
2458         iface = ds->interface;
2459         if( iface == NULL ) return retVal;
2460         if( iface->getReadData ) {
2461                 /*
2462                 gchar *name = ( iface->getName ) ( ds->rawDataSource );
2463                 g_print( "addrindex_ds_read_data...reading:::%s:::\n", name );
2464                 */
2465                 retVal = ( iface->getReadData ) ( ds->rawDataSource );
2466         }
2467         return retVal;
2468 }
2469
2470 /*
2471  * Return data read flag for specified data source.
2472  */
2473 ItemFolder *addrindex_ds_get_root_folder( AddressDataSource *ds ) {
2474         ItemFolder *retVal = NULL;
2475         AddressInterface *iface;
2476
2477         if( ds == NULL ) return retVal;
2478         iface = ds->interface;
2479         if( iface == NULL ) return retVal;
2480         if( iface->getRootFolder ) {
2481                 retVal = ( iface->getRootFolder ) ( ds->rawDataSource );
2482         }
2483         return retVal;
2484 }
2485
2486 /*
2487  * Return name for specified data source.
2488  */
2489 gchar *addrindex_ds_get_name( AddressDataSource *ds ) {
2490         gchar *retVal = FALSE;
2491         AddressInterface *iface;
2492
2493         if( ds == NULL ) return retVal;
2494         iface = ds->interface;
2495         if( iface == NULL ) return retVal;
2496         if( iface->getName ) {
2497                 retVal = ( iface->getName ) ( ds->rawDataSource );
2498         }
2499         return retVal;
2500 }
2501
2502 /*
2503  * Set the access flag inside the data source.
2504  */
2505 void addrindex_ds_set_access_flag( AddressDataSource *ds, gboolean *value ) {
2506         AddressInterface *iface;
2507
2508         if( ds == NULL ) return;
2509         iface = ds->interface;
2510         if( iface == NULL ) return;
2511         if( iface->setAccessFlag ) {
2512                 ( iface->setAccessFlag ) ( ds->rawDataSource, value );
2513         }
2514 }
2515
2516 /*
2517  * Return read only flag for specified data source.
2518  */
2519 gboolean addrindex_ds_get_readonly( AddressDataSource *ds ) {
2520         AddressInterface *iface;
2521         if( ds == NULL ) return TRUE;
2522         iface = ds->interface;
2523         if( iface == NULL ) return TRUE;
2524         return iface->readOnly;
2525 }
2526
2527 /*
2528  * Return list of all persons for specified data source.
2529  */
2530 static GList *addrindex_ds_get_all_persons( AddressDataSource *ds ) {
2531         GList *retVal = NULL;
2532         AddressInterface *iface;
2533
2534         if( ds == NULL ) return retVal;
2535         iface = ds->interface;
2536         if( iface == NULL ) return retVal;
2537         if( iface->getAllPersons ) {
2538                 retVal = ( iface->getAllPersons ) ( ds->rawDataSource );
2539         }
2540         return retVal;
2541 }
2542
2543 /*
2544  * Return list of all groups for specified data source.
2545  */
2546 static GList *addrindex_ds_get_all_groups( AddressDataSource *ds ) {
2547         GList *retVal = NULL;
2548         AddressInterface *iface;
2549
2550         if( ds == NULL ) return retVal;
2551         iface = ds->interface;
2552         if( iface == NULL ) return retVal;
2553         if( iface->getAllGroups ) {
2554                 retVal = ( iface->getAllGroups ) ( ds->rawDataSource );
2555         }
2556         return retVal;
2557 }
2558
2559 /* **********************************************************************
2560 * Address search stuff.
2561 * ***********************************************************************
2562 */
2563
2564 /**
2565  * Setup or register the dynamic search that will be performed. The search
2566  * is registered with the query manager.
2567  *
2568  * \param searchTerm    Search term. A private copy will be made.
2569  * \param callBackEntry Callback function that should be called when
2570  *                      each entry is received.
2571  * \param callBackEnd   Callback function that should be called when
2572  *                      search has finished running.
2573  * \return ID allocated to query that will be executed.
2574  */
2575 gint addrindex_setup_search(
2576         const gchar *searchTerm, void *callBackEnd, void *callBackEntry )
2577 {
2578         QueryRequest *req;
2579         gint queryID;
2580
2581         /* Set up a dynamic address query */
2582         req = qrymgr_add_request( searchTerm, callBackEnd, callBackEntry );
2583         queryID = req->queryID;
2584         qryreq_set_search_type( req, ADDRSEARCH_DYNAMIC );
2585
2586         /* g_print( "***> query ID ::%d::\n", queryID ); */
2587         return queryID;
2588 }
2589
2590 #ifdef USE_LDAP
2591
2592 /*
2593  * Function prototypes (not in header file or circular reference errors are
2594  * encountered!)
2595  */
2596 LdapQuery *ldapsvr_new_dynamic_search( 
2597                 LdapServer *server, QueryRequest *req );
2598 LdapQuery *ldapsvr_new_explicit_search(
2599                 LdapServer *server, QueryRequest *req, ItemFolder *folder );
2600 void ldapsvr_execute_query( LdapServer *server, LdapQuery *qry );
2601
2602 #endif
2603
2604 /**
2605  * Execute the previously registered dynamic search.
2606  *
2607  * \param  req Address query object to execute.
2608  * \return <i>TRUE</i> if search started successfully, or <i>FALSE</i> if
2609  *         failed.
2610  */
2611 static gboolean addrindex_start_dynamic( QueryRequest *req ) {
2612         AddressInterface *iface;
2613         AddressDataSource *ds;
2614         GList *nodeIf;
2615         GList *nodeDS;
2616         gint type;
2617
2618         /* g_print( "addrindex_start_dynamic::%d::\n", req->queryID ); */
2619         nodeIf = _addressIndex_->searchOrder;
2620         while( nodeIf ) {
2621                 iface = nodeIf->data;
2622                 nodeIf = g_list_next( nodeIf );
2623
2624                 if( ! iface->useInterface ) {
2625                         continue;
2626                 }
2627                 if( ! iface->externalQuery ) {
2628                         continue;
2629                 }
2630
2631                 type = iface->type;
2632                 nodeDS = iface->listSource;
2633                 while( nodeDS ) {
2634                         ds = nodeDS->data;
2635                         nodeDS = g_list_next( nodeDS );
2636 #ifdef USE_LDAP
2637                         if( type == ADDR_IF_LDAP ) {
2638                                 LdapServer *server;
2639                                 LdapQuery *qry;
2640
2641                                 server = ds->rawDataSource;
2642                                 if( ! server->searchFlag ) {
2643                                         continue;
2644                                 }
2645                                 if( ldapsvr_reuse_previous( server, req ) ) {
2646                                         continue;
2647                                 }
2648
2649                                 /* Start a new dynamic search */
2650                                 qry = ldapsvr_new_dynamic_search( server, req );
2651                                 if( qry ) {
2652                                         ldapsvr_execute_query( server, qry );
2653                                 }
2654                         }
2655 #endif
2656                 }
2657         }
2658         return TRUE;
2659 }
2660
2661 /**
2662  * Stop the previously registered search.
2663  *
2664  * \param queryID ID of search query to stop.
2665  */
2666 void addrindex_stop_search( const gint queryID ){
2667         QueryRequest *req;
2668         AddrQueryObject *aqo;
2669         GList *node;
2670
2671         /* g_print( "addrindex_stop_search/queryID=%d\n", queryID ); */
2672         /* If query ID does not match, search has not been setup */
2673         req = qrymgr_find_request( queryID );
2674         if( req == NULL ) {
2675                 return;
2676         }
2677
2678         /* Stop all queries that were associated with request */
2679         node = req->queryList;
2680         while( node ) {
2681                 aqo = node->data;
2682 #ifdef USE_LDAP
2683                 if( aqo->queryType == ADDRQUERY_LDAP ) {
2684                         LdapQuery *qry = ( LdapQuery * ) aqo;
2685                         ldapqry_set_stop_flag( qry, TRUE );
2686                 }
2687 #endif
2688                 node->data = NULL;
2689                 node = g_list_next( node );
2690         }
2691
2692         /* Delete query request */
2693         qrymgr_delete_request( queryID );
2694 }
2695
2696 /**
2697  * Setup or register the explicit search that will be performed. The search is
2698  * registered with the query manager.
2699  *
2700  * \param  ds            Data source to search.
2701  * \param  searchTerm    Search term to locate.
2702  * \param  folder        Folder to receive search results; may be NULL.
2703  * \param  callbackEnd   Function to call when search has terminated.
2704  * \param  callbackEntry Function to called for each entry processed.
2705  * \return ID allocated to query that will be executed.
2706  */
2707 gint addrindex_setup_explicit_search(
2708         AddressDataSource *ds, const gchar *searchTerm, ItemFolder *folder,
2709         void *callBackEnd, void *callBackEntry )
2710 {
2711         QueryRequest *req;
2712         gint queryID;
2713         gchar *name;
2714         gchar *mySearch;
2715
2716         /* Name the query */
2717         name = g_strdup_printf( "Search '%s'", searchTerm );
2718
2719         /* Set up query request */
2720         if (!strcmp(searchTerm, "*"))
2721                 mySearch = g_strdup("*@");
2722         else
2723                 mySearch = g_strdup(searchTerm);
2724         
2725         req = qrymgr_add_request( mySearch, callBackEnd, callBackEntry );
2726
2727         g_free(mySearch);
2728
2729         qryreq_set_search_type( req, ADDRSEARCH_EXPLICIT );
2730         queryID = req->queryID;
2731
2732         if( ds->type == ADDR_IF_LDAP ) {
2733 #ifdef USE_LDAP
2734                 LdapServer *server;
2735
2736                 server = ds->rawDataSource;
2737                 ldapsvr_new_explicit_search( server, req, folder );
2738 #endif
2739         }
2740         else {
2741                 qrymgr_delete_request( queryID );
2742                 queryID = 0;
2743         }
2744         g_free( name );
2745
2746         return queryID;
2747 }
2748
2749 /**
2750  * Execute the previously registered explicit search.
2751  *
2752  * \param  req Address query request object to execute.
2753  * \return <i>TRUE</i> if search started successfully, or <i>FALSE</i> if
2754  *         failed.
2755  */
2756 static gboolean addrindex_start_explicit( QueryRequest *req ) {
2757         gboolean retVal;
2758         AddrQueryObject *aqo;
2759
2760         retVal = FALSE;
2761
2762         /* Note: there should only be one query in the list. */
2763         aqo = req->queryList->data;
2764 #ifdef USE_LDAP
2765         if( aqo->queryType == ADDRQUERY_LDAP ) {
2766                 LdapServer *server;
2767                 LdapQuery *qry;
2768
2769                 qry = ( LdapQuery * ) aqo;
2770                 server = qry->server;
2771
2772                 /* Start the search */
2773                 retVal = TRUE;
2774                 ldapsvr_execute_query( server, qry );
2775         }
2776 #endif
2777         return retVal;
2778 }
2779
2780 /**
2781  * Start the previously registered search.
2782  *
2783  * \param  queryID    ID of search query to be executed.
2784  * \return <i>TRUE</i> if search started successfully, or <i>FALSE</i> if
2785  *         failed.
2786  */
2787 gboolean addrindex_start_search( const gint queryID ) {
2788         gboolean retVal;
2789         QueryRequest *req;
2790         AddrSearchType searchType;
2791
2792         retVal = FALSE;
2793         /* g_print( "addrindex_start_search/queryID=%d\n", queryID ); */
2794         req = qrymgr_find_request( queryID );
2795         if( req == NULL ) {
2796                 return retVal;
2797         }
2798
2799         searchType = req->searchType;
2800         if( searchType == ADDRSEARCH_DYNAMIC ) {
2801                 retVal = addrindex_start_dynamic( req );
2802         }
2803         else if( searchType == ADDRSEARCH_EXPLICIT ) {
2804                 retVal = addrindex_start_explicit( req );
2805         }
2806
2807         return retVal;
2808 }
2809
2810 /**
2811  * Remove results (folder and data) for specified data source and folder.
2812  * \param ds     Data source to process.
2813  * \param folder Results folder to remove.
2814  */
2815 void addrindex_remove_results( AddressDataSource *ds, ItemFolder *folder ) {
2816         AddrBookBase *adbase;
2817         AddressCache *cache;
2818         gint queryID = 0;
2819
2820         /* g_print( "addrindex_remove_results/start\n" ); */
2821
2822         /* Test for folder */
2823         if( folder->folderType != ADDRFOLDER_QUERY_RESULTS ) return;
2824         /* g_print( "folder name ::%s::\n", ADDRITEM_NAME(folder) ); */
2825         adbase = ( AddrBookBase * ) ds->rawDataSource;
2826         if( adbase == NULL ) return;
2827         cache = adbase->addressCache;
2828
2829         /* Hide folder to prevent re-display */
2830         addritem_folder_set_hidden( folder, TRUE );
2831
2832         if( ds->type == ADDR_IF_LDAP ) {
2833 #ifdef USE_LDAP
2834                 LdapQuery *qry;
2835                 gboolean  delFlag;
2836
2837                 qry = ( LdapQuery * ) folder->folderData;
2838                 queryID = ADDRQUERY_ID(qry);
2839                 /* g_print( "calling ldapquery_remove_results...queryID=%d\n", queryID ); */
2840                 delFlag = ldapquery_remove_results( qry );
2841                 if (delFlag) {
2842                         ldapqry_free( qry );
2843                 }
2844                 /* g_print( "calling ldapquery_remove_results...done\n" ); */
2845                 /*
2846                 if( delFlag ) {
2847                         g_print( "delFlag IS-TRUE\n" );
2848                 }
2849                 else {
2850                         g_print( "delFlag IS-FALSE\n" );
2851                 }
2852                 */
2853 #endif
2854         }
2855         /* g_print( "addrindex_remove_results/end\n" ); */
2856
2857         /* Delete query request */
2858         if( queryID > 0 ) {
2859                 qrymgr_delete_request( queryID );
2860         }
2861 }
2862
2863 /* **********************************************************************
2864 * Address completion stuff.
2865 * ***********************************************************************
2866 */
2867
2868 static void addrindex_load_completion_load_persons(
2869                 gint (*callBackFunc) ( const gchar *, const gchar *, 
2870                                        const gchar *, const gchar *, GList * ),
2871                 AddressDataSource *ds)
2872 {
2873         GList *listP, *nodeP;
2874         GList *nodeM;
2875         gchar *sName;
2876
2877         /* Read address book */
2878         if( addrindex_ds_get_modify_flag( ds ) ) {
2879                 addrindex_ds_read_data( ds );
2880         }
2881
2882         if( ! addrindex_ds_get_read_flag( ds ) ) {
2883                 addrindex_ds_read_data( ds );
2884         }
2885
2886         /* Get all groups */
2887         listP = addrindex_ds_get_all_groups( ds );
2888         nodeP = listP;
2889         while( nodeP ) {
2890                 ItemGroup *group = nodeP->data;
2891                 GList *emails = NULL;
2892                 for (nodeM = group->listEMail; nodeM; nodeM = g_list_next(nodeM)) {
2893                         ItemEMail *email = nodeM->data;
2894                         if (email->address)
2895                                 emails = g_list_append(emails, email);
2896                 }
2897                 callBackFunc( ((AddrItemObject *)group)->name, NULL,
2898                               NULL, NULL, emails );
2899                 nodeP = g_list_next( nodeP );
2900         }
2901
2902         /* Free up the list */
2903         g_list_free( listP );
2904         /* Get all persons */
2905         listP = addrindex_ds_get_all_persons( ds );
2906         nodeP = listP;
2907         while( nodeP ) {
2908                 ItemPerson *person = nodeP->data;
2909                 nodeM = person->listEMail;
2910
2911                 /* Figure out name to use */
2912                 sName = ADDRITEM_NAME(person);
2913                 if( sName == NULL || *sName == '\0' ) {
2914                         sName = person->nickName;
2915                 }
2916
2917                 /* Process each E-Mail address */
2918                 while( nodeM ) {
2919                         ItemEMail *email = nodeM->data;
2920
2921                         callBackFunc( sName, email->address, person->nickName, 
2922                                       ADDRITEM_NAME(email), NULL );
2923
2924                         nodeM = g_list_next( nodeM );
2925                 }
2926                 nodeP = g_list_next( nodeP );
2927         }
2928
2929         /* Free up the list */
2930         g_list_free( listP );
2931 }               
2932
2933 /**
2934  * This function is used by the address completion function to load
2935  * addresses for all non-external address book interfaces.
2936  *
2937  * \param callBackFunc Function to be called when an address is
2938  *                     to be loaded.
2939  * \param folderpath Addressbook's Book/folder path to restrict to (if NULL or ""
2940  *                     or "Any", assume the whole addressbook
2941  * \return <i>TRUE</i> if data loaded, <i>FALSE</i> if address index not loaded.
2942  */
2943
2944 gboolean addrindex_load_completion(
2945                 gint (*callBackFunc) ( const gchar *, const gchar *, 
2946                                        const gchar *, const gchar *, GList * ),
2947                 gchar *folderpath )
2948 {
2949         GList *nodeIf, *nodeDS;
2950
2951         if( folderpath != NULL ) {
2952                 AddressDataSource *book;
2953                 ItemFolder* folder;
2954
2955                 /* split the folder path we've received, we'll try to match this path, subpath by
2956                    subpath against the book/folder structure in order and restrict loading of
2957                    addresses to that subpart (if matches). book/folder path must exist and
2958                    folderpath must not be empty or NULL */
2959                 
2960                 if( ! addressbook_peek_folder_exists( folderpath, &book, &folder ) ) {
2961                         g_warning("addrindex_load_completion: folder path '%s' doesn't exist\n", folderpath);
2962                         return FALSE;
2963                 }
2964
2965                 if( folder != NULL ) {
2966
2967                         GList *items;
2968                         GList *nodeM;
2969                         gchar *sName;
2970                         ItemPerson *person;
2971
2972                         debug_print("addrindex_load_completion: folder %p '%s'\n", folder, folder->obj.name);
2973
2974                         /* Load email addresses */
2975                         items = addritem_folder_get_person_list( folder );
2976                         for( ; items != NULL; items = g_list_next( items ) ) {
2977                                 person = items->data;
2978                                 nodeM = person->listEMail;
2979
2980                                 /* Figure out name to use */
2981                                 sName = ADDRITEM_NAME(person);
2982                                 if( sName == NULL || *sName == '\0' ) {
2983                                         sName = person->nickName;
2984                                 }
2985
2986                                 /* Process each E-Mail address */
2987                                 while( nodeM ) {
2988                                         ItemEMail *email = nodeM->data;
2989
2990                                         callBackFunc( sName, email->address, person->nickName, 
2991                                                           ADDRITEM_NAME(email), NULL );
2992
2993                                         nodeM = g_list_next( nodeM );
2994                                 }
2995                         }
2996                         /* Free up the list */
2997                         mgu_clear_list( items );
2998                         g_list_free( items );
2999
3000                         return TRUE;
3001
3002                 } else {
3003
3004                         if( book != NULL ) {
3005
3006                                 AddressBookFile *abf = book->rawDataSource;
3007
3008                                 debug_print("addrindex_load_completion: book %p '%s'\n", book, abf?abf->fileName:"(null)");
3009
3010                                 addrindex_load_completion_load_persons( callBackFunc, book );
3011
3012                                 return TRUE;
3013
3014                         } else {
3015                                 g_warning("addrindex_load_completion: book/folder path is valid but got no pointer\n");
3016                         }
3017                 }
3018                 return FALSE;
3019
3020         } else {
3021
3022                 nodeIf = addrindex_get_interface_list( _addressIndex_ );
3023                 while( nodeIf ) {
3024                         AddressInterface *iface = nodeIf->data;
3025
3026                         nodeIf = g_list_next( nodeIf );
3027
3028                         if( ! iface->useInterface || iface->externalQuery )
3029                                 continue;
3030
3031                         nodeDS = iface->listSource;
3032                         while( nodeDS ) {
3033                                 addrindex_load_completion_load_persons( callBackFunc, nodeDS->data );
3034                         nodeDS = g_list_next( nodeDS );
3035                 }
3036         }
3037         }
3038
3039         return TRUE;
3040 }
3041
3042 /**
3043  * This function can be used to collect information about
3044  * addressbook entries that contain a specific attribute.
3045  *
3046  * \param attr         Name of attribute to look for
3047  * \param callBackFunc Function to be called when a matching attribute was found
3048  * \return <i>TRUE</i>
3049  */
3050 gboolean addrindex_load_person_attribute(
3051                 const gchar *attr,
3052                 gint (*callBackFunc) ( ItemPerson *, const gchar * ) )
3053 {
3054         AddressDataSource *ds;
3055         GList *nodeIf, *nodeDS;
3056         GList *listP, *nodeP;
3057         GList *nodeA;
3058
3059         nodeIf = addrindex_get_interface_list( _addressIndex_ );
3060         while( nodeIf ) {
3061                 gchar *cur_bname;
3062                 AddressInterface *iface = nodeIf->data;
3063
3064                 nodeIf = g_list_next( nodeIf );
3065
3066                 if( ! iface->useInterface || iface->externalQuery )
3067                         continue;
3068
3069                 nodeDS = iface->listSource;
3070                 while( nodeDS ) {
3071                         ds = nodeDS->data;
3072
3073                         /* Read address book */
3074                         if( addrindex_ds_get_modify_flag( ds ) ) {
3075                                 addrindex_ds_read_data( ds );
3076                         }
3077
3078                         if( ! addrindex_ds_get_read_flag( ds ) ) {
3079                                 addrindex_ds_read_data( ds );
3080                         }
3081
3082                         /* Check addressbook name */
3083                         cur_bname = addrindex_ds_get_name( ds );
3084
3085                         /* Get all persons */
3086                         listP = addrindex_ds_get_all_persons( ds );
3087                         nodeP = listP;
3088                         while( nodeP ) {
3089                                 ItemPerson *person = nodeP->data;
3090
3091                                 /* Return all ItemPerson's if attr is NULL */
3092                                 if( attr == NULL ) {
3093                                         callBackFunc(person, cur_bname);
3094                                 }
3095
3096                                 /* Return ItemPerson's with specific attribute */
3097                                 else {
3098                                         nodeA = person->listAttrib;
3099                                         /* Process each User Attribute */
3100                                         while( nodeA ) {
3101                                                 UserAttribute *attrib = nodeA->data;
3102                                                 if( attrib->name && 
3103                                                     !strcmp( attrib->name,attr ) ) {
3104                                                         callBackFunc(person, cur_bname);
3105                                                 }
3106                                                 nodeA = g_list_next( nodeA );
3107                                         }
3108                                 }
3109                                 nodeP = g_list_next( nodeP );
3110                         }
3111                         /* Free up the list */
3112                         g_list_free( listP );
3113
3114                         nodeDS = g_list_next( nodeDS );
3115                 }
3116         }
3117         return TRUE;
3118 }
3119
3120 /**
3121  * This function can be used to collect information about
3122  * addressbook entries
3123  *
3124  * \param callBackFunc Function to be called for each ItemPerson
3125  * \return <i>TRUE</i>
3126  */
3127 gboolean addrindex_load_person_ds( gint (*callBackFunc)
3128                         ( ItemPerson *, AddressDataSource * ) )
3129 {
3130         AddressDataSource *ds;
3131         GList *nodeIf, *nodeDS;
3132         GList *listP, *nodeP;
3133
3134         nodeIf = addrindex_get_interface_list( _addressIndex_ );
3135         while( nodeIf ) {
3136                 AddressInterface *iface = nodeIf->data;
3137
3138                 nodeIf = g_list_next( nodeIf );
3139
3140                 if( ! iface->useInterface || iface->externalQuery )
3141                         continue;
3142
3143                 nodeDS = iface->listSource;
3144                 while( nodeDS ) {
3145                         ds = nodeDS->data;
3146
3147                         /* Read address book */
3148                         if( addrindex_ds_get_modify_flag( ds ) ) {
3149                                 addrindex_ds_read_data( ds );
3150                         }
3151
3152                         if( ! addrindex_ds_get_read_flag( ds ) ) {
3153                                 addrindex_ds_read_data( ds );
3154                         }
3155
3156                         /* Get all persons */
3157                         listP = addrindex_ds_get_all_persons( ds );
3158                         nodeP = listP;
3159                         while( nodeP ) {
3160                                 ItemPerson *person = nodeP->data;
3161
3162                                 callBackFunc(person, ds);
3163                                 nodeP = g_list_next( nodeP );
3164                         }
3165                         /* Free up the list */
3166                         g_list_free( listP );
3167
3168                         nodeDS = g_list_next( nodeDS );
3169                 }
3170         }
3171         return TRUE;
3172 }
3173
3174 gchar *addrindex_get_picture_file(const gchar *emailaddr)
3175 {
3176         AddressDataSource *ds;
3177         GList *nodeIf, *nodeDS;
3178         GList *listP, *nodeP;
3179         gboolean found = FALSE;
3180         gchar *filename = NULL;
3181         gchar *raw_addr = NULL;
3182         
3183         if (!emailaddr)
3184                 return NULL;
3185
3186         Xstrdup_a(raw_addr, emailaddr, return NULL);
3187         extract_address(raw_addr);
3188
3189         nodeIf = addrindex_get_interface_list( _addressIndex_ );
3190         while( nodeIf ) {
3191                 AddressInterface *iface = nodeIf->data;
3192
3193                 nodeIf = g_list_next( nodeIf );
3194
3195                 if( ! iface->useInterface || iface->externalQuery )
3196                         continue;
3197
3198                 nodeDS = iface->listSource;
3199                 while( nodeDS && !found) {
3200                         ds = nodeDS->data;
3201
3202                         /* Read address book */
3203                         if( addrindex_ds_get_modify_flag( ds ) ) {
3204                                 addrindex_ds_read_data( ds );
3205                         }
3206
3207                         if( ! addrindex_ds_get_read_flag( ds ) ) {
3208                                 addrindex_ds_read_data( ds );
3209                         }
3210
3211                         /* Get all persons */
3212                         listP = addrindex_ds_get_all_persons( ds );
3213                         nodeP = listP;
3214                         while( nodeP ) {
3215                                 GList *nodeM;
3216                                 ItemPerson *person = nodeP->data;
3217                                 nodeM = person->listEMail;
3218                                 while(nodeM) {
3219                                         ItemEMail *email = nodeM->data;
3220                                         if (email->address && !strcasecmp(raw_addr, email->address)) {
3221                                                 found = TRUE;
3222                                                 filename = g_strconcat( get_rc_dir(), G_DIR_SEPARATOR_S, 
3223                                                         ADDRBOOK_DIR, G_DIR_SEPARATOR_S, 
3224                                                         person->picture, ".png", NULL );
3225                                                 break;
3226                                         }
3227                                         nodeM = nodeM->next;
3228                                 }
3229                                 nodeP = g_list_next( nodeP );
3230                         }
3231                         /* Free up the list */
3232                         g_list_free( listP );
3233
3234                         nodeDS = g_list_next( nodeDS );
3235                 }
3236         }
3237
3238         return filename;
3239 }
3240
3241 /*
3242  * End of Source.
3243  */