%PDF- %PDF-
| Direktori : /www/varak.net/dmarc.varak.net/utils/ |
| Current File : //www/varak.net/dmarc.varak.net/utils/summary_report.php |
<?php
/**
* dmarc-srg - A php parser, viewer and summary report generator for incoming DMARC reports.
* Copyright (C) 2020-2025 Aleksey Andreev (liuch)
*
* Available at:
* https://github.com/liuch/dmarc-srg
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* =========================
*
* This script creates a summary report and sends it by email.
* The email addresses must be specified in the configuration file.
* The script have two required parameters: `domain` and `period`,
* and five optional: `offset`, `emailto`, `format`, `user`, `subject`.
* The `domain` parameter must contain a domain name, a comma-separated list of domains, or `all`.
* The `period` parameter must have one of these values:
* `lastmonth` - to make a report for the last month;
* `lastweek` - to make a report for the last week;
* `lastndays:N` - to make a report for the last N days;
* The `offset` parameter is optional. It is intended to be used in conjunction with the `period` parameter
* and allows you to specify a date range offset to the past. Must be the number days, weeks or months
* for `lastNDays`, `lastWeek` and `lastMonth` respectively. The default value is 0.
* The `emailto` parameter is optional. Set it if you want to use a different email address to sent the report to.
* The `format` parameter is optional. It provides the ability to specify the email message format.
* Possible values are: `text`, `html`, `text+html`. The default value is `text`.
* The `user` parameter is optional. It can be useful for specifying a list of assigned domains for a single user
* when the `domain` options is set to `all`. Only makes sense if the user_management mode is active.
* The default value is `admin`.
* The `subject` parameter is optional. It allows you to specify the subject line of the summary report message
* instead of the default subject line. Remember to escape spaces and special characters!
*
* Some examples:
*
* $ php utils/summary_report.php domain=example.com period=lastweek
* will send a summary report for the last week for the domain example.com via email
*
* $ php utils/summary_report.php domain=example.com period=lastweek offset=1
* will send a summary report for the week before last week for the domain example.com via email
*
* $ php utils/summary_report.php domain=example.com period=lastndays:10 "subject:My cool summary report"
* will send a summary report for last 10 days for the domain example.com via email with the specified subject line
*
* $ php utils/summary_report.php domain=all user=frederick1 period=lastmonth emailto=frederick@example.com
* will send a summary report for the last month for all domains assigned to user frederick1 to frederick@example.com.
*
* The best place to use it is cron.
*
* @category Utilities
* @package DmarcSrg
* @author Aleksey Andreev (liuch)
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU/GPLv3
*/
namespace Liuch\DmarcSrg;
use Liuch\DmarcSrg\Mail\Mailer;
use Liuch\DmarcSrg\Mail\MailBody;
use Liuch\DmarcSrg\Domains\Domain;
use Liuch\DmarcSrg\Domains\DomainList;
use Liuch\DmarcSrg\Report\SummaryReport;
use Liuch\DmarcSrg\Report\OverallReport;
use Liuch\DmarcSrg\Exception\SoftException;
use Liuch\DmarcSrg\Exception\RuntimeException;
require realpath(__DIR__ . '/..') . '/init.php';
if (Core::isWEB()) {
echo 'Forbidden';
exit(1);
}
if (!isset($argv)) {
echo 'Cannot get the script arguments. Probably register_argc_argv is disabled.', PHP_EOL;
exit(1);
}
$domain = null;
$all_dom = false;
$period = null;
$offset = '0';
$emailto = null;
$format = 'text';
$uname = null;
$acount = count($argv);
$subject = '';
if ($acount <= 1) {
echo "Usage: {$argv[0]} domain=<domains>|all", PHP_EOL;
echo ' period=lastmonth|lastweek|lastndays:<days>', PHP_EOL;
echo ' [offset=<days>] [format=text|html|text+html]', PHP_EOL;
echo ' [emailto=<email address>] [user=<username>]', PHP_EOL;
exit(1);
}
for ($i = 1; $i < $acount; ++$i) {
$av = explode('=', $argv[$i]);
if (count($av) == 2) {
switch ($av[0]) {
case 'domain':
$domain = $av[1];
break;
case 'period':
$period = $av[1];
break;
case 'offset':
$offset = $av[1];
break;
case 'emailto':
$emailto = $av[1];
break;
case 'format':
$format = $av[1];
break;
case 'user':
$uname = $av[1];
break;
case 'subject':
$subject = $av[1];
break;
}
}
}
$core = Core::instance();
try {
$core->setCurrentUser($uname ?? 'admin');
if (!$domain) {
throw new SoftException('Parameter "domain" is not specified');
}
if (!$period) {
throw new SoftException('Parameter "period" is not specified');
}
if (filter_var($offset, FILTER_VALIDATE_INT, [ 'options' => [ 'min_range' => 0 ] ]) === false) {
throw new SoftException('Parameter "offset" must be a positive integer');
}
if (!in_array($format, [ 'text', 'html', 'text+html' ], true)) {
throw new SoftException('Unknown email message format: ' . $format);
}
if (!$emailto) {
$emailto = $core->config('mailer/default', '');
}
if ($domain === 'all') {
$domains = (new DomainList($core->getCurrentUser()))->getList()['domains'];
$all_dom = true;
} else {
$domains = array_map(function ($d) {
return new Domain($d);
}, explode(',', $domain));
}
$rep = new SummaryReport($period, intval($offset));
switch ($format) {
case 'text':
$text = [];
$html = null;
break;
case 'html':
$text = null;
$html = [];
break;
default:
$text = [];
$html = [];
break;
}
$dom_cnt = count($domains);
if ($dom_cnt == 0) {
throw new SoftException('The user has no assigned domains');
}
$overall = null;
if ($dom_cnt > 1) {
$overall = new OverallReport();
$rep->bindSection($overall);
}
$add_sep = function () use (&$text, &$html) {
if (!is_null($text)) {
$text[] = '-----------------------------------';
$text[] = '';
}
if (!is_null($html)) {
$html[] = '<hr style="margin:2em 0;" />';
}
};
$rep_cnt = 0;
$err_cnt = 0;
for ($i = 0; $i < $dom_cnt; ++$i) {
$domain = $domains[$i];
if ($domain->isAssigned($core->getCurrentUser())) {
$rep->setDomain($domain);
if (!$all_dom || $domain->active() || !$rep->isEmpty()) {
if ($rep_cnt || $err_cnt) {
$add_sep();
}
if (!is_null($text)) {
foreach ($rep->text() as &$row) {
$text[] = $row;
}
unset($row);
}
if (!is_null($html)) {
foreach ($rep->html() as &$row) {
$html[] = $row;
}
unset($row);
}
++$rep_cnt;
} else {
$overall->removeRow($domain);
}
} else {
if ($rep_cnt || $err_cnt) {
$add_sep();
}
$nf_message = "Domain \"{$domain->fqdn()}\" does not exist";
if ($dom_cnt === 1) {
throw new SoftException($nf_message);
}
if (!is_null($text)) {
$text[] = "# {$nf_message}";
$text[] = '';
}
if (!is_null($html)) {
$html[] = '<h2>' . htmlspecialchars($nf_message) . '</h2>';
}
++$err_cnt;
}
}
if (!is_null($text)) {
if (!$rep_cnt && !$err_cnt) {
$text[] = 'There are no active domains';
} elseif ($overall) {
$text = array_merge($overall->text(), [ '-----------------------------------', '' ], $text);
}
}
if (!is_null($html)) {
if (!$rep_cnt && !$err_cnt) {
$html[] = '<p>There are no active domains</p>';
} elseif ($overall) {
$html = array_merge($overall->html(), [ '<hr style="margin:2em 0;" />' ], $html);
}
}
$mbody = new MailBody();
$mbody->text = $text;
$mbody->html = $html;
$mailer = Mailer::get($core->config('mailer', []));
$mailer->setFrom($core->config('mailer/from', ''));
$mailer->addAddress($emailto);
if (!empty($subject)) {
$mailer->setSubject($subject);
} elseif ($dom_cnt === 1) {
$mailer->setSubject("{$rep->subject()} for {$domain->fqdn()}");
} elseif ($uname && $all_dom) {
$mailer->setSubject("{$rep->subject()} for {$uname}'s domains");
} else {
$dom_cnt = $rep_cnt + $err_cnt;
$mailer->setSubject("{$rep->subject()} for {$dom_cnt} domains");
}
$mailer->setBody($mbody);
$mailer->send();
} catch (SoftException $e) {
echo 'Error: ' . $e->getMessage() . PHP_EOL;
exit(1);
} catch (RuntimeException $e) {
echo ErrorHandler::exceptionText($e);
exit(1);
}
exit(0);