From patchwork Sun Nov 26 10:31:03 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nils Landt X-Patchwork-Id: 56826 Return-Path: X-Original-To: patchwork@mira.cbaines.net Delivered-To: patchwork@mira.cbaines.net Received: by mira.cbaines.net (Postfix, from userid 113) id CDD5E27BBE2; Sun, 26 Nov 2023 10:33:27 +0000 (GMT) X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on mira.cbaines.net X-Spam-Level: X-Spam-Status: No, score=-2.7 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,SPF_HELO_PASS,URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.6 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mira.cbaines.net (Postfix) with ESMTPS id DE60E27BBE9 for ; Sun, 26 Nov 2023 10:33:23 +0000 (GMT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r7CRk-0007hj-2S; Sun, 26 Nov 2023 05:33:04 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r7CRc-0007fb-Sh for guix-patches@gnu.org; Sun, 26 Nov 2023 05:32:59 -0500 Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1r7CRc-00051w-Kn for guix-patches@gnu.org; Sun, 26 Nov 2023 05:32:56 -0500 Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1r7CRi-00071E-6r for guix-patches@gnu.org; Sun, 26 Nov 2023 05:33:02 -0500 X-Loop: help-debbugs@gnu.org Subject: [bug#66557] [PATCH] home: services: Add goimapnotify service. References: In-Reply-To: Resent-From: Nils Landt Original-Sender: "Debbugs-submit" Resent-CC: , guix-patches@gnu.org Resent-Date: Sun, 26 Nov 2023 10:33:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 66557 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: patch To: 66557@debbugs.gnu.org Cc: Nils Landt , ( , Andrew Tropin , Ludovic =?utf-8?q?Court=C3=A8s?= X-Debbugs-Original-Xcc: ( , Andrew Tropin , Ludovic =?utf-8?q?Court=C3=A8s?= Received: via spool by 66557-submit@debbugs.gnu.org id=B66557.170099473426910 (code B ref 66557); Sun, 26 Nov 2023 10:33:02 +0000 Received: (at 66557) by debbugs.gnu.org; 26 Nov 2023 10:32:14 +0000 Received: from localhost ([127.0.0.1]:40743 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1r7CQv-0006zx-5A for submit@debbugs.gnu.org; Sun, 26 Nov 2023 05:32:14 -0500 Received: from mout-p-101.mailbox.org ([2001:67c:2050:0:465::101]:36746) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1r7CQr-0006zc-Ns for 66557@debbugs.gnu.org; Sun, 26 Nov 2023 05:32:11 -0500 Received: from smtp102.mailbox.org (smtp102.mailbox.org [10.196.197.102]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-101.mailbox.org (Postfix) with ESMTPS id 4SdQ4p20Y0z9snt; Sun, 26 Nov 2023 11:31:54 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=landt.email; s=MBO0001; t=1700994714; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=qvp3XXcVHfb3ZzxIT+1SwiBtG+XyfneT2jeYzQkOXmM=; b=FwJ0sYkysSHA9dNodhY+z75JPuAQaZHggKI+i5sUr4wydSu7J1aS4L452QkgTJyeF7DOTX MHs+gOH9XfNDeZOh1EpHn+w+XQS+T/2uM3rO1PBFe+PM4mRqmRa7iWBnJqqJ8VCzRChiZl dwOoohJV3pW1aTHGJphKVcH28kQh3l8HuN9nYAF7G1rk6yPktSjIdIobAgQb6RByACPLy0 vT/H0irsBz/A++dBw4PC55XVAA7GKnIoS2NlNytNsEAU/ZBIRVnk0ejcWNE1mpcp0BDtcl c8c6u2QcVxp/Pe9aCXNiqn8q0FDl/f36mx5PqubwrWfsLMBZ+xI8O5Jz1EsTNw== From: Nils Landt Date: Sun, 26 Nov 2023 11:31:03 +0100 Message-ID: MIME-Version: 1.0 X-MBO-RS-ID: 599fd8fb332b41b8c94 X-MBO-RS-META: 4sfnu4kc3i6y9nk47op3b9t5tjzpdnry X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+patchwork=mira.cbaines.net@gnu.org Sender: guix-patches-bounces+patchwork=mira.cbaines.net@gnu.org X-getmail-retrieved-from-mailbox: Patches From: Nils Landt * gnu/home/services/mail.scm: (home-goimapnotify-configuration, home-goimapnotify-service-type, goimapnotify-account, goimapnotify-tls-options): New variables. (goimapnotify-format-field, goimapnotify-serialize-field, goimapnotify-serialize-goimapnotify-tls-options): New procedures. * doc/guix.texi (Mail Home Services): New node. --- doc/guix.texi | 139 ++++++++++++++++++++++ gnu/home/services/mail.scm | 232 ++++++++++++++++++++++++++++++++++++- 2 files changed, 370 insertions(+), 1 deletion(-) base-commit: 513bf164592e2b44e3e556cc5099a19bd6977790 diff --git a/doc/guix.texi b/doc/guix.texi index 1fd2e21608..aed15685e5 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -44996,6 +44996,145 @@ Mail Home Services The @code{(gnu home services mail)} module provides services that help you set up the tools to work with emails in your home environment. +@cindex goimapnotify +@uref{https://gitlab.com/shackra/goimapnotify, goimapnotify} watches your +mailbox(es) and executes a script on (new / deleted / updated) messages. + +Using @code{home-goimapnotify-configuration}, you can generate a config file +for each account you want to watch (file name relative to @code{$HOME}), e.g.: + +@lisp +(simple-service 'mail-imapnotify-config-examples + home-goimapnotify-service-type + (home-goimapnotify-configuration + (accounts (list + `(".config/goimapnotify/private-account.conf" + ,(goimapnotify-account + (host "imap.example.org") + (port 993) + (tls #t) + (username "example") + (password-cmd + (file-append password-store + "/bin/pass my-private-email-account") + (on-new-mail + (file-append mbsync "/bin/mbsync private-account")) + (on-new-mail-post + (file-append mu "/bin/mu index")) + (boxes '("INBOX")))) + `(".config/goimapnotify/work-account.conf" + ,(goimapnotify-account + (host "imap.work.example.org") + (port 993) + (tls #t) + (username "example") + (password "12345") + (on-new-mail + (file-append mbsync "/bin/mbsync work-account")) + (on-new-mail-post + "notify-send 'New mail'") + (boxes '("INBOX" + "On Call"))))))))) +@end lisp + +Note: to utilize the config files, you need to start a separate goimapnotify +process for each one. Continuing the example above: +@code{goimapnotify -conf "$HOME/.config/goimapnotify/private-account.conf"} and +@code{goimapnotify -conf "$HOME/.config/goimapnotify/work-account.conf"}. + +@c %start of fragment +@deftp {Data Type} home-goimapnotify-configuration +Available @code{home-goimapnotify-configuration} fields are: + +@table @asis +@item @code{accounts} (default: @code{()}) (type: list-of-goimapnotify-accounts) +List of accounts that goimapnotify should watch. For each account, a +separate configuration file will be generated. +@end table + +@end deftp +@c %end of fragment + +@c %start of fragment + +@deftp {Data Type} goimapnotify-account +Available @code{goimapnotify-account} fields are: + +@table @asis +@item @code{host} (type: maybe-string) +Address of the IMAP server to connect to. + +@item @code{host-cmd} (type: maybe-string-or-file-like) +An executable or script that retrieves your host from somewhere, we +cannot pass arguments to this command from stdin. + +@item @code{port} (type: maybe-integer) +Port of the IMAP server to connect to. + +@item @code{tls?} (type: maybe-boolean) + +Use TLS? + +@item @code{tls-options} (type: maybe-goimapnotify-tls-options) +Option(s) for the TLS connection. Currently, only one option is +supported. + +@item @code{username} (type: maybe-string) +Username for authentication. + +@item @code{username-cmd} (type: maybe-string-or-file-like) +An executable or script that retrieves your username from +somewhere, we cannot pass arguments to this command from stdin. + +@item @code{password} (type: maybe-string) +Password for authentication. + +@item @code{password-cmd} (type: + maybe-string-or-file-like) +An executable or script that retrieves your password from somewhere, we +cannot pass arguments to this command from stdin. + +@item @code{xoauth2?} +(type: 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: +@uref{https://github.com/google/oauth2l,Google oauth2l} or +@uref{https://github.com/harishkrupo/oauth2ms,xoauth2 fetcher for O36 +5}. + +@item @code{on-new-mail} (type: maybe-string-or-file-like) +An executable or script to run when new mail has arrived. + +@item @code{on-new-mail-post} (type: maybe-string-or-file-like) +An executable or script to run after onNewMail has ran. + +@item @code{wait} (type: maybe-integer) +The delay in seconds before the mail syncing is triggered. + +@item @code{boxes} (type: maybe-list-of-strings) +Mailboxes to watch. + +@end table + +@end deftp + +@c %end of fragment + +@c %start of fragment + +@deftp {Data Type} goimapnotify-tls-options +Available @code{goimapnotify-tls-options} fields are: + +@table @asis +@item @code{reject-unauthorized?} (type: maybe-boolean) +Skip verifying CA server identify? + +@end table + +@end deftp +@c %end of fragment + @cindex msmtp @uref{https://marlam.de/msmtp, MSMTP} is a @acronym{SMTP, Simple Mail Transfer Protocol} client. It sends mail to a predefined SMTP server diff --git a/gnu/home/services/mail.scm b/gnu/home/services/mail.scm index 5445c82c67..b86a787963 100644 --- a/gnu/home/services/mail.scm +++ b/gnu/home/services/mail.scm @@ -18,15 +18,44 @@ (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) - #:export (home-msmtp-configuration + #: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 @@ -220,3 +249,204 @@ (define home-msmtp-service-type (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) +(define-maybe integer) +(define-maybe boolean) +(define-maybe list-of-strings) +(define-maybe string-or-file-like) + +(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-or-file-like field-name value) + (goimapnotify-serialize-string field-name value)) + +(define (goimapnotify-maybe-serialize field-name value serialization-function) + (if (maybe-value-set? value) + (serialization-function field-name value) + "")) + +(define (goimapnotify-serialize-maybe-string-or-file-like field-name value) + (goimapnotify-maybe-serialize field-name value + goimapnotify-serialize-string-or-file-like)) + +(define goimapnotify-serialize-string goimapnotify-serialize-field) +(define (goimapnotify-serialize-maybe-string field-name value) + (goimapnotify-maybe-serialize field-name value goimapnotify-serialize-string)) + +(define (goimapnotify-serialize-maybe-integer field-name value) + (goimapnotify-maybe-serialize field-name value goimapnotify-serialize-integer)) +(define goimapnotify-serialize-integer goimapnotify-serialize-field) + +(define (goimapnotify-serialize-maybe-boolean field-name value) + (goimapnotify-maybe-serialize field-name value goimapnotify-serialize-boolean)) +(define goimapnotify-serialize-boolean goimapnotify-serialize-field) + +(define (goimapnotify-serialize-maybe-list-of-strings field-name value) + (goimapnotify-maybe-serialize field-name value goimapnotify-serialize-list-of-strings)) +(define (goimapnotify-serialize-list-of-strings field-name value) + (goimapnotify-serialize-field field-name (list->array 1 value))) + +(define (goimapnotify-serialize-maybe-goimapnotify-tls-options field-name config) + (goimapnotify-maybe-serialize field-name config + goimapnotify-serialize-goimapnotify-tls-options)) + +(define (goimapnotify-serialize-goimapnotify-tls-options field-name config) + (goimapnotify-serialize-field + field-name + (prepare-configuration-for-json config goimapnotify-tls-options-fields))) + +(define (prepare-configuration-for-json config fields) + "Convert the configuration to the format expected by guile-json. + Unset maybe-values do not appear in the configuration file." + (fold + (lambda (field accu) + (let ((value ((configuration-field-getter field) config))) + (append accu + (if (maybe-value-set? value) + (list ((configuration-field-serializer field) + (configuration-field-name field) + value)) + '())))) + '() + fields)) + +(define-configuration goimapnotify-tls-options + (reject-unauthorized? + maybe-boolean + "Skip verifying CA server identify?") + (prefix goimapnotify-)) + +(define-maybe goimapnotify-tls-options) + +; 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? lst) + "List is in the form of '((file-name file-like))" + (every (compose goimapnotify-account? second) lst)) + +(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) + (define* (account->json account-config-and-path) + (match + account-config-and-path + ((path account-config) + (let ((prepared-config + (prepare-configuration-for-json + account-config + goimapnotify-account-fields))) + `((,path + ,(computed-file + (string-append + "mail-imapnotify-config-" + (goimapnotify-account-host account-config)) + (with-extensions (list guile-json-4) + #~(begin + (use-modules (json builder)) + + (with-output-to-file #$output + (lambda () + (scm->json '(#$@prepared-config) + #:pretty #t)))))))))))) + + (match-record config + (accounts) + (append-map + (cut account->json <>) + 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.")))