This commit is contained in:
Dominik Chilla 2018-12-09 17:08:50 +01:00
parent a1ceb75805
commit 9234312dbd
4 changed files with 89 additions and 22 deletions

View File

@ -1,4 +1,4 @@
import json,sys import json,sys,os,logging
import email,email.header,email.message import email,email.header,email.message
from GulagDB import GulagDB,GulagDBException from GulagDB import GulagDB,GulagDBException
from GulagMailbox import IMAPmailbox,IMAPmailboxException from GulagMailbox import IMAPmailbox,IMAPmailboxException
@ -23,13 +23,32 @@ class Gulag:
f.close() f.close()
except: except:
raise GulagException(whoami(self) + str(sys.exc_info())) raise GulagException(whoami(self) + str(sys.exc_info()))
# logging
# logging_level = logging.INFO
# if 'level' in self.config['logging']:
if 'logging' not in self.config:
raise GulagException(whoami(self) + "Logging not configured!")
if('filename' in self.config['logging'] and
len(self.config['logging']['filename']) > 0):
logging.basicConfig(
filename=self.config['logging']['filename'],
format='%(asctime)s %(levelname)s %(message)s',
level=self.config['logging']['level']
)
else:
logging.basicConfig(
format='%(asctime)s %(levelname)s %(message)s',
level=self.config['logging']['level']
)
try: try:
self.db = GulagDB(self.config['db'],self.config['uri_prefixes']) self.db = GulagDB(self.config['db'],self.config['uri_prefixes'])
self.fields['Mailboxes'] = self.db.get_fields('Mailboxes') self.fields['Mailboxes'] = self.db.get_fields('Mailboxes')
self.fields['QuarMails'] = self.db.get_fields('QuarMails') self.fields['QuarMails'] = self.db.get_fields('QuarMails')
self.fields['Attachments'] = self.db.get_fields('Attachments') self.fields['Attachments'] = self.db.get_fields('Attachments')
except GulagDBException as e: except GulagDBException as e:
logging.warning(whoami(self) + e.message)
raise GulagException(whoami(self) + e.message) from e raise GulagException(whoami(self) + e.message) from e
logging.info('Gulag core initialized by ' + os.path.basename(__file__))
def check_fields(self,fields_target,args): def check_fields(self,fields_target,args):
if fields_target not in self.fields: if fields_target not in self.fields:
@ -54,7 +73,7 @@ class Gulag:
try: try:
imap_mb = IMAPmailbox(mailbox) imap_mb = IMAPmailbox(mailbox)
except IMAPmailboxException as e: except IMAPmailboxException as e:
print(e.message) logging.warning(whoami(self) + e.message)
continue continue
for unseen in imap_mb.get_unseen_messages(): for unseen in imap_mb.get_unseen_messages():
quarmail_ids = [] quarmail_ids = []
@ -70,13 +89,17 @@ class Gulag:
try: try:
r5321_rcpts = email.header.decode_header(msg['X-Envelope-To-Blocked'])[0][0] r5321_rcpts = email.header.decode_header(msg['X-Envelope-To-Blocked'])[0][0]
except: except:
print("Failed to extract envelope recipients! Skipping mail") logging.warning(whoami(self) +
"Failed to extract envelope recipients! Skipping mail"
)
continue continue
r5322_from = None r5322_from = None
try: try:
r5322_from = email.header.decode_header(msg['From'])[0][0] r5322_from = email.header.decode_header(msg['From'])[0][0]
except: except:
print("Failed to extract from header! Skipping mail") logging.warning(whoami(self) +
"Failed to extract from header! Skipping mail"
)
continue 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
@ -109,7 +132,7 @@ class Gulag:
'hdr_msgid': msg_id, 'hdr_date': date, 'cf_meta': 'cf_meta', 'hdr_msgid': msg_id, 'hdr_date': date, 'cf_meta': 'cf_meta',
'mailbox_id': 'quarantine@zwackl.de', 'imap_uid': uid, 'msg_size': msg_size 'mailbox_id': 'quarantine@zwackl.de', 'imap_uid': uid, 'msg_size': msg_size
}) })
print("QuarMail (%s) imported" % (quarmail_id)) logging.info(whoami(self) + "QuarMail (%s) imported" % (quarmail_id))
quarmail_ids.append(quarmail_id) quarmail_ids.append(quarmail_id)
# Ende for rcpts # Ende for rcpts
# Alle MIME-Parts durchiterieren und Attachments # Alle MIME-Parts durchiterieren und Attachments
@ -142,16 +165,15 @@ class Gulag:
# Ende for get_mailboxes # Ende for get_mailboxes
def cleanup_quarmails(self): def cleanup_quarmails(self):
print("Mails to expunge: " + logging.info(whoami(self) + "QuarMails to purge: " + str(len(
str(len( self.db.get_deprecated_mails(self.config['cleaner']['retention_period'])
self.db.get_deprecated_mails(self.config['cleaner']['retention_period']) )))
))
)
def get_mailboxes(self): def get_mailboxes(self):
try: try:
return self.db.get_mailboxes() return self.db.get_mailboxes()
except GulagDBException as e: except GulagDBException as e:
logging.warning(whoami(self) + e.message)
raise GulagException(whoami(self) + e.message) from e raise GulagException(whoami(self) + e.message) from e
def get_quarmails(self,args): def get_quarmails(self,args):
@ -159,6 +181,7 @@ class Gulag:
self.check_fields('QuarMails',args) self.check_fields('QuarMails',args)
return self.db.get_quarmails(args) return self.db.get_quarmails(args)
except(GulagException,GulagDBException) as e: except(GulagException,GulagDBException) as e:
logging.warning(whoami(self) + e.message)
raise GulagException( raise GulagException(
whoami(self) + e.message whoami(self) + e.message
) from e ) from e
@ -168,6 +191,7 @@ class Gulag:
try: try:
qm_db = self.db.get_quarmail({"id": args['quarmail_id']}) qm_db = self.db.get_quarmail({"id": args['quarmail_id']})
except GulagDBException as e: except GulagDBException as e:
logging.warning(whoami(self) + e.message)
raise GulagException(whoami(self) + e.message) from e raise GulagException(whoami(self) + e.message) from e
if 'rfc822_message' not in args: if 'rfc822_message' not in args:
return qm_db return qm_db
@ -176,6 +200,7 @@ class Gulag:
try: try:
mailbox = self.db.get_mailbox(qm_db['mailbox_id']) mailbox = self.db.get_mailbox(qm_db['mailbox_id'])
except GulagDBException as e: except GulagDBException as e:
logging.warning(whoami(self) + e.message)
raise GulagException(whoami(self) + e.message) from e raise GulagException(whoami(self) + e.message) from e
imap_mb = None imap_mb = None
try: try:
@ -183,14 +208,14 @@ class Gulag:
qm_db['rfc822_message'] = imap_mb.get_message(qm_db['imap_uid']).decode("utf-8") qm_db['rfc822_message'] = imap_mb.get_message(qm_db['imap_uid']).decode("utf-8")
return qm_db return qm_db
except IMAPmailboxException as e: except IMAPmailboxException as e:
print(e.message) logging.warning(whoami(self) + e.message)
raise GulagException(whoami(self) + e.message) from e raise GulagException(whoami(self) + e.message) from e
def get_quarmail_attachments(self,args): def get_quarmail_attachments(self,args):
try: try:
return self.db.get_quarmail_attachments(args['quarmail_id']) return self.db.get_quarmail_attachments(args['quarmail_id'])
except GulagDBException as e: except GulagDBException as e:
print(e.message) logging.warning(whoami(self) + e.message)
raise GulagException(whoami(self) + e.message) from e raise GulagException(whoami(self) + e.message) from e
def get_quarmail_attachment(self,args): def get_quarmail_attachment(self,args):
@ -200,7 +225,7 @@ class Gulag:
args['quarmail_id'],args['attachment_id'] args['quarmail_id'],args['attachment_id']
) )
except GulagDBException as e: except GulagDBException as e:
print(e.message) logging.warning(whoami(self) + e.message)
raise GulagException(whoami(self) + e.message) from e raise GulagException(whoami(self) + e.message) from e
if 'data' not in args: if 'data' not in args:
return qmat_db return qmat_db
@ -209,6 +234,7 @@ class Gulag:
try: try:
mailbox = self.db.get_mailbox(qmat_db['mailbox_id']) mailbox = self.db.get_mailbox(qmat_db['mailbox_id'])
except GulagDBException as e: except GulagDBException as e:
logging.warning(whoami(self) + e.message)
raise GulagException(whoami(self) + e.message) from e raise GulagException(whoami(self) + e.message) from e
imap_mb = None imap_mb = None
try: try:
@ -218,7 +244,7 @@ class Gulag:
) )
return qmat_db return qmat_db
except IMAPmailboxException as e: except IMAPmailboxException as e:
print(e.message) logging.warning(whoami(self) + e.message)
raise GulagException(whoami(self) + e.message) from e raise GulagException(whoami(self) + e.message) from e
def get_attachment(self,args): def get_attachment(self,args):
@ -243,13 +269,46 @@ class Gulag:
# check if the request comes really from rspamd´s metadata_exporter # check if the request comes really from rspamd´s metadata_exporter
# default metadata_header prefix 'X-Rspamd' will be expected # default metadata_header prefix 'X-Rspamd' will be expected
if('X-Rspamd-From' not in args['req_headers']): if('X-Rspamd-From' not in args['req_headers']):
raise GulagException(whoami(self) err = str(whoami(self)
+ "Missing Rspamd-specific headers (e.g. X-Rspamd-From)!" + "Missing Rspamd-specific request header X-Rspamd-From!"
) )
logging.error(err)
raise GulagException(err)
# Prepend Gulag-specific headers to rejected mail # Prepend Gulag-specific headers to rejected mail
# before pushing into quarantine mailbox # before pushing into quarantine mailbox
msg = None msg = None
try: try:
if('X-Rspamd-From' not in args['req_headers']):
err = str(whoami(self)
+ "Missing Rspamd-specific request header X-Rspamd-From!"
)
logging.error(err)
raise GulagException(err)
if('X-Rspamd-Rcpt' not in args['req_headers']):
err = str(whoami(self)
+ "Missing Rspamd-specific request header X-Rspamd-Rcpt!"
)
logging.error(err)
raise GulagException(err)
if('X-Rspamd-Symbols' not in args['req_headers']):
err = str(whoami(self)
+ "Missing Rspamd-specific request header X-Rspamd-Symbols!"
)
logging.error(err)
raise GulagException(err)
if('X-Rspamd-Qid' not in args['req_headers']):
err = str(whoami(self)
+ "Missing Rspamd-specific request header X-Rspamd-Qid!"
)
logging.error(err)
raise GulagException(err)
if('rfc822_message' not in args['rfc822_message']):
err = str(whoami(self)
+ "Missing rfc822_message!"
)
logging.error(err)
raise GulagException(err)
# all mandatory request headers and body are present
rcpts_hdr = "" rcpts_hdr = ""
for rcpt in json.loads(str(args['req_headers']['X-Rspamd-Rcpt'])): for rcpt in json.loads(str(args['req_headers']['X-Rspamd-Rcpt'])):
if(len(rcpts_hdr) > 0): if(len(rcpts_hdr) > 0):
@ -264,6 +323,8 @@ class Gulag:
msg += "X-Spam-QID: " + args['req_headers']['X-Rspamd-Qid'] + "\r\n" msg += "X-Spam-QID: " + args['req_headers']['X-Rspamd-Qid'] + "\r\n"
# append original mail # append original mail
msg += args['rfc822_message'] msg += args['rfc822_message']
except GulagException as e:
raise GulagException(e.message) from e
except: except:
raise GulagException(whoami(self) + str(sys.exc_info())) raise GulagException(whoami(self) + str(sys.exc_info()))
imap_mb = None imap_mb = None

View File

@ -1,7 +1,6 @@
import sys import sys
from smtplib import SMTP from smtplib import SMTP
def whoami(obj): def whoami(obj):
return type(obj).__name__ + "::" + sys._getframe(1).f_code.co_name + "(): " return type(obj).__name__ + "::" + sys._getframe(1).f_code.co_name + "(): "

View File

@ -103,6 +103,7 @@ class ResRSPAMDImporter(GulagResource):
"rfc822_message": request.get_data(as_text=True) "rfc822_message": request.get_data(as_text=True)
}) })
# TODO: Response mit Location-Header? # TODO: Response mit Location-Header?
# https://stackoverflow.com/a/22707491
return {"resource: ": "HTTP2IMAP for RSPAMD"} return {"resource: ": "HTTP2IMAP for RSPAMD"}
except GulagException as e: except GulagException as e:
abort(400, message=e.message) abort(400, message=e.message)

View File

@ -1,4 +1,10 @@
{ {
"logging": {
"__level": "default: WARNING. Possible: INFO,ERROR,CRITICAL,DEBUG",
"level": "DEBUG",
"__filename": "default: empty string (stdout).",
"filename": ""
},
"daemon":{ "daemon":{
"listen_host": "127.0.0.1", "listen_host": "127.0.0.1",
"listen_port": 5001 "listen_port": 5001
@ -17,10 +23,10 @@
} }
}, },
"uri_prefixes": { "uri_prefixes": {
"root": "https://gulag.example.org/api/v1/", "root": "http://127.0.0.1:9090/api/v1/",
"mailboxes": "https://gulag.example.org/api/v1/mailboxes/", "mailboxes": "http://127.0.0.1:9090/api/v1/mailboxes/",
"quarmails": "https://gulag.example.org/api/v1/quarmails/", "quarmails": "http://127.0.0.1:9090/api/v1/quarmails/",
"attachments": "https://gulag.example.org/api/v1/attachments/" "attachments": "http://127.0.0.1:9090/api/v1/attachments/"
}, },
"db":{ "db":{
"unix_socket": "/mysqld/mysqld.sock", "unix_socket": "/mysqld/mysqld.sock",