diff --git a/data/Dockerfiles/dovecot/quarantine_notify.py b/data/Dockerfiles/dovecot/quarantine_notify.py index a681c1fda..5757cdf53 100755 --- a/data/Dockerfiles/dovecot/quarantine_notify.py +++ b/data/Dockerfiles/dovecot/quarantine_notify.py @@ -47,7 +47,7 @@ try: if max_score == "": max_score = 9999.0 - def query_mysql(query, headers = True, update = False): + def query_mysql(query, params = None, headers = True, update = False): while True: try: cnx = MySQLdb.connect(user=os.environ.get('DBUSER'), password=os.environ.get('DBPASS'), database=os.environ.get('DBNAME'), charset="utf8mb4", collation="utf8mb4_general_ci") @@ -57,7 +57,10 @@ try: else: break cur = cnx.cursor() - cur.execute(query) + if params: + cur.execute(query, params) + else: + cur.execute(query) if not update: result = [] columns = tuple( [d[0] for d in cur.description] ) @@ -76,7 +79,7 @@ try: def notify_rcpt(rcpt, msg_count, quarantine_acl, category): if category == "add_header": category = "add header" - meta_query = query_mysql('SELECT `qhash`, id, subject, score, sender, created, action FROM quarantine WHERE notified = 0 AND rcpt = "%s" AND score < %f AND (action = "%s" OR "all" = "%s")' % (rcpt, max_score, category, category)) + meta_query = query_mysql('SELECT `qhash`, id, subject, score, sender, created, action FROM quarantine WHERE notified = 0 AND rcpt = %s AND score < %s AND (action = %s OR "all" = %s)', (rcpt, max_score, category, category)) print("%s: %d of %d messages qualify for notification" % (rcpt, len(meta_query), msg_count)) if len(meta_query) == 0: return @@ -130,7 +133,7 @@ try: server.sendmail(msg['From'], [str(redirect)] + [str(bcc)], text) server.quit() for res in meta_query: - query_mysql('UPDATE quarantine SET notified = 1 WHERE id = "%d"' % (res['id']), update = True) + query_mysql('UPDATE quarantine SET notified = 1 WHERE id = %s', (res['id'],), update = True) r.hset('Q_LAST_NOTIFIED', record['rcpt'], time_now) break except Exception as ex: @@ -138,7 +141,7 @@ try: print('%s' % (ex)) time.sleep(3) - records = query_mysql('SELECT IFNULL(user_acl.quarantine, 0) AS quarantine_acl, count(id) AS counter, rcpt FROM quarantine LEFT OUTER JOIN user_acl ON user_acl.username = rcpt WHERE notified = 0 AND score < %f AND rcpt in (SELECT username FROM mailbox) GROUP BY rcpt' % (max_score)) + records = query_mysql('SELECT IFNULL(user_acl.quarantine, 0) AS quarantine_acl, count(id) AS counter, rcpt FROM quarantine LEFT OUTER JOIN user_acl ON user_acl.username = rcpt WHERE notified = 0 AND score < %s AND rcpt in (SELECT username FROM mailbox) GROUP BY rcpt', (max_score,)) for record in records: attrs = '' @@ -156,7 +159,7 @@ try: except Exception as ex: print('Could not determine last notification for %s, assuming never' % (record['rcpt'])) last_notification = 0 - attrs_json = query_mysql('SELECT attributes FROM mailbox WHERE username = "%s"' % (record['rcpt'])) + attrs_json = query_mysql('SELECT attributes FROM mailbox WHERE username = %s', (record['rcpt'],)) attrs = attrs_json[0]['attributes'] if isinstance(attrs, str): # if attr is str then just load it diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php index 496421634..adb330ea8 100644 --- a/data/web/inc/functions.mailbox.inc.php +++ b/data/web/inc/functions.mailbox.inc.php @@ -1111,6 +1111,15 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { $relayhost = (isset($_data['relayhost'])) ? intval($_data['relayhost']) : 0; $quarantine_notification = (isset($_data['quarantine_notification'])) ? strval($_data['quarantine_notification']) : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_notification']); $quarantine_category = (isset($_data['quarantine_category'])) ? strval($_data['quarantine_category']) : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_category']); + // Validate quarantine_category + if (!in_array($quarantine_category, array('add_header', 'reject', 'all'))) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), + 'msg' => 'quarantine_category_invalid' + ); + return false; + } $quota_b = ($quota_m * 1048576); $attribute_hash = (!empty($_data['attribute_hash'])) ? $_data['attribute_hash'] : ''; if (in_array($authsource, array('keycloak', 'generic-oidc', 'ldap'))){ @@ -1733,6 +1742,15 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { $attr["tagged_mail_handler"] = (!empty($_data['tagged_mail_handler'])) ? $_data['tagged_mail_handler'] : strval($MAILBOX_DEFAULT_ATTRIBUTES['tagged_mail_handler']); $attr["quarantine_notification"] = (!empty($_data['quarantine_notification'])) ? $_data['quarantine_notification'] : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_notification']); $attr["quarantine_category"] = (!empty($_data['quarantine_category'])) ? $_data['quarantine_category'] : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_category']); + // Validate quarantine_category + if (!in_array($attr["quarantine_category"], array('add_header', 'reject', 'all'))) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_extra), + 'msg' => 'quarantine_category_invalid' + ); + return false; + } $attr["rl_frame"] = (!empty($_data['rl_frame'])) ? $_data['rl_frame'] : "s"; $attr["rl_value"] = (!empty($_data['rl_value'])) ? $_data['rl_value'] : ""; $attr["force_pw_update"] = isset($_data['force_pw_update']) ? intval($_data['force_pw_update']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['force_pw_update']); @@ -3798,6 +3816,15 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { $attr["tagged_mail_handler"] = (!empty($_data['tagged_mail_handler'])) ? $_data['tagged_mail_handler'] : $is_now['tagged_mail_handler']; $attr["quarantine_notification"] = (!empty($_data['quarantine_notification'])) ? $_data['quarantine_notification'] : $is_now['quarantine_notification']; $attr["quarantine_category"] = (!empty($_data['quarantine_category'])) ? $_data['quarantine_category'] : $is_now['quarantine_category']; + // Validate quarantine_category + if (!in_array($attr["quarantine_category"], array('add_header', 'reject', 'all'))) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_extra), + 'msg' => 'quarantine_category_invalid' + ); + continue; + } $attr["rl_frame"] = (!empty($_data['rl_frame'])) ? $_data['rl_frame'] : $is_now['rl_frame']; $attr["rl_value"] = (!empty($_data['rl_value'])) ? $_data['rl_value'] : $is_now['rl_value']; $attr["force_pw_update"] = isset($_data['force_pw_update']) ? intval($_data['force_pw_update']) : $is_now['force_pw_update']; diff --git a/data/web/lang/lang.de-de.json b/data/web/lang/lang.de-de.json index 487300fe9..3672762df 100644 --- a/data/web/lang/lang.de-de.json +++ b/data/web/lang/lang.de-de.json @@ -512,6 +512,7 @@ "pushover_credentials_missing": "Pushover Token und/oder Key fehlen", "pushover_key": "Pushover Key hat das falsche Format", "pushover_token": "Pushover Token hat das falsche Format", + "quarantine_category_invalid": "Quarantäne-Kategorie muss eine der folgenden sein: add_header, reject, all", "quota_not_0_not_numeric": "Speicherplatz muss numerisch und >= 0 sein", "recipient_map_entry_exists": "Eine Empfängerumschreibung für Objekt \"%s\" existiert bereits", "recovery_email_failed": "E-Mail zur Wiederherstellung konnte nicht gesendet werden. Bitte wenden Sie sich an Ihren Administrator.", diff --git a/data/web/lang/lang.en-gb.json b/data/web/lang/lang.en-gb.json index eb26e3fcf..e786bcbe5 100644 --- a/data/web/lang/lang.en-gb.json +++ b/data/web/lang/lang.en-gb.json @@ -513,6 +513,7 @@ "pushover_credentials_missing": "Pushover token and or key missing", "pushover_key": "Pushover key has a wrong format", "pushover_token": "Pushover token has a wrong format", + "quarantine_category_invalid": "Quarantine category must be one of: add_header, reject, all", "quota_not_0_not_numeric": "Quota must be numeric and >= 0", "recipient_map_entry_exists": "A Recipient map entry \"%s\" exists", "recovery_email_failed": "Could not send a recovery email. Please contact your administrator.", diff --git a/docker-compose.yml b/docker-compose.yml index edddc6558..0a9578de6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -252,7 +252,7 @@ services: - sogo dovecot-mailcow: - image: ghcr.io/mailcow/dovecot:2.3.21.1-1 + image: ghcr.io/mailcow/dovecot:2.3.21.1-2 depends_on: - mysql-mailcow - netfilter-mailcow