File Coverage

blib/lib/Mail/QmailRemote.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Mail::QmailRemote;
2              
3 1     1   656 use strict;
  1         1  
  1         32  
4 1     1   5 use vars qw($VERSION);
  1         1  
  1         48  
5              
6 1     1   1447 use Net::DNS;
  0            
  0            
7             use IPC::Open3;
8              
9             $VERSION = '0.02';
10              
11             sub new {
12             my $class = shift;
13             my $bin = shift || '/var/qmail/bin/qmail-remote';
14             my $self = bless {
15             bin => $bin,
16             rcpt_map => undef,
17             },$class;
18             $self;
19             }
20              
21             sub mail {
22             my $self = shift;
23             if (@_) {
24             $self->{mail} = shift;
25             }
26             $self->{mail};
27             }
28              
29             *sender = \&mail;
30              
31             sub recipient {
32             my $self = shift;
33             if (@_) {
34             if ($self->{recipient}) {
35             push(@{$self->{recipient}},@_);
36             } else {
37             $self->{recipient} = [ @_ ];
38             }
39             }
40             $self->{recipient};
41             }
42              
43             *to = \&recipient;
44              
45             sub data {
46             my $self = shift;
47             if ($_[0]) {
48             $self->{data} = shift;
49             }
50             $self->{data} =~ s/\r\n/\n/g;
51             $self->{data} .= "\n";
52             $self->{data};
53             }
54              
55             sub send {
56             my $self = shift;
57             $self->_rcpt_map;
58             $self->_do_send;
59             delete $self->{data};
60             delete $self->{mail};
61             delete $self->{rcpt_map};
62             }
63              
64             sub errstr {
65             my $self = shift;
66             $self->{error};
67             }
68              
69             sub _do_send {
70             my $self = shift;
71             foreach my $host(keys %{$self->{rcpt_map}}) {
72             my $mailhosts = $self->_find_MX($host);
73             unless ($mailhosts) {
74             $mailhosts = $self->_find_A($host);
75             }
76             foreach my $mailhost(@$mailhosts) {
77             my $res = $self->_qmail_remote($mailhost,$self->{mail},@{$self->{rcpt_map}->{$host}});
78             last if $res;
79             }
80             }
81             }
82              
83             sub _qmail_remote {
84             my $self = shift;
85             my ($host,$from,@to) = @_;
86            
87             my $w = IO::Handle->new;
88             my $r = IO::Handle->new;
89             my $e = IO::Handle->new;
90              
91             open3($w,$r,$e,
92             $self->{bin},$host,$from,@to);
93             $w->print($self->{data});
94             $w->close;
95            
96             my $res = $r->getline;
97             $r->close;
98             $e->close;
99             if ($res =~ /^r/) {
100             $self->{error} = undef;
101             }
102             else {
103             warn "$res\n";
104             $self->{error} = $res;
105             }
106             return ($self->{error} ? 1 : undef);
107             }
108              
109              
110             sub _rcpt_map{
111             my $self = shift;
112             foreach my $rcpt(@{$self->{recipient}}) {
113             my($name,$host) = split(/\@/,$rcpt);
114             push(@{$self->{rcpt_map}->{$host}},$rcpt);
115             }
116             return $self->{rcpt_map};
117             }
118              
119             sub _find_MX {
120             my $self = shift;
121             my $host = shift;
122             my $res = Net::DNS::Resolver->new;
123             my @mx = mx($res,$host);
124             unless (@mx) {
125             warn "not found MX of $host.\n";
126             return undef;
127             }
128             # order by preference.
129             return [map{$_->[0]}
130             sort {$a->[1] <=> $b->[1]}
131             map {[$_->exchange,$_->preference]} @mx];
132             }
133              
134             sub _find_A {
135             my $self = shift;
136             my $host = shift;
137             my $res = Net::DNS::Resolver->new;
138             my $query = $res->query($host,"A");
139             unless ($query) {
140             return undef;
141             }
142             my @a_records;
143             foreach my $ans($query->answer) {
144             push(@a_records,$ans->name);
145             }
146             return \@a_records;
147             }
148              
149             1;
150             __END__