/* * kxmlrpcclient.cpp - (c) 2003 Frerich Raabe * * 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 #include #include #include #include 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 &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() ); TDEIO::TransferJob *job = TDEIO::http_post( KURL( server ), postData, false ); job->addMetaData( "UserAgent", userAgent ); job->addMetaData( "content-type", "Content-Type: text/xml; charset=utf-8" ); connect( job, TQ_SIGNAL( infoMessage( TDEIO::Job *, const TQString & ) ), this, TQ_SLOT( slotInfoMessage( TDEIO::Job *, const TQString & ) ) ); connect( job, TQ_SIGNAL( data( TDEIO::Job *, const TQByteArray & ) ), this, TQ_SLOT( slotData( TDEIO::Job *, const TQByteArray & ) ) ); connect( job, TQ_SIGNAL( result( TDEIO::Job * ) ), this, TQ_SLOT( slotResult( TDEIO::Job * ) ) ); } void Query::slotInfoMessage( TDEIO::Job *, const TQString &msg ) { emit infoMessage( msg ); } void Query::slotData( TDEIO::Job *, const TQByteArray &data ) { m_buffer.writeBlock( data ); } void Query::slotResult( TDEIO::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 &args ) const { TQString markup = ""; markup += "" + cmd + ""; if ( !args.isEmpty() ) { markup += ""; TQValueList::ConstIterator it = args.begin(); TQValueList::ConstIterator end = args.end(); for ( ; it != end; ++it ) markup += "" + marshal( *it ) + ""; markup += ""; } markup += ""; return markup; } TQString Query::marshal( const TQVariant &arg ) { TQString s = ""; switch ( arg.type() ) { case TQVariant::String: case TQVariant::CString: s += "" + arg.toString() + ""; break; case TQVariant::Int: s += "" + TQString::number( arg.toInt() ) + ""; break; case TQVariant::Double: s += "" + TQString::number( arg.toDouble() ) + ""; break; case TQVariant::Bool: s += ""; s += arg.toBool() ? "true" : "false"; s += ""; break; case TQVariant::ByteArray: s += "" + KCodecs::base64Encode( arg.toByteArray() ) + ""; break; case TQVariant::DateTime: s += "" + arg.toDateTime().toString( TQt::ISODate ) + ""; break; case TQVariant::List: { s += ""; const TQValueList args = arg.toList(); TQValueList::ConstIterator it = args.begin(); TQValueList::ConstIterator end = args.end(); for ( ; it != end; ++it ) s += marshal( *it ); s += ""; break; } case TQVariant::Map: { s += ""; TQStringVariantMap map = arg.toMap(); TQStringVariantMap::ConstIterator it = map.begin(); TQStringVariantMap::ConstIterator end = map.end(); for ( ; it != end; ++it ) { s += ""; s += "" + it.key() + ""; s += marshal( it.data() ); s += ""; } s += ""; break; } default: kdWarning() << "Failed to marshal unknown variant type: " << arg.type() << endl; return ""; }; return s + ""; } 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(), TQt::ISODate ) ); else if ( typeName == "array" ) { TQValueList values; TQDomNode valueNode = typeElement.firstChild().firstChild(); while ( !valueNode.isNull() ) { values << demarshal( valueNode.toElement() ); valueNode = valueNode.nextSibling(); } return TQVariant( values ); } else if ( typeName == "struct" ) { TQStringVariantMap 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 Server::toVariantList( const TQVariant &arg ) { TQValueList args; args << arg ; return args; } TQValueList Server::toVariantList( int arg ) { TQValueList args; args << arg ; return args; } TQValueList Server::toVariantList( bool arg ) { TQValueList args; args << arg ; return args; } TQValueList Server::toVariantList( double arg ) { TQValueList args; args << arg ; return args; } TQValueList Server::toVariantList( const TQString &arg ) { TQValueList args; args << arg ; return args; } TQValueList Server::toVariantList( const TQCString &arg ) { TQValueList args; args << arg ; return args; } TQValueList Server::toVariantList( const TQByteArray &arg ) { TQValueList args; args << arg ; return args; } TQValueList Server::toVariantList( const TQDateTime &arg ) { TQValueList args; args << arg ; return args; } TQValueList Server::toVariantList( const TQStringList &arg ) { TQValueList 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 &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, TQ_SIGNAL( infoMessage( const TQString & ) ), this, TQ_SIGNAL( infoMessage( const TQString & ) ) ); connect( query, TQ_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 &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, TQ_SIGNAL( infoMessage( const TQString &msg ) ), infoObject, infoSlot ); connect( query, TQ_SIGNAL( finished( const KXMLRPC::Query::Result & ) ), receiver, slot ); query->call( m_url.url(), method, args, m_userAgent ); } #include "xmlrpciface.moc"