llfiltersd2xmlrpc.cpp

Go to the documentation of this file.
00001 
00078 #include "linden_common.h"
00079 #include "llfiltersd2xmlrpc.h"
00080 
00081 #include <sstream>
00082 #include <iterator>
00083 #include <xmlrpc-epi/xmlrpc.h>
00084 #include "apr-1/apr_base64.h"
00085 
00086 #include "llbuffer.h"
00087 #include "llbufferstream.h"
00088 #include "llmemorystream.h"
00089 #include "llsd.h"
00090 #include "llsdserialize.h"
00091 #include "lluuid.h"
00092 
00093 // spammy mode
00094 //#define LL_SPEW_STREAM_OUT_DEBUGGING 1
00095 
00099 static const char XML_HEADER[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
00100 static const char XMLRPC_REQUEST_HEADER_1[] = "<methodCall><methodName>";
00101 static const char XMLRPC_REQUEST_HEADER_2[] = "</methodName><params>";
00102 static const char XMLRPC_REQUEST_FOOTER[] = "</params></methodCall>";
00103 static const char XMLRPC_METHOD_RESPONSE_HEADER[] = "<methodResponse>";
00104 static const char XMLRPC_METHOD_RESPONSE_FOOTER[] = "</methodResponse>";
00105 static const char XMLRPC_RESPONSE_HEADER[] = "<params><param>";
00106 static const char XMLRPC_RESPONSE_FOOTER[] = "</param></params>";
00107 static const char XMLRPC_FAULT_1[] = "<fault><value><struct><member><name>faultCode</name><value><int>";
00108 static const char XMLRPC_FAULT_2[] = "</int></value></member><member><name>faultString</name><value><string>";
00109 static const char XMLRPC_FAULT_3[] = "</string></value></member></struct></value></fault>";
00110 static const char LLSDRPC_RESPONSE_HEADER[] = "{'response':";
00111 static const char LLSDRPC_RESPONSE_FOOTER[] = "}";
00112 const char LLSDRPC_REQUEST_HEADER_1[] = "{'method':'";
00113 const char LLSDRPC_REQUEST_HEADER_2[] = "', 'parameter': ";
00114 const char LLSDRPC_REQUEST_FOOTER[] = "}";
00115 static const char LLSDRPC_FAULT_HADER_1[] = "{ 'fault': {'code':i";
00116 static const char LLSDRPC_FAULT_HADER_2[] = ", 'description':";
00117 static const char LLSDRPC_FAULT_FOOTER[] = "} }";
00118 static const S32 DEFAULT_PRECISION = 20;
00119 
00123 LLFilterSD2XMLRPC::LLFilterSD2XMLRPC()
00124 {
00125 }
00126 
00127 LLFilterSD2XMLRPC::~LLFilterSD2XMLRPC()
00128 {
00129 }
00130 
00131 std::string xml_escape_string(const std::string& in)
00132 {
00133         std::ostringstream out;
00134         std::string::const_iterator it = in.begin();
00135         std::string::const_iterator end = in.end();
00136         for(; it != end; ++it)
00137         {
00138                 switch((*it))
00139                 {
00140                 case '<':
00141                         out << "&lt;";
00142                         break;
00143                 case '>':
00144                         out << "&gt;";
00145                         break;
00146                 case '&':
00147                         out << "&amp;";
00148                         break;
00149                 case '\'':
00150                         out << "&apos;";
00151                         break;
00152                 case '"':
00153                         out << "&quot;";
00154                         break;
00155                 default:
00156                         out << (*it);
00157                         break;
00158                 }
00159         }
00160         return out.str();
00161 }
00162 
00163 void LLFilterSD2XMLRPC::streamOut(std::ostream& ostr, const LLSD& sd)
00164 {
00165         ostr << "<value>";
00166         switch(sd.type())
00167         {
00168         case LLSD::TypeMap:
00169         {
00170 #if LL_SPEW_STREAM_OUT_DEBUGGING
00171                 llinfos << "streamOut(map) BEGIN" << llendl;
00172 #endif
00173                 ostr << "<struct>";
00174                 if(ostr.fail())
00175                 {
00176                         llinfos << "STREAM FAILURE writing struct" << llendl;
00177                 }
00178                 LLSD::map_const_iterator it = sd.beginMap();
00179                 LLSD::map_const_iterator end = sd.endMap();
00180                 for(; it != end; ++it)
00181                 {
00182                         ostr << "<member><name>" << xml_escape_string((*it).first)
00183                                 << "</name>";
00184                         streamOut(ostr, (*it).second);
00185                         if(ostr.fail())
00186                         {
00187                                 llinfos << "STREAM FAILURE writing '" << (*it).first
00188                                                 << "' with sd type " << (*it).second.type() << llendl;
00189                         }
00190                         ostr << "</member>";
00191                 }
00192                 ostr << "</struct>";
00193 #if LL_SPEW_STREAM_OUT_DEBUGGING
00194                 llinfos << "streamOut(map) END" << llendl;
00195 #endif
00196                 break;
00197         }
00198         case LLSD::TypeArray:
00199         {
00200 #if LL_SPEW_STREAM_OUT_DEBUGGING
00201                 llinfos << "streamOut(array) BEGIN" << llendl;
00202 #endif
00203                 ostr << "<array><data>";
00204                 LLSD::array_const_iterator it = sd.beginArray();
00205                 LLSD::array_const_iterator end = sd.endArray();
00206                 for(; it != end; ++it)
00207                 {
00208                         streamOut(ostr, *it);
00209                         if(ostr.fail())
00210                         {
00211                                 llinfos << "STREAM FAILURE writing array element sd type "
00212                                                 << (*it).type() << llendl;
00213                         }
00214                 }
00215 #if LL_SPEW_STREAM_OUT_DEBUGGING
00216                 llinfos << "streamOut(array) END" << llendl;
00217 #endif
00218                 ostr << "</data></array>";
00219                 break;
00220         }
00221         case LLSD::TypeUndefined:
00222                 // treat undefined as a bool with a false value.
00223         case LLSD::TypeBoolean:
00224 #if LL_SPEW_STREAM_OUT_DEBUGGING
00225                 llinfos << "streamOut(bool)" << llendl;
00226 #endif
00227                 ostr << "<boolean>" << (sd.asBoolean() ? "1" : "0") << "</boolean>";
00228                 break;
00229         case LLSD::TypeInteger:
00230 #if LL_SPEW_STREAM_OUT_DEBUGGING
00231                 llinfos << "streamOut(int)" << llendl;
00232 #endif
00233                 ostr << "<i4>" << sd.asInteger() << "</i4>";
00234                 break;
00235         case LLSD::TypeReal:
00236 #if LL_SPEW_STREAM_OUT_DEBUGGING
00237                 llinfos << "streamOut(real)" << llendl;
00238 #endif
00239                 ostr << "<double>" << sd.asReal() << "</double>";
00240                 break;
00241         case LLSD::TypeString:
00242 #if LL_SPEW_STREAM_OUT_DEBUGGING
00243                 llinfos << "streamOut(string)" << llendl;
00244 #endif
00245                 ostr << "<string>" << xml_escape_string(sd.asString()) << "</string>";
00246                 break;
00247         case LLSD::TypeUUID:
00248 #if LL_SPEW_STREAM_OUT_DEBUGGING
00249                 llinfos << "streamOut(uuid)" << llendl;
00250 #endif
00251                 // serialize it as a string
00252                 ostr << "<string>" << sd.asString() << "</string>";
00253                 break;
00254         case LLSD::TypeURI:
00255         {
00256 #if LL_SPEW_STREAM_OUT_DEBUGGING
00257                 llinfos << "streamOut(uri)" << llendl;
00258 #endif
00259                 // serialize it as a string
00260                 ostr << "<string>" << xml_escape_string(sd.asString()) << "</string>";
00261                 break;
00262         }
00263         case LLSD::TypeBinary:
00264         {
00265 #if LL_SPEW_STREAM_OUT_DEBUGGING
00266                 llinfos << "streamOut(binary)" << llendl;
00267 #endif
00268                 // this is pretty inefficient, but we'll deal with that
00269                 // problem when it becomes one.
00270                 ostr << "<base64>";
00271                 LLSD::Binary buffer = sd.asBinary();
00272                 if(!buffer.empty())
00273                 {
00274                         // *TODO: convert to LLBase64
00275                         int b64_buffer_length = apr_base64_encode_len(buffer.size());
00276                         char* b64_buffer = new char[b64_buffer_length];
00277                         b64_buffer_length = apr_base64_encode_binary(
00278                                 b64_buffer,
00279                                 &buffer[0],
00280                                 buffer.size());
00281                         ostr.write(b64_buffer, b64_buffer_length - 1);
00282                         delete[] b64_buffer;
00283                 }
00284                 ostr << "</base64>";
00285                 break;
00286         }
00287         case LLSD::TypeDate:
00288 #if LL_SPEW_STREAM_OUT_DEBUGGING
00289                 llinfos << "streamOut(date)" << llendl;
00290 #endif
00291                 // no need to escape this since it will be alpha-numeric.
00292                 ostr << "<dateTime.iso8601>" << sd.asString() << "</dateTime.iso8601>";
00293                 break;
00294         default:
00295                 // unhandled type
00296                 llwarns << "Unhandled structured data type: " << sd.type()
00297                         << llendl;
00298                 break;
00299         }
00300         ostr << "</value>";
00301 }
00302 
00307 LLFilterSD2XMLRPCResponse::LLFilterSD2XMLRPCResponse()
00308 {
00309 }
00310 
00311 LLFilterSD2XMLRPCResponse::~LLFilterSD2XMLRPCResponse()
00312 {
00313 }
00314 
00315 
00316 // virtual
00317 LLIOPipe::EStatus LLFilterSD2XMLRPCResponse::process_impl(
00318         const LLChannelDescriptors& channels,
00319         buffer_ptr_t& buffer,
00320         bool& eos,
00321         LLSD& context,
00322         LLPumpIO* pump)
00323 {
00324         PUMP_DEBUG;
00325         // This pipe does not work if it does not have everyting. This
00326         // could be addressed by making a stream parser for llsd which
00327         // handled partial information.
00328         if(!eos)
00329         {
00330                 return STATUS_BREAK;
00331         }
00332 
00333         PUMP_DEBUG;
00334         // we have everyting in the buffer, so turn the structure data rpc
00335         // response into an xml rpc response.
00336         LLBufferStream stream(channels, buffer.get());
00337         stream << XML_HEADER << XMLRPC_METHOD_RESPONSE_HEADER;
00338         LLSD sd;
00339         LLSDSerialize::fromNotation(sd, stream);
00340 
00341         PUMP_DEBUG;
00342         LLIOPipe::EStatus rv = STATUS_ERROR;
00343         if(sd.has("response"))
00344         {
00345                 PUMP_DEBUG;
00346                 // it is a normal response. pack it up and ship it out.
00347                 stream.precision(DEFAULT_PRECISION);
00348                 stream << XMLRPC_RESPONSE_HEADER;
00349                 streamOut(stream, sd["response"]);
00350                 stream << XMLRPC_RESPONSE_FOOTER << XMLRPC_METHOD_RESPONSE_FOOTER;
00351                 rv = STATUS_DONE;
00352         }
00353         else if(sd.has("fault"))
00354         {
00355                 PUMP_DEBUG;
00356                 // it is a fault.
00357                 stream << XMLRPC_FAULT_1 << sd["fault"]["code"].asInteger()
00358                         << XMLRPC_FAULT_2
00359                         << xml_escape_string(sd["fault"]["description"].asString())
00360                         << XMLRPC_FAULT_3 << XMLRPC_METHOD_RESPONSE_FOOTER;
00361                 rv = STATUS_DONE;
00362         }
00363         else
00364         {
00365                 llwarns << "Unable to determine the type of LLSD response." << llendl;
00366         }
00367         PUMP_DEBUG;
00368         return rv;
00369 }
00370 
00374 LLFilterSD2XMLRPCRequest::LLFilterSD2XMLRPCRequest()
00375 {
00376 }
00377 
00378 LLFilterSD2XMLRPCRequest::LLFilterSD2XMLRPCRequest(const char* method)
00379 {
00380         if(method)
00381         {
00382                 mMethod.assign(method);
00383         }
00384 }
00385 
00386 LLFilterSD2XMLRPCRequest::~LLFilterSD2XMLRPCRequest()
00387 {
00388 }
00389 
00390 // virtual
00391 LLIOPipe::EStatus LLFilterSD2XMLRPCRequest::process_impl(
00392         const LLChannelDescriptors& channels,
00393         buffer_ptr_t& buffer,
00394         bool& eos,
00395         LLSD& context,
00396         LLPumpIO* pump)
00397 {
00398         // This pipe does not work if it does not have everyting. This
00399         // could be addressed by making a stream parser for llsd which
00400         // handled partial information.
00401         PUMP_DEBUG;
00402         if(!eos)
00403         {
00404                 llinfos << "!eos" << llendl;
00405                 return STATUS_BREAK;
00406         }
00407 
00408         // See if we can parse it
00409         LLBufferStream stream(channels, buffer.get());
00410         LLSD sd;
00411         LLSDSerialize::fromNotation(sd, stream);
00412         if(stream.fail())
00413         {
00414                 llinfos << "STREAM FAILURE reading structure data." << llendl;
00415         }
00416 
00417         PUMP_DEBUG;
00418         // We can get the method and parameters from either the member
00419         // function or passed in via the buffer. We prefer the buffer if
00420         // we found a parameter and a method, or fall back to using
00421         // mMethod and putting everyting in the buffer into the parameter.
00422         std::string method;
00423         LLSD param_sd;
00424         if(sd.has("method") && sd.has("parameter"))
00425         {
00426                 method = sd["method"].asString();
00427                 param_sd = sd["parameter"];
00428         }
00429         else
00430         {
00431                 method = mMethod;
00432                 param_sd = sd;
00433         }
00434         if(method.empty())
00435         {
00436                 llwarns << "SD -> XML Request no method found." << llendl;
00437                 return STATUS_ERROR;
00438         }
00439 
00440         PUMP_DEBUG;
00441         // We have a method, and some kind of parameter, so package it up
00442         // and send it out.
00443         LLBufferStream ostream(channels, buffer.get());
00444         ostream.precision(DEFAULT_PRECISION);
00445         if(ostream.fail())
00446         {
00447                 llinfos << "STREAM FAILURE setting precision" << llendl;
00448         }
00449         ostream << XML_HEADER << XMLRPC_REQUEST_HEADER_1
00450                 << xml_escape_string(method) << XMLRPC_REQUEST_HEADER_2;
00451         if(ostream.fail())
00452         {
00453                 llinfos << "STREAM FAILURE writing method headers" << llendl;
00454         }
00455         switch(param_sd.type())
00456         {
00457         case LLSD::TypeMap:
00458                 // If the params are a map, then we do not want to iterate
00459                 // through them since the iterators returned will be map
00460                 // ordered un-named values, which will lose the names, and
00461                 // only stream the values, turning it into an array.
00462                 ostream << "<param>";
00463                 streamOut(ostream, param_sd);
00464                 ostream << "</param>";
00465                 break;
00466         case LLSD::TypeArray:
00467         {
00468 
00469                 LLSD::array_iterator it = param_sd.beginArray();
00470                 LLSD::array_iterator end = param_sd.endArray();
00471                 for(; it != end; ++it)
00472                 {
00473                         ostream << "<param>";
00474                         streamOut(ostream, *it);
00475                         ostream << "</param>";
00476                 }
00477                 break;
00478         }
00479         default:
00480                 ostream << "<param>";
00481                 streamOut(ostream, param_sd);
00482                 ostream << "</param>";
00483                 break;
00484         }
00485 
00486         stream << XMLRPC_REQUEST_FOOTER;
00487         return STATUS_DONE;
00488 }
00489 
00493 // this is a c function here since it's really an implementation
00494 // detail that requires a header file just get the definition of the
00495 // parameters.
00496 LLIOPipe::EStatus stream_out(std::ostream& ostr, XMLRPC_VALUE value)
00497 {
00498         XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(value);
00499         LLIOPipe::EStatus status = LLIOPipe::STATUS_OK;
00500         switch(type)
00501         {
00502         case xmlrpc_type_base64:
00503         {
00504                 S32 len = XMLRPC_GetValueStringLen(value);
00505                 const char* buf = XMLRPC_GetValueBase64(value);
00506                 ostr << " b(";
00507                 if((len > 0) && buf)
00508                 {
00509                         ostr << len << ")\"";
00510                         ostr.write(buf, len);
00511                         ostr << "\"";
00512                 }
00513                 else
00514                 {
00515                         ostr << "0)\"\"";
00516                 }
00517                 break;
00518         }
00519         case xmlrpc_type_boolean:
00520                 //lldebugs << "stream_out() bool" << llendl;
00521                 ostr << " " << (XMLRPC_GetValueBoolean(value) ? "true" : "false");
00522                 break;
00523         case xmlrpc_type_datetime:
00524                 ostr << " d\"" << XMLRPC_GetValueDateTime_ISO8601(value) << "\"";
00525                 break;
00526         case xmlrpc_type_double:
00527                 ostr << " r" << XMLRPC_GetValueDouble(value);
00528                 //lldebugs << "stream_out() double" << XMLRPC_GetValueDouble(value)
00529                 //               << llendl;
00530                 break;
00531         case xmlrpc_type_int:
00532                 ostr << " i" << XMLRPC_GetValueInt(value);
00533                 //lldebugs << "stream_out() integer:" << XMLRPC_GetValueInt(value)
00534                 //               << llendl;
00535                 break;
00536         case xmlrpc_type_string:
00537                 //lldebugs << "stream_out() string: " << str << llendl;
00538                 ostr << " s(" << XMLRPC_GetValueStringLen(value) << ")'"
00539                         << XMLRPC_GetValueString(value) << "'";
00540                 break;
00541         case xmlrpc_type_array: // vector
00542         case xmlrpc_type_mixed: // vector
00543         {
00544                 //lldebugs << "stream_out() array" << llendl;
00545                 ostr << " [";
00546                 U32 needs_comma = 0;
00547                 XMLRPC_VALUE current = XMLRPC_VectorRewind(value);
00548                 while(current && (LLIOPipe::STATUS_OK == status))
00549                 {
00550                         if(needs_comma++) ostr << ",";
00551                         status = stream_out(ostr, current);
00552                         current = XMLRPC_VectorNext(value);
00553                 }
00554                 ostr << "]";
00555                 break;
00556         }
00557         case xmlrpc_type_struct: // still vector
00558         {
00559                 //lldebugs << "stream_out() struct" << llendl;
00560                 ostr << " {";
00561                 std::string name;
00562                 U32 needs_comma = 0;
00563                 XMLRPC_VALUE current = XMLRPC_VectorRewind(value);
00564                 while(current && (LLIOPipe::STATUS_OK == status))
00565                 {
00566                         if(needs_comma++) ostr << ",";
00567                         name.assign(XMLRPC_GetValueID(current));
00568                         ostr << "'" << LLSDNotationFormatter::escapeString(name) << "':";
00569                         status = stream_out(ostr, current);
00570                         current = XMLRPC_VectorNext(value);
00571                 }
00572                 ostr << "}";
00573                 break;
00574         }
00575         case xmlrpc_type_empty:
00576         case xmlrpc_type_none:
00577         default:
00578                 status = LLIOPipe::STATUS_ERROR;
00579                 llwarns << "Found an empty xmlrpc type.." << llendl;
00580                 // not much we can do here...
00581                 break;
00582         };
00583         return status;
00584 }
00585 
00586 LLFilterXMLRPCResponse2LLSD::LLFilterXMLRPCResponse2LLSD()
00587 {
00588 }
00589 
00590 LLFilterXMLRPCResponse2LLSD::~LLFilterXMLRPCResponse2LLSD()
00591 {
00592 }
00593 
00594 LLIOPipe::EStatus LLFilterXMLRPCResponse2LLSD::process_impl(
00595         const LLChannelDescriptors& channels,
00596         buffer_ptr_t& buffer,
00597         bool& eos,
00598         LLSD& context,
00599         LLPumpIO* pump)
00600 {
00601         PUMP_DEBUG;
00602         if(!eos) return STATUS_BREAK;
00603         if(!buffer) return STATUS_ERROR;
00604 
00605         PUMP_DEBUG;
00606         // *FIX: This technique for reading data is far from optimal. We
00607         // need to have some kind of istream interface into the xml
00608         // parser...
00609         S32 bytes = buffer->countAfter(channels.in(), NULL);
00610         if(!bytes) return STATUS_ERROR;
00611         char* buf = new char[bytes + 1];
00612         buf[bytes] = '\0';
00613         buffer->readAfter(channels.in(), NULL, (U8*)buf, bytes);
00614 
00615         //lldebugs << "xmlrpc response: " << buf << llendl;
00616 
00617         PUMP_DEBUG;
00618         XMLRPC_REQUEST response = XMLRPC_REQUEST_FromXML(
00619                 buf,
00620                 bytes,
00621                 NULL);
00622         if(!response)
00623         {
00624                 llwarns << "XML -> SD Response unable to parse xml." << llendl;
00625                 delete[] buf;
00626                 return STATUS_ERROR;
00627         }
00628 
00629         PUMP_DEBUG;
00630         LLBufferStream stream(channels, buffer.get());
00631         stream.precision(DEFAULT_PRECISION);
00632         if(XMLRPC_ResponseIsFault(response))
00633         {
00634                 PUMP_DEBUG;
00635                 stream << LLSDRPC_FAULT_HADER_1
00636                            << XMLRPC_GetResponseFaultCode(response)
00637                            << LLSDRPC_FAULT_HADER_2;
00638                 const char* fault_str = XMLRPC_GetResponseFaultString(response);
00639                 std::string fault_string;
00640                 if(fault_str)
00641                 {
00642                         fault_string.assign(fault_str);
00643                 }
00644                 stream << "'" << LLSDNotationFormatter::escapeString(fault_string)
00645                    << "'" <<LLSDRPC_FAULT_FOOTER;
00646         }
00647         else
00648         {
00649                 PUMP_DEBUG;
00650                 stream << LLSDRPC_RESPONSE_HEADER;
00651                 XMLRPC_VALUE param = XMLRPC_RequestGetData(response);
00652                 if(param)
00653                 {
00654                         stream_out(stream, param);
00655                 }
00656                 stream << LLSDRPC_RESPONSE_FOOTER;
00657         }
00658         PUMP_DEBUG;
00659         XMLRPC_RequestFree(response, 1);
00660         delete[] buf;
00661         PUMP_DEBUG;
00662         return STATUS_DONE;
00663 }
00664 
00668 LLFilterXMLRPCRequest2LLSD::LLFilterXMLRPCRequest2LLSD()
00669 {
00670 }
00671 
00672 LLFilterXMLRPCRequest2LLSD::~LLFilterXMLRPCRequest2LLSD()
00673 {
00674 }
00675 
00676 LLIOPipe::EStatus LLFilterXMLRPCRequest2LLSD::process_impl(
00677         const LLChannelDescriptors& channels,
00678         buffer_ptr_t& buffer,
00679         bool& eos,
00680         LLSD& context,
00681         LLPumpIO* pump)
00682 {
00683         PUMP_DEBUG;
00684         if(!eos) return STATUS_BREAK;
00685         if(!buffer) return STATUS_ERROR;
00686 
00687         PUMP_DEBUG;
00688         // *FIX: This technique for reading data is far from optimal. We
00689         // need to have some kind of istream interface into the xml
00690         // parser...
00691         S32 bytes = buffer->countAfter(channels.in(), NULL);
00692         if(!bytes) return STATUS_ERROR;
00693         char* buf = new char[bytes + 1];
00694         buf[bytes] = '\0';
00695         buffer->readAfter(channels.in(), NULL, (U8*)buf, bytes);
00696 
00697         //lldebugs << "xmlrpc request: " << buf << llendl;
00698 
00699         PUMP_DEBUG;
00700         XMLRPC_REQUEST request = XMLRPC_REQUEST_FromXML(
00701                 buf,
00702                 bytes,
00703                 NULL);
00704         if(!request)
00705         {
00706                 llwarns << "XML -> SD Request process parse error." << llendl;
00707                 delete[] buf;
00708                 return STATUS_ERROR;
00709         }
00710 
00711         PUMP_DEBUG;
00712         LLBufferStream stream(channels, buffer.get());
00713         stream.precision(DEFAULT_PRECISION);
00714         const char* name = XMLRPC_RequestGetMethodName(request);
00715         stream << LLSDRPC_REQUEST_HEADER_1 << (name ? name : "")
00716                    << LLSDRPC_REQUEST_HEADER_2;
00717         XMLRPC_VALUE param = XMLRPC_RequestGetData(request);
00718         if(param)
00719         {
00720                 PUMP_DEBUG;
00721                 S32 size = XMLRPC_VectorSize(param);
00722                 if(size > 1)
00723                 {
00724                         // if there are multiple parameters, stuff the values into
00725                         // an array so that the next step in the chain can read them.
00726                         stream << "[";
00727                 }
00728                 XMLRPC_VALUE current = XMLRPC_VectorRewind(param);
00729                 bool needs_comma = false;
00730                 while(current)
00731                 {
00732                         if(needs_comma)
00733                         {
00734                                 stream << ",";
00735                         }
00736                         needs_comma = true;
00737                         stream_out(stream, current);
00738                         current = XMLRPC_VectorNext(param);
00739                 }
00740                 if(size > 1)
00741                 {
00742                         // close the array
00743                         stream << "]";
00744                 }
00745         }
00746         stream << LLSDRPC_REQUEST_FOOTER;
00747         XMLRPC_RequestFree(request, 1);
00748         delete[] buf;
00749         PUMP_DEBUG;
00750         return STATUS_DONE;
00751 }
00752 

Generated on Thu Jul 1 06:08:30 2010 for Second Life Viewer by  doxygen 1.4.7