2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2014 Hiroyuki Yamamoto and the Claws Mail team
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 3 of the License, or
8 * (at your option) any later version.
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.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "claws-features.h"
26 #include <glib/gi18n.h>
37 #include "procheader.h"
40 #include "prefs_common.h"
47 static gchar monthstr
[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
49 typedef char *(*getlinefunc
) (char *, size_t, void *);
50 typedef int (*peekcharfunc
) (void *);
51 typedef int (*getcharfunc
) (void *);
52 typedef gint (*get_one_field_func
) (gchar
*, size_t, void *, HeaderEntry
[]);
54 static gint
string_get_one_field(gchar
*buf
, size_t len
, char **str
,
55 HeaderEntry hentry
[]);
57 static char *string_getline(char *buf
, size_t len
, char **str
);
58 static int string_peekchar(char **str
);
59 static int file_peekchar(FILE *fp
);
60 static gint
generic_get_one_field(gchar
*buf
, size_t len
, void *data
,
63 peekcharfunc peekchar
,
65 static MsgInfo
*parse_stream(void *data
, gboolean isstring
, MsgFlags flags
,
66 gboolean full
, gboolean decrypted
);
69 gint
procheader_get_one_field(gchar
*buf
, size_t len
, FILE *fp
,
72 return generic_get_one_field(buf
, len
, fp
, hentry
,
73 (getlinefunc
)fgets_crlf
, (peekcharfunc
)file_peekchar
,
77 static gint
string_get_one_field(gchar
*buf
, size_t len
, char **str
,
80 return generic_get_one_field(buf
, len
, str
, hentry
,
81 (getlinefunc
)string_getline
,
82 (peekcharfunc
)string_peekchar
,
86 static char *string_getline(char *buf
, size_t len
, char **str
)
88 gboolean is_cr
= FALSE
;
89 gboolean last_was_cr
= FALSE
;
94 for (; **str
&& len
> 1; --len
) {
95 is_cr
= (**str
== '\r');
96 if ((*buf
++ = *(*str
)++) == '\n') {
112 static int string_peekchar(char **str
)
117 static int file_peekchar(FILE *fp
)
119 return ungetc(getc(fp
), fp
);
122 static gint
generic_get_one_field(gchar
*buf
, size_t len
, void *data
,
124 getlinefunc getline
, peekcharfunc peekchar
,
129 HeaderEntry
*hp
= NULL
;
131 if (hentry
!= NULL
) {
132 /* skip non-required headers */
135 if (getline(buf
, len
, data
) == NULL
)
137 if (buf
[0] == '\r' || buf
[0] == '\n')
139 } while (buf
[0] == ' ' || buf
[0] == '\t');
141 for (hp
= hentry
, hnum
= 0; hp
->name
!= NULL
;
143 if (!g_ascii_strncasecmp(hp
->name
, buf
,
147 } while (hp
->name
== NULL
);
149 if (getline(buf
, len
, data
) == NULL
) return -1;
150 if (buf
[0] == '\r' || buf
[0] == '\n') return -1;
155 nexthead
= peekchar(data
);
156 /* ([*WSP CRLF] 1*WSP) */
157 if (nexthead
== ' ' || nexthead
== '\t') {
159 gboolean skiptab
= (nexthead
== '\t');
160 /* trim previous trailing \n if requesting one header or
161 * unfolding was requested */
162 if ((!hentry
&& unfold
) || (hp
&& hp
->unfold
))
165 buflen
= strlen(buf
);
167 /* concatenate next line */
168 if ((len
- buflen
) > 2) {
169 if (getline(buf
+ buflen
, len
- buflen
, data
) == NULL
)
171 if (skiptab
) { /* replace tab with space */
172 *(buf
+ buflen
) = ' ';
177 /* remove trailing new line */
186 gint
procheader_get_one_field_asis(gchar
*buf
, size_t len
, FILE *fp
)
188 return generic_get_one_field(buf
, len
, fp
, NULL
,
189 (getlinefunc
)fgets_crlf
,
190 (peekcharfunc
)file_peekchar
,
194 GPtrArray
*procheader_get_header_array_asis(FILE *fp
)
200 cm_return_val_if_fail(fp
!= NULL
, NULL
);
202 headers
= g_ptr_array_new();
204 while (procheader_get_one_field_asis(buf
, sizeof(buf
), fp
) != -1) {
205 if ((header
= procheader_parse_header(buf
)) != NULL
)
206 g_ptr_array_add(headers
, header
);
212 void procheader_header_array_destroy(GPtrArray
*harray
)
217 for (i
= 0; i
< harray
->len
; i
++) {
218 header
= g_ptr_array_index(harray
, i
);
219 procheader_header_free(header
);
222 g_ptr_array_free(harray
, TRUE
);
225 void procheader_header_free(Header
*header
)
229 g_free(header
->name
);
230 g_free(header
->body
);
235 tests whether two headers' names are equal
236 remove the trailing ':' or ' ' before comparing
239 gboolean
procheader_headername_equal(char * hdr1
, char * hdr2
)
246 if (hdr1
[len1
- 1] == ':')
248 if (hdr2
[len2
- 1] == ':')
253 return (g_ascii_strncasecmp(hdr1
, hdr2
, len1
) == 0);
257 parse headers, for example :
258 From: dinh@enseirb.fr becomes :
259 header->name = "From:"
260 header->body = "dinh@enseirb.fr"
262 static gboolean
header_is_addr_field(const gchar
*hdr
)
264 static char *addr_headers
[] = {
271 "Followup-and-Reply-To:",
272 "Disposition-Notification-To:",
273 "Return-Receipt-To:",
280 for (i
= 0; addr_headers
[i
] != NULL
; i
++)
281 if (!strcasecmp(hdr
, addr_headers
[i
]))
287 Header
* procheader_parse_header(gchar
* buf
)
291 gboolean addr_field
= FALSE
;
293 if ((*buf
== ':') || (*buf
== ' '))
296 for (p
= buf
; *p
; p
++) {
297 if ((*p
== ':') || (*p
== ' ')) {
298 header
= g_new(Header
, 1);
299 header
->name
= g_strndup(buf
, p
- buf
+ 1);
300 addr_field
= header_is_addr_field(header
->name
);
302 while (*p
== ' ' || *p
== '\t') p
++;
303 header
->body
= conv_unmime_header(p
, NULL
, addr_field
);
310 void procheader_get_header_fields(FILE *fp
, HeaderEntry hentry
[])
317 if (hentry
== NULL
) return;
319 while ((hnum
= procheader_get_one_field(buf
, sizeof(buf
), fp
, hentry
))
323 p
= buf
+ strlen(hp
->name
);
324 while (*p
== ' ' || *p
== '\t') p
++;
326 if (hp
->body
== NULL
)
327 hp
->body
= g_strdup(p
);
328 else if (procheader_headername_equal(hp
->name
, "To") ||
329 procheader_headername_equal(hp
->name
, "Cc")) {
330 gchar
*tp
= hp
->body
;
331 hp
->body
= g_strconcat(tp
, ", ", p
, NULL
);
337 MsgInfo
*procheader_parse_file(const gchar
*file
, MsgFlags flags
,
338 gboolean full
, gboolean decrypted
)
344 if (g_stat(file
, &s
) < 0) {
345 FILE_OP_ERROR(file
, "stat");
348 if (!S_ISREG(s
.st_mode
))
351 if ((fp
= g_fopen(file
, "rb")) == NULL
) {
352 FILE_OP_ERROR(file
, "fopen");
356 msginfo
= procheader_parse_stream(fp
, flags
, full
, decrypted
);
360 msginfo
->size
= s
.st_size
;
361 msginfo
->mtime
= s
.st_mtime
;
367 MsgInfo
*procheader_parse_str(const gchar
*str
, MsgFlags flags
, gboolean full
,
370 return parse_stream(&str
, TRUE
, flags
, full
, decrypted
);
388 H_SC_PLANNED_DOWNLOAD
,
392 H_DISPOSITION_NOTIFICATION_TO
,
394 H_SC_PARTIALLY_RETRIEVED
,
406 static HeaderEntry hentry_full
[] = {
407 {"Date:", NULL
, FALSE
},
408 {"From:", NULL
, TRUE
},
411 {"Newsgroups:", NULL
, TRUE
},
412 {"Subject:", NULL
, TRUE
},
413 {"Message-ID:", NULL
, FALSE
},
414 {"References:", NULL
, FALSE
},
415 {"In-Reply-To:", NULL
, FALSE
},
416 {"Content-Type:", NULL
, FALSE
},
417 {"Seen:", NULL
, FALSE
},
418 {"Status:", NULL
, FALSE
},
419 {"From ", NULL
, FALSE
},
420 {"SC-Marked-For-Download:", NULL
, FALSE
},
421 {"SC-Message-Size:", NULL
, FALSE
},
422 {"Face:", NULL
, FALSE
},
423 {"X-Face:", NULL
, FALSE
},
424 {"Disposition-Notification-To:", NULL
, FALSE
},
425 {"Return-Receipt-To:", NULL
, FALSE
},
426 {"SC-Partially-Retrieved:", NULL
, FALSE
},
427 {"SC-Account-Server:", NULL
, FALSE
},
428 {"SC-Account-Login:",NULL
, FALSE
},
429 {"List-Post:", NULL
, TRUE
},
430 {"List-Subscribe:", NULL
, TRUE
},
431 {"List-Unsubscribe:",NULL
, TRUE
},
432 {"List-Help:", NULL
, TRUE
},
433 {"List-Archive:", NULL
, TRUE
},
434 {"List-Owner:", NULL
, TRUE
},
435 {"Resent-From:", NULL
, TRUE
},
436 {NULL
, NULL
, FALSE
}};
438 static HeaderEntry hentry_short
[] = {
439 {"Date:", NULL
, FALSE
},
440 {"From:", NULL
, TRUE
},
443 {"Newsgroups:", NULL
, TRUE
},
444 {"Subject:", NULL
, TRUE
},
445 {"Message-ID:", NULL
, FALSE
},
446 {"References:", NULL
, FALSE
},
447 {"In-Reply-To:", NULL
, FALSE
},
448 {"Content-Type:", NULL
, FALSE
},
449 {"Seen:", NULL
, FALSE
},
450 {"Status:", NULL
, FALSE
},
451 {"From ", NULL
, FALSE
},
452 {"SC-Marked-For-Download:", NULL
, FALSE
},
453 {"SC-Message-Size:",NULL
, FALSE
},
454 {NULL
, NULL
, FALSE
}};
456 static HeaderEntry
* procheader_get_headernames(gboolean full
)
458 return full
? hentry_full
: hentry_short
;
461 MsgInfo
*procheader_parse_stream(FILE *fp
, MsgFlags flags
, gboolean full
,
464 return parse_stream(fp
, FALSE
, flags
, full
, decrypted
);
467 static gboolean
avatar_from_some_face(gpointer source
, gpointer userdata
)
469 AvatarCaptureData
*acd
= (AvatarCaptureData
*)source
;
471 if (*(acd
->content
) == '\0') /* won't be null, but may be empty */
474 if (!strcmp(acd
->header
, hentry_full
[H_FACE
].name
)) {
475 debug_print("avatar_from_some_face: found 'Face' header\n");
476 procmsg_msginfo_add_avatar(acd
->msginfo
, AVATAR_FACE
, acd
->content
);
479 else if (!strcmp(acd
->header
, hentry_full
[H_X_FACE
].name
)) {
480 debug_print("avatar_from_some_face: found 'X-Face' header\n");
481 procmsg_msginfo_add_avatar(acd
->msginfo
, AVATAR_XFACE
, acd
->content
);
487 static guint avatar_hook_id
= 0;
489 static MsgInfo
*parse_stream(void *data
, gboolean isstring
, MsgFlags flags
,
490 gboolean full
, gboolean decrypted
)
498 void *orig_data
= data
;
500 get_one_field_func get_one_field
=
501 isstring
? (get_one_field_func
)string_get_one_field
502 : (get_one_field_func
)procheader_get_one_field
;
504 hentry
= procheader_get_headernames(full
);
506 if (MSG_IS_QUEUED(flags
) || MSG_IS_DRAFT(flags
)) {
507 while (get_one_field(buf
, sizeof(buf
), data
, NULL
) != -1) {
508 if ((!strncmp(buf
, "X-Claws-End-Special-Headers: 1",
509 strlen("X-Claws-End-Special-Headers:"))) ||
510 (!strncmp(buf
, "X-Sylpheed-End-Special-Headers: 1",
511 strlen("X-Sylpheed-End-Special-Headers:"))))
513 /* from other mailers */
514 if (!strncmp(buf
, "Date: ", 6)
515 || !strncmp(buf
, "To: ", 4)
516 || !strncmp(buf
, "From: ", 6)
517 || !strncmp(buf
, "Subject: ", 9)) {
521 rewind((FILE *)data
);
527 msginfo
= procmsg_msginfo_new();
529 if (flags
.tmp_flags
|| flags
.perm_flags
)
530 msginfo
->flags
= flags
;
532 MSG_SET_PERM_FLAGS(msginfo
->flags
, MSG_NEW
| MSG_UNREAD
);
534 msginfo
->inreplyto
= NULL
;
536 if (avatar_hook_id
== 0 && (prefs_common
.enable_avatars
& AVATARS_ENABLE_CAPTURE
)) {
537 avatar_hook_id
= hooks_register_hook(AVATAR_HEADER_UPDATE_HOOKLIST
, avatar_from_some_face
, NULL
);
538 } else if (avatar_hook_id
!= 0 && !(prefs_common
.enable_avatars
& AVATARS_ENABLE_CAPTURE
)) {
539 hooks_unregister_hook(AVATAR_HEADER_UPDATE_HOOKLIST
, avatar_hook_id
);
543 while ((hnum
= get_one_field(buf
, sizeof(buf
), data
, hentry
))
545 hp
= buf
+ strlen(hentry
[hnum
].name
);
546 while (*hp
== ' ' || *hp
== '\t') hp
++;
550 if (msginfo
->date
) break;
552 procheader_date_parse(NULL
, hp
, 0);
553 if (g_utf8_validate(hp
, -1, NULL
)) {
554 msginfo
->date
= g_strdup(hp
);
556 gchar
*utf
= conv_codeset_strdup(
558 conv_get_locale_charset_str_no_utf8(),
561 !g_utf8_validate(utf
, -1, NULL
)) {
563 utf
= g_malloc(strlen(buf
)*2+1);
564 conv_localetodisp(utf
,
571 if (msginfo
->from
) break;
572 msginfo
->from
= conv_unmime_header(hp
, NULL
, TRUE
);
573 msginfo
->fromname
= procheader_get_fromname(msginfo
->from
);
574 remove_return(msginfo
->from
);
575 remove_return(msginfo
->fromname
);
578 tmp
= conv_unmime_header(hp
, NULL
, TRUE
);
583 g_strconcat(p
, ", ", tmp
, NULL
);
586 msginfo
->to
= g_strdup(tmp
);
590 tmp
= conv_unmime_header(hp
, NULL
, TRUE
);
595 g_strconcat(p
, ", ", tmp
, NULL
);
598 msginfo
->cc
= g_strdup(tmp
);
602 if (msginfo
->newsgroups
) {
603 p
= msginfo
->newsgroups
;
604 msginfo
->newsgroups
=
605 g_strconcat(p
, ",", hp
, NULL
);
608 msginfo
->newsgroups
= g_strdup(hp
);
611 if (msginfo
->subject
) break;
612 msginfo
->subject
= conv_unmime_header(hp
, NULL
, FALSE
);
613 unfold_line(msginfo
->subject
);
616 if (msginfo
->msgid
) break;
618 extract_parenthesis(hp
, '<', '>');
620 msginfo
->msgid
= g_strdup(hp
);
623 msginfo
->references
=
624 references_list_prepend(msginfo
->references
,
628 if (msginfo
->inreplyto
) break;
630 eliminate_parenthesis(hp
, '(', ')');
631 if ((p
= strrchr(hp
, '<')) != NULL
&&
632 strchr(p
+ 1, '>') != NULL
) {
633 extract_parenthesis(p
, '<', '>');
636 msginfo
->inreplyto
= g_strdup(p
);
640 if (!g_ascii_strncasecmp(hp
, "multipart/", 10))
641 MSG_SET_TMP_FLAGS(msginfo
->flags
, MSG_MULTIPART
);
643 case H_DISPOSITION_NOTIFICATION_TO
:
644 if (!msginfo
->extradata
)
645 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
646 if (msginfo
->extradata
->dispositionnotificationto
) break;
647 msginfo
->extradata
->dispositionnotificationto
= g_strdup(hp
);
649 case H_RETURN_RECEIPT_TO
:
650 if (!msginfo
->extradata
)
651 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
652 if (msginfo
->extradata
->returnreceiptto
) break;
653 msginfo
->extradata
->returnreceiptto
= g_strdup(hp
);
655 /* partial download infos */
656 case H_SC_PARTIALLY_RETRIEVED
:
657 if (!msginfo
->extradata
)
658 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
659 if (msginfo
->extradata
->partial_recv
) break;
660 msginfo
->extradata
->partial_recv
= g_strdup(hp
);
662 case H_SC_ACCOUNT_SERVER
:
663 if (!msginfo
->extradata
)
664 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
665 if (msginfo
->extradata
->account_server
) break;
666 msginfo
->extradata
->account_server
= g_strdup(hp
);
668 case H_SC_ACCOUNT_LOGIN
:
669 if (!msginfo
->extradata
)
670 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
671 if (msginfo
->extradata
->account_login
) break;
672 msginfo
->extradata
->account_login
= g_strdup(hp
);
674 case H_SC_MESSAGE_SIZE
:
675 if (msginfo
->total_size
) break;
676 msginfo
->total_size
= atoi(hp
);
678 case H_SC_PLANNED_DOWNLOAD
:
679 msginfo
->planned_download
= atoi(hp
);
681 /* end partial download infos */
683 if (msginfo
->fromspace
) break;
684 msginfo
->fromspace
= g_strdup(hp
);
685 remove_return(msginfo
->fromspace
);
689 if (!msginfo
->extradata
)
690 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
691 if (msginfo
->extradata
->list_post
) break;
692 msginfo
->extradata
->list_post
= g_strdup(hp
);
694 case H_LIST_SUBSCRIBE
:
695 if (!msginfo
->extradata
)
696 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
697 if (msginfo
->extradata
->list_subscribe
) break;
698 msginfo
->extradata
->list_subscribe
= g_strdup(hp
);
700 case H_LIST_UNSUBSCRIBE
:
701 if (!msginfo
->extradata
)
702 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
703 if (msginfo
->extradata
->list_unsubscribe
) break;
704 msginfo
->extradata
->list_unsubscribe
= g_strdup(hp
);
707 if (!msginfo
->extradata
)
708 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
709 if (msginfo
->extradata
->list_help
) break;
710 msginfo
->extradata
->list_help
= g_strdup(hp
);
713 if (!msginfo
->extradata
)
714 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
715 if (msginfo
->extradata
->list_archive
) break;
716 msginfo
->extradata
->list_archive
= g_strdup(hp
);
719 if (!msginfo
->extradata
)
720 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
721 if (msginfo
->extradata
->list_owner
) break;
722 msginfo
->extradata
->list_owner
= g_strdup(hp
);
725 if (!msginfo
->extradata
)
726 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
727 if (msginfo
->extradata
->resent_from
) break;
728 msginfo
->extradata
->resent_from
= g_strdup(hp
);
734 /* to avoid performance penalty hooklist is invoked only for
735 headers known to be able to generate avatars */
736 if (hnum
== H_FROM
|| hnum
== H_X_FACE
|| hnum
== H_FACE
) {
737 AvatarCaptureData
*acd
= g_new0(AvatarCaptureData
, 1);
738 /* no extra memory is wasted, hooks are expected to
739 take care of copying members when needed */
740 acd
->msginfo
= msginfo
;
741 acd
->header
= hentry_full
[hnum
].name
;
743 hooks_invoke(AVATAR_HEADER_UPDATE_HOOKLIST
, (gpointer
)acd
);
748 if (!msginfo
->inreplyto
&& msginfo
->references
)
750 g_strdup((gchar
*)msginfo
->references
->data
);
755 gchar
*procheader_get_fromname(const gchar
*str
)
759 Xstrdup_a(tmp
, str
, return NULL
);
762 extract_quote(tmp
, '\"');
764 } else if (strchr(tmp
, '<')) {
765 eliminate_parenthesis(tmp
, '<', '>');
769 extract_parenthesis(tmp
, '<', '>');
772 } else if (strchr(tmp
, '(')) {
773 extract_parenthesis(tmp
, '(', ')');
778 name
= g_strdup(str
);
780 name
= g_strdup(tmp
);
785 static gint
procheader_scan_date_string(const gchar
*str
,
786 gchar
*weekday
, gint
*day
,
787 gchar
*month
, gint
*year
,
788 gint
*hh
, gint
*mm
, gint
*ss
,
794 gint zone1
= 0, zone2
= 0;
795 gchar offset_sign
, zonestr
[7];
801 result
= sscanf(str
, "%10s %d %9s %d %2d:%2d:%2d %6s",
802 weekday
, day
, month
, year
, hh
, mm
, ss
, zone
);
803 if (result
== 8) return 0;
806 result
= sscanf(str
, "%3s,%d %9s %d %2d:%2d:%2d %6s",
807 weekday
, day
, month
, year
, hh
, mm
, ss
, zone
);
808 if (result
== 8) return 0;
810 result
= sscanf(str
, "%d %9s %d %2d:%2d:%2d %6s",
811 day
, month
, year
, hh
, mm
, ss
, zone
);
812 if (result
== 7) return 0;
815 result
= sscanf(str
, "%10s %d %9s %d %2d:%2d:%2d",
816 weekday
, day
, month
, year
, hh
, mm
, ss
);
817 if (result
== 7) return 0;
819 result
= sscanf(str
, "%d %9s %d %2d:%2d:%2d",
820 day
, month
, year
, hh
, mm
, ss
);
821 if (result
== 6) return 0;
824 result
= sscanf(str
, "%10s %d %9s %d %2d:%2d %6s",
825 weekday
, day
, month
, year
, hh
, mm
, zone
);
826 if (result
== 7) return 0;
828 result
= sscanf(str
, "%d %9s %d %2d:%2d %5s",
829 day
, month
, year
, hh
, mm
, zone
);
830 if (result
== 6) return 0;
833 result
= sscanf(str
, "%10s %d %9s %d %2d:%2d",
834 weekday
, day
, month
, year
, hh
, mm
);
835 if (result
== 6) return 0;
837 result
= sscanf(str
, "%d %9s %d %2d:%2d",
838 day
, month
, year
, hh
, mm
);
839 if (result
== 5) return 0;
843 /* RFC3339 subset, with fraction of second */
844 result
= sscanf(str
, "%4d-%2d-%2d%c%2d:%2d:%2d.%d%6s",
845 year
, &month_n
, day
, &sep1
, hh
, mm
, ss
, &secfract
, zonestr
);
847 && (sep1
== 'T' || sep1
== 't' || sep1
== ' ')) {
848 if (month_n
>= 1 && month_n
<= 12) {
849 strncpy2(month
, monthstr
+((month_n
-1)*3), 4);
850 if (zonestr
[0] == 'z' || zonestr
[0] == 'Z') {
851 strcat(zone
, "+00:00");
852 } else if (sscanf(zonestr
, "%c%2d:%2d",
853 &offset_sign
, &zone1
, &zone2
) == 3) {
854 strcat(zone
, zonestr
);
860 /* RFC3339 subset, no fraction of second */
861 result
= sscanf(str
, "%4d-%2d-%2d%c%2d:%2d:%2d%6s",
862 year
, &month_n
, day
, &sep1
, hh
, mm
, ss
, zonestr
);
864 && (sep1
== 'T' || sep1
== 't' || sep1
== ' ')) {
865 if (month_n
>= 1 && month_n
<= 12) {
866 strncpy2(month
, monthstr
+((month_n
-1)*3), 4);
867 if (zonestr
[0] == 'z' || zonestr
[0] == 'Z') {
868 strcat(zone
, "+00:00");
869 } else if (sscanf(zonestr
, "%c%2d:%2d",
870 &offset_sign
, &zone1
, &zone2
) == 3) {
871 strcat(zone
, zonestr
);
880 /* This particular "subset" is invalid, RFC requires the time offset */
881 result
= sscanf(str
, "%4d-%2d-%2d %2d:%2d:%2d",
882 year
, &month_n
, day
, hh
, mm
, ss
);
884 if (1 <= month_n
&& month_n
<= 12) {
885 strncpy2(month
, monthstr
+((month_n
-1)*3), 4);
894 * Hiro, most UNIXen support this function:
895 * http://www.mcsr.olemiss.edu/cgi-bin/man-cgi?getdate
897 gboolean
procheader_date_parse_to_tm(const gchar
*src
, struct tm
*t
, char *zone
)
910 memset(t
, 0, sizeof *t
);
912 if (procheader_scan_date_string(src
, weekday
, &day
, month
, &year
,
913 &hh
, &mm
, &ss
, zone
) < 0) {
914 g_warning("Invalid date: %s", src
);
918 /* Y2K compliant :) */
927 if ((p
= strstr(monthstr
, month
)) != NULL
)
928 dmonth
= (gint
)(p
- monthstr
) / 3 + 1;
930 g_warning("Invalid month: %s", month
);
931 dmonth
= G_DATE_BAD_MONTH
;
938 t
->tm_mon
= dmonth
- 1;
939 t
->tm_year
= year
- 1900;
949 time_t procheader_date_parse(gchar
*dest
, const gchar
*src
, gint len
)
957 GDateMonth dmonth
= G_DATE_BAD_MONTH
;
961 if (procheader_scan_date_string(src
, weekday
, &day
, month
, &year
,
962 &hh
, &mm
, &ss
, zone
) < 0) {
964 strncpy2(dest
, src
, len
);
969 for (p
= monthstr
; *p
!= '\0'; p
+= 3) {
970 if (!g_ascii_strncasecmp(p
, month
, 3)) {
971 dmonth
= (gint
)(p
- monthstr
) / 3 + 1;
980 tz
= g_time_zone_new(zone
);
981 dt
= g_date_time_new(tz
, year
, dmonth
, day
, hh
, mm
, ss
);
983 timer
= g_date_time_to_unix(dt
);
985 g_date_time_unref(dt
);
986 g_time_zone_unref(tz
);
992 /* Y2K compliant :) */
1004 t
.tm_mon
= dmonth
- 1;
1005 t
.tm_year
= year
- 1900;
1011 tz_offset
= remote_tzoffset_sec(zone
);
1012 if (tz_offset
!= -1)
1013 timer
+= tzoffset_sec(&timer
) - tz_offset
;
1016 procheader_date_get_localtime(dest
, len
, timer
);
1022 void procheader_date_get_localtime(gchar
*dest
, gint len
, const time_t timer
)
1025 gchar
*default_format
= "%y/%m/%d(%a) %H:%M";
1027 const gchar
*src_codeset
, *dest_codeset
;
1031 lt
= localtime_r(&timer
, &buf
);
1034 lt
= localtime_r(&dummy
, &buf
);
1037 if (prefs_common
.date_format
)
1038 fast_strftime(dest
, len
, prefs_common
.date_format
, lt
);
1040 fast_strftime(dest
, len
, default_format
, lt
);
1042 if (!g_utf8_validate(dest
, -1, NULL
)) {
1043 src_codeset
= conv_get_locale_charset_str_no_utf8();
1044 dest_codeset
= CS_UTF_8
;
1045 str
= conv_codeset_strdup(dest
, src_codeset
, dest_codeset
);
1047 strncpy2(dest
, str
, len
);
1053 /* Added by Mel Hadasht on 27 Aug 2001 */
1054 /* Get a header from msginfo */
1055 gint
procheader_get_header_from_msginfo(MsgInfo
*msginfo
, gchar
*buf
, gint len
, gchar
*header
)
1059 HeaderEntry hentry
[]={ { NULL
, NULL
, TRUE
},
1060 { NULL
, NULL
, FALSE
} };
1063 hentry
[0].name
= header
;
1065 cm_return_val_if_fail(msginfo
!= NULL
, -1);
1066 file
= procmsg_get_message_file_path(msginfo
);
1067 if ((fp
= g_fopen(file
, "rb")) == NULL
) {
1068 FILE_OP_ERROR(file
, "fopen");
1072 val
= procheader_get_one_field(buf
,len
, fp
, hentry
);
1073 if (fclose(fp
) == EOF
) {
1074 FILE_OP_ERROR(file
, "fclose");
1087 HeaderEntry
*procheader_entries_from_str(const gchar
*str
)
1089 HeaderEntry
*entries
= NULL
, *he
;
1090 int numh
= 0, i
= 0;
1091 gchar
**names
= NULL
;
1092 const gchar
*s
= str
;
1097 while (*s
!= '\0') {
1098 if (*s
== ' ') ++numh
;
1104 entries
= g_new0(HeaderEntry
, numh
+ 1); /* room for last NULL */
1106 ++s
; /* skip first space */
1107 names
= g_strsplit(s
, " ", numh
);
1110 he
->name
= g_strdup_printf("%s:", names
[i
]);
1120 void procheader_entries_free (HeaderEntry
*entries
)
1122 if (entries
!= NULL
) {
1123 HeaderEntry
*he
= entries
;
1124 while (he
->name
!= NULL
) {
1126 if (he
->body
!= NULL
)
1134 gboolean
procheader_header_is_internal(const gchar
*hdr_name
)
1136 const gchar
*internal_hdrs
[] = {
1137 "AF:", "NF:", "PS:", "SRH:", "SFN:", "DSR:", "MID:", "CFG:",
1138 "PT:", "S:", "RQ:", "SSV:", "NSV:", "SSH:", "R:", "MAID:",
1139 "SCF:", "RMID:", "FMID:", "NAID:",
1140 "X-Claws-Account-Id:",
1143 "X-Claws-Privacy-System:",
1144 "X-Claws-Auto-Wrapping:",
1145 "X-Claws-Auto-Indent:",
1146 "X-Claws-End-Special-Headers:",
1147 "X-Sylpheed-Account-Id:",
1149 "X-Sylpheed-Encrypt:",
1150 "X-Sylpheed-Privacy-System:",
1151 "X-Sylpheed-End-Special-Headers:",
1156 for (i
= 0; internal_hdrs
[i
]; i
++) {
1157 if (!strcmp(hdr_name
, internal_hdrs
[i
]))