summaryrefslogtreecommitdiffstats
path: root/modules-available/eventlog/inc/notificationtransport.inc.php
diff options
context:
space:
mode:
Diffstat (limited to 'modules-available/eventlog/inc/notificationtransport.inc.php')
-rw-r--r--modules-available/eventlog/inc/notificationtransport.inc.php279
1 files changed, 279 insertions, 0 deletions
diff --git a/modules-available/eventlog/inc/notificationtransport.inc.php b/modules-available/eventlog/inc/notificationtransport.inc.php
new file mode 100644
index 00000000..499f6371
--- /dev/null
+++ b/modules-available/eventlog/inc/notificationtransport.inc.php
@@ -0,0 +1,279 @@
+<?php
+
+abstract class NotificationTransport
+{
+
+ public static function getInstance(array $data)
+ {
+ switch ($data['type'] ?? '') {
+ case 'mail':
+ return new MailNotificationTransport($data);
+ case 'irc':
+ return new IrcNotificationTransport($data);
+ case 'http':
+ return new HttpNotificationTransport($data);
+ case 'group':
+ return new GroupNotificationTransport($data);
+ }
+ error_log('Invalid Notification Transport: ' . ($data['type'] ?? '(unset)'));
+ return null;
+ }
+
+ public static function newGroup(int ...$ids): GroupNotificationTransport
+ {
+ return new GroupNotificationTransport(['group-list' => $ids]);
+ }
+
+ public abstract function __construct(array $data);
+
+ public abstract function toString(): string;
+
+ public abstract function fire(string $subject, string $message, array $raw): bool;
+
+ public abstract function isValid(): bool;
+
+}
+
+class MailNotificationTransport extends NotificationTransport
+{
+
+ /** @var int */
+ private $mailConfigId;
+
+ /** @var int[] */
+ private $userIds;
+
+ /** @var string */
+ private $extraMails;
+
+ public function __construct(array $data)
+ {
+ $this->mailConfigId = (int)($data['mail-config-id'] ?? 0);
+ $this->userIds = array_map(function ($i) { return (int)$i; }, $data['mail-users'] ?? []);
+ $this->extraMails = (string)($data['mail-extra-mails'] ?? '');
+ }
+
+ public function toString(): string
+ {
+ static $mailList = null;
+ if ($mailList === null) {
+ $mailList = Database::queryIndexedList("SELECT configid, host, senderaddress, replyto
+ FROM mail_config");
+ }
+ $str = 'Via: ' . ($mailList[$this->mailConfigId]['host'] ?? '<none>')
+ . ' as ' . ($mailList[$this->mailConfigId]['senderaddress'] ?? $mailList[$this->mailConfigId]['replyto'] ?? '<none>');
+ if (!empty($this->userIds)) {
+ $str .= ', Users: ' . count($this->userIds);
+ }
+ if (!empty($this->extraMails)) {
+ $str .= ', External: ' . substr_count($this->extraMails, '@');
+ }
+ return $str;
+ }
+
+ public function fire(string $subject, string $message, array $raw): bool
+ {
+ if (!$this->isValid())
+ return false;
+ $addrsOut = [];
+ if (preg_match_all('/[^@\s]+@[^@\s]+/', $this->extraMails, $out)) {
+ $addrsOut = $out[0];
+ }
+ if (!empty($this->userIds)) {
+ $mails = Database::queryColumnArray("SELECT email
+ FROM user
+ WHERE userid IN (:users)",
+ ['users' => $this->userIds]);
+ foreach ($mails as $mail) {
+ if (preg_match('/^[^@\s]+@[^@\s]+$/', $mail)) {
+ $addrsOut[] = $mail;
+ }
+ }
+ }
+ if (empty($addrsOut))
+ return false;
+ Mailer::queue($this->mailConfigId, $addrsOut, $subject, $message);
+ return true;
+ }
+
+ public function isValid(): bool
+ {
+ if ($this->mailConfigId === 0)
+ return false;
+ $mailer = Mailer::instanceFromConfig($this->mailConfigId);
+ return $mailer !== null;
+ }
+}
+
+class IrcNotificationTransport extends NotificationTransport
+{
+
+ private $server;
+
+ private $serverPasswort;
+
+ private $target;
+
+ private $nickName;
+
+ public function __construct(array $data)
+ {
+ $this->server = $data['irc-server'] ?? '';
+ $this->serverPasswort = $data['irc-server-password'] ?? '';
+ $this->target = $data['irc-target'] ?? '';
+ $this->nickName = $data['irc-nickname'] ?? 'BWLP-' . mt_rand(10000, 99999);
+ }
+
+ public function toString(): string
+ {
+ return '(' . $this->server . '), ' . $this->nickName . ' @ ' . $this->target;
+ }
+
+ public function fire(string $subject, string $message, array $raw): bool
+ {
+ if (!$this->isValid())
+ return false;
+ return !Taskmanager::isFailed(Taskmanager::submit('IrcNotification', [
+ 'serverAddress' => $this->server,
+ 'serverPassword' => $this->serverPasswort,
+ 'channel' => $this->target,
+ 'message' => preg_replace('/[\r\n]+\s*/', ' ', $message),
+ 'nickName' => $this->nickName,
+ ]));
+ }
+
+ public function isValid(): bool
+ {
+ return !empty($this->server) && !empty($this->target);
+ }
+}
+
+class HttpNotificationTransport extends NotificationTransport
+{
+
+ /** @var string */
+ private $uri;
+
+ /** @var string */
+ private $method;
+
+ /** @var string */
+ private $postField;
+
+ /** @var string */
+ private $postFormat;
+
+ public function __construct(array $data)
+ {
+ $this->uri = $data['http-uri'] ?? '';
+ $this->method = $data['http-method'] ?? 'POST';
+ $this->postField = $data['http-post-field'] ?? 'message=%TEXT%&subject=%SUBJECT%';
+ $this->postFormat = $data['http-post-format'] ?? 'FORM';
+ }
+
+ public function toString(): string
+ {
+ return $this->uri . ' (' . $this->method . ')';
+ }
+
+ public function fire(string $subject, string $message, array $raw): bool
+ {
+ if (!$this->isValid())
+ return false;
+ $url = str_replace(['%TEXT%', '%SUBJECT%'], [urlencode($message), urlencode($subject)], $this->uri);
+ if ($this->method === 'POST') {
+ switch ($this->postFormat) {
+ case 'FORM':
+ $body = str_replace(['%TEXT%', '%SUBJECT%'], [urlencode($message), urlencode($subject)], $this->postField);
+ $ctype = 'application/x-www-form-urlencoded';
+ break;
+ case 'JSON':
+ $body = str_replace(['%TEXT%', '%SUBJECT%'], [json_encode($message),
+ json_encode($subject)], $this->postField);
+ $ctype = 'application/json';
+ break;
+ default:
+ $out = [];
+ foreach ($raw as $k1 => $a) {
+ foreach ($a as $k2 => $v) {
+ $out["$k1.$k2"] = $v;
+ }
+ }
+ $body = json_encode($out);
+ $ctype = 'application/json';
+ }
+ } else {
+ $body = null;
+ $ctype = null;
+ }
+ return !Taskmanager::isFailed(Taskmanager::submit('HttpRequest', [
+ 'url' => $url,
+ 'postData' => $body,
+ 'contentType' => $ctype,
+ ]));
+ }
+
+ public function isValid(): bool
+ {
+ return !empty($this->uri);
+ }
+}
+
+class GroupNotificationTransport extends NotificationTransport
+{
+
+ /** @var int[] list of contained notification transports */
+ private $list;
+
+ public function __construct(array $data)
+ {
+ $this->list = array_map(function ($i) { return (int)$i; }, $data['group-list'] ?? []);
+ }
+
+ public function toString(): string
+ {
+ static $groupList = null;
+ if ($groupList === null) {
+ $groupList = Database::queryKeyValueList("SELECT transportid, title FROM notification_backend");
+ }
+ $out = array_map(function ($i) use ($groupList) { return $groupList[$i] ?? "#$i"; }, $this->list);
+ return implode(', ', $out);
+ }
+
+ public function fire(string $subject, string $message, array $raw): bool
+ {
+ // This is static, so recursing into groups will keep track of ones we already saw
+ static $done = false;
+ $first = ($done === false);
+ if ($first) { // Non-recursive call, init list
+ $done = [];
+ }
+ $list = array_diff($this->list, $done);
+ if (!empty($list)) {
+ $done = array_merge($done, $list);
+ $res = Database::simpleQuery("SELECT data FROM notification_backend WHERE transportid IN (:ids)",
+ ['ids' => $list]);
+ foreach ($res as $row) {
+ $data = json_decode($row['data'], true);
+ if (is_array($data)) {
+ $inst = NotificationTransport::getInstance($data);
+ if ($inst !== null) {
+ $inst->fire($subject, $message, $raw);
+ }
+ }
+ }
+ }
+ if ($first) {
+ $done = false; // Outer-most call, reset
+ }
+ return true;
+ }
+
+ public function isValid(): bool
+ {
+ // Do we really care about empty groups? They might be pointless, but not really invalid
+ // We could consider groups containing invalid IDs as invalid, but that would mean that we
+ // potentially ignore all the other existing IDs in this group, as it would never fire
+ return true;
+ }
+} \ No newline at end of file