~yoctocell/guix-yoctocell

9452849c34b0b71b96893cbdd81eddaeb67bc277 — Xinglu Chen 10 months ago c6cc616
Remove service.
5 files changed, 0 insertions(+), 2217 deletions(-)

D yoctocell/gnu/services/editors.scm
D yoctocell/gnu/services/mail.scm
D yoctocell/gnu/services/messaging.scm
D yoctocell/gnu/services/notifications.scm
D yoctocell/gnu/services/version-control.scm
D yoctocell/gnu/services/editors.scm => yoctocell/gnu/services/editors.scm +0 -40
@@ 1,40 0,0 @@
;; ;;; Guix
;; ;;;
;; ;;; Copyright © 2020 Jelle Licht <jlicht@fsfe.org>
;; ;;;
;; ;;; This program is free software: you can redistribute it and/or modify
;; ;;; it under the terms of the GNU General Public License as published by
;; ;;; the Free Software Foundation, either version 3 of the License, or
;; ;;; (at your option) any later version.
;; ;;;
;; ;;; This program is distributed in the hope that it will be useful,
;; ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; ;;; GNU General Public License for more details.
;; ;;;
;; ;;; You should have received a copy of the GNU General Public License
;; ;;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
;; (define-module (yoctocell gnu services editors)
;;   #:use-module (gnu packages emacs)
;;   #:use-module (gnu packages emacs-xyz)
;;   #:use-module (guix build utils)
;;   #:use-module (guix gexp)
;;   #:use-module (gnu services configuration)
;;   #:use-module (guix records)
;;   #:use-module ((guix licenses) #:prefix license:)
;;   #:use-module (gnu home-services)
;;   #:use-module (gnu home-services files)
;;   #:use-module (gnu home-services-utils)
;;   #:use-module (gnu home-services shepherd)
;;   #:use-module (ice-9 match)
;;   #:use-module (gnu packages vim)
;;   #:use-module (srfi srfi-1)
;;   #:use-module (ice-9 curried-definitions)
;;   #:use-module (ice-9 pretty-print)
;;   #:use-module (srfi srfi-26)
;;   #:export (home-neovim-service-type
;;             home-neovim-configuration))





D yoctocell/gnu/services/mail.scm => yoctocell/gnu/services/mail.scm +0 -1295
@@ 1,1295 0,0 @@
;; (define-module (yoctocell gnu services mail)
;;   #:use-module (gnu services)
;;   #:use-module (gnu home-services)
;;   #:use-module (gnu home-services files)
;;   #:use-module (gnu home-services-utils)
;;   #:use-module (gnu services configuration)
;;   #:use-module (gnu packages bash)
;;   #:use-module (gnu packages mail)
;;   #:use-module (gnu packages version-control)
;;   #:use-module (guix packages)
;;   #:use-module (guix records)
;;   #:use-module (guix gexp)
;;   #:use-module (guix diagnostics)
;;   #:use-module (guix i18n)
;;   #:use-module ((guix import utils) #:select (flatten))
;;   #:use-module (srfi srfi-1)
;;   #:use-module (srfi srfi-26)
;;   #:use-module (ice-9 match)
;;   #:use-module (ice-9 string-fun)
;;   #:use-module (ice-9 regex)
;; 
;;   #:export (home-notmuch-service-type
;;             notmuch-configuration
;;             serialize-notmuch-configuration
;; 
;;             ;; home-isync-service-type
;;             ;; isync-configuration
;; 
;;             home-procmail-service-type
;;             home-procmail-configuration
;;             procmail-recipe
;; 
;;             home-fetchmail-service-type
;;             home-fetchmail-configuration
;;             fetchmail-account
;; 
;;             home-msmtp-service-type
;;             home-msmtp-configuration
;;             msmtp-account))
;; 
;; ;;; Commentary:
;; ;;;
;; ;;; This module provides various Guix services for dealing with mail,
;; ;;; some of them will hopefully be upstreamed in the future.
;; ;;;
;; ;;; It also includes a fairly complete service for dealing with
;; ;;; mailing lists, public-inbox archives.  The idea as that the user
;; ;;; creates a list of <mailing-list> which specify things like the
;; ;;; address, list-id, public-inbox url, and Notmuch tags to apply.
;; ;;; The `mailing-lists-service-type' will then figure out how to
;; ;;; configure different programs to make sure to refile, tag, and
;; ;;; filter all the specified mailing lists accordingly.
;; ;;;
;; ;;; Code:
;; 
;; 
;; ;;;
;; ;;; Mailing lists.
;; ;;;
;; 
;; (define-configuration/no-serialization mailing-list
;;   (name
;;    (string)
;;    "The name you want to give to the mailing list.  This will also be
;; used as a tag in Notmuch for tagging messages from this mailing
;; list.")
;;   (address
;;    (string)
;;    "The address of the mailing list.")
;;   (list-id
;;    (string)
;;    "The List-Id of the mailing list, this is usually the same as the
;; @{address}, but the ``@@'' is replaced with ``.''.")
;;   (maildir
;;    (string)
;;    "The maildir to put messages from this list in.")
;;   (coderepo
;;    (string "")
;;    "The directory of code repository corresponding to this mailing list.")
;;   (public-inbox-url
;;    (string "")
;;    "The URL of the public-inbox archive.")
;;   (notmuch-tags
;;    (list-of-strings '())
;;    "A list of tag operations to apply to messages from this list."))
;; 
;; (define list-of-mailing-lists?
;;   (compose list-of mailing-list?))
;; 
;; 
;; ;;;
;; ;;; Service for handling mailing list and GitHub notification
;; ;;; messages, with public-inbox and Emacs integration.
;; ;;;
;; 
;; (define-configuration mailing-lists-emacs-configuration
;;   (piem?
;;    (boolean #f)
;;    "Whether to enable piem integration, this includes setting up
;; @code{piem-inboxes}.")
;;   ;; (gnus-gcc?)
;;   ;; (notmuch-fcc?)
;;   (no-serialization))
;; 
;; (define-configuration mailing-lists-public-inbox-configuration
;;   (l2md?
;;    (boolean #f)
;;    "Whether to use L2md for fetching public-inbox archives.  This will
;; then pipe the messages to Procmail which refiles them into the correct
;; maildirs.")
;;   (grokmirror?
;;    (boolean #f)
;;    "Whether to use Grokmirror for fetching public-inbox archives.  This
;; will then pipe the messages to Procmail which refiles them into the
;; correct maildirs.")
;;   (update-interval
;;    (integer)
;;    "Time in seconds between updates of public-inbox archives")
;;   (repo-dir
;;    (string (string-append (getenv "XDG_DATA_HOME") "/public-inbox"))
;;    "Where to store public-inbox Git repositories.")
;;   (no-serialization))
;; 
;; (define-configuration mailing-lists-notmuch-configuration
;;   (notmuch-tags
;;    (list-of-strings '())
;;    "List of tag operations to apply to all mailing list and GitHub
;; messages.")
;;   (notmuch-directory-prefix
;;    (string)
;;    "Prefix for directories relative to @code{maildir}.  If
;; @code{notmuch-directory-prefix} is set to @file{yoctocell.xyz/public},
;; the directory for @file{lists.guix.guix-devel} will be located in
;; @file{yoctocell.xyz/public/.lists.guix.guix-devel}, relative to
;; @code{maildir}.")
;;   (no-serialization))
;; 
;; (define-configuration mailing-lists-configuration
;;   (maildir
;;    (string (string-append (getenv "XDG_DATA_HOME") "/mail"))
;;    "Path to your maildir, this is where the emails will be stored.")
;;   (mailing-lists
;;    (list-of-mailing-lists '())
;;    "A list of mailing lists to track.")
;;   (emacs-config
;;    (mailing-lists-emacs-configuration)
;;    "Emacs configuration for mailing lists.")
;;   (public-inbox-config
;;    (mailing-lists-public-inbox-configuration)
;;    "public-inbox related configuration for mailing lists.")
;;   ;; (sieve?)
;;   (notmuch-config
;;    (mailing-lists-notmuch-configuration)
;;    "Notmuch configuration for mailing lists.")
;;   (no-serialization))
;; 
;; 
;; ;;;
;; ;;; Notmuch.
;; ;;;
;; 
;; (define-maybe/no-serialization gexp)
;; (define (string-or-list-of-strings? val)
;;   (or (string? val) (list-of-strings? val)))
;; (define (string-or-file-like-object? val)
;;   (or (string? val) (file-like? val)))
;; 
;; ;; TODO: Deal with [STORED IN DATABASE].
;; ;; Set NOTMUCH_CONFIG
;; ;; TODO: `regex' newtype?
;; (define-configuration/no-serialization notmuch-configuration
;;   (package
;;     (package notmuch)
;;     "The Notmuch package to use.")
;;   (database
;;    (string (or (string-append (getenv "XDG_DATA_HOME") "/notmuch/database")
;;                (string-append (getenv "HOME") "/.local/share/notmuch/database")))
;;    "Where Notmuch will store its database.")
;;   (mail-root
;;    (string (or (string-append (getenv "XDG_DATA_HOME") "/mail")
;;                (string-append (getenv "HOME") "/.local/share/mail")))
;;    "The top-level directory where your mail exists.")
;;   (backup-dir
;;    (string (or (string-append (getenv "XDG_DATA_HOME") "/notmuch/backups")
;;                (string-append (getenv "HOME") "/.local/share/notmuch/backups")))
;;    "The directory to stor tag dumps when upgrading the databse.")
;;   (hook-dir
;;    (string (or (string-append (getenv "XDG_DATA_HOME") "/notmuch/hooks")
;;                (string-append (getenv "HOME") "/.local/share/notmuch/hooks")))
;;    "The directory containing hooks run by Notmuch commands.")
;;   (name
;;    (string)
;;    "Your full name.")
;;   (primary-email
;;    (string)
;;    "Your primary email address.")
;;   (other-emails
;;    (list-of-strings '())
;;    "A list of other email addresses you use.")
;;   (new-tags
;;    (string-or-list-of-strings '("unread" "inbox"))
;;    "A list of tags that should automatically be added to all messages
;; when running @command{notmuch new}.")
;;   (ignore
;;    (list-of-strings '())
;;    "A list of files and directories that will not be indexed by Notmuch.
;; Regular expressions are also allowed, they have to start and end with
;; a @verb{/}.")
;;   (exclude-tags
;;    (list-of-strings '("deleted" "spam"))
;;    "A list of tags that will be excluded from search results.")
;;   (synchronize-maildir-flags?
;;    (boolean #t)
;;    "Whether maildir flags will be synchronized with the corresponding
;; Notmuch tags (see @command{man notmuch-config} for more details).")
;;   (pre-new-hook
;;    (maybe-gexp 'disabled)
;;    "A G-expression that will be run before @command{notmuch new} starts
;; importing new messages into the database.")
;;   (post-new-hook
;;    (maybe-gexp 'disabled)
;;    "A G-expression that will be run after @command{notmuch new} has
;; imported new messages and applied initial tags to them.")
;;   (post-insert-hook
;;    (maybe-gexp 'disabled)
;;    "A G-expression that will be run after the messages have been added to
;; the database and initial tags have been applied."))
;; 
;; (define (serialize-notmuch-configuration config)
;;   (define (serialize-val val)
;;     (cond
;;      ((list? val) (serialize-list val))
;;      ((boolean? val) (if val "true" "false"))
;;      (else (maybe-object->string val))))
;;   
;;   (define (serialize-list lst)
;;     (interpose (map serialize-val lst) ";"))
;; 
;;   (define (serialize-field field-name val)
;;     (list (object->snake-case-string field-name) " = " (serialize-val val) "\n"))
;; 
;;   (match config
;;     (($ <notmuch-configuration> _ package database mail-root backup-dir hook-dir
;;                                 name primary-email other-emails new-tags ignore
;;                                 exclude-tags synchronize-maildir-flags?)
;;      (generic-serialize-ini-config
;;       #:combine-ini (compose flatten list)
;;       #:combine-alist append
;;       #:combine-section-alist cons*
;;       #:serialize-field serialize-field
;;       #:fields
;;       `((database
;;          ((path . ,database)
;;           (mail-root . ,mail-root)
;;           (backup-dir . ,backup-dir)
;;           (hook-dir . ,hook-dir)))
;;         (user
;;          ((name . ,name)
;;           (primary-email . ,primary-email)
;;           (other-email . ,other-emails)))
;;         (new
;;          ((tags . ,new-tags)
;;           (ignore . ,ignore)))
;;         (search
;;          ((exclude-tags . ,exclude-tags)))
;;         (maildir
;;          ((synchronize-flags . ,synchronize-maildir-flags?))))))))
;; 
;; (define (ensure-trailing-slash str)
;;   (let ((len (string-length str)))
;;     (if (equal? (string-index-right str #\/) (- len 1))
;;         str
;;         (string-append str "/"))))
;; 
;; (define (notmuch-files-service config)
;;   (let ((hook-dir (ensure-trailing-slash
;;                    (notmuch-configuration-hook-dir config)))
;;         (pre-new (notmuch-configuration-pre-new-hook config))
;;         (post-new (notmuch-configuration-post-new-hook config))
;;         (post-insert (notmuch-configuration-post-insert-hook config))
;;         (notmuch-config (apply mixed-text-file
;;                                "notmuch-config"
;;                                (serialize-notmuch-configuration config))))
;;     (filter
;;      (compose not null?)
;;      (map (lambda (name gexp)
;;             (optional (gexp? gexp)
;;                       `(,(regexp-substitute
;;                           #f (or (string-match (string-append (getenv "HOME")
;;                                                               "/.(.*)")
;;                                                hook-dir)
;;                                  (string-match "~/.(.*)" hook-dir))
;;                           1 'post name)
;;                         ,(program-file
;;                           (string-append "notmuch-" name "-hook")
;;                           `(begin
;;                              (setenv "NOTMUCH_CONFIG" ,notmuch-config)
;;                              (setenv "PATH" "$GUIX_HOME_DIRECTORY/profile/bin")
;;                              ,gexp)))))
;;           '("pre-new" "post-new" "post-insert")
;;           (list pre-new post-new post-insert)))))
;; 
;; (define (notmuch-environment-variables-service config)
;;   `(("NOTMUCH_CONFIG" . ,(apply mixed-text-file
;;                                 "notmuch-config"
;;                                 (serialize-notmuch-configuration config)))))
;; 
;; (define (notmuch-profile-service config)
;;   (list (notmuch-configuration-package config)))
;; 
;; (define-configuration/no-serialization notmuch-extension
;;   (pre-new-hook
;;    (maybe-gexp #f)
;;    "A G-expression that will be run before @command{notmuch new} starts
;; importing new messages into the database.")
;;   (post-new-hook
;;    (maybe-gexp #f)
;;    "A G-expression that will be run after @command{notmuch new} has
;; imported new messages and applied initial tags to them.")
;;   (post-insert-hook
;;    (maybe-gexp #f)
;;    "A G-expression that will be run after the messages have been added to
;; the database and initial tags have been applied."))
;; 
;; (define home-notmuch-service-type
;;   (service-type (name 'home-notmuch)
;;                 (extensions
;;                  (list (service-extension
;;                         home-environment-variables-service-type
;;                         notmuch-environment-variables-service)
;;                        (service-extension
;;                         home-profile-service-type
;;                         notmuch-profile-service)
;;                        (service-extension
;;                         home-files-service-type
;;                         notmuch-files-service)))
;;                 (description "Install and configure the Notmuch mail indexer.")))
;; 
;; 
;; (define test-config
;;   (notmuch-configuration
;;    (database "/home/yoctocell/.local/share/mail/.notmuch")
;;    (mail-root "/home/yoctocell/.local/share/mail")
;;    (name "Xinglu Chen")
;;    (primary-email "public@yoctocell.xyz")
;;    (other-emails '("private@yoctocell.xyz" "lists@yoctocell.xyz"))
;;    (new-tags "new")
;;    (ignore '(".uidvalidity"
;;              ".mbsyncstate"
;;              "/.*dovecot.*/"
;;              "/.*subscriptions/"
;;              "mbsyncstate.lock"))
;;    (exclude-tags '("deleted" "spam"))
;;    (pre-new-hook #~(system* "notmuch" "tag" "+inbox" "to:public@yoctocell.xyz"))))
;; 
;; (notmuch-files-service test-config)
;; 
;; 
;; 
;; ;;;
;; ;;; Msmtp.
;; ;;;
;; 
;; (define-enum msmtp-protocol
;;   '(smtp lmtp))
;; 
;; (define-enum msmtp-authentication-method
;;   '(plain scram-sha-1 scram-sha-256 cram-md5 digest-md5
;;           login oauthbearer xoauth2 external gssapi))
;; 
;; (define-enum msmtp-syslog-facility
;;   '(log-user log-mail log-local0 log-local1 log-local2 log-local3
;;              log-local4 log-local5 log-local6 log-local7))
;; 
;; (define-maybe boolean
;;   (prefix msmtp-))
;; 
;; (define-maybe string
;;   (prefix msmtp-))
;; 
;; (define-maybe integer
;;   (prefix msmtp-))
;; 
;; (define-maybe/no-serialization alist)
;; 
;; (define (boolean-or-msmtp-authentication-method? val)
;;   (or (boolean? val)
;;       (msmtp-authentication-method? val)))
;; 
;; (define (boolean-or-msmtp-syslog-facility? val)
;;   (or (boolean? val)
;;       (msmtp-syslog-facility? val)))
;; 
;; (define (msmtp-uglify-field-name field-name)
;;   (let ((str (string-map (lambda (char)
;;                            (case char
;;                              ((#\-) #\_)
;;                              (else char)))
;;                          (symbol->string field-name))))
;;     (if (string-suffix? "?" str)
;;         (string-drop-right str 1)
;;         str)))
;; 
;; (define* (msmtp-serialize-field field-name val)
;;   (cond
;;    ((boolean? val) (msmtp-serialize-boolean field-name val))
;;    (else #~(string-append #$(msmtp-uglify-field-name field-name)
;;                           " "  #$(maybe-object->string val) "\n"))))
;; 
;; (define msmtp-serialize-string msmtp-serialize-field)
;; (define msmtp-serialize-integer msmtp-serialize-field)
;; 
;; (define (msmtp-serialize-toplevel field-name val)
;;   (msmtp-serialize-field field-name val))
;; 
;; (define (msmtp-serialize-boolean field-name val)
;;   (msmtp-serialize-field field-name (if val "on" "off")))
;; 
;; 
;; (define (msmtp-serialize-aliases field-name val)
;;   (if (list? val)
;;       (let ((config-str
;;              (map (match-lambda
;;                     ((key . val)
;;                      #~(string-append #$key " "
;;                                       #$(if (list? val)
;;                                             (string-join val ", ")
;;                                             val)
;;                                       "\n")))
;;                   val)))
;;         (msmtp-serialize-field
;;          field-name (apply mixed-text-file "msmtp-aliases" config-str)))
;;       ""))
;; 
;; (define (msmtp-serialize-boolean-or-msmtp-syslog-facility field-name val)
;;   (if (msmtp-syslog-facility? val)
;;       (msmtp-serialize-field field-name
;;                              (string-upcase
;;                               (string-map (lambda (char)
;;                                             (case char
;;                                               ((#\-) #\_)
;;                                               (else char)))
;;                                           (symbol->string val))))
;;       (msmtp-serialize-boolean field-name val)))
;; 
;; ;;; Msmtp account configuration.
;; 
;; (define-configuration msmtp-account
;;   (account
;;    (symbol)
;;    "The name/label of the account."
;;    msmtp-serialize-field)
;;   (host
;;    (string)
;;    "The SMTP server to send the mail to.")
;;   (port
;;    (integer)
;;    "The port that the SMTP server listens on.")
;;   (source-ip
;;    (maybe-string 'disabled)
;;    "The source IP address to bind the outgoing connection to; it is useful only
;; in special cases on multi-home systems.")
;;   (proxy-host
;;    (maybe-string 'disabled)
;;    "The IP or hostname of the SOCKS proxy to use; only SOCKS 5 is supported.")
;;   (proxy-port
;;    (maybe-integer 'disabled)
;;    "The port number for the proxy host")
;;   (socket
;;    (maybe-string 'disabled)
;;    "The file name of a Unix domain socket to connect to; this overrides both the
;; @code{host} and @code{port}, and @code{proxy-host} and @code{proxy-port}
;; fields.")
;;   (timeout
;;    (maybe-integer 'disabled)
;;    "The network timeout in seconds; if unset, the operating system default will
;; be used.")
;;   (protocol
;;    (msmtp-protocol 'smtp)
;;    "The protocol to use, the only options are @code{smtp} and @code{lmtp}."
;;    msmtp-serialize-field)
;;   (domain
;;    (string "localhost")
;;    "The argument of the SMTP EHLO (or LMTP LHLO) command.")
;;   (auth
;;    (boolean-or-msmtp-authentication-method)
;;    "If set to @code{#f}, disabled authentication; if set to @code{#t}, enable
;; authentication and choose a method automatically; otherwise, use one of the
;; methods in @code{msmtp-authentication-methods}."
;;    msmtp-serialize-field)
;;   (user
;;    (maybe-string 'disabled)
;;    "The username for authentication.")
;;   (password
;;    (maybe-string 'disabled)
;;    "The password for authentication.  Consider using the @code{passwordeval}
;; field to avoid storing secrets in the Guix store, which is world-readable.")
;;   (passwordeval
;;    (maybe-string 'disabled)
;;    "A shell command to run to output the password to stdout; it can for example
;; retrieve the password from a password manager or keyring.")
;; 
;;   ;; TLS
;;   (tls?
;;    (boolean #t)
;;    "Whether to enable TLS (SSL) for encrypted connections.")
;;   (tls-starttls?
;;    (boolean #t)
;;    "Whether to use STARTTLS.  A server can use TLS in one of two modes: via a
;; STARTTLS command---the session starts with the normal protocol initialization,
;; and TLS is then started using the protocol's STARTTLS command, or
;; immediately---TLS is initialized before the normal protocol initialization.")
;;   (tls-trust-file
;;    (maybe-string 'disabled)
;;    "A file from which trusted @acronym{Certification Authorities, CAs} are read.")
;;   (tls-fingerprint
;;    (maybe-string 'disabled)
;;    "The fingerprint of a single certificate to accept for TLS.  This certificate
;; will be trusted regardless of its contents, and will override the
;; @code{tls-trust-file} field.")
;;   (tls-key-file
;;    (maybe-string 'disabled)
;;    "A file that contains the private key of a certificate in PEM format.  This
;; should be used with the @code{tls-key-file} field.")
;;   (tls-cert-file
;;    (maybe-string 'disabled)
;;    "A file that contains a certificate in PEM format.  This should be used with
;; the @code{tls-cert-file} field.")
;;   (tls-certcheck?
;;    (boolean #t)
;;    "Whether to enable checks of the server certificate.  If set to @code{#f}, it
;; will override the @code{tls-trust-file} and @code{tls-fingerprint} fields.")
;;   (tls-priorities
;;    (maybe-string 'disabled)
;;    "The priorities for the TLS session parameters, be default, it is set by the
;; TLS library that is used.  For GnuTLS, @pxref{Priority strings,,,gnutls}.")
;;   (tls-host-override
;;    (maybe-string 'disabled)
;;    "The host used for TLS host verification, by default, TLS host verification
;; uses the host name given by the @code{host} field.  This option is only useful
;; in special cases.")
;; 
;;   ;; Misc
;;   (from
;;    (maybe-string 'disabled)
;;    "The envelope-from address.  The following substitution patterns are
;; supported:
;; 
;; @itemize
;; @item @code{%U} will be replaced by @code{$USER};
;; @item @code{%H} will be replaced by @code{$HOSTNAME};
;; @item @code{%C} will be replcaed by the canonical name of @code{%H};
;; @item @code{%M} will be replaced by the contents of @file{/etc/mailname}.
;; @end itemize")
;;   (dsn-notify
;;    (maybe-string 'disabled)
;;    "The condition(s) under which the mail system should send @acronym{Delivery
;; Status Notifaction, DSN} messages.  If disabled, the mail system decides when
;; to send DSN messages.")
;;   (dsn-return
;;    (maybe-string 'disabled)
;;    "How much of a mail that should be returned in DSN messages.  If disabled, the
;; mail system decides how much of a mail it returns in DSN messagse.")
;;   (set-from-header?
;;    (maybe-boolean 'disabled)
;;    "When to set a @code{From} header.  If set to @code{#f}, always set a
;; @code{From} header; if set to @code{#t}, never set a @code{From} header.  If
;; disabled, add a @code{From} header if the mail does not have one.")
;;   (set-date-header?
;;    (maybe-boolean 'disabled)
;;    "When to set a @code{Date} header.  If set to @code{#f}, always set a
;; @code{Dat} header; if set to @code{#t}, never set a @code{Date} header.  If
;; disabled, add a @code{Date} header if the mail does not have one.")
;;   (remove-bcc-headers?
;;    (boolean #t)
;;    "Whether to remove @code{Bcc} headers.")
;;   (undisclosed-recipients?
;;    (boolean #f)
;;    "Whether the original @code{To}, @code{Cc}, and @code{Bcc} headers of the mail
;; a re removed and a single new header line @code{\"To:
;; undisclosed-recipients:;\"} is added.")
;;   (logfile
;;    (maybe-string 'disabled)
;;    "The log file to use; Msmtp will append one line to the log file for each mail
;; it tries to send via the account.  If disabled, disable logging.")
;;   (log-time-format
;;    (maybe-string 'disabled)
;;    "The log file time format.  This will be used as the format string for the
;; @code{strftime()} function (pxref{Formatting Calendar Time,strftime,,libc})")
;;   (syslog
;;    (boolean-or-msmtp-syslog-facility 'log-user)
;;    "Enable or disable syslog logging.  The facility can be one of
;; @code{log-user}, @code{log-mail}, @code{log-local0} @dots, @code{log-local7}.")
;;   (aliases
;;    (maybe-alist 'disabled)
;;    "Association list of aliases; the car is the local address, and the cdr is the
;; replacement address(es)."
;;    msmtp-serialize-aliases)
;;   (prefix msmtp-))
;; 
;; (define list-of-msmtp-accounts? (list-of msmtp-account?))
;; 
;; ;;; Msmtp toplevel configuration.
;; 
;; (define-configuration/no-serialization home-msmtp-configuration
;;   (package
;;     (package msmtp)
;;     "The Msmtp package to use.")
;;   (default-account
;;     (symbol)
;;     "The label of the default account.")
;;   (accounts
;;    (list-of-msmtp-accounts)
;;    "A list of @code{<msmtp-accounts>} record type that configures accounts for
;; Msmtp."))
;; 
;; (define (msmtp-files-service config)
;;   (match config
;;     (($ <home-msmtp-configuration> _ _ default-account accounts)
;;      `(("config/msmtp/config"
;;         ,(apply mixed-text-file "msmtp-config"
;;                 `(,@(interpose (map (lambda (account)
;;                             (serialize-configuration account
;;                                                      msmtp-account-fields))
;;                                     accounts)
;;                                "\n\n")
;;                   "\naccount default : "
;;                   ,(symbol->string default-account))))))))
;; 
;; (define (msmtp-profile-service config)
;;   (list (home-msmtp-configuration-package config)))
;; 
;; (define home-msmtp-service-type
;;   (service-type (name 'home-fetchmail)
;;                 (extensions
;;                  (list (service-extension
;;                         home-files-service-type
;;                         msmtp-files-service)
;;                        (service-extension
;;                         home-profile-service-type
;;                         msmtp-profile-service)))
;;                 (compose concatenate)
;;                 (extend (lambda (config accounts)
;;                           (home-msmtp-configuration
;;                            (inherit config)
;;                            (accounts
;;                             (append (home-msmtp-configuration-accounts config)
;;                                     accounts)))))
;;                 (description "Configure Msmtp for sending mail.")))
;; 
;; (define test-config
;;   (home-msmtp-configuration
;;    (default-account 'public)
;;    (accounts
;;     (list (msmtp-account
;;            (account 'public)
;;            (host "mail.yoctocell.xyz")
;;            (port 465)
;;            (protocol 'smtp)
;;            (auth #t)
;;            (from "public@yoctocell.xyz")
;;            (user "public@yoctocell.xyz")
;;            (passwordeval "pass mail/public@yoctocell.xyz:465")
;;            (tls? #t)
;;            (tls-starttls? #f)
;;            (tls-trust-file "/etc/ssl/certs/ca-certificates.crt"))))))
;; 
;; (msmtp-files-service test-config)
;; 
;; 
;; 
;; ;;;
;; ;;; Isync.
;; ;;;
;; 
;; ;; (define-enum isync-location
;; ;;   '(none far near both))
;; ;; 
;; ;; (define-maybe string
;; ;;   (prefix isync-))
;; ;; 
;; ;; (define isync-serialize-package empty-serializer)
;; ;; ;; (define %isync-all-caps-words
;; ;; ;;   '("Ssl" "Imap" "Starttls" "Imaps"))
;; ;; ;; 
;; ;; ;; ;; TODO: Better way to do this
;; ;; (define (isync-uglify-field-name field-name)
;; ;;   (string-replace-substring
;; ;;    (string-replace-substring
;; ;;     (string-replace-substring
;; ;;      (string-replace-substring
;; ;;       (object->snake-case-string field-name #:style 'upper)
;; ;;       "Ssl" "SSL")
;; ;;      "Imap" "IMAP")
;; ;;     "Starttls" "STARTTLS")
;; ;;    "Imaps" "IMAPS"))
;; ;; 
;; ;; (define (isync-serialize-field field-name val)
;; ;;   (cond
;; ;;    ((boolean? val) (serialize-bool field-name val))
;; ;;    ((list? val) (serialize-list field-name val))
;; ;;    ((symbol? val (serialize-symbol field-name val)))
;; ;;    (else
;; ;;     #~(string-append #$(uglify-field-name field-name) " = " $val "\n"))))
;; ;; 
;; ;; (define (isync-serialize-symbol field-name val)
;; ;;   (serialize-field field-name (isync-uglify-field-name val)))
;; ;; 
;; ;; (define isync-serialize-string isync-serialize-field)
;; ;; 
;; ;; ;; check host and tunnel
;; ;; 
;; ;; (define-configuration isync-account
;; ;;   (host
;; ;;    (maybe-string 'disabled)
;; ;;    "The DNS name or IP address of the IMAP server.  If the @code{tunnel}
;; ;; option is specified, this setting is neede only if @code{ssl-type} is
;; ;; not @code{none} and @code{certificate-file} is not used, in which case
;; ;; the host name is used for certificate subject verification.")
;; ;;   (port
;; ;;    (maybe-string 'disabled)
;; ;;    "The TCP port number of the IMAP server.  If the @code{tunnel} option
;; ;; is used, this setting is ignored.")
;; ;;   ;; (create 
;; ;;   ;;  (isync-location 'none)
;; ;;   ;;  (string-append "Where to automatically create missing mailboxes.  Valid options are: "
;; ;;   ;;                (list->human-readable-list
;; ;;   ;;                 (enum-value isync-location)
;; ;;   ;;                 #:cumulative? #t
;; ;;   ;;                 #:proc (cut format #f "@code{~a}" <>))
;; ;;   ;;                ".")
;; ;;   ;;  isync-serialize-symbol)
;; ;;   ;; (remove
;; ;;   ;;  (isync-location 'none)
;; ;;   ;;  (string-append "How to propagate mailbox deletions.  Valid options are: "
;; ;;   ;;                (list->human-readable-list
;; ;;   ;;                 (enum-value isync-location)
;; ;;   ;;                 #:cumulative? #t
;; ;;   ;;                 #:proc (cut format #f "@code{~a}" <>))
;; ;;   ;;                "."))
;; ;;   ;; (expunge
;; ;;   ;;  (isync-location 'none)
;; ;;   ;;  (string-append "Where to permanently remove all messages.  Valid options are: "
;; ;;   ;;                 (list->human-readable-list
;; ;;   ;;                 (enum-value isync-location)
;; ;;   ;;                 #:cumulative? #t
;; ;;   ;;                 #:proc (cut format #f "@code{~a}" <>))
;; ;;   ;;                 "."))
;; ;;   (prefix isync-))
;; ;; 
;; ;; (define-configuration isync-configuration
;; ;;   (package
;; ;;     (package isync)
;; ;;     "The Isync package to use")
;; ;;   ;; (accounts
;; ;;   ;;  (list-of-isync-accounts '())
;; ;;   ;;  "List of @code{isync-account}s.")
;; ;;   (prefix isync-))
;; ;; 
;; ;; (define (isync-profile-service config)
;; ;;   (list (isync-configuration-package config)))
;; ;; 
;; ;; (define home-isync-service-type
;; ;;   (service-type (name 'home-isync)
;; ;;                 (extensions
;; ;;                  (list ;; (service-extension
;; ;;                        ;;  home-files-service-type
;; ;;                        ;;  procmail-files-service)
;; ;;                        (service-extension
;; ;;                         home-profile-service-type
;; ;;                         isync-profile-service)))
;; ;;                 (default-value (isync-configuration))
;; ;;                 (description "Install and configure Isync.")))
;; 
;; 
;; ;;;
;; ;;; Procmail.
;; ;;;
;; ;;;
;; ;;;
;; ;;;   (home-procmail-configuration
;; ;;;             (recipes
;; ;;;              (list (procmail-recipe
;; ;;;                     (flags '(f w))
;; ;;;                     (conditions '("^X-Virus: Yes"))
;; ;;;                     (action "virus"))
;; ;;;                    (procmail-recipe
;; ;;;                     (flags '(f))
;; ;;;                     (conditions '("^From: Google"))
;; ;;;                     (action "spam"))))
;; ;;;
;; 
;; (define (procmail-serialize-string-or-gexp field-name val) val)
;; (define (procmail-serialize-list-of-procmail-flags field-name val)
;;   (if (null? val)
;;       ""
;;       (format #f "\n\n:0 ~a\n" (string-join (map symbol->string val) ""))))
;; 
;; (define (procmail-serialize-lockfile field-name val)
;;   (cond
;;    ((string? val) (format #f "\n\n:0: ~a\n" val))
;;    ((eq? val #t) (format #f "\n\n:0:\n"))
;;    (else "")))
;; 
;; (define (procmail-serialize-conditions field-name val)
;;   (string-join (map (cut string-append "* " <>) val) "\n" 'suffix))
;; 
;; (define (procmail-serialize-assignments field-name val)
;;   #~(string-append
;;      #$@(map
;;          (match-lambda
;;            ((key . #f)
;;             "")
;;            ((key . #t)
;;             #~(string-append #$key "\n"))
;;            ((key . value)
;;             #~(string-append #$key "=" #$value "\n")))
;;          val)))
;; 
;; (define (procmail-serialize-list-of-procmail-recipes field-name val)
;;   (define (serialize-recipe recipe)
;;     (procmail-flags-or-lockfile? recipe)
;;     (serialize-configuration
;;      recipe
;;      procmail-recipe-fields))
;; 
;;   #~(string-append #$@(map serialize-recipe val)))
;; 
;; ;;; Predicates.
;; 
;; (define-enum procmail-flag
;;   '(H B D A a E e h b f c w W i r))
;; 
;; (define (list-of-procmail-flags? val)
;;   (list-of procmail-flag?))
;; 
;; (define (string-or-boolean? val)
;;   (or (string? val) (boolean? val)))
;; 
;; (define (list-of-procmail-recipes? val)
;;   (list-of procmail-recipe?))
;; 
;; (define (procmail-flags-or-lockfile? recipe)
;;   (match recipe
;;     (($ <procmail-recipe> _ flags lockfile?)
;;      (when (and (not (null? flags))
;;                 (not (eq? lockfile? #f)))
;;        (raise (formatted-message
;;                (G_ "\
;; `flags' and `lockfile?' in `<procmail-recipe>' are mutually exclusive")))))))
;; 
;; ;;; Configuration.
;; 
;; (define-configuration procmail-recipe
;;   (flags
;;    (list-of-procmail-flags '())
;;    "List of flags to use, can only be @code{f}, or @code{w}.  This
;; option is mutually exclusive with the @code{lockfile} option."
;;    )
;;   (lockfile?
;;    (string-or-boolean #f)
;;    "Whether to use a lockfile.  This is useful if you have multiple
;; Procmail instances running.  This must be a boolean or a string, the
;; string will be the name of the lockfile.  This option is mutually
;; exclusive with the @{flags} option."
;;    procmail-serialize-lockfile)
;;   (conditions
;;    (list '())
;;    "List of strings representing regular expressions for matching
;; messages."
;;    procmail-serialize-conditions)
;;   (action
;;    (string-or-gexp)
;;    "An shell command to run the matched message.")
;;   (prefix procmail-))
;; 
;; (define-configuration home-procmail-configuration
;;   (package
;;     (package procmail)
;;     "The Procmail package to use."
;;     empty-serializer)
;;   ;; TODO: Set appropriate MAILDIR
;;   (assignments
;;    (alist '())
;;    "Association list of environment variables to set."
;;    procmail-serialize-assignments)
;;   (recipes
;;    (list-of-procmail-recipes '())
;;    "List of @code{procmail-recipe}s."
;;    )
;;   (prefix procmail-))
;; 
;; ;;; Services.
;; 
;; (define (procmail-profile-service config)
;;   (list (home-procmail-configuration-package config)))
;; 
;; (define (procmail-files-service config)
;;   `(("procmailrc"
;;      ,(mixed-text-file
;;        "procmailrc"
;;        (serialize-configuration
;;         config
;;         home-procmail-configuration-fields)))))
;; 
;; (define home-procmail-service-type
;;   (service-type (name 'home-procmail)
;;                 (extensions
;;                  (list (service-extension
;;                         home-files-service-type
;;                         procmail-files-service)
;;                        (service-extension
;;                         home-profile-service-type
;;                         procmail-profile-service)))
;;                 (compose concatenate)
;;                 (extend (lambda (config recipes)
;;                           (home-procmail-configuration
;;                            (inherit config)
;;                            (recipes
;;                             (append (home-procmail-configuration-recipes config)
;;                                     recipes)))))
;;                 (default-value (home-procmail-configuration))
;;                 (description "Install and configure the Procmail MDA.")))
;; 
;; 
;; (define test-config
;;   (home-procmail-configuration
;;    (assignments
;;     '(("MAILDIR" . "$HOME/.local/share/mail")))
;;    (recipes
;;     (list (procmail-recipe
;;            (flags '(f w))
;;            (conditions '("^X-Virus: Yes"))
;;            (action "virus"))
;;           (procmail-recipe
;;            (lockfile? #t)
;;            (conditions '("^From: Google"))
;;            (action "spam"))))))
;; 
;; 
;; ;;;
;; ;;; Fetchmail.
;; ;;;
;; 
;; 
;; ;; ;;; Fetchmail accounts.
;; ;; 
;; ;; (define list-of-strings? (list-of string?))
;; ;; (define (string-or-integer? val)
;; ;;   (or (string? val)
;; ;;       (integer? val)))
;; ;; 
;; ;; (define (fetchmail-uglify-field-name field-name)
;; ;;   (let* ((str* (symbol->string field-name))
;; ;;         (str (if (string=? str* "bad-header")
;; ;;                  str*
;; ;;                  (string-delete #\- str*))))
;; ;;       (if (string-suffix? "?" str)
;; ;;           (string-drop-right str 1)
;; ;;           str)))
;; ;; 
;; ;; (define* (fetchmail-serialize-field field-name val #:key (toplevel? #f))
;; ;;   (cond
;; ;;    ((boolean? val) (fetchmail-serialize-boolean field-name val))
;; ;;    (else #~(string-append #$(if toplevel? "" "    ")
;; ;;                           #$(fetchmail-uglify-field-name field-name)
;; ;;                           " "  #$(maybe-object->string val) "\n"))))
;; ;; 
;; ;; (define fetchmail-serialize-string fetchmail-serialize-field)
;; ;; (define fetchmail-serialize-integer fetchmail-serialize-field)
;; ;; 
;; ;; (define (fetchmail-serialize-toplevel field-name val)
;; ;;   (fetchmail-serialize-field field-name val #:toplevel? #t))
;; ;; 
;; ;; (define (fetchmail-serialize-boolean field-name val)
;; ;;   (if val (fetchmail-serialize-field field-name "") ""))
;; ;; 
;; ;; (define (fetchmail-serialize-list-of-strings field-name val)
;; ;;   (fetchmail-serialize-field
;; ;;    field-name
;; ;;    (string-join val ", ")))
;; ;; 
;; ;; (define (fetchmail-serialize-escaped-string field-name val)
;; ;;   (if (string? val)
;; ;;       (fetchmail-serialize-string field-name (string-append "\"" val "\""))
;; ;;       ""))
;; ;; 
;; ;; (define-enum fetchmail-authentication
;; ;;   '(any password kerberos_v5 kerberos gssapi
;; ;;         cram-md5 otp ntlm msn external ssh))
;; ;; 
;; ;; (define-enum reject-or-accept
;; ;;   '(reject accept))
;; ;; 
;; ;; (define-maybe string
;; ;;   (prefix fetchmail-))
;; ;; 
;; ;; (define-maybe list-of-strings
;; ;;   (prefix fetchmail-))
;; ;; 
;; ;; (define-maybe/no-serialization string-or-integer)
;; ;; 
;; ;; (define-configuration/no-serialization fetchmail-envelope
;; ;;   (header
;; ;;    (string)
;; ;;    "The header Fetchmail assumes will carry a copy of a mail's envelope address.")
;; ;;   (count
;; ;;    (maybe-integer 'disabled)
;; ;;    "The number of header lines of this kind are skipped.  A count of 1 means:
;; ;; skip the first, take the second.  A count of 2 means: skip the first and
;; ;; second, take the third, and so on"))
;; ;; 
;; ;; (define (fetchmail-serialize-fetchmail-envelope _ envelope)
;; ;;   (let ((count (fetchmail-envelope-count envelope)))
;; ;;     #~(string-append (if (integer? count)
;; ;;                          (string-append "    " (number->string count) " ")
;; ;;                          "    ")
;; ;;                      "envelope" " " (fetchmail-envelope-header envelope) "\n")))
;; ;; 
;; ;; (define-maybe fetchmail-envelope
;; ;;   (prefix fetchmail-))
;; ;; 
;; ;; (define-configuration fetchmail-account
;; ;;   (host
;; ;;    (string)
;; ;;    "The host from which the mail is fetched from."
;; ;;    fetchmail-serialize-toplevel)
;; ;;   (protocol
;; ;;    (string)
;; ;;    "The protocol used for fetching the mail.")
;; ;;   (username
;; ;;    (string)
;; ;;    "The username for the account."
;; ;;    fetchmail-serialize-escaped-string)
;; ;;   (password
;; ;;    (string)
;; ;;    "The password for the account."
;; ;;    fetchmail-serialize-escaped-string)
;; ;;   (via
;; ;;    (maybe-string 'disabled)
;; ;;    "The DNS name of the mail server, overriding the value of the @code{host}
;; ;; field.")
;; ;;   (local-domains
;; ;;    (maybe-list-of-strings '())
;; ;;    "A list of domains which Fetchmail should consider local.  When Fetchmail is
;; ;; parsing address lines in multidrop modes, and a trailing segment of a host
;; ;; name matches a declared local domain, that address is passed through to the
;; ;; listener or MDA unaltered (local-name mappings are not applied).")
;; ;;   (service
;; ;;    (maybe-string-or-integer 'disabled)
;; ;;    "A service name to connect to; if an integer is specified it will treated as
;; ;; the TCP port number."
;; ;;    fetchmail-serialize-field)
;; ;;   (authenticate
;; ;;    (fetchmail-authentication 'any)
;; ;;    "The authentication type for the account."
;; ;;    fetchmail-serialize-field)
;; ;;   (timeout
;; ;;    (integer 300)
;; ;;    "The server inactivity timeout in seconds.")
;; ;;   (envelope
;; ;;    (maybe-fetchmail-envelope 'disabled)
;; ;;    "A @code{<fetchmail-envelope>} record type that configures the
;; ;; envelope-address header name and count.")
;; ;;   (qvirtual
;; ;;    (maybe-string 'disabled)
;; ;;    "A string prefix that will be removed from the user name found in the header
;; ;; specified with the @code{envelope} field.")
;; ;;   (aka
;; ;;    (maybe-list-of-strings 'disabled)
;; ;;    "This field is for use with multidrop mailboxes; it allows you to
;; ;; pre-declare a list of DNS aliases for a server.  This is an optimization hack
;; ;; that allows you to trade space for speed. When fetchmail, while processing a
;; ;; multidrop mailbox, grovels through message headers looking for names of the
;; ;; mailserver, pre-declaring common ones can save it from having to do DNS
;; ;; lookups.")
;; ;;   (interface
;; ;;    (maybe-list-of-strings 'disabled)
;; ;;    "A list of IP interface that must be up for server poll to take place.
;; ;; A list of IP interfaces required be up and have a specific local or remote
;; ;; IPv4 (IPv6 is not supported yet) address (or range) before polling.  The format is the following
;; ;; 
;; ;; @example
;; ;; interface/iii.iii.iii.iii[/mmm.mmm.mmm.mmm]
;; ;; @end example
;; ;; 
;; ;; The field before the first slash is the interface name (e.g., @code{sl0},
;; ;; @code{ppp0}, etc.).  The field before the second slash is the acceptable IP
;; ;; address.  The field after the second slash is a mask which specifies a range
;; ;; of IP addresses to accept. If no mask is present 255.255.255.255 is
;; ;; assumed (i.e. an exact match)")
;; ;;   (monitor
;; ;;    (maybe-string 'disabled)
;; ;;    "Daemon mode can cause transient links which are automatically taken down
;; ;; after a period of inactivity (e.g. PPP links) to remain up indefinitely. This
;; ;; option identifies a system TCP/IP interface to be monitored for
;; ;; activity. After each poll interval, if the link is up but no other activity
;; ;; has occurred on the link, then the poll will be skipped.")
;; ;;   (plugin
;; ;;    (maybe-string 'disabled)
;; ;;    "An external program to establish the TCP connection.  This is useful if you
;; ;; want to use SSH, or need some special firewalling setup.  @code{%h} and
;; ;; @code{%p} will be substituted by the @code{host} and @code{port} options,
;; ;; respectively."
;; ;;    fetchmail-serialize-escaped-string)
;; ;;   ;; (plugout)
;; ;;   ;; (dns?)
;; ;;   ;; (check-alias)
;; ;;   ;; (uidl)
;; ;;   ;; (intervals)
;; ;;   ;; (trace-polls)
;; ;;   ;; (principal)
;; ;;   ;; (esmtp-name)
;; ;;   ;; (esmtp-password)
;; ;;   ;; (bad-header)
;; ;; 
;; ;;   ;; (ssl?)
;; ;;   ;; (ssl-cert)
;; ;;   ;; (ssl-cert-ck?)
;; ;;   ;; (ssl-cert-file)
;; ;;   ;; (ssl-cert-path)
;; ;;   ;; (ssl-fingerprint)
;; ;;   ;; (ssl-key)
;; ;;   ;; (ss-lproto)
;; ;;   
;; ;;   ;; (folder)
;; ;;   ;; (smtp-host)
;; ;;   ;; (fetch-domains)
;; ;;   ;; (smtp-address)
;; ;;   ;; (smtp-name)
;; ;;   ;; (anti-spam)
;; ;;   ;; (mda)
;; ;;   ;; (bsmtp)
;; ;;   ;; (pre-connect)
;; ;;   ;; (post-connect)
;; ;;   ;; (keep?)
;; ;;   ;; (flush?)
;; ;;   ;; (limit-flush?)
;; ;;   ;; (fetch-all?)
;; ;;   ;; (rewrite?)
;; ;;   ;; (stripcr?)
;; ;;   ;; (forcecr?)
;; ;;   ;; (pass-8-bits?)
;; ;;   ;; (drop-status?)
;; ;;   ;; (drop-delivered?)
;; ;;   ;; (mime-decode?)
;; ;;   ;; (idle?)
;; ;;   ;; (limit)
;; ;;   ;; (warnings)
;; ;;   ;; (batch-limit)
;; ;;   ;; (fetch-limit)
;; ;;   ;; (fetch-size-limit)
;; ;;   ;; (fast-uidl)
;; ;;   ;; (expunge)
;; ;;   ;; (properties)
;; ;;   (prefix fetchmail-))
;; ;; 
;; ;; (define list-of-fetchmail-accounts? (list-of fetchmail-account?))
;; ;; 
;; ;; ;;; Fetchmail global options.
;; ;; 
;; ;; (define-maybe string
;; ;;   (prefix fetchmail-global-))
;; ;; 
;; ;; (define-maybe integer
;; ;;   (prefix fetchmail-global-))
;; ;; 
;; ;; (define (fetchmail-global-serialize-boolean field-name val)
;; ;;   (if val
;; ;;       (string-append "set " (fetchmail-uglify-field-name field-name) "\n")
;; ;;       (string-append "set no" (fetchmail-uglify-field-name field-name) "\n")))
;; ;; 
;; ;; (define (fetchmail-global-serialize-string field-name val)
;; ;;   (string-append "set " (fetchmail-uglify-field-name field-name) " " val "\n"))
;; ;; 
;; ;; (define (fetchmail-global-serialize-integer field-name val)
;; ;;   (string-append "set" (fetchmail-uglify-field-name field-name)
;; ;;                  " " (number->string val) "\n"))
;; ;; 
;; ;; (define-configuration fetchmail-global-options
;; ;;   (daemon
;; ;;    (maybe-integer 'disabled)
;; ;;    "The background poll interval in seconds.")
;; ;;   (postmaster
;; ;;    (maybe-string 'disabled)
;; ;;    "The last-resort username to which multidrop mail is to be forwarded if no
;; ;; matching local recipient can be found.")
;; ;;   (bouncemail?
;; ;;    (boolean #t)
;; ;;    "If set to @code{#t}, error mail will be directed to the sender; if set to
;; ;; @code{#f}, error mail will be directed to the local postmaster, see the
;; ;; @code{postmaster} field.")
;; ;;   (spambounce?
;; ;;    (boolean #f)
;; ;;    "If set to @code{#t}, bounce blocked spam-blocked mail (as per the
;; ;; @code{antispam} field in @code{<fetchmail-account-configuration}) back to the
;; ;; destination as indicated by the @code{bouncemail} field.  If set to @code{#f},
;; ;; do not bounce spam-blocked mail")
;; ;;   (softbounce?
;; ;;    (boolean #t)
;; ;;    "If set to @code{#t}, keep permanently undeliverable mail as though a
;; ;; temporary error had occurred; if set to @code{#f}, delete permanently
;; ;; undeliverable mail.")
;; ;;   (logfile
;; ;;    (maybe-string 'disabled)
;; ;;    "The path to the file to append error and status messages to; it is only
;; ;; effective in daemon mode and if Fetchmail detaches. If effective, it overrides
;; ;; the @code{syslog} field.")
;; ;;   (pidfile
;; ;;    (maybe-string 'disabled)
;; ;;    "The path to the PID file.")
;; ;;   (idfile
;; ;;    (maybe-string 'disabled)
;; ;;    "The path to the file to store UID lists in.")
;; ;;   (syslog?
;; ;;    (boolean #f)
;; ;;    "If set to @code{#t}, perform error logging through
;; ;; Syslog (@pxref{Syslog,,,libc}).  It may be overridden
;; ;; by the @code{logfile} field.  If set to @code{#t}, disable error logging
;; ;; through @code{syslog}.")
;; ;;   (properties
;; ;;    (maybe-string 'disabled)
;; ;;    "A string value that is ignored by Fetchmail.  The string may be used
;; ;; to store configuration information for scripts which require it.  In
;; ;; particular, the output of @code{--configdump} option will make properties
;; ;; associated with a configuration readily available to a Python script")
;; ;;   (prefix fetchmail-global-))
;; ;; 
;; ;; ;;; Fetchmail toplevel configuration.
;; ;; 
;; ;; (define-configuration/no-serialization home-fetchmail-configuration
;; ;;   (package
;; ;;     (package fetchmail)
;; ;;     "The Fetchmail package to use.")
;; ;;   (fetchmail-home
;; ;;    (string "${$XDG_DATA_HOME:-$HOME/.local/share}/fetchmail")
;; ;;    "The value of the @code{FETCHMAILHOME} environment variable")
;; ;;   (global-options
;; ;;    (fetchmail-global-options (fetchmail-global-options))
;; ;;    "A @code{<fetchmail-global-options>} record type that configures the
;; ;; global options.")
;; ;;   (accounts
;; ;;    (list-of-fetchmail-accounts '())
;; ;;    "A list of @code{<fetchmail-account>} record types that configure the accounts.")
;; ;;   )
;; ;; 
;; ;; (define (fetchmail-files-service config)
;; ;;   (match config
;; ;;     (($ <home-fetchmail-configuration> _ package fetchmail-home
;; ;;                                        global-options accounts)
;; ;;      (let* ((config-file (string-append (string-drop fetchmail-home 1)
;; ;;                                         "/fetchmailrc")))
;; ;;        `((,config-file
;; ;;           ,(apply mixed-text-file "fetchmail-config"
;; ;;                   (serialize-configuration
;; ;;                    global-options fetchmail-global-options-fields)
;; ;;                   (map (lambda (account)
;; ;;                          (serialize-configuration
;; ;;                           account fetchmail-account-fields))
;; ;;                        accounts))))))))
;; ;; 
;; ;; (define (fetchmail-profile-service config)
;; ;;   (list (home-fetchmail-configuration-package config)))
;; ;; 
;; ;; (define (fetchmail-environment-variables-service config)
;; ;;   `(("FETCHMAILHOME" . ,(home-fetchmail-configuration-fetchmail-home config))))
;; ;; 
;; ;; (define home-fetchmail-service-type
;; ;;   (service-type (name 'home-fetchmail)
;; ;;                 (extensions
;; ;;                  (list ;; (service-extension
;; ;;                   ;;  home-mcron-service-type
;; ;;                   ;;  fetchmail-mcron-service)
;; ;;                   (service-extension
;; ;;                    home-environment-variables-service-type
;; ;;                    fetchmail-environment-variables-service)
;; ;;                   (service-extension
;; ;;                    home-profile-service-type
;; ;;                    fetchmail-profile-service)))
;; ;;                 (default-value (home-fetchmail-configuration))
;; ;;                 (description "Configure Fetchmail for fetching mail from a remote")))
;; ;; 
;; ;; (define test-config
;; ;;   (home-fetchmail-configuration
;; ;;    (global-options
;; ;;     (fetchmail-global-options
;; ;;      (softbounce? #f)))
;; ;;    (accounts
;; ;;     (list (fetchmail-account
;; ;;            (host "mail.yoctocell.xyz")
;; ;;            (protocol "IMAP")
;; ;;            (username "public@yoctocell.xyz")
;; ;;            (password "test"))))))
;; ;; 
;; ;; (fetchmail-files-service test-config)

D yoctocell/gnu/services/messaging.scm => yoctocell/gnu/services/messaging.scm +0 -185
@@ 1,185 0,0 @@
;; (define-module (yoctocell gnu services messaging)
;;   #:use-module (gnu services)
;;   #:use-module (gnu home-services)
;;   #:use-module (gnu home-services files)
;;   #:use-module (gnu home-services-utils)
;;   #:use-module (gnu services configuration)
;;   #:use-module (gnu home-services shepherd)
;;   #:use-module (gnu services shepherd)
;;   #:use-module (gnu packages irc)
;;   #:use-module (guix packages)
;;   #:use-module (guix gexp)
;;   #:use-module (ice-9 match)
;;   #:export (home-weechat-configuration
;;             home-weechat-service-type))
;; 
;; 
;; (define list-of-lists?
;;   (list-of list?))
;; 
;; (define-configuration/no-serialization home-weechat-configuration
;;   (package
;;     (package weechat)
;;     "The WeeChat package to use.")
;;   (relay?
;;    (boolean #f)
;;    "Whether to run a headless WeeChat relay so other clients can connect to it
;; remotely, see the
;; @uref{https://www.weechat.org/files/doc/stable/weechat_user.en.html#relay_plugin,
;; official documentation} for how to configure a relay.")
;;   (relay-options
;;    (list '())
;;    "Command line options to give the WeeChat relay, this will only have a effect
;; if the @code{weechat-relay?} option is set to @code{#t}.  See @command{man
;; weechat-headless} for a list of valid options.")
;;   (relay-environment-variables
;;    (list '())
;;    "List of environment variables to set for the WeeChat relay.")
;;   (config
;;    (list-of-lists '())
;;    "Nested lists representing configuration options to set in WeeChat.  The
;; first element of a nested list is the name of the configuration file, e.g., if
;; the configuration looks like this
;; 
;; @lisp
;; (home-weechat-configuration
;;   (config
;;     '((irc
;;        ((server
;;          ((libera.addresses . \"irc.libera.chat/6697\")
;;           (libera.ssl? . #t)))
;;         (server_default
;;          ((autoconnect? . #f))))))))
;; @end lisp
;; 
;; the second element of the list will represent the contents of the
;; @file{$XDG_CONFIG_HOME/weechat/irc.conf} file.  The configuration
;; corresponding to the file also consists of nested lists, the first element of
;; the list is the name of the @dfn{section}, @code{server} in the first case;
;; the second element is an association list of options to set under that
;; section.  The resulting @file{$XDG_CONFIG_HOME/weechat/irc.conf} will look
;; like this
;; 
;; @example
;; [server]
;; libera.addresses = irc.libera.chat/6697
;; libera.ssl = on
;; 
;; [server_default]
;; autoconnect = off
;; @end example."))
;; 
;; (define (weechat-serialize-section section-name value)
;;   (define (uglify-field-name field-name)
;;     (let ((str (string-map (lambda (char)
;;                              (case char
;;                                ((#\-) #\_)
;;                                (else char)))
;;                            (symbol->string field-name))))
;;       (if (string-suffix? "?" str)
;;           (string-drop-right str 1)
;;           str)))
;; 
;;   (define (serialize-field field-name val)
;;     (cond
;;      ((boolean? val) (serialize-boolean field-name val))
;;      ((symbol? val) (serialize-symbol field-name val))
;;      (else #~(format #f "~a = ~a\n" #$(uglify-field-name field-name) #$val))))
;; 
;;   (define (serialize-boolean field-name val)
;;     (serialize-field field-name (if val "on" "off")))
;; 
;;   (define (serialize-symbol field-name val)
;;     (serialize-field field-name (symbol->string val)))
;; 
;;   #~(string-append
;;      "\n[" #$(maybe-object->string section-name) "]\n"
;;      #$@(map (match-lambda
;;                ((key . val)
;;                 (serialize-field key val)))
;;              value)))
;; 
;; (define (weechat-serialize-file value)
;;   #~(string-append
;;      #$@(map (match-lambda
;;                ((section-name alist)
;;                 (weechat-serialize-section section-name alist)))
;;              value)))
;; 
;; (define (serialize-weechat-file-configuration config)
;;   (weechat-serialize-file config))
;; 
;; ;; WeeChat will often try to write stuff to the config, so we don't symlink
;; ;; the files to the store, instead, we overwrite the old config during the
;; ;; activation.
;; (define (weechat-activation-service config)
;;   #~(begin
;;       (define files
;;         '#$(map car (home-weechat-configuration-config config)))
;;       
;;       (define configs
;;         (list #$@(map (match-lambda
;;                       ((basename config)
;;                        (weechat-serialize-file config)))
;;                     (home-weechat-configuration-config config))))
;; 
;;       (for-each (lambda (file config)
;;                   (call-with-output-file
;;                       (let ((xdg-config-home (getenv "XDG_CONFIG_HOME")))
;;                         (string-append (or xdg-config-home (getenv "HOME"))
;;                                        (if xdg-config-home "" "/.config")
;;                                        "/weechat/" (symbol->string file) ".conf"))
;;                     (lambda (port)
;;                       (display config port))))
;;                 files configs)))
;; 
;; (define (weechat-profile-service config)
;;   (list (home-weechat-configuration-package config)))
;; 
;; (define (weechat-shepherd-service config)
;;   (match config
;;     (($ <home-weechat-configuration> _ package relay? relay-options
;;                                      relay-environment-variables)
;;      (optional relay?
;;                (list (shepherd-service
;;                       (documentation "WeeChat relay, use one of the supported
;; clients to connect to it.")
;;                       (provision '(weechat-relay))
;;                       (start #~(make-forkexec-constructor
;;                                 (list #$(file-append
;;                                          package "/bin/weechat-headless")
;;                                       #$@relay-options)
;;                                 #:environment-variables
;;                                 relay-environment-variables))
;;                       (stop #~(make-kill-destructor))))))))
;; 
;; (define home-weechat-service-type
;;   (service-type (name 'home-weechat)
;;                 (extensions
;;                  (list (service-extension
;;                         home-activation-service-type
;;                         weechat-activation-service)
;;                        (service-extension
;;                         home-profile-service-type
;;                         weechat-profile-service)
;;                        (service-extension
;;                         home-shepherd-service-type
;;                         weechat-shepherd-service)))
;;                 (default-value (home-weechat-configuration))
;;                 (description "Configure the WeeChat IRC client, and optionally
;; run a WeeChat relay")))
;; 
;; 
;; (define test-config
;;   (home-weechat-configuration
;;    (config
;;     '((alias
;;        ((cmd
;;          ((BYE . "quit")))))
;;       (irc
;;        ((server
;;          ((libera.addresses . "irc.libera.chat/6697")
;;           (libera.ssl? . #t)
;;           (libera.username . "yoctocell_")))))))))
;; 
;; ;; (weechat-files-service test-config)

D yoctocell/gnu/services/notifications.scm => yoctocell/gnu/services/notifications.scm +0 -202
@@ 1,202 0,0 @@
;; (define-module (yoctocell gnu services notifications)
;;   #:use-module (guix gexp)
;;   #:use-module (guix packages)
;;   #:use-module (gnu packages dunst)
;;   #:use-module (gnu packages gnome)
;;   #:use-module (gnu services configuration)
;;   #:use-module (gnu home-services)
;;   #:use-module (gnu home-services files)
;;   #:use-module (gnu home-services-utils)
;;   #:use-module (gnu home-services shepherd)
;;   #:use-module (ice-9 match)
;;   #:use-module (srfi srfi-1)
;;   #:use-module (srfi srfi-9)
;;   #:export (home-dunst-service-type
;;             home-dunst-configuration
;;             dunst-icon-theme))
;; 
;; (define-configuration/no-serialization dunst-icon-theme
;;   (package
;;     (package)
;;     "The package providing the theme.")
;;   (name
;;    (string)
;;    "The name of the theme within the package.")
;;   (size
;;    (string "32x32")
;;    "The size of the icon"))
;; 
;; (define (dunst-serialize-dunst-icon-theme field-name val)
;;   (match val
;;     (($ <dunst-icon-theme> _ package name size)
;;      (define base-paths
;;        #~(list #$(string-append (getenv "HOME") "/.guix-home/profile")
;;                #$(file-append package)))
;; 
;;      (define categories
;;        #~(list "actions" "animations" "apps" "categories" "devices" "emblems"
;;                "emotes" "filesystem" "intl" "legacy" "mimetypes" "places"
;;                "status" "stock"))
;; 
;;      (use-modules ((srfi srfi-1)))
;;      
;;      (define icon-paths
;;        #~(string-join
;;           ((@ (srfi srfi-1) concatenate)
;;            (map (lambda (base-path)
;;                   (map (lambda (category)
;;                      (string-append base-path "/share/icons/" #$name "/"
;;                                       #$size "/" category))
;;                        #$categories))
;;                 #$base-paths))
;;           ":"))
;;      
;;      #~(string-append "\nicon_path = " #$icon-paths))))
;; 
;; (define (dunst-serialize-section-name name)
;;   (string-append "\n\n[" name  "]"))
;; 
;; (define (dunst-uglify-key key)
;;   (let ((str (string-map (lambda (char)
;;                            (case char
;;                              ((#\-) #\_)
;;                              (else char)))
;;                          (maybe-object->string key))))
;;     (if (string-suffix? "?" str)
;;         (string-drop-right str 1)
;;         str)))
;; 
;; (define (dunst-serialize-option key val)
;;   (cond
;;    ((boolean? val)
;;     (dunst-serialize-boolean key val))
;;    (else
;;     #~(string-append "\n" #$(dunst-uglify-key key)
;;                      " = " #$(maybe-object->string val)))))
;; 
;; (define (dunst-serialize-boolean key val)
;;   (dunst-serialize-option key (boolean->true-or-false val)))
;; 
;; (define (dunst-uglify-field-name field-name)
;;   (let ((str (symbol->string field-name)))
;;     (if (string-suffix? "-option" str)
;;         (string-drop-right str 7)
;;         str)))
;; 
;; (define (dunst-serialize-alist field-name val)
;;   #~(string-append #$(dunst-serialize-section-name
;;                       (dunst-uglify-field-name field-name))
;;                    #$(dunst-serialize-options val)))
;; 
;; (define (dunst-serialize-options options)
;;   #~(string-append
;;      #$@(map (match-lambda
;;                ((key . val)
;;                 (dunst-serialize-option key val)))
;;              options)))
;; 
;; (define (dunst-serialize-custom-section field-name config)
;;   (match config
;;     (($ <dunst-custom-section> _ name)
;;      #~(string-append #$(dunst-serialize-section-name name)
;;                       #$(dunst-serialize-options config)))))
;; 
;; (define (dunst-serialize-list-of-dunst-custom-sections field-name config)
;;   #~(string-append #$@(map dunst-serialize-custom-section config)))
;; 
;; (define-configuration/no-serialization dunst-custom-section
;;   (name
;;    (string)
;;    "The name of the section.")
;;   (options
;;    (alist '())
;;    "An association list of options to set."))
;; 
;; (define list-of-dunst-custom-sections?
;;   (list-of dunst-custom-section?))
;; 
;; (define-configuration home-dunst-configuration
;;   (package
;;     (package dunst)
;;     "The Dunst package to use."
;;     empty-serializer)
;;   (extra-options
;;    (list-of-strings '())
;;    "A list of extra command line options to give to Dunst."
;;    empty-serializer)
;;   (global-options
;;    (alist '())
;;    "An association list of global options to set")
;;   (icon-theme
;;    (dunst-icon-theme)
;;    "The icon theme to use.")
;;   (urgency-low-options
;;    (alist '())
;;    "An association list of options to set for low-urgency notifications.")
;;   (urgency-normal-options
;;    (alist '())
;;    "An association list of options to set for normal-urgency
;; notifications.")
;;   (urgency-critical-options
;;    (alist '())
;;    "An association list of options to set for ciritical-urgency
;; notifications.")
;;   (custom-sections
;;    (list-of-dunst-custom-sections '())
;;    "A list of @code{<dunst-custom-section>} records for adding custom
;; sections.")
;;   (prefix dunst-))
;; 
;; (define (dunst-files-service config)
;;   `(("config/dunst/dunstrc"
;;      ,(mixed-text-file "dunst-dunstrc"
;;                        (serialize-configuration
;;                         config
;;                         home-dunst-configuration-fields)))))
;; 
;; (define (dunst-shepherd-service config)
;;   (match config
;;     (($ <home-dunst-configuration> _ package extra-options)
;;      (let ((config-file (mixed-text-file "dunst-dunstrc"
;;                                          (serialize-configuration
;;                                           config
;;                                           home-dunst-configuration-fields))))
;;        (list (shepherd-service
;;               (documentation "Dunst notification daemon.")
;;               (provision '(dunst))
;;               (start #~(make-forkexec-constructor
;;                         (list #$(file-append package "/bin/dunst")
;;                               "-config" #$config-file
;;                               #$@extra-options)))
;;               (stop #~(make-kill-destructor))))))))
;; 
;; (define (dunst-profile-service config)
;;   (list (home-dunst-configuration-package config)))
;; 
;; (define home-dunst-service-type
;;   (service-type (name 'home-fetchmail)
;;                 (extensions
;;                  (list
;;                   (service-extension
;;                    home-shepherd-service-type
;;                    dunst-shepherd-service)
;;                   (service-extension
;;                    home-profile-service-type
;;                    dunst-profile-service)))
;;                 (description "Configure the Dunst notification daemon.")))
;; 
;; (define test-config
;;   (home-dunst-configuration
;;    (icon-theme (dunst-icon-theme
;;                 (package adwaita-icon-theme)
;;                 (name "Adwaita")
;;                 (size "32x32")))
;;    (global-options
;;     '((transparency . 0)
;;       (padding . 5)
;;       (frame-width . 3)
;;       (frame-color . "\"#feacd0\"")))
;;    (urgency-normal-options
;;     '((background . "\"#181a20\"")))))
;; 
;; (dunst-files-service test-config)

D yoctocell/gnu/services/version-control.scm => yoctocell/gnu/services/version-control.scm +0 -495
@@ 1,495 0,0 @@
;; (define-module (yoctocell gnu services version-control)
;;   #:use-module (gnu services)
;;   #:use-module (gnu home-services)
;;   #:use-module (gnu home-services files)
;;   #:use-module (gnu home-services version-control)
;;   #:use-module (gnu home-services-utils)
;;   #:use-module (gnu services configuration)
;;   #:use-module (gnu packages bash)
;;   #:use-module (gnu packages mail)
;;   #:use-module (guix packages)
;;   #:use-module (guix records)
;;   #:use-module (guix gexp)
;;   #:use-module (guix diagnostics)
;;   #:use-module (guix i18n)
;;   #:use-module (guix import utils)
;;   #:use-module (ice-9 match)
;;   #:use-module (ice-9 regex)
;;   #:use-module (srfi srfi-1)
;;   #:use-module (srfi srfi-9)
;;   #:use-module (srfi srfi-11)
;;   #:use-module (srfi srfi-26)
;;   #:use-module (gnu home-services files)
;;   #:use-module (gnu home-services state)
;;   #:use-module (gnu packages version-control)
;;   #:use-module (guix packages)
;;   #:use-module (guix gexp)
;;   #:use-module (guix monads)
;;   #:use-module (guix modules)
;;   #:use-module (guix records)
;; 
;; 
;;   #:export (
;;             ;; home-hg-state-service-type
;;             ;; hg-state-configuration
;; 
;;             home-grokmirror-service-type
;;             grokmirror-configuration
;;             grokmirror-remote-configuration))
;; 
;; 
;; ;;;
;; ;;; Hg.
;; ;;;
;; 
;; ;; (define-configuration/no-serialization hg-state-configuration
;; ;;   (path
;; ;;    (string)
;; ;;    "The directory for the Mercurial repository.")
;; ;;   (remote
;; ;;    (string)
;; ;;    "A URI pointing to the remote repository for the @code{directory} field.")
;; ;;   (config
;; ;;    (alist '())
;; ;;    "A list of lists containing the configuration for the Mercurial
;; ;; repository, it uses the same syntax as the @code{config} field for the
;; ;; @code{home-mercurial-configuration} record type.  The contents will be
;; ;; written to the @file{.hg/hgrc}."))
;; ;; 
;; ;; (define (hg-state-state-service config)
;; ;;   (match config
;; ;;     (($ <hg-state-configuration> _ path remote repo-config)
;; ;;      (state-generic
;; ;;       path
;; ;;       #:init-gexp
;; ;;       #~(lambda* (_ self)
;; ;;           (let* ((meta (first (action self 'metadata)))
;; ;;                  (path (assoc-ref meta 'path))
;; ;;                  (remote (assoc-ref meta 'remote)))
;; ;;             (format #t "Initializing ~a.\n" self)
;; ;;             ;; TODO: revisit git clone implementation
;; ;;             ;; FIXME: Hang up shepherd if username/password asked
;; ;;             (let* ((port ((@@ (guix build utils) open-pipe-with-stderr)
;; ;;                           #$(file-append mercurial "/bin/hg")
;; ;;                           "clone" remote path)))
;; ;;               (waitpid WAIT_ANY)
;; ;;               (display ((@@ (ice-9 rdelim) read-delimited) "" port))
;; ;;               (close-port port))
;; ;;      
;; ;;             (unless (null? repo-config)
;; ;;               (call-with-output-file (string-append path "/.hg/hgrc")
;; ;;                 (lambda (port)
;; ;;                   (display #$(serialize-hg-config repo-config) port))))))
;; ;;       #:additional-metadata `((remote . ,remote)
;; ;;                               (general-sync? . #f))))))
;; ;; 
;; ;; (define home-hg-state-service-type
;; ;;   (service-type (name 'hg-state)
;; ;;                 (extensions
;; ;;                  (list (service-extension
;; ;;                         home-state-service-type
;; ;;                         (lambda (configs)
;; ;;                           (map hg-state-state-service configs)))))
;; ;;                 (description "Clone and configure, Mercurial repositories")))
;; 
;; 
;; 
;; 
;; 
;; 
;; ;;;
;; ;;; Grokmirror.
;; ;;;
;; 
;; (define (uglify-field-name field-name)
;;   (string-map (lambda (char)
;;                         (case char
;;                           ((#\-) #\_)
;;                           (else char)))
;;               (string-delete #\? (symbol->string field-name))))
;; 
;; (define (serialize-field field-name val)
;;   (cond
;;    ((list? val) (serialize-list field-name val))
;;    ((boolean? val) (serialize-boolean field-name val))
;;    ((eq? val 'disabled) "")
;;    (else #~(format #f "~a = ~a\n" #$(uglify-field-name field-name) #$val))))
;; 
;; (define serialize-string serialize-field)
;; (define serialize-integer serialize-field)
;; (define (serialize-symbol field-name val)
;;   (serialize-field field-name (symbol->string val)))
;; 
;; (define (serialize-list field-name val)
;;   (if (null? val) "" (serialize-field field-name (string-join val))))
;; 
;; (define (serialize-multiline-list field-name val)
;;   (if (null? val)
;;       ""
;;       (let* ((length (string-length (symbol->string field-name)))
;;              (delim (string-append "\n" (make-string (+ length 3) #\space))))
;;         (serialize-field field-name (string-join val delim)))))
;; 
;; (define (serialize-boolean field-name bool)
;;   (serialize-field field-name (boolean->yes-or-no bool)))
;; 
;; ;; REVIEW: file-like?
;; (define (list-of-strings-or-gexps? val)
;;   (list-of (lambda (val) (or (string? val) (gexp? val)))))
;; 
;; (define (string-or-list-of-strings? val)
;;   (or (string? val) (list-of-strings? val)))
;; 
;; (define (percent? val)
;;   (and (integer? val) (and (<= val 100) (>= val 0))))
;; 
;; (define-maybe string)
;; (define-maybe integer)
;; (define-maybe grokmirror-remote-configuration)
;; (define-maybe grokmirror-pull-configuration)
;; (define-maybe/no-serialization string-or-gexp)
;; (define-maybe/no-serialization list-of-strings)
;; (define-maybe/no-serialization string-or-list-of-strings)
;; 
;; (define-enum grokmirror-operation-modes
;;   '(onetime continous))
;; 
;; (define-enum grokmirror-log-level
;;   '(info debug))
;; 
;; (define-configuration grokmirror-manifest-configuration
;;   (pretty?
;;    (boolean #f)
;;    "Whether to save pretty-printed JavaScript.  This will be slower and
;; use more disk space, but will be easier to debug.")
;;   (ignore
;;    (list-of-strings '())
;;    "List of repositories to ignore; shell glob syntax can be used for
;; matching repositories."
;;    serialize-multiline-list)
;;   (fetch-objstore
;;    (boolean #f)
;;    "Whether to fetch objects into objstore repositories after a
;; commit.  This can be useful if someone tries to push the same objects
;; to a sibling repository, but may significantly slow down post-commit
;; hook operation, negating any speed gains. If set to @code{#f}, the objects
;; will be fetched during regular @command{grok-fsck} runs.")
;;   (check-export-ok
;;    (boolean #f)
;;    "Whether to only include repositories that have a
;; @file{git-daemon-export-ok} file."))
;; 
;; (define (serialize-grokmirror-manifest-configuration field-name val)
;;   #~(string-append "\n[manifest]\n"
;;                     #$(serialize-configuration
;;                        val
;;                        grokmirror-manifest-configuration-fields)))
;; 
;; (define-configuration grokmirror-remote-configuration
;;   (site
;;    (string)
;;    "The scheme plus host part of the URL you are pulling from,
;; e.g. the scheme plus host part of
;; @uref{https://www.gnu.org/gnu/about-gnu.html} would be
;; @samp{https://www.gnu.org}.")
;;   (manifest
;;    (maybe-string 'disabled)
;;    "Where the grok manifest is published on the remote.  Note that you
;; cannot specify @code{USERNAME:PASSWORD} as part of the URL with
;; Grokmirror 2.x and above.  You can use a @file{netrc} file for this
;; purpose (@pxref{The .netrc file,,, inetutils, GNU Inetutils}).")
;;   (manifest-command
;;    (maybe-string-or-gexp 'disabled)
;;    "An alternative to setting the @code{manifest} URL.  It should be an
;; executable that has three possible outcomes:
;; 
;; @itemize
;; @item exit code 0 and full remote manifest on stdout (must be valid
;; JSON)
;; @item exit code 1 and error message on stdout
;; @item exit code 127 and nothing on stdout if remote manifest hasn't
;; changed
;; @end itemize
;; 
;; It should also accept @code{--force} as a single argument to force
;; manifest retrieval even if it hasn't changed."
;;    serialize-string)
;;   (preload-bundle-url
;;    (maybe-string 'disabled)
;;    "The URL to a pre-generated preload bundles provided by the remote.
;; This is only useful if you are mirroring the entire repository
;; collection and not just a handful of select repos."))
;; 
;; (define (serialize-grokmirror-remote-configuration field-name val)
;;   #~(string-append "\n[remote]\n"
;;                     #$(serialize-configuration
;;                        val
;;                        grokmirror-remote-configuration-fields)))
;; 
;; (define-configuration grokmirror-pull-configuration
;;   (projectslist
;;    (maybe-string 'disabled)
;;    "Where to write the @file{project.list} file used by Gitweb or
;; Cgit (@pxref{Cgit Service}).")
;;   (projectslist-trimtop
;;    (maybe-string 'disabled)
;;    "Subpath to start at when generating the @file{projects.list} file.
;; This is Useful when generating multiple Gitweb/Cgit configurations for
;; the same tree. ")
;;   (projectslist-symlinks?
;;    (boolean #f)
;;    "Whether to create entries for symlinks when generating the
;; @file{projects.list} file.")
;;   (post-update-hook
;;    (list-of-strings-or-gexps '())
;;    "Hooks to execute whenever a repository is modified.  The hook should
;; take the full path to the modified Git repository as the final
;; argument."
;;    serialize-multiline-list)
;;   (purge?
;;    (boolean #t)
;;    "Whether repositories that are not present in the remote manifest
;; should be purged.")
;;   (nopurge
;;    (list-of-strings '())
;;    "List of repositories that aren't replicated with Grokmirror that
;; should not be purged.  Shell-style globbing can be used to match
;; directories."
;;    serialize-multiline-list)
;;   (purgeprotect
;;    (percent 5)
;;    "Refuse a purge if the remote manifest has over a certain percantage
;; fewer repositories than what the client has.  This prevents
;; catastrophic mirror purges when our upstream gives the client a
;; manifest that is dramatically smaller than the client's."
;;    serialize-field)
;;   (default-owner
;;     (string "Grokmirror User")
;;     "The default owner to be displayed in Gitweb and Cgit web interfaces.")
;;   (remotename
;;    (string "_grokmirror")
;;    "The name of the upstream origin.")
;;   (pull-threads
;;    (integer 5)
;;    "The number of threads to use when running @command{grok-pull}.")
;;   (retries
;;    (integer 3)
;;    "The number of retries before giving up if @command{git fetch} fails.")
;;   (include
;;    (string-or-list-of-strings "*")
;;    "Pattern for all the repositories you would like to mirror, @code{*}
;; means to mirror everything."
;;    serialize-field)
;;   (exclude
;;    (maybe-string-or-list-of-strings 'disabled)
;;    "Pattern for all the repositories you do not want to mirror,
;; @code{*} will match everything."
;;    serialize-field)
;;   (ffonly
;;    (maybe-string-or-list-of-strings 'disabled)
;;    "Repositories that should always reject forced pushes."
;;    serialize-field)
;;   (refresh
;;    (maybe-integer 'disabled)
;;    "If enabled, running @command{grok-pull -o}, grok-pull will
;; grok-pull will run continuously and will periodically recheck the
;; remote manifest for new updates.")
;;   (socket
;;    (maybe-string 'disabled)
;;    "Path to the socket to listen on for any push updates."))
;; 
;; (define (serialize-grokmirror-pull-configuration field-name val)
;;   #~(string-append "\n[pull]\n"
;;                     #$(serialize-configuration
;;                        val
;;                        grokmirror-pull-configuration-fields)))
;; 
;; (define-configuration grokmirror-fsck-configuration
;;   (frequency
;;    (integer 30)
;;    "How often (in days) each repository should be checked.")
;;   (statusfile
;;    (string "${core:toplevel}/fsck.status.js")
;;    "Where to keep the status file.")
;;   (ignore-errors
;;    (list-of-strings '())
;;    "Errors that can safely be ignored, it only has to match a substring
;; of the full error."
;;    serialize-multiline-list)
;;   (reclone-on-errors
;;    (list-of-strings '())
;;    "If the fsck process finds errors that match any of these strings
;; during its run, it will ask @command{grok-pull} to reclone the
;; repository when it runs next."
;;    serialize-multiline-list)
;;   (repack?
;;    (boolean #t)
;;    "Whether repositories should be repacked.")
;;   (extra-repack-flags
;;    (list-of-strings '())
;;    "Flags for repacking depending if the repository is using
;; alternates or not, and whether this is a full repack or not."
;;    serialize-multiline-list)
;;   (extra-repack-flags-full
;;    (list-of-strings '())
;;    "Flags to add in addition to @code{extra-repack-flags}."
;;    serialize-multiline-list)
;;   (commitgraph
;;    (boolean #t)
;;    "Whether to generate commit graphs.  Graph generation will be skipped
;; for child repositories that use alternates.")
;;   (prune
;;    (boolean #f)
;;    "Whether to run @command{git prune} to remove obsolete loose objects.")
;;   (precious
;;    (string "yes")
;;    "If set to @code{yes}, temporarily turn of prevention of potential
;; repository corruption between @command{grok-fsck} runs when running
;; scheduled repacks in order to be able to delete redundant packs and
;; loose objects that have already been packed.  If set to @code{always},
;; Grokmirror will still help prevent repository corruption even udring
;; @command{grok-fsck} runs.")
;;   (baselines
;;    (list-of-strings '())
;;    "If you have a lot of forks using the same objstore repository, you
;; may end up with thousands of refs being negotiated during each remote
;; update.  This tends to result in higher load and bigger negotiation
;; transfers.  Setting the @code{baselines} option allows you to designate a
;; set of repos that are likely to have most of the relevant objects and
;; ignore the rest of the objstore refs."
;;    serialize-multiline-list)
;;   (islandcores
;;    (list-of-strings '())
;;    "Objstore repos are repacked with delta island support, but if you
;; have one repo that is a lot more likely to be cloned than all the
;; other ones, you can designate it as @dfn{islandCore} which will give
;; it priority when creating packs."
;;    serialize-multiline-list)
;;   (preload-bundle-outdir
;;    (maybe-string 'disabled)
;;    "Generate repload bundles for objstore repositories and put them into
;; the given path.")
;;   (report-to
;;    (string "root")
;;    "Who to send the report to if there are critical errors.")
;;   (report-from
;;    (string "root")
;;    "From who to send the report as if there are critical errors.")
;;   (report-subject
;;    (string "Git fsck error on Grokmirror Git repository")
;;    "The supbject of the report.")
;;   (report-mailhost
;;    (string "localhost")
;;    "The mail host address to use for sending the report."))
;; 
;; (define (serialize-grokmirror-fsck-configuration field-name val)
;;   #~(string-append "\n[fsck]\n"
;;                     #$(serialize-configuration
;;                        val
;;                        grokmirror-fsck-configuration-fields)))
;; 
;; (define-configuration grokmirror-configuration
;;   (package
;;     (package grokmirror)
;;     "The Grokmirror package to use.")
;;   (operation-mode
;;    (grokmirror-operation-modes)
;;    "The operation mode for Grokmirror, it can be @code{onetime} or
;; @code{continous}.  In one-time operation mode, it downloads the latest
;; manifest and applies any outstanding updates.  If there are new
;; repositories or changes in the existing repositories,
;; @command{grok-pull} will perform the necessary git commands to clone
;; or fetch the required data from the master.  Once all updates are
;; applied, it will write its own manifest and exit.  In this mode,
;; grok-pull can be run manually or from cron.
;; 
;; In continuous operation mode (daemon), @command{grok-pull} will continue
;; running after all updates have been applied and will periodically
;; re-download the manifest from the server to check for new updates.
;; 
;; If you fetch your Git repositories more frequently than once every few
;; hours, it is recommended to it as a daemon in order to improve
;; performance."
;;    serialize-symbol)
;;   (toplevel
;;    (string)
;;    "The directory where mirrored Git repositories are stored.")
;;   (manifest-file
;;    (string "${toplevel}/manifest.js.gz")
;;    "The path to a file where the manifest file is stored.")
;;   (log
;;    (string "${toplevel}/log")
;;    "The path to a file where logs should be written to.")
;;   (loglevel
;;    (grokmirror-log-level 'info)
;;    "How much information to log, valid options are @code{info} and
;; @code{debug}."
;;    serialize-symbol)
;;   (objstore
;;    (string "${toplevel}/objstore")
;;    "The directory @dfn{object storage} repositories.  Grokmirror
;; version 2 and above can automatically recognize related repositories
;; by analyzing root commits.  If it finds two or more related
;; repositories, it can set up a unified @dfn{object storage} repository
;; and fetch all refs from each related repository.")
;;   (objstore-uses-plumbing?
;;    (boolean #t)
;;    "Whether to use Git plumbing commands for copying object into object
;; storage repositories.  This will improve performance if you have busy
;; mirrors.")
;;   (private
;;    (maybe-string 'disabled)
;;    "Private repositories that should ne reveal any objects.  Shell-style
;; globbing can be used to match repositories.")
;;   (manifest-config
;;    (grokmirror-manifest-configuration (grokmirror-manifest-configuration))
;;    "Configuration options used by @command{grok-manifest}.")
;;   (remote-config
;;    (grokmirror-remote-configuration)
;;    "Configuration options related to the remote, mostly used by
;; @command{grok-pull}.")
;;   (pull-config
;;    (grokmirror-pull-configuration (grokmirror-pull-configuration))
;;    "Configuration options used by @command{grok-pull}.")
;;   (fsck-config
;;    (grokmirror-fsck-configuration (grokmirror-fsck-configuration))
;;    "Configuration options used by @command{grok-fsck}"))
;; 
;; (define (grokmirror-profile-service config)
;;   (list (grokmirror-configuration-package config)))
;; 
;; (define (serialize-grokmirror-config config)
;;   (mixed-text-file
;;    "grokmirror-config"
;;    #~(string-append "[core]\n"
;;                     #$(serialize-configuration
;;                        config
;;                        grokmirror-configuration-fields))))
;; 
;; (define (grokmirror-files-service config)
;;   `(("config/grokmirror/grokmirror.conf"
;;      ,(serialize-grokmirror-config config))))
;; 
;; (define home-grokmirror-service-type
;;   (service-type (name 'home-grokmirror)
;;                 (extensions
;;                  (list (service-extension
;;                         home-files-service-type
;;                         grokmirror-files-service)
;;                        (service-extension
;;                         home-profile-service-type
;;                         grokmirror-profile-service)))
;;                 (description "Install and configure Grokmirror.")))
;; 
;; ;; (define test-config
;; ;;   (grokmirror-configuration
;; ;;    (operation-mode 'onetime)
;; ;;    (toplevel "/tmp")
;; ;;    (manifest-file "/tmp/manifest")
;; ;;    (log (string-append (getenv "XDG_LOG_HOME") "/grokmirror.log"))
;; ;;    (loglevel 'debug)
;; ;;    (objstore "/tmp/objstore")
;; ;;    (remote-config
;; ;;     (grokmirror-remote-configuration
;; ;;      (site "alsdkf")
;; ;;      (manifest "asdf")))))