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