aboutsummaryrefslogtreecommitdiff
path: root/.local
diff options
context:
space:
mode:
authorPatrick Spek <p.spek@tyil.nl>2020-03-09 12:48:12 +0100
committerPatrick Spek <p.spek@tyil.nl>2021-08-14 11:59:32 +0200
commitc8123c155b237e6717a500923376918bb0a4c38a (patch)
tree31d494ba02c8a3b476433a8ea189ce586588c359 /.local
parent06683b22c139a8ebf637256fad1a79734d872964 (diff)
Add Mutt configuration
Diffstat (limited to '.local')
-rwxr-xr-x.local/bin/localmail198
-rwxr-xr-x.local/bin/smtp-capture52
-rw-r--r--.local/lib/perl5/App/Localmail.pm54
3 files changed, 304 insertions, 0 deletions
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 <<EOF
+Usage:
+ ${0##*/} -h
+
+Capture email ment to be send through an SMTP client. This allows you to queue
+them up easily.
+
+Options:
+ -h Show this help text and exit.
+EOF
+}
+
+main "$@"
diff --git a/.local/lib/perl5/App/Localmail.pm b/.local/lib/perl5/App/Localmail.pm
new file mode 100644
index 0000000..db39fae
--- /dev/null
+++ b/.local/lib/perl5/App/Localmail.pm
@@ -0,0 +1,54 @@
+#!/usr/bin/env false
+
+use v5.20;
+use feature qw/say signatures/;
+use strict;
+use utf8;
+use warnings;
+
+use YAML::XS;
+use File::Basename;
+use File::Slurp;
+
+package App::Localmail;
+
+sub base_config () {
+ my $config_dir = "$ENV{HOME}/.config/localmail";
+ my $config = {};
+
+ # Load main configuration
+ $config->{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;