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.

124 lines
3.4 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;
use serde::Serialize;
pub struct ClientIp(Option<IpAddr>);
#[derive(Serialize)]
pub struct Response {
status: &'static str,
message: Option<String>,
}
#[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<Json<Response>, (Status, Json<Response>)> {
{
let rate_limit = &state.rate_limit;
let client_ip = client_ip.0.ok_or(
(Status::InternalServerError, Json(Response {
status: "error",
message: Some("Could not identify client IP".to_string())
}))
)?;
let rate_limit_outcome = rate_limit.check_key(&client_ip);
if let Err(_not_until) = rate_limit_outcome {
return Err((Status::TooManyRequests, Json(Response {
status: "ratelimit",
message: Some("Too many messages, play nice friend".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
});
let form_conf = form_conf.ok_or(
(Status::NotFound, Json(Response {
status: "notfound",
message: Some(format!("Form ID {} not found", slug))
}))
)?;
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, Json(Response {
status: "error",
message: Some(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, Json(Response {
status: "error",
message: Some("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, Json(Response {
status: "error",
message: Some("Could not send message".to_string())
})));
}
}
println!("Message sent");
return Ok(Json(Response {
status: "Message sent",
message: None
}));
};
}