#include #include #include #include "xmlrpcinterface.h" #include "xmlrpcpendingcall.h" XmlRpcPendingCall::XmlRpcPendingCall(QNetworkReply *reply, XmlRpcInterface *parent) : QObject(parent), _reply(reply), _state(StateWaitingReply) { connect(_reply, SIGNAL(finished()), SLOT(handleRequestFinished())); _reply->setParent(this); } void XmlRpcPendingCall::waitForFinished() { if (_state == StateWaitingReply) { QEventLoop loop; connect(this, SIGNAL(finished(XmlRpcPendingCall*)), &loop, SLOT(quit())); loop.exec(); } } bool XmlRpcPendingCall::decodeMethodResponse(QXmlStreamReader *r) { if (r->readNextStartElement()) { if (r->name() == "fault") { _state = StateFaultReceived; if (r->readNextStartElement()) { if (r->name() != "value") return false; _value = decodeValue(r); if (!_value.isValid()) return false; } return true; } else if (r->name() == "params") { _state = StateReplyReceived; if (r->readNextStartElement()) { if (r->name() != "param") return false; if (r->readNextStartElement()) { if (r->name() != "value") return false; _value = decodeValue(r); if (!_value.isValid()) return false; } else { return false; } } return true; } } return false; } QVariant XmlRpcPendingCall::decodeValue(QXmlStreamReader *r) { Q_ASSERT(r->isStartElement() && r->name() == "value"); QVariant value; if (r->readNextStartElement()) { if (r->name() == "string") { value = QVariant::fromValue(r->readElementText()); } else if (r->name() == "int") { bool ok; value = QVariant::fromValue(r->readElementText().toInt(&ok)); if (!ok) value.clear(); } else if (r->name() == "boolean") { bool ok; value = QVariant::fromValue(r->readElementText().toInt(&ok)); if (!ok) value.clear(); } else if (r->name() == "double") { bool ok; value = QVariant::fromValue(r->readElementText().toDouble(&ok)); if (!ok) value.clear(); } else if (r->name() == "dateTime.iso8601") { QString text = r->readElementText(); value = QVariant::fromValue(decodeISODate(text)); } else if (r->name() == "base64") { QByteArray data = r->readElementText().toAscii(); value = QVariant::fromValue(QByteArray::fromBase64(data)); } else if (r->name() == "array") { QList list; if (!r->readNextStartElement() || r->name() != "data") { qWarning() << "Unexpected element inside :" << r->name(); return QVariant(); } while (r->readNextStartElement()) { if (r->name() != "value") return QVariant(); QVariant value = decodeValue(r); if (!value.isValid()) return QVariant(); list.append(value); } if (r->readNextStartElement()) { // No other elements qWarning() << "Unexpected element inside :" << r->name(); return QVariant(); } value = QVariant::fromValue(list); } else if (r->name() == "struct") { QMap map; while (r->readNextStartElement()) { if (r->name() != "member") return QVariant(); QString name; if (r->readNextStartElement() && r->name() == "name") { name = r->readElementText(); } else { qWarning() << "Malformed struct"; return QVariant(); } if (r->readNextStartElement() && r->name() == "value") { QVariant value = decodeValue(r); if (!value.isValid()) return QVariant(); map.insert(name, value); } else { qWarning() << "Malformed struct"; return QVariant(); } if (r->readNextStartElement()) { // No other elements qWarning() << "Unexpected element inside " << r->name(); return false; } } value = QVariant::fromValue(map); } else { qWarning() << "Unknown value type:" << r->name(); } } if (r->readNextStartElement()) { // There is more than one element inside this qWarning() << "More than element inside "; return QVariant(); } else if (r->isEndElement() && r->name() == "value") { // Everything OK return value; } else { qWarning() << "Expected instead of" << r->name(); return QVariant(); } } QDateTime XmlRpcPendingCall::decodeISODate(QString text) { if (text.length() < 8) { // Too short! return QDateTime(); } if (text[4].isNumber() && text[7].isNumber()) { // Qt seems not be happy without dashes (YYYYMMDD vs YYYY-MM-DD) text.insert(4, '-'); text.insert(7, '-'); } // Qt will consider UTC offset "+00:00" invalid, so we replace it by "Z". if (text.endsWith("+00:00") || text.endsWith("-00:00")) { const int len = 6; // "+00:00" const int pos = text.length() - len; text.replace(pos, len, "Z"); } else if (text.endsWith("+0000") || text.endsWith("-0000")) { const int len = 5; // "+0000" const int pos = text.length() - len; text.replace(pos, len, "Z"); } else if (text.endsWith("+00") || text.endsWith("-00")) { const int len = 3; // "+00" const int pos = text.length() - len; text.replace(pos, len, "Z"); } QDateTime dt = QDateTime::fromString(text, Qt::ISODate); return dt; } void XmlRpcPendingCall::handleRequestFinished() { Q_ASSERT(_state == StateWaitingReply); QNetworkReply::NetworkError error = _reply->error(); if (error == QNetworkReply::NoError) { QByteArray data = _reply->readAll(); #if XML_RPC_DEBUG qDebug() << "Response:" << data; #endif QXmlStreamReader reader(data); bool parse_ok = false; if (reader.readNextStartElement()) { if (reader.name() == "methodResponse") { parse_ok = decodeMethodResponse(&reader); } } if (parse_ok) { Q_ASSERT(_state == StateReplyReceived || _state == StateFaultReceived); } else { qWarning() << "Parse error!"; QVariantMap obj; obj.insert("code", QVariant(reader.error())); obj.insert("message", QVariant(reader.errorString())); _value = obj; _state = StateParseError; } } else { qWarning() << "Network error!" << error; QVariantMap obj; obj.insert("code", QVariant(error)); obj.insert("message", QVariant(_reply->errorString())); _value = obj; _state = StateNetworkError; } emit finished(this); }