1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
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
use rocket::{
data::{self, FromData},
http::Status,
Data, Request, State,
};
use rocket_contrib::Json;
use crate::{
db::{auth_db::DbClient, delivery_problems::DeliveryProblems, message_data::MessageData},
logging::MozlogLogger,
providers::{Headers, Providers},
types::{
email_address::EmailAddress,
error::{AppError, AppErrorKind, AppResult},
},
};
#[cfg(test)]
mod test;
#[derive(Debug, Deserialize)]
struct Body {
text: String,
html: Option<String>,
}
#[derive(Debug, Deserialize)]
struct Email {
to: EmailAddress,
cc: Option<Vec<EmailAddress>>,
headers: Option<Headers>,
subject: String,
body: Body,
provider: Option<String>,
metadata: Option<String>,
}
impl FromData for Email {
type Error = AppError;
fn from_data(request: &Request, data: Data) -> data::Outcome<Self, Self::Error> {
Json::<Email>::from_data(request, data)
.map_failure(|(_status, error)| {
(
Status::BadRequest,
AppErrorKind::InvalidPayload(error.to_string()).into(),
)
})
.map(|json| json.into_inner())
}
}
#[post("/send", format = "application/json", data = "<email>")]
fn handler(
email: AppResult<Email>,
bounces: State<DeliveryProblems<DbClient>>,
logger: State<MozlogLogger>,
message_data: State<MessageData>,
providers: State<Providers>,
) -> AppResult<Json> {
let email = email?;
bounces.check(&email.to)?;
let cc = if let Some(ref cc) = email.cc {
let mut refs = Vec::new();
for address in cc.iter() {
bounces.check(&address)?;
refs.push(address.as_ref());
}
refs
} else {
Vec::new()
};
providers
.send(
email.to.as_ref(),
cc.as_ref(),
email.headers.as_ref(),
email.subject.as_ref(),
email.body.text.as_ref(),
email.body.html.as_ref().map(|html| html.as_ref()),
email.provider.as_ref().map(|provider| provider.as_ref()),
)
.map(|message_id| {
email
.metadata
.as_ref()
.and_then(|metadata| message_data.set(message_id.as_str(), metadata).err())
.map(|error| {
let log = MozlogLogger::with_app_error(&logger, &error)
.expect("MozlogLogger::with_request error");
slog_error!(log, "{}", "Request errored");
});
Json(json!({ "messageId": message_id }))
})
.map_err(|error| error)
}