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