2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2001 Hiroyuki Yamamoto
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.
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, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31 #include "procheader.h"
34 #include "prefs_common.h"
39 gint procheader_get_one_field(gchar *buf, gint len, FILE *fp,
44 HeaderEntry *hp = NULL;
47 /* skip non-required headers */
50 if (fgets(buf, len, fp) == NULL)
52 if (buf[0] == '\r' || buf[0] == '\n')
54 } while (buf[0] == ' ' || buf[0] == '\t');
56 for (hp = hentry, hnum = 0; hp->name != NULL;
58 if (!strncasecmp(hp->name, buf,
62 } while (hp->name == NULL);
64 if (fgets(buf, len, fp) == NULL) return -1;
65 if (buf[0] == '\r' || buf[0] == '\n') return -1;
68 /* unfold the specified folded line */
69 if (hp && hp->unfold) {
70 gboolean folded = FALSE;
71 gchar *bufp = buf + strlen(buf);
77 if (nexthead == ' ' || nexthead == '\t')
79 else if (nexthead == EOF)
81 else if (folded == TRUE) {
82 /* concatenate next line */
83 if ((len - (bufp - buf)) <= 2) break;
85 /* replace return code on the tail end
90 if (nexthead == '\r' || nexthead == '\n') {
94 if (fgets(bufp, len - (bufp - buf), fp)
100 ungetc(nexthead, fp);
105 /* remove trailing return code */
112 nexthead = fgetc(fp);
113 if (nexthead == ' ' || nexthead == '\t') {
114 size_t buflen = strlen(buf);
116 /* concatenate next line */
117 if ((len - buflen) > 2) {
118 gchar *p = buf + buflen;
123 if (fgets(p, len - buflen, fp) == NULL)
129 ungetc(nexthead, fp);
134 /* remove trailing return code */
140 gchar *procheader_get_unfolded_line(gchar *buf, gint len, FILE *fp)
142 gboolean folded = FALSE;
146 if (fgets(buf, len, fp) == NULL) return NULL;
147 if (buf[0] == '\r' || buf[0] == '\n') return NULL;
148 bufp = buf + strlen(buf);
151 nexthead = fgetc(fp);
154 if (nexthead == ' ' || nexthead == '\t')
156 else if (nexthead == EOF)
158 else if (folded == TRUE) {
159 /* concatenate next line */
160 if ((len - (bufp - buf)) <= 2) break;
162 /* replace return code on the tail end
167 if (nexthead == '\r' || nexthead == '\n') {
171 if (fgets(bufp, len - (bufp - buf), fp)
173 bufp += strlen(bufp);
177 ungetc(nexthead, fp);
182 /* remove trailing return code */
188 GSList *procheader_get_header_list_from_file(const gchar *file)
193 if ((fp = fopen(file, "r")) == NULL) {
194 FILE_OP_ERROR(file, "fopen");
198 hlist = procheader_get_header_list(fp);
204 GSList *procheader_get_header_list(FILE *fp)
206 gchar buf[BUFFSIZE], tmp[BUFFSIZE];
208 GSList *hlist = NULL;
211 g_return_val_if_fail(fp != NULL, NULL);
213 while (procheader_get_unfolded_line(buf, sizeof(buf), fp) != NULL) {
214 if (*buf == ':') continue;
215 for (p = buf; *p && *p != ' '; p++) {
217 header = g_new(Header, 1);
218 header->name = g_strndup(buf, p - buf);
220 while (*p == ' ' || *p == '\t') p++;
221 conv_unmime_header(tmp, sizeof(tmp), p, NULL);
222 header->body = g_strdup(tmp);
224 hlist = g_slist_append(hlist, header);
233 GPtrArray *procheader_get_header_array(FILE *fp)
235 gchar buf[BUFFSIZE], tmp[BUFFSIZE];
240 g_return_val_if_fail(fp != NULL, NULL);
242 headers = g_ptr_array_new();
244 while (procheader_get_unfolded_line(buf, sizeof(buf), fp) != NULL) {
245 if (*buf == ':') continue;
246 for (p = buf; *p && *p != ' '; p++) {
248 header = g_new(Header, 1);
249 header->name = g_strndup(buf, p - buf);
251 while (*p == ' ' || *p == '\t') p++;
252 conv_unmime_header(tmp, sizeof(tmp), p, NULL);
253 header->body = g_strdup(tmp);
255 g_ptr_array_add(headers, header);
264 GPtrArray *procheader_get_header_array_asis(FILE *fp)
266 gchar buf[BUFFSIZE], tmp[BUFFSIZE];
271 g_return_val_if_fail(fp != NULL, NULL);
273 headers = g_ptr_array_new();
275 while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) != -1) {
276 if (*buf == ':') continue;
277 for (p = buf; *p && *p != ' '; p++) {
279 header = g_new(Header, 1);
280 header->name = g_strndup(buf, p - buf);
282 conv_unmime_header(tmp, sizeof(tmp), p, NULL);
283 header->body = g_strdup(tmp);
285 g_ptr_array_add(headers, header);
294 void procheader_header_list_destroy(GSList *hlist)
298 while (hlist != NULL) {
299 header = hlist->data;
300 procheader_header_free(header);
301 hlist = g_slist_remove(hlist, header);
305 void procheader_header_array_destroy(GPtrArray *harray)
310 for (i = 0; i < harray->len; i++) {
311 header = g_ptr_array_index(harray, i);
312 procheader_header_free(header);
315 g_ptr_array_free(harray, TRUE);
318 void procheader_header_free(Header *header)
322 g_free(header->name);
323 g_free(header->body);
328 tests whether two headers' names are equal
329 remove the trailing ':' or ' ' before comparing
332 gboolean procheader_headername_equal(char * hdr1, char * hdr2)
339 if ((hdr1[len1 - 1] == ':') || (hdr1[len1 - 1] == ' '))
341 if ((hdr2[len2 - 1] == ':') || (hdr2[len2 - 1] == ' '))
345 return (strncasecmp(hdr1, hdr2, len1) == 0);
349 parse headers, for example :
350 From: dinh@enseirb.fr becomes :
351 header->name = "From:"
352 header->body = "dinh@enseirb.fr"
355 Header * procheader_parse_header(gchar * buf)
361 if ((*buf == ':') || (*buf == ' '))
364 for (p = buf; *p ; p++) {
365 if ((*p == ':') || (*p == ' ')) {
366 header = g_new(Header, 1);
367 header->name = g_strndup(buf, p - buf + 1);
369 while (*p == ' ' || *p == '\t') p++;
370 conv_unmime_header(tmp, sizeof(tmp), p, NULL);
371 header->body = g_strdup(tmp);
378 void procheader_get_header_fields(FILE *fp, HeaderEntry hentry[])
385 if (hentry == NULL) return;
387 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry))
391 p = buf + strlen(hp->name);
392 while (*p == ' ' || *p == '\t') p++;
394 if (hp->body == NULL)
395 hp->body = g_strdup(p);
396 else if (!strcasecmp(hp->name, "To:") ||
397 !strcasecmp(hp->name, "Cc:")) {
398 gchar *tp = hp->body;
399 hp->body = g_strconcat(tp, ", ", p, NULL);
419 H_DISPOSITION_NOTIFICATION_TO = 12
422 MsgInfo *procheader_parse(const gchar *file, MsgFlags flags, gboolean full)
424 static HeaderEntry hentry_full[] = {{"Date:", NULL, FALSE},
425 {"From:", NULL, TRUE},
428 {"Newsgroups:", NULL, TRUE},
429 {"Subject:", NULL, TRUE},
430 {"Message-Id:", NULL, FALSE},
431 {"References:", NULL, FALSE},
432 {"In-Reply-To:", NULL, FALSE},
433 {"Content-Type:", NULL, FALSE},
434 {"Seen:", NULL, FALSE},
435 {"X-Face:", NULL, FALSE},
436 {"Disposition-Notification-To:",NULL, FALSE},
437 {NULL, NULL, FALSE}};
439 static HeaderEntry hentry_short[] = {{"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 {NULL, NULL, FALSE}};
454 gchar buf[BUFFSIZE], tmp[BUFFSIZE];
455 gchar *reference = NULL;
461 hentry = full ? hentry_full : hentry_short;
463 if ((fp = fopen(file, "r")) == NULL) {
464 FILE_OP_ERROR(file, "fopen");
467 if (MSG_IS_QUEUED(flags)) {
468 while (fgets(buf, sizeof(buf), fp) != NULL)
469 if (buf[0] == '\r' || buf[0] == '\n') break;
472 msginfo = g_new0(MsgInfo, 1);
473 msginfo->flags = flags != 0 ? flags : MSG_NEW|MSG_UNREAD;
474 msginfo->inreplyto = NULL;
476 while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry))
478 hp = buf + strlen(hentry[hnum].name);
479 while (*hp == ' ' || *hp == '\t') hp++;
483 if (msginfo->date) break;
485 procheader_date_parse(NULL, hp, 0);
486 msginfo->date = g_strdup(hp);
489 if (msginfo->from) break;
490 conv_unmime_header(tmp, sizeof(tmp), hp, NULL);
491 msginfo->from = g_strdup(tmp);
492 msginfo->fromname = procheader_get_fromname(tmp);
495 conv_unmime_header(tmp, sizeof(tmp), hp, NULL);
499 g_strconcat(p, ", ", tmp, NULL);
502 msginfo->to = g_strdup(tmp);
505 conv_unmime_header(tmp, sizeof(tmp), hp, NULL);
509 g_strconcat(p, ", ", tmp, NULL);
512 msginfo->cc = g_strdup(tmp);
515 if (msginfo->newsgroups) {
516 p = msginfo->newsgroups;
517 msginfo->newsgroups =
518 g_strconcat(p, ",", hp, NULL);
521 msginfo->newsgroups = g_strdup(buf + 12);
524 if (msginfo->subject) break;
525 conv_unmime_header(tmp, sizeof(tmp), hp, NULL);
526 msginfo->subject = g_strdup(tmp);
529 if (msginfo->msgid) break;
531 extract_parenthesis(hp, '<', '>');
533 msginfo->msgid = g_strdup(hp);
537 eliminate_parenthesis(hp, '(', ')');
538 if ((p = strrchr(hp, '<')) != NULL &&
539 strchr(p + 1, '>') != NULL) {
540 extract_parenthesis(p, '<', '>');
543 reference = g_strdup(p);
549 eliminate_parenthesis(hp, '(', ')');
550 extract_parenthesis(hp, '<', '>');
553 reference = g_strdup(hp);
557 if (!strncasecmp(hp, "multipart", 9))
558 msginfo->flags |= MSG_MIME;
561 /* mnews Seen header */
562 MSG_UNSET_FLAGS(msginfo->flags, MSG_NEW|MSG_UNREAD);
565 if (msginfo->xface) break;
566 msginfo->xface = g_strdup(hp);
568 case H_DISPOSITION_NOTIFICATION_TO:
569 if (msginfo->dispositionnotificationto) break;
570 msginfo->dispositionnotificationto = g_strdup(hp);
575 msginfo->inreplyto = reference;
582 gchar *procheader_get_fromname(const gchar *str)
586 Xalloca(tmp, strlen(str) + 1, return NULL);
590 extract_quote(tmp, '\"');
592 } else if (strchr(tmp, '<')) {
593 eliminate_parenthesis(tmp, '<', '>');
597 extract_parenthesis(tmp, '<', '>');
600 } else if (strchr(tmp, '(')) {
601 extract_parenthesis(tmp, '(', ')');
606 name = g_strdup(str);
608 name = g_strdup(tmp);
613 time_t procheader_date_parse(gchar *dest, const gchar *src, gint len)
615 static gchar monthstr[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
628 /* parsing date field... */
629 result = sscanf(src, "%3s, %d %3s %d %2d:%2d:%2d %5s",
630 weekday, &day, month, &year, &hh, &mm, &ss, zone);
632 result = sscanf(src, "%d %3s %d %2d:%2d:%2d %5s",
633 &day, month, &year, &hh, &mm, &ss, zone);
636 result = sscanf(src, "%3s, %d %3s %d %2d:%2d %5s",
637 weekday, &day, month, &year, &hh, &mm, zone);
639 result = sscanf(src, "%d %3s %d %2d:%2d %5s",
640 &day, month, &year, &hh, &mm,
643 g_warning("Invalid date: %s\n", src);
645 strncpy2(dest, src, len);
652 /* Y2K compliant :) */
660 if ((p = strstr(monthstr, month)) != NULL)
661 dmonth = (gint)(p - monthstr) / 3 + 1;
663 g_warning("Invalid month: %s\n", month);
664 dmonth = G_DATE_BAD_MONTH;
671 t.tm_mon = dmonth - 1;
672 t.tm_year = year - 1900;
678 timer += tzoffset_sec(&timer) - remote_tzoffset_sec(zone);
681 procheader_date_get_localtime(dest, len, timer);
686 void procheader_date_get_localtime(gchar *dest, gint len, const time_t timer)
689 gchar *default_format = "%y/%m/%d(%a) %H:%M";
691 lt = localtime(&timer);
693 if (prefs_common.date_format)
694 strftime(dest, len, prefs_common.date_format, lt);
696 strftime(dest, len, default_format, lt);