From c8123c155b237e6717a500923376918bb0a4c38a Mon Sep 17 00:00:00 2001 From: Patrick Spek Date: Mon, 9 Mar 2020 12:48:12 +0100 Subject: Add Mutt configuration --- .config/localmail/accounts/tyil@freedom.nl.yaml | 18 ++ .../accounts/tyil@national.shitposting.agency.yaml | 18 ++ .../accounts/tyil@scriptkitties.church.yaml | 18 ++ .config/localmail/config.yaml | 1 + .config/neomutt/neomuttrc | 90 ++++++++++ .local/bin/localmail | 198 +++++++++++++++++++++ .local/bin/smtp-capture | 52 ++++++ .local/lib/perl5/App/Localmail.pm | 54 ++++++ 8 files changed, 449 insertions(+) create mode 100644 .config/localmail/accounts/tyil@freedom.nl.yaml create mode 100644 .config/localmail/accounts/tyil@national.shitposting.agency.yaml create mode 100644 .config/localmail/accounts/tyil@scriptkitties.church.yaml create mode 100644 .config/localmail/config.yaml create mode 100644 .config/neomutt/neomuttrc create mode 100755 .local/bin/localmail create mode 100755 .local/bin/smtp-capture create mode 100644 .local/lib/perl5/App/Localmail.pm diff --git a/.config/localmail/accounts/tyil@freedom.nl.yaml b/.config/localmail/accounts/tyil@freedom.nl.yaml new file mode 100644 index 0000000..f51f49f --- /dev/null +++ b/.config/localmail/accounts/tyil@freedom.nl.yaml @@ -0,0 +1,18 @@ +incoming: + protocol: imap + server: imap.freedom.nl + ssl: true + username: tyil@freedom.nl + password: + source: pass + file: /home/tyil/.password-store/email/freedom.nl/tyil.gpg +outgoing: + protocol: smtp + server: smtp.freedom.nl + ssl: true + username: tyil@freedom.nl + password: + source: pass + file: /home/tyil/.password-store/email/freedom.nl/tyil.gpg + handles: + - tyil@freedom.nl diff --git a/.config/localmail/accounts/tyil@national.shitposting.agency.yaml b/.config/localmail/accounts/tyil@national.shitposting.agency.yaml new file mode 100644 index 0000000..959ea70 --- /dev/null +++ b/.config/localmail/accounts/tyil@national.shitposting.agency.yaml @@ -0,0 +1,18 @@ +incoming: + protocol: imap + server: mail.cock.li + ssl: true + username: tyil@national.shitposting.agency + password: + source: pass + file: /home/tyil/.password-store/email/national.shitposting.agency/tyil.gpg +outgoing: + protocol: smtp + server: mail.cock.li + ssl: true + username: tyil@national.shitposting.agency + password: + source: pass + file: /home/tyil/.password-store/email/national.shitposting.agency/tyil.gpg + handles: + - tyil@national.shitposting.agency diff --git a/.config/localmail/accounts/tyil@scriptkitties.church.yaml b/.config/localmail/accounts/tyil@scriptkitties.church.yaml new file mode 100644 index 0000000..c455413 --- /dev/null +++ b/.config/localmail/accounts/tyil@scriptkitties.church.yaml @@ -0,0 +1,18 @@ +incoming: + protocol: imap + server: tyil.email + ssl: true + username: tyil@scriptkitties.church + password: + source: pass + file: /home/tyil/.password-store/email/scriptkitties.church/tyil.gpg +outgoing: + protocol: smtp + server: tyil.email + ssl: true + username: tyil@scriptkitties.church + password: + source: pass + file: /home/tyil/.password-store/email/scriptkitties.church/tyil.gpg + handles: + - tyil@scriptkitties.church diff --git a/.config/localmail/config.yaml b/.config/localmail/config.yaml new file mode 100644 index 0000000..3fef4af --- /dev/null +++ b/.config/localmail/config.yaml @@ -0,0 +1 @@ +maildir: /home/tyil/mail diff --git a/.config/neomutt/neomuttrc b/.config/neomutt/neomuttrc new file mode 100644 index 0000000..76f48b6 --- /dev/null +++ b/.config/neomutt/neomuttrc @@ -0,0 +1,90 @@ +# Unbind keys +bind index,pager a noop +bind index,pager d noop +bind index,pager t noop +bind index,pager \\ noop + +# "Global" keys +bind attach,index,pager \CD next-page +bind attach,index,pager \CU previous-page +bind attach,index g first-entry +bind attach,index G last-entry + +macro index,pager t "" +macro index,pager \\ "" + +# Sidebar +set sidebar_visible=yes +set sidebar_width=10 + +bind index J sidebar-next +bind index K sidebar-prev +bind index sidebar-open + +# Email addresses in use +alternates -group personal p.spek@tyil.nl +alternates -group personal p.spek@tyil.work +alternates -group personal tyil@freedom.nl + +# Inbox +set mbox_type=Maildir +set folder="~/mail" +set spoolfile=+/ +set sort=threads +set index_format="%-60.60s %zc %-15.15L %g" + +set virtual_spoolfile=yes +virtual-mailboxes \ + "Inbox" "notmuch://?query=not tag:archive and not tag:trash"\ + "Archive" "notmuch://?query=tag:archive"\ + "Sent" "notmuch://?query=tag:sent"\ + "Trash" "notmuch://?query=tag:trash" + +macro index a "+archive -unread -inbox" +macro index d "+trash -unread -inbox" + +# Pager +macro pager a "+archive -unread -inbox" +macro pager d "+trash -unread -inbox" + +bind pager G bottom +bind pager g top +bind pager j next-line +bind pager k previous-line + +# Composing +set edit_headers=yes +set signature="~/documents/email-signature.txt" +set send_charset="utf-8" + +set from="p.spek@tyil.nl" +set reverse_name=yes + +set crypt_autosign=yes +set pgp_sign_as="0x031D65902E840821" +set pgp_self_encrypt=yes +set crypt_opportunistic_encrypt = yes + +# Sending mail +set sendmail="/home/tyil/.local/bin/smtp-capture" +set copy=no +set record="" + +# PGP +set pgp_decode_command="gpg --status-fd=2 %?p?--passphrase-fd 0? --no-verbose --quiet --batch --output - %f" +set pgp_verify_command="gpg --status-fd=2 --no-verbose --quiet --batch --output - --verify %s %f" +set pgp_decrypt_command="gpg --status-fd=2 %?p?--passphrase-fd 0? --no-verbose --quiet --batch --output - %f" +set pgp_sign_command="gpg --no-verbose --batch --quiet --output - %?p?--passphrase-fd 0? --armor --detach-sign --textmode %?a?-u %a? %f" +set pgp_clearsign_command="gpg --no-verbose --batch --quiet --output - %?p?--passphrase-fd 0? --armor --textmode --clearsign %?a?-u %a? %f" +set pgp_encrypt_only_command="pgpewrap gpg --batch --quiet --no-verbose --output - --encrypt --textmode --armor --always-trust -- -r %r -- %f" +set pgp_encrypt_sign_command="pgpewrap gpg %?p?--passphrase-fd 0? --batch --quiet --no-verbose --textmode --output - --encrypt --sign %?a?-u %a? --armor --always-trust -- -r %r -- %f" +set pgp_import_command="gpg --no-verbose --import %f" +set pgp_export_command="gpg --no-verbose --export --armor %r" +set pgp_verify_key_command="gpg --verbose --batch --fingerprint --check-sigs %r" +set pgp_list_pubring_command="gpg --no-verbose --batch --quiet --with-colons --with-fingerprint --with-fingerprint --list-keys %r" +set pgp_list_secring_command="gpg --no-verbose --batch --quiet --with-colons --with-fingerprint --with-fingerprint --list-secret-keys %r" +set pgp_good_sign="^\\[GNUPG:\\] GOODSIG" +set pgp_check_gpg_decrypt_status_fd + +# "Theme" +color index default default '.*' diff --git a/.local/bin/localmail b/.local/bin/localmail new file mode 100755 index 0000000..dc1092a --- /dev/null +++ b/.local/bin/localmail @@ -0,0 +1,198 @@ +#!/usr/bin/env perl + +use v5.16; +use feature qw/say signatures/; +use strict; +use utf8; +use warnings; + +use Data::UUID; +use Email::Simple; +use File::Copy; +use File::Slurp; +use Net::IMAP::Simple; +use Net::SMTP; +use File::Path; +use File::Basename; + +use Data::Dumper; + +use App::Localmail; + +sub main (@args) { + my $action = shift @args; + + return 1 unless defined $action; + + my %actions = ( + fetch => \&fetch, + send => \&send, + ); + + return 2 unless exists $actions{$action}; + + return $actions{$action}(App::Localmail::base_config(), @args); +} + +sub fetch ($config, @args) { + # Get all passwords + App::Localmail::config_passwords($config); + + # Fetch email for each account + my $uuid = Data::UUID->new; + my $maildir = $config->{localmail}{maildir}; + + for my $account (keys %{$config->{accounts}}) { + my $current = $config->{accounts}{$account}{incoming}; + + if (!defined $current->{password}{plain}) { + say "No password for '$account'"; + next; + } + + say "Fetching mail for $account"; + say " Connecting to $current->{server}"; + + my $connection = Net::IMAP::Simple->new( + $current->{server}, + use_ssl => $current->{ssl} // 1, + ); + + if (!$connection) { + say " Failed: ". $Net::IMAP::Simple::errstr."\n"; + next; + } + + if ($current->{starttls}) { + say ' Applying StartTLS'; + $connection->starttls; + } + + say " Authenticating"; + if (!$connection->login($current->{username}, $current->{password}{plain})) { + say ' Failed: '.$connection->errstr."\n"; + next; + } + + say " Fetching mailboxes"; + for my $mbox ($connection->mailboxes) { + $connection->select($mbox); + + my @messages = keys(%{$connection->list}); + my $count = 0; + + say " $mbox (".($#messages + 1).")"; + + for my $message (@messages) { + $count++; + my $current_uuid = fc($uuid->to_string($uuid->create)); + + say " $current_uuid ($count)"; + + open(FH, '>', "$maildir/tmp/$current_uuid.eml") or die $!; + print FH $connection->fetch($message); + close(FH); + + move("$maildir/tmp/$current_uuid.eml", "$maildir/new/$current_uuid.eml"); + $connection->delete($message); + } + + $connection->close; + } + + say " Closing connection"; + $connection->quit; + } + + return 0; +} + +sub send ($config, @args) { + # Get all passwords + App::Localmail::config_passwords($config); + + my $uuid = Data::UUID->new; + my $maildir = $config->{localmail}{maildir}; + + # Create a hash of sender addresses and related accounts + my %senders; + + for my $account (keys %{$config->{accounts}}) { + for my $handler (@{$config->{accounts}{$account}{outgoing}{handles}}) { + $senders{$handler} = $account; + } + } + + # Walk through the queue of emails to send + for my $item (glob("$maildir/queue/*")) { + next unless -d $item; + next unless -f "$item/message.eml"; + + my ($id, $path) = File::Basename::fileparse($item); + + say "Sending $id"; + + my $body = File::Slurp::read_file("$item/message.eml"); + my $email = Email::Simple->new($body); + my $from = $email->header("From"); + my @recipients = File::Slurp::read_file("$item/recipients"); + + if (!exists $senders{$from}) { + say " No account handles mail from '$from'"; + next; + } + + my $current = $config->{accounts}{$senders{$from}}{outgoing}; + + say " Connecting to ".$current->{server}; + + # Send the email + my $connection = Net::SMTP->new( + $current->{server}, + SSL => $current->{ssl}, + Timeout => $current->{timeout} // 5, + ); + + say " Authenticating"; + + if (!$connection->auth($current->{username}, $current->{password}{plain})) { + say " Failed: ".$connection->message; + next; + } + + say " MAIL FROM"; + if (!$connection->mail($from)) { + say " Failed: ".$connection->message; + next; + } + + say " RCPT TO"; + if (!$connection->recipient(@recipients)) { + say " Failed: ".$connection->message; + next; + } + + say " DATA"; + if (!$connection->data($body)) { + say " Failed: ".$connection->message; + next; + } + + $connection->quit; + + say " Sent!"; + + if (open(FH, '>', "$maildir/sent/$id.eml")) { + say " Moving to sentdir"; + + print FH $email->as_string; + close(FH); + + File::Path::rmtree($item); + } + } + + return 0; +} + +exit main(@ARGV); diff --git a/.local/bin/smtp-capture b/.local/bin/smtp-capture new file mode 100755 index 0000000..bd31cb3 --- /dev/null +++ b/.local/bin/smtp-capture @@ -0,0 +1,52 @@ +#!/bin/sh + +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Affero 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 Affero General Public License for more +# details. + +main() +{ + # Handle opts + while getopts ":h" opt + do + case "$opt" in + h) usage && exit 0 ;; + *) + printf "Invalid option passed: %s\n" "$OPTARG" >&2 + ;; + esac + done + + shift $(( OPTIND - 1 )) + + queuedir="$HOME/mail/queue/$(uuidgen)" + mkdir -p -- "$queuedir" + cat > "$queuedir/message.eml" + + for i in "$@" + do + printf "$i\n" >> "$queuedir/recipients" + done +} + +usage() +{ + cat <{localmail} = YAML::XS::Load(File::Slurp::read_file("$config_dir/config.yaml")); + + # Load account configurations + for (glob("$config_dir/accounts/*.yaml")) { + my ($name, $path, $suffix) = File::Basename::fileparse($_, ('.yaml')); + my $yaml = File::Slurp::read_file($_); + + $config->{accounts}->{$name} = YAML::XS::Load($yaml); + } + + $config->{localmail}{maildir} //= "$ENV{HOME}/.maildir"; + + $config; +} + +sub config_passwords ($config) { + # Get passwords where necessary + for (keys %{$config->{accounts}}) { + if (!defined($config->{accounts}{$_}{incoming}{password}{plain})) { + my $file = $config->{accounts}{$_}{incoming}{password}{file}; + chomp (my $password = qx/gpg -d "$file" | head -n 1/); + + $config->{accounts}{$_}{incoming}{password}{plain} = $password; + } + + if (!defined($config->{accounts}{$_}{outgoing}{password}{plain})) { + my $file = $config->{accounts}{$_}{outgoing}{password}{file}; + chomp (my $password = qx/gpg -d "$file" | head -n 1/); + + $config->{accounts}{$_}{outgoing}{password}{plain} = $password; + } + } +} + +1; -- cgit v1.1