llfiltersd2xmlrpc.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:19k
源码类别:
游戏引擎
开发平台:
C++ Builder
- /**
- * @file llfiltersd2xmlrpc.cpp
- * @author Phoenix
- * @date 2005-04-26
- *
- * $LicenseInfo:firstyear=2005&license=viewergpl$
- *
- * Copyright (c) 2005-2010, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- /**
- * xml rpc request:
- * <code>
- * <?xml version="1.0"?>
- * <methodCall><methodName>examples.getStateName</methodName>
- * <params><param><value><i4>41</i4></value></param></params>
- * </methodCall>
- * </code>
- *
- * xml rpc response:
- * <code>
- * <?xml version="1.0"?>
- * <methodResponse>
- * <params><param><value><string>South Dakota</string></value></param></params>
- * </methodResponse>
- * </code>
- *
- * xml rpc fault:
- * </code>
- * <?xml version="1.0"?>
- * <methodResponse>
- * <fault><value><struct>
- * <member><name>faultCode</name><value><int>4</int></value></member>
- * <member><name>faultString</name><value><string>...</string></value></member>
- * </struct></value></fault>
- * </methodResponse>
- * </code>
- *
- * llsd rpc request:
- * <code>
- * { 'method':'...', 'parameter':...]}
- * </code>
- *
- * llsd rpc response:
- * <code>
- * { 'response':... }
- * </code>
- *
- * llsd rpc fault:
- * <code>
- * { 'fault': {'code':i..., 'description':'...'} }
- * </code>
- *
- */
- #include "linden_common.h"
- #include "llfiltersd2xmlrpc.h"
- #include <sstream>
- #include <iterator>
- #include <xmlrpc-epi/xmlrpc.h>
- #include "apr_base64.h"
- #include "llbuffer.h"
- #include "llbufferstream.h"
- #include "llmemorystream.h"
- #include "llsd.h"
- #include "llsdserialize.h"
- #include "lluuid.h"
- // spammy mode
- //#define LL_SPEW_STREAM_OUT_DEBUGGING 1
- /**
- * String constants
- */
- static const char XML_HEADER[] = "<?xml version="1.0" encoding="UTF-8"?>";
- static const char XMLRPC_REQUEST_HEADER_1[] = "<methodCall><methodName>";
- static const char XMLRPC_REQUEST_HEADER_2[] = "</methodName><params>";
- static const char XMLRPC_REQUEST_FOOTER[] = "</params></methodCall>";
- static const char XMLRPC_METHOD_RESPONSE_HEADER[] = "<methodResponse>";
- static const char XMLRPC_METHOD_RESPONSE_FOOTER[] = "</methodResponse>";
- static const char XMLRPC_RESPONSE_HEADER[] = "<params><param>";
- static const char XMLRPC_RESPONSE_FOOTER[] = "</param></params>";
- static const char XMLRPC_FAULT_1[] = "<fault><value><struct><member><name>faultCode</name><value><int>";
- static const char XMLRPC_FAULT_2[] = "</int></value></member><member><name>faultString</name><value><string>";
- static const char XMLRPC_FAULT_3[] = "</string></value></member></struct></value></fault>";
- static const char LLSDRPC_RESPONSE_HEADER[] = "{'response':";
- static const char LLSDRPC_RESPONSE_FOOTER[] = "}";
- const char LLSDRPC_REQUEST_HEADER_1[] = "{'method':'";
- const char LLSDRPC_REQUEST_HEADER_2[] = "', 'parameter': ";
- const char LLSDRPC_REQUEST_FOOTER[] = "}";
- static const char LLSDRPC_FAULT_HADER_1[] = "{ 'fault': {'code':i";
- static const char LLSDRPC_FAULT_HADER_2[] = ", 'description':";
- static const char LLSDRPC_FAULT_FOOTER[] = "} }";
- static const S32 DEFAULT_PRECISION = 20;
- /**
- * LLFilterSD2XMLRPC
- */
- LLFilterSD2XMLRPC::LLFilterSD2XMLRPC()
- {
- }
- LLFilterSD2XMLRPC::~LLFilterSD2XMLRPC()
- {
- }
- std::string xml_escape_string(const std::string& in)
- {
- std::ostringstream out;
- std::string::const_iterator it = in.begin();
- std::string::const_iterator end = in.end();
- for(; it != end; ++it)
- {
- switch((*it))
- {
- case '<':
- out << "<";
- break;
- case '>':
- out << ">";
- break;
- case '&':
- out << "&";
- break;
- case ''':
- out << "'";
- break;
- case '"':
- out << """;
- break;
- default:
- out << (*it);
- break;
- }
- }
- return out.str();
- }
- void LLFilterSD2XMLRPC::streamOut(std::ostream& ostr, const LLSD& sd)
- {
- ostr << "<value>";
- switch(sd.type())
- {
- case LLSD::TypeMap:
- {
- #if LL_SPEW_STREAM_OUT_DEBUGGING
- llinfos << "streamOut(map) BEGIN" << llendl;
- #endif
- ostr << "<struct>";
- if(ostr.fail())
- {
- llinfos << "STREAM FAILURE writing struct" << llendl;
- }
- LLSD::map_const_iterator it = sd.beginMap();
- LLSD::map_const_iterator end = sd.endMap();
- for(; it != end; ++it)
- {
- ostr << "<member><name>" << xml_escape_string((*it).first)
- << "</name>";
- streamOut(ostr, (*it).second);
- if(ostr.fail())
- {
- llinfos << "STREAM FAILURE writing '" << (*it).first
- << "' with sd type " << (*it).second.type() << llendl;
- }
- ostr << "</member>";
- }
- ostr << "</struct>";
- #if LL_SPEW_STREAM_OUT_DEBUGGING
- llinfos << "streamOut(map) END" << llendl;
- #endif
- break;
- }
- case LLSD::TypeArray:
- {
- #if LL_SPEW_STREAM_OUT_DEBUGGING
- llinfos << "streamOut(array) BEGIN" << llendl;
- #endif
- ostr << "<array><data>";
- LLSD::array_const_iterator it = sd.beginArray();
- LLSD::array_const_iterator end = sd.endArray();
- for(; it != end; ++it)
- {
- streamOut(ostr, *it);
- if(ostr.fail())
- {
- llinfos << "STREAM FAILURE writing array element sd type "
- << (*it).type() << llendl;
- }
- }
- #if LL_SPEW_STREAM_OUT_DEBUGGING
- llinfos << "streamOut(array) END" << llendl;
- #endif
- ostr << "</data></array>";
- break;
- }
- case LLSD::TypeUndefined:
- // treat undefined as a bool with a false value.
- case LLSD::TypeBoolean:
- #if LL_SPEW_STREAM_OUT_DEBUGGING
- llinfos << "streamOut(bool)" << llendl;
- #endif
- ostr << "<boolean>" << (sd.asBoolean() ? "1" : "0") << "</boolean>";
- break;
- case LLSD::TypeInteger:
- #if LL_SPEW_STREAM_OUT_DEBUGGING
- llinfos << "streamOut(int)" << llendl;
- #endif
- ostr << "<i4>" << sd.asInteger() << "</i4>";
- break;
- case LLSD::TypeReal:
- #if LL_SPEW_STREAM_OUT_DEBUGGING
- llinfos << "streamOut(real)" << llendl;
- #endif
- ostr << "<double>" << sd.asReal() << "</double>";
- break;
- case LLSD::TypeString:
- #if LL_SPEW_STREAM_OUT_DEBUGGING
- llinfos << "streamOut(string)" << llendl;
- #endif
- ostr << "<string>" << xml_escape_string(sd.asString()) << "</string>";
- break;
- case LLSD::TypeUUID:
- #if LL_SPEW_STREAM_OUT_DEBUGGING
- llinfos << "streamOut(uuid)" << llendl;
- #endif
- // serialize it as a string
- ostr << "<string>" << sd.asString() << "</string>";
- break;
- case LLSD::TypeURI:
- {
- #if LL_SPEW_STREAM_OUT_DEBUGGING
- llinfos << "streamOut(uri)" << llendl;
- #endif
- // serialize it as a string
- ostr << "<string>" << xml_escape_string(sd.asString()) << "</string>";
- break;
- }
- case LLSD::TypeBinary:
- {
- #if LL_SPEW_STREAM_OUT_DEBUGGING
- llinfos << "streamOut(binary)" << llendl;
- #endif
- // this is pretty inefficient, but we'll deal with that
- // problem when it becomes one.
- ostr << "<base64>";
- LLSD::Binary buffer = sd.asBinary();
- if(!buffer.empty())
- {
- // *TODO: convert to LLBase64
- int b64_buffer_length = apr_base64_encode_len(buffer.size());
- char* b64_buffer = new char[b64_buffer_length];
- b64_buffer_length = apr_base64_encode_binary(
- b64_buffer,
- &buffer[0],
- buffer.size());
- ostr.write(b64_buffer, b64_buffer_length - 1);
- delete[] b64_buffer;
- }
- ostr << "</base64>";
- break;
- }
- case LLSD::TypeDate:
- #if LL_SPEW_STREAM_OUT_DEBUGGING
- llinfos << "streamOut(date)" << llendl;
- #endif
- // no need to escape this since it will be alpha-numeric.
- ostr << "<dateTime.iso8601>" << sd.asString() << "</dateTime.iso8601>";
- break;
- default:
- // unhandled type
- llwarns << "Unhandled structured data type: " << sd.type()
- << llendl;
- break;
- }
- ostr << "</value>";
- }
- /**
- * LLFilterSD2XMLRPCResponse
- */
- LLFilterSD2XMLRPCResponse::LLFilterSD2XMLRPCResponse()
- {
- }
- LLFilterSD2XMLRPCResponse::~LLFilterSD2XMLRPCResponse()
- {
- }
- // virtual
- LLIOPipe::EStatus LLFilterSD2XMLRPCResponse::process_impl(
- const LLChannelDescriptors& channels,
- buffer_ptr_t& buffer,
- bool& eos,
- LLSD& context,
- LLPumpIO* pump)
- {
- PUMP_DEBUG;
- // This pipe does not work if it does not have everyting. This
- // could be addressed by making a stream parser for llsd which
- // handled partial information.
- if(!eos)
- {
- return STATUS_BREAK;
- }
- PUMP_DEBUG;
- // we have everyting in the buffer, so turn the structure data rpc
- // response into an xml rpc response.
- LLBufferStream stream(channels, buffer.get());
- stream << XML_HEADER << XMLRPC_METHOD_RESPONSE_HEADER;
- LLSD sd;
- LLSDSerialize::fromNotation(sd, stream, buffer->count(channels.in()));
- PUMP_DEBUG;
- LLIOPipe::EStatus rv = STATUS_ERROR;
- if(sd.has("response"))
- {
- PUMP_DEBUG;
- // it is a normal response. pack it up and ship it out.
- stream.precision(DEFAULT_PRECISION);
- stream << XMLRPC_RESPONSE_HEADER;
- streamOut(stream, sd["response"]);
- stream << XMLRPC_RESPONSE_FOOTER << XMLRPC_METHOD_RESPONSE_FOOTER;
- rv = STATUS_DONE;
- }
- else if(sd.has("fault"))
- {
- PUMP_DEBUG;
- // it is a fault.
- stream << XMLRPC_FAULT_1 << sd["fault"]["code"].asInteger()
- << XMLRPC_FAULT_2
- << xml_escape_string(sd["fault"]["description"].asString())
- << XMLRPC_FAULT_3 << XMLRPC_METHOD_RESPONSE_FOOTER;
- rv = STATUS_DONE;
- }
- else
- {
- llwarns << "Unable to determine the type of LLSD response." << llendl;
- }
- PUMP_DEBUG;
- return rv;
- }
- /**
- * LLFilterSD2XMLRPCRequest
- */
- LLFilterSD2XMLRPCRequest::LLFilterSD2XMLRPCRequest()
- {
- }
- LLFilterSD2XMLRPCRequest::LLFilterSD2XMLRPCRequest(const char* method)
- {
- if(method)
- {
- mMethod.assign(method);
- }
- }
- LLFilterSD2XMLRPCRequest::~LLFilterSD2XMLRPCRequest()
- {
- }
- // virtual
- LLIOPipe::EStatus LLFilterSD2XMLRPCRequest::process_impl(
- const LLChannelDescriptors& channels,
- buffer_ptr_t& buffer,
- bool& eos,
- LLSD& context,
- LLPumpIO* pump)
- {
- // This pipe does not work if it does not have everyting. This
- // could be addressed by making a stream parser for llsd which
- // handled partial information.
- PUMP_DEBUG;
- if(!eos)
- {
- llinfos << "!eos" << llendl;
- return STATUS_BREAK;
- }
- // See if we can parse it
- LLBufferStream stream(channels, buffer.get());
- LLSD sd;
- LLSDSerialize::fromNotation(sd, stream, buffer->count(channels.in()));
- if(stream.fail())
- {
- llinfos << "STREAM FAILURE reading structure data." << llendl;
- }
- PUMP_DEBUG;
- // We can get the method and parameters from either the member
- // function or passed in via the buffer. We prefer the buffer if
- // we found a parameter and a method, or fall back to using
- // mMethod and putting everyting in the buffer into the parameter.
- std::string method;
- LLSD param_sd;
- if(sd.has("method") && sd.has("parameter"))
- {
- method = sd["method"].asString();
- param_sd = sd["parameter"];
- }
- else
- {
- method = mMethod;
- param_sd = sd;
- }
- if(method.empty())
- {
- llwarns << "SD -> XML Request no method found." << llendl;
- return STATUS_ERROR;
- }
- PUMP_DEBUG;
- // We have a method, and some kind of parameter, so package it up
- // and send it out.
- LLBufferStream ostream(channels, buffer.get());
- ostream.precision(DEFAULT_PRECISION);
- if(ostream.fail())
- {
- llinfos << "STREAM FAILURE setting precision" << llendl;
- }
- ostream << XML_HEADER << XMLRPC_REQUEST_HEADER_1
- << xml_escape_string(method) << XMLRPC_REQUEST_HEADER_2;
- if(ostream.fail())
- {
- llinfos << "STREAM FAILURE writing method headers" << llendl;
- }
- switch(param_sd.type())
- {
- case LLSD::TypeMap:
- // If the params are a map, then we do not want to iterate
- // through them since the iterators returned will be map
- // ordered un-named values, which will lose the names, and
- // only stream the values, turning it into an array.
- ostream << "<param>";
- streamOut(ostream, param_sd);
- ostream << "</param>";
- break;
- case LLSD::TypeArray:
- {
- LLSD::array_iterator it = param_sd.beginArray();
- LLSD::array_iterator end = param_sd.endArray();
- for(; it != end; ++it)
- {
- ostream << "<param>";
- streamOut(ostream, *it);
- ostream << "</param>";
- }
- break;
- }
- default:
- ostream << "<param>";
- streamOut(ostream, param_sd);
- ostream << "</param>";
- break;
- }
- stream << XMLRPC_REQUEST_FOOTER;
- return STATUS_DONE;
- }
- /**
- * LLFilterXMLRPCResponse2LLSD
- */
- // this is a c function here since it's really an implementation
- // detail that requires a header file just get the definition of the
- // parameters.
- LLIOPipe::EStatus stream_out(std::ostream& ostr, XMLRPC_VALUE value)
- {
- XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(value);
- LLIOPipe::EStatus status = LLIOPipe::STATUS_OK;
- switch(type)
- {
- case xmlrpc_type_base64:
- {
- S32 len = XMLRPC_GetValueStringLen(value);
- const char* buf = XMLRPC_GetValueBase64(value);
- ostr << " b(";
- if((len > 0) && buf)
- {
- ostr << len << ")"";
- ostr.write(buf, len);
- ostr << """;
- }
- else
- {
- ostr << "0)""";
- }
- break;
- }
- case xmlrpc_type_boolean:
- //lldebugs << "stream_out() bool" << llendl;
- ostr << " " << (XMLRPC_GetValueBoolean(value) ? "true" : "false");
- break;
- case xmlrpc_type_datetime:
- ostr << " d"" << XMLRPC_GetValueDateTime_ISO8601(value) << """;
- break;
- case xmlrpc_type_double:
- ostr << " r" << XMLRPC_GetValueDouble(value);
- //lldebugs << "stream_out() double" << XMLRPC_GetValueDouble(value)
- // << llendl;
- break;
- case xmlrpc_type_int:
- ostr << " i" << XMLRPC_GetValueInt(value);
- //lldebugs << "stream_out() integer:" << XMLRPC_GetValueInt(value)
- // << llendl;
- break;
- case xmlrpc_type_string:
- //lldebugs << "stream_out() string: " << str << llendl;
- ostr << " s(" << XMLRPC_GetValueStringLen(value) << ")'"
- << XMLRPC_GetValueString(value) << "'";
- break;
- case xmlrpc_type_array: // vector
- case xmlrpc_type_mixed: // vector
- {
- //lldebugs << "stream_out() array" << llendl;
- ostr << " [";
- U32 needs_comma = 0;
- XMLRPC_VALUE current = XMLRPC_VectorRewind(value);
- while(current && (LLIOPipe::STATUS_OK == status))
- {
- if(needs_comma++) ostr << ",";
- status = stream_out(ostr, current);
- current = XMLRPC_VectorNext(value);
- }
- ostr << "]";
- break;
- }
- case xmlrpc_type_struct: // still vector
- {
- //lldebugs << "stream_out() struct" << llendl;
- ostr << " {";
- std::string name;
- U32 needs_comma = 0;
- XMLRPC_VALUE current = XMLRPC_VectorRewind(value);
- while(current && (LLIOPipe::STATUS_OK == status))
- {
- if(needs_comma++) ostr << ",";
- name.assign(XMLRPC_GetValueID(current));
- ostr << "'" << LLSDNotationFormatter::escapeString(name) << "':";
- status = stream_out(ostr, current);
- current = XMLRPC_VectorNext(value);
- }
- ostr << "}";
- break;
- }
- case xmlrpc_type_empty:
- case xmlrpc_type_none:
- default:
- status = LLIOPipe::STATUS_ERROR;
- llwarns << "Found an empty xmlrpc type.." << llendl;
- // not much we can do here...
- break;
- };
- return status;
- }
- LLFilterXMLRPCResponse2LLSD::LLFilterXMLRPCResponse2LLSD()
- {
- }
- LLFilterXMLRPCResponse2LLSD::~LLFilterXMLRPCResponse2LLSD()
- {
- }
- LLIOPipe::EStatus LLFilterXMLRPCResponse2LLSD::process_impl(
- const LLChannelDescriptors& channels,
- buffer_ptr_t& buffer,
- bool& eos,
- LLSD& context,
- LLPumpIO* pump)
- {
- PUMP_DEBUG;
- if(!eos) return STATUS_BREAK;
- if(!buffer) return STATUS_ERROR;
- PUMP_DEBUG;
- // *FIX: This technique for reading data is far from optimal. We
- // need to have some kind of istream interface into the xml
- // parser...
- S32 bytes = buffer->countAfter(channels.in(), NULL);
- if(!bytes) return STATUS_ERROR;
- char* buf = new char[bytes + 1];
- buf[bytes] = ' ';
- buffer->readAfter(channels.in(), NULL, (U8*)buf, bytes);
- //lldebugs << "xmlrpc response: " << buf << llendl;
- PUMP_DEBUG;
- XMLRPC_REQUEST response = XMLRPC_REQUEST_FromXML(
- buf,
- bytes,
- NULL);
- if(!response)
- {
- llwarns << "XML -> SD Response unable to parse xml." << llendl;
- delete[] buf;
- return STATUS_ERROR;
- }
- PUMP_DEBUG;
- LLBufferStream stream(channels, buffer.get());
- stream.precision(DEFAULT_PRECISION);
- if(XMLRPC_ResponseIsFault(response))
- {
- PUMP_DEBUG;
- stream << LLSDRPC_FAULT_HADER_1
- << XMLRPC_GetResponseFaultCode(response)
- << LLSDRPC_FAULT_HADER_2;
- const char* fault_str = XMLRPC_GetResponseFaultString(response);
- std::string fault_string;
- if(fault_str)
- {
- fault_string.assign(fault_str);
- }
- stream << "'" << LLSDNotationFormatter::escapeString(fault_string)
- << "'" <<LLSDRPC_FAULT_FOOTER;
- }
- else
- {
- PUMP_DEBUG;
- stream << LLSDRPC_RESPONSE_HEADER;
- XMLRPC_VALUE param = XMLRPC_RequestGetData(response);
- if(param)
- {
- stream_out(stream, param);
- }
- stream << LLSDRPC_RESPONSE_FOOTER;
- }
- PUMP_DEBUG;
- XMLRPC_RequestFree(response, 1);
- delete[] buf;
- PUMP_DEBUG;
- return STATUS_DONE;
- }
- /**
- * LLFilterXMLRPCRequest2LLSD
- */
- LLFilterXMLRPCRequest2LLSD::LLFilterXMLRPCRequest2LLSD()
- {
- }
- LLFilterXMLRPCRequest2LLSD::~LLFilterXMLRPCRequest2LLSD()
- {
- }
- LLIOPipe::EStatus LLFilterXMLRPCRequest2LLSD::process_impl(
- const LLChannelDescriptors& channels,
- buffer_ptr_t& buffer,
- bool& eos,
- LLSD& context,
- LLPumpIO* pump)
- {
- PUMP_DEBUG;
- if(!eos) return STATUS_BREAK;
- if(!buffer) return STATUS_ERROR;
- PUMP_DEBUG;
- // *FIX: This technique for reading data is far from optimal. We
- // need to have some kind of istream interface into the xml
- // parser...
- S32 bytes = buffer->countAfter(channels.in(), NULL);
- if(!bytes) return STATUS_ERROR;
- char* buf = new char[bytes + 1];
- buf[bytes] = ' ';
- buffer->readAfter(channels.in(), NULL, (U8*)buf, bytes);
- //lldebugs << "xmlrpc request: " << buf << llendl;
- // Check the value in the buffer. XMLRPC_REQUEST_FromXML will report a error code 4 if
- // values that are less than 0x20 are passed to it, except
- // 0x09: Horizontal tab; 0x0a: New Line; 0x0d: Carriage
- U8* cur_pBuf = (U8*)buf;
- U8 cur_char;
- for (S32 i=0; i<bytes; i++)
- {
- cur_char = *cur_pBuf;
- if ( cur_char < 0x20
- && 0x09 != cur_char
- && 0x0a != cur_char
- && 0x0d != cur_char )
- {
- *cur_pBuf = '?';
- }
- ++cur_pBuf;
- }
- PUMP_DEBUG;
- XMLRPC_REQUEST request = XMLRPC_REQUEST_FromXML(
- buf,
- bytes,
- NULL);
- if(!request)
- {
- llwarns << "XML -> SD Request process parse error." << llendl;
- delete[] buf;
- return STATUS_ERROR;
- }
- PUMP_DEBUG;
- LLBufferStream stream(channels, buffer.get());
- stream.precision(DEFAULT_PRECISION);
- const char* name = XMLRPC_RequestGetMethodName(request);
- stream << LLSDRPC_REQUEST_HEADER_1 << (name ? name : "")
- << LLSDRPC_REQUEST_HEADER_2;
- XMLRPC_VALUE param = XMLRPC_RequestGetData(request);
- if(param)
- {
- PUMP_DEBUG;
- S32 size = XMLRPC_VectorSize(param);
- if(size > 1)
- {
- // if there are multiple parameters, stuff the values into
- // an array so that the next step in the chain can read them.
- stream << "[";
- }
- XMLRPC_VALUE current = XMLRPC_VectorRewind(param);
- bool needs_comma = false;
- while(current)
- {
- if(needs_comma)
- {
- stream << ",";
- }
- needs_comma = true;
- stream_out(stream, current);
- current = XMLRPC_VectorNext(param);
- }
- if(size > 1)
- {
- // close the array
- stream << "]";
- }
- }
- stream << LLSDRPC_REQUEST_FOOTER;
- XMLRPC_RequestFree(request, 1);
- delete[] buf;
- PUMP_DEBUG;
- return STATUS_DONE;
- }