]> git.proxmox.com Git - pmg-api.git/blame - src/PMG/API2/Statistics.pm
api: cluster: add update-fingerprints call
[pmg-api.git] / src / PMG / API2 / Statistics.pm
CommitLineData
5de9374c
DM
1package PMG::API2::Statistics;
2
3use strict;
4use warnings;
5use Data::Dumper;
15a1e62c 6use JSON;
0f6b8ccb 7use Time::Local;
5de9374c
DM
8
9use PVE::Tools;
10use PVE::SafeSyslog;
11use PVE::INotify;
12use PVE::Exception qw(raise_param_exc);
13use PVE::RESTHandler;
14use PMG::RESTEnvironment;
15use PVE::JSONSchema qw(get_standard_option);
16
17use PMG::Utils;
44017d49 18use PMG::Config;
5de9374c
DM
19use PMG::RuleDB;
20use PMG::Statistic;
21
22use base qw(PVE::RESTHandler);
23
24__PACKAGE__->register_method ({
25 name => 'index',
26 path => '',
27 method => 'GET',
28 description => "Directory index.",
29 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
30 parameters => {
31 additionalProperties => 0,
d67b777f 32 properties => {},
5de9374c
DM
33 },
34 returns => {
35 type => 'array',
36 items => {
37 type => "object",
38 properties => {},
39 },
40 links => [ { rel => 'child', href => "{name}" } ],
41 },
42 code => sub {
43 my ($param) = @_;
44
45 return [
bcf493af 46 { name => "contact" },
e89b61c5 47 { name => "detail" },
d19c40e0 48 { name => "domains" },
5de9374c 49 { name => "mail" },
6156e4b6 50 { name => "mailcount" },
fea9d463 51 { name => "recent" },
56e1cb1b 52 { name => "recentreceivers" },
89891fa2 53 { name => "maildistribution" },
67832798 54 { name => "spamscores" },
15a1e62c 55 { name => "sender" },
2719ef94 56 { name => "rblcount" },
0ab6dc2a 57 { name => "receiver" },
d67b777f 58 { name => "virus" },
5de9374c
DM
59 ];
60 }});
61
15a1e62c
DM
62my $decode_orderby = sub {
63 my ($orderby, $allowed_props) = @_;
64
65 my $sorters;
66
67 eval { $sorters = decode_json($orderby); };
68 if (my $err = $@) {
69 raise_param_exc({ orderby => 'invalid JSON'});
70 }
71
72 my $schema = {
73 type => 'array',
74 items => {
75 type => "object",
76 properties => {
77 property => {
78 type => 'string',
79 enum => $allowed_props,
80 },
81 direction => {
82 type => 'string',
83 enum => ['ASC', 'DESC'],
84 },
85 },
86 },
87 };
88
89 PVE::JSONSchema::validate($sorters, $schema, "Parameter 'orderby' verification failed\n");
90
91 return $sorters;
92};
93
f194ac27
DM
94my $api_properties = {
95 orderby => {
96 description => "Remote sorting configuration(JSON, ExtJS compatible).",
97 type => 'string',
98 optional => 1,
99 maxLength => 4096,
100 },
15a1e62c
DM
101};
102
f194ac27
DM
103my $default_properties = sub {
104 my ($prop) = @_;
105
106 $prop //= {};
107
108 $prop->{starttime} = get_standard_option('pmg-starttime');
109 $prop->{endtime} = get_standard_option('pmg-endtime');
110
0f6b8ccb
DM
111 $prop->{year} = {
112 description => "Year. Defaults to current year. You will get statistics for the whole year if you do not specify a month or day.",
113 type => 'integer',
114 minimum => 1900,
115 maximum => 3000,
116 optional => 1,
117 };
118
119 $prop->{month} = {
120 description => "Month. You will get statistics for the whole month if you do not specify a day.",
121 type => 'integer',
122 minimum => 1,
123 maximum => 12,
124 optional => 1,
125 };
126
127 $prop->{day} = {
128 description => "Day of month. Get statistics for a single day.",
129 type => 'integer',
130 minimum => 1,
131 maximum => 31,
132 optional => 1,
133 };
134
f194ac27
DM
135 return $prop;
136};
137
138my $extract_start_end = sub {
139 my ($param) = @_;
140
0f6b8ccb
DM
141 my $has_ymd;
142 foreach my $k (qw(year month day)) {
143 if (defined($param->{$k})) {
144 $has_ymd = $k;
145 last;
146 }
147 }
148 my $has_se;
149 foreach my $k (qw(starttime endtime)) {
150 if (defined($param->{$k})) {
151 $has_se = $k;
152 last;
153 }
154 }
155
156 raise_param_exc({ $has_se => "parameter conflicts with parameter '$has_ymd'"})
157 if $has_se && $has_ymd;
158
159 my $start;
160 my $end;
161
162 if ($has_ymd) {
163 my (undef, undef, undef, undef, $month, $year) = localtime(time());
164 $month += 1;
165 $year = $param->{year} if defined($param->{year});
166 if (defined($param->{day})) {
9bb1fcf1 167 my $day = $param->{day};
0f6b8ccb 168 $month = $param->{month} if defined($param->{month});
9bb1fcf1
DC
169 $start = timelocal(0, 0, 0, $day, $month - 1, $year);
170 $end = timelocal(59, 59, 23, $day, $month - 1, $year);
0f6b8ccb
DM
171 } elsif (defined($param->{month})) {
172 my $month = $param->{month};
173 if ($month < 12) {
174 $start = timelocal(0, 0, 0, 1, $month - 1, $year);
175 $end = timelocal(0, 0, 0, 1, $month, $year);
176 } else {
177 $start = timelocal(0, 0, 0, 1, 11, $year);
178 $end = timelocal(0, 0, 0, 1, 0, $year + 1);
179 }
180 } else {
181 $start = timelocal(0, 0, 0, 1, 0, $year);
182 $end = timelocal(0, 0, 0, 1, 0, $year + 1);
183 }
184 } else {
185 $start = $param->{starttime} // (time - 86400);
186 $end = $param->{endtime} // ($start + 86400);
187 }
f194ac27
DM
188
189 return ($start, $end);
190};
15a1e62c 191
f194ac27 192my $userstat_limit = 2000; # hardcoded limit
bcf493af
DM
193
194__PACKAGE__->register_method ({
195 name => 'contact',
196 path => 'contact',
197 method => 'GET',
198 description => "Contact Address Statistics.",
199 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
200 parameters => {
201 additionalProperties => 0,
f194ac27 202 properties => $default_properties->({
bcf493af
DM
203 filter => {
204 description => "Contact address filter.",
205 type => 'string',
206 maxLength => 512,
207 optional => 1,
208 },
f194ac27
DM
209 orderby => $api_properties->{orderby},
210 }),
bcf493af
DM
211 },
212 returns => {
213 type => 'array',
214 items => {
215 type => "object",
216 properties => {
217 contact => {
218 description => "Contact email.",
219 type => 'string',
220 },
221 count => {
222 description => "Mail count.",
223 type => 'number',
224 optional => 1,
225 },
226 bytes => {
227 description => "Mail traffic (Bytes).",
228 type => 'number',
229 },
230 viruscount => {
231 description => "Number of sent virus mails.",
232 type => 'number',
233 optional => 1,
234 },
235 },
236 },
237 links => [ { rel => 'child', href => "{contact}" } ],
238 },
239 code => sub {
240 my ($param) = @_;
241
f194ac27 242 my ($start, $end) = $extract_start_end->($param);
bcf493af 243
44017d49
DM
244 my $cfg = PMG::Config->new();
245 my $advfilter = $cfg->get('admin', 'advfilter');
246
bcf493af
DM
247 my $stat = PMG::Statistic->new($start, $end);
248 my $rdb = PMG::RuleDB->new();
249
250 my $sorters = [];
251 if ($param->{orderby}) {
252 my $props = ['contact', 'count', 'bytes', 'viruscount'];
253 $sorters = $decode_orderby->($param->{orderby}, $props);
254 }
255
44017d49 256 my $res = $stat->user_stat_contact($rdb, $userstat_limit, $sorters, $param->{filter}, $advfilter);
bcf493af
DM
257
258 return $res;
259 }});
260
5522af5f
SI
261my $detail_return_properties = sub {
262 my ($prop) = @_;
263
264 $prop //= {};
265
266 $prop->{time} = {
267 description => "Receive time stamp",
268 type => 'integer',
269 };
270
271 $prop->{bytes} = {
272 description => "Mail traffic (Bytes).",
273 type => 'number',
274 };
275
276 $prop->{blocked} = {
277 description => "Mail was blocked.",
278 type => 'boolean',
279 };
280
281 $prop->{spamlevel} = {
282 description => "Spam score.",
283 type => 'number',
284 };
285
286 $prop->{virusinfo} = {
287 description => "Virus name.",
288 type => 'string',
289 optional => 1,
290 };
291
292 return $prop;
293};
294
d865dfba 295sub get_detail_statistics {
6500e09b 296 my ($type, $param) = @_;
d865dfba
SI
297
298 my ($start, $end) = $extract_start_end->($param);
d865dfba
SI
299 my $sorters = [];
300 if ($param->{orderby}) {
301 my $props = ['time', 'sender', 'bytes', 'blocked', 'spamlevel', 'virusinfo'];
302 $props->[1] = 'receiver' if $type eq 'sender';
303 $sorters = $decode_orderby->($param->{orderby}, $props);
304 }
6500e09b
TL
305 my $address = $param->{address} // $param->{$type};
306 my $rdb = PMG::RuleDB->new();
307
308 my @args = ($rdb, $address, $userstat_limit, $sorters, $param->{filter});
d865dfba 309
6500e09b 310 my $stat = PMG::Statistic->new($start, $end);
d865dfba 311 if ($type eq 'contact') {
6500e09b 312 return $stat->user_stat_contact_details(@args);
d865dfba 313 } elsif ($type eq 'sender') {
6500e09b 314 return $stat->user_stat_sender_details(@args);
d865dfba 315 } elsif ($type eq 'receiver') {
6500e09b 316 return $stat->user_stat_receiver_details(@args);
d865dfba
SI
317 } else {
318 die "invalid type provided (not 'contact', 'sender', 'receiver')\n";
319 }
d865dfba
SI
320}
321
e89b61c5
SI
322__PACKAGE__->register_method ({
323 name => 'detailstats',
324 path => 'detail',
325 method => 'GET',
326 description => "Detailed Statistics.",
327 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
328 parameters => {
329 additionalProperties => 0,
330 properties => $default_properties->({
331 type => {
332 description => "Type of statistics",
333 type => 'string',
334 enum => [ 'contact', 'sender', 'receiver' ],
335 },
336 address => get_standard_option('pmg-email-address', {
337 description => "Email address.",
338 }),
339 filter => {
340 description => "Address filter.",
341 type => 'string',
342 maxLength => 512,
343 optional => 1,
344 },
345 orderby => $api_properties->{orderby},
346 }),
347 },
348 returns => {
349 type => 'array',
350 items => {
351 type => "object",
352 properties => $detail_return_properties->({
353 sender => {
354 description => "Sender email. (for contact and receiver statistics)",
355 type => 'string',
356 optional => 1,
357 },
358 receiver => {
359 description => "Receiver email. (for sender statistics)",
360 type => 'string',
361 optional => 1,
362 },
363 }),
364 },
365 },
366 code => sub {
367 my ($param) = @_;
368
e89b61c5
SI
369 return get_detail_statistics($param->{type}, $param);
370 }});
371
372# FIXME: remove for PMG 7.0 - handled by 'details' API call which allows addresses containing '/'
bcf493af
DM
373__PACKAGE__->register_method ({
374 name => 'contactdetails',
375 path => 'contact/{contact}',
376 method => 'GET',
377 description => "Detailed Contact Statistics.",
378 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
379 parameters => {
380 additionalProperties => 0,
f194ac27 381 properties => $default_properties->({
bcf493af
DM
382 contact => get_standard_option('pmg-email-address', {
383 description => "Contact email address.",
384 }),
385 filter => {
386 description => "Sender address filter.",
387 type => 'string',
388 maxLength => 512,
389 optional => 1,
390 },
f194ac27
DM
391 orderby => $api_properties->{orderby},
392 }),
bcf493af
DM
393 },
394 returns => {
395 type => 'array',
396 items => {
397 type => "object",
5522af5f 398 properties => $detail_return_properties->({
bcf493af
DM
399 sender => {
400 description => "Sender email.",
401 type => 'string',
402 },
5522af5f 403 }),
bcf493af
DM
404 },
405 },
406 code => sub {
407 my ($param) = @_;
408
d865dfba 409 return get_detail_statistics('contact', $param);
bcf493af
DM
410 }});
411
15a1e62c
DM
412__PACKAGE__->register_method ({
413 name => 'sender',
414 path => 'sender',
415 method => 'GET',
416 description => "Sender Address Statistics.",
417 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
418 parameters => {
419 additionalProperties => 0,
f194ac27 420 properties => $default_properties->({
15a1e62c
DM
421 filter => {
422 description => "Sender address filter.",
423 type => 'string',
424 maxLength => 512,
425 optional => 1,
426 },
f194ac27
DM
427 orderby => $api_properties->{orderby},
428 }),
15a1e62c
DM
429 },
430 returns => {
431 type => 'array',
432 items => {
433 type => "object",
434 properties => {
435 sender => {
436 description => "Sender email.",
437 type => 'string',
438 },
439 count => {
440 description => "Mail count.",
441 type => 'number',
442 optional => 1,
443 },
444 bytes => {
445 description => "Mail traffic (Bytes).",
446 type => 'number',
447 },
448 viruscount => {
449 description => "Number of sent virus mails.",
450 type => 'number',
451 optional => 1,
452 },
453 },
454 },
455 links => [ { rel => 'child', href => "{sender}" } ],
456 },
457 code => sub {
458 my ($param) = @_;
459
f194ac27 460 my ($start, $end) = $extract_start_end->($param);
15a1e62c
DM
461
462 my $stat = PMG::Statistic->new($start, $end);
463 my $rdb = PMG::RuleDB->new();
464
465 my $sorters = [];
466 if ($param->{orderby}) {
467 my $props = ['sender', 'count', 'bytes', 'viruscount'];
468 $sorters = $decode_orderby->($param->{orderby}, $props);
469 }
470
471 my $res = $stat->user_stat_sender($rdb, $userstat_limit, $sorters, $param->{filter});
472
473 return $res;
474 }});
475
e89b61c5 476# FIXME: remove for PMG 7.0 - handled by 'details' API call which allows addresses containing '/'
15a1e62c
DM
477__PACKAGE__->register_method ({
478 name => 'senderdetails',
479 path => 'sender/{sender}',
480 method => 'GET',
481 description => "Detailed Sender Statistics.",
482 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
483 parameters => {
484 additionalProperties => 0,
f194ac27 485 properties => $default_properties->({
15a1e62c
DM
486 sender => get_standard_option('pmg-email-address', {
487 description => "Sender email address.",
488 }),
489 filter => {
490 description => "Receiver address filter.",
491 type => 'string',
492 maxLength => 512,
493 optional => 1,
494 },
f194ac27
DM
495 orderby => $api_properties->{orderby},
496 }),
15a1e62c
DM
497 },
498 returns => {
499 type => 'array',
500 items => {
501 type => "object",
5522af5f 502 properties => $detail_return_properties->({
15a1e62c
DM
503 receiver => {
504 description => "Receiver email.",
505 type => 'string',
506 },
5522af5f 507 }),
15a1e62c
DM
508 },
509 },
510 code => sub {
511 my ($param) = @_;
512
d865dfba 513 return get_detail_statistics('sender', $param);
0ab6dc2a
DM
514 }});
515
516__PACKAGE__->register_method ({
517 name => 'receiver',
518 path => 'receiver',
519 method => 'GET',
520 description => "Receiver Address Statistics.",
521 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
522 parameters => {
523 additionalProperties => 0,
f194ac27 524 properties => $default_properties->({
0ab6dc2a
DM
525 filter => {
526 description => "Receiver address filter.",
527 type => 'string',
528 maxLength => 512,
529 optional => 1,
530 },
f194ac27
DM
531 orderby => $api_properties->{orderby},
532 }),
0ab6dc2a
DM
533 },
534 returns => {
535 type => 'array',
536 items => {
537 type => "object",
538 properties => {
539 receiver => {
540 description => "Sender email.",
541 type => 'string',
542 },
543 count => {
544 description => "Mail count.",
545 type => 'number',
546 optional => 1,
547 },
548 bytes => {
549 description => "Mail traffic (Bytes).",
550 type => 'number',
551 },
552 spamcount => {
553 description => "Number of sent spam mails.",
554 type => 'number',
555 optional => 1,
556 },
557 viruscount => {
558 description => "Number of sent virus mails.",
559 type => 'number',
560 optional => 1,
561 },
562 },
563 },
564 links => [ { rel => 'child', href => "{receiver}" } ],
565 },
566 code => sub {
567 my ($param) = @_;
568
f194ac27 569 my ($start, $end) = $extract_start_end->($param);
0ab6dc2a 570
44017d49
DM
571 my $cfg = PMG::Config->new();
572 my $advfilter = $cfg->get('admin', 'advfilter');
573
0ab6dc2a
DM
574 my $stat = PMG::Statistic->new($start, $end);
575 my $rdb = PMG::RuleDB->new();
576
577 my $sorters = [];
578 if ($param->{orderby}) {
579 my $props = ['receiver', 'count', 'bytes', 'spamcount', 'viruscount'];
580 $sorters = $decode_orderby->($param->{orderby}, $props);
581 }
582
44017d49 583 my $res = $stat->user_stat_receiver($rdb, $userstat_limit, $sorters, $param->{filter}, $advfilter);
0ab6dc2a
DM
584
585 return $res;
586 }});
587
e89b61c5 588# FIXME: remove for PMG 7.0 - handled by 'details' API call which allows addresses containing '/'
0ab6dc2a
DM
589__PACKAGE__->register_method ({
590 name => 'receiverdetails',
591 path => 'receiver/{receiver}',
592 method => 'GET',
593 description => "Detailed Receiver Statistics.",
594 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
595 parameters => {
596 additionalProperties => 0,
f194ac27 597 properties => $default_properties->({
0ab6dc2a
DM
598 receiver => get_standard_option('pmg-email-address', {
599 description => "Receiver email address.",
600 }),
601 filter => {
602 description => "Sender address filter.",
603 type => 'string',
604 maxLength => 512,
605 optional => 1,
606 },
f194ac27
DM
607 orderby => $api_properties->{orderby},
608 }),
0ab6dc2a
DM
609 },
610 returns => {
611 type => 'array',
612 items => {
613 type => "object",
5522af5f 614 properties => $detail_return_properties->({
0ab6dc2a
DM
615 sender => {
616 description => "Sender email.",
617 type => 'string',
618 },
5522af5f 619 }),
0ab6dc2a
DM
620 },
621 },
622 code => sub {
623 my ($param) = @_;
624
d865dfba 625 return get_detail_statistics('receiver', $param);
15a1e62c
DM
626 }});
627
d19c40e0
DM
628__PACKAGE__->register_method ({
629 name => 'domains',
630 path => 'domains',
631 method => 'GET',
632 description => "Mail Domains Statistics.",
633 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
634 parameters => {
635 additionalProperties => 0,
f194ac27 636 properties => $default_properties->(),
d19c40e0
DM
637 },
638 returns => {
639 type => 'array',
640 items => {
641 type => "object",
642 properties => {
643 domain => {
644 description => "Domain name.",
645 type => 'string',
646 },
647 count_in => {
648 description => "Incoming mail count.",
649 type => 'number',
650 },
651 count_out => {
652 description => "Outgoing mail count.",
653 type => 'number',
654 },
655 spamcount_in => {
656 description => "Incoming spam mails.",
657 type => 'number',
658 },
659 spamcount_out => {
660 description => "Outgoing spam mails.",
661 type => 'number',
662 },
6bc396fc
DM
663 bytes_in => {
664 description => "Incoming mail traffic (Bytes).",
d19c40e0
DM
665 type => 'number',
666 },
6bc396fc
DM
667 bytes_out => {
668 description => "Outgoing mail traffic (Bytes).",
d19c40e0
DM
669 type => 'number',
670 },
671 viruscount_in => {
672 description => "Number of incoming virus mails.",
673 type => 'number',
674 },
675 viruscount_out => {
676 description => "Number of outgoing virus mails.",
677 type => 'number',
678 },
679 },
680 },
681 },
682 code => sub {
683 my ($param) = @_;
684
f194ac27 685 my ($start, $end) = $extract_start_end->($param);
d19c40e0
DM
686
687 my $stat = PMG::Statistic->new($start, $end);
688 my $rdb = PMG::RuleDB->new();
689
690 #PMG::Statistic::update_stats_domainstat_in($rdb->{dbh}, $cinfo);
691 #PMG::Statistic::update_stats_domainstat_out($rdb->{dbh}, $cinfo);
692
693 my $res = $stat->total_domain_stat($rdb);
694
15a1e62c 695
d19c40e0
DM
696 return $res;
697 }});
698
5de9374c
DM
699__PACKAGE__->register_method ({
700 name => 'mail',
701 path => 'mail',
702 method => 'GET',
703 description => "General Mail Statistics.",
704 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
5de9374c
DM
705 parameters => {
706 additionalProperties => 0,
f194ac27 707 properties => $default_properties->(),
5de9374c
DM
708 },
709 returns => {
710 type => "object",
26e55ddd
DM
711 properties => {
712 avptime => {
713 description => "Average mail processing time in seconds.",
714 type => 'number',
d3550ac9
DM
715 },
716 bounces_in => {
717 description => "Incoming bounce mail count (sender = <>).",
718 type => 'number',
719 },
720 bounces_out => {
721 description => "Outgoing bounce mail count (sender = <>).",
722 type => 'number',
723 },
724 count => {
725 description => "Overall mail count (in and out).",
726 type => 'number',
727 },
728 count_in => {
729 description => "Incoming mail count.",
730 type => 'number',
731 },
732 count_out => {
733 description => "Outgoing mail count.",
734 type => 'number',
735 },
736 glcount => {
737 description => "Number of greylisted mails.",
738 type => 'number',
739 },
4681645a
DM
740 rbl_rejects => {
741 description => "Number of RBL rejects.",
742 type => 'integer',
743 },
744 pregreet_rejects => {
745 description => "PREGREET recject count.",
746 type => 'integer',
747 },
d3550ac9 748 junk_in => {
4681645a 749 description => "Incoming junk mail count (viruscount_in + spamcount_in + glcount + spfcount + rbl_rejects + pregreet_rejects).",
d3550ac9
DM
750 type => 'number',
751 },
752 junk_out => {
753 description => "Outgoing junk mail count (viruscount_out + spamcount_out).",
754 type => 'number',
755 },
756 spamcount_in => {
757 description => "Incoming spam mails.",
758 type => 'number',
759 },
760 spamcount_out => {
761 description => "Outgoing spam mails.",
762 type => 'number',
763 },
764 spfcount => {
765 description => "Mails rejected by SPF.",
766 type => 'number',
767 },
d19c40e0 768 bytes_in => {
d3550ac9
DM
769 description => "Incoming mail traffic (bytes).",
770 type => 'number',
771 },
d19c40e0 772 bytes_out => {
d3550ac9
DM
773 description => "Outgoing mail traffic (bytes).",
774 type => 'number',
775 },
776 viruscount_in => {
777 description => "Number of incoming virus mails.",
778 type => 'number',
779 },
780 viruscount_out => {
781 description => "Number of outgoing virus mails.",
782 type => 'number',
783 },
26e55ddd 784 },
5de9374c
DM
785 },
786 code => sub {
787 my ($param) = @_;
91bc7652 788
f194ac27 789 my ($start, $end) = $extract_start_end->($param);
91bc7652 790
5de9374c
DM
791 my $stat = PMG::Statistic->new($start, $end);
792 my $rdb = PMG::RuleDB->new();
91bc7652 793
5de9374c
DM
794 my $res = $stat->total_mail_stat($rdb);
795
4681645a
DM
796 my $rejects = $stat->postscreen_stat($rdb);
797
798 $res->{rbl_rejects} //= 0;
799 if (defined(my $rbl_rejects = $rejects->{rbl_rejects})) {
800 foreach my $k (qw(rbl_rejects junk_in count_in count)) {
801 $res->{$k} += $rbl_rejects;
802 }
803 }
804
805 $res->{pregreet_rejects} //= 0;
806 if (defined(my $pregreet_rejects = $rejects->{pregreet_rejects})) {
807 foreach my $k (qw(pregreet_rejects junk_in count_in count)) {
808 $res->{$k} += $pregreet_rejects;
809 }
810 }
811
5de9374c
DM
812 return $res;
813 }});
814
fea9d463
DC
815__PACKAGE__->register_method ({
816 name => 'recent',
817 path => 'recent',
818 method => 'GET',
819 description => "Mail Count Statistics.",
820 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
821 parameters => {
822 additionalProperties => 0,
823 properties => {
824 hours => {
825 description => "How many hours you want to get",
826 type => 'integer',
827 minimum => 1,
828 maximum => 24,
829 optional => 1,
830 default => 12,
831 },
832 timespan => {
833 description => "The Timespan for one datapoint (in seconds)",
834 type => 'integer',
835 minimum => 1,
836 maximum => 1800,
837 optional => 1,
838 default => 1800,
839 },
840 },
841 },
842 returns => {
843 type => 'array',
844 items => {
845 type => "object",
846 properties => {
847 index => {
848 description => "Time index.",
849 type => 'integer',
850 },
851 time => {
852 description => "Time (Unix epoch).",
853 type => 'integer',
854 },
855 count => {
856 description => "Overall mail count (in and out).",
857 type => 'number',
858 },
859 count_in => {
860 description => "Incoming mail count.",
861 type => 'number',
862 },
863 count_out => {
864 description => "Outgoing mail count.",
865 type => 'number',
866 },
867 spam => {
868 description => "Overall spam mail count (in and out).",
869 type => 'number',
870 },
871 spam_in => {
872 description => "Incoming spam mails (spamcount_in + glcount + spfcount).",
873 type => 'number',
874 },
875 spam_out => {
876 description => "Outgoing spam mails.",
877 type => 'number',
878 },
879 bytes_in => {
880 description => "Number of incoming bytes mails.",
881 type => 'number',
882 },
883 bytes_out => {
884 description => "Number of outgoing bytes mails.",
885 type => 'number',
886 },
887 virus_in => {
888 description => "Number of incoming virus mails.",
889 type => 'number',
890 },
891 virus_out => {
892 description => "Number of outgoing virus mails.",
893 type => 'number',
894 },
895 timespan => {
896 description => "Timespan in seconds for one data point",
897 type => 'number',
898 }
899 },
900 },
901 },
902 code => sub {
903 my ($param) = @_;
904
fea9d463
DC
905 my $hours = $param->{hours} // 12;
906 my $span = $param->{timespan} // 1800;
907
908 my $end = time();
909 my $start = $end - 3600*$hours;
910
911 my $stat = PMG::Statistic->new($start, $end);
912 my $rdb = PMG::RuleDB->new();
913
914 my $res = $stat->recent_mailcount($rdb, $span);
915
916 return $res;
917 }});
918
56e1cb1b
DC
919__PACKAGE__->register_method ({
920 name => 'recentreceivers',
921 path => 'recentreceivers',
922 method => 'GET',
923 description => "Top recent Mail Receivers (including spam)",
924 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
925 parameters => {
926 additionalProperties => 0,
927 properties => {
928 hours => {
929 description => "How many hours you want to get",
930 type => 'integer',
931 minimum => 1,
932 maximum => 24,
933 optional => 1,
934 default => 12,
935 },
936 limit => {
937 description => "The maximum number of receivers to return.",
938 type => 'integer',
939 minimum => 1,
940 maximum => 50,
941 optional => 1,
942 default => 5,
943 },
944 },
945 },
946 returns => {
947 type => 'array',
948 items => {
949 type => "object",
950 properties => {
951 count => {
952 description => "The count of incoming not blocked E-Mails",
953 type => 'integer',
954 },
955 receiver => {
956 description => "The receiver",
957 type => 'string',
958 },
959 },
960 },
961 },
962 code => sub {
963 my ($param) = @_;
964
56e1cb1b
DC
965 my $hours = $param->{hours} // 12;
966
967 my $limit = $param->{limit} // 5;
968
969 my $end = time();
970 my $start = $end - 3600*$hours;
971
972 my $stat = PMG::Statistic->new($start, $end);
973 my $rdb = PMG::RuleDB->new();
974
975 my $res = $stat->recent_receivers($rdb, $limit);
976
977 return $res;
978 }});
979
6156e4b6
DM
980__PACKAGE__->register_method ({
981 name => 'mailcount',
982 path => 'mailcount',
983 method => 'GET',
984 description => "Mail Count Statistics.",
985 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
986 parameters => {
987 additionalProperties => 0,
f194ac27 988 properties => $default_properties->({
6156e4b6 989 timespan => {
f194ac27 990 description => "Return Mails/<timespan>, where <timespan> is specified in seconds.",
6156e4b6
DM
991 type => 'integer',
992 minimum => 3600,
993 maximum => 366*86400,
994 optional => 1,
995 default => 3600,
f194ac27
DM
996 },
997 }),
6156e4b6
DM
998 },
999 returns => {
1000 type => 'array',
1001 items => {
1002 type => "object",
1003 properties => {
1004 index => {
1005 description => "Time index.",
1006 type => 'integer',
1007 },
1008 time => {
1009 description => "Time (Unix epoch).",
1010 type => 'integer',
1011 },
1012 count => {
1013 description => "Overall mail count (in and out).",
1014 type => 'number',
1015 },
1016 count_in => {
1017 description => "Incoming mail count.",
1018 type => 'number',
1019 },
1020 count_out => {
1021 description => "Outgoing mail count.",
1022 type => 'number',
1023 },
1024 spamcount_in => {
4681645a 1025 description => "Incoming spam mails (spamcount_in + glcount + spfcount + rbl_rejects + pregreet_rejects).",
6156e4b6
DM
1026 type => 'number',
1027 },
1028 spamcount_out => {
1029 description => "Outgoing spam mails.",
1030 type => 'number',
1031 },
1032 viruscount_in => {
1033 description => "Number of incoming virus mails.",
1034 type => 'number',
1035 },
1036 viruscount_out => {
1037 description => "Number of outgoing virus mails.",
1038 type => 'number',
1039 },
4681645a
DM
1040 rbl_rejects => {
1041 description => "Number of RBL rejects.",
1042 type => 'integer',
1043 },
1044 pregreet_rejects => {
1045 description => "PREGREET recject count.",
1046 type => 'integer',
1047 },
6156e4b6
DM
1048 bounces_in => {
1049 description => "Incoming bounce mail count (sender = <>).",
1050 type => 'number',
1051 },
1052 bounces_out => {
1053 description => "Outgoing bounce mail count (sender = <>).",
1054 type => 'number',
1055 },
1056 },
1057 },
1058 },
1059 code => sub {
1060 my ($param) = @_;
1061
f194ac27 1062 my ($start, $end) = $extract_start_end->($param);
6156e4b6
DM
1063
1064 my $span = $param->{timespan} // 3600;
1065
1066 my $count = ($end - $start)/$span;
1067
1068 die "too many entries - try to increase parameter 'span'\n" if $count > 5000;
1069
1070 my $stat = PMG::Statistic->new($start, $end);
1071 my $rdb = PMG::RuleDB->new();
1072
1073 #PMG::Statistic::update_stats_dailystat($rdb->{dbh}, $cinfo);
1074
4681645a
DM
1075 my $rejects = $stat->postscreen_stat_graph($rdb, $span);
1076
6156e4b6
DM
1077 my $res = $stat->traffic_stat_graph ($rdb, $span);
1078
4681645a
DM
1079 my $element_count = scalar(@$res);
1080
1081 for (my $i = 0; $i < $element_count; $i++) {
1082 my $el = $rejects->[$i];
1083 next if !$el;
1084 my $d = $res->[$i];
1085 foreach my $k ('rbl_rejects', 'pregreet_rejects') {
1086 my $count = $el->{$k} // 0;
1087 $d->{$k} = $count;
1088 foreach my $k (qw(count count_in spamcount_in)) {
1089 $d->{$k} += $count;
1090 }
1091 }
1092 }
1093
6156e4b6
DM
1094 return $res;
1095 }});
1096
91bc7652
DM
1097__PACKAGE__->register_method ({
1098 name => 'virus',
1099 path => 'virus',
1100 method => 'GET',
1101 description => "Get Statistics about detected Viruses.",
1102 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
91bc7652
DM
1103 parameters => {
1104 additionalProperties => 0,
f194ac27 1105 properties => $default_properties->(),
91bc7652
DM
1106 },
1107 returns => {
1108 type => 'array',
1109 items => {
1110 type => "object",
1111 properties => {
1112 name => {
1113 description => 'Virus name.',
1114 type => 'string',
1115 },
1116 count => {
1117 description => 'Detection count.',
1118 type => 'integer',
1119 },
1120 },
1121 }
1122 },
1123 code => sub {
1124 my ($param) = @_;
1125
f194ac27 1126 my ($start, $end) = $extract_start_end->($param);
91bc7652
DM
1127
1128 my $stat = PMG::Statistic->new($start, $end);
1129 my $rdb = PMG::RuleDB->new();
1130
1131 my $res = $stat->total_virus_stat($rdb);
1132
1133 return $res;
1134 }});
1135
1136__PACKAGE__->register_method ({
67832798
DM
1137 name => 'spamscores',
1138 path => 'spamscores',
91bc7652 1139 method => 'GET',
67832798
DM
1140 description => "Get the count of spam mails grouped by spam score. " .
1141 "Count for score 10 includes mails with spam score > 10.",
91bc7652 1142 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
91bc7652
DM
1143 parameters => {
1144 additionalProperties => 0,
f194ac27 1145 properties => $default_properties->(),
91bc7652
DM
1146 },
1147 returns => {
1148 type => 'array',
1149 items => {
1150 type => "object",
1151 properties => {
1152 level => {
1153 description => 'Spam level.',
1154 type => 'string',
1155 },
1156 count => {
1157 description => 'Detection count.',
1158 type => 'integer',
1159 },
67832798
DM
1160 ratio => {
1161 description => 'Portion of overall mail count.',
1162 type => 'number',
1163 },
91bc7652
DM
1164 },
1165 }
1166 },
1167 code => sub {
1168 my ($param) = @_;
1169
f194ac27 1170 my ($start, $end) = $extract_start_end->($param);
91bc7652
DM
1171
1172 my $stat = PMG::Statistic->new($start, $end);
1173 my $rdb = PMG::RuleDB->new();
1174
1175 my $totalstat = $stat->total_mail_stat ($rdb);
1176 my $spamstat = $stat->total_spam_stat($rdb);
1177
d67b777f 1178 my $res = [];
91bc7652 1179
67832798 1180 my $count_in = $totalstat->{count_in};
83081b3d
DM
1181
1182 my $levelcount = {};
1767b03d 1183 my $spamcount = 0;
91bc7652 1184 foreach my $ref (@$spamstat) {
1767b03d
DM
1185 if (my $level = $ref->{spamlevel}) {
1186 next if $level < 1; # just to be sure
1187 $spamcount += $ref->{count};
1188 $level = 10 if $level > 10;
1189 $levelcount->{$level} += $ref->{count};
1190 }
91bc7652
DM
1191 }
1192
1767b03d 1193 $levelcount->{0} = $count_in - $spamcount;
83081b3d
DM
1194
1195 for (my $i = 0; $i <= 10; $i++) {
67832798
DM
1196 my $count = $levelcount->{$i} // 0;
1197 my $ratio = $count_in ? $count/$count_in : 0;
1198 push @$res, { level => $i, count => $count, ratio => $ratio };
83081b3d 1199 }
91bc7652
DM
1200
1201 return $res;
1202 }});
1203
89891fa2
DM
1204__PACKAGE__->register_method ({
1205 name => 'maildistribution',
1206 path => 'maildistribution',
1207 method => 'GET',
1208 description => "Get the count of spam mails grouped by spam score. " .
1209 "Count for score 10 includes mails with spam score > 10.",
1210 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
1211 parameters => {
1212 additionalProperties => 0,
f194ac27 1213 properties => $default_properties->(),
89891fa2
DM
1214 },
1215 returns => {
1216 type => 'array',
1217 items => {
1218 type => "object",
1219 properties => {
1220 index => {
1221 description => "Hour (0-23).",
1222 type => 'integer',
1223 },
1224 count => {
1225 description => "Overall mail count (in and out).",
1226 type => 'number',
1227 },
1228 count_in => {
1229 description => "Incoming mail count.",
1230 type => 'number',
1231 },
1232 count_out => {
1233 description => "Outgoing mail count.",
1234 type => 'number',
1235 },
1236 spamcount_in => {
1237 description => "Incoming spam mails (spamcount_in + glcount + spfcount).",
1238 type => 'number',
1239 },
1240 spamcount_out => {
1241 description => "Outgoing spam mails.",
1242 type => 'number',
1243 },
1244 viruscount_in => {
1245 description => "Number of incoming virus mails.",
1246 type => 'number',
1247 },
1248 viruscount_out => {
1249 description => "Number of outgoing virus mails.",
1250 type => 'number',
1251 },
1252 bounces_in => {
1253 description => "Incoming bounce mail count (sender = <>).",
1254 type => 'number',
1255 },
1256 bounces_out => {
1257 description => "Outgoing bounce mail count (sender = <>).",
1258 type => 'number',
1259 },
1260 },
1261 },
1262 },
1263 code => sub {
1264 my ($param) = @_;
1265
f194ac27 1266 my ($start, $end) = $extract_start_end->($param);
89891fa2
DM
1267
1268 my $stat = PMG::Statistic->new($start, $end);
1269 my $rdb = PMG::RuleDB->new();
1270
1271 #PMG::Statistic::update_stats_dailystat($rdb->{dbh}, $cinfo);
1272
1273 my $res = $stat->traffic_stat_day_dist ($rdb);
1274
1275 return $res;
1276 }});
1277
2719ef94 1278__PACKAGE__->register_method ({
980ce907
DM
1279 name => 'rejectcount',
1280 path => 'rejectcount',
2719ef94 1281 method => 'GET',
980ce907 1282 description => "Early SMTP reject count statistic (RBL, PREGREET rejects with postscreen)",
2719ef94
DM
1283 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
1284 parameters => {
1285 additionalProperties => 0,
1286 properties => $default_properties->({
1287 timespan => {
980ce907 1288 description => "Return RBL/PREGREET rejects/<timespan>, where <timespan> is specified in seconds.",
2719ef94
DM
1289 type => 'integer',
1290 minimum => 3600,
1291 maximum => 366*86400,
1292 optional => 1,
1293 default => 3600,
1294 },
1295 }),
1296 },
1297 returns => {
1298 type => 'array',
1299 items => {
1300 type => "object",
1301 properties => {
1302 index => {
1303 description => "Time index.",
1304 type => 'integer',
1305 },
1306 time => {
1307 description => "Time (Unix epoch).",
1308 type => 'integer',
1309 },
980ce907 1310 rbl_rejects => {
2719ef94 1311 description => "RBL recject count.",
980ce907
DM
1312 type => 'integer',
1313 },
1314 pregreet_rejects => {
1315 description => "PREGREET recject count.",
1316 type => 'integer',
2719ef94
DM
1317 },
1318 },
1319 },
1320 },
1321 code => sub {
1322 my ($param) = @_;
1323
2719ef94
DM
1324 my ($start, $end) = $extract_start_end->($param);
1325
1326 my $span = $param->{timespan} // 3600;
1327
1328 my $count = ($end - $start)/$span;
1329
1330 die "too many entries - try to increase parameter 'span'\n" if $count > 5000;
1331
1332 my $stat = PMG::Statistic->new($start, $end);
1333 my $rdb = PMG::RuleDB->new();
1334
4681645a 1335 my $res = $stat->postscreen_stat_graph($rdb, $span);
2719ef94
DM
1336
1337 return $res;
1338 }});
1339
5de9374c 13401;