1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2014 Hiroyuki Yamamoto and the Claws Mail team
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 3 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, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24 
25 #include <glib.h>
26 #include <glib/gi18n.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <time.h>
31 #include <sys/stat.h>
32 
33 #ifdef G_OS_WIN32
34 #  include <w32lib.h>
35 #endif
36 
37 #include "procheader.h"
38 #include "procmsg.h"
39 #include "codeconv.h"
40 #include "prefs_common.h"
41 #include "hooks.h"
42 #include "utils.h"
43 #include "defs.h"
44 #include "file-utils.h"
45 
46 #define BUFFSIZE	8192
47 
48 static gchar monthstr[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
49 
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[]);
54 
55 static gint string_get_one_field(gchar **buf, char **str,
56 				 HeaderEntry hentry[]);
57 
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,
62 				  HeaderEntry hentry[],
63 				  getlinefunc getline,
64 				  peekcharfunc peekchar,
65 				  gboolean unfold);
66 static MsgInfo *parse_stream(void *data, gboolean isstring, MsgFlags flags,
67 			     gboolean full, gboolean decrypted);
68 
69 
procheader_get_one_field(gchar ** buf,FILE * fp,HeaderEntry hentry[])70 gint procheader_get_one_field(gchar **buf, FILE *fp,
71 			      HeaderEntry hentry[])
72 {
73 	return generic_get_one_field(buf, fp, hentry,
74 				     (getlinefunc)fgets_crlf, (peekcharfunc)file_peekchar,
75 				     TRUE);
76 }
77 
string_get_one_field(gchar ** buf,char ** str,HeaderEntry hentry[])78 static gint string_get_one_field(gchar **buf, char **str,
79 				 HeaderEntry hentry[])
80 {
81 	return generic_get_one_field(buf, str, hentry,
82 				     (getlinefunc)string_getline,
83 				     (peekcharfunc)string_peekchar,
84 				     TRUE);
85 }
86 
procheader_skip_headers(FILE * fp)87 gboolean procheader_skip_headers(FILE *fp)
88 {
89 	gchar *buf = g_malloc(BUFFSIZE);
90 	do {
91 		if (fgets_crlf(buf, BUFFSIZE - 1, fp) == NULL) {
92 			g_free(buf);
93 			return FALSE;
94 		}
95 		if (buf[0] == '\r' || buf[0] == '\n') {
96 			break;
97 		}
98 	} while (TRUE);
99 	g_free(buf);
100 
101 	return TRUE;
102 }
103 
104 
string_getline(char * buf,size_t len,char ** str)105 static char *string_getline(char *buf, size_t len, char **str)
106 {
107 	gboolean is_cr = FALSE;
108 	gboolean last_was_cr = FALSE;
109 
110 	if (!*str || !**str)
111 		return NULL;
112 
113 	for (; **str && len > 1; --len) {
114 		is_cr = (**str == '\r');
115 		if ((*buf++ = *(*str)++) == '\n') {
116 		    break;
117 		}
118 		if (last_was_cr) {
119 			*(--buf) = '\n';
120 			buf++;
121 		    break;
122 		}
123 		last_was_cr = is_cr;
124 	}
125 
126 	*buf = '\0';
127 
128 	return buf;
129 }
130 
string_peekchar(char ** str)131 static int string_peekchar(char **str)
132 {
133 	return **str;
134 }
135 
file_peekchar(FILE * fp)136 static int file_peekchar(FILE *fp)
137 {
138 	return ungetc(getc(fp), fp);
139 }
140 
generic_get_one_field(gchar ** bufptr,void * data,HeaderEntry * hentry,getlinefunc getline,peekcharfunc peekchar,gboolean unfold)141 static gint generic_get_one_field(gchar **bufptr, void *data,
142 			  HeaderEntry *hentry,
143 			  getlinefunc getline, peekcharfunc peekchar,
144 			  gboolean unfold)
145 {
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
150 	*/
151 	gint nexthead;
152 	gint hnum = 0;
153 	HeaderEntry *hp = NULL;
154 	size_t len;
155 	gchar *buf;
156 
157 	cm_return_val_if_fail(bufptr != NULL, -1);
158 
159 	len = BUFFSIZE;
160 	buf = g_malloc(len);
161 
162 	if (hentry != NULL) {
163 		/* skip non-required headers */
164 		/* and get hentry header line */
165 		do {
166 			do {
167 				if (getline(buf, len, data) == NULL) {
168 					debug_print("generic_get_one_field: getline\n");
169 					g_free(buf);
170 					*bufptr = NULL;
171 					return -1;
172 				}
173 				if (buf[0] == '\r' || buf[0] == '\n') {
174 					debug_print("generic_get_one_field: empty line\n");
175 					g_free(buf);
176 					*bufptr = NULL;
177 					return -1;
178 				}
179 			} while (buf[0] == ' ' || buf[0] == '\t');
180 
181 			for (hp = hentry, hnum = 0; hp->name != NULL;
182 			     hp++, hnum++) {
183 				if (!g_ascii_strncasecmp(hp->name, buf,
184 						 strlen(hp->name)))
185 					break;
186 			}
187 		} while (hp->name == NULL);
188 	} else {
189 		/* read first line */
190 		if (getline(buf, len, data) == NULL) {
191 			debug_print("generic_get_one_field: getline\n");
192 			g_free(buf);
193 			*bufptr = NULL;
194 			return -1;
195 		}
196 		if (buf[0] == '\r' || buf[0] == '\n') {
197 			debug_print("generic_get_one_field: empty line\n");
198 			g_free(buf);
199 			*bufptr = NULL;
200 			return -1;
201 		}
202 	}
203 	/* reduce initial buffer to its useful part */
204 	len = strlen(buf)+1;
205 	buf = g_realloc(buf, len);
206 	if (buf == NULL) {
207 		debug_print("generic_get_one_field: reallocation error\n");
208 		*bufptr = NULL;
209 		return -1;
210 	}
211 
212 	/* unfold line */
213 	while (1) {
214 		nexthead = peekchar(data);
215 		/* ([*WSP CRLF] 1*WSP) */
216 		if (nexthead == ' ' || nexthead == '\t') {
217 			size_t buflen;
218 			gchar *tmpbuf;
219 			size_t tmplen;
220 
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))
225 				strretchomp(buf);
226 
227 			buflen = strlen(buf);
228 
229 			/* read next line */
230 			tmpbuf = g_malloc(BUFFSIZE);
231 
232 			if (getline(tmpbuf, BUFFSIZE, data) == NULL) {
233 				g_free(tmpbuf);
234 				break;
235 			}
236 			tmplen = strlen(tmpbuf)+1;
237 
238 			/* extend initial buffer and concatenate next line */
239 			len += tmplen;
240 			buf = g_realloc(buf, len);
241 			if (buf == NULL) {
242 				debug_print("generic_get_one_field: reallocation error\n");
243 				g_free(buf);
244 				*bufptr = NULL;
245 				return -1;
246 			}
247 			memcpy(buf+buflen, tmpbuf, tmplen);
248 			g_free(tmpbuf);
249 			if (skiptab) { /* replace tab with space */
250 				*(buf + buflen) = ' ';
251 			}
252 		} else {
253 			/* remove trailing new line */
254 			strretchomp(buf);
255 			break;
256 		}
257 	}
258 
259 	*bufptr = buf;
260 
261 	return hnum;
262 }
263 
procheader_get_one_field_asis(gchar ** buf,FILE * fp)264 gint procheader_get_one_field_asis(gchar **buf, FILE *fp)
265 {
266 	return generic_get_one_field(buf, fp, NULL,
267 				     (getlinefunc)fgets_crlf,
268 				     (peekcharfunc)file_peekchar,
269 				     FALSE);
270 }
271 
procheader_get_header_array_asis(FILE * fp)272 GPtrArray *procheader_get_header_array_asis(FILE *fp)
273 {
274 	gchar *buf = NULL;
275 	GPtrArray *headers;
276 	Header *header;
277 
278 	cm_return_val_if_fail(fp != NULL, NULL);
279 
280 	headers = g_ptr_array_new();
281 
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);
285 		g_free(buf);
286 		buf = NULL;
287 	}
288 
289 	return headers;
290 }
291 
procheader_header_array_destroy(GPtrArray * harray)292 void procheader_header_array_destroy(GPtrArray *harray)
293 {
294 	gint i;
295 	Header *header;
296 
297 	cm_return_if_fail(harray != NULL);
298 
299 	for (i = 0; i < harray->len; i++) {
300 		header = g_ptr_array_index(harray, i);
301 		procheader_header_free(header);
302 	}
303 
304 	g_ptr_array_free(harray, TRUE);
305 }
306 
procheader_header_free(Header * header)307 void procheader_header_free(Header *header)
308 {
309 	if (!header) return;
310 
311 	g_free(header->name);
312 	g_free(header->body);
313 	g_free(header);
314 }
315 
316 /*
317   tests whether two headers' names are equal
318   remove the trailing ':' or ' ' before comparing
319 */
320 
procheader_headername_equal(char * hdr1,char * hdr2)321 gboolean procheader_headername_equal(char * hdr1, char * hdr2)
322 {
323 	int len1;
324 	int len2;
325 
326 	len1 = strlen(hdr1);
327 	len2 = strlen(hdr2);
328 	if (hdr1[len1 - 1] == ':')
329 		len1--;
330 	if (hdr2[len2 - 1] == ':')
331 		len2--;
332 	if (len1 != len2)
333 		return 0;
334 
335 	return (g_ascii_strncasecmp(hdr1, hdr2, len1) == 0);
336 }
337 
338 /*
339   parse headers, for example :
340   From: dinh@enseirb.fr becomes :
341   header->name = "From:"
342   header->body = "dinh@enseirb.fr"
343  */
header_is_addr_field(const gchar * hdr)344 static gboolean header_is_addr_field(const gchar *hdr)
345 {
346 	static char *addr_headers[] = {
347 				"To:",
348 				"Cc:",
349 				"Bcc:",
350 				"From:",
351 				"Reply-To:",
352 				"Followup-To:",
353 				"Followup-and-Reply-To:",
354 				"Disposition-Notification-To:",
355 				"Return-Receipt-To:",
356 				NULL};
357 	int i;
358 
359 	if (!hdr)
360 		return FALSE;
361 
362 	for (i = 0; addr_headers[i] != NULL; i++)
363 		if (!strcasecmp(hdr, addr_headers[i]))
364 			return FALSE;
365 
366 	return FALSE;
367 }
368 
procheader_parse_header(gchar * buf)369 Header * procheader_parse_header(gchar * buf)
370 {
371 	gchar *p;
372 	Header * header;
373 	gboolean addr_field = FALSE;
374 
375 	cm_return_val_if_fail(buf != NULL, NULL);
376 
377 	if ((*buf == ':') || (*buf == ' '))
378 		return NULL;
379 
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);
385 			p++;
386 			while (*p == ' ' || *p == '\t') p++;
387 			header->body = conv_unmime_header(p, NULL, addr_field);
388 			return header;
389 		}
390 	}
391 	return NULL;
392 }
393 
procheader_get_header_fields(FILE * fp,HeaderEntry hentry[])394 void procheader_get_header_fields(FILE *fp, HeaderEntry hentry[])
395 {
396 	gchar *buf = NULL;
397 	HeaderEntry *hp;
398 	gint hnum;
399 	gchar *p;
400 
401 	if (hentry == NULL) return;
402 
403 	while ((hnum = procheader_get_one_field(&buf, fp, hentry)) != -1) {
404 		hp = hentry + hnum;
405 
406 		p = buf + strlen(hp->name);
407 		while (*p == ' ' || *p == '\t') p++;
408 
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);
415 			g_free(tp);
416 		}
417 		g_free(buf);
418 		buf = NULL;
419 	}
420 }
421 
procheader_parse_file(const gchar * file,MsgFlags flags,gboolean full,gboolean decrypted)422 MsgInfo *procheader_parse_file(const gchar *file, MsgFlags flags,
423 			       gboolean full, gboolean decrypted)
424 {
425 #ifdef G_OS_WIN32
426 	GFile *f;
427 	GFileInfo *fi;
428 	GTimeVal tv;
429 	GError *error = NULL;
430 #else
431 	GStatBuf s;
432 #endif
433 	FILE *fp;
434 	MsgInfo *msginfo;
435 
436 #ifdef G_OS_WIN32
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);
440 	if (error != NULL) {
441 		g_warning(error->message);
442 		g_error_free(error);
443 		g_object_unref(f);
444 	}
445 #else
446 	if (g_stat(file, &s) < 0) {
447 		FILE_OP_ERROR(file, "stat");
448 		return NULL;
449 	}
450 #endif
451 
452 #ifdef G_OS_WIN32
453 	if (g_file_info_get_file_type(fi) != G_FILE_TYPE_REGULAR) {
454 		g_object_unref(fi);
455 		g_object_unref(f);
456 		return NULL;
457 	}
458 #else
459 	if (!S_ISREG(s.st_mode))
460 		return NULL;
461 #endif
462 
463 	if ((fp = claws_fopen(file, "rb")) == NULL) {
464 		FILE_OP_ERROR(file, "claws_fopen");
465 		return NULL;
466 	}
467 
468 	msginfo = procheader_parse_stream(fp, flags, full, decrypted);
469 	claws_fclose(fp);
470 
471 	if (msginfo) {
472 #ifdef G_OS_WIN32
473 		msginfo->size = g_file_info_get_size(fi);
474 		g_file_info_get_modification_time(fi, &tv);
475 		msginfo->mtime = tv.tv_sec;
476 #else
477 		msginfo->size = s.st_size;
478 		msginfo->mtime = s.st_mtime;
479 #endif
480 	}
481 
482 #ifdef G_OS_WIN32
483 	g_object_unref(fi);
484 	g_object_unref(f);
485 #endif
486 
487 	return msginfo;
488 }
489 
procheader_parse_str(const gchar * str,MsgFlags flags,gboolean full,gboolean decrypted)490 MsgInfo *procheader_parse_str(const gchar *str, MsgFlags flags, gboolean full,
491 			      gboolean decrypted)
492 {
493 	return parse_stream(&str, TRUE, flags, full, decrypted);
494 }
495 
496 enum
497 {
498 	H_DATE = 0,
499 	H_FROM,
500 	H_TO,
501 	H_CC,
502 	H_NEWSGROUPS,
503 	H_SUBJECT,
504 	H_MSG_ID,
505 	H_REFERENCES,
506 	H_IN_REPLY_TO,
507 	H_CONTENT_TYPE,
508 	H_SEEN,
509 	H_STATUS,
510 	H_FROM_SPACE,
511 	H_SC_PLANNED_DOWNLOAD,
512 	H_SC_MESSAGE_SIZE,
513 	H_FACE,
514 	H_X_FACE,
515 	H_DISPOSITION_NOTIFICATION_TO,
516 	H_RETURN_RECEIPT_TO,
517 	H_SC_PARTIALLY_RETRIEVED,
518 	H_SC_ACCOUNT_SERVER,
519 	H_SC_ACCOUNT_LOGIN,
520 	H_LIST_POST,
521 	H_LIST_SUBSCRIBE,
522 	H_LIST_UNSUBSCRIBE,
523 	H_LIST_HELP,
524 	H_LIST_ARCHIVE,
525 	H_LIST_OWNER,
526 	H_RESENT_FROM,
527 };
528 
529 static HeaderEntry hentry_full[] = {
530 				   {"Date:",		NULL, FALSE},
531 				   {"From:",		NULL, TRUE},
532 				   {"To:",		NULL, TRUE},
533 				   {"Cc:",		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}};
560 
561 static HeaderEntry hentry_short[] = {
562 				    {"Date:",		NULL, FALSE},
563 				    {"From:",		NULL, TRUE},
564 				    {"To:",		NULL, TRUE},
565 				    {"Cc:",		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}};
578 
procheader_get_headernames(gboolean full)579 static HeaderEntry* procheader_get_headernames(gboolean full)
580 {
581 	return full ? hentry_full : hentry_short;
582 }
583 
procheader_parse_stream(FILE * fp,MsgFlags flags,gboolean full,gboolean decrypted)584 MsgInfo *procheader_parse_stream(FILE *fp, MsgFlags flags, gboolean full,
585 				 gboolean decrypted)
586 {
587 	return parse_stream(fp, FALSE, flags, full, decrypted);
588 }
589 
avatar_from_some_face(gpointer source,gpointer userdata)590 static gboolean avatar_from_some_face(gpointer source, gpointer userdata)
591 {
592 	AvatarCaptureData *acd = (AvatarCaptureData *)source;
593 
594 	if (*(acd->content) == '\0') /* won't be null, but may be empty */
595 		return FALSE;
596 
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);
600 	}
601 #if HAVE_LIBCOMPFACE
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);
605 	}
606 #endif
607 	return FALSE;
608 }
609 
610 static gulong avatar_hook_id = HOOK_NONE;
611 
parse_stream(void * data,gboolean isstring,MsgFlags flags,gboolean full,gboolean decrypted)612 static MsgInfo *parse_stream(void *data, gboolean isstring, MsgFlags flags,
613 			     gboolean full, gboolean decrypted)
614 {
615 	MsgInfo *msginfo;
616 	gchar *buf = NULL;
617 	gchar *p, *tmp;
618 	gchar *hp;
619 	HeaderEntry *hentry;
620 	gint hnum;
621 	void *orig_data = data;
622 
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;
626 
627 	hentry = procheader_get_headernames(full);
628 
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:")))) {
635 				g_free(buf);
636 				buf = NULL;
637 				break;
638 			}
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)) {
644 				if (isstring)
645 					data = orig_data;
646 				else
647 					rewind((FILE *)data);
648 				g_free(buf);
649 				buf = NULL;
650 				break;
651 			}
652 			g_free(buf);
653 			buf = NULL;
654 		}
655 	}
656 
657 	msginfo = procmsg_msginfo_new();
658 
659 	if (flags.tmp_flags || flags.perm_flags)
660 		msginfo->flags = flags;
661 	else
662 		MSG_SET_PERM_FLAGS(msginfo->flags, MSG_NEW | MSG_UNREAD);
663 
664 	msginfo->inreplyto = NULL;
665 
666 	if (avatar_hook_id == HOOK_NONE &&
667 	    (prefs_common.enable_avatars && (AVATARS_ENABLE_CAPTURE || AVATARS_ENABLE_RENDER))) {
668 		avatar_hook_id = hooks_register_hook(AVATAR_HEADER_UPDATE_HOOKLIST,
669 						     avatar_from_some_face, NULL);
670 	} else if (avatar_hook_id != HOOK_NONE &&
671 		   !(prefs_common.enable_avatars && AVATARS_ENABLE_CAPTURE)) {
672 		hooks_unregister_hook(AVATAR_HEADER_UPDATE_HOOKLIST, avatar_hook_id);
673 		avatar_hook_id = HOOK_NONE;
674 	}
675 
676 	while ((hnum = get_one_field(&buf, data, hentry)) != -1) {
677 		hp = buf + strlen(hentry[hnum].name);
678 		while (*hp == ' ' || *hp == '\t') hp++;
679 
680 		switch (hnum) {
681 		case H_DATE:
682 			if (msginfo->date) break;
683 			msginfo->date_t =
684 				procheader_date_parse(NULL, hp, 0);
685 			if (g_utf8_validate(hp, -1, NULL)) {
686 				msginfo->date = g_strdup(hp);
687 			} else {
688 				gchar *utf = conv_codeset_strdup(
689 					hp,
690 					conv_get_locale_charset_str_no_utf8(),
691 					CS_INTERNAL);
692 				if (utf == NULL ||
693 				    !g_utf8_validate(utf, -1, NULL)) {
694 					g_free(utf);
695 					utf = g_malloc(strlen(buf)*2+1);
696 					conv_localetodisp(utf,
697 						strlen(hp)*2+1, hp);
698 				}
699 				msginfo->date = utf;
700 			}
701 			break;
702 		case H_FROM:
703 			if (msginfo->from) break;
704 			msginfo->from = conv_unmime_header(hp, NULL, TRUE);
705 			msginfo->fromname = procheader_get_fromname(msginfo->from);
706 			remove_return(msginfo->from);
707 			remove_return(msginfo->fromname);
708 			break;
709 		case H_TO:
710 			tmp = conv_unmime_header(hp, NULL, TRUE);
711 			remove_return(tmp);
712 			if (msginfo->to) {
713 				p = msginfo->to;
714 				msginfo->to =
715 					g_strconcat(p, ", ", tmp, NULL);
716 				g_free(p);
717 			} else
718 				msginfo->to = g_strdup(tmp);
719                         g_free(tmp);
720 			break;
721 		case H_CC:
722 			tmp = conv_unmime_header(hp, NULL, TRUE);
723 			remove_return(tmp);
724 			if (msginfo->cc) {
725 				p = msginfo->cc;
726 				msginfo->cc =
727 					g_strconcat(p, ", ", tmp, NULL);
728 				g_free(p);
729 			} else
730 				msginfo->cc = g_strdup(tmp);
731                         g_free(tmp);
732 			break;
733 		case H_NEWSGROUPS:
734 			if (msginfo->newsgroups) {
735 				p = msginfo->newsgroups;
736 				msginfo->newsgroups =
737 					g_strconcat(p, ",", hp, NULL);
738 				g_free(p);
739 			} else
740 				msginfo->newsgroups = g_strdup(hp);
741 			break;
742 		case H_SUBJECT:
743 			if (msginfo->subject) break;
744 			msginfo->subject = conv_unmime_header(hp, NULL, FALSE);
745 			unfold_line(msginfo->subject);
746                        break;
747 		case H_MSG_ID:
748 			if (msginfo->msgid) break;
749 
750 			extract_parenthesis(hp, '<', '>');
751 			remove_space(hp);
752 			msginfo->msgid = g_strdup(hp);
753 			break;
754 		case H_REFERENCES:
755 			msginfo->references =
756 				references_list_prepend(msginfo->references,
757 							hp);
758 			break;
759 		case H_IN_REPLY_TO:
760 			if (msginfo->inreplyto) break;
761 
762 			eliminate_parenthesis(hp, '(', ')');
763 			if ((p = strrchr(hp, '<')) != NULL &&
764 			    strchr(p + 1, '>') != NULL) {
765 				extract_parenthesis(p, '<', '>');
766 				remove_space(p);
767 				if (*p != '\0')
768 					msginfo->inreplyto = g_strdup(p);
769 			}
770 			break;
771 		case H_CONTENT_TYPE:
772 			if (!g_ascii_strncasecmp(hp, "multipart/", 10))
773 				MSG_SET_TMP_FLAGS(msginfo->flags, MSG_MULTIPART);
774 			break;
775 		case H_DISPOSITION_NOTIFICATION_TO:
776 			if (!msginfo->extradata)
777 				msginfo->extradata = g_new0(MsgInfoExtraData, 1);
778 			if (msginfo->extradata->dispositionnotificationto) break;
779 			msginfo->extradata->dispositionnotificationto = g_strdup(hp);
780 			break;
781 		case H_RETURN_RECEIPT_TO:
782 			if (!msginfo->extradata)
783 				msginfo->extradata = g_new0(MsgInfoExtraData, 1);
784 			if (msginfo->extradata->returnreceiptto) break;
785 			msginfo->extradata->returnreceiptto = g_strdup(hp);
786 			break;
787 /* partial download infos */
788 		case H_SC_PARTIALLY_RETRIEVED:
789 			if (!msginfo->extradata)
790 				msginfo->extradata = g_new0(MsgInfoExtraData, 1);
791 			if (msginfo->extradata->partial_recv) break;
792 			msginfo->extradata->partial_recv = g_strdup(hp);
793 			break;
794 		case H_SC_ACCOUNT_SERVER:
795 			if (!msginfo->extradata)
796 				msginfo->extradata = g_new0(MsgInfoExtraData, 1);
797 			if (msginfo->extradata->account_server) break;
798 			msginfo->extradata->account_server = g_strdup(hp);
799 			break;
800 		case H_SC_ACCOUNT_LOGIN:
801 			if (!msginfo->extradata)
802 				msginfo->extradata = g_new0(MsgInfoExtraData, 1);
803 			if (msginfo->extradata->account_login) break;
804 			msginfo->extradata->account_login = g_strdup(hp);
805 			break;
806 		case H_SC_MESSAGE_SIZE:
807 			if (msginfo->total_size) break;
808 			msginfo->total_size = atoi(hp);
809 			break;
810 		case H_SC_PLANNED_DOWNLOAD:
811 			msginfo->planned_download = atoi(hp);
812 			break;
813 /* end partial download infos */
814 		case H_FROM_SPACE:
815 			if (msginfo->fromspace) break;
816 			msginfo->fromspace = g_strdup(hp);
817 			remove_return(msginfo->fromspace);
818 			break;
819 /* list infos */
820  		case H_LIST_POST:
821 			if (!msginfo->extradata)
822 				msginfo->extradata = g_new0(MsgInfoExtraData, 1);
823 			if (msginfo->extradata->list_post) break;
824 			msginfo->extradata->list_post = g_strdup(hp);
825 			break;
826 		case H_LIST_SUBSCRIBE:
827 			if (!msginfo->extradata)
828 				msginfo->extradata = g_new0(MsgInfoExtraData, 1);
829 			if (msginfo->extradata->list_subscribe) break;
830 			msginfo->extradata->list_subscribe = g_strdup(hp);
831 			break;
832 		case H_LIST_UNSUBSCRIBE:
833 			if (!msginfo->extradata)
834 				msginfo->extradata = g_new0(MsgInfoExtraData, 1);
835 			if (msginfo->extradata->list_unsubscribe) break;
836 			msginfo->extradata->list_unsubscribe = g_strdup(hp);
837 			break;
838 		case H_LIST_HELP:
839 			if (!msginfo->extradata)
840 				msginfo->extradata = g_new0(MsgInfoExtraData, 1);
841 			if (msginfo->extradata->list_help) break;
842 			msginfo->extradata->list_help = g_strdup(hp);
843 			break;
844 		case H_LIST_ARCHIVE:
845 			if (!msginfo->extradata)
846 				msginfo->extradata = g_new0(MsgInfoExtraData, 1);
847 			if (msginfo->extradata->list_archive) break;
848 			msginfo->extradata->list_archive = g_strdup(hp);
849 			break;
850 		case H_LIST_OWNER:
851 			if (!msginfo->extradata)
852 				msginfo->extradata = g_new0(MsgInfoExtraData, 1);
853 			if (msginfo->extradata->list_owner) break;
854 			msginfo->extradata->list_owner = g_strdup(hp);
855 			break;
856 		case H_RESENT_FROM:
857 			if (!msginfo->extradata)
858 				msginfo->extradata = g_new0(MsgInfoExtraData, 1);
859 			if (msginfo->extradata->resent_from) break;
860 			msginfo->extradata->resent_from = g_strdup(hp);
861 			break;
862 /* end list infos */
863 		default:
864 			break;
865 		}
866 		/* to avoid performance penalty hooklist is invoked only for
867 		   headers known to be able to generate avatars */
868 		if (hnum == H_FROM || hnum == H_X_FACE || hnum == H_FACE) {
869 			AvatarCaptureData *acd = g_new0(AvatarCaptureData, 1);
870 			/* no extra memory is wasted, hooks are expected to
871 			   take care of copying members when needed */
872 			acd->msginfo = msginfo;
873 			acd->header  = hentry_full[hnum].name;
874 			acd->content = hp;
875 			hooks_invoke(AVATAR_HEADER_UPDATE_HOOKLIST, (gpointer)acd);
876 			g_free(acd);
877 		}
878 		g_free(buf);
879 		buf = NULL;
880 	}
881 
882 	if (!msginfo->inreplyto && msginfo->references)
883 		msginfo->inreplyto =
884 			g_strdup((gchar *)msginfo->references->data);
885 
886 	return msginfo;
887 }
888 
procheader_get_fromname(const gchar * str)889 gchar *procheader_get_fromname(const gchar *str)
890 {
891 	gchar *tmp, *name;
892 
893 	Xstrdup_a(tmp, str, return NULL);
894 
895 	if (*tmp == '\"') {
896 		extract_quote(tmp, '\"');
897 		g_strstrip(tmp);
898 	} else if (strchr(tmp, '<')) {
899 		eliminate_parenthesis(tmp, '<', '>');
900 		g_strstrip(tmp);
901 		if (*tmp == '\0') {
902 			strcpy(tmp, str);
903 			extract_parenthesis(tmp, '<', '>');
904 			g_strstrip(tmp);
905 		}
906 	} else if (strchr(tmp, '(')) {
907 		extract_parenthesis(tmp, '(', ')');
908 		g_strstrip(tmp);
909 	}
910 
911 	if (*tmp == '\0')
912 		name = g_strdup(str);
913 	else
914 		name = g_strdup(tmp);
915 
916 	return name;
917 }
918 
procheader_scan_date_string(const gchar * str,gchar * weekday,gint * day,gchar * month,gint * year,gint * hh,gint * mm,gint * ss,gchar * zone)919 static gint procheader_scan_date_string(const gchar *str,
920 					gchar *weekday, gint *day,
921 					gchar *month, gint *year,
922 					gint *hh, gint *mm, gint *ss,
923 					gchar *zone)
924 {
925 	gint result;
926 	gint month_n;
927 	gint secfract;
928 	gint zone1 = 0, zone2 = 0;
929 	gchar offset_sign, zonestr[7];
930 	gchar sep1;
931 
932 	if (str == NULL)
933 		return -1;
934 
935 	result = sscanf(str, "%10s %d %9s %d %2d:%2d:%2d %6s",
936 			weekday, day, month, year, hh, mm, ss, zone);
937 	if (result == 8) return 0;
938 
939 	/* RFC2822 */
940 	result = sscanf(str, "%3s,%d %9s %d %2d:%2d:%2d %6s",
941 			weekday, day, month, year, hh, mm, ss, zone);
942 	if (result == 8) return 0;
943 
944 	result = sscanf(str, "%3s %3s %d %2d:%2d:%2d %d %6s",
945 			weekday, month, day, hh, mm, ss, year, zone);
946 	if (result == 8) return 0;
947 
948 	result = sscanf(str, "%d %9s %d %2d:%2d:%2d %6s",
949 			day, month, year, hh, mm, ss, zone);
950 	if (result == 7) return 0;
951 
952 	*zone = '\0';
953 	result = sscanf(str, "%10s %d %9s %d %2d:%2d:%2d",
954 			weekday, day, month, year, hh, mm, ss);
955 	if (result == 7) return 0;
956 
957 	result = sscanf(str, "%3s %3s %d %2d:%2d:%2d %d",
958 			weekday, month, day, hh, mm, ss, year);
959 	if (result == 7) return 0;
960 
961 	result = sscanf(str, "%d %9s %d %2d:%2d:%2d",
962 			day, month, year, hh, mm, ss);
963 	if (result == 6) return 0;
964 
965 	*ss = 0;
966 	result = sscanf(str, "%10s %d %9s %d %2d:%2d %6s",
967 			weekday, day, month, year, hh, mm, zone);
968 	if (result == 7) return 0;
969 
970 	result = sscanf(str, "%d %9s %d %2d:%2d %5s",
971 			day, month, year, hh, mm, zone);
972 	if (result == 6) return 0;
973 
974 	*zone = '\0';
975 	result = sscanf(str, "%10s %d %9s %d %2d:%2d",
976 			weekday, day, month, year, hh, mm);
977 	if (result == 6) return 0;
978 
979 	result = sscanf(str, "%d %9s %d %2d:%2d",
980 			day, month, year, hh, mm);
981 	if (result == 5) return 0;
982 
983 	*weekday = '\0';
984 
985 	/* RFC3339 subset, with fraction of second */
986 	result = sscanf(str, "%4d-%2d-%2d%c%2d:%2d:%2d.%d%6s",
987 			year, &month_n, day, &sep1, hh, mm, ss, &secfract, zonestr);
988 	if (result == 9
989 			&& (sep1 == 'T' || sep1 == 't' || sep1 == ' ')) {
990 		if (month_n >= 1 && month_n <= 12) {
991 			strncpy2(month, monthstr+((month_n-1)*3), 4);
992 			if (zonestr[0] == 'z' || zonestr[0] == 'Z') {
993 				strcat(zone, "+00:00");
994 			} else if (sscanf(zonestr, "%c%2d:%2d",
995 						&offset_sign, &zone1, &zone2) == 3) {
996 				strcat(zone, zonestr);
997 			}
998 			return 0;
999 		}
1000 	}
1001 
1002 	/* RFC3339 subset, no fraction of second */
1003 	result = sscanf(str, "%4d-%2d-%2d%c%2d:%2d:%2d%6s",
1004 			year, &month_n, day, &sep1, hh, mm, ss, zonestr);
1005 	if (result == 8
1006 			&& (sep1 == 'T' || sep1 == 't' || sep1 == ' ')) {
1007 		if (month_n >= 1 && month_n <= 12) {
1008 			strncpy2(month, monthstr+((month_n-1)*3), 4);
1009 			if (zonestr[0] == 'z' || zonestr[0] == 'Z') {
1010 				strcat(zone, "+00:00");
1011 			} else if (sscanf(zonestr, "%c%2d:%2d",
1012 						&offset_sign, &zone1, &zone2) == 3) {
1013 				strcat(zone, zonestr);
1014 			}
1015 			return 0;
1016 		}
1017 	}
1018 
1019 	*zone = '\0';
1020 
1021 	/* RFC3339 subset, no fraction of second, and no timezone offset */
1022 	/* This particular "subset" is invalid, RFC requires the offset */
1023 	result = sscanf(str, "%4d-%2d-%2d %2d:%2d:%2d",
1024 			year, &month_n, day, hh, mm, ss);
1025 	if (result == 6) {
1026 		if (1 <= month_n && month_n <= 12) {
1027 			strncpy2(month, monthstr+((month_n-1)*3), 4);
1028 			return 0;
1029 		}
1030 	}
1031 
1032 	/* ISO8601 format with just date (YYYY-MM-DD) */
1033 	result = sscanf(str, "%4d-%2d-%2d",
1034 			year, &month_n, day);
1035 	if (result == 3) {
1036 		*hh = *mm = *ss = 0;
1037 		if (1 <= month_n && month_n <= 12) {
1038 			strncpy2(month, monthstr+((month_n-1)*3), 4);
1039 			return 0;
1040 		}
1041 	}
1042 
1043 	return -1;
1044 }
1045 
1046 /*
1047  * Hiro, most UNIXen support this function:
1048  * http://www.mcsr.olemiss.edu/cgi-bin/man-cgi?getdate
1049  */
procheader_date_parse_to_tm(const gchar * src,struct tm * t,char * zone)1050 gboolean procheader_date_parse_to_tm(const gchar *src, struct tm *t, char *zone)
1051 {
1052 	gchar weekday[11];
1053 	gint day;
1054 	gchar month[10];
1055 	gint year;
1056 	gint hh, mm, ss;
1057 	GDateMonth dmonth;
1058 	gchar *p;
1059 
1060 	if (!t)
1061 		return FALSE;
1062 
1063 	memset(t, 0, sizeof *t);
1064 
1065 	if (procheader_scan_date_string(src, weekday, &day, month, &year,
1066 					&hh, &mm, &ss, zone) < 0) {
1067 		g_warning("Invalid date: %s", src);
1068 		return FALSE;
1069 	}
1070 
1071 	/* Y2K compliant :) */
1072 	if (year < 100) {
1073 		if (year < 70)
1074 			year += 2000;
1075 		else
1076 			year += 1900;
1077 	}
1078 
1079 	month[3] = '\0';
1080 	if ((p = strstr(monthstr, month)) != NULL)
1081 		dmonth = (gint)(p - monthstr) / 3 + 1;
1082 	else {
1083 		g_warning("Invalid month: %s", month);
1084 		dmonth = G_DATE_BAD_MONTH;
1085 	}
1086 
1087 	t->tm_sec = ss;
1088 	t->tm_min = mm;
1089 	t->tm_hour = hh;
1090 	t->tm_mday = day;
1091 	t->tm_mon = dmonth - 1;
1092 	t->tm_year = year - 1900;
1093 	t->tm_wday = 0;
1094 	t->tm_yday = 0;
1095 	t->tm_isdst = -1;
1096 
1097 	mktime(t);
1098 
1099 	return TRUE;
1100 }
1101 
procheader_date_parse(gchar * dest,const gchar * src,gint len)1102 time_t procheader_date_parse(gchar *dest, const gchar *src, gint len)
1103 {
1104 	gchar weekday[11];
1105 	gint day;
1106 	gchar month[10];
1107 	gint year;
1108 	gint hh, mm, ss;
1109 	gchar zone[7];
1110 	GDateMonth dmonth = G_DATE_BAD_MONTH;
1111 	gchar *p;
1112 	time_t timer;
1113 
1114 	if (procheader_scan_date_string(src, weekday, &day, month, &year,
1115 					&hh, &mm, &ss, zone) < 0) {
1116 		if (dest && len > 0)
1117 			strncpy2(dest, src, len);
1118 		return 0;
1119 	}
1120 
1121 	month[3] = '\0';
1122 	for (p = monthstr; *p != '\0'; p += 3) {
1123 		if (!g_ascii_strncasecmp(p, month, 3)) {
1124 			dmonth = (gint)(p - monthstr) / 3 + 1;
1125 			break;
1126 		}
1127 	}
1128 
1129 #ifdef G_OS_WIN32
1130 	GTimeZone *tz;
1131 	GDateTime *dt, *dt2;
1132 
1133 	tz = g_time_zone_new(zone); // can't return NULL no need to check for it
1134 	dt = g_date_time_new(tz, 1, 1, 1, 0, 0, 0);
1135 	g_time_zone_unref(tz);
1136 	dt2 = g_date_time_add_full(dt, year-1, dmonth-1, day-1, hh, mm, ss);
1137 	g_date_time_unref(dt);
1138 
1139 	timer = g_date_time_to_unix(dt2);
1140 	g_date_time_unref(dt2);
1141 
1142 #else
1143 	struct tm t;
1144 	time_t tz_offset;
1145 
1146 	/* Y2K compliant :) */
1147 	if (year < 1000) {
1148 		if (year < 50)
1149 			year += 2000;
1150 		else
1151 			year += 1900;
1152 	}
1153 
1154 	t.tm_sec = ss;
1155 	t.tm_min = mm;
1156 	t.tm_hour = hh;
1157 	t.tm_mday = day;
1158 	t.tm_mon = dmonth - 1;
1159 	t.tm_year = year - 1900;
1160 	t.tm_wday = 0;
1161 	t.tm_yday = 0;
1162 	t.tm_isdst = -1;
1163 
1164 	timer = mktime(&t);
1165 	tz_offset = remote_tzoffset_sec(zone);
1166 	if (tz_offset != -1)
1167 		timer += tzoffset_sec(&timer) - tz_offset;
1168 
1169 	if (dest)
1170 		procheader_date_get_localtime(dest, len, timer);
1171 #endif
1172 
1173 	return timer;
1174 }
1175 
procheader_date_get_localtime(gchar * dest,gint len,const time_t timer)1176 void procheader_date_get_localtime(gchar *dest, gint len, const time_t timer)
1177 {
1178 	struct tm *lt;
1179 	gchar *default_format = "%y/%m/%d(%a) %H:%M";
1180 	gchar *str;
1181 	const gchar *src_codeset, *dest_codeset;
1182 	struct tm buf;
1183 
1184 	if (timer > 0)
1185 		lt = localtime_r(&timer, &buf);
1186 	else {
1187 		time_t dummy = 1;
1188 		lt = localtime_r(&dummy, &buf);
1189 	}
1190 
1191 	if (prefs_common.date_format)
1192 		fast_strftime(dest, len, prefs_common.date_format, lt);
1193 	else
1194 		fast_strftime(dest, len, default_format, lt);
1195 
1196 	if (!g_utf8_validate(dest, -1, NULL)) {
1197 		src_codeset = conv_get_locale_charset_str_no_utf8();
1198 		dest_codeset = CS_UTF_8;
1199 		str = conv_codeset_strdup(dest, src_codeset, dest_codeset);
1200 		if (str) {
1201 			strncpy2(dest, str, len);
1202 			g_free(str);
1203 		}
1204 	}
1205 }
1206 
1207 /* Added by Mel Hadasht on 27 Aug 2001 */
1208 /* Get a header from msginfo */
procheader_get_header_from_msginfo(MsgInfo * msginfo,gchar ** buf,gchar * header)1209 gint procheader_get_header_from_msginfo(MsgInfo *msginfo, gchar **buf, gchar *header)
1210 {
1211 	gchar *file;
1212 	FILE *fp;
1213 	HeaderEntry hentry[]={ { NULL, NULL, TRUE  },
1214 						   { NULL, NULL, FALSE } };
1215 	gint val;
1216 
1217 	cm_return_val_if_fail(msginfo != NULL, -1);
1218 	cm_return_val_if_fail(buf != NULL, -1);
1219 	cm_return_val_if_fail(header != NULL, -1);
1220 
1221 	hentry[0].name = header;
1222 
1223 	file = procmsg_get_message_file_path(msginfo);
1224 	if ((fp = claws_fopen(file, "rb")) == NULL) {
1225 		FILE_OP_ERROR(file, "claws_fopen");
1226 		g_free(file);
1227 		g_free(*buf);
1228 		*buf = NULL;
1229 		return -1;
1230 	}
1231 	val = procheader_get_one_field(buf, fp, hentry);
1232 
1233 	if (claws_fclose(fp) == EOF) {
1234 		FILE_OP_ERROR(file, "claws_fclose");
1235 		claws_unlink(file);
1236 		g_free(file);
1237 		g_free(*buf);
1238 		*buf = NULL;
1239 		return -1;
1240 	}
1241 
1242 	g_free(file);
1243 	if (val == -1) {
1244 		/* *buf is already NULL in that case, see procheader_get_one_field() */
1245 		return -1;
1246 	}
1247 
1248 	return 0;
1249 }
1250 
procheader_entries_from_str(const gchar * str)1251 HeaderEntry *procheader_entries_from_str(const gchar *str)
1252 {
1253 	HeaderEntry *entries = NULL, *he;
1254 	int numh = 0, i = 0;
1255 	gchar **names = NULL;
1256 	const gchar *s = str;
1257 
1258 	if (s == NULL) {
1259 		return NULL;
1260 	}
1261 	while (*s != '\0') {
1262 		if (*s == ' ') ++numh;
1263 		++s;
1264 	}
1265 	if (numh == 0) {
1266 		return NULL;
1267 	}
1268 	entries = g_new0(HeaderEntry, numh + 1); /* room for last NULL */
1269 	s = str;
1270 	++s; /* skip first space */
1271 	names = g_strsplit(s, " ", numh);
1272 	he = entries;
1273 	while (names[i]) {
1274 		he->name = g_strdup_printf("%s:", names[i]);
1275 		he->body = NULL;
1276 		he->unfold = FALSE;
1277 		++i, ++he;
1278 	}
1279 	he->name = NULL;
1280 	g_strfreev(names);
1281 	return entries;
1282 }
1283 
procheader_entries_free(HeaderEntry * entries)1284 void procheader_entries_free (HeaderEntry *entries)
1285 {
1286 	if (entries != NULL) {
1287 		HeaderEntry *he = entries;
1288 		while (he->name != NULL) {
1289 			g_free(he->name);
1290 			if (he->body != NULL)
1291 				g_free(he->body);
1292 			++he;
1293 		}
1294 		g_free(entries);
1295 	}
1296 }
1297 
procheader_header_is_internal(const gchar * hdr_name)1298 gboolean procheader_header_is_internal(const gchar *hdr_name)
1299 {
1300 	const gchar *internal_hdrs[] = {
1301 		"AF:", "NF:", "PS:", "SRH:", "SFN:", "DSR:", "MID:", "CFG:",
1302 		"PT:", "S:", "RQ:", "SSV:", "NSV:", "SSH:", "R:", "MAID:",
1303 		"SCF:", "RMID:", "FMID:", "NAID:",
1304 		"X-Claws-Account-Id:",
1305 		"X-Claws-Sign:",
1306 		"X-Claws-Encrypt:",
1307 		"X-Claws-Privacy-System:",
1308 		"X-Claws-Auto-Wrapping:",
1309 		"X-Claws-Auto-Indent:",
1310 		"X-Claws-End-Special-Headers:",
1311 		"X-Sylpheed-Account-Id:",
1312 		"X-Sylpheed-Sign:",
1313 		"X-Sylpheed-Encrypt:",
1314 		"X-Sylpheed-Privacy-System:",
1315 		"X-Sylpheed-End-Special-Headers:",
1316 	         NULL
1317 	};
1318 	int i;
1319 
1320 	for (i = 0; internal_hdrs[i]; i++) {
1321 	        if (!strcmp(hdr_name, internal_hdrs[i]))
1322 	                return TRUE;
1323 	}
1324 	return FALSE;
1325 }
1326