File Coverage

blib/lib/Mail/Qmail/Queue/Receive/Envelope.pm
Criterion Covered Total %
statement 55 64 85.9
branch 17 28 60.7
condition n/a
subroutine 10 10 100.0
pod 4 4 100.0
total 86 106 81.1


line stmt bran cond sub pod time code
1             package Mail::Qmail::Queue::Receive::Envelope;
2             our $VERSION = 0.02;
3             #
4             # Copyright 2006 Scott Gifford
5             #
6             # This library is free software; you can redistribute it and/or
7             # modify it under the same terms as Perl itself.
8              
9 4     4   26501 use warnings;
  4         6  
  4         131  
10 4     4   20 use strict;
  4         8  
  4         151  
11              
12 4     4   21 use constant QQ_ENV_FD => 1;
  4         6  
  4         388  
13              
14             =head1 NAME
15              
16             Mail::Qmail::Queue::Receive::Envelope - Receive envelope information when emulating qmail-queue
17              
18             =head1 SYNOPSIS
19              
20             use Mail::Qmail::Queue::Receive::Envelope;
21              
22             my $qq_env = Mail::Qmail::Queue::Receive::Envelope->new
23             or die "Couldn't get qmail-queue envelope\n"
24              
25             print "Message from: ",$qq_env->from,"\n";
26             foreach ($qq_env->to) {
27             print "Message to: $_\n"
28             }
29              
30             =head1 DESCRIPTION
31              
32             C is designed for use in
33             C emulation. This is a way of modifying the behavior of
34             qmail by replacing its queueing mechanism with your own program, which
35             may modify or reject the message, then call the real C
36             program to queue the message. This is commonly done with Bruce
37             Guenter's QMAILQUEUE patch (L), also included in netqmail (L). This patch lets
38             you override the standard C program by setting the
39             environment variable C. It can also be done by renaming
40             the original C, installing your script in its place, and
41             having your script call the renamed C to inject the
42             message.
43              
44             For a simplified interface, see L. To
45             read the body of the message, see L.
46             To re-inject the message, see L.
47              
48             Note that the specifications for C's interface require
49             that the message be read before the envelope (perhaps with
50             L)
51              
52             If the environment variable C is set, this module
53             will treat it as a space-seperated list, remove its first item, and
54             place that item into the environment variable C; if
55             C is unset or empty, C will be removed
56             from the environment. This allows chaining of qmail-queue processors.
57              
58             The constructor and methods of this class will C if they
59             encounter a serious error. If you would prefer different behavior,
60             use C to catch these and handle them as exceptions.
61              
62             =cut
63              
64 4     4   549 use Mail::Qmail::Queue::Error qw(:errcodes :fail);
  4         8  
  4         1045  
65 4     4   3438 use FileHandle;
  4         64846  
  4         43  
66              
67             =head2 CONSTRUCTOR
68              
69             =over 4
70              
71             =item new ( %options )
72              
73             Creates a new qmail-queue envelope reader, but does not start reading
74             it. This constructor will also modify the C and
75             C environment variables, as described above.
76              
77             Available options are:
78              
79             =over 4
80              
81             =item FileHandle
82              
83             Read the envelope from the specified file handle, instead of the
84             default of file desriptor 1.
85              
86             =back
87              
88             =back
89              
90             =cut
91              
92             sub new
93             {
94 5     5 1 71 my $class = shift;
95 5         17 my %o = @_;
96 5         19 my $self = bless {}, $class;
97            
98 5 50       20 if ($o{FileHandle})
99             {
100 5         32 $self->{_fh} = $o{FileHandle};
101             }
102             else
103             {
104 0 0       0 $self->{_fh} = FileHandle->new_from_fd(QQ_ENV_FD,"r")
105             or tempfail QQ_EXIT_READERR, "Couldn't open FD 1 to read envelope: $!\n";
106             }
107             # Special handling of QMAILQUEUE
108 5 50       17 if ($ENV{QMAILQUEUE_CHAIN})
109             {
110 0         0 my(@qqchain)=split(' ',$ENV{QMAILQUEUE_CHAIN});
111 0         0 $ENV{QMAILQUEUE}=shift(@qqchain);
112 0         0 $ENV{QMAILQUEUE_CHAIN}=join(' ',@qqchain);
113             }
114             else
115             {
116 5         44 delete $ENV{QMAILQUEUE};
117             }
118              
119 5         30 $self;
120             }
121              
122             =head2 METHODS
123              
124             =over 4
125              
126             =item from ( )
127              
128             Returns the sender of the incoming message.
129              
130             =cut
131              
132             sub from
133             {
134 16     16 1 25 my $self = shift;
135              
136 16 100       46 if (!defined($self->{_from}))
137             {
138 4 50       24 my $e = $self->read_envelope_string()
139             or tempfail QQ_EXIT_BADENVELOPE, "Couldn't read envelope string: $!\n";
140 4         14 my($type,$val)=_parse_envelope($e);
141 4 50       12 if ($type ne 'F')
142             {
143 0         0 tempfail QQ_EXIT_BADENVELOPE, "Invalid envelope: No From entry\n";
144             }
145 4         12 $self->{_from} = $val;
146             }
147 16         42 $self->{_from};
148             }
149              
150             =item to ( )
151              
152             Returns the next recipient of the message, or C if there are no
153             more recipients. In a list context, returns all remaining recipients
154             of the message.
155              
156             =cut
157              
158             sub to
159             {
160 14     14 1 22 my $self = shift;
161            
162 14 100       37 if (wantarray)
163             {
164 2         2 my @ret;
165 2         10 while(my $r = $self->to())
166             {
167 4         10 push(@ret,$r);
168             }
169 2         14 return @ret;
170             }
171             # Make sure we've read the sender
172 12         25 $self->from();
173 12         15 my $e;
174 12 100       26 $e = $self->read_envelope_string()
175             or return $e;
176 8 50       23 if ($e eq '')
177             {
178 0         0 return undef;
179             }
180 8         19 my($type,$val)=_parse_envelope($e);
181              
182 8 50       32 if ($type ne 'T')
183             {
184 0         0 tempfail QQ_EXIT_BADENVELOPE, "Invalid envelope: Expected To entry, but got something else!\n";
185             }
186 8         32 return $val;
187             }
188              
189             =item read_envelope_string ( )
190              
191             Reads and returns the next envelope entry. The entry will be a type
192             code followed by the value. If all envelope entries have been read,
193             C will be returned.
194              
195             These strings can be passed to L
196             method|Mail::Qmail::Queue::Send/put_envelope_entry> to send them along
197             to another C filter.
198              
199             Note that this method does not return the empty item at the end of the
200             list; it detects it, verifieds it's at the end of the envelope, and
201             returns C. If an empty envelope entry occurs someplace other
202             than the end of the envelope, or if the envelope ends before reading
203             an empty entry, this method will C.
204              
205             =cut
206              
207             sub read_envelope_string
208             {
209 20     20 1 29 my $self = shift;
210 20         62 local $/ = "\0";
211 20         92 my $fh = $self->{_fh};
212 20         123 my $line = <$fh>;
213 20 50       47 if (!defined($line))
214             {
215 0         0 tempfail QQ_EXIT_BADENVELOPE, "Invalid envelope: EOF appeared before null entry\n";
216             }
217 20         34 chomp($line);
218 20 100       51 if ($line eq '')
219             {
220             # This should be the last entry
221 5         29 $line = <$fh>;
222 5 50       16 if (defined($line))
223             {
224 0         0 tempfail QQ_EXIT_BADENVELOPE, "Invalid envelope: null entry appeared before EOF\n";
225             }
226 5 50       74 close($fh)
227             or tempfail QQ_EXIT_BADENVELOPE,"Error closing envelope filehandle: $!\n";
228             }
229 20         105 $line;
230             }
231              
232             # Helper method
233             sub _parse_envelope
234             {
235 12     12   48 return unpack("aa*",$_[0]);
236             }
237              
238             =back
239              
240             =head1 SEE ALSO
241              
242             L, L,
243             L, L.
244              
245             =head1 COPYRIGHT
246              
247             Copyright 2006 Scott Gifford.
248              
249             This library is free software; you can redistribute it and/or
250             modify it under the same terms as Perl itself.
251              
252             =cut
253              
254             1;