/*-*- mode: C; tab-width:4 -*-*/
/* socket */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/* select */
#include <sys/select.h>
/* execve */
#include <unistd.h>
#include <sys/types.h>
/* dynamic loader */
#include <dlfcn.h>
/* strings */
#include <string.h>
/* setenv */
#include <stdlib.h>
/* posix threads implementation */
#include <pthread.h>
/* wait */
#include <sys/types.h>
#include <sys/wait.h>
/* miscellaneous */
#include <stdio.h>
#include <assert.h>
#include <errno.h>
/* jni */
#include <jni.h>
/* php */
#include "php.h"
#include "protocol.h"
#include "java_bridge.h"
#include "php_java.h"
ZEND_DECLARE_MODULE_GLOBALS(java)
static int abort_on_error(proxyenv *jenv, int nr TSRMLS_DC) {
jthrowable error = (*(jenv))->ExceptionOccurred(jenv);
if(!error) return 0;
jclass errClass;
jmethodID toString;
jobject errString;
const char *errAsUTF;
jboolean isCopy;
(*jenv)->ExceptionClear(jenv);
errClass = (*jenv)->GetObjectClass(jenv, error);
toString = (*jenv)->GetMethodID(jenv, errClass, "toString", "()Ljava/lang/String;");
errString = (*jenv)->CallObjectMethod(0, jenv, error, toString);
errAsUTF = (*jenv)->GetStringUTFChars(jenv, errString, &isCopy);
fprintf(stdout, "php_mod_java(%d): %s",nr, errAsUTF);
php_error(E_ERROR, "php_mod_java(%d): %s",nr, errAsUTF);
if(isCopy) (*jenv)->ReleaseStringUTFChars(jenv, errString, errAsUTF);
return 1;
}
static void swrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) {
int n = fwrite(ptr, size, nmemb, stream);
//printf("write char:::%d\n", (unsigned int) ((char*)ptr)[0]);
assert(n==nmemb);
}
static void sread(void *ptr, size_t size, size_t nmemb, FILE *stream) {
int n = fread(ptr, size, nmemb, stream);
//printf("read char:::%d\n", (unsigned int) ((char*)ptr)[0]);
assert(n==nmemb);
}
static void id(proxyenv *env, char id) {
swrite(&id, sizeof id, 1, (*env)->peer);
}
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;
}
static void setResultFromObject (proxyenv *jenv, pval *presult, jobject value) {
/* wrap the java object in a pval object */
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;
jobject _ob= (*jenv)->NewGlobalRef(jenv, value);
Z_LVAL_P(handle) = zend_list_insert(_ob, le_jobject);
pval_copy_constructor(handle);
INIT_PZVAL(handle);
zval_add_ref(&handle);
zend_hash_index_update(Z_OBJPROP_P(presult), 0, &handle, sizeof(pval *), NULL);
}
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;
}
static void setException (proxyenv *jenv, pval *presult, jbyteArray value) {
setResultFromString(jenv, presult, value);
Z_TYPE_P(presult)=IS_EXCEPTION;
}
static int handle_request(proxyenv *env) {
jlong result;
char c;
FILE *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);
setResultFromString(env, (pval*)(long)result, jvalue);
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: {
sread(&result, sizeof result, 1, peer);
setResultFromArray(env, (pval*)(long)result);
break;
}
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;
}
case SETEXCEPTION: {
jbyteArray jvalue;
sread(&result, sizeof result, 1, peer);
sread(&jvalue, sizeof jvalue, 1, peer);
setException(env, (pval*)(long)result, jvalue);
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;
}
static int java_do_test_server(struct cfg*cfg TSRMLS_DC) {
char term=0;
int sock;
int n, c;
sock = socket (PF_UNIX, SOCK_STREAM, 0);
assert(sock);
if(!sock) return FAILURE;
n = connect(sock,(struct sockaddr*)&cfg->saddr, sizeof cfg->saddr);
if(n!=-1) c = write(sock, &term, sizeof term);
close(sock);
return (n!=-1 && c==1)?SUCCESS:FAILURE;
}
int java_test_server(struct cfg*cfg TSRMLS_DC) {
int count=15;
if(java_do_test_server(cfg TSRMLS_CC)==SUCCESS) return SUCCESS;
/* wait for the server that has just started */
while(cfg->cid && (java_do_test_server(cfg TSRMLS_CC)==FAILURE) && count--) {
php_error(E_NOTICE, "php_mod_java(%d): waiting for server another %d seconds",57, count);
sleep(1);
if(waitpid(cfg->cid, NULL, WNOHANG)) cfg->cid=0; // child died
}
return (cfg->cid && count)?SUCCESS:FAILURE;
}
int java_connect_to_server(struct cfg*cfg TSRMLS_DC) {
int sock, s, i, n, len;
FILE *fd;
sock = socket (PF_UNIX, SOCK_STREAM, 0);
assert(sock);
n = connect(sock,(struct sockaddr*)&cfg->saddr, sizeof cfg->saddr);
if(n==-1) {
php_error(E_WARNING, "php_mod_java(%d): Could not connect to server: %s -- Have you started the java bridge?",52, strerror(errno));
return FAILURE;
}
FILE *peer = fdopen(sock, "r+");
assert(peer);
JG(jenv)=java_createSecureEnvironment(peer, handle_requests);
JG(reflect_class) = (*JG(jenv))->FindClass(JG(jenv), "JavaBridge");
if(abort_on_error(JG(jenv), 3 TSRMLS_CC)) return FAILURE;
jobject local_php_reflect = (*JG(jenv))->AllocObject(JG(jenv), JG(reflect_class));
if(abort_on_error(JG(jenv), 4 TSRMLS_CC)) return FAILURE;
JG(php_reflect) = (*JG(jenv))->NewGlobalRef(JG(jenv), local_php_reflect);
if(abort_on_error(JG(jenv), 5 TSRMLS_CC)) return FAILURE;
return SUCCESS;
}