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.
 
 
 
 
 
 

431 lines
14 KiB

  1. /* This file is part of the KDE libraries
  2. Copyright (C) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
  3. Based on the davjob:
  4. Copyright (C) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
  5. XML-RPC specific parts taken from the xmlrpciface:
  6. Copyright (C) 2003 - 2004 by Frerich Raabe <raabe@kde.org>
  7. Tobias Koenig <tokoe@kde.org>
  8. This library is free software; you can redistribute it and/or
  9. modify it under the terms of the GNU Library General Public
  10. License as published by the Free Software Foundation; either
  11. version 2 of the License, or (at your option) any later version.
  12. This library is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. Library General Public License for more details.
  16. You should have received a copy of the GNU Library General Public License
  17. along with this library; see the file COPYING.LIB. If not, write to
  18. the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  19. Boston, MA 02110-1301, USA.
  20. */
  21. #include "xmlrpcjob.h"
  22. #include <tqvariant.h>
  23. #include <tqregexp.h>
  24. #include <kdebug.h>
  25. #include <tdelocale.h>
  26. #include <tdeio/http.h>
  27. #include <kmdcodec.h>
  28. #include <tdeio/davjob.h>
  29. #define TDEIO_ARGS TQByteArray packedArgs; \
  30. TQDataStream stream( packedArgs, IO_WriteOnly ); stream
  31. using namespace TDEIO;
  32. namespace TDEIO {
  33. class XMLRPCResult
  34. {
  35. friend class XmlrpcJob;
  36. public:
  37. XMLRPCResult() {}
  38. bool success() const { return m_success; }
  39. int errorCode() const { return m_errorCode; }
  40. TQString errorString() const { return m_errorString; }
  41. TQValueList<TQVariant> data() const { return m_data; }
  42. private:
  43. bool m_success;
  44. int m_errorCode;
  45. TQString m_errorString;
  46. TQValueList<TQVariant> m_data;
  47. };
  48. }
  49. class XmlrpcJob::XmlrpcJobPrivate
  50. {
  51. public:
  52. // TQByteArray savedStaticData;
  53. };
  54. XmlrpcJob::XmlrpcJob( const KURL& url, const TQString& method,
  55. const TQValueList<TQVariant> &params, bool showProgressInfo)
  56. : TransferJob( url, TDEIO::CMD_SPECIAL, TQByteArray(), TQByteArray(),
  57. showProgressInfo )
  58. {
  59. d = new XmlrpcJobPrivate;
  60. // We couldn't set the args when calling the parent constructor,
  61. // so do it now.
  62. TQDataStream stream( m_packedArgs, IO_WriteOnly );
  63. stream << (int)1 << url;
  64. kdDebug()<<"XMLrpcJob::url="<<url.url()<<endl;
  65. kdDebug()<<"XmlrpcJob::XmlrpcJob, method="<<method<<endl;
  66. // Same for static data
  67. if ( ! method.isEmpty() ) {
  68. kdDebug()<<"XmlrpcJob::XmlrpcJob, method not empty."<<endl;
  69. TQString call = markupCall( method, params );
  70. staticData = call.utf8();
  71. staticData.truncate( staticData.size() - 1 );
  72. kdDebug() << "Message: " << call << endl;
  73. // d->savedStaticData = staticData.copy();
  74. }
  75. addMetaData( "UserAgent", "KDE XML-RPC TransferJob" );
  76. addMetaData( "content-type", "Content-Type: text/xml; charset=utf-8" );
  77. addMetaData( "ConnectTimeout", "50" );
  78. }
  79. XmlrpcJob::~XmlrpcJob()
  80. {
  81. delete d;
  82. d = 0;
  83. }
  84. TQString XmlrpcJob::markupCall( const TQString &cmd,
  85. const TQValueList<TQVariant> &args )
  86. {
  87. kdDebug()<<"XmlrpcJob::markupCall, cmd="<<cmd<<endl;
  88. TQString markup = "<?xml version=\"1.0\" ?>\r\n<methodCall>\r\n";
  89. markup += "<methodName>" + cmd + "</methodName>\r\n";
  90. if ( !args.isEmpty() )
  91. {
  92. markup += "<params>\r\n";
  93. TQValueList<TQVariant>::ConstIterator it = args.begin();
  94. TQValueList<TQVariant>::ConstIterator end = args.end();
  95. for ( ; it != end; ++it )
  96. markup += "<param>\r\n" + marshal( *it ) + "</param>\r\n";
  97. markup += "</params>\r\n";
  98. }
  99. markup += "</methodCall>\r\n";
  100. return markup;
  101. }
  102. void XmlrpcJob::slotData( const TQByteArray& data )
  103. {
  104. kdDebug()<<"XmlrpcJob::slotData()"<<endl;
  105. if ( m_redirectionURL.isEmpty() || !m_redirectionURL.isValid() || m_error )
  106. m_str_response.append( TQString( data ) );
  107. }
  108. void XmlrpcJob::slotFinished()
  109. {
  110. kdDebug() << "XmlrpcJob::slotFinished()" << endl;
  111. kdDebug() << m_str_response << endl;
  112. // TODO: Redirection with XML-RPC??
  113. /* if (! m_redirectionURL.isEmpty() && m_redirectionURL.isValid() ) {
  114. TQDataStream istream( m_packedArgs, IO_ReadOnly );
  115. int s_cmd, s_method;
  116. KURL s_url;
  117. istream >> s_cmd;
  118. istream >> s_url;
  119. istream >> s_method;
  120. // PROPFIND
  121. if ( (s_cmd == 7) && (s_method == (int)TDEIO::HTTP_POST) ) {
  122. m_packedArgs.truncate(0);
  123. TQDataStream stream( m_packedArgs, IO_WriteOnly );
  124. stream << (int)7 << m_redirectionURL << (int)TDEIO::HTTP_POST;
  125. }
  126. } else */
  127. kdDebug() << "\033[35;40mResult: " << m_str_response << "\033[0;0m" << endl;
  128. TQDomDocument doc;
  129. TQString errMsg;
  130. int errLine, errCol;
  131. if ( doc.setContent( m_str_response, false, &errMsg, &errLine, &errCol ) ) {
  132. if ( isMessageResponse( doc ) ) {
  133. m_response = parseMessageResponse( doc ).data();
  134. m_responseType = XMLRPCMessageResponse;
  135. } else if ( isFaultResponse( doc ) ) {
  136. // TODO: Set the error of the job
  137. m_response.clear();
  138. m_response << TQVariant( parseFaultResponse( doc ).errorString() );
  139. m_responseType = XMLRPCFaultResponse;
  140. } else {
  141. // TODO: Set the error of the job
  142. m_response.clear();
  143. m_response << TQVariant( i18n( "Unknown type of XML markup received. "
  144. "Markup: \n %1" ).arg( m_str_response ) );
  145. m_responseType = XMLRPCUnknownResponse;
  146. }
  147. } else {
  148. // TODO: if we can't parse the XML response, set the correct error message!
  149. // emit fault( -1, i18n( "Received invalid XML markup: %1 at %2:%3" )
  150. // .arg( errMsg ).arg( errLine ).arg( errCol ), m_id );
  151. }
  152. TransferJob::slotFinished();
  153. // TODO: Redirect: if( d ) staticData = d->savedStaticData.copy();
  154. // Need to send XMLRPC request to this host too
  155. }
  156. bool XmlrpcJob::isMessageResponse( const TQDomDocument &doc )
  157. {
  158. return doc.documentElement().firstChild().toElement()
  159. .tagName().lower() == "params";
  160. }
  161. XMLRPCResult XmlrpcJob::parseMessageResponse( const TQDomDocument &doc )
  162. {
  163. XMLRPCResult response;
  164. response.m_success = true;
  165. TQDomNode paramNode = doc.documentElement().firstChild().firstChild();
  166. while ( !paramNode.isNull() ) {
  167. response.m_data << demarshal( paramNode.firstChild().toElement() );
  168. paramNode = paramNode.nextSibling();
  169. }
  170. return response;
  171. }
  172. bool XmlrpcJob::isFaultResponse( const TQDomDocument &doc )
  173. {
  174. return doc.documentElement().firstChild().toElement()
  175. .tagName().lower() == "fault";
  176. }
  177. XMLRPCResult XmlrpcJob::parseFaultResponse( const TQDomDocument &doc )
  178. {
  179. XMLRPCResult response;
  180. response.m_success = false;
  181. TQDomNode errorNode = doc.documentElement().firstChild().firstChild();
  182. const TQVariant errorVariant = demarshal( errorNode.toElement() );
  183. response.m_errorCode = errorVariant.toMap() [ "faultCode" ].toInt();
  184. response.m_errorString = errorVariant.toMap() [ "faultString" ].toString();
  185. return response;
  186. }
  187. TQString XmlrpcJob::marshal( const TQVariant &arg )
  188. {
  189. switch ( arg.type() )
  190. {
  191. case TQVariant::String:
  192. case TQVariant::CString:
  193. return "<value><string>" + arg.toString() + "</string></value>\r\n";
  194. case TQVariant::Int:
  195. return "<value><int>" + TQString::number( arg.toInt() ) +
  196. "</int></value>\r\n";
  197. case TQVariant::Double:
  198. return "<value><double>" + TQString::number( arg.toDouble() ) +
  199. "</double></value>\r\n";
  200. case TQVariant::Bool:
  201. {
  202. TQString markup = "<value><boolean>";
  203. markup += arg.toBool() ? "1" : "0";
  204. markup += "</boolean></value>\r\n";
  205. return markup;
  206. }
  207. case TQVariant::ByteArray:
  208. return "<value><base64>" + KCodecs::base64Encode( arg.toByteArray() ) +
  209. "</base64></value>\r\n";
  210. case TQVariant::DateTime:
  211. return "<value><datetime.iso8601>" +
  212. arg.toDateTime().toString( TQt::ISODate ) +
  213. "</datetime.iso8601></value>\r\n";
  214. case TQVariant::List:
  215. {
  216. TQString markup = "<value><array><data>\r\n";
  217. const TQValueList<TQVariant> args = arg.toList();
  218. TQValueList<TQVariant>::ConstIterator it = args.begin();
  219. TQValueList<TQVariant>::ConstIterator end = args.end();
  220. for ( ; it != end; ++it )
  221. markup += marshal( *it );
  222. markup += "</data></array></value>\r\n";
  223. return markup;
  224. }
  225. case TQVariant::Map:
  226. {
  227. TQString markup = "<value><struct>\r\n";
  228. TQStringVariantMap map = arg.toMap();
  229. TQStringVariantMap::ConstIterator it = map.begin();
  230. TQStringVariantMap::ConstIterator end = map.end();
  231. for ( ; it != end; ++it )
  232. {
  233. markup += "<member>\r\n";
  234. markup += "<name>" + it.key() + "</name>\r\n";
  235. markup += marshal( it.data() );
  236. markup += "</member>\r\n";
  237. }
  238. markup += "</struct></value>\r\n";
  239. return markup;
  240. }
  241. default:
  242. kdWarning() << "Failed to marshal unknown variant type: "
  243. << arg.type() << endl;
  244. };
  245. return TQString();
  246. }
  247. TQVariant XmlrpcJob::demarshal( const TQDomElement &elem )
  248. {
  249. Q_ASSERT( elem.tagName().lower() == "value" );
  250. if ( !elem.hasChildNodes() ) {
  251. // it doesn't have child nodes, so no explicit type name was given,
  252. // i.e. <value>here comes the value</value> instead of
  253. // <value><string>here comes the value</string></value>
  254. // Assume <string> in that case:
  255. // Actually, the element will still have a child node, so this will not help here.
  256. // The dirty hack is at the end of this method.
  257. kdDebug()<<"XmlrpcJob::demarshal: No child nodes, assume type=string. Text: "<<elem.text()<<endl;
  258. return TQVariant( elem.text() );
  259. }
  260. kdDebug()<<"Demarshalling element \"" << elem.text() <<"\"" << endl;
  261. const TQDomElement typeElement = elem.firstChild().toElement();
  262. const TQString typeName = typeElement.tagName().lower();
  263. if ( typeName == "string" )
  264. return TQVariant( typeElement.text() );
  265. else if ( typeName == "i4" || typeName == "int" )
  266. return TQVariant( typeElement.text().toInt() );
  267. else if ( typeName == "double" )
  268. return TQVariant( typeElement.text().toDouble() );
  269. else if ( typeName == "boolean" )
  270. {
  271. if ( typeElement.text().lower() == "true" || typeElement.text() == "1" )
  272. return TQVariant( true );
  273. else
  274. return TQVariant( false );
  275. }
  276. else if ( typeName == "base64" )
  277. return TQVariant( KCodecs::base64Decode( typeElement.text().latin1() ) );
  278. else if ( typeName == "datetime" || typeName == "datetime.iso8601" ) {
  279. TQString text( typeElement.text() );
  280. if ( text.find( TQRegExp("^[0-9]{8,8}T") ) >= 0 ) {
  281. // It's in the format 20041120T...., so adjust it to correct
  282. // ISO 8601 Format 2004-11-20T..., else TQDateTime::fromString won't work:
  283. text = text.insert( 6, '-' );
  284. text = text.insert( 4, '-' );
  285. }
  286. return TQVariant( TQDateTime::fromString( text, TQt::ISODate ) );
  287. } else if ( typeName == "array" ) {
  288. TQValueList<TQVariant> values;
  289. TQDomNode valueNode = typeElement.firstChild().firstChild();
  290. while ( !valueNode.isNull() ) {
  291. values << demarshal( valueNode.toElement() );
  292. valueNode = valueNode.nextSibling();
  293. }
  294. return TQVariant( values );
  295. } else if ( typeName == "struct" ) {
  296. TQStringVariantMap map;
  297. TQDomNode memberNode = typeElement.firstChild();
  298. while ( !memberNode.isNull() ) {
  299. const TQString key = memberNode.toElement().elementsByTagName( "name" ).item( 0 ).toElement().text();
  300. const TQVariant data = demarshal( memberNode.toElement().elementsByTagName( "value" ).item( 0 ).toElement() );
  301. map[ key ] = data;
  302. memberNode = memberNode.nextSibling();
  303. }
  304. return TQVariant( map );
  305. } else {
  306. kdWarning() << "Cannot demarshal unknown type " << typeName << ", text= " << typeElement.text() << endl;
  307. // FIXME: This is just a workaround, for the issue mentioned at the beginning of this method.
  308. return TQVariant( elem.text() );
  309. }
  310. return TQVariant();
  311. }
  312. /* Convenience methods */
  313. XmlrpcJob* TDEIO::xmlrpcCall( const KURL& url, const TQString &method, const TQValueList<TQVariant> &params, bool showProgressInfo )
  314. {
  315. if ( url.isEmpty() ) {
  316. kdWarning() << "Cannot execute call to " << method << ": empty server URL" << endl;
  317. return 0;
  318. }
  319. XmlrpcJob *job = new XmlrpcJob( url, method, params, showProgressInfo );
  320. // job->addMetaData( "xmlrpcDepth", depth );
  321. return job;
  322. }
  323. XmlrpcJob* TDEIO::xmlrpcCall( const KURL& url, const TQString &method,
  324. const TQVariant &arg, bool showProgressInfo )
  325. {
  326. TQValueList<TQVariant> args;
  327. args << arg;
  328. return TDEIO::xmlrpcCall( url, method, args, showProgressInfo );
  329. }
  330. XmlrpcJob* TDEIO::xmlrpcCall( const KURL& url, const TQString &method,
  331. const TQStringList &arg, bool showProgressInfo )
  332. {
  333. TQValueList<TQVariant> args;
  334. TQStringList::ConstIterator it = arg.begin();
  335. TQStringList::ConstIterator end = arg.end();
  336. for ( ; it != end; ++it )
  337. args << TQVariant( *it );
  338. return TDEIO::xmlrpcCall( url, method, args, showProgressInfo );
  339. }
  340. template <typename T>
  341. XmlrpcJob* TDEIO::xmlrpcCall( const KURL& url, const TQString &method,
  342. const TQValueList<T>&arg, bool showProgressInfo )
  343. {
  344. TQValueList<TQVariant> args;
  345. typename TQValueList<T>::ConstIterator it = arg.begin();
  346. typename TQValueList<T>::ConstIterator end = arg.end();
  347. for ( ; it != end; ++it )
  348. args << TQVariant( *it );
  349. return TDEIO::xmlrpcCall( url, method, args, showProgressInfo );
  350. }
  351. #include "xmlrpcjob.moc"