* src/procmsg.c
[claws.git] / src / procheader.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2002 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                         header->body = g_strdup(tmp);
402                         return header;
403                 }
404         }
405         return NULL;
406 }
407
408 void procheader_get_header_fields(FILE *fp, HeaderEntry hentry[])
409 {
410         gchar buf[BUFFSIZE];
411         HeaderEntry *hp;
412         gint hnum;
413         gchar *p;
414
415         if (hentry == NULL) return;
416
417         while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry))
418                != -1) {
419                 hp = hentry + hnum;
420
421                 p = buf + strlen(hp->name);
422                 while (*p == ' ' || *p == '\t') p++;
423
424                 if (hp->body == NULL)
425                         hp->body = g_strdup(p);
426                 else if (procheader_headername_equal(hp->name, "To") ||
427                          procheader_headername_equal(hp->name, "Cc")) {
428                         gchar *tp = hp->body;
429                         hp->body = g_strconcat(tp, ", ", p, NULL);
430                         g_free(tp);
431                 }
432         }
433 }
434
435 MsgInfo *procheader_parse_file(const gchar *file, MsgFlags flags,
436                                gboolean full, gboolean decrypted)
437 {
438         FILE *fp;
439         MsgInfo *msginfo;
440
441         if ((fp = fopen(file, "rb")) == NULL) {
442                 FILE_OP_ERROR(file, "fopen");
443                 return NULL;
444         }
445
446         msginfo = procheader_parse_stream(fp, flags, full, decrypted);
447         fclose(fp);
448         return msginfo;
449 }
450
451 MsgInfo *procheader_parse_str(const gchar *str, MsgFlags flags, gboolean full,
452                               gboolean decrypted)
453 {
454         FILE *fp;
455         MsgInfo *msginfo;
456
457         if ((fp = str_open_as_stream(str)) == NULL)
458                 return NULL;
459
460         msginfo = procheader_parse_stream(fp, flags, full, decrypted);
461         fclose(fp);
462         return msginfo;
463 }
464
465 enum
466 {
467         H_DATE          = 0,
468         H_FROM          = 1,
469         H_TO            = 2,
470         H_CC            = 3,
471         H_NEWSGROUPS    = 4,
472         H_SUBJECT       = 5,
473         H_MSG_ID        = 6,
474         H_REFERENCES    = 7,
475         H_IN_REPLY_TO   = 8,
476         H_CONTENT_TYPE  = 9,
477         H_SEEN          = 10,
478         H_STATUS        = 11,
479         H_X_STATUS      = 12,
480         H_FROM_SPACE    = 13,
481         H_X_FACE        = 14,
482         H_DISPOSITION_NOTIFICATION_TO = 15,
483         H_RETURN_RECEIPT_TO = 16
484 };
485
486 MsgInfo *procheader_parse_stream(FILE *fp, MsgFlags flags, gboolean full, 
487                                  gboolean decrypted)
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         MsgInfo *msginfo;
525         gchar buf[BUFFSIZE], tmp[BUFFSIZE];
526         gchar *reference = NULL;
527         gchar *p;
528         gchar *hp;
529         HeaderEntry *hentry;
530         gint hnum;
531
532         hentry = full ? hentry_full : hentry_short;
533
534         if (MSG_IS_QUEUED(flags) || MSG_IS_DRAFT(flags)) {
535                 while (fgets(buf, sizeof(buf), fp) != NULL)
536                         if (buf[0] == '\r' || buf[0] == '\n') break;
537         }
538
539         msginfo = procmsg_msginfo_new();
540         
541         if (flags.tmp_flags || flags.perm_flags) 
542                 msginfo->flags = flags;
543         else 
544                 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_NEW | MSG_UNREAD);
545         
546         if (decrypted)
547                 MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MIME);
548
549         msginfo->inreplyto = NULL;
550
551         while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry))
552                != -1) {
553                 hp = buf + strlen(hentry[hnum].name);
554                 while (*hp == ' ' || *hp == '\t') hp++;
555
556                 switch (hnum) {
557                 case H_DATE:
558                         if (msginfo->date) break;
559                         msginfo->date_t =
560                                 procheader_date_parse(NULL, hp, 0);
561                         msginfo->date = g_strdup(hp);
562                         break;
563                 case H_FROM:
564                         if (msginfo->from) break;
565                         conv_unmime_header(tmp, sizeof(tmp), hp, NULL);
566                         msginfo->from = g_strdup(tmp);
567                         msginfo->fromname = procheader_get_fromname(tmp);
568                         break;
569                 case H_TO:
570                         conv_unmime_header(tmp, sizeof(tmp), hp, NULL);
571                         if (msginfo->to) {
572                                 p = msginfo->to;
573                                 msginfo->to =
574                                         g_strconcat(p, ", ", tmp, NULL);
575                                 g_free(p);
576                         } else
577                                 msginfo->to = g_strdup(tmp);
578                         break;
579                 case H_CC:
580                         conv_unmime_header(tmp, sizeof(tmp), hp, NULL);
581                         if (msginfo->cc) {
582                                 p = msginfo->cc;
583                                 msginfo->cc =
584                                         g_strconcat(p, ", ", tmp, NULL);
585                                 g_free(p);
586                         } else
587                                 msginfo->cc = g_strdup(tmp);
588                         break;
589                 case H_NEWSGROUPS:
590                         if (msginfo->newsgroups) {
591                                 p = msginfo->newsgroups;
592                                 msginfo->newsgroups =
593                                         g_strconcat(p, ",", hp, NULL);
594                                 g_free(p);
595                         } else
596                                 msginfo->newsgroups = g_strdup(buf + 12);
597                         break;
598                 case H_SUBJECT:
599                         if (msginfo->subject) break;
600                         conv_unmime_header(tmp, sizeof(tmp), hp, NULL);
601                         msginfo->subject = g_strdup(tmp);
602                         break;
603                 case H_MSG_ID:
604                         if (msginfo->msgid) break;
605
606                         extract_parenthesis(hp, '<', '>');
607                         remove_space(hp);
608                         msginfo->msgid = g_strdup(hp);
609                         break;
610                 case H_REFERENCES:
611                 case H_IN_REPLY_TO:
612                         if (!reference) {
613                                 msginfo->references = g_strdup(hp);
614                                 eliminate_parenthesis(hp, '(', ')');
615                                 if ((p = strrchr(hp, '<')) != NULL &&
616                                     strchr(p + 1, '>') != NULL) {
617                                         extract_parenthesis(p, '<', '>');
618                                         remove_space(p);
619                                         if (*p != '\0')
620                                                 reference = g_strdup(p);
621                                 }
622                         }
623                         break;
624                 case H_CONTENT_TYPE:
625                         if (decrypted) {
626                                 if (!strncasecmp(hp, "multipart", 9) &&
627                                     strncasecmp(hp, "multipart/signed", 16)) {
628                                         MSG_SET_TMP_FLAGS(msginfo->flags,
629                                                           MSG_MIME);
630                                 }
631                         }
632                         else if (!strncasecmp(hp, "multipart/encrypted", 19)) {
633                                 MSG_SET_TMP_FLAGS(msginfo->flags,
634                                                   MSG_ENCRYPTED);
635                         }
636                         else if (!strncasecmp(hp, "multipart", 9))
637                                 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_MIME);
638                         break;
639 #ifdef ALLOW_HEADER_HINT                        
640                 case H_SEEN:
641                         /* mnews Seen header */
642                         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW|MSG_UNREAD);
643                         break;
644 #endif                  
645                 case H_X_FACE:
646                         if (msginfo->xface) break;
647                         msginfo->xface = g_strdup(hp);
648                         break;
649                 case H_DISPOSITION_NOTIFICATION_TO:
650                         if (msginfo->dispositionnotificationto) break;
651                         msginfo->dispositionnotificationto = g_strdup(hp);
652                         MSG_SET_PERM_FLAGS(msginfo->flags, MSG_RETRCPT_PENDING);        
653                         break;
654                 case H_RETURN_RECEIPT_TO:
655                         if (msginfo->returnreceiptto) break;
656                         msginfo->returnreceiptto = g_strdup(hp);
657                         MSG_SET_PERM_FLAGS(msginfo->flags, MSG_RETRCPT_PENDING);        
658                         break;
659 #ifdef ALLOW_HEADER_HINT                        
660                 case H_STATUS:
661                         if (strchr(hp, 'R') != NULL)
662                                 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_UNREAD);
663                         if (strchr(hp, 'O') != NULL)
664                                 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW);
665                         if (strchr(hp, 'U') != NULL)
666                                 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_UNREAD);
667                         break;
668                 case H_X_STATUS:
669                         if (strchr(hp, 'D') != NULL)
670                                 MSG_SET_PERM_FLAGS(msginfo->flags,
671                                               MSG_REALLY_DELETED);
672                         if (strchr(hp, 'F') != NULL)
673                                 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_MARKED);
674                         if (strchr(hp, 'd') != NULL)
675                                 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
676                         if (strchr(hp, 'r') != NULL)
677                                 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_REPLIED);
678                         if (strchr(hp, 'f') != NULL)
679                                 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_FORWARDED);
680                         break;
681 #endif                  
682                 case H_FROM_SPACE:
683                         if (msginfo->fromspace) break;
684                         msginfo->fromspace = g_strdup(hp);
685                         break;
686                 default:
687                         break;
688                 }
689         }
690         msginfo->inreplyto = reference;
691
692         return msginfo;
693 }
694
695 gchar *procheader_get_fromname(const gchar *str)
696 {
697         gchar *tmp, *name;
698
699         Xstrdup_a(tmp, str, return NULL);
700
701         if (*tmp == '\"') {
702                 extract_quote(tmp, '\"');
703                 g_strstrip(tmp);
704         } else if (strchr(tmp, '<')) {
705                 eliminate_parenthesis(tmp, '<', '>');
706                 g_strstrip(tmp);
707                 if (*tmp == '\0') {
708                         strcpy(tmp, str);
709                         extract_parenthesis(tmp, '<', '>');
710                         g_strstrip(tmp);
711                 }
712         } else if (strchr(tmp, '(')) {
713                 extract_parenthesis(tmp, '(', ')');
714                 g_strstrip(tmp);
715         }
716
717         if (*tmp == '\0')
718                 name = g_strdup(str);
719         else
720                 name = g_strdup(tmp);
721
722         return name;
723 }
724
725 static gint procheader_scan_date_string(const gchar *str,
726                                         gchar *weekday, gint *day,
727                                         gchar *month, gint *year,
728                                         gint *hh, gint *mm, gint *ss,
729                                         gchar *zone)
730 {
731         gint result;
732
733         result = sscanf(str, "%10s %d %9s %d %2d:%2d:%2d %5s",
734                         weekday, day, month, year, hh, mm, ss, zone);
735         if (result == 8) return 0;
736
737         result = sscanf(str, "%3s,%d %9s %d %2d:%2d:%2d %5s",
738                         weekday, day, month, year, hh, mm, ss, zone);
739         if (result == 8) return 0;
740
741         result = sscanf(str, "%d %9s %d %2d:%2d:%2d %5s",
742                         day, month, year, hh, mm, ss, zone);
743         if (result == 7) return 0;
744
745         *ss = 0;
746         result = sscanf(str, "%10s %d %9s %d %2d:%2d %5s",
747                         weekday, day, month, year, hh, mm, zone);
748         if (result == 7) return 0;
749
750         result = sscanf(str, "%d %9s %d %2d:%2d %5s",
751                         day, month, year, hh, mm, zone);
752         if (result == 6) return 0;
753
754         return -1;
755 }
756
757 /*
758  * Hiro, most UNIXen support this function:
759  * http://www.mcsr.olemiss.edu/cgi-bin/man-cgi?getdate
760  */
761 gboolean procheader_date_parse_to_tm(const gchar *src, struct tm *t, char *zone)
762 {
763         static gchar monthstr[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
764         gchar weekday[11];
765         gint day;
766         gchar month[10];
767         gint year;
768         gint hh, mm, ss;
769         GDateMonth dmonth;
770         gchar *p;
771
772         if (!t)
773                 return FALSE;
774         
775         memset(t, 0, sizeof *t);        
776
777         if (procheader_scan_date_string(src, weekday, &day, month, &year,
778                                         &hh, &mm, &ss, zone) < 0) {
779                 g_warning("Invalid date: %s\n", src);
780                 return FALSE;
781         }
782
783         /* Y2K compliant :) */
784         if (year < 100) {
785                 if (year < 70)
786                         year += 2000;
787                 else
788                         year += 1900;
789         }
790
791         month[3] = '\0';
792         if ((p = strstr(monthstr, month)) != NULL)
793                 dmonth = (gint)(p - monthstr) / 3 + 1;
794         else {
795                 g_warning("Invalid month: %s\n", month);
796                 dmonth = G_DATE_BAD_MONTH;
797         }
798
799         t->tm_sec = ss;
800         t->tm_min = mm;
801         t->tm_hour = hh;
802         t->tm_mday = day;
803         t->tm_mon = dmonth - 1;
804         t->tm_year = year - 1900;
805         t->tm_wday = 0;
806         t->tm_yday = 0;
807         t->tm_isdst = -1;
808
809         mktime(t);
810
811         return TRUE;
812 }
813
814 time_t procheader_date_parse(gchar *dest, const gchar *src, gint len)
815 {
816         static gchar monthstr[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
817         gchar weekday[11];
818         gint day;
819         gchar month[10];
820         gint year;
821         gint hh, mm, ss;
822         gchar zone[6];
823         GDateMonth dmonth;
824         struct tm t;
825         gchar *p;
826         time_t timer;
827
828         if (procheader_scan_date_string(src, weekday, &day, month, &year,
829                                         &hh, &mm, &ss, zone) < 0) {
830                 g_warning("Invalid date: %s\n", src);
831                 if (dest && len > 0)
832                         strncpy2(dest, src, len);
833                 return 0;
834         }
835
836         /* Y2K compliant :) */
837         if (year < 100) {
838                 if (year < 70)
839                         year += 2000;
840                 else
841                         year += 1900;
842         }
843
844         month[3] = '\0';
845         if ((p = strstr(monthstr, month)) != NULL)
846                 dmonth = (gint)(p - monthstr) / 3 + 1;
847         else {
848                 g_warning("Invalid month: %s\n", month);
849                 dmonth = G_DATE_BAD_MONTH;
850         }
851
852         t.tm_sec = ss;
853         t.tm_min = mm;
854         t.tm_hour = hh;
855         t.tm_mday = day;
856         t.tm_mon = dmonth - 1;
857         t.tm_year = year - 1900;
858         t.tm_wday = 0;
859         t.tm_yday = 0;
860         t.tm_isdst = -1;
861
862         timer = mktime(&t);
863         timer += tzoffset_sec(&timer) - remote_tzoffset_sec(zone);
864
865         if (dest)
866                 procheader_date_get_localtime(dest, len, timer);
867
868         return timer;
869 }
870
871 void procheader_date_get_localtime(gchar *dest, gint len, const time_t timer)
872 {
873         struct tm *lt;
874         gchar *default_format = "%y/%m/%d(%a) %H:%M";
875
876         lt = localtime(&timer);
877
878         if (prefs_common.date_format)
879                 strftime(dest, len, prefs_common.date_format, lt);
880         else
881                 strftime(dest, len, default_format, lt);
882 }
883
884 gint get_header_from_msginfo(MsgInfo *msginfo, gchar *buf, gint len, gchar *header)
885 {
886         gchar *file;
887         FILE *fp;
888         HeaderEntry hentry[]={ { header, NULL, TRUE  },
889                                { NULL,   NULL, FALSE } };
890         gint val;
891        
892         g_return_val_if_fail(msginfo != NULL, -1);
893         file = procmsg_get_message_file_path(msginfo);
894         if ((fp = fopen(file, "rb")) == NULL) {
895                FILE_OP_ERROR(file, "fopen");
896                g_free(file);
897                return -1;
898         }
899         val = procheader_get_one_field(buf,len, fp, hentry);
900         if (fclose(fp) == EOF) {
901                 FILE_OP_ERROR(file, "fclose");
902                 unlink(file);
903                 g_free(file);
904                 return -1;
905         }
906
907         g_free(file);
908         if (val == -1)
909                 return -1;
910
911         return 0;
912 }