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