From 6dbea1df2ef5381a9cd4234884a60785ffd58583 Mon Sep 17 00:00:00 2001 From: cxl Date: Sun, 20 Dec 2009 20:16:01 +0000 Subject: [PATCH] New XmlRpc package git-svn-id: svn://ultimatepp.org/upp/trunk@1821 f0d560ea-af0d-0410-9eb7-867de7ffcac7 --- uppsrc/XmlRpc/Client.cpp | 43 ++++++++++ uppsrc/XmlRpc/Server.cpp | 107 +++++++++++++++++++++++ uppsrc/XmlRpc/Value.cpp | 110 ++++++++++++++++++++++++ uppsrc/XmlRpc/Xml.cpp | 170 +++++++++++++++++++++++++++++++++++++ uppsrc/XmlRpc/XmlRpc.h | 169 ++++++++++++++++++++++++++++++++++++ uppsrc/XmlRpc/XmlRpc.upp | 14 +++ uppsrc/XmlRpc/init | 4 + uppsrc/XmlRpc/xrs.h | 9 ++ uppsrc/XmlRpc/xrs_header.h | 27 ++++++ uppsrc/XmlRpc/xrs_source.h | 27 ++++++ 10 files changed, 680 insertions(+) create mode 100644 uppsrc/XmlRpc/Client.cpp create mode 100644 uppsrc/XmlRpc/Server.cpp create mode 100644 uppsrc/XmlRpc/Value.cpp create mode 100644 uppsrc/XmlRpc/Xml.cpp create mode 100644 uppsrc/XmlRpc/XmlRpc.h create mode 100644 uppsrc/XmlRpc/XmlRpc.upp create mode 100644 uppsrc/XmlRpc/init create mode 100644 uppsrc/XmlRpc/xrs.h create mode 100644 uppsrc/XmlRpc/xrs_header.h create mode 100644 uppsrc/XmlRpc/xrs_source.h diff --git a/uppsrc/XmlRpc/Client.cpp b/uppsrc/XmlRpc/Client.cpp new file mode 100644 index 000000000..138760fdd --- /dev/null +++ b/uppsrc/XmlRpc/Client.cpp @@ -0,0 +1,43 @@ +#include "XmlRpc.h" + +#define LLOG(x) LOG(x) + +Value XmlRpcCall::Execute() +{ + String request = XmlHeader(); + request << XmlTag("methodCall")(XmlTag("methodName")(method) + FormatXmlRpcParams(data.out)); + String response = server.Post(request).TimeoutMsecs(timeout).ExecuteRedirect(); + LLOG("response: " << response); + if(IsNull(response)) { + return ErrorValue("Http request failed: " + server.GetError()); + } + XmlParser p(response); + try { + p.ReadPI(); + p.PassTag("methodResponse"); + if(p.Tag("fault")) { + Value m = ParseXmlRpcValue(p); + if(IsValueMap(m)) { + ValueMap mm = m; + String s; + s << "Failed '" << mm["faultString"] << "' (" << mm["faultCode"] << ')'; + error = s; + LLOG(s); + return ErrorValue(s); + } + } + else { + data.in = ParseXmlRpcParams(p); + data.ii = 0; + p.PassEnd(); + } + } + catch(XmlError e) { + String s; + s << "XmlError " << e << ": " << p.GetPtr(); + LOG(s); + error = s; + return ErrorValue(s); + } + return data.in.GetCount() ? data.in[0] : Null; +} diff --git a/uppsrc/XmlRpc/Server.cpp b/uppsrc/XmlRpc/Server.cpp new file mode 100644 index 000000000..70d4792ab --- /dev/null +++ b/uppsrc/XmlRpc/Server.cpp @@ -0,0 +1,107 @@ +#include "XmlRpc.h" +#include "XmlRpc.h" + +#define LLOG(x) // LOG(x) + +VectorMap< String, void (*)(XmlRpcData&) >& XmlRpcMap(const char *group) +{ + static VectorMap > mm; + return mm.GetAdd(group); +} + +void Register(const char *name, void (*method)(XmlRpcData&), const char *group) +{ + XmlRpcMap(group).Add(name, method); +} + +String XmlRpcExecute(const String& request, const char *group) +{ + VectorMap< String, void (*)(XmlRpcData&) >& xmlrpcmap = XmlRpcMap(group); + XmlParser p(request); + XmlRpcData data; + String methodname; + try { + String r = XmlHeader(); + r << "\r\n"; + p.ReadPI(); + p.PassTag("methodCall"); + p.PassTag("methodName"); + methodname = p.ReadText(); + LLOG("method name: " << methodname); + p.PassEnd(); + data.in = ParseXmlRpcParams(p); + int q = xmlrpcmap.Find(methodname); + if(q < 0) + return FormatXmlRpcError(4, methodname + " method is unknown"); + else { + (*xmlrpcmap[q])(data); + if(IsValueArray(data.out)) { + if(IsError(data.out[0])) { + LLOG("ProcessingError"); + return FormatXmlRpcError(3, methodname + " Processing error: " + GetErrorText(data.out[0])); + } + r << FormatXmlRpcParams(data.out); + } + r << "\r\n\r\n"; + } + p.PassEnd(); + return r; + } + catch(XmlError e) { + LLOG("XmlError " << e << ": " << p.GetPtr()); + return FormatXmlRpcError(1, methodname + " XML Error: " + e); + } + catch(ValueTypeMismatch) { + LLOG("ValueTypeMismatch at parameter " << data.ii); + return FormatXmlRpcError(2, methodname + " Value type mismatch at parameter " + AsString(data.ii)); + } + return Null; +} + +int CharFilterNoCr(int c) +{ + return c == '\r' ? 0 : c; +} + +String ReadLine(Socket& s) +{ + return Filter(s.ReadUntil('\n'), CharFilterNoCr); +} + +bool XmlRpcServer(int port, const char *group) +{ + Socket rpc; + if(!ServerSocket(rpc, 1234, true, 5)) + return false; + for(;;) { + Socket http; + if(rpc.Accept(http)) { + LLOG("=== Accepted connection ==================================================="); + String request = ReadLine(http); + if(http.IsError()) continue; + VectorMap hdr; + for(;;) { + String s = ReadLine(http); + if(s.IsEmpty()) break; + LLOG(s); + int q = s.Find(':'); + if(q >= 0) + hdr.GetAdd(s.Mid(0, q)) = TrimLeft(s.Mid(q + 1)); + } + if(http.IsError()) continue; + String r = XmlRpcExecute(http.ReadCount(atoi(hdr.Get("Content-Length", "")), 90000), group); + LLOG("--------- Server response:\n" << r << "============="); + String response; + response << + "HTTP/1.1 200 OK\r\n" + "Date: Mon, 23 May 2005 22:38:34 GMT\r\n" + "Server: U++\r\n" + "Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT\r\n" + "Accept-Ranges: bytes\r\n" + "Content-Length: " << r.GetCount() << "\r\n" + "Connection: close\r\n" + "Content-Type: application/soap+xml; charset=utf-8\r\n\r\n" << r; + http.Write(response); + } + } +} diff --git a/uppsrc/XmlRpc/Value.cpp b/uppsrc/XmlRpc/Value.cpp new file mode 100644 index 000000000..1ebe3ab54 --- /dev/null +++ b/uppsrc/XmlRpc/Value.cpp @@ -0,0 +1,110 @@ +#include "XmlRpc.h" + +void ValueCheck(bool b) +{ + if(!b) + throw ValueTypeMismatch(); +} + +void ValueGet(int& n, const Value& v) +{ + ValueCheck(IsNumber(v)); + n = v; +} + +void ValueGet(String& s, const Value& v) +{ + ValueCheck(IsString(s)); + s = v; +} + +void ValueGet(double& x, const Value& v) +{ + ValueCheck(IsNumber(v)); + x = v; +} + +void ValueGet(bool& x, const Value& v) +{ + ValueCheck(IsNumber(x)); + x = v; +} + +void ValueGet(Date& x, const Value& v) +{ + ValueCheck(IsDateTime(v)); + x = v; +} + +void ValueGet(Time& x, const Value& v) +{ + ValueCheck(IsDateTime(v)); + x = v; +} + +void ValueGet(Value& t, const Value& v) +{ + t = v; +} + +void ValueGet(ValueArray& va, const Value& v) +{ + ValueCheck(IsValueArray(v)); + va = v; +} + +void ValueGet(ValueMap& vm, const Value& v) +{ + ValueCheck(IsValueMap(v)); + vm = v; +} + +void ValuePut(Value& v, int n) +{ + v = n; +} + +void ValuePut(Value& v, const String& s) +{ + v = s; +} + +void ValuePut(Value& v, const char *s) +{ + ValuePut(v, String(s)); +} + +void ValuePut(Value& v, double x) +{ + v = x; +} + +void ValuePut(Value& v, bool x) +{ + v = x; +} + +void ValuePut(Value& v, const Date& x) +{ + v = x; +} + +void ValuePut(Value& v, const Time& x) +{ + v = x; +} + +void ValuePut(Value& v, const Value& t) +{ + v = t; +} + +void ValuePut(Value& v, const ValueArray& va) +{ + v = va; +} + +void ValuePut(Value& v, const ValueMap& vm) +{ + v = vm; +} diff --git a/uppsrc/XmlRpc/Xml.cpp b/uppsrc/XmlRpc/Xml.cpp new file mode 100644 index 000000000..4d61519f4 --- /dev/null +++ b/uppsrc/XmlRpc/Xml.cpp @@ -0,0 +1,170 @@ +#include "XmlRpc.h" + +Value ParseXmlRpcValue(XmlParser& p) +{ + Value r; + p.PassTag("value"); + if(p.Tag("int") || p.Tag("i4")) { + String s = p.ReadText(); + CParser p(s); + if(!p.IsInt()) + throw XmlError("integer expected"); + r = p.ReadInt(); + } + else + if(p.Tag("boolean")) { + int n = StrInt(p.ReadText()); + if(n != 0 && n != 1) + throw XmlError("boolean expected"); + r = (bool)n; + } + else + if(p.Tag("double")) { + String s = p.ReadText(); + CParser p(s); + if(!p.IsDouble()) + throw XmlError("double expected"); + r = p.ReadDouble(); + } + else + if(p.Tag("string") || p.Tag("base64")) + r = p.ReadText(); + else + if(p.Tag("dateTime.iso8601")) { + String s = TrimBoth(p.ReadText()); + // 19980717T14:08:55 + // 01234567890123456 + if(s.GetCount() != 17 || s[8] != 'T' || s[11] != ':' || s[14] != ':') + throw XmlError("invalid dateTime format"); + Time tm; + tm.year = atoi(s.Mid(0, 4)); + tm.month = atoi(s.Mid(4, 2)); + tm.day = atoi(s.Mid(6, 2)); + tm.hour = atoi(s.Mid(9, 2)); + tm.minute = atoi(s.Mid(12, 2)); + tm.second = atoi(s.Mid(15, 2)); + r = tm; + } + else + if(p.Tag("array")) { + ValueArray va; + p.PassTag("data"); + while(!p.End()) + va.Add(ParseXmlRpcValue(p)); + r = va; + } + else + if(p.Tag("struct")) { + ValueMap vm; + while(p.Tag("member")) { + p.PassTag("name"); + String name = p.ReadText(); + p.PassEnd(); + vm.Add((Value)name, ParseXmlRpcValue(p)); + p.PassEnd(); + } + r = vm; + } + else + throw XmlError("unknown value type"); + p.PassEnd(); + p.PassEnd(); + return r; +} + +Value ParseXmlRpcParam(XmlParser& p) +{ + p.PassTag("param"); + Value v = ParseXmlRpcValue(p); + p.PassEnd(); + return v; +} + +ValueArray ParseXmlRpcParams(XmlParser& p) +{ + ValueArray va; + if(p.Tag("params")) + while(!p.End()) + va.Add(ParseXmlRpcParam(p)); + return va; +} + +String FormatXmlRpcValue(const Value& v) +{ + String r; + if(v.GetType() == INT_V) + r = XmlTag("int")(Format("%d", (int)v)); + else + if(v.GetType() == BOOL_V) + r = XmlTag("boolean")(AsString(v)); + else + if(IsNumber(v)) + r = XmlTag("double")(Format("%d", (double)v)); + else + if(IsDateTime(v)) { + Time t = v; + r = XmlTag("dateTime.iso8601") + (Format("%04.4d%02.2d%02.2d`T%02.2d`:%02.2d`:%02.2d", + t.year, t.month, t.day, t.hour, t.minute, t.second)); + } + else + if(v.GetType() == VALUEMAP_V) { + r = ""; + ValueMap vm = v; + const Index& k = vm.GetKeys(); + ValueArray va = vm.GetValues(); + for(int i = 0; i < k.GetCount(); i++) + r << XmlTag("member")(XmlTag("name")(k[i]) + FormatXmlRpcValue(va[i])); + r << ""; + } + else + if( v.GetType() == VALUEARRAY_V) { + r = ""; + ValueArray va = v; + for(int i = 0; i < va.GetCount(); i++) + r << FormatXmlRpcValue(va[i]); + r << ""; + } + else + r = XmlTag("string")(AsString(v)); + return XmlTag("value")(r); +} + +String FormatXmlRpcParam(const Value& param) +{ + return XmlTag("param")(FormatXmlRpcValue(param)); +} + +String FormatXmlRpcParams(const ValueArray& params) +{ + String r; + r = ""; + for(int i = 0; i < params.GetCount(); i++) + r << FormatXmlRpcParam(params[i]); + r << ""; + return r; +} + +struct XmlRpcErrorStruct { + int code; + String text; + + void Map(ValueMapper& m) { + m("faultCode", code)("faultString", text); + } +}; + +String FormatXmlRpcError(int code, const char *text) +{ + XmlRpcErrorStruct e; + e.code = code; + e.text = text; + XmlRpcData d; + d << e; + String r; + r << XmlHeader() + << "\r\n" + << FormatXmlRpcValue(d.out[0]) + << "\r\n"; + return r; +} diff --git a/uppsrc/XmlRpc/XmlRpc.h b/uppsrc/XmlRpc/XmlRpc.h new file mode 100644 index 000000000..cbb45f4d9 --- /dev/null +++ b/uppsrc/XmlRpc/XmlRpc.h @@ -0,0 +1,169 @@ +#ifndef _XmlRpc_XmlRpc_h +#define _XmlRpc_XmlRpc_h + +#include + +using namespace Upp; + +struct ValueTypeMismatch {}; + +void ValueCheck(bool b); + +void ValueGet(int& n, const Value& v); +void ValueGet(bool& b, const Value& v); +void ValueGet(String& s, const Value& v); +void ValueGet(double& x, const Value& v); +void ValueGet(Date& x, const Value& v); +void ValueGet(Time& x, const Value& v); +void ValueGet(Value& t, const Value& v); +void ValueGet(ValueArray& va, const Value& v); +void ValueGet(ValueMap& vm, const Value& v); + +template +void ValueGet(Array& x, Value v) { + ValueCheck(IsValueArray(v)); + ValueArray va = v; + x.SetCount(va.GetCount()); + for(int i = 0; i < va.GetCount(); i++) + ValueGet(x[i], va[i]); +} + +template +void ValueGet(Vector& x, Value v) { + ValueCheck(IsValueArray(v)); + ValueArray va = v; + x.SetCount(va.GetCount()); + for(int i = 0; i < va.GetCount(); i++) + ValueGet(x[i], va[i]); +} + +void ValuePut(Value& v, int n); +void ValuePut(Value& v, const String& s); +void ValuePut(Value& v, const char *s); +void ValuePut(Value& v, double x); +void ValuePut(Value& v, bool x); +void ValuePut(Value& v, const Date& x); +void ValuePut(Value& v, const Time& x); +void ValuePut(Value& v, const Value& t); +void ValuePut(Value& v, const ValueArray& va); +void ValuePut(Value& v, const ValueMap& vm); + +struct ValueMapper { + ValueMap map; + bool get; + + template + ValueMapper& operator()(const char *name, T& x) { + if(get) + ValueGet(x, map[name]); + else { + Value v; + ValuePut(v, x); + map.Add(name, v); + } + return *this; + } +}; + +template +void ValueGet(T& x, const Value& v) { + ValueMapper m; + m.map = v; + m.get = true; + x.Map(m); +} + +template +void ValuePut(Value& v, const T& x) { + ValueMapper m; + m.get = false; + const_cast(x).Map(m); + v = m.map; +} + +Value ParseXmlRpcValue(XmlParser& p); +Value ParseXmlRpcParam(XmlParser& p); +ValueArray ParseXmlRpcParams(XmlParser& p); + +struct XmlRpcData { + ValueArray in; + int ii; + ValueArray out; + + Value Get() { if(ii >= in.GetCount()) throw ValueTypeMismatch(); return in[ii++]; } + + template + XmlRpcData& operator>>(T& x) { ValueGet(x, Get()); return *this; } + + template + XmlRpcData& operator<<(const T& x) { Value v; ValuePut(v, x); out.Add(v); return *this; } + + void Reset() { in.Clear(); out.Clear(); ii = 0; } + + XmlRpcData() { ii = 0; } +}; + +String FormatXmlRpcValue(const Value& value); +String FormatXmlRpcParam(const Value& param); +String FormatXmlRpcParams(const ValueArray& params); + +String FormatXmlRpcError(int code, const char *text); + +void Register(const char *name, void (*method)(XmlRpcData&), const char *group = NULL); +bool XmlRpcServer(int port = 80, const char *group = NULL); + +#define XMLRPC_METHOD(x) \ +void xmlrpc##x(XmlRpcData& rpc); \ +INITBLOCK { Register(#x, xmlrpc##x); } \ +void xmlrpc##x(XmlRpcData& rpc) + +#define XMLRPC_GMETHOD(x, group) \ +void xmlrpc##x(XmlRpcData& rpc); \ +INITBLOCK { Register(#x, xmlrpc##x, group); } \ +void xmlrpc##x(XmlRpcData& rpc) + +class XmlRpcCall { + HttpClient server; + XmlRpcData data; + String method; + String error; + int timeout; + +public: + XmlRpcCall& Method(const char *name) { method = name; data.Reset(); error.Clear(); return *this; } + + template + XmlRpcCall& operator<<(const T& x) { data << x; return *this; } + + Value Execute(); + + template + bool operator>>(T& x) { if(IsError(Execute())) return false; try { data >> x; } catch(ValueTypeMismatch) { return false; } return true; } + + XmlRpcCall& operator()(const char *method) { Method(method); return *this; } + +#define E__Templ(I) class COMBINE(T, I) +#define E__Decl(I) const COMBINE(T, I)& COMBINE(p, I) +#define E__Param(I) *this << COMBINE(p, I) +#define E__Body(I) \ + template <__List##I(E__Templ)> \ + XmlRpcCall& operator()(const char *method, __List##I(E__Decl)) { \ + Method(method); \ + __List##I(E__Param); \ + return *this; \ + } + + __Expand20(E__Body) + +#undef E__Templ +#undef E__Decl +#undef E__Param +#undef E__Body + + String GetError() const { return error; } + XmlRpcCall& TimeOut(int msec) { timeout = msec; } + + XmlRpcCall(const char *url) : server(url) { server.ContentType("text/xml"); timeout = 30000; } +}; + +#endif diff --git a/uppsrc/XmlRpc/XmlRpc.upp b/uppsrc/XmlRpc/XmlRpc.upp new file mode 100644 index 000000000..bb5a96456 --- /dev/null +++ b/uppsrc/XmlRpc/XmlRpc.upp @@ -0,0 +1,14 @@ +uses + Web; + +file + XmlRpc.h, + Value.cpp, + Xml.cpp, + Client.cpp, + Server.cpp, + xrs readonly separator, + xrs_header.h, + xrs_source.h, + xrs.h; + diff --git a/uppsrc/XmlRpc/init b/uppsrc/XmlRpc/init new file mode 100644 index 000000000..cc1468a4a --- /dev/null +++ b/uppsrc/XmlRpc/init @@ -0,0 +1,4 @@ +#ifndef _XmlRpc_icpp_init_stub +#define _XmlRpc_icpp_init_stub +#include "Web/init" +#endif diff --git a/uppsrc/XmlRpc/xrs.h b/uppsrc/XmlRpc/xrs.h new file mode 100644 index 000000000..8401479ac --- /dev/null +++ b/uppsrc/XmlRpc/xrs.h @@ -0,0 +1,9 @@ +#ifndef _XmlRpc_xrs_h_ +#define _XmlRpc_xrs_h_ + +#define XRS_KEEP +#include +#undef XRS_KEEP +#include + +#endif diff --git a/uppsrc/XmlRpc/xrs_header.h b/uppsrc/XmlRpc/xrs_header.h new file mode 100644 index 000000000..e71c29699 --- /dev/null +++ b/uppsrc/XmlRpc/xrs_header.h @@ -0,0 +1,27 @@ +#include "XmlRpc.h" + +#define STRUCT(id) struct id { +#define VAR(type, id) type id; +#define INT(id) int id; +#define BOOL(id) bool id; +#define STRING(id) String id; +#define DOUBLE(id) double id; +#define DATE(id) Date id; +#define TIME(id) Time id; +#define END_STRUCT void Map(ValueMapper& m); }; + +#include XRSFILE + +#undef STRUCT +#undef VAR +#undef INT +#undef BOOL +#undef STRING +#undef DOUBLE +#undef DATE +#undef TIME +#undef END_STRUCT + +#ifndef XRS_KEEP +#undef XRSFILE +#endif diff --git a/uppsrc/XmlRpc/xrs_source.h b/uppsrc/XmlRpc/xrs_source.h new file mode 100644 index 000000000..483af8553 --- /dev/null +++ b/uppsrc/XmlRpc/xrs_source.h @@ -0,0 +1,27 @@ +#include "XmlRpc.h" + +#define STRUCT(id) void id::Map(ValueMapper& m) { +#define VAR(type, id) m(#id, id); +#define INT(id) m(#id, id); +#define BOOL(id) m(#id, id); +#define STRING(id) m(#id, id); +#define DOUBLE(id) m(#id, id); +#define DATE(id) m(#id, id); +#define TIME(id) m(#id, id); +#define END_STRUCT }; + +#include XRSFILE + +#undef STRUCT +#undef VAR +#undef INT +#undef BOOL +#undef STRING +#undef DOUBLE +#undef DATE +#undef TIME +#undef END_STRUCT + +#ifndef XRS_KEEP +#undef XRSFILE +#endif