/*-*- mode: C; tab-width:4 -*-*/ /* execve */ #include #include /* strings */ #include /* setenv */ #include /* miscellaneous */ #include #include #include /* jni */ #include /* php */ #include "php_wrapper.h" #ifdef ZEND_ENGINE_2 #include "zend_exceptions.h" #endif #include "protocol.h" #include "java_bridge.h" #include "php_java.h" ZEND_EXTERN_MODULE_GLOBALS(java) #define swrite java_swrite extern void java_swrite(const void *ptr, size_t size, size_t nmemb, SFILE *stream); #define sread java_sread extern void java_sread(void *ptr, size_t size, size_t nmemb, SFILE *stream); #define id java_id extern void java_id(proxyenv *env, char id); static void setResultFromString (proxyenv *jenv, pval *presult, jbyteArray jvalue){ jboolean isCopy; jbyte *value = (*jenv)->GetByteArrayElements(jenv, jvalue, &isCopy); Z_TYPE_P(presult)=IS_STRING; Z_STRLEN_P(presult)=(*jenv)->GetArrayLength(jenv, jvalue); Z_STRVAL_P(presult)=emalloc(Z_STRLEN_P(presult)+1); memcpy(Z_STRVAL_P(presult), value, Z_STRLEN_P(presult)); Z_STRVAL_P(presult)[Z_STRLEN_P(presult)]=0; if (isCopy) (*jenv)->ReleaseByteArrayElements(jenv, jvalue, value, 0); } static void setResultFromLong (proxyenv *jenv, pval *presult, jlong value) { Z_TYPE_P(presult)=IS_LONG; Z_LVAL_P(presult)=(long)value; } static void setResultFromDouble (proxyenv *jenv, pval *presult, jdouble value) { Z_TYPE_P(presult)=IS_DOUBLE; Z_DVAL_P(presult)=value; } static void setResultFromBoolean (proxyenv *jenv, pval *presult, jboolean value) { Z_TYPE_P(presult)=IS_BOOL; Z_LVAL_P(presult)=value; } #ifdef ZEND_ENGINE_2 static void setResultFromException (proxyenv *jenv, pval *presult, jthrowable value) { /* wrap the java object in a pval object */ jobject _ob; pval *handle; TSRMLS_FETCH(); if (Z_TYPE_P(presult) != IS_OBJECT) { object_init_ex(presult, php_java_exception_class_entry); presult->is_ref=1; presult->refcount=1; } ALLOC_ZVAL(handle); Z_TYPE_P(handle) = IS_LONG; _ob= (*jenv)->NewGlobalRef(jenv, value); Z_LVAL_P(handle) = zend_list_insert(_ob, le_jobject); 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 (proxyenv *jenv, pval *presult, jobject value) { /* wrap the java object in a pval object */ jobject _ob; pval *handle; TSRMLS_FETCH(); if (Z_TYPE_P(presult) != IS_OBJECT) { object_init_ex(presult, php_java_class_entry); presult->is_ref=1; presult->refcount=1; } ALLOC_ZVAL(handle); Z_TYPE_P(handle) = IS_LONG; _ob= (*jenv)->NewGlobalRef(jenv, value); Z_LVAL_P(handle) = zend_list_insert(_ob, le_jobject); 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); } #ifndef ZEND_ENGINE_2 static void setResultFromArray (proxyenv *jenv, pval *presult) { array_init( presult ); INIT_PZVAL( presult ); } static pval*nextElement (proxyenv *jenv, pval *handle) { pval *result; zval_add_ref(&handle); ALLOC_ZVAL(result); zval_add_ref(&result); zend_hash_next_index_insert(Z_ARRVAL_P(handle), &result, sizeof(zval *), NULL); return result; } static pval*hashIndexUpdate (proxyenv *jenv, pval *handle, jlong key) { pval *result; zval_add_ref(&handle); ALLOC_ZVAL(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 (proxyenv *jenv, pval *handle, jbyteArray key) { pval *result; pval pkey; zval_add_ref(&handle); ALLOC_ZVAL(result); setResultFromString(jenv, &pkey, key); 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; } #endif static void setException (proxyenv *jenv, pval *presult, jthrowable value, jbyteArray strValue) { #ifndef ZEND_ENGINE_2 setResultFromString(jenv, presult, strValue); Z_TYPE_P(presult)=IS_EXCEPTION; #else zval *exception; TSRMLS_FETCH(); setResultFromObject(jenv, presult, value); /* discarded */ MAKE_STD_ZVAL(exception); setResultFromException(jenv, exception, value); zend_throw_exception_object(exception TSRMLS_CC); #endif } static int handle_request(proxyenv *env) { jlong result; char c; SFILE *peer=(*env)->peer; sread(&c, 1, 1, peer); switch(c) { case PROTOCOL_END: { return 0; } case SETRESULTFROMSTRING: { jbyteArray jvalue; sread(&result, sizeof result, 1, peer); sread(&jvalue, sizeof jvalue, 1, peer); if(jvalue) setResultFromString(env, (pval*)(long)result, jvalue); else ZVAL_NULL((pval*)(long)result); break; } case SETRESULTFROMLONG: { jlong jvalue; sread(&result, sizeof result, 1, peer); sread(&jvalue, sizeof jvalue, 1, peer); setResultFromLong(env, (pval*)(long)result, jvalue); break; } case SETRESULTFROMDOUBLE: { jdouble jvalue; sread(&result, sizeof result, 1, peer); sread(&jvalue, sizeof jvalue, 1, peer); setResultFromDouble(env, (pval*)(long)result, jvalue); break; } case SETRESULTFROMBOOLEAN: { jboolean jvalue; sread(&result, sizeof result, 1, peer); sread(&jvalue, sizeof jvalue, 1, peer); setResultFromBoolean(env, (pval*)(long)result, jvalue); break; } case SETRESULTFROMOBJECT: { jobject jvalue; sread(&result, sizeof result, 1, peer); sread(&jvalue, sizeof jvalue, 1, peer); setResultFromObject(env, (pval*)(long)result, jvalue); break; } case SETRESULTFROMARRAY: { jobject jvalue; #ifdef ZEND_ENGINE_2 static const jboolean send_content = JNI_FALSE; #else static const jboolean send_content = JNI_TRUE; #endif sread(&result, sizeof result, 1, peer); sread(&jvalue, sizeof jvalue, 1, peer); swrite(&send_content, sizeof send_content, 1, peer); #ifdef ZEND_ENGINE_2 setResultFromObject(env, (pval*)(long)result, jvalue); #else setResultFromArray(env, (pval*)(long)result); #endif break; } /* * The following is from Sam Ruby's original PHP 4 bridge. When the * result was an array or Hashtable, the ext/java extension copied the * entire(!) array or hash to the PHP interpreter. Since PHP 5 this * is dead code. */ #ifndef ZEND_ENGINE_2 case NEXTELEMENT: { sread(&result, sizeof result, 1, peer); result=(jlong)(long)nextElement(env, (pval*)(long)result); id(env, 0); swrite(&result, sizeof result, 1, peer); break; } case HASHINDEXUPDATE: { jlong jvalue; sread(&result, sizeof result, 1, peer); sread(&jvalue, sizeof jvalue, 1, peer); result=(jlong)(long)hashIndexUpdate(env, (pval*)(long)result, jvalue); id(env, 0); swrite(&result, sizeof result, 1, peer); break; } case HASHUPDATE: { jbyteArray jvalue; sread(&result, sizeof result, 1, peer); sread(&jvalue, sizeof jvalue, 1, peer); result=(jlong)(long)hashUpdate(env, (pval*)(long)result, jvalue); id(env, 0); swrite(&result, sizeof result, 1, peer); break; } #endif case SETEXCEPTION: { jthrowable jvalue; jbyteArray jstrValue; sread(&result, sizeof result, 1, peer); sread(&jvalue, sizeof jvalue, 1, peer); sread(&jstrValue, sizeof jstrValue, 1, peer); setException(env, (pval*)(long)result, jvalue, jstrValue); break; } default: { php_error(E_ERROR, "php_mod_java(%d): %s, %i",61, "protocol error: ", (unsigned int)c); id(env, 0); return 0; } } // acknowledge id(env, 0); return 1; } static int handle_requests(proxyenv *env) { // continue to handle server requests until the server says the // packet is finished (one of the three main methods has sent 0) while(handle_request(env)); return 0; } #define JAVA_METHOD(name, strname, class, param) \ JG(name) = (*jenv)->GetMethodID(jenv, JG(class), strname, param);\ if(!JG(name)) exit(9) /* out of memory or some other fatal error */ proxyenv *java_connect_to_server(TSRMLS_D) { jobject local_php_reflect; jclass local_class; int sock, n=-1; SFILE *peer; proxyenv *jenv =JG(jenv); if(jenv) return jenv; #ifndef CFG_JAVA_SOCKET_INET sock = socket (PF_LOCAL, SOCK_STREAM, 0); #else sock = socket (PF_INET, SOCK_STREAM, 0); #endif if(sock!=-1) { n = connect(sock,(struct sockaddr*)&cfg->saddr, sizeof cfg->saddr); } if(n==-1) { php_error(E_ERROR, "php_mod_java(%d): Could not connect to server: %s -- Have you started the java bridge?",52, strerror(errno)); return 0; } peer = SFDOPEN(sock, "r+"); assert(peer); if(!peer) { php_error(E_ERROR, "php_mod_java(%d): Could not connect to server: %s -- Have you started the java bridge?",53, strerror(errno)); return 0; } jenv=java_createSecureEnvironment(peer, handle_requests); assert(jenv); if(jenv&&SFREAD(&local_php_reflect, sizeof local_php_reflect, 1, peer)!=1) { php_error(E_ERROR, "php_mod_java(%d): Could not connect to server: %s -- Have you started the java bridge?",58, strerror(errno)); return 0; } BEGIN_TRANSACTION(jenv); /* java bridge class */ local_class = (*jenv)->FindClass(jenv, "JavaBridge"); if(!local_class) exit(9); JG(reflect_class) = (*jenv)->NewGlobalRef(jenv, local_class); if(!JG(reflect_class)) exit(9); /* java bridge instance */ JG(php_reflect) = (*jenv)->NewGlobalRef(jenv, local_php_reflect); if(!JG(php_reflect)) exit(9); JAVA_METHOD(setJarPath, "setJarLibraryPath", reflect_class, "(Ljava/lang/String;)V"); JAVA_METHOD(clearEx, "clearException", reflect_class, "()V"); JAVA_METHOD(lastEx, "lastException", reflect_class, "(JJ)V"); JAVA_METHOD(getPhpMap, "getPhpMap", reflect_class, "(Ljava/lang/Object;)LJavaBridge$PhpMap;"); local_class = (*jenv)->FindClass(jenv, "JavaBridge$PhpMap"); if(!local_class) exit(9); JG(iterator_class) = (*jenv)->NewGlobalRef(jenv, local_class); if(!JG(iterator_class)) exit(9); JAVA_METHOD(moveForward, "moveForward", iterator_class, "()Ljava/lang/Object;"); JAVA_METHOD(hasMore, "hasMore", iterator_class, "()Ljava/lang/Object;"); JAVA_METHOD(getType, "getType", iterator_class, "()Ljava/lang/Object;"); JAVA_METHOD(invoke, "Invoke", reflect_class, "(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;JJ)V"); JAVA_METHOD(gsp, "GetSetProp", reflect_class, "(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;JJ)V"); JAVA_METHOD(co, "CreateObject", reflect_class, "(Ljava/lang/String;Z[Ljava/lang/Object;JJ)V"); END_TRANSACTION(jenv); return JG(jenv)=jenv; } #ifndef PHP_WRAPPER_H #error must include php_wrapper.h #endif