1 #! /usr/bin/perl |
|
2 # © Heiko Schlittermann |
|
3 # $Id$ |
|
4 # $URL$ |
|
5 use constant USAGE => << '#'; |
|
6 !ME! [options] [-- exim native options] |
|
7 |
|
8 --[no]log show the log output [!$Cf->log!] |
|
9 --[no]debug show debug debug output [!$Cf->debug!] |
|
10 |
|
11 --from=s from: [!$Cf->from!] |
|
12 --to=s to: [!$Cf->to!] |
|
13 |
|
14 --Helo=s HELO [!$Cf->Helo!] |
|
15 --From=s MAIL FROM: [!$Cf->From!] |
|
16 --To=s RCPT TO: [!$Cf->To!] |
|
17 |
|
18 --src=s src ip/name [!$Cf->src!] |
|
19 --dst=s dst ip/name [!$Cf->dst!] |
|
20 |
|
21 --exim=s exim binary [!$Cf->exim!] |
|
22 --config=s exim config file [!$Cf->config!] |
|
23 |
|
24 $Id$ |
|
25 $URL$ |
|
26 # |
|
27 |
|
28 |
|
29 use strict; |
|
30 use warnings; |
|
31 use AppConfig; |
|
32 use IPC::Open3; |
|
33 use IO::Select; |
|
34 use Socket; |
|
35 use File::Basename; |
|
36 |
|
37 |
|
38 sub exim_option($); |
|
39 sub read_exim($); |
|
40 sub write_exim($@); |
|
41 sub addr(@); |
|
42 sub hostname() { chomp (my $h = `hostname -f`); return $h; } |
|
43 |
|
44 use constant ME => basename $0; |
|
45 use constant HOSTNAME => hostname; |
|
46 use constant CONFIG => ( |
|
47 { CASE => 1 }, |
|
48 |
|
49 log => { ARGS => "!", DEFAULT => 1 }, |
|
50 debug => { ARGS => "!", DEFAULT => 0 }, |
|
51 |
|
52 from => { ARGS => "=s" }, |
|
53 to => { ARGS => "=s" }, |
|
54 |
|
55 Helo => { ARGS => "=s", ALIAS => "ehlo" }, |
|
56 From => { ARGS => "=s" }, |
|
57 To => { ARGS => "=s" }, |
|
58 |
|
59 src => { ARGS => "=s", DEFAULT => "172.20.1.8" }, |
|
60 dst => { ARGS => "=s" }, # exim primary_hostname |
|
61 |
|
62 exim => { ARGS => "=s", DEFAULT => $ENV{EXIM} || "exim" }, |
|
63 config => { ARGS => "=s", DEFAULT => $ENV{EXIM_CONF} || "/etc/exim/exim.conf.t" , |
|
64 ALIAS => "C" }, |
|
65 |
|
66 help => { ARGS => "!" }, |
|
67 |
|
68 ); |
|
69 |
|
70 |
|
71 |
|
72 my $Cf; |
|
73 $Cf = new AppConfig CONFIG or die; |
|
74 $Cf->dst(addr(exim_option("primary_hostname"))); |
|
75 $Cf->getopt(qw(pass_through no_ignore_case)) or die $@; |
|
76 |
|
77 $Cf->From($Cf->from) unless $Cf->From; |
|
78 $Cf->To($Cf->to) unless $Cf->To; |
|
79 |
|
80 $Cf->Helo((split/@/, $Cf->from||"")[1] || HOSTNAME) |
|
81 unless $Cf->Helo; |
|
82 $Cf->dst(addr(HOSTNAME)) unless $Cf->dst; |
|
83 |
|
84 $@ = ""; |
|
85 foreach (qw/from to src dst/) { |
|
86 $Cf->get($_) or $@ = join " ", $@, $_; |
|
87 } |
|
88 |
|
89 die ME.": Missing values: $@\n" if $@; |
|
90 |
|
91 |
|
92 MAIN: { |
|
93 die "Config file for exim not readable ".$Cf->config.": $!\n" if not -r $Cf->config; |
|
94 |
|
95 my ($w, $r); |
|
96 my @cmd = ($Cf->exim, |
|
97 -C => $Cf->config, |
|
98 -oMi => addr($Cf->dst), |
|
99 -bhc => addr($Cf->src), |
|
100 @ARGV); # remaining args are exim native |
|
101 |
|
102 if ($Cf->help) { |
|
103 ($_ = USAGE) =~ s/!(.*?)!/eval $1||""/egs; |
|
104 print; exit; |
|
105 } |
|
106 |
|
107 my $s = new IO::Select; |
|
108 |
|
109 print "**> @cmd\n"; |
|
110 |
|
111 open3($w, $r, undef, @cmd) or die "Can't run @cmd: $!\n"; |
|
112 |
|
113 read_exim $r; |
|
114 write_exim $w, "EHLO ".$Cf->Helo."\n"; |
|
115 read_exim $r; |
|
116 write_exim $w, "MAIL FROM: ".$Cf->From."\n"; |
|
117 read_exim $r; |
|
118 write_exim $w, "RCPT TO: ".$Cf->to."\n"; |
|
119 read_exim $r; |
|
120 write_exim $w, "DATA\n"; |
|
121 read_exim $r; |
|
122 write_exim $w, "From: ".$Cf->from."\n"; |
|
123 write_exim $w, "To: ".$Cf->to."\n"; |
|
124 write_exim $w, "Subject: Test\n"; |
|
125 |
|
126 if (not -t STDIN) { |
|
127 write_exim $w, "\n"; |
|
128 while (<>) { |
|
129 write_exim $w, $_; |
|
130 } |
|
131 } |
|
132 |
|
133 write_exim $w, "\n.\n"; |
|
134 read_exim $r; |
|
135 write_exim $w, "QUIT\n"; |
|
136 |
|
137 |
|
138 } |
|
139 |
|
140 sub read_exim($) { |
|
141 my $fh = shift; |
|
142 while (<$fh>) { |
|
143 /^\d\d\d/ and print("< $_") and next; |
|
144 /^LOG/ and print and next if $Cf->log; |
|
145 print and next if $Cf->debug; |
|
146 } continue { |
|
147 last if /^\d\d\d\s/; |
|
148 } |
|
149 exit if /^5/; |
|
150 } |
|
151 |
|
152 sub write_exim($@) { |
|
153 my $fh = shift; |
|
154 print "> ", @_; |
|
155 print $fh @_; |
|
156 } |
|
157 |
|
158 |
|
159 { |
|
160 my %opts; |
|
161 sub exim_option($) { |
|
162 my $opt = shift; |
|
163 my $exim = $Cf->exim; |
|
164 if (!%opts) { |
|
165 %opts = map { chomp; /^(.*?)\s*=\s*(.*)/ ? ($1, $2) : (/no_(.*)/ ? ($1, 0) : ($_, 1)) } grep !/^\s*$/, `$exim -bP`; |
|
166 } |
|
167 $opts{$opt} |
|
168 } } |
|
169 |
|
170 |
|
171 sub addr(@) { |
|
172 map { inet_ntoa scalar gethostbyname $_ } @_; |
|
173 } |
|
174 |
|
175 # vim:sts=4 sw=4 aw ai sm: |
|