[or-cvs] r21930: {weather} Introduce utils module, clean up some more code (in weather/trunk: . lib/weather)

Christian Fromme kaner at strace.org
Fri Mar 12 18:39:42 UTC 2010


Author: kaner
Date: 2010-03-12 18:39:42 +0000 (Fri, 12 Mar 2010)
New Revision: 21930

Added:
   weather/trunk/lib/weather/utils.py
Modified:
   weather/trunk/Weather.py
   weather/trunk/lib/weather/poller.py
   weather/trunk/lib/weather/queries.py
   weather/trunk/setup.py
Log:
Introduce utils module, clean up some more code


Modified: weather/trunk/Weather.py
===================================================================
--- weather/trunk/Weather.py	2010-03-12 18:22:25 UTC (rev 21929)
+++ weather/trunk/Weather.py	2010-03-12 18:39:42 UTC (rev 21930)
@@ -3,29 +3,20 @@
 import os
 import re
 import sys
-import DNS
-import base64
-import smtplib
-import socket
 from twisted.web import resource, static, server, http
 from twisted.enterprise import adbapi
 from twisted.internet import reactor
 from twisted.internet.task import LoopingCall 
-from email.mime.multipart import MIMEMultipart
-from email.mime.base import MIMEBase
-from email.mime.text import MIMEText
 
 from weather.constants import PAGE_TEMPLATE, PAGE_SIGNUP
 from weather.queries import CHECK_SUBS_Q, INSERT_SUBS_Q, CHECK_SUBS_AUTH_Q, ACK_SUB_Q, UNSUBSCRIBE_Q
 from weather.poller import WeatherPoller
-from weather.config import pollPeriod, URLbase
+from weather.config import pollPeriod, URLbase, mailFrom
+import weather.utils as utils
 
-def pageOut(text):
-    return PAGE_TEMPLATE % text
-
 class WeatherIndex(resource.Resource):
     def render(self, request):
-        return pageOut(PAGE_SIGNUP)
+        return utils.pageOut(PAGE_SIGNUP)
 
 class SubscribeRequest(resource.Resource):
     def __init__(self, dbConn):
@@ -37,11 +28,11 @@
         self.email = request.args['email'][0]
         self.node = request.args['node'][0]
 
-        if not self._checkMail():
+        if not utils.checkMail(self.email):
             return "Error: Bad email address '%s'" % self.email
 
-        self.subs_auth = self._getRandString()
-        self.unsubs_auth = self._getRandString()
+        self.subs_auth = utils.getRandString()
+        self.unsubs_auth = utils.getRandString()
         self._isSubscribedAlready()
         return server.NOT_DONE_YET
 
@@ -55,7 +46,7 @@
         # Do we already have a subscription for this address for this node?
         if len(result) is not 0:
             self.request.setResponseCode(http.OK)
-            self.request.write(pageOut("Error: Already subscribed."))
+            self.request.write(utils.pageOut("Error: Already subscribed."))
             self.request.finish()
         else:
             # Alright, subscribe it
@@ -72,7 +63,8 @@
     def _saved(self, result):
         url = URLbase + "/confirm-subscribe?auth=" + self.subs_auth
         try:
-            self._sendConfirmationMail(url)
+            mailText = CONFIRMATION_MAIL % (self.node, url)
+            utils.sendMail(mailFrom, self.email, mailText)
         except Exception, e:
             self.error = "Unknown error while sending confirmation mail. " + \
                          "Please try again later."
@@ -82,7 +74,7 @@
 
         self.request.setResponseCode(http.OK)
         text = THANKS_OUT % self.email
-        self.request.write(pageOut(text))
+        self.request.write(utils.pageOut(text))
         self.request.finish()
 
     def _rollBack(self):
@@ -93,71 +85,13 @@
 
     def _errOut(self, result):
         self.request.setResponseCode(http.INTERNAL_SERVER_ERROR)
-        self.request.write(pageOut(self.error))
+        self.request.write(utils.pageOut(self.error))
         self.request.finish()
 
     def _errback(self, failure):
         self.error = "Error: %s" % (failure.getErrorMessage())
         self._errOut()
 
-    def _sendConfirmationMail(self, url):
-        message = MIMEMultipart()
-        message['Subject'] = "Tor Weather Subscription Request"
-        message['To'] = self.email
-        message['From'] = mailFrom
-
-        messageText = CONFIRMATION_MAIL % (self.node, url)
-        text = MIMEText(messageText, _subtype="plain", _charset="utf-8")
-        # Add text part
-        message.attach(text)
-
-        # Try to send
-        smtp = smtplib.SMTP("localhost:25")
-        smtp.sendmail(mailFrom, self.email, message.as_string())
-        smtp.quit()
-
-    def _getRandString(self):
-        """Produce a random alphanumeric string for authentication"""
-        r = base64.urlsafe_b64encode(os.urandom(18))[:-1]
-        # some email clients don't like URLs ending in -
-        if r[-1] == "-":    
-            r.replace("-", "x")
-        return r
-
-    def _checkMail(self):
-        # Unsure if this is enough
-        mailValidator = "^[a-zA-Z0-9._%-+]+@([a-zA-Z0-9._%-]+\\.[a-zA-Z]{2,6}$)"
-        mailOk = re.compile(mailValidator)
-        match = mailOk.match(self.email)
-        if match:
-            mailDomain = match.group(1)
-            return self._doDNSLookup(mailDomain)
-        else:
-            return False
-
-    def _doDNSLookup(self, mailDomain):
-        DNS.DiscoverNameServers()
-        querinator = DNS.Request(qtype='mx')
-        try:
-            dnsquery = querinator.req(mailDomain)
-        except DNS.DNSError, type:
-            if type == 'Timeout':
-                return False
-            else:
-                raise
-        if not dnsquery.answers:
-            # No DNS MX records for this domain
-            return False
-
-        return True
-
-    def _checkNode(self):
-        nodeOk = re.compile("(0x)?[a-fA-F0-9]{40}\Z")
-        if nodeOk.match(self.node):
-            return True
-        else:
-            return False
-
 class ConfirmSubscribeRequest(resource.Resource):
     def __init__(self, dbConn):
         self.dbConn = dbConn
@@ -177,7 +111,7 @@
     def _checkRet(self, result, request):
         if len(result) is 0:
             request.setResponseCode(http.OK)
-            request.write(pageOut("Error: No subscription with your auth code"))
+            request.write(utils.pageOut("Error: No subscription with your auth code"))
             request.finish()
         else:
             self.unsubs_auth = str(result[0][0])
@@ -196,13 +130,13 @@
         pageout += "You can unsubscribe anytime with the following link: "
         pageout += link
         pageout += "</p>"
-        request.write(pageOut(pageout))
+        request.write(utils.pageOut(pageout))
         request.finish()
 
     def _errback(self, failure, request):
         request.setResponseCode(http.INTERNAL_SERVER_ERROR)
         text = "Error: %s" % (failure.getErrorMessage())
-        request.write(pageOut(text))
+        request.write(utils.pageOut(text))
         request.finish()
 
 class UnsubscribeRequest(resource.Resource):
@@ -223,13 +157,13 @@
 
     def _deleteDone(self, result, request):
         request.setResponseCode(http.OK)
-        request.write(pageOut("Subscription deleted. Goodbye."))
+        request.write(utils.pageOut("Subscription deleted. Goodbye."))
         request.finish()
 
     def _errback(self, failure, request):
         request.setResponseCode(http.INTERNAL_SERVER_ERROR)
         text = "Error: %s" % (failure.getErrorMessage())
-        request.write(pageOut(text))
+        request.write(utils.pageOut(text))
         request.finish()
 
 class RootResource(resource.Resource):
@@ -246,7 +180,7 @@
 
 def main():
     # Set up database connection
-    dbConn = adbapi.ConnectionPool("sqlite3", "subscriptions.db")
+    dbConn = utils.setupDBConn("subscriptions.db")
     # Set up polling timer
     weatherPoller = WeatherPoller(dbConn)
     pollTimer = LoopingCall(weatherPoller.poller)

Modified: weather/trunk/lib/weather/poller.py
===================================================================
--- weather/trunk/lib/weather/poller.py	2010-03-12 18:22:25 UTC (rev 21929)
+++ weather/trunk/lib/weather/poller.py	2010-03-12 18:39:42 UTC (rev 21930)
@@ -2,15 +2,12 @@
 
 import smtplib
 
-from email.mime.multipart import MIMEMultipart
-from email.mime.base import MIMEBase
-from email.mime.text import MIMEText
-
 from twisted.web import server
 from weather.queries import GETALL_SUBS_Q
 from weather.torping import TorPing
 from weather.config import URLbase, mailFrom
 from weather.constants import REPORT_MAIL
+import weather.utils as utils
 
 class WeatherPoller():
     def __init__(self, dbConn):
@@ -56,17 +53,5 @@
     def _sendNotice(self, dbRow):
         nodeId = dbRow[2]
         unsubsURL =  URLbase + "/unsubscribe?auth=" + str(dbRow[4])
-        message = MIMEMultipart()
-        message['Subject'] = "Tor Weather Subscription Request"
-        message['To'] = dbRow[1]
-        message['From'] = mailFrom
-
-        messageText = REPORT_MAIL % (nodeId, unsubsURL)
-        text = MIMEText(messageText, _subtype="plain", _charset="ascii")
-        # Add text part
-        message.attach(text)
-
-        # Try to send
-        smtp = smtplib.SMTP("localhost:25")
-        smtp.sendmail(mailFrom, dbRow[1], message.as_string())
-        smtp.quit()
+        mailText = REPORT_MAIL % (nodeId, unsubsURL)
+        utils.sendMail(mailFrom, dbRow[1], mailText)

Modified: weather/trunk/lib/weather/queries.py
===================================================================
--- weather/trunk/lib/weather/queries.py	2010-03-12 18:22:25 UTC (rev 21929)
+++ weather/trunk/lib/weather/queries.py	2010-03-12 18:39:42 UTC (rev 21930)
@@ -9,4 +9,6 @@
 ACK_SUB_Q = "UPDATE subscriptions SET subscribed=1 WHERE subs_auth='%s'"
 UNSUBSCRIBE_Q = "DELETE from subscriptions where unsubs_auth='%s'"
 GETALL_SUBS_Q = "SELECT * from subscriptions WHERE subscribed=1"
-
+CREATE_TABLE_Q = """
+    CREATE TABLE IF NOT EXISTS %s (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, email VARCHAR(255) NOT NULL, node VARCHAR(255) NOT NULL, subs_auth VARCHAR(255) NOT NULL, unsubs_auth VARCHAR(255) NOT NULL, subscribed INTEGER NOT NULL)
+"""

Added: weather/trunk/lib/weather/utils.py
===================================================================
--- weather/trunk/lib/weather/utils.py	                        (rev 0)
+++ weather/trunk/lib/weather/utils.py	2010-03-12 18:39:42 UTC (rev 21930)
@@ -0,0 +1,88 @@
+#!/usr/bin/python
+
+import os
+import re
+import DNS
+import base64
+import sqlite3
+import smtplib
+
+from email.mime.multipart import MIMEMultipart
+from email.mime.base import MIMEBase
+from email.mime.text import MIMEText
+from twisted.enterprise import adbapi
+
+from weather.constants import PAGE_TEMPLATE, PAGE_SIGNUP
+from weather.queries import CREATE_TABLE_Q
+
+def setupDBConn(databaseName):
+    """Create database and table in case they don't exist yet"""
+    db = sqlite3.connect(databaseName)
+    db.execute(CREATE_TABLE_Q % "subscriptions")
+    db.close()
+    dbConn = adbapi.ConnectionPool("sqlite3", databaseName)
+    return dbConn
+
+def checkMail(email):
+    """Check if email is a valid address"""
+    mailValidator = "^[a-zA-Z0-9._%-+]+@([a-zA-Z0-9._%-]+\\.[a-zA-Z]{2,6}$)"
+    mailOk = re.compile(mailValidator)
+    match = mailOk.match(email)
+    if match:
+        mailDomain = match.group(1)
+        return doDNSLookup(mailDomain)
+    else:
+        return False
+
+def doDNSLookup(mailDomain):
+    """Do DNS mx lookup of a given domain"""
+    DNS.DiscoverNameServers()
+    querinator = DNS.Request(qtype='mx')
+    try:
+        dnsquery = querinator.req(mailDomain)
+    except DNS.DNSError, type:
+        if type == 'Timeout':
+            return False
+        else:
+            raise
+    if not dnsquery.answers:
+        # No DNS MX records for this domain
+        return False
+    return True
+
+def getRandString():
+    """Produce a random alphanumeric string for authentication"""
+    r = base64.urlsafe_b64encode(os.urandom(18))[:-1]
+    # some email clients don't like URLs ending in -
+    if r[-1] == "-":    
+        r.replace("-", "x")
+    return r
+
+def isValidNodeID(nodeID):
+    """Check if a given Tor node ID looks ok"""
+    nodeOk = re.compile("(0x)?[a-fA-F0-9]{40}\Z")
+    if nodeOk.match(nodeID):
+        return True
+    else:
+        return False
+
+def pageOut(text):
+    """Our great template engine ;-)"""
+    return PAGE_TEMPLATE % text
+
+def sendMail(fromPart, toPart, messageText):
+    """Send a certain mail text with certain From: and certain To: field"""
+    message = MIMEMultipart()
+    message['Subject'] = "Tor Weather Subscription Request"
+    message['To'] = toPart
+    message['From'] = fromPart
+
+    text = MIMEText(messageText, _subtype="plain", _charset="ascii")
+    #text = MIMEText(messageText, _subtype="plain", _charset="utf-8")
+    # Add text part
+    message.attach(text)
+
+    # Try to send
+    smtp = smtplib.SMTP("localhost:25")
+    smtp.sendmail(fromPart, toPart, message.as_string())
+    smtp.quit()

Modified: weather/trunk/setup.py
===================================================================
--- weather/trunk/setup.py	2010-03-12 18:22:25 UTC (rev 21929)
+++ weather/trunk/setup.py	2010-03-12 18:39:42 UTC (rev 21930)
@@ -17,7 +17,6 @@
                           'data/top-middle.png', 
                           'data/top-right.png',
                           'data/stylesheet.css',
-                          'data/subscribe.template',
                           'data/favicon.ico']),
                ('TorCtl',['TorCtl/PathSupport.py',
                           'TorCtl/ScanSupport.py',



More information about the tor-commits mailing list