diff --git a/src/input.rs b/src/input.rs
new file mode 100644
index 0000000..2eca268
--- /dev/null
+++ b/src/input.rs
@@ -0,0 +1,46 @@
+use crate::address::CodedAddress;
+use anyhow::{anyhow, ensure, Result};
+use std::str::FromStr;
+
+macro_rules! next_param {
+ ($it: ident) => {
+ $it.next().ok_or(anyhow!("missing parameter"))
+ };
+}
+
+#[derive(Debug)]
+pub struct Input {
+ session_id: String,
+ token: String,
+ address: CodedAddress,
+}
+
+impl Input {
+ pub fn answer(&self, msg: &str) {
+ println!("filter‐result|{0}|{1}|{msg}", self.session_id, self.token);
+ }
+
+ pub fn get_coded_address(&self) -> &CodedAddress {
+ &self.address
+ }
+}
+
+pub fn parse_input(input: &str) -> Result {
+ let mut params_it = input.split(crate::PARAM_SEPARATOR);
+ let stream = next_param!(params_it)?;
+ ensure!(stream == "filter", "{stream}: invalid stream");
+ let _version = next_param!(params_it)?;
+ let _timestamp = next_param!(params_it)?;
+ let _subsystem = next_param!(params_it)?;
+ let filter = next_param!(params_it)?;
+ ensure!(filter == "rcpt‐to", "{filter}: invalid filter");
+ let session_id = next_param!(params_it)?.to_string();
+ let token = next_param!(params_it)?.to_string();
+ let address = next_param!(params_it)?.trim_end();
+ let address = CodedAddress::from_str(address)?;
+ Ok(Input {
+ session_id,
+ token,
+ address,
+ })
+}
diff --git a/src/main.rs b/src/main.rs
index ce4f5e2..f8b1fa8 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,11 +2,13 @@ use std::process::ExitCode;
mod address;
mod config;
+mod input;
mod service;
const COMMENT_CHAR: char = '#';
const DEFAULT_SEPARATOR: char = '+';
const KEY_SEPARATOR: char = ':';
+const PARAM_SEPARATOR: char = '|';
fn main() -> ExitCode {
match service::start_service() {
diff --git a/src/service.rs b/src/service.rs
index b120416..6632015 100644
--- a/src/service.rs
+++ b/src/service.rs
@@ -1,14 +1,19 @@
-use crate::address::CodedAddress;
+use crate::address::KeyedAddress;
use crate::config::Config;
+use crate::input::{parse_input, Input};
use anyhow::Result;
use clap::Parser;
+use std::collections::HashSet;
use std::io;
pub const BUFF_SIZE: usize = 4096;
pub const CONFIG_END: &str = "config|ready\n";
+pub const ANSWER_OK: &str = "proceed";
+pub const ANSWER_ERR: &str = "reject|550 No such recipient here";
pub fn start_service() -> Result<()> {
let cfg = Config::parse();
+ let addresses = cfg.addresses()?;
let mut buffer = String::with_capacity(BUFF_SIZE);
let stdin = io::stdin();
@@ -20,12 +25,37 @@ pub fn start_service() -> Result<()> {
break;
}
}
- println!("register|report|smtp‐in|tx‐rcpt");
+ println!("register|filter|smtp‐in|rcpt‐to");
println!("register|ready");
// Input processing
loop {
buffer.clear();
stdin.read_line(&mut buffer)?;
+ if buffer.is_empty() {
+ continue;
+ }
+ match parse_input(&buffer) {
+ Ok(input) => {
+ if allow_email(&input, &addresses) {
+ input.answer(ANSWER_OK);
+ } else {
+ input.answer(ANSWER_ERR);
+ }
+ }
+ Err(e) => {
+ eprintln!("error: {e:#}");
+ }
+ }
}
}
+
+fn allow_email(input: &Input, addr_lst: &HashSet) -> bool {
+ let address = input.get_coded_address();
+ for addr_k in addr_lst {
+ if addr_k == address {
+ return addr_k.check_code(&address);
+ }
+ }
+ true
+}