services: Add gmnisrv web service.
[jackhill/guix/guix.git] / gnu / services / messaging.scm
index 427e212..8f2f391 100644 (file)
@@ -1,7 +1,8 @@
 ;;; GNU Guix --- Functional package management for GNU
 ;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2017 Clément Lassieur <clement@lassieur.org>
+;;; Copyright © 2017, 2018 Clément Lassieur <clement@lassieur.org>
 ;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
 ;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
-;;; Copyright © 2015, 2017, 2018 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2015, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2018 Pierre-Antoine Rouby <contact@parouby.fr>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -21,6 +22,8 @@
 (define-module (gnu services messaging)
   #:use-module (gnu packages messaging)
   #:use-module (gnu packages admin)
 (define-module (gnu services messaging)
   #:use-module (gnu packages messaging)
   #:use-module (gnu packages admin)
+  #:use-module (gnu packages irc)
+  #:use-module (gnu packages tls)
   #:use-module (gnu services)
   #:use-module (gnu services shepherd)
   #:use-module (gnu services configuration)
   #:use-module (gnu services)
   #:use-module (gnu services shepherd)
   #:use-module (gnu services configuration)
@@ -29,6 +32,7 @@
   #:use-module (guix modules)
   #:use-module (guix records)
   #:use-module (guix packages)
   #:use-module (guix modules)
   #:use-module (guix records)
   #:use-module (guix packages)
+  #:use-module (guix deprecation)
   #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-35)
   #:use-module (ice-9 match)
   #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-35)
   #:use-module (ice-9 match)
             bitlbee-configuration
             bitlbee-configuration?
             bitlbee-service
             bitlbee-configuration
             bitlbee-configuration?
             bitlbee-service
-            bitlbee-service-type))
+            bitlbee-service-type
+
+            quassel-configuration
+            quassel-service-type))
 
 ;;; Commentary:
 ;;;
 
 ;;; Commentary:
 ;;;
                  "_")))
 
 (define (serialize-field field-name val)
                  "_")))
 
 (define (serialize-field field-name val)
-  (format #t "~a = ~a;\n" (uglify-field-name field-name) val))
+  #~(format #f "~a = ~a;\n" #$(uglify-field-name field-name) #$val))
 (define (serialize-field-list field-name val)
 (define (serialize-field-list field-name val)
-  (serialize-field field-name
-                   (with-output-to-string
-                     (lambda ()
-                       (format #t "{\n")
-                       (for-each (lambda (x)
-                                   (format #t "~a;\n" x))
-                                 val)
-                       (format #t "}")))))
+  (serialize-field field-name #~(format #f "{\n~@{~a;\n~}}" #$@val)))
 
 (define (serialize-boolean field-name val)
   (serialize-field field-name (if val "true" "false")))
 
 (define (serialize-boolean field-name val)
   (serialize-field field-name (if val "true" "false")))
 (define (non-negative-integer? val)
   (and (exact-integer? val) (not (negative? val))))
 (define (serialize-non-negative-integer field-name val)
 (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))
+  (serialize-field field-name (number->string val)))
 (define-maybe non-negative-integer)
 
 (define (non-negative-integer-list? val)
   (and (list? val) (and-map non-negative-integer? val)))
 (define (serialize-non-negative-integer-list field-name val)
 (define-maybe non-negative-integer)
 
 (define (non-negative-integer-list? val)
   (and (list? val) (and-map non-negative-integer? val)))
 (define (serialize-non-negative-integer-list field-name val)
-  (serialize-field-list field-name val))
+  (serialize-field-list field-name (map number->string val)))
 (define-maybe non-negative-integer-list)
 
 (define (enclose-quotes s)
 (define-maybe non-negative-integer-list)
 
 (define (enclose-quotes s)
-  (format #f "\"~a\"" s))
+  #~(string-append "\"" #$s "\""))
 (define (serialize-string field-name val)
   (serialize-field field-name (enclose-quotes val)))
 (define-maybe string)
 (define (serialize-string field-name val)
   (serialize-field field-name (enclose-quotes val)))
 (define-maybe string)
   (serialize-string-list field-name val))
 (define-maybe file-name)
 
   (serialize-string-list field-name val))
 (define-maybe file-name)
 
+(define (file-object? val)
+  (or (file-like? val) (file-name? val)))
+(define (serialize-file-object field-name val)
+  (serialize-string field-name val))
+(define-maybe file-object)
+
+(define (file-object-list? val)
+  (and (list? val) (and-map file-object? val)))
+(define (serialize-file-object-list field-name val)
+  (serialize-string-list field-name val))
+(define-maybe file-object)
+
 (define (raw-content? val)
   (not (eq? val 'disabled)))
 (define (serialize-raw-content field-name val)
 (define (raw-content? val)
   (not (eq? val 'disabled)))
 (define (serialize-raw-content field-name val)
-  (format #t "~a" val))
+  val)
 (define-maybe raw-content)
 
 (define-configuration mod-muc-configuration
 (define-maybe raw-content)
 
 (define-configuration mod-muc-configuration
@@ -224,12 +236,12 @@ just joined the room."))
    "Path to your certificate file.")
 
   (capath
    "Path to your certificate file.")
 
   (capath
-   (file-name "/etc/ssl/certs")
+   (file-object "/etc/ssl/certs")
    "Path to directory containing root certificates that you wish Prosody to
 trust when verifying the certificates of remote servers.")
 
   (cafile
    "Path to directory containing root certificates that you wish Prosody to
 trust when verifying the certificates of remote servers.")
 
   (cafile
-   (maybe-file-name 'disabled)
+   (maybe-file-object 'disabled)
    "Path to a file containing root certificates that you wish Prosody to trust.
 Similar to @code{capath} but with all certificates concatenated together.")
 
    "Path to a file containing root certificates that you wish Prosody to trust.
 Similar to @code{capath} but with all certificates concatenated together.")
 
@@ -273,9 +285,8 @@ can create such a file with:
    (maybe-string 'disabled)
    "Password for encrypted private keys."))
 (define (serialize-ssl-configuration field-name val)
    (maybe-string 'disabled)
    "Password for encrypted private keys."))
 (define (serialize-ssl-configuration field-name val)
-  (format #t "ssl = {\n")
-  (serialize-configuration val ssl-configuration-fields)
-  (format #t "};\n"))
+  #~(format #f "ssl = {\n~a};\n"
+            #$(serialize-configuration val ssl-configuration-fields)))
 (define-maybe ssl-configuration)
 
 (define %default-modules-enabled
 (define-maybe ssl-configuration)
 
 (define %default-modules-enabled
@@ -303,20 +314,23 @@ can create such a file with:
   (define (virtualhost-configuration-list? val)
     (and (list? val) (and-map virtualhost-configuration? val)))
   (define (serialize-virtualhost-configuration-list l)
   (define (virtualhost-configuration-list? val)
     (and (list? val) (and-map virtualhost-configuration? val)))
   (define (serialize-virtualhost-configuration-list l)
-    (for-each
-     (lambda (val) (serialize-virtualhost-configuration val)) l))
+    #~(string-append
+       #$@(map (lambda (val)
+                 (serialize-virtualhost-configuration val)) l)))
 
   (define (int-component-configuration-list? val)
     (and (list? val) (and-map int-component-configuration? val)))
   (define (serialize-int-component-configuration-list l)
 
   (define (int-component-configuration-list? val)
     (and (list? val) (and-map int-component-configuration? val)))
   (define (serialize-int-component-configuration-list l)
-    (for-each
-     (lambda (val) (serialize-int-component-configuration val)) l))
+    #~(string-append
+       #$@(map (lambda (val)
+                 (serialize-int-component-configuration val)) l)))
 
   (define (ext-component-configuration-list? val)
     (and (list? val) (and-map ext-component-configuration? val)))
   (define (serialize-ext-component-configuration-list l)
 
   (define (ext-component-configuration-list? val)
     (and (list? val) (and-map ext-component-configuration? val)))
   (define (serialize-ext-component-configuration-list l)
-    (for-each
-     (lambda (val) (serialize-ext-component-configuration val)) l))
+    #~(string-append
+       #$@(map (lambda (val)
+                 (serialize-ext-component-configuration val)) l)))
 
   (define-all-configurations prosody-configuration
     (prosody
 
   (define-all-configurations prosody-configuration
     (prosody
@@ -331,7 +345,7 @@ can create such a file with:
      global)
 
     (plugin-paths
      global)
 
     (plugin-paths
-     (file-name-list '())
+     (file-object-list '())
      "Additional plugin directories.  They are searched in all the specified
 paths in order.  See @url{https://prosody.im/doc/plugins_directory}."
      global)
      "Additional plugin directories.  They are searched in all the specified
 paths in order.  See @url{https://prosody.im/doc/plugins_directory}."
      global)
@@ -372,7 +386,7 @@ should you want to disable them then add them to this list."
      common)
 
     (groups-file
      common)
 
     (groups-file
-     (file-name "/var/lib/prosody/sharedgroups.txt")
+     (file-object "/var/lib/prosody/sharedgroups.txt")
      "Path to a text file where the shared groups are defined.  If this path is
 empty then @samp{mod_groups} does nothing.  See
 @url{https://prosody.im/doc/modules/mod_groups}."
      "Path to a text file where the shared groups are defined.  If this path is
 empty then @samp{mod_groups} does nothing.  See
 @url{https://prosody.im/doc/modules/mod_groups}."
@@ -447,7 +461,7 @@ about using the hashed backend.  See also
     (log
      (maybe-string "*syslog")
      "Set logging options.  Advanced logging configuration is not yet supported
     (log
      (maybe-string "*syslog")
      "Set logging options.  Advanced logging configuration is not yet supported
-by the GuixSD Prosody Service.  See @url{https://prosody.im/doc/logging}."
+by the Prosody service.  See @url{https://prosody.im/doc/logging}."
      common)
 
     (pidfile
      common)
 
     (pidfile
@@ -566,8 +580,9 @@ See also @url{https://prosody.im/doc/modules/mod_muc}."
                '(domain))))
   (let ((domain (virtualhost-configuration-domain config))
         (rest (filter rest? virtualhost-configuration-fields)))
                '(domain))))
   (let ((domain (virtualhost-configuration-domain config))
         (rest (filter rest? virtualhost-configuration-fields)))
-    (format #t "VirtualHost \"~a\"\n" domain)
-    (serialize-configuration config rest)))
+    #~(string-append
+       #$(format #f "VirtualHost \"~a\"\n" domain)
+       #$(serialize-configuration config rest))))
 
 ;; Serialize Component line first.
 (define (serialize-int-component-configuration config)
 
 ;; Serialize Component line first.
 (define (serialize-int-component-configuration config)
@@ -577,8 +592,9 @@ See also @url{https://prosody.im/doc/modules/mod_muc}."
   (let ((hostname (int-component-configuration-hostname config))
         (plugin (int-component-configuration-plugin config))
         (rest (filter rest? int-component-configuration-fields)))
   (let ((hostname (int-component-configuration-hostname config))
         (plugin (int-component-configuration-plugin config))
         (rest (filter rest? int-component-configuration-fields)))
-    (format #t "Component \"~a\" \"~a\"\n" hostname plugin)
-    (serialize-configuration config rest)))
+    #~(string-append
+       #$(format #f "Component \"~a\" \"~a\"\n" hostname plugin)
+       #$(serialize-configuration config rest))))
 
 ;; Serialize Component line first.
 (define (serialize-ext-component-configuration config)
 
 ;; Serialize Component line first.
 (define (serialize-ext-component-configuration config)
@@ -587,22 +603,24 @@ See also @url{https://prosody.im/doc/modules/mod_muc}."
                '(hostname))))
   (let ((hostname (ext-component-configuration-hostname config))
         (rest (filter rest? ext-component-configuration-fields)))
                '(hostname))))
   (let ((hostname (ext-component-configuration-hostname config))
         (rest (filter rest? ext-component-configuration-fields)))
-    (format #t "Component \"~a\"\n" hostname)
-    (serialize-configuration config rest)))
+    #~(string-append
+       #$(format #f "Component \"~a\"\n" hostname)
+       #$(serialize-configuration config rest))))
 
 ;; Serialize virtualhosts and components last.
 (define (serialize-prosody-configuration config)
   (define (rest? field)
     (not (memq (configuration-field-name field)
                '(virtualhosts int-components ext-components))))
 
 ;; Serialize virtualhosts and components last.
 (define (serialize-prosody-configuration config)
   (define (rest? field)
     (not (memq (configuration-field-name field)
                '(virtualhosts int-components ext-components))))
-  (let ((rest (filter rest? prosody-configuration-fields)))
-    (serialize-configuration config rest))
-  (serialize-virtualhost-configuration-list
-   (prosody-configuration-virtualhosts config))
-  (serialize-int-component-configuration-list
-   (prosody-configuration-int-components config))
-  (serialize-ext-component-configuration-list
-   (prosody-configuration-ext-components config)))
+  #~(string-append
+     #$(let ((rest (filter rest? prosody-configuration-fields)))
+         (serialize-configuration config rest))
+     #$(serialize-virtualhost-configuration-list
+        (prosody-configuration-virtualhosts config))
+     #$(serialize-int-component-configuration-list
+        (prosody-configuration-int-components config))
+     #$(serialize-ext-component-configuration-list
+        (prosody-configuration-ext-components config))))
 
 (define-configuration opaque-prosody-configuration
   (prosody
 
 (define-configuration opaque-prosody-configuration
   (prosody
@@ -620,13 +638,20 @@ See also @url{https://prosody.im/doc/modules/mod_muc}."
                       (opaque-prosody-configuration-prosody config)
                       (prosody-configuration-prosody config)))
          (prosodyctl-bin (file-append prosody "/bin/prosodyctl"))
                       (opaque-prosody-configuration-prosody config)
                       (prosody-configuration-prosody config)))
          (prosodyctl-bin (file-append prosody "/bin/prosodyctl"))
+         (pid-file (prosody-configuration-pidfile config))
          (prosodyctl-action (lambda args
                               #~(lambda _
          (prosodyctl-action (lambda args
                               #~(lambda _
-                                  (zero? (system* #$prosodyctl-bin #$@args))))))
+                                  (invoke #$prosodyctl-bin #$@args)
+                                  (match '#$args
+                                    (("start")
+                                     (call-with-input-file #$pid-file read))
+                                    (_ #t))))))
     (list (shepherd-service
            (documentation "Run the Prosody XMPP server")
            (provision '(prosody xmpp-daemon))
            (requirement '(networking syslogd user-processes))
     (list (shepherd-service
            (documentation "Run the Prosody XMPP server")
            (provision '(prosody xmpp-daemon))
            (requirement '(networking syslogd user-processes))
+           (modules `((ice-9 match)
+                      ,@%default-modules))
            (start (prosodyctl-action "start"))
            (stop (prosodyctl-action "stop"))))))
 
            (start (prosodyctl-action "start"))
            (stop (prosodyctl-action "stop"))))))
 
@@ -646,13 +671,12 @@ See also @url{https://prosody.im/doc/modules/mod_muc}."
          (default-certs-dir "/etc/prosody/certs")
          (data-path (prosody-configuration-data-path config))
          (pidfile-dir (dirname (prosody-configuration-pidfile config)))
          (default-certs-dir "/etc/prosody/certs")
          (data-path (prosody-configuration-data-path config))
          (pidfile-dir (dirname (prosody-configuration-pidfile config)))
-         (config-str
-          (if (opaque-prosody-configuration? config)
-              (opaque-prosody-configuration-prosody.cfg.lua config)
-              (with-output-to-string
-                (lambda ()
-                  (serialize-prosody-configuration config)))))
-         (config-file (plain-file "prosody.cfg.lua" config-str)))
+         (config-str (if (opaque-prosody-configuration? config)
+                         (opaque-prosody-configuration-prosody.cfg.lua config)
+                         #~(begin
+                             (use-modules (ice-9 format))
+                             #$(serialize-prosody-configuration config))))
+         (config-file (mixed-text-file "prosody.cfg.lua" config-str)))
     #~(begin
         (use-modules (guix build utils))
         (define %user (getpw "prosody"))
     #~(begin
         (use-modules (guix build utils))
         (define %user (getpw "prosody"))
@@ -681,7 +705,14 @@ See also @url{https://prosody.im/doc/modules/mod_muc}."
                        (service-extension account-service-type
                                           (const %prosody-accounts))
                        (service-extension activation-service-type
                        (service-extension account-service-type
                                           (const %prosody-accounts))
                        (service-extension activation-service-type
-                                          prosody-activation)))))
+                                          prosody-activation)))
+                (default-value (prosody-configuration
+                                (virtualhosts
+                                 (list
+                                  (virtualhost-configuration
+                                   (domain "localhost"))))))
+                (description
+                 "Run Prosody, a modern XMPP communication server.")))
 
 ;; A little helper to make it easier to document all those fields.
 (define (generate-documentation)
 
 ;; A little helper to make it easier to document all those fields.
 (define (generate-documentation)
@@ -773,20 +804,25 @@ string, you could instantiate a prosody service like this:
              (default "127.0.0.1"))
   (port bitlbee-configuration-port
         (default 6667))
              (default "127.0.0.1"))
   (port bitlbee-configuration-port
         (default 6667))
+  (plugins bitlbee-plugins
+           (default '()))
   (extra-settings bitlbee-configuration-extra-settings
                   (default "")))
 
 (define bitlbee-shepherd-service
   (match-lambda
   (extra-settings bitlbee-configuration-extra-settings
                   (default "")))
 
 (define bitlbee-shepherd-service
   (match-lambda
-    (($ <bitlbee-configuration> bitlbee interface port extra-settings)
-     (let ((conf (plain-file "bitlbee.conf"
-                             (string-append "
+    (($ <bitlbee-configuration> bitlbee interface port
+                                plugins extra-settings)
+     (let* ((plugins (directory-union "bitlbee-plugins" plugins))
+            (conf    (mixed-text-file "bitlbee.conf"
+                                  "
   [settings]
   User = bitlbee
   ConfigDir = /var/lib/bitlbee
   DaemonInterface = " interface "
   DaemonPort = " (number->string port) "
   [settings]
   User = bitlbee
   ConfigDir = /var/lib/bitlbee
   DaemonInterface = " interface "
   DaemonPort = " (number->string port) "
-" extra-settings))))
+  PluginDir = " plugins "/lib/bitlbee
+" extra-settings)))
 
        (with-imported-modules (source-module-closure
                                '((gnu build shepherd)
 
        (with-imported-modules (source-module-closure
                                '((gnu build shepherd)
@@ -805,6 +841,11 @@ string, you could instantiate a prosody service like this:
                           (list #$(file-append bitlbee "/sbin/bitlbee")
                                 "-n" "-F" "-u" "bitlbee" "-c" #$conf)
 
                           (list #$(file-append bitlbee "/sbin/bitlbee")
                                 "-n" "-F" "-u" "bitlbee" "-c" #$conf)
 
+                          ;; Allow 'bitlbee-purple' to use libpurple plugins.
+                          #:environment-variables
+                          (list (string-append "PURPLE_PLUGIN_PATH="
+                                               #$plugins "/lib/purple-2"))
+
                           #:pid-file "/var/run/bitlbee.pid"
                           #:mappings (list (file-system-mapping
                                             (source "/var/lib/bitlbee")
                           #:pid-file "/var/run/bitlbee.pid"
                           #:mappings (list (file-system-mapping
                                             (source "/var/lib/bitlbee")
@@ -848,9 +889,10 @@ string, you could instantiate a prosody service like this:
                  "Run @url{http://bitlbee.org,BitlBee}, a daemon that acts as
 a gateway between IRC and chat networks.")))
 
                  "Run @url{http://bitlbee.org,BitlBee}, a daemon that acts as
 a gateway between IRC and chat networks.")))
 
-(define* (bitlbee-service #:key (bitlbee bitlbee) ;deprecated
-                          (interface "127.0.0.1") (port 6667)
-                          (extra-settings ""))
+(define-deprecated (bitlbee-service #:key (bitlbee bitlbee)
+                                    (interface "127.0.0.1") (port 6667)
+                                    (extra-settings ""))
+  bitlbee-service-type
   "Return a service that runs @url{http://bitlbee.org,BitlBee}, a daemon that
 acts as a gateway between IRC and chat networks.
 
   "Return a service that runs @url{http://bitlbee.org,BitlBee}, a daemon that
 acts as a gateway between IRC and chat networks.
 
@@ -866,3 +908,86 @@ configuration file."
             (bitlbee bitlbee)
             (interface interface) (port port)
             (extra-settings extra-settings))))
             (bitlbee bitlbee)
             (interface interface) (port port)
             (extra-settings extra-settings))))
+
+\f
+;;;
+;;; Quassel.
+;;;
+
+(define-record-type* <quassel-configuration>
+  quassel-configuration make-quassel-configuration
+  quassel-configuration?
+  (quassel quassel-configuration-quassel
+           (default quassel))
+  (interface quassel-configuration-interface
+             (default "::,0.0.0.0"))
+  (port quassel-configuration-port
+        (default 4242))
+  (loglevel quassel-configuration-loglevel
+            (default "Info")))
+
+(define quassel-shepherd-service
+  (match-lambda
+    (($ <quassel-configuration> quassel interface port loglevel)
+     (with-imported-modules (source-module-closure
+                              '((gnu build shepherd)
+                                (gnu system file-systems)))
+       (list (shepherd-service
+               (provision '(quassel))
+               (requirement '(user-processes networking))
+               (modules '((gnu build shepherd)
+                          (gnu system file-systems)))
+               (start #~(make-forkexec-constructor/container
+                          (list #$(file-append quassel "/bin/quasselcore")
+                                "--configdir=/var/lib/quassel"
+                                "--logfile=/var/log/quassel/core.log"
+                                (string-append "--loglevel=" #$loglevel)
+                                (string-append "--port=" (number->string #$port))
+                                (string-append "--listen=" #$interface))
+                          #:mappings (list (file-system-mapping
+                                             (source "/var/lib/quassel")
+                                             (target source)
+                                             (writable? #t))
+                                           (file-system-mapping
+                                             (source "/var/log/quassel")
+                                             (target source)
+                                             (writable? #t)))))
+               (stop  #~(make-kill-destructor))))))))
+
+(define %quassel-account
+  (list (user-group (name "quassel") (system? #t))
+        (user-account
+          (name "quasselcore")
+          (group "quassel")
+          (system? #t)
+          (comment "Quassel daemon user")
+          (home-directory "/var/lib/quassel")
+          (shell (file-append shadow "/sbin/nologin")))))
+
+(define %quassel-activation
+  #~(begin
+      (use-modules (guix build utils))
+      (mkdir-p "/var/lib/quassel")
+      (mkdir-p "/var/log/quassel")
+      (let ((cert "/var/lib/quassel/quasselCert.pem"))
+        (unless (file-exists? cert)
+          (invoke #$(file-append openssl "/bin/openssl")
+                  "req" "-x509" "-nodes" "-batch" "-days" "680" "-newkey"
+                  "rsa" "-keyout" cert "-out" cert)))))
+
+(define quassel-service-type
+  (service-type (name 'quassel)
+                (extensions
+                  (list (service-extension shepherd-root-service-type
+                                           quassel-shepherd-service)
+                        (service-extension profile-service-type
+                                           (compose list quassel-configuration-quassel))
+                        (service-extension account-service-type
+                                           (const %quassel-account))
+                        (service-extension activation-service-type
+                                           (const %quassel-activation))))
+                (default-value (quassel-configuration))
+                (description
+                 "Run @url{https://quassel-irc.org/,quasselcore}, the backend
+for the distributed IRC client quassel, which allows you to connect from
+multiple machines simultaneously.")))