quota_notify.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. #!/usr/bin/python3
  2. import smtplib
  3. import os
  4. from email.mime.multipart import MIMEMultipart
  5. from email.mime.text import MIMEText
  6. from email.utils import COMMASPACE, formatdate
  7. import jinja2
  8. from jinja2.sandbox import SandboxedEnvironment
  9. import redis
  10. import time
  11. import json
  12. import sys
  13. import html2text
  14. from subprocess import Popen, PIPE, STDOUT
  15. if len(sys.argv) > 2:
  16. percent = int(sys.argv[1])
  17. username = str(sys.argv[2])
  18. else:
  19. print("Args missing")
  20. sys.exit(1)
  21. while True:
  22. try:
  23. r = redis.StrictRedis(host='redis', decode_responses=True, port=6379, db=0, username='quota_notify', password='')
  24. r.ping()
  25. except Exception as ex:
  26. print('%s - trying again...' % (ex))
  27. time.sleep(3)
  28. else:
  29. break
  30. if r.get('QW_HTML'):
  31. try:
  32. env = SandboxedEnvironment()
  33. template = env.from_string(r.get('QW_HTML'))
  34. except Exception:
  35. print("Error: Cannot parse quota template, falling back to default template.")
  36. with open('/templates/quota.tpl') as file_:
  37. env = SandboxedEnvironment()
  38. template = env.from_string(file_.read())
  39. else:
  40. with open('/templates/quota.tpl') as file_:
  41. env = SandboxedEnvironment()
  42. template = env.from_string(file_.read())
  43. try:
  44. html = template.render(username=username, percent=percent)
  45. except (jinja2.exceptions.SecurityError, jinja2.TemplateError) as ex:
  46. print(f"SecurityError or TemplateError in template rendering: {ex}")
  47. sys.exit(1)
  48. text = html2text.html2text(html)
  49. try:
  50. msg = MIMEMultipart('alternative')
  51. msg['From'] = r.get('QW_SENDER') or "quota-warning@localhost"
  52. msg['Subject'] = r.get('QW_SUBJ') or "Quota warning"
  53. msg['Date'] = formatdate(localtime = True)
  54. text_part = MIMEText(text, 'plain', 'utf-8')
  55. html_part = MIMEText(html, 'html', 'utf-8')
  56. msg.attach(text_part)
  57. msg.attach(html_part)
  58. msg['To'] = username
  59. p = Popen(['/usr/libexec/dovecot/dovecot-lda', '-d', username, '-o', '"plugin/quota=maildir:User quota:noenforcing"'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)
  60. p.communicate(input=bytes(msg.as_string(), 'utf-8'))
  61. domain = username.split("@")[-1]
  62. if domain and r.hget('QW_BCC', domain):
  63. bcc_data = json.loads(r.hget('QW_BCC', domain))
  64. bcc_rcpts = bcc_data['bcc_rcpts']
  65. if bcc_data['active'] == 1:
  66. for rcpt in bcc_rcpts:
  67. msg = MIMEMultipart('alternative')
  68. msg['From'] = username
  69. subject = r.get('QW_SUBJ') or "Quota warning"
  70. msg['Subject'] = subject + ' (' + username + ')'
  71. msg['Date'] = formatdate(localtime = True)
  72. text_part = MIMEText(text, 'plain', 'utf-8')
  73. html_part = MIMEText(html, 'html', 'utf-8')
  74. msg.attach(text_part)
  75. msg.attach(html_part)
  76. msg['To'] = rcpt
  77. server = smtplib.SMTP('postfix', 588, 'quotanotification')
  78. server.ehlo()
  79. server.sendmail(msg['From'], str(rcpt), msg.as_string())
  80. server.quit()
  81. except Exception as ex:
  82. print('Failed to send quota notification: %s' % (ex))
  83. sys.exit(1)
  84. try:
  85. sys.stdout.close()
  86. except:
  87. pass
  88. try:
  89. sys.stderr.close()
  90. except:
  91. pass