fgetc() returns an int which should never ever be put in a char.
[claws.git] / src / mutt.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2001 Match Grun
4  *
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 2 of the License, or
8  * (at your option) any later version.
9  *
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.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Functions necessary to access MUTT address book file.
22  */
23
24 #include <sys/stat.h>
25 #include <glib.h>
26
27 #include "mgutils.h"
28 #include "mutt.h"
29 #include "addritem.h"
30 #include "addrcache.h"
31
32 #define MUTT_HOME_FILE  ".muttrc"
33
34 /*
35 * Create new object.
36 */
37 MuttFile *mutt_create() {
38         MuttFile *muttFile;
39         muttFile = g_new0( MuttFile, 1 );
40         muttFile->path = NULL;
41         muttFile->file = NULL;
42         muttFile->bufptr = muttFile->buffer;
43         muttFile->retVal = MGU_SUCCESS;
44         muttFile->cbProgress = NULL;
45         return muttFile;
46 }
47
48 /*
49 * Properties...
50 */
51 void mutt_set_file( MuttFile* muttFile, const gchar *value ) {
52         g_return_if_fail( muttFile != NULL );
53         muttFile->path = mgu_replace_string( muttFile->path, value );
54         g_strstrip( muttFile->path );
55 }
56
57 /*
58 * Register a callback function. When called, the function will be passed
59 * the following arguments:
60 *       MuttFile object,
61 *       File size (long),
62 *       Current position (long)
63 * This can be used for a progress indicator.
64 */
65 void mutt_set_callback( MuttFile *muttFile, void *func ) {
66         muttFile->cbProgress = func;
67 }
68
69 /*
70 * Free up object by releasing internal memory.
71 */
72 void mutt_free( MuttFile *muttFile ) {
73         g_return_if_fail( muttFile != NULL );
74
75         /* Close file */
76         if( muttFile->file ) fclose( muttFile->file );
77
78         /* Free internal stuff */
79         g_free( muttFile->path );
80
81         /* Clear pointers */
82         muttFile->file = NULL;
83         muttFile->path = NULL;
84         muttFile->retVal = MGU_SUCCESS;
85         muttFile->cbProgress = NULL;
86
87         /* Now release file object */
88         g_free( muttFile );
89 }
90
91 /*
92 * Display object to specified stream.
93 */
94 void mutt_print_file( MuttFile *muttFile, FILE *stream ) {
95         g_return_if_fail( muttFile != NULL );
96         fprintf( stream, "MUTT File:\n" );
97         fprintf( stream, "file spec: '%s'\n", muttFile->path );
98         fprintf( stream, "  ret val: %d\n",   muttFile->retVal );
99 }
100
101 /*
102 * Open file for read.
103 * return: TRUE if file opened successfully.
104 */
105 static gint mutt_open_file( MuttFile* muttFile ) {
106         /* printf( "Opening file\n" ); */
107         if( muttFile->path ) {
108                 muttFile->file = fopen( muttFile->path, "r" );
109                 if( ! muttFile->file ) {
110                         /* printf( "can't open %s\n", muttFile->path ); */
111                         muttFile->retVal = MGU_OPEN_FILE;
112                         return muttFile->retVal;
113                 }
114         }
115         else {
116                 /* printf( "file not specified\n" ); */
117                 muttFile->retVal = MGU_NO_FILE;
118                 return muttFile->retVal;
119         }
120
121         /* Setup a buffer area */
122         muttFile->buffer[0] = '\0';
123         muttFile->bufptr = muttFile->buffer;
124         muttFile->retVal = MGU_SUCCESS;
125         return muttFile->retVal;
126 }
127
128 /*
129 * Close file.
130 */
131 static void mutt_close_file( MuttFile *muttFile ) {
132         g_return_if_fail( muttFile != NULL );
133         if( muttFile->file ) fclose( muttFile->file );
134         muttFile->file = NULL;
135 }
136
137 /*
138 * Read line of text from file.
139 * Enter: muttFile File object.
140 *        flagCont Continuation flag, set if back-slash character at EOL.
141 * Return: ptr to buffer where line starts.
142 */
143 static gchar *mutt_get_line( MuttFile *muttFile, gboolean *flagCont ) {
144         gchar buf[ MUTTBUFSIZE ];
145         int ch, lch;
146         gchar *ptr, *lptr;
147
148         *flagCont = FALSE;
149         if( feof( muttFile->file ) ) return NULL;
150
151         ptr = buf;
152         lch = '\0';
153         lptr = NULL;
154         while( TRUE ) {
155                 *ptr = '\0';
156                 ch = fgetc( muttFile->file );
157                 if( ch == '\0' || ch == EOF ) {
158                         if( *buf == '\0' ) return NULL;
159                         break;
160                 }
161                 if( ch == '\n' ) {
162                         if( lch == '\\' ) {
163                                 /* Replace backslash with NULL */
164                                 if( lptr ) *lptr = '\0';
165                                 *flagCont = TRUE;
166                         }
167                         break;
168                 }
169                 *ptr = ch;
170                 lptr = ptr;
171                 lch = ch;
172                 ptr++;
173         }
174
175         /* Copy into private buffer */
176         return g_strdup( buf );
177 }
178
179 /*
180 * Parsed address data.
181 */
182 typedef struct _Mutt_ParsedRec_ Mutt_ParsedRec;
183 struct _Mutt_ParsedRec_ {
184         gchar *address;
185         gchar *name;
186 };
187
188 static 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 );
195         }
196 }
197
198 void mutt_print_rec( Mutt_ParsedRec *rec, FILE *stream ) {
199         fprintf( stream, "\taddr: %s\tname: %s\n", rec->address, rec->name );
200 }
201
202 /*
203 * Parse recipient list for each address.
204 * Enter: rcpList   Recipients extracted from file.
205 *        addrCount Updated with recipient count.
206 * Return: Linked list of recipients.
207 */
208 static GSList *mutt_parse_rcplist( gchar *rcpList, gint *addrCount ) {
209         gchar *ptr, *pStart, *pEnd, *pAddr, *pName, *address, *name;
210         gchar ch;
211         GSList *list;
212         Mutt_ParsedRec *rec;
213         gint  cnt;
214
215         list = NULL;
216         cnt = 0;
217         pStart = rcpList;
218         while( *pStart ) {
219                 ptr = pStart;
220                 address = name = NULL;
221                 pName = pAddr = NULL;
222                 /* Chew up spaces */
223                 while( *ptr ) {
224                         if( ! isspace( *ptr ) ) break;
225                         ptr++;
226                 }
227
228                 /* Find address */
229                 while( *ptr ) {
230                         ch = *ptr;
231                         if( ch == '(' ) {
232                                 pAddr = pName = ptr;
233                                 break;
234                         }
235                         if( ch == ',' ) {
236                                 pAddr = ptr;
237                                 ptr++;
238                                 break;
239                         }
240                         if( isspace( ch ) ) {
241                                 pAddr = ptr;
242                                 break;
243                         }
244                         ptr++;
245                 }
246
247                 /* Extract address */
248                 if( pAddr ) {
249                         address = g_strndup( pStart, pAddr - pStart );
250                 }
251                 else {
252                         address = g_strdup( pStart );
253                 }
254                 g_strstrip( address );
255
256                 /* Chew up spaces */
257                 while( *ptr ) {
258                         ch = *ptr;
259                         if( ch == '(' ) {
260                                 pName = ptr;
261                                 break;
262                         }
263                         if( ch == ',' ) {
264                                 ptr++;
265                                 break;
266                         }
267                         ptr++;
268                         if( isspace( ch ) ) continue;
269                 }
270                 pStart = ptr;
271         
272                 /* Extract name (if any) */
273                 if( pName ) {
274                         /* Look for closing parens */
275                         pName++;
276                         pEnd = NULL;
277                         ptr = pName;
278                         while( *ptr ) {
279                                 if( *ptr == ')' ) {
280                                         pEnd = ptr;
281                                         break;
282                                 }
283                                 ptr++;
284                         }
285                         if( pEnd ) {
286                                 name = g_strndup( pName, pEnd - pName );
287                                 pEnd++;
288                                 if( *pEnd ) pEnd++;
289                         }
290                         else {
291                                 name = g_strdup( pName );
292                         }
293                         g_strstrip( name );
294                         pStart = pEnd;
295                 }
296                 else {
297                         name = g_strdup( "" );
298                 }
299
300                 /* New record */
301                 rec = g_new0( Mutt_ParsedRec, 1 );
302                 rec->address = address;
303                 rec->name = name;
304                 list = g_slist_append( list, rec );
305
306                 cnt++;
307
308                 /* mutt_print_rec( rec, stdout ); */
309         }
310         *addrCount = cnt;
311         return list;
312 }
313
314 /*
315 * Build address book entries.
316 * Enter: aliasName Alias,
317 *        listAddr  List of address items.
318 *        addrCount Address list count.
319 *        cache     Cache to update.
320 */
321 static void mutt_build_address(
322                 gchar *aliasName, GSList *listAddr, gint addrCount,
323                 AddressCache *cache )
324 {
325         GSList *node = NULL;
326         ItemPerson *person;
327         ItemEMail *email;
328         ItemGroup *group;
329         Mutt_ParsedRec *rec;
330
331         email = NULL;
332         group = NULL;
333         if( listAddr != NULL && addrCount > 1 ) {
334                 group = addritem_create_item_group();
335                 addritem_group_set_name( group, aliasName );
336                 addrcache_id_group( cache, group );
337                 addrcache_add_group( cache, group );
338         }
339
340         node = listAddr;
341         while( node ) {
342                 rec = node->data;
343
344                 /* Create person */
345                 person = addritem_create_item_person();
346                 addritem_person_set_common_name( person, rec->name );
347                 addrcache_id_person( cache, person );
348                 addrcache_add_person( cache, person );
349                 if( addrCount < 2 ) {
350                         addritem_person_set_nick_name( person, aliasName );
351                 }
352
353                 /* Add email for person */
354                 email = addritem_create_item_email();
355                 addritem_email_set_address( email, rec->address );
356                 addrcache_id_email( cache, email );
357                 addrcache_person_add_email( cache, person, email );
358
359                 /* Add email to group */
360                 if( group ) {
361                         addritem_group_add_email( group, email );
362                 }
363
364                 mutt_free_rec( rec );
365                 rec = NULL;
366
367                 node = g_slist_next( node );
368         }
369 }
370
371 /*
372 * Parse address line adn build address items.
373 * Enter: line  Data record.
374 *        cache Address cache.
375 */
376 static void mutt_build_items( gchar *line, AddressCache *cache ) {
377         GList *list, *node;
378         gint tCount, aCount;
379         gchar *aliasTag, *aliasName, *recipient;
380         GSList *addrList;
381
382         /* printf( "\nBUILD >%s<\n", line ); */
383         list = mgu_parse_string( line,  3, &tCount );
384         if( tCount < 3 ) {
385                 if( list ) {
386                         mgu_free_dlist( list );
387                         list = NULL;
388                 }
389                 return;
390         }
391
392         aliasTag = list->data;
393         node = g_list_next( list );
394         aliasName = node->data;
395         node = g_list_next( node );
396         recipient = node->data;
397
398         addrList = NULL;
399         if( strcmp( aliasTag, "alias" ) == 0 ) {
400                 aCount = 0;
401                 /* printf( "aliasName :%s:\n", aliasName ); */
402                 /* printf( "recipient :%s:\n", recipient ); */
403                 addrList = mutt_parse_rcplist( recipient, &aCount );
404                 /* printf( "---\n" ); */
405                 mutt_build_address( aliasName, addrList, aCount, cache );
406         }
407
408         mgu_free_dlist( list );
409         list = NULL;
410
411 }
412
413 /*
414 * Read file data into address cache.
415 */
416 static void mutt_read_file( MuttFile *muttFile, AddressCache *cache ) {
417         GSList *listValue = NULL;
418         gboolean flagEOF = FALSE, flagCont = FALSE, lastCont = FALSE;
419         gchar *line =  NULL, *lineValue = NULL;
420         long posEnd = 0L;
421         long posCur = 0L;
422
423         /* Find EOF for progress indicator */
424         fseek( muttFile->file, 0L, SEEK_END );
425         posEnd = ftell( muttFile->file );
426         fseek( muttFile->file, 0L, SEEK_SET );
427
428         while( ! flagEOF ) {
429                 flagCont = FALSE;
430                 line =  mutt_get_line( muttFile, &flagCont );
431
432                 posCur = ftell( muttFile->file );
433                 if( muttFile->cbProgress ) {
434                         /* Call progress indicator */
435                         ( muttFile->cbProgress ) ( muttFile, & posEnd, & posCur );
436                 }
437
438                 if( line == NULL ) flagEOF = TRUE;
439                 if( ! lastCont ) {
440                         /* Save data */
441                         lineValue = mgu_list_coalesce( listValue );
442                         if( lineValue ) {
443                                 mutt_build_items( lineValue, cache );
444                         }
445                         g_free( lineValue );
446                         lineValue = NULL;
447                         mgu_free_list( listValue );
448                         listValue = NULL;
449                 }
450                 lastCont = flagCont;
451
452                 /* Add line to list */
453                 listValue = g_slist_append( listValue, g_strdup( line ) );
454
455                 g_free( line );
456                 line = NULL;
457         }
458
459         /* Release data */
460         mgu_free_list( listValue );
461         listValue = NULL;
462 }
463
464 /*
465 * ============================================================================================
466 * Read file into list. Main entry point
467 * Enter:  muttFile MUTT control data.
468 *         cache    Address cache to load.
469 * Return: Status code.
470 * ============================================================================================
471 */
472 gint mutt_import_data( MuttFile *muttFile, AddressCache *cache ) {
473         g_return_val_if_fail( muttFile != NULL, MGU_BAD_ARGS );
474         g_return_val_if_fail( cache != NULL, MGU_BAD_ARGS );
475         muttFile->retVal = MGU_SUCCESS;
476         addrcache_clear( cache );
477         cache->dataRead = FALSE;
478         mutt_open_file( muttFile );
479         if( muttFile->retVal == MGU_SUCCESS ) {
480                 /* Read data into the cache */
481                 mutt_read_file( muttFile, cache );
482                 mutt_close_file( muttFile );
483
484                 /* Mark cache */
485                 cache->modified = FALSE;
486                 cache->dataRead = TRUE;
487         }
488         return muttFile->retVal;
489 }
490
491 #define WORK_BUFLEN 1024
492
493 /*
494 * Attempt to find a Mutt file.
495 * Return: Filename, or home directory if not found, or empty string if
496 * no home. Filename should be g_free() when done.
497 */
498 gchar *mutt_find_file( void ) {
499         gchar *homedir;
500         gchar str[ WORK_BUFLEN ];
501         gint len;
502         FILE *fp;
503
504         homedir = g_get_home_dir();
505         if( ! homedir ) return g_strdup( "" );
506
507         strcpy( str, homedir );
508         len = strlen( str );
509         if( len > 0 ) {
510                 if( str[ len-1 ] != G_DIR_SEPARATOR ) {
511                         str[ len ] = G_DIR_SEPARATOR;
512                         str[ ++len ] = '\0';
513                 }
514         }
515         strcat( str, MUTT_HOME_FILE );
516
517         /* Attempt to open */
518         if( ( fp = fopen( str, "r" ) ) != NULL ) {
519                 fclose( fp );
520         }
521         else {
522                 /* Truncate filename */
523                 str[ len ] = '\0';
524         }
525         return g_strdup( str );
526 }
527
528 /*
529 * End of Source.
530 */
531