26 #include <QtCore/QDateTime>
27 #include <QtCore/QVariant>
28 #include <QtXml/QDomDocument>
30 using namespace KXmlRpc;
51 friend class Query::Private;
77 int errorCode()
const;
84 QString errorString()
const;
89 QList<QVariant> data()
const;
95 QList<QVariant> mData;
100 KXmlRpc::Result::Result()
104 KXmlRpc::Result::~Result()
108 bool KXmlRpc::Result::success()
const
113 int KXmlRpc::Result::errorCode()
const
118 QString KXmlRpc::Result::errorString()
const
123 QList<QVariant> KXmlRpc::Result::data()
const
131 Private(
Query *parent )
136 bool isMessageResponse(
const QDomDocument &doc )
const;
137 bool isFaultResponse(
const QDomDocument &doc )
const;
139 Result parseMessageResponse(
const QDomDocument &doc )
const;
140 Result parseFaultResponse(
const QDomDocument &doc )
const;
142 QString markupCall(
const QString &method,
const QList<QVariant> &args )
const;
143 QString marshal(
const QVariant &value )
const;
144 QVariant demarshal(
const QDomElement &element )
const;
146 void slotData( KIO::Job *job,
const QByteArray &data );
147 void slotResult( KJob *job );
152 QList<KJob*> mPendingJobs;
155 bool Query::Private::isMessageResponse(
const QDomDocument &doc )
const
157 return doc.documentElement().firstChild().toElement().tagName().toLower()
161 bool Query::Private::isFaultResponse(
const QDomDocument &doc )
const
163 return doc.documentElement().firstChild().toElement().tagName().toLower()
167 Result Query::Private::parseMessageResponse(
const QDomDocument &doc )
const
170 response.mSuccess =
true;
172 QDomNode paramNode = doc.documentElement().firstChild().firstChild();
173 while ( !paramNode.isNull() ) {
174 response.mData << demarshal( paramNode.firstChild().toElement() );
175 paramNode = paramNode.nextSibling();
181 Result Query::Private::parseFaultResponse(
const QDomDocument &doc )
const
184 response.mSuccess =
false;
186 QDomNode errorNode = doc.documentElement().firstChild().firstChild();
187 const QVariant errorVariant = demarshal( errorNode.toElement() );
188 response.mErrorCode = errorVariant.toMap() [
"faultCode" ].toInt();
189 response.mErrorString = errorVariant.toMap() [
"faultString" ].toString();
194 QString Query::Private::markupCall(
const QString &cmd,
195 const QList<QVariant> &args )
const
197 QString markup =
"<?xml version=\"1.0\" ?>\r\n<methodCall>\r\n";
199 markup +=
"<methodName>" + cmd +
"</methodName>\r\n";
201 if ( !args.isEmpty() ) {
203 markup +=
"<params>\r\n";
204 QList<QVariant>::ConstIterator it = args.begin();
205 QList<QVariant>::ConstIterator end = args.end();
206 for ( ; it != end; ++it ) {
207 markup +=
"<param>\r\n" + marshal( *it ) +
"</param>\r\n";
209 markup +=
"</params>\r\n";
212 markup +=
"</methodCall>\r\n";
217 QString Query::Private::marshal(
const QVariant &arg )
const
219 switch ( arg.type() ) {
221 case QVariant::String:
222 return "<value><string><![CDATA[" + arg.toString() +
"]]></string></value>\r\n";
223 case QVariant::StringList:
225 QStringList data = arg.toStringList();
226 QStringListIterator dataIterator( data );
228 markup +=
"<value><array><data>";
229 while ( dataIterator.hasNext() ) {
230 markup +=
"<value><string><![CDATA[" + dataIterator.next() +
"]]></string></value>\r\n";
232 markup +=
"</data></array></value>";
236 return "<value><int>" + QString::number( arg.toInt() ) +
"</int></value>\r\n";
237 case QVariant::Double:
238 return "<value><double>" + QString::number( arg.toDouble() ) +
"</double></value>\r\n";
241 QString markup =
"<value><boolean>";
242 markup += arg.toBool() ?
"1" :
"0";
243 markup +=
"</boolean></value>\r\n";
246 case QVariant::ByteArray:
247 return "<value><base64>" + arg.toByteArray().toBase64() +
"</base64></value>\r\n";
248 case QVariant::DateTime:
250 return "<value><dateTime.iso8601>" +
251 arg.toDateTime().toString( Qt::ISODate ) +
252 "</dateTime.iso8601></value>\r\n";
256 QString markup =
"<value><array><data>\r\n";
257 const QList<QVariant> args = arg.toList();
258 QList<QVariant>::ConstIterator it = args.begin();
259 QList<QVariant>::ConstIterator end = args.end();
260 for ( ; it != end; ++it ) {
261 markup += marshal( *it );
263 markup +=
"</data></array></value>\r\n";
268 QString markup =
"<value><struct>\r\n";
269 QMap<QString, QVariant> map = arg.toMap();
270 QMap<QString, QVariant>::ConstIterator it = map.constBegin();
271 QMap<QString, QVariant>::ConstIterator end = map.constEnd();
272 for ( ; it != end; ++it ) {
273 markup +=
"<member>\r\n";
274 markup +=
"<name>" + it.key() +
"</name>\r\n";
275 markup += marshal( it.value() );
276 markup +=
"</member>\r\n";
278 markup +=
"</struct></value>\r\n";
282 kWarning() <<
"Failed to marshal unknown variant type:" << arg.type();
288 QVariant Query::Private::demarshal(
const QDomElement &element )
const
290 Q_ASSERT( element.tagName().toLower() ==
"value" );
292 const QDomElement typeElement = element.firstChild().toElement();
293 const QString typeName = typeElement.tagName().toLower();
295 if ( typeName ==
"string" ) {
296 return QVariant( typeElement.text() );
297 }
else if ( typeName ==
"i4" || typeName ==
"int" ) {
298 return QVariant( typeElement.text().toInt() );
299 }
else if ( typeName ==
"double" ) {
300 return QVariant( typeElement.text().toDouble() );
301 }
else if ( typeName ==
"boolean" ) {
303 if ( typeElement.text().toLower() ==
"true" || typeElement.text() ==
"1" ) {
304 return QVariant(
true );
306 return QVariant(
false );
308 }
else if ( typeName ==
"base64" ) {
309 return QVariant( QByteArray::fromBase64( typeElement.text().toLatin1() ) );
310 }
else if ( typeName ==
"datetime" || typeName ==
"datetime.iso8601" ) {
312 QString dateText = typeElement.text();
314 if ( 17 <= dateText.length() && dateText.length() <= 18 &&
315 dateText.at( 4 ) !=
'-' && dateText.at( 11 ) ==
':' ) {
316 if ( dateText.endsWith(
'Z' ) ) {
317 date = QDateTime::fromString( dateText,
"yyyyMMddTHH:mm:ssZ" );
319 date = QDateTime::fromString( dateText,
"yyyyMMddTHH:mm:ss" );
322 date = QDateTime::fromString( dateText, Qt::ISODate );
324 return QVariant( date );
325 }
else if ( typeName ==
"array" ) {
326 QList<QVariant> values;
327 QDomNode valueNode = typeElement.firstChild().firstChild();
328 while ( !valueNode.isNull() ) {
329 values << demarshal( valueNode.toElement() );
330 valueNode = valueNode.nextSibling();
332 return QVariant( values );
333 }
else if ( typeName ==
"struct" ) {
335 QMap<QString, QVariant> map;
336 QDomNode memberNode = typeElement.firstChild();
337 while ( !memberNode.isNull() ) {
338 const QString key = memberNode.toElement().elementsByTagName(
339 "name" ).item( 0 ).toElement().text();
340 const QVariant data = demarshal( memberNode.toElement().elementsByTagName(
341 "value" ).item( 0 ).toElement() );
343 memberNode = memberNode.nextSibling();
345 return QVariant( map );
347 kWarning() <<
"Cannot demarshal unknown type" << typeName;
352 void Query::Private::slotData( KIO::Job *,
const QByteArray &data )
354 unsigned int oldSize = mBuffer.size();
355 mBuffer.resize( oldSize + data.size() );
356 memcpy( mBuffer.data() + oldSize, data.data(), data.size() );
359 void Query::Private::slotResult( KJob *job )
361 mPendingJobs.removeAll( job );
363 if ( job->error() != 0 ) {
364 emit mParent->fault( job->error(), job->errorString(), mId );
365 emit mParent->finished( mParent );
372 if ( !doc.setContent( mBuffer,
false, &errMsg, &errLine, &errCol ) ) {
373 emit mParent->fault( -1, i18n(
"Received invalid XML markup: %1 at %2:%3",
374 errMsg, errLine, errCol ), mId );
375 emit mParent->finished( mParent );
379 mBuffer.truncate( 0 );
381 if ( isMessageResponse( doc ) ) {
382 emit mParent->message( parseMessageResponse( doc ).data(), mId );
383 }
else if ( isFaultResponse( doc ) ) {
384 emit mParent->fault( parseFaultResponse( doc ).errorCode(),
385 parseFaultResponse( doc ).errorString(), mId );
387 emit mParent->fault( 1, i18n(
"Unknown type of XML markup received" ),
391 emit mParent->finished( mParent );
396 return new Query(
id, parent );
400 const QString &method,
401 const QList<QVariant> &args,
402 const QMap<QString, QString> &jobMetaData )
405 const QString xmlMarkup = d->markupCall( method, args );
407 QMap<QString, QString>::const_iterator mapIter;
409 QDataStream stream( &postData, QIODevice::WriteOnly );
410 stream.writeRawData( xmlMarkup.toUtf8(), xmlMarkup.toUtf8().length() );
412 KIO::TransferJob *job = KIO::http_post( KUrl( server ), postData, KIO::HideProgressInfo );
415 kWarning() <<
"Unable to create KIO job for" << server;
419 job->addMetaData(
"content-type",
"Content-Type: text/xml; charset=utf-8" );
420 job->addMetaData(
"ConnectTimeout",
"50" );
422 for ( mapIter = jobMetaData.begin(); mapIter != jobMetaData.end(); ++mapIter ) {
423 job->addMetaData( mapIter.key(), mapIter.value() );
426 connect( job, SIGNAL(data(KIO::Job*,QByteArray)),
427 this, SLOT(slotData(KIO::Job*,QByteArray)) );
428 connect( job, SIGNAL(result(KJob*)),
429 this, SLOT(slotResult(KJob*)) );
431 d->mPendingJobs.append( job );
434 Query::Query(
const QVariant &
id, QObject *parent )
435 : QObject( parent ), d( new Private( this ) )
442 QList<KJob*>::Iterator it;
443 for ( it = d->mPendingJobs.begin(); it != d->mPendingJobs.end(); ++it ) {
449 #include "moc_query.cpp"