+++ /dev/null
-commit 2357aa78ccd7182cad14307eb89cb1065f078356
-Author: Jeremy Harris <jgh146exb@wizmail.org>
-Date: Sun Aug 1 18:15:39 2021 +0100
-
- ACL: "seen" condition
-
-diff --git a/src/src/acl.c b/src/src/acl.c
-index f47259ca0..be17b5768 100644
---- a/src/src/acl.c
-+++ b/src/src/acl.c
-@@ -103,6 +103,7 @@ enum { ACLC_ACL,
- ACLC_REGEX,
- #endif
- ACLC_REMOVE_HEADER,
-+ ACLC_SEEN,
- ACLC_SENDER_DOMAINS,
- ACLC_SENDERS,
- ACLC_SET,
-@@ -288,6 +289,7 @@ static condition_def conditions[] = {
- ACL_BIT_MIME | ACL_BIT_NOTSMTP |
- ACL_BIT_NOTSMTP_START),
- },
-+ [ACLC_SEEN] = { US"seen", TRUE, FALSE, 0 },
- [ACLC_SENDER_DOMAINS] = { US"sender_domains", FALSE, FALSE,
- ACL_BIT_AUTH | ACL_BIT_CONNECT |
- ACL_BIT_HELO |
-@@ -2815,6 +2817,143 @@ return rc;
-
-
-
-+/*************************************************
-+* Handle a check for previously-seen *
-+*************************************************/
-+
-+/*
-+ACL clauses like: seen = -5m / key=$foo / readonly
-+
-+Return is true for condition-true - but the semantics
-+depend heavily on the actual use-case.
-+
-+Negative times test for seen-before, positive for seen-more-recently-than
-+(the given interval before current time).
-+
-+All are subject to history not having been cleaned from the DB.
-+
-+Default for seen-before is to create if not present, and to
-+update if older than 10d (with the seen-test time).
-+Default for seen-since is to always create or update.
-+
-+Options:
-+ key=value. Default key is $sender_host_address
-+ readonly
-+ write
-+ refresh=<interval>: update an existing DB entry older than given
-+ amount. Default refresh lacking this option is 10d.
-+ The update sets the record timestamp to the seen-test time.
-+
-+XXX do we need separate nocreate, noupdate controls?
-+
-+Arguments:
-+ arg the option string for seen=
-+ where ACL_WHERE_xxxx indicating which ACL this is
-+ log_msgptr for error messages
-+
-+Returns: OK - Condition is true
-+ FAIL - Condition is false
-+ DEFER - Problem opening history database
-+ ERROR - Syntax error in options
-+*/
-+
-+static int
-+acl_seen(const uschar * arg, int where, uschar ** log_msgptr)
-+{
-+enum { SEEN_DEFAULT, SEEN_READONLY, SEEN_WRITE };
-+
-+const uschar * list = arg;
-+int slash = '/', equal = '=', interval, mode = SEEN_DEFAULT, yield = FAIL;
-+BOOL before;
-+int refresh = 10 * 24 * 60 * 60; /* 10 days */
-+const uschar * ele, * key = sender_host_address;
-+open_db dbblock, * dbm;
-+dbdata_seen * dbd;
-+time_t now;
-+
-+/* Parse the first element, the time-relation. */
-+
-+if (!(ele = string_nextinlist(&list, &slash, NULL, 0)))
-+ goto badparse;
-+if ((before = *ele == '-'))
-+ ele++;
-+if ((interval = readconf_readtime(ele, 0, FALSE)) < 0)
-+ goto badparse;
-+
-+/* Remaining elements are options */
-+
-+while ((ele = string_nextinlist(&list, &slash, NULL, 0)))
-+ if (Ustrncmp(ele, "key=", 4) == 0)
-+ key = ele + 4;
-+ else if (Ustrcmp(ele, "readonly") == 0)
-+ mode = SEEN_READONLY;
-+ else if (Ustrcmp(ele, "write") == 0)
-+ mode = SEEN_WRITE;
-+ else if (Ustrncmp(ele, "refresh=", 8) == 0)
-+ {
-+ if ((refresh = readconf_readtime(ele + 8, 0, FALSE)) < 0)
-+ goto badparse;
-+ }
-+ else
-+ goto badopt;
-+
-+if (!(dbm = dbfn_open(US"seen", O_RDWR, &dbblock, TRUE, TRUE)))
-+ {
-+ HDEBUG(D_acl) debug_printf_indent("database for 'seen' not available\n");
-+ *log_msgptr = US"database for 'seen' not available";
-+ return DEFER;
-+ }
-+
-+dbd = dbfn_read_with_length(dbm, key, NULL);
-+now = time(NULL);
-+if (dbd) /* an existing record */
-+ {
-+ time_t diff = now - dbd->time_stamp; /* time since the record was written */
-+
-+ if (before ? diff >= interval : diff < interval)
-+ yield = OK;
-+
-+ if (mode == SEEN_READONLY)
-+ { HDEBUG(D_acl) debug_printf_indent("seen db not written (readonly)\n"); }
-+ else if (mode == SEEN_WRITE || !before)
-+ {
-+ dbd->time_stamp = now;
-+ dbfn_write(dbm, key, dbd, sizeof(*dbd));
-+ HDEBUG(D_acl) debug_printf_indent("seen db written (update)\n");
-+ }
-+ else if (diff >= refresh)
-+ {
-+ dbd->time_stamp = now - interval;
-+ dbfn_write(dbm, key, dbd, sizeof(*dbd));
-+ HDEBUG(D_acl) debug_printf_indent("seen db written (refresh)\n");
-+ }
-+ }
-+else
-+ { /* No record found, yield always FAIL */
-+ if (mode != SEEN_READONLY)
-+ {
-+ dbdata_seen d = {.time_stamp = now};
-+ dbfn_write(dbm, key, &d, sizeof(*dbd));
-+ HDEBUG(D_acl) debug_printf_indent("seen db written (create)\n");
-+ }
-+ else
-+ HDEBUG(D_acl) debug_printf_indent("seen db not written (readonly)\n");
-+ }
-+
-+dbfn_close(dbm);
-+return yield;
-+
-+
-+badparse:
-+ *log_msgptr = string_sprintf("failed to parse '%s'", arg);
-+ return ERROR;
-+badopt:
-+ *log_msgptr = string_sprintf("unrecognised option '%s' in '%s'", ele, arg);
-+ return ERROR;
-+}
-+
-+
-+
- /*************************************************
- * The udpsend ACL modifier *
- *************************************************/
-@@ -3740,6 +3879,10 @@ for (; cb; cb = cb->next)
- setup_remove_header(arg);
- break;
-
-+ case ACLC_SEEN:
-+ rc = acl_seen(arg, where, log_msgptr);
-+ break;
-+
- case ACLC_SENDER_DOMAINS:
- {
- uschar *sdomain;
-diff --git a/src/src/dbstuff.h b/src/src/dbstuff.h
-index 2f00dffb4..94db7f7fd 100644
---- a/src/src/dbstuff.h
-+++ b/src/src/dbstuff.h
-@@ -788,6 +788,12 @@ typedef struct {
- uschar bloom[40]; /* Bloom filter which may be larger than this */
- } dbdata_ratelimit_unique;
-
-+
-+/* For "seen" ACL condition */
-+typedef struct {
-+ time_t time_stamp;
-+} dbdata_seen;
-+
- #ifndef DISABLE_PIPE_CONNECT
- /* This structure records the EHLO responses, cleartext and crypted,
- for an IP, as bitmasks (cf. OPTION_TLS). For LIMITS, also values
-diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c
-index 13f74540e..45b778fc0 100644
---- a/src/src/exim_dbutil.c
-+++ b/src/src/exim_dbutil.c
-@@ -21,7 +21,9 @@ argument is the name of the database file. The available names are:
- misc: miscellaneous hints data
- wait-<t>: message waiting information; <t> is a transport name
- callout: callout verification cache
-+ ratelimit: ACL 'ratelimit' condition
- tls: TLS session resumption cache
-+ seen: ACL 'seen' condition
-
- There are a number of common subroutines, followed by three main programs,
- whose inclusion is controlled by -D on the compilation command. */
-@@ -38,6 +40,7 @@ whose inclusion is controlled by -D on the compilation command. */
- #define type_callout 4
- #define type_ratelimit 5
- #define type_tls 6
-+#define type_seen 7
-
-
- /* This is used by our cut-down dbfn_open(). */
-@@ -126,7 +129,7 @@ static void
- usage(uschar *name, uschar *options)
- {
- printf("Usage: exim_%s%s <spool-directory> <database-name>\n", name, options);
--printf(" <database-name> = retry | misc | wait-<transport-name> | callout | ratelimit | tls\n");
-+printf(" <database-name> = retry | misc | wait-<transport-name> | callout | ratelimit | tls | seen\n");
- exit(1);
- }
-
-@@ -150,6 +153,7 @@ if (argc == 3)
- if (Ustrcmp(argv[2], "callout") == 0) return type_callout;
- if (Ustrcmp(argv[2], "ratelimit") == 0) return type_ratelimit;
- if (Ustrcmp(argv[2], "tls") == 0) return type_tls;
-+ if (Ustrcmp(argv[2], "seen") == 0) return type_seen;
- }
- usage(name, options);
- return -1; /* Never obeyed */
-@@ -581,6 +585,7 @@ for (uschar * key = dbfn_scan(dbm, TRUE, &cursor);
- dbdata_ratelimit *ratelimit;
- dbdata_ratelimit_unique *rate_unique;
- dbdata_tls_session *session;
-+ dbdata_seen *seen;
- int count_bad = 0;
- int length;
- uschar *t;
-@@ -720,6 +725,11 @@ for (uschar * key = dbfn_scan(dbm, TRUE, &cursor);
- session = (dbdata_tls_session *)value;
- printf(" %s %.*s\n", keybuffer, length, session->session);
- break;
-+
-+ case type_seen:
-+ seen = (dbdata_seen *)value;
-+ printf("%s\t%s\n", keybuffer, print_time(seen->time_stamp));
-+ break;
- }
- }
- store_reset(reset_point);
-diff --git a/test/confs/0626 b/test/confs/0626
-new file mode 100644
-index 000000000..872c4b20a
---- /dev/null
-+++ b/test/confs/0626
-@@ -0,0 +1,31 @@
-+# Exim test configuration 0626
-+# ACL seen condition
-+
-+.include DIR/aux-var/std_conf_prefix
-+
-+
-+# ----- Main settings -----
-+
-+primary_hostname = test.ex
-+queue_only
-+
-+acl_smtp_rcpt = chk_rcpt
-+
-+# ----- ACL -----
-+
-+begin acl
-+
-+chk_rcpt:
-+ accept seen = OPT
-+
-+# seen = never / $sender_host_addreee / per_call
-+# seen = before=10s
-+# seen = before=10s / write
-+# seen = since / readonly
-+#
-+# seen = -10s
-+# seen = -10s / readonly
-+# seen = 2s
-+# seen = 0s / update=20d
-+#
-+# End
-diff --git a/test/scripts/0000-Basic/0626 b/test/scripts/0000-Basic/0626
-new file mode 100644
-index 000000000..6da58ee48
---- /dev/null
-+++ b/test/scripts/0000-Basic/0626
-@@ -0,0 +1,82 @@
-+# ACL 'seen' condition
-+#
-+exim -DOPT='-1s' -bh 127.0.0.1
-+HELO test
-+MAIL FROM:<tester@test.ex>
-+RCPT TO:<a1@test.ex>
-+QUIT
-+****
-+# Check that a hints DB was created.
-+# Only the key is useful thanks to munging; should match the IP used above.
-+dump seen
-+#
-+sleep 1
-+# should now see old-enough record
-+exim -DOPT='-1s' -bh 127.0.0.1
-+HELO test
-+MAIL FROM:<tester@test.ex>
-+RCPT TO:<a1@test.ex>
-+QUIT
-+****
-+# force an update (visible via debug output in stdout for -bh)
-+exim -DOPT='-1s / write' -bh 127.0.0.1
-+HELO test
-+MAIL FROM:<tester@test.ex>
-+RCPT TO:<a1@test.ex>
-+QUIT
-+****
-+# default key should change with ip
-+exim -DOPT='-1s' -bh HOSTIPV4
-+HELO test
-+MAIL FROM:<tester@test.ex>
-+RCPT TO:<a1@test.ex>
-+QUIT
-+****
-+dump seen
-+# explicit key (also checking expansion)
-+exim -DOPT='-1s / key=${sender_host_address}_foo' -bh 127.0.0.1
-+HELO test
-+MAIL FROM:<tester@test.ex>
-+RCPT TO:<a1@test.ex>
-+QUIT
-+****
-+dump seen
-+# check refresh
-+sleep 1
-+exim -DOPT='-1s / refresh=1s' -bh 127.0.0.1
-+HELO test
-+MAIL FROM:<tester@test.ex>
-+RCPT TO:<a1@test.ex>
-+QUIT
-+****
-+#
-+#
-+#
-+#
-+#
-+# test for seen-more-recently-than
-+# that previous one should be no older than 5s, so this should pass
-+# do not update
-+# check list-parsing spaceless while we're here
-+exim -DOPT='5s/key=${sender_host_address}_foo/readonly' -bh 127.0.0.1
-+HELO test
-+MAIL FROM:<tester@test.ex>
-+RCPT TO:<a1@test.ex>
-+QUIT
-+****
-+# check the above no-update by waiting longer than the later-than interval; should fail
-+# should update
-+sleep 2
-+exim -DOPT='1s / key=${sender_host_address}_foo' -bh 127.0.0.1
-+HELO test
-+MAIL FROM:<tester@test.ex>
-+RCPT TO:<a1@test.ex>
-+QUIT
-+****
-+# having updated, should pass
-+exim -DOPT='1s / key=${sender_host_address}_foo' -bh 127.0.0.1
-+HELO test
-+MAIL FROM:<tester@test.ex>
-+RCPT TO:<a1@test.ex>
-+QUIT
-+****
-diff --git a/test/stderr/0626 b/test/stderr/0626
-new file mode 100644
-index 000000000..25e96bc4e
---- /dev/null
-+++ b/test/stderr/0626
-@@ -0,0 +1,142 @@
-+>>> host in hosts_connection_nolog? no (option unset)
-+>>> host in host_lookup? no (option unset)
-+>>> host in host_reject_connection? no (option unset)
-+>>> host in sender_unqualified_hosts? no (option unset)
-+>>> host in recipient_unqualified_hosts? no (option unset)
-+>>> host in helo_verify_hosts? no (option unset)
-+>>> host in helo_try_verify_hosts? no (option unset)
-+>>> host in helo_accept_junk_hosts? no (option unset)
-+>>> test in helo_lookup_domains? no (end of list)
-+>>> using ACL "chk_rcpt"
-+>>> processing "accept" (TESTSUITE/test-config 19)
-+>>> check seen = -1s
-+>>> seen db written (create)
-+>>> accept: condition test failed in ACL "chk_rcpt"
-+>>> end of ACL "chk_rcpt": implicit DENY
-+LOG: H=(test) [127.0.0.1] F=<tester@test.ex> rejected RCPT <a1@test.ex>
-+>>> host in hosts_connection_nolog? no (option unset)
-+>>> host in host_lookup? no (option unset)
-+>>> host in host_reject_connection? no (option unset)
-+>>> host in sender_unqualified_hosts? no (option unset)
-+>>> host in recipient_unqualified_hosts? no (option unset)
-+>>> host in helo_verify_hosts? no (option unset)
-+>>> host in helo_try_verify_hosts? no (option unset)
-+>>> host in helo_accept_junk_hosts? no (option unset)
-+>>> test in helo_lookup_domains? no (end of list)
-+>>> using ACL "chk_rcpt"
-+>>> processing "accept" (TESTSUITE/test-config 19)
-+>>> check seen = -1s
-+>>> accept: condition test succeeded in ACL "chk_rcpt"
-+>>> end of ACL "chk_rcpt": ACCEPT
-+>>> host in hosts_connection_nolog? no (option unset)
-+>>> host in host_lookup? no (option unset)
-+>>> host in host_reject_connection? no (option unset)
-+>>> host in sender_unqualified_hosts? no (option unset)
-+>>> host in recipient_unqualified_hosts? no (option unset)
-+>>> host in helo_verify_hosts? no (option unset)
-+>>> host in helo_try_verify_hosts? no (option unset)
-+>>> host in helo_accept_junk_hosts? no (option unset)
-+>>> test in helo_lookup_domains? no (end of list)
-+>>> using ACL "chk_rcpt"
-+>>> processing "accept" (TESTSUITE/test-config 19)
-+>>> check seen = -1s / write
-+>>> seen db written (update)
-+>>> accept: condition test succeeded in ACL "chk_rcpt"
-+>>> end of ACL "chk_rcpt": ACCEPT
-+>>> host in hosts_connection_nolog? no (option unset)
-+>>> host in host_lookup? no (option unset)
-+>>> host in host_reject_connection? no (option unset)
-+>>> host in sender_unqualified_hosts? no (option unset)
-+>>> host in recipient_unqualified_hosts? no (option unset)
-+>>> host in helo_verify_hosts? no (option unset)
-+>>> host in helo_try_verify_hosts? no (option unset)
-+>>> host in helo_accept_junk_hosts? no (option unset)
-+>>> test in helo_lookup_domains? no (end of list)
-+>>> using ACL "chk_rcpt"
-+>>> processing "accept" (TESTSUITE/test-config 19)
-+>>> check seen = -1s
-+>>> seen db written (create)
-+>>> accept: condition test failed in ACL "chk_rcpt"
-+>>> end of ACL "chk_rcpt": implicit DENY
-+LOG: H=(test) [ip4.ip4.ip4.ip4] F=<tester@test.ex> rejected RCPT <a1@test.ex>
-+>>> host in hosts_connection_nolog? no (option unset)
-+>>> host in host_lookup? no (option unset)
-+>>> host in host_reject_connection? no (option unset)
-+>>> host in sender_unqualified_hosts? no (option unset)
-+>>> host in recipient_unqualified_hosts? no (option unset)
-+>>> host in helo_verify_hosts? no (option unset)
-+>>> host in helo_try_verify_hosts? no (option unset)
-+>>> host in helo_accept_junk_hosts? no (option unset)
-+>>> test in helo_lookup_domains? no (end of list)
-+>>> using ACL "chk_rcpt"
-+>>> processing "accept" (TESTSUITE/test-config 19)
-+>>> check seen = -1s / key=${sender_host_address}_foo
-+>>> = -1s / key=127.0.0.1_foo
-+>>> seen db written (create)
-+>>> accept: condition test failed in ACL "chk_rcpt"
-+>>> end of ACL "chk_rcpt": implicit DENY
-+LOG: H=(test) [127.0.0.1] F=<tester@test.ex> rejected RCPT <a1@test.ex>
-+>>> host in hosts_connection_nolog? no (option unset)
-+>>> host in host_lookup? no (option unset)
-+>>> host in host_reject_connection? no (option unset)
-+>>> host in sender_unqualified_hosts? no (option unset)
-+>>> host in recipient_unqualified_hosts? no (option unset)
-+>>> host in helo_verify_hosts? no (option unset)
-+>>> host in helo_try_verify_hosts? no (option unset)
-+>>> host in helo_accept_junk_hosts? no (option unset)
-+>>> test in helo_lookup_domains? no (end of list)
-+>>> using ACL "chk_rcpt"
-+>>> processing "accept" (TESTSUITE/test-config 19)
-+>>> check seen = -1s / refresh=1s
-+>>> seen db written (refresh)
-+>>> accept: condition test succeeded in ACL "chk_rcpt"
-+>>> end of ACL "chk_rcpt": ACCEPT
-+>>> host in hosts_connection_nolog? no (option unset)
-+>>> host in host_lookup? no (option unset)
-+>>> host in host_reject_connection? no (option unset)
-+>>> host in sender_unqualified_hosts? no (option unset)
-+>>> host in recipient_unqualified_hosts? no (option unset)
-+>>> host in helo_verify_hosts? no (option unset)
-+>>> host in helo_try_verify_hosts? no (option unset)
-+>>> host in helo_accept_junk_hosts? no (option unset)
-+>>> test in helo_lookup_domains? no (end of list)
-+>>> using ACL "chk_rcpt"
-+>>> processing "accept" (TESTSUITE/test-config 19)
-+>>> check seen = 5s/key=${sender_host_address}_foo/readonly
-+>>> = 5s/key=127.0.0.1_foo/readonly
-+>>> seen db not written (readonly)
-+>>> accept: condition test succeeded in ACL "chk_rcpt"
-+>>> end of ACL "chk_rcpt": ACCEPT
-+>>> host in hosts_connection_nolog? no (option unset)
-+>>> host in host_lookup? no (option unset)
-+>>> host in host_reject_connection? no (option unset)
-+>>> host in sender_unqualified_hosts? no (option unset)
-+>>> host in recipient_unqualified_hosts? no (option unset)
-+>>> host in helo_verify_hosts? no (option unset)
-+>>> host in helo_try_verify_hosts? no (option unset)
-+>>> host in helo_accept_junk_hosts? no (option unset)
-+>>> test in helo_lookup_domains? no (end of list)
-+>>> using ACL "chk_rcpt"
-+>>> processing "accept" (TESTSUITE/test-config 19)
-+>>> check seen = 1s / key=${sender_host_address}_foo
-+>>> = 1s / key=127.0.0.1_foo
-+>>> seen db written (update)
-+>>> accept: condition test failed in ACL "chk_rcpt"
-+>>> end of ACL "chk_rcpt": implicit DENY
-+LOG: H=(test) [127.0.0.1] F=<tester@test.ex> rejected RCPT <a1@test.ex>
-+>>> host in hosts_connection_nolog? no (option unset)
-+>>> host in host_lookup? no (option unset)
-+>>> host in host_reject_connection? no (option unset)
-+>>> host in sender_unqualified_hosts? no (option unset)
-+>>> host in recipient_unqualified_hosts? no (option unset)
-+>>> host in helo_verify_hosts? no (option unset)
-+>>> host in helo_try_verify_hosts? no (option unset)
-+>>> host in helo_accept_junk_hosts? no (option unset)
-+>>> test in helo_lookup_domains? no (end of list)
-+>>> using ACL "chk_rcpt"
-+>>> processing "accept" (TESTSUITE/test-config 19)
-+>>> check seen = 1s / key=${sender_host_address}_foo
-+>>> = 1s / key=127.0.0.1_foo
-+>>> seen db written (update)
-+>>> accept: condition test succeeded in ACL "chk_rcpt"
-+>>> end of ACL "chk_rcpt": ACCEPT
-diff --git a/test/stdout/0626 b/test/stdout/0626
-new file mode 100644
-index 000000000..44b481f31
---- /dev/null
-+++ b/test/stdout/0626
-@@ -0,0 +1,99 @@
-+
-+**** SMTP testing session as if from host 127.0.0.1
-+**** but without any ident (RFC 1413) callback.
-+**** This is not for real!
-+
-+220 test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
-+250 test.ex Hello test [127.0.0.1]\r
-+250 OK\r
-+550 Administrative prohibition\r
-+221 test.ex closing connection\r
-++++++++++++++++++++++++++++
-+127.0.0.1 07-Mar-2000 12:21:52
-+
-+**** SMTP testing session as if from host 127.0.0.1
-+**** but without any ident (RFC 1413) callback.
-+**** This is not for real!
-+
-+220 test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
-+250 test.ex Hello test [127.0.0.1]\r
-+250 OK\r
-+250 Accepted\r
-+221 test.ex closing connection\r
-+
-+**** SMTP testing session as if from host 127.0.0.1
-+**** but without any ident (RFC 1413) callback.
-+**** This is not for real!
-+
-+220 test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
-+250 test.ex Hello test [127.0.0.1]\r
-+250 OK\r
-+250 Accepted\r
-+221 test.ex closing connection\r
-+
-+**** SMTP testing session as if from host ip4.ip4.ip4.ip4
-+**** but without any ident (RFC 1413) callback.
-+**** This is not for real!
-+
-+220 test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
-+250 test.ex Hello test [ip4.ip4.ip4.ip4]\r
-+250 OK\r
-+550 Administrative prohibition\r
-+221 test.ex closing connection\r
-++++++++++++++++++++++++++++
-+ip4.ip4.ip4.ip4 07-Mar-2000 12:21:52
-+127.0.0.1 07-Mar-2000 12:21:52
-+
-+**** SMTP testing session as if from host 127.0.0.1
-+**** but without any ident (RFC 1413) callback.
-+**** This is not for real!
-+
-+220 test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
-+250 test.ex Hello test [127.0.0.1]\r
-+250 OK\r
-+550 Administrative prohibition\r
-+221 test.ex closing connection\r
-++++++++++++++++++++++++++++
-+127.0.0.1_foo 07-Mar-2000 12:21:52
-+ip4.ip4.ip4.ip4 07-Mar-2000 12:21:52
-+127.0.0.1 07-Mar-2000 12:21:52
-+
-+**** SMTP testing session as if from host 127.0.0.1
-+**** but without any ident (RFC 1413) callback.
-+**** This is not for real!
-+
-+220 test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
-+250 test.ex Hello test [127.0.0.1]\r
-+250 OK\r
-+250 Accepted\r
-+221 test.ex closing connection\r
-+
-+**** SMTP testing session as if from host 127.0.0.1
-+**** but without any ident (RFC 1413) callback.
-+**** This is not for real!
-+
-+220 test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
-+250 test.ex Hello test [127.0.0.1]\r
-+250 OK\r
-+250 Accepted\r
-+221 test.ex closing connection\r
-+
-+**** SMTP testing session as if from host 127.0.0.1
-+**** but without any ident (RFC 1413) callback.
-+**** This is not for real!
-+
-+220 test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
-+250 test.ex Hello test [127.0.0.1]\r
-+250 OK\r
-+550 Administrative prohibition\r
-+221 test.ex closing connection\r
-+
-+**** SMTP testing session as if from host 127.0.0.1
-+**** but without any ident (RFC 1413) callback.
-+**** This is not for real!
-+
-+220 test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
-+250 test.ex Hello test [127.0.0.1]\r
-+250 OK\r
-+250 Accepted\r
-+221 test.ex closing connection\r