/**
* @author Ma Bingyao(andot@ujn.edu.cn)
* @copyright CoolCode.CN
* @package DOTNET_PHPRPC_SERVER
* @version 2.1
* @last_update 2006-08-09
* @link http://www.coolcode.cn/?p=195
*
* Example usage:
*
* rpc.aspx
<%@ Page Language="C#" %>
<script runat="server">
public class Test
{
public double add(double a, double b) {
return a + b;
}
public string add(string a, string b) {
return a + b;
}
public int sub(int a, int b) {
return a - b;
}
public int inc(ref int n) {
return n++;
}
public static string hello(string name) {
string result = String.Concat("hello ", name);
Console.Write("output: " + result);
return result;
}
};
</script>
<%
PHPRPC.Server server = new PHPRPC.Server();
server.Add(new string[] { "add", "sub", "inc" }, new Test());
server.Add("hello", typeof(Test));
server.Start();
%>
*
*/
using System;
using System.IO;
using System.Collections;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.SessionState;
using Mono.Math;
namespace PHPRPC
{
public class Server
{
private class RemoteFunction
{
public string name;
public object obj;
public Type type;
public MethodInfo[] functions;
public RemoteFunction(string name, object obj, Type type, MethodInfo[] functions)
{
this.name = name;
this.obj = obj;
this.type = type;
this.functions = functions;
}
}
private HttpRequest request;
private HttpResponse response;
private HttpSessionState session;
private ArrayList functions;
private RemoteFunction[] rfs;
private bool debug;
private Encoding encoding;
private bool encode;
private bool byref;
private bool 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;
private static Regex test = new Regex(@"[\0-\037\042\047\134]", RegexOptions.Compiled);
public Server()
{
Init();
}
public Server(string function, object obj)
{
Init();
Add(function, obj);
Start(false, Encoding.UTF8);
}
public Server(string[] functions, object obj)
{
Init();
Add(functions, obj);
Start(false, Encoding.UTF8);
}
public Server(string function, object obj, Encoding encoding)
{
Init();
Add(function, obj);
Start(false, encoding);
}
public Server(string[] functions, object obj, Encoding encoding)
{
Init();
Add(functions, obj);
Start(false, encoding);
}
public Server(string function, Type type)
{
Init();
Add(function, type);
Start(false, Encoding.UTF8);
}
public Server(string[] functions, Type type)
{
Init();
Add(functions, type);
Start(false, Encoding.UTF8);
}
public Server(string function, Type type, Encoding encoding)
{
Init();
Add(function, type);
Start(false, encoding);
}
public Server(string[] functions, Type type, Encoding encoding)
{
Init();
Add(functions, type);
Start(false, encoding);
}
public Server(string function, bool debug, object obj)
{
Init();
Add(function, obj);
Start(debug, Encoding.UTF8);
}
public Server(string[] functions, bool debug, object obj)
{
Init();
Add(functions, obj);
Start(debug, Encoding.UTF8);
}
public Server(string function, bool debug, object obj, Encoding encoding)
{
Init();
Add(function, obj);
Start(debug, encoding);
}
public Server(string[] functions, bool debug, object obj, Encoding encoding)
{
Init();
Add(functions, obj);
Start(debug, encoding);
}
public Server(string function, bool debug, Type type)
{
Init();
Add(function, type);
Start(debug, Encoding.UTF8);
}
public Server(string[] functions, bool debug, Type type)
{
Init();
Add(functions, type);
Start(debug, Encoding.UTF8);
}
public Server(string function, bool debug, Type type, Encoding encoding)
{
Init();
Add(function, type);
Start(debug, encoding);
}
public Server(string[] functions, bool debug, Type type, Encoding encoding)
{
Init();
Add(functions, type);
Start(debug, encoding);
}
public void Add(string function, object obj)
{
Add(new string[] { function }, obj, obj.GetType());
}
public void Add(string[] functions, object obj)
{
Add(functions, obj, obj.GetType());
}
public void Add(string function, Type type)
{
Add(new string[] { function }, null, type);
}
public void Add(string[] functions, Type type)
{
Add(functions, null, type);
}
private void Add(string[] functions, object obj, Type type)
{
MethodInfo[] ms = type.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
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].ToLower() == ms[j].Name.ToLower())
{
fs.Add(ms[j]);
}
}
this.functions.Add(new RemoteFunction(functions[i], obj, type, (MethodInfo[])fs.ToArray(typeof(MethodInfo))));
}
}
private string JsReplace(Match m)
{
return String.Concat("\\x", ((int)m.Value.ToCharArray()[0]).ToString("x2"));
}
private string AddJsSlashes(string str)
{
return test.Replace(str, new MatchEvaluator(this.JsReplace));
}
private string AddJsSlashes(byte[] s)
{
StringBuilder sb = new StringBuilder();
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(String.Concat("\\x", s[i].ToString("x2")));
}
else
{
sb.Append((char)s[i]);
}
}
return sb.ToString();
}
private RemoteFunction[] GetFunction(string function)
{
ArrayList rf = new ArrayList();
function = function.ToLower();
for (int i = 0, n = this.rfs.Length; i < n; i++) {
if (function == this.rfs[i].name.ToLower()) {
rf.Add(this.rfs[i]);
}
}
return (RemoteFunction[])(rf.ToArray(typeof(RemoteFunction)));
}
private void AppendHeader()
{
response.AppendHeader("Content-Type", "text/plain; charset=" + this.encoding.WebName);
response.AppendHeader("X-Powered-By", "PHPRPC Server/2.1");
response.Cache.SetLastModified(DateTime.Now);
response.AppendHeader("Cache-Control", "no-store, no-cache, must-revalidate");
response.AppendHeader("Cache-Control", "pre-check=0, post-check=0, max-age=0");
response.AppendHeader("Content-Encoding", "none");
}
private void WriteError()
{
response.Write(String.Concat("phprpc_errno=\"", this.errno.ToString(), "\";\r\n"));
if (this.encode)
{
response.Write(String.Concat("phprpc_errstr=\"", Convert.ToBase64String(this.encoding.GetBytes(this.errstr)), "\";\r\n"));
response.Write(String.Concat("phprpc_output=\"", Convert.ToBase64String(this.encoding.GetBytes(this.output)), "\";\r\n"));
}
else
{
response.Write(String.Concat("phprpc_errstr=\"", this.AddJsSlashes(this.errstr), "\";\r\n"));
response.Write(String.Concat("phprpc_output=\"", this.AddJsSlashes(this.output), "\";\r\n"));
}
response.Write(this.callback);
}
private byte[] Call(MethodInfo function, object obj, byte[] arguments, out object[] args)
{
if (request["phprpc_args"] != null)
{
args = (object[])PHPSerializer.UnSerialize(arguments, typeof(object[]), this.encoding);
ParameterInfo[] p = function.GetParameters();
for (int i = 0, n = Math.Min(p.Length, args.Length); i < n; i++)
{
if (args[i] != null)
{
args[i] = PHPSerializer.ChangeType(args[i], p[i].ParameterType);
}
}
}
else
{
args = null;
}
MemoryStream ms = new MemoryStream();
TextWriter defaultout = Console.Out;
StreamWriter sw = new StreamWriter(ms);
Console.SetOut(sw);
byte[] result = PHPSerializer.Serialize(function.Invoke(obj, args), this.encoding);
Console.SetOut(defaultout);
sw.Close();
this.output = Encoding.UTF8.GetString(ms.ToArray());
return result;
}
private void Init()
{
request = System.Web.HttpContext.Current.Request;
response = System.Web.HttpContext.Current.Response;
session = System.Web.HttpContext.Current.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 Start()
{
Start(false, Encoding.UTF8);
}
public void Start(Encoding encoding)
{
Start(false, encoding);
}
public void Start(bool debug)
{
Start(debug, Encoding.UTF8);
}
public void Start(bool debug, Encoding encoding)
{
this.debug = debug;
this.encoding = encoding;
this.rfs = (RemoteFunction[])this.functions.ToArray(typeof(RemoteFunction));
response.Clear();
response.Buffer = true;
response.BufferOutput = true;
if (request["phprpc_encode"] != null)
{
this.encode = (request["phprpc_encode"].ToLower() != "false");
}
if (request["phprpc_callback"] != null)
{
this.callback = encoding.GetString(Convert.FromBase64String(request["phprpc_callback"]));
}
if (request["phprpc_ref"] != null)
{
this.byref = (request["phprpc_ref"].ToLower() != "false");
}
if (request["phprpc_encrypt"] != null)
{
string encrypt = request["phprpc_encrypt"].ToLower();
switch (encrypt)
{
case "true": this.encrypt = true; break;
case "false": this.encrypt = false; break;
case "0": this.encryptmode = 0; break;
case "1": this.encryptmode = 1; break;
case "2": this.encryptmode = 2; break;
default: break;
}
}
if (session["phprpc_encrypt"] != null)
{
Hashtable kp = (Hashtable)(session["phprpc_encrypt"]);
if (kp.ContainsKey("k"))
{
this.key = new byte[16];
byte[] key = BigInteger.Parse(kp["k"] as string).GetBytes();
for (int i = 1, n = key.Length; i <= n; i++)
{
this.key[16 - i] = key[n - i];
}
}
}
try
{
if (request["phprpc_func"] != null)
{
RemoteFunction[] rf = GetFunction(request["phprpc_func"]);
object[] args = null;
if (rf.Length > 0)
{
byte[] arguments;
arguments = PHPSerializer.Serialize(new object[] { }, this.encoding);
if (request["phprpc_args"] != null)
{
arguments = Convert.FromBase64String(request["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.";
response.Clear();
this.AppendHeader();
this.WriteError();
return;
}
}
}
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
{
result = Call(rf[i].functions[j], rf[i].obj, arguments, out args);
break;
}
catch (Exception e)
{
if (i == n - 1 && j == m - 1) throw e;
}
}
}
if (this.byref && args != null)
{
arguments = PHPSerializer.Serialize(args, this.encoding);
}
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.";
response.Clear();
this.AppendHeader();
this.WriteError();
return;
}
}
if (this.encode)
{
this.result = Convert.ToBase64String(result);
if (this.byref)
{
this.arguments = Convert.ToBase64String(arguments);
}
}
else
{
this.result = this.AddJsSlashes(result);
if (this.byref)
{
this.arguments = this.AddJsSlashes(arguments);
}
}
}
else
{
this.errno = 1;
this.errstr = String.Concat("Can't find this function ", request["phprpc_func"], "().");
}
response.Clear();
this.AppendHeader();
if (this.errno == 0)
{
response.Write(String.Concat("phprpc_result=\"", this.result, "\";\r\n"));
if (this.byref)
{
response.Write(String.Concat("phprpc_args=\"", this.arguments, "\";\r\n"));
}
}
this.WriteError();
}
else
{
byte[] encrypt = null;
if (this.encrypt)
{
Hashtable kp = KeyPairGen.genRandomKeyPair();
BigInteger p = BigInteger.Parse(kp["p"] as string);
BigInteger g = BigInteger.Parse(kp["g"] as string);
BigInteger x = BigInteger.GenerateRandom(127);
BigInteger y = g.ModPow(x, p);
kp["y"] = y.ToString();
encrypt = PHPSerializer.Serialize(kp, this.encoding);
kp["x"] = x.ToString();
session["phprpc_encrypt"] = kp;
}
else if (request["phprpc_encrypt"] != null && request["phprpc_encrypt"].ToLower() != "false")
{
Hashtable kp = session["phprpc_encrypt"] as Hashtable;
kp["y"] = request["phprpc_encrypt"];
BigInteger y = BigInteger.Parse(kp["y"] as string);
BigInteger x = BigInteger.Parse(kp["x"] as string);
BigInteger p = BigInteger.Parse(kp["p"] as string);
BigInteger k = y.ModPow(x, p);
kp["k"] = k.ToString();
session["phprpc_encrypt"] = kp;
encrypt = PHPSerializer.Serialize(true, this.encoding);
}
response.Clear();
this.AppendHeader();
if (encrypt != null)
{
if (this.encode)
{
response.Write(String.Concat("phprpc_encrypt=\"", Convert.ToBase64String(encrypt), "\";\r\n"));
}
else
{
response.Write(String.Concat("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)
{
response.Write(String.Concat("phprpc_functions=\"", Convert.ToBase64String(PHPSerializer.Serialize(fns, this.encoding)), "\";\r\n"));
}
else
{
response.Write(String.Concat("phprpc_functions=\"", this.AddJsSlashes(PHPSerializer.Serialize(fns, this.encoding)), "\";\r\n"));
}
response.Write(this.callback);
}
}
catch (Exception e)
{
this.errno = 1;
if (this.debug)
{
this.errstr = e.ToString();
}
else
{
this.errstr = e.Message;
}
response.Clear();
this.AppendHeader();
this.WriteError();
}
}
}
}