#!/usr/bin/env python
 
"""WebWare for Python adapter for mod_python.
 
Contributed by: Dave Wallace
Modified by Jay Love and Geoff Talvola
 
Here's how I set up my Apache conf:
 
    <Location /WK >
       SetHandler python-program
       # add the directory that contains ModPythonAdapter.py
       PythonPath "sys.path+['/path/to/WebKit']"
       PythonOption AppWorkDir /path/to/dir/with/adapter.address
       PythonHandler WebKit.Adapters.ModPythonAdapter
       PythonDebug On
    </Location>
 
If you used the MakeAppWorkDir.py script to make a seperate
application working directory, specify that path for the AppWorkDir
option, otherwise it should be in your WebKit directory in which case
you should use /path/to/WebKit/adapter.address
 
http://localhost/WK/Welcome
 
You may also send all requests with a .psp extension to WebKit
by adding these lines, outside of any location or directory:
 
    AddHandler python-program .psp
    PythonPath "sys.path+['/path/to/WebKit']"
    PythonHandler modpHandler::pspHandler
    PythonOption AppWorkDir /path/to/dir/with/adapter.address
 
"""
 
# Fix the current working directory -- this gets initialized incorrectly
# for some reason when run using mod_python.
import os
try:
    os.chdir(os.path.abspath(os.path.dirname(__file__)))
except Exception:
    pass
 
from mod_python import apache
 
from socket import *
import sys
 
from Adapter import Adapter
 
debug = 0
__adapter = None
bufsize = 32*1024
 
 
class ModPythonAdapter(Adapter):
 
    def __init__(self, host, port, webKitDir):
        Adapter.__init__(self, webKitDir)
        self.host = host
        self.port = port
 
    def handler(self, req):
        self.reset(req)
        try:
            # Get input
            myInput = self.input(req)
 
            # get the apache module to do the grunt work of
            # building the environment
            env = apache.build_cgi_env(req)
 
            #make sure env is a dictionary (may not be for Apache2)
            env = dict(env)
 
            # Fix up the path
            if 'PATH_INFO' not in env:
                env['PATH_INFO'] = req.path_info
 
            # Communicate with the app server
            respdict = self.transactWithAppServer(env, myInput, self.host, self.port)
 
            # Respond back to Apache
            # self.respond(req, respdict)
 
        except:
            self.handleException(req)
        return apache.OK
 
    def pspHandler(self, req):
        self.reset(req)
        try:
            # Get input
            myInput = self.input(req)
 
            # get the apache module to do the grunt work of
            #   building the environment
            env = apache.build_cgi_env(req)
 
            #make sure env is a dictionary (may not be for Apache2)
            env = dict(env)
 
            # Special environment setup needed for psp handler
            env['WK_ABSOLUTE'] = '1'
 
            # Fix up the path
            if 'PATH_INFO' not in env:
                env['PATH_INFO'] = req.path_info
 
            # Communicate with the app server
            respdict = self.transactWithAppServer(env, myInput, self.host, self.port)
 
            # Respond back to Apache
            self.respond(req, respdict)
 
        except:
            self.handleException(req)
        return apache.OK
 
    def typehandler(self, req):
        """Type handler.
 
        Not being used yet. Probably never be used, because the req.handler
        field is read only in mod_python.
 
        """
        self.reset(req)
        debug = True
        if debug:
            ot = open("/tmp/log2.txt","a")
            ot.write("In type handler\n")
            ot.flush()
 
        if req.filename is None:
            return apache.DECLINED
        fn = req.filename
        ext = fn[fn.rfind("."):]
 
        if debug:
            ot.write("TH: Filename: %s\n"%fn)
            ot.write("TH: Extension: %s\n" % ext)
            ot.write("Req_Handler = %s\n" % req.handler)
            ot.flush()
            ot.close()
 
        if ext == ".psp":
            req.handler = "python-program"
            return apache.OK
        else:
            return apache.DECLINED
 
    def input(self, req):
        myInput = ''
        inp = req.read(bufsize)
        # this looks like bad performance if we don't get it all
        # in the first buffer
        while inp:
            myInput += inp
            inp = req.read(bufsize)
        return myInput
 
    def processResponse(self, data):
        req = self.request()
        if self.doneHeader():
            req.write(data)
            return
        headerData = self.headerData() + data
        self.setHeaderData(headerData)
        headerend = headerData.find("\r\n\r\n")
        if headerend < 0:
            return
        headers = headerData[:headerend]
        for header in headers.split("\r\n"):
            colon = header.find(':')
            name = header[:colon]
            value = header[colon+1:]
            req.headers_out.add(name, value)
            if name.lower() == 'content-type':
                req.content_type = value
            if name.lower() == 'status':
                req.status = int(value.lstrip().split(None, 1)[0])
        req.send_http_header()
        req.write(headerData[headerend+4:])
        self.setDoneHeader(1)
 
    def handleException(self, req):
        import traceback
 
        apache.log_error('WebKit mod_python: Error while responding to request\n')
        apache.log_error('Python exception:\n')
        traceback.print_exc(file=sys.stderr)
 
        output = traceback.format_exception(*sys.exc_info())
        output = ''.join(output)
        output = output.replace('&', '&amp;'
            ).replace('<', '&lt;').replace('>', '&gt;')
        req.write('''
<html><body>
<p><pre>ERROR
 
%s</pre>
</body></html>\n''' % output)
 
    def reset(self, request):
        self.setDoneHeader(0)
        self.setHeaderData('')
        self.setRequest(request)
 
    # These methods are non-thread-safe. On platforms like NT where Apache
    # runs multi-threaded, and the same ModPythonAdapter instance may be
    # called simultaneously for different requests, they need to replaced
    # with threadsafe versions. See WinModPythonAdapter below.
 
    def doneHeader(self):
        return self._doneHeader
 
    def setDoneHeader(self, doneHeader):
        self._doneHeader = doneHeader
 
    def headerData(self):
        return self._headerData
 
    def setHeaderData(self, headerData):
        self._headerData = headerData
 
    def request(self):
        return self._request
 
    def setRequest(self, request):
        self._request = request
 
 
# NT-specific, thread-safe version of ModPythonAdapter. Requires Win32 extensions.
 
if os.name == 'nt':
    import win32api
 
    # This is a Windows-specific thread-safe version of ModPythonAdapter.
    # It replaces the non-thread-safe [set]doneHeader, [set]headerData, and
    # [set]request with versions that store the information keyed by thread ID.
    #
    # This seems a bit hokey, but it was easy to write and it works.
 
    OriginalModPythonAdapter = ModPythonAdapter
 
    class WinModPythonAdapter(OriginalModPythonAdapter):
 
        def __init__(self, host, port, webKitDir):
            OriginalModPythonAdapter.__init__(self, host, port, webKitDir)
            self._threadSafeStorage = {}
 
        def threadSafeValue(self, name):
            threadID = win32api.GetCurrentThreadId()
            return self._threadSafeStorage[threadID, name]
 
        def setThreadSafeValue(self, name, value):
            threadID = win32api.GetCurrentThreadId()
            self._threadSafeStorage[threadID, name] = value
 
        def doneHeader(self):
            return self.threadSafeValue('doneHeader')
 
        def setDoneHeader(self, doneHeader):
            self.setThreadSafeValue('doneHeader', doneHeader)
 
        def headerData(self):
            return self.threadSafeValue('headerData')
 
        def setHeaderData(self, headerData):
            self.setThreadSafeValue('headerData', headerData)
 
        def request(self):
            return self.threadSafeValue('request')
 
        def setRequest(self, request):
            self.setThreadSafeValue('request', request)
 
    # Replace ModPythonAdapter with the Windows-safe version.
    ModPythonAdapter = WinModPythonAdapter
 
 
def _adapter(req):
    global __adapter
    if __adapter is None:
        appWorkDir = req.get_options()['AppWorkDir']
        WEBWARE_ADDRESS_FILE = os.path.join(appWorkDir, 'adapter.address')
        host, port = open(WEBWARE_ADDRESS_FILE).read().split(':')
        port = int(port)
        __adapter = ModPythonAdapter(host, port, appWorkDir)
    return __adapter
 
 
def handler(req):
    return _adapter(req).handler(req)
 
def pspHandler(req):
    return _adapter(req).pspHandler(req)
 
def typehandler(req):
    return _adapter(req).typehandler(req)