README

managesieve
===========

Description
-----------

The package managesieve implements the ManageSieve protocol as specified in
RFC 5804.  It covers all mandatory parts of the protocol with the exception of
the SCRAM-SHA-1 SASL mechanism.  Additional SASL authentication mechanisms can
be provided by consumers.

Usage
-----

API documentation and usage examples can be displayed using the `go doc`
command or accessed on [pkg.go.dev][1].

[1]: https://pkg.go.dev/go.guido-berhoerster.org/managesieve
     "managesieve documentation"

Build Instructions
------------------

managesieve is a Go module and requires Go version 1.14 or later.  It can be
used by including its canonical name "go.guido-berhoerster.org/managesieve".
See the Go documentation for details.

Contact
-------

Please send any feedback, translations or bug reports via email to
<guido+managesieve@berhoerster.name>.

Bug Reports
-----------

When sending bug reports, please always mention the exact version of the
managesieve package with which the issue occurs as well as the Go
compiler and version and version of the operating system you are using
and make sure that you provide sufficient information to reproduce the issue
and include any input, output, any error messages.

In case of build issues, please also specify the implementations and versions
of the tools used to build the package and/or program, in particular the Go
compiler.

In case of crashes, please attach the full backtrace to the bug report.

License
-------

Except otherwise noted, all files are Copyright (C) 2020 Guido Berhoerster and
distributed under the following license terms:

Copyright (C) 2020 Guido Berhoerster <guido+managesieve@berhoerster.name>

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Documentation

Overview

Package managesieve implements the MANAGESIEVE protocol as specified in RFC 5804. It covers all mandatory parts of the protocol with the exception of the SCRAM-SHA-1 SASL mechanism. Additional SASL authentication mechanisms can be provided by consumers.

Example
package main

import (
	"crypto/tls"
	"fmt"
	"log"

	"go.guido-berhoerster.org/managesieve"
)

func main() {
	host := "mail.example.org"
	username := "foo"
	password := "S3cR3T"
	script := `require "fileinto";

if address :is "from" "foo@example.com" {
    fileinto "INBOX/foo";
}
`
	scriptName := "newscript"

	// Try to look up a SRV record for given domain and fall back to the
	// domain and port 4190.
	var hostport string
	if services, err := managesieve.LookupService(host); err != nil {
		hostport = host + ":4190"
	} else {
		hostport = services[0]
	}

	// Connect to the ManageSieve server.
	c, err := managesieve.Dial(hostport)
	if err != nil {
		log.Fatalf("failed to connect: %s", err)
	}

	// Establish a TLS connection.
	tlsConf := &tls.Config{ServerName: host}
	if err := c.StartTLS(tlsConf); err != nil {
		log.Fatalf("failed to start TLS connection: %s", err)
	}

	// Authenticate the user using the PLAIN SASL mechanism.
	auth := managesieve.PlainAuth("", username, password, host)
	if err := c.Authenticate(auth); err != nil {
		log.Fatalf("failed to authenticate user %s: %s", username, err)
	}

	// Check the validity of the script.
	if warnings, err := c.CheckScript(script); err != nil {
		log.Fatalf("script %q is not valid: %s", scriptName, err)
	} else if warnings != "" {
		log.Printf("warning: %s", warnings)
	}

	// Check whether ther is sufficient space for uploading the script.
	if ok, err := c.HaveSpace(scriptName, int64(len(script))); err != nil {
		log.Fatalf("failed to determine whether there is enough space: %s",
			err)
	} else if !ok {
		log.Fatalf("not enough space to upload script %q", scriptName)
	}

	// Upload the script.
	if warnings, err := c.PutScript(scriptName, script); err != nil {
		log.Fatalf("failed to upload script %q: %s", scriptName, err)
	} else if warnings != "" {
		log.Printf("warning: %s", warnings)
	}

	// Activate the uploaded script
	if err = c.ActivateScript(scriptName); err != nil {
		log.Fatalf("failed to set script %q active: %s", scriptName,
			err)
	}

	// Get a list of the names of all scripts on the server and determine
	// the currently active script.
	scripts, active, err := c.ListScripts()
	if err != nil {
		log.Fatalf("failed to list scripts: %s", err)
	}
	if active != scriptName {
		log.Fatalf("%q is not the active script", scriptName)
	}
	// Download each script from the list.
	for _, name := range scripts {
		if name == active {
			fmt.Printf("%q:\n", name)
		} else {
			fmt.Printf("%q (active):\n", name)
		}

		content, err := c.GetScript(name)
		if err != nil {
			log.Fatalf("failed to get script %q: %s", name, err)
		}
		fmt.Println(content)
	}

	// Rename the script to "temp".
	if err = c.RenameScript(scriptName, "temp"); err != nil {
		log.Fatalf("RENAMESCRIPT failed: %s", err)
	}

	// Delete the script.
	if err = c.DeleteScript("temp"); err != nil {
		log.Fatalf("DELETESCRIPT failed: %s", err)
	}

	// Log out and close the connection.
	if err = c.Logout(); err != nil {
		log.Fatalf("failed to log out: %s", err)
	}
}
Output:

Index

Examples

Constants

View Source
const ReadLimit = 1 * 1024 * 1024 // 1 MiB

Variables

View Source
var (
	// ErrPlainAuthNotSupported is returned if the server does not support
	// the SASL PLAIN authentication mechanism.
	ErrPlainAuthNotSupported = errors.New("the server does not support PLAIN authentication")
	// ErrPlainAuthTLSRequired is returned when the SASL PLAIN
	// authentication mechanism is used without TLS against a server other
	// than localhost.
	ErrPlainAuthTLSRequired = errors.New("PLAIN authentication requires a TLS connection")
)

Functions

func IsNetUnicode

func IsNetUnicode(s string) bool

Checks whtether the given string conforms to the "Unicode Format for Network Interchange" specified in RFC 5198.

func LookupService

func LookupService(domain string) ([]string, error)

Tries to look up the MANAGESIEVE SRV record for the domain and returns an slice of strings containing hostnames and ports. If no SRV record was found it falls back to the given domain name and port 4190.

Types

type Auth

type Auth interface {
	// Initiate SASL authentication.  A non-nil response will be sent in
	// response to an empty challenge from the server if mandated by the
	// authentication mechanism.  The name of the SASL authentication
	// mechanism is returned in mechanism.  If an error is returned SASL
	// authentication will be aborted and an AuthenticationError will be
	// returned to the caller.
	Start(server *ServerInfo) (mechanism string, response []byte, err error)
	// Handle a challenge received from the server, if more is true the
	// server expects a response, otherwise the response should be nil. If
	// an error is returned SASL authentication will be aborted and an
	// AuthenticationError will be returned to the caller.
	Next(challenge []byte, more bool) (response []byte, err error)
}

func PlainAuth

func PlainAuth(identity, username, password, host string) Auth

PlainAuth provides an Auth implementation of SASL PLAIN authentication as specified in RFC 4616 using the provided authorization identity, username and password. If the identity is an empty string the server will derive an identity from the credentials.

type AuthenticationError

type AuthenticationError string

AuthenticationError is returned if an authentication attempt has failed.

func (AuthenticationError) Error

func (e AuthenticationError) Error() string

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client represents a client connection to a MANAGESIEVE server.

func Dial

func Dial(addr string) (*Client, error)

Dial creates a new connection to a MANAGESIEVE server. The given addr must contain both a hostname or IP address and post.

func NewClient

func NewClient(conn net.Conn, host string) (*Client, error)

NewClient create a new client based on an existing connection to a MANAGESIEVE server where host specifies the hostname of the remote end of the connection.

func (*Client) ActivateScript

func (c *Client) ActivateScript(name string) error

ActivateScript activates a script. Only one script can be active at the same time, activating a script will deactivate the previously active script. If the name is the empty string the currently active script will be deactivated.

func (*Client) Authenticate

func (c *Client) Authenticate(a Auth) error

Authenticate authenticates a client using the given authentication mechanism. In case of an AuthenticationError the client remains in a defined state and can continue to be used.

func (*Client) CheckScript

func (c *Client) CheckScript(content string) (warnings string, err error)

CheckScript checks if the given script contains any errors. This operation is only available if the server conforms to RFC 5804.

func (*Client) Close

func (c *Client) Close() error

Close closes the connection to the server immediately without informing the remote end that the client has finished. Under normal circumstances Logout should be used instead.

func (*Client) DeleteScript

func (c *Client) DeleteScript(name string) error

DeleteScript deletes the script with the given name from the server.

func (*Client) Extensions

func (c *Client) Extensions() []string

Extensions returns the Sieve script extensions supported by the Sieve engine.

func (*Client) GetScript

func (c *Client) GetScript(name string) (string, error)

GetScript returns the content of the script with the given name.

func (*Client) HaveSpace

func (c *Client) HaveSpace(name string, size int64) (bool, error)

HaveSpace queries the server if there is sufficient space to store a script with the given name and size. An already existing script with the same name will be treated as if it were replaced with a script of the given size.

func (*Client) Implementation

func (c *Client) Implementation() string

Implementation returns the name and version of the implementation as reported by the server.

func (*Client) ListScripts

func (c *Client) ListScripts() ([]string, string, error)

ListScripts returns the names of all scripts on the server and the name of the currently active script. If there is no active script it returns the empty string.

func (*Client) Logout

func (c *Client) Logout() error

Logout first indicates to the server that the client is finished and subsequently closes the connection. No further commands can be sent after this.

func (*Client) MaxRedirects

func (c *Client) MaxRedirects() int

MaxRedirects returns the limit on the number of Sieve "redirect" during a single evaluation.

func (*Client) Noop

func (c *Client) Noop() error

Noop does nothing but contact the server and can be used to prevent timeouts and to check whether the connection is still alive. This operation is only available if the server conforms to RFC 5804.

func (*Client) NotifyMethods

func (c *Client) NotifyMethods() []string

NotifyMethods returns the URI schema parts for supported notification methods.

func (*Client) PutScript

func (c *Client) PutScript(name, content string) (warnings string, err error)

PutScript stores the script content with the given name on the server. An already existing script with the same name will be replaced.

func (*Client) RenameScript

func (c *Client) RenameScript(oldName, newName string) error

RenameScript renames a script on the server. This operation is only available if the server conforms to RFC 5804.

func (*Client) SASLMechanisms

func (c *Client) SASLMechanisms() []string

SASLMechanisms returns the SASL authentication mechanism supported by the server. This may change depending on whether a TLS connection is used.

func (*Client) StartTLS

func (c *Client) StartTLS(config *tls.Config) error

StartTLS upgrades the connection to use TLS encryption based on the given configuration.

func (*Client) SupportsRFC5804

func (c *Client) SupportsRFC5804() bool

SupportsRFC5804 returns true if the server conforms to RFC 5804.

func (*Client) SupportsTLS

func (c *Client) SupportsTLS() bool

SupportsTLS returns true if the server supports TLS connections via the STARTTLS command.

func (*Client) TLSConnectionState

func (c *Client) TLSConnectionState() (state tls.ConnectionState, ok bool)

TLSConnectionState returns the ConnectionState of the current TLS connection.

type ConnClosedError

type ConnClosedError struct {
	Code string
	Msg  string
}

The ConnClosedError is returned if the server has closed the connection.

func (*ConnClosedError) Error

func (e *ConnClosedError) Error() string

type HostNameVerificationError

type HostNameVerificationError struct {
	ExpectedHost, ActualHost string
}

HostNameVerificationError is returned when the hostname which was passed to the Auth implementation could not be verified against the TLS certificate.

func (*HostNameVerificationError) Error

func (e *HostNameVerificationError) Error() string

type NotSupportedError

type NotSupportedError string

NotSupportedError is returned if an operation requires an extension that is not available.

func (NotSupportedError) Error

func (e NotSupportedError) Error() string

type ParserError

type ParserError string

ParserError represents a syntax error encountered while parsing a response from the server.

func (ParserError) Error

func (e ParserError) Error() string

type ProtocolError

type ProtocolError string

ProtocolError represents a MANAGESIEVE protocol violation.

func (ProtocolError) Error

func (e ProtocolError) Error() string

type ServerError

type ServerError struct {
	Code string // optional response code of the error
	Msg  string // optional human readable error message
}

A ServerError is represents an error returned by the server in the form of a NO response.

func (*ServerError) Error

func (e *ServerError) Error() string

type ServerInfo

type ServerInfo struct {
	Name string   // hostname of the server
	TLS  bool     // whether a verified TLS connection is used
	Auth []string // authentication methods advertised in capabilities
}

ServerInfo stores information about the ManageSieve server.

func (*ServerInfo) HaveAuth

func (s *ServerInfo) HaveAuth(wanted string) bool

Check whether the server supports the wanted SASL authentication mechanism.