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