resource routing

This commit is contained in:
Dominik Chilla 2018-11-18 00:52:37 +01:00
parent e1dcba19c1
commit 6c23eda022
7 changed files with 216 additions and 71 deletions

View File

@ -1,5 +1,149 @@
import re
class MailboxException(Exception):
message = None
def __init__(self,message):
self.message = message
class Mailbox: class Mailbox:
email_address = None
name = None
imap_server = None
imap_security = None
imap_user = None
imap_pass = None
imap_mailbox = None
imap_mailbox_fp = None
imap_separator = None
comment = None
href = None
def __init__(self,mb_ref):
if 'email_address' not in mb_ref:
raise MailboxException("'email_address' is mandatory!")
self.email_address = mb_ref['email_address']
if 'name' not in mb_ref:
raise MailboxException("'name' is mandatory!")
self.name = mb_ref['name']
if 'imap_server' not in mb_ref:
raise MailboxException("'imap_server' is mandatory!")
self.imap_server = mb_ref['imap_server']
if 'imap_security' in mb_ref:
if re.match("^(plain|starttls|tls)$", mb_ref['imap_security']) is not None:
self.imap_security = mb_ref['imap_security']
else:
raise MailboxException('imap_security: {} is invalid! '+
'Valid values: plain,starttls,tls'.format(mb_ref['imap_security'])
)
else:
raise MailboxException("'imap_security' is a mandatory!")
if 'imap_user' not in mb_ref:
raise MailboxException("'imap_user' is mandatory!")
self.imap_user = mb_ref['imap_user']
if 'imap_pass' not in mb_ref:
raise MailboxException("'imap_pass' is mandatory!")
self.imap_pass = mb_ref['imap_pass']
if 'imap_mailbox' not in mb_ref:
raise MailboxException("'imap_mailbox' is mandatory!")
self.imap_mailbox = mb_ref['imap_mailbox']
if 'imap_mailbox_fp' not in mb_ref:
raise MailboxException("'imap_mailbox_fp' is mandatory!")
self.imap_mailbox_fp = mb_ref['imap_mailbox_fp']
if 'imap_separator' not in mb_ref:
raise MailboxException("'imap_separator' is mandatory!")
self.imap_seperator = mb_ref['imap_separator']
if 'comment' in mb_ref:
self.comment = mb_ref['comment']
if 'href' in mb_ref:
self.href = mb_ref['href']
class QuarMailException(Exception):
message = None
def __init__(self,message):
self.message = message
class QuarMail: class QuarMail:
id = None
ctime = None
mx_queue_id = None
env_from = None
env_rcpt = None
hdr_cf = None
hdr_from = None
hdr_subject = None
hdr_msgid = None
hdr_date = None
cf_meta = None
mailbox_id = None
imap_uid = None
msg_size = None
href = None
def __init__(self,qm_ref):
if 'id' not in qm_ref:
raise QuarMailException("'id' is mandatory!")
self.id = qm_ref['id']
if 'ctime' not in qm_ref:
raise QuarMailException("'ctime' is mandatory!")
self.ctime = qm_ref['ctime']
if 'mx_queue_id' not in qm_ref:
raise QuarMailException("'mx_queue_id' is mandatory!")
self.mx_queue_id = qm_ref['mx_queue_id']
if 'env_from' not in qm_ref:
raise QuarMailException("'env_from' is mandatory!")
self.env_from = qm_ref['env_from']
if 'env_rcpt' not in qm_ref:
raise QuarMailException("'env_rcpt' is mandatory!")
self.env_rcpt = qm_ref['env_rcpt']
if 'hdr_cf' in qm_ref:
self.hdr_cf = qm_ref['hdr_cf']
if 'hdr_from' in qm_ref:
self.hdr_from = qm_ref['hdr_from']
if 'hdr_subject' in qm_ref:
self.hdr_subject = qm_ref['hdr_subject']
if 'hdr_msgid' in qm_ref:
self.hdr_msgid = qm_ref['hdr_msgid']
if 'hdr_date' in qm_ref:
self.hdr_date = qm_ref['hdr_date']
if 'cf_meta' in qm_ref:
self.cf_meta = qm_ref['cf_meta']
if 'mailbox_id' not in qm_ref:
raise QuarMailException("'mailbox_id' is mandatory!")
self.mailbox_id = qm_ref['mailbox_id']
if 'imap_uid' not in qm_ref:
raise QuarMailException("'imap_uid' is mandatory!")
self.imap_uid = qm_ref['imap_uid']
if 'msg_size' not in qm_ref:
raise QuarMailException("'msg_size' is mandatory!")
self.msg_size = qm_ref['msg_size']
if 'href' in qm_ref:
self.href = qm_ref['href']
class AttachmentException(Exception):
message = None
def __init__(self,message):
self.message = message
class Attachment: class Attachment:
id = None
filename = None
content_type = None
comment = None
href = None
def __init__(self,at_ref):
if 'id' not in at_ref:
raise AttachmentException("'id' is mandatory!")
self.id = at_ref['id']
if 'filename' not in at_ref:
raise AttachmentException("'filename' is mandatory!")
self.filename = at_ref['filename']
if 'content_type' not in at_ref:
raise AttachmentException("'content_type' is mandatory!")
self.content_type = at_ref['content_type']
if 'comment' in at_ref:
self.comment = at_ref['comment']
if 'href' in at_ref:
self.href = at_ref['href']

View File

@ -27,7 +27,8 @@ class Gulag:
self.config['db']['server'], self.config['db']['server'],
self.config['db']['user'], self.config['db']['user'],
self.config['db']['password'], self.config['db']['password'],
self.config['db']['name'] self.config['db']['name'],
self.config['uri_prefixes']
) )
except GulagDBException as e: except GulagDBException as e:
raise GulagException(e.message) from e raise GulagException(e.message) from e
@ -54,8 +55,18 @@ class Gulag:
msg = unseen['msg'] msg = unseen['msg']
msg_size = len(str(msg)) msg_size = len(str(msg))
r5321_from = email.header.decode_header(msg['Return-Path'])[0][0] r5321_from = email.header.decode_header(msg['Return-Path'])[0][0]
r5321_rcpts = email.header.decode_header(msg['X-Envelope-To-Blocked'])[0][0] r5321_rcpts = None
r5322_from = email.header.decode_header(msg['From'])[0][0] try:
r5321_rcpts = email.header.decode_header(msg['X-Envelope-To-Blocked'])[0][0]
except:
print("Failed to extract envelope recipients! Skipping mail")
continue
r5322_from = None
try:
r5322_from = email.header.decode_header(msg['From'])[0][0]
except:
print("Failed to extract from header! Skipping mail")
continue
subject = email.header.decode_header(msg['Subject'])[0][0] subject = email.header.decode_header(msg['Subject'])[0][0]
msg_id = None msg_id = None
try: try:
@ -117,3 +128,15 @@ class Gulag:
)) ))
) )
def get_mailboxes(self):
try:
return self.db.get_mailboxes()
except GulagDBException as e:
raise GulagException("GulagDBException: " + e.message) from e
def get_quarmails(self):
try:
return self.db.get_quarmails()
except GulagDBException as e:
raise GulagException("GulagDBException: " + e.message) from e

View File

@ -1,4 +1,5 @@
import mysql.connector as mariadb import mysql.connector as mariadb
from Entities import Mailbox,MailboxException,QuarMail,QuarMailException,Attachment,AttachmentException
class GulagDBException(Exception): class GulagDBException(Exception):
message = None message = None
@ -7,15 +8,18 @@ class GulagDBException(Exception):
class GulagDB: class GulagDB:
conn = None conn = None
uri_prefixes = None
def __init__(self, server, user, password, name): def __init__(self, server, user, password, name, uri_prefixes):
try: try:
self.conn = mariadb.connect( self.conn = mariadb.connect(
host=server, host=server,
user=user, user=user,
password=password, password=password,
database=name database=name,
autocommit=True
) )
self.uri_prefixes = uri_prefixes
except mariadb.Error as e: except mariadb.Error as e:
raise GulagDBException(e) from e raise GulagDBException(e) from e
@ -35,7 +39,12 @@ 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(dict) dict['href'] = self.uri_prefixes['mailboxes'] + dict['email_address']
try:
results.append(Mailbox(dict).__dict__)
except MailboxException as e:
print("MailboxException: " + e.message)
continue
return results return results
except mariadb.Error as e: except mariadb.Error as e:
raise GulagDBException(e) from e raise GulagDBException(e) from e
@ -55,8 +64,9 @@ class GulagDB:
quarmail['mailbox_id'],quarmail['imap_uid'],quarmail['msg_size'] quarmail['mailbox_id'],quarmail['imap_uid'],quarmail['msg_size']
) )
) )
self.conn.commit() id = cursor.lastrowid
return cursor.lastrowid cursor.close()
return id
except mariadb.Error as e: except mariadb.Error as e:
raise GulagDBException(e) from e raise GulagDBException(e) from e
@ -64,28 +74,35 @@ class GulagDB:
try: try:
cursor = self.conn.cursor() cursor = self.conn.cursor()
cursor.execute("delete from QuarMails where id=%s;", (id)) cursor.execute("delete from QuarMails where id=%s;", (id))
self.conn.commit() cursor.close()
return cursor.lastrowid return True
except mariadb.Error as e: except mariadb.Error as e:
raise GulagDBException(e) from e raise GulagDBException(e) from e
def get_quarmails(self, mailbox_id): # def get_quarmails(self,mailbox_id):
def get_quarmails(self):
try: try:
cursor = self.conn.cursor() cursor = self.conn.cursor()
cursor.execute( # cursor.execute(
"select * from QuarMails where mailbox_id='%s';", # "select * from QuarMails where mailbox_id='%s';",
(mailbox_id) # (mailbox_id)
) # )
cursor.execute("select * from QuarMails;")
results = [] results = []
data = cursor.fetchall() data = cursor.fetchall()
if data == None: if data == None:
return results return results
desc = cursor.description desc = cursor.description
cursor.close()
for tuple in data: for tuple in data:
dict = {} dict = {}
for (name, value) in zip(desc, tuple): for (name, value) in zip(desc, tuple):
dict[name[0]] = value if(name[0] == 'ctime'):
results.append(dict) dict[name[0]] = value.strftime('%Y-%m-%d %H:%M:%S')
else:
dict[name[0]] = value
dict['href'] = self.uri_prefixes['quarmails'] + str(dict['id'])
results.append(QuarMail(dict).__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
@ -104,7 +121,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(dict) results.append(QuarMail(dict).__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
@ -116,7 +133,6 @@ class GulagDB:
"(filename, content_type) values (%s,%s)", "(filename, content_type) values (%s,%s)",
(attach['filename'], attach['content_type']) (attach['filename'], attach['content_type'])
) )
self.conn.commit()
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
@ -128,7 +144,6 @@ class GulagDB:
"(quarmail_id, attachment_id) values (%s,%s)", "(quarmail_id, attachment_id) values (%s,%s)",
(quarmail_id, attachment_id) (quarmail_id, attachment_id)
) )
self.conn.commit()
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,7 @@
from flask import request from flask import request
from flask_restful import Resource, abort from flask_restful import Resource, abort
import json from Entities import Mailbox,MailboxException,QuarMail,QuarMailException,Attachment,AttachmentException
from Gulag import GulagException
class GulagResource(Resource): class GulagResource(Resource):
gulag = None gulag = None
@ -33,7 +34,10 @@ class ResRoot(GulagResource):
class ResMailboxes(GulagResource): class ResMailboxes(GulagResource):
def get(self): def get(self):
return {"resource": "Mailboxes"} try:
return self.gulag.get_mailboxes()
except GulagException as e:
abort(500, message=e.message)
class ResMailbox(GulagResource): class ResMailbox(GulagResource):
def get(self,id): def get(self,id):
@ -41,7 +45,10 @@ class ResMailbox(GulagResource):
class ResQuarMails(GulagResource): class ResQuarMails(GulagResource):
def get(self): def get(self):
return {"resource": "QuarMails"} try:
return self.gulag.get_quarmails()
except GulagException as e:
abort(500, message=e.message)
class ResQuarMail(GulagResource): class ResQuarMail(GulagResource):
def get(self,id): def get(self,id):

View File

@ -1,37 +0,0 @@
{
"daemon":{
"listen_host": "127.0.0.1",
"listen_port": 5001
},
"trusted_proxies": {
"rprx01":[
"172.16.100.5", "fd00:100::5"
],
"rprx02":[
"172.16.100.6", "fd00:100::6"
]
},
"api_keys": {
"HIGHLY_SECURE_API_KEY": {
"user": "GULAG APP"
}
},
"uri_prefixes": {
"root": "https://<fqdn>/api/v1/",
"quarmails": "https://<fqdn>/api/v1/quarmails/",
"attachments": "https://<fqdn>/api/v1/attachments/"
},
"db":{
"server": "127.0.0.1",
"user": "root",
"password": "",
"name": "Gulag"
},
"cleaner":{
"retention_period": "12 hour",
"interval": 10
},
"importer":{
"interval": 10
}
}

View File

@ -1,9 +0,0 @@
[uwsgi]
processes = 4
cheaper = 1
cheaper-initial = 1
cheaper-step = 1
python-path = /app
wsgi-file = /app/uwsgi.py
pyargv = --config /config/config.json
socket = /socket/gulag_uwsgi.sock

View File

@ -1,5 +1,7 @@
create database Gulag; create database Gulag;
use Gulag;
create table Mailboxes( 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,
@ -9,10 +11,10 @@ create table Mailboxes(
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 '/',
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)
values('quarantine-in@example.org','E-Mail inbound quarantine','quarantine-in','quarantine-in_secure_password'); values('quarantine-in@example.org','E-Mail inbound quarantine','quarantine-in','quarantine-in_secure_password');
insert into Mailboxes (email_address,name,imap_user,imap_pass) insert into Mailboxes (email_address,name,imap_user,imap_pass)
values('quarantine-out@example.org','E-Mail outbound quarantine','quarantine-out','quarantine-out_secure_password'); values('quarantine-out@example.org','E-Mail outbound quarantine','quarantine-out','quarantine-out_secure_password');