/* * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client * Copyright (C) 2001 Match Grun * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * Functions necessary to access MUTT address book file. */ #include #include #include #include #include "mgutils.h" #include "mutt.h" #include "addritem.h" #include "addrcache.h" #define MUTT_HOME_FILE ".muttrc" #define MUTTBUFSIZE 2048 #define MUTT_TAG_ALIAS "alias" /* * Create new object. */ MuttFile *mutt_create() { MuttFile *muttFile; muttFile = g_new0( MuttFile, 1 ); muttFile->path = NULL; muttFile->file = NULL; muttFile->retVal = MGU_SUCCESS; muttFile->uniqTable = g_hash_table_new( g_str_hash, g_str_equal ); muttFile->cbProgress = NULL; return muttFile; } /* * Properties... */ void mutt_set_file( MuttFile* muttFile, const gchar *value ) { g_return_if_fail( muttFile != NULL ); muttFile->path = mgu_replace_string( muttFile->path, value ); g_strstrip( muttFile->path ); } /* * Register a callback function. When called, the function will be passed * the following arguments: * MuttFile object, * File size (long), * Current position (long) * This can be used for a progress indicator. */ void mutt_set_callback( MuttFile *muttFile, void *func ) { muttFile->cbProgress = func; } /* * Free key in table. */ static gint mutt_free_table_vis( gpointer key, gpointer value, gpointer data ) { g_free( key ); key = NULL; value = NULL; return TRUE; } /* * Free up object by releasing internal memory. */ void mutt_free( MuttFile *muttFile ) { g_return_if_fail( muttFile != NULL ); /* Close file */ if( muttFile->file ) fclose( muttFile->file ); /* Free internal stuff */ g_free( muttFile->path ); /* Free unique address table */ g_hash_table_freeze( muttFile->uniqTable ); g_hash_table_foreach_remove( muttFile->uniqTable, mutt_free_table_vis, NULL ); g_hash_table_thaw( muttFile->uniqTable ); g_hash_table_destroy( muttFile->uniqTable ); /* Clear pointers */ muttFile->file = NULL; muttFile->path = NULL; muttFile->retVal = MGU_SUCCESS; muttFile->uniqTable = NULL; muttFile->cbProgress = NULL; /* Now release file object */ g_free( muttFile ); } /* * Display object to specified stream. */ void mutt_print_file( MuttFile *muttFile, FILE *stream ) { g_return_if_fail( muttFile != NULL ); fprintf( stream, "MUTT File:\n" ); fprintf( stream, "file spec: '%s'\n", muttFile->path ); fprintf( stream, " ret val: %d\n", muttFile->retVal ); } /* * Open file for read. * return: TRUE if file opened successfully. */ static gint mutt_open_file( MuttFile* muttFile ) { if( muttFile->path ) { muttFile->file = fopen( muttFile->path, "rb" ); if( ! muttFile->file ) { muttFile->retVal = MGU_OPEN_FILE; return muttFile->retVal; } } else { /* printf( "file not specified\n" ); */ muttFile->retVal = MGU_NO_FILE; return muttFile->retVal; } /* Setup a buffer area */ muttFile->retVal = MGU_SUCCESS; return muttFile->retVal; } /* * Close file. */ static void mutt_close_file( MuttFile *muttFile ) { g_return_if_fail( muttFile != NULL ); if( muttFile->file ) fclose( muttFile->file ); muttFile->file = NULL; } /* * Read line of text from file. * Enter: muttFile File object. * flagCont Continuation flag, set if back-slash character at EOL. * Return: ptr to buffer where line starts. */ static gchar *mutt_get_line( MuttFile *muttFile, gboolean *flagCont ) { gchar buf[ MUTTBUFSIZE ]; int ch, lch; gchar *ptr, *lptr; *flagCont = FALSE; if( feof( muttFile->file ) ) return NULL; ptr = buf; lch = '\0'; lptr = NULL; while( TRUE ) { *ptr = '\0'; ch = fgetc( muttFile->file ); if( ch == '\0' || ch == EOF ) { if( *buf == '\0' ) return NULL; break; } if( ch == '\n' ) { if( lch == '\\' ) { /* Replace backslash with NULL */ if( lptr ) *lptr = '\0'; *flagCont = TRUE; } break; } *ptr = ch; lptr = ptr; lch = ch; ptr++; } /* Copy into private buffer */ return g_strdup( buf ); } /* * Parsed address data. */ typedef struct _Mutt_ParsedRec_ Mutt_ParsedRec; struct _Mutt_ParsedRec_ { gchar *address; gchar *name; }; /* * Free data record. * Enter: rec Data record. */ static void mutt_free_rec( Mutt_ParsedRec *rec ) { if( rec ) { g_free( rec->address ); g_free( rec->name ); rec->address = NULL; rec->name = NULL; g_free( rec ); } } /* * Print data record. * Enter: rec Data record. * stream File. */void mutt_print_rec( Mutt_ParsedRec *rec, FILE *stream ) { fprintf( stream, "\taddr: %s\tname: %s\n", rec->address, rec->name ); } /* * Parse recipient list for each address. * Enter: rcpList Recipients extracted from file. * addrCount Updated with recipient count. * Return: Linked list of recipients. */ static GSList *mutt_parse_rcplist( gchar *rcpList, gint *addrCount ) { gchar *ptr, *pStart, *pEnd, *pAddr, *pName, *address, *name; gchar ch; GSList *list; Mutt_ParsedRec *rec; gint cnt; list = NULL; cnt = 0; pStart = rcpList; while( *pStart ) { ptr = pStart; address = name = NULL; pName = pAddr = NULL; /* Chew up spaces */ while( *ptr ) { if( ! isspace( *ptr ) ) break; ptr++; } /* Find address */ while( *ptr ) { ch = *ptr; if( ch == '(' ) { pAddr = pName = ptr; break; } if( ch == ',' ) { pAddr = ptr; ptr++; break; } if( isspace( ch ) ) { pAddr = ptr; break; } ptr++; } /* Extract address */ if( pAddr ) { address = g_strndup( pStart, pAddr - pStart ); } else { address = g_strdup( pStart ); } g_strstrip( address ); /* Chew up spaces */ while( *ptr ) { ch = *ptr; if( ch == '(' ) { pName = ptr; break; } if( ch == ',' ) { ptr++; break; } ptr++; if( isspace( ch ) ) continue; } pStart = ptr; /* Extract name (if any) */ if( pName ) { /* Look for closing parens */ pName++; pEnd = NULL; ptr = pName; while( *ptr ) { if( *ptr == ')' ) { pEnd = ptr; break; } ptr++; } if( pEnd ) { name = g_strndup( pName, pEnd - pName ); pEnd++; if( *pEnd ) pEnd++; } else { name = g_strdup( pName ); } g_strstrip( name ); pStart = pEnd; } else { name = g_strdup( "" ); } /* New record */ rec = g_new0( Mutt_ParsedRec, 1 ); rec->address = address; rec->name = name; list = g_slist_append( list, rec ); cnt++; /* mutt_print_rec( rec, stdout ); */ } *addrCount = cnt; return list; } /* * Insert person and address into address cache. * Enter: muttFile MUTT control data. * cache Address cache. * address E-Mail address. * name Name. * Return: E-Mail object, either inserted or found in hash table. */ static ItemEMail *mutt_insert_table( MuttFile *muttFile, AddressCache *cache, gchar *address, gchar *name ) { ItemPerson *person; ItemEMail *email; gchar *key; /* Test whether address already in hash table */ key = g_strdup( address ); g_strdown( key ); email = g_hash_table_lookup( muttFile->uniqTable, key ); if( email == NULL ) { /* No - create person */ person = addritem_create_item_person(); addritem_person_set_common_name( person, name ); addrcache_id_person( cache, person ); addrcache_add_person( cache, person ); /* Add email for person */ email = addritem_create_item_email(); addritem_email_set_address( email, address ); addrcache_id_email( cache, email ); addrcache_person_add_email( cache, person, email ); /* Insert entry */ g_hash_table_insert( muttFile->uniqTable, key, email ); } else { /* Yes - update person with longest name */ person = ( ItemPerson * ) ADDRITEM_PARENT(email); if( strlen( name ) > strlen( ADDRITEM_NAME(person) ) ) { addritem_person_set_common_name( person, name ); } /* Free up */ g_free( key ); } return email; } /* * Build address book entries. * Enter: muttFile MUTT control data. * cache Address cache. * aliasName Alias, * listAddr List of address items. * addrCount Address list count. */ static void mutt_build_address( MuttFile *muttFile, AddressCache *cache, gchar *aliasName, GSList *listAddr, gint addrCount ) { GSList *node = NULL; ItemEMail *email; ItemGroup *group; Mutt_ParsedRec *rec; group = NULL; if( listAddr != NULL && addrCount > 1 ) { group = addritem_create_item_group(); addritem_group_set_name( group, aliasName ); addrcache_id_group( cache, group ); addrcache_add_group( cache, group ); } email = NULL; node = listAddr; while( node ) { rec = node->data; /* Insert person/email */ email = mutt_insert_table( muttFile, cache, rec->address, rec->name ); /* Add email to group */ if( group ) { addritem_group_add_email( group, email ); } mutt_free_rec( rec ); node = g_slist_next( node ); } } /* * Parse address line adn build address items. * Enter: muttFile MUTT control data. * cache Address cache. * line Data record. */ static void mutt_build_items( MuttFile *muttFile, AddressCache *cache, gchar *line ) { GList *list, *node; gint tCount, aCount; gchar *aliasTag, *aliasName, *recipient; GSList *addrList; /* printf( "\nBUILD >%s<\n", line ); */ list = mgu_parse_string( line, 3, &tCount ); if( tCount < 3 ) { if( list ) { mgu_free_dlist( list ); list = NULL; } return; } aliasTag = list->data; node = g_list_next( list ); aliasName = node->data; node = g_list_next( node ); recipient = node->data; addrList = NULL; if( strcmp( aliasTag, MUTT_TAG_ALIAS ) == 0 ) { aCount = 0; /* printf( "aliasName :%s:\n", aliasName ); */ /* printf( "recipient :%s:\n", recipient ); */ addrList = mutt_parse_rcplist( recipient, &aCount ); /* printf( "---\n" ); */ mutt_build_address( muttFile, cache, aliasName, addrList, aCount ); } mgu_free_dlist( list ); list = NULL; } /* * Read file data into address cache. * Enter: muttFile MUTT control data. * cache Address cache. */ static void mutt_read_file( MuttFile *muttFile, AddressCache *cache ) { GSList *listValue = NULL; gboolean flagEOF = FALSE, flagCont = FALSE, lastCont = FALSE; gchar *line = NULL, *lineValue = NULL; long posEnd = 0L; long posCur = 0L; /* Find EOF for progress indicator */ fseek( muttFile->file, 0L, SEEK_END ); posEnd = ftell( muttFile->file ); fseek( muttFile->file, 0L, SEEK_SET ); while( ! flagEOF ) { flagCont = FALSE; line = mutt_get_line( muttFile, &flagCont ); posCur = ftell( muttFile->file ); if( muttFile->cbProgress ) { /* Call progress indicator */ ( muttFile->cbProgress ) ( muttFile, & posEnd, & posCur ); } if( line == NULL ) flagEOF = TRUE; if( ! lastCont ) { /* Save data */ lineValue = mgu_list_coalesce( listValue ); if( lineValue ) { mutt_build_items( muttFile, cache, lineValue ); } g_free( lineValue ); lineValue = NULL; mgu_free_list( listValue ); listValue = NULL; } lastCont = flagCont; /* Add line to list */ listValue = g_slist_append( listValue, g_strdup( line ) ); g_free( line ); line = NULL; } /* Release data */ mgu_free_list( listValue ); listValue = NULL; } /* * ============================================================================================ * Read file into list. Main entry point * Enter: muttFile MUTT control data. * cache Address cache to load. * Return: Status code. * ============================================================================================ */ gint mutt_import_data( MuttFile *muttFile, AddressCache *cache ) { g_return_val_if_fail( muttFile != NULL, MGU_BAD_ARGS ); g_return_val_if_fail( cache != NULL, MGU_BAD_ARGS ); muttFile->retVal = MGU_SUCCESS; addrcache_clear( cache ); cache->dataRead = FALSE; mutt_open_file( muttFile ); if( muttFile->retVal == MGU_SUCCESS ) { /* Read data into the cache */ mutt_read_file( muttFile, cache ); mutt_close_file( muttFile ); /* Mark cache */ cache->modified = FALSE; cache->dataRead = TRUE; } return muttFile->retVal; } #define WORK_BUFLEN 1024 /* * Attempt to find a Mutt file. * Return: Filename, or home directory if not found, or empty string if * no home. Filename should be g_free() when done. */ gchar *mutt_find_file( void ) { gchar *homedir; gchar str[ WORK_BUFLEN ]; gint len; FILE *fp; homedir = g_get_home_dir(); if( ! homedir ) return g_strdup( "" ); strcpy( str, homedir ); len = strlen( str ); if( len > 0 ) { if( str[ len-1 ] != G_DIR_SEPARATOR ) { str[ len ] = G_DIR_SEPARATOR; str[ ++len ] = '\0'; } } strcat( str, MUTT_HOME_FILE ); /* Attempt to open */ if( ( fp = fopen( str, "rb" ) ) != NULL ) { fclose( fp ); } else { /* Truncate filename */ str[ len ] = '\0'; } return g_strdup( str ); } /* * End of Source. */