HTTP2SMTP relay for Rspamd :)

This commit is contained in:
Dominik Chilla 2018-11-18 23:56:38 +01:00
parent 6c23eda022
commit 71d6b0b200
6 changed files with 103 additions and 6 deletions

View File

@ -9,12 +9,18 @@ class Mailbox:
email_address = None email_address = None
name = None name = None
imap_server = None imap_server = None
imap_port = None
imap_security = None imap_security = None
imap_user = None imap_user = None
imap_pass = None imap_pass = None
imap_mailbox = None imap_mailbox = None
imap_mailbox_fp = None imap_mailbox_fp = None
imap_separator = None imap_separator = None
smtp_server = None
smtp_port = None
smtp_security = None
smtp_user = None
smtp_pass = None
comment = None comment = None
href = None href = None
@ -37,6 +43,8 @@ class Mailbox:
) )
else: else:
raise MailboxException("'imap_security' is a mandatory!") raise MailboxException("'imap_security' is a mandatory!")
if 'imap_port' in mb_ref:
self.imap_port = mb_ref['imap_port']
if 'imap_user' not in mb_ref: if 'imap_user' not in mb_ref:
raise MailboxException("'imap_user' is mandatory!") raise MailboxException("'imap_user' is mandatory!")
self.imap_user = mb_ref['imap_user'] self.imap_user = mb_ref['imap_user']
@ -52,6 +60,16 @@ class Mailbox:
if 'imap_separator' not in mb_ref: if 'imap_separator' not in mb_ref:
raise MailboxException("'imap_separator' is mandatory!") raise MailboxException("'imap_separator' is mandatory!")
self.imap_seperator = mb_ref['imap_separator'] self.imap_seperator = mb_ref['imap_separator']
if 'smtp_server' in mb_ref:
self.smtp_server = mb_ref['smtp_server']
if 'smtp_port' in mb_ref:
self.smtp_port = mb_ref['smtp_port']
if 'smtp_security' in mb_ref:
self.smtp_security = mb_ref['smtp_security']
if 'smtp_user' in mb_ref:
self.smtp_user = mb_ref['smtp_user']
if 'smtp_pass' in mb_ref:
self.smtp_pass = mb_ref['smtp_pass']
if 'comment' in mb_ref: if 'comment' in mb_ref:
self.comment = mb_ref['comment'] self.comment = mb_ref['comment']
if 'href' in mb_ref: if 'href' in mb_ref:

View File

@ -1,5 +1,7 @@
import json,sys import json,sys
import email,email.header import email,email.header,email.message
from flask import request
from smtplib import SMTP
from GulagDB import GulagDB,GulagDBException from GulagDB import GulagDB,GulagDBException
from GulagMailbox import IMAPmailbox,IMAPmailboxException from GulagMailbox import IMAPmailbox,IMAPmailboxException
@ -140,3 +142,40 @@ class Gulag:
except GulagDBException as e: except GulagDBException as e:
raise GulagException("GulagDBException: " + e.message) from e raise GulagException("GulagDBException: " + e.message) from e
def rspamd_http2smtp(self,mailbox_id):
mailbox = None
try:
mailbox = self.db.get_mailbox(mailbox_id)
except GulagDBException as e:
raise GulagException(e.message) from e
if(request.headers.get('X-Rspamd-From') == None):
raise GulagException("Missing Rspamd-specific headers (e.g. X-Rspamd-From)!")
# recompose rejected mail that will be sent to quarantine mailbox
#FIXME: print("mx_queue_id: " + request.headers.get('X-Rspamd-Qid'))
msg = None
try:
msg = email.message_from_string(request.get_data(as_text=True))
rcpts_hdr = str(request.headers.get('X-Rspamd-Rcpt'))
# FIXME: special chars []" rausstrippen! -> JSON!!!
print("RCPTs neu: " + rcpts_hdr)
msg.add_header("X-Envelope-To-Blocked", rcpts_hdr)
msg.add_header("X-Spam-Status", request.headers.get('X-Rspamd-Symbols'))
# except email.errors.* as e:
except:
raise GulagException(str(sys.exc_info()))
try:
with SMTP(host=mailbox['smtp_server'],port=mailbox['smtp_port']) as smtp:
try:
smtp.sendmail(
request.headers.get('X-Rspamd-From'),
mailbox_id,
msg.as_string()
)
except (SMTPRecipientsRefused,SMTPHeloError,SMTPSenderRefused,SMTPDataError) as e:
raise GulagException(str(e)) from e
except TimeoutError as e:
raise GulagException(str(e)) from e

View File

@ -49,6 +49,28 @@ class GulagDB:
except mariadb.Error as e: except mariadb.Error as e:
raise GulagDBException(e) from e raise GulagDBException(e) from e
def get_mailbox(self,mailbox_id):
try:
cursor = self.conn.cursor()
cursor.execute(
"select * from Mailboxes where email_address='" + mailbox_id + "' limit 1;"
)
data = cursor.fetchall()
if data == None:
raise GulagDBException("Mailbox '" + mailbox_id + "' does not exist!")
desc = cursor.description
tuple = data[0]
dict = {}
for (name, value) in zip(desc, tuple):
dict[name[0]] = value
dict['href'] = self.uri_prefixes['mailboxes'] + dict['email_address']
try:
return Mailbox(dict).__dict__
except MailboxException as e:
raise GulagDBException(e.message) from e
except mariadb.Error as e:
raise GulagDBException(e) from e
def add_quarmail(self, quarmail): def add_quarmail(self, quarmail):
try: try:
cursor = self.conn.cursor() cursor = self.conn.cursor()
@ -121,7 +143,7 @@ class GulagDB:
dict = {} dict = {}
for (name, value) in zip(desc, tuple): for (name, value) in zip(desc, tuple):
dict[name[0]] = value dict[name[0]] = value
results.append(QuarMail(dict).__dict__) results.append(dict)
return results return results
except mariadb.Error as e: except mariadb.Error as e:
raise GulagDBException(e) from e raise GulagDBException(e) from e

View File

@ -1,6 +1,5 @@
from flask import request #from flask import request
from flask_restful import Resource, abort from flask_restful import Resource, abort, reqparse
from Entities import Mailbox,MailboxException,QuarMail,QuarMailException,Attachment,AttachmentException
from Gulag import GulagException from Gulag import GulagException
class GulagResource(Resource): class GulagResource(Resource):
@ -62,3 +61,12 @@ class ResAttachment(GulagResource):
def get(self,id): def get(self,id):
return {"resource": "Attachment by ID"} return {"resource": "Attachment by ID"}
class ResRSPAMDImporter(GulagResource):
def post(self,mailbox_id):
try:
self.gulag.rspamd_http2smtp(mailbox_id)
# TODO: Response mit Location-Header?
return {"resource: ": "HTTP2SMTP for RSPAMD"}
except GulagException as e:
abort(400, message=e.message)

View File

@ -4,7 +4,7 @@ import argparse,sys
from flask import Flask from flask import Flask
from flask_restful import Api from flask_restful import Api
from Gulag import Gulag,GulagException from Gulag import Gulag,GulagException
from Resources import ResRoot,ResMailboxes,ResQuarMails,ResAttachments from Resources import ResRoot,ResMailboxes,ResQuarMails,ResAttachments,ResRSPAMDImporter
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('--config', required=True, help="Path to config file") parser.add_argument('--config', required=True, help="Path to config file")
@ -33,6 +33,10 @@ try:
'/api/v1/attachments/', '/api/v1/attachments/',
resource_class_kwargs={'gulag_object': gulag} resource_class_kwargs={'gulag_object': gulag}
) )
api.add_resource(ResRSPAMDImporter,
'/api/v1/mailboxes/<string:mailbox_id>/rspamdimporter/',
resource_class_kwargs={'gulag_object': gulag}
)
if __name__ == '__main__': if __name__ == '__main__':
app.run(debug=False, app.run(debug=False,
# will be overriden by uwsgi.ini # will be overriden by uwsgi.ini

View File

@ -6,12 +6,18 @@ create table Mailboxes(
email_address varchar(767) not null primary key collate 'ascii_general_ci', email_address varchar(767) not null primary key collate 'ascii_general_ci',
name varchar(256) not null, name varchar(256) not null,
imap_server varchar(256) not null default '127.0.0.1', imap_server varchar(256) not null default '127.0.0.1',
imap_port smallint unsigned not null default 143,
imap_security varchar(32) not null default 'plain', imap_security varchar(32) not null default 'plain',
imap_user varchar(256) not null, imap_user varchar(256) not null,
imap_pass varchar(256) not null, imap_pass varchar(256) not null,
imap_mailbox varchar(256) not null default 'INBOX', imap_mailbox varchar(256) not null default 'INBOX',
imap_mailbox_fp varchar(256) not null default 'false-positives', imap_mailbox_fp varchar(256) not null default 'false-positives',
imap_separator varchar(4) not null default '/', imap_separator varchar(4) not null default '/',
smtp_server varchar(256) default null,
smtp_port smallint unsigned not null default 25,
smtp_security varchar(32) not null default 'plain',
smtp_user varchar(256) default null,
smtp_pass varchar(2048) default null,
comment varchar(256) default null comment varchar(256) default null
)ENGINE = InnoDB; )ENGINE = InnoDB;
insert into Mailboxes (email_address,name,imap_user,imap_pass) insert into Mailboxes (email_address,name,imap_user,imap_pass)