[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[or-cvs] r21930: {weather} Introduce utils module, clean up some more code (in weather/trunk: . lib/weather)
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',