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