Commit 6b56b532 authored by ilv's avatar ilv
Browse files

Added SMTP module skeleton. Simple stuff for now.

parent a0c4cae5
import os
import re
import sys
import email
import logging
import ConfigParser
import gettor
class SingleLevelFilter(logging.Filter):
"""
Filter logging levels to create separated logs.
Public methods:
filter(record)
"""
def __init__(self, passlevel, reject):
"""
Initialize a new object with level to be filtered.
If reject value is false, all but the passlevel will be
filtered. Useful for logging in separated files.
"""
self.passlevel = passlevel
self.reject = reject
def filter(self, record):
"""
Do the actual filtering.
"""
if self.reject:
return (record.levelno != self.passlevel)
else:
return (record.levelno == self.passlevel)
class SMTP(object):
"""
Class for the GetTor's SMTP service. Provides an interface to
interact with requests received by email.
"""
def __init__(self, config):
"""
Creates new object by reading a configuration file.
Args:
- config (string): the path of the file that will be used as
configuration
"""
logging.basicConfig(format='[%(levelname)s] %(asctime)s - %(message)s',
datefmt="%Y-%m-%d %H:%M:%S")
logger = logging.getLogger(__name__)
self.delay = True
self.logdir = 'smtp/log/'
self.loglevel = 'DEBUG'
# Better log format
string_format = '[%(levelname)7s] %(asctime)s - %(message)s'
formatter = logging.Formatter(string_format, '%Y-%m-%d %H:%M:%S')
# Keep logs separated (and filtered)
# all.log depends on level specified on configuration file
all_log = logging.FileHandler(os.path.join(self.logdir, 'all.log'),
mode='a+')
all_log.setLevel(logging.getLevelName(self.loglevel))
all_log.setFormatter(formatter)
debug_log = logging.FileHandler(os.path.join(self.logdir, 'debug.log'),
mode='a+')
debug_log.setLevel('DEBUG')
debug_log.addFilter(SingleLevelFilter(logging.DEBUG, False))
debug_log.setFormatter(formatter)
info_log = logging.FileHandler(os.path.join(self.logdir, 'info.log'),
mode='a+')
info_log.setLevel('INFO')
info_log.addFilter(SingleLevelFilter(logging.INFO, False))
info_log.setFormatter(formatter)
warn_log = logging.FileHandler(os.path.join(self.logdir, 'warn.log'),
mode='a+')
warn_log.setLevel('WARNING')
warn_log.addFilter(SingleLevelFilter(logging.WARNING, False))
warn_log.setFormatter(formatter)
error_log = logging.FileHandler(os.path.join(self.logdir, 'error.log'),
mode='a+')
error_log.setLevel('ERROR')
error_log.addFilter(SingleLevelFilter(logging.ERROR, False))
error_log.setFormatter(formatter)
logger.addHandler(all_log)
logger.addHandler(info_log)
logger.addHandler(debug_log)
logger.addHandler(warn_log)
logger.addHandler(error_log)
self.logger = logger
self.logger.setLevel(logging.getLevelName(self.loglevel))
logger.debug('Redirecting logging to %s' % self.logdir)
# Stop logging on stdout from now on
logger.propagate = False
self.logger.debug("New smtp object created")
def _log_request(self):
"""
Logs a given request
This should be called when something goes wrong. It saves the
email content that triggered the malfunctioning
Raises:
- RuntimeError: if something goes wrong while trying to save the
email
"""
self.logger.debug("Logging the mail content...")
def _check_blacklist(self):
"""
Check if an email is blacklisted
It opens the corresponding blacklist file and search for the
sender address.
Raises:
- BlacklistError: if the user is blacklisted.
"""
self.logger.debug("Checking if address %s is blacklisted" %
self.from_addr)
def _get_locale(self):
"""
Get the locale from an email address
It process the email received and look for the locale in the
recipient address (e.g. gettor+en@torproject.org)
If no locale found, english by default
Returns a string containing the locale
"""
self.logger.debug("Trying to obtain locale from recipient address")
# If no match found, english by default
locale = 'en'
# Look for word+locale@something
# Should we specify gettor and torproject?
m = re.match('\w+\+(\w\w)@', self.to_addr)
if m:
self.logger.debug("Request for locale %s" % m.groups())
locale = "%s" % m.groups()
return locale
def _parse_email(self):
"""
Parses the email received
It obtains the locale and parse the text for the rest of the info
Returns a 4-tuple with locale, package, os and type
"""
self.logger.debug("Parsing email")
locale = self._get_locale()
req_type, req_pkg, req_os = self._parse_text()
request = {}
request['locale'] = locale
request['package'] = req_pkg
request['type'] = req_type
request['os'] = req_os
return request
def _parse_text(self):
"""
Parses the text part of the email received
It tries to figure out what the user is asking, namely, the type
of request, the package and os required (if applies)
Returns a 3-tuple with the type of request, package and os
"""
self.logger.debug("Parsing email text part")
return ('links', 'pkg', 'linux')
def _create_email(self, msg):
"""
Creates an email object
This object will be used to construct the reply
Returns the email object
"""
self.logger.debug("Creating email object for replying")
def _send_email(self, msg):
"""
Send email with msg as content
"""
self._create_email(msg)
self.logger.debug("Email sent")
def _send_delay(self):
"""
Send delay message
If delay is setted on configuration, then sends a reply to the
user saying that the package is on the way
"""
self.logger.debug("Sending delay message...")
self._send_email("delay")
def _send_links(self, links):
"""
Send the links to the user
"""
self.logger.debug("Sending links...")
self._send_email(links)
def _send_help(self):
"""
Send help message to the user
"""
self.logger.debug("Sending help...")
self._send_email("help")
def process_email(self, raw_msg):
"""
Process the email received.
It create an email object from the string received. The processing
flow is as following:
- Check for blacklisted address
- Parse the email
- Check the type of request
- Send reply
Raise:
- ValueError if the address is blacklisted, or if the request
asks for unsupported locales and/or operating systems, or if
it's not possible to recognize what type of request (help, links)
the user is asking
- InternalError if something goes wrong while trying to obtain
the links from the Core
"""
self.parsed_msg = email.message_from_string(raw_msg)
# Just for easy access
# Normalize pending
self.from_addr = self.parsed_msg['From']
self.to_addr = self.parsed_msg['To']
# We have the info we need on self.parsed_msg
try:
self._check_blacklist()
except ValueError as e:
raise ValueError("The address %s is blacklisted!" %
self.from_addr)
# Try to figure out what the user is asking
request = self._parse_email()
# Two possible options: asking for help or for the links
# If not, it means malformed message, and no default values
self.logger.info("New request for %s" % request['type'])
if request['type'] == 'help':
self._send_help(request['locale'])
elif request['type'] == 'links':
if self.delay:
self._send_delay()
try:
self.logger.info("Asking Core for links in %s for %s" %
(request['locale'], request['os']))
# links = self.core.get_links(request['os'], request['locale'])
links = "dummy links"
self._send_links(links)
except ValueError as e:
raise ValueError(str(e))
except RuntimeError as e:
raise RuntimeError(str(e))
else:
raise ValueError("Malformed message. No default values either")
[ DEBUG] 2014-07-01 19:32:28 - Redirecting logging to smtp/log/
[ DEBUG] 2014-07-01 19:32:28 - New smtp object created
[ DEBUG] 2014-07-01 19:32:28 - Checking if address "Jacob Applebaum" <ioerror@gmail.com> is blacklisted
[ DEBUG] 2014-07-01 19:32:28 - Parsing email
[ DEBUG] 2014-07-01 19:32:28 - Trying to obtain locale from recipient address
[ DEBUG] 2014-07-01 19:32:28 - Request for locale en
[ DEBUG] 2014-07-01 19:32:28 - Parsing email text part
[ INFO] 2014-07-01 19:32:28 - New request for links
[ DEBUG] 2014-07-01 19:32:28 - Sending delay message...
[ DEBUG] 2014-07-01 19:32:28 - Creating email object for replying
[ DEBUG] 2014-07-01 19:32:28 - Email sent
[ INFO] 2014-07-01 19:32:28 - Asking Core for links in en for linux
[ DEBUG] 2014-07-01 19:32:28 - Sending links...
[ DEBUG] 2014-07-01 19:32:28 - Creating email object for replying
[ DEBUG] 2014-07-01 19:32:28 - Email sent
[ DEBUG] 2014-07-01 19:32:28 - Redirecting logging to smtp/log/
[ DEBUG] 2014-07-01 19:32:28 - New smtp object created
[ DEBUG] 2014-07-01 19:32:28 - Checking if address "Jacob Applebaum" <ioerror@gmail.com> is blacklisted
[ DEBUG] 2014-07-01 19:32:28 - Parsing email
[ DEBUG] 2014-07-01 19:32:28 - Trying to obtain locale from recipient address
[ DEBUG] 2014-07-01 19:32:28 - Request for locale en
[ DEBUG] 2014-07-01 19:32:28 - Parsing email text part
[ DEBUG] 2014-07-01 19:32:28 - Sending delay message...
[ DEBUG] 2014-07-01 19:32:28 - Creating email object for replying
[ DEBUG] 2014-07-01 19:32:28 - Email sent
[ DEBUG] 2014-07-01 19:32:28 - Sending links...
[ DEBUG] 2014-07-01 19:32:28 - Creating email object for replying
[ DEBUG] 2014-07-01 19:32:28 - Email sent
[ INFO] 2014-07-01 19:32:28 - New request for links
[ INFO] 2014-07-01 19:32:28 - Asking Core for links in en for linux
X-Account-Key: account6
X-UIDL: 1214981061.25808.faustus,S=2285
X-Mozilla-Status: 0001
X-Mozilla-Status2: 02000000
Return-Path: <ioerror@gmail.com>
Delivered-To: jpopped@appelbaum.net
Received: (qmail 25806 invoked by uid 89); 2 Jul 2008 06:44:21 -0000
Delivered-To: appelbaum.net-jacob@appelbaum.net
Received: (qmail 25804 invoked by uid 89); 2 Jul 2008 06:44:21 -0000
Received: from unknown (HELO wa-out-1112.google.com) (209.85.146.180)
by 0 with SMTP; 2 Jul 2008 06:44:21 -0000
Received-SPF: pass (0: SPF record at _spf.google.com designates 209.85.146.180 as permitted sender)
Received: by wa-out-1112.google.com with SMTP id j40so170432wah.1
for <jacob@appelbaum.net>; Tue, 01 Jul 2008 23:42:01 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=gmail.com; s=gamma;
h=domainkey-signature:received:received:message-id:date:from:to
:subject:mime-version:content-type;
bh=IvFqNkffeoST7vamh2ytuq/b7GpLhg2hStTrQq3I3rE=;
b=xQR0hE/J4AXpAqH1UDXTtDrU9Izc6WM8vtFudRBzldWYyRx3Vvfh2I2Opu8+O6wbAv
jlDi18anUMbZqlIGSgGOxvXW4CltpX/SFZm1aGL4AisQ1Bi5xEqlrc8OnX3sA2xKeM3g
KWsWm+GVSMI4XAqnY9FYAfPx4DmOAfkdMyWCU=
DomainKey-Signature: a=rsa-sha1; c=nofws;
d=gmail.com; s=gamma;
h=message-id:date:from:to:subject:mime-version:content-type;
b=kyzDtGRDbiC5y4Bz/ylQjyHOChiOP2A6QDzybsVXc0C1hjHLImOQYR8gOxcRY+mRkN
1xpBaEF4UloZAxTb79khRRp4TWmjT1DagtLx2MFzIj/F6awtdE/9U3p4QyKr8S43tGcE
ET26BSfT5u9zrXblVVAP3JedMPZ8mlIGQxyDs=
Received: by 10.115.90.1 with SMTP id s1mr6711509wal.51.1214980921268;
Tue, 01 Jul 2008 23:42:01 -0700 (PDT)
Received: by 10.114.184.16 with HTTP; Tue, 1 Jul 2008 23:41:57 -0700 (PDT)
Message-ID: <7fadd8130807012341n3b3af401mbdb4a29c80310bd3@mail.gmail.com>
Date: Tue, 1 Jul 2008 23:41:57 -0700
From: "Jacob Applebaum" <ioerror@gmail.com>
To: gettor+en@torproject.org
Subject: Positive DKIM header
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="----=_Part_462_28562233.1214980917793"
------=_Part_462_28562233.1214980917793
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
This email should have a positive DKIM header.
------=_Part_462_28562233.1214980917793
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
This email should have a positive DKIM header.<br>
------=_Part_462_28562233.1214980917793--
#!/usr/bin/env python
import sys
import smtp
service = smtp.SMTP('smtp.cfg')
# For now we simulate mails reading from stdin
# In linux test as follows:
# $ python smtp_demo.py < email.eml
incoming = sys.stdin.read()
try:
print "Email received!"
service.process_email(incoming)
print "Email sent!"
except ValueError as e:
print "Value error: " + str(e)
except RuntimeError as e:
print "Runtime error: " + str(e)
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment