mirror of
https://bitbucket.org/smil3y/kde-playground.git
synced 2025-02-23 10:22:50 +00:00
451 lines
13 KiB
C++
451 lines
13 KiB
C++
/******************************************************************************
|
|
* Copyright (C) 2003 - 2004 by Frerich Raabe <raabe@kde.org> *
|
|
* Tobias Koenig <tokoe@kde.org> *
|
|
* Copyright (C) 2006 by Narayan Newton <narayannewton@gmail.com> *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution *
|
|
* details, check the accompanying file 'COPYING.BSD'. *
|
|
*****************************************************************************/
|
|
/**
|
|
@file
|
|
|
|
This file is part of KXmlRpc and defines our internal classes.
|
|
|
|
@author Frerich Raabe <raabe@kde.org>
|
|
@author Tobias Koenig <tokoe@kde.org>
|
|
@author Narayan Newton <narayannewton@gmail.com>
|
|
*/
|
|
|
|
#include "query.h"
|
|
|
|
#include <kdebug.h>
|
|
#include <klocalizedstring.h>
|
|
#include <kurl.h>
|
|
|
|
#include <QtCore/QDateTime>
|
|
#include <QtCore/QVariant>
|
|
#include <QtXml/QDomDocument>
|
|
|
|
using namespace KXmlRpc;
|
|
|
|
/**
|
|
@file
|
|
|
|
Implementation of Query
|
|
**/
|
|
|
|
namespace KXmlRpc {
|
|
|
|
/**
|
|
@brief
|
|
Result is an internal class that represents a response
|
|
from a XML-RPC server.
|
|
|
|
This is an internal class and is only used by Query.
|
|
@internal
|
|
*/
|
|
class Result
|
|
{
|
|
friend class Query;
|
|
friend class Query::Private;
|
|
|
|
public:
|
|
/**
|
|
Constructs a result.
|
|
*/
|
|
Result();
|
|
|
|
/**
|
|
Destroys a result.
|
|
*/
|
|
~Result();
|
|
|
|
/**
|
|
Returns true if the method call succeeded, false
|
|
if there was an XML-RPC fault.
|
|
|
|
@see errorCode(), errorString()
|
|
*/
|
|
bool success() const;
|
|
|
|
/**
|
|
Returns the error code of the fault.
|
|
|
|
@see success(), errorString()
|
|
*/
|
|
int errorCode() const;
|
|
|
|
/**
|
|
Returns the error string that describes the fault.
|
|
|
|
@see success, errorCode()
|
|
*/
|
|
QString errorString() const;
|
|
|
|
/**
|
|
Returns the data sent to us from the server.
|
|
*/
|
|
QList<QVariant> data() const;
|
|
|
|
private:
|
|
bool mSuccess;
|
|
int mErrorCode;
|
|
QString mErrorString;
|
|
QList<QVariant> mData;
|
|
};
|
|
|
|
} // namespace KXmlRpcClient
|
|
|
|
KXmlRpc::Result::Result()
|
|
{
|
|
}
|
|
|
|
KXmlRpc::Result::~Result()
|
|
{
|
|
}
|
|
|
|
bool KXmlRpc::Result::success() const
|
|
{
|
|
return mSuccess;
|
|
}
|
|
|
|
int KXmlRpc::Result::errorCode() const
|
|
{
|
|
return mErrorCode;
|
|
}
|
|
|
|
QString KXmlRpc::Result::errorString() const
|
|
{
|
|
return mErrorString;
|
|
}
|
|
|
|
QList<QVariant> KXmlRpc::Result::data() const
|
|
{
|
|
return mData;
|
|
}
|
|
|
|
class Query::Private
|
|
{
|
|
public:
|
|
Private( Query *parent )
|
|
: mParent( parent )
|
|
{
|
|
}
|
|
|
|
bool isMessageResponse( const QDomDocument &doc ) const;
|
|
bool isFaultResponse( const QDomDocument &doc ) const;
|
|
|
|
Result parseMessageResponse( const QDomDocument &doc ) const;
|
|
Result parseFaultResponse( const QDomDocument &doc ) const;
|
|
|
|
QString markupCall( const QString &method, const QList<QVariant> &args ) const;
|
|
QString marshal( const QVariant &value ) const;
|
|
QVariant demarshal( const QDomElement &element ) const;
|
|
|
|
void slotData( KIO::Job *job, const QByteArray &data );
|
|
void slotResult( KJob *job );
|
|
|
|
Query *mParent;
|
|
QByteArray mBuffer;
|
|
QVariant mId;
|
|
QList<KJob*> mPendingJobs;
|
|
};
|
|
|
|
bool Query::Private::isMessageResponse( const QDomDocument &doc ) const
|
|
{
|
|
return doc.documentElement().firstChild().toElement().tagName().toLower()
|
|
== "params";
|
|
}
|
|
|
|
bool Query::Private::isFaultResponse( const QDomDocument &doc ) const
|
|
{
|
|
return doc.documentElement().firstChild().toElement().tagName().toLower()
|
|
== "fault";
|
|
}
|
|
|
|
Result Query::Private::parseMessageResponse( const QDomDocument &doc ) const
|
|
{
|
|
Result response;
|
|
response.mSuccess = true;
|
|
|
|
QDomNode paramNode = doc.documentElement().firstChild().firstChild();
|
|
while ( !paramNode.isNull() ) {
|
|
response.mData << demarshal( paramNode.firstChild().toElement() );
|
|
paramNode = paramNode.nextSibling();
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
Result Query::Private::parseFaultResponse( const QDomDocument &doc ) const
|
|
{
|
|
Result response;
|
|
response.mSuccess = false;
|
|
|
|
QDomNode errorNode = doc.documentElement().firstChild().firstChild();
|
|
const QVariant errorVariant = demarshal( errorNode.toElement() );
|
|
response.mErrorCode = errorVariant.toMap() [ "faultCode" ].toInt();
|
|
response.mErrorString = errorVariant.toMap() [ "faultString" ].toString();
|
|
|
|
return response;
|
|
}
|
|
|
|
QString Query::Private::markupCall( const QString &cmd,
|
|
const QList<QVariant> &args ) const
|
|
{
|
|
QString markup = "<?xml version=\"1.0\" ?>\r\n<methodCall>\r\n";
|
|
|
|
markup += "<methodName>" + cmd + "</methodName>\r\n";
|
|
|
|
if ( !args.isEmpty() ) {
|
|
|
|
markup += "<params>\r\n";
|
|
QList<QVariant>::ConstIterator it = args.begin();
|
|
QList<QVariant>::ConstIterator end = args.end();
|
|
for ( ; it != end; ++it ) {
|
|
markup += "<param>\r\n" + marshal( *it ) + "</param>\r\n";
|
|
}
|
|
markup += "</params>\r\n";
|
|
}
|
|
|
|
markup += "</methodCall>\r\n";
|
|
|
|
return markup;
|
|
}
|
|
|
|
QString Query::Private::marshal( const QVariant &arg ) const
|
|
{
|
|
switch ( arg.type() ) {
|
|
|
|
case QVariant::String:
|
|
return "<value><string><![CDATA[" + arg.toString() + "]]></string></value>\r\n";
|
|
case QVariant::StringList:
|
|
{
|
|
QStringList data = arg.toStringList();
|
|
QStringListIterator dataIterator( data );
|
|
QString markup;
|
|
markup += "<value><array><data>";
|
|
while ( dataIterator.hasNext() ) {
|
|
markup += "<value><string><![CDATA[" + dataIterator.next() + "]]></string></value>\r\n";
|
|
}
|
|
markup += "</data></array></value>";
|
|
return markup;
|
|
}
|
|
case QVariant::Int:
|
|
return "<value><int>" + QString::number( arg.toInt() ) + "</int></value>\r\n";
|
|
case QVariant::Double:
|
|
return "<value><double>" + QString::number( arg.toDouble() ) + "</double></value>\r\n";
|
|
case QVariant::Bool:
|
|
{
|
|
QString markup = "<value><boolean>";
|
|
markup += arg.toBool() ? "1" : "0";
|
|
markup += "</boolean></value>\r\n";
|
|
return markup;
|
|
}
|
|
case QVariant::ByteArray:
|
|
return QByteArray(QByteArray("<value><base64>") + arg.toByteArray().toBase64() + QByteArray("</base64></value>\r\n"));
|
|
case QVariant::DateTime:
|
|
{
|
|
return "<value><dateTime.iso8601>" +
|
|
arg.toDateTime().toString( Qt::ISODate ) +
|
|
"</dateTime.iso8601></value>\r\n";
|
|
}
|
|
case QVariant::List:
|
|
{
|
|
QString markup = "<value><array><data>\r\n";
|
|
const QList<QVariant> args = arg.toList();
|
|
QList<QVariant>::ConstIterator it = args.begin();
|
|
QList<QVariant>::ConstIterator end = args.end();
|
|
for ( ; it != end; ++it ) {
|
|
markup += marshal( *it );
|
|
}
|
|
markup += "</data></array></value>\r\n";
|
|
return markup;
|
|
}
|
|
case QVariant::Map:
|
|
{
|
|
QString markup = "<value><struct>\r\n";
|
|
QMap<QString, QVariant> map = arg.toMap();
|
|
QMap<QString, QVariant>::ConstIterator it = map.constBegin();
|
|
QMap<QString, QVariant>::ConstIterator end = map.constEnd();
|
|
for ( ; it != end; ++it ) {
|
|
markup += "<member>\r\n";
|
|
markup += "<name>" + it.key() + "</name>\r\n";
|
|
markup += marshal( it.value() );
|
|
markup += "</member>\r\n";
|
|
}
|
|
markup += "</struct></value>\r\n";
|
|
return markup;
|
|
}
|
|
default:
|
|
kWarning() << "Failed to marshal unknown variant type:" << arg.type();
|
|
};
|
|
|
|
return QString();
|
|
}
|
|
|
|
QVariant Query::Private::demarshal( const QDomElement &element ) const
|
|
{
|
|
Q_ASSERT( element.tagName().toLower() == "value" );
|
|
|
|
const QDomElement typeElement = element.firstChild().toElement();
|
|
const QString typeName = typeElement.tagName().toLower();
|
|
|
|
if ( typeName == "string" ) {
|
|
return QVariant( typeElement.text() );
|
|
} else if ( typeName == "i4" || typeName == "int" ) {
|
|
return QVariant( typeElement.text().toInt() );
|
|
} else if ( typeName == "double" ) {
|
|
return QVariant( typeElement.text().toDouble() );
|
|
} else if ( typeName == "boolean" ) {
|
|
|
|
if ( typeElement.text().toLower() == "true" || typeElement.text() == "1" ) {
|
|
return QVariant( true );
|
|
} else {
|
|
return QVariant( false );
|
|
}
|
|
} else if ( typeName == "base64" ) {
|
|
return QVariant( QByteArray::fromBase64( typeElement.text().toLatin1() ) );
|
|
} else if ( typeName == "datetime" || typeName == "datetime.iso8601" ) {
|
|
QDateTime date;
|
|
QString dateText = typeElement.text();
|
|
// Test for broken use of Basic ISO8601 date and extended ISO8601 time
|
|
if ( 17 <= dateText.length() && dateText.length() <= 18 &&
|
|
dateText.at( 4 ) != '-' && dateText.at( 11 ) == ':' ) {
|
|
if ( dateText.endsWith( 'Z' ) ) {
|
|
date = QDateTime::fromString( dateText, "yyyyMMddTHH:mm:ssZ" );
|
|
} else {
|
|
date = QDateTime::fromString( dateText, "yyyyMMddTHH:mm:ss" );
|
|
}
|
|
} else {
|
|
date = QDateTime::fromString( dateText, Qt::ISODate );
|
|
}
|
|
return QVariant( date );
|
|
} else if ( typeName == "array" ) {
|
|
QList<QVariant> values;
|
|
QDomNode valueNode = typeElement.firstChild().firstChild();
|
|
while ( !valueNode.isNull() ) {
|
|
values << demarshal( valueNode.toElement() );
|
|
valueNode = valueNode.nextSibling();
|
|
}
|
|
return QVariant( values );
|
|
} else if ( typeName == "struct" ) {
|
|
|
|
QMap<QString, QVariant> map;
|
|
QDomNode memberNode = typeElement.firstChild();
|
|
while ( !memberNode.isNull() ) {
|
|
const QString key = memberNode.toElement().elementsByTagName(
|
|
"name" ).item( 0 ).toElement().text();
|
|
const QVariant data = demarshal( memberNode.toElement().elementsByTagName(
|
|
"value" ).item( 0 ).toElement() );
|
|
map[ key ] = data;
|
|
memberNode = memberNode.nextSibling();
|
|
}
|
|
return QVariant( map );
|
|
} else {
|
|
kWarning() << "Cannot demarshal unknown type" << typeName;
|
|
}
|
|
return QVariant();
|
|
}
|
|
|
|
void Query::Private::slotData( KIO::Job *, const QByteArray &data )
|
|
{
|
|
unsigned int oldSize = mBuffer.size();
|
|
mBuffer.resize( oldSize + data.size() );
|
|
memcpy( mBuffer.data() + oldSize, data.data(), data.size() );
|
|
}
|
|
|
|
void Query::Private::slotResult( KJob *job )
|
|
{
|
|
mPendingJobs.removeAll( job );
|
|
|
|
if ( job->error() != 0 ) {
|
|
emit mParent->fault( job->error(), job->errorString(), mId );
|
|
emit mParent->finished( mParent );
|
|
return;
|
|
}
|
|
|
|
QDomDocument doc;
|
|
QString errMsg;
|
|
int errLine, errCol;
|
|
if ( !doc.setContent( mBuffer, false, &errMsg, &errLine, &errCol ) ) {
|
|
emit mParent->fault( -1, i18n( "Received invalid XML markup: %1 at %2:%3",
|
|
errMsg, errLine, errCol ), mId );
|
|
emit mParent->finished( mParent );
|
|
return;
|
|
}
|
|
|
|
mBuffer.truncate( 0 );
|
|
|
|
if ( isMessageResponse( doc ) ) {
|
|
emit mParent->message( parseMessageResponse( doc ).data(), mId );
|
|
} else if ( isFaultResponse( doc ) ) {
|
|
emit mParent->fault( parseFaultResponse( doc ).errorCode(),
|
|
parseFaultResponse( doc ).errorString(), mId );
|
|
} else {
|
|
emit mParent->fault( 1, i18n( "Unknown type of XML markup received" ),
|
|
mId );
|
|
}
|
|
|
|
emit mParent->finished( mParent );
|
|
}
|
|
|
|
Query *Query::create( const QVariant &id, QObject *parent )
|
|
{
|
|
return new Query( id, parent );
|
|
}
|
|
|
|
void Query::call( const QString &server,
|
|
const QString &method,
|
|
const QList<QVariant> &args,
|
|
const QMap<QString, QString> &jobMetaData )
|
|
{
|
|
|
|
const QString xmlMarkup = d->markupCall( method, args );
|
|
|
|
QMap<QString, QString>::const_iterator mapIter;
|
|
QByteArray postData;
|
|
QDataStream stream( &postData, QIODevice::WriteOnly );
|
|
stream.writeRawData( xmlMarkup.toUtf8(), xmlMarkup.toUtf8().length() );
|
|
|
|
KIO::TransferJob *job = KIO::http_post( KUrl( server ), postData, KIO::HideProgressInfo );
|
|
|
|
if ( !job ) {
|
|
kWarning() << "Unable to create KIO job for" << server;
|
|
return;
|
|
}
|
|
|
|
job->addMetaData( "content-type", "Content-Type: text/xml; charset=utf-8" );
|
|
job->addMetaData( "ConnectTimeout", "50" );
|
|
|
|
for ( mapIter = jobMetaData.begin(); mapIter != jobMetaData.end(); ++mapIter ) {
|
|
job->addMetaData( mapIter.key(), mapIter.value() );
|
|
}
|
|
|
|
connect( job, SIGNAL(data(KIO::Job*,QByteArray)),
|
|
this, SLOT(slotData(KIO::Job*,QByteArray)) );
|
|
connect( job, SIGNAL(result(KJob*)),
|
|
this, SLOT(slotResult(KJob*)) );
|
|
|
|
d->mPendingJobs.append( job );
|
|
}
|
|
|
|
Query::Query( const QVariant &id, QObject *parent )
|
|
: QObject( parent ), d( new Private( this ) )
|
|
{
|
|
d->mId = id;
|
|
}
|
|
|
|
Query::~Query()
|
|
{
|
|
QList<KJob*>::Iterator it;
|
|
for ( it = d->mPendingJobs.begin(); it != d->mPendingJobs.end(); ++it ) {
|
|
( *it )->kill();
|
|
}
|
|
delete d;
|
|
}
|
|
|
|
#include "moc_query.cpp"
|
|
|