From 5b0870ddaac421af2639058648a218c7061cdd6f Mon Sep 17 00:00:00 2001 From: Ian Geiser Date: Sun, 8 Nov 2009 22:02:28 +0000 Subject: Added start of XEP-009. This is only the inteface, being able to invoke remote methods is next. --- source/xmlrpc.cpp | 470 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 470 insertions(+) create mode 100644 source/xmlrpc.cpp (limited to 'source/xmlrpc.cpp') diff --git a/source/xmlrpc.cpp b/source/xmlrpc.cpp new file mode 100644 index 00000000..2ed2a6e2 --- /dev/null +++ b/source/xmlrpc.cpp @@ -0,0 +1,470 @@ +#include +#include +#include +#include +#include +#include + +XMLRPC::RequestMessage::RequestMessage( const QByteArray &method, const QList &args ) +: MessageBase() +{ + m_method = method; + m_args = args; +} + +XMLRPC::RequestMessage::RequestMessage( const QByteArray &method, const QVariant &arg ) +: MessageBase() +{ + m_method = method; + m_args.append(arg); +} + +void XMLRPC::RequestMessage::writeXml( QXmlStreamWriter *writer ) const +{ + writer->writeStartElement("methodCall"); + writer->writeTextElement("methodName", m_method ); + if( !m_args.isEmpty() ) + { + writer->writeStartElement("params"); + foreach( QVariant arg, m_args) + { + writer->writeStartElement("param"); + marshall( writer, arg ); + writer->writeEndElement(); + } + writer->writeEndElement(); + } + writer->writeEndElement(); + +} + +QByteArray XMLRPC::RequestMessage::xml() const +{ + if( m_method.isEmpty() ) + return QByteArray(); + + QByteArray returnXML; + QXmlStreamWriter writer( &returnXML ); + writer.writeStartDocument(); + writeXml(&writer ); + writer.writeEndDocument(); + return returnXML; +} + +void XMLRPC::MessageBase::marshall( QXmlStreamWriter *writer, const QVariant &value ) const +{ + writer->writeStartElement("value"); + switch( value.type() ) + { + case QVariant::Int: + case QVariant::UInt: + case QVariant::LongLong: + case QVariant::ULongLong: + writer->writeTextElement("i4", value.toString()); + break; + case QVariant::Double: + writer->writeTextElement("double", value.toString()); + break; + case QVariant::Bool: + writer->writeTextElement("boolean", (value.toBool()?"true":"false") ); + break; + case QVariant::Date: + writer->writeTextElement("dateTime.iso8601", value.toDate().toString( Qt::ISODate ) ); + break; + case QVariant::DateTime: + writer->writeTextElement("dateTime.iso8601", value.toDateTime().toString( Qt::ISODate ) ); + break; + case QVariant::Time: + writer->writeTextElement("dateTime.iso8601", value.toTime().toString( Qt::ISODate ) ); + break; + case QVariant::StringList: + case QVariant::List: + { + writer->writeStartElement("array"); + writer->writeStartElement("data"); + foreach( QVariant item, value.toList() ) + marshall( writer, item ); + writer->writeEndElement(); + writer->writeEndElement(); + break; + } + case QVariant::Map: + { + writer->writeStartElement("struct"); + QMap map = value.toMap(); + QMap::ConstIterator index = map.begin(); + while( index != map.end() ) + { + writer->writeStartElement("member"); + writer->writeTextElement("name", index.key()); + marshall( writer, *index ); + writer->writeEndElement(); + ++index; + } + writer->writeEndElement(); + break; + } + case QVariant::ByteArray: + { + writer->writeTextElement("base64", value.toByteArray().toBase64() ); + break; + } + default: + { + if( value.canConvert(QVariant::String) ) + { + writer->writeTextElement( "string", value.toString() ); + } + else + { + + } + break; + } + } + writer->writeEndElement(); + } +XMLRPC::ResponseMessage::ResponseMessage( const QDomElement &element ) +{ + const QDomElement contents = element.firstChild().toElement(); + if( contents.tagName().toLower() == "params") + { + QDomNode param = contents.firstChild(); + while( !param.isNull() && isValid() ) + { + m_values.append( demarshall( param.firstChild().toElement() ) ); + param = param.nextSibling(); + } + } + else if( contents.tagName().toLower() == "fault") + { + const QDomElement errElement = contents.firstChild().toElement(); + const QVariant error = demarshall( errElement ); + + setError( QString("XMLRPC Fault %1: %2") + .arg(error.toMap()["faultCode"].toString() ) + .arg(error.toMap()["faultString"].toString() ) ); + } + else + { + setError("Bad XML response"); + } +} + +XMLRPC::ResponseMessage::ResponseMessage( const QByteArray &xml ) +: MessageBase() +{ + QDomDocument message; + QString xmlErrMsg; + int xmlErrLine; + int xmlErrCol; + if( message.setContent(xml, &xmlErrMsg, &xmlErrLine, &xmlErrCol) ) + { + const QDomElement contents = message.documentElement().firstChild().toElement(); + if( contents.tagName().toLower() == "params") + { + QDomNode param = contents.firstChild(); + while( !param.isNull() && isValid() ) + { + m_values.append( demarshall( param.firstChild().toElement() ) ); + param = param.nextSibling(); + } + } + else if( message.documentElement().firstChild().toElement().tagName().toLower() == "fault") + { + const QDomElement errElement = contents.firstChild().toElement(); + const QVariant error = demarshall( errElement ); + + setError( QString("XMLRPC Fault %1: %2") + .arg(error.toMap()["faultCode"].toString() ) + .arg(error.toMap()["faultString"].toString() ) ); + } + else + { + setError("Bad XML response"); + } + } + else + { + setError(QString( "XML Error: %1 at row %2 and col %3") + .arg(xmlErrMsg).arg(xmlErrLine).arg(xmlErrCol)); + } +} + +int XMLRPC::ResponseMessage::count() const +{ + return m_values.count(); +} + +QVariant XMLRPC::ResponseMessage::value( int index) const +{ + return m_values[index]; +} + +bool XMLRPC::MessageBase::isValid() const +{ + return m_valid; +} + +QString XMLRPC::MessageBase::error() const +{ + return m_message; +} + +QVariant XMLRPC::MessageBase::demarshall( const QDomElement &elem ) const +{ + if ( elem.tagName().toLower() != "value" ) + { + m_valid = false; + m_message = "bad param value"; + return QVariant(); + } + + if ( !elem.firstChild().isElement() ) + { + return QVariant( elem.text() ); + } + + const QDomElement typeData = elem.firstChild().toElement(); + const QString typeName = typeData.tagName().toLower(); + + if ( typeName == "string" ) + return QVariant( typeData.text() ); + else if (typeName == "int" || typeName == "i4" ) + { + bool ok = false; + QVariant val( typeData.text().toInt( &ok ) ); + if( ok ) + return val; + m_message = "I was looking for an integer but data was courupt"; + } + else if( typeName == "double" ) + { + bool ok = false; + QVariant val( typeData.text().toDouble( &ok ) ); + if( ok ) + return val; + m_message = "I was looking for an double but data was courupt"; + } + else if( typeName == "boolean" ) + return QVariant( ( typeData.text().toLower() == "true" || typeData.text() == "1")?true:false ); + else if( typeName == "datetime" || typeName == "dateTime.iso8601" ) + return QVariant( QDateTime::fromString( typeData.text(), Qt::ISODate ) ); + else if( typeName == "array" ) + { + QList arr; + QDomNode valueNode = typeData.firstChild().firstChild(); + while( !valueNode.isNull() && m_valid ) + { + arr.append( demarshall( valueNode.toElement() ) ); + valueNode = valueNode.nextSibling(); + } + return QVariant( arr ); + } + else if( typeName == "struct" ) + { + QMap stct; + QDomNode valueNode = typeData.firstChild(); + while( !valueNode.isNull() && m_valid ) + { + const QDomElement memberNode = valueNode.toElement().elementsByTagName("name").item(0).toElement(); + const QDomElement dataNode = valueNode.toElement().elementsByTagName("value").item(0).toElement(); + stct[ memberNode.text() ] = demarshall( dataNode ); + valueNode = valueNode.nextSibling(); + } + return QVariant(stct); + } + else if( typeName == "base64" ) + { + QVariant returnVariant; + QByteArray dest; + QByteArray src = typeData.text().toLatin1(); + dest = QByteArray::fromBase64( src ); + QDataStream ds(&dest, QIODevice::ReadOnly); + ds.setVersion(QDataStream::Qt_4_0); + ds >> returnVariant; + if( returnVariant.isValid() ) + return returnVariant; + else + return QVariant( dest ); + } + setError(QString( "Cannot handle type %1").arg(typeName)); + return QVariant(); +} + +void XMLRPC::MessageBase::setError( const QString & message ) const +{ + m_valid = false; + m_message = message; +} + +XMLRPC::MessageBase::MessageBase( ) : m_valid(true) +{ +} + +XMLRPC::MessageBase::~MessageBase( ) +{ +} + + +QList< QVariant > XMLRPC::ResponseMessage::values() const +{ + return m_values; +} + +QList< QVariant > XMLRPC::RequestMessage::args() const +{ + return m_args; +} + +QByteArray XMLRPC::RequestMessage::method() const +{ + return m_method; +} + +XMLRPC::RequestMessage::RequestMessage( const QDomElement &element ) +{ + m_args.clear(); + m_method.clear(); + + const QDomElement methodName = element.firstChildElement("methodName"); + if( !methodName.isNull() ) + { + m_method = methodName.text().toLatin1(); + } + else + { + setError("Missing methodName property."); + return; + } + + const QDomElement methodParams = element.firstChildElement("params"); + if( !methodParams.isNull() ) + { + QDomNode param = methodParams.firstChild(); + while( !param.isNull() && isValid() ) + { + m_args.append( demarshall( param.firstChild().toElement() ) ); + param = param.nextSibling(); + } + } +} + +XMLRPC::RequestMessage::RequestMessage( const QByteArray & xml ) +{ + QDomDocument message; + QString xmlErrMsg; + int xmlErrLine; + int xmlErrCol; + m_args.clear(); + m_method.clear(); + + if( message.setContent(xml, &xmlErrMsg, &xmlErrLine, &xmlErrCol) ) + { + const QDomElement methodCall = message.firstChildElement("methodCall"); + if( !methodCall.isNull() ) + { + const QDomElement methodName = methodCall.firstChildElement("methodName"); + if( !methodName.isNull() ) + { + m_method = methodName.text().toLatin1(); + } + else + { + setError("Missing methodName property."); + return; + } + + const QDomElement methodParams = methodCall.firstChildElement("params"); + if( !methodParams.isNull() ) + { + QDomNode param = methodParams.firstChild(); + while( !param.isNull() && isValid() ) + { + m_args.append( demarshall( param.firstChild().toElement() ) ); + param = param.nextSibling(); + } + } + } + else + setError("Not a valid methodCall message."); + } + else + { + setError(QString( "XML Error: %1 at row %2 and col %3") + .arg(xmlErrMsg).arg(xmlErrLine).arg(xmlErrCol)); + } +} + +void XMLRPC::ResponseMessage::writeXml( QXmlStreamWriter *writer ) const +{ + writer->writeStartElement("methodResponse"); + + if( !m_values.isEmpty() ) + { + writer->writeStartElement("params"); + foreach( QVariant arg, m_values) + { + writer->writeStartElement("params"); + marshall( writer, arg ); + writer->writeEndElement(); + } + writer->writeEndElement(); + } + writer->writeEndElement(); +} + +QByteArray XMLRPC::ResponseMessage::xml( ) const +{ + QByteArray returnXML ; + QXmlStreamWriter writer(&returnXML); + writer.writeStartDocument(); + writeXml( &writer ); + writer.writeEndElement(); + writer.writeEndDocument(); + return returnXML; +} + +void XMLRPC::ResponseMessage::setValues( const QList< QVariant > vals ) +{ + m_values = vals; +} + +XMLRPC::ResponseMessage::ResponseMessage( const QList< QVariant > & theValue ) +: MessageBase(), m_values(theValue) +{ +} + +XMLRPC::FaultMessage::FaultMessage( int code, const QString & message ) : +ResponseMessage(QList() ) +{ + QList args; + QMap fault; + fault["faultCode"] = code; + fault["faultString"] = message; + args << fault; + setValues(args); +} + +void XMLRPC::FaultMessage::writeXml( QXmlStreamWriter *writer ) const +{ + writer->writeStartElement("fault"); + marshall( writer, values().first() ); + writer->writeEndElement(); +} + +QByteArray XMLRPC::FaultMessage::xml( ) const +{ + QByteArray returnXML; + QXmlStreamWriter writer( &returnXML ); + writer.writeStartDocument(); + writeXml(&writer); + writer.writeEndDocument(); + return returnXML; +} + +XMLRPC::ResponseMessage::ResponseMessage( const QVariant & theValue ) +: MessageBase() +{ + m_values << theValue; +} -- cgit v1.2.3