gnu: base: Add greetd-service-type.
[jackhill/guix/guix.git] / gnu / services / messaging.scm
CommitLineData
78cef99b 1;;; GNU Guix --- Functional package management for GNU
bdcf0e6f 2;;; Copyright © 2017, 2018 Clément Lassieur <clement@lassieur.org>
e7c797f3 3;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
211fe3f6 4;;; Copyright © 2015, 2017-2020, 2022 Ludovic Courtès <ludo@gnu.org>
36d619e8 5;;; Copyright © 2018 Pierre-Antoine Rouby <contact@parouby.fr>
78cef99b
CL
6;;;
7;;; This file is part of GNU Guix.
8;;;
9;;; GNU Guix is free software; you can redistribute it and/or modify it
10;;; under the terms of the GNU General Public License as published by
11;;; the Free Software Foundation; either version 3 of the License, or (at
12;;; your option) any later version.
13;;;
14;;; GNU Guix is distributed in the hope that it will be useful, but
15;;; WITHOUT ANY WARRANTY; without even the implied warranty of
16;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17;;; GNU General Public License for more details.
18;;;
19;;; You should have received a copy of the GNU General Public License
20;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
21
22(define-module (gnu services messaging)
78cef99b 23 #:use-module (gnu packages admin)
47d73ac4 24 #:use-module (gnu packages base)
dcad57d5 25 #:use-module (gnu packages irc)
47d73ac4 26 #:use-module (gnu packages messaging)
dcad57d5 27 #:use-module (gnu packages tls)
78cef99b
CL
28 #:use-module (gnu services)
29 #:use-module (gnu services shepherd)
30 #:use-module (gnu services configuration)
31 #:use-module (gnu system shadow)
211fe3f6
LC
32 #:autoload (gnu build linux-container) (%namespaces)
33 #:use-module ((gnu system file-systems) #:select (file-system-mapping))
78cef99b 34 #:use-module (guix gexp)
f2bee421 35 #:use-module (guix modules)
78cef99b
CL
36 #:use-module (guix records)
37 #:use-module (guix packages)
65a67bf7 38 #:use-module (guix deprecation)
211fe3f6 39 #:use-module (guix least-authority)
78cef99b
CL
40 #:use-module (srfi srfi-1)
41 #:use-module (srfi srfi-35)
42 #:use-module (ice-9 match)
43 #:export (prosody-service-type
44 prosody-configuration
45 opaque-prosody-configuration
46
47 virtualhost-configuration
48 int-component-configuration
49 ext-component-configuration
50
51 mod-muc-configuration
52 ssl-configuration
53
533bc514 54 %default-modules-enabled
f2bee421
LC
55 prosody-configuration-pidfile
56
57 bitlbee-configuration
58 bitlbee-configuration?
dcad57d5
EF
59 bitlbee-service-type
60
61 quassel-configuration
62 quassel-service-type))
78cef99b
CL
63
64;;; Commentary:
65;;;
66;;; Messaging services.
67;;;
68;;; Code:
69
78cef99b
CL
70(define-syntax define-all-configurations
71 (lambda (stx)
e7c797f3
MO
72 (define-syntax-rule (id ctx parts ...)
73 "Assemble PARTS into a raw (unhygienic) identifier."
74 (datum->syntax ctx (symbol-append (syntax->datum parts) ...)))
78cef99b
CL
75 (define (make-pred arg)
76 (lambda (field target)
77 (and (memq (syntax->datum target) `(common ,arg)) field)))
78 (syntax-case stx ()
8cb1a49a 79 ;; TODO Also handle (field-type) form, without a default.
78cef99b
CL
80 ((_ stem (field (field-type def) doc target) ...)
81 (with-syntax (((new-field-type ...)
82 (map (lambda (field-type target)
83 (if (and (eq? 'common (syntax->datum target))
84 (not (string-prefix?
85 "maybe-"
86 (symbol->string
87 (syntax->datum field-type)))))
88 (id #'stem #'maybe- field-type) field-type))
89 #'(field-type ...) #'(target ...)))
90 ((new-def ...)
91 (map (lambda (def target)
92 (if (eq? 'common (syntax->datum target))
8cb1a49a 93 #'*unspecified* def))
78cef99b
CL
94 #'(def ...) #'(target ...)))
95 ((new-doc ...)
96 (map (lambda (doc target)
97 (if (eq? 'common (syntax->datum target))
98 "" doc))
99 #'(doc ...) #'(target ...))))
100 #`(begin
10999889 101 (define #,(id #'stem #'common-fields)
78cef99b 102 '(#,@(filter-map (make-pred #f) #'(field ...) #'(target ...))))
10999889 103 (define-configuration #,(id #'stem #'prosody-configuration)
78cef99b
CL
104 #,@(filter-map (make-pred 'global)
105 #'((field (field-type def) doc) ...)
106 #'(target ...)))
10999889 107 (define-configuration #,(id #'stem #'virtualhost-configuration)
78cef99b
CL
108 #,@(filter-map (make-pred 'virtualhost)
109 #'((field (new-field-type new-def) new-doc) ...)
110 #'(target ...)))
10999889 111 (define-configuration #,(id #'stem #'int-component-configuration)
78cef99b
CL
112 #,@(filter-map (make-pred 'int-component)
113 #'((field (new-field-type new-def) new-doc) ...)
114 #'(target ...)))
10999889 115 (define-configuration #,(id #'stem #'ext-component-configuration)
78cef99b
CL
116 #,@(filter-map (make-pred 'ext-component)
117 #'((field (new-field-type new-def) new-doc) ...)
118 #'(target ...)))))))))
119
120(define (uglify-field-name field-name)
121 (let ((str (symbol->string field-name)))
122 (string-join (string-split (if (string-suffix? "?" str)
123 (substring str 0 (1- (string-length str)))
124 str)
125 #\-)
126 "_")))
127
128(define (serialize-field field-name val)
bdcf0e6f 129 #~(format #f "~a = ~a;\n" #$(uglify-field-name field-name) #$val))
78cef99b 130(define (serialize-field-list field-name val)
bdcf0e6f 131 (serialize-field field-name #~(format #f "{\n~@{~a;\n~}}" #$@val)))
78cef99b
CL
132
133(define (serialize-boolean field-name val)
134 (serialize-field field-name (if val "true" "false")))
135(define-maybe boolean)
136
137(define (string-or-boolean? val)
138 (or (string? val) (boolean? val)))
139(define (serialize-string-or-boolean field-name val)
140 (if (string? val)
141 (serialize-string field-name val)
142 (serialize-boolean field-name val)))
143
144(define (non-negative-integer? val)
145 (and (exact-integer? val) (not (negative? val))))
146(define (serialize-non-negative-integer field-name val)
bdcf0e6f 147 (serialize-field field-name (number->string val)))
78cef99b
CL
148(define-maybe non-negative-integer)
149
150(define (non-negative-integer-list? val)
151 (and (list? val) (and-map non-negative-integer? val)))
152(define (serialize-non-negative-integer-list field-name val)
bdcf0e6f 153 (serialize-field-list field-name (map number->string val)))
78cef99b
CL
154(define-maybe non-negative-integer-list)
155
156(define (enclose-quotes s)
bdcf0e6f 157 #~(string-append "\"" #$s "\""))
78cef99b
CL
158(define (serialize-string field-name val)
159 (serialize-field field-name (enclose-quotes val)))
160(define-maybe string)
161
162(define (string-list? val)
163 (and (list? val)
164 (and-map (lambda (x)
165 (and (string? x) (not (string-index x #\,))))
166 val)))
167(define (serialize-string-list field-name val)
168 (serialize-field-list field-name (map enclose-quotes val)))
169(define-maybe string-list)
170
171(define (module-list? val)
172 (string-list? val))
173(define (serialize-module-list field-name val)
5cc6dcd7 174 (serialize-string-list field-name val))
78cef99b
CL
175(define-maybe module-list)
176
177(define (file-name? val)
178 (and (string? val)
179 (string-prefix? "/" val)))
180(define (serialize-file-name field-name val)
181 (serialize-string field-name val))
182(define-maybe file-name)
183
184(define (file-name-list? val)
185 (and (list? val) (and-map file-name? val)))
186(define (serialize-file-name-list field-name val)
187 (serialize-string-list field-name val))
9ef6d80e 188(define-maybe file-name-list)
78cef99b 189
bdcf0e6f
CL
190(define (file-object? val)
191 (or (file-like? val) (file-name? val)))
192(define (serialize-file-object field-name val)
193 (serialize-string field-name val))
194(define-maybe file-object)
195
196(define (file-object-list? val)
197 (and (list? val) (and-map file-object? val)))
198(define (serialize-file-object-list field-name val)
199 (serialize-string-list field-name val))
9ef6d80e 200(define-maybe file-object-list)
bdcf0e6f 201
274b9500 202(define (raw-content? val)
8cb1a49a 203 (not (unspecified? val)))
274b9500 204(define (serialize-raw-content field-name val)
bdcf0e6f 205 val)
274b9500
CL
206(define-maybe raw-content)
207
78cef99b
CL
208(define-configuration mod-muc-configuration
209 (name
210 (string "Prosody Chatrooms")
211 "The name to return in service discovery responses.")
212
213 (restrict-room-creation
214 (string-or-boolean #f)
215 "If @samp{#t}, this will only allow admins to create new chatrooms.
216Otherwise anyone can create a room. The value @samp{\"local\"} restricts room
217creation to users on the service's parent domain. E.g. @samp{user@@example.com}
218can create rooms on @samp{rooms.example.com}. The value @samp{\"admin\"}
219restricts to service administrators only.")
220
221 (max-history-messages
222 (non-negative-integer 20)
223 "Maximum number of history messages that will be sent to the member that has
224just joined the room."))
225(define (serialize-mod-muc-configuration field-name val)
226 (serialize-configuration val mod-muc-configuration-fields))
227(define-maybe mod-muc-configuration)
228
229(define-configuration ssl-configuration
230 (protocol
8cb1a49a 231 maybe-string
78cef99b
CL
232 "This determines what handshake to use.")
233
234 (key
8cb1a49a 235 maybe-file-name
5cc6dcd7 236 "Path to your private key file.")
78cef99b
CL
237
238 (certificate
8cb1a49a 239 maybe-file-name
5cc6dcd7 240 "Path to your certificate file.")
78cef99b
CL
241
242 (capath
bdcf0e6f 243 (file-object "/etc/ssl/certs")
78cef99b
CL
244 "Path to directory containing root certificates that you wish Prosody to
245trust when verifying the certificates of remote servers.")
246
247 (cafile
8cb1a49a 248 maybe-file-object
78cef99b
CL
249 "Path to a file containing root certificates that you wish Prosody to trust.
250Similar to @code{capath} but with all certificates concatenated together.")
251
252 (verify
8cb1a49a 253 maybe-string-list
78cef99b
CL
254 "A list of verification options (these mostly map to OpenSSL's
255@code{set_verify()} flags).")
256
257 (options
8cb1a49a 258 maybe-string-list
78cef99b
CL
259 "A list of general options relating to SSL/TLS. These map to OpenSSL's
260@code{set_options()}. For a full list of options available in LuaSec, see the
261LuaSec source.")
262
263 (depth
8cb1a49a 264 maybe-non-negative-integer
78cef99b
CL
265 "How long a chain of certificate authorities to check when looking for a
266trusted root certificate.")
267
268 (ciphers
8cb1a49a 269 maybe-string
78cef99b
CL
270 "An OpenSSL cipher string. This selects what ciphers Prosody will offer to
271clients, and in what order.")
272
273 (dhparam
8cb1a49a 274 maybe-file-name
78cef99b
CL
275 "A path to a file containing parameters for Diffie-Hellman key exchange. You
276can create such a file with:
277@code{openssl dhparam -out /etc/prosody/certs/dh-2048.pem 2048}")
278
279 (curve
8cb1a49a 280 maybe-string
78cef99b
CL
281 "Curve for Elliptic curve Diffie-Hellman. Prosody's default is
282@samp{\"secp384r1\"}.")
283
284 (verifyext
8cb1a49a 285 maybe-string-list
78cef99b
CL
286 "A list of \"extra\" verification options.")
287
288 (password
8cb1a49a 289 maybe-string
78cef99b
CL
290 "Password for encrypted private keys."))
291(define (serialize-ssl-configuration field-name val)
bdcf0e6f
CL
292 #~(format #f "ssl = {\n~a};\n"
293 #$(serialize-configuration val ssl-configuration-fields)))
78cef99b
CL
294(define-maybe ssl-configuration)
295
296(define %default-modules-enabled
297 '("roster"
298 "saslauth"
299 "tls"
300 "dialback"
301 "disco"
5cc6dcd7 302 "carbons"
78cef99b 303 "private"
5cc6dcd7 304 "blocklist"
78cef99b
CL
305 "vcard"
306 "version"
307 "uptime"
308 "time"
309 "ping"
310 "pep"
311 "register"
312 "admin_adhoc"))
313
314;; Guile bug. Use begin wrapper, because otherwise virtualhost-configuration
315;; is assumed to be a function. See
316;; https://www.gnu.org/software/guile/manual/html_node/R6RS-Incompatibilities.html
317(begin
318 (define (virtualhost-configuration-list? val)
319 (and (list? val) (and-map virtualhost-configuration? val)))
320 (define (serialize-virtualhost-configuration-list l)
bdcf0e6f
CL
321 #~(string-append
322 #$@(map (lambda (val)
323 (serialize-virtualhost-configuration val)) l)))
78cef99b
CL
324
325 (define (int-component-configuration-list? val)
326 (and (list? val) (and-map int-component-configuration? val)))
327 (define (serialize-int-component-configuration-list l)
bdcf0e6f
CL
328 #~(string-append
329 #$@(map (lambda (val)
330 (serialize-int-component-configuration val)) l)))
78cef99b
CL
331
332 (define (ext-component-configuration-list? val)
333 (and (list? val) (and-map ext-component-configuration? val)))
334 (define (serialize-ext-component-configuration-list l)
bdcf0e6f
CL
335 #~(string-append
336 #$@(map (lambda (val)
337 (serialize-ext-component-configuration val)) l)))
78cef99b
CL
338
339 (define-all-configurations prosody-configuration
340 (prosody
892f1b72 341 (file-like prosody)
78cef99b
CL
342 "The Prosody package."
343 global)
344
345 (data-path
346 (file-name "/var/lib/prosody")
347 "Location of the Prosody data storage directory. See
7459cb93 348@url{https://prosody.im/doc/configure}."
78cef99b
CL
349 global)
350
351 (plugin-paths
bdcf0e6f 352 (file-object-list '())
78cef99b 353 "Additional plugin directories. They are searched in all the specified
7459cb93 354paths in order. See @url{https://prosody.im/doc/plugins_directory}."
78cef99b
CL
355 global)
356
5cc6dcd7
CL
357 (certificates
358 (file-name "/etc/prosody/certs")
359 "Every virtual host and component needs a certificate so that clients and
360servers can securely verify its identity. Prosody will automatically load
361certificates/keys from the directory specified here."
362 global)
363
78cef99b
CL
364 (admins
365 (string-list '())
366 "This is a list of accounts that are admins for the server. Note that you
7459cb93
TGR
367must create the accounts separately. See @url{https://prosody.im/doc/admins} and
368@url{https://prosody.im/doc/creating_accounts}.
78cef99b
CL
369Example: @code{(admins '(\"user1@@example.com\" \"user2@@example.net\"))}"
370 common)
371
372 (use-libevent?
373 (boolean #f)
374 "Enable use of libevent for better performance under high load. See
7459cb93 375@url{https://prosody.im/doc/libevent}."
78cef99b
CL
376 common)
377
378 (modules-enabled
379 (module-list %default-modules-enabled)
380 "This is the list of modules Prosody will load on startup. It looks for
381@code{mod_modulename.lua} in the plugins folder, so make sure that exists too.
19ff1f26 382Documentation on modules can be found at:
7459cb93 383@url{https://prosody.im/doc/modules}."
78cef99b
CL
384 common)
385
386 (modules-disabled
387 (string-list '())
388 "@samp{\"offline\"}, @samp{\"c2s\"} and @samp{\"s2s\"} are auto-loaded, but
389should you want to disable them then add them to this list."
390 common)
391
392 (groups-file
bdcf0e6f 393 (file-object "/var/lib/prosody/sharedgroups.txt")
78cef99b
CL
394 "Path to a text file where the shared groups are defined. If this path is
395empty then @samp{mod_groups} does nothing. See
7459cb93 396@url{https://prosody.im/doc/modules/mod_groups}."
78cef99b
CL
397 common)
398
399 (allow-registration?
400 (boolean #f)
401 "Disable account creation by default, for security. See
7459cb93 402@url{https://prosody.im/doc/creating_accounts}."
78cef99b
CL
403 common)
404
405 (ssl
406 (maybe-ssl-configuration (ssl-configuration))
407 "These are the SSL/TLS-related settings. Most of them are disabled so to
408use Prosody's defaults. If you do not completely understand these options, do
409not add them to your config, it is easy to lower the security of your server
7459cb93 410using them. See @url{https://prosody.im/doc/advanced_ssl_config}."
78cef99b
CL
411 common)
412
413 (c2s-require-encryption?
414 (boolean #f)
415 "Whether to force all client-to-server connections to be encrypted or not.
7459cb93 416See @url{https://prosody.im/doc/modules/mod_tls}."
78cef99b
CL
417 common)
418
e30038da
CL
419 (disable-sasl-mechanisms
420 (string-list '("DIGEST-MD5"))
421 "Set of mechanisms that will never be offered. See
422@url{https://prosody.im/doc/modules/mod_saslauth}."
423 common)
424
78cef99b
CL
425 (s2s-require-encryption?
426 (boolean #f)
427 "Whether to force all server-to-server connections to be encrypted or not.
7459cb93 428See @url{https://prosody.im/doc/modules/mod_tls}."
78cef99b
CL
429 common)
430
431 (s2s-secure-auth?
432 (boolean #f)
433 "Whether to require encryption and certificate authentication. This
434provides ideal security, but requires servers you communicate with to support
435encryption AND present valid, trusted certificates. See
7459cb93 436@url{https://prosody.im/doc/s2s#security}."
78cef99b
CL
437 common)
438
439 (s2s-insecure-domains
440 (string-list '())
441 "Many servers don't support encryption or have invalid or self-signed
442certificates. You can list domains here that will not be required to
443authenticate using certificates. They will be authenticated using DNS. See
7459cb93 444@url{https://prosody.im/doc/s2s#security}."
78cef99b
CL
445 common)
446
447 (s2s-secure-domains
448 (string-list '())
449 "Even if you leave @code{s2s-secure-auth?} disabled, you can still require
450valid certificates for some domains by specifying a list here. See
7459cb93 451@url{https://prosody.im/doc/s2s#security}."
78cef99b
CL
452 common)
453
454 (authentication
455 (string "internal_plain")
456 "Select the authentication backend to use. The default provider stores
457passwords in plaintext and uses Prosody's configured data storage to store the
458authentication data. If you do not trust your server please see
7459cb93 459@url{https://prosody.im/doc/modules/mod_auth_internal_hashed} for information
78cef99b 460about using the hashed backend. See also
7459cb93 461@url{https://prosody.im/doc/authentication}"
78cef99b
CL
462 common)
463
464 ;; TODO: Handle more complicated log structures.
465 (log
466 (maybe-string "*syslog")
467 "Set logging options. Advanced logging configuration is not yet supported
59e80445 468by the Prosody service. See @url{https://prosody.im/doc/logging}."
78cef99b
CL
469 common)
470
471 (pidfile
472 (file-name "/var/run/prosody/prosody.pid")
7459cb93 473 "File to write pid in. See @url{https://prosody.im/doc/modules/mod_posix}."
78cef99b
CL
474 global)
475
1f6f1a07 476 (http-max-content-size
8cb1a49a 477 (maybe-non-negative-integer *unspecified*)
1f6f1a07
CL
478 "Maximum allowed size of the HTTP body (in bytes)."
479 common)
480
f59de6be 481 (http-external-url
8cb1a49a 482 (maybe-string *unspecified*)
f59de6be
CL
483 "Some modules expose their own URL in various ways. This URL is built
484from the protocol, host and port used. If Prosody sits behind a proxy, the
485public URL will be @code{http-external-url} instead. See
486@url{https://prosody.im/doc/http#external_url}."
487 common)
488
78cef99b
CL
489 (virtualhosts
490 (virtualhost-configuration-list
491 (list (virtualhost-configuration
492 (domain "localhost"))))
493 "A host in Prosody is a domain on which user accounts can be created. For
494example if you want your users to have addresses like
495@samp{\"john.smith@@example.com\"} then you need to add a host
496@samp{\"example.com\"}. All options in this list will apply only to this host.
497
498Note: the name \"virtual\" host is used in configuration to avoid confusion with
499the actual physical host that Prosody is installed on. A single Prosody
500instance can serve many domains, each one defined as a VirtualHost entry in
501Prosody's configuration. Conversely a server that hosts a single domain would
502have just one VirtualHost entry.
503
7459cb93 504See @url{https://prosody.im/doc/configure#virtual_host_settings}."
78cef99b
CL
505 global)
506
507 (int-components
508 (int-component-configuration-list '())
509 "Components are extra services on a server which are available to clients,
510usually on a subdomain of the main server (such as
511@samp{\"mycomponent.example.com\"}). Example components might be chatroom
512servers, user directories, or gateways to other protocols.
513
514Internal components are implemented with Prosody-specific plugins. To add an
515internal component, you simply fill the hostname field, and the plugin you wish
516to use for the component.
517
7459cb93 518See @url{https://prosody.im/doc/components}."
78cef99b
CL
519 global)
520
521 (ext-components
522 (ext-component-configuration-list '())
523 "External components use XEP-0114, which most standalone components
524support. To add an external component, you simply fill the hostname field. See
7459cb93 525@url{https://prosody.im/doc/components}."
78cef99b
CL
526 global)
527
528 (component-secret
529 (string (configuration-missing-field 'ext-component 'component-secret))
530 "Password which the component will use to log in."
531 ext-component)
532
533 (component-ports
534 (non-negative-integer-list '(5347))
535 "Port(s) Prosody listens on for component connections."
536 global)
537
538 (component-interface
539 (string "127.0.0.1")
540 "Interface Prosody listens on for component connections."
541 global)
542
543 (domain
544 (string (configuration-missing-field 'virtualhost 'domain))
545 "Domain you wish Prosody to serve."
546 virtualhost)
547
548 (hostname
549 (string (configuration-missing-field 'int-component 'hostname))
550 "Hostname of the component."
551 int-component)
552
553 (plugin
554 (string (configuration-missing-field 'int-component 'plugin))
555 "Plugin you wish to use for the component."
556 int-component)
557
558 (mod-muc
8cb1a49a 559 (maybe-mod-muc-configuration *unspecified*)
78cef99b
CL
560 "Multi-user chat (MUC) is Prosody's module for allowing you to create
561hosted chatrooms/conferences for XMPP users.
562
563General information on setting up and using multi-user chatrooms can be found
7459cb93 564in the \"Chatrooms\" documentation (@url{https://prosody.im/doc/chatrooms}),
78cef99b
CL
565which you should read if you are new to XMPP chatrooms.
566
7459cb93 567See also @url{https://prosody.im/doc/modules/mod_muc}."
78cef99b
CL
568 int-component)
569
570 (hostname
571 (string (configuration-missing-field 'ext-component 'hostname))
572 "Hostname of the component."
274b9500
CL
573 ext-component)
574
575 (raw-content
8cb1a49a 576 (maybe-raw-content *unspecified*)
274b9500
CL
577 "Raw content that will be added to the configuration file."
578 common)))
78cef99b
CL
579
580;; Serialize Virtualhost line first.
581(define (serialize-virtualhost-configuration config)
582 (define (rest? field)
583 (not (memq (configuration-field-name field)
584 '(domain))))
585 (let ((domain (virtualhost-configuration-domain config))
586 (rest (filter rest? virtualhost-configuration-fields)))
bdcf0e6f
CL
587 #~(string-append
588 #$(format #f "VirtualHost \"~a\"\n" domain)
589 #$(serialize-configuration config rest))))
78cef99b
CL
590
591;; Serialize Component line first.
592(define (serialize-int-component-configuration config)
593 (define (rest? field)
594 (not (memq (configuration-field-name field)
595 '(hostname plugin))))
596 (let ((hostname (int-component-configuration-hostname config))
597 (plugin (int-component-configuration-plugin config))
598 (rest (filter rest? int-component-configuration-fields)))
bdcf0e6f
CL
599 #~(string-append
600 #$(format #f "Component \"~a\" \"~a\"\n" hostname plugin)
601 #$(serialize-configuration config rest))))
78cef99b
CL
602
603;; Serialize Component line first.
604(define (serialize-ext-component-configuration config)
605 (define (rest? field)
606 (not (memq (configuration-field-name field)
607 '(hostname))))
608 (let ((hostname (ext-component-configuration-hostname config))
609 (rest (filter rest? ext-component-configuration-fields)))
bdcf0e6f
CL
610 #~(string-append
611 #$(format #f "Component \"~a\"\n" hostname)
612 #$(serialize-configuration config rest))))
78cef99b
CL
613
614;; Serialize virtualhosts and components last.
615(define (serialize-prosody-configuration config)
616 (define (rest? field)
617 (not (memq (configuration-field-name field)
618 '(virtualhosts int-components ext-components))))
bdcf0e6f
CL
619 #~(string-append
620 #$(let ((rest (filter rest? prosody-configuration-fields)))
621 (serialize-configuration config rest))
622 #$(serialize-virtualhost-configuration-list
623 (prosody-configuration-virtualhosts config))
624 #$(serialize-int-component-configuration-list
625 (prosody-configuration-int-components config))
626 #$(serialize-ext-component-configuration-list
627 (prosody-configuration-ext-components config))))
78cef99b
CL
628
629(define-configuration opaque-prosody-configuration
630 (prosody
892f1b72 631 (file-like prosody)
78cef99b
CL
632 "The prosody package.")
633
634 (prosody.cfg.lua
635 (string (configuration-missing-field 'opaque-prosody-configuration
636 'prosody.cfg.lua))
637 "The contents of the @code{prosody.cfg.lua} to use."))
638
639(define (prosody-shepherd-service config)
640 "Return a <shepherd-service> for Prosody with CONFIG."
641 (let* ((prosody (if (opaque-prosody-configuration? config)
642 (opaque-prosody-configuration-prosody config)
643 (prosody-configuration-prosody config)))
644 (prosodyctl-bin (file-append prosody "/bin/prosodyctl"))
fdbca05d 645 (pid-file (prosody-configuration-pidfile config))
78cef99b
CL
646 (prosodyctl-action (lambda args
647 #~(lambda _
fdbca05d
CL
648 (invoke #$prosodyctl-bin #$@args)
649 (match '#$args
650 (("start")
651 (call-with-input-file #$pid-file read))
652 (_ #t))))))
78cef99b
CL
653 (list (shepherd-service
654 (documentation "Run the Prosody XMPP server")
533bc514 655 (provision '(prosody xmpp-daemon))
78cef99b 656 (requirement '(networking syslogd user-processes))
fdbca05d
CL
657 (modules `((ice-9 match)
658 ,@%default-modules))
78cef99b
CL
659 (start (prosodyctl-action "start"))
660 (stop (prosodyctl-action "stop"))))))
661
662(define %prosody-accounts
663 (list (user-group (name "prosody") (system? #t))
664 (user-account
665 (name "prosody")
666 (group "prosody")
667 (system? #t)
668 (comment "Prosody daemon user")
669 (home-directory "/var/empty")
670 (shell (file-append shadow "/sbin/nologin")))))
671
672(define (prosody-activation config)
673 "Return the activation gexp for CONFIG."
674 (let* ((config-dir "/etc/prosody")
675 (default-certs-dir "/etc/prosody/certs")
676 (data-path (prosody-configuration-data-path config))
677 (pidfile-dir (dirname (prosody-configuration-pidfile config)))
bdcf0e6f
CL
678 (config-str (if (opaque-prosody-configuration? config)
679 (opaque-prosody-configuration-prosody.cfg.lua config)
680 #~(begin
681 (use-modules (ice-9 format))
682 #$(serialize-prosody-configuration config))))
683 (config-file (mixed-text-file "prosody.cfg.lua" config-str)))
78cef99b 684 #~(begin
87508d9a 685 (use-modules (guix build utils))
78cef99b
CL
686 (define %user (getpw "prosody"))
687
688 (mkdir-p #$config-dir)
689 (chown #$config-dir (passwd:uid %user) (passwd:gid %user))
690 (copy-file #$config-file (string-append #$config-dir
691 "/prosody.cfg.lua"))
692
693 (mkdir-p #$default-certs-dir)
694 (chown #$default-certs-dir (passwd:uid %user) (passwd:gid %user))
695 (chmod #$default-certs-dir #o750)
696
697 (mkdir-p #$data-path)
698 (chown #$data-path (passwd:uid %user) (passwd:gid %user))
699 (chmod #$data-path #o750)
700
701 (mkdir-p #$pidfile-dir)
702 (chown #$pidfile-dir (passwd:uid %user) (passwd:gid %user)))))
703
704(define prosody-service-type
705 (service-type (name 'prosody)
706 (extensions
707 (list (service-extension shepherd-root-service-type
708 prosody-shepherd-service)
709 (service-extension account-service-type
710 (const %prosody-accounts))
711 (service-extension activation-service-type
9c6a461c 712 prosody-activation)))
cb7c80f6
CL
713 (default-value (prosody-configuration
714 (virtualhosts
715 (list
716 (virtualhost-configuration
717 (domain "localhost"))))))
9c6a461c
CL
718 (description
719 "Run Prosody, a modern XMPP communication server.")))
78cef99b
CL
720
721;; A little helper to make it easier to document all those fields.
722(define (generate-documentation)
723 (define documentation
724 `((prosody-configuration
725 ,prosody-configuration-fields
726 (ssl ssl-configuration)
727 (virtualhosts virtualhost-configuration)
728 (int-components int-component-configuration)
729 (ext-components ext-component-configuration))
730 (ssl-configuration ,ssl-configuration-fields)
731 (int-component-configuration ,int-component-configuration-fields
732 (mod-muc mod-muc-configuration))
733 (ext-component-configuration ,ext-component-configuration-fields)
734 (mod-muc-configuration ,mod-muc-configuration-fields)
735 (virtualhost-configuration ,virtualhost-configuration-fields)
736 (opaque-prosody-configuration ,opaque-prosody-configuration-fields)))
737 (define (generate configuration-name)
738 (match (assq-ref documentation configuration-name)
739 ((fields . sub-documentation)
740 (format #t "\nAvailable @code{~a} fields are:\n\n" configuration-name)
741 (when (memq configuration-name
742 '(virtualhost-configuration
743 int-component-configuration
744 ext-component-configuration))
745 (format #t "all these @code{prosody-configuration} fields: ~a, plus:\n"
746 (string-join (map (lambda (s)
747 (format #f "@code{~a}" s)) common-fields)
748 ", ")))
749 (for-each
750 (lambda (f)
751 (let ((field-name (configuration-field-name f))
752 (field-type (configuration-field-type f))
753 (field-docs (string-trim-both
754 (configuration-field-documentation f)))
755 (default (catch #t
756 (configuration-field-default-value-thunk f)
757 (lambda _ 'nope))))
758 (define (escape-chars str chars escape)
759 (with-output-to-string
760 (lambda ()
761 (string-for-each (lambda (c)
762 (when (char-set-contains? chars c)
763 (display escape))
764 (display c))
765 str))))
766 (define (show-default? val)
19ff1f26 767 (or (string? val) (number? val) (boolean? val)
78cef99b
CL
768 (and (list? val) (and-map show-default? val))))
769 (format #t "@deftypevr {@code{~a} parameter} ~a ~a\n~a\n"
770 configuration-name field-type field-name field-docs)
771 (when (show-default? default)
772 (format #t "Defaults to @samp{~a}.\n"
773 (escape-chars (format #f "~s" default)
774 (char-set #\@ #\{ #\})
775 #\@)))
776 (for-each generate (or (assq-ref sub-documentation field-name) '()))
777 (format #t "@end deftypevr\n\n")))
778 (filter (lambda (f)
779 (not (string=? "" (configuration-field-documentation f))))
780 fields)))))
781 (generate 'prosody-configuration)
782 (format #t "It could be that you just want to get a @code{prosody.cfg.lua}
783up and running. In that case, you can pass an
784@code{opaque-prosody-configuration} record as the value of
785@code{prosody-service-type}. As its name indicates, an opaque configuration
786does not have easy reflective capabilities.")
787 (generate 'opaque-prosody-configuration)
788 (format #t "For example, if your @code{prosody.cfg.lua} is just the empty
789string, you could instantiate a prosody service like this:
790
791@example
792(service prosody-service-type
793 (opaque-prosody-configuration
794 (prosody.cfg.lua \"\")))
795@end example"))
f2bee421
LC
796
797\f
798;;;
799;;; BitlBee.
800;;;
801
802(define-record-type* <bitlbee-configuration>
803 bitlbee-configuration make-bitlbee-configuration
804 bitlbee-configuration?
805 (bitlbee bitlbee-configuration-bitlbee
806 (default bitlbee))
807 (interface bitlbee-configuration-interface
808 (default "127.0.0.1"))
809 (port bitlbee-configuration-port
810 (default 6667))
36d619e8
PAR
811 (plugins bitlbee-plugins
812 (default '()))
f2bee421
LC
813 (extra-settings bitlbee-configuration-extra-settings
814 (default "")))
815
816(define bitlbee-shepherd-service
817 (match-lambda
36d619e8
PAR
818 (($ <bitlbee-configuration> bitlbee interface port
819 plugins extra-settings)
1b157bbe
LC
820 (let* ((plugins (directory-union "bitlbee-plugins" plugins))
821 (conf (mixed-text-file "bitlbee.conf"
36d619e8 822 "
f2bee421
LC
823 [settings]
824 User = bitlbee
825 ConfigDir = /var/lib/bitlbee
826 DaemonInterface = " interface "
827 DaemonPort = " (number->string port) "
1b157bbe 828 PluginDir = " plugins "/lib/bitlbee
211fe3f6
LC
829" extra-settings))
830 (bitlbee* (least-authority-wrapper
831 (file-append bitlbee "/sbin/bitlbee")
832 #:name "bitlbee"
6075d251 833 #:preserved-environment-variables
47d73ac4 834 '("PURPLE_PLUGIN_PATH" "GUIX_LOCPATH" "LC_ALL")
211fe3f6
LC
835 #:mappings (list (file-system-mapping
836 (source "/var/lib/bitlbee")
837 (target source)
838 (writable? #t))
47d73ac4
LC
839 (file-system-mapping
840 (source "/run/current-system/locale")
841 (target source))
211fe3f6
LC
842 (file-system-mapping
843 (source conf)
844 (target conf)))
845 #:namespaces (delq 'net %namespaces))))
f2bee421
LC
846
847 (with-imported-modules (source-module-closure
848 '((gnu build shepherd)
849 (gnu system file-systems)))
850 (list (shepherd-service
851 (provision '(bitlbee))
852
853 ;; Note: If networking is not up, then /etc/resolv.conf
854 ;; doesn't get mapped in the container, hence the dependency
855 ;; on 'networking'.
856 (requirement '(user-processes networking))
857
858 (modules '((gnu build shepherd)
859 (gnu system file-systems)))
211fe3f6
LC
860 (start #~(if (defined? 'make-inetd-constructor)
861
862 (make-inetd-constructor
ecfcdff2 863 (list #$bitlbee* "-I" "-c" #$conf)
211fe3f6
LC
864 (addrinfo:addr
865 (car (getaddrinfo #$interface
866 #$(number->string port)
867 (logior AI_NUMERICHOST
868 AI_NUMERICSERV))))
869 #:service-name-stem "bitlbee"
ecfcdff2 870 #:user "bitlbee" #:group "bitlbee"
211fe3f6
LC
871
872 ;; Allow 'bitlbee-purple' to use libpurple plugins.
873 #:environment-variables
874 (list (string-append "PURPLE_PLUGIN_PATH="
47d73ac4
LC
875 #$plugins "/lib/purple-2")
876 "GUIX_LOCPATH=/run/current-system/locale"))
211fe3f6
LC
877
878 (make-forkexec-constructor/container
879 (list #$(file-append bitlbee "/sbin/bitlbee")
880 "-n" "-F" "-u" "bitlbee" "-c" #$conf)
881
882 ;; Allow 'bitlbee-purple' to use libpurple plugins.
883 #:environment-variables
884 (list (string-append "PURPLE_PLUGIN_PATH="
885 #$plugins "/lib/purple-2"))
886
887 #:pid-file "/var/run/bitlbee.pid"
888 #:mappings (list (file-system-mapping
889 (source "/var/lib/bitlbee")
890 (target source)
891 (writable? #t))))))
2a37f174
LC
892 (stop #~(if (defined? 'make-inetd-destructor)
893 (make-inetd-destructor)
894 (make-kill-destructor))))))))))
f2bee421
LC
895
896(define %bitlbee-accounts
897 ;; User group and account to run BitlBee.
898 (list (user-group (name "bitlbee") (system? #t))
899 (user-account
900 (name "bitlbee")
901 (group "bitlbee")
902 (system? #t)
903 (comment "BitlBee daemon user")
904 (home-directory "/var/empty")
905 (shell (file-append shadow "/sbin/nologin")))))
906
907(define %bitlbee-activation
908 ;; Activation gexp for BitlBee.
909 #~(begin
910 (use-modules (guix build utils))
911
912 ;; This directory is used to store OTR data.
913 (mkdir-p "/var/lib/bitlbee")
914 (let ((user (getpwnam "bitlbee")))
915 (chown "/var/lib/bitlbee"
916 (passwd:uid user) (passwd:gid user)))))
917
918(define bitlbee-service-type
919 (service-type (name 'bitlbee)
920 (extensions
921 (list (service-extension shepherd-root-service-type
922 bitlbee-shepherd-service)
923 (service-extension account-service-type
924 (const %bitlbee-accounts))
925 (service-extension activation-service-type
926 (const %bitlbee-activation))))
927 (default-value (bitlbee-configuration))
928 (description
929 "Run @url{http://bitlbee.org,BitlBee}, a daemon that acts as
930a gateway between IRC and chat networks.")))
931
dcad57d5
EF
932\f
933;;;
934;;; Quassel.
935;;;
936
937(define-record-type* <quassel-configuration>
938 quassel-configuration make-quassel-configuration
939 quassel-configuration?
940 (quassel quassel-configuration-quassel
941 (default quassel))
942 (interface quassel-configuration-interface
943 (default "::,0.0.0.0"))
944 (port quassel-configuration-port
945 (default 4242))
946 (loglevel quassel-configuration-loglevel
947 (default "Info")))
948
949(define quassel-shepherd-service
950 (match-lambda
951 (($ <quassel-configuration> quassel interface port loglevel)
dac4efc4
LC
952 (let ((quassel (least-authority-wrapper
953 (file-append quassel "/bin/quasselcore")
954 #:name "quasselcore"
955 #:mappings (list (file-system-mapping
956 (source "/var/lib/quassel")
957 (target source)
958 (writable? #t))
959 (file-system-mapping
960 (source "/var/log/quassel")
961 (target source)
962 (writable? #t)))
963 ;; XXX: The daemon needs to live in the main user
964 ;; namespace, as root, so it can access /var/lib/quassel
965 ;; owned by "quasselcore".
966 #:namespaces (fold delq %namespaces '(net user)))))
dcad57d5
EF
967 (list (shepherd-service
968 (provision '(quassel))
969 (requirement '(user-processes networking))
dac4efc4
LC
970 (start #~(make-forkexec-constructor
971 (list #$quassel
972 "--configdir=/var/lib/quassel"
973 "--logfile=/var/log/quassel/core.log"
974 (string-append "--loglevel=" #$loglevel)
975 (string-append "--port=" (number->string #$port))
976 (string-append "--listen=" #$interface))))
dcad57d5
EF
977 (stop #~(make-kill-destructor))))))))
978
979(define %quassel-account
980 (list (user-group (name "quassel") (system? #t))
981 (user-account
982 (name "quasselcore")
983 (group "quassel")
984 (system? #t)
985 (comment "Quassel daemon user")
986 (home-directory "/var/lib/quassel")
987 (shell (file-append shadow "/sbin/nologin")))))
988
989(define %quassel-activation
990 #~(begin
991 (use-modules (guix build utils))
992 (mkdir-p "/var/lib/quassel")
993 (mkdir-p "/var/log/quassel")
994 (let ((cert "/var/lib/quassel/quasselCert.pem"))
995 (unless (file-exists? cert)
996 (invoke #$(file-append openssl "/bin/openssl")
997 "req" "-x509" "-nodes" "-batch" "-days" "680" "-newkey"
998 "rsa" "-keyout" cert "-out" cert)))))
999
1000(define quassel-service-type
1001 (service-type (name 'quassel)
1002 (extensions
1003 (list (service-extension shepherd-root-service-type
1004 quassel-shepherd-service)
1005 (service-extension profile-service-type
1006 (compose list quassel-configuration-quassel))
1007 (service-extension account-service-type
1008 (const %quassel-account))
1009 (service-extension activation-service-type
1010 (const %quassel-activation))))
1011 (default-value (quassel-configuration))
1012 (description
1013 "Run @url{https://quassel-irc.org/,quasselcore}, the backend
1014for the distributed IRC client quassel, which allows you to connect from
1015multiple machines simultaneously.")))