File Coverage

blib/lib/Postfix/Parse/Mailq.pm
Criterion Covered Total %
statement 38 41 92.6
branch 12 18 66.6
condition 7 12 58.3
subroutine 5 5 100.0
pod 2 2 100.0
total 64 78 82.0


line stmt bran cond sub pod time code
1 1     1   21258 use strict;
  1         2  
  1         25  
2 1     1   5 use warnings;
  1         2  
  1         60  
3             package Postfix::Parse::Mailq;
4             # ABSTRACT: parse the output of the postfix mailq command
5             $Postfix::Parse::Mailq::VERSION = '1.005';
6 1     1   809 use Mixin::Linewise::Readers -readers;
  1         28491  
  1         9  
7              
8             #pod =head1 SYNOPSIS
9             #pod
10             #pod use Postfix::Parse::Mailq;
11             #pod
12             #pod my $mailq_output = `mailq`;
13             #pod my $entries = Postfix::Parse::Mailq->read_string($mailq_output);
14             #pod
15             #pod my $bytes = 0;
16             #pod for my $entry (@$entries) {
17             #pod next unless grep { /\@aol.com$/ } @{ $entry->{remaining_rcpts} };
18             #pod $bytes += $entry->{size};
19             #pod }
20             #pod
21             #pod print "$bytes bytes remain to send to AOL destinations\n";
22             #pod
23             #pod =head1 WARNING
24             #pod
25             #pod This code is really rough and the interface will change. Entries will be
26             #pod objects. There will be some more methods. Still, the basics are likely to
27             #pod keep working, or keep pretty close to what you see here now.
28             #pod
29             #pod =method read_file
30             #pod
31             #pod =method read_handle
32             #pod
33             #pod =method read_string
34             #pod
35             #pod my $entries = Postfix::Parse::Mailq->read_string($string, \%arg);
36             #pod
37             #pod This methods read the output of postfix's F from a file (by name), a
38             #pod filehandle, or a string, respectively. They return an arrayref of hashrefs,
39             #pod each hashref representing one entry in the queue as reported by F.
40             #pod
41             #pod Valid arguments are:
42             #pod
43             #pod spool - a hashref of { queue_id -> spool_name } pairs
44             #pod if given, this will be used to attempt to indicate in which
45             #pod spool messages currently are; it is not entirely reliable (race!)
46             #pod
47             #pod =cut
48              
49             sub read_handle {
50 2     2 1 5425 my ($self, $handle, $arg) = @_;
51 2   100     14 $arg ||= {};
52 2   100     11 $arg->{spool} ||= {};
53              
54 2         85 my $first = $handle->getline;
55              
56 2         99 chomp $first;
57 2 50       11 return [] if $first eq 'Mail queue is empty';
58              
59 2 50       19 Carp::confess("first line did not appear to be first line of mailq output")
60             unless $first =~ m{\A-+Queue ID-+};
61              
62 2         4 my @current;
63             my @entries;
64 2         58 LINE: while (my $line = $handle->getline) {
65 38 100       1220 if ($line eq "\n") {
66 10         31 my $entry = $self->parse_block(\@current);
67 10 50       86 $entry->{spool} = $arg->{spool}{ $entry->{queue_id} } if $arg->{spool};
68 10         19 push @entries, $entry;
69 10         19 @current = ();
70 10         275 next LINE;
71             }
72              
73 28         701 push @current, $line;
74             }
75              
76 2 50 33     89 if (@current and $current[0] !~ /^-- \d+ .?bytes/i) {
77 0         0 my $entry = $self->parse_block(\@current);
78 0 0       0 $entry->{spool} = $arg->{spool}{ $entry->{queue_id} } if $arg->{spool};
79 0         0 push @entries, $entry;
80             }
81              
82 2         27 return \@entries;
83             }
84              
85             #pod =method parse_block
86             #pod
87             #pod my $entry = Mailq->parse_block(\@lines);
88             #pod
89             #pod Given all the lines in a single entry's block of lines in mailq output, this
90             #pod returns data about the entry.
91             #pod
92             #pod =cut
93              
94             my %STATUS_FOR = (
95             '!' => 'held',
96             '*' => 'active',
97             );
98              
99             sub parse_block {
100 10     10 1 17 my ($self, $block) = @_;
101              
102 10         23 chomp @$block;
103 10         18 my $first = shift @$block;
104 10 100 33     148 my $error = defined $block->[0] && ($block->[0] =~ /\A\S/ || $block->[0] =~ /\A\s+\(/)
105             ? (shift @$block)
106             : undef;
107 10 100       32 $error =~ s/\A\s+// if defined $error;
108 10         24 my @dest = map { s/^\s+//; $_; } @$block;
  14         73  
  14         51  
109              
110 10         83 my ($qid, $status_chr, $size, $date, $sender) = $first =~ m/
111             \A
112             ([A-F0-9]+|[0-9B-Zb-z]+)
113             ([*!])?
114             \s+
115             (\d+)
116             \s+
117             (.{19})
118             \s+
119             (\S.+)
120             \z
121             /x;
122              
123 10 100 50     46 my $status = $status_chr ? ($STATUS_FOR{$status_chr} || 'unknown') : 'queued';
124              
125             return {
126 10         77 queue_id => $qid,
127             status => $status,
128             size => $size,
129             date => $date,
130             sender => $sender,
131             error_string => $error,
132             remaining_rcpts => \@dest,
133             }
134             }
135              
136             1;
137              
138             __END__