[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']
+     )