[Dovecot] Quota notifications: Allow to send to external address (BCC via mailcow UI)

master
andryyy 2021-05-23 09:49:36 +02:00
parent 9c075af2d9
commit beda649ecf
No known key found for this signature in database
GPG Key ID: 8EC34FF2794E25EF
6 changed files with 36 additions and 8 deletions

View File

@ -9,6 +9,7 @@ import jinja2
from jinja2 import Template from jinja2 import Template
import redis import redis
import time import time
import json
import sys import sys
import html2text import html2text
from subprocess import Popen, PIPE, STDOUT from subprocess import Popen, PIPE, STDOUT
@ -57,6 +58,27 @@ try:
p = Popen(['/usr/lib/dovecot/dovecot-lda', '-d', username, '-o', '"plugin/quota=maildir:User quota:noenforcing"'], stdout=PIPE, stdin=PIPE, stderr=STDOUT) p = Popen(['/usr/lib/dovecot/dovecot-lda', '-d', username, '-o', '"plugin/quota=maildir:User quota:noenforcing"'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)
p.communicate(input=bytes(msg.as_string(), 'utf-8')) p.communicate(input=bytes(msg.as_string(), 'utf-8'))
domain = username.split("@")[-1]
if domain and r.hget('QW_BCC', domain):
bcc_data = json.loads(r.hget('QW_BCC', domain))
bcc_rcpts = bcc_data['bcc_rcpts']
if bcc_data['active'] == 1:
for rcpt in bcc_rcpts:
msg = MIMEMultipart('alternative')
msg['From'] = username
subject = r.get('QW_SUBJ') or "Quota warning"
msg['Subject'] = subject + ' (' + username + ')'
msg['Date'] = formatdate(localtime = True)
text_part = MIMEText(text, 'plain', 'utf-8')
html_part = MIMEText(html, 'html', 'utf-8')
msg.attach(text_part)
msg.attach(html_part)
msg['To'] = rcpt
server = smtplib.SMTP('postfix', 588, 'quarantine')
server.ehlo()
server.sendmail(msg['From'], str(rcpt), msg.as_string())
server.quit()
except Exception as ex: except Exception as ex:
print('Failed to send quota notification: %s' % (ex)) print('Failed to send quota notification: %s' % (ex))
sys.exit(1) sys.exit(1)
@ -69,4 +91,4 @@ except:
try: try:
sys.stderr.close() sys.stderr.close()
except: except:
pass pass

View File

@ -104,7 +104,7 @@ table.footable > tbody > tr.footable-empty > th {
} }
.fooicon { .fooicon {
position: relative; position: relative;
top: 1px; top: 0px;
display: inline-block; display: inline-block;
font-family: "bootstrap-icons" !important; font-family: "bootstrap-icons" !important;
font-style: normal; font-style: normal;
@ -123,10 +123,10 @@ table.footable > tbody > tr.footable-empty > th {
content: "\f130"; content: "\f130";
} }
.fooicon-plus:before { .fooicon-plus:before {
content: "\f64d"; content: "\f4fc";
} }
.fooicon-minus:before { .fooicon-minus:before {
content: "\f63b"; content: "\f2e8";
} }
.fooicon-search:before { .fooicon-search:before {
content: "\f52a"; content: "\f52a";

View File

@ -230,3 +230,9 @@ table.footable>tbody>tr.footable-empty>td {
font-style:italic; font-style:italic;
font-size: 1rem; font-size: 1rem;
} }
.navbar-nav > li {
font-size: 1rem !important;
}
.dropdown-menu > li > a {
font-size: 1rem !important;
}

View File

@ -83,7 +83,7 @@ $xmpp_status = xmpp_control('status');
<p><?=$lang['debug']['started_at'];?>: <span class="parse_date"><?=$solr_status['status']['dovecot-fts']['startTime'];?></span></p> <p><?=$lang['debug']['started_at'];?>: <span class="parse_date"><?=$solr_status['status']['dovecot-fts']['startTime'];?></span></p>
<p><?=$lang['debug']['last_modified'];?>: <span class="parse_date"><?=$solr_status['status']['dovecot-fts']['index']['lastModified'];?></span></p> <p><?=$lang['debug']['last_modified'];?>: <span class="parse_date"><?=$solr_status['status']['dovecot-fts']['index']['lastModified'];?></span></p>
<p><?=$lang['debug']['size'];?>: <?=$solr_status['status']['dovecot-fts']['index']['size'];?></p> <p><?=$lang['debug']['size'];?>: <?=$solr_status['status']['dovecot-fts']['index']['size'];?></p>
<p><?=$lang['debug']['docs'];?>: <?=$solr_status['status']['dovecot-fts']['index']['numDocs'];?></p> <p><i class="bi bi-file-text"></i> <?=$lang['debug']['docs'];?>: <?=$solr_status['status']['dovecot-fts']['index']['numDocs'];?></p>
<?php <?php
else: else:
?> ?>

View File

@ -127,9 +127,9 @@
</ul> </ul>
</li> </li>
<?php } if (!isset($_SESSION['dual-login']) && isset($_SESSION['mailcow_cc_username'])) { ?> <?php } if (!isset($_SESSION['dual-login']) && isset($_SESSION['mailcow_cc_username'])) { ?>
<li class="logged-in-as"><a href="#" onclick="logout.submit()"><b class="username-lia"><?= htmlspecialchars($_SESSION['mailcow_cc_username']); ?></b> <i class="bi bi-door-open"></i></a></li> <li class="logged-in-as"><a href="#" onclick="logout.submit()"><b class="username-lia"><?= htmlspecialchars($_SESSION['mailcow_cc_username']); ?></b> <i class="bi bi-x-circle"></i></a></li>
<?php } elseif (isset($_SESSION['dual-login'])) { ?> <?php } elseif (isset($_SESSION['dual-login'])) { ?>
<li class="logged-in-as"><a href="#" onclick="logout.submit()"><b class="username-lia"><?= htmlspecialchars($_SESSION['mailcow_cc_username']); ?> <span class="text-info">(<?= htmlspecialchars($_SESSION['dual-login']['username']); ?>)</span> </b><i class="bi bi-door-open"></i></a></li> <li class="logged-in-as"><a href="#" onclick="logout.submit()"><b class="username-lia"><?= htmlspecialchars($_SESSION['mailcow_cc_username']); ?> <span class="text-info">(<?= htmlspecialchars($_SESSION['dual-login']['username']); ?>)</span> </b><i class="bi bi-x-circle"></i></a></li>
<?php } if (!preg_match('/y|yes/i', getenv('MASTER'))) { ?> <?php } if (!preg_match('/y|yes/i', getenv('MASTER'))) { ?>
<li class="text-warning slave-info">[ slave ]</li> <li class="text-warning slave-info">[ slave ]</li>
<?php } ?> <?php } ?>

View File

@ -291,7 +291,7 @@ jQuery(function($){
else { else {
item.action += '<a href="/edit/domain/' + encodeURIComponent(item.domain_name) + '" class="btn btn-xs btn-default"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>'; item.action += '<a href="/edit/domain/' + encodeURIComponent(item.domain_name) + '" class="btn btn-xs btn-default"><i class="bi bi-pencil-fill"></i> ' + lang.edit + '</a>';
} }
item.action += '<a href="#dnsInfoModal" class="btn btn-xs btn-info" data-toggle="modal" data-domain="' + encodeURIComponent(item.domain_name) + '"><i class="bi bi-question-lg"></i> DNS</a></div>'; item.action += '<a href="#dnsInfoModal" class="btn btn-xs btn-info" data-toggle="modal" data-domain="' + encodeURIComponent(item.domain_name) + '"><i class="bi bi-globe2"></i> DNS</a></div>';
if (item.backupmx == 1) { if (item.backupmx == 1) {
if (item.relay_unknown_only == 1) { if (item.relay_unknown_only == 1) {
item.domain_name = '<div class="label label-info">Relay Non-Local</div> ' + item.domain_name; item.domain_name = '<div class="label label-info">Relay Non-Local</div> ' + item.domain_name;