• Skip to content
  • Skip to link menu
KDE 4.0 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • 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 += "<string><![CDATA[" + dataIterator.next() + "]]></string>\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>" + arg.toDateTime().toString( Qt::ISODate ) + "</dateTime.iso8601></value>\r\n";
00251       }
00252     case QVariant::List:
00253       {
00254         QString markup = "<value><array><data>\r\n";
00255         const QList<QVariant> args = arg.toList();
00256         QList<QVariant>::ConstIterator it = args.begin();
00257         QList<QVariant>::ConstIterator end = args.end();
00258         for ( ; it != end; ++it ) {
00259           markup += marshal( *it );
00260         }
00261         markup += "</data></array></value>\r\n";
00262         return markup;
00263       }
00264     case QVariant::Map:
00265       {
00266         QString markup = "<value><struct>\r\n";
00267         QMap<QString, QVariant> map = arg.toMap();
00268         QMap<QString, QVariant>::ConstIterator it = map.begin();
00269         QMap<QString, QVariant>::ConstIterator end = map.end();
00270         for ( ; it != end; ++it ) {
00271           markup += "<member>\r\n";
00272           markup += "<name>" + it.key() + "</name>\r\n";
00273           markup += marshal( it.value() );
00274           markup += "</member>\r\n";
00275         }
00276         markup += "</struct></value>\r\n";
00277         return markup;
00278       }
00279     default:
00280       kWarning() << "Failed to marshal unknown variant type:" << arg.type();
00281   };
00282 
00283   return QString();
00284 }
00285 
00286 QVariant Query::Private::demarshal( const QDomElement &element ) const
00287 {
00288   Q_ASSERT( element.tagName().toLower() == "value" );
00289 
00290   const QDomElement typeElement = element.firstChild().toElement();
00291   const QString typeName = typeElement.tagName().toLower();
00292 
00293   if ( typeName == "string" ) {
00294     return QVariant( typeElement.text() );
00295   } else if ( typeName == "i4" || typeName == "int" ) {
00296     return QVariant( typeElement.text().toInt() );
00297   } else if ( typeName == "double" ) {
00298     return QVariant( typeElement.text().toDouble() );
00299   } else if ( typeName == "boolean" ) {
00300 
00301     if ( typeElement.text().toLower() == "true" || typeElement.text() == "1" ) {
00302       return QVariant( true );
00303     } else {
00304       return QVariant( false );
00305     }
00306   } else if ( typeName == "base64" ) {
00307     return QVariant( QByteArray::fromBase64( typeElement.text().toLatin1() ) );
00308   } else if ( typeName == "datetime" || typeName == "datetime.iso8601" ) {
00309     QDateTime date;
00310     QString dateText = typeElement.text();
00311     // Test for broken use of Basic ISO8601 date and extended ISO8601 time
00312     if ( 17 <= dateText.length() && dateText.length() <= 18 &&
00313          dateText.at( 4 ) != '-' && dateText.at( 11 ) == ':' ) {
00314         if ( dateText.endsWith( 'Z' ) ) {
00315           date = QDateTime::fromString( dateText, "yyyyMMddTHH:mm:ssZ" );
00316         } else {
00317           date = QDateTime::fromString( dateText, "yyyyMMddTHH:mm:ss" );
00318         }
00319     } else {
00320       date = QDateTime::fromString( dateText, Qt::ISODate );
00321     }
00322     return QVariant( date );
00323   } else if ( typeName == "array" ) {
00324     QList<QVariant> values;
00325     QDomNode valueNode = typeElement.firstChild().firstChild();
00326     while ( !valueNode.isNull() ) {
00327       values << demarshal( valueNode.toElement() );
00328       valueNode = valueNode.nextSibling();
00329     }
00330     return QVariant( values );
00331   } else if ( typeName == "struct" ) {
00332 
00333     QMap<QString, QVariant> map;
00334     QDomNode memberNode = typeElement.firstChild();
00335     while ( !memberNode.isNull() ) {
00336       const QString key = memberNode.toElement().elementsByTagName(
00337                               "name" ).item( 0 ).toElement().text();
00338       const QVariant data = demarshal( memberNode.toElement().elementsByTagName(
00339                                        "value" ).item( 0 ).toElement() );
00340       map[ key ] = data;
00341       memberNode = memberNode.nextSibling();
00342     }
00343     return QVariant( map );
00344   } else {
00345     kWarning() << "Cannot demarshal unknown type" << typeName;
00346   }
00347   return QVariant();
00348 }
00349 
00350 void Query::Private::slotData( KIO::Job *, const QByteArray &data )
00351 {
00352   unsigned int oldSize = mBuffer.size();
00353   mBuffer.resize( oldSize + data.size() );
00354   memcpy( mBuffer.data() + oldSize, data.data(), data.size() );
00355 }
00356 
00357 void Query::Private::slotResult( KJob *job )
00358 {
00359   mPendingJobs.removeAll( job );
00360 
00361   if ( job->error() != 0 ) {
00362     emit mParent->fault( job->error(), job->errorString(), mId );
00363     emit mParent->finished( mParent );
00364     return;
00365   }
00366 
00367   const QString data = QString::fromUtf8( mBuffer.data(), mBuffer.size() );
00368 
00369   QDomDocument doc;
00370   QString errMsg;
00371   int errLine, errCol;
00372   if ( !doc.setContent( data, 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 *, const QByteArray & ) ),
00427            this, SLOT( slotData( KIO::Job *, const 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 

KXMLRPC Client Library

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

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • kabc
  • kblog
  • kcal
  • kimap
  • kioslave
  •   imap4
  •   mbox
  • kldap
  • kmime
  • kpimidentities
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.5.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal