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

KXMLRPC Client Library

  • kxmlrpcclient
query.cpp
Go to the documentation of this file.
1 /******************************************************************************
2  * Copyright (C) 2003 - 2004 by Frerich Raabe <raabe@kde.org> *
3  * Tobias Koenig <tokoe@kde.org> *
4  * Copyright (C) 2006 by Narayan Newton <narayannewton@gmail.com> *
5  * *
6  * This program is distributed in the hope that it will be useful, but *
7  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
8  * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution *
9  * details, check the accompanying file 'COPYING.BSD'. *
10  *****************************************************************************/
21 #include "query.h"
22 
23 #include <kdebug.h>
24 #include <klocale.h>
25 
26 #include <QtCore/QDateTime>
27 #include <QtCore/QVariant>
28 #include <QtXml/QDomDocument>
29 
30 using namespace KXmlRpc;
31 
38 namespace KXmlRpc {
39 
48 class Result
49 {
50  friend class Query;
51  friend class Query::Private;
52 
53  public:
57  Result();
58 
62  ~Result();
63 
70  bool success() const;
71 
77  int errorCode() const;
78 
84  QString errorString() const;
85 
89  QList<QVariant> data() const;
90 
91  private:
92  bool mSuccess;
93  int mErrorCode;
94  QString mErrorString;
95  QList<QVariant> mData;
96 };
97 
98 } // namespace KXmlRpcClient
99 
100 KXmlRpc::Result::Result()
101 {
102 }
103 
104 KXmlRpc::Result::~Result()
105 {
106 }
107 
108 bool KXmlRpc::Result::success() const
109 {
110  return mSuccess;
111 }
112 
113 int KXmlRpc::Result::errorCode() const
114 {
115  return mErrorCode;
116 }
117 
118 QString KXmlRpc::Result::errorString() const
119 {
120  return mErrorString;
121 }
122 
123 QList<QVariant> KXmlRpc::Result::data() const
124 {
125  return mData;
126 }
127 
128 class Query::Private
129 {
130  public:
131  Private( Query *parent )
132  : mParent( parent )
133  {
134  }
135 
136  bool isMessageResponse( const QDomDocument &doc ) const;
137  bool isFaultResponse( const QDomDocument &doc ) const;
138 
139  Result parseMessageResponse( const QDomDocument &doc ) const;
140  Result parseFaultResponse( const QDomDocument &doc ) const;
141 
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;
145 
146  void slotData( KIO::Job *job, const QByteArray &data );
147  void slotResult( KJob *job );
148 
149  Query *mParent;
150  QByteArray mBuffer;
151  QVariant mId;
152  QList<KJob*> mPendingJobs;
153 };
154 
155 bool Query::Private::isMessageResponse( const QDomDocument &doc ) const
156 {
157  return doc.documentElement().firstChild().toElement().tagName().toLower()
158  == "params";
159 }
160 
161 bool Query::Private::isFaultResponse( const QDomDocument &doc ) const
162 {
163  return doc.documentElement().firstChild().toElement().tagName().toLower()
164  == "fault";
165 }
166 
167 Result Query::Private::parseMessageResponse( const QDomDocument &doc ) const
168 {
169  Result response;
170  response.mSuccess = true;
171 
172  QDomNode paramNode = doc.documentElement().firstChild().firstChild();
173  while ( !paramNode.isNull() ) {
174  response.mData << demarshal( paramNode.firstChild().toElement() );
175  paramNode = paramNode.nextSibling();
176  }
177 
178  return response;
179 }
180 
181 Result Query::Private::parseFaultResponse( const QDomDocument &doc ) const
182 {
183  Result response;
184  response.mSuccess = false;
185 
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();
190 
191  return response;
192 }
193 
194 QString Query::Private::markupCall( const QString &cmd,
195  const QList<QVariant> &args ) const
196 {
197  QString markup = "<?xml version=\"1.0\" ?>\r\n<methodCall>\r\n";
198 
199  markup += "<methodName>" + cmd + "</methodName>\r\n";
200 
201  if ( !args.isEmpty() ) {
202 
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";
208  }
209  markup += "</params>\r\n";
210  }
211 
212  markup += "</methodCall>\r\n";
213 
214  return markup;
215 }
216 
217 QString Query::Private::marshal( const QVariant &arg ) const
218 {
219  switch ( arg.type() ) {
220 
221  case QVariant::String:
222  return "<value><string><![CDATA[" + arg.toString() + "]]></string></value>\r\n";
223  case QVariant::StringList:
224  {
225  QStringList data = arg.toStringList();
226  QStringListIterator dataIterator( data );
227  QString markup;
228  markup += "<value><array><data>";
229  while ( dataIterator.hasNext() ) {
230  markup += "<value><string><![CDATA[" + dataIterator.next() + "]]></string></value>\r\n";
231  }
232  markup += "</data></array></value>";
233  return markup;
234  }
235  case QVariant::Int:
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";
239  case QVariant::Bool:
240  {
241  QString markup = "<value><boolean>";
242  markup += arg.toBool() ? "1" : "0";
243  markup += "</boolean></value>\r\n";
244  return markup;
245  }
246  case QVariant::ByteArray:
247  return "<value><base64>" + arg.toByteArray().toBase64() + "</base64></value>\r\n";
248  case QVariant::DateTime:
249  {
250  return "<value><dateTime.iso8601>" +
251  arg.toDateTime().toString( Qt::ISODate ) +
252  "</dateTime.iso8601></value>\r\n";
253  }
254  case QVariant::List:
255  {
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 );
262  }
263  markup += "</data></array></value>\r\n";
264  return markup;
265  }
266  case QVariant::Map:
267  {
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";
277  }
278  markup += "</struct></value>\r\n";
279  return markup;
280  }
281  default:
282  kWarning() << "Failed to marshal unknown variant type:" << arg.type();
283  };
284 
285  return QString();
286 }
287 
288 QVariant Query::Private::demarshal( const QDomElement &element ) const
289 {
290  Q_ASSERT( element.tagName().toLower() == "value" );
291 
292  const QDomElement typeElement = element.firstChild().toElement();
293  const QString typeName = typeElement.tagName().toLower();
294 
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" ) {
302 
303  if ( typeElement.text().toLower() == "true" || typeElement.text() == "1" ) {
304  return QVariant( true );
305  } else {
306  return QVariant( false );
307  }
308  } else if ( typeName == "base64" ) {
309  return QVariant( QByteArray::fromBase64( typeElement.text().toLatin1() ) );
310  } else if ( typeName == "datetime" || typeName == "datetime.iso8601" ) {
311  QDateTime date;
312  QString dateText = typeElement.text();
313  // Test for broken use of Basic ISO8601 date and extended ISO8601 time
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" );
318  } else {
319  date = QDateTime::fromString( dateText, "yyyyMMddTHH:mm:ss" );
320  }
321  } else {
322  date = QDateTime::fromString( dateText, Qt::ISODate );
323  }
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();
331  }
332  return QVariant( values );
333  } else if ( typeName == "struct" ) {
334 
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() );
342  map[ key ] = data;
343  memberNode = memberNode.nextSibling();
344  }
345  return QVariant( map );
346  } else {
347  kWarning() << "Cannot demarshal unknown type" << typeName;
348  }
349  return QVariant();
350 }
351 
352 void Query::Private::slotData( KIO::Job *, const QByteArray &data )
353 {
354  unsigned int oldSize = mBuffer.size();
355  mBuffer.resize( oldSize + data.size() );
356  memcpy( mBuffer.data() + oldSize, data.data(), data.size() );
357 }
358 
359 void Query::Private::slotResult( KJob *job )
360 {
361  mPendingJobs.removeAll( job );
362 
363  if ( job->error() != 0 ) {
364  emit mParent->fault( job->error(), job->errorString(), mId );
365  emit mParent->finished( mParent );
366  return;
367  }
368 
369  QDomDocument doc;
370  QString errMsg;
371  int errLine, errCol;
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 );
376  return;
377  }
378 
379  mBuffer.truncate( 0 );
380 
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 );
386  } else {
387  emit mParent->fault( 1, i18n( "Unknown type of XML markup received" ),
388  mId );
389  }
390 
391  emit mParent->finished( mParent );
392 }
393 
394 Query *Query::create( const QVariant &id, QObject *parent )
395 {
396  return new Query( id, parent );
397 }
398 
399 void Query::call( const QString &server,
400  const QString &method,
401  const QList<QVariant> &args,
402  const QMap<QString, QString> &jobMetaData )
403 {
404 
405  const QString xmlMarkup = d->markupCall( method, args );
406 
407  QMap<QString, QString>::const_iterator mapIter;
408  QByteArray postData;
409  QDataStream stream( &postData, QIODevice::WriteOnly );
410  stream.writeRawData( xmlMarkup.toUtf8(), xmlMarkup.toUtf8().length() );
411 
412  KIO::TransferJob *job = KIO::http_post( KUrl( server ), postData, KIO::HideProgressInfo );
413 
414  if ( !job ) {
415  kWarning() << "Unable to create KIO job for" << server;
416  return;
417  }
418 
419  job->addMetaData( "content-type", "Content-Type: text/xml; charset=utf-8" );
420  job->addMetaData( "ConnectTimeout", "50" );
421 
422  for ( mapIter = jobMetaData.begin(); mapIter != jobMetaData.end(); ++mapIter ) {
423  job->addMetaData( mapIter.key(), mapIter.value() );
424  }
425 
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*)) );
430 
431  d->mPendingJobs.append( job );
432 }
433 
434 Query::Query( const QVariant &id, QObject *parent )
435  : QObject( parent ), d( new Private( this ) )
436 {
437  d->mId = id;
438 }
439 
440 Query::~Query()
441 {
442  QList<KJob*>::Iterator it;
443  for ( it = d->mPendingJobs.begin(); it != d->mPendingJobs.end(); ++it ) {
444  ( *it )->kill();
445  }
446  delete d;
447 }
448 
449 #include "moc_query.cpp"
450 
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Sat Jul 13 2013 01:26:10 by doxygen 1.8.3.1 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.10.5 API Reference

Skip menu "kdepimlibs-4.10.5 API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • 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