[Commits] [SCM] claws branch, gtk3, updated. 3.16.0-161-g06cd7d5

ticho at claws-mail.org ticho at claws-mail.org
Fri Apr 13 20:10:59 CEST 2018


The branch, gtk3 has been updated
       via  06cd7d5ff519c5e42f42a31039da08da2a852eb4 (commit)
       via  3f37fcce10bcfa459de264e16fa5fe1af11266da (commit)
       via  626d2ce199ae3dfe0d9df9c9066c9db0a1954f8c (commit)
       via  1cdd65c44bfa6a6c4c8974739bc47a1a533ede7c (commit)
       via  d6a1f0517342b78cda0124ddd40a267bdeb21e46 (commit)
       via  ff34cac72c75d8a27768d816e21b8964d17f374f (commit)
       via  51a0775804b6263c7b271d2aa57724beb3c14b3d (commit)
       via  41e1892a4aece3d3c61b4bb4fcc9c8d7e304606c (commit)
       via  bf01fb086584be7a2a700b0922cc6dff6dbee536 (commit)
       via  b60df1ce713f7bea19165353ffc29473856c1c71 (commit)
       via  8fc140e5e240bc0e07e8de3e1d968920176f622f (commit)
       via  0556e39de02c64f5b56f3842206178be294bdb9e (commit)
       via  0586c3cf2219f2bf97a1bcbbd6d466f0eaad6b1c (commit)
       via  43ca86e8186a530dacd3109db30e891a9f2bbccb (commit)
       via  aeee0bb656c377ef4f1dce8397e87d5fa148b601 (commit)
       via  1604a580535adbec6760ef29cce70cc80fe0cf39 (commit)
       via  230d47a8a5f98eb3a6e44d98b5796a3fd8f5fac9 (commit)
       via  45eaea6de4237e6aab2a2fc3b333a520ae4876d1 (commit)
       via  e95d423306c62cd3bfbf9abba895f968f8a67846 (commit)
      from  06906075b0304ace66e876c722c53cc376455dbe (commit)

Summary of changes:
 configure.ac                                |   22 +
 src/compose.c                               |  124 +-
 src/compose.h                               |   13 +-
 src/editaddress.c                           |    4 +-
 src/folder.c                                |    6 +-
 src/plugins/clamd/libclamd/clamd-plugin.c   |   12 +-
 src/plugins/pgpcore/select-keys.c           |    2 +-
 src/plugins/smime/smime.c                   |    8 +-
 src/plugins/spamassassin/Makefile.am        |    8 +-
 src/plugins/spamassassin/NOTICE             |   35 +
 src/plugins/spamassassin/libspamc.c         | 1648 +++++++++++++++++++++------
 src/plugins/spamassassin/libspamc.h         |  126 +-
 src/plugins/spamassassin/spamassassin.c     |   22 +-
 src/plugins/spamassassin/spamassassin.h     |    1 +
 src/plugins/spamassassin/spamassassin_gtk.c |   30 +-
 src/plugins/spamassassin/utils.c            |   41 +-
 src/plugins/spamassassin/utils.h            |   59 +-
 src/plugins/vcalendar/vcal_manager.c        |    2 +-
 src/summaryview.c                           |   11 +-
 19 files changed, 1698 insertions(+), 476 deletions(-)
 create mode 100644 src/plugins/spamassassin/NOTICE


- Log -----------------------------------------------------------------
commit 06cd7d5ff519c5e42f42a31039da08da2a852eb4
Author: Andrej Kacian <ticho at claws-mail.org>
Date:   Tue Apr 10 19:46:01 2018 +0200

    Rework and simplify how compose_queue() return values are handled
    
    Also closes bug #3960 - Sends unencrypted emails when encryption fails
    because we now return from compose_queue() with an error when
    that happens, instead of ignoring it.

diff --git a/src/compose.c b/src/compose.c
index 25488d6..a6eb6d0 100644
--- a/src/compose.c
+++ b/src/compose.c
@@ -317,7 +317,7 @@ static gint compose_write_body_to_file		(Compose	*compose,
 static gint compose_remove_reedit_target	(Compose	*compose,
 						 gboolean	 force);
 static void compose_remove_draft			(Compose	*compose);
-static gint compose_queue_sub			(Compose	*compose,
+static ComposeQueueResult compose_queue_sub			(Compose	*compose,
 						 gint		*msgnum,
 						 FolderItem	**item,
 						 gchar		**msgpath,
@@ -5224,11 +5224,47 @@ static gboolean compose_check_entries(Compose *compose, gboolean check_everythin
 	return TRUE;
 }
 
+static void _display_queue_error(ComposeQueueResult val)
+{
+	switch (val) {
+		case COMPOSE_QUEUE_SUCCESS:
+			break;
+		case COMPOSE_QUEUE_ERROR_NO_MSG:
+			alertpanel_error(_("Could not queue message."));
+			break;
+		case COMPOSE_QUEUE_ERROR_WITH_ERRNO:
+			alertpanel_error(_("Could not queue message:\n\n%s."),
+					g_strerror(errno));
+			break;
+		case COMPOSE_QUEUE_ERROR_SIGNING_FAILED:
+			alertpanel_error(_("Could not queue message for sending:\n\n"
+						"Signature failed: %s"), privacy_get_error());
+			break;
+		case COMPOSE_QUEUE_ERROR_ENCRYPT_FAILED:
+			alertpanel_error(_("Could not queue message for sending:\n\n"
+						"Encryption failed: %s"), privacy_get_error());
+			break;
+		case COMPOSE_QUEUE_ERROR_CHAR_CONVERSION:
+			alertpanel_error(_("Could not queue message for sending:\n\n"
+						"Charset conversion failed."));
+			break;
+		case COMPOSE_QUEUE_ERROR_NO_ENCRYPTION_KEY:
+			alertpanel_error(_("Could not queue message for sending:\n\n"
+						"Couldn't get recipient encryption key."));
+			break;
+		default:
+			/* unhandled error */
+			debug_print("oops, unhandled compose_queue() return value %d\n",
+					val);
+			break;
+	}
+}
+
 gint compose_send(Compose *compose)
 {
 	gint msgnum;
 	FolderItem *folder = NULL;
-	gint val = -1;
+	ComposeQueueResult val = COMPOSE_QUEUE_ERROR_NO_MSG;
 	gchar *msgpath = NULL;
 	gboolean discard_window = FALSE;
 	gchar *errstr = NULL;
@@ -5253,27 +5289,13 @@ gint compose_send(Compose *compose)
 	inc_lock();
 	val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
 
-	if (val) {
+	if (val != COMPOSE_QUEUE_SUCCESS) {
 		if (compose->batch) {
 			gtk_widget_show_all(compose->window);
 		}
-		if (val == -4) {
-			alertpanel_error(_("Could not queue message for sending:\n\n"
-					   "Charset conversion failed."));
-		} else if (val == -5) {
-			alertpanel_error(_("Could not queue message for sending:\n\n"
-					   "Couldn't get recipient encryption key."));
-		} else if (val == -6) {
-			/* silent error */
-		} else if (val == -3) {
-			if (privacy_peek_error())
-			alertpanel_error(_("Could not queue message for sending:\n\n"
-					   "Signature failed: %s"), privacy_get_error());
-		} else if (val == -2 && errno != 0) {
-			alertpanel_error(_("Could not queue message for sending:\n\n%s."), g_strerror(errno));
-		} else {
-			alertpanel_error(_("Could not queue message for sending."));
-		}
+
+		_display_queue_error(val);
+
 		goto bail;
 	}
 
@@ -5750,7 +5772,7 @@ static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gbool
 
 			if (aval != G_ALERTALTERNATE) {
 				g_free(chars);
-				return -3;
+				return COMPOSE_QUEUE_ERROR_CHAR_CONVERSION;
 			} else {
 				buf = chars;
 				out_codeset = src_codeset;
@@ -5811,7 +5833,7 @@ static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gbool
 		g_free(msg);
 		if (aval != G_ALERTALTERNATE) {
 			g_free(buf);
-			return -1;
+			return COMPOSE_QUEUE_ERROR_NO_MSG;
 		}
 	}
 	
@@ -5914,8 +5936,7 @@ static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gbool
 			if (!privacy_encrypt(compose->privacy_system, mimemsg, compose->encdata)) {
 				debug_print("Couldn't encrypt mime structure: %s.\n",
 						privacy_get_error());
-				alertpanel_error(_("Couldn't encrypt the email: %s"),
-						privacy_get_error());
+				return COMPOSE_QUEUE_ERROR_ENCRYPT_FAILED;
 			}
 		}
 	}
@@ -6021,7 +6042,7 @@ static void compose_remove_draft(Compose *compose)
 
 }
 
-gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
+ComposeQueueResult compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
 		   gboolean remove_reedit_target)
 {
 	return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
@@ -6052,7 +6073,7 @@ static gboolean compose_warn_encryption(Compose *compose)
 	} 
 }
 
-static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, 
+static ComposeQueueResult compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, 
 			      gchar **msgpath, gboolean perform_checks,
 			      gboolean remove_reedit_target)
 {
@@ -6071,12 +6092,12 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
 		if (compose->batch) {
 			gtk_widget_show_all(compose->window);
 		}
-                return -1;
+                return COMPOSE_QUEUE_ERROR_NO_MSG;
 	}
 
 	if (!compose->to_list && !compose->newsgroup_list) {
 	        g_warning("can't get recipient list.");
-                return -1;
+                return COMPOSE_QUEUE_ERROR_NO_MSG;
         }
 
 	if (compose->to_list) {
@@ -6086,7 +6107,7 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
 	    		mailac = cur_account;
 		else if (!(mailac = compose_current_mail_account())) {
 			alertpanel_error(_("No account for sending mails available!"));
-			return -1;
+			return COMPOSE_QUEUE_ERROR_NO_MSG;
 		}
 	}
 
@@ -6095,7 +6116,7 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
                         newsac = compose->account;
                 else {
 			alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
-			return -1;
+			return COMPOSE_QUEUE_ERROR_NO_MSG;
 		}			
 	}
 
@@ -6106,7 +6127,7 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
 	if ((fp = g_fopen(tmp, "w+b")) == NULL) {
 		FILE_OP_ERROR(tmp, "fopen");
 		g_free(tmp);
-		return -2;
+		return COMPOSE_QUEUE_ERROR_WITH_ERRNO;
 	}
 
 	if (change_file_mode_rw(fp, tmp) < 0) {
@@ -6169,7 +6190,7 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
 				fclose(fp);
 				claws_unlink(tmp);
 				g_free(tmp);
-				return -6;
+				return COMPOSE_QUEUE_ERROR_NO_MSG;
 			}
 			if (mailac && mailac->encrypt_to_self) {
 				GSList *tmp_list = g_slist_copy(compose->to_list);
@@ -6194,7 +6215,7 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
 				fclose(fp);
 				claws_unlink(tmp);
 				g_free(tmp);
-				return -5;
+				return COMPOSE_QUEUE_ERROR_NO_ENCRYPTION_KEY;
 			}
 		}
 	}
@@ -6247,7 +6268,7 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
 			fclose(fp);
 			claws_unlink(tmp);
 			g_free(tmp);
-			return -2;
+			return COMPOSE_QUEUE_ERROR_WITH_ERRNO;
 		}
 	} else {
 		gint result = 0;
@@ -6255,7 +6276,7 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
 			fclose(fp);
 			claws_unlink(tmp);
 			g_free(tmp);
-			return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
+			return result;
 		}
 	}
 	if (err == TRUE) {
@@ -6263,13 +6284,13 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
 		fclose(fp);
 		claws_unlink(tmp);
 		g_free(tmp);
-		return -2;
+		return COMPOSE_QUEUE_ERROR_WITH_ERRNO;
 	}
 	if (fclose(fp) == EOF) {
 		FILE_OP_ERROR(tmp, "fclose");
 		claws_unlink(tmp);
 		g_free(tmp);
-		return -2;
+		return COMPOSE_QUEUE_ERROR_WITH_ERRNO;
 	}
 
 	if (item && *item) {
@@ -6281,14 +6302,14 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
 		g_warning("can't find queue folder");
 		claws_unlink(tmp);
 		g_free(tmp);
-		return -1;
+		return COMPOSE_QUEUE_ERROR_NO_MSG;
 	}
 	folder_item_scan(queue);
 	if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
 		g_warning("can't queue the message");
 		claws_unlink(tmp);
 		g_free(tmp);
-		return -1;
+		return COMPOSE_QUEUE_ERROR_NO_MSG;
 	}
 	
 	if (msgpath == NULL) {
@@ -6306,7 +6327,7 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
 		*item = queue;
 	}
 
-	return 0;
+	return COMPOSE_QUEUE_SUCCESS;
 }
 
 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
@@ -10210,7 +10231,7 @@ static void compose_send_cb(GtkAction *action, gpointer data)
 static void compose_send_later_cb(GtkAction *action, gpointer data)
 {
 	Compose *compose = (Compose *)data;
-	gint val;
+	ComposeQueueResult val;
 
 	inc_lock();
 	compose_allow_user_actions(compose, FALSE);
@@ -10218,25 +10239,12 @@ static void compose_send_later_cb(GtkAction *action, gpointer data)
 	compose_allow_user_actions(compose, TRUE);
 	inc_unlock();
 
-	if (!val) {
+	if (val == COMPOSE_QUEUE_SUCCESS) {
 		compose_close(compose);
-	} else if (val == -1) {
-		alertpanel_error(_("Could not queue message."));
-	} else if (val == -2) {
-		alertpanel_error(_("Could not queue message:\n\n%s."), g_strerror(errno));
-	} else if (val == -3) {
-		if (privacy_peek_error())
-		alertpanel_error(_("Could not queue message for sending:\n\n"
-				   "Signature failed: %s"), privacy_get_error());
-	} else if (val == -4) {
-		alertpanel_error(_("Could not queue message for sending:\n\n"
-				   "Charset conversion failed."));
-	} else if (val == -5) {
-		alertpanel_error(_("Could not queue message for sending:\n\n"
-				   "Couldn't get recipient encryption key."));
-	} else if (val == -6) {
-		/* silent error */
+	} else {
+		_display_queue_error(val);
 	}
+
 	toolbar_main_set_sensitive(mainwindow_get_mainwindow());
 }
 
diff --git a/src/compose.h b/src/compose.h
index ae2212b..dee18a1 100644
--- a/src/compose.h
+++ b/src/compose.h
@@ -84,6 +84,17 @@ typedef enum
 	COMPOSE_REEDIT
 } ComposeMode;
 
+typedef enum
+{
+	COMPOSE_QUEUE_SUCCESS = 0,
+	COMPOSE_QUEUE_ERROR_NO_MSG = -1,
+	COMPOSE_QUEUE_ERROR_WITH_ERRNO = -2,
+	COMPOSE_QUEUE_ERROR_SIGNING_FAILED = -3,
+	COMPOSE_QUEUE_ERROR_ENCRYPT_FAILED = -4,
+	COMPOSE_QUEUE_ERROR_CHAR_CONVERSION = -5,
+	COMPOSE_QUEUE_ERROR_NO_ENCRYPTION_KEY = -6
+} ComposeQueueResult;
+
 typedef enum {
 	PREF_ACCOUNT,
 	PREF_FOLDER,
@@ -345,7 +356,7 @@ gboolean compose_search_string			(Compose	*compose,
 gboolean compose_search_string_backward	(Compose	*compose,
 						 const gchar	*str,
 						 gboolean	 case_sens);
-gint compose_queue			(Compose *compose, 
+ComposeQueueResult compose_queue	(Compose *compose,
 					 gint *msgnum, 
 					 FolderItem **item, 
 					 gchar **msgpath,
diff --git a/src/folder.c b/src/folder.c
index 3f35f5e..503a049 100644
--- a/src/folder.c
+++ b/src/folder.c
@@ -3474,7 +3474,7 @@ static gint do_copy_msgs(FolderItem *dest, GSList *msglist, gboolean remove_sour
 		for (; cur; cur = cur->next) {
 			Compose *compose = NULL;
 			FolderItem *queue = dest;
-			int val = 0;
+			ComposeQueueResult val = COMPOSE_QUEUE_SUCCESS;
 			
 			msginfo = (MsgInfo *)cur->data;
 			compose = compose_reedit(msginfo, TRUE);
@@ -3484,12 +3484,12 @@ static gint do_copy_msgs(FolderItem *dest, GSList *msglist, gboolean remove_sour
 			}
 			val = compose_queue(compose, NULL, &queue, NULL,
 					FALSE);
-			if (val < 0) {
+			if (val != COMPOSE_QUEUE_SUCCESS) {
 				queue_err = TRUE;
 			} else if (remove_source) {
 				folder_item_remove_msg(msginfo->folder, msginfo->msgnum);
 			}
-			if (val == 0)
+			if (val == COMPOSE_QUEUE_SUCCESS)
 				compose_close(compose);
 		}
 		return queue_err ? -1:0;

commit 3f37fcce10bcfa459de264e16fa5fe1af11266da
Author: wwp <wwp at free.fr>
Date:   Fri Apr 13 12:32:47 2018 +0200

    ClamAV plugin: manage read errors (fixes CID #1220483).

diff --git a/src/plugins/clamd/libclamd/clamd-plugin.c b/src/plugins/clamd/libclamd/clamd-plugin.c
index 963cbca..15122bb 100644
--- a/src/plugins/clamd/libclamd/clamd-plugin.c
+++ b/src/plugins/clamd/libclamd/clamd-plugin.c
@@ -556,6 +556,12 @@ Clamd_Stat clamd_verify_email(const gchar* path, response* result) {
 		if (n_read == 0) {
 			buf[n_read] = '\0';
 			debug_print("response: %s\n", buf);
+		} else {
+			/* in case read() fails */
+			debug_print("read error %d\n", errno);
+			result->msg = NULL;
+			close(sock);
+			return NO_CONNECTION;
 		}
 	}
 	if (strstr(buf, "ERROR")) {

commit 626d2ce199ae3dfe0d9df9c9066c9db0a1954f8c
Author: wwp <wwp at free.fr>
Date:   Fri Apr 13 12:15:24 2018 +0200

    Fix another leak in smime plugin (CID #1220437).

diff --git a/src/plugins/smime/smime.c b/src/plugins/smime/smime.c
index 823ba37..93268a3 100644
--- a/src/plugins/smime/smime.c
+++ b/src/plugins/smime/smime.c
@@ -313,9 +313,11 @@ static gint smime_check_signature(MimeInfo *mimeinfo)
 					}
 				}
 				g_node_prepend(parentinfo->node, decinfo->node);
+				g_free(textstr);
 				return 0;
 			} else {
-				g_free(textstr);
+				if (textstr)
+					g_free(textstr);
 				return -1;
 			}
 		}

commit 1cdd65c44bfa6a6c4c8974739bc47a1a533ede7c
Author: wwp <wwp at free.fr>
Date:   Thu Apr 12 21:37:21 2018 +0200

    Spamassassin plug-in: add support for compression (server must have
    compression enabled, local spamc too).

diff --git a/configure.ac b/configure.ac
index 6990978..7a5cfb7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1841,6 +1841,28 @@ if test x"$enable_spamassassin_plugin" != xno; then
 	PLUGINS="$PLUGINS spamassassin"
 	AC_MSG_RESULT(yes)
 	AC_SPAMASSASSIN
+
+	dnl check for zlib (optional)
+	spamassassin_zlib=0
+	SPAMASSASSIN_CFLAGS=""
+	SPAMASSASSIN_LIBS=""
+	AC_CHECK_HEADER([zlib.h],
+			[AC_DEFINE(HAVE_ZLIB_H,1,[optional zlib support for spamassassin plugin])]
+			[spamassassin_zlib=1],
+			[spamassassin_zlib=0])
+	if test $spamassassin_zlib -eq 1; then
+		AC_CHECK_LIB(z, deflate, [spamassassin_zlib=1], [spamassassin_zlib=0])
+		AC_MSG_CHECKING([whether to build spamassassin plugin with zlib support])
+		if test $spamassassin_zlib -eq 1; then
+			AC_MSG_RESULT(yes)
+			SPAMASSASSIN_CFLAGS="-DHAVE_LIBZ"
+			SPAMASSASSIN_LIBS="-lz"
+		else
+			AC_MSG_RESULT(no)
+		fi
+	fi
+	AC_SUBST(SPAMASSASSIN_CFLAGS)
+	AC_SUBST(SPAMASSASSIN_LIBS)
 else
 	DISABLED_PLUGINS="$DISABLED_PLUGINS spamassassin"
 	AC_MSG_RESULT(no)
diff --git a/src/plugins/spamassassin/Makefile.am b/src/plugins/spamassassin/Makefile.am
index 92712f0..71e6c06 100644
--- a/src/plugins/spamassassin/Makefile.am
+++ b/src/plugins/spamassassin/Makefile.am
@@ -16,7 +16,8 @@ spamassassin_la_SOURCES = \
 	utils.c utils.h
 
 spamassassin_la_LDFLAGS = \
-	-avoid-version -module
+	-avoid-version -module \
+	$(SPAMASSASSIN_LIBS)
 
 if CYGWIN
 cygwin_export_lib = -L$(top_builddir)/src -lclaws-mail
@@ -33,6 +34,7 @@ spamassassin_la_CPPFLAGS = \
 	-I$(top_srcdir)/src/gtk \
 	$(ENCHANT_CFLAGS) \
 	$(GLIB_CFLAGS) \
-	$(GTK_CFLAGS)
+	$(GTK_CFLAGS) \
+	$(SPAMASSASSIN_CFLAGS)
 
 EXTRA_DIST = README NOTICE
diff --git a/src/plugins/spamassassin/spamassassin.c b/src/plugins/spamassassin/spamassassin.c
index f01bd92..c33307b 100644
--- a/src/plugins/spamassassin/spamassassin.c
+++ b/src/plugins/spamassassin/spamassassin.c
@@ -116,6 +116,8 @@ static PrefParam param[] = {
 	 NULL, NULL, NULL},
 	{"whitelist_ab_folder", N_("Any"), &config.whitelist_ab_folder, P_STRING,
 	 NULL, NULL, NULL},
+	{"compress", "FALSE", &config.compress, P_BOOL,
+	 NULL, NULL, NULL},
 
 	{NULL, NULL, NULL, P_OTHER, NULL, NULL, NULL}
 };
@@ -137,6 +139,15 @@ typedef enum {
 	MSG_FILTERING_ERROR = 2
 } MsgStatus;
 
+static void update_flags(void)
+{
+	/* set the SPAMC_USE_ZLIB flag according to config */
+	if (config.compress)
+		flags |= SPAMC_USE_ZLIB;
+	else
+		flags &= ~SPAMC_USE_ZLIB;
+}
+
 static MsgStatus msg_is_spam(FILE *fp)
 {
 	struct transport trans;
@@ -146,6 +157,7 @@ static MsgStatus msg_is_spam(FILE *fp)
 	if (!config.enable)
 		return MSG_IS_HAM;
 
+	update_flags();
 	transport_init(&trans);
 	switch (config.transport) {
 	case SPAMASSASSIN_TRANSPORT_LOCALHOST:
@@ -371,10 +383,11 @@ gchar* spamassassin_create_tmp_spamc_wrapper(gboolean spam)
 
 	if (fname != NULL) {
 		contents = g_strdup_printf(
-						"spamc -d %s -p %u -u %s -t %u -s %u -L %s<\"$*\";exit $?",
+						"spamc -d %s -p %u -u %s -t %u -s %u %s -L %s<\"$*\";exit $?",
 						config.hostname, config.port, 
 						config.username, config.timeout,
-						config.max_size * 1024, spam?"spam":"ham");
+						config.max_size * 1024, config.compress?"-z":"",
+						spam?"spam":"ham");
 		if (str_write_to_file(contents, fname) < 0) {
 			g_free(fname);
 			fname = NULL;
diff --git a/src/plugins/spamassassin/spamassassin.h b/src/plugins/spamassassin/spamassassin.h
index d786da9..c13d4bc 100644
--- a/src/plugins/spamassassin/spamassassin.h
+++ b/src/plugins/spamassassin/spamassassin.h
@@ -50,6 +50,7 @@ struct _SpamAssassinConfig
 	gboolean		 mark_as_read;
 	gboolean		 whitelist_ab;
 	gchar			*whitelist_ab_folder;
+	gboolean		 compress;
 };
 
 SpamAssassinConfig *spamassassin_get_config	      (void);
diff --git a/src/plugins/spamassassin/spamassassin_gtk.c b/src/plugins/spamassassin/spamassassin_gtk.c
index 430066e..0276723 100644
--- a/src/plugins/spamassassin/spamassassin_gtk.c
+++ b/src/plugins/spamassassin/spamassassin_gtk.c
@@ -60,6 +60,7 @@ struct SpamAssassinPage
 	GtkWidget *save_folder_select;
 	GtkWidget *max_size;
 	GtkWidget *timeout;
+	GtkWidget *compress;
 	GtkWidget *mark_as_read;
 	GtkWidget *whitelist_ab;
 	GtkWidget *whitelist_ab_folder_combo;
@@ -216,8 +217,8 @@ static void spamassassin_create_widget_func(PrefsPage * _page,
 
 	GtkWidget *vbox1, *vbox2;
 	GtkWidget *frame_transport, *table_transport, *vbox_transport;
-	GtkWidget *hbox_spamd, *hbox_max_size, *hbox_timeout;
-	GtkWidget *hbox_process_emails, *hbox_save_spam;
+	GtkWidget *hbox_spamd, *hbox_compress, *hbox_max_size;
+	GtkWidget *hbox_timeout, *hbox_process_emails, *hbox_save_spam;
 	GtkWidget *hbox_mark_as_read, *hbox_whitelist;
 	GtkWidget *whitelist_ab_checkbtn;
 	GtkWidget *whitelist_ab_folder_combo;
@@ -238,6 +239,8 @@ static void spamassassin_create_widget_func(PrefsPage * _page,
 	GtkWidget *spamd_port_spinbtn;
 	GtkWidget *spamd_socket_entry;
 
+	GtkWidget *compress_checkbtn;
+
 	GtkWidget *max_size_label;
 	GtkAdjustment *max_size_spinbtn_adj;
 	GtkWidget *max_size_spinbtn;
@@ -353,6 +356,17 @@ static void spamassassin_create_widget_func(PrefsPage * _page,
 	gtk_box_pack_start(GTK_BOX(hbox_spamd), spamd_socket_entry, TRUE, TRUE, 0);
 	CLAWS_SET_TIP(spamd_socket_entry, _("Path of Unix socket"));
 
+	hbox_compress = gtk_hbox_new(FALSE, 8);
+	gtk_widget_show(hbox_compress);
+	gtk_box_pack_start (GTK_BOX (vbox2), hbox_compress, TRUE, TRUE, 0);
+
+	compress_checkbtn = gtk_check_button_new_with_label(
+			_("Use compression"));
+	gtk_widget_show(compress_checkbtn);
+	gtk_box_pack_start(GTK_BOX(hbox_compress), compress_checkbtn, TRUE, TRUE, 0);
+	CLAWS_SET_TIP(compress_checkbtn,
+			_("Enable compression if spamd uses it, otherwise disable it."));
+
 	hbox_max_size = gtk_hbox_new(FALSE, 8);
 	gtk_widget_show(hbox_max_size);
 	gtk_box_pack_start (GTK_BOX (vbox2), hbox_max_size, TRUE, TRUE, 0);
@@ -453,6 +467,7 @@ static void spamassassin_create_widget_func(PrefsPage * _page,
 			_("Click this button to select a book or folder in the address book"));
 
 	SET_TOGGLE_SENSITIVITY(enable_sa_checkbtn, frame_transport);
+	SET_TOGGLE_SENSITIVITY(enable_sa_checkbtn, hbox_compress);
 	SET_TOGGLE_SENSITIVITY(enable_sa_checkbtn, hbox_max_size);
 	SET_TOGGLE_SENSITIVITY(enable_sa_checkbtn, hbox_timeout);
 	SET_TOGGLE_SENSITIVITY(enable_sa_checkbtn, hbox_save_spam);
@@ -498,6 +513,7 @@ static void spamassassin_create_widget_func(PrefsPage * _page,
 					config->whitelist_ab_folder);
 	}
 	gtk_spin_button_set_value(GTK_SPIN_BUTTON(spamd_port_spinbtn), (float) config->port);
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compress_checkbtn), config->compress);
 	gtk_spin_button_set_value(GTK_SPIN_BUTTON(max_size_spinbtn), (float) config->max_size);
 	gtk_spin_button_set_value(GTK_SPIN_BUTTON(timeout_spinbtn), (float) config->timeout);
 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(process_emails_checkbtn), config->process_emails);
@@ -515,6 +531,7 @@ static void spamassassin_create_widget_func(PrefsPage * _page,
 	page->colon = spamd_colon_label;
 	page->port = spamd_port_spinbtn;
 	page->socket = spamd_socket_entry;
+	page->compress = compress_checkbtn;
 	page->max_size = max_size_spinbtn;
 	page->timeout = timeout_spinbtn;
 	page->process_emails = process_emails_checkbtn;
@@ -603,6 +620,9 @@ static void spamassassin_save_func(PrefsPage *_page)
 	/* timeout */
 	config->timeout = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(page->timeout));
 
+	/* compress */
+	config->compress = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->compress));
+
 	/* mark_as_read */
 	config->mark_as_read = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->mark_as_read));
 

commit d6a1f0517342b78cda0124ddd40a267bdeb21e46
Author: wwp <wwp at free.fr>
Date:   Thu Apr 12 17:35:04 2018 +0200

    Disable SSLv3 in libspamc.c (patch from Debian spamassassin package).

diff --git a/src/plugins/spamassassin/libspamc.c b/src/plugins/spamassassin/libspamc.c
index 0be68e8..623c1ab 100644
--- a/src/plugins/spamassassin/libspamc.c
+++ b/src/plugins/spamassassin/libspamc.c
@@ -1188,7 +1188,7 @@ int message_filter(struct transport *tp, const char *username,
     unsigned int throwaway;
     SSL_CTX *ctx = NULL;
     SSL *ssl = NULL;
-    SSL_METHOD *meth;
+    const SSL_METHOD *meth;
     char zlib_on = 0;
     unsigned char *zlib_buf = NULL;
     int zlib_bufsiz = 0;
@@ -1214,11 +1214,7 @@ int message_filter(struct transport *tp, const char *username,
     if (flags & SPAMC_USE_SSL) {
 #ifdef SPAMC_SSL
 	SSLeay_add_ssl_algorithms();
-	if (flags & SPAMC_TLSV1) {
-	    meth = TLSv1_client_method();
-	} else {
-	    meth = SSLv3_client_method(); /* default */
-	}
+	meth = SSLv23_client_method();
 	SSL_load_error_strings();
 	ctx = SSL_CTX_new(meth);
 #else
@@ -1597,7 +1593,7 @@ int message_tell(struct transport *tp, const char *username, int flags,
     int failureval;
     SSL_CTX *ctx = NULL;
     SSL *ssl = NULL;
-    SSL_METHOD *meth;
+    const SSL_METHOD *meth;
 
     assert(tp != NULL);
     assert(m != NULL);
@@ -1605,7 +1601,7 @@ int message_tell(struct transport *tp, const char *username, int flags,
     if (flags & SPAMC_USE_SSL) {
 #ifdef SPAMC_SSL
 	SSLeay_add_ssl_algorithms();
-	meth = SSLv3_client_method();
+	meth = SSLv23_client_method();
 	SSL_load_error_strings();
 	ctx = SSL_CTX_new(meth);
 #else

commit ff34cac72c75d8a27768d816e21b8964d17f374f
Author: wwp <wwp at free.fr>
Date:   Wed Apr 11 22:25:55 2018 +0200

    Fix sensitivity of few preferences widgets of the SA plug-in.

diff --git a/src/plugins/spamassassin/spamassassin_gtk.c b/src/plugins/spamassassin/spamassassin_gtk.c
index 9df89b3..430066e 100644
--- a/src/plugins/spamassassin/spamassassin_gtk.c
+++ b/src/plugins/spamassassin/spamassassin_gtk.c
@@ -459,7 +459,8 @@ static void spamassassin_create_widget_func(PrefsPage * _page,
 	SET_TOGGLE_SENSITIVITY(save_spam_checkbtn, save_spam_folder_entry);
 	SET_TOGGLE_SENSITIVITY(save_spam_checkbtn, save_spam_folder_select);
 	SET_TOGGLE_SENSITIVITY(enable_sa_checkbtn, hbox_process_emails);
-	SET_TOGGLE_SENSITIVITY(save_spam_checkbtn, mark_as_read_checkbtn);
+	SET_TOGGLE_SENSITIVITY(enable_sa_checkbtn, mark_as_read_checkbtn);
+	SET_TOGGLE_SENSITIVITY(enable_sa_checkbtn, hbox_whitelist);
 	SET_TOGGLE_SENSITIVITY(whitelist_ab_checkbtn, whitelist_ab_folder_combo);
 #ifndef USE_ALT_ADDRBOOK
 	SET_TOGGLE_SENSITIVITY(whitelist_ab_checkbtn, whitelist_ab_select_btn);

commit 51a0775804b6263c7b271d2aa57724beb3c14b3d
Author: wwp <wwp at free.fr>
Date:   Wed Apr 11 19:23:21 2018 +0200

    Add NOTICE file to EXTRA_DIST.

diff --git a/src/plugins/spamassassin/Makefile.am b/src/plugins/spamassassin/Makefile.am
index 40525a9..92712f0 100644
--- a/src/plugins/spamassassin/Makefile.am
+++ b/src/plugins/spamassassin/Makefile.am
@@ -35,4 +35,4 @@ spamassassin_la_CPPFLAGS = \
 	$(GLIB_CFLAGS) \
 	$(GTK_CFLAGS)
 
-EXTRA_DIST = README
+EXTRA_DIST = README NOTICE

commit 41e1892a4aece3d3c61b4bb4fcc9c8d7e304606c
Author: wwp <wwp at free.fr>
Date:   Wed Apr 11 19:22:54 2018 +0200

    Add SpamAssassin's NOTICE file.

diff --git a/src/plugins/spamassassin/NOTICE b/src/plugins/spamassassin/NOTICE
new file mode 100644
index 0000000..f8515ed
--- /dev/null
+++ b/src/plugins/spamassassin/NOTICE
@@ -0,0 +1,35 @@
+This product includes software developed by the Apache Software
+Foundation (http://www.apache.org/).
+
+SpamAssassin is a trademark of the Apache Software Foundation.
+
+This distribution includes cryptographic software.  The country in
+which you currently reside may have restrictions on the import,
+possession, use, and/or re-export to another country, of
+encryption software.  BEFORE using any encryption software, please
+check your country's laws, regulations and policies concerning the
+import, possession, or use, and re-export of encryption software, to
+see if this is permitted.  See <http://www.wassenaar.org/> for more
+information.
+
+The U.S. Government Department of Commerce, Bureau of Industry and
+Security (BIS), has classified this software as Export Commodity
+Control Number (ECCN) 5D002.C.1, which includes information security
+software using or performing cryptographic functions with asymmetric
+algorithms.  The form and manner of this Apache Software Foundation
+distribution makes it eligible for export under the License Exception
+ENC Technology Software Unrestricted (TSU) exception (see the BIS
+Export Administration Regulations, Section 740.13) for both object
+code and source code.
+
+The following provides more details on the included cryptographic
+software:
+
+The OpenSSL Project - http://www.openssl.org/source/
+
+spamc and libspamc use OpenSSL to perform SSL encryption.
+
+Steffen Ullrich - http://search.cpan.org/%7esullr/
+
+spamd uses IO::Socket::SSL to perform SSL encryption.
+

commit bf01fb086584be7a2a700b0922cc6dff6dbee536
Author: wwp <wwp at free.fr>
Date:   Wed Apr 11 19:19:50 2018 +0200

    SA plugin: better way to make sure Unix sockets are not used in Windows,
    from a config coming from Unix: do it when loading the configs, not
    when handling the plug-in preferences in GUI.

diff --git a/src/plugins/spamassassin/spamassassin.c b/src/plugins/spamassassin/spamassassin.c
index 5df6de1..f01bd92 100644
--- a/src/plugins/spamassassin/spamassassin.c
+++ b/src/plugins/spamassassin/spamassassin.c
@@ -547,6 +547,11 @@ gint plugin_init(gchar **error)
 		*error = g_strdup(_("Failed to get username"));
 		return -1;
 	}
+#ifdef G_OS_WIN32
+	/* no Unix socket in Windows, and in case our config comes from Unix, switch to TCP */
+	if (config.transport == SPAMASSASSIN_TRANSPORT_UNIX)
+		config.transport = SPAMASSASSIN_TRANSPORT_TCP;
+#endif
 	spamassassin_gtk_init();
 		
 	debug_print("SpamAssassin plugin loaded\n");
diff --git a/src/plugins/spamassassin/spamassassin_gtk.c b/src/plugins/spamassassin/spamassassin_gtk.c
index 66afb20..9df89b3 100644
--- a/src/plugins/spamassassin/spamassassin_gtk.c
+++ b/src/plugins/spamassassin/spamassassin_gtk.c
@@ -524,11 +524,6 @@ static void spamassassin_create_widget_func(PrefsPage * _page,
 	page->whitelist_ab = whitelist_ab_checkbtn;
 	page->whitelist_ab_folder_combo = whitelist_ab_folder_combo;
 
-#ifdef G_OS_WIN32
-	/* no Unix socket in Windows, and in case our config comes from Unix, switch to TCP */
-	if (config->transport == SPAMASSASSIN_TRANSPORT_UNIX)
-		config->transport = SPAMASSASSIN_TRANSPORT_TCP;
-#endif
 	active = 0;
 	for (i = 0; i < (sizeof(transports) / sizeof(struct Transport)); i++) {
 		

commit b60df1ce713f7bea19165353ffc29473856c1c71
Author: Paul <paul at claws-mail.org>
Date:   Wed Apr 11 15:45:55 2018 +0100

    fix bug 3978, '"From" column displays both name and email address for Outbox'

diff --git a/src/summaryview.c b/src/summaryview.c
index 586d7b1..df982dc 100644
--- a/src/summaryview.c
+++ b/src/summaryview.c
@@ -3468,10 +3468,19 @@ static inline void summary_set_header(SummaryView *summaryview, gchar *text[],
 	
 	to_text = msginfo->to ? msginfo->to : 
 		   (msginfo->cc ? msginfo->cc :
-		     (msginfo->newsgroups ? msginfo->newsgroups : _("(No Recipient)")
+		     (msginfo->newsgroups ? msginfo->newsgroups : NULL
 		     )
 		   );
 
+	if (!to_text)
+		to_text = _("(No Recipient)");
+	else {
+		if (prefs_common.summary_from_show == SHOW_NAME)
+			to_text = procheader_get_fromname(to_text);
+		else if (prefs_common.summary_from_show == SHOW_ADDR)
+			extract_address(to_text);
+	}
+
 	text[col_pos[S_COL_TO]] = to_text;
 	if (!should_swap) {
 		text[col_pos[S_COL_FROM]] = from_text;

commit 8fc140e5e240bc0e07e8de3e1d968920176f622f
Author: wwp <wwp at free.fr>
Date:   Wed Apr 11 12:18:26 2018 +0200

    Use G_OS_WIN32, the glib-proper way to check for WIN32.

diff --git a/src/plugins/spamassassin/spamassassin_gtk.c b/src/plugins/spamassassin/spamassassin_gtk.c
index e44bc22..66afb20 100644
--- a/src/plugins/spamassassin/spamassassin_gtk.c
+++ b/src/plugins/spamassassin/spamassassin_gtk.c
@@ -89,7 +89,7 @@ struct Transport transports[] = {
 	/*{ N_("Disabled"),	SPAMASSASSIN_DISABLED,			PAGE_DISABLED, 0 },*/
 	{ N_("Localhost"),	SPAMASSASSIN_TRANSPORT_LOCALHOST,	PAGE_NETWORK, 0 },
 	{ N_("TCP"),		SPAMASSASSIN_TRANSPORT_TCP,		PAGE_NETWORK, NETWORK_HOSTNAME },
-#ifndef _WIN32
+#ifndef G_OS_WIN32
 	{ N_("Unix Socket"),	SPAMASSASSIN_TRANSPORT_UNIX,		PAGE_UNIX,    0 },
 #endif
 };
@@ -524,7 +524,7 @@ static void spamassassin_create_widget_func(PrefsPage * _page,
 	page->whitelist_ab = whitelist_ab_checkbtn;
 	page->whitelist_ab_folder_combo = whitelist_ab_folder_combo;
 
-#ifdef _WIN32
+#ifdef G_OS_WIN32
 	/* no Unix socket in Windows, and in case our config comes from Unix, switch to TCP */
 	if (config->transport == SPAMASSASSIN_TRANSPORT_UNIX)
 		config->transport = SPAMASSASSIN_TRANSPORT_TCP;

commit 0556e39de02c64f5b56f3842206178be294bdb9e
Author: wwp <wwp at free.fr>
Date:   Wed Apr 11 12:16:03 2018 +0200

    Spamassassin plugin in Windows: don't allow Unix socket transport
    mode (and switch to TCP if the config says Unix socket).

diff --git a/src/plugins/spamassassin/spamassassin_gtk.c b/src/plugins/spamassassin/spamassassin_gtk.c
index 00bf6a9..e44bc22 100644
--- a/src/plugins/spamassassin/spamassassin_gtk.c
+++ b/src/plugins/spamassassin/spamassassin_gtk.c
@@ -89,7 +89,9 @@ struct Transport transports[] = {
 	/*{ N_("Disabled"),	SPAMASSASSIN_DISABLED,			PAGE_DISABLED, 0 },*/
 	{ N_("Localhost"),	SPAMASSASSIN_TRANSPORT_LOCALHOST,	PAGE_NETWORK, 0 },
 	{ N_("TCP"),		SPAMASSASSIN_TRANSPORT_TCP,		PAGE_NETWORK, NETWORK_HOSTNAME },
+#ifndef _WIN32
 	{ N_("Unix Socket"),	SPAMASSASSIN_TRANSPORT_UNIX,		PAGE_UNIX,    0 },
+#endif
 };
 
 #ifndef USE_ALT_ADDRBOOK
@@ -522,6 +524,11 @@ static void spamassassin_create_widget_func(PrefsPage * _page,
 	page->whitelist_ab = whitelist_ab_checkbtn;
 	page->whitelist_ab_folder_combo = whitelist_ab_folder_combo;
 
+#ifdef _WIN32
+	/* no Unix socket in Windows, and in case our config comes from Unix, switch to TCP */
+	if (config->transport == SPAMASSASSIN_TRANSPORT_UNIX)
+		config->transport = SPAMASSASSIN_TRANSPORT_TCP;
+#endif
 	active = 0;
 	for (i = 0; i < (sizeof(transports) / sizeof(struct Transport)); i++) {
 		
@@ -529,7 +536,6 @@ static void spamassassin_create_widget_func(PrefsPage * _page,
 		gtk_list_store_set(store, &iter,
 				   0, gettext(transports[i].name),
 				   1, &transports[i], -1);
-
 		if (config->transport == transports[i].transport) {
 			show_transport(page, &transports[i]);
 			active = i;

commit 0586c3cf2219f2bf97a1bcbbd6d466f0eaad6b1c
Author: Paul <paul at claws-mail.org>
Date:   Wed Apr 11 11:06:29 2018 +0100

    fix display of Key ID

diff --git a/src/plugins/pgpcore/select-keys.c b/src/plugins/pgpcore/select-keys.c
index 4bfae56..76b3ebd 100644
--- a/src/plugins/pgpcore/select-keys.c
+++ b/src/plugins/pgpcore/select-keys.c
@@ -264,7 +264,7 @@ set_row (GtkListStore *store, gpgme_key_t key, gpgme_protocol_t proto)
     gtk_list_store_append(store, &iter);
     gtk_list_store_set(store, &iter,
         COL_ALGO, algo_buf,
-        COL_KEYID, key->uids->name,
+        COL_KEYID, key->subkeys->keyid,
         COL_NAME, name,
         COL_ADDRESS, address,
         COL_TRUST, s,

commit 43ca86e8186a530dacd3109db30e891a9f2bbccb
Author: wwp <wwp at free.fr>
Date:   Wed Apr 11 10:54:47 2018 +0200

    Updated Apache SpamAssassin code (libspam.[ch], utils.[ch]) from 3.0.x
    to 3.4.1.

diff --git a/src/plugins/spamassassin/libspamc.c b/src/plugins/spamassassin/libspamc.c
index e78fd00..0be68e8 100644
--- a/src/plugins/spamassassin/libspamc.c
+++ b/src/plugins/spamassassin/libspamc.c
@@ -1,9 +1,10 @@
 /* <@LICENSE>
- * Copyright 2004 Apache Software Foundation
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at:
  * 
  *     http://www.apache.org/licenses/LICENSE-2.0
  * 
@@ -15,11 +16,18 @@
  * </@LICENSE>
  */
 
+/* 
+  Compile with extra warnings -- gcc only, not suitable for use as default:
+
+  gcc -Wextra -Wdeclaration-after-statement -Wall -g -O2 spamc/spamc.c \
+  spamc/getopt.c spamc/libspamc.c spamc/utils.c -o spamc/spamc -ldl -lz
+ */
+
 #include "config.h"
 #include "claws-features.h"
 #include "libspamc.h"
-#include "utils.h"
 
+#include <stdarg.h>
 #include <stdlib.h>
 #include <assert.h>
 #include <stdio.h>
@@ -57,9 +65,12 @@
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
 
-#define MAX_CONNECT_RETRIES 3
-#define CONNECT_RETRY_SLEEP 1
+/* must load *after* errno.h, Bug 6697 */
+#include "utils.h"
 
 /* RedHat 5.2 doesn't define Shutdown 2nd Parameter Constants */
 /* KAM 12-4-01 */
@@ -78,6 +89,12 @@
 #define h_errno errno
 #endif
 
+#ifdef _WIN32
+#define spamc_get_errno()   WSAGetLastError()
+#else
+#define spamc_get_errno()   errno
+#endif
+
 #ifndef HAVE_OPTARG
 extern char *optarg;
 #endif
@@ -95,9 +112,25 @@ extern char *optarg;
 #endif
 
 #undef DO_CONNECT_DEBUG_SYSLOGS
-/* or #define DO_CONNECT_DEBUG_SYSLOGS 1 */
+/*
+#define DO_CONNECT_DEBUG_SYSLOGS 1
+#define CONNECT_DEBUG_LEVEL LOG_DEBUG
+*/
+
+/* bug 4477 comment 14 */
+#ifdef NI_MAXHOST
+#define SPAMC_MAXHOST NI_MAXHOST
+#else
+#define SPAMC_MAXHOST 256
+#endif
+
+#ifdef NI_MAXSERV
+#define SPAMC_MAXSERV NI_MAXSERV
+#else
+#define SPAMC_MAXSERV 256
+#endif
 
-static const int ESC_PASSTHROUGHRAW = EX__MAX + 666;
+/* static const int ESC_PASSTHROUGHRAW = EX__MAX + 666;  No longer seems to be used */
 
 /* set EXPANSION_ALLOWANCE to something more than might be
    added to a message in X-headers and the report template */
@@ -110,7 +143,7 @@ static const int EXPANSION_ALLOWANCE = 16384;
  */
 
 /* Set the protocol version that this spamc speaks */
-static const char *PROTOCOL_VERSION = "SPAMC/1.3";
+static const char *PROTOCOL_VERSION = "SPAMC/1.5";
 
 /* "private" part of struct message.
  * we use this instead of the struct message directly, so that we
@@ -119,9 +152,16 @@ static const char *PROTOCOL_VERSION = "SPAMC/1.3";
 struct libspamc_private_message
 {
     int flags;			/* copied from "flags" arg to message_read() */
+    int alloced_size;           /* allocated space for the "out" buffer */
+
+    void (*spamc_header_callback)(struct message *m, int flags, char *buf, int len);
+    void (*spamd_header_callback)(struct message *m, int flags, const char *buf, int len);
 };
 
+void (*libspamc_log_callback)(int flags, int level, char *msg, va_list args) = NULL;
+
 int libspamc_timeout = 0;
+int libspamc_connect_timeout = 0;	/* Sep 8, 2008 mrgus: separate connect timeout */
 
 /*
  * translate_connect_errno()
@@ -161,17 +201,27 @@ static int _translate_connect_errno(int err)
 /*
  * opensocket()
  *
- *	Given a socket type (PF_INET or PF_UNIX), try to create this socket
- *	and store the FD in the pointed-to place. If it's successful, do any
- *	other setup required to make the socket ready to use, such as setting
- *	TCP_NODELAY mode, and in any case we return EX_OK if all is well.
+ *	Given a socket family (PF_INET or PF_INET6 or PF_UNIX), try to
+ *	create this socket and store the FD in the pointed-to place.
+ *	If it's successful, do any other setup required to make the socket
+ *	ready to use, such as setting TCP_NODELAY mode, and in any case
+ *      we return EX_OK if all is well.
  *
  *	Upon failure we return one of the other EX_??? error codes.
  */
+#ifdef SPAMC_HAS_ADDRINFO
+static int _opensocket(int flags, struct addrinfo *res, int *psock)
+{
+#else
 static int _opensocket(int flags, int type, int *psock)
 {
-    const char *typename;
     int proto = 0;
+#endif
+    const char *typename;
+    int origerr;
+#ifdef _WIN32
+    int socktout;
+#endif
 
     assert(psock != 0);
 
@@ -180,6 +230,22 @@ static int _opensocket(int flags, int type, int *psock)
 	 * type given by the user. The typename is strictly used for debug
 	 * reporting.
 	 */
+#ifdef SPAMC_HAS_ADDRINFO
+    switch(res->ai_family) {
+       case PF_UNIX:
+          typename = "PF_UNIX";
+          break;
+       case PF_INET:
+          typename = "PF_INET";
+          break;
+       case PF_INET6:
+          typename = "PF_INET6";
+          break;
+       default:
+          typename = "Unknown";
+          break;
+    }
+#else
     if (type == PF_UNIX) {
 	typename = "PF_UNIX";
     }
@@ -187,30 +253,33 @@ static int _opensocket(int flags, int type, int *psock)
 	typename = "PF_INET";
 	proto = IPPROTO_TCP;
     }
+#endif
 
 #ifdef DO_CONNECT_DEBUG_SYSLOGS
-    libspamc_log(flags, DEBUG_LEVEL, "dbg: create socket(%s)", typename);
+    libspamc_log(flags, CONNECT_DEBUG_LEVEL, "dbg: create socket(%s)", typename);
 #endif
 
+#ifdef SPAMC_HAS_ADDRINFO
+    if ((*psock = socket(res->ai_family, res->ai_socktype, res->ai_protocol))
+#else
     if ((*psock = socket(type, SOCK_STREAM, proto))
+#endif
 #ifndef _WIN32
 	< 0
 #else
 	== INVALID_SOCKET
 #endif
 	) {
-	int origerr;
 
 		/*--------------------------------------------------------
 		 * At this point we had a failure creating the socket, and
 		 * this is pretty much fatal. Translate the error reason
 		 * into something the user can understand.
 		 */
+	origerr = spamc_get_errno();
 #ifndef _WIN32
-	origerr = errno;	/* take a copy before syslog() */
 	libspamc_log(flags, LOG_ERR, "socket(%s) to spamd failed: %s", typename, strerror(origerr));
 #else
-	origerr = WSAGetLastError();
 	libspamc_log(flags, LOG_ERR, "socket(%s) to spamd failed: %d", typename, origerr);
 #endif
 
@@ -233,6 +302,29 @@ static int _opensocket(int flags, int type, int *psock)
 	}
     }
 
+#ifdef _WIN32
+    /* bug 4344: makes timeout functional on Win32 */
+    socktout = libspamc_timeout * 1000;
+    if (type == PF_INET
+        && setsockopt(*psock, SOL_SOCKET, SO_RCVTIMEO, (char *)&socktout, sizeof(socktout)) != 0)
+    {
+
+        origerr = spamc_get_errno();
+        switch (origerr)
+        {
+        case EBADF:
+        case ENOTSOCK:
+        case ENOPROTOOPT:
+        case EFAULT:
+            libspamc_log(flags, LOG_ERR, "setsockopt(SO_RCVTIMEO) failed: %d", origerr);
+            closesocket(*psock);
+            return EX_SOFTWARE;
+
+        default:
+            break;		/* ignored */
+        }
+    }
+#endif
 
 	/*----------------------------------------------------------------
 	 * Do a bit of setup on the TCP socket if required. Notes above
@@ -242,14 +334,12 @@ static int _opensocket(int flags, int type, int *psock)
     {
 	int one = 1;
 
-	if (type == PF_INET
-	    && setsockopt(*psock, 0, TCP_NODELAY, &one, sizeof one) != 0) {
-	    int origerrno;
-#ifndef _WIN32
-	    origerr = errno;
-#else
-	    origerrno = WSAGetLastError();
+	if ( (   type == PF_INET
+#ifdef PF_INET6
+              || type == PF_INET6
 #endif
+             ) && setsockopt(*psock, 0, TCP_NODELAY, &one, sizeof one) != 0) {
+	    origerr = spamc_get_errno();
 	    switch (origerr) {
 	    case EBADF:
 	    case ENOTSOCK:
@@ -287,16 +377,28 @@ static int _try_to_connect_unix(struct transport *tp, int *sockptr)
 #ifndef _WIN32
     int mysock, status, origerr;
     struct sockaddr_un addrbuf;
+#ifdef SPAMC_HAS_ADDRINFO
+    struct addrinfo hints, *res;
+#else
+    int res = PF_UNIX;
+#endif
     int ret;
 
     assert(tp != 0);
     assert(sockptr != 0);
     assert(tp->socketpath != 0);
 
+#ifdef SPAMC_HAS_ADDRINFO
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = PF_UNIX;
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_protocol = 0;
+    res = &hints;
+#endif
 	/*----------------------------------------------------------------
 	 * If the socket itself can't be created, this is a fatal error.
 	 */
-    if ((ret = _opensocket(tp->flags, PF_UNIX, &mysock)) != EX_OK)
+    if ((ret = _opensocket(tp->flags, res, &mysock)) != EX_OK)
 	return ret;
 
     /* set up the UNIX domain socket */
@@ -306,17 +408,17 @@ static int _try_to_connect_unix(struct transport *tp, int *sockptr)
     addrbuf.sun_path[sizeof addrbuf.sun_path - 1] = '\0';
 
 #ifdef DO_CONNECT_DEBUG_SYSLOGS
-    libspamc_log(tp->flags, DEBUG_LEVEL, "dbg: connect(AF_UNIX) to spamd at %s",
+    libspamc_log(tp->flags, CONNECT_DEBUG_LEVEL, "dbg: connect(AF_UNIX) to spamd at %s",
 	   addrbuf.sun_path);
 #endif
 
-    status = connect(mysock, (struct sockaddr *) &addrbuf, sizeof(addrbuf));
+    status = timeout_connect(mysock, (struct sockaddr *) &addrbuf, sizeof(addrbuf));
 
     origerr = errno;
 
     if (status >= 0) {
 #ifdef DO_CONNECT_DEBUG_SYSLOGS
-	libspamc_log(tp->flags, DEBUG_LEVEL, "dbg: connect(AF_UNIX) ok");
+	libspamc_log(tp->flags, CONNECT_DEBUG_LEVEL, "dbg: connect(AF_UNIX) ok");
 #endif
 
 	*sockptr = mysock;
@@ -324,7 +426,7 @@ static int _try_to_connect_unix(struct transport *tp, int *sockptr)
 	return EX_OK;
     }
 
-    libspamc_log(tp->flags, LOG_ERR, "connect(AF_UNIX) to spamd %s failed: %s",
+    libspamc_log(tp->flags, LOG_ERR, "connect(AF_UNIX) to spamd using --socket='%s' failed: %s",
 	   addrbuf.sun_path, strerror(origerr));
     closesocket(mysock);
 
@@ -349,78 +451,158 @@ static int _try_to_connect_tcp(const struct transport *tp, int *sockptr)
     int numloops;
     int origerr = 0;
     int ret;
+#ifdef SPAMC_HAS_ADDRINFO
+    struct addrinfo *res = NULL;
+    char port[SPAMC_MAXSERV-1]; /* port, for logging */
+#else
+    int res = PF_INET;
+#endif
+    char host[SPAMC_MAXHOST-1]; /* hostname, for logging */
+    int connect_retries, retry_sleep;
 
     assert(tp != 0);
     assert(sockptr != 0);
     assert(tp->nhosts > 0);
 
-#ifdef DO_CONNECT_DEBUG_SYSLOGS
-    for (numloops = 0; numloops < tp->nhosts; numloops++) {
-	libspamc_log(tp->flags, LOG_ERR, "dbg: %d/%d: %s",
-		numloops + 1, tp->nhosts, inet_ntoa(tp->hosts[numloops]));
+    /* default values */
+    retry_sleep = tp->retry_sleep;
+    connect_retries = tp->connect_retries;
+    if (connect_retries == 0) {
+      connect_retries = 3;
+    }
+    if (retry_sleep < 0) {
+      retry_sleep = 1;
     }
-#endif
 
-    for (numloops = 0; numloops < MAX_CONNECT_RETRIES; numloops++) {
-	struct sockaddr_in addrbuf;
-	const int hostix = numloops % tp->nhosts;
-	int status, mysock;
-	const char *ipaddr;
+    for (numloops = 0; numloops < connect_retries; numloops++) {
+        const int hostix = numloops % tp->nhosts;
+        int status, mysock;
+        int innocent = 0;
+
+                /*--------------------------------------------------------
+                * We always start by creating the socket, as we get only
+                * one attempt to connect() on each one. If this fails,
+                * we're done.
+                */
+
+#ifdef SPAMC_HAS_ADDRINFO
+        res = tp->hosts[hostix];
+        while(res) {
+            char *family = NULL;
+            switch(res->ai_family) {
+            case AF_INET:
+                family = "AF_INET";
+                break;
+            case AF_INET6:
+                family = "AF_INET6";
+                break;
+            default:
+                family = "Unknown";
+                break;
+            }
+
+            if ((ret = _opensocket(tp->flags, res, &mysock)) != EX_OK) {
+                res = res->ai_next;
+                continue;
+            }
+
+            getnameinfo(res->ai_addr, res->ai_addrlen,
+                  host, sizeof(host),
+                  port, sizeof(port),
+                  NI_NUMERICHOST|NI_NUMERICSERV);
 
-		/*--------------------------------------------------------
-		 * We always start by creating the socket, as we get only
-		 * one attempt to connect() on each one. If this fails,
-		 * we're done.
-		 */
-	if ((ret = _opensocket(tp->flags, PF_INET, &mysock)) != EX_OK)
-	    return ret;
-
-	memset(&addrbuf, 0, sizeof(addrbuf));
+#ifdef DO_CONNECT_DEBUG_SYSLOGS
+            libspamc_log(tp->flags, CONNECT_DEBUG_LEVEL,
+              "dbg: connect(%s) to spamd (host %s, port %s) (try #%d of %d)",
+                      family, host, port, numloops + 1, connect_retries);
+#endif
 
-	addrbuf.sin_family = AF_INET;
-	addrbuf.sin_port = htons(tp->port);
-	addrbuf.sin_addr = tp->hosts[hostix];
+            /* this is special-cased so that we have an address we can
+             * safely use as an "always fail" test case */
+            if (!strcmp(host, "255.255.255.255")) {
+              libspamc_log(tp->flags, LOG_ERR,
+                          "connect to spamd on %s failed, broadcast addr",
+                          host);
+              status = -1;
+            }
+            else {
+              status = timeout_connect(mysock, res->ai_addr, res->ai_addrlen);
+              if (status != 0) origerr = spamc_get_errno();
+            }
 
-	ipaddr = inet_ntoa(addrbuf.sin_addr);
+#else
+	    struct sockaddr_in addrbuf;
+	    const char *ipaddr;
+	    const char* family="AF_INET";
+	    if ((ret = _opensocket(tp->flags, PF_INET, &mysock)) != EX_OK)
+	      return ret;
+	    
+	    memset(&addrbuf, 0, sizeof(addrbuf));
+	    
+	    addrbuf.sin_family = AF_INET;
+	    addrbuf.sin_port = htons(tp->port);
+	    addrbuf.sin_addr = tp->hosts[hostix];
+	    
+	    ipaddr = inet_ntoa(addrbuf.sin_addr);
+
+            /* make a copy in host, for logging (bug 5577) */
+            strncpy (host, ipaddr, sizeof(host) - 1);
 
 #ifdef DO_CONNECT_DEBUG_SYSLOGS
-	libspamc_log(tp->flags, DEBUG_LEVEL,
-	       "dbg: connect(AF_INET) to spamd at %s (try #%d of %d)",
-		ipaddr, numloops + 1, MAX_CONNECT_RETRIES);
+	    libspamc_log(tp->flags, LOG_DEBUG,
+			 "dbg: connect(AF_INET) to spamd at %s (try #%d of %d)",
+			 ipaddr, numloops + 1, connect_retries);
 #endif
 
-	status =
-	    connect(mysock, (struct sockaddr *) &addrbuf, sizeof(addrbuf));
+            /* this is special-cased so that we have an address we can
+             * safely use as an "always fail" test case */
+            if (!strcmp(ipaddr, "255.255.255.255")) {
+              libspamc_log(tp->flags, LOG_ERR,
+                          "connect to spamd on %s failed, broadcast addr",
+                          ipaddr);
+              status = -1;
+            }
+            else {
+              status = timeout_connect(mysock, (struct sockaddr *) &addrbuf,
+                        sizeof(addrbuf));
+              if (status != 0) origerr = spamc_get_errno();
+            }
 
-	if (status != 0) {
-#ifndef _WIN32
-	    origerr = errno;
-	    libspamc_log(tp->flags, LOG_ERR,
-		   "connect(AF_INET) to spamd at %s failed, retrying (#%d of %d): %s",
-		   ipaddr, numloops + 1, MAX_CONNECT_RETRIES, strerror(origerr));
+#endif
+
+            if (status != 0) {
+                  closesocket(mysock);
+
+                  innocent = origerr == ECONNREFUSED && numloops+1 < tp->nhosts;
+                  libspamc_log(tp->flags, innocent ? LOG_DEBUG : LOG_ERR,
+                      "connect to spamd on %s failed, retrying (#%d of %d): %s",
+                      host, numloops+1, connect_retries,
+#ifdef _WIN32
+                      origerr
 #else
-	    origerr = WSAGetLastError();
-	    libspamc_log(tp->flags, LOG_ERR,
-		   "connect(AF_INET) to spamd at %s failed, retrying (#%d of %d): %d",
-		   ipaddr, numloops + 1, MAX_CONNECT_RETRIES, origerr);
+                      strerror(origerr)
 #endif
-	    closesocket(mysock);
+                  );
 
-	    sleep(CONNECT_RETRY_SLEEP);
-	}
-	else {
+            } else {
 #ifdef DO_CONNECT_DEBUG_SYSLOGS
-	    libspamc_log(tp->flags, DEBUG_LEVEL,
-		   "dbg: connect(AF_INET) to spamd at %s done", ipaddr);
+                  libspamc_log(tp->flags, CONNECT_DEBUG_LEVEL,
+                          "dbg: connect(%s) to spamd done",family);
 #endif
-	    *sockptr = mysock;
+                  *sockptr = mysock;
 
-	    return EX_OK;
-	}
-    }
+                  return EX_OK;
+            }
+#ifdef SPAMC_HAS_ADDRINFO
+            res = res->ai_next;
+        }
+#endif
+        if (numloops+1 < connect_retries && !innocent) sleep(retry_sleep);
+    } /* for(numloops...) */
 
-    libspamc_log(tp->flags, LOG_ERR, "connection attempt to spamd aborted after %d retries",
-	    MAX_CONNECT_RETRIES);
+    libspamc_log(tp->flags, LOG_ERR,
+              "connection attempt to spamd aborted after %d retries",
+              connect_retries);
 
     return _translate_connect_errno(origerr);
 }
@@ -444,11 +626,30 @@ static void _clear_message(struct message *m)
     m->is_spam = EX_TOOBIG;
     m->score = 0.0;
     m->threshold = 0.0;
+    m->outbuf = NULL;
     m->out = NULL;
     m->out_len = 0;
     m->content_length = -1;
 }
 
+static void _free_zlib_buffer(unsigned char **zlib_buf, int *zlib_bufsiz)
+{
+	if(*zlib_buf) {
+	free(*zlib_buf);
+	*zlib_buf=NULL;
+	}
+	*zlib_bufsiz=0;
+}
+
+static void _use_msg_for_out(struct message *m)
+{
+    if (m->outbuf)
+	free(m->outbuf);
+    m->outbuf = NULL;
+    m->out = m->msg;
+    m->out_len = m->msg_len;
+}
+
 static int _message_read_raw(int fd, struct message *m)
 {
     _clear_message(m);
@@ -462,8 +663,13 @@ static int _message_read_raw(int fd, struct message *m)
 	return EX_IOERR;
     }
     m->type = MESSAGE_ERROR;
-    if (m->raw_len > m->max_len)
+    if (m->raw_len > (int) m->max_len)
+    {
+        libspamc_log(m->priv->flags, LOG_NOTICE,
+                "skipped message, greater than max message size (%d bytes)",
+                m->max_len);
 	return EX_TOOBIG;
+    }
     m->type = MESSAGE_RAW;
     m->msg = m->raw;
     m->msg_len = m->raw_len;
@@ -474,8 +680,9 @@ static int _message_read_raw(int fd, struct message *m)
 
 static int _message_read_bsmtp(int fd, struct message *m)
 {
-    unsigned int i, j;
+    unsigned int i, j, p_len;
     char prev;
+    char* p;
 
     _clear_message(m);
     if ((m->raw = malloc(m->max_len + 1)) == NULL)
@@ -490,44 +697,52 @@ static int _message_read_bsmtp(int fd, struct message *m)
 	return EX_IOERR;
     }
     m->type = MESSAGE_ERROR;
-    if (m->raw_len > m->max_len)
+    if (m->raw_len > (int) m->max_len)
 	return EX_TOOBIG;
-    m->pre = m->raw;
-    for (i = 0; i < m->raw_len - 6; i++) {
-	if ((m->raw[i] == '\n') &&
-	    (m->raw[i + 1] == 'D' || m->raw[i + 1] == 'd') &&
-	    (m->raw[i + 2] == 'A' || m->raw[i + 2] == 'a') &&
-	    (m->raw[i + 3] == 'T' || m->raw[i + 3] == 't') &&
-	    (m->raw[i + 4] == 'A' || m->raw[i + 4] == 'a') &&
-	    ((m->raw[i + 5] == '\r' && m->raw[i + 6] == '\n')
-	     || m->raw[i + 5] == '\n')) {
-	    /* Found it! */
-	    i += 6;
-	    if (m->raw[i - 1] == '\r')
-		i++;
-	    m->pre_len = i;
-	    m->msg = m->raw + i;
-	    m->msg_len = m->raw_len - i;
-	    break;
+    p = m->pre = m->raw;
+    /* Search for \nDATA\n which marks start of actual message */
+    while ((p_len = (m->raw_len - (p - m->raw))) > 8) { /* leave room for at least \nDATA\n.\n */
+      char* q = memchr(p, '\n', p_len - 8);  /* find next \n then see if start of \nDATA\n */
+      if (q == NULL) break;
+      q++;
+      if (((q[0]|0x20) == 'd') && /* case-insensitive ASCII comparison */
+	  ((q[1]|0x20) == 'a') &&
+	  ((q[2]|0x20) == 't') &&
+	  ((q[3]|0x20) == 'a')) {
+	q+=4;
+	if (q[0] == '\r') ++q;
+	if (*(q++) == '\n') {  /* leave q at start of message if we found it */
+	  m->msg = q;
+	  m->pre_len = q - m->raw;
+	  m->msg_len = m->raw_len - m->pre_len;
+	  break;
 	}
+      }
+      p = q; /* the above code ensures no other '\n' comes before q */
     }
     if (m->msg == NULL)
 	return EX_DATAERR;
 
+    /* ensure this is >= 0 */
+    if (m->msg_len < 0) {
+	return EX_SOFTWARE;
+    }
+
     /* Find the end-of-DATA line */
     prev = '\n';
-    for (i = j = 0; i < m->msg_len; i++) {
+    for (i = j = 0; i < (unsigned int) m->msg_len; i++) {
 	if (prev == '\n' && m->msg[i] == '.') {
 	    /* Dot at the beginning of a line */
-	    if ((m->msg[i + 1] == '\r' && m->msg[i + 2] == '\n')
-		|| m->msg[i + 1] == '\n') {
+            if (((int) (i+1) == m->msg_len)
+                || ((int) (i+1) < m->msg_len && m->msg[i + 1] == '\n')
+                || ((int) (i+2) < m->msg_len && m->msg[i + 1] == '\r' && m->msg[i + 2] == '\n')) {
 		/* Lone dot! That's all, folks */
 		m->post = m->msg + i;
 		m->post_len = m->msg_len - i;
 		m->msg_len = j;
 		break;
 	    }
-	    else if (m->msg[i + 1] == '.') {
+	    else if ((int) (i+1) < m->msg_len && m->msg[i + 1] == '.') {
 		/* Escaping dot, eliminate. */
 		prev = '.';
 		continue;
@@ -537,6 +752,9 @@ static int _message_read_bsmtp(int fd, struct message *m)
 	m->msg[j++] = m->msg[i];
     }
 
+    /* if bad format with no end "\n.\n", error out */
+    if (m->post == NULL)
+	return EX_DATAERR;
     m->type = MESSAGE_BSMTP;
     m->out = m->msg;
     m->out_len = m->msg_len;
@@ -545,6 +763,8 @@ static int _message_read_bsmtp(int fd, struct message *m)
 
 int message_read(int fd, int flags, struct message *m)
 {
+    assert(m != NULL);
+
     libspamc_timeout = 0;
 
     /* create the "private" part of the struct message */
@@ -554,6 +774,14 @@ int message_read(int fd, int flags, struct message *m)
 	return EX_OSERR;
     }
     m->priv->flags = flags;
+    m->priv->alloced_size = 0;
+    m->priv->spamc_header_callback = 0;
+    m->priv->spamd_header_callback = 0;
+
+    if (flags & SPAMC_PING) {
+      _clear_message(m);
+      return EX_OK;
+    }
 
     switch (flags & SPAMC_MODE_MASK) {
     case SPAMC_RAW_MODE:
@@ -576,7 +804,9 @@ long message_write(int fd, struct message *m)
     off_t jlimit;
     char buffer[1024];
 
-    if (m->priv->flags & SPAMC_CHECK_ONLY) {
+    assert(m != NULL);
+
+    if (m->priv->flags & (SPAMC_CHECK_ONLY|SPAMC_PING)) {
 	if (m->is_spam == EX_ISSPAM || m->is_spam == EX_NOTSPAM) {
 	    return full_write(fd, 1, m->out, m->out_len);
 
@@ -628,17 +858,23 @@ long message_write(int fd, struct message *m)
     }
 }
 
-void message_dump(int in_fd, int out_fd, struct message *m)
+void message_dump(int in_fd, int out_fd, struct message *m, int flags)
 {
     char buf[8196];
     int bytes;
 
-    if (m != NULL && m->type != MESSAGE_NONE) {
+    if (m == NULL) {
+	libspamc_log(flags, LOG_ERR, "oops! message_dump called with NULL message");
+	return;
+    }
+
+    if (m->type != MESSAGE_NONE) {
 	message_write(out_fd, m);
     }
+
     while ((bytes = full_read(in_fd, 1, buf, 8192, 8192)) > 0) {
 	if (bytes != full_write(out_fd, 1, buf, bytes)) {
-	    libspamc_log(m->priv->flags, LOG_ERR, "oops! message_dump of %d returned different",
+	    libspamc_log(flags, LOG_ERR, "oops! message_dump of %d returned different",
 		   bytes);
 	}
     }
@@ -719,7 +955,8 @@ static float _locale_safe_string_to_float(char *buf, int siz)
 
     cp = (dot + 1);
     postdot = (float) (strtol(cp, NULL, 10));
-    if (postdot == 0.0) {
+    /* note: don't compare floats == 0.0, it's unsafe.  use a range */
+    if (postdot >= -0.00001 && postdot <= 0.00001) {
 	return ret;
     }
 
@@ -751,10 +988,13 @@ static float _locale_safe_string_to_float(char *buf, int siz)
 }
 
 static int
-_handle_spamd_header(struct message *m, int flags, char *buf, int len)
+_handle_spamd_header(struct message *m, int flags, char *buf, int len,
+		     unsigned int *didtellflags)
 {
     char is_spam[6];
     char s_str[21], t_str[21];
+    char didset_ret[15];
+    char didremove_ret[15];
 
     UNUSED_VARIABLE(len);
 
@@ -801,13 +1041,140 @@ _handle_spamd_header(struct message *m, int flags, char *buf, int len)
 	}
 	return EX_OK;
     }
+    else if (sscanf(buf, "DidSet: %14s", didset_ret) == 1) {
+      if (strstr(didset_ret, "local")) {
+	  *didtellflags |= SPAMC_SET_LOCAL;
+	}
+	if (strstr(didset_ret, "remote")) {
+	  *didtellflags |= SPAMC_SET_REMOTE;
+	}
+    }
+    else if (sscanf(buf, "DidRemove: %14s", didremove_ret) == 1) {
+        if (strstr(didremove_ret, "local")) {
+	  *didtellflags |= SPAMC_REMOVE_LOCAL;
+	}
+	if (strstr(didremove_ret, "remote")) {
+	  *didtellflags |= SPAMC_REMOVE_REMOTE;
+	}
+    }
+    else if (m->priv->spamd_header_callback != NULL)
+      m->priv->spamd_header_callback(m, flags, buf, len);
+
+    return EX_OK;
+}
+
+static int
+_zlib_compress (char *m_msg, int m_msg_len,
+        unsigned char **zlib_buf, int *zlib_bufsiz, int flags)
+{
+    int rc;
+    int len, totallen;
+
+#ifndef HAVE_LIBZ
+
+    UNUSED_VARIABLE(m_msg);
+    UNUSED_VARIABLE(m_msg_len);
+    UNUSED_VARIABLE(zlib_buf);
+    UNUSED_VARIABLE(zlib_bufsiz);
+    UNUSED_VARIABLE(rc);
+    UNUSED_VARIABLE(len);
+    UNUSED_VARIABLE(totallen);
+    libspamc_log(flags, LOG_ERR, "spamc not built with zlib support");
+    return EX_SOFTWARE;
+
+#else
+    z_stream strm;
+
+    UNUSED_VARIABLE(flags);
+
+    /* worst-case, according to http://www.zlib.org/zlib_tech.html ;
+      * same as input, plus 5 bytes per 16k, plus 6 bytes.  this should
+      * be plenty */
+    *zlib_bufsiz = (int) (m_msg_len * 1.0005) + 1024;
+    *zlib_buf = (unsigned char *) malloc (*zlib_bufsiz);
+    if (*zlib_buf == NULL) {
+        return EX_OSERR;
+    }
+
+    strm.zalloc = Z_NULL;
+    strm.zfree = Z_NULL;
+    strm.opaque = Z_NULL;
+    rc = deflateInit(&strm, 3);
+    if (rc != Z_OK) {
+        return EX_OSERR;
+    }
 
-    libspamc_log(flags, LOG_ERR, "spamd responded with bad header '%s'", buf);
-    return EX_PROTOCOL;
+    strm.avail_in = m_msg_len;
+    strm.next_in = (unsigned char *) m_msg;
+    strm.avail_out = *zlib_bufsiz;
+    strm.next_out = (unsigned char *) *zlib_buf;
+
+    totallen = 0;
+    do {
+        rc = deflate(&strm, Z_FINISH);
+        assert(rc != Z_STREAM_ERROR);
+        len = (size_t) (*zlib_bufsiz - strm.avail_out);
+        strm.next_out += len;
+        totallen += len;
+    } while (strm.avail_out == 0);
+
+    *zlib_bufsiz = totallen;
+    return EX_OK;
+
+#endif
+}
+
+int
+_append_original_body (struct message *m, int flags)
+{
+    char *cp, *cpend, *bodystart;
+    int bodylen, outspaceleft, towrite;
+
+    /* at this stage, m->out now contains the rewritten headers.
+     * find and append the raw message's body, up to m->priv->alloced_size
+     * bytes.
+     */
+
+#define CRNLCRNL        "\r\n\r\n"
+#define CRNLCRNL_LEN    4
+#define NLNL            "\n\n"
+#define NLNL_LEN        2
+
+    cpend = m->raw + m->raw_len;
+    bodystart = NULL;
+
+    for (cp = m->raw; cp < cpend; cp++) {
+        if (*cp == '\r' && cpend - cp >= CRNLCRNL_LEN && 
+                            !strncmp(cp, CRNLCRNL, CRNLCRNL_LEN))
+        {
+            bodystart = cp + CRNLCRNL_LEN;
+            break;
+        }
+        else if (*cp == '\n' && cpend - cp >= NLNL_LEN && 
+                           !strncmp(cp, NLNL, NLNL_LEN))
+        {
+            bodystart = cp + NLNL_LEN;
+            break;
+        }
+    }
+
+    if (bodystart == NULL) {
+        libspamc_log(flags, LOG_ERR, "failed to find end-of-headers");
+        return EX_SOFTWARE;
+    }
+
+    bodylen = cpend - bodystart;
+    outspaceleft = (m->priv->alloced_size-1) - m->out_len;
+    towrite = (bodylen < outspaceleft ? bodylen : outspaceleft);
+
+    /* copy in the body; careful not to overflow */
+    strncpy (m->out + m->out_len, bodystart, towrite);
+    m->out_len += towrite;
+    return EX_OK;
 }
 
 int message_filter(struct transport *tp, const char *username,
-		   int flags, struct message *m)
+                   int flags, struct message *m)
 {
     char buf[8192];
     size_t bufsiz = (sizeof(buf) / sizeof(*buf)) - 4; /* bit of breathing room */
@@ -817,15 +1184,41 @@ int message_filter(struct transport *tp, const char *username,
     char versbuf[20];
     float version;
     int response;
-    int failureval;
+    int failureval = EX_SOFTWARE;
+    unsigned int throwaway;
     SSL_CTX *ctx = NULL;
     SSL *ssl = NULL;
     SSL_METHOD *meth;
+    char zlib_on = 0;
+    unsigned char *zlib_buf = NULL;
+    int zlib_bufsiz = 0;
+    unsigned char *towrite_buf;
+    int towrite_len;
+    int filter_retry_count;
+    int filter_retry_sleep;
+    int filter_retries;
+    #ifdef SPAMC_HAS_ADDRINFO
+        struct addrinfo *tmphost;
+    #else
+        struct in_addr tmphost;
+    #endif
+    int nhost_counter;
+
+    assert(tp != NULL);
+    assert(m != NULL);
+
+    if ((flags & SPAMC_USE_ZLIB) != 0) {
+      zlib_on = 1;
+    }
 
     if (flags & SPAMC_USE_SSL) {
 #ifdef SPAMC_SSL
 	SSLeay_add_ssl_algorithms();
-	meth = SSLv2_client_method();
+	if (flags & SPAMC_TLSV1) {
+	    meth = TLSv1_client_method();
+	} else {
+	    meth = SSLv3_client_method(); /* default */
+	}
 	SSL_load_error_strings();
 	ctx = SSL_CTX_new(meth);
 #else
@@ -838,97 +1231,184 @@ int message_filter(struct transport *tp, const char *username,
     }
 
     m->is_spam = EX_TOOBIG;
-    if ((m->out = malloc(m->max_len + EXPANSION_ALLOWANCE + 1)) == NULL) {
+
+    if (m->outbuf != NULL)
+        free(m->outbuf);
+    m->priv->alloced_size = m->max_len + EXPANSION_ALLOWANCE + 1;
+    if ((m->outbuf = malloc(m->priv->alloced_size)) == NULL) {
 	failureval = EX_OSERR;
 	goto failure;
     }
+    m->out = m->outbuf;
     m->out_len = 0;
 
+    /* If the spamd filter takes too long and we timeout, then
+     * retry again.  This gets us around a hung child thread 
+     * in spamd or a problem on a spamd host in a multi-host
+     * setup.  If there is more than one destination host
+     * we move to the next host on each attempt.
+     */
 
-    /* Build spamd protocol header */
-    if (flags & SPAMC_CHECK_ONLY)
-	strcpy(buf, "CHECK ");
-    else if (flags & SPAMC_REPORT_IFSPAM)
-	strcpy(buf, "REPORT_IFSPAM ");
-    else if (flags & SPAMC_REPORT)
-	strcpy(buf, "REPORT ");
-    else if (flags & SPAMC_SYMBOLS)
-	strcpy(buf, "SYMBOLS ");
-    else
-	strcpy(buf, "PROCESS ");
-
-    len = strlen(buf);
-    if (len + strlen(PROTOCOL_VERSION) + 2 >= bufsiz) {
-	free(m->out);
-	m->out = m->msg;
-	m->out_len = m->msg_len;
-	return EX_OSERR;
-    }
-
-    strcat(buf, PROTOCOL_VERSION);
-    strcat(buf, "\r\n");
-    len = strlen(buf);
-
-    if (username != NULL) {
-	if (strlen(username) + 8 >= (bufsiz - len)) {
-	    free(m->out);
-	    m->out = m->msg;
-	    m->out_len = m->msg_len;
-	    return EX_OSERR;
-	}
-	strcpy(buf + len, "User: ");
-	strcat(buf + len, username);
-	strcat(buf + len, "\r\n");
-	len += strlen(buf + len);
+    /* default values */
+    filter_retry_sleep = tp->filter_retry_sleep;
+    filter_retries = tp->filter_retries;
+    if (filter_retries == 0) {
+        filter_retries = 1;
     }
-    if ((m->msg_len > 9999999) || ((len + 27) >= (bufsiz - len))) {
-	free(m->out);
-	m->out = m->msg;
-	m->out_len = m->msg_len;
-	return EX_OSERR;
+    if (filter_retry_sleep < 0) {
+        filter_retry_sleep = 1;
     }
-    len += sprintf(buf + len, "Content-length: %d\r\n\r\n", m->msg_len);
 
-    libspamc_timeout = m->timeout;
-
-    if (tp->socketpath)
-	rc = _try_to_connect_unix(tp, &sock);
-    else
-	rc = _try_to_connect_tcp(tp, &sock);
-
-    if (rc != EX_OK) {
-	free(m->out);
-	m->out = m->msg;
-	m->out_len = m->msg_len;
-	return rc;      /* use the error code try_to_connect_*() gave us. */
-    }
-
-    if (flags & SPAMC_USE_SSL) {
+    /* filterloop - Ensure that we run through this at least
+     * once, and again if there are errors 
+     */
+    filter_retry_count = 0;
+    while ((filter_retry_count==0) || 
+                ((filter_retry_count<tp->filter_retries) && (failureval == EX_IOERR)))
+    {
+        if (filter_retry_count != 0){
+            /* Ensure that the old socket gets closed */
+            if (sock != -1) {
+                closesocket(sock);
+                sock=-1;
+            }
+
+            /* Move to the next host in the list, if nhosts>1 */
+            if (tp->nhosts > 1) {
+                tmphost = tp->hosts[0];
+
+                /* TODO: free using freeaddrinfo() */
+                for (nhost_counter = 1; nhost_counter < tp->nhosts; nhost_counter++) {
+                    tp->hosts[nhost_counter - 1] = tp->hosts[nhost_counter];
+                }
+        
+                tp->hosts[nhost_counter - 1] = tmphost;
+            }
+
+            /* Now sleep the requested amount */
+            sleep(filter_retry_sleep);
+        }
+
+        filter_retry_count++;
+    
+        /* Build spamd protocol header */
+        if (flags & SPAMC_CHECK_ONLY)
+          strcpy(buf, "CHECK ");
+        else if (flags & SPAMC_REPORT_IFSPAM)
+          strcpy(buf, "REPORT_IFSPAM ");
+        else if (flags & SPAMC_REPORT)
+          strcpy(buf, "REPORT ");
+        else if (flags & SPAMC_SYMBOLS)
+          strcpy(buf, "SYMBOLS ");
+        else if (flags & SPAMC_PING)
+          strcpy(buf, "PING ");
+        else if (flags & SPAMC_HEADERS)
+          strcpy(buf, "HEADERS ");
+        else
+          strcpy(buf, "PROCESS ");
+    
+        len = strlen(buf);
+        if (len + strlen(PROTOCOL_VERSION) + 2 >= bufsiz) {
+            _use_msg_for_out(m);
+            return EX_OSERR;
+        }
+    
+        strcat(buf, PROTOCOL_VERSION);
+        strcat(buf, "\r\n");
+        len = strlen(buf);
+    
+        towrite_buf = (unsigned char *) m->msg;
+        towrite_len = (int) m->msg_len;
+        if (zlib_on) {
+            if (_zlib_compress(m->msg, m->msg_len, &zlib_buf, &zlib_bufsiz, flags) != EX_OK)
+            {
+                _free_zlib_buffer(&zlib_buf, &zlib_bufsiz);
+                return EX_OSERR;
+            }
+            towrite_buf = zlib_buf;
+            towrite_len = zlib_bufsiz;
+        }
+    
+        if (!(flags & SPAMC_PING)) {
+          if (username != NULL) {
+              if (strlen(username) + 8 >= (bufsiz - len)) {
+                  _use_msg_for_out(m);
+                  if (zlib_on) {
+                      _free_zlib_buffer(&zlib_buf, &zlib_bufsiz);
+                  }
+                  return EX_OSERR;
+              }
+              strcpy(buf + len, "User: ");
+              strcat(buf + len, username);
+              strcat(buf + len, "\r\n");
+              len += strlen(buf + len);
+          }
+          if (zlib_on) {
+              len += snprintf(buf + len, 8192-len, "Compress: zlib\r\n");
+          }
+          if ((m->msg_len > SPAMC_MAX_MESSAGE_LEN) || ((len + 27) >= (bufsiz - len))) {
+              _use_msg_for_out(m);
+              if (zlib_on) {
+                  _free_zlib_buffer(&zlib_buf, &zlib_bufsiz);
+              }
+              return EX_DATAERR;
+          }
+          len += snprintf(buf + len, 8192-len, "Content-length: %d\r\n", (int) towrite_len);
+        }
+        /* bug 6187, PING needs empty line too, bumps protocol version to 1.5 */
+        len += snprintf(buf + len, 8192-len, "\r\n");
+    
+        libspamc_timeout = m->timeout;
+        libspamc_connect_timeout = m->connect_timeout;	/* Sep 8, 2008 mrgus: separate connect timeout */
+
+        if (tp->socketpath)
+          rc = _try_to_connect_unix(tp, &sock);
+        else
+          rc = _try_to_connect_tcp(tp, &sock);
+    
+        if (rc != EX_OK) {
+          _use_msg_for_out(m);
+          if (zlib_on) {
+              _free_zlib_buffer(&zlib_buf, &zlib_bufsiz);
+          }
+          return rc;      /* use the error code try_to_connect_*() gave us. */
+        }
+    
+        if (flags & SPAMC_USE_SSL) {
 #ifdef SPAMC_SSL
-	ssl = SSL_new(ctx);
-	SSL_set_fd(ssl, sock);
-	SSL_connect(ssl);
+            ssl = SSL_new(ctx);
+            SSL_set_fd(ssl, sock);
+            SSL_connect(ssl);
 #endif
-    }
-
-    /* Send to spamd */
-    if (flags & SPAMC_USE_SSL) {
+        }
+    
+        /* Send to spamd */
+        if (flags & SPAMC_USE_SSL) {
 #ifdef SPAMC_SSL
-	SSL_write(ssl, buf, len);
-	SSL_write(ssl, m->msg, m->msg_len);
+            SSL_write(ssl, buf, len);
+            SSL_write(ssl, towrite_buf, towrite_len);
 #endif
-    }
-    else {
-	full_write(sock, 0, buf, len);
-	full_write(sock, 0, m->msg, m->msg_len);
-	shutdown(sock, SHUT_WR);
-    }
+        }
+        else {
+            full_write(sock, 0, buf, len);
+            full_write(sock, 0, towrite_buf, towrite_len);
+            shutdown(sock, SHUT_WR);
+        }
+
+        /* free zlib buffer
+        * bug 6025: zlib buffer not freed if compression is used
+        */
+        if (zlib_on) {
+            _free_zlib_buffer(&zlib_buf, &zlib_bufsiz);
+        }
+    
+        /* ok, now read and parse it.  SPAMD/1.2 line first... */
+        failureval =
+                _spamc_read_full_line(m, flags, ssl, sock, buf, &len, bufsiz);
+    } /* end of filterloop */
 
-    /* ok, now read and parse it.  SPAMD/1.2 line first... */
-    failureval =
-	_spamc_read_full_line(m, flags, ssl, sock, buf, &len, bufsiz);
     if (failureval != EX_OK) {
-	goto failure;
+        goto failure;
     }
 
     if (sscanf(buf, "SPAMD/%18s %d %*s", versbuf, &response) != 2) {
@@ -946,6 +1426,14 @@ int message_filter(struct transport *tp, const char *username,
 	goto failure;
     }
 
+    if (flags & SPAMC_PING) {
+	closesocket(sock);
+	sock = -1;
+        m->out_len = sprintf(m->out, "SPAMD/%s %d\n", versbuf, response);
+        m->is_spam = EX_NOTSPAM;
+        return EX_OK;
+    }
+
     m->score = 0;
     m->threshold = 0;
     m->is_spam = EX_TOOBIG;
@@ -960,7 +1448,7 @@ int message_filter(struct transport *tp, const char *username,
 	    break;		/* end of headers */
 	}
 
-	if (_handle_spamd_header(m, flags, buf, len) < 0) {
+	if (_handle_spamd_header(m, flags, buf, len, &throwaway) < 0) {
 	    failureval = EX_PROTOCOL;
 	    goto failure;
 	}
@@ -995,20 +1483,17 @@ int message_filter(struct transport *tp, const char *username,
 
 	if (flags & SPAMC_USE_SSL) {
 	    len = full_read_ssl(ssl, (unsigned char *) m->out + m->out_len,
-				m->max_len + EXPANSION_ALLOWANCE + 1 -
-				m->out_len,
-				m->max_len + EXPANSION_ALLOWANCE + 1 -
-				m->out_len);
+				m->priv->alloced_size - m->out_len,
+				m->priv->alloced_size - m->out_len);
 	}
 	else {
 	    len = full_read(sock, 0, m->out + m->out_len,
-			    m->max_len + EXPANSION_ALLOWANCE + 1 - m->out_len,
-			    m->max_len + EXPANSION_ALLOWANCE + 1 -
-			    m->out_len);
+			    m->priv->alloced_size - m->out_len,
+			    m->priv->alloced_size - m->out_len);
 	}
 
 
-	if (len + m->out_len > m->max_len + EXPANSION_ALLOWANCE) {
+	if ((int) len + (int) m->out_len > (m->priv->alloced_size - 1)) {
 	    failureval = EX_TOOBIG;
 	    goto failure;
 	}
@@ -1028,12 +1513,16 @@ int message_filter(struct transport *tp, const char *username,
 	goto failure;
     }
 
+    if (flags & SPAMC_HEADERS) {
+        if (_append_original_body(m, flags) != EX_OK) {
+            goto failure;
+        }
+    }
+
     return EX_OK;
 
   failure:
-    free(m->out);
-    m->out = m->msg;
-    m->out_len = m->msg_len;
+	_use_msg_for_out(m);
     if (sock != -1) {
 	closesocket(sock);
     }
@@ -1048,52 +1537,289 @@ int message_filter(struct transport *tp, const char *username,
     return failureval;
 }
 
-
 int message_process(struct transport *trans, char *username, int max_size,
 		    int in_fd, int out_fd, const int flags)
 {
     int ret;
     struct message m;
 
+    assert(trans != NULL);
+
     m.type = MESSAGE_NONE;
 
-    m.max_len = max_size;
+    /* enforce max_size being unsigned, therefore >= 0 */
+    if (max_size < 0) {
+	ret = EX_SOFTWARE;
+        goto FAIL;
+    }
+    m.max_len = (unsigned int) max_size;
+
     ret = message_read(in_fd, flags, &m);
     if (ret != EX_OK)
-	goto FAIL;
+        goto FAIL;
     ret = message_filter(trans, username, flags, &m);
     if (ret != EX_OK)
-	goto FAIL;
+        goto FAIL;
     if (message_write(out_fd, &m) < 0)
-	goto FAIL;
+        goto FAIL;
     if (m.is_spam != EX_TOOBIG) {
-	message_cleanup(&m);
-	return m.is_spam;
+        message_cleanup(&m);
+        return m.is_spam;
     }
     message_cleanup(&m);
     return ret;
 
   FAIL:
     if (flags & SPAMC_CHECK_ONLY) {
-	full_write(out_fd, 1, "0/0\n", 4);
-	message_cleanup(&m);
-	return EX_NOTSPAM;
+        full_write(out_fd, 1, "0/0\n", 4);
+        message_cleanup(&m);
+        return EX_NOTSPAM;
     }
     else {
-	message_dump(in_fd, out_fd, &m);
-	message_cleanup(&m);
-	return ret;
+        message_dump(in_fd, out_fd, &m, flags);
+        message_cleanup(&m);
+        return ret;
+    }
+}
+
+int message_tell(struct transport *tp, const char *username, int flags,
+		 struct message *m, int msg_class,
+		 unsigned int tellflags, unsigned int *didtellflags)
+{
+    char buf[8192];
+    size_t bufsiz = (sizeof(buf) / sizeof(*buf)) - 4; /* bit of breathing room */
+    size_t len;
+    int sock = -1;
+    int rc;
+    char versbuf[20];
+    float version;
+    int response;
+    int failureval;
+    SSL_CTX *ctx = NULL;
+    SSL *ssl = NULL;
+    SSL_METHOD *meth;
+
+    assert(tp != NULL);
+    assert(m != NULL);
+
+    if (flags & SPAMC_USE_SSL) {
+#ifdef SPAMC_SSL
+	SSLeay_add_ssl_algorithms();
+	meth = SSLv3_client_method();
+	SSL_load_error_strings();
+	ctx = SSL_CTX_new(meth);
+#else
+	UNUSED_VARIABLE(ssl);
+	UNUSED_VARIABLE(meth);
+	UNUSED_VARIABLE(ctx);
+	libspamc_log(flags, LOG_ERR, "spamc not built with SSL support");
+	return EX_SOFTWARE;
+#endif
+    }
+
+    m->is_spam = EX_TOOBIG;
+
+    if (m->outbuf != NULL)
+        free(m->outbuf);
+    m->priv->alloced_size = m->max_len + EXPANSION_ALLOWANCE + 1;
+    if ((m->outbuf = malloc(m->priv->alloced_size)) == NULL) {
+	failureval = EX_OSERR;
+	goto failure;
+    }
+    m->out = m->outbuf;
+    m->out_len = 0;
+
+    /* Build spamd protocol header */
+    strcpy(buf, "TELL ");
+
+    len = strlen(buf);
+    if (len + strlen(PROTOCOL_VERSION) + 2 >= bufsiz) {
+	_use_msg_for_out(m);
+	return EX_OSERR;
+    }
+
+    strcat(buf, PROTOCOL_VERSION);
+    strcat(buf, "\r\n");
+    len = strlen(buf);
+
+    if (msg_class != 0) {
+      strcpy(buf + len, "Message-class: ");
+      if (msg_class == SPAMC_MESSAGE_CLASS_SPAM) {
+	strcat(buf + len, "spam\r\n");
+      }
+      else {
+	strcat(buf + len, "ham\r\n");
+      }
+      len += strlen(buf + len);
+    }
+
+    if ((tellflags & SPAMC_SET_LOCAL) || (tellflags & SPAMC_SET_REMOTE)) {
+      int needs_comma_p = 0;
+      strcat(buf + len, "Set: ");
+      if (tellflags & SPAMC_SET_LOCAL) {
+	strcat(buf + len, "local");
+	needs_comma_p = 1;
+      }
+      if (tellflags & SPAMC_SET_REMOTE) {
+	if (needs_comma_p == 1) {
+	  strcat(buf + len, ",");
+	}
+	strcat(buf + len, "remote");
+      }
+      strcat(buf + len, "\r\n");
+      len += strlen(buf + len);
+    }
+
+    if ((tellflags & SPAMC_REMOVE_LOCAL) || (tellflags & SPAMC_REMOVE_REMOTE)) {
+      int needs_comma_p = 0;
+      strcat(buf + len, "Remove: ");
+      if (tellflags & SPAMC_REMOVE_LOCAL) {
+	strcat(buf + len, "local");
+	needs_comma_p = 1;
+      }
+      if (tellflags & SPAMC_REMOVE_REMOTE) {
+	if (needs_comma_p == 1) {
+	  strcat(buf + len, ",");
+	}
+	strcat(buf + len, "remote");
+      }
+      strcat(buf + len, "\r\n");
+      len += strlen(buf + len);
+    }
+
+    if (username != NULL) {
+	if (strlen(username) + 8 >= (bufsiz - len)) {
+	    _use_msg_for_out(m);
+	    return EX_OSERR;
+	}
+	strcpy(buf + len, "User: ");
+	strcat(buf + len, username);
+	strcat(buf + len, "\r\n");
+	len += strlen(buf + len);
+    }
+    if ((m->msg_len > SPAMC_MAX_MESSAGE_LEN) || ((len + 27) >= (bufsiz - len))) {
+	_use_msg_for_out(m);
+	return EX_DATAERR;
+    }
+    len += sprintf(buf + len, "Content-length: %d\r\n\r\n", (int) m->msg_len);
+
+    if (m->priv->spamc_header_callback != NULL) {
+      char buf2[1024];
+      m->priv->spamc_header_callback(m, flags, buf2, 1024);
+      strncat(buf, buf2, bufsiz - len);
+    }
+
+    libspamc_timeout = m->timeout;
+    libspamc_connect_timeout = m->connect_timeout;	/* Sep 8, 2008 mrgus: separate connect timeout */
+
+    if (tp->socketpath)
+	rc = _try_to_connect_unix(tp, &sock);
+    else
+	rc = _try_to_connect_tcp(tp, &sock);
+
+    if (rc != EX_OK) {
+	_use_msg_for_out(m);
+	return rc;      /* use the error code try_to_connect_*() gave us. */
+    }
+
+    if (flags & SPAMC_USE_SSL) {
+#ifdef SPAMC_SSL
+	ssl = SSL_new(ctx);
+	SSL_set_fd(ssl, sock);
+	SSL_connect(ssl);
+#endif
+    }
+
+    /* Send to spamd */
+    if (flags & SPAMC_USE_SSL) {
+#ifdef SPAMC_SSL
+	SSL_write(ssl, buf, len);
+	SSL_write(ssl, m->msg, m->msg_len);
+#endif
+    }
+    else {
+	full_write(sock, 0, buf, len);
+	full_write(sock, 0, m->msg, m->msg_len);
+	shutdown(sock, SHUT_WR);
+    }
+
+    /* ok, now read and parse it.  SPAMD/1.2 line first... */
+    failureval =
+	_spamc_read_full_line(m, flags, ssl, sock, buf, &len, bufsiz);
+    if (failureval != EX_OK) {
+	goto failure;
+    }
+
+    if (sscanf(buf, "SPAMD/%18s %d %*s", versbuf, &response) != 2) {
+	libspamc_log(flags, LOG_ERR, "spamd responded with bad string '%s'", buf);
+	failureval = EX_PROTOCOL;
+	goto failure;
+    }
+
+    versbuf[19] = '\0';
+    version = _locale_safe_string_to_float(versbuf, 20);
+    if (version < 1.0) {
+	libspamc_log(flags, LOG_ERR, "spamd responded with bad version string '%s'",
+	       versbuf);
+	failureval = EX_PROTOCOL;
+	goto failure;
+    }
+
+    m->score = 0;
+    m->threshold = 0;
+    m->is_spam = EX_TOOBIG;
+    while (1) {
+	failureval =
+	    _spamc_read_full_line(m, flags, ssl, sock, buf, &len, bufsiz);
+	if (failureval != EX_OK) {
+	    goto failure;
+	}
+
+	if (len == 0 && buf[0] == '\0') {
+	    break;		/* end of headers */
+	}
+
+	if (_handle_spamd_header(m, flags, buf, len, didtellflags) < 0) {
+	    failureval = EX_PROTOCOL;
+	    goto failure;
+	}
+    }
+
+    len = 0;			/* overwrite those headers */
+
+    shutdown(sock, SHUT_RD);
+    closesocket(sock);
+    sock = -1;
+
+    libspamc_timeout = 0;
+
+    return EX_OK;
+
+  failure:
+    _use_msg_for_out(m);
+    if (sock != -1) {
+        closesocket(sock);
     }
+    libspamc_timeout = 0;
+
+    if (flags & SPAMC_USE_SSL) {
+#ifdef SPAMC_SSL
+	SSL_free(ssl);
+	SSL_CTX_free(ctx);
+#endif
+    }
+    return failureval;
 }
 
 void message_cleanup(struct message *m)
 {
-    if (m->out != NULL)
-	free(m->out);
-    if (m->raw != NULL && m->raw != m->out)
-	free(m->raw);
-    if (m->priv != NULL && (char*)m->priv != m->out && (char*)m->priv != m->raw)
-	free(m->priv);
+    assert(m != NULL);
+    if (m->outbuf != NULL)
+        free(m->outbuf);
+    if (m->raw != NULL)
+        free(m->raw);
+    if (m->priv != NULL)
+        free(m->priv);
     _clear_message(m);
 }
 
@@ -1106,19 +1832,19 @@ int process_message(struct transport *tp, char *username, int max_size,
 
     flags = SPAMC_RAW_MODE;
     if (my_check_only)
-	flags |= SPAMC_CHECK_ONLY;
+        flags |= SPAMC_CHECK_ONLY;
     if (my_safe_fallback)
-	flags |= SPAMC_SAFE_FALLBACK;
+        flags |= SPAMC_SAFE_FALLBACK;
 
     return message_process(tp, username, max_size, in_fd, out_fd, flags);
 }
 
 /*
- * init_transport()
- *
- *	Given a pointer to a transport structure, set it to "all empty".
- *	The default is a localhost connection.
- */
+* init_transport()
+*
+*	Given a pointer to a transport structure, set it to "all empty".
+*	The default is a localhost connection.
+*/
 void transport_init(struct transport *tp)
 {
     assert(tp != 0);
@@ -1128,163 +1854,364 @@ void transport_init(struct transport *tp)
     tp->type = TRANSPORT_LOCALHOST;
     tp->port = 783;
     tp->flags = 0;
+    tp->retry_sleep = -1;
 }
 
 /*
- * randomize_hosts()
- *
- *	Given the transport object that contains one or more IP addresses
- *	in this "hosts" list, rotate it by a random number of shifts to
- *	randomize them - this is a kind of load balancing. It's possible
- *	that the random number will be 0, which says not to touch. We don't
- *	do anything unless 
- */
+* randomize_hosts()
+*
+*	Given the transport object that contains one or more IP addresses
+*	in this "hosts" list, rotate it by a random number of shifts to
+*	randomize them - this is a kind of load balancing. It's possible
+*	that the random number will be 0, which says not to touch. We don't
+*	do anything unless 
+*/
 
 static void _randomize_hosts(struct transport *tp)
 {
+#ifdef SPAMC_HAS_ADDRINFO
+    struct addrinfo *tmp;
+#else
+    struct in_addr tmp;
+#endif
+    int i;
     int rnum;
 
     assert(tp != 0);
 
     if (tp->nhosts <= 1)
-	return;
+        return;
 
     rnum = rand() % tp->nhosts;
 
     while (rnum-- > 0) {
-	struct in_addr tmp = tp->hosts[0];
-	int i;
+        tmp = tp->hosts[0];
 
-	for (i = 1; i < tp->nhosts; i++)
-	    tp->hosts[i - 1] = tp->hosts[i];
+        for (i = 1; i < tp->nhosts; i++)
+            tp->hosts[i - 1] = tp->hosts[i];
 
-	tp->hosts[i - 1] = tmp;
+        tp->hosts[i - 1] = tmp;
     }
 }
 
 /*
- * transport_setup()
- *
- *	Given a "transport" object that says how we're to connect to the
- *	spam daemon, perform all the initial setup required to make the
- *	connection process a smooth one. The main work is to do the host
- *	name lookup and copy over all the IP addresses to make a local copy
- *	so they're not kept in the resolver's static state.
- *
- *	Here we also manage quasi-load balancing and failover: if we're
- *	doing load balancing, we randomly "rotate" the list to put it in
- *	a different order, and then if we're not doing failover we limit
- *	the hosts to just one. This way *all* connections are done with
- *	the intention of failover - makes the code a bit more clear.
- */
+* transport_setup()
+*
+*	Given a "transport" object that says how we're to connect to the
+*	spam daemon, perform all the initial setup required to make the
+*	connection process a smooth one. The main work is to do the host
+*	name lookup and copy over all the IP addresses to make a local copy
+*	so they're not kept in the resolver's static state.
+*
+*	Here we also manage quasi-load balancing and failover: if we're
+*	doing load balancing, we randomly "rotate" the list to put it in
+*	a different order, and then if we're not doing failover we limit
+*	the hosts to just one. This way *all* connections are done with
+*	the intention of failover - makes the code a bit more clear.
+*/
 int transport_setup(struct transport *tp, int flags)
 {
-    struct hostent *hp = 0;
+#ifdef SPAMC_HAS_ADDRINFO
+    struct addrinfo hints, *res, *addrp;
+    char port[6];
+    int origerr;
+#else
+    struct hostent *hp;
     char **addrp;
+#endif
+    char *hostlist, *hostname;
+    int errbits;
 
 #ifdef _WIN32
     /* Start Winsock up */
     WSADATA wsaData;
     int nCode;
     if ((nCode = WSAStartup(MAKEWORD(1, 1), &wsaData)) != 0) {
-	g_print("WSAStartup() returned error code %d\n", nCode);
-	return EX_OSERR;
+        printf("WSAStartup() returned error code %d\n", nCode);
+        return EX_OSERR;
     }
 
 #endif
 
+    assert(tp != NULL);
     tp->flags = flags;
 
-    assert(tp != 0);
+#ifdef SPAMC_HAS_ADDRINFO
+    snprintf(port, 6, "%d", tp->port);
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_flags = 0;
+    hints.ai_socktype = SOCK_STREAM;
+
+    if (       (flags & SPAMC_USE_INET4) && !(flags & SPAMC_USE_INET6)) {
+      hints.ai_family = PF_INET;
+#ifdef PF_INET6
+    } else if ((flags & SPAMC_USE_INET6) && !(flags & SPAMC_USE_INET4)) {
+      hints.ai_family = PF_INET6;
+#endif
+    } else {
+      hints.ai_family = PF_UNSPEC;
+    }
+#endif
 
     switch (tp->type) {
 #ifndef _WIN32
     case TRANSPORT_UNIX:
-	assert(tp->socketpath != 0);
-	return EX_OK;
+        assert(tp->socketpath != 0);
+        return EX_OK;
 #endif
     case TRANSPORT_LOCALHOST:
-	tp->hosts[0].s_addr = inet_addr("127.0.0.1");
-	tp->nhosts = 1;
-	return EX_OK;
+#ifdef SPAMC_HAS_ADDRINFO
+        /* getaddrinfo(NULL) will look up the loopback address.
+         * See also bug 5057,  ::1 will be tried before 127.0.0.1
+         * unless overridden (through hints) by a command line option -4
+         */
+        if ((origerr = getaddrinfo(NULL, port, &hints, &res)) != 0) {
+            libspamc_log(flags, LOG_ERR, 
+                  "getaddrinfo for a loopback address failed: %s",
+                  gai_strerror(origerr));
+            return EX_OSERR;
+        }
+        tp->hosts[0] = res;
+#else
+        tp->hosts[0].s_addr = inet_addr("127.0.0.1");
+#endif
+        tp->nhosts = 1;
+        return EX_OK;
 
     case TRANSPORT_TCP:
-	if (NULL == (hp = gethostbyname(tp->hostname))) {
-	    int origherr = h_errno;	/* take a copy before syslog() */
-
-	    libspamc_log(flags, LOG_ERR, "gethostbyname(%s) failed: h_errno=%d",
-		    tp->hostname, origherr);
-	    switch (origherr) {
-	    case HOST_NOT_FOUND:
-	    case NO_ADDRESS:
-	    case NO_RECOVERY:
-		return EX_NOHOST;
-	    case TRY_AGAIN:
-		return EX_TEMPFAIL;
-	    default:
-		return EX_OSERR;
-	    }
-	}
+        if ((hostlist = strdup(tp->hostname)) == NULL)
+            return EX_OSERR;
+
+        /* We want to return the least permanent error, in this bitmask we
+         * record the errors seen with:
+         *  0: no error
+         *  1: EX_TEMPFAIL
+         *  2: EX_NOHOST
+         * EX_OSERR will return immediately.
+         * Bits aren't reset so a check against nhosts is needed to determine
+         * if something went wrong.
+         */
+        errbits = 0;
+        tp->nhosts = 0;
+        /* Start with char offset in front of the string because we'll add 
+         * one in the loop
+         */
+        hostname = hostlist - 1;
+        do {
+            char *hostend;
+            
+            hostname += 1;
+            hostend = strchr(hostname, ',');
+            if (hostend != NULL) {
+                *hostend = '\0';
+            }
+#ifdef SPAMC_HAS_ADDRINFO            
+            if ((origerr = getaddrinfo(hostname, port, &hints, &res))) {
+                libspamc_log(flags, LOG_DEBUG, 
+                      "getaddrinfo(%s) failed: %s",
+                      hostname, gai_strerror(origerr));
+                switch (origerr) { 
+                case EAI_AGAIN:
+                    errbits |= 1;
+                    break;
+                case EAI_FAMILY: /*address family not supported*/
+                case EAI_SOCKTYPE: /*socket type not supported*/
+                case EAI_BADFLAGS: /*ai_flags is invalid*/
+                case EAI_NONAME: /*node or service unknown*/
+                case EAI_SERVICE: /*service not available*/
+/* work around Cygwin IPv6 patch - err codes not defined in Windows aren't in patch */
+#ifdef HAVE_EAI_ADDRFAMILY
+                case EAI_ADDRFAMILY: /*no addresses in requested family*/
+#endif
+#ifdef HAVE_EAI_SYSTEM
+                case EAI_SYSTEM: /*system error, check errno*/
+#endif
+#ifdef HAVE_EAI_NODATA
+                case EAI_NODATA: /*address exists, but no data*/
+#endif
+                case EAI_MEMORY: /*out of memory*/
+                case EAI_FAIL: /*name server returned permanent error*/
+                    errbits |= 2;
+                    break;
+                default:
+                    /* should not happen, all errors are checked above */
+                    free(hostlist);
+                    return EX_OSERR;
+                }
+                goto nexthost; /* try next host in list */
+            }
+#else
+            if ((hp = gethostbyname(hostname)) == NULL) {
+                int origerr = h_errno; /* take a copy before syslog() */
+                libspamc_log(flags, LOG_DEBUG, "gethostbyname(%s) failed: h_errno=%d",
+                    hostname, origerr);
+                switch (origerr) {
+                case TRY_AGAIN:
+                    errbits |= 1;
+                    break;
+                case HOST_NOT_FOUND:
+                case NO_ADDRESS:
+                case NO_RECOVERY:
+                    errbits |= 2;
+                    break;
+                default:
+                    /* should not happen, all errors are checked above */
+                    free(hostlist);
+                    return EX_OSERR;
+                }
+                goto nexthost; /* try next host in list */
+            }
+#endif
+            
+            /* If we have no hosts at all */
+#ifdef SPAMC_HAS_ADDRINFO
+            if(res == NULL)
+#else
+            if (hp->h_addr_list[0] == NULL
+             || hp->h_length != sizeof tp->hosts[0]
+             || hp->h_addrtype != AF_INET)
+                /* no hosts/bad size/wrong family */
+#endif
+            {
+                errbits |= 1;
+                goto nexthost; /* try next host in list */
+            }
+
+            /* Copy all the IP addresses into our private structure.
+             * This gets them out of the resolver's static area and
+             * means we won't ever walk all over the list with other
+             * calls.
+             */
+#ifdef SPAMC_HAS_ADDRINFO
+            if(tp->nhosts == TRANSPORT_MAX_HOSTS) {
+               libspamc_log(flags, LOG_NOTICE, 
+                     "hit limit of %d hosts, ignoring remainder",
+                     TRANSPORT_MAX_HOSTS);
+               break;
+            }
+
+            /* treat all A or AAAA records of each host as one entry */
+            tp->hosts[tp->nhosts++] = res;
+
+            /* alternatively, treat multiple A or AAAA records
+               of one host as individual entries */
+/*          for (addrp = res; addrp != NULL; ) {
+ *              tp->hosts[tp->nhosts] = addrp;
+ *              addrp = addrp->ai_next;     /-* before NULLing ai_next *-/
+ *              tp->hosts[tp->nhosts]->ai_next = NULL;
+ *              tp->nhosts++;
+ *          }
+ */
 
-		/*--------------------------------------------------------
-		 * If we have no hosts at all, or if they are some other
-	 	 * kind of address family besides IPv4, then we really
-		 * just have no hosts at all.
-		 */
-	if (hp->h_addr_list[0] == 0) {
-	    /* no hosts in this list */
-	    return EX_NOHOST;
-	}
+#else
+            for (addrp = hp->h_addr_list; *addrp; addrp++) {
+                if (tp->nhosts == TRANSPORT_MAX_HOSTS) {
+                    libspamc_log(flags, LOG_NOTICE, "hit limit of %d hosts, ignoring remainder",
+                        TRANSPORT_MAX_HOSTS);
+                    break;
+                }
+                memcpy(&tp->hosts[tp->nhosts], *addrp, hp->h_length);
+                tp->nhosts++;
+            }
+#endif            
+nexthost:
+            hostname = hostend;
+        } while (hostname != NULL);
+        free(hostlist);
+        
+        if (tp->nhosts == 0) {
+            if (errbits & 1) {
+                libspamc_log(flags, LOG_ERR, "could not resolve any hosts (%s): a temporary error occurred",
+                    tp->hostname); 
+                return EX_TEMPFAIL;
+            }
+            else {
+                libspamc_log(flags, LOG_ERR, "could not resolve any hosts (%s): no such host",
+                    tp->hostname); 
+                return EX_NOHOST;
+            }
+        }
+        
+        /* QUASI-LOAD-BALANCING
+         *
+         * If the user wants to do quasi load balancing, "rotate"
+         * the list by a random amount based on the current time.
+         * This may later be truncated to a single item. This is
+         * meaningful only if we have more than one host.
+	 */
 
-	if (hp->h_length != sizeof tp->hosts[0]
-	    || hp->h_addrtype != AF_INET) {
-	    /* FAIL - bad size/protocol/family? */
-	    return EX_NOHOST;
-	}
+        if ((flags & SPAMC_RANDOMIZE_HOSTS) && tp->nhosts > 1) {
+            _randomize_hosts(tp);
+        }
+
+        /* If the user wants no fallback, simply truncate the host
+         * list to just one - this pretends that this is the extent
+         * of our connection list - then it's not a special case.
+         */
+        if (!(flags & SPAMC_SAFE_FALLBACK) && tp->nhosts > 1) {
+            /* truncating list */
+            tp->nhosts = 1;
+        }
+        
+        return EX_OK;
+    }
+    
+    /* oops, unknown transport type */
+    return EX_OSERR;
+}
 
-		/*--------------------------------------------------------
-		 * Copy all the IP addresses into our private structure.
-		 * This gets them out of the resolver's static area and
-		 * means we won't ever walk all over the list with other
-		 * calls.
-		 */
-	tp->nhosts = 0;
+/*
+* transport_cleanup()
+*
+*	Given a "transport" object that says how we're to connect to the
+*	spam daemon, delete and free any buffers allocated so that it
+*       can be discarded without causing a memory leak.
+*/
+void transport_cleanup(struct transport *tp)
+{
 
-	for (addrp = hp->h_addr_list; *addrp; addrp++) {
-	    if (tp->nhosts >= TRANSPORT_MAX_HOSTS - 1) {
-		libspamc_log(flags, LOG_ERR, "hit limit of %d hosts, ignoring remainder",
-		       TRANSPORT_MAX_HOSTS - 1);
-		break;
-	    }
+#ifdef SPAMC_HAS_ADDRINFO
+  int i;
 
-	    memcpy(&tp->hosts[tp->nhosts], *addrp, sizeof tp->hosts[0]);
+  for(i=0;i<tp->nhosts;i++) {
+      if (tp->hosts[i] != NULL) {
+          freeaddrinfo(tp->hosts[i]);
+          tp->hosts[i] = NULL;
+      }
+  }
+#endif
 
-	    tp->nhosts++;
-	}
+}
 
-		/*--------------------------------------------------------
-		 * QUASI-LOAD-BALANCING
-		 *
-		 * If the user wants to do quasi load balancing, "rotate"
-		 * the list by a random amount based on the current time.
-		 * This may later be truncated to a single item. This is
-		 * meaningful only if we have more than one host.
-		 */
-	if ((flags & SPAMC_RANDOMIZE_HOSTS) && tp->nhosts > 1) {
-	    _randomize_hosts(tp);
-	}
+/*
+* register_libspamc_log_callback()
+*
+* Register a callback handler for libspamc_log to replace the default behaviour.
+*/
 
-		/*--------------------------------------------------------
-		 * If the user wants no fallback, simply truncate the host
-		 * list to just one - this pretends that this is the extent
-		 * of our connection list - then it's not a special case.
-		 */
-	if (!(flags & SPAMC_SAFE_FALLBACK) && tp->nhosts > 1) {
-	    /* truncating list */
-	    tp->nhosts = 1;
-	}
-    }
-    return EX_OK;
+void register_libspamc_log_callback(void (*function)(int flags, int level, char *msg, va_list args)) {
+  libspamc_log_callback = function;
+}
+
+/*
+* register_spamc_header_callback()
+*
+* Register a callback handler to generate spamc headers for a given message
+*/
+
+void register_spamc_header_callback(const struct message *m, void (*func)(struct message *m, int flags, char *buf, int len)) {
+  m->priv->spamc_header_callback = func;
+}
+
+/*
+* register_spamd_header_callback()
+*
+* Register a callback handler to generate spamd headers for a given message
+*/
+
+void register_spamd_header_callback(const struct message *m, void (*func)(struct message *m, int flags, const char *buf, int len)) {
+  m->priv->spamd_header_callback = func;
 }
 
 /* --------------------------------------------------------------------------- */
@@ -1300,7 +2227,10 @@ libspamc_log (int flags, int level, char *msg, ...)
 
     va_start(ap, msg);
 
-    if ((flags & SPAMC_LOG_TO_STDERR) != 0) {
+    if ((flags & SPAMC_LOG_TO_CALLBACK) != 0 && libspamc_log_callback != NULL) {
+      libspamc_log_callback(flags, level, msg, ap);
+    }
+    else if ((flags & SPAMC_LOG_TO_STDERR) != 0) {
         /* create a log-line buffer */
         len = snprintf(buf, LOG_BUFSIZ, "spamc: ");
         len += vsnprintf(buf+len, LOG_BUFSIZ-len, msg, ap);
@@ -1310,8 +2240,8 @@ libspamc_log (int flags, int level, char *msg, ...)
 
         len += snprintf(buf+len, LOG_BUFSIZ-len, "\n");
         buf[LOG_BUFSIZ] = '\0';     /* ensure termination */
-        if (write (2, buf, len) != len)
-		goto out;
+        (void) write (2, buf, len);
+
     } else {
         vsnprintf(buf, LOG_BUFSIZ, msg, ap);
         buf[LOG_BUFSIZ] = '\0';     /* ensure termination */
@@ -1319,22 +2249,22 @@ libspamc_log (int flags, int level, char *msg, ...)
         syslog (level, "%s", buf);
 #else
         (void) level;  /* not used. suppress compiler warning */
-        g_printerr ("%s\n", buf);
+        f_printerr ("%s\n", buf);
 #endif
     }
-out:
+
     va_end(ap);
 }
 
 /* --------------------------------------------------------------------------- */
 
 /*
- * Unit tests.  Must be built externally, e.g.:
- *
- * gcc -g -DLIBSPAMC_UNIT_TESTS spamd/spamc.c spamd/libspamc.c spamd/utils.c -o libspamctest
- * ./libspamctest
- *
- */
+* Unit tests.  Must be built externally, e.g.:
+*
+* gcc -g -DLIBSPAMC_UNIT_TESTS spamd/spamc.c spamd/libspamc.c spamd/utils.c -o libspamctest
+* ./libspamctest
+*
+*/
 #ifdef LIBSPAMC_UNIT_TESTS
 
 static void _test_locale_safe_string_to_float_val(float input)
@@ -1346,42 +2276,42 @@ static void _test_locale_safe_string_to_float_val(float input)
     sprintf(inputstr, "%f", input);
     output = _locale_safe_string_to_float(inputstr, 99);
     if (input == output) {
-	return;
+        return;
     }
 
     /* could be a rounding error.  print as string and compare those */
     sprintf(cmpbuf1, "%f", input);
     sprintf(cmpbuf2, "%f", output);
     if (!strcmp(cmpbuf1, cmpbuf2)) {
-	return;
+        return;
     }
 
-    g_print("FAIL: input=%f != output=%f\n", input, output);
+    printf("FAIL: input=%f != output=%f\n", input, output);
 }
 
 static void unit_test_locale_safe_string_to_float(void)
 {
     float statictestset[] = {	/* will try both +ve and -ve */
-	0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001,
-	9.1, 9.91, 9.991, 9.9991, 9.99991, 9.999991,
-	0.0			/* end of set constant */
+        0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001,
+        9.1, 9.91, 9.991, 9.9991, 9.99991, 9.999991,
+        0.0			/* end of set constant */
     };
     float num;
     int i;
 
-    g_print("starting unit_test_locale_safe_string_to_float\n");
+    printf("starting unit_test_locale_safe_string_to_float\n");
     /* tests of precision */
     for (i = 0; statictestset[i] != 0.0; i++) {
-	_test_locale_safe_string_to_float_val(statictestset[i]);
-	_test_locale_safe_string_to_float_val(-statictestset[i]);
-	_test_locale_safe_string_to_float_val(1 - statictestset[i]);
-	_test_locale_safe_string_to_float_val(1 + statictestset[i]);
+        _test_locale_safe_string_to_float_val(statictestset[i]);
+        _test_locale_safe_string_to_float_val(-statictestset[i]);
+        _test_locale_safe_string_to_float_val(1 - statictestset[i]);
+        _test_locale_safe_string_to_float_val(1 + statictestset[i]);
     }
     /* now exhaustive, in steps of 0.01 */
     for (num = -1000.0; num < 1000.0; num += 0.01) {
-	_test_locale_safe_string_to_float_val(num);
+        _test_locale_safe_string_to_float_val(num);
     }
-    g_print("finished unit_test_locale_safe_string_to_float\n");
+    printf("finished unit_test_locale_safe_string_to_float\n");
 }
 
 void do_libspamc_unit_tests(void)
diff --git a/src/plugins/spamassassin/libspamc.h b/src/plugins/spamassassin/libspamc.h
index dd44ddc..7a6fc12 100644
--- a/src/plugins/spamassassin/libspamc.h
+++ b/src/plugins/spamassassin/libspamc.h
@@ -1,9 +1,10 @@
 /* <@LICENSE>
- * Copyright 2004 Apache Software Foundation
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at:
  * 
  *     http://www.apache.org/licenses/LICENSE-2.0
  * 
@@ -26,17 +27,26 @@
  4115 named type definition in parentheses
  4127 conditional expression is constant
  4514 unreferenced inline function removed
+ 4996 deprecated "unsafe" functions (bug 4855)
  */
 #pragma warning( disable : 4115 4127 4514 )
+#if (_MSC_VER >= 1400)  /* VC8+ */
+#pragma warning( disable : 4996 )
+#endif
+
 #endif
 #include <winsock.h>
 #else
 #include <netdb.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
+/* some platforms (Cygwin) don't implement getaddrinfo */
+#ifdef EAI_AGAIN
+#define SPAMC_HAS_ADDRINFO 1
+#endif
 #endif
 
-#ifdef _WIN32
+#if (defined(_WIN32) || !defined(_SYSEXITS_H))
 /* FIXME: This stuff has to go somewhere else */
 
 #define EX_OK           0
@@ -80,22 +90,65 @@
 #define SPAMC_RAW_MODE       0
 #define SPAMC_BSMTP_MODE     1
 
-#define SPAMC_USE_SSL	     (1<<27)
-#define SPAMC_SAFE_FALLBACK  (1<<28)
-#define SPAMC_CHECK_ONLY     (1<<29)
+#define SPAMC_USE_INET6       (1<<31)
+#define SPAMC_USE_INET4       (1<<30)
+
+#define SPAMC_CHECK_ONLY      (1<<29)
+#define SPAMC_SAFE_FALLBACK   (1<<28)
+#define SPAMC_USE_SSL         (1<<27)
 
 /* Jan 30, 2003 ym: added reporting options */
-#define SPAMC_REPORT         (1<<26)
-#define SPAMC_REPORT_IFSPAM  (1<<25)
+#define SPAMC_REPORT          (1<<26)
+#define SPAMC_REPORT_IFSPAM   (1<<25)
 
 /* Feb  1 2003 jm: might as well fix bug 191 as well */
-#define SPAMC_SYMBOLS        (1<<24)
+#define SPAMC_SYMBOLS         (1<<24)
 
 /* 2003/04/16 SJF: randomize hostname order (quasi load balancing) */
 #define SPAMC_RANDOMIZE_HOSTS (1<<23)
 
 /* log to stderr */
-#define SPAMC_LOG_TO_STDERR  (1<<22)
+#define SPAMC_LOG_TO_STDERR   (1<<22)
+
+/* Nov 24, 2004 NP: added learning support */
+#define SPAMC_LEARN	      (1<<21)
+
+/* May 5, 2005 NP: added list reporting support */
+#define SPAMC_REPORT_MSG      (1<<20)
+
+/* Oct 21, 2005 sidney: added ping test */
+#define SPAMC_PING      (1<<19)
+
+/* Jan 1, 2007 sidney: added SSL protocol versions */
+/* no flags means use default of SSL_v23 */
+/* Set both flags to specify TSL_v1 */
+#define SPAMC_TLSV1 (1<<18)
+#define SPAMC_SSLV3 (1<<17)
+
+/* Nov 30, 2006 jm: add -z, zlib support */
+#define SPAMC_USE_ZLIB        (1<<16)
+
+/* Jan 16, 2007 jm: get markup headers from spamd */
+#define SPAMC_HEADERS         (1<<15)
+
+/* December 5, 2007 duncf: send log messages to callback */
+#define SPAMC_LOG_TO_CALLBACK (1<<14)
+
+/* December 6, 2011 Sebastian Wiesinger <sebastian at karotte.org>:
+ * Turn EX_UNAVAILABLE into EX_TEMPFAIL - bug 6717
+ * */
+#define SPAMC_UNAVAIL_TEMPFAIL (1<<13)
+
+#define SPAMC_MESSAGE_CLASS_SPAM 1
+#define SPAMC_MESSAGE_CLASS_HAM  2
+
+#define SPAMC_SET_LOCAL          1
+#define SPAMC_SET_REMOTE         2
+
+#define SPAMC_REMOVE_LOCAL       4
+#define SPAMC_REMOVE_REMOTE      8
+
+#define SPAMC_MAX_MESSAGE_LEN     (256 * 1024 * 1024)     /* see bug 4928 */
 
 /* Aug 14, 2002 bj: A struct for storing a message-in-progress */
 typedef enum
@@ -114,15 +167,17 @@ struct message
     /* Set before passing the struct on! */
     unsigned int max_len; /* messages larger than this will return EX_TOOBIG */
     int timeout;		/* timeout for read() system calls */
+    int connect_timeout;	/* Sep 8, 2008 mrgus: timeout for opening sockets */
 
     /* Filled in by message_read */
     message_type_t type;
     char *raw;
-    unsigned int raw_len;		/* Raw message buffer */
+    int raw_len;		/* Raw message buffer */
+    /* note: do not make "raw_len" in particular unsigned! see bug 4593 */
     char *pre;
     int pre_len;		/* Pre-message data (e.g. SMTP commands) */
     char *msg;
-    unsigned int msg_len;		/* The message */
+    int msg_len;		/* The message */
     char *post;
     int post_len;		/* Post-message data (e.g. SMTP commands) */
     int content_length;
@@ -131,6 +186,7 @@ struct message
     int is_spam;		/* EX_ISSPAM if the message is spam, EX_NOTSPAM
 				   if not */
     float score, threshold;	/* score and threshold */
+    char *outbuf;		/* Buffer for output from spamd */
     char *out;
     int out_len;		/* Output from spamd. Either the filtered
 				   message, or the check-only response. Or else,
@@ -178,11 +234,27 @@ struct transport
 
     unsigned short port;	/* for TCP sockets              */
 
+#ifdef SPAMC_HAS_ADDRINFO
+    struct addrinfo *hosts[TRANSPORT_MAX_HOSTS];
+#else
     struct in_addr hosts[TRANSPORT_MAX_HOSTS];
+#endif
     int nhosts;
     int flags;
+
+    /* added in SpamAssassin 3.2.0 */
+    int connect_retries;
+    int retry_sleep;
+
+    /* Added for filterloop */
+    int filter_retries;
+    int filter_retry_sleep;
 };
 
+/* Initialise and setup transport-specific context for the connection
+ * to spamd.  Note that this may leak a small amount of string data for
+ * the remote hostname (bug 5531) if called repeatedly; use
+ *  transport_cleanup() to clean this up. */
 extern void transport_init(struct transport *tp);
 extern int transport_setup(struct transport *tp, int flags);
 
@@ -207,11 +279,20 @@ long message_write(int out_fd, struct message *m);
 int message_filter(struct transport *tp, const char *username,
 		   int flags, struct message *m);
 
+/* Process the message through the spamd tell command, making as many
+ * connection attempts as are implied by the transport structure. To make
+ * this do failover, more than one host is defined, but if there is only
+ * one there, no failover is done.
+ */
+int message_tell(struct transport *tp, const char *username, int flags,
+		 struct message *m, int msg_class,
+		 unsigned int tellflags, unsigned int *didtellflags);
+
 /* Dump the message. If there is any data in the message (typically, m->type
  * will be MESSAGE_ERROR) it will be message_writed. Then, fd_in will be piped
  * to fd_out intol EOF. This is particularly useful if you get back an
  * EX_TOOBIG. */
-void message_dump(int in_fd, int out_fd, struct message *m);
+void message_dump(int in_fd, int out_fd, struct message *m, int flags);
 
 /* Do a message_read->message_filter->message_write sequence, handling errors
  * appropriately with dump_message or appropriate CHECK_ONLY output. Returns
@@ -228,6 +309,19 @@ int process_message(struct transport *tp, char *username,
 		    int max_size, int in_fd, int out_fd,
 		    const int check_only, const int safe_fallback);
 
+void register_spamc_header_callback(const struct message *m, void (*func)(struct message *m, int flags, char *buf, int len));
+void register_spamd_header_callback(const struct message *m, void (*func)(struct message *m, int flags, const char *buf, int len));
+
+void register_libspamc_log_callback(void (*function)(int flags, int level, char *msg, va_list args));
+
 void libspamc_log(int flags, int level, char *msg, ...);
 
+/* Cleanup the resources allocated for storing details of the transport.
+ * Added in SpamAssassin 3.3.0. */
+void transport_cleanup(struct transport *tp);
+
+/* define a preprocessor symbol so that calling code can tell if the
+ * transport_cleanup() API function is available. */
+#define SPAMC_HAS_TRANSPORT_CLEANUP
+
 #endif
diff --git a/src/plugins/spamassassin/utils.c b/src/plugins/spamassassin/utils.c
index f42c961..0e130fb 100644
--- a/src/plugins/spamassassin/utils.c
+++ b/src/plugins/spamassassin/utils.c
@@ -1,9 +1,10 @@
 /* <@LICENSE>
- * Copyright 2004 Apache Software Foundation
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at:
  * 
  *     http://www.apache.org/licenses/LICENSE-2.0
  * 
@@ -27,8 +28,9 @@
  4115 named type definition in parentheses
  4127 conditional expression is constant
  4514 unreferenced inline function removed
+ 4996 deprecated "unsafe" functions
  */
-#pragma warning( disable : 4115 4127 4514 )
+#pragma warning( disable : 4115 4127 4514 4996 )
 #endif
 
 #include <io.h>
@@ -67,6 +69,33 @@ static void catch_alrm(int x)
 }
 #endif
 
+int timeout_connect (int sockfd, const struct sockaddr *serv_addr, size_t addrlen)
+{
+    int ret;
+
+#ifndef _WIN32
+    sigfunc* sig;
+
+    sig = sig_catch(SIGALRM, catch_alrm);
+    if (libspamc_connect_timeout > 0) {
+      alarm(libspamc_connect_timeout);
+    }
+#endif
+
+    ret = connect(sockfd, serv_addr, addrlen);
+
+#ifndef _WIN32
+    if (libspamc_connect_timeout > 0) {
+      alarm(0);
+    }
+  
+    /* restore old signal handler */
+    sig_catch(SIGALRM, sig);
+#endif
+  
+    return ret;
+}
+
 int fd_timeout_read(int fd, char fdflag, void *buf, size_t nbytes)
 {
     int nred;
diff --git a/src/plugins/spamassassin/utils.h b/src/plugins/spamassassin/utils.h
index 3a28754..fc34443 100644
--- a/src/plugins/spamassassin/utils.h
+++ b/src/plugins/spamassassin/utils.h
@@ -1,9 +1,10 @@
 /* <@LICENSE>
- * Copyright 2004 Apache Software Foundation
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at:
  * 
  *     http://www.apache.org/licenses/LICENSE-2.0
  * 
@@ -20,7 +21,10 @@
 
 #define UNUSED_VARIABLE(v)	((void)(v))
 
+#include <stddef.h>
+
 extern int libspamc_timeout;	/* default timeout in seconds */
+extern int libspamc_connect_timeout;	/* Sep 8, 2008 mrgus: default connect timeout in seconds */
 
 #ifdef SPAMC_SSL
 #include <openssl/crypto.h>
@@ -35,49 +39,77 @@ typedef int SSL_METHOD;
 
 #ifdef _WIN32
 #include <winsock.h>
-//
-// BSD-compatible socket error codes for Win32
-//
-
+/*
+ * BSD-compatible socket error codes for Win32
+ */
+#undef  EWOULDBLOCK      /* override definition in errno.h */
 #define EWOULDBLOCK             WSAEWOULDBLOCK
+#undef  EINPROGRESS      /* override definition in errno.h */
 #define EINPROGRESS             WSAEINPROGRESS
+#undef  EALREADY         /* override definition in errno.h */
 #define EALREADY                WSAEALREADY
+#undef  ENOTSOCK         /* override definition in errno.h */
 #define ENOTSOCK                WSAENOTSOCK
+#undef  EDESTADDRREQ     /* override definition in errno.h */
 #define EDESTADDRREQ            WSAEDESTADDRREQ
+#undef  EMSGSIZE         /* override definition in errno.h */
 #define EMSGSIZE                WSAEMSGSIZE
+#undef  EPROTOTYPE       /* override definition in errno.h */
 #define EPROTOTYPE              WSAEPROTOTYPE
+#undef  ENOPROTOOPT      /* override definition in errno.h */
 #define ENOPROTOOPT             WSAENOPROTOOPT
+#undef  EPROTONOSUPPORT  /* override definition in errno.h */
 #define EPROTONOSUPPORT         WSAEPROTONOSUPPORT
+#undef  ESOCKTNOSUPPORT  /* override definition in errno.h */
 #define ESOCKTNOSUPPORT         WSAESOCKTNOSUPPORT
+#undef  EOPNOTSUPP       /* override definition in errno.h */
 #define EOPNOTSUPP              WSAEOPNOTSUPP
+#undef  EPFNOSUPPORT     /* override definition in errno.h */
 #define EPFNOSUPPORT            WSAEPFNOSUPPORT
+#undef  EAFNOSUPPORT     /* override definition in errno.h */
 #define EAFNOSUPPORT            WSAEAFNOSUPPORT
+#undef  EADDRINUSE       /* override definition in errno.h */
 #define EADDRINUSE              WSAEADDRINUSE
+#undef  EADDRNOTAVAIL    /* override definition in errno.h */
 #define EADDRNOTAVAIL           WSAEADDRNOTAVAIL
+#undef  ENETDOWN         /* override definition in errno.h */
 #define ENETDOWN                WSAENETDOWN
+#undef  ENETUNREACH      /* override definition in errno.h */
 #define ENETUNREACH             WSAENETUNREACH
+#undef  ENETRESET        /* override definition in errno.h */
 #define ENETRESET               WSAENETRESET
+#undef  ECONNABORTED     /* override definition in errno.h */
 #define ECONNABORTED            WSAECONNABORTED
+#undef  ECONNRESET       /* override definition in errno.h */
 #define ECONNRESET              WSAECONNRESET
+#undef  ENOBUFS          /* override definition in errno.h */
 #define ENOBUFS                 WSAENOBUFS
+#undef  EISCONN          /* override definition in errno.h */
 #define EISCONN                 WSAEISCONN
+#undef  ENOTCONN         /* override definition in errno.h */
 #define ENOTCONN                WSAENOTCONN
+#undef  ESHUTDOWN        /* override definition in errno.h */
 #define ESHUTDOWN               WSAESHUTDOWN
+#undef  ETOOMANYREFS     /* override definition in errno.h */
 #define ETOOMANYREFS            WSAETOOMANYREFS
+#undef  ETIMEDOUT        /* override definition in errno.h */
 #define ETIMEDOUT               WSAETIMEDOUT
+#undef  ECONNREFUSED     /* override definition in errno.h */
 #define ECONNREFUSED            WSAECONNREFUSED
+#undef  ELOOP            /* override definition in errno.h */
 #define ELOOP                   WSAELOOP
-// #define ENAMETOOLONG            WSAENAMETOOLONG
+/* #define ENAMETOOLONG            WSAENAMETOOLONG */
 #define EHOSTDOWN               WSAEHOSTDOWN
+#undef  EHOSTUNREACH     /* override definition in errno.h */
 #define EHOSTUNREACH            WSAEHOSTUNREACH
-// #define ENOTEMPTY               WSAENOTEMPTY
+/* #define ENOTEMPTY               WSAENOTEMPTY */
 #define EPROCLIM                WSAEPROCLIM
 #define EUSERS                  WSAEUSERS
 #define EDQUOT                  WSAEDQUOT
 #define ESTALE                  WSAESTALE
 #define EREMOTE                 WSAEREMOTE
 
-// NOTE: these are not errno constants in UNIX!
+/* NOTE: these are not errno constants in UNIX! */
 #define HOST_NOT_FOUND          WSAHOST_NOT_FOUND
 #define TRY_AGAIN               WSATRY_AGAIN
 #define NO_RECOVERY             WSANO_RECOVERY
@@ -88,6 +120,9 @@ typedef int SSL_METHOD;
 int fd_timeout_read(int fd, char fdflag, void *, size_t);
 int ssl_timeout_read(SSL * ssl, void *, int);
 
+/* uses size_t instead of socket_t because socket_t not defined on some platforms */
+int timeout_connect (int sockfd, const struct sockaddr *serv_addr, size_t addrlen);
+
 /* these are fd-only, no SSL support */
 int full_read(int fd, char fdflag, void *buf, int min, int len);
 int full_read_ssl(SSL * ssl, unsigned char *buf, int min, int len);

commit aeee0bb656c377ef4f1dce8397e87d5fa148b601
Author: wwp <wwp at free.fr>
Date:   Wed Apr 11 09:40:05 2018 +0200

    Fix a leak, CID #1220437.

diff --git a/src/plugins/smime/smime.c b/src/plugins/smime/smime.c
index 7e3af5e..823ba37 100644
--- a/src/plugins/smime/smime.c
+++ b/src/plugins/smime/smime.c
@@ -290,8 +290,10 @@ static gint smime_check_signature(MimeInfo *mimeinfo)
 				decinfo = g_node_first_child(newinfo->node) != NULL ?
 					g_node_first_child(newinfo->node)->data : NULL;
 
-				if (decinfo == NULL)
+				if (decinfo == NULL) {
+					g_free(textstr);
 					return -1;
+				}
 
 				g_node_unlink(decinfo->node);
 				procmime_mimeinfo_free_all(&newinfo);

commit 1604a580535adbec6760ef29cce70cc80fe0cf39
Author: wwp <wwp at free.fr>
Date:   Wed Apr 11 09:32:53 2018 +0200

    Fix indentation, silents up CID #1434201.

diff --git a/src/editaddress.c b/src/editaddress.c
index bdaf536..921fadb 100644
--- a/src/editaddress.c
+++ b/src/editaddress.c
@@ -1939,8 +1939,8 @@ no_img:
 	model = gtk_tree_view_get_model(GTK_TREE_VIEW(personeditdlg.view_attrib));
 	if (gtk_tree_model_get_iter_first(model, &iter))
 		gtk_tree_selection_select_iter(sel, &iter);
-		edit_person_attrib_cursor_changed(
-				GTK_TREE_VIEW(personeditdlg.view_attrib), NULL);
+	edit_person_attrib_cursor_changed(
+			GTK_TREE_VIEW(personeditdlg.view_attrib), NULL);
 
 	edit_person_email_clear( NULL );
 	edit_person_attrib_clear( NULL );

commit 230d47a8a5f98eb3a6e44d98b5796a3fd8f5fac9
Author: wwp <wwp at free.fr>
Date:   Wed Apr 11 08:57:32 2018 +0200

    Fix wrong use of pointer-to-array as an array, CID #1434191.

diff --git a/src/plugins/clamd/libclamd/clamd-plugin.c b/src/plugins/clamd/libclamd/clamd-plugin.c
index d88b131..963cbca 100644
--- a/src/plugins/clamd/libclamd/clamd-plugin.c
+++ b/src/plugins/clamd/libclamd/clamd-plugin.c
@@ -499,7 +499,7 @@ static Clamd_Stat clamd_stream_scan(int sock,
 		*res = g_strconcat("ERROR -> ", _("Socket read error"), NULL);
 		return SCAN_ERROR;
 	}
-	res[n_read] = '\0';
+	(*res)[n_read] = '\0';
 	debug_print("received: %s\n", *res);
 	return OK;
 }

commit 45eaea6de4237e6aab2a2fc3b333a520ae4876d1
Author: wwp <wwp at free.fr>
Date:   Wed Apr 11 08:40:57 2018 +0200

    Fix buffer overrun, always writing at buffer size + 1.
    Fixes CID #1434188.

diff --git a/src/plugins/clamd/libclamd/clamd-plugin.c b/src/plugins/clamd/libclamd/clamd-plugin.c
index 314f626..d88b131 100644
--- a/src/plugins/clamd/libclamd/clamd-plugin.c
+++ b/src/plugins/clamd/libclamd/clamd-plugin.c
@@ -461,7 +461,7 @@ static Clamd_Stat clamd_stream_scan(int sock,
 		return NO_CONNECTION;
 	}
 
-	while ((count = read(fd, (void *) buf, sizeof(buf))) > 0) {
+	while ((count = read(fd, (void *) buf, BUFSIZ - 1)) > 0) {
 		buf[count] = '\0';
 		if (buf[count - 1] == '\n')
 			buf[count - 1] = '\0';
@@ -478,7 +478,7 @@ static Clamd_Stat clamd_stream_scan(int sock,
 			*res = g_strconcat("ERROR -> ", _("Socket write error"), NULL);
 			return SCAN_ERROR;
 		}
-		memset(buf, '\0', sizeof(buf));
+		memset(buf, '\0', BUFSIZ - 1);
 	}
 	if (count == -1) {
 		close(fd);

commit e95d423306c62cd3bfbf9abba895f968f8a67846
Author: wwp <wwp at free.fr>
Date:   Wed Apr 11 08:30:13 2018 +0200

    Fix CID #1434197, mismatch and unneeded display of unavailable folder class
    in warning.

diff --git a/src/plugins/vcalendar/vcal_manager.c b/src/plugins/vcalendar/vcal_manager.c
index 8b9a9b5..260ad83 100644
--- a/src/plugins/vcalendar/vcal_manager.c
+++ b/src/plugins/vcalendar/vcal_manager.c
@@ -1455,7 +1455,7 @@ static gboolean vcal_manager_send (PrefsAccount 	*account,
 		folder_item_scan(folder->inbox);
 		vcalviewer_reload(folder->inbox);
 	} else
-		g_warning("couldn't find vCalendar folder %s", vcal_folder_get_class());
+		g_warning("couldn't find vCalendar folder class");
 	return TRUE;
 }
 

-----------------------------------------------------------------------


hooks/post-receive
-- 
Claws Mail


More information about the Commits mailing list