Last active
August 31, 2025 20:41
-
-
Save mwinters0/1b4015347ef0a12d7ca9fd81218301e8 to your computer and use it in GitHub Desktop.
Upload a Sieve script via ManageSieve
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
set -e | |
# Upload a Sieve script via ManageSieve. Depends on `expect` and `openssl`. | |
# Currently only tested with Stalwart. | |
# | |
# Usage 1: | |
# SIEVE_FILENAME=foo.sieve \ | |
# SIEVE_HOSTNAME=mail.yay.wow \ | |
# SIEVE_PORT=4190 \ | |
# SIEVE_USERNAME=foo \ | |
# SIEVE_PASSWORD=hunter2 \ | |
# putsieve.sh | |
# | |
# Usage 2: | |
# putsieve.sh foo.sieve | |
# | |
# Usage 3: | |
# putsieve.sh | |
# check dependencies | |
for d in expect openssl base64; do | |
if [ ! $(which $d) ]; then | |
echo "Can't locate the '$d' binary." >&2 | |
exit 1 | |
fi | |
done | |
# args | |
while [ -z "$SIEVE_FILENAME" ]; do | |
if [[ $# -eq 1 && -r "$1" ]]; then | |
# putsieve.sh foo.sieve | |
export SIEVE_FILENAME="$1" | |
break | |
fi | |
echo -n "Script filename: " | |
read SIEVE_FILENAME | |
if [ ! -r "$SIEVE_FILENAME" ]; then | |
echo \'"$SIEVE_FILENAME"\' is not a readable file. >&2 | |
unset SIEVE_FILENAME | |
fi | |
done | |
if [ ! -r "$SIEVE_FILENAME" ]; then | |
echo \'"$SIEVE_FILENAME"\' is not a readable file. >&2 | |
exit 1 | |
fi | |
while [ -z "$SIEVE_HOSTNAME" ]; do | |
echo -n "Hostname: " | |
read SIEVE_HOSTNAME | |
done | |
while [ -z "$SIEVE_PORT" ]; do | |
echo -n "Port (typically 4190): " | |
read SIEVE_PORT | |
done | |
while [ -z "$SIEVE_USERNAME" ]; do | |
echo -n "Username: " | |
read SIEVE_USERNAME | |
done | |
while [ -z "$SIEVE_PASSWORD" ]; do | |
echo -n "Password for user '${SIEVE_USERNAME}': " | |
read -s SIEVE_PASSWORD | |
echo | |
done | |
export SIEVE_AUTH=$(echo -ne "\0$SIEVE_USERNAME\0$SIEVE_PASSWORD" | base64) | |
export SIEVE_CONNECT="${SIEVE_HOSTNAME}:${SIEVE_PORT}" | |
export SIEVE_OPENSSL=$(which openssl) | |
export SIEVE_SCRIPT_NAME="${SIEVE_FILENAME%.*}" # strip extension | |
cat <<-'EOF' | expect -f - | |
set timeout 60 | |
set script_file [open "$env(SIEVE_FILENAME)" r] | |
set script_size [file size "$env(SIEVE_FILENAME)"] | |
set script_name "$env(SIEVE_SCRIPT_NAME)" | |
set sieve_auth "$env(SIEVE_AUTH)" | |
set sieve_connect "$env(SIEVE_CONNECT)" | |
set openssl "$env(SIEVE_OPENSSL)" | |
spawn $openssl s_client -connect $sieve_connect | |
expect { | |
-re {(?n)^NO.*} { | |
send_user "Server said NO before we could authenticate.\n" | |
exit | |
} | |
-re {(?n)^BYE.*} { | |
send_user "Server hung up\n" | |
exit | |
} | |
-re {(?n)^OK.*} | |
} | |
send "AUTHENTICATE PLAIN\r" | |
send -- "$sieve_auth\r" | |
expect { | |
-re {(?n)^NO.*} { | |
send_user "Authentication failure\n" | |
exit | |
} | |
-re {(?n)^BYE.*} { | |
send_user "Server hung up\n" | |
exit | |
} | |
-re {(?n)^OK.*} | |
} | |
send -- "HAVESPACE \"$script_name\" $script_size\r" | |
expect { | |
-re {(?n)^NO.*} { | |
send_user "Insufficient storage space for the script\n" | |
exit | |
} | |
-re {(?n)^BYE.*} { | |
send_user "Server hung up\n" | |
exit | |
} | |
-re {(?n)^OK.*} | |
} | |
send -- "PUTSCRIPT \"$script_name\" \{$script_size+\}\r" | |
send [read $script_file] | |
send "\r\r" | |
expect { | |
-re {(?n)^NO.*} { | |
send_user "Error calling PUTSCRIPT\n" | |
exit | |
} | |
-re {(?n)^BYE.*} { | |
send_user "Server hung up\n" | |
exit | |
} | |
-re {(?n)^OK.*} | |
} | |
close $script_file | |
send -- "SETACTIVE $script_name\r" | |
expect { | |
-re {(?n)^NO.*} { | |
send_user "Error calling SETACTIVE\n" | |
exit | |
} | |
-re {(?n)^BYE.*} { | |
send_user "Server hung up\n" | |
exit | |
} | |
-re {(?n)^OK.*} | |
} | |
send "LOGOUT\r" | |
wait | |
EOF |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment