diff options
-rw-r--r-- | MANIFEST | 72 | ||||
-rw-r--r-- | MANIFEST.BIN | 68 | ||||
-rw-r--r-- | MANIFEST.SKIP | 15 | ||||
-rwxr-xr-x | actions | 91 | ||||
-rwxr-xr-x | configure | 140 | ||||
-rw-r--r-- | confninja.sh | 109 | ||||
-rw-r--r-- | jwebmail.development.toml | 21 | ||||
-rw-r--r-- | jwebmail.production.toml | 15 | ||||
-rw-r--r-- | jwebmail.service | 13 | ||||
-rw-r--r-- | jwebmail.service.tmpl | 15 | ||||
-rw-r--r-- | lib/JWebmail.pm | 37 | ||||
-rw-r--r-- | lib/JWebmail/Config.pm.in | 25 | ||||
-rw-r--r-- | lib/JWebmail/Controller/Webmail.pm | 17 | ||||
-rw-r--r-- | lib/JWebmail/Model/ReadMails/MockJSON.pm | 16 | ||||
-rw-r--r-- | lib/JWebmail/Model/ReadMails/MockMaildir.pm | 23 | ||||
-rw-r--r-- | lib/JWebmail/Model/ReadMails/QMailAuthuser.pm | 23 | ||||
-rw-r--r-- | lib/JWebmail/Model/WriteMails.pm | 6 | ||||
-rw-r--r-- | lib/JWebmail/Plugin/I18N2/Maketext.pm | 19 | ||||
-rw-r--r-- | t/Webmail.t | 9 | ||||
-rw-r--r-- | templates/webmail/about.html.ep | 1 | ||||
-rw-r--r-- | templates/webmail/login.html.ep | 3 |
21 files changed, 510 insertions, 228 deletions
@@ -1,63 +1,73 @@ +b/extract.pl + script/jwebmail +script/mojocookiecheck.pl script/qmauth.pl script/qmauth.py -bin/jwebmail-extract -lib/JWebmail.pm lib/JWebmail/Controller/Webmail.pm -lib/JWebmail/I18N.pm lib/JWebmail/I18N/de.pm lib/JWebmail/I18N/en.pm -lib/JWebmail/Model/ReadMails/Role.pm -lib/JWebmail/Model/ReadMails/MockMaildir.pm lib/JWebmail/Model/ReadMails/MockJSON.pm +lib/JWebmail/Model/ReadMails/MockMaildir.pm lib/JWebmail/Model/ReadMails/QMailAuthuser.pm +lib/JWebmail/Model/ReadMails/Role.pm lib/JWebmail/Model/WriteMails.pm -lib/JWebmail/Plugin/TOMLConfig.pm -lib/JWebmail/Plugin/I18N2.pm -lib/JWebmail/Plugin/I18N2/Role.pm lib/JWebmail/Plugin/I18N2/INI.pm lib/JWebmail/Plugin/I18N2/Maketext.pm +lib/JWebmail/Plugin/I18N2/Role.pm +lib/JWebmail/Plugin/I18N2.pm +lib/JWebmail/Plugin/INIConfig.pm lib/JWebmail/Plugin/Paginate.pm lib/JWebmail/Plugin/ServerSideSessionData.pm +lib/JWebmail/Plugin/TOMLConfig.pm lib/JWebmail/View/RenderMail.pm lib/JWebmail/View/Webmail.pm +lib/JWebmail/Config.pm.in +lib/JWebmail/I18N.pm +lib/JWebmail.pm + +scss/my_bulma.scss + +src/displayheaders.js +src/login_cram.js +src/rendermail.js -t/Webmail.t -t/ViewWebmail.t -t/INI.t t/Extract.t -t/Pagination.t t/I18N2.t +t/INI.t +t/Pagination.t +t/ViewWebmail.t +t/Webmail.t -templates/webmail/readmail.html.ep -templates/webmail/writemail.html.ep -templates/webmail/displayheaders.html.ep -templates/webmail/about.html.ep -templates/webmail/login.html.ep -templates/layouts/mainlayout.html.ep -templates/displayheaders/_top_nav.html.ep templates/displayheaders/_bot_nav.html.ep templates/displayheaders/_folders.html.ep templates/displayheaders/_main_table.html.ep templates/displayheaders/_pagination1.html.ep templates/displayheaders/_pagination2.html.ep templates/displayheaders/_pagination3.html.ep -templates/not_found.production.html.ep +templates/displayheaders/_top_nav.html.ep +templates/layouts/mainlayout.html.ep +templates/webmail/about.html.ep +templates/webmail/displayheaders.html.ep +templates/webmail/login.html.ep +templates/webmail/readmail.html.ep +templates/webmail/writemail.html.ep templates/exception_.html.ep +templates/not_found.html.ep -public/css/style.css -public/src/login_cram.js - -jwebmail.development.toml - -README.md +.editorconfig +actions CHANGES.md +configure +confninja.sh +jwebmail.development.toml +jwebmail.production.toml +jwebmail.service LICENSE - -actions -jwebmail.service.tmpl - Makefile.PL MANIFEST -MANIFEST.SKIP +MANIFEST.BIN +package-lock.json +package.json +README.md diff --git a/MANIFEST.BIN b/MANIFEST.BIN new file mode 100644 index 0000000..58d4dc9 --- /dev/null +++ b/MANIFEST.BIN @@ -0,0 +1,68 @@ +b/extract.pl + +bin/jwebmail-extract + +script/jwebmail +script/qmauth.pl +script/qmauth.py + +lib/JWebmail/Controller/Webmail.pm +lib/JWebmail/I18N/de.pm +lib/JWebmail/I18N/en.pm +lib/JWebmail/Model/ReadMails/MockJSON.pm +lib/JWebmail/Model/ReadMails/MockMaildir.pm +lib/JWebmail/Model/ReadMails/QMailAuthuser.pm +lib/JWebmail/Model/ReadMails/Role.pm +lib/JWebmail/Model/WriteMails.pm +lib/JWebmail/Plugin/I18N2/INI.pm +lib/JWebmail/Plugin/I18N2/Maketext.pm +lib/JWebmail/Plugin/I18N2/Role.pm +lib/JWebmail/Plugin/I18N2.pm +lib/JWebmail/Plugin/INIConfig.pm +lib/JWebmail/Plugin/Paginate.pm +lib/JWebmail/Plugin/ServerSideSessionData.pm +lib/JWebmail/Plugin/TOMLConfig.pm +lib/JWebmail/View/RenderMail.pm +lib/JWebmail/View/Webmail.pm +lib/JWebmail/Config.pm +lib/JWebmail/I18N.pm +lib/JWebmail.pm + +public/css/my_bulma.css +public/src/displayheaders.js +public/src/login_cram.js +public/src/rendermail.js + +t/Extract.t +t/I18N2.t +t/INI.t +t/Pagination.t +t/ViewWebmail.t +t/Webmail.t + +templates/displayheaders/_bot_nav.html.ep +templates/displayheaders/_folders.html.ep +templates/displayheaders/_main_table.html.ep +templates/displayheaders/_pagination1.html.ep +templates/displayheaders/_pagination2.html.ep +templates/displayheaders/_pagination3.html.ep +templates/displayheaders/_top_nav.html.ep +templates/layouts/mainlayout.html.ep +templates/webmail/about.html.ep +templates/webmail/displayheaders.html.ep +templates/webmail/login.html.ep +templates/webmail/readmail.html.ep +templates/webmail/writemail.html.ep +templates/exception_.html.ep +templates/not_found.html.ep + +actions +CHANGES.md +build.ninja +jwebmail.development.toml +jwebmail.production.toml +jwebmail.service +LICENSE +Makefile.PL +MANIFEST.BIN +README.md diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP deleted file mode 100644 index 6036ec7..0000000 --- a/MANIFEST.SKIP +++ /dev/null @@ -1,15 +0,0 @@ -extract/ -log/ -.git/ -.gitignore -.vscode/settings.json -.mypy_cache/ -lang/ -node_modules/ -css/ -src/ -t/testdata/ -package.json -package-lock.json -b/extract.pl -__pycache__ @@ -1,31 +1,32 @@ -#!/usr/bin/env sh +#!/bin/sh set -euC help_text= -help_text="$help_text install\t[]\t\n" -install () { - perl Makefile.PL - make - make test - make install +help_text="$help_text build\t[]\t\n" +dev_config () { + ./configure -e extractrs -n cat } -help_text="$help_text dist\t[]\tcreate a source distribution\n" -dist () { - perl Makefile.PL - make - make test - make dist +help_text="$help_text unconfigure\t[]\tresets the build\n" +unconfigure () { + rm -f .configured jwebmail.service lib/JWebmail/Config.pm +} + +help_text="$help_text build\t[]\t\n" +build () { + env PATH="node_modules/.bin/:$PATH" ninja "$@" } -help_text="$help_text run_tests\t[args='t/' ...]\tpasses arg to the 'prove' tool\n" -run_tests () { - if [ $# -gt 0 ] - then prove -l "$@" - else prove -l t/ +help_text="$help_text install\t[]\t\n" +install () { + perl Makefile.PL + make + if readlink /bin/init | grep -q 'systemd' + then install -m 644 jwebmail.service /etc/systemd/system fi + install -m 644 jwebmail.toml /etc } help_text="$help_text start_dev\t[]\tstarts a hot reloading dev server\n" @@ -33,29 +34,26 @@ start_dev () { morbo script/jwebmail } -help_text="$help_text logrotate\t[mode=development]\tarchives the current log file\n" -logrotate () { +help_text="$help_text logrotate_dev\t[mode=development]\tarchives the current log file\n" +logrotate_dev () { mode=${1:-development} mv -i "log/$mode.log" "log/${mode}_$(date --iso-8601=minutes).log" } -help_text="$help_text linelength\t[[files]]\tchecks documentation files for overly long lines\n" -linelength () { - files=${1:-'README.md CHANGES.md LICENSE'} +help_text="$help_text follow_log\t[mode=development]\tfollows the current log file\n" +follow_log () { + mode=${1:-development} + tail -f "log/$mode.log" +} + +help_text="$help_text release_check\t[]\t\n" +release_check () { + files='README.md CHANGES.md LICENSE' for file in $files do fold -s -w 85 "$file" | diff "$file" - done -} - -help_text="$help_text follow\t[mode=development]\tfollows the current log file\n" -follow () { - mode=${1:-development} - tail -f "log/$mode.log" -} -help_text="$help_text check_manifest\t[]\tchecks if files in the MANIFEST actually exist\n" -check_manifest () { perl -nE 'chomp; say if $_ && !-e' MANIFEST } @@ -87,37 +85,10 @@ list_translations () { fi } -help_text="$help_text rust_extract\t[]\tupdates and gets the rust extract binary\n" -rust_extract () { - cd extract - cargo build --release --target=x86_64-unknown-linux-musl - cp target/x86_64-unknown-linux-musl/release/jwebmail-extract ../bin - cd .. -} - -help_text="$help_text systemd_unit\t[path=/etc/systemd/system/\$JWM_NAME.service] env(JWM_{NAME,USER,HOME,LOG_LEVEL})\tcreates a systemd unit file\n" -systemd_unit () { - : "${JWM_NAME=jwebmail}" "${JWM_USER=$USER}" "${JWM_HOME=$(pwd)}" "${JWM_LOG_LEVEL=info}" - export JWM_NAME JWM_USER JWM_HOME JWM_LOG_LEVEL - - cmd='perl -pe s/\$(\w+)/$ENV{$1}/ga jwebmail.service.tmpl' - path=${1-/etc/systemd/system/$JWM_NAME.service} - if [ "$path" = '-' ] - then $cmd - else $cmd >|"$path" - fi -} - -help_text="$help_text compile_python\t[]\tprepare python bytecode files\n" -compile_python () { - python3 -m compileall script/qmauth.py -} - help () { echo "The following actions are available:" echo - printf "$help_text" | expand -t 22,43 - echo + printf "$help_text\n" | expand -t 21,41 } cmd=${1-help} diff --git a/configure b/configure new file mode 100755 index 0000000..6196300 --- /dev/null +++ b/configure @@ -0,0 +1,140 @@ +#!/bin/sh + +set -euC + +check_command () { + printf 'Checking for command %s ... ' "$1" + if command -v "$1" >/dev/null + then echo 'found!' + else echo 'not available!'; exit 1 + fi +} + +check_perl_deps () { + for d in "$@" + do + printf 'Checking for perl module %s ... ' "$d" + if perl -Ilib -e "use $d;" + then echo 'found!' + else echo 'not available!'; exit 1 + fi + done +} + +check_node_module () { + printf 'Checking for node module %s ... ' "$1" + if [ -d "node_modules/$1" ] + then echo 'found!' + else echo 'not available!'; exit 1 + fi +} + +check_ninja_version () { + ninja_file="$(mktemp -t jwm-build-XXXXXXXX)" + echo "ninja_required_version = $1" >>"$ninja_file" + printf 'Checking ninja version at least %s ... ' "$1" + if ninja -f "$ninja_file" >/dev/null + then echo 'sufficient!' + else echo 'inadequate!'; exit 1 + fi + rm "$ninja_file" +} + +detect_languages () { + cd lib/JWebmail/I18N + for l in * + do echo "${l%.pm}" + done +} + +allargs="$*" +while getopts 'c:e:hm:n:r:u:' opt +do case $opt in + (e) JWM_MAILDIR_EXTRACTOR="$OPTARG";; + (r) JWM_READ_MODEL="$OPTARG";; + (m) JWM_MODE="$OPTARG";; + (n) JWM_SENDMAIL="$OPTARG";; + (u) JWM_USER="$OPTARG";; + (c) JWM_LOGIN_SCHEME="$OPTARG";; + (h) + t="$(printf '\t')" + expand -t 70 <<END +usage: $0 configure [options...] + + OPTIONS + FLAG ARGUMENT DEFAULT VALUES DESCRIPTION + -e MAILDIR_EXTRACTOR extractpy {extractpy,extractpl,extractrs} + -r PERL_MODULE JWebmail::Model::ReadMails::MockMaildir The read model implementation + -m MODE development {development,production} + -u USERNAME $USER $t The server runs as this user + -c LOGIN_SCHEME plain {plain,cram_md5} The login scheme (don't use plain unless over TLS) + -n PATH sendmail The path to a sendmail executable +END + exit + ;; + (*) exit 2;; +esac +done +shift $((OPTIND-1)) + +: "${JWM_MAILDIR_EXTRACTOR=extractpy}" && echo "$JWM_MAILDIR_EXTRACTOR" | grep -Eq '^extract(pl|py|rs)$' +: "${JWM_READ_MODEL=JWebmail::Model::ReadMails::MockMaildir}" +: "${JWM_MODE=development}" && [ "$JWM_MODE" = development -o "$JWM_MODE" = production ] +: "${JWM_USER=$USER}" && id "$JWM_USER" >/dev/null +: "${JWM_LOGIN_SCHEME=plain}" && [ "$JWM_LOGIN_SCHEME" = plain -o "$JWM_LOGIN_SCHEME" = cram_md5 ] +: "${JWM_SENDMAIL=sendmail}" && [ -x "$(command -v "$JWM_SENDMAIL")" ] + +PATH="$(pwd)/node_modules/.bin${PATH:+:$PATH}" + +check_command perl +check_perl_deps v5.24 +check_command prove +check_command morbo +check_command hypnotoad +check_command npm +check_command esbuild +check_command sass +check_command ninja +check_ninja_version 1.11 + +check_perl_deps 'Mojolicious 9.31' Email::MIME Role::Tiny Class::Method::Modifiers TOML::Tiny namespace::clean + +check_node_module bulma + +if [ "$JWM_LOGIN_SCHEME" = cram_md5 ] +then + check_node_module crypto-js + check_perl_deps Digest::HMAC_MD5 +fi + +case "$JWM_MAILDIR_EXTRACTOR" in + (extractpl) + [ -x "$(pwd)/script/extract.pl" ] + check_perl_deps Mail::Box::Maildir + MAILDIR_EXTRACTOR_BIN=script/extract.pl + ;; + (extractpy) + [ -x "$(pwd)/script/extract.py" ] + check_command python3 + MAILDIR_EXTRACTOR_BIN=script/extract.py + ;; + (extractrs) + [ -f "$(pwd)/extract/Cargo.toml" ] + JWM_EXTRACTRS_DIR=extract + check_command cargo + MAILDIR_EXTRACTOR_BIN=bin/jwebmail-extract + ;; +esac + +export SENDMAIL="$JWM_SENDMAIL" +export LOGIN_SCHEME="$JWM_LOGIN_SCHEME" +export CONFARGS="$allargs" +export MAILDIR_EXTRACTOR_NAME="$JWM_MAILDIR_EXTRACTOR" +export MAILDIR_EXTRACTOR_BIN +export JWM_EXTRACTRS_DIR +export JWM_READ_MODEL +export JWM_MODE + +sh confninja.sh >|build.ninja + +perl -pe 's/@(\w+)@/$ENV{$1}/ga' lib/JWebmail/Config.pm.in >|lib/JWebmail/Config.pm diff --git a/confninja.sh b/confninja.sh new file mode 100644 index 0000000..f1507a0 --- /dev/null +++ b/confninja.sh @@ -0,0 +1,109 @@ +set -eu + +cat <<'END' +# File generated by confninja.sh; DO NOT EDIT! + +ninja_required_version = 1.11 + +rule cargo + command = cd $extractrs_dir && cargo build $rust_mode && cp $rs_bin ../bin + description = CARGO $out + +rule scss + command = sass --load-path=node_modules/ $scss_mode $in $out + description = SCSS $out + +rule bundle + command = esbuild --bundle $bundle_mode --outfile=$out $in + description = BUNDLE $out + +rule dist + command = tar --posix --zstd -cf $out $in + description = DIST $out + +rule cpy + command = cp $in $out + description = CPY $out + +rule perltest + command = prove -l t/ + description = PERLTEST + +rule configure + command = ./configure $confargs + description = CONFIGURE + generator = 1 + +build test: perltest +END + +echo "confargs = $CONFARGS" +echo "build build.ninja lib/JWebmail/Config.pm: configure configure confninja.sh lib/JWebmail/Config.pm.in MANIFEST" + +case "$JWM_MODE" in + (development) + echo 'bundle_mode = --sourcemap' + echo 'rs_bin = target/debug/jwebmail-extract' + ;; + (production) + echo 'bundle_mode = --minify' + echo 'sass_mode = --style=compress --no-source-maps' + echo 'rust_mode = --release --target x86_64-unknown-linux-musl' + echo 'rs_bin = target/x86_64-unknown-linux-musl/release/jwebmail-extract' + ;; +esac + +case "$MAILDIR_EXTRACTOR_NAME" in + (extractrs) + echo "extractrs_dir = $JWM_EXTRACTRS_DIR" + printf '%s' 'build bin/jwebmail-extract: cargo' + FILES_RS='arguments.rs cmd.rs error.rs main.rs rfc822.rs cmd/count.rs cmd/folders.rs cmd/list.rs cmd/raw.rs' + for f in $FILES_RS + do + printf '%s' " \$extractrs_dir/src/$f" + done + echo + ;; +esac + +FILES_JS='src/displayheaders.js src/login_cram.js src/rendermail.js' +for f in $FILES_JS +do case "$JWM_MODE" in + (development) echo "build public/$f | public/$f.map: bundle $f";; + (production) echo "build public/$f: bundle $f";; +esac +done + +FILES_CSS='my_bulma' +for f in $FILES_CSS +do case "$JWM_MODE" in + (development) echo "build public/css/$f.css | public/css/$f.css.map: scss scss/$f.scss";; + (production) echo "build public/css/$f.css: scss scss/$f.scss";; +esac +done + +while read f +do [ -n "$f" ] && echo "build JWebmail-srcdist/$f: cpy $f" +done <MANIFEST + +printf '%s' "build JWebmail-srcdist.tar.zstd: dist" +while read f +do [ -n "$f" ] && printf '%s' " JWebmail-srcdist/$f" +done <MANIFEST +echo + +#if [ "$JWM_MODE" = production ] +#then for f in +#do echo "build JWebmail-bindist/$f: cp $f" +#done +#fi + +while read f +do [ -n "$f" ] && echo "build JWebmail-bindist/$f: cpy $f" +done <MANIFEST.BIN + +printf '%s' "build JWebmail-bindist.tar.zstd: dist" +while read f +do [ -n "$f" ] && printf '%s' " JWebmail-bindist/$f" +done <MANIFEST.BIN +echo diff --git a/jwebmail.development.toml b/jwebmail.development.toml index c4aa39c..f8b670b 100644 --- a/jwebmail.development.toml +++ b/jwebmail.development.toml @@ -1,21 +1,8 @@ -logpath = "log/" - -[defaults] -scriptadmin = "me@example.com" # for complaints / support +admin_mail = "me@example.com" # for complaints / support [i18n] default_language = "en" -directory = "lib/JWebmail/I18N" -# languages = ["en", "de"] - -[model.read.devel] -driver = "JWebmail::Model::ReadMails::MockMaildir" -#driver = "JWebmail::Model::ReadMails::MockJSON" - -[model.write] -#sendmail = "/usr/sbin/sendmail" -devel.block_writes = 1 -[session] -# secure sesssion [none, cram, s3d] -secure = "cram" +[model.read] +virtual_user = 'jmhoffmann' +mailbox_path = 't/testdata' diff --git a/jwebmail.production.toml b/jwebmail.production.toml new file mode 100644 index 0000000..b18fbaf --- /dev/null +++ b/jwebmail.production.toml @@ -0,0 +1,15 @@ +secret = "S3CR3T" + +admin_mail = "test@example.org" + +[i18n] +default_language = "en" + +[model.read] +virtual_user = netfehcom +mailbox_path = /home/netfehcom/users + +[hypnotoad] +proxy = 1 +pid_file = /run/jwebmail/hypnotoad.pid +listen = ["http://127.0.0.1:8081"] diff --git a/jwebmail.service b/jwebmail.service new file mode 100644 index 0000000..9c52ac1 --- /dev/null +++ b/jwebmail.service @@ -0,0 +1,13 @@ +[Unit] +Description=JWebmail managed by hypnotoad +After=network.target + +[Service] +Type=exec +ExecStart=/usr/bin/hypnotoad -f jwebmail +ExecReload=/usr/bin/hypnotoad -f jwebmail +PIDFile=/run/jwebmail/hypnotoad.pid +Environment=MOJO_LOG_SHORT=1 + +[Install] +WantedBy=multi-user.target diff --git a/jwebmail.service.tmpl b/jwebmail.service.tmpl deleted file mode 100644 index bbd7e39..0000000 --- a/jwebmail.service.tmpl +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -Description=JWebmail managed by hypnotoad -After=network.target - -[Service] -Type=exec -User=$JWM_USER -ExecStart=/usr/bin/hypnotoad -f $JWM_HOME/script/jwebmail -ExecReload=/usr/bin/hypnotoad -f $JWM_HOME/script/jwebmail -PIDFile=$JWM_HOME/script/hypnotoad.pid -WorkingDirectory=$JWM_HOME -Environment=MOJO_LOG_LEVEL=$JWM_LOG_LEVEL MOJO_LOG_SHORT=1 - -[Install] -WantedBy=multi-user.target diff --git a/lib/JWebmail.pm b/lib/JWebmail.pm index e2c291c..993ad59 100644 --- a/lib/JWebmail.pm +++ b/lib/JWebmail.pm @@ -8,6 +8,7 @@ use JWebmail::Controller::Webmail; use JWebmail::Model::ReadMails::Role; use JWebmail::Model::ReadMails::QMailAuthuser; use JWebmail::Model::WriteMails; +use JWebmail::Config qw'LOGIN_SCHEME MAILDIR_READER'; sub validateConf { @@ -15,11 +16,16 @@ sub validateConf { my $conf = $self->config; - exists $conf->{session}{secure} or die; - grep(sub { $_ eq $conf->{session}{secure} }, qw(none cram s3d)) > 0 or die; + exists $conf->{admin_mail} or die; + $conf->{admin_mail} =~ /@/ or die; - exists $conf->{defaults}{scriptadmin} or die; - $conf->{defaults}{scriptadmin} =~ /@/ or die; + exists $conf->{i18n}{default_language} or die; + $conf->{i18n}{default_language} =~ /^[\w_]+$/a or die; + + exists $conf->{model}{read}{virtual_user} or die; + getpwnam $conf->{model}{read}{virtual_user} or die; + exists $conf->{model}{read}{mailbox_path} or die; + -d $conf->{model}{read}{mailbox_path} or die; return 1; } @@ -36,11 +42,7 @@ sub startup { $self->plugin('TOMLConfig'); $self->validateConf; - if (my $logpath = $self->config('logpath')) { - $self->log->path($logpath . '/' . $self->mode . '.log'); - } - - if (fc $self->config->{session}{secure} eq fc 's3d') { + if (fc LOGIN_SCHEME eq fc 's3d') { $self->plugin('ServerSideSessionData'); } $self->plugin('Paginate'); @@ -51,23 +53,14 @@ sub startup { # initialize models my $read_mails = do { - if ($self->mode eq 'development') { - my $cls = $self->config->{model}{read}{devel}{driver}; - eval { load $cls; 1 } || die "Issue for module $cls with: $@"; - $cls->new(($self->config->{model}{read}{devel} // {})->%*) - } - else { - JWebmail::Model::ReadMails::QMailAuthuser->new( - ($self->config->{model}{read}{prod} // {})->%* - ) - } + my $cls = MAILDIR_READER; + eval { load $cls; 1 } || die "Issue for module $cls with: $@"; + $cls->new(($self->config->{model}{read} // {})->%*) }; die "given class @{[ ref $read_mails ]} does not ReadMails" unless $read_mails->DOES('JWebmail::Model::ReadMails::Role'); $self->helper(users => sub { $read_mails }); $self->helper(send_mail => sub { my ($c, $mail) = @_; JWebmail::Model::WriteMails::sendmail($mail) }); - $JWebmail::Model::WriteMails::Block_Writes = 1 - if $self->mode eq 'development' && $self->config->{model}{write}{devel}{block_writes}; $self->validator->add_check(mail_line => \&_mail_line); $self->validator->add_filter(non_empty_ul => \&_filter_empty_upload); @@ -149,7 +142,7 @@ JWebmail - Provides a web based e-mail client meant to be used with s/qmail. hypnotoad script/jwebmail -And use a server in reverse proxy configuration. +And use a server in reverse proxy configuration. =head1 DESCRIPTION diff --git a/lib/JWebmail/Config.pm.in b/lib/JWebmail/Config.pm.in new file mode 100644 index 0000000..e91f933 --- /dev/null +++ b/lib/JWebmail/Config.pm.in @@ -0,0 +1,25 @@ +package JWebmail::Config; + +use v5.24; +use warnings; +use utf8; + +use Exporter 'import'; +our @EXPORT_OK = qw(MAILDIR_READER MAILDIR_EXTRACTOR SENDMAIL LOGIN_SCHEME); + +use constant { + MAILDIR_READER => '@JWM_READ_MODEL@', + MAILDIR_EXTRACTOR => '@MAILDIR_EXTRACTOR_BIN@', + SENDMAIL => '@SENDMAIL@', + LOGIN_SCHEME => fc '@LOGIN_SCHEME@', +}; + +1 + +__END__ + +=encoding utf-8 + +=head1 NAME + +JWebmail::Config - Fixed configuration parameters diff --git a/lib/JWebmail/Controller/Webmail.pm b/lib/JWebmail/Controller/Webmail.pm index e06a8f7..94df668 100644 --- a/lib/JWebmail/Controller/Webmail.pm +++ b/lib/JWebmail/Controller/Webmail.pm @@ -8,6 +8,7 @@ use List::Util qw(any first); use Mojo::Util qw(encode decode b64_encode b64_decode); use Mojolicious::Types; +use JWebmail::Config 'LOGIN_SCHEME'; use JWebmail::View::Webmail; use JWebmail::View::RenderMail; @@ -72,7 +73,7 @@ sub _time :prototype(&$$) { sub login { my $self = shift; - my $uses_cram = $self->config->{session}{secure} eq 'cram'; + my $uses_cram = LOGIN_SCHEME eq fc 'cram_md5'; my $v = $self->validation; @@ -338,12 +339,12 @@ sub _rand_data { sub _session_passwd { my ($self, $passwd, $challenge) = @_; - my $secAlg = $self->config->{session}{secure}; + my $secAlg = LOGIN_SCHEME; $self->_warn_crypt; if (defined $passwd) { # set - if ($secAlg eq 'cram') { + if ($secAlg eq fc 'cram_md5') { $self->session(S_PASSWD() => $passwd, challenge => $challenge); } elsif ($secAlg eq 's3d') { @@ -352,7 +353,7 @@ sub _session_passwd { delete $self->session->{S_OTP_S3D_PW()}; return; } - die "'$passwd' contains invalid character \\n" if $passwd =~ /\n/; + die "'$passwd' contains invalid character \\n" if $passwd =~ /\n/; if (length $passwd < 20) { $passwd .= "\n" . ' ' x (20 - length($passwd) - 1); } @@ -366,7 +367,7 @@ sub _session_passwd { } } else { # get - if ($secAlg eq 'cram') { + if ($secAlg eq fc 'cram_md5') { wantarray or carp "you forgot the challenge"; return ($self->session(S_PASSWD), $self->session('challenge')); } @@ -472,18 +473,18 @@ Currently the following modes are supported: =over 6 -=item none +=item none The password is plainly stored in session cookie. The cookie is stored on the client side and send with every request. -=item cram +=item cram A nonce is send to the client and the cram_md5 is generated there via js and crypto-js. This is vulnurable to replay attacks as the nonce is not invalidated ever. -=item s3d +=item s3d The password is stored on the server. Additionally the password is encrypted by an one-time-pad that is stored in the users cookie. diff --git a/lib/JWebmail/Model/ReadMails/MockJSON.pm b/lib/JWebmail/Model/ReadMails/MockJSON.pm index 64d6873..9ad5f09 100644 --- a/lib/JWebmail/Model/ReadMails/MockJSON.pm +++ b/lib/JWebmail/Model/ReadMails/MockJSON.pm @@ -21,14 +21,12 @@ use constant { with 'JWebmail::Model::ReadMails::Role'; -sub new { bless {}, shift } +sub new { bless {%$_[1]}, shift } sub _read_json_file { - my ($file_name) = @_; + my ($self, $file_name) = @_; - use constant PREFIX => 't/testdata/json/'; - - open my $body_file, '<', PREFIX . $file_name; + open my $body_file, '<', $self->{mailbox_path} . '/' . $file_name; local $/; my $body = <$body_file>; close $body_file; @@ -37,13 +35,15 @@ sub _read_json_file { } sub list_reply { - state $init = _read_json_file('msgs.json'); + my $self = shift; + state $init = _read_json_file($self, 'msgs.json'); } sub read_reply { + my $self = shift; state $init = { - 'SC-ORD-MAIL54526c63b751646618a793be3f8329cca@sc-ord-mail5' => _read_json_file('msg2.json'), - 'example' => _read_json_file('msg.json'), + 'SC-ORD-MAIL54526c63b751646618a793be3f8329cca@sc-ord-mail5' => _read_json_file($self, 'msg2.json'), + 'example' => _read_json_file($self, 'msg.json'), }; } diff --git a/lib/JWebmail/Model/ReadMails/MockMaildir.pm b/lib/JWebmail/Model/ReadMails/MockMaildir.pm index fc9cc4a..f9d530f 100644 --- a/lib/JWebmail/Model/ReadMails/MockMaildir.pm +++ b/lib/JWebmail/Model/ReadMails/MockMaildir.pm @@ -4,35 +4,26 @@ use Mojo::Base 'JWebmail::Model::ReadMails::QMailAuthuser'; use Mojo::JSON 'decode_json'; -use Digest::HMAC_MD5 'hmac_md5_hex'; +use JWebmail::Config 'LOGIN_SCHEME'; +if (LOGIN_SCHEME eq fc 'cram_md5') { + require Digest::HMAC_MD5; + Digest::HMAC_MD5->import('hmac_md5_hex'); +} use constant { VALID_USER => 'mockmaildir@example.org', VALID_PW => '12345', }; -has user => sub { $ENV{USER} }; -has maildir => 't/testdata/'; -has extractor => 'python'; - -our %EXTRACTORS = ( - perl => 'script/qmauth.pl', - python => 'script/qmauth.py', - rust => 'bin/jwebmail-extract', -); - sub new { my $cls = shift; my %args = @_ == 1 ? %$_[0] : @_; my $self = bless {%args}, ref $cls || $cls; - $self->user; - $self->maildir; - $self->next::method(prog => $EXTRACTORS{$self->extractor}); - return $self; + return $self->next::method(); } @@ -56,7 +47,7 @@ sub start_qmauth { my ($auth, $mode, $args) = @_; my $mail_user = 'maildir'; - my @exec = ($EXTRACTORS{$self->extractor}, $self->maildir, $self->user, $mail_user, $mode, @$args); + my @exec = ($self->{prog}, $self->{mailbox_path}, $self->{virtual_user}, $mail_user, $mode, @$args); my $pid = open(my $reader, '-|', @exec) or die "failed to create subprocess: $!"; diff --git a/lib/JWebmail/Model/ReadMails/QMailAuthuser.pm b/lib/JWebmail/Model/ReadMails/QMailAuthuser.pm index 19f8b12..b2015aa 100644 --- a/lib/JWebmail/Model/ReadMails/QMailAuthuser.pm +++ b/lib/JWebmail/Model/ReadMails/QMailAuthuser.pm @@ -1,10 +1,9 @@ package JWebmail::Model::ReadMails::QMailAuthuser; -use v5.22; +use v5.24; use warnings; use utf8; -use File::Basename 'fileparse'; use IPC::Open2; use JSON::PP 'decode_json'; use Params::Check 'check'; @@ -12,6 +11,8 @@ use Role::Tiny::With; use Scalar::Util 'blessed'; use namespace::clean; +use JWebmail::Config 'MAILDIR_EXTRACTOR'; + with 'JWebmail::Model::ReadMails::Role'; @@ -73,12 +74,9 @@ package JWebmail::Model::ReadMails::QMailAuthuser::Error { my $QMailAuthuserCheck = { - user => {required => 1}, - maildir => {required => 1}, - prog => {required => 1}, - prefix => {default => ''}, - qmail_dir => {default => '/var/qmail/'}, - logfile => {default => '/dev/null'}, + virtual_user => {required => 1}, + mailbox_path => {required => 1}, + qmail_dir => {default => '/var/qmail/'}, }; sub new { @@ -94,6 +92,7 @@ sub new { local $Params::Check::WARNINGS_FATAL = 1; my $s = check($QMailAuthuserCheck, $self) or die __PACKAGE__ . " creation failed!"; + $s->{prog} = MAILDIR_EXTRACTOR; return bless $s, $cls; } @@ -180,11 +179,9 @@ sub build_arg { my ($user_name) = $user_mail_addr =~ /(\w*)@/; - return $self->{qmail_dir}.'/bin/qmail-authuser' - . $self->{prefix} . ' ' - . join(' ', map { my $x = s/(['\\])/\\$1/gr; "'$x'" } ($self->{prog}, $self->{maildir}, $self->{user}, $user_name, $mode, @$args)) - . ' 3<&0' - . ' 2>>'.$self->{logfile}; + return $self->{qmail_dir}.'/bin/qmail-authuser ' + . join(' ', map { my $x = s/(['\\])/\\$1/gr; "'$x'" } ($self->{prog}, $self->{mailbox_path}, $self->{virtual_user}, $user_name, $mode, @$args)) + . ' 3<&0'; } sub start_qmauth { diff --git a/lib/JWebmail/Model/WriteMails.pm b/lib/JWebmail/Model/WriteMails.pm index 05c4cd1..751192a 100644 --- a/lib/JWebmail/Model/WriteMails.pm +++ b/lib/JWebmail/Model/WriteMails.pm @@ -1,9 +1,11 @@ package JWebmail::Model::WriteMails; -use v5.18; +use v5.24; use warnings; use utf8; +use JWebmail::Config 'SENDMAIL'; + use Exporter 'import'; our @EXPORT_OK = qw(sendmail); @@ -51,7 +53,7 @@ sub _build_mail { sub _send { my ($mime, @recipients) = @_; - open(my $m, '|-', 'sendmail', '-i', @recipients) + open(my $m, '|-', SENDMAIL, '-i', @recipients) or die 'Connecting to sendmail failed. Is it in your PATH?'; $m->print($mime->as_string()); close($m); diff --git a/lib/JWebmail/Plugin/I18N2/Maketext.pm b/lib/JWebmail/Plugin/I18N2/Maketext.pm index ef3b08d..1046a99 100644 --- a/lib/JWebmail/Plugin/I18N2/Maketext.pm +++ b/lib/JWebmail/Plugin/I18N2/Maketext.pm @@ -1,12 +1,12 @@ package JWebmail::Plugin::I18N2::Maketext; -use v5.22; +use v5.24; use warnings; use utf8; use JWebmail::I18N; -use File::Basename 'fileparse'; +use Mojo::File qw(path curfile); use Role::Tiny::With; with 'JWebmail::Plugin::I18N2::Role'; @@ -16,19 +16,10 @@ sub new { my $class = shift; my $conf = @_ == 1 ? shift : {@_}; - my $lexica = $conf->{directory}; + my $lexica = curfile->dirname->child('..', '..', 'I18N'); - my @languages = keys %{$conf->{languages} // {}}; - - unless (@languages) { - use autodie; - - opendir(my $dh, $lexica); - my @res = grep { /\.pm$/ && -f "$lexica/$_" } readdir $dh; - closedir($dh); - @languages = map { scalar fileparse $_, '.pm' } @res; - @languages = map { my ($l, $c) = split '_', $_, 2; $c ? "$l-\U$c" : $l } @languages; - } + my $res = $lexica->list()->map('basename', '.pm'); + my @languages = map { my ($l, $c) = split '_', $_, 2; $c ? "$l-\U$c" : $l } @$res; if (my $dl = $conf->{default_language}) { push @languages, $dl; }; my $self = {}; diff --git a/t/Webmail.t b/t/Webmail.t index 3deb547..c88b31a 100644 --- a/t/Webmail.t +++ b/t/Webmail.t @@ -14,12 +14,11 @@ my $pw = JWebmail::Model::ReadMails::MockJSON::VALID_PW; my $t = Test::Mojo->new('JWebmail', { - model => { read => { devel => { driver => 'JWebmail::Model::ReadMails::MockJSON' }}, - write => { devel => { block_writes => 1 }}}, - i18n => { default_language => DEFAULT_LANGUAGE, directory => 'lib/JWebmail/I18N' }, - session => { secure => 'none' }, - defaults => { scriptadmin => 'test@example.org' }, + model => { read => { virtual_user => $ENV{USER}, mailbox_path => 't/testdata/json' }}, + i18n => { default_language => DEFAULT_LANGUAGE }, + admin_mail => 'test@example.org', }); +$t->app->helper(users => sub { state $read = 'JWebmail::Model::ReadMails::MockJSON'->new($t->app->config->{model}{read}) }); $t->get_ok('/')->status_is(200); diff --git a/templates/webmail/about.html.ep b/templates/webmail/about.html.ep index a5cd4a7..15e35ab 100644 --- a/templates/webmail/about.html.ep +++ b/templates/webmail/about.html.ep @@ -38,7 +38,6 @@ and currently maintained by <a href="mailto:jannis@fehcom.de">Jannis M. Hoffmann</a> </p> - <p> <h3>Supported languages</h3> diff --git a/templates/webmail/login.html.ep b/templates/webmail/login.html.ep index 54ab40a..378ac05 100644 --- a/templates/webmail/login.html.ep +++ b/templates/webmail/login.html.ep @@ -1,6 +1,7 @@ % layout 'mainlayout'; -% my $uses_cram = config->{session}{secure} eq 'cram'; +% use JWebmail::Config; +% my $uses_cram = JWebmail::Config::LOGIN_SCHEME eq fc 'cram_md5'; <section class=section> <div class="container is-max-desktop box"> |