#!/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);