talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

addrindex.c (60425B)


      1 /*
      2  * Claws Mail -- a GTK based, lightweight, and fast e-mail client
      3  * Copyright (C) 2001-2022 the Claws Mail team and Match Grun
      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 #include "defs.h"
     20 
     21 #include <glib.h>
     22 #include <glib/gi18n.h>
     23 
     24 #include "mgutils.h"
     25 #include "addritem.h"
     26 #include "addrcache.h"
     27 #include "addrbook.h"
     28 #include "addressbook.h"
     29 #include "addrindex.h"
     30 #include "xml.h"
     31 #include "addrquery.h"
     32 #include "addr_compl.h"
     33 #include "utils.h"
     34 #include "alertpanel.h"
     35 #include "passwordstore.h"
     36 #include "file-utils.h"
     37 
     38 #include "prefs_gtk.h"
     39 #include "codeconv.h"
     40 
     41 #define TAG_ADDRESS_INDEX    "addressbook"
     42 
     43 #define TAG_IF_ADDRESS_BOOK  "book_list"
     44 
     45 #define TAG_DS_ADDRESS_BOOK  "book"
     46 
     47 /* XML Attribute names */
     48 #define ATTAG_BOOK_NAME       "name"
     49 #define ATTAG_BOOK_FILE       "file"
     50 
     51 /* Attribute values */
     52 #define ATVAL_BOOLEAN_YES         "yes"
     53 #define ATVAL_BOOLEAN_NO          "no"
     54 
     55 
     56 #define DISP_NEW_COMMON       _("Common addresses")
     57 #define DISP_NEW_PERSONAL     _("Personal addresses")
     58 
     59 /* Old address book */
     60 #define TAG_IF_OLD_COMMON     "common_address"
     61 #define TAG_IF_OLD_PERSONAL   "personal_address"
     62 
     63 #define DISP_OLD_COMMON       _("Common address")
     64 #define DISP_OLD_PERSONAL     _("Personal address")
     65 
     66 /**
     67  * Singleton object.
     68  */
     69 static AddressIndex *_addressIndex_ = NULL;
     70 
     71 /*
     72  * Define attribute name-value pair.
     73  */
     74 typedef struct _AddressIfAttr AddressIfAttrib;
     75 struct _AddressIfAttr {
     76 	gchar *name;
     77 	gchar *value;
     78 };
     79 
     80 static AddressDataSource *addrindex_create_datasource	( AddressIfType ifType );
     81 
     82 static GList *addrindex_ds_get_all_persons	( AddressDataSource *ds );
     83 static GList *addrindex_ds_get_all_groups	( AddressDataSource *ds );
     84 static AddressDataSource *addrindex_get_datasource	( AddressIndex *addrIndex,
     85 						  const gchar *cacheID );
     86 static AddressInterface *addrindex_get_interface	( AddressIndex *addrIndex,
     87 						  AddressIfType ifType );
     88 static gint addrindex_write_to			( AddressIndex *addrIndex,
     89 					  const gchar *newFile );
     90 
     91 /*
     92  * Define DOM fragment.
     93  */
     94 typedef struct _AddressIfFrag AddressIfFragment;
     95 struct _AddressIfFrag {
     96 	AddressBookType type;
     97 	AddressCache *addressCache;
     98 	gchar *name;
     99 	GList *children;
    100 	GList *attributes;
    101 };
    102 
    103 /**
    104  * Build interface with default values.
    105  *
    106  * \param type Interface type.
    107  * \param name Interface name.
    108  * \param tagIf XML tag name for interface in address index file.
    109  * \param tagDS XML tag name for datasource in address index file.
    110  * \return Address interface object.
    111 */
    112 static AddressInterface *addrindex_create_interface(
    113 		gint type, gchar *name, gchar *tagIf, gchar *tagDS )
    114 {
    115 	AddressInterface *iface = g_new0( AddressInterface, 1 );
    116 
    117 	ADDRITEM_TYPE(iface) = ITEMTYPE_INTERFACE;
    118 	ADDRITEM_ID(iface) = NULL;
    119 	ADDRITEM_NAME(iface) = g_strdup( name );
    120 	ADDRITEM_PARENT(iface) = NULL;
    121 	ADDRITEM_SUBTYPE(iface) = type;
    122 	iface->type = type;
    123 	iface->name = g_strdup( name );
    124 	iface->listTag = g_strdup( tagIf );
    125 	iface->itemTag = g_strdup( tagDS );
    126 	iface->legacyFlag = FALSE;
    127 	iface->haveLibrary = TRUE;
    128 	iface->useInterface = TRUE;
    129 	iface->readOnly      = TRUE;
    130 
    131 	/* Set callbacks to NULL values - override for each interface */
    132 	iface->getAccessFlag = NULL;
    133 	iface->getModifyFlag = NULL;
    134 	iface->getReadFlag   = NULL;
    135 	iface->getStatusCode = NULL;
    136 	iface->getReadData   = NULL;
    137 	iface->getRootFolder = NULL;
    138 	iface->getListFolder = NULL;
    139 	iface->getListPerson = NULL;
    140 	iface->getAllPersons = NULL;
    141 	iface->getAllGroups  = NULL;
    142 	iface->getName       = NULL;
    143 	iface->listSource = NULL;
    144 
    145 	/* Search stuff */
    146 	iface->externalQuery = FALSE;
    147 	iface->searchOrder = 0;		/* Ignored */
    148 	iface->startSearch = NULL;
    149 	iface->stopSearch = NULL;
    150 
    151 	return iface;
    152 }
    153 
    154 /**
    155  * Build table of of all address book interfaces.
    156  * \param addrIndex Address index object.
    157  */
    158 static void addrindex_build_if_list( AddressIndex *addrIndex ) {
    159 	AddressInterface *iface;
    160 
    161 	/* Create intrinsic XML address book interface */
    162 	iface = addrindex_create_interface(
    163 			ADDR_IF_BOOK, "Address Book", TAG_IF_ADDRESS_BOOK,
    164 			TAG_DS_ADDRESS_BOOK );
    165 	iface->readOnly      = FALSE;
    166 	iface->getModifyFlag = ( void * ) addrbook_get_modified;
    167 	iface->getAccessFlag = ( void * ) addrbook_get_accessed;
    168 	iface->getReadFlag   = ( void * ) addrbook_get_read_flag;
    169 	iface->getStatusCode = ( void * ) addrbook_get_status;
    170 	iface->getReadData   = ( void * ) addrbook_read_data;
    171 	iface->getRootFolder = ( void * ) addrbook_get_root_folder;
    172 	iface->getListFolder = ( void * ) addrbook_get_list_folder;
    173 	iface->getListPerson = ( void * ) addrbook_get_list_person;
    174 	iface->getAllPersons = ( void * ) addrbook_get_all_persons;
    175 	iface->getAllGroups  = ( void * ) addrbook_get_all_groups;
    176 	iface->getName       = ( void * ) addrbook_get_name;
    177 	iface->setAccessFlag = ( void * ) addrbook_set_accessed;
    178 	iface->searchOrder   = 0;
    179 
    180 	/* Add to list of interfaces in address book */
    181 	addrIndex->interfaceList =
    182 		g_list_append( addrIndex->interfaceList, iface );
    183 	ADDRITEM_PARENT(iface) = ADDRITEM_OBJECT(addrIndex);
    184 }
    185 
    186 /**
    187  * Free DOM fragment.
    188  * \param fragment Fragment to free.
    189  */
    190 static void addrindex_free_fragment( AddressIfFragment *fragment ) {
    191 	GList *node;
    192 
    193 	/* Free children */
    194 	node = fragment->children;
    195 	while( node ) {
    196 		AddressIfFragment *child = node->data;
    197 		addrindex_free_fragment( child );
    198 		node->data = NULL;
    199 		node = g_list_next( node );
    200 	}
    201 	g_list_free( fragment->children );
    202 
    203 	/* Free attributes */
    204 	node = fragment->attributes;
    205 	while( node ) {
    206 		AddressIfAttrib *nv = node->data;
    207 		g_free( nv->name );
    208 		g_free( nv->value );
    209 		g_free( nv );
    210 		node->data = NULL;
    211 		node = g_list_next( node );
    212 	}
    213 	g_list_free( fragment->attributes );
    214 
    215 	g_free( fragment->name );
    216 	fragment->name = NULL;
    217 	fragment->attributes = NULL;
    218 	fragment->children = NULL;
    219 
    220 	g_free( fragment );
    221 }
    222 
    223 /**
    224  * Create a new data source.
    225  * \param ifType Interface type to create.
    226  * \return Initialized data source.
    227  */
    228 AddressDataSource *addrindex_create_datasource( AddressIfType ifType ) {
    229 	AddressDataSource *ds = g_new0( AddressDataSource, 1 );
    230 
    231 	ADDRITEM_TYPE(ds) = ITEMTYPE_DATASOURCE;
    232 	ADDRITEM_ID(ds) = NULL;
    233 	ADDRITEM_NAME(ds) = NULL;
    234 	ADDRITEM_PARENT(ds) = NULL;
    235 	ADDRITEM_SUBTYPE(ds) = 0;
    236 	ds->type = ifType;
    237 	ds->rawDataSource = NULL;
    238 	ds->interface = NULL;
    239 	return ds;
    240 }
    241 
    242 /**
    243  * Free up data source.
    244  * \param ds Data source to free.
    245  */
    246 void addrindex_free_datasource( AddressDataSource *ds ) {
    247 	AddressInterface *iface;
    248 
    249 	cm_return_if_fail( ds != NULL );
    250 
    251 	iface = ds->interface;
    252 	if( ds->rawDataSource != NULL ) {
    253 		if( iface != NULL ) {
    254 			if( iface->useInterface ) {
    255 				if( iface->type == ADDR_IF_BOOK ) {
    256 					AddressBookFile *abf = ds->rawDataSource;
    257 					addrbook_free_book( abf );
    258 				}
    259 			} else {
    260 				AddressIfFragment *fragment = ds->rawDataSource;
    261 				addrindex_free_fragment( fragment );
    262 			}
    263 		}
    264 	}
    265 
    266 	ADDRITEM_TYPE(ds) = ITEMTYPE_NONE;
    267 	ADDRITEM_ID(ds) = NULL;
    268 	ADDRITEM_NAME(ds) = NULL;
    269 	ADDRITEM_PARENT(ds) = NULL;
    270 	ADDRITEM_SUBTYPE(ds) = 0;
    271 	ds->type = ADDR_IF_NONE;
    272 	ds->interface = NULL;
    273 	ds->rawDataSource = NULL;
    274 
    275 	g_free( ds );
    276 }
    277 
    278 /**
    279  * Free up all data sources for specified interface.
    280  * \param iface Address interface to process.
    281  */
    282 static void addrindex_free_all_datasources( AddressInterface *iface ) {
    283 	GList *node = iface->listSource;
    284 	while( node ) {
    285 		AddressDataSource *ds = node->data;
    286 		addrindex_free_datasource( ds );
    287 		node->data = NULL;
    288 		node = g_list_next( node );
    289 	}
    290 }
    291 
    292 /**
    293  * Free up specified interface.
    294  * \param iface Interface to process.
    295  */
    296 static void addrindex_free_interface( AddressInterface *iface ) {
    297 	/* Free up data sources */
    298 	addrindex_free_all_datasources( iface );
    299 	g_list_free( iface->listSource );
    300 
    301 	/* Free internal storage */
    302 	g_free( ADDRITEM_ID(iface) );
    303 	g_free( ADDRITEM_NAME(iface) );
    304 	g_free( iface->name );
    305 	g_free( iface->listTag );
    306 	g_free( iface->itemTag );
    307 
    308 	/* Clear all pointers */
    309 	ADDRITEM_TYPE(iface) = ITEMTYPE_NONE;
    310 	ADDRITEM_ID(iface) = NULL;
    311 	ADDRITEM_NAME(iface) = NULL;
    312 	ADDRITEM_PARENT(iface) = NULL;
    313 	ADDRITEM_SUBTYPE(iface) = 0;
    314 	iface->type = ADDR_IF_NONE;
    315 	iface->name = NULL;
    316 	iface->listTag = NULL;
    317 	iface->itemTag = NULL;
    318 	iface->legacyFlag = FALSE;
    319 	iface->useInterface = FALSE;
    320 	iface->haveLibrary = FALSE;
    321 	iface->listSource = NULL;
    322 
    323 	/* Search stuff */
    324 	iface->searchOrder = 0;
    325 	iface->startSearch = NULL;
    326 	iface->stopSearch = NULL;
    327 
    328 	g_free( iface );
    329 }
    330 
    331 /**
    332  * Return cache ID for specified data source.
    333  *
    334  * \param  addrIndex Address index.
    335  * \param  ds        Data source.
    336  * \return ID or NULL if not found. This should be <code>g_free()</code>
    337  *         when done.
    338  */
    339 gchar *addrindex_get_cache_id( AddressIndex *addrIndex, AddressDataSource *ds ) {
    340 	gchar *cacheID = NULL;
    341 	AddrBookBase *adbase;
    342 	AddressCache *cache;
    343 
    344 	cm_return_val_if_fail( addrIndex != NULL, NULL );
    345 	cm_return_val_if_fail( ds != NULL, NULL );
    346 
    347 	adbase = ( AddrBookBase * ) ds->rawDataSource;
    348 	if( adbase ) {
    349 		cache = adbase->addressCache;
    350 		if( cache ) {
    351 			cacheID = g_strdup( cache->cacheID );
    352 		}
    353 	}
    354 
    355 	return cacheID;
    356 }
    357 
    358 /**
    359  * Return reference to data source for specified cacheID.
    360  * \param addrIndex Address index.
    361  * \param cacheID   ID.
    362  * \return Data source, or NULL if not found.
    363  */
    364 static AddressDataSource *addrindex_get_datasource(
    365 		AddressIndex *addrIndex, const gchar *cacheID )
    366 {
    367 	cm_return_val_if_fail( addrIndex != NULL, NULL );
    368 	cm_return_val_if_fail( cacheID != NULL, NULL );
    369 	return ( AddressDataSource * ) g_hash_table_lookup( addrIndex->hashCache, cacheID );
    370 }
    371 
    372 /**
    373  * Return reference to address cache for specified cacheID.
    374  * \param addrIndex Address index.
    375  * \param cacheID   ID.
    376  * \return Address cache, or NULL if not found.
    377  */
    378 AddressCache *addrindex_get_cache( AddressIndex *addrIndex, const gchar *cacheID ) {
    379 	AddressDataSource *ds;
    380 	AddrBookBase *adbase;
    381 	AddressCache *cache;
    382 
    383 	cm_return_val_if_fail( addrIndex != NULL, NULL );
    384 	cm_return_val_if_fail( cacheID != NULL, NULL );
    385 
    386 	cache = NULL;
    387 	ds = addrindex_get_datasource( addrIndex, cacheID );
    388 	if( ds ) {
    389 		adbase = ( AddrBookBase * ) ds->rawDataSource;
    390 		cache = adbase->addressCache;
    391 	}
    392 	return cache;
    393 }
    394 
    395 /**
    396  * Add data source into hash table.
    397  * \param addrIndex Address index.
    398  * \param ds        Data source.
    399  */
    400 static void addrindex_hash_add_cache(
    401 		AddressIndex *addrIndex, AddressDataSource *ds )
    402 {
    403 	gchar *cacheID;
    404 
    405 	cacheID = addrindex_get_cache_id( addrIndex, ds );
    406 	if( cacheID ) {
    407 		g_hash_table_insert( addrIndex->hashCache, cacheID, ds );
    408 	}
    409 }
    410 
    411 /*
    412  * Free hash table callback function.
    413  */
    414 static gboolean addrindex_free_cache_cb( gpointer key, gpointer value, gpointer data ) {
    415 	g_free( key );
    416 	key = NULL;
    417 	value = NULL;
    418 	return TRUE;
    419 }
    420 
    421 /*
    422  * Free hash table of address cache items.
    423  */
    424 static void addrindex_free_cache_hash( GHashTable *table ) {
    425 	g_hash_table_foreach_remove( table, addrindex_free_cache_cb, NULL );
    426 	g_hash_table_destroy( table );
    427 }
    428 
    429 /**
    430  * Remove data source from internal hashtable.
    431  * \param addrIndex Address index.
    432  * \param ds        Data source to remove.
    433  */
    434 static void addrindex_hash_remove_cache(
    435 		AddressIndex *addrIndex, AddressDataSource *ds )
    436 {
    437 	gchar *cacheID;
    438 
    439 	cacheID = addrindex_get_cache_id( addrIndex, ds );
    440 	if( cacheID ) {
    441 		g_hash_table_remove( addrIndex->hashCache, cacheID );
    442 		g_free( cacheID );
    443 		cacheID = NULL;
    444 	}
    445 }
    446 
    447 /**
    448  * Create a new address index. This is created as a singleton object.
    449  * \return Initialized address index object.
    450  */
    451 AddressIndex *addrindex_create_index( void ) {
    452 	AddressIndex *index;
    453 
    454 	if( _addressIndex_ == NULL ) {
    455 		index = g_new0( AddressIndex, 1 );
    456 		ADDRITEM_TYPE(index) = ITEMTYPE_INDEX;
    457 		ADDRITEM_ID(index) = NULL;
    458 		ADDRITEM_NAME(index) = g_strdup( "Address Index" );
    459 		ADDRITEM_PARENT(index) = NULL;
    460 		ADDRITEM_SUBTYPE(index) = 0;
    461 		index->filePath = NULL;
    462 		index->fileName = NULL;
    463 		index->retVal = MGU_SUCCESS;
    464 		index->needsConversion = FALSE;
    465 		index->wasConverted = FALSE;
    466 		index->conversionError = FALSE;
    467 		index->interfaceList = NULL;
    468 		index->lastType = ADDR_IF_NONE;
    469 		index->dirtyFlag = FALSE;
    470 		index->hashCache = g_hash_table_new( g_str_hash, g_str_equal );
    471 		index->loadedFlag = FALSE;
    472 		index->searchOrder = NULL;
    473 		addrindex_build_if_list( index );
    474 		_addressIndex_ = index;
    475 	}
    476 	return _addressIndex_;
    477 }
    478 
    479 /**
    480  * Property - Specify file path to address index file.
    481  * \param addrIndex Address index.
    482  * \param value Path to index file.
    483  */
    484 void addrindex_set_file_path( AddressIndex *addrIndex, const gchar *value ) {
    485 	cm_return_if_fail( addrIndex != NULL );
    486 	addrIndex->filePath = mgu_replace_string( addrIndex->filePath, value );
    487 }
    488 
    489 /**
    490  * Property - Specify file name to address index file.
    491  * \param addrIndex Address index.
    492  * \param value File name.
    493  */
    494 void addrindex_set_file_name( AddressIndex *addrIndex, const gchar *value ) {
    495 	cm_return_if_fail( addrIndex != NULL );
    496 	addrIndex->fileName = mgu_replace_string( addrIndex->fileName, value );
    497 }
    498 
    499 /**
    500  * Return list of address interfaces.
    501  * \param addrIndex Address index.
    502  * \return List of address interfaces.
    503  */
    504 GList *addrindex_get_interface_list( AddressIndex *addrIndex ) {
    505 	cm_return_val_if_fail( addrIndex != NULL, NULL );
    506 	return addrIndex->interfaceList;
    507 }
    508 
    509 /**
    510  * Perform any other initialization of address index.
    511  */
    512 void addrindex_initialize( void ) {
    513 	qrymgr_initialize();
    514 	addrcompl_initialize();
    515 }
    516 
    517 /**
    518  * Perform any other teardown of address index.
    519  */
    520 void addrindex_teardown( void ) {
    521 	addrcompl_teardown();
    522 	qrymgr_teardown();
    523 }
    524 
    525 /**
    526  * Free up address index.
    527  * \param addrIndex Address index.
    528  */
    529 void addrindex_free_index( AddressIndex *addrIndex ) {
    530 	GList *node;
    531 
    532 	cm_return_if_fail( addrIndex != NULL );
    533 
    534 	/* Search stuff */
    535 	g_list_free( addrIndex->searchOrder );
    536 	addrIndex->searchOrder = NULL;
    537 
    538 	/* Free internal storage */
    539 	g_free( ADDRITEM_ID(addrIndex) );
    540 	g_free( ADDRITEM_NAME(addrIndex) );
    541 	g_free( addrIndex->filePath );
    542 	g_free( addrIndex->fileName );
    543 
    544 	/* Clear pointers */
    545 	ADDRITEM_TYPE(addrIndex) = ITEMTYPE_NONE;
    546 	ADDRITEM_ID(addrIndex) = NULL;
    547 	ADDRITEM_NAME(addrIndex) = NULL;
    548 	ADDRITEM_PARENT(addrIndex) = NULL;
    549 	ADDRITEM_SUBTYPE(addrIndex) = 0;
    550 	addrIndex->filePath = NULL;
    551 	addrIndex->fileName = NULL;
    552 	addrIndex->retVal = MGU_SUCCESS;
    553 	addrIndex->needsConversion = FALSE;
    554 	addrIndex->wasConverted = FALSE;
    555 	addrIndex->conversionError = FALSE;
    556 	addrIndex->lastType = ADDR_IF_NONE;
    557 	addrIndex->dirtyFlag = FALSE;
    558 
    559 	/* Free up interfaces */
    560 	node = addrIndex->interfaceList;
    561 	while( node ) {
    562 		AddressInterface *iface = node->data;
    563 		addrindex_free_interface( iface );
    564 		node = g_list_next( node );
    565 	}
    566 	g_list_free( addrIndex->interfaceList );
    567 	addrIndex->interfaceList = NULL;
    568 
    569 	/* Free up hash cache */
    570 	addrindex_free_cache_hash( addrIndex->hashCache );
    571 	addrIndex->hashCache = NULL;
    572 
    573 	addrIndex->loadedFlag = FALSE;
    574 
    575 	g_free( addrIndex );
    576 	addrIndex = NULL;
    577 	_addressIndex_ = NULL;
    578 }
    579 
    580 /**
    581  * Print address index.
    582  * \param addrIndex Address index.
    583  * \parem stream    Stream to print.
    584 */
    585 void addrindex_print_index( AddressIndex *addrIndex, FILE *stream ) {
    586 	cm_return_if_fail( addrIndex != NULL );
    587 	fprintf( stream, "AddressIndex:\n" );
    588 	fprintf( stream, "\tfile path: '%s'\n", addrIndex->filePath );
    589 	fprintf( stream, "\tfile name: '%s'\n", addrIndex->fileName );
    590 	fprintf( stream, "\t   status: %d\n", addrIndex->retVal );
    591 	fprintf( stream, "\tconverted: '%s'\n",
    592 			addrIndex->wasConverted ? "yes" : "no" );
    593 	fprintf( stream, "\tcvt error: '%s'\n",
    594 			addrIndex->conversionError ? "yes" : "no" );
    595 	fprintf( stream, "\t---\n" );
    596 }
    597 
    598 /**
    599  * Retrieve reference to address interface for specified interface type.
    600  * \param  addrIndex Address index.
    601  * \param  ifType Interface type.
    602  * \return Address interface, or NULL if not found.
    603  */
    604 static AddressInterface *addrindex_get_interface(
    605 	AddressIndex *addrIndex, AddressIfType ifType )
    606 {
    607 	AddressInterface *retVal = NULL;
    608 	GList *node;
    609 
    610 	cm_return_val_if_fail( addrIndex != NULL, NULL );
    611 
    612 	node = addrIndex->interfaceList;
    613 	while( node ) {
    614 		AddressInterface *iface = node->data;
    615 		node = g_list_next( node );
    616 		if( iface->type == ifType ) {
    617 			retVal = iface;
    618 			break;
    619 		}
    620 	}
    621 	return retVal;
    622 }
    623 
    624 /**
    625  * Add raw data source to index. The raw data object (an AddressBookFile for example)
    626  * should be supplied as the raw dataSource argument.
    627  *
    628  * \param  addrIndex Address index.
    629  * \param ifType     Interface type to add.
    630  * \param dataSource Actual raw data source to add.
    631  * \return Data source added, or NULL if invalid interface type.
    632  */
    633 AddressDataSource *addrindex_index_add_datasource(
    634 	AddressIndex *addrIndex, AddressIfType ifType, gpointer dataSource )
    635 {
    636 	AddressInterface *iface;
    637 	AddressDataSource *ds = NULL;
    638 
    639 	cm_return_val_if_fail( addrIndex != NULL, NULL );
    640 	cm_return_val_if_fail( dataSource != NULL, NULL );
    641 
    642 	iface = addrindex_get_interface( addrIndex, ifType );
    643 	if( iface ) {
    644 		ds = addrindex_create_datasource( ifType );
    645 		ADDRITEM_PARENT(ds) = ADDRITEM_OBJECT(iface);
    646 		ds->type = ifType;
    647 		ds->rawDataSource = dataSource;
    648 		ds->interface = iface;
    649 		iface->listSource = g_list_append( iface->listSource, ds );
    650 		addrIndex->dirtyFlag = TRUE;
    651 
    652 		addrindex_hash_add_cache( addrIndex, ds );
    653 	}
    654 	return ds;
    655 }
    656 
    657 /**
    658  * Remove specified data source from index.
    659  * \param  addrIndex Address index.
    660  * \param  dataSource Data source to add.
    661  * \return Reference to data source if removed, or NULL if data source was not
    662  *         found in index. Note the this object must still be freed.
    663  */
    664 AddressDataSource *addrindex_index_remove_datasource(
    665 	AddressIndex *addrIndex, AddressDataSource *dataSource )
    666 {
    667 	AddressDataSource *retVal = FALSE;
    668 	AddressInterface *iface;
    669 
    670 	cm_return_val_if_fail( addrIndex != NULL, NULL );
    671 	cm_return_val_if_fail( dataSource != NULL, NULL );
    672 
    673 	iface = addrindex_get_interface( addrIndex, dataSource->type );
    674 	if( iface ) {
    675 		iface->listSource = g_list_remove( iface->listSource, dataSource );
    676 		addrIndex->dirtyFlag = TRUE;
    677 		dataSource->interface = NULL;
    678 
    679 		/* Remove cache from hash table */
    680 		addrindex_hash_remove_cache( addrIndex, dataSource );
    681 
    682 		retVal = dataSource;
    683 	}
    684 	return retVal;
    685 }
    686 
    687 /**
    688  * Retrieve a reference to address interface for specified interface type and
    689  * XML interface tag name.
    690  * \param  addrIndex Address index.
    691  * \param  tag       XML interface tag name to match.
    692  * \param  ifType    Interface type to match.
    693  * \return Reference to address index, or NULL if not found in index.
    694  */
    695 static AddressInterface *addrindex_tag_get_interface(
    696 	AddressIndex *addrIndex, gchar *tag, AddressIfType ifType )
    697 {
    698 	AddressInterface *retVal = NULL;
    699 	GList *node = addrIndex->interfaceList;
    700 
    701 	while( node ) {
    702 		AddressInterface *iface = node->data;
    703 		node = g_list_next( node );
    704 		if( tag ) {
    705 			if( strcmp( iface->listTag, tag ) == 0 ) {
    706 				retVal = iface;
    707 				break;
    708 			}
    709 		}
    710 		else {
    711 			if( iface->type == ifType ) {
    712 				retVal = iface;
    713 				break;
    714 			}
    715 		}
    716 	}
    717 	return retVal;
    718 }
    719 
    720 /**
    721  * Retrieve a reference to address interface for specified interface type and
    722  * XML datasource tag name.
    723  * \param  addrIndex Address index.
    724  * \param  ifType    Interface type to match.
    725  * \param  tag       XML datasource tag name to match.
    726  * \return Reference to address index, or NULL if not found in index.
    727  */
    728 static AddressInterface *addrindex_tag_get_datasource(
    729 	AddressIndex *addrIndex, AddressIfType ifType, gchar *tag )
    730 {
    731 	AddressInterface *retVal = NULL;
    732 	GList *node = addrIndex->interfaceList;
    733 
    734 	while( node ) {
    735 		AddressInterface *iface = node->data;
    736 		node = g_list_next( node );
    737 		if( iface->type == ifType && iface->itemTag ) {
    738 			if( strcmp( iface->itemTag, tag ) == 0 ) {
    739 				retVal = iface;
    740 				break;
    741 			}
    742 		}
    743 	}
    744 	return retVal;
    745 }
    746 
    747 /* **********************************************************************
    748 * Interface XML parsing functions.
    749 * ***********************************************************************
    750 */
    751 
    752 /**
    753  * Write start of XML element to file.
    754  * \param fp   File.
    755  * \param lvl  Indentation level.
    756  * \param name Element name.
    757  */
    758 static int addrindex_write_elem_s( FILE *fp, const gint lvl, const gchar *name ) {
    759 	gint i;
    760 	for( i = 0; i < lvl; i++ )
    761 		if (fputs( "  ", fp ) == EOF)
    762 			return -1;
    763 	if (fputs( "<", fp ) == EOF)
    764 		return -1;
    765 	if (fputs( name, fp ) == EOF)
    766 		return -1;
    767 	return 0;
    768 }
    769 
    770 /**
    771  * Write end of XML element to file.
    772  * \param fp   File.
    773  * \param lvl  Indentation level.
    774  * \param name Element name.
    775  */
    776 static int addrindex_write_elem_e( FILE *fp, const gint lvl, const gchar *name ) {
    777 	gint i;
    778 	for( i = 0; i < lvl; i++ )
    779 		if (fputs( "  ", fp ) == EOF)
    780 			return -1;
    781 	if (fputs( "</", fp ) == EOF)
    782 		return -1;
    783 	if (fputs( name, fp ) == EOF)
    784 		return -1;
    785 	if (fputs( ">\n", fp ) == EOF)
    786 		return -1;
    787 	return 0;
    788 }
    789 
    790 /**
    791  * Write XML attribute to file.
    792  * \param fp    File.
    793  * \param name  Attribute name.
    794  * \param value Attribute value.
    795  */
    796 static int addrindex_write_attr( FILE *fp, const gchar *name, const gchar *value ) {
    797 	if (fputs( " ", fp ) == EOF)
    798 		return -1;
    799 	if (fputs( name, fp ) == EOF)
    800 		return -1;
    801 	if (fputs( "=\"", fp ) == EOF)
    802 		return -1;
    803 	if (xml_file_put_escape_str( fp, value ) < 0)
    804 		return -1;
    805 	if (fputs( "\"", fp ) == EOF)
    806 		return -1;
    807 	return 0;
    808 }
    809 
    810 /**
    811  * Read/parse address index file, creating a data source for a regular
    812  * intrinsic XML addressbook.
    813  * \param  file Address index file.
    814  * \return Data source.
    815  */
    816 static AddressDataSource *addrindex_parse_book( XMLFile *file ) {
    817 	AddressDataSource *ds;
    818 	AddressBookFile *abf;
    819 	GList *attr;
    820 
    821 	ds = addrindex_create_datasource( ADDR_IF_BOOK );
    822 	abf = addrbook_create_book();
    823 	attr = xml_get_current_tag_attr( file );
    824 	while( attr ) {
    825 		gchar *name = ((XMLAttr *)attr->data)->name;
    826 		gchar *value = ((XMLAttr *)attr->data)->value;
    827 		if( strcmp( name, ATTAG_BOOK_NAME ) == 0 ) {
    828 			addrbook_set_name( abf, value );
    829 		}
    830 		else if( strcmp( name, ATTAG_BOOK_FILE ) == 0) {
    831 			addrbook_set_file( abf, value );
    832 		}
    833 		attr = g_list_next( attr );
    834 	}
    835 	ds->rawDataSource = abf;
    836 	return ds;
    837 }
    838 
    839 static int addrindex_write_book( FILE *fp, AddressDataSource *ds, gint lvl ) {
    840 	AddressBookFile *abf = ds->rawDataSource;
    841 	if( abf ) {
    842 		if (addrindex_write_elem_s( fp, lvl, TAG_DS_ADDRESS_BOOK ) < 0)
    843 			return -1;
    844 		if (addrindex_write_attr( fp, ATTAG_BOOK_NAME, addrbook_get_name( abf ) ) < 0)
    845 			return -1;
    846 		if (addrindex_write_attr( fp, ATTAG_BOOK_FILE, abf->fileName ) < 0)
    847 			return -1;
    848 		if (fputs( " />\n", fp ) == EOF)
    849 			return -1;
    850 	}
    851 	return 0;
    852 }
    853 
    854 /* **********************************************************************
    855 * Address index I/O functions.
    856 * ***********************************************************************
    857 */
    858 /**
    859  * Read address index file, creating appropriate data sources for each address
    860  * index file entry.
    861  *
    862  * \param  addrIndex Address index.
    863  * \param  file Address index file.
    864  */
    865 static void addrindex_read_index( AddressIndex *addrIndex, XMLFile *file ) {
    866 	XMLTag *xtag;
    867 	AddressInterface *iface = NULL, *dsIFace = NULL;
    868 	AddressDataSource *ds;
    869 	gint rc;
    870 
    871 	addrIndex->loadedFlag = FALSE;
    872 	for (;;) {
    873 		rc = xml_parse_next_tag( file );
    874 		if( rc < 0 || file->level == 0 ) return;
    875 
    876 		xtag = xml_get_current_tag( file );
    877 
    878 		iface = addrindex_tag_get_interface( addrIndex, xtag->tag, ADDR_IF_NONE );
    879 		if( iface ) {
    880 			addrIndex->lastType = iface->type;
    881 			if( iface->legacyFlag ) addrIndex->needsConversion = TRUE;
    882 		}
    883 		else {
    884 			dsIFace = addrindex_tag_get_datasource(
    885 					addrIndex, addrIndex->lastType, xtag->tag );
    886 			if( dsIFace ) {
    887 				/* Add data source to list */
    888 				ds = NULL;
    889 				if( addrIndex->lastType == ADDR_IF_BOOK ) {
    890 					ds = addrindex_parse_book( file );
    891 					if( ds->rawDataSource ) {
    892 						addrbook_set_path( ds->rawDataSource,
    893 							addrIndex->filePath );
    894 					}
    895 				}
    896 				if( ds ) {
    897 					ds->interface = dsIFace;
    898 					addrindex_hash_add_cache( addrIndex, ds );
    899 					dsIFace->listSource =
    900 						g_list_append( dsIFace->listSource, ds );
    901 				}
    902 			}
    903 		}
    904 	}
    905 }
    906 
    907 /*
    908  * Search order sorting comparison function for building search order list.
    909  */
    910 static gint addrindex_search_order_compare( gconstpointer ptrA, gconstpointer ptrB ) {
    911 	AddressInterface *ifaceA = ( AddressInterface * ) ptrA;
    912 	AddressInterface *ifaceB = ( AddressInterface * ) ptrB;
    913 
    914 	return ifaceA->searchOrder - ifaceB->searchOrder;
    915 }
    916 
    917 /**
    918  * Build list of data sources to process.
    919  * \param addrIndex Address index object.
    920  */
    921 static void addrindex_build_search_order( AddressIndex *addrIndex ) {
    922 	GList *nodeIf;
    923 
    924 	/* Clear existing list */
    925 	g_list_free( addrIndex->searchOrder );
    926 	addrIndex->searchOrder = NULL;
    927 
    928 	/* Build new list */
    929 	nodeIf = addrIndex->interfaceList;
    930 	while( nodeIf ) {
    931 		AddressInterface *iface = nodeIf->data;
    932 		if( iface->useInterface ) {
    933 			if( iface->searchOrder > 0 ) {
    934 				/* Add to search order list */
    935 				addrIndex->searchOrder = g_list_insert_sorted(
    936 					addrIndex->searchOrder, iface,
    937 					addrindex_search_order_compare );
    938 			}
    939 		}
    940 		nodeIf = g_list_next( nodeIf );
    941 	}
    942 }
    943 
    944 static gint addrindex_read_file( AddressIndex *addrIndex ) {
    945 	XMLFile *file = NULL;
    946 	gchar *fileSpec = NULL;
    947 
    948 	cm_return_val_if_fail( addrIndex != NULL, -1 );
    949 
    950 	fileSpec = g_strconcat( addrIndex->filePath, G_DIR_SEPARATOR_S, addrIndex->fileName, NULL );
    951 	addrIndex->retVal = MGU_NO_FILE;
    952 	file = xml_open_file( fileSpec );
    953 	g_free( fileSpec );
    954 
    955 	if( file == NULL ) {
    956 		/*
    957 		g_print( " file '%s' does not exist.\n", addrIndex->fileName );
    958 		*/
    959 		return addrIndex->retVal;
    960 	}
    961 
    962 	addrIndex->retVal = MGU_BAD_FORMAT;
    963 	if( xml_get_dtd( file ) == 0 ) {
    964 		if( xml_parse_next_tag( file ) == 0 ) {
    965 			if( xml_compare_tag( file, TAG_ADDRESS_INDEX ) ) {
    966 				addrindex_read_index( addrIndex, file );
    967 				addrIndex->retVal = MGU_SUCCESS;
    968 			}
    969 		}
    970 	}
    971 	xml_close_file( file );
    972 
    973 	addrindex_build_search_order( addrIndex );
    974 
    975 	return addrIndex->retVal;
    976 }
    977 
    978 static int addrindex_write_index( AddressIndex *addrIndex, FILE *fp ) {
    979 	GList *nodeIF, *nodeDS;
    980 	gint lvlList = 1;
    981 	gint lvlItem = 1 + lvlList;
    982 
    983 	nodeIF = addrIndex->interfaceList;
    984 	while( nodeIF ) {
    985 		AddressInterface *iface = nodeIF->data;
    986 		if( ! iface->legacyFlag ) {
    987 			nodeDS = iface->listSource;
    988 			if (addrindex_write_elem_s( fp, lvlList, iface->listTag ) < 0)
    989 				return -1;
    990 			if (fputs( ">\n", fp ) == EOF)
    991 				return -1;
    992 			while( nodeDS ) {
    993 				AddressDataSource *ds = nodeDS->data;
    994 				if( ds ) {
    995 					if( iface->type == ADDR_IF_BOOK ) {
    996 						if (addrindex_write_book( fp, ds, lvlItem ) < 0)
    997 							return -1;
    998 					}
    999 				}
   1000 				nodeDS = g_list_next( nodeDS );
   1001 			}
   1002 			if (addrindex_write_elem_e( fp, lvlList, iface->listTag ) < 0)
   1003 				return -1;
   1004 		}
   1005 		nodeIF = g_list_next( nodeIF );
   1006 	}
   1007 	return 0;
   1008 }
   1009 
   1010 /*
   1011 * Write data to specified file.
   1012 * Enter: addrIndex Address index object.
   1013 *        newFile   New file name.
   1014 * return: Status code, from addrIndex->retVal.
   1015 * Note: File will be created in directory specified by addrIndex.
   1016 */
   1017 static gint addrindex_write_to( AddressIndex *addrIndex, const gchar *newFile ) {
   1018 	FILE *fp;
   1019 	gchar *fileSpec;
   1020 	PrefFile *pfile;
   1021 
   1022 	cm_return_val_if_fail( addrIndex != NULL, -1 );
   1023 
   1024 	fileSpec = g_strconcat( addrIndex->filePath, G_DIR_SEPARATOR_S, newFile, NULL );
   1025 	addrIndex->retVal = MGU_OPEN_FILE;
   1026 	pfile = prefs_write_open( fileSpec );
   1027 	g_free( fileSpec );
   1028 	if( pfile ) {
   1029 		fp = pfile->fp;
   1030 		if (fprintf( fp, "<?xml version=\"1.0\" encoding=\"%s\" ?>\n", CS_INTERNAL ) < 0)
   1031 			goto fail;
   1032 		if (addrindex_write_elem_s( fp, 0, TAG_ADDRESS_INDEX ) < 0)
   1033 			goto fail;
   1034 		if (fputs( ">\n", fp ) == EOF)
   1035 			goto fail;
   1036 
   1037 		if (addrindex_write_index( addrIndex, fp ) < 0)
   1038 			goto fail;
   1039 		if (addrindex_write_elem_e( fp, 0, TAG_ADDRESS_INDEX ) < 0)
   1040 			goto fail;
   1041 
   1042 		addrIndex->retVal = MGU_SUCCESS;
   1043 		if (prefs_file_close(pfile) < 0)
   1044 			addrIndex->retVal = MGU_ERROR_WRITE;
   1045 	}
   1046 
   1047 	fileSpec = NULL;
   1048 	return addrIndex->retVal;
   1049 fail:
   1050 	g_warning("error writing AB index");
   1051 	addrIndex->retVal = MGU_ERROR_WRITE;
   1052 	if (pfile)
   1053 		prefs_file_close_revert( pfile );
   1054 	return addrIndex->retVal;
   1055 }
   1056 
   1057 /*
   1058 * Save address index data to original file.
   1059 * return: Status code, from addrIndex->retVal.
   1060 */
   1061 gint addrindex_save_data( AddressIndex *addrIndex ) {
   1062 	cm_return_val_if_fail( addrIndex != NULL, -1 );
   1063 
   1064 	addrIndex->retVal = MGU_NO_FILE;
   1065 	if( addrIndex->fileName == NULL || *addrIndex->fileName == '\0' ) return addrIndex->retVal;
   1066 	if( addrIndex->filePath == NULL || *addrIndex->filePath == '\0' ) return addrIndex->retVal;
   1067 
   1068 	addrindex_write_to( addrIndex, addrIndex->fileName );
   1069 	if( addrIndex->retVal == MGU_SUCCESS ) {
   1070 		addrIndex->dirtyFlag = FALSE;
   1071 	}
   1072 	return addrIndex->retVal;
   1073 }
   1074 
   1075 /*
   1076 * Save all address book files which may have changed.
   1077 * Return: Status code, set if there was a problem saving data.
   1078 */
   1079 gint addrindex_save_all_books( AddressIndex *addrIndex ) {
   1080 	gint retVal = MGU_SUCCESS;
   1081 	GList *nodeIf, *nodeDS;
   1082 
   1083 	nodeIf = addrIndex->interfaceList;
   1084 	while( nodeIf ) {
   1085 		AddressInterface *iface = nodeIf->data;
   1086 		if( iface->type == ADDR_IF_BOOK ) {
   1087 			nodeDS = iface->listSource;
   1088 			while( nodeDS ) {
   1089 				AddressDataSource *ds = nodeDS->data;
   1090 				AddressBookFile *abf = ds->rawDataSource;
   1091 				if( addrbook_get_dirty( abf ) ) {
   1092 					if( addrbook_get_read_flag( abf ) ) {
   1093 						addrbook_save_data( abf );
   1094 						if( abf->retVal != MGU_SUCCESS ) {
   1095 							retVal = abf->retVal;
   1096 						}
   1097 					}
   1098 				}
   1099 				nodeDS = g_list_next( nodeDS );
   1100 			}
   1101 			break;
   1102 		}
   1103 		nodeIf = g_list_next( nodeIf );
   1104 	}
   1105 	return retVal;
   1106 }
   1107 
   1108 
   1109 /* **********************************************************************
   1110 * Address book conversion to new format.
   1111 * ***********************************************************************
   1112 */
   1113 
   1114 #define ELTAG_IF_OLD_FOLDER   "folder"
   1115 #define ELTAG_IF_OLD_GROUP    "group"
   1116 #define ELTAG_IF_OLD_ITEM     "item"
   1117 #define ELTAG_IF_OLD_NAME     "name"
   1118 #define ELTAG_IF_OLD_ADDRESS  "address"
   1119 #define ELTAG_IF_OLD_REMARKS  "remarks"
   1120 #define ATTAG_IF_OLD_NAME     "name"
   1121 
   1122 #define TEMPNODE_ROOT         0
   1123 #define TEMPNODE_FOLDER       1
   1124 #define TEMPNODE_GROUP        2
   1125 #define TEMPNODE_ADDRESS      3
   1126 
   1127 typedef struct _AddressCvt_Node AddressCvtNode;
   1128 struct _AddressCvt_Node {
   1129 	gint  type;
   1130 	gchar *name;
   1131 	gchar *address;
   1132 	gchar *remarks;
   1133 	GList *list;
   1134 };
   1135 
   1136 /*
   1137 * Parse current address item.
   1138 */
   1139 static AddressCvtNode *addrindex_parse_item( XMLFile *file ) {
   1140 	gchar *element;
   1141 	guint level;
   1142 	AddressCvtNode *nn;
   1143 
   1144 	nn = g_new0( AddressCvtNode, 1 );
   1145 	nn->type = TEMPNODE_ADDRESS;
   1146 	nn->list = NULL;
   1147 
   1148 	level = file->level;
   1149 
   1150 	for (;;) {
   1151 		xml_parse_next_tag(file);
   1152 		if (file->level < level) return nn;
   1153 
   1154 		element = xml_get_element( file );
   1155 		if( xml_compare_tag( file, ELTAG_IF_OLD_NAME ) ) {
   1156 			nn->name = g_strdup( element );
   1157 		}
   1158 		if( xml_compare_tag( file, ELTAG_IF_OLD_ADDRESS ) ) {
   1159 			nn->address = g_strdup( element );
   1160 		}
   1161 		if( xml_compare_tag( file, ELTAG_IF_OLD_REMARKS ) ) {
   1162 			nn->remarks = g_strdup( element );
   1163 		}
   1164 		g_free(element);
   1165 		xml_parse_next_tag(file);
   1166 	}
   1167 }
   1168 
   1169 /*
   1170 * Create a temporary node below specified node.
   1171 */
   1172 static AddressCvtNode *addrindex_add_object( AddressCvtNode *node, gint type, gchar *name, gchar *addr, char *rem ) {
   1173 	AddressCvtNode *nn;
   1174 	nn = g_new0( AddressCvtNode, 1 );
   1175 	nn->type = type;
   1176 	nn->name = g_strdup( name );
   1177 	nn->remarks = g_strdup( rem );
   1178 	node->list = g_list_append( node->list, nn );
   1179 	return nn;
   1180 }
   1181 
   1182 /*
   1183 * Process current temporary node.
   1184 */
   1185 static void addrindex_add_obj( XMLFile *file, AddressCvtNode *node ) {
   1186 	GList *attr;
   1187 	guint prev_level;
   1188 	AddressCvtNode *newNode = NULL;
   1189 	gchar *name;
   1190 	gchar *value;
   1191 
   1192 	for (;;) {
   1193 		prev_level = file->level;
   1194 		xml_parse_next_tag( file );
   1195 		if (file->level < prev_level) return;
   1196 		name = NULL;
   1197 		value = NULL;
   1198 
   1199 		if( xml_compare_tag( file, ELTAG_IF_OLD_GROUP ) ) {
   1200 			attr = xml_get_current_tag_attr(file);
   1201 			if (attr) {
   1202 				name = ((XMLAttr *)attr->data)->name;
   1203 				if( strcmp( name, ATTAG_IF_OLD_NAME ) == 0 ) {
   1204 					value = ((XMLAttr *)attr->data)->value;
   1205 				}
   1206 			}
   1207 			newNode = addrindex_add_object( node, TEMPNODE_GROUP, value, "", "" );
   1208 			addrindex_add_obj( file, newNode );
   1209 
   1210 		}
   1211 		else if( xml_compare_tag( file, ELTAG_IF_OLD_FOLDER ) ) {
   1212 			attr = xml_get_current_tag_attr(file);
   1213 			if (attr) {
   1214 				name = ((XMLAttr *)attr->data)->name;
   1215 				if( strcmp( name, ATTAG_IF_OLD_NAME ) == 0 ) {
   1216 					value = ((XMLAttr *)attr->data)->value;
   1217 				}
   1218 			}
   1219 			newNode = addrindex_add_object( node, TEMPNODE_FOLDER, value, "", "" );
   1220 			addrindex_add_obj( file, newNode );
   1221 		}
   1222 		else if( xml_compare_tag( file, ELTAG_IF_OLD_ITEM ) ) {
   1223 			newNode = addrindex_parse_item( file );
   1224 			node->list = g_list_append( node->list, newNode );
   1225 		}
   1226 		else {
   1227 			g_warning("invalid tag");
   1228 		}
   1229 	}
   1230 }
   1231 
   1232 /*
   1233 * Consume all nodes below current tag.
   1234 */
   1235 static void addrindex_consume_tree( XMLFile *file ) {
   1236 	guint prev_level;
   1237 
   1238 	for (;;) {
   1239 		prev_level = file->level;
   1240 		xml_parse_next_tag( file );
   1241 		if (file->level < prev_level)
   1242 			return;
   1243 
   1244 		addrindex_consume_tree( file );
   1245 	}
   1246 }
   1247 
   1248 /*
   1249 * Free up temporary tree.
   1250 */
   1251 static void addrindex_free_node( AddressCvtNode *node ) {
   1252 	GList *list = node->list;
   1253 
   1254 	while( list ) {
   1255 		AddressCvtNode *lNode = list->data;
   1256 		list = g_list_next( list );
   1257 		addrindex_free_node( lNode );
   1258 	}
   1259 	node->type = TEMPNODE_ROOT;
   1260 	g_free( node->name );
   1261 	g_free( node->address );
   1262 	g_free( node->remarks );
   1263 	g_list_free( node->list );
   1264 	g_free( node );
   1265 }
   1266 
   1267 /*
   1268 * Process address book for specified node.
   1269 */
   1270 static void addrindex_process_node(
   1271 		AddressBookFile *abf, AddressCvtNode *node, ItemFolder *parent,
   1272 		ItemGroup *parentGrp, ItemFolder *folderGrp )
   1273 {
   1274 	GList *list;
   1275 	ItemFolder *itemFolder = NULL;
   1276 	ItemGroup *itemGParent = parentGrp;
   1277 	ItemFolder *itemGFolder = folderGrp;
   1278 	AddressCache *cache = abf->addressCache;
   1279 
   1280 	if( node->type == TEMPNODE_ROOT ) {
   1281 		itemFolder = parent;
   1282 	}
   1283 	else if( node->type == TEMPNODE_FOLDER ) {
   1284 		itemFolder = addritem_create_item_folder();
   1285 		addritem_folder_set_name( itemFolder, node->name );
   1286 		addrcache_id_folder( cache, itemFolder );
   1287 		addrcache_folder_add_folder( cache, parent, itemFolder );
   1288 		itemGFolder = NULL;
   1289 	}
   1290 	else if( node->type == TEMPNODE_GROUP ) {
   1291 		ItemGroup *itemGroup;
   1292 		gchar *fName;
   1293 
   1294 		/* Create a folder for group */
   1295 		fName = g_strdup_printf( "Cvt - %s", node->name );
   1296 		itemGFolder = addritem_create_item_folder();
   1297 		addritem_folder_set_name( itemGFolder, fName );
   1298 		addrcache_id_folder( cache, itemGFolder );
   1299 		addrcache_folder_add_folder( cache, parent, itemGFolder );
   1300 		g_free( fName );
   1301 
   1302 		/* Add group into folder */
   1303 		itemGroup = addritem_create_item_group();
   1304 		addritem_group_set_name( itemGroup, node->name );
   1305 		addrcache_id_group( cache, itemGroup );
   1306 		addrcache_folder_add_group( cache, itemGFolder, itemGroup );
   1307 		itemGParent = itemGroup;
   1308 	}
   1309 	else if( node->type == TEMPNODE_ADDRESS ) {
   1310 		ItemPerson *itemPerson;
   1311 		ItemEMail *itemEMail;
   1312 
   1313 		/* Create person and email objects */
   1314 		itemPerson = addritem_create_item_person();
   1315 		addritem_person_set_common_name( itemPerson, node->name );
   1316 		addrcache_id_person( cache, itemPerson );
   1317 		itemEMail = addritem_create_item_email();
   1318 		addritem_email_set_address( itemEMail, node->address );
   1319 		addritem_email_set_remarks( itemEMail, node->remarks );
   1320 		addrcache_id_email( cache, itemEMail );
   1321 		addrcache_person_add_email( cache, itemPerson, itemEMail );
   1322 
   1323 		/* Add person into appropriate folder */
   1324 		if( itemGFolder ) {
   1325 			addrcache_folder_add_person( cache, itemGFolder, itemPerson );
   1326 		}
   1327 		else {
   1328 			addrcache_folder_add_person( cache, parent, itemPerson );
   1329 		}
   1330 
   1331 		/* Add email address only into group */
   1332 		if( parentGrp ) {
   1333 			addrcache_group_add_email( cache, parentGrp, itemEMail );
   1334 		}
   1335 	}
   1336 
   1337 	list = node->list;
   1338 	while( list ) {
   1339 		AddressCvtNode *lNode = list->data;
   1340 		list = g_list_next( list );
   1341 		addrindex_process_node( abf, lNode, itemFolder, itemGParent, itemGFolder );
   1342 	}
   1343 }
   1344 
   1345 /*
   1346 * Process address book to specified file number.
   1347 */
   1348 static gboolean addrindex_process_book( AddressIndex *addrIndex, XMLFile *file, gchar *displayName ) {
   1349 	gboolean retVal = FALSE;
   1350 	AddressBookFile *abf = NULL;
   1351 	AddressCvtNode *rootNode = NULL;
   1352 	gchar *newFile = NULL;
   1353 	GList *fileList = NULL;
   1354 	gint fileNum  = 0;
   1355 
   1356 	/* Setup root node */
   1357 	rootNode = g_new0( AddressCvtNode, 1 );
   1358 	rootNode->type = TEMPNODE_ROOT;
   1359 	rootNode->name = g_strdup( "root" );
   1360 	rootNode->list = NULL;
   1361 	addrindex_add_obj( file, rootNode );
   1362 	/* addrindex_print_node( rootNode, stdout ); */
   1363 
   1364 	/* Create new address book */
   1365 	abf = addrbook_create_book();
   1366 	addrbook_set_name( abf, displayName );
   1367 	addrbook_set_path( abf, addrIndex->filePath );
   1368 
   1369 	/* Determine next available file number */
   1370 	fileList = addrbook_get_bookfile_list( abf );
   1371 	if( fileList ) {
   1372 		fileNum = 1 + abf->maxValue;
   1373 	}
   1374 	g_list_free( fileList );
   1375 	fileList = NULL;
   1376 
   1377 	newFile = addrbook_gen_new_file_name( fileNum );
   1378 	if( newFile ) {
   1379 		addrbook_set_file( abf, newFile );
   1380 	}
   1381 
   1382 	addrindex_process_node( abf, rootNode, abf->addressCache->rootFolder, NULL, NULL );
   1383 
   1384 #ifdef DEBUG_ADDRBOOK
   1385 	addrbook_dump_book( abf, stdout );
   1386 #endif
   1387 	addrbook_save_data( abf );
   1388 	addrIndex->retVal = abf->retVal;
   1389 	if( abf->retVal == MGU_SUCCESS ) retVal = TRUE;
   1390 
   1391 	addrbook_free_book( abf );
   1392 	abf = NULL;
   1393 	addrindex_free_node( rootNode );
   1394 	rootNode = NULL;
   1395 
   1396 	/* Create entries in address index */
   1397 	if( retVal ) {
   1398 		abf = addrbook_create_book();
   1399 		addrbook_set_name( abf, displayName );
   1400 		addrbook_set_path( abf, addrIndex->filePath );
   1401 		addrbook_set_file( abf, newFile );
   1402 		addrindex_index_add_datasource( addrIndex, ADDR_IF_BOOK, abf );
   1403 	}
   1404 
   1405 	return retVal;
   1406 }
   1407 
   1408 /*
   1409 * Process tree converting data.
   1410 */
   1411 static void addrindex_convert_tree( AddressIndex *addrIndex, XMLFile *file ) {
   1412 	guint prev_level;
   1413 	XMLTag *xtag;
   1414 
   1415 	/* Process file */
   1416 	for (;;) {
   1417 		prev_level = file->level;
   1418 		xml_parse_next_tag( file );
   1419 		if (file->level < prev_level) return;
   1420 
   1421 		xtag = xml_get_current_tag( file );
   1422 		/* g_print( "tag : %d : %s\n", prev_level, xtag->tag ); */
   1423 		if( strcmp( xtag->tag, TAG_IF_OLD_COMMON ) == 0 ) {
   1424 			if( addrindex_process_book( addrIndex, file, DISP_OLD_COMMON ) ) {
   1425 				addrIndex->needsConversion = FALSE;
   1426 				addrIndex->wasConverted = TRUE;
   1427 				continue;
   1428 			}
   1429 			return;
   1430 		}
   1431 		if( strcmp( xtag->tag, TAG_IF_OLD_PERSONAL ) == 0 ) {
   1432 			if( addrindex_process_book( addrIndex, file, DISP_OLD_PERSONAL ) ) {
   1433 				addrIndex->needsConversion = FALSE;
   1434 				addrIndex->wasConverted = TRUE;
   1435 				continue;
   1436 			}
   1437 			return;
   1438 		}
   1439 		addrindex_consume_tree( file );
   1440 	}
   1441 }
   1442 
   1443 static gint addrindex_convert_data( AddressIndex *addrIndex ) {
   1444 	XMLFile *file = NULL;
   1445 	gchar *fileSpec;
   1446 
   1447 	fileSpec = g_strconcat( addrIndex->filePath, G_DIR_SEPARATOR_S, addrIndex->fileName, NULL );
   1448 	addrIndex->retVal = MGU_NO_FILE;
   1449 	file = xml_open_file( fileSpec );
   1450 	g_free( fileSpec );
   1451 
   1452 	if( file == NULL ) {
   1453 		/* g_print( " file '%s' does not exist.\n", addrIndex->fileName ); */
   1454 		return addrIndex->retVal;
   1455 	}
   1456 
   1457 	addrIndex->retVal = MGU_BAD_FORMAT;
   1458 	if( xml_get_dtd( file ) == 0 ) {
   1459 		if( xml_parse_next_tag( file ) == 0 ) {
   1460 			if( xml_compare_tag( file, TAG_ADDRESS_INDEX ) ) {
   1461 				addrindex_convert_tree( addrIndex, file );
   1462 			}
   1463 		}
   1464 	}
   1465 	xml_close_file( file );
   1466 	return addrIndex->retVal;
   1467 }
   1468 
   1469 /*
   1470 * Create a new address book file.
   1471 */
   1472 static gboolean addrindex_create_new_book( AddressIndex *addrIndex, gchar *displayName ) {
   1473 	gboolean retVal = FALSE;
   1474 	AddressBookFile *abf = NULL;
   1475 	gchar *newFile = NULL;
   1476 	GList *fileList = NULL;
   1477 	gint fileNum = 0;
   1478 
   1479 	/* Create new address book */
   1480 	abf = addrbook_create_book();
   1481 	addrbook_set_name( abf, displayName );
   1482 	addrbook_set_path( abf, addrIndex->filePath );
   1483 
   1484 	/* Determine next available file number */
   1485 	fileList = addrbook_get_bookfile_list( abf );
   1486 	if( fileList ) {
   1487 		fileNum = 1 + abf->maxValue;
   1488 	}
   1489 	g_list_free( fileList );
   1490 	fileList = NULL;
   1491 
   1492 	newFile = addrbook_gen_new_file_name( fileNum );
   1493 	if( newFile ) {
   1494 		addrbook_set_file( abf, newFile );
   1495 	}
   1496 
   1497 	addrbook_save_data( abf );
   1498 	addrIndex->retVal = abf->retVal;
   1499 	if( abf->retVal == MGU_SUCCESS ) retVal = TRUE;
   1500 	addrbook_free_book( abf );
   1501 	abf = NULL;
   1502 
   1503 	/* Create entries in address index */
   1504 	if( retVal ) {
   1505 		abf = addrbook_create_book();
   1506 		addrbook_set_name( abf, displayName );
   1507 		addrbook_set_path( abf, addrIndex->filePath );
   1508 		addrbook_set_file( abf, newFile );
   1509 		addrindex_index_add_datasource( addrIndex, ADDR_IF_BOOK, abf );
   1510 	}
   1511 
   1512 	return retVal;
   1513 }
   1514 
   1515 /*
   1516 * Read data for address index performing a conversion if necesary.
   1517 * Enter: addrIndex Address index object.
   1518 * return: Status code, from addrIndex->retVal.
   1519 * Note: New address book files will be created in directory specified by
   1520 * addrIndex. Three files will be created, for the following:
   1521 *	"Common addresses"
   1522 *	"Personal addresses"
   1523 *	"Gathered addresses" - a new address book.
   1524 */
   1525 gint addrindex_read_data( AddressIndex *addrIndex ) {
   1526 	cm_return_val_if_fail( addrIndex != NULL, -1 );
   1527 
   1528 	addrIndex->conversionError = FALSE;
   1529 	addrindex_read_file( addrIndex );
   1530 	if( addrIndex->retVal == MGU_SUCCESS ) {
   1531 		if( addrIndex->needsConversion ) {
   1532 			if( addrindex_convert_data( addrIndex ) == MGU_SUCCESS )
   1533 				addrIndex->conversionError = FALSE;
   1534 			else
   1535 				addrIndex->conversionError = TRUE;
   1536 		}
   1537 		addrIndex->dirtyFlag = TRUE;
   1538 	}
   1539 	return addrIndex->retVal;
   1540 }
   1541 
   1542 /*
   1543 * Create new address books for a new address index.
   1544 * Enter: addrIndex Address index object.
   1545 * return: Status code, from addrIndex->retVal.
   1546 * Note: New address book files will be created in directory specified by
   1547 * addrIndex. Three files will be created, for the following:
   1548 *	"Common addresses"
   1549 *	"Personal addresses"
   1550 *	"Gathered addresses" - a new address book.
   1551 */
   1552 gint addrindex_create_new_books( AddressIndex *addrIndex ) {
   1553 	gboolean flg;
   1554 
   1555 	cm_return_val_if_fail( addrIndex != NULL, -1 );
   1556 
   1557 	flg = addrindex_create_new_book( addrIndex, DISP_NEW_COMMON );
   1558 	if( flg ) {
   1559 		flg = addrindex_create_new_book( addrIndex, DISP_NEW_PERSONAL );
   1560 		addrIndex->dirtyFlag = TRUE;
   1561 	}
   1562 	return addrIndex->retVal;
   1563 }
   1564 
   1565 /* **********************************************************************
   1566 * New interface stuff.
   1567 * ***********************************************************************
   1568 */
   1569 
   1570 /*
   1571  * Return modified flag for specified data source.
   1572  */
   1573 gboolean addrindex_ds_get_modify_flag( AddressDataSource *ds ) {
   1574 	gboolean retVal = FALSE;
   1575 	AddressInterface *iface;
   1576 
   1577 	if( ds == NULL ) return retVal;
   1578 	iface = ds->interface;
   1579 	if( iface == NULL ) return retVal;
   1580 	if( iface->getModifyFlag ) {
   1581 		retVal = ( iface->getModifyFlag ) ( ds->rawDataSource );
   1582 	}
   1583 	return retVal;
   1584 }
   1585 
   1586 /*
   1587  * Return accessed flag for specified data source.
   1588  */
   1589 gboolean addrindex_ds_get_access_flag( AddressDataSource *ds ) {
   1590 	gboolean retVal = FALSE;
   1591 	AddressInterface *iface;
   1592 
   1593 	if( ds == NULL ) return retVal;
   1594 	iface = ds->interface;
   1595 	if( iface == NULL ) return retVal;
   1596 	if( iface->getAccessFlag ) {
   1597 		retVal = ( iface->getAccessFlag ) ( ds->rawDataSource );
   1598 	}
   1599 	return retVal;
   1600 }
   1601 
   1602 /*
   1603  * Return data read flag for specified data source.
   1604  */
   1605 gboolean addrindex_ds_get_read_flag( AddressDataSource *ds ) {
   1606 	gboolean retVal = TRUE;
   1607 	AddressInterface *iface;
   1608 
   1609 	if( ds == NULL ) return retVal;
   1610 	iface = ds->interface;
   1611 	if( iface == NULL ) return retVal;
   1612 	if( iface->getReadFlag ) {
   1613 		retVal = ( iface->getReadFlag ) ( ds->rawDataSource );
   1614 	}
   1615 	return retVal;
   1616 }
   1617 
   1618 /*
   1619  * Return status code for specified data source.
   1620  */
   1621 gint addrindex_ds_get_status_code( AddressDataSource *ds ) {
   1622 	gint retVal = MGU_SUCCESS;
   1623 	AddressInterface *iface;
   1624 
   1625 	if( ds == NULL ) return retVal;
   1626 	iface = ds->interface;
   1627 	if( iface == NULL ) return retVal;
   1628 	if( iface->getStatusCode ) {
   1629 		retVal = ( iface->getStatusCode ) ( ds->rawDataSource );
   1630 	}
   1631 	return retVal;
   1632 }
   1633 
   1634 /*
   1635  * Return data read flag for specified data source.
   1636  */
   1637 gint addrindex_ds_read_data( AddressDataSource *ds ) {
   1638 	gint retVal = MGU_SUCCESS;
   1639 	AddressInterface *iface;
   1640 
   1641 	if( ds == NULL ) return retVal;
   1642 	iface = ds->interface;
   1643 	if( iface == NULL ) return retVal;
   1644 	if( iface->getReadData ) {
   1645 		/*
   1646 		gchar *name = ( iface->getName ) ( ds->rawDataSource );
   1647 		g_print( "addrindex_ds_read_data...reading:::%s:::\n", name );
   1648 		*/
   1649 		retVal = ( iface->getReadData ) ( ds->rawDataSource );
   1650 	}
   1651 	return retVal;
   1652 }
   1653 
   1654 /*
   1655  * Return data read flag for specified data source.
   1656  */
   1657 ItemFolder *addrindex_ds_get_root_folder( AddressDataSource *ds ) {
   1658 	ItemFolder *retVal = NULL;
   1659 	AddressInterface *iface;
   1660 
   1661 	if( ds == NULL ) return retVal;
   1662 	iface = ds->interface;
   1663 	if( iface == NULL ) return retVal;
   1664 	if( iface->getRootFolder ) {
   1665 		retVal = ( iface->getRootFolder ) ( ds->rawDataSource );
   1666 	}
   1667 	return retVal;
   1668 }
   1669 
   1670 /*
   1671  * Return name for specified data source.
   1672  */
   1673 gchar *addrindex_ds_get_name( AddressDataSource *ds ) {
   1674 	gchar *retVal = FALSE;
   1675 	AddressInterface *iface;
   1676 
   1677 	if( ds == NULL ) return retVal;
   1678 	iface = ds->interface;
   1679 	if( iface == NULL ) return retVal;
   1680 	if( iface->getName ) {
   1681 		retVal = ( iface->getName ) ( ds->rawDataSource );
   1682 	}
   1683 	return retVal;
   1684 }
   1685 
   1686 /*
   1687  * Set the access flag inside the data source.
   1688  */
   1689 void addrindex_ds_set_access_flag( AddressDataSource *ds, gboolean *value ) {
   1690 	AddressInterface *iface;
   1691 
   1692 	if( ds == NULL ) return;
   1693 	iface = ds->interface;
   1694 	if( iface == NULL ) return;
   1695 	if( iface->setAccessFlag ) {
   1696 		( iface->setAccessFlag ) ( ds->rawDataSource, value );
   1697 	}
   1698 }
   1699 
   1700 /*
   1701  * Return read only flag for specified data source.
   1702  */
   1703 gboolean addrindex_ds_get_readonly( AddressDataSource *ds ) {
   1704 	AddressInterface *iface;
   1705 	if( ds == NULL ) return TRUE;
   1706 	iface = ds->interface;
   1707 	if( iface == NULL ) return TRUE;
   1708 	return iface->readOnly;
   1709 }
   1710 
   1711 /*
   1712  * Return list of all persons for specified data source.
   1713  */
   1714 static GList *addrindex_ds_get_all_persons( AddressDataSource *ds ) {
   1715 	GList *retVal = NULL;
   1716 	AddressInterface *iface;
   1717 
   1718 	if( ds == NULL ) return retVal;
   1719 	iface = ds->interface;
   1720 	if( iface == NULL ) return retVal;
   1721 	if( iface->getAllPersons ) {
   1722 		retVal = ( iface->getAllPersons ) ( ds->rawDataSource );
   1723 	}
   1724 	return retVal;
   1725 }
   1726 
   1727 /*
   1728  * Return list of all groups for specified data source.
   1729  */
   1730 static GList *addrindex_ds_get_all_groups( AddressDataSource *ds ) {
   1731 	GList *retVal = NULL;
   1732 	AddressInterface *iface;
   1733 
   1734 	if( ds == NULL ) return retVal;
   1735 	iface = ds->interface;
   1736 	if( iface == NULL ) return retVal;
   1737 	if( iface->getAllGroups ) {
   1738 		retVal = ( iface->getAllGroups ) ( ds->rawDataSource );
   1739 	}
   1740 	return retVal;
   1741 }
   1742 
   1743 /* **********************************************************************
   1744 * Address search stuff.
   1745 * ***********************************************************************
   1746 */
   1747 
   1748 /**
   1749  * Setup or register the dynamic search that will be performed. The search
   1750  * is registered with the query manager.
   1751  *
   1752  * \param searchTerm    Search term. A private copy will be made.
   1753  * \param callBackEntry Callback function that should be called when
   1754  * 			each entry is received.
   1755  * \param callBackEnd   Callback function that should be called when
   1756  * 			search has finished running.
   1757  * \return ID allocated to query that will be executed.
   1758  */
   1759 gint addrindex_setup_search(
   1760 	const gchar *searchTerm, void *callBackEnd, void *callBackEntry )
   1761 {
   1762 	QueryRequest *req;
   1763 	gint queryID;
   1764 
   1765 	/* Set up a dynamic address query */
   1766 	req = qrymgr_add_request( searchTerm, callBackEnd, callBackEntry );
   1767 	queryID = req->queryID;
   1768 	qryreq_set_search_type( req, ADDRSEARCH_DYNAMIC );
   1769 
   1770 	/* g_print( "***> query ID ::%d::\n", queryID ); */
   1771 	return queryID;
   1772 }
   1773 
   1774 /**
   1775  * Execute the previously registered dynamic search.
   1776  *
   1777  * \param  req Address query object to execute.
   1778  * \return <i>TRUE</i> if search started successfully, or <i>FALSE</i> if
   1779  *         failed.
   1780  */
   1781 static gboolean addrindex_start_dynamic( QueryRequest *req ) {
   1782 	AddressInterface *iface;
   1783 	GList *nodeIf;
   1784 	GList *nodeDS;
   1785 
   1786 	nodeIf = _addressIndex_->searchOrder;
   1787 	while( nodeIf ) {
   1788 		iface = nodeIf->data;
   1789 		nodeIf = g_list_next( nodeIf );
   1790 
   1791 		if( ! iface->useInterface ) {
   1792 			continue;
   1793 		}
   1794 		if( ! iface->externalQuery ) {
   1795 			continue;
   1796 		}
   1797 
   1798 		nodeDS = iface->listSource;
   1799 		while( nodeDS ) {
   1800 			nodeDS = g_list_next( nodeDS );
   1801 		}
   1802 	}
   1803 	return TRUE;
   1804 }
   1805 
   1806 /**
   1807  * Stop the previously registered search.
   1808  *
   1809  * \param queryID ID of search query to stop.
   1810  */
   1811 void addrindex_stop_search( const gint queryID ){
   1812 	QueryRequest *req;
   1813 	AddrQueryObject *aqo;
   1814 	GList *node;
   1815 
   1816 	/* g_print( "addrindex_stop_search/queryID=%d\n", queryID ); */
   1817 	/* If query ID does not match, search has not been setup */
   1818 	req = qrymgr_find_request( queryID );
   1819 	if( req == NULL ) {
   1820 		return;
   1821 	}
   1822 
   1823 	/* Stop all queries that were associated with request */
   1824 	node = req->queryList;
   1825 	while( node ) {
   1826 		aqo = node->data;
   1827 		node->data = NULL;
   1828 		node = g_list_next( node );
   1829 	}
   1830 
   1831 	/* Delete query request */
   1832 	qrymgr_delete_request( queryID );
   1833 }
   1834 
   1835 /**
   1836  * Setup or register the explicit search that will be performed. The search is
   1837  * registered with the query manager.
   1838  *
   1839  * \param  ds            Data source to search.
   1840  * \param  searchTerm    Search term to locate.
   1841  * \param  folder        Folder to receive search results; may be NULL.
   1842  * \param  callbackEnd   Function to call when search has terminated.
   1843  * \param  callbackEntry Function to called for each entry processed.
   1844  * \return ID allocated to query that will be executed.
   1845  */
   1846 gint addrindex_setup_explicit_search(
   1847 	AddressDataSource *ds, const gchar *searchTerm, ItemFolder *folder,
   1848 	void *callBackEnd, void *callBackEntry )
   1849 {
   1850 	QueryRequest *req;
   1851 	gint queryID;
   1852 	gchar *name;
   1853 	gchar *mySearch;
   1854 
   1855 	/* Name the query */
   1856 	name = g_strdup_printf( "Search '%s'", searchTerm );
   1857 
   1858 	/* Set up query request */
   1859 	if (!strcmp(searchTerm, "*"))
   1860 		mySearch = g_strdup("*@");
   1861 	else
   1862 		mySearch = g_strdup(searchTerm);
   1863 
   1864 	req = qrymgr_add_request( mySearch, callBackEnd, callBackEntry );
   1865 
   1866 	g_free(mySearch);
   1867 
   1868 	qryreq_set_search_type( req, ADDRSEARCH_EXPLICIT );
   1869 	queryID = req->queryID;
   1870 
   1871 	qrymgr_delete_request( queryID );
   1872 	queryID = 0;
   1873 	g_free( name );
   1874 
   1875 	return queryID;
   1876 }
   1877 
   1878 /**
   1879  * Start the previously registered search.
   1880  *
   1881  * \param  queryID    ID of search query to be executed.
   1882  * \return <i>TRUE</i> if search started successfully, or <i>FALSE</i> if
   1883  *         failed.
   1884  */
   1885 gboolean addrindex_start_search( const gint queryID ) {
   1886 	gboolean retVal;
   1887 	QueryRequest *req;
   1888 	AddrSearchType searchType;
   1889 
   1890 	retVal = FALSE;
   1891 	/* g_print( "addrindex_start_search/queryID=%d\n", queryID ); */
   1892 	req = qrymgr_find_request( queryID );
   1893 	if( req == NULL ) {
   1894 		return retVal;
   1895 	}
   1896 
   1897 	searchType = req->searchType;
   1898 	if( searchType == ADDRSEARCH_DYNAMIC ) {
   1899 		retVal = addrindex_start_dynamic( req );
   1900 	}
   1901 	else if( searchType == ADDRSEARCH_EXPLICIT ) {
   1902 		retVal = FALSE;
   1903 	}
   1904 
   1905 	return retVal;
   1906 }
   1907 
   1908 /**
   1909  * Remove results (folder and data) for specified data source and folder.
   1910  * \param ds     Data source to process.
   1911  * \param folder Results folder to remove.
   1912  */
   1913 void addrindex_remove_results( AddressDataSource *ds, ItemFolder *folder ) {
   1914 	AddrBookBase *adbase;
   1915 	gint queryID = 0;
   1916 
   1917 	/* g_print( "addrindex_remove_results/start\n" ); */
   1918 
   1919 	/* Test for folder */
   1920 	if( folder->folderType != ADDRFOLDER_QUERY_RESULTS ) return;
   1921 	/* g_print( "folder name ::%s::\n", ADDRITEM_NAME(folder) ); */
   1922 	adbase = ( AddrBookBase * ) ds->rawDataSource;
   1923 	if( adbase == NULL ) return;
   1924 
   1925 	/* Hide folder to prevent re-display */
   1926 	addritem_folder_set_hidden( folder, TRUE );
   1927 
   1928 	/* Delete query request */
   1929 	if( queryID > 0 ) {
   1930 		qrymgr_delete_request( queryID );
   1931 	}
   1932 }
   1933 
   1934 /* **********************************************************************
   1935 * Address completion stuff.
   1936 * ***********************************************************************
   1937 */
   1938 
   1939 static void addrindex_load_completion_load_persons(
   1940 		gint (*callBackFunc) ( const gchar *, const gchar *,
   1941 				       const gchar *, const gchar *, GList * ),
   1942 		AddressDataSource *ds)
   1943 {
   1944 	GList *listP, *nodeP;
   1945 	GList *nodeM;
   1946 	gchar *sName;
   1947 
   1948 	/* Read address book */
   1949 	if( addrindex_ds_get_modify_flag( ds ) ) {
   1950 		addrindex_ds_read_data( ds );
   1951 	}
   1952 
   1953 	if( ! addrindex_ds_get_read_flag( ds ) ) {
   1954 		addrindex_ds_read_data( ds );
   1955 	}
   1956 
   1957 	/* Get all groups */
   1958 	listP = addrindex_ds_get_all_groups( ds );
   1959 	nodeP = listP;
   1960 	while( nodeP ) {
   1961 		ItemGroup *group = nodeP->data;
   1962 		GList *emails = NULL;
   1963 		for (nodeM = group->listEMail; nodeM; nodeM = g_list_next(nodeM)) {
   1964 			ItemEMail *email = nodeM->data;
   1965 			if (email->address)
   1966 				emails = g_list_append(emails, email);
   1967 		}
   1968 		callBackFunc( ((AddrItemObject *)group)->name, NULL,
   1969 			      NULL, NULL, emails );
   1970 		nodeP = g_list_next( nodeP );
   1971 	}
   1972 
   1973 	/* Free up the list */
   1974 	g_list_free( listP );
   1975 	/* Get all persons */
   1976 	listP = addrindex_ds_get_all_persons( ds );
   1977 	nodeP = listP;
   1978 	while( nodeP ) {
   1979 		ItemPerson *person = nodeP->data;
   1980 		nodeM = person->listEMail;
   1981 
   1982 		/* Figure out name to use */
   1983 		sName = ADDRITEM_NAME(person);
   1984 		if( sName == NULL || *sName == '\0' ) {
   1985 			sName = person->nickName;
   1986 		}
   1987 
   1988 		/* Process each E-Mail address */
   1989 		while( nodeM ) {
   1990 			ItemEMail *email = nodeM->data;
   1991 
   1992 			callBackFunc( sName, email->address, person->nickName,
   1993 				      ADDRITEM_NAME(email), NULL );
   1994 
   1995 			nodeM = g_list_next( nodeM );
   1996 		}
   1997 		nodeP = g_list_next( nodeP );
   1998 	}
   1999 
   2000 	/* Free up the list */
   2001 	g_list_free( listP );
   2002 }
   2003 
   2004 /**
   2005  * This function is used by the address completion function to load
   2006  * addresses for all non-external address book interfaces.
   2007  *
   2008  * \param callBackFunc Function to be called when an address is
   2009  *                     to be loaded.
   2010  * \param folderpath Addressbook's Book/folder path to restrict to (if NULL or ""
   2011  *                     or "Any", assume the whole addressbook
   2012  * \return <i>TRUE</i> if data loaded, <i>FALSE</i> if address index not loaded.
   2013  */
   2014 
   2015 gboolean addrindex_load_completion(
   2016 		gint (*callBackFunc) ( const gchar *, const gchar *,
   2017 				       const gchar *, const gchar *, GList * ),
   2018 		gchar *folderpath )
   2019 {
   2020 	GList *nodeIf, *nodeDS;
   2021 
   2022 	if( folderpath != NULL ) {
   2023 		AddressDataSource *book;
   2024 		ItemFolder* folder;
   2025 
   2026 		/* split the folder path we've received, we'll try to match this path, subpath by
   2027 		   subpath against the book/folder structure in order and restrict loading of
   2028 		   addresses to that subpart (if matches). book/folder path must exist and
   2029 		   folderpath must not be empty or NULL */
   2030 
   2031 		if( ! addressbook_peek_folder_exists( folderpath, &book, &folder ) ) {
   2032 			g_warning("addrindex_load_completion: folder path '%s' doesn't exist", folderpath);
   2033 			return FALSE;
   2034 		}
   2035 
   2036 		if( folder != NULL ) {
   2037 
   2038 			GList *items;
   2039 			GList *nodeM;
   2040 			gchar *sName;
   2041 			ItemPerson *person;
   2042 
   2043 			debug_print("addrindex_load_completion: folder %p '%s'\n", folder, folder->obj.name);
   2044 
   2045 			/* Load email addresses */
   2046 			items = addritem_folder_get_person_list( folder );
   2047 			for( ; items != NULL; items = g_list_next( items ) ) {
   2048 				person = items->data;
   2049 				nodeM = person->listEMail;
   2050 
   2051 				/* Figure out name to use */
   2052 				sName = ADDRITEM_NAME(person);
   2053 				if( sName == NULL || *sName == '\0' ) {
   2054 					sName = person->nickName;
   2055 				}
   2056 
   2057 				/* Process each E-Mail address */
   2058 				while( nodeM ) {
   2059 					ItemEMail *email = nodeM->data;
   2060 
   2061 					callBackFunc( sName, email->address, person->nickName,
   2062 							  ADDRITEM_NAME(email), NULL );
   2063 
   2064 					nodeM = g_list_next( nodeM );
   2065 				}
   2066 			}
   2067 			/* Free up the list (but not the data inside the
   2068 			 * individual list items) */
   2069 			g_list_free( items );
   2070 
   2071 			return TRUE;
   2072 
   2073 		} else {
   2074 
   2075 			if( book != NULL ) {
   2076 
   2077 				AddressBookFile *abf = book->rawDataSource;
   2078 
   2079 				debug_print("addrindex_load_completion: book %p '%s'\n", book, abf?abf->fileName:"(null)");
   2080 
   2081 				addrindex_load_completion_load_persons( callBackFunc, book );
   2082 
   2083 				return TRUE;
   2084 
   2085 			} else {
   2086 				g_warning("addrindex_load_completion: book/folder path is valid but got no pointer");
   2087 			}
   2088 		}
   2089 		return FALSE;
   2090 
   2091 	} else {
   2092 
   2093 		nodeIf = addrindex_get_interface_list( _addressIndex_ );
   2094 		while( nodeIf ) {
   2095 			AddressInterface *iface = nodeIf->data;
   2096 
   2097 			nodeIf = g_list_next( nodeIf );
   2098 
   2099 			if( ! iface->useInterface || iface->externalQuery )
   2100 				continue;
   2101 
   2102 			nodeDS = iface->listSource;
   2103 			while( nodeDS ) {
   2104 				addrindex_load_completion_load_persons( callBackFunc, nodeDS->data );
   2105 			nodeDS = g_list_next( nodeDS );
   2106 		}
   2107 	}
   2108 	}
   2109 
   2110 	return TRUE;
   2111 }
   2112 
   2113 /**
   2114  * This function can be used to collect information about
   2115  * addressbook entries that contain a specific attribute.
   2116  *
   2117  * \param attr         Name of attribute to look for
   2118  * \param callBackFunc Function to be called when a matching attribute was found
   2119  * \return <i>TRUE</i>
   2120  */
   2121 gboolean addrindex_load_person_attribute(
   2122 		const gchar *attr,
   2123 		gint (*callBackFunc) ( ItemPerson *, const gchar * ) )
   2124 {
   2125 	AddressDataSource *ds;
   2126 	GList *nodeIf, *nodeDS;
   2127 	GList *listP, *nodeP;
   2128 	GList *nodeA;
   2129 
   2130 	nodeIf = addrindex_get_interface_list( _addressIndex_ );
   2131 	while( nodeIf ) {
   2132 	        gchar *cur_bname;
   2133 		AddressInterface *iface = nodeIf->data;
   2134 
   2135 		nodeIf = g_list_next( nodeIf );
   2136 
   2137 		if( ! iface->useInterface || iface->externalQuery )
   2138 			continue;
   2139 
   2140 		nodeDS = iface->listSource;
   2141 		while( nodeDS ) {
   2142 			ds = nodeDS->data;
   2143 
   2144 			/* Read address book */
   2145 			if( addrindex_ds_get_modify_flag( ds ) ) {
   2146 				addrindex_ds_read_data( ds );
   2147 			}
   2148 
   2149 			if( ! addrindex_ds_get_read_flag( ds ) ) {
   2150 				addrindex_ds_read_data( ds );
   2151 			}
   2152 
   2153 			/* Check addressbook name */
   2154 			cur_bname = addrindex_ds_get_name( ds );
   2155 
   2156 			/* Get all persons */
   2157 			listP = addrindex_ds_get_all_persons( ds );
   2158 			nodeP = listP;
   2159 			while( nodeP ) {
   2160 				ItemPerson *person = nodeP->data;
   2161 
   2162 				/* Return all ItemPerson's if attr is NULL */
   2163 				if( attr == NULL ) {
   2164 					callBackFunc(person, cur_bname);
   2165 				}
   2166 
   2167 				/* Return ItemPerson's with specific attribute */
   2168 				else {
   2169 					nodeA = person->listAttrib;
   2170 					/* Process each User Attribute */
   2171 				        while( nodeA ) {
   2172 						UserAttribute *attrib = nodeA->data;
   2173 						if( attrib->name &&
   2174 						    !strcmp( attrib->name,attr ) ) {
   2175 							callBackFunc(person, cur_bname);
   2176 						}
   2177 						nodeA = g_list_next( nodeA );
   2178 					}
   2179 				}
   2180 				nodeP = g_list_next( nodeP );
   2181 			}
   2182 			/* Free up the list */
   2183 			g_list_free( listP );
   2184 
   2185 			nodeDS = g_list_next( nodeDS );
   2186 		}
   2187 	}
   2188 	return TRUE;
   2189 }
   2190 
   2191 /**
   2192  * This function can be used to collect information about
   2193  * addressbook entries
   2194  *
   2195  * \param callBackFunc Function to be called for each ItemPerson
   2196  * \return <i>TRUE</i>
   2197  */
   2198 gboolean addrindex_load_person_ds( gint (*callBackFunc)
   2199 			( ItemPerson *, AddressDataSource * ) )
   2200 {
   2201 	AddressDataSource *ds;
   2202 	GList *nodeIf, *nodeDS;
   2203 	GList *listP, *nodeP;
   2204 
   2205 	nodeIf = addrindex_get_interface_list( _addressIndex_ );
   2206 	while( nodeIf ) {
   2207 		AddressInterface *iface = nodeIf->data;
   2208 
   2209 		nodeIf = g_list_next( nodeIf );
   2210 
   2211 		if( ! iface->useInterface || iface->externalQuery )
   2212 			continue;
   2213 
   2214 		nodeDS = iface->listSource;
   2215 		while( nodeDS ) {
   2216 			ds = nodeDS->data;
   2217 
   2218 			/* Read address book */
   2219 			if( addrindex_ds_get_modify_flag( ds ) ) {
   2220 				addrindex_ds_read_data( ds );
   2221 			}
   2222 
   2223 			if( ! addrindex_ds_get_read_flag( ds ) ) {
   2224 				addrindex_ds_read_data( ds );
   2225 			}
   2226 
   2227 			/* Get all persons */
   2228 			listP = addrindex_ds_get_all_persons( ds );
   2229 			nodeP = listP;
   2230 			while( nodeP ) {
   2231 				ItemPerson *person = nodeP->data;
   2232 
   2233 				callBackFunc(person, ds);
   2234 				nodeP = g_list_next( nodeP );
   2235 			}
   2236 			/* Free up the list */
   2237 			g_list_free( listP );
   2238 
   2239 			nodeDS = g_list_next( nodeDS );
   2240 		}
   2241 	}
   2242 	return TRUE;
   2243 }
   2244 
   2245 gchar *addrindex_get_picture_file(const gchar *emailaddr)
   2246 {
   2247 	AddressDataSource *ds;
   2248 	GList *nodeIf, *nodeDS;
   2249 	GList *listP, *nodeP;
   2250 	gboolean found = FALSE;
   2251 	gchar *filename = NULL;
   2252 	gchar *raw_addr = NULL;
   2253 
   2254 	if (!emailaddr)
   2255 		return NULL;
   2256 
   2257 	Xstrdup_a(raw_addr, emailaddr, return NULL);
   2258 	extract_address(raw_addr);
   2259 
   2260 	nodeIf = addrindex_get_interface_list( _addressIndex_ );
   2261 	while( nodeIf ) {
   2262 		AddressInterface *iface = nodeIf->data;
   2263 
   2264 		nodeIf = g_list_next( nodeIf );
   2265 
   2266 		if( ! iface->useInterface || iface->externalQuery )
   2267 			continue;
   2268 
   2269 		nodeDS = iface->listSource;
   2270 		while( nodeDS && !found) {
   2271 			ds = nodeDS->data;
   2272 
   2273 			/* Read address book */
   2274 			if( addrindex_ds_get_modify_flag( ds ) ) {
   2275 				addrindex_ds_read_data( ds );
   2276 			}
   2277 
   2278 			if( ! addrindex_ds_get_read_flag( ds ) ) {
   2279 				addrindex_ds_read_data( ds );
   2280 			}
   2281 
   2282 			/* Get all persons */
   2283 			listP = addrindex_ds_get_all_persons( ds );
   2284 			nodeP = listP;
   2285 			while( nodeP && !found) {
   2286 				GList *nodeM;
   2287 				ItemPerson *person = nodeP->data;
   2288 				nodeM = person->listEMail;
   2289 				while(nodeM) {
   2290 					ItemEMail *email = nodeM->data;
   2291 					if (email->address && !strcasecmp(raw_addr, email->address)) {
   2292 						found = TRUE;
   2293 						filename = g_strconcat( get_rc_dir(), G_DIR_SEPARATOR_S,
   2294 							ADDRBOOK_DIR, G_DIR_SEPARATOR_S,
   2295 							person->picture, ".png", NULL );
   2296 						break;
   2297 					}
   2298 					nodeM = nodeM->next;
   2299 				}
   2300 				nodeP = g_list_next( nodeP );
   2301 			}
   2302 			/* Free up the list */
   2303 			g_list_free( listP );
   2304 
   2305 			nodeDS = g_list_next( nodeDS );
   2306 		}
   2307 	}
   2308 
   2309 	return filename;
   2310 }