import os.path as os_path import pwd import sys from datetime import datetime from os import environ from babel import parse_locale from flask import Flask, abort, g, redirect, url_for from flask_babel import Babel, get_locale from flask_login import LoginManager, login_required from flask_wtf.csrf import CSRFProtect from jinja2 import ChainableUndefined from markupsafe import Markup from werkzeug.middleware.proxy_fix import ProxyFix from .css import compile_css_command from .read_mails import load_user from .render_mail import format_mail from .webmail import ( about, displayheaders, login, logout, move, readmail, sendmail, writemail, ) if sys.version_info >= (3, 11): from tomllib import load as toml_load toml_read_file = dict(load=toml_load, text=False) else: from toml import load as toml_load toml_read_file = dict(load=toml_load, text=True) __version__ = "2.3.0.dev4" csrf = CSRFProtect() def validate_config(app): conf = app.config assert "@" in conf["JWEBMAIL"]["ADMIN_MAIL"] assert os_path.isdir(conf["JWEBMAIL"]["READ_MAILS"]["MAILBOX"]) assert os_path.abspath(conf["JWEBMAIL"]["READ_MAILS"]["BACKEND"]) assert pwd.getpwnam( conf["JWEBMAIL"]["READ_MAILS"].setdefault("MAILBOX_USER", environ["USER"]) ) assert os_path.abspath( conf["JWEBMAIL"]["READ_MAILS"].setdefault("AUTHENTICATOR", "qmail-authuser") ) def create_app(): app = Flask(__name__) app.jinja_options = dict(undefined=ChainableUndefined) if app.config["DEBUG"]: app.config.from_file("../../jwebmail.toml", **toml_read_file) else: app.config.from_file(environ["JWEBMAIL_CONFIG"], **toml_read_file) if app.config["JWEBMAIL"].get("PROXY"): app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1) validate_config(app) csrf.init_app(app) babel = Babel(app, locale_selector=lambda: g.get("lang_code")) app.cli.add_command(compile_css_command) login_manager = LoginManager() login_manager.login_view = "login" login_manager.user_loader(load_user) login_manager.init_app(app) route(app) tr_dict = { "en": str.maketrans({" ": " "}), "de": str.maketrans({".": ",", " ": " "}), } @app.template_filter("num_localize") def num_localize(txt): if str(get_locale()).startswith("de"): return Markup(txt.translate(tr_dict["de"])) else: return Markup(txt.translate(tr_dict["en"])) @app.context_processor def inject_version(): return { "version": __version__, "get_locale": get_locale, "format_mail": format_mail, "parse_iso_date": datetime.fromisoformat, } @app.url_defaults def add_language_code(endpoint, values): if "lang_code" in values: return if app.url_map.is_endpoint_expecting(endpoint, "lang_code"): values["lang_code"] = g.get("lang_code", babel.default_locale.language) @app.url_value_preprocessor def pull_lang_code(endpoint, values): if values and values.get("lang_code"): try: pl = parse_locale(values["lang_code"]) if pl[0] in ["en", "de"]: g.lang_code = values.pop("lang_code") else: abort(404) except ValueError: abort(404) return app def route(app): app.add_url_rule( "/", endpoint="root", view_func=lambda: redirect(url_for("login", lang_code=get_locale().language)), ) app.add_url_rule("//", view_func=login, methods=["GET", "POST"]) app.add_url_rule("//about", view_func=about) app.add_url_rule("//logout", view_func=logout) dh = login_required(displayheaders) app.add_url_rule("//home/", view_func=dh) app.add_url_rule("//home/", view_func=dh) lr_readmail = login_required(readmail) app.add_url_rule( "//read/", endpoint="read", view_func=lr_readmail ) app.add_url_rule( "//read//", endpoint="read", view_func=lr_readmail ) lr_writemail = login_required(writemail) app.add_url_rule("//write", endpoint="write", view_func=lr_writemail) app.add_url_rule( "//write", endpoint="send", view_func=login_required(sendmail), methods=["POST"], ) app.add_url_rule( "//move/", view_func=login_required(move), methods=["POST"] )