/**********************************************************\
| |
| The implementation of PHPRPC Protocol 3.0 |
| |
| phprpc_client.js |
| |
| Release 3.0.0 beta 4 |
| Copyright (c) 2005-2006 by Team-PHPRPC |
| |
| WebSite: http://www.phprpc.org/ |
| http://www.phprpc.com/ |
| http://sourceforge.net/projects/php-rpc/ |
| |
| Authors: Ma Bingyao <andot@ujn.edu.cn> |
| |
| This file may be distributed and/or modified under the |
| terms of the GNU Lesser General Public License (LGPL) |
| version 2.1 as published by the Free Software Foundation |
| and appearing in the included file LICENSE. |
| |
\**********************************************************/
/* PHPRPC Client for browser JavaScript
*
* Copyright (C) 2006 Ma Bingyao <andot@ujn.edu.cn>
* Version: 3.0.0 beta 4
* LastModified: Feb 5, 2007
* This library is free. You can redistribute it and/or modify it.
*/
/*
* Interfaces:
* var rpc = new PHPRPC_Client();
* rpc.useService('http://domain.com/rpcserver.php');
* rpc.setKeyLength(96);
* rpc.setEncryptMode(3);
* rpc.onready = function () {
* rpc.remoteFunctionName(args1, args2, ..., function(result, args, output, warning) {
* if (result instanceof PHPRPC_Error) {
* alert(result.Number);
* alert(result.Message);
* alert(result.toString());
* }
* else {
* alert(result); // or do any other things.
* alert(args[1]);
* alert(output);
* alert(warning.Number);
* alert(warning.Message);
* alert(warning.toString());
* }
* }, true);
* }
*/
/*@cc_on @*/
/*@if (@_jscript_version < 5.5)
Array.prototype.push = function () {
var curlen = this.length;
for (var i = 0; i < arguments.length; i++) {
this[curlen + i] = arguments[i];
}
return this.length;
}
Array.prototype.shift = function () {
var returnValue = this[0];
for (var i = 1; i < this.length; i++) {
this[i - 1] = this[i];
}
this.length--;
return returnValue;
}
@end @*/
// Public objects, properties & methods
function PHPRPC_Error(errno, errstr) {
this.Number = errno;
this.Message = errstr;
}
PHPRPC_Error.prototype.toString = function() {
return this.Number + ":" + this.Message;
}
function PHPRPC_Client(serverURL) {
this.ready = false;
this.__id = PHPRPC_Client.__clientList.length;
PHPRPC_Client.__clientList[this.__id] = this;
this.__name = 'PHPRPC_Client.__clientList[' + this.__id + ']';
if (typeof(serverURL) != "undefined")
{
this.useService(serverURL);
}
}
PHPRPC_Client.create = function(serverURL) {
return new PHPRPC_Client(serverURL);
}
PHPRPC_Client.prototype.dispose = function() {
PHPRPC_Client.__clientList[this.__id] = null;
}
PHPRPC_Client.prototype.useService = function(serverURL, username, password) {
this.__username = null;
this.__password = null;
if (typeof(serverURL) == "undefined") {
return new PHPRPC_Error(1, "You should set serverURL first!");
}
this.__url = serverURL;
if (typeof(username) != "undefined" && typeof(password) != "undefined") {
this.__username = username;
this.__password = password;
}
this.__initService();
this.__useService();
return true;
}
PHPRPC_Client.prototype.setKeyLength = function(keyLength) {
if (this.__encrypt != null) {
return false;
}
else {
this.__keyLength = keyLength;
return true;
}
}
PHPRPC_Client.prototype.getKeyLength = function() {
return this.__keyLength;
}
PHPRPC_Client.prototype.setEncryptMode = function(encryptMode) {
if (encryptMode >= 0 && encryptMode <= 3) {
this.__encryptMode = parseInt(encryptMode);
return true;
}
else {
this.__encryptMode = 0;
return false;
}
}
PHPRPC_Client.prototype.invoke = function() {
var args = this.__argsToArray(arguments);
var func = args.shift();
this.__invoke(func, args);
}
// Private objects, properties & methods
PHPRPC_Client.__clientList = [];
/*
* the function __createXMLHttp() modified from
* http://webfx.eae.net/dhtml/xmlextras/xmlextras.html and
* http://www.ugia.cn/?p=85
*/
PHPRPC_Client.__createXMLHttp = function() {
if (window.XMLHttpRequest) {
var objXMLHttp = new XMLHttpRequest();
// some older versions of Moz did not support the readyState property
// and the onreadystate event so we patch it!
if (objXMLHttp.readyState == null) {
objXMLHttp.readyState = 0;
objXMLHttp.addEventListener(
"load",
function () {
objXMLHttp.readyState = 4;
if (typeof(objXMLHttp.onreadystatechange) == "function") {
objXMLHttp.onreadystatechange();
}
},
false
);
}
return objXMLHttp;
}
else {
var MSXML = ['MSXML2.XMLHTTP.5.0',
'MSXML2.XMLHTTP.4.0',
'MSXML2.XMLHTTP.3.0',
'MSXML2.XMLHTTP',
'Microsoft.XMLHTTP'];
var n = MSXML.length;
for(var i = 0; i < n; i++) {
try {
return new ActiveXObject(MSXML[i]);
}
catch(e) {}
}
return null;
}
}
PHPRPC_Client.__createID = function() {
return (new Date()).getTime().toString(36)
+ Math.floor(Math.random() * 100000000).toString(36);
}
PHPRPC_Client.prototype.__initService = function() {
this.ready = false;
this.__encrypt = null;
this.__keyLength = 128;
this.__encryptMode = 0;
this.__keySwitching = false;
this.__taskQueue = [];
this.__dataObject = [];
var protocol = null;
var host = null;
if (this.__url.substr(0, 7) == "http://") {
protocol = "http:";
host = this.__url.substring(7, this.__url.indexOf('/', 7));
}
else if (this.__url.substr(0, 8) == "https://") {
protocol = "https:";
host = this.__url.substring(8, this.__url.indexOf('/', 8));
}
if (((protocol == null && host == null)
|| (protocol == location.protocol && host == location.host)
|| location.protocol == "file:")
&& PHPRPC_Client.__createXMLHttp() != null) {
this.__ajax = true;
}
else {
this.__ajax = false;
}
this.__url = this.__url.replace(/[\&\?]+$/g, "");
this.__url += (this.__url.indexOf('?', 0) == -1) ? '?' : '&';
}
PHPRPC_Client.prototype.__useService = function() {
if (this.__ajax) {
var xmlhttp = PHPRPC_Client.__createXMLHttp();
var __rpc = this;
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4) {
if (xmlhttp.responseText) {
var id = PHPRPC_Client.__createID();
__rpc.__createDataObject(xmlhttp.responseText, id);
__rpc.__createFunctions(unserialize(__rpc.__dataObject[id].phprpc_functions));
__rpc.__deleteDataObject(id);
}
__rpc = null;
xmlhttp = null;
}
}
try {
xmlhttp.open("GET", this.__url + 'phprpc_encode=false', true);
if (this.__username !== null) {
xmlhttp.setRequestHeader('Authorization', 'Basic ' + base64_encode(this.__username + ":" + this.__password));
}
xmlhttp.send(null);
}
catch (e) {
xmlhttp = null;
this.__ajax = false;
this.__useService();
}
}
else {
var id = PHPRPC_Client.__createID();
var callback = base64encode(utf16to8(this.__name + ".__getFunctions('" + id + "');"));
var request = 'phprpc_encode=false&phprpc_callback=' + callback;
this.__appendScript(id, request);
}
}
PHPRPC_Client.prototype.__appendScript = function(id, request, args, ref, encrypt, callback) {
var script = document.createElement("script");
script.id = "script_" + id;
script.src = this.__url + request.replace(/\+/g, '%2B');
script.defer = true;
script.type = "text/javascript";
script.args = args;
script.ref = ref;
script.encrypt = encrypt;
script.callback = callback;
var head = document.getElementsByTagName("head").item(0);
head.appendChild(script);
}
PHPRPC_Client.prototype.__removeScript = function(id) {
var script = document.getElementById("script_" + id);
var head = document.getElementsByTagName("head").item(0);
head.removeChild(script);
}
PHPRPC_Client.prototype.__argsToArray = function(args) {
var n = args.length;
var argArray = new Array(n);
for (i = 0; i < n; i++) {
argArray[i] = args[i];
}
return argArray;
}
PHPRPC_Client.prototype.__createDataObject = function(str, id) {
var params = str.split(";\r\n");
var result = {};
var n = 0;
for (var i = 0; i < params.length; i++) {
var p = params[i].indexOf("=");
if (p >= 0) {
var l = params[i].substr(0, p);
var r = params[i].substr(p + 1);
result[l] = eval(r);
}
}
this.__dataObject[id] = result;
}
PHPRPC_Client.prototype.__deleteDataObject = function(id) {
delete this.__dataObject[id];
}
PHPRPC_Client.prototype.__invoke = function(func, args) {
var __rpc = this;
var task = function() {
__rpc.__call(func, args);
__rpc = null;
};
this.__taskQueue.push(task);
this.__switchKey();
}
PHPRPC_Client.prototype.__createFunctions = function(func) {
for (var i = 0; i < func.length; i++) {
PHPRPC_Client.__clientList[this.__id][func[i]] = new Function("this.__invoke('" + func[i] + "', this.__argsToArray(arguments));");
}
this.ready = true;
if (typeof(this.onready) == "function") {
this.onready();
}
}
PHPRPC_Client.prototype.__getFunctions = function(id) {
this.__createFunctions(unserialize(phprpc_functions));
this.__removeScript(id);
}
PHPRPC_Client.prototype.__switchKey = function() {
if (this.__keySwitching) return;
if (this.__encrypt === null && this.__encryptMode > 0) {
this.__keySwitching = true;
if (this.__ajax) {
var xmlhttp = PHPRPC_Client.__createXMLHttp();
var __rpc = this;
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4) {
if (xmlhttp.responseText) {
var id = PHPRPC_Client.__createID();
__rpc.__createDataObject(xmlhttp.responseText, id);
__rpc.__switchKey2(id);
__rpc.__deleteDataObject(id);
}
__rpc = null;
xmlhttp = null;
}
}
xmlhttp.open("GET", this.__url + 'phprpc_encrypt=true&phprpc_encode=false&phprpc_keylen=' + this.__keyLength, true);
if (this.__username !== null) {
xmlhttp.setRequestHeader('Authorization', 'Basic ' + base64_encode(this.__username + ":" + this.__password));
}
xmlhttp.send(null);
}
else {
var id = PHPRPC_Client.__createID();
var callback = base64encode(utf16to8(this.__name + ".__switchKey2('" + id + "');"));
var request = 'phprpc_encrypt=true&phprpc_encode=false&phprpc_keylen=' + this.__keyLength + '&phprpc_callback=' + callback;
this.__appendScript(id, request);
}
}
else {
this.__keySwitched();
}
}
PHPRPC_Client.prototype.__switchKey2 = function(id) {
if (this.__ajax) {
if (typeof(this.__dataObject[id].phprpc_encrypt) == "undefined") {
this.__encrypt = null;
this.__encorytMode = 0;
this.__keySwitching = false;
this.__keySwitched();
}
else {
if (typeof(this.__dataObject[id].phprpc_keylen) != "undefined") {
this.__keyLength = parseInt(this.__dataObject[id].phprpc_keylen);
}
else {
this.__keyLength = 128;
}
this.__encrypt = unserialize(this.__dataObject[id].phprpc_encrypt);
var encrypt = this.__getKey().replace(/\+/g, '%2B');
var __rpc = this;
var xmlhttp = PHPRPC_Client.__createXMLHttp();
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4) {
if (xmlhttp.responseText) {
__rpc.__keySwitching = false;
__rpc.__keySwitched();
}
__rpc = null;
xmlhttp = null;
}
}
xmlhttp.open("GET", this.__url + 'phprpc_encode=false&phprpc_encrypt=' + encrypt, true);
if (this.__username !== null) {
xmlhttp.setRequestHeader('Authorization', 'Basic ' + base64_encode(this.__username + ":" + this.__password));
}
xmlhttp.send(null);
}
}
else {
this.__removeScript(id);
if (typeof(phprpc_encrypt) == "undefined") {
this.__encrypt = null;
this.__encorytMode = 0;
this.__keySwitching = false;
this.__keySwitched();
}
else {
this.__encrypt = unserialize(phprpc_encrypt);
if ((typeof(phprpc_keylen) != "undefined") && (phprpc_keylen !== null)) {
this.__keyLength = parseInt(phprpc_keylen);
phprpc_keylen = null;
}
else {
this.__keyLength = 128;
}
var callback = base64encode(utf16to8(this.__name + ".__removeScript('" + id + "');"));
var request = 'phprpc_encrypt=' + this.__getKey()
+ '&phprpc_encode=false&phprpc_callback=' + callback;
this.__appendScript(id, request);
this.__keySwitching = false;
this.__keySwitched();
}
}
}
PHPRPC_Client.prototype.__getKey = function() {
this.__encrypt['p'] = dec2num(this.__encrypt['p']);
this.__encrypt['g'] = dec2num(this.__encrypt['g']);
this.__encrypt['y'] = dec2num(this.__encrypt['y']);
this.__encrypt['x'] = rand(this.__keyLength - 1, 1);
var key = pow_mod(this.__encrypt['y'],
this.__encrypt['x'], this.__encrypt['p']);
if (this.__keyLength == 128) {
key = num2str(key);
var n = 16 - key.length;
var k = [];
for (var i = 0; i < n; i++) {
k[i] = '\0';
}
k[n] = key;
this.__encrypt['k'] = k.join('');
}
else {
this.__encrypt['k'] = md5(num2dec(key));
}
return num2dec(pow_mod(this.__encrypt['g'],
this.__encrypt['x'], this.__encrypt['p']));
}
PHPRPC_Client.prototype.__keySwitched = function() {
while (this.__taskQueue.length > 0) {
var task = this.__taskQueue.shift();
if (typeof(task) == "function") {
task();
}
}
}
PHPRPC_Client.prototype.__call = function (func, args) {
var id = PHPRPC_Client.__createID();
var ref = false;
var encrypt = this.__encryptMode;
var callback = PHPRPC_Client.__clientList[this.__id][func + "_callback"];
if (typeof(callback) != "function") {
callback = null;
}
if (typeof(args[args.length - 1]) == "boolean" && typeof(args[args.length - 2]) == "function") {
ref = args[args.length - 1];
callback = args[args.length - 2];
args.length -= 2;
}
if (typeof(args[args.length - 1]) == "function") {
callback = args[args.length - 1];
args.length--;
}
var __args = serialize(args);
if ((this.__encrypt !== null) && (encrypt > 0)) {
__args = xxtea_encrypt(__args, this.__encrypt['k']);
}
__args = base64encode(__args);
var request = 'phprpc_func=' + func
+ '&phprpc_args=' + __args
+ '&phprpc_encode=false'
+ '&phprpc_encrypt=' + encrypt;
if (!ref) {
request += '&phprpc_ref=false';
}
if (this.__ajax) {
var xmlhttp = PHPRPC_Client.__createXMLHttp();
var __rpc = this;
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4) {
if (xmlhttp.responseText) {
__rpc.__createDataObject(xmlhttp.responseText, id);
__rpc.__getResult(id, args, ref, encrypt, callback);
__rpc.__deleteDataObject(id);
}
__rpc = null;
xmlhttp = null;
}
}
xmlhttp.open("POST", this.__url, true);
xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
if (this.__username !== null) {
xmlhttp.setRequestHeader('Authorization', 'Basic ' + base64_encode(this.__username + ":" + this.__password));
}
xmlhttp.send(request.replace(/\+/g, '%2B'));
}
else {
request += '&phprpc_callback=' + base64encode(utf16to8(this.__name + ".__callback('" + id + "');"));
this.__appendScript(id, request, args, ref, encrypt, callback);
}
}
PHPRPC_Client.prototype.__getResult = function(id, args, ref, encrypt, callback) {
if (typeof(callback) == "function") {
var errno = this.__dataObject[id].phprpc_errno;
var errstr = this.__dataObject[id].phprpc_errstr;
var output = this.__dataObject[id].phprpc_output;
var result = new PHPRPC_Error(errno, errstr);
var warning = result;
if ((errno != 1) && (errno != 16) && (errno != 64) && (errno != 256)) {
result = this.__dataObject[id].phprpc_result;
if (ref) {
args = this.__dataObject[id].phprpc_args;
}
if ((this.__encrypt !== null) && (encrypt > 0)) {
if (encrypt > 2) {
output = xxtea_decript(output, this.__encrypt['k']);
if (output === null) {
output = this.__dataObject[id].phprpc_output;
}
}
if (encrypt > 1) {
result = xxtea_decrypt(result, this.__encrypt['k']);
}
if (ref) {
args = xxtea_decrypt(args, this.__encrypt['k']);
}
}
result = unserialize(result);
if (ref) {
args = unserialize(args);
}
}
callback(result, args, output, warning);
}
}
PHPRPC_Client.prototype.__callback = function (id) {
this.__dataObject[id] = {};
this.__dataObject[id].phprpc_errno = phprpc_errno;
this.__dataObject[id].phprpc_errstr = phprpc_errstr;
this.__dataObject[id].phprpc_output = phprpc_output;
if (typeof(phprpc_result) != "undefined") {
this.__dataObject[id].phprpc_result = phprpc_result;
}
if (typeof(phprpc_args) != "undefined") {
this.__dataObject[id].phprpc_args = phprpc_args;
}
var script = document.getElementById("script_" + id);
this.__getResult(id, script.args, script.ref, script.encrypt, script.callback);
this.__deleteDataObject(id);
this.__removeScript(id);
}