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

KXMLRPC Client Library

  • sources
  • kde-4.12
  • kdepimlibs
  • 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 <klocalizedstring.h>
25 #include <kurl.h>
26 
27 #include <QtCore/QDateTime>
28 #include <QtCore/QVariant>
29 #include <QtXml/QDomDocument>
30 
31 using namespace KXmlRpc;
32 
39 namespace KXmlRpc {
40 
49 class Result
50 {
51  friend class Query;
52  friend class Query::Private;
53 
54  public:
58  Result();
59 
63  ~Result();
64 
71  bool success() const;
72 
78  int errorCode() const;
79 
85  QString errorString() const;
86 
90  QList<QVariant> data() const;
91 
92  private:
93  bool mSuccess;
94  int mErrorCode;
95  QString mErrorString;
96  QList<QVariant> mData;
97 };
98 
99 } // namespace KXmlRpcClient
100 
101 KXmlRpc::Result::Result()
102 {
103 }
104 
105 KXmlRpc::Result::~Result()
106 {
107 }
108 
109 bool KXmlRpc::Result::success() const
110 {
111  return mSuccess;
112 }
113 
114 int KXmlRpc::Result::errorCode() const
115 {
116  return mErrorCode;
117 }
118 
119 QString KXmlRpc::Result::errorString() const
120 {
121  return mErrorString;
122 }
123 
124 QList<QVariant> KXmlRpc::Result::data() const
125 {
126  return mData;
127 }
128 
129 class Query::Private
130 {
131  public:
132  Private( Query *parent )
133  : mParent( parent )
134  {
135  }
136 
137  bool isMessageResponse( const QDomDocument &doc ) const;
138  bool isFaultResponse( const QDomDocument &doc ) const;
139 
140  Result parseMessageResponse( const QDomDocument &doc ) const;
141  Result parseFaultResponse( const QDomDocument &doc ) const;
142 
143  QString markupCall( const QString &method, const QList<QVariant> &args ) const;
144  QString marshal( const QVariant &value ) const;
145  QVariant demarshal( const QDomElement &element ) const;
146 
147  void slotData( KIO::Job *job, const QByteArray &data );
148  void slotResult( KJob *job );
149 
150  Query *mParent;
151  QByteArray mBuffer;
152  QVariant mId;
153  QList<KJob*> mPendingJobs;
154 };
155 
156 bool Query::Private::isMessageResponse( const QDomDocument &doc ) const
157 {
158  return doc.documentElement().firstChild().toElement().tagName().toLower()
159  == "params";
160 }
161 
162 bool Query::Private::isFaultResponse( const QDomDocument &doc ) const
163 {
164  return doc.documentElement().firstChild().toElement().tagName().toLower()
165  == "fault";
166 }
167 
168 Result Query::Private::parseMessageResponse( const QDomDocument &doc ) const
169 {
170  Result response;
171  response.mSuccess = true;
172 
173  QDomNode paramNode = doc.documentElement().firstChild().firstChild();
174  while ( !paramNode.isNull() ) {
175  response.mData << demarshal( paramNode.firstChild().toElement() );
176  paramNode = paramNode.nextSibling();
177  }
178 
179  return response;
180 }
181 
182 Result Query::Private::parseFaultResponse( const QDomDocument &doc ) const
183 {
184  Result response;
185  response.mSuccess = false;
186 
187  QDomNode errorNode = doc.documentElement().firstChild().firstChild();
188  const QVariant errorVariant = demarshal( errorNode.toElement() );
189  response.mErrorCode = errorVariant.toMap() [ "faultCode" ].toInt();
190  response.mErrorString = errorVariant.toMap() [ "faultString" ].toString();
191 
192  return response;
193 }
194 
195 QString Query::Private::markupCall( const QString &cmd,
196  const QList<QVariant> &args ) const
197 {
198  QString markup = "<?xml version=\"1.0\" ?>\r\n<methodCall>\r\n";
199 
200  markup += "<methodName>" + cmd + "</methodName>\r\n";
201 
202  if ( !args.isEmpty() ) {
203 
204  markup += "<params>\r\n";
205  QList<QVariant>::ConstIterator it = args.begin();
206  QList<QVariant>::ConstIterator end = args.end();
207  for ( ; it != end; ++it ) {
208  markup += "<param>\r\n" + marshal( *it ) + "</param>\r\n";
209  }
210  markup += "</params>\r\n";
211  }
212 
213  markup += "</methodCall>\r\n";
214 
215  return markup;
216 }
217 
218 QString Query::Private::marshal( const QVariant &arg ) const
219 {
220  switch ( arg.type() ) {
221 
222  case QVariant::String:
223  return "<value><string><![CDATA[" + arg.toString() + "]]></string></value>\r\n";
224  case QVariant::StringList:
225  {
226  QStringList data = arg.toStringList();
227  QStringListIterator dataIterator( data );
228  QString markup;
229  markup += "<value><array><data>";
230  while ( dataIterator.hasNext() ) {
231  markup += "<value><string><![CDATA[" + dataIterator.next() + "]]></string></value>\r\n";
232  }
233  markup += "</data></array></value>";
234  return markup;
235  }
236  case QVariant::Int:
237  return "<value><int>" + QString::number( arg.toInt() ) + "</int></value>\r\n";
238  case QVariant::Double:
239  return "<value><double>" + QString::number( arg.toDouble() ) + "</double></value>\r\n";
240  case QVariant::Bool:
241  {
242  QString markup = "<value><boolean>";
243  markup += arg.toBool() ? "1" : "0";
244  markup += "</boolean></value>\r\n";
245  return markup;
246  }
247  case QVariant::ByteArray:
248  return "<value><base64>" + arg.toByteArray().toBase64() + "</base64></value>\r\n";
249  case QVariant::DateTime:
250  {
251  return "<value><dateTime.iso8601>" +
252  arg.toDateTime().toString( Qt::ISODate ) +
253  "</dateTime.iso8601></value>\r\n";
254  }
255  case QVariant::List:
256  {
257  QString markup = "<value><array><data>\r\n";
258  const QList<QVariant> args = arg.toList();
259  QList<QVariant>::ConstIterator it = args.begin();
260  QList<QVariant>::ConstIterator end = args.end();
261  for ( ; it != end; ++it ) {
262  markup += marshal( *it );
263  }
264  markup += "</data></array></value>\r\n";
265  return markup;
266  }
267  case QVariant::Map:
268  {
269  QString markup = "<value><struct>\r\n";
270  QMap<QString, QVariant> map = arg.toMap();
271  QMap<QString, QVariant>::ConstIterator it = map.constBegin();
272  QMap<QString, QVariant>::ConstIterator end = map.constEnd();
273  for ( ; it != end; ++it ) {
274  markup += "<member>\r\n";
275  markup += "<name>" + it.key() + "</name>\r\n";
276  markup += marshal( it.value() );
277  markup += "</member>\r\n";
278  }
279  markup += "</struct></value>\r\n";
280  return markup;
281  }
282  default:
283  kWarning() << "Failed to marshal unknown variant type:" << arg.type();
284  };
285 
286  return QString();
287 }
288 
289 QVariant Query::Private::demarshal( const QDomElement &element ) const
290 {
291  Q_ASSERT( element.tagName().toLower() == "value" );
292 
293  const QDomElement typeElement = element.firstChild().toElement();
294  const QString typeName = typeElement.tagName().toLower();
295 
296  if ( typeName == "string" ) {
297  return QVariant( typeElement.text() );
298  } else if ( typeName == "i4" || typeName == "int" ) {
299  return QVariant( typeElement.text().toInt() );
300  } else if ( typeName == "double" ) {
301  return QVariant( typeElement.text().toDouble() );
302  } else if ( typeName == "boolean" ) {
303 
304  if ( typeElement.text().toLower() == "true" || typeElement.text() == "1" ) {
305  return QVariant( true );
306  } else {
307  return QVariant( false );
308  }
309  } else if ( typeName == "base64" ) {
310  return QVariant( QByteArray::fromBase64( typeElement.text().toLatin1() ) );
311  } else if ( typeName == "datetime" || typeName == "datetime.iso8601" ) {
312  QDateTime date;
313  QString dateText = typeElement.text();
314  // Test for broken use of Basic ISO8601 date and extended ISO8601 time
315  if ( 17 <= dateText.length() && dateText.length() <= 18 &&
316  dateText.at( 4 ) != '-' && dateText.at( 11 ) == ':' ) {
317  if ( dateText.endsWith( 'Z' ) ) {
318  date = QDateTime::fromString( dateText, "yyyyMMddTHH:mm:ssZ" );
319  } else {
320  date = QDateTime::fromString( dateText, "yyyyMMddTHH:mm:ss" );
321  }
322  } else {
323  date = QDateTime::fromString( dateText, Qt::ISODate );
324  }
325  return QVariant( date );
326  } else if ( typeName == "array" ) {
327  QList<QVariant> values;
328  QDomNode valueNode = typeElement.firstChild().firstChild();
329  while ( !valueNode.isNull() ) {
330  values << demarshal( valueNode.toElement() );
331  valueNode = valueNode.nextSibling();
332  }
333  return QVariant( values );
334  } else if ( typeName == "struct" ) {
335 
336  QMap<QString, QVariant> map;
337  QDomNode memberNode = typeElement.firstChild();
338  while ( !memberNode.isNull() ) {
339  const QString key = memberNode.toElement().elementsByTagName(
340  "name" ).item( 0 ).toElement().text();
341  const QVariant data = demarshal( memberNode.toElement().elementsByTagName(
342  "value" ).item( 0 ).toElement() );
343  map[ key ] = data;
344  memberNode = memberNode.nextSibling();
345  }
346  return QVariant( map );
347  } else {
348  kWarning() << "Cannot demarshal unknown type" << typeName;
349  }
350  return QVariant();
351 }
352 
353 void Query::Private::slotData( KIO::Job *, const QByteArray &data )
354 {
355  unsigned int oldSize = mBuffer.size();
356  mBuffer.resize( oldSize + data.size() );
357  memcpy( mBuffer.data() + oldSize, data.data(), data.size() );
358 }
359 
360 void Query::Private::slotResult( KJob *job )
361 {
362  mPendingJobs.removeAll( job );
363 
364  if ( job->error() != 0 ) {
365  emit mParent->fault( job->error(), job->errorString(), mId );
366  emit mParent->finished( mParent );
367  return;
368  }
369 
370  QDomDocument doc;
371  QString errMsg;
372  int errLine, errCol;
373  if ( !doc.setContent( mBuffer, false, &errMsg, &errLine, &errCol ) ) {
374  emit mParent->fault( -1, i18n( "Received invalid XML markup: %1 at %2:%3",
375  errMsg, errLine, errCol ), mId );
376  emit mParent->finished( mParent );
377  return;
378  }
379 
380  mBuffer.truncate( 0 );
381 
382  if ( isMessageResponse( doc ) ) {
383  emit mParent->message( parseMessageResponse( doc ).data(), mId );
384  } else if ( isFaultResponse( doc ) ) {
385  emit mParent->fault( parseFaultResponse( doc ).errorCode(),
386  parseFaultResponse( doc ).errorString(), mId );
387  } else {
388  emit mParent->fault( 1, i18n( "Unknown type of XML markup received" ),
389  mId );
390  }
391 
392  emit mParent->finished( mParent );
393 }
394 
395 Query *Query::create( const QVariant &id, QObject *parent )
396 {
397  return new Query( id, parent );
398 }
399 
400 void Query::call( const QString &server,
401  const QString &method,
402  const QList<QVariant> &args,
403  const QMap<QString, QString> &jobMetaData )
404 {
405 
406  const QString xmlMarkup = d->markupCall( method, args );
407 
408  QMap<QString, QString>::const_iterator mapIter;
409  QByteArray postData;
410  QDataStream stream( &postData, QIODevice::WriteOnly );
411  stream.writeRawData( xmlMarkup.toUtf8(), xmlMarkup.toUtf8().length() );
412 
413  KIO::TransferJob *job = KIO::http_post( KUrl( server ), postData, KIO::HideProgressInfo );
414 
415  if ( !job ) {
416  kWarning() << "Unable to create KIO job for" << server;
417  return;
418  }
419 
420  job->addMetaData( "content-type", "Content-Type: text/xml; charset=utf-8" );
421  job->addMetaData( "ConnectTimeout", "50" );
422 
423  for ( mapIter = jobMetaData.begin(); mapIter != jobMetaData.end(); ++mapIter ) {
424  job->addMetaData( mapIter.key(), mapIter.value() );
425  }
426 
427  connect( job, SIGNAL(data(KIO::Job*,QByteArray)),
428  this, SLOT(slotData(KIO::Job*,QByteArray)) );
429  connect( job, SIGNAL(result(KJob*)),
430  this, SLOT(slotResult(KJob*)) );
431 
432  d->mPendingJobs.append( job );
433 }
434 
435 Query::Query( const QVariant &id, QObject *parent )
436  : QObject( parent ), d( new Private( this ) )
437 {
438  d->mId = id;
439 }
440 
441 Query::~Query()
442 {
443  QList<KJob*>::Iterator it;
444  for ( it = d->mPendingJobs.begin(); it != d->mPendingJobs.end(); ++it ) {
445  ( *it )->kill();
446  }
447  delete d;
448 }
449 
450 #include "moc_query.cpp"
451 
query.h
This file is part of KXmlRpc and defines our internal classes.
KXmlRpc::Query
Query is a class that represents an individual XML-RPC call.
Definition: query.h:44
KXmlRpc::Query::call
void call(const QString &server, const QString &method, const QList< QVariant > &args, const QMap< QString, QString > &jobMetaData)
Calls the specified method on the specified server with the given argument list.
Definition: query.cpp:400
KXmlRpc::Query::create
static Query * create(const QVariant &id=QVariant(), QObject *parent=0)
Constructs a query.
Definition: query.cpp:395
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 23:00:17 by doxygen 1.8.7 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 API Reference

Skip menu "kdepimlibs API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kldap
  • kmbox
  • kmime
  • kpimidentities
  • kpimtextedit
  • kresources
  • ktnef
  • kxmlrpcclient
  • microblog

Search



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