Subject: bug#26306: exim service patches (including mail-aliases-service-type)

From: Carlo Zancanaro <[email-masked]>
Date: Thu, 20 Apr 2017 00:46:57 +1000

Hey Ludo!

On Fri, Apr 07 2017, Ludovic Courtès wrote:
> Could you send updated patches?

I finally got around to fixing up my patches! How exciting. I've also
added an extra patch for updating guix.texi.

The MAIN PANIC DIE is still an issue, but I'm fairly sure it only
affects the tests.

Carlo

From e2445e69f24baed9f0a625dcd6ff2dbfd8ec7f45 Mon Sep 17 00:00:00 2001
From: Carlo Zancanaro <[email-masked]>
Date: Thu, 30 Mar 2017 15:25:58 +1100
Subject: [PATCH 1/4] services: Add mail-aliases-service-type.

* gnu/services/mail.scm (mail-aliases-etc): New procedure.
(mail-aliases-service-type): New variable.
---
 gnu/services/mail.scm | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/gnu/services/mail.scm b/gnu/services/mail.scm
index 20043d751..6325589bf 100644
--- a/gnu/services/mail.scm
+++ b/gnu/services/mail.scm
@@ -35,6 +35,7 @@
   #:use-module (guix gexp)
   #:use-module (ice-9 match)
   #:use-module (ice-9 format)
+  #:use-module (srfi srfi-1)
   #:export (dovecot-service
             dovecot-service-type
             dovecot-configuration
@@ -57,6 +58,8 @@
             opensmtpd-service-type
             %default-opensmtpd-config-file
 
+            mail-aliases-service-type
+
             exim-configuration
             exim-configuration?
             exim-service-type
@@ -1662,6 +1665,31 @@ accept from local for any relay
 
 
 ;;;
+;;; 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)))
+
+
+;;;
 ;;; Exim.
 ;;;
 
-- 
2.12.2

From ddfb6544a2b5404a4845179462e5388740428099 Mon Sep 17 00:00:00 2001
From: Carlo Zancanaro <[email-masked]>
Date: Thu, 30 Mar 2017 15:28:26 +1100
Subject: [PATCH 2/4] services: Make exim-service-type use
 mail-aliases-service-type

* gnu/services/mail.scm (exim-configuration)[aliases]: Remove field.
(exim-activation, exim-shepherd-service): Remove alias from matches.
(exim-etc): Remove procedure.
(exim-service-type): Extend mail-aliases-service-type instead of
etc-service-type.
---
 gnu/services/mail.scm | 24 ++++--------------------
 1 file changed, 4 insertions(+), 20 deletions(-)

diff --git a/gnu/services/mail.scm b/gnu/services/mail.scm
index 6325589bf..6305f06f8 100644
--- a/gnu/services/mail.scm
+++ b/gnu/services/mail.scm
@@ -1699,9 +1699,7 @@ accept from local for any relay
   (package       exim-configuration-package ;<package>
                  (default exim))
   (config-file   exim-configuration-config-file ;file-like
-                 (default #f))
-  (aliases       exim-configuration-aliases ;; list of lists
-                 (default '())))
+                 (default #f)))
 
 (define %exim-accounts
   (list (user-group
@@ -1728,7 +1726,7 @@ exim_group = exim
 
 (define exim-shepherd-service
   (match-lambda
-    (($ <exim-configuration> package config-file aliases)
+    (($ <exim-configuration> package config-file)
      (list (shepherd-service
             (provision '(exim mta))
             (documentation "Run the exim daemon.")
@@ -1741,7 +1739,7 @@ exim_group = exim
 
 (define exim-activation
   (match-lambda
-    (($ <exim-configuration> package config-file aliases)
+    (($ <exim-configuration> package config-file)
      (with-imported-modules '((guix build utils))
        #~(begin
            (use-modules (guix build utils))
@@ -1754,20 +1752,6 @@ exim_group = exim
            (zero? (system* #$(file-append package "/bin/exim")
                            "-bV" "-C" #$(exim-computed-config-file package config-file))))))))
 
-(define exim-etc
-  (match-lambda
-    (($ <exim-configuration> package config-file 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 (lambda (entry)
-                                              (list (car entry)
-                                                    (string-join (cdr entry) ",")))
-                                            aliases))))))))
-
 (define exim-profile
   (compose list exim-configuration-package))
 
@@ -1779,4 +1763,4 @@ exim_group = exim
           (service-extension account-service-type (const %exim-accounts))
           (service-extension activation-service-type exim-activation)
           (service-extension profile-service-type exim-profile)
-          (service-extension etc-service-type exim-etc)))))
+          (service-extension mail-aliases-service-type (const '()))))))
-- 
2.12.2

From 763abe687f7d25f7c6d6cbbc161fca08910b1b3c Mon Sep 17 00:00:00 2001
From: Carlo Zancanaro <[email-masked]>
Date: Thu, 30 Mar 2017 15:13:56 +1100
Subject: [PATCH 3/4] tests: mail: Add test for exim

* gnu/tests/mail.scm (%exim-os, %test-exim): New variables.
(run-exim-test): New procedure.
---
 gnu/tests/mail.scm | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 134 insertions(+), 1 deletion(-)

diff --git a/gnu/tests/mail.scm b/gnu/tests/mail.scm
index d5c08b7f0..247f4f667 100644
--- a/gnu/tests/mail.scm
+++ b/gnu/tests/mail.scm
@@ -1,5 +1,6 @@
 ;;; GNU Guix --- Functional package management for GNU
 ;;; Copyright © 2016 Sou Bunnbu <[email-masked]>
+;;; Copyright © 2017 Carlo Zancanaro <[email-masked]>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -26,7 +27,9 @@
   #:use-module (guix gexp)
   #:use-module (guix monads)
   #:use-module (guix store)
-  #:export (%test-opensmtpd))
+  #:use-module (ice-9 ftw)
+  #:export (%test-opensmtpd
+            %test-exim))
 
 (define %opensmtpd-os
   (simple-operating-system
@@ -146,3 +149,133 @@ accept from any for local deliver to mbox
    (name "opensmtpd")
    (description "Send an email to a running OpenSMTPD server.")
    (value (run-opensmtpd-test))))
+
+
+(define %exim-os
+  (simple-operating-system
+   (dhcp-client-service)
+   (service mail-aliases-service-type '())
+   (service exim-service-type
+            (exim-configuration
+             (config-file
+              (plain-file "exim.conf" "
+primary_hostname = komputilo
+domainlist local_domains = @
+domainlist relay_to_domains =
+hostlist   relay_from_hosts = localhost
+
+never_users =
+
+acl_smtp_rcpt = acl_check_rcpt
+acl_smtp_data = acl_check_data
+
+begin acl
+
+acl_check_rcpt:
+  accept
+acl_check_data:
+  accept
+"))))))
+
+(define (run-exim-test)
+  "Return a test of an OS running an Exim service."
+  (mlet* %store-monad ((command (system-qemu-image/shared-store-script
+                                 (marionette-operating-system
+                                  %exim-os
+                                  #:imported-modules '((gnu services herd)))
+                                 #:graphic? #f)))
+    (define test
+      (with-imported-modules '((gnu build marionette)
+                               (ice-9 ftw))
+        #~(begin
+            (use-modules (rnrs base)
+                         (srfi srfi-64)
+                         (ice-9 ftw)
+                         (ice-9 rdelim)
+                         (ice-9 regex)
+                         (gnu build marionette))
+
+            (define marionette
+              (make-marionette
+               ;; Enable TCP forwarding of the guest's port 25.
+               '(#$command "-net" "user,hostfwd=tcp::1025-:25")))
+
+            (define (read-reply-code port)
+              "Read a SMTP reply from PORT and return its reply code."
+              (let* ((line      (read-line port))
+                     (mo        (string-match "([0-9]+)([ -]).*" line))
+                     (code      (string->number (match:substring mo 1)))
+                     (finished? (string= " " (match:substring mo 2))))
+                (if finished?
+                    code
+                    (read-reply-code port))))
+
+            (define smtp (socket AF_INET SOCK_STREAM 0))
+            (define addr (make-socket-address AF_INET INADDR_LOOPBACK 1025))
+
+            (mkdir #$output)
+            (chdir #$output)
+
+            (test-begin "exim")
+
+            (test-assert "service is running"
+              (marionette-eval
+               '(begin
+                  (use-modules (gnu services herd))
+                  (start-service 'exim)
+                  #t)
+               marionette))
+
+            (sleep 1) ;; give the service time to start talking
+
+            (connect smtp addr)
+            ;; Be greeted.
+            (test-eq "greeting received"
+              220 (read-reply-code smtp))
+            ;; Greet the server.
+            (write-line "EHLO somehost" smtp)
+            (test-eq "greeting successful"
+              250 (read-reply-code smtp))
+            ;; Set sender email.
+            (write-line "MAIL FROM: [email-masked]" smtp)
+            (test-eq "sender set"
+              250 (read-reply-code smtp)) ;250
+            ;; Set recipient email.
+            (write-line "RCPT TO: root@komputilo" smtp)
+            (test-eq "recipient set"
+              250 (read-reply-code smtp)) ;250
+            ;; Send message.
+            (write-line "DATA" smtp)
+            (test-eq "data begun"
+              354 (read-reply-code smtp)) ;354
+            (write-line "Subject: Hello" smtp)
+            (newline smtp)
+            (write-line "Nice to meet you!" smtp)
+            (write-line "." smtp)
+            (test-eq "message sent"
+              250 (read-reply-code smtp)) ;250
+            ;; Say goodbye.
+            (write-line "QUIT" smtp)
+            (test-eq "quit successful"
+              221 (read-reply-code smtp)) ;221
+            (close smtp)
+
+            (test-eq "the email is received"
+              1
+              (marionette-eval
+               '(begin
+                  (use-modules (ice-9 ftw))
+                  (length (scandir "/var/spool/exim/msglog"
+                                   (lambda (x) (not (string-prefix? "." x))))))
+               marionette))
+
+            (test-end)
+            (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
+
+    (gexp->derivation "exim-test" test)))
+
+(define %test-exim
+  (system-test
+   (name "exim")
+   (description "Send an email to a running an Exim server.")
+   (value (run-exim-test))))
-- 
2.12.2

From f133eca18dbf1b6483050ebe9e15ba7eafc457f0 Mon Sep 17 00:00:00 2001
From: Carlo Zancanaro <[email-masked]>
Date: Wed, 19 Apr 2017 23:58:24 +1000
Subject: [PATCH 4/4] doc: Update exim service documentation

* doc/guix.texi (Mail Services): Update exim service documentation to remove
mail-aliases, add mail-aliases-service-type documentation.
---
 doc/guix.texi | 40 ++++++++++++++++++++++++++++++----------
 1 file changed, 30 insertions(+), 10 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index f2eba59d9..8cc2c43d0 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -12644,12 +12644,14 @@ should be an @code{exim-configuration} object as in this example:
 @example
 (service exim-service-type
          (exim-configuration
-           (config-file (local-file "./my-exim.conf"))
-           (aliases '(("postmaster" "bob")
-                      ("bob" "bob@@example.com" "bob@@example2.com")))))
+           (config-file (local-file "./my-exim.conf"))))
 @end example
 @end deffn
 
+In order to use an @code{exim-service-type} service you must also have a
+@code{mail-aliases-service-type} service present in your
+@code{operating-system} (even if it has no aliases).
+
 @deftp {Data Type} exim-configuration
 Data type representing the configuration of exim.
 
@@ -12664,16 +12666,34 @@ provided in @code{package}. The resulting configuration file is loaded
 after setting the @code{exim_user} and @code{exim_group} configuration
 variables.
 
-@item @code{aliases} (default: @code{'()})
-List of aliases to use when delivering mail on this system. The
-@code{car} of each list is used to match incoming mail, with the
-@code{cdr} of each list designating how to deliver it. There may be many
-delivery methods provided, in which case the mail is delivered to them
-all.
-
 @end table
 @end deftp
 
+@subsubheading Mail Aliases Service
+
+@deffn {Scheme Variable} mail-aliases-service-type
+This is the type of the service which provides @code{/etc/aliases},
+specifying how to deliver mail to users on this system.
+
+@example
+(service mail-aliases-service-type
+         '(("postmaster" "bob")
+           ("bob" "bob@@example.com" "bob@@example2.com")))
+@end example
+@end deffn
+
+The configuration for a @code{mail-aliases-service-type} service is an
+association list denoting how to deliver mail that comes to this
+system. Each entry is of the form @code{(alias addresses ...)}, with
+@code{alias} specifying the local alias and @code{addresses} specifying
+where to deliver this user's mail.
+
+The aliases aren't required to exist as users on the local system. In
+the above example, there doesn't need to be a @code{postmaster} entry in
+the @code{operating-system}'s @code{user-accounts} in order to deliver
+the @code{postmaster} mail to @code{bob} (which subsequently would
+deliver mail to @code{bob@@example.com} and @code{bob@@example2.com}).
+
 @node Messaging Services
 @subsubsection Messaging Services
 
-- 
2.12.2