mirror of
https://github.com/Team-Resurgent/XBMC4Xbox.git
synced 2026-08-15 11:18:13 -07:00
If ever adding the XBMC4Xbox source to GitHub use "git add -f -A" or it wont add all the files.
266 lines
8.9 KiB
Python
266 lines
8.9 KiB
Python
##################################################
|
|
# SPYCE - Python-based HTML Scripting
|
|
# Copyright (c) 2002 Rimon Barr.
|
|
#
|
|
# Refer to spyce.py
|
|
# CVS: $Id: fcgi.py 31144 2012-07-15 15:38:56Z buzz $
|
|
##################################################
|
|
|
|
# Taken originally from: http://alldunn.com/python/fcgi.py
|
|
# Edited a fair bit. -- RB
|
|
|
|
__doc__ = 'Python Fast CGI implementation'
|
|
|
|
import os, sys, string, socket, errno, cgi
|
|
from cStringIO import StringIO
|
|
import spyceUtil
|
|
|
|
##################################################
|
|
# Constants
|
|
#
|
|
|
|
# Protol constants: record types
|
|
FCGI_BEGIN_REQUEST = 1
|
|
FCGI_ABORT_REQUEST = 2
|
|
FCGI_END_REQUEST = 3
|
|
FCGI_PARAMS = 4
|
|
FCGI_STDIN = 5
|
|
FCGI_STDOUT = 6
|
|
FCGI_STDERR = 7
|
|
FCGI_DATA = 8
|
|
FCGI_GET_VALUES = 9
|
|
FCGI_GET_VALUES_RESULT = 10
|
|
FCGI_UNKNOWN_TYPE = 11
|
|
FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
|
|
# Protocol constants: FCGI_BEGIN_REQUEST flag mask
|
|
FCGI_KEEP_CONN = 1
|
|
# Protocol constants: FCGI_BEGIN_REQUEST role
|
|
FCGI_RESPONDER = 1
|
|
FCGI_AUTHORIZER = 2
|
|
FCGI_FILTER = 3
|
|
# Protocol constants: FCGI_END_REQUEST protocolStatus
|
|
FCGI_REQUEST_COMPLETE = 0 # ok
|
|
FCGI_CANT_MPX_CONN = 1 # can not multiplex
|
|
FCGI_OVERLOADED = 2 # too busy
|
|
FCGI_UNKNOWN_ROLE = 3 # role unknown
|
|
# Protocol constants: management record types
|
|
FCGI_NULL_REQUEST_ID = 0
|
|
|
|
# Protocol setting: maximum number of requests
|
|
FCGI_MAX_REQS = 1
|
|
FCGI_MAX_CONNS = 1
|
|
# Protocol setting: can multiplex?
|
|
FCGI_MPXS_CONNS = 0
|
|
# Protocol setting: FastCGI protocol version
|
|
FCGI_VERSION_1 = 1
|
|
|
|
##################################################
|
|
# Protocol
|
|
#
|
|
|
|
class record:
|
|
def __init__(self):
|
|
self.version = FCGI_VERSION_1
|
|
self.recType = FCGI_UNKNOWN_TYPE
|
|
self.reqId = FCGI_NULL_REQUEST_ID
|
|
self.content = ""
|
|
def readRecord(self, sock):
|
|
# read content
|
|
hdr = map(ord, self.readExact(sock, 8))
|
|
self.version = hdr[0]
|
|
self.recType = hdr[1]
|
|
self.reqId = (hdr[2]<<8)+hdr[3]
|
|
contentLength = (hdr[4]<<8)+hdr[5]
|
|
paddingLength = hdr[6]
|
|
self.content = self.readExact(sock, contentLength)
|
|
self.readExact(sock, paddingLength)
|
|
# parse
|
|
c = self.content
|
|
if self.recType == FCGI_BEGIN_REQUEST:
|
|
self.role = (ord(c[0])<<8) + ord(c[1])
|
|
self.flags = ord(c[2])
|
|
elif self.recType == FCGI_UNKNOWN_TYPE:
|
|
self.unknownType = ord(c[0])
|
|
elif self.recType == FCGI_GET_VALUES or self.recType == FCGI_PARAMS:
|
|
self.values={}
|
|
pos=0
|
|
while pos < len(c):
|
|
name, value, pos = self.decodePair(c, pos)
|
|
self.values[name] = value
|
|
elif self.recType == FCGI_END_REQUEST:
|
|
b = map(ord, c[0:5])
|
|
self.appStatus = (b[0]<<24) + (b[1]<<16) + (b[2]<<8) + b[3]
|
|
self.protocolStatus = b[4]
|
|
def writeRecord(self, sock):
|
|
content = self.content
|
|
if self.recType == FCGI_BEGIN_REQUEST:
|
|
content = chr(self.role>>8) + chr(self.role & 255) + chr(self.flags) + 5*'\000'
|
|
elif self.recType == FCGI_UNKNOWN_TYPE:
|
|
content = chr(self.unknownType) + 7*'\000'
|
|
elif self.recType==FCGI_GET_VALUES or self.recType==FCGI_PARAMS:
|
|
content = ""
|
|
for i in self.values.keys():
|
|
content = content + self.encodePair(i, self.values[i])
|
|
elif self.recType==FCGI_END_REQUEST:
|
|
v = self.appStatus
|
|
content = chr((v>>24)&255) + chr((v>>16)&255) + chr((v>>8)&255) + chr(v&255)
|
|
content = content + chr(self.protocolStatus) + 3*'\000'
|
|
cLen = len(content)
|
|
eLen = (cLen + 7) & (0xFFFF - 7) # align to an 8-byte boundary
|
|
padLen = eLen - cLen
|
|
hdr = [ self.version, self.recType, self.reqId >> 8,
|
|
self.reqId & 255, cLen >> 8, cLen & 255, padLen, 0]
|
|
hdr = string.joinfields(map(chr, hdr), '')
|
|
sock.send(hdr + content + padLen*'\000')
|
|
def readExact(self, sock, amount):
|
|
data = ''
|
|
while amount and len(data) < amount:
|
|
data = data + sock.recv(amount-len(data))
|
|
return data
|
|
def decodePair(self, s, pos):
|
|
nameLen=ord(s[pos]) ; pos=pos+1
|
|
if nameLen & 128:
|
|
b=map(ord, s[pos:pos+3]) ; pos=pos+3
|
|
nameLen=((nameLen&127)<<24) + (b[0]<<16) + (b[1]<<8) + b[2]
|
|
valueLen=ord(s[pos]) ; pos=pos+1
|
|
if valueLen & 128:
|
|
b=map(ord, s[pos:pos+3]) ; pos=pos+3
|
|
valueLen=((valueLen&127)<<24) + (b[0]<<16) + (b[1]<<8) + b[2]
|
|
name = s[pos:pos+nameLen] ; pos = pos + nameLen
|
|
value = s[pos:pos+valueLen] ; pos = pos + valueLen
|
|
return name, value, pos
|
|
def encodePair(self, name, value):
|
|
l=len(name)
|
|
if l<128: s=chr(l)
|
|
else: s=chr(128|(l>>24)&255)+chr((l>>16)&255)+chr((l>>8)&255)+chr(l&255)
|
|
l=len(value)
|
|
if l<128: s=s+chr(l)
|
|
else: s=s+chr(128|(l>>24)&255)+chr((l>>16)&255)+chr((l>>8)&255)+chr(l&255)
|
|
return s + name + value
|
|
|
|
class FCGI:
|
|
def __init__(self, port=None):
|
|
# environment variables
|
|
try:
|
|
self.FCGI_PORT = int(os.environ['FCGI_PORT'])
|
|
except:
|
|
self.FCGI_PORT = None
|
|
if port: self.FCGI_PORT = port
|
|
self.FCGI_PORT = None
|
|
try:
|
|
self.FCGI_ALLOWED_ADDR = os.environ['FCGI_WEB_SERVER_ADDRS']
|
|
self.FCGI_ALLOWED_ADDR = map(string.strip, string.split(self.FCGI_ALLOWED_ADDR, ','))
|
|
except:
|
|
self.FCGI_ALLOWED_ADDR = None
|
|
self.firstCall = 1
|
|
self.clearState()
|
|
self.socket = None
|
|
self.createServerSocket()
|
|
def createServerSocket(self):
|
|
if self.FCGI_PORT:
|
|
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
s.set_reuse_addr()
|
|
s.bind(('127.0.0.1', self.FCGI_PORT))
|
|
else:
|
|
try:
|
|
s=socket.fromfd(sys.stdin.fileno(), socket.AF_INET, socket.SOCK_STREAM)
|
|
s.getpeername()
|
|
except socket.error, (err, errmsg):
|
|
if err!=errno.ENOTCONN:
|
|
return
|
|
except:
|
|
return
|
|
self.socket = s
|
|
def accept(self):
|
|
if not self.socket: # plain CGI
|
|
if self.firstCall:
|
|
result = sys.stdin, spyceUtil.NoCloseOut(sys.stdout), sys.stderr, os.environ
|
|
else:
|
|
return 0
|
|
else: # FCGI
|
|
if not self.firstCall:
|
|
self.send()
|
|
result = self.recv()
|
|
self.firstCall = 0
|
|
return result
|
|
def clearState(self):
|
|
self.reqID = 0
|
|
self.connection = None
|
|
self.environ = {}
|
|
self.stdin = StringIO()
|
|
self.stderr = StringIO()
|
|
self.stdout = StringIO()
|
|
self.data = StringIO()
|
|
def send(self):
|
|
self.stderr.seek(0,0)
|
|
self.stdout.seek(0,0)
|
|
self.sendStream(FCGI_STDERR, self.stderr.read())
|
|
self.sendStream(FCGI_STDOUT, self.stdout.read())
|
|
r=record()
|
|
r.recType=FCGI_END_REQUEST
|
|
r.reqId=self.reqID
|
|
r.appStatus=0
|
|
r.protocolStatus=FCGI_REQUEST_COMPLETE
|
|
r.writeRecord(self.connection)
|
|
self.connection.close()
|
|
self.clearState()
|
|
def sendStream(self, streamType, streamData):
|
|
r=record()
|
|
r.recType = streamType
|
|
r.reqId = self.reqID
|
|
data = streamData
|
|
while data:
|
|
r.content, data = data[:8192], data[8192:]
|
|
r.writeRecord(self.connection)
|
|
r.content='' ; r.writeRecord(self.connection)
|
|
def recv(self):
|
|
self.connection, address = self.socket.accept()
|
|
# rimtodo: filter to serve only allowed addresses
|
|
# if good_addrs!=None and addr not in good_addrs:
|
|
# raise 'Connection from invalid server!'
|
|
remaining=1
|
|
while remaining:
|
|
r=record(); r.readRecord(self.connection)
|
|
if r.recType in [FCGI_GET_VALUES]: # management records
|
|
if r.recType == FCGI_GET_VALUES:
|
|
r.recType = FCGI_GET_VALUES_RESULT
|
|
v={}
|
|
vars={'FCGI_MAX_CONNS' : FCGI_MAX_CONNS,
|
|
'FCGI_MAX_REQS' : FCGI_MAX_REQS,
|
|
'FCGI_MPXS_CONNS': FCGI_MPXS_CONNS}
|
|
for i in r.values.keys():
|
|
if vars.has_key(i): v[i]=vars[i]
|
|
r.values=vars
|
|
r.writeRecord(self.connection)
|
|
elif r.reqId == 0: # management record of unknown type
|
|
r2 = record()
|
|
r2.recType = FCGI_UNKNOWN_TYPE ; r2.unknownType = r.recType
|
|
r2.writeRecord(self.connection)
|
|
continue
|
|
elif r.reqId != self.reqID and r.recType != FCGI_BEGIN_REQUEST:
|
|
continue # ignore inactive requests
|
|
elif r.recType == FCGI_BEGIN_REQUEST and self.reqID != 0:
|
|
continue # ignore BEGIN_REQUESTs in the middle of request
|
|
if r.recType == FCGI_BEGIN_REQUEST: # begin request
|
|
self.reqID = r.reqId
|
|
if r.role == FCGI_AUTHORIZER: remaining=1
|
|
elif r.role == FCGI_RESPONDER: remaining=2
|
|
elif r.role == FCGI_FILTER: remaining=3
|
|
elif r.recType == FCGI_PARAMS: # environment
|
|
if r.content == '': remaining=remaining-1
|
|
else:
|
|
for i in r.values.keys():
|
|
self.environ[i] = r.values[i]
|
|
elif r.recType == FCGI_STDIN: # stdin
|
|
if r.content == '': remaining=remaining-1
|
|
else: self.stdin.write(r.content)
|
|
elif r.recType == FCGI_DATA: # data
|
|
if r.content == '': remaining=remaining-1
|
|
else: self.data.write(r.content)
|
|
# end while
|
|
self.stdin.seek(0,0)
|
|
self.data.seek(0,0)
|
|
# return CGI environment
|
|
return self.stdin, spyceUtil.NoCloseOut(self.stdout), self.stderr, self.environ
|
|
|