# File: libspf-1.0.0/patches/Postfix/2.1.x/postfix-2.1.4-libspf-1.0.0-RC6.diff
# Date: 09/15/04
# Desc: Patch for libSPF v1.0 against Postfix v2.1.4
# 
# Patch coder: Travis Anderson <tanderson@codeshare.ca>
# Diff author: James Couzens <jcouzens@codeshare.ca>
#
# Contents: 
#
# diff -rpN -C4 postfix-2.1.4/include/mail_params.h postfix-2.1.4.libspf/include/mail_params.h >> postfix-2.1.4-libspf-1.0-RC6.diff
# diff -rpN -C4 postfix-2.1.4/src/global/mail_params.h postfix-2.1.4.libspf/src/global/mail_params.h >> postfix-2.1.4-libspf-1.0-RC6.diff
# diff -rpN -C4 postfix-2.1.4/src/smtpd/smtpd.c postfix-2.1.4.libspf/src/smtpd/smtpd.c >> postfix-2.1.4-libspf-1.0-RC6.diff
# diff -rpN -C4 postfix-2.1.4/src/smtpd/smtpd_check.c postfix-2.1.4.libspf/src/smtpd/smtpd_check.c >> postfix-2.1.4-libspf-1.0-RC6.diff
# diff -rpN -C4 postfix-2.1.4/src/smtpd/smtpd_state.c postfix-2.1.4.libspf/src/smtpd/smtpd_state.c >> postfix-2.1.4-libspf-1.0-RC6.diff
# diff -rpN -C4 postfix-2.1.4/src/smtpd/Makefile postfix-2.1.4.libspf/src/smtpd/Makefile >> postfix-2.1.4-libspf-1.0-RC6.diff
# diff -rpN -C4 postfix-2.1.4/src/smtpd/smtpd.h postfix-2.1.4.libspf/src/smtpd/smtpd.h >> postfix-2.1.4-libspf-1.0-RC6.diff
# diff -rpN -C4 postfix-2.1.4/conf/spf.cf.example postfix-2.1.4.libspf/conf/spf.cf.example >> postfix-2.1.4-libspf-1.0-RC6.diff

*** postfix-2.1.4/include/mail_params.h	Wed Sep 15 08:52:07 2004
--- postfix-2.1.4.libspf/include/mail_params.h	Wed Sep 15 08:33:42 2004
*************** extern char *var_syslog_facility;
*** 274,282 ****
  extern char *var_always_bcc;
  
   /*
    * What to put in the To: header when no recipients were disclosed.
!   * 
    * XXX 2822: When no recipient headers remain, a system should insert a Bcc:
    * header without additional information. That is not so great given that
    * MTAs routinely strip Bcc: headers from message headers.
    */
--- 274,282 ----
  extern char *var_always_bcc;
  
   /*
    * What to put in the To: header when no recipients were disclosed.
!   *
    * XXX 2822: When no recipient headers remain, a system should insert a Bcc:
    * header without additional information. That is not so great given that
    * MTAs routinely strip Bcc: headers from message headers.
    */
*************** extern int var_hash_queue_depth;
*** 747,755 ****
    * SMTP client. Timeouts inspired by RFC 1123. The SMTP recipient limit
    * determines how many recipient addresses the SMTP client sends along with
    * each message. Unfortunately, some mailers misbehave and disconnect (smap)
    * when given more recipients than they are willing to handle.
!   * 
    * XXX 2821: A mail system is supposed to use EHLO instead of HELO, and to fall
    * back to HELO if EHLO is not supported.
    */
  #define VAR_BESTMX_TRANSP	"best_mx_transport"
--- 747,755 ----
    * SMTP client. Timeouts inspired by RFC 1123. The SMTP recipient limit
    * determines how many recipient addresses the SMTP client sends along with
    * each message. Unfortunately, some mailers misbehave and disconnect (smap)
    * when given more recipients than they are willing to handle.
!   *
    * XXX 2821: A mail system is supposed to use EHLO instead of HELO, and to fall
    * back to HELO if EHLO is not supported.
    */
  #define VAR_BESTMX_TRANSP	"best_mx_transport"
*************** extern char *var_smtp_sasl_passwd;
*** 944,951 ****
--- 944,988 ----
  #define VAR_SMTP_SASL_OPTS	"smtp_sasl_security_options"
  #define DEF_SMTP_SASL_OPTS	"noplaintext, noanonymous"
  extern char *var_smtp_sasl_opts;
  
+ /*
+  * LIBSPF support
+  */
+ #define REJECT_SPF                              "reject_spf"
+ 
+ #define VAR_SPF_ACTION                          "spf_action"
+ #define DEF_SPF_ACTION                          1
+ extern int var_spf_action;
+ 
+ #define VAR_SPF_DO_PREPEND_HEADER               "spf_do_prepend_header"
+ #define DEF_SPF_DO_PREPEND_HEADER               1
+ extern bool var_spf_do_prepend_header;
+ 
+ #define VAR_SPF_DO_EXPLAIN                      "spf_do_explain"
+ #define DEF_SPF_DO_EXPLAIN                      0
+ extern bool var_spf_do_explain;
+ 
+ #define VAR_SPF_DO_USE_BEST_GUESS_RECORD        "spf_do_use_best_guess_record"
+ #define DEF_SPF_DO_USE_BEST_GUESS_RECORD        0
+ extern bool var_spf_do_use_best_guess_record;
+ 
+ #define VAR_SPF_DO_USE_TRUSTED_FORWARDER        "spf_do_use_trusted_forwarder"
+ #define DEF_SPF_DO_USE_TRUSTED_FORWARDER        0
+ extern bool var_spf_do_use_trusted_forwarder;
+ 
+ #define VAR_SPF_CUSTOM_EXPLANATION              "spf_custom_explanation"
+ #define DEF_SPF_CUSTOM_EXPLANATION              ""
+ extern char *var_spf_custom_explanation;
+ 
+ #define VAR_SPF_CUSTOM_BEST_GUESS_RECORD        "spf_custom_best_guess_record"
+ #define DEF_SPF_CUSTOM_BEST_GUESS_RECORD        ""
+ extern char *var_spf_custom_best_guess_record;
+ 
+ #define VAR_SPF_CUSTOM_TRUSTED_FORWARDER        "spf_custom_trusted_forwarder"
+ #define DEF_SPF_CUSTOM_TRUSTED_FORWARDER        ""
+ extern char *var_spf_custom_trusted_forwarder;
+ 
   /*
    * LMTP server. The soft error limit determines how many errors an LMTP
    * client may make before we start to slow down; the hard error limit
    * determines after how many client errors we disconnect.
*** postfix-2.1.4/src/global/mail_params.h	Wed Apr 21 11:56:04 2004
--- postfix-2.1.4.libspf/src/global/mail_params.h	Wed Sep 15 08:19:42 2004
*************** extern char *var_syslog_facility;
*** 274,282 ****
  extern char *var_always_bcc;
  
   /*
    * What to put in the To: header when no recipients were disclosed.
!   * 
    * XXX 2822: When no recipient headers remain, a system should insert a Bcc:
    * header without additional information. That is not so great given that
    * MTAs routinely strip Bcc: headers from message headers.
    */
--- 274,282 ----
  extern char *var_always_bcc;
  
   /*
    * What to put in the To: header when no recipients were disclosed.
!   *
    * XXX 2822: When no recipient headers remain, a system should insert a Bcc:
    * header without additional information. That is not so great given that
    * MTAs routinely strip Bcc: headers from message headers.
    */
*************** extern int var_hash_queue_depth;
*** 747,755 ****
    * SMTP client. Timeouts inspired by RFC 1123. The SMTP recipient limit
    * determines how many recipient addresses the SMTP client sends along with
    * each message. Unfortunately, some mailers misbehave and disconnect (smap)
    * when given more recipients than they are willing to handle.
!   * 
    * XXX 2821: A mail system is supposed to use EHLO instead of HELO, and to fall
    * back to HELO if EHLO is not supported.
    */
  #define VAR_BESTMX_TRANSP	"best_mx_transport"
--- 747,755 ----
    * SMTP client. Timeouts inspired by RFC 1123. The SMTP recipient limit
    * determines how many recipient addresses the SMTP client sends along with
    * each message. Unfortunately, some mailers misbehave and disconnect (smap)
    * when given more recipients than they are willing to handle.
!   *
    * XXX 2821: A mail system is supposed to use EHLO instead of HELO, and to fall
    * back to HELO if EHLO is not supported.
    */
  #define VAR_BESTMX_TRANSP	"best_mx_transport"
*************** extern char *var_smtp_sasl_passwd;
*** 944,951 ****
--- 944,988 ----
  #define VAR_SMTP_SASL_OPTS	"smtp_sasl_security_options"
  #define DEF_SMTP_SASL_OPTS	"noplaintext, noanonymous"
  extern char *var_smtp_sasl_opts;
  
+ /*
+  * LIBSPF support
+  */
+ #define REJECT_SPF                              "reject_spf"
+ 
+ #define VAR_SPF_ACTION                          "spf_action"
+ #define DEF_SPF_ACTION                          1
+ extern int var_spf_action;
+ 
+ #define VAR_SPF_DO_PREPEND_HEADER               "spf_do_prepend_header"
+ #define DEF_SPF_DO_PREPEND_HEADER               1
+ extern bool var_spf_do_prepend_header;
+ 
+ #define VAR_SPF_DO_EXPLAIN                      "spf_do_explain"
+ #define DEF_SPF_DO_EXPLAIN                      0
+ extern bool var_spf_do_explain;
+ 
+ #define VAR_SPF_DO_USE_BEST_GUESS_RECORD        "spf_do_use_best_guess_record"
+ #define DEF_SPF_DO_USE_BEST_GUESS_RECORD        0
+ extern bool var_spf_do_use_best_guess;
+ 
+ #define VAR_SPF_DO_USE_TRUSTED_FORWARDER        "spf_do_use_trusted_forwarder"
+ #define DEF_SPF_DO_USE_TRUSTED_FORWARDER        0
+ extern bool var_spf_do_use_trusted_forwarder;
+ 
+ #define VAR_SPF_CUSTOM_EXPLANATION              "spf_custom_explanation"
+ #define DEF_SPF_CUSTOM_EXPLANATION              ""
+ extern char *var_spf_custom_explanation;
+ 
+ #define VAR_SPF_CUSTOM_BEST_GUESS_RECORD        "spf_custom_best_guess_record"
+ #define DEF_SPF_CUSTOM_BEST_GUESS_RECORD        ""
+ extern char *var_spf_custom_guess_record;
+ 
+ #define VAR_SPF_CUSTOM_TRUSTED_FORWARDER        "spf_custom_trusted_forwarder"
+ #define DEF_SPF_CUSTOM_TRUSTED_FORWARDER        ""
+ extern char *var_spf_custom_trusted_forwarder;
+ 
   /*
    * LMTP server. The soft error limit determines how many errors an LMTP
    * client may make before we start to slow down; the hard error limit
    * determines after how many client errors we disconnect.
*** postfix-2.1.4/src/smtpd/smtpd.c	Wed Jun 16 15:40:36 2004
--- postfix-2.1.4.libspf/src/smtpd/smtpd.c	Sat Sep 11 12:48:53 2004
*************** char   *var_xclient_hosts;
*** 754,761 ****
--- 754,771 ----
  char   *var_xforward_hosts;
  bool    var_smtpd_rej_unl_from;
  bool    var_smtpd_rej_unl_rcpt;
  
+ /* LibSPF vars */
+ int     var_spf_action;
+ bool    var_spf_do_prepend_header;
+ bool    var_spf_do_explain;
+ bool    var_spf_do_use_best_guess_record;
+ bool    var_spf_do_use_trusted_forwarder;
+ char   *var_spf_custom_explanation;
+ char   *var_spf_custom_best_guess_record;
+ char   *var_spf_custom_trusted_forwarder;
+ 
  #ifdef SNAPSHOT
  int     var_smtpd_crate_limit;
  int     var_smtpd_cconn_limit;
  char   *var_smtpd_hoggers;
*************** static void mail_open_stream(SMTPD_STATE
*** 984,992 ****
      /*
       * XXX 2821: An SMTP server is not allowed to "clean up" mail except in
       * the case of original submissions. Presently, Postfix always runs all
       * mail through the cleanup server.
!      * 
       * We could approximate the RFC as follows: Postfix rewrites mail if it
       * comes from a source that we are willing to relay for. This way, we
       * avoid rewriting most mail that comes from elsewhere. However, that
       * requires moving functionality away from the cleanup daemon elsewhere,
--- 994,1002 ----
      /*
       * XXX 2821: An SMTP server is not allowed to "clean up" mail except in
       * the case of original submissions. Presently, Postfix always runs all
       * mail through the cleanup server.
!      *
       * We could approximate the RFC as follows: Postfix rewrites mail if it
       * comes from a source that we are willing to relay for. This way, we
       * avoid rewriting most mail that comes from elsewhere. However, that
       * requires moving functionality away from the cleanup daemon elsewhere,
*************** static char *extract_addr(SMTPD_STATE *s
*** 1092,1104 ****
      /*
       * Some mailers send RFC822-style address forms (with comments and such)
       * in SMTP envelopes. We cannot blame users for this: the blame is with
       * programmers violating the RFC, and with sendmail for being permissive.
!      * 
       * XXX The SMTP command tokenizer must leave the address in externalized
       * (quoted) form, so that the address parser can correctly extract the
       * address from surrounding junk.
!      * 
       * XXX We have only one address parser, written according to the rules of
       * RFC 822. That standard differs subtly from RFC 821.
       */
      if (msg_verbose)
--- 1102,1114 ----
      /*
       * Some mailers send RFC822-style address forms (with comments and such)
       * in SMTP envelopes. We cannot blame users for this: the blame is with
       * programmers violating the RFC, and with sendmail for being permissive.
!      *
       * XXX The SMTP command tokenizer must leave the address in externalized
       * (quoted) form, so that the address parser can correctly extract the
       * address from surrounding junk.
!      *
       * XXX We have only one address parser, written according to the rules of
       * RFC 822. That standard differs subtly from RFC 821.
       */
      if (msg_verbose)
*************** static int mail_cmd(SMTPD_STATE *state, 
*** 1191,1199 ****
      state->encoding = 0;
  
      /*
       * Sanity checks.
!      * 
       * XXX 2821 pedantism: Section 4.1.2 says that SMTP servers that receive a
       * command in which invalid character codes have been employed, and for
       * which there are no other reasons for rejection, MUST reject that
       * command with a 501 response. So much for the principle of "be liberal
--- 1201,1209 ----
      state->encoding = 0;
  
      /*
       * Sanity checks.
!      *
       * XXX 2821 pedantism: Section 4.1.2 says that SMTP servers that receive a
       * command in which invalid character codes have been employed, and for
       * which there are no other reasons for rejection, MUST reject that
       * command with a 501 response. So much for the principle of "be liberal
*************** static int rcpt_cmd(SMTPD_STATE *state, 
*** 1385,1393 ****
      char   *arg;
  
      /*
       * Sanity checks.
!      * 
       * XXX 2821 pedantism: Section 4.1.2 says that SMTP servers that receive a
       * command in which invalid character codes have been employed, and for
       * which there are no other reasons for rejection, MUST reject that
       * command with a 501 response. So much for the principle of "be liberal
--- 1395,1403 ----
      char   *arg;
  
      /*
       * Sanity checks.
!      *
       * XXX 2821 pedantism: Section 4.1.2 says that SMTP servers that receive a
       * command in which invalid character codes have been employed, and for
       * which there are no other reasons for rejection, MUST reject that
       * command with a 501 response. So much for the principle of "be liberal
*************** static int rcpt_cmd(SMTPD_STATE *state, 
*** 1476,1484 ****
      }
  
      /*
       * Store the recipient. Remember the first one.
!      * 
       * Flush recipients to maintain a stiffer coupling with the next stage and
       * to better utilize parallelism.
       */
      state->rcpt_count++;
--- 1486,1494 ----
      }
  
      /*
       * Store the recipient. Remember the first one.
!      *
       * Flush recipients to maintain a stiffer coupling with the next stage and
       * to better utilize parallelism.
       */
      state->rcpt_count++;
*************** static int data_cmd(SMTPD_STATE *state, 
*** 1572,1580 ****
      /*
       * Flush out any access table actions that are delegated to the cleanup
       * server, and that may trigger before we accept the first valid
       * recipient.
!      * 
       * Terminate the message envelope segment. Start the message content
       * segment, and prepend our own Received: header. If there is only one
       * recipient, list the recipient address.
       */
--- 1582,1590 ----
      /*
       * Flush out any access table actions that are delegated to the cleanup
       * server, and that may trigger before we accept the first valid
       * recipient.
!      *
       * Terminate the message envelope segment. Start the message content
       * segment, and prepend our own Received: header. If there is only one
       * recipient, list the recipient address.
       */
*************** static int data_cmd(SMTPD_STATE *state, 
*** 1633,1645 ****
      /*
       * Copy the message content. If the cleanup process has a problem, keep
       * reading until the remote stops sending, then complain. Produce typed
       * records from the SMTP stream so we can handle data that spans buffers.
!      * 
       * XXX Force an empty record when the queue file content begins with
       * whitespace, so that it won't be considered as being part of our own
       * Received: header. What an ugly Kluge.
!      * 
       * XXX Deal with UNIX-style From_ lines at the start of message content
       * because sendmail permits it.
       */
      for (prev_rec_type = 0; /* void */ ; prev_rec_type = curr_rec_type) {
--- 1643,1655 ----
      /*
       * Copy the message content. If the cleanup process has a problem, keep
       * reading until the remote stops sending, then complain. Produce typed
       * records from the SMTP stream so we can handle data that spans buffers.
!      *
       * XXX Force an empty record when the queue file content begins with
       * whitespace, so that it won't be considered as being part of our own
       * Received: header. What an ugly Kluge.
!      *
       * XXX Deal with UNIX-style From_ lines at the start of message content
       * because sendmail permits it.
       */
      for (prev_rec_type = 0; /* void */ ; prev_rec_type = curr_rec_type) {
*************** static int data_cmd(SMTPD_STATE *state, 
*** 1652,1659 ****
--- 1662,1683 ----
  	if (first) {
  	    if (strncmp(start + strspn(start, ">"), "From ", 5) == 0) {
  		out_fprintf(out_stream, curr_rec_type,
  			    "X-Mailbox-Line: %s", start);
+ 
+ #ifdef USE_LIBSPF
+ 		/* LibSPF: If we're giving an explanation, we want to output
+ 		   it at the very start of DATA
+ 		*/
+ 		if (state->spf_explanation != NULL) {
+ 		    out_fprintf(out_stream, curr_rec_type,
+ 		                state->spf_explanation);
+ 
+ 		    /* make sure we leave nothing behind */
+ 		    free(state->spf_explanation);
+ 		    state->spf_explanation = NULL;
+ 		}
+ #endif
  		continue;
  	    }
  	    first = 0;
  	    if (len > 0 && IS_SPACE_TAB(start[0]))
*************** static int data_cmd(SMTPD_STATE *state, 
*** 1669,1677 ****
  
      /*
       * Send the end of DATA and finish the proxy connection. Set the
       * CLEANUP_STAT_PROXY error flag in case of trouble.
!      * 
       * XXX The low-level proxy output routines should set "state" error
       * attributes. This requires making "state" a context attribute of the
       * VSTREAM.
       */
--- 1693,1701 ----
  
      /*
       * Send the end of DATA and finish the proxy connection. Set the
       * CLEANUP_STAT_PROXY error flag in case of trouble.
!      *
       * XXX The low-level proxy output routines should set "state" error
       * attributes. This requires making "state" a context attribute of the
       * VSTREAM.
       */
*************** static int data_cmd(SMTPD_STATE *state, 
*** 1711,1719 ****
      /*
       * Handle any errors. One message may suffer from multiple errors, so
       * complain only about the most severe error. Forgive any previous client
       * errors when a message was received successfully.
!      * 
       * See also: qmqpd.c
       */
      if (state->err == CLEANUP_STAT_OK) {
  	state->error_count = 0;
--- 1735,1743 ----
      /*
       * Handle any errors. One message may suffer from multiple errors, so
       * complain only about the most severe error. Forgive any previous client
       * errors when a message was received successfully.
!      *
       * See also: qmqpd.c
       */
      if (state->err == CLEANUP_STAT_OK) {
  	state->error_count = 0;
*************** static int noop_cmd(SMTPD_STATE *state, 
*** 1800,1808 ****
      /*
       * XXX 2821 incompatibility: Section 4.1.1.9 says that NOOP can have a
       * parameter string which is to be ignored. NOOP instructions with
       * parameters? Go figure.
!      * 
       * RFC 2821 violates RFC 821, which says that NOOP takes no parameters.
       */
  #ifdef RFC821_SYNTAX
  
--- 1824,1832 ----
      /*
       * XXX 2821 incompatibility: Section 4.1.1.9 says that NOOP can have a
       * parameter string which is to be ignored. NOOP instructions with
       * parameters? Go figure.
!      *
       * RFC 2821 violates RFC 821, which says that NOOP takes no parameters.
       */
  #ifdef RFC821_SYNTAX
  
*************** static int vrfy_cmd(SMTPD_STATE *state, 
*** 1828,1847 ****
      /*
       * The SMTP standard (RFC 821) disallows unquoted special characters in
       * the VRFY argument. Common practice violates the standard, however.
       * Postfix accomodates common practice where it violates the standard.
!      * 
       * XXX Impedance mismatch! The SMTP command tokenizer preserves quoting,
       * whereas the recipient restrictions checks expect unquoted (internal)
       * address forms. Therefore we must parse out the address, or we must
       * stop doing recipient restriction checks and lose the opportunity to
       * say "user unknown" at the SMTP port.
!      * 
       * XXX 2821 incompatibility and brain damage: Section 4.5.1 requires that
       * VRFY is implemented. RFC 821 specifies that VRFY is optional. It gets
       * even worse: section 3.5.3 says that a 502 (command recognized but not
       * implemented) reply is not fully compliant.
!      * 
       * Thus, an RFC 2821 compliant implementation cannot refuse to supply
       * information in reply to VRFY queries. That is simply bogus. The only
       * reply we could supply is a generic 252 reply. This causes spammers to
       * add tons of bogus addresses to their mailing lists (spam harvesting by
--- 1852,1871 ----
      /*
       * The SMTP standard (RFC 821) disallows unquoted special characters in
       * the VRFY argument. Common practice violates the standard, however.
       * Postfix accomodates common practice where it violates the standard.
!      *
       * XXX Impedance mismatch! The SMTP command tokenizer preserves quoting,
       * whereas the recipient restrictions checks expect unquoted (internal)
       * address forms. Therefore we must parse out the address, or we must
       * stop doing recipient restriction checks and lose the opportunity to
       * say "user unknown" at the SMTP port.
!      *
       * XXX 2821 incompatibility and brain damage: Section 4.5.1 requires that
       * VRFY is implemented. RFC 821 specifies that VRFY is optional. It gets
       * even worse: section 3.5.3 says that a 502 (command recognized but not
       * implemented) reply is not fully compliant.
!      *
       * Thus, an RFC 2821 compliant implementation cannot refuse to supply
       * information in reply to VRFY queries. That is simply bogus. The only
       * reply we could supply is a generic 252 reply. This causes spammers to
       * add tons of bogus addresses to their mailing lists (spam harvesting by
*************** static int vrfy_cmd(SMTPD_STATE *state, 
*** 1876,1884 ****
       * XXX 2821 new feature: Section 3.5.1 requires that the VRFY response is
       * either "full name <user@domain>" or "user@domain". Postfix replies
       * with the address that was provided by the client, whether or not it is
       * in fully qualified domain form or not.
!      * 
       * Reply code 250 is reserved for the case where the address is verified;
       * reply code 252 should be used when no definitive certainty exists.
       */
      smtpd_chat_reply(state, "252 %s", argv[1].strval);
--- 1900,1908 ----
       * XXX 2821 new feature: Section 3.5.1 requires that the VRFY response is
       * either "full name <user@domain>" or "user@domain". Postfix replies
       * with the address that was provided by the client, whether or not it is
       * in fully qualified domain form or not.
!      *
       * Reply code 250 is reserved for the case where the address is verified;
       * reply code 252 should be used when no definitive certainty exists.
       */
      smtpd_chat_reply(state, "252 %s", argv[1].strval);
*************** static void smtpd_proto(SMTPD_STATE *sta
*** 2368,2382 ****
       * Print a greeting banner and run the state machine. Read SMTP commands
       * one line at a time. According to the standard, a sender or recipient
       * address could contain an escaped newline. I think this is perverse,
       * and anyone depending on this is really asking for trouble.
!      * 
       * In case of mail protocol trouble, the program jumps back to this place,
       * so that it can perform the necessary cleanup before talking to the
       * next client. The setjmp/longjmp primitives are like a sharp tool: use
       * with care. I would certainly recommend against the use of
       * setjmp/longjmp in programs that change privilege levels.
!      * 
       * In case of file system trouble the program terminates after logging the
       * error and after informing the client. In all other cases (out of
       * memory, panic) the error is logged, and the msg_cleanup() exit handler
       * cleans up, but no attempt is made to inform the client of the nature
--- 2392,2406 ----
       * Print a greeting banner and run the state machine. Read SMTP commands
       * one line at a time. According to the standard, a sender or recipient
       * address could contain an escaped newline. I think this is perverse,
       * and anyone depending on this is really asking for trouble.
!      *
       * In case of mail protocol trouble, the program jumps back to this place,
       * so that it can perform the necessary cleanup before talking to the
       * next client. The setjmp/longjmp primitives are like a sharp tool: use
       * with care. I would certainly recommend against the use of
       * setjmp/longjmp in programs that change privilege levels.
!      *
       * In case of file system trouble the program terminates after logging the
       * error and after informing the client. In all other cases (out of
       * memory, panic) the error is logged, and the msg_cleanup() exit handler
       * cleans up, but no attempt is made to inform the client of the nature
*************** static void smtpd_service(VSTREAM *strea
*** 2552,2560 ****
  
      /*
       * This routine runs when a client has connected to our network port, or
       * when the smtp server is run in stand-alone mode (input from pipe).
!      * 
       * Look up and sanitize the peer name, then initialize some connection-
       * specific state. When the name service is hosed, hostname lookup will
       * take a while. This is why I always run a local name server on critical
       * machines.
--- 2576,2584 ----
  
      /*
       * This routine runs when a client has connected to our network port, or
       * when the smtp server is run in stand-alone mode (input from pipe).
!      *
       * Look up and sanitize the peer name, then initialize some connection-
       * specific state. When the name service is hosed, hostname lookup will
       * take a while. This is why I always run a local name server on critical
       * machines.
*************** int     main(int argc, char **argv)
*** 2706,2713 ****
--- 2730,2741 ----
  	VAR_VIRT_ALIAS_CODE, DEF_VIRT_ALIAS_CODE, &var_virt_alias_code, 0, 0,
  	VAR_VIRT_MAILBOX_CODE, DEF_VIRT_MAILBOX_CODE, &var_virt_mailbox_code, 0, 0,
  	VAR_RELAY_RCPT_CODE, DEF_RELAY_RCPT_CODE, &var_relay_rcpt_code, 0, 0,
  	VAR_VERIFY_POLL_COUNT, DEF_VERIFY_POLL_COUNT, &var_verify_poll_count, 1, 0,
+ 
+ 	/* LibSPF vars */
+ 	VAR_SPF_ACTION, DEF_SPF_ACTION, &var_spf_action, 0, 7,
+ 
  #ifdef SNAPSHOT
  	VAR_SMTPD_CRATE_LIMIT, DEF_SMTPD_CRATE_LIMIT, &var_smtpd_crate_limit, 0, 0,
  	VAR_SMTPD_CCONN_LIMIT, DEF_SMTPD_CCONN_LIMIT, &var_smtpd_cconn_limit, 0, 0,
  #endif
*************** int     main(int argc, char **argv)
*** 2733,2740 ****
--- 2761,2774 ----
  	VAR_BROKEN_AUTH_CLNTS, DEF_BROKEN_AUTH_CLNTS, &var_broken_auth_clients,
  	VAR_SHOW_UNK_RCPT_TABLE, DEF_SHOW_UNK_RCPT_TABLE, &var_show_unk_rcpt_table,
  	VAR_SMTPD_REJ_UNL_FROM, DEF_SMTPD_REJ_UNL_FROM, &var_smtpd_rej_unl_from,
  	VAR_SMTPD_REJ_UNL_RCPT, DEF_SMTPD_REJ_UNL_RCPT, &var_smtpd_rej_unl_rcpt,
+ 
+ 	/* LibSPF vars */
+ 	VAR_SPF_DO_PREPEND_HEADER, DEF_SPF_DO_PREPEND_HEADER, &var_spf_do_prepend_header,
+ 	VAR_SPF_DO_EXPLAIN, DEF_SPF_DO_EXPLAIN, &var_spf_do_explain,
+ 	VAR_SPF_DO_USE_BEST_GUESS_RECORD, DEF_SPF_DO_USE_BEST_GUESS_RECORD, &var_spf_do_use_best_guess_record,
+ 	VAR_SPF_DO_USE_TRUSTED_FORWARDER, DEF_SPF_DO_USE_TRUSTED_FORWARDER, &var_spf_do_use_trusted_forwarder,
  	0,
      };
      static CONFIG_STR_TABLE str_table[] = {
  	VAR_SMTPD_BANNER, DEF_SMTPD_BANNER, &var_smtpd_banner, 1, 0,
*************** int     main(int argc, char **argv)
*** 2771,2778 ****
--- 2805,2818 ----
  	VAR_SMTPD_PROXY_EHLO, DEF_SMTPD_PROXY_EHLO, &var_smtpd_proxy_ehlo, 0, 0,
  	VAR_INPUT_TRANSP, DEF_INPUT_TRANSP, &var_input_transp, 0, 0,
  	VAR_XCLIENT_HOSTS, DEF_XCLIENT_HOSTS, &var_xclient_hosts, 0, 0,
  	VAR_XFORWARD_HOSTS, DEF_XFORWARD_HOSTS, &var_xforward_hosts, 0, 0,
+ 
+ 	/* LibSPF vars */
+ 	VAR_SPF_CUSTOM_EXPLANATION, DEF_SPF_CUSTOM_EXPLANATION, &var_spf_custom_explanation, 0, 0,
+ 	VAR_SPF_CUSTOM_BEST_GUESS_RECORD, DEF_SPF_CUSTOM_BEST_GUESS_RECORD, &var_spf_custom_best_guess_record, 0, 0,
+ 	VAR_SPF_CUSTOM_TRUSTED_FORWARDER, DEF_SPF_CUSTOM_TRUSTED_FORWARDER, &var_spf_custom_trusted_forwarder, 0, 0,
+ 
  #ifdef SNAPSHOT
  	VAR_SMTPD_HOGGERS, DEF_SMTPD_HOGGERS, &var_smtpd_hoggers, 0, 0,
  #endif
  	0,
*** postfix-2.1.4/src/smtpd/smtpd_check.c	Mon May  3 08:40:13 2004
--- postfix-2.1.4.libspf/src/smtpd/smtpd_check.c	Sat Sep 11 12:47:17 2004
***************
*** 207,214 ****
--- 207,218 ----
  #include <is_header.h>
  
  /* Application-specific. */
  
+ #ifdef USE_LIBSPF
+ #include <spf.h>
+ #endif /* USE_LIBSPF */
+ 
  #include "smtpd.h"
  #include "smtpd_sasl_glue.h"
  #include "smtpd_check.h"
  
*************** static int check_rcpt_maps(SMTPD_STATE *
*** 324,348 ****
  
   /*
    * If some decision can't be made due to a temporary error, then change
    * other decisions into deferrals.
!   * 
    * XXX Deferrals can be postponed only with restrictions that are based on
    * client-specified information: this restricts their use to parameters
    * given in HELO, MAIL FROM, RCPT TO commands.
!   * 
    * XXX Deferrals must not be postponed after client hostname lookup failure.
    * The reason is that the effect of access tables may depend on whether a
    * client hostname is available or not. Thus, the reject_unknown_client
    * restriction must defer immediately when lookup fails, otherwise incorrect
    * results happen with:
!   * 
    * reject_unknown_client, hostname-based white-list, reject
!   * 
    * XXX With warn_if_reject, don't raise the defer_if_permit flag when a
    * reject-style restriction fails. Instead, log the warning for the
    * resulting defer message.
!   * 
    * XXX With warn_if_reject, do raise the defer_if_reject flag when a
    * permit-style restriction fails. Otherwise, we could reject legitimate
    * mail.
    */
--- 328,352 ----
  
   /*
    * If some decision can't be made due to a temporary error, then change
    * other decisions into deferrals.
!   *
    * XXX Deferrals can be postponed only with restrictions that are based on
    * client-specified information: this restricts their use to parameters
    * given in HELO, MAIL FROM, RCPT TO commands.
!   *
    * XXX Deferrals must not be postponed after client hostname lookup failure.
    * The reason is that the effect of access tables may depend on whether a
    * client hostname is available or not. Thus, the reject_unknown_client
    * restriction must defer immediately when lookup fails, otherwise incorrect
    * results happen with:
!   *
    * reject_unknown_client, hostname-based white-list, reject
!   *
    * XXX With warn_if_reject, don't raise the defer_if_permit flag when a
    * reject-style restriction fails. Instead, log the warning for the
    * resulting defer message.
!   *
    * XXX With warn_if_reject, do raise the defer_if_reject flag when a
    * permit-style restriction fails. Otherwise, we could reject legitimate
    * mail.
    */
*************** static int smtpd_check_reject(SMTPD_STAT
*** 757,776 ****
      }
  
      /*
       * Soft bounce safety net.
!      * 
       * XXX The code below also appears in the Postfix SMTP server reply output
       * routine. It is duplicated here in order to avoid discrepancies between
       * the reply codes that are shown in "reject" logging and the reply codes
       * that are actually sent to the SMTP client.
!      * 
       * Implementing the soft_bounce safety net in the SMTP server reply output
       * routine has the advantage that it covers all 5xx replies, including
       * SMTP protocol or syntax errors, which makes soft_bounce great for
       * non-destructive tests (especially by people who are paranoid about
       * losing mail).
!      * 
       * We could eliminate the code duplication and implement the soft_bounce
       * safety net only in the code below. But then the safety net would cover
       * the UCE restrictions only. This would be at odds with documentation
       * which says soft_bounce changes all 5xx replies into 4xx ones.
--- 761,780 ----
      }
  
      /*
       * Soft bounce safety net.
!      *
       * XXX The code below also appears in the Postfix SMTP server reply output
       * routine. It is duplicated here in order to avoid discrepancies between
       * the reply codes that are shown in "reject" logging and the reply codes
       * that are actually sent to the SMTP client.
!      *
       * Implementing the soft_bounce safety net in the SMTP server reply output
       * routine has the advantage that it covers all 5xx replies, including
       * SMTP protocol or syntax errors, which makes soft_bounce great for
       * non-destructive tests (especially by people who are paranoid about
       * losing mail).
!      *
       * We could eliminate the code duplication and implement the soft_bounce
       * safety net only in the code below. But then the safety net would cover
       * the UCE restrictions only. This would be at odds with documentation
       * which says soft_bounce changes all 5xx replies into 4xx ones.
*************** static char *dup_if_truncate(char *name)
*** 874,882 ****
      char   *result;
  
      /*
       * Truncate hostnames ending in dot but not dot-dot.
!      * 
       * XXX This should not be distributed all over the code. Problem is,
       * addresses can enter the system via multiple paths: networks, local
       * forward/alias/include files, even as the result of address rewriting.
       */
--- 878,886 ----
      char   *result;
  
      /*
       * Truncate hostnames ending in dot but not dot-dot.
!      *
       * XXX This should not be distributed all over the code. Problem is,
       * addresses can enter the system via multiple paths: networks, local
       * forward/alias/include files, even as the result of address rewriting.
       */
*************** static int not_in_client_helo(SMTPD_STAT
*** 1644,1652 ****
       * immediately, outside any particular mail transaction context. For
       * example, rejecting HELO does not affect subsequent mail deliveries.
       * Thus, if delay_reject=no, client and helo actions such as FILTER or
       * HOLD also should not affect subsequent mail deliveries. Hmm...
!      * 
       * XXX If the MAIL FROM command is rejected then we have to reset access
       * map side effects such as FILTER.
       */
      if (state->sender == 0) {
--- 1648,1656 ----
       * immediately, outside any particular mail transaction context. For
       * example, rejecting HELO does not affect subsequent mail deliveries.
       * Thus, if delay_reject=no, client and helo actions such as FILTER or
       * HOLD also should not affect subsequent mail deliveries. Hmm...
!      *
       * XXX If the MAIL FROM command is rejected then we have to reset access
       * map side effects such as FILTER.
       */
      if (state->sender == 0) {
*************** static int check_table_result(SMTPD_STAT
*** 1878,1886 ****
       * Unfortunately, maps must be declared ahead of time so they can be
       * opened before we go to jail. We could insist that the RHS can only
       * contain a pre-defined restriction class name, but that would be too
       * restrictive. Instead we warn if an access table references any map.
!      * 
       * XXX Don't use passwd files or address rewriting maps as access tables.
       */
      if (strchr(value, ':') != 0) {
  	msg_warn("SMTPD access map %s has entry with lookup table: %s",
--- 1882,1890 ----
       * Unfortunately, maps must be declared ahead of time so they can be
       * opened before we go to jail. We could insist that the RHS can only
       * contain a pre-defined restriction class name, but that would be too
       * restrictive. Instead we warn if an access table references any map.
!      *
       * XXX Don't use passwd files or address rewriting maps as access tables.
       */
      if (strchr(value, ':') != 0) {
  	msg_warn("SMTPD access map %s has entry with lookup table: %s",
*************** static int check_table_result(SMTPD_STAT
*** 1904,1912 ****
      /*
       * Recursively evaluate the restrictions given in the right-hand side. In
       * the dark ages, an empty right-hand side meant OK. Make some
       * discouraging comments.
!      * 
       * XXX Jump some hoops to avoid a minute memory leak in case of a file
       * configuration error.
       */
  #define ADDROF(x) ((char *) &(x))
--- 1908,1916 ----
      /*
       * Recursively evaluate the restrictions given in the right-hand side. In
       * the dark ages, an empty right-hand side meant OK. Make some
       * discouraging comments.
!      *
       * XXX Jump some hoops to avoid a minute memory leak in case of a file
       * configuration error.
       */
  #define ADDROF(x) ((char *) &(x))
*************** static int check_domain_access(SMTPD_STA
*** 1985,1993 ****
  	msg_info("%s: %s", myname, domain);
  
      /*
       * Try the name and its parent domains. Including top-level domains.
!      * 
       * Helo names can end in ".". The test below avoids lookups of the empty
       * key, because Berkeley DB cannot deal with it. [Victor Duchovni, Morgan
       * Stanley].
       */
--- 1989,1997 ----
  	msg_info("%s: %s", myname, domain);
  
      /*
       * Try the name and its parent domains. Including top-level domains.
!      *
       * Helo names can end in ".". The test below avoids lookups of the empty
       * key, because Berkeley DB cannot deal with it. [Victor Duchovni, Morgan
       * Stanley].
       */
*************** static int check_server_access(SMTPD_STA
*** 2136,2147 ****
  	domain = name;
  
      /*
       * If the domain name does not exist then we apply no restriction.
!      * 
       * If the domain name exists but no MX record exists, fabricate an MX record
       * that points to the domain name itself.
!      * 
       * If the domain name exists but no NS record exists, look up parent domain
       * NS records.
       */
      dns_status = dns_lookup(domain, type, 0, &server_list,
--- 2140,2151 ----
  	domain = name;
  
      /*
       * If the domain name does not exist then we apply no restriction.
!      *
       * If the domain name exists but no MX record exists, fabricate an MX record
       * that points to the domain name itself.
!      *
       * If the domain name exists but no NS record exists, look up parent domain
       * NS records.
       */
      dns_status = dns_lookup(domain, type, 0, &server_list,
*************** static const char *smtpd_expand_lookup(c
*** 2432,2440 ****
  
      /*
       * Don't query main.cf parameters, as the result of expansion could
       * reveal system-internal information in server replies.
!      * 
       * Return NULL only for non-existent names.
       */
      if (STREQ(name, MAIL_ATTR_CLIENT)) {
  	return (state->namaddr);
--- 2436,2444 ----
  
      /*
       * Don't query main.cf parameters, as the result of expansion could
       * reveal system-internal information in server replies.
!      *
       * Return NULL only for non-existent names.
       */
      if (STREQ(name, MAIL_ATTR_CLIENT)) {
  	return (state->namaddr);
*************** static int reject_unauth_sender_login_mi
*** 2825,2832 ****
--- 2829,2960 ----
  }
  
  #endif
  
+ #ifdef USE_LIBSPF
+ 
+ /* reject spf - checks if the sender+client_addr passes an SPF check */
+ 
+ /*
+ TODO
+ -test config file settings
+ -document config file settings
+ -add debug info
+ -write comments
+ */
+ 
+ static int reject_spf(SMTPD_STATE *state, const char *sender, const char *client_addr)
+ {
+     int pass = 0;
+     SPF_RESULT res = 0;
+     char *client_helo = NULL;
+     char *recieved_spf = NULL;
+ 
+     char *spf_explain = NULL;
+     char *spf_trusted_forwarder = NULL;
+     char *spf_best_guess = NULL;
+ 
+     char *header_response = NULL;
+     VSTRING *vstr_header_response = NULL;
+     char *header_response_full = NULL;
+ 
+     peer_info_t *pi = NULL;
+ 
+     if (var_spf_action > 0)
+     {
+ 	recieved_spf = "Recieved-SPF: ";
+ 
+ 	if (state->helo_name && *state->helo_name) {
+ 	    client_helo = state->helo_name;
+ 	}
+ 
+ 	if (var_spf_custom_explanation != NULL && *var_spf_custom_explanation) {
+ 	    spf_explain = var_spf_custom_explanation;
+ 	}
+ 
+ 	if (var_spf_custom_trusted_forwarder != NULL && *var_spf_custom_trusted_forwarder) {
+ 	    spf_trusted_forwarder = var_spf_custom_trusted_forwarder;
+ 	}
+ 
+ 	if (var_spf_custom_best_guess_record != NULL && *var_spf_custom_best_guess_record) {
+ 	    spf_best_guess = var_spf_custom_best_guess_record;
+ 	}
+ 
+ 	pi = SPF_init(var_myhostname, client_addr, spf_explain, spf_trusted_forwarder,
+ 		      spf_best_guess, var_spf_do_use_trusted_forwarder,
+ 		      var_spf_do_use_best_guess_record);
+ 
+ 	if (pi == NULL) {
+ 	    msg_fatal("SPF: Could not allocate peer_info_t structure");
+ 	    return (SMTPD_CHECK_DUNNO);
+ 	}
+ 
+ 	SPF_smtp_helo(pi, client_helo);
+ 
+ 	if (SPF_smtp_from(pi, sender) == 0) {
+ 	    msg_error("SPF: Bad address: %s", sender);
+ 	    SPF_close(pi);
+ 
+ 	    return(SMTPD_CHECK_DUNNO);
+ 	}
+ 
+ 	res = SPF_policy_main(pi);
+ 
+ 	if (var_spf_action > 1) {
+ 	    switch (res) {
+ 		case SPF_H_FAIL:  pass = (var_spf_action > 1 ? 0 : 1); break;
+ 		case SPF_S_FAIL:  pass = (var_spf_action > 2 ? 0 : 1); break;
+ 		case SPF_NEUTRAL: pass = (var_spf_action > 3 ? 0 : 1); break;
+ 		case SPF_NONE:    pass = (var_spf_action > 4 ? 0 : 1); break;
+ 		case SPF_ERROR:   pass = (var_spf_action > 5 ? 0 : 1); break;
+ 		case SPF_UNKNOWN: pass = (var_spf_action > 6 ? 0 : 1); break;
+ 		default:          pass = 1; break;
+ 	    }
+ 	} else {
+ 	    pass = 1;
+ 	}
+ 
+ 	if (!pass) {
+ 	    SPF_close(pi);
+ 
+ 	    return(smtpd_check_reject(state, MAIL_ERROR_POLICY,
+ 		"553: Sender address rejected: SPF check failed"));
+ 	} else {
+ 	    if (var_spf_do_explain) {
+ 		state->spf_explanation = SPF_get_explain(pi);
+ 	    }
+ 
+ 	    if (var_spf_do_prepend_header) {
+ 		header_response = SPF_build_header(pi);
+ 	    }
+ 
+ 	    if (header_response != NULL) {
+ 		vstr_header_response = vstring_alloc(strlen(recieved_spf) + strlen(header_response) + 1);
+ 		vstring_strcat(vstr_header_response, recieved_spf);
+ 		vstring_strcat(vstr_header_response, header_response);
+ 		header_response_full = vstring_export(vstr_header_response);
+ 
+ 		if (state->prepend == 0) {
+ 		    state->prepend = argv_alloc(1);
+ 		}
+ 
+ 		argv_add(state->prepend, header_response_full, (char *) 0);
+ 
+ 		free(header_response);
+ 		free(header_response_full);
+ 	    }
+ 
+ 	    SPF_close(pi);
+ 
+ 	    return(SMTPD_CHECK_DUNNO);
+ 	}
+     }
+ 
+     return(SMTPD_CHECK_DUNNO);
+ }
+ 
+ #endif /* USE_LIBSPF */
+ 
  /* check_policy_service - check delegated policy service */
  
  static int check_policy_service(SMTPD_STATE *state, const char *server,
  		            const char *reply_name, const char *reply_class,
*************** static int generic_checks(SMTPD_STATE *s
*** 3176,3183 ****
--- 3304,3318 ----
  		    status = reject_unauth_sender_login_mismatch(state, state->sender);
  	    } else
  #endif
  		msg_warn("restriction `%s' ignored: no SASL support", name);
+ 	} else if (strcasecmp(name, REJECT_SPF) == 0) {
+ #ifdef USE_LIBSPF
+ 	    if (state->sender && *state->sender && state->addr) {
+ 		status = reject_spf(state, state->sender, state->addr);
+ 	    } else
+ #endif /* USE_LIBSPF */
+ 		msg_warn("restriction `%s' ignored: no SPF support", name);
  	} else if (is_map_command(state, name, CHECK_SENDER_NS_ACL, &cpp)) {
  	    if (state->sender && *state->sender) {
  		status = check_server_access(state, *cpp, state->sender,
  					     T_NS, state->sender,
*************** static int generic_checks(SMTPD_STATE *s
*** 3303,3310 ****
--- 3438,3446 ----
  	    status = generic_checks(state, list, reply_name,
  				    reply_class, def_acl);
  	}
  
+ 
  	/*
  	 * Error: undefined restriction name.
  	 */
  	else {
*************** static int check_rcpt_maps(SMTPD_STATE *
*** 3738,3746 ****
  
      /*
       * At this point, anything that resolves to the error mailer is known to
       * be undeliverable.
!      * 
       * XXX Until the address resolver does final address resolution, known and
       * unknown recipients in virtual alias domains will both resolve to
       * "error:user unknown".
       */
--- 3874,3882 ----
  
      /*
       * At this point, anything that resolves to the error mailer is known to
       * be undeliverable.
!      *
       * XXX Until the address resolver does final address resolution, known and
       * unknown recipients in virtual alias domains will both resolve to
       * "error:user unknown".
       */
*************** static int check_rcpt_maps(SMTPD_STATE *
*** 3753,3765 ****
  				   STR(reply->nexthop)));
  
      /*
       * Search the recipient lookup tables of the respective address class.
!      * 
       * XXX Use the less expensive maps_find() (case is already folded) instead
       * of the baroque mail_addr_find(). But then we have to strip the domain
       * and deal with address extensions ourselves.
!      * 
       * XXX But that would break sites that use the virtual delivery agent for
       * local delivery, because the virtual delivery agent requires
       * user@domain style addresses in its user database.
       */
--- 3889,3901 ----
  				   STR(reply->nexthop)));
  
      /*
       * Search the recipient lookup tables of the respective address class.
!      *
       * XXX Use the less expensive maps_find() (case is already folded) instead
       * of the baroque mail_addr_find(). But then we have to strip the domain
       * and deal with address extensions ourselves.
!      *
       * XXX But that would break sites that use the virtual delivery agent for
       * local delivery, because the virtual delivery agent requires
       * user@domain style addresses in its user database.
       */
*************** char   *smtpd_check_data(SMTPD_STATE *st
*** 3895,3903 ****
      state->defer_if_permit.active = 0;
  
      /*
       * Apply restrictions in the order as specified.
!      * 
       * XXX We cannot specify a default target for a bare access map.
       */
      SMTPD_CHECK_RESET();
      status = setjmp(smtpd_check_buf);
--- 4031,4039 ----
      state->defer_if_permit.active = 0;
  
      /*
       * Apply restrictions in the order as specified.
!      *
       * XXX We cannot specify a default target for a bare access map.
       */
      SMTPD_CHECK_RESET();
      status = setjmp(smtpd_check_buf);
*** postfix-2.1.4/src/smtpd/smtpd_state.c	Wed Apr 21 11:23:49 2004
--- postfix-2.1.4.libspf/src/smtpd/smtpd_state.c	Sat Sep 11 10:15:11 2004
*************** void    smtpd_state_init(SMTPD_STATE *st
*** 118,125 ****
--- 118,129 ----
      if (var_smtpd_sasl_enable)
  	smtpd_sasl_connect(state, VAR_SMTPD_SASL_OPTS, var_smtpd_sasl_opts);
  #endif
  
+ #ifdef USE_LIBSPF
+     state->spf_explanation = NULL;
+ #endif
+ 
      /*
       * Initialize peer information.
       */
      smtpd_peer_init(state);
*** postfix-2.1.4/src/smtpd/Makefile	Wed Sep 15 08:51:49 2004
--- postfix-2.1.4.libspf/src/smtpd/Makefile	Sat Sep 11 06:30:32 2004
*************** OBJS	= smtpd.o smtpd_token.o smtpd_check
*** 22,35 ****
  	smtpd_xforward.o
  HDRS	= smtpd_token.h smtpd_check.h smtpd_chat.h smtpd_sasl_proto.h \
  	smtpd_sasl_glue.h smtpd_proxy.h
  TESTSRC	= smtpd_token_test.c
! DEFS	= -I. -I$(INC_DIR) -D$(SYSTYPE)
  CFLAGS	= $(DEBUG) $(OPT) $(DEFS)
  TESTPROG= smtpd_token smtpd_check
  PROG	= smtpd
  INC_DIR	= ../../include
! LIBS	= ../../lib/libmaster.a ../../lib/libglobal.a ../../lib/libdns.a ../../lib/libutil.a
  
  .c.o:;	$(CC) $(CFLAGS) -c $*.c
  
  $(PROG): $(OBJS) $(LIBS)
--- 22,35 ----
  	smtpd_xforward.o
  HDRS	= smtpd_token.h smtpd_check.h smtpd_chat.h smtpd_sasl_proto.h \
  	smtpd_sasl_glue.h smtpd_proxy.h
  TESTSRC	= smtpd_token_test.c
! DEFS	= -I. -I$(INC_DIR) -D$(SYSTYPE) -DUSE_LIBSPF
  CFLAGS	= $(DEBUG) $(OPT) $(DEFS)
  TESTPROG= smtpd_token smtpd_check
  PROG	= smtpd
  INC_DIR	= ../../include
! LIBS	= ../../lib/libmaster.a ../../lib/libglobal.a ../../lib/libdns.a ../../lib/libutil.a /usr/local/lib/libspf.so
  
  .c.o:;	$(CC) $(CFLAGS) -c $*.c
  
  $(PROG): $(OBJS) $(LIBS)
*** postfix-2.1.4/src/smtpd/smtpd.h	Wed Apr 21 11:23:33 2004
--- postfix-2.1.4.libspf/src/smtpd/smtpd.h	Sat Sep 11 10:03:17 2004
*************** typedef struct SMTPD_STATE {
*** 104,111 ****
--- 104,118 ----
      VSTRING *sasl_decoded;
  #endif
  
      /*
+      * LibSPF specific
+      */
+ #ifdef USE_LIBSPF
+     char *spf_explanation;
+ #endif
+ 
+     /*
       * Specific to smtpd access checks.
       */
      int     sender_rcptmap_checked;	/* sender validated against maps */
      int     recipient_rcptmap_checked;	/* recipient validated against maps */
*************** extern void smtpd_peer_reset(SMTPD_STATE
*** 207,225 ****
  #define SMTPD_PEER_CODE_PERM	5
  
   /*
    * Choose between normal or forwarded attributes.
!   * 
    * Note 1: inside the SMTP server, forwarded attributes must have the exact
    * same representation as normal attributes: unknown string values are
    * "unknown", except for HELO which defaults to null. This is better than
    * having to change every piece of code that accesses a possibly forwarded
    * attribute.
!   * 
    * Note 2: outside the SMTP server, the representation of unknown/known
    * attribute values is different in queue files, in queue manager delivery
    * requests, and in over-the-network XFORWARD commands.
!   * 
    * Note 3: if forwarding client information, don't mix information from the
    * current SMTP session with forwarded information from an up-stream
    * session.
    */
--- 214,232 ----
  #define SMTPD_PEER_CODE_PERM	5
  
   /*
    * Choose between normal or forwarded attributes.
!   *
    * Note 1: inside the SMTP server, forwarded attributes must have the exact
    * same representation as normal attributes: unknown string values are
    * "unknown", except for HELO which defaults to null. This is better than
    * having to change every piece of code that accesses a possibly forwarded
    * attribute.
!   *
    * Note 2: outside the SMTP server, the representation of unknown/known
    * attribute values is different in queue files, in queue manager delivery
    * requests, and in over-the-network XFORWARD commands.
!   *
    * Note 3: if forwarding client information, don't mix information from the
    * current SMTP session with forwarded information from an up-stream
    * session.
    */
*** postfix-2.1.4/conf/spf.cf.example	Wed Dec 31 16:00:00 1969
--- postfix-2.1.4.libspf/conf/spf.cf.example	Sat Sep 11 12:55:49 2004
***************
*** 0 ****
--- 1,98 ----
+ # SPF ACTION
+ #
+ # Describes the action to take (i.e., accept or reject) based on the SPF
+ # result.
+ #
+ # 0:  SPF checking disabled
+ # 1+: SPF checks enabled; all mail is accepted regardless of result.
+ # 2:  messages with a result of 'fail' are rejected;
+ #     all other result types accepted
+ # 3:  rejected: fail, softfail; accepted: all others
+ # 4:  rejected: fail, softfail, neutral; accepted: all others
+ # 5:  rejected: fail, softfail, neutral, none; accepted: all others
+ # 6:  rejected: fail, softfail, neutral, none, error; accepted: all others
+ # 7:  rejected: fail, softfail, neutral, none, error, unknown;
+ #     accepted: all others (only pass at this point)
+ #
+ #spf_action = 1
+ 
+ # PREPEND SPF HEADER
+ #
+ # Add the Recieved-SPF: header to SPF-checked messages
+ #
+ #spf_do_prepend_header = yes
+ 
+ # ADD EXPLANATIONS TO DATA
+ #
+ # Prepends a string explaining the SPF result (except if result is PASS)
+ # to DATA.
+ #
+ # Note: LibSPF has a built-in default explanation; this setting
+ #       may be turned on without modifying spf_custom_explanation.  The
+ #       default LibSPF values are given as examples for their respective
+ #       spf_custom_* conf vars.
+ #
+ # See also: spf_custom_explanation
+ #
+ #spf_do_explain = no
+ 
+ # USE A TRUSTED FORWARDER
+ #
+ # For domains that do not publish SPF records, try to use a trusted forwarder
+ # instead.
+ #
+ # Note: LibSPF has a built-in default trusted forwarder record; this setting
+ #       may be turned on without modifying spf_custom_trusted_forwarder.  The
+ #       default LibSPF values are given as examples for their respective
+ #       spf_custom_* conf vars.
+ #
+ # See also: spf_custom_trusted_forwarder
+ #
+ #spf_do_use_trusted_forwarder = no
+ 
+ # USE A BEST GUESS RECORD
+ #
+ # Apply a "best guess" generic record for domains who do not publish SPF and
+ # did not pass with a trusted forwarder.
+ #
+ # Note: LibSPF has a built-in default best guess record; this setting
+ #       may be turned on without modifying spf_custom_best_guess_record.  The
+ #       default LibSPF values are given as examples for their respective
+ #       spf_custom_* conf vars.
+ #
+ # See also: spf_custom_best_guess_record
+ #
+ #spf_do_use_best_guess_record = no
+ 
+ # SET CUSTOM EXPLANATION
+ #
+ # Set a custom SPF result explanation that is prepended to DATA whenever the
+ # result is anything other than PASS.
+ #
+ # Note: Is not used unless spf_do_explain = yes
+ #
+ # Ex: See http://spf.pobox.com/why.html?sender=%{S}&ip=%{I}&receiver=%{xR}
+ #
+ #spf_custom_explanation =
+ 
+ # SET CUSTOM TRUSTED FORWARDER
+ #
+ # Set a custom trusted forwarder that is used when considering a domain which
+ # does not publish SPF records.
+ #
+ # Note: Is not used unless spf_do_use_trusted_forwarder = yes
+ #
+ # Ex: v=spf1 include:spf.trusted-forwarder.org
+ #
+ #spf_custom_trusted_forwarder =
+ 
+ # SET CUSTOM BEST GUESS RECORD
+ #
+ # Set a custom best guess record that is applied to domains who do not publish
+ # SPF and did not pass with a trusted forwarder.
+ #
+ # Note: Is not used unless spf_do_use_best_guess_record = yes
+ #
+ # Ex: v=spf1 a/24 mx/24 ptr
+ #
+ #spf_custom_best_guess_record =
