from configparser import ConfigParser from email.message import EmailMessage import random import operator from os.path import exists from secrets import token_urlsafe from smtplib import SMTP from time import time import flask import sqlalchemy import sqlalchemy.orm CONFIG_PATH = "/etc/contact-form/config.ini" OPERATORS = ( ('+', operator.add), ('×', operator.mul), ('-', operator.sub), ) config = ConfigParser() config.read(CONFIG_PATH) app = flask.Flask(__name__) Base = sqlalchemy.orm.declarative_base() class Captcha(Base): __tablename__ = "captcha" token = sqlalchemy.Column(sqlalchemy.String(90), primary_key=True) answer = sqlalchemy.Column(sqlalchemy.String(3)) expiration = sqlalchemy.Column(sqlalchemy.Integer()) def get_session(): db_path = config.get("app", "db_path", fallback="") engine = sqlalchemy.create_engine(f"sqlite:///{db_path}") if not exists(db_path): Base.metadata.create_all(engine) session_factory = sqlalchemy.orm.sessionmaker(engine) return session_factory() def clean_db(session): session.query(Captcha).filter(Captcha.expiration < int(time())).delete() def generate_captacha(): session = get_session() clean_db(session) first_number = random.randrange(10) second_number = random.randrange(10) op_text, op_func = random.choice(OPERATORS) if op_text == "-" and first_number < second_number: first_number, second_number = second_number, first_number result = op_func(first_number, second_number) captcha = Captcha( token=token_urlsafe(67), answer=str(result), expiration=int(time()) + config.getint("app", "captcha_timout", fallback=3600), ) session.add(captcha) session.commit() return { "first_number": first_number, "second_number": second_number, "op_text": op_text, "token": captcha.token, } def validate_captcha(token, value): result = False session = get_session() clean_db(session) captcha = session.query(Captcha).filter(Captcha.token == token).one_or_none() if captcha: result = value == captcha.answer session.query(Captcha).filter(Captcha.token == token).delete() session.commit() return result def send_email(sender, message): katzei_email = EmailMessage() katzei_email.set_content(message) katzei_email["From"] = sender katzei_email["To"] = config.get("email", "reciever") katzei_email["Subject"] = config.get("email", "subject") contact_email = EmailMessage() contact_email.set_content(message) contact_email["From"] = config.get("email", "sender") contact_email["To"] = sender contact_email["Subject"] = config.get("email", "subject") smtp = SMTP(config.get("email", "server")) smtp.send_message(katzei_email) smtp.send_message(contact_email) smtp.quit() @app.route(config.get("app", "path"), methods=["GET"]) def get_form(context=None): context = (context or {}) | generate_captacha() return flask.render_template("form.html", **context) @app.route(config.get("app", "path"), methods=["POST"]) def validate_form(): form = flask.request.form if not all(form.get(f, False) for f in ("email", "message", "captcha", "token")): return "Erreur", 400 if not validate_captcha(form["token"], form["captcha"]): context = dict(**form) context["captcha_error"] = True context.pop("captcha", None) return get_form(context=context) send_email(sender=form["email"], message=form["message"]) return flask.render_template("success.html") application=app