TDE personal information management applications
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

465 lines
14 KiB

  1. /**************************************************************************
  2. * Copyright (C) 2003 - 2004 by Frerich Raabe <raabe@kde.org> *
  3. * Tobias Koenig <tokoe@kde.org> *
  4. * *
  5. * This program is free software; you can redistribute it and/or modify *
  6. * it under the terms of the GNU General Public License as published by *
  7. * the Free Software Foundation; either version 2 of the License, or *
  8. * (at your option) any later version. *
  9. ***************************************************************************/
  10. #include <tqfile.h>
  11. #include <kdebug.h>
  12. #include <tdeio/job.h>
  13. #include <tdelocale.h>
  14. #include <kmdcodec.h>
  15. #include "debugdialog.h"
  16. #include "xmlrpciface.h"
  17. using namespace KXMLRPC;
  18. namespace KXMLRPC
  19. {
  20. class Result
  21. {
  22. friend class Query;
  23. public:
  24. Result()
  25. { }
  26. bool success() const
  27. {
  28. return m_success;
  29. }
  30. int errorCode() const
  31. {
  32. return m_errorCode;
  33. }
  34. TQString errorString() const
  35. {
  36. return m_errorString;
  37. }
  38. TQValueList<TQVariant> data() const
  39. {
  40. return m_data;
  41. }
  42. private:
  43. bool m_success;
  44. int m_errorCode;
  45. TQString m_errorString;
  46. TQValueList<TQVariant> m_data;
  47. };
  48. }
  49. Query *Query::create( const TQVariant &id, TQObject *parent, const char *name )
  50. {
  51. return new Query( id, parent, name );
  52. }
  53. void Query::call( const TQString &server, const TQString &method,
  54. const TQValueList<TQVariant> &args, const TQString &userAgent )
  55. {
  56. const TQString xmlMarkup = markupCall( method, args );
  57. DebugDialog::addMessage( xmlMarkup, DebugDialog::Output );
  58. TQByteArray postData;
  59. TQDataStream stream( postData, IO_WriteOnly );
  60. stream.writeRawBytes( xmlMarkup.utf8(), xmlMarkup.utf8().length() );
  61. TDEIO::TransferJob *job = TDEIO::http_post( KURL( server ), postData, false );
  62. if ( !job ) {
  63. kdWarning() << "Unable to create TDEIO job for " << server << endl;
  64. return;
  65. }
  66. job->addMetaData( "UserAgent", userAgent );
  67. job->addMetaData( "content-type", "Content-Type: text/xml; charset=utf-8" );
  68. job->addMetaData( "ConnectTimeout", "50" );
  69. connect( job, TQT_SIGNAL( data( TDEIO::Job *, const TQByteArray & ) ),
  70. this, TQT_SLOT( slotData( TDEIO::Job *, const TQByteArray & ) ) );
  71. connect( job, TQT_SIGNAL( result( TDEIO::Job * ) ),
  72. this, TQT_SLOT( slotResult( TDEIO::Job * ) ) );
  73. m_pendingJobs.append( job );
  74. }
  75. void Query::slotData( TDEIO::Job *, const TQByteArray &data )
  76. {
  77. unsigned int oldSize = m_buffer.size();
  78. m_buffer.resize( oldSize + data.size() );
  79. memcpy( m_buffer.data() + oldSize, data.data(), data.size() );
  80. }
  81. void Query::slotResult( TDEIO::Job *job )
  82. {
  83. m_pendingJobs.remove( job );
  84. if ( job->error() != 0 )
  85. {
  86. emit fault( job->error(), job->errorString(), m_id );
  87. emit finished( this );
  88. return ;
  89. }
  90. TQString data = TQString::fromUtf8( m_buffer.data(), m_buffer.size() );
  91. DebugDialog::addMessage( data, DebugDialog::Input );
  92. TQDomDocument doc;
  93. TQString errMsg;
  94. int errLine, errCol;
  95. if ( !doc.setContent( data, false, &errMsg, &errLine, &errCol ) )
  96. {
  97. emit fault( -1, i18n( "Received invalid XML markup: %1 at %2:%3" )
  98. .arg( errMsg ).arg( errLine ).arg( errCol ), m_id );
  99. emit finished( this );
  100. return ;
  101. }
  102. m_buffer.truncate( 0 );
  103. if ( isMessageResponse( doc ) )
  104. emit message( parseMessageResponse( doc ).data(), m_id );
  105. else if ( isFaultResponse( doc ) )
  106. {
  107. emit fault( parseFaultResponse( doc ).errorCode(), parseFaultResponse( doc ).errorString(), m_id );
  108. }
  109. else
  110. {
  111. emit fault( 1, i18n( "Unknown type of XML markup received" ), m_id );
  112. }
  113. emit finished( this );
  114. }
  115. bool Query::isMessageResponse( const TQDomDocument &doc ) const
  116. {
  117. return doc.documentElement().firstChild().toElement().tagName().lower() == "params";
  118. }
  119. Result Query::parseMessageResponse( const TQDomDocument &doc ) const
  120. {
  121. Result response;
  122. response.m_success = true;
  123. TQDomNode paramNode = doc.documentElement().firstChild().firstChild();
  124. while ( !paramNode.isNull() )
  125. {
  126. response.m_data << demarshal( paramNode.firstChild().toElement() );
  127. paramNode = paramNode.nextSibling();
  128. }
  129. return response;
  130. }
  131. bool Query::isFaultResponse( const TQDomDocument &doc ) const
  132. {
  133. return doc.documentElement().firstChild().toElement().tagName().lower() == "fault";
  134. }
  135. Result Query::parseFaultResponse( const TQDomDocument &doc ) const
  136. {
  137. Result response;
  138. response.m_success = false;
  139. TQDomNode errorNode = doc.documentElement().firstChild().firstChild();
  140. const TQVariant errorVariant = demarshal( errorNode.toElement() );
  141. response.m_errorCode = errorVariant.toMap() [ "faultCode" ].toInt();
  142. response.m_errorString = errorVariant.toMap() [ "faultString" ].toString();
  143. return response;
  144. }
  145. TQString Query::markupCall( const TQString &cmd,
  146. const TQValueList<TQVariant> &args ) const
  147. {
  148. TQString markup = "<?xml version=\"1.0\" ?>\r\n<methodCall>\r\n";
  149. markup += "<methodName>" + cmd + "</methodName>\r\n";
  150. if ( !args.isEmpty() )
  151. {
  152. markup += "<params>\r\n";
  153. TQValueList<TQVariant>::ConstIterator it = args.begin();
  154. TQValueList<TQVariant>::ConstIterator end = args.end();
  155. for ( ; it != end; ++it )
  156. markup += "<param>\r\n" + marshal( *it ) + "</param>\r\n";
  157. markup += "</params>\r\n";
  158. }
  159. markup += "</methodCall>\r\n";
  160. return markup;
  161. }
  162. TQString Query::marshal( const TQVariant &arg ) const
  163. {
  164. switch ( arg.type() )
  165. {
  166. case TQVariant::String:
  167. case TQVariant::CString:
  168. {
  169. TQString result = arg.toString();
  170. result = result.replace( "&", "&amp;" );
  171. result = result.replace( "\"", "&quot;" );
  172. result = result.replace( "<", "&lt;" );
  173. result = result.replace( ">", "&gt;" );
  174. return "<value><string>" + result + "</string></value>\r\n";
  175. }
  176. case TQVariant::Int:
  177. return "<value><int>" + TQString::number( arg.toInt() ) + "</int></value>\r\n";
  178. case TQVariant::Double:
  179. return "<value><double>" + TQString::number( arg.toDouble() ) + "</double></value>\r\n";
  180. case TQVariant::Bool:
  181. {
  182. TQString markup = "<value><boolean>";
  183. markup += arg.toBool() ? "1" : "0";
  184. markup += "</boolean></value>\r\n";
  185. return markup;
  186. }
  187. case TQVariant::ByteArray:
  188. return "<value><base64>" + KCodecs::base64Encode( arg.toByteArray() ) + "</base64></value>\r\n";
  189. case TQVariant::DateTime:
  190. return "<value><datetime.iso8601>" + arg.toDateTime().toString( Qt::ISODate ) + "</datetime.iso8601></value>\r\n";
  191. case TQVariant::List:
  192. {
  193. TQString markup = "<value><array><data>\r\n";
  194. const TQValueList<TQVariant> args = arg.toList();
  195. TQValueList<TQVariant>::ConstIterator it = args.begin();
  196. TQValueList<TQVariant>::ConstIterator end = args.end();
  197. for ( ; it != end; ++it )
  198. markup += marshal( *it );
  199. markup += "</data></array></value>\r\n";
  200. return markup;
  201. }
  202. case TQVariant::Map:
  203. {
  204. TQString markup = "<value><struct>\r\n";
  205. TQStringVariantMap map = arg.toMap();
  206. TQStringVariantMap::ConstIterator it = map.begin();
  207. TQStringVariantMap::ConstIterator end = map.end();
  208. for ( ; it != end; ++it )
  209. {
  210. markup += "<member>\r\n";
  211. markup += "<name>" + it.key() + "</name>\r\n";
  212. markup += marshal( it.data() );
  213. markup += "</member>\r\n";
  214. }
  215. markup += "</struct></value>\r\n";
  216. return markup;
  217. }
  218. default:
  219. kdWarning() << "Failed to marshal unknown variant type: " << arg.type() << endl;
  220. };
  221. return TQString();
  222. }
  223. TQVariant Query::demarshal( const TQDomElement &elem ) const
  224. {
  225. Q_ASSERT( elem.tagName().lower() == "value" );
  226. const TQDomElement typeElement = elem.firstChild().toElement();
  227. const TQString typeName = typeElement.tagName().lower();
  228. if ( typeName == "string" )
  229. return TQVariant( typeElement.text() );
  230. else if ( typeName == "i4" || typeName == "int" )
  231. return TQVariant( typeElement.text().toInt() );
  232. else if ( typeName == "double" )
  233. return TQVariant( typeElement.text().toDouble() );
  234. else if ( typeName == "boolean" )
  235. {
  236. if ( typeElement.text().lower() == "true" || typeElement.text() == "1" )
  237. return TQVariant( true );
  238. else
  239. return TQVariant( false );
  240. }
  241. else if ( typeName == "base64" )
  242. return TQVariant( KCodecs::base64Decode( TQCString(typeElement.text().latin1()) ) );
  243. else if ( typeName == "datetime" || typeName == "datetime.iso8601" )
  244. return TQVariant( TQDateTime::fromString( typeElement.text(), Qt::ISODate ) );
  245. else if ( typeName == "array" )
  246. {
  247. TQValueList<TQVariant> values;
  248. TQDomNode valueNode = typeElement.firstChild().firstChild();
  249. while ( !valueNode.isNull() )
  250. {
  251. values << demarshal( valueNode.toElement() );
  252. valueNode = valueNode.nextSibling();
  253. }
  254. return TQVariant( values );
  255. }
  256. else if ( typeName == "struct" )
  257. {
  258. TQStringVariantMap map;
  259. TQDomNode memberNode = typeElement.firstChild();
  260. while ( !memberNode.isNull() )
  261. {
  262. const TQString key = memberNode.toElement().elementsByTagName( "name" ).item( 0 ).toElement().text();
  263. const TQVariant data = demarshal( memberNode.toElement().elementsByTagName( "value" ).item( 0 ).toElement() );
  264. map[ key ] = data;
  265. memberNode = memberNode.nextSibling();
  266. }
  267. return TQVariant( map );
  268. }
  269. else
  270. kdWarning() << "Cannot demarshal unknown type " << typeName << endl;
  271. return TQVariant();
  272. }
  273. Query::Query( const TQVariant &id, TQObject *parent, const char *name )
  274. : TQObject( parent, name ), m_id( id )
  275. {}
  276. Query::~Query()
  277. {
  278. TQValueList<TDEIO::Job*>::Iterator it;
  279. for ( it = m_pendingJobs.begin(); it != m_pendingJobs.end(); ++it )
  280. (*it)->kill();
  281. }
  282. Server::Server( const KURL &url, TQObject *parent, const char *name )
  283. : TQObject( parent, name )
  284. {
  285. if ( url.isValid() )
  286. m_url = url;
  287. m_userAgent = "KDE XMLRPC resources";
  288. DebugDialog::init();
  289. }
  290. Server::~Server()
  291. {
  292. TQValueList<Query*>::Iterator it;
  293. for ( it = mPendingQueries.begin(); it !=mPendingQueries.end(); ++it )
  294. (*it)->deleteLater();
  295. mPendingQueries.clear();
  296. }
  297. void Server::queryFinished( Query *query )
  298. {
  299. mPendingQueries.remove( query );
  300. query->deleteLater();
  301. }
  302. void Server::setUrl( const KURL &url )
  303. {
  304. m_url = url.isValid() ? url : KURL();
  305. }
  306. void Server::call( const TQString &method, const TQValueList<TQVariant> &args,
  307. TQObject* msgObj, const char* messageSlot,
  308. TQObject* faultObj, const char* faultSlot, const TQVariant &id )
  309. {
  310. if ( m_url.isEmpty() )
  311. kdWarning() << "Cannot execute call to " << method << ": empty server URL" << endl;
  312. Query *query = Query::create( id, this );
  313. connect( query, TQT_SIGNAL( message( const TQValueList<TQVariant> &, const TQVariant& ) ), msgObj, messageSlot );
  314. connect( query, TQT_SIGNAL( fault( int, const TQString&, const TQVariant& ) ), faultObj, faultSlot );
  315. connect( query, TQT_SIGNAL( finished( Query* ) ), this, TQT_SLOT( queryFinished( Query* ) ) );
  316. mPendingQueries.append( query );
  317. query->call( m_url.url(), method, args, m_userAgent );
  318. }
  319. void Server::call( const TQString &method, const TQVariant &arg,
  320. TQObject* msgObj, const char* messageSlot,
  321. TQObject* faultObj, const char* faultSlot,
  322. const TQVariant &id )
  323. {
  324. TQValueList<TQVariant> args;
  325. args << arg ;
  326. call( method, args, msgObj, messageSlot, faultObj, faultSlot, id );
  327. }
  328. void Server::call( const TQString &method, int arg,
  329. TQObject* msgObj, const char* messageSlot,
  330. TQObject* faultObj, const char* faultSlot,
  331. const TQVariant &id )
  332. {
  333. TQValueList<TQVariant> args;
  334. args << TQVariant( arg );
  335. call( method, args, msgObj, messageSlot, faultObj, faultSlot, id );
  336. }
  337. void Server::call( const TQString &method, bool arg,
  338. TQObject* msgObj, const char* messageSlot,
  339. TQObject* faultObj, const char* faultSlot,
  340. const TQVariant &id )
  341. {
  342. TQValueList<TQVariant> args;
  343. args << TQVariant( arg );
  344. call( method, args, msgObj, messageSlot, faultObj, faultSlot, id );
  345. }
  346. void Server::call( const TQString &method, double arg ,
  347. TQObject* msgObj, const char* messageSlot,
  348. TQObject* faultObj, const char* faultSlot,
  349. const TQVariant &id )
  350. {
  351. TQValueList<TQVariant> args;
  352. args << TQVariant( arg );
  353. call( method, args, msgObj, messageSlot, faultObj, faultSlot, id );
  354. }
  355. void Server::call( const TQString &method, const TQString &arg ,
  356. TQObject* msgObj, const char* messageSlot,
  357. TQObject* faultObj, const char* faultSlot,
  358. const TQVariant &id )
  359. {
  360. TQValueList<TQVariant> args;
  361. args << TQVariant( arg );
  362. call( method, args, msgObj, messageSlot, faultObj, faultSlot, id );
  363. }
  364. void Server::call( const TQString &method, const TQCString &arg,
  365. TQObject* msgObj, const char* messageSlot,
  366. TQObject* faultObj, const char* faultSlot,
  367. const TQVariant &id )
  368. {
  369. TQValueList<TQVariant> args;
  370. args << TQVariant( arg );
  371. call( method, args, msgObj, messageSlot, faultObj, faultSlot, id );
  372. }
  373. void Server::call( const TQString &method, const TQByteArray &arg ,
  374. TQObject* msgObj, const char* messageSlot,
  375. TQObject* faultObj, const char* faultSlot,
  376. const TQVariant &id )
  377. {
  378. TQValueList<TQVariant> args;
  379. args << TQVariant( arg );
  380. call( method, args, faultObj, faultSlot, msgObj, messageSlot, id );
  381. }
  382. void Server::call( const TQString &method, const TQDateTime &arg,
  383. TQObject* msgObj, const char* messageSlot,
  384. TQObject* faultObj, const char* faultSlot,
  385. const TQVariant &id )
  386. {
  387. TQValueList<TQVariant> args;
  388. args << TQVariant( arg );
  389. call( method, args, msgObj, messageSlot, faultObj, faultSlot, id );
  390. }
  391. void Server::call( const TQString &method, const TQStringList &arg,
  392. TQObject* msgObj, const char* messageSlot,
  393. TQObject* faultObj, const char* faultSlot,
  394. const TQVariant &id )
  395. {
  396. TQValueList<TQVariant> args;
  397. TQStringList::ConstIterator it = arg.begin();
  398. TQStringList::ConstIterator end = arg.end();
  399. for ( ; it != end; ++it )
  400. args << TQVariant( *it );
  401. call( method, args, msgObj, messageSlot, faultObj, faultSlot, id );
  402. }
  403. #include "xmlrpciface.moc"