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