You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
402 lines
12 KiB
402 lines
12 KiB
/*
|
|
* kxmlrpcclient.cpp - (c) 2003 Frerich Raabe <raabe@kde.org>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#include "xmlrpciface.h"
|
|
|
|
#include <kdebug.h>
|
|
#include <kio/job.h>
|
|
#include <klocale.h>
|
|
#include <kmdcodec.h>
|
|
|
|
#include <tqdom.h>
|
|
|
|
using namespace KXMLRPC;
|
|
|
|
Query *Query::create( TQObject *parent, const char *name )
|
|
{
|
|
return new Query( parent, name );
|
|
}
|
|
|
|
void Query::call( const TQString &server, const TQString &method,
|
|
const TQValueList<TQVariant> &args, const TQString &userAgent )
|
|
{
|
|
m_buffer.open( IO_ReadWrite );
|
|
m_server = server;
|
|
m_method = method;
|
|
m_args = args;
|
|
|
|
const TQString xmlMarkup = markupCall( method, args );
|
|
|
|
TQByteArray postData;
|
|
TQDataStream stream( postData, IO_WriteOnly );
|
|
stream.writeRawBytes( xmlMarkup.utf8(), xmlMarkup.length() );
|
|
|
|
KIO::TransferJob *job = KIO::http_post( KURL( server ), postData, false );
|
|
job->addMetaData( "UserAgent", userAgent );
|
|
job->addMetaData( "content-type", "Content-Type: text/xml; charset=utf-8" );
|
|
connect( job, TQT_SIGNAL( infoMessage( KIO::Job *, const TQString & ) ),
|
|
this, TQT_SLOT( slotInfoMessage( KIO::Job *, const TQString & ) ) );
|
|
connect( job, TQT_SIGNAL( data( KIO::Job *, const TQByteArray & ) ),
|
|
this, TQT_SLOT( slotData( KIO::Job *, const TQByteArray & ) ) );
|
|
connect( job, TQT_SIGNAL( result( KIO::Job * ) ),
|
|
this, TQT_SLOT( slotResult( KIO::Job * ) ) );
|
|
}
|
|
|
|
void Query::slotInfoMessage( KIO::Job *, const TQString &msg )
|
|
{
|
|
emit infoMessage( msg );
|
|
}
|
|
|
|
void Query::slotData( KIO::Job *, const TQByteArray &data )
|
|
{
|
|
m_buffer.writeBlock( data );
|
|
}
|
|
|
|
void Query::slotResult( KIO::Job *job )
|
|
{
|
|
Result response;
|
|
response.m_server = m_server;
|
|
response.m_method = m_method;
|
|
response.m_args = m_args;
|
|
|
|
response.m_success = false;
|
|
|
|
if ( job->error() != 0 ) {
|
|
response.m_errorCode = job->error();
|
|
response.m_errorString = job->errorString();
|
|
emit finished( response );
|
|
delete this;
|
|
return;
|
|
}
|
|
|
|
TQDomDocument doc;
|
|
if ( !doc.setContent( m_buffer.buffer() ) ) {
|
|
response.m_errorCode = -1;
|
|
response.m_errorString = i18n( "Received invalid XML markup" );
|
|
emit finished( response );
|
|
delete this;
|
|
return;
|
|
}
|
|
|
|
m_buffer.close();
|
|
|
|
if ( isMessageResponse( doc ) )
|
|
response = parseMessageResponse( doc );
|
|
else if ( isFaultResponse( doc ) )
|
|
response = parseFaultResponse( doc );
|
|
else {
|
|
response.m_errorCode = 1;
|
|
response.m_errorString = i18n( "Unknown type of XML markup received" );
|
|
}
|
|
|
|
// parserMessageResponse and parseFaultResponse overwrite these fields.
|
|
response.m_server = m_server;
|
|
response.m_method = m_method;
|
|
response.m_args = m_args;
|
|
|
|
emit finished( response );
|
|
delete this;
|
|
}
|
|
|
|
bool Query::isMessageResponse( const TQDomDocument &doc ) const
|
|
{
|
|
return doc.documentElement().firstChild().toElement().tagName().lower() == "params";
|
|
}
|
|
|
|
Query::Result Query::parseMessageResponse( const TQDomDocument &doc ) const
|
|
{
|
|
Result response;
|
|
response.m_success = true;
|
|
|
|
TQDomNode paramNode = doc.documentElement().firstChild().firstChild();
|
|
while ( !paramNode.isNull() ) {
|
|
response.m_data << demarshal( paramNode.firstChild().toElement() );
|
|
paramNode = paramNode.nextSibling();
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
bool Query::isFaultResponse( const TQDomDocument &doc ) const
|
|
{
|
|
return doc.documentElement().firstChild().toElement().tagName().lower() == "fault";
|
|
}
|
|
|
|
Query::Result Query::parseFaultResponse( const TQDomDocument &doc ) const
|
|
{
|
|
Result response;
|
|
response.m_success = false;
|
|
|
|
TQDomNode errorNode = doc.documentElement().firstChild().firstChild();
|
|
const TQVariant errorVariant = demarshal( errorNode.toElement() );
|
|
response.m_errorCode = errorVariant.toMap()[ "faultCode" ].toInt();
|
|
response.m_errorString = errorVariant.toMap()[ "faultString" ].toString();
|
|
|
|
return response;
|
|
}
|
|
|
|
TQString Query::markupCall( const TQString &cmd,
|
|
const TQValueList<TQVariant> &args ) const
|
|
{
|
|
TQString markup = "<?xml version='1.0' ?><methodCall>";
|
|
|
|
markup += "<methodName>" + cmd + "</methodName>";
|
|
|
|
if ( !args.isEmpty() ) {
|
|
markup += "<params>";
|
|
TQValueList<TQVariant>::ConstIterator it = args.begin();
|
|
TQValueList<TQVariant>::ConstIterator end = args.end();
|
|
for ( ; it != end; ++it )
|
|
markup += "<param>" + marshal( *it ) + "</param>";
|
|
markup += "</params>";
|
|
}
|
|
|
|
markup += "</methodCall>";
|
|
|
|
return markup;
|
|
}
|
|
|
|
TQString Query::marshal( const TQVariant &arg )
|
|
{
|
|
TQString s = "<value>";
|
|
switch ( arg.type() ) {
|
|
case TQVariant::String:
|
|
case TQVariant::CString:
|
|
s += "<string>" + arg.toString() + "</string>";
|
|
break;
|
|
case TQVariant::Int:
|
|
s += "<int>" + TQString::number( arg.toInt() ) + "</int>";
|
|
break;
|
|
case TQVariant::Double:
|
|
s += "<double>" + TQString::number( arg.toDouble() ) + "</double>";
|
|
break;
|
|
case TQVariant::Bool:
|
|
s += "<boolean>";
|
|
s += arg.toBool() ? "true" : "false";
|
|
s += "</boolean>";
|
|
break;
|
|
case TQVariant::ByteArray:
|
|
s += "<base64>" + KCodecs::base64Encode( arg.toByteArray() ) + "</base64>";
|
|
break;
|
|
case TQVariant::DateTime:
|
|
s += "<datetime.iso8601>" + arg.toDateTime().toString( Qt::ISODate ) + "</datetime.iso8601>";
|
|
break;
|
|
case TQVariant::List: {
|
|
s += "<array><data>";
|
|
const TQValueList<TQVariant> args = arg.toList();
|
|
TQValueList<TQVariant>::ConstIterator it = args.begin();
|
|
TQValueList<TQVariant>::ConstIterator end = args.end();
|
|
for ( ; it != end; ++it )
|
|
s += marshal( *it );
|
|
s += "</data></array>";
|
|
break;
|
|
}
|
|
case TQVariant::Map: {
|
|
s += "<struct>";
|
|
TQMap<TQString, TQVariant> map = arg.toMap();
|
|
TQMap<TQString, TQVariant>::ConstIterator it = map.begin();
|
|
TQMap<TQString, TQVariant>::ConstIterator end = map.end();
|
|
for ( ; it != end; ++it ) {
|
|
s += "<member>";
|
|
s += "<name>" + it.key() + "</name>";
|
|
s += marshal( it.data() );
|
|
s += "</member>";
|
|
}
|
|
s += "</struct>";
|
|
break;
|
|
}
|
|
default:
|
|
kdWarning() << "Failed to marshal unknown variant type: " << arg.type() << endl;
|
|
return "<value/>";
|
|
};
|
|
return s + "</value>";
|
|
}
|
|
|
|
TQVariant Query::demarshal( const TQDomElement &elem )
|
|
{
|
|
Q_ASSERT( elem.tagName().lower() == "value" );
|
|
|
|
if ( !elem.firstChild().isElement() )
|
|
return TQVariant( elem.text() );
|
|
|
|
const TQDomElement typeElement = elem.firstChild().toElement();
|
|
const TQString typeName = typeElement.tagName().lower();
|
|
|
|
if ( typeName == "string" )
|
|
return TQVariant( typeElement.text() );
|
|
else if ( typeName == "i4" || typeName == "int" )
|
|
return TQVariant( typeElement.text().toInt() );
|
|
else if ( typeName == "double" )
|
|
return TQVariant( typeElement.text().toDouble() );
|
|
else if ( typeName == "boolean" ) {
|
|
if ( typeElement.text().lower() == "true" || typeElement.text() == "1" )
|
|
return TQVariant( true );
|
|
else
|
|
return TQVariant( false );
|
|
} else if ( typeName == "base64" )
|
|
return TQVariant( KCodecs::base64Decode( TQCString(typeElement.text().latin1() )) );
|
|
else if ( typeName == "datetime" || typeName == "datetime.iso8601" )
|
|
return TQVariant( TQDateTime::fromString( typeElement.text(), Qt::ISODate ) );
|
|
else if ( typeName == "array" ) {
|
|
TQValueList<TQVariant> values;
|
|
TQDomNode valueNode = typeElement.firstChild().firstChild();
|
|
while ( !valueNode.isNull() ) {
|
|
values << demarshal( valueNode.toElement() );
|
|
valueNode = valueNode.nextSibling();
|
|
}
|
|
return TQVariant( values );
|
|
} else if ( typeName == "struct" ) {
|
|
TQMap<TQString, TQVariant> map;
|
|
TQDomNode memberNode = typeElement.firstChild();
|
|
while ( !memberNode.isNull() ) {
|
|
const TQString key = memberNode.toElement().elementsByTagName( "name" ).item( 0 ).toElement().text();
|
|
const TQVariant data = demarshal( memberNode.toElement().elementsByTagName( "value" ).item( 0 ).toElement() );
|
|
map[ key ] = data;
|
|
memberNode = memberNode.nextSibling();
|
|
}
|
|
return TQVariant( map );
|
|
} else
|
|
kdWarning() << "Cannot demarshal unknown type " << typeName << endl;
|
|
|
|
return TQVariant();
|
|
}
|
|
|
|
Query::Query( TQObject *parent, const char *name ) : TQObject( parent, name )
|
|
{
|
|
}
|
|
|
|
TQValueList<TQVariant> Server::toVariantList( const TQVariant &arg )
|
|
{
|
|
TQValueList<TQVariant> args;
|
|
args << arg ;
|
|
return args;
|
|
}
|
|
|
|
TQValueList<TQVariant> Server::toVariantList( int arg )
|
|
{
|
|
TQValueList<TQVariant> args;
|
|
args << arg ;
|
|
return args;
|
|
}
|
|
|
|
TQValueList<TQVariant> Server::toVariantList( bool arg )
|
|
{
|
|
TQValueList<TQVariant> args;
|
|
args << arg ;
|
|
return args;
|
|
}
|
|
|
|
TQValueList<TQVariant> Server::toVariantList( double arg )
|
|
{
|
|
TQValueList<TQVariant> args;
|
|
args << arg ;
|
|
return args;
|
|
}
|
|
|
|
TQValueList<TQVariant> Server::toVariantList( const TQString &arg )
|
|
{
|
|
TQValueList<TQVariant> args;
|
|
args << arg ;
|
|
return args;
|
|
}
|
|
|
|
TQValueList<TQVariant> Server::toVariantList( const TQCString &arg )
|
|
{
|
|
TQValueList<TQVariant> args;
|
|
args << arg ;
|
|
return args;
|
|
}
|
|
|
|
TQValueList<TQVariant> Server::toVariantList( const TQByteArray &arg )
|
|
{
|
|
TQValueList<TQVariant> args;
|
|
args << arg ;
|
|
return args;
|
|
}
|
|
|
|
TQValueList<TQVariant> Server::toVariantList( const TQDateTime &arg )
|
|
{
|
|
TQValueList<TQVariant> args;
|
|
args << arg ;
|
|
return args;
|
|
}
|
|
|
|
TQValueList<TQVariant> Server::toVariantList( const TQStringList &arg )
|
|
{
|
|
TQValueList<TQVariant> args;
|
|
TQStringList::ConstIterator it = arg.begin();
|
|
TQStringList::ConstIterator end = arg.end();
|
|
for ( ; it != end; ++it )
|
|
args << TQVariant( *it );
|
|
return args;
|
|
}
|
|
|
|
Server::Server( const KURL &url, TQObject *parent, const char *name )
|
|
: TQObject( parent, name )
|
|
{
|
|
if ( url.isValid() )
|
|
m_url = url;
|
|
}
|
|
|
|
void Server::setUrl( const KURL &url )
|
|
{
|
|
m_url = url.isValid() ? url : KURL();
|
|
}
|
|
|
|
void Server::call( const TQString &method, const TQValueList<TQVariant> &args,
|
|
TQObject *receiver, const char *slot )
|
|
{
|
|
if ( m_url.isEmpty() ) {
|
|
kdWarning() << "Cannot execute call to " << method << ": empty server URL" << endl;
|
|
return;
|
|
}
|
|
|
|
Query *query = Query::create( this );
|
|
connect( query, TQT_SIGNAL( infoMessage( const TQString & ) ),
|
|
this, TQT_SIGNAL( infoMessage( const TQString & ) ) );
|
|
connect( query, TQT_SIGNAL( finished( const KXMLRPC::Query::Result & ) ),
|
|
receiver, slot );
|
|
query->call( m_url.url(), method, args, m_userAgent );
|
|
}
|
|
|
|
void Server::call( const TQString &method, const TQValueList<TQVariant> &args,
|
|
TQObject *receiver, const char *slot,
|
|
TQObject *infoObject, const char *infoSlot )
|
|
{
|
|
if ( m_url.isEmpty() ) {
|
|
kdWarning() << "Cannot execute call to " << method << ": empty server URL" << endl;
|
|
return;
|
|
}
|
|
|
|
Query *query = Query::create( this );
|
|
connect( query, TQT_SIGNAL( infoMessage( const TQString &msg ) ),
|
|
infoObject, infoSlot );
|
|
connect( query, TQT_SIGNAL( finished( const KXMLRPC::Query::Result & ) ),
|
|
receiver, slot );
|
|
query->call( m_url.url(), method, args, m_userAgent );
|
|
}
|
|
|
|
#include "xmlrpciface.moc"
|
|
// vim:ts=4:sw=4:noet
|