remove unnecessary cm_return_val_if_fail()
[claws.git] / src / mutt.c
blob0747cf512d62ce79b3cdc8f909878d3e8ac8f8d5
1 /*
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.
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"
34 #include "file-utils.h"
36 #define MUTT_HOME_FILE ".muttrc"
37 #define MUTTBUFSIZE 2048
38 #define MUTT_TAG_ALIAS "alias"
41 * Create new object.
43 MuttFile *mutt_create() {
44 MuttFile *muttFile;
45 muttFile = g_new0( MuttFile, 1 );
46 muttFile->path = NULL;
47 muttFile->file = NULL;
48 muttFile->retVal = MGU_SUCCESS;
49 muttFile->uniqTable = g_hash_table_new( g_str_hash, g_str_equal );
50 muttFile->cbProgress = NULL;
51 return muttFile;
55 * Properties...
57 void mutt_set_file( MuttFile* muttFile, const gchar *value ) {
58 cm_return_if_fail( muttFile != NULL );
59 muttFile->path = mgu_replace_string( muttFile->path, value );
60 g_strstrip( muttFile->path );
64 * Free key in table.
66 static gint mutt_free_table_vis( gpointer key, gpointer value, gpointer data ) {
67 g_free( key );
68 return TRUE;
72 * Free up object by releasing internal memory.
74 void mutt_free( MuttFile *muttFile ) {
75 cm_return_if_fail( muttFile != NULL );
77 /* Close file */
78 if( muttFile->file ) claws_fclose( muttFile->file );
80 /* Free internal stuff */
81 g_free( muttFile->path );
83 /* Free unique address table */
84 g_hash_table_foreach_remove( muttFile->uniqTable, mutt_free_table_vis, NULL );
85 g_hash_table_destroy( muttFile->uniqTable );
87 /* Clear pointers */
88 muttFile->file = NULL;
89 muttFile->path = NULL;
90 muttFile->retVal = MGU_SUCCESS;
91 muttFile->uniqTable = NULL;
92 muttFile->cbProgress = NULL;
94 /* Now release file object */
95 g_free( muttFile );
99 * Open file for read.
100 * return: TRUE if file opened successfully.
102 static gint mutt_open_file( MuttFile* muttFile ) {
103 if( muttFile->path ) {
104 muttFile->file = claws_fopen( muttFile->path, "rb" );
105 if( ! muttFile->file ) {
106 muttFile->retVal = MGU_OPEN_FILE;
107 return muttFile->retVal;
110 else {
111 /* g_print( "file not specified\n" ); */
112 muttFile->retVal = MGU_NO_FILE;
113 return muttFile->retVal;
116 /* Setup a buffer area */
117 muttFile->retVal = MGU_SUCCESS;
118 return muttFile->retVal;
122 * Close file.
124 static void mutt_close_file( MuttFile *muttFile ) {
125 cm_return_if_fail( muttFile != NULL );
126 if( muttFile->file ) claws_fclose( muttFile->file );
127 muttFile->file = NULL;
131 * Read line of text from file.
132 * Enter: muttFile File object.
133 * flagCont Continuation flag, set if back-slash character at EOL.
134 * Return: ptr to buffer where line starts.
136 static gchar *mutt_get_line( MuttFile *muttFile, gboolean *flagCont ) {
137 gchar buf[ MUTTBUFSIZE ];
138 int ch, lch;
139 int i = 0, li = 0;
141 *flagCont = FALSE;
142 if( claws_feof( muttFile->file ) )
143 return NULL;
145 memset(buf, 0, MUTTBUFSIZE);
147 lch = '\0';
148 while( i < MUTTBUFSIZE-1 ) {
149 ch = fgetc( muttFile->file );
150 if( ch == '\0' || ch == EOF ) {
151 if( i == 0 )
152 return NULL;
153 break;
155 if( ch == '\n' ) {
156 if( lch == '\\' ) {
157 /* Replace backslash with NULL */
158 if( li != 0 )
159 buf[li] = '\0';
160 *flagCont = TRUE;
162 break;
164 buf[i] = ch;
165 li = i;
166 lch = ch;
167 i++;
169 buf[i]='\0';
171 /* Copy into private buffer */
172 return g_strdup( buf );
176 * Parsed address data.
178 typedef struct _Mutt_ParsedRec_ Mutt_ParsedRec;
179 struct _Mutt_ParsedRec_ {
180 gchar *address;
181 gchar *name;
185 * Free data record.
186 * Enter: rec Data record.
188 static void mutt_free_rec( Mutt_ParsedRec *rec ) {
189 if( rec ) {
190 g_free( rec->address );
191 g_free( rec->name );
192 rec->address = NULL;
193 rec->name = NULL;
194 g_free( rec );
199 * Parse recipient list for each address.
200 * Enter: rcpList Recipients extracted from file.
201 * addrCount Updated with recipient count.
202 * Return: Linked list of recipients.
204 static GSList *mutt_parse_rcplist( gchar *rcpList, gint *addrCount ) {
205 gchar *ptr, *pStart, *pEnd, *pAddr, *pName, *address, *name;
206 gchar ch;
207 GSList *list;
208 Mutt_ParsedRec *rec;
209 gint cnt;
211 list = NULL;
212 cnt = 0;
213 pStart = rcpList;
214 while( pStart && *pStart ) {
215 ptr = pStart;
216 address = NULL;
217 pName = pAddr = NULL;
218 /* Chew up spaces */
219 while( *ptr ) {
220 if( ! isspace( *ptr ) ) break;
221 ptr++;
224 /* Find address */
225 while( *ptr ) {
226 ch = *ptr;
227 if( ch == '(' ) {
228 pAddr = pName = ptr;
229 break;
231 if( ch == ',' ) {
232 pAddr = ptr;
233 ptr++;
234 break;
236 if( isspace( ch ) ) {
237 pAddr = ptr;
238 break;
240 ptr++;
243 /* Extract address */
244 if( pAddr ) {
245 address = g_strndup( pStart, pAddr - pStart );
247 else {
248 address = g_strdup( pStart );
250 g_strstrip( address );
252 /* Chew up spaces */
253 while( *ptr ) {
254 ch = *ptr;
255 if( ch == '(' ) {
256 pName = ptr;
257 break;
259 if( ch == ',' ) {
260 ptr++;
261 break;
263 ptr++;
264 if( isspace( ch ) ) continue;
266 pStart = ptr;
268 /* Extract name (if any) */
269 if( pName ) {
270 /* Look for closing parens */
271 pName++;
272 pEnd = NULL;
273 ptr = pName;
274 while( *ptr ) {
275 if( *ptr == ')' ) {
276 pEnd = ptr;
277 break;
279 ptr++;
281 if( pEnd ) {
282 name = g_strndup( pName, pEnd - pName );
283 pEnd++;
284 if( *pEnd ) pEnd++;
286 else {
287 name = g_strdup( pName );
289 g_strstrip( name );
290 pStart = pEnd;
292 else {
293 name = g_strdup( "" );
296 /* New record */
297 rec = g_new0( Mutt_ParsedRec, 1 );
298 rec->address = address;
299 rec->name = name;
300 list = g_slist_append( list, rec );
301 cnt++;
303 /* mutt_print_rec( rec, stdout ); */
305 *addrCount = cnt;
306 return list;
310 * Insert person and address into address cache.
311 * Enter: muttFile MUTT control data.
312 * cache Address cache.
313 * address E-Mail address.
314 * name Name.
315 * Return: E-Mail object, either inserted or found in hash table.
317 static ItemEMail *mutt_insert_table(
318 MuttFile *muttFile, AddressCache *cache, gchar *address,
319 gchar *name )
321 ItemPerson *person;
322 ItemEMail *email;
323 gchar *key;
325 /* Test whether address already in hash table */
326 key = g_utf8_strdown( address, -1 );
327 email = g_hash_table_lookup( muttFile->uniqTable, key );
329 if( email == NULL ) {
330 /* No - create person */
331 person = addritem_create_item_person();
332 addritem_person_set_common_name( person, name );
333 addrcache_id_person( cache, person );
334 addrcache_add_person( cache, person );
336 /* Add email for person */
337 email = addritem_create_item_email();
338 addritem_email_set_address( email, address );
339 addrcache_id_email( cache, email );
340 addrcache_person_add_email( cache, person, email );
342 /* Insert entry */
343 g_hash_table_insert( muttFile->uniqTable, key, email );
345 else {
346 /* Yes - update person with longest name */
347 person = ( ItemPerson * ) ADDRITEM_PARENT(email);
348 if( strlen( name ) > strlen( ADDRITEM_NAME(person) ) ) {
349 addritem_person_set_common_name( person, name );
352 /* Free up */
353 g_free( key );
356 return email;
360 * Build address book entries.
361 * Enter: muttFile MUTT control data.
362 * cache Address cache.
363 * aliasName Alias,
364 * listAddr List of address items.
365 * addrCount Address list count.
367 static void mutt_build_address(
368 MuttFile *muttFile, AddressCache *cache,
369 gchar *aliasName, GSList *listAddr, gint addrCount )
371 GSList *node = NULL;
372 ItemEMail *email;
373 ItemGroup *group;
374 Mutt_ParsedRec *rec;
376 group = NULL;
377 if( listAddr != NULL && addrCount > 1 ) {
378 group = addritem_create_item_group();
379 addritem_group_set_name( group, aliasName );
380 addrcache_id_group( cache, group );
381 addrcache_add_group( cache, group );
384 email = NULL;
385 node = listAddr;
386 while( node ) {
387 rec = node->data;
389 /* Insert person/email */
390 email = mutt_insert_table(
391 muttFile, cache, rec->address, rec->name );
393 /* Add email to group */
394 if( group ) {
395 addritem_group_add_email( group, email );
398 mutt_free_rec( rec );
399 node = g_slist_next( node );
404 * Parse address line adn build address items.
405 * Enter: muttFile MUTT control data.
406 * cache Address cache.
407 * line Data record.
409 static void mutt_build_items( MuttFile *muttFile, AddressCache *cache, gchar *line ) {
410 GList *list, *node;
411 gint tCount, aCount;
412 gchar *aliasTag, *aliasName, *recipient;
413 GSList *addrList;
415 /* g_print( "\nBUILD >%s<\n", line ); */
416 list = mgu_parse_string( line, 3, &tCount );
417 if( tCount < 3 ) {
418 if( list ) {
419 g_list_free_full( list, g_free );
420 list = NULL;
422 return;
425 aliasTag = list->data;
426 node = g_list_next( list );
427 aliasName = node->data;
428 node = g_list_next( node );
429 recipient = node->data;
431 addrList = NULL;
432 if( strcmp( aliasTag, MUTT_TAG_ALIAS ) == 0 ) {
433 aCount = 0;
434 /* g_print( "aliasName :%s:\n", aliasName ); */
435 /* g_print( "recipient :%s:\n", recipient ); */
436 addrList = mutt_parse_rcplist( recipient, &aCount );
437 /* g_print( "---\n" ); */
438 mutt_build_address( muttFile, cache, aliasName, addrList, aCount );
441 g_list_free_full( list, g_free );
442 list = NULL;
447 * Read file data into address cache.
448 * Enter: muttFile MUTT control data.
449 * cache Address cache.
451 static void mutt_read_file( MuttFile *muttFile, AddressCache *cache ) {
452 GSList *listValue = NULL;
453 gboolean flagEOF = FALSE, flagCont = FALSE, lastCont = FALSE;
454 gchar *line = NULL, *lineValue = NULL;
455 long posEnd = 0L;
456 long posCur = 0L;
458 /* Find EOF for progress indicator */
459 fseek( muttFile->file, 0L, SEEK_END );
460 posEnd = ftell( muttFile->file );
461 fseek( muttFile->file, 0L, SEEK_SET );
463 while( ! flagEOF ) {
464 flagCont = FALSE;
465 line = mutt_get_line( muttFile, &flagCont );
467 posCur = ftell( muttFile->file );
468 if( muttFile->cbProgress ) {
469 /* Call progress indicator */
470 ( muttFile->cbProgress ) ( muttFile, & posEnd, & posCur );
473 if( line == NULL ) flagEOF = TRUE;
474 if( ! lastCont ) {
475 /* Save data */
476 lineValue = mgu_list_coalesce( listValue );
477 if( lineValue ) {
478 mutt_build_items( muttFile, cache, lineValue );
480 g_free( lineValue );
481 lineValue = NULL;
482 g_slist_free_full( listValue, g_free );
483 listValue = NULL;
485 lastCont = flagCont;
487 /* Add line to list */
488 listValue = g_slist_append( listValue, g_strdup( line ) );
490 g_free( line );
491 line = NULL;
494 /* Release data */
495 g_slist_free_full( listValue, g_free );
496 listValue = NULL;
500 * ============================================================================================
501 * Read file into list. Main entry point
502 * Enter: muttFile MUTT control data.
503 * cache Address cache to load.
504 * Return: Status code.
505 * ============================================================================================
507 gint mutt_import_data( MuttFile *muttFile, AddressCache *cache ) {
508 cm_return_val_if_fail( muttFile != NULL, MGU_BAD_ARGS );
509 cm_return_val_if_fail( cache != NULL, MGU_BAD_ARGS );
510 muttFile->retVal = MGU_SUCCESS;
511 addrcache_clear( cache );
512 cache->dataRead = FALSE;
513 mutt_open_file( muttFile );
514 if( muttFile->retVal == MGU_SUCCESS ) {
515 /* Read data into the cache */
516 mutt_read_file( muttFile, cache );
517 mutt_close_file( muttFile );
519 /* Mark cache */
520 cache->modified = FALSE;
521 cache->dataRead = TRUE;
523 return muttFile->retVal;
526 #define WORK_BUFLEN 1024
529 * Attempt to find a Mutt file.
530 * Return: Filename, or home directory if not found, or empty string if
531 * no home. Filename should be g_free() when done.
533 gchar *mutt_find_file( void ) {
534 const gchar *homedir;
535 gchar str[ WORK_BUFLEN + 1 ];
536 gint len;
537 FILE *fp;
539 homedir = get_home_dir();
540 if( ! homedir ) return g_strdup( "" );
542 strncpy( str, homedir, WORK_BUFLEN );
543 len = strlen( str );
544 if( len > 0 ) {
545 if( str[ len-1 ] != G_DIR_SEPARATOR ) {
546 str[ len ] = G_DIR_SEPARATOR;
547 str[ ++len ] = '\0';
550 strncat( str, MUTT_HOME_FILE, WORK_BUFLEN - strlen(str) );
552 /* Attempt to open */
553 if( ( fp = claws_fopen( str, "rb" ) ) != NULL ) {
554 claws_fclose( fp );
556 else {
557 /* Truncate filename */
558 str[ len ] = '\0';
560 return g_strdup( str );
564 * End of Source.