[or-cvs] r20777: {projects} First round of re-organizing GetTor (in projects/gettor: . gettor)

kaner at seul.org kaner at seul.org
Sun Oct 11 16:58:48 UTC 2009


Author: kaner
Date: 2009-10-11 12:58:48 -0400 (Sun, 11 Oct 2009)
New Revision: 20777

Added:
   projects/gettor/gettor/constants.py
   projects/gettor/gettor/utils.py
Modified:
   projects/gettor/GetTor.py
   projects/gettor/gettor/blacklist.py
   projects/gettor/gettor/config.py
   projects/gettor/gettor/gtlog.py
   projects/gettor/gettor/packages.py
   projects/gettor/gettor/requests.py
   projects/gettor/gettor/responses.py
Log:
First round of re-organizing GetTor


Modified: projects/gettor/GetTor.py
===================================================================
--- projects/gettor/GetTor.py	2009-10-11 12:33:01 UTC (rev 20776)
+++ projects/gettor/GetTor.py	2009-10-11 16:58:48 UTC (rev 20777)
@@ -1,53 +1,10 @@
 #!/usr/bin/python2.5
 # -*- coding: utf-8 -*-
-"""
 
- gettor.py by Jacob Appelbaum <jacob at appelbaum.net>,
-              Christian Fromme <kaner at strace.org>
- 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__ = '20090621.01'
+__program__ = 'GetTor.py'
 __url__ = 'https://tor-svn.freehaven.net/svn/tor/trunk/contrib/gettor/'
 __author__ = 'Jacob Appelbaum <jacob at appelbaum.net>, Christian Fromme <kaner at strace.org>'
-__copyright__ = 'Copyright (c) 2008, Jacob Appelbaum, Christian Fromme'
+__copyright__ = 'Copyright (c) 2008, 2009, The Tor Project'
 __license__ = 'See LICENSE for licensing information'
 
 try:
@@ -56,375 +13,81 @@
     antigravity = None
 
 import sys
-import os
-import subprocess
-import gettext
-import re
-import gettor.blacklist
-import gettor.requests
-import gettor.responses
+
 import gettor.gtlog
-import gettor.config
 import gettor.opt
-import gettor.packages
+import gettor.config
+import gettor.requests
+import gettor.responses
+import gettor.utils
 
-# Global logger
-log = None
+log = gettor.gtlog.getLogger()
 
-# XXX Move this into a utils class or something
-def createDir(path):
-    try:
-        log.info("Creating directory %s.." % path)
-        os.makedirs(path)
-    except OSError, e:
-        log.error("Failed to create directory %s: %s" % (path, e))
-        return False
-    return True
+def processMail(conf):
+    """All mail processing happens here. Processing goes as follows:
+    - Parse request. This means: Find out everything we need to reply in 
+      an appropriate manner. Reply address, language, package name.
+      Also try to find out if the user wants split packages and if he has 
+      a valid signature on his mail.
+    - Send reply. Use all information gathered from the request and pass
+      it on to the reply class/method to decide what to do."""
+        
+    log.info("Processing mail..")
+    # Retrieve request from stdin
+    request = gettor.requests.requestMail(conf)
+    replyTo, lang, pack, split, sig = request.parseMail()
+    log.info("Request from %s package %s, lang %s, split %s" \
+                % (replyTo, pack, lang, split))
+    log.info("Signature is %s" % sig)
 
-# 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"
+    # Ok, information aquired. Initiate reply sequence
     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.error("Comilation failed: " % e)
-        return False
-    return True
+        reply = gettor.responses.Response(conf, replyTo, lang, pack, split, sig)
+        reply.sendReply()
+    except:
+        log.error("Sending the reply failed. See previous errors for reasons.")
+        # XXX Error handling???
+        raise
 
-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.info("Not a directory: %s" % localeSrcdir)
-        if not createDir(localeSrcdir):
-            log.error(_("Giving up on %s" % localeSrcdir))
-            return False
-    localeDir = config.getLocaleDir()
-    if not os.path.isdir(localeDir):
-        log.info("Not a directory: %s" % localeDir)
-        if not createDir(localeDir):
-            log.error(_("Giving up on %s" % localeDir))
-            return False
-
-    for root, dirs, files in os.walk(localeSrcdir):
-        # Python lacks 'depth' feature for os.walk()
-        if root != localeSrcdir:
-            continue
-        for dir in dirs:
-            hasDirs = True
-            if dir.startswith("."):
-                continue
-            # We ignore the templates dir for now
-            if dir.startswith("templates"):
-                continue
-            try:
-                poFile = os.path.join(root, dir) + "/gettor.po"
-                # Construct target dir
-                targetDir = localeDir + "/" + dir + "/LC_MESSAGES"
-                if not os.path.isdir(targetDir):
-                    log.info("Not a directory: %s" % targetDir)
-                    if not createDir(targetDir):
-                        log.error(_("Giving up on %s" % targetDir))
-                        return False
-                if installMo(poFile, targetDir) == False:
-                    log.error("Installing .mo files failed.")
-                    return False
-            except Exception:
-                log.error("Error accessing translation files.")
-                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, packs, blackList, whiteList):
-    global log
-
-    packageList = packs.getPackageList()
-    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
-    # XXX TODO: Implement a blacklist check here
-    if (srcEmail == replyTo):
-        log.error(_("Won't send myself emails."))
-        return False
-
-    delayAlert = conf.getDelayAlert()
-    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
-## XXX Hack below while yahoo.cn and yahoo.com have dkim headers that
-## our dkim py thinks always fail. -RD
-    if not signature and not whiteList.lookupListEntry(replyTo) and not re.compile(".*@yahoo.com.cn").match(replyTo) and not re.compile(".*@yahoo.cn").match(replyTo) and not re.compile(".*@gmail.com").match(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))
-            log.info(_("Sending delay alert email: %s") % str(delayAlert))
-            if delayAlert == True:
-                message = _("""
-    Hello, This is the "gettor" robot.
-
-    Thank you for your request. It was successfully understood. Your request is
-    currently being processed. Your package should arrive within the next ten
-    minutes.
-
-    If it doesn't arrive, the package might be too big for your mail provider.
-    Try resending the mail from a gmail.com or yahoo.cn account. Also,
-    try asking for tor-browser-bundle rather than tor-im-browser-bundle,
-    since it's smaller.
-
-    If you have any questions or it doesn't work, you can contact a
-    human at this support email address: tor-assistants at torproject.org
-
-            """)
-                status = resp.sendGenericMessage(srcEmail, replyTo, message)
-                if status != True:
-                    log.info(_("Failed to send delay alert"))
-            if (rmail.getSplitDelivery()):
-                status = resp.sendSplitPackage(srcEmail, replyTo, conf.getPackDir() + "/" + package + ".split")
-            else:
-                status = resp.sendPackage(srcEmail, replyTo, packageList[package])
-            if status != True:
-                log.info(_("Failed at attempt to send package: %s") % str(package))
-                return False
-            else:
-                log.info(_("Package sent: %s") % str(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
+def processOptions(options, conf):
+    """Do everything that's not part of parsing a mail. Prepare GetTor usage,
+    install files, fetch packages, do some black/whitelist voodoo and so on""" 
+    print "Options: ", options
+    # Order matters!
     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.info(_("Sorry, %s is not a directory.") % distDir)
-        if not createDir(distDir):
-            log.error(_("Giving up on %s", 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
-
+        gettor.utils.installTrans(conf, options.i18ndir)
     if options.fetchpackages:
-        if packs.syncWithMirror() != 0:
-            log.error(_("Syncing Tor packages failed."))
-            return False
-        else:
-            log.info(_("Syncing Tor packages done."))
-            success = True
+        gettor.utils.fetchPackages(conf, options.mirror)
     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
+        gettor.utils.prepPackages(conf)
     if options.installcron:
-        if installCron() != 0:
-            log.error(_("Installing cron failed"))
-            return False
-        else:
-            log.info(_("Installing cron done."))
-            success = True
+        gettor.utils.installCron()
     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
+        gettor.utils.addWhitelistEntry(options.whitelist)
     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
+        gettor.utils.addBlacklistEntry(options.blacklist)
     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
+        gettor.utils.lookupAddress(options.lookup)
     if options.clearwl:
-        if not whiteList.removeAll():
-            log.error(_("Deleting whitelist failed."))
-            return False
-        else:
-            log.info(_("Deleting whitelist done."))
-            success = True
+        gettor.utils.clearWhitelist()
     if options.clearbl:
-        if not blackList.removeAll():
-            log.error(_("Deleting blacklist failed."))
-            return False
-        else:
-            log.info(_("Deleting blacklist done."))
-            success = True
+        gettor.utils.clearBlacklist()
 
-    # Break here if preparation work has been done
-    if success is not None:
-        return success
-    
-    # Main loop
-    if not processMail(conf, logLang, packs, blackList,
-                       whiteList):
-        log.error(_("Processing mail failed."))
-        return False
+def main():
+    # Parse command line, setup config and logging
+    options, arguments = gettor.opt.parseOpts()
+    config = gettor.config.Config(options.configfile)
+    gettor.gtlog.initialize()
 
-    return True
+    if sys.stdin.isatty():
+        # We separate this because we need a way to know how we reply to the 
+        # caller: Send mail or just dump to stdout/stderr.
+        processOptions(options, config)
+    else:
+        # We've got mail
+        processMail(config)
 
-if __name__ == "__main__":
-    if not main():
-        generalerror = """
-     An error occured while processing your request to GetTor.  Most likely 
-     this happened because we are too busy at the moment.
+    log.info("Done.")
 
-     Please try again later, sorry for the inconvenience.
-
-     Please note that currently, we can't process HTML emails. You will need 
-     to send plain text.
-
-     Best,
-     Your GetTor robot
-     """
-        # XXX This is a heavy cludge to prevent the mail user to see
-        # "Main loop exited with errors." when something bad happened.
-        # Instead, 'generalerror' is send back to the user. 
-        # If GetTor runs from the command line (indicator: args are present),
-        # we presume the "Main loop exited with errors." is more 
-        # appropriate. However, this should go soon and be replaced by real 
-        # error handling (and mail user informing)
-        #   -- kaner
-        if len(sys.argv) == 1:
-            print >> sys.stderr, generalerror
-        else:
-            print >> sys.stderr, "Main loop exited with errors."
-        exit(1)
-    else:
-        exit(0)
+if __name__ == "__main__":
+    main()

Modified: projects/gettor/gettor/blacklist.py
===================================================================
--- projects/gettor/gettor/blacklist.py	2009-10-11 12:33:01 UTC (rev 20776)
+++ projects/gettor/gettor/blacklist.py	2009-10-11 16:58:48 UTC (rev 20777)
@@ -12,7 +12,7 @@
 
 log = gettor.gtlog.getLogger()
 
-conf = gettor.config.gettorConf()
+conf = gettor.config.Config()
 stateDir = conf.getStateDir()
 blStateDir = conf.getBlStateDir()
 

Modified: projects/gettor/gettor/config.py
===================================================================
--- projects/gettor/gettor/config.py	2009-10-11 12:33:01 UTC (rev 20776)
+++ projects/gettor/gettor/config.py	2009-10-11 16:58:48 UTC (rev 20777)
@@ -66,9 +66,9 @@
 import sys
 import ConfigParser
 
-__all__ = ["gettorConf"]
+__all__ = ["Config"]
 
-class gettorConf:
+class Config:
     '''
     Initialize gettor with default values if one or more values are missing 
     from the config file. This will return entirely default values if the 
@@ -175,6 +175,6 @@
         return self.useConf["delayAlert"][0]
 
 if __name__ == "__main__" :
-    c = gettorConf()
+    c = Config()
     print "# This is a suitable default configuration. Tune to fit your needs."
     c.printConfiguration()

Added: projects/gettor/gettor/constants.py
===================================================================
--- projects/gettor/gettor/constants.py	                        (rev 0)
+++ projects/gettor/gettor/constants.py	2009-10-11 16:58:48 UTC (rev 20777)
@@ -0,0 +1,165 @@
+#!/usr/bin/python2.5
+# -*- coding: utf-8 -*-
+"""
+ constants.py
+
+ Copyright (c) 2008, Jacob Appelbaum <jacob at appelbaum.net>, 
+                     Christian Fromme <kaner at strace.org>
+
+ This is Free Software. See LICENSE for license information.
+
+ This library implements all of the email replying features needed for gettor. 
+"""
+
+import gettext
+
+_ = gettext.gettext
+
+helpmsg = _("""
+    Hello! This is the "gettor" robot.
+
+    Unfortunately, we won't answer you at this address. You should make
+    an account with GMAIL.COM or YAHOO.CN and send the mail from
+    one of those.
+
+    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.
+
+    (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.)
+
+    Please note that currently, we can't process HTML emails or base 64
+    mails. You will need to send plain text.
+
+    If you have any questions or it doesn't work, you can contact a
+    human at this support email address: tor-assistants at torproject.org
+        """)
+
+packagehelpmsg = _("""
+    Hello, This is the "gettor" robot.
+
+    I will mail you a Tor package, if you tell me which one you want.
+    Please select one of the following package names:
+
+        tor-browser-bundle
+        macosx-universal-bundle
+        panther-bundle
+        tor-im-browser-bundle
+        source-bundle
+
+    Please reply to this mail (to gettor at torproject.org), and tell me
+    a single package name anywhere in the body of your email.
+
+    Please note that currently we can't process HTML emails or base64
+    emails. You will need to send plain text.
+
+    If you have any questions or it doesn't work, you can contact a
+    human at this support email address: tor-assistants at torproject.org
+
+        """)
+
+packagemsg = _("""
+    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 at mit.edu>"
+
+    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
+
+    If your Internet connection blocks access to the Tor network, you
+    may need a bridge relay. Bridge relays (or "bridges" for short)
+    are Tor relays that aren't listed in the main directory. Since there
+    is no complete public list of them, even if your ISP is filtering
+    connections to all the known Tor relays, they probably won't be able
+    to block all the bridges.
+
+    You can acquire a bridge by sending an email that contains "get bridges"
+    in the body of the email to the following email address:
+    bridges at torproject.org
+
+    It is also possible to fetch bridges with a web browser at the following
+    url: https://bridges.torproject.org/
+
+    If you have any questions or it doesn't work, you can contact a
+    human at this support email address: tor-assistants at torproject.org
+
+        """)
+
+splitpackagemsg = _("""
+    Hello! This is the "gettor" robot.
+
+    Here's your requested software as a zip file. Please unzip the
+    package and verify the signature.
+
+    IMPORTANT NOTE:
+    Since this is part of a split-file request, you need to wait for 
+    all split files to be received by you before you can save them all
+    into the same directory and unpack them by double-clicking the 
+    first file. 
+
+    Packages might come out of order! Please make sure you received
+    all packages before you attempt to unpack them!
+
+    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 at mit.edu>"
+
+    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
+
+    If your Internet connection blocks access to the Tor network, you
+    may need a bridge relay. Bridge relays (or "bridges" for short)
+    are Tor relays that aren't listed in the main directory. Since there
+    is no complete public list of them, even if your ISP is filtering
+    connections to all the known Tor relays, they probably won't be able
+    to block all the bridges.
+
+    You can acquire a bridge by sending an email that contains "get bridges"
+    in the body of the email to the following email address:
+    bridges at torproject.org
+
+    It is also possible to fetch bridges with a web browser at the following
+    url: https://bridges.torproject.org/
+
+    If you have any questions or it doesn't work, you can contact a
+    human at this support email address: tor-assistants at torproject.org
+
+        """)
+
+delayalertmsg = _("""
+    Hello, This is the "gettor" robot.
+
+    Thank you for your request. It was successfully understood. Your request is
+    currently being processed. Your package should arrive within the next ten
+    minutes.
+
+    If it doesn't arrive, the package might be too big for your mail provider.
+    Try resending the mail from a gmail.com or yahoo.cn account. Also,
+    try asking for tor-browser-bundle rather than tor-im-browser-bundle,
+    since it's smaller.
+
+    If you have any questions or it doesn't work, you can contact a
+    human at this support email address: tor-assistants at torproject.org
+
+            """)

Modified: projects/gettor/gettor/gtlog.py
===================================================================
--- projects/gettor/gettor/gtlog.py	2009-10-11 12:33:01 UTC (rev 20776)
+++ projects/gettor/gettor/gtlog.py	2009-10-11 16:58:48 UTC (rev 20777)
@@ -41,7 +41,7 @@
     # Don't add handlers twice
     if initialized == True:
         return
-    conf = gettor.config.gettorConf() 
+    conf = gettor.config.Config() 
     logger = logging.getLogger('gettor')
     logger.setLevel(loglevel)
     logSubSystem = conf.getLogSubSystem()

Modified: projects/gettor/gettor/packages.py
===================================================================
--- projects/gettor/gettor/packages.py	2009-10-11 12:33:01 UTC (rev 20776)
+++ projects/gettor/gettor/packages.py	2009-10-11 16:58:48 UTC (rev 20777)
@@ -1,7 +1,7 @@
 #!/usr/bin/python
 # -*- coding: utf-8 -*-
 '''
- gettor_packages.py: Package related stuff
+ packages.py: Package related stuff
 
  Copyright (c) 2008, Jacob Appelbaum <jacob at appelbaum.net>, 
                      Christian Fromme <kaner at strace.org>
@@ -18,7 +18,7 @@
 import gettor.config
 import re
 
-__all__ = ["gettorPackages"]
+__all__ = ["Packages"]
 
 log = gettor.gtlog.getLogger()
 
@@ -32,7 +32,7 @@
         return False
     return True
 
-class gettorPackages:
+class Packages:
     #                "bundle name": ("single file regex", "split file regex")
     packageRegex = { "windows-bundle": ("vidalia-bundle-.*.exe$", "vidalia-bundle-.*_split"),
                      "panther-bundle": ("vidalia-bundle-.*-ppc.dmg$", "vidalia-bundle-.*-ppc_split"),
@@ -44,14 +44,13 @@
                      #"torbutton": "torbutton-current.xpi$",
                    }
 
-    def __init__(self, mirror, config, silent=False):
-        self.mirror = mirror
+    def __init__(self, config):
         self.packageList = {}
         self.distDir = config.getDistDir()
         try:
             entry = os.stat(self.distDir)
         except OSError, e:
-            if not createDir(self.Distdir):
+            if not createDir(self.distDir):
                 log.error("Bad dist dir %s: %s" % (self.distDir, e))
                 raise IOError
         self.packDir = config.getPackDir()
@@ -61,14 +60,6 @@
             if not createDir(self.packDir):
                 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'
@@ -76,14 +67,15 @@
             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)
+            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)
+                log.info("Warning: %s not accessable. Removing from list." \
+                            % val)
                 del self.packageList[key]
         return self.packageList
 
@@ -98,13 +90,17 @@
                         if not os.access(packSplitDir, os.R_OK):
                             os.mkdir(packSplitDir)
                     except OSError, e:
-                        log.error(_("Could not create dir %s: %s" % (packSplitDir, e)))
-                    # Loop through split dir, look if every partXX.ZZZ has a matching signature,
-                    # pack them together in a .z
+                        log.error("Could not create dir %s: %s" \
+                                        % (packSplitDir, e))
+                    # Loop through split dir, look if every partXX.ZZZ has a 
+                    # matching signature, pack them together in a .z
                     splitdir = self.distDir + "/" + filename
                     for splitfile in os.listdir(splitdir):
+                        # Skip signature files
+                        if splitfile.endswith(".asc"):
+                            continue
                         if re.compile(".*split.part.*").match(splitfile):
-                            ascfile = splitdir + "/signatures/" + splitfile + ".asc"
+                            ascfile = splitdir + "/" + splitfile + ".asc"
                             file = splitdir + "/" + splitfile
                             zipFileName = packSplitDir + "/" + splitfile + ".z"
                             if os.access(ascfile, os.R_OK) and os.access(file, os.R_OK):
@@ -114,7 +110,7 @@
                                 zip.write(ascfile, os.path.basename(ascfile))
                                 zip.close()
                             else:
-                                log.error(_("Uhm, no signature for %s found" % splitfile))
+                                log.error("Uhm, expected signature file for %s to be: %s" % (file, ascfile))
                                 return False
                 if re.compile(regex_single).match(filename):
                     file = self.distDir + "/" + filename
@@ -131,21 +127,37 @@
         if len(self.packageList) > 0:
             return True
         else:
-            log.error(_("Failed at building packages"))
+            log.error("Failed to build packages")
             return False
 
-    def syncWithMirror(self):
-        process = subprocess.Popen(self.rsync)
+    def syncWithMirror(self, mirror, silent):
+        rsync = ["rsync"]
+        rsync.append("-a")
+        # Don't download dotdirs
+        rsync.append("--exclude='.*'")
+        if not silent:
+            rsync.append("--progress")
+        rsync.append("rsync://%s/tor/dist/current/" % mirror)
+        rsync.append(self.distDir)
+        process = subprocess.Popen(rsync)
         process.wait()
         return process.returncode
 
-    def getCommandToStr(self):
+    def getCommandToStr(self, mirror, silent):
         """This is useful for cronjob installations
         """
+        rsync = ["rsync"]
+        rsync.append("-a")
+        # Don't download dotdirs
+        rsync.append("--exclude='.*'")
+        if not silent:
+            rsync.append("--progress")
+        rsync.append("rsync://%s/tor/dist/current/" % mirror)
+        rsync.append(self.distDir)
         return ''.join(self.rsync)
 
 if __name__ == "__main__" :
-    c = gettor_config.gettorConf()
+    c = gettor_config.Config()
     p = gettorPackages("rsync.torproject.org", c)
     print "Building packagelist.."
     if p.syncwithMirror() != 0:

Modified: projects/gettor/gettor/requests.py
===================================================================
--- projects/gettor/gettor/requests.py	2009-10-11 12:33:01 UTC (rev 20776)
+++ projects/gettor/gettor/requests.py	2009-10-11 16:58:48 UTC (rev 20777)
@@ -17,6 +17,7 @@
 import re
 
 import gettor.gtlog
+import gettor.packages
 
 __all__ = ["requestMail"]
 
@@ -25,13 +26,13 @@
 class requestMail:
 
     defaultLang = "en"
+    # XXX
     supportedLangs = { "en": "English", 
                        "de": "Deutsch" }
 
-    def __init__(self, packages):
+    def __init__(self, config):
+        """ Read message from stdin, parse all the stuff we want to know
         """
-        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
@@ -47,44 +48,52 @@
 
         # 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
         self.splitDelivery = False
+        self.replyLocale = "en"
+        packager = gettor.packages.Packages(config)
+        self.packages = packager.getPackageList()
+
+    def parseMail(self):
         # Parse line by line
         for line in email.Iterators.body_line_iterator(self.parsedMessage):
-            # Remove comments
+            # Remove quotes
             if line.startswith(">"):
                 continue
-            for package in packages.keys():
+            # XXX This is a bit clumsy, but i cant think of a better way
+            # currently. A map also doesnt really help i think. -kaner
+            for package in self.packages.keys():
                 matchme = ".*" + package + ".*"
                 match = re.match(matchme, line)    
                 if match: 
                     self.returnPackage = package
-                    log.info(_("User requested package %s") % self.returnPackage)
-            # If we find 'split' somewhere in the mail, we assume that the user wants
-            # a split delivery
+                    log.info("User requested package %s" % self.returnPackage)
+            # If we find 'split' somewhere in the mail, we assume that the user 
+            # wants a split delivery
             match = re.match(".*split.*", line)
             if match:
-                log.info(_("User requested a split delivery"))
                 self.splitDelivery = True
-
-        self.replyLocale = None
-        pattern = re.compile("^Lang:\s+(.*)$")
-        for line in email.Iterators.body_line_iterator(self.parsedMessage):
-            match = pattern.match(line)
+                log.info("User requested a split delivery")
+            # Default locale is english
+            match = re.match(".*[Ll]ang:\s+(.*)$", line)
             if match:
                 self.replyLocale = match.group(1)
-                log.info(_("User requested locale %s") % self.replyLocale)
+                log.info("User requested locale %s" % self.replyLocale)
 
+        # Actually use a map here later XXX
         for (key, lang) in self.supportedLangs.items():
             if self.replyLocale == key:
                 break
         else:
-            log.info(_("Requested language %s not supported. Falling back to %s") % (self.replyLocale, self.defaultLang))
+            log.info("Requested language %s not supported. Falling back to %s" \
+                        % (self.replyLocale, self.defaultLang))
             self.replyLocale = self.defaultLang
 
+        return (self.replytoAddress, self.replyLocale, \
+                self.returnPackage, self.splitDelivery, self.signature)
+
     def getRawMessage(self):
         return self.rawMessage
 
@@ -106,33 +115,6 @@
     def getSplitDelivery(self):
         return self.splitDelivery
 
-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
+    def getAll(self):
+        return (self.replytoAddress, self.replyLocale, \
+                self.returnPackage, self.splitDelivery, self.signature)

Modified: projects/gettor/gettor/responses.py
===================================================================
--- projects/gettor/gettor/responses.py	2009-10-11 12:33:01 UTC (rev 20776)
+++ projects/gettor/gettor/responses.py	2009-10-11 16:58:48 UTC (rev 20777)
@@ -17,62 +17,114 @@
 import StringIO
 import base64
 import gettext
+import re
 
 import gettor.gtlog
+import gettor.blacklist
+import gettor.constants
 
-__all__ = ["gettorResponse"]
+__all__ = ["Response"]
 
 log = gettor.gtlog.getLogger()
 
-class gettorResponse:
+class Response:
 
-    def __init__(self, config, mailLang="en", logLang="en"):
-        self.mailLang = mailLang
-        self.logLang = logLang
+    def __init__(self, config, replyto, lang, package, split, signature):
         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])
+        self.srcEmail = "GetTor <gettor at torproject.org>"
+        self.replyTo = replyto
+        self.mailLang = lang
+        self.package = package
+        self.splitsend = split
+        self.signature = signature
+        self.whiteList = gettor.blacklist.BWList(config.getWlStateDir())
+        try:
+            trans = gettext.translation("gettor", config.getLocaleDir(), [lang])
             trans.install()
+        except IOError:
+            log.error("Translation fail. Trying running with -r.")
+            raise
 
-    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.
+    def sendReply(self):
+        """All routing decisions take place here."""
+        if not self.signature \
+           and not self.whiteList.lookupListEntry(self.replyTo) \
+           and not re.compile(".*@yahoo.com.cn").match(self.replyTo) \
+           and not re.compile(".*@yahoo.cn").match(self.replyTo) \
+           and not re.compile(".*@gmail.com").match(self.replyTo):
+            blackListed = blackList.lookupListEntry(self.replyTo)
+            if blackListed:
+                log.info("Unsigned messaged to gettor by blacklisted user dropped.")
+                return False
+            else:
+                # Reply with some help and bail out
+                blackList.createListEntry(self.replyTo)
+                sendHelp()
+                log.info("Unsigned messaged to gettor. We issued some help.")
+                return True
+        else:
+            print "Sending mail.."
+            delayAlert = self.config.getDelayAlert()
+            if self.splitsend:
+                print "Sending split packages"
+                self.sendSplitPackage()
+            else:
+                print "Sending non-split package"
+                self.sendPackage()
 
-    Unfortunately, we won't answer you at this address. You should make
-    an account with GMAIL.COM or YAHOO.CN and send the mail from
-    one of those.
+    def sendPackage(self):
+        """ Send a message with an attachment to the user"""
+        packages = gettor.packages.Packages(self.config)
+        packageList = packages.getPackageList()
+        filename = packageList[self.package]
+        message = gettor.constants.packagemsg
+        package = self.constructMessage(message, "", filename)
+        try:
+            status = self.sendMessage(package)
+        except:
+            log.error("Could not send package to user")
+            status = False
 
-    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.
+        return status
 
-    (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.)
+    def sendSplitPackage(self):
+        """XXX XXX XXX alpha state XXX XXX XXX"""
+        splitdir = self.config.getPackDir() + "/" + self.package + ".split"
+        try:
+            entry = os.stat(splitdir)
+        except OSError, e:
+            log.error("Not a valid directory: %s" % splitdir)
+            return False
+        files = os.listdir(splitdir)
+        # Sort the files, so we can send 01 before 02 and so on..
+        files.sort()
+        nFiles = len(files)
+        num = 0
+        for filename in files:
+            fullPath = splitdir + "/" + filename
+            num = num + 1
+            subj = "[GetTor] Split package [%02d / %02d] " % (num, nFiles) 
+            message = gettor.constants.splitpackagemsg
+            package = self.constructMessage(message, subj, fullPath)
+            try:
+                status = self.sendMessage(package)
+            except:
+                log.error("Could not send package %s to user" % filename)
+                # XXX What now? Keep on sending? Bail out? Use might have 
+                # already received 10 out of 12 packages..
+                status = False
 
-    Please note that currently, we can't process HTML emails or base 64
-    mails. You will need to send plain text.
-
-    If you have any questions or it doesn't work, you can contact a
-    human at this support email address: tor-assistants at torproject.org
-        """)
-        help = self.constructMessage(message, source, destination, "")
+        return status
+            
+    def sendHelp(self):
+        """ Send a helpful message to the user interacting with us """
+        message = gettor.constants.helpmsg
+        help = self.constructMessage(message, "")
         try:
-            status = self.sendMessage(help, source, destination)
+            status = self.sendMessage(help)
         except:
             log.error(_("Could not send help message to user"))
             status = False
-        self.setLang(self.logLang)
 
         return status
 
@@ -83,192 +135,40 @@
 ## Somebody should figure out how to automate yet sort. -RD
 ##    """ + "".join([ "\t%s\n" % key for key in packageList.keys()]) + """
 
-    def sendPackageHelp(self, packageList, source, destination):
+    def sendPackageHelp(self, packageList):
         """ Send a helpful message to the user interacting with us """
-        self.setLang(self.mailLang)
-        message = _("""
-    Hello, This is the "gettor" robot.
-
-    I will mail you a Tor package, if you tell me which one you want.
-    Please select one of the following package names:
-
-        tor-browser-bundle
-        macosx-universal-bundle
-        panther-bundle
-        tor-im-browser-bundle
-        source-bundle
-
-    Please reply to this mail (to gettor at torproject.org), and tell me
-    a single package name anywhere in the body of your email.
-
-    Please note that currently we can't process HTML emails or base64
-    emails. You will need to send plain text.
-
-    If you have any questions or it doesn't work, you can contact a
-    human at this support email address: tor-assistants at torproject.org
-
-        """)
-        help = self.constructMessage(message, source, destination, "")
+        message = gettor.constants.packagehelpmsg
+        help = self.constructMessage(message, "")
         try:
-            status = self.sendMessage(help, source, destination)
+            status = self.sendMessage(help)
         except:
             status = False
             log.error(_("Could not send package help message to user"))
-        self.setLang(self.logLang)
 
         return status
 
-    def sendGenericMessage(self, source, destination, message):
+    def sendGenericMessage(self, message):
         """ Send a helpful message of some sort """
-        self.setLang(self.mailLang)
-        help = self.constructMessage(message, source, destination, "")
+        help = self.constructMessage(message, "")
         try:
-            status = self.sendMessage(help, source, destination)
+            status = self.sendMessage(help)
         except:
-            log.error(_("Could not send generic help message to user"))
+            log.error("Could not send generic help message to user")
             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 at mit.edu>"
-
-    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
-
-    If your Internet connection blocks access to the Tor network, you
-    may need a bridge relay. Bridge relays (or "bridges" for short)
-    are Tor relays that aren't listed in the main directory. Since there
-    is no complete public list of them, even if your ISP is filtering
-    connections to all the known Tor relays, they probably won't be able
-    to block all the bridges.
-
-    You can acquire a bridge by sending an email that contains "get bridges"
-    in the body of the email to the following email address:
-    bridges at torproject.org
-
-    It is also possible to fetch bridges with a web browser at the following
-    url: https://bridges.torproject.org/
-
-    If you have any questions or it doesn't work, you can contact a
-    human at this support email address: tor-assistants at torproject.org
-
-        """)
-        package = self.constructMessage(message, source, destination, "", filename)
-        try:
-            status = self.sendMessage(package, source, destination)
-        except:
-            log.error(_("Could not send package to user"))
-            status = False
-        self.setLang(self.mailLang)
-
-        return status
-
-    def sendSplitPackage(self, source, destination, splitdir):
-        message = _("""
-    Hello! This is the "gettor" robot.
-
-    Here's your requested software as a zip file. Please unzip the
-    package and verify the signature.
-
-    IMPORTANT NOTE:
-    Since this is part of a split-file request, you need to wait for 
-    all split files to be received by you before you can save them all
-    into the same directory and unpack them by double-clicking the 
-    first file. 
-
-    Packages might come out of order! Please make sure you received
-    all packages before you attempt to unpack them!
-
-    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 at mit.edu>"
-
-    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
-
-    If your Internet connection blocks access to the Tor network, you
-    may need a bridge relay. Bridge relays (or "bridges" for short)
-    are Tor relays that aren't listed in the main directory. Since there
-    is no complete public list of them, even if your ISP is filtering
-    connections to all the known Tor relays, they probably won't be able
-    to block all the bridges.
-
-    You can acquire a bridge by sending an email that contains "get bridges"
-    in the body of the email to the following email address:
-    bridges at torproject.org
-
-    It is also possible to fetch bridges with a web browser at the following
-    url: https://bridges.torproject.org/
-
-    If you have any questions or it doesn't work, you can contact a
-    human at this support email address: tor-assistants at torproject.org
-
-        """)
-        print splitdir
-        try:
-            entry = os.stat(splitdir)
-        except OSError, e:
-            log.error(_("Not a valid directory: %s" % splitdir))
-            return False
-        files = os.listdir(splitdir)
-        # Sort the files, so we can send 01 before 02 and so on..
-        files.sort()
-        nFiles = len(files)
-        num = 0
-        for filename in files:
-            fullPath = splitdir + "/" + filename
-            num = num + 1
-            subj = "[gettor] Split package [%02d / %02d] " % (num, nFiles) 
-            package = self.constructMessage(message, source, destination, subj, fullPath)
-            try:
-                status = self.sendMessage(package, source, destination)
-            except:
-                log.error(_("Could not send package %s to user" % filename))
-                # XXX What now? Keep on sending? Bail out? Use might have 
-                # already received 10 out of 12 packages..
-                status = False
-        self.setLang(self.mailLang)
-
-        return status
-            
-
-    def constructMessage(self, messageText, ourAddress, recipient, subj, fileName=None):
+    def constructMessage(self, messageText, subj, fileName=None):
         """ Construct a multi-part mime message, including only the first part
         with plaintext."""
 
         if subj == "":
-            subj =_('Re: Your "gettor" request')
+            subj =_('[GetTor] Your request')
         message = StringIO.StringIO()
         mime = MimeWriter.MimeWriter(message)
         mime.addheader('MIME-Version', '1.0')
         mime.addheader('Subject', subj)
-        mime.addheader('To', recipient)
-        mime.addheader('From', ourAddress)
+        mime.addheader('To', self.replyTo)
+        mime.addheader('From', self.srcEmail)
         mime.startmultipartbody('mixed')
 
         firstPart = mime.nextpart()
@@ -286,10 +186,10 @@
         mime.lastpart()
         return message
 
-    def sendMessage(self, message, src, dst, smtpserver="localhost:25"):
+    def sendMessage(self, message, smtpserver="localhost:25"):
         try:
             smtp = smtplib.SMTP(smtpserver)
-            smtp.sendmail(src, dst, message.getvalue())
+            smtp.sendmail(self.srcEmail, self.replyTo, message.getvalue())
             smtp.quit()
             status = True
         except smtplib.SMTPAuthenticationError:
@@ -324,7 +224,3 @@
             return False
 
         return status
-
-if __name__ == "__main__" :
-    print "This is the response handling code. You probably do not want to call it by hand."
-

Added: projects/gettor/gettor/utils.py
===================================================================
--- projects/gettor/gettor/utils.py	                        (rev 0)
+++ projects/gettor/gettor/utils.py	2009-10-11 16:58:48 UTC (rev 20777)
@@ -0,0 +1,247 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+'''
+ utils.py: Useful helper routines
+
+ Copyright (c) 2008, Jacob Appelbaum <jacob at appelbaum.net>, 
+                     Christian Fromme <kaner at strace.org>
+
+ This is Free Software. See LICENSE for license information.
+
+ This module handles all package related functionality
+'''
+
+import os
+
+import gettor.gtlog
+import gettor.blacklist
+import gettor.packages
+
+log = gettor.gtlog.getLogger()
+
+def createDir(path):
+    """Helper routine that creates a given directory"""
+    try:
+        log.info("Creating directory %s.." % path)
+        os.makedirs(path)
+    except OSError, e:
+        log.error("Failed to create directory %s: %s" % (path, e))
+        return False
+    return True
+
+def installTrans(conf, dir):
+    """Install all translation files to 'dir'"""
+    log.info("Installing translation files..")
+    hasDirs = None
+
+    if conf is None:
+        log.error("Bad arg.")
+        return False
+    if not os.path.isdir(localeSrcdir):
+        log.info("Not a directory: %s" % localeSrcdir)
+        if not createDir(localeSrcdir):
+            log.error(_("Giving up on %s" % localeSrcdir))
+            return False
+    localeDir = conf.getLocaleDir()
+    if not os.path.isdir(localeDir):
+        log.info("Not a directory: %s" % localeDir)
+        if not createDir(localeDir):
+            log.error(_("Giving up on %s" % localeDir))
+            return False
+
+    # XXX: Warn if there is no translation files anywhere..
+    for root, dirs, files in os.walk(localeSrcdir):
+        # Python lacks 'depth' feature for os.walk()
+        if root != localeSrcdir:
+            continue
+        for dir in dirs:
+            hasDirs = True
+            if dir.startswith("."):
+                continue
+            # We ignore the templates dir for now
+            if dir.startswith("templates"):
+                continue
+            try:
+                poFile = os.path.join(root, dir) + "/gettor.po"
+                # Construct target dir
+                targetDir = localeDir + "/" + dir + "/LC_MESSAGES"
+                if not os.path.isdir(targetDir):
+                    log.info("Not a directory: %s" % targetDir)
+                    if not createDir(targetDir):
+                        log.error(_("Giving up on %s" % targetDir))
+                        return False
+                if installMo(poFile, targetDir) == False:
+                    log.error("Installing .mo files failed.")
+                    return False
+            except Exception:
+                log.error("Error accessing translation files.")
+                return False
+    if hasDirs is None:
+        log.errpr("Empty locale dir: " % localeSrcdir)
+        return False
+
+    return True
+
+def fetchPackages(conf, mirror):
+    """Fetch Tor packages from a mirror"""
+    log.info("Fetching package files..")
+    try:
+        packs = gettor.packages.Packages(conf)
+    except IOError:
+        log.error("Error initiating package list.")
+        return False
+    if packs.syncWithMirror(mirror, False) != 0:
+        log.error("Syncing Tor packages failed.")
+        return False
+    else:
+        log.info("Syncing Tor packages done.")
+        return True
+
+def prepPackages(conf):
+    """Prepare the downloaded packages in a way so GetTor can work with them"""
+    log.info("Preparing package files..")
+    try:
+        packs = gettor.packages.Packages(conf)
+    except IOError:
+        log.error("Error initiating package list.")
+        return False
+    if not packs.buildPackages():
+        log.error("Building packages failed.")
+        return False
+    else:
+        log.info("Building packages done.")
+        return True
+
+def installCron():
+    log.info("Installing cronjob..")
+    # XXX: Check if cron is installed and understands our syntax?
+    currentCronTab = getCurrentCrontab()
+    path = os.getcwd() + "/" + os.path.basename(sys.argv[0])
+    args = " --clear-blacklist --fetch-packages --prep-packages"
+    newCronTab = currentCronTab + '\n' + '3 2 * * * ' + path + args
+    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 addWhitelistEntry(address):
+    log.info("Adding address to whitelist: %s" % address)
+    try:
+        whiteList = gettor.blacklist.BWList(conf.getWlStateDir())
+    except IOError, e:
+        log.error("Whitelist error: %s" % e)
+        return False
+    if not whiteList.createListEntry(options.whitelist):
+        log.error("Creating whitelist entry failed.")
+        return False
+    else:
+        log.info("Creating whitelist entry ok.")
+        return True
+
+def addBlacklistEntry(address):
+    log.info("Adding address to blacklist: %s" % address)
+    try:
+        blackList = gettor.blacklist.BWList(conf.getBlStateDir())
+    except IOError, e:
+        log.error("Blacklist error: %s" % e)
+        return False
+    if not blackList.createListEntry(options.whitelist):
+        log.error("Creating blacklist entry failed.")
+        return False
+    else:
+        log.info("Creating whitelist entry ok.")
+        return True
+
+def lookupAddress(address):
+    log.info("Lookup address: %s" % address)
+    found = False
+    try:
+        whiteList = gettor.blacklist.BWList(conf.getWlStateDir())
+        blackList = gettor.blacklist.BWList(conf.getBlStateDir())
+    except IOError, e:
+        log.error("White/Blacklist error: %s" % e)
+        return False
+    if whiteList.lookupListEntry(address):
+        log.info("Address '%s' is present in the whitelist." % address)
+        found = True
+    if blackList.lookupListEntry(address):
+        log.info("Address '%s' is present in the blacklist." % address)
+        found = True
+    if not success:
+        log.info("Address '%s' neither in blacklist or whitelist." % address)
+        found = True
+
+    return found
+
+def clearWhitelist():
+    try:
+        whiteList = gettor.blacklist.BWList(conf.getWlStateDir())
+    except IOError, e:
+        log.error("Whitelist error: %s" % e)
+        return False
+    log.info("Clearing whitelist..")
+    if not whiteList.removeAll():
+        log.error("Deleting whitelist failed.")
+        return False
+    else:
+        log.info("Deleting whitelist done.")
+        return True
+
+def clearBlacklist():
+    log.info("Clearing blacklist..")
+    try:
+        blackList = gettor.blacklist.BWList(conf.getBlStateDir())
+    except IOError, e:
+        log.error("Blacklist error: %s" % e)
+        return False
+    if not blackList.removeAll():
+        log.error("Deleting blacklist failed.")
+        return False
+    else:
+        log.info("Deleting blacklist done.")
+        return True
+
+# Helper routines go here ####################################################
+
+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.error("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.info("Not a directory: %s" % localeSrcdir)
+        if not createDir(localeSrcdir):
+            log.error(_("Giving up on %s" % localeSrcdir))
+            return False
+    localeDir = config.getLocaleDir()
+    if not os.path.isdir(localeDir):
+        log.info("Not a directory: %s" % localeDir)
+        if not createDir(localeDir):
+            log.error(_("Giving up on %s" % localeDir))
+            return False
+
+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
+



More information about the tor-commits mailing list