File Coverage

blib/lib/Mail/SRS/Daemon.pm
Criterion Covered Total %
statement 35 84 41.6
branch 5 34 14.7
condition 1 6 16.6
subroutine 9 11 81.8
pod 2 2 100.0
total 52 137 37.9


line stmt bran cond sub pod time code
1             package Mail::SRS::Daemon;
2              
3 1     1   23392 use strict;
  1         2  
  1         35  
4 1     1   4 use warnings;
  1         1  
  1         83  
5 1     1   6 use vars qw(@ISA @EXPORT_OK %EXPORT_TAGS $SRSSOCKET);
  1         2  
  1         89  
6 1     1   4 use Exporter;
  1         1  
  1         55  
7 1     1   1150 use IO::Socket;
  1         26492  
  1         4  
8 1     1   1723 use IO::Select;
  1         1438  
  1         44  
9 1     1   1145 use Getopt::Long;
  1         13891  
  1         6  
10 1     1   699 use Mail::SRS qw(:all);
  1         3  
  1         1341  
11              
12             @ISA = qw(Exporter);
13              
14             @EXPORT_OK = qw($SRSSOCKET);
15             %EXPORT_TAGS = (
16             all => \@EXPORT_OK,
17             );
18              
19             $SRSSOCKET = '/tmp/srsd';
20              
21             sub new {
22 1     1 1 12 my $class = shift;
23 1 50       6 my $args = ($#_ == 0) ? %{ (shift) } : { @_ };
  0         0  
24              
25 0         0 my @secrets = ref($args->{Secret}) eq 'ARRAY'
26 1 50       6 ? @{ $args->{Secret} }
27             : [ $args->{Secret} ];
28              
29 1 50 33     6 if (exists $args->{SecretFile} && defined $args->{SecretFile}) {
30 0         0 my $secretfile = $args->{SecretFile};
31 0 0       0 die "Secret file $secretfile not readable"
32             unless -r $secretfile;
33 0         0 local *FH;
34 0 0       0 open(FH, "<$secretfile")
35             or die "Cannot open $secretfile: $!";
36 0         0 while () {
37 0 0       0 next unless /\S/;
38 0 0       0 next if /^#/;
39 0         0 push(@secrets, $_);
40             }
41 0         0 close(FH);
42             }
43              
44 1 50       3 die "No secret or secretfile given. Use --secret or --secretfile, ".
45             "and ensure the secret file is not empty."
46             unless @secrets;
47              
48             # Preserve the pertinent original arguments, mostly for fun.
49 1         5 my $self = {
50             Secret => $args->{Secret},
51             SecretFile => $args->{SecretFile},
52             };
53 1 50       5 $self->{Socket} = delete $args->{Socket} if exists $args->{Socket};
54              
55             # An alternative pattern would be to inherit this, rather than
56             # delegate to it.
57 1         3 $args->{Secret} = \@secrets;
58             # All other args are passed on verbatim.
59 1         10 my $srs = new Mail::SRS($args);
60              
61 1         5 $self->{Instance} = $srs;
62              
63 1         7 return bless $self, $class;
64             }
65              
66             sub run {
67 0     0 1   my ($self) = @_;
68 0           my $srs = $self->{Instance};
69              
70 0           print STDERR "Starting SRS daemon in PID $$\n";
71              
72             # Until we decide that forward() and reverse() can die, this will
73             # allow us to trap the error messages from those subroutines.
74 0     0     local $SIG{__WARN__} = sub { die @_; };
  0            
75              
76 0           my $listen = $self->{Socket};
77 0 0         unless ($listen) {
78 0 0         unlink($SRSSOCKET) if -e $SRSSOCKET;
79 0   0       $listen ||= new IO::Socket::UNIX(
80             Type => SOCK_STREAM,
81             Local => $SRSSOCKET,
82             Listen => 1,
83             );
84 0 0         die "Unable to create listen socket: $!" unless $listen;
85             }
86              
87 0           my $select = new IO::Select();
88 0           $select->add($listen);
89              
90 0           while (my @socks = $select->can_read) {
91 0           foreach my $sock (@socks) {
92 0 0         if ($sock == $listen) {
93             # print "Accept on $sock\n";
94 0           $select->add($listen->accept());
95             }
96             else {
97 0           my $line = <$sock>;
98 0 0         if (defined($line)) {
99 0           chomp($line);
100             # print "Read '$line' on $sock\n";
101 0           my @args = split(/\s+/, $line);
102 0           my $cmd = uc shift @args;
103 0           eval {
104 0 0         if ($cmd eq 'FORWARD') {
    0          
105 0           $sock->print($srs->forward(@args), "\n");
106             }
107             elsif ($cmd eq 'REVERSE') {
108 0           $sock->print($srs->reverse(@args), "\n");
109             }
110             else {
111 0           die "Invalid command $cmd";
112             }
113             };
114 0 0         if ($@) {
115 0           $sock->print("ERROR: $@");
116 0           $select->remove($sock);
117 0           $sock->close();
118             }
119             }
120              
121             # Exim requires that we unconditionally close the socket
122             # print "Close on $sock\n";
123 0           $select->remove($sock);
124 0           $sock->flush();
125 0           $sock->close();
126 0           undef $sock;
127             }
128             }
129 0           my @exc = $select->has_exception(0);
130 0           foreach my $sock (@exc) {
131             # print "Exception on $sock\n";
132 0           $select->remove($sock);
133 0           $sock->close();
134             }
135             }
136             }
137              
138             __END__