summaryrefslogtreecommitdiff
path: root/libsylph/procheader.c
diff options
context:
space:
mode:
Diffstat (limited to 'libsylph/procheader.c')
-rw-r--r--libsylph/procheader.c979
1 files changed, 979 insertions, 0 deletions
diff --git a/libsylph/procheader.c b/libsylph/procheader.c
new file mode 100644
index 0000000..1dcffa2
--- /dev/null
+++ b/libsylph/procheader.c
@@ -0,0 +1,979 @@
1/*
2 * LibSylph -- E-Mail client library
3 * Copyright (C) 1999-2013 Hiroyuki Yamamoto
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library 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 GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifdef HAVE_CONFIG_H
21# include "config.h"
22#endif
23
24#include <glib.h>
25#include <glib/gi18n.h>
26#include <stdio.h>
27#include <string.h>
28#include <stdlib.h>
29#include <time.h>
30#include <sys/stat.h>
31
32#include "procheader.h"
33#include "procmsg.h"
34#include "codeconv.h"
35#include "displayheader.h"
36#include "prefs_common.h"
37#include "utils.h"
38
39#define BUFFSIZE 8192
40
41gint procheader_get_one_field(gchar *buf, size_t len, FILE *fp,
42 HeaderEntry hentry[])
43{
44 gint nexthead;
45 gint hnum = 0;
46 HeaderEntry *hp = NULL;
47
48 if (hentry != NULL) {
49 /* skip non-required headers */
50 do {
51 do {
52 if (fgets(buf, len, fp) == NULL)
53 return -1;
54 if (buf[0] == '\r' || buf[0] == '\n')
55 return -1;
56 } while (buf[0] == ' ' || buf[0] == '\t');
57
58 for (hp = hentry, hnum = 0; hp->name != NULL;
59 hp++, hnum++) {
60 if (!g_ascii_strncasecmp(hp->name, buf,
61 strlen(hp->name)))
62 break;
63 }
64 } while (hp->name == NULL);
65 } else {
66 if (fgets(buf, len, fp) == NULL) return -1;
67 if (buf[0] == '\r' || buf[0] == '\n') return -1;
68 }
69
70 /* unfold the specified folded line */
71 if (hp && hp->unfold) {
72 gboolean folded = FALSE;
73 gchar *bufp = buf + strlen(buf);
74
75 for (; bufp > buf &&
76 (*(bufp - 1) == '\n' || *(bufp - 1) == '\r');
77 bufp--)
78 *(bufp - 1) = '\0';
79
80 while (1) {
81 nexthead = fgetc(fp);
82
83 /* folded */
84 if (nexthead == ' ' || nexthead == '\t')
85 folded = TRUE;
86 else if (nexthead == EOF)
87 break;
88 else if (folded == TRUE) {
89 if ((len - (bufp - buf)) <= 2) break;
90
91 if (nexthead == '\n') {
92 folded = FALSE;
93 continue;
94 }
95
96 /* replace return code on the tail end
97 with space */
98 *bufp++ = ' ';
99 *bufp++ = nexthead;
100 *bufp = '\0';
101
102 /* concatenate next line */
103 if (fgets(bufp, len - (bufp - buf), fp)
104 == NULL) break;
105 bufp += strlen(bufp);
106
107 for (; bufp > buf &&
108 (*(bufp - 1) == '\n' || *(bufp - 1) == '\r');
109 bufp--)
110 *(bufp - 1) = '\0';
111
112 folded = FALSE;
113 } else {
114 ungetc(nexthead, fp);
115 break;
116 }
117 }
118
119 return hnum;
120 }
121
122 while (1) {
123 nexthead = fgetc(fp);
124 if (nexthead == ' ' || nexthead == '\t') {
125 size_t buflen = strlen(buf);
126
127 /* concatenate next line */
128 if ((len - buflen) > 2) {
129 gchar *p = buf + buflen;
130
131 *p++ = nexthead;
132 *p = '\0';
133 buflen++;
134 if (fgets(p, len - buflen, fp) == NULL)
135 break;
136 } else
137 break;
138 } else {
139 if (nexthead != EOF)
140 ungetc(nexthead, fp);
141 break;
142 }
143 }
144
145 /* remove trailing return code */
146 strretchomp(buf);
147
148 return hnum;
149}
150
151gchar *procheader_get_unfolded_line(gchar *buf, size_t len, FILE *fp)
152{
153 gboolean folded = FALSE;
154 gint nexthead;
155 gchar *bufp;
156
157 if (fgets(buf, len, fp) == NULL) return NULL;
158 if (buf[0] == '\r' || buf[0] == '\n') return NULL;
159 bufp = buf + strlen(buf);
160
161 for (; bufp > buf &&
162 (*(bufp - 1) == '\n' || *(bufp - 1) == '\r');
163 bufp--)
164 *(bufp - 1) = '\0';
165
166 while (1) {
167 nexthead = fgetc(fp);
168
169 /* folded */
170 if (nexthead == ' ' || nexthead == '\t')
171 folded = TRUE;
172 else if (nexthead == EOF)
173 break;
174 else if (folded == TRUE) {
175 if ((len - (bufp - buf)) <= 2) break;
176
177 if (nexthead == '\n') {
178 folded = FALSE;
179 continue;
180 }
181
182 /* replace return code on the tail end
183 with space */
184 *bufp++ = ' ';
185 *bufp++ = nexthead;
186 *bufp = '\0';
187
188 /* concatenate next line */
189 if (fgets(bufp, len - (bufp - buf), fp)
190 == NULL) break;
191 bufp += strlen(bufp);
192
193 for (; bufp > buf &&
194 (*(bufp - 1) == '\n' || *(bufp - 1) == '\r');
195 bufp--)
196 *(bufp - 1) = '\0';
197
198 folded = FALSE;
199 } else {
200 ungetc(nexthead, fp);
201 break;
202 }
203 }
204
205 /* remove trailing return code */
206 strretchomp(buf);
207
208 return buf;
209}
210
211GSList *procheader_get_header_list_from_file(const gchar *file)
212{
213 FILE *fp;
214 GSList *hlist;
215
216 if ((fp = g_fopen(file, "rb")) == NULL) {
217 FILE_OP_ERROR(file, "procheader_get_header_list_from_file: fopen");
218 return NULL;
219 }
220
221 hlist = procheader_get_header_list(fp);
222
223 fclose(fp);
224 return hlist;
225}
226
227GSList *procheader_get_header_list(FILE *fp)
228{
229 gchar buf[BUFFSIZE];
230 gchar *p;
231 GSList *hlist = NULL;
232 Header *header;
233
234 g_return_val_if_fail(fp != NULL, NULL);
235
236 while (procheader_get_unfolded_line(buf, sizeof(buf), fp) != NULL) {
237 if (*buf == ':') continue;
238 for (p = buf; *p && *p != ' '; p++) {
239 if (*p == ':') {
240 header = g_new(Header, 1);
241 header->name = g_strndup(buf, p - buf);
242 p++;
243 while (*p == ' ' || *p == '\t') p++;
244 header->body = conv_unmime_header(p, NULL);
245
246 hlist = g_slist_append(hlist, header);
247 break;
248 }
249 }
250 }
251
252 return hlist;
253}
254
255GSList *procheader_get_header_list_from_msginfo(MsgInfo *msginfo)
256{
257 GSList *hlist = NULL;
258
259 g_return_val_if_fail(msginfo != NULL, NULL);
260
261 if (msginfo->subject)
262 hlist = procheader_add_header_list(hlist, "Subject",
263 msginfo->subject);
264 if (msginfo->from)
265 hlist = procheader_add_header_list(hlist, "From",
266 msginfo->from);
267 if (msginfo->to)
268 hlist = procheader_add_header_list(hlist, "To", msginfo->to);
269 if (msginfo->cc)
270 hlist = procheader_add_header_list(hlist, "Cc", msginfo->cc);
271 if (msginfo->newsgroups)
272 hlist = procheader_add_header_list(hlist, "Newsgroups",
273 msginfo->newsgroups);
274 if (msginfo->date)
275 hlist = procheader_add_header_list(hlist, "Date",
276 msginfo->date);
277
278 return hlist;
279}
280
281GSList *procheader_add_header_list(GSList *hlist, const gchar *header_name,
282 const gchar *body)
283{
284 Header *header;
285
286 g_return_val_if_fail(header_name != NULL, hlist);
287
288 header = g_new(Header, 1);
289 header->name = g_strdup(header_name);
290 header->body = g_strdup(body);
291
292 return g_slist_append(hlist, header);
293}
294
295GSList *procheader_copy_header_list(GSList *hlist)
296{
297 GSList *newlist = NULL, *cur;
298
299 for (cur = hlist; cur != NULL; cur = cur->next) {
300 Header *header = (Header *)cur->data;
301 newlist = procheader_add_header_list(newlist, header->name,
302 header->body);
303 }
304
305 return newlist;
306}
307
308GSList *procheader_merge_header_list(GSList *hlist1, GSList *hlist2)
309{
310 GSList *cur;
311
312 for (cur = hlist2; cur != NULL; cur = cur->next) {
313 Header *header = (Header *)cur->data;
314 if (procheader_find_header_list(hlist1, header->name) < 0)
315 hlist1 = g_slist_append(hlist1, header);
316 }
317
318 return hlist1;
319}
320
321GSList *procheader_merge_header_list_dup(GSList *hlist1, GSList *hlist2)
322{
323 GSList *list, *cur;
324
325 list = procheader_copy_header_list(hlist1);
326
327 for (cur = hlist2; cur != NULL; cur = cur->next) {
328 Header *header = (Header *)cur->data;
329 if (procheader_find_header_list(list, header->name) < 0)
330 list = procheader_add_header_list(list, header->name,
331 header->body);
332 }
333
334 return list;
335}
336
337gint procheader_find_header_list(GSList *hlist, const gchar *header_name)
338{
339 GSList *cur;
340 gint index = 0;
341 Header *header;
342
343 g_return_val_if_fail(header_name != NULL, -1);
344
345 for (cur = hlist; cur != NULL; cur = cur->next, index++) {
346 header = (Header *)cur->data;
347 if (g_ascii_strcasecmp(header->name, header_name) == 0)
348 return index;
349 }
350
351 return -1;
352}
353
354GPtrArray *procheader_get_header_array(FILE *fp, const gchar *encoding)
355{
356 gchar buf[BUFFSIZE];
357 gchar *p;
358 GPtrArray *headers;
359 Header *header;
360
361 g_return_val_if_fail(fp != NULL, NULL);
362
363 headers = g_ptr_array_new();
364
365 while (procheader_get_unfolded_line(buf, sizeof(buf), fp) != NULL) {
366 if (*buf == ':') continue;
367 for (p = buf; *p && *p != ' '; p++) {
368 if (*p == ':') {
369 header = g_new(Header, 1);
370 header->name = g_strndup(buf, p - buf);
371 p++;
372 while (*p == ' ' || *p == '\t') p++;
373 header->body = conv_unmime_header(p, encoding);
374
375 g_ptr_array_add(headers, header);
376 break;
377 }
378 }
379 }
380
381 return headers;
382}
383
384GPtrArray *procheader_get_header_array_asis(FILE *fp, const gchar *encoding)
385{
386 gchar buf[BUFFSIZE];
387 gchar *p;
388 GPtrArray *headers;
389 Header *header;
390
391 g_return_val_if_fail(fp != NULL, NULL);
392
393 headers = g_ptr_array_new();
394
395 while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) != -1) {
396 if (*buf == ':') continue;
397 for (p = buf; *p && *p != ' '; p++) {
398 if (*p == ':') {
399 header = g_new(Header, 1);
400 header->name = g_strndup(buf, p - buf);
401 p++;
402 header->body = conv_unmime_header(p, encoding);
403
404 g_ptr_array_add(headers, header);
405 break;
406 }
407 }
408 }
409
410 return headers;
411}
412
413GPtrArray *procheader_get_header_array_for_display(FILE *fp,
414 const gchar *encoding)
415{
416 GPtrArray *headers, *sorted_headers;
417 GSList *disphdr_list;
418 Header *header;
419 gint i;
420
421 g_return_val_if_fail(fp != NULL, NULL);
422
423 headers = procheader_get_header_array_asis(fp, encoding);
424
425 sorted_headers = g_ptr_array_new();
426
427 for (disphdr_list = prefs_common.disphdr_list; disphdr_list != NULL;
428 disphdr_list = disphdr_list->next) {
429 DisplayHeaderProp *dp =
430 (DisplayHeaderProp *)disphdr_list->data;
431
432 for (i = 0; i < headers->len; i++) {
433 header = g_ptr_array_index(headers, i);
434
435 if (!g_ascii_strcasecmp(header->name, dp->name)) {
436 if (dp->hidden)
437 procheader_header_free(header);
438 else
439 g_ptr_array_add(sorted_headers, header);
440
441 g_ptr_array_remove_index(headers, i);
442 i--;
443 }
444 }
445 }
446
447 if (prefs_common.show_other_header) {
448 for (i = 0; i < headers->len; i++) {
449 header = g_ptr_array_index(headers, i);
450 g_ptr_array_add(sorted_headers, header);
451 }
452 g_ptr_array_free(headers, TRUE);
453 } else
454 procheader_header_array_destroy(headers);
455
456 return sorted_headers;
457}
458
459void procheader_header_list_destroy(GSList *hlist)
460{
461 Header *header;
462
463 while (hlist != NULL) {
464 header = hlist->data;
465 procheader_header_free(header);
466 hlist = g_slist_remove(hlist, header);
467 }
468}
469
470void procheader_header_array_destroy(GPtrArray *harray)
471{
472 gint i;
473 Header *header;
474
475 for (i = 0; i < harray->len; i++) {
476 header = g_ptr_array_index(harray, i);
477 procheader_header_free(header);
478 }
479
480 g_ptr_array_free(harray, TRUE);
481}
482
483void procheader_header_free(Header *header)
484{
485 if (!header) return;
486
487 g_free(header->name);
488 g_free(header->body);
489 g_free(header);
490}
491
492void procheader_get_header_fields(FILE *fp, HeaderEntry hentry[])
493{
494 gchar buf[BUFFSIZE];
495 HeaderEntry *hp;
496 gint hnum;
497 gchar *p;
498
499 if (hentry == NULL) return;
500
501 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry))
502 != -1) {
503 hp = hentry + hnum;
504
505 p = buf + strlen(hp->name);
506 while (*p == ' ' || *p == '\t') p++;
507
508 if (hp->body == NULL)
509 hp->body = g_strdup(p);
510 else if (!g_ascii_strcasecmp(hp->name, "To:") ||
511 !g_ascii_strcasecmp(hp->name, "Cc:")) {
512 gchar *tp = hp->body;
513 hp->body = g_strconcat(tp, ", ", p, NULL);
514 g_free(tp);
515 }
516 }
517}
518
519MsgInfo *procheader_parse_file(const gchar *file, MsgFlags flags,
520 gboolean full)
521{
522 GStatBuf s;
523 FILE *fp;
524 MsgInfo *msginfo;
525
526 if (g_stat(file, &s) < 0) {
527 FILE_OP_ERROR(file, "stat");
528 return NULL;
529 }
530 if (!S_ISREG(s.st_mode))
531 return NULL;
532
533 if ((fp = g_fopen(file, "rb")) == NULL) {
534 FILE_OP_ERROR(file, "procheader_parse_file: fopen");
535 return NULL;
536 }
537
538 msginfo = procheader_parse_stream(fp, flags, full);
539 fclose(fp);
540
541 if (msginfo) {
542 msginfo->size = s.st_size;
543 msginfo->mtime = s.st_mtime;
544 }
545
546 return msginfo;
547}
548
549MsgInfo *procheader_parse_str(const gchar *str, MsgFlags flags, gboolean full)
550{
551 FILE *fp;
552 MsgInfo *msginfo;
553
554 if ((fp = str_open_as_stream(str)) == NULL)
555 return NULL;
556
557 msginfo = procheader_parse_stream(fp, flags, full);
558 fclose(fp);
559 return msginfo;
560}
561
562enum
563{
564 H_DATE = 0,
565 H_FROM = 1,
566 H_TO = 2,
567 H_NEWSGROUPS = 3,
568 H_SUBJECT = 4,
569 H_MSG_ID = 5,
570 H_REFERENCES = 6,
571 H_IN_REPLY_TO = 7,
572 H_CONTENT_TYPE = 8,
573 H_SEEN = 9,
574 H_CC = 10,
575 H_X_FACE = 11
576};
577
578MsgInfo *procheader_parse_stream(FILE *fp, MsgFlags flags, gboolean full)
579{
580 static HeaderEntry hentry_full[] = {{"Date:", NULL, FALSE},
581 {"From:", NULL, TRUE},
582 {"To:", NULL, TRUE},
583 {"Newsgroups:", NULL, TRUE},
584 {"Subject:", NULL, TRUE},
585 {"Message-Id:", NULL, FALSE},
586 {"References:", NULL, FALSE},
587 {"In-Reply-To:", NULL, FALSE},
588 {"Content-Type:", NULL, FALSE},
589 {"Seen:", NULL, FALSE},
590 {"Cc:", NULL, TRUE},
591 {"X-Face:", NULL, FALSE},
592 {NULL, NULL, FALSE}};
593
594 static HeaderEntry hentry_short[] = {{"Date:", NULL, FALSE},
595 {"From:", NULL, TRUE},
596 {"To:", NULL, TRUE},
597 {"Newsgroups:", NULL, TRUE},
598 {"Subject:", NULL, TRUE},
599 {"Message-Id:", NULL, FALSE},
600 {"References:", NULL, FALSE},
601 {"In-Reply-To:", NULL, FALSE},
602 {"Content-Type:", NULL, FALSE},
603 {"Seen:", NULL, FALSE},
604 {NULL, NULL, FALSE}};
605
606 MsgInfo *msginfo;
607 gchar buf[BUFFSIZE];
608 gchar *p;
609 gchar *hp;
610 HeaderEntry *hentry;
611 gint hnum;
612 gchar *from = NULL, *to = NULL, *subject = NULL, *cc = NULL;
613 gchar *charset = NULL;
614
615 hentry = full ? hentry_full : hentry_short;
616
617 if (MSG_IS_QUEUED(flags)) {
618 while (fgets(buf, sizeof(buf), fp) != NULL)
619 if (buf[0] == '\r' || buf[0] == '\n') break;
620 }
621
622 msginfo = g_new0(MsgInfo, 1);
623 msginfo->flags = flags;
624 msginfo->references = NULL;
625 msginfo->inreplyto = NULL;
626
627 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry))
628 != -1) {
629 hp = buf + strlen(hentry[hnum].name);
630 while (*hp == ' ' || *hp == '\t') hp++;
631
632 switch (hnum) {
633 case H_DATE:
634 if (msginfo->date) break;
635 msginfo->date_t =
636 procheader_date_parse(NULL, hp, 0);
637 msginfo->date = g_strdup(hp);
638 break;
639 case H_FROM:
640 if (from) break;
641 from = g_strdup(hp);
642 break;
643 case H_TO:
644 if (to) {
645 p = to;
646 to = g_strconcat(p, ", ", hp, NULL);
647 g_free(p);
648 } else
649 to = g_strdup(hp);
650 break;
651 case H_NEWSGROUPS:
652 if (msginfo->newsgroups) {
653 p = msginfo->newsgroups;
654 msginfo->newsgroups =
655 g_strconcat(p, ",", hp, NULL);
656 g_free(p);
657 } else
658 msginfo->newsgroups = g_strdup(buf + 12);
659 break;
660 case H_SUBJECT:
661 if (msginfo->subject) break;
662 subject = g_strdup(hp);
663 break;
664 case H_MSG_ID:
665 if (msginfo->msgid) break;
666
667 extract_parenthesis(hp, '<', '>');
668 remove_space(hp);
669 msginfo->msgid = g_strdup(hp);
670 break;
671 case H_REFERENCES:
672 msginfo->references =
673 references_list_prepend(msginfo->references,
674 hp);
675 break;
676 case H_IN_REPLY_TO:
677 if (msginfo->inreplyto) break;
678
679 eliminate_parenthesis(hp, '(', ')');
680 if ((p = strrchr(hp, '<')) != NULL &&
681 strchr(p + 1, '>') != NULL) {
682 extract_parenthesis(p, '<', '>');
683 remove_space(p);
684 if (*p != '\0')
685 msginfo->inreplyto = g_strdup(p);
686 }
687 break;
688 case H_CONTENT_TYPE:
689 if (!g_ascii_strncasecmp(hp, "multipart", 9)) {
690 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_MIME);
691 } else {
692 if (!g_ascii_strncasecmp(hp, "text/html", 9)) {
693 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_MIME_HTML);
694 }
695 if (!charset) {
696 procmime_scan_content_type_str
697 (hp, NULL, &charset, NULL, NULL);
698 }
699 }
700 break;
701 case H_SEEN:
702 /* mnews Seen header */
703 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW|MSG_UNREAD);
704 break;
705 case H_CC:
706 if (cc) {
707 p = cc;
708 cc = g_strconcat(p, ", ", hp, NULL);
709 g_free(p);
710 } else
711 cc = g_strdup(hp);
712 break;
713 case H_X_FACE:
714 if (msginfo->xface) break;
715 msginfo->xface = g_strdup(hp);
716 break;
717 default:
718 break;
719 }
720 }
721
722 if (from) {
723 msginfo->from = conv_unmime_header(from, charset);
724 subst_control(msginfo->from, ' ');
725 msginfo->fromname = procheader_get_fromname(msginfo->from);
726 g_free(from);
727 }
728 if (to) {
729 msginfo->to = conv_unmime_header(to, charset);
730 subst_control(msginfo->to, ' ');
731 g_free(to);
732 }
733 if (subject) {
734 msginfo->subject = conv_unmime_header(subject, charset);
735 subst_control(msginfo->subject, ' ');
736 g_free(subject);
737 }
738 if (cc) {
739 msginfo->cc = conv_unmime_header(cc, charset);
740 subst_control(msginfo->cc, ' ');
741 g_free(cc);
742 }
743
744 if (!msginfo->inreplyto && msginfo->references)
745 msginfo->inreplyto =
746 g_strdup((gchar *)msginfo->references->data);
747
748 if (MSG_IS_MIME(msginfo->flags)) {
749 MimeInfo *mimeinfo, *part;
750 gboolean has_html = FALSE;
751
752 part = mimeinfo = procmime_scan_message_stream(fp);
753 while (part) {
754 if (part->mime_type != MIME_TEXT &&
755 part->mime_type != MIME_TEXT_HTML &&
756 part->mime_type != MIME_MULTIPART)
757 break;
758 if (part->mime_type == MIME_TEXT_HTML)
759 has_html = TRUE;
760 part = procmime_mimeinfo_next(part);
761 }
762
763 if (has_html && !part) {
764 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_MIME_HTML);
765 }
766
767 procmime_mimeinfo_free_all(mimeinfo);
768 }
769
770 g_free(charset);
771
772 return msginfo;
773}
774
775gchar *procheader_get_fromname(const gchar *str)
776{
777 gchar *tmp, *name;
778
779 tmp = g_strdup(str);
780
781 if (*tmp == '\"') {
782 extract_quote_with_escape(tmp, '\"');
783 g_strstrip(tmp);
784 } else if (strchr(tmp, '<')) {
785 eliminate_parenthesis(tmp, '<', '>');
786 g_strstrip(tmp);
787 if (*tmp == '\0') {
788 strcpy(tmp, str);
789 extract_parenthesis(tmp, '<', '>');
790 g_strstrip(tmp);
791 }
792 } else if (strchr(tmp, '(')) {
793 extract_parenthesis_with_escape(tmp, '(', ')');
794 g_strstrip(tmp);
795 }
796
797 if (*tmp == '\0') {
798 g_free(tmp);
799 name = g_strdup(str);
800 } else
801 name = tmp;
802
803 return name;
804}
805
806gchar *procheader_get_toname(const gchar *str)
807{
808 GSList *addr_list, *cur;
809 GString *toname;
810 gchar *name;
811
812 if (strchr(str, ',') == NULL)
813 return procheader_get_fromname(str);
814
815 addr_list = address_list_append_orig(NULL, str);
816 toname = g_string_new(NULL);
817
818 for (cur = addr_list; cur != NULL; cur = cur->next) {
819 name = procheader_get_fromname((gchar *)cur->data);
820 g_string_append(toname, name);
821 g_free(name);
822 if (cur->next)
823 g_string_append(toname, ", ");
824 }
825
826 slist_free_strings(addr_list);
827
828 return g_string_free(toname, FALSE);
829}
830
831static gint procheader_scan_date_string(const gchar *str,
832 gchar *weekday, gint *day,
833 gchar *month, gint *year,
834 gint *hh, gint *mm, gint *ss,
835 gchar *zone)
836{
837 gint result;
838
839 *zone = '\0';
840 result = sscanf(str, "%10s %d %9s %d %2d:%2d:%2d %5s",
841 weekday, day, month, year, hh, mm, ss, zone);
842 if (result >= 7) return 0;
843
844 result = sscanf(str, "%3s,%d %9s %d %2d:%2d:%2d %5s",
845 weekday, day, month, year, hh, mm, ss, zone);
846 if (result >= 7) return 0;
847
848 result = sscanf(str, "%3s,%d %9s %d %2d.%2d.%2d %5s",
849 weekday, day, month, year, hh, mm, ss, zone);
850 if (result >= 7) return 0;
851
852 result = sscanf(str, "%3s %d, %9s %d %2d:%2d:%2d %5s",
853 weekday, day, month, year, hh, mm, ss, zone);
854 if (result >= 7) return 0;
855
856 result = sscanf(str, "%d %9s %d %2d:%2d:%2d %5s",
857 day, month, year, hh, mm, ss, zone);
858 if (result >= 6) return 0;
859
860 result = sscanf(str, "%d-%2s-%2d %2d:%2d:%2d",
861 year, month, day, hh, mm, ss);
862 if (result == 6) return 0;
863
864 *ss = 0;
865 result = sscanf(str, "%10s %d %9s %d %2d:%2d %5s",
866 weekday, day, month, year, hh, mm, zone);
867 if (result >= 6) return 0;
868
869 result = sscanf(str, "%d %9s %d %2d:%2d %5s",
870 day, month, year, hh, mm, zone);
871 if (result >= 5) return 0;
872
873 return -1;
874}
875
876stime_t procheader_date_parse(gchar *dest, const gchar *src, gint len)
877{
878 static gchar monthstr[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
879 gchar weekday[11];
880 gint day;
881 gchar month[10];
882 gint year;
883 gint hh, mm, ss;
884 gchar zone[6];
885 GDateMonth dmonth = G_DATE_BAD_MONTH;
886 struct tm t;
887 gchar *p;
888 time_t timer_;
889 time_t tz_offset;
890 stime_t timer;
891
892 if (procheader_scan_date_string(src, weekday, &day, month, &year,
893 &hh, &mm, &ss, zone) < 0) {
894 g_warning("procheader_scan_date_string: date parse failed: %s", src);
895 if (dest && len > 0)
896 strncpy2(dest, src, len);
897 return 0;
898 }
899
900 /* Y2K compliant :) */
901 if (year < 1000) {
902 if (year < 50)
903 year += 2000;
904 else
905 year += 1900;
906 }
907
908 month[3] = '\0';
909 if (g_ascii_isdigit(month[0])) {
910 dmonth = atoi(month);
911 } else {
912 for (p = monthstr; *p != '\0'; p += 3) {
913 if (!g_ascii_strncasecmp(p, month, 3)) {
914 dmonth = (gint)(p - monthstr) / 3 + 1;
915 break;
916 }
917 }
918 }
919
920 t.tm_sec = ss;
921 t.tm_min = mm;
922 t.tm_hour = hh;
923 t.tm_mday = day;
924 t.tm_mon = dmonth - 1;
925 t.tm_year = year - 1900;
926 t.tm_wday = 0;
927 t.tm_yday = 0;
928 t.tm_isdst = -1;
929
930 timer_ = mktime(&t);
931 if (timer_ == -1) {
932 if (year >= 2038) {
933 g_warning("mktime: date overflow: %s", src);
934 timer_ = G_MAXINT - 12 * 3600;
935 } else {
936 g_warning("mktime: can't convert date: %s", src);
937 if (dest)
938 dest[0] = '\0';
939 return 0;
940 }
941 }
942
943 timer = timer_;
944 if (timer < G_MAXINT - 12 * 3600) {
945 tz_offset = remote_tzoffset_sec(zone);
946 if (tz_offset != -1)
947 timer += tzoffset_sec(&timer) - tz_offset;
948 }
949
950 if (dest)
951 procheader_date_get_localtime(dest, len, timer);
952
953 return timer;
954}
955
956void procheader_date_get_localtime(gchar *dest, gint len, const stime_t timer)
957{
958 time_t timer_ = timer;
959 struct tm *lt;
960 gchar *default_format = "%y/%m/%d(%a) %H:%M";
961 gchar *buf;
962 gchar tmp[BUFFSIZE];
963
964 lt = localtime(&timer_);
965 if (!lt) {
966 g_warning("can't get localtime of %ld\n", timer);
967 dest[0] = '\0';
968 return;
969 }
970
971 if (prefs_common.date_format)
972 strftime(tmp, sizeof(tmp), prefs_common.date_format, lt);
973 else
974 strftime(tmp, sizeof(tmp), default_format, lt);
975
976 buf = conv_localetodisp(tmp, NULL);
977 strncpy2(dest, buf, len);
978 g_free(buf);
979}