;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2023 Tanguy Le Carrour ;;; ;;; This file is part of GNU Guix. ;;; ;;; GNU Guix 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. ;;; ;;; GNU Guix 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 GNU Guix. If not, see . (define-module (gnu home services mail) #:use-module (guix gexp) #:use-module (guix records) #:use-module (gnu services) #:use-module (gnu services configuration) #:use-module (gnu home services) #:use-module (gnu home services shepherd) #:use-module (gnu home services utils) #:use-module (gnu packages mail) #:use-module (gnu packages guile) #:use-module (ice-9 match) #:use-module (ice-9 string-fun) #:use-module (srfi srfi-1) #:use-module (srfi srfi-26) #:use-module (srfi srfi-171) #:export (home-goimapnotify-configuration home-goimapnotify-configuration-fields home-goimapnotify-configuration? home-goimapnotify-configuration-accounts home-goimapnotify-service-type goimapnotify-account goimapnotify-account-fields goimapnotify-account-host goimapnotify-account-host-cmd goimapnotify-account-port goimapnotify-account-tls goimapnotify-account-tls-options goimapnotify-account-username goimapnotify-account-username-cmd goimapnotify-account-password goimapnotify-account-password-cmd goimapnotify-account-xoauth2 goimapnotify-account-on-new-mail goimapnotify-account-on-new-mail-post goimapnotify-account-wait goimapnotify-account-boxes goimapnotify-tls-options goimapnotify-tls-options-fields goimapnotify-tls-options-reject-unauthorized home-msmtp-configuration home-msmtp-configuration? home-msmtp-configuration-defaults home-msmtp-configuration-accounts home-msmtp-configuration-default-account home-msmtp-configuration-extra-content home-msmtp-service-type msmtp-configuration msmtp-configuration-auth? msmtp-configuration-tls? msmtp-configuration-tls-starttls? msmtp-configuration-tls-trust-file msmtp-configuration-log-file msmtp-configuration-host msmtp-configuration-port msmtp-configuration-user msmtp-configuration-from msmtp-configuration-password-eval msmtp-configuration-extra-content msmtp-account msmtp-account-name msmtp-account-configuration)) (define-maybe string (prefix msmtp-configuration-)) (define-maybe boolean (prefix msmtp-configuration-)) (define-maybe integer (prefix msmtp-configuration-)) ;; Serialization of 'msmtp'. (define (uglify-symbol field-name) (let* ((name (symbol->string field-name)) (ugly-name (string-replace-substring name "-" "_"))) (if (string-suffix? "?" ugly-name) (string-drop-right ugly-name 1) ugly-name))) (define (msmtp-configuration-serialize-boolean field-name value) #~(string-append #$(uglify-symbol field-name) " " (if #$value "on" "off") "\n")) (define (msmtp-configuration-serialize-string field-name value) #~(string-append #$(uglify-symbol field-name) " " #$value "\n")) (define (msmtp-configuration-serialize-maybe-string-no-underscore field-name value) #~(if #$(maybe-value-set? value) (string-append #$(string-replace-substring (uglify-symbol field-name) "_" "") " " #$value "\n") "")) (define (msmtp-configuration-serialize-integer field-name value) #~(string-append #$(uglify-symbol field-name) " " (number->string #$value) "\n")) (define (msmtp-configuration-serialize-extra-content field-name value) #~(if (string=? #$value "") "" (string-append #$value "\n"))) (define (msmtp-account-serialize-name field-name value) #~(string-append "\naccount " #$value "\n")) (define (msmtp-account-serialize-msmtp-configuration field-name value) #~(string-append #$(serialize-configuration value msmtp-configuration-fields))) (define (home-msmtp-configuration-serialize-list-of-msmtp-accounts field-name value) #~(string-append #$@(map (cut serialize-configuration <> msmtp-account-fields) value))) (define (home-msmtp-configuration-serialize-msmtp-configuration field-name value) #~(string-append "defaults\n" #$(serialize-configuration value msmtp-configuration-fields))) (define (home-msmtp-configuration-serialize-default-account field-name value) #~(if #$(maybe-value-set? value) (string-append "\naccount default : " #$value "\n") "")) (define (home-msmtp-configuration-serialize-extra-content field-name value) #~(if (string=? #$value "") "" (string-append #$value "\n"))) ;; Configuration of 'msmtp'. ;; Source . (define-configuration msmtp-configuration (auth? maybe-boolean "Enable or disable authentication.") (tls? maybe-boolean "Enable or disable TLS (also known as SSL) for secured connections.") (tls-starttls? maybe-boolean "Choose the TLS variant: start TLS from within the session (‘on’, default), or tunnel the session through TLS (‘off’).") (tls-trust-file maybe-string "Activate server certificate verification using a list of trusted Certification Authorities (CAs).") (log-file maybe-string "Enable logging to the specified file. An empty argument disables logging. The file name ‘-’ directs the log information to standard output." (serializer msmtp-configuration-serialize-maybe-string-no-underscore)) (host maybe-string "The SMTP server to send the mail to.") (port maybe-integer "The port that the SMTP server listens on. The default is 25 (\"smtp\"), unless TLS without STARTTLS is used, in which case it is 465 (\"smtps\").") (user maybe-string "Set the user name for authentication.") (from maybe-string "Set the envelope-from address.") (password-eval maybe-string "Set the password for authentication to the output (stdout) of the command cmd." (serializer msmtp-configuration-serialize-maybe-string-no-underscore)) (extra-content (string "") "Extra content appended as-is to the configuration block. Run @command{man msmtp} for more information about the configuration file format." (serializer msmtp-configuration-serialize-extra-content)) (prefix msmtp-configuration-)) (define-configuration msmtp-account (name (string) "The unique name of the account." (serializer msmtp-account-serialize-name)) (configuration (msmtp-configuration) "The configuration for this given account.") (prefix msmtp-account-)) (define (list-of-msmtp-accounts? lst) (every msmtp-account? lst)) (define-configuration home-msmtp-configuration (defaults (msmtp-configuration (msmtp-configuration)) "The configuration that will be set as default for all accounts.") (accounts (list-of-msmtp-accounts '()) "A list of @code{msmtp-account} records which contain information about all your accounts.") (default-account maybe-string "Set the default account." (serializer home-msmtp-configuration-serialize-default-account)) (extra-content (string "") "Extra content appended as-is to the configuration file. Run @command{man msmtp} for more information about the configuration file format." (serializer home-msmtp-configuration-serialize-extra-content)) (prefix home-msmtp-configuration-)) (define (home-msmtp-files config) (list `(".config/msmtp/config" ,(mixed-text-file "msmtp-config" (serialize-configuration config home-msmtp-configuration-fields))))) (define (home-msmtp-profile-entries config) (list msmtp)) (define home-msmtp-service-type (service-type (name 'home-msmtp) (extensions (list (service-extension home-profile-service-type home-msmtp-profile-entries) (service-extension home-files-service-type home-msmtp-files))) (default-value (home-msmtp-configuration)) (description "Configure msmtp, a simple @acronym{SMTP, Simple Mail Transfer Protocol} client that can relay email to SMTP servers."))) ; Configuration for goimapnotify from (gnu packages mail) (define-maybe string (prefix goimapnotify-)) (define-maybe integer (prefix goimapnotify-)) (define-maybe boolean (prefix goimapnotify-)) (define-maybe list-of-strings (prefix goimapnotify-)) (define-maybe string-or-file-like (prefix goimapnotify-)) (define (string-or-file-like? value) (or (string? value) (file-like? value))) (define (goimapnotify-format-field field-name) (object->camel-case-string (string-trim-right (object->string field-name) #\?))) (define (goimapnotify-serialize-field field-name value) "This is converted to JSON later, so we don't return a string here" #~(#$(goimapnotify-format-field field-name) . #$value)) (define goimapnotify-serialize-string goimapnotify-serialize-field) (define goimapnotify-serialize-string-or-file-like goimapnotify-serialize-string) (define goimapnotify-serialize-string goimapnotify-serialize-field) (define goimapnotify-serialize-integer goimapnotify-serialize-field) (define goimapnotify-serialize-boolean goimapnotify-serialize-field) (define (goimapnotify-serialize-list-of-strings field-name value) (goimapnotify-serialize-field field-name (list->array 1 value))) (define-configuration goimapnotify-tls-options (reject-unauthorized? maybe-boolean "Skip verifying CA server identify?") (prefix goimapnotify-)) (define-maybe goimapnotify-tls-options (prefix goimapnotify-)) ; See https://gitlab.com/shackra/goimapnotify/-/blob/423f0e65350f7e042edbd2c373f252c5a0d31dc2/config.go#L46-62 (define-configuration goimapnotify-account (host maybe-string "Address of the IMAP server to connect to.") (host-cmd maybe-string-or-file-like "An executable or script that retrieves your host from somewhere, we cannot pass arguments to this command from stdin.") (port maybe-integer "Port of the IMAP server to connect to.") (tls? maybe-boolean "Use TLS?") (tls-options maybe-goimapnotify-tls-options "Option(s) for the TLS connection. Currently, only one option is supported.") (username maybe-string "Username for authentication.") (username-cmd maybe-string-or-file-like "An executable or script that retrieves your username from somewhere, we cannot pass arguments to this command from stdin.") (password maybe-string "Password for authentication.") (password-cmd maybe-string-or-file-like "An executable or script that retrieves your password from somewhere, we cannot pass arguments to this command from stdin.") (xoauth2? maybe-boolean "You can also use xoauth2 instead of password based authentication by setting the xoauth2 option to true and the output of a tool which can provide xoauth2 encoded tokens in passwordCmd. Examples: @url{https://github.com/google/oauth2l, Google oauth2l} or @url{https://github.com/harishkrupo/oauth2ms, xoauth2 fetcher for O365}.") (on-new-mail maybe-string-or-file-like "An executable or script to run when new mail has arrived.") (on-new-mail-post maybe-string-or-file-like "An executable or script to run after on-new-mail has ran.") (wait maybe-integer "The delay in seconds before the mail syncing is triggered.") (boxes maybe-list-of-strings "Mailboxes to watch.") (prefix goimapnotify-)) (define list-of-goimapnotify-accounts? (list-of (match-lambda (? string?) ($ )))) (define-configuration/no-serialization home-goimapnotify-configuration (accounts (list-of-goimapnotify-accounts '()) "List of accounts that goimapnotify should watch. For each account, a separate configuration file will be generated.")) (define (home-goimapnotify-extension old-config extensions) (match-record old-config (accounts) (home-goimapnotify-configuration (inherit old-config) (accounts (append accounts (append-map home-goimapnotify-configuration-accounts extensions)))))) (define (goimapnotify-files config) (match-record config (accounts) (map (match-lambda ((path account) (list path (computed-file (string-append "mail-imapnotify-config-" (goimapnotify-account-host account)) (with-extensions (list guile-json-4) #~(begin (use-modules (json builder)) (with-output-to-file #$output (lambda () (scm->json '(#$@(list-transduce (base-transducer account) rcons goimapnotify-account-fields)) #:pretty #t))))))))) accounts))) (define home-goimapnotify-service-type (service-type (name 'home-goimapnotify-service) (extensions (list (service-extension home-files-service-type goimapnotify-files))) (compose identity) (extend home-goimapnotify-extension) (default-value (home-goimapnotify-configuration)) (description "Configure goimapnotify to execute scripts on IMAP mailbox changes.")))