Index: acl.c
===================================================================
RCS file: /milter-greylist/milter-greylist/acl.c,v
retrieving revision 1.113
diff -u -r1.113 acl.c
--- acl.c	16 Jun 2015 12:27:29 -0000	1.113
+++ acl.c	28 Sep 2015 18:55:48 -0000
@@ -2273,6 +2273,7 @@
 			mg_log(LOG_ERR, "strdup failed");
 			exit(EX_OSERR);
 		}
+		priv->priv_sr.sr_addheader_index = ap->ap_addheader_index;
 	}
 	if (ap->ap_addfooter) {
 		priv->priv_sr.sr_addfooter = strdup(ap->ap_addfooter);
@@ -2418,6 +2419,7 @@
 		ap.ap_msg = acl->a_msg;
 		ap.ap_report = acl->a_report;
 		ap.ap_addheader = acl->a_addheader;
+		ap.ap_addheader_index = acl->a_addheader_index;
 		ap.ap_addfooter = acl->a_addfooter;
 		ap.ap_subjtag = acl->a_subjtag;
 		ap.ap_maxpeek = acl->a_maxpeek;
@@ -2790,8 +2792,13 @@
 		mystrlcat(entrystr, tempstr, len);
 	}
 	if (acl->a_addheader) {
-		snprintf(tempstr, sizeof(tempstr), 
-		    "[addheader \"%s\"] ", acl->a_addheader);
+		if (acl->a_addheader_index >= 0)
+			snprintf(tempstr, sizeof(tempstr), 
+				"[addheader \"%s\" %d] ",
+				acl->a_addheader, acl->a_addheader_index);
+		else
+			snprintf(tempstr, sizeof(tempstr), 
+				"[addheader \"%s\"] ", acl->a_addheader);
 		mystrlcat(entrystr, tempstr, len);
 	}
 	if (acl->a_addfooter) {
@@ -3070,8 +3077,9 @@
 }
 
 void 
-acl_add_addheader(hdr)
+acl_add_addheader(hdr, index)
 	char *hdr;
+	int index;
 {
 	if (gacl->a_addheader) {
 		mg_log(LOG_ERR,
@@ -3084,9 +3092,11 @@
 		    "malloc failed in ACL line %d", conf_line);
 		exit(EX_OSERR);
 	}
+
+	gacl->a_addheader_index = index;
 		
 	if (conf.c_debug || conf.c_acldebug)
-		mg_log(LOG_DEBUG, "load acl addheader \"%s\"", hdr);
+		mg_log(LOG_DEBUG, "load acl addheader \"%s\" %d", hdr, index);
 
 	return;
 }
Index: acl.h
===================================================================
RCS file: /milter-greylist/milter-greylist/acl.h,v
retrieving revision 1.55
diff -u -r1.55 acl.h
--- acl.h	16 Jun 2015 12:27:29 -0000	1.55
+++ acl.h	28 Sep 2015 18:55:48 -0000
@@ -183,6 +183,7 @@
 	char *ap_msg;
 	char *ap_report;
 	char *ap_addheader;
+	int ap_addheader_index;
 	char *ap_addfooter;
 	char *ap_subjtag;
 	int ap_maxpeek;
@@ -290,6 +291,7 @@
 	char *a_msg;
 	char *a_report;
 	char *a_addheader;
+	int a_addheader_index;
 	char *a_addfooter;
 	char *a_subjtag;
 	int a_maxpeek;
@@ -317,7 +319,7 @@
 void acl_add_ecode(char *);
 void acl_add_msg(char *);
 void acl_add_report(char *);
-void acl_add_addheader(char *);
+void acl_add_addheader(char *, int);
 void acl_add_addfooter(char *);
 void acl_add_subjtag(char *);
 void acl_add_maxpeek(int);
@@ -407,9 +409,9 @@
 #define EXF_HELO	(1LL << 26)
 #define EXF_LDAPCHECK	(1LL << 27)
 #define EXF_NOLOG	(1LL << 28)
-#define	EXF_DKIM	(1LL << 28)
 #define EXF_P0F		(1LL << 29)
 #define EXF_SA		(1LL << 30)
 #define EXF_TARPIT	(1LL << 31)
 #define EXF_MX		(1LL << 32) /* Need long long (64b) */
+#define	EXF_DKIM	(1LL << 33)
 #endif /* _ACL_H_ */
Index: conf_yacc.y
===================================================================
RCS file: /milter-greylist/milter-greylist/conf_yacc.y,v
retrieving revision 1.126
diff -u -r1.126 conf_yacc.y
--- conf_yacc.y	16 Jun 2015 12:27:29 -0000	1.126
+++ conf_yacc.y	28 Sep 2015 18:55:48 -0000
@@ -1213,7 +1213,14 @@
 addheader_value:	ADDHEADER QSTRING {
 				char hdr[QSTRLEN + 1];
 
-				acl_add_addheader(quotepath(hdr, $2, QSTRLEN));
+				acl_add_addheader(
+					quotepath(hdr, $2, QSTRLEN), -1);
+			}
+	|	ADDHEADER QSTRING TNUMBER {
+				char hdr[QSTRLEN + 1];
+
+				acl_add_addheader(
+					quotepath(hdr, $2, QSTRLEN), atoi($3));
 			}
 	;
 addfooter_value:	ADDFOOTER QSTRING {
Index: dkimcheck.c
===================================================================
RCS file: /milter-greylist/milter-greylist/dkimcheck.c,v
retrieving revision 1.8
diff -u -r1.8 dkimcheck.c
--- dkimcheck.c	4 Oct 2013 08:03:42 -0000	1.8
+++ dkimcheck.c	28 Sep 2015 18:55:48 -0000
@@ -67,56 +67,6 @@
 #include "dkimcheck.h"
 
 static DKIM_LIB *dkim_ptr = NULL;
-static sfsistat dkimcheck_error(struct mlfi_priv *);
-
-static sfsistat
-dkimcheck_error(priv)
-	struct mlfi_priv *priv;
-{
-	sfsistat retval;
-
-	switch (priv->priv_dkimstat) {
-	case DKIM_STAT_OK:
-		retval = SMFIS_CONTINUE;
-		break;
-
-	case DKIM_STAT_NOSIG: 
-		mg_log(LOG_DEBUG, "DKIM failed: %s",
-		       dkim_getresultstr(priv->priv_dkimstat));
-
-		retval = SMFIS_CONTINUE;
-		break;
-
-	case DKIM_STAT_KEYFAIL:
-	case DKIM_STAT_CBTRYAGAIN:
-		mg_log(LOG_WARNING, "DKIM failed: %s",
-		       dkim_getresultstr(priv->priv_dkimstat));
-
-		retval = SMFIS_TEMPFAIL;
-		break;
-
-	case DKIM_STAT_INTERNAL:
-		mg_log(LOG_WARNING, "DKIM failed: %s",
-		       dkim_getresultstr(priv->priv_dkimstat));
-
-		exit(EX_OSERR);
-		break;
-
-	default:
-		mg_log(LOG_ERR, "DKIM failed: %s",
-		       dkim_getresultstr(priv->priv_dkimstat));
-
-		retval = SMFIS_CONTINUE;
-		break;
-	}
-
-	if (priv->priv_dkimstat != DKIM_STAT_OK) {
-		(void)dkim_free(priv->priv_dkim);
-		priv->priv_dkim = NULL;
-	}
-
-	return retval;
-}
 
 void
 dkimcheck_init(void)
@@ -232,18 +182,16 @@
 	priv->priv_dkimstat = dkim_header(priv->priv_dkim, header, len);
 	free(header);
 
-	return dkimcheck_error(priv);
+	return SMFIS_CONTINUE;
 }
 
 sfsistat
 dkimcheck_eoh(priv)
 	struct mlfi_priv *priv;
 {
-	if (priv->priv_dkim == NULL)
-		return SMFIS_CONTINUE;
-
-	priv->priv_dkimstat = dkim_eoh(priv->priv_dkim);
-	return dkimcheck_error(priv);
+	if ((priv->priv_dkim != NULL) && (priv->priv_dkimstat == DKIM_STAT_OK))
+        priv->priv_dkimstat = dkim_eoh(priv->priv_dkim);
+	return SMFIS_CONTINUE;
 }
 
 sfsistat
@@ -252,11 +200,9 @@
 	size_t size;
 	struct mlfi_priv *priv;
 {
-	if (priv->priv_dkim == NULL)
-		return SMFIS_CONTINUE;
-
-	priv->priv_dkimstat = dkim_body(priv->priv_dkim, chunk, size);
-	return dkimcheck_error(priv);
+	if ((priv->priv_dkim != NULL) && (priv->priv_dkimstat == DKIM_STAT_OK))
+        priv->priv_dkimstat = dkim_body(priv->priv_dkim, chunk, size);
+	return SMFIS_CONTINUE;
 }
 
 sfsistat
@@ -265,11 +211,9 @@
 {
 	bool testkey;
 
-	if (priv->priv_dkim == NULL)
-		return SMFIS_CONTINUE;
-
-	priv->priv_dkimstat = dkim_eom(priv->priv_dkim, &testkey);
-	return dkimcheck_error(priv);
+	if ((priv->priv_dkim != NULL) && (priv->priv_dkimstat == DKIM_STAT_OK)) 
+        priv->priv_dkimstat = dkim_eom(priv->priv_dkim, &testkey);
+	return SMFIS_CONTINUE;
 }
 
 int
@@ -280,53 +224,52 @@
 	struct mlfi_priv *priv;
 {
 	enum spf_status stat;
-	int result;
 
 	if (stage != AS_DATA) {
 		mg_log(LOG_ERR, "dkim clause called at non DATA stage");
 		exit(EX_SOFTWARE);
 	}
 
-	stat = ad ? *(enum spf_status *)ad : MGSPF_PASS;
+	switch (priv->priv_dkimstat) {
+	case DKIM_STAT_OK:
+		strcpy(priv->priv_dkim_result, "pass");
+		stat = MGSPF_PASS;
+		break;
 
-	switch (stat) {
-	case MGSPF_PASS:
-		result = (priv->priv_dkimstat == DKIM_STAT_OK);
+	case DKIM_STAT_BADSIG:
+	case DKIM_STAT_REVOKED:
+	case DKIM_STAT_CBREJECT:
+		strcpy(priv->priv_dkim_result, "fail");
+		stat = MGSPF_FAIL;
 		break;
 
-	case MGSPF_FAIL:
-		switch (priv->priv_dkimstat) {
-		case DKIM_STAT_BADSIG:
-		case DKIM_STAT_NOKEY:
-		case DKIM_STAT_REVOKED:
-		case DKIM_STAT_CBREJECT:
-			result = 1;
-			break;
-		default:
-			result = 0;
-		}
+	case DKIM_STAT_NOKEY:
+	case DKIM_STAT_SYNTAX:
+	case DKIM_STAT_INVALID:
+	case DKIM_STAT_NOTIMPLEMENT:
+	case DKIM_STAT_CBERROR:
+	case DKIM_STAT_CBINVALID:
+		strcpy(priv->priv_dkim_result, "permerror");
+		stat = MGSPF_ERROR;
 		break;
 
-	case MGSPF_ERROR:
-		switch (priv->priv_dkimstat) {
-		case DKIM_STAT_SYNTAX:
-		case DKIM_STAT_INVALID:
-		case DKIM_STAT_NOTIMPLEMENT:
-		case DKIM_STAT_CBERROR:
-		case DKIM_STAT_MULTIDNSREPLY:
-			result = 1;
-			break;
-		default:
-			result = 0;
-		}
+	case DKIM_STAT_NORESOURCE:
+	case DKIM_STAT_INTERNAL:
+	case DKIM_STAT_CBTRYAGAIN:
+	case DKIM_STAT_KEYFAIL:
+	case DKIM_STAT_MULTIDNSREPLY:
+		strcpy(priv->priv_dkim_result, "temperror");
+		stat = MGSPF_ERROR;
 		break;
 
-	case MGSPF_NONE:
-		result = (priv->priv_dkimstat == DKIM_STAT_NOSIG);
+	case DKIM_STAT_NOSIG:
+		strcpy(priv->priv_dkim_result, "none");
+		stat = MGSPF_NONE;
 		break;
 
-	case MGSPF_UNKNOWN:
-		result = (priv->priv_dkimstat == DKIM_STAT_CANTVRFY);
+	case DKIM_STAT_CANTVRFY:
+		strcpy(priv->priv_dkim_result, "neutral");
+		stat = MGSPF_UNKNOWN;
 		break;
 
 	default:
@@ -335,7 +278,7 @@
 		break;
 	}
 	
-	return result;
+	return (stat == ad ? *(enum spf_status *)ad : MGSPF_PASS);
 }
 
 char *
Index: greylist.conf.5
===================================================================
RCS file: /milter-greylist/milter-greylist/greylist.conf.5,v
retrieving revision 1.127
diff -u -r1.127 greylist.conf.5
--- greylist.conf.5	16 Jun 2015 12:27:29 -0000	1.127
+++ greylist.conf.5	28 Sep 2015 18:55:48 -0000
@@ -389,7 +389,10 @@
 This quoted string is a RFC822 header that gets added to the message. 
 Format string substitution is supported. No check is done for header
 length standard compliance, so make sure the substituted string is
-shorter than 2048 characters.
+shorter than 2048 characters. Headers are appended to the bottom,
+unless an optional index integer (>=0) is specified after the quoted
+string; in this case the header will be inserted at this index point
+from the top.
 .TP
 .I addfooter
 Append a footer to the message. Usual escape sequences such as \\n can
@@ -1509,6 +1512,20 @@
 .I %P{mail}
 Note that this copes very badly with multivalued properties.
 .TP
+.I %Qd
+DKIM query result (pass, fail, etc.), if built with DKIM support.
+.TP
+.I %Qs
+SPF query result (pass, (soft)fail, etc.), if built with SPF support.
+.TP
+.I %QA
+Authentication results summary, suitable for Authentication-Results
+header. Will included formatted query results from DKIM and SPF if checked.
+.TP
+.I %QS
+SPF result, suitable for Received-SPF header. Available when
+compiled with libspf2.
+.TP
 .I %%
 a single % character
 .PP
Index: milter-greylist.c
===================================================================
RCS file: /milter-greylist/milter-greylist/milter-greylist.c,v
retrieving revision 1.277
diff -u -r1.277 milter-greylist.c
--- milter-greylist.c	26 Apr 2014 15:03:33 -0000	1.277
+++ milter-greylist.c	28 Sep 2015 18:55:49 -0000
@@ -424,9 +424,17 @@
 #ifdef USE_GEOIP
 	geoip_set_ccode(priv);
 #endif
+#ifdef HAVE_SPF2
+	priv->priv_spf_header = NULL;
+#endif
+#if (defined(HAVE_SPF) || defined(HAVE_SPF_ALT) || \
+     defined(HAVE_SPF2_10) || defined(HAVE_SPF2))
+	priv->priv_spf_result[0] = '\0';
+#endif
 #ifdef USE_DKIM
 	priv->priv_dkim = NULL;
 	priv->priv_dkimstat = DKIM_STAT_OK;
+	priv->priv_dkim_result[0] = '\0';
 #endif
 #ifdef USE_P0F
 	priv->priv_p0f = NULL;
@@ -1247,7 +1255,12 @@
 		} else {
 			*hdrvalue = '\0';
 			hdrvalue += strlen(sep);
-			smfi_addheader(ctx, hdrname, hdrvalue);
+			if (priv->priv_sr.sr_addheader_index >= 0)
+				smfi_insheader(
+					ctx, priv->priv_sr.sr_addheader_index,
+					hdrname, hdrvalue);
+			else
+				smfi_addheader(ctx, hdrname, hdrvalue);
 		}
 
 		free(hdrname);
@@ -1386,6 +1399,10 @@
 				ADD_REASON(whystr, "Sender passed SPF test");
 				priv->priv_last_whitelist &= ~EXF_SPF;
 			}
+			if (priv->priv_last_whitelist & EXF_DKIM) {
+				ADD_REASON(whystr, "Sender passed DKIM test");
+				priv->priv_last_whitelist &= ~EXF_DKIM;
+			}
 			if (priv->priv_last_whitelist & EXF_NONIP) {
 #ifdef AF_INET6
 				ADD_REASON(whystr, 
@@ -1486,7 +1503,12 @@
 		} else {
 			*hdrvalue = '\0';
 			hdrvalue += strlen(sep);
-			smfi_addheader(ctx, hdrname, hdrvalue);
+			if (priv->priv_sr.sr_addheader_index >= 0)
+				smfi_insheader(
+					ctx, priv->priv_sr.sr_addheader_index,
+					hdrname, hdrvalue);
+			else
+				smfi_addheader(ctx, hdrname, hdrvalue);
 		}
 
 		free(hdrname);
@@ -1556,6 +1578,10 @@
 		if (priv->priv_p0f != NULL)
 			free(priv->priv_p0f);
 #endif
+#ifdef HAVE_SPF2
+		if (priv->priv_spf_header != NULL)
+			free(priv->priv_spf_header);
+#endif
 #ifdef USE_DKIM
 		dkimcheck_free(priv);
 #endif
@@ -3120,7 +3146,104 @@
 			mystrncat(&outstr, tzstr, &outmaxlen, cv);
 			break;
 		}
-		
+		case 'Q': { /* "Query" results */
+			fstr_len =  2;
+			switch(*(ptok + 1)) {
+			case 's': /* SPF result */
+#if (defined(HAVE_SPF) || defined(HAVE_SPF_ALT) || \
+     defined(HAVE_SPF2_10) || defined(HAVE_SPF2))
+				if (priv->priv_spf_result[0])
+					mystrncat(&outstr, 
+					  	priv->priv_spf_result,
+					  	&outmaxlen, cv);
+#else
+				fstr_len = 0;
+#endif
+				break;
+			case 'd': /* DKIM result */
+#ifdef USE_DKIM
+				if (priv->priv_dkim_result[0])
+					mystrncat(&outstr, 
+					  	priv->priv_dkim_result,
+					  	&outmaxlen, cv);
+#else
+				fstr_len = 0;
+#endif
+				break;
+			case 'A': { /* Authentication-Results header value */
+				char host[ADDRLEN + 1];
+				char output[HDRLEN + 1];
+				char *fqdn;
+				int cx;
+
+				fqdn = smfi_getsymval(priv->priv_ctx, "{j}");
+				if (fqdn == NULL) {
+					mg_log(LOG_DEBUG, 
+					  "smfi_getsymval failed for {j}");
+					gethostname(host, ADDRLEN);
+					fqdn = host;
+				}
+
+				cx = snprintf(output, HDRLEN, "%s", fqdn);
+
+#if (defined(HAVE_SPF) || defined(HAVE_SPF_ALT) || \
+     defined(HAVE_SPF2_10) || defined(HAVE_SPF2))
+				if ((cx < HDRLEN) && priv->priv_spf_result[0]) {
+					char tmpfrom[
+						strlen(priv->priv_from) - 1];
+
+					memcpy(tmpfrom,
+						priv->priv_from + 1,
+						sizeof(tmpfrom) - 1);
+					tmpfrom[sizeof(tmpfrom) - 1] = '\0';
+
+					cx += snprintf(output+cx, HDRLEN-cx,
+					      ";\r\n\tspf=%s smtp.mailfrom=%s",
+					      priv->priv_spf_result, tmpfrom);
+				}
+#endif
+#ifdef USE_DKIM
+				if ((cx < HDRLEN) &&
+					priv->priv_dkim_result[0]) {
+					DKIM_SIGINFO *sig=NULL;
+					unsigned const char *domain;
+					cx += snprintf(output+cx, HDRLEN-cx,
+						";\r\n\tdkim=%s",
+						priv->priv_dkim_result);
+
+					if (priv->priv_dkim != NULL)
+						sig = dkim_getsignature(
+							priv->priv_dkim);
+					if ((cx < HDRLEN) && (sig != NULL) &&
+					    ((domain = dkim_sig_getdomain(sig))
+					     != NULL))
+						cx += snprintf(output+cx,
+						HDRLEN-cx, " header.d=%s",
+						domain);
+				}
+#endif
+				if ((cx < HDRLEN) && (cx == strlen(fqdn)))
+					snprintf(output+cx, HDRLEN-cx,
+						"; none");
+				mystrncat(&outstr, output, &outmaxlen, cv);
+				break;
+			}
+			case 'S': /* Received-SPF header value */
+#ifdef HAVE_SPF2
+				if (priv->priv_spf_header != NULL)
+					mystrncat(&outstr, 
+					  	priv->priv_spf_header,
+					  	&outmaxlen, cv);
+#else
+				fstr_len = 0;
+#endif
+				break;
+			default:
+				fstr_len = 0;
+				break;
+			}
+			break;
+		}
 		case 'C': {	/* Country code from GeoIP */
 #ifdef USE_GEOIP
 			if (priv->priv_ccode != NULL)
Index: milter-greylist.h
===================================================================
RCS file: /milter-greylist/milter-greylist/milter-greylist.h,v
retrieving revision 1.97
diff -u -r1.97 milter-greylist.h
--- milter-greylist.h	17 Dec 2014 08:36:43 -0000	1.97
+++ milter-greylist.h	28 Sep 2015 18:55:49 -0000
@@ -168,6 +168,7 @@
 	char *sr_report;
 	char *sr_report_x;
 	char *sr_addheader;
+	int sr_addheader_index;
 	char *sr_addfooter;
 	char *sr_subjtag;
 	sfsistat sr_retcode;
@@ -219,9 +220,17 @@
 #ifdef USE_DNSRBL
 	LIST_HEAD(, dnsrbl_list) priv_dnsrbl;
 #endif
+#ifdef HAVE_SPF2
+	char *priv_spf_header;
+#endif
+#if (defined(HAVE_SPF) || defined(HAVE_SPF_ALT) || \
+     defined(HAVE_SPF2_10) || defined(HAVE_SPF2))
+	char priv_spf_result[10];
+#endif
 #ifdef USE_DKIM
 	DKIM *priv_dkim;
 	DKIM_STAT priv_dkimstat;
+	char priv_dkim_result[10];
 #endif
 #ifdef USE_P0F
 	char *priv_p0f;
Index: spf.c
===================================================================
RCS file: /milter-greylist/milter-greylist/spf.c,v
retrieving revision 1.38
diff -u -r1.38 spf.c
--- spf.c	4 Oct 2013 08:01:10 -0000	1.38
+++ spf.c	28 Sep 2015 18:55:49 -0000
@@ -96,7 +96,6 @@
 	int result = 0;
 	struct timeval tv1, tv2, tv3;
 	enum spf_status status;
-	char *statestr = "";
 
 	if (conf.c_debug)
 		gettimeofday(&tv1, NULL);
@@ -116,45 +115,46 @@
 	p->RES = SPF_policy_main(p);
 
 	status = ad ? *(enum spf_status *)ad : MGSPF_PASS;
-	switch (status) {
-	case MGSPF_PASS:
-		result = (p->RES == SPF_PASS);
-		statestr = "pass";
-		break;
-	case MGSPF_FAIL:
-		result = (p->RES == SPF_H_FAIL);
-		statestr = "fail";
-		break;
-	case MGSPF_SOFTFAIL:
-		result = (p->RES == SPF_S_FAIL);
-		statestr = "softfail";
-		break;
-	case MGSPF_NEUTRAL:
-		result = (p->RES == SPF_NEUTRAL);
-		statestr = "neutral";
-		break;
-	case MGSPF_UNKNOWN:
-		result = (p->RES == SPF_UNKNOWN || p->RES == SPF_UNMECH);
-		statestr = "unknown";
-		break;
-	case MGSPF_ERROR:
-		result = (p->RES == SPF_ERROR);
-		statestr = "error";
-		break;
-	case MGSPF_NONE:
-		result = (p->RES == SPF_NONE);
-		statestr = "none";
+	switch (p->RES) {
+	case SPF_PASS:
+		result = (status == MGSPF_PASS);
+		strcpy(priv->priv_spf_result, "pass");
+		break;
+	case SPF_H_FAIL:
+		result = (status == MGSPF_FAIL);
+		strcpy(priv->priv_spf_result, "fail");
+		break;
+	case SPF_S_FAIL:
+		result = (status == MGSPF_SOFTFAIL);
+		strcpy(priv->priv_spf_result, "softfail");
+		break;
+	case SPF_NEUTRAL:
+		result = (status == MGSPF_NEUTRAL);
+		strcpy(priv->priv_spf_result, "neutral");
+		break;
+	case SPF_UNKNOWN:
+	case SPF_UNMECH:
+		result = (status == MGSPF_UNKNOWN);
+		strcpy(priv->priv_spf_result, "unknown");
+		break;
+	case SPF_ERROR:
+		result = (status == MGSPF_ERROR);
+		strcpy(priv->priv_spf_result, "error");
+		break;
+	case SPF_NONE:
+		result = (status == MGSPF_NONE);
+		strcpy(priv->priv_spf_result, "none");
 		break;
 	default:
-		mg_log(LOG_ERR, "Internal error: unexpected spf_status %d",
-			" spf raw result %d", status, p->RES);
+		mg_log(LOG_ERR, "Internal error: unexpected spf result %d",
+			" spf test code %d", p->RES, status);
 		exit(EX_SOFTWARE);
 		break;
 	}
 
 	if (conf.c_debug)
-		mg_log(LOG_DEBUG, "SPF return code %d (test %s, result %d)",
-			p->RES, statestr, result);
+		mg_log(LOG_DEBUG, "SPF return %s (test code %d, result %d)",
+			priv->priv_spf_result, status, result);
 
 	SPF_close(p);
 
@@ -211,7 +211,6 @@
 	struct timeval tv1, tv2, tv3;
 	size_t len;
 	enum spf_status status;
-	char *statestr = "";
 
 	if (conf.c_debug)
 		gettimeofday(&tv1, NULL);
@@ -271,45 +270,46 @@
 #endif
 
 	status = ad ? *(enum spf_status *)ad : MGSPF_PASS;
-	switch (status) {
-	case MGSPF_PASS:
-		result = (out.result == SPF_RESULT_PASS);
-		statestr = "pass";
-		break;
-	case MGSPF_FAIL:
-		result = (out.result == SPF_RESULT_FAIL);
-		statestr = "fail";
-		break;
-	case MGSPF_SOFTFAIL:
-		result = (out.result == SPF_RESULT_SOFTFAIL);
-		statestr = "softfail";
-		break;
-	case MGSPF_NEUTRAL:
-		result = (out.result == SPF_RESULT_NEUTRAL);
-		statestr = "neutral";
-		break;
-	case MGSPF_UNKNOWN:
-		result = (out.result == SPF_RESULT_UNKNOWN);
-		statestr = "unknown";
-		break;
-	case MGSPF_ERROR:
-		result = (out.result == SPF_RESULT_ERROR);
-		statestr = "error";
-		break;
-	case MGSPF_NONE:
-		result = (out.result == SPF_RESULT_NONE);
-		statestr = "none";
+	switch (out.result) {
+	case SPF_RESULT_PASS:
+		result = (status == MGSPF_PASS);
+		strcpy(priv->priv_spf_result, "pass");
+		break;
+	case SPF_RESULT_FAIL:
+		result = (status == MGSPF_FAIL);
+		strcpy(priv->priv_spf_result, "fail");
+		break;
+	case SPF_RESULT_SOFTFAIL:
+		result = (status == MGSPF_SOFTFAIL);
+		strcpy(priv->priv_spf_result, "softfail");
+		break;
+	case SPF_RESULT_NEUTRAL:
+		result = (status == MGSPF_NEUTRAL);
+		strcpy(priv->priv_spf_result, "neutral");
+		break;
+	case SPF_RESULT_UNKNOWN:
+		result = (status == MGSPF_UNKNOWN);
+		strcpy(priv->priv_spf_result, "unknown");
+		break;
+	case SPF_RESULT_ERROR:
+		result = (status == MGSPF_ERROR);
+		strcpy(priv->priv_spf_result, "error");
+		break;
+	case SPF_RESULT_NONE:
+		result = (status == MGSPF_NONE);
+		strcpy(priv->priv_spf_result, "none");
 		break;
 	default:
-		mg_log(LOG_ERR, "Internal error: unexpected spf_status %d",
-			" spf_alt/spf_2_10 raw result %d", status, out.result);
+		mg_log(LOG_ERR,
+			"Internal error: unexpected spf_alt/spf_2_10 result %d",
+			" spf test code %d", out.result, status);
 		exit(EX_SOFTWARE);
 		break;
 	}
 
 	if (conf.c_debug)
-		mg_log(LOG_DEBUG, "SPF return code %d (test %s, result %d)",
-			out.result, statestr, result);
+		mg_log(LOG_DEBUG, "SPF return %s (test code %d, result %d)",
+			priv->priv_spf_result, out.result, result);
 
 	SPF_free_output(&out);
 out3:
@@ -340,6 +340,7 @@
 	struct mlfi_priv *priv;
 {
 	struct sockaddr *sa = SA(&priv->priv_addr);
+	char *fqdn;
 	char *helo = priv->priv_helo;
 	char *fromp = priv->priv_from;
 	SPF_server_t *spf_server;
@@ -350,7 +351,6 @@
 	struct timeval tv1, tv2, tv3;
 	size_t len;
 	enum spf_status status;
-	char *statestr = "";
 
 	if (conf.c_debug)
 		gettimeofday(&tv1, NULL);
@@ -365,6 +365,11 @@
 		goto out2;
 	}
 
+	/* Local machine FQDN */
+	fqdn = smfi_getsymval(priv->priv_ctx, "{j}");
+	if (fqdn != NULL && SPF_server_set_rec_dom(spf_server, fqdn) != 0)
+		mg_log(LOG_WARNING, "SPF_server_set_rec_dom failed");
+
 	/*
 	 * Get the IP address
 	 */
@@ -415,46 +420,53 @@
 	res = SPF_response_result(spf_response);
 
 	status = ad ? *(enum spf_status *)ad : MGSPF_PASS;
-	switch (status) {
-	case MGSPF_PASS:
-		result = (res == SPF_RESULT_PASS);
-		statestr = "pass";
-		break;
-	case MGSPF_FAIL:
-		result = (res == SPF_RESULT_FAIL);
-		statestr = "fail";
-		break;
-	case MGSPF_SOFTFAIL:
-		result = (res == SPF_RESULT_SOFTFAIL);
-		statestr = "softfail";
-		break;
-	case MGSPF_NEUTRAL:
-		result = (res == SPF_RESULT_NEUTRAL);
-		statestr = "neutral";
-		break;
-	case MGSPF_UNKNOWN:
-		result = 
-		    (res == SPF_RESULT_PERMERROR || res == SPF_RESULT_INVALID);
-		statestr = "unknown";
-		break;
-	case MGSPF_ERROR:
-		result = (res == SPF_RESULT_TEMPERROR);
-		statestr = "error";
-		break;
-	case MGSPF_NONE:
-		result = (res == SPF_RESULT_NONE);
-		statestr = "none";
+	switch (res) {
+	case SPF_RESULT_PASS:
+		result = (status == MGSPF_PASS);
+		strcpy(priv->priv_spf_result, "pass");
+		break;
+	case SPF_RESULT_FAIL:
+		result = (status == MGSPF_FAIL);
+		strcpy(priv->priv_spf_result, "fail");
+		break;
+	case SPF_RESULT_SOFTFAIL:
+		result = (status == MGSPF_SOFTFAIL);
+		strcpy(priv->priv_spf_result, "softfail");
+		break;
+	case SPF_RESULT_NEUTRAL:
+		result = (status == MGSPF_NEUTRAL);
+		strcpy(priv->priv_spf_result, "neutral");
+		break;
+	case SPF_RESULT_INVALID:
+		result = (status == MGSPF_UNKNOWN);
+		strcpy(priv->priv_spf_result, "unknown");
+		break;
+	case SPF_RESULT_PERMERROR:
+		result = (status == MGSPF_ERROR);
+		strcpy(priv->priv_spf_result, "permerror");
+		break;
+	case SPF_RESULT_TEMPERROR:
+		result = (status == MGSPF_ERROR);
+		strcpy(priv->priv_spf_result, "temperror");
+		break;
+	case SPF_RESULT_NONE:
+		result = (status == MGSPF_NONE);
+		strcpy(priv->priv_spf_result, "none");
 		break;
 	default:
-		mg_log(LOG_ERR, "Internal error: unexpected spf_status %d",
-			" spf2 raw result %d", status, res);
+		mg_log(LOG_ERR, "Internal error: unexpected spf2 result %d",
+			" spf test code %d", res, status);
 		exit(EX_SOFTWARE);
 		break;
 	}
 
 	if (conf.c_debug)
-		mg_log(LOG_DEBUG, "SPF return code %d (test %s, result %d)",
-			res, statestr, result);
+		mg_log(LOG_DEBUG, "SPF return %s (test code %d, result %d)",
+			priv->priv_spf_result, res, result);
+
+	if (priv->priv_spf_header != NULL)
+		free(priv->priv_spf_header);
+	priv->priv_spf_header = strdup(spf_response->received_spf_value);
 
 	SPF_response_free(spf_response);
 out3:
