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"
44 #include "file-utils.h"
48 static gchar monthstr
[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
50 typedef char *(*getlinefunc
) (char *, size_t, void *);
51 typedef int (*peekcharfunc
) (void *);
52 typedef int (*getcharfunc
) (void *);
53 typedef gint (*get_one_field_func
) (gchar
**, void *, HeaderEntry
[]);
55 static gint
string_get_one_field(gchar
**buf
, char **str
,
56 HeaderEntry hentry
[]);
58 static char *string_getline(char *buf
, size_t len
, char **str
);
59 static int string_peekchar(char **str
);
60 static int file_peekchar(FILE *fp
);
61 static gint
generic_get_one_field(gchar
**bufptr
, void *data
,
64 peekcharfunc peekchar
,
66 static MsgInfo
*parse_stream(void *data
, gboolean isstring
, MsgFlags flags
,
67 gboolean full
, gboolean decrypted
);
70 gint
procheader_get_one_field(gchar
**buf
, FILE *fp
,
73 return generic_get_one_field(buf
, fp
, hentry
,
74 (getlinefunc
)fgets_crlf
, (peekcharfunc
)file_peekchar
,
78 static gint
string_get_one_field(gchar
**buf
, char **str
,
81 return generic_get_one_field(buf
, str
, hentry
,
82 (getlinefunc
)string_getline
,
83 (peekcharfunc
)string_peekchar
,
87 gboolean
procheader_skip_headers(FILE *fp
)
89 gchar
*buf
= g_malloc(BUFFSIZE
);
91 if (fgets_crlf(buf
, BUFFSIZE
- 1, fp
) == NULL
) {
95 if (buf
[0] == '\r' || buf
[0] == '\n') {
105 static char *string_getline(char *buf
, size_t len
, char **str
)
107 gboolean is_cr
= FALSE
;
108 gboolean last_was_cr
= FALSE
;
113 for (; **str
&& len
> 1; --len
) {
114 is_cr
= (**str
== '\r');
115 if ((*buf
++ = *(*str
)++) == '\n') {
131 static int string_peekchar(char **str
)
136 static int file_peekchar(FILE *fp
)
138 return ungetc(getc(fp
), fp
);
141 static gint
generic_get_one_field(gchar
**bufptr
, void *data
,
143 getlinefunc getline
, peekcharfunc peekchar
,
146 /* returns -1 in case of failure of any kind, whatever it's a parsing error
147 or an allocation error. if returns -1, *bufptr is always NULL, and vice-versa,
148 and if returning 0 (OK), *bufptr is always non-NULL, so callers just have to
149 test the return value
153 HeaderEntry
*hp
= NULL
;
157 cm_return_val_if_fail(bufptr
!= NULL
, -1);
162 if (hentry
!= NULL
) {
163 /* skip non-required headers */
164 /* and get hentry header line */
167 if (getline(buf
, len
, data
) == NULL
) {
168 debug_print("generic_get_one_field: getline\n");
173 if (buf
[0] == '\r' || buf
[0] == '\n') {
174 debug_print("generic_get_one_field: empty line\n");
179 } while (buf
[0] == ' ' || buf
[0] == '\t');
181 for (hp
= hentry
, hnum
= 0; hp
->name
!= NULL
;
183 if (!g_ascii_strncasecmp(hp
->name
, buf
,
187 } while (hp
->name
== NULL
);
189 /* read first line */
190 if (getline(buf
, len
, data
) == NULL
) {
191 debug_print("generic_get_one_field: getline\n");
196 if (buf
[0] == '\r' || buf
[0] == '\n') {
197 debug_print("generic_get_one_field: empty line\n");
203 /* reduce initial buffer to its useful part */
205 buf
= g_realloc(buf
, len
);
207 debug_print("generic_get_one_field: reallocation error\n");
214 nexthead
= peekchar(data
);
215 /* ([*WSP CRLF] 1*WSP) */
216 if (nexthead
== ' ' || nexthead
== '\t') {
221 gboolean skiptab
= (nexthead
== '\t');
222 /* trim previous trailing \n if requesting one header or
223 * unfolding was requested */
224 if ((!hentry
&& unfold
) || (hp
&& hp
->unfold
))
227 buflen
= strlen(buf
);
230 tmpbuf
= g_malloc(BUFFSIZE
);
232 if (getline(tmpbuf
, BUFFSIZE
, data
) == NULL
) {
236 tmplen
= strlen(tmpbuf
)+1;
238 /* extend initial buffer and concatenate next line */
240 buf
= g_realloc(buf
, len
);
242 debug_print("generic_get_one_field: reallocation error\n");
247 memcpy(buf
+buflen
, tmpbuf
, tmplen
);
249 if (skiptab
) { /* replace tab with space */
250 *(buf
+ buflen
) = ' ';
253 /* remove trailing new line */
264 gint
procheader_get_one_field_asis(gchar
**buf
, FILE *fp
)
266 return generic_get_one_field(buf
, fp
, NULL
,
267 (getlinefunc
)fgets_crlf
,
268 (peekcharfunc
)file_peekchar
,
272 GPtrArray
*procheader_get_header_array_asis(FILE *fp
)
278 cm_return_val_if_fail(fp
!= NULL
, NULL
);
280 headers
= g_ptr_array_new();
282 while (procheader_get_one_field_asis(&buf
, fp
) != -1) {
283 if ((header
= procheader_parse_header(buf
)) != NULL
)
284 g_ptr_array_add(headers
, header
);
292 void procheader_header_array_destroy(GPtrArray
*harray
)
297 cm_return_if_fail(harray
!= NULL
);
299 for (i
= 0; i
< harray
->len
; i
++) {
300 header
= g_ptr_array_index(harray
, i
);
301 procheader_header_free(header
);
304 g_ptr_array_free(harray
, TRUE
);
307 void procheader_header_free(Header
*header
)
311 g_free(header
->name
);
312 g_free(header
->body
);
317 tests whether two headers' names are equal
318 remove the trailing ':' or ' ' before comparing
321 gboolean
procheader_headername_equal(char * hdr1
, char * hdr2
)
328 if (hdr1
[len1
- 1] == ':')
330 if (hdr2
[len2
- 1] == ':')
335 return (g_ascii_strncasecmp(hdr1
, hdr2
, len1
) == 0);
339 parse headers, for example :
340 From: dinh@enseirb.fr becomes :
341 header->name = "From:"
342 header->body = "dinh@enseirb.fr"
344 static gboolean
header_is_addr_field(const gchar
*hdr
)
346 static char *addr_headers
[] = {
353 "Followup-and-Reply-To:",
354 "Disposition-Notification-To:",
355 "Return-Receipt-To:",
362 for (i
= 0; addr_headers
[i
] != NULL
; i
++)
363 if (!strcasecmp(hdr
, addr_headers
[i
]))
369 Header
* procheader_parse_header(gchar
* buf
)
373 gboolean addr_field
= FALSE
;
375 cm_return_val_if_fail(buf
!= NULL
, NULL
);
377 if ((*buf
== ':') || (*buf
== ' '))
380 for (p
= buf
; *p
; p
++) {
381 if ((*p
== ':') || (*p
== ' ')) {
382 header
= g_new(Header
, 1);
383 header
->name
= g_strndup(buf
, p
- buf
+ 1);
384 addr_field
= header_is_addr_field(header
->name
);
386 while (*p
== ' ' || *p
== '\t') p
++;
387 header
->body
= conv_unmime_header(p
, NULL
, addr_field
);
394 void procheader_get_header_fields(FILE *fp
, HeaderEntry hentry
[])
401 if (hentry
== NULL
) return;
403 while ((hnum
= procheader_get_one_field(&buf
, fp
, hentry
)) != -1) {
406 p
= buf
+ strlen(hp
->name
);
407 while (*p
== ' ' || *p
== '\t') p
++;
409 if (hp
->body
== NULL
)
410 hp
->body
= g_strdup(p
);
411 else if (procheader_headername_equal(hp
->name
, "To") ||
412 procheader_headername_equal(hp
->name
, "Cc")) {
413 gchar
*tp
= hp
->body
;
414 hp
->body
= g_strconcat(tp
, ", ", p
, NULL
);
422 MsgInfo
*procheader_parse_file(const gchar
*file
, MsgFlags flags
,
423 gboolean full
, gboolean decrypted
)
429 GError
*error
= NULL
;
437 f
= g_file_new_for_path(file
);
438 fi
= g_file_query_info(f
, "standard::size,standard::type,time::modified",
439 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
441 g_warning(error
->message
);
446 if (g_stat(file
, &s
) < 0) {
447 FILE_OP_ERROR(file
, "stat");
453 if (g_file_info_get_file_type(fi
) != G_FILE_TYPE_REGULAR
) {
459 if (!S_ISREG(s
.st_mode
))
463 if ((fp
= claws_fopen(file
, "rb")) == NULL
) {
464 FILE_OP_ERROR(file
, "claws_fopen");
468 msginfo
= procheader_parse_stream(fp
, flags
, full
, decrypted
);
473 msginfo
->size
= g_file_info_get_size(fi
);
474 g_file_info_get_modification_time(fi
, &tv
);
475 msginfo
->mtime
= tv
.tv_sec
;
477 msginfo
->size
= s
.st_size
;
478 msginfo
->mtime
= s
.st_mtime
;
490 MsgInfo
*procheader_parse_str(const gchar
*str
, MsgFlags flags
, gboolean full
,
493 return parse_stream(&str
, TRUE
, flags
, full
, decrypted
);
511 H_SC_PLANNED_DOWNLOAD
,
515 H_DISPOSITION_NOTIFICATION_TO
,
517 H_SC_PARTIALLY_RETRIEVED
,
529 static HeaderEntry hentry_full
[] = {
530 {"Date:", NULL
, FALSE
},
531 {"From:", NULL
, TRUE
},
534 {"Newsgroups:", NULL
, TRUE
},
535 {"Subject:", NULL
, TRUE
},
536 {"Message-ID:", NULL
, FALSE
},
537 {"References:", NULL
, FALSE
},
538 {"In-Reply-To:", NULL
, FALSE
},
539 {"Content-Type:", NULL
, FALSE
},
540 {"Seen:", NULL
, FALSE
},
541 {"Status:", NULL
, FALSE
},
542 {"From ", NULL
, FALSE
},
543 {"SC-Marked-For-Download:", NULL
, FALSE
},
544 {"SC-Message-Size:", NULL
, FALSE
},
545 {"Face:", NULL
, FALSE
},
546 {"X-Face:", NULL
, FALSE
},
547 {"Disposition-Notification-To:", NULL
, FALSE
},
548 {"Return-Receipt-To:", NULL
, FALSE
},
549 {"SC-Partially-Retrieved:", NULL
, FALSE
},
550 {"SC-Account-Server:", NULL
, FALSE
},
551 {"SC-Account-Login:",NULL
, FALSE
},
552 {"List-Post:", NULL
, TRUE
},
553 {"List-Subscribe:", NULL
, TRUE
},
554 {"List-Unsubscribe:",NULL
, TRUE
},
555 {"List-Help:", NULL
, TRUE
},
556 {"List-Archive:", NULL
, TRUE
},
557 {"List-Owner:", NULL
, TRUE
},
558 {"Resent-From:", NULL
, TRUE
},
559 {NULL
, NULL
, FALSE
}};
561 static HeaderEntry hentry_short
[] = {
562 {"Date:", NULL
, FALSE
},
563 {"From:", NULL
, TRUE
},
566 {"Newsgroups:", NULL
, TRUE
},
567 {"Subject:", NULL
, TRUE
},
568 {"Message-ID:", NULL
, FALSE
},
569 {"References:", NULL
, FALSE
},
570 {"In-Reply-To:", NULL
, FALSE
},
571 {"Content-Type:", NULL
, FALSE
},
572 {"Seen:", NULL
, FALSE
},
573 {"Status:", NULL
, FALSE
},
574 {"From ", NULL
, FALSE
},
575 {"SC-Marked-For-Download:", NULL
, FALSE
},
576 {"SC-Message-Size:",NULL
, FALSE
},
577 {NULL
, NULL
, FALSE
}};
579 static HeaderEntry
* procheader_get_headernames(gboolean full
)
581 return full
? hentry_full
: hentry_short
;
584 MsgInfo
*procheader_parse_stream(FILE *fp
, MsgFlags flags
, gboolean full
,
587 return parse_stream(fp
, FALSE
, flags
, full
, decrypted
);
590 static gboolean
avatar_from_some_face(gpointer source
, gpointer userdata
)
592 AvatarCaptureData
*acd
= (AvatarCaptureData
*)source
;
594 if (*(acd
->content
) == '\0') /* won't be null, but may be empty */
597 if (!strcmp(acd
->header
, hentry_full
[H_FACE
].name
)) {
598 debug_print("avatar_from_some_face: found 'Face' header\n");
599 procmsg_msginfo_add_avatar(acd
->msginfo
, AVATAR_FACE
, acd
->content
);
602 else if (!strcmp(acd
->header
, hentry_full
[H_X_FACE
].name
)) {
603 debug_print("avatar_from_some_face: found 'X-Face' header\n");
604 procmsg_msginfo_add_avatar(acd
->msginfo
, AVATAR_XFACE
, acd
->content
);
610 static gulong avatar_hook_id
= HOOK_NONE
;
612 static MsgInfo
*parse_stream(void *data
, gboolean isstring
, MsgFlags flags
,
613 gboolean full
, gboolean decrypted
)
621 void *orig_data
= data
;
623 get_one_field_func get_one_field
=
624 isstring
? (get_one_field_func
)string_get_one_field
625 : (get_one_field_func
)procheader_get_one_field
;
627 hentry
= procheader_get_headernames(full
);
629 if (MSG_IS_QUEUED(flags
) || MSG_IS_DRAFT(flags
)) {
630 while (get_one_field(&buf
, data
, NULL
) != -1) {
631 if ((!strncmp(buf
, "X-Claws-End-Special-Headers: 1",
632 strlen("X-Claws-End-Special-Headers:"))) ||
633 (!strncmp(buf
, "X-Sylpheed-End-Special-Headers: 1",
634 strlen("X-Sylpheed-End-Special-Headers:")))) {
639 /* from other mailers */
640 if (!strncmp(buf
, "Date: ", 6)
641 || !strncmp(buf
, "To: ", 4)
642 || !strncmp(buf
, "From: ", 6)
643 || !strncmp(buf
, "Subject: ", 9)) {
647 rewind((FILE *)data
);
657 msginfo
= procmsg_msginfo_new();
659 if (flags
.tmp_flags
|| flags
.perm_flags
)
660 msginfo
->flags
= flags
;
662 MSG_SET_PERM_FLAGS(msginfo
->flags
, MSG_NEW
| MSG_UNREAD
);
664 msginfo
->inreplyto
= NULL
;
666 if (avatar_hook_id
== HOOK_NONE
&& (prefs_common
.enable_avatars
& AVATARS_ENABLE_CAPTURE
)) {
667 avatar_hook_id
= hooks_register_hook(AVATAR_HEADER_UPDATE_HOOKLIST
, avatar_from_some_face
, NULL
);
668 } else if (avatar_hook_id
!= HOOK_NONE
&& !(prefs_common
.enable_avatars
& AVATARS_ENABLE_CAPTURE
)) {
669 hooks_unregister_hook(AVATAR_HEADER_UPDATE_HOOKLIST
, avatar_hook_id
);
670 avatar_hook_id
= HOOK_NONE
;
673 while ((hnum
= get_one_field(&buf
, data
, hentry
)) != -1) {
674 hp
= buf
+ strlen(hentry
[hnum
].name
);
675 while (*hp
== ' ' || *hp
== '\t') hp
++;
679 if (msginfo
->date
) break;
681 procheader_date_parse(NULL
, hp
, 0);
682 if (g_utf8_validate(hp
, -1, NULL
)) {
683 msginfo
->date
= g_strdup(hp
);
685 gchar
*utf
= conv_codeset_strdup(
687 conv_get_locale_charset_str_no_utf8(),
690 !g_utf8_validate(utf
, -1, NULL
)) {
692 utf
= g_malloc(strlen(buf
)*2+1);
693 conv_localetodisp(utf
,
700 if (msginfo
->from
) break;
701 msginfo
->from
= conv_unmime_header(hp
, NULL
, TRUE
);
702 msginfo
->fromname
= procheader_get_fromname(msginfo
->from
);
703 remove_return(msginfo
->from
);
704 remove_return(msginfo
->fromname
);
707 tmp
= conv_unmime_header(hp
, NULL
, TRUE
);
712 g_strconcat(p
, ", ", tmp
, NULL
);
715 msginfo
->to
= g_strdup(tmp
);
719 tmp
= conv_unmime_header(hp
, NULL
, TRUE
);
724 g_strconcat(p
, ", ", tmp
, NULL
);
727 msginfo
->cc
= g_strdup(tmp
);
731 if (msginfo
->newsgroups
) {
732 p
= msginfo
->newsgroups
;
733 msginfo
->newsgroups
=
734 g_strconcat(p
, ",", hp
, NULL
);
737 msginfo
->newsgroups
= g_strdup(hp
);
740 if (msginfo
->subject
) break;
741 msginfo
->subject
= conv_unmime_header(hp
, NULL
, FALSE
);
742 unfold_line(msginfo
->subject
);
745 if (msginfo
->msgid
) break;
747 extract_parenthesis(hp
, '<', '>');
749 msginfo
->msgid
= g_strdup(hp
);
752 msginfo
->references
=
753 references_list_prepend(msginfo
->references
,
757 if (msginfo
->inreplyto
) break;
759 eliminate_parenthesis(hp
, '(', ')');
760 if ((p
= strrchr(hp
, '<')) != NULL
&&
761 strchr(p
+ 1, '>') != NULL
) {
762 extract_parenthesis(p
, '<', '>');
765 msginfo
->inreplyto
= g_strdup(p
);
769 if (!g_ascii_strncasecmp(hp
, "multipart/", 10))
770 MSG_SET_TMP_FLAGS(msginfo
->flags
, MSG_MULTIPART
);
772 case H_DISPOSITION_NOTIFICATION_TO
:
773 if (!msginfo
->extradata
)
774 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
775 if (msginfo
->extradata
->dispositionnotificationto
) break;
776 msginfo
->extradata
->dispositionnotificationto
= g_strdup(hp
);
778 case H_RETURN_RECEIPT_TO
:
779 if (!msginfo
->extradata
)
780 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
781 if (msginfo
->extradata
->returnreceiptto
) break;
782 msginfo
->extradata
->returnreceiptto
= g_strdup(hp
);
784 /* partial download infos */
785 case H_SC_PARTIALLY_RETRIEVED
:
786 if (!msginfo
->extradata
)
787 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
788 if (msginfo
->extradata
->partial_recv
) break;
789 msginfo
->extradata
->partial_recv
= g_strdup(hp
);
791 case H_SC_ACCOUNT_SERVER
:
792 if (!msginfo
->extradata
)
793 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
794 if (msginfo
->extradata
->account_server
) break;
795 msginfo
->extradata
->account_server
= g_strdup(hp
);
797 case H_SC_ACCOUNT_LOGIN
:
798 if (!msginfo
->extradata
)
799 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
800 if (msginfo
->extradata
->account_login
) break;
801 msginfo
->extradata
->account_login
= g_strdup(hp
);
803 case H_SC_MESSAGE_SIZE
:
804 if (msginfo
->total_size
) break;
805 msginfo
->total_size
= atoi(hp
);
807 case H_SC_PLANNED_DOWNLOAD
:
808 msginfo
->planned_download
= atoi(hp
);
810 /* end partial download infos */
812 if (msginfo
->fromspace
) break;
813 msginfo
->fromspace
= g_strdup(hp
);
814 remove_return(msginfo
->fromspace
);
818 if (!msginfo
->extradata
)
819 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
820 if (msginfo
->extradata
->list_post
) break;
821 msginfo
->extradata
->list_post
= g_strdup(hp
);
823 case H_LIST_SUBSCRIBE
:
824 if (!msginfo
->extradata
)
825 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
826 if (msginfo
->extradata
->list_subscribe
) break;
827 msginfo
->extradata
->list_subscribe
= g_strdup(hp
);
829 case H_LIST_UNSUBSCRIBE
:
830 if (!msginfo
->extradata
)
831 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
832 if (msginfo
->extradata
->list_unsubscribe
) break;
833 msginfo
->extradata
->list_unsubscribe
= g_strdup(hp
);
836 if (!msginfo
->extradata
)
837 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
838 if (msginfo
->extradata
->list_help
) break;
839 msginfo
->extradata
->list_help
= g_strdup(hp
);
842 if (!msginfo
->extradata
)
843 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
844 if (msginfo
->extradata
->list_archive
) break;
845 msginfo
->extradata
->list_archive
= g_strdup(hp
);
848 if (!msginfo
->extradata
)
849 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
850 if (msginfo
->extradata
->list_owner
) break;
851 msginfo
->extradata
->list_owner
= g_strdup(hp
);
854 if (!msginfo
->extradata
)
855 msginfo
->extradata
= g_new0(MsgInfoExtraData
, 1);
856 if (msginfo
->extradata
->resent_from
) break;
857 msginfo
->extradata
->resent_from
= g_strdup(hp
);
863 /* to avoid performance penalty hooklist is invoked only for
864 headers known to be able to generate avatars */
865 if (hnum
== H_FROM
|| hnum
== H_X_FACE
|| hnum
== H_FACE
) {
866 AvatarCaptureData
*acd
= g_new0(AvatarCaptureData
, 1);
867 /* no extra memory is wasted, hooks are expected to
868 take care of copying members when needed */
869 acd
->msginfo
= msginfo
;
870 acd
->header
= hentry_full
[hnum
].name
;
872 hooks_invoke(AVATAR_HEADER_UPDATE_HOOKLIST
, (gpointer
)acd
);
879 if (!msginfo
->inreplyto
&& msginfo
->references
)
881 g_strdup((gchar
*)msginfo
->references
->data
);
886 gchar
*procheader_get_fromname(const gchar
*str
)
890 Xstrdup_a(tmp
, str
, return NULL
);
893 extract_quote(tmp
, '\"');
895 } else if (strchr(tmp
, '<')) {
896 eliminate_parenthesis(tmp
, '<', '>');
900 extract_parenthesis(tmp
, '<', '>');
903 } else if (strchr(tmp
, '(')) {
904 extract_parenthesis(tmp
, '(', ')');
909 name
= g_strdup(str
);
911 name
= g_strdup(tmp
);
916 static gint
procheader_scan_date_string(const gchar
*str
,
917 gchar
*weekday
, gint
*day
,
918 gchar
*month
, gint
*year
,
919 gint
*hh
, gint
*mm
, gint
*ss
,
925 gint zone1
= 0, zone2
= 0;
926 gchar offset_sign
, zonestr
[7];
932 result
= sscanf(str
, "%10s %d %9s %d %2d:%2d:%2d %6s",
933 weekday
, day
, month
, year
, hh
, mm
, ss
, zone
);
934 if (result
== 8) return 0;
937 result
= sscanf(str
, "%3s,%d %9s %d %2d:%2d:%2d %6s",
938 weekday
, day
, month
, year
, hh
, mm
, ss
, zone
);
939 if (result
== 8) return 0;
941 result
= sscanf(str
, "%3s %3s %d %2d:%2d:%2d %d %6s",
942 weekday
, month
, day
, hh
, mm
, ss
, year
, zone
);
943 if (result
== 8) return 0;
945 result
= sscanf(str
, "%d %9s %d %2d:%2d:%2d %6s",
946 day
, month
, year
, hh
, mm
, ss
, zone
);
947 if (result
== 7) return 0;
950 result
= sscanf(str
, "%10s %d %9s %d %2d:%2d:%2d",
951 weekday
, day
, month
, year
, hh
, mm
, ss
);
952 if (result
== 7) return 0;
954 result
= sscanf(str
, "%3s %3s %d %2d:%2d:%2d %d",
955 weekday
, month
, day
, hh
, mm
, ss
, year
);
956 if (result
== 7) return 0;
958 result
= sscanf(str
, "%d %9s %d %2d:%2d:%2d",
959 day
, month
, year
, hh
, mm
, ss
);
960 if (result
== 6) return 0;
963 result
= sscanf(str
, "%10s %d %9s %d %2d:%2d %6s",
964 weekday
, day
, month
, year
, hh
, mm
, zone
);
965 if (result
== 7) return 0;
967 result
= sscanf(str
, "%d %9s %d %2d:%2d %5s",
968 day
, month
, year
, hh
, mm
, zone
);
969 if (result
== 6) return 0;
972 result
= sscanf(str
, "%10s %d %9s %d %2d:%2d",
973 weekday
, day
, month
, year
, hh
, mm
);
974 if (result
== 6) return 0;
976 result
= sscanf(str
, "%d %9s %d %2d:%2d",
977 day
, month
, year
, hh
, mm
);
978 if (result
== 5) return 0;
982 /* RFC3339 subset, with fraction of second */
983 result
= sscanf(str
, "%4d-%2d-%2d%c%2d:%2d:%2d.%d%6s",
984 year
, &month_n
, day
, &sep1
, hh
, mm
, ss
, &secfract
, zonestr
);
986 && (sep1
== 'T' || sep1
== 't' || sep1
== ' ')) {
987 if (month_n
>= 1 && month_n
<= 12) {
988 strncpy2(month
, monthstr
+((month_n
-1)*3), 4);
989 if (zonestr
[0] == 'z' || zonestr
[0] == 'Z') {
990 strcat(zone
, "+00:00");
991 } else if (sscanf(zonestr
, "%c%2d:%2d",
992 &offset_sign
, &zone1
, &zone2
) == 3) {
993 strcat(zone
, zonestr
);
999 /* RFC3339 subset, no fraction of second */
1000 result
= sscanf(str
, "%4d-%2d-%2d%c%2d:%2d:%2d%6s",
1001 year
, &month_n
, day
, &sep1
, hh
, mm
, ss
, zonestr
);
1003 && (sep1
== 'T' || sep1
== 't' || sep1
== ' ')) {
1004 if (month_n
>= 1 && month_n
<= 12) {
1005 strncpy2(month
, monthstr
+((month_n
-1)*3), 4);
1006 if (zonestr
[0] == 'z' || zonestr
[0] == 'Z') {
1007 strcat(zone
, "+00:00");
1008 } else if (sscanf(zonestr
, "%c%2d:%2d",
1009 &offset_sign
, &zone1
, &zone2
) == 3) {
1010 strcat(zone
, zonestr
);
1018 /* RFC3339 subset, no fraction of second, and no timezone offset */
1019 /* This particular "subset" is invalid, RFC requires the offset */
1020 result
= sscanf(str
, "%4d-%2d-%2d %2d:%2d:%2d",
1021 year
, &month_n
, day
, hh
, mm
, ss
);
1023 if (1 <= month_n
&& month_n
<= 12) {
1024 strncpy2(month
, monthstr
+((month_n
-1)*3), 4);
1029 /* ISO8601 format with just date (YYYY-MM-DD) */
1030 result
= sscanf(str
, "%4d-%2d-%2d",
1031 year
, &month_n
, day
);
1033 *hh
= *mm
= *ss
= 0;
1034 if (1 <= month_n
&& month_n
<= 12) {
1035 strncpy2(month
, monthstr
+((month_n
-1)*3), 4);
1044 * Hiro, most UNIXen support this function:
1045 * http://www.mcsr.olemiss.edu/cgi-bin/man-cgi?getdate
1047 gboolean
procheader_date_parse_to_tm(const gchar
*src
, struct tm
*t
, char *zone
)
1060 memset(t
, 0, sizeof *t
);
1062 if (procheader_scan_date_string(src
, weekday
, &day
, month
, &year
,
1063 &hh
, &mm
, &ss
, zone
) < 0) {
1064 g_warning("Invalid date: %s", src
);
1068 /* Y2K compliant :) */
1077 if ((p
= strstr(monthstr
, month
)) != NULL
)
1078 dmonth
= (gint
)(p
- monthstr
) / 3 + 1;
1080 g_warning("Invalid month: %s", month
);
1081 dmonth
= G_DATE_BAD_MONTH
;
1088 t
->tm_mon
= dmonth
- 1;
1089 t
->tm_year
= year
- 1900;
1099 time_t procheader_date_parse(gchar
*dest
, const gchar
*src
, gint len
)
1107 GDateMonth dmonth
= G_DATE_BAD_MONTH
;
1111 if (procheader_scan_date_string(src
, weekday
, &day
, month
, &year
,
1112 &hh
, &mm
, &ss
, zone
) < 0) {
1113 if (dest
&& len
> 0)
1114 strncpy2(dest
, src
, len
);
1119 for (p
= monthstr
; *p
!= '\0'; p
+= 3) {
1120 if (!g_ascii_strncasecmp(p
, month
, 3)) {
1121 dmonth
= (gint
)(p
- monthstr
) / 3 + 1;
1128 GDateTime
*dt
, *dt2
;
1130 tz
= g_time_zone_new(zone
); // can't return NULL no need to check for it
1131 dt
= g_date_time_new(tz
, 1, 1, 1, 0, 0, 0);
1132 g_time_zone_unref(tz
);
1133 dt2
= g_date_time_add_full(dt
, year
-1, dmonth
-1, day
-1, hh
, mm
, ss
);
1134 g_date_time_unref(dt
);
1136 timer
= g_date_time_to_unix(dt2
);
1137 g_date_time_unref(dt2
);
1143 /* Y2K compliant :) */
1155 t
.tm_mon
= dmonth
- 1;
1156 t
.tm_year
= year
- 1900;
1162 tz_offset
= remote_tzoffset_sec(zone
);
1163 if (tz_offset
!= -1)
1164 timer
+= tzoffset_sec(&timer
) - tz_offset
;
1167 procheader_date_get_localtime(dest
, len
, timer
);
1173 void procheader_date_get_localtime(gchar
*dest
, gint len
, const time_t timer
)
1176 gchar
*default_format
= "%y/%m/%d(%a) %H:%M";
1178 const gchar
*src_codeset
, *dest_codeset
;
1182 lt
= localtime_r(&timer
, &buf
);
1185 lt
= localtime_r(&dummy
, &buf
);
1188 if (prefs_common
.date_format
)
1189 fast_strftime(dest
, len
, prefs_common
.date_format
, lt
);
1191 fast_strftime(dest
, len
, default_format
, lt
);
1193 if (!g_utf8_validate(dest
, -1, NULL
)) {
1194 src_codeset
= conv_get_locale_charset_str_no_utf8();
1195 dest_codeset
= CS_UTF_8
;
1196 str
= conv_codeset_strdup(dest
, src_codeset
, dest_codeset
);
1198 strncpy2(dest
, str
, len
);
1204 /* Added by Mel Hadasht on 27 Aug 2001 */
1205 /* Get a header from msginfo */
1206 gint
procheader_get_header_from_msginfo(MsgInfo
*msginfo
, gchar
**buf
, gchar
*header
)
1210 HeaderEntry hentry
[]={ { NULL
, NULL
, TRUE
},
1211 { NULL
, NULL
, FALSE
} };
1214 cm_return_val_if_fail(msginfo
!= NULL
, -1);
1215 cm_return_val_if_fail(buf
!= NULL
, -1);
1216 cm_return_val_if_fail(header
!= NULL
, -1);
1218 hentry
[0].name
= header
;
1220 file
= procmsg_get_message_file_path(msginfo
);
1221 if ((fp
= claws_fopen(file
, "rb")) == NULL
) {
1222 FILE_OP_ERROR(file
, "claws_fopen");
1228 val
= procheader_get_one_field(buf
, fp
, hentry
);
1230 if (claws_fclose(fp
) == EOF
) {
1231 FILE_OP_ERROR(file
, "claws_fclose");
1241 /* *buf is already NULL in that case, see procheader_get_one_field() */
1248 HeaderEntry
*procheader_entries_from_str(const gchar
*str
)
1250 HeaderEntry
*entries
= NULL
, *he
;
1251 int numh
= 0, i
= 0;
1252 gchar
**names
= NULL
;
1253 const gchar
*s
= str
;
1258 while (*s
!= '\0') {
1259 if (*s
== ' ') ++numh
;
1265 entries
= g_new0(HeaderEntry
, numh
+ 1); /* room for last NULL */
1267 ++s
; /* skip first space */
1268 names
= g_strsplit(s
, " ", numh
);
1271 he
->name
= g_strdup_printf("%s:", names
[i
]);
1281 void procheader_entries_free (HeaderEntry
*entries
)
1283 if (entries
!= NULL
) {
1284 HeaderEntry
*he
= entries
;
1285 while (he
->name
!= NULL
) {
1287 if (he
->body
!= NULL
)
1295 gboolean
procheader_header_is_internal(const gchar
*hdr_name
)
1297 const gchar
*internal_hdrs
[] = {
1298 "AF:", "NF:", "PS:", "SRH:", "SFN:", "DSR:", "MID:", "CFG:",
1299 "PT:", "S:", "RQ:", "SSV:", "NSV:", "SSH:", "R:", "MAID:",
1300 "SCF:", "RMID:", "FMID:", "NAID:",
1301 "X-Claws-Account-Id:",
1304 "X-Claws-Privacy-System:",
1305 "X-Claws-Auto-Wrapping:",
1306 "X-Claws-Auto-Indent:",
1307 "X-Claws-End-Special-Headers:",
1308 "X-Sylpheed-Account-Id:",
1310 "X-Sylpheed-Encrypt:",
1311 "X-Sylpheed-Privacy-System:",
1312 "X-Sylpheed-End-Special-Headers:",
1317 for (i
= 0; internal_hdrs
[i
]; i
++) {
1318 if (!strcmp(hdr_name
, internal_hdrs
[i
]))