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