Skip to content

Instantly share code, notes, and snippets.

@mwinters0
Last active August 31, 2025 20:41
Show Gist options
  • Save mwinters0/1b4015347ef0a12d7ca9fd81218301e8 to your computer and use it in GitHub Desktop.
Save mwinters0/1b4015347ef0a12d7ca9fd81218301e8 to your computer and use it in GitHub Desktop.
Upload a Sieve script via ManageSieve
#!/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