Process the input

This commit is contained in:
Rodolphe Bréard 2023-07-14 20:00:12 +02:00
parent 783a8b1bf2
commit aa43eb9999
3 changed files with 80 additions and 2 deletions

46
src/input.rs Normal file
View file

@ -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!("filterresult|{0}|{1}|{msg}", self.session_id, self.token);
}
pub fn get_coded_address(&self) -> &CodedAddress {
&self.address
}
}
pub fn parse_input(input: &str) -> Result<Input> {
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 == "rcptto", "{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,
})
}

View file

@ -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() {

View file

@ -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|smtpin|txrcpt");
println!("register|filter|smtpin|rcptto");
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<KeyedAddress>) -> bool {
let address = input.get_coded_address();
for addr_k in addr_lst {
if addr_k == address {
return addr_k.check_code(&address);
}
}
true
}