[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [gettor/master] Progress for status update August 1st. New code structure and XMPP module.
commit 143129d8381b40c373de8537dfd1ec9e11296f05
Author: ilv <ilv@xxxxxxxxxxxxxxxxxxxxxxxx>
Date: Fri Aug 1 20:26:14 2014 -0400
Progress for status update August 1st. New code structure and XMPP module.
---
src/core_demo.py | 16 +-
src/dropbox.py | 139 +++++++-----
src/gettor/core.py | 402 +++++++++++++++++++++++++++++++++
src/gettor/smtp.py | 612 +++++++++++++++++++++++++++++++++++++++++++++++++++
src/gettor/utils.py | 87 ++++++++
src/gettor/xmpp.py | 378 +++++++++++++++++++++++++++++++
src/smtp_demo.py | 14 +-
src/xmpp.cfg | 11 +
src/xmpp_demo.py | 7 +
9 files changed, 1598 insertions(+), 68 deletions(-)
diff --git a/src/core_demo.py b/src/core_demo.py
index c783517..6298f70 100644
--- a/src/core_demo.py
+++ b/src/core_demo.py
@@ -1,15 +1,19 @@
#!/usr/bin/python
#
-# Dummy script to test GetTore's Core module progress
+# Dummy script to test GetTore's Core module
#
-import gettor
+import gettor.core
try:
- core = gettor.Core('gettor.cfg')
+ core = gettor.core.Core()
links = core.get_links('dummy service', 'linux', 'es')
print links
-except ValueError as e:
- print "Value error: " + str(e)
-except RuntimeError as e:
+except gettor.core.ConfigurationError as e:
+ print "Misconfiguration: " + str(e)
+except gettor.core.UnsupportedOSError as e:
+ print "Unsupported OS: " + str(e)
+except gettor.core.UnsupportedLocaleError as e:
+ print "Unsupported Locale: " + str(e)
+except gettor.core.InternalError as e:
print "Internal error: " + str(e)
diff --git a/src/dropbox.py b/src/dropbox.py
index 08084ef..1be1ae8 100644
--- a/src/dropbox.py
+++ b/src/dropbox.py
@@ -1,9 +1,13 @@
-#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# This file is part of GetTor, a Tor Browser Bundle distribution system.
+#
import re
import os
import gnupg
+import hashlib
import dropbox
-import gettor
+import gettor.core
def valid_bundle_format(file):
@@ -24,12 +28,12 @@ def valid_bundle_format(file):
def get_bundle_info(file):
- """
- Get the operating system and locale from a bundle string.
+ """Get the operating system and locale from a bundle string.
+
+ it raises a ValueError if the bundle doesn't have a valid format
+ (although you should probably call valid_bundle_format first).
+ It returns the pair of strings operating system, locale.
- it raises a ValueError if the bundle doesn't have a valid format
- (although you should probably call valid_bundle_format first).
- It returns the pair of strings operating system, locale.
"""
m = re.search(
'tor-browser-(\w+)\d\d-\d\.\d\.\d_(\w\w)-\w+\.tar\.xz',
@@ -41,17 +45,33 @@ def get_bundle_info(file):
else:
raise ValueError("Bundle invalid format %s" % file)
+def get_file_sha1(file):
+ """Get the sha1 of a file.
+
+ Desc.
+
+ """
+
+ # as seen on the internet
+ BLOCKSIZE = 65536
+ hasher = hashlib.sha1()
+ with open(file, 'rb') as afile:
+ buf = afile.read(BLOCKSIZE)
+ while len(buf) > 0:
+ hasher.update(buf)
+ buf = afile.read(BLOCKSIZE)
+ return hasher.hexdigest()
def upload_files(basedir, client):
- """
- Upload files from 'basedir' to Dropbox.
+ """Upload files from 'basedir' to Dropbox.
+
+ It looks for files ending with 'tar.xz' inside 'basedir'. It
+ raises ValueError in case the given file doesn't have a .asc file.
+ It raises UploadError if something goes wrong while uploading the
+ files to Dropbox. All files are uploaded to '/'.
- It looks for files ending with 'tar.xz' inside 'basedir'. It
- raises ValueError in case the given file doesn't have a .asc file.
- It raises UploadError if something goes wrong while uploading the
- files to Dropbox. All files are uploaded to '/'.
+ Returns a list with the names of the uploaded files.
- Returns a list with the names of the uploaded files.
"""
files = []
@@ -87,46 +107,53 @@ def upload_files(basedir, client):
return files
-# Test app for now
-# TO DO: use config file
-app_key = ''
-app_secret = ''
-access_token = ''
-upload_dir = 'upload/'
-tbb_key = 'tbb-key.asc'
-
-client = dropbox.client.DropboxClient(access_token)
-
-# Import key that signed the packages and get fingerprint
-gpg = gnupg.GPG()
-key_data = open(tbb_key).read()
-import_result = gpg.import_keys(key_data)
-fingerprint = import_result.results[0]['fingerprint']
-# Make groups of four characters to make fingerprint more readable
-# e.g. 123A 456B 789C 012D 345E 678F 901G 234H 567I 890J
-readable = ' '.join(fingerprint[i:i+4] for i in xrange(0, len(fingerprint), 4))
-
-try:
- uploaded_files = upload_files(upload_dir, client)
- core = gettor.Core('gettor.cfg')
- # This erases the old links file
- core.create_links_file('Dropbox', readable)
-
- for file in uploaded_files:
- # build file names
- asc = file + '.asc'
- abs_file = os.path.abspath(os.path.join(upload_dir, file))
- abs_asc = os.path.abspath(os.path.join(upload_dir, asc))
-
- # build links
- link_file = client.share(file)
- link_asc = client.share(asc)
- link = link_file[u'url'] + ' ' + link_asc[u'url']
+if __name__ == '__main__':
+ # to-do: use config file
+ app_key = ''
+ app_secret = ''
+ access_token = ''
+ upload_dir = 'upload/'
+
+ # important: this must be the key that signed the packages
+ tbb_key = 'tbb-key.asc'
+
+ client = dropbox.client.DropboxClient(access_token)
+
+ # import key fingerprint
+ gpg = gnupg.GPG()
+ key_data = open(tbb_key).read()
+ import_result = gpg.import_keys(key_data)
+ fp = import_result.results[0]['fingerprint']
+
+ # make groups of four characters to make fingerprint more readable
+ # e.g. 123A 456B 789C 012D 345E 678F 901G 234H 567I 890J
+ readable = ' '.join(fp[i:i+4] for i in xrange(0, len(fp), 4))
+
+ try:
+ uploaded_files = upload_files(upload_dir, client)
+ # use default config
+ core = gettor.core.Core()
- # add links
- operating_system, locale = get_bundle_info(file)
- core.add_link('Dropbox', operating_system, locale, link)
-except (ValueError, RuntimeError) as e:
- print str(e)
-except dropbox.rest.ErrorResponse as e:
- print str(e)
+ # erase old links
+ core.create_links_file('Dropbox', readable)
+
+ for file in uploaded_files:
+ # build file names
+ asc = file + '.asc'
+ abs_file = os.path.abspath(os.path.join(upload_dir, file))
+ abs_asc = os.path.abspath(os.path.join(upload_dir, asc))
+
+ sha1_file = get_file_sha1(abs_file)
+
+ # build links
+ link_file = client.share(file)
+ link_asc = client.share(asc)
+ link = link_file[u'url'] + ' ' + link_asc[u'url'] + ' ' + sha1_file
+
+ # add links
+ operating_system, locale = get_bundle_info(file)
+ core.add_link('Dropbox', operating_system, locale, link)
+ except (ValueError, RuntimeError) as e:
+ print str(e)
+ except dropbox.rest.ErrorResponse as e:
+ print str(e)
diff --git a/src/gettor/core.py b/src/gettor/core.py
new file mode 100644
index 0000000..407e080
--- /dev/null
+++ b/src/gettor/core.py
@@ -0,0 +1,402 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of GetTor, a Tor Browser Bundle distribution system.
+#
+
+import os
+import re
+import inspect
+import logging
+import tempfile
+import ConfigParser
+
+import utils
+
+"""Core module for getting links from providers."""
+
+
+class ConfigurationError(Exception):
+ pass
+
+
+class UnsupportedOSError(Exception):
+ pass
+
+
+class UnsupportedLocaleError(Exception):
+ pass
+
+
+class LinkFormatError(Exception):
+ pass
+
+
+class LinkFileError(Exception):
+ pass
+
+
+class InternalError(Exception):
+ pass
+
+
+class Core(object):
+ """Get links from providers and deliver them to other modules.
+
+ Public methods:
+
+ get_links(): Get the links for the OS and locale requested.
+ create_links_file(): Create a file to store links of a provider.
+ add_link(): Add a link to a links file of a provider.
+ get_supported_os(): Get a list of supported operating systems.
+ get_supported_locale(): Get a list of supported locales.
+
+ Exceptions:
+
+ UnsupportedOSError: Request for an unsupported operating system.
+ UnsupportedLocaleError: Request for an unsupported locale.
+ ConfigurationError: Something's misconfigured.
+ LinkFormatError: The link added doesn't seem legit.
+ LinkFileError: Error related to the links file of a provider.
+ InternalError: Something went wrong internally.
+
+ """
+
+ def __init__(self, cfg=None):
+ """Create a new core object by reading a configuration file.
+
+ Raises: ConfigurationError if the configuration file doesn't exists
+ or if something goes wrong while reading options from it.
+
+ Params: cfg - path of the configuration file.
+
+ """
+ # Define a set of default values
+ DEFAULT_CONFIG_FILE = 'core.cfg'
+
+ logging.basicConfig(format='[%(levelname)s] %(asctime)s - %(message)s',
+ datefmt="%Y-%m-%d %H:%M:%S")
+ logger = logging.getLogger(__name__)
+ config = ConfigParser.ConfigParser()
+
+ if cfg is None or not os.path.isfile(cfg):
+ cfg = DEFAULT_CONFIG_FILE
+ logger.info("Using default configuration")
+
+ logger.info("Reading configuration file %s" % cfg)
+ config.read(cfg)
+
+ try:
+ self.basedir = config.get('general', 'basedir')
+ except ConfigParser.Error as e:
+ logger.warning("Couldn't read 'basedir' from 'general' (%s)" % cfg)
+ raise ConfigurationError("Error with conf. See log file.")
+
+ try:
+ self.linksdir = config.get('links', 'dir')
+ self.linksdir = os.path.join(self.basedir, self.linksdir)
+ except ConfigParser.Error as e:
+ logger.warning("Couldn't read 'links' from 'dir' (%s)" % cfg)
+ raise ConfigurationError("Error with conf. See log file.")
+
+ try:
+ self.supported_locales = config.get('links', 'locales')
+ except ConfigParser.Error as e:
+ logger.warning("Couldn't read 'locales' from 'links' (%s)" % cfg)
+ raise ConfigurationError("Error with conf. See log file.")
+
+ try:
+ self.supported_os = config.get('links', 'os')
+ except ConfigParser.Error as e:
+ logger.warning("Couldn't read 'os' from 'links' (%s)" % cfg)
+ raise ConfigurationError("Error with conf. See log file.")
+
+ try:
+ self.loglevel = config.get('log', 'level')
+ except ConfigParser.Error as e:
+ logger.warning("Couldn't read 'level' from 'log' (%s)" % cfg)
+ raise ConfigurationError("Error with conf. See log file.")
+
+ try:
+ self.logdir = config.get('log', 'dir')
+ self.logdir = os.path.join(self.basedir, self.logdir)
+ except ConfigParser.Error as e:
+ logger.warning("Couldn't read 'dir' from 'log' %s)" % cfg)
+ raise ConfigurationError("Error with conf. See log file.")
+
+ # Keep log levels separated
+ self.logger = utils.filter_logging(logger, self.logdir, self.loglevel)
+ # self.logger.setLevel(logging.getLevelName(self.loglevel))
+ self.logger.info('Redirecting logging to %s' % self.logdir)
+
+ # Stop logging on stdout from now on
+ self.logger.propagate = False
+ self.logger.debug("New core object created")
+
+ def get_links(self, service, operating_system, locale):
+ """Get links for OS in locale.
+
+ This method should be called from the services modules of
+ GetTor (e.g. SMTP). To make it easy we let the module calling us
+ specify the name of the service (for stats purpose).
+
+ Raises: UnsupportedOSError: if the operating system is not supported.
+ UnsupportedLocaleError: if the locale is not supported.
+ InternalError: if something goes wrong while internally.
+
+ Params: service - the name of the service trying to get the links.
+ operating_system - the name of the operating system.
+ locale - two-character string representing the locale.
+
+ Returns: String with links.
+
+ """
+
+ # Which module called us and what was asking for?
+ self.logger.info("%s did a request for %s, %s." %
+ (service, operating_system, locale))
+
+ if locale not in self.supported_locales:
+ self.logger.warning("Request for unsupported locale: %s" % locale)
+ raise UnsupportedLocaleError("Locale %s not supported at the "
+ "moment" % locale)
+
+ if operating_system not in self.supported_os:
+ self.logger.warning("Request for unsupported operating system: %s"
+ % operating_system)
+ raise UnsupportedOSError("Operating system %s not supported at the"
+ "moment" % operating_system)
+
+ # This could change in the future, let's leave it isolated.
+ links = self._get_links(operating_system, locale)
+
+ if links is None:
+ self.logger.error("Couldn't get the links", exc_info=True)
+ raise InternalError("Something went wrong internally. See logs for"
+ " detailed info.")
+
+ self.logger.info("Returning the links")
+ return links
+
+ def _get_links(self, operating_system, locale):
+ """Internal method to get the links.
+
+ Looks for the links inside each provider file. This should only be
+ called from get_links() method.
+
+ Returns: String with the links on success.
+ None on failure.
+
+ Params: operating_system - name of the operating system
+ locale: two-character string representing the locale.
+
+ """
+
+ # Read the links files using ConfigParser
+ # See the README for more details on the format used
+ links = []
+
+ # Look for files ending with .links
+ p = re.compile('.*\.links$')
+
+ for name in os.listdir(self.linksdir):
+ path = os.path.abspath(os.path.join(self.linksdir, name))
+ if os.path.isfile(path) and p.match(path):
+ links.append(path)
+
+ # Let's create a dictionary linking each provider with the links
+ # found for operating_system and locale. This way makes it easy
+ # to check if no links were found
+ providers = {}
+
+ self.logger.info("Reading links from providers directory")
+ for name in links:
+ self.logger.debug("Reading %s" % name)
+ # We're reading files listed on linksdir, so they must exist!
+ config = ConfigParser.ConfigParser()
+ config.read(name)
+
+ try:
+ pname = config.get('provider', 'name')
+ except ConfigParser.Error as e:
+ self.logger.warning("Couldn't get 'name' from 'provider' (%s)"
+ % name)
+ raise InternalError("Error while reading %s links file. See "
+ "log file" % name)
+
+ self.logger.debug("Checking if %s has links for %s in %s" %
+ (pname, operating_system, locale))
+
+ try:
+ providers[pname] = config.get(operating_system, locale)
+ except ConfigParser.Error as e:
+ self.logger.warning("Couldn't get %s from %s (%s)" %
+ (locale, operating_system, name))
+ raise InternalError("Error while reading %s links file. See "
+ "log file" % name)
+
+ # Each provider must have a fingerprint of the key used to
+ # sign the uploaded packages
+ try:
+ self.logger.debug("Trying to get fingerprint from %s", pname)
+ fingerprint = config.get('key', 'fingerprint')
+ providers[pname] = providers[pname] + "\nFingerprint: "
+ providers[pname] = providers[pname] + fingerprint
+ self.logger.debug("Fingerprint added %s", fingerprint)
+ except ConfigParser.Error as e:
+ self.logger.warning("Couldn't get 'fingerprint' from 'key' "
+ "(%s)" % name)
+ raise InternalError("Error while reading %s links file. See "
+ "log file" % name)
+
+ # Create the final links list with all providers
+ all_links = []
+
+ self.logger.debug("Joining all links found for %s in %s" %
+ (operating_system, locale))
+ for key in providers.keys():
+ all_links.append(
+ "\n%s\n%s\n" % (key, ''.join(providers[key]))
+ )
+
+ if all_links:
+ return "".join(all_links)
+ else:
+ self.logger.warning("Trying to get supported os and locales, but"
+ " no links were found")
+ return None
+
+ def get_supported_os(self):
+ """Public method to get the list of supported operating systems.
+
+ Returns: List of strings.
+
+ """
+ return self.supported_os.split(',')
+
+ def get_supported_locales(self):
+ """Public method to get the list of supported locales.
+
+ Returns: List of strings.
+
+ """
+ return self.supported_locales.split(',')
+
+ def create_links_file(self, provider, fingerprint):
+ """Public method to create a links file for a provider.
+
+ This should be used by all providers since it writes the links
+ file with the proper format. It backs up the old links file
+ (if exists) and creates a new one.
+
+ Params: provider - provider's name (links file will use this
+ name in lower case).
+
+ fingerprint: fingerprint of the key that signed the packages
+ to be uploaded to the provider.
+
+ """
+ linksfile = os.path.join(self.linksdir, provider.lower() + '.links')
+ linksfile_backup = ""
+ self.logger.info("Request to create new %s" % linksfile)
+
+ if os.path.isfile(linksfile):
+ # Backup the old file in case something fails
+ linksfile_backup = linksfile + '.backup'
+ self.logger.info("Backing up %s to %s"
+ % (linksfile, linksfile_backup))
+ os.rename(linksfile, linksfile_backup)
+
+ try:
+ # This creates an empty links file (with no links)
+ content = ConfigParser.RawConfigParser()
+ content.add_section('provider')
+ content.set('provider', 'name', provider)
+ content.add_section('key')
+ content.set('key', 'fingerprint', fingerprint)
+ content.add_section('linux')
+ content.add_section('windows')
+ content.add_section('osx')
+ with open(linksfile, 'w+') as f:
+ content.write(f)
+ self.logger.info("New %s created" % linksfile)
+ except Exception as e:
+ if linksfile_backup:
+ os.rename(linksfile_backup, linksfile)
+ raise LinkFileError("Error while trying to create new links file.")
+
+ def add_link(self, provider, operating_system, locale, link):
+ """Public method to add a link to a provider's links file.
+
+ Use ConfigParser to add a link into the operating_system
+ section, under the locale option. It checks for valid format;
+ the provider's script should use the right format (see design).
+
+ Raises: UnsupportedOSError: if the operating system is not supported.
+ UnsupportedLocaleError: if the locale is not supported.
+ LinkFileError: if there is no links file for the provider.
+ LinkFormatError: if the link format doesn't seem legit.
+ InternalError: if the links file doesn't have a section for the
+ OS requested. This *shouldn't* happen because
+ it means the file wasn't created correctly.
+
+ Params: provider - name of the provider.
+ operating_system - name of the operating system.
+ locale - two-character string representing the locale.
+ link - string to be added. The format should be as follows:
+
+ https://pkg_url https://asc_url
+
+ where pkg_url is the url for the bundle and asc_url is the
+ url for the asc of the bundle.
+
+ """
+ linksfile = os.path.join(self.linksdir, provider.lower() + '.links')
+
+ # Don't try to add unsupported stuff
+ if locale not in self.supported_locales:
+ self.logger.warning("Trying to add link for unsupported locale: %s"
+ % locale)
+ raise UnsupportedLocaleError("Locale %s not supported at the "
+ "moment" % locale)
+
+ if operating_system not in self.supported_os:
+ self.logger.warning("Trying to add link for unsupported operating "
+ "system: %s" % operating_system)
+ raise UnsupportedOSError("Operating system %s not supported at the"
+ " moment" % operating_system)
+
+ # Check if the link has a legit format
+ # e.g. https://db.tt/JjfUTb04 https://db.tt/MEfUTb04
+ p = re.compile('^https://.+\shttps://.+$')
+
+ if not p.match(link):
+ self.logger.warning("Trying to add an invalid link: %s"
+ % link)
+ raise LinkFormatError("Link '%s' doesn't seem to have a valid "
+ "format" % link)
+
+ if os.path.isfile(linksfile):
+ content = ConfigParser.RawConfigParser()
+ content.readfp(open(linksfile))
+ # Check if exists and entry for locale; if not, create it
+ try:
+ links = content.get(operating_system, locale)
+ links = links + ",\n" + link
+ content.set(operating_system, locale, links)
+ with open(linksfile, 'w') as f:
+ content.write(f)
+ self.logger.info("Link %s added to %s %s in %s"
+ % (link, operating_system, locale, provider))
+ except ConfigParser.NoOptionError:
+ content.set(operating_system, locale, link)
+ with open(linksfile, 'w') as f:
+ content.write(f)
+ self.logger.info("Link %s added to %s-%s in %s"
+ % (link, operating_system, locale, provider))
+ except ConfigParser.NoSectionError:
+ # This shouldn't happen, but just in case
+ self.logger.error("Unknown section %s in links file")
+ raise InternalError("Unknown %s section in links file"
+ % operating_system)
+ else:
+ raise LinkFileError("There is no links file for %s" % provider)
diff --git a/src/gettor/smtp.py b/src/gettor/smtp.py
new file mode 100644
index 0000000..6e684ae
--- /dev/null
+++ b/src/gettor/smtp.py
@@ -0,0 +1,612 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of GetTor, a Tor Browser Bundle distribution system.
+#
+
+
+import os
+import re
+import sys
+import time
+import email
+import gettext
+import hashlib
+import logging
+import ConfigParser
+
+import utils
+import core
+
+"""SMTP module for processing email requests."""
+
+
+class ConfigurationError(Exception):
+ pass
+
+
+class BlacklistError(Exception):
+ pass
+
+
+class AddressError(Exception):
+ pass
+
+
+class SendEmailError(Exception):
+ pass
+
+
+class InternalError(Exception):
+ pass
+
+
+class SMTP(object):
+ """Receive and reply requests by email.
+
+ Public methods:
+
+ process_email(): Process the email received.
+
+ Exceptions:
+
+ ConfigurationError: Bad configuration.
+ BlacklistError: Address of the sender is blacklisted.
+ AddressError: Address of the sender malformed.
+ SendEmailError: SMTP server not responding.
+ InternalError: Something went wrong internally.
+
+ """
+
+ def __init__(self, cfg=None):
+ """Create new object by reading a configuration file.
+
+ Params: cfg - path of the configuration file.
+
+ """
+ # Define a set of default values
+ DEFAULT_CONFIG_FILE = 'smtp.cfg'
+
+ logging.basicConfig(format='[%(levelname)s] %(asctime)s - %(message)s',
+ datefmt="%Y-%m-%d %H:%M:%S")
+ logger = logging.getLogger(__name__)
+ config = ConfigParser.ConfigParser()
+
+ if cfg is None or not os.path.isfile(cfg):
+ cfg = DEFAULT_CONFIG_FILE
+ logger.info("Using default configuration")
+
+ logger.info("Reading configuration file %s" % cfg)
+ config.read(cfg)
+
+ try:
+ self.basedir = config.get('general', 'basedir')
+ except ConfigParser.Error as e:
+ logger.warning("Couldn't read 'basedir' from 'general' (%s)" % cfg)
+ raise ConfigurationError("Error with conf. See log file.")
+
+ try:
+ self.delay = config.get('general', 'delay')
+ # There has to be a better way for doing this...
+ if self.delay == 'False':
+ self.delay = False
+
+ except ConfigParser.Error as e:
+ logger.warning("Couldn't read 'delay' from 'general' (%s)" % cfg)
+ raise ConfigurationError("Error with conf. See log file.")
+
+ try:
+ self.our_addr = config.get('general', 'our_addr')
+ except ConfigParser.Error as e:
+ logger.warning("Couldn't read 'our_addr' from 'general' (%s)" %
+ cfg)
+ raise ConfigurationError("Error with conf. See log file.")
+
+ try:
+ self.logdir = config.get('log', 'dir')
+ self.logdir = os.path.join(self.basedir, self.logdir)
+ except ConfigParser.Error as e:
+ logger.warning("Couldn't read 'dir' from 'log' (%s)" % cfg)
+ raise ConfigurationError("Error with conf. See log file.")
+
+ try:
+ self.logdir_emails = config.get('log', 'emails_dir')
+ self.logdir_emails = os.path.join(self.logdir, self.logdir_emails)
+ except ConfigParser.Error as e:
+ logger.warning("Couldn't read 'emails_dir' from 'log' (%s)" % cfg)
+ raise ConfigurationError("Error with conf. See log file.")
+
+ try:
+ self.loglevel = config.get('log', 'level')
+ except ConfigParser.Error as e:
+ logger.warning("Couldn't read 'level' from 'log' (%s)" % cfg)
+ raise ConfigurationError("Error with conf. See log file.")
+
+ # Use default values
+ self.core = core.Core()
+
+ # Keep log levels separated
+ self.logger = utils.filter_logging(logger, self.logdir, self.loglevel)
+ 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 _get_sha1(self, string):
+ """Get sha1 of a string.
+
+ Used whenever we want to do things with addresses (log, blacklist,
+ etc.)
+
+ Params: The string to be sha1'ed.
+
+ Returns: sha1 of string.
+
+ """
+ return str(hashlib.sha1(string).hexdigest())
+
+ def _log_request(self, addr, raw_msg):
+ """Log a request.
+
+ This should be called when something goes wrong. It saves the
+ email content that triggered the malfunctioning.
+
+ Raises: InternalError: if something goes wrong while trying to
+ save the email.
+
+ Params: addr - The address of the sender.
+ content - The content of the email received.
+
+ """
+
+ # to do: obtain content of msg, not the entire email
+ content = raw_msg
+
+ # We store the sha1 of the original address in order to know when
+ # specific addresses are doing weird requests
+ log_addr = self._get_sha1(addr)
+ filename = str(time.time()) + '.log'
+ path = self.logdir_emails + filename
+ abs_path = os.path.abspath(path)
+
+ if os.path.isfile(abs_path):
+ log_file = open(abs_path, 'w+')
+ log_file.write(content)
+ log_file.close()
+ self.logger.debug("Logging request from %s in %s" %
+ (log_addr, abs_path))
+ else:
+ self.logger.warning("Couldn't open emails' log file (%s)" %
+ abs_path)
+ raise InternalError("Error while saving the email content.")
+
+ def _check_blacklist(self, addr):
+ """Check if an email address is blacklisted.
+
+ Look for the address in the file of blacklisted addresses.
+
+ Raises: BlacklistError if the user is blacklisted.
+
+ Params: addr - the address we want to check.
+
+ """
+ anon_addr = self._get_sha1(addr)
+ self.logger.debug("Checking if address %s is blacklisted" %
+ anon_addr)
+
+ # if blacklisted:
+ # raise BlacklistError("Address %s is blacklisted!" % anon_addr)
+
+ def _get_locale(self, addr):
+ """Get the locale from an email address.
+
+ Process the email received and look for the locale in the recipient
+ address (e.g. gettor+en@xxxxxxxxxxxxxx). If no locale found, english
+ by default.
+
+ Params: The email address we want to get the locale.
+
+ Returns: 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 gettor+locale@xxxxxxxxxxxxxx
+ m = re.match('gettor\+(\w\w)@torproject\.org', addr)
+ if m:
+ self.logger.debug("Request for locale %s" % m.groups())
+ locale = "%s" % m.groups()
+
+ return locale.lower()
+
+ def _get_normalized_address(self, addr):
+ """Get normalized address.
+
+ We look for anything inside the last '<' and '>'. Code taken from
+ the old GetTor (utils.py).
+
+ Raises: AddressError: if address can't be normalized.
+
+ Params: addr - the address we want to normalize.
+
+ Returns: String with the normalized address on success.
+
+ """
+ if '<' in addr:
+ idx = addr.rindex('<')
+ addr = addr[idx:]
+ m = re.search(r'<([^>]*)>', addr)
+ if m is None:
+ raise AddressError("Couldn't extract normalized address "
+ "from %s" % self_get_sha1(addr))
+ addr = m.group(1)
+ return addr
+
+ def _parse_email(self, raw_msg, addr):
+ """Parse the email received.
+
+ Get the locale and parse the text for the rest of the info.
+
+ Params: raw_msg - content of the email to be parsed.
+ addr - address of the recipient (i.e. us).
+
+ Returns: a 3-tuple with locale, os and type.
+
+ """
+ self.logger.debug("Parsing email")
+
+ request = self._parse_text(raw_msg)
+ locale = self._get_locale(addr)
+ request['locale'] = locale
+
+ return request
+
+ def _parse_text(self, raw_msg):
+ """Parse the text part of the email received.
+
+ Try to figure out what the user is asking, namely, the type
+ of request, the package and os required (if applies).
+
+ Params: raw_msg - content of the email to be parsed.
+
+ Returns: Tuple with the type of request and os
+ (None if request is for help).
+ """
+ self.logger.debug("Parsing email text part")
+
+ # By default we asume the request is asking for help
+ request = {}
+ request['type'] = 'help'
+ request['os'] = None
+
+ # core knows what OS are supported
+ supported_os = self.core.get_supported_os()
+
+ lines = raw_msg.split('\n')
+ found_os = False
+ for line in lines:
+ # Check for help request
+ if re.match('.*help.*', line, re.IGNORECASE):
+ self.logger.info("Request for help found")
+ request['type'] = 'help'
+ break
+ # Check for os
+ if not found_os:
+ for supported in supported_os:
+ p = '.*' + supported + '.*'
+ if re.match(p, line, re.IGNORECASE):
+ request['os'] = supported
+ request['type'] = 'links'
+ self.logger.debug("Request for links found")
+ found_os = True
+ break
+ # Check if the user is asking for terms related to pt
+ if re.match("[obfs|plugabble transport|pt]", line, re.IGNORECASE):
+ self.logger.info("Request for PT found")
+ request['pt'] = True
+
+ return request
+
+ def _create_email(self, from_addr, to_addr, subject, msg):
+ """Create an email object.
+
+ This object will be used to construct the reply.
+
+ Params: from_addr - address of the sender.
+ to_addr - address of the recipient.
+ subject - subject of the email.
+ msg - content of the email.
+
+ Returns: The email object.
+
+ """
+ self.logger.debug("Creating email object for replying")
+ # try:
+ # email_obj = MIMEtext(msg)
+ # email_obj['Subject'] = subject
+ # email_obj['From'] = from_addr
+ # email_obj['To'] = to_addr
+
+ reply = "From: " + from_addr + ", To: " + to_addr
+ reply = reply + ", Subject: " + subject + "\n\n" + msg
+
+ # return email_obj
+ return reply
+
+ def _send_email(self, from_addr, to_addr, subject, msg):
+ """Send an email.
+
+ Take a 'from' and 'to' addresses, a subject and the content, creates
+ the email and send it.
+
+ Params: from_addr - address of the sender.
+ to_addr - address of the recipient.
+ subject - subject of the email.
+ msg - content of the email.
+
+ """
+ email_obj = self._create_email(from_addr, to_addr, subject, msg)
+ # try:
+ # s = smtplib.SMTP("localhost")
+ # s.sendmail(from_addr, to_addr, msg.as_string())
+ # s.quit()
+ # except SMTPException as e:
+ # self.logger.error("Couldn't send the email: %s" % str(e))
+ # raise SendEmailError("Error with SMTP: %s" % str(e))
+ print email_obj
+ self.logger.debug("Email sent")
+
+ def _send_delay(self, locale, from_addr, to_addr):
+ """Send delay message.
+
+ If the config says so, send a delay message.
+
+ Params: locale - two-character string describing a locale.
+ from_addr - address of the sender.
+ to_addr - address of the recipient.
+
+ """
+ self.logger.debug("Delay is ON. Sending a delay message.")
+
+ # Obtain the content in the proper language and send it
+ t = gettext.translation(locale, './i18n', languages=[locale])
+ _ = t.ugettext
+
+ delay_subject = _('delay_subject')
+ delay_msg = _('delay_msg')
+
+ try:
+ self._send_email(from_addr, to_addr, delay_subject, delay_msg)
+ except SendEmailError as e:
+ self.logger.warning("Couldn't send delay message")
+ raise InternalError("Error while sending delay message")
+
+ def _send_links(self, links, locale, operating_system, from_addr, to_addr,
+ pt):
+ """Send links to the user.
+
+ Get the message in the proper language (according to the locale),
+ replace variables and send the email.
+
+ Params: links - links to be sent.
+ locale - two-character string describing a locale.
+ from_addr - address of the sender.
+ to_addr - address of the recipient.
+ pt - True/False if the user did a PT request.
+
+ """
+ self.logger.debug("Request for links in %s" % locale)
+
+ # Obtain the content in the proper language and send it
+ t = gettext.translation(locale, './i18n', languages=[locale])
+ _ = t.ugettext
+
+ links_subject = _('links_subject')
+ links_msg = _('links_msg')
+ links_msg = links_msg % (operating_system, locale, links, links)
+
+ # Don't forget to check if user did a PT request
+ if pt:
+ # If so, we get the links message + info about PT included.
+ links_subject = _('links_pt_subject')
+ links_msg = _('links_pt_msg')
+ links_msg = links_msg % (operating_system, locale, links, links)
+
+ try:
+ self._send_email(from_addr, to_addr, links_subject, links_msg)
+ except SendEmailError as e:
+ self.logger.warning("Couldn't send links message")
+ raise InternalError("Error while sending links message")
+
+ def _send_help(self, locale, from_addr, to_addr):
+ """Send help message.
+
+ Get the message in the proper language (according to the locale),
+ replace variables (if any) and send the email.
+
+ Params: locale - two-character string describing a locale.
+ from_addr - address of the sender.
+ to_addr - address of the recipient.
+
+ """
+ self.logger.debug("Request for help in %s" % locale)
+
+ # Obtain the content in the proper language and send it
+ t = gettext.translation(locale, './i18n', languages=[locale])
+ _ = t.ugettext
+
+ help_subject = _('help_subject')
+ help_msg = _('help_msg')
+
+ try:
+ self._send_email(from_addr, to_addr, help_subject, help_msg)
+ except SendEmailError as e:
+ self.logger.warning("Couldn't send help message")
+ raise InternalError("Error while sending help message")
+
+ def _send_unsupported_os(self, operating_system, locale, from_addr,
+ to_addr):
+ """Send unsupported OS message.
+
+ Get the message for unsupported OS in the proper language
+ (according to the locale, or in english if the locale is
+ unsupported too), replace variables (if any) and send the email.
+
+ Params: locale - two-character string describing a locale.
+ from_addr - address of the sender.
+ to_addr - address of the recipient.
+
+ """
+ # Check if the locale is unsupported too
+ # If so, english by default
+ supported_locales = self.core.get_supported_locales()
+ if locale not in supported_locales:
+ locale = 'en'
+
+ # Obtain the content in the proper language and send it
+ t = gettext.translation(locale, './i18n', languages=[locale])
+ _ = t.ugettext
+
+ unsupported_os_subject = _('unsupported_os_subject')
+ unsupported_os_msg = _('unsupported_os_msg')
+ unsupported_os_msg = unsupported_os_msg % operating_system
+
+ try:
+ self._send_email(from_addr, to_addr, unsupported_os_subject,
+ unsupported_os_msg)
+ except SendEmailError as e:
+ self.logger.warning("Couldn't send unsupported OS message")
+ raise InternalError("Error while sending unsupported OS message")
+
+ def _send_unsupported_locale(self, locale, operating_system, from_addr,
+ to_addr):
+ """Send unsupported locale message.
+
+ Get the message for unsupported locale in english replace variables
+ (if any) and send the email.
+
+ Params: operating_system - name of the operating system.
+ from_addr - address of the sender.
+ to_addr - address of the recipient.
+
+ """
+
+ # Obtain the content in english and send it
+ t = gettext.translation(locale, './i18n', languages=['en'])
+ _ = t.ugettext
+
+ unsupported_locale_subject = _('unsupported_locale_subject')
+ unsupported_locale_msg = _('unsupported_locale_msg')
+ unsupported_locale_msg = unsupported_locale_msg % locale
+
+ try:
+ self._send_email(from_addr, to_addr, unsupported_locale_subject,
+ unsupported_locale_msg)
+ except SendEmailError as e:
+ self.logger.warning("Couldn't send unsupported locale message")
+ raise InternalError("Error while sending unsupported locale"
+ "message")
+
+ def process_email(self, raw_msg):
+ """Process the email received.
+
+ 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.
+
+ Raises: InternalError if something goes wrong while asking for the
+ links to the Core module.
+
+ Params: raw_msg - the email received.
+
+ """
+ parsed_msg = email.message_from_string(raw_msg)
+ from_addr = parsed_msg['From']
+ to_addr = parsed_msg['To']
+ bogus_request = False
+
+ try:
+ norm_from_addr = self._get_normalized_address(from_addr)
+ except AddressError as e:
+ # This shouldn't stop us from receiving other requests
+ self.logger.warning(str(e))
+ bogus_request = True
+
+ if norm_from_addr:
+ try:
+ self._check_blacklist(self._get_sha1(norm_from_addr))
+ except BlacklistError as e:
+ # This shouldn't stop us from receiving other requests
+ self.logger.warning(str(e))
+ bogus_request = True
+
+ if not bogus_request:
+ # Try to figure out what the user is asking
+ request = self._parse_email(raw_msg, to_addr)
+
+ # Two possible options: asking for help or for the links
+ self.logger.info("New request for %s" % request['type'])
+ if request['type'] == 'help':
+ # make sure we can send emails
+ try:
+ self._send_help(request['locale'], self.our_addr,
+ norm_from_addr)
+ except SendEmailError as e:
+ raise InternalError("Something's wrong with the SMTP "
+ "server: %s" % str(e))
+
+ elif request['type'] == 'links':
+ if self.delay:
+ # make sure we can send emails
+ try:
+ self._send_delay(request['locale'], self.our_addr,
+ norm_from_addr)
+ except SendEmailError as e:
+ raise InternalError("Something's wrong with the SMTP "
+ "server: %s" % str(e))
+
+ try:
+ self.logger.info("Asking core for links in %s for %s" %
+ (request['locale'], request['os']))
+
+ links = self.core.get_links('SMTP', request['os'],
+ request['locale'])
+
+ except UnsupportedOSError as e:
+ self.logger.info("Request for unsupported OS: %s (%s)" %
+ (request['os'], str(e)))
+ # if we got here, the address of the sender should be valid
+ # so we send him/her a message about the unsupported OS
+ self._send_unsupported_os(request['os'], request['locale'],
+ self.our_addr, norm_from_addr)
+
+ except UnsupportedLocaleError as e:
+ self.logger.info("Request for unsupported locale: %s (%s)"
+ % (request['locale'], str(e)))
+ # if we got here, the address of the sender should be valid
+ # so we send him/her a message about the unsupported locale
+ self._send_unsupported_locale(request['locale'],
+ request['os'], self.our_addr,
+ norm_from_addr)
+
+ # if core fails, we fail too
+ except (InternalError, ConfigurationError) as e:
+ self.logger.error("Something's wrong with the Core module:"
+ " %s" % str(e))
+ raise InternalError("Error obtaining the links.")
+
+ # make sure we can send emails
+ try:
+ self._send_links(links, request['locale'], request['os'],
+ self.our_addr, norm_from_addr)
+ except SendEmailError as e:
+ raise SendEmailError("Something's wrong with the SMTP "
+ "server: %s" % str(e))
diff --git a/src/gettor/utils.py b/src/gettor/utils.py
new file mode 100644
index 0000000..cc927fe
--- /dev/null
+++ b/src/gettor/utils.py
@@ -0,0 +1,87 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of GetTor, a Tor Browser Bundle distribution system.
+#
+
+import os
+import logging
+
+"""Common utilities for GetTor modules."""
+
+class SingleLevelFilter(logging.Filter):
+ """Filter logging levels to create separated logs.
+
+ Public methods:
+
+ filter(): fitler logging levels.
+
+ """
+
+ def __init__(self, passlevel, reject):
+ """Create 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.
+
+ Params: passlevel - name of a logging level.
+
+ """
+
+ 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)
+
+def filter_logging(logger, dir, level):
+ """Create separated files for each level of logging.
+
+ Params: logger - a logging object.
+ dir - directory to put the log files.
+ level - the level of logging for the all.log file.
+
+ Returns: logger object.
+
+ """
+ # Keep a good 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 as param
+ all_log = logging.FileHandler(os.path.join(dir, 'all.log'), mode='a+')
+ all_log.setLevel(logging.getLevelName(level))
+ all_log.setFormatter(formatter)
+
+ debug_log = logging.FileHandler(os.path.join(dir, '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(dir, '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(dir, '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(dir, '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)
+
+ return logger
+
diff --git a/src/gettor/xmpp.py b/src/gettor/xmpp.py
new file mode 100644
index 0000000..7940359
--- /dev/null
+++ b/src/gettor/xmpp.py
@@ -0,0 +1,378 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of GetTor, a Tor Browser Bundle distribution system.
+#
+
+import os
+import re
+import sys
+import time
+import gettext
+import hashlib
+import logging
+import ConfigParser
+
+from sleekxmpp import ClientXMPP
+from sleekxmpp.exceptions import IqError, IqTimeout
+
+import utils
+import core
+
+
+"""XMPP module for processing requests."""
+
+
+class Bot(ClientXMPP):
+ """XMPP bot.
+
+ Handle messages and pass them to XMPP module for parsing.
+
+ """
+
+ def __init__(self, jid, password, xmpp_obj):
+ ClientXMPP.__init__(self, jid, password)
+
+ self.xmpp = xmpp_obj
+ self.add_event_handler("session_start", self.session_start)
+ self.add_event_handler("message", self.message)
+
+ def session_start(self, event):
+ self.send_presence()
+ self.get_roster()
+
+ try:
+ self.get_roster()
+ except IqError as err:
+ logging.error('There was an error getting the roster')
+ logging.error(err.iq['error']['condition'])
+ self.disconnect()
+ except IqTimeout:
+ logging.error('Server is taking too long to respond')
+ self.disconnect()
+
+ def message(self, msg):
+ if msg['type'] in ('chat', 'normal'):
+ msg_to_send = self.xmpp.parse_request(msg['from'], msg['body'])
+ if msg_to_send:
+ msg.reply(msg_to_send).send()
+
+
+class ConfigurationError(Exception):
+ pass
+
+
+class BlacklistError(Exception):
+ pass
+
+
+class InternalError(Exception):
+ pass
+
+
+class XMPP(object):
+ """Receive and reply requests by XMPP.
+
+ Public methods:
+
+ parse_request(): parses a message and tries to figure out what the user
+ is asking for.
+
+ Exceptions:
+
+ ConfigurationError: Bad configuration.
+ BlacklistError: User is blacklisted.
+ InternalError: Something went wrong internally.
+
+ """
+
+ def __init__(self, cfg=None):
+ """Create new object by reading a configuration file.
+
+ Params: cfg - path of the configuration file.
+
+ """
+ # Define a set of default values
+ DEFAULT_CONFIG_FILE = 'xmpp.cfg'
+
+ logging.basicConfig(format='[%(levelname)s] %(asctime)s - %(message)s',
+ datefmt="%Y-%m-%d %H:%M:%S")
+ logger = logging.getLogger(__name__)
+ config = ConfigParser.ConfigParser()
+
+ if cfg is None or not os.path.isfile(cfg):
+ cfg = DEFAULT_CONFIG_FILE
+ logger.info("Using default configuration")
+
+ logger.info("Reading configuration file %s" % cfg)
+ config.read(cfg)
+
+ try:
+ self.user = config.get('account', 'user')
+ except ConfigParser.Error as e:
+ logger.warning("Couldn't read 'user' from 'account' (%s)" % cfg)
+ raise ConfigurationError("Error with conf. See log file.")
+
+ try:
+ self.password = config.get('account', 'password')
+ except ConfigParser.Error as e:
+ logger.warning("Couldn't read 'password' from 'account' (%s)" %
+ cfg)
+ raise ConfigurationError("Error with conf. See log file.")
+
+ try:
+ self.basedir = config.get('general', 'basedir')
+ except ConfigParser.Error as e:
+ logger.warning("Couldn't read 'basedir' from 'general' (%s)" % cfg)
+ raise ConfigurationError("Error with conf. See log file.")
+
+ try:
+ self.logdir = config.get('log', 'dir')
+ self.logdir = os.path.join(self.basedir, self.logdir)
+ except ConfigParser.Error as e:
+ logger.warning("Couldn't read 'dir' from 'log' (%s)" % cfg)
+ raise ConfigurationError("Error with conf. See log file.")
+
+ try:
+ self.logdir_msgs = config.get('log', 'msgs_dir')
+ self.logdir_msgs = os.path.join(self.logdir, self.logdir_msgs)
+ except ConfigParser.Error as e:
+ logger.warning("Couldn't read 'msgs_dir' from 'log' (%s)" % cfg)
+ raise ConfigurationError("Error with conf. See log file.")
+
+ try:
+ self.loglevel = config.get('log', 'level')
+ except ConfigParser.Error as e:
+ logger.warning("Couldn't read 'level' from 'log' (%s)" % cfg)
+ raise ConfigurationError("Error with conf. See log file.")
+
+ # Use default values
+ self.core = core.Core()
+
+ # Keep log levels separated
+ self.logger = utils.filter_logging(logger, self.logdir, self.loglevel)
+ 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 xmpp object created")
+
+ def start_bot(self):
+ """Start the bot for handling requests.
+
+ Start a new sleekxmpp bot.
+
+ """
+
+ self.logger.debug("Calling sleekmppp bot")
+ xmpp = Bot(self.user, self.password, self)
+ xmpp.connect()
+ xmpp.process(block=True)
+
+ def _get_sha1(self, string):
+ """Get sha1 of a string.
+
+ Used whenever we want to do things with accounts (log, blacklist,
+ etc.)
+
+ Params: The string to be sha1'ed.
+
+ Returns: sha1 of string.
+
+ """
+ return str(hashlib.sha1(string).hexdigest())
+
+ def _check_blacklist(self, account):
+ """Check if an account is blacklisted.
+
+ Look for the account in the file of blacklisted accounts.
+
+ Raises: BlacklistError if the user is blacklisted.
+
+ Params: account - the account we want to check.
+
+ """
+ anon_account = self._get_sha1(account)
+ self.logger.debug("Checking if address %s is blacklisted" %
+ anon_account)
+
+ # if blacklisted:
+ # raise BlacklistError("Account %s is blacklisted!" % anon_account)
+
+ def _get_help_msg(self, locale):
+ """Get help message for a given locale.
+
+ Get the message in the proper language (according to the locale),
+ replace variables (if any) and return the message.
+
+ Return: a string containing the message.
+
+ """
+ self.logger.debug("Getting help message")
+ # Obtain the content in the proper language
+ t = gettext.translation(locale, './xmpp/i18n', languages=[locale])
+ _ = t.ugettext
+
+ help_msg = _('help_msg')
+ return help_msg
+
+ def _get_unsupported_locale_msg(self, locale):
+ """Get unsupported locale message for a given locale.
+
+ Get the message in the proper language (according to the locale),
+ replace variables (if any) and return the message.
+
+ Return: a string containing the message.
+
+ """
+ self.logger.debug("Getting unsupported locale message")
+ # Obtain the content in the proper language
+ t = gettext.translation(locale, './xmpp/i18n', languages=[locale])
+ _ = t.ugettext
+
+ unsupported_locale_msg = _('unsupported_locale_msg')
+ return unsupported_locale_msg
+
+ def _get_unsupported_os_msg(self, locale):
+ """Get unsupported OS message for a given locale.
+
+ Get the message in the proper language (according to the locale),
+ replace variables (if any) and return the message.
+
+ Return: a string containing the message.
+
+ """
+ self.logger.debug("Getting unsupported os message")
+ # Obtain the content in the proper language
+ t = gettext.translation(locale, './xmpp/i18n', languages=[locale])
+ _ = t.ugettext
+
+ unsupported_os_msg = _('unsupported_os_msg')
+ return unsupported_os_msg
+
+ def _get_internal_error_msg(self, locale):
+ """Get internal error message for a given locale.
+
+ Get the message in the proper language (according to the locale),
+ replace variables (if any) and return the message.
+
+ Return: a string containing the message.
+
+ """
+ self.logger.debug("Getting internal error message")
+ # Obtain the content in the proper language
+ t = gettext.translation(locale, './xmpp/i18n', languages=[locale])
+ _ = t.ugettext
+
+ unsupported_locale_msg = _('internal_error_msg')
+ return internal_error_msg
+
+ def _get_links_msg(self, locale, operating_system, pt, links):
+ """Get links message for a given locale, operating system and PT
+ request.
+
+ Get the message in the proper language (according to the locale),
+ replace variables (if any) and return the message.
+
+ Return: a string containing the message.
+ """
+ self.logger.debug("Getting links message")
+ # Obtain the content in the proper language
+ t = gettext.translation(locale, './xmpp/i18n', languages=[locale])
+ _ = t.ugettext
+
+ if pt:
+ links_msg = _('links_pt_msg')
+ else:
+ links_msg = _('links_msg')
+
+ links_msg = links_msg % (locale, operating_system, links)
+
+ return links_msg
+
+ def _parse_text(self, msg):
+ """Parse the text part of a message.
+
+ Split the message in words and look for patterns for locale,
+ operating system and built-in pluggable transport info.
+
+ """
+ self.logger.debug("Starting text parsing")
+ # core knows what OS are supported
+ supported_os = self.core.get_supported_os()
+ supported_locales = self.core.get_supported_locales()
+
+ # default values
+ request = {}
+ request['locale'] = 'en'
+ request['os'] = 'windows'
+ request['type'] = 'help'
+ found_locale = False
+ found_os = False
+
+ # analyze every word
+ # request shouldn't be more than 10 words long, so there should
+ # be a limit for the amount of words
+ for word in msg.split(' '):
+ # look for locale, os and pt
+ if not found_locale:
+ for locale in supported_locales:
+ if re.match(locale, word, re.IGNORECASE):
+ found_locale = True
+ request['locale'] = locale
+ self.logger.debug("Found locale: %s" % locale)
+ if not found_os:
+ for operating_system in supported_os:
+ if re.match(operating_system, word, re.IGNORECASE):
+ found_os = True
+ request['os'] = operating_system
+ request['type'] = 'links'
+ self.logger.debug("Found OS: %s" % operating_system)
+ if re.match("[obfs|plugabble transport|pt]", word,
+ re.IGNORECASE):
+ request['pt'] = True
+ self.logger.debug("Found PT request")
+
+ return request
+
+ def parse_request(self, account, msg):
+ """Process the request received.
+
+ Check if the user is not blacklisted and then check the body of
+ the message to find out what is asking.
+
+ Params: account - the account that did the request.
+ msg - the body of the message sent to us.
+
+ """
+ try:
+ self._check_blacklist(str(account))
+ except BlacklistError as e:
+ return None
+
+ # let's try to guess what the user is asking
+ request = self._parse_text(str(msg))
+
+ if request['type'] == 'help':
+ return_msg = self._get_help_msg(request['locale'])
+ elif request['type'] == 'links':
+ try:
+ links = self.core._get_links("XMPP",
+ request['operating_system'],
+ request['locale'])
+
+ return_msg = self._get_links_msg(request['locale'],
+ request['operating_system'],
+ request['pt'], links)
+
+ except (ConfigurationError, InternalError) as e:
+ return_msg = self.core._get_internal_error_msg(
+ request['locale'])
+
+ except UnsupportedLocaleError as e:
+ self.core._get_unsupported_locale_msg(request['locale'])
+
+ except UnsupportedOSError as e:
+ self.core._get_unsupported_os_msg(request['locale'])
+
+ return return_msg
diff --git a/src/smtp_demo.py b/src/smtp_demo.py
index 390377c..418b3bd 100644
--- a/src/smtp_demo.py
+++ b/src/smtp_demo.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python
import sys
-import smtp
+import gettor.smtp
-service = smtp.SMTP('smtp.cfg')
+service = gettor.smtp.SMTP()
# For now we simulate mails reading from stdin
# In linux test as follows:
@@ -14,7 +14,9 @@ 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)
+except gettor.smtp.ConfigurationError as e:
+ print "Misconfiguration: " + str(e)
+except gettor.smtp.SendEmailError as e:
+ print "SMTP not working: " + str(e)
+except gettor.smtp.InternalError as e:
+ print "Core module not working: " + str(e)
diff --git a/src/xmpp.cfg b/src/xmpp.cfg
new file mode 100644
index 0000000..a78ef88
--- /dev/null
+++ b/src/xmpp.cfg
@@ -0,0 +1,11 @@
+[account]
+user:
+password:
+
+[general]
+basedir: xmpp/
+
+[log]
+level: DEBUG
+dir: log/
+msgs_dir: msgs/
diff --git a/src/xmpp_demo.py b/src/xmpp_demo.py
new file mode 100644
index 0000000..144f4a8
--- /dev/null
+++ b/src/xmpp_demo.py
@@ -0,0 +1,7 @@
+#!/usr/bin/env python
+import sys
+
+import gettor.xmpp
+
+bot = gettor.xmpp.XMPP()
+bot.start_bot()
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits