/* This file is part of the KDE libraries Copyright (C) 2004 Reinhold Kainhofer Based on the davjob: Copyright (C) 2002 Jan-Pascal van Best XML-RPC specific parts taken from the xmlrpciface: Copyright (C) 2003 - 2004 by Frerich Raabe Tobias Koenig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "xmlrpcjob.h" #include #include #include #include #include #include #include #define TDEIO_ARGS TQByteArray packedArgs; \ TQDataStream stream( packedArgs, IO_WriteOnly ); stream using namespace TDEIO; namespace TDEIO { class XMLRPCResult { friend class XmlrpcJob; public: XMLRPCResult() {} bool success() const { return m_success; } int errorCode() const { return m_errorCode; } TQString errorString() const { return m_errorString; } TQValueList data() const { return m_data; } private: bool m_success; int m_errorCode; TQString m_errorString; TQValueList m_data; }; } class XmlrpcJob::XmlrpcJobPrivate { public: // TQByteArray savedStaticData; }; XmlrpcJob::XmlrpcJob( const KURL& url, const TQString& method, const TQValueList ¶ms, bool showProgressInfo) : TransferJob( url, TDEIO::CMD_SPECIAL, TQByteArray(), TQByteArray(), showProgressInfo ) { d = new XmlrpcJobPrivate; // We couldn't set the args when calling the parent constructor, // so do it now. TQDataStream stream( m_packedArgs, IO_WriteOnly ); stream << (int)1 << url; kdDebug()<<"XMLrpcJob::url="<savedStaticData = staticData.copy(); } addMetaData( "UserAgent", "KDE XML-RPC TransferJob" ); addMetaData( "content-type", "Content-Type: text/xml; charset=utf-8" ); addMetaData( "ConnectTimeout", "50" ); } XmlrpcJob::~XmlrpcJob() { delete d; d = 0; } TQString XmlrpcJob::markupCall( const TQString &cmd, const TQValueList &args ) { kdDebug()<<"XmlrpcJob::markupCall, cmd="<\r\n\r\n"; markup += "" + cmd + "\r\n"; if ( !args.isEmpty() ) { markup += "\r\n"; TQValueList::ConstIterator it = args.begin(); TQValueList::ConstIterator end = args.end(); for ( ; it != end; ++it ) markup += "\r\n" + marshal( *it ) + "\r\n"; markup += "\r\n"; } markup += "\r\n"; return markup; } void XmlrpcJob::slotData( const TQByteArray& data ) { kdDebug()<<"XmlrpcJob::slotData()"<> s_cmd; istream >> s_url; istream >> s_method; // PROPFIND if ( (s_cmd == 7) && (s_method == (int)TDEIO::HTTP_POST) ) { m_packedArgs.truncate(0); TQDataStream stream( m_packedArgs, IO_WriteOnly ); stream << (int)7 << m_redirectionURL << (int)TDEIO::HTTP_POST; } } else */ kdDebug() << "\033[35;40mResult: " << m_str_response << "\033[0;0m" << endl; TQDomDocument doc; TQString errMsg; int errLine, errCol; if ( doc.setContent( m_str_response, false, &errMsg, &errLine, &errCol ) ) { if ( isMessageResponse( doc ) ) { m_response = parseMessageResponse( doc ).data(); m_responseType = XMLRPCMessageResponse; } else if ( isFaultResponse( doc ) ) { // TODO: Set the error of the job m_response.clear(); m_response << TQVariant( parseFaultResponse( doc ).errorString() ); m_responseType = XMLRPCFaultResponse; } else { // TODO: Set the error of the job m_response.clear(); m_response << TQVariant( i18n( "Unknown type of XML markup received. " "Markup: \n %1" ).arg( m_str_response ) ); m_responseType = XMLRPCUnknownResponse; } } else { // TODO: if we can't parse the XML response, set the correct error message! // emit fault( -1, i18n( "Received invalid XML markup: %1 at %2:%3" ) // .arg( errMsg ).arg( errLine ).arg( errCol ), m_id ); } TransferJob::slotFinished(); // TODO: Redirect: if( d ) staticData = d->savedStaticData.copy(); // Need to send XMLRPC request to this host too } bool XmlrpcJob::isMessageResponse( const TQDomDocument &doc ) { return doc.documentElement().firstChild().toElement() .tagName().lower() == "params"; } XMLRPCResult XmlrpcJob::parseMessageResponse( const TQDomDocument &doc ) { XMLRPCResult response; response.m_success = true; TQDomNode paramNode = doc.documentElement().firstChild().firstChild(); while ( !paramNode.isNull() ) { response.m_data << demarshal( paramNode.firstChild().toElement() ); paramNode = paramNode.nextSibling(); } return response; } bool XmlrpcJob::isFaultResponse( const TQDomDocument &doc ) { return doc.documentElement().firstChild().toElement() .tagName().lower() == "fault"; } XMLRPCResult XmlrpcJob::parseFaultResponse( const TQDomDocument &doc ) { XMLRPCResult response; response.m_success = false; TQDomNode errorNode = doc.documentElement().firstChild().firstChild(); const TQVariant errorVariant = demarshal( errorNode.toElement() ); response.m_errorCode = errorVariant.toMap() [ "faultCode" ].toInt(); response.m_errorString = errorVariant.toMap() [ "faultString" ].toString(); return response; } TQString XmlrpcJob::marshal( const TQVariant &arg ) { switch ( arg.type() ) { case TQVariant::String: case TQVariant::CString: return "" + arg.toString() + "\r\n"; case TQVariant::Int: return "" + TQString::number( arg.toInt() ) + "\r\n"; case TQVariant::Double: return "" + TQString::number( arg.toDouble() ) + "\r\n"; case TQVariant::Bool: { TQString markup = ""; markup += arg.toBool() ? "1" : "0"; markup += "\r\n"; return markup; } case TQVariant::ByteArray: return "" + KCodecs::base64Encode( arg.toByteArray() ) + "\r\n"; case TQVariant::DateTime: return "" + arg.toDateTime().toString( TQt::ISODate ) + "\r\n"; case TQVariant::List: { TQString markup = "\r\n"; const TQValueList args = arg.toList(); TQValueList::ConstIterator it = args.begin(); TQValueList::ConstIterator end = args.end(); for ( ; it != end; ++it ) markup += marshal( *it ); markup += "\r\n"; return markup; } case TQVariant::Map: { TQString markup = "\r\n"; TQMap map = arg.toMap(); TQMap::ConstIterator it = map.begin(); TQMap::ConstIterator end = map.end(); for ( ; it != end; ++it ) { markup += "\r\n"; markup += "" + it.key() + "\r\n"; markup += marshal( it.data() ); markup += "\r\n"; } markup += "\r\n"; return markup; } default: kdWarning() << "Failed to marshal unknown variant type: " << arg.type() << endl; }; return TQString(); } TQVariant XmlrpcJob::demarshal( const TQDomElement &elem ) { Q_ASSERT( elem.tagName().lower() == "value" ); if ( !elem.hasChildNodes() ) { // it doesn't have child nodes, so no explicit type name was given, // i.e. here comes the value instead of // here comes the value // Assume in that case: // Actually, the element will still have a child node, so this will not help here. // The dirty hack is at the end of this method. kdDebug()<<"XmlrpcJob::demarshal: No child nodes, assume type=string. Text: "<= 0 ) { // It's in the format 20041120T...., so adjust it to correct // ISO 8601 Format 2004-11-20T..., else TQDateTime::fromString won't work: text = text.insert( 6, '-' ); text = text.insert( 4, '-' ); } return TQVariant( TQDateTime::fromString( text, TQt::ISODate ) ); } else if ( typeName == "array" ) { TQValueList values; TQDomNode valueNode = typeElement.firstChild().firstChild(); while ( !valueNode.isNull() ) { values << demarshal( valueNode.toElement() ); valueNode = valueNode.nextSibling(); } return TQVariant( values ); } else if ( typeName == "struct" ) { TQMap map; TQDomNode memberNode = typeElement.firstChild(); while ( !memberNode.isNull() ) { const TQString key = memberNode.toElement().elementsByTagName( "name" ).item( 0 ).toElement().text(); const TQVariant data = demarshal( memberNode.toElement().elementsByTagName( "value" ).item( 0 ).toElement() ); map[ key ] = data; memberNode = memberNode.nextSibling(); } return TQVariant( map ); } else { kdWarning() << "Cannot demarshal unknown type " << typeName << ", text= " << typeElement.text() << endl; // FIXME: This is just a workaround, for the issue mentioned at the beginning of this method. return TQVariant( elem.text() ); } return TQVariant(); } /* Convenience methods */ XmlrpcJob* TDEIO::xmlrpcCall( const KURL& url, const TQString &method, const TQValueList ¶ms, bool showProgressInfo ) { if ( url.isEmpty() ) { kdWarning() << "Cannot execute call to " << method << ": empty server URL" << endl; return 0; } XmlrpcJob *job = new XmlrpcJob( url, method, params, showProgressInfo ); // job->addMetaData( "xmlrpcDepth", depth ); return job; } XmlrpcJob* TDEIO::xmlrpcCall( const KURL& url, const TQString &method, const TQVariant &arg, bool showProgressInfo ) { TQValueList args; args << arg; return TDEIO::xmlrpcCall( url, method, args, showProgressInfo ); } XmlrpcJob* TDEIO::xmlrpcCall( const KURL& url, const TQString &method, const TQStringList &arg, bool showProgressInfo ) { TQValueList args; TQStringList::ConstIterator it = arg.begin(); TQStringList::ConstIterator end = arg.end(); for ( ; it != end; ++it ) args << TQVariant( *it ); return TDEIO::xmlrpcCall( url, method, args, showProgressInfo ); } template XmlrpcJob* TDEIO::xmlrpcCall( const KURL& url, const TQString &method, const TQValueList&arg, bool showProgressInfo ) { TQValueList args; typename TQValueList::ConstIterator it = arg.begin(); typename TQValueList::ConstIterator end = arg.end(); for ( ; it != end; ++it ) args << TQVariant( *it ); return TDEIO::xmlrpcCall( url, method, args, showProgressInfo ); } #include "xmlrpcjob.moc"