+ (protocol-configuration ,protocol-configuration-fields))
+ 'dovecot-configuration))
+
+\f
+;;;
+;;; OpenSMTPD.
+;;;
+
+(define-record-type* <opensmtpd-configuration>
+ opensmtpd-configuration make-opensmtpd-configuration
+ opensmtpd-configuration?
+ (package opensmtpd-configuration-package
+ (default opensmtpd))
+ (config-file opensmtpd-configuration-config-file
+ (default %default-opensmtpd-config-file)))
+
+(define %default-opensmtpd-config-file
+ (plain-file "smtpd.conf" "
+listen on lo
+
+action inbound mbox
+match for local action inbound
+
+action outbound relay
+match from local for any action outbound
+"))
+
+(define opensmtpd-shepherd-service
+ (match-lambda
+ (($ <opensmtpd-configuration> package config-file)
+ (list (shepherd-service
+ (provision '(smtpd))
+ (requirement '(loopback))
+ (documentation "Run the OpenSMTPD daemon.")
+ (start (let ((smtpd (file-append package "/sbin/smtpd")))
+ #~(make-forkexec-constructor
+ (list #$smtpd "-f" #$config-file)
+ #:pid-file "/var/run/smtpd.pid")))
+ (stop #~(make-kill-destructor)))))))
+
+(define %opensmtpd-accounts
+ (list (user-group
+ (name "smtpq")
+ (system? #t))
+ (user-account
+ (name "smtpd")
+ (group "nogroup")
+ (system? #t)
+ (comment "SMTP Daemon")
+ (home-directory "/var/empty")
+ (shell (file-append shadow "/sbin/nologin")))
+ (user-account
+ (name "smtpq")
+ (group "smtpq")
+ (system? #t)
+ (comment "SMTPD Queue")
+ (home-directory "/var/empty")
+ (shell (file-append shadow "/sbin/nologin")))))
+
+(define opensmtpd-activation
+ (match-lambda
+ (($ <opensmtpd-configuration> package config-file)
+ (let ((smtpd (file-append package "/sbin/smtpd")))
+ #~(begin
+ (use-modules (guix build utils))
+ ;; Create mbox and spool directories.
+ (mkdir-p "/var/mail")
+ (mkdir-p "/var/spool/smtpd")
+ (chmod "/var/spool/smtpd" #o711)
+ (mkdir-p "/var/spool/mail")
+ (chmod "/var/spool/mail" #o711))))))
+
+(define %opensmtpd-pam-services
+ (list (unix-pam-service "smtpd")))
+
+(define opensmtpd-service-type
+ (service-type
+ (name 'opensmtpd)
+ (extensions
+ (list (service-extension account-service-type
+ (const %opensmtpd-accounts))
+ (service-extension activation-service-type
+ opensmtpd-activation)
+ (service-extension pam-root-service-type
+ (const %opensmtpd-pam-services))
+ (service-extension profile-service-type
+ (compose list opensmtpd-configuration-package))
+ (service-extension shepherd-root-service-type
+ opensmtpd-shepherd-service)))))
+
+\f
+;;;
+;;; mail aliases.
+;;;
+
+(define (mail-aliases-etc aliases)
+ `(("aliases" ,(plain-file "aliases"
+ ;; Ideally we'd use a format string like
+ ;; "~:{~a: ~{~a~^,~}\n~}", but it gives a
+ ;; warning that I can't figure out how to fix,
+ ;; so we'll just use string-join below instead.
+ (format #f "~:{~a: ~a\n~}"
+ (map (match-lambda
+ ((alias addresses ...)
+ (list alias (string-join addresses ","))))
+ aliases))))))
+
+(define mail-aliases-service-type
+ (service-type
+ (name 'mail-aliases)
+ (extensions
+ (list (service-extension etc-service-type mail-aliases-etc)))
+ (compose concatenate)
+ (extend append)))
+
+\f
+;;;
+;;; Exim.
+;;;
+
+(define-record-type* <exim-configuration> exim-configuration
+ make-exim-configuration
+ exim-configuration?
+ (package exim-configuration-package ;<package>
+ (default exim))
+ (config-file exim-configuration-config-file ;file-like
+ (default #f)))
+
+(define %exim-accounts
+ (list (user-group
+ (name "exim")
+ (system? #t))
+ (user-account
+ (name "exim")
+ (group "exim")
+ (system? #t)
+ (comment "Exim Daemon")
+ (home-directory "/var/empty")
+ (shell (file-append shadow "/sbin/nologin")))))
+
+(define (exim-computed-config-file package config-file)
+ (computed-file "exim.conf"
+ #~(call-with-output-file #$output
+ (lambda (port)
+ (format port "
+exim_user = exim
+exim_group = exim
+.include ~a"
+ #$(or config-file
+ (file-append package "/etc/exim.conf")))))))
+
+(define exim-shepherd-service
+ (match-lambda
+ (($ <exim-configuration> package config-file)
+ (list (shepherd-service
+ (provision '(exim mta))
+ (documentation "Run the exim daemon.")
+ (requirement '(networking))
+ (start #~(make-forkexec-constructor
+ '(#$(file-append package "/bin/exim")
+ "-bd" "-v" "-C"
+ #$(exim-computed-config-file package config-file))))
+ (stop #~(make-kill-destructor)))))))
+
+(define exim-activation
+ (match-lambda
+ (($ <exim-configuration> package config-file)
+ (with-imported-modules '((guix build utils))
+ #~(begin
+ (use-modules (guix build utils))
+
+ (let ((uid (passwd:uid (getpw "exim")))
+ (gid (group:gid (getgr "exim"))))
+ (mkdir-p "/var/spool/exim")
+ (chown "/var/spool/exim" uid gid))
+
+ (zero? (system* #$(file-append package "/bin/exim")
+ "-bV" "-C" #$(exim-computed-config-file package config-file))))))))
+
+(define exim-profile
+ (compose list exim-configuration-package))
+
+(define exim-service-type
+ (service-type
+ (name 'exim)
+ (extensions
+ (list (service-extension shepherd-root-service-type exim-shepherd-service)
+ (service-extension account-service-type (const %exim-accounts))
+ (service-extension activation-service-type exim-activation)
+ (service-extension profile-service-type exim-profile)
+ (service-extension mail-aliases-service-type (const '()))))))
+
+\f
+;;;
+;;; GNU Mailutils IMAP4 Daemon.
+;;;
+
+(define %default-imap4d-config-file
+ (plain-file "imap4d.conf" "server localhost {};\n"))
+
+(define-record-type* <imap4d-configuration>
+ imap4d-configuration make-imap4d-configuration imap4d-configuration?
+ (package imap4d-configuration-package
+ (default mailutils))
+ (config-file imap4d-configuration-config-file
+ (default %default-imap4d-config-file)))
+
+(define imap4d-shepherd-service
+ (match-lambda
+ (($ <imap4d-configuration> package config-file)
+ (list (shepherd-service
+ (provision '(imap4d))
+ (requirement '(networking syslogd))
+ (documentation "Run the imap4d daemon.")
+ (start (let ((imap4d (file-append package "/sbin/imap4d")))
+ #~(make-forkexec-constructor
+ (list #$imap4d "--daemon" "--foreground"
+ "--config-file" #$config-file))))
+ (stop #~(make-kill-destructor)))))))
+
+(define imap4d-service-type
+ (service-type
+ (name 'imap4d)
+ (description
+ "Run the GNU @command{imap4d} to serve e-mail messages through IMAP.")
+ (extensions
+ (list (service-extension
+ shepherd-root-service-type imap4d-shepherd-service)))
+ (default-value (imap4d-configuration))))
+
+\f
+;;;
+;;; Radicale.
+;;;
+
+(define-record-type* <radicale-configuration>
+ radicale-configuration make-radicale-configuration
+ radicale-configuration?
+ (package radicale-configuration-package
+ (default radicale))
+ (config-file radicale-configuration-config-file
+ (default %default-radicale-config-file)))
+
+(define %default-radicale-config-file
+ (plain-file "radicale.conf" "
+[auth]
+type = htpasswd
+htpasswd_filename = /var/lib/radicale/users
+htpasswd_encryption = plain
+
+[server]
+hosts = localhost:5232"))
+
+(define %radicale-accounts
+ (list (user-group
+ (name "radicale")
+ (system? #t))
+ (user-account
+ (name "radicale")
+ (group "radicale")
+ (system? #t)
+ (comment "Radicale Daemon")
+ (home-directory "/var/empty")
+ (shell (file-append shadow "/sbin/nologin")))))
+
+(define radicale-shepherd-service
+ (match-lambda
+ (($ <radicale-configuration> package config-file)
+ (list (shepherd-service
+ (provision '(radicale))
+ (documentation "Run the radicale daemon.")
+ (requirement '(networking))
+ (start #~(make-forkexec-constructor
+ (list #$(file-append package "/bin/radicale")
+ "-C" #$config-file)
+ #:user "radicale"
+ #:group "radicale"))
+ (stop #~(make-kill-destructor)))))))
+
+(define radicale-activation
+ (match-lambda
+ (($ <radicale-configuration> package config-file)
+ (with-imported-modules '((guix build utils))
+ #~(begin
+ (use-modules (guix build utils))
+ (let ((uid (passwd:uid (getpw "radicale")))
+ (gid (group:gid (getgr "radicale"))))
+ (mkdir-p "/var/lib/radicale/collections")
+ (chown "/var/lib/radicale" uid gid)
+ (chown "/var/lib/radicale/collections" uid gid)
+ (chmod "/var/lib/radicale" #o700)))))))
+
+(define radicale-service-type
+ (service-type
+ (name 'radicale)
+ (description "Run radicale, a small CalDAV and CardDAV server.")
+ (extensions
+ (list (service-extension shepherd-root-service-type radicale-shepherd-service)
+ (service-extension account-service-type (const %radicale-accounts))
+ (service-extension activation-service-type radicale-activation)))
+ (default-value (radicale-configuration))))