From mboxrd@z Thu Jan 1 00:00:00 1970 From: Andy Wingo Subject: [PATCH 2/2] gnu: Add dovecot service Date: Mon, 14 Dec 2015 22:09:48 +0100 Message-ID: <1450127388-8654-2-git-send-email-wingo@igalia.com> References: <1450127388-8654-1-git-send-email-wingo@igalia.com> Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Return-path: Received: from eggs.gnu.org ([2001:4830:134:3::10]:34311) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1a8aOR-00054j-Iw for guix-devel@gnu.org; Mon, 14 Dec 2015 16:11:02 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1a8aOG-0001sH-Aj for guix-devel@gnu.org; Mon, 14 Dec 2015 16:10:51 -0500 Received: from pb-sasl0.int.icgroup.com ([208.72.237.25]:50063 helo=sasl.smtp.pobox.com) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1a8aOF-0001hL-RZ for guix-devel@gnu.org; Mon, 14 Dec 2015 16:10:40 -0500 Received: from sasl.smtp.pobox.com (unknown [127.0.0.1]) by pb-sasl0.pobox.com (Postfix) with ESMTP id BE6BE31545 for ; Mon, 14 Dec 2015 16:10:04 -0500 (EST) Received: from pb-sasl0.int.icgroup.com (unknown [127.0.0.1]) by pb-sasl0.pobox.com (Postfix) with ESMTP id AA33A31542 for ; Mon, 14 Dec 2015 16:10:04 -0500 (EST) Received: from clucks (unknown [88.160.190.192]) (using TLSv1 with cipher ECDHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by pb-sasl0.pobox.com (Postfix) with ESMTPSA id 72DCF3153B for ; Mon, 14 Dec 2015 16:09:59 -0500 (EST) In-Reply-To: <1450127388-8654-1-git-send-email-wingo@igalia.com> List-Id: "Development of GNU Guix and the GNU System distribution." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-devel-bounces+gcggd-guix-devel=m.gmane.org@gnu.org Sender: guix-devel-bounces+gcggd-guix-devel=m.gmane.org@gnu.org To: guix-devel@gnu.org * gnu/services/mail.scm: New file. (dovecot-service, dovecot-configuration, dict-configuration) (passdb-configuration, userdb-configuration) (unix-listener-configuration, fifo-listener-configuration) (inet-listener-configuration, service-configuration) (protocol-configuration, plugin-configuration, mailbox-configuration) (namespace-configuration): New public variables. * gnu-system.am (GNU_SYSTEM_MODULES): Add (gnu services mail). * doc/guix.texi (Mail Services): New node. --- doc/guix.texi | 1338 +++++++++++++++++++++++++++++++++++++++ gnu-system.am | 1 + gnu/services/mail.scm | 1650 +++++++++++++++++++++++++++++++++++++++++++= ++++++ 3 files changed, 2989 insertions(+) create mode 100644 gnu/services/mail.scm diff --git a/doc/guix.texi b/doc/guix.texi index f7adfaf..6f327b0 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -181,6 +181,7 @@ Services * X Window:: Graphical display. * Desktop Services:: D-Bus and desktop services. * Database Services:: SQL databases. +* Mail Services:: IMAP, POP3, SMTP, and all that. * Web Services:: Web servers. * Various Services:: Other services. =20 @@ -6311,6 +6312,7 @@ declaration. * X Window:: Graphical display. * Desktop Services:: D-Bus and desktop services. * Database Services:: SQL databases. +* Mail Services:: IMAP, POP3, SMTP, and all that. * Web Services:: Web servers. * Various Services:: Other services. @end menu @@ -7034,6 +7036,1342 @@ The PostgreSQL daemon loads its runtime configur= ation from @var{data-directory}. @end deffn =20 +@node Mail Services +@subsubsection Mail Services + +The @code{(gnu services mail)} module provides Guix service definitions +for mail services. Currently the only implemented service is Dovecot, +an IMAP, POP3, and LMTP server. + +Guix does not yet have a mail transfer agent (MTA), although for some +lightweight purposes the @code{esmtp} relay-only MTA may suffice. Help +is needed to properly integrate a full MTA, such as Postfix. Patches +welcome! + +To add IMAP support to a GuixSD system, add a @code{dovecot-service} to +the operating system definition: + +@deffn {Scheme Procedure} dovecot-service [#:config (dovecot-configurati= on)] +Return a service that runs the Dovecot IMAP/POP3/LMTP mail server. +@end deffn + +By default, Dovecot doesn't need much configuration; the default +configuration object created by @code{(dovecot-configuration)} will +suffice if your mail is delivered to @code{~/Maildir}. A self-signed +certificate will be generated for TLS-protected connections, though +Dovecot will also listen on cleartext ports by default. There are a +number of options though which mail administrators might need to change, +and as is the case with other services, Guix allows the system +administrator to specify these parameters via a uniform Scheme interface= . + +For example, to specify that mail is located at @code{maildir~/.mail}, +one would instantiate the Dovecot service like this: + +@example +(dovecot-service #:config + (dovecot-configuration + (mail-location "maildir:~/.mail"))) +@end example + +The available configuration parameters follow. Each parameter +definition is preceded by its type; for example, @samp{string-list foo} +indicates that the @code{foo} parameter should be specified as a list of +strings. + +@c The following documentation was initially generated by +@c (generate-documentation) in (gnu services mail). Manually maintained +@c documentation is better, so we shouldn't hesitate to edit below as +@c needed. However if the change you want to make to this documentation +@c can be done in an automated way, it's probably easier to change +@c (generate-documentation) than to make it below and have to deal with +@c the churn as dovecot updates. + +Available @code{dovecot-configuration} fields are: + +@deftypevr {@code{dovecot-configuration} parameter} package dovecot +The dovecot package. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} comma-separated-stri= ng-list listen +A list of IPs or hosts where to listen in for connections. @samp{*} +listens in all IPv4 interfaces, @samp{::} listens in all IPv6 +interfaces. If you want to specify non-default ports or anything more +complex, customize the address and port fields of the +@samp{inet-listener} of the specific services you are interested in. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} protocol-configurati= on-list protocols +List of protocols we want to serve. Available protocols include +@samp{imap}, @samp{pop3}, and @samp{lmtp}. + +Available @code{protocol-configuration} fields are: + +@deftypevr {@code{protocol-configuration} parameter} string name +The name of the protocol. +@end deftypevr + +@deftypevr {@code{protocol-configuration} parameter} string auth-socket-= path +UNIX socket path to master authentication server to find users. +This is used by imap (for shared users) and lda. +Defaults to @samp{"/var/run/dovecot/auth-userdb"}. +@end deftypevr + +@deftypevr {@code{protocol-configuration} parameter} space-separated-str= ing-list mail-plugins +Space separated list of plugins to load. +@end deftypevr + +@deftypevr {@code{protocol-configuration} parameter} non-negative-intege= r mail-max-userip-connections +Maximum number of IMAP connections allowed for a user from each IP +address. NOTE: The username is compared case-sensitively. +Defaults to @samp{10}. +@end deftypevr + +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} service-configuratio= n-list services +List of services to enable. Available services include @samp{imap}, +@samp{imap-login}, @samp{pop3}, @samp{pop3-login}, @samp{auth}, and +@samp{lmtp}. + +Available @code{service-configuration} fields are: + +@deftypevr {@code{service-configuration} parameter} string kind +The service kind. Valid values include @code{director}, +@code{imap-login}, @code{pop3-login}, @code{lmtp}, @code{imap}, +@code{pop3}, @code{auth}, @code{auth-worker}, @code{dict}, +@code{tcpwrap}, @code{quota-warning}, or anything else. +@end deftypevr + +@deftypevr {@code{service-configuration} parameter} listener-configurati= on-list listeners +Listeners for the service. A listener is either an +@code{unix-listener-configuration}, a @code{fifo-listener-configuration}= , or +an @code{inet-listener-configuration}. +Defaults to @samp{()}. + +Available @code{unix-listener-configuration} fields are: + +@deftypevr {@code{unix-listener-configuration} parameter} file-name path +The file name on which to listen. +@end deftypevr + +@deftypevr {@code{unix-listener-configuration} parameter} string mode +The access mode for the socket. +Defaults to @samp{"0600"}. +@end deftypevr + +@deftypevr {@code{unix-listener-configuration} parameter} string user +The user to own the the socket. +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{unix-listener-configuration} parameter} string group +The group to own the socket. +Defaults to @samp{""}. +@end deftypevr + + +Available @code{fifo-listener-configuration} fields are: + +@deftypevr {@code{fifo-listener-configuration} parameter} file-name path +The file name on which to listen. +@end deftypevr + +@deftypevr {@code{fifo-listener-configuration} parameter} string mode +The access mode for the socket. +Defaults to @samp{"0600"}. +@end deftypevr + +@deftypevr {@code{fifo-listener-configuration} parameter} string user +The user to own the the socket. +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{fifo-listener-configuration} parameter} string group +The group to own the socket. +Defaults to @samp{""}. +@end deftypevr + + +Available @code{inet-listener-configuration} fields are: + +@deftypevr {@code{inet-listener-configuration} parameter} string protoco= l +The protocol to listen for. +@end deftypevr + +@deftypevr {@code{inet-listener-configuration} parameter} string address +The address on which to listen, or empty for all addresses. +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{inet-listener-configuration} parameter} non-negative-i= nteger port +The port on which to listen. +@end deftypevr + +@deftypevr {@code{inet-listener-configuration} parameter} boolean ssl? +Whether to use SSL for this service; @samp{yes}, @samp{no}, or +@samp{required}. +Defaults to @samp{#t}. +@end deftypevr + +@end deftypevr + +@deftypevr {@code{service-configuration} parameter} non-negative-integer= service-count +Number of connections to handle before starting a new process. +Typically the only useful values are 0 (unlimited) or 1. 1 is more +secure, but 0 is faster. . +Defaults to @samp{1}. +@end deftypevr + +@deftypevr {@code{service-configuration} parameter} non-negative-integer= process-min-avail +Number of processes to always keep waiting for more connections. +Defaults to @samp{0}. +@end deftypevr + +@deftypevr {@code{service-configuration} parameter} non-negative-integer= vsz-limit +If you set @samp{service-count 0}, you probably need to grow +this. +Defaults to @samp{256000000}. +@end deftypevr + +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} dict-configuration d= ict +Dict configuration, as created by the @code{dict-configuration} +constructor. + +Available @code{dict-configuration} fields are: + +@deftypevr {@code{dict-configuration} parameter} free-form-fields entrie= s +A list of key-value pairs that this dict should hold. +Defaults to @samp{()}. +@end deftypevr + +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} passdb-configuration= -list passdbs +List of passdb configurations, each one created by the +@code{passdb-configuration} constructor. + +Available @code{passdb-configuration} fields are: + +@deftypevr {@code{passdb-configuration} parameter} string driver +The driver that the passdb should use. Valid values include +@samp{pam}, @samp{passwd}, @samp{shadow}, @samp{bsdauth}, and +@samp{static}. +Defaults to @samp{"pam"}. +@end deftypevr + +@deftypevr {@code{passdb-configuration} parameter} free-form-args args +A list of key-value args to the passdb driver. +Defaults to @samp{()}. +@end deftypevr + +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} userdb-configuration= -list userdbs +List of userdb configurations, each one created by the +@code{userdb-configuration} constructor. + +Available @code{userdb-configuration} fields are: + +@deftypevr {@code{userdb-configuration} parameter} string driver +The driver that the userdb should use. Valid values include +@samp{passwd} and @samp{static}. +Defaults to @samp{"passwd"}. +@end deftypevr + +@deftypevr {@code{userdb-configuration} parameter} free-form-args args +A list of key-value args to the userdb driver. +Defaults to @samp{()}. +@end deftypevr + +@deftypevr {@code{userdb-configuration} parameter} free-form-args overri= de-fields +Override fields from passwd. +Defaults to @samp{()}. +@end deftypevr + +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} plugin-configuration= plugin-configuration +Plug-in configuration, created by the @code{plugin-configuration} +constructor. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} list-of-namespace-co= nfiguration namespaces +List of namespaces. Each item in the list is created by the +@code{namespace-configuration} constructor. + +Available @code{namespace-configuration} fields are: + +@deftypevr {@code{namespace-configuration} parameter} string name +Name for this namespace. +@end deftypevr + +@deftypevr {@code{namespace-configuration} parameter} string type +Namespace type: @samp{private}, @samp{shared} or @samp{public}. +Defaults to @samp{"private"}. +@end deftypevr + +@deftypevr {@code{namespace-configuration} parameter} string separator +Hierarchy separator to use. You should use the same separator for +all namespaces or some clients get confused. @samp{/} is usually a good +one. The default however depends on the underlying mail storage +format. +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{namespace-configuration} parameter} string prefix +Prefix required to access this namespace. This needs to be +different for all namespaces. For example @samp{Public/}. +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{namespace-configuration} parameter} string location +Physical location of the mailbox. This is in same format as +mail_location, which is also the default for it. +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{namespace-configuration} parameter} boolean inbox? +There can be only one INBOX, and this setting defines which +namespace has it. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{namespace-configuration} parameter} boolean hidden? +If namespace is hidden, it's not advertised to clients via NAMESPACE +extension. You'll most likely also want to set @samp{list? #f}. This is= mostly +useful when converting from another server with different namespaces +which you want to deprecate but still keep working. For example you can +create hidden namespaces with prefixes @samp{~/mail/}, @samp{~%u/mail/} +and @samp{mail/}. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{namespace-configuration} parameter} boolean list? +Show the mailboxes under this namespace with LIST command. This +makes the namespace visible for clients that don't support NAMESPACE +extension. The special @code{children} value lists child mailboxes, but +hides the namespace prefix. +Defaults to @samp{#t}. +@end deftypevr + +@deftypevr {@code{namespace-configuration} parameter} boolean subscripti= ons? +Namespace handles its own subscriptions. If set to @code{#f}, the +parent namespace handles them. The empty prefix should always have this +as @code{#t}.) +Defaults to @samp{#t}. +@end deftypevr + +@deftypevr {@code{namespace-configuration} parameter} mailbox-configurat= ion-list mailboxes +List of predefined mailboxes in this namespace. +Defaults to @samp{()}. + +Available @code{mailbox-configuration} fields are: + +@deftypevr {@code{mailbox-configuration} parameter} string name +Name for this mailbox. +@end deftypevr + +@deftypevr {@code{mailbox-configuration} parameter} string auto +@samp{create} will automatically create this mailbox. +@samp{subscribe} will both create and subscribe to the mailbox. +Defaults to @samp{"no"}. +@end deftypevr + +@deftypevr {@code{mailbox-configuration} parameter} space-separated-stri= ng-list special-use +List of IMAP @code{SPECIAL-USE} attributes as specified by RFC 6154. +Valid values are @code{\All}, @code{\Archive}, @code{\Drafts}, +@code{\Flagged}, @code{\Junk}, @code{\Sent}, and @code{\Trash}. +Defaults to @samp{()}. +@end deftypevr + +@end deftypevr + +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} file-name base-dir +Base directory where to store runtime data. +Defaults to @samp{"/var/run/dovecot/"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string login-greetin= g +Greeting message for clients. +Defaults to @samp{"Dovecot ready."}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} space-separated-stri= ng-list login-trusted-networks +List of trusted network ranges. Connections from these IPs are +allowed to override their IP addresses and ports (for logging and for +authentication checks). @samp{disable-plaintext-auth} is also ignored +for these networks. Typically you'd specify your IMAP proxy servers +here. +Defaults to @samp{()}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} space-separated-stri= ng-list login-access-sockets +List of login access check sockets (e.g. tcpwrap). +Defaults to @samp{()}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean verbose-proc= title? +Show more verbose process titles (in ps). Currently shows user name +and IP address. Useful for seeing who are actually using the IMAP +processes (e.g. shared mailboxes or if same uid is used for multiple +accounts). +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean shutdown-cli= ents? +Should all processes be killed when Dovecot master process shuts down. +Setting this to @code{#f} means that Dovecot can be upgraded without +forcing existing client connections to close (although that could also +be a problem if the upgrade is e.g. because of a security fix). +Defaults to @samp{#t}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} non-negative-integer= doveadm-worker-count +If non-zero, run mail commands via this many connections to doveadm +server, instead of running them directly in the same process. +Defaults to @samp{0}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string doveadm-socke= t-path +UNIX socket or host:port used for connecting to doveadm server. +Defaults to @samp{"doveadm-server"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} space-separated-stri= ng-list import-environment +List of environment variables that are preserved on Dovecot startup +and passed down to all of its child processes. You can also give +key=3Dvalue pairs to always set specific settings. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean disable-plai= ntext-auth? +Disable LOGIN command and all other plaintext authentications unless +SSL/TLS is used (LOGINDISABLED capability). Note that if the remote IP +matches the local IP (i.e. you're connecting from the same computer), +the connection is considered secure and plaintext authentication is +allowed. See also ssl=3Drequired setting. +Defaults to @samp{#t}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} non-negative-integer= auth-cache-size +Authentication cache size (e.g. @samp{#e10e6}). 0 means it's disabled. +Note that bsdauth, PAM and vpopmail require @samp{cache-key} to be set +for caching to be used. +Defaults to @samp{0}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string auth-cache-tt= l +Time to live for cached data. After TTL expires the cached record +is no longer used, *except* if the main database lookup returns internal +failure. We also try to handle password changes automatically: If +user's previous authentication was successful, but this one wasn't, the +cache isn't used. For now this works only with plaintext +authentication. +Defaults to @samp{"1 hour"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string auth-cache-ne= gative-ttl +TTL for negative hits (user not found, password mismatch). +0 disables caching them completely. +Defaults to @samp{"1 hour"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} space-separated-stri= ng-list auth-realms +List of realms for SASL authentication mechanisms that need them. +You can leave it empty if you don't want to support multiple realms. +Many clients simply use the first one listed here, so keep the default +realm first. +Defaults to @samp{()}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string auth-default-= realm +Default realm/domain to use if none was specified. This is used for +both SASL realms and appending @@domain to username in plaintext +logins. +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string auth-username= -chars +List of allowed characters in username. If the user-given username +contains a character not listed in here, the login automatically fails. +This is just an extra check to make sure user can't exploit any +potential quote escaping vulnerabilities with SQL/LDAP databases. If +you want to allow all characters, set this value to empty. +Defaults to @samp{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0= 1234567890.-_@@"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string auth-username= -translation +Username character translations before it's looked up from +databases. The value contains series of from -> to characters. For +example @samp{#@@/@@} means that @samp{#} and @samp{/} characters are +translated to @samp{@@}. +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string auth-username= -format +Username formatting before it's looked up from databases. You can +use the standard variables here, e.g. %Lu would lowercase the username, +%n would drop away the domain if it was given, or @samp{%n-AT-%d} would +change the @samp{@@} into @samp{-AT-}. This translation is done after +@samp{auth-username-translation} changes. +Defaults to @samp{"%Lu"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string auth-master-u= ser-separator +If you want to allow master users to log in by specifying the master +username within the normal username string (i.e. not using SASL +mechanism's support for it), you can specify the separator character +here. The format is then . +UW-IMAP uses @samp{*} as the separator, so that could be a good +choice. +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string auth-anonymou= s-username +Username to use for users logging in with ANONYMOUS SASL +mechanism. +Defaults to @samp{"anonymous"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} non-negative-integer= auth-worker-max-count +Maximum number of dovecot-auth worker processes. They're used to +execute blocking passdb and userdb queries (e.g. MySQL and PAM). +They're automatically created and destroyed as needed. +Defaults to @samp{30}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string auth-gssapi-h= ostname +Host name to use in GSSAPI principal names. The default is to use +the name returned by gethostname(). Use @samp{$ALL} (with quotes) to +allow all keytab entries. +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string auth-krb5-key= tab +Kerberos keytab to use for the GSSAPI mechanism. Will use the +system default (usually /etc/krb5.keytab) if not specified. You may +need to change the auth service to run as root to be able to read this +file. +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean auth-use-win= bind? +Do NTLM and GSS-SPNEGO authentication using Samba's winbind daemon +and @samp{ntlm-auth} helper. +. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} file-name auth-winbi= nd-helper-path +Path for Samba's @samp{ntlm-auth} helper binary. +Defaults to @samp{"/usr/bin/ntlm_auth"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string auth-failure-= delay +Time to delay before replying to failed authentications. +Defaults to @samp{"2 secs"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean auth-ssl-req= uire-client-cert? +Require a valid SSL client certificate or the authentication +fails. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean auth-ssl-use= rname-from-cert? +Take the username from client's SSL certificate, using +@code{X509_NAME_get_text_by_NID()} which returns the subject's DN's +CommonName. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} space-separated-stri= ng-list auth-mechanisms +List of wanted authentication mechanisms. Supported mechanisms are: +@samp{plain}, @samp{login}, @samp{digest-md5}, @samp{cram-md5}, +@samp{ntlm}, @samp{rpa}, @samp{apop}, @samp{anonymous}, @samp{gssapi}, +@samp{otp}, @samp{skey}, and @samp{gss-spnego}. NOTE: See also +@samp{disable-plaintext-auth} setting. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} space-separated-stri= ng-list director-servers +List of IPs or hostnames to all director servers, including ourself. +Ports can be specified as ip:port. The default port is the same as what +director service's @samp{inet-listener} is using. +Defaults to @samp{()}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} space-separated-stri= ng-list director-mail-servers +List of IPs or hostnames to all backend mail servers. Ranges are +allowed too, like 10.0.0.10-10.0.0.30. +Defaults to @samp{()}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string director-user= -expire +How long to redirect users to a specific server after it no longer +has any connections. +Defaults to @samp{"15 min"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} non-negative-integer= director-doveadm-port +TCP/IP port that accepts doveadm connections (instead of director +connections) If you enable this, you'll also need to add +@samp{inet-listener} for the port. +Defaults to @samp{0}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string director-user= name-hash +How the username is translated before being hashed. Useful values +include %Ln if user can log in with or without @@domain, %Ld if mailboxe= s +are shared within domain. +Defaults to @samp{"%Lu"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string log-path +Log file to use for error messages. @samp{syslog} logs to syslog, +@samp{/dev/stderr} logs to stderr. +Defaults to @samp{"syslog"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string info-log-path +Log file to use for informational messages. Defaults to +@samp{log-path}. +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string debug-log-pat= h +Log file to use for debug messages. Defaults to +@samp{info-log-path}. +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string syslog-facili= ty +Syslog facility to use if you're logging to syslog. Usually if you +don't want to use @samp{mail}, you'll use local0..local7. Also other +standard facilities are supported. +Defaults to @samp{"mail"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean auth-verbose= ? +Log unsuccessful authentication attempts and the reasons why they +failed. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean auth-verbose= -passwords? +In case of password mismatches, log the attempted password. Valid +values are no, plain and sha1. sha1 can be useful for detecting brute +force password attempts vs. user simply trying the same password over +and over again. You can also truncate the value to n chars by appending +":n" (e.g. sha1:6). +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean auth-debug? +Even more verbose logging for debugging purposes. Shows for example +SQL queries. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean auth-debug-p= asswords? +In case of password mismatches, log the passwords and used scheme so +the problem can be debugged. Enabling this also enables +@samp{auth-debug}. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean mail-debug? +Enable mail process debugging. This can help you figure out why +Dovecot isn't finding your mails. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean verbose-ssl? +Show protocol level SSL errors. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string log-timestamp +Prefix for each line written to log file. % codes are in +strftime(3) format. +Defaults to @samp{"\"%b %d %H:%M:%S \""}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} space-separated-stri= ng-list login-log-format-elements +List of elements we want to log. The elements which have a +non-empty variable value are joined together to form a comma-separated +string. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string login-log-for= mat +Login log format. %s contains @samp{login-log-format-elements} +string, %$ contains the data we want to log. +Defaults to @samp{"%$: %s"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string mail-log-pref= ix +Log prefix for mail processes. See doc/wiki/Variables.txt for list +of possible variables you can use. +Defaults to @samp{"\"%s(%u): \""}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string deliver-log-f= ormat +Format to use for logging mail deliveries. You can use variables: +@table @code +@item %$ +Delivery status message (e.g. @samp{saved to INBOX}) +@item %m +Message-ID +@item %s +Subject +@item %f +From address +@item %p +Physical size +@item %w +Virtual size. +@end table +Defaults to @samp{"msgid=3D%m: %$"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string mail-location +Location for users' mailboxes. The default is empty, which means +that Dovecot tries to find the mailboxes automatically. This won't work +if the user doesn't yet have any mail, so you should explicitly tell +Dovecot the full location. + +If you're using mbox, giving a path to the INBOX +file (e.g. /var/mail/%u) isn't enough. You'll also need to tell Dovecot +where the other mailboxes are kept. This is called the "root mail +directory", and it must be the first path given in the +@samp{mail-location} setting. + +There are a few special variables you can use, eg.: + +@table @samp +@item %u +username +@item %n +user part in user@@domain, same as %u if there's no domain +@item %d +domain part in user@@domain, empty if there's no domain +@item %h +home director +@end table + +See doc/wiki/Variables.txt for full list. Some examples: +@table @samp +@item maildir:~/Maildir +@item mbox:~/mail:INBOX=3D/var/mail/%u +@item mbox:/var/mail/%d/%1n/%n:INDEX=3D/var/indexes/%d/%1n/% +@end table +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string mail-uid +System user and group used to access mails. If you use multiple, +userdb can override these by returning uid or gid fields. You can use +either numbers or names. . +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string mail-gid + +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string mail-privileg= ed-group +Group to enable temporarily for privileged operations. Currently +this is used only with INBOX when either its initial creation or +dotlocking fails. Typically this is set to "mail" to give access to +/var/mail. +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string mail-access-g= roups +Grant access to these supplementary groups for mail processes. +Typically these are used to set up access to shared mailboxes. Note +that it may be dangerous to set these if users can create +symlinks (e.g. if "mail" group is set here, ln -s /var/mail ~/mail/var +could allow a user to delete others' mailboxes, or ln -s +/secret/shared/box ~/mail/mybox would allow reading it). +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean mail-full-fi= lesystem-access? +Allow full filesystem access to clients. There's no access checks +other than what the operating system does for the active UID/GID. It +works with both maildir and mboxes, allowing you to prefix mailboxes +names with e.g. /path/ or ~user/. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean mmap-disable= ? +Don't use mmap() at all. This is required if you store indexes to +shared filesystems (NFS or clustered filesystem). +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean dotlock-use-= excl? +Rely on @samp{O_EXCL} to work when creating dotlock files. NFS +supports @samp{O_EXCL} since version 3, so this should be safe to use +nowadays by default. +Defaults to @samp{#t}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string mail-fsync +When to use fsync() or fdatasync() calls: +@table @code +@item optimized +Whenever necessary to avoid losing important data +@item always +Useful with e.g. NFS when write()s are delayed +@item never +Never use it (best performance, but crashes can lose data). +@end table +Defaults to @samp{"optimized"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean mail-nfs-sto= rage? +Mail storage exists in NFS. Set this to yes to make Dovecot flush +NFS caches whenever needed. If you're using only a single mail server +this isn't needed. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean mail-nfs-ind= ex? +Mail index files also exist in NFS. Setting this to yes requires +@samp{mmap-disable? #t} and @samp{fsync-disable? #f}. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string lock-method +Locking method for index files. Alternatives are fcntl, flock and +dotlock. Dotlocking uses some tricks which may create more disk I/O +than other locking methods. NFS users: flock doesn't work, remember to +change @samp{mmap-disable}. +Defaults to @samp{"fcntl"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} file-name mail-temp-= dir +Directory in which LDA/LMTP temporarily stores incoming mails >128 +kB. +Defaults to @samp{"/tmp"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} non-negative-integer= first-valid-uid +Valid UID range for users. This is mostly to make sure that users can't +log in as daemons or other system users. Note that denying root logins = is +hardcoded to dovecot binary and can't be done even if @samp{first-valid-= uid} +is set to 0. +Defaults to @samp{500}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} non-negative-integer= last-valid-uid + +Defaults to @samp{0}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} non-negative-integer= first-valid-gid +Valid GID range for users. Users having non-valid GID as primary group = ID +aren't allowed to log in. If user belongs to supplementary groups with +non-valid GIDs, those groups are not set. +Defaults to @samp{1}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} non-negative-integer= last-valid-gid + +Defaults to @samp{0}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} non-negative-integer= mail-max-keyword-length +Maximum allowed length for mail keyword name. It's only forced when +trying to create new keywords. +Defaults to @samp{50}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} colon-separated-file= -name-list valid-chroot-dirs +List of directories under which chrooting is allowed for mail +processes (i.e. /var/mail will allow chrooting to /var/mail/foo/bar +too). This setting doesn't affect @samp{login-chroot} +@samp{mail-chroot} or auth chroot settings. If this setting is empty, +"/./" in home dirs are ignored. WARNING: Never add directories here +which local users can modify, that may lead to root exploit. Usually +this should be done only if you don't allow shell access for users. +. +Defaults to @samp{()}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string mail-chroot +Default chroot directory for mail processes. This can be overridden +for specific users in user database by giving /./ in user's home +directory (e.g. /home/./user chroots into /home). Note that usually +there is no real need to do chrooting, Dovecot doesn't allow users to +access files outside their mail directory anyway. If your home +directories are prefixed with the chroot directory, append "/." to +@samp{mail-chroot}. . +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} file-name auth-socke= t-path +UNIX socket path to master authentication server to find users. +This is used by imap (for shared users) and lda. +Defaults to @samp{"/var/run/dovecot/auth-userdb"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} file-name mail-plugi= n-dir +Directory where to look up mail plugins. +Defaults to @samp{"/usr/lib/dovecot"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} space-separated-stri= ng-list mail-plugins +List of plugins to load for all services. Plugins specific to IMAP, +LDA, etc. are added to this list in their own .conf files. +Defaults to @samp{()}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} non-negative-integer= mail-cache-min-mail-count +The minimum number of mails in a mailbox before updates are done to +cache file. This allows optimizing Dovecot's behavior to do less disk +writes at the cost of more disk reads. +Defaults to @samp{0}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string mailbox-idle-= check-interval +When IDLE command is running, mailbox is checked once in a while to +see if there are any new mails or other changes. This setting defines +the minimum time to wait between those checks. Dovecot can also use +dnotify, inotify and kqueue to find out immediately when changes +occur. +Defaults to @samp{"30 secs"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean mail-save-cr= lf? +Save mails with CR+LF instead of plain LF. This makes sending those +mails take less CPU, especially with sendfile() syscall with Linux and +FreeBSD. But it also creates a bit more disk I/O which may just make it +slower. Also note that if other software reads the mboxes/maildirs, +they may handle the extra CRs wrong and cause problems. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean maildir-stat= -dirs? +By default LIST command returns all entries in maildir beginning +with a dot. Enabling this option makes Dovecot return only entries +which are directories. This is done by stat()ing each entry, so it +causes more disk I/O. + (For systems setting struct @samp{dirent->d_type} this check is free +and it's done always regardless of this setting). +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean maildir-copy= -with-hardlinks? +When copying a message, do it with hard links whenever possible. +This makes the performance much better, and it's unlikely to have any +side effects. +Defaults to @samp{#t}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean maildir-very= -dirty-syncs? +Assume Dovecot is the only MUA accessing Maildir: Scan cur/ +directory only when its mtime changes unexpectedly or when we can't find +the mail otherwise. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} space-separated-stri= ng-list mbox-read-locks +Which locking methods to use for locking mbox. There are four +available: + +@table @code +@item dotlock +Create .lock file. This is the oldest and most NFS-safe +solution. If you want to use /var/mail/ like directory, the users will +need write access to that directory. +@item dotlock-try +Same as dotlock, but if it fails because of permissions or because there +isn't enough disk space, just skip it. +@item fcntl +Use this if possible. Works with NFS too if lockd is used. +@item flock +May not exist in all systems. Doesn't work with NFS.=20 +@item lockf +May not exist in all systems. Doesn't work with NFS. +@end table + +You can use multiple locking methods; if you do the order they're declar= ed +in is important to avoid deadlocks if other MTAs/MUAs are using multiple +locking methods as well. Some operating systems don't allow using some = of +them simultaneously. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} space-separated-stri= ng-list mbox-write-locks + +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string mbox-lock-tim= eout +Maximum time to wait for lock (all of them) before aborting. +Defaults to @samp{"5 mins"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string mbox-dotlock-= change-timeout +If dotlock exists but the mailbox isn't modified in any way, +override the lock file after this much time. +Defaults to @samp{"2 mins"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean mbox-dirty-s= yncs? +When mbox changes unexpectedly we have to fully read it to find out +what changed. If the mbox is large this can take a long time. Since +the change is usually just a newly appended mail, it'd be faster to +simply read the new mails. If this setting is enabled, Dovecot does +this but still safely fallbacks to re-reading the whole mbox file +whenever something in mbox isn't how it's expected to be. The only real +downside to this setting is that if some other MUA changes message +flags, Dovecot doesn't notice it immediately. Note that a full sync is +done with SELECT, EXAMINE, EXPUNGE and CHECK commands. +Defaults to @samp{#t}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean mbox-very-di= rty-syncs? +Like @samp{mbox-dirty-syncs}, but don't do full syncs even with SELECT, +EXAMINE, EXPUNGE or CHECK commands. If this is set, +@samp{mbox-dirty-syncs} is ignored. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean mbox-lazy-wr= ites? +Delay writing mbox headers until doing a full write sync (EXPUNGE +and CHECK commands and when closing the mailbox). This is especially +useful for POP3 where clients often delete all mails. The downside is +that our changes aren't immediately visible to other MUAs. +Defaults to @samp{#t}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} non-negative-integer= mbox-min-index-size +If mbox size is smaller than this (e.g. 100k), don't write index +files. If an index file already exists it's still read, just not +updated. +Defaults to @samp{0}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} non-negative-integer= mdbox-rotate-size +Maximum dbox file size until it's rotated. +Defaults to @samp{2000000}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string mdbox-rotate-= interval +Maximum dbox file age until it's rotated. Typically in days. Day +begins from midnight, so 1d =3D today, 2d =3D yesterday, etc. 0 =3D che= ck +disabled. +Defaults to @samp{"1d"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean mdbox-preall= ocate-space? +When creating new mdbox files, immediately preallocate their size to +@samp{mdbox-rotate-size}. This setting currently works only in Linux +with some filesystems (ext4, xfs). +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string mail-attachme= nt-dir +sdbox and mdbox support saving mail attachments to external files, +which also allows single instance storage for them. Other backends +don't support this for now. + +WARNING: This feature hasn't been tested much yet. Use at your own risk= . + +Directory root where to store mail attachments. Disabled, if empty. +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} non-negative-integer= mail-attachment-min-size +Attachments smaller than this aren't saved externally. It's also +possible to write a plugin to disable saving specific attachments +externally. +Defaults to @samp{128000}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string mail-attachme= nt-fs +Filesystem backend to use for saving attachments: +@table @code +@item posix +No SiS done by Dovecot (but this might help FS's own deduplication) +@item sis posix +SiS with immediate byte-by-byte comparison during saving +@item sis-queue posix +SiS with delayed comparison and deduplication. +@end table +Defaults to @samp{"sis posix"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string mail-attachme= nt-hash +Hash format to use in attachment filenames. You can add any text and +variables: @code{%@{md4@}}, @code{%@{md5@}}, @code{%@{sha1@}}, +@code{%@{sha256@}}, @code{%@{sha512@}}, @code{%@{size@}}. Variables can= be +truncated, e.g. @code{%@{sha256:80@}} returns only first 80 bits. +Defaults to @samp{"%@{sha1@}"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} non-negative-integer= default-process-limit + +Defaults to @samp{100}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} non-negative-integer= default-client-limit + +Defaults to @samp{1000}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} non-negative-integer= default-vsz-limit +Default VSZ (virtual memory size) limit for service processes. +This is mainly intended to catch and kill processes that leak memory +before they eat up everything. +Defaults to @samp{256000000}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string default-login= -user +Login user is internally used by login processes. This is the most +untrusted user in Dovecot system. It shouldn't have access to anything +at all. +Defaults to @samp{"dovenull"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string default-inter= nal-user +Internal user is used by unprivileged processes. It should be +separate from login user, so that login processes can't disturb other +processes. +Defaults to @samp{"dovecot"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string ssl? +SSL/TLS support: yes, no, required. . +Defaults to @samp{"required"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string ssl-cert +PEM encoded X.509 SSL/TLS certificate (public key). +Defaults to @samp{". %d expands to recipient domain. +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string hostname +Hostname to use in various parts of sent mails (e.g. in Message-Id) +and in LMTP replies. Default is the system's real hostname@@domain. +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean quota-full-t= empfail? +If user is over quota, return with temporary failure instead of +bouncing the mail. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} file-name sendmail-p= ath +Binary to use for sending mails. +Defaults to @samp{"/usr/sbin/sendmail"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string submission-ho= st +If non-empty, send mails via this SMTP host[:port] instead of +sendmail. +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string rejection-sub= ject +Subject: header to use for rejection mails. You can use the same +variables as for @samp{rejection-reason} below. +Defaults to @samp{"Rejected: %s"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string rejection-rea= son +Human readable error message for rejection mails. You can use +variables: + +@table @code +@item %n +CRLF +@item %r +reason +@item %s +original subject +@item %t +recipient +@end table +Defaults to @samp{"Your message to <%t> was automatically rejected:%n%r"= }. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string recipient-del= imiter +Delimiter character between local-part and detail in email +address. +Defaults to @samp{"+"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string lda-original-= recipient-header +Header where the original recipient address (SMTP's RCPT TO: +address) is taken from if not available elsewhere. With dovecot-lda -a +parameter overrides this. A commonly used header for this is +X-Original-To. +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean lda-mailbox-= autocreate? +Should saving a mail to a nonexistent mailbox automatically create +it?. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} boolean lda-mailbox-= autosubscribe? +Should automatically created mailboxes be also automatically +subscribed?. +Defaults to @samp{#f}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} non-negative-integer= imap-max-line-length +Maximum IMAP command line length. Some clients generate very long +command lines with huge mailboxes, so you may need to raise this if you +get "Too long argument" or "IMAP command line too large" errors +often. +Defaults to @samp{64000}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string imap-logout-f= ormat +IMAP logout format string: +@table @code +@item %i +total number of bytes read from client +@item %o +total number of bytes sent to client. +@end table +Defaults to @samp{"in=3D%i out=3D%o"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string imap-capabili= ty +Override the IMAP CAPABILITY response. If the value begins with '+', +add the given capabilities on top of the defaults (e.g. +XFOO XBAR). +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string imap-idle-not= ify-interval +How long to wait between "OK Still here" notifications when client +is IDLEing. +Defaults to @samp{"2 mins"}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string imap-id-send +ID field names and values to send to clients. Using * as the value +makes Dovecot use the default value. The following fields have default +values currently: name, version, os, os-version, support-url, +support-email. +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} string imap-id-log +ID fields sent by client to log. * means everything. +Defaults to @samp{""}. +@end deftypevr + +@deftypevr {@code{dovecot-configuration} parameter} space-separated-stri= ng-list imap-client-workarounds +Workarounds for various client bugs: + +@table @code +@item delay-newmail +Send EXISTS/RECENT new mail notifications only when replying to NOOP and +CHECK commands. Some clients ignore them otherwise, for example OSX +Mail ( +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (a= t +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see . +;;; +;;; Some of the help text was taken from the default dovecot.conf files. + +(define-module (gnu services mail) + #:use-module (gnu services) + #:use-module (gnu services base) + #:use-module (gnu services dmd) + #:use-module (gnu system pam) + #:use-module (gnu system shadow) + #:use-module (gnu packages mail) + #:use-module (gnu packages admin) + #:use-module (gnu packages tls) + #:use-module (guix records) + #:use-module (guix packages) + #:use-module (guix gexp) + #:use-module (ice-9 match) + #:export (dovecot-service + dovecot-configuration + + dict-configuration + passdb-configuration + userdb-configuration + unix-listener-configuration + fifo-listener-configuration + inet-listener-configuration + service-configuration + protocol-configuration + plugin-configuration + mailbox-configuration + namespace-configuration)) + +;;; Commentary: +;;; +;;; This module provides service definitions for the Dovecot POP3 and IM= AP +;;; mail server. +;;; +;;; Code: + +(define-record-type* + configuration-field make-configuration-field configuration-field? + (name configuration-field-name) + (type configuration-field-type) + (getter configuration-field-getter) + (predicate configuration-field-predicate) + (serializer configuration-field-serializer) + (default-value-thunk configuration-field-default-value-thunk) + (documentation configuration-field-documentation)) + +(define-syntax define-configuration + (lambda (stx) + (define (id ctx part . parts) + (let ((part (syntax->datum part))) + (datum->syntax + ctx + (match parts + (() part) + (parts (symbol-append part + (syntax->datum (apply id ctx parts)))))= ))) + (syntax-case stx () + ((_ stem (field (field-type def) doc) ...) + (with-syntax (((field-getter ...) + (map (lambda (field) + (id #'stem #'stem #'- field)) + #'(field ...))) + ((field-predicate ...) + (map (lambda (type) + (id #'stem type #'?)) + #'(field-type ...))) + ((field-serializer ...) + (map (lambda (type) + (id #'stem #'serialize- type)) + #'(field-type ...)))) + #`(begin + (define-record-type* #,(id #'stem #'< #'stem #'>) + stem #,(id #'stem #'make- #'stem) #,(id #'stem #'stem #= '?) + (field field-getter (default def)) + ...) + (define #,(id #'stem #'stem #'-fields) + (list (configuration-field + (name 'field) + (type 'field-type) + (getter field-getter) + (predicate field-predicate) + (serializer field-serializer) + (default-value-thunk (lambda () def)) + (documentation doc)) + ...)))))))) + +(define (serialize-configuration config fields) + (for-each (lambda (field) + ((configuration-field-serializer field) + (configuration-field-name field) + ((configuration-field-getter field) config))) + fields)) + +(define (validate-configuration config fields) + (for-each (lambda (field) + (let ((val ((configuration-field-getter field) config))) + (unless ((configuration-field-predicate field) val) + (error "Invalid value for field:" + (configuration-field-name field) val)))) + fields)) + +(define (validate-package field-name package) + (unless (package? package) + (error "not a package" package))) + +(define (uglify-field-name field-name) + (let ((str (symbol->string field-name))) + (string-join (string-split (if (string-suffix? "?" str) + (substring str 0 (1- (string-length s= tr))) + str) + #\-) + "_"))) + +(define (serialize-package field-name val) + #f) + +(define (serialize-field field-name val) + (format #t "~a=3D~a\n" (uglify-field-name field-name) val)) + +(define (serialize-string field-name val) + (serialize-field field-name val)) + +(define (space-separated-string-list? val) + (and (list? val) + (and-map (lambda (x) + (and (string? x) (not (string-index x #\space)))) + val))) +(define (serialize-space-separated-string-list field-name val) + (serialize-field field-name (string-join val " "))) + +(define (comma-separated-string-list? val) + (and (list? val) + (and-map (lambda (x) + (and (string? x) (not (string-index x #\,)))) + val))) +(define (serialize-comma-separated-string-list field-name val) + (serialize-field field-name (string-join val ","))) + +(define (file-name? val) + (and (string? val) + (string-prefix? "/" val))) +(define (serialize-file-name field-name val) + (serialize-string field-name val)) + +(define (colon-separated-file-name-list? val) + (and (list? val) + ;; Trailing slashes not needed and not + (and-map file-name? val))) +(define (serialize-colon-separated-file-name-list field-name val) + (serialize-field field-name (string-join val ":"))) + +(define (serialize-boolean field-name val) + (serialize-string field-name (if val "yes" "no"))) + +(define (non-negative-integer? val) + (and (exact-integer? val) (not (negative? val)))) +(define (serialize-non-negative-integer field-name val) + (serialize-field field-name val)) + +(define (hours? val) (non-negative-integer? val)) +(define (serialize-hours field-name val) + (serialize-field field-name (format #f "~a hours" val))) + +(define (free-form-fields? val) + (match val + (() #t) + ((((? symbol?) . (? string)) . val) (free-form-fields? val)) + (_ #f))) +(define (serialize-free-form-fields field-name val) + (for-each (match-lambda ((k . v) (serialize-field k v))) val)) + +(define (free-form-args? val) + (match val + (() #t) + ((((? symbol?) . (? string)) . val) (free-form-args? val)) + (_ #f))) +(define (serialize-free-form-args field-name val) + (serialize-field field-name + (string-join + (map (match-lambda ((k . v) (format #t "~a=3D~a" k v= ))) val) + " "))) + +(define-configuration dict-configuration + (entries + (free-form-fields '()) + "A list of key-value pairs that this dict should hold.")) + +(define (serialize-dict-configuration field-name val) + (format #t "dict {\n") + (serialize-configuration val dict-configuration-fields) + (format #t "}\n")) + +(define-configuration passdb-configuration + (driver + (string "pam") + "The driver that the passdb should use. Valid values include +@samp{pam}, @samp{passwd}, @samp{shadow}, @samp{bsdauth}, and +@samp{static}.") + (args + (free-form-args '()) + "A list of key-value args to the passdb driver.")) + +(define (serialize-passdb-configuration field-name val) + (format #t "passdb {\n") + (serialize-configuration val passdb-configuration-fields) + (format #t "}\n")) +(define (passdb-configuration-list? val) + (and (list? val) (and-map passdb-configuration? val))) +(define (serialize-passdb-configuration-list field-name val) + (for-each (lambda (val) (serialize-passdb-configuration field-name val= )) val)) + +(define-configuration userdb-configuration + (driver + (string "passwd") + "The driver that the userdb should use. Valid values include +@samp{passwd} and @samp{static}.") + (args + (free-form-args '()) + "A list of key-value args to the userdb driver.") + (override-fields + (free-form-args '()) + "Override fields from passwd.")) + +(define (serialize-userdb-configuration field-name val) + (format #t "userdb {\n") + (serialize-configuration val userdb-configuration-fields) + (format #t "}\n")) +(define (userdb-configuration-list? val) + (and (list? val) (and-map userdb-configuration? val))) +(define (serialize-userdb-configuration-list field-name val) + (for-each (lambda (val) (serialize-userdb-configuration field-name val= )) val)) + +(define-configuration unix-listener-configuration + (path + (file-name (error "unix listener path required")) + "The file name on which to listen.") + (mode + (string "0600") + "The access mode for the socket.") + (user + (string "") + "The user to own the the socket.") + (group + (string "") + "The group to own the socket.")) + +(define (serialize-unix-listener-configuration field-name val) + (format #t "unix_listener ~a {\n" (unix-listener-configuration-path va= l)) + (serialize-configuration val (cdr unix-listener-configuration-fields)) + (format #t "}\n")) + +(define-configuration fifo-listener-configuration + (path + (file-name (error "unix listener path required")) + "The file name on which to listen.") + (mode + (string "0600") + "The access mode for the socket.") + (user + (string "") + "The user to own the the socket.") + (group + (string "") + "The group to own the socket.")) + +(define (serialize-fifo-listener-configuration field-name val) + (format #t "fifo_listener ~a {\n" (fifo-listener-configuration-path va= l)) + (serialize-configuration val (cdr fifo-listener-configuration-fields)) + (format #t "}\n")) + +(define-configuration inet-listener-configuration + (protocol + (string (error "inet listener protocol required")) + "The protocol to listen for.") + (address + (string "") + "The address on which to listen, or empty for all addresses.") + (port + (non-negative-integer (error "inet listener port required")) + "The port on which to listen.") + (ssl? + (boolean #t) + "Whether to use SSL for this service; @samp{yes}, @samp{no}, or +@samp{required}.")) + +(define (serialize-inet-listener-configuration field-name val) + (format #t "inet_listener ~a {\n" (inet-listener-configuration-protoco= l val)) + (serialize-configuration val (cdr inet-listener-configuration-fields)) + (format #t "}\n")) + +(define (listener-configuration? val) + (or (unix-listener-configuration? val) + (fifo-listener-configuration? val) + (inet-listener-configuration? val))) +(define (serialize-listener-configuration field-name val) + (cond + ((unix-listener-configuration? val) + (serialize-unix-listener-configuration field-name val)) + ((fifo-listener-configuration? val) + (serialize-fifo-listener-configuration field-name val)) + ((inet-listener-configuration? val) + (serialize-inet-listener-configuration field-name val)) + (else (error "what" val)))) +(define (listener-configuration-list? val) + (and (list? val) (and-map listener-configuration? val))) +(define (serialize-listener-configuration-list field-name val) + (for-each (lambda (val) + (serialize-listener-configuration field-name val)) + val)) + +(define-configuration service-configuration + (kind + (string (error "service kind required")) + "The service kind. Valid values include @code{director}, +@code{imap-login}, @code{pop3-login}, @code{lmtp}, @code{imap}, +@code{pop3}, @code{auth}, @code{auth-worker}, @code{dict}, +@code{tcpwrap}, @code{quota-warning}, or anything else.") + (listeners + (listener-configuration-list '()) + "Listeners for the service. A listener is either an +@code{unix-listener-configuration}, a @code{fifo-listener-configuration}= , or +an @code{inet-listener-configuration}.") + (service-count + (non-negative-integer 1) + "Number of connections to handle before starting a new process. +Typically the only useful values are 0 (unlimited) or 1. 1 is more +secure, but 0 is faster. .") + (process-min-avail + (non-negative-integer 0) + "Number of processes to always keep waiting for more connections.") + ;; FIXME: Need to be able to take the default for this value from othe= r + ;; parts of the config. + (vsz-limit + (non-negative-integer #e256e6) + "If you set @samp{service-count 0}, you probably need to grow +this.")) + +(define (serialize-service-configuration field-name val) + (format #t "service ~a {\n" (service-configuration-kind val)) + (serialize-configuration val (cdr service-configuration-fields)) + (format #t "}\n")) +(define (service-configuration-list? val) + (and (list? val) (and-map service-configuration? val))) +(define (serialize-service-configuration-list field-name val) + (for-each (lambda (val) + (serialize-service-configuration field-name val)) + val)) + +(define-configuration protocol-configuration + (name + (string (error "protocol name required")) + "The name of the protocol.") + (auth-socket-path + (string "/var/run/dovecot/auth-userdb") + "UNIX socket path to master authentication server to find users. +This is used by imap (for shared users) and lda.") + (mail-plugins + (space-separated-string-list '("$mail_plugins")) + "Space separated list of plugins to load.") + (mail-max-userip-connections + (non-negative-integer 10) + "Maximum number of IMAP connections allowed for a user from each IP +address. NOTE: The username is compared case-sensitively.")) + +(define (serialize-protocol-configuration field-name val) + (format #t "protocol ~a {\n" (protocol-configuration-name val)) + (serialize-configuration val (cdr protocol-configuration-fields)) + (format #t "}\n")) +(define (protocol-configuration-list? val) + (and (list? val) (and-map protocol-configuration? val))) +(define (serialize-protocol-configuration-list field-name val) + (serialize-field 'protocols + (string-join (map protocol-configuration-name val) " = ")) + (for-each (lambda (val) + (serialize-protocol-configuration field-name val)) + val)) + +(define-configuration plugin-configuration + (entries + (free-form-fields '()) + "A list of key-value pairs that this dict should hold.")) + +(define (serialize-plugin-configuration field-name val) + (format #t "plugin {\n") + (serialize-configuration val plugin-configuration-fields) + (format #t "}\n")) + +(define-configuration mailbox-configuration + (name + (string (error "mailbox name is required")) + "Name for this mailbox.") + + (auto + (string "no") + "@samp{create} will automatically create this mailbox. +@samp{subscribe} will both create and subscribe to the mailbox.") + + (special-use + (space-separated-string-list '()) + "List of IMAP @code{SPECIAL-USE} attributes as specified by RFC 6154. +Valid values are @code{\\All}, @code{\\Archive}, @code{\\Drafts}, +@code{\\Flagged}, @code{\\Junk}, @code{\\Sent}, and @code{\\Trash}.")) + +(define (serialize-mailbox-configuration field-name val) + (format #t "mailbox \"~a\" {\n" (mailbox-configuration-name val)) + (serialize-configuration val (cdr mailbox-configuration-fields)) + (format #t "}\n")) +(define (mailbox-configuration-list? val) + (and (list? val) (and-map mailbox-configuration? val))) +(define (serialize-mailbox-configuration-list field-name val) + (for-each (lambda (val) + (serialize-mailbox-configuration field-name val)) + val)) + +(define-configuration namespace-configuration + (name + (string (error "namespace name is required")) + "Name for this namespace.") + + (type + (string "private") + "Namespace type: @samp{private}, @samp{shared} or @samp{public}.") + + (separator + (string "") + "Hierarchy separator to use. You should use the same separator for +all namespaces or some clients get confused. @samp{/} is usually a good +one. The default however depends on the underlying mail storage +format.") + + (prefix + (string "") + "Prefix required to access this namespace. This needs to be +different for all namespaces. For example @samp{Public/}.") + + (location + (string "") + "Physical location of the mailbox. This is in same format as +mail_location, which is also the default for it.") + + (inbox? + (boolean #f) + "There can be only one INBOX, and this setting defines which +namespace has it.") + + (hidden? + (boolean #f) + "If namespace is hidden, it's not advertised to clients via NAMESPACE +extension. You'll most likely also want to set @samp{list? #f}. This is= mostly +useful when converting from another server with different namespaces +which you want to deprecate but still keep working. For example you can +create hidden namespaces with prefixes @samp{~/mail/}, @samp{~%u/mail/} +and @samp{mail/}.") + + (list? + (boolean #t) + "Show the mailboxes under this namespace with LIST command. This +makes the namespace visible for clients that don't support NAMESPACE +extension. The special @code{children} value lists child mailboxes, but +hides the namespace prefix.") + + (subscriptions? + (boolean #t) + "Namespace handles its own subscriptions. If set to @code{#f}, the +parent namespace handles them. The empty prefix should always have this +as @code{#t}.)") + + (mailboxes + (mailbox-configuration-list '()) + "List of predefined mailboxes in this namespace.")) + +(define (serialize-namespace-configuration field-name val) + (format #t "namespace ~a {\n" (namespace-configuration-name val)) + (serialize-configuration val (cdr namespace-configuration-fields)) + (format #t "}\n")) +(define (list-of-namespace-configuration? val) + (and (list? val) (and-map namespace-configuration? val))) +(define (serialize-list-of-namespace-configuration field-name val) + (for-each (lambda (val) + (serialize-namespace-configuration field-name val)) + val)) + +(define-configuration dovecot-configuration + (dovecot + (package dovecot) + "The dovecot package.") + + (listen + (comma-separated-string-list '("*" "::")) + "A list of IPs or hosts where to listen in for connections. @samp{*} +listens in all IPv4 interfaces, @samp{::} listens in all IPv6 +interfaces. If you want to specify non-default ports or anything more +complex, customize the address and port fields of the +@samp{inet-listener} of the specific services you are interested in.") + + (protocols + (protocol-configuration-list + (list (protocol-configuration + (name "imap")))) + "List of protocols we want to serve. Available protocols include +@samp{imap}, @samp{pop3}, and @samp{lmtp}.") + + (services + (service-configuration-list + (list + (service-configuration + (kind "imap-login") + (listeners + (list + (inet-listener-configuration (protocol "imap") (port 143) (ssl? = #f)) + (inet-listener-configuration (protocol "imaps") (port 993) (ssl?= #t))))) + (service-configuration + (kind "pop3-login") + (listeners + (list + (inet-listener-configuration (protocol "pop3") (port 110) (ssl? = #f)) + (inet-listener-configuration (protocol "pop3s") (port 995) (ssl?= #t))))) + (service-configuration + (kind "lmtp") + (listeners + (list (unix-listener-configuration (path "lmtp") (mode "0666"))))= ) + (service-configuration (kind "imap")) + (service-configuration (kind "pop3")) + (service-configuration (kind "auth") + ;; In what could be taken to be a bug, the default value of 1 for + ;; service-count makes it so that a PAM auth worker can't fork off + ;; subprocesses for making blocking queries. The result is that n= obody + ;; can log in -- very secure, but not very useful! If we simply o= mit + ;; the service-count, it will default to the value of + ;; auth-worker-max-count, which is 30, instead of defaulting to 1,= which + ;; is the default for all other services. As a hack, bump this va= lue to + ;; 30. + (service-count 30) + (listeners + (list (unix-listener-configuration (path "auth-userdb"))))) + (service-configuration (kind "auth-worker")) + (service-configuration (kind "dict") + (listeners (list (unix-listener-configuration (path "dict"))))))) + "List of services to enable. Available services include @samp{imap}, +@samp{imap-login}, @samp{pop3}, @samp{pop3-login}, @samp{auth}, and +@samp{lmtp}.") + + (dict + (dict-configuration (dict-configuration)) + "Dict configuration, as created by the @code{dict-configuration} +constructor.") + + (passdbs + (passdb-configuration-list (list (passdb-configuration (driver "pam")= ))) + "List of passdb configurations, each one created by the +@code{passdb-configuration} constructor.") + + (userdbs + (userdb-configuration-list (list (userdb-configuration (driver "passw= d")))) + "List of userdb configurations, each one created by the +@code{userdb-configuration} constructor.") + + (plugin-configuration + (plugin-configuration (plugin-configuration)) + "Plug-in configuration, created by the @code{plugin-configuration} +constructor.") + + (namespaces + (list-of-namespace-configuration + (list + (namespace-configuration + (name "inbox") + (prefix "") + (inbox? #t) + (mailboxes + (list + (mailbox-configuration (name "Drafts") (special-use '("\\Drafts"= ))) + (mailbox-configuration (name "Junk") (special-use '("\\Junk"))) + (mailbox-configuration (name "Trash") (special-use '("\\Trash"))= ) + (mailbox-configuration (name "Sent") (special-use '("\\Sent"))) + (mailbox-configuration (name "Sent Messages") (special-use '("\\= Sent"))) + (mailbox-configuration (name "Drafts") (special-use '("\\Drafts"= )))))))) + "List of namespaces. Each item in the list is created by the +@code{namespace-configuration} constructor.") + + (base-dir + (file-name "/var/run/dovecot/") + "Base directory where to store runtime data.") + + (login-greeting + (string "Dovecot ready.") + "Greeting message for clients.") + + (login-trusted-networks + (space-separated-string-list '()) + "List of trusted network ranges. Connections from these IPs are +allowed to override their IP addresses and ports (for logging and for +authentication checks). @samp{disable-plaintext-auth} is also ignored +for these networks. Typically you'd specify your IMAP proxy servers +here.") + + (login-access-sockets + (space-separated-string-list '()) + "List of login access check sockets (e.g. tcpwrap).") + + (verbose-proctitle? + (boolean #f) + "Show more verbose process titles (in ps). Currently shows user name +and IP address. Useful for seeing who are actually using the IMAP +processes (e.g. shared mailboxes or if same uid is used for multiple +accounts).") + + (shutdown-clients? + (boolean #t) + "Should all processes be killed when Dovecot master process shuts dow= n. +Setting this to @code{#f} means that Dovecot can be upgraded without +forcing existing client connections to close (although that could also +be a problem if the upgrade is e.g. because of a security fix).") + + (doveadm-worker-count + (non-negative-integer 0) + "If non-zero, run mail commands via this many connections to doveadm +server, instead of running them directly in the same process.") + + (doveadm-socket-path + (string "doveadm-server") + "UNIX socket or host:port used for connecting to doveadm server.") + + (import-environment + (space-separated-string-list '("TZ")) + "List of environment variables that are preserved on Dovecot startup +and passed down to all of its child processes. You can also give +key=3Dvalue pairs to always set specific settings.") + +;;; Authentication processes + + (disable-plaintext-auth? + (boolean #t) + "Disable LOGIN command and all other plaintext authentications unless +SSL/TLS is used (LOGINDISABLED capability). Note that if the remote IP +matches the local IP (i.e. you're connecting from the same computer), +the connection is considered secure and plaintext authentication is +allowed. See also ssl=3Drequired setting.") + + (auth-cache-size + (non-negative-integer 0) + "Authentication cache size (e.g. @samp{#e10e6}). 0 means it's disabl= ed. +Note that bsdauth, PAM and vpopmail require @samp{cache-key} to be set +for caching to be used.") + + (auth-cache-ttl + (string "1 hour") + "Time to live for cached data. After TTL expires the cached record +is no longer used, *except* if the main database lookup returns internal +failure. We also try to handle password changes automatically: If +user's previous authentication was successful, but this one wasn't, the +cache isn't used. For now this works only with plaintext +authentication.") + + (auth-cache-negative-ttl + (string "1 hour") + "TTL for negative hits (user not found, password mismatch). +0 disables caching them completely.") + + (auth-realms + (space-separated-string-list '()) + "List of realms for SASL authentication mechanisms that need them. +You can leave it empty if you don't want to support multiple realms. +Many clients simply use the first one listed here, so keep the default +realm first.") + + (auth-default-realm + (string "") + "Default realm/domain to use if none was specified. This is used for +both SASL realms and appending @@domain to username in plaintext +logins.") + + (auth-username-chars + (string + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@= ") + "List of allowed characters in username. If the user-given username +contains a character not listed in here, the login automatically fails. +This is just an extra check to make sure user can't exploit any +potential quote escaping vulnerabilities with SQL/LDAP databases. If +you want to allow all characters, set this value to empty.") + + (auth-username-translation + (string "") + "Username character translations before it's looked up from +databases. The value contains series of from -> to characters. For +example @samp{#@@/@@} means that @samp{#} and @samp{/} characters are +translated to @samp{@@}.") + + (auth-username-format + (string "%Lu") + "Username formatting before it's looked up from databases. You can +use the standard variables here, e.g. %Lu would lowercase the username, +%n would drop away the domain if it was given, or @samp{%n-AT-%d} would +change the @samp{@@} into @samp{-AT-}. This translation is done after +@samp{auth-username-translation} changes.") + + (auth-master-user-separator + (string "") + "If you want to allow master users to log in by specifying the master +username within the normal username string (i.e. not using SASL +mechanism's support for it), you can specify the separator character +here. The format is then . +UW-IMAP uses @samp{*} as the separator, so that could be a good +choice.") + + (auth-anonymous-username + (string "anonymous") + "Username to use for users logging in with ANONYMOUS SASL +mechanism.") + + (auth-worker-max-count + (non-negative-integer 30) + "Maximum number of dovecot-auth worker processes. They're used to +execute blocking passdb and userdb queries (e.g. MySQL and PAM). +They're automatically created and destroyed as needed.") + + (auth-gssapi-hostname + (string "") + "Host name to use in GSSAPI principal names. The default is to use +the name returned by gethostname(). Use @samp{$ALL} (with quotes) to +allow all keytab entries.") + + (auth-krb5-keytab + (string "") + "Kerberos keytab to use for the GSSAPI mechanism. Will use the +system default (usually /etc/krb5.keytab) if not specified. You may +need to change the auth service to run as root to be able to read this +file.") + + (auth-use-winbind? + (boolean #f) + "Do NTLM and GSS-SPNEGO authentication using Samba's winbind daemon +and @samp{ntlm-auth} helper. +.") + + (auth-winbind-helper-path + (file-name "/usr/bin/ntlm_auth") + "Path for Samba's @samp{ntlm-auth} helper binary.") + + (auth-failure-delay + (string "2 secs") + "Time to delay before replying to failed authentications.") + + (auth-ssl-require-client-cert? + (boolean #f) + "Require a valid SSL client certificate or the authentication +fails.") + + (auth-ssl-username-from-cert? + (boolean #f) + "Take the username from client's SSL certificate, using +@code{X509_NAME_get_text_by_NID()} which returns the subject's DN's +CommonName.") + + (auth-mechanisms + (space-separated-string-list '("plain")) + "List of wanted authentication mechanisms. Supported mechanisms are: +@samp{plain}, @samp{login}, @samp{digest-md5}, @samp{cram-md5}, +@samp{ntlm}, @samp{rpa}, @samp{apop}, @samp{anonymous}, @samp{gssapi}, +@samp{otp}, @samp{skey}, and @samp{gss-spnego}. NOTE: See also +@samp{disable-plaintext-auth} setting.") + + (director-servers + (space-separated-string-list '()) + "List of IPs or hostnames to all director servers, including ourself. +Ports can be specified as ip:port. The default port is the same as what +director service's @samp{inet-listener} is using.") + + (director-mail-servers + (space-separated-string-list '()) + "List of IPs or hostnames to all backend mail servers. Ranges are +allowed too, like 10.0.0.10-10.0.0.30.") + + (director-user-expire + (string "15 min") + "How long to redirect users to a specific server after it no longer +has any connections.") + + (director-doveadm-port + (non-negative-integer 0) + "TCP/IP port that accepts doveadm connections (instead of director +connections) If you enable this, you'll also need to add +@samp{inet-listener} for the port.") + + (director-username-hash + (string "%Lu") + "How the username is translated before being hashed. Useful values +include %Ln if user can log in with or without @@domain, %Ld if mailboxe= s +are shared within domain.") + +;;; Log destination. + + (log-path + (string "syslog") + "Log file to use for error messages. @samp{syslog} logs to syslog, +@samp{/dev/stderr} logs to stderr.") + + (info-log-path + (string "") + "Log file to use for informational messages. Defaults to +@samp{log-path}.") + + (debug-log-path + (string "") + "Log file to use for debug messages. Defaults to +@samp{info-log-path}.") + + (syslog-facility + (string "mail") + "Syslog facility to use if you're logging to syslog. Usually if you +don't want to use @samp{mail}, you'll use local0..local7. Also other +standard facilities are supported.") + + (auth-verbose? + (boolean #f) + "Log unsuccessful authentication attempts and the reasons why they +failed.") + + (auth-verbose-passwords? + (boolean #f) + "In case of password mismatches, log the attempted password. Valid +values are no, plain and sha1. sha1 can be useful for detecting brute +force password attempts vs. user simply trying the same password over +and over again. You can also truncate the value to n chars by appending +\":n\" (e.g. sha1:6).") + + (auth-debug? + (boolean #f) + "Even more verbose logging for debugging purposes. Shows for example +SQL queries.") + + (auth-debug-passwords? + (boolean #f) + "In case of password mismatches, log the passwords and used scheme so +the problem can be debugged. Enabling this also enables +@samp{auth-debug}.") + + (mail-debug? + (boolean #f) + "Enable mail process debugging. This can help you figure out why +Dovecot isn't finding your mails.") + + (verbose-ssl? + (boolean #f) + "Show protocol level SSL errors.") + + (log-timestamp + (string "\"%b %d %H:%M:%S \"") + "Prefix for each line written to log file. % codes are in +strftime(3) format.") + + (login-log-format-elements + (space-separated-string-list + '("user=3D<%u>" "method=3D%m" "rip=3D%r" "lip=3D%l" "mpid=3D%e" "%c"= )) + "List of elements we want to log. The elements which have a +non-empty variable value are joined together to form a comma-separated +string.") + + (login-log-format + (string "%$: %s") + "Login log format. %s contains @samp{login-log-format-elements} +string, %$ contains the data we want to log.") + + (mail-log-prefix + (string "\"%s(%u): \"") + "Log prefix for mail processes. See doc/wiki/Variables.txt for list +of possible variables you can use.") + + (deliver-log-format + (string "msgid=3D%m: %$") + "Format to use for logging mail deliveries. You can use variables: +@table @code +@item %$ +Delivery status message (e.g. @samp{saved to INBOX}) +@item %m +Message-ID +@item %s +Subject +@item %f +From address +@item %p +Physical size +@item %w +Virtual size. +@end table") + +;;; Mailbox locations and namespaces + + (mail-location + (string "") + "Location for users' mailboxes. The default is empty, which means +that Dovecot tries to find the mailboxes automatically. This won't work +if the user doesn't yet have any mail, so you should explicitly tell +Dovecot the full location. + +If you're using mbox, giving a path to the INBOX +file (e.g. /var/mail/%u) isn't enough. You'll also need to tell Dovecot +where the other mailboxes are kept. This is called the \"root mail +directory\", and it must be the first path given in the +@samp{mail-location} setting. + +There are a few special variables you can use, eg.: + +@table @samp +@item %u +username +@item %n +user part in user@@domain, same as %u if there's no domain +@item %d +domain part in user@@domain, empty if there's no domain +@item %h +home director +@end table + +See doc/wiki/Variables.txt for full list. Some examples: +@table @samp +@item maildir:~/Maildir +@item mbox:~/mail:INBOX=3D/var/mail/%u +@item mbox:/var/mail/%d/%1n/%n:INDEX=3D/var/indexes/%d/%1n/% +@end table") + + (mail-uid + (string "") + "System user and group used to access mails. If you use multiple, +userdb can override these by returning uid or gid fields. You can use +either numbers or names. .") + + (mail-gid + (string "") + "") + + (mail-privileged-group + (string "") + "Group to enable temporarily for privileged operations. Currently +this is used only with INBOX when either its initial creation or +dotlocking fails. Typically this is set to \"mail\" to give access to +/var/mail.") + + (mail-access-groups + (string "") + "Grant access to these supplementary groups for mail processes. +Typically these are used to set up access to shared mailboxes. Note +that it may be dangerous to set these if users can create +symlinks (e.g. if \"mail\" group is set here, ln -s /var/mail ~/mail/var +could allow a user to delete others' mailboxes, or ln -s +/secret/shared/box ~/mail/mybox would allow reading it).") + + (mail-full-filesystem-access? + (boolean #f) + "Allow full filesystem access to clients. There's no access checks +other than what the operating system does for the active UID/GID. It +works with both maildir and mboxes, allowing you to prefix mailboxes +names with e.g. /path/ or ~user/.") + +;;; Mail processes + + (mmap-disable? + (boolean #f) + "Don't use mmap() at all. This is required if you store indexes to +shared filesystems (NFS or clustered filesystem).") + + (dotlock-use-excl? + (boolean #t) + "Rely on @samp{O_EXCL} to work when creating dotlock files. NFS +supports @samp{O_EXCL} since version 3, so this should be safe to use +nowadays by default.") + + (mail-fsync + (string "optimized") + "When to use fsync() or fdatasync() calls: +@table @code +@item optimized +Whenever necessary to avoid losing important data +@item always +Useful with e.g. NFS when write()s are delayed +@item never +Never use it (best performance, but crashes can lose data). +@end table") + + (mail-nfs-storage? + (boolean #f) + "Mail storage exists in NFS. Set this to yes to make Dovecot flush +NFS caches whenever needed. If you're using only a single mail server +this isn't needed.") + + (mail-nfs-index? + (boolean #f) + "Mail index files also exist in NFS. Setting this to yes requires +@samp{mmap-disable? #t} and @samp{fsync-disable? #f}.") + + (lock-method + (string "fcntl") + "Locking method for index files. Alternatives are fcntl, flock and +dotlock. Dotlocking uses some tricks which may create more disk I/O +than other locking methods. NFS users: flock doesn't work, remember to +change @samp{mmap-disable}.") + + (mail-temp-dir + (file-name "/tmp") + "Directory in which LDA/LMTP temporarily stores incoming mails >128 +kB.") + + (first-valid-uid + (non-negative-integer 500) + "Valid UID range for users. This is mostly to make sure that users c= an't +log in as daemons or other system users. Note that denying root logins = is +hardcoded to dovecot binary and can't be done even if @samp{first-valid-= uid} +is set to 0.") + + (last-valid-uid + (non-negative-integer 0) + "") + + (first-valid-gid + (non-negative-integer 1) + "Valid GID range for users. Users having non-valid GID as primary gr= oup ID +aren't allowed to log in. If user belongs to supplementary groups with +non-valid GIDs, those groups are not set.") + + (last-valid-gid + (non-negative-integer 0) + "") + + (mail-max-keyword-length + (non-negative-integer 50) + "Maximum allowed length for mail keyword name. It's only forced when +trying to create new keywords.") + + (valid-chroot-dirs + (colon-separated-file-name-list '()) + "List of directories under which chrooting is allowed for mail +processes (i.e. /var/mail will allow chrooting to /var/mail/foo/bar +too). This setting doesn't affect @samp{login-chroot} +@samp{mail-chroot} or auth chroot settings. If this setting is empty, +\"/./\" in home dirs are ignored. WARNING: Never add directories here +which local users can modify, that may lead to root exploit. Usually +this should be done only if you don't allow shell access for users. +.") + + (mail-chroot + (string "") + "Default chroot directory for mail processes. This can be overridden +for specific users in user database by giving /./ in user's home +directory (e.g. /home/./user chroots into /home). Note that usually +there is no real need to do chrooting, Dovecot doesn't allow users to +access files outside their mail directory anyway. If your home +directories are prefixed with the chroot directory, append \"/.\" to +@samp{mail-chroot}. .") + + (auth-socket-path + (file-name "/var/run/dovecot/auth-userdb") + "UNIX socket path to master authentication server to find users. +This is used by imap (for shared users) and lda.") + + (mail-plugin-dir + (file-name "/usr/lib/dovecot") + "Directory where to look up mail plugins.") + + (mail-plugins + (space-separated-string-list '()) + "List of plugins to load for all services. Plugins specific to IMAP, +LDA, etc. are added to this list in their own .conf files.") + + + (mail-cache-min-mail-count + (non-negative-integer 0) + "The minimum number of mails in a mailbox before updates are done to +cache file. This allows optimizing Dovecot's behavior to do less disk +writes at the cost of more disk reads.") + + (mailbox-idle-check-interval + (string "30 secs") + "When IDLE command is running, mailbox is checked once in a while to +see if there are any new mails or other changes. This setting defines +the minimum time to wait between those checks. Dovecot can also use +dnotify, inotify and kqueue to find out immediately when changes +occur.") + + (mail-save-crlf? + (boolean #f) + "Save mails with CR+LF instead of plain LF. This makes sending those +mails take less CPU, especially with sendfile() syscall with Linux and +FreeBSD. But it also creates a bit more disk I/O which may just make it +slower. Also note that if other software reads the mboxes/maildirs, +they may handle the extra CRs wrong and cause problems.") + + (maildir-stat-dirs? + (boolean #f) + "By default LIST command returns all entries in maildir beginning +with a dot. Enabling this option makes Dovecot return only entries +which are directories. This is done by stat()ing each entry, so it +causes more disk I/O. + (For systems setting struct @samp{dirent->d_type} this check is free +and it's done always regardless of this setting).") + + (maildir-copy-with-hardlinks? + (boolean #t) + "When copying a message, do it with hard links whenever possible. +This makes the performance much better, and it's unlikely to have any +side effects.") + + (maildir-very-dirty-syncs? + (boolean #f) + "Assume Dovecot is the only MUA accessing Maildir: Scan cur/ +directory only when its mtime changes unexpectedly or when we can't find +the mail otherwise.") + + (mbox-read-locks + (space-separated-string-list '("fcntl")) + "Which locking methods to use for locking mbox. There are four +available: + +@table @code +@item dotlock +Create .lock file. This is the oldest and most NFS-safe +solution. If you want to use /var/mail/ like directory, the users will +need write access to that directory. +@item dotlock-try +Same as dotlock, but if it fails because of permissions or because there +isn't enough disk space, just skip it. +@item fcntl +Use this if possible. Works with NFS too if lockd is used. +@item flock +May not exist in all systems. Doesn't work with NFS.=20 +@item lockf +May not exist in all systems. Doesn't work with NFS. +@end table + +You can use multiple locking methods; if you do the order they're declar= ed +in is important to avoid deadlocks if other MTAs/MUAs are using multiple +locking methods as well. Some operating systems don't allow using some = of +them simultaneously.") + + (mbox-write-locks + (space-separated-string-list '("dotlock" "fcntl")) + "") + + (mbox-lock-timeout + (string "5 mins") + "Maximum time to wait for lock (all of them) before aborting.") + + (mbox-dotlock-change-timeout + (string "2 mins") + "If dotlock exists but the mailbox isn't modified in any way, +override the lock file after this much time.") + + (mbox-dirty-syncs? + (boolean #t) + "When mbox changes unexpectedly we have to fully read it to find out +what changed. If the mbox is large this can take a long time. Since +the change is usually just a newly appended mail, it'd be faster to +simply read the new mails. If this setting is enabled, Dovecot does +this but still safely fallbacks to re-reading the whole mbox file +whenever something in mbox isn't how it's expected to be. The only real +downside to this setting is that if some other MUA changes message +flags, Dovecot doesn't notice it immediately. Note that a full sync is +done with SELECT, EXAMINE, EXPUNGE and CHECK commands.") + + (mbox-very-dirty-syncs? + (boolean #f) + "Like @samp{mbox-dirty-syncs}, but don't do full syncs even with SELE= CT, +EXAMINE, EXPUNGE or CHECK commands. If this is set, +@samp{mbox-dirty-syncs} is ignored.") + + (mbox-lazy-writes? + (boolean #t) + "Delay writing mbox headers until doing a full write sync (EXPUNGE +and CHECK commands and when closing the mailbox). This is especially +useful for POP3 where clients often delete all mails. The downside is +that our changes aren't immediately visible to other MUAs.") + + (mbox-min-index-size + (non-negative-integer 0) + "If mbox size is smaller than this (e.g. 100k), don't write index +files. If an index file already exists it's still read, just not +updated.") + + (mdbox-rotate-size + (non-negative-integer #e2e6) + "Maximum dbox file size until it's rotated.") + + (mdbox-rotate-interval + (string "1d") + "Maximum dbox file age until it's rotated. Typically in days. Day +begins from midnight, so 1d =3D today, 2d =3D yesterday, etc. 0 =3D che= ck +disabled.") + + (mdbox-preallocate-space? + (boolean #f) + "When creating new mdbox files, immediately preallocate their size to +@samp{mdbox-rotate-size}. This setting currently works only in Linux +with some filesystems (ext4, xfs).") + + (mail-attachment-dir + (string "") + "sdbox and mdbox support saving mail attachments to external files, +which also allows single instance storage for them. Other backends +don't support this for now. + +WARNING: This feature hasn't been tested much yet. Use at your own risk= . + +Directory root where to store mail attachments. Disabled, if empty.") + + (mail-attachment-min-size + (non-negative-integer #e128e3) + "Attachments smaller than this aren't saved externally. It's also +possible to write a plugin to disable saving specific attachments +externally.") + + (mail-attachment-fs + (string "sis posix") + "Filesystem backend to use for saving attachments: +@table @code +@item posix +No SiS done by Dovecot (but this might help FS's own deduplication) +@item sis posix +SiS with immediate byte-by-byte comparison during saving +@item sis-queue posix +SiS with delayed comparison and deduplication. +@end table") + + (mail-attachment-hash + (string "%{sha1}") + "Hash format to use in attachment filenames. You can add any text an= d +variables: @code{%@{md4@}}, @code{%@{md5@}}, @code{%@{sha1@}}, +@code{%@{sha256@}}, @code{%@{sha512@}}, @code{%@{size@}}. Variables can= be +truncated, e.g. @code{%@{sha256:80@}} returns only first 80 bits.") + + (default-process-limit + (non-negative-integer 100) + "") + + (default-client-limit + (non-negative-integer 1000) + "") + + (default-vsz-limit + (non-negative-integer #e256e6) + "Default VSZ (virtual memory size) limit for service processes. +This is mainly intended to catch and kill processes that leak memory +before they eat up everything.") + + (default-login-user + (string "dovenull") + "Login user is internally used by login processes. This is the most +untrusted user in Dovecot system. It shouldn't have access to anything +at all.") + + (default-internal-user + (string "dovecot") + "Internal user is used by unprivileged processes. It should be +separate from login user, so that login processes can't disturb other +processes.") + + (ssl? + (string "required") + "SSL/TLS support: yes, no, required. .") + + (ssl-cert + (string ". %d expands to recipient domain.") + + (hostname + (string "") + "Hostname to use in various parts of sent mails (e.g. in Message-Id) +and in LMTP replies. Default is the system's real hostname@@domain.") + + (quota-full-tempfail? + (boolean #f) + "If user is over quota, return with temporary failure instead of +bouncing the mail.") + + (sendmail-path + (file-name "/usr/sbin/sendmail") + "Binary to use for sending mails.") + + (submission-host + (string "") + "If non-empty, send mails via this SMTP host[:port] instead of +sendmail.") + + (rejection-subject + (string "Rejected: %s") + "Subject: header to use for rejection mails. You can use the same +variables as for @samp{rejection-reason} below.") + + (rejection-reason + (string "Your message to <%t> was automatically rejected:%n%r") + "Human readable error message for rejection mails. You can use +variables: + +@table @code +@item %n +CRLF +@item %r +reason +@item %s +original subject +@item %t +recipient +@end table") + + (recipient-delimiter + (string "+") + "Delimiter character between local-part and detail in email +address.") + + (lda-original-recipient-header + (string "") + "Header where the original recipient address (SMTP's RCPT TO: +address) is taken from if not available elsewhere. With dovecot-lda -a +parameter overrides this. A commonly used header for this is +X-Original-To.") + + (lda-mailbox-autocreate? + (boolean #f) + "Should saving a mail to a nonexistent mailbox automatically create +it?.") + + (lda-mailbox-autosubscribe? + (boolean #f) + "Should automatically created mailboxes be also automatically +subscribed?.") + + + (imap-max-line-length + (non-negative-integer #e64e3) + "Maximum IMAP command line length. Some clients generate very long +command lines with huge mailboxes, so you may need to raise this if you +get \"Too long argument\" or \"IMAP command line too large\" errors +often.") + + (imap-logout-format + (string "in=3D%i out=3D%o") + "IMAP logout format string: +@table @code +@item %i +total number of bytes read from client +@item %o +total number of bytes sent to client. +@end table") + + (imap-capability + (string "") + "Override the IMAP CAPABILITY response. If the value begins with '+'= , +add the given capabilities on top of the defaults (e.g. +XFOO XBAR).") + + (imap-idle-notify-interval + (string "2 mins") + "How long to wait between \"OK Still here\" notifications when client +is IDLEing.") + + (imap-id-send + (string "") + "ID field names and values to send to clients. Using * as the value +makes Dovecot use the default value. The following fields have default +values currently: name, version, os, os-version, support-url, +support-email.") + + (imap-id-log + (string "") + "ID fields sent by client to log. * means everything.") + + (imap-client-workarounds + (space-separated-string-list '()) + "Workarounds for various client bugs: + +@table @code +@item delay-newmail +Send EXISTS/RECENT new mail notifications only when replying to NOOP and +CHECK commands. Some clients ignore them otherwise, for example OSX +Mail ( for CONFIG." + (let* ((config-str (with-output-to-string + (lambda () + (serialize-configuration config + dovecot-configuration-= fields)))) + (config-file (plain-file "dovecot.conf" config-str)) + (dovecot (dovecot-configuration-dovecot config))) + (list (dmd-service + (documentation "Run the Dovecot POP3/IMAP mail server.") + (provision '(dovecot)) + (requirement '(networking)) + (start #~(make-forkexec-constructor + (list (string-append #$dovecot "/sbin/dovecot") + "-F" "-c" #$config-file))) + (stop #~(make-forkexec-constructor + (list (string-append #$dovecot "/sbin/dovecot") + "-c" #$config-file "stop"))))))) + +(define %dovecot-pam-services + (list (unix-pam-service "dovecot"))) + +(define dovecot-service-type + (let ((dovecot-package (compose list dovecot-configuration-dovecot))) + (service-type (name 'dovecot) + (extensions + (list (service-extension dmd-root-service-type + dovecot-dmd-service) + (service-extension account-service-type + (const %dovecot-accounts)) + (service-extension pam-root-service-type + (const %dovecot-pam-services= )) + (service-extension activation-service-type + (const %dovecot-activation))= ))))) + +(define* (dovecot-service #:key (config (dovecot-configuration))) + "Return a service that runs @command{dovecot}, a mail server that can +run POP3, IMAP, and LMTP." + (validate-configuration config dovecot-configuration-fields) + (service dovecot-service-type config)) + +;; A little helper to make it easier to document all those fields. +(define (generate-documentation) + (define documentation + `((dovecot-configuration + ,dovecot-configuration-fields + (dict dict-configuration) + (namespaces namespace-configuration) + (plugin plugin-configuration) + (passdbs passdb-configuration) + (userdbs userdb-configuration) + (services service-configuration) + (protocols protocol-configuration)) + (dict-configuration ,dict-configuration-fields) + (plugin-configuration ,plugin-configuration-fields) + (passdb-configuration ,passdb-configuration-fields) + (userdb-configuration ,userdb-configuration-fields) + (unix-listener-configuration ,unix-listener-configuration-fields) + (fifo-listener-configuration ,fifo-listener-configuration-fields) + (inet-listener-configuration ,inet-listener-configuration-fields) + (namespace-configuration + ,namespace-configuration-fields + (mailboxes mailbox-configuration)) + (mailbox-configuration ,mailbox-configuration-fields) + (service-configuration + ,service-configuration-fields + (listeners unix-listener-configuration fifo-listener-configuratio= n + inet-listener-configuration)) + (protocol-configuration ,protocol-configuration-fields))) + (define (generate configuration-name) + (match (assq-ref documentation configuration-name) + ((fields . sub-documentation) + (format #t "\nAvailable @code{~a} fields are:\n\n" configuration-= name) + (for-each + (lambda (f) + (let ((field-name (configuration-field-name f)) + (field-type (configuration-field-type f)) + (field-docs (string-trim-both + (configuration-field-documentation f))) + (default (catch #t + (configuration-field-default-value-thunk f) + (lambda _ 'nope)))) + (define (escape-chars str chars escape) + (with-output-to-string + (lambda () + (string-for-each (lambda (c) + (when (char-set-contains? chars c) + (display escape)) + (display c)) + str)))) + (define (show-default? val) + (or (string? default) (number? default) (boolean? default) + (and (list? val) (and-map show-default? val)))) + (format #t "@deftypevr {@code{~a} parameter} ~a ~a\n~a\n" + configuration-name field-type field-name field-docs) + (when (show-default? default) + (format #t "Defaults to @samp{~a}.\n" + (escape-chars (format #f "~s" default) + (char-set #\@ #\{ #\}) + #\@))) + (for-each generate (or (assq-ref sub-documentation field-nam= e) '())) + (format #t "@end deftypevr\n\n"))) + fields)))) + (generate 'dovecot-configuration)) --=20 2.6.3