initial commit
[ssoma-mda.git] / lib / Ssoma / IMAP.pm
1 # Copyright (C) 2013, Eric Wong <normalperson@yhbt.net> and all contributors
2 # License: AGPLv3 or later (https://www.gnu.org/licenses/agpl-3.0.txt)
3 #
4 # IMAP delivery module, used by Ssoma::Extractor if Email::LocalDelivery
5 # is not available.  Since we are dependent on git, we use the same config
6 # settings as those used by git-imap-send(1)
7 package Ssoma::IMAP;
8 use strict;
9 use warnings;
10 use Ssoma::Git;
11 use Net::IMAP::Simple;
12
13 sub new {
14         my ($class, $git) = @_;
15         my $file = "$git->{git_dir}/config";
16         my $cfg = $git->config_list($file);
17         my %opts = ();
18         my $self = bless { opts => \%opts }, $class;
19         foreach my $k (qw/folder host user pass port tunnel/) {
20                 $self->{$k} = $cfg->{"imap.$k"};
21         }
22
23         check_unsupported($git, $cfg);
24
25         my $imap;
26         if ((my $host = $self->{host})) {
27                 $host =~ s!imap://!!;
28                 $host =~ s!imaps://!! and $opts{use_ssl} = 1;
29                 my $port = $self->{port};
30                 $host .= ":$port" if defined $port;
31                 $self->get_pass($host);
32                 $imap = Net::IMAP::Simple->new($host, %opts) or conn_fail();
33                 $imap->login($self->{user}, $self->{pass}) or
34                                 die "Login failed: " . $imap->errstr . "\n";
35         } elsif ((my $tunnel = $self->{tunnel})) {
36                 # XXX not tested
37                 $host = "cmd:$tunnel";
38                 $imap = Net::IMAP::Simple->new($host, %opts) or conn_fail();
39         } else {
40                 die "neither imap.host nor imap.tunnel set in $file\n";
41         }
42         $self->{imap} = $imap;
43         $self;
44 }
45
46 sub imap_deliver {
47         my ($self, $msg) = @_;
48         $self->{imap}->put($self->{folder}, $msg);
49 }
50
51 sub check_unsupported {
52         my ($git, $cfg) = @_;
53
54         if ((my $sslverify = $cfg->{"imap.sslverify"})) {
55                 local $ENV{GIT_CONFIG} = "$git->{git_dir}/config";
56                 $sslverify = `git config --bool imap.sslverify`;
57                 chomp $sslverify;
58                 if ($sslverify eq "false") {
59                         die "imap.sslverify=false not supported\n";
60                 }
61         }
62
63         if (defined $cfg->{"imap.authmethod"}) {
64                 die "imap.authMethod not supported by Net::IMAP::Simple\n";
65         }
66 }
67
68 sub get_pass {
69         my ($self, $host) = @_;
70
71         return if defined $self->{pass};
72         my $pass = "";
73
74         print STDERR "$self->{user}\@$host password:";
75         STDERR->flush;
76         my $readkey;
77         eval {
78                 require Term::ReadKey;
79                 Term::ReadKey::ReadMode('noecho');
80         };
81         if ($@) {
82                 my $cmd = 'stty -echo';
83                 print STDERR "Term::ReadKey not available, using `$cmd'\n";
84                 system($cmd) and die "$cmd failed: $?\n";
85                 $pass = <STDIN>;
86                 $cmd = 'stty echo';
87                 system($cmd) and die "$cmd failed: $?\n";
88                 chomp $pass;
89         } else {
90                 # read the password
91                 while (defined(my $key = Term::ReadKey::ReadKey(0))) {
92                         last if $key =~ /[\012\015]/; # [\r\n]
93                         $pass .= $key;
94                 }
95                 Term::ReadKey::ReadMode('restore');
96         }
97         print STDERR "\n";
98         STDERR->flush;
99
100         $self->{pass} = $pass;
101 }
102
103 sub conn_fail {
104       die "Unable to connect to IMAP: $Net::IMAP::Simple::errstr\n";
105 }
106
107 sub quit {
108         my ($self) = @_;
109         $self->{imap}->quit;
110 }
111
112 1;