attachment improvements

This commit is contained in:
Dominik Chilla 2018-11-25 00:01:01 +01:00
parent c66758110e
commit bc4796b885
5 changed files with 111 additions and 38 deletions

View File

@ -96,6 +96,8 @@ class QuarMail:
imap_uid = None imap_uid = None
msg_size = None msg_size = None
href = None href = None
attach_count = None
attachments = None
def __init__(self,qm_ref): def __init__(self,qm_ref):
if 'id' not in qm_ref: if 'id' not in qm_ref:
@ -136,6 +138,10 @@ class QuarMail:
self.msg_size = qm_ref['msg_size'] self.msg_size = qm_ref['msg_size']
if 'href' in qm_ref: if 'href' in qm_ref:
self.href = qm_ref['href'] self.href = qm_ref['href']
if 'attach_count' in qm_ref:
self.attach_count = qm_ref['attach_count']
if 'attachments' in qm_ref:
self.attachments = qm_ref['attachments']
class AttachmentException(Exception): class AttachmentException(Exception):

View File

@ -39,9 +39,9 @@ class Gulag:
except IMAPmailboxException as e: except IMAPmailboxException as e:
print(e.message) print(e.message)
continue continue
quarmail_ids = []
attachments = []
for unseen in imap_mb.get_unseen_messages(): for unseen in imap_mb.get_unseen_messages():
quarmail_ids = []
attachments = []
uid = unseen['imap_uid'] uid = unseen['imap_uid']
msg = unseen['msg'] msg = unseen['msg']
msg_size = len(str(msg)) msg_size = len(str(msg))
@ -113,13 +113,14 @@ class Gulag:
attachments.append(attach_id) attachments.append(attach_id)
# Ende if part.get_filename() # Ende if part.get_filename()
# Ende for msg.walk() # Ende for msg.walk()
# QuarMails und Attachments verknüpfen
if(len(attachments) > 0):
for quarmail_id in quarmail_ids:
for attachment_id in attachments:
self.db.quarmail2attachment(str(quarmail_id), str(attachment_id))
# Ende for(unseen) # Ende for(unseen)
imap_mb.close() imap_mb.close()
# QuarMails und Attachments verknüpfen
if(len(attachments) > 0):
for quarmail_id in quarmail_ids:
for attachment_id in attachments:
self.db.quarmail2attachment(str(quarmail_id), str(attachment_id))
# Ende for get_mailboxes # Ende for get_mailboxes
def cleanup_quarmails(self): def cleanup_quarmails(self):
@ -144,13 +145,12 @@ class Gulag:
def get_quarmail(self,args): def get_quarmail(self,args):
qm_db = None qm_db = None
try: try:
qm_db = self.db.get_quarmail({ qm_db = self.db.get_quarmail({"id": args['id']})
"id": args['id']
})
except GulagDBException as e: except GulagDBException as e:
raise GulagException("GulagDBException: " + e.message) from e raise GulagException("GulagDBException: " + e.message) from e
if 'rfc822_message' not in args: if 'rfc822_message' not in args:
return qm_db return qm_db
# pull full RFC822 message from mailbox
mailbox = None mailbox = None
try: try:
mailbox = self.db.get_mailbox(qm_db['mailbox_id']) mailbox = self.db.get_mailbox(qm_db['mailbox_id'])
@ -164,6 +164,14 @@ class Gulag:
except IMAPmailboxException as e: except IMAPmailboxException as e:
print(e.message) print(e.message)
raise GulagException(e.message) from e raise GulagException(e.message) from e
def get_attachment(self,args):
at_db = None
try:
at_db = self.db.get_attachment({"id": args['id']})
return at_db
except GulagDBException as e:
raise GulagException(e.message) from e
def rspamd_http2imap(self,mailbox_id): def rspamd_http2imap(self,mailbox_id):
mailbox = None mailbox = None
@ -171,10 +179,12 @@ class Gulag:
mailbox = self.db.get_mailbox(mailbox_id) mailbox = self.db.get_mailbox(mailbox_id)
except GulagDBException as e: except GulagDBException as e:
raise GulagException(e.message) from e raise GulagException(e.message) from e
# check if the request comes really from rspamd´s metadata_exporter
# default metadata_header prefix 'X-Rspamd' will be expected
if(request.headers.get('X-Rspamd-From') == None): if(request.headers.get('X-Rspamd-From') == None):
raise GulagException("Missing Rspamd-specific headers (e.g. X-Rspamd-From)!") raise GulagException("Missing Rspamd-specific headers (e.g. X-Rspamd-From)!")
# recompose rejected mail that will be sent to quarantine mailbox # Prepend gulag-specific headers to rejected mail
# before pushing into quarantine mailbox
msg = None msg = None
try: try:
rcpts_hdr = "" rcpts_hdr = ""
@ -184,35 +194,33 @@ class Gulag:
else: else:
rcpts_hdr = rcpt rcpts_hdr = rcpt
msg = "Return-Path: <" + request.headers.get('X-Rspamd-From') + ">\r\n" msg = "Return-Path: <" + request.headers.get('X-Rspamd-From') + ">\r\n"
msg = msg + "Received: from rspamd_http2imap relay by gulag-mailbox " + mailbox_id + "\r\n" msg += "Received: from rspamd_http2imap relay by gulag-mailbox " + mailbox_id + "\r\n"
msg = msg + "X-Envelope-To-Blocked: " + rcpts_hdr + "\r\n" msg += "X-Envelope-To-Blocked: " + rcpts_hdr + "\r\n"
msg = msg + "X-Spam-Status: " + request.headers.get('X-Rspamd-Symbols') + "\r\n" msg += "X-Spam-Status: " + request.headers.get('X-Rspamd-Symbols') + "\r\n"
msg = msg + "X-Spam-QID: " + request.headers.get('X-Rspamd-Qid') + "\r\n" msg += "X-Spam-QID: " + request.headers.get('X-Rspamd-Qid') + "\r\n"
msg = msg + request.get_data(as_text=True) # append original mail
#FIXME: except email.errors.* as e: msg += request.get_data(as_text=True)
except: except:
raise GulagException(str(sys.exc_info())) raise GulagException(str(sys.exc_info()))
# Use IMAP´s APPEND command to store the message into mailbox
imap_mb = None imap_mb = None
try: try:
imap_mb = IMAPmailbox(mailbox) imap_mb = IMAPmailbox(mailbox)
imap_mb.append_message(msg) imap_mb.append_message(msg)
except IMAPmailboxException as e: except IMAPmailboxException as e:
raise GulagException(e.message) from e raise GulagException(e.message) from e
# def send_mail(self,args):
# try: # try:
# if not mailbox['smtp_server']:
# raise GulagException("No SMTP server configured for mailbox " + mailbox_id)
# # FIXME: SMTP tranaport security and authentication! # # FIXME: SMTP tranaport security and authentication!
# with SMTP(host=mailbox['smtp_server'],port=mailbox['smtp_port']) as smtp: # # with SMTP(host=mailbox['smtp_server'],port=mailbox['smtp_port']) as smtp:
# try: # # try:
# smtp.sendmail( # # smtp.sendmail(
# request.headers.get('X-Rspamd-From'), # # request.headers.get('X-Rspamd-From'),
# mailbox_id, # # mailbox_id,
# msg # # msg
# ) # # )
# except (SMTPRecipientsRefused,SMTPHeloError,SMTPSenderRefused,SMTPDataError) as e: # # except (SMTPRecipientsRefused,SMTPHeloError,SMTPSenderRefused,SMTPDataError) as e:
# raise GulagException(str(e)) from e # # raise GulagException(str(e)) from e
# except TimeoutError as e: # except TimeoutError as e:
# raise GulagException(str(e)) from e # raise GulagException(str(e)) from e

View File

@ -10,7 +10,6 @@ class GulagDB:
conn = None conn = None
uri_prefixes = None uri_prefixes = None
# def __init__(self, server, user, password, name, uri_prefixes):
def __init__(self, args, uri_prefixes): def __init__(self, args, uri_prefixes):
try: try:
if 'unix_socket' in args: if 'unix_socket' in args:
@ -114,7 +113,11 @@ class GulagDB:
def get_quarmails(self): def get_quarmails(self):
try: try:
cursor = self.conn.cursor() cursor = self.conn.cursor()
cursor.execute("select * from QuarMails;") # cursor.execute("select * from QuarMails;")
query = "select *,(select count(*) from QuarMail2Attachment"
query += " where QuarMails.id=QuarMail2Attachment.quarmail_id) as attach_count"
query += " from QuarMails;"
cursor.execute(query)
results = [] results = []
data = cursor.fetchall() data = cursor.fetchall()
if not data: if not data:
@ -138,7 +141,10 @@ class GulagDB:
try: try:
cursor = self.conn.cursor() cursor = self.conn.cursor()
# TODO: build SQL query by args # TODO: build SQL query by args
query = "select * from QuarMails where id='" + args['id'] + "';" #query = "select * from QuarMails where id='" + args['id'] + "';"
query = "select *,(select count(*) from QuarMail2Attachment"
query += " where QuarMails.id=QuarMail2Attachment.quarmail_id) as attach_count"
query += " from QuarMails where QuarMails.id="+ str(args['id']) +";"
cursor.execute(query) cursor.execute(query)
data = cursor.fetchall() data = cursor.fetchall()
if not data: if not data:
@ -153,6 +159,10 @@ class GulagDB:
else: else:
dict[name[0]] = value dict[name[0]] = value
dict['href'] = self.uri_prefixes['quarmails'] + str(dict['id']) dict['href'] = self.uri_prefixes['quarmails'] + str(dict['id'])
try:
dict['attachments'] = self.get_attachments_by_quarmail(args['id'])
except GulagDBException as e:
pass
return QuarMail(dict).__dict__ return QuarMail(dict).__dict__
except mariadb.Error as e: except mariadb.Error as e:
raise GulagDBException(e) from e raise GulagDBException(e) from e
@ -160,7 +170,8 @@ class GulagDB:
def get_deprecated_mails(self,retention_period): def get_deprecated_mails(self,retention_period):
try: try:
cursor = self.conn.cursor() cursor = self.conn.cursor()
query = "select ctime,mailbox_id,imap_uid from QuarMails where ctime < date_sub(NOW(), INTERVAL "+ retention_period +");" query = "select ctime,mailbox_id,imap_uid from QuarMails"
query += " where ctime < date_sub(NOW(), INTERVAL "+ str(retention_period) +");"
cursor.execute(query) cursor.execute(query)
results = [] results = []
data = cursor.fetchall() data = cursor.fetchall()
@ -186,7 +197,47 @@ class GulagDB:
return cursor.lastrowid return cursor.lastrowid
except mariadb.Error as e: except mariadb.Error as e:
raise GulagDBException(e) from e raise GulagDBException(e) from e
def get_attachment(self, args):
try:
cursor = self.conn.cursor()
cursor.execute("select * from Attachments where id=" + str(args['id']) + ";")
data = cursor.fetchall()
if not data:
raise GulagDBException("Attachment("+ str(args['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['attachments'] + str(dict['id'])
return Attachment(dict).__dict__
except mariadb.Error as e:
raise GulagDBException(e) from e
def get_attachments_by_quarmail(self,quarmail_id):
try:
query = "select Attachments.* from QuarMail2Attachment"
query += " left join QuarMails ON QuarMails.id = QuarMail2Attachment.quarmail_id"
query += " left join Attachments ON Attachments.id = QuarMail2Attachment.attachment_id"
query += " where QuarMails.id = " + str(quarmail_id) + ";"
cursor = self.conn.cursor()
cursor.execute(query)
results = []
data = cursor.fetchall()
if not data:
raise GulagDBException("QuarMail("+ str(quarmail_id) +") has no attachments!")
desc = cursor.description
for tuple in data:
dict = {}
for (name, value) in zip(desc, tuple):
dict[name[0]] = value
dict['href'] = self.uri_prefixes['attachments'] + str(dict['id'])
results.append(Attachment(dict).__dict__)
return results
except mariadb.Error as e:
raise GulagDBException(e) from e
def quarmail2attachment(self,quarmail_id,attachment_id): def quarmail2attachment(self,quarmail_id,attachment_id):
try: try:
cursor = self.conn.cursor() cursor = self.conn.cursor()

View File

@ -65,7 +65,11 @@ class ResAttachments(GulagResource):
class ResAttachment(GulagResource): class ResAttachment(GulagResource):
def get(self,id): def get(self,id):
return {"resource": "Attachment by ID"} args = {"id": id}
try:
return self.gulag.get_attachment(args)
except GulagException as e:
abort(400, message=e.message)
class ResRSPAMDImporter(GulagResource): class ResRSPAMDImporter(GulagResource):
def post(self,mailbox_id): def post(self,mailbox_id):

View File

@ -6,7 +6,7 @@ from flask_restful import Api
from Gulag import Gulag,GulagException from Gulag import Gulag,GulagException
from Resources import (ResRoot,ResMailboxes, from Resources import (ResRoot,ResMailboxes,
ResQuarMails,ResQuarMail,ResAttachments, ResQuarMails,ResQuarMail,ResAttachments,
ResRSPAMDImporter ResAttachment,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")
@ -39,6 +39,10 @@ try:
'/api/v1/attachments/', '/api/v1/attachments/',
resource_class_kwargs={'gulag_object': gulag} resource_class_kwargs={'gulag_object': gulag}
) )
api.add_resource(ResAttachment,
'/api/v1/attachments/<string:id>',
resource_class_kwargs={'gulag_object': gulag}
)
api.add_resource(ResRSPAMDImporter, api.add_resource(ResRSPAMDImporter,
'/api/v1/mailboxes/<string:mailbox_id>/rspamdimporter/', '/api/v1/mailboxes/<string:mailbox_id>/rspamdimporter/',
resource_class_kwargs={'gulag_object': gulag} resource_class_kwargs={'gulag_object': gulag}