#include #include #include #include #include #include #include #include "xmlrpcserver.h" // initialization speed up for createFault() and isFault() static QString faultCode("faultCode"); static QString faultString("faultString"); // returns default http header for our xmlrpc server static QHttpResponseHeader xmlRpcResponseHeader( const qint64 contentLength ); // create xmlrpc response from QVariant static QByteArray toXmlRpcResponse( const QVariant& v ); // QVariant to xml conversions // use QByteArray & reference, becouse it is faster, then return QByteArray static void toXmlRpcValue( const int spaces, const QVariant & child, QByteArray & b ); static void toXmlRpcStruct( const int spaces, const QVariantMap & child, QByteArray & b ); static void toXmlRpcArray( const int spaces, const QVariantList & child, QByteArray & b ); // xml to QVariant conversions static QVariant parseXmlRpcValue( const QDomElement & e, QString& err ); static QVariant parseXmlRpcStruct( const QDomElement & e, QString& err ); static QVariant parseXmlRpcArray( const QDomElement & e, QString& err ); SslParams::SslParams( const QString & certFile, const QString & keyFile, QObject * parent ) : QObject( parent ) { QFile fc( certFile, this ); fc.open( QFile::ReadOnly ); certificate = QSslCertificate( fc.readAll() ); fc.close(); ca << certificate; QFile fk( keyFile, this ); fk.open( QFile::ReadOnly ); privateKey = QSslKey( fk.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey/*, passwd*/ ); fk.close(); } inline bool isFault( const QVariant& v ) { if( v.type() != QVariant::Map ) return false; const QVariantMap& m = v.toMap(); return m.size() == 2 && m.contains(faultCode) && m[faultCode].type() == QVariant::Int && m.contains(faultString) && m[faultString].type() == QVariant::String; } Protocol::Protocol( QAbstractSocket * parent, const int _timeout ) : QObject( parent ), timeout( 0 ), timeoutTimerId( 0 ), socket( parent ) { #ifdef DEBUG_PROTOCOL qDebug() << this << "Protocol(): child of" << socket; #endif // check we are only one protocol, working with this socket, at this time foreach( QObject * o, parent->children() ) if( o != this && qobject_cast(o) ) { qCritical() << this << "Protocol(): socket" << parent << "already have another protocol" << o; qFatal( "programming error" ); } if( _timeout > 0 ) setTimeout( _timeout ); } Protocol::~Protocol() { #ifdef DEBUG_PROTOCOL qDebug() << this << "~Protocol()"; #endif } // timeout restarts void Protocol::setTimeout( const int _timeout ) { #ifdef DEBUG_PROTOCOL qDebug() << this << "setTimeout():" << _timeout << "prev timeout was" << timeout; #endif if( timeoutTimerId ) { #ifdef DEBUG_PROTOCOL qDebug() << this << "setTimeout(): stopping previous timer"; #endif killTimer( timeoutTimerId ); timeoutTimerId = 0; } if( !(timeout > 0) && _timeout > 0 ) { // если предыдущий timeout НЕ был больше ноля, а текущий больше, // значит нужно прицепить сигналы приема/отсылки данных на сокете #ifdef DEBUG_PROTOCOL qDebug() << this << "setTimeout(): connect socket signals readyRead() and bytesWritten()"; #endif connect( socket, SIGNAL(readyRead()), this, SLOT(__slotReadyRead()) ); connect( socket, SIGNAL(bytesWritten(qint64)), this, SLOT(__slotBytesWritten(qint64)) ); } else if( timeout > 0 && !(_timeout > 0) ) { // новый выключен, старый был включен #ifdef DEBUG_PROTOCOL qDebug() << this << "setTimeout(): disconnect socket signals readyRead() and bytesWritten()"; #endif disconnect( socket, SIGNAL(readyRead()), this, SLOT(__slotReadyRead()) ); disconnect( socket, SIGNAL(bytesWritten(qint64)), this, SLOT(__slotBytesWritten(qint64)) ); } timeout = _timeout; if( timeout > 0 ) restartProtocolTimeoutTimer(); } void Protocol::restartProtocolTimeoutTimer() { #ifdef DEBUG_PROTOCOL qDebug() << this << "restartProtocolTimeoutTimer()"; #endif if( timeoutTimerId ) killTimer( timeoutTimerId ); Q_ASSERT( timeout > 0 ); timeoutTimerId = startTimer( timeout ); } void Protocol::timerEvent( QTimerEvent * event ) { #ifdef DEBUG_PROTOCOL qDebug() << this << "timerEvent():" << event->timerId() << "protocol timeout timer id" << timeoutTimerId; #endif if( event->timerId() == timeoutTimerId ) { #ifdef DEBUG_PROTOCOL qDebug() << this << "timerEvent(): emit ProtocolTimeout()"; #endif emit protocolTimeout( this ); killTimer( timeoutTimerId ); timeoutTimerId = 0; } } void Protocol::__slotReadyRead() { #ifdef DEBUG_PROTOCOL qDebug() << this << "__slotReadyRead():" << socket->bytesAvailable() << "bytes available, restarting protocol timeout timer"; #endif restartProtocolTimeoutTimer(); } void Protocol::__slotBytesWritten( qint64 /*bytes*/ ) { #ifdef DEBUG_PROTOCOL qDebug() << this << "__slotBytesWritten():" << bytes << "written, restarting protocol timeout timer"; #endif restartProtocolTimeoutTimer(); } static QHttpResponseHeader xmlRpcResponseHeader( const qint64 contentLength ) { #ifdef DEBUG_XMLRPC qDebug() << "xmlRpcHeader():" << contentLength; #endif QHttpResponseHeader h( 200, "OK", 1, 0 ); h.setContentType( "text/xml" ); h.setContentLength( contentLength ); h.setValue( "connection", "close" ); h.setValue( "server", "qt-xmlrpc" ); return h; } HttpServer::HttpServer( QAbstractSocket * parent ) : Protocol( parent, defaultTimeout ), state( ReadingHeader ) { #ifdef DEBUG_XMLRPC qDebug() << this << "HttpServer():" << parent; #endif connect( socket, SIGNAL( readyRead() ), this, SLOT( slotReadyRead() ) ); connect( socket, SIGNAL( bytesWritten(qint64) ), this, SLOT( slotBytesWritten(qint64) ) ); if ( socket->inherits( "QSslSocket" ) ) { QSslSocket *sslServer = qobject_cast( socket ); sslServer->startServerEncryption(); } else { if ( socket->bytesAvailable() > 0 ) slotReadyRead(); } } void HttpServer::slotReadyRead() { #ifdef DEBUG_XMLRPC qDebug() << this << "slotReadyRead():" << socket->bytesAvailable() << "bytes available"; #endif if( !socket->bytesAvailable() ) return; switch( state ) { case ReadingHeader: // если заголовок прочитан if( readRequestHeader() ) { // если судя по заголовку есть тело, меняем статус на чтение тела // и попадаем в следующий case if( requestContainsBody() ) state = ReadingBody; } else { // тела нет, бросаем сигнал, что заголовок получен Q_ASSERT( !socket->bytesAvailable() ); state = WaitingReply; #ifdef DEBUG_XMLRPC qDebug() << this << "slotReadyRead(): emit requestReceived()"; #endif emit requestReceived( this, requestHeader, requestBody ); break; } case ReadingBody: if( readRequestBody() ) { // тело прочитано, бросаем сигнал, что запрос получен Q_ASSERT( !socket->bytesAvailable() ); state = WaitingReply; #ifdef DEBUG_XMLRPC qDebug() << this << "slotReadyRead(): emit requestReceived()"; #endif emit requestReceived( this, requestHeader, requestBody ); } break; case WaitingReply: qCritical() << this << "slotReadyRead(): got data in WaitingReply state, emit parseError()"; emit parseError( this ); break; case SendingReply: qCritical() << this << "slotReadyRead(): got data in SendingHeader state, emit parseError()"; emit parseError( this ); break; case Done: qCritical() << this << "slotReadyRead(): got data in Done state, emit parseError()"; emit parseError( this ); break; default: qCritical() << this << "slotReadyRead(): unknown state"; qFatal( "programming error" ); } } bool HttpServer::readRequestHeader() { #ifdef DEBUG_XMLRPC qDebug() << this << "readRequestHeader()"; #endif // code from qhttp.cpp bool end = false; QByteArray tmp; QByteArray rn("\r\n",2), n("\n",1); while (!end && socket->canReadLine()) { tmp = socket->readLine(); if (tmp == rn || tmp == n || tmp.isEmpty()) end = true; else requestHeaderBody.append( tmp ); } if( !end ) { #ifdef DEBUG_XMLRPC qDebug() << this << "readRequestHeader(): waiting more data, readed" << endl << requestHeaderBody; #endif return false; } requestHeader = QHttpRequestHeader( requestHeaderBody ); requestHeaderBody.clear(); requestBody.clear(); if( requestHeader.isValid() ) { #ifdef DEBUG_XMLRPC qDebug() << this << "readRequestHeader(): header valid" << endl << requestHeader.toString(); #endif return true; } qWarning() << this << "readRequestHeader(): invalid requestHeader, emit parseError()" << endl << requestHeader.toString(); emit parseError( this ); return false; } bool HttpServer::readRequestBody() { Q_ASSERT( requestHeader.isValid() ); qint64 bytesToRead = (qint64)requestHeader.contentLength() - (qint64)requestBody.size(); if( bytesToRead > socket->bytesAvailable() ) bytesToRead = socket->bytesAvailable(); #ifdef DEBUG_XMLRPC qDebug() << this << "readRequestBody(): already read" << requestBody.size() << "to read" << bytesToRead; #endif requestBody.append( socket->read(bytesToRead) ); #ifdef DEBUG_XMLRPC qDebug() << this << "readRequestBody(): already read" << requestBody.size() << "contentLength" << requestHeader.contentLength(); #endif if( requestBody.size() == (int)requestHeader.contentLength() ) return true; else return false; } bool HttpServer::requestContainsBody() { #ifdef DEBUG_XMLRPC qDebug() << this << "requestContainsBody()"; #endif Q_ASSERT( requestHeader.isValid() ); return requestHeader.hasContentLength() && requestHeader.hasContentLength(); } void HttpServer::slotBytesWritten( qint64 bytes ) { #ifdef DEBUG_XMLRPC qDebug() << this << "slotBytesWritten():" << bytes; #endif bytesWritten += bytes; if( bytesWritten == bytesToWrite ) { state = Done; emit replySent( this ); } } void HttpServer::slotSendReply( const QByteArray& body ) { #ifdef DEBUG_XMLRPC qDebug() << this << "sendReply():" << body; #endif Q_ASSERT( state == WaitingReply ); state = SendingReply; //QByteArray body = toXmlRpcResponse( e ); QHttpResponseHeader h( xmlRpcResponseHeader(body.size()) ); QByteArray hb = h.toString().toLatin1(); bytesToWrite = hb.size() + body.size(); bytesWritten = 0; socket->write( hb ); socket->write( body ); socket->flush(); } void HttpServer::slotSendReply( const QVariant& e ) { #ifdef DEBUG_XMLRPC qDebug() << this << "sendReply():" << e; #endif Q_ASSERT( state == WaitingReply ); state = SendingReply; QByteArray body = toXmlRpcResponse( e ); QHttpResponseHeader h( xmlRpcResponseHeader(body.size()) ); QByteArray hb = h.toString().toLatin1(); bytesToWrite = hb.size() + body.size(); bytesWritten = 0; socket->write( hb ); socket->write( body ); socket->flush(); } XmlRpcServer::XmlRpcServer( QObject * parent, const QString & cert, const QString & key, const QByteArray & ) : QTcpServer( parent ) { #ifdef DEBUG_XMLRPC qDebug() << this << "XmlRpcServer(): parent" << parent; #endif sslParams = NULL; if ( !cert.isEmpty() && !key.isEmpty() ) sslParams = new SslParams( cert, key, this ); } void XmlRpcServer::incomingConnection( int socketDescriptor ) { #ifdef DEBUG_XMLRPC qDebug() << this << "new incoming connection"; #endif QAbstractSocket * s = NULL; if ( sslParams != NULL && !sslParams->certificate.isNull() ) { s = new QSslSocket( this ); s->setSocketDescriptor( socketDescriptor ); ((QSslSocket *)s)->setLocalCertificate( sslParams->certificate ); ((QSslSocket *)s)->setPrivateKey( sslParams->privateKey ); ((QSslSocket *)s)->setCaCertificates( sslParams->ca ); } else { s = new QTcpSocket( this ); s->setSocketDescriptor( socketDescriptor ); } Q_ASSERT( s->state() == QAbstractSocket::ConnectedState ); connect( s, SIGNAL(disconnected()), this, SLOT(slotSocketDisconnected()) ); HttpServer * p = new HttpServer( s ); connect( p, SIGNAL(protocolTimeout(Protocol*)), this, SLOT(slotProtocolTimeout(Protocol*)) ); connect( p, SIGNAL(parseError(HttpServer*)), this, SLOT(slotParseError(HttpServer*)) ); connect( p, SIGNAL(requestReceived(HttpServer*, const QHttpRequestHeader&, const QByteArray&)), this, SLOT(slotRequestReceived(HttpServer*, const QHttpRequestHeader&, const QByteArray&)) ); connect( p, SIGNAL(replySent(HttpServer*)), this, SLOT(slotReplySent(HttpServer*)) ); } void XmlRpcServer::slotSocketDisconnected() { #ifdef DEBUG_XMLRPC qDebug() << this << "slotSocketDisconnected()"; #endif // just delete this socket. sender()->deleteLater(); } void XmlRpcServer::slotProtocolTimeout( Protocol * p ) { #ifdef DEBUG_XMLRPC qDebug() << this << "slotProtocolTimeout()"; #endif qWarning() << this << "slotProtocolTimeout(): client" << p->getSocket()->peerAddress() << ":" << p->getSocket()->peerPort() << "closing connection"; p->getSocket()->close(); } void XmlRpcServer::slotParseError( HttpServer * p ) { #ifdef DEBUG_XMLRPC qDebug() << this << "slotParseError()"; #endif qWarning() << this << "slotParseError(): client" << p->getSocket()->peerAddress() << ":" << p->getSocket()->peerPort() << "closing connection"; p->getSocket()->close(); } void XmlRpcServer::registerSlot( QObject * receiver, const char * slot, QByteArray path ) { #ifdef DEBUG_XMLRPC qDebug() << this << "registerSlot():" << receiver << slot; #endif // skip code first symbol 1 from SLOT macro (from qobject.cpp) ++slot; // find QMetaMethod.. const QMetaObject * mo = receiver->metaObject(); // we need buf in memory for const char (from qobject.cpp) QByteArray method = QMetaObject::normalizedSignature( slot ); const char * normalized = method.constData(); int slotId = mo->indexOfSlot( normalized ); if( slotId == -1 ) { qCritical() << this << "registerSlot():" << receiver << normalized << "can't find slot"; qFatal("programming error"); } QMetaMethod m = receiver->metaObject()->method( slotId ); bool isDefferedResult = !qstrcmp( m.typeName(), "DeferredResult*" ); if( qstrcmp(m.typeName(), "QVariant") && !isDefferedResult ) { qCritical() << this << "registerSlot():" << receiver << normalized << "rpc return type should be QVariant or DeferredResult*, but" << m.typeName(); qFatal("programming error"); } foreach( QByteArray c, m.parameterTypes() ) if( c != "QVariant" ) { qCritical() << this << "registerSlot():" << receiver << normalized << "all parameters should be QVariant"; qFatal("programming error"); } // ok, now lets make just function name from our SLOT Q_ASSERT( method.indexOf('(') > 0 ); method.truncate( method.indexOf('(') ); if( path[0] != '/' ) path.prepend( '/' ); if( path[path.size()-1] != '/' ) path.append( '/' ); method.prepend( path ); // check if allready exists if( callbacks.contains(method) ) { qCritical() << this << "registerSlot():" << receiver << method << "allready registered, receiver" << callbacks[method]; qFatal( "programming error" ); } callbacks[ method ] = IsMethodDeffered( receiver, isDefferedResult ); Methods& methods = objectMethods[ receiver ]; if( methods.isEmpty() ) { #ifdef DEBUG_XMLRPC qDebug() << this << "registerSlot(): connecting SIGNAL(destroyed()) of" << receiver; #endif connect( receiver, SIGNAL(destroyed(QObject*)), this, SLOT(slotReceiverDestroed(QObject*)) ); } methods.append( method ); #ifdef DEBUG_XMLRPC qDebug() << this << "registerSlot(): callbacks" << callbacks; qDebug() << this << "registerSlot(): objectMethods" << objectMethods; #endif } void XmlRpcServer::slotReceiverDestroed( QObject * o ) { #ifdef DEBUG_XMLRPC qDebug() << this << "slotReceiverDestroed():" << o; #endif Q_ASSERT( objectMethods.contains(o) ); foreach( const char * m, objectMethods[o] ) if( !callbacks.remove(m) ) qCritical() << this << "slotReceiverDestroed():" << "can't remove" << m << "from callbacks"; objectMethods.remove( o ); #ifdef DEBUG_XMLRPC qDebug() << this << "slotReceiverDestroed(): callbacks" << callbacks; qDebug() << this << "slotReceiverDestroed(): objectMethods" << objectMethods; #endif } void XmlRpcServer::slotRequestReceived( HttpServer * p, const QHttpRequestHeader & h, const QByteArray & body ) { #ifdef DEBUG_XMLRPC qDebug() << this << "slotRequestReceived():" << p << h.toString() << body; #endif QDomDocument doc; QString errorMsg; int errorLine, errorColumn; if( !doc.setContent(body, &errorMsg, &errorLine, &errorColumn) ) { qCritical() << this << "slotRequestReceived(): can't parse xml" << endl << body << endl << errorMsg << endl << "line:" << errorLine << "column:" << errorColumn; p->getSocket()->close(); return; } QVariantList params; QDomNode n; QDomElement e; // run until first element n = doc.firstChild(); while( !n.isNull() ) { e = n.toElement(); if( !e.isNull() ) break; n = n.nextSibling(); } if( e.tagName() != "methodCall" ) { qCritical() << this << "slotRequestReceived(): first element is not methodCall" << e.tagName() << endl<< body; p->getSocket()->close(); return; } // first child SHOULD be methodName e = e.firstChild().toElement(); if( e.tagName() != "methodName" ) { qCritical() << this << "slotRequestReceived(): first element of methodCall is not methodName" << e.tagName() << endl<< body; p->getSocket()->close(); return; } QByteArray methodName = e.firstChild().toText().data().toLatin1(); if( methodName.isEmpty() ) { qCritical() << this << "slotRequestReceived(): methodName empty" << endl << body; p->getSocket()->close(); return; } QByteArray path = h.path().toLatin1(); if( path[0] != '/' ) path.prepend( '/' ); if( path[path.size()-1] != '/' ) path.append( '/' ); if( !callbacks.contains(path + methodName) ) { qCritical() << this << "slotRequestReceived(): method" << path + methodName << "is not registered as callback"; p->slotSendReply( toXmlRpcResponse( createFault(-1, "method " + path + methodName + " not registered") ) ); return; } // run over params e = e.nextSibling().toElement(); if( e.tagName() != "params" ) { qCritical() << this << "slotRequestReceived(): method" << methodName << "no params element in xml request" << endl << body; p->slotSendReply( toXmlRpcResponse( createFault(-1, "no params element in xml request") ) ); return; } // run until first element e = e.firstChild().toElement(); while( !e.isNull() ) { if( e.tagName() != "param" ) { qCritical() << this << "slotRequestReceived(): method" << methodName << "no param element in xml request" << endl << body; p->slotSendReply( toXmlRpcResponse( createFault(-1, "bad param element in xml request") ) ); return; } QString err; params.append( parseXmlRpcValue(e.firstChild().toElement(), err) ); if( !err.isEmpty() ) { qCritical() << this << "slotRequestReceived(): method" << methodName << "parse error:" << err << endl << body; p->slotSendReply( toXmlRpcResponse( createFault(-1, err) ) ); return; } e = e.nextSibling().toElement(); } QAbstractSocket * s = p->getSocket(); if ( s->inherits( "QSslSocket" ) ) { QSslSocket * sslSkt = qobject_cast(s); QString commonName = sslSkt-> peerCertificate().subjectInfo( QSslCertificate::CommonName ); params.append( commonName ); } // params parsed, now invoke registered slot. QVariant retVal; DeferredResult * retDeffered; const IsMethodDeffered& md = callbacks[path + methodName]; bool invoked; if( md.second ) { invoked = QMetaObject::invokeMethod( md.first, methodName.constData(), Qt::DirectConnection, Q_RETURN_ARG(DeferredResult*, retDeffered), params.size() > 0 ? Q_ARG(QVariant, params[0]) : QGenericArgument(), params.size() > 1 ? Q_ARG(QVariant, params[1]) : QGenericArgument(), params.size() > 2 ? Q_ARG(QVariant, params[2]) : QGenericArgument(), params.size() > 3 ? Q_ARG(QVariant, params[3]) : QGenericArgument(), params.size() > 4 ? Q_ARG(QVariant, params[4]) : QGenericArgument(), params.size() > 5 ? Q_ARG(QVariant, params[5]) : QGenericArgument(), params.size() > 6 ? Q_ARG(QVariant, params[6]) : QGenericArgument(), params.size() > 7 ? Q_ARG(QVariant, params[7]) : QGenericArgument(), params.size() > 8 ? Q_ARG(QVariant, params[8]) : QGenericArgument(), params.size() > 9 ? Q_ARG(QVariant, params[9]) : QGenericArgument() ); } else { invoked = QMetaObject::invokeMethod( md.first, methodName.constData(), Qt::DirectConnection, Q_RETURN_ARG(QVariant, retVal), params.size() > 0 ? Q_ARG(QVariant, params[0]) : QGenericArgument(), params.size() > 1 ? Q_ARG(QVariant, params[1]) : QGenericArgument(), params.size() > 2 ? Q_ARG(QVariant, params[2]) : QGenericArgument(), params.size() > 3 ? Q_ARG(QVariant, params[3]) : QGenericArgument(), params.size() > 4 ? Q_ARG(QVariant, params[4]) : QGenericArgument(), params.size() > 5 ? Q_ARG(QVariant, params[5]) : QGenericArgument(), params.size() > 6 ? Q_ARG(QVariant, params[6]) : QGenericArgument(), params.size() > 7 ? Q_ARG(QVariant, params[7]) : QGenericArgument(), params.size() > 8 ? Q_ARG(QVariant, params[8]) : QGenericArgument(), params.size() > 9 ? Q_ARG(QVariant, params[9]) : QGenericArgument() ); } if( !invoked ) { qCritical() << this << "slotRequestReceived(): can't invoke" << methodName << params; QVariant fault = createFault( -1, "can't invoke " + path + methodName + ", wrong parameters number?" ); p->slotSendReply( toXmlRpcResponse( fault ) ); return; } if( md.second ) { retDeffered->setParent( p->getSocket() ); connect( retDeffered, SIGNAL(sendReply(const QVariant&)), p, SLOT(slotSendReply(const QVariant&)) ); } else p->slotSendReply( toXmlRpcResponse( retVal ) ); return; } void XmlRpcServer::slotReplySent( HttpServer * p ) { #ifdef DEBUG_XMLRPC qDebug() << this << "slotReplySent():" << p; #endif p->getSocket()->close(); } void toXmlRpcArray( const int spaces, const QVariantList & child, QByteArray & b ) { #ifdef DEBUG_XMLRPC qDebug() << "toXmlRpcArray()"; #endif QListIterator< QVariant > i(child); while( i.hasNext() ) toXmlRpcValue( spaces + 2, i.next(), b ); } void toXmlRpcStruct( const int spaces, const QVariantMap & child, QByteArray & b ) { #ifdef DEBUG_XMLRPC qDebug() << "toXmlRpcStruct()"; #endif QMapIterator< QString, QVariant > i(child); while( i.hasNext() ) { i.next(); #ifdef XMLRPC_WITHSPACES b.append( '\n' ); b.append( QByteArray(spaces, ' ') ); #endif b.append( "" ); #ifdef XMLRPC_WITHSPACES b.append( '\n' ); b.append( QByteArray(spaces + 2, ' ') ); #endif b.append( "" + i.key() + "" ); toXmlRpcValue( spaces + 2, i.value(), b ); #ifdef XMLRPC_WITHSPACES b.append( '\n' ); b.append( QByteArray(spaces, ' ') ); #endif b.append( "" ); } } void toXmlRpcValue( const int spaces, const QVariant & child, QByteArray & b ) { #ifdef DEBUG_XMLRPC qDebug() << "toXmlRpcValue()"; #endif switch( child.type() ) { case QVariant::Int: #ifdef XMLRPC_WITHSPACES b.append( '\n' ); b.append( QByteArray(spaces, ' ') ); #endif b.append( "" + QString::number(child.toInt()) + "" ); break; case QVariant::Bool: #ifdef XMLRPC_WITHSPACES b.append( '\n' ); b.append( QByteArray(spaces, ' ') ); #endif b.append( QString("") + (child.toBool() ? "1" : "0") + "" ); break; case QVariant::String: #ifdef XMLRPC_WITHSPACES b.append( '\n' ); b.append( QByteArray(spaces, ' ') ); #endif b.append( "" + child.toString() + "" ); break; case QVariant::Double: #ifdef XMLRPC_WITHSPACES b.append( '\n' ); b.append( QByteArray(spaces, ' ') ); #endif b.append( "" + QString::number(child.toDouble()) + "" ); break; case QVariant::DateTime: #ifdef XMLRPC_WITHSPACES b.append( '\n' ); b.append( QByteArray(spaces, ' ') ); #endif b.append( "" + child.toDateTime().toString("yyyyMMddTHH:mm:ss") + "" ); break; case QVariant::ByteArray: #ifdef XMLRPC_WITHSPACES b.append( '\n' ); b.append( QByteArray(spaces, ' ') ); #endif b.append( "" + child.toByteArray().toBase64() + "" ); break; case QVariant::List: #ifdef XMLRPC_WITHSPACES b.append( '\n' ); b.append( QByteArray(spaces, ' ') ); #endif b.append( "" ); toXmlRpcArray( spaces + 2, child.toList(), b ); #ifdef XMLRPC_WITHSPACES b.append( '\n' ); b.append( QByteArray(spaces, ' ') ); #endif b.append( "" ); break; case QVariant::Map: #ifdef XMLRPC_WITHSPACES b.append( '\n' ); b.append( QByteArray(spaces, ' ') ); #endif b.append( "" ); toXmlRpcStruct( spaces + 2, child.toMap(), b ); #ifdef XMLRPC_WITHSPACES b.append( '\n' ); b.append( QByteArray(spaces, ' ') ); #endif b.append( "" ); break; default: qCritical() << "toXmlRpcValue(): unknown return xmlrpc type" << child.typeName() << endl << child; qFatal("programming error"); } } QByteArray toXmlRpcResponse( const QVariant& v ) { #ifdef XMLRPC_WITHSPACES QByteArray r( "\n" ); #else QByteArray r( "" ); #endif // this is error? if( isFault(v) ) { #ifdef XMLRPC_WITHSPACES r.append( "\n \n " ); #else r.append( "" ); #endif toXmlRpcValue( 6, v, r ); #ifdef XMLRPC_WITHSPACES r.append( "\n \n " ); #else r.append( "" ); #endif } else { #ifdef XMLRPC_WITHSPACES r.append( "\n \n " ); #else r.append( "" ); #endif toXmlRpcValue( 6, v, r ); #ifdef XMLRPC_WITHSPACES r.append( "\n \n " ); #else r.append( "" ); #endif } #ifdef XMLRPC_WITHSPACES r.append( "\n" ); #else r.append( "" ); #endif #ifdef DEBUG_XMLRPC qDebug() << "toXmlRpc():" << v << endl << r; #endif return r; } QVariant parseXmlRpcValue( const QDomElement & e, QString& err ) { #ifdef DEBUG_XMLRPC qDebug() << "parseXmlRpcValue():" << e.tagName(); #endif QVariant v; if( e.tagName() != "value" ) { err = "first param tag not value"; return v; } QDomElement t = e.firstChild().toElement(); QString type = t.tagName(); if( type == "int" || type == "i4" ) { bool ok; v = t.firstChild().toText().data().toInt( &ok ); if( !ok ) err = "Can't convert int text '" + t.firstChild().toText().data() + "' to number"; } else if( type == "boolean" ) v = t.firstChild().toText().data() == "1" ? true : false; else if( type == "string" ) v = t.firstChild().toText().data(); else if( type == "double" ) { bool ok; v = t.firstChild().toText().data().toDouble( &ok ); if( !ok ) err = "Can't convert int text '" + t.firstChild().toText().data() + "' to number"; } else if( type == "dateTime.iso8601" ) v = QDateTime::fromString( t.firstChild().toText().data(), "yyyyMMddTHH:mm:ss" ); else if( type == "base64" ) v = QByteArray::fromBase64( t.firstChild().toText().data().toLatin1() ); else if ( type == "array" ) v = parseXmlRpcArray( t.firstChild().toElement(), err ); else if ( type == "struct" ) v = parseXmlRpcStruct( t.firstChild().toElement(), err ); else err = "unknown type: '" + type + "'"; return v; } QVariant parseXmlRpcStruct( const QDomElement & e, QString& err ) { #ifdef DEBUG_XMLRPC qDebug() << "parseXmlRpcStruct():" << e.tagName(); #endif QDomElement t = e; QVariantMap r; while( !t.isNull() ) { if( t.tagName() != "member" ) { err = "no member tag in struct, tag " + t.tagName(); return r; } QDomElement s = t.firstChild().toElement(); if( s.tagName() != "name" ) { err = "no name tag in member struct, tag " + s.tagName(); return r; } // set map value r[ s.firstChild().toText().data() ] = parseXmlRpcValue( s.nextSibling().toElement(), err ); if( !err.isEmpty() ) return r; t = t.nextSibling().toElement(); } return r; } QVariant parseXmlRpcArray( const QDomElement & e, QString& err ) { #ifdef DEBUG_XMLRPC qDebug() << "parseXmlRpcArray():" << e.tagName(); #endif QVariantList r; if( e.tagName() != "data" ) { err = "no data tag in array, tag " + e.tagName(); return r; } QDomElement t = e.firstChild().toElement(); while( !t.isNull() ) { r.append( parseXmlRpcValue(t,err) ); if( !err.isEmpty() ) return r; t = t.nextSibling().toElement(); } return r; } DeferredEcho::DeferredEcho( const QVariant& e ) : echo( e ) { startTimer( 1000 ); // one second timeout, before echo } void DeferredEcho::timerEvent( QTimerEvent * ) { emit sendReply( echo ); } DeferredResult::DeferredResult() { #ifdef DEBUG_XMLRPC qDebug() << this << "DeferredResult()"; #endif } DeferredResult::~DeferredResult() { #ifdef DEBUG_XMLRPC qDebug() << this << "~DeferredResult()"; #endif }