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