#!/usr/local/bin/python
#
# Copyright (c) 2002 Neil Blakey-Milner
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#

from twisted.protocols import http
from twisted.web import server

class SCGITransport:
    def __init__(self, realtransport):
        self.realtransport = realtransport
        self.firstline = 1

    def write(self, data):
        if self.firstline:
            data = data.replace("HTTP/1.1", "Status:")
            self.firstline = 0
        self.realtransport.write(data)

    def writeSequence(self, data):
        for entry in data:
            self.write(entry)

    def setPeer(self, peer):
        self.peer = peer

    def setHost(self, host):
        self.host = host

    def getPeer(self):
        return self.peer

    def getHost(self):
        return self.host

    def loseConnection(self):
        self.realtransport.loseConnection()

class SCGIChannel(http.HTTPChannel):
    def __init__(self):
        http.HTTPChannel.__init__(self)
        self._buffer = ""
        self._maxbufferlength = None
        self.prefix = ""

    def dataReceived(self, data):
        self._buffer = self._buffer + data
        if not self._maxbufferlength:
            idx = self._buffer.index(":")
            if not idx:
                return
            self._maxbufferlength = long(self._buffer[:idx])

        if len(self._buffer) - (idx + 2) == self._maxbufferlength:
            scgistring = self._buffer[idx + 1:]
            items = scgistring.split("\0")
            items = items[:-1]
            assert len(items) % 2 == 0, "malformed headers"
            env = {}
            for i in range(0, len(items), 2):
                env[items[i]] = items[i+1]

            request = self.requestFactory(self, len(self.requests))
            self.requests.append(request)

            if env.has_key("HTTP_USER_AGENT"):
                request.received_headers["User-Agent"] = env["HTTP_USER_AGENT"]
            if env.has_key("HTTP_ACCEPT"):
                request.received_headers["Accept"] = env["HTTP_ACCEPT"]
            if env.has_key("SERVER_NAME"):
                request.received_headers["host"] = env["SERVER_NAME"]
            if env.has_key("CONTENT_LENGTH"):
                request.received_headers["Content-Length"] = env["CONTENT_LENGTH"]

            # XXX: working around use of __version, &c. in HTTPChannel
            self._HTTPChannel__command = env["REQUEST_METHOD"]
            self._HTTPChannel__path = env["REQUEST_URI"][len(self.prefix):]
            self._HTTPChannel__version = env["SERVER_PROTOCOL"]
            self.transport.setPeer(("INET", env["REMOTE_ADDR"], int(env["REMOTE_PORT"])))
            self.transport.setHost(("INET", env["SERVER_NAME"], int(env["SERVER_PORT"])))

            self.allHeadersReceived()
            self.allContentReceived()
                
    def requestDone(self, request):
        self.transport.loseConnection()

    def connectionMade(self):
        self.transport = SCGITransport(self.transport)

class SCGISite(server.Site):
    def __init__(self, resource, prefix = ""):
        server.Site.__init__(self, resource)
        self.protocol = SCGIChannel
        self.prefix = prefix

    # XXX: Fix when twisted.web.server.Site starts using self.protocol()
    def buildProtocol(self, addr):
        """Generate a channel attached to this site.
        """
        channel = self.protocol()
        channel.requestFactory = server.Request
        channel.site = self
        channel.factory = self
        channel.prefix = self.prefix
        return channel

def main():
    from twisted.internet import reactor
    from twisted.web import resource, widgets

    class IndexDisplay(widgets.Presentation):
        template = """<h1>Index</h1>"""

    gdgt = widgets.Gadget()
    gdgt.putWidget('', IndexDisplay())
    site = SCGISite(gdgt, "/jobsearch")
    site.protocol = SCGIChannel
    reactor.listenTCP(4000, site)
    reactor.run()

if __name__ == "__main__":
    main()
