[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[or-cvs] r12801: Implement HTTP/HTTPS server and SMTP server and client code, (in bridgedb/trunk: . lib/bridgedb)
Author: nickm
Date: 2007-12-13 16:25:00 -0500 (Thu, 13 Dec 2007)
New Revision: 12801
Added:
bridgedb/trunk/lib/bridgedb/Server.py
Modified:
bridgedb/trunk/
bridgedb/trunk/TODO
bridgedb/trunk/lib/bridgedb/Main.py
Log:
r15450@tombo: nickm | 2007-12-13 16:24:44 -0500
Implement HTTP/HTTPS server and SMTP server and client code, mostly.
Property changes on: bridgedb/trunk
___________________________________________________________________
svk:merge ticket from /bridgedb/trunk [r15450] on d9e39d38-0f13-419c-a857-e10a0ce2aa0c
Modified: bridgedb/trunk/TODO
===================================================================
--- bridgedb/trunk/TODO 2007-12-13 15:24:57 UTC (rev 12800)
+++ bridgedb/trunk/TODO 2007-12-13 21:25:00 UTC (rev 12801)
@@ -1,7 +1,8 @@
For dec:
-- https frontend
-- email frontend
+- make mail get configured right.
+- actually invoke server stuff.
+- reload bridges on sighup
- write a README
Later:
@@ -10,5 +11,7 @@
- make all proxies get stuck in their own area.
- implement hop
- implement slightly nicer logging
-
-
+- add captchas
+- decent template for web interface
+- decent template for mail interface
+- implement 'help' command
Modified: bridgedb/trunk/lib/bridgedb/Main.py
===================================================================
--- bridgedb/trunk/lib/bridgedb/Main.py 2007-12-13 15:24:57 UTC (rev 12800)
+++ bridgedb/trunk/lib/bridgedb/Main.py 2007-12-13 21:25:00 UTC (rev 12801)
@@ -19,15 +19,25 @@
BRIDGE_PURPOSE = "bridge",
DB_FILE = [ "./bridgedist" ],
DB_LOG_FILE = [ "./bridgedist.log" ],
- HTTPS_DIST = True,
- EMAIL_DIST = True,
+
N_IP_CLUSTERS = 8,
MASTER_KEY_FILE = [ "./secret_key" ],
+
+ HTTPS_DIST = True,
HTTPS_SHARE=10,
+ HTTPS_PORT=6789,
+ HTTPS_CERT_FILE="cert",
+ HTTPS_KEY_FILE="key",
+ HTTP_UNENCRYPTED_PORT=6788,
+ HTTPS_N_BRIDGES_PER_ANSWER=2,
+
+ EMAIL_DIST = True,
EMAIL_SHARE=10,
EMAIL_DOMAINS = [ "gmail.com", "yahoo.com" ],
EMAIL_DOMAIN_MAP = { "mail.google.com" : "gmail.com",
"googlemail.com" : "gmail.com", },
+ EMAIL_PORT=6725,
+
RESERVED_SHARE=2,
)
Added: bridgedb/trunk/lib/bridgedb/Server.py
===================================================================
--- bridgedb/trunk/lib/bridgedb/Server.py (rev 0)
+++ bridgedb/trunk/lib/bridgedb/Server.py 2007-12-13 21:25:00 UTC (rev 12801)
@@ -0,0 +1,189 @@
+# BridgeDB by Nick Mathewson.
+# Copyright (c) 2007, The Tor Project, Inc.
+# See LICENSE for licensing informatino
+
+from cStringIO import StringIO
+import MimeWriter
+import rfc822
+import time
+
+from zope.interface import implements
+
+from twisted.internet import reactor
+from twisted.internet.defer import Deferred
+import twisted.web.resource
+import twisted.web.server
+import twisted.mail.smtp
+
+class WebResource(twisted.web.resource.Resource):
+ isLeaf = True
+
+ def __init__(self, distributor, schedule, N=1):
+ self.distributor = distributor
+ self.schedule = schedule
+ self.nBridgesToGive = N
+
+ def render_GET(self, request):
+ interval = self.schedule.getInterval(time.time())
+ ip = request.getClientIP()
+ bridges = self.distributor.getBridgesForIP(ip, interval,
+ self.nBridgesToGive)
+ if bridges:
+ answer = "".join("%s\n" % b.getConfigLine() for b in bridges)
+ else:
+ answer = "No bridges available."
+
+ return "<html><body><pre>%s</pre></body></html>" % answer
+
+def addWebServer(cfg, dist, sched):
+ from twised.web.server import Site
+ resource = WebResource(dist, sched, cfg.HTTPS_N_BRIDGES_PER_ANSWER)
+ site = Site(resource)
+ if cfg.HTTP_UNENCRYPTED_PORT:
+ reactor.listenTCP(cfg.HTTP_UNENCRYPTED_PORT, site)
+ if cfg.HTTPS_PORT:
+ from twisted.internet.ssl import DefaultOpenSSLContextFactory
+ from OpenSSL.SSL import SSLv3_METHOD
+ factory = DefaultOpenSSLContextFactory(cfg.HTTPS_KEY_FILE,
+ cfg.HTTPS_CERT_FILE)
+ reactor.listenSSL(cfg.HTTPS_PORT, site, factory)
+ return site
+
+class MailFile:
+ def __init__(self, lines):
+ self.idx = 0
+ def readline(self):
+ try :
+ line = self.lines[self.idx]
+ self.idx += 1
+ return line #Append a \n? XXXX
+ except IndexError:
+ return ""
+
+def getMailResponse(lines, ctx):
+ # Extract data from the headers.
+ msg = rfc822(MailFile(lines))
+ subject = msg.getheader("Subject", None)
+ if not subject: subject = "[no subject]"
+ clientFromAddr = msg.getaddr("From")
+ clientSenderAddr = msg.getaddr("Sender")
+ msgID = msg.getheader("Message-ID")
+ if clientSenderAddr:
+ clientAddr = clientSenderAddr[1]
+ elif clientFromAddr:
+ clientAddr = clientFromAddr[1]
+ else:
+ return None
+ for ln in lines:
+ if ln.strip() in ("get bridges", "Subject: get bridges"):
+ break
+ else:
+ return None
+
+ try:
+ interval = ctx.schedule.getInterval(time.time())
+ bridges = ctx.distributor.getBridgesForEmail(clientAddr,
+ interval, ctx.N)
+ except bridgedb.Dist.BadEmail:
+ return None
+ if not bridges:
+ return None
+
+ # Generate the message.
+ f = StringIO()
+ w = MimeWriter.MimeWriter(f)
+ w.addHeader("From", ctx.fromAddr)
+ w.addHeader("To", clientAddr)
+ w.addHeader("Message-ID", twisted.mail.smtp.messageid())
+ if not subject.startswith("Re:"): subject = "Re: %s"%subject
+ w.addHeader("Subject", subject)
+ w.addHeader("In-Reply-To", msgID)
+ w.addHeader("Date", twisted.mail.smtp.rfc822date())
+ body = w.startbody("text/plain")
+ for b in bridges:
+ body.write("%s\n" % b.getConfigLine())
+
+ f.seek(0)
+ return f
+
+def replyToMail(lines, ctx):
+ sendToUser, response = getMailResponse(lines)
+ if response is None:
+ return
+ d = Deferred()
+ factory = twisted.mail.smtp.SMTPSenderFactory(
+ ctx.fromAddr,
+ sendToUser,
+ StringIO(response),
+ d)
+ reactor.connectTCP(ctx.smtpServer, ctx.smtpPort, factory)
+ return d
+
+class MailContext:
+ def __init__(self, cfg):
+ self.username = "bridges"
+ self.maximumSize = 32*1024
+ self.smtpServer
+ self.smtpPort
+ self.fromAddr
+ self.distributor
+ self.schedule
+
+class MailMessage:
+ implements(twisted.mail.smtp.IMessage)
+
+ def __init__(self, ctx)
+ self.ctx = ctx
+ self.lines = []
+ self.nBytes = 0
+ self.ignoring = False
+
+ def lineReceived(self, line):
+ self.nBytes += len(line)
+ if self.nBytes > ctx.maximumSize:
+ self.ignoring = True
+ else:
+ self.lines.append(line)
+
+ def eomReceived(self):
+ if not self.ignoring:
+ replyToMail(self.lines, self.ctx)
+ return defer.succeed(None)
+
+ def connectionLost(self):
+ pass
+
+class MailDelivery:
+ implements(twisted.mail.smtp.IMessageDelivery)
+ def setBridgeDBContext(self, ctx):
+ self.ctx = ctx
+ def receivedHeader(self, helo, origin, recipients):
+ #XXXX what is this for? what should it be?
+ return "Received: BridgeDB"
+ def validateFrom(self, helo, origin):
+ return origin
+ def validateTo(self, user):
+ if user.dest.local != self.ctx.username:
+ raise twisted.mail.smtp.SMTPBadRcpt(user)
+ return lambda: MailMessage(self.ctx)
+
+class MailFactory(twisted.mail.smtp.SMTPFactory):
+ def __init__(self, *a, **kw):
+ twisted.mail.smtp.SMTPFactory.__init__(self, *a, **kw)
+ self.delivery = MailDelivery()
+
+ def setBridgeDBContext(self, ctx):
+ self.ctx = ctx
+ self.delivery.setBridgeDBContext(ctx)
+
+ def buildProtocol(self, addr):
+ p = twisted.mail.smtp.SMTPFactory.buildProtocol(self, addr)
+ p.delivery = self.delivery
+ return p
+
+def addSMTPServer(cfg, dist, sched):
+ ctx = MailContext() #XXXX
+ factory = MailFactory()
+ factory.setBridgeDBContext(ctx)
+ reactor.listenTCP(cfg.EMAIL_PORT, factory)
+ return factory