From c5718959e6a6d0454a870cbd311e707e69c98e85 Mon Sep 17 00:00:00 2001 From: Derrell Lipman Date: Mon, 25 Sep 2006 02:49:56 +0000 Subject: r18880: JSON-RPC work in progress (This used to be commit 34bffbaebf50c2a75c91285d5ec82e8f377981cc) --- jsonrpc/jsondate.esp | 189 +++++++++++++ jsonrpc/qooxdoo/test.esp | 230 ++++++++++++++++ jsonrpc/request.esp | 503 +++++++++++++++++++++++++++++++++++ source4/param/loadparm.c | 3 + source4/scripting/ejs/smbcalls.c | 82 ++++++ source4/scripting/ejs/smbcalls_sys.c | 84 ++++++ source4/scripting/libjs/provision.js | 2 +- source4/web_server/http.c | 85 ++++-- 8 files changed, 1152 insertions(+), 26 deletions(-) create mode 100644 jsonrpc/jsondate.esp create mode 100644 jsonrpc/qooxdoo/test.esp create mode 100644 jsonrpc/request.esp diff --git a/jsonrpc/jsondate.esp b/jsonrpc/jsondate.esp new file mode 100644 index 0000000000..af2c7e2e3f --- /dev/null +++ b/jsonrpc/jsondate.esp @@ -0,0 +1,189 @@ + +/* + * 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(); + + 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); + } + + 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); + } + + if (! secondsSinceEpoch) + { + var now = getTimeOfDay(); + o.setEpochTime(now.sec); + } + else + { + o.setEpochTime(secondsSinceEpoch); + } + o.year = null; + o.month = null; + o.day = null; + o.hour = null; + o.minute = null; + o.second = null; + o.millisecond = null; + return o; +} + +JSON_Date = new Object(); +JSON_Date.create = _JSON_Date_create; +_JSON_Date_create = null; diff --git a/jsonrpc/qooxdoo/test.esp b/jsonrpc/qooxdoo/test.esp new file mode 100644 index 0000000000..5fd893c217 --- /dev/null +++ b/jsonrpc/qooxdoo/test.esp @@ -0,0 +1,230 @@ +<% +/* + * 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 null; + } + + 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 ""; +} +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; + +%> diff --git a/jsonrpc/request.esp b/jsonrpc/request.esp new file mode 100644 index 0000000000..a8080d9dc7 --- /dev/null +++ b/jsonrpc/request.esp @@ -0,0 +1,503 @@ +<% + +/* + * 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 date class */ +jsonrpc_include("jsondate.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)); +} + + +/* KLUDGE... */ +form = new Array(); +server = new Array(); +request = new Array(); +/* ...KLUDGE */ + +/* + * 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. */ + printf(reply); + } + else + { + /* Otherwise, we need to add a call to a qooxdoo-specific function */ + reply = + "qx.io.remote.ScriptTransport._requestFinished(" + + scriptTransportId + ", " + reply + + ");"; + printf(reply); + } +} + + +/* + * class Json + * + * This class provides the JSON encoder and decoder, and some utility + * functions. + */ +Json = new Object(); + +/* KLUDGE... */ +function _jsonDecode(s) +{ + var o = new Object(); + o.id = 23; + o.service = "qooxdoo.test"; + o.method = "echo"; + o.params = new Array(1); + o.params[0] = "hello world"; + return o; +} +/* ...KLUDGE */ + +Json.decode = _jsonDecode; + +/* KLUDGE... */ +function _jsonEncode(o) +{ + return "{ result: \"hello world\" }" +} +/* ...KLUDGE */ + +Json.encode = _jsonEncode; + +function _jsonValidRequest(req) +{ + if (req == undefined) + { + 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.origin = origin; + } + o.setOrigin = _origin; + + function _setError(code, message) + { + this.code = code; + this.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 Array(2); + 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 (server["REQUEST_METHOD"] == "POST" && + server["CONENT_TYPE"] == "text/json") +{ + /* We found literal POSTed json-rpc data (we hope) */ + input = request["POST_DATA"]; + jsonInput = Json.decode(input); +} +else if (server["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); +} + +/* KLUDGE... */ +jsonInput = Json.decode(input); +/* ...KLUDGE */ + +/* 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. + */ + printf("JSON-RPC request expected; service, method or params missing
"); + 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 + * bars 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; +} + +/* 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 */ +var wasError = (retval["__type"] != undefined); +if (wasError) +{ + wasError = retval.__type == "_JsonRpcError"; +} +if (wasError) +{ + /* 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); +%> diff --git a/source4/param/loadparm.c b/source4/param/loadparm.c index 2b2926d053..1e5df4fcbe 100644 --- a/source4/param/loadparm.c +++ b/source4/param/loadparm.c @@ -114,6 +114,7 @@ typedef struct char *szWINS_URL; char *szPrivateDir; char **jsInclude; + char *jsonrpcBase; char **szPasswordServers; char *szSocketOptions; char *szRealm; @@ -542,6 +543,7 @@ static struct parm_struct parm_table[] = { {"modules dir", P_STRING, P_GLOBAL, &Globals.szModulesDir, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, {"pid directory", P_STRING, P_GLOBAL, &Globals.szPidDir, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, {"js include", P_LIST, P_GLOBAL, &Globals.jsInclude, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"jsonrpc base", P_STRING, P_GLOBAL, &Globals.jsonrpcBase, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, {"setup directory", P_STRING, P_GLOBAL, &Globals.szSetupDir, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, {"socket address", P_STRING, P_GLOBAL, &Globals.szSocketAddress, NULL, NULL, FLAG_DEVELOPER}, @@ -911,6 +913,7 @@ _PUBLIC_ FN_GLOBAL_LIST(lp_auth_methods, &Globals.AuthMethods) _PUBLIC_ FN_GLOBAL_BOOL(lp_paranoid_server_security, &Globals.paranoid_server_security) static FN_GLOBAL_INTEGER(lp_announce_as, &Globals.announce_as) _PUBLIC_ FN_GLOBAL_LIST(lp_js_include, &Globals.jsInclude) +_PUBLIC_ FN_GLOBAL_STRING(lp_jsonrpc_base, &Globals.jsonrpcBase) _PUBLIC_ _PUBLIC_ _PUBLIC_ FN_LOCAL_STRING(lp_servicename, szService) diff --git a/source4/scripting/ejs/smbcalls.c b/source4/scripting/ejs/smbcalls.c index 1bfbd3b47a..85cc5f7027 100644 --- a/source4/scripting/ejs/smbcalls.c +++ b/source4/scripting/ejs/smbcalls.c @@ -66,6 +66,45 @@ static int ejs_typeof(MprVarHandle eid, int argc, struct MprVar **argv) return 0; } +/* + return the native type of a variable +*/ +static int ejs_typeof_native(MprVarHandle eid, int argc, struct MprVar **argv) +{ + const struct { + MprType type; + const char *name; + } types[] = { + { MPR_TYPE_UNDEFINED, "undefined" }, + { MPR_TYPE_NULL, "null" }, + { MPR_TYPE_BOOL, "boolean" }, + { MPR_TYPE_CFUNCTION, "c_function" }, + { MPR_TYPE_FLOAT, "float" }, + { MPR_TYPE_INT, "integer" }, + { MPR_TYPE_INT64, "integer64" }, + { MPR_TYPE_OBJECT, "object" }, + { MPR_TYPE_FUNCTION, "function" }, + { MPR_TYPE_STRING, "string" }, + { MPR_TYPE_STRING_CFUNCTION, "string_c_function" }, + { MPR_TYPE_PTR, "pointer" } + }; + int i; + const char *type = NULL; + + if (argc != 1) return -1; + + for (i=0;itype == types[i].type) { + type = types[i].name; + break; + } + } + if (type == NULL) return -1; + + mpr_ReturnString(eid, type); + return 0; +} + /* libinclude() allows you to include js files using a search path specified in "js include =" in smb.conf. @@ -112,6 +151,47 @@ static int ejs_libinclude(int eid, int argc, char **argv) return 0; } +/* + jsonrpc_include() allows you to include jsonrpc files from a path + based at "jsonrpc base =" in smb.conf. +*/ +static int ejs_jsonrpc_include(int eid, int argc, char **argv) +{ + int ret = -1; + char *path; + char *emsg; + const char *jsonrpc_base = lp_jsonrpc_base(); + struct MprVar result; + + + if (jsonrpc_base == NULL || jsonrpc_base == NULL) { + ejsSetErrorMsg(eid, "js include path not set"); + return -1; + } + + if (argc != 1) { + mpr_Return(eid, mprCreateIntegerVar(-1)); + return 0; + } + + path = talloc_asprintf(mprMemCtx(), "%s/%s", jsonrpc_base, argv[0]); + if (path == NULL) { + mpr_Return(eid, mprCreateIntegerVar(-1)); + return 0; + } + + if (file_exist(path)) { + ret = ejsEvalFile(eid, path, &result, &emsg); + if (ret < 0) { + printf("file found; ret=%d (%s)\n", ret, emsg); + } + } + + mpr_Return(eid, mprCreateIntegerVar(ret)); + talloc_free(path); + return 0; +} + /* return the current version */ @@ -153,7 +233,9 @@ void smb_setup_ejs_functions(void (*exception_handler)(const char *)) talloc_free(shared_init); ejsDefineCFunction(-1, "typeof", ejs_typeof, NULL, MPR_VAR_SCRIPT_HANDLE); + ejsDefineCFunction(-1, "nativeTypeOf", ejs_typeof_native, NULL, MPR_VAR_SCRIPT_HANDLE); ejsDefineStringCFunction(-1, "libinclude", ejs_libinclude, NULL, MPR_VAR_SCRIPT_HANDLE); + ejsDefineStringCFunction(-1, "jsonrpc_include", ejs_jsonrpc_include, NULL, MPR_VAR_SCRIPT_HANDLE); ejsDefineCFunction(-1, "version", ejs_version, NULL, MPR_VAR_SCRIPT_HANDLE); } diff --git a/source4/scripting/ejs/smbcalls_sys.c b/source4/scripting/ejs/smbcalls_sys.c index d8aaf3898a..42990f49c0 100644 --- a/source4/scripting/ejs/smbcalls_sys.c +++ b/source4/scripting/ejs/smbcalls_sys.c @@ -57,6 +57,22 @@ static int ejs_sys_hostname(MprVarHandle eid, int argc, struct MprVar **argv) } +/* + return current time as seconds and microseconds +*/ +static int ejs_sys_gettimeofday(MprVarHandle eid, int argc, struct MprVar **argv) +{ + struct timeval tv = timeval_current(); + struct MprVar v = mprObject("timeval"); + struct MprVar sec = mprCreateIntegerVar(tv.tv_sec); + struct MprVar usec = mprCreateIntegerVar(tv.tv_usec); + + mprCreateProperty(&v, "sec", &sec); + mprCreateProperty(&v, "usec", &usec); + mpr_Return(eid, v); + return 0; +} + /* return current time as a 64 bit nttime value */ @@ -85,6 +101,35 @@ static int ejs_sys_unix2nttime(MprVarHandle eid, int argc, struct MprVar **argv) return 0; } +/* + return the GMT time represented by the struct tm argument, as a time_t value +*/ +static int ejs_sys_gmmktime(MprVarHandle eid, int argc, struct MprVar **argv) +{ + struct MprVar *o; + struct tm tm; + if (argc != 1 || !mprVarIsObject(argv[0]->type)) { + ejsSetErrorMsg(eid, "sys_gmmktime invalid arguments"); + return -1; + } + + o = argv[0]; +#define TM_EL(n) tm.n = mprVarToNumber(mprGetProperty(o, #n, NULL)) + TM_EL(tm_sec); + TM_EL(tm_min); + TM_EL(tm_hour); + TM_EL(tm_mday); + TM_EL(tm_mon); + TM_EL(tm_year); + TM_EL(tm_wday); + TM_EL(tm_yday); + TM_EL(tm_isdst); +#undef TM_EL + + mpr_Return(eid, mprCreateIntegerVar(mktime(&tm))); + return 0; +} + /* return the given time as a gmtime structure */ @@ -97,6 +142,41 @@ static int ejs_sys_gmtime(MprVarHandle eid, int argc, struct MprVar **argv) ejsSetErrorMsg(eid, "sys_gmtime invalid arguments"); return -1; } + t = (time_t) mprVarToNumber(argv[0]); + tm = gmtime(&t); + if (tm == NULL) { + mpr_Return(eid, mprCreateUndefinedVar()); + return 0; + } + ret = mprObject("gmtime"); +#define TM_EL(n) mprSetVar(&ret, #n, mprCreateIntegerVar(tm->n)) + TM_EL(tm_sec); + TM_EL(tm_min); + TM_EL(tm_hour); + TM_EL(tm_mday); + TM_EL(tm_mon); + TM_EL(tm_year); + TM_EL(tm_wday); + TM_EL(tm_yday); + TM_EL(tm_isdst); +#undef TM_EL + + mpr_Return(eid, ret); + return 0; +} + +/* + return the given NT time as a gmtime structure +*/ +static int ejs_sys_ntgmtime(MprVarHandle eid, int argc, struct MprVar **argv) +{ + time_t t; + struct MprVar ret; + struct tm *tm; + if (argc != 1 || !mprVarIsNumber(argv[0]->type)) { + ejsSetErrorMsg(eid, "sys_ntgmtime invalid arguments"); + return -1; + } t = nt_time_to_unix(mprVarToNumber(argv[0])); tm = gmtime(&t); if (tm == NULL) { @@ -114,6 +194,7 @@ static int ejs_sys_gmtime(MprVarHandle eid, int argc, struct MprVar **argv) TM_EL(tm_wday); TM_EL(tm_yday); TM_EL(tm_isdst); +#undef TM_EL mpr_Return(eid, ret); return 0; @@ -332,8 +413,11 @@ static int ejs_sys_init(MprVarHandle eid, int argc, struct MprVar **argv) mprSetCFunction(obj, "interfaces", ejs_sys_interfaces); mprSetCFunction(obj, "hostname", ejs_sys_hostname); mprSetCFunction(obj, "nttime", ejs_sys_nttime); + mprSetCFunction(obj, "getTimeOfDay", ejs_sys_gettimeofday); mprSetCFunction(obj, "unix2nttime", ejs_sys_unix2nttime); + mprSetCFunction(obj, "gmmktime", ejs_sys_gmmktime); mprSetCFunction(obj, "gmtime", ejs_sys_gmtime); + mprSetCFunction(obj, "ntgmtime", ejs_sys_ntgmtime); mprSetCFunction(obj, "ldaptime", ejs_sys_ldaptime); mprSetCFunction(obj, "httptime", ejs_sys_httptime); mprSetStringCFunction(obj, "unlink", ejs_sys_unlink); diff --git a/source4/scripting/libjs/provision.js b/source4/scripting/libjs/provision.js index 1328cfe8fe..bba3d124ff 100644 --- a/source4/scripting/libjs/provision.js +++ b/source4/scripting/libjs/provision.js @@ -113,7 +113,7 @@ function ldaptime() */ function datestring() { - var t = sys.gmtime(sys.nttime()); + var t = sys.ntgmtime(sys.nttime()); return sprintf("%04u%02u%02u%02u", t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour); } diff --git a/source4/web_server/http.c b/source4/web_server/http.c index 45cc57d87a..6e42c8a39c 100644 --- a/source4/web_server/http.c +++ b/source4/web_server/http.c @@ -35,6 +35,7 @@ #define SWAT_SESSION_KEY "SwatSessionId" #define HTTP_PREAUTH_URI "/scripting/preauth.esp" +#define JSONRPC_REQUEST "/services" /* state of the esp subsystem for a specific request */ struct esp_state { @@ -414,6 +415,9 @@ static void http_setup_arrays(struct esp_state *esp) SETVAR(ESP_REQUEST_OBJ, "CONTENT_LENGTH", talloc_asprintf(esp, "%u", web->input.content_length)); SETVAR(ESP_REQUEST_OBJ, "QUERY_STRING", web->input.query_string); +#if 0 /* djl -- not yet. need to track down the compiler warning */ + SETVAR(ESP_REQUEST_OBJ, "POST_DATA", web->input.partial); +#endif SETVAR(ESP_REQUEST_OBJ, "REQUEST_METHOD", web->input.post_request?"POST":"GET"); SETVAR(ESP_REQUEST_OBJ, "REQUEST_URI", web->input.url); p = strrchr(web->input.url, '/'); @@ -518,7 +522,6 @@ static void esp_request(struct esp_state *esp, const char *url) talloc_free(buf); } - /* perform pre-authentication on every page is /scripting/preauth.esp exists. If this script generates any non-whitepace output at all, @@ -541,9 +544,9 @@ static BOOL http_preauth(struct esp_state *esp) esp_request(esp, HTTP_PREAUTH_URI); for (i=0;iweb->output.content.length;i++) { if (!isspace(esp->web->output.content.data[i])) { - /* if the preauth has generated content, then force it to be - html, so that we can show the login page for failed - access to images */ + /* if the preauth has generated content, then force it + to be html, so that we can show the login page for + failed access to images */ http_setHeader(esp->web, "Content-Type: text/html", 0); return False; } @@ -763,11 +766,16 @@ void http_process_input(struct websrv_context *web) void *ejs_save = ejs_save_state(); int i; const char *file_type = NULL; - BOOL esp_enable = False; + enum page_type { + page_type_simple, + page_type_esp, + page_type_jsonrpc + }; + enum page_type page_type; const struct { const char *extension; const char *mime_type; - BOOL esp_enable; + enum page_type page_type; } mime_types[] = { {"gif", "image/gif"}, {"png", "image/png"}, @@ -847,20 +855,29 @@ void http_process_input(struct websrv_context *web) esp->req = espCreateRequest(web, web->input.url, esp->variables); if (esp->req == NULL) goto internal_error; - /* work out the mime type */ - p = strrchr(web->input.url, '.'); - if (p == NULL) { - esp_enable = True; - } - for (i=0;p && iinput.url, JSONRPC_REQUEST) == 0) { + page_type = page_type_jsonrpc; + file_type = "text/json"; + + } else { + p = strrchr(web->input.url, '.'); + if (p == NULL) { + page_type = page_type_esp; + } + for (i=0;p && iinput.url); - } else { - http_simple_request(web); - } - } + /* + * Do pre-authentication. If pre-authentication succeeds, do + * page-type-specific processing. + */ + switch(page_type) + { + case page_type_simple: + if (http_preauth(esp)) { + http_simple_request(web); + } + break; + + case page_type_esp: + if (http_preauth(esp)) { + esp_request(esp, web->input.url); + } + break; + + case page_type_jsonrpc: +#if 0 /* djl -- not yet */ + if (! jsonrpc_request(esp)) { + http_error(web, 500, "Out of memory"); + } +#endif + break; + } if (web->conn == NULL) { /* the connection has been terminated above us, probably -- cgit