24 #include <klocalizedstring.h>
27 #include <QtCore/QDateTime>
28 #include <QtCore/QVariant>
29 #include <QtXml/QDomDocument>
31 using namespace KXmlRpc;
52 friend class Query::Private;
78 int errorCode()
const;
85 QString errorString()
const;
90 QList<QVariant> data()
const;
96 QList<QVariant> mData;
101 KXmlRpc::Result::Result()
105 KXmlRpc::Result::~Result()
109 bool KXmlRpc::Result::success()
const
114 int KXmlRpc::Result::errorCode()
const
119 QString KXmlRpc::Result::errorString()
const
124 QList<QVariant> KXmlRpc::Result::data()
const
132 Private(
Query *parent )
137 bool isMessageResponse(
const QDomDocument &doc )
const;
138 bool isFaultResponse(
const QDomDocument &doc )
const;
140 Result parseMessageResponse(
const QDomDocument &doc )
const;
141 Result parseFaultResponse(
const QDomDocument &doc )
const;
143 QString markupCall(
const QString &method,
const QList<QVariant> &args )
const;
144 QString marshal(
const QVariant &value )
const;
145 QVariant demarshal(
const QDomElement &element )
const;
147 void slotData( KIO::Job *job,
const QByteArray &data );
148 void slotResult( KJob *job );
153 QList<KJob*> mPendingJobs;
156 bool Query::Private::isMessageResponse(
const QDomDocument &doc )
const
158 return doc.documentElement().firstChild().toElement().tagName().toLower()
162 bool Query::Private::isFaultResponse(
const QDomDocument &doc )
const
164 return doc.documentElement().firstChild().toElement().tagName().toLower()
168 Result Query::Private::parseMessageResponse(
const QDomDocument &doc )
const
171 response.mSuccess =
true;
173 QDomNode paramNode = doc.documentElement().firstChild().firstChild();
174 while ( !paramNode.isNull() ) {
175 response.mData << demarshal( paramNode.firstChild().toElement() );
176 paramNode = paramNode.nextSibling();
182 Result Query::Private::parseFaultResponse(
const QDomDocument &doc )
const
185 response.mSuccess =
false;
187 QDomNode errorNode = doc.documentElement().firstChild().firstChild();
188 const QVariant errorVariant = demarshal( errorNode.toElement() );
189 response.mErrorCode = errorVariant.toMap() [
"faultCode" ].toInt();
190 response.mErrorString = errorVariant.toMap() [
"faultString" ].toString();
195 QString Query::Private::markupCall(
const QString &cmd,
196 const QList<QVariant> &args )
const
198 QString markup =
"<?xml version=\"1.0\" ?>\r\n<methodCall>\r\n";
200 markup +=
"<methodName>" + cmd +
"</methodName>\r\n";
202 if ( !args.isEmpty() ) {
204 markup +=
"<params>\r\n";
205 QList<QVariant>::ConstIterator it = args.begin();
206 QList<QVariant>::ConstIterator end = args.end();
207 for ( ; it != end; ++it ) {
208 markup +=
"<param>\r\n" + marshal( *it ) +
"</param>\r\n";
210 markup +=
"</params>\r\n";
213 markup +=
"</methodCall>\r\n";
218 QString Query::Private::marshal(
const QVariant &arg )
const
220 switch ( arg.type() ) {
222 case QVariant::String:
223 return "<value><string><![CDATA[" + arg.toString() +
"]]></string></value>\r\n";
224 case QVariant::StringList:
226 QStringList data = arg.toStringList();
227 QStringListIterator dataIterator( data );
229 markup +=
"<value><array><data>";
230 while ( dataIterator.hasNext() ) {
231 markup +=
"<value><string><![CDATA[" + dataIterator.next() +
"]]></string></value>\r\n";
233 markup +=
"</data></array></value>";
237 return "<value><int>" + QString::number( arg.toInt() ) +
"</int></value>\r\n";
238 case QVariant::Double:
239 return "<value><double>" + QString::number( arg.toDouble() ) +
"</double></value>\r\n";
242 QString markup =
"<value><boolean>";
243 markup += arg.toBool() ?
"1" :
"0";
244 markup +=
"</boolean></value>\r\n";
247 case QVariant::ByteArray:
248 return "<value><base64>" + arg.toByteArray().toBase64() +
"</base64></value>\r\n";
249 case QVariant::DateTime:
251 return "<value><dateTime.iso8601>" +
252 arg.toDateTime().toString( Qt::ISODate ) +
253 "</dateTime.iso8601></value>\r\n";
257 QString markup =
"<value><array><data>\r\n";
258 const QList<QVariant> args = arg.toList();
259 QList<QVariant>::ConstIterator it = args.begin();
260 QList<QVariant>::ConstIterator end = args.end();
261 for ( ; it != end; ++it ) {
262 markup += marshal( *it );
264 markup +=
"</data></array></value>\r\n";
269 QString markup =
"<value><struct>\r\n";
270 QMap<QString, QVariant> map = arg.toMap();
271 QMap<QString, QVariant>::ConstIterator it = map.constBegin();
272 QMap<QString, QVariant>::ConstIterator end = map.constEnd();
273 for ( ; it != end; ++it ) {
274 markup +=
"<member>\r\n";
275 markup +=
"<name>" + it.key() +
"</name>\r\n";
276 markup += marshal( it.value() );
277 markup +=
"</member>\r\n";
279 markup +=
"</struct></value>\r\n";
283 kWarning() <<
"Failed to marshal unknown variant type:" << arg.type();
289 QVariant Query::Private::demarshal(
const QDomElement &element )
const
291 Q_ASSERT( element.tagName().toLower() ==
"value" );
293 const QDomElement typeElement = element.firstChild().toElement();
294 const QString typeName = typeElement.tagName().toLower();
296 if ( typeName ==
"string" ) {
297 return QVariant( typeElement.text() );
298 }
else if ( typeName ==
"i4" || typeName ==
"int" ) {
299 return QVariant( typeElement.text().toInt() );
300 }
else if ( typeName ==
"double" ) {
301 return QVariant( typeElement.text().toDouble() );
302 }
else if ( typeName ==
"boolean" ) {
304 if ( typeElement.text().toLower() ==
"true" || typeElement.text() ==
"1" ) {
305 return QVariant(
true );
307 return QVariant(
false );
309 }
else if ( typeName ==
"base64" ) {
310 return QVariant( QByteArray::fromBase64( typeElement.text().toLatin1() ) );
311 }
else if ( typeName ==
"datetime" || typeName ==
"datetime.iso8601" ) {
313 QString dateText = typeElement.text();
315 if ( 17 <= dateText.length() && dateText.length() <= 18 &&
316 dateText.at( 4 ) !=
'-' && dateText.at( 11 ) ==
':' ) {
317 if ( dateText.endsWith(
'Z' ) ) {
318 date = QDateTime::fromString( dateText,
"yyyyMMddTHH:mm:ssZ" );
320 date = QDateTime::fromString( dateText,
"yyyyMMddTHH:mm:ss" );
323 date = QDateTime::fromString( dateText, Qt::ISODate );
325 return QVariant( date );
326 }
else if ( typeName ==
"array" ) {
327 QList<QVariant> values;
328 QDomNode valueNode = typeElement.firstChild().firstChild();
329 while ( !valueNode.isNull() ) {
330 values << demarshal( valueNode.toElement() );
331 valueNode = valueNode.nextSibling();
333 return QVariant( values );
334 }
else if ( typeName ==
"struct" ) {
336 QMap<QString, QVariant> map;
337 QDomNode memberNode = typeElement.firstChild();
338 while ( !memberNode.isNull() ) {
339 const QString key = memberNode.toElement().elementsByTagName(
340 "name" ).item( 0 ).toElement().text();
341 const QVariant data = demarshal( memberNode.toElement().elementsByTagName(
342 "value" ).item( 0 ).toElement() );
344 memberNode = memberNode.nextSibling();
346 return QVariant( map );
348 kWarning() <<
"Cannot demarshal unknown type" << typeName;
353 void Query::Private::slotData( KIO::Job *,
const QByteArray &data )
355 unsigned int oldSize = mBuffer.size();
356 mBuffer.resize( oldSize + data.size() );
357 memcpy( mBuffer.data() + oldSize, data.data(), data.size() );
360 void Query::Private::slotResult( KJob *job )
362 mPendingJobs.removeAll( job );
364 if ( job->error() != 0 ) {
365 emit mParent->fault( job->error(), job->errorString(), mId );
366 emit mParent->finished( mParent );
373 if ( !doc.setContent( mBuffer,
false, &errMsg, &errLine, &errCol ) ) {
374 emit mParent->fault( -1, i18n(
"Received invalid XML markup: %1 at %2:%3",
375 errMsg, errLine, errCol ), mId );
376 emit mParent->finished( mParent );
380 mBuffer.truncate( 0 );
382 if ( isMessageResponse( doc ) ) {
383 emit mParent->message( parseMessageResponse( doc ).data(), mId );
384 }
else if ( isFaultResponse( doc ) ) {
385 emit mParent->fault( parseFaultResponse( doc ).errorCode(),
386 parseFaultResponse( doc ).errorString(), mId );
388 emit mParent->fault( 1, i18n(
"Unknown type of XML markup received" ),
392 emit mParent->finished( mParent );
397 return new Query(
id, parent );
401 const QString &method,
402 const QList<QVariant> &args,
403 const QMap<QString, QString> &jobMetaData )
406 const QString xmlMarkup = d->markupCall( method, args );
408 QMap<QString, QString>::const_iterator mapIter;
410 QDataStream stream( &postData, QIODevice::WriteOnly );
411 stream.writeRawData( xmlMarkup.toUtf8(), xmlMarkup.toUtf8().length() );
413 KIO::TransferJob *job = KIO::http_post( KUrl( server ), postData, KIO::HideProgressInfo );
416 kWarning() <<
"Unable to create KIO job for" << server;
420 job->addMetaData(
"content-type",
"Content-Type: text/xml; charset=utf-8" );
421 job->addMetaData(
"ConnectTimeout",
"50" );
423 for ( mapIter = jobMetaData.begin(); mapIter != jobMetaData.end(); ++mapIter ) {
424 job->addMetaData( mapIter.key(), mapIter.value() );
427 connect( job, SIGNAL(data(KIO::Job*,QByteArray)),
428 this, SLOT(slotData(KIO::Job*,QByteArray)) );
429 connect( job, SIGNAL(result(KJob*)),
430 this, SLOT(slotResult(KJob*)) );
432 d->mPendingJobs.append( job );
435 Query::Query(
const QVariant &
id, QObject *parent )
436 : QObject( parent ), d( new Private( this ) )
443 QList<KJob*>::Iterator it;
444 for ( it = d->mPendingJobs.begin(); it != d->mPendingJobs.end(); ++it ) {
450 #include "moc_query.cpp"
This file is part of KXmlRpc and defines our internal classes.
Query is a class that represents an individual XML-RPC call.
void call(const QString &server, const QString &method, const QList< QVariant > &args, const QMap< QString, QString > &jobMetaData)
Calls the specified method on the specified server with the given argument list.
static Query * create(const QVariant &id=QVariant(), QObject *parent=0)
Constructs a query.