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