PageRenderTime 84ms CodeModel.GetById 15ms app.highlight 63ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llmessage/llfiltersd2xmlrpc.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 777 lines | 690 code | 9 blank | 78 comment | 2 complexity | fb70d2815b9e772f10fbb3b6cc057b82 MD5 | raw file
  1/** 
  2 * @file llfiltersd2xmlrpc.cpp
  3 * @author Phoenix
  4 * @date 2005-04-26
  5 *
  6 * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  7 * Second Life Viewer Source Code
  8 * Copyright (C) 2010, Linden Research, Inc.
  9 * 
 10 * This library is free software; you can redistribute it and/or
 11 * modify it under the terms of the GNU Lesser General Public
 12 * License as published by the Free Software Foundation;
 13 * version 2.1 of the License only.
 14 * 
 15 * This library is distributed in the hope that it will be useful,
 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 18 * Lesser General Public License for more details.
 19 * 
 20 * You should have received a copy of the GNU Lesser General Public
 21 * License along with this library; if not, write to the Free Software
 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 23 * 
 24 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 25 * $/LicenseInfo$
 26 */
 27
 28/** 
 29 * xml rpc request: 
 30 * <code>
 31 * <?xml version="1.0"?>
 32 * <methodCall><methodName>examples.getStateName</methodName>
 33 * <params><param><value><i4>41</i4></value></param></params>
 34 * </methodCall>
 35 * </code>
 36 *
 37 * xml rpc response:
 38 * <code>
 39 * <?xml version="1.0"?>
 40 * <methodResponse>
 41 * <params><param><value><string>South Dakota</string></value></param></params>
 42 * </methodResponse>
 43 * </code>
 44 *
 45 * xml rpc fault:
 46 * </code>
 47 * <?xml version="1.0"?>
 48 * <methodResponse>
 49 * <fault><value><struct>
 50 * <member><name>faultCode</name><value><int>4</int></value></member>
 51 * <member><name>faultString</name><value><string>...</string></value></member>
 52 * </struct></value></fault>
 53 * </methodResponse>
 54 * </code>
 55 *
 56 * llsd rpc request:
 57 * <code>
 58 * { 'method':'...', 'parameter':...]}
 59 * </code>
 60 * 
 61 * llsd rpc response:
 62 * <code>
 63 * { 'response':... }
 64 * </code>
 65 * 
 66 * llsd rpc fault: 
 67 * <code>
 68 * { 'fault': {'code':i..., 'description':'...'} }
 69 * </code>
 70 * 
 71 */
 72
 73#include "linden_common.h"
 74#include "llfiltersd2xmlrpc.h"
 75
 76#include <sstream>
 77#include <iterator>
 78#include <xmlrpc-epi/xmlrpc.h>
 79#include "apr_base64.h"
 80
 81#include "llbuffer.h"
 82#include "llbufferstream.h"
 83#include "llmemorystream.h"
 84#include "llsd.h"
 85#include "llsdserialize.h"
 86#include "lluuid.h"
 87
 88// spammy mode
 89//#define LL_SPEW_STREAM_OUT_DEBUGGING 1
 90
 91/**
 92 * String constants
 93 */
 94static const char XML_HEADER[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
 95static const char XMLRPC_REQUEST_HEADER_1[] = "<methodCall><methodName>";
 96static const char XMLRPC_REQUEST_HEADER_2[] = "</methodName><params>";
 97static const char XMLRPC_REQUEST_FOOTER[] = "</params></methodCall>";
 98static const char XMLRPC_METHOD_RESPONSE_HEADER[] = "<methodResponse>";
 99static const char XMLRPC_METHOD_RESPONSE_FOOTER[] = "</methodResponse>";
100static const char XMLRPC_RESPONSE_HEADER[] = "<params><param>";
101static const char XMLRPC_RESPONSE_FOOTER[] = "</param></params>";
102static const char XMLRPC_FAULT_1[] = "<fault><value><struct><member><name>faultCode</name><value><int>";
103static const char XMLRPC_FAULT_2[] = "</int></value></member><member><name>faultString</name><value><string>";
104static const char XMLRPC_FAULT_3[] = "</string></value></member></struct></value></fault>";
105static const char LLSDRPC_RESPONSE_HEADER[] = "{'response':";
106static const char LLSDRPC_RESPONSE_FOOTER[] = "}";
107const char LLSDRPC_REQUEST_HEADER_1[] = "{'method':'";
108const char LLSDRPC_REQUEST_HEADER_2[] = "', 'parameter': ";
109const char LLSDRPC_REQUEST_FOOTER[] = "}";
110static const char LLSDRPC_FAULT_HADER_1[] = "{ 'fault': {'code':i";
111static const char LLSDRPC_FAULT_HADER_2[] = ", 'description':";
112static const char LLSDRPC_FAULT_FOOTER[] = "} }";
113static const S32 DEFAULT_PRECISION = 20;
114
115/**
116 * LLFilterSD2XMLRPC
117 */
118LLFilterSD2XMLRPC::LLFilterSD2XMLRPC()
119{
120}
121
122LLFilterSD2XMLRPC::~LLFilterSD2XMLRPC()
123{
124}
125
126std::string xml_escape_string(const std::string& in)
127{
128	std::ostringstream out;
129	std::string::const_iterator it = in.begin();
130	std::string::const_iterator end = in.end();
131	for(; it != end; ++it)
132	{
133		switch((*it))
134		{
135		case '<':
136			out << "&lt;";
137			break;
138		case '>':
139			out << "&gt;";
140			break;
141		case '&':
142			out << "&amp;";
143			break;
144		case '\'':
145			out << "&apos;";
146			break;
147		case '"':
148			out << "&quot;";
149			break;
150		default:
151			out << (*it);
152			break;
153		}
154	}
155	return out.str();
156}
157
158void LLFilterSD2XMLRPC::streamOut(std::ostream& ostr, const LLSD& sd)
159{
160	ostr << "<value>";
161	switch(sd.type())
162	{
163	case LLSD::TypeMap:
164	{
165#if LL_SPEW_STREAM_OUT_DEBUGGING
166		llinfos << "streamOut(map) BEGIN" << llendl;
167#endif
168		ostr << "<struct>";
169		if(ostr.fail())
170		{
171			llinfos << "STREAM FAILURE writing struct" << llendl;
172		}
173		LLSD::map_const_iterator it = sd.beginMap();
174		LLSD::map_const_iterator end = sd.endMap();
175		for(; it != end; ++it)
176		{
177			ostr << "<member><name>" << xml_escape_string((*it).first)
178				<< "</name>";
179			streamOut(ostr, (*it).second);
180			if(ostr.fail())
181			{
182				llinfos << "STREAM FAILURE writing '" << (*it).first
183						<< "' with sd type " << (*it).second.type() << llendl;
184			}
185			ostr << "</member>";
186		}
187		ostr << "</struct>";
188#if LL_SPEW_STREAM_OUT_DEBUGGING
189		llinfos << "streamOut(map) END" << llendl;
190#endif
191		break;
192	}
193	case LLSD::TypeArray:
194	{
195#if LL_SPEW_STREAM_OUT_DEBUGGING
196		llinfos << "streamOut(array) BEGIN" << llendl;
197#endif
198		ostr << "<array><data>";
199		LLSD::array_const_iterator it = sd.beginArray();
200		LLSD::array_const_iterator end = sd.endArray();
201		for(; it != end; ++it)
202		{
203			streamOut(ostr, *it);
204			if(ostr.fail())
205			{
206				llinfos << "STREAM FAILURE writing array element sd type "
207						<< (*it).type() << llendl;
208			}
209		}
210#if LL_SPEW_STREAM_OUT_DEBUGGING
211		llinfos << "streamOut(array) END" << llendl;
212#endif
213		ostr << "</data></array>";
214		break;
215	}
216	case LLSD::TypeUndefined:
217		// treat undefined as a bool with a false value.
218	case LLSD::TypeBoolean:
219#if LL_SPEW_STREAM_OUT_DEBUGGING
220		llinfos << "streamOut(bool)" << llendl;
221#endif
222		ostr << "<boolean>" << (sd.asBoolean() ? "1" : "0") << "</boolean>";
223		break;
224	case LLSD::TypeInteger:
225#if LL_SPEW_STREAM_OUT_DEBUGGING
226		llinfos << "streamOut(int)" << llendl;
227#endif
228		ostr << "<i4>" << sd.asInteger() << "</i4>";
229		break;
230	case LLSD::TypeReal:
231#if LL_SPEW_STREAM_OUT_DEBUGGING
232		llinfos << "streamOut(real)" << llendl;
233#endif
234		ostr << "<double>" << sd.asReal() << "</double>";
235		break;
236	case LLSD::TypeString:
237#if LL_SPEW_STREAM_OUT_DEBUGGING
238		llinfos << "streamOut(string)" << llendl;
239#endif
240		ostr << "<string>" << xml_escape_string(sd.asString()) << "</string>";
241		break;
242	case LLSD::TypeUUID:
243#if LL_SPEW_STREAM_OUT_DEBUGGING
244		llinfos << "streamOut(uuid)" << llendl;
245#endif
246		// serialize it as a string
247		ostr << "<string>" << sd.asString() << "</string>";
248		break;
249	case LLSD::TypeURI:
250	{
251#if LL_SPEW_STREAM_OUT_DEBUGGING
252		llinfos << "streamOut(uri)" << llendl;
253#endif
254		// serialize it as a string
255		ostr << "<string>" << xml_escape_string(sd.asString()) << "</string>";
256		break;
257	}
258	case LLSD::TypeBinary:
259	{
260#if LL_SPEW_STREAM_OUT_DEBUGGING
261		llinfos << "streamOut(binary)" << llendl;
262#endif
263		// this is pretty inefficient, but we'll deal with that
264		// problem when it becomes one.
265		ostr << "<base64>";
266		LLSD::Binary buffer = sd.asBinary();
267		if(!buffer.empty())
268		{
269			// *TODO: convert to LLBase64
270			int b64_buffer_length = apr_base64_encode_len(buffer.size());
271			char* b64_buffer = new char[b64_buffer_length];
272			b64_buffer_length = apr_base64_encode_binary(
273				b64_buffer,
274				&buffer[0],
275				buffer.size());
276			ostr.write(b64_buffer, b64_buffer_length - 1);
277			delete[] b64_buffer;
278		}
279		ostr << "</base64>";
280		break;
281	}
282	case LLSD::TypeDate:
283#if LL_SPEW_STREAM_OUT_DEBUGGING
284		llinfos << "streamOut(date)" << llendl;
285#endif
286		// no need to escape this since it will be alpha-numeric.
287		ostr << "<dateTime.iso8601>" << sd.asString() << "</dateTime.iso8601>";
288		break;
289	default:
290		// unhandled type
291		llwarns << "Unhandled structured data type: " << sd.type()
292			<< llendl;
293		break;
294	}
295	ostr << "</value>";
296}
297
298/**
299 * LLFilterSD2XMLRPCResponse
300 */
301
302LLFilterSD2XMLRPCResponse::LLFilterSD2XMLRPCResponse()
303{
304}
305
306LLFilterSD2XMLRPCResponse::~LLFilterSD2XMLRPCResponse()
307{
308}
309
310
311static LLFastTimer::DeclareTimer FTM_PROCESS_SD2XMLRPC_RESPONSE("SD2XMLRPC Response");
312// virtual
313LLIOPipe::EStatus LLFilterSD2XMLRPCResponse::process_impl(
314	const LLChannelDescriptors& channels,
315	buffer_ptr_t& buffer,
316	bool& eos,
317	LLSD& context,
318	LLPumpIO* pump)
319{
320	LLFastTimer t(FTM_PROCESS_SD2XMLRPC_RESPONSE);
321
322	PUMP_DEBUG;
323	// This pipe does not work if it does not have everyting. This
324	// could be addressed by making a stream parser for llsd which
325	// handled partial information.
326	if(!eos)
327	{
328		return STATUS_BREAK;
329	}
330
331	PUMP_DEBUG;
332	// we have everyting in the buffer, so turn the structure data rpc
333	// response into an xml rpc response.
334	LLBufferStream stream(channels, buffer.get());
335	stream << XML_HEADER << XMLRPC_METHOD_RESPONSE_HEADER;
336	LLSD sd;
337	LLSDSerialize::fromNotation(sd, stream, buffer->count(channels.in()));
338
339	PUMP_DEBUG;
340	LLIOPipe::EStatus rv = STATUS_ERROR;
341	if(sd.has("response"))
342	{
343		PUMP_DEBUG;
344		// it is a normal response. pack it up and ship it out.
345		stream.precision(DEFAULT_PRECISION);
346		stream << XMLRPC_RESPONSE_HEADER;
347		streamOut(stream, sd["response"]);
348		stream << XMLRPC_RESPONSE_FOOTER << XMLRPC_METHOD_RESPONSE_FOOTER;
349		rv = STATUS_DONE;
350	}
351	else if(sd.has("fault"))
352	{
353		PUMP_DEBUG;
354		// it is a fault.
355		stream << XMLRPC_FAULT_1 << sd["fault"]["code"].asInteger()
356			<< XMLRPC_FAULT_2
357			<< xml_escape_string(sd["fault"]["description"].asString())
358			<< XMLRPC_FAULT_3 << XMLRPC_METHOD_RESPONSE_FOOTER;
359		rv = STATUS_DONE;
360	}
361	else
362	{
363		llwarns << "Unable to determine the type of LLSD response." << llendl;
364	}
365	PUMP_DEBUG;
366	return rv;
367}
368
369/**
370 * LLFilterSD2XMLRPCRequest
371 */
372LLFilterSD2XMLRPCRequest::LLFilterSD2XMLRPCRequest()
373{
374}
375
376LLFilterSD2XMLRPCRequest::LLFilterSD2XMLRPCRequest(const char* method)
377{
378	if(method)
379	{
380		mMethod.assign(method);
381	}
382}
383
384LLFilterSD2XMLRPCRequest::~LLFilterSD2XMLRPCRequest()
385{
386}
387
388static LLFastTimer::DeclareTimer FTM_PROCESS_SD2XMLRPC_REQUEST("S22XMLRPC Request");
389
390// virtual
391LLIOPipe::EStatus LLFilterSD2XMLRPCRequest::process_impl(
392	const LLChannelDescriptors& channels,
393	buffer_ptr_t& buffer,
394	bool& eos,
395	LLSD& context,
396	LLPumpIO* pump)
397{
398	LLFastTimer t(FTM_PROCESS_SD2XMLRPC_REQUEST);
399	// This pipe does not work if it does not have everyting. This
400	// could be addressed by making a stream parser for llsd which
401	// handled partial information.
402	PUMP_DEBUG;
403	if(!eos)
404	{
405		llinfos << "!eos" << llendl;
406		return STATUS_BREAK;
407	}
408
409	// See if we can parse it
410	LLBufferStream stream(channels, buffer.get());
411	LLSD sd;
412	LLSDSerialize::fromNotation(sd, stream, buffer->count(channels.in()));
413	if(stream.fail())
414	{
415		llinfos << "STREAM FAILURE reading structure data." << llendl;
416	}
417
418	PUMP_DEBUG;
419	// We can get the method and parameters from either the member
420	// function or passed in via the buffer. We prefer the buffer if
421	// we found a parameter and a method, or fall back to using
422	// mMethod and putting everyting in the buffer into the parameter.
423	std::string method;
424	LLSD param_sd;
425	if(sd.has("method") && sd.has("parameter"))
426	{
427		method = sd["method"].asString();
428		param_sd = sd["parameter"];
429	}
430	else
431	{
432		method = mMethod;
433		param_sd = sd;
434	}
435	if(method.empty())
436	{
437		llwarns << "SD -> XML Request no method found." << llendl;
438		return STATUS_ERROR;
439	}
440
441	PUMP_DEBUG;
442	// We have a method, and some kind of parameter, so package it up
443	// and send it out.
444	LLBufferStream ostream(channels, buffer.get());
445	ostream.precision(DEFAULT_PRECISION);
446	if(ostream.fail())
447	{
448		llinfos << "STREAM FAILURE setting precision" << llendl;
449	}
450	ostream << XML_HEADER << XMLRPC_REQUEST_HEADER_1
451		<< xml_escape_string(method) << XMLRPC_REQUEST_HEADER_2;
452	if(ostream.fail())
453	{
454		llinfos << "STREAM FAILURE writing method headers" << llendl;
455	}
456	switch(param_sd.type())
457	{
458	case LLSD::TypeMap:
459		// If the params are a map, then we do not want to iterate
460		// through them since the iterators returned will be map
461		// ordered un-named values, which will lose the names, and
462		// only stream the values, turning it into an array.
463		ostream << "<param>";
464		streamOut(ostream, param_sd);
465		ostream << "</param>";
466		break;
467	case LLSD::TypeArray:
468	{
469
470		LLSD::array_iterator it = param_sd.beginArray();
471		LLSD::array_iterator end = param_sd.endArray();
472		for(; it != end; ++it)
473		{
474			ostream << "<param>";
475			streamOut(ostream, *it);
476			ostream << "</param>";
477		}
478		break;
479	}
480	default:
481		ostream << "<param>";
482		streamOut(ostream, param_sd);
483		ostream << "</param>";
484		break;
485	}
486
487	stream << XMLRPC_REQUEST_FOOTER;
488	return STATUS_DONE;
489}
490
491/**
492 * LLFilterXMLRPCResponse2LLSD
493 */
494// this is a c function here since it's really an implementation
495// detail that requires a header file just get the definition of the
496// parameters.
497LLIOPipe::EStatus stream_out(std::ostream& ostr, XMLRPC_VALUE value)
498{
499	XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(value);
500	LLIOPipe::EStatus status = LLIOPipe::STATUS_OK;
501	switch(type)
502	{
503	case xmlrpc_type_base64:
504	{
505		S32 len = XMLRPC_GetValueStringLen(value);
506		const char* buf = XMLRPC_GetValueBase64(value);
507		ostr << " b(";
508		if((len > 0) && buf)
509		{
510			ostr << len << ")\"";
511			ostr.write(buf, len);
512			ostr << "\"";
513		}
514		else
515		{
516			ostr << "0)\"\"";
517		}
518		break;
519	}
520	case xmlrpc_type_boolean:
521		//lldebugs << "stream_out() bool" << llendl;
522		ostr << " " << (XMLRPC_GetValueBoolean(value) ? "true" : "false");
523		break;
524	case xmlrpc_type_datetime:
525		ostr << " d\"" << XMLRPC_GetValueDateTime_ISO8601(value) << "\"";
526		break;
527	case xmlrpc_type_double:
528		ostr << " r" << XMLRPC_GetValueDouble(value);
529		//lldebugs << "stream_out() double" << XMLRPC_GetValueDouble(value)
530		//		 << llendl;
531		break;
532	case xmlrpc_type_int:
533		ostr << " i" << XMLRPC_GetValueInt(value);
534		//lldebugs << "stream_out() integer:" << XMLRPC_GetValueInt(value)
535		//		 << llendl;
536		break;
537	case xmlrpc_type_string:
538		//lldebugs << "stream_out() string: " << str << llendl;
539		ostr << " s(" << XMLRPC_GetValueStringLen(value) << ")'"
540			<< XMLRPC_GetValueString(value) << "'";
541		break;
542	case xmlrpc_type_array: // vector
543	case xmlrpc_type_mixed: // vector
544	{
545		//lldebugs << "stream_out() array" << llendl;
546		ostr << " [";
547		U32 needs_comma = 0;
548		XMLRPC_VALUE current = XMLRPC_VectorRewind(value);
549		while(current && (LLIOPipe::STATUS_OK == status))
550		{
551			if(needs_comma++) ostr << ",";
552			status = stream_out(ostr, current);
553			current = XMLRPC_VectorNext(value);
554		}
555		ostr << "]";
556		break;
557	}
558	case xmlrpc_type_struct: // still vector
559	{
560		//lldebugs << "stream_out() struct" << llendl;
561		ostr << " {";
562		std::string name;
563		U32 needs_comma = 0;
564		XMLRPC_VALUE current = XMLRPC_VectorRewind(value);
565		while(current && (LLIOPipe::STATUS_OK == status))
566		{
567			if(needs_comma++) ostr << ",";
568			name.assign(XMLRPC_GetValueID(current));
569			ostr << "'" << LLSDNotationFormatter::escapeString(name) << "':";
570			status = stream_out(ostr, current);
571			current = XMLRPC_VectorNext(value);
572		}
573		ostr << "}";
574		break;
575	}
576	case xmlrpc_type_empty:
577	case xmlrpc_type_none:
578	default:
579		status = LLIOPipe::STATUS_ERROR;
580		llwarns << "Found an empty xmlrpc type.." << llendl;
581		// not much we can do here...
582		break;
583	};
584	return status;
585}
586
587LLFilterXMLRPCResponse2LLSD::LLFilterXMLRPCResponse2LLSD()
588{
589}
590
591LLFilterXMLRPCResponse2LLSD::~LLFilterXMLRPCResponse2LLSD()
592{
593}
594
595static LLFastTimer::DeclareTimer FTM_PROCESS_XMLRPC2LLSD_RESPONSE("XMLRPC2LLSD Response");
596
597LLIOPipe::EStatus LLFilterXMLRPCResponse2LLSD::process_impl(
598	const LLChannelDescriptors& channels,
599	buffer_ptr_t& buffer,
600	bool& eos,
601	LLSD& context,
602	LLPumpIO* pump)
603{
604	LLFastTimer t(FTM_PROCESS_XMLRPC2LLSD_RESPONSE);
605
606	PUMP_DEBUG;
607	if(!eos) return STATUS_BREAK;
608	if(!buffer) return STATUS_ERROR;
609
610	PUMP_DEBUG;
611	// *FIX: This technique for reading data is far from optimal. We
612	// need to have some kind of istream interface into the xml
613	// parser...
614	S32 bytes = buffer->countAfter(channels.in(), NULL);
615	if(!bytes) return STATUS_ERROR;
616	char* buf = new char[bytes + 1];
617	buf[bytes] = '\0';
618	buffer->readAfter(channels.in(), NULL, (U8*)buf, bytes);
619
620	//lldebugs << "xmlrpc response: " << buf << llendl;
621
622	PUMP_DEBUG;
623	XMLRPC_REQUEST response = XMLRPC_REQUEST_FromXML(
624		buf,
625		bytes,
626		NULL);
627	if(!response)
628	{
629		llwarns << "XML -> SD Response unable to parse xml." << llendl;
630		delete[] buf;
631		return STATUS_ERROR;
632	}
633
634	PUMP_DEBUG;
635	LLBufferStream stream(channels, buffer.get());
636	stream.precision(DEFAULT_PRECISION);
637	if(XMLRPC_ResponseIsFault(response))
638	{
639		PUMP_DEBUG;
640		stream << LLSDRPC_FAULT_HADER_1
641			   << XMLRPC_GetResponseFaultCode(response)
642			   << LLSDRPC_FAULT_HADER_2;
643		const char* fault_str = XMLRPC_GetResponseFaultString(response);
644		std::string fault_string;
645		if(fault_str)
646		{
647			fault_string.assign(fault_str);
648		}
649		stream << "'" << LLSDNotationFormatter::escapeString(fault_string)
650		   << "'" <<LLSDRPC_FAULT_FOOTER;
651	}
652	else
653	{
654		PUMP_DEBUG;
655		stream << LLSDRPC_RESPONSE_HEADER;
656		XMLRPC_VALUE param = XMLRPC_RequestGetData(response);
657		if(param)
658		{
659			stream_out(stream, param);
660		}
661		stream << LLSDRPC_RESPONSE_FOOTER;
662	}
663	PUMP_DEBUG;
664	XMLRPC_RequestFree(response, 1);
665	delete[] buf;
666	PUMP_DEBUG;
667	return STATUS_DONE;
668}
669
670/**
671 * LLFilterXMLRPCRequest2LLSD
672 */
673LLFilterXMLRPCRequest2LLSD::LLFilterXMLRPCRequest2LLSD()
674{
675}
676
677LLFilterXMLRPCRequest2LLSD::~LLFilterXMLRPCRequest2LLSD()
678{
679}
680
681static LLFastTimer::DeclareTimer FTM_PROCESS_XMLRPC2LLSD_REQUEST("XMLRPC2LLSD Request");
682LLIOPipe::EStatus LLFilterXMLRPCRequest2LLSD::process_impl(
683	const LLChannelDescriptors& channels,
684	buffer_ptr_t& buffer,
685	bool& eos,
686	LLSD& context,
687	LLPumpIO* pump)
688{
689	LLFastTimer t(FTM_PROCESS_XMLRPC2LLSD_REQUEST);
690	PUMP_DEBUG;
691	if(!eos) return STATUS_BREAK;
692	if(!buffer) return STATUS_ERROR;
693
694	PUMP_DEBUG;
695	// *FIX: This technique for reading data is far from optimal. We
696	// need to have some kind of istream interface into the xml
697	// parser...
698	S32 bytes = buffer->countAfter(channels.in(), NULL);
699	if(!bytes) return STATUS_ERROR;
700	char* buf = new char[bytes + 1];
701	buf[bytes] = '\0';
702	buffer->readAfter(channels.in(), NULL, (U8*)buf, bytes);
703
704	//lldebugs << "xmlrpc request: " << buf << llendl;
705	
706	// Check the value in the buffer. XMLRPC_REQUEST_FromXML will report a error code 4 if 
707	// values that are less than 0x20 are passed to it, except
708	// 0x09: Horizontal tab; 0x0a: New Line; 0x0d: Carriage
709	U8* cur_pBuf = (U8*)buf;
710    U8 cur_char;
711	for (S32 i=0; i<bytes; i++) 
712	{
713        cur_char = *cur_pBuf;
714		if (   cur_char < 0x20
715            && 0x09 != cur_char
716            && 0x0a != cur_char
717            && 0x0d != cur_char )
718        {
719			*cur_pBuf = '?';
720        }
721		++cur_pBuf;
722	}
723
724	PUMP_DEBUG;
725	XMLRPC_REQUEST request = XMLRPC_REQUEST_FromXML(
726		buf,
727		bytes,
728		NULL);
729	if(!request)
730	{
731		llwarns << "XML -> SD Request process parse error." << llendl;
732		delete[] buf;
733		return STATUS_ERROR;
734	}
735
736	PUMP_DEBUG;
737	LLBufferStream stream(channels, buffer.get());
738	stream.precision(DEFAULT_PRECISION);
739	const char* name = XMLRPC_RequestGetMethodName(request);
740	stream << LLSDRPC_REQUEST_HEADER_1 << (name ? name : "")
741		   << LLSDRPC_REQUEST_HEADER_2;
742	XMLRPC_VALUE param = XMLRPC_RequestGetData(request);
743	if(param)
744	{
745		PUMP_DEBUG;
746		S32 size = XMLRPC_VectorSize(param);
747		if(size > 1)
748		{
749			// if there are multiple parameters, stuff the values into
750			// an array so that the next step in the chain can read them.
751			stream << "[";
752		}
753 		XMLRPC_VALUE current = XMLRPC_VectorRewind(param);
754		bool needs_comma = false;
755 		while(current)
756 		{
757			if(needs_comma)
758			{
759				stream << ",";
760			}
761			needs_comma = true;
762 			stream_out(stream, current);
763 			current = XMLRPC_VectorNext(param);
764 		}
765		if(size > 1)
766		{
767			// close the array
768			stream << "]";
769		}
770	}
771	stream << LLSDRPC_REQUEST_FOOTER;
772	XMLRPC_RequestFree(request, 1);
773	delete[] buf;
774	PUMP_DEBUG;
775	return STATUS_DONE;
776}
777