src/compose.c: corrected and modified an alert message
[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                         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 MsgInfo *procheader_parse_stream(FILE *fp, MsgFlags flags, gboolean full, 
490                                  gboolean decrypted)
491 {
492         static HeaderEntry hentry_full[] = {{"Date:",           NULL, FALSE},
493                                            {"From:",            NULL, TRUE},
494                                            {"To:",              NULL, TRUE},
495                                            {"Cc:",              NULL, TRUE},
496                                            {"Newsgroups:",      NULL, TRUE},
497                                            {"Subject:",         NULL, TRUE},
498                                            {"Message-Id:",      NULL, FALSE},
499                                            {"References:",      NULL, FALSE},
500                                            {"In-Reply-To:",     NULL, FALSE},
501                                            {"Content-Type:",    NULL, FALSE},
502                                            {"Seen:",            NULL, FALSE},
503                                            {"Status:",          NULL, FALSE},
504                                            {"X-Status:",        NULL, FALSE},
505                                            {"From ",            NULL, FALSE},
506                                            {"X-Face:",          NULL, FALSE},
507                                            {"Disposition-Notification-To:", NULL, FALSE},
508                                            {"Return-Receipt-To:", NULL, FALSE},
509                                            {NULL,               NULL, FALSE}};
510
511         static HeaderEntry hentry_short[] = {{"Date:",          NULL, FALSE},
512                                             {"From:",           NULL, TRUE},
513                                             {"To:",             NULL, TRUE},
514                                             {"Cc:",             NULL, TRUE},
515                                             {"Newsgroups:",     NULL, TRUE},
516                                             {"Subject:",        NULL, TRUE},
517                                             {"Message-Id:",     NULL, FALSE},
518                                             {"References:",     NULL, FALSE},
519                                             {"In-Reply-To:",    NULL, FALSE},
520                                             {"Content-Type:",   NULL, FALSE},
521                                             {"Seen:",           NULL, FALSE},
522                                             {"Status:",         NULL, FALSE},
523                                             {"X-Status:",       NULL, FALSE},
524                                             {"From ",           NULL, FALSE},
525                                             {NULL,              NULL, FALSE}};
526
527         MsgInfo *msginfo;
528         gchar buf[BUFFSIZE], tmp[BUFFSIZE];
529         gchar *reference = NULL;
530         gchar *p;
531         gchar *hp;
532         HeaderEntry *hentry;
533         gint hnum;
534
535         hentry = full ? hentry_full : hentry_short;
536
537         if (MSG_IS_QUEUED(flags) || MSG_IS_DRAFT(flags)) {
538                 while (fgets(buf, sizeof(buf), fp) != NULL)
539                         if (buf[0] == '\r' || buf[0] == '\n') break;
540         }
541
542         msginfo = procmsg_msginfo_new();
543         
544         if (flags.tmp_flags || flags.perm_flags) 
545                 msginfo->flags = flags;
546         else 
547                 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_NEW | MSG_UNREAD);
548         
549         if (decrypted)
550                 MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MIME);
551
552         msginfo->inreplyto = NULL;
553
554         while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry))
555                != -1) {
556                 hp = buf + strlen(hentry[hnum].name);
557                 while (*hp == ' ' || *hp == '\t') hp++;
558
559                 switch (hnum) {
560                 case H_DATE:
561                         if (msginfo->date) break;
562                         msginfo->date_t =
563                                 procheader_date_parse(NULL, hp, 0);
564                         msginfo->date = g_strdup(hp);
565                         break;
566                 case H_FROM:
567                         if (msginfo->from) break;
568                         conv_unmime_header(tmp, sizeof(tmp), hp, NULL);
569                         msginfo->from = g_strdup(tmp);
570                         msginfo->fromname = procheader_get_fromname(tmp);
571                         break;
572                 case H_TO:
573                         conv_unmime_header(tmp, sizeof(tmp), hp, NULL);
574                         if (msginfo->to) {
575                                 p = msginfo->to;
576                                 msginfo->to =
577                                         g_strconcat(p, ", ", tmp, NULL);
578                                 g_free(p);
579                         } else
580                                 msginfo->to = g_strdup(tmp);
581                         break;
582                 case H_CC:
583                         conv_unmime_header(tmp, sizeof(tmp), hp, NULL);
584                         if (msginfo->cc) {
585                                 p = msginfo->cc;
586                                 msginfo->cc =
587                                         g_strconcat(p, ", ", tmp, NULL);
588                                 g_free(p);
589                         } else
590                                 msginfo->cc = g_strdup(tmp);
591                         break;
592                 case H_NEWSGROUPS:
593                         if (msginfo->newsgroups) {
594                                 p = msginfo->newsgroups;
595                                 msginfo->newsgroups =
596                                         g_strconcat(p, ",", hp, NULL);
597                                 g_free(p);
598                         } else
599                                 msginfo->newsgroups = g_strdup(hp);
600                         break;
601                 case H_SUBJECT:
602                         if (msginfo->subject) break;
603                         conv_unmime_header(tmp, sizeof(tmp), hp, NULL);
604                         msginfo->subject = g_strdup(tmp);
605                         break;
606                 case H_MSG_ID:
607                         if (msginfo->msgid) break;
608
609                         extract_parenthesis(hp, '<', '>');
610                         remove_space(hp);
611                         msginfo->msgid = g_strdup(hp);
612                         break;
613                 case H_REFERENCES:
614                 case H_IN_REPLY_TO:
615                         if (!reference) {
616                                 msginfo->references = g_strdup(hp);
617                                 eliminate_parenthesis(hp, '(', ')');
618                                 if ((p = strrchr(hp, '<')) != NULL &&
619                                     strchr(p + 1, '>') != NULL) {
620                                         extract_parenthesis(p, '<', '>');
621                                         remove_space(p);
622                                         if (*p != '\0')
623                                                 reference = g_strdup(p);
624                                 }
625                         }
626                         break;
627                 case H_CONTENT_TYPE:
628                         if (decrypted) {
629                                 if (!strncasecmp(hp, "multipart", 9)) {
630                                         if (strncasecmp(hp, "multipart/signed", 16)) {
631                                                 MSG_SET_TMP_FLAGS(msginfo->flags,
632                                                           MSG_MIME);
633                                         } else {
634                                                 MSG_SET_TMP_FLAGS(msginfo->flags,
635                                                           MSG_SIGNED);
636                                         }
637                                 }
638                         }
639                         else if (!strncasecmp(hp, "multipart/encrypted", 19)) {
640                                 MSG_SET_TMP_FLAGS(msginfo->flags,
641                                                   MSG_ENCRYPTED);
642                         } 
643                         else if (!strncasecmp(hp, "multipart", 9) &&
644                                    !strncasecmp(hp, "multipart/signed", 16)) {
645                                 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_SIGNED);
646                         } 
647                         else if (!strncasecmp(hp, "multipart", 9))
648                                 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_MIME);
649                         break;
650 #ifdef ALLOW_HEADER_HINT                        
651                 case H_SEEN:
652                         /* mnews Seen header */
653                         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW|MSG_UNREAD);
654                         break;
655 #endif                  
656                 case H_X_FACE:
657                         if (msginfo->xface) break;
658                         msginfo->xface = g_strdup(hp);
659                         break;
660                 case H_DISPOSITION_NOTIFICATION_TO:
661                         if (msginfo->dispositionnotificationto) break;
662                         msginfo->dispositionnotificationto = g_strdup(hp);
663                         MSG_SET_PERM_FLAGS(msginfo->flags, MSG_RETRCPT_PENDING);        
664                         break;
665                 case H_RETURN_RECEIPT_TO:
666                         if (msginfo->returnreceiptto) break;
667                         msginfo->returnreceiptto = g_strdup(hp);
668                         MSG_SET_PERM_FLAGS(msginfo->flags, MSG_RETRCPT_PENDING);        
669                         break;
670 #ifdef ALLOW_HEADER_HINT                        
671                 case H_STATUS:
672                         if (strchr(hp, 'R') != NULL)
673                                 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_UNREAD);
674                         if (strchr(hp, 'O') != NULL)
675                                 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW);
676                         if (strchr(hp, 'U') != NULL)
677                                 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_UNREAD);
678                         break;
679                 case H_X_STATUS:
680                         if (strchr(hp, 'D') != NULL)
681                                 MSG_SET_PERM_FLAGS(msginfo->flags,
682                                               MSG_REALLY_DELETED);
683                         if (strchr(hp, 'F') != NULL)
684                                 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_MARKED);
685                         if (strchr(hp, 'd') != NULL)
686                                 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
687                         if (strchr(hp, 'r') != NULL)
688                                 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_REPLIED);
689                         if (strchr(hp, 'f') != NULL)
690                                 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_FORWARDED);
691                         break;
692 #endif                  
693                 case H_FROM_SPACE:
694                         if (msginfo->fromspace) break;
695                         msginfo->fromspace = g_strdup(hp);
696                         break;
697                 default:
698                         break;
699                 }
700         }
701         msginfo->inreplyto = reference;
702
703         return msginfo;
704 }
705
706 gchar *procheader_get_fromname(const gchar *str)
707 {
708         gchar *tmp, *name;
709
710         Xstrdup_a(tmp, str, return NULL);
711
712         if (*tmp == '\"') {
713                 extract_quote(tmp, '\"');
714                 g_strstrip(tmp);
715         } else if (strchr(tmp, '<')) {
716                 eliminate_parenthesis(tmp, '<', '>');
717                 g_strstrip(tmp);
718                 if (*tmp == '\0') {
719                         strcpy(tmp, str);
720                         extract_parenthesis(tmp, '<', '>');
721                         g_strstrip(tmp);
722                 }
723         } else if (strchr(tmp, '(')) {
724                 extract_parenthesis(tmp, '(', ')');
725                 g_strstrip(tmp);
726         }
727
728         if (*tmp == '\0')
729                 name = g_strdup(str);
730         else
731                 name = g_strdup(tmp);
732
733         return name;
734 }
735
736 static gint procheader_scan_date_string(const gchar *str,
737                                         gchar *weekday, gint *day,
738                                         gchar *month, gint *year,
739                                         gint *hh, gint *mm, gint *ss,
740                                         gchar *zone)
741 {
742         gint result;
743
744         result = sscanf(str, "%10s %d %9s %d %2d:%2d:%2d %5s",
745                         weekday, day, month, year, hh, mm, ss, zone);
746         if (result == 8) return 0;
747
748         result = sscanf(str, "%3s,%d %9s %d %2d:%2d:%2d %5s",
749                         weekday, day, month, year, hh, mm, ss, zone);
750         if (result == 8) return 0;
751
752         result = sscanf(str, "%d %9s %d %2d:%2d:%2d %5s",
753                         day, month, year, hh, mm, ss, zone);
754         if (result == 7) return 0;
755
756         *ss = 0;
757         result = sscanf(str, "%10s %d %9s %d %2d:%2d %5s",
758                         weekday, day, month, year, hh, mm, zone);
759         if (result == 7) return 0;
760
761         result = sscanf(str, "%d %9s %d %2d:%2d %5s",
762                         day, month, year, hh, mm, zone);
763         if (result == 6) return 0;
764
765         return -1;
766 }
767
768 /*
769  * Hiro, most UNIXen support this function:
770  * http://www.mcsr.olemiss.edu/cgi-bin/man-cgi?getdate
771  */
772 gboolean procheader_date_parse_to_tm(const gchar *src, struct tm *t, char *zone)
773 {
774         static gchar monthstr[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
775         gchar weekday[11];
776         gint day;
777         gchar month[10];
778         gint year;
779         gint hh, mm, ss;
780         GDateMonth dmonth;
781         gchar *p;
782
783         if (!t)
784                 return FALSE;
785         
786         memset(t, 0, sizeof *t);        
787
788         if (procheader_scan_date_string(src, weekday, &day, month, &year,
789                                         &hh, &mm, &ss, zone) < 0) {
790                 g_warning("Invalid date: %s\n", src);
791                 return FALSE;
792         }
793
794         /* Y2K compliant :) */
795         if (year < 100) {
796                 if (year < 70)
797                         year += 2000;
798                 else
799                         year += 1900;
800         }
801
802         month[3] = '\0';
803         if ((p = strstr(monthstr, month)) != NULL)
804                 dmonth = (gint)(p - monthstr) / 3 + 1;
805         else {
806                 g_warning("Invalid month: %s\n", month);
807                 dmonth = G_DATE_BAD_MONTH;
808         }
809
810         t->tm_sec = ss;
811         t->tm_min = mm;
812         t->tm_hour = hh;
813         t->tm_mday = day;
814         t->tm_mon = dmonth - 1;
815         t->tm_year = year - 1900;
816         t->tm_wday = 0;
817         t->tm_yday = 0;
818         t->tm_isdst = -1;
819
820         mktime(t);
821
822         return TRUE;
823 }
824
825 time_t procheader_date_parse(gchar *dest, const gchar *src, gint len)
826 {
827         static gchar monthstr[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
828         gchar weekday[11];
829         gint day;
830         gchar month[10];
831         gint year;
832         gint hh, mm, ss;
833         gchar zone[6];
834         GDateMonth dmonth;
835         struct tm t;
836         gchar *p;
837         time_t timer;
838
839         if (procheader_scan_date_string(src, weekday, &day, month, &year,
840                                         &hh, &mm, &ss, zone) < 0) {
841                 g_warning("Invalid date: %s\n", src);
842                 if (dest && len > 0)
843                         strncpy2(dest, src, len);
844                 return 0;
845         }
846
847         /* Y2K compliant :) */
848         if (year < 100) {
849                 if (year < 70)
850                         year += 2000;
851                 else
852                         year += 1900;
853         }
854
855         month[3] = '\0';
856         if ((p = strstr(monthstr, month)) != NULL)
857                 dmonth = (gint)(p - monthstr) / 3 + 1;
858         else {
859                 g_warning("Invalid month: %s\n", month);
860                 dmonth = G_DATE_BAD_MONTH;
861         }
862
863         t.tm_sec = ss;
864         t.tm_min = mm;
865         t.tm_hour = hh;
866         t.tm_mday = day;
867         t.tm_mon = dmonth - 1;
868         t.tm_year = year - 1900;
869         t.tm_wday = 0;
870         t.tm_yday = 0;
871         t.tm_isdst = -1;
872
873         timer = mktime(&t);
874         timer += tzoffset_sec(&timer) - remote_tzoffset_sec(zone);
875
876         if (dest)
877                 procheader_date_get_localtime(dest, len, timer);
878
879         return timer;
880 }
881
882 void procheader_date_get_localtime(gchar *dest, gint len, const time_t timer)
883 {
884         struct tm *lt;
885         gchar *default_format = "%y/%m/%d(%a) %H:%M";
886
887         lt = localtime(&timer);
888
889         if (prefs_common.date_format)
890                 strftime(dest, len, prefs_common.date_format, lt);
891         else
892                 strftime(dest, len, default_format, lt);
893 }
894
895 gint get_header_from_msginfo(MsgInfo *msginfo, gchar *buf, gint len, gchar *header)
896 {
897         gchar *file;
898         FILE *fp;
899         HeaderEntry hentry[]={ { header, NULL, TRUE  },
900                                { NULL,   NULL, FALSE } };
901         gint val;
902        
903         g_return_val_if_fail(msginfo != NULL, -1);
904         file = procmsg_get_message_file_path(msginfo);
905         if ((fp = fopen(file, "rb")) == NULL) {
906                FILE_OP_ERROR(file, "fopen");
907                g_free(file);
908                return -1;
909         }
910         val = procheader_get_one_field(buf,len, fp, hentry);
911         if (fclose(fp) == EOF) {
912                 FILE_OP_ERROR(file, "fclose");
913                 unlink(file);
914                 g_free(file);
915                 return -1;
916         }
917
918         g_free(file);
919         if (val == -1)
920                 return -1;
921
922         return 0;
923 }