• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.8.5 API Reference
  • KDE Home
  • Contact Us
 

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 Aug 27 2012 22:08:45 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KXMLRPC Client Library

Skip menu "KXMLRPC Client Library"
  • Main Page
  • Namespace List
  • Alphabetical List
  • Class List
  • Class Members
  • File List
  • Related Pages

kdepimlibs-4.8.5 API Reference

Skip menu "kdepimlibs-4.8.5 API Reference"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal