quarantine_notify.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import smtplib
  2. import os
  3. import mysql.connector
  4. from email.mime.multipart import MIMEMultipart
  5. from email.mime.text import MIMEText
  6. from email.utils import COMMASPACE, formatdate
  7. import cgi
  8. import jinja2
  9. from jinja2 import Template
  10. import json
  11. import redis
  12. import time
  13. import html2text
  14. import socket
  15. while True:
  16. try:
  17. r = redis.StrictRedis(host='redis', decode_responses=True, port=6379, db=0)
  18. r.ping()
  19. except Exception as ex:
  20. print('%s - trying again...' % (ex))
  21. time.sleep(3)
  22. else:
  23. break
  24. time_now = int(time.time())
  25. def query_mysql(query, headers = True, update = False):
  26. while True:
  27. try:
  28. cnx = mysql.connector.connect(unix_socket = '/var/run/mysqld/mysqld.sock', user='mailcow', passwd='sdvZ39iWd86UnVI772spU48VO1T8', database='mailcow', charset="utf8")
  29. except Exception as ex:
  30. print('%s - trying again...' % (ex))
  31. time.sleep(3)
  32. else:
  33. break
  34. cur = cnx.cursor()
  35. cur.execute(query)
  36. if not update:
  37. result = []
  38. columns = tuple( [d[0] for d in cur.description] )
  39. for row in cur:
  40. if headers:
  41. result.append(dict(list(zip(columns, row))))
  42. else:
  43. result.append(row)
  44. cur.close()
  45. cnx.close()
  46. return result
  47. else:
  48. cnx.commit()
  49. cur.close()
  50. cnx.close()
  51. def notify_rcpt(rcpt, msg_count, quarantine_acl):
  52. meta_query = query_mysql('SELECT SHA2(CONCAT(id, qid), 256) AS qhash, id, subject, score, sender, created FROM quarantine WHERE notified = 0 AND rcpt = "%s"' % (rcpt))
  53. if r.get('Q_HTML'):
  54. try:
  55. template = Template(r.get('Q_HTML'))
  56. except:
  57. print("Error: Cannot parse quarantine template, falling back to default template.")
  58. with open('/templates/quarantine.tpl') as file_:
  59. template = Template(file_.read())
  60. else:
  61. with open('/templates/quarantine.tpl') as file_:
  62. template = Template(file_.read())
  63. html = template.render(meta=meta_query, counter=msg_count, hostname=socket.gethostname(), quarantine_acl=quarantine_acl)
  64. text = html2text.html2text(html)
  65. count = 0
  66. while count < 15:
  67. try:
  68. server = smtplib.SMTP('postfix', 590, 'quarantine')
  69. server.ehlo()
  70. msg = MIMEMultipart('alternative')
  71. msg['From'] = r.get('Q_SENDER') or "quarantine@localhost"
  72. msg['Subject'] = r.get('Q_SUBJ') or "Spam Quarantine Notification"
  73. msg['Date'] = formatdate(localtime = True)
  74. text_part = MIMEText(text, 'plain', 'utf-8')
  75. html_part = MIMEText(html, 'html', 'utf-8')
  76. msg.attach(text_part)
  77. msg.attach(html_part)
  78. msg['To'] = str(rcpt)
  79. text = msg.as_string()
  80. server.sendmail(msg['From'].encode("ascii", errors="ignore"), msg['To'], text)
  81. server.quit()
  82. for res in meta_query:
  83. query_mysql('UPDATE quarantine SET notified = 1 WHERE id = "%d"' % (res['id']), update = True)
  84. r.hset('Q_LAST_NOTIFIED', record['rcpt'], time_now)
  85. break
  86. except Exception as ex:
  87. server.quit()
  88. print('%s' % (ex))
  89. time.sleep(3)
  90. 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 rcpt in (SELECT username FROM mailbox) GROUP BY rcpt')
  91. for record in records:
  92. attrs = ''
  93. attrs_json = ''
  94. try:
  95. last_notification = int(r.hget('Q_LAST_NOTIFIED', record['rcpt']))
  96. if last_notification > time_now:
  97. print('Last notification is > time now, assuming never')
  98. last_notification = 0
  99. except Exception as ex:
  100. print('Could not determine last notification for %s, assuming never' % (record['rcpt']))
  101. last_notification = 0
  102. attrs_json = query_mysql('SELECT attributes FROM mailbox WHERE username = "%s"' % (record['rcpt']))
  103. attrs = json.loads(str(attrs_json[0]['attributes']))
  104. if attrs['quarantine_notification'] not in ('hourly', 'daily', 'weekly', 'never'):
  105. print('Abnormal quarantine_notification value')
  106. continue
  107. if attrs['quarantine_notification'] == 'hourly':
  108. if last_notification == 0 or (last_notification + 3600) < time_now:
  109. print("Notifying %s about %d new items in quarantine" % (record['rcpt'], record['counter']))
  110. notify_rcpt(record['rcpt'], record['counter'], record['quarantine_acl'])
  111. elif attrs['quarantine_notification'] == 'daily':
  112. if last_notification == 0 or (last_notification + 86400) < time_now:
  113. print("Notifying %s about %d new items in quarantine" % (record['rcpt'], record['counter']))
  114. notify_rcpt(record['rcpt'], record['counter'], record['quarantine_acl'])
  115. elif attrs['quarantine_notification'] == 'weekly':
  116. if last_notification == 0 or (last_notification + 604800) < time_now:
  117. print("Notifying %s about %d new items in quarantine" % (record['rcpt'], record['counter']))
  118. notify_rcpt(record['rcpt'], record['counter'], record['quarantine_acl'])