Commits

Anonymous committed 60b1870

Initial.

Comments (0)

Files changed (8)

+(^|/)\..+$
+(^|/).+?\.pyc$
+from runtime import *
+SUCCESS = 0
+FAILURE = -1
+
+IS_NULL     = 0
+IS_LONG     = 1
+IS_DOUBLE   = 2
+IS_BOOL     = 3
+IS_ARRAY    = 4
+IS_OBJECT   = 5
+IS_STRING   = 6
+IS_RESOURCE = 7
+from ctypes import *
+from structures import *
+
+__all__ = [
+    'php_module',
+    ]
+
+php_module = None
+
+for mod_file in ('php5ts.dll', 'libphp5.so'):
+    try:
+        php_module = CDLL(mod_file)
+        break
+    except OSError:
+        pass
+
+if php_module is None:
+    raise ImportError('Failed to find PHP shared library')
+
+php_module.get_zend_version.restype = c_char_p
+php_module.zend_eval_string_ex.restype = c_int
+php_module.zend_eval_string_ex.argtypes = (c_char_p, POINTER(ZValStruct), c_char_p, c_int, c_void_p)
+php_module._zval_dtor_func.argtypes = (POINTER(ZValStruct),)
+php_module._zval_ptr_dtor.argtypes = (POINTER(POINTER(ZValStruct)),)
+php_module.zend_hash_find.restype = c_int
+php_module.zend_hash_find.argtypes = (POINTER(HashTable), POINTER(c_char), c_uint, POINTER(c_void_p))
+php_module.ts_resource_ex.restype = c_void_p
+php_module.zend_stream_open.restype = c_int
+php_module.zend_stream_open.argtypes = (c_char_p, POINTER(ZendFileHandle), c_void_p)
+import sys
+from ctypes import *
+from structures import *
+from constants import *
+from module import *
+from wrappers import GlobalsAccessor
+from cStringIO import StringIO
+
+class PHPRuntimeError(Exception):
+    pass
+
+def typeof(structure, name):
+    for k, v in structure._fields_:
+        if k == name:
+            return v
+    raise KeyError(name)
+
+php_import_environment_variables_proto = CFUNCTYPE(None, POINTER(ZValStruct), c_void_p)
+
+SG = None
+
+class PythonSAPIContext(object):
+    def __init__(self):
+        self.post_data = None
+        self.cookies = ""
+
+class PythonSAPIModule(object):
+    @staticmethod
+    def startup(module):
+        if php_module.php_module_startup(module, c_void_p(0), 0):
+            return FAILURE
+        return SUCCESS
+
+    @staticmethod
+    def ub_write(buf, buf_len, tsrm_ls):
+        sys.stdout.write(buf[0:buf_len])
+        return buf_len
+
+    @staticmethod
+    def flush(context):
+        pass
+
+    @staticmethod
+    def send_headers(*arg, **kwarg):
+        return 1 # SAPI_HEADER_SENT_SUCCESSFULLY
+
+    @staticmethod
+    def register_variables(track_vars_array, tsrm_ls):
+        php_import_environment_variables_proto.in_dll(php_module, 'php_import_environment_variables')(track_vars_array, tsrm_ls)
+
+    @staticmethod
+    def getenv(name, name_len, tsrm_ls):
+        return name[0:name_len]
+
+    def read_post(self, buf, buf_len, tsrm_ls):
+        post_data = self.tls[tsrm_ls].post_data 
+        assert post_data is not None
+        read_data = post_data.read(buf_len)
+        read_data_len = len(read_data)
+        memmove(buf, c_char_p(read_data), read_data_len)
+        return read_data_len
+
+    def read_cookies(self, tsrm_ls):
+        cookies = self.tls[tsrm_ls].cookies
+
+    def __init__(self):
+        self.tls = {}
+        self.module_struct = SAPIModuleStruct(
+            name='python',
+            pretty_name='Python SAPI module',
+            startup=typeof(SAPIModuleStruct, 'startup')(self.startup),
+            shutdown=typeof(SAPIModuleStruct, 'shutdown')(php_module.php_module_shutdown_wrapper),
+            activate=typeof(SAPIModuleStruct, 'activate')(0),
+            deactivate=typeof(SAPIModuleStruct, 'deactivate')(0),
+            ub_write=typeof(SAPIModuleStruct, 'ub_write')(self.ub_write),
+            flush=typeof(SAPIModuleStruct, 'flush')(self.flush),
+            get_stat=typeof(SAPIModuleStruct, 'get_stat')(0),
+            getenv=typeof(SAPIModuleStruct, 'getenv')(self.getenv),
+            sapi_error=typeof(SAPIModuleStruct, 'sapi_error')(php_module.zend_error),
+            header_handler=typeof(SAPIModuleStruct, 'header_handler')(0),
+            send_headers=typeof(SAPIModuleStruct, 'send_headers')(self.send_headers),
+            send_header=typeof(SAPIModuleStruct, 'send_header')(0),
+            read_post=typeof(SAPIModuleStruct, 'read_post')(self.read_post),
+            read_cookies=typeof(SAPIModuleStruct, 'read_cookies')(self.read_cookies),
+            register_server_variables=typeof(SAPIModuleStruct, 'register_server_variables')(self.register_variables),
+            log_message=typeof(SAPIModuleStruct, 'log_message')(0),
+            get_request_time=typeof(SAPIModuleStruct, 'get_request_time')(0),
+            terminate_process=typeof(SAPIModuleStruct, 'terminate_process')(0),
+            php_ini_path_override=c_char_p(0),
+            block_interruptions=typeof(SAPIModuleStruct, 'block_interruptions')(0),
+            unblock_interruptions=typeof(SAPIModuleStruct, 'unblock_interruptions')(0),
+            default_post_reader=typeof(SAPIModuleStruct, 'default_post_reader')(0),
+            treat_data=typeof(SAPIModuleStruct, 'treat_data')(0),
+            php_ini_ignore=0,
+            get_fd=typeof(SAPIModuleStruct, 'get_fd')(0),
+            force_http_10=typeof(SAPIModuleStruct, 'force_http_10')(0),
+            input_filter=typeof(SAPIModuleStruct, 'input_filter')(0),
+            ini_defaults=typeof(SAPIModuleStruct, 'ini_defaults')(0),
+            phpinfo_as_text=1,
+            ini_entries=c_char_p(0),
+            additional_functions=(ZendFunctionEntry*1)(ZendFunctionEntry(name=c_char_p(0))),
+            input_filter_init=typeof(SAPIModuleStruct, 'input_filter_init')(0),
+            )
+        php_module.tsrm_startup(c_int(1), c_int(1), c_int(0), c_char_p(0))
+        php_module.sapi_startup(byref(self.module_struct))
+        if self.startup(byref(self.module_struct)):
+            raise PHPRuntimeError("failed to initialize PHP runtime")
+        global SG
+        SG = GlobalsAccessor(
+            c_int.in_dll(php_module, 'sapi_globals_id').value,
+            SAPIGlobals)
+
+    def __del__(self):
+        self.module_struct.shutdown(byref(self.module_struct))
+        php_module.sapi_shutdown()
+        php_module.tsrm_shutdown()
+
+    def __call__(self, *arg, **kwarg):
+        tsrm_ls = php_module.ts_resource_ex(0, None)
+        self.tls[tsrm_ls] = context = PythonSAPIContext()
+        return SAPIRequest(context, tsrm_ls, *arg, **kwarg)
+
+class SAPIRequest(object):
+    def __init__(self, context, tsrm_ls, request_method="GET", query_string="", content_type="", post_data=""):
+        self.context = context
+        self.tsrm_ls = tsrm_ls
+        self.request_method = request_method
+        self.query_string = query_string
+        self.content_type = content_type
+        self.globals = SG(tsrm_ls)
+        self.globals.server_context = addressof(py_object(context))
+
+        context.post_data = StringIO(post_data)
+
+        request_info = self.globals.request_info
+        request_info.request_method = request_method
+        request_info.query_string = query_string
+        request_info.content_type = content_type
+        request_info.content_length = len(post_data)
+
+    def __enter__(self):
+        if php_module.php_request_startup(self.tsrm_ls):
+            raise PHPRuntimeError("failed to initialize PHP runtime")
+        return self.tsrm_ls
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        php_module.php_request_shutdown(c_void_p(None))
+        return None
+
+python_sapi_module = PythonSAPIModule()
+
+def eval(expr, *arg, **kwarg):
+    from utils import to_python_value
+    with python_sapi_module(*arg, **kwarg) as tsrm_ls:
+        _retval = ZValStruct(type=IS_NULL, refcount=1, is_ref=0)
+        if php_module.zend_eval_string_ex(expr, _retval, "eval", 1, tsrm_ls):
+            raise PHPRuntimeError("failed to execute PHP script")
+        retval = to_python_value(_retval) 
+        php_module._zval_dtor_func(_retval)
+    return retval
+
+def run(file, *arg, **kwarg):
+    with python_sapi_module(*arg, **kwarg) as tsrm_ls:
+        hdl = ZendFileHandle()
+        if php_module.zend_stream_open(file, byref(hdl), tsrm_ls) == FAILURE:
+            raise PHPRuntimeError("failed to open '%s'" % file)
+        if php_module.php_execute_script(byref(hdl), tsrm_ls) == FAILURE:
+            raise PHPRuntimeError("failed to execute PHP script")
+
+if __name__ == '__main__':
+    print eval('$_POST["a"]', request_method='POST', query_string="a=2", content_type='application/x-www-form-urlencoded', post_data='a=123')
+    run("test.php")

php/structures.py

+from ctypes import *
+
+__all__ = [
+    'Stat',
+    'StringValue',
+    'ZValueValue',
+    'ZValStruct',
+    'ZendLList',
+    'SAPIHeaderStruct',
+    'SAPIHeadersStruct',
+    'SAPIModuleStruct',
+    'SAPIPostEntry',
+    'SAPIRequestInfo',
+    'SAPIGlobals',
+    'HashTable',
+    'ArgInfo',
+    'ZendFunctionEntry',
+    'ZendFileHandle',
+    'ZendStream',
+    'ZendMmap',
+    ]
+
+c_int_p = POINTER(c_int)
+c_uint_p = POINTER(c_uint)
+
+class Stat(Structure):
+    _fields_ = [
+        ('st_dev', c_uint64),
+        ('st_ino', c_ulong),
+        ('st_mode', c_uint32),
+        ('st_nlink', c_uint),
+        ('st_uid', c_uint32),
+        ('st_gid', c_uint32),
+        ('st_rdev', c_uint64),
+        ('st_size', c_int64),
+        ('st_blksize', c_long),
+        ('st_blocks', c_long),
+        ('st_atime', c_int),
+        ('st_mtime', c_int),
+        ('st_ctime', c_int),
+        ]
+
+class StringValue(Structure):
+    _fields_ = [
+        ('val', POINTER(c_char)),
+        ('len', c_int),
+        ]
+
+class ObjectValue(Structure):
+    _fields_ = [
+        ('handle', c_uint),
+        ('handlers', c_void_p),
+        ]
+
+class HashTable(Structure):
+    pass
+
+class ZValueValue(Union):
+    _fields_ = [
+        ('lval', c_long),
+        ('dval', c_double),
+        ('str', StringValue),
+        ('ht', POINTER(HashTable)),
+        ('obj', ObjectValue)
+        ]
+
+class ZValStruct(Structure):
+    _fields_ = [ 
+        ('value', ZValueValue),
+        ('refcount', c_uint),
+        ('type', c_uint8),
+        ('is_ref', c_uint8),
+        ]
+
+class ZendLList(Structure):
+    _fields_ = [
+        ('head', c_void_p),
+        ('tail', c_void_p),
+        ('count', c_size_t),
+        ('size', c_size_t),
+        ('dtor', POINTER(CFUNCTYPE(None, c_void_p))),
+        ('persistent', c_uint8),
+        ('traverse_ptr', c_void_p),
+        ]
+
+class SAPIHeaderStruct(Structure):
+    _fields_ = [ 
+        ('header', c_char_p),
+        ('header_len', c_uint),
+        ]
+
+class SAPIHeadersStruct(Structure):
+    _fields_ = [
+        ('headers', ZendLList),
+        ('http_response_code', c_int),
+        ('send_default_content_type', c_uint8),
+        ('mimetype', c_char_p),
+        ('http_status_line', c_char_p),
+        ]
+
+class ArgInfo(Structure):
+    _fields_ = [
+        ('name', c_char_p),
+        ('name_len', c_uint),
+        ('class_name', c_char_p),
+        ('class_name_len', c_uint),
+        ('type_hint', c_uint8),
+        ('allow_null', c_int),
+        ('pass_by_reference', c_int),
+        ]
+
+class ZendFunctionEntry(Structure):
+    _fields_ = [
+        ('fname', c_char_p),
+        ('handler', CFUNCTYPE(None, c_int, POINTER(ZValStruct), POINTER(POINTER(ZValStruct)), POINTER(ZValStruct), c_int, c_void_p)),
+        ('arg_info', POINTER(ArgInfo)),
+        ('num_args', c_uint),
+        ('flags', c_uint),
+        ]
+
+class ZendMmap(Structure):
+    _fields_ = [
+        ('len', c_size_t),
+        ('pos', c_size_t),
+        ('map', c_void_p),
+        ('buf', c_char_p),
+        ('old_handle', c_void_p),
+        ('old_closer', CFUNCTYPE(None, c_void_p)),
+        ]
+
+class ZendStream(Structure):
+    _fields_ = [
+        ('handle', c_void_p),
+        ('isatty', c_int),
+        ('mmap', ZendMmap),
+        ('reader', CFUNCTYPE(c_size_t, c_void_p, POINTER(c_char), c_size_t, c_void_p)), 
+        ('fsizer', CFUNCTYPE(c_size_t, c_void_p, c_void_p)),
+        ('closer', CFUNCTYPE(None, c_void_p)),
+        ]
+
+class ZendFileHandle(Structure):
+    class Handle(Union):
+        _fields_ = [
+            ('fd', c_int),
+            ('stream', ZendStream),
+            ]
+
+    _fields_ = [
+        ('type', c_int),
+        ('filename', c_char_p),
+        ('opened_path', c_char_p),
+        ('handle', Handle),
+        ('free_filename', c_int)
+        ]
+
+class SAPIModuleStruct(Structure):
+    pass
+
+SAPIModuleStruct._fields_ = [
+    ('name', c_char_p),
+    ('pretty_name', c_char_p),
+    ('startup', CFUNCTYPE(c_int, POINTER(SAPIModuleStruct))),
+    ('shutdown', CFUNCTYPE(c_int, POINTER(SAPIModuleStruct))),
+    ('activate', CFUNCTYPE(c_int, c_void_p)),
+    ('deactivate', CFUNCTYPE(c_int, c_void_p)),
+    ('ub_write', CFUNCTYPE(c_int, POINTER(c_char), c_uint, c_void_p)),
+    ('flush', CFUNCTYPE(None, c_void_p)),
+    ('get_stat', CFUNCTYPE(c_void_p, c_void_p)),
+    ('getenv', CFUNCTYPE(c_char_p, POINTER(c_char), c_size_t, c_void_p)),
+    ('sapi_error', CFUNCTYPE(None, c_int, c_char_p)),
+    ('header_handler', CFUNCTYPE(c_int, POINTER(SAPIHeaderStruct), c_int, POINTER(SAPIHeadersStruct), c_void_p)),
+    ('send_headers', CFUNCTYPE(c_int, POINTER(SAPIHeadersStruct), c_void_p)),
+    ('send_header', CFUNCTYPE(None, POINTER(SAPIHeaderStruct), c_void_p, c_void_p)),
+    ('read_post', CFUNCTYPE(c_int, POINTER(c_char), c_uint, c_void_p)),
+    ('read_cookies', CFUNCTYPE(c_char_p, c_void_p)),
+    ('register_server_variables', CFUNCTYPE(None, POINTER(ZValStruct), c_void_p)),
+    ('log_message', CFUNCTYPE(None, c_char_p, c_void_p)),
+    ('get_request_time', CFUNCTYPE(c_uint, c_void_p)),
+    ('terminate_process', CFUNCTYPE(None, c_void_p)),
+    ('php_ini_path_override', c_char_p),
+    ('block_interruptions', CFUNCTYPE(None)),
+    ('unblock_interruptions', CFUNCTYPE(None)),
+    ('default_post_reader', CFUNCTYPE(None, c_void_p)),
+    ('treat_data', CFUNCTYPE(None, c_int, c_char_p, POINTER(ZValStruct), c_void_p)),
+    ('executable_location', c_char_p),
+    ('php_ini_ignore', c_int),
+    ('get_fd', CFUNCTYPE(c_int, c_int_p, c_void_p)),
+    ('force_http_10', CFUNCTYPE(c_int, c_void_p)),
+    ('get_target_uid', CFUNCTYPE(c_int, c_int_p, c_void_p)),
+    ('get_target_gid', CFUNCTYPE(c_int, c_int_p, c_void_p)),
+    ('input_filter', CFUNCTYPE(c_uint, c_int, c_char_p, POINTER(c_char_p), c_uint, c_uint, c_void_p)),
+    ('ini_defaults', CFUNCTYPE(None, POINTER(HashTable))),
+    ('phpinfo_as_text', c_int),
+    ('ini_entries', c_char_p),
+    ('additional_functions', POINTER(ZendFunctionEntry)),
+    ('input_filter_init', CFUNCTYPE(c_uint, c_void_p)),
+    ]
+
+class SAPIPostEntry(Structure):
+    _fields_ = [
+        ('content_type', POINTER(c_char)),
+        ('content_type_len', c_uint),
+        ('post_reader', CFUNCTYPE(None, c_void_p)),
+        ('post_handler', CFUNCTYPE(None, c_char_p, c_void_p, c_void_p)),
+        ]
+
+class SAPIRequestInfo(Structure):
+    _fields_ = [
+        ('request_method', c_char_p),
+        ('query_string', c_char_p),
+        ('post_data', POINTER(c_char)),
+        ('raw_post_data', POINTER(c_char)),
+        ('cookie_data', c_char_p),
+        ('content_length', c_long),
+        ('post_data_length', c_uint),
+        ('raw_post_data_length', c_uint),
+        ('path_translated', c_char_p),
+        ('request_uri', c_char_p),
+        ('content_type', c_char_p),
+        ('headers_only', c_int),
+        ('no_headers', c_int),
+        ('headers_read', c_int),
+        ('post_entry', POINTER(SAPIPostEntry)),
+        ('content_type_dup', c_char_p),
+        ('auth_user', c_char_p),
+        ('auth_password', c_char_p),
+        ('auth_digest', c_char_p),
+        ('argv0', c_char_p),
+        ('current_user', POINTER(c_char)),
+        ('current_user_length', c_int),
+        ('argc', c_int),
+        ('argv', POINTER(c_char_p)),
+        ('proto_num', c_int),
+        ]
+
+class SAPIGlobals(Structure):
+    _fields_ = [
+        ('server_context', c_void_p), 
+        ('request_info', SAPIRequestInfo),
+        ('sapi_headers', SAPIHeaderStruct),
+        ('read_post_bytes', c_int),
+        ('headers_sent', c_uint8),
+        ('global_stat', Stat),
+        ('default_mimetype', c_char_p),
+        ('default_charset', c_char_p),
+        ('rfc1867_uploaded_files', POINTER(HashTable)),
+        ('post_max_size', c_long),
+        ('options', c_int),
+        ('sapi_started', c_int),
+        ('global_request_time', c_int),
+        ('known_post_content_types', HashTable),
+        ]
+
+from structures import *
+from constants import *
+from wrappers import *
+
+def to_python_value(zval):
+    if zval.type == IS_NULL:
+        return None
+    elif zval.type == IS_LONG:
+        return zval.value.lval
+    elif zval.type == IS_DOUBLE:
+        return zval.value.dval
+    elif zval.type == IS_STRING:
+        return zval.value.str.val[0:zval.value.str.len]
+    elif zval.type == IS_ARRAY:
+        return HashTableWrapper(zval)
+    else:
+        raise NotImplemented()
+from module import php_module
+from ctypes import *
+
+__all__ = [
+    'HashTableWrapper',
+    'GlobalsAccessor',
+    ]
+
+class HashTableWrapper(object):
+    def __init__(self, pzval):
+        if pzval.contents.type != IS_ARRAY:
+            raise ValueError('argument is not a PHP array')
+        self.ppzval = pointer(pzval)
+
+    def __del__(self):
+        php_module._zval_ptr_dtor_wrapper(self.pzval)
+
+    def __getitem__(self, idx):
+        from utils import to_python_value
+        retval = POINTER(ZValStruct)(0)
+        if php_module.zend_hash_find(self.pzval.contents.value.ht, idx, len(idx), byref(retval)) == FAILURE:
+            raise KeyError(idx)
+        return to_python_value(retval)
+
+class GlobalsAccessor(object):
+    def __init__(self, id, type):
+        self.id = id
+        self.type = POINTER(POINTER(POINTER(type)))
+
+    def __call__(self, tsrm_ls):
+        return cast(tsrm_ls, self.type).contents[self.id - 1].contents