[or-cvs] r22668: {projects} Adding some more comments to the code so that buddies maybe (in projects/gettor: . lib/gettor)

Christian Fromme kaner at strace.org
Wed Jul 21 14:32:30 UTC 2010


Author: kaner
Date: 2010-07-21 14:32:30 +0000 (Wed, 21 Jul 2010)
New Revision: 22668

Modified:
   projects/gettor/GetTor.py
   projects/gettor/TODO
   projects/gettor/lib/gettor/blacklist.py
   projects/gettor/lib/gettor/config.py
   projects/gettor/lib/gettor/packages.py
   projects/gettor/lib/gettor/requests.py
   projects/gettor/lib/gettor/responses.py
   projects/gettor/lib/gettor/utils.py
Log:
Adding some more comments to the code so that buddies maybe find it easier to understand what GetTor does.
Also, do some cosmetic cleanups


Modified: projects/gettor/GetTor.py
===================================================================
--- projects/gettor/GetTor.py	2010-07-19 22:39:50 UTC (rev 22667)
+++ projects/gettor/GetTor.py	2010-07-21 14:32:30 UTC (rev 22668)
@@ -24,7 +24,8 @@
 log = gettor.gtlog.getLogger()
 
 def processFail(conf, rawMessage, reqval, failedAction, e=None):
-    """This routine gets called when something went wrong with the processing"""
+    """This routine gets called when something went wrong with the processing
+    """
     log.error("Failing to " + failedAction)
     if e is not None:
         log.error("Here is the exception I saw: %s" % sys.exc_info()[0])
@@ -40,7 +41,8 @@
     #        log.info("Failure notification sent to user %s" % reqval.sendTo)
 
 def dumpInfo(reqval):
-    """Dump some info to the logfile"""
+    """Dump some info to the logfile
+    """
     log.info("Request From: %s To: %s Package: %s Lang: %s Split: %s Signature: %s Cmdaddr: %s" % (reqval.replyTo, reqval.toField, reqval.pack, reqval.lang, reqval.split, reqval.sign, reqval.cmdAddr))
 
 def processMail(conf):
@@ -50,8 +52,8 @@
          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."""
-        
+         it on to the reply class/method to decide what to do.
+    """
     rawMessage = ""
     reqval = None
     log.info("Processing mail..")
@@ -79,14 +81,15 @@
 
 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""" 
+       install files, fetch packages, do some black/whitelist voodoo and so on
+    """ 
     # Order matters!
     if options.insttrans:
         m = gettor.utils.installTranslations(conf, options.i18ndir)
     if options.fetchpackages:
         gettor.utils.fetchPackages(conf, options.mirror)
     if options.preppackages:
-        gettor.utils.prepPackages(conf)
+       gettor.utils.prepPackages(conf)
     if options.installcron:
         gettor.utils.installCron()
     if options.whitelist:
@@ -103,7 +106,8 @@
         gettor.utils.setCmdPassword(conf, options.cmdpass)
 
 def main():
-    # Parse command line, setup config and logging
+    """Parse command line, setup config and logging
+    """
     options, arguments = gettor.opt.parseOpts()
     config = gettor.config.Config(options.configfile)
     gettor.gtlog.initialize()

Modified: projects/gettor/TODO
===================================================================
--- projects/gettor/TODO	2010-07-19 22:39:50 UTC (rev 22667)
+++ projects/gettor/TODO	2010-07-21 14:32:30 UTC (rev 22668)
@@ -16,3 +16,11 @@
 - Implement test (-t switch) functionality
 - Add torbutton (Mike, please sign torbutton and populate a proper .asc)
 - Add GetTor to GetTor and it will be able to distribute itself
+- Merge checkInternalEarlyBlacklist() in requests.py with the real blacklist
+  mechanism of GetTor
+  Related:
+  - Make blacklists learn wildcards: Something like "*@torproject.org" as a 
+    reply-to address should really be blacklisted, for instance.
+- Come up with something more elegant than torSpecialPackageExpansion() in
+  requests.py. Hardcoding package names in other places than packages.py is
+  inviting bugs.

Modified: projects/gettor/lib/gettor/blacklist.py
===================================================================
--- projects/gettor/lib/gettor/blacklist.py	2010-07-19 22:39:50 UTC (rev 22667)
+++ projects/gettor/lib/gettor/blacklist.py	2010-07-21 14:32:30 UTC (rev 22668)
@@ -1,4 +1,10 @@
 #!/usr/bin/python2.5
+'''
+ 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 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.
@@ -16,13 +22,19 @@
 
 class BWList:
     def __init__(self, blacklistDir):
-        """A blacklist lives as hash files inside a directory."""
+        """A blacklist lives as hash files inside a directory and is simply a 
+           number of files that represent hashed email addresses.
+        """
         self.blacklistDir = blacklistDir
         # "general" is the main blacklist
         self.createSublist("general")
 
     def createSublist(self, blacklistName):
-        """Create a sub blacklist"""
+        """Create a sub blacklist. A blacklist is built of several sublists, 
+           each for a certain purpose. There are blacklists for many 
+           different types of mail. Users get blacklisted for package sending
+           after they received a package for 7 days, for example.
+        """
         fullDir = os.path.join(self.blacklistDir, blacklistName)
         if not os.path.isdir(fullDir):
             if not gettor.utils.createDir(fullDir):
@@ -30,7 +42,8 @@
                 raise IOError("Bad dir: %s" % fullDir)
 
     def lookupListEntry(self, address, blacklistName="*"):
-        """Check to see if we have a list entry for the given address."""
+        """Check to see if we have a list entry for the given address.
+        """
         if address is None:
            log.error("Argument 'address' is None")
            return False
@@ -42,7 +55,8 @@
         return False
 
     def createListEntry(self, address, blacklistName="general"):
-        """ Create a black- or whitelist entry """
+        """Create a black- or whitelist entry
+        """
         if address is None or blacklistName is None:
            log.error("Bad args in createListEntry()")
            return False
@@ -61,7 +75,8 @@
             return False
 
     def removeListEntry(self, address, blacklistName="*"):
-        """ Remove an entry from the black- or whitelist """
+        """Remove an entry from the black- or whitelist
+        """
         if address is None:
            log.error("Argument 'address' is None")
            return False
@@ -79,6 +94,9 @@
             return False
 
     def removeAll(self, olderThanDays=0):
+        """Remove all blacklist entries that are older than 'olderThanDays'
+           days.
+        """
         for root, dirs, files in os.walk(self.blacklistDir):
             for file in files:
                 rmfile = os.path.join(root, file)
@@ -98,13 +116,17 @@
         return True
 
     def stripEmail(self, address):
-        '''Strip "Bart Foobar <bart at foobar.net>" to "<bart at foobar.net">'''
+        """Strip "Bart Foobar <bart at foobar.net>" to "<bart at foobar.net">
+        """
         match = re.search('<.*?>', address)
         if match is not None:
             return match.group()
-        return address
+        # Make sure to return the address in the form of '<bla at foo.de>'
+        return gettor.utils.normalizeAddress(address)
 
     def getHash(self, address):
+        """Return hash for a given emailaddress
+        """
         emailonly = self.stripEmail(address)
         return str(hashlib.sha1(emailonly).hexdigest())
 

Modified: projects/gettor/lib/gettor/config.py
===================================================================
--- projects/gettor/lib/gettor/config.py	2010-07-19 22:39:50 UTC (rev 22667)
+++ projects/gettor/lib/gettor/config.py	2010-07-21 14:32:30 UTC (rev 22668)
@@ -1,8 +1,6 @@
 #!/usr/bin/python
 # -*- coding: utf-8 -*-
 '''
- gettor_config.py - Parse configuration file for gettor
-
  Copyright (c) 2008, Jacob Appelbaum <jacob at appelbaum.net>, 
                      Christian Fromme <kaner at strace.org>
 
@@ -84,10 +82,9 @@
     '''
 
     def __init__(self, path = os.path.expanduser("~/.gettorrc")):
-        '''
-        Most of the work happens here. Parse config, merge with default values,
-        prepare outConf.
-        '''
+        """Most of the work happens here. Parse config, merge with default values,
+           prepare outConf.
+        """
 
         self.configFile = os.path.expanduser(path)
 
@@ -145,9 +142,8 @@
                 self.outConf.set(sec, dkey, dval)
 
     def printConfiguration(self):
-        '''
-        Print out config file. This works even if there is none
-        '''
+        """Print out config file. This works even if there is none
+        """
         return self.outConf.write(sys.stdout)
 
     # All getter routines live below

Modified: projects/gettor/lib/gettor/packages.py
===================================================================
--- projects/gettor/lib/gettor/packages.py	2010-07-19 22:39:50 UTC (rev 22667)
+++ projects/gettor/lib/gettor/packages.py	2010-07-21 14:32:30 UTC (rev 22668)
@@ -1,14 +1,10 @@
 #!/usr/bin/python
 # -*- coding: utf-8 -*-
 '''
- packages.py: Package related stuff
-
  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
@@ -105,7 +101,8 @@
                 raise IOError
 
     def getPackageList(self):
-        # Build dict like 'name': 'name.z'
+        """Build dict like 'name': 'name.z'
+        """
         try:
             for filename in os.listdir(self.packDir):
                 self.packageList[filename[:-2]] = os.path.join(self.packDir, filename)
@@ -123,8 +120,9 @@
         return self.packageList
 
     def preparePackages(self):
-        # Move some stuff around the way we need it: GetTor expects a flat
-        # file system after rsync is finished   
+        """Move some stuff around the way we need it: GetTor expects a flat
+           file system after rsync is finished   
+        """
         log.info("Preparing Linux Bundles..")
         # Prepare Linux Bundles: Move them from linux/i386/* to 
         # ./i386-* and from linux/x86_64/* to ./x86_64-*
@@ -141,6 +139,9 @@
                     shutil.move(os.path.join(lxarchdir, srcfile), destfile)
 
     def buildPackages(self):
+        """Action! Take all packages in distdir and turn them into GetTor-
+           digestables in packdir
+        """
         for (pack, (regex_single, regex_split)) in self.packageRegex.items():
             for dirname in os.listdir(self.distDir):
                 subdir = os.path.join(self.distDir, dirname)
@@ -172,6 +173,8 @@
             return False
 
     def buildSplitFiles(self, pack, dirname, filename):
+        """Build split file packages
+        """
         packSplitDir = None
         try:
             splitpack = pack + ".split"
@@ -228,6 +231,8 @@
         return True
 
     def initRsync(self, mirror="rsync.torproject.org", silent=False):
+        """Build rsync command for fetching packages
+        """
         # Rsync command 1
         self.rsync = "rsync -a" 
         self.rsync += " "
@@ -296,6 +301,8 @@
         self.rsync += " "
 
     def syncWithMirror(self):
+        """Actually execute rsync
+        """
         process = subprocess.Popen(self.rsync, shell=True)
         process.wait()
 

Modified: projects/gettor/lib/gettor/requests.py
===================================================================
--- projects/gettor/lib/gettor/requests.py	2010-07-19 22:39:50 UTC (rev 22667)
+++ projects/gettor/lib/gettor/requests.py	2010-07-21 14:32:30 UTC (rev 22668)
@@ -1,9 +1,7 @@
 #!/usr/bin/python2.5
 # -*- coding: utf-8 -*-
 """
- gettor_config.py - Parse configuration file for gettor
-
- Copyright (c) 2008, Jacob Appelbaum <jacob at appelbaum.net>, 
+  Copyright (c) 2008, Jacob Appelbaum <jacob at appelbaum.net>, 
                      Christian Fromme <kaner at strace.org>
 
  This is Free Software. See LICENSE for license information.
@@ -24,6 +22,15 @@
 log = gettor.gtlog.getLogger()
 
 class RequestVal:
+    """A simple value class carrying everything that is interesting from a
+       parsed email
+       toField  - Who this mail was sent to
+       replyTo  - Who sent us the mail, this is also who gets the reply
+       lang     - The language the user wants the reply in
+       split    - True if the user wants us to send Tor in split files
+       sign     - True if that mail carried a good DKIM signature
+       cmdAddr  - Special commands from the GetTor admin
+    """
     def __init__(self, toField, replyTo, lang, pack, split, sign, cmdAddr):
         self.toField = toField
         self.replyTo = replyTo
@@ -50,23 +57,27 @@
                        "zh_CN": ("chinese", "zh",) }
 
     def __init__(self, config):
-        """ Read message from stdin, parse all the stuff we want to know
+        """ Read message from stdin, initialize and do some early filtering and
+            sanitization
         """
         # Read email from stdin
         self.rawMessage = sys.stdin.read()
         self.parsedMessage = email.message_from_string(self.rawMessage)
 
-        # WARNING WARNING *** This next line whitelists all addresses ***
+        # WARNING WARNING *** This next line whitelists all addresses
+        #                 *** It exists as long as we don't want to check 
+        #                 *** for DKIM properly
         self.signature = True
+        # WARNING WARNING ***
 
         self.config = config
-        self.gotPlusReq = False
-        self.returnPackage = None
+        self.gotPlusReq = False     # Was this a gettor+lang@ request?
+        self.returnPackage = None   
         self.splitDelivery = False
         self.commandAddress = None
         self.replyLocale = self.defaultLang
         self.replytoAddress = self.parsedMessage["Return-Path"]
-        self.bounce = False
+        self.bounce = False         # Is the mail a bounce? 
         self.defaultFrom = self.config.getDefaultFrom()
         
         # Filter rough edges
@@ -103,6 +114,8 @@
             self.toAddress = self.defaultFrom
 
     def parseMail(self):
+        """Main mail parsing routine. Returns a RequestVal value class
+        """
         if self.parsedMessage.is_multipart():
             for part in self.parsedMessage.walk():
                 if part.get_content_maintype() == "text":
@@ -123,6 +136,10 @@
                           self.commandAddress)
 
     def parseTextPart(self, text):
+        """If we've found a text part in a multipart email or if we just want
+           to parse a non-multipart message, this is the routine to call with
+           the text body as its argument
+        """
         text = self.stripTags(text)
         if not self.gotPlusReq:
             self.matchLang(text)
@@ -140,6 +157,10 @@
         self.torSpecialPackageExpansion()
 
     def matchPlusAddress(self):
+        """See whether the user sent his email to a 'plus' address, for 
+           instance to gettor+fa at tpo. Plus addresses are the current 
+           mechanism to set the reply language
+        """
         regexPlus = '.*(<)?(\w+\+(\w+)@\w+(?:\.\w+)+)(?(1)>)'
         match = re.match(regexPlus, self.toAddress)
         if match:
@@ -151,6 +172,7 @@
             return False
 
     def matchPackage(self, line):
+        """Look up which package the user wants to have"""
         for package in self.packages.keys():
             matchme = ".*" + package + ".*"
             match = re.match(matchme, line, re.DOTALL)    
@@ -160,20 +182,36 @@
                 return
 
     def matchSplit(self, line):
-        # If we find 'split' somewhere we assume that the user wants a split 
-        # delivery
+        """If we find 'split' somewhere we assume that the user wants a split 
+           delivery
+        """
         match = re.match(".*split.*", line, re.DOTALL)
         if match:
             self.splitDelivery = True
             log.info("User requested a split delivery")
 
     def matchLang(self, line):
+        """See if we have a "Lang: <lang>" directive in the mail. If so,
+           set the reply language appropriatly.
+           Note that setting the language in this way is somewhat deprecated.
+           Currently, replay language is chosen by the user with "plus" email
+           addresses (e.g. gettor+fa at tpo)
+        """
         match = re.match(".*[Ll]ang:\s+(.*)$", line, re.DOTALL)
         if match:
             self.replyLocale = match.group(1)
             log.info("User requested locale %s" % self.replyLocale)
 
     def matchCommand(self, line):
+        """Check if we have a command from the GetTor admin in this email.
+           Command lines always consists of the following syntax:
+           'Command: <password> <command part 1> <command part 2>'
+           For the forwarding command, part 1 is the email address of the
+           recipient, part 2 is the package name of the package that needs
+           to be forwarded.
+           The password is checked against the password found in the file
+           configured as cmdPassFile in the GetTor configuration.
+        """
         match = re.match(".*[Cc]ommand:\s+(.*)$", line, re.DOTALL)
         if match:
             log.info("Command received from %s" % self.replytoAddress) 
@@ -191,9 +229,10 @@
             self.commandAddress = address
 
     def torSpecialPackageExpansion(self):
-        # If someone wants one of the localizable packages, add language 
-        # suffix. This isn't nice because we're hard-coding package names here
-        # Attention: This needs to correspond to the  packages in packages.py
+        """If someone wants one of the localizable packages, add language 
+           suffix. This isn't nice because we're hard-coding package names here
+           Attention: This needs to correspond to the  packages in packages.py
+        """
         if self.returnPackage == "tor-browser-bundle" \
                or self.returnPackage == "tor-im-browser-bundle" \
                or self.returnPackage == "linux-browser-bundle-i386" \
@@ -202,7 +241,8 @@
 	    self.returnPackage = self.returnPackage + "_" + self.replyLocale 
 
     def stripTags(self, string):
-        """Simple HTML stripper"""
+        """Simple HTML stripper
+        """
         return re.sub(r'<[^>]*?>', '', string)
 
     def getRawMessage(self):
@@ -231,10 +271,11 @@
                 self.returnPackage, self.splitDelivery, self.signature)
 
     def checkLang(self):
-        # Look through our aliases list for languages and check if the user
-        # requested an alias rather than an 'official' language name. If he 
-        # does, map back to that official name. Also, if the user didn't 
-        # request a language we support, fall back to default
+        """Look through our aliases list for languages and check if the user
+           requested an alias rather than an 'official' language name. If he 
+           does, map back to that official name. Also, if the user didn't 
+           request a language we support, fall back to default.
+        """
         for (lang, aliases) in self.supportedLangs.items():
             if lang == self.replyLocale:
                 log.info("User requested lang %s" % lang)
@@ -253,12 +294,22 @@
             return
 
     def checkInternalEarlyBlacklist(self):
+        """This is called by doEarlyFilter() and makes it possible to add
+           hardcoded blacklist entries
+           XXX: This should merge somehow with the GetTor blacklisting
+                mechanism at some point
+        """
         if re.compile(".*@.*torproject.org.*").match(self.replytoAddress):
             return True
         else:
             return False
             
     def doEarlyFilter(self):
+        """This exists to by able to drop mails as early as possible to avoid
+           mail loops and other terrible things. 
+           Calls checkInternalEarlyBlacklist() to filter unwanted sender 
+           addresses
+        """
         # Make sure we drop bounce mails
         if self.replytoAddress == "<>":
                 log.info("We've received a bounce")

Modified: projects/gettor/lib/gettor/responses.py
===================================================================
--- projects/gettor/lib/gettor/responses.py	2010-07-19 22:39:50 UTC (rev 22667)
+++ projects/gettor/lib/gettor/responses.py	2010-07-21 14:32:30 UTC (rev 22668)
@@ -1,8 +1,6 @@
 #!/usr/bin/python2.5
 # -*- coding: utf-8 -*-
 """
- gettor_config.py - Parse configuration file for gettor
-
  Copyright (c) 2008, Jacob Appelbaum <jacob at appelbaum.net>, 
                      Christian Fromme <kaner at strace.org>
 
@@ -24,8 +22,6 @@
 import gettor.gtlog
 import gettor.blacklist
 
-
-
 __all__ = ["Response"]
 
 log = gettor.gtlog.getLogger()
@@ -33,25 +29,31 @@
 trans = None
 
 def sendNotification(config, sendFr, sendTo):
-    """Send notification to user"""
+    """Send notification to user
+    """
     response = Response(config, sendFr, sendTo, None, "", False, True, "")
     message = gettor.constants.mailfailmsg
     return response.sendGenericMessage(message)
 
 class Response:
-
     def __init__(self, config, reqval):
+        """Intialize the reply class. The most important values are passed in
+           via the 'reqval' RequestValue class. Initialize the locale subsystem
+           and do early blacklist checks of reply-to addresses 
+        """
         self.config = config
         self.reqval = reqval
+        # Set default From: field for reply. Defaults to gettor at torproject.org
         if reqval.toField is None:
-            self.srcEmail = "GetTor <gettor at torproject.org>"
+            self.srcEmail = config.getDefaultFrom()
         else:
             self.srcEmail = reqval.toField
         self.replyTo = reqval.replyTo
+        # Make sure we know who to reply to. If not, we can't continue   
         assert self.replyTo is not None, "Empty reply address."
-        # Default lang is en
+        # Set default lang in none is set. Defaults to 'en'
         if reqval.lang is None:
-            reqval.lang = "en"
+            reqval.lang = config.getLocale()
         self.mailLang = reqval.lang
         self.package = reqval.pack
         self.splitsend = reqval.split
@@ -64,11 +66,13 @@
         else:
             self.sendTo = self.replyTo
 
+        # Initialize the reply language usage
         try:
             localeDir = config.getLocaleDir()
             trans = gettext.translation("gettor", localeDir, [reqval.lang])
             trans.install()
-            # OMG TEH HACK!!
+            # OMG TEH HACK!! Constants need to be imported *after* we've 
+            # initialized the locale/gettext subsystem
             import gettor.constants
         except IOError:
             log.error("Translation fail. Trying running with -r.")
@@ -78,12 +82,15 @@
         self.whiteList = gettor.blacklist.BWList(config.getWlStateDir())
         self.blackList = gettor.blacklist.BWList(config.getBlStateDir())
         # Check blacklist section 'general' list & Drop if necessary
+        # XXX: This should learn wildcards
         blacklisted = self.blackList.lookupListEntry(self.replyTo, "general")
         assert blacklisted is not True, \
             "Mail from blacklisted user %s" % self.replyTo 
 
     def sendReply(self):
-        """All routing decisions take place here."""
+        """All routing decisions take place here. Sending of mails takes place
+           here, too.
+        """
         if self.isAllowed():
             # Ok, see what we've got here.
             # Was this a GetTor control command wanting us to forward a package?
@@ -108,6 +115,9 @@
                 return self.sendPackage()
 
     def isAllowed(self):
+        """Do all checks necessary to decide whether the reply-to user is 
+           allowed to get a reply by us.
+        """
         # Check we're happy with sending this user a package
         # XXX This is currently useless since we set self.signature = True
         if not self.signature and not self.cmdAddr \
@@ -127,9 +137,11 @@
         else:
             return True
 
-    def isBlacklisted(self, fname):
+    def isBlacklistedForMessageType(self, fname):
         """This routine takes care that for each function fname, a given user
-           can access it only once"""
+           can access it only once. The 'fname' parameter carries the message
+           type name we're looking for
+        """
         # First of all, check if user is whitelisted: Whitelist beats Blacklist
         if self.whiteList.lookupListEntry(self.replyTo, "general"):
             log.info("Whitelisted user " + self.replyTo)
@@ -145,8 +157,10 @@
             return False
 
     def sendPackage(self):
-        """ Send a message with an attachment to the user"""
-        if self.isBlacklisted("sendPackage"):
+        """ Send a message with an attachment to the user. The attachment is 
+            chosen automatically from the selected self.package
+        """
+        if self.isBlacklistedForMessageType("sendPackage"):
             # Don't send anything
             return False
         log.info("Sending out %s to %s." % (self.package, self.sendTo))
@@ -165,7 +179,10 @@
         return status
 
     def sendSplitPackage(self):
-        if self.isBlacklisted("sendSplitPackage"):
+        """Send a number of messages with attachments to the user. The number
+           depends on the number of parts of the package.
+        """
+        if self.isBlacklistedForMessageType("sendSplitPackage"):
             # Don't send anything
             return False
         splitpack = self.package + ".split"
@@ -180,6 +197,7 @@
         files.sort()
         nFiles = len(files)
         num = 0
+        # For each available split file, send a mail
         for filename in files:
             fullPath = os.path.join(splitdir, filename)
             num = num + 1
@@ -199,18 +217,21 @@
         return status
 
     def sendDelayAlert(self):
-        """ Send a delay notification """
-        if self.isBlacklisted("sendDelayAlert"):
+        """Send a polite delay notification
+        """
+        if self.isBlacklistedForMessageType("sendDelayAlert"):
             # Don't send anything
             return False
         log.info("Sending delay alert to %s" % self.sendTo)
         return self.sendGenericMessage(gettor.constants.delayalertmsg)
             
     def sendHelp(self):
-        if self.isBlacklisted("sendHelp"):
+        """Send a help mail. This happens when a user sent us a request we 
+           didn't really understand
+        """
+        if self.isBlacklistedForMessageType("sendHelp"):
             # Don't send anything
             return False
-        """ Send a helpful message to the user interacting with us """
         log.info("Sending out help message to %s" % self.sendTo)
         return self.sendGenericMessage(gettor.constants.helpmsg)
 
@@ -222,15 +243,18 @@
 ##    """ + "".join([ "\t%s\n" % key for key in packageList.keys()]) + """
 
     def sendPackageHelp(self):
-        """ Send a helpful message to the user interacting with us """
-        if self.isBlacklisted("sendPackageHelp"):
+        """Send a helpful message to the user interacting with us about
+           how to select a proper package
+        """
+        if self.isBlacklistedForMessageType("sendPackageHelp"):
             # Don't send anything
             return False
         log.info("Sending package help to %s" % self.sendTo)
         return self.sendGenericMessage(gettor.constants.multilangpackagehelpmsg)
 
     def sendForwardReply(self, status):
-        " Send a message to the user that issued the forward command """
+        """Send a message to the user that issued the forward command
+        """
         log.info("Sending reply to forwarder '%s'" % self.replyTo)
         message = "Forwarding mail to '%s' status: %s" % (self.sendTo, status)
         # Magic: We're now returning to the original issuer
@@ -238,7 +262,9 @@
         return self.sendGenericMessage(message)
 
     def sendGenericMessage(self, text):
-        """ Send a message of some sort """
+        """Generic message sending routine. All mails that are being sent out
+           go through this function.
+        """
         message = self.constructMessage(text)
         try:
             status = self.sendMessage(message)
@@ -250,8 +276,9 @@
         return status
 
     def constructMessage(self, messageText, subj="[GetTor] Your Request", fileName=None):
-        """ Construct a multi-part mime message, including only the first part
-        with plaintext."""
+        """Construct a multi-part mime message, including only the first part
+           with plaintext.
+        """
 
         message = MIMEMultipart()
         message['Subject'] = subj
@@ -276,6 +303,9 @@
         return message
 
     def sendMessage(self, message, smtpserver="localhost:25"):
+        """Send out message via STMP. If an error happens, be verbose about 
+           the reason
+        """
         try:
             smtp = smtplib.SMTP(smtpserver)
             smtp.sendmail(self.srcEmail, self.sendTo, message.as_string())

Modified: projects/gettor/lib/gettor/utils.py
===================================================================
--- projects/gettor/lib/gettor/utils.py	2010-07-19 22:39:50 UTC (rev 22667)
+++ projects/gettor/lib/gettor/utils.py	2010-07-21 14:32:30 UTC (rev 22668)
@@ -1,14 +1,10 @@
 #!/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
@@ -26,7 +22,8 @@
 log = gettor.gtlog.getLogger()
 
 def createDir(path):
-    """Helper routine that creates a given directory"""
+    """Helper routine that creates a given directory
+    """
     try:
         log.info("Creating directory %s.." % path)
         os.makedirs(path)
@@ -36,7 +33,8 @@
     return True
 
 def dumpMessage(conf, message):
-    """Dump a message to our dumpfile"""
+    """Dump a (mail) message to our dumpfile
+    """
     dumpFile = conf.getDumpFile()
     # Be nice: Create dir if it's not there
     dumpDir = os.path.dirname(dumpFile)
@@ -57,7 +55,8 @@
         return False
 
 def installTranslations(conf, localeSrcdir):
-    """Install all translation files to 'dir'"""
+    """Install all translation files to 'dir'
+    """
     log.info("Installing translation files..")
     hasDirs = None
 
@@ -112,7 +111,8 @@
     return True
 
 def fetchPackages(conf, mirror):
-    """Fetch Tor packages from a mirror"""
+    """Fetch Tor packages from a mirror
+    """
     log.info("Fetching package files..")
     try:
         packs = gettor.packages.Packages(conf, mirror, False)
@@ -127,7 +127,8 @@
         return True
 
 def prepPackages(conf):
-    """Prepare the downloaded packages in a way so GetTor can work with them"""
+    """Prepare the downloaded packages in a way so GetTor can work with them
+    """
     log.info("Preparing package files..")
     try:
         packs = gettor.packages.Packages(conf)
@@ -145,6 +146,8 @@
         return True
 
 def installCron():
+    """Install all needed cronjobs for GetTor
+    """
     log.info("Installing cronjob..")
     # XXX: Check if cron is installed and understands our syntax?
     currentCronTab = getCurrentCrontab()
@@ -159,13 +162,15 @@
     return cronProc.returncode
 
 def addWhitelistEntry(conf, address):
+    """Add an entry to the global whitelist
+    """
     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(prepareAddress(address), "general"):
+    if not whiteList.createListEntry(normalizeAddress(address), "general"):
         log.error("Creating whitelist entry failed.")
         return False
     else:
@@ -173,13 +178,15 @@
         return True
 
 def addBlacklistEntry(conf, address):
+    """Add an entry to the global blacklist
+    """
     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(prepareAddress(address), "general"):
+    if not blackList.createListEntry(normalizeAddress(address), "general"):
         log.error("Creating blacklist entry failed.")
         return False
     else:
@@ -187,6 +194,8 @@
         return True
 
 def lookupAddress(conf, address):
+    """Lookup if a given address is in the blacklist- or whitelist pool
+    """
     log.info("Lookup address: %s" % address)
     found = False
     try:
@@ -209,6 +218,8 @@
     return found
 
 def clearWhitelist(conf):
+    """Delete all entries in the global whitelist
+    """
     try:
         whiteList = gettor.blacklist.BWList(conf.getWlStateDir())
     except IOError, e:
@@ -223,6 +234,9 @@
         return True
 
 def clearBlacklist(conf, olderThanDays):
+    """Delete all entries in the global blacklist that are older than
+       'olderThanDays' days
+    """
     log.info("Clearing blacklist..")
     try:
         blackList = gettor.blacklist.BWList(conf.getBlStateDir())
@@ -237,6 +251,9 @@
         return True
 
 def setCmdPassword(conf, password):
+    """Write the password for the admin commands in the configured file
+       Hash routine used: SHA1
+    """
     log.info("Setting command password")
     passwordHash = str(hashlib.sha1(password).hexdigest())
     cmdPassFile = conf.getCmdPassFile()
@@ -258,6 +275,9 @@
         return False
 
 def verifyPassword(conf, password):
+    """Verify that a given password matches the one provided in the
+       password file
+    """
     candidateHash = str(hashlib.sha1(password).hexdigest())
     cmdPassFile = conf.getCmdPassFile()
     try:
@@ -275,12 +295,19 @@
         return False
 
 def hasExe(filename):
+    """Helper routine for building the packages for GetTor:
+       Check if a file ends in .exe
+    """
     if re.compile(".*.exe.*").match(filename):
         return True
     else:
         return False
 
 def renameExe(filename, renameFile=True):
+    """Helper routine for building the packages for GetTor:
+       If we find a file ending in .exe, we rename it to .ex_RENAME
+       to get past Google's spam filters
+    """
     if renameFile and not os.access(filename, os.R_OK):
         log.error("Could not access file %s" % filename)
         raise OSError
@@ -292,6 +319,8 @@
     return newfilename
 
 def fileIsOlderThan(filename, olderThanDays):
+    """Return True if file 'filename' is older than 'olderThandays'
+    """
     olderThanDays = int(olderThanDays)
     if olderThanDays is not 0:
         daysold = datetime.now() - timedelta(days=olderThanDays)
@@ -304,6 +333,8 @@
     return True
 
 def getVersionStringFromFile(filename):
+    """Return what version string is encoded in Tor package filename
+    """
     regex = "[a-z-]*-([0-9]*\.[0-9]*\.[0-9]*)"
     match = re.match(regex, filename)
     if match:
@@ -312,6 +343,8 @@
         return None
 
 def isNewTorVersion(old, new):
+    """Return True if Tor version string 'new' is newer than 'old'
+    """
     oldsplit = old.split(".")
     newsplit = new.split(".")
     if len(oldsplit) != 3 or len(newsplit) != 3:
@@ -335,9 +368,9 @@
                 # Same version
                 return False
 
-# Helper routines go here ####################################################
-
 def installMo(poFile, targetDir):
+    """Install a certain gettext .mo file
+    """
     global log
     args = os.getcwd() + "/" + poFile + " -o " + targetDir + "/gettor.mo"
     try:
@@ -350,36 +383,19 @@
         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
+    """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 prepareAddress(address):
+def normalizeAddress(address):
     """We need this because we internally store email addresses in this format
-       in the black- and whitelists"""
+       in the black- and whitelists
+    """
     if address.startswith("<"):
         return address
     else:



More information about the tor-commits mailing list