Python module¶
Synopsis¶
from sievemgr import SieveManager
Description¶
- class sievemgr.SieveManager[source]¶
Connection to a ManageSieve server.
For example:
>>> with SieveManager('imap.foo.example') as mgr: >>> mgr.authenticate('user', 'password') >>> with open('sieve.script', 'br') as script: >>> mgr.putscript(script, 'sieve.script') >>> mgr.setactive('sieve.script')
Warning
SieveManager
is not thread-safe.- __init__(*args, backup: int = 0, memory: int = 524_288, **kwargs)[source]¶
Create a
SieveManager
object.If args or kwargs are given, they are passed to
open()
. Otherwise, no connection is established.- Parameters:
backup – How many backups to keep by default.
memory – See max_size in
tempfile.SpooledTemporaryFile
.args – Positional arguments for
open()
.kwargs – Keyword arguments for
open()
.
- Raises:
AppConnectionError –
sock
has died.SieveCapabilityError – “STARTTLS” not supported.
SieveProtocolError – Server violated the ManageSieve protocol.
TLSSecurityError – Server certificate has been revoked.
- authenticate(login: str, *auth, owner: str = '', sasl: type[AbstractAuth] | Iterable[type[AbstractAuth]] = (), logauth: bool = False, **kwauth)¶
Authenticate as login.
How the user is authenticated depends on the type of SASL mechanisms given in sasl (e.g., password-based or the “EXTERNAL” mechanism).
If no mechanisms are given, authentication is attempted with every supported non-obsolete password-based mechanism, starting with those with better security properties and progressing to those with worse security properties.
Unrecognised arguments are passed on to SASL mechanism constructors. Password-based mechanisms require a password:
>>> mgr.authenticate('user', 'password')
By contrast, the “EXTERNAL” mechanism takes no arguments:
>>> mgr.authenticate('user', sasl=ExternalAuth)
If an owner is given, the scripts of that owner are managed, instead of those owned by login. This requires elevated privileges.
- Parameters:
login – User to login as (authentication ID).
owner – User whose scripts to manage (authorisation ID).
sasl – SASL mechanisms (default:
BasePwdAuth.getmechs()
).logauth – Log authentication exchange?
auth – Positional arguments for SASL mechanism constructors.
kwauth – Keyword arguments for SASL mechanism constructors.
- Raises:
AppConnectionError –
sock
has died.AppOperationError – Another operation is already in progress.
SASLCapabilityError – Authentication mechanisms exhausted.
SASLProtocolError – Server violated the SASL protocol.
SASLSecurityError – Server could not be verified.
SieveConnectionError – Server has closed the connection.
SieveOperationError – Authentication failed.
SieveProtocolError – Server violated the ManageSieve protocol.
ValueError – Bad characters in credentials.
Note
If an owner is given, but the selected authentication mechanism does not support proxy authentication, an error is logged to the console and authentication is attempted with the next mechanism.
- backupscript(script: str, keep: int = 1)[source]¶
Make an Emacs-style backup of script.
- keep = 0
Do nothing.
- keep = 1
script
is backed up asscript~
.- keep > 1
script
is backed up asscript.~n~
. n starts with 1 and increments with each backup. Old backups are deleted if there are more than keep backups.
For example:
>>> mgr.listscripts() [('script.sieve', True)] >>> mgr.backupscript('script.sieve', keep=0) >>> mgr.listscripts() [('script.sieve', True)]
>>> mgr.listscripts() [('script.sieve', True)] >>> mgr.backupscript('script.sieve', keep=1) >>> mgr.listscripts() [('script.sieve', True), ('script.sieve~', False)]
>>> mgr.listscripts() [('script.sieve', True)] >>> mgr.backupscript('script.sieve', keep=2) >>> mgr.listscripts() [('script.sieve', True), ('script.sieve.~1~', False)] >>> mgr.backupscript('script.sieve', keep=2) >>> mgr.listscripts() [('script.sieve', True), ('script.sieve.~1~', False), ('script.sieve.~2~', False)] >>> mgr.backupscript('script.sieve', keep=2) >>> mgr.listscripts() [('script.sieve', True), ('script.sieve.~2~', False), ('script.sieve.~3~', False)]
- Parameters:
script – Script name.
keep – How many backups to keep.
- Raises:
AppConnectionError –
sock
has died.AppOperationError – Another operation is already in progress.
SieveConnectionError – Server has closed the connection.
SieveProtocolError – Server violated the ManageSieve protocol.
- checkscript(script: str | IO)[source]¶
Check whether script is valid.
Syntax errors trigger a
SieveOperationError
. Semantic errors are reported inwarning
.For example:
>>> checkscript('foo') Traceback (most recent call last): [...] SieveOperationError: line 1: error: expected end of command ';' error: parse failed.
>>> checkscript('# foo') >>>
- Parameters:
script – Script (not script name).
- Raises:
AppConnectionError –
sock
has died.AppOperationError – Another operation is already in progress.
SieveCapabilityError – “CHECKSCRIPT” not supported.
SieveConnectionError – Server has closed the connection.
SieveOperationError – Script contains syntax errors.
SieveProtocolError – Server violated the ManageSieve protocol.
Important
Sieve scripts must be encoded in UTF-8.
- close()¶
Close the client side of the connection.
Warning
Call only when the server has closed the connection.
- collect(check: bool = False) tuple[Response, list[Line]] ¶
Collect the server’s response to the last command.
For example:
>>> conn.sendline(Atom('listscripts')) >>> conn.collect() (Response(response=Atom('OK'), code=(), message=None), [['foo.sieve', 'ACTIVE'], ['bar.sieve'], ['baz.sieve']])
- Parameters:
check – Raise an error if the response is not “OK”?
- Raises:
AppConnectionError –
sock
has died.SieveConnectionError – Server said “BYE”. [1]
SieveOperationError – Server said “NO”. [1]
SieveProtocolError – Server violated the ManageSieve protocol.
- copyscript(source: str, target: str, backup: int | None = None)[source]¶
Download source and re-upload it as target.
- Parameters:
source – Source name.
target – Target name.
backup – How many backups to keep (default:
backup
).
- Raises:
AppConnectionError –
sock
has died.AppOperationError – Another operation is already in progress.
SieveConnectionError – Server has closed the connection.
SieveProtocolError – Server violated the ManageSieve protocol.
- deletescript(script: str)[source]¶
Delete script.
- Raises:
AppConnectionError –
sock
has died.AppOperationError – Another operation is already in progress.
SieveConnectionError – Server has closed the connection.
SieveProtocolError – Server violated the ManageSieve protocol.
- editscripts(command: list[str], scripts: list[str], *args, catch: Callable[[Exception, str], bool] | None = None, check: bool = True, create: bool = True, **kwargs) CompletedProcess [source]¶
Download scripts, edit them with command, and re-upload them.
The scripts are appended to the command, which is then passed to
subprocess.run()
. Scripts that have been changed are then re-uploaded to the server. If the server has closed the connection in the meantime, the connection is re-established automatically.If
putscript()
raises an error and catch has been given, then the error and the name of the offending script are passed to catch, which should return True if the command should be re-invoked for that script and False otherwise. Either way, the error will be suppressed.For example:
>>> mgr.editscripts(['vi'], ['foo.sieve'])
>>> cp = mgr.editscripts(['cmp'], ['a.sieve', 'b.sieve'], check=False) >>> if cp.returncode != 0: >>> print('a.sieve and b.sieve differ')
- Parameters:
command – Command to run.
scripts – Scripts to edit.
catch – Error handler.
check – See
subprocess.run()
.create – Create scripts that do not exist?
args – Positional arguments for
subprocess.run()
.kwargs – Keywords arguments for
subprocess.run()
.
- Raises:
AppConnectionError –
sock
has died.AppOperationError – Another operation is already in progress.
SieveConnectionError – Server has closed the connection.
SieveOperationError – At least one script contains a syntax error. [2]
SieveProtocolError – Server violated the ManageSieve protocol.
ValueError – Script name contains path separator.
[2] Only raised if catch has not been given.
- execute(command: str, *args: IO | Word) tuple[Response, list[Line]] ¶
Execute command and return the server’s response.
For example:
>>> conn.execute('listscripts') (Response(response=Atom('OK'), code=(), message=None), [['foo.sieve', 'ACTIVE'], ['bar.sieve'], ['baz.sieve']])
- Raises:
AppConnectionError –
sock
has died.AppOperationError – Another operation is already in progress.
SieveConnectionError – Server said “BYE”.
SieveOperationError – Server said “NO”.
SieveProtocolError – Server violated the ManageSieve protocol.
Note
Referrals are followed automatically.
- getactive() str | None [source]¶
Get the name of the active script.
- Raises:
AppConnectionError –
sock
has died.AppOperationError – Another operation is already in progress.
SieveConnectionError – Server has closed the connection.
SieveProtocolError – Server violated the ManageSieve protocol.
- getscript(script: str) str [source]¶
Download script.
For example:
>>> with open('foo.sieve', 'w', encoding='utf8') as file: >>> file.write(mgr.getscript('foo.sieve'))
- Parameters:
script – Script name.
- Raises:
AppConnectionError –
sock
has died.AppOperationError – Another operation is already in progress.
SieveConnectionError – Server has closed the connection.
SieveProtocolError – Server violated the ManageSieve protocol.
- geturl() URL | None ¶
URL of the current connection.
For example:
>>> with SieveManager('imap.foo.example') as mgr: >>> mgr.authenticate('user', 'password') >>> mgr.geturl() 'sieve://user@imap.foo.example'
Note
Only changes to the connection state effected by
open()
,close()
,shutdown()
,authenticate()
,unauthenticate()
, and referrals are tracked.
- havespace(script: str, size: int)[source]¶
Check whether there is enough space for script.
- Parameters:
script – Script name.
size – Script size in bytes.
- Raises:
AppConnectionError –
sock
has died.AppOperationError – Another operation is already in progress.
SieveConnectionError – Server has closed the connection.
SieveOperationError – There is not enough space.
SieveProtocolError – Server violated the ManageSieve protocol.
- isalive(check: bool = False) bool ¶
Check whether
sock
is alive.- Parameters:
check – Raise an error if
sock
has died.- Raises:
AppConnectionError –
sock
has died. [3]
[3] Only raised if check is True.
- listscripts(cached: bool = False) list[tuple[str, bool]] [source]¶
List scripts and whether they are the active script.
For example:
>>> mgr.listscripts() [('foo.sieve', False), ('bar.sieve', True)]
>>> scripts = [script for script, _ in mgr.listscripts()]
- Parameters:
cached – Return cached response? [4]
- Returns:
A list of script name/status tuples.
- Raises:
AppConnectionError –
sock
has died.AppOperationError – Another operation is already in progress.
SieveConnectionError – Server has closed the connection.
SieveProtocolError – Server violated the ManageSieve protocol.
[4] (1,2) The cache is cleared after
copyscript()
,deletescript()
,putscript()
,renamescript()
,setactive()
, andunsetactive()
.
- logout()[source]¶
Log out.
Note
logout()
should be called to close the connection unlessSieveManager
is used as a context manager.Warning
Logging out is unsafe after a
ProtocolError
. Useshutdown()
instead.
- noop(tag: str | None = None) str | None [source]¶
Request a no-op.
For example:
>>> mgr.noop('foo') 'foo'
- Parameters:
tag – String for the server to echo back.
- Returns:
Server echo.
- Raises:
AppConnectionError –
sock
has died.AppOperationError – Another operation is already in progress.
SieveCapabilityError – “NOOP” not supported.
SieveConnectionError – Server has closed the connection.
SieveProtocolError – Server violated the ManageSieve protocol.
- open(host: str, port: int = 4190, source: tuple[str, int] = ('', 0), timeout: float | None = socket.getdefaulttimeout(), tls: bool = True, ocsp: bool = True)¶
Connect to host at port.
- Parameters:
host – Server name or address.
port – Server port.
source – Source address and port.
timeout – Timeout in seconds.
tls – Secure the connection?
ocsp – Check whether the server certificate was revoked?
- Raises:
ConnectionError – Connection failed.
SieveCapabilityError – “STARTTLS” not supported.
SieveProtocolError – Server violated the ManageSieve protocol.
TLSSecurityError – Server certificate has been revoked.
- putscript(source: str | IO, target: str, backup: int | None = None)[source]¶
Upload source to the server as target.
The server should reject syntactically invalid scripts. It may issue a
warning
for semantically invalid scripts, but should accept them nonetheless. Updates are atomic.For example:
>>> mgr.putscript('# empty', 'foo.sieve')
>>> with open('foo.sieve', 'br') as file: >>> mgr.putscript(file, 'foo.sieve')
- Parameters:
source – Script (not script name).
target – Script name.
backup – How many backups to keep (default:
backup
).
- Raises:
AppConnectionError –
sock
has died.AppOperationError – Another operation is already in progress.
SieveConnectionError – Server has closed the connection.
SieveOperationError – Script contains syntax errors.
SieveProtocolError – Server violated the ManageSieve protocol.
Important
Sieve scripts must be encoded in UTF-8.
- receiveline() Line ¶
Receive a line and parse it.
For example:
>>> mgr.sendline(Atom('listscripts')) >>> mgr.receiveline() ['foo.sieve', 'ACTIVE'] >>> mgr.receiveline() ['bar.sieve'] >>> mgr.receiveline() ['baz.sieve'] >>> mgr.receiveline() ['OK', 'Listscripts completed.']
- Raises:
ValueError – Line is malformed.
- renamescript(source: str, target: str, emulate: bool = True)[source]¶
Rename source to target.
Some servers do not the support the “RENAMESCRIPT” command. On such servers, renaming is emulated by downloading source, re-uploading it as target, marking target as the active script if source is the active script, and then deleting source.
For example:
>>> mgr.renamescript('foo.sieve', 'bar.sieve', emulate=False)
- Parameters:
source – Script name.
target – Script name.
emulate – Emulate “RENAMESCRIPT” if the server does not support it?
- Raises:
SieveCapabilityError – “RENAMESCRIPT” not supported. [5]
SieveOperationError – source does not exist or target exists.
[5] Only raised if emulate is False.
- scriptexists(script: str, cached: bool = False) bool [source]¶
Check if script exists.
- Parameters:
script – Script name.
cached – Return cached response? [4]
- Raises:
AppConnectionError –
sock
has died.AppOperationError – Another operation is already in progress.
SieveConnectionError – Server has closed the connection.
SieveProtocolError – Server violated the ManageSieve protocol.
- sendline(*objs: IO[Any] | Word, whole: bool = True)¶
Convert objs to ACAP types and send them.
For example:
>>> mgr.sendline(Atom('havespace'), 'script.sieve', 12345) >>> mgr.receiveline() ['OK', 'Putscript would succeed.']
Pipeline commands:
>>> mgr.isalive(check=True) >>> with open('foo.sieve') as script: >>> mgr.sendline(Atom('putscript', script, 'foo.sieve')) >>> mgr.sendline(Atom('logout')) >>> for _ in range(2): >>> mgr.collect(check=True)
- Parameters:
objs – Objects to serialise.
whole – Conclude data with CRLF?
- Raises:
ValueError – Number is not within the range [0, 4,294,967,295].
TypeError – Object cannot be represented as ACAP data type.
[6] (1,2) Depending on content.
[7] Numbers must be within the range [0, 4,294,967,295]
[8] Strings are encoded in UTF-8 and normalised to form C.
- setactive(script: str)[source]¶
Mark script as the active script.
- Raises:
AppConnectionError –
sock
has died.AppOperationError – Another operation is already in progress.
SieveConnectionError – Server has closed the connection.
SieveProtocolError – Server violated the ManageSieve protocol.
- shutdown()¶
Shut the connection down.
Note
Use only when
logging out
would be unsafe.
- unauthenticate()[source]¶
Unauthenticate.
- Raises:
AppConnectionError –
sock
has died.AppOperationError – Another operation is already in progress.
SieveCapabilityError – “UNAUTHENTICATE” not supported.
SieveConnectionError – Server has closed the connection.
SieveProtocolError – Server violated the ManageSieve protocol.
- unsetactive()[source]¶
Deactivate the active script.
- Raises:
AppConnectionError –
sock
has died.AppOperationError – Another operation is already in progress.
SieveConnectionError – Server has closed the connection.
SieveProtocolError – Server violated the ManageSieve protocol.
- classmethod validname(script: str, check: bool = False) bool [source]¶
Check whether script is a valid script name.
- Parameters:
script – Script name
check – Raise an error if script is not a valid script name?
- Raises:
ValueError – script is not valid. [9]
[9] Only raised if check is True.
- capabilities: Capabilities | None = None¶
Server capabilities.
- lock: allocate_lock = <unlocked _thread.lock object>¶
Operation lock.
- logger: Logger = <Logger sievemgr (WARNING)>¶
Logger to use.
Messages are logged with the following priorities:
Priority
Used for
logging.ERROR
Non-fatal errors
logging.INFO
State changes
logging.DEBUG
Data sent to/received from the server
Suppress logging:
>>> from logging import getLogger >>> getLogger('sievemgr').setLevel(logging.CRITICAL)
Use a custom logger:
>>> from logging import getLogger >>> mgr.logger = getLogger('foo').addHandler(logging.NullHandler())
Print data send to/received from the server to standard error:
>>> from logging import getLogger >>> getLogger('sievemgr').setLevel(logging.DEBUG) >>> mgr.listscripts() C: LISTSCRIPTS S: "foo.sieve" ACTIVE S: "bar.sieve" S: "baz.sieve" S: OK "Listscripts completed" (Response(response=Atom('OK'), code=(), message=None), [('foo.sieve', True), ('bar.sieve', False), ('baz.sieve', False)])
- sslcontext: SSLContext = <ssl.SSLContext object>¶
Settings for negotiating Transport Layer Security (TLS).
Disable workarounds for broken X.509 certificates:
>>> with SieveManager() as mgr: >>> mgr.sslcontext.verify_flags |= ssl.VERIFY_X509_STRICT >>> mgr.open('imap.foo.example') >>> ...
Load client certificate/key pair:
>>> with SieveManager() as mgr: >>> mgr.sslcontext.load_cert_chain(cert='cert.pem') >>> mgr.open('imap.foo.example') >>> ...
Use a custom certificate authority:
>>> with SieveManager() as mgr: >>> mgr.sslcontext.load_verify_locations(cafile='ca.pem') >>> mgr.open('imap.foo.example') >>> ...
- property timeout: float | None¶
Connection timeout in seconds.
Set timeout to 500 ms:
>>> mgr.timeout = 0.5
Note
The timeout can only be set while a connection is open.
- warning: str | None = None¶
Warning issued in response to the last “CHECKSCRIPT” or “PUTSCRIPT”.
For example:
>>> with open('script.sieve', 'br') as file: >>> mgr.execute('putscript', file, 'script.sieve') (Response(response=Atom('OK'), code=('warnings,'), message='line 7: may need to be frobnicated'), []) >>> mgr.warning 'line 7: may need to be frobnicated'
Note
Only set by
collect()
,execute()
,checkscript()
, andputscript()
.See also
- RFC 5804 (sec. 1.3)
ManageSieve “WARNINGS” response code.
- class sievemgr.Capabilities[source]¶
Bases:
object
Server capabilities.
- __init__(implementation: str | None = None, sieve: tuple[str, ...] = (), language: str | None = None, maxredirects: int | None = None, notify: tuple[str, ...] = (), owner: str = '', sasl: tuple[str, ...] = (), starttls: bool = False, unauthenticate: bool = False, version: str | None = None, notunderstood: dict = <factory>) None ¶
- classmethod fromlines(lines: Iterable[Line]) CapabilitiesT [source]¶
Create a
Capabilities
object from a server response.
- class sievemgr.Response[source]¶
Bases:
object
Server response to a command.
See also
- RFC 5804 (secs. 1.2, 1.3, 4, 6.4, and passim)
ManageSieve responses
- matches(*categories: str) bool [source]¶
Check if
code
matches any of the given categories.Returns False if
code
is empty. Matching is case-insensitive.For example:
>>> with open('script.sieve') as script: >>> try: >>> mgr.putscript(script, script.name) >>> except SieveOperationError as err: >>> if err.matches('QUOTA'): >>> print('over quota')
Print more informative messages:
>>> with open('script.sieve') as script: >>> try: >>> mgr.putscript(script, script.name) >>> except SieveOperationError as err: >>> if err.matches('QUOTA/MAXSCRIPTS'): >>> print('too many scripts') >>> elif err.matches('QUOTA/MAXSIZE'): >>> print(f'{script.name} is too large') >>> elif err.matches('QUOTA'): >>> print('over quota')
- toerror() SieveError [source]¶
Convert a
Response
into an error.
- code: tuple[Word, ...] = ()¶
Response code.
ManageSieve response codes are lists of categories, separated by slashes (“/”), where each category is the super-category of the next (e.g., “quota/maxsize”).
Some response codes carry data (e.g.,
TAG "SYNC-123"
).See RFC 5804 (sec. 1.3) for a list of response codes.
Warning
Servers need not return response codes.
- class sievemgr.URL[source]¶
Bases:
object
Sieve URL.
See also
- RFC 5804 (sec. 3)
Sieve URL Scheme
- __init__(hostname: str, scheme: str = 'sieve', username: str | None = None, password: str | None = None, port: int | None = None, owner: str | None = None, scriptname: str | None = None) None ¶
- classmethod fromstr(url: str) URLT [source]¶
Create a
URL
object from a URL string.For example:
>>> URL.fromstr('sieve://user@imap.foo.example') URL(hostname='imap.foo.example', scheme='sieve', username='user', password=None, port=None, owner=None, scriptname=None)
- Raises:
ValueError – Not a valid Sieve URL.
SASL¶
Note
You need not read this section unless you want to implement an authentication mechanism.
Warning
This API will change at some point in the future.
- class sievemgr.AbstractAuth[source]¶
Bases:
ABC
Abstract base class for SASL mechanisms.
The ManageSieve “AUTHENTICATE” command performs a Simple Authentication and Security Layer (SASL) protocol exchange. SASL is a framework that comprises different authentication mechanisms (“SASL mechanisms”).
SieveManager.authenticate()
does not implement any such mechanism itself, but delegates the SASL protocol exchange to classes that do. Such classes must subclass this class and have aname
attribute that indicates the mechanism they implemented.Tip
Do not subclass
AbstractAuth
directly. SubclassBaseAuth
instead.See also
AbstractSASLAdapter
Abstract base class for sending and receiving SASL messages.
- RFC 3454
Preparation of Internationalized Strings
- RFC 4013
Stringprep Profile for User Names and Passwords
- RFC 4422
Simple Authentication and Security Layer (SASL)
- RFC 5804 (sec. 2.1)
ManageSieve “AUTHENTICATE” command
- abstract __call__() Any | None [source]¶
Authenticate as
authcid
.authcid
is authorised asauthzid
ifauthzid
is set (proxy authentication).- Returns:
Data returned by the server, if any.
- Raises:
ConnectionError – Server has closed the connection.
OperationError – Authentication failed.
SASLCapabilityError – Some feature is not supported.
SASLProtocolError – Server violated the SASL protocol.
SASLSecurityError – Server verification failed.
TLSCapabilityError – Channel-binding is not supported.
- abstract __init__(conn: AbstractSASLAdapter, authcid: str, authzid: str = '', prepare: SASLPrep = SASLPrep.ALL)[source]¶
Prepare authentication.
authcid and authzid are prepared according to RFC 3454 and RFC 4013 if the specification of the SASL mechanism mandates or recommends string preparation and
prepare & SASLPrep.USERNAMES
evaluates as true.- Parameters:
conn – Connection over which to authenticate.
authcid – Authentication ID (user to login as).
authzid – Authorisation ID (user whose rights to acquire).
prepare – Which credentials to prepare.
- Raises:
ValueError – Bad characters in username.
- class sievemgr.AbstractSASLAdapter[source]¶
Bases:
ABC
Abstract base class for sending and receiving SASL messages.
Messages that comprise an SASL protocol exchange (“SASL messages”) must be translated to the underlying protocol. This class defines the types of messages that may occur in an SASL protocol exchange. Classes that translate between SASL and the underlying protocol must subclass this class.
See also
AbstractAuth
Abstract base class for SASL mechanisms.
- RFC 4422
Simple Authentication and Security Layer (SASL)
- abstract abort()[source]¶
Abort authentication.
- Raises:
ProtocolError – Protocol violation.
- abstract begin(name: str, data: bytes | None = None)[source]¶
Begin authentication.
- Parameters:
name – SASL mechanism name.
data – Optional client-first message.
- Raises:
ConnectionError – Connection was closed.
ProtocolError – Protocol violation.
- abstract end()[source]¶
Conclude authentication.
- Raises:
ConnectionError – Connection was closed.
OperationError – Authentication failed.
ProtocolError – Protocol violation.
- abstract receive() bytes [source]¶
Receive and decode an SASL message.
- Raises:
ConnectionError – Connection was closed.
OperationError – Authentication failed.
ProtocolError – Protocol violation.
- abstract send(data: bytes)[source]¶
Encode and send an SASL message.
- Raises:
ConnectionError – Connection was closed.
ProtocolError – Protocol violation.
- class sievemgr.BaseAuth[source]¶
Bases:
AbstractAuth
,ABC
Base class for authentication mechanisms.
BaseAuth
provides methods to prepare strings according to RFC 3454 and RFC 4013 and a layer over the underlyingAbstractSASLAdapter
object that callsAbstractSASLAdapter.begin()
andAbstractSASLAdapter.end()
transparently.Credentials must be prepared in
__init__()
. Subclasses should pass connection, authcid, authzid, and prepare tosuper().__init__
and useprepare()
to prepare the remaining credentials. For example:def __init__(self, connection: AbstractSASLAdapter, authcid: str, password: str, authzid: str = '', prepare: SASLPrep = SASLPrep.ALL): """Prepare authentication. `authcid`, `password`, and `authzid` are prepared according to :rfc:`3454` and :rfc:`4013` if ``prepare & SASLPrep.USERNAMES`` and/or ``prepare & SASLPrep.PASSWORDS`` are non-zero. Arguments: conn: Connection over which to authenticate. authcid: Authentication ID (user to login as). password: Password. authzid: Authorisation ID (user whose rights to acquire). prepare: Which credentials to prepare. Raises: ValueError: Bad characters in username or password. """ super().__init__(connection, authcid, authzid, prepare) prepare &= SASLPrep.PASSWORDS # type: ignore[assignment] self.password = self.prepare(password) if prepare else password
The SASL exchange must be implemented in
exchange()
. Subclasses should usesend()
andreceive()
to exchange SASL messages. For example:def exchange(self): data = '\0'.join((self.authzid, self.authcid, self.password)) self.send(data.encode('utf8'))
- __call__() Any | None [source]¶
Authenticate as
authcid
.authcid
is authorised asauthzid
ifauthzid
is set (proxy authentication).- Returns:
Data returned by the server, if any.
- Raises:
ConnectionError – Server has closed the connection.
OperationError – Authentication failed.
SASLCapabilityError – Some feature is not supported.
SASLProtocolError – Server violated the SASL protocol.
SASLSecurityError – Server verification failed.
TLSCapabilityError – Channel-binding is not supported.
Note
Calls
exchange()
andend()
.
- __init__(adapter: AbstractSASLAdapter, authcid: str, authzid: str = '', prepare: SASLPrep = SASLPrep.ALL)[source]¶
Prepare authentication.
authcid and authzid are prepared according to RFC 3454 and RFC 4013 if
prepare & SASLPrep.USERNAMES
evaluates as true.- Parameters:
conn – Connection over which to authenticate.
authcid – Authentication ID (user to login as).
authzid – Authorisation ID (user whose rights to acquire).
prepare – Which credentials to prepare.
- Raises:
ValueError – Bad characters in username.
- abort()[source]¶
Abort authentication.
- Raises:
ProtocolError – Protocol violation.
- begin(data: bytes | None = None)[source]¶
Begin authentication.
- Parameters:
data – Optional client-first message.
- Raises:
ConnectionError – Connection was closed.
ProtocolError – Protocol violation.
- end()[source]¶
Conclude authentication.
- Raises:
ConnectionError – Connection was closed.
OperationError – Authentication failed.
ProtocolError – Protocol violation.
- static prepare(string: str) str [source]¶
Prepare string according to RFC 3454 and RFC 4013.
- Returns:
Prepared string.
- Raises:
ValueError – String is malformed.
- receive() bytes [source]¶
Receive and decode an SASL message.
- Raises:
ConnectionError – Connection was closed.
OperationError – Authentication failed.
ProtocolError – Protocol violation.
Note
Calls
begin()
if needed.
- send(data: bytes)[source]¶
Encode and send an SASL message.
- Raises:
ConnectionError – Connection was closed.
ProtocolError – Protocol violation.
Note
Calls
begin()
if needed.
- adapter: AbstractSASLAdapter¶
Underlying SASL adapter.
- class sievemgr.BasePwdAuth[source]¶
-
Base class for password-based authentication mechanisms.
Prepares credentials, so that subclasses need only implement
exchange()
. For example:class PlainAuth(BasePwdAuth): """PLAIN authentication. .. seealso:: :rfc:`4616` PLAIN authentication mechanism. """ def exchange(self): data = '\0'.join((self.authzid, self.authcid, self.password)) self.send(data.encode('utf8')) name = 'PLAIN'
- __init__(connection: AbstractSASLAdapter, authcid: str, password: str, authzid: str = '', prepare: SASLPrep = SASLPrep.ALL)[source]¶
Prepare authentication.
authcid, password, and authzid are prepared according to RFC 3454 and RFC 4013 if
prepare & SASLPrep.USERNAMES
and/orprepare & SASLPrep.PASSWORDS
are non-zero.- Parameters:
conn – Connection over which to authenticate.
authcid – Authentication ID (user to login as).
password – Password.
authzid – Authorisation ID (user whose rights to acquire).
prepare – Which credentials to prepare.
- Raises:
ValueError – Bad characters in username or password.
- adapter: AbstractSASLAdapter¶
Underlying SASL adapter.
- class sievemgr.BaseScramAuth[source]¶
Bases:
BasePwdAuth
,ABC
Base class for SCRAM authentication mechanisms.
Implements
exchange()
, so that subclasses need only define a digest. For example:class ScramSHA1Auth(BaseScramAuth): """SCRAM-SHA-1 authentication.""" @property def digest(self) -> str: return 'sha1' name = 'SCRAM-SHA-1' order = -10
See also
- RFC 5802
Salted Challenge Response Authentication Mechanism (SCRAM).
- RFC 7677
SCRAM-SHA-256 and SCRAM-SHA-256-PLUS.
- https://datatracker.ietf.org/doc/html/draft-melnikov-scram-bis
Updated recommendations for implementing SCRAM.
- https://datatracker.ietf.org/doc/html/draft-melnikov-scram-sha-512-03
SCRAM-SHA-512 and SCRAM-SHA-512-PLUS.
- https://datatracker.ietf.org/doc/html/draft-melnikov-scram-sha3-512-03
SCRAM-SHA3-512 and SCRAM-SHA3-512-PLUS.
- https://csb.stevekerrison.com/post/2022-01-channel-binding
Discussion of TLS channel binding.
- https://csb.stevekerrison.com/post/2022-05-scram-detail
Discussion of SCRAM.
- adapter: AbstractSASLAdapter¶
Underlying SASL adapter.
- class sievemgr.BaseScramPlusAuth[source]¶
Bases:
BaseScramAuth
,ABC
Base class for SCRAM mechanisms with channel binding.
For example:
class ScramSHA1PlusAuth(BaseScramPlusAuth, ScramSHA1Auth): """SCRAM-SHA-1-PLUS authentication.""" name = 'SCRAM-SHA-1-PLUS' order = -1000
- __init__(*args, **kwargs)[source]¶
Prepare authentication.
authcid, password, and authzid are prepared according to RFC 3454 and RFC 4013 if
prepare & SASLPrep.USERNAMES
and/orprepare & SASLPrep.PASSWORDS
are non-zero.- Parameters:
conn – Connection over which to authenticate.
authcid – Authentication ID (user to login as).
password – Password.
authzid – Authorisation ID (user whose rights to acquire).
prepare – Which credentials to prepare.
- Raises:
ValueError – Bad characters in username or password.
- class sievemgr.AuthzUnsupportedMixin[source]¶
Bases:
object
Mixin for SASL mechanisms that do not support authorisation.
For example:
- __init__(*args, **kwargs)[source]¶
Prepare authentication.
- Raises:
SASLCapabilityError –
authzid
is set.
- class sievemgr.CramMD5Auth[source]¶
Bases:
AuthzUnsupportedMixin
,BasePwdAuth
CRAM-MD5 authentication.
See also
- RFC 2195 (sec. 2)
Definition of CRAM-MD5.
- adapter: AbstractSASLAdapter¶
Underlying SASL adapter.
- class sievemgr.ExternalAuth[source]¶
Bases:
BaseAuth
EXTERNAL authentication.
See also
- RFC 4422 (App. A)
Definition of the EXTERNAL mechanism.
- adapter: AbstractSASLAdapter¶
Underlying SASL adapter.
- class sievemgr.LoginAuth[source]¶
Bases:
AuthzUnsupportedMixin
,BasePwdAuth
LOGIN authentication.
See also
- https://datatracker.ietf.org/doc/draft-murchison-sasl-login
Definition of the LOGIN mechanism.
- __init__(*args, **kwargs)[source]¶
Prepare authentication.
- Parameters:
conn – Connection over which to authenticate.
authcid – Authentication ID (user to login as).
password – Password.
authzid – Authorisation ID (user whose rights to acquire).
prepare – Which credentials to prepare.
- Raises:
ValueError – Password contains CR, LF, or NUL.
- adapter: AbstractSASLAdapter¶
Underlying SASL adapter.
- class sievemgr.PlainAuth[source]¶
Bases:
BasePwdAuth
PLAIN authentication.
See also
- RFC 4616
PLAIN authentication mechanism.
- adapter: AbstractSASLAdapter¶
Underlying SASL adapter.
- class sievemgr.ScramSHA1Auth[source]¶
Bases:
BaseScramAuth
SCRAM-SHA-1 authentication.
- adapter: AbstractSASLAdapter¶
Underlying SASL adapter.
- class sievemgr.ScramSHA1PlusAuth[source]¶
Bases:
BaseScramPlusAuth
,ScramSHA1Auth
SCRAM-SHA-1-PLUS authentication.
- adapter: AbstractSASLAdapter¶
Underlying SASL adapter.
- class sievemgr.ScramSHA224Auth[source]¶
Bases:
BaseScramAuth
SCRAM-SHA-224 authentication.
- adapter: AbstractSASLAdapter¶
Underlying SASL adapter.
- class sievemgr.ScramSHA224PlusAuth[source]¶
Bases:
BaseScramPlusAuth
,ScramSHA224Auth
SCRAM-SHA-224-PLUS authentication.
- adapter: AbstractSASLAdapter¶
Underlying SASL adapter.
- class sievemgr.ScramSHA256Auth[source]¶
Bases:
BaseScramAuth
SCRAM-SHA-256 authentication.
- adapter: AbstractSASLAdapter¶
Underlying SASL adapter.
- class sievemgr.ScramSHA256PlusAuth[source]¶
Bases:
BaseScramPlusAuth
,ScramSHA256Auth
SCRAM-SHA-256-PLUS authentication.
- adapter: AbstractSASLAdapter¶
Underlying SASL adapter.
- class sievemgr.ScramSHA384Auth[source]¶
Bases:
BaseScramAuth
SCRAM-SHA-384 authentication.
- adapter: AbstractSASLAdapter¶
Underlying SASL adapter.
- class sievemgr.ScramSHA384PlusAuth[source]¶
Bases:
BaseScramPlusAuth
,ScramSHA384Auth
SCRAM-SHA-384-PLUS authentication.
- adapter: AbstractSASLAdapter¶
Underlying SASL adapter.
- class sievemgr.ScramSHA512Auth[source]¶
Bases:
BaseScramAuth
SCRAM-SHA-512 authentication.
- adapter: AbstractSASLAdapter¶
Underlying SASL adapter.
- class sievemgr.ScramSHA512PlusAuth[source]¶
Bases:
BaseScramPlusAuth
,ScramSHA512Auth
SCRAM-SHA-512-PLUS authentication.
- adapter: AbstractSASLAdapter¶
Underlying SASL adapter.
- class sievemgr.ScramSHA3_512Auth[source]¶
Bases:
BaseScramAuth
SCRAM-SHA-512 authentication.
- adapter: AbstractSASLAdapter¶
Underlying SASL adapter.
- class sievemgr.ScramSHA3_512PlusAuth[source]¶
Bases:
BaseScramPlusAuth
,ScramSHA3_512Auth
SCRAM-SHA-512-PLUS authentication.
- adapter: AbstractSASLAdapter¶
Underlying SASL adapter.
- enum sievemgr.AuthState(value)[source]¶
Bases:
IntEnum
State of the authentication process.
- Member Type:
Valid values are as follows:
- PREAUTH = <AuthState.PREAUTH: 1>¶
- SENT = <AuthState.SENT: 2>¶
- RECEIVED = <AuthState.RECEIVED: 3>¶
- DONE = <AuthState.DONE: 4>¶
The
Enum
and its members also have the following methods:- __new__(value)¶
- enum sievemgr.SASLPrep(value)[source]¶
Bases:
IntEnum
Controls which strings are prepared for authentication.
See also
- Member Type:
Valid values are as follows:
- NONE = <SASLPrep.NONE: 0>¶
- USERNAMES = <SASLPrep.USERNAMES: 1>¶
- PASSWORDS = <SASLPrep.PASSWORDS: 2>¶
- ALL = <SASLPrep.ALL: 3>¶
The
Enum
and its members also have the following methods:- __new__(value)¶
ERRORS¶
- exception sievemgr.ProtocolError[source]¶
Bases:
Error
Base class for protocol errors.
Danger
Continuing after a
ProtocolError
may cause undefined behaviour.
- exception sievemgr.SecurityError[source]¶
Bases:
Error
Base class for security errors.
Danger
Continuing after a
SecurityError
compromises the connection.
- exception sievemgr.AppConfigError[source]¶
Bases:
AppError
,ConfigError
Applicaiton configuration error.
- exception sievemgr.AppConnectionError[source]¶
Bases:
AppError
,ConnectionError
Client-side connection error.
- exception sievemgr.AppOperationError[source]¶
Bases:
AppError
,OperationError
Client-side operation error.
- exception sievemgr.AppSecurityError[source]¶
Bases:
AppError
,SecurityError
Client security error.
- exception sievemgr.OCSPOperationError[source]¶
Bases:
OCSPError
,OperationError
OCSP operation error.
- exception sievemgr.SASLProtocolError[source]¶
Bases:
SASLError
,ProtocolError
Server violated the SASL protocol.
- exception sievemgr.SASLSecurityError[source]¶
Bases:
SASLError
,SecurityError
SASL security error.
- exception sievemgr.SieveCapabilityError[source]¶
Bases:
SieveError
,CapabilityError
Capability not supported by the server.
- exception sievemgr.SieveConnectionError[source]¶
Bases:
Response
,SieveError
,ConnectionError
Server said “BYE”.
- exception sievemgr.SieveOperationError[source]¶
Bases:
Response
,SieveError
,OperationError
Server said “NO”.
- exception sievemgr.SieveProtocolError[source]¶
Bases:
SieveError
,ProtocolError
Server violated the ManageSieve protocol error.
- exception sievemgr.TLSCapabilityError[source]¶
Bases:
TLSError
,CapabilityError
TLS capability error.
- exception sievemgr.TLSSecurityError[source]¶
Bases:
TLSError
,OperationError
TLS security error.