]> git.proxmox.com Git - pmg-api.git/blame - PMG/Statistic.pm
API /statistics/spam: always return counts for levels 0 to 10
[pmg-api.git] / PMG / Statistic.pm
CommitLineData
5414dee4 1package PMG::Statistic;
e350fb98
DM
2
3use strict;
5414dee4 4use warnings;
e350fb98 5use DBI;
e350fb98
DM
6use Time::Local;
7use Time::Zone;
8
5414dee4
DM
9use PVE::SafeSyslog;
10
9f67f5b3 11use PMG::ClusterConfig;
5414dee4
DM
12use PMG::RuleDB;
13
e350fb98
DM
14sub new {
15 my ($self, $start, $end, $advanced) = @_;
3d511edd 16
e350fb98 17 $self = {};
3d511edd 18
e350fb98
DM
19 bless($self);
20
9b1db2e4
DM
21 if (defined($start) && defined($end)) {
22 $self->timespan($start, $end);
e350fb98 23 } else {
9b1db2e4
DM
24 my $ctime = time();
25 $self->timespan($ctime, $ctime - 24*3600);
e350fb98
DM
26 }
27
28 $self->{adv} = $advanced;
3d511edd 29
e350fb98
DM
30 return $self;
31}
32
33sub clear_stats {
34 my ($dbh) = @_;
35
36 eval {
37 $dbh->begin_work;
3d511edd 38
e350fb98
DM
39 $dbh->do ("LOCK TABLE StatInfo");
40 $dbh->do ("LOCK TABLE ClusterInfo");
41
42 $dbh->do ("DELETE FROM Statinfo");
43 $dbh->do ("DELETE FROM DailyStat");
44 $dbh->do ("DELETE FROM DomainStat");
45 $dbh->do ("DELETE FROM VirusInfo");
46 $dbh->do ("DELETE FROM ClusterInfo WHERE name = 'lastmt_DomainStat'");
47 $dbh->do ("DELETE FROM ClusterInfo WHERE name = 'lastmt_DailyStat'");
48 $dbh->do ("DELETE FROM ClusterInfo WHERE name = 'lastmt_VirusInfo'");
49
50 $dbh->commit;
51 };
52 if ($@) {
53 $dbh->rollback;
54 die $@;
3d511edd 55 }
e350fb98
DM
56}
57
58sub update_stats_generic {
59 my ($dbh, $statinfoid, $select, $update, $insert) = @_;
60
61 my $todo = 0;
62 my $maxentries = 100000;
63
64
65 eval {
66 $dbh->begin_work;
67
68 $dbh->do("LOCK TABLE StatInfo IN EXCLUSIVE MODE");
69
70 my $sth = $dbh->prepare("SELECT last_value FROM cstatistic_id_seq");
71 $sth->execute();
72 my $maxinfo = $sth->fetchrow_hashref();
73 goto COMMIT if !$maxinfo;
74 my $last_value = $maxinfo->{last_value};
75 goto COMMIT if !defined ($last_value);
76
77 $sth = $dbh->prepare("SELECT ivalue as value FROM StatInfo WHERE NAME = '$statinfoid'");
78 $sth->execute();
79 my $statinfo = $sth->fetchrow_hashref();
80
81 my $startid = $statinfo ? $statinfo->{value} : 0;
82 goto COMMIT if $startid > $last_value;
83
84 my $endid = $startid + $maxentries;
85 $endid = $last_value + 1 if $endid > $last_value;
86 $todo = $last_value + 1 - $endid;
87
88 my $timezone = tz_local_offset();;
89
90 $select =~ s/__timezone__/$timezone/g;
91 $select =~ s/__startid__/$startid/g;
92 $select =~ s/__endid__/$endid/g;
93
94 $sth = $dbh->prepare($select);
95 $sth->execute();
96
97 my $cmd = "";
98 #print "TEST:$last_value:$endid:$todo\n";
99
100 while (my $ref = $sth->fetchrow_hashref()) {
101 if ($ref->{exists}) {
102 $cmd .= &$update($ref);
103 } else {
104 $cmd .= &$insert($ref);
105 }
106 }
107
108 $dbh->do ($cmd) if $cmd;
109
110 $sth->finish();
111
112 if ($statinfo) {
113 $dbh->do("UPDATE StatInfo SET ivalue = $endid WHERE NAME = '$statinfoid'");
114 } else {
115 $dbh->do("INSERT INTO StatInfo VALUES ('$statinfoid', $endid)");
116 }
117
3d511edd 118 COMMIT:
e350fb98
DM
119 $dbh->commit;
120 };
121
122 if ($@) {
123 $dbh->rollback;
124 die $@;
125 }
126
127 return $todo;
128}
129
130sub update_stats_dailystat {
131 my ($dbh, $cinfo) = @_;
132
9f67f5b3
DM
133 my $role = $cinfo->{local}->{type} // '-';
134 return 0 if !(($role eq '-') || ($role eq 'master'));
e350fb98
DM
135
136 my $select = "SELECT sub.*, dailystat.time IS NOT NULL as exists FROM " .
137 "(SELECT COUNT (CASE WHEN direction THEN 1 ELSE NULL END) as count_in, " .
138 "COUNT (CASE WHEN NOT direction THEN 1 ELSE NULL END) as count_out, " .
139 "SUM (CASE WHEN direction THEN bytes ELSE NULL END) / (1024.0*1024) as bytes_in, " .
140 "SUM (CASE WHEN NOT direction THEN bytes ELSE NULL END) / (1024.0*1024) as bytes_out, " .
141 "COUNT (CASE WHEN virusinfo IS NOT NULL AND direction THEN 1 ELSE NULL END) AS virus_in, " .
142 "COUNT (CASE WHEN virusinfo IS NOT NULL AND NOT direction THEN 1 ELSE NULL END) AS virus_out, " .
143 "COUNT (CASE WHEN virusinfo IS NULL AND direction AND ptime > 0 AND spamlevel >= 3 THEN 1 ELSE NULL END) as spam_in, " .
144 "COUNT (CASE WHEN virusinfo IS NULL AND NOT direction AND ptime > 0 AND spamlevel >= 3 THEN 1 ELSE NULL END) as spam_out, " .
145 "COUNT (CASE WHEN virusinfo IS NULL AND direction AND sender = '' THEN 1 ELSE NULL END) as bounces_in, " .
146 "COUNT (CASE WHEN virusinfo IS NULL AND NOT direction AND sender = '' THEN 1 ELSE NULL END) as bounces_out, " .
147 "COUNT (CASE WHEN virusinfo IS NULL AND ptime = 0 AND spamlevel = 5 THEN 1 ELSE NULL END) as glcount, " .
148 "COUNT (CASE WHEN virusinfo IS NULL AND ptime = 0 AND spamlevel = 4 THEN 1 ELSE NULL END) as spfcount, " .
149 "sum (cstatistic.ptime) / 1000.0 as ptimesum, " .
150 "((cstatistic.time + __timezone__) / 3600) * 3600 as hour " .
151 "from cstatistic where id >= __startid__ and id < __endid__ group by hour) as sub " .
152 "left join dailystat on (sub.hour = dailystat.time)";
153
154 my $update = sub {
155 my $ref = shift;
156 my @values = ();
157 my $sql = '';
158
159 push @values, "CountIn = CountIn + $ref->{count_in}" if $ref->{count_in};
160 push @values, "CountOut = CountOut + $ref->{count_out}" if $ref->{count_out};
161 push @values, "BytesIn = BytesIn + $ref->{bytes_in}" if $ref->{bytes_in};
162 push @values, "BytesOut = BytesOut + $ref->{bytes_out}" if $ref->{bytes_out};
163 push @values, "VirusIn = VirusIn + $ref->{virus_in}" if $ref->{virus_in};
164 push @values, "VirusOut = VirusOut + $ref->{virus_out}" if $ref->{virus_out};
165 push @values, "SpamIn = SpamIn + $ref->{spam_in}" if $ref->{spam_in};
166 push @values, "SpamOut = SpamOut + $ref->{spam_out}" if $ref->{spam_out};
167 push @values, "BouncesIn = BouncesIn + $ref->{bounces_in}" if $ref->{bounces_in};
168 push @values, "BouncesOut = BouncesOut + $ref->{bounces_out}" if $ref->{bounces_out};
169 push @values, "GreylistCount = GreylistCount + $ref->{glcount}" if $ref->{glcount};
170 push @values, "SPFCount = SPFCount + $ref->{spfcount}" if $ref->{spfcount};
171 push @values, "PTimeSum = PTimeSum + $ref->{ptimesum}" if $ref->{ptimesum};
172 push @values, "MTime = EXTRACT(EPOCH FROM now())";
173
174 if (scalar (@values)) {
175 $sql .= "UPDATE dailystat SET ";
176 $sql .= join (',', @values);
177 $sql .= " WHERE time = $ref->{hour};";
178 }
179 return $sql;
180 };
181
182 my $insert = sub {
183 my $ref = shift;
184
3d511edd 185 my $sql = "INSERT INTO dailystat " .
e350fb98
DM
186 "(Time,CountIn,CountOut,BytesIn,BytesOut,VirusIn,VirusOut,SpamIn,SpamOut," .
187 "BouncesIn,BouncesOut,GreylistCount,SPFCount,RBLCount,PTimeSum,Mtime) " .
3d511edd 188 "VALUES ($ref->{hour}," . ($ref->{count_in} || 0) . ',' . ($ref->{count_out} || 0) . ',' .
e350fb98
DM
189 ($ref->{bytes_in} || 0) . ',' . ($ref->{bytes_out} || 0) . ',' .
190 ($ref->{virus_in} || 0) . ',' . ($ref->{virus_out} || 0) . ',' .
191 ($ref->{spam_in} || 0) . ',' . ($ref->{spam_out} || 0) . ',' .
192 ($ref->{bounces_in} || 0) . ',' . ($ref->{bounces_out} || 0) . ',' .
3d511edd 193 ($ref->{glcount} || 0) . ',' . ($ref->{spfcount} || 0) . ',0,' . ($ref->{ptimesum} || 0) .
e350fb98
DM
194 ",EXTRACT(EPOCH FROM now()));";
195
196 return $sql;
197 };
198
199 return update_stats_generic ($dbh, 'dailystat_index', $select, $update, $insert);
200
201}
202
203sub update_stats_domainstat_in {
204 my ($dbh, $cinfo) = @_;
205
9f67f5b3
DM
206 my $role = $cinfo->{local}->{type} // '-';
207 return 0 if !(($role eq '-') || ($role eq 'master'));
e350fb98
DM
208
209 my $sub1 = "select distinct cstatistic_cid, cstatistic_rid, " .
210 "lower(substring(receiver from position ('\@' in receiver) + 1)) as domain, " .
211 "((cstatistic.time + __timezone__) / 86400) * 86400 as day " .
212 "from CStatistic, CReceivers where cid = cstatistic_cid AND rid = cstatistic_rid AND " .
213 "id >= __startid__ and id < __endid__ AND direction " .
214 "group by cstatistic_cid, cstatistic_rid, day, domain";
3d511edd 215
e350fb98
DM
216
217 my $select = "SELECT sub.*, domainstat.time IS NOT NULL as exists FROM " .
218 "(SELECT day, domain, COUNT (id) as count_in, SUM (bytes) / (1024.0*1024) as bytes_in, " .
219 "COUNT (CASE WHEN virusinfo IS NOT NULL THEN 1 ELSE NULL END) AS virus_in, " .
220 "COUNT (CASE WHEN virusinfo IS NULL AND spamlevel >= 3 THEN 1 ELSE NULL END) as spam_in, " .
221 "COUNT (CASE WHEN virusinfo IS NULL AND sender = '' THEN 1 ELSE NULL END) as bounces_in, " .
222 "sum (cstatistic.ptime) / 1000.0 as ptimesum " .
223 "from cstatistic, ($sub1) as ddb " .
224 "WHERE ddb.cstatistic_cid = cstatistic.cid AND ddb.cstatistic_rid = cstatistic.rid GROUP BY day, domain) as sub " .
225 "left join domainstat on (day = domainstat.time and sub.domain = domainstat.domain)";
226
227 my $update = sub {
228 my $ref = shift;
229 my @values = ();
230 my $sql = '';
231
232 push @values, "CountIn = CountIn + $ref->{count_in}" if $ref->{count_in};
233 push @values, "BytesIn = BytesIn + $ref->{bytes_in}" if $ref->{bytes_in};
234 push @values, "VirusIn = VirusIn + $ref->{virus_in}" if $ref->{virus_in};
235 push @values, "SpamIn = SpamIn + $ref->{spam_in}" if $ref->{spam_in};
236 push @values, "BouncesIn = BouncesIn + $ref->{bounces_in}" if $ref->{bounces_in};
237 push @values, "PTimeSum = PTimeSum + $ref->{ptimesum}" if $ref->{ptimesum};
238 push @values, "MTime = EXTRACT(EPOCH FROM now())";
3d511edd 239
e350fb98
DM
240 if (scalar (@values)) {
241 $sql .= "UPDATE domainstat SET ";
242 $sql .= join (',', @values);
243 $sql .= " WHERE time = $ref->{day} and domain = " . $dbh->quote($ref->{domain}) . ';';
244 }
245 return $sql;
246 };
247
248 my $insert = sub {
249 my $ref = shift;
250
3d511edd
DM
251 my $sql .= "INSERT INTO domainstat values ($ref->{day}, " . $dbh->quote($ref->{domain}) . ',' .
252 ($ref->{count_in} || 0) . ',0,' .
e350fb98
DM
253 ($ref->{bytes_in} || 0) . ',0,' .
254 ($ref->{virus_in} || 0) . ',0,' .
255 ($ref->{spam_in} || 0) . ',0,' .
3d511edd 256 ($ref->{bounces_in} || 0) . ',0,' .
e350fb98
DM
257 ($ref->{ptimesum} || 0) .
258 ",EXTRACT(EPOCH FROM now()));";
3d511edd 259
e350fb98
DM
260 return $sql;
261 };
262
263 update_stats_generic ($dbh, 'domainstat_in_index', $select, $update, $insert);
264
265}
266
267sub update_stats_domainstat_out {
268 my ($dbh, $cinfo) = @_;
269
9f67f5b3
DM
270 my $role = $cinfo->{local}->{type} // '-';
271 return 0 if !(($role eq '-') || ($role eq 'master'));
e350fb98
DM
272
273 my $select = "SELECT sub.*, domainstat.time IS NOT NULL as exists FROM " .
274 "(SELECT COUNT (ID) as count_out, SUM (bytes) / (1024.0*1024) as bytes_out, " .
275 "COUNT (CASE WHEN virusinfo IS NOT NULL THEN 1 ELSE NULL END) AS virus_out, " .
276 "COUNT (CASE WHEN virusinfo IS NULL AND spamlevel >= 3 THEN 1 ELSE NULL END) as spam_out, " .
277 "COUNT (CASE WHEN virusinfo IS NULL AND sender = '' THEN 1 ELSE NULL END) as bounces_out, " .
278 "sum (cstatistic.ptime) / 1000.0 as ptimesum, " .
279 "((cstatistic.time + __timezone__) / 86400) * 86400 as day, " .
280 "lower(substring(sender from position ('\@' in sender) + 1)) as domain " .
281 "from cstatistic where id >= __startid__ and id < __endid__ and not direction " .
282 "group by day, domain) as sub " .
283 "left join domainstat on (day = domainstat.time and sub.domain = domainstat.domain)";
284
285 my $update = sub {
286 my $ref = shift;
287 my @values = ();
288 my $sql = '';
289
290 push @values, "CountOut = CountOut + $ref->{count_out}" if $ref->{count_out};
291 push @values, "BytesOut = BytesOut + $ref->{bytes_out}" if $ref->{bytes_out};
292 push @values, "VirusOut = VirusOut + $ref->{virus_out}" if $ref->{virus_out};
293 push @values, "SpamOut = SpamOut + $ref->{spam_out}" if $ref->{spam_out};
294 push @values, "BouncesOut = BouncesOut + $ref->{bounces_out}" if $ref->{bounces_out};
295 push @values, "PTimeSum = PTimeSum + $ref->{ptimesum}" if $ref->{ptimesum};
296 push @values, "MTime = EXTRACT(EPOCH FROM now())";
3d511edd 297
e350fb98
DM
298 if (scalar (@values)) {
299 $sql .= "UPDATE domainstat SET ";
300 $sql .= join (',', @values);
301 $sql .= " WHERE time = $ref->{day} and domain = " . $dbh->quote($ref->{domain}) . ';';
302 }
303 return $sql;
304 };
305
306 my $insert = sub {
307 my $ref = shift;
308
309 my $sql .= "INSERT INTO domainstat values ($ref->{day}, " . $dbh->quote($ref->{domain}) .
3d511edd 310 ',0,' . ($ref->{count_out} || 0) .
e350fb98
DM
311 ',0,' . ($ref->{bytes_out} || 0) .
312 ',0,' . ($ref->{virus_out} || 0) .
3d511edd
DM
313 ',0,' . ($ref->{spam_out} || 0) .
314 ',0,' . ($ref->{bounces_out} || 0) .
315 ','. ($ref->{ptimesum} || 0) .
e350fb98
DM
316 ",EXTRACT(EPOCH FROM now()));";
317
318 return $sql;
319 };
320
321 update_stats_generic ($dbh, 'domainstat_out_index', $select, $update, $insert);
322
323}
324
325sub update_stats_virusinfo {
326 my ($dbh, $cinfo) = @_;
327
9f67f5b3
DM
328 my $role = $cinfo->{local}->{type} // '-';
329 return 0 if !(($role eq '-') || ($role eq 'master'));
e350fb98
DM
330
331 my $select = "SELECT sub.*, virusinfo.time IS NOT NULL as exists FROM " .
332 "(SELECT ((cstatistic.time + __timezone__) / 86400) * 86400 as day, " .
333 "count (virusinfo) as count, virusinfo AS name " .
334 "FROM cstatistic WHERE id >= __startid__ AND id < __endid__ AND virusinfo IS NOT NULL " .
335 "group by day, name) as sub " .
336 "left join VirusInfo on (day = virusinfo.time and sub.name = virusinfo.name)";
337
338 my $update = sub {
339 my $ref = shift;
340 my @values = ();
341 my $sql = '';
342
343 push @values, "Count = Count + $ref->{count}" if $ref->{count};
344 push @values, "MTime = EXTRACT(EPOCH FROM now())";
3d511edd 345
e350fb98
DM
346 if (scalar (@values)) {
347 $sql .= "UPDATE VirusInfo SET ";
348 $sql .= join (',', @values);
349 $sql .= " WHERE time = $ref->{day} and Name = " . $dbh->quote($ref->{name}) . ';';
350 }
351 return $sql;
352 };
353
354 my $insert = sub {
355 my $ref = shift;
356
357 my $sql .= "INSERT INTO VirusInfo values ($ref->{day}, " . $dbh->quote($ref->{name}) .
3d511edd 358 ',' . ($ref->{count} || 0) .
e350fb98
DM
359 ",EXTRACT(EPOCH FROM now()));";
360
361 return $sql;
362 };
363
364 update_stats_generic ($dbh, 'virusinfo_index', $select, $update, $insert);
365
366}
367
368
369sub update_stats {
370 my ($dbh, $cinfo) = @_;
371
372 while (update_stats_dailystat ($dbh, $cinfo) > 0) {};
373 while (update_stats_domainstat_in ($dbh, $cinfo) > 0) {};
374 while (update_stats_domainstat_out ($dbh, $cinfo) > 0) {};
375 while (update_stats_virusinfo ($dbh, $cinfo) > 0) {};
376}
377
378sub total_mail_stat {
379 my ($self, $rdb) = @_;
380
381 my ($from, $to) = $self->localdayspan();
3d511edd 382
e350fb98
DM
383 my ($sth, $ref);
384 my $glcount = 0;
385
386# this is to slow for high volume sites
387# $sth = $rdb->{dbh}->prepare("SELECT COUNT(DISTINCT Instance) AS GL FROM CGreylist " .
388# "WHERE passed = 0 AND rctime >= ? AND rctime < ? ");
3d511edd 389# $sth->execute($from, $to);
e350fb98
DM
390# $ref = $sth->fetchrow_hashref();
391# $glcount = $ref->{gl};
392
393 my $cmds = "SELECT sum(CountIn) + $glcount AS count_in, sum(CountOut) AS count_out, " .
3d511edd
DM
394 "sum (VirusIn) AS viruscount_in, sum (VirusOut) AS viruscount_out, " .
395 "sum (SpamIn) AS spamcount_in, sum (SpamOut) AS spamcount_out, " .
396 "sum (BytesIn) AS traffic_in, sum (BytesOut) AS traffic_out, " .
397 "sum (BouncesIn) AS bounces_in, sum (BouncesOut) AS bounces_out, " .
e350fb98
DM
398 "sum (GreylistCount) + $glcount as glcount, " .
399 "sum (SPFCount) as spfcount, " .
400 "sum (RBLCount) as rblcount, " .
401 "sum(PTimeSum)/(sum(CountIn) + $glcount + sum(CountOut)) AS avptime " .
402 "FROM DailyStat where time >= $from and time < $to";
403
404 $sth = $rdb->{dbh}->prepare($cmds);
3d511edd 405 $sth->execute();
e350fb98
DM
406 $ref = $sth->fetchrow_hashref();
407 $sth->finish();
408
409 if (!$ref->{avptime}) {
3d511edd
DM
410 $ref->{count_in} = $ref->{count_out} = $ref->{viruscount_in} = $ref->{viruscount_out} =
411 $ref->{spamcount_in} = $ref->{spamcount_out} = $ref->{glcount} = $ref->{spfcount} =
412 $ref->{rblcount} = $ref->{bounces_in} = $ref->{bounces_out} = $ref->{traffic_in} =
e350fb98
DM
413 $ref->{traffic_out} = $ref->{avptime} = 0;
414 }
415
416 $ref->{count} = $ref->{count_in} + $ref->{count_out};
417
418 $ref->{count_in_per} = $ref->{count} > 0 ? ($ref->{count_in} * 100)/$ref->{count} : 0;
419 $ref->{count_out_per} = 100 - $ref->{count_in_per};
420
421 $ref->{viruscount_in_per} = $ref->{count_in} > 0 ? ($ref->{viruscount_in} * 100)/$ref->{count_in} : 0;
422 $ref->{viruscount_out_per} = $ref->{count_out} > 0 ? ($ref->{viruscount_out} * 100)/$ref->{count_out} : 0;
423
424 $ref->{spamcount_in_per} = $ref->{count_in} > 0 ? ($ref->{spamcount_in} * 100)/$ref->{count_in} : 0;
425 $ref->{spamcount_out_per} = $ref->{count_out} > 0 ? ($ref->{spamcount_out} * 100)/$ref->{count_out} : 0;
426
427 $ref->{bounces_in_per} = $ref->{count_in} > 0 ? ($ref->{bounces_in} * 100)/$ref->{count_in} : 0;
428 $ref->{bounces_out_per} = $ref->{count_out} > 0 ? ($ref->{bounces_out} * 100)/$ref->{count_out} : 0;
429
430 $ref->{glcount_per} = $ref->{count_in} > 0 ? ($ref->{glcount} * 100)/$ref->{count_in} : 0;
431 $ref->{spfcount_per} = $ref->{count_in} > 0 ? ($ref->{spfcount} * 100)/$ref->{count_in} : 0;
432 $ref->{rblcount_per} = $ref->{count_in} > 0 ? ($ref->{rblcount} * 100)/$ref->{count_in} : 0;
433
3d511edd 434 $ref->{junk_in} = $ref->{viruscount_in} + $ref->{spamcount_in} + $ref->{glcount} +
e350fb98
DM
435 $ref->{spfcount} + $ref->{rblcount};
436
437 $ref->{junk_out} = $ref->{viruscount_out} + $ref->{spamcount_out};
438
439 $ref->{junk_in_per} = $ref->{count_in} > 0 ? ($ref->{junk_in} * 100)/$ref->{count_in} : 0;
440 $ref->{junk_out_per} = $ref->{count_out} > 0 ? ($ref->{junk_out} * 100)/$ref->{count_out} : 0;
441
442 return $ref;
443}
444
445sub total_spam_stat {
446 my ($self, $rdb) = @_;
447 my ($from, $to) = $self->timespan();
3d511edd 448
e350fb98 449 my $sth = $rdb->{dbh}->prepare("SELECT spamlevel, COUNT(spamlevel) AS count FROM CStatistic " .
3d511edd 450 "WHERE virusinfo IS NULL and time >= ? AND time < ? AND ptime > 0 AND spamlevel > 0 " .
e350fb98 451 "GROUP BY spamlevel ORDER BY spamlevel LIMIT 10");
3d511edd 452 $sth->execute($from, $to);
e350fb98
DM
453
454 my $res = $sth->fetchall_arrayref({});
455
456 $sth->finish();
457
458 return $res;
459}
460
461sub total_virus_stat {
462 my ($self, $rdb, $order) = @_;
463
464 my ($from, $to) = $self->localdayspan();
3d511edd 465
e350fb98
DM
466 $order = "count" if !$order;
467
468 my @oa = split (',', $order);
469
470 $order = join (' DESC, ', @oa);
471 $order .= ' DESC';
3d511edd 472
e350fb98 473 my $sth = $rdb->{dbh}->prepare("SELECT Name, SUM (Count) as count FROM VirusInfo " .
3d511edd 474 "WHERE time >= ? AND time < ? " .
e350fb98
DM
475 "GROUP BY name ORDER BY $order, name");
476
3d511edd 477 $sth->execute($from, $to);
e350fb98
DM
478
479 my $res = $sth->fetchall_arrayref({});
480
481 $sth->finish();
482
483 return $res;
484}
485
486sub rule_count {
487 my ($self, $rdb) = @_;
488
489 my $sth = $rdb->{dbh}->prepare("SELECT id, name, count from rule order by count desc, name");
3d511edd
DM
490 $sth->execute();
491
e350fb98
DM
492 my $res = $sth->fetchall_arrayref({});
493 $sth->finish();
494
3d511edd 495 return $res;
e350fb98
DM
496}
497
498sub total_domain_stat {
499 my ($self, $rdb, $orderby) = @_;
3d511edd 500
e350fb98
DM
501 $orderby || ($orderby = 'domain');
502 my $sortdir = sort_dir ($orderby);
503
504 my ($from, $to) = $self->localdayspan();
505
506 my $query = "SELECT domain, SUM (CountIn) AS count_in, SUM (CountOut) AS count_out," .
507 "SUM (BytesIn) AS bytes_in, SUM (BytesOut) AS bytes_out, " .
508 "SUM (VirusIn) AS virus_in, SUM (VirusOut) AS virus_out," .
509 "SUM (SpamIn) as spam_in, SUM (SpamOut) as spam_out " .
510 "FROM DomainStat where time >= $from AND time < $to " .
511 "GROUP BY domain ORDER BY $orderby $sortdir, domain ASC";
512
513 my $sth = $rdb->{dbh}->prepare($query);
3d511edd 514 $sth->execute();
e350fb98
DM
515
516 my $res = $sth->fetchall_arrayref({});
517
518 $sth->finish();
519
520 return $res;
521}
522
523sub clear_rule_count {
524 my ($self, $rdb, $id) = @_;
525
526 if (defined($id)) {
527 $rdb->{dbh}->do ("UPDATE rule set count = 0 where id = ?", undef, $id);
528 } else {
529 $rdb->{dbh}->do("UPDATE rule set count = 0");
530 }
531}
532
533sub query_cond_good_mail {
534 my ($self, $from, $to) = @_;
535 return "time >= $from AND time < $to AND bytes > 0 AND sender IS NOT NULL";
536}
537
538sub query_active_workers {
539 my ($self) = @_;
540 my ($from, $to) = $self->timespan();
3d511edd 541
e350fb98
DM
542 my $start = $from - (3600*24)*90; # from - 90 days
543 my $cond_good_mail = $self->query_cond_good_mail ($start, $to);
544
545 return "SELECT DISTINCT sender as worker FROM CStatistic WHERE $cond_good_mail AND NOT direction";
546}
547
548sub sort_dir {
549 my ($orderby) = @_;
550
551 my $sortdir = ($orderby eq "virusinfo" || $orderby eq 'sender' || $orderby eq 'domain' || $orderby eq 'receiver') ? 'ASC' : 'DESC';
552
553 return $sortdir;
554}
555
556sub user_stat_contact_details {
557 my ($self, $rdb, $receiver, $limit, $orderby) = @_;
558 my ($from, $to) = $self->timespan();
559 my $sth;
560 my $res;
561
562 $orderby || ($orderby = 'time');
563 my $sortdir = sort_dir ($orderby);
564
565 my $cond_good_mail = $self->query_cond_good_mail ($from, $to);
566
567 my $query = "SELECT * FROM CStatistic, CReceivers " .
568 "WHERE cid = cstatistic_cid AND rid = cstatistic_rid AND $cond_good_mail AND NOT direction AND sender != '' AND receiver = ? " .
569 "ORDER BY $orderby $sortdir, receiver limit $limit";
570
571 $sth = $rdb->{dbh}->prepare($query);
572
3d511edd 573 $sth->execute ($receiver);
e350fb98
DM
574
575 while (my $ref = $sth->fetchrow_hashref()) {
576 push @$res, $ref;
577 }
578
579 $sth->finish();
580
581 return $res;
582}
583
584sub user_stat_contact {
585 my ($self, $rdb, $limit, $orderby) = @_;
586 my ($from, $to) = $self->timespan();
587 my $sth;
588 my $res;
589 my $query;
590
591 $orderby || ($orderby = 'count');
592 my $sortdir = sort_dir ($orderby);
593
594 my $cond_good_mail = $self->query_cond_good_mail ($from, $to);
595
596 if ($self->{adv}) {
597 my $active_workers = $self->query_active_workers ();
598
599 $query = "SELECT receiver, count(*) AS count, sum (bytes) AS bytes " .
600 "FROM CStatistic, CReceivers WHERE cid = cstatistic_cid AND rid = cstatistic_rid " .
601 "AND $cond_good_mail AND NOT direction AND sender != '' AND " .
602 "receiver NOT IN ($active_workers) " .
603 "GROUP BY receiver ORDER BY $orderby $sortdir, receiver limit $limit";
604 } else {
605 $query = "SELECT receiver, count(*) AS count, sum (bytes) AS bytes " .
606 "FROM CStatistic, CReceivers WHERE cid = cstatistic_cid AND rid = cstatistic_rid " .
607 "AND $cond_good_mail AND NOT direction AND sender != '' " .
608 "GROUP BY receiver ORDER BY $orderby $sortdir, receiver limit $limit";
609 }
610
611 $sth = $rdb->{dbh}->prepare($query);
612
3d511edd 613 $sth->execute();
e350fb98
DM
614
615 while (my $ref = $sth->fetchrow_hashref()) {
616 push @$res, $ref;
617 }
618
619 $sth->finish();
620
621 return $res;
622}
623
624sub user_stat_sender_details {
625 my ($self, $rdb, $sender, $limit, $orderby) = @_;
626 my ($from, $to) = $self->timespan();
627 my $sth;
628 my $res;
629
630 $orderby || ($orderby = 'time');
631 my $sortdir = sort_dir ($orderby);
632
633 my $cond_good_mail = $self->query_cond_good_mail ($from, $to);
634
3d511edd 635 $sth = $rdb->{dbh}->prepare("SELECT * FROM CStatistic, CReceivers WHERE cid = cstatistic_cid AND rid = cstatistic_rid AND " .
e350fb98
DM
636 "$cond_good_mail AND NOT direction AND sender = ? " .
637 "ORDER BY $orderby $sortdir, receiver limit $limit");
3d511edd 638 $sth->execute($sender);
e350fb98
DM
639
640 while (my $ref = $sth->fetchrow_hashref()) {
641 push @$res, $ref;
642 }
643
644 $sth->finish();
645
646 return $res;
647}
648
649sub user_stat_sender {
650 my ($self, $rdb, $limit, $orderby) = @_;
651 my ($from, $to) = $self->timespan();
652 my $sth;
653 my $res;
654 my $query;
655
656 $orderby || ($orderby = 'count');
657 my $sortdir = sort_dir ($orderby);
658
659 my $cond_good_mail = $self->query_cond_good_mail ($from, $to);
660
3d511edd
DM
661 $query = "SELECT sender,count(*) AS count, sum (bytes) AS bytes, " .
662 "count (virusinfo) as viruscount, " .
e350fb98
DM
663 "count (CASE WHEN spamlevel >= 3 THEN 1 ELSE NULL END) as spamcount " .
664 "FROM CStatistic WHERE $cond_good_mail AND NOT direction AND sender != '' " .
3d511edd
DM
665 "GROUP BY sender ORDER BY $orderby $sortdir, sender limit $limit";
666
e350fb98 667 $sth = $rdb->{dbh}->prepare($query);
3d511edd 668 $sth->execute();
e350fb98
DM
669
670 while (my $ref = $sth->fetchrow_hashref()) {
671 push @$res, $ref;
672 }
673
674 $sth->finish();
675
676 return $res;
677}
678
679sub user_stat_receiver_details {
680 my ($self, $rdb, $receiver, $limit, $orderby) = @_;
681 my ($from, $to) = $self->timespan();
682 my $sth;
683 my $res;
684
685 $orderby || ($orderby = 'time');
686 my $sortdir = sort_dir ($orderby);
687
688 my $cond_good_mail = $self->query_cond_good_mail ($from, $to);
689
3d511edd
DM
690 $sth = $rdb->{dbh}->prepare("SELECT * FROM CStatistic, CReceivers " .
691 "WHERE cid = cstatistic_cid AND rid = cstatistic_rid AND $cond_good_mail AND receiver = ? " .
e350fb98 692 "ORDER BY $orderby $sortdir, sender limit $limit");
3d511edd 693 $sth->execute($receiver);
e350fb98
DM
694
695 while (my $ref = $sth->fetchrow_hashref()) {
696 push @$res, $ref;
697 }
698
699 $sth->finish();
700
701 return $res;
702}
703
704sub user_stat_receiver {
705 my ($self, $rdb, $limit, $orderby) = @_;
706 my ($from, $to) = $self->timespan();
707 my $sth;
708 my $res;
709 my $query;
710
711 $orderby || ($orderby = 'count');
712 my $sortdir = sort_dir ($orderby);
713
714 my $cond_good_mail = $self->query_cond_good_mail ($from, $to) . " AND " .
715 "receiver IS NOT NULL AND receiver != ''";
716
717 if ($self->{adv}) {
718 my $active_workers = $self->query_active_workers ();
719
3d511edd
DM
720 $query = "SELECT receiver, count(*) AS count, sum (bytes) AS bytes, " .
721 "count (virusinfo) as viruscount, " .
e350fb98 722 "count (CASE WHEN spamlevel >= 3 THEN 1 ELSE NULL END) as spamcount " .
3d511edd
DM
723 "FROM CStatistic, CReceivers, ($active_workers) as workers " .
724 "WHERE cid = cstatistic_cid AND rid = cstatistic_rid AND $cond_good_mail AND direction AND worker=receiver " .
e350fb98
DM
725 "GROUP BY receiver " .
726 "ORDER BY $orderby $sortdir, receiver LIMIT $limit";
727 } else {
3d511edd
DM
728 $query = "SELECT receiver, count(*) AS count, sum (bytes) AS bytes, " .
729 "count (virusinfo) as viruscount, " .
e350fb98 730 "count (CASE WHEN spamlevel >= 3 THEN 1 ELSE NULL END) as spamcount " .
3d511edd
DM
731 "FROM CStatistic, CReceivers " .
732 "WHERE cid = cstatistic_cid AND rid = cstatistic_rid AND $cond_good_mail and direction " .
e350fb98
DM
733 "GROUP BY receiver " .
734 "ORDER BY $orderby $sortdir, receiver LIMIT $limit";
735 }
736
737 $sth = $rdb->{dbh}->prepare($query);
3d511edd 738 $sth->execute();
e350fb98
DM
739
740 while (my $ref = $sth->fetchrow_hashref()) {
741 push @$res, $ref;
742 }
743
744 $sth->finish();
745
746 return $res;
747}
748
749sub traffic_stat_graph {
750 my ($self, $rdb, $span, $dir) = @_;
751 my $res;
752
753 my ($from, $to) = $self->localhourspan();
754 my $p = $dir ? "In" : "Out";
755 my $timezone = tz_local_offset();;
756
757 my $spam = $dir ? "sum (SpamIn) + sum (GreylistCount) + sum (SPFCount) + sum (RBLCount)" : "sum (SpamOut)";
758
759 my $cmd = "SELECT sum(Count$p) as count, (time - $from) / $span AS index, " .
760 "sum (Virus$p) as viruscount, $spam as spamcount, sum (Bounces$p) as bounces " .
761 "FROM DailyStat WHERE time >= $from AND time < $to " .
762 "GROUP BY index ORDER BY index";
763
764 my $sth = $rdb->{dbh}->prepare($cmd);
765
3d511edd 766 $sth->execute ();
e350fb98
DM
767
768 while (my $ref = $sth->fetchrow_hashref()) {
769 @$res[$ref->{index}] = $ref;
770 }
771
772 my $c = int (($to - $from) / $span);
773
774 for (my $i = 0; $i < $c; $i++) {
775 my $eref = {count => 0, index => $i, spamcount => 0, viruscount => 0, bounces => 0};
776 @$res[$i] = $eref if !@$res[$i];
777 @$res[$i]->{time} = $from + ($i+1)*$span - $timezone;
778 }
779 $sth->finish();
780
781 return $res;
782}
783
784sub traffic_stat_day_dist {
785 my ($self, $rdb, $dir) = @_;
786 my $res;
787
788 my ($from, $to) = $self->localhourspan();
789 my $p = $dir ? "In" : "Out";
790
791 my $spam = $dir ? "sum (SpamIn) + sum (GreylistCount) + sum (SPFCount) + sum (RBLCount)" : "sum (SpamOut)";
792
793 my $cmd = "SELECT sum(Count$p) as count, ((time - $from) / 3600) % 24 AS index, " .
794 "sum (Virus$p) as viruscount, $spam as spamcount, sum (Bounces$p) as bounces " .
795 "FROM DailyStat WHERE time >= $from AND time < $to " .
796 "GROUP BY index ORDER BY index";
3d511edd 797
e350fb98
DM
798 my $sth = $rdb->{dbh}->prepare($cmd);
799
3d511edd 800 $sth->execute ();
e350fb98
DM
801
802 while (my $ref = $sth->fetchrow_hashref()) {
803 @$res[$ref->{index}] = $ref;
804 }
805
806 for (my $i = 0; $i < 24; $i++) {
807 my $eref = {count => 0, index => $i, spamcount => 0, viruscount => 0, bounces => 0};
808 @$res[$i] = $eref if !@$res[$i];
809 }
810 $sth->finish();
811
812 return $res;
813}
814
3d511edd
DM
815sub timespan {
816 my ($self, $from, $to) = @_;
817
e350fb98
DM
818 if (defined ($from) && defined ($to)) {
819 $self->{from} = $from;
820 $self->{to} = $to;
821 }
822
823 return ($self->{from}, $self->{to});
824}
825
3d511edd 826sub localdayspan {
e350fb98 827 my ($self) = @_;
3d511edd 828
e350fb98
DM
829 my ($from, $to) = $self->timespan();
830
831 my $timezone = tz_local_offset();;
832 $from = (($from + $timezone)/86400) * 86400;
833 $to = (($to + $timezone)/86400) * 86400;
834
835 $to += 86400 if $from == $to;
836
837 return ($from, $to);
838}
839
3d511edd 840sub localhourspan {
e350fb98 841 my ($self) = @_;
3d511edd 842
e350fb98
DM
843 my ($from, $to) = $self->timespan();
844
845 my $timezone = tz_local_offset();;
846 $from = (($from + $timezone)/3600) * 3600;
847 $to = (($to + $timezone)/3600) * 3600;
848
849 $to += 3600 if $from == $to;
850
851 return ($from, $to);
852}
853
e350fb98
DM
854
8551;