- [Show pagesource]
- [Old revisions]
- [[unknown link type]]
- []
This is an old revision of the document!
Table of Contents
Python sendmail wrapper
Sendmail wrapper limiting amount of emails sent by php
Installation
- Copy the script below into /usr/sbin/safe_sendmail
- Put the path to script into sendmail_path variable in php.ini
- Database and log files will be created in /tmp/safe_sendmail.db and /tmp/safe_sendmail.log respectively
- Timewindow (default 1 hour) and treshold (default 200 emails) parameters are declared in script
Testing
- Put following content in /var/www/testmail.txt: (few blank lines may be needed at the end of the file)
To: firma@starlab.cz Subject: This is a test message subject X-PHP-Originating-Script: 0:index.php This is a test message body
- Run:
for i in `seq 1 300`; do cat /var/www/testmail.txt | safe_sendmail firma@starlab.cz ; done
- Observe log file:
tail -f /tmp/safe_sendmail
- After the treshold number of emails script refuses to send more emails.
Code
#!/usr/bin/env python import os import sqlite3 import sys import logging import re import datetime as dt import subprocess import shlex import psutil logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) work_dir = "/usr/local/safe_sendmail" # create a file handler logfile = os.path.join(work_dir, 'safe_sendmail.log') handler = logging.FileHandler(logfile) handler.setLevel(logging.DEBUG) formatter = logging.Formatter('%(asctime)s %(levelname)s: %(message)s') handler.setFormatter(formatter) logger.addHandler(handler) sendmail_bin = '/usr/sbin/sendmail -t -i' db_file = os.path.join(work_dir, 'safe_sendmail.db') # Number of mails in given amount of minutes to stop the sending threshold = 200 time_scope = 60 def check_db(): with sqlite3.connect(db_file) as conn: c = conn.cursor() c.execute("SELECT name FROM sqlite_master WHERE name='mails'") is_table = len(c.fetchall()) if is_table == 0: c.execute("CREATE TABLE mails (date text, address_to text)") conn.commit() return True def send_mail(mail): logger.debug('Sending mail!\n%s' % mail) command = shlex.split(sendmail_bin) try: process = subprocess.Popen(command, stdin=subprocess.PIPE) process.communicate(mail) if process.returncode == 0: return True else: return False except: logger.exception("Unable to send mail") return False if __name__ == "__main__": parent_process = psutil.Process(os.getppid()) grandparent_process = psutil.Process(parent_process.ppid()) check_db() mail = sys.stdin.read() address_regex = re.compile(u"To: (?P<mail_address>\S+)", re.UNICODE) address = address_regex.match(mail.replace('\n', ' ')).group('mail_address') timestamp = dt.datetime.now() logger.debug("Address: %s" % address) logger.debug("Datetime: %s" % timestamp) past = timestamp - dt.timedelta(minutes=time_scope) with sqlite3.connect(db_file) as conn: c = conn.cursor() c.execute('INSERT INTO mails VALUES(?,?)', (timestamp, address)) conn.commit() c.execute('SELECT COUNT(*) FROM mails where date > ?', (past,)) count_since_past = c.fetchone()[0] logger.debug("Count since %s %d" % (timestamp, count_since_past)) if count_since_past < threshold: if send_mail(mail): sys.exit(0) else: sys.exit(2) else: # logger.info("Parent: {}".format(parent_process.cmdline())) # uncomment to get parent's cmdline # logger.info("Grandparent: {}".format(grandparent_process.cmdline())) # uncomment to get grandparent's cmdline # logger.info(mail) # uncomment to get raw input (mail content and headers) logger.warning("Unable to send mail to address %s - quota exceeded! (%d mails sent in last %d minutes - limit %s)" % (address, count_since_past, time_scope, threshold)) sys.exit(1)
linux/apache/sendmail_python.1487257456.txt.gz · Last modified: 2017/02/16 16:04 by vondra