Command-line client#

SYNOPSIS#

sievemgr [server] [command] [argument …]

sievemgr -e expression […] [server]

sievemgr -s file [server]

sievemgr -h

sievemgr -V

DESCRIPTION#

sievemgr is a command-line client for uploading, downloading, and managing Sieve scripts using the ManageSieve protocol. By default, a shell is entered and commands are read from standard input; the shell supports tab-completion. See COMMANDS below for a list.

Commands can also be given either on the command line, as expression with -e, or read from a file given with -s. The shell is not entered if commands are given.

The server defaults to the host set in sieve.cf or localhost.

OPERANDS#

server#

URL of the form [sieve://][login[:passwd]@]host[:port][/owner].

login

defaults to the login set for host in sieve.cf or .netrc, or to the current user.

passwd

is prompted for by default (see LOGIN for automation).

port

defaults to 4190 (the standard port for ManageSieve).

owner

defaults to login.

Danger

Other users can see passwords given on the command line.

command#

Command to run (see COMMANDS below).

argument#

Argument to that command.

OPTIONS#

-C#

Do not overwrite files.

-N file#

Use file as .netrc file.

-V#

Print version.

-c file#

Read configuration from file.

-d#

Enable debugging mode.

-e expression#

Execute expression on the server.

-f#

Overwrite and remove files without confirmation.

-h#

Print help.

-i#

Confirm removing or overwriting files.

-o key=value#

Set the configuration key to value.

-o key=yes can be shortened to -o key. -o key=no can be shortened to -o nokey.

See sieve.cf for a list of keys.

-q#

Be quieter.

-s file#

Execute expressions read from file.

-v#

Be more verbose.

-c, -e, -o, -q, and -v can be given multiple times.

COMMANDS#

about#

Print information about SieveManager.

activate script#

Marks script as the active script. This is the script that the mail server will run for incoming mail. Only one script can be active at a time.

caps#

Print the server’s capabilities (YAML).

cat [script ...]#

Print the given scripts to standard output.

cd [localdir]#

Change to local working directory to localdir. localdir defaults to the current user’s home directory.

cert#

Print information about the server’s TLS certificate (YAML).

check localscript#

Check whether localscript is semantically valid.

cmp script1 [...] scriptN#

Check whether scripts are equal.

cp [-f|-i] source target#

Download source and re-upload it as target.

Options:

-f

Overwrite target without confirmation.

-i

Ask for confirmation before overwriting target.

deactivate#

Deactivate the active script

diff [-C n|-U n|-c|-u] [-b] script1 script2#

Show how script1 and script2 differ.

Options:

-C n

Show n lines of copied context.

-U n

Show n lines of unified context.

-b

Ignore whitespace before a linefeed.

-c

Show three lines of copied context.

-u

Show three lines of unified context.

echo word [...]#

Print word to standard output.

ed [-a] script [...]#

Download script, edit it with a line editor, and re-upload it.

Options:

-a

Edit the active script (any script given is ignored).

exit#

Log out and exit.

get [-f|-i] [-a] [-o file] [script ...]#

Download scripts.

Options:

-a

Download the active script only.

-f

Overwrite files without confirmation.

-i

Ask for confirmation before overwriting a file.

-o file

Save script as file.

help | ? [command]#

Print help for command. List commands if command is omitted.

For example:

sieve://user@imap.foo.example> ?ls
ls [script ...] - list scripts
ls [-1al] [script ...]#

List the given scripts or all scripts if no script is given. The active script is marked with an asterisk (”*”).

Options:

-1

List one script per line. Implied if standard input is not a terminal.

-a

List the active script only.

-l

List one flag-script name pair per line, separated by whitespace, where the flag is one of:

a

Script is active.

e

End-of-transmission mark. Not followed by a script name.

If neither flag applies, a dash (“-”) is printed instead.

The active script is not marked with an asterisk (”*”) if -1, -a, or -l is given.

Examples:

sieve://user@imap.foo.example> ls
bar.sieve foo.sieve*
sieve://user@imap.foo.example> ls -l
- bar.sieve
a foo.sieve
e
more [-aceis] [script ...]#

Display scripts page-by-page.

Options:

-a

Display the active script only.

-c

Clear screen instead of scrolling.

-e

Exit immediately after writing the last line.

-i

Ignore case in pattern matching.

-s

Treat consecutive empty lines as a single empty line.

mv [-f|-i] source target#

Rename source to target.

Options:

-f

Replace target without confirmation.

-i

Ask for confirmation before replacing target.

put [-f|-i] [-a] [-o name] [localscript ...]#

Upload scripts.

-a

Replace the active script or, if no script is active or if -o has been given after -a, activate the script after uploading.

-f

Replace scripts without confirmation.

-i

Ask for confirmation before replacing a script.

-o name

Upload localscript as name.

The server should reject syntactically invalid scripts. It may issue a warning for semantically invalid scripts, but should accept them nonetheless. Updates are atomic.

python#

Enter a Python read-evaluate-print loop, with the SieveManager object that represents the connection as namespace.

rm [-f|-i] [script ...]#

Remove scripts.

-f

Remove scripts without confirmation.

-i

Ask for confirmation before removing a script.

sh | ! [command] [argument ...]#

Run system command. If command is omitted, enter a system shell.

Examples:

sieve://user@imap.foo.example> cd sieve
sieve://user@imap.foo.example> !pwd
/home/user/sieve
sieve://user@imap.foo.example> !ls
foo.sieve bar.sieve
sieve://user@imap.foo.example> put foo.sieve
sieve://user@imap.foo.example> !
bash-2.0$
su user#

Manage the scripts of user. Requires elevated privileges.

vi [-a] script [...]#

Download script, edit it with a visual editor, and re-upload it.

Options:

-a

Edit the active script (any script given is ignored).

xargs command [arg ...]#

Call command with the given arguments and each line from standard input as additional argument up to, but excluding, the first empty line or the end-of-file mark. The lines read from standard input are neither subject to WORD SPLITTING nor to PATTERN EXPANSION.

WORD SPLITTING#

Lines are split into words at any run of consecutive whitespace. If a filename contains whitespace, that whitespace must either be escaped with a backslash (”\”) or the filename must be quoted.

For example,

sieve://user@imap.foo.example> get foo bar

downloads the two files foo and bar.

But

sieve://user@imap.foo.example> get "foo bar"

downloads the single file foo bar.

If a filename contains backslashes or quotes (single or double), they must be escaped. For example,

sieve://user@imap.foo.example> get foo\\bar\'

downloads the file foo\bar'.

See sh(1) and wordexp(3) for details.

PATTERN EXPANSION#

If *, ?, or [chars] occur in an expression given with -e, read from a script given with -s, or read from standard input, they are expanded to local or remote filenames in the same way as a they would be expanded by a system shell. If a command operates on local scripts, patterns are expanded to matching filenames on the local system; if a command operates on remote scripts, patterns are expanded to matching filenames on the remote system.

For example,

sieve://user@imap.foo.example> put *.sieve

uploads every local file that matches *.sieve.

But

sieve://user@imap.foo.example> get *.sieve

downloads every remote file that matches *.sieve.

If a filename contains *, ?, or [chars], those characters must be escaped with a backslash (”\”) or the filename as a whole must be quoted. For example,

sieve://user@imap.foo.example> get *.sieve

downloads every file that matches the pattern *.sieve.

But

sieve://user@imap.foo.example> get "*.sieve"

downloads the file *.sieve.

See sh(1) and fnmatch(3) for details.

SCRIPTING#

Operations can be scripted by giving a command, redirecting standard input, or with -e or -s.

Basics#

Scripts abort if an error occurs, so errors must be prevented.

Confirmation is always prompted for on the controlling terminal, regardless of input/output redirection. If there is no controlling terminal, operations that require confirmation trigger an error.

Comments start with a ‘#’ and are ignored.

Comparing Scripts#

cmp can be used to compare remote scripts.

$ if sievemgr user@imap.foo.example cmp -s foo.sieve bar.sieve
> then echo 'foo.sieve and bar.sieve are equal'
> else echo 'foo.sieve and bar.sieve differ'
> fi
$ case $(sievemgr user@imap.foo.example cmp foo.sieve bar.sieve) in
> (*equal)   echo 'foo.sieve and bar.sieve are equal' ;;
> (*differs) echo 'foo.sieve and bar.sieve differ' ;;
> esac

Listing Scripts#

ls prints one script name per line if standard input is not a terminal. Sieve script names must not contain linefeeds, so ls can safely be used in scripts.

$ sievemgr -e'ls -a' -e'deactivate' user@imap.foo.example |
> sievemgr user@imap.foo.example xargs rm -f
$ mkfifo pipe
$ sievemgr user@imap.foo.example ls -l >pipe & pid=$!
$ nscripts=0
$ while read -r _ script && [ "$script" ]
> do
>     eval "script_${nscripts}"='$script'
>     nscripts=$((nscripts + 1))
> done <pipe
$ wait $pid

Persistent Connections#

A connection is opened each time sievemgr is called. So if multiple messages are going to be exchanged between the client and the server, it is more efficient to run sievemgr in the background and send and receive messages through pipes than to call sievemgr for each exchange.

Create one pipe for sending commands and another one for receiving responses:

$ mkfifo -m 0600 send recv

Start sievemgr and redirect its input and output to these pipes:

$ sievemgr user@imap.foo.example <send >recv & pid=$!

Open the pipes in the shell:

$ exec 3>send
$ exec 4<recv

Commands can now be sent by writing them to file descriptor (FD) 3:

$ echo ls -l >&3

And responses can be read from FD 4:

$ nscripts=0
$ while read -r _ script && [ "$script" ]
> do
>     eval "script_${nscripts}"='$script'
>     nscripts=$((nscripts + 1))
> done <&4

However, be careful to avoid deadlocks:

Deadlock#
echo ls -a >&3
# read will wait forever if there is no active script.
active="$(read <&4)"
Deadlock#
echo ls -l >&3
nscripts=0
# The loop reads past the output of ls -l and then waits forever.
while read -r _ script
do
    eval "script_${nscripts}"='$script'
    nscripts=$((nscripts + 1))
done <&4

The output of SieveManager commands must be checked for an end-of-transmission mark:

Command

End-of-transmission mark

caps

...

cert

...

cmp

Ends with equal or differs

ls -l

First word is e

Also be careful to avoid races:

Race condition#
echo get foo.sieve >&3
# patch will likely run BEFORE get has downloaded foo.sieve.
patch foo.sieve <patchfile

Use echo to wait for previously sent commands to finish:

$ cat <<EOF >&3
> get foo.sieve
> echo fin
> EOF
$ while read -r line && [ "$line" != fin ]
> do :
> done <&4
$ patch foo.sieve <patchfile

read blocks, so this is not a busy wait.

Exit by sending exit.

$ echo exit >&3
$ wait "$pid"

But abort by:

$ kill "$pid"
$ wait "$pid"

Exiting by writing exit to FD 3, instead of by sending a TERM with kill, makes sure that sievemgr exits only after it has finished executing previously sent commands. Conversely, aborting with kill makes sure that sievemgr exits right away.

See examples/sievepatch for an extended example.

LOGIN#

Logins can be automated by reading passwords from the standard output of a command, from sieve.cf, or from .netrc, or by using TLS client authentication.

Files that contain passwords must be neither group- nor world-readable.

Danger

Password should be stored in encrypted form only. Prefer using a password manager over sieve.cf or .netrc.

Password Managers#

Set getpassword to a command to read the password from the standard output of that command.

For example, add

getpassword pass $login@$host

to your sieve.cf to query pass for the password for the current host.

$host and $login are expanded to the given host and the login for that host respectively.

Warning

Commands that can be run by sievemgr can, at least, be run by any application that can run python.

The sieve.cf File#

Set password to a string to log in using that string as your password.

For example, add

account imap.foo.example
    login user
    password pencil

to your sieve.cf to automatically log in as user with the password pencil on imap.foo.example.

The .netrc File#

The .netrc file is a traditional facility to automate logins.

For example, add

machine imap.foo.example
    login user
    password pencil

to your .netrc to automatically log in as user with the password pencil on imap.foo.example.

See the GNU Inetutils manual (chap. 11.7) for details.

TLS Client Authentication#

There are two types of TLS client authentication. Sending a TLS client certificate may be required for another form of authentication to be permitted to begin with or the user may be authenticated by sending a certificate.

Both types require sending a TLS client certificate. Set cert to a file that contains a TLS key and a TLS certificate to decrypt messages with that key and send that certificate. For example:

account imap.foo.example
    login user
    cert cert.pem

In addition, set saslmechs to external to authenticate by sending that certificate:

account imap.foo.example
    login user
    cert cert.pem
    saslmechs external

See sieve.cf for details.

EXIT STATUS#

0

Success

1

Failure

2

Usage error

ENVIRONMENT#

COLUMNS#

Terminal width in characters.

EDITOR#

Editor called by ed (default: ed).

HOME#

Home directory of the current user.

LANG, LC_ALL, LC_CTYPE#

Encoding for reading from/writing to the terminal and applications. Sieve scripts are always assumed to be encoded as UTF-8. The order of preference is LC_ALL > LC_CTYPE > LANG.

LINES#

Terminal height in lines.

LOGNAME#

Login name of the current user.

PAGER#

Pager called by more (default: more).

NETRC#

Filename of the .netrc file (default: $HOME/.netrc).

VISUAL#

Editor called by vi (default: vi).

XDG_CONFIG_HOME#

X Desktop Group base configuration directory (default: $HOME/.config).

FILES#

/etc/sieve/config, /etc/sieve.cf, $XDG_CONFIG_HOME/sieve/config, $HOME/.sieve/config, $HOME/.sieve.cf

Default configuration files. Not read when -c is given.

See sieve.cf for details.

.netrc

Login information.

STANDARDS#

RFC 2195 (CRAM-MD5)

RFC 2244 (ACAP)

RFC 2782 (SRV records)

RFC 4013 (SASLprep)

RFC 4422 (SASL)

RFC 4616 (PLAIN)

RFC 5228 (Sieve)

RFC 5802 (SCRAM)

RFC 5804 (ManageSieve)

RFC 5019 (Lightweight OCSP)

RFC 6960 (OCSP)

RFC 7677 (SCRAM-SHA-256 and SCRAM-SHA-256-PLUS)

SECURITY#

Credentials are stored in memory so that they need not be entered again in case of a referral. However, because page-locking is unfeasible in Python, they may be swapped out to the disk.

SieveManager can query password managers to automate logins. However, any command that can be run by sievemgr can, at the very least, also be run by any application that can run python.

BUGS#

.netrc records without a password token wrongly trigger a parse error in Python up to version 3.9.

EXAMPLES#

Upload script.sieve to imap.foo.example and activate it:

$ sievemgr user@imap.foo.example
sieve://user@imap.foo.example> put script.sieve
sieve://user@imap.foo.example> activate script.sieve
sieve://user@imap.foo.example> exit

Edit the active script on imap.foo.example:

$ sievemgr user@imap.foo.example vi -a

Reading commands from standard input:

$ sievemgr user@imap.foo.example <<EOF
> put script.sieve
> activate script.sieve
> EOF

Download all scripts from imap.foo.example:

$ sievemgr -e'get *' user@imap.foo.example