2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2001-2012 Match Grun and the Claws Mail team
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.
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.
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/>.
21 * Functions necessary to access MUTT address book file.
33 #include "addrcache.h"
35 #define MUTT_HOME_FILE ".muttrc"
36 #define MUTTBUFSIZE 2048
37 #define MUTT_TAG_ALIAS "alias"
42 MuttFile
*mutt_create() {
44 muttFile
= g_new0( MuttFile
, 1 );
45 muttFile
->path
= NULL
;
46 muttFile
->file
= NULL
;
47 muttFile
->retVal
= MGU_SUCCESS
;
48 muttFile
->uniqTable
= g_hash_table_new( g_str_hash
, g_str_equal
);
49 muttFile
->cbProgress
= NULL
;
56 void mutt_set_file( MuttFile
* muttFile
, const gchar
*value
) {
57 cm_return_if_fail( muttFile
!= NULL
);
58 muttFile
->path
= mgu_replace_string( muttFile
->path
, value
);
59 g_strstrip( muttFile
->path
);
65 static gint
mutt_free_table_vis( gpointer key
, gpointer value
, gpointer data
) {
71 * Free up object by releasing internal memory.
73 void mutt_free( MuttFile
*muttFile
) {
74 cm_return_if_fail( muttFile
!= NULL
);
77 if( muttFile
->file
) fclose( muttFile
->file
);
79 /* Free internal stuff */
80 g_free( muttFile
->path
);
82 /* Free unique address table */
83 g_hash_table_foreach_remove( muttFile
->uniqTable
, mutt_free_table_vis
, NULL
);
84 g_hash_table_destroy( muttFile
->uniqTable
);
87 muttFile
->file
= NULL
;
88 muttFile
->path
= NULL
;
89 muttFile
->retVal
= MGU_SUCCESS
;
90 muttFile
->uniqTable
= NULL
;
91 muttFile
->cbProgress
= NULL
;
93 /* Now release file object */
99 * return: TRUE if file opened successfully.
101 static gint
mutt_open_file( MuttFile
* muttFile
) {
102 if( muttFile
->path
) {
103 muttFile
->file
= g_fopen( muttFile
->path
, "rb" );
104 if( ! muttFile
->file
) {
105 muttFile
->retVal
= MGU_OPEN_FILE
;
106 return muttFile
->retVal
;
110 /* g_print( "file not specified\n" ); */
111 muttFile
->retVal
= MGU_NO_FILE
;
112 return muttFile
->retVal
;
115 /* Setup a buffer area */
116 muttFile
->retVal
= MGU_SUCCESS
;
117 return muttFile
->retVal
;
123 static void mutt_close_file( MuttFile
*muttFile
) {
124 cm_return_if_fail( muttFile
!= NULL
);
125 if( muttFile
->file
) fclose( muttFile
->file
);
126 muttFile
->file
= NULL
;
130 * Read line of text from file.
131 * Enter: muttFile File object.
132 * flagCont Continuation flag, set if back-slash character at EOL.
133 * Return: ptr to buffer where line starts.
135 static gchar
*mutt_get_line( MuttFile
*muttFile
, gboolean
*flagCont
) {
136 gchar buf
[ MUTTBUFSIZE
];
141 if( feof( muttFile
->file
) )
144 memset(buf
, 0, MUTTBUFSIZE
);
147 while( i
< MUTTBUFSIZE
-1 ) {
148 ch
= fgetc( muttFile
->file
);
149 if( ch
== '\0' || ch
== EOF
) {
156 /* Replace backslash with NULL */
170 /* Copy into private buffer */
171 return g_strdup( buf
);
175 * Parsed address data.
177 typedef struct _Mutt_ParsedRec_ Mutt_ParsedRec
;
178 struct _Mutt_ParsedRec_
{
185 * Enter: rec Data record.
187 static void mutt_free_rec( Mutt_ParsedRec
*rec
) {
189 g_free( rec
->address
);
198 * Parse recipient list for each address.
199 * Enter: rcpList Recipients extracted from file.
200 * addrCount Updated with recipient count.
201 * Return: Linked list of recipients.
203 static GSList
*mutt_parse_rcplist( gchar
*rcpList
, gint
*addrCount
) {
204 gchar
*ptr
, *pStart
, *pEnd
, *pAddr
, *pName
, *address
, *name
;
213 while( pStart
&& *pStart
) {
216 pName
= pAddr
= NULL
;
219 if( ! isspace( *ptr
) ) break;
235 if( isspace( ch
) ) {
242 /* Extract address */
244 address
= g_strndup( pStart
, pAddr
- pStart
);
247 address
= g_strdup( pStart
);
249 g_strstrip( address
);
263 if( isspace( ch
) ) continue;
267 /* Extract name (if any) */
269 /* Look for closing parens */
281 name
= g_strndup( pName
, pEnd
- pName
);
286 name
= g_strdup( pName
);
292 name
= g_strdup( "" );
296 rec
= g_new0( Mutt_ParsedRec
, 1 );
297 rec
->address
= address
;
299 list
= g_slist_append( list
, rec
);
302 /* mutt_print_rec( rec, stdout ); */
309 * Insert person and address into address cache.
310 * Enter: muttFile MUTT control data.
311 * cache Address cache.
312 * address E-Mail address.
314 * Return: E-Mail object, either inserted or found in hash table.
316 static ItemEMail
*mutt_insert_table(
317 MuttFile
*muttFile
, AddressCache
*cache
, gchar
*address
,
324 /* Test whether address already in hash table */
325 key
= g_utf8_strdown( address
, -1 );
326 email
= g_hash_table_lookup( muttFile
->uniqTable
, key
);
328 if( email
== NULL
) {
329 /* No - create person */
330 person
= addritem_create_item_person();
331 addritem_person_set_common_name( person
, name
);
332 addrcache_id_person( cache
, person
);
333 addrcache_add_person( cache
, person
);
335 /* Add email for person */
336 email
= addritem_create_item_email();
337 addritem_email_set_address( email
, address
);
338 addrcache_id_email( cache
, email
);
339 addrcache_person_add_email( cache
, person
, email
);
342 g_hash_table_insert( muttFile
->uniqTable
, key
, email
);
345 /* Yes - update person with longest name */
346 person
= ( ItemPerson
* ) ADDRITEM_PARENT(email
);
347 if( strlen( name
) > strlen( ADDRITEM_NAME(person
) ) ) {
348 addritem_person_set_common_name( person
, name
);
359 * Build address book entries.
360 * Enter: muttFile MUTT control data.
361 * cache Address cache.
363 * listAddr List of address items.
364 * addrCount Address list count.
366 static void mutt_build_address(
367 MuttFile
*muttFile
, AddressCache
*cache
,
368 gchar
*aliasName
, GSList
*listAddr
, gint addrCount
)
376 if( listAddr
!= NULL
&& addrCount
> 1 ) {
377 group
= addritem_create_item_group();
378 addritem_group_set_name( group
, aliasName
);
379 addrcache_id_group( cache
, group
);
380 addrcache_add_group( cache
, group
);
388 /* Insert person/email */
389 email
= mutt_insert_table(
390 muttFile
, cache
, rec
->address
, rec
->name
);
392 /* Add email to group */
394 addritem_group_add_email( group
, email
);
397 mutt_free_rec( rec
);
398 node
= g_slist_next( node
);
403 * Parse address line adn build address items.
404 * Enter: muttFile MUTT control data.
405 * cache Address cache.
408 static void mutt_build_items( MuttFile
*muttFile
, AddressCache
*cache
, gchar
*line
) {
411 gchar
*aliasTag
, *aliasName
, *recipient
;
414 /* g_print( "\nBUILD >%s<\n", line ); */
415 list
= mgu_parse_string( line
, 3, &tCount
);
418 mgu_free_dlist( list
);
424 aliasTag
= list
->data
;
425 node
= g_list_next( list
);
426 aliasName
= node
->data
;
427 node
= g_list_next( node
);
428 recipient
= node
->data
;
431 if( strcmp( aliasTag
, MUTT_TAG_ALIAS
) == 0 ) {
433 /* g_print( "aliasName :%s:\n", aliasName ); */
434 /* g_print( "recipient :%s:\n", recipient ); */
435 addrList
= mutt_parse_rcplist( recipient
, &aCount
);
436 /* g_print( "---\n" ); */
437 mutt_build_address( muttFile
, cache
, aliasName
, addrList
, aCount
);
440 mgu_free_dlist( list
);
446 * Read file data into address cache.
447 * Enter: muttFile MUTT control data.
448 * cache Address cache.
450 static void mutt_read_file( MuttFile
*muttFile
, AddressCache
*cache
) {
451 GSList
*listValue
= NULL
;
452 gboolean flagEOF
= FALSE
, flagCont
= FALSE
, lastCont
= FALSE
;
453 gchar
*line
= NULL
, *lineValue
= NULL
;
457 /* Find EOF for progress indicator */
458 fseek( muttFile
->file
, 0L, SEEK_END
);
459 posEnd
= ftell( muttFile
->file
);
460 fseek( muttFile
->file
, 0L, SEEK_SET
);
464 line
= mutt_get_line( muttFile
, &flagCont
);
466 posCur
= ftell( muttFile
->file
);
467 if( muttFile
->cbProgress
) {
468 /* Call progress indicator */
469 ( muttFile
->cbProgress
) ( muttFile
, & posEnd
, & posCur
);
472 if( line
== NULL
) flagEOF
= TRUE
;
475 lineValue
= mgu_list_coalesce( listValue
);
477 mutt_build_items( muttFile
, cache
, lineValue
);
481 mgu_free_list( listValue
);
486 /* Add line to list */
487 listValue
= g_slist_append( listValue
, g_strdup( line
) );
494 mgu_free_list( listValue
);
499 * ============================================================================================
500 * Read file into list. Main entry point
501 * Enter: muttFile MUTT control data.
502 * cache Address cache to load.
503 * Return: Status code.
504 * ============================================================================================
506 gint
mutt_import_data( MuttFile
*muttFile
, AddressCache
*cache
) {
507 cm_return_val_if_fail( muttFile
!= NULL
, MGU_BAD_ARGS
);
508 cm_return_val_if_fail( cache
!= NULL
, MGU_BAD_ARGS
);
509 muttFile
->retVal
= MGU_SUCCESS
;
510 addrcache_clear( cache
);
511 cache
->dataRead
= FALSE
;
512 mutt_open_file( muttFile
);
513 if( muttFile
->retVal
== MGU_SUCCESS
) {
514 /* Read data into the cache */
515 mutt_read_file( muttFile
, cache
);
516 mutt_close_file( muttFile
);
519 cache
->modified
= FALSE
;
520 cache
->dataRead
= TRUE
;
522 return muttFile
->retVal
;
525 #define WORK_BUFLEN 1024
528 * Attempt to find a Mutt file.
529 * Return: Filename, or home directory if not found, or empty string if
530 * no home. Filename should be g_free() when done.
532 gchar
*mutt_find_file( void ) {
533 const gchar
*homedir
;
534 gchar str
[ WORK_BUFLEN
+ 1 ];
538 homedir
= get_home_dir();
539 if( ! homedir
) return g_strdup( "" );
541 strncpy( str
, homedir
, WORK_BUFLEN
);
544 if( str
[ len
-1 ] != G_DIR_SEPARATOR
) {
545 str
[ len
] = G_DIR_SEPARATOR
;
549 strncat( str
, MUTT_HOME_FILE
, WORK_BUFLEN
- strlen(str
) );
551 /* Attempt to open */
552 if( ( fp
= g_fopen( str
, "rb" ) ) != NULL
) {
556 /* Truncate filename */
559 return g_strdup( str
);