Initial commit: Imported yaala 0.7.3.
[yaala.git] / lib / Yaala / Parser / Postfix.pm
1 package Yaala::Parser;
2
3 use strict;
4 use warnings;
5 use vars qw#%DATAFIELDS#;
6
7 use Exporter;
8 use Yaala::Data::Persistent qw#init#;
9 use Yaala::Parser::WebserverTools qw#%MONTH_NUMBERS#;
10
11 @Yaala::Parser::EXPORT_OK = qw(parse extra %DATAFIELDS);
12 @Yaala::Parser::ISA = ('Exporter');
13
14 our $LASTDATE = init ('$LASTDATE', 'scalar');
15 our $EXTRA = init ('$EXTRA', 'hash');
16 our $MAILS = init ('$MAILS', 'hash');
17
18 if (!$$LASTDATE) { $$LASTDATE = 0; }
19 if (!defined ($EXTRA->{'relay_denied'})) { $EXTRA->{'relay_denied'} = 0; }
20 if (!defined ($EXTRA->{'tls_hosts'}   )) { $EXTRA->{'tls_hosts'}    = {}; }
21
22 %DATAFIELDS =
23 (
24         date            => 'key:date',
25         hour            => 'key:hour',
26
27         sender          => 'key',
28         recipient       => 'key',
29
30         defer_count     => 'key:numeric',
31         delay           => 'key:time',
32
33         incoming_host   => 'key:host',
34         outgoing_host   => 'key:host',
35
36         count           => 'agg',
37         bytes           => 'agg:bytes'
38 );
39
40 # This needs to be done at runtime, since Data uses Setup which relies on
41 # %DATAFIELDS to be defined  -octo
42 require Yaala::Data::Core;
43 import Yaala::Data::Core qw#store#;
44
45 my $VERSION = '$Id: Postfix.pm,v 1.6 2003/12/07 15:42:22 octo Exp $';
46 print STDERR $/, __FILE__, ": $VERSION" if ($::DEBUG);
47
48 return (1);
49
50 sub parse
51 {
52         my $line = shift;
53
54         if ($line =~ m#^(\w{3})\s+(\d+) (\d\d):(\d\d):(\d\d) (\S+) postfix/([^\[]+)[^:]+: ([A-F0-9]+): (.+)$#)
55         {
56                 my ($month, $day, $hour, $minute, $second,
57                         $hostname, $service, $id, $line_end) =
58                 ($1, $2, $3, $4, $5, $6, $7, $8, $9);
59                 my $year = (localtime ())[5] + 1900;
60                 $month = $MONTH_NUMBERS{$month};
61
62                 {
63                         my $tmp = int (sprintf ("%04u%02u%02u%02u%02u%02u",
64                                         $year, $month, $day, $hour, $minute, $second));
65
66                         if ($tmp < $$LASTDATE)
67                         {
68                                 print STDERR $/, __FILE__, ": Skipping.. ($tmp <= $$LASTDATE)" if ($::DEBUG & 0x0200);
69                                 return (undef);
70                         }
71                         else { $$LASTDATE = $tmp; }
72                 }
73         
74                 my $date = sprintf ("%04u-%02u-%02u", $year, $month, $day);
75
76                 if (!defined ($MAILS->{$id}))
77                 {
78                         $MAILS->{$id} =
79                         {
80                                 date            => $date,
81                                 hour            => $hour,
82                                 sender          => '*UNKNOWN*',
83                                 recipient       => '*UNKNOWN*',
84                                 defer_count     => 0,
85                                 delay           => 0,
86                                 incoming_host   => '*UNKNOWN*',
87                                 outgoing_host   => '*UNKNOWN*',
88                                 count           => 1,
89                                 bytes           => 0
90                         };
91                 }
92
93                 $MAILS->{$id}{'date'} = $date;
94                 $MAILS->{$id}{'hour'} = $hour;
95                 
96                 if ($line_end =~ m/^to=<([^>]+)>, relay=([^,]+), delay=(\d+), status=(\w+)/)
97                 {
98                         my ($to, $relay, $delay, $status) = ($1, $2, $3, $4);
99
100                         $MAILS->{$id}{'recipient'} = $to;
101                         if ($MAILS->{$id}{'delay'} < $delay)
102                         {
103                                 $MAILS->{$id}{'delay'} = $delay;
104                         }
105
106                         if ($relay =~ m/^([^\[]+)\[([\d\.]+)\]$/)
107                         {
108                                 my $host = $1;
109                                 my $ip = $2;
110
111                                 if ($host eq 'unknown')
112                                 {
113                                         $MAILS->{$id}{'outgoing_host'} = $ip;
114                                 }
115                                 else
116                                 {
117                                         $MAILS->{$id}{'outgoing_host'} = $host;
118                                 }
119                         }
120                         elsif ($relay eq 'local')
121                         {
122                                 $MAILS->{$id}{'outgoing_host'} = 'localhost';
123                         }
124
125                         if ($status eq 'sent')
126                         {
127                                 store_mail ($id);
128                         }
129                         elsif ($status eq 'deferred')
130                         {
131                                 $MAILS->{$id}{'defer_count'}++;
132                         }
133                         elsif ($status eq 'bounced')
134                         {
135                                 $MAILS->{$id}{'recipient_count'}--;
136                                 if ($MAILS->{$id}{'recipient_count'} < 1)
137                                 {
138                                         delete ($MAILS->{$id});
139                                 }
140                         }
141                         elsif ($::DEBUG)
142                         {
143                                 print STDERR $/, __FILE__, ": Unknown status: $status";
144                         }
145                 }
146                 elsif ($line_end =~ m/^from=<([^>]*)>, size=(\d+), nrcpt=(\d+)/)
147                 {
148                         my ($from, $size, $nrcpt) = ($1, $2, $3, $4);
149
150                         $MAILS->{$id}{'sender'} = $from if ($from);
151                         $MAILS->{$id}{'bytes'} = $size;
152                         
153                         $MAILS->{$id}{'recipient_count'} = $nrcpt;
154                 }
155                 elsif ($line_end =~ m/client=([^ ,]+)/)
156                 {
157                         my $client = $1;
158
159                         if ($client =~ m/^([^\[]+)\[([\d\.]+)\]$/)
160                         {
161                                 my $host = $1;
162                                 my $ip = $2;
163
164                                 if ($host eq 'unknown')
165                                 {
166                                         $MAILS->{$id}{'incoming_host'} = $ip;
167                                 }
168                                 else
169                                 {
170                                         $MAILS->{$id}{'incoming_host'} = $host;
171                                 }
172                         }
173                         elsif ($::DEBUG)
174                         {
175                                 print STDERR $/, __FILE__,
176                                 ": Unable to parse client string: $client";
177                         }
178                 }
179         }
180         elsif ($line =~ m/Relay access denied/i)
181         {
182                 $EXTRA->{'relay_denied'}++;
183         }
184         elsif ($line =~ m/TLS connection established (?:to|from) ([^\[]+)\[([^\]]+)\]/i)
185         {
186                 my $host = $1;
187                 my $ip = $2;
188
189                 my $ident = ($host eq 'unknown' ? $ip : $host);
190
191                 $EXTRA->{'tls_hosts'}{$ident} = 1;
192         }
193         elsif ($::DEBUG and 0)
194         {
195                 chomp ($line);
196                 print STDERR $/, __FILE__, ": Unable to parse line: $line";
197         }
198 }
199
200 sub store_mail
201 {
202         my $id = shift;
203         my $mail = $MAILS->{$id};
204
205         store ($mail);
206         
207         $mail->{'recipient_count'}--;
208         if ($mail->{'recipient_count'} < 1)
209         {
210                 delete ($MAILS->{$id});
211         }
212 }
213
214 sub extra
215 {
216         if ($EXTRA->{'relay_denied'})
217         {
218                 $::EXTRA->{'Relay access denied'} = sprintf ("%u times", $EXTRA->{'relay_denied'});
219         }
220
221         my $tls_hosts = scalar (keys (%{$EXTRA->{'tls_hosts'}}));
222         if ($tls_hosts)
223         {
224                 $::EXTRA->{'TLS connections'} = sprintf ("%u hosts", $tls_hosts);
225         }
226 }