6 * Portable Windows Library
8 * Copyright (c) 2002 Equivalence Pty. Ltd.
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
27 * Revision 1.24 2003/04/15 03:00:41 robertj
28 * Added array support to XML/RPC
29 * Fixed XML/RPC parsing when lots of white space in raw XML, caused by
30 * big fix to base XML parser not returning internal data elements.
32 * Revision 1.23 2003/02/21 05:07:27 robertj
33 * Fixed GetParam() for an int type so can accept i4/int/boolean type names.
35 * Revision 1.22 2003/01/28 07:42:17 robertj
36 * Improved trace output of errors.
38 * Revision 1.21 2002/12/16 06:53:19 robertj
39 * Added ability to specify certain elemets (by name) that are exempt from
40 * the indent formatting. Useful for XML/RPC where leading white space is
41 * not ignored by all servers.
42 * Allowed for some servers that only send "string" type for "int" etc
43 * Fixed problem with autodetecting reply that is a single struct.
45 * Revision 1.20 2002/12/13 01:12:24 robertj
46 * Added copy constructor and assignment operator to XML/RPC structs
48 * Revision 1.19 2002/12/10 03:51:17 robertj
49 * Fixed member variable display in structure
51 * Revision 1.18 2002/12/09 04:06:44 robertj
52 * Added macros for defining multi-argument functions
54 * Revision 1.17 2002/12/04 00:31:13 robertj
55 * Fixed GNU compatibility
57 * Revision 1.16 2002/12/04 00:15:44 robertj
58 * Changed usage of PHTTPClient so supports chunked transfer encoding.
59 * Large enhancement to create automatically encoding and decoding structures
60 * using macros to build a class.
62 * Revision 1.15 2002/11/06 22:47:25 robertj
63 * Fixed header comment (copyright etc)
65 * Revision 1.14 2002/10/08 12:26:31 craigs
66 * Changed struct members to always contain name/value in that order
68 * Revision 1.13 2002/10/08 12:09:28 craigs
69 * More fixes for creation of struct params
71 * Revision 1.12 2002/10/08 11:58:01 craigs
72 * Fixed creation of struct params
74 * Revision 1.11 2002/10/08 11:48:37 craigs
75 * Added logging of incoming and outgoing XML at highest log level
77 * Revision 1.10 2002/10/08 11:36:56 craigs
80 * Revision 1.9 2002/10/08 08:22:18 craigs
81 * Fixed problem with parsing struct parameters
83 * Revision 1.8 2002/10/02 08:54:01 craigs
84 * Added support for XMLRPC server
86 * Revision 1.7 2002/08/13 03:02:07 robertj
87 * Removed previous fix for memory leak, as object was already deleted.
89 * Revision 1.6 2002/08/13 01:54:47 craigs
90 * Fixed memory leak on PXMLRPCRequest class
92 * Revision 1.5 2002/08/06 01:04:03 robertj
93 * Fixed missing pragma interface/implementation
95 * Revision 1.4 2002/08/02 05:42:10 robertj
96 * Fixed confusion between in and out MIME.
97 * Improved trace logging and error reporting.
99 * Revision 1.3 2002/07/12 05:51:35 craigs
100 * Added structs to XMLRPC response types
102 * Revision 1.2 2002/03/27 00:50:29 craigs
103 * Fixed problems with parsing faults and creating structs
105 * Revision 1.1 2002/03/26 07:06:29 craigs
110 // This depends on the expat XML library by Jim Clark
111 // See http://www.jclark.com/xml/expat.html for more information
116 #pragma implementation "pxmlrpc.h"
119 #include <ptclib/pxmlrpc.h>
124 #include <ptclib/mime.h>
125 #include <ptclib/http.h>
128 static const char NoIndentElements
[] = "methodName name string int boolean double dateTime.iso8601";
131 /////////////////////////////////////////////////////////////////
133 PXMLRPCBlock::PXMLRPCBlock()
134 : PXML(-1, NoIndentElements
)
136 faultCode
= P_MAX_INDEX
;
137 SetRootElement("methodResponse");
141 PXMLRPCBlock::PXMLRPCBlock(const PString
& method
)
142 : PXML(-1, NoIndentElements
)
144 faultCode
= P_MAX_INDEX
;
145 SetRootElement("methodCall");
146 rootElement
->AddChild(new PXMLElement(rootElement
, "methodName", method
));
150 PXMLRPCBlock::PXMLRPCBlock(const PString
& method
, const PXMLRPCStructBase
& data
)
151 : PXML(-1, NoIndentElements
)
153 faultCode
= P_MAX_INDEX
;
154 SetRootElement("methodCall");
155 rootElement
->AddChild(new PXMLElement(rootElement
, "methodName", method
));
158 for (PINDEX i
= 0; i
< data
.GetNumVariables(); i
++) {
159 PXMLRPCVariableBase
& variable
= data
.GetVariable(i
);
160 if (variable
.IsArray())
161 AddParam(CreateArray(variable
));
163 PXMLRPCStructBase
* structVar
= variable
.GetStruct(0);
164 if (structVar
!= NULL
)
165 AddParam(*structVar
);
167 AddParam(CreateValueElement(new PXMLElement(NULL
, variable
.GetType(), variable
.ToString(0))));
173 BOOL
PXMLRPCBlock::Load(const PString
& str
)
175 if (!PXML::Load(str
))
178 if (rootElement
!= NULL
)
179 params
= rootElement
->GetElement("params");
185 PXMLElement
* PXMLRPCBlock::GetParams()
188 params
= rootElement
->AddChild(new PXMLElement(rootElement
, "params"));
193 PXMLElement
* PXMLRPCBlock::CreateValueElement(PXMLElement
* element
)
195 PXMLElement
* value
= new PXMLElement(NULL
, "value");
196 value
->AddChild(element
);
197 element
->SetParent(value
);
202 PXMLElement
* PXMLRPCBlock::CreateScalar(const PString
& type
,
203 const PString
& scalar
)
205 return CreateValueElement(new PXMLElement(NULL
, type
, scalar
));
208 PXMLElement
* PXMLRPCBlock::CreateScalar(const PString
& str
)
210 return CreateScalar("string", str
);
213 PXMLElement
* PXMLRPCBlock::CreateScalar(int value
)
215 return CreateScalar("int", PString(PString::Unsigned
, value
));
218 PXMLElement
* PXMLRPCBlock::CreateScalar(double value
)
220 return CreateScalar("double", psprintf("%lf", value
));
223 PXMLElement
* PXMLRPCBlock::CreateDateAndTime(const PTime
& time
)
225 return CreateScalar("dateTime.iso8601", PXMLRPC::PTimeToISO8601(time
));
228 PXMLElement
* PXMLRPCBlock::CreateBinary(const PBYTEArray
& data
)
230 return CreateScalar("base64", PBase64::Encode(data
));
233 PXMLElement
* PXMLRPCBlock::CreateStruct()
235 PAssertAlways("Not used");
240 PXMLElement
* PXMLRPCBlock::CreateStruct(const PStringToString
& dict
)
242 return CreateStruct(dict
, "string");
245 PXMLElement
* PXMLRPCBlock::CreateStruct(const PStringToString
& dict
, const PString
& typeStr
)
247 PXMLElement
* structElement
= new PXMLElement(NULL
, "struct");
248 PXMLElement
* valueElement
= CreateValueElement(structElement
);
251 for (i
= 0; i
< dict
.GetSize(); i
++) {
252 PString key
= dict
.GetKeyAt(i
);
253 structElement
->AddChild(CreateMember(key
, CreateScalar(typeStr
, dict
[key
])));
260 PXMLElement
* PXMLRPCBlock::CreateStruct(const PXMLRPCStructBase
& data
)
262 PXMLElement
* structElement
= new PXMLElement(NULL
, "struct");
263 PXMLElement
* valueElement
= PXMLRPCBlock::CreateValueElement(structElement
);
266 for (i
= 0; i
< data
.GetNumVariables(); i
++) {
267 PXMLElement
* element
;
268 PXMLRPCVariableBase
& variable
= data
.GetVariable(i
);
270 if (variable
.IsArray())
271 element
= CreateArray(variable
);
273 PXMLRPCStructBase
* nested
= variable
.GetStruct(0);
275 element
= CreateStruct(*nested
);
277 element
= CreateScalar(variable
.GetType(), variable
.ToString(0));
280 structElement
->AddChild(CreateMember(variable
.GetName(), element
));
287 PXMLElement
* PXMLRPCBlock::CreateMember(const PString
& name
, PXMLElement
* value
)
289 PXMLElement
* member
= new PXMLElement(NULL
, "member");
290 member
->AddChild(new PXMLElement(member
, "name", name
));
291 member
->AddChild(value
);
297 PXMLElement
* PXMLRPCBlock::CreateArray(const PStringArray
& array
)
299 return CreateArray(array
, "string");
303 PXMLElement
* PXMLRPCBlock::CreateArray(const PStringArray
& array
, const PString
& typeStr
)
305 PXMLElement
* arrayElement
= new PXMLElement(NULL
, "array");
307 PXMLElement
* dataElement
= new PXMLElement(arrayElement
, "data");
308 arrayElement
->AddChild(dataElement
);
311 for (i
= 0; i
< array
.GetSize(); i
++)
312 dataElement
->AddChild(CreateScalar(typeStr
, array
[i
]));
314 return CreateValueElement(arrayElement
);
318 PXMLElement
* PXMLRPCBlock::CreateArray(const PStringArray
& array
, const PStringArray
& types
)
320 PXMLElement
* arrayElement
= new PXMLElement(NULL
, "array");
322 PXMLElement
* dataElement
= new PXMLElement(arrayElement
, "data");
323 arrayElement
->AddChild(dataElement
);
326 for (i
= 0; i
< array
.GetSize(); i
++)
327 dataElement
->AddChild(CreateScalar(types
[i
], array
[i
]));
329 return CreateValueElement(arrayElement
);
333 PXMLElement
* PXMLRPCBlock::CreateArray(const PArray
<PStringToString
> & array
)
335 PXMLElement
* arrayElement
= new PXMLElement(NULL
, "array");
337 PXMLElement
* dataElement
= new PXMLElement(arrayElement
, "data");
338 arrayElement
->AddChild(dataElement
);
341 for (i
= 0; i
< array
.GetSize(); i
++)
342 dataElement
->AddChild(CreateStruct(array
[i
]));
344 return CreateValueElement(arrayElement
);
348 PXMLElement
* PXMLRPCBlock::CreateArray(const PXMLRPCVariableBase
& array
)
350 PXMLElement
* arrayElement
= new PXMLElement(NULL
, "array");
352 PXMLElement
* dataElement
= new PXMLElement(arrayElement
, "data");
353 arrayElement
->AddChild(dataElement
);
356 for (i
= 0; i
< array
.GetSize(); i
++) {
357 PXMLElement
* element
;
358 PXMLRPCStructBase
* structure
= array
.GetStruct(i
);
359 if (structure
!= NULL
)
360 element
= CreateStruct(*structure
);
362 element
= CreateScalar(array
.GetType(), array
.ToString(i
));
363 dataElement
->AddChild(element
);
366 return CreateValueElement(arrayElement
);
370 /////////////////////////////////////////////
372 void PXMLRPCBlock::AddParam(PXMLElement
* parm
)
375 PXMLElement
* child
= params
->AddChild(new PXMLElement(params
, "param"));
376 child
->AddChild(parm
);
377 parm
->SetParent(child
);
380 void PXMLRPCBlock::AddParam(const PString
& str
)
382 AddParam(CreateScalar(str
));
385 void PXMLRPCBlock::AddParam(int value
)
387 AddParam(CreateScalar(value
));
390 void PXMLRPCBlock::AddParam(double value
)
392 AddParam(CreateScalar(value
));
395 void PXMLRPCBlock::AddParam(const PTime
& time
)
397 AddParam(CreateDateAndTime(time
));
400 void PXMLRPCBlock::AddBinary(const PBYTEArray
& data
)
402 AddParam(CreateBinary(data
));
405 void PXMLRPCBlock::AddParam(const PXMLRPCStructBase
& data
)
407 AddParam(CreateStruct(data
));
410 void PXMLRPCBlock::AddStruct(const PStringToString
& dict
)
412 AddParam(CreateStruct(dict
, "string"));
415 void PXMLRPCBlock::AddStruct(const PStringToString
& dict
, const PString
& typeStr
)
417 AddParam(CreateStruct(dict
, typeStr
));
420 void PXMLRPCBlock::AddArray(const PStringArray
& array
)
422 AddParam(CreateArray(array
, "string"));
425 void PXMLRPCBlock::AddArray(const PStringArray
& array
, const PString
& typeStr
)
427 AddParam(CreateArray(array
, typeStr
));
430 void PXMLRPCBlock::AddArray(const PStringArray
& array
, const PStringArray
& types
)
432 AddParam(CreateArray(array
, types
));
435 void PXMLRPCBlock::AddArray(const PArray
<PStringToString
> & array
)
437 AddParam(CreateArray(array
));
441 /////////////////////////////////////////////
443 BOOL
PXMLRPCBlock::ValidateResponse()
445 // ensure root element exists and has correct name
446 if ((rootElement
== NULL
) ||
447 (rootElement
->GetName() != "methodResponse")) {
448 SetFault(PXMLRPC::ResponseRootNotMethodResponse
, "Response root not methodResponse");
449 PTRACE(2, "XMLRPC\t" << GetFaultText());
453 // determine if response returned
455 params
= rootElement
->GetElement("params");
459 // determine if fault
460 if (params
->GetName() == "fault") {
462 // assume fault is a simple struct
463 PStringToString faultInfo
;
464 PXMLElement
* value
= params
->GetElement("value");
467 txt
<< "Fault does not contain value\n" << *this;
468 SetFault(PXMLRPC::FaultyFault
, txt
);
469 } else if (!ParseStruct(value
->GetElement("struct"), faultInfo
) ||
470 (faultInfo
.GetSize() != 2) ||
471 (!faultInfo
.Contains("faultCode")) ||
472 (!faultInfo
.Contains("faultString"))
475 txt
<< "Fault return is faulty:\n" << *this;
476 SetFault(PXMLRPC::FaultyFault
, txt
);
477 PTRACE(2, "XMLRPC\t" << GetFaultText());
481 // get fault code and string
482 SetFault(faultInfo
["faultCode"].AsInteger(), faultInfo
["faultString"]);
488 else if (params
->GetName() != "params") {
489 SetFault(PXMLRPC::ResponseUnknownFormat
, PString("Response contains unknown element") & params
->GetName());
490 PTRACE(2, "XMLRPC\t" << GetFaultText());
497 BOOL
PXMLRPCBlock::ParseScalar(PXMLElement
* valueElement
,
501 if (valueElement
== NULL
)
504 if (!valueElement
->IsElement())
507 if (valueElement
->GetName() != "value") {
508 SetFault(PXMLRPC::ParamNotValue
, "Scalar value does not contain value element");
509 PTRACE(2, "RPCXML\t" << GetFaultText());
513 for (PINDEX i
= 0; i
< valueElement
->GetSize(); i
++) {
514 PXMLElement
* element
= (PXMLElement
*)valueElement
->GetElement(i
);
515 if (element
!= NULL
&& element
->IsElement()) {
516 type
= element
->GetName();
517 value
= element
->GetData();
522 SetFault(PXMLRPC::ScalarWithoutElement
, "Scalar without sub-element");
523 PTRACE(2, "XMLRPC\t" << GetFaultText());
528 static BOOL
ParseStructBase(PXMLRPCBlock
& block
, PXMLElement
* & element
)
533 if (!element
->IsElement())
536 if (element
->GetName() == "struct")
539 if (element
->GetName() != "value")
540 block
.SetFault(PXMLRPC::ParamNotStruct
, "Param is not struct");
542 element
= element
->GetElement("struct");
546 block
.SetFault(PXMLRPC::ParamNotStruct
, "nested structure not present");
549 PTRACE(2, "XMLRPC\t" << block
.GetFaultText());
554 static PXMLElement
* ParseStructElement(PXMLRPCBlock
& block
,
555 PXMLElement
* structElement
,
559 if (structElement
== NULL
)
562 PXMLElement
* member
= (PXMLElement
*)structElement
->GetElement(idx
);
566 if (!member
->IsElement())
569 if (member
->GetName() != "member") {
571 txt
<< "Member " << idx
<< " missing";
572 block
.SetFault(PXMLRPC::MemberIncomplete
, txt
);
573 PTRACE(2, "XMLRPC\t" << block
.GetFaultText());
577 PXMLElement
* nameElement
= member
->GetElement("name");
578 PXMLElement
* valueElement
= member
->GetElement("value");
579 if ((nameElement
== NULL
) || (valueElement
== NULL
)) {
581 txt
<< "Member " << idx
<< " incomplete";
582 block
.SetFault(PXMLRPC::MemberIncomplete
, txt
);
583 PTRACE(2, "XMLRPC\t" << block
.GetFaultText());
587 if (nameElement
->GetName() != "name") {
589 txt
<< "Member " << idx
<< " unnamed";
590 block
.SetFault(PXMLRPC::MemberUnnamed
, txt
);
591 PTRACE(2, "XMLRPC\t" << block
.GetFaultText());
595 name
= nameElement
->GetData();
600 BOOL
PXMLRPCBlock::ParseStruct(PXMLElement
* structElement
,
601 PStringToString
& structDict
)
603 if (!ParseStructBase(*this, structElement
))
606 for (PINDEX i
= 0; i
< structElement
->GetSize(); i
++) {
608 PXMLElement
* element
= ParseStructElement(*this, structElement
, i
, name
);
609 if (element
!= NULL
) {
612 if (ParseScalar(element
, type
, value
))
613 structDict
.SetAt(name
, value
);
621 BOOL
PXMLRPCBlock::ParseStruct(PXMLElement
* structElement
, PXMLRPCStructBase
& data
)
623 if (!ParseStructBase(*this, structElement
))
626 for (PINDEX i
= 0; i
< structElement
->GetSize(); i
++) {
628 PXMLElement
* element
= ParseStructElement(*this, structElement
, i
, name
);
629 if (element
!= NULL
) {
630 PXMLRPCVariableBase
* variable
= data
.GetVariable(name
);
631 if (variable
!= NULL
) {
632 if (variable
->IsArray()) {
633 if (!ParseArray(element
, *variable
))
637 PXMLRPCStructBase
* nested
= variable
->GetStruct(0);
638 if (nested
!= NULL
) {
639 if (!ParseStruct(element
, *nested
))
644 PCaselessString type
;
645 if (!ParseScalar(element
, type
, value
))
648 if (type
!= "string" && type
!= variable
->GetType()) {
649 PTRACE(2, "RPCXML\tMember " << i
<< " is not of expected type: " << variable
->GetType());
653 variable
->FromString(0, value
);
664 static PXMLElement
* ParseArrayBase(PXMLRPCBlock
& block
, PXMLElement
* element
)
669 if (!element
->IsElement())
672 if (element
->GetName() == "value")
673 element
= element
->GetElement("array");
676 block
.SetFault(PXMLRPC::ParamNotArray
, "array not present");
678 if (element
->GetName() != "array")
679 block
.SetFault(PXMLRPC::ParamNotArray
, "Param is not array");
681 element
= element
->GetElement("data");
684 block
.SetFault(PXMLRPC::ParamNotArray
, "Array param has no data");
688 PTRACE(2, "XMLRPC\t" << block
.GetFaultText());
693 BOOL
PXMLRPCBlock::ParseArray(PXMLElement
* arrayElement
, PStringArray
& array
)
695 PXMLElement
* dataElement
= ParseArrayBase(*this, arrayElement
);
696 if (dataElement
== NULL
)
699 array
.SetSize(dataElement
->GetSize());
702 for (PINDEX i
= 0; i
< dataElement
->GetSize(); i
++) {
705 if (ParseScalar((PXMLElement
*)dataElement
->GetElement(i
), type
, value
))
706 array
[count
++] = value
;
709 array
.SetSize(count
);
714 BOOL
PXMLRPCBlock::ParseArray(PXMLElement
* arrayElement
, PArray
<PStringToString
> & array
)
716 PXMLElement
* dataElement
= ParseArrayBase(*this, arrayElement
);
717 if (dataElement
== NULL
)
720 array
.SetSize(dataElement
->GetSize());
723 for (PINDEX i
= 0; i
< dataElement
->GetSize(); i
++) {
724 PStringToString values
;
725 if (!ParseStruct((PXMLElement
*)dataElement
->GetElement(i
), values
))
728 array
[count
++] = values
;
731 array
.SetSize(count
);
736 BOOL
PXMLRPCBlock::ParseArray(PXMLElement
* arrayElement
, PXMLRPCVariableBase
& array
)
738 PXMLElement
* dataElement
= ParseArrayBase(*this, arrayElement
);
739 if (dataElement
== NULL
)
742 array
.SetSize(dataElement
->GetSize());
745 for (PINDEX i
= 0; i
< dataElement
->GetSize(); i
++) {
746 PXMLElement
* element
= (PXMLElement
*)dataElement
->GetElement(i
);
748 PXMLRPCStructBase
* structure
= array
.GetStruct(i
);
749 if (structure
!= NULL
) {
750 if (ParseStruct(element
, *structure
))
755 PCaselessString type
;
756 if (ParseScalar(element
, type
, value
)) {
757 if (type
!= "string" && type
!= array
.GetType())
758 PTRACE(2, "RPCXML\tArray entry " << i
<< " is not of expected type: " << array
.GetType());
760 array
.FromString(count
++, value
);
765 array
.SetSize(count
);
770 PINDEX
PXMLRPCBlock::GetParamCount() const
776 for (PINDEX i
= 0; i
< params
->GetSize(); i
++) {
777 PXMLElement
* element
= (PXMLElement
*)params
->GetElement(i
);
778 if (element
!= NULL
&& element
->IsElement() && element
->GetName() == "param")
785 PXMLElement
* PXMLRPCBlock::GetParam(PINDEX idx
) const
790 PXMLElement
* param
= NULL
;
792 for (i
= 0; i
< params
->GetSize(); i
++) {
793 PXMLElement
* element
= (PXMLElement
*)params
->GetElement(i
);
794 if (element
!= NULL
&& element
->IsElement() && element
->GetName() == "param") {
806 for (i
= 0; i
< param
->GetSize(); i
++) {
807 PXMLObject
* parm
= param
->GetElement(i
);
808 if (parm
!= NULL
&& parm
->IsElement())
809 return (PXMLElement
*)parm
;
816 BOOL
PXMLRPCBlock::GetParams(PXMLRPCStructBase
& data
)
821 // Special case to allow for server implementations that always return
822 // values as a struct rather than multiple parameters.
823 if (GetParamCount() == 1 &&
824 (data
.GetNumVariables() > 1 || data
.GetVariable(0).GetStruct(0) == NULL
)) {
826 if (ParseScalar(GetParam(0), type
, value
) && type
== "struct")
827 return GetParam(0, data
);
830 for (PINDEX i
= 0; i
< data
.GetNumVariables(); i
++) {
831 PXMLRPCVariableBase
& variable
= data
.GetVariable(i
);
832 if (variable
.IsArray()) {
833 if (!ParseArray(GetParam(i
), variable
))
837 PXMLRPCStructBase
* structure
= variable
.GetStruct(0);
838 if (structure
!= NULL
) {
839 if (!GetParam(i
, *structure
))
844 if (!GetExpectedParam(i
, variable
.GetType(), value
))
847 variable
.FromString(0, value
);
856 BOOL
PXMLRPCBlock::GetParam(PINDEX idx
, PString
& type
, PString
& value
)
859 if (!ParseScalar(GetParam(idx
), type
, value
)) {
860 PTRACE(3, "XMLRPC\tCannot get scalar parm " << idx
);
868 BOOL
PXMLRPCBlock::GetExpectedParam(PINDEX idx
, const PString
& expectedType
, PString
& value
)
873 if (!GetParam(idx
, type
, value
))
876 // see if correct type
877 if (!expectedType
.IsEmpty() && (type
!= expectedType
)) {
878 PTRACE(3, "XMLRPC\tExpected parm " << idx
<< " to be " << expectedType
<< ", was " << type
);
886 BOOL
PXMLRPCBlock::GetParam(PINDEX idx
, PString
& result
)
888 return GetExpectedParam(idx
, "string", result
);
891 BOOL
PXMLRPCBlock::GetParam(PINDEX idx
, int & val
)
893 PString type
, result
;
894 if (!GetParam(idx
, type
, result
))
897 if ((type
!= "i4") &&
899 (type
!= "boolean")) {
900 PTRACE(3, "XMLRPC\tExpected parm " << idx
<< " to be intger compatible, was " << type
);
904 val
= result
.AsInteger();
908 BOOL
PXMLRPCBlock::GetParam(PINDEX idx
, double & val
)
911 if (!GetExpectedParam(idx
, "double", result
))
914 val
= result
.AsReal();
921 BOOL
PXMLRPCBlock::GetParam(PINDEX idx
, PTime
& val
, int tz
)
924 if (!GetExpectedParam(idx
, "dateTime.iso8601", result
))
927 return PXMLRPC::ISO8601ToPTime(result
, val
, tz
);
930 BOOL
PXMLRPCBlock::GetParam(PINDEX idx
, PStringArray
& result
)
932 return ParseArray(GetParam(idx
), result
);
935 BOOL
PXMLRPCBlock::GetParam(PINDEX idx
, PArray
<PStringToString
> & result
)
937 return ParseArray(GetParam(idx
), result
);
941 BOOL
PXMLRPCBlock::GetParam(PINDEX idx
, PStringToString
& result
)
943 return ParseStruct(GetParam(idx
), result
);
947 BOOL
PXMLRPCBlock::GetParam(PINDEX idx
, PXMLRPCStructBase
& data
)
949 return ParseStruct(GetParam(idx
), data
);
953 ////////////////////////////////////////////////////////
955 PXMLRPC::PXMLRPC(const PURL
& _url
, unsigned opts
)
962 BOOL
PXMLRPC::MakeRequest(const PString
& method
)
964 PXMLRPCBlock
request(method
);
965 PXMLRPCBlock response
;
967 return MakeRequest(request
, response
);
970 BOOL
PXMLRPC::MakeRequest(const PString
& method
, PXMLRPCBlock
& response
)
972 PXMLRPCBlock
request(method
);
974 return MakeRequest(request
, response
);
977 BOOL
PXMLRPC::MakeRequest(PXMLRPCBlock
& request
, PXMLRPCBlock
& response
)
979 if (PerformRequest(request
, response
))
982 faultCode
= response
.GetFaultCode();
983 faultText
= response
.GetFaultText();
988 BOOL
PXMLRPC::MakeRequest(const PString
& method
, const PXMLRPCStructBase
& args
, PXMLRPCStructBase
& reply
)
990 PXMLRPCBlock
request(method
, args
);
991 PXMLRPCBlock response
;
993 if (!MakeRequest(request
, response
))
996 if (response
.GetParams(reply
))
999 PTRACE(2, "XMLRPC\tParsing response failed: " << response
.GetFaultText());
1004 BOOL
PXMLRPC::PerformRequest(PXMLRPCBlock
& request
, PXMLRPCBlock
& response
)
1006 // create XML version of request
1008 if (!request
.Save(requestXML
, options
)) {
1010 txt
<< "Error creating request XML ("
1011 << request
.GetErrorLine()
1013 << request
.GetErrorString();
1014 response
.SetFault(PXMLRPC::CannotCreateRequestXML
, txt
);
1015 PTRACE(2, "XMLRPC\t" << response
.GetFaultText());
1019 // make sure the request ends with a newline
1024 PMIMEInfo sendMIME
, replyMIME
;
1025 sendMIME
.SetAt("Server", url
.GetHostName());
1026 sendMIME
.SetAt(PHTTP::ContentTypeTag
, "text/xml");
1028 PTRACE(5, "XMLRPC\tOutgoing XML/RPC:\n" << url
<< '\n' << sendMIME
<< requestXML
);
1030 // apply the timeout
1031 client
.SetReadTimeout(timeout
);
1036 BOOL ok
= client
.PostData(url
, sendMIME
, requestXML
, replyMIME
, replyXML
);
1038 PTRACE(5, "XMLRPC\tIncoming XML/RPC:\n" << replyMIME
<< replyXML
);
1040 // make sure the request worked
1043 txt
<< "HTTP POST failed: "
1044 << client
.GetLastResponseCode() << ' '
1045 << client
.GetLastResponseInfo() << '\n'
1046 << replyMIME
<< '\n'
1048 response
.SetFault(PXMLRPC::HTTPPostFailed
, txt
);
1049 PTRACE(2, "XMLRPC\t" << response
.GetFaultText());
1053 // parse the response
1054 if (!response
.Load(replyXML
)) {
1056 txt
<< "Error parsing response XML ("
1057 << response
.GetErrorLine()
1059 << response
.GetErrorString() << '\n';
1061 PStringArray lines
= replyXML
.Lines();
1062 for (int offset
= -2; offset
<= 2; offset
++) {
1063 int line
= response
.GetErrorLine() + offset
;
1064 if (line
>= 0 && line
< lines
.GetSize())
1065 txt
<< lines
[(PINDEX
)line
] << '\n';
1068 response
.SetFault(PXMLRPC::CannotParseResponseXML
, txt
);
1069 PTRACE(2, "XMLRPC\t" << response
.GetFaultText());
1073 // validate the response
1074 if (!response
.ValidateResponse()) {
1075 PTRACE(2, "XMLRPC\tValidation of response failed: " << response
.GetFaultText());
1082 BOOL
PXMLRPC::ISO8601ToPTime(const PString
& iso8601
, PTime
& val
, int tz
)
1084 if ((iso8601
.GetLength() != 17) ||
1085 (iso8601
[8] != 'T') ||
1086 (iso8601
[11] != ':') ||
1087 (iso8601
[14] != ':'))
1090 val
= PTime(iso8601
.Mid(15,2).AsInteger(), // seconds
1091 iso8601
.Mid(12,2).AsInteger(), // minutes
1092 iso8601
.Mid( 9,2).AsInteger(), // hours
1093 iso8601
.Mid( 6,2).AsInteger(), // day
1094 iso8601
.Mid( 4,2).AsInteger(), // month
1095 iso8601
.Mid( 0,4).AsInteger(), // year
1102 PString
PXMLRPC::PTimeToISO8601(const PTime
& time
)
1104 return time
.AsString("yyyyMMddThh:mm:ss");
1109 /////////////////////////////////////////////////////////////////
1111 PXMLRPCVariableBase::PXMLRPCVariableBase(const char * n
, const char * t
)
1113 type(t
!= NULL
? t
: "string")
1115 PXMLRPCStructBase::GetInitialiser().AddVariable(this);
1119 PXMLRPCStructBase
* PXMLRPCVariableBase::GetStruct(PINDEX
) const
1125 BOOL
PXMLRPCVariableBase::IsArray() const
1131 PINDEX
PXMLRPCVariableBase::GetSize() const
1137 BOOL
PXMLRPCVariableBase::SetSize(PINDEX
)
1143 PString
PXMLRPCVariableBase::ToString(PINDEX
) const
1145 PStringStream stream
;
1151 void PXMLRPCVariableBase::FromString(PINDEX
, const PString
& str
)
1153 PStringStream
stream(str
);
1158 PString
PXMLRPCVariableBase::ToBase64(PAbstractArray
& data
) const
1160 return PBase64::Encode(data
.GetPointer(), data
.GetSize());
1164 void PXMLRPCVariableBase::FromBase64(const PString
& str
, PAbstractArray
& data
)
1167 decoder
.StartDecoding();
1168 decoder
.ProcessDecoding(str
);
1169 data
= decoder
.GetDecodedData();
1173 /////////////////////////////////////////////////////////////////
1175 PXMLRPCArrayBase::PXMLRPCArrayBase(PContainer
& a
, const char * n
, const char * t
)
1176 : PXMLRPCVariableBase(n
, t
),
1182 void PXMLRPCArrayBase::PrintOn(ostream
& strm
) const
1184 strm
<< setfill('\n') << array
<< setfill(' ');
1188 void PXMLRPCArrayBase::Copy(const PXMLRPCVariableBase
& other
)
1190 array
= ((PXMLRPCArrayBase
&)other
).array
;
1194 BOOL
PXMLRPCArrayBase::IsArray() const
1200 PINDEX
PXMLRPCArrayBase::GetSize() const
1202 return array
.GetSize();
1206 BOOL
PXMLRPCArrayBase::SetSize(PINDEX sz
)
1208 return array
.SetSize(sz
);
1212 /////////////////////////////////////////////////////////////////
1214 PXMLRPCArrayObjectsBase::PXMLRPCArrayObjectsBase(PArrayObjects
& a
, const char * n
, const char * t
)
1215 : PXMLRPCArrayBase(a
, n
, t
),
1221 BOOL
PXMLRPCArrayObjectsBase::SetSize(PINDEX sz
)
1223 if (!array
.SetSize(sz
))
1226 for (PINDEX i
= 0; i
< sz
; i
++) {
1227 if (array
.GetAt(i
) == NULL
) {
1228 PObject
* object
= CreateObject();
1231 array
.SetAt(i
, object
);
1239 PString
PXMLRPCArrayObjectsBase::ToString(PINDEX i
) const
1241 PStringStream stream
;
1242 stream
<< *array
.GetAt(i
);
1247 void PXMLRPCArrayObjectsBase::FromString(PINDEX i
, const PString
& str
)
1249 PObject
* object
= array
.GetAt(i
);
1250 if (object
== NULL
) {
1251 object
= CreateObject();
1252 array
.SetAt(i
, object
);
1255 PStringStream
stream(str
);
1260 /////////////////////////////////////////////////////////////////
1262 PMutex
PXMLRPCStructBase::initialiserMutex
;
1263 PXMLRPCStructBase
* PXMLRPCStructBase::initialiserInstance
= NULL
;
1266 PXMLRPCStructBase::PXMLRPCStructBase()
1268 variablesByOrder
.DisallowDeleteObjects();
1269 variablesByName
.DisallowDeleteObjects();
1271 initialiserMutex
.Wait();
1272 initialiserStack
= initialiserInstance
;
1273 initialiserInstance
= this;
1277 void PXMLRPCStructBase::EndConstructor()
1279 initialiserInstance
= initialiserStack
;
1280 initialiserMutex
.Signal();
1284 PXMLRPCStructBase
& PXMLRPCStructBase::operator=(const PXMLRPCStructBase
& other
)
1286 for (PINDEX i
= 0; i
< variablesByOrder
.GetSize(); i
++)
1287 variablesByOrder
[i
].Copy(other
.variablesByOrder
[i
]);
1293 void PXMLRPCStructBase::PrintOn(ostream
& strm
) const
1295 for (PINDEX i
= 0; i
< variablesByOrder
.GetSize(); i
++) {
1296 PXMLRPCVariableBase
& var
= variablesByOrder
[i
];
1297 strm
<< var
.GetName() << '=' << var
<< '\n';
1302 void PXMLRPCStructBase::AddVariable(PXMLRPCVariableBase
* var
)
1304 variablesByOrder
.Append(var
);
1305 variablesByName
.SetAt(var
->GetName(), var
);
1312 // End of file ///////////////////////////////////////////////////////////////