Add --reply-text option

This commit is contained in:
David Bürgin 2020-06-14 09:53:08 +02:00
parent 40b9f4920c
commit 17eeb8da4b
7 changed files with 44 additions and 5 deletions

View file

@ -1,5 +1,10 @@
# SpamAssassin Milter changelog
## unreleased
* Add `--reply-text` option to allow customising the reply text when rejecting
spam.
## 0.1.2 (2020-06-07)
* Bump milter dependency to version 0.2.1.

View file

@ -7,6 +7,7 @@ spamassassin-milter \- milter for spam filtering with SpamAssassin
.RB [ \-B ]
.RB [ \-H ]
.RB [ \-n ]
[\fB\-R\fR \fIMSG\fR]
.RB [ \-r ]
[\fB\-s\fR \fIBYTES\fR]
[\fB\-t\fR \fINETS\fR]
@ -114,6 +115,12 @@ Reject messages flagged as spam at the SMTP level.
Rejection results in a permanent SMTP error being returned to the client, and
the message is not delivered.
.TP
.BR \-R ", " \-\-reply-text " \fIMSG\fR"
Reply with SMTP reply text
.I MSG
when rejecting a message flagged as spam.
For multiline replies, use an ASCII newline character as the line separator.
.TP
.BR \-t ", " \-\-trusted-networks " \fINETS\fR"
Trust connections coming from the IP networks or addresses
.IR NETS .

View file

@ -266,8 +266,8 @@ fn reject_spam(id: &str, actions: &impl SetErrorReply, config: &Config) -> milte
Status::Accept
} else {
// These reply codes are the most appropriate according to RFCs 5321 and
// 3463. The text is kept generic and makes no mention of SpamAssassin.
actions.set_error_reply("550", Some("5.7.1"), vec!["Spam message refused"])?;
// 3463. The default text is generic and does not mention SpamAssassin.
actions.set_error_reply("550", Some("5.7.1"), config.reply_text().lines().collect())?;
verbose!(config, "{}: rejected message flagged as spam", id);
Status::Reject

View file

@ -13,6 +13,7 @@ pub struct ConfigBuilder {
max_message_size: usize,
dry_run: bool,
reject_spam: bool,
reply_text: String,
preserve_headers: bool,
preserve_body: bool,
verbose: bool,
@ -64,6 +65,11 @@ impl ConfigBuilder {
self
}
pub fn reply_text(&mut self, value: String) -> &mut Self {
self.reply_text = value;
self
}
pub fn preserve_headers(&mut self, value: bool) -> &mut Self {
self.preserve_headers = value;
self
@ -94,6 +100,7 @@ impl ConfigBuilder {
max_message_size: self.max_message_size,
dry_run: self.dry_run,
reject_spam: self.reject_spam,
reply_text: self.reply_text,
preserve_headers: self.preserve_headers,
preserve_body: self.preserve_body,
verbose: self.verbose,
@ -112,6 +119,8 @@ impl Default for ConfigBuilder {
max_message_size: 512_000, // same as in `spamc`
dry_run: Default::default(),
reject_spam: Default::default(),
// Generic default reply text that makes no mention of SpamAssassin.
reply_text: String::from("Spam message refused"),
preserve_headers: Default::default(),
preserve_body: Default::default(),
verbose: Default::default(),
@ -130,6 +139,7 @@ pub struct Config {
max_message_size: usize,
dry_run: bool,
reject_spam: bool,
reply_text: String,
preserve_headers: bool,
preserve_body: bool,
verbose: bool,
@ -172,6 +182,10 @@ impl Config {
self.reject_spam
}
pub fn reply_text(&self) -> &str {
&self.reply_text
}
pub fn preserve_headers(&self) -> bool {
self.preserve_headers
}

View file

@ -9,6 +9,7 @@ const ARG_MILTER_DEBUG_LEVEL: &str = "MILTER_DEBUG_LEVEL";
const ARG_PRESERVE_BODY: &str = "PRESERVE_BODY";
const ARG_PRESERVE_HEADERS: &str = "PRESERVE_HEADERS";
const ARG_REJECT_SPAM: &str = "REJECT_SPAM";
const ARG_REPLY_TEXT: &str = "REPLY_TEXT";
const ARG_TRUSTED_NETWORKS: &str = "TRUSTED_NETWORKS";
const ARG_VERBOSE: &str = "VERBOSE";
const ARG_SOCKET: &str = "SOCKET";
@ -52,6 +53,12 @@ fn main() {
.long("reject-spam")
.conflicts_with_all(&[ARG_PRESERVE_BODY, ARG_PRESERVE_HEADERS])
.help("Reject messages flagged as spam"))
.arg(Arg::with_name(ARG_REPLY_TEXT)
.short("R")
.long("reply-text")
.value_name("MSG")
.requires(ARG_REJECT_SPAM)
.help("Reply text when rejecting messages"))
.arg(Arg::with_name(ARG_TRUSTED_NETWORKS)
.short("t")
.long("trusted-networks")
@ -147,6 +154,10 @@ fn build_config(matches: &ArgMatches<'_>) -> Result<Config> {
config.verbose(true);
}
if let Some(msg) = matches.value_of(ARG_REPLY_TEXT) {
config.reply_text(msg.to_owned());
}
if let Some(level) = matches.value_of(ARG_MILTER_DEBUG_LEVEL) {
config.milter_debug_level(level.parse().unwrap());
}

View file

@ -63,7 +63,7 @@ local err = mt.eom(conn)
assert(err == nil, err)
assert(mt.getreply(conn) == SMFIR_REPLYCODE)
assert(mt.eom_check(conn, MT_SMTPREPLY, "550", "5.7.1", "Spam message refused"))
assert(mt.eom_check(conn, MT_SMTPREPLY, "550", "5.7.1", "Not allowed!"))
local err = mt.disconnect(conn)
assert(err == nil, err)

View file

@ -6,8 +6,10 @@ use spamassassin_milter::*;
#[test]
fn reject_spam() {
let mut builder = Config::builder();
builder.reject_spam(true);
builder.spamc_args(vec![format!("--port={}", SPAMD_PORT)]);
builder
.reject_spam(true)
.reply_text("Not allowed!".into())
.spamc_args(vec![format!("--port={}", SPAMD_PORT)]);
let config = builder.build();
let server = spawn_mock_spamd_server(SPAMD_PORT, |spam| {