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