2 * Claws Mail -- 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.
35 #include "addrcache.h"
36 #include "file-utils.h"
38 #define MUTT_HOME_FILE ".muttrc"
39 #define MUTTBUFSIZE 2048
40 #define MUTT_TAG_ALIAS "alias"
45 MuttFile
*mutt_create() {
47 muttFile
= g_new0( MuttFile
, 1 );
48 muttFile
->path
= NULL
;
49 muttFile
->file
= NULL
;
50 muttFile
->retVal
= MGU_SUCCESS
;
51 muttFile
->uniqTable
= g_hash_table_new( g_str_hash
, g_str_equal
);
52 muttFile
->cbProgress
= NULL
;
59 void mutt_set_file( MuttFile
* muttFile
, const gchar
*value
) {
60 cm_return_if_fail( muttFile
!= NULL
);
61 muttFile
->path
= mgu_replace_string( muttFile
->path
, value
);
62 g_strstrip( muttFile
->path
);
68 static gint
mutt_free_table_vis( gpointer key
, gpointer value
, gpointer data
) {
74 * Free up object by releasing internal memory.
76 void mutt_free( MuttFile
*muttFile
) {
77 cm_return_if_fail( muttFile
!= NULL
);
80 if( muttFile
->file
) claws_fclose( muttFile
->file
);
82 /* Free internal stuff */
83 g_free( muttFile
->path
);
85 /* Free unique address table */
86 g_hash_table_foreach_remove( muttFile
->uniqTable
, mutt_free_table_vis
, NULL
);
87 g_hash_table_destroy( muttFile
->uniqTable
);
90 muttFile
->file
= NULL
;
91 muttFile
->path
= NULL
;
92 muttFile
->retVal
= MGU_SUCCESS
;
93 muttFile
->uniqTable
= NULL
;
94 muttFile
->cbProgress
= NULL
;
96 /* Now release file object */
101 * Open file for read.
102 * return: TRUE if file opened successfully.
104 static gint
mutt_open_file( MuttFile
* muttFile
) {
105 if( muttFile
->path
) {
106 muttFile
->file
= claws_fopen( muttFile
->path
, "rb" );
107 if( ! muttFile
->file
) {
108 muttFile
->retVal
= MGU_OPEN_FILE
;
109 return muttFile
->retVal
;
113 /* g_print( "file not specified\n" ); */
114 muttFile
->retVal
= MGU_NO_FILE
;
115 return muttFile
->retVal
;
118 /* Setup a buffer area */
119 muttFile
->retVal
= MGU_SUCCESS
;
120 return muttFile
->retVal
;
126 static void mutt_close_file( MuttFile
*muttFile
) {
127 cm_return_if_fail( muttFile
!= NULL
);
128 if( muttFile
->file
) claws_fclose( muttFile
->file
);
129 muttFile
->file
= NULL
;
133 * Read line of text from file.
134 * Enter: muttFile File object.
135 * flagCont Continuation flag, set if back-slash character at EOL.
136 * Return: ptr to buffer where line starts.
138 static gchar
*mutt_get_line( MuttFile
*muttFile
, gboolean
*flagCont
) {
139 gchar buf
[ MUTTBUFSIZE
];
144 if( claws_feof( muttFile
->file
) )
147 memset(buf
, 0, MUTTBUFSIZE
);
150 while( i
< MUTTBUFSIZE
-1 ) {
151 ch
= fgetc( muttFile
->file
);
152 if( ch
== '\0' || ch
== EOF
) {
159 /* Replace backslash with NULL */
173 /* Copy into private buffer */
174 return g_strdup( buf
);
178 * Parsed address data.
180 typedef struct _Mutt_ParsedRec_ Mutt_ParsedRec
;
181 struct _Mutt_ParsedRec_
{
188 * Enter: rec Data record.
190 static void mutt_free_rec( Mutt_ParsedRec
*rec
) {
192 g_free( rec
->address
);
201 * Parse recipient list for each address.
202 * Enter: rcpList Recipients extracted from file.
203 * addrCount Updated with recipient count.
204 * Return: Linked list of recipients.
206 static GSList
*mutt_parse_rcplist( gchar
*rcpList
, gint
*addrCount
) {
207 gchar
*ptr
, *pStart
, *pEnd
, *pAddr
, *pName
, *address
, *name
;
216 while( pStart
&& *pStart
) {
219 pName
= pAddr
= NULL
;
222 if( ! isspace( *ptr
) ) break;
238 if( isspace( ch
) ) {
245 /* Extract address */
247 address
= g_strndup( pStart
, pAddr
- pStart
);
250 address
= g_strdup( pStart
);
252 g_strstrip( address
);
266 if( isspace( ch
) ) continue;
270 /* Extract name (if any) */
272 /* Look for closing parens */
284 name
= g_strndup( pName
, pEnd
- pName
);
289 name
= g_strdup( pName
);
295 name
= g_strdup( "" );
299 rec
= g_new0( Mutt_ParsedRec
, 1 );
300 rec
->address
= address
;
302 list
= g_slist_append( list
, rec
);
305 /* mutt_print_rec( rec, stdout ); */
312 * Insert person and address into address cache.
313 * Enter: muttFile MUTT control data.
314 * cache Address cache.
315 * address E-Mail address.
317 * Return: E-Mail object, either inserted or found in hash table.
319 static ItemEMail
*mutt_insert_table(
320 MuttFile
*muttFile
, AddressCache
*cache
, gchar
*address
,
327 /* Test whether address already in hash table */
328 key
= g_utf8_strdown( address
, -1 );
329 email
= g_hash_table_lookup( muttFile
->uniqTable
, key
);
331 if( email
== NULL
) {
332 /* No - create person */
333 person
= addritem_create_item_person();
334 addritem_person_set_common_name( person
, name
);
335 addrcache_id_person( cache
, person
);
336 addrcache_add_person( cache
, person
);
338 /* Add email for person */
339 email
= addritem_create_item_email();
340 addritem_email_set_address( email
, address
);
341 addrcache_id_email( cache
, email
);
342 addrcache_person_add_email( cache
, person
, email
);
345 g_hash_table_insert( muttFile
->uniqTable
, key
, email
);
348 /* Yes - update person with longest name */
349 person
= ( ItemPerson
* ) ADDRITEM_PARENT(email
);
350 if( strlen( name
) > strlen( ADDRITEM_NAME(person
) ) ) {
351 addritem_person_set_common_name( person
, name
);
362 * Build address book entries.
363 * Enter: muttFile MUTT control data.
364 * cache Address cache.
366 * listAddr List of address items.
367 * addrCount Address list count.
369 static void mutt_build_address(
370 MuttFile
*muttFile
, AddressCache
*cache
,
371 gchar
*aliasName
, GSList
*listAddr
, gint addrCount
)
379 if( listAddr
!= NULL
&& addrCount
> 1 ) {
380 group
= addritem_create_item_group();
381 addritem_group_set_name( group
, aliasName
);
382 addrcache_id_group( cache
, group
);
383 addrcache_add_group( cache
, group
);
391 /* Insert person/email */
392 email
= mutt_insert_table(
393 muttFile
, cache
, rec
->address
, rec
->name
);
395 /* Add email to group */
397 addritem_group_add_email( group
, email
);
400 mutt_free_rec( rec
);
401 node
= g_slist_next( node
);
406 * Parse address line adn build address items.
407 * Enter: muttFile MUTT control data.
408 * cache Address cache.
411 static void mutt_build_items( MuttFile
*muttFile
, AddressCache
*cache
, gchar
*line
) {
414 gchar
*aliasTag
, *aliasName
, *recipient
;
417 /* g_print( "\nBUILD >%s<\n", line ); */
418 list
= mgu_parse_string( line
, 3, &tCount
);
421 g_list_free_full( list
, g_free
);
427 aliasTag
= list
->data
;
428 node
= g_list_next( list
);
429 aliasName
= node
->data
;
430 node
= g_list_next( node
);
431 recipient
= node
->data
;
434 if( strcmp( aliasTag
, MUTT_TAG_ALIAS
) == 0 ) {
436 /* g_print( "aliasName :%s:\n", aliasName ); */
437 /* g_print( "recipient :%s:\n", recipient ); */
438 addrList
= mutt_parse_rcplist( recipient
, &aCount
);
439 /* g_print( "---\n" ); */
440 mutt_build_address( muttFile
, cache
, aliasName
, addrList
, aCount
);
443 g_list_free_full( list
, g_free
);
449 * Read file data into address cache.
450 * Enter: muttFile MUTT control data.
451 * cache Address cache.
453 static void mutt_read_file( MuttFile
*muttFile
, AddressCache
*cache
) {
454 GSList
*listValue
= NULL
;
455 gboolean flagEOF
= FALSE
, flagCont
= FALSE
, lastCont
= FALSE
;
456 gchar
*line
= NULL
, *lineValue
= NULL
;
460 /* Find EOF for progress indicator */
461 fseek( muttFile
->file
, 0L, SEEK_END
);
462 posEnd
= ftell( muttFile
->file
);
463 fseek( muttFile
->file
, 0L, SEEK_SET
);
467 line
= mutt_get_line( muttFile
, &flagCont
);
469 posCur
= ftell( muttFile
->file
);
470 if( muttFile
->cbProgress
) {
471 /* Call progress indicator */
472 ( muttFile
->cbProgress
) ( muttFile
, & posEnd
, & posCur
);
475 if( line
== NULL
) flagEOF
= TRUE
;
478 lineValue
= mgu_list_coalesce( listValue
);
480 mutt_build_items( muttFile
, cache
, lineValue
);
484 g_slist_free_full( listValue
, g_free
);
489 /* Add line to list */
490 listValue
= g_slist_append( listValue
, g_strdup( line
) );
497 g_slist_free_full( listValue
, g_free
);
502 * ============================================================================================
503 * Read file into list. Main entry point
504 * Enter: muttFile MUTT control data.
505 * cache Address cache to load.
506 * Return: Status code.
507 * ============================================================================================
509 gint
mutt_import_data( MuttFile
*muttFile
, AddressCache
*cache
) {
510 cm_return_val_if_fail( muttFile
!= NULL
, MGU_BAD_ARGS
);
511 cm_return_val_if_fail( cache
!= NULL
, MGU_BAD_ARGS
);
512 muttFile
->retVal
= MGU_SUCCESS
;
513 addrcache_clear( cache
);
514 cache
->dataRead
= FALSE
;
515 mutt_open_file( muttFile
);
516 if( muttFile
->retVal
== MGU_SUCCESS
) {
517 /* Read data into the cache */
518 mutt_read_file( muttFile
, cache
);
519 mutt_close_file( muttFile
);
522 cache
->modified
= FALSE
;
523 cache
->dataRead
= TRUE
;
525 return muttFile
->retVal
;
528 #define WORK_BUFLEN 1024
531 * Attempt to find a Mutt file.
532 * Return: Filename, or home directory if not found, or empty string if
533 * no home. Filename should be g_free() when done.
535 gchar
*mutt_find_file( void ) {
536 const gchar
*homedir
;
537 gchar str
[ WORK_BUFLEN
+ 1 ];
541 homedir
= get_home_dir();
542 if( ! homedir
) return g_strdup( "" );
544 strncpy( str
, homedir
, WORK_BUFLEN
);
547 if( str
[ len
-1 ] != G_DIR_SEPARATOR
) {
548 str
[ len
] = G_DIR_SEPARATOR
;
552 strncat( str
, MUTT_HOME_FILE
, WORK_BUFLEN
- strlen(str
) );
554 /* Attempt to open */
555 if( ( fp
= claws_fopen( str
, "rb" ) ) != NULL
) {
559 /* Truncate filename */
562 return g_strdup( str
);