Monday, November 08, 2010 at 12:02 AM.
system.verbs.builtins.betty.rpc.server
on server (xmlText, adrParamTable) { //called from the RPC2 responder on an incoming request
<<Change notes:
<<5/24/06; 8:15:45 AM by DW
<<Handle multiple domains as described here...
<<http://geeks.opml.org/2006/05/24#a865
<<01/31/02; 2:13:15 PM by JES
<<Factored the security check by IP address to betty.rpc.checkClient, so the check can be used for both XML-RPC and SOAP.
<<04/27/01; 7:58:15 PM by JES
<<If a request doesn't have a <params> element, don't fault. Instead treat it as a call with no input parameters.
<<Thu, 02 Dec 1999 at 5:17:37 PM by AR
<<Disabled fldebug flag again.
<<Wed, 04 Nov 1999 at 10:33:45 PM by AR
<<If user.betty.prefs.flAllowByIpOnly is true, block XML-RPC requests from anyone except those IP addresses listed in user.betty.prefs.allowedIpAddresses
<<Mon, 25 Oct 1999 at 11:07:19 PM by AR
<<If user.betty.prefs.flKeepServerLog is true, we log all incoming requests to the rpcServer section of the daily log GDB.
<<Mon, 14 Jun 1999 15:46:57 GMT by AR
<<When walking the XML structure to extract the parameters, we now handle the case correctly when a parameter is a table and the next parameter is a scalar.
<<If an RPC fault occurs, left angle brackets and ampersands in the error string are now properly encoded to ensure that the XML-RPC response is well-formed.
<<Friday, July 17, 1998 at 12:57:38 PM by PBS
<<XML header is now lowercase to conform to the XML spec.
<<Turned off debugging; use a local table rather than a global table so the server is thread-safe.
local (fldebug = false, flFault = false, securitylist = {});
betty.init ();
if not defined (user.betty.prefs.flServerDomains) {
user.betty.prefs.flServerDomains = false};
local (adrhandlerstable = @user.betty.rpcHandlers);
if user.betty.prefs.flServerDomains { //5/24/06 by DW
if defined (user.betty.domains) {
if defined (adrparamtable^.host) {
local (host = adrparamtable^.host);
if host contains ":" { //remove the port designator
host = string.nthfield (host, ":", 1)};
local (adrtable = @user.betty.domains.[host]);
if defined (adrtable^) {
adrhandlerstable = adrtable}}}};
local (flLog = user.betty.prefs.flKeepServerLog);
local (xmlResponse = "", indentlevel = 1);
on add (s) {
xmlResponse = xmlResponse + string.filledString ("\t", indentlevel) + s + "\r\n"};
on addToRpcLog (errorNum = nil) { //AR 10/25/1999
local (adrHitTable = log.addToGuestDatabase ("rpcServer", adrParamTable^.client, true));
adrHitTable^.time = clock.now ();
adrHitTable^.threads = Frontier.countThreads ();
adrHitTable^.sizeIn = sizeof (adrParamTable^.requestBody);
adrHitTable^.sizeOut = sizeof (xmlResponse);
if errorNum != nil {
adrHitTable^.faultCode= errorNum};
if procedureName != nil {
adrHitTable^.methodName= procedureName};
if defined (adrParamTable^.host) {
adrHitTable^.host = adrParamTable^.host};
if defined (adrParamTable^.requestHeaders.["User-Agent"]) {
adrHitTable^.agent = adrParamTable^.requestHeaders.["User-Agent"]};
adrHitTable^.ticks = clock.ticks () - adrParamTable^.stats.requestProcessingStarted};
on rpcFault (errorstring, errornum) {
flFault = true;
add ("<fault>"); indentlevel++;
add ("<value>"); indentlevel++;
add ("<struct>"); indentlevel++;
add ("<member>"); indentlevel++;
add ("<name>faultCode</name>");
add ("<value>"); indentlevel++;
add ("<int>" + errornum + "</int>");
add ("</value>"); indentlevel--;
add ("</member>"); indentlevel--;
bundle { //AR 6/14/1999: make sure errorstring is encoded
errorstring = string.replaceAll (errorstring, "&", "&");
errorstring = string.replaceAll (errorstring, "<", "<")};
add ("<member>"); indentlevel++;
add ("<name>faultString</name>");
add ("<value>"); indentlevel++;
add ("<string>" + errorstring + "</string>");
add ("</value>"); indentlevel--;
add ("</member>"); indentlevel--;
add ("</struct>"); indentlevel--;
add ("</value>"); indentlevel++;
add ("</fault>"); indentlevel--;
if flLog {
try {
addToRpcLog (errornum)}}};
local (procedureName, paramlist);
bundle { //AR 11/04/1999: check for rpc request blocking
if not betty.rpc.checkClient (adrParamTable) { //1/31/02 JES: factored for use for both XML-RPC and SOAP
rpcFault ("Access not allowed from " + adrParamTable^.client + ".", betty.rpc.errorCodes.securityFault)}};
<<if defined (user.betty.prefs.flAllowByIpOnly)
<<if user.betty.prefs.flAllowByIpOnly
<<local (flBlocked = true)
<<local (okIpList = user.betty.prefs.allowedIpAddresses)
<<if typeOf (okIpList) == stringType
<<It's a comma-delimited string: create a local list from that string.
<<local (s = okIpList)
<<okIpList = {}
<<local (i)
<<for i = 1 to string.countFields (s, ',')
<<local (oneIpAddress = string.nthField (s, ',', i))
<<oneIpAddress = string.trimWhiteSpace (oneIpAddress)
<<if oneIpAddress != ""
<<okIpList = okIpList + oneIpAddress
<<local (oneIp)
<<for oneIp in okIpList
<<if string.trimWhiteSpace (oneIp) != ""
<<if adrParamTable^.client == oneIp
<<flblocked = false
<<break
<<if flblocked
<<rpcFault ("Access not allowed from " + adrParamTable^.client + ".", betty.rpc.errorCodes.securityFault)
if (not flFault) { //get the procedure name and the paramlist
on parseXML () {
local (xtable);
try { //parse the XML into a table
xml.compile (xmlText, @xtable)}
else {
rpcFault (tryError, betty.rpc.errorcodes.xmlFormatError);
return};
if fldebug {
scratchpad.rpcTable = xtable};
try { //set the procedure name and fill the params list by walking the XML structure
paramlist = {};
adrTable = @xtable; //PBS 7/17/98
local (adrcall = xml.getAddress (adrtable, "methodCall"));
procedureName = xml.getValue (adrcall, "methodName");
local (adrparams, flParams = true);
try { //04/27/2001 JES: if there's no <params> element, getting its address will fail
adrparams = xml.getAddress (adrcall, "params")}
else { //no params
flParams = false};
if flParams {
local (xmlparamlist = xml.getAddressList (adrparams, "param"));
local (param, name, adrvalue, val);
for param in xmlparamlist {
adrvalue = xml.getAddress (param, "value");
if typeof (adrvalue^) == tabletype {
xml.coercions.structToFrontierValue (@adrvalue^ [1], @val)}
else {
table.assign (@val, adrvalue^)}; //AR 6/14/1999
paramlist = paramlist + {val}}}}
else {
rpcFault (tryError, betty.rpc.errorcodes.illegalCallStructureError);
return};
if fldebug {
try {delete (@scratchpad.rpcParams)};
scratchpad.rpcParams = paramlist}};
parseXML ()};
if (not flFault) { //call the script, put result in xmlResponse
on callrpcScript () {
local (nomad, adrscript);
try { //locate the script
local (s = procedureName, name);
nomad = adrhandlerstable;
loop {
if typeOf (nomad^) == tabletype {
local (adr = @nomad^.["#security"]);
if defined (adr^) {
securitylist = securitylist + {adr}}};
if sizeof (s) == 0 {
break};
name = string.nthField (s, '.', 1);
nomad = @nomad^.[name];
if typeof (nomad^) == addresstype {
nomad = nomad^};
s = string.delete (s, 1, sizeof (name) + 1)};
adrscript = nomad}
else {
rpcFault (tryError, betty.rpc.errorcodes.scriptAddressError);
return};
try { //check in with the #security scripts
local (adr);
for adr in securitylist {
callScript (string (adr), {}, adrParamTable)}}
else {
rpcFault (tryError, betty.rpc.errorcodes.securityFault);
return};
local (scriptResponse);
try { //call the script
scriptResponse = callScript (string (adrscript), paramlist, adrParamTable)}
else {
rpcFault (tryError, betty.rpc.errorcodes.scriptError);
return};
local (encodedString);
try { //encode the response
encodedString = xml.coercions.frontierValueToTaggedText (@scriptResponse, 1)}
else {
rpcFault (tryError, betty.rpc.errorcodes.encodingFailure);
return};
try { //add the encoded string to the returned XML text
add ("<params>"); indentlevel++;
add ("<param>"); indentlevel++;
add ("<value>" + encodedString + "</value>");
add ("</param>"); indentlevel--;
add ("</params>"); indentlevel--}
else {
rpcFault (tryError, betty.rpc.errorcodes.addReturnedValueError);
return}};
callrpcScript ()};
local (pre = "<?xml version=\"1.0\"?>\r\n<methodResponse>\r\n");
local (post = "\t</methodResponse>\r\n");
xmlResponse = pre + xmlResponse + post;
if fldebug {
table.assign (@scratchpad.xmlResponse, xmlResponse)};
if flLog and not flFault { //AR 10/25/1999: don't log twice
try {
addToRpcLog ()}};
return (xmlResponse)}
This listing is for code that runs in the OPML Editor environment. I created these listings because I wanted the search engines to index it, so that when I want to look up something in my codebase I don't have to use the much slower search functionality in my object database. Dave Winer.