From 67440844c422ee30b31df9a46a7f99ac0e833add Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Sat, 9 Nov 2019 03:13:45 +0100 Subject: Refactor and improve test suite. --- tests/run | 173 ++++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 113 insertions(+), 60 deletions(-) (limited to 'tests/run') diff --git a/tests/run b/tests/run index ee11757..cb52518 100755 --- a/tests/run +++ b/tests/run @@ -22,27 +22,27 @@ set -ue PATH=/usr/bin:/bin export PATH -if [ $# -ne 1 ]; then - printf "Usage: %s TESTNAME\\n" "$0" >&2 +if [ $# -eq 0 ] || [ $# -gt 2 ]; then + printf "Usage: %s TESTFILE [TESTNAME]\\n" "$0" >&2 exit 1 fi -TEST="${1%/}" -TEST="${TEST##*/}" -NAME="${TEST#[0-9]*-}" -TESTDIR="$(dirname -- "$0")/$TEST" +TESTDIR="$(dirname -- "$0")/$1" +TESTNAME="${2-$1}" if [ ! -d "$TESTDIR" ]; then printf "ERROR: Not a directory: %s\\n" "$TESTDIR" >&2 exit 1 fi -ROOTDIR="$(mktemp --tmpdir="${TMPDIR:-/dev/shm}" --directory "$NAME.XXXXXXXXXX")" +ROOTDIR="$(mktemp --tmpdir="${TMPDIR:-/dev/shm}" --directory "$1.XXXXXXXXXX")" trap 'rm -rf -- "$ROOTDIR"' EXIT INT TERM -STDOUT="$ROOTDIR/stdout" -STDERR="$ROOTDIR/stderr" +_STDOUT="$ROOTDIR/stdout" +_STDERR="$ROOTDIR/stderr" TMPDIR="$ROOTDIR/tmp" +STDERR="$(mktemp --tmpdir="$ROOTDIR" "stderr.XXXXXXXXXX")" mkdir -- "$TMPDIR" "$ROOTDIR/home" +declare -a REMOTES=() # Set environment for the given user environ_set() { @@ -60,24 +60,35 @@ environ_set() { # Prepare the test harness prepare() { declare -a ENVIRON=() - local src cfg target u home + local src cfg target u home n capability + if [ -f "$TESTDIR/remotes" ]; then + for cfg in $(seq 1 "$(< "$TESTDIR/remotes")"); do + REMOTES+=( "remote$cfg" ) + done + else + REMOTES+=( "remote" ) + fi # copy dovecot config - for src in "$TESTDIR/local.conf" "$TESTDIR"/remote*.conf; do - [ -r "$src" ] || continue - u="${src#"$TESTDIR/"}" - u="${u%.conf}" - home="$ROOTDIR/home/$u" + for u in "local" "${REMOTES[@]}"; do + home="$ROOTDIR/$u/home" export "HOME_$u"="$home" - mkdir -pm0755 -- "$home/.local/bin" mkdir -pm0700 -- "$home/.config/dovecot" cat >"$home/.config/dovecot/config" <<-EOF - log_path = /dev/null - mail_home = $ROOTDIR/home/%u + log_path = $HOME_local/mail.log + mail_home = $home + mail_location = dbox:~/inbox:LAYOUT=index mailbox_list_index = yes ssl = no + namespace inbox { + inbox = yes + } EOF - cat >>"$home/.config/dovecot/config" <"$src" + if [ -f "$TESTDIR/$u.conf" ]; then + cat >>"$home/.config/dovecot/config" <"$TESTDIR/$u.conf" + fi + environ_set "$u" + mkdir -pm0755 -- "$home/.local/bin" cat >"$home/.local/bin/doveadm" <<-EOF #!/bin/sh exec env -i ${ENVIRON[@]@Q} \\ @@ -89,36 +100,62 @@ prepare() { # copy interimap config mkdir -pm0700 -- "$HOME_local/.local/share/interimap" mkdir -pm0700 -- "$HOME_local/.config/interimap" - for cfg in "$TESTDIR"/remote*.conf; do - cfg="${cfg#"$TESTDIR/remote"}" - cfg="${cfg%.conf}" - u="remote$cfg" + for u in "${REMOTES[@]}"; do + n="${u#remote}" eval home="\$HOME_$u" - if [ -f "$TESTDIR/interimap.conf" ]; then - cat <"$TESTDIR/interimap.conf" >>"$HOME_local/.config/interimap/config$cfg" - fi - cat >>"$HOME_local/.config/interimap/config$cfg" <<-EOF + + cat >>"$HOME_local/.config/interimap/config$n" <<-EOF database = $u.db - + #logfile = $HOME_local/interimap$n.log + EOF + if [ -f "$TESTDIR/interimap$n.conf" ]; then + cat <"$TESTDIR/interimap$n.conf" >>"$HOME_local/.config/interimap/config$n" + fi + + cat >>"$HOME_local/.config/interimap/config$n" <<-EOF + [local] type = tunnel command = exec ${HOME_local@Q}/.local/bin/doveadm exec imap null-stderr = NO + EOF + if [ -f "$TESTDIR/interimap$n.local" ]; then + cat <"$TESTDIR/interimap$n.local" >>"$HOME_local/.config/interimap/config$n" + fi + + # `doveadm exec imap` ignores 'imap_capability' from doveconf/config + capability="$(doveconf -c "$home/.config/dovecot/config" -h imap_capability)" + cat >>"$HOME_local/.config/interimap/config$n" <<-EOF [remote] type = tunnel - command = exec ${home@Q}/.local/bin/doveadm exec imap + command = exec ${home@Q}/.local/bin/doveadm exec imap ${capability:+-oimap_capability=${capability@Q}} null-stderr = NO EOF + if [ -f "$TESTDIR/interimap$n.remote" ]; then + cat <"$TESTDIR/interimap$n.remote" >>"$HOME_local/.config/interimap/config$n" + fi done } prepare # Wrappers for interimap(1) and doveadm(1) interimap() { - declare -a ENVIRON=() + declare -a ENVIRON=() r=0 environ_set "local" - env -i "${ENVIRON[@]}" perl -I./lib -T ./interimap "$@" + env -i "${ENVIRON[@]}" perl -I./lib -T ./interimap "$@" 2>"$STDERR" || r=$? + cat "$STDERR" >&2 + return $r +} +interimap_init() { + local u="${1-remote}" + local db="$XDG_DATA_HOME/interimap/$u.db" + local cfg="config${u#remote}" + + test \! -e "$db" || error "Database already exists" 1 + interimap --config "$cfg" || error "Couldn't initialize interimap" 1 + test -f "$db" || error "Database is still missing" 1 + grep -Fx "Creating new schema in database file $db" <"$STDERR" || error "DB wasn't created" 1 } doveadm() { if [ $# -le 2 ] || [ "$1" != "-u" ]; then @@ -167,9 +204,7 @@ dump_test_result() { local above="<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" local src u home declare -a ENVIRON=() - for src in "$TESTDIR/local.conf" "$TESTDIR"/remote*.conf; do - u="${src#"$TESTDIR/"}" - u="${u%.conf}" + for u in "local" "${REMOTES[@]}"; do environ_set "$u" eval home="\$HOME_$u" printf "%s dovecot configuration:\\n%s\\n" "$u" "$below" @@ -177,16 +212,18 @@ dump_test_result() { printf "%s\\n\\n" "$above" done - printf "(local) interimap configuration:\\n%s\\n" "$below" - cat <"$HOME_local/.config/interimap/config" - printf "%s\\n\\n" "$above" + for u in "${REMOTES[@]}"; do + printf "interimap configuration (local <-> $u):\\n%s\\n" "$below" + cat <"$HOME_local/.config/interimap/config${u#remote}" + printf "%s\\n\\n" "$above" + done - printf "standard output was:\\n%s\\n" "$below" - cat <"$STDOUT" + printf "standard output:\\n%s\\n" "$below" + cat <"$_STDOUT" printf "%s\\n\\n" "$above" - printf "standard error was:\\n%s\\n" "$below" - cat <"$STDERR" + printf "standard error:\\n%s\\n" "$below" + cat <"$_STDERR" printf "%s\\n\\n" "$above" } @@ -225,7 +262,7 @@ check_mailbox_status2() { WHERE mailbox = $blob EOF ) - check_mailbox_status_values "local" "$lmailbox" $lUIDVALIDITY $lUIDNEXT $lHIGHESTMODSEQ $MESSAGES + check_mailbox_status_values "local" "$lmailbox" $lUIDVALIDITY $lUIDNEXT $lHIGHESTMODSEQ $MESSAGES check_mailbox_status_values "$u" "$rmailbox" $rUIDVALIDITY $rUIDNEXT $rHIGHESTMODSEQ $MESSAGES local a b @@ -304,38 +341,54 @@ check_mailbox_list() { <( printf "%s" "${lmailboxes[*]}" | sort ) <( printf "%s" "${rmailboxes[*]}" | sort ) } -# Wrappers for grep(1) and `grep -C` -xgrep() { - if ! grep -q "$@"; then - printf "\`grep %s\` failed on line %d\\n" "${*@Q}" ${BASH_LINENO[0]} >&2 - exit 1 - fi -} +# Wrapper for `grep -c` xcgrep() { local m="$1" n shift if ! n="$(grep -c "$@")" || [ $m -ne $n ]; then - printf "\`grep -c %s\` failed on line %d: %d != %d\\n" "${*@Q}" ${BASH_LINENO[0]} "$m" "$n" >&2 - exit 1 + error "\`grep -c ${*@Q}\` failed ($m != $n)" 1 fi } +error() { + local err="${1+": $1"}" i=${2-0} + printf "ERROR$err on file %s line %d\\n" "${BASH_SOURCE[i+1]}" ${BASH_LINENO[i]} >&2 + exit 1 +} +ptree_abort() { + local pid + for pid in "$@"; do + # kill a process and its children + pkill -TERM -P "$pid" || printf "pkill(1) exited with status %d\\n" "$?" >&2 + kill -TERM "$pid" || printf "kill(1) exited with status %d\\n" "$?" >&2 + done + wait +} +step_start() { printf "%s%s..." "${INDENT-}" "$1" >&3; } +step_done() { passed >&3; } +failed() { + [ -t 1 ] && printf " \\x1B[1;31mFAILED\\x1B[0m\\n" || echo " FAILED" +} +passed() { + [ -t 1 ] && printf " \\x1B[1;32mPASSED\\x1B[0m\\n" || echo " PASSED" +} # Run test in a sub-shell declare -a ENVIRON=() environ_set "local" -export TMPDIR TESTDIR STDOUT STDERR "${ENVIRON[@]}" +export TMPDIR TESTDIR STDERR "${ENVIRON[@]}" export -f environ_set doveadm interimap sqlite3 sample_message deliver +export -f interimap_init ptree_abort step_start step_done passed export -f check_mailbox_status check_mailbox_status_values check_mailbox_status2 -export -f check_mailboxes_status check_mailbox_list xgrep xcgrep -printf "%s..." "$TEST" -if ! bash -ue "$TESTDIR/run" >"$STDOUT" 2>"$STDERR"; then - echo " FAILED" - dump_test_result +export -f check_mailboxes_status check_mailbox_list xcgrep error +[ "$TESTNAME" = "..." ] || printf "%s%s..." "${INDENT-}" "$TESTNAME" +if ! bash -ue "$TESTDIR/t" 3>&1 >"$_STDOUT" 2>"$_STDERR"; then + failed + [ "${QUIET-n}" = "y" ] || dump_test_result exit 1 else - echo " OK" - if grep -Paq "\\x00" -- "$STDOUT" "$STDERR"; then - printf "\\tWarn: binary output (outstanding \\0)!\\n" + [ "$TESTNAME" = "..." ] || passed + if grep -Paq "\\x00" -- "$_STDOUT" "$_STDERR"; then + printf "\\tWARN: binary output (outstanding \\0)!\\n" fi exit 0 fi -- cgit v1.2.3 From 78522acced782587b3768f3fb57f2f25cb905754 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Sun, 10 Nov 2019 03:18:26 +0100 Subject: Test suite: add new tests for authentication. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This can't be done with `doveadm exec imap`, so the IMAPd needs to bind to TCP port 10143 on the loopback interface. Also, no longer pass ‘imap_capability’ Dovecot setting explicitely to `doveadm exec imap`; changed tests/sync-live-crippled to use type=imap instead of type=tunnel. --- tests/auth-login/interimap.remote | 5 + tests/auth-login/remote.conf | 2 + tests/auth-login/t | 12 +++ tests/auth-logindisabled/interimap.remote | 1 + tests/auth-logindisabled/remote.conf | 4 + tests/auth-logindisabled/t | 16 +++ tests/auth-noplaintext/interimap.remote | 3 + tests/auth-noplaintext/remote.conf | 1 + tests/auth-noplaintext/t | 15 +++ tests/auth-sasl-plain-no-ir/interimap.remote | 1 + tests/auth-sasl-plain-no-ir/remote.conf | 2 + tests/auth-sasl-plain-no-ir/t | 26 +++++ tests/auth-sasl-plain/interimap.remote | 4 + tests/auth-sasl-plain/remote.conf | 1 + tests/auth-sasl-plain/t | 12 +++ tests/list | 7 ++ tests/run | 109 ++++++++++++++++----- tests/snippets/dovecot/imapd.conf | 12 +++ .../dovecot/interimap-required-capabilities.conf | 3 + tests/sync-live-crippled/interimap.remote | 1 + tests/sync-live-crippled/remote.conf | 4 +- 21 files changed, 213 insertions(+), 28 deletions(-) create mode 100644 tests/auth-login/interimap.remote create mode 100644 tests/auth-login/remote.conf create mode 100644 tests/auth-login/t create mode 120000 tests/auth-logindisabled/interimap.remote create mode 100644 tests/auth-logindisabled/remote.conf create mode 100644 tests/auth-logindisabled/t create mode 100644 tests/auth-noplaintext/interimap.remote create mode 120000 tests/auth-noplaintext/remote.conf create mode 100644 tests/auth-noplaintext/t create mode 120000 tests/auth-sasl-plain-no-ir/interimap.remote create mode 100644 tests/auth-sasl-plain-no-ir/remote.conf create mode 100644 tests/auth-sasl-plain-no-ir/t create mode 100644 tests/auth-sasl-plain/interimap.remote create mode 100644 tests/auth-sasl-plain/remote.conf create mode 100644 tests/auth-sasl-plain/t create mode 100644 tests/snippets/dovecot/imapd.conf create mode 100644 tests/snippets/dovecot/interimap-required-capabilities.conf create mode 120000 tests/sync-live-crippled/interimap.remote (limited to 'tests/run') diff --git a/tests/auth-login/interimap.remote b/tests/auth-login/interimap.remote new file mode 100644 index 0000000..b7d67bf --- /dev/null +++ b/tests/auth-login/interimap.remote @@ -0,0 +1,5 @@ +type = imap +host = localhost +port = 10143 +STARTTLS = NO +auth = login diff --git a/tests/auth-login/remote.conf b/tests/auth-login/remote.conf new file mode 100644 index 0000000..4ab127a --- /dev/null +++ b/tests/auth-login/remote.conf @@ -0,0 +1,2 @@ +!include conf.d/imapd.conf +auth_mechanisms = plain login diff --git a/tests/auth-login/t b/tests/auth-login/t new file mode 100644 index 0000000..7fd83d5 --- /dev/null +++ b/tests/auth-login/t @@ -0,0 +1,12 @@ +for ((i = 0; i < 32; i++)); do + u="$(shuf -n1 -e "local" "remote")" + sample_message | deliver -u "$u" +done + +# check that credentials aren't leaked to the debug output +interimap --debug || error +grep -Fx "remote: C: xxx LOGIN [REDACTED]" <"$STDERR" || error + +check_mailbox_status "INBOX" + +# vim: set filetype=sh : diff --git a/tests/auth-logindisabled/interimap.remote b/tests/auth-logindisabled/interimap.remote new file mode 120000 index 0000000..a4ea3f3 --- /dev/null +++ b/tests/auth-logindisabled/interimap.remote @@ -0,0 +1 @@ +../auth-sasl-plain/interimap.remote \ No newline at end of file diff --git a/tests/auth-logindisabled/remote.conf b/tests/auth-logindisabled/remote.conf new file mode 100644 index 0000000..1f02afe --- /dev/null +++ b/tests/auth-logindisabled/remote.conf @@ -0,0 +1,4 @@ +!include conf.d/imapd.conf + +# trick dovecot into treating local connections as insecure +imap_capability = +LOGINDISABLED diff --git a/tests/auth-logindisabled/t b/tests/auth-logindisabled/t new file mode 100644 index 0000000..0bcd0d6 --- /dev/null +++ b/tests/auth-logindisabled/t @@ -0,0 +1,16 @@ +! interimap --debug || error + +# double check the presence of 'LOGINDISABLED' in the preauth capability list +grep -oE -m1 '^remote: S: \* OK \[CAPABILITY IMAP4rev1( [^]]*)? AUTH=[^]]*\]' <"$STDERR" >"$TMPDIR/capability" + +sed -ri 's/^remote: S: \* OK \[CAPABILITY (.*)\]$/\1/' "$TMPDIR/capability" +tr " " "\\n" <"$TMPDIR/capability" >"$TMPDIR/capabilities" +grep -Fx "IMAP4rev1" <"$TMPDIR/capabilities" || error +grep -Fx "LOGINDISABLED" <"$TMPDIR/capabilities" || error +! grep -Fx "STARTTLS" <"$TMPDIR/capabilities" || error # otherwise we'd try to upgrade the connectionn + +# make sure we didn't send any credentials +grep -Fx "remote: ERROR: Logins are disabled." <"$STDERR" || error +! grep -E "^remote: C: .* (AUTHENTICATE|LOGIN) " <"$STDERR" || error + +# vim: set filetype=sh : diff --git a/tests/auth-noplaintext/interimap.remote b/tests/auth-noplaintext/interimap.remote new file mode 100644 index 0000000..60567e2 --- /dev/null +++ b/tests/auth-noplaintext/interimap.remote @@ -0,0 +1,3 @@ +type = imap +host = localhost +port = 10143 diff --git a/tests/auth-noplaintext/remote.conf b/tests/auth-noplaintext/remote.conf new file mode 120000 index 0000000..dbbb908 --- /dev/null +++ b/tests/auth-noplaintext/remote.conf @@ -0,0 +1 @@ +../auth-sasl-plain/remote.conf \ No newline at end of file diff --git a/tests/auth-noplaintext/t b/tests/auth-noplaintext/t new file mode 100644 index 0000000..11d7d4d --- /dev/null +++ b/tests/auth-noplaintext/t @@ -0,0 +1,15 @@ +! interimap --debug || error + +# double check the presence of 'STARTTLS' in the preauth capability list +grep -oE -m1 '^remote: S: \* OK \[CAPABILITY IMAP4rev1( [^]]*)? AUTH=[^]]*\]' <"$STDERR" >"$TMPDIR/capability" + +sed -ri 's/^remote: S: \* OK \[CAPABILITY (.*)\]$/\1/' "$TMPDIR/capability" +tr " " "\\n" <"$TMPDIR/capability" >"$TMPDIR/capabilities" + grep -Fx "IMAP4rev1" <"$TMPDIR/capabilities" || error +! grep -Fx "STARTTLS" <"$TMPDIR/capabilities" || error + +# make sure we didn't send any credentials +grep -Fx "remote: ERROR: Server did not advertise STARTTLS capability." <"$STDERR" || error +! grep -E "^remote: C: .* (AUTHENTICATE|LOGIN) " <"$STDERR" || error + +# vim: set filetype=sh : diff --git a/tests/auth-sasl-plain-no-ir/interimap.remote b/tests/auth-sasl-plain-no-ir/interimap.remote new file mode 120000 index 0000000..a4ea3f3 --- /dev/null +++ b/tests/auth-sasl-plain-no-ir/interimap.remote @@ -0,0 +1 @@ +../auth-sasl-plain/interimap.remote \ No newline at end of file diff --git a/tests/auth-sasl-plain-no-ir/remote.conf b/tests/auth-sasl-plain-no-ir/remote.conf new file mode 100644 index 0000000..dae9545 --- /dev/null +++ b/tests/auth-sasl-plain-no-ir/remote.conf @@ -0,0 +1,2 @@ +!include conf.d/imapd.conf +!include conf.d/interimap-required-capabilities.conf diff --git a/tests/auth-sasl-plain-no-ir/t b/tests/auth-sasl-plain-no-ir/t new file mode 100644 index 0000000..17aa9e6 --- /dev/null +++ b/tests/auth-sasl-plain-no-ir/t @@ -0,0 +1,26 @@ +n=1 # at least one message to send remotely +sample_message | deliver -u "local" +for ((i = 0; i < 32; i++)); do + u="$(shuf -n1 -e "local" "remote")" + [ "$u" = "remote" ] || n=$(( n+1 )) + sample_message | deliver -u "$u" +done + +# check that credentials aren't leaked to the debug output +interimap --debug || error +grep -Fx "remote: C: xxx AUTHENTICATE PLAIN [REDACTED]" <"$STDERR" || error + +# make sure we didn't use SASL-IR +grep -oE -m1 '^remote: S: \* OK \[CAPABILITY IMAP4rev1( [^]]*)? AUTH=[^]]*\]' <"$STDERR" >"$TMPDIR/capability" + +sed -ri 's/^remote: S: \* OK \[CAPABILITY (.*)\]$/\1/' "$TMPDIR/capability" +tr " " "\\n" <"$TMPDIR/capability" >"$TMPDIR/capabilities" + grep -Fx "IMAP4rev1" <"$TMPDIR/capabilities" || error +! grep -Fx "SASL-IR" <"$TMPDIR/capabilities" || error + +# make sure all literals were synchronizing (and that we didn't use MULTIAPPEND) +xcgrep "$n" -E "^remote(\(INBOX\))?: C: [0-9]+ APPEND INBOX .* \{[0-9]+\}$" <"$STDERR" + +check_mailbox_status "INBOX" + +# vim: set filetype=sh : diff --git a/tests/auth-sasl-plain/interimap.remote b/tests/auth-sasl-plain/interimap.remote new file mode 100644 index 0000000..9c0a623 --- /dev/null +++ b/tests/auth-sasl-plain/interimap.remote @@ -0,0 +1,4 @@ +type = imap +host = localhost +port = 10143 +STARTTLS = NO diff --git a/tests/auth-sasl-plain/remote.conf b/tests/auth-sasl-plain/remote.conf new file mode 100644 index 0000000..3ccbd42 --- /dev/null +++ b/tests/auth-sasl-plain/remote.conf @@ -0,0 +1 @@ +!include conf.d/imapd.conf diff --git a/tests/auth-sasl-plain/t b/tests/auth-sasl-plain/t new file mode 100644 index 0000000..68f71a9 --- /dev/null +++ b/tests/auth-sasl-plain/t @@ -0,0 +1,12 @@ +for ((i = 0; i < 32; i++)); do + u="$(shuf -n1 -e "local" "remote")" + sample_message | deliver -u "$u" +done + +# check that credentials aren't leaked to the debug output +interimap --debug || error +grep -Fx "remote: C: xxx AUTHENTICATE PLAIN [REDACTED]" <"$STDERR" || error + +check_mailbox_status "INBOX" + +# vim: set filetype=sh : diff --git a/tests/list b/tests/list index 21aa3f4..86034ef 100644 --- a/tests/list +++ b/tests/list @@ -31,6 +31,13 @@ largeint Large UIDVALIDITY/UIDNEXT/HIGHESTMODSEQ values resume Resume when aborted repair --repair +. Authentication + auth-sasl-plain AUTHENTICATE (SASL PLAIN) + auth-sasl-plain-no-ir AUTHENTICATE (SASL PLAIN, no SASL-IR) + auth-login LOGIN + auth-logindisabled LOGINDISABLED + auth-noplaintext abort when STARTTLS is not offered + . Live synchronization (60s) sync-live local/remote simulation sync-live-crippled local/remote simulation (crippled remote) diff --git a/tests/run b/tests/run index cb52518..bff9c18 100755 --- a/tests/run +++ b/tests/run @@ -27,7 +27,8 @@ if [ $# -eq 0 ] || [ $# -gt 2 ]; then exit 1 fi -TESTDIR="$(dirname -- "$0")/$1" +BASEDIR="$(dirname -- "$0")" +TESTDIR="$BASEDIR/$1" TESTNAME="${2-$1}" if [ ! -d "$TESTDIR" ]; then printf "ERROR: Not a directory: %s\\n" "$TESTDIR" >&2 @@ -35,7 +36,18 @@ if [ ! -d "$TESTDIR" ]; then fi ROOTDIR="$(mktemp --tmpdir="${TMPDIR:-/dev/shm}" --directory "$1.XXXXXXXXXX")" -trap 'rm -rf -- "$ROOTDIR"' EXIT INT TERM +declare -a DOVECOT_SERVER=() +trap cleanup EXIT INT TERM +cleanup() { + local pid c + for c in "${DOVECOT_SERVER[@]}"; do + if [ ! -f "$c" ] || ! env -i PATH="/usr/bin:/bin" doveadm -c "$c" stop; then + pid="$(< "${c%/*}/run/master.pid")" + kill -TERM "$pid" || printf "kill(1) exited with status %d\\n" "$?" >&2 + fi + done + rm -rf -- "$ROOTDIR" +} _STDOUT="$ROOTDIR/stdout" _STDERR="$ROOTDIR/stderr" @@ -60,7 +72,7 @@ environ_set() { # Prepare the test harness prepare() { declare -a ENVIRON=() - local src cfg target u home n capability + local src cfg target u home n proto if [ -f "$TESTDIR/remotes" ]; then for cfg in $(seq 1 "$(< "$TESTDIR/remotes")"); do REMOTES+=( "remote$cfg" ) @@ -72,27 +84,64 @@ prepare() { for u in "local" "${REMOTES[@]}"; do home="$ROOTDIR/$u/home" export "HOME_$u"="$home" - mkdir -pm0700 -- "$home/.config/dovecot" - cat >"$home/.config/dovecot/config" <<-EOF + environ_set "$u" + + mkdir -pm0700 -- "$home/.dovecot" + cat >"$home/.dovecot/config" <<-EOF log_path = $HOME_local/mail.log mail_home = $home mail_location = dbox:~/inbox:LAYOUT=index mailbox_list_index = yes ssl = no + listen = 127.0.0.1, ::1 namespace inbox { inbox = yes } EOF if [ -f "$TESTDIR/$u.conf" ]; then - cat >>"$home/.config/dovecot/config" <"$TESTDIR/$u.conf" + cat >>"$home/.dovecot/config" <"$TESTDIR/$u.conf" + fi + cp -aT -- "$BASEDIR/snippets/dovecot" "$home/.dovecot/conf.d" + + proto="$(env -i "${ENVIRON[@]}" doveconf -c "$home/.dovecot/config" -h protocols)" + if [ -n "$proto" ]; then + cat >>"$home/.dovecot/config" <<-EOF + # https://wiki.dovecot.org/HowTo/Rootless + base_dir = $home/.dovecot/run + default_internal_user = $(id -un) + default_internal_group = $(id -gn) + default_login_user = $(id -un) + + service anvil { + chroot = + } + service imap-login { + chroot = + } + service stats { + chroot = + } + + passdb { + args = scheme=PLAIN username_format=%u $home/.dovecot/users + driver = passwd-file + } + userdb { + args = username_format=%u $home/.dovecot/users + driver = passwd-file + } + EOF + + env -i PATH="/usr/bin:/bin" /usr/sbin/dovecot -c "$home/.dovecot/config" + DOVECOT_SERVER+=( "$home/.dovecot/config" ) + printf "%s:%s:::::\\n" "$u" "$(xxd -l16 -p "$home/.dovecot/users" fi - environ_set "$u" mkdir -pm0755 -- "$home/.local/bin" cat >"$home/.local/bin/doveadm" <<-EOF #!/bin/sh exec env -i ${ENVIRON[@]@Q} \\ - doveadm -c ${home@Q}/.config/dovecot/config "\$@" + doveadm -c ${home@Q}/.dovecot/config "\$@" EOF chmod +x -- "$home/.local/bin/doveadm" done @@ -123,15 +172,19 @@ prepare() { cat <"$TESTDIR/interimap$n.local" >>"$HOME_local/.config/interimap/config$n" fi - # `doveadm exec imap` ignores 'imap_capability' from doveconf/config - capability="$(doveconf -c "$home/.config/dovecot/config" -h imap_capability)" - cat >>"$HOME_local/.config/interimap/config$n" <<-EOF - - [remote] - type = tunnel - command = exec ${home@Q}/.local/bin/doveadm exec imap ${capability:+-oimap_capability=${capability@Q}} - null-stderr = NO - EOF + printf "\\n[remote]\\n" >>"$HOME_local/.config/interimap/config$n" + if [ -s "$home/.dovecot/users" ]; then + cat <<-EOF + username = $u + password = $(awk -F: -vu="$u" '$1 == u {print $2}' <"$home/.dovecot/users") + EOF + else + cat <<-EOF + type = tunnel + command = exec ${home@Q}/.local/bin/doveadm exec imap + null-stderr = NO + EOF + fi >>"$HOME_local/.config/interimap/config$n" if [ -f "$TESTDIR/interimap$n.remote" ]; then cat <"$TESTDIR/interimap$n.remote" >>"$HOME_local/.config/interimap/config$n" fi @@ -208,7 +261,7 @@ dump_test_result() { environ_set "$u" eval home="\$HOME_$u" printf "%s dovecot configuration:\\n%s\\n" "$u" "$below" - env -i "${ENVIRON[@]}" doveconf -c "$home/.config/dovecot/config" -n + env -i "${ENVIRON[@]}" doveconf -c "$home/.dovecot/config" -n printf "%s\\n\\n" "$above" done @@ -218,6 +271,10 @@ dump_test_result() { printf "%s\\n\\n" "$above" done + printf "mail.log:\\n%s\\n" "$below" + cat -- "$HOME_local/mail.log" 2>/dev/null || true + printf "%s\\n\\n" "$above" + printf "standard output:\\n%s\\n" "$below" cat <"$_STDOUT" printf "%s\\n\\n" "$above" @@ -230,10 +287,10 @@ dump_test_result() { # Check mailbox consistency between the local/remote server and interimap's database check_mailbox_status() { local mailbox="$1" lns="inbox" lsep lprefix rns="inbox" rsep rprefix - lsep="$(doveconf -c "$HOME_local/.config/dovecot/config" -h "namespace/$lns/separator")" - lprefix="$(doveconf -c "$HOME_local/.config/dovecot/config" -h "namespace/$lns/prefix")" - rsep="$(doveconf -c "$HOME_remote/.config/dovecot/config" -h "namespace/$lns/separator")" - rprefix="$(doveconf -c "$HOME_remote/.config/dovecot/config" -h "namespace/$lns/prefix")" + lsep="$(doveconf -c "$HOME_local/.dovecot/config" -h "namespace/$lns/separator")" + lprefix="$(doveconf -c "$HOME_local/.dovecot/config" -h "namespace/$lns/prefix")" + rsep="$(doveconf -c "$HOME_remote/.dovecot/config" -h "namespace/$lns/separator")" + rprefix="$(doveconf -c "$HOME_remote/.dovecot/config" -h "namespace/$lns/prefix")" local blob="x'$(printf "%s" "$mailbox" | tr "$lsep" "\\0" | xxd -c256 -ps)'" local rmailbox="$(printf "%s" "$mailbox" | tr "$lsep" "$rsep")" @@ -305,10 +362,10 @@ check_mailboxes_status() { # Check mailbox list constency between the local and remote servers check_mailbox_list() { local m i lns="inbox" lsep lprefix rns="inbox" rsep rprefix sub= - lsep="$(doveconf -c "$HOME_local/.config/dovecot/config" -h "namespace/$lns/separator")" - lprefix="$(doveconf -c "$HOME_local/.config/dovecot/config" -h "namespace/$lns/prefix")" - rsep="$(doveconf -c "$HOME_remote/.config/dovecot/config" -h "namespace/$lns/separator")" - rprefix="$(doveconf -c "$HOME_remote/.config/dovecot/config" -h "namespace/$lns/prefix")" + lsep="$(doveconf -c "$HOME_local/.dovecot/config" -h "namespace/$lns/separator")" + lprefix="$(doveconf -c "$HOME_local/.dovecot/config" -h "namespace/$lns/prefix")" + rsep="$(doveconf -c "$HOME_remote/.dovecot/config" -h "namespace/$lns/separator")" + rprefix="$(doveconf -c "$HOME_remote/.dovecot/config" -h "namespace/$lns/prefix")" if [ $# -gt 0 ] && [ "$1" = "-s" ]; then sub="-s" shift diff --git a/tests/snippets/dovecot/imapd.conf b/tests/snippets/dovecot/imapd.conf new file mode 100644 index 0000000..c9926ce --- /dev/null +++ b/tests/snippets/dovecot/imapd.conf @@ -0,0 +1,12 @@ +protocols = $protocols imap + +mail_plugins = $mail_plugins zlib +protocol imap { + mail_plugins = $mail_plugins imap_zlib +} + +service imap-login { + inet_listener imap { + port = 10143 + } +} diff --git a/tests/snippets/dovecot/interimap-required-capabilities.conf b/tests/snippets/dovecot/interimap-required-capabilities.conf new file mode 100644 index 0000000..10dd8e1 --- /dev/null +++ b/tests/snippets/dovecot/interimap-required-capabilities.conf @@ -0,0 +1,3 @@ +# strict minimum of IMAP capabilities required for interimap to work +# (in particular, no LITERAL+, MULTIAPPEND, COMPRESS=DEFLATE, SASL-IR) +imap_capability = IMAP4rev1 ENABLE UIDPLUS LIST-EXTENDED QRESYNC LIST-STATUS diff --git a/tests/sync-live-crippled/interimap.remote b/tests/sync-live-crippled/interimap.remote new file mode 120000 index 0000000..a4ea3f3 --- /dev/null +++ b/tests/sync-live-crippled/interimap.remote @@ -0,0 +1 @@ +../auth-sasl-plain/interimap.remote \ No newline at end of file diff --git a/tests/sync-live-crippled/remote.conf b/tests/sync-live-crippled/remote.conf index 76c08e0..ee22c5f 100644 --- a/tests/sync-live-crippled/remote.conf +++ b/tests/sync-live-crippled/remote.conf @@ -2,5 +2,5 @@ namespace inbox { separator = ^ } -# strict minimum of IMAP capabilities required for interimap to work -imap_capability = IMAP4rev1 ENABLE UIDPLUS LIST-EXTENDED QRESYNC LIST-STATUS +!include conf.d/imapd.conf +!include conf.d/interimap-required-capabilities.conf -- cgit v1.2.3 From b7514eeac609a7e99c66031f853f695bb82c990a Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Sun, 10 Nov 2019 18:11:05 +0100 Subject: Test suite: don't treat broken symlinks as missing. `test -f` deferences paths so fails on broken symlinks, yielding an incorrect test environment and perhaps even a false negative. --- tests/run | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'tests/run') diff --git a/tests/run b/tests/run index bff9c18..2903938 100755 --- a/tests/run +++ b/tests/run @@ -73,7 +73,7 @@ environ_set() { prepare() { declare -a ENVIRON=() local src cfg target u home n proto - if [ -f "$TESTDIR/remotes" ]; then + if [ -f "$TESTDIR/remotes" ] || [ -L "$TESTDIR/remotes" ]; then for cfg in $(seq 1 "$(< "$TESTDIR/remotes")"); do REMOTES+=( "remote$cfg" ) done @@ -98,7 +98,7 @@ prepare() { inbox = yes } EOF - if [ -f "$TESTDIR/$u.conf" ]; then + if [ -f "$TESTDIR/$u.conf" ] || [ -L "$TESTDIR/$u.conf" ]; then cat >>"$home/.dovecot/config" <"$TESTDIR/$u.conf" fi cp -aT -- "$BASEDIR/snippets/dovecot" "$home/.dovecot/conf.d" @@ -157,7 +157,7 @@ prepare() { database = $u.db #logfile = $HOME_local/interimap$n.log EOF - if [ -f "$TESTDIR/interimap$n.conf" ]; then + if [ -f "$TESTDIR/interimap$n.conf" ] || [ -L "$TESTDIR/interimap$n.conf" ]; then cat <"$TESTDIR/interimap$n.conf" >>"$HOME_local/.config/interimap/config$n" fi @@ -168,7 +168,7 @@ prepare() { command = exec ${HOME_local@Q}/.local/bin/doveadm exec imap null-stderr = NO EOF - if [ -f "$TESTDIR/interimap$n.local" ]; then + if [ -f "$TESTDIR/interimap$n.local" ] || [ -L "$TESTDIR/interimap$n.local" ]; then cat <"$TESTDIR/interimap$n.local" >>"$HOME_local/.config/interimap/config$n" fi @@ -185,7 +185,7 @@ prepare() { null-stderr = NO EOF fi >>"$HOME_local/.config/interimap/config$n" - if [ -f "$TESTDIR/interimap$n.remote" ]; then + if [ -f "$TESTDIR/interimap$n.remote" ] || [ -L "$TESTDIR/interimap$n.remote" ]; then cat <"$TESTDIR/interimap$n.remote" >>"$HOME_local/.config/interimap/config$n" fi done -- cgit v1.2.3 From dac4ab1c9306bf2035bc1547d2ed27ab09850120 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Wed, 13 Nov 2019 04:16:48 +0100 Subject: Test suite: add new test for pullimap(1). This adds a dependency on Dovecot's LMTPd, which will bind to to TCP port 10024 on the loopback interface. --- pullimap | 5 +- tests/list | 3 ++ tests/pullimap/interimap.remote | 1 + tests/pullimap/local.conf | 1 + tests/pullimap/pullimap.conf | 1 + tests/pullimap/remote.conf | 1 + tests/pullimap/t | 96 +++++++++++++++++++++++++++++++++++++++ tests/run | 38 ++++++++++------ tests/snippets/dovecot/lmtpd.conf | 7 +++ 9 files changed, 136 insertions(+), 17 deletions(-) create mode 120000 tests/pullimap/interimap.remote create mode 100644 tests/pullimap/local.conf create mode 100644 tests/pullimap/pullimap.conf create mode 120000 tests/pullimap/remote.conf create mode 100644 tests/pullimap/t create mode 100644 tests/snippets/dovecot/lmtpd.conf (limited to 'tests/run') diff --git a/pullimap b/pullimap index 3d1a0ec..1dc4b9e 100755 --- a/pullimap +++ b/pullimap @@ -29,7 +29,7 @@ use Errno 'EINTR'; use Fcntl qw/O_CREAT O_RDWR O_DSYNC F_SETLK F_WRLCK SEEK_SET F_GETFD F_SETFD FD_CLOEXEC/; use Getopt::Long qw/:config posix_default no_ignore_case gnu_getopt auto_version/; use List::Util 'first'; -use Socket qw/PF_INET PF_INET6 SOCK_STREAM/; +use Socket qw/PF_INET PF_INET6 SOCK_STREAM IPPROTO_TCP/; use lib 'lib'; use Net::IMAP::InterIMAP 0.0.5 qw/xdg_basedir read_config compact_set/; @@ -146,8 +146,7 @@ sub sendmail($$) { : $fam == PF_INET6 ? Socket::pack_sockaddr_in6($port, $addr) : die; - my $proto = getprotobyname("tcp") // die; - socket($SMTP, $fam, SOCK_STREAM, $proto) or die "socket: $!"; + socket($SMTP, $fam, SOCK_STREAM, IPPROTO_TCP) or die "socket: $!"; until (connect($SMTP, $sockaddr)) { next if $! == EINTR; # try again if connect(2) was interrupted by a signal die "connect: $!"; diff --git a/tests/list b/tests/list index a18cb29..52417c1 100644 --- a/tests/list +++ b/tests/list @@ -55,3 +55,6 @@ split-set Split large sets to avoid extra-long command lines sync-live-crippled local/remote simulation (crippled remote) sync-live-tls local/remote simulation (TLS remote) sync-live-multi local/remote1+remote2+remote3 simulation (3 local namespaces) + +. pullimap + ... pullimap diff --git a/tests/pullimap/interimap.remote b/tests/pullimap/interimap.remote new file mode 120000 index 0000000..daf3741 --- /dev/null +++ b/tests/pullimap/interimap.remote @@ -0,0 +1 @@ +../tls/interimap.remote \ No newline at end of file diff --git a/tests/pullimap/local.conf b/tests/pullimap/local.conf new file mode 100644 index 0000000..b67641f --- /dev/null +++ b/tests/pullimap/local.conf @@ -0,0 +1 @@ +!include conf.d/lmtpd.conf diff --git a/tests/pullimap/pullimap.conf b/tests/pullimap/pullimap.conf new file mode 100644 index 0000000..3f6c2e1 --- /dev/null +++ b/tests/pullimap/pullimap.conf @@ -0,0 +1 @@ +deliver-method = lmtp:[127.0.0.1]:10024 diff --git a/tests/pullimap/remote.conf b/tests/pullimap/remote.conf new file mode 120000 index 0000000..6029749 --- /dev/null +++ b/tests/pullimap/remote.conf @@ -0,0 +1 @@ +../tls/remote.conf \ No newline at end of file diff --git a/tests/pullimap/t b/tests/pullimap/t new file mode 100644 index 0000000..7ae0c5f --- /dev/null +++ b/tests/pullimap/t @@ -0,0 +1,96 @@ +MAILBOX="INBOX" +TIMEOUT=60 +N=2048 + +step_start "\`pullimap --idle\` refuses to create the state file" +! pullimap --idle "remote" || error +step_done + +# make sure remote UIDs are 11-bytes long +doveadm -u "remote" mailbox update --min-next-uid 1000000000 "$MAILBOX" + +# compare mailboxes; can't compare the RFC 3501 TEXT as LMTP adds a +# Received: header. +# TODO unset lmtp_add_received_header once avaisable in Sid: +# https://doc.dovecot.org/settings/dovecot_core_settings/#lmtp-add-received-header +list_mails_sha256() { + local u="$1" guid uid + while read guid uid; do + doveadm -u "$u" -f "flow" fetch body mailbox-guid "$guid" uid "$uid" \ + | sed "1s/body=//" | sha256sum + done < <(doveadm -u "$u" search mailbox "$MAILBOX") | sort -f +} +check() { + diff -u --label="local/mails" --label="remote/mails" \ + <( list_mails_sha256 "local" ) \ + <( list_mails_sha256 "remote" ) \ + || error "mailboxes differ" +} + + +# Add some messages and sync +step_start "Fetching messages" +for ((i = 0; i < 32; i++)); do + sample_message | deliver -u "remote" -- -m "$MAILBOX" +done + +pullimap "remote" || error +check + +# same thing, but with some missing messages +for ((i = 0; i < N; i+=2)); do + sample_message | deliver -u "remote" -- -m "$MAILBOX" + deliver -u "remote" -- -m "$MAILBOX" "$TMPDIR/unseen" +[ ! -s "$TMPDIR/unseen" ] || error "\\Unseen messages left" +step_done + + +step_start "--idle (${TIMEOUT}s)" + +pullimap --idle "remote" & PID=$! +trap "ptree_abort $PID" EXIT INT TERM + +timer=$(( $(date +%s) + TIMEOUT )) +while [ $(date +%s) -le $timer ]; do + n="$(shuf -n1 -i1-5)" + for (( i=0; i < n; i++)); do + sample_message | deliver -u "remote" -- -m "$MAILBOX" + done + + s=$(shuf -n1 -i1-1500) + [ $s -ge 1000 ] && s="$(printf "1.%03d" $((s-1000)))" || s="$(printf "0.%03d" $s)" + sleep "$s" +done + +sleep 2 +ptree_abort $PID +trap - EXIT INT TERM + +check +step_done + + +step_start "Purging" +echo "purge-after = 0" >>"$XDG_CONFIG_HOME/pullimap/config" +for ((i = 0; i < 32; i++)); do + sample_message | deliver -u "remote" -- -m "$MAILBOX" +done +pullimap "remote" + +doveadm -u "remote" search mailbox "$MAILBOX" all >"$TMPDIR/messages" +[ ! -s "$TMPDIR/messages" ] || error "messages left" +step_done + +# vim: set filetype=sh : diff --git a/tests/run b/tests/run index 2903938..30d20f9 100755 --- a/tests/run +++ b/tests/run @@ -146,9 +146,10 @@ prepare() { chmod +x -- "$home/.local/bin/doveadm" done - # copy interimap config - mkdir -pm0700 -- "$HOME_local/.local/share/interimap" - mkdir -pm0700 -- "$HOME_local/.config/interimap" + # copy interimap and pullimap configuration + mkdir -pm0700 -- "$HOME_local/.local/share/interimap" "$HOME_local/.local/share/pullimap" + mkdir -pm0700 -- "$HOME_local/.config/interimap" "$HOME_local/.config/pullimap" + echo "deliver-rcpt = local" >>"$HOME_local/.config/pullimap/config" for u in "${REMOTES[@]}"; do n="${u#remote}" eval home="\$HOME_$u" @@ -160,6 +161,9 @@ prepare() { if [ -f "$TESTDIR/interimap$n.conf" ] || [ -L "$TESTDIR/interimap$n.conf" ]; then cat <"$TESTDIR/interimap$n.conf" >>"$HOME_local/.config/interimap/config$n" fi + if [ -f "$TESTDIR/pullimap.conf" ] || [ -L "$TESTDIR/pullimap.conf" ]; then + cat <"$TESTDIR/pullimap.conf" >>"$HOME_local/.config/pullimap/config" + fi cat >>"$HOME_local/.config/interimap/config$n" <<-EOF @@ -172,7 +176,6 @@ prepare() { cat <"$TESTDIR/interimap$n.local" >>"$HOME_local/.config/interimap/config$n" fi - printf "\\n[remote]\\n" >>"$HOME_local/.config/interimap/config$n" if [ -s "$home/.dovecot/users" ]; then cat <<-EOF username = $u @@ -184,21 +187,26 @@ prepare() { command = exec ${home@Q}/.local/bin/doveadm exec imap null-stderr = NO EOF - fi >>"$HOME_local/.config/interimap/config$n" + fi >"$HOME_local/.$u.conf" if [ -f "$TESTDIR/interimap$n.remote" ] || [ -L "$TESTDIR/interimap$n.remote" ]; then - cat <"$TESTDIR/interimap$n.remote" >>"$HOME_local/.config/interimap/config$n" + cat <"$TESTDIR/interimap$n.remote" >>"$HOME_local/.$u.conf" fi + + { printf "\\n[remote]\\n"; cat <"$HOME_local/.$u.conf"; } >>"$HOME_local/.config/interimap/config$n" + { printf "\\n[%s]\\n" "$u"; cat <"$HOME_local/.$u.conf"; } >>"$HOME_local/.config/pullimap/config" done } prepare # Wrappers for interimap(1) and doveadm(1) -interimap() { +interimap() { _interimap_cmd "interimap" "$@"; } +pullimap() { _interimap_cmd "pullimap" "$@"; } +_interimap_cmd() { declare -a ENVIRON=() r=0 + local script="$1" + shift environ_set "local" - env -i "${ENVIRON[@]}" perl -I./lib -T ./interimap "$@" 2>"$STDERR" || r=$? - cat "$STDERR" >&2 - return $r + env -i "${ENVIRON[@]}" perl -I./lib -T "./$script" "$@" 2> >(tee "$STDERR" >&2) } interimap_init() { local u="${1-remote}" @@ -227,8 +235,10 @@ sqlite3() { # Sample (random) message sample_message() { local date="$(date +"%s.%N")" + # also try non-conventional addresses for pullimap + local sender="$(shuf -n1 -e "sender" "first.last" "foo-bar" \"\" "\"x\\\" #&\\\\y\"" )" cat <<-EOF - From: + From: <$sender@example.net> To: Date: $(date -R -d@"$date") Message-ID: <$date@example.net> @@ -240,7 +250,7 @@ sample_message() { # Wrapper for dovecot-lda(1) deliver() { - local -a argv + declare -a argv while [ $# -gt 0 ] && [ "$1" != "--" ]; do argv+=( "$1" ) shift @@ -433,8 +443,8 @@ passed() { declare -a ENVIRON=() environ_set "local" export TMPDIR TESTDIR STDERR "${ENVIRON[@]}" -export -f environ_set doveadm interimap sqlite3 sample_message deliver -export -f interimap_init ptree_abort step_start step_done passed +export -f environ_set doveadm interimap interimap_init pullimap _interimap_cmd +export -f sqlite3 sample_message deliver ptree_abort step_start step_done passed export -f check_mailbox_status check_mailbox_status_values check_mailbox_status2 export -f check_mailboxes_status check_mailbox_list xcgrep error [ "$TESTNAME" = "..." ] || printf "%s%s..." "${INDENT-}" "$TESTNAME" diff --git a/tests/snippets/dovecot/lmtpd.conf b/tests/snippets/dovecot/lmtpd.conf new file mode 100644 index 0000000..6aa8365 --- /dev/null +++ b/tests/snippets/dovecot/lmtpd.conf @@ -0,0 +1,7 @@ +protocols = $protocols lmtp + +service lmtp { + inet_listener lmtp { + port = 10024 + } +} -- cgit v1.2.3