mod mailer;
use mailer::{default_subject_line, send_email};
mod spam;
use spam::{check_for_spam, check_stop_forum_spam};
mod strings;
use env_logger::Env;
use serde::Deserialize;
use strings::{REJECT_MSG, SUCCESS_MSG, VERSION, WELCOME_MSG};
use validator::Validate;
mod mail_template;
use actix_web::{
error, get,
middleware::Logger,
post,
web::{self, Redirect},
App, Either, HttpResponse, HttpServer, Responder,
};
fn redirect_default() -> bool {
false
}
#[derive(Debug, Deserialize, Validate)]
struct Submission {
#[serde(alias = "fullname")]
#[serde(alias = "fullName")]
full_name: String,
#[validate(email(
message = "Invalid email address provided. Please check email and try again."
))]
email: String,
#[serde(default = "default_subject_line")]
subject: String,
message: String,
#[serde(alias = "site")]
#[serde(alias = "website")]
#[serde(alias = "location")]
from_site: String,
#[serde(default = "redirect_default")]
redirect: bool,
}
type FormSubmission = Either<web::Json<Submission>, web::Form<Submission>>;
type FormResponse = Either<HttpResponse, Redirect>;
#[get("/")]
async fn hello() -> impl Responder {
HttpResponse::Ok().body(format!("{WELCOME_MSG}\n{VERSION}"))
}
#[post("/")]
async fn submit(form: FormSubmission) -> Result<FormResponse, error::Error> {
let (validated, email, full_name, subject, message, from_site, redirect) = match form {
Either::Left(submission) => (
submission.validate(),
submission.email.trim().to_owned(),
submission.full_name.to_owned(),
submission.subject.to_owned(),
submission.message.to_owned(),
submission.from_site.to_owned(),
submission.redirect,
),
Either::Right(submission) => (
submission.validate(),
submission.email.trim().to_owned(),
submission.full_name.to_owned(),
submission.subject.to_owned(),
submission.message.to_owned(),
submission.from_site.to_owned(),
submission.redirect,
),
};
if let Err(error) = validated {
Err(error::ErrorBadRequest(error.to_string()))
} else {
check_for_spam(&message, REJECT_MSG)?;
check_stop_forum_spam(&email, REJECT_MSG).await?;
send_email(&email, &full_name, &subject, &message, &from_site)
.map(|_| {
if redirect {
Either::Right(Redirect::to(from_site.to_string()))
} else {
Either::Left(HttpResponse::Ok().body(SUCCESS_MSG))
}
})
.map_err(error::ErrorBadRequest)
}
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
env_logger::init_from_env(Env::default().default_filter_or("info"));
HttpServer::new(|| {
App::new()
.wrap(Logger::default())
.wrap(Logger::new("%a %{User-Agent}i"))
.service(hello)
.service(submit)
})
.bind(("0.0.0.0", 8000))?
.run()
.await
}