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