0.9.8claws14
[claws.git] / src / procheader.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2003 Hiroyuki Yamamoto
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 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include <glib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <time.h>
29
30 #include "intl.h"
31 #include "procheader.h"
32 #include "procmsg.h"
33 #include "codeconv.h"
34 #include "prefs_common.h"
35 #include "utils.h"
36
37 #define BUFFSIZE        8192
38
39 typedef char *(*getlinefunc)(char *, int, void *);
40 typedef int (*peekcharfunc)(void *);
41 typedef int (*getcharfunc)(void *);
42 typedef gint (*get_one_field_func)(gchar *, gint, void *, HeaderEntry[]);
43
44 static gint string_get_one_field(gchar *buf, gint len, char **str,
45                                  HeaderEntry hentry[]);
46
47 static char *string_getline(char *buf, int len, char **str);
48 static int string_peekchar(char **str);
49 static int string_getchar(char **str);
50 static int file_peekchar(FILE *fp);
51 static int file_getchar(FILE *fp);
52 static gint generic_get_one_field(gchar *buf, gint len, void *data,
53                                   HeaderEntry hentry[],
54                                   getlinefunc getline, 
55                                   peekcharfunc peekchar,
56                                   getcharfunc getchar);
57 static MsgInfo *parse_stream(void *data, gboolean isstring, MsgFlags flags,
58                              gboolean full, gboolean decrypted);
59
60
61 gint procheader_get_one_field(gchar *buf, gint len, FILE *fp,
62                               HeaderEntry hentry[])
63 {
64         return generic_get_one_field(buf, len, fp, hentry,
65                                      (getlinefunc)fgets, (peekcharfunc)file_peekchar,
66                                      (getcharfunc)file_getchar);
67 }
68
69 static gint string_get_one_field(gchar *buf, gint len, char **str,
70                                  HeaderEntry hentry[])
71 {
72         return generic_get_one_field(buf, len, str, hentry,
73                                      (getlinefunc)string_getline,
74                                      (peekcharfunc)string_peekchar,
75                                      (getcharfunc)string_getchar);
76 }
77
78 static char *string_getline(char *buf, int len, char **str)
79 {
80         if (!**str)
81                 return NULL;
82
83         for (; **str && len > 1; --len)
84                 if ((*buf++ = *(*str)++) == '\n')
85                     break;
86         *buf = '\0';
87
88         return buf;
89 }
90
91 static int string_peekchar(char **str)
92 {
93         return **str;
94 }
95
96 static int string_getchar(char **str)
97 {
98         return *(*str)++;
99 }
100
101 static int file_peekchar(FILE *fp)
102 {
103         return ungetc(getc(fp), fp);
104 }
105
106 static int file_getchar(FILE *fp)
107 {
108         return getc(fp);
109 }
110
111 static gint generic_get_one_field(gchar *buf, gint len, void *data,
112                           HeaderEntry *hentry,
113                           getlinefunc getline, peekcharfunc peekchar,
114                           getcharfunc getchar_)
115 {
116         gint nexthead;
117         gint hnum = 0;
118         HeaderEntry *hp = NULL;
119
120         if (hentry != NULL) {
121                 /* skip non-required headers */
122                 do {
123                         do {
124                                 if (getline(buf, len, data) == NULL)
125                                         return -1;
126                                 if (buf[0] == '\r' || buf[0] == '\n')
127                                         return -1;
128                         } while (buf[0] == ' ' || buf[0] == '\t');
129
130                         for (hp = hentry, hnum = 0; hp->name != NULL;
131                              hp++, hnum++) {
132                                 if (!strncasecmp(hp->name, buf,
133                                                  strlen(hp->name)))
134                                         break;
135                         }
136                 } while (hp->name == NULL);
137         } else {
138                 if (getline(buf, len, data) == NULL) return -1;
139                 if (buf[0] == '\r' || buf[0] == '\n') return -1;
140         }
141
142         /* unfold line */
143         while (1) {
144                 nexthead = peekchar(data);
145                 /* ([*WSP CRLF] 1*WSP) */
146                 if (nexthead == ' ' || nexthead == '\t') {
147                         size_t buflen;
148                         /* trim previous trailing \n if requesting one header or
149                          * unfolding was requested */
150                         if (!hentry || (hp && hp->unfold))
151                                 strretchomp(buf);
152
153                         buflen = strlen(buf);
154                         
155                         /* concatenate next line */
156                         if ((len - buflen) > 2) {
157                                 if (getline(buf + buflen, len - buflen, data) == NULL)
158                                         break;
159                         } else
160                                 break;
161                 } else {
162                         /* remove trailing new line */
163                         strretchomp(buf);
164                         break;
165                 }
166         }
167
168         return hnum;
169 }
170
171 gint procheader_get_one_field_asis(gchar *buf, gint len, FILE *fp)
172 {
173         gint nexthead;
174
175         if (fgets(buf, len, fp) == NULL) return -1;
176         if (buf[0] == '\r' || buf[0] == '\n') return -1;
177
178         /* concatenate multi-line fields */
179         while (1) {
180                 nexthead = file_peekchar(fp);
181                 /* ([*WSP CRLF] 1*WSP) */
182                 if (nexthead == ' ' || nexthead == '\t') {
183                         size_t buflen = strlen(buf);
184                         
185                         /* concatenate next line */
186                         if ((len - buflen) > 2) {
187                                 if (fgets(buf + buflen, len - buflen, fp) == NULL)
188                                         break;
189                         } else
190                                 break;
191                 } else {
192                         /* remove trailing new line */
193                         strretchomp(buf);
194                         break;
195                 }
196         }
197         return 0;
198 }
199
200 #if 0
201 gchar *procheader_get_unfolded_line(gchar *buf, gint len, FILE *fp)
202 {
203         gboolean folded = FALSE;
204         gint nexthead;
205         gchar *bufp;
206
207         if (fgets(buf, len, fp) == NULL) return NULL;
208         if (buf[0] == '\r' || buf[0] == '\n') return NULL;
209         bufp = buf + strlen(buf);
210
211         for (; bufp > buf &&
212              (*(bufp - 1) == '\n' || *(bufp - 1) == '\r');
213              bufp--)
214                 *(bufp - 1) = '\0';
215
216         while (1) {
217                 nexthead = fgetc(fp);
218
219                 /* folded */
220                 if (nexthead == ' ' || nexthead == '\t')
221                         folded = TRUE;
222                 else if (nexthead == EOF)
223                         break;
224                 else if (folded == TRUE) {
225                         if (nexthead == '\r' || nexthead == '\n') {
226                                 folded = FALSE;
227                                 continue;
228                         }
229
230                         if ((len - (bufp - buf)) <= 2) break;
231
232                         /* replace return code on the tail end
233                            with space */
234                         *bufp++ = ' ';
235                         *bufp++ = nexthead;
236                         *bufp = '\0';
237
238                         /* concatenate next line */
239                         if (fgets(bufp, len - (bufp - buf), fp)
240                             == NULL) break;
241                         bufp += strlen(bufp);
242
243                         for (; bufp > buf &&
244                              (*(bufp - 1) == '\n' || *(bufp - 1) == '\r');
245                              bufp--)
246                                 *(bufp - 1) = '\0';
247
248                         folded = FALSE;
249                 } else {
250                         ungetc(nexthead, fp);
251                         break;
252                 }
253         }
254
255         /* remove trailing return code */
256         strretchomp(buf);
257
258         return buf;
259 }
260 #endif
261
262 #if 0
263 GSList *procheader_get_header_list_from_file(const gchar *file)
264 {
265         FILE *fp;
266         GSList *hlist;
267
268         if ((fp = fopen(file, "rb")) == NULL) {
269                 FILE_OP_ERROR(file, "fopen");
270                 return NULL;
271         }
272
273         hlist = procheader_get_header_list(fp);
274
275         fclose(fp);
276         return hlist;
277 }
278
279 GSList *procheader_get_header_list(FILE *fp)
280 {
281         gchar buf[BUFFSIZE];
282         GSList *hlist = NULL;
283         Header *header;
284
285         g_return_val_if_fail(fp != NULL, NULL);
286
287         while (procheader_get_unfolded_line(buf, sizeof(buf), fp) != NULL) {
288                 if ((header = procheader_parse_header(buf)) != NULL)
289                         hlist = g_slist_append(hlist, header);
290                 /*
291                 if (*buf == ':') continue;
292                 for (p = buf; *p && *p != ' '; p++) {
293                         if (*p == ':') {
294                                 header = g_new(Header, 1);
295                                 header->name = g_strndup(buf, p - buf);
296                                 p++;
297                                 while (*p == ' ' || *p == '\t') p++;
298                                 conv_unmime_header(tmp, sizeof(tmp), p, NULL);
299                                 header->body = g_strdup(tmp);
300
301                                 hlist = g_slist_append(hlist, header);
302                                 break;
303                         }
304                 }
305                 */
306         }
307
308         return hlist;
309 }
310 #endif
311
312 #if 0
313 GPtrArray *procheader_get_header_array(FILE *fp)
314 {
315         gchar buf[BUFFSIZE];
316         GPtrArray *headers;
317         Header *header;
318
319         g_return_val_if_fail(fp != NULL, NULL);
320
321         headers = g_ptr_array_new();
322
323         while (procheader_get_unfolded_line(buf, sizeof(buf), fp) != NULL) {
324                 if ((header = procheader_parse_header(buf)) != NULL)
325                         g_ptr_array_add(headers, header);
326                 /*
327                 if (*buf == ':') continue;
328                 for (p = buf; *p && *p != ' '; p++) {
329                         if (*p == ':') {
330                                 header = g_new(Header, 1);
331                                 header->name = g_strndup(buf, p - buf);
332                                 p++;
333                                 while (*p == ' ' || *p == '\t') p++;
334                                 conv_unmime_header(tmp, sizeof(tmp), p, NULL);
335                                 header->body = g_strdup(tmp);
336
337                                 g_ptr_array_add(headers, header);
338                                 break;
339                         }
340                 }
341                 */
342         }
343
344         return headers;
345 }
346 #endif
347
348 GPtrArray *procheader_get_header_array_asis(FILE *fp)
349 {
350         gchar buf[BUFFSIZE];
351         GPtrArray *headers;
352         Header *header;
353
354         g_return_val_if_fail(fp != NULL, NULL);
355
356         headers = g_ptr_array_new();
357
358         while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
359                 if ((header = procheader_parse_header(buf)) != NULL)
360                         g_ptr_array_add(headers, header);
361                         /*
362                 if (*buf == ':') continue;
363                 for (p = buf; *p && *p != ' '; p++) {
364                         if (*p == ':') {
365                                 header = g_new(Header, 1);
366                                 header->name = g_strndup(buf, p - buf);
367                                 p++;
368                                 conv_unmime_header(tmp, sizeof(tmp), p, NULL);
369                                 header->body = g_strdup(tmp);
370
371                                 g_ptr_array_add(headers, header);
372                                 break;
373                         }
374                 }
375                         */
376         }
377
378         return headers;
379 }
380
381 #if 0
382 void procheader_header_list_destroy(GSList *hlist)
383 {
384         Header *header;
385
386         while (hlist != NULL) {
387                 header = hlist->data;
388                 procheader_header_free(header);
389                 hlist = g_slist_remove(hlist, header);
390         }
391 }
392 #endif
393
394 void procheader_header_array_destroy(GPtrArray *harray)
395 {
396         gint i;
397         Header *header;
398
399         for (i = 0; i < harray->len; i++) {
400                 header = g_ptr_array_index(harray, i);
401                 procheader_header_free(header);
402         }
403
404         g_ptr_array_free(harray, TRUE);
405 }
406
407 void procheader_header_free(Header *header)
408 {
409         if (!header) return;
410
411         g_free(header->name);
412         g_free(header->body);
413         g_free(header);
414 }
415
416 /*
417   tests whether two headers' names are equal
418   remove the trailing ':' or ' ' before comparing
419 */
420
421 gboolean procheader_headername_equal(char * hdr1, char * hdr2)
422 {
423         int len1;
424         int len2;
425
426         len1 = strlen(hdr1);
427         len2 = strlen(hdr2);
428         if (hdr1[len1 - 1] == ':')
429                 len1--;
430         if (hdr2[len2 - 1] == ':')
431                 len2--;
432         if (len1 != len2)
433                 return 0;
434
435         return (g_strncasecmp(hdr1, hdr2, len1) == 0);
436 }
437
438 /*
439   parse headers, for example :
440   From: dinh@enseirb.fr becomes :
441   header->name = "From:"
442   header->body = "dinh@enseirb.fr"
443  */
444
445 Header * procheader_parse_header(gchar * buf)
446 {
447         gchar tmp[BUFFSIZE];
448         gchar *p = buf;
449         Header * header;
450
451         if ((*buf == ':') || (*buf == ' '))
452                 return NULL;
453
454         for (p = buf; *p ; p++) {
455                 if ((*p == ':') || (*p == ' ')) {
456                         header = g_new(Header, 1);
457                         header->name = g_strndup(buf, p - buf + 1);
458                         p++;
459                         while (*p == ' ' || *p == '\t') p++;
460                         conv_unmime_header(tmp, sizeof(tmp), p, NULL);
461                         if(tmp == NULL) 
462                                 header->body = g_strdup(p);
463                         else    
464                                 header->body = g_strdup(tmp);
465                         return header;
466                 }
467         }
468         return NULL;
469 }
470
471 void procheader_get_header_fields(FILE *fp, HeaderEntry hentry[])
472 {
473         gchar buf[BUFFSIZE];
474         HeaderEntry *hp;
475         gint hnum;
476         gchar *p;
477
478         if (hentry == NULL) return;
479
480         while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry))
481                != -1) {
482                 hp = hentry + hnum;
483
484                 p = buf + strlen(hp->name);
485                 while (*p == ' ' || *p == '\t') p++;
486
487                 if (hp->body == NULL)
488                         hp->body = g_strdup(p);
489                 else if (procheader_headername_equal(hp->name, "To") ||
490                          procheader_headername_equal(hp->name, "Cc")) {
491                         gchar *tp = hp->body;
492                         hp->body = g_strconcat(tp, ", ", p, NULL);
493                         g_free(tp);
494                 }
495         }
496 }
497
498 MsgInfo *procheader_parse_file(const gchar *file, MsgFlags flags,
499                                gboolean full, gboolean decrypted)
500 {
501         FILE *fp;
502         MsgInfo *msginfo;
503
504         if ((fp = fopen(file, "rb")) == NULL) {
505                 FILE_OP_ERROR(file, "fopen");
506                 return NULL;
507         }
508
509         msginfo = procheader_parse_stream(fp, flags, full, decrypted);
510         fclose(fp);
511         return msginfo;
512 }
513
514 MsgInfo *procheader_parse_str(const gchar *str, MsgFlags flags, gboolean full,
515                               gboolean decrypted)
516 {
517         return parse_stream(&str, TRUE, flags, full, decrypted);
518 }
519
520 enum
521 {
522         H_DATE          = 0,
523         H_FROM          = 1,
524         H_TO            = 2,
525         H_CC            = 3,
526         H_NEWSGROUPS    = 4,
527         H_SUBJECT       = 5,
528         H_MSG_ID        = 6,
529         H_REFERENCES    = 7,
530         H_IN_REPLY_TO   = 8,
531         H_CONTENT_TYPE  = 9,
532         H_SEEN          = 10,
533         H_STATUS        = 11,
534         H_X_STATUS      = 12,
535         H_FROM_SPACE    = 13,
536         H_X_FACE        = 14,
537         H_DISPOSITION_NOTIFICATION_TO = 15,
538         H_RETURN_RECEIPT_TO = 16
539 };
540
541 static HeaderEntry hentry_full[] = {{"Date:",           NULL, FALSE},
542                                    {"From:",            NULL, TRUE},
543                                    {"To:",              NULL, TRUE},
544                                    {"Cc:",              NULL, TRUE},
545                                    {"Newsgroups:",      NULL, TRUE},
546                                    {"Subject:",         NULL, TRUE},
547                                    {"Message-Id:",      NULL, FALSE},
548                                    {"References:",      NULL, FALSE},
549                                    {"In-Reply-To:",     NULL, FALSE},
550                                    {"Content-Type:",    NULL, FALSE},
551                                    {"Seen:",            NULL, FALSE},
552                                    {"Status:",          NULL, FALSE},
553                                    {"X-Status:",        NULL, FALSE},
554                                    {"From ",            NULL, FALSE},
555                                    {"X-Face:",          NULL, FALSE},
556                                    {"Disposition-Notification-To:", NULL, FALSE},
557                                    {"Return-Receipt-To:", NULL, FALSE},
558                                    {NULL,               NULL, FALSE}};
559
560 static HeaderEntry hentry_short[] = {{"Date:",          NULL, FALSE},
561                                     {"From:",           NULL, TRUE},
562                                     {"To:",             NULL, TRUE},
563                                     {"Cc:",             NULL, TRUE},
564                                     {"Newsgroups:",     NULL, TRUE},
565                                     {"Subject:",        NULL, TRUE},
566                                     {"Message-Id:",     NULL, FALSE},
567                                     {"References:",     NULL, FALSE},
568                                     {"In-Reply-To:",    NULL, FALSE},
569                                     {"Content-Type:",   NULL, FALSE},
570                                     {"Seen:",           NULL, FALSE},
571                                     {"Status:",         NULL, FALSE},
572                                     {"X-Status:",       NULL, FALSE},
573                                     {"From ",           NULL, FALSE},
574                                     {NULL,              NULL, FALSE}};
575
576 HeaderEntry* procheader_get_headernames(gboolean full)
577 {
578         return full ? hentry_full : hentry_short;
579 }
580
581 MsgInfo *procheader_parse_stream(FILE *fp, MsgFlags flags, gboolean full,
582                                  gboolean decrypted)
583 {
584         return parse_stream(fp, FALSE, flags, full, decrypted);
585 }
586
587 static MsgInfo *parse_stream(void *data, gboolean isstring, MsgFlags flags,
588                              gboolean full, gboolean decrypted)
589 {
590         MsgInfo *msginfo;
591         gchar buf[BUFFSIZE], tmp[BUFFSIZE];
592         gchar *reference = NULL;
593         gchar *p;
594         gchar *hp;
595         HeaderEntry *hentry;
596         gint hnum;
597         get_one_field_func get_one_field =
598                 isstring ? (get_one_field_func)string_get_one_field
599                          : (get_one_field_func)procheader_get_one_field;
600
601         hentry = procheader_get_headernames(full);
602
603         if (MSG_IS_QUEUED(flags) || MSG_IS_DRAFT(flags)) {
604                 while (get_one_field(buf, sizeof(buf), data, NULL) != -1)
605                         ; /* loop */
606         }
607
608         msginfo = procmsg_msginfo_new();
609         
610         if (flags.tmp_flags || flags.perm_flags) 
611                 msginfo->flags = flags;
612         else 
613                 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_NEW | MSG_UNREAD);
614         
615         if (decrypted)
616                 MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MIME);
617
618         msginfo->inreplyto = NULL;
619
620         while ((hnum = get_one_field(buf, sizeof(buf), data, hentry))
621                != -1) {
622                 hp = buf + strlen(hentry[hnum].name);
623                 while (*hp == ' ' || *hp == '\t') hp++;
624
625                 switch (hnum) {
626                 case H_DATE:
627                         if (msginfo->date) break;
628                         msginfo->date_t =
629                                 procheader_date_parse(NULL, hp, 0);
630                         msginfo->date = g_strdup(hp);
631                         break;
632                 case H_FROM:
633                         if (msginfo->from) break;
634                         conv_unmime_header(tmp, sizeof(tmp), hp, NULL);
635                         msginfo->from = g_strdup(tmp);
636                         msginfo->fromname = procheader_get_fromname(tmp);
637                         break;
638                 case H_TO:
639                         conv_unmime_header(tmp, sizeof(tmp), hp, NULL);
640                         if (msginfo->to) {
641                                 p = msginfo->to;
642                                 msginfo->to =
643                                         g_strconcat(p, ", ", tmp, NULL);
644                                 g_free(p);
645                         } else
646                                 msginfo->to = g_strdup(tmp);
647                         break;
648                 case H_CC:
649                         conv_unmime_header(tmp, sizeof(tmp), hp, NULL);
650                         if (msginfo->cc) {
651                                 p = msginfo->cc;
652                                 msginfo->cc =
653                                         g_strconcat(p, ", ", tmp, NULL);
654                                 g_free(p);
655                         } else
656                                 msginfo->cc = g_strdup(tmp);
657                         break;
658                 case H_NEWSGROUPS:
659                         if (msginfo->newsgroups) {
660                                 p = msginfo->newsgroups;
661                                 msginfo->newsgroups =
662                                         g_strconcat(p, ",", hp, NULL);
663                                 g_free(p);
664                         } else
665                                 msginfo->newsgroups = g_strdup(hp);
666                         break;
667                 case H_SUBJECT:
668                         if (msginfo->subject) break;
669                         conv_unmime_header(tmp, sizeof(tmp), hp, NULL);
670                         msginfo->subject = g_strdup(tmp);
671                         break;
672                 case H_MSG_ID:
673                         if (msginfo->msgid) break;
674
675                         extract_parenthesis(hp, '<', '>');
676                         remove_space(hp);
677                         msginfo->msgid = g_strdup(hp);
678                         break;
679                 case H_REFERENCES:
680                 case H_IN_REPLY_TO:
681                         if (!reference) {
682                                 msginfo->references = g_strdup(hp);
683                                 eliminate_parenthesis(hp, '(', ')');
684                                 if ((p = strrchr(hp, '<')) != NULL &&
685                                     strchr(p + 1, '>') != NULL) {
686                                         extract_parenthesis(p, '<', '>');
687                                         remove_space(p);
688                                         if (*p != '\0')
689                                                 reference = g_strdup(p);
690                                 }
691                         }
692                         break;
693                 case H_CONTENT_TYPE:
694                         if (decrypted) {
695                                 if (!strncasecmp(hp, "multipart", 9)) {
696                                         if (strncasecmp(hp, "multipart/signed", 16)) {
697                                                 MSG_SET_TMP_FLAGS(msginfo->flags,
698                                                           MSG_MIME);
699                                         } else {
700                                                 MSG_SET_TMP_FLAGS(msginfo->flags,
701                                                           MSG_SIGNED);
702                                         }
703                                 }
704                         }
705                         else if (!strncasecmp(hp, "multipart/encrypted", 19)) {
706                                 MSG_SET_TMP_FLAGS(msginfo->flags,
707                                                   MSG_ENCRYPTED);
708                         } 
709                         else if (!strncasecmp(hp, "multipart", 9) &&
710                                    !strncasecmp(hp, "multipart/signed", 16)) {
711                                 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_SIGNED);
712                         } 
713                         else if (!strncasecmp(hp, "multipart", 9))
714                                 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_MIME);
715                         break;
716 #ifdef ALLOW_HEADER_HINT                        
717                 case H_SEEN:
718                         /* mnews Seen header */
719                         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW|MSG_UNREAD);
720                         break;
721 #endif                  
722                 case H_X_FACE:
723                         if (msginfo->xface) break;
724                         msginfo->xface = g_strdup(hp);
725                         break;
726                 case H_DISPOSITION_NOTIFICATION_TO:
727                         if (msginfo->dispositionnotificationto) break;
728                         msginfo->dispositionnotificationto = g_strdup(hp);
729                         break;
730                 case H_RETURN_RECEIPT_TO:
731                         if (msginfo->returnreceiptto) break;
732                         msginfo->returnreceiptto = g_strdup(hp);
733                         break;
734 #ifdef ALLOW_HEADER_HINT                        
735                 case H_STATUS:
736                         if (strchr(hp, 'R') != NULL)
737                                 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_UNREAD);
738                         if (strchr(hp, 'O') != NULL)
739                                 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW);
740                         if (strchr(hp, 'U') != NULL)
741                                 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_UNREAD);
742                         break;
743                 case H_X_STATUS:
744                         if (strchr(hp, 'D') != NULL)
745                                 MSG_SET_PERM_FLAGS(msginfo->flags,
746                                               MSG_REALLY_DELETED);
747                         if (strchr(hp, 'F') != NULL)
748                                 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_MARKED);
749                         if (strchr(hp, 'd') != NULL)
750                                 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
751                         if (strchr(hp, 'r') != NULL)
752                                 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_REPLIED);
753                         if (strchr(hp, 'f') != NULL)
754                                 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_FORWARDED);
755                         break;
756 #endif                  
757                 case H_FROM_SPACE:
758                         if (msginfo->fromspace) break;
759                         msginfo->fromspace = g_strdup(hp);
760                         break;
761                 default:
762                         break;
763                 }
764         }
765         msginfo->inreplyto = reference;
766
767         return msginfo;
768 }
769
770 gchar *procheader_get_fromname(const gchar *str)
771 {
772         gchar *tmp, *name;
773
774         Xstrdup_a(tmp, str, return NULL);
775
776         if (*tmp == '\"') {
777                 extract_quote(tmp, '\"');
778                 g_strstrip(tmp);
779         } else if (strchr(tmp, '<')) {
780                 eliminate_parenthesis(tmp, '<', '>');
781                 g_strstrip(tmp);
782                 if (*tmp == '\0') {
783                         strcpy(tmp, str);
784                         extract_parenthesis(tmp, '<', '>');
785                         g_strstrip(tmp);
786                 }
787         } else if (strchr(tmp, '(')) {
788                 extract_parenthesis(tmp, '(', ')');
789                 g_strstrip(tmp);
790         }
791
792         if (*tmp == '\0')
793                 name = g_strdup(str);
794         else
795                 name = g_strdup(tmp);
796
797         return name;
798 }
799
800 static gint procheader_scan_date_string(const gchar *str,
801                                         gchar *weekday, gint *day,
802                                         gchar *month, gint *year,
803                                         gint *hh, gint *mm, gint *ss,
804                                         gchar *zone)
805 {
806         gint result;
807
808         result = sscanf(str, "%10s %d %9s %d %2d:%2d:%2d %5s",
809                         weekday, day, month, year, hh, mm, ss, zone);
810         if (result == 8) return 0;
811
812         result = sscanf(str, "%3s,%d %9s %d %2d:%2d:%2d %5s",
813                         weekday, day, month, year, hh, mm, ss, zone);
814         if (result == 8) return 0;
815
816         result = sscanf(str, "%d %9s %d %2d:%2d:%2d %5s",
817                         day, month, year, hh, mm, ss, zone);
818         if (result == 7) return 0;
819
820         *zone = '\0';
821         result = sscanf(str, "%10s %d %9s %d %2d:%2d:%2d",
822                         weekday, day, month, year, hh, mm, ss);
823         if (result == 7) return 0;
824
825         *ss = 0;
826         result = sscanf(str, "%10s %d %9s %d %2d:%2d %5s",
827                         weekday, day, month, year, hh, mm, zone);
828         if (result == 7) return 0;
829
830         result = sscanf(str, "%d %9s %d %2d:%2d %5s",
831                         day, month, year, hh, mm, zone);
832         if (result == 6) return 0;
833
834         return -1;
835 }
836
837 /*
838  * Hiro, most UNIXen support this function:
839  * http://www.mcsr.olemiss.edu/cgi-bin/man-cgi?getdate
840  */
841 gboolean procheader_date_parse_to_tm(const gchar *src, struct tm *t, char *zone)
842 {
843         static gchar monthstr[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
844         gchar weekday[11];
845         gint day;
846         gchar month[10];
847         gint year;
848         gint hh, mm, ss;
849         GDateMonth dmonth;
850         gchar *p;
851
852         if (!t)
853                 return FALSE;
854         
855         memset(t, 0, sizeof *t);        
856
857         if (procheader_scan_date_string(src, weekday, &day, month, &year,
858                                         &hh, &mm, &ss, zone) < 0) {
859                 g_warning("Invalid date: %s\n", src);
860                 return FALSE;
861         }
862
863         /* Y2K compliant :) */
864         if (year < 100) {
865                 if (year < 70)
866                         year += 2000;
867                 else
868                         year += 1900;
869         }
870
871         month[3] = '\0';
872         if ((p = strstr(monthstr, month)) != NULL)
873                 dmonth = (gint)(p - monthstr) / 3 + 1;
874         else {
875                 g_warning("Invalid month: %s\n", month);
876                 dmonth = G_DATE_BAD_MONTH;
877         }
878
879         t->tm_sec = ss;
880         t->tm_min = mm;
881         t->tm_hour = hh;
882         t->tm_mday = day;
883         t->tm_mon = dmonth - 1;
884         t->tm_year = year - 1900;
885         t->tm_wday = 0;
886         t->tm_yday = 0;
887         t->tm_isdst = -1;
888
889         mktime(t);
890
891         return TRUE;
892 }
893
894 time_t procheader_date_parse(gchar *dest, const gchar *src, gint len)
895 {
896         static gchar monthstr[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
897         gchar weekday[11];
898         gint day;
899         gchar month[10];
900         gint year;
901         gint hh, mm, ss;
902         gchar zone[6];
903         GDateMonth dmonth = G_DATE_BAD_MONTH;
904         struct tm t;
905         gchar *p;
906         time_t timer;
907         time_t tz_offset;
908
909         if (procheader_scan_date_string(src, weekday, &day, month, &year,
910                                         &hh, &mm, &ss, zone) < 0) {
911                 g_warning("Invalid date: %s\n", src);
912                 if (dest && len > 0)
913                         strncpy2(dest, src, len);
914                 return 0;
915         }
916
917         /* Y2K compliant :) */
918         if (year < 1000) {
919                 if (year < 50)
920                         year += 2000;
921                 else
922                         year += 1900;
923         }
924
925         month[3] = '\0';
926         for (p = monthstr; *p != '\0'; p += 3) {
927                 if (!strncasecmp(p, month, 3)) {
928                         dmonth = (gint)(p - monthstr) / 3 + 1;
929                         break;
930                 }
931         }
932         if (*p == '\0')
933                 g_warning("Invalid month: %s\n", month);
934
935         t.tm_sec = ss;
936         t.tm_min = mm;
937         t.tm_hour = hh;
938         t.tm_mday = day;
939         t.tm_mon = dmonth - 1;
940         t.tm_year = year - 1900;
941         t.tm_wday = 0;
942         t.tm_yday = 0;
943         t.tm_isdst = -1;
944
945         timer = mktime(&t);
946         tz_offset = remote_tzoffset_sec(zone);
947         if (tz_offset != -1)
948                 timer += tzoffset_sec(&timer) - tz_offset;
949
950         if (dest)
951                 procheader_date_get_localtime(dest, len, timer);
952
953         return timer;
954 }
955
956 void procheader_date_get_localtime(gchar *dest, gint len, const time_t timer)
957 {
958         struct tm *lt;
959         gchar *default_format = "%y/%m/%d(%a) %H:%M";
960
961         lt = localtime(&timer);
962
963         if (prefs_common.date_format)
964                 strftime(dest, len, prefs_common.date_format, lt);
965         else
966                 strftime(dest, len, default_format, lt);
967 }
968
969 /* Added by Mel Hadasht on 27 Aug 2001 */
970 /* Get a header from msginfo */
971 gint procheader_get_header_from_msginfo(MsgInfo *msginfo, gchar *buf, gint len, gchar *header)
972 {
973         gchar *file;
974         FILE *fp;
975         HeaderEntry hentry[]={ { header, NULL, TRUE  },
976                                { NULL,   NULL, FALSE } };
977         gint val;
978        
979         g_return_val_if_fail(msginfo != NULL, -1);
980         file = procmsg_get_message_file_path(msginfo);
981         if ((fp = fopen(file, "rb")) == NULL) {
982                FILE_OP_ERROR(file, "fopen");
983                g_free(file);
984                return -1;
985         }
986         val = procheader_get_one_field(buf,len, fp, hentry);
987         if (fclose(fp) == EOF) {
988                 FILE_OP_ERROR(file, "fclose");
989                 unlink(file);
990                 g_free(file);
991                 return -1;
992         }
993
994         g_free(file);
995         if (val == -1)
996                 return -1;
997
998         return 0;
999 }