line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
########################################### |
2
|
|
|
|
|
|
|
package Gaim::Log::Mailer; |
3
|
|
|
|
|
|
|
########################################### |
4
|
|
|
|
|
|
|
|
5
|
1
|
|
|
1
|
|
59640
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
34
|
|
6
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
37
|
|
7
|
1
|
|
|
1
|
|
920
|
use Gaim::Log::Parser 0.10; |
|
1
|
|
|
|
|
243763
|
|
|
1
|
|
|
|
|
31
|
|
8
|
1
|
|
|
1
|
|
867
|
use Gaim::Log::Finder; |
|
1
|
|
|
|
|
1018
|
|
|
1
|
|
|
|
|
25
|
|
9
|
1
|
|
|
1
|
|
5
|
use Log::Log4perl qw(:easy); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
10
|
1
|
|
|
1
|
|
1304
|
use URI::Find; |
|
1
|
|
|
|
|
8921
|
|
|
1
|
|
|
|
|
75
|
|
11
|
1
|
|
|
1
|
|
874
|
use Data::Throttler; |
|
1
|
|
|
|
|
9953
|
|
|
1
|
|
|
|
|
34
|
|
12
|
1
|
|
|
1
|
|
908
|
use Mail::DWIM qw(mail); |
|
1
|
|
|
|
|
15032
|
|
|
1
|
|
|
|
|
71
|
|
13
|
1
|
|
|
1
|
|
700
|
use Text::TermExtract; |
|
1
|
|
|
|
|
10063
|
|
|
1
|
|
|
|
|
39
|
|
14
|
1
|
|
|
1
|
|
9
|
use YAML qw(LoadFile); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
54
|
|
15
|
1
|
|
|
1
|
|
7
|
use URI; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
1301
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
our $VERSION = "0.02"; |
18
|
|
|
|
|
|
|
our %SEEN; |
19
|
|
|
|
|
|
|
my $name = "gaimlogmailer"; |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
########################################### |
22
|
|
|
|
|
|
|
sub new { |
23
|
|
|
|
|
|
|
########################################### |
24
|
1
|
|
|
1
|
0
|
1100
|
my($class, %options) = @_; |
25
|
|
|
|
|
|
|
|
26
|
1
|
|
|
|
|
45
|
my($home) = glob "~"; |
27
|
|
|
|
|
|
|
|
28
|
1
|
|
|
|
|
16
|
my $self = { |
29
|
|
|
|
|
|
|
config_file => "$home/.$name.yml", |
30
|
|
|
|
|
|
|
conf => { |
31
|
|
|
|
|
|
|
min_age => 3600, |
32
|
|
|
|
|
|
|
throttle_interval => 3600, |
33
|
|
|
|
|
|
|
throttle_max_emails => 10, |
34
|
|
|
|
|
|
|
logfile => undef, |
35
|
|
|
|
|
|
|
email_to => undef, |
36
|
|
|
|
|
|
|
languages => ['en'], |
37
|
|
|
|
|
|
|
exclude_words => [], |
38
|
|
|
|
|
|
|
}, |
39
|
|
|
|
|
|
|
%options, |
40
|
|
|
|
|
|
|
}; |
41
|
|
|
|
|
|
|
|
42
|
1
|
|
|
|
|
5
|
$self->{base_dir} = "$home/.$name"; |
43
|
1
|
50
|
|
|
|
20
|
if(! -d $self->{base_dir}) { |
44
|
0
|
0
|
|
|
|
0
|
mkdir $self->{base_dir} or |
45
|
|
|
|
|
|
|
LOGDIE "Cannot create $self->{base_dir} ($!)"; |
46
|
|
|
|
|
|
|
} |
47
|
|
|
|
|
|
|
|
48
|
1
|
50
|
|
|
|
20
|
if(-f $self->{config_file}) { |
49
|
1
|
|
|
|
|
8
|
my $conf = LoadFile( $self->{config_file} ); |
50
|
1
|
|
|
|
|
19392
|
foreach my $key (keys %$conf) { |
51
|
2
|
50
|
|
|
|
14
|
if(!exists $self->{conf}->{$key}) { |
52
|
0
|
|
|
|
|
0
|
LOGDIE "Unknown configuration parameter '$key' ", |
53
|
|
|
|
|
|
|
"in $self->{config_file}"; |
54
|
|
|
|
|
|
|
} |
55
|
2
|
|
|
|
|
10
|
$self->{conf}->{$key} = $conf->{$key}; |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
|
58
|
1
|
50
|
|
|
|
6
|
if($conf->{exclude_words}) { |
59
|
0
|
|
|
|
|
0
|
$self->{conf}->{exclude_words} = |
60
|
|
|
|
|
|
|
[split ' ', $conf->{exclude_words}]; |
61
|
|
|
|
|
|
|
} |
62
|
|
|
|
|
|
|
|
63
|
1
|
50
|
|
|
|
6
|
if($conf->{languages}) { |
64
|
0
|
|
|
|
|
0
|
$self->{conf}->{languages} = |
65
|
|
|
|
|
|
|
[split ' ', $conf->{languages}]; |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
} else { |
68
|
0
|
|
|
|
|
0
|
LOGDIE "Cannot open conf file $self->{config_file} ($!)"; |
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
|
71
|
0
|
|
|
|
|
0
|
$self->{conf}->{exclude_hash} = { map { $_ => 1 } |
|
1
|
|
|
|
|
9
|
|
72
|
1
|
|
|
|
|
3
|
@{ $self->{conf}->{exclude_words} } }; |
73
|
|
|
|
|
|
|
|
74
|
1
|
50
|
|
|
|
6
|
if(!defined $self->{conf}->{email_to}) { |
75
|
0
|
|
|
|
|
0
|
LOGDIE "Mandatory parameter email_to missing in configuration."; |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
|
78
|
1
|
|
|
|
|
20
|
$self->{throttler} = Data::Throttler->new( |
79
|
|
|
|
|
|
|
db_file => "$self->{base_dir}/throttle", |
80
|
|
|
|
|
|
|
interval => $self->{conf}->{throttle_interval}, |
81
|
|
|
|
|
|
|
max_items => $self->{conf}->{throttle_max_emails}, |
82
|
|
|
|
|
|
|
); |
83
|
|
|
|
|
|
|
|
84
|
1
|
50
|
|
|
|
54449
|
dbmopen %SEEN, "$self->{base_dir}/seen", 0644 or |
85
|
|
|
|
|
|
|
LOGDIE "Cannot open dbm file $self->{base_dir}/seen ($!)"; |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
$SIG{TERM} = sub { |
88
|
0
|
|
|
0
|
|
0
|
INFO "Exiting"; |
89
|
0
|
|
|
|
|
0
|
dbmclose %SEEN; |
90
|
0
|
|
|
|
|
0
|
exit 0; |
91
|
1
|
|
|
|
|
3599
|
}; |
92
|
|
|
|
|
|
|
|
93
|
1
|
|
|
|
|
9
|
bless $self, $class; |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
########################################### |
97
|
|
|
|
|
|
|
sub process { |
98
|
|
|
|
|
|
|
########################################### |
99
|
0
|
|
|
0
|
0
|
|
my($self) = @_; |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
my $finder = Gaim::Log::Finder->new( |
102
|
|
|
|
|
|
|
callback => sub { |
103
|
0
|
|
|
0
|
|
|
my($self, $file, $protocol, $from, $to) = @_; |
104
|
|
|
|
|
|
|
|
105
|
0
|
0
|
|
|
|
|
return 1 if $from eq $to; |
106
|
|
|
|
|
|
|
|
107
|
0
|
|
|
|
|
|
my $mtime = (stat $file)[9]; |
108
|
0
|
|
|
|
|
|
my $age = time() - $mtime; |
109
|
|
|
|
|
|
|
|
110
|
0
|
0
|
0
|
|
|
|
return 1 if $SEEN{$file} and |
111
|
|
|
|
|
|
|
$SEEN{$file} == $mtime; |
112
|
|
|
|
|
|
|
|
113
|
0
|
0
|
|
|
|
|
if($age < $self->{mailer}->{conf}->{min_age}) { |
114
|
0
|
|
|
|
|
|
INFO "$file: Too recent ($age)"; |
115
|
0
|
|
|
|
|
|
return 1; |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
0
|
|
|
|
|
|
INFO "Processing log file: $file"; |
119
|
0
|
|
|
|
|
|
my($subject, $formatted, $epoch) = $self->{mailer}->examine($file); |
120
|
|
|
|
|
|
|
|
121
|
0
|
|
|
|
|
|
DEBUG "subject: $subject"; |
122
|
0
|
|
|
|
|
|
DEBUG "formatted: $formatted"; |
123
|
0
|
|
|
|
|
|
DEBUG "epoch: $epoch"; |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
|
126
|
0
|
0
|
|
|
|
|
if(! $self->{mailer}->email_send($epoch, $to, $subject, $formatted)) { |
127
|
0
|
|
|
|
|
|
DEBUG "Email couldn't be sent. Exiting"; |
128
|
0
|
|
|
|
|
|
exit 0; |
129
|
|
|
|
|
|
|
} |
130
|
0
|
|
|
|
|
|
$SEEN{$file} = $mtime; |
131
|
0
|
|
|
|
|
|
}); |
132
|
|
|
|
|
|
|
|
133
|
0
|
|
|
|
|
|
$finder->{mailer} = $self; |
134
|
0
|
|
|
|
|
|
$finder->find(); |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
########################################### |
138
|
|
|
|
|
|
|
sub examine { |
139
|
|
|
|
|
|
|
########################################### |
140
|
0
|
|
|
0
|
0
|
|
my($self, $file) = @_; |
141
|
|
|
|
|
|
|
|
142
|
0
|
|
|
|
|
|
my $extr = Text::TermExtract->new( |
143
|
|
|
|
|
|
|
languages => $self->{conf}->{languages} ); |
144
|
|
|
|
|
|
|
|
145
|
0
|
|
|
|
|
|
$extr->exclude( $self->{conf}->{exclude_words} ); |
146
|
|
|
|
|
|
|
|
147
|
0
|
|
|
|
|
|
my $parser = Gaim::Log::Parser->new( |
148
|
|
|
|
|
|
|
file => $file, |
149
|
|
|
|
|
|
|
); |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
# Search+delete URL processor |
152
|
0
|
|
|
|
|
|
my @hosts = (); |
153
|
0
|
|
|
0
|
|
|
my $urifind = URI::Find->new(sub {push @hosts, $_[0]->host(); |
154
|
0
|
|
|
|
|
|
return "";}); |
|
0
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
|
156
|
0
|
|
|
|
|
|
my $content = ""; |
157
|
0
|
|
|
|
|
|
my $urifound = 0; |
158
|
|
|
|
|
|
|
|
159
|
0
|
|
|
|
|
|
while(my $m = $parser->next_message()) { |
160
|
0
|
|
|
|
|
|
$content .= " " . $m->content(); |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
|
163
|
0
|
|
|
|
|
|
$urifound = $urifind->find(\$content); |
164
|
0
|
0
|
|
|
|
|
$content = " " unless length $content; |
165
|
|
|
|
|
|
|
|
166
|
0
|
|
|
|
|
|
my @words = $extr->terms_extract( $content, {max => 20} ); |
167
|
|
|
|
|
|
|
|
168
|
0
|
|
|
|
|
|
my $char = ""; |
169
|
0
|
0
|
|
|
|
|
my $subj = ($urifound ? "*L* $hosts[0] " : ""); |
170
|
|
|
|
|
|
|
|
171
|
0
|
|
0
|
|
|
|
while(@words and |
172
|
|
|
|
|
|
|
length($subj) + length($char . $words[0]) <= 70) { |
173
|
0
|
|
|
|
|
|
$subj .= $char . shift @words; |
174
|
0
|
|
|
|
|
|
$char = ", "; |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
|
177
|
0
|
|
|
|
|
|
return($subj, $parser->as_string(), $parser->{dt}->epoch()); |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
########################################### |
181
|
|
|
|
|
|
|
sub email_send { |
182
|
|
|
|
|
|
|
########################################### |
183
|
0
|
|
|
0
|
0
|
|
my($self, $epoch, $from, $subject, $text) = @_; |
184
|
|
|
|
|
|
|
|
185
|
0
|
0
|
|
|
|
|
if(!$self->{throttler}->try_push()) { |
186
|
0
|
|
|
|
|
|
ERROR "Email throttled."; |
187
|
0
|
|
|
|
|
|
return undef; |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
0
|
0
|
|
|
|
|
if($self->{fake_email}) { |
191
|
0
|
|
|
|
|
|
print <
|
192
|
|
|
|
|
|
|
========================================================================== |
193
|
|
|
|
|
|
|
From: $from |
194
|
|
|
|
|
|
|
To: $self->{conf}->{email_to} |
195
|
|
|
|
|
|
|
Subject: [gaim $from] $subject |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
$text |
198
|
|
|
|
|
|
|
========================================================================== |
199
|
|
|
|
|
|
|
EOT |
200
|
0
|
|
|
|
|
|
return 1; |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
|
203
|
0
|
|
|
|
|
|
INFO "Sending email '$subject'"; |
204
|
|
|
|
|
|
|
|
205
|
0
|
|
|
|
|
|
mail( |
206
|
|
|
|
|
|
|
from => "$from\@gaim", |
207
|
|
|
|
|
|
|
to => $self->{conf}->{email_to}, |
208
|
|
|
|
|
|
|
subject => "[gaim] " . $subject, |
209
|
|
|
|
|
|
|
text => "From: $from\n" . |
210
|
|
|
|
|
|
|
"Date: " . |
211
|
|
|
|
|
|
|
(scalar localtime $epoch) . |
212
|
|
|
|
|
|
|
"\n\n$text", |
213
|
|
|
|
|
|
|
); |
214
|
|
|
|
|
|
|
|
215
|
0
|
|
|
|
|
|
return 1; |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
1; |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
__END__ |