File Coverage

blib/lib/Net/LMTP.pm
Criterion Covered Total %
statement 18 171 10.5
branch 0 82 0.0
condition 0 25 0.0
subroutine 6 42 14.2
pod 17 18 94.4
total 41 338 12.1


line stmt bran cond sub pod time code
1             # Net::LMTP.pm
2             #
3             # Copyright (c) 2001 Les Howard . This module
4             # is directly derived from the Net::SMTP module. All rights reserved.
5             # This program is free software; you can redistribute it and/or
6             # modify it under the same terms as Perl itself.
7              
8             package Net::LMTP;
9              
10             require 5.001;
11              
12 1     1   729 use strict;
  1         2  
  1         42  
13 1     1   6 use vars qw($VERSION @ISA);
  1         1  
  1         74  
14 1     1   1040 use Socket 1.3;
  1         4649  
  1         493  
15 1     1   10 use Carp;
  1         1  
  1         46  
16 1     1   1095 use IO::Socket;
  1         24063  
  1         6  
17 1     1   2821 use Net::Cmd;
  1         5481  
  1         2437  
18              
19             $VERSION = "0.02"; # $Id$
20              
21             @ISA = qw(Net::Cmd IO::Socket::INET);
22              
23             sub new
24             {
25 0     0 1   my $self = shift;
26 0   0       my $type = ref($self) || $self;
27 0           my $host = shift;
28 0           my $port = shift;
29 0           my %arg = @_;
30 0           my $obj;
31              
32 0 0         if(!defined $host){
33 0           warn "Net::LMTP:new - no host specified\n";
34 0           return undef;
35             }
36 0 0         if(!defined $port){
37 0           warn "Net::LMTP:new - no port specified\n";
38 0           return undef;
39             }
40              
41 0 0         $obj = $type->SUPER::new(PeerAddr => ($host = $host),
42             PeerPort => $port,
43             Proto => 'tcp',
44             Timeout => defined $arg{Timeout}
45             ? $arg{Timeout}
46             : 120
47             );
48 0           $obj->autoflush(1);
49              
50 0 0         $obj->debug(exists $arg{Debug} ? $arg{Debug} : undef);
51              
52 0 0         unless ($obj->response() == CMD_OK)
53             {
54 0           $obj->close();
55 0           return undef;
56             }
57              
58 0           ${*$obj}{'net_lmtp_host'} = $host;
  0            
59              
60 0           (${*$obj}{'net_lmtp_banner'}) = $obj->message;
  0            
61 0           (${*$obj}{'net_lmtp_domain'}) = $obj->message =~ /\A\s*(\S+)/;
  0            
62              
63 0 0 0       unless($obj->hello($arg{Hello} || ""))
64             {
65 0           $obj->close();
66 0           return undef;
67             }
68              
69 0           $obj;
70             }
71              
72             ##
73             ## User interface methods
74             ##
75              
76             sub banner
77             {
78 0     0 1   my $me = shift;
79              
80 0   0       return ${*$me}{'net_lmtp_banner'} || undef;
81             }
82              
83             sub domain
84             {
85 0     0 1   my $me = shift;
86              
87 0   0       return ${*$me}{'net_lmtp_domain'} || undef;
88             }
89              
90             sub etrn {
91 0     0 1   my $self = shift;
92 0 0         defined($self->supports('ETRN',500,["Command unknown: 'ETRN'"])) &&
93             $self->_ETRN(@_);
94             }
95              
96             sub hello
97             {
98 0     0 1   my $me = shift;
99             my $domain = shift ||
100 0   0       eval {
101             require Net::Domain;
102             Net::Domain::hostfqdn();
103             } ||
104             "";
105 0           my $ok = $me->_LHLO($domain);
106 0           my @msg = $me->message;
107              
108 0 0         if($ok)
109             {
110 0           my $h = ${*$me}{'net_lmtp_lmtp'} = {};
  0            
111 0           my $ln;
112 0           foreach $ln (@msg) {
113 0 0         $h->{$1} = $2
114             if $ln =~ /(\S+)\b[ \t]*([^\n]*)/;
115             }
116             }
117              
118 0 0 0       $ok && $msg[0] =~ /\A(\S+)/
119             ? $1
120             : undef;
121             }
122              
123             sub supports {
124 0     0 0   my $self = shift;
125 0           my $cmd = uc shift;
126 0           return ${*$self}{'net_lmtp_lmtp'}->{$cmd}
  0            
127 0 0         if exists ${*$self}{'net_lmtp_lmtp'}->{$cmd};
128 0 0         $self->set_status(@_)
129             if @_;
130 0           return;
131             }
132              
133             sub _addr
134             {
135 0   0 0     my $addr = shift || "";
136              
137 0 0         return $1
138             if $addr =~ /(<[^>]+>)/so;
139              
140 0           $addr =~ s/\n/ /sog;
141 0           $addr =~ s/(\A\s+|\s+\Z)//sog;
142              
143 0           return "<" . $addr . ">";
144             }
145              
146              
147             sub mail
148             {
149 0     0 1   my $me = shift;
150 0           my $addr = _addr(shift);
151 0           my $opts = "";
152              
153 0 0         if(@_)
154             {
155 0           my %opt = @_;
156 0           my($k,$v);
157              
158 0 0         if(exists ${*$me}{'net_lmtp_lmtp'})
  0            
159             {
160 0           my $lmtp = ${*$me}{'net_lmtp_lmtp'};
  0            
161              
162 0 0         if(defined($v = delete $opt{Size}))
163             {
164 0 0         if(exists $lmtp->{SIZE})
165             {
166 0           $opts .= sprintf " SIZE=%d", $v + 0
167             }
168             else
169             {
170 0           carp 'Net::LMTP::mail: SIZE option not supported by host';
171             }
172             }
173              
174 0 0         if(defined($v = delete $opt{Return}))
175             {
176 0 0         if(exists $lmtp->{DSN})
177             {
178 0           $opts .= " RET=" . uc $v
179             }
180             else
181             {
182 0           carp 'Net:::LMTP::mail: DSN option not supported by host';
183             }
184             }
185              
186 0 0         if(defined($v = delete $opt{Bits}))
187             {
188 0 0         if(exists $lmtp->{'8BITMIME'})
189             {
190 0 0         $opts .= $v == 8 ? " BODY=8BITMIME" : " BODY=7BIT"
191             }
192             else
193             {
194 0           carp 'Net::LMTP::mail: 8BITMIME option not supported by host';
195             }
196             }
197              
198 0 0         if(defined($v = delete $opt{Transaction}))
199             {
200 0 0         if(exists $lmtp->{CHECKPOINT})
201             {
202 0           $opts .= " TRANSID=" . _addr($v);
203             }
204             else
205             {
206 0           carp 'Net::LMTP::mail: CHECKPOINT option not supported by host';
207             }
208             }
209              
210 0 0         if(defined($v = delete $opt{Envelope}))
211             {
212 0 0         if(exists $lmtp->{DSN})
213             {
214 0           $v =~ s/([^\041-\176]|=|\+)/sprintf "+%02x", ord($1)/sge;
  0            
215 0           $opts .= " ENVID=$v"
216             }
217             else
218             {
219 0           carp 'Net::LMTP::mail: DSN option not supported by host';
220             }
221             }
222              
223 0 0         carp 'Net::LMTP::recipient: unknown option(s) '
224             . join(" ", keys %opt)
225             . ' - ignored'
226             if scalar keys %opt;
227             }
228             else
229             {
230 0           carp 'Net::LMTP::mail: LMTP not supported by host - options discarded :-(';
231             }
232             }
233              
234 0           $me->_MAIL("FROM:".$addr.$opts);
235             }
236              
237 0     0 1   sub send { shift->_SEND("FROM:" . _addr($_[0])) }
238 0     0 1   sub send_or_mail { shift->_SOML("FROM:" . _addr($_[0])) }
239 0     0 1   sub send_and_mail { shift->_SAML("FROM:" . _addr($_[0])) }
240              
241             sub reset
242             {
243 0     0 1   my $me = shift;
244              
245 0           $me->dataend()
246 0 0         if(exists ${*$me}{'net_lmtp_lastch'});
247              
248 0           $me->_RSET();
249             }
250              
251              
252             sub recipient
253             {
254 0     0 1   my $lmtp = shift;
255 0           my $opts = "";
256 0           my $skip_bad = 0;
257              
258 0 0 0       if(@_ && ref($_[-1]))
259             {
260 0           my %opt = %{pop(@_)};
  0            
261 0           my $v;
262              
263 0           $skip_bad = delete $opt{'SkipBad'};
264              
265 0 0         if(exists ${*$lmtp}{'net_lmtp_lmtp'})
  0 0          
266             {
267 0           my $lmtp = ${*$lmtp}{'net_lmtp_lmtp'};
  0            
268              
269 0 0         if(defined($v = delete $opt{Notify}))
270             {
271 0 0         if(exists $lmtp->{DSN})
272             {
273 0           $opts .= " NOTIFY=" . join(",",map { uc $_ } @$v)
  0            
274             }
275             else
276             {
277 0           carp 'Net::LMTP::recipient: DSN option not supported by host';
278             }
279             }
280              
281 0 0         carp 'Net::LMTP::recipient: unknown option(s) '
282             . join(" ", keys %opt)
283             . ' - ignored'
284             if scalar keys %opt;
285             }
286             elsif(%opt)
287             {
288 0           carp 'Net::LMTP::recipient: LMTP not supported by host - options discarded :-(';
289             }
290             }
291              
292 0           my @ok;
293             my $addr;
294 0           foreach $addr (@_)
295             {
296 0 0         if($lmtp->_RCPT("TO:" . _addr($addr) . $opts)) {
    0          
297 0 0         push(@ok,$addr) if $skip_bad;
298             }
299             elsif(!$skip_bad) {
300 0           return 0;
301             }
302             }
303              
304 0 0         return $skip_bad ? @ok : 1;
305             }
306              
307 0     0 1   sub to { shift->recipient(@_) }
308              
309             sub data
310             {
311 0     0 1   my $me = shift;
312              
313 0   0       my $ok = $me->_DATA() && $me->datasend(@_);
314              
315 0 0 0       $ok && @_ ? $me->dataend
316             : $ok;
317             }
318              
319             sub expand
320             {
321 0     0 1   my $me = shift;
322              
323 0 0         $me->_EXPN(@_) ? ($me->message)
324             : ();
325             }
326              
327              
328 0     0 1   sub verify { shift->_VRFY(@_) }
329              
330             sub help
331             {
332 0     0 1   my $me = shift;
333              
334 0 0         $me->_HELP(@_) ? scalar $me->message
335             : undef;
336             }
337              
338             sub quit
339             {
340 0     0 1   my $me = shift;
341              
342 0           $me->_QUIT;
343 0           $me->close;
344             }
345              
346             sub DESTROY
347 0     0     {
348             # ignore
349             }
350              
351             ##
352             ## SMTP commands that remain in LMTP
353             ##
354              
355 0     0     sub _MAIL { shift->command("MAIL", @_)->response() == CMD_OK }
356 0     0     sub _RCPT { shift->command("RCPT", @_)->response() == CMD_OK }
357 0     0     sub _SEND { shift->command("SEND", @_)->response() == CMD_OK }
358 0     0     sub _SAML { shift->command("SAML", @_)->response() == CMD_OK }
359 0     0     sub _SOML { shift->command("SOML", @_)->response() == CMD_OK }
360 0     0     sub _VRFY { shift->command("VRFY", @_)->response() == CMD_OK }
361 0     0     sub _EXPN { shift->command("EXPN", @_)->response() == CMD_OK }
362 0     0     sub _HELP { shift->command("HELP", @_)->response() == CMD_OK }
363 0     0     sub _RSET { shift->command("RSET")->response() == CMD_OK }
364 0     0     sub _NOOP { shift->command("NOOP")->response() == CMD_OK }
365 0     0     sub _QUIT { shift->command("QUIT")->response() == CMD_OK }
366 0     0     sub _DATA { shift->command("DATA")->response() == CMD_MORE }
367 0     0     sub _TURN { shift->unsupported(@_); }
368 0     0     sub _ETRN { shift->command("ETRN", @_)->response() == CMD_OK }
369              
370              
371             ##
372             ## RFC2033 commands
373             ##
374 0     0     sub _LHLO { shift->command("LHLO", @_)->response() == CMD_OK }
375 0     0     sub _BDAT { shift->unsupported(@_); }
376              
377             1;
378              
379             __END__