/* PHPRPCServer.java
*
* Author: Ma Bingyao <andot@ujn.edu.cn>
* Copyright: CoolCode.CN
* Version: 2.1
* LastModified: 2006-08-07
* This library is free. You can redistribute it and/or modify it.
* http://www.coolcode.cn/?p=204
*/
package PHPRPC;
import java.lang.*;
import java.lang.reflect.*;
import java.io.*;
import java.math.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
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 Integer errno;
private String errstr;
//private static Regex test = new Regex("[\0-\037\042\047\134]", RegexOptions.Compiled);
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() == 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 == 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.toString() + "\";\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, this.charset);
System.setOut(ps);
byte[] result = PHPSerializer.serialize(function.invoke(obj, args), this.charset);
System.setOut(defaultout);
ps.close();
this.output = new String(bs.toByteArray(), this.charset);
return result;
}
public void start() throws UnsupportedEncodingException, IOException
{
start(false, "UTF-8");
}
public void start(String charset) throws UnsupportedEncodingException, IOException
{
start(false, charset);
}
public void start(boolean debug) throws UnsupportedEncodingException, IOException
{
start(debug, "UTF-8");
}
public void start(boolean debug, String charset) throws UnsupportedEncodingException, IOException
{
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() != "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() != "false");
}
if (this.request.getParameter("phprpc_encrypt") != null)
{
String encrypt = this.request.getParameter("phprpc_encrypt").toLowerCase();
if (encrypt == "true")
{
this.encrypt = true;
}
else if (encrypt == "false")
{
this.encrypt = false;
}
else if (encrypt == "0")
{
this.encryptmode = 0;
}
else if (encrypt == "1")
{
this.encryptmode = 1;
}
else if (encrypt == "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[n - i];
}
}
}
try
{
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.appendHeader();
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.appendHeader();
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") + "().";
}
this.appendHeader();
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, 0, 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() != "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(true, this.charset);
}
this.appendHeader();
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.appendHeader();
this.writeError();
}
}
}