2007-08-27 [colin] 2.10.0cvs179
[claws.git] / src / mutt.c
blob4aa4a2dec4c10c9fc09e05c8f03079153f7e461e
1 /*
2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2001-2007 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.
24 #include <sys/stat.h>
25 #include <ctype.h>
26 #include <string.h>
27 #include <glib.h>
29 #include "utils.h"
30 #include "mgutils.h"
31 #include "mutt.h"
32 #include "addritem.h"
33 #include "addrcache.h"
35 #define MUTT_HOME_FILE ".muttrc"
36 #define MUTTBUFSIZE 2048
37 #define MUTT_TAG_ALIAS "alias"
40 * Create new object.
42 MuttFile *mutt_create() {
43 MuttFile *muttFile;
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;
50 return muttFile;
54 * Properties...
56 void mutt_set_file( MuttFile* muttFile, const gchar *value ) {
57 g_return_if_fail( muttFile != NULL );
58 muttFile->path = mgu_replace_string( muttFile->path, value );
59 g_strstrip( muttFile->path );
63 * Free key in table.
65 static gint mutt_free_table_vis( gpointer key, gpointer value, gpointer data ) {
66 g_free( key );
67 key = NULL;
68 value = NULL;
69 return TRUE;
73 * Free up object by releasing internal memory.
75 void mutt_free( MuttFile *muttFile ) {
76 g_return_if_fail( muttFile != NULL );
78 /* Close file */
79 if( muttFile->file ) fclose( muttFile->file );
81 /* Free internal stuff */
82 g_free( muttFile->path );
84 /* Free unique address table */
85 g_hash_table_foreach_remove( muttFile->uniqTable, mutt_free_table_vis, NULL );
86 g_hash_table_destroy( muttFile->uniqTable );
88 /* Clear pointers */
89 muttFile->file = NULL;
90 muttFile->path = NULL;
91 muttFile->retVal = MGU_SUCCESS;
92 muttFile->uniqTable = NULL;
93 muttFile->cbProgress = NULL;
95 /* Now release file object */
96 g_free( muttFile );
100 * Open file for read.
101 * return: TRUE if file opened successfully.
103 static gint mutt_open_file( MuttFile* muttFile ) {
104 if( muttFile->path ) {
105 muttFile->file = g_fopen( muttFile->path, "rb" );
106 if( ! muttFile->file ) {
107 muttFile->retVal = MGU_OPEN_FILE;
108 return muttFile->retVal;
111 else {
112 /* g_print( "file not specified\n" ); */
113 muttFile->retVal = MGU_NO_FILE;
114 return muttFile->retVal;
117 /* Setup a buffer area */
118 muttFile->retVal = MGU_SUCCESS;
119 return muttFile->retVal;
123 * Close file.
125 static void mutt_close_file( MuttFile *muttFile ) {
126 g_return_if_fail( muttFile != NULL );
127 if( muttFile->file ) fclose( muttFile->file );
128 muttFile->file = NULL;
132 * Read line of text from file.
133 * Enter: muttFile File object.
134 * flagCont Continuation flag, set if back-slash character at EOL.
135 * Return: ptr to buffer where line starts.
137 static gchar *mutt_get_line( MuttFile *muttFile, gboolean *flagCont ) {
138 gchar buf[ MUTTBUFSIZE ];
139 int ch, lch;
140 int i = 0, li = 0;
142 *flagCont = FALSE;
143 if( feof( muttFile->file ) )
144 return NULL;
146 memset(buf, 0, MUTTBUFSIZE);
148 lch = '\0';
149 while( i < MUTTBUFSIZE-1 ) {
150 ch = fgetc( muttFile->file );
151 if( ch == '\0' || ch == EOF ) {
152 if( i == 0 )
153 return NULL;
154 break;
156 if( ch == '\n' ) {
157 if( lch == '\\' ) {
158 /* Replace backslash with NULL */
159 if( li != 0 )
160 buf[li] = '\0';
161 *flagCont = TRUE;
163 break;
165 buf[i] = ch;
166 li = i;
167 lch = ch;
168 i++;
170 buf[i]='\0';
172 /* Copy into private buffer */
173 return g_strdup( buf );
177 * Parsed address data.
179 typedef struct _Mutt_ParsedRec_ Mutt_ParsedRec;
180 struct _Mutt_ParsedRec_ {
181 gchar *address;
182 gchar *name;
186 * Free data record.
187 * Enter: rec Data record.
189 static void mutt_free_rec( Mutt_ParsedRec *rec ) {
190 if( rec ) {
191 g_free( rec->address );
192 g_free( rec->name );
193 rec->address = NULL;
194 rec->name = NULL;
195 g_free( rec );
200 * Parse recipient list for each address.
201 * Enter: rcpList Recipients extracted from file.
202 * addrCount Updated with recipient count.
203 * Return: Linked list of recipients.
205 static GSList *mutt_parse_rcplist( gchar *rcpList, gint *addrCount ) {
206 gchar *ptr, *pStart, *pEnd, *pAddr, *pName, *address, *name;
207 gchar ch;
208 GSList *list;
209 Mutt_ParsedRec *rec;
210 gint cnt;
212 list = NULL;
213 cnt = 0;
214 pStart = rcpList;
215 while( *pStart ) {
216 ptr = pStart;
217 address = name = NULL;
218 pName = pAddr = NULL;
219 /* Chew up spaces */
220 while( *ptr ) {
221 if( ! isspace( *ptr ) ) break;
222 ptr++;
225 /* Find address */
226 while( *ptr ) {
227 ch = *ptr;
228 if( ch == '(' ) {
229 pAddr = pName = ptr;
230 break;
232 if( ch == ',' ) {
233 pAddr = ptr;
234 ptr++;
235 break;
237 if( isspace( ch ) ) {
238 pAddr = ptr;
239 break;
241 ptr++;
244 /* Extract address */
245 if( pAddr ) {
246 address = g_strndup( pStart, pAddr - pStart );
248 else {
249 address = g_strdup( pStart );
251 g_strstrip( address );
253 /* Chew up spaces */
254 while( *ptr ) {
255 ch = *ptr;
256 if( ch == '(' ) {
257 pName = ptr;
258 break;
260 if( ch == ',' ) {
261 ptr++;
262 break;
264 ptr++;
265 if( isspace( ch ) ) continue;
267 pStart = ptr;
269 /* Extract name (if any) */
270 if( pName ) {
271 /* Look for closing parens */
272 pName++;
273 pEnd = NULL;
274 ptr = pName;
275 while( *ptr ) {
276 if( *ptr == ')' ) {
277 pEnd = ptr;
278 break;
280 ptr++;
282 if( pEnd ) {
283 name = g_strndup( pName, pEnd - pName );
284 pEnd++;
285 if( *pEnd ) pEnd++;
287 else {
288 name = g_strdup( pName );
290 g_strstrip( name );
291 pStart = pEnd;
293 else {
294 name = g_strdup( "" );
297 /* New record */
298 rec = g_new0( Mutt_ParsedRec, 1 );
299 rec->address = address;
300 rec->name = name;
301 list = g_slist_append( list, rec );
302 cnt++;
304 /* mutt_print_rec( rec, stdout ); */
306 *addrCount = cnt;
307 return list;
311 * Insert person and address into address cache.
312 * Enter: muttFile MUTT control data.
313 * cache Address cache.
314 * address E-Mail address.
315 * name Name.
316 * Return: E-Mail object, either inserted or found in hash table.
318 static ItemEMail *mutt_insert_table(
319 MuttFile *muttFile, AddressCache *cache, gchar *address,
320 gchar *name )
322 ItemPerson *person;
323 ItemEMail *email;
324 gchar *key;
326 /* Test whether address already in hash table */
327 key = g_strdup( address );
328 g_strdown( key );
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 );
344 /* Insert entry */
345 g_hash_table_insert( muttFile->uniqTable, key, email );
347 else {
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 );
354 /* Free up */
355 g_free( key );
358 return email;
362 * Build address book entries.
363 * Enter: muttFile MUTT control data.
364 * cache Address cache.
365 * aliasName Alias,
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 )
373 GSList *node = NULL;
374 ItemEMail *email;
375 ItemGroup *group;
376 Mutt_ParsedRec *rec;
378 group = NULL;
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 );
386 email = NULL;
387 node = listAddr;
388 while( node ) {
389 rec = node->data;
391 /* Insert person/email */
392 email = mutt_insert_table(
393 muttFile, cache, rec->address, rec->name );
395 /* Add email to group */
396 if( 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.
409 * line Data record.
411 static void mutt_build_items( MuttFile *muttFile, AddressCache *cache, gchar *line ) {
412 GList *list, *node;
413 gint tCount, aCount;
414 gchar *aliasTag, *aliasName, *recipient;
415 GSList *addrList;
417 /* g_print( "\nBUILD >%s<\n", line ); */
418 list = mgu_parse_string( line, 3, &tCount );
419 if( tCount < 3 ) {
420 if( list ) {
421 mgu_free_dlist( list );
422 list = NULL;
424 return;
427 aliasTag = list->data;
428 node = g_list_next( list );
429 aliasName = node->data;
430 node = g_list_next( node );
431 recipient = node->data;
433 addrList = NULL;
434 if( strcmp( aliasTag, MUTT_TAG_ALIAS ) == 0 ) {
435 aCount = 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 mgu_free_dlist( list );
444 list = NULL;
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;
457 long posEnd = 0L;
458 long posCur = 0L;
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 );
465 while( ! flagEOF ) {
466 flagCont = FALSE;
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;
476 if( ! lastCont ) {
477 /* Save data */
478 lineValue = mgu_list_coalesce( listValue );
479 if( lineValue ) {
480 mutt_build_items( muttFile, cache, lineValue );
482 g_free( lineValue );
483 lineValue = NULL;
484 mgu_free_list( listValue );
485 listValue = NULL;
487 lastCont = flagCont;
489 /* Add line to list */
490 listValue = g_slist_append( listValue, g_strdup( line ) );
492 g_free( line );
493 line = NULL;
496 /* Release data */
497 mgu_free_list( listValue );
498 listValue = NULL;
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 g_return_val_if_fail( muttFile != NULL, MGU_BAD_ARGS );
511 g_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 );
521 /* Mark cache */
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 ];
538 gint len;
539 FILE *fp;
541 homedir = get_home_dir();
542 if( ! homedir ) return g_strdup( "" );
544 strcpy( str, homedir );
545 len = strlen( str );
546 if( len > 0 ) {
547 if( str[ len-1 ] != G_DIR_SEPARATOR ) {
548 str[ len ] = G_DIR_SEPARATOR;
549 str[ ++len ] = '\0';
552 strcat( str, MUTT_HOME_FILE );
554 /* Attempt to open */
555 if( ( fp = g_fopen( str, "rb" ) ) != NULL ) {
556 fclose( fp );
558 else {
559 /* Truncate filename */
560 str[ len ] = '\0';
562 return g_strdup( str );
566 * End of Source.