diff options
Diffstat (limited to 'services')
-rw-r--r-- | services/json.esp | 269 | ||||
-rw-r--r-- | services/json_auth.esp | 13 | ||||
-rw-r--r-- | services/jsondate.esp | 200 | ||||
-rw-r--r-- | services/qooxdoo/test.esp | 236 | ||||
-rw-r--r-- | services/request.esp | 483 |
5 files changed, 1201 insertions, 0 deletions
diff --git a/services/json.esp b/services/json.esp new file mode 100644 index 0000000000..6c59db0fca --- /dev/null +++ b/services/json.esp @@ -0,0 +1,269 @@ +<% + +/* + * Copyright: + * (C) 2006 by Derrell Lipman + * All rights reserved + * + * License: + * LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/ + */ + +/* + * This module provides a JSON encoder. + */ + + +/* escape a string as required by json */ +function _escape(s) +{ + var i; + var arr = new Array(); + + for (i = 0; i < strlen(s); i++) + { + var c = substr(s, i, 1); + if (c == '\x00') + { + arr[i] = '\\u0000'; + } + if (Json._internal.convert[c] != undefined) + { + arr[i] = Json._internal.convert[c]; + } + else + { + arr[i] = c; + } + } + + if (arr.length == 0) + { + return ""; + } + + return join("", arr); +} + +/* encode an arbitrary object. called recursively, for object and array */ +function _encode(o) +{ + var type = nativeTypeOf(o); + + if (type == "undefined") + { + return "null"; /* you really shouldn't count on this! */ + } + else if (type == "null") + { + return "null"; + } + else if (type == "boolean") + { + if (o) + { + return "true"; + } + else + { + return "false"; + } + } + else if (type == "c_function" || + type == "js_function" || + type == "string_c_function") + { + /* no output */ + } + else if (type == "float" || + type == "integer" || + type == "integer64") + { + return o + 0; + } + else if (type == "pointer") + { + var x = "" + o; + return '"' + substr(x, 16, strlen(x) - 16 - 1) + '"'; + } + else if (type == "object") + { + var buf; + + /* Is this an array or an ordinary object? */ + if (o["length"] != undefined) + { + var i; + + /* Assume it's an array if there's a length field */ + buf = "["; + for (i = 0; i < o.length; i++) + { + /* + * NOTE: We don't support sparse arrays nor associative + * arrays. Should we later want to do either, we're supposed + * to send it as an object rather than as an array. + */ + if (i > 0) + { + buf = buf + ","; + } + buf = buf + this.encode(o[i]); + } + buf = buf + "]"; + } + else if (o["__type"] == "_JSON_Date") + { + buf = "" + o.encoding(); + } + else + { + /* No length field, so it must be an ordinary object */ + var key; + var first = true; + + buf = "{"; + for (key in o) + { + if (! first) + { + buf = buf + ","; + } + buf = buf + '"' + key + '":' + this.encode(o[key]); + first = false; + } + buf = buf + "}"; + } + + return buf; + } + else if (type == "string") + { + return '"' + this._internal.escape(o) + '"'; + } + else + { + return '{ "unknown_object":"' + type + '"}'; + } +} + +/* Allocate the public Json access object */ +Json = new Object(); + +/* Json.encode(): encode an arbitrary object */ +Json.encode = _encode; +_encode = null; + +/* Json.decode(): decode a string into its object form */ +Json.decode = literal_to_var; + +/* Internal stuff, not for external access */ +Json._internal = new Object(); + +Json._internal.escape = _escape; +_escape = null; + +Json._internal.convert = new Object(); +Json._internal.convert['\b'] = '\\b'; +Json._internal.convert['\t'] = '\\t'; +Json._internal.convert['\n'] = '\\n'; +Json._internal.convert['\f'] = '\\f'; +Json._internal.convert['\r'] = '\\r'; +Json._internal.convert['"'] = '\\"'; +Json._internal.convert['\\'] = '\\\\'; +Json._internal.convert['\x01'] = '\\u0001'; +Json._internal.convert['\x02'] = '\\u0002'; +Json._internal.convert['\x03'] = '\\u0003'; +Json._internal.convert['\x04'] = '\\u0004'; +Json._internal.convert['\x05'] = '\\u0005'; +Json._internal.convert['\x06'] = '\\u0006'; +Json._internal.convert['\x07'] = '\\u0007'; +Json._internal.convert['\x08'] = '\\u0008'; +Json._internal.convert['\x09'] = '\\u0009'; +Json._internal.convert['\x0a'] = '\\u000a'; +Json._internal.convert['\x0b'] = '\\u000b'; +Json._internal.convert['\x0c'] = '\\u000c'; +Json._internal.convert['\x0d'] = '\\u000d'; +Json._internal.convert['\x0e'] = '\\u000e'; +Json._internal.convert['\x0f'] = '\\u000f'; +Json._internal.convert['\x10'] = '\\u0010'; +Json._internal.convert['\x11'] = '\\u0011'; +Json._internal.convert['\x12'] = '\\u0012'; +Json._internal.convert['\x13'] = '\\u0013'; +Json._internal.convert['\x14'] = '\\u0014'; +Json._internal.convert['\x15'] = '\\u0015'; +Json._internal.convert['\x16'] = '\\u0016'; +Json._internal.convert['\x17'] = '\\u0017'; +Json._internal.convert['\x18'] = '\\u0018'; +Json._internal.convert['\x19'] = '\\u0019'; +Json._internal.convert['\x1a'] = '\\u001a'; +Json._internal.convert['\x1b'] = '\\u001b'; +Json._internal.convert['\x1c'] = '\\u001c'; +Json._internal.convert['\x1d'] = '\\u001d'; +Json._internal.convert['\x1e'] = '\\u001e'; +Json._internal.convert['\x1f'] = '\\u001f'; +/* + * At some point, we probably want to add \x80-\xff as well, and it's then + * probably more efficient to generate these strings dynamically. (Even now + * it may be, but this was the the way I started, and so it remains.) + */ + + +/* Test it */ +/* +libinclude("base.js"); +function testFormat() +{ + var test = new Object(); + test.int = 23; + test.str = "hello world"; + test.float = 223.1; + test.bool = true; + test.array = new Array(); + test.array[0] = "hello"; + test.array[1] = "world"; + test.obj = new Object(); + test.obj.int = 1000; + test.obj.array = new Array(); + test.obj.array[0] = 42; + test.obj.array[1] = 223; + printf("%s\n", Json.encode(test)); +} +testFormat(); +*/ + +/* +libinclude("base.js"); +function testParse() +{ + var s; + + s = '{ "x" : 23 }'; + obj = Json.decode(s); + printf("Decode/encode of\n\t%s\nyielded\n\t%s\n\n", s, Json.encode(obj)); + + s = '{ "x" : [ 23, 42] }'; + obj = Json.decode(s); + printf("Decode/encode of\n\t%s\nyielded\n\t%s\n\n", s, Json.encode(obj)); + + s = '[ 13, 19, { "x" : [ 23, 42] }, 223 ]'; + obj = Json.decode(s); + printf("Decode/encode of\n\t%s\nyielded\n\t%s\n\n", s, Json.encode(obj)); + + s = '{ "x" : [ "hi" ] }'; + obj = Json.decode(s); + printf("Decode/encode of\n\t%s\nyielded\n\t%s\n\n", s, Json.encode(obj)); + + s = '[ 13, 19, { "x" : [ 23, 42, { "y":{"a":"hello", "b":"world", "c":[1,2,3]}}] }, 223 ]'; + obj = Json.decode(s); + printf("Decode/encode of\n\t%s\nyielded\n\t%s\n\n", s, Json.encode(obj)); +} +testParse(); +*/ + +/* + * Local Variables: + * mode: c + * End: + */ +%> diff --git a/services/json_auth.esp b/services/json_auth.esp new file mode 100644 index 0000000000..2d58b6e2af --- /dev/null +++ b/services/json_auth.esp @@ -0,0 +1,13 @@ +<% +/* Return true to allow access; false otherwise */ +function json_authenticate(serviceComponents, method) +{ + return true; +} + +/* + * Local Variables: + * mode: c + * End: + */ +%> diff --git a/services/jsondate.esp b/services/jsondate.esp new file mode 100644 index 0000000000..3467228df6 --- /dev/null +++ b/services/jsondate.esp @@ -0,0 +1,200 @@ +<% +/* + * Copyright: + * (C) 2006 by Derrell Lipman + * All rights reserved + * + * License: + * LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/ + */ + +/* + * Date class for JSON-RPC + */ + + +function _JSON_Date_create(secondsSinceEpoch) +{ + var o = new Object(); + o.__type = "_JSON_Date"; + + function _setUtcDateTimeFields(year, month, day, hour, minute, second, millisecond) + { + this.year = year + 0; + this.month = month + 0; + this.day = day + 0; + this.hour = hour + 0; + this.minute = minute + 0; + this.second = second + 0; + this.millisecond = millisecond + 0; + } + + o.setUtcYear = _setUtcDateTimeFields; + + function _setUtcYear(year) + { + this.year = year + 0; + } + o.setUtcYear = _setUtcYear; + + function _setUtcMonth(month) + { + this.month = month + 0; + } + o.setUtcMonth = _setUtcMonth; + + function _setUtcDay(day) + { + this.day = day + 0; + } + o.setUtcDay = _setUtcDay; + + function _setUtcHour(hour) + { + this.hour = hour + 0; + } + o.setUtcHour = _setUtcHour; + + function _setUtcMinute(minute) + { + this.minute = minute + 0; + } + o.setUtcMinute = _setUtcMinute; + + function _setUtcSecond(second) + { + this.second = second + 0; + } + o.setUtcSecond = _setUtcSecond; + + function _setUtcMillisecond(millisecond) + { + this.millisecond = millisecond + 0; + } + o.setUtcMillisecond = _setUtcMillisecond; + + function _setEpochTime(secondsSinceEpoch) + { + var microseconds = 0; + + if (typeof(secondsSinceEpoch) != "number") + { + var currentTime = gettimeofday(); + secondsSinceEpoch = currentTime.sec; + microseconds = currentTime.usec; + } + + var tm = gmtime(secondsSinceEpoch); + + this.year = 1900 + tm.tm_year; + this.month = tm.tm_mon; + this.day = tm.tm_mday; + this.hour = tm.tm_hour; + this.minute = tm.tm_min; + this.second = tm.tm_sec; + this.millisecond = 0; + } + o.setEpochTime = _setEpochTime; + + function _getUtcYear() + { + return this.year; + } + o.getUtcYear = _getUtcYear; + + function _getUtcMonth() + { + return this.month; + } + o.getUtcMonth = _getUtcMonth; + + function _getUtcDay() + { + return this.day; + } + o.getUtcDay = _getUtcDay; + + function _getUtcHour() + { + return this.hour; + } + o.getUtcHour = _getUtcHour; + + function _getUtcMinute() + { + return this.minute; + } + o.getUtcMinute = _getUtcMinute; + + function _getUtcSecond() + { + return this.second; + } + o.getUtcSecond = _getUtcSecond; + + function _getUtcMillisecond() + { + return this.millisecond; + } + o.getUtcMillisecond = _getUtcMillisecond; + + function _getEpochTime() + { + var tm = new Object(); + tm.tm_sec = this.second; + tm.tm_min = this.minute; + tm.tm_hour = this.hour; + tm.tm_mday = -1; + tm.tm_mon = this.month; + tm.tm_year = this.year; + tm.tm_wday = -1; + tm.tm_yday = -1; + tm.isdst = 0; + return gmmktime(tm); + } + o.getEpochTime = _getEpochTime; + + function _encoding() + { + /* Encode the date in a well-documented fashion */ + return sprintf("new Date(Date.UTC(%d,%d,%d,%d,%d,%d,%d))", + this.year, + this.month, + this.day, + this.hour, + this.minute, + this.second, + this.millisecond); + } + o.encoding = _encoding; + + if (! secondsSinceEpoch) + { + var now = gettimeofday(); + o.setEpochTime(now.sec); + } + else + { + o.setEpochTime(secondsSinceEpoch); + } + o.year = 0; + o.month = 0; + o.day = 0; + o.hour = 0; + o.minute = 0; + o.second = 0; + o.millisecond = 0; + return o; +} + +JSON_Date = new Object(); +JSON_Date.create = _JSON_Date_create; +_JSON_Date_create = null; + + +/* + * Local Variables: + * mode: c + * End: + */ +%> diff --git a/services/qooxdoo/test.esp b/services/qooxdoo/test.esp new file mode 100644 index 0000000000..e8686dcc25 --- /dev/null +++ b/services/qooxdoo/test.esp @@ -0,0 +1,236 @@ +<% +/* + * Copyright: + * (C) 2006 by Derrell Lipman + * All rights reserved + * + * License: + * LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/ + */ + +/* + * This is the standard qooxdoo test class. There are tests for each of the + * primitive types here, along with standard named tests "echo", "sink" and + * "sleep". + */ + +/** + * Echo the (one and only) parameter. + * + * @param params + * An array containing the parameters to this method + * + * @param error + * An object of class JsonRpcError. + * + * @return + * Success: The object containing the result of the method; + * Failure: null + */ +function _echo(params, error) +{ + if (params.length != 1) + { + error.setError(JsonRpcError_ParameterMismatch, + "Expected 1 parameter; got " + params.length); + return error; + } + return "Client said: [" + params[0] + "]"; +} +jsonrpc.method.echo = _echo; + +/** + * Sink all data and never return. + * + * @param params + * An array containing the parameters to this method (none expected) + * + * @param error + * An object of class JsonRpcError. + * + * @return + * "Never" + */ +function _sink(params, error) +{ + /* We're never supposed to return. Just sleep for a very long time. */ + sleep(240); +} +jsonrpc.method.sink = _sink; + +/** + * Sleep for the number of seconds specified by the parameter. + * + * @param params + * An array containing the parameters to this method (one expected) + * + * @param error + * An object of class JsonRpcError. + * + * @return + * Success: The object containing the result of the method; + * Failure: null + */ +function _sleep(params, error) +{ + if (params.length != 1) + { + error.setError(JsonRpcError_ParameterMismatch, + "Expected 1 parameter; got " + params.length); + return error; + } + + sleep(params[0]); + return params[0]; +} +jsonrpc.method.sleep = _sleep; + +/*************************************************************************/ + +/* + * The remainder of the functions test each individual primitive type, and + * test echoing arbitrary types. Hopefully the name is self-explanatory. + */ + +function _getInteger(params, error) +{ + return 1; +} +jsonrpc.method.getInteger = _getInteger; + +function _getFloat(params, error) +{ + return 1/3; +} +jsonrpc.method.getFloat = _getFloat; + +function _getString(params, error) +{ + return "Hello world"; +} +jsonrpc.method.getString = _getString; + +function _getBadString(params, error) +{ + return "<!DOCTYPE HTML \"-//IETF//DTD HTML 2.0//EN\">"; +} +jsonrpc.method.getBadString = _getBadString; + +function _getArrayInteger(params, error) +{ + return new Array(1, 2, 3, 4); +} +jsonrpc.method.getArrayInteger = _getArrayInteger; + +function _getArrayString(params, error) +{ + return new Array("one", "two", "three", "four"); +} +jsonrpc.method.getArrayString = _getArrayString; + +function _getObject(params, error) +{ + o = new Object(); // some arbitrary object + o.something = 23; + o.garbage = 'lkasjdff;lajsdfkl;sadf'; + return o; +} +jsonrpc.method.getObject = _getObject; + +function _getTrue(params, error) +{ + return true; +} +jsonrpc.method.getTrue = _getTrue; + +function _getFalse(params, error) +{ + return false; +} +jsonrpc.method.getFalse = _getFalse; + +function _getNull(params, error) +{ + return null; +} +jsonrpc.method.getNull = _getNull; + +function _isInteger(params, error) +{ + var type = nativeTypeOf(params[0]); + return type == "integer" || type == "integer64"; +} +jsonrpc.method.isInteger = _isInteger; + +function _isFloat(params, error) +{ + return nativeTypeOf(params[0]) == "float"; +} +jsonrpc.method.isFloat = _isFloat; + +function _isString(params, error) +{ + return nativeTypeOf(params[0]) == "string"; +} +jsonrpc.method.isString = _isString; + +function _isBoolean(params, error) +{ + return nativeTypeOf(params[0]) == "boolean"; +} +jsonrpc.method.isBoolean = _isBoolean; + +function _isArray(params, error) +{ + return nativeTypeOf(params[0]) == "object" && params.length != undefined; +} +jsonrpc.method.isArray = _isArray; + +function _isObject(params, error) +{ + return nativeTypeOf(params[0]) == "object"; +} +jsonrpc.method.isObject = _isObject; + +function _isNull(params, error) +{ + return nativeTypeOf(params[0]) == "null"; +} +jsonrpc.method.isNull = _isNull; + +function _getParams(params, error) +{ + return params; +} +jsonrpc.method.getParams = _getParams; + +function _getParam(params, error) +{ + return params[0]; +} +jsonrpc.method.getParam = _getParam; + +function _getCurrentTimestamp() +{ + now = gettimeofday(); + obj = new Object(); + obj.now = now.sec; + obj.json = JSON_Date.create(now); + return obj; +} +jsonrpc.method.getCurrentTimestamp = _getCurrentTimestamp; + +function _getError(params, error) +{ + error.setError(23, "This is an application-provided error"); + return error; +} +jsonrpc.method.getError = _getError; + + +/* + * Local Variables: + * mode: c + * End: + */ +%> diff --git a/services/request.esp b/services/request.esp new file mode 100644 index 0000000000..1cd22a71a8 --- /dev/null +++ b/services/request.esp @@ -0,0 +1,483 @@ +<% + +/* + * Copyright: + * (C) 2006 by Derrell Lipman + * All rights reserved + * + * License: + * LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/ + */ + +/* + * This is a simple JSON-RPC server. + */ + +/* Bring in the json format/parse functions */ +jsonrpc_include("json.esp"); + +/* Bring in the date class */ +jsonrpc_include("jsondate.esp"); + +/* Load the authentication script */ +jsonrpc_include("json_auth.esp"); + + +/* bring the string functions into the global frame */ +string_init(global); + +/* Bring the system functions into the global frame */ +sys_init(global); + +function printf() +{ + print(vsprintf(arguments)); +} + + +/* + * All of our manipulation of JSON RPC methods will be through this object. + * Each class of methods will assign to here, and all of the constants will + * also be in this object. + */ +jsonrpc = new Object(); +jsonrpc.Constant = new Object(); +jsonrpc.Constant.ErrorOrigin = new Object(); /* error origins */ +jsonrpc.Constant.ErrorCode = new Object(); /* server-generated error codes */ +jsonrpc.method = new Object(); /* methods available in requested class */ + + +/* + * ScriptTransport constants + */ +jsonrpc.Constant.ScriptTransport = new Object(); +jsonrpc.Constant.ScriptTransport.NotInUse = -1; + + +/* + * JSON-RPC error origin constants + */ +jsonrpc.Constant.ErrorOrigin.Server = 1; +jsonrpc.Constant.ErrorOrigin.Application = 2; +jsonrpc.Constant.ErrorOrigin.Transport = 3; +jsonrpc.Constant.ErrorOrigin.Client = 4; + + + +/* + * JSON-RPC server-generated error code constants + */ + +/** + * Error code, value 0: Unknown Error + * + * The default error code, used only when no specific error code is passed to + * the JsonRpcError constructor. This code should generally not be used. + */ +jsonrpc.Constant.ErrorCode.Unknown = 0; + +/** + * Error code, value 1: Illegal Service + * + * The service name contains illegal characters or is otherwise deemed + * unacceptable to the JSON-RPC server. + */ +jsonrpc.Constant.ErrorCode.IllegalService = 1; + +/** + * Error code, value 2: Service Not Found + * + * The requested service does not exist at the JSON-RPC server. + */ +jsonrpc.Constant.ErrorCode.ServiceNotFound = 2; + +/** + * Error code, value 3: Class Not Found + * + * If the JSON-RPC server divides service methods into subsets (classes), this + * indicates that the specified class was not found. This is slightly more + * detailed than "Method Not Found", but that error would always also be legal + * (and true) whenever this one is returned. (Not used in this implementation) + */ +jsonrpc.Constant.ErrorCode.ClassNotFound = 3; // not used in this implementation + +/** + * Error code, value 4: Method Not Found + * + * The method specified in the request is not found in the requested service. + */ +jsonrpc.Constant.ErrorCode.MethodNotFound = 4; + +/** + * Error code, value 5: Parameter Mismatch + * + * If a method discovers that the parameters (arguments) provided to it do not + * match the requisite types for the method's parameters, it should return + * this error code to indicate so to the caller. + */ +jsonrpc.Constant.ErrorCode.PaameterMismatch = 5; + +/** + * Error code, value 6: Permission Denied + * + * A JSON-RPC service provider can require authentication, and that + * authentication can be implemented such the method takes authentication + * parameters, or such that a method or class of methods requires prior + * authentication. If the caller has not properly authenticated to use the + * requested method, this error code is returned. + */ +jsonrpc.Constant.ErrorCode.PermissionDenied = 6; + +/* + * Error code, value 7: Unexpected Output + * + * The called method illegally generated output to the browser, which would + * have preceeded the JSON-RPC data. + */ +jsonrpc.Constant.ErrorCode.UnexpectedOutput = 7; + + + + + + + +function sendReply(reply, scriptTransportId) +{ + /* If not using ScriptTransport... */ + if (scriptTransportId == jsonrpc.Constant.ScriptTransport.NotInUse) + { + /* ... then just output the reply. */ + write(reply); + } + else + { + /* Otherwise, we need to add a call to a qooxdoo-specific function */ + reply = + "qx.io.remote.ScriptTransport._requestFinished(" + + scriptTransportId + ", " + reply + + ");"; + write(reply); + } +} + + +function _jsonValidRequest(req) +{ + if (req == undefined) + { + return false; + } + + if (typeof(req) != "object") + { + return false; + } + + if (req["id"] == undefined) + { + return false; + } + + if (req["service"] == undefined) + { + return false; + } + + if (req["method"] == undefined) + { + return false; + } + + if (req["params"] == undefined) + { + return false; + } + + return true; +} +jsonrpc.validRequest = _jsonValidRequest; +_jsonValidRequest = null; + +/* + * class JsonRpcError + * + * This class allows service methods to easily provide error information for + * return via JSON-RPC. + */ +function _JsonRpcError_create(origin, code, message) +{ + var o = new Object(); + + o.data = new Object(); + o.data.origin = origin; + o.data.code = code; + o.data.message = message; + o.scriptTransportId = jsonrpc.Constant.ScriptTransport.NotInUse; + o.__type = "_JsonRpcError"; + + function _origin(origin) + { + this.data.origin = origin; + } + o.setOrigin = _origin; + + function _setError(code, message) + { + this.data.code = code; + this.data.message = message; + } + o.setError = _setError; + + function _setId(id) + { + this.id = id; + } + o.setId = _setId; + + function _setScriptTransportId(id) + { + this.scriptTransportId = id; + } + o.setScriptTransportId = _setScriptTransportId; + + function _Send() + { + var error = this; + var id = this.id; + var ret = new Object(); + ret.error = this.data; + ret.id = this.id; + sendReply(Json.encode(ret), this.scriptTransportId); + } + o.Send = _Send; + + return o; +} + +jsonrpc.createError = _JsonRpcError_create; +_JsonRpcError_create = null; + +/* + * 'input' is the user-provided json-encoded request + * 'jsonInput' is that request, decoded into its object form + */ +var input; +var jsonInput = null; + +/* Allocate a generic error object */ +error = jsonrpc.createError(jsonrpc.Constant.ErrorOrigin.Server, + jsonrpc.Constant.ErrorCode.Unknown, + "Unknown error"); + +/* Assume (default) we're not using ScriptTransport */ +scriptTransportId = jsonrpc.Constant.ScriptTransport.NotInUse; + +/* What type of request did we receive? */ +if (request["REQUEST_METHOD"] == "POST" && + request["CONTENT_TYPE"] == "text/json") +{ + /* We found literal POSTed json-rpc data (we hope) */ + input = request["POST_DATA"]; + jsonInput = Json.decode(input); +} +else if (request["REQUEST_METHOD"] == "GET" && + form["_ScriptTransport_id"] != undefined && + form["_ScriptTransport_data"] != undefined) +{ + /* We have what looks like a valid ScriptTransport request */ + scriptTransportId = form["_ScriptTransport_id"]; + error.setScriptTransportId(scriptTransportId); + input = form["_ScriptTransport_data"]; + jsonInput = Json.decode(input); +} + +/* Ensure that this was a JSON-RPC service request */ +if (! jsonrpc.validRequest(jsonInput)) +{ + /* + * This request was not issued with JSON-RPC so echo the error rather than + * issuing a JsonRpcError response. + */ + write("JSON-RPC request expected; service, method or params missing<br>"); + return; +} + +/* + * Ok, it looks like JSON-RPC, so we'll return an Error object if we encounter + * errors from here on out. + */ +error.setId(jsonInput.id); + +/* Service and method names may contain these characters */ +var nameChars = + "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; + +/* The first letter of service and method names must be a letter */ +var nameFirstLetter = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +/* + * Ensure the method name is kosher. A meethod name should be: + * + * - first character is in [a-zA-Z] + * - other characters are in [_a-zA-Z0-9] + */ + +/* First check for legal characters */ +if (strspn(jsonInput.method, nameChars) != strlen(jsonInput.method)) +{ + /* There's some illegal character in the service name */ + error.setError(JsonRpcError.MethodNotFound, + "Illegal character found in method name."); + error.Send(); + return; +} + +/* Now ensure that it begins with a letter */ +if (strspn(substr(jsonInput.method, 0, 1), nameFirstLetter) != 1) +{ + error.setError(jsonrpc.Constant.ErrorCode.MethodNotFound, + "The method name does not begin with a letter"); + error.Send(); + return; +} + +/* + * Ensure the requested service name is kosher. A service name should be: + * + * - a dot-separated sequences of strings; no adjacent dots + * - first character of each string is in [a-zA-Z] + * - other characters are in [_a-zA-Z0-9] + */ + +/* First check for legal characters */ +if (strspn(jsonInput.service, "." + nameChars) != strlen(jsonInput.service)) +{ + /* There's some illegal character in the service name */ + error.setError(JsonRpcError.IllegalService, + "Illegal character found in service name."); + error.Send(); + return; +} + +/* + * Now ensure there are no double dots. + * + * Frustration with ejs. Result must be NULL, but we can't use the === + * operator: strstr() === null so we have to use typeof. If the result isn't + * null, then it'll be a number and therefore not type "pointer". + */ +if (typeof(strstr(jsonInput.service, "..")) != "pointer") +{ + error.setError(JsonRpcError.IllegalService, + "Illegal use of two consecutive dots in service name"); + error.Send(); + return; +} + +/* Explode the service name into its dot-separated parts */ +var serviceComponents = split(".", jsonInput.service); + +/* Ensure that each component begins with a letter */ +for (var i = 0; i < serviceComponents.length; i++) +{ + if (strspn(substr(serviceComponents[i], 0, 1), nameFirstLetter) != 1) + { + error.setError(jsonrpc.Constant.ErrorCode.IllegalService, + "A service name component does not begin with a letter"); + error.Send(); + return; + } +} + +/* + * Now replace all dots with slashes so we can locate the service script. We + * also retain the split components of the path, as the class name of the + * service is the last component of the path. + */ +var servicePath = join("/", serviceComponents) + ".esp"; + +/* Load the requested class */ +if (jsonrpc_include(servicePath)) +{ + /* Couldn't find the requested service */ + error.setError(jsonrpc.Constant.ErrorCode.ServiceNotFound, + "Service class `" + servicePath + "` does not exist."); + error.Send(); + return; +} + +/* + * Find the requested method. + * + * What we really want to do here, and could do in any reasonable language, + * is: + * + * method = jsonrpc.method[jsonInput.method]; + * if (method && typeof(method) == "function") ... + * + * The following completely unreasonable sequence of commands is because: + * + * (a) ejs evaluates all OR'ed expressions even if an early one is false, and + * barfs on the typeof(method) call if method is undefined + * + * (b) ejs does not allow comparing against the string "function"!!! What + * the hell is special about that particular string??? + * + * E-gad. What a mess. + */ +var method = jsonrpc.method[jsonInput.method]; +var valid = (method != undefined); +if (valid) +{ + var type = typeof(method); + if (substr(type, 0, 1) != 'f' || substr(type, 1) != "unction") + { + valid = false; + } +} + +if (! valid) +{ + error.setError(jsonrpc.Constant.ErrorCode.MethodNotFound, + "Method `" + method + "` not found."); + error.Send(); + return; +} + +/* Ensure the logged-in user is allowed to issue the requested method */ +if (! json_authenticate(serviceComponents, method)) +{ + error.setError(jsonrpc.Constant.ErrorCode.PermissionDenied, + "Permission denied"); + error.Send(); + return; +} + +/* Most errors from here on out will be Application-generated */ +error.setOrigin(jsonrpc.Constant.ErrorOrigin.Application); + +/* Call the requested method passing it the provided params */ +var retval = method(jsonInput.params, error); + +/* See if the result of the function was actually an error object */ +if (retval["__type"] == "_JsonRpcError") +{ + /* Yup, it was. Return the error */ + retval.Send(); + return; +} + +/* Give 'em what they came for! */ +var ret = new Object(); +ret.result = retval; +ret.id = jsonInput.id; +sendReply(Json.encode(ret), scriptTransportId); + +/* + * Local Variables: + * mode: c + * End: + */ +%> |