RSSyl: preferences for HTTP auth basic
[claws.git] / src / mutt.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2001-2012 Match Grun and the Claws Mail team
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 3 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, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 /*
21  * Functions necessary to access MUTT address book file.
22  */
23
24 #include <sys/stat.h>
25 #include <ctype.h>
26 #include <string.h>
27 #include <glib.h>
28
29 #include "utils.h"
30 #include "mgutils.h"
31 #include "mutt.h"
32 #include "addritem.h"
33 #include "addrcache.h"
34
35 #define MUTT_HOME_FILE  ".muttrc"
36 #define MUTTBUFSIZE     2048
37 #define MUTT_TAG_ALIAS  "alias"
38
39 /*
40 * Create new object.
41 */
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;
51 }
52
53 /*
54 * Properties...
55 */
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 );
60 }
61
62 /*
63  * Free key in table.
64  */
65 static gint mutt_free_table_vis( gpointer key, gpointer value, gpointer data ) {
66         g_free( key );
67         return TRUE;
68 }
69
70 /*
71 * Free up object by releasing internal memory.
72 */
73 void mutt_free( MuttFile *muttFile ) {
74         cm_return_if_fail( muttFile != NULL );
75
76         /* Close file */
77         if( muttFile->file ) fclose( muttFile->file );
78
79         /* Free internal stuff */
80         g_free( muttFile->path );
81
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 );
85
86         /* Clear pointers */
87         muttFile->file = NULL;
88         muttFile->path = NULL;
89         muttFile->retVal = MGU_SUCCESS;
90         muttFile->uniqTable = NULL;
91         muttFile->cbProgress = NULL;
92
93         /* Now release file object */
94         g_free( muttFile );
95 }
96
97 /*
98 * Open file for read.
99 * return: TRUE if file opened successfully.
100 */
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;
107                 }
108         }
109         else {
110                 /* g_print( "file not specified\n" ); */
111                 muttFile->retVal = MGU_NO_FILE;
112                 return muttFile->retVal;
113         }
114
115         /* Setup a buffer area */
116         muttFile->retVal = MGU_SUCCESS;
117         return muttFile->retVal;
118 }
119
120 /*
121 * Close file.
122 */
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;
127 }
128
129 /*
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.
134 */
135 static gchar *mutt_get_line( MuttFile *muttFile, gboolean *flagCont ) {
136         gchar buf[ MUTTBUFSIZE ];
137         int ch, lch;
138         int i = 0, li = 0;
139
140         *flagCont = FALSE;
141         if( feof( muttFile->file ) ) 
142                 return NULL;
143
144         memset(buf, 0, MUTTBUFSIZE);
145
146         lch = '\0';
147         while( i < MUTTBUFSIZE-1 ) {
148                 ch = fgetc( muttFile->file );
149                 if( ch == '\0' || ch == EOF ) {
150                         if( i == 0 ) 
151                                 return NULL;
152                         break;
153                 }
154                 if( ch == '\n' ) {
155                         if( lch == '\\' ) {
156                                 /* Replace backslash with NULL */
157                                 if( li != 0 ) 
158                                         buf[li] = '\0';
159                                 *flagCont = TRUE;
160                         }
161                         break;
162                 }
163                 buf[i] = ch;
164                 li = i;
165                 lch = ch;
166                 i++;
167         }
168         buf[i]='\0';
169
170         /* Copy into private buffer */
171         return g_strdup( buf );
172 }
173
174 /*
175  * Parsed address data.
176  */
177 typedef struct _Mutt_ParsedRec_ Mutt_ParsedRec;
178 struct _Mutt_ParsedRec_ {
179         gchar *address;
180         gchar *name;
181 };
182
183 /*
184  * Free data record.
185  * Enter: rec Data record.
186  */
187 static void mutt_free_rec( Mutt_ParsedRec *rec ) {
188         if( rec ) {
189                 g_free( rec->address );
190                 g_free( rec->name );
191                 rec->address = NULL;
192                 rec->name = NULL;
193                 g_free( rec );
194         }
195 }
196
197 /*
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.
202 */
203 static GSList *mutt_parse_rcplist( gchar *rcpList, gint *addrCount ) {
204         gchar *ptr, *pStart, *pEnd, *pAddr, *pName, *address, *name;
205         gchar ch;
206         GSList *list;
207         Mutt_ParsedRec *rec;
208         gint  cnt;
209
210         list = NULL;
211         cnt = 0;
212         pStart = rcpList;
213         while( pStart && *pStart ) {
214                 ptr = pStart;
215                 address = NULL;
216                 pName = pAddr = NULL;
217                 /* Chew up spaces */
218                 while( *ptr ) {
219                         if( ! isspace( *ptr ) ) break;
220                         ptr++;
221                 }
222
223                 /* Find address */
224                 while( *ptr ) {
225                         ch = *ptr;
226                         if( ch == '(' ) {
227                                 pAddr = pName = ptr;
228                                 break;
229                         }
230                         if( ch == ',' ) {
231                                 pAddr = ptr;
232                                 ptr++;
233                                 break;
234                         }
235                         if( isspace( ch ) ) {
236                                 pAddr = ptr;
237                                 break;
238                         }
239                         ptr++;
240                 }
241
242                 /* Extract address */
243                 if( pAddr ) {
244                         address = g_strndup( pStart, pAddr - pStart );
245                 }
246                 else {
247                         address = g_strdup( pStart );
248                 }
249                 g_strstrip( address );
250
251                 /* Chew up spaces */
252                 while( *ptr ) {
253                         ch = *ptr;
254                         if( ch == '(' ) {
255                                 pName = ptr;
256                                 break;
257                         }
258                         if( ch == ',' ) {
259                                 ptr++;
260                                 break;
261                         }
262                         ptr++;
263                         if( isspace( ch ) ) continue;
264                 }
265                 pStart = ptr;
266         
267                 /* Extract name (if any) */
268                 if( pName ) {
269                         /* Look for closing parens */
270                         pName++;
271                         pEnd = NULL;
272                         ptr = pName;
273                         while( *ptr ) {
274                                 if( *ptr == ')' ) {
275                                         pEnd = ptr;
276                                         break;
277                                 }
278                                 ptr++;
279                         }
280                         if( pEnd ) {
281                                 name = g_strndup( pName, pEnd - pName );
282                                 pEnd++;
283                                 if( *pEnd ) pEnd++;
284                         }
285                         else {
286                                 name = g_strdup( pName );
287                         }
288                         g_strstrip( name );
289                         pStart = pEnd;
290                 }
291                 else {
292                         name = g_strdup( "" );
293                 }
294
295                 /* New record */
296                 rec = g_new0( Mutt_ParsedRec, 1 );
297                 rec->address = address;
298                 rec->name = name;
299                 list = g_slist_append( list, rec );
300                 cnt++;
301
302                 /* mutt_print_rec( rec, stdout ); */
303         }
304         *addrCount = cnt;
305         return list;
306 }
307
308 /*
309  * Insert person and address into address cache.
310  * Enter: muttFile MUTT control data.
311  *        cache    Address cache.
312  *        address  E-Mail address.
313  *        name     Name.
314  * Return: E-Mail object, either inserted or found in hash table.
315  */
316 static ItemEMail *mutt_insert_table(
317                 MuttFile *muttFile, AddressCache *cache, gchar *address,
318                 gchar *name )
319 {
320         ItemPerson *person;
321         ItemEMail *email;
322         gchar *key;
323
324         /* Test whether address already in hash table */
325         key = g_utf8_strdown( address, -1 );
326         email = g_hash_table_lookup( muttFile->uniqTable, key );
327
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 );
334
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 );
340
341                 /* Insert entry */
342                 g_hash_table_insert( muttFile->uniqTable, key, email );
343         }
344         else {
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 );
349                 }
350
351                 /* Free up */
352                 g_free( key );
353         }
354
355         return email;
356 }
357
358 /*
359  * Build address book entries.
360  * Enter: muttFile  MUTT control data.
361  *        cache     Address cache.
362  *        aliasName Alias,
363  *        listAddr  List of address items.
364  *        addrCount Address list count.
365  */
366 static void mutt_build_address(
367                 MuttFile *muttFile, AddressCache *cache,
368                 gchar *aliasName, GSList *listAddr, gint addrCount )
369 {
370         GSList *node = NULL;
371         ItemEMail *email;
372         ItemGroup *group;
373         Mutt_ParsedRec *rec;
374
375         group = NULL;
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 );
381         }
382
383         email = NULL;
384         node = listAddr;
385         while( node ) {
386                 rec = node->data;
387
388                 /* Insert person/email */
389                 email = mutt_insert_table(
390                                 muttFile, cache, rec->address, rec->name );
391
392                 /* Add email to group */
393                 if( group ) {
394                         addritem_group_add_email( group, email );
395                 }
396
397                 mutt_free_rec( rec );
398                 node = g_slist_next( node );
399         }
400 }
401
402 /*
403  * Parse address line adn build address items.
404  * Enter: muttFile MUTT control data.
405  *        cache    Address cache.
406  *        line     Data record.
407  */
408 static void mutt_build_items( MuttFile *muttFile, AddressCache *cache, gchar *line ) {
409         GList *list, *node;
410         gint tCount, aCount;
411         gchar *aliasTag, *aliasName, *recipient;
412         GSList *addrList;
413
414         /* g_print( "\nBUILD >%s<\n", line ); */
415         list = mgu_parse_string( line,  3, &tCount );
416         if( tCount < 3 ) {
417                 if( list ) {
418                         mgu_free_dlist( list );
419                         list = NULL;
420                 }
421                 return;
422         }
423
424         aliasTag = list->data;
425         node = g_list_next( list );
426         aliasName = node->data;
427         node = g_list_next( node );
428         recipient = node->data;
429
430         addrList = NULL;
431         if( strcmp( aliasTag, MUTT_TAG_ALIAS ) == 0 ) {
432                 aCount = 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 );
438         }
439
440         mgu_free_dlist( list );
441         list = NULL;
442
443 }
444
445 /*
446  * Read file data into address cache.
447  * Enter: muttFile MUTT control data.
448  *        cache Address cache.
449  */
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;
454         long posEnd = 0L;
455         long posCur = 0L;
456
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 );
461
462         while( ! flagEOF ) {
463                 flagCont = FALSE;
464                 line =  mutt_get_line( muttFile, &flagCont );
465
466                 posCur = ftell( muttFile->file );
467                 if( muttFile->cbProgress ) {
468                         /* Call progress indicator */
469                         ( muttFile->cbProgress ) ( muttFile, & posEnd, & posCur );
470                 }
471
472                 if( line == NULL ) flagEOF = TRUE;
473                 if( ! lastCont ) {
474                         /* Save data */
475                         lineValue = mgu_list_coalesce( listValue );
476                         if( lineValue ) {
477                                 mutt_build_items( muttFile, cache, lineValue );
478                         }
479                         g_free( lineValue );
480                         lineValue = NULL;
481                         mgu_free_list( listValue );
482                         listValue = NULL;
483                 }
484                 lastCont = flagCont;
485
486                 /* Add line to list */
487                 listValue = g_slist_append( listValue, g_strdup( line ) );
488
489                 g_free( line );
490                 line = NULL;
491         }
492
493         /* Release data */
494         mgu_free_list( listValue );
495         listValue = NULL;
496 }
497
498 /*
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 * ============================================================================================
505 */
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 );
517
518                 /* Mark cache */
519                 cache->modified = FALSE;
520                 cache->dataRead = TRUE;
521         }
522         return muttFile->retVal;
523 }
524
525 #define WORK_BUFLEN 1024
526
527 /*
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.
531 */
532 gchar *mutt_find_file( void ) {
533         const gchar *homedir;
534         gchar str[ WORK_BUFLEN + 1 ];
535         gint len;
536         FILE *fp;
537
538         homedir = get_home_dir();
539         if( ! homedir ) return g_strdup( "" );
540
541         strncpy( str, homedir, WORK_BUFLEN );
542         len = strlen( str );
543         if( len > 0 ) {
544                 if( str[ len-1 ] != G_DIR_SEPARATOR ) {
545                         str[ len ] = G_DIR_SEPARATOR;
546                         str[ ++len ] = '\0';
547                 }
548         }
549         strncat( str, MUTT_HOME_FILE, WORK_BUFLEN - strlen(str) );
550
551         /* Attempt to open */
552         if( ( fp = g_fopen( str, "rb" ) ) != NULL ) {
553                 fclose( fp );
554         }
555         else {
556                 /* Truncate filename */
557                 str[ len ] = '\0';
558         }
559         return g_strdup( str );
560 }
561
562 /*
563 * End of Source.
564 */
565