FYI: XML assumes UTF-8 as the
encoding type if not otherwise speficied (like in XML-RPC), in your software you
should expect UTF-8 strings when parsing results. SL uses UTF-8 for it's
encoding of strings.
ASP
This ASP example
uses the aspxmlrpc http://aspxmlrpc.sourceforge.net/
library by David Carter-Tod, et. al..
<%
Dim paramList(0)
Set paramList(0) = CreateObject("Scripting.Dictionary")
paramList(0).Add "Channel","0d58c21c-b56f-57df-745f-9ccd0e06df49"
paramList(0).Add "IntValue",CByte(17)
paramList(0).Add "StringValue","Hello, Alexander!"
Dim myresp
on error resume next
Set myresp = xmlRPC("http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi", "llRemoteData", paramList)
on error goto 0
if err.number <>0 then
response.write("Error number: " & err.number & "<P>")
response.write("Error description: " & err.description & "<P>")
else
response.write("Channel: " & myresp("Channel") & vbCrLf)
response.write("IntValue: " & myresp("IntValue") & vbCrLf)
response.write("StringValue: " & myresp("StringValue") & vbCrLf)
end if
%>
C#
C# Implementation by DedricMauriac.
Make sure to add a reference to System.Web in your project.
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Web;
/*
* XmlRpc rpc = new XmlRpc(
* new Guid("ba70440d-7d8b-cd1e-db1c-198d563bae55"),
* "This is some text",
* 42);
* rpc.llRemoteData();
* Debug.WriteLine(rpc.channel, "channel");
* Debug.WriteLine(rpc.stringValue, "string");
* Debug.WriteLine(rpc.intValue, "int");
*/
public class XmlRpc
{
private const string MethodCallTemplate = @"<?xml version=""1.0""?><methodCall><methodName>llRemoteData</methodName><params><param><value><struct><member><name>Channel</name><value><string>{0}</string></value></member><member><name>IntValue</name><value><int>{1}</int></value></member><member><name>StringValue</name><value><string>{2}</string></value></member></struct></value></param></params></methodCall>";
public Guid channel;
public string stringValue;
public int intValue;
public bool TimedOut;
public XmlRpc()
{
}
public XmlRpc(Guid channel, string stringValue, int intValue)
{
this.channel = channel;
this.stringValue = stringValue;
this.intValue = intValue;
}
public void llRemoteData()
{
string methodCall = string.Format(MethodCallTemplate,
this.channel,
this.intValue,
HttpUtility.HtmlEncode(this.stringValue)
);
Uri requestUri = new Uri("http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi");
string methodResponse = Post(requestUri, methodCall);
ParseResponse(methodResponse);
}
private void ParseResponse(string methodResponse)
{
if (methodResponse.IndexOf("<fault>") != -1)
{
string message = Regex.Match(methodResponse,
"<value><string>([^>]+)</string></value>")
.Groups[1].Value;
throw new ApplicationException(message);
}
MatchCollection matches = Regex.Matches(methodResponse, @"<name>([^<]+)</name>\s*<value>\s*<(string|int)>([^<]+)</\2>\s*</value>");
foreach (Match match in matches)
{
string name = match.Groups[1].Value;
string value = match.Groups[3].Value;
switch (name)
{
case "Channel":
this.channel = new Guid(value);
break;
case "StringValue":
this.stringValue = HttpUtility.HtmlDecode(value);
break;
case "IntValue":
this.intValue = int.Parse(value);
break;
}
}
}
private string Post(Uri requestUri, string data)
{
TimedOut = false;
byte[] postData = ASCIIEncoding.Default.GetBytes(data);
HttpWebRequest request = null;
Stream writer = null;
Stream reader = null;
WebResponse response = null;
try
{
request = HttpWebRequest.Create(requestUri) as HttpWebRequest;
request.Method = "POST";
request.KeepAlive = false;
request.ContentType = "application/x-www-form-urlencoded";
request.Timeout = 10000;
request.ContentLength = postData.Length;
writer = request.GetRequestStream();
writer.Write(postData, 0, postData.Length);
writer.Close();
response = request.GetResponse();
reader = response.GetResponseStream();
byte[] postResponse = new byte[response.ContentLength];
reader.Read(postResponse, 0, postResponse.Length);
return ASCIIEncoding.Default.GetString(postResponse);
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.Timeout)
{
TimedOut = true;
return "";
}
else
throw;
}
finally
{
if (writer != null)
{
writer.Close();
writer.Dispose();
}
if (reader != null)
{
reader.Close();
reader.Dispose();
}
if (response != null)
{
response.Close();
}
}
}
}
C++
This C++ example uses the XmlRpc++ implementation, with some
modifications listed below
Modified XmlRpcValue.cpp (v.07)
#include "XmlRpcValue.h"
#include "XmlRpcException.h"
#include "XmlRpcUtil.h"
#include "base64.h"
#ifndef MAKEDEPEND
# include <iostream>
# include <ostream>
# include <stdlib.h>
# include <stdio.h>
#endif
namespace XmlRpc {
static const char VALUE_TAG[] = "<value>";
static const char VALUE_ETAG[] = "</value>";
static const char BOOLEAN_TAG[] = "<boolean>";
static const char BOOLEAN_ETAG[] = "</boolean>";
static const char DOUBLE_TAG[] = "<double>";
static const char DOUBLE_ETAG[] = "</double>";
static const char INT_TAG[] = "<int>";
static const char I4_TAG[] = "<int>"; /* Was i4 */
static const char I4_ETAG[] = "</int>"; /* Was i4 */
static const char STRING_TAG[] = "<string>";
static const char STRING_ETAG[] = "</string>";
static const char DATETIME_TAG[] = "<dateTime.iso8601>";
static const char DATETIME_ETAG[] = "</dateTime.iso8601>";
static const char BASE64_TAG[] = "<base64>";
static const char BASE64_ETAG[] = "</base64>";
static const char ARRAY_TAG[] = "<array>";
static const char DATA_TAG[] = "<data>";
static const char DATA_ETAG[] = "</data>";
static const char ARRAY_ETAG[] = "</array>";
static const char STRUCT_TAG[] = "<struct>";
static const char MEMBER_TAG[] = "<member>";
static const char NAME_TAG[] = "<name>";
static const char NAME_ETAG[] = "</name>";
static const char MEMBER_ETAG[] = "</member>";
static const char STRUCT_ETAG[] = "</struct>";
// Format strings
std::string XmlRpcValue::_doubleFormat("%f");
// Clean up
void XmlRpcValue::invalidate()
{
switch (_type) {
case TypeString: delete _value.asString; break;
case TypeDateTime: delete _value.asTime; break;
case TypeBase64: delete _value.asBinary; break;
case TypeArray: delete _value.asArray; break;
case TypeStruct: delete _value.asStruct; break;
default: break;
}
_type = TypeInvalid;
_value.asBinary = 0;
}
// Type checking
void XmlRpcValue::assertTypeOrInvalid(Type t)
{
if (_type == TypeInvalid)
{
_type = t;
switch (_type) { // Ensure there is a valid value for the type
case TypeString: _value.asString = new std::string(); break;
case TypeDateTime: _value.asTime = new struct tm(); break;
case TypeBase64: _value.asBinary = new BinaryData(); break;
case TypeArray: _value.asArray = new ValueArray(); break;
case TypeStruct: _value.asStruct = new ValueStruct(); break;
default: _value.asBinary = 0; break;
}
}
else if (_type != t)
throw XmlRpcException("type error");
}
void XmlRpcValue::assertArray(int size) const
{
if (_type != TypeArray)
throw XmlRpcException("type error: expected an array");
else if (int(_value.asArray->size()) < size)
throw XmlRpcException("range error: array index too large");
}
void XmlRpcValue::assertArray(int size)
{
if (_type == TypeInvalid) {
_type = TypeArray;
_value.asArray = new ValueArray(size);
} else if (_type == TypeArray) {
if (int(_value.asArray->size()) < size)
_value.asArray->resize(size);
} else
throw XmlRpcException("type error: expected an array");
}
void XmlRpcValue::assertStruct()
{
if (_type == TypeInvalid) {
_type = TypeStruct;
_value.asStruct = new ValueStruct();
} else if (_type != TypeStruct)
throw XmlRpcException("type error: expected a struct");
}
// Operators
XmlRpcValue& XmlRpcValue::operator=(XmlRpcValue const& rhs)
{
if (this != &rhs)
{
invalidate();
_type = rhs._type;
switch (_type) {
case TypeBoolean: _value.asBool = rhs._value.asBool; break;
case TypeInt: _value.asInt = rhs._value.asInt; break;
case TypeDouble: _value.asDouble = rhs._value.asDouble; break;
case TypeDateTime: _value.asTime = new struct tm(*rhs._value.asTime); break;
case TypeString: _value.asString = new std::string(*rhs._value.asString); break;
case TypeBase64: _value.asBinary = new BinaryData(*rhs._value.asBinary); break;
case TypeArray: _value.asArray = new ValueArray(*rhs._value.asArray); break;
case TypeStruct: _value.asStruct = new ValueStruct(*rhs._value.asStruct); break;
default: _value.asBinary = 0; break;
}
}
return *this;
}
// Predicate for tm equality
static bool tmEq(struct tm const& t1, struct tm const& t2) {
return t1.tm_sec == t2.tm_sec && t1.tm_min == t2.tm_min &&
t1.tm_hour == t2.tm_hour && t1.tm_mday == t1.tm_mday &&
t1.tm_mon == t2.tm_mon && t1.tm_year == t2.tm_year;
}
bool XmlRpcValue::operator==(XmlRpcValue const& other) const
{
if (_type != other._type)
return false;
switch (_type) {
case TypeBoolean: return ( !_value.asBool && !other._value.asBool) ||
( _value.asBool && other._value.asBool);
case TypeInt: return _value.asInt == other._value.asInt;
case TypeDouble: return _value.asDouble == other._value.asDouble;
case TypeDateTime: return tmEq(*_value.asTime, *other._value.asTime);
case TypeString: return *_value.asString == *other._value.asString;
case TypeBase64: return *_value.asBinary == *other._value.asBinary;
case TypeArray: return *_value.asArray == *other._value.asArray;
// The map<>::operator== requires the definition of value< for kcc
case TypeStruct: //return *_value.asStruct == *other._value.asStruct;
{
if (_value.asStruct->size() != other._value.asStruct->size())
return false;
ValueStruct::const_iterator it1=_value.asStruct->begin();
ValueStruct::const_iterator it2=other._value.asStruct->begin();
while (it1 != _value.asStruct->end()) {
const XmlRpcValue& v1 = it1->second;
const XmlRpcValue& v2 = it2->second;
if ( ! (v1 == v2))
return false;
it1++;
it2++;
}
return true;
}
default: break;
}
return true; // Both invalid values ...
}
bool XmlRpcValue::operator!=(XmlRpcValue const& other) const
{
return !(*this == other);
}
// Works for strings, binary data, arrays, and structs.
int XmlRpcValue::size() const
{
switch (_type) {
case TypeString: return int(_value.asString->size());
case TypeBase64: return int(_value.asBinary->size());
case TypeArray: return int(_value.asArray->size());
case TypeStruct: return int(_value.asStruct->size());
default: break;
}
throw XmlRpcException("type error");
}
// Checks for existence of struct member
bool XmlRpcValue::hasMember(const std::string& name) const
{
return _type == TypeStruct && _value.asStruct->find(name) != _value.asStruct->end();
}
// Set the value from xml. The chars at *offset into valueXml
// should be the start of a <value> tag. Destroys any existing value.
bool XmlRpcValue::fromXml(std::string const& valueXml, int* offset)
{
int savedOffset = *offset;
invalidate();
if ( ! XmlRpcUtil::nextTagIs(VALUE_TAG, valueXml, offset))
return false; // Not a value, offset not updated
int afterValueOffset = *offset;
std::string typeTag = XmlRpcUtil::getNextTag(valueXml, offset);
bool result = false;
if (typeTag == BOOLEAN_TAG)
result = boolFromXml(valueXml, offset);
else if (typeTag == I4_TAG || typeTag == INT_TAG)
result = intFromXml(valueXml, offset);
else if (typeTag == DOUBLE_TAG)
result = doubleFromXml(valueXml, offset);
else if (typeTag.empty() || typeTag == STRING_TAG)
result = stringFromXml(valueXml, offset);
else if (typeTag == DATETIME_TAG)
result = timeFromXml(valueXml, offset);
else if (typeTag == BASE64_TAG)
result = binaryFromXml(valueXml, offset);
else if (typeTag == ARRAY_TAG)
result = arrayFromXml(valueXml, offset);
else if (typeTag == STRUCT_TAG)
result = structFromXml(valueXml, offset);
// Watch for empty/blank strings with no <string>tag
else if (typeTag == VALUE_ETAG)
{
*offset = afterValueOffset; // back up & try again
result = stringFromXml(valueXml, offset);
}
if (result) // Skip over the </value> tag
XmlRpcUtil::findTag(VALUE_ETAG, valueXml, offset);
else // Unrecognized tag after <value>
*offset = savedOffset;
return result;
}
// Encode the Value in xml
std::string XmlRpcValue::toXml() const
{
switch (_type) {
case TypeBoolean: return boolToXml();
case TypeInt: return intToXml();
case TypeDouble: return doubleToXml();
case TypeString: return stringToXml();
case TypeDateTime: return timeToXml();
case TypeBase64: return binaryToXml();
case TypeArray: return arrayToXml();
case TypeStruct: return structToXml();
default: break;
}
return std::string(); // Invalid value
}
// Boolean
bool XmlRpcValue::boolFromXml(std::string const& valueXml, int* offset)
{
const char* valueStart = valueXml.c_str() + *offset;
char* valueEnd;
long ivalue = strtol(valueStart, &valueEnd, 10);
if (valueEnd == valueStart || (ivalue != 0 && ivalue != 1))
return false;
_type = TypeBoolean;
_value.asBool = (ivalue == 1);
*offset += int(valueEnd - valueStart);
return true;
}
std::string XmlRpcValue::boolToXml() const
{
std::string xml = VALUE_TAG;
xml += BOOLEAN_TAG;
xml += (_value.asBool ? "1" : "0");
xml += BOOLEAN_ETAG;
xml += VALUE_ETAG;
return xml;
}
// Int
bool XmlRpcValue::intFromXml(std::string const& valueXml, int* offset)
{
const char* valueStart = valueXml.c_str() + *offset;
char* valueEnd;
long ivalue = strtol(valueStart, &valueEnd, 10);
if (valueEnd == valueStart)
return false;
_type = TypeInt;
_value.asInt = int(ivalue);
*offset += int(valueEnd - valueStart);
return true;
}
std::string XmlRpcValue::intToXml() const
{
char buf[256];
snprintf(buf, sizeof(buf)-1, "%d", _value.asInt);
buf[sizeof(buf)-1] = 0;
std::string xml = VALUE_TAG;
xml += I4_TAG;
xml += buf;
xml += I4_ETAG;
xml += VALUE_ETAG;
return xml;
}
// Double
bool XmlRpcValue::doubleFromXml(std::string const& valueXml, int* offset)
{
const char* valueStart = valueXml.c_str() + *offset;
char* valueEnd;
double dvalue = strtod(valueStart, &valueEnd);
if (valueEnd == valueStart)
return false;
_type = TypeDouble;
_value.asDouble = dvalue;
*offset += int(valueEnd - valueStart);
return true;
}
std::string XmlRpcValue::doubleToXml() const
{
char buf[256];
snprintf(buf, sizeof(buf)-1, getDoubleFormat().c_str(), _value.asDouble);
buf[sizeof(buf)-1] = 0;
std::string xml = VALUE_TAG;
xml += DOUBLE_TAG;
xml += buf;
xml += DOUBLE_ETAG;
xml += VALUE_ETAG;
return xml;
}
// String
bool XmlRpcValue::stringFromXml(std::string const& valueXml, int* offset)
{
size_t valueEnd = valueXml.find('<', *offset);
if (valueEnd == std::string::npos)
return false; // No end tag;
_type = TypeString;
_value.asString = new std::string(XmlRpcUtil::xmlDecode(valueXml.substr(*offset, valueEnd-*offset)));
*offset += int(_value.asString->length());
return true;
}
std::string XmlRpcValue::stringToXml() const
{
std::string xml = VALUE_TAG;
xml += STRING_TAG;// optional
xml += XmlRpcUtil::xmlEncode(*_value.asString);
xml += STRING_ETAG;
xml += VALUE_ETAG;
return xml;
}
// DateTime (stored as a struct tm)
bool XmlRpcValue::timeFromXml(std::string const& valueXml, int* offset)
{
size_t valueEnd = valueXml.find('<', *offset);
if (valueEnd == std::string::npos)
return false; // No end tag;
std::string stime = valueXml.substr(*offset, valueEnd-*offset);
struct tm t;
if (sscanf(stime.c_str(),"%4d%2d%2dT%2d:%2d:%2d",&t.tm_year,&t.tm_mon,&t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec) != 6)
return false;
t.tm_isdst = -1;
_type = TypeDateTime;
_value.asTime = new struct tm(t);
*offset += int(stime.length());
return true;
}
std::string XmlRpcValue::timeToXml() const
{
struct tm* t = _value.asTime;
char buf[20];
snprintf(buf, sizeof(buf)-1, "%4d%02d%02dT%02d:%02d:%02d",
t->tm_year,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
buf[sizeof(buf)-1] = 0;
std::string xml = VALUE_TAG;
xml += DATETIME_TAG;
xml += buf;
xml += DATETIME_ETAG;
xml += VALUE_ETAG;
return xml;
}
// Base64
bool XmlRpcValue::binaryFromXml(std::string const& valueXml, int* offset)
{
size_t valueEnd = valueXml.find('<', *offset);
if (valueEnd == std::string::npos)
return false; // No end tag;
_type = TypeBase64;
std::string asString = valueXml.substr(*offset, valueEnd-*offset);
_value.asBinary = new BinaryData();
// check whether base64 encodings can contain chars xml encodes...
// convert from base64 to binary
int iostatus = 0;
base64<char> decoder;
std::back_insert_iterator<BinaryData> ins = std::back_inserter(*(_value.asBinary));
decoder.get(asString.begin(), asString.end(), ins, iostatus);
*offset += int(asString.length());
return true;
}
std::string XmlRpcValue::binaryToXml() const
{
// convert to base64
std::vector<char> base64data;
int iostatus = 0;
base64<char> encoder;
std::back_insert_iterator<std::vector<char> > ins = std::back_inserter(base64data);
encoder.put(_value.asBinary->begin(), _value.asBinary->end(), ins, iostatus, base64<>::crlf());
// Wrap with xml
std::string xml = VALUE_TAG;
xml += BASE64_TAG;
xml.append(base64data.begin(), base64data.end());
xml += BASE64_ETAG;
xml += VALUE_ETAG;
return xml;
}
// Array
bool XmlRpcValue::arrayFromXml(std::string const& valueXml, int* offset)
{
if ( ! XmlRpcUtil::nextTagIs(DATA_TAG, valueXml, offset))
return false;
_type = TypeArray;
_value.asArray = new ValueArray;
XmlRpcValue v;
while (v.fromXml(valueXml, offset))
_value.asArray->push_back(v); // copy...
// Skip the trailing </data>
(void) XmlRpcUtil::nextTagIs(DATA_ETAG, valueXml, offset);
return true;
}
// In general, its preferable to generate the xml of each element of the
// array as it is needed rather than glomming up one big string.
std::string XmlRpcValue::arrayToXml() const
{
std::string xml = VALUE_TAG;
xml += ARRAY_TAG;
xml += DATA_TAG;
int s = int(_value.asArray->size());
for (int i=0; i<s; ++i)
xml += _value.asArray->at(i).toXml();
xml += DATA_ETAG;
xml += ARRAY_ETAG;
xml += VALUE_ETAG;
return xml;
}
// Struct
bool XmlRpcValue::structFromXml(std::string const& valueXml, int* offset)
{
_type = TypeStruct;
_value.asStruct = new ValueStruct;
while (XmlRpcUtil::nextTagIs(MEMBER_TAG, valueXml, offset)) {
// name
const std::string name = XmlRpcUtil::parseTag(NAME_TAG, valueXml, offset);
// value
XmlRpcValue val(valueXml, offset);
if ( ! val.valid()) {
invalidate();
return false;
}
const std::pair<const std::string, XmlRpcValue> p(name, val);
_value.asStruct->insert(p);
(void) XmlRpcUtil::nextTagIs(MEMBER_ETAG, valueXml, offset);
}
return true;
}
// In general, its preferable to generate the xml of each element
// as it is needed rather than glomming up one big string.
std::string XmlRpcValue::structToXml() const
{
std::string xml = VALUE_TAG;
xml += STRUCT_TAG;
ValueStruct::const_iterator it;
for (it=_value.asStruct->begin(); it!=_value.asStruct->end(); ++it) {
xml += MEMBER_TAG;
xml += NAME_TAG;
xml += XmlRpcUtil::xmlEncode(it->first);
xml += NAME_ETAG;
xml += it->second.toXml();
xml += MEMBER_ETAG;
}
xml += STRUCT_ETAG;
xml += VALUE_ETAG;
return xml;
}
// Write the value without xml encoding it
std::ostream& XmlRpcValue::write(std::ostream& os) const {
switch (_type) {
default: break;
case TypeBoolean: os << _value.asBool; break;
case TypeInt: os << _value.asInt; break;
case TypeDouble: os << _value.asDouble; break;
case TypeString: os << *_value.asString; break;
case TypeDateTime:
{
struct tm* t = _value.asTime;
char buf[20];
snprintf(buf, sizeof(buf)-1, "%4d%02d%02dT%02d:%02d:%02d",
t->tm_year,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
buf[sizeof(buf)-1] = 0;
os << buf;
break;
}
case TypeBase64:
{
int iostatus = 0;
std::ostreambuf_iterator<char> out(os);
base64<char> encoder;
encoder.put(_value.asBinary->begin(), _value.asBinary->end(), out, iostatus, base64<>::crlf());
break;
}
case TypeArray:
{
int s = int(_value.asArray->size());
os << '{';
for (int i=0; i<s; ++i)
{
if (i > 0) os << ',';
_value.asArray->at(i).write(os);
}
os << '}';
break;
}
case TypeStruct:
{
os << '[';
ValueStruct::const_iterator it;
for (it=_value.asStruct->begin(); it!=_value.asStruct->end(); ++it)
{
if (it!=_value.asStruct->begin()) os << ',';
os << it->first << ':';
it->second.write(os);
}
os << ']';
break;
}
}
return os;
}
} // namespace XmlRpc
// ostream
std::ostream& operator<<(std::ostream& os, XmlRpc::XmlRpcValue& v)
{
// If you want to output in xml format:
//return os << v.toXml();
return v.write(os);
}
Test.c
#include <iostream>
#include "src/XmlRpc.h"
using namespace XmlRpc;
int main(int argc, char* argv[])
{
XmlRpcClient c("xmlrpc.secondlife.com", 80, "/cgi-bin/xmlrpc.cgi");
XmlRpcValue testLSL,result;
testLSL["Channel"] = "83af2b8d-80fa-1db6-b35a-b9c0c2dc8670";
testLSL["IntValue"] = 50;
testLSL["StringValue"] = "Hello World!";
if (c.execute("llRemoteData", testLSL, result))
{
std::cout << "Returned:\n";
std::cout << "\t" << result["IntValue"] << "\n\t" << result["StringValue"] << "\n\n";
}
else
{
std::cout << "Error calling 'llRemoteData'\n\n";
}
return 0;
}
C++ Example 2
This C++ example uses the KDE 3.x libraries (Linux)
and reads the XML data from a file.
test.h
#include <kio/job.h>
class Test : public QObject
{
Q_OBJECT
public:
Test();
protected slots:
void requestData
(KIO::Job *job, const QByteArray &data);
void requestResult
(KIO::Job *job);
};
test.cpp
#include "test.moc"
#include <kapp.h>
#include <kaboutdata.h>
#include <kcmdlineargs.h>
#include <kfile.h>
#include <stream.h>
int main(int argc, char **argv)
{
KAboutData about("test", "Test", "0.1",
"A test of communication with SL", KAboutData::License_GPL,
"Copyright (c) 2007 LSL wiki");
KCmdLineArgs::init(argc, argv, &about);
KApplication application(false, false);
Test *test = new Test();
return application.exec();
}
Test::Test()
: QObject()
{
KIO::SimpleJob *job;
KURL address("http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi");
QFile testFile("test.xml");
QByteArray toSend;
if (!testFile.open(IO_ReadOnly))
{
cerr << "Failed to open XML file";
return;
}
toSend = testFile.readAll();
testFile.close();
job = KIO::http_post(address, toSend, false);
job->addMetaData("accept", "text/xml, */*");
job->addMetaData("content-type", "Content-type: application/x-www-form-urlencoded");
connect(job,
SIGNAL(data(KIO::Job *, const QByteArray &)),
this,
SLOT(requestData(KIO::Job *, const QByteArray &)));
connect(job,
SIGNAL(result(KIO::Job *)),
this,
SLOT(requestResult(KIO::Job *)));
}
void Test::requestData(KIO::Job *job, const QByteArray &data)
{
cout << data;
}
void Test::requestResult(KIO::Job *job)
{
if (job->error())
cerr << "Failure";
else cout << "Transfer succeeded";
}
test.moc
Is generated automatically using Qt's "moc"
utility.
Java
This Java example uses the XML-RPC implementation available from the Apache
Web Services Project. This example uses an outdated version of
the library... does anyone want to fix it? -Fixed using v3.0 of apache. Revised
code below. DeleriumHannibal
import java.net.URL;
import java.util.Hashtable;
import java.util.Vector;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
public class RpcTest {
public static void main(String[] args) {
/*
* Put the arguments into an XML-RPC struct, represented by
* a Java Hashtable.
*/
Hashtable s = new Hashtable();
s.put("Channel", "5b90c3ad-a641-fc2c-55e1-dfcc3eefc7cd");
s.put("IntValue", new Integer(17));
s.put("StringValue", "Hello, Alexander!");
/*
* Argument list for the call is a vector containing the struct.
*/
Vector v = new Vector();
v.add(s);
try {
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
config.setServerURL(new URL("http://xmlrpc.secondlife.com:80/cgi-bin/xmlrpc.cgi"));
XmlRpcClient client = new XmlRpcClient();
client.setConfig(config);
Object result = client.execute("llRemoteData", v);
if (result != null)
System.out.println("Result class: " + result.getClass().getName());
} catch(Exception e) {
System.out.println("Failed: " + e);
}
}
}
JavaScript
This JavaScript example uses the JavaScript O Lait http://jsolait.net/index.xhtml library
by Jan-Klaas Kollhof. However you should note that the default settings on most
browsers do not allow XML-RPC to external websites.
<script type="text/javascript" src="http://jsolait.net/examples/xmlrpc/jsolait/init.js"></script> <!--- You can replace these with local copies (download from http://jsolait.net/doc/index.xhtml) --->
<script type="text/javascript" src="http://jsolait.net/examples/xmlrpc/jsolait/lib/urllib.js"></script>
<script type="text/javascript" src="http://jsolait.net/examples/xmlrpc/jsolait/lib/xml.js"></script>
<script type="text/javascript" src="http://jsolait.net/examples/xmlrpc/jsolait/lib/xmlrpc.js"></script>
<script type="text/javascript" >
var xmlrpc=null;
try{
var xmlrpc = importModule("xmlrpc");
}catch(e){
reportException(e);
throw "importing of xmlrpc module failed.";
}
function send_rpc(){
var addr = "http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi";
var sChannel = "0d58c21c-b56f-57df-745f-9ccd0e06df49"; // Add your ChannelID and arguments (can be pulled from textboxes)
var iValue = 17;
var sString = "Hello, Alexander!";
var evalStr = {"Channel" : sChannel, "IntValue" : iValue, "StringValue" : sString};
try{
var service = new xmlrpc.ServiceProxy(addr);
var rslt = service.llRemoteData(evalStr); // Result object
alert(rslt["StringValue"]); // Returns String Value
alert(rslt["IntValue"]); // Returns Int Value
}catch(e){
var em;
if(e.toTraceString){
em=e.toTraceString();
}else{
em = e.message;
}
alert("Error trace: \n\n" + em);
}
return false;
}
</script>
Perl
Here's an example in Perl. It uses the RPC::XML module that is
available from CPAN.
The values for $key, $ival and
$sval need to be valid of course.
require RPC::XML;
require RPC::XML::Client;
my $client = RPC::XML::Client->new('http://xmlrpc.secondlife.com:80/cgi-bin/xmlrpc.cgi');
my $request = RPC::XML::request->new('llRemoteData', RPC::XML::struct->new(
'Channel' => RPC::XML::string->new($key),
'IntValue' => RPC::XML::int->new($ival),
'StringValue' => RPC::XML::string->new($sval)
)
);
my $response = $client->simple_request($request);
if (!$response) {
print $RPC::XML::ERROR, "\n";
} else {
print $response->{StringValue}, "\n";
print $response->{IntValue}, "\n";
}
PHP Example 1
Here's an implementation in PHP, using PHP's XML-RPC library.
Note that you may need to compile it into your PHP build. Users of the PHP
Windows binaries just need to add extension=php_xmlrpc.dll into their
php.ini.
<?php
function llRemoteData($channel, $intvalue, $stringvalue) {
$host = "xmlrpc.secondlife.com";
$port = 80;
$uri = "/cgi-bin/xmlrpc.cgi";
$request = xmlrpc_encode_request('llRemoteData',
array( 'Channel' => $channel,
'IntValue' => $intvalue,
'StringValue' => $stringvalue));
$fp = fsockopen($host, $port, $errno, $errstr);
$query = "POST " . $uri . " HTTP/1.0\n" .
"User_Agent: PHP XML-RPC Interface\n" .
"Host: " . $host . "\n" .
"Content-Type: text/xml\n" .
"Content-Length: " . strlen($request) . "\n\n" .
$request . "\n";
if (!fputs($fp, $query, strlen($query)))
return NULL; // some sort of write error occured
$xmlresp = "";
//I replaced some code that was here but did not work. This should work just as well, in less code and quicker ;p
//read the maximum amount (if you have more bytes than 8192, this method will not work)
$response = fread($fp, 8192);
//grab the XML
$xmlresp = (substr($response, strpos($response, "\r\n\r\n")+4));
fclose($fp);
if ($xmlresp == "")
return NULL;
else
return xmlrpc_decode($xmlresp);
}
?>
PHP Example 2
Here is a way to send RPC without the need of special
includes
<?php
function postToHost($host, $path, $data_to_send) {
$fp = fsockopen($host, 80);
fputs($fp, "POST $path HTTP/1.1\r\n");
fputs($fp, "Host: $host\r\n");
fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n");
fputs($fp, "Content-length: ". strlen($data_to_send) ."\r\n");
fputs($fp, "Connection: close\r\n\r\n");
fputs($fp, $data_to_send);
$res = "";
while(!feof($fp)) {
$res .= fgets($fp, 128);
}
fclose($fp);
return substr($res, strpos($res, "\r\n\r\n"));;
}
function parseResponse($response) {
$result = array();
if (preg_match_all('#<name>(.+)</name><value><(string|int)>(.*)</\2></value>#U', $response, $regs, PREG_SET_ORDER)) {
foreach($regs as $key=>$val) {
$result[$val[1]] = $val[3];
}
}
return $result;
}
function sendRequest($channel, $intValue, $stringValue) {
$channel = htmlspecialchars($channel);
$int = (int)$intValue;
$string = htmlspecialchars($stringValue);
$data = '<?xml version="1.0"?>';
$data .= '<methodCall>';
$data .= '<methodName>llRemoteData</methodName>';
$data .= '<params><param><value><struct>';
$data .= '<member><name>Channel</name><value><string>'.$channel.'</string></value></member>';
$data .= '<member><name>IntValue</name><value><int>'.$int.'</int></value></member>';
$data .= '<member><name>StringValue</name><value><string>'.$string.'</string></value></member>';
$data .= '</struct></value></param></params></methodCall>';
return parseResponse(postToHost("xmlrpc.secondlife.com","/cgi-bin/xmlrpc.cgi", $data));
}
?>
Python Example 1
Client example in python using xmlrpclib (included in
Python 2.2+):
#!/usr/local/bin/python
from xmlrpclib import ServerProxy;
import sys;
def llRemoteData(Channel, Int, String):
client = ServerProxy("http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi");
return client.llRemoteData({"Channel" : Channel, "IntValue" : Int, "StringValue" : String});
reply = llRemoteData("9307b83e-0739-63d5-86a5-f510aded12f4", 0, "Hello from " + sys.platform);
for name, value in reply.iteritems():
print name, value;
Python Example 2
Another python example using the python-xmlrpc module
which comes as a nice debian package:
#!/usr/bin/python
import xmlrpc
client = xmlrpc.client("xmlrpc.secondlife.com", 80, "/cgi-bin/xmlrpc.cgi")
print client.execute("llRemoteData", [{"Channel": "2ac9fac6-6194-8e2a-2415-06257dea4a72", "IntValue": 0, "StringValue": "yourmessage"}])
Python Example 3
Another python example using twisted:
from twisted.web.xmlrpc import Proxy
from twisted.internet import reactor
def printValue(value):
print repr(value)
reactor.stop()
def printError(error):
print 'error', error
reactor.stop()
proxy = Proxy('http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi')
proxy.callRemote('llRemoteData', {"Channel": "a9c9b2e5-2b91-3e3e-ef98-a26b83d21fcd", "IntValue": 0, "StringValue": "yourmessage"}).addCallbacks(printValue, printError)
reactor.run()
Ruby
Here is a Ruby example using xmlrpc4r:
#!/usr/bin/ruby
require "xmlrpc/client"
server = XMLRPC::Client.new("xmlrpc.secondlife.com","/cgi-bin/xmlrpc.cgi")
begin
result = server.call("llRemoteData", {
"Channel" => channel,
"StringValue" => sdata,
"IntValue" => idata
})
puts "Channel: #{result['Channel']}"
puts "StringValue: #{result['StringValue']}"
puts "IntValue: #{result['IntValue']}"
rescue XMLRPC::FaultException => e
puts "Error: #{e.faultCode} (#{e.faultString})"
end
XMLRPC / XMLRPCDiscussion