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