tor-commits
Threads by month
- ----- 2025 -----
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
September 2015
- 15 participants
- 1411 discussions

22 Sep '15
commit 9e0ff07e73c1af0cea3c67b9389d534b64614e56
Merge: 6649daa 53e394f
Author: ilv <ilv(a)users.noreply.github.com>
Date: Wed Aug 6 23:21:16 2014 -0400
Merge branch 'master' of github.com:ileiva/gettor
README.md | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
1
0
commit 53e394f84e3e307ead8b8268b151503c6e9dc37b
Author: ilv <ilv(a)users.noreply.github.com>
Date: Wed Aug 6 17:29:26 2014 -0400
Added XMPP commands
Current supported commands.
---
README.md | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 0026b96..93a43e6 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,7 @@ gettor
======
GetTor Revamp (on development).
+
Google Summer of Code 2014.
* To get the current repo:
@@ -57,7 +58,13 @@ $ pip install sleekxmpp
$ python xmpp_demo.py
-4) To communicate with the bot using Pidgin click on Friends -> New instant message. There are still some issues with bot responses.
+4) To communicate with the bot using Pidgin click on Friends -> New instant message -> Enter the address used in xmpp.cfg to start comunicating with it. Current commands are as follows:
+
+operating_system locale: it'll get you links for operating system in the locale specified e.g. linux en
+
+help locale: it'll get you help info in locale e.g. help es
+
+Default locale is en, and default response is help.
The xmpp module has been used in the following providers:
1
0

[gettor/master] Changes related to GetTor meeting of October 10th. Deleted obsolete documentation.
by ilv@torproject.org 22 Sep '15
by ilv@torproject.org 22 Sep '15
22 Sep '15
commit 150b15fc333d70f4f3b673846d177c6c21338bff
Author: ilv <ilv(a)users.noreply.github.com>
Date: Sat Oct 18 18:09:49 2014 -0300
Changes related to GetTor meeting of October 10th. Deleted obsolete documentation.
---
LICENSE | 31 +
README.md | 80 +-
blacklist.cfg | 6 +
core.cfg | 12 +
core_demo.py | 19 +
dropbox.cfg | 8 +
dropbox.py | 190 ++
gettor/__init__.py | 1 +
gettor/blacklist.py | 136 +
gettor/core.py | 355 +++
gettor/db.py | 106 +
gettor/smtp.py | 493 ++++
gettor/utils.py | 27 +
gettor/xmpp.py | 304 ++
lang/smtp/i18n/en/LC_MESSAGES/en.mo | Bin 0 -> 1730 bytes
lang/smtp/i18n/en/LC_MESSAGES/en.po | 95 +
lang/smtp/i18n/es/LC_MESSAGES/es.mo | Bin 0 -> 1811 bytes
lang/smtp/i18n/es/LC_MESSAGES/es.po | 94 +
lang/xmpp/i18n/en/LC_MESSAGES/en.mo | Bin 0 -> 340 bytes
lang/xmpp/i18n/en/LC_MESSAGES/en.po | 22 +
lang/xmpp/i18n/en/LC_MESSAGES/es.mo | Bin 0 -> 340 bytes
lang/xmpp/i18n/es/LC_MESSAGES/es.mo | Bin 0 -> 342 bytes
lang/xmpp/i18n/es/LC_MESSAGES/es.po | 22 +
providers/dropbox.links | 25 +
providers/dropbox.links.backup | 25 +
scripts/blacklist.py | 111 +
scripts/create_db.py | 57 +
scripts/stats.py | 123 +
smtp.cfg | 16 +
smtp_demo.py | 22 +
spec/design/blacklist.txt | 74 -
spec/design/core.txt | 155 -
spec/design/smtp.txt | 118 -
spec/design/twitter.txt | 99 -
spec/overview.txt | 94 -
src/LICENSE | 31 -
src/blacklist.cfg | 6 -
src/core.cfg | 12 -
src/core_demo.py | 19 -
src/create_links_demo.py | 19 -
src/dropbox.py | 163 --
src/gettor/__init__.py | 1 -
src/gettor/blacklist.py | 143 -
src/gettor/core.py | 419 ---
src/gettor/db.py | 113 -
src/gettor/smtp.py | 603 ----
src/gettor/utils.py | 104 -
src/gettor/xmpp.py | 378 ---
src/i18n/en/LC_MESSAGES/en.mo | Bin 1730 -> 0 bytes
src/i18n/en/LC_MESSAGES/en.po | 95 -
src/i18n/es/LC_MESSAGES/es.mo | Bin 1811 -> 0 bytes
src/i18n/es/LC_MESSAGES/es.po | 94 -
src/log/all.log | 21 -
src/log/debug.log | 12 -
src/log/error.log | 1 -
src/log/info.log | 10 -
src/log/warn.log | 1 -
src/providers/dropbox.links | 17 -
src/scripts/blacklist.py | 111 -
src/scripts/create_db.py | 54 -
src/scripts/stats.py | 123 -
src/smtp.cfg | 17 -
src/smtp/log/all.log | 16 -
src/smtp/log/debug.log | 14 -
src/smtp/log/info.log | 3 -
src/smtp/sample/sample-email.eml | 57 -
src/smtp_demo.py | 22 -
src/tbb-key.asc | 2986 --------------------
src/upload/tor-browser-linux32-3.6.2_en-EN.tar.xz | 1 -
.../tor-browser-linux32-3.6.2_en-EN.tar.xz.asc | 11 -
src/upload/tor-browser-linux32-3.6.2_es-ES.tar.xz | 1 -
.../tor-browser-linux32-3.6.2_es-ES.tar.xz.asc | 11 -
src/xmpp.cfg | 20 -
src/xmpp/i18n/en/LC_MESSAGES/en.mo | Bin 340 -> 0 bytes
src/xmpp/i18n/en/LC_MESSAGES/en.po | 22 -
src/xmpp/i18n/en/LC_MESSAGES/es.mo | Bin 340 -> 0 bytes
src/xmpp/i18n/es/LC_MESSAGES/es.mo | Bin 342 -> 0 bytes
src/xmpp/i18n/es/LC_MESSAGES/es.po | 22 -
src/xmpp_demo.py | 7 -
upload/tor-browser-linux32-3.6.2_en-EN.tar.xz | 1 +
upload/tor-browser-linux32-3.6.2_en-EN.tar.xz.asc | 11 +
upload/tor-browser-linux32-3.6.2_es-ES.tar.xz | 1 +
upload/tor-browser-linux32-3.6.2_es-ES.tar.xz.asc | 11 +
upload/tor-browser-linux64-3.6.2_en-EN.tar.xz | 1 +
upload/tor-browser-linux64-3.6.2_en-EN.tar.xz.asc | 11 +
upload/tor-browser-osx32-3.6.2_en-EN.tar.xz | 1 +
upload/tor-browser-osx32-3.6.2_en-EN.tar.xz.asc | 11 +
upload/tor-browser-windows32-3.6.2_en-EN.tar.xz | 1 +
.../tor-browser-windows32-3.6.2_en-EN.tar.xz.asc | 11 +
xmpp.cfg | 19 +
xmpp_demo.py | 7 +
91 files changed, 2389 insertions(+), 6377 deletions(-)
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..530ff57
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,31 @@
+GetTor is distributed under this license:
+
+Copyright (c) 2008-2014, The Tor Project, Inc.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+ * Neither the names of the copyright owners nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/README.md b/README.md
index 93a43e6..e194589 100644
--- a/README.md
+++ b/README.md
@@ -1,80 +1,6 @@
-gettor
+GetTor
======
-GetTor Revamp (on development).
-
-Google Summer of Code 2014.
-
-* To get the current repo:
-$ git clone https://github.com/ileiva/gettor.git
-
-* To upload bundles to Dropbox and create a links file:
-
-1) Install the Dropbox and GnuPG Python modules (just the first time):
-
-$ pip install dropbox gnupg
-
-2) Change account info in src/dropbox.py (app_key, app_secret, access_token)
-
-3) Specify the path of the PGP key that signed the packages (to include fingerprint).
-
-4) Run the script:
-
-$ cd src/providers/;rm *.links;cd ..; python dropbox.py
-
-If everything works good, you should see a dropbox.links file inside the 'providers' directory. The script will take the files on upload_dir (default to 'upload/') which end up on .xz and .xz.asc respectively. To add more locales for testing do the following (example for german):
-
-$ cd upload; cp tor-browser-linux32-3.6.2_en-EN.tar.xz tor-browser-linux32-3.6.2_de-DE.tar.xz
-
-$ cd upload; cp tor-browser-linux32-3.6.2_en-EN.tar.xz.asc tor-browser-linux32-3.6.2_de-DE.tar.xz.asc
-
-A script for getting the latest bundles is pending.
-
-* To test if the core module is working:
-
-1) Use the dummy script provided:
-
-$ python core_demo.py
-
-* To test the smtp module (without mail server):
-
-1) Set request parameters on smtp/sample/sample-email.eml (by default, 'To: gettor+en(a)torproject.org' and 'linux' in the body of the message.
-
-2) Run dummy script:
-
-$ python smtp_demo.py < smtp/sample/sample-email.eml
-
-If mail server is configured, then uncomment lines 328-332, 337, 353-359, and comment lines 334-335, 338, 360 on gettor/smtp.py. Also, you should enable e-mail forwarding as specified on https://gitweb.torproject.org/gettor.git/blob/HEAD:/README
-
-* To test the xmpp module
-
-1) Install the SleekXMPP module:
-
-$ pip install sleekxmpp
-
-2) Change user details on xmpp.cfg
-
-3) Run dummy script.
-
-$ python xmpp_demo.py
-
-4) To communicate with the bot using Pidgin click on Friends -> New instant message -> Enter the address used in xmpp.cfg to start comunicating with it. Current commands are as follows:
-
-operating_system locale: it'll get you links for operating system in the locale specified e.g. linux en
-
-help locale: it'll get you help info in locale e.g. help es
-
-Default locale is en, and default response is help.
-
-The xmpp module has been used in the following providers:
-
- * dukgo.com (works)
- * riseup.net (works)
- * jabber.ccc.de (works)
- * jit.si (worked for a while, not any longer)
-
-
-
-
-
+GetTor development. Project started at the Google Summer of Code 2014, under the
+Tor Project organization.
diff --git a/blacklist.cfg b/blacklist.cfg
new file mode 100644
index 0000000..bb8a3d8
--- /dev/null
+++ b/blacklist.cfg
@@ -0,0 +1,6 @@
+[general]
+db: /path/to/gettor.db
+
+[log]
+level: DEBUG
+dir: /path/to/log
diff --git a/core.cfg b/core.cfg
new file mode 100644
index 0000000..f82c890
--- /dev/null
+++ b/core.cfg
@@ -0,0 +1,12 @@
+[general]
+basedir: /home/ilv/Proyectos/tor/gettor/src/
+db: gettor.db
+
+[links]
+dir: /path/to/providers/
+os: linux,windows,osx
+locales: es,en
+
+[log]
+dir: /home/ilv/Proyectos/tor/gettor/src/log/
+level: DEBUG
\ No newline at end of file
diff --git a/core_demo.py b/core_demo.py
new file mode 100644
index 0000000..88f725d
--- /dev/null
+++ b/core_demo.py
@@ -0,0 +1,19 @@
+#!/usr/bin/python
+#
+# Dummy script to test GetTore's Core module
+#
+
+import gettor.core
+
+try:
+ core = gettor.core.Core()
+ links = core.get_links('dummy service', 'linux', 'en')
+ print links
+except gettor.core.ConfigError 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/dropbox.cfg b/dropbox.cfg
new file mode 100644
index 0000000..a29989c
--- /dev/null
+++ b/dropbox.cfg
@@ -0,0 +1,8 @@
+[general]
+upload_dir: upload
+tbb_key: tbb-key.asc
+
+[app]
+key: suchkey
+secret: suchsecret
+access_token: suchtoken
\ No newline at end of file
diff --git a/dropbox.py b/dropbox.py
new file mode 100644
index 0000000..457955f
--- /dev/null
+++ b/dropbox.py
@@ -0,0 +1,190 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of GetTor, a Tor Browser distribution system.
+#
+# :authors: Israel Leiva <ilv(a)riseup.net>
+# see also AUTHORS file
+#
+# :copyright: (c) 2008-2014, The Tor Project, Inc.
+# (c) 2014, Israel Leiva
+#
+# :license: This is Free Software. See LICENSE for license information.
+
+import re
+import os
+import gnupg
+import hashlib
+import ConfigParser
+
+import dropbox
+import gettor.core
+
+
+def valid_format(file):
+ """Check for valid bundle format
+
+ Check if the given file has a valid bundle format
+ (e.g. tor-browser-linux32-3.6.2_es-ES.tar.xz)
+
+ :param: file (string) the name of the file.
+
+ :return: (boolean) true if the bundle format is valid, false otherwise.
+
+ """
+ m = re.search(
+ 'tor-browser-(\w+)\d\d-\d\.\d\.\d_(\w\w)-\w+\.tar\.xz',
+ file)
+ if m:
+ return True
+ else:
+ return False
+
+
+def get_bundle_info(file):
+ """Get the os, arch and lc from a bundle string.
+
+ :param: file (string) the name of the file.
+
+ :raise: ValueError if the bundle doesn't have a valid bundle format.
+
+ :return: (list) the os, arch and lc.
+
+ """
+ m = re.search(
+ 'tor-browser-(\w+)(\d\d)-\d\.\d\.\d_(\w\w)-\w+\.tar\.xz',
+ file)
+ if m:
+ os = m.group(1)
+ arch = m.group(2)
+ lc = m.group(3)
+ return os, arch, lc
+ else:
+ raise ValueError("Invalid bundle format %s" % file)
+
+
+def get_file_sha256(file):
+ """Get the sha256 of a file.
+
+ :param: file (string) the path of the file.
+
+ :return: (string) the sha256 hash.
+
+ """
+ # as seen on the internetz
+ BLOCKSIZE = 65536
+ hasher = hashlib.sha256()
+ 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 to Dropbox.
+
+ Looks for files ending with 'tar.xz' inside basedir.
+
+ :param: basedir (string) path of the folder with the files to be
+ uploaded.
+ :param: client (object) DropboxClient object.
+
+ :raise: ValueError if the .xz file doesn't have an .asc file.
+ :raise: UploadError if something goes wrong while uploading the
+ files to Dropbox. All files are uploaded to '/'.
+
+ :return: (list) the names of the uploaded files.
+
+ """
+ files = []
+
+ p = re.compile('.*\.tar.xz$')
+
+ for name in os.listdir(basedir):
+ path = os.path.abspath(os.path.join(basedir, name))
+ if os.path.isfile(path) and p.match(path) and valid_format(name):
+ files.append(name)
+
+ for file in files:
+ asc = "%s.asc" % file
+ abs_file = os.path.abspath(os.path.join(basedir, file))
+ abs_asc = os.path.abspath(os.path.join(basedir, asc))
+
+ if not os.path.isfile(abs_asc):
+ raise ValueError("%s doesn't exist!" % asc)
+
+ # chunk upload for big files
+ to_upload = open(abs_file, 'rb')
+ size = os.path.getsize(abs_file)
+ uploader = client.get_chunked_uploader(to_upload, size)
+ while uploader.offset < size:
+ try:
+ upload = uploader.upload_chunked()
+ except rest.ErrorResponse, e:
+ UploadError("An error ocurred while uploading %s" % abs_file)
+ uploader.finish(file)
+ print "Uploading %s" % file
+
+ # this should be small, upload it simple
+ to_upload_asc = open(abs_asc, 'rb')
+ response = client.put_file(asc, to_upload_asc)
+ print "Uploading %s" % asc
+
+ return files
+
+if __name__ == '__main__':
+ config = ConfigParser.ConfigParser()
+ config.read('dropbox.cfg')
+
+ app_key = config.get('app', 'key')
+ app_secret = config.get('app', 'secret')
+ access_token = config.get('app', 'access_token')
+ upload_dir = config.get('general', 'upload_dir')
+
+ # important: this key must be the one that signed the packages
+ tbb_key = config.get('general', 'tbb_key')
+
+ 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('/home/gettor/core.cfg')
+
+ # erase old links
+ core.create_links_file('Dropbox', readable)
+
+ for file in uploaded_files:
+ # build file names
+ asc = "%s.asc" % file
+ abs_file = os.path.abspath(os.path.join(upload_dir, file))
+ abs_asc = os.path.abspath(os.path.join(upload_dir, asc))
+
+ sha_file = get_file_sha256(abs_file)
+
+ # build links
+ link_file = client.share(file, short_url=False)
+ link_asc = client.share(asc, short_url=False)
+ osys, arch, lc = get_bundle_info(file)
+
+ link = "Package (%s-bit): %s\nASC signature (%s-bit): %s\n"\
+ "Package SHA256 checksum (%s-bit): %s\n" %\
+ (arch, link_file[u'url'], arch, link_asc[u'url'],
+ arch, sha_file)
+
+ core.add_link('Dropbox', osys, lc, link)
+ except (ValueError, RuntimeError) as e:
+ print str(e)
+ except dropbox.rest.ErrorResponse as e:
+ print str(e)
diff --git a/gettor/__init__.py b/gettor/__init__.py
new file mode 100644
index 0000000..c87425a
--- /dev/null
+++ b/gettor/__init__.py
@@ -0,0 +1 @@
+# yes it's empty, of such a fullness
diff --git a/gettor/blacklist.py b/gettor/blacklist.py
new file mode 100644
index 0000000..b95d888
--- /dev/null
+++ b/gettor/blacklist.py
@@ -0,0 +1,136 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of GetTor, a Tor Browser distribution system.
+#
+# :authors: Israel Leiva <ilv(a)riseup.net>
+# see also AUTHORS file
+#
+# :copyright: (c) 2008-2014, The Tor Project, Inc.
+# (c) 2014, Israel Leiva
+#
+# :license: This is Free Software. See LICENSE for license information.
+
+import os
+import time
+import logging
+import sqlite3
+import datetime
+import ConfigParser
+
+import db
+import utils
+
+"""Blacklist module for managing blacklisting of users."""
+
+
+class BlacklistError(Exception):
+ pass
+
+
+class Blacklist(object):
+ """Manage blacklisting of users.
+
+ Public methods:
+
+ is_blacklisted(): Check if someone is blacklisted.
+
+ Exceptions:
+
+ ConfigurationError: Bad configuration.
+ BlacklistError: User is blacklisted.
+
+ """
+
+ def __init__(self, cfg=None):
+ """Create new object by reading a configuration file.
+
+ :param: cfg (string) path of the configuration file.
+
+ """
+ # define a set of default values
+ DEFAULT_CONFIG_FILE = 'blacklist.cfg'
+
+ logging.basicConfig(format='[%(levelname)s] %(asctime)s - %(message)s',
+ datefmt="%Y-%m-%d %H:%M:%S")
+ log = logging.getLogger(__name__)
+ config = ConfigParser.ConfigParser()
+
+ if cfg is None or not os.path.isfile(cfg):
+ cfg = DEFAULT_CONFIG_FILE
+
+ config.read(cfg)
+
+ try:
+ dbname = config.get('general', 'db')
+ self.db = db.DB(dbname)
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'db' from 'general'")
+
+ try:
+ logdir = config.get('log', 'dir')
+ logfile = os.path.join(logdir, 'blacklist.log')
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'dir' from 'log'")
+
+ try:
+ loglevel = config.get('log', 'level')
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'level' from 'log'")
+
+ # establish log level and redirect to log file
+ log.info('Redirecting logging to %s' % logfile)
+ logfileh = logging.FileHandler(logfile, mode='a+')
+ logfileh.setLevel(logging.getLevelName(loglevel))
+ log.addHandler(logfileh)
+
+ # stop logging on stdout from now on
+ log.propagate = False
+
+ def is_blacklisted(self, user, service, max_req, wait_time):
+ """Check if a user is blacklisted.
+
+ The user is blacklisted if:
+
+ a) The 'blocked' field is set to one, meaning that is permanently
+ blacklisted.
+
+ b) Does too many requests on a short period of time. For now, a user
+ that makes more than 'max_req' requests should wait 'wait_time'
+ minutes to make a new request.
+
+ :param: user (string) the hashed user.
+ :param: service (string) the service the user is making a request to.
+ :param: max_req (int) maximum number of requests a user can make
+ in a row.
+ :param: wait_time (int) amount of time the user must wait before
+ making requests again after 'max_req' requests is reached.
+ For now this is considered in minutes.
+
+ :raise: BlacklistError if the user is blacklisted
+
+ """
+ r = self.db.get_user(user, service)
+ if r:
+ # permanently blacklisted
+ if r['blocked']:
+ self.db.update_user(user, service, r['times']+1, 1)
+ raise BlacklistError("Blocked user")
+ # don't be greedy
+ elif r['times'] >= max_req:
+ last = datetime.datetime.fromtimestamp(float(
+ r['last_request']))
+ next = last + datetime.timedelta(minutes=wait_time)
+
+ if datetime.datetime.now() < next:
+ # too many requests from the same user
+ self.db.update_user(user, service, r['times']+1, 0)
+ raise BlacklistError("Too many requests")
+ else:
+ # fresh user again!
+ self.db.update_user(user, service, 1, 0)
+ else:
+ # adding up a request for user
+ self.db.update_user(user, service, r['times']+1, 0)
+ else:
+ # new request for user
+ self.db.add_user(user, service, 0)
diff --git a/gettor/core.py b/gettor/core.py
new file mode 100644
index 0000000..792add0
--- /dev/null
+++ b/gettor/core.py
@@ -0,0 +1,355 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of GetTor, a Tor Browser distribution system.
+#
+# :authors: Israel Leiva <ilv(a)riseup.net>
+# see also AUTHORS file
+#
+# :copyright: (c) 2008-2014, The Tor Project, Inc.
+# (c) 2014, Israel Leiva
+#
+# :license: This is Free Software. See LICENSE for license information.
+
+import os
+import re
+import inspect
+import logging
+import tempfile
+import ConfigParser
+
+import db
+import utils
+
+"""Core module for getting links from providers."""
+
+
+class ConfigError(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_lc(): Get a list of supported locales.
+
+ Exceptions:
+
+ UnsupportedOSError: Request for an unsupported operating system.
+ UnsupportedLocaleError: Request for an unsupported locale.
+ ConfigError: 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.
+
+ :param: cfg (string) the path of the configuration file.
+ :raise: ConfigurationError if the configuration file doesn't exists
+ or if something goes wrong while reading options from it.
+
+ """
+ # 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")
+ log = logging.getLogger(__name__)
+ config = ConfigParser.ConfigParser()
+
+ if cfg is None or not os.path.isfile(cfg):
+ cfg = DEFAULT_CONFIG_FILE
+
+ config.read(cfg)
+
+ try:
+ basedir = config.get('general', 'basedir')
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'basedir' from 'general'")
+
+ try:
+ dbname = config.get('general', 'db')
+ dbname = os.path.join(basedir, dbname)
+ self.db = db.DB(dbname)
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'db' from 'general'")
+
+ try:
+ self.linksdir = config.get('links', 'dir')
+ self.linksdir = os.path.join(basedir, self.linksdir)
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'links' from 'dir'")
+
+ try:
+ self.supported_lc = config.get('links', 'locales')
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'locales' from 'links'")
+
+ try:
+ self.supported_os = config.get('links', 'os')
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'os' from 'links'")
+
+ try:
+ loglevel = config.get('log', 'level')
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'level' from 'log'")
+
+ try:
+ logdir = config.get('log', 'dir')
+ logfile = os.path.join(logdir, 'core.log')
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'dir' from 'log'")
+
+ # establish log level and redirect to log file
+ log.info('Redirecting logging to %s' % logfile)
+ logfileh = logging.FileHandler(logfile, mode='a+')
+ logfileh.setLevel(logging.getLevelName(loglevel))
+ log.addHandler(logfileh)
+
+ # stop logging on stdout from now on
+ log.propagate = False
+
+ def get_links(self, service, os, lc):
+ """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).
+
+ :param: service (string) the service trying to get the links.
+ :param: os (string) the operating system.
+ :param: lc (string) tthe locale.
+
+ :raise: UnsupportedOSError if the operating system is not supported.
+ :raise: UnsupportedLocaleError if the locale is not supported.
+ :raise: InternalError if something goes wrong while internally.
+
+ :return: (string) the links.
+
+ """
+
+ if lc not in self.supported_lc:
+ raise UnsupportedLocaleError("Locale %s not supported" % lc)
+
+ if os not in self.supported_os:
+ raise UnsupportedOSError("OS %s not supported " % os)
+
+ # this could change in the future, let's leave it isolated.
+ links = self._get_links(os, lc)
+
+ if links is None:
+ raise InternalError("Something went wrong internally")
+
+ # thanks for stopping by
+ return links
+
+ def _get_links(self, osys, lc):
+ """Internal method to get the links.
+
+ Looks for the links inside each provider file. This should only be
+ called from get_links() method.
+
+ :param: osys (string) the operating system.
+ :param: lc (string) the locale.
+
+ :return: (string/None) links on success, None otherwise.
+
+ """
+
+ # 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 os and lc. This way makes it easy to check if no
+ # links were found
+ providers = {}
+
+ # reading links from providers directory
+ for name in links:
+ # 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:
+ raise InternalError("Couldn't get 'name' from 'provider'")
+
+ # checking if current provider pname has links for os in lc
+ try:
+ providers[pname] = config.get(osys, lc)
+ # avoid showing it all together
+ providers[pname] = providers[pname].replace(",", "\n")
+ except ConfigParser.Error as e:
+ raise InternalError("Couldn't get %s from %s (%s)" %
+ (lc, osys, name))
+
+ # each provider must have a fingerprint of the key used to
+ # sign the uploaded packages
+ try:
+ fingerprint = config.get('key', 'fingerprint')
+ providers[pname] = "%s\n\nFingerprint: %s" %\
+ (providers[pname], fingerprint)
+ except ConfigParser.Error as e:
+ raise InternalError("Couldn't get 'fingerprint' from 'key'")
+
+ # create the final links list with all providers
+ all_links = []
+
+ for key in providers.keys():
+ all_links.append(
+ "\n%s\n\n%s\n" % (key, ''.join(providers[key]))
+ )
+
+ if all_links:
+ return "".join(all_links)
+ else:
+ # we're trying to get supported os an lc
+ # but there aren't any links!
+ return None
+
+ def get_supported_os(self):
+ """Public method to get the list of supported operating systems.
+
+ :return: (list) the supported operating systems.
+
+ """
+ return self.supported_os.split(',')
+
+ def get_supported_lc(self):
+ """Public method to get the list of supported locales.
+
+ :return: (list) the supported locales.
+
+ """
+ return self.supported_lc.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.
+
+ :param: provider (string) the provider (links file will use this
+ name in slower case).
+ :param: fingerprint (string) the 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 = ""
+
+ if os.path.isfile(linksfile):
+ # backup the old file in case something fails
+ linksfile_backup = linksfile + '.backup'
+ os.rename(linksfile, linksfile_backup)
+
+ try:
+ # this creates an empty links file
+ 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)
+ except Exception as e:
+ if linksfile_backup:
+ os.rename(linksfile_backup, linksfile)
+ raise LinkFileError("Error while creating new links file: %s" % e)
+
+ def add_link(self, provider, osys, lc, link):
+ """Public method to add a link to a provider's links file.
+
+ Use ConfigParser to add a link into the os section, under the lc
+ option. It checks for valid format; the provider's script should
+ use the right format (see design).
+
+ :param: provider (string) the provider.
+ :param: os (string) the operating system.
+ :param: lc (string) the locale.
+ :param: link (string) link to be added.
+
+ :raise: UnsupportedOSError if the operating system is not supported.
+ :raise: UnsupportedLocaleError if the locale is not supported.
+ :raise: LinkFileError if there is no links file for the provider.
+ :raise: LinkFormatError if the link format doesn't seem legit.
+ :raise: 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.
+
+ """
+ linksfile = os.path.join(self.linksdir, provider.lower() + '.links')
+
+ # don't try to add unsupported stuff
+ if lc not in self.supported_lc:
+ raise UnsupportedLocaleError("Locale %s not supported" % lc)
+
+ if osys not in self.supported_os:
+ raise UnsupportedOSError("OS %s not supported" % osys)
+
+ 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(osys, lc)
+ links = "%s,\n%s" % (links, link)
+ content.set(osys, lc, links)
+ with open(linksfile, 'w') as f:
+ content.write(f)
+ except ConfigParser.NoOptionError:
+ content.set(osys, lc, link)
+ with open(linksfile, 'w') as f:
+ content.write(f)
+ except ConfigParser.NoSectionError:
+ # this shouldn't happen, but just in case
+ raise InternalError("Unknown %s section in links file" % osys)
+ else:
+ raise LinkFileError("There is no links file for %s" % provider)
+
+ def add_request_to_db(self):
+ """Add request to database."""
+ self.db.add_request()
diff --git a/gettor/db.py b/gettor/db.py
new file mode 100644
index 0000000..e5e0acc
--- /dev/null
+++ b/gettor/db.py
@@ -0,0 +1,106 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of GetTor, a Tor Browser distribution system.
+#
+# :authors: Israel Leiva <ilv(a)riseup.net>
+# see also AUTHORS file
+#
+# :copyright: (c) 2008-2014, The Tor Project, Inc.
+# (c) 2014, Israel Leiva
+#
+# :license: This is Free Software. See LICENSE for license information.
+
+import time
+import sqlite3
+import datetime
+
+"""DB interface for comunicating with sqlite3"""
+
+
+class DB(object):
+ """
+
+ Public methods:
+
+ add_request(): add a request to the database (requests table).
+ get_user(): get user info from the database (users table).
+ add_user(): add a user to the database (users table).
+ update_user(): update a user on the database (users table).
+
+ """
+
+ def __init__(self, dbname):
+ """Create a new db object.
+
+ :param: dbname (string) the path of the database.
+
+ """
+ self.con = sqlite3.connect(dbname)
+ self.con.row_factory = sqlite3.Row
+
+ def add_request(self):
+ """Add a request to the database.
+
+ For now we just count the number of requests we have received so far.
+
+ """
+ with self.con:
+ cur = self.con.cursor()
+ cur.execute("SELECT counter FROM requests WHERE id = 1")
+ row = cur.fetchone()
+ if row:
+ cur.execute("UPDATE requests SET counter=? WHERE id=?",
+ (row['counter']+1, 1))
+ else:
+ cur.execute("INSERT INTO requests VALUES(?, ?)", (1, 1))
+
+ def get_user(self, user, service):
+ """Get user info from the database.
+
+ :param: user (string) unique (hashed) string that represents the user.
+ :param: service (string) the service related to the user (e.g. SMTP).
+
+ :return: (dict) the user information, with fields as indexes
+ (e.g. row['user']).
+
+ """
+ with self.con:
+ cur = self.con.cursor()
+ cur.execute("SELECT * FROM users WHERE id =? AND service =?",
+ (user, service))
+
+ row = cur.fetchone()
+ return row
+
+ def add_user(self, user, service, blocked):
+ """Add a user to the database.
+
+ We add a user with one 'times' and the current time as 'last_request'
+ by default.
+
+ :param: user (string) unique (hashed) string that represents the user.
+ :param: service (string) the service related to the user (e.g. SMTP).
+ :param: blocked (int) one if user is blocked, zero otherwise.
+
+ """
+ with self.con:
+ cur = self.con.cursor()
+ cur.execute("INSERT INTO users VALUES(?,?,?,?,?)",
+ (user, service, 1, blocked, str(time.time())))
+
+ def update_user(self, user, service, times, blocked):
+ """Update a user on the database.
+
+ We update the user info with the current time as 'last_request'.
+
+ :param: user (string) unique (hashed) string that represents the user.
+ :param: service (string) the service related to the user (e.g. SMTP).
+ :param: times (int) the number of requests the user has made.
+ :param: blocked (int) one if user is blocked, zero otherwise.
+
+ """
+ with self.con:
+ cur = self.con.cursor()
+ cur.execute("UPDATE users SET times =?, blocked =?,"
+ " last_request =? WHERE id =? AND service =?",
+ (times, blocked, str(time.time()), user, service))
diff --git a/gettor/smtp.py b/gettor/smtp.py
new file mode 100644
index 0000000..655b5df
--- /dev/null
+++ b/gettor/smtp.py
@@ -0,0 +1,493 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of GetTor, a Tor Browser distribution system.
+#
+# :authors: Israel Leiva <ilv(a)riseup.net>
+# see also AUTHORS file
+#
+# :copyright: (c) 2008-2014, The Tor Project, Inc.
+# (c) 2014, Israel Leiva
+#
+# :license: This is Free Software. See LICENSE for license information.
+
+
+import os
+import re
+import sys
+import time
+import email
+import gettext
+import logging
+import smtplib
+import datetime
+import ConfigParser
+
+from email.mime.text import MIMEText
+
+import core
+import utils
+import blacklist
+
+"""SMTP module for processing email requests."""
+
+
+class ConfigError(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:
+
+ ConfigError: Bad configuration.
+ 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.
+
+ :param: cfg (string) 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")
+ log = logging.getLogger(__name__)
+ config = ConfigParser.ConfigParser()
+
+ if cfg is None or not os.path.isfile(cfg):
+ cfg = DEFAULT_CONFIG_FILE
+
+ config.read(cfg)
+
+ try:
+ self.our_domain = config.get('general', 'our_domain')
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'our_domain' from 'general'")
+
+ try:
+ core_cfg = config.get('general', 'core_cfg')
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'core_cfg' from 'general'")
+
+ try:
+ blacklist_cfg = config.get('blacklist', 'cfg')
+ self.bl = blacklist.Blacklist(blacklist_cfg)
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'cfg' from 'blacklist'")
+
+ try:
+ self.bl_max_req = config.get('blacklist', 'max_requests')
+ self.bl_max_req = int(self.bl_max_req)
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'max_requests' from 'blacklist'")
+
+ try:
+ self.bl_wait_time = config.get('blacklist', 'wait_time')
+ self.bl_wait_time = int(self.bl_wait_time)
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'wait_time' from 'blacklist'")
+
+ try:
+ self.i18ndir = config.get('i18n', 'dir')
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'dir' from 'i18n'")
+
+ try:
+ logdir = config.get('log', 'dir')
+ logfile = os.path.join(logdir, 'smtp.log')
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'dir' from 'log'")
+
+ try:
+ loglevel = config.get('log', 'level')
+ except ConfigParser.Error as e:
+ raise ConfigurationError("Couldn't read 'level' from 'log'")
+
+ # use default values
+ self.core = core.Core(core_cfg)
+
+ # establish log level and redirect to log file
+ log.info('Redirecting logging to %s' % logfile)
+ logfileh = logging.FileHandler(logfile, mode='a+')
+ logfileh.setLevel(logging.getLevelName(loglevel))
+ log.addHandler(logfileh)
+
+ # stop logging on stdout from now on
+ log.propagate = False
+
+ def _is_blacklisted(self, addr):
+ """Check if a user is blacklisted.
+
+ :param: addr (string) the hashed address of the user.
+
+ :return: true is the address is blacklisted, false otherwise.
+
+ """
+
+ try:
+ self.bl.is_blacklisted(addr, 'SMTP', self.bl_max_req,
+ self.bl_wait_time)
+ return False
+ except blacklist.BlacklistError as e:
+ return True
+
+ def _get_lc(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(a)torproject.org) If no locale found, english
+ by default.
+
+ :param: (string) the email address we want to get the locale from.
+
+ :return: (string) the locale (english if none).
+
+ """
+ # if no match found, english by default
+ lc = 'en'
+
+ # look for gettor+locale(a)torproject.org
+ m = re.match('gettor\+(\w\w)(a)\w+\.\w+', addr)
+ if m:
+ # we found a request for locale lc
+ lc = "%s" % m.groups()
+
+ return lc.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).
+
+ :param: addr (string) the address we want to normalize.
+
+ :raise: AddressError if the address can't be normalized.
+
+ :return: (string) the normalized address.
+
+ """
+ if '<' in addr:
+ idx = addr.rindex('<')
+ addr = addr[idx:]
+ m = re.search(r'<([^>]*)>', addr)
+ if m is None:
+ # malformed address
+ raise AddressError("Couldn't extract normalized address "
+ "from %s" % self_get_sha256(addr))
+ addr = m.group(1)
+ return addr
+
+ def _get_content(self, email):
+ """Get the body content of an email.
+
+ :param: email (object) the email object to extract the content from.
+
+ :return: (string) body of the message.
+
+ """
+ # get the body content of the email
+ maintype = email.get_content_maintype()
+ if maintype == 'multipart':
+ for part in email.get_payload():
+ if part.get_content_maintype() == 'text':
+ return part.get_payload()
+ elif maintype == 'text':
+ return email.get_payload()
+
+ def _get_msg(self, msgid, lc):
+ """Get message identified by msgid in a specific locale.
+
+ :param: msgid (string) the identifier of a string.
+ :param: lc (string) the locale.
+
+ :return: (string) the message from the .po file.
+
+ """
+ # obtain the content in the proper language
+ t = gettext.translation(lc, self.i18ndir, languages=[lc])
+ _ = t.ugettext
+
+ msgstr = _(msgid)
+ return msgstr
+
+ def _parse_email(self, msg, addr):
+ """Parse the email received.
+
+ Get the locale and parse the text for the rest of the info.
+
+ :param: msg (string) the content of the email to be parsed.
+ :param: addr (string) the address of the recipient (i.e. us).
+
+ :return: (list) 4-tuple with locale, os and type of request.
+
+ """
+ req = self._parse_text(msg)
+ lc = self._get_lc(addr)
+ req['lc'] = lc
+
+ return req
+
+ def _parse_text(self, 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).
+
+ :param: msg (string) the content of the email to be parsed.
+
+ :return: (list) 3-tuple with the type of request, os and pt info.
+
+ """
+ # by default we asume the request is asking for help
+ req = {}
+ req['type'] = 'help'
+ req['os'] = None
+
+ # core knows what OS are supported
+ supported_os = self.core.get_supported_os()
+
+ # if no OS is found, help request by default
+ found_os = False
+ lines = msg.split(' ')
+ for word in lines:
+ if not found_os:
+ for os in supported_os:
+ if re.match(os, word, re.IGNORECASE):
+ req['os'] = os
+ req['type'] = 'links'
+ found_os = True
+ break
+ else:
+ break
+ return req
+
+ def _create_email(self, from_addr, to_addr, subject, msg):
+ """Create an email object.
+
+ This object will be used to construct the reply.
+
+ :param: from_addr (string) the address of the sender.
+ :param: to_addr (string) the address of the recipient.
+ :param: subject (string) the subject of the email.
+ :param: msg (string) the content of the email.
+
+ :return: (object) the email object.
+
+ """
+ email_obj = MIMEText(msg)
+ email_obj.set_charset("utf-8")
+ email_obj['Subject'] = subject
+ email_obj['From'] = from_addr
+ email_obj['To'] = to_addr
+
+ return email_obj
+
+ 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.
+
+ :param: from_addr (string) the address of the sender.
+ :param: to_addr (string) the address of the recipient.
+ :param: subject (string) the subject of the email.
+ :param: msg (string) the 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, email_obj.as_string())
+ s.quit()
+ except smtplib.SMTPException as e:
+ raise SendEmailError("Error with SMTP: %s" % str(e))
+
+ def _send_links(self, links, lc, os, from_addr, to_addr):
+ """Send links to the user.
+
+ Get the message in the proper language (according to the locale),
+ replace variables and send the email.
+
+ :param: links (string) the links to be sent.
+ :param: lc (string) the locale.
+ :param: os (string) the operating system.
+ :param: from_addr (string) the address of the sender.
+ :param: to_addr (string) the address of the recipient.
+
+ """
+ # obtain the content in the proper language and send it
+ links_subject = self._get_msg('links_subject', lc)
+ links_msg = self._get_msg('links_msg', lc)
+ links_msg = links_msg % (os, lc, links)
+
+ try:
+ self._send_email(from_addr, to_addr, links_subject, links_msg)
+ except SendEmailError as e:
+ raise InternalError("Error while sending links message")
+
+ def _send_help(self, lc, 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.
+
+ :param: lc (string) the locale.
+ :param: from_addr (string) the address of the sender.
+ :param: to_addr (string) the address of the recipient.
+
+ """
+ # obtain the content in the proper language and send it
+ help_subject = self._get_msg('help_subject', lc)
+ help_msg = self._get_msg('help_msg', lc)
+
+ try:
+ self._send_email(from_addr, to_addr, help_subject, help_msg)
+ except SendEmailError as e:
+ raise InternalError("Error while sending help message")
+
+ def _send_unsupported_lc(self, lc, os, from_addr, to_addr):
+ """Send unsupported locale message.
+
+ Get the message for unsupported locale in english, replace variables
+ (if any) and send the email.
+
+ :param: lc (string) the locale.
+ :param: os (string) the operating system.
+ :param: from_addr (string) the address of the sender.
+ :param: to_addr (string) the address of the recipient.
+
+ """
+
+ # obtain the content in english and send it
+ un_lc_subject = self._get_msg('unsupported_lc_subject', 'en')
+ un_lc_msg = self._get_msg('unsupported_lc_msg', 'en')
+ un_lc_msg = un_lc_msg % lc
+
+ try:
+ self._send_email(from_addr, to_addr, un_lc_subject, un_lc_msg)
+
+ except SendEmailError as e:
+ raise InternalError("Error while sending unsupported lc 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.
+
+ :param: raw_msg (string) the email received.
+
+ :raise: InternalError if something goes wrong while asking for the
+ links to the Core module.
+
+ """
+ parsed_msg = email.message_from_string(raw_msg)
+ content = self._get_content(parsed_msg)
+ from_addr = parsed_msg['From']
+ to_addr = parsed_msg['To']
+ bogus_request = False
+ status = ''
+ req = None
+
+ try:
+ # two ways for a request to be bogus: address malformed or
+ # blacklisted
+ try:
+ norm_from_addr = self._get_normalized_address(from_addr)
+ except AddressError as e:
+ status = 'malformed'
+ bogus_request = True
+ # it might be interesting to know what triggered this
+ # we are not logging this for now
+ # logfile = self._log_email('malformed', content)
+
+ if norm_from_addr:
+ anon_addr = utils.get_sha256(norm_from_addr)
+
+ if self._is_blacklisted(anon_addr):
+ status = 'blacklisted'
+ bogus_request = True
+ # it might be interesting to know extra info
+ # we are not logging this for now
+ # logfile = self._log_email(anon_addr, content)
+
+ if not bogus_request:
+ # try to figure out what the user is asking
+ req = self._parse_email(content, to_addr)
+
+ # our address should have the locale requested
+ our_addr = "gettor+%s@%s" % (req['lc'], self.our_domain)
+
+ # two possible options: asking for help or for the links
+ if req['type'] == 'help':
+ # make sure we can send emails
+ try:
+ self._send_help(req['lc'], our_addr, norm_from_addr)
+ except SendEmailError as e:
+ status = 'internal_error'
+ raise InternalError("Something's wrong with the SMTP "
+ "server: %s" % str(e))
+
+ elif req['type'] == 'links':
+ try:
+ links = self.core.get_links('SMTP', req['os'],
+ req['lc'])
+
+ except core.UnsupportedLocaleError as e:
+ # if we got here, the address of the sender should
+ # be valid so we send him/her a message about the
+ # unsupported locale
+ status = 'unsupported_lc'
+ self._send_unsupported_lc(req['lc'], req['os'],
+ our_addr, norm_from_addr)
+ return
+
+ # if core fails, we fail too
+ except (core.InternalError, core.ConfigurationError) as e:
+ status = 'core_error'
+ # something went wrong with the core
+ raise InternalError("Error obtaining the links")
+
+ # make sure we can send emails
+ try:
+ self._send_links(links, req['lc'], req['os'], our_addr,
+ norm_from_addr)
+ except SendEmailError as e:
+ status = 'internal_error'
+ raise SendEmailError("Something's wrong with the SMTP "
+ "server: %s" % str(e))
+ status = 'success'
+ finally:
+ # keep stats
+ if req:
+ self.core.add_request_to_db()
diff --git a/gettor/utils.py b/gettor/utils.py
new file mode 100644
index 0000000..cbf01ce
--- /dev/null
+++ b/gettor/utils.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of GetTor, a Tor Browser distribution system.
+#
+# :authors: Israel Leiva <ilv(a)riseup.net>
+# see also AUTHORS file
+#
+# :copyright: (c) 2008-2014, The Tor Project, Inc.
+# (c) 2014, Israel Leiva
+#
+# :license: This is Free Software. See LICENSE for license information.
+
+import os
+import hashlib
+
+"""Common utilities for GetTor modules."""
+
+
+def get_sha256(string):
+ """Get sha256 of a string.
+
+ :param: (string) the string to be hashed.
+
+ :return: (string) the sha256 of string.
+
+ """
+ return str(hashlib.sha256(string).hexdigest())
diff --git a/gettor/xmpp.py b/gettor/xmpp.py
new file mode 100644
index 0000000..e9d2a10
--- /dev/null
+++ b/gettor/xmpp.py
@@ -0,0 +1,304 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of GetTor, a Tor Browser distribution system.
+#
+# :authors: Israel Leiva <ilv(a)riseup.net>
+# see also AUTHORS file
+#
+# :copyright: (c) 2008-2014, The Tor Project, Inc.
+# (c) 2014, Israel Leiva
+#
+# :license: This is Free Software. See LICENSE for license information.
+
+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 core
+import utils
+import blacklist
+
+
+"""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:
+ # error getting the roster
+ # logging.error(err.iq['error']['condition'])
+ self.disconnect()
+ except IqTimeout:
+ # 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 ConfigError(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:
+
+ ConfigError: Bad configuration.
+ InternalError: Something went wrong internally.
+
+ """
+
+ def __init__(self, cfg=None):
+ """Create new object by reading a configuration file.
+
+ :param: cfg (string) the 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")
+ log = logging.getLogger(__name__)
+ config = ConfigParser.ConfigParser()
+
+ if cfg is None or not os.path.isfile(cfg):
+ cfg = DEFAULT_CONFIG_FILE
+
+ config.read(cfg)
+
+ try:
+ self.user = config.get('account', 'user')
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'user' from 'account'")
+
+ try:
+ self.password = config.get('account', 'password')
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'password' from 'account'")
+
+ try:
+ self.core_cfg = config.get('general', 'core_cfg')
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'core_cfg' from 'general'")
+
+ try:
+ blacklist_cfg = config.get('blacklist', 'cfg')
+ self.bl = blacklist_cfg
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'cfg' from 'blacklist'")
+
+ try:
+ self.bl_max_req = config.get('blacklist', 'max_requests')
+ self.bl_max_req = int(self.bl_max_req)
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'max_requests' from 'blacklist'")
+
+ try:
+ self.bl_wait_time = config.get('blacklist', 'wait_time')
+ self.bl_wait_time = int(self.bl_wait_time)
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'wait_time' from 'blacklist'")
+
+ try:
+ self.i18ndir = config.get('i18n', 'dir')
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'dir' from 'i18n'")
+
+ try:
+ logdir = config.get('log', 'dir')
+ logfile = os.path.join(logdir, 'xmpp.log')
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'dir' from 'log'")
+
+ try:
+ loglevel = config.get('log', 'level')
+ except ConfigParser.Error as e:
+ raise ConfigError("Couldn't read 'level' from 'log'")
+
+ # establish log level and redirect to log file
+ log.info('Redirecting logging to %s' % logfile)
+ logfileh = logging.FileHandler(logfile, mode='a+')
+ logfileh.setLevel(logging.getLevelName(loglevel))
+ log.addHandler(logfileh)
+
+ # stop logging on stdout from now on
+ log.propagate = False
+
+ def start_bot(self):
+ """Start the bot for handling requests.
+
+ Start a new sleekxmpp bot.
+
+ """
+ xmpp = Bot(self.user, self.password, self)
+ xmpp.connect()
+ xmpp.process(block=True)
+
+ def _is_blacklisted(self, account):
+ """Check if a user is blacklisted.
+
+ :param: addr (string) the hashed address of the user.
+
+ :return: true is the address is blacklisted, false otherwise.
+
+ """
+ anon_acc = utils.get_sha256(account)
+ bl = blacklist.Blacklist(self.bl)
+
+ try:
+ bl.is_blacklisted(anon_acc, 'XMPP', self.bl_max_req,
+ self.bl_wait_time)
+ return False
+ except blacklist.BlacklistError as e:
+ return True
+
+ def _get_msg(self, msgid, lc):
+ """Get message identified by msgid in a specific locale.
+
+ :param: msgid (string) the identifier of a string.
+ :param: lc (string) the locale.
+
+ :return: (string) the message from the .po file.
+
+ """
+ # obtain the content in the proper language
+ t = gettext.translation(lc, self.i18ndir, languages=[lc])
+ _ = t.ugettext
+
+ msgstr = _(msgid)
+ return msgstr
+
+ def _parse_text(self, msg, core_obj):
+ """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.
+
+ :param: msg (string) the message received.
+ :param: core_obj (object) the object of gettor core module.
+
+ :return: request (list) 4-tuple with locale, os, type of request
+ and pt info.
+
+ """
+ # core knows what OS are supported
+ supported_os = core_obj.get_supported_os()
+ supported_lc = core_obj.get_supported_lc()
+
+ # default values
+ req = {}
+ req['lc'] = 'en'
+ req['os'] = None
+ req['type'] = 'help'
+ found_lc = 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 lc and os
+ if not found_lc:
+ for lc in supported_lc:
+ if re.match(lc, word, re.IGNORECASE):
+ found_lc = True
+ req['lc'] = lc
+ if not found_os:
+ for os in supported_os:
+ if re.match(os, word, re.IGNORECASE):
+ found_os = True
+ req['os'] = os
+ req['type'] = 'links'
+ return req
+
+ 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.
+
+ :param: account (string) the account that did the request.
+ :param: msg (string) the body of the message sent to us.
+
+ :return: (string/None) the message to be sent to the user via the
+ bot, or None if the user is blacklisted.
+
+ """
+ bogus_request = False
+ reply = ''
+ status = ''
+ req = None
+ core_obj = core.Core(self.core_cfg)
+
+ try:
+ if self._is_blacklisted(str(account)):
+ status = 'blacklisted'
+ bogus_request = True
+
+ if not bogus_request:
+ # let's try to guess what the user is asking
+ req = self._parse_text(str(msg), core_obj)
+
+ if req['type'] == 'help':
+ status = 'success'
+ reply = self._get_msg('help', req['lc'])
+ elif req['type'] == 'links':
+ try:
+ links = core_obj.get_links("XMPP", req['os'],
+ req['lc'])
+ reply = self._get_msg('links', req['lc'])
+ reply = reply % (req['os'], req['lc'], links)
+
+ status = 'success'
+ except (core.ConfigError, core.InternalError) as e:
+ # if core failes, send the user an error message, but
+ # keep going
+ status = 'core_error'
+ reply = self._get_msg('internal_error', req['lc'])
+ finally:
+ # keep stats
+ if req:
+ core_obj.add_request_to_db()
+
+ return reply
diff --git a/lang/smtp/i18n/en/LC_MESSAGES/en.mo b/lang/smtp/i18n/en/LC_MESSAGES/en.mo
new file mode 100644
index 0000000..e686fcb
Binary files /dev/null and b/lang/smtp/i18n/en/LC_MESSAGES/en.mo differ
diff --git a/lang/smtp/i18n/en/LC_MESSAGES/en.po b/lang/smtp/i18n/en/LC_MESSAGES/en.po
new file mode 100644
index 0000000..6f78da2
--- /dev/null
+++ b/lang/smtp/i18n/en/LC_MESSAGES/en.po
@@ -0,0 +1,95 @@
+domain "en"
+
+#: Links subject
+msgid "links_subject"
+msgstr "[GetTor] Links for your request"
+
+#: Links subject (PT)
+msgid "links_pt_subject"
+msgstr "[GetTor] Links for your request"
+
+#: Help subject
+msgid "help_subject"
+msgstr "[GetTor] Help"
+
+#: Delay subject
+msgid "delay_subject"
+msgstr "[GetTor] Delay message"
+
+# Unsupported locale subject
+msgid "unsupported_locale_subject"
+msgstr "[GetTor] Unsupported locale"
+
+# Unsupported locale message
+msgid "unsupported_locale_msg"
+msgstr "The locale you requested '%s' is not supported."
+
+#: Links message
+msgid "links_msg"
+msgstr "Thank you for your request for %s-%s.\n\
+\n\
+Here are the download links:\n\
+\n\
+===\n\
+Tor Browser Bundle:\n\
+===\n\
+%s\n\
+===\n\
+\n\
+===\n\
+Support:\n\
+===\n\
+\n\
+Still need help? If you have any questions, trouble connecting to Tor\n\
+network, or need to talk to a human, please contact our support team at:\n\
+\n\
+ help(a)rt.torproject.org\n\
+\n\
+We are ready to answer your queries in English, Farsi, Chinese, Arabic,\n\
+French and Spanish."
+
+#: Links message (PT)
+msgid "links_pt_msg"
+msgstr "Thank you for your request for %s-%s.\n\
+\n\
+Here are the download links:\n\
+\n\
+===\n\
+Tor Browser Bundle:\n\
+===\n\
+%s\n\
+===\n\
+Info about pluggable transports.\n\
+\n\
+===\n\
+Support:\n\
+===\n\
+\n\
+Still need help? If you have any questions, trouble connecting to Tor\n\
+network, or need to talk to a human, please contact our support team at:\n\
+\n\
+ help(a)rt.torproject.org\n\
+\n\
+We are ready to answer your queries in English, Farsi, Chinese, Arabic,\n\
+French and Spanish."
+
+#: Help message
+msgid "help_msg"
+msgstr "Hello, this is the 'GetTor' robot.\n\
+\n\
+Thank you for your request. I am here to help you download the latest\n\
+Tor Browser Bundle.\n\
+\n\
+Please reply to this message with one of the options below:\n\
+\n\
+ windows\n\
+ linux\n\
+ osx\n\
+\n\
+And I will send you the download instructions quickly.\n\
+\n\
+Tip: Just send a blank reply to this message if you are not sure."
+
+#: Delay message
+msgid "delay_msg"
+msgstr "Delay message."
diff --git a/lang/smtp/i18n/es/LC_MESSAGES/es.mo b/lang/smtp/i18n/es/LC_MESSAGES/es.mo
new file mode 100644
index 0000000..c21fe7b
Binary files /dev/null and b/lang/smtp/i18n/es/LC_MESSAGES/es.mo differ
diff --git a/lang/smtp/i18n/es/LC_MESSAGES/es.po b/lang/smtp/i18n/es/LC_MESSAGES/es.po
new file mode 100644
index 0000000..bc4b5a8
--- /dev/null
+++ b/lang/smtp/i18n/es/LC_MESSAGES/es.po
@@ -0,0 +1,94 @@
+domain "es"
+
+#: Links subject
+msgid "links_subject"
+msgstr "[GetTor] Enlaces para tu petición"
+
+#: Links subject (PT)
+msgid "links_pt_subject"
+msgstr "[GetTor] Enlaces para tu petición"
+
+#: Help subject
+msgid "help_subject"
+msgstr "[GetTor] Ayuda"
+
+#: Delay subject
+msgid "delay_subject"
+msgstr "[GetTor] Mensaje de demora"
+
+# Unsupported locale subject
+msgid "unsupported_locale_subject"
+msgstr "[GetTor] Locale no soportado"
+
+# Unsupported locale message
+msgid "unsupported_locale_msg"
+msgstr "El locale especificado %s no está soportado."
+
+#: Links message
+msgid "links_msg"
+msgstr """Gracias por tu petición para %s-%s.\n\
+\n\
+Aquí están los links de descarga:\n\
+\n\
+===\n\
+%s\n\
+===\n\
+\n\
+===\n\
+Soporte:\n\
+===\n\
+\n\
+¿Aún necesitas ayuda? Si tienes dudas, problemas para conectarte a la\n\
+red Tor, o necesitas hablar con un humano, por favor contacta a nuestro\n\
+equipo de soporte a:\n\
+\n\
+ help(a)rt.torproject.org\n\
+\n\
+Estamos listos para responder tus consultas en Inglés, Farsi, Chino,\n\
+Arábico, Francés y Español."""
+
+#: Links message (PT)
+msgid "links_pt_msg"
+msgstr """Gracias por tu petición para %s-%s.\n\
+\n\
+Aquí están los links de descarga:\n\
+\n\
+===\n\
+%s\n\
+===\n\
+Tip: Información sobre pluggable transports.\n\
+\n\
+===\n\
+Soporte:\n\
+===\n\
+\n\
+¿Aún necesitas ayuda? Si tienes dudas, problemas para conectarte a la\n\
+red Tor, o necesitas hablar con un humano, por favor contacta a nuestro\n\
+equipo de soporte a:\n\
+\n\
+ help(a)rt.torproject.org\n\
+\n\
+Estamos listos para responder tus consultas en Inglés, Farsi, Chino,\n\
+Arábico, Francés y Español."""
+
+#: Help message
+msgid "help_msg"
+msgstr "Hola, este es el robot 'GetTor'.\n\
+\n\
+Gracias por tu petición. Estoy aquí para ayudarte a descargar la última\n\
+versión del Tor Browser Bundle.\n\
+\n\
+Por favor responde a este mensaje con una de las siguientes opciones:\n\
+\n\
+ windows\n\
+ linux\n\
+ osx\n\
+\n\
+Y te enviaré las instrucciones de descarga.\n\
+\n\
+Tip: Sólo envía una respuesta en blanco a este mensaje si no estás\n\
+seguro."
+
+#: Delay message
+msgid "delay_msg"
+msgstr "Mensaje de demora."
diff --git a/lang/xmpp/i18n/en/LC_MESSAGES/en.mo b/lang/xmpp/i18n/en/LC_MESSAGES/en.mo
new file mode 100644
index 0000000..2480872
Binary files /dev/null and b/lang/xmpp/i18n/en/LC_MESSAGES/en.mo differ
diff --git a/lang/xmpp/i18n/en/LC_MESSAGES/en.po b/lang/xmpp/i18n/en/LC_MESSAGES/en.po
new file mode 100644
index 0000000..d242625
--- /dev/null
+++ b/lang/xmpp/i18n/en/LC_MESSAGES/en.po
@@ -0,0 +1,22 @@
+domain "en"
+
+#: Links
+msgid "links"
+msgstr "Links %s-%s:\n %s"
+
+#: Links
+msgid "links_pt"
+msgstr "Links-PT for %s-%s:\n %s"
+
+#: Help
+msgid "help"
+msgstr "*help*"
+
+#: Unsupported locale
+msgid "unsupported_lc"
+msgstr "Unsupported locale"
+
+#: Internal error
+msgid "internal_error"
+msgstr "Internal error"
+
diff --git a/lang/xmpp/i18n/en/LC_MESSAGES/es.mo b/lang/xmpp/i18n/en/LC_MESSAGES/es.mo
new file mode 100644
index 0000000..5896536
Binary files /dev/null and b/lang/xmpp/i18n/en/LC_MESSAGES/es.mo differ
diff --git a/lang/xmpp/i18n/es/LC_MESSAGES/es.mo b/lang/xmpp/i18n/es/LC_MESSAGES/es.mo
new file mode 100644
index 0000000..d54f22c
Binary files /dev/null and b/lang/xmpp/i18n/es/LC_MESSAGES/es.mo differ
diff --git a/lang/xmpp/i18n/es/LC_MESSAGES/es.po b/lang/xmpp/i18n/es/LC_MESSAGES/es.po
new file mode 100644
index 0000000..b6caac5
--- /dev/null
+++ b/lang/xmpp/i18n/es/LC_MESSAGES/es.po
@@ -0,0 +1,22 @@
+domain "es"
+
+#: Links
+msgid "links"
+msgstr "Enlaces para %s-%s:\n %s"
+
+#: Links
+msgid "links_pt"
+msgstr "Enlaces-PT para %s-%s:\n %s"
+
+#: Help
+msgid "help"
+msgstr "ayuda"
+
+#: Unsupported locale
+msgid "unsupported_lc"
+msgstr "Locale no soportado"
+
+#: Internal error
+msgid "internal_error"
+msgstr "Error interno"
+
diff --git a/log/blacklist.log b/log/blacklist.log
new file mode 100644
index 0000000..e69de29
diff --git a/log/core.log b/log/core.log
new file mode 100644
index 0000000..e69de29
diff --git a/log/smtp.log b/log/smtp.log
new file mode 100644
index 0000000..e69de29
diff --git a/log/xmpp.log b/log/xmpp.log
new file mode 100644
index 0000000..e69de29
diff --git a/providers/dropbox.links b/providers/dropbox.links
new file mode 100644
index 0000000..e0ffaf0
--- /dev/null
+++ b/providers/dropbox.links
@@ -0,0 +1,25 @@
+[provider]
+name = Dropbox
+
+[key]
+fingerprint = 8738 A680 B84B 3031 A630 F2DB 416F 0610 63FE E659
+
+[linux]
+en = Package (64-bit): https://www.dropbox.com/s/p2l6keaoly2kzce/tor-browser-linux64-3.6.2_en-EN.t…
+ ASC signature (64-bit): https://www.dropbox.com/s/b85ecxkoz9126j3/tor-browser-linux64-3.6.2_en-EN.t…
+ Package SHA256 checksum (64-bit): 98ea6e4f216f2fb4b69fff9b3a44842c38686ca685f3f55dc48c5d3fb1107be4,
+ Package (32-bit): https://www.dropbox.com/s/rxfczlz7j8gn6pi/tor-browser-linux32-3.6.2_en-EN.t…
+ ASC signature (32-bit): https://www.dropbox.com/s/g5g0havjgunf1un/tor-browser-linux32-3.6.2_en-EN.t…
+ Package SHA256 checksum (32-bit): 98ea6e4f216f2fb4b69fff9b3a44842c38686ca685f3f55dc48c5d3fb1107be4
+es = Package (32-bit): https://www.dropbox.com/s/xkv4dvmeuppscs2/tor-browser-linux32-3.6.2_es-ES.t…
+ ASC signature (32-bit): https://www.dropbox.com/s/66c5x6jnzrs67vw/tor-browser-linux32-3.6.2_es-ES.t…
+ Package SHA256 checksum (32-bit): 98ea6e4f216f2fb4b69fff9b3a44842c38686ca685f3f55dc48c5d3fb1107be4
+
+
+[windows]
+en = Package (32-bit): https://www.dropbox.com/s/oocrw3joj4d8kn8/tor-browser-windows32-3.6.2_en-EN…
+ ASC signature (32-bit): https://www.dropbox.com/s/gwbepymnaxvhm94/tor-browser-windows32-3.6.2_en-EN…
+ Package SHA256 checksum (32-bit): 98ea6e4f216f2fb4b69fff9b3a44842c38686ca685f3f55dc48c5d3fb1107be4
+
+[osx]
+
diff --git a/providers/dropbox.links.backup b/providers/dropbox.links.backup
new file mode 100644
index 0000000..e0ffaf0
--- /dev/null
+++ b/providers/dropbox.links.backup
@@ -0,0 +1,25 @@
+[provider]
+name = Dropbox
+
+[key]
+fingerprint = 8738 A680 B84B 3031 A630 F2DB 416F 0610 63FE E659
+
+[linux]
+en = Package (64-bit): https://www.dropbox.com/s/p2l6keaoly2kzce/tor-browser-linux64-3.6.2_en-EN.t…
+ ASC signature (64-bit): https://www.dropbox.com/s/b85ecxkoz9126j3/tor-browser-linux64-3.6.2_en-EN.t…
+ Package SHA256 checksum (64-bit): 98ea6e4f216f2fb4b69fff9b3a44842c38686ca685f3f55dc48c5d3fb1107be4,
+ Package (32-bit): https://www.dropbox.com/s/rxfczlz7j8gn6pi/tor-browser-linux32-3.6.2_en-EN.t…
+ ASC signature (32-bit): https://www.dropbox.com/s/g5g0havjgunf1un/tor-browser-linux32-3.6.2_en-EN.t…
+ Package SHA256 checksum (32-bit): 98ea6e4f216f2fb4b69fff9b3a44842c38686ca685f3f55dc48c5d3fb1107be4
+es = Package (32-bit): https://www.dropbox.com/s/xkv4dvmeuppscs2/tor-browser-linux32-3.6.2_es-ES.t…
+ ASC signature (32-bit): https://www.dropbox.com/s/66c5x6jnzrs67vw/tor-browser-linux32-3.6.2_es-ES.t…
+ Package SHA256 checksum (32-bit): 98ea6e4f216f2fb4b69fff9b3a44842c38686ca685f3f55dc48c5d3fb1107be4
+
+
+[windows]
+en = Package (32-bit): https://www.dropbox.com/s/oocrw3joj4d8kn8/tor-browser-windows32-3.6.2_en-EN…
+ ASC signature (32-bit): https://www.dropbox.com/s/gwbepymnaxvhm94/tor-browser-windows32-3.6.2_en-EN…
+ Package SHA256 checksum (32-bit): 98ea6e4f216f2fb4b69fff9b3a44842c38686ca685f3f55dc48c5d3fb1107be4
+
+[osx]
+
diff --git a/scripts/blacklist.py b/scripts/blacklist.py
new file mode 100644
index 0000000..1aefe1a
--- /dev/null
+++ b/scripts/blacklist.py
@@ -0,0 +1,111 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# This file is part of GetTor, a Tor Browser distribution system.
+#
+# :authors: Israel Leiva <ilv(a)riseup.net>
+# see also AUTHORS file
+#
+# :copyright: (c) 2008-2014, The Tor Project, Inc.
+# (c) 2014, Israel Leiva
+#
+# :license: This is Free Software. See LICENSE for license information.
+
+import sys
+import time
+import sqlite3
+import argparse
+
+
+def main():
+ """Script for managing blacklisting of users.
+
+ See argparse usage for more details.
+
+ """
+ parser = argparse.ArgumentParser(description='Utility for GetTor'
+ ' blacklisting')
+ parser.add_argument('database', metavar='database.db', type=str,
+ help='the database file')
+ parser.add_argument('-u', '--user', default=None,
+ help='filter by user hash')
+ parser.add_argument('-s', '--service', default=None,
+ help='filter by service')
+ parser.add_argument('-b', '--blocked', default=None,
+ help='filter by blocked users')
+ parser.add_argument('-a', '--add', default=None, nargs=3,
+ metavar=('USER', 'SERVICE', 'BLOCKED'),
+ help='add user')
+ parser.add_argument('-c', '--clean', default=None, const='c', nargs='?',
+ metavar='user hash',
+ help='clean table (delete expired blacklistings)')
+ parser.add_argument('-r', '--requests', default=None,
+ help='number of requests; everyone with number of'
+ ' requests greather than this will be cleaned up')
+
+ args = parser.parse_args()
+ query = ''
+ con = sqlite3.connect(args.database)
+
+ if args.add:
+ # add new entry, useful for adding users permanently blocked
+ query = "INSERT INTO users VALUES('%s', '%s', 1, %s, %s)"\
+ % (args.add[0], args.add[1], args.add[2], time.time())
+ with con:
+ cur = con.cursor()
+ cur.execute(query)
+ print "Query execute successfully"
+ elif args.clean:
+ if args.clean == 'c':
+ if args.requests:
+ # delete by number of times
+ query = "DELETE FROM users WHERE times > %s" % args.requests
+ with con:
+ cur = con.cursor()
+ cur.execute(query)
+ print "Query executed successfully."
+ else:
+ sys.exit("Number of requests missing. See --help.")
+ else:
+ # delete by id
+ query = "DELETE FROM users WHERE id='%s'" % args.clean
+ with con:
+ cur = con.cursor()
+ cur.execute(query)
+ print "Query execute succcessfully."
+ else:
+ query = "SELECT * FROM users"
+ has_where = False
+ # filter
+ if args.service:
+ query = "%s %s" % (query, "WHERE service='%s'" % args.service)
+ has_where = True
+ if args.user:
+ if has_where:
+ query = "%s %s" % (query, "AND id='%s'" % args.user)
+ else:
+ query = "%s %s" % (query, "WHERE id='%s'" % args.user)
+ has_where = True
+ if args.blocked:
+ if has_where:
+ query = "%s %s" % (query, "AND blocked=%s" % args.blocked)
+ has_where = True
+ else:
+ query = "%s %s" % (query, "WHERE blocked=%s" % args.blocked)
+
+ with con:
+ cur = con.cursor()
+ cur.execute(query)
+ rows = cur.fetchall()
+ # show it nice
+ print "\nNumber of results: %s\n" % len(rows)
+ cns = [cn[0] for cn in cur.description]
+ print "%-70s %-10s %-10s %-10s %-s" % (cns[0], cns[1], cns[2],
+ cns[3], cns[4])
+
+ for row in rows:
+ print "%-70s %-10s %-10s %-10s %s" % (row[0], row[1], row[2],
+ row[3], row[4])
+
+if __name__ == "__main__":
+ main()
diff --git a/scripts/create_db.py b/scripts/create_db.py
new file mode 100644
index 0000000..ec4ba32
--- /dev/null
+++ b/scripts/create_db.py
@@ -0,0 +1,57 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# This file is part of GetTor, a Tor Browser distribution system.
+#
+# :authors: Israel Leiva <ilv(a)riseup.net>
+# see also AUTHORS file
+#
+# :copyright: (c) 2008-2014, The Tor Project, Inc.
+# (c) 2014, Israel Leiva
+#
+# :license: This is Free Software. See LICENSE for license information.
+
+import os
+import sqlite3
+import argparse
+
+
+def main():
+ """Create/delete GetTor database for managing stats and blacklisting.
+
+ Database file (.db) must be empty. If it doesn't exist, it will be
+ created. See argparse usage for more details.
+
+ """
+ parser = argparse.ArgumentParser(description='Utility for GetTor'
+ ' database')
+ parser.add_argument('-c', '--create', default=None,
+ metavar='path_to_database.db',
+ help='create database')
+ parser.add_argument('-d', '--delete', default=None,
+ metavar='path_to_database.db',
+ help='delete database')
+
+ args = parser.parse_args()
+ if args.create:
+ con = sqlite3.connect(args.create)
+ with con:
+ cur = con.cursor()
+ # table for handling users (i.e. blacklist)
+ cur.execute("CREATE TABLE users(id TEXT, service TEXT, times INT,"
+ " blocked INT, last_request TEXT)")
+ # table for stats
+ # cur.execute("CREATE TABLE requests(service TEXT, type TEXT,"
+ # " os TEXT, lc TEXT, pt INT, year INT, month INT,"
+ # " day INT, status TEXT, logfile TEXT)")
+
+ # for now we will use a counter
+ cur.execute("CREATE TABLE requests(id INT, counter INT)")
+ print "Database %s created" % os.path.abspath(args.create)
+ elif args.delete:
+ os.remove(os.path.abspath(args.delete))
+ print "Database %s deleted" % os.path.abspath(args.delete)
+ else:
+ print "See --help for details on usage."
+if __name__ == "__main__":
+ main()
diff --git a/scripts/stats.py b/scripts/stats.py
new file mode 100644
index 0000000..7fca2af
--- /dev/null
+++ b/scripts/stats.py
@@ -0,0 +1,123 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# This file is part of GetTor, a Tor Browser distribution system.
+#
+# :authors: Israel Leiva <ilv(a)riseup.net>
+# see also AUTHORS file
+#
+# :copyright: (c) 2008-2014, The Tor Project, Inc.
+# (c) 2014, Israel Leiva
+#
+# :license: This is Free Software. See LICENSE for license information.
+
+
+import sqlite3
+import argparse
+
+
+def main():
+ """Script for showing stats.
+
+ See argparse usage for more details.
+
+ """
+ parser = argparse.ArgumentParser(description='Utility for GetTor stats')
+ parser.add_argument('database', metavar='database.db', type=str,
+ help='the database file')
+ parser.add_argument('-s', '--service', default=None,
+ help='filter by service')
+ parser.add_argument('-t', '--type', default=None,
+ help='filter by type of request')
+ parser.add_argument('-o', '--os', default=None,
+ help='filter by OS')
+ parser.add_argument('-l', '--lc', default=None,
+ help='filter by locale')
+ parser.add_argument('-p', '--pt', default=None,
+ help='filter by PT requests')
+ parser.add_argument('-y', '--year', default=None,
+ help='filter by year')
+ parser.add_argument('-m', '--month', default=None,
+ help='filter by month')
+ parser.add_argument('-d', '--day', default=None,
+ help='filter by day')
+ parser.add_argument('-u', '--status', default=None,
+ help='filter by status of the request')
+
+ args = parser.parse_args()
+ query = 'SELECT * FROM requests'
+ has_where = False
+
+ # we build the query piece by piece
+ if args.service:
+ query = "%s %s" % (query, "WHERE service = '%s'" % args.service)
+ has_where = True
+ if args.type:
+ if has_where:
+ query = "%s %s" % (query, "AND type = '%s'" % args.type)
+ else:
+ query = "%s %s" % (query, "WHERE type = '%s'" % args.type)
+ has_where = True
+ if args.os:
+ if has_where:
+ query = "%s %s" % (query, "AND os = '%s'" % args.os)
+ else:
+ query = "%s %s" % (query, "WHERE os = '%s'" % args.os)
+ has_where = True
+ if args.lc:
+ if has_where:
+ query = "%s %s" % (query, "AND lc = '%s'" % args.lc)
+ else:
+ query = "%s %s" % (query, "WHERE lc = '%s'" % args.lc)
+ has_where = True
+ if args.pt:
+ if has_where:
+ query = "%s %s" % (query, "AND pt = %s" % args.pt)
+ else:
+ query = "%s %s" % (query, "WHERE pt = %s" % args.pt)
+ has_where = True
+ if args.year:
+ if has_where:
+ query = "%s %s" % (query, "AND year = %s" % args.year)
+ else:
+ query = "%s %s" % (query, "WHERE year = %s" % args.year)
+ has_where = True
+ if args.month:
+ if has_where:
+ query = "%s %s" % (query, "AND month = %s" % args.month)
+ else:
+ query = "%s %s" % (query, "WHERE month = %s" % args.month)
+ has_where = True
+ if args.day:
+ if has_where:
+ query = "%s %s" % (query, "AND day = %s" % args.day)
+ else:
+ query = "%s %s" % (query, "WHERE day = %s" % args.day)
+ has_where = True
+ if args.status:
+ if has_where:
+ query = "%s %s" % (query, "AND status = '%s'" % args.status)
+ else:
+ query = "%s %s" % (query, "WHERE status = '%s'" % args.status)
+ has_where = True
+
+ con = sqlite3.connect(args.database)
+
+ with con:
+ cur = con.cursor()
+ cur.execute(query)
+ rows = cur.fetchall()
+ # show it nice
+ print "\nNumber of results: %s\n" % len(rows)
+ cns = [cn[0] for cn in cur.description]
+ print "%-10s %-10s %-10s %-10s %-10s %-10s %-10s %-10s"\
+ " %-15s %s"\ % (cns[0], cns[1], cns[2], cns[3], cns[4], cns[5],
+ cns[6], cns[7], cns[8], cns[9])
+
+ for row in rows:
+ print "%-10s %-10s %-10s %-10s %-10s %-10s %-10s %-10s"\
+ " %-15s %s" % (row[0], row[1], row[2], row[3], row[4],
+ row[5], row[6], row[7], row[8], row[9])
+
+if __name__ == "__main__":
+ main()
diff --git a/smtp.cfg b/smtp.cfg
new file mode 100644
index 0000000..671c090
--- /dev/null
+++ b/smtp.cfg
@@ -0,0 +1,16 @@
+[general]
+basedir: /path/to/gettor/smtp
+our_domain: torproject.org
+core_cfg: /path/to/core.cfg
+
+[blacklist]
+cfg: /path/to/blacklist.cfg
+max_requests: 3
+wait_time: 20
+
+[i18n]
+dir: /path/to/i18n/
+
+[log]
+level: DEBUG
+dir: /path/to/log/
\ No newline at end of file
diff --git a/smtp_demo.py b/smtp_demo.py
new file mode 100644
index 0000000..0ec341e
--- /dev/null
+++ b/smtp_demo.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+import sys
+
+import gettor.smtp
+
+service = gettor.smtp.SMTP()
+
+# 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 gettor.smtp.ConfigError 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/spec/design/blacklist.txt b/spec/design/blacklist.txt
deleted file mode 100644
index 34ce40e..0000000
--- a/spec/design/blacklist.txt
+++ /dev/null
@@ -1,74 +0,0 @@
- Google Summer of Code 2014 GetTor Revamp - Blacklist module
- Author: Israel Leiva - <israel.leiva(a)usach.cl>
- Last update: 2014-05-16
- Version: 0.01
- Changes: First version
-
-1. Preface
-
- Since GetTor was created it has been a collection of functions and
- classes separated in various modules. As its main purpose was
- to serve files over SMTP, almost all current files have SMTP-related
- procedures, including black and white lists. The proposed design for
- the blacklist module intends to separate GetTor services from the
- blacklist procedures.
-
-2. Blacklist module
-
- The main functionalities the blacklist module should provide are:
-
- * Check if a given entry is blacklisted for a given service
- * Add/update/remove entries.
- * Provide a standard critera to prevent flood.
-
-3. Design
-
- The new design should consist of the following files, directories and
- functions:
-
- * lib/gettor/blacklist/: Directory for storing blacklisted users of
- different services.
-
- ----- service1.blacklist: users blocked for service1
- ----- service2.blacklist: users blocked for service2
-
- * lib/gettor/blacklist.py: Blacklist module of GetTor.
-
- isBlacklisted(address, provider)
- Check if the given address is in the blacklist of services and
- if it acomplish certain restrictions. For example, it could
- check when was the last time it was updated on the blacklist.
-
-
-4. Roadmap
-
- A possible example of how the blacklist module could work:
-
- a. A service receives a request and call the blacklist module.
- b. The blacklist module check the blacklist for that service.
- c. If the user (hashed) is present in the blacklist, it checks when
- was the last time it was updated. If this date is more than X days
- ago, updates the entry with the current date and returns false. If
- not, returns true. If the user is not present in the file, it adds
- the user with the current date and returns false.
-
-
-5. Discussion
-
-5.1 Mistakes
-
- Mistakes concerning the package requested should be consider. If I
- send a message asking for a Linux 'es' request but I write 'en' instead
- of 'es', should I be able to ask again? or wait until I'm no longer
- blacklisted?
-
-5.2 Users
-
- The method presented above (Roadmap) should consider weekly or monthly
- clean-up of the list.
-
-5.3 SorryMessage
-
- May be when a user tries to send too many requests we could send him
- a message saying that he won't be able to ask for packages in the next
- X days.
diff --git a/spec/design/core.txt b/spec/design/core.txt
deleted file mode 100644
index ce07238..0000000
--- a/spec/design/core.txt
+++ /dev/null
@@ -1,155 +0,0 @@
- Google Summer of Code 2014 GetTor Revamp - Core module
- Author: Israel Leiva - <israel.leiva(a)usach.cl, ilv(a)riseup.net>
- Last update: 2014-07-15
- Version: 0.05
- Changes:
- [0.05]
- Added fingerprint support
- [0.04]
- Changed log format in 'Design'
- Added section 'Features'
- Deleted stuff from 'Discussion' and added some others
- [0.03]
- Changed proposed format for link files to RFC 882 (ConfigParser).
- Read configuration from file with ConfigParser.
- [0.02]
- Combine official mirrors with providers links (as another provider).
- Eliminated on demand link generation. Now it reads from files.
- Modified description according to PEP-8.
- [0.01]
- First version.
-
-
-1. Preface
-
- Since GetTor was created it has been a collection of functions and
- classes separated in various modules. As its main purpose was
- to serve files over SMTP, almost all current files have SMTP-related
- procedures, including address normalization, message composition, etc.
- The proposed design for the core module intends to separate GetTor
- main functionalities which are independent of the service that
- transports the bundles.
-
-2. Core module
-
- The main functionalities the core module should provide are:
-
- * Receive a request with OS version, bundle language, and respond
- with the respective links.
- * Read links from providers files.
- * Log anonymous transactions.
-
-3. Design
-
- The new design should consist of the following files, directories and
- methods:
-
- * gettor.cfg: Configuration values, e.g. base directory.
-
- * providers/: Directory for generated links. Should be specified on
- gettor.cfg.
-
- ----- provider1.links: links from provider1.
- ----- provider2.links: links from provider2.
- ----- mirrors.links: links of official mirrors.
-
- All this data is generated automatically.
-
- * logs/: Directory for logs. Should be specified on gettor.cfg
-
- ----- all.log
- ----- info.log
- ----- debug.log
- ----- warn.log
- ----- error.log
-
- * Core module of GetTor.
-
- __init(config_file)__
- Creates a new Core object. It reads its configuration from
- the config_file using ConfigParser.
-
- get_links(operating_system, locale)
- Public method to obtain the links. Returns links for
- operating_system in locale language. It checks if the operating
- system and locale are supported and then calls the private
- method _get_links()
-
-
- Example: get_links('linux', 'en')
-
- _get_links(operating_system, locale)
- Gets the links for a specific operating system and locale
- according to the options received. It reads all the .links
- files inside the providers directory. Each one of these files
- should follow the ConfigParser's format. There should be a
- section [provider] with the option 'name' for the provider's
- name (e.g. Dropbox), and a section [key] with the option
- 'fingerprint' for the key's fingerprint that signed the
- uploaded packages.
-
- Following sections should specify the operating system and
- its options should be the locale. When more than one link is
- available per operating system and locale (always) the links
- should be specified as a multiline value. Each link has the
- format:
-
- link link_signature
-
- Example:
-
- [provider]
- name: Dropbox
-
- [key]
- fingerprint: 123A 456B 789C 012D 345E 678F 901G 234H 567I 890J
-
- [linux]
- en: https://foo.bar https://foo.bar.asc,
- https://foo.bar https://foo.bar.asc
-
- es: https://bar.baz https://bar.baz.asc
-
- PROVIDER NAME
- operating_system locale link package_signature
-
- NOTE: For now, the official mirrors are considered just as
- another provider.
-
-
- _log_request(operating_system, locale)
- Log information about the request for future stats (e.g. which
- OS from which service is the most required). All logging
- should be done with the logging module.
-
- * Providers: There should be one module/script per provider in charge
- of generating the .links file with the proper format, including
- the official mirrors.
-
-
-4. Roadmap
-
- An example of how the core module work:
-
- a. The SMTP service receives a request.
- b. The SMTP calls core.get_links() with the options sent by the user.
- c. get_links() calls to _log_request().
- d. get_links() calls to _get_links().
- e. get_links() constructs a message with the information obtained.
- f. get_links() returns the message previously constructed.
- g. The SMTP service creates a message with the links obtained and
- send it to the user.
-
-5. Discussion
-
-5.1 Daily logs
-
- Currently, logs are separated by level of information (debug, info, error).
- Is it necessary to do this by days/weeks too?
-
-6. Features
-
- Possible features to be added in the future (open to discussion)
-
- a. Send HTTP links (currently some official mirrors are HTTP only).
-
diff --git a/spec/design/smtp.txt b/spec/design/smtp.txt
deleted file mode 100644
index e86e864..0000000
--- a/spec/design/smtp.txt
+++ /dev/null
@@ -1,118 +0,0 @@
- Google Summer of Code 2014 GetTor Revamp - SMTP module
- Author: Israel Leiva - <israel.leiva(a)usach.cl>
- Last update: 2014-05-16
- Version: 0.01
- Changes: First version
-
-1. Preface
-
- Since GetTor was created it has been a collection of functions and
- classes separated in various modules. As its main purpose was
- to serve files over SMTP, almost all current files have SMTP-related
- procedures, including address normalization, message composition, etc.
- The proposed design for the SMTP module intends to separate GetTor
- main functionalities from the services, in this case, SMTP.
-
-2. SMTP module
-
- The main functionalities the SMTP module should provide are:
-
- * Receive requests via mail.
- * Identify user instructions, such as ask for help or for a specific
- bundle (OS version, language).
- * Get the required links from the core module.
- * Send different types of answers to the user.
- * Manage black lists to avoid flood.
- * Log requests for stats (anonymous).
-
-3. Design
-
- The new design should consist of the following files, directories and
- functions:
-
- * lib/gettor/services/smtp.py: SMTP module of GetTor.
-
- __init__(configuration path)
- Creates an object according to the configuration values.
-
- processEmail(email object)
- Process emails received (by forwarding).
-
- __parseEmail(email object)
- Parse the raw email sent by processMail(). Check for multi-part
- emails and then parse the text part. It also tries to get the
- locale information from the user's address.
-
- __parseText(email object)
- Parse the text part of an email looking for the package requested.
-
- __getFrom(email object)
- Returns the from address of an email object.
-
- __getLocale(address)
- Tries to get the locale information from an email address.
-
- __checkBlacklist(address)
- Check if the given address is blacklisted by comparing the
- hashed address. If address is not present, it's added. If present,
- check for the date when it was added. Yet to define how many
- days will be considered for blacklisting or if another method
- will be used. For this it uses the blacklist module.
-
- __sendReply(address)
- Sends a reply to the user with the links required. It asks for
- the links to the core module.
-
- __sendDelayAlert(address)
- If enabled (on configuration), sends a delay message to the user
- letting him know that the links are on their way.
-
- __sendHelp(address)
- Sends a message to the user with help instructions.
-
- __createEmail(from, to, subject, body)
- Creates an email object.
-
- __logRequest(options)
- Log information about the request for future stats (e.g. which
- OS and language are the most required). If this is called
- after a failure, a copy of the email should be stored.
-
- * BASE_DIR/logs/: Directory for logs. The BASE_DIR should be in the
- configuration file.
-
- ----- yyyy-mm-dd.log: daily log of requests.
-
-
-4. Roadmap
-
- An example of how the SMTP module should work:
-
- a. The SMTP service receives a request (via forwarding).
- b. The email sender is checked for blacklisting (comparing hashes).
- c. The email is parsed, obtaining the package requested and the
- locale information.
- d. If the email was asking for help, a help reply is sent.
- e. If the email was invalid, the process break. This fail is logged
- and the email that triggered it, too.
- f. If the email was valid and the delay alert is set, then a reply
- informing the links are on their way is sent.
- g. If the email was valid, the SMTP service asks for the links to the
- code module.
- h. After that, a reply is sent to the user.
- i. In all cases the request is logged (with no user information).
-
-
-5. Discussion
-
-5.1 Email forwarding
-
- Are we going to support forwarding emails as ForwardPackage() did in
- the old GetTor?
-
-5.2 Blacklist sublists
-
- Now with less types of request (two if no forwarding is added), creating
- sublists for different types of requests necessary to blacklist and
- email address? Or should we blacklist it if it floods anything?
-
diff --git a/spec/design/twitter.txt b/spec/design/twitter.txt
deleted file mode 100644
index 46a178a..0000000
--- a/spec/design/twitter.txt
+++ /dev/null
@@ -1,99 +0,0 @@
- Google Summer of Code 2014 GetTor Revamp - Twitter module
- Author: Israel Leiva - <israel.leiva(a)usach.cl>
- Last update: 2014-05-16
- Version: 0.01
- Changes: First version
-
-1. Preface
-
- Since GetTor was created it has been a collection of functions and
- classes separated in various modules. As its main purpose was
- to serve files over SMTP, almost all current files have SMTP-related
- procedures, including address normalization, message composition, etc.
- The proposed design for the Twitter module intends to separate GetTor
- main functionalities from the services, in this case, Twitter.
-
-2. Twitter module
-
- The main functionalities the Twitter module should provide are:
-
- * Receive requests via direct messages.
- * Identify user instructions, such as ask for help or for a specific
- bundle (OS version, language).
- * Get the required links from the core module.
- * Send different types of answers to the user.
- * Split answers to fit Twitter's format.
- * Manage black lists to avoid flood.
- * Log requests for stats (anonymous).
-
-3. Design
-
- The new design should consist of the following files, directories and
- functions:
-
- * lib/gettor/services/Twitter.py: Twitter module of GetTor.
-
- __init__(configuration path)
- Creates an object according to the configuration values.
-
- processDM(message)
- Process direct messages received.
-
- __parseDM(message)
- Parse the direct message received. Check for the package requested
- and the locale information.
-
- __getUser(message)
- Gets the user from the message sent.
-
- __checkBlacklist(user)
- Check if the given user is blacklisted by comparing the
- hashed user. Yet to define how many days will be considered for
- blacklisting or if another method will be used. For this it uses
- the blacklist module.
-
- __sendReply(user)
- Sends a reply to the user with the links required. It asks for
- the links to the core module and then split them.
-
- __sendHelp(user)
- Sends a message to the user with help instructions.
-
- __splitMessage(message)
- Receives the links message and split it according to Twitter's
- format.
-
- __CheckNewFollowers()
- In order to ask for links the user has to follow the GetTor
- account. The Twitter module will be constantly checking for
- new followers and follow them back.
-
- __FollowUser(user)
- Follow the given user.
-
- __logRequest(options)
- Log information about the request for future stats (e.g. which
- OS and language are the most required). If this is called
- after a failure, a copy of the DM should be stored.
-
- * BASE_DIR/logs/: Directory for logs. BASE_DIR should be in the
- configuration file.
-
- ----- yyyy-mm-dd.log: daily log of requests.
-
-
-4. Roadmap
-
- An example of how the Twitter module should work:
-
- a. The Twitter account receives a DM.
- b. The Twitter service check if is a valid message and if the user is
- in the blacklist, and then tries to obtain the package requested and
- the locale information.
- c. The Twitter service asks for the links to the core module, then it
- splits the message received to adopt Twitter's format.
- d. One or more DMs are sent back to the user.
- e. For all this, the user must follow the GetTor account. The Twitter
- service will be constantly checking for new followers and following
- them back.
-
diff --git a/spec/overview.txt b/spec/overview.txt
deleted file mode 100644
index 435d256..0000000
--- a/spec/overview.txt
+++ /dev/null
@@ -1,94 +0,0 @@
- Google Summer of Code 2014 GetTor Revamp - Overview
- Author: Israel Leiva - <israel.leiva(a)usach.cl>
- Last update: 2014-05-16
- Version: 0.01
- Changes: First version
-
-1. Background
-
- GetTor was created as a program for serving Tor and related files over
- SMTP, thus avoiding direct and indirect censorship of Tor's software,
- in particular, the Tor Browser Bundle (TBB). Users interact with GetTor
- by sending emails to a specific email address. In the past, after the
- user specified his OS and language, GetTor would send him an attachment
- with the required package. This worked until the bundles were too large
- to be sent as attachments in most email providers. In order to fix this
- GetTor started to send links instead.
-
-2. Current status
-
- The GetTor status can be summarized in the following points:
-
- * Emails are sent to gettor(a)torproject.org
- * The GetTor reply contains: TBB links, signatures (with text guides
- for verification), mirrors, support instructions in six languages.
- * Dropbox links are sent to download the TBB and signatures.
- * Users can not ask for packages in their language.
- * English-only replies are sent.
- * Any email directed to GetTor is replied with the same information,
- there is no recognition of instructions.
- * Links generation is not fully automated.
- * All code is written in Python. Various parts are not currently used.
- * Current repositories are [0] and [1].
-
-3. Proposal
-
- The accepted proposal [2] for Google Summer of Code (GSoC) 2014 proposes
- rewriting the current GetTor using a modular design, with a core module
- that handles the main GetTor functionalities, and several other modules,
- one for each service (e.g. SMTP), which can interact with the core and
- send replies to the users. Three modules will be developed for the
- purposes of GSoC: SMTP, Twitter, Skype|XMPP.
-
-3.1. Goals
-
- The main goals of this proposal are the following:
-
- * Provide old GetTor functionalites, such as replies in several
- languages and recognize user instructions.
- * Send fewer information in each reply.
- * Support more providers for uploading the TBB packages.
- * Automate links generation.
- * Clearer, modular and well-documented code.
- * Possibilty to create new modules for other common services.
-
-3.2. Design
-
- Preliminar designs for the core module and the services can be found
- in the design/ folder. All services consider creating a python script
- to add the logic for using them. For example, there should be a script
- that receives the emails and uses the SMTP module. For simplicity,
- I've tried to specify mostly the main functions of every module; there
- are some functions, like opening and writing files, that were not
- considered in this preliminar phase.
-
-4. Discussion
-
-4.1. Skype
-
- My co-mentor for GSoC, Nima, has publicly rejected the idea of creating
- a module for Skype and proposed to implement one for XMPP instead.
- I've chosen Skype for its popularity, but I have no other main reason
- to maintain this option, so it's probable that XMPP transport will be
- implemented.
-
-4.2. Storing links
-
- My original proposal considered the fact that links could be stored
- somewhere with restricted access, ideally a git repository. Nima
- mentioned that ideally the links shouldn't be stored. May be this idea
- could be used only to the mirrors and providers configuration (see
- core module design).
-
-4.3. Generating unique URLs
-
- Nima mentioned that unique URLs could be generated for each request,
- and in case the user don't have access to SSL, these links could be
- served and later deleted or recycled. I like this idea.
-
-4. References
-
- [0] https://gitweb.torproject.org/gettor.git
- [1] https://gitweb.torproject.org/user/sukhbir/gettor.git
- [2] https://ileiva.github.io/gettor_proposal.html
-
diff --git a/src/LICENSE b/src/LICENSE
deleted file mode 100644
index c71c94c..0000000
--- a/src/LICENSE
+++ /dev/null
@@ -1,31 +0,0 @@
-gettor is distributed under this license:
-
-Copyright (c) 2008-2014, The Tor Project, Inc.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
-
- * Neither the names of the copyright owners nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/blacklist.cfg b/src/blacklist.cfg
deleted file mode 100644
index bb8a3d8..0000000
--- a/src/blacklist.cfg
+++ /dev/null
@@ -1,6 +0,0 @@
-[general]
-db: /path/to/gettor.db
-
-[log]
-level: DEBUG
-dir: /path/to/log
diff --git a/src/core.cfg b/src/core.cfg
deleted file mode 100644
index 0b3231e..0000000
--- a/src/core.cfg
+++ /dev/null
@@ -1,12 +0,0 @@
-[general]
-basedir: /path/to/gettor
-db: gettor.db
-
-[links]
-dir: providers/
-os: linux,windows,osx
-locales: es,en
-
-[log]
-dir: log/
-level: DEBUG
diff --git a/src/core_demo.py b/src/core_demo.py
deleted file mode 100644
index 6298f70..0000000
--- a/src/core_demo.py
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/python
-#
-# Dummy script to test GetTore's Core module
-#
-
-import gettor.core
-
-try:
- core = gettor.core.Core()
- links = core.get_links('dummy service', 'linux', 'es')
- print links
-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/create_links_demo.py b/src/create_links_demo.py
deleted file mode 100644
index e97138b..0000000
--- a/src/create_links_demo.py
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/python
-#
-# Dummy script to test GetTore's Core module progress
-#
-
-import gettor
-
-try:
- core = gettor.Core('gettor.cfg')
- core.create_links_file('Github')
- core.add_link('Github', 'linux', 'es',
- 'https://foo.bar https://foo.bar.asc 111-222-333-444')
- core.add_link('Github', 'linux', 'es',
- 'https://foo.bar https://foo.bar.asc 555-666-777-888')
-
-except ValueError as e:
- print "Value error: " + str(e)
-except RuntimeError as e:
- print "Internal error: " + str(e)
diff --git a/src/dropbox.py b/src/dropbox.py
deleted file mode 100644
index baaafee..0000000
--- a/src/dropbox.py
+++ /dev/null
@@ -1,163 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of GetTor, a Tor Browser Bundle distribution system.
-#
-# :authors: Israel Leiva <ilv(a)riseup.net>
-# see also AUTHORS file
-#
-# :copyright: (c) 2008-2014, The Tor Project, Inc.
-# (c) 2014, Israel Leiva
-#
-# :license: This is Free Software. See LICENSE for license information.
-
-import re
-import os
-import gnupg
-import hashlib
-import dropbox
-import gettor.core
-
-
-def valid_bundle_format(file):
- """
- Checks for a valid bundle format
- (e.g. tor-browser-linux32-3.6.2_es-ES.tar.xz
-
- Returns True or False if it's valid or not.
- """
-
- m = re.search(
- 'tor-browser-(\w+)\d\d-\d\.\d\.\d_(\w\w)-\w+\.tar\.xz',
- file)
- if m:
- return True
- else:
- return False
-
-
-def get_bundle_info(file):
- """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.
-
- """
- m = re.search(
- 'tor-browser-(\w+)\d\d-\d\.\d\.\d_(\w\w)-\w+\.tar\.xz',
- file)
- if m:
- operating_system = m.group(1)
- locale = m.group(2)
- return operating_system, locale
- else:
- raise ValueError("Bundle invalid format %s" % file)
-
-def get_file_sha256(file):
- """Get the sha256 of a file."""
-
- # as seen on the internet
- BLOCKSIZE = 65536
- hasher = hashlib.sha256()
- 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.
-
- 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.
-
- """
- files = []
-
- p = re.compile('.*\.tar.xz$')
-
- for name in os.listdir(basedir):
- path = os.path.abspath(os.path.join(basedir, name))
- if os.path.isfile(path) and p.match(path) and valid_bundle_format(name):
- files.append(name)
-
- for file in files:
- asc = file + '.asc'
- abs_file = os.path.abspath(os.path.join(basedir, file))
- abs_asc = os.path.abspath(os.path.join(basedir, asc))
-
- if not os.path.isfile(abs_asc):
- raise ValueError("%s doesn't exist!" % asc)
-
- # Chunk upload for big files
- to_upload = open(abs_file, 'rb')
- size = os.path.getsize(abs_file)
- uploader = client.get_chunked_uploader(to_upload, size)
- while uploader.offset < size:
- try:
- upload = uploader.upload_chunked()
- except rest.ErrorResponse, e:
- UploadError("An error ocurred while uploading %s" % abs_file)
- uploader.finish(file)
-
- # This should be small, upload it simple
- to_upload_asc = open(abs_asc, 'rb')
- response = client.put_file(asc, to_upload_asc)
-
- return files
-
-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()
-
- # 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))
-
- sha_file = get_file_sha256(abs_file)
-
- # build links
- link_file = client.share(file)
- link_asc = client.share(asc)
- link = link_file[u'url'] + ' ' + link_asc[u'url'] + ' ' + sha_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/__init__.py b/src/gettor/__init__.py
deleted file mode 100644
index c87425a..0000000
--- a/src/gettor/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# yes it's empty, of such a fullness
diff --git a/src/gettor/blacklist.py b/src/gettor/blacklist.py
deleted file mode 100644
index 1c721bb..0000000
--- a/src/gettor/blacklist.py
+++ /dev/null
@@ -1,143 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of GetTor, a Tor Browser Bundle distribution system.
-#
-# :authors: Israel Leiva <ilv(a)riseup.net>
-# see also AUTHORS file
-#
-# :copyright: (c) 2008-2014, The Tor Project, Inc.
-# (c) 2014, Israel Leiva
-#
-# :license: This is Free Software. See LICENSE for license information.
-
-import os
-import time
-import logging
-import sqlite3
-import datetime
-import ConfigParser
-
-import db
-import utils
-
-"""Blacklist module for managing blacklisting of users."""
-
-
-class BlacklistError(Exception):
- pass
-
-
-class Blacklist(object):
- """Manage blacklisting of users.
-
- Public methods:
-
- is_blacklisted(): Check if someone is blacklisted.
-
- Exceptions:
-
- ConfigurationError: Bad configuration.
- BlacklistError: User is blacklisted.
-
- """
-
- def __init__(self, cfg=None):
- """Create new object by reading a configuration file.
-
- :param: cfg (string) path of the configuration file.
-
- """
- # define a set of default values
- DEFAULT_CONFIG_FILE = 'blacklist.cfg'
-
- logging.basicConfig(format='[%(levelname)s] %(asctime)s - %(message)s',
- datefmt="%Y-%m-%d %H:%M:%S")
- log = logging.getLogger(__name__)
- config = ConfigParser.ConfigParser()
-
- if cfg is None or not os.path.isfile(cfg):
- cfg = DEFAULT_CONFIG_FILE
- log.info("Using default configuration")
-
- log.info("Reading configuration file %s" % cfg)
- config.read(cfg)
-
- try:
- dbname = config.get('general', 'db')
- self.db = db.DB(dbname)
- except ConfigParser.Error as e:
- log.warning("Couldn't read 'db' from 'general' (%s)" % cfg)
- raise ConfigurationError("Error with conf. See log file.")
-
- try:
- self.logdir = config.get('log', 'dir')
- except ConfigParser.Error as e:
- log.warning("Couldn't read '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:
- log.warning("Couldn't read 'level' from 'log' (%s)" % cfg)
- raise ConfigurationError("Error with conf. See log file.")
-
- # keep log levels separated
- self.log = utils.filter_logging(log, self.logdir, self.loglevel)
- self.log.setLevel(logging.getLevelName(self.loglevel))
- log.debug('Redirecting logging to %s' % self.logdir)
-
- # stop logging on stdout from now on
- log.propagate = False
- self.log.debug("New blacklist object created")
-
- def is_blacklisted(self, user, service, max_req, wait_time):
- """Check if a user is blacklisted.
-
- The user is blacklisted if:
-
- a) The 'blocked' field is set to one, meaning that is permanently
- blacklisted.
-
- b) Does too many requests on a short period of time. For now, a user
- that makes more than 'max_req' requests should wait 'wait_time'
- minutes to make a new request.
-
- :param: user (string) the hashed user.
- :param: service (string) the service the user is making a request to.
- :param: max_req (int) maximum number of requests a user can make
- in a row.
- :param: wait_time (int) amount of time the user must wait before
- making requests again after 'max_req' requests is reached.
- For now this is considered in minutes.
-
- :raise: BlacklistError if the user is blacklisted
-
- """
- r = self.db.get_user(user, service)
- if r:
- # permanently blacklisted
- if r['blocked']:
- self.log.info("Request from blocked user %s" % user)
- self.db.update_user(user, service, r['times']+1, 1)
- raise BlacklistError("Blocked user")
- # don't be greedy
- elif r['times'] >= max_req:
- last = datetime.datetime.fromtimestamp(float(
- r['last_request']))
- next = last + datetime.timedelta(minutes=wait_time)
-
- if datetime.datetime.now() < next:
- self.log.info("Too many requests from user %s" % user)
- self.db.update_user(user, service, r['times']+1, 0)
- raise BlacklistError("Too many requests")
- else:
- # fresh user again!
- self.log.debug("Request after wait time, cleaning up for"
- " %s" % user)
- self.db.update_user(user, service, 1, 0)
- else:
- self.log.debug("Adding up a request for %s" % user)
- self.db.update_user(user, service, r['times']+1, 0)
- else:
- self.log.debug("New request for %s" % user)
- self.db.add_user(user, service, 0)
diff --git a/src/gettor/core.py b/src/gettor/core.py
deleted file mode 100644
index 8b1f51a..0000000
--- a/src/gettor/core.py
+++ /dev/null
@@ -1,419 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of GetTor, a Tor Browser Bundle distribution system.
-#
-# :authors: Israel Leiva <ilv(a)riseup.net>
-# see also AUTHORS file
-#
-# :copyright: (c) 2008-2014, The Tor Project, Inc.
-# (c) 2014, Israel Leiva
-#
-# :license: This is Free Software. See LICENSE for license information.
-
-import os
-import re
-import inspect
-import logging
-import tempfile
-import ConfigParser
-
-import db
-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_lc(): 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.
-
- :param: cfg (string) the path of the configuration file.
- :raise: ConfigurationError if the configuration file doesn't exists
- or if something goes wrong while reading options from it.
-
- """
- # 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:
- dbname = config.get('general', 'db')
- dbname = os.path.join(self.basedir, dbname)
- self.db = db.DB(dbname)
- except ConfigParser.Error as e:
- logger.warning("Couldn't read 'db' 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_lc = 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.log = utils.filter_logging(logger, self.logdir, self.loglevel)
- # self.log.setLevel(logging.getLevelName(self.loglevel))
- self.log.info('Redirecting logging to %s' % self.logdir)
-
- # stop logging on stdout from now on
- self.log.propagate = False
- self.log.debug("New core object created")
-
- def get_links(self, service, os, lc):
- """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).
-
- :param: service (string) the service trying to get the links.
- :param: os (string) the operating system.
- :param: lc (string) tthe locale.
-
- :raise: UnsupportedOSError if the operating system is not supported.
- :raise: UnsupportedLocaleError if the locale is not supported.
- :raise: InternalError if something goes wrong while internally.
-
- :return: (string) the links.
-
- """
-
- # Which module called us and what was asking for?
- self.log.info("%s did a request for %s, %s." % (service, os, lc))
-
- if lc not in self.supported_lc:
- self.log.warning("Request for unsupported locale: %s" % lc)
- raise UnsupportedLocaleError("Locale %s not supported" % lc)
-
- if os not in self.supported_os:
- self.log.warning("Request for unsupported OS: %s" % os)
- raise UnsupportedOSError("OS %s not supported " % os)
-
- # this could change in the future, let's leave it isolated.
- links = self._get_links(os, lc)
-
- if links is None:
- self.log.error("Couldn't get the links", exc_info=True)
- raise InternalError("Something went wrong internally. See logs for"
- " detailed info.")
-
- self.log.info("Returning the links")
- return links
-
- def _get_links(self, os, lc):
- """Internal method to get the links.
-
- Looks for the links inside each provider file. This should only be
- called from get_links() method.
-
- :param: os (string) the operating system.
- :param: lc (string) the locale.
-
- :return: (string/None) links on success, None otherwise.
-
- """
-
- # 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 os and lc. This way makes it easy to check if no
- # links were found
- providers = {}
-
- self.log.info("Reading links from providers directory")
- for name in links:
- self.log.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.log.warning("Couldn't get 'name' from 'provider' (%s)"
- % name)
- raise InternalError("Error while reading %s links file. See "
- "log file" % name)
-
- self.log.debug("Checking if %s has links for %s in %s" %
- (pname, os, lc))
-
- try:
- providers[pname] = config.get(os, lc)
- except ConfigParser.Error as e:
- self.log.warning("Couldn't get %s from %s (%s)" %
- (lc, os, 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.log.debug("Trying to get fingerprint from %s", pname)
- fingerprint = config.get('key', 'fingerprint')
- providers[pname] = providers[pname] + "\nFingerprint: "
- providers[pname] = providers[pname] + fingerprint
- self.log.debug("Fingerprint added %s", fingerprint)
- except ConfigParser.Error as e:
- self.log.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.log.debug("Joining all links found for %s in %s" % (os, lc))
- 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.log.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.
-
- :return: (list) the supported operating systems.
-
- """
- return self.supported_os.split(',')
-
- def get_supported_lc(self):
- """Public method to get the list of supported locales.
-
- :return: (list) the supported locales.
-
- """
- return self.supported_lc.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.
-
- :param: provider (string) the provider (links file will use this
- name in slower case).
- :param: fingerprint (string) the 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.log.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.log.info("Backing up %s (%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)
- for os in self.supported_os:
- content.add_section(os)
- with open(linksfile, 'w+') as f:
- content.write(f)
- self.log.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, os, lc, link):
- """Public method to add a link to a provider's links file.
-
- Use ConfigParser to add a link into the os section, under the lc
- option. It checks for valid format; the provider's script should
- use the right format (see design).
-
- :param: provider (string) the provider.
- :param: os (string) the operating system.
- :param: lc (string) the locale.
- :param: link (string) link 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.
-
- :raise: UnsupportedOSError if the operating system is not supported.
- :raise: UnsupportedLocaleError if the locale is not supported.
- :raise: LinkFileError if there is no links file for the provider.
- :raise: LinkFormatError if the link format doesn't seem legit.
- :raise: 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.
-
- """
- linksfile = os.path.join(self.linksdir, provider.lower() + '.links')
-
- # don't try to add unsupported stuff
- if lc not in self.supported_lc:
- self.log.warning("Can't add link for unsupported lc: %s" % lc)
- raise UnsupportedLocaleError("Locale %s not supported" % lc)
-
- if os not in self.supported_os:
- self.log.warning("Can't add link for unsupported os: %s" % os)
- raise UnsupportedOSError("OS %s not supported" % os)
-
- # 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.log.warning("Can't add an invalid link: %s" % link)
- raise LinkFormatError("Link '%s' doesn't seem legit" % 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(os, lc)
- links = links + ",\n" + link
- content.set(oc, lc, links)
- with open(linksfile, 'w') as f:
- content.write(f)
- self.log.info("Link %s added to %s %s in %s" %
- (link, os, lc, provider))
- except ConfigParser.NoOptionError:
- content.set(os, lc, link)
- with open(linksfile, 'w') as f:
- content.write(f)
- self.log.info("Link %s added to %s-%s in %s" %
- (link, os, lc, provider))
- except ConfigParser.NoSectionError:
- # this shouldn't happen, but just in case
- self.log.error("Unknown section %s in links file")
- raise InternalError("Unknown %s section in links file" % os)
- else:
- raise LinkFileError("There is no links file for %s" % provider)
-
- def add_request_to_db(self, service, type, os, lc, pt, status, logfile):
- """Add request to database.
-
- This is for keeping stats about what is the most, less requested
- and stuff like that. Hopefully it will help to improve user experience.
-
- :param: type (string) the type of the request.
- :param: os (string) the operating system.
- :param: lc (string) the locale.
- :param: pt (bool) true if the user asked about pt, false otherwise.
- :param: status (string) short text describing the status.
- :param: logfile (string) path of the logfile of the email in case
- something went really wrong (address blacklisted/malformed).
-
- """
- self.db.add_request(service, type, os, lc, pt, status, logfile)
diff --git a/src/gettor/db.py b/src/gettor/db.py
deleted file mode 100644
index 1f6cfbd..0000000
--- a/src/gettor/db.py
+++ /dev/null
@@ -1,113 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of GetTor, a Tor Browser Bundle distribution system.
-#
-# :authors: Israel Leiva <ilv(a)riseup.net>
-# see also AUTHORS file
-#
-# :copyright: (c) 2008-2014, The Tor Project, Inc.
-# (c) 2014, Israel Leiva
-#
-# :license: This is Free Software. See LICENSE for license information.
-
-import time
-import sqlite3
-import datetime
-
-"""DB interface for comunicating with sqlite3"""
-
-
-class DB(object):
- """
-
- Public methods:
-
- add_request(): add a request to the database (requests table).
- get_user(): get user info from the database (users table).
- add_user(): add a user to the database (users table).
- update_user(): update a user on the database (users table).
-
- """
-
- def __init__(self, dbname):
- """Create a new db object.
-
- :param: dbname (string) the path of the database.
-
- """
- self.con = sqlite3.connect(dbname)
- self.con.row_factory = sqlite3.Row
-
- def add_request(self, service, type, os, lc, pt, status, logfile):
- """Add a request to the database.
-
- This is for keeping stats about what is the most, less requested
- and stuff like that. Hopefully it will help to improve user experience.
-
- :param: type (string) the type of the request.
- :param: os (string) the operating system.
- :param: lc (string) the locale.
- :param: pt (bool) true if the user asked about pt, false otherwise.
- :param: status (string) short text describing the status.
- :param: logfile (string) path of the logfile of the email in case
- something went really wrong (address blacklisted/malformed).
-
- """
- now = datetime.datetime.now()
-
- with self.con:
- cur = self.con.cursor()
- cur.execute("INSERT INTO requests VALUES(?,?,?,?,?,?,?,?,?,?)",
- (service, type, os, lc, pt, now.year, now.month,
- now.day, status, logfile))
-
- def get_user(self, user, service):
- """Get user info from the database.
-
- :param: user (string) unique (hashed) string that represents the user.
- :param: service (string) the service related to the user (e.g. SMTP).
-
- :return: (dict) the user information, with fields as indexes
- (e.g. row['user']).
-
- """
- with self.con:
- cur = self.con.cursor()
- cur.execute("SELECT * FROM users WHERE id =? AND service =?",
- (user, service))
-
- row = cur.fetchone()
- return row
-
- def add_user(self, user, service, blocked):
- """Add a user to the database.
-
- We add a user with one 'times' and the current time as 'last_request'
- by default.
-
- :param: user (string) unique (hashed) string that represents the user.
- :param: service (string) the service related to the user (e.g. SMTP).
- :param: blocked (int) one if user is blocked, zero otherwise.
-
- """
- with self.con:
- cur = self.con.cursor()
- cur.execute("INSERT INTO users VALUES(?,?,?,?,?)",
- (user, service, 1, blocked, str(time.time())))
-
- def update_user(self, user, service, times, blocked):
- """Update a user on the database.
-
- We update the user info with the current time as 'last_request'.
-
- :param: user (string) unique (hashed) string that represents the user.
- :param: service (string) the service related to the user (e.g. SMTP).
- :param: times (int) the number of requests the user has made.
- :param: blocked (int) one if user is blocked, zero otherwise.
-
- """
- with self.con:
- cur = self.con.cursor()
- cur.execute("UPDATE users SET times =?, blocked =?,"
- " last_request =? WHERE id =? AND service =?",
- (times, blocked, str(time.time()), user, service))
diff --git a/src/gettor/smtp.py b/src/gettor/smtp.py
deleted file mode 100644
index b4fa3cd..0000000
--- a/src/gettor/smtp.py
+++ /dev/null
@@ -1,603 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of GetTor, a Tor Browser Bundle distribution system.
-#
-# :authors: Israel Leiva <ilv(a)riseup.net>
-# see also AUTHORS file
-#
-# :copyright: (c) 2008-2014, The Tor Project, Inc.
-# (c) 2014, Israel Leiva
-#
-# :license: This is Free Software. See LICENSE for license information.
-
-
-import os
-import re
-import sys
-import time
-import email
-import gettext
-import logging
-import smtplib
-import datetime
-import ConfigParser
-
-from email.mime.text import MIMEText
-
-import core
-import utils
-import blacklist
-
-"""SMTP module for processing email requests."""
-
-
-class ConfigurationError(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.
- 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.
-
- :param: cfg (string) 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")
- log = logging.getLogger(__name__)
- config = ConfigParser.ConfigParser()
-
- if cfg is None or not os.path.isfile(cfg):
- cfg = DEFAULT_CONFIG_FILE
- log.info("Using default configuration")
-
- log.info("Reading configuration file %s" % cfg)
- config.read(cfg)
-
- try:
- self.basedir = config.get('general', 'basedir')
- except ConfigParser.Error as e:
- log.warning("Couldn't read 'basedir' from 'general' (%s)" % cfg)
- raise ConfigurationError("Error with conf. See log file.")
-
- try:
- self.our_domain = config.get('general', 'our_domain')
- except ConfigParser.Error as e:
- log.warning("Couldn't read 'our_domain' from 'general' (%s)" % cfg)
- raise ConfigurationError("Error with conf. See log file.")
-
- try:
- core_cfg = config.get('general', 'core_cfg')
- except ConfigParser.Error as e:
- log.warning("Couldn't read 'core_cfg' from 'general' (%s)" % cfg)
- raise ConfigurationError("Error with conf. See log file.")
-
- try:
- blacklist_cfg = config.get('blacklist', 'cfg')
- self.bl = blacklist.Blacklist(blacklist_cfg)
- except ConfigParser.Error as e:
- log.warning("Couldn't read 'cfg' from 'blacklist' (%s)" % cfg)
- raise ConfigurationError("Error with conf. See log file.")
-
- try:
- self.bl_max_req = config.get('blacklist', 'max_requests')
- self.bl_max_req = int(self.bl_max_req)
- except ConfigParser.Error as e:
- log.warning("Couldn't read 'max_requests' from 'blacklist' (%s)"
- % cfg)
- raise ConfigurationError("Error with conf. See log file.")
-
- try:
- self.bl_wait_time = config.get('blacklist', 'wait_time')
- self.bl_wait_time = int(self.bl_wait_time)
- except ConfigParser.Error as e:
- log.warning("Couldn't read 'wait_time' from 'blacklist' (%s)"
- % cfg)
- raise ConfigurationError("Error with conf. See log file.")
-
- try:
- self.i18ndir = config.get('i18n', 'dir')
- self.i18ndir = os.path.join(self.basedir, self.i18ndir)
- except ConfigParser.Error as e:
- log.warning("Couldn't read 'dir' from 'i18n' (%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:
- log.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:
- log.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:
- log.warning("Couldn't read 'level' from 'log' (%s)" % cfg)
- raise ConfigurationError("Error with conf. See log file.")
-
- # use default values
- self.core = core.Core(core_cfg)
-
- # keep log levels separated
- self.log = utils.filter_logging(log, self.logdir, self.loglevel)
- self.log.setLevel(logging.getLevelName(self.loglevel))
- log.debug('Redirecting logging to %s' % self.logdir)
-
- # stop logging on stdout from now on
- log.propagate = False
- self.log.debug("New smtp object created")
-
- def _log_email(self, addr, content):
- """Log a request.
-
- This should be called when something goes wrong. It saves the
- email content that triggered the malfunctioning.
-
- :param: addr (string) the address of the sender.
- :param: content (string) the content of the email received.
-
- :return: (string) the path of the logfile of the email.
- """
- # we store the sha256 of the original address in order to know when
- # specific addresses are doing weird requests
- filename = "%s.log" % str(time.time())
- path = os.path.join(self.logdir_emails, filename)
- abs_path = os.path.abspath(path)
-
- log_file = open(abs_path, 'w+')
- log_file.write("Address: %s " % addr)
- log_file.write("\nBody:\n%s" % content)
- log_file.close()
- self.log.debug("Logging request from %s in %s" % (addr, abs_path))
-
- return filename
-
- def _is_blacklisted(self, addr):
- """Check if a user is blacklisted.
-
- :param: addr (string) the hashed address of the user.
-
- :return: true is the address is blacklisted, false otherwise.
-
- """
- self.log.debug("Checking if address %s is blacklisted" % addr)
-
- try:
- self.bl.is_blacklisted(addr, 'SMTP', self.bl_max_req,
- self.bl_wait_time)
- return False
- except blacklist.BlacklistError as e:
- self.log.info("Blacklisted address %s. Reason: %s" % (addr, e))
- return True
-
- def _get_lc(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(a)torproject.org) If no locale found, english
- by default.
-
- :param: (string) the email address we want to get the locale from.
-
- :return: (string) the locale (english if none).
-
- """
- self.log.debug("Trying to obtain locale from recipient address")
-
- # if no match found, english by default
- lc = 'en'
-
- # look for gettor+locale(a)torproject.org
- m = re.match('gettor\+(\w\w)(a)\w+\.\w+', addr)
- if m:
- self.log.debug("Request for locale %s" % m.groups())
- lc = "%s" % m.groups()
-
- return lc.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).
-
- :param: addr (string) the address we want to normalize.
-
- :raise: AddressError if the address can't be normalized.
-
- :return: (string) the normalized address.
-
- """
- if '<' in addr:
- idx = addr.rindex('<')
- addr = addr[idx:]
- m = re.search(r'<([^>]*)>', addr)
- if m is None:
- self.log.info("Malformed address: %s" % addr)
- raise AddressError("Couldn't extract normalized address "
- "from %s" % self_get_sha256(addr))
- addr = m.group(1)
- return addr
-
- def _get_content(self, email):
- """Get the body content of an email.
-
- :param: email (object) the email object to extract the content from.
-
- :return: (string) body of the message.
-
- """
- self.log.debug("Getting the body content of the email")
- maintype = email.get_content_maintype()
- if maintype == 'multipart':
- for part in email.get_payload():
- if part.get_content_maintype() == 'text':
- return part.get_payload()
- elif maintype == 'text':
- return email.get_payload()
-
- def _get_msg(self, msgid, lc):
- """Get message identified by msgid in a specific locale.
-
- :param: msgid (string) the identifier of a string.
- :param: lc (string) the locale.
-
- :return: (string) the message from the .po file.
-
- """
- self.log.debug("Getting message '%s' for locale %s" % (msgid, lc))
- # obtain the content in the proper language
- t = gettext.translation(lc, self.i18ndir, languages=[lc])
- _ = t.ugettext
-
- msgstr = _(msgid)
- return msgstr
-
- def _parse_email(self, msg, addr):
- """Parse the email received.
-
- Get the locale and parse the text for the rest of the info.
-
- :param: msg (string) the content of the email to be parsed.
- :param: addr (string) the address of the recipient (i.e. us).
-
- :return: (list) 4-tuple with locale, os and type of request.
-
- """
- self.log.debug("Parsing email")
-
- req = self._parse_text(msg)
- lc = self._get_lc(addr)
- req['lc'] = lc
-
- return req
-
- def _parse_text(self, 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).
-
- :param: msg (string) the content of the email to be parsed.
-
- :return: (list) 3-tuple with the type of request, os and pt info.
-
- """
- self.log.debug("Parsing email text part")
-
- # by default we asume the request is asking for help
- req = {}
- req['type'] = 'help'
- req['os'] = None
- req['pt'] = False
-
- # core knows what OS are supported
- supported_os = self.core.get_supported_os()
-
- # we must search word by word, otherwise we could misinterpret
- # stuff (i.e. 'option' will match .*pt.*)
- found_os = False
- found_help = False
- lines = msg.split(' ')
- for word in lines:
- # check for help request
- if not found_os and re.match('help', word, re.IGNORECASE):
- self.log.info("Request for help found")
- req['type'] = 'help'
- found_help = True
- break
- # check for os
- if not found_os and not found_help:
- for os in supported_os:
- if re.match(os, word, re.IGNORECASE):
- req['os'] = os
- req['type'] = 'links'
- self.log.debug("Request for links found")
- found_os = True
- break
- # check if the user is asking for terms related to pt
- if re.match(".*obfs.*|pluggable|transport|pt",
- word, re.IGNORECASE):
- self.log.info("Request for PT found")
- req['pt'] = True
-
- return req
-
- def _create_email(self, from_addr, to_addr, subject, msg):
- """Create an email object.
-
- This object will be used to construct the reply.
-
- :param: from_addr (string) the address of the sender.
- :param: to_addr (string) the address of the recipient.
- :param: subject (string) the subject of the email.
- :param: msg (string) the content of the email.
-
- :return: (object) the email object.
-
- """
- self.log.debug("Creating email object for replying")
- email_obj = MIMEText(msg)
- email_obj.set_charset("utf-8")
- email_obj['Subject'] = subject
- email_obj['From'] = from_addr
- email_obj['To'] = to_addr
-
- return email_obj
-
- 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.
-
- :param: from_addr (string) the address of the sender.
- :param: to_addr (string) the address of the recipient.
- :param: subject (string) the subject of the email.
- :param: msg (string) the 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, email_obj.as_string())
- s.quit()
- except smtplib.SMTPException as e:
- self.log.error("Couldn't send the email: %s" % str(e))
- raise SendEmailError("Error with SMTP: %s" % str(e))
-
- self.log.debug("Email sent")
-
- def _send_links(self, links, lc, os, 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.
-
- :param: links (string) the links to be sent.
- :param: lc (string) the locale.
- :param: os (string) the operating system.
- :param: from_addr (string) the address of the sender.
- :param: to_addr (string) the address of the recipient.
- :param: pt (bool) true if the used asked for PT info; false otherwise.
-
- """
- self.log.debug("Request for links in %s" % lc)
-
- # obtain the content in the proper language and send it
- links_subject = self._get_msg('links_subject', lc)
- links_msg = self._get_msg('links_msg', lc)
- links_msg = links_msg % (os, lc, 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 = self._get_msg('links_pt_subject', lc)
- links_msg = self._get_msg('links_pt_msg', lc)
- links_msg = links_msg % (os, lc, links)
-
- try:
- self._send_email(from_addr, to_addr, links_subject, links_msg)
- except SendEmailError as e:
- self.log.warning("Couldn't send links message")
- raise InternalError("Error while sending links message")
-
- def _send_help(self, lc, 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.
-
- :param: lc (string) the locale.
- :param: from_addr (string) the address of the sender.
- :param: to_addr (string) the address of the recipient.
-
- """
- self.log.debug("Request for help in %s" % lc)
-
- # obtain the content in the proper language and send it
- help_subject = self._get_msg('help_subject', lc)
- help_msg = self._get_msg('help_msg', lc)
-
- try:
- self._send_email(from_addr, to_addr, help_subject, help_msg)
- except SendEmailError as e:
- self.log.warning("Couldn't send help message")
- raise InternalError("Error while sending help message")
-
- def _send_unsupported_lc(self, lc, os, from_addr, to_addr):
- """Send unsupported locale message.
-
- Get the message for unsupported locale in english replace variables
- (if any) and send the email.
-
- :param: lc (string) the locale.
- :param: os (string) the operating system.
- :param: from_addr (string) the address of the sender.
- :param: to_addr (string) the address of the recipient.
-
- """
-
- # obtain the content in english and send it
- unsupported_lc_subject = self._get_msg('unsupported_lc_subject', 'en')
- unsupported_lc_msg = self._get_msg('unsupported_lc_msg', 'en')
- unsupported_lc_msg = unsupported_lc_msg % lc
-
- try:
- self._send_email(from_addr, to_addr, unsupported_lc_subject,
- unsupported_lc_msg)
-
- except SendEmailError as e:
- self.log.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.
-
- :param: raw_msg (string) the email received.
-
- :raise: InternalError if something goes wrong while asking for the
- links to the Core module.
-
- """
- parsed_msg = email.message_from_string(raw_msg)
- content = self._get_content(parsed_msg)
- from_addr = parsed_msg['From']
- to_addr = parsed_msg['To']
- bogus_request = False
- logfile = ''
- status = ''
- req = None
-
- try:
- # two ways for a request to be bogus: address malformed or
- # blacklisted
- try:
- norm_from_addr = self._get_normalized_address(from_addr)
- except AddressError as e:
- status = 'malformed'
- bogus_request = True
- # it might be interesting to know what triggered this
- logfile = self._log_email('malformed', content)
-
- if norm_from_addr:
- anon_addr = utils.get_sha256(norm_from_addr)
-
- if self._is_blacklisted(anon_addr):
- status = 'blacklisted'
- bogus_request = True
- # it might be interesting to know extra info
- logfile = self._log_email(anon_addr, content)
-
- if not bogus_request:
- # try to figure out what the user is asking
- req = self._parse_email(content, to_addr)
-
- # our address should have the locale requested
- our_addr = "gettor+%s@%s" % (req['lc'], self.our_domain)
-
- # two possible options: asking for help or for the links
- self.log.info("New request for %s" % req['type'])
- if req['type'] == 'help':
- # make sure we can send emails
- try:
- self._send_help(req['lc'], our_addr, norm_from_addr)
- except SendEmailError as e:
- status = 'internal_error'
- raise InternalError("Something's wrong with the SMTP "
- "server: %s" % str(e))
-
- elif req['type'] == 'links':
- try:
- self.log.info("Asking core for links in %s for %s" %
- (req['lc'], req['os']))
-
- links = self.core.get_links('SMTP', req['os'],
- req['lc'])
-
- except core.UnsupportedLocaleError as e:
- self.log.info("Request for unsupported locale: %s (%s)"
- % (req['lc'], 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
- status = 'unsupported_lc'
- self._send_unsupported_lc(req['lc'], req['os'],
- our_addr, norm_from_addr)
- return
-
- # if core fails, we fail too
- except (core.InternalError, core.ConfigurationError) as e:
- status = 'core_error'
- self.log.error("Something went wrong with Core: %s"
- % str(e))
- raise InternalError("Error obtaining the links.")
-
- # make sure we can send emails
- try:
- self._send_links(links, req['lc'], req['os'], our_addr,
- norm_from_addr, req['pt'])
- except SendEmailError as e:
- status = 'internal_error'
- raise SendEmailError("Something's wrong with the SMTP "
- "server: %s" % str(e))
- status = 'success'
- finally:
- # keep stats
- if req:
- self.core.add_request_to_db('SMTP',
- req['type'], req['os'],
- req['lc'], req['pt'],
- status, logfile)
- else:
- # invalid request, so no info about it
- # logfiles were created for this
- self.core.add_request_to_db('SMTP', '', '', '', '',
- status, logfile)
diff --git a/src/gettor/utils.py b/src/gettor/utils.py
deleted file mode 100644
index 53891ea..0000000
--- a/src/gettor/utils.py
+++ /dev/null
@@ -1,104 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of GetTor, a Tor Browser Bundle distribution system.
-#
-# :authors: Israel Leiva <ilv(a)riseup.net>
-# see also AUTHORS file
-#
-# :copyright: (c) 2008-2014, The Tor Project, Inc.
-# (c) 2014, Israel Leiva
-#
-# :license: This is Free Software. See LICENSE for license information.
-
-import os
-import logging
-import hashlib
-
-"""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.
-
- :param: passlevel (string) the 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.
-
- :param: logger (object) a logging object.
- :param: dir (string) directory to put the log files.
- :param: level (string) the level of logging for the all.log file.
-
- :return: (object) a logging 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
-
-def get_sha256(string):
- """Get sha256 of a string.
-
- :param: (string) the string to be hashed.
-
- :return: (string) the sha256 of string.
-
- """
- return str(hashlib.sha256(string).hexdigest())
diff --git a/src/gettor/xmpp.py b/src/gettor/xmpp.py
deleted file mode 100644
index f296559..0000000
--- a/src/gettor/xmpp.py
+++ /dev/null
@@ -1,378 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of GetTor, a Tor Browser Bundle distribution system.
-#
-# :authors: Israel Leiva <ilv(a)riseup.net>
-# see also AUTHORS file
-#
-# :copyright: (c) 2008-2014, The Tor Project, Inc.
-# (c) 2014, Israel Leiva
-#
-# :license: This is Free Software. See LICENSE for license information.
-
-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 core
-import utils
-import blacklist
-
-
-"""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 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.
- InternalError: Something went wrong internally.
-
- """
-
- def __init__(self, cfg=None):
- """Create new object by reading a configuration file.
-
- :param: cfg (string) the 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")
- log = logging.getLogger(__name__)
- config = ConfigParser.ConfigParser()
-
- if cfg is None or not os.path.isfile(cfg):
- cfg = DEFAULT_CONFIG_FILE
- log.info("Using default configuration")
-
- log.info("Reading configuration file %s" % cfg)
- config.read(cfg)
-
- try:
- self.user = config.get('account', 'user')
- except ConfigParser.Error as e:
- log.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:
- log.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:
- log.warning("Couldn't read 'basedir' from 'general' (%s)" % cfg)
- raise ConfigurationError("Error with conf. See log file.")
-
- try:
- self.core_cfg = config.get('general', 'core_cfg')
- except ConfigParser.Error as e:
- log.warning("Couldn't read 'core_cfg' from 'general' (%s)" % cfg)
- raise ConfigurationError("Error with conf. See log file.")
-
- try:
- blacklist_cfg = config.get('blacklist', 'cfg')
- self.bl = blacklist_cfg
- except ConfigParser.Error as e:
- log.warning("Couldn't read 'cfg' from 'blacklist' (%s)" % cfg)
- raise ConfigurationError("Error with conf. See log file.")
-
- try:
- self.bl_max_req = config.get('blacklist', 'max_requests')
- self.bl_max_req = int(self.bl_max_req)
- except ConfigParser.Error as e:
- log.warning("Couldn't read 'max_requests' from 'blacklist' (%s)"
- % cfg)
- raise ConfigurationError("Error with conf. See log file.")
-
- try:
- self.bl_wait_time = config.get('blacklist', 'wait_time')
- self.bl_wait_time = int(self.bl_wait_time)
- except ConfigParser.Error as e:
- log.warning("Couldn't read 'wait_time' from 'blacklist' (%s)"
- % cfg)
- raise ConfigurationError("Error with conf. See log file.")
-
- try:
- self.i18ndir = config.get('i18n', 'dir')
- self.i18ndir = os.path.join(self.basedir, self.i18ndir)
- except ConfigParser.Error as e:
- log.warning("Couldn't read 'dir' from 'i18n' (%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:
- log.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:
- log.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:
- log.warning("Couldn't read 'level' from 'log' (%s)" % cfg)
- raise ConfigurationError("Error with conf. See log file.")
-
- # keep log levels separated
- self.log = utils.filter_logging(log, self.logdir, self.loglevel)
- self.log.setLevel(logging.getLevelName(self.loglevel))
- log.debug('Redirecting logging to %s' % self.logdir)
-
- # stop logging on stdout from now on
- log.propagate = False
- self.log.debug("New xmpp object created")
-
- def start_bot(self):
- """Start the bot for handling requests.
-
- Start a new sleekxmpp bot.
-
- """
-
- self.log.debug("Calling sleekmppp bot")
- xmpp = Bot(self.user, self.password, self)
- xmpp.connect()
- xmpp.process(block=True)
-
- def _is_blacklisted(self, account):
- """Check if a user is blacklisted.
-
- :param: addr (string) the hashed address of the user.
-
- :return: true is the address is blacklisted, false otherwise.
-
- """
- anon_acc = utils.get_sha256(account)
- bl = blacklist.Blacklist(self.bl)
- self.log.debug("Checking if address %s is blacklisted" % anon_acc)
-
- try:
- bl.is_blacklisted(anon_acc, 'XMPP', self.bl_max_req,
- self.bl_wait_time)
- return False
- except blacklist.BlacklistError as e:
- self.log.info("Blacklisted address %s. Reason: %s" % (anon_acc, e))
- return True
-
- def _get_msg(self, msgid, lc):
- """Get message identified by msgid in a specific locale.
-
- :param: msgid (string) the identifier of a string.
- :param: lc (string) the locale.
-
- :return: (string) the message from the .po file.
-
- """
- self.log.debug("Getting message '%s' for locale %s" % (msgid, lc))
- # obtain the content in the proper language
- t = gettext.translation(lc, self.i18ndir, languages=[lc])
- _ = t.ugettext
-
- msgstr = _(msgid)
- return msgstr
-
- def _parse_text(self, msg, core_obj):
- """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.
-
- :param: msg (string) the message received.
- :param: core_obj (object) the object of gettor core module.
-
- :return: request (list) 4-tuple with locale, os, type of request
- and pt info.
-
- """
- self.log.debug("Starting text parsing")
- # core knows what OS are supported
- supported_os = core_obj.get_supported_os()
- supported_lc = core_obj.get_supported_lc()
-
- # default values
- req = {}
- req['lc'] = 'en'
- req['os'] = ''
- req['type'] = 'help'
- req['pt'] = False
- found_lc = False
- found_os = False
- found_help = 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(' '):
- # check for help request
- if not found_os and re.match('help', word, re.IGNORECASE):
- self.log.info("Request for help found")
- req['type'] = 'help'
- found_help = True
- # look for locale, os and pt
- if not found_lc:
- for lc in supported_lc:
- if re.match(lc, word, re.IGNORECASE):
- found_lc = True
- req['lc'] = lc
- self.log.debug("Found locale: %s" % lc)
- if not found_os and not found_help:
- for os in supported_os:
- if re.match(os, word, re.IGNORECASE):
- found_os = True
- req['os'] = os
- req['type'] = 'links'
- self.log.debug("Found OS: %s" % os)
- if re.match("obfs|plugabble|transport|pt", word, re.IGNORECASE):
- req['pt'] = True
- self.log.debug("Found PT request")
-
- return req
-
- 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.
-
- :param: account (string) the account that did the request.
- :param: msg (string) the body of the message sent to us.
-
- :return: (string/None) the message to be sent to the user via the
- bot, or None if the user is blacklisted.
-
- """
- bogus_request = False
- reply = ''
- logfile = ''
- status = ''
- req = None
- core_obj = core.Core(self.core_cfg)
-
- try:
- if self._is_blacklisted(str(account)):
- status = 'blacklisted'
- bogus_request = True
-
- if not bogus_request:
- self.log.debug("Request seems legit, let's parse it")
- # let's try to guess what the user is asking
- req = self._parse_text(str(msg), core_obj)
-
- if req['type'] == 'help':
- status = 'success'
- self.log.debug("Got a help request")
- reply = self._get_msg('help', req['lc'])
- elif req['type'] == 'links':
- self.log.debug("Got a links request")
- try:
- links = core_obj.get_links("XMPP", req['os'],
- req['lc'])
- # did the user asked for PT stuff?
- if req['pt']:
- self.log.debug("Also asked for PT info")
- reply = self._get_msg('links_pt', req['lc'])
- reply = reply % (req['os'], req['lc'], links)
- else:
- reply = self._get_msg('links', req['lc'])
- reply = reply % (req['os'], req['lc'], links)
-
- status = 'success'
- except (core.ConfigurationError, core.InternalError) as e:
- # if core failes, send the user an error message, but
- # keep going
- status = 'core_error'
- self.log.debug("Something went wrong with Core")
- reply = self._get_msg('internal_error', req['lc'])
-
- # if the user asked for an unsupported locale, warn him
- # and keep going
- except core.UnsupportedLocaleError as e:
- status = 'unsupported_lc'
- self.log.debug("User asked for unsupported locale")
- reply = self._get_msg('unsupported_lc', req['lc'])
- finally:
- # keep stats
- self.log.debug("Request processed, saving stats.")
-
- if req:
- core_obj.add_request_to_db('XMPP',
- req['type'], req['os'],
- req['lc'], req['pt'],
- status, logfile)
- else:
- # invalid request, so no info about it
- # logfiles were created for this
- core_obj.add_request_to_db('XMPP', '', '', '', '',
- status, logfile)
-
- return reply
diff --git a/src/i18n/en/LC_MESSAGES/en.mo b/src/i18n/en/LC_MESSAGES/en.mo
deleted file mode 100644
index e686fcb..0000000
Binary files a/src/i18n/en/LC_MESSAGES/en.mo and /dev/null differ
diff --git a/src/i18n/en/LC_MESSAGES/en.po b/src/i18n/en/LC_MESSAGES/en.po
deleted file mode 100644
index 6f78da2..0000000
--- a/src/i18n/en/LC_MESSAGES/en.po
+++ /dev/null
@@ -1,95 +0,0 @@
-domain "en"
-
-#: Links subject
-msgid "links_subject"
-msgstr "[GetTor] Links for your request"
-
-#: Links subject (PT)
-msgid "links_pt_subject"
-msgstr "[GetTor] Links for your request"
-
-#: Help subject
-msgid "help_subject"
-msgstr "[GetTor] Help"
-
-#: Delay subject
-msgid "delay_subject"
-msgstr "[GetTor] Delay message"
-
-# Unsupported locale subject
-msgid "unsupported_locale_subject"
-msgstr "[GetTor] Unsupported locale"
-
-# Unsupported locale message
-msgid "unsupported_locale_msg"
-msgstr "The locale you requested '%s' is not supported."
-
-#: Links message
-msgid "links_msg"
-msgstr "Thank you for your request for %s-%s.\n\
-\n\
-Here are the download links:\n\
-\n\
-===\n\
-Tor Browser Bundle:\n\
-===\n\
-%s\n\
-===\n\
-\n\
-===\n\
-Support:\n\
-===\n\
-\n\
-Still need help? If you have any questions, trouble connecting to Tor\n\
-network, or need to talk to a human, please contact our support team at:\n\
-\n\
- help(a)rt.torproject.org\n\
-\n\
-We are ready to answer your queries in English, Farsi, Chinese, Arabic,\n\
-French and Spanish."
-
-#: Links message (PT)
-msgid "links_pt_msg"
-msgstr "Thank you for your request for %s-%s.\n\
-\n\
-Here are the download links:\n\
-\n\
-===\n\
-Tor Browser Bundle:\n\
-===\n\
-%s\n\
-===\n\
-Info about pluggable transports.\n\
-\n\
-===\n\
-Support:\n\
-===\n\
-\n\
-Still need help? If you have any questions, trouble connecting to Tor\n\
-network, or need to talk to a human, please contact our support team at:\n\
-\n\
- help(a)rt.torproject.org\n\
-\n\
-We are ready to answer your queries in English, Farsi, Chinese, Arabic,\n\
-French and Spanish."
-
-#: Help message
-msgid "help_msg"
-msgstr "Hello, this is the 'GetTor' robot.\n\
-\n\
-Thank you for your request. I am here to help you download the latest\n\
-Tor Browser Bundle.\n\
-\n\
-Please reply to this message with one of the options below:\n\
-\n\
- windows\n\
- linux\n\
- osx\n\
-\n\
-And I will send you the download instructions quickly.\n\
-\n\
-Tip: Just send a blank reply to this message if you are not sure."
-
-#: Delay message
-msgid "delay_msg"
-msgstr "Delay message."
diff --git a/src/i18n/es/LC_MESSAGES/es.mo b/src/i18n/es/LC_MESSAGES/es.mo
deleted file mode 100644
index c21fe7b..0000000
Binary files a/src/i18n/es/LC_MESSAGES/es.mo and /dev/null differ
diff --git a/src/i18n/es/LC_MESSAGES/es.po b/src/i18n/es/LC_MESSAGES/es.po
deleted file mode 100644
index bc4b5a8..0000000
--- a/src/i18n/es/LC_MESSAGES/es.po
+++ /dev/null
@@ -1,94 +0,0 @@
-domain "es"
-
-#: Links subject
-msgid "links_subject"
-msgstr "[GetTor] Enlaces para tu petición"
-
-#: Links subject (PT)
-msgid "links_pt_subject"
-msgstr "[GetTor] Enlaces para tu petición"
-
-#: Help subject
-msgid "help_subject"
-msgstr "[GetTor] Ayuda"
-
-#: Delay subject
-msgid "delay_subject"
-msgstr "[GetTor] Mensaje de demora"
-
-# Unsupported locale subject
-msgid "unsupported_locale_subject"
-msgstr "[GetTor] Locale no soportado"
-
-# Unsupported locale message
-msgid "unsupported_locale_msg"
-msgstr "El locale especificado %s no está soportado."
-
-#: Links message
-msgid "links_msg"
-msgstr """Gracias por tu petición para %s-%s.\n\
-\n\
-Aquí están los links de descarga:\n\
-\n\
-===\n\
-%s\n\
-===\n\
-\n\
-===\n\
-Soporte:\n\
-===\n\
-\n\
-¿Aún necesitas ayuda? Si tienes dudas, problemas para conectarte a la\n\
-red Tor, o necesitas hablar con un humano, por favor contacta a nuestro\n\
-equipo de soporte a:\n\
-\n\
- help(a)rt.torproject.org\n\
-\n\
-Estamos listos para responder tus consultas en Inglés, Farsi, Chino,\n\
-Arábico, Francés y Español."""
-
-#: Links message (PT)
-msgid "links_pt_msg"
-msgstr """Gracias por tu petición para %s-%s.\n\
-\n\
-Aquí están los links de descarga:\n\
-\n\
-===\n\
-%s\n\
-===\n\
-Tip: Información sobre pluggable transports.\n\
-\n\
-===\n\
-Soporte:\n\
-===\n\
-\n\
-¿Aún necesitas ayuda? Si tienes dudas, problemas para conectarte a la\n\
-red Tor, o necesitas hablar con un humano, por favor contacta a nuestro\n\
-equipo de soporte a:\n\
-\n\
- help(a)rt.torproject.org\n\
-\n\
-Estamos listos para responder tus consultas en Inglés, Farsi, Chino,\n\
-Arábico, Francés y Español."""
-
-#: Help message
-msgid "help_msg"
-msgstr "Hola, este es el robot 'GetTor'.\n\
-\n\
-Gracias por tu petición. Estoy aquí para ayudarte a descargar la última\n\
-versión del Tor Browser Bundle.\n\
-\n\
-Por favor responde a este mensaje con una de las siguientes opciones:\n\
-\n\
- windows\n\
- linux\n\
- osx\n\
-\n\
-Y te enviaré las instrucciones de descarga.\n\
-\n\
-Tip: Sólo envía una respuesta en blanco a este mensaje si no estás\n\
-seguro."
-
-#: Delay message
-msgid "delay_msg"
-msgstr "Mensaje de demora."
diff --git a/src/log/all.log b/src/log/all.log
deleted file mode 100644
index f7eb32f..0000000
--- a/src/log/all.log
+++ /dev/null
@@ -1,21 +0,0 @@
-
-[ INFO] 2014-06-20 20:51:38 - Redirecting logging to ./log/
-[ DEBUG] 2014-06-20 20:51:38 - New core object created
-[ INFO] 2014-06-20 20:51:38 - Request to create new ./providers/dropbox.links
-[ INFO] 2014-06-20 20:51:38 - Backing up ./providers/dropbox.links to ./providers/dropbox.links.backup
-[ INFO] 2014-06-20 20:51:38 - New ./providers/dropbox.links created
-[ INFO] 2014-06-20 20:51:39 - Link https://db.tt/jp3Ytnzh https://db.tt/MDfUTb04 111-222-333-444 added to linux es in Dropbox
-[ INFO] 2014-06-20 20:51:43 - Redirecting logging to ./log/
-[ DEBUG] 2014-06-20 20:51:43 - New core object created
-[ INFO] 2014-06-20 20:51:43 - <module '__main__' from 'core_demo.py'> did a request for linux, es.
-[ INFO] 2014-06-20 20:51:43 - Reading links from providers directory
-[ DEBUG] 2014-06-20 20:51:43 - -- Reading /home/ilv/Proyectos/github/gettor/src/providers/mirrors.links
-[ DEBUG] 2014-06-20 20:51:43 - -- Checking if Official mirrors has links for linux in es
-[ DEBUG] 2014-06-20 20:51:43 - -- Reading /home/ilv/Proyectos/github/gettor/src/providers/dropbox.links
-[ DEBUG] 2014-06-20 20:51:43 - -- Checking if Dropbox has links for linux in es
-[ DEBUG] 2014-06-20 20:51:43 - -- Reading /home/ilv/Proyectos/github/gettor/src/providers/gdrive.links
-[ DEBUG] 2014-06-20 20:51:43 - -- Checking if Google Drive has links for linux in es
-[ DEBUG] 2014-06-20 20:51:43 - -- Reading /home/ilv/Proyectos/github/gettor/src/providers/github.links
-[ DEBUG] 2014-06-20 20:51:43 - -- Checking if Github has links for linux in es
-[ DEBUG] 2014-06-20 20:51:43 - Joining all links found for linux in es
-[ INFO] 2014-06-20 20:51:43 - Returning the links
diff --git a/src/log/debug.log b/src/log/debug.log
deleted file mode 100644
index 9698f90..0000000
--- a/src/log/debug.log
+++ /dev/null
@@ -1,12 +0,0 @@
-
-[ DEBUG] 2014-06-20 20:51:38 - New core object created
-[ DEBUG] 2014-06-20 20:51:43 - New core object created
-[ DEBUG] 2014-06-20 20:51:43 - -- Reading /home/ilv/Proyectos/github/gettor/src/providers/mirrors.links
-[ DEBUG] 2014-06-20 20:51:43 - -- Checking if Official mirrors has links for linux in es
-[ DEBUG] 2014-06-20 20:51:43 - -- Reading /home/ilv/Proyectos/github/gettor/src/providers/dropbox.links
-[ DEBUG] 2014-06-20 20:51:43 - -- Checking if Dropbox has links for linux in es
-[ DEBUG] 2014-06-20 20:51:43 - -- Reading /home/ilv/Proyectos/github/gettor/src/providers/gdrive.links
-[ DEBUG] 2014-06-20 20:51:43 - -- Checking if Google Drive has links for linux in es
-[ DEBUG] 2014-06-20 20:51:43 - -- Reading /home/ilv/Proyectos/github/gettor/src/providers/github.links
-[ DEBUG] 2014-06-20 20:51:43 - -- Checking if Github has links for linux in es
-[ DEBUG] 2014-06-20 20:51:43 - Joining all links found for linux in es
diff --git a/src/log/error.log b/src/log/error.log
deleted file mode 100644
index 8b13789..0000000
--- a/src/log/error.log
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/log/info.log b/src/log/info.log
deleted file mode 100644
index 06d7160..0000000
--- a/src/log/info.log
+++ /dev/null
@@ -1,10 +0,0 @@
-
-[ INFO] 2014-06-20 20:51:38 - Redirecting logging to ./log/
-[ INFO] 2014-06-20 20:51:38 - Request to create new ./providers/dropbox.links
-[ INFO] 2014-06-20 20:51:38 - Backing up ./providers/dropbox.links to ./providers/dropbox.links.backup
-[ INFO] 2014-06-20 20:51:38 - New ./providers/dropbox.links created
-[ INFO] 2014-06-20 20:51:39 - Link https://db.tt/jp3Ytnzh https://db.tt/MDfUTb04 111-222-333-444 added to linux es in Dropbox
-[ INFO] 2014-06-20 20:51:43 - Redirecting logging to ./log/
-[ INFO] 2014-06-20 20:51:43 - <module '__main__' from 'core_demo.py'> did a request for linux, es.
-[ INFO] 2014-06-20 20:51:43 - Reading links from providers directory
-[ INFO] 2014-06-20 20:51:43 - Returning the links
diff --git a/src/log/warn.log b/src/log/warn.log
deleted file mode 100644
index 8b13789..0000000
--- a/src/log/warn.log
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/providers/dropbox.links b/src/providers/dropbox.links
deleted file mode 100644
index 7cccca0..0000000
--- a/src/providers/dropbox.links
+++ /dev/null
@@ -1,17 +0,0 @@
-[provider]
-name = Dropbox
-
-[key]
-fingerprint = 8738 A680 B84B 3031 A630 F2DB 416F 0610 63FE E659
-
-[linux]
-en = https://db.tt/ZAzgGm4Q https://db.tt/dUI80d2K 98ea6e4f216f2fb4b69fff9b3a44842c38686ca685f3f55dc48c5d3fb1107be4
-es = https://db.tt/jp3Ytnzh https://db.tt/MDfUTb04 98ea6e4f216f2fb4b69fff9b3a44842c38686ca685f3f55dc48c5d3fb1107be4
-
-[windows]
-en = https://db.tt/ZAzgGm4Q https://db.tt/dUI80d2K 98ea6e4f216f2fb4b69fff9b3a44842c38686ca685f3f55dc48c5d3fb1107be4
-es = https://db.tt/jp3Ytnzh https://db.tt/MDfUTb04 98ea6e4f216f2fb4b69fff9b3a44842c38686ca685f3f55dc48c5d3fb1107be4
-
-[osx]
-en = https://db.tt/ZAzgGm4Q https://db.tt/dUI80d2K 98ea6e4f216f2fb4b69fff9b3a44842c38686ca685f3f55dc48c5d3fb1107be4
-es = https://db.tt/jp3Ytnzh https://db.tt/MDfUTb04 98ea6e4f216f2fb4b69fff9b3a44842c38686ca685f3f55dc48c5d3fb1107be4
diff --git a/src/scripts/blacklist.py b/src/scripts/blacklist.py
deleted file mode 100644
index bf812e8..0000000
--- a/src/scripts/blacklist.py
+++ /dev/null
@@ -1,111 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-#
-# This file is part of GetTor, a Tor Browser Bundle distribution system.
-#
-# :authors: Israel Leiva <ilv(a)riseup.net>
-# see also AUTHORS file
-#
-# :copyright: (c) 2008-2014, The Tor Project, Inc.
-# (c) 2014, Israel Leiva
-#
-# :license: This is Free Software. See LICENSE for license information.
-
-import sys
-import time
-import sqlite3
-import argparse
-
-
-def main():
- """Script for managing blacklisting of users.
-
- See argparse usage for more details.
-
- """
- parser = argparse.ArgumentParser(description='Utility for GetTor'
- ' blacklisting')
- parser.add_argument('database', metavar='database.db', type=str,
- help='the database file')
- parser.add_argument('-u', '--user', default=None,
- help='filter by user hash')
- parser.add_argument('-s', '--service', default=None,
- help='filter by service')
- parser.add_argument('-b', '--blocked', default=None,
- help='filter by blocked users')
- parser.add_argument('-a', '--add', default=None, nargs=3,
- metavar=('USER', 'SERVICE', 'BLOCKED'),
- help='add user')
- parser.add_argument('-c', '--clean', default=None, const='c', nargs='?',
- metavar='user hash',
- help='clean table (delete expired blacklistings)')
- parser.add_argument('-r', '--requests', default=None,
- help='number of requests; everyone with number of'
- ' requests greather than this will be cleaned up')
-
- args = parser.parse_args()
- query = ''
- con = sqlite3.connect(args.database)
-
- if args.add:
- # add new entry, useful for adding users permanently blocked
- query = "INSERT INTO users VALUES('%s', '%s', 1, %s, %s)"\
- % (args.add[0], args.add[1], args.add[2], time.time())
- with con:
- cur = con.cursor()
- cur.execute(query)
- print "Query execute successfully"
- elif args.clean:
- if args.clean == 'c':
- if args.requests:
- # delete by number of times
- query = "DELETE FROM users WHERE times > %s" % args.requests
- with con:
- cur = con.cursor()
- cur.execute(query)
- print "Query executed successfully."
- else:
- sys.exit("Number of requests missing. See --help.")
- else:
- # delete by id
- query = "DELETE FROM users WHERE id='%s'" % args.clean
- with con:
- cur = con.cursor()
- cur.execute(query)
- print "Query execute succcessfully."
- else:
- query = "SELECT * FROM users"
- has_where = False
- # filter
- if args.service:
- query = "%s %s" % (query, "WHERE service='%s'" % args.service)
- has_where = True
- if args.user:
- if has_where:
- query = "%s %s" % (query, "AND id='%s'" % args.user)
- else:
- query = "%s %s" % (query, "WHERE id='%s'" % args.user)
- has_where = True
- if args.blocked:
- if has_where:
- query = "%s %s" % (query, "AND blocked=%s" % args.blocked)
- has_where = True
- else:
- query = "%s %s" % (query, "WHERE blocked=%s" % args.blocked)
-
- with con:
- cur = con.cursor()
- cur.execute(query)
- rows = cur.fetchall()
- # show it nice
- print "\nNumber of results: %s\n" % len(rows)
- cns = [cn[0] for cn in cur.description]
- print "%-70s %-10s %-10s %-10s %-s" % (cns[0], cns[1], cns[2],
- cns[3], cns[4])
-
- for row in rows:
- print "%-70s %-10s %-10s %-10s %s" % (row[0], row[1], row[2],
- row[3], row[4])
-
-if __name__ == "__main__":
- main()
diff --git a/src/scripts/create_db.py b/src/scripts/create_db.py
deleted file mode 100644
index 057ae82..0000000
--- a/src/scripts/create_db.py
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-#
-# This file is part of GetTor, a Tor Browser Bundle distribution system.
-#
-# :authors: Israel Leiva <ilv(a)riseup.net>
-# see also AUTHORS file
-#
-# :copyright: (c) 2008-2014, The Tor Project, Inc.
-# (c) 2014, Israel Leiva
-#
-# :license: This is Free Software. See LICENSE for license information.
-
-import os
-import sqlite3
-import argparse
-
-
-def main():
- """Create/delete GetTor database for managing stats and blacklisting.
-
- Database file (.db) must be empty. If it doesn't exist, it will be
- created. See argparse usage for more details.
-
- """
- parser = argparse.ArgumentParser(description='Utility for GetTor'
- ' database')
- parser.add_argument('-c', '--create', default=None,
- metavar='path_to_database.db',
- help='create database')
- parser.add_argument('-d', '--delete', default=None,
- metavar='path_to_database.db',
- help='delete database')
-
- args = parser.parse_args()
- if args.create:
- con = sqlite3.connect(args.create)
- with con:
- cur = con.cursor()
- # table for handling users (i.e. blacklist)
- cur.execute("CREATE TABLE users(id TEXT, service TEXT, times INT,"
- " blocked INT, last_request TEXT)")
- # table for stats
- cur.execute("CREATE TABLE requests(service TEXT, type TEXT,"
- " os TEXT, lc TEXT, pt INT, year INT, month INT,"
- " day INT, status TEXT, logfile TEXT)")
- print "Database %s created" % os.path.abspath(args.create)
- elif args.delete:
- os.remove(os.path.abspath(args.delete))
- print "Database %s deleted" % os.path.abspath(args.delete)
- else:
- print "See --help for details on usage."
-if __name__ == "__main__":
- main()
diff --git a/src/scripts/stats.py b/src/scripts/stats.py
deleted file mode 100644
index f1d782d..0000000
--- a/src/scripts/stats.py
+++ /dev/null
@@ -1,123 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-#
-# This file is part of GetTor, a Tor Browser Bundle distribution system.
-#
-# :authors: Israel Leiva <ilv(a)riseup.net>
-# see also AUTHORS file
-#
-# :copyright: (c) 2008-2014, The Tor Project, Inc.
-# (c) 2014, Israel Leiva
-#
-# :license: This is Free Software. See LICENSE for license information.
-
-
-import sqlite3
-import argparse
-
-
-def main():
- """Script for showing stats.
-
- See argparse usage for more details.
-
- """
- parser = argparse.ArgumentParser(description='Utility for GetTor stats')
- parser.add_argument('database', metavar='database.db', type=str,
- help='the database file')
- parser.add_argument('-s', '--service', default=None,
- help='filter by service')
- parser.add_argument('-t', '--type', default=None,
- help='filter by type of request')
- parser.add_argument('-o', '--os', default=None,
- help='filter by OS')
- parser.add_argument('-l', '--lc', default=None,
- help='filter by locale')
- parser.add_argument('-p', '--pt', default=None,
- help='filter by PT requests')
- parser.add_argument('-y', '--year', default=None,
- help='filter by year')
- parser.add_argument('-m', '--month', default=None,
- help='filter by month')
- parser.add_argument('-d', '--day', default=None,
- help='filter by day')
- parser.add_argument('-u', '--status', default=None,
- help='filter by status of the request')
-
- args = parser.parse_args()
- query = 'SELECT * FROM requests'
- has_where = False
-
- # we build the query piece by piece
- if args.service:
- query = "%s %s" % (query, "WHERE service = '%s'" % args.service)
- has_where = True
- if args.type:
- if has_where:
- query = "%s %s" % (query, "AND type = '%s'" % args.type)
- else:
- query = "%s %s" % (query, "WHERE type = '%s'" % args.type)
- has_where = True
- if args.os:
- if has_where:
- query = "%s %s" % (query, "AND os = '%s'" % args.os)
- else:
- query = "%s %s" % (query, "WHERE os = '%s'" % args.os)
- has_where = True
- if args.lc:
- if has_where:
- query = "%s %s" % (query, "AND lc = '%s'" % args.lc)
- else:
- query = "%s %s" % (query, "WHERE lc = '%s'" % args.lc)
- has_where = True
- if args.pt:
- if has_where:
- query = "%s %s" % (query, "AND pt = %s" % args.pt)
- else:
- query = "%s %s" % (query, "WHERE pt = %s" % args.pt)
- has_where = True
- if args.year:
- if has_where:
- query = "%s %s" % (query, "AND year = %s" % args.year)
- else:
- query = "%s %s" % (query, "WHERE year = %s" % args.year)
- has_where = True
- if args.month:
- if has_where:
- query = "%s %s" % (query, "AND month = %s" % args.month)
- else:
- query = "%s %s" % (query, "WHERE month = %s" % args.month)
- has_where = True
- if args.day:
- if has_where:
- query = "%s %s" % (query, "AND day = %s" % args.day)
- else:
- query = "%s %s" % (query, "WHERE day = %s" % args.day)
- has_where = True
- if args.status:
- if has_where:
- query = "%s %s" % (query, "AND status = '%s'" % args.status)
- else:
- query = "%s %s" % (query, "WHERE status = '%s'" % args.status)
- has_where = True
-
- con = sqlite3.connect(args.database)
-
- with con:
- cur = con.cursor()
- cur.execute(query)
- rows = cur.fetchall()
- # show it nice
- print "\nNumber of results: %s\n" % len(rows)
- cns = [cn[0] for cn in cur.description]
- print "%-10s %-10s %-10s %-10s %-10s %-10s %-10s %-10s"\
- " %-15s %s"\ % (cns[0], cns[1], cns[2], cns[3], cns[4], cns[5],
- cns[6], cns[7], cns[8], cns[9])
-
- for row in rows:
- print "%-10s %-10s %-10s %-10s %-10s %-10s %-10s %-10s"\
- " %-15s %s" % (row[0], row[1], row[2], row[3], row[4],
- row[5], row[6], row[7], row[8], row[9])
-
-if __name__ == "__main__":
- main()
diff --git a/src/smtp.cfg b/src/smtp.cfg
deleted file mode 100644
index ddbf5fb..0000000
--- a/src/smtp.cfg
+++ /dev/null
@@ -1,17 +0,0 @@
-[general]
-basedir: /path/to/gettor/smtp
-our_domain: torproject.org
-core_cfg: /path/to/core.cfg
-
-[blacklist]
-cfg: /path/to/blacklist.cfg
-max_requests: 3
-wait_time: 20
-
-[i18n]
-dir: i18n/
-
-[log]
-level: DEBUG
-dir: log/
-emails_dir: emails/
diff --git a/src/smtp/log/all.log b/src/smtp/log/all.log
deleted file mode 100644
index 21e5f3b..0000000
--- a/src/smtp/log/all.log
+++ /dev/null
@@ -1,16 +0,0 @@
-
-[ 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(a)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
diff --git a/src/smtp/log/debug.log b/src/smtp/log/debug.log
deleted file mode 100644
index bef7c7a..0000000
--- a/src/smtp/log/debug.log
+++ /dev/null
@@ -1,14 +0,0 @@
-
-[ 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(a)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
diff --git a/src/smtp/log/error.log b/src/smtp/log/error.log
deleted file mode 100644
index e69de29..0000000
diff --git a/src/smtp/log/info.log b/src/smtp/log/info.log
deleted file mode 100644
index 66d40f1..0000000
--- a/src/smtp/log/info.log
+++ /dev/null
@@ -1,3 +0,0 @@
-
-[ 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
diff --git a/src/smtp/log/warn.log b/src/smtp/log/warn.log
deleted file mode 100644
index e69de29..0000000
diff --git a/src/smtp/sample/sample-email.eml b/src/smtp/sample/sample-email.eml
deleted file mode 100644
index 7c30644..0000000
--- a/src/smtp/sample/sample-email.eml
+++ /dev/null
@@ -1,57 +0,0 @@
-X-Account-Key: account6
-X-UIDL: 1214981061.25808.faustus,S=2285
-X-Mozilla-Status: 0001
-X-Mozilla-Status2: 02000000
-Return-Path: <ioerror(a)gmail.com>
-Delivered-To: jpopped(a)appelbaum.net
-Received: (qmail 25806 invoked by uid 89); 2 Jul 2008 06:44:21 -0000
-Delivered-To: appelbaum.net-jacob(a)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(a)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(a)mail.gmail.com>
-Date: Tue, 1 Jul 2008 23:41:57 -0700
-From: "Jacob Applebaum" <ioerror(a)gmail.com>
-To: gettor+en(a)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
-
-linux
-
-------=_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--
-
-
diff --git a/src/smtp_demo.py b/src/smtp_demo.py
deleted file mode 100644
index 418b3bd..0000000
--- a/src/smtp_demo.py
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/env python
-import sys
-
-import gettor.smtp
-
-service = gettor.smtp.SMTP()
-
-# 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 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/tbb-key.asc b/src/tbb-key.asc
deleted file mode 100644
index bd13d7e..0000000
--- a/src/tbb-key.asc
+++ /dev/null
@@ -1,2986 +0,0 @@
------BEGIN PGP PUBLIC KEY BLOCK-----
-Version: GnuPG v1.4.11 (GNU/Linux)
-
-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
-aH2j7xNkRUp2beptFF6GFRJMzcHKA7Pl6z/A0BdXwjMuAFZf7PwoxzWEqJ18XXWs
-pXfK8MP6q3SA24siAeMdO1fFbwBgIPbNBc/TEAGT4GA4vWtlL8h4sJFt/HpWJ748
-RbGg9Nfre7dUzwMVD5YPcag4i1YJgVrmiYk9K3p/2xx2kajqW/h+i3hboJyEIU9b
-o8qGamTnpcpYKWO8iEX3S6RlMkPi/6V+qgl20iiMyHtVrEToHQekrSh/EgtmeWx1
-zZum6y8SKCFjkU9IJry6NTZ7cmuZyD4LYXAtefUDv3beMwWASQGBCq+zd3b3Rn5I
-wg4j+4NN226xKyZ+tD3sn3WtHSlhq+wfN1BRi0tX/BZq4YGu2VAAqF/q9n4F/o4a
-UhNHxpgqb/IUtkmPpjhBEuOqj0lbTb6KFcG+H1EeQAot5TM7M5fGUbU8ZlY742AJ
-IhvjhoP/3POCeryNYIkCHAQQAQoABgUCS6e8LgAKCRDM0u2U0hc56W6PD/4vIdIw
-AW6JcFgwBmIL5eVJhS4NszELeLJoVWOgE1bxFYTLn0WJiMg1IJtURJNste2+tE8p
-tVFDS88cVdVKH67olE7PUFKPS4h3CPf7NQCbqx9igN42w0EpdedvRZSvQ2rEIU9Q
-Z2uk2CWWGsKMny2GFG9zSDWGgiSX+sJKaGxvuGnZLfNf////////////////////
-////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////
-/////////////////////////////////////4kCHAQQAQoABgUCUhL5sAAKCRAx
-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
-5r/sl2AcI/A8jxLn7WtKShq5rfUtNXwrDV3MXtaKw+1Mypqy6zkuYmjXZr5/7zYs
-Czzc0vM0FOZfmzBSznGG9ZoYCuGbi/T1pHqZ3VteamTdh8qHV1CYNTuJvldULI08
-cqnIlDIWK4Le3g5AA5hMuT3gIdo9KCZzfqHR855UbzkWqfI41LnjkGm/IfzwDeGZ
-1aTHUhrwwsEtig+UUCn+gna8Fz/w3GjZN/YCg/n7k6gqKsV7spdM1T0uA0+skrqD
-P3nx6Jv8CH5fCUwx5mJKUdpJz0HGnkbxFo+zoECZt8poO2/nS0woFSeifuFbDpMJ
-2m+OiHkYwR/a/JkQn+snJ/n6iym2ajoPiTqtmSjIlbBph52ky8QdLHh8fqJcZTCr
-T8bZOBv0/DXIX/cdTA8pOsXrzAv8P20u+AR8QauGljq8cxPIPv9Ojq2TX2lNUDDT
-EyuiJUHUe68rS50/8Q6OgfGt1KoNY8Q3VOjNQdCOjWDPSBhdRezJt13DGrv+ihGN
-MeNehdOtdOtxRBOufRQCCxA9////////////////////////////////////////
-////////////////////////////////////////////////////////////////
-/4kCHAQTAQIABgUCTPn4SgAKCRAN+KXPnVGk/OXwD/9xbPPhWix1my7+O9Z1MWv8
-Q+pPVIOOqgQe0UmtQdsd5r/sl2AcI/A8jxLn7WtKShq5rfUtNXwrDV3MXtaKw+1M
-ypqy6zkuYmjXZr5/7zYsCzzc0vM0FOZfmzBSznGG9ZoYCuGbi/T1pHqZ3VteamTd
-h8qHV1CYNTuJvldULI08cqnIlDIWK4Le3g5AA5hMuT3gIdo9KCZzfqHR855UbzkW
-qfI41LnjkGm/IfzwDeGZ1aTHUhrwwsEtig+UUCn+gna8Fz/w3GjZN/YCg/n7k6gq
-KsV7spdM1T0uA0+skrqDP3nx6Jv8CH5fCUwx5mJKUdpJz0HGnkbxFo+zoECZt8po
-O2/nS0woFSeifuFbDpMJ2m+OiHkYwR/a/JkQn+snJ/n6iym2ajoPiTqtmSjIlbBp
-h52ky8QdLHh8fqJcZTCrT8bZOBv0/DXIX/cdTA8pOsXrzAv8P20u+AR8QauGljq8
-cxPIPv9Ojq2TX2lNUDDTEyuiJUHUe68rS50/8Q6OgfGt1KoNY8Q3VOjNQdCOjWDP
-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
-rGB4f7DBY5fpJsJz8gFH+YSG05igQ14z4yTotUSGXjG3z1j8lgWDmhG/SDAtFjV4
-cTYoZZKDXYipCOkPRL7emZ6/XaUBlPOVqIF7l4OnDjbre0K+XQUDdUif+Dn2WAOu
-v+xdyR/ynxICaqrIZC8NtLSWdQPzGTPJlhRF3gSPE/65ChNWQ5ocNGLq2kxTir9r
-5j1SFgz+gCmCsm544pbGvhRrHcsKz8mnEIiILl0gth+tAJZuOZcJPaxZlI3C/F5S
-2yQwGQWBM2H/ATZcQhdh/fjHqd27UMpSCC6ma15JL/a/o/KV0Eav/sXeLTwNBNlS
-UoudfxAxyrXtkGYhCvBU1hlh+H8tfXHkfBUqi3eu4fy+MMTO9YVs+3E9uYm+wmra
-xoeQe/j5Jkvlp7Y9eiCt70hEKdoGeQvNfyyP0EYCAKq51bmqn+9kaQGoQ51N5srE
-hoTXuNn+SKEsb+MttQ02kLaX024QuEQNYT6YXFbsbAi5SajBDfupYivDDKylqyNC
-PP26hlD/1lpFz1pSUqYNExv6fSjMZgYSYw2tKD2nU++b6gvQmAFqW4fU0BC1QkWB
-BQcZF11C3IO00bXHf9fKo4kCHAQQAQIABgUCTE+AHwAKCRBmyMLXxapEbZWyD/wM
-2shOlJ7gVDVThP6FbyJkM0YtEErqRcLfwIGbx4HrycUnyxXiHBqE540w17511QVb
-rnSXabfqJP64wRis09MG3oquPsXdTPcF5NIX1nX6pP77oxsbCncym25Hid00v4pY
-nx8Oiu1JLv5PueUNa5YEkiPqrNMiOsOlk2P5yH9krjfNSvQPpX/HJOWOyhvzEqID
-WC71WMlzl4JQLl7R8+5kW3scjUvL6z2Ewc+YD5fVzETfYmx1R0Xg3QZ+qSiY6Mif
-JK+xajcLXCdUXsp0ZKEANxJBpjjcxPGtXM1cnLSS6lzevBNh+HmRdc/bQHbKd7/W
-h1DNDdRAa4J5tSiT3FgEAOJulwaw81ibxlE4qsiwG6gFRHAFCEwc76qRpxMtB+f6
-JWIdGU2jt8/5u1OuD2SCvrAeXIPftPuQA8/iTAhHGmHYNi1+AKNPQlraOEHxeHdG
-EfpX+s56NASI3UG3ZuzVafqGbg+pUtU7DPbmE12M+Zhh9G/QFrJHuCl6rS44IIfC
-vTn9Mj8Q7otXdRQ0q54mRmy6BvMi1pi1XshPzsmLCZiDvs8h7u9YkKXopdKRT0UR
-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
-qg==
-=QqHT
------END PGP PUBLIC KEY BLOCK-----
diff --git a/src/upload/tor-browser-linux32-3.6.2_en-EN.tar.xz b/src/upload/tor-browser-linux32-3.6.2_en-EN.tar.xz
deleted file mode 100644
index 45b983b..0000000
--- a/src/upload/tor-browser-linux32-3.6.2_en-EN.tar.xz
+++ /dev/null
@@ -1 +0,0 @@
-hi
diff --git a/src/upload/tor-browser-linux32-3.6.2_en-EN.tar.xz.asc b/src/upload/tor-browser-linux32-3.6.2_en-EN.tar.xz.asc
deleted file mode 100644
index f540c1f..0000000
--- a/src/upload/tor-browser-linux32-3.6.2_en-EN.tar.xz.asc
+++ /dev/null
@@ -1,11 +0,0 @@
------BEGIN PGP SIGNATURE-----
-Version: GnuPG v1
-
-iQEcBAABCAAGBQJTliy3AAoJEEFvBhBj/uZZKm0IAMZDMpDlYMY2bbSSBPNNzA9r
-B7FYXTDSYIOIqU4s+bPkG1JRnkeu9nNvv/2YPDtJxAjAxVv0NkXN+XMgeUWOKxhu
-kvDw5+0qPIS6IF9AiWAors5pDBCT1+oRN1jizM+NgolLYJygJ4YHggIwStoypiei
-1nZDCEPMStNSSCTeJd1Y8US9oqD7I3SMtAoYzpc6fnuQ7bWrd4GxdBlNEBoEZOP3
-B4y37hQ1Ada/i+WPnShjMBtIuO2G6/E/JSiUbNHzwNZ+KyuO2KxFC8V6Z57bzULa
-WqB/tUWtBVfPPNxzQTaT919CLYyrUcQf6beVSxVuBNz94wntVbjVLr9T3nskNdo=
-=LPhT
------END PGP SIGNATURE-----
diff --git a/src/upload/tor-browser-linux32-3.6.2_es-ES.tar.xz b/src/upload/tor-browser-linux32-3.6.2_es-ES.tar.xz
deleted file mode 100644
index 45b983b..0000000
--- a/src/upload/tor-browser-linux32-3.6.2_es-ES.tar.xz
+++ /dev/null
@@ -1 +0,0 @@
-hi
diff --git a/src/upload/tor-browser-linux32-3.6.2_es-ES.tar.xz.asc b/src/upload/tor-browser-linux32-3.6.2_es-ES.tar.xz.asc
deleted file mode 100644
index f540c1f..0000000
--- a/src/upload/tor-browser-linux32-3.6.2_es-ES.tar.xz.asc
+++ /dev/null
@@ -1,11 +0,0 @@
------BEGIN PGP SIGNATURE-----
-Version: GnuPG v1
-
-iQEcBAABCAAGBQJTliy3AAoJEEFvBhBj/uZZKm0IAMZDMpDlYMY2bbSSBPNNzA9r
-B7FYXTDSYIOIqU4s+bPkG1JRnkeu9nNvv/2YPDtJxAjAxVv0NkXN+XMgeUWOKxhu
-kvDw5+0qPIS6IF9AiWAors5pDBCT1+oRN1jizM+NgolLYJygJ4YHggIwStoypiei
-1nZDCEPMStNSSCTeJd1Y8US9oqD7I3SMtAoYzpc6fnuQ7bWrd4GxdBlNEBoEZOP3
-B4y37hQ1Ada/i+WPnShjMBtIuO2G6/E/JSiUbNHzwNZ+KyuO2KxFC8V6Z57bzULa
-WqB/tUWtBVfPPNxzQTaT919CLYyrUcQf6beVSxVuBNz94wntVbjVLr9T3nskNdo=
-=LPhT
------END PGP SIGNATURE-----
diff --git a/src/xmpp.cfg b/src/xmpp.cfg
deleted file mode 100644
index 20d8227..0000000
--- a/src/xmpp.cfg
+++ /dev/null
@@ -1,20 +0,0 @@
-[account]
-user: account@domain
-password:
-
-[general]
-basedir: /path/to/gettor/xmpp/
-core_cfg: /path/to/core.cfg
-
-[blacklist]
-cfg: /path/to/blacklist.cfg
-max_requests: 3
-wait_time: 20
-
-[i18n]
-dir: i18n/
-
-[log]
-level: DEBUG
-dir: log/
-msgs_dir: msgs/
diff --git a/src/xmpp/i18n/en/LC_MESSAGES/en.mo b/src/xmpp/i18n/en/LC_MESSAGES/en.mo
deleted file mode 100644
index 2480872..0000000
Binary files a/src/xmpp/i18n/en/LC_MESSAGES/en.mo and /dev/null differ
diff --git a/src/xmpp/i18n/en/LC_MESSAGES/en.po b/src/xmpp/i18n/en/LC_MESSAGES/en.po
deleted file mode 100644
index d242625..0000000
--- a/src/xmpp/i18n/en/LC_MESSAGES/en.po
+++ /dev/null
@@ -1,22 +0,0 @@
-domain "en"
-
-#: Links
-msgid "links"
-msgstr "Links %s-%s:\n %s"
-
-#: Links
-msgid "links_pt"
-msgstr "Links-PT for %s-%s:\n %s"
-
-#: Help
-msgid "help"
-msgstr "*help*"
-
-#: Unsupported locale
-msgid "unsupported_lc"
-msgstr "Unsupported locale"
-
-#: Internal error
-msgid "internal_error"
-msgstr "Internal error"
-
diff --git a/src/xmpp/i18n/en/LC_MESSAGES/es.mo b/src/xmpp/i18n/en/LC_MESSAGES/es.mo
deleted file mode 100644
index 5896536..0000000
Binary files a/src/xmpp/i18n/en/LC_MESSAGES/es.mo and /dev/null differ
diff --git a/src/xmpp/i18n/es/LC_MESSAGES/es.mo b/src/xmpp/i18n/es/LC_MESSAGES/es.mo
deleted file mode 100644
index d54f22c..0000000
Binary files a/src/xmpp/i18n/es/LC_MESSAGES/es.mo and /dev/null differ
diff --git a/src/xmpp/i18n/es/LC_MESSAGES/es.po b/src/xmpp/i18n/es/LC_MESSAGES/es.po
deleted file mode 100644
index b6caac5..0000000
--- a/src/xmpp/i18n/es/LC_MESSAGES/es.po
+++ /dev/null
@@ -1,22 +0,0 @@
-domain "es"
-
-#: Links
-msgid "links"
-msgstr "Enlaces para %s-%s:\n %s"
-
-#: Links
-msgid "links_pt"
-msgstr "Enlaces-PT para %s-%s:\n %s"
-
-#: Help
-msgid "help"
-msgstr "ayuda"
-
-#: Unsupported locale
-msgid "unsupported_lc"
-msgstr "Locale no soportado"
-
-#: Internal error
-msgid "internal_error"
-msgstr "Error interno"
-
diff --git a/src/xmpp_demo.py b/src/xmpp_demo.py
deleted file mode 100644
index 144f4a8..0000000
--- a/src/xmpp_demo.py
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/env python
-import sys
-
-import gettor.xmpp
-
-bot = gettor.xmpp.XMPP()
-bot.start_bot()
diff --git a/upload/tor-browser-linux32-3.6.2_en-EN.tar.xz b/upload/tor-browser-linux32-3.6.2_en-EN.tar.xz
new file mode 100644
index 0000000..45b983b
--- /dev/null
+++ b/upload/tor-browser-linux32-3.6.2_en-EN.tar.xz
@@ -0,0 +1 @@
+hi
diff --git a/upload/tor-browser-linux32-3.6.2_en-EN.tar.xz.asc b/upload/tor-browser-linux32-3.6.2_en-EN.tar.xz.asc
new file mode 100644
index 0000000..f540c1f
--- /dev/null
+++ b/upload/tor-browser-linux32-3.6.2_en-EN.tar.xz.asc
@@ -0,0 +1,11 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1
+
+iQEcBAABCAAGBQJTliy3AAoJEEFvBhBj/uZZKm0IAMZDMpDlYMY2bbSSBPNNzA9r
+B7FYXTDSYIOIqU4s+bPkG1JRnkeu9nNvv/2YPDtJxAjAxVv0NkXN+XMgeUWOKxhu
+kvDw5+0qPIS6IF9AiWAors5pDBCT1+oRN1jizM+NgolLYJygJ4YHggIwStoypiei
+1nZDCEPMStNSSCTeJd1Y8US9oqD7I3SMtAoYzpc6fnuQ7bWrd4GxdBlNEBoEZOP3
+B4y37hQ1Ada/i+WPnShjMBtIuO2G6/E/JSiUbNHzwNZ+KyuO2KxFC8V6Z57bzULa
+WqB/tUWtBVfPPNxzQTaT919CLYyrUcQf6beVSxVuBNz94wntVbjVLr9T3nskNdo=
+=LPhT
+-----END PGP SIGNATURE-----
diff --git a/upload/tor-browser-linux32-3.6.2_es-ES.tar.xz b/upload/tor-browser-linux32-3.6.2_es-ES.tar.xz
new file mode 100644
index 0000000..45b983b
--- /dev/null
+++ b/upload/tor-browser-linux32-3.6.2_es-ES.tar.xz
@@ -0,0 +1 @@
+hi
diff --git a/upload/tor-browser-linux32-3.6.2_es-ES.tar.xz.asc b/upload/tor-browser-linux32-3.6.2_es-ES.tar.xz.asc
new file mode 100644
index 0000000..f540c1f
--- /dev/null
+++ b/upload/tor-browser-linux32-3.6.2_es-ES.tar.xz.asc
@@ -0,0 +1,11 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1
+
+iQEcBAABCAAGBQJTliy3AAoJEEFvBhBj/uZZKm0IAMZDMpDlYMY2bbSSBPNNzA9r
+B7FYXTDSYIOIqU4s+bPkG1JRnkeu9nNvv/2YPDtJxAjAxVv0NkXN+XMgeUWOKxhu
+kvDw5+0qPIS6IF9AiWAors5pDBCT1+oRN1jizM+NgolLYJygJ4YHggIwStoypiei
+1nZDCEPMStNSSCTeJd1Y8US9oqD7I3SMtAoYzpc6fnuQ7bWrd4GxdBlNEBoEZOP3
+B4y37hQ1Ada/i+WPnShjMBtIuO2G6/E/JSiUbNHzwNZ+KyuO2KxFC8V6Z57bzULa
+WqB/tUWtBVfPPNxzQTaT919CLYyrUcQf6beVSxVuBNz94wntVbjVLr9T3nskNdo=
+=LPhT
+-----END PGP SIGNATURE-----
diff --git a/upload/tor-browser-linux64-3.6.2_en-EN.tar.xz b/upload/tor-browser-linux64-3.6.2_en-EN.tar.xz
new file mode 100644
index 0000000..45b983b
--- /dev/null
+++ b/upload/tor-browser-linux64-3.6.2_en-EN.tar.xz
@@ -0,0 +1 @@
+hi
diff --git a/upload/tor-browser-linux64-3.6.2_en-EN.tar.xz.asc b/upload/tor-browser-linux64-3.6.2_en-EN.tar.xz.asc
new file mode 100644
index 0000000..f540c1f
--- /dev/null
+++ b/upload/tor-browser-linux64-3.6.2_en-EN.tar.xz.asc
@@ -0,0 +1,11 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1
+
+iQEcBAABCAAGBQJTliy3AAoJEEFvBhBj/uZZKm0IAMZDMpDlYMY2bbSSBPNNzA9r
+B7FYXTDSYIOIqU4s+bPkG1JRnkeu9nNvv/2YPDtJxAjAxVv0NkXN+XMgeUWOKxhu
+kvDw5+0qPIS6IF9AiWAors5pDBCT1+oRN1jizM+NgolLYJygJ4YHggIwStoypiei
+1nZDCEPMStNSSCTeJd1Y8US9oqD7I3SMtAoYzpc6fnuQ7bWrd4GxdBlNEBoEZOP3
+B4y37hQ1Ada/i+WPnShjMBtIuO2G6/E/JSiUbNHzwNZ+KyuO2KxFC8V6Z57bzULa
+WqB/tUWtBVfPPNxzQTaT919CLYyrUcQf6beVSxVuBNz94wntVbjVLr9T3nskNdo=
+=LPhT
+-----END PGP SIGNATURE-----
diff --git a/upload/tor-browser-osx32-3.6.2_en-EN.tar.xz b/upload/tor-browser-osx32-3.6.2_en-EN.tar.xz
new file mode 100644
index 0000000..45b983b
--- /dev/null
+++ b/upload/tor-browser-osx32-3.6.2_en-EN.tar.xz
@@ -0,0 +1 @@
+hi
diff --git a/upload/tor-browser-osx32-3.6.2_en-EN.tar.xz.asc b/upload/tor-browser-osx32-3.6.2_en-EN.tar.xz.asc
new file mode 100644
index 0000000..f540c1f
--- /dev/null
+++ b/upload/tor-browser-osx32-3.6.2_en-EN.tar.xz.asc
@@ -0,0 +1,11 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1
+
+iQEcBAABCAAGBQJTliy3AAoJEEFvBhBj/uZZKm0IAMZDMpDlYMY2bbSSBPNNzA9r
+B7FYXTDSYIOIqU4s+bPkG1JRnkeu9nNvv/2YPDtJxAjAxVv0NkXN+XMgeUWOKxhu
+kvDw5+0qPIS6IF9AiWAors5pDBCT1+oRN1jizM+NgolLYJygJ4YHggIwStoypiei
+1nZDCEPMStNSSCTeJd1Y8US9oqD7I3SMtAoYzpc6fnuQ7bWrd4GxdBlNEBoEZOP3
+B4y37hQ1Ada/i+WPnShjMBtIuO2G6/E/JSiUbNHzwNZ+KyuO2KxFC8V6Z57bzULa
+WqB/tUWtBVfPPNxzQTaT919CLYyrUcQf6beVSxVuBNz94wntVbjVLr9T3nskNdo=
+=LPhT
+-----END PGP SIGNATURE-----
diff --git a/upload/tor-browser-windows32-3.6.2_en-EN.tar.xz b/upload/tor-browser-windows32-3.6.2_en-EN.tar.xz
new file mode 100644
index 0000000..45b983b
--- /dev/null
+++ b/upload/tor-browser-windows32-3.6.2_en-EN.tar.xz
@@ -0,0 +1 @@
+hi
diff --git a/upload/tor-browser-windows32-3.6.2_en-EN.tar.xz.asc b/upload/tor-browser-windows32-3.6.2_en-EN.tar.xz.asc
new file mode 100644
index 0000000..f540c1f
--- /dev/null
+++ b/upload/tor-browser-windows32-3.6.2_en-EN.tar.xz.asc
@@ -0,0 +1,11 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1
+
+iQEcBAABCAAGBQJTliy3AAoJEEFvBhBj/uZZKm0IAMZDMpDlYMY2bbSSBPNNzA9r
+B7FYXTDSYIOIqU4s+bPkG1JRnkeu9nNvv/2YPDtJxAjAxVv0NkXN+XMgeUWOKxhu
+kvDw5+0qPIS6IF9AiWAors5pDBCT1+oRN1jizM+NgolLYJygJ4YHggIwStoypiei
+1nZDCEPMStNSSCTeJd1Y8US9oqD7I3SMtAoYzpc6fnuQ7bWrd4GxdBlNEBoEZOP3
+B4y37hQ1Ada/i+WPnShjMBtIuO2G6/E/JSiUbNHzwNZ+KyuO2KxFC8V6Z57bzULa
+WqB/tUWtBVfPPNxzQTaT919CLYyrUcQf6beVSxVuBNz94wntVbjVLr9T3nskNdo=
+=LPhT
+-----END PGP SIGNATURE-----
diff --git a/xmpp.cfg b/xmpp.cfg
new file mode 100644
index 0000000..0b2f547
--- /dev/null
+++ b/xmpp.cfg
@@ -0,0 +1,19 @@
+[account]
+user: account@domain
+password:
+
+[general]
+basedir: /path/to/gettor/xmpp/
+core_cfg: /path/to/core.cfg
+
+[blacklist]
+cfg: /path/to/blacklist.cfg
+max_requests: 3
+wait_time: 20
+
+[i18n]
+dir: /path/to/i18n/
+
+[log]
+level: DEBUG
+dir: /path/to/log/
diff --git a/xmpp_demo.py b/xmpp_demo.py
new file mode 100644
index 0000000..144f4a8
--- /dev/null
+++ b/xmpp_demo.py
@@ -0,0 +1,7 @@
+#!/usr/bin/env python
+import sys
+
+import gettor.xmpp
+
+bot = gettor.xmpp.XMPP()
+bot.start_bot()
1
0

[gettor/master] Seems like I messed up with changes for cleaning the code and comments. Fixed
by ilv@torproject.org 22 Sep '15
by ilv@torproject.org 22 Sep '15
22 Sep '15
commit f377c24d92ed9e72a7aeb6134bcb25f7718564c2
Author: ilv <ilv(a)users.noreply.github.com>
Date: Mon Aug 25 18:14:10 2014 -0400
Seems like I messed up with changes for cleaning the code and comments. Fixed
---
src/gettor/core.py | 236 ++++++++++++++++++++++++----------------------------
1 file changed, 110 insertions(+), 126 deletions(-)
diff --git a/src/gettor/core.py b/src/gettor/core.py
index f25b27d..8b1f51a 100644
--- a/src/gettor/core.py
+++ b/src/gettor/core.py
@@ -72,13 +72,12 @@ class Core(object):
def __init__(self, cfg=None):
"""Create a new core object by reading a configuration file.
- Raises: ConfigurationError if the configuration file doesn't exists
+ :param: cfg (string) the path of the configuration file.
+ :raise: 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
+ # define a set of default values
DEFAULT_CONFIG_FILE = 'core.cfg'
logging.basicConfig(format='[%(levelname)s] %(asctime)s - %(message)s',
@@ -115,7 +114,7 @@ class Core(object):
raise ConfigurationError("Error with conf. See log file.")
try:
- self.supported_locales = config.get('links', 'locales')
+ self.supported_lc = 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.")
@@ -139,79 +138,74 @@ class Core(object):
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)
+ # keep log levels separated
+ self.log = utils.filter_logging(logger, self.logdir, self.loglevel)
+ # self.log.setLevel(logging.getLevelName(self.loglevel))
+ self.log.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")
+ # stop logging on stdout from now on
+ self.log.propagate = False
+ self.log.debug("New core object created")
- def get_links(self, service, operating_system, locale):
+ def get_links(self, service, os, lc):
"""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.
+ :param: service (string) the service trying to get the links.
+ :param: os (string) the operating system.
+ :param: lc (string) tthe locale.
- 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.
+ :raise: UnsupportedOSError if the operating system is not supported.
+ :raise: UnsupportedLocaleError if the locale is not supported.
+ :raise: InternalError if something goes wrong while internally.
- Returns: String with links.
+ :return: (string) the links.
"""
# Which module called us and what was asking for?
- self.logger.info("%s did a request for %s, %s." %
- (service, operating_system, locale))
+ self.log.info("%s did a request for %s, %s." % (service, os, lc))
- 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 lc not in self.supported_lc:
+ self.log.warning("Request for unsupported locale: %s" % lc)
+ raise UnsupportedLocaleError("Locale %s not supported" % lc)
- 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)
+ if os not in self.supported_os:
+ self.log.warning("Request for unsupported OS: %s" % os)
+ raise UnsupportedOSError("OS %s not supported " % os)
- # This could change in the future, let's leave it isolated.
- links = self._get_links(operating_system, locale)
+ # this could change in the future, let's leave it isolated.
+ links = self._get_links(os, lc)
if links is None:
- self.logger.error("Couldn't get the links", exc_info=True)
+ self.log.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")
+ self.log.info("Returning the links")
return links
- def _get_links(self, operating_system, locale):
+ def _get_links(self, os, lc):
"""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.
+ :param: os (string) the operating system.
+ :param: lc (string) the locale.
- Params: operating_system - name of the operating system
- locale: two-character string representing the locale.
+ :return: (string/None) links on success, None otherwise.
"""
- # Read the links files using ConfigParser
- # See the README for more details on the format used
+ # read the links files using ConfigParser
+ # see the README for more details on the format used
links = []
- # Look for files ending with .links
+ # look for files ending with .links
p = re.compile('.*\.links$')
for name in os.listdir(self.linksdir):
@@ -219,14 +213,14 @@ class Core(object):
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
+ # let's create a dictionary linking each provider with the links
+ # found for os and lc. This way makes it easy to check if no
+ # links were found
providers = {}
- self.logger.info("Reading links from providers directory")
+ self.log.info("Reading links from providers directory")
for name in links:
- self.logger.debug("Reading %s" % name)
+ self.log.debug("Reading %s" % name)
# We're reading files listed on linksdir, so they must exist!
config = ConfigParser.ConfigParser()
config.read(name)
@@ -234,41 +228,40 @@ class Core(object):
try:
pname = config.get('provider', 'name')
except ConfigParser.Error as e:
- self.logger.warning("Couldn't get 'name' from 'provider' (%s)"
- % name)
+ self.log.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))
+ self.log.debug("Checking if %s has links for %s in %s" %
+ (pname, os, lc))
try:
- providers[pname] = config.get(operating_system, locale)
+ providers[pname] = config.get(os, lc)
except ConfigParser.Error as e:
- self.logger.warning("Couldn't get %s from %s (%s)" %
- (locale, operating_system, name))
+ self.log.warning("Couldn't get %s from %s (%s)" %
+ (lc, os, name))
raise InternalError("Error while reading %s links file. See "
"log file" % name)
- # Each provider must have a fingerprint of the key used to
+ # 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)
+ self.log.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)
+ self.log.debug("Fingerprint added %s", fingerprint)
except ConfigParser.Error as e:
- self.logger.warning("Couldn't get 'fingerprint' from 'key' "
- "(%s)" % name)
+ self.log.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
+ # create the final links list with all providers
all_links = []
- self.logger.debug("Joining all links found for %s in %s" %
- (operating_system, locale))
+ self.log.debug("Joining all links found for %s in %s" % (os, lc))
for key in providers.keys():
all_links.append(
"\n%s\n%s\n" % (key, ''.join(providers[key]))
@@ -277,14 +270,14 @@ class Core(object):
if all_links:
return "".join(all_links)
else:
- self.logger.warning("Trying to get supported os and locales, but"
- " no links were found")
+ self.log.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: (list) the supported operating systems.
"""
return self.supported_os.split(',')
@@ -292,10 +285,10 @@ class Core(object):
def get_supported_lc(self):
"""Public method to get the list of supported locales.
- Returns: List of strings.
+ :return: (list) the supported locales.
"""
- return self.supported_locales.split(',')
+ return self.supported_lc.split(',')
def create_links_file(self, provider, fingerprint):
"""Public method to create a links file for a provider.
@@ -304,116 +297,107 @@ class Core(object):
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.
+ :param: provider (string) the provider (links file will use this
+ name in slower case).
+ :param: fingerprint (string) the 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)
+ self.log.info("Request to create new %s" % linksfile)
if os.path.isfile(linksfile):
- # Backup the old file in case something fails
+ # backup the old file in case something fails
linksfile_backup = linksfile + '.backup'
- self.logger.info("Backing up %s to %s"
- % (linksfile, linksfile_backup))
+ self.log.info("Backing up %s (%s)" % (linksfile, linksfile_backup))
os.rename(linksfile, linksfile_backup)
try:
- # This creates an empty links file (with no links)
+ # 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')
+ for os in self.supported_os:
+ content.add_section(os)
with open(linksfile, 'w+') as f:
content.write(f)
- self.logger.info("New %s created" % linksfile)
+ self.log.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):
+ def add_link(self, provider, os, lc, 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).
+ Use ConfigParser to add a link into the os section, under the lc
+ 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:
+ :param: provider (string) the provider.
+ :param: os (string) the operating system.
+ :param: lc (string) the locale.
+ :param: link (string) link 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.
+ :raise: UnsupportedOSError if the operating system is not supported.
+ :raise: UnsupportedLocaleError if the locale is not supported.
+ :raise: LinkFileError if there is no links file for the provider.
+ :raise: LinkFormatError if the link format doesn't seem legit.
+ :raise: 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.
+
"""
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)
+ # don't try to add unsupported stuff
+ if lc not in self.supported_lc:
+ self.log.warning("Can't add link for unsupported lc: %s" % lc)
+ raise UnsupportedLocaleError("Locale %s not supported" % lc)
- 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)
+ if os not in self.supported_os:
+ self.log.warning("Can't add link for unsupported os: %s" % os)
+ raise UnsupportedOSError("OS %s not supported" % os)
- # Check if the link has a legit format
+ # 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)
+ self.log.warning("Can't add an invalid link: %s" % link)
+ raise LinkFormatError("Link '%s' doesn't seem legit" % link)
if os.path.isfile(linksfile):
content = ConfigParser.RawConfigParser()
content.readfp(open(linksfile))
- # Check if exists and entry for locale; if not, create it
+ # check if exists and entry for locale; if not, create it
try:
- links = content.get(operating_system, locale)
+ links = content.get(os, lc)
links = links + ",\n" + link
- content.set(operating_system, locale, links)
+ content.set(oc, lc, 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))
+ self.log.info("Link %s added to %s %s in %s" %
+ (link, os, lc, provider))
except ConfigParser.NoOptionError:
- content.set(operating_system, locale, link)
+ content.set(os, lc, 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))
+ self.log.info("Link %s added to %s-%s in %s" %
+ (link, os, lc, 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)
+ # this shouldn't happen, but just in case
+ self.log.error("Unknown section %s in links file")
+ raise InternalError("Unknown %s section in links file" % os)
else:
raise LinkFileError("There is no links file for %s" % provider)
1
0

[gettor/master] Added create_links_file() and add_link() methods. See coments.
by ilv@torproject.org 22 Sep '15
by ilv@torproject.org 22 Sep '15
22 Sep '15
commit 7bdf92917320e95b4f2f9ae25a519995ef292dde
Author: ilv <ilv(a)users.noreply.github.com>
Date: Fri Jun 20 20:55:48 2014 -0400
Added create_links_file() and add_link() methods. See coments.
---
src/gettor.py | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 114 insertions(+), 7 deletions(-)
diff --git a/src/gettor.py b/src/gettor.py
index 40de034..3187fa1 100644
--- a/src/gettor.py
+++ b/src/gettor.py
@@ -2,6 +2,7 @@ import os
import re
import inspect
import logging
+import tempfile
import ConfigParser
"""
@@ -17,6 +18,11 @@ import ConfigParser
Core.get_links(): Get the links. It throws ValueError and
RuntimeError on failure.
+
+ Core.create_links_file(): Create a file to store links of a given
+ provider.
+
+ Core.add_link(): Add a link to a links file of a given provider.
Exceptions:
ValueError: Request for an unsupported locale/operating system.
@@ -70,7 +76,7 @@ class Core:
Raises a RuntimeError if the configuration file doesn't exists
or if something goes wrong while reading options from it.
- Parameters:
+ Arguments:
config_file: path for the configuration file
"""
@@ -142,19 +148,19 @@ class Core:
info_log = logging.FileHandler(os.path.join(self.logdir, 'info.log'),
mode='a+')
info_log.setLevel('INFO')
- debug_log.addFilter(SingleLevelFilter(logging.INFO, False))
+ 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')
- debug_log.addFilter(SingleLevelFilter(logging.WARNING, False))
+ 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')
- debug_log.addFilter(SingleLevelFilter(logging.ERROR, False))
+ error_log.addFilter(SingleLevelFilter(logging.ERROR, False))
error_log.setFormatter(formatter)
logger.addHandler(all_log)
@@ -241,11 +247,10 @@ class Core:
providers = {}
self.logger.info("Reading links from providers directory")
- # We trust links have been generated properly
- config = ConfigParser.ConfigParser()
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:
@@ -288,7 +293,7 @@ class Core:
ConfigParser. It catches possible exceptions and raises
RuntimeError if something goes wrong.
- Parameters:
+ Arguments:
config: ConfigParser object
section: section inside config
option: option inside section
@@ -310,3 +315,105 @@ class Core:
# No other errors should occurr, unless something's terribly wrong
except ConfigParser.Error as e:
raise RuntimeError("Unexpected error: %s" % str(e))
+
+ def create_links_file(self, provider):
+ """
+ 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. The name for the links file
+ is the provider's name in lowercase. It raises a general
+ exception if something goes wrong while creating the new file.
+
+ Arguments:
+ provider: Provider's name. The links file will use this
+ name in lower case.
+ """
+ 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('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)
+
+ def add_link(self, provider, operating_system, locale, link):
+ """
+ Public method to add a link to a provider's links file.
+
+ It uses ConfigParser to add a link into the operating_system
+ section, under the locale option. It check for valid format;
+ the provider's script should use the right format (see design).
+ It raises ValueError in case the operating_system or locale
+ are not supported (see config file for supported ones), or if
+ if there is not a section for the operating_system in the
+ links file, or if there is no links file for the given provider.
+ """
+ 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 ValueError("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 ValueError("Operating system %s not supported at the moment"
+ % operating_system)
+
+ # Check if seems legit format
+ # e.g. https://foo.bar https://foo.bar.asc 111-222-333-444
+ #p = re.compile('^https://\.+\shttps://\.+\s\.+$')
+
+ #if p.match(link):
+ # self.logger.warning("Trying to add an invalid link: %s"
+ # % link)
+ # raise ValueError("The 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 ValueError("Unknown %s section in links file"
+ % operating_system)
+ else:
+ raise ValueError("There is no links file for %s" % provider)
1
0

[gettor/master] Enhanced logging. Deleted private method for logging (one call only). Added private methods for getting configuration options and filtering logging levels
by ilv@torproject.org 22 Sep '15
by ilv@torproject.org 22 Sep '15
22 Sep '15
commit 1aa6d5cd8260cc5a5e11eca371ed8d21ed0cf7ee
Author: ilv <ilv(a)users.noreply.github.com>
Date: Fri Jun 13 23:43:05 2014 -0400
Enhanced logging. Deleted private method for logging (one call only). Added private methods for getting configuration options and filtering logging levels
---
src/gettor.py | 232 ++++++++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 180 insertions(+), 52 deletions(-)
diff --git a/src/gettor.py b/src/gettor.py
index 2157898..40de034 100644
--- a/src/gettor.py
+++ b/src/gettor.py
@@ -8,10 +8,14 @@ import ConfigParser
GetTor main module.
Classes:
+ SingleLevelFilter: Filter logging levels.
Core: Get links from providers.
Methods:
- Core.get_links(): Get the links. It throws ValueError and
+ SingleLevelFilter.filter(): Filter logging levels. All except
+ the one specified will be filtered.
+
+ Core.get_links(): Get the links. It throws ValueError and
RuntimeError on failure.
Exceptions:
@@ -20,6 +24,36 @@ import ConfigParser
"""
+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 Core:
"""
@@ -31,36 +65,110 @@ class Core:
def __init__(self, config_file):
"""
- Initialize a Core object by reading a configuration file.
+ Initialize a new object by reading a configuration file.
- Raises a RuntimeError if the configuration file doesn't exists.
+ Raises a RuntimeError if the configuration file doesn't exists
+ or if something goes wrong while reading options from it.
- Parameters: none
+ Parameters:
+ config_file: path for the configuration file
"""
- logging.basicConfig()
+ logging.basicConfig(format='[%(levelname)s] %(asctime)s - %(message)s',
+ datefmt="%Y-%m-%d %H:%M:%S")
logger = logging.getLogger(__name__)
-
config = ConfigParser.ConfigParser()
if os.path.isfile(config_file):
- logger.debug("Reading configuration from %s" % config_file)
+ logger.info("Reading configuration from %s" % config_file)
config.read(config_file)
else:
logger.error("Error while trying to read %s" % config_file)
raise RuntimeError("Couldn't read the configuration file %s"
% config_file)
- # To do: check for unspecified sections and/or options
- self.basedir = config.get('general', 'basedir')
- self.linksdir = config.get('links', 'linksdir')
- self.supported_locales = config.get('links', 'locales').split(', ')
- self.supported_os = config.get('links', 'os').split(', ')
- self.loglevel = config.get('log', 'loglevel')
- self.logdir = config.get('log', 'logdir')
+ # Handle the gets internally to catch proper exceptions
+ try:
+ self.basedir = self._get_config_option('general',
+ 'basedir', config)
+ except RuntimeError as e:
+ logger.warning("%s misconfigured. %s" % (config_file, str(e)))
+ try:
+ self.linksdir = self._get_config_option('links', 'dir', config)
+ self.linksdir = os.path.join(self.basedir, self.linksdir)
+ except RuntimeError as e:
+ logger.warning("%s misconfigured. %s" % (config_file, str(e)))
+
+ try:
+ self.supported_locales = self._get_config_option('links',
+ 'locales',
+ config)
+ except RuntimeError as e:
+ logger.warning("%s misconfigured. %s" % (config_file, str(e)))
+
+ try:
+ self.supported_os = self._get_config_option('links', 'os', config)
+ except RuntimeError as e:
+ logger.warning("%s misconfigured. %s" % (config_file, str(e)))
+
+ try:
+ self.loglevel = self._get_config_option('log', 'level', config)
+ except RuntimeError as e:
+ logger.warning("%s misconfigured. %s" % (config_file, str(e)))
+
+ try:
+ self.logdir = self._get_config_option('log', 'dir', config)
+ self.logdir = os.path.join(self.basedir, self.logdir)
+ except RuntimeError as e:
+ logger.warning("%s misconfigured. %s" % (config_file, str(e)))
+
+ # 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')
+ debug_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')
+ debug_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')
+ debug_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.info('Redirecting logging to %s' % self.logdir)
+ # Stop logging on stdout from now on
+ logger.propagate = False
self.logger.debug("New core object created")
def get_links(self, operating_system, locale):
@@ -75,7 +183,11 @@ class Core:
(e.g. SMTP).
"""
- self._log_request(operating_system, locale)
+ # Figure out which module called us and what was asking for
+ caller = inspect.stack()[1]
+ module = inspect.getmodule(caller[0])
+ self.logger.info("%s did a request for %s, %s." %
+ (str(module), operating_system, locale))
if locale not in self.supported_locales:
self.logger.warning("Request for unsupported locale: %s" % locale)
@@ -92,7 +204,8 @@ class Core:
if links is None:
self.logger.error("Couldn't get the links", exc_info=True)
- raise RuntimeError("Something went wrong with GetTor's core")
+ raise RuntimeError("Something went wrong internally. See logs for \
+ detailed info.")
self.logger.info("Returning the links")
return links
@@ -110,37 +223,15 @@ class Core:
locale: string describing the locale
"""
- # We read the links files from the 'linksdir' inside 'basedir'
- #
- # Each .links file uses the ConfigParser's format.
- # There should be a section [provider] with the option 'name' for
- # the provider's name (e.g. Dropbox)
- #
- # Following sections should specify the operating system and its
- # options should be the locale. When more than one link is available
- # per operating system and locale (always) the links should be
- # specified as a multiline value. Each link has the format:
- #
- # link link_signature key_fingerprint
- #
- # e.g.
- #
- # [provider]
- # name: Dropbox
- #
- # [linux]
- # en: https://foo.bar https://foo.bar.asc 111-222-333-444,
- # https://foo.bar https://foo.bar.asc 555-666-777-888
- #
- # es: https://bar.baz https://bar.baz.asc 555-666-777-888
- #
+ # 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.basedir):
- path = os.path.abspath(os.path.join(self.basedir, name))
+ 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)
@@ -153,13 +244,30 @@ class Core:
# We trust links have been generated properly
config = ConfigParser.ConfigParser()
for name in links:
+ self.logger.debug("-- Reading %s" % name)
+ # We're reading files listed on linksdir, so they must exist!
config.read(name)
- provider_name = config.get('provider', 'name')
- providers[provider_name] = config.get(operating_system, locale)
+
+ try:
+ pname = self._get_config_option('provider',
+ 'name', config)
+ except RuntimeError as e:
+ self.logger.warning("Links misconfiguration %s" % str(e))
+
+ self.logger.debug("-- Checking if %s has links for %s in %s" %
+ (pname, operating_system, locale))
+
+ try:
+ providers[pname] = self._get_config_option(operating_system,
+ locale, config)
+ except RuntimeError as e:
+ self.logger.warning("Links misconfiguration %s" % str(e))
# 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]))
@@ -168,17 +276,37 @@ class Core:
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 _log_request(self, operating_system, locale):
+ def _get_config_option(self, section, option, config):
"""
- Private method to log what service module called to get the links.
+ Private method to get configuration options.
- Parameters: none
- """
+ It tries to obtain a value from a section in config using
+ ConfigParser. It catches possible exceptions and raises
+ RuntimeError if something goes wrong.
- caller = inspect.stack()[2]
- module = inspect.getmodule(caller[0])
+ Parameters:
+ config: ConfigParser object
+ section: section inside config
+ option: option inside section
- self.logger.info("\n%s did a request for %s, %s\n" %
- (str(module), operating_system, locale))
+ Returns the value of the option inside the section in the
+ config object.
+ """
+
+ try:
+ value = config.get(section, option)
+ return value
+ # This exceptions should appear when messing with the configuration
+ except (ConfigParser.NoSectionError,
+ ConfigParser.NoOptionError,
+ ConfigParser.InterpolationError,
+ ConfigParser.MissingSectionHeaderError,
+ ConfigParser.ParsingError) as e:
+ raise RuntimeError("%s" % str(e))
+ # No other errors should occurr, unless something's terribly wrong
+ except ConfigParser.Error as e:
+ raise RuntimeError("Unexpected error: %s" % str(e))
1
0
commit 36ec85a8d8ba6991bb79c8478bab4ecd8be2fd2b
Author: ilv <ilv(a)users.noreply.github.com>
Date: Fri Jun 13 23:42:03 2014 -0400
Changed some redundant option names
---
src/gettor.cfg | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/gettor.cfg b/src/gettor.cfg
index e56cf3a..3492681 100644
--- a/src/gettor.cfg
+++ b/src/gettor.cfg
@@ -1,11 +1,11 @@
[general]
-basedir: ./providers/
+basedir: ./
[links]
-linksdir: providers/
+dir: providers/
os: linux, windows, osx
locales: es, en
[log]
-logdir = log/
-loglevel = INFO
+dir: log/
+level: DEBUG
1
0

[gettor/master] Changed log format in 'Design'. Added section 'Features'. Deleted stuff from 'Discussion' and added some others.
by ilv@torproject.org 22 Sep '15
by ilv@torproject.org 22 Sep '15
22 Sep '15
commit 623b5bf370a97d823127242464f66750ac1a97ca
Author: ilv <ilv(a)users.noreply.github.com>
Date: Fri Jun 20 20:54:28 2014 -0400
Changed log format in 'Design'. Added section 'Features'. Deleted stuff from 'Discussion' and added some others.
---
spec/design/core.txt | 47 +++++++++++++++++++++--------------------------
1 file changed, 21 insertions(+), 26 deletions(-)
diff --git a/spec/design/core.txt b/spec/design/core.txt
index cc7b05d..4c180d6 100644
--- a/spec/design/core.txt
+++ b/spec/design/core.txt
@@ -1,8 +1,12 @@
Google Summer of Code 2014 GetTor Revamp - Core module
Author: Israel Leiva - <israel.leiva(a)usach.cl, ilv(a)riseup.net>
- Last update: 2014-06-06
- Version: 0.03
- Changes: [0.03]
+ Last update: 2014-06-20
+ Version: 0.04
+ Changes: [0.04]
+ Changed log format in 'Design'
+ Added section 'Features'
+ Deleted stuff from 'Discussion' and added some others
+ [0.03]
Changed proposed format for link files to RFC 882 (ConfigParser).
Read configuration from file with ConfigParser.
[0.02]
@@ -50,7 +54,11 @@
* logs/: Directory for logs. Should be specified on gettor.cfg
- ----- core_yyyy-mm-dd.log: daily log of requests.
+ ----- all.log
+ ----- info.log
+ ----- debug.log
+ ----- warn.log
+ ----- error.log
* Core module of GetTor.
@@ -125,30 +133,17 @@
f. get_links() returns the message previously constructed.
g. The SMTP service creates a message with the links obtained and
send it to the user.
-
+
5. Discussion
-
-5.1 On demand generation
- Now we consider links generation over a period of time, say, once a week.
- Generating links on demand don't seem to be necessary and it would
- certainly be resource consuming. The core module doesn't care about
- how links are generated, it just assumes they exist. Is there any case
- where on demand link generation should be considered?
+5.1 Daily logs
-5.2 Core class
-
- Should the Core be a module on its own, or a class of GetTor?
- Currently it's just a class, after realizing that
-
- core = gettor.core.core()
-
- Was too much. It's better to do
-
- core = gettor.Core()
-
- Isn't?
+ Currently, logs are separated by level of information (debug, info, error).
+ Is it necessary to do this by days/weeks too?
+
+6. Features
-5.3 Logs
+ Possible features to be added in the future (open to discussion)
+
+ a. Send HTTP links (currently some official mirrors are HTTP only).
- Should we mantain separate logs for successful and fail requests?
1
0

22 Sep '15
commit eb288d2801aa225d6207a44c08595a84faddfd11
Author: ilv <ilv(a)users.noreply.github.com>
Date: Fri Jun 13 23:43:54 2014 -0400
Enhanced logging structure (separated log files)
---
src/log/all.log | 37 +++++++++++++++++++++++++++++++++++++
src/log/debug.log | 1 +
src/log/error.log | 1 +
src/log/info.log | 13 +++++++++++++
src/log/warn.log | 1 +
5 files changed, 53 insertions(+)
diff --git a/src/log/all.log b/src/log/all.log
new file mode 100644
index 0000000..d961a72
--- /dev/null
+++ b/src/log/all.log
@@ -0,0 +1,37 @@
+
+[ INFO] 2014-06-13 23:36:47 - Redirecting logging to ./log/
+[ DEBUG] 2014-06-13 23:36:47 - New core object created
+[ INFO] 2014-06-13 23:36:47 - <module '__main__' from 'core_demo.py'> did a request for linux, en.
+[ INFO] 2014-06-13 23:36:47 - Reading links from providers directory
+[ DEBUG] 2014-06-13 23:36:47 - -- Reading /home/ilv/Proyectos/github/gettor/src/providers/mirrors.links
+[ DEBUG] 2014-06-13 23:36:47 - -- Checking if Official mirrors has links for linux in en
+[ DEBUG] 2014-06-13 23:36:47 - -- Reading /home/ilv/Proyectos/github/gettor/src/providers/dropbox.links
+[ DEBUG] 2014-06-13 23:36:47 - -- Checking if Dropbox has links for linux in en
+[ DEBUG] 2014-06-13 23:36:47 - -- Reading /home/ilv/Proyectos/github/gettor/src/providers/gdrive.links
+[ DEBUG] 2014-06-13 23:36:47 - -- Checking if Google Drive has links for linux in en
+[ DEBUG] 2014-06-13 23:36:47 - Joining all links found for linux in en
+[ INFO] 2014-06-13 23:36:47 - Returning the links
+[ INFO] 2014-06-13 23:38:27 - Redirecting logging to ./log/
+[ DEBUG] 2014-06-13 23:38:27 - New core object created
+[ INFO] 2014-06-13 23:38:27 - <module '__main__' from 'core_demo.py'> did a request for linux, en.
+[ INFO] 2014-06-13 23:38:27 - Reading links from providers directory
+[ DEBUG] 2014-06-13 23:38:27 - -- Reading /home/ilv/Proyectos/github/gettor/src/providers/mirrors.links
+[ DEBUG] 2014-06-13 23:38:27 - -- Checking if Official mirrors has links for linux in en
+[ DEBUG] 2014-06-13 23:38:27 - -- Reading /home/ilv/Proyectos/github/gettor/src/providers/dropbox.links
+[ DEBUG] 2014-06-13 23:38:27 - -- Checking if Dropbox has links for linux in en
+[ DEBUG] 2014-06-13 23:38:27 - -- Reading /home/ilv/Proyectos/github/gettor/src/providers/gdrive.links
+[ DEBUG] 2014-06-13 23:38:27 - -- Checking if Google Drive has links for linux in en
+[ DEBUG] 2014-06-13 23:38:27 - Joining all links found for linux in en
+[ INFO] 2014-06-13 23:38:27 - Returning the links
+[ INFO] 2014-06-13 23:39:20 - Redirecting logging to ./log/
+[ DEBUG] 2014-06-13 23:39:20 - New core object created
+[ INFO] 2014-06-13 23:39:20 - <module '__main__' from 'core_demo.py'> did a request for linux, en.
+[ INFO] 2014-06-13 23:39:20 - Reading links from providers directory
+[ DEBUG] 2014-06-13 23:39:20 - -- Reading /home/ilv/Proyectos/github/gettor/src/providers/mirrors.links
+[ DEBUG] 2014-06-13 23:39:20 - -- Checking if Official mirrors has links for linux in en
+[ DEBUG] 2014-06-13 23:39:20 - -- Reading /home/ilv/Proyectos/github/gettor/src/providers/dropbox.links
+[ DEBUG] 2014-06-13 23:39:20 - -- Checking if Dropbox has links for linux in en
+[ DEBUG] 2014-06-13 23:39:20 - -- Reading /home/ilv/Proyectos/github/gettor/src/providers/gdrive.links
+[ DEBUG] 2014-06-13 23:39:20 - -- Checking if Google Drive has links for linux in en
+[ DEBUG] 2014-06-13 23:39:20 - Joining all links found for linux in en
+[ INFO] 2014-06-13 23:39:20 - Returning the links
diff --git a/src/log/debug.log b/src/log/debug.log
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/log/debug.log
@@ -0,0 +1 @@
+
diff --git a/src/log/error.log b/src/log/error.log
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/log/error.log
@@ -0,0 +1 @@
+
diff --git a/src/log/info.log b/src/log/info.log
new file mode 100644
index 0000000..d767f9d
--- /dev/null
+++ b/src/log/info.log
@@ -0,0 +1,13 @@
+
+[ INFO] 2014-06-13 23:36:47 - Redirecting logging to ./log/
+[ INFO] 2014-06-13 23:36:47 - <module '__main__' from 'core_demo.py'> did a request for linux, en.
+[ INFO] 2014-06-13 23:36:47 - Reading links from providers directory
+[ INFO] 2014-06-13 23:36:47 - Returning the links
+[ INFO] 2014-06-13 23:38:27 - Redirecting logging to ./log/
+[ INFO] 2014-06-13 23:38:27 - <module '__main__' from 'core_demo.py'> did a request for linux, en.
+[ INFO] 2014-06-13 23:38:27 - Reading links from providers directory
+[ INFO] 2014-06-13 23:38:27 - Returning the links
+[ INFO] 2014-06-13 23:39:20 - Redirecting logging to ./log/
+[ INFO] 2014-06-13 23:39:20 - <module '__main__' from 'core_demo.py'> did a request for linux, en.
+[ INFO] 2014-06-13 23:39:20 - Reading links from providers directory
+[ INFO] 2014-06-13 23:39:20 - Returning the links
diff --git a/src/log/warn.log b/src/log/warn.log
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/log/warn.log
@@ -0,0 +1 @@
+
1
0
commit 68e97f6fd0fe724b48db4996ed8d701eaa0b72ec
Author: ilv <ilv(a)users.noreply.github.com>
Date: Fri Jun 20 20:58:01 2014 -0400
Links file generation tests
---
src/create_links_demo.py | 19 +++++++++++++++++++
src/providers/github.links | 11 +++++++++++
2 files changed, 30 insertions(+)
diff --git a/src/create_links_demo.py b/src/create_links_demo.py
new file mode 100644
index 0000000..7e571cf
--- /dev/null
+++ b/src/create_links_demo.py
@@ -0,0 +1,19 @@
+#!/usr/bin/python
+#
+# Dummy script to test GetTore's Core module progress
+#
+
+import gettor
+
+try:
+ core = gettor.Core('gettor.cfg')
+ core.create_links_file('Github')
+ core.add_link('Github', 'linux', 'es',
+ 'https://foo.bar https://foo.bar.asc 111-222-333-444')
+ core.add_link('Github', 'linux', 'es',
+ 'https://foo.bar https://foo.bar.asc 555-666-777-888')
+
+except ValueError as e:
+ print "Value error: " + str(e)
+except RuntimeError as e:
+ print "Internal error: " + str(e)
diff --git a/src/providers/github.links b/src/providers/github.links
new file mode 100644
index 0000000..6079df2
--- /dev/null
+++ b/src/providers/github.links
@@ -0,0 +1,11 @@
+[provider]
+name = Github
+
+[linux]
+es = https://foo.bar https://foo.bar.asc 111-222-333-444,
+ https://foo.bar https://foo.bar.asc 555-666-777-888
+
+[windows]
+
+[osx]
+
1
0