2012-11-28 [wwp] 3.9.0cvs29
[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( rc < 0 || 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
2019         for (;;) {
2020                 prev_level = file->level;
2021                 xml_parse_next_tag( file );
2022                 if (file->level < prev_level)
2023                         return;
2024
2025                 addrindex_consume_tree( file );
2026         }
2027 }
2028
2029 /*
2030 * Free up temporary tree.
2031 */
2032 static void addrindex_free_node( AddressCvtNode *node ) {
2033         GList *list = node->list;
2034
2035         while( list ) {
2036                 AddressCvtNode *lNode = list->data;
2037                 list = g_list_next( list );
2038                 addrindex_free_node( lNode );
2039         }
2040         node->type = TEMPNODE_ROOT;
2041         g_free( node->name );
2042         g_free( node->address );
2043         g_free( node->remarks );
2044         g_list_free( node->list );
2045         g_free( node );
2046 }
2047
2048 /*
2049 * Process address book for specified node.
2050 */
2051 static void addrindex_process_node(
2052                 AddressBookFile *abf, AddressCvtNode *node, ItemFolder *parent,
2053                 ItemGroup *parentGrp, ItemFolder *folderGrp )
2054 {
2055         GList *list;
2056         ItemFolder *itemFolder = NULL;
2057         ItemGroup *itemGParent = parentGrp;
2058         ItemFolder *itemGFolder = folderGrp;
2059         AddressCache *cache = abf->addressCache;
2060
2061         if( node->type == TEMPNODE_ROOT ) {
2062                 itemFolder = parent;
2063         }
2064         else if( node->type == TEMPNODE_FOLDER ) {
2065                 itemFolder = addritem_create_item_folder();
2066                 addritem_folder_set_name( itemFolder, node->name );
2067                 addrcache_id_folder( cache, itemFolder );
2068                 addrcache_folder_add_folder( cache, parent, itemFolder );
2069                 itemGFolder = NULL;
2070         }
2071         else if( node->type == TEMPNODE_GROUP ) {
2072                 ItemGroup *itemGroup;
2073                 gchar *fName;
2074
2075                 /* Create a folder for group */
2076                 fName = g_strdup_printf( "Cvt - %s", node->name );
2077                 itemGFolder = addritem_create_item_folder();
2078                 addritem_folder_set_name( itemGFolder, fName );
2079                 addrcache_id_folder( cache, itemGFolder );
2080                 addrcache_folder_add_folder( cache, parent, itemGFolder );
2081                 g_free( fName );
2082
2083                 /* Add group into folder */
2084                 itemGroup = addritem_create_item_group();
2085                 addritem_group_set_name( itemGroup, node->name );
2086                 addrcache_id_group( cache, itemGroup );
2087                 addrcache_folder_add_group( cache, itemGFolder, itemGroup );
2088                 itemGParent = itemGroup;
2089         }
2090         else if( node->type == TEMPNODE_ADDRESS ) {
2091                 ItemPerson *itemPerson;
2092                 ItemEMail *itemEMail;
2093
2094                 /* Create person and email objects */
2095                 itemPerson = addritem_create_item_person();
2096                 addritem_person_set_common_name( itemPerson, node->name );
2097                 addrcache_id_person( cache, itemPerson );
2098                 itemEMail = addritem_create_item_email();
2099                 addritem_email_set_address( itemEMail, node->address );
2100                 addritem_email_set_remarks( itemEMail, node->remarks );
2101                 addrcache_id_email( cache, itemEMail );
2102                 addrcache_person_add_email( cache, itemPerson, itemEMail );
2103
2104                 /* Add person into appropriate folder */
2105                 if( itemGFolder ) {
2106                         addrcache_folder_add_person( cache, itemGFolder, itemPerson );
2107                 }
2108                 else {
2109                         addrcache_folder_add_person( cache, parent, itemPerson );
2110                 }
2111
2112                 /* Add email address only into group */
2113                 if( parentGrp ) {
2114                         addrcache_group_add_email( cache, parentGrp, itemEMail );
2115                 }
2116         }
2117
2118         list = node->list;
2119         while( list ) {
2120                 AddressCvtNode *lNode = list->data;
2121                 list = g_list_next( list );
2122                 addrindex_process_node( abf, lNode, itemFolder, itemGParent, itemGFolder );
2123         }
2124 }
2125
2126 /*
2127 * Process address book to specified file number.
2128 */
2129 static gboolean addrindex_process_book( AddressIndex *addrIndex, XMLFile *file, gchar *displayName ) {
2130         gboolean retVal = FALSE;
2131         AddressBookFile *abf = NULL;
2132         AddressCvtNode *rootNode = NULL;
2133         gchar *newFile = NULL;
2134         GList *fileList = NULL;
2135         gint fileNum  = 0;
2136
2137         /* Setup root node */
2138         rootNode = g_new0( AddressCvtNode, 1 );
2139         rootNode->type = TEMPNODE_ROOT;
2140         rootNode->name = g_strdup( "root" );
2141         rootNode->list = NULL;
2142         addrindex_add_obj( file, rootNode );
2143         /* addrindex_print_node( rootNode, stdout ); */
2144
2145         /* Create new address book */
2146         abf = addrbook_create_book();
2147         addrbook_set_name( abf, displayName );
2148         addrbook_set_path( abf, addrIndex->filePath );
2149
2150         /* Determine next available file number */
2151         fileList = addrbook_get_bookfile_list( abf );
2152         if( fileList ) {
2153                 fileNum = 1 + abf->maxValue;
2154         }
2155         g_list_free( fileList );
2156         fileList = NULL;
2157
2158         newFile = addrbook_gen_new_file_name( fileNum );
2159         if( newFile ) {
2160                 addrbook_set_file( abf, newFile );
2161         }
2162
2163         addrindex_process_node( abf, rootNode, abf->addressCache->rootFolder, NULL, NULL );
2164
2165         /* addrbook_dump_book( abf, stdout ); */
2166         addrbook_save_data( abf );
2167         addrIndex->retVal = abf->retVal;
2168         if( abf->retVal == MGU_SUCCESS ) retVal = TRUE;
2169
2170         addrbook_free_book( abf );
2171         abf = NULL;
2172         addrindex_free_node( rootNode );
2173         rootNode = NULL;
2174
2175         /* Create entries in address index */
2176         if( retVal ) {
2177                 abf = addrbook_create_book();
2178                 addrbook_set_name( abf, displayName );
2179                 addrbook_set_path( abf, addrIndex->filePath );
2180                 addrbook_set_file( abf, newFile );
2181                 addrindex_index_add_datasource( addrIndex, ADDR_IF_BOOK, abf );
2182         }
2183
2184         return retVal;
2185 }
2186
2187 /*
2188 * Process tree converting data.
2189 */
2190 static void addrindex_convert_tree( AddressIndex *addrIndex, XMLFile *file ) {
2191         guint prev_level;
2192         XMLTag *xtag;
2193
2194         /* Process file */
2195         for (;;) {
2196                 prev_level = file->level;
2197                 xml_parse_next_tag( file );
2198                 if (file->level < prev_level) return;
2199
2200                 xtag = xml_get_current_tag( file );
2201                 /* g_print( "tag : %d : %s\n", prev_level, xtag->tag ); */
2202                 if( strcmp( xtag->tag, TAG_IF_OLD_COMMON ) == 0 ) {
2203                         if( addrindex_process_book( addrIndex, file, DISP_OLD_COMMON ) ) {
2204                                 addrIndex->needsConversion = FALSE;
2205                                 addrIndex->wasConverted = TRUE;
2206                                 continue;
2207                         }
2208                         return;
2209                 }
2210                 if( strcmp( xtag->tag, TAG_IF_OLD_PERSONAL ) == 0 ) {
2211                         if( addrindex_process_book( addrIndex, file, DISP_OLD_PERSONAL ) ) {
2212                                 addrIndex->needsConversion = FALSE;
2213                                 addrIndex->wasConverted = TRUE;
2214                                 continue;
2215                         }
2216                         return;
2217                 }
2218                 addrindex_consume_tree( file );
2219         }
2220 }
2221
2222 static gint addrindex_convert_data( AddressIndex *addrIndex ) {
2223         XMLFile *file = NULL;
2224         gchar *fileSpec;
2225
2226         fileSpec = g_strconcat( addrIndex->filePath, G_DIR_SEPARATOR_S, addrIndex->fileName, NULL );
2227         addrIndex->retVal = MGU_NO_FILE;
2228         file = xml_open_file( fileSpec );
2229         g_free( fileSpec );
2230
2231         if( file == NULL ) {
2232                 /* g_print( " file '%s' does not exist.\n", addrIndex->fileName ); */
2233                 return addrIndex->retVal;
2234         }
2235
2236         addrIndex->retVal = MGU_BAD_FORMAT;
2237         if( xml_get_dtd( file ) == 0 ) {
2238                 if( xml_parse_next_tag( file ) == 0 ) {
2239                         if( xml_compare_tag( file, TAG_ADDRESS_INDEX ) ) {
2240                                 addrindex_convert_tree( addrIndex, file );
2241                         }
2242                 }
2243         }
2244         xml_close_file( file );
2245         return addrIndex->retVal;
2246 }
2247
2248 /*
2249 * Create a new address book file.
2250 */
2251 static gboolean addrindex_create_new_book( AddressIndex *addrIndex, gchar *displayName ) {
2252         gboolean retVal = FALSE;
2253         AddressBookFile *abf = NULL;
2254         gchar *newFile = NULL;
2255         GList *fileList = NULL;
2256         gint fileNum = 0;
2257
2258         /* Create new address book */
2259         abf = addrbook_create_book();
2260         addrbook_set_name( abf, displayName );
2261         addrbook_set_path( abf, addrIndex->filePath );
2262
2263         /* Determine next available file number */
2264         fileList = addrbook_get_bookfile_list( abf );
2265         if( fileList ) {
2266                 fileNum = 1 + abf->maxValue;
2267         }
2268         g_list_free( fileList );
2269         fileList = NULL;
2270
2271         newFile = addrbook_gen_new_file_name( fileNum );
2272         if( newFile ) {
2273                 addrbook_set_file( abf, newFile );
2274         }
2275
2276         addrbook_save_data( abf );
2277         addrIndex->retVal = abf->retVal;
2278         if( abf->retVal == MGU_SUCCESS ) retVal = TRUE;
2279         addrbook_free_book( abf );
2280         abf = NULL;
2281
2282         /* Create entries in address index */
2283         if( retVal ) {
2284                 abf = addrbook_create_book();
2285                 addrbook_set_name( abf, displayName );
2286                 addrbook_set_path( abf, addrIndex->filePath );
2287                 addrbook_set_file( abf, newFile );
2288                 addrindex_index_add_datasource( addrIndex, ADDR_IF_BOOK, abf );
2289         }
2290
2291         return retVal;
2292 }
2293
2294 /*
2295 * Read data for address index performing a conversion if necesary.
2296 * Enter: addrIndex Address index object.
2297 * return: Status code, from addrIndex->retVal.
2298 * Note: New address book files will be created in directory specified by
2299 * addrIndex. Three files will be created, for the following:
2300 *       "Common addresses"
2301 *       "Personal addresses"
2302 *       "Gathered addresses" - a new address book.
2303 */
2304 gint addrindex_read_data( AddressIndex *addrIndex ) {
2305         cm_return_val_if_fail( addrIndex != NULL, -1 );
2306
2307         addrIndex->conversionError = FALSE;
2308         addrindex_read_file( addrIndex );
2309         if( addrIndex->retVal == MGU_SUCCESS ) {
2310                 if( addrIndex->needsConversion ) {
2311                         if( addrindex_convert_data( addrIndex ) == MGU_SUCCESS )
2312                                 addrIndex->conversionError = FALSE;
2313                         else
2314                                 addrIndex->conversionError = TRUE;
2315                 }
2316                 addrIndex->dirtyFlag = TRUE;
2317         }
2318         return addrIndex->retVal;
2319 }
2320
2321 /*
2322 * Create new address books for a new address index.
2323 * Enter: addrIndex Address index object.
2324 * return: Status code, from addrIndex->retVal.
2325 * Note: New address book files will be created in directory specified by
2326 * addrIndex. Three files will be created, for the following:
2327 *       "Common addresses"
2328 *       "Personal addresses"
2329 *       "Gathered addresses" - a new address book.
2330 */
2331 gint addrindex_create_new_books( AddressIndex *addrIndex ) {
2332         gboolean flg;
2333
2334         cm_return_val_if_fail( addrIndex != NULL, -1 );
2335
2336         flg = addrindex_create_new_book( addrIndex, DISP_NEW_COMMON );
2337         if( flg ) {
2338                 flg = addrindex_create_new_book( addrIndex, DISP_NEW_PERSONAL );
2339                 addrIndex->dirtyFlag = TRUE;
2340         }
2341         return addrIndex->retVal;
2342 }
2343
2344 /* **********************************************************************
2345 * New interface stuff.
2346 * ***********************************************************************
2347 */
2348
2349 /*
2350  * Return modified flag for specified data source.
2351  */
2352 gboolean addrindex_ds_get_modify_flag( AddressDataSource *ds ) {
2353         gboolean retVal = FALSE;
2354         AddressInterface *iface;
2355
2356         if( ds == NULL ) return retVal;
2357         iface = ds->interface;
2358         if( iface == NULL ) return retVal;
2359         if( iface->getModifyFlag ) {
2360                 retVal = ( iface->getModifyFlag ) ( ds->rawDataSource );
2361         }
2362         return retVal;
2363 }
2364
2365 /*
2366  * Return accessed flag for specified data source.
2367  */
2368 gboolean addrindex_ds_get_access_flag( AddressDataSource *ds ) {
2369         gboolean retVal = FALSE;
2370         AddressInterface *iface;
2371
2372         if( ds == NULL ) return retVal;
2373         iface = ds->interface;
2374         if( iface == NULL ) return retVal;
2375         if( iface->getAccessFlag ) {
2376                 retVal = ( iface->getAccessFlag ) ( ds->rawDataSource );
2377         }
2378         return retVal;
2379 }
2380
2381 /*
2382  * Return data read flag for specified data source.
2383  */
2384 gboolean addrindex_ds_get_read_flag( AddressDataSource *ds ) {
2385         gboolean retVal = TRUE;
2386         AddressInterface *iface;
2387
2388         if( ds == NULL ) return retVal;
2389         iface = ds->interface;
2390         if( iface == NULL ) return retVal;
2391         if( iface->getReadFlag ) {
2392                 retVal = ( iface->getReadFlag ) ( ds->rawDataSource );
2393         }
2394         return retVal;
2395 }
2396
2397 /*
2398  * Return status code for specified data source.
2399  */
2400 gint addrindex_ds_get_status_code( AddressDataSource *ds ) {
2401         gint retVal = MGU_SUCCESS;
2402         AddressInterface *iface;
2403
2404         if( ds == NULL ) return retVal;
2405         iface = ds->interface;
2406         if( iface == NULL ) return retVal;
2407         if( iface->getStatusCode ) {
2408                 retVal = ( iface->getStatusCode ) ( ds->rawDataSource );
2409         }
2410         return retVal;
2411 }
2412
2413 /*
2414  * Return data read flag for specified data source.
2415  */
2416 gint addrindex_ds_read_data( AddressDataSource *ds ) {
2417         gint retVal = MGU_SUCCESS;
2418         AddressInterface *iface;
2419
2420         if( ds == NULL ) return retVal;
2421         iface = ds->interface;
2422         if( iface == NULL ) return retVal;
2423         if( iface->getReadData ) {
2424                 /*
2425                 gchar *name = ( iface->getName ) ( ds->rawDataSource );
2426                 g_print( "addrindex_ds_read_data...reading:::%s:::\n", name );
2427                 */
2428                 retVal = ( iface->getReadData ) ( ds->rawDataSource );
2429         }
2430         return retVal;
2431 }
2432
2433 /*
2434  * Return data read flag for specified data source.
2435  */
2436 ItemFolder *addrindex_ds_get_root_folder( AddressDataSource *ds ) {
2437         ItemFolder *retVal = NULL;
2438         AddressInterface *iface;
2439
2440         if( ds == NULL ) return retVal;
2441         iface = ds->interface;
2442         if( iface == NULL ) return retVal;
2443         if( iface->getRootFolder ) {
2444                 retVal = ( iface->getRootFolder ) ( ds->rawDataSource );
2445         }
2446         return retVal;
2447 }
2448
2449 /*
2450  * Return name for specified data source.
2451  */
2452 gchar *addrindex_ds_get_name( AddressDataSource *ds ) {
2453         gchar *retVal = FALSE;
2454         AddressInterface *iface;
2455
2456         if( ds == NULL ) return retVal;
2457         iface = ds->interface;
2458         if( iface == NULL ) return retVal;
2459         if( iface->getName ) {
2460                 retVal = ( iface->getName ) ( ds->rawDataSource );
2461         }
2462         return retVal;
2463 }
2464
2465 /*
2466  * Set the access flag inside the data source.
2467  */
2468 void addrindex_ds_set_access_flag( AddressDataSource *ds, gboolean *value ) {
2469         AddressInterface *iface;
2470
2471         if( ds == NULL ) return;
2472         iface = ds->interface;
2473         if( iface == NULL ) return;
2474         if( iface->setAccessFlag ) {
2475                 ( iface->setAccessFlag ) ( ds->rawDataSource, value );
2476         }
2477 }
2478
2479 /*
2480  * Return read only flag for specified data source.
2481  */
2482 gboolean addrindex_ds_get_readonly( AddressDataSource *ds ) {
2483         AddressInterface *iface;
2484         if( ds == NULL ) return TRUE;
2485         iface = ds->interface;
2486         if( iface == NULL ) return TRUE;
2487         return iface->readOnly;
2488 }
2489
2490 /*
2491  * Return list of all persons for specified data source.
2492  */
2493 static GList *addrindex_ds_get_all_persons( AddressDataSource *ds ) {
2494         GList *retVal = NULL;
2495         AddressInterface *iface;
2496
2497         if( ds == NULL ) return retVal;
2498         iface = ds->interface;
2499         if( iface == NULL ) return retVal;
2500         if( iface->getAllPersons ) {
2501                 retVal = ( iface->getAllPersons ) ( ds->rawDataSource );
2502         }
2503         return retVal;
2504 }
2505
2506 /*
2507  * Return list of all groups for specified data source.
2508  */
2509 static GList *addrindex_ds_get_all_groups( AddressDataSource *ds ) {
2510         GList *retVal = NULL;
2511         AddressInterface *iface;
2512
2513         if( ds == NULL ) return retVal;
2514         iface = ds->interface;
2515         if( iface == NULL ) return retVal;
2516         if( iface->getAllGroups ) {
2517                 retVal = ( iface->getAllGroups ) ( ds->rawDataSource );
2518         }
2519         return retVal;
2520 }
2521
2522 /* **********************************************************************
2523 * Address search stuff.
2524 * ***********************************************************************
2525 */
2526
2527 /**
2528  * Setup or register the dynamic search that will be performed. The search
2529  * is registered with the query manager.
2530  *
2531  * \param searchTerm    Search term. A private copy will be made.
2532  * \param callBackEntry Callback function that should be called when
2533  *                      each entry is received.
2534  * \param callBackEnd   Callback function that should be called when
2535  *                      search has finished running.
2536  * \return ID allocated to query that will be executed.
2537  */
2538 gint addrindex_setup_search(
2539         const gchar *searchTerm, void *callBackEnd, void *callBackEntry )
2540 {
2541         QueryRequest *req;
2542         gint queryID;
2543
2544         /* Set up a dynamic address query */
2545         req = qrymgr_add_request( searchTerm, callBackEnd, callBackEntry );
2546         queryID = req->queryID;
2547         qryreq_set_search_type( req, ADDRSEARCH_DYNAMIC );
2548
2549         /* g_print( "***> query ID ::%d::\n", queryID ); */
2550         return queryID;
2551 }
2552
2553 #ifdef USE_LDAP
2554
2555 /*
2556  * Function prototypes (not in header file or circular reference errors are
2557  * encountered!)
2558  */
2559 LdapQuery *ldapsvr_new_dynamic_search( 
2560                 LdapServer *server, QueryRequest *req );
2561 LdapQuery *ldapsvr_new_explicit_search(
2562                 LdapServer *server, QueryRequest *req, ItemFolder *folder );
2563 void ldapsvr_execute_query( LdapServer *server, LdapQuery *qry );
2564
2565 #endif
2566
2567 /**
2568  * Execute the previously registered dynamic search.
2569  *
2570  * \param  req Address query object to execute.
2571  * \return <i>TRUE</i> if search started successfully, or <i>FALSE</i> if
2572  *         failed.
2573  */
2574 static gboolean addrindex_start_dynamic( QueryRequest *req ) {
2575         AddressInterface *iface;
2576         AddressDataSource *ds;
2577         GList *nodeIf;
2578         GList *nodeDS;
2579         gint type;
2580
2581         /* g_print( "addrindex_start_dynamic::%d::\n", req->queryID ); */
2582         nodeIf = _addressIndex_->searchOrder;
2583         while( nodeIf ) {
2584                 iface = nodeIf->data;
2585                 nodeIf = g_list_next( nodeIf );
2586
2587                 if( ! iface->useInterface ) {
2588                         continue;
2589                 }
2590                 if( ! iface->externalQuery ) {
2591                         continue;
2592                 }
2593
2594                 type = iface->type;
2595                 nodeDS = iface->listSource;
2596                 while( nodeDS ) {
2597                         ds = nodeDS->data;
2598                         nodeDS = g_list_next( nodeDS );
2599 #ifdef USE_LDAP
2600                         if( type == ADDR_IF_LDAP ) {
2601                                 LdapServer *server;
2602                                 LdapQuery *qry;
2603
2604                                 server = ds->rawDataSource;
2605                                 if( ! server->searchFlag ) {
2606                                         continue;
2607                                 }
2608                                 if( ldapsvr_reuse_previous( server, req ) ) {
2609                                         continue;
2610                                 }
2611
2612                                 /* Start a new dynamic search */
2613                                 qry = ldapsvr_new_dynamic_search( server, req );
2614                                 if( qry ) {
2615                                         ldapsvr_execute_query( server, qry );
2616                                 }
2617                         }
2618 #endif
2619                 }
2620         }
2621         return TRUE;
2622 }
2623
2624 /**
2625  * Stop the previously registered search.
2626  *
2627  * \param queryID ID of search query to stop.
2628  */
2629 void addrindex_stop_search( const gint queryID ){
2630         QueryRequest *req;
2631         AddrQueryObject *aqo;
2632         GList *node;
2633
2634         /* g_print( "addrindex_stop_search/queryID=%d\n", queryID ); */
2635         /* If query ID does not match, search has not been setup */
2636         req = qrymgr_find_request( queryID );
2637         if( req == NULL ) {
2638                 return;
2639         }
2640
2641         /* Stop all queries that were associated with request */
2642         node = req->queryList;
2643         while( node ) {
2644                 aqo = node->data;
2645 #ifdef USE_LDAP
2646                 if( aqo->queryType == ADDRQUERY_LDAP ) {
2647                         LdapQuery *qry = ( LdapQuery * ) aqo;
2648                         ldapqry_set_stop_flag( qry, TRUE );
2649                 }
2650 #endif
2651                 node->data = NULL;
2652                 node = g_list_next( node );
2653         }
2654
2655         /* Delete query request */
2656         qrymgr_delete_request( queryID );
2657 }
2658
2659 /**
2660  * Setup or register the explicit search that will be performed. The search is
2661  * registered with the query manager.
2662  *
2663  * \param  ds            Data source to search.
2664  * \param  searchTerm    Search term to locate.
2665  * \param  folder        Folder to receive search results; may be NULL.
2666  * \param  callbackEnd   Function to call when search has terminated.
2667  * \param  callbackEntry Function to called for each entry processed.
2668  * \return ID allocated to query that will be executed.
2669  */
2670 gint addrindex_setup_explicit_search(
2671         AddressDataSource *ds, const gchar *searchTerm, ItemFolder *folder,
2672         void *callBackEnd, void *callBackEntry )
2673 {
2674         QueryRequest *req;
2675         gint queryID;
2676         gchar *name;
2677         gchar *mySearch;
2678
2679         /* Name the query */
2680         name = g_strdup_printf( "Search '%s'", searchTerm );
2681
2682         /* Set up query request */
2683         if (!strcmp(searchTerm, "*"))
2684                 mySearch = g_strdup("*@");
2685         else
2686                 mySearch = g_strdup(searchTerm);
2687         
2688         req = qrymgr_add_request( mySearch, callBackEnd, callBackEntry );
2689
2690         g_free(mySearch);
2691
2692         qryreq_set_search_type( req, ADDRSEARCH_EXPLICIT );
2693         queryID = req->queryID;
2694
2695         if( ds->type == ADDR_IF_LDAP ) {
2696 #ifdef USE_LDAP
2697                 LdapServer *server;
2698
2699                 server = ds->rawDataSource;
2700                 ldapsvr_new_explicit_search( server, req, folder );
2701 #endif
2702         }
2703         else {
2704                 qrymgr_delete_request( queryID );
2705                 queryID = 0;
2706         }
2707         g_free( name );
2708
2709         return queryID;
2710 }
2711
2712 /**
2713  * Execute the previously registered explicit search.
2714  *
2715  * \param  req Address query request object to execute.
2716  * \return <i>TRUE</i> if search started successfully, or <i>FALSE</i> if
2717  *         failed.
2718  */
2719 static gboolean addrindex_start_explicit( QueryRequest *req ) {
2720         gboolean retVal;
2721         AddrQueryObject *aqo;
2722
2723         retVal = FALSE;
2724
2725         /* Note: there should only be one query in the list. */
2726         aqo = req->queryList->data;
2727 #ifdef USE_LDAP
2728         if( aqo->queryType == ADDRQUERY_LDAP ) {
2729                 LdapServer *server;
2730                 LdapQuery *qry;
2731
2732                 qry = ( LdapQuery * ) aqo;
2733                 server = qry->server;
2734
2735                 /* Start the search */
2736                 retVal = TRUE;
2737                 ldapsvr_execute_query( server, qry );
2738         }
2739 #endif
2740         return retVal;
2741 }
2742
2743 /**
2744  * Start the previously registered search.
2745  *
2746  * \param  queryID    ID of search query to be executed.
2747  * \return <i>TRUE</i> if search started successfully, or <i>FALSE</i> if
2748  *         failed.
2749  */
2750 gboolean addrindex_start_search( const gint queryID ) {
2751         gboolean retVal;
2752         QueryRequest *req;
2753         AddrSearchType searchType;
2754
2755         retVal = FALSE;
2756         /* g_print( "addrindex_start_search/queryID=%d\n", queryID ); */
2757         req = qrymgr_find_request( queryID );
2758         if( req == NULL ) {
2759                 return retVal;
2760         }
2761
2762         searchType = req->searchType;
2763         if( searchType == ADDRSEARCH_DYNAMIC ) {
2764                 retVal = addrindex_start_dynamic( req );
2765         }
2766         else if( searchType == ADDRSEARCH_EXPLICIT ) {
2767                 retVal = addrindex_start_explicit( req );
2768         }
2769
2770         return retVal;
2771 }
2772
2773 /**
2774  * Remove results (folder and data) for specified data source and folder.
2775  * \param ds     Data source to process.
2776  * \param folder Results folder to remove.
2777  */
2778 void addrindex_remove_results( AddressDataSource *ds, ItemFolder *folder ) {
2779         AddrBookBase *adbase;
2780         gint queryID = 0;
2781
2782         /* g_print( "addrindex_remove_results/start\n" ); */
2783
2784         /* Test for folder */
2785         if( folder->folderType != ADDRFOLDER_QUERY_RESULTS ) return;
2786         /* g_print( "folder name ::%s::\n", ADDRITEM_NAME(folder) ); */
2787         adbase = ( AddrBookBase * ) ds->rawDataSource;
2788         if( adbase == NULL ) return;
2789
2790         /* Hide folder to prevent re-display */
2791         addritem_folder_set_hidden( folder, TRUE );
2792
2793         if( ds->type == ADDR_IF_LDAP ) {
2794 #ifdef USE_LDAP
2795                 LdapQuery *qry;
2796                 gboolean  delFlag;
2797
2798                 qry = ( LdapQuery * ) folder->folderData;
2799                 queryID = ADDRQUERY_ID(qry);
2800                 /* g_print( "calling ldapquery_remove_results...queryID=%d\n", queryID ); */
2801                 delFlag = ldapquery_remove_results( qry );
2802                 if (delFlag) {
2803                         ldapqry_free( qry );
2804                 }
2805                 /* g_print( "calling ldapquery_remove_results...done\n" ); */
2806                 /*
2807                 if( delFlag ) {
2808                         g_print( "delFlag IS-TRUE\n" );
2809                 }
2810                 else {
2811                         g_print( "delFlag IS-FALSE\n" );
2812                 }
2813                 */
2814 #endif
2815         }
2816         /* g_print( "addrindex_remove_results/end\n" ); */
2817
2818         /* Delete query request */
2819         if( queryID > 0 ) {
2820                 qrymgr_delete_request( queryID );
2821         }
2822 }
2823
2824 /* **********************************************************************
2825 * Address completion stuff.
2826 * ***********************************************************************
2827 */
2828
2829 static void addrindex_load_completion_load_persons(
2830                 gint (*callBackFunc) ( const gchar *, const gchar *, 
2831                                        const gchar *, const gchar *, GList * ),
2832                 AddressDataSource *ds)
2833 {
2834         GList *listP, *nodeP;
2835         GList *nodeM;
2836         gchar *sName;
2837
2838         /* Read address book */
2839         if( addrindex_ds_get_modify_flag( ds ) ) {
2840                 addrindex_ds_read_data( ds );
2841         }
2842
2843         if( ! addrindex_ds_get_read_flag( ds ) ) {
2844                 addrindex_ds_read_data( ds );
2845         }
2846
2847         /* Get all groups */
2848         listP = addrindex_ds_get_all_groups( ds );
2849         nodeP = listP;
2850         while( nodeP ) {
2851                 ItemGroup *group = nodeP->data;
2852                 GList *emails = NULL;
2853                 for (nodeM = group->listEMail; nodeM; nodeM = g_list_next(nodeM)) {
2854                         ItemEMail *email = nodeM->data;
2855                         if (email->address)
2856                                 emails = g_list_append(emails, email);
2857                 }
2858                 callBackFunc( ((AddrItemObject *)group)->name, NULL,
2859                               NULL, NULL, emails );
2860                 nodeP = g_list_next( nodeP );
2861         }
2862
2863         /* Free up the list */
2864         g_list_free( listP );
2865         /* Get all persons */
2866         listP = addrindex_ds_get_all_persons( ds );
2867         nodeP = listP;
2868         while( nodeP ) {
2869                 ItemPerson *person = nodeP->data;
2870                 nodeM = person->listEMail;
2871
2872                 /* Figure out name to use */
2873                 sName = ADDRITEM_NAME(person);
2874                 if( sName == NULL || *sName == '\0' ) {
2875                         sName = person->nickName;
2876                 }
2877
2878                 /* Process each E-Mail address */
2879                 while( nodeM ) {
2880                         ItemEMail *email = nodeM->data;
2881
2882                         callBackFunc( sName, email->address, person->nickName, 
2883                                       ADDRITEM_NAME(email), NULL );
2884
2885                         nodeM = g_list_next( nodeM );
2886                 }
2887                 nodeP = g_list_next( nodeP );
2888         }
2889
2890         /* Free up the list */
2891         g_list_free( listP );
2892 }               
2893
2894 /**
2895  * This function is used by the address completion function to load
2896  * addresses for all non-external address book interfaces.
2897  *
2898  * \param callBackFunc Function to be called when an address is
2899  *                     to be loaded.
2900  * \param folderpath Addressbook's Book/folder path to restrict to (if NULL or ""
2901  *                     or "Any", assume the whole addressbook
2902  * \return <i>TRUE</i> if data loaded, <i>FALSE</i> if address index not loaded.
2903  */
2904
2905 gboolean addrindex_load_completion(
2906                 gint (*callBackFunc) ( const gchar *, const gchar *, 
2907                                        const gchar *, const gchar *, GList * ),
2908                 gchar *folderpath )
2909 {
2910         GList *nodeIf, *nodeDS;
2911
2912         if( folderpath != NULL ) {
2913                 AddressDataSource *book;
2914                 ItemFolder* folder;
2915
2916                 /* split the folder path we've received, we'll try to match this path, subpath by
2917                    subpath against the book/folder structure in order and restrict loading of
2918                    addresses to that subpart (if matches). book/folder path must exist and
2919                    folderpath must not be empty or NULL */
2920                 
2921                 if( ! addressbook_peek_folder_exists( folderpath, &book, &folder ) ) {
2922                         g_warning("addrindex_load_completion: folder path '%s' doesn't exist\n", folderpath);
2923                         return FALSE;
2924                 }
2925
2926                 if( folder != NULL ) {
2927
2928                         GList *items;
2929                         GList *nodeM;
2930                         gchar *sName;
2931                         ItemPerson *person;
2932
2933                         debug_print("addrindex_load_completion: folder %p '%s'\n", folder, folder->obj.name);
2934
2935                         /* Load email addresses */
2936                         items = addritem_folder_get_person_list( folder );
2937                         for( ; items != NULL; items = g_list_next( items ) ) {
2938                                 person = items->data;
2939                                 nodeM = person->listEMail;
2940
2941                                 /* Figure out name to use */
2942                                 sName = ADDRITEM_NAME(person);
2943                                 if( sName == NULL || *sName == '\0' ) {
2944                                         sName = person->nickName;
2945                                 }
2946
2947                                 /* Process each E-Mail address */
2948                                 while( nodeM ) {
2949                                         ItemEMail *email = nodeM->data;
2950
2951                                         callBackFunc( sName, email->address, person->nickName, 
2952                                                           ADDRITEM_NAME(email), NULL );
2953
2954                                         nodeM = g_list_next( nodeM );
2955                                 }
2956                         }
2957                         /* Free up the list */
2958                         mgu_clear_list( items );
2959                         g_list_free( items );
2960
2961                         return TRUE;
2962
2963                 } else {
2964
2965                         if( book != NULL ) {
2966
2967                                 AddressBookFile *abf = book->rawDataSource;
2968
2969                                 debug_print("addrindex_load_completion: book %p '%s'\n", book, abf?abf->fileName:"(null)");
2970
2971                                 addrindex_load_completion_load_persons( callBackFunc, book );
2972
2973                                 return TRUE;
2974
2975                         } else {
2976                                 g_warning("addrindex_load_completion: book/folder path is valid but got no pointer\n");
2977                         }
2978                 }
2979                 return FALSE;
2980
2981         } else {
2982
2983                 nodeIf = addrindex_get_interface_list( _addressIndex_ );
2984                 while( nodeIf ) {
2985                         AddressInterface *iface = nodeIf->data;
2986
2987                         nodeIf = g_list_next( nodeIf );
2988
2989                         if( ! iface->useInterface || iface->externalQuery )
2990                                 continue;
2991
2992                         nodeDS = iface->listSource;
2993                         while( nodeDS ) {
2994                                 addrindex_load_completion_load_persons( callBackFunc, nodeDS->data );
2995                         nodeDS = g_list_next( nodeDS );
2996                 }
2997         }
2998         }
2999
3000         return TRUE;
3001 }
3002
3003 /**
3004  * This function can be used to collect information about
3005  * addressbook entries that contain a specific attribute.
3006  *
3007  * \param attr         Name of attribute to look for
3008  * \param callBackFunc Function to be called when a matching attribute was found
3009  * \return <i>TRUE</i>
3010  */
3011 gboolean addrindex_load_person_attribute(
3012                 const gchar *attr,
3013                 gint (*callBackFunc) ( ItemPerson *, const gchar * ) )
3014 {
3015         AddressDataSource *ds;
3016         GList *nodeIf, *nodeDS;
3017         GList *listP, *nodeP;
3018         GList *nodeA;
3019
3020         nodeIf = addrindex_get_interface_list( _addressIndex_ );
3021         while( nodeIf ) {
3022                 gchar *cur_bname;
3023                 AddressInterface *iface = nodeIf->data;
3024
3025                 nodeIf = g_list_next( nodeIf );
3026
3027                 if( ! iface->useInterface || iface->externalQuery )
3028                         continue;
3029
3030                 nodeDS = iface->listSource;
3031                 while( nodeDS ) {
3032                         ds = nodeDS->data;
3033
3034                         /* Read address book */
3035                         if( addrindex_ds_get_modify_flag( ds ) ) {
3036                                 addrindex_ds_read_data( ds );
3037                         }
3038
3039                         if( ! addrindex_ds_get_read_flag( ds ) ) {
3040                                 addrindex_ds_read_data( ds );
3041                         }
3042
3043                         /* Check addressbook name */
3044                         cur_bname = addrindex_ds_get_name( ds );
3045
3046                         /* Get all persons */
3047                         listP = addrindex_ds_get_all_persons( ds );
3048                         nodeP = listP;
3049                         while( nodeP ) {
3050                                 ItemPerson *person = nodeP->data;
3051
3052                                 /* Return all ItemPerson's if attr is NULL */
3053                                 if( attr == NULL ) {
3054                                         callBackFunc(person, cur_bname);
3055                                 }
3056
3057                                 /* Return ItemPerson's with specific attribute */
3058                                 else {
3059                                         nodeA = person->listAttrib;
3060                                         /* Process each User Attribute */
3061                                         while( nodeA ) {
3062                                                 UserAttribute *attrib = nodeA->data;
3063                                                 if( attrib->name && 
3064                                                     !strcmp( attrib->name,attr ) ) {
3065                                                         callBackFunc(person, cur_bname);
3066                                                 }
3067                                                 nodeA = g_list_next( nodeA );
3068                                         }
3069                                 }
3070                                 nodeP = g_list_next( nodeP );
3071                         }
3072                         /* Free up the list */
3073                         g_list_free( listP );
3074
3075                         nodeDS = g_list_next( nodeDS );
3076                 }
3077         }
3078         return TRUE;
3079 }
3080
3081 /**
3082  * This function can be used to collect information about
3083  * addressbook entries
3084  *
3085  * \param callBackFunc Function to be called for each ItemPerson
3086  * \return <i>TRUE</i>
3087  */
3088 gboolean addrindex_load_person_ds( gint (*callBackFunc)
3089                         ( ItemPerson *, AddressDataSource * ) )
3090 {
3091         AddressDataSource *ds;
3092         GList *nodeIf, *nodeDS;
3093         GList *listP, *nodeP;
3094
3095         nodeIf = addrindex_get_interface_list( _addressIndex_ );
3096         while( nodeIf ) {
3097                 AddressInterface *iface = nodeIf->data;
3098
3099                 nodeIf = g_list_next( nodeIf );
3100
3101                 if( ! iface->useInterface || iface->externalQuery )
3102                         continue;
3103
3104                 nodeDS = iface->listSource;
3105                 while( nodeDS ) {
3106                         ds = nodeDS->data;
3107
3108                         /* Read address book */
3109                         if( addrindex_ds_get_modify_flag( ds ) ) {
3110                                 addrindex_ds_read_data( ds );
3111                         }
3112
3113                         if( ! addrindex_ds_get_read_flag( ds ) ) {
3114                                 addrindex_ds_read_data( ds );
3115                         }
3116
3117                         /* Get all persons */
3118                         listP = addrindex_ds_get_all_persons( ds );
3119                         nodeP = listP;
3120                         while( nodeP ) {
3121                                 ItemPerson *person = nodeP->data;
3122
3123                                 callBackFunc(person, ds);
3124                                 nodeP = g_list_next( nodeP );
3125                         }
3126                         /* Free up the list */
3127                         g_list_free( listP );
3128
3129                         nodeDS = g_list_next( nodeDS );
3130                 }
3131         }
3132         return TRUE;
3133 }
3134
3135 gchar *addrindex_get_picture_file(const gchar *emailaddr)
3136 {
3137         AddressDataSource *ds;
3138         GList *nodeIf, *nodeDS;
3139         GList *listP, *nodeP;
3140         gboolean found = FALSE;
3141         gchar *filename = NULL;
3142         gchar *raw_addr = NULL;
3143         
3144         if (!emailaddr)
3145                 return NULL;
3146
3147         Xstrdup_a(raw_addr, emailaddr, return NULL);
3148         extract_address(raw_addr);
3149
3150         nodeIf = addrindex_get_interface_list( _addressIndex_ );
3151         while( nodeIf ) {
3152                 AddressInterface *iface = nodeIf->data;
3153
3154                 nodeIf = g_list_next( nodeIf );
3155
3156                 if( ! iface->useInterface || iface->externalQuery )
3157                         continue;
3158
3159                 nodeDS = iface->listSource;
3160                 while( nodeDS && !found) {
3161                         ds = nodeDS->data;
3162
3163                         /* Read address book */
3164                         if( addrindex_ds_get_modify_flag( ds ) ) {
3165                                 addrindex_ds_read_data( ds );
3166                         }
3167
3168                         if( ! addrindex_ds_get_read_flag( ds ) ) {
3169                                 addrindex_ds_read_data( ds );
3170                         }
3171
3172                         /* Get all persons */
3173                         listP = addrindex_ds_get_all_persons( ds );
3174                         nodeP = listP;
3175                         while( nodeP ) {
3176                                 GList *nodeM;
3177                                 ItemPerson *person = nodeP->data;
3178                                 nodeM = person->listEMail;
3179                                 while(nodeM) {
3180                                         ItemEMail *email = nodeM->data;
3181                                         if (email->address && !strcasecmp(raw_addr, email->address)) {
3182                                                 found = TRUE;
3183                                                 filename = g_strconcat( get_rc_dir(), G_DIR_SEPARATOR_S, 
3184                                                         ADDRBOOK_DIR, G_DIR_SEPARATOR_S, 
3185                                                         person->picture, ".png", NULL );
3186                                                 break;
3187                                         }
3188                                         nodeM = nodeM->next;
3189                                 }
3190                                 nodeP = g_list_next( nodeP );
3191                         }
3192                         /* Free up the list */
3193                         g_list_free( listP );
3194
3195                         nodeDS = g_list_next( nodeDS );
3196                 }
3197         }
3198
3199         return filename;
3200 }
3201
3202 /*
3203  * End of Source.
3204  */