| ︙ | | | ︙ | |
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
INVALID_CREDENTIALS,
NO_MAILBOX,
OK
},
};
use regex::{
Regex,
escape,
};
use stacked_errors::{
Result,
StackableErr,
bail,
};
|
>
|
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
INVALID_CREDENTIALS,
NO_MAILBOX,
OK
},
};
use regex::{
Regex,
RegexBuilder,
escape,
};
use stacked_errors::{
Result,
StackableErr,
bail,
};
|
| ︙ | | | ︙ | |
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
}
/// `MailServer` Central object with TG api and configuration
#[derive(Clone, Debug)]
pub struct MailServer {
data: Vec<u8>,
headers: Option<SomeHeaders>,
relay: bool,
tg: Arc<TelegramTransport>,
fields: HashSet<String>,
address: Regex,
}
impl MailServer {
/// Initializes the mail server: sets up the Telegram API client and
|
<
|
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
}
/// `MailServer` Central object with TG api and configuration
#[derive(Clone, Debug)]
pub struct MailServer {
data: Vec<u8>,
headers: Option<SomeHeaders>,
tg: Arc<TelegramTransport>,
fields: HashSet<String>,
address: Regex,
}
impl MailServer {
/// Initializes the mail server: sets up the Telegram API client and
|
| ︙ | | | ︙ | |
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
.context("[smtp2tg.toml] missing \"api_key\" parameter.\n")?;
let mut recipients = HashMap::new();
for (name, value) in settings.get_table("recipients")
.expect("[smtp2tg.toml] missing table \"recipients\".\n")
{
let value = value.into_int()
.context("[smtp2tg.toml] \"recipient\" table values should be integers.\n")?;
recipients.insert(name, value);
}
let tg = Arc::new(TelegramTransport::new(api_key, recipients, &settings)?);
let fields = HashSet::<String>::from_iter(settings.get_array("fields")
.expect("[smtp2tg.toml] \"fields\" should be an array")
.iter().map(|x| x.clone().into_string().expect("should be strings")));
let mut domains: HashSet<String> = HashSet::new();
let extra_domains = settings.get_array("domains").stack()?;
for domain in extra_domains {
let domain = domain.to_string().to_lowercase();
if RE_DOMAIN.is_match(&domain) {
domains.insert(domain);
} else {
panic!("[smtp2tg.toml] can't check of domains in \"domains\": {domain}");
}
}
let domains = domains.into_iter().map(|s| escape(&s))
.collect::<Vec<String>>().join("|");
let address = Regex::new(&format!("^[a-z0-9][-a-z0-9]*(@({domains}))?$")).stack()?;
let relay = match settings.get_string("unknown")
.context("[smtp2tg.toml] can't get \"unknown\" policy.\n")?.as_str()
{
"relay" => true,
"deny" => false,
_ => {
bail!("[smtp2tg.toml] \"unknown\" should be either \"relay\" or \"deny\".\n");
},
};
Ok(MailServer {
data: vec!(),
headers: None,
relay,
tg,
fields,
address,
})
}
/// Retrieves the Telegram chat ID for a given email address, checks that
|
|
|
<
<
<
|
<
<
<
<
<
<
|
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
.context("[smtp2tg.toml] missing \"api_key\" parameter.\n")?;
let mut recipients = HashMap::new();
for (name, value) in settings.get_table("recipients")
.expect("[smtp2tg.toml] missing table \"recipients\".\n")
{
let value = value.into_int()
.context("[smtp2tg.toml] \"recipient\" table values should be integers.\n")?;
recipients.insert(name.to_lowercase().replace('.', ""), value);
}
let tg = Arc::new(TelegramTransport::new(api_key, recipients, &settings)?);
let fields = HashSet::<String>::from_iter(settings.get_array("fields")
.expect("[smtp2tg.toml] \"fields\" should be an array")
.iter().map(|x| x.clone().into_string().expect("should be strings")));
let mut domains: HashSet<String> = HashSet::new();
let extra_domains = settings.get_array("domains").stack()?;
for domain in extra_domains {
let domain = domain.to_string().to_lowercase();
if RE_DOMAIN.is_match(&domain) {
domains.insert(domain);
} else {
panic!("[smtp2tg.toml] can't check of domains in \"domains\": {domain}");
}
}
let domains = domains.into_iter().map(|s| escape(&s))
.collect::<Vec<String>>().join("|");
let address = RegexBuilder::new(&format!("^[a-z0-9][a-z0-9.-]*(@({domains}))?$"))
.case_insensitive(true).build().stack()?;
Ok(MailServer {
data: vec!(),
headers: None,
tg,
fields,
address,
})
}
/// Retrieves the Telegram chat ID for a given email address, checks that
|
| ︙ | | | ︙ | |
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
if let Some(headers) = &self.headers {
let mail = mail_parser::MessageParser::new().parse(&self.data)
.context("Failed to parse mail.")?;
// Adding all known addresses to recipient list, for anyone else adding default
// Also if list is empty also adding default
let mut rcpt: HashSet<&ChatPeerId> = HashSet::new();
if headers.to.is_empty() && !self.relay {
bail!("Relaying is disabled, and there's no destination address");
}
for item in &headers.to {
rcpt.insert(self.get_id(item)?);
};
if rcpt.is_empty() {
self.tg.debug("No recipient or envelope address.").await?;
|
|
|
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
if let Some(headers) = &self.headers {
let mail = mail_parser::MessageParser::new().parse(&self.data)
.context("Failed to parse mail.")?;
// Adding all known addresses to recipient list, for anyone else adding default
// Also if list is empty also adding default
let mut rcpt: HashSet<&ChatPeerId> = HashSet::new();
if headers.to.is_empty() {
bail!("Relaying is disabled, and there's no destination address");
}
for item in &headers.to {
rcpt.insert(self.get_id(item)?);
};
if rcpt.is_empty() {
self.tg.debug("No recipient or envelope address.").await?;
|
| ︙ | | | ︙ | |
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
|
/// Just deny plain auth
fn auth_plain (&mut self, _authorization_id: &str, _authentication_id: &str, _password: &str) -> Response {
INVALID_CREDENTIALS
}
/// Verify whether address is deliverable
fn rcpt (&mut self, to: &str) -> Response {
if self.relay || self.get_id(to).is_ok() {
OK
} else {
NO_MAILBOX
}
}
/// Save headers we need
|
|
|
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
|
/// Just deny plain auth
fn auth_plain (&mut self, _authorization_id: &str, _authentication_id: &str, _password: &str) -> Response {
INVALID_CREDENTIALS
}
/// Verify whether address is deliverable
fn rcpt (&mut self, to: &str) -> Response {
if self.get_id(to).is_ok() {
OK
} else {
NO_MAILBOX
}
}
/// Save headers we need
|
| ︙ | | | ︙ | |