/*-*- mode: C; tab-width:4 -*-*/ /* execve */ #include #include /* strings */ #include #include /* setenv */ #include /* miscellaneous */ #include #include /* php */ #include "php_java.h" #ifdef ZEND_ENGINE_2 #include "zend_exceptions.h" #else #include "zend_stack.h" #endif #include "protocol.h" #include "parser.h" #include "java_bridge.h" EXT_EXTERN_MODULE_GLOBALS(EXT) static void setResultFromString (pval *presult, unsigned char*s, size_t len){ Z_TYPE_P(presult)=IS_STRING; Z_STRLEN_P(presult)=len; Z_STRVAL_P(presult)=emalloc(Z_STRLEN_P(presult)+1); memcpy(Z_STRVAL_P(presult), s, Z_STRLEN_P(presult)); Z_STRVAL_P(presult)[Z_STRLEN_P(presult)]=0; } static void setResultFromLong (pval *presult, long value) { Z_TYPE_P(presult)=IS_LONG; Z_LVAL_P(presult)=value; } static void setResultFromDouble (pval *presult, double value) { Z_TYPE_P(presult)=IS_DOUBLE; Z_DVAL_P(presult)=value; } static void setResultFromBoolean (pval *presult, short value) { Z_TYPE_P(presult)=IS_BOOL; Z_LVAL_P(presult)=value; } #ifdef ZEND_ENGINE_2 static void setResultFromException (pval *presult, long value) { /* wrap the vm object in a pval object */ pval *handle; TSRMLS_FETCH(); if (Z_TYPE_P(presult) != IS_OBJECT) { object_init_ex(presult, EXT_GLOBAL(exception_class_entry)); presult->is_ref=1; presult->refcount=1; } ALLOC_ZVAL(handle); Z_TYPE_P(handle) = IS_LONG; Z_LVAL_P(handle) = value; pval_copy_constructor(handle); INIT_PZVAL(handle); #ifndef ZEND_ENGINE_2 zval_add_ref(&handle); // FIXME, this should be unnecessary #endif zend_hash_index_update(Z_OBJPROP_P(presult), 0, &handle, sizeof(pval *), NULL); } #endif static void setResultFromObject (pval *presult, long value) { /* wrap the vm object in a pval object */ pval *handle; TSRMLS_FETCH(); if (Z_TYPE_P(presult) != IS_OBJECT) { object_init_ex(presult, EXT_GLOBAL(class_entry)); presult->is_ref=1; presult->refcount=1; } ALLOC_ZVAL(handle); Z_TYPE_P(handle) = IS_LONG; Z_LVAL_P(handle) = value; pval_copy_constructor(handle); INIT_PZVAL(handle); #ifndef ZEND_ENGINE_2 zval_add_ref(&handle); // FIXME, this should be unnecessary #endif zend_hash_index_update(Z_OBJPROP_P(presult), 0, &handle, sizeof(pval *), NULL); } static void setResultFromApply(zval *presult, unsigned char *cname, size_t clen, unsigned char*fname, size_t flen, zval *object, zval *params) { zval ***func_params, *func, *retval_ptr; HashTable *func_params_ht; char *name; int count; int current = 0; zend_class_entry *ce = 0; int key_type; char *string_key; ulong num_key; TSRMLS_FETCH(); MAKE_STD_ZVAL(func); setResultFromString(func, cname, clen); func_params_ht = Z_ARRVAL_P(params); count = zend_hash_num_elements(func_params_ht); func_params = safe_emalloc(sizeof(zval **), count, 0); for (zend_hash_internal_pointer_reset(func_params_ht); zend_hash_get_current_data(func_params_ht, (void **) &func_params[current]) == SUCCESS; zend_hash_move_forward(func_params_ht) ) { current++; } if (call_user_function_ex(object?0:EG(function_table), &object, func, &retval_ptr, count, func_params, 0, NULL TSRMLS_CC) != SUCCESS) { php_error(E_ERROR, "php_mod_"/**/EXT_NAME()/**/"(%d): Could not call user function: %s.", 23, fname); } #ifdef ZEND_ENGINE_2 if(EG(exception)) { php_error(E_WARNING, "php_mod_"/**/EXT_NAME()/**/"(%d): Unhandled exception during callback in user function: %s.", 25, fname); zend_clear_exception(TSRMLS_C); } #endif EXT_GLOBAL(result)(retval_ptr, 0, presult TSRMLS_CC); if(retval_ptr) zval_ptr_dtor(&retval_ptr); efree(func_params); } static void setResultFromArray (pval *presult) { array_init( presult ); INIT_PZVAL( presult ); } static pval*nextElement (pval *handle) { pval *result; zval_add_ref(&handle); ALLOC_ZVAL(result); ZVAL_NULL(result); zval_add_ref(&result); zend_hash_next_index_insert(Z_ARRVAL_P(handle), &result, sizeof(zval *), NULL); return result; } static pval*hashIndexUpdate (pval *handle, long key) { pval *result; zval_add_ref(&handle); ALLOC_ZVAL(result); ZVAL_NULL(result); zval_add_ref(&result); zend_hash_index_update(Z_ARRVAL_P(handle), (unsigned long)key, &result, sizeof(zval *), NULL); return result; } static pval*hashUpdate (pval *handle, unsigned char *key, size_t len) { pval *result; pval pkey; zval_add_ref(&handle); ALLOC_ZVAL(result); ZVAL_NULL(result); setResultFromString(&pkey, key, len); assert(key); zval_add_ref(&result); zend_hash_update(Z_ARRVAL_P(handle), Z_STRVAL(pkey), Z_STRLEN(pkey)+1, &result, sizeof(zval *), NULL); return result; } static void setException (pval *presult, long value, unsigned char *strValue, size_t len) { #ifndef ZEND_ENGINE_2 setResultFromString(presult, strValue, len); Z_TYPE_P(presult)=IS_EXCEPTION; #else zval *exception; TSRMLS_FETCH(); ZVAL_NULL(presult); MAKE_STD_ZVAL(exception); ZVAL_NULL(exception); setResultFromException(exception, value); zend_throw_exception_object(exception TSRMLS_CC); #endif } #define GET_RESULT(pos) if(!ctx->id) {ctx->id=(zval*)strtol((const char*)PARSER_GET_STRING(st, pos), 0, 10);} struct stack_elem { zval *container; /* ctx->id */ char composite_type; /* A|H */ unsigned char *m, *p; /* see Apply in PROTOCOL.TXT */ size_t m_length, p_length; long v, n; zval *retval; }; struct parse_ctx { zval*id; zend_stack containers; }; static void begin(parser_tag_t tag[3], parser_cb_t *cb){ struct parse_ctx *ctx=(struct parse_ctx*)cb->ctx; parser_string_t *st=tag[2].strings; switch ((*tag[0].strings[0].string)[tag[0].strings[0].off]) { case 'A': /* receive apply args as normal array */ GET_RESULT(4); { /* store array result in tmp_retval, keep ctx->id in retval */ zval *tmp_retval; MAKE_STD_ZVAL(tmp_retval); ZVAL_NULL(tmp_retval); struct stack_elem stack_elem = { tmp_retval, 'A', /*FIXME*/ strdup(PARSER_GET_STRING(st, 2)), /* m */ /*FIXME*/ strdup(PARSER_GET_STRING(st, 1)), /* p */ st[2].length, /* m_length */ st[1].length, /* m_length */ strtol((const char*)PARSER_GET_STRING(st, 0), 0, 10), /* v */ strtol((const char*)PARSER_GET_STRING(st, 3), 0, 10), /* n */ ctx->id }; zend_stack_push(&ctx->containers, &stack_elem, sizeof stack_elem); setResultFromArray(tmp_retval); break; } case 'X': GET_RESULT(1); { struct stack_elem stack_elem = { ctx->id, *PARSER_GET_STRING(st, 0) }; zend_stack_push(&ctx->containers, &stack_elem, sizeof stack_elem); setResultFromArray(ctx->id); break; } case 'P': { struct stack_elem *stack_elem; zend_stack_top(&ctx->containers, (void**)&stack_elem); if(stack_elem->composite_type=='H') { /* hash table */ if(*PARSER_GET_STRING(st, 0)=='N') /* number */ ctx->id=hashIndexUpdate(stack_elem->container, strtol((const char*)PARSER_GET_STRING(st, 1), 0, 10)); else ctx->id=hashUpdate(stack_elem->container, PARSER_GET_STRING(st, 1), st[1].length); } else { /* array */ ctx->id=nextElement(stack_elem->container); } break; } case 'S': GET_RESULT(1); setResultFromString(ctx->id, PARSER_GET_STRING(st, 0), st[0].length); break; case 'B': GET_RESULT(1); setResultFromBoolean(ctx->id, *PARSER_GET_STRING(st, 0)=='T'); break; case 'L': GET_RESULT(1); setResultFromLong(ctx->id, strtol((const char*)PARSER_GET_STRING(st, 0), 0, 10)); break; case 'D': GET_RESULT(1); setResultFromDouble(ctx->id, zend_string_to_double((const char*)PARSER_GET_STRING(st, 0), st[0].length)); break; case 'O': GET_RESULT(1); if(!st[0].length) { ZVAL_NULL(ctx->id); } else { setResultFromObject(ctx->id, strtol((const char*)PARSER_GET_STRING(st, 0), 0, 10)); } break; case 'E': { unsigned char *stringRepresentation=PARSER_GET_STRING(st, 1); size_t len=st[1].length; long obj = strtol((const char*)PARSER_GET_STRING(st, 0), 0, 10); GET_RESULT(2); setException(ctx->id, obj, stringRepresentation, len); break; } default: assert(0); } } static void end(parser_string_t st[1], parser_cb_t *cb){ char c = (*(st[0].string)[st[0].off]); switch (c) { case 'X': { int err; struct parse_ctx *ctx=(struct parse_ctx*)cb->ctx; err=zend_stack_del_top(&ctx->containers); assert(SUCCESS==err); } break; } } static void begin_header(parser_tag_t tag[3], parser_cb_t *cb){ proxyenv *ctx=(proxyenv*)cb->ctx; char *str=(char*)PARSER_GET_STRING(tag[0].strings, 0); switch (*str) { case 'S'://Set-Cookie: { char *cookie, *path; static const char setcookie[]="Set-Cookie"; if(strcmp(str, setcookie) || ((*ctx)->cookie_name)) return; (*ctx)->cookie_name = strdup((char*)PARSER_GET_STRING(tag[1].strings, 0)); cookie = (char*)PARSER_GET_STRING(tag[2].strings, 0); if((path=strchr(cookie, ';'))) *path=0; /* strip off path */ (*ctx)->cookie_value = strdup(cookie); assert((*ctx)->cookie_name && (*ctx)->cookie_value); if(!(*ctx)->cookie_name || !(*ctx)->cookie_value) exit(6); break; } case 'C'://Content-Length or Connection { static const char con_connection[]="Connection", con_close[]="close"; if(!strcmp(str, con_connection)&&!strcmp((char*)PARSER_GET_STRING(tag[1].strings, 0), con_close)) { (*ctx)->must_reopen = 1; } break; } } } static void handle_request(proxyenv *env) { short tail_call; struct parse_ctx ctx = {0}; parser_cb_t cb = {begin, end, &ctx}; struct stack_elem *stack_elem; handle_request: if(!(*env)->is_local && EXT_GLOBAL (get_servlet_context) ()) { parser_cb_t cb_header = {begin_header, 0, env}; EXT_GLOBAL (parse_header) (env, &cb_header); } zend_stack_init(&ctx.containers); EXT_GLOBAL (parse) (env, &cb); /* pull off A, if any */ if(SUCCESS==zend_stack_top(&ctx.containers, (void**)&stack_elem)) { int err; setResultFromApply(stack_elem->retval, stack_elem->p, stack_elem->p_length, stack_elem->m, stack_elem->m_length, (zval*)stack_elem->v, stack_elem->container); err=zend_stack_del_top(&ctx.containers); assert(SUCCESS==err); tail_call = 1; } else { tail_call = 0; } assert(zend_stack_is_empty(&ctx.containers)); zend_stack_destroy(&ctx.containers); /* re-open a closed HTTP connection */ if((*env)->must_reopen) { char*server; (*env)->must_reopen = 0; assert((*env)->peer); if((*env)->peer) close((*env)->peer); server = EXT_GLOBAL(test_server)(&(*env)->peer, 0); /* FIXME: this is not very efficient */ assert(server); if(!server) exit(9); free(server); } if(tail_call) { memset(&ctx, 0, sizeof ctx); goto handle_request; } } unsigned char EXT_GLOBAL (get_mode) () { #ifndef ZEND_ENGINE_2 // we want arrays as values static const unsigned char arraysAsValues = 2; #else static const unsigned char arraysAsValues = 0; #endif unsigned short is_level = ((EXT_GLOBAL (ini_last_updated)&U_LOGLEVEL)!=0); unsigned short level = 0; if (is_level) level = EXT_GLOBAL(cfg)->logLevel_val>7?7:EXT_GLOBAL(cfg)->logLevel_val; return (is_level<<7)|64|(level<<2)|arraysAsValues; } static proxyenv *try_connect_to_server(short bail TSRMLS_DC) { char *server; int sock; short is_local; proxyenv *jenv =JG(jenv); if(jenv) return jenv; if(JG(is_closed)) { php_error(E_ERROR, "php_mod_"/**/EXT_NAME()/**/"(%d): Could not connect to server: Session is closed. -- This usually means that you have tried to access the server in your class' __destruct() method.",51); return 0; } if(!(server=EXT_GLOBAL(test_server)(&sock, &is_local))) { if (bail) php_error(E_ERROR, "php_mod_"/**/EXT_NAME()/**/"(%d): Could not connect to server: %s -- Have you started the "/**/EXT_NAME()/**/" bridge and set the "/**/EXT_NAME()/**/".socketname option?",52, strerror(errno)); return 0; } if(is_local || !EXT_GLOBAL (get_servlet_context) ()) { unsigned char mode = EXT_GLOBAL (get_mode) (); send(sock, &mode, sizeof mode, 0); } return JG(jenv) = EXT_GLOBAL(createSecureEnvironment)(sock, handle_request, server, is_local); } proxyenv *EXT_GLOBAL(connect_to_server)(TSRMLS_D) { return try_connect_to_server(1 TSRMLS_CC); } proxyenv *EXT_GLOBAL(try_connect_to_server)(TSRMLS_D) { return try_connect_to_server(0 TSRMLS_CC); } #ifndef PHP_WRAPPER_H #error must include php_wrapper.h #endif