You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
100 lines
2.9 KiB
Rust
100 lines
2.9 KiB
Rust
use std::convert::Infallible;
|
|
use std::net::IpAddr;
|
|
use std::collections::HashMap;
|
|
|
|
use rocket::request::{self, FromRequest};
|
|
use rocket::serde::json::Json;
|
|
use rocket::http::Status;
|
|
use rocket::Request;
|
|
use mail_builder::MessageBuilder;
|
|
|
|
pub struct ClientIp(Option<IpAddr>);
|
|
|
|
#[rocket::async_trait]
|
|
impl<'r> FromRequest<'r> for ClientIp {
|
|
type Error = Infallible;
|
|
|
|
async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
|
request::Outcome::Success(ClientIp(request.client_ip()))
|
|
}
|
|
}
|
|
|
|
#[post("/api/contact/<slug>", data = "<fields>")]
|
|
pub async fn handler(state: &rocket::State<crate::State>, client_ip: ClientIp, slug: &str, fields: Json<HashMap<&str, &str>>) -> Result<String, (Status, String)> {
|
|
{
|
|
let rate_limit = &state.rate_limit;
|
|
|
|
if client_ip.0.is_none() {
|
|
return Err((Status::InternalServerError, "Could not identify client IP".to_string()))
|
|
}
|
|
|
|
let client_ip = client_ip.0.unwrap();
|
|
|
|
let rate_limit_outcome = rate_limit.check_key(&client_ip);
|
|
|
|
if let Err(_not_until) = rate_limit_outcome {
|
|
return Err((Status::TooManyRequests, "Rate limit error".to_string()));
|
|
}
|
|
|
|
println!("Passed rate limit");
|
|
};
|
|
|
|
{
|
|
let email_client = &state.smtp_builder;
|
|
let mail_conf = &state.config.mailer;
|
|
let form_conf = &state.config.forms.iter().find(|form| {
|
|
form.slug == slug
|
|
});
|
|
|
|
if form_conf.is_none() {
|
|
return Err((Status::NotFound, "Not found".to_string()));
|
|
}
|
|
|
|
let form_conf = form_conf.unwrap();
|
|
|
|
let mut message_text = format!(
|
|
"You have recieved a message on {}:
|
|
|
|
Form fields:", form_conf.name);
|
|
|
|
for field in &form_conf.fields {
|
|
let value_opt = fields.get(field.name.as_str());
|
|
let required = field.required;
|
|
|
|
if value_opt.is_none() && required {
|
|
return Err((Status::BadRequest, format!("Missing field: {}", field.name)))
|
|
}
|
|
|
|
message_text.push_str(&format!(" - {}: {}\n", field.name, value_opt.unwrap_or(&"[empty]")));
|
|
}
|
|
|
|
let message = MessageBuilder::new()
|
|
.from(mail_conf.from_address.clone())
|
|
.to(form_conf.recipient_email.clone())
|
|
.subject(form_conf.subject.clone())
|
|
.text_body(message_text);
|
|
|
|
println!("Message composed");
|
|
|
|
let connection_result = email_client.connect().await;
|
|
|
|
if let Err(err) = connection_result {
|
|
println!("Error connecting to server: {}", err.to_string());
|
|
|
|
return Err((Status::InternalServerError, "Could not send message".to_string()));
|
|
} else {
|
|
let mut connection = connection_result.unwrap();
|
|
println!("Connected to mail server");
|
|
|
|
if let Err(err) = connection.send(message).await {
|
|
println!("Error sending email: {}", err.to_string());
|
|
|
|
return Err((Status::InternalServerError, "Could not send message".to_string()));
|
|
}
|
|
}
|
|
|
|
println!("Message sent");
|
|
return Ok("Message sent".to_string());
|
|
};
|
|
}
|