[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