Merge branch 'master' into synth-relay

This commit is contained in:
David Bürgin 2021-12-23 20:57:44 +01:00
commit f639e0313e
27 changed files with 115 additions and 94 deletions

View file

@ -1,11 +1,7 @@
image: rust
before_script:
- apt-get --assume-yes update
# TODO Replace opendkim-tools with miltertest in next Debian stable.
- apt-get --assume-yes install pkg-config libmilter-dev spamc opendkim-tools
- apt-get --assume-yes install pkg-config libmilter-dev spamc miltertest
test:
script:
# TODO Dont run integration tests for now. The miltertest tests depend on
# a patch only available in Debian testing. Update once next Debian stable
# is released.
- PKG_CONFIG_PATH=. cargo test --verbose --lib
- cargo test --verbose

View file

@ -1,11 +1,17 @@
# SpamAssassin Milter changelog
## 0.2.1 (unreleased)
## 0.2.2 (unreleased)
* Add `--synth-relay` option to allow inserting the synthesised internal relay
header (the MTAs `Received` header) at a different position than the at the
header (the MTAs `Received` header) at a different position than at the
very beginning.
## 0.2.1 (2021-12-23)
* Various cosmetic improvements in code and tests, and updates to
documentation.
* Update dependencies.
## 0.2.0 (2021-08-26)
* Bump minimum supported Rust version to 1.46.0.

50
Cargo.lock generated
View file

@ -13,9 +13,9 @@ dependencies = [
[[package]]
name = "ansi_term"
version = "0.11.0"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
@ -58,9 +58,9 @@ dependencies = [
[[package]]
name = "clap"
version = "2.33.3"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
@ -88,9 +88,9 @@ checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9"
[[package]]
name = "libc"
version = "0.2.99"
version = "0.2.112"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765"
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
[[package]]
name = "memchr"
@ -100,9 +100,9 @@ checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
[[package]]
name = "milter"
version = "0.2.3"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92e63d2a1125192952a422c891f6860ee48af6e256c07a48792d181e2752e20d"
checksum = "6f8303a6ac7b50962cb1a24ea9f35fd336dc680a628e167962adee4a9a1babf3"
dependencies = [
"bitflags",
"libc",
@ -113,9 +113,9 @@ dependencies = [
[[package]]
name = "milter-callback"
version = "0.2.3"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bf7ad54ab8a2d8f324b5628140a585cfae22081d7bde7ebdc11bb7568e890ea"
checksum = "6288a38fe087aff33e732c50d4eb05a782a19eef0f29eaf1f9b931ba9ef37f1e"
dependencies = [
"proc-macro2",
"quote",
@ -124,9 +124,9 @@ dependencies = [
[[package]]
name = "milter-sys"
version = "0.2.2"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6a5a6fe12debcf5428e77f5c0e569444a131c8c6043b1e2d1513680fa93cd25"
checksum = "46d0d54709e88c3b120d9c7bc90555bd5a1584c19f78ca15cd262d4de7faf34c"
dependencies = [
"libc",
"pkg-config",
@ -153,30 +153,30 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.8.0"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[package]]
name = "pkg-config"
version = "0.3.19"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
[[package]]
name = "proc-macro2"
version = "1.0.28"
version = "1.0.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612"
checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.9"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
dependencies = [
"proc-macro2",
]
@ -200,7 +200,7 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "spamassassin-milter"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"chrono",
"clap",
@ -219,9 +219,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
version = "1.0.74"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c"
checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
dependencies = [
"proc-macro2",
"quote",
@ -250,9 +250,9 @@ dependencies = [
[[package]]
name = "unicode-width"
version = "0.1.8"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "unicode-xid"

View file

@ -1,7 +1,8 @@
[package]
name = "spamassassin-milter"
version = "0.2.0"
version = "0.2.1"
edition = "2018"
rust-version = "1.46.0"
description = "Milter for spam filtering with SpamAssassin"
license = "GPL-3.0-or-later"
categories = ["email"]

View file

@ -55,7 +55,7 @@ provided `milter.pc` file. Put this file on the pkg-config path when running any
Cargo command:
```
PKG_CONFIG_PATH=. cargo build
PKG_CONFIG_PATH=/path/to/milter.pc cargo build
```
SpamAssassin Milter uses the `spamc` program for communication with SpamAssassin
@ -151,6 +151,12 @@ property. The classification threshold can be adjusted by setting
required_score 9.0
```
In SpamAssassin Milter, the `--reject-spam` option will cause messages flagged
as spam to be rejected during the SMTP conversation. When rejecting spam in this
manner, accepted (not rejected) messages will not have the `X-Spam-Flag: YES`
header; if you need to make a further decision about the message coming through,
look at the score in the `X-Spam-Level` header instead.
By default, SpamAssassin creates reports for messages it recognises as spam.
These reports replace the message body, that is, the message body is rewritten
to present a report instead of the original message, and the original message is
@ -244,7 +250,8 @@ on those.
As an example, in case [Dovecot] does mail delivery using [LMTP], enable the
Sieve plugin for the LMTP protocol, and then set up a global Sieve script that
files messages flagged as spam into the Junk folder:
files messages flagged as spam into the Junk folder. That is, test for the
header `X-Spam-Flag: YES`:
```
require "fileinto";
@ -255,6 +262,10 @@ if header "X-Spam-Flag" "YES" {
}
```
Alternatively, use the value of the `X-Spam-Level` header instead. In the
example above, the test `header :contains "X-Spam-Level" "*****"` would file
messages with score 5.0 or above into Junk.
[Sieve]: http://sieve.info
[Dovecot]: https://dovecot.org
[LMTP]: https://doc.dovecot.org/configuration_manual/protocols/lmtp_server

View file

@ -1,4 +1,4 @@
.TH SPAMASSASSIN-MILTER 8 2021-08-26
.TH SPAMASSASSIN-MILTER 8 2021-12-23
.SH NAME
spamassassin-milter \- milter for spam filtering with SpamAssassin
.SH SYNOPSIS

View file

@ -48,7 +48,8 @@ fn handle_negotiate(
}
}
let req_protocol_opts = ProtocolOpts::SKIP | ProtocolOpts::HEADER_LEADING_SPACE;
let req_protocol_opts =
ProtocolOpts::NO_UNKNOWN | ProtocolOpts::SKIP | ProtocolOpts::HEADER_LEADING_SPACE;
assert!(actions.contains(req_actions), "required milter actions not supported");
assert!(protocol_opts.contains(req_protocol_opts), "required milter protocol options not supported");

View file

@ -319,7 +319,7 @@ impl Client {
}
}
Ok(Status::Accept)
Ok(Status::Continue)
}
}
@ -435,27 +435,27 @@ mod tests {
impl ActionContext for MockActionContext {
fn add_header(&self, name: &str, value: &str) -> milter::Result<()> {
Ok(self.called.borrow_mut().push(
Action::AddHeader(name.to_owned(), value.to_owned())
))
let action = Action::AddHeader(name.to_owned(), value.to_owned());
self.called.borrow_mut().push(action);
Ok(())
}
fn insert_header(&self, index: usize, name: &str, value: &str) -> milter::Result<()> {
Ok(self.called.borrow_mut().push(
Action::InsertHeader(index, name.to_owned(), value.to_owned())
))
let action = Action::InsertHeader(index, name.to_owned(), value.to_owned());
self.called.borrow_mut().push(action);
Ok(())
}
fn replace_header(&self, name: &str, index: usize, value: Option<&str>) -> milter::Result<()> {
Ok(self.called.borrow_mut().push(
Action::ReplaceHeader(name.to_owned(), index, value.map(|v| v.to_owned()))
))
let action = Action::ReplaceHeader(name.to_owned(), index, value.map(|v| v.to_owned()));
self.called.borrow_mut().push(action);
Ok(())
}
fn append_body_chunk(&self, bytes: &[u8]) -> milter::Result<()> {
Ok(self.called.borrow_mut().push(
Action::AppendBodyChunk(bytes.to_owned())
))
let action = Action::AppendBodyChunk(bytes.to_owned());
self.called.borrow_mut().push(action);
Ok(())
}
fn replace_sender(&self, _: &str, _: Option<&str>) -> milter::Result<()> {
@ -486,11 +486,13 @@ mod tests {
ext_code: Option<&str>,
msg_lines: Vec<&str>,
) -> milter::Result<()> {
Ok(self.called.borrow_mut().push(Action::SetErrorReply(
let action = Action::SetErrorReply(
code.to_owned(),
ext_code.map(|c| c.to_owned()),
msg_lines.into_iter().map(|l| l.to_owned()).collect(),
)))
);
self.called.borrow_mut().push(action);
Ok(())
}
}
@ -590,7 +592,7 @@ mod tests {
let status = client.process("id", &actions, &config).unwrap();
assert_eq!(status, Status::Accept);
assert_eq!(status, Status::Continue);
assert_eq!(
actions.called.borrow().as_slice(),
[
@ -616,7 +618,7 @@ mod tests {
let status = client.process("id", &actions, &config).unwrap();
assert_eq!(status, Status::Accept);
assert_eq!(status, Status::Continue);
let called = actions.called.borrow();
assert!(called.contains(&Action::InsertHeader(0, "X-Spam-Level".into(), " *****".into())));

View file

@ -74,7 +74,7 @@ fn header_lines(header: &[u8]) -> Vec<&[u8]> {
fn parse_header_line(bytes: &[u8]) -> Result<Header<'_>> {
// This assumes that headers received back from SpamAssassin are valid
// UTF-8, which is plausible since the client only sent UTF-8 earlier.
// UTF-8, which should be the case since the client only sent UTF-8 earlier.
let line = str::from_utf8(bytes).map_err(|_| Error::ParseEmail)?;
let (name, value) = line.split_at(line.find(':').ok_or(Error::ParseEmail)?);
@ -235,12 +235,13 @@ impl<'a, 'c> HeaderRewriter<'a, 'c> {
id: &str,
actions: &impl ActionContext,
) -> milter::Result<()> {
let mods = self.spam_assassin_mods.iter();
if self.prepend.unwrap_or(false) {
// Prepend X-Spam- headers in reverse order, so that they appear
// in the order received from SpamAssassin.
execute_mods(id, self.spam_assassin_mods.iter().rev(), actions, self.config)?;
execute_mods(id, mods.rev(), actions, self.config)?;
} else {
execute_mods(id, self.spam_assassin_mods.iter(), actions, self.config)?;
execute_mods(id, mods, actions, self.config)?;
}
// Delete all incoming X-Spam- headers not returned by SpamAssassin to
@ -370,9 +371,9 @@ mod tests {
#[test]
fn email_split_at_eoh() {
assert_eq!(split_at_eoh(b"x\r\n\r\ny"), Ok((b"x\r\n" as &[_], b"y" as &[_])));
assert_eq!(split_at_eoh(b"x\r\n\r\n"), Ok((b"x\r\n" as &[_], b"" as &[_])));
assert_eq!(split_at_eoh(b"\r\n\r\ny"), Ok((b"\r\n" as &[_], b"y" as &[_])));
assert_eq!(split_at_eoh(b"x\r\n\r\ny"), Ok((&b"x\r\n"[..], &b"y"[..])));
assert_eq!(split_at_eoh(b"x\r\n\r\n"), Ok((&b"x\r\n"[..], &b""[..])));
assert_eq!(split_at_eoh(b"\r\n\r\ny"), Ok((&b"\r\n"[..], &b"y"[..])));
assert_eq!(split_at_eoh(b"\r\ny"), Err(Error::ParseEmail));
assert_eq!(split_at_eoh(b"y"), Err(Error::ParseEmail));
}
@ -380,25 +381,25 @@ mod tests {
#[test]
fn email_header_lines_empty() {
assert_eq!(header_lines(b""), Vec::<&[_]>::new());
assert_eq!(header_lines(b"\r\n"), vec![b"" as &[_]]);
assert_eq!(header_lines(b"\r\n\r\n"), vec![b"" as &[_], b"" as &[_]]);
assert_eq!(header_lines(b"\r\n"), vec![&b""[..]]);
assert_eq!(header_lines(b"\r\n\r\n"), vec![&b""[..], &b""[..]]);
}
#[test]
fn email_header_lines_simple() {
assert_eq!(header_lines(b"x\r\n"), vec![b"x" as &[_]]);
assert_eq!(header_lines(b"x\r\ny"), vec![b"x" as &[_], b"y" as &[_]]);
assert_eq!(header_lines(b"x\r\ny\r\n"), vec![b"x" as &[_], b"y" as &[_]]);
assert_eq!(header_lines(b"x\r\n"), vec![&b"x"[..]]);
assert_eq!(header_lines(b"x\r\ny"), vec![&b"x"[..], &b"y"[..]]);
assert_eq!(header_lines(b"x\r\ny\r\n"), vec![&b"x"[..], &b"y"[..]]);
}
#[test]
fn email_header_lines_multi() {
assert_eq!(header_lines(b"x\r\n\t"), vec![b"x\r\n\t" as &[_]]);
assert_eq!(header_lines(b"x\r\n\ty"), vec![b"x\r\n\ty" as &[_]]);
assert_eq!(header_lines(b"x\r\n\ty\r\n"), vec![b"x\r\n\ty" as &[_]]);
assert_eq!(header_lines(b"x\r\n\t"), vec![&b"x\r\n\t"[..]]);
assert_eq!(header_lines(b"x\r\n\ty"), vec![&b"x\r\n\ty"[..]]);
assert_eq!(header_lines(b"x\r\n\ty\r\n"), vec![&b"x\r\n\ty"[..]]);
assert_eq!(
header_lines(b"x\r\n\ty\r\n\tz\r\nq"),
vec![b"x\r\n\ty\r\n\tz" as &[_], b"q" as &[_]]
vec![&b"x\r\n\ty\r\n\tz"[..], &b"q"[..]]
);
}

View file

@ -1,6 +1,6 @@
-- An authenticated sender is accepted, the message is not processed.
conn = mt.connect("inet:3333@localhost")
local conn = mt.connect("inet:3333@localhost")
assert(conn, "could not open connection")
local err = mt.conninfo(conn, "client.gluet.ch", "123.123.123.123")

View file

@ -1,6 +1,6 @@
mod common;
pub use common::*; // `pub` only to silence unused code warnings
pub use common::*;
use spamassassin_milter::*;
#[test]

View file

@ -1,6 +1,6 @@
-- Happy path processing of an ordinary ham (not spam) message.
conn = mt.connect("inet:3333@localhost")
local conn = mt.connect("inet:3333@localhost")
assert(conn, "could not open connection")
local err = mt.conninfo(conn, "client.gluet.ch", "123.123.123.123")
@ -65,7 +65,7 @@ assert(mt.getreply(conn) == SMFIR_CONTINUE)
local err = mt.eom(conn)
assert(err == nil, err)
assert(mt.getreply(conn) == SMFIR_ACCEPT)
assert(mt.getreply(conn) == SMFIR_CONTINUE)
assert(mt.eom_check(conn, MT_HDRINSERT, "X-Spam-Custom", " Custom-Value"))
assert(mt.eom_check(conn, MT_HDRDELETE, "X-Spam-Checker-Version"))

View file

@ -1,6 +1,6 @@
mod common;
pub use common::*; // `pub` only to silence unused code warnings
pub use common::*;
use spamassassin_milter::*;
#[test]
@ -23,6 +23,7 @@ fn ham_message() {
Ok(ham)
});
let miltertest = spawn_miltertest_runner(file!());
run("inet:3333@localhost", config).expect("milter execution failed");

View file

@ -1,6 +1,6 @@
-- Live test against a real SpamAssassin server.
conn = mt.connect("inet:3333@localhost")
local conn = mt.connect("inet:3333@localhost")
assert(conn, "could not open connection")
local err = mt.conninfo(conn, "client.gluet.ch", "123.123.123.123")
@ -65,7 +65,7 @@ assert(mt.getreply(conn) == SMFIR_CONTINUE)
local err = mt.eom(conn)
assert(err == nil, err)
assert(mt.getreply(conn) == SMFIR_ACCEPT)
assert(mt.getreply(conn) == SMFIR_CONTINUE)
local err = mt.disconnect(conn)
assert(err == nil, err)

View file

@ -1,6 +1,6 @@
mod common;
pub use common::*; // `pub` only to silence unused code warnings
pub use common::*;
use spamassassin_milter::*;
/// Runs a live test against a real SpamAssassin server instance. This test is

View file

@ -1,6 +1,6 @@
-- 1) A connection from the loopback IP address is accepted.
conn = mt.connect("inet:3333@localhost")
local conn = mt.connect("inet:3333@localhost")
assert(conn, "could not open connection")
local err = mt.conninfo(conn, nil, "127.0.0.1")
@ -13,7 +13,7 @@ assert(err == nil, err)
-- 2) A connection from an unknown IP address (for example, from a UNIX
-- domain socket) is also accepted.
conn = mt.connect("inet:3333@localhost")
local conn = mt.connect("inet:3333@localhost")
assert(conn, "could not open connection")
local err = mt.conninfo(conn, nil, "unspec")

View file

@ -1,6 +1,6 @@
mod common;
pub use common::*; // `pub` only to silence unused code warnings
pub use common::*;
use spamassassin_milter::*;
#[test]

View file

@ -1,6 +1,6 @@
-- A spam message is rejected with an SMTP error reply.
conn = mt.connect("inet:3333@localhost")
local conn = mt.connect("inet:3333@localhost")
assert(conn, "could not open connection")
local err = mt.conninfo(conn, "client.gluet.ch", "123.123.123.123")
@ -56,7 +56,7 @@ local err = mt.bodystring(conn, "Test message body")
assert(err == nil, err)
assert(mt.getreply(conn) == SMFIR_CONTINUE)
-- A `miltertest` (or milter protocol?) pitfall: Even though we return the
-- A `miltertest` (or milter protocol) pitfall: Even though we return the
-- `SMFIR_REJECT` status in the application code, because we use a custom error
-- reply, we must check for `SMFIR_REPLYCODE` instead.
local err = mt.eom(conn)

View file

@ -1,6 +1,6 @@
mod common;
pub use common::*; // `pub` only to silence unused code warnings
pub use common::*;
use spamassassin_milter::*;
#[test]
@ -16,6 +16,7 @@ fn reject_spam() {
let server = spawn_mock_spamd_server(SPAMD_PORT, |spam| {
Err(spam.replacen("\r\n\r\n", "\r\nX-Spam-Flag: YES\r\n\r\n", 1))
});
let miltertest = spawn_miltertest_runner(file!());
run("inet:3333@localhost", config).expect("milter execution failed");

View file

@ -2,7 +2,7 @@
-- reached, the rest is skipped (oversized messages are not processed by
-- SpamAssassin, so it is futile to send the whole message in this case).
conn = mt.connect("inet:3333@localhost")
local conn = mt.connect("inet:3333@localhost")
assert(conn, "could not open connection")
local err = mt.conninfo(conn, "client.gluet.ch", "123.123.123.123")
@ -72,7 +72,7 @@ assert(mt.getreply(conn) == SMFIR_SKIP)
local err = mt.eom(conn)
assert(err == nil, err)
assert(mt.getreply(conn) == SMFIR_ACCEPT)
assert(mt.getreply(conn) == SMFIR_CONTINUE)
local err = mt.disconnect(conn)
assert(err == nil, err)

View file

@ -1,6 +1,6 @@
mod common;
pub use common::*; // `pub` only to silence unused code warnings
pub use common::*;
use spamassassin_milter::*;
#[test]

View file

@ -1,6 +1,6 @@
-- Happy path processing of a spam message.
conn = mt.connect("inet:3333@localhost")
local conn = mt.connect("inet:3333@localhost")
assert(conn, "could not open connection")
local err = mt.conninfo(conn, "client.gluet.ch", "123.123.123.123")
@ -65,7 +65,7 @@ assert(mt.getreply(conn) == SMFIR_CONTINUE)
local err = mt.eom(conn)
assert(err == nil, err)
assert(mt.getreply(conn) == SMFIR_ACCEPT)
assert(mt.getreply(conn) == SMFIR_CONTINUE)
assert(mt.eom_check(conn, MT_HDRDELETE, "X-Spam-Checker-Version"))
assert(mt.eom_check(conn, MT_HDRADD, "X-Spam-Checker-Version", " MyChecker 1.0.0"))

View file

@ -1,6 +1,6 @@
mod common;
pub use common::*; // `pub` only to silence unused code warnings
pub use common::*;
use spamassassin_milter::*;
#[test]
@ -37,6 +37,7 @@ fn spam_message() {
Err(spam)
});
let miltertest = spawn_miltertest_runner(file!());
run("inet:3333@localhost", config).expect("milter execution failed");

View file

@ -1,6 +1,6 @@
-- When no `spamd` server is available, `spamc` fails to connect.
conn = mt.connect("inet:3333@localhost")
local conn = mt.connect("inet:3333@localhost")
assert(conn, "could not open connection")
local err = mt.conninfo(conn, "client.gluet.ch", "123.123.123.123")

View file

@ -1,6 +1,6 @@
mod common;
pub use common::*; // `pub` only to silence unused code warnings
pub use common::*;
use spamassassin_milter::*;
#[test]

View file

@ -1,6 +1,6 @@
-- A connection from a trusted network is accepted.
conn = mt.connect("inet:3333@localhost")
local conn = mt.connect("inet:3333@localhost")
assert(conn, "could not open connection")
local err = mt.conninfo(conn, "client.gluet.ch", "123.123.123.123")

View file

@ -1,6 +1,6 @@
mod common;
pub use common::*; // `pub` only to silence unused code warnings
pub use common::*;
use spamassassin_milter::*;
#[test]