[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[or-cvs] r18034: {projects} Add setup script Move files around Look cleaner (in projects/gettor: . gettor lib)
Author: kaner
Date: 2009-01-08 21:58:32 -0500 (Thu, 08 Jan 2009)
New Revision: 18034
Added:
projects/gettor/GetTor.py
projects/gettor/gettor/
projects/gettor/gettor/__init__.py
projects/gettor/gettor/blacklist.py
projects/gettor/gettor/config.py
projects/gettor/gettor/gtlog.py
projects/gettor/gettor/opt.py
projects/gettor/gettor/packages.py
projects/gettor/gettor/requests.py
projects/gettor/gettor/responses.py
projects/gettor/lib/
projects/gettor/setup.py
Removed:
projects/gettor/gettor.py
projects/gettor/gettor_blacklist.py
projects/gettor/gettor_config.py
projects/gettor/gettor_log.py
projects/gettor/gettor_opt.py
projects/gettor/gettor_packages.py
projects/gettor/gettor_requests.py
projects/gettor/gettor_responses.py
Modified:
projects/gettor/README
Log:
Add setup script
Move files around
Look cleaner
Added: projects/gettor/GetTor.py
===================================================================
--- projects/gettor/GetTor.py (rev 0)
+++ projects/gettor/GetTor.py 2009-01-09 02:58:32 UTC (rev 18034)
@@ -0,0 +1,352 @@
+#!/usr/bin/python2.5
+# -*- coding: utf-8 -*-
+"""
+
+ gettor.py by Jacob Appelbaum <jacob@xxxxxxxxxxxxx>,
+ Christian Fromme <kaner@xxxxxxxxxx>
+ This program will hand out Tor via email to supported systems.
+ This program is Free Software, see LICENSE for details.
+
+ It is intended to be used in a .forward file as part of a pipe like so:
+
+ cat <<'EOF'> .forward
+ |/usr/local/bin/gettor.py
+ EOF
+
+ You should have a dist/current/ mirror in a directory that gettor can read.
+ Such a mirror can be created like so:
+
+ cd /usr/local/
+ rsync -av rsync://rsync.torproject.org/tor/dist/current tor-dist-current/
+
+ You can keep it updated with a cronjob like so:
+
+ MirrorDir=/usr/local/tor-dist-current/
+ 0 3 * * * rsync -a rsync://rsync.torproject.org/tor/dist/current/ $MirrorDir
+
+ You should ensure that for each file and signature pair you wish to
+ distribute, you have created a zip file containing both.
+
+ While this program isn't written in a threaded manner per se, it is designed to function
+ as if it will be called as a pipe many times at once. There is a slight
+ desynchronization with blacklist entry checking and may result in false
+ negatives. This isn't perfect but it is designed to be lightweight. It could
+ be fixed easily with a shared locking system but this isn't implemented yet.
+
+ To clean out the blacklist on a daily basis, install the following cronjob:
+
+ # m h dom mon dow command
+ 1 1 * * * /bin/rm -rf /var/lib/gettor/bl/*
+
+ You'll probably want a directory structure like this owned by uid/gid 'gettor':
+ /var/lib/gettor/{bl,pkg}
+
+"""
+
+__program__ = 'gettor.py'
+__version__ = '20080914.01'
+__url__ = 'https://tor-svn.freehaven.net/svn/tor/trunk/contrib/gettor/'
+__author__ = 'Jacob Appelbaum <jacob@xxxxxxxxxxxxx>, Christian Fromme <kaner@xxxxxxxxxx>'
+__copyright__ = 'Copyright (c) 2008, Jacob Appelbaum, Christian Fromme'
+__license__ = 'See LICENSE for licensing information'
+
+try:
+ from future import antigravity
+except ImportError:
+ antigravity = None
+
+import sys
+import os
+import subprocess
+import gettext
+import gettor.blacklist
+import gettor.requests
+import gettor.responses
+import gettor.gtlog
+import gettor.config
+import gettor.opt
+import gettor.packages
+
+# Global logger
+log = None
+
+# Switch language to 'newlocale'. Return default if language is not supported.
+def switchLocale(newlocale, localedir):
+ trans = gettext.translation("gettor",
+ localedir,
+ [newlocale],
+ fallback=True)
+ trans.install()
+
+def runTests():
+ # XXX: Implement me
+ return True
+
+def installMo(poFile, targetDir):
+ global log
+ args = os.getcwd() + "/" + poFile + " -o " + targetDir + "/gettor.mo"
+ try:
+ ret = subprocess.call("msgfmt" + " " + args, shell=True)
+ if ret < 0:
+ log.error("Error in msgfmt execution: %s" % ret)
+ return False
+ except OSError, e:
+ log.erro("Comilation failed: " % e)
+ return False
+ return True
+
+def installTrans(config, localeSrcdir):
+ global log
+ hasDirs = None
+
+ if config is None:
+ log.error("Bad arg.")
+ return False
+ if not os.path.isdir(localeSrcdir):
+ log.error("Not a directory: " % localeSrcdir)
+ return False
+ localeDir = config.getLocaleDir()
+ if not os.path.isdir(localeDir):
+ log.error("Sorry, %s is not a directory." % distDir)
+ return False
+
+ for root, dirs, files in os.walk(localeSrcdir):
+ # Python lacks 'depth' featue for os.walk()
+ if root != localeSrcdir:
+ continue
+ for dir in dirs:
+ hasDirs = True
+ if dir.startswith("."):
+ continue
+ try:
+ poFile = os.path.join(root, dir) + "/gettor_" + dir + ".po"
+ # Construct target dir
+ targetDir = localeDir + "/" + dir + "/LC_MESSAGES"
+ if os.path.isdir(targetDir):
+ if installMo(poFile, targetDir) == False:
+ log.error("Installing .mo files failed.")
+ return False
+ else:
+ log.error("Not a directory: " % targetDir)
+ return False
+ except Exception, e:
+ log.error("Error accessing translation files: " % e)
+ return False
+ if hasDirs is None:
+ log.errpr("Empty locale dir: " % localeSrcdir)
+ return False
+
+ return True
+
+def installCron():
+ # XXX: Check if cron is installed and understands our syntax?
+ currentCronTab = getCurrentCrontab()
+ gettorPath = os.getcwd() + "/" + os.path.basename(sys.argv[0])
+ gettorArgs = " --clear-blacklist --fetch-packages --prep-packages"
+ newCronTab = currentCronTab + '\n' + '3 2 * * * ' + gettorPath + gettorArgs
+ echoCmd = ['echo', newCronTab ]
+ cronCmd = ['crontab', '-']
+ echoProc = subprocess.Popen(echoCmd, stdout=subprocess.PIPE)
+ cronProc = subprocess.Popen(cronCmd, stdin=echoProc.stdout)
+ cronProc.communicate()[0]
+ return cronProc.returncode
+
+def getCurrentCrontab():
+ # This returns our current crontab
+ savedTab = "# This crontab has been tampered with by gettor.py\n"
+ currentTab = os.popen("crontab -l")
+ for line in currentTab:
+ savedTab += line
+ return savedTab
+
+def processMail(conf, logLang, packageList, blackList, whiteList):
+ global log
+
+ if packageList is None or len(packageList) < 1:
+ log.error(_("Sorry, your package list is unusable."))
+ log.error(_("Try running with --fetch-packages --prep-packages."))
+ return False
+
+ # Receive mail from stdin
+ rmail = gettor.requests.requestMail(packageList)
+ rawMessage = rmail.getRawMessage()
+ if not rawMessage:
+ log.error(_("No raw message. Something went wrong."))
+ return False
+ parsedMessage = rmail.getParsedMessage()
+ if not parsedMessage:
+ log.error(_("No parsed message. Dropping message."))
+ return False
+ replyTo = rmail.getReplyTo()
+ if not replyTo:
+ log.error(_("No help dispatched. Invalid reply address for user."))
+ return False
+ replyLang = rmail.getLocale()
+ if not replyLang:
+ replyLang = logLang
+
+ # Initialize response
+ srcEmail = conf.getSrcEmail()
+ # Bail out if someone tries to be funny
+ if (srcEmail == replyTo):
+ log.error(_("Won't send myself emails."))
+ return False
+
+ resp = gettor.responses.gettorResponse(conf, replyLang, logLang)
+ signature = rmail.hasVerifiedSignature()
+ log.info(_("Signature is: %s") % str(signature))
+ # Addresses from whitelist can pass without DKIM signature
+ if not signature and not whiteList.lookupListEntry(replyTo):
+ # Check to see if we've helped them to understand that they need DKIM
+ # in the past
+ previouslyHelped = blackList.lookupListEntry(replyTo)
+ if previouslyHelped:
+ log.info(_("Unsigned messaged to gettor by blacklisted user dropped."))
+ return False
+ else:
+ # Reply with some help and bail out
+ blackList.createListEntry(replyTo)
+ resp.sendHelp(srcEmail, replyTo)
+ log.info(_("Unsigned messaged to gettor. We issued some help."))
+ return True
+ else:
+ log.info(_("Good message to gettor."))
+ package = rmail.getPackage()
+ if package != None:
+ log.info(_("Package: %s selected.") % str(package))
+ resp.sendPackage(srcEmail, replyTo, packageList[package])
+ else:
+ resp.sendPackageHelp(packageList, srcEmail, replyTo)
+ log.info(_("We issued some help about proper email formatting."))
+
+ return True
+
+def main():
+ global log
+ success = None
+
+ # Parse command line, setup config, logging and language
+ options, arguments = gettor.opt.parseOpts()
+ conf = gettor.config.gettorConf(options.configfile)
+ gettor.gtlog.initialize()
+ log = gettor.gtlog.getLogger()
+
+ # Setup locale
+ logLang = conf.getLocale()
+ localeDir = conf.getLocaleDir()
+ # We need to do this first
+ if options.insttrans:
+ if installTrans(conf, options.i18ndir):
+ log.info("Installing translation files done.")
+ success = True
+ else:
+ log.error("Installing translation files failed.")
+ return False
+ # Just check for the english .mo file, because that's the fallback
+ englishMoFile = localeDir + "/en/LC_MESSAGES/gettor.mo"
+ if not os.path.isfile(englishMoFile):
+ log.error("Sorry, %s is not a file we can access." % englishMoFile)
+ return False
+ switchLocale(logLang, localeDir)
+
+ distDir = conf.getDistDir()
+ if not os.path.isdir(distDir):
+ log.error(_("Sorry, %s is not a directory.") % distDir)
+ return False
+ try:
+ packs = gettor.packages.gettorPackages(options.mirror, conf)
+ except IOError:
+ log.error(_("Error initiating package list."))
+ return False
+ try:
+ whiteList = gettor.blacklist.BWList(conf.getWlStateDir())
+ blackList = gettor.blacklist.BWList(conf.getBlStateDir())
+ except IOError, e:
+ log.error(_("White/Black list error: %s") % e)
+ return False
+
+ if options.fetchpackages:
+ if packs.syncWithMirror() != 0:
+ log.error(_("Syncing Tor packages failed."))
+ return False
+ else:
+ log.info(_("Syncing Tor packages done."))
+ success = True
+ if options.preppackages:
+ if not packs.buildPackages():
+ log.error(_("Building packages failed."))
+ return False
+ else:
+ log.info(_("Building packages done."))
+ success = True
+ if options.runtests:
+ if not runTests():
+ log.error(_("Tests failed."))
+ return False
+ else:
+ log.info(_("Tests passed."))
+ success = True
+ if options.installcron:
+ if installCron() != 0:
+ log.error(_("Installing cron failed"))
+ return False
+ else:
+ log.info(_("Installing cron done."))
+ success = True
+ if options.whitelist:
+ if not whiteList.createListEntry(options.whitelist):
+ log.error(_("Creating whitelist entry failed."))
+ return False
+ else:
+ log.info(_("Creating whitelist entry ok."))
+ success = True
+ if options.blacklist:
+ if not blackList.createListEntry(options.blacklist):
+ log.error(_("Creating blacklist entry failed."))
+ return False
+ else:
+ log.info(_("Creating blacklist entry ok."))
+ success = True
+ if options.lookup:
+ if whiteList.lookupListEntry(options.lookup):
+ log.info(_("Present in whitelist."))
+ success = True
+ if blackList.lookupListEntry(options.lookup):
+ log.info(_("Present in blacklist."))
+ success = True
+ if not success:
+ log.info(_("Address neither in blacklist or whitelist."))
+ success = True
+ if options.clearwl:
+ if not whiteList.removeAll():
+ log.error(_("Deleting whitelist failed."))
+ return False
+ else:
+ log.info(_("Deleting whitelist done."))
+ success = True
+ if options.clearbl:
+ if not blackList.removeAll():
+ log.error(_("Deleting blacklist failed."))
+ return False
+ else:
+ log.info(_("Deleting blacklist done."))
+ success = True
+
+ # Break here if preparation work has been done
+ if success is not None:
+ return success
+
+ # Main loop
+ if not processMail(conf, logLang, packs.getPackageList(), blackList,
+ whiteList):
+ log.error(_("Processing mail failed."))
+ return False
+
+ return True
+
+if __name__ == "__main__":
+ if not main():
+ print >> sys.stderr, "Main loop exited with errors."
+ exit(1)
+ else:
+ exit(0)
Property changes on: projects/gettor/GetTor.py
___________________________________________________________________
Name: svn:mergeinfo
+
Modified: projects/gettor/README
===================================================================
--- projects/gettor/README 2009-01-09 01:21:27 UTC (rev 18033)
+++ projects/gettor/README 2009-01-09 02:58:32 UTC (rev 18034)
@@ -1,12 +1,12 @@
OVERVIEW
--------
-gettor is a program for serving Tor and Tor related files over SMTP.
-Users interface with gettor by sending it an email. The users must use a mail
+GetTor is a program for serving Tor and Tor related files over SMTP.
+Users interface with GetTor by sending it an email. The users must use a mail
server that signs outgoing mail with DKIM or have their email address added
to the whitelist. This is to prevent abuse. The following document explains
-how to setup gettor for a server admin.
+how to setup GetTor for a server admin.
-To use gettor, you'll want a machine that invokes .forward files for users.
+To use GetTor, you'll want a machine that invokes .forward files for users.
You'll also want to have python and rsync installed.
There some limits with smtp software for outgoing email sizes. You should
@@ -23,9 +23,9 @@
You will want to ensure that you have a properly configured set
of mo files for each translation. Generate and install mo files as follows:
- gettor.py -r
+ GetTor.py -r
-As an example, for the user 'gettor' we will setup gettor.py like so:
+As an example, for the user 'gettor' we will setup GetTor.py like so:
Configure the default ~/.gettorrc file:
@@ -44,9 +44,9 @@
Your .forward file should look like this:
gettor@moria:~$ cat .forward
- "|/home/gettor/bin/gettor.py"
+ "|/home/gettor/bin/GetTor.py"
-These are the python files we need to run gettor.py:
+These are the python files we need to run GetTor.py:
gettor@moria:~/bin$ ls
dkim.py gettor_log.py gettor.py
@@ -67,41 +67,41 @@
Now you'll install the cronjob. This clears the blacklist and updates
packages daily:
- gettor@moria:~/bin$ ./gettor.py -i
+ gettor@moria:~/bin$ ./GetTor.py -i
2009-01-05 17:34:53,911 (16646) Installing cron done.
-Now gettor.py is installed, prepared and ready to serve files. Send it email!
+Now GetTor.py is installed, prepared and ready to serve files. Send it email!
TRANSLATION FILES
-----------------
-Provided there is a working locale environment, gettor will compile and setup
+Provided there is a working locale environment, GetTor will compile and setup
locale files for you as follows:
- gettor@moria:~/bin$ ./gettor.py -r
+ gettor@moria:~/bin$ ./GetTor.py -r
2009-01-08 12:18:09,041 (19287) Installing translation files done.
-You can also configure the .mo files to live in another place by telling gettor
+You can also configure the .mo files to live in another place by telling GetTor
so in the config file, for example:
localeDir = /home/gettor/gettor/i18n
-This will result in gettor expected the english .mo file in the directory
+This will result in GetTor expected the english .mo file in the directory
/home/gettor/gettor/i18n/en/LC_MESSAGES/gettor.mo
Also, in case your .po files for some reason live in a different directory
-than i18n/ (relative to gettor.py, as provided by gettor), you can tell this
-to gettor as follows:
+than i18n/ (relative to GetTor.py, as provided by GetTor), you can tell this
+to GetTor as follows:
- gettor@moria:~/bin$ ./gettor.py -r -d /path/to/my/mofiles/directory
+ gettor@moria:~/bin$ ./GetTor.py -r -d /path/to/my/mofiles/directory
-Note that gettor will expect the same directory structure as provided under
-i18n/ in the gettor source package, e.g. 'i18n/en/gettor_en.po',
+Note that GetTor will expect the same directory structure as provided under
+i18n/ in the GetTor source package, e.g. 'i18n/en/gettor_en.po',
'i18n/de/gettor_de.po' and so on.
CONFIGURATION
-------------
-A proper gettor configuration file is expected in the user's home directory
+A proper GetTor configuration file is expected in the user's home directory
and should look like this:
[global]
Property changes on: projects/gettor/gettor
___________________________________________________________________
Name: svn:mergeinfo
+
Added: projects/gettor/gettor/__init__.py
===================================================================
--- projects/gettor/gettor/__init__.py (rev 0)
+++ projects/gettor/gettor/__init__.py 2009-01-09 02:58:32 UTC (rev 18034)
@@ -0,0 +1 @@
+# :)
Added: projects/gettor/gettor/blacklist.py
===================================================================
--- projects/gettor/gettor/blacklist.py (rev 0)
+++ projects/gettor/gettor/blacklist.py 2009-01-09 02:58:32 UTC (rev 18034)
@@ -0,0 +1,115 @@
+#!/usr/bin/python2.5
+"""This library implements all of the black listing features needed for gettor.
+Basically, it offers creation, removal and lookup of email addresses stored as
+SHA1 hashes in a dedicated directory on the filesystem.
+"""
+
+import hashlib
+import os
+import re
+import gettor.config
+import gettor.gtlog
+
+log = gettor.gtlog.getLogger()
+
+conf = gettor.config.gettorConf()
+stateDir = conf.getStateDir()
+blStateDir = conf.getBlStateDir()
+
+class BWList:
+ def __init__(self, listdir):
+ self.listDir = listdir
+ if not os.path.isdir(self.listDir):
+ # XXX Change this to something more appropriate
+ raise IOError("Bad dir: %s" % self.listDir)
+
+ def lookupListEntry(self, address):
+ """Check to see if we have a list entry for the given address."""
+ emailonly = self.stripEmail(address)
+ entry = self.listDir + "/" + str(hashlib.sha1(emailonly).hexdigest())
+ try:
+ entry = os.stat(entry)
+ except OSError:
+ return False
+ return True
+
+ def createListEntry(self, address):
+ emailonly = self.stripEmail(address)
+ entry = self.listDir + "/" + str(hashlib.sha1(emailonly).hexdigest())
+ if self.lookupListEntry(address) == False:
+ try:
+ fd = open(entry, 'w')
+ fd.close
+ return True
+ except:
+ log.error(_("Creating list entry %s failed.") % entry)
+ return False
+ else:
+ # List entry already exists
+ return False
+
+ def removeListEntry(self, address):
+ emailonly = self.stripEmail(address)
+ entry = self.listDir + "/" + str(hashlib.sha1(emailonly).hexdigest())
+ if (self.lookupListEntry(address) == True):
+ try:
+ os.unlink(entry)
+ except OSError:
+ log.error(_("Could not unlink entry %s") % entry)
+ return False
+ else:
+ log.info(_("Requested removal of non-existing entry %s. Abord.")
+ % entry)
+ return False
+
+ def removeAll(self):
+ print "Removing all entries from list!"
+ for root, dirs, files in os.walk(self.listDir):
+ for file in files:
+ try:
+ rmfile = os.path.join(root, file)
+ os.remove(rmfile)
+ except:
+ log.error(_("Could not remove %s." % rmfile))
+ return False
+ return True
+
+ def stripEmail(self, address):
+ '''Strip "Bart Foobar <bart@xxxxxxxxxx>" to "<bart@xxxxxxxxxx">'''
+ match = re.search('<.*?>', address)
+ if match is not None:
+ return match.group()
+ return address
+
+def blackListtests(address):
+ """ This is a basic evaluation of our blacklist functionality """
+ bwlist = BWList("/tmp/")
+ print bwlist.createListEntry(address)
+ print bwlist.lookupListEntry(address)
+ #prepBLStateDir()
+ #privateAddress = makeAddressPrivate(address)
+ #print "We have a private address of: " + privateAddress
+ #print "Testing creation of blacklist entry: "
+ #blackListEntry = createBlackListEntry(privateAddress)
+ #print blackListEntry
+ #print "We're testing a lookup of a known positive blacklist entry: "
+ #blackListEntry = lookupBlackListEntry(privateAddress)
+ #print blackListEntry
+ #print "We're testing removal of a known blacklist entry: "
+ #blackListEntry = removeBlackListEntry(privateAddress)
+ #print blackListEntry
+ #print "We're testing a lookup of a known false blacklist entry: "
+ #blackListEntry = lookupBlackListEntry(privateAddress)
+ #print blackListEntry
+ #print "Now we'll test the higher level blacklist functionality..."
+ #print "This should not find an entry in the blacklist: "
+ #print blackList(address)
+ #print "This should add an entry to the blacklist: "
+ #print blackList(address, True)
+ #print "This should find the previous addition to the blacklist: "
+ #print blackList(address)
+ #print "Please ensure the tests match what you would expect for your environment."
+
+if __name__ == "__main__" :
+ print "Running some tests.."
+ blackListtests("requestingUser@xxxxxxxxxxx")
Added: projects/gettor/gettor/config.py
===================================================================
--- projects/gettor/gettor/config.py (rev 0)
+++ projects/gettor/gettor/config.py 2009-01-09 02:58:32 UTC (rev 18034)
@@ -0,0 +1,172 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+'''
+ gettor_config.py - Parse configuration file for gettor
+
+ Copyright (c) 2008, Jacob Appelbaum <jacob@xxxxxxxxxxxxx>,
+ Christian Fromme <kaner@xxxxxxxxxx>
+
+ This is Free Software. See LICENSE for license information.
+
+
+ We grab configurable values from the users' gettor config file. If that file
+ is not present, we will supply reasonable defaults. Config files are ini-style
+ formatted. We know this is ugly, but we prefer it to XML and also ConfigParser
+ seems handy. ;-)
+
+ A valid config file should look like this:
+
+ [global]
+ stateDir = /var/lib/gettor/
+ blStateDir = /var/lib/gettor/bl/
+ distDir = /var/lib/gettor/pkg/
+ srcEmail = gettor@xxxxxxx
+ locale = en
+ logSubSystem = nothing
+ logFile = /dev/null
+ localeDir = /usr/share/locale
+
+ Note that you can set from none to any of these values in your config file.
+ Values you dont provide will be taken from the defaults in 'useConf'.
+
+ Here is what each of them is used for individually:
+
+ blStateDir: Blacklisted (hashed) email addresses go here
+ wlStateDir: Whitelisted (hashed) email addresses go here
+ distDir: Sent-out Tor packages are found here
+ srcEmail: The email containing the Tor package will use this as 'From:'
+ locale: Choose your default mail and log locale
+ logFile: If 'file' logging is chosen, log to this file
+ logSubSystem: This has to be one of the following strings:
+ 'nothing': Nothing is logged anywhere (Recommended)
+ 'stdout': Log to stdout
+ 'syslog': Logmessages will be written to syslog
+ 'file': Logmessages will be written to a file (Not that
+ this needs the 'logFile' option in the config file
+ also set to something useful
+ localeDir: This is where the 'en/LC_MESSAGES/gettor.mo' or
+ 'whateverlang/LC_MESSAGES/gettor.mo' should go
+
+ If no valid config file is provided to __init__, gettorConf will try to use
+ '~/.gettorrc' as default config file. If that fails, the default values from
+ useConf will be used.
+
+ Run this module from the commandline to have it print a useful default config
+ like so:
+
+ $ ./gettor_config.py > ~/.gettorrc
+
+'''
+
+import os
+import sys
+import ConfigParser
+
+__all__ = ["gettorConf"]
+
+class gettorConf:
+ '''
+ Initialize gettor with default values if one or more values are missing
+ from the config file. This will return entirely default values if the
+ configuration file is missing. Our default file location is ~/.gettorrc
+ of $USER.
+ '''
+
+ def __init__(self, path = os.path.expanduser("~/.gettorrc")):
+ '''
+ Most of the work happens here. Parse config, merge with default values,
+ prepare outConf.
+ '''
+
+ self.configFile = os.path.expanduser(path)
+
+ # Variable name | Default value | Section
+ self.useConf = {"stateDir": ("/var/lib/gettor/", "global"),
+ "blStateDir": ("/var/lib/gettor/bl/", "global"),
+ "wlStateDir": ("/var/lib/gettor/wl/", "global"),
+ "srcEmail": ("gettor@xxxxxxxxxxxxxx", "global"),
+ "distDir": ("/var/lib/gettor/dist/", "global"),
+ "packDir": ("/var/lib/gettor/pkg/", "global"),
+ "locale": ("en", "global"),
+ "logSubSystem": ("nothing", "global"),
+ "logFile": ("/dev/null", "global"),
+ "localeDir": ("/usr/share/locale", "global")}
+
+ # One ConfigParser instance to read the actual values from config
+ self.config = ConfigParser.ConfigParser()
+ # And another to provide a useable default config as output. This is
+ # only because the user may have strange stuff inside his config file.
+ # We're trying to be failsafe here
+ self.outConf = ConfigParser.ConfigParser()
+
+ try:
+ if os.access(self.configFile, os.R_OK):
+ self.config.read(self.configFile)
+ except:
+ pass
+
+ # Main parser loop:
+ # * Merge default values with those from the config file, if there are
+ # any
+ # * Update values from config file into useConf
+ # * Ignore sections and values that are not found in useConf, but in
+ # the config file (wtf?)
+ # * Prepare outConf
+ for dkey, (dval, sec) in self.useConf.items():
+ if not self.outConf.has_section(sec):
+ self.outConf.add_section(sec)
+ try:
+ for key, val in self.config.items(sec):
+ # Unfortunatly, keys from the config are not case-sensitive
+ if key.lower() == dkey.lower():
+ self.useConf[dkey] = val, sec
+ self.outConf.set(sec, dkey, val)
+ break
+ else:
+ # Add default value for dkey
+ self.outConf.set(sec, dkey, dval)
+
+ except:
+ self.outConf.set(sec, dkey, dval)
+
+ def printConfiguration(self):
+ '''
+ Print out config file. This works even if there is none
+ '''
+ return self.outConf.write(sys.stdout)
+
+ # All getter routines live below
+ def getStateDir(self):
+ return self.useConf["stateDir"][0]
+
+ def getBlStateDir(self):
+ return self.useConf["blStateDir"][0]
+
+ def getWlStateDir(self):
+ return self.useConf["wlStateDir"][0]
+
+ def getSrcEmail(self):
+ return self.useConf["srcEmail"][0]
+
+ def getDistDir(self):
+ return self.useConf["distDir"][0]
+
+ def getPackDir(self):
+ return self.useConf["packDir"][0]
+
+ def getLocale(self):
+ return self.useConf["locale"][0]
+
+ def getLogSubSystem(self):
+ return self.useConf["logSubSystem"][0]
+
+ def getLogFile(self):
+ return self.useConf["logFile"][0]
+
+ def getLocaleDir(self):
+ return self.useConf["localeDir"][0]
+
+if __name__ == "__main__" :
+ c = gettorConf()
+ print "# This is a suitable default configuration. Tune to fit your needs."
+ c.printConfiguration()
Added: projects/gettor/gettor/gtlog.py
===================================================================
--- projects/gettor/gettor/gtlog.py (rev 0)
+++ projects/gettor/gettor/gtlog.py 2009-01-09 02:58:32 UTC (rev 18034)
@@ -0,0 +1,92 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+'''
+ gettor_log.py - gettor logging configuration
+
+ Copyright (c) 2008, Jacob Appelbaum <jacob@xxxxxxxxxxxxx>,
+ Christian Fromme <kaner@xxxxxxxxxx>
+
+ This is Free Software. See LICENSE for license information.
+
+ gettor may log information, this is how we handle that logging requirement.
+ A user may log to 'syslog', a 'file', 'stdout' or 'nothing'.
+ The user can choose one of those four options in a configuration file.
+
+ Note that this module will silently fall back to 'nothing' if anything is
+ minconfigured. Might be harder to debug, but is safer for now.
+'''
+
+import os
+import sys
+from time import gmtime, strftime
+import ConfigParser
+import syslog
+import logging
+import gettor.config
+from logging import handlers
+
+__all__ = ["initalize", "getLogger", "getLogSubSystem"]
+
+# Leave this to INFO for now
+loglevel = logging.INFO
+format = '%(asctime)-15s (%(process)d) %(message)s'
+logger = None
+logSubSystem = None
+initialized = False
+
+def initialize():
+ global logger
+ global logSubSystem
+ global initialized
+ # Don't add handlers twice
+ if initialized == True:
+ return
+ conf = gettor.config.gettorConf()
+ logger = logging.getLogger('gettor')
+ logger.setLevel(loglevel)
+ logSubSystem = conf.getLogSubSystem()
+
+ if logSubSystem == "stdout":
+ handler = logging.StreamHandler()
+ elif logSubSystem == "file":
+ # Silently fail if things are misconfigured
+ logFile = conf.getLogFile()
+ try:
+ if os.access(os.path.dirname(logFile), os.W_OK):
+ handler = logging.FileHandler(logFile)
+ else:
+ logSubSystem = "nothing"
+ except:
+ logSubSystem = "nothing"
+ elif logSubSystem == "syslog":
+ handler = logging.handlers.SysLogHandler(address="/dev/log")
+ else:
+ # Failsafe fallback
+ logSubSystem = "nothing"
+
+ # If anything went wrong or the user doesn't want to log
+ if logSubSystem == "nothing":
+ handler = logging.FileHandler(os.devnull)
+
+ formatter = logging.Formatter(fmt=format)
+ handler.setFormatter(formatter)
+ logger.addHandler(handler)
+ initialized = True
+
+def getLogSubSystem():
+ global logSubSystem
+ return logSubSystem
+
+def getLogger():
+ global logger
+ if logger is None:
+ initialize()
+ return logger
+
+if __name__ == "__main__" :
+ initialize()
+ print "This is the logging module. You probably do not want to call it by hand."
+ print "We'll send a test logging message now with the following subsystem: " + \
+ getLogSubSystem()
+ log = getLogger()
+ log.info("I'm a logger, logging!")
Added: projects/gettor/gettor/opt.py
===================================================================
--- projects/gettor/gettor/opt.py (rev 0)
+++ projects/gettor/gettor/opt.py 2009-01-09 02:58:32 UTC (rev 18034)
@@ -0,0 +1,69 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+'''
+ gettor_config.py: Command line parser for gettor
+
+ Copyright (c) 2008, Jacob Appelbaum <jacob@xxxxxxxxxxxxx>,
+ Christian Fromme <kaner@xxxxxxxxxx>
+
+ This is Free Software. See LICENSE for license information.
+
+
+ This is the option parser module for gettor.
+'''
+
+import optparse
+
+__all__ = ["parseOpts"]
+
+def parseOpts():
+ cmdParser = optparse.OptionParser()
+ cmdParser.add_option("-c", "--config", dest="configfile",
+ default="~/.gettorrc",
+ help="set config file to FILE", metavar="FILE")
+ cmdParser.add_option("-m", "--use-mirror", dest="mirror",
+ default="rsync.torproject.org",
+ help="set Tor package mirror to MIRROR",
+ metavar="MIRROR")
+ cmdParser.add_option("-i", "--install-crontab", dest="installcron",
+ action="store_true", default=False,
+ help="install crontab to refresh packagelist")
+ cmdParser.add_option("-f", "--fetch-packages", dest="fetchpackages",
+ action="store_true", default=False,
+ help="fetch Tor packages from mirror")
+ cmdParser.add_option("-p", "--prep-packages", dest="preppackages",
+ action="store_true", default=False,
+ help="prepare packages (zip them)")
+ cmdParser.add_option("-t", "--run-tests", dest="runtests",
+ action="store_true", default=False,
+ help="run some tests")
+ cmdParser.add_option("-w", "--whitelist", dest="whitelist",
+ default="",
+ help="add an email address to the whitelist",
+ metavar="WHITELIST")
+ cmdParser.add_option("-b", "--blacklist", dest="blacklist",
+ default="",
+ help="add an email address to the blacklist",
+ metavar="BLACKLIST")
+ cmdParser.add_option("-l", "--lookup", dest="lookup",
+ default="",
+ help="check black/white list presence of address",
+ metavar="CHECKADDRESS")
+ cmdParser.add_option("-x", "--clear-whitelist", dest="clearwl",
+ action="store_true", default=False,
+ help="clear all entrys in the whitelist")
+ cmdParser.add_option("-y", "--clear-blacklist", dest="clearbl",
+ action="store_true", default=False,
+ help="clear all entrys in the blacklist")
+ cmdParser.add_option("-r", "--install-translations", dest="insttrans",
+ action="store_true", default=False,
+ help="Compile and install translation files [check -d]")
+ cmdParser.add_option("-d", "--i18n-dir", dest="i18ndir",
+ default="./i18n",
+ help="Set your locale src dir to DIR [default = %default]",
+ metavar="DIR")
+
+ return cmdParser.parse_args()
+
+if __name__ == "__main__":
+ print >> sys.stderr, "You shouldn't run this directly."
Added: projects/gettor/gettor/packages.py
===================================================================
--- projects/gettor/gettor/packages.py (rev 0)
+++ projects/gettor/gettor/packages.py 2009-01-09 02:58:32 UTC (rev 18034)
@@ -0,0 +1,116 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+'''
+ gettor_packages.py: Package related stuff
+
+ Copyright (c) 2008, Jacob Appelbaum <jacob@xxxxxxxxxxxxx>,
+ Christian Fromme <kaner@xxxxxxxxxx>
+
+ This is Free Software. See LICENSE for license information.
+
+ This module handles all package related functionality
+'''
+
+import os
+import zipfile
+import subprocess
+import gettor.gtlog
+import gettor.config
+import re
+
+__all__ = ["gettorPackages"]
+
+log = gettor.gtlog.getLogger()
+
+class gettorPackages:
+
+ packageRegex = { "windows-bundle": "vidalia-bundle-.*.exe$",
+ "panther-bundle": "vidalia-bundle-.*-panther.dmg$",
+ "tiger-bundle": "vidalia-bundle-.*-tiger.dmg$",
+ "source-bundle": "tor-.*.tar.gz",
+ }
+
+ def __init__(self, mirror, config, silent=False):
+ self.mirror = mirror
+ self.packageList = {}
+ self.distDir = config.getDistDir()
+ try:
+ entry = os.stat(self.distDir)
+ except OSError, e:
+ log.error("Bad dist dir %s: %s" % (self.distDir, e))
+ raise IOError
+ self.packDir = config.getPackDir()
+ try:
+ entry = os.stat(self.packDir)
+ except OSError, e:
+ log.error("Bad pack dir %s: %s" % (self.packDir, e))
+ raise IOError
+ self.rsync = ["rsync"]
+ self.rsync.append("-a")
+ # Don't download dotdirs
+ self.rsync.append("--exclude='.*'")
+ if not silent:
+ self.rsync.append("--progress")
+ self.rsync.append("rsync://%s/tor/dist/current/" % self.mirror)
+ self.rsync.append(self.distDir)
+
+ def getPackageList(self):
+ # Build dict like 'name': 'name.z'
+ try:
+ for filename in os.listdir(self.packDir):
+ self.packageList[filename[:-2]] = self.packDir + "/" + filename
+ except OSError, (strerror):
+ log.error(_("Failed to build package list: %s") % strerror)
+ return None
+
+ # Check sanity
+ for key, val in self.packageList.items():
+ # Remove invalid packages
+ if not os.access(val, os.R_OK):
+ log.info(_("Warning: %s not accessable. Removing from list.") % val)
+ del self.packageList[key]
+ return self.packageList
+
+ def buildPackages(self):
+ for filename in os.listdir(self.distDir):
+ for (pack, regex) in self.packageRegex.items():
+ if re.compile(regex).match(filename):
+ file = self.distDir + "/" + filename
+ ascfile = file + ".asc"
+ zipFileName = self.packDir + "/" + pack + ".z"
+ # If .asc file is there, build Zip file
+ if os.access(ascfile, os.R_OK):
+ zip = zipfile.ZipFile(zipFileName, "w")
+ zip.write(file, os.path.basename(file))
+ zip.write(ascfile, os.path.basename(ascfile))
+ zip.close()
+ self.packageList[pack] = zipFileName
+ break
+ if len(self.packageList) > 0:
+ return True
+ else:
+ return False
+
+ def syncWithMirror(self):
+ process = subprocess.Popen(self.rsync)
+ process.wait()
+ return process.returncode
+
+ def getCommandToStr(self):
+ """This is useful for cronjob installations
+ """
+ return ''.join(self.rsync)
+
+if __name__ == "__main__" :
+ c = gettor_config.gettorConf()
+ p = gettorPackages("rsync.torproject.org", c)
+ print "Building packagelist.."
+ if p.syncwithMirror() != 0:
+ print "Failed."
+ exit(1)
+ if not p.buildPackageList():
+ print "Failed."
+ exit(1)
+ print "Done."
+ for (pack, file) in p.getPackageList().items():
+ print "Bundle is mapped to file: ", pack, file
Added: projects/gettor/gettor/requests.py
===================================================================
--- projects/gettor/gettor/requests.py (rev 0)
+++ projects/gettor/gettor/requests.py 2009-01-09 02:58:32 UTC (rev 18034)
@@ -0,0 +1,116 @@
+#!/usr/bin/python2.5
+# -*- coding: utf-8 -*-
+"""
+ gettor_config.py - Parse configuration file for gettor
+
+ Copyright (c) 2008, Jacob Appelbaum <jacob@xxxxxxxxxxxxx>,
+ Christian Fromme <kaner@xxxxxxxxxx>
+
+ This is Free Software. See LICENSE for license information.
+
+ This library implements all of the email parsing features needed for gettor.
+"""
+
+import sys
+import email
+import dkim
+import re
+
+__all__ = ["requestMail"]
+
+class requestMail:
+
+ defaultLang = "en"
+ supportedLangs = { "en": "English",
+ "de": "Deutsch" }
+
+ def __init__(self, packages):
+ """
+ Read message from stdin, parse all the stuff we want to know
+ """
+ self.rawMessage = sys.stdin.read()
+ self.parsedMessage = email.message_from_string(self.rawMessage)
+ self.signature = False
+ # TODO XXX:
+ # This should catch DNS exceptions and fail to verify if we have a
+ # dns timeout
+ # We also should catch totally malformed messages here
+ try:
+ if dkim.verify(self.rawMessage):
+ self.signature = True
+ except:
+ pass
+
+ # TODO XXX:
+ # Scrub this data
+ self.replyToAddress = None
+ self.replytoAddress = self.parsedMessage["from"]
+ # If no package name could be recognized, use 'None'
+ self.returnPackage = None
+ for line in email.Iterators.body_line_iterator(self.parsedMessage):
+ for package in packages.keys():
+ match = re.match(package, line)
+ if match:
+ self.returnPackage = package
+
+ self.replyLocale = None
+ pattern = re.compile("^Lang:\s+(.*)$")
+ for line in email.Iterators.body_line_iterator(self.parsedMessage):
+ match = pattern.match(line)
+ if match:
+ self.replyLocale = match.group(1)
+
+ for (key, lang) in self.supportedLangs.items():
+ if self.replyLocale == key:
+ break
+ else:
+ self.replyLocale = self.defaultLang
+
+ def getRawMessage(self):
+ return self.rawMessage
+
+ def hasVerifiedSignature(self):
+ return self.signature
+
+ def getParsedMessage(self):
+ return self.parsedMessage
+
+ def getReplyTo(self):
+ return self.replytoAddress
+
+ def getPackage(self):
+ return self.returnPackage
+
+ def getLocale(self):
+ return self.replyLocale
+
+if __name__ == "__main__" :
+ """ Give us an email to understand what we think of it. """
+ packageList = {
+ "windows-bundle": "/var/lib/gettor/pkg/windows-bundle.z",
+ "macosx-bundle": "/var/lib/gettor/pkg/macosx-bundle.z",
+ "linux-bundle": "/var/lib/gettor/pkg/linux-bundle.z",
+ "source-bundle": "/var/lib/gettor/pkg/source-bundle.z"
+ }
+
+ rmail = requestMail(packageList)
+ print "Fetching raw message."
+ rawMessage = rmail.getRawMessage()
+ # This doesn't work without DNS ( no wifi on board current airplane )
+ print "Verifying signature of message."
+ signature = rmail.hasVerifiedSignature()
+ print "Parsing Message."
+ parsedMessage = rmail.getParsedMessage()
+ print "Parsing reply."
+ parsedReply = rmail.getReplyTo()
+ print "Parsing package request."
+ package = rmail.getRequestPackage()
+ if package == None:
+ packageFile = "help"
+ else:
+ packageFile = packageList[package]
+
+ print "The signature status of the email is: %s" % str(signature)
+ print "The email requested the following reply address: %s" % parsedReply
+ print "It looks like the email requested the following package: %s" % package
+ print "We would select the following package file: %s" % packageFile
Added: projects/gettor/gettor/responses.py
===================================================================
--- projects/gettor/gettor/responses.py (rev 0)
+++ projects/gettor/gettor/responses.py 2009-01-09 02:58:32 UTC (rev 18034)
@@ -0,0 +1,165 @@
+#!/usr/bin/python2.5
+# -*- coding: utf-8 -*-
+"""
+ gettor_config.py - Parse configuration file for gettor
+
+ Copyright (c) 2008, Jacob Appelbaum <jacob@xxxxxxxxxxxxx>,
+ Christian Fromme <kaner@xxxxxxxxxx>
+
+ This is Free Software. See LICENSE for license information.
+
+ This library implements all of the email replying features needed for gettor.
+"""
+
+import os
+import smtplib
+import MimeWriter
+import StringIO
+import base64
+import gettext
+
+__all__ = ["gettorResponse"]
+
+
+class gettorResponse:
+
+ def __init__(self, config, mailLang="en", logLang="en"):
+ self.mailLang = mailLang
+ self.logLang = logLang
+ self.config = config
+
+ def setLang(self, language):
+ # XXX: Sorta hack, have nothing better on my mind right now
+ # On every entry to a translation-needing function, call this with
+ # lang=maillang
+ # On every exit of a translation-needing function, call this with
+ # lang=loglang
+ # :-/
+ if self.config:
+ trans = gettext.translation("gettor", self.config.getLocaleDir(), [language])
+ trans.install()
+
+ def sendHelp(self, source, destination):
+ """ Send a helpful message to the user interacting with us """
+ self.setLang(self.mailLang)
+ message = _("""
+ Hello! This is the "gettor" robot.
+
+ Unfortunately, we won't answer you at this address. We only process
+ requests from email services that support "DKIM", which is an email
+ feature that lets us verify that the address in the "From" line is
+ actually the one who sent the mail.
+
+ Gmail and Yahoo Mail both use DKIM. You will have better luck sending
+ us mail from one of those.
+
+ (We apologize if you didn't ask for this mail. Since your email is from
+ a service that doesn't use DKIM, we're sending a short explanation,
+ and then we'll ignore this email address for the next day or so.)
+ """)
+ help = self.constructMessage(message, source, destination)
+ try:
+ status = self.sendMessage(help, source, destination)
+ except:
+ status = False
+ self.setLang(self.logLang)
+
+ return status
+
+ def sendPackageHelp(self, packageList, source, destination):
+ """ Send a helpful message to the user interacting with us """
+ self.setLang(self.mailLang)
+ message = _("""
+ Hello, This is the "gettor" robot.
+
+ I am sorry, but your request was not understood. Please select one of the
+ following package names:
+
+ """ + "".join([ "\t%s\n" % key for key in packageList.keys()]) + """
+ Please send me another email. It only needs a single package name anywhere
+ in the body of your email.
+ """)
+ help = self.constructMessage(message, source, destination)
+ try:
+ status = self.sendMessage(help, source, destination)
+ except:
+ status = False
+ self.setLang(self.logLang)
+
+ return status
+
+ def sendPackage(self, source, destination, filename):
+ """ Send a message with an attachment to the user interacting with us """
+ self.setLang(self.mailLang)
+ message = _("""
+ Hello! This is the "gettor" robot.
+
+ Here's your requested software as a zip file. Please unzip the
+ package and verify the signature.
+
+ Hint: If your computer has GnuPG installed, use the gpg
+ commandline tool as follows after unpacking the zip file:
+
+ gpg --verify <packagename>.asc <packagename>
+
+ The output should look somewhat like this:
+
+ gpg: Good signature from "Roger Dingledine <arma@xxxxxxx>"
+
+ If you're not familiar with commandline tools, try looking for
+ a graphical user interface for GnuPG on this website:
+
+ http://www.gnupg.org/related_software/frontends.html
+
+ Have fun.
+ """)
+ package = self.constructMessage(message, source, destination, filename)
+ try:
+ status = self.sendMessage(package, source, destination)
+ except:
+ status = False
+ self.setLang(self.mailLang)
+
+ return status
+
+ def constructMessage(self, messageText, ourAddress, recipient, fileName=None):
+ """ Construct a multi-part mime message, including only the first part
+ with plaintext."""
+
+ message = StringIO.StringIO()
+ mime = MimeWriter.MimeWriter(message)
+ mime.addheader('MIME-Version', '1.0')
+ mime.addheader('Subject', _('Re: Your "gettor" request'))
+ mime.addheader('To', recipient)
+ mime.addheader('From', ourAddress)
+ mime.startmultipartbody('mixed')
+
+ firstPart = mime.nextpart()
+ emailBody = firstPart.startbody('text/plain')
+ emailBody.write(messageText)
+
+ # Add a file if we have one
+ if fileName:
+ filePart = mime.nextpart()
+ filePart.addheader('Content-Transfer-Encoding', 'base64')
+ emailBody = filePart.startbody('application/zip; name=%s' % os.path.basename(fileName))
+ base64.encode(open(fileName, 'rb'), emailBody)
+
+ # Now end the mime messsage
+ mime.lastpart()
+ return message
+
+ def sendMessage(self, message, src, dst, smtpserver="localhost:25"):
+ try:
+ smtp = smtplib.SMTP(smtpserver)
+ smtp.sendmail(src, dst, message.getvalue())
+ smtp.quit()
+ status = True
+ except:
+ return False
+
+ return status
+
+if __name__ == "__main__" :
+ print "This is the response handling code. You probably do not want to call it by hand."
+
Deleted: projects/gettor/gettor.py
===================================================================
--- projects/gettor/gettor.py 2009-01-09 01:21:27 UTC (rev 18033)
+++ projects/gettor/gettor.py 2009-01-09 02:58:32 UTC (rev 18034)
@@ -1,352 +0,0 @@
-#!/usr/bin/python2.5
-# -*- coding: utf-8 -*-
-"""
-
- gettor.py by Jacob Appelbaum <jacob@xxxxxxxxxxxxx>,
- Christian Fromme <kaner@xxxxxxxxxx>
- This program will hand out Tor via email to supported systems.
- This program is Free Software, see LICENSE for details.
-
- It is intended to be used in a .forward file as part of a pipe like so:
-
- cat <<'EOF'> .forward
- |/usr/local/bin/gettor.py
- EOF
-
- You should have a dist/current/ mirror in a directory that gettor can read.
- Such a mirror can be created like so:
-
- cd /usr/local/
- rsync -av rsync://rsync.torproject.org/tor/dist/current tor-dist-current/
-
- You can keep it updated with a cronjob like so:
-
- MirrorDir=/usr/local/tor-dist-current/
- 0 3 * * * rsync -a rsync://rsync.torproject.org/tor/dist/current/ $MirrorDir
-
- You should ensure that for each file and signature pair you wish to
- distribute, you have created a zip file containing both.
-
- While this program isn't written in a threaded manner per se, it is designed to function
- as if it will be called as a pipe many times at once. There is a slight
- desynchronization with blacklist entry checking and may result in false
- negatives. This isn't perfect but it is designed to be lightweight. It could
- be fixed easily with a shared locking system but this isn't implemented yet.
-
- To clean out the blacklist on a daily basis, install the following cronjob:
-
- # m h dom mon dow command
- 1 1 * * * /bin/rm -rf /var/lib/gettor/bl/*
-
- You'll probably want a directory structure like this owned by uid/gid 'gettor':
- /var/lib/gettor/{bl,pkg}
-
-"""
-
-__program__ = 'gettor.py'
-__version__ = '20080914.01'
-__url__ = 'https://tor-svn.freehaven.net/svn/tor/trunk/contrib/gettor/'
-__author__ = 'Jacob Appelbaum <jacob@xxxxxxxxxxxxx>, Christian Fromme <kaner@xxxxxxxxxx>'
-__copyright__ = 'Copyright (c) 2008, Jacob Appelbaum, Christian Fromme'
-__license__ = 'See LICENSE for licensing information'
-
-try:
- from future import antigravity
-except ImportError:
- antigravity = None
-
-import sys
-import os
-import subprocess
-import gettext
-import gettor_blacklist
-import gettor_requests
-import gettor_responses
-import gettor_log
-import gettor_config
-import gettor_opt
-import gettor_packages
-
-# Global logger
-log = None
-
-# Switch language to 'newlocale'. Return default if language is not supported.
-def switchLocale(newlocale, localedir):
- trans = gettext.translation("gettor",
- localedir,
- [newlocale],
- fallback=True)
- trans.install()
-
-def runTests():
- # XXX: Implement me
- return True
-
-def installMo(poFile, targetDir):
- global log
- args = os.getcwd() + "/" + poFile + " -o " + targetDir + "/gettor.mo"
- try:
- ret = subprocess.call("msgfmt" + " " + args, shell=True)
- if ret < 0:
- log.error("Error in msgfmt execution: %s" % ret)
- return False
- except OSError, e:
- log.erro("Comilation failed: " % e)
- return False
- return True
-
-def installTrans(config, localeSrcdir):
- global log
- hasDirs = None
-
- if config is None:
- log.error("Bad arg.")
- return False
- if not os.path.isdir(localeSrcdir):
- log.error("Not a directory: " % localeSrcdir)
- return False
- localeDir = config.getLocaleDir()
- if not os.path.isdir(localeDir):
- log.error("Sorry, %s is not a directory." % distDir)
- return False
-
- for root, dirs, files in os.walk(localeSrcdir):
- # Python lacks 'depth' featue for os.walk()
- if root != localeSrcdir:
- continue
- for dir in dirs:
- hasDirs = True
- if dir.startswith("."):
- continue
- try:
- poFile = os.path.join(root, dir) + "/gettor_" + dir + ".po"
- # Construct target dir
- targetDir = localeDir + "/" + dir + "/LC_MESSAGES"
- if os.path.isdir(targetDir):
- if installMo(poFile, targetDir) == False:
- log.error("Installing .mo files failed.")
- return False
- else:
- log.error("Not a directory: " % targetDir)
- return False
- except Exception, e:
- log.error("Error accessing translation files: " % e)
- return False
- if hasDirs is None:
- log.errpr("Empty locale dir: " % localeSrcdir)
- return False
-
- return True
-
-def installCron():
- # XXX: Check if cron is installed and understands our syntax?
- currentCronTab = getCurrentCrontab()
- gettorPath = os.getcwd() + "/" + os.path.basename(sys.argv[0])
- gettorArgs = " --clear-blacklist --fetch-packages --prep-packages"
- newCronTab = currentCronTab + '\n' + '3 2 * * * ' + gettorPath + gettorArgs
- echoCmd = ['echo', newCronTab ]
- cronCmd = ['crontab', '-']
- echoProc = subprocess.Popen(echoCmd, stdout=subprocess.PIPE)
- cronProc = subprocess.Popen(cronCmd, stdin=echoProc.stdout)
- cronProc.communicate()[0]
- return cronProc.returncode
-
-def getCurrentCrontab():
- # This returns our current crontab
- savedTab = "# This crontab has been tampered with by gettor.py\n"
- currentTab = os.popen("crontab -l")
- for line in currentTab:
- savedTab += line
- return savedTab
-
-def processMail(conf, logLang, packageList, blackList, whiteList):
- global log
-
- if packageList is None or len(packageList) < 1:
- log.error(_("Sorry, your package list is unusable."))
- log.error(_("Try running with --fetch-packages --prep-packages."))
- return False
-
- # Receive mail from stdin
- rmail = gettor_requests.requestMail(packageList)
- rawMessage = rmail.getRawMessage()
- if not rawMessage:
- log.error(_("No raw message. Something went wrong."))
- return False
- parsedMessage = rmail.getParsedMessage()
- if not parsedMessage:
- log.error(_("No parsed message. Dropping message."))
- return False
- replyTo = rmail.getReplyTo()
- if not replyTo:
- log.error(_("No help dispatched. Invalid reply address for user."))
- return False
- replyLang = rmail.getLocale()
- if not replyLang:
- replyLang = logLang
-
- # Initialize response
- srcEmail = conf.getSrcEmail()
- # Bail out if someone tries to be funny
- if (srcEmail == replyTo):
- log.error(_("Won't send myself emails."))
- return False
-
- resp = gettor_responses.gettorResponse(conf, replyLang, logLang)
- signature = rmail.hasVerifiedSignature()
- log.info(_("Signature is: %s") % str(signature))
- # Addresses from whitelist can pass without DKIM signature
- if not signature and not whiteList.lookupListEntry(replyTo):
- # Check to see if we've helped them to understand that they need DKIM
- # in the past
- previouslyHelped = blackList.lookupListEntry(replyTo)
- if previouslyHelped:
- log.info(_("Unsigned messaged to gettor by blacklisted user dropped."))
- return False
- else:
- # Reply with some help and bail out
- blackList.createListEntry(replyTo)
- resp.sendHelp(srcEmail, replyTo)
- log.info(_("Unsigned messaged to gettor. We issued some help."))
- return True
- else:
- log.info(_("Good message to gettor."))
- package = rmail.getPackage()
- if package != None:
- log.info(_("Package: %s selected.") % str(package))
- resp.sendPackage(srcEmail, replyTo, packageList[package])
- else:
- resp.sendPackageHelp(packageList, srcEmail, replyTo)
- log.info(_("We issued some help about proper email formatting."))
-
- return True
-
-def main():
- global log
- success = None
-
- # Parse command line, setup config, logging and language
- options, arguments = gettor_opt.parseOpts()
- conf = gettor_config.gettorConf(options.configfile)
- gettor_log.initialize()
- log = gettor_log.getLogger()
-
- # Setup locale
- logLang = conf.getLocale()
- localeDir = conf.getLocaleDir()
- # We need to do this first
- if options.insttrans:
- if installTrans(conf, options.i18ndir):
- log.info("Installing translation files done.")
- success = True
- else:
- log.error("Installing translation files failed.")
- return False
- # Just check for the english .mo file, because that's the fallback
- englishMoFile = localeDir + "/en/LC_MESSAGES/gettor.mo"
- if not os.path.isfile(englishMoFile):
- log.error("Sorry, %s is not a file we can access." % englishMoFile)
- return False
- switchLocale(logLang, localeDir)
-
- distDir = conf.getDistDir()
- if not os.path.isdir(distDir):
- log.error(_("Sorry, %s is not a directory.") % distDir)
- return False
- try:
- packs = gettor_packages.gettorPackages(options.mirror, conf)
- except IOError:
- log.error(_("Error initiating package list."))
- return False
- try:
- whiteList = gettor_blacklist.BWList(conf.getWlStateDir())
- blackList = gettor_blacklist.BWList(conf.getBlStateDir())
- except IOError, e:
- log.error(_("White/Black list error: %s") % e)
- return False
-
- if options.fetchpackages:
- if packs.syncWithMirror() != 0:
- log.error(_("Syncing Tor packages failed."))
- return False
- else:
- log.info(_("Syncing Tor packages done."))
- success = True
- if options.preppackages:
- if not packs.buildPackages():
- log.error(_("Building packages failed."))
- return False
- else:
- log.info(_("Building packages done."))
- success = True
- if options.runtests:
- if not runTests():
- log.error(_("Tests failed."))
- return False
- else:
- log.info(_("Tests passed."))
- success = True
- if options.installcron:
- if installCron() != 0:
- log.error(_("Installing cron failed"))
- return False
- else:
- log.info(_("Installing cron done."))
- success = True
- if options.whitelist:
- if not whiteList.createListEntry(options.whitelist):
- log.error(_("Creating whitelist entry failed."))
- return False
- else:
- log.info(_("Creating whitelist entry ok."))
- success = True
- if options.blacklist:
- if not blackList.createListEntry(options.blacklist):
- log.error(_("Creating blacklist entry failed."))
- return False
- else:
- log.info(_("Creating blacklist entry ok."))
- success = True
- if options.lookup:
- if whiteList.lookupListEntry(options.lookup):
- log.info(_("Present in whitelist."))
- success = True
- if blackList.lookupListEntry(options.lookup):
- log.info(_("Present in blacklist."))
- success = True
- if not success:
- log.info(_("Address neither in blacklist or whitelist."))
- success = True
- if options.clearwl:
- if not whiteList.removeAll():
- log.error(_("Deleting whitelist failed."))
- return False
- else:
- log.info(_("Deleting whitelist done."))
- success = True
- if options.clearbl:
- if not blackList.removeAll():
- log.error(_("Deleting blacklist failed."))
- return False
- else:
- log.info(_("Deleting blacklist done."))
- success = True
-
- # Break here if preparation work has been done
- if success is not None:
- return success
-
- # Main loop
- if not processMail(conf, logLang, packs.getPackageList(), blackList,
- whiteList):
- log.error(_("Processing mail failed."))
- return False
-
- return True
-
-if __name__ == "__main__":
- if not main():
- print >> sys.stderr, "Main loop exited with errors."
- exit(1)
- else:
- exit(0)
Deleted: projects/gettor/gettor_blacklist.py
===================================================================
--- projects/gettor/gettor_blacklist.py 2009-01-09 01:21:27 UTC (rev 18033)
+++ projects/gettor/gettor_blacklist.py 2009-01-09 02:58:32 UTC (rev 18034)
@@ -1,115 +0,0 @@
-#!/usr/bin/python2.5
-"""This library implements all of the black listing features needed for gettor.
-Basically, it offers creation, removal and lookup of email addresses stored as
-SHA1 hashes in a dedicated directory on the filesystem.
-"""
-
-import hashlib
-import os
-import re
-import gettor_config
-import gettor_log
-
-log = gettor_log.getLogger()
-
-conf = gettor_config.gettorConf()
-stateDir = conf.getStateDir()
-blStateDir = conf.getBlStateDir()
-
-class BWList:
- def __init__(self, listdir):
- self.listDir = listdir
- if not os.path.isdir(self.listDir):
- # XXX Change this to something more appropriate
- raise IOError("Bad dir: %s" % self.listDir)
-
- def lookupListEntry(self, address):
- """Check to see if we have a list entry for the given address."""
- emailonly = self.stripEmail(address)
- entry = self.listDir + "/" + str(hashlib.sha1(emailonly).hexdigest())
- try:
- entry = os.stat(entry)
- except OSError:
- return False
- return True
-
- def createListEntry(self, address):
- emailonly = self.stripEmail(address)
- entry = self.listDir + "/" + str(hashlib.sha1(emailonly).hexdigest())
- if self.lookupListEntry(address) == False:
- try:
- fd = open(entry, 'w')
- fd.close
- return True
- except:
- log.error(_("Creating list entry %s failed.") % entry)
- return False
- else:
- # List entry already exists
- return False
-
- def removeListEntry(self, address):
- emailonly = self.stripEmail(address)
- entry = self.listDir + "/" + str(hashlib.sha1(emailonly).hexdigest())
- if (self.lookupListEntry(address) == True):
- try:
- os.unlink(entry)
- except OSError:
- log.error(_("Could not unlink entry %s") % entry)
- return False
- else:
- log.info(_("Requested removal of non-existing entry %s. Abord.")
- % entry)
- return False
-
- def removeAll(self):
- print "Removing all entries from list!"
- for root, dirs, files in os.walk(self.listDir):
- for file in files:
- try:
- rmfile = os.path.join(root, file)
- os.remove(rmfile)
- except:
- log.error(_("Could not remove %s." % rmfile))
- return False
- return True
-
- def stripEmail(self, address):
- '''Strip "Bart Foobar <bart@xxxxxxxxxx>" to "<bart@xxxxxxxxxx">'''
- match = re.search('<.*?>', address)
- if match is not None:
- return match.group()
- return address
-
-def blackListtests(address):
- """ This is a basic evaluation of our blacklist functionality """
- bwlist = BWList("/tmp/")
- print bwlist.createListEntry(address)
- print bwlist.lookupListEntry(address)
- #prepBLStateDir()
- #privateAddress = makeAddressPrivate(address)
- #print "We have a private address of: " + privateAddress
- #print "Testing creation of blacklist entry: "
- #blackListEntry = createBlackListEntry(privateAddress)
- #print blackListEntry
- #print "We're testing a lookup of a known positive blacklist entry: "
- #blackListEntry = lookupBlackListEntry(privateAddress)
- #print blackListEntry
- #print "We're testing removal of a known blacklist entry: "
- #blackListEntry = removeBlackListEntry(privateAddress)
- #print blackListEntry
- #print "We're testing a lookup of a known false blacklist entry: "
- #blackListEntry = lookupBlackListEntry(privateAddress)
- #print blackListEntry
- #print "Now we'll test the higher level blacklist functionality..."
- #print "This should not find an entry in the blacklist: "
- #print blackList(address)
- #print "This should add an entry to the blacklist: "
- #print blackList(address, True)
- #print "This should find the previous addition to the blacklist: "
- #print blackList(address)
- #print "Please ensure the tests match what you would expect for your environment."
-
-if __name__ == "__main__" :
- print "Running some tests.."
- blackListtests("requestingUser@xxxxxxxxxxx")
Deleted: projects/gettor/gettor_config.py
===================================================================
--- projects/gettor/gettor_config.py 2009-01-09 01:21:27 UTC (rev 18033)
+++ projects/gettor/gettor_config.py 2009-01-09 02:58:32 UTC (rev 18034)
@@ -1,172 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-'''
- gettor_config.py - Parse configuration file for gettor
-
- Copyright (c) 2008, Jacob Appelbaum <jacob@xxxxxxxxxxxxx>,
- Christian Fromme <kaner@xxxxxxxxxx>
-
- This is Free Software. See LICENSE for license information.
-
-
- We grab configurable values from the users' gettor config file. If that file
- is not present, we will supply reasonable defaults. Config files are ini-style
- formatted. We know this is ugly, but we prefer it to XML and also ConfigParser
- seems handy. ;-)
-
- A valid config file should look like this:
-
- [global]
- stateDir = /var/lib/gettor/
- blStateDir = /var/lib/gettor/bl/
- distDir = /var/lib/gettor/pkg/
- srcEmail = gettor@xxxxxxx
- locale = en
- logSubSystem = nothing
- logFile = /dev/null
- localeDir = /usr/share/locale
-
- Note that you can set from none to any of these values in your config file.
- Values you dont provide will be taken from the defaults in 'useConf'.
-
- Here is what each of them is used for individually:
-
- blStateDir: Blacklisted (hashed) email addresses go here
- wlStateDir: Whitelisted (hashed) email addresses go here
- distDir: Sent-out Tor packages are found here
- srcEmail: The email containing the Tor package will use this as 'From:'
- locale: Choose your default mail and log locale
- logFile: If 'file' logging is chosen, log to this file
- logSubSystem: This has to be one of the following strings:
- 'nothing': Nothing is logged anywhere (Recommended)
- 'stdout': Log to stdout
- 'syslog': Logmessages will be written to syslog
- 'file': Logmessages will be written to a file (Not that
- this needs the 'logFile' option in the config file
- also set to something useful
- localeDir: This is where the 'en/LC_MESSAGES/gettor.mo' or
- 'whateverlang/LC_MESSAGES/gettor.mo' should go
-
- If no valid config file is provided to __init__, gettorConf will try to use
- '~/.gettorrc' as default config file. If that fails, the default values from
- useConf will be used.
-
- Run this module from the commandline to have it print a useful default config
- like so:
-
- $ ./gettor_config.py > ~/.gettorrc
-
-'''
-
-import os
-import sys
-import ConfigParser
-
-__all__ = ["gettorConf"]
-
-class gettorConf:
- '''
- Initialize gettor with default values if one or more values are missing
- from the config file. This will return entirely default values if the
- configuration file is missing. Our default file location is ~/.gettorrc
- of $USER.
- '''
-
- def __init__(self, path = os.path.expanduser("~/.gettorrc")):
- '''
- Most of the work happens here. Parse config, merge with default values,
- prepare outConf.
- '''
-
- self.configFile = os.path.expanduser(path)
-
- # Variable name | Default value | Section
- self.useConf = {"stateDir": ("/var/lib/gettor/", "global"),
- "blStateDir": ("/var/lib/gettor/bl/", "global"),
- "wlStateDir": ("/var/lib/gettor/wl/", "global"),
- "srcEmail": ("gettor@xxxxxxxxxxxxxx", "global"),
- "distDir": ("/var/lib/gettor/dist/", "global"),
- "packDir": ("/var/lib/gettor/pkg/", "global"),
- "locale": ("en", "global"),
- "logSubSystem": ("nothing", "global"),
- "logFile": ("/dev/null", "global"),
- "localeDir": ("/usr/share/locale", "global")}
-
- # One ConfigParser instance to read the actual values from config
- self.config = ConfigParser.ConfigParser()
- # And another to provide a useable default config as output. This is
- # only because the user may have strange stuff inside his config file.
- # We're trying to be failsafe here
- self.outConf = ConfigParser.ConfigParser()
-
- try:
- if os.access(self.configFile, os.R_OK):
- self.config.read(self.configFile)
- except:
- pass
-
- # Main parser loop:
- # * Merge default values with those from the config file, if there are
- # any
- # * Update values from config file into useConf
- # * Ignore sections and values that are not found in useConf, but in
- # the config file (wtf?)
- # * Prepare outConf
- for dkey, (dval, sec) in self.useConf.items():
- if not self.outConf.has_section(sec):
- self.outConf.add_section(sec)
- try:
- for key, val in self.config.items(sec):
- # Unfortunatly, keys from the config are not case-sensitive
- if key.lower() == dkey.lower():
- self.useConf[dkey] = val, sec
- self.outConf.set(sec, dkey, val)
- break
- else:
- # Add default value for dkey
- self.outConf.set(sec, dkey, dval)
-
- except:
- self.outConf.set(sec, dkey, dval)
-
- def printConfiguration(self):
- '''
- Print out config file. This works even if there is none
- '''
- return self.outConf.write(sys.stdout)
-
- # All getter routines live below
- def getStateDir(self):
- return self.useConf["stateDir"][0]
-
- def getBlStateDir(self):
- return self.useConf["blStateDir"][0]
-
- def getWlStateDir(self):
- return self.useConf["wlStateDir"][0]
-
- def getSrcEmail(self):
- return self.useConf["srcEmail"][0]
-
- def getDistDir(self):
- return self.useConf["distDir"][0]
-
- def getPackDir(self):
- return self.useConf["packDir"][0]
-
- def getLocale(self):
- return self.useConf["locale"][0]
-
- def getLogSubSystem(self):
- return self.useConf["logSubSystem"][0]
-
- def getLogFile(self):
- return self.useConf["logFile"][0]
-
- def getLocaleDir(self):
- return self.useConf["localeDir"][0]
-
-if __name__ == "__main__" :
- c = gettorConf()
- print "# This is a suitable default configuration. Tune to fit your needs."
- c.printConfiguration()
Deleted: projects/gettor/gettor_log.py
===================================================================
--- projects/gettor/gettor_log.py 2009-01-09 01:21:27 UTC (rev 18033)
+++ projects/gettor/gettor_log.py 2009-01-09 02:58:32 UTC (rev 18034)
@@ -1,92 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-'''
- gettor_log.py - gettor logging configuration
-
- Copyright (c) 2008, Jacob Appelbaum <jacob@xxxxxxxxxxxxx>,
- Christian Fromme <kaner@xxxxxxxxxx>
-
- This is Free Software. See LICENSE for license information.
-
- gettor may log information, this is how we handle that logging requirement.
- A user may log to 'syslog', a 'file', 'stdout' or 'nothing'.
- The user can choose one of those four options in a configuration file.
-
- Note that this module will silently fall back to 'nothing' if anything is
- minconfigured. Might be harder to debug, but is safer for now.
-'''
-
-import os
-import sys
-from time import gmtime, strftime
-import ConfigParser
-import syslog
-import logging
-import gettor_config
-from logging import handlers
-
-__all__ = ["initalize", "getLogger", "getLogSubSystem"]
-
-# Leave this to INFO for now
-loglevel = logging.INFO
-format = '%(asctime)-15s (%(process)d) %(message)s'
-logger = None
-logSubSystem = None
-initialized = False
-
-def initialize():
- global logger
- global logSubSystem
- global initialized
- # Don't add handlers twice
- if initialized == True:
- return
- config = gettor_config.gettorConf()
- logger = logging.getLogger('gettor')
- logger.setLevel(loglevel)
- logSubSystem = config.getLogSubSystem()
-
- if logSubSystem == "stdout":
- handler = logging.StreamHandler()
- elif logSubSystem == "file":
- # Silently fail if things are misconfigured
- logFile = config.getLogFile()
- try:
- if os.access(os.path.dirname(logFile), os.W_OK):
- handler = logging.FileHandler(logFile)
- else:
- logSubSystem = "nothing"
- except:
- logSubSystem = "nothing"
- elif logSubSystem == "syslog":
- handler = logging.handlers.SysLogHandler(address="/dev/log")
- else:
- # Failsafe fallback
- logSubSystem = "nothing"
-
- # If anything went wrong or the user doesn't want to log
- if logSubSystem == "nothing":
- handler = logging.FileHandler(os.devnull)
-
- formatter = logging.Formatter(fmt=format)
- handler.setFormatter(formatter)
- logger.addHandler(handler)
- initialized = True
-
-def getLogSubSystem():
- global logSubSystem
- return logSubSystem
-
-def getLogger():
- global logger
- if logger is None:
- initialize()
- return logger
-
-if __name__ == "__main__" :
- initialize()
- print "This is the logging module. You probably do not want to call it by hand."
- print "We'll send a test logging message now with the following subsystem: " + \
- getLogSubSystem()
- log = getLogger()
- log.info("I'm a logger, logging!")
Deleted: projects/gettor/gettor_opt.py
===================================================================
--- projects/gettor/gettor_opt.py 2009-01-09 01:21:27 UTC (rev 18033)
+++ projects/gettor/gettor_opt.py 2009-01-09 02:58:32 UTC (rev 18034)
@@ -1,69 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-'''
- gettor_config.py: Command line parser for gettor
-
- Copyright (c) 2008, Jacob Appelbaum <jacob@xxxxxxxxxxxxx>,
- Christian Fromme <kaner@xxxxxxxxxx>
-
- This is Free Software. See LICENSE for license information.
-
-
- This is the option parser module for gettor.
-'''
-
-import optparse
-
-__all__ = ["parseOpts"]
-
-def parseOpts():
- cmdParser = optparse.OptionParser()
- cmdParser.add_option("-c", "--config", dest="configfile",
- default="~/.gettorrc",
- help="set config file to FILE", metavar="FILE")
- cmdParser.add_option("-m", "--use-mirror", dest="mirror",
- default="rsync.torproject.org",
- help="set Tor package mirror to MIRROR",
- metavar="MIRROR")
- cmdParser.add_option("-i", "--install-crontab", dest="installcron",
- action="store_true", default=False,
- help="install crontab to refresh packagelist")
- cmdParser.add_option("-f", "--fetch-packages", dest="fetchpackages",
- action="store_true", default=False,
- help="fetch Tor packages from mirror")
- cmdParser.add_option("-p", "--prep-packages", dest="preppackages",
- action="store_true", default=False,
- help="prepare packages (zip them)")
- cmdParser.add_option("-t", "--run-tests", dest="runtests",
- action="store_true", default=False,
- help="run some tests")
- cmdParser.add_option("-w", "--whitelist", dest="whitelist",
- default="",
- help="add an email address to the whitelist",
- metavar="WHITELIST")
- cmdParser.add_option("-b", "--blacklist", dest="blacklist",
- default="",
- help="add an email address to the blacklist",
- metavar="BLACKLIST")
- cmdParser.add_option("-l", "--lookup", dest="lookup",
- default="",
- help="check black/white list presence of address",
- metavar="CHECKADDRESS")
- cmdParser.add_option("-x", "--clear-whitelist", dest="clearwl",
- action="store_true", default=False,
- help="clear all entrys in the whitelist")
- cmdParser.add_option("-y", "--clear-blacklist", dest="clearbl",
- action="store_true", default=False,
- help="clear all entrys in the blacklist")
- cmdParser.add_option("-r", "--install-translations", dest="insttrans",
- action="store_true", default=False,
- help="Compile and install translation files [check -d]")
- cmdParser.add_option("-d", "--i18n-dir", dest="i18ndir",
- default="./i18n",
- help="Set your locale src dir to DIR [default = %default]",
- metavar="DIR")
-
- return cmdParser.parse_args()
-
-if __name__ == "__main__":
- print >> sys.stderr, "You shouldn't run this directly."
Deleted: projects/gettor/gettor_packages.py
===================================================================
--- projects/gettor/gettor_packages.py 2009-01-09 01:21:27 UTC (rev 18033)
+++ projects/gettor/gettor_packages.py 2009-01-09 02:58:32 UTC (rev 18034)
@@ -1,116 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-'''
- gettor_packages.py: Package related stuff
-
- Copyright (c) 2008, Jacob Appelbaum <jacob@xxxxxxxxxxxxx>,
- Christian Fromme <kaner@xxxxxxxxxx>
-
- This is Free Software. See LICENSE for license information.
-
- This module handles all package related functionality
-'''
-
-import os
-import zipfile
-import subprocess
-import gettor_log
-import gettor_config
-import re
-
-__all__ = ["gettorPackages"]
-
-log = gettor_log.getLogger()
-
-class gettorPackages:
-
- packageRegex = { "windows-bundle": "vidalia-bundle-.*.exe$",
- "panther-bundle": "vidalia-bundle-.*-panther.dmg$",
- "tiger-bundle": "vidalia-bundle-.*-tiger.dmg$",
- "source-bundle": "tor-.*.tar.gz",
- }
-
- def __init__(self, mirror, config, silent=False):
- self.mirror = mirror
- self.packageList = {}
- self.distDir = config.getDistDir()
- try:
- entry = os.stat(self.distDir)
- except OSError, e:
- log.error("Bad dist dir %s: %s" % (self.distDir, e))
- raise IOError
- self.packDir = config.getPackDir()
- try:
- entry = os.stat(self.packDir)
- except OSError, e:
- log.error("Bad pack dir %s: %s" % (self.packDir, e))
- raise IOError
- self.rsync = ["rsync"]
- self.rsync.append("-a")
- # Don't download dotdirs
- self.rsync.append("--exclude='.*'")
- if not silent:
- self.rsync.append("--progress")
- self.rsync.append("rsync://%s/tor/dist/current/" % self.mirror)
- self.rsync.append(self.distDir)
-
- def getPackageList(self):
- # Build dict like 'name': 'name.z'
- try:
- for filename in os.listdir(self.packDir):
- self.packageList[filename[:-2]] = self.packDir + "/" + filename
- except OSError, (strerror):
- log.error(_("Failed to build package list: %s") % strerror)
- return None
-
- # Check sanity
- for key, val in self.packageList.items():
- # Remove invalid packages
- if not os.access(val, os.R_OK):
- log.info(_("Warning: %s not accessable. Removing from list.") % val)
- del self.packageList[key]
- return self.packageList
-
- def buildPackages(self):
- for filename in os.listdir(self.distDir):
- for (pack, regex) in self.packageRegex.items():
- if re.compile(regex).match(filename):
- file = self.distDir + "/" + filename
- ascfile = file + ".asc"
- zipFileName = self.packDir + "/" + pack + ".z"
- # If .asc file is there, build Zip file
- if os.access(ascfile, os.R_OK):
- zip = zipfile.ZipFile(zipFileName, "w")
- zip.write(file, os.path.basename(file))
- zip.write(ascfile, os.path.basename(ascfile))
- zip.close()
- self.packageList[pack] = zipFileName
- break
- if len(self.packageList) > 0:
- return True
- else:
- return False
-
- def syncWithMirror(self):
- process = subprocess.Popen(self.rsync)
- process.wait()
- return process.returncode
-
- def getCommandToStr(self):
- """This is useful for cronjob installations
- """
- return ''.join(self.rsync)
-
-if __name__ == "__main__" :
- c = gettor_config.gettorConf()
- p = gettorPackages("rsync.torproject.org", c)
- print "Building packagelist.."
- if p.syncwithMirror() != 0:
- print "Failed."
- exit(1)
- if not p.buildPackageList():
- print "Failed."
- exit(1)
- print "Done."
- for (pack, file) in p.getPackageList().items():
- print "Bundle is mapped to file: ", pack, file
Deleted: projects/gettor/gettor_requests.py
===================================================================
--- projects/gettor/gettor_requests.py 2009-01-09 01:21:27 UTC (rev 18033)
+++ projects/gettor/gettor_requests.py 2009-01-09 02:58:32 UTC (rev 18034)
@@ -1,116 +0,0 @@
-#!/usr/bin/python2.5
-# -*- coding: utf-8 -*-
-"""
- gettor_config.py - Parse configuration file for gettor
-
- Copyright (c) 2008, Jacob Appelbaum <jacob@xxxxxxxxxxxxx>,
- Christian Fromme <kaner@xxxxxxxxxx>
-
- This is Free Software. See LICENSE for license information.
-
- This library implements all of the email parsing features needed for gettor.
-"""
-
-import sys
-import email
-import dkim
-import re
-
-__all__ = ["requestMail"]
-
-class requestMail:
-
- defaultLang = "en"
- supportedLangs = { "en": "English",
- "de": "Deutsch" }
-
- def __init__(self, packages):
- """
- Read message from stdin, parse all the stuff we want to know
- """
- self.rawMessage = sys.stdin.read()
- self.parsedMessage = email.message_from_string(self.rawMessage)
- self.signature = False
- # TODO XXX:
- # This should catch DNS exceptions and fail to verify if we have a
- # dns timeout
- # We also should catch totally malformed messages here
- try:
- if dkim.verify(self.rawMessage):
- self.signature = True
- except:
- pass
-
- # TODO XXX:
- # Scrub this data
- self.replyToAddress = None
- self.replytoAddress = self.parsedMessage["from"]
- # If no package name could be recognized, use 'None'
- self.returnPackage = None
- for line in email.Iterators.body_line_iterator(self.parsedMessage):
- for package in packages.keys():
- match = re.match(package, line)
- if match:
- self.returnPackage = package
-
- self.replyLocale = None
- pattern = re.compile("^Lang:\s+(.*)$")
- for line in email.Iterators.body_line_iterator(self.parsedMessage):
- match = pattern.match(line)
- if match:
- self.replyLocale = match.group(1)
-
- for (key, lang) in self.supportedLangs.items():
- if self.replyLocale == key:
- break
- else:
- self.replyLocale = self.defaultLang
-
- def getRawMessage(self):
- return self.rawMessage
-
- def hasVerifiedSignature(self):
- return self.signature
-
- def getParsedMessage(self):
- return self.parsedMessage
-
- def getReplyTo(self):
- return self.replytoAddress
-
- def getPackage(self):
- return self.returnPackage
-
- def getLocale(self):
- return self.replyLocale
-
-if __name__ == "__main__" :
- """ Give us an email to understand what we think of it. """
- packageList = {
- "windows-bundle": "/var/lib/gettor/pkg/windows-bundle.z",
- "macosx-bundle": "/var/lib/gettor/pkg/macosx-bundle.z",
- "linux-bundle": "/var/lib/gettor/pkg/linux-bundle.z",
- "source-bundle": "/var/lib/gettor/pkg/source-bundle.z"
- }
-
- rmail = requestMail(packageList)
- print "Fetching raw message."
- rawMessage = rmail.getRawMessage()
- # This doesn't work without DNS ( no wifi on board current airplane )
- print "Verifying signature of message."
- signature = rmail.hasVerifiedSignature()
- print "Parsing Message."
- parsedMessage = rmail.getParsedMessage()
- print "Parsing reply."
- parsedReply = rmail.getReplyTo()
- print "Parsing package request."
- package = rmail.getRequestPackage()
- if package == None:
- packageFile = "help"
- else:
- packageFile = packageList[package]
-
- print "The signature status of the email is: %s" % str(signature)
- print "The email requested the following reply address: %s" % parsedReply
- print "It looks like the email requested the following package: %s" % package
- print "We would select the following package file: %s" % packageFile
Deleted: projects/gettor/gettor_responses.py
===================================================================
--- projects/gettor/gettor_responses.py 2009-01-09 01:21:27 UTC (rev 18033)
+++ projects/gettor/gettor_responses.py 2009-01-09 02:58:32 UTC (rev 18034)
@@ -1,165 +0,0 @@
-#!/usr/bin/python2.5
-# -*- coding: utf-8 -*-
-"""
- gettor_config.py - Parse configuration file for gettor
-
- Copyright (c) 2008, Jacob Appelbaum <jacob@xxxxxxxxxxxxx>,
- Christian Fromme <kaner@xxxxxxxxxx>
-
- This is Free Software. See LICENSE for license information.
-
- This library implements all of the email replying features needed for gettor.
-"""
-
-import os
-import smtplib
-import MimeWriter
-import StringIO
-import base64
-import gettext
-
-__all__ = ["gettorResponse"]
-
-
-class gettorResponse:
-
- def __init__(self, config, mailLang="en", logLang="en"):
- self.mailLang = mailLang
- self.logLang = logLang
- self.config = config
-
- def setLang(self, language):
- # XXX: Sorta hack, have nothing better on my mind right now
- # On every entry to a translation-needing function, call this with
- # lang=maillang
- # On every exit of a translation-needing function, call this with
- # lang=loglang
- # :-/
- if self.config:
- trans = gettext.translation("gettor", self.config.getLocaleDir(), [language])
- trans.install()
-
- def sendHelp(self, source, destination):
- """ Send a helpful message to the user interacting with us """
- self.setLang(self.mailLang)
- message = _("""
- Hello! This is the "gettor" robot.
-
- Unfortunately, we won't answer you at this address. We only process
- requests from email services that support "DKIM", which is an email
- feature that lets us verify that the address in the "From" line is
- actually the one who sent the mail.
-
- Gmail and Yahoo Mail both use DKIM. You will have better luck sending
- us mail from one of those.
-
- (We apologize if you didn't ask for this mail. Since your email is from
- a service that doesn't use DKIM, we're sending a short explanation,
- and then we'll ignore this email address for the next day or so.)
- """)
- help = self.constructMessage(message, source, destination)
- try:
- status = self.sendMessage(help, source, destination)
- except:
- status = False
- self.setLang(self.logLang)
-
- return status
-
- def sendPackageHelp(self, packageList, source, destination):
- """ Send a helpful message to the user interacting with us """
- self.setLang(self.mailLang)
- message = _("""
- Hello, This is the "gettor" robot.
-
- I am sorry, but your request was not understood. Please select one of the
- following package names:
-
- """ + "".join([ "\t%s\n" % key for key in packageList.keys()]) + """
- Please send me another email. It only needs a single package name anywhere
- in the body of your email.
- """)
- help = self.constructMessage(message, source, destination)
- try:
- status = self.sendMessage(help, source, destination)
- except:
- status = False
- self.setLang(self.logLang)
-
- return status
-
- def sendPackage(self, source, destination, filename):
- """ Send a message with an attachment to the user interacting with us """
- self.setLang(self.mailLang)
- message = _("""
- Hello! This is the "gettor" robot.
-
- Here's your requested software as a zip file. Please unzip the
- package and verify the signature.
-
- Hint: If your computer has GnuPG installed, use the gpg
- commandline tool as follows after unpacking the zip file:
-
- gpg --verify <packagename>.asc <packagename>
-
- The output should look somewhat like this:
-
- gpg: Good signature from "Roger Dingledine <arma@xxxxxxx>"
-
- If you're not familiar with commandline tools, try looking for
- a graphical user interface for GnuPG on this website:
-
- http://www.gnupg.org/related_software/frontends.html
-
- Have fun.
- """)
- package = self.constructMessage(message, source, destination, filename)
- try:
- status = self.sendMessage(package, source, destination)
- except:
- status = False
- self.setLang(self.mailLang)
-
- return status
-
- def constructMessage(self, messageText, ourAddress, recipient, fileName=None):
- """ Construct a multi-part mime message, including only the first part
- with plaintext."""
-
- message = StringIO.StringIO()
- mime = MimeWriter.MimeWriter(message)
- mime.addheader('MIME-Version', '1.0')
- mime.addheader('Subject', _('Re: Your "gettor" request'))
- mime.addheader('To', recipient)
- mime.addheader('From', ourAddress)
- mime.startmultipartbody('mixed')
-
- firstPart = mime.nextpart()
- emailBody = firstPart.startbody('text/plain')
- emailBody.write(messageText)
-
- # Add a file if we have one
- if fileName:
- filePart = mime.nextpart()
- filePart.addheader('Content-Transfer-Encoding', 'base64')
- emailBody = filePart.startbody('application/zip; name=%s' % os.path.basename(fileName))
- base64.encode(open(fileName, 'rb'), emailBody)
-
- # Now end the mime messsage
- mime.lastpart()
- return message
-
- def sendMessage(self, message, src, dst, smtpserver="localhost:25"):
- try:
- smtp = smtplib.SMTP(smtpserver)
- smtp.sendmail(src, dst, message.getvalue())
- smtp.quit()
- status = True
- except:
- return False
-
- return status
-
-if __name__ == "__main__" :
- print "This is the response handling code. You probably do not want to call it by hand."
-
Property changes on: projects/gettor/lib
___________________________________________________________________
Name: svn:mergeinfo
+
Added: projects/gettor/setup.py
===================================================================
--- projects/gettor/setup.py (rev 0)
+++ projects/gettor/setup.py 2009-01-09 02:58:32 UTC (rev 18034)
@@ -0,0 +1,16 @@
+#!/usr/bin/python2.5
+# (c) 2009 The Tor project
+# GetTor installer & packer
+
+from distutils.core import setup
+
+setup(name='GetTor',
+ version='0.1',
+ description='GetTor enables users to obtain Tor via email',
+ author='Jake Appelbaum, Christian Fromme',
+ author_email='jacob at appelbaum dot net, kaner at strace dot org',
+ url='https://www.torpeoject.org/gettor/',
+ package_dir={'': '.'},
+ packages=['gettor'],
+ py_modules=['GetTor']
+ )