/**
* @author Ma Bingyao(andot@ujn.edu.cn)
* @copyright CoolCode.CN
* @package JAVA_PHPRPC_SERVER
* @version 2.1
* @last_update 2006-08-09
* @link http://www.coolcode.cn/?p=204
*
* Example usage:
*
* rpc.jsp
<%@ page import="java.lang.*" %>
<%@ page import="coolcode.phprpc.*" %>
<%
PHPRPCServer phprpc_server = new PHPRPCServer(request, response, session);
phprpc_server.add("min", Math.class);
phprpc_server.add(new String[] { "sin", "cos" }, Math.class);
phprpc_server.start();
%>
*/
package coolcode.phprpc;
import java.lang.*;
import java.lang.reflect.*;
import java.io.*;
import java.math.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import coolcode.*;
class RemoteFunction {
protected String name;
protected Object obj;
protected Class cls;
protected Method[] functions;
protected RemoteFunction(String name, Object obj, Class cls, Method[] functions) {
this.name = name;
this.obj = obj;
this.cls = cls;
this.functions = functions;
}
}
public class PHPRPCServer {
private HttpServletRequest request;
private HttpServletResponse response;
private HttpSession session;
private PrintWriter reswriter;
private ArrayList functions;
private RemoteFunction[] rfs;
private boolean debug;
private String charset;
private boolean encode;
private boolean byref;
private boolean encrypt;
private int encryptmode;
private byte[] key;
private String result;
private String arguments;
private String output;
private String callback;
private int errno;
private String errstr;
public PHPRPCServer(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
this.request = request;
this.response = response;
this.session = session;
this.functions = new ArrayList();
this.encode = true;
this.encrypt = false;
this.encryptmode = 0;
this.errno = 0;
this.errstr = "";
this.output = "";
this.callback = "";
this.byref = true;
this.key = null;
}
public void add(String function, Object obj) {
add(new String[] { function }, obj, obj.getClass());
}
public void add(String[] functions, Object obj) {
add(functions, obj, obj.getClass());
}
public void add(String function, Class cls) {
add(new String[] { function }, null, cls);
}
public void add(String[] functions, Class cls) {
add(functions, null, cls);
}
private void add(String[] functions, Object obj, Class cls) {
Method[] ms = cls.getMethods();
for (int i = 0, n = functions.length; i < n; i++) {
ArrayList fs = new ArrayList();
for (int j = 0, m = ms.length; j < m; j++) {
if (functions[i].toLowerCase().equals(
ms[j].getName().toLowerCase())
&& Modifier.isPublic(ms[j].getModifiers())) {
fs.add(ms[j]);
}
}
this.functions.add(
new RemoteFunction(functions[i], obj, cls,
(Method[]) fs.toArray(new Method[0])));
}
}
private String toHexString(int n) {
return ((n < 16) ? "0" : "") + Integer.toHexString(n);
}
private String addJsSlashes(String str) {
char[] s = str.toCharArray();
StringBuffer sb = new StringBuffer();
for (int i = 0, n = s.length; i < n; i++) {
if (s[i] <= 31 || s[i] == 34 || s[i] == 39 || s[i] == 92
|| s[i] == 127) {
sb.append("\\x");
sb.append(toHexString((int) s[i] & 0xff));
} else {
sb.append(s[i]);
}
}
return sb.toString();
}
private String addJsSlashes(byte[] s) {
StringBuffer sb = new StringBuffer();
for (int i = 0, n = s.length; i < n; i++) {
if (s[i] <= 31 || s[i] == 34 || s[i] == 39 || s[i] == 92
|| s[i] == 127) {
sb.append("\\x");
sb.append(toHexString((int) s[i] & 0xff));
} else {
sb.append((char) s[i]);
}
}
return sb.toString();
}
private RemoteFunction[] getFunction(String function) {
ArrayList rf = new ArrayList();
function = function.toLowerCase();
for (int i = 0, n = this.rfs.length; i < n; i++) {
if (function.equals(this.rfs[i].name.toLowerCase())) {
rf.add(this.rfs[i]);
}
}
return (RemoteFunction[]) (rf.toArray(new RemoteFunction[0]));
}
private void appendHeader() throws IOException {
this.response.setContentType("text/plain; charset=" + this.charset);
this.response.addHeader("X-Powered-By", "PHPRPC Server/2.1");
this.response.setDateHeader("Date", (new Date()).getTime());
this.response.setDateHeader("Last-Modified", (new Date()).getTime());
this.response.addHeader("Cache-Control",
"no-store, no-cache, must-revalidate");
this.response.addHeader("Cache-Control",
"pre-check=0, post-check=0, max-age=0");
this.response.addHeader("Content-Encoding", "none");
this.reswriter = response.getWriter();
}
private void writeError() throws UnsupportedEncodingException {
reswriter.print("phprpc_errno=\"" + this.errno + "\";\r\n");
if (this.encode) {
reswriter.print(
"phprpc_errstr=\""
+ Base64.encode(this.errstr.getBytes(this.charset))
+ "\";\r\n");
} else {
reswriter.print(
"phprpc_errstr=\"" + this.addJsSlashes(this.errstr)
+ "\";\r\n");
}
reswriter.print("phprpc_output=\"" + this.output + "\";\r\n");
reswriter.print(this.callback);
}
private byte[] call(Method function, Object obj, Object[] args) throws UnSerializeException, UnsupportedEncodingException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
if (request.getParameter("phprpc_args") != null) {
Class[] p = function.getParameterTypes();
for (int i = 0, n = Math.min(p.length, args.length); i < n; i++) {
if (args[i] != null) {
args[i] = PHPSerializer.cast(args[i], p[i]);
}
}
}
ByteArrayOutputStream bs = new ByteArrayOutputStream();
PrintStream defaultout = System.out;
PrintStream ps = new PrintStream(bs, true);
System.setOut(ps);
byte[] result = PHPSerializer.serialize(function.invoke(obj, args),
this.charset);
System.setOut(defaultout);
ps.close();
this.output = new String(bs.toByteArray());
return result;
}
public void start() throws UnsupportedEncodingException {
start(false, "UTF-8");
}
public void start(String charset) throws UnsupportedEncodingException {
start(false, charset);
}
public void start(boolean debug) throws UnsupportedEncodingException {
start(debug, "UTF-8");
}
public void start(boolean debug, String charset) throws UnsupportedEncodingException {
this.debug = debug;
this.charset = charset;
this.rfs = (RemoteFunction[]) this.functions.toArray(
new RemoteFunction[0]);
if (this.request.getParameter("phprpc_encode") != null) {
this.encode = !this.request.getParameter("phprpc_encode").toLowerCase().equals(
"false");
}
if (this.request.getParameter("phprpc_callback") != null) {
this.callback = new String(
Base64.decode(this.request.getParameter("phprpc_callback")),
this.charset);
}
if (this.request.getParameter("phprpc_ref") != null) {
this.byref = !this.request.getParameter("phprpc_ref").toLowerCase().equals(
"false");
}
if (this.request.getParameter("phprpc_encrypt") != null) {
String encrypt = this.request.getParameter("phprpc_encrypt").toLowerCase();
if (encrypt.equals("true")) {
this.encrypt = true;
} else if (encrypt.equals("false")) {
this.encrypt = false;
} else if (encrypt.equals("0")) {
this.encryptmode = 0;
} else if (encrypt.equals("1")) {
this.encryptmode = 1;
} else if (encrypt.equals("2")) {
this.encryptmode = 2;
}
}
if (this.session.getAttribute("phprpc_encrypt") != null) {
HashMap kp = (HashMap) (this.session.getAttribute("phprpc_encrypt"));
if (kp.containsKey("k")) {
this.key = new byte[16];
byte[] key = new BigInteger((String) (kp.get("k"))).toByteArray();
for (int i = 1, n = Math.min(key.length, 16); i <= n; i++) {
this.key[16 - i] = key[key.length - i];
}
}
}
try {
this.appendHeader();
if (this.request.getParameter("phprpc_func") != null) {
RemoteFunction[] rf = this.getFunction(
this.request.getParameter("phprpc_func"));
if (rf.length > 0) {
byte[] arguments;
arguments = PHPSerializer.serialize(new Object[] {},
this.charset);
if (this.request.getParameter("phprpc_args") != null) {
arguments = Base64.decode(
this.request.getParameter("phprpc_args"));
if (this.encryptmode > 0) {
if (this.key != null) {
arguments = XXTEA.decrypt(arguments, this.key);
} else {
this.errno = 1;
this.errstr = "Can't find the key for decryption.";
this.writeError();
return;
}
}
}
Object[] args = null;
byte[] result = null;
for (int i = 0, n = rf.length; i < n; i++) {
for (int j = 0, m = rf[i].functions.length; j < m; j++) {
try {
args = (Object[]) PHPSerializer.unserialize(
arguments, (new Object[0]).getClass(),
this.charset);
result = call(rf[i].functions[j], rf[i].obj,
args);
break;
} catch (Exception e) {
if (i == n - 1 && j == m - 1) {
throw e;
}
}
}
}
if (this.byref && args != null) {
arguments = PHPSerializer.serialize(args, this.charset);
}
if (this.encryptmode > 0) {
if (this.key != null) {
if (this.encryptmode > 1) {
result = XXTEA.encrypt(result, this.key);
}
if (this.byref) {
arguments = XXTEA.encrypt(arguments, this.key);
}
} else {
this.errno = 1;
this.errstr = "Can't find the key for encryption.";
this.writeError();
return;
}
}
if (this.encode) {
this.result = Base64.encode(result);
if (this.byref) {
this.arguments = Base64.encode(arguments);
}
} else {
this.result = this.addJsSlashes(result);
if (this.byref) {
this.arguments = this.addJsSlashes(arguments);
}
}
} else {
this.errno = 1;
this.errstr = "Can't find this function "
+ this.request.getParameter("phprpc_func") + "().";
}
if (this.errno == 0) {
this.reswriter.print(
"phprpc_result=\"" + this.result + "\";\r\n");
if (this.byref) {
this.reswriter.print(
"phprpc_args=\"" + this.arguments + "\";\r\n");
}
}
this.writeError();
} else {
byte[] encrypt = null;
if (this.encrypt) {
HashMap kp = KeyPairGen.genRandomKeyPair();
BigInteger p = new BigInteger((String) kp.get("p"));
BigInteger g = new BigInteger((String) kp.get("g"));
BigInteger x = new BigInteger(127, new Random());
BigInteger y = g.modPow(x, p);
kp.put("y", y.toString());
encrypt = PHPSerializer.serialize(kp, this.charset);
kp.put("x", x.toString());
this.session.setAttribute("phprpc_encrypt", kp);
} else if (this.request.getParameter("phprpc_encrypt") != null
&& !this.request.getParameter("phprpc_encrypt").toLowerCase().equals(
"false")) {
HashMap kp = (HashMap) (this.session.getAttribute(
"phprpc_encrypt"));
kp.put("y", this.request.getParameter("phprpc_encrypt"));
BigInteger y = new BigInteger((String) (kp.get("y")));
BigInteger x = new BigInteger((String) (kp.get("x")));
BigInteger p = new BigInteger((String) (kp.get("p")));
BigInteger k = y.modPow(x, p);
kp.put("k", k.toString());
this.session.setAttribute("phprpc_encrypt", kp);
encrypt = PHPSerializer.serialize(new Boolean(true), this.charset);
}
if (encrypt != null) {
if (this.encode) {
this.reswriter.print(
"phprpc_encrypt=\"" + Base64.encode(encrypt)
+ "\";\r\n");
} else {
this.reswriter.print(
"phprpc_encrypt=\"" + this.addJsSlashes(encrypt)
+ "\";\r\n");
}
}
String[] fns = new String[this.rfs.length];
for (int i = 0, n = fns.length; i < n; i++) {
fns[i] = this.rfs[i].name;
}
if (this.encode) {
this.reswriter.print(
"phprpc_functions=\""
+ Base64.encode(
PHPSerializer.serialize(fns,
this.charset))
+ "\";\r\n");
} else {
this.reswriter.print(
"phprpc_functions=\""
+ this.addJsSlashes(
PHPSerializer.serialize(fns,
this.charset))
+ "\";\r\n");
}
this.reswriter.print(this.callback);
}
} catch (Exception e) {
this.errno = 1;
if (this.debug) {
this.errstr = e.toString();
} else {
this.errstr = e.getMessage();
}
this.writeError();
}
}
}