******************* Command-line client ******************* Synopsis ======== :command:`sievemgr` [:option:`server`] [:option:`command`] [:option:`argument` ...] :command:`sievemgr` :option:`-e expression` [...] [:option:`server`] :command:`sievemgr` :option:`-s file` [:option:`server`] :command:`sievemgr` :option:`-h` :command:`sievemgr` :option:`-V` Description =========== :command:`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 :option:`-e`, or read from a `file` given with :option:`-s`. The shell is not entered if commands are given. .. only:: man The `server` defaults to the :confvar:`host` set in :manpage:`sieve.cf(5)` or :samp:`localhost`. .. only:: not man The `server` defaults to the :confvar:`host` set in :doc:`sieve.cf ` or :samp:`localhost`. Operands ======== .. option:: server URL of the form :samp:`[sieve://][{login}[:{passwd}]@]{host}[:{port}][/{owner}]`. `login` .. only:: man defaults to the :confvar:`login` set for `host` in :manpage:`sieve.cf(5)` or :file:`.netrc`, or to the current user. .. only:: not man defaults to the :confvar:`login` set for `host` in :doc:`sieve.cf ` or :file:`.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. .. option:: command Command to run (see COMMANDS_ below). .. option:: argument Argument to that command. Options ======= .. option:: -C Do *not* overwrite files. .. option:: -N file Use `file` as :file:`.netrc` file. .. option:: -V Print version. .. option:: -c file Read configuration from `file`. .. option:: -d Enable debugging mode. .. option:: -e expression Execute `expression` on the `server`. .. option:: -f Overwrite and remove files without confirmation. .. option:: -h Print help. .. option:: -i Confirm removing or overwriting files. .. option:: -o key=value Set the configuration `key` to `value`. :samp:`-o {key}=yes` can be shortened to :samp:`-o {key}`. :samp:`-o {key}=no` can be shortened to :samp:`-o no{key}`. .. only:: man See :manpage:`sieve.cf(5)` for a list of keys. .. only:: not man See :doc:`sieve.cf ` for a list of keys. .. option:: -q Be quieter. .. option:: -s file Execute expressions read from `file`. .. option:: -v Be more verbose. :option:`-c`, :option:`-e`, :option:`-o`, :option:`-q`, and :option:`-v` can be given multiple times. Commands ======== .. sievecmd:: ! [command] [argument ...] Shorthand for :sievecmd:`sh`. .. sievecmd:: ? [command] Shorthand for :sievecmd:`help`. .. sievecmd:: about Print information about SieveManager. .. sievecmd:: 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. .. sievecmd:: caps Print the server's capabilities (output is YAML_). .. sievecmd:: cat [script ...] Print the given scripts to standard output. .. sievecmd:: cd [localdir] Change to local working directory to `localdir`, which defaults to the current user's home directory. .. sievecmd:: cert Print information about the server's TLS certificate (output is YAML_). .. sievecmd:: check localscript Check whether `localscript` is semantically valid. .. sievecmd:: cmp script1 [...] scriptN Check whether scripts are equal. .. sievecmd:: cp [-f|-i] source target Download `source` and re-upload it as `target`. .. rubric:: Options: -f Overwrite `target` without confirmation. -i Ask for confirmation before overwriting `target`. .. sievecmd:: deactivate Deactivate the active script .. sievecmd:: diff [-C n|-U n|-c|-u] [-b] script1 script2 Show how `script1` and `script2` differ. .. rubric:: 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. .. sievecmd:: echo word [...] Print `word` to standard output. .. sievecmd:: ed [-a] script [...] Download `script`, edit it with a line editor, and re-upload it. .. rubric:: Options: -a Edit the active script (any `script` given is ignored). .. sievecmd:: exit Log out and exit. .. sievecmd:: get [-f|-i] [-a] [-o file] [script ...] Download scripts. .. rubric:: 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:`file`. .. sievecmd:: help [command] Print help for `command` or list commands if `command` is omitted. Can be shortened to ``?``. For example: .. code:: none sieve://user@imap.foo.example> ?ls ls [script ...] - list scripts .. sievecmd:: ls [-1al] [script ...] List the given scripts or all scripts if no `script` is given. The active script is marked with an asterisk ("\*"). .. rubric:: 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: :literal:`a` Script is active. :literal:`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. .. rubric:: Examples: .. code:: none sieve://user@imap.foo.example> ls bar.sieve foo.sieve* .. code:: none sieve://user@imap.foo.example> ls -l - bar.sieve a foo.sieve e .. sievecmd:: more [-aceis] [script ...] Display scripts page-by-page. .. rubric:: 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. .. sievecmd:: mv [-f|-i] source target Rename `source` to `target`. .. rubric:: Options: -f Replace `target` without confirmation. -i Ask for confirmation before replacing `target`. .. sievecmd:: 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. .. sievecmd:: python Enter a Python read-evaluate-print loop, with the :mod:`SieveManager ` object that represents the connection as namespace. .. sievecmd:: rm [-f|-i] [script ...] Remove scripts. -f Remove scripts without confirmation. -i Ask for confirmation before removing a script. .. sievecmd:: sh [command] [argument ...] Run system `command`. If `command` is omitted, enter a system shell. Can be shortened to ``!``. .. rubric:: Examples: .. code:: none 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 .. code:: none sieve://user@imap.foo.example> ! bash-2.0$ .. sievecmd:: su user Manage the scripts of `user`. Requires elevated privileges. .. sievecmd:: vi [-a] script [...] Download `script`, edit it with a visual editor, and re-upload it. .. rubric:: Options: -a Edit the active script (any `script` given is ignored). .. sievecmd:: 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, .. code:: none sieve://user@imap.foo.example> get foo bar downloads the two files :file:`foo` and :file:`bar`. But .. code:: none sieve://user@imap.foo.example> get "foo bar" downloads the single file :file:`foo bar`. If a filename contains backslashes or quotes (single or double), they must be escaped. For example, .. code:: none sieve://user@imap.foo.example> get foo\\bar\' downloads the file :file:`foo\\bar'`. See :manpage:`sh(1)` and :manpage:`wordexp(3)` for details. Pattern Expansion ================= If :samp:`*`, :samp:`?`, or :samp:`[` occur in an expression given with :option:`-e`, read from a script given with :option:`-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, .. code:: none sieve://user@imap.foo.example> put *.sieve uploads every *local* file that matches :samp:`*.sieve`. But .. code:: none sieve://user@imap.foo.example> get *.sieve downloads every *remote* file that matches :samp:`*.sieve`. If a filename contains :samp:`*`, :samp:`?`, or :samp:`[`, those characters must be escaped with a backslash ("\\") or the filename as a whole must be quoted. For example, .. code:: none sieve://user@imap.foo.example> get *.sieve downloads every file that matches the *pattern* :samp:`*.sieve`. But .. code:: none sieve://user@imap.foo.example> get "*.sieve" downloads the *file* :file:`*.sieve`. See :manpage:`sh(1)` and :manpage:`fnmatch(3)` for details. Scripting ========= Operations can be scripted by giving a :option:`command`, redirecting standard input, or with :option:`-e` or :option:`-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 raise an error. Comments start with a '#' and are ignored. Comparing Scripts ----------------- :sievecmd:`cmp` can be used to compare remote scripts. .. code:: bash $ 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 .. code:: bash $ 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 --------------- :sievecmd:`ls` prints one script name per line if standard input is not a terminal. Sieve script names must not contain linefeeds, so :sievecmd:`ls` can safely be used in scripts. .. code:: bash $ sievemgr -e 'ls -a' -e deactivate user@imap.foo.example | > sievemgr user@imap.foo.example xargs rm -f .. code:: bash $ 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 recv & pid=$! Open the pipes in the shell: .. code:: bash $ exec 3>send $ exec 4&3 And responses can be read from FD 4: .. code-block:: bash :emphasize-lines: 6 $ nscripts=0 $ while read -r _ script && [ "$script" ] > do > eval "script_${nscripts}"='$script' > nscripts=$((nscripts + 1)) > done <&4 However, be careful to avoid deadlocks: .. code-block:: bash :caption: Deadlock :emphasize-lines: 3 echo ls -a >&3 # read will wait forever if there is no active script. read -r active <&4 .. code-block:: bash :caption: Deadlock :emphasize-lines: 4 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 ====================== ================================== :sievecmd:`caps` ``...`` :sievecmd:`cert` ``...`` :sievecmd:`cmp` Ends with ``equal`` or ``differs`` :sievecmd:`ls -l ` First word is ``e`` ====================== ================================== Also be careful to avoid races: .. code-block:: bash :caption: Race condition :emphasize-lines: 3 echo get foo.sieve >&3 # patch will likely run BEFORE get has downloaded foo.sieve. patch foo.sieve &3 > get foo.sieve > echo fin > EOF $ while read -r line && [ "$line" != fin ] > do : > done <&4 $ patch foo.sieve &3 $ wait "$pid" But abort by: .. code:: bash $ kill "$pid" $ wait "$pid" Exiting by writing :sievecmd:`exit` to FD 3, instead of by sending a :literal:`TERM` with :command:`kill`, makes sure that :command:`sievemgr` exits only after it has finished executing previously sent commands. Conversely, aborting with :command:`kill` makes sure that :command:`sievemgr` exits right away. See :download:`examples/sievepatch <../examples/sievepatch>` for an extended example. Login ===== .. only:: man Logins can be automated by reading passwords from the standard output of a command, from :manpage:`sieve.cf(5)`, or from :file:`.netrc`, or by using TLS client authentication. .. only:: not man Logins can be automated by reading passwords from the standard output of a command, from :doc:`sieve.cf `, or from :file:`.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 :file:`sieve.cf` or :file:`.netrc`. Password Managers ----------------- Set :confvar:`getpassword` to a :samp:`{command}` to read the password from the standard output of that *command*. For example, add .. code:: none getpassword pass $login@$host .. only:: man to your :manpage:`sieve.cf(5)` to query pass_ for the password for the current `host`. .. only:: not man to your :doc:`sieve.cf ` to query pass_ for the password for the current `host`. :samp:`$host` and :samp:`$login` are expanded to the given `host` and the login for that `host` respectively. The :file:`sieve.cf` File ------------------------- Set :confvar:`password` to a :samp:`{word}` to log in using that :samp:`{word}` as your password. For example, add .. code:: none account imap.foo.example login user password pencil .. only:: man to your :manpage:`sieve.cf(5)` to automatically log in as :samp:`user` with the password :samp:`pencil` on :samp:`imap.foo.example`. .. only:: not man to your :doc:`sieve.cf ` to automatically log in as :samp:`user` with the password :samp:`pencil` on :samp:`imap.foo.example`. The :file:`.netrc` File ----------------------- The :file:`.netrc` file is a traditional facility to automate logins. For example, add .. code:: none machine imap.foo.example login user password pencil to your :file:`.netrc` to automatically log in as :samp:`user` with the password :samp:`pencil` on :samp:`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 *before* you can authenticate through another mechanism or you may authenticate *by* sending the certificate. To send a TLS certificate, set :confvar:`cert` to a :file:`{file}` that contains a TLS key and a TLS certificate: .. code:: none account imap.foo.example login user cert cert.pem To authenticate *by* sending that cerficiate, additionally set :confvar:`saslmechs` to ``external``: .. code:: none account imap.foo.example login user cert cert.pem saslmechs external .. only:: man See :manpage:`sieve.cf(5)` for details. .. only:: not man See :doc:`sieve.cf ` for details. Exit Status =========== 0 Success 1 Failure 2 Usage error Environment =========== .. envvar:: COLUMNS Terminal width in characters. .. envvar:: EDITOR Editor called by :sievecmd:`ed` (default: :command:`ed`). .. envvar:: HOME Home directory of the current user. .. envvar:: LANG, LC_ALL, LC_CTYPE Encoding for reading from/writing to the terminal and applications. Does *not* apply to Sieve scripts, which *must* be encoded as UTF-8. Order of preference is :envvar:`LC_ALL` > :envvar:`LC_CTYPE` > :envvar:`LANG`. .. envvar:: LINES Terminal height in lines. .. envvar:: LOGNAME Login name of the current user. .. envvar:: PAGER Pager called by :sievecmd:`more` (default: :command:`more`). .. envvar:: NETRC Filename of the :file:`.netrc` file (default: :file:`{$HOME}/.netrc`). .. envvar:: VISUAL Editor called by :sievecmd:`vi` (default: :command:`vi`). .. envvar:: XDG_CONFIG_HOME X Desktop Group base configuration directory (default: :file:`{$HOME}/.config`). Files ===== .. index:: pair: /etc/sieve/config; file .. index:: pair: /etc/sieve.cf; file .. index:: pair: $XDG_CONFIG_HOME/sieve/config; file .. index:: pair: $HOME/.sieve/config; file .. index:: pair: $HOME/.sieve.cf; file :file:`/etc/sieve/config`, :file:`/etc/sieve.cf`, :file:`{$XDG_CONFIG_HOME}/sieve/config`, :file:`{$HOME}/.sieve/config`, :file:`{$HOME}/.sieve.cf` Default configuration files. Not read when :option:`-c` is given. .. only:: man See :manpage:`sieve.cf(5)` for details. .. only:: not man See :doc:`sieve.cf ` for details. .. index:: pair: .netrc; file :file:`.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. Passwords can be queried from password managers to automate logins. However, any command that can be run by :command:`sievemgr` can, at the very least, also be run by any application that can run :command:`python`. Bugs ==== :file:`.netrc` records without a ``password`` token wrongly trigger a parse error in Python up to version 3.9. Examples ======== Upload :file:`script.sieve` to :samp:`imap.foo.example` and activate it: .. code:: none $ 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 Reading commands from standard input: .. code:: bash $ sievemgr user@imap.foo.example < put script.sieve > activate script.sieve > EOF Edit the active script on :samp:`imap.foo.example`: .. code:: bash $ sievemgr user@imap.foo.example vi -a Download all scripts from :samp:`imap.foo.example`: .. code:: bash $ sievemgr -e'get *' user@imap.foo.example .. only:: man See Also ======== :manpage:`sieve.cf(5)`