KXMLRPC Client Library
query.cpp
Go to the documentation of this file.
00001 /****************************************************************************** 00002 * Copyright (C) 2003 - 2004 by Frerich Raabe <raabe@kde.org> * 00003 * Tobias Koenig <tokoe@kde.org> * 00004 * Copyright (C) 2006 by Narayan Newton <narayannewton@gmail.com> * 00005 * * 00006 * This program is distributed in the hope that it will be useful, but * 00007 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * 00008 * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution * 00009 * details, check the accompanying file 'COPYING.BSD'. * 00010 *****************************************************************************/ 00021 #include "query.h" 00022 00023 #include <kdebug.h> 00024 #include <klocale.h> 00025 00026 #include <QtCore/QDateTime> 00027 #include <QtCore/QVariant> 00028 #include <QtXml/QDomDocument> 00029 00030 using namespace KXmlRpc; 00031 00038 namespace KXmlRpc { 00039 00048 class Result 00049 { 00050 friend class Query; 00051 friend class Query::Private; 00052 00053 public: 00057 Result(); 00058 00062 ~Result(); 00063 00070 bool success() const; 00071 00077 int errorCode() const; 00078 00084 QString errorString() const; 00085 00089 QList<QVariant> data() const; 00090 00091 private: 00092 bool mSuccess; 00093 int mErrorCode; 00094 QString mErrorString; 00095 QList<QVariant> mData; 00096 }; 00097 00098 } // namespace KXmlRpcClient 00099 00100 KXmlRpc::Result::Result() 00101 { 00102 } 00103 00104 KXmlRpc::Result::~Result() 00105 { 00106 } 00107 00108 bool KXmlRpc::Result::success() const 00109 { 00110 return mSuccess; 00111 } 00112 00113 int KXmlRpc::Result::errorCode() const 00114 { 00115 return mErrorCode; 00116 } 00117 00118 QString KXmlRpc::Result::errorString() const 00119 { 00120 return mErrorString; 00121 } 00122 00123 QList<QVariant> KXmlRpc::Result::data() const 00124 { 00125 return mData; 00126 } 00127 00128 class Query::Private 00129 { 00130 public: 00131 Private( Query *parent ) 00132 : mParent( parent ) 00133 { 00134 } 00135 00136 bool isMessageResponse( const QDomDocument &doc ) const; 00137 bool isFaultResponse( const QDomDocument &doc ) const; 00138 00139 Result parseMessageResponse( const QDomDocument &doc ) const; 00140 Result parseFaultResponse( const QDomDocument &doc ) const; 00141 00142 QString markupCall( const QString &method, const QList<QVariant> &args ) const; 00143 QString marshal( const QVariant &value ) const; 00144 QVariant demarshal( const QDomElement &element ) const; 00145 00146 void slotData( KIO::Job *job, const QByteArray &data ); 00147 void slotResult( KJob *job ); 00148 00149 Query *mParent; 00150 QByteArray mBuffer; 00151 QVariant mId; 00152 QList<KJob*> mPendingJobs; 00153 }; 00154 00155 bool Query::Private::isMessageResponse( const QDomDocument &doc ) const 00156 { 00157 return doc.documentElement().firstChild().toElement().tagName().toLower() 00158 == "params"; 00159 } 00160 00161 bool Query::Private::isFaultResponse( const QDomDocument &doc ) const 00162 { 00163 return doc.documentElement().firstChild().toElement().tagName().toLower() 00164 == "fault"; 00165 } 00166 00167 Result Query::Private::parseMessageResponse( const QDomDocument &doc ) const 00168 { 00169 Result response; 00170 response.mSuccess = true; 00171 00172 QDomNode paramNode = doc.documentElement().firstChild().firstChild(); 00173 while ( !paramNode.isNull() ) { 00174 response.mData << demarshal( paramNode.firstChild().toElement() ); 00175 paramNode = paramNode.nextSibling(); 00176 } 00177 00178 return response; 00179 } 00180 00181 Result Query::Private::parseFaultResponse( const QDomDocument &doc ) const 00182 { 00183 Result response; 00184 response.mSuccess = false; 00185 00186 QDomNode errorNode = doc.documentElement().firstChild().firstChild(); 00187 const QVariant errorVariant = demarshal( errorNode.toElement() ); 00188 response.mErrorCode = errorVariant.toMap() [ "faultCode" ].toInt(); 00189 response.mErrorString = errorVariant.toMap() [ "faultString" ].toString(); 00190 00191 return response; 00192 } 00193 00194 QString Query::Private::markupCall( const QString &cmd, 00195 const QList<QVariant> &args ) const 00196 { 00197 QString markup = "<?xml version=\"1.0\" ?>\r\n<methodCall>\r\n"; 00198 00199 markup += "<methodName>" + cmd + "</methodName>\r\n"; 00200 00201 if ( !args.isEmpty() ) { 00202 00203 markup += "<params>\r\n"; 00204 QList<QVariant>::ConstIterator it = args.begin(); 00205 QList<QVariant>::ConstIterator end = args.end(); 00206 for ( ; it != end; ++it ) { 00207 markup += "<param>\r\n" + marshal( *it ) + "</param>\r\n"; 00208 } 00209 markup += "</params>\r\n"; 00210 } 00211 00212 markup += "</methodCall>\r\n"; 00213 00214 return markup; 00215 } 00216 00217 QString Query::Private::marshal( const QVariant &arg ) const 00218 { 00219 switch ( arg.type() ) { 00220 00221 case QVariant::String: 00222 return "<value><string><![CDATA[" + arg.toString() + "]]></string></value>\r\n"; 00223 case QVariant::StringList: 00224 { 00225 QStringList data = arg.toStringList(); 00226 QStringListIterator dataIterator(data); 00227 QString markup; 00228 markup += "<value><array><data>"; 00229 while ( dataIterator.hasNext() ) { 00230 markup += "<value><string><![CDATA[" + dataIterator.next() + "]]></string></value>\r\n"; 00231 } 00232 markup += "</data></array></value>"; 00233 return markup; 00234 } 00235 case QVariant::Int: 00236 return "<value><int>" + QString::number( arg.toInt() ) + "</int></value>\r\n"; 00237 case QVariant::Double: 00238 return "<value><double>" + QString::number( arg.toDouble() ) + "</double></value>\r\n"; 00239 case QVariant::Bool: 00240 { 00241 QString markup = "<value><boolean>"; 00242 markup += arg.toBool() ? "1" : "0"; 00243 markup += "</boolean></value>\r\n"; 00244 return markup; 00245 } 00246 case QVariant::ByteArray: 00247 return "<value><base64>" + arg.toByteArray().toBase64() + "</base64></value>\r\n"; 00248 case QVariant::DateTime: 00249 { 00250 return "<value><dateTime.iso8601>" + 00251 arg.toDateTime().toString( Qt::ISODate ) + 00252 "</dateTime.iso8601></value>\r\n"; 00253 } 00254 case QVariant::List: 00255 { 00256 QString markup = "<value><array><data>\r\n"; 00257 const QList<QVariant> args = arg.toList(); 00258 QList<QVariant>::ConstIterator it = args.begin(); 00259 QList<QVariant>::ConstIterator end = args.end(); 00260 for ( ; it != end; ++it ) { 00261 markup += marshal( *it ); 00262 } 00263 markup += "</data></array></value>\r\n"; 00264 return markup; 00265 } 00266 case QVariant::Map: 00267 { 00268 QString markup = "<value><struct>\r\n"; 00269 QMap<QString, QVariant> map = arg.toMap(); 00270 QMap<QString, QVariant>::ConstIterator it = map.constBegin(); 00271 QMap<QString, QVariant>::ConstIterator end = map.constEnd(); 00272 for ( ; it != end; ++it ) { 00273 markup += "<member>\r\n"; 00274 markup += "<name>" + it.key() + "</name>\r\n"; 00275 markup += marshal( it.value() ); 00276 markup += "</member>\r\n"; 00277 } 00278 markup += "</struct></value>\r\n"; 00279 return markup; 00280 } 00281 default: 00282 kWarning() << "Failed to marshal unknown variant type:" << arg.type(); 00283 }; 00284 00285 return QString(); 00286 } 00287 00288 QVariant Query::Private::demarshal( const QDomElement &element ) const 00289 { 00290 Q_ASSERT( element.tagName().toLower() == "value" ); 00291 00292 const QDomElement typeElement = element.firstChild().toElement(); 00293 const QString typeName = typeElement.tagName().toLower(); 00294 00295 if ( typeName == "string" ) { 00296 return QVariant( typeElement.text() ); 00297 } else if ( typeName == "i4" || typeName == "int" ) { 00298 return QVariant( typeElement.text().toInt() ); 00299 } else if ( typeName == "double" ) { 00300 return QVariant( typeElement.text().toDouble() ); 00301 } else if ( typeName == "boolean" ) { 00302 00303 if ( typeElement.text().toLower() == "true" || typeElement.text() == "1" ) { 00304 return QVariant( true ); 00305 } else { 00306 return QVariant( false ); 00307 } 00308 } else if ( typeName == "base64" ) { 00309 return QVariant( QByteArray::fromBase64( typeElement.text().toLatin1() ) ); 00310 } else if ( typeName == "datetime" || typeName == "datetime.iso8601" ) { 00311 QDateTime date; 00312 QString dateText = typeElement.text(); 00313 // Test for broken use of Basic ISO8601 date and extended ISO8601 time 00314 if ( 17 <= dateText.length() && dateText.length() <= 18 && 00315 dateText.at( 4 ) != '-' && dateText.at( 11 ) == ':' ) { 00316 if ( dateText.endsWith( 'Z' ) ) { 00317 date = QDateTime::fromString( dateText, "yyyyMMddTHH:mm:ssZ" ); 00318 } else { 00319 date = QDateTime::fromString( dateText, "yyyyMMddTHH:mm:ss" ); 00320 } 00321 } else { 00322 date = QDateTime::fromString( dateText, Qt::ISODate ); 00323 } 00324 return QVariant( date ); 00325 } else if ( typeName == "array" ) { 00326 QList<QVariant> values; 00327 QDomNode valueNode = typeElement.firstChild().firstChild(); 00328 while ( !valueNode.isNull() ) { 00329 values << demarshal( valueNode.toElement() ); 00330 valueNode = valueNode.nextSibling(); 00331 } 00332 return QVariant( values ); 00333 } else if ( typeName == "struct" ) { 00334 00335 QMap<QString, QVariant> map; 00336 QDomNode memberNode = typeElement.firstChild(); 00337 while ( !memberNode.isNull() ) { 00338 const QString key = memberNode.toElement().elementsByTagName( 00339 "name" ).item( 0 ).toElement().text(); 00340 const QVariant data = demarshal( memberNode.toElement().elementsByTagName( 00341 "value" ).item( 0 ).toElement() ); 00342 map[ key ] = data; 00343 memberNode = memberNode.nextSibling(); 00344 } 00345 return QVariant( map ); 00346 } else { 00347 kWarning() << "Cannot demarshal unknown type" << typeName; 00348 } 00349 return QVariant(); 00350 } 00351 00352 void Query::Private::slotData( KIO::Job *, const QByteArray &data ) 00353 { 00354 unsigned int oldSize = mBuffer.size(); 00355 mBuffer.resize( oldSize + data.size() ); 00356 memcpy( mBuffer.data() + oldSize, data.data(), data.size() ); 00357 } 00358 00359 void Query::Private::slotResult( KJob *job ) 00360 { 00361 mPendingJobs.removeAll( job ); 00362 00363 if ( job->error() != 0 ) { 00364 emit mParent->fault( job->error(), job->errorString(), mId ); 00365 emit mParent->finished( mParent ); 00366 return; 00367 } 00368 00369 QDomDocument doc; 00370 QString errMsg; 00371 int errLine, errCol; 00372 if ( !doc.setContent( mBuffer, false, &errMsg, &errLine, &errCol ) ) { 00373 emit mParent->fault( -1, i18n( "Received invalid XML markup: %1 at %2:%3", 00374 errMsg, errLine, errCol ), mId ); 00375 emit mParent->finished( mParent ); 00376 return; 00377 } 00378 00379 mBuffer.truncate( 0 ); 00380 00381 if ( isMessageResponse( doc ) ) { 00382 emit mParent->message( parseMessageResponse( doc ).data(), mId ); 00383 } else if ( isFaultResponse( doc ) ) { 00384 emit mParent->fault( parseFaultResponse( doc ).errorCode(), 00385 parseFaultResponse( doc ).errorString(), mId ); 00386 } else { 00387 emit mParent->fault( 1, i18n( "Unknown type of XML markup received" ), 00388 mId ); 00389 } 00390 00391 emit mParent->finished( mParent ); 00392 } 00393 00394 Query *Query::create( const QVariant &id, QObject *parent ) 00395 { 00396 return new Query( id, parent ); 00397 } 00398 00399 void Query::call( const QString &server, 00400 const QString &method, 00401 const QList<QVariant> &args, 00402 const QMap<QString, QString> &jobMetaData ) 00403 { 00404 00405 const QString xmlMarkup = d->markupCall( method, args ); 00406 00407 QMap<QString, QString>::const_iterator mapIter; 00408 QByteArray postData; 00409 QDataStream stream( &postData, QIODevice::WriteOnly ); 00410 stream.writeRawData( xmlMarkup.toUtf8(), xmlMarkup.toUtf8().length() ); 00411 00412 KIO::TransferJob *job = KIO::http_post( KUrl( server ), postData, KIO::HideProgressInfo ); 00413 00414 if ( !job ) { 00415 kWarning() << "Unable to create KIO job for" << server; 00416 return; 00417 } 00418 00419 job->addMetaData( "content-type", "Content-Type: text/xml; charset=utf-8" ); 00420 job->addMetaData( "ConnectTimeout", "50" ); 00421 00422 for ( mapIter = jobMetaData.begin(); mapIter != jobMetaData.end(); ++mapIter ) { 00423 job->addMetaData( mapIter.key(), mapIter.value() ); 00424 } 00425 00426 connect( job, SIGNAL(data(KIO::Job*,QByteArray)), 00427 this, SLOT(slotData(KIO::Job*,QByteArray)) ); 00428 connect( job, SIGNAL(result(KJob*)), 00429 this, SLOT(slotResult(KJob*)) ); 00430 00431 d->mPendingJobs.append( job ); 00432 } 00433 00434 Query::Query( const QVariant &id, QObject *parent ) 00435 : QObject( parent ), d( new Private( this ) ) 00436 { 00437 d->mId = id; 00438 } 00439 00440 Query::~Query() 00441 { 00442 QList<KJob*>::Iterator it; 00443 for ( it = d->mPendingJobs.begin(); it != d->mPendingJobs.end(); ++it ) { 00444 (*it)->kill(); 00445 } 00446 delete d; 00447 } 00448 00449 #include "query.moc" 00450
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon Apr 30 2012 21:48:50 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon Apr 30 2012 21:48:50 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.