Fixed incorrect usage of result (now object rather than scalar), thanks Michal Zygmun...
[pwlib.git] / src / ptclib / pxmlrpc.cxx
blobb54f907fb6eb2feda38ff8f295232dc146bdfccc
1 /*
2 * pxmlrpc.cxx
4 * XML/RPC support
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
18 * under the License.
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
26 * $Log$
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
78 * Fixed fault parsing
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
106 * Initial version
110 // This depends on the expat XML library by Jim Clark
111 // See http://www.jclark.com/xml/expat.html for more information
113 #include <ptlib.h>
115 #ifdef __GNUC__
116 #pragma implementation "pxmlrpc.h"
117 #endif
119 #include <ptclib/pxmlrpc.h>
122 #if P_EXPAT
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");
138 params = NULL;
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));
147 params = NULL;
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));
156 params = NULL;
158 for (PINDEX i = 0; i < data.GetNumVariables(); i++) {
159 PXMLRPCVariableBase & variable = data.GetVariable(i);
160 if (variable.IsArray())
161 AddParam(CreateArray(variable));
162 else {
163 PXMLRPCStructBase * structVar = variable.GetStruct(0);
164 if (structVar != NULL)
165 AddParam(*structVar);
166 else
167 AddParam(CreateValueElement(new PXMLElement(NULL, variable.GetType(), variable.ToString(0))));
173 BOOL PXMLRPCBlock::Load(const PString & str)
175 if (!PXML::Load(str))
176 return FALSE;
178 if (rootElement != NULL)
179 params = rootElement->GetElement("params");
181 return TRUE;
185 PXMLElement * PXMLRPCBlock::GetParams()
187 if (params == NULL)
188 params = rootElement->AddChild(new PXMLElement(rootElement, "params"));
190 return params;
193 PXMLElement * PXMLRPCBlock::CreateValueElement(PXMLElement * element)
195 PXMLElement * value = new PXMLElement(NULL, "value");
196 value->AddChild(element);
197 element->SetParent(value);
199 return 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");
236 return NULL;
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);
250 PINDEX i;
251 for (i = 0; i < dict.GetSize(); i++) {
252 PString key = dict.GetKeyAt(i);
253 structElement->AddChild(CreateMember(key, CreateScalar(typeStr, dict[key])));
256 return valueElement;
260 PXMLElement * PXMLRPCBlock::CreateStruct(const PXMLRPCStructBase & data)
262 PXMLElement * structElement = new PXMLElement(NULL, "struct");
263 PXMLElement * valueElement = PXMLRPCBlock::CreateValueElement(structElement);
265 PINDEX i;
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);
272 else {
273 PXMLRPCStructBase * nested = variable.GetStruct(0);
274 if (nested != NULL)
275 element = CreateStruct(*nested);
276 else
277 element = CreateScalar(variable.GetType(), variable.ToString(0));
280 structElement->AddChild(CreateMember(variable.GetName(), element));
283 return valueElement;
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);
293 return member;
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);
310 PINDEX i;
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);
325 PINDEX i;
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);
340 PINDEX i;
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);
355 PINDEX i;
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);
361 else
362 element = CreateScalar(array.GetType(), array.ToString(i));
363 dataElement->AddChild(element);
366 return CreateValueElement(arrayElement);
370 /////////////////////////////////////////////
372 void PXMLRPCBlock::AddParam(PXMLElement * parm)
374 GetParams();
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());
450 return FALSE;
453 // determine if response returned
454 if (params == NULL)
455 params = rootElement->GetElement("params");
456 if (params == NULL)
457 return TRUE;
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");
465 if (value == NULL) {
466 PStringStream txt;
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"))
474 PStringStream txt;
475 txt << "Fault return is faulty:\n" << *this;
476 SetFault(PXMLRPC::FaultyFault, txt);
477 PTRACE(2, "XMLRPC\t" << GetFaultText());
478 return FALSE;
481 // get fault code and string
482 SetFault(faultInfo["faultCode"].AsInteger(), faultInfo["faultString"]);
484 return FALSE;
487 // must be params
488 else if (params->GetName() != "params") {
489 SetFault(PXMLRPC::ResponseUnknownFormat, PString("Response contains unknown element") & params->GetName());
490 PTRACE(2, "XMLRPC\t" << GetFaultText());
491 return FALSE;
494 return TRUE;
497 BOOL PXMLRPCBlock::ParseScalar(PXMLElement * valueElement,
498 PString & type,
499 PString & value)
501 if (valueElement == NULL)
502 return FALSE;
504 if (!valueElement->IsElement())
505 return FALSE;
507 if (valueElement->GetName() != "value") {
508 SetFault(PXMLRPC::ParamNotValue, "Scalar value does not contain value element");
509 PTRACE(2, "RPCXML\t" << GetFaultText());
510 return FALSE;
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();
518 return TRUE;
522 SetFault(PXMLRPC::ScalarWithoutElement, "Scalar without sub-element");
523 PTRACE(2, "XMLRPC\t" << GetFaultText());
524 return FALSE;
528 static BOOL ParseStructBase(PXMLRPCBlock & block, PXMLElement * & element)
530 if (element == NULL)
531 return FALSE;
533 if (!element->IsElement())
534 return FALSE;
536 if (element->GetName() == "struct")
537 return TRUE;
539 if (element->GetName() != "value")
540 block.SetFault(PXMLRPC::ParamNotStruct, "Param is not struct");
541 else {
542 element = element->GetElement("struct");
543 if (element != NULL)
544 return TRUE;
546 block.SetFault(PXMLRPC::ParamNotStruct, "nested structure not present");
549 PTRACE(2, "XMLRPC\t" << block.GetFaultText());
550 return FALSE;
554 static PXMLElement * ParseStructElement(PXMLRPCBlock & block,
555 PXMLElement * structElement,
556 PINDEX idx,
557 PString & name)
559 if (structElement == NULL)
560 return NULL;
562 PXMLElement * member = (PXMLElement *)structElement->GetElement(idx);
563 if (member == NULL)
564 return NULL;
566 if (!member->IsElement())
567 return NULL;
569 if (member->GetName() != "member") {
570 PStringStream txt;
571 txt << "Member " << idx << " missing";
572 block.SetFault(PXMLRPC::MemberIncomplete, txt);
573 PTRACE(2, "XMLRPC\t" << block.GetFaultText());
574 return NULL;
577 PXMLElement * nameElement = member->GetElement("name");
578 PXMLElement * valueElement = member->GetElement("value");
579 if ((nameElement == NULL) || (valueElement == NULL)) {
580 PStringStream txt;
581 txt << "Member " << idx << " incomplete";
582 block.SetFault(PXMLRPC::MemberIncomplete, txt);
583 PTRACE(2, "XMLRPC\t" << block.GetFaultText());
584 return NULL;
587 if (nameElement->GetName() != "name") {
588 PStringStream txt;
589 txt << "Member " << idx << " unnamed";
590 block.SetFault(PXMLRPC::MemberUnnamed, txt);
591 PTRACE(2, "XMLRPC\t" << block.GetFaultText());
592 return NULL;
595 name = nameElement->GetData();
596 return valueElement;
600 BOOL PXMLRPCBlock::ParseStruct(PXMLElement * structElement,
601 PStringToString & structDict)
603 if (!ParseStructBase(*this, structElement))
604 return FALSE;
606 for (PINDEX i = 0; i < structElement->GetSize(); i++) {
607 PString name;
608 PXMLElement * element = ParseStructElement(*this, structElement, i, name);
609 if (element != NULL) {
610 PString value;
611 PString type;
612 if (ParseScalar(element, type, value))
613 structDict.SetAt(name, value);
617 return TRUE;
621 BOOL PXMLRPCBlock::ParseStruct(PXMLElement * structElement, PXMLRPCStructBase & data)
623 if (!ParseStructBase(*this, structElement))
624 return FALSE;
626 for (PINDEX i = 0; i < structElement->GetSize(); i++) {
627 PString name;
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))
634 return FALSE;
636 else {
637 PXMLRPCStructBase * nested = variable->GetStruct(0);
638 if (nested != NULL) {
639 if (!ParseStruct(element, *nested))
640 return FALSE;
642 else {
643 PString value;
644 PCaselessString type;
645 if (!ParseScalar(element, type, value))
646 return FALSE;
648 if (type != "string" && type != variable->GetType()) {
649 PTRACE(2, "RPCXML\tMember " << i << " is not of expected type: " << variable->GetType());
650 return FALSE;
653 variable->FromString(0, value);
660 return TRUE;
664 static PXMLElement * ParseArrayBase(PXMLRPCBlock & block, PXMLElement * element)
666 if (element == NULL)
667 return NULL;
669 if (!element->IsElement())
670 return NULL;
672 if (element->GetName() == "value")
673 element = element->GetElement("array");
675 if (element == NULL)
676 block.SetFault(PXMLRPC::ParamNotArray, "array not present");
677 else {
678 if (element->GetName() != "array")
679 block.SetFault(PXMLRPC::ParamNotArray, "Param is not array");
680 else {
681 element = element->GetElement("data");
682 if (element != NULL)
683 return element;
684 block.SetFault(PXMLRPC::ParamNotArray, "Array param has no data");
688 PTRACE(2, "XMLRPC\t" << block.GetFaultText());
689 return NULL;
693 BOOL PXMLRPCBlock::ParseArray(PXMLElement * arrayElement, PStringArray & array)
695 PXMLElement * dataElement = ParseArrayBase(*this, arrayElement);
696 if (dataElement == NULL)
697 return FALSE;
699 array.SetSize(dataElement->GetSize());
701 PINDEX count = 0;
702 for (PINDEX i = 0; i < dataElement->GetSize(); i++) {
703 PString value;
704 PString type;
705 if (ParseScalar((PXMLElement *)dataElement->GetElement(i), type, value))
706 array[count++] = value;
709 array.SetSize(count);
710 return TRUE;
714 BOOL PXMLRPCBlock::ParseArray(PXMLElement * arrayElement, PArray<PStringToString> & array)
716 PXMLElement * dataElement = ParseArrayBase(*this, arrayElement);
717 if (dataElement == NULL)
718 return FALSE;
720 array.SetSize(dataElement->GetSize());
722 PINDEX count = 0;
723 for (PINDEX i = 0; i < dataElement->GetSize(); i++) {
724 PStringToString values;
725 if (!ParseStruct((PXMLElement *)dataElement->GetElement(i), values))
726 return FALSE;
728 array[count++] = values;
731 array.SetSize(count);
732 return TRUE;
736 BOOL PXMLRPCBlock::ParseArray(PXMLElement * arrayElement, PXMLRPCVariableBase & array)
738 PXMLElement * dataElement = ParseArrayBase(*this, arrayElement);
739 if (dataElement == NULL)
740 return FALSE;
742 array.SetSize(dataElement->GetSize());
744 PINDEX count = 0;
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))
751 count++;
753 else {
754 PString value;
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());
759 else
760 array.FromString(count++, value);
765 array.SetSize(count);
766 return TRUE;
770 PINDEX PXMLRPCBlock::GetParamCount() const
772 if (params == NULL)
773 return 0;
775 PINDEX count = 0;
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")
779 count++;
781 return count;
785 PXMLElement * PXMLRPCBlock::GetParam(PINDEX idx) const
787 if (params == NULL)
788 return NULL;
790 PXMLElement * param = NULL;
791 PINDEX i;
792 for (i = 0; i < params->GetSize(); i++) {
793 PXMLElement * element = (PXMLElement *)params->GetElement(i);
794 if (element != NULL && element->IsElement() && element->GetName() == "param") {
795 if (idx <= 0) {
796 param = element;
797 break;
799 idx--;
803 if (param == NULL)
804 return FALSE;
806 for (i = 0; i < param->GetSize(); i++) {
807 PXMLObject * parm = param->GetElement(i);
808 if (parm != NULL && parm->IsElement())
809 return (PXMLElement *)parm;
812 return NULL;
816 BOOL PXMLRPCBlock::GetParams(PXMLRPCStructBase & data)
818 if (params == NULL)
819 return FALSE;
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)) {
825 PString type, value;
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))
834 return FALSE;
836 else {
837 PXMLRPCStructBase * structure = variable.GetStruct(0);
838 if (structure != NULL) {
839 if (!GetParam(i, *structure))
840 return FALSE;
842 else {
843 PString value;
844 if (!GetExpectedParam(i, variable.GetType(), value))
845 return FALSE;
847 variable.FromString(0, value);
852 return TRUE;
856 BOOL PXMLRPCBlock::GetParam(PINDEX idx, PString & type, PString & value)
858 // get the parameter
859 if (!ParseScalar(GetParam(idx), type, value)) {
860 PTRACE(3, "XMLRPC\tCannot get scalar parm " << idx);
861 return FALSE;
864 return TRUE;
868 BOOL PXMLRPCBlock::GetExpectedParam(PINDEX idx, const PString & expectedType, PString & value)
870 PString type;
872 // get parameter
873 if (!GetParam(idx, type, value))
874 return FALSE;
876 // see if correct type
877 if (!expectedType.IsEmpty() && (type != expectedType)) {
878 PTRACE(3, "XMLRPC\tExpected parm " << idx << " to be " << expectedType << ", was " << type);
879 return FALSE;
882 return TRUE;
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))
895 return FALSE;
897 if ((type != "i4") &&
898 (type != "int") &&
899 (type != "boolean")) {
900 PTRACE(3, "XMLRPC\tExpected parm " << idx << " to be intger compatible, was " << type);
901 return FALSE;
904 val = result.AsInteger();
905 return TRUE;
908 BOOL PXMLRPCBlock::GetParam(PINDEX idx, double & val)
910 PString result;
911 if (!GetExpectedParam(idx, "double", result))
912 return FALSE;
914 val = result.AsReal();
915 return TRUE;
918 // 01234567890123456
919 // yyyyMMddThh:mm:ss
921 BOOL PXMLRPCBlock::GetParam(PINDEX idx, PTime & val, int tz)
923 PString result;
924 if (!GetExpectedParam(idx, "dateTime.iso8601", result))
925 return FALSE;
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)
956 : url(_url)
958 timeout = 10000;
959 options = 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))
980 return TRUE;
982 faultCode = response.GetFaultCode();
983 faultText = response.GetFaultText();
985 return FALSE;
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))
994 return FALSE;
996 if (response.GetParams(reply))
997 return TRUE;
999 PTRACE(2, "XMLRPC\tParsing response failed: " << response.GetFaultText());
1000 return FALSE;
1004 BOOL PXMLRPC::PerformRequest(PXMLRPCBlock & request, PXMLRPCBlock & response)
1006 // create XML version of request
1007 PString requestXML;
1008 if (!request.Save(requestXML, options)) {
1009 PStringStream txt;
1010 txt << "Error creating request XML ("
1011 << request.GetErrorLine()
1012 << ") :"
1013 << request.GetErrorString();
1014 response.SetFault(PXMLRPC::CannotCreateRequestXML, txt);
1015 PTRACE(2, "XMLRPC\t" << response.GetFaultText());
1016 return FALSE;
1019 // make sure the request ends with a newline
1020 requestXML += "\n";
1022 // do the request
1023 PHTTPClient client;
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);
1033 PString replyXML;
1035 // do the request
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
1041 if (!ok) {
1042 PStringStream txt;
1043 txt << "HTTP POST failed: "
1044 << client.GetLastResponseCode() << ' '
1045 << client.GetLastResponseInfo() << '\n'
1046 << replyMIME << '\n'
1047 << replyXML;
1048 response.SetFault(PXMLRPC::HTTPPostFailed, txt);
1049 PTRACE(2, "XMLRPC\t" << response.GetFaultText());
1050 return FALSE;
1053 // parse the response
1054 if (!response.Load(replyXML)) {
1055 PStringStream txt;
1056 txt << "Error parsing response XML ("
1057 << response.GetErrorLine()
1058 << ") :"
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());
1070 return FALSE;
1073 // validate the response
1074 if (!response.ValidateResponse()) {
1075 PTRACE(2, "XMLRPC\tValidation of response failed: " << response.GetFaultText());
1076 return FALSE;
1079 return TRUE;
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] != ':'))
1088 return FALSE;
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
1099 return TRUE;
1102 PString PXMLRPC::PTimeToISO8601(const PTime & time)
1104 return time.AsString("yyyyMMddThh:mm:ss");
1109 /////////////////////////////////////////////////////////////////
1111 PXMLRPCVariableBase::PXMLRPCVariableBase(const char * n, const char * t)
1112 : name(n),
1113 type(t != NULL ? t : "string")
1115 PXMLRPCStructBase::GetInitialiser().AddVariable(this);
1119 PXMLRPCStructBase * PXMLRPCVariableBase::GetStruct(PINDEX) const
1121 return NULL;
1125 BOOL PXMLRPCVariableBase::IsArray() const
1127 return FALSE;
1131 PINDEX PXMLRPCVariableBase::GetSize() const
1133 return 1;
1137 BOOL PXMLRPCVariableBase::SetSize(PINDEX)
1139 return TRUE;
1143 PString PXMLRPCVariableBase::ToString(PINDEX) const
1145 PStringStream stream;
1146 PrintOn(stream);
1147 return stream;
1151 void PXMLRPCVariableBase::FromString(PINDEX, const PString & str)
1153 PStringStream stream(str);
1154 ReadFrom(stream);
1158 PString PXMLRPCVariableBase::ToBase64(PAbstractArray & data) const
1160 return PBase64::Encode(data.GetPointer(), data.GetSize());
1164 void PXMLRPCVariableBase::FromBase64(const PString & str, PAbstractArray & data)
1166 PBase64 decoder;
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),
1177 array(a)
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
1196 return TRUE;
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),
1216 array(a)
1221 BOOL PXMLRPCArrayObjectsBase::SetSize(PINDEX sz)
1223 if (!array.SetSize(sz))
1224 return FALSE;
1226 for (PINDEX i = 0; i < sz; i++) {
1227 if (array.GetAt(i) == NULL) {
1228 PObject * object = CreateObject();
1229 if (object == NULL)
1230 return FALSE;
1231 array.SetAt(i, object);
1235 return TRUE;
1239 PString PXMLRPCArrayObjectsBase::ToString(PINDEX i) const
1241 PStringStream stream;
1242 stream << *array.GetAt(i);
1243 return stream;
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);
1256 stream >> *object;
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]);
1289 return *this;
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);
1309 #endif
1312 // End of file ///////////////////////////////////////////////////////////////