|
|
|
#include <QNetworkAccessManager>
|
|
|
|
#include <QNetworkReply>
|
|
|
|
|
|
|
|
#include "eetsender.h"
|
|
|
|
#include "eetsigner.h"
|
|
|
|
|
|
|
|
#include <QDebug>
|
|
|
|
#include <QXmlQuery>
|
|
|
|
#include <QXmlResultItems>
|
|
|
|
#include <QTimer>
|
|
|
|
|
|
|
|
const QString EetSender::ms_nsDef = "declare namespace eet = \"http://fs.mfcr.cz/eet/schema/v3\";\n"
|
|
|
|
"declare namespace ds = \"http://www.w3.org/2000/09/xmldsig#\";\n"
|
|
|
|
"declare namespace wsu = \"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\";\n"
|
|
|
|
"declare namespace wsse = \"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\";\n"
|
|
|
|
"declare namespace senc = \"http://schemas.xmlsoap.org/soap/encoding/\";\n"
|
|
|
|
"declare namespace senv = \"http://schemas.xmlsoap.org/soap/envelope/\";\n";
|
|
|
|
|
|
|
|
bool EetSender::m_online = true;
|
|
|
|
|
|
|
|
EetSender::EetSender(QObject *parent) : QObject(parent)
|
|
|
|
{
|
|
|
|
m_signer = nullptr;
|
|
|
|
m_resut = nullptr;
|
|
|
|
m_checkSignature = true;
|
|
|
|
|
|
|
|
m_resut = new EetResult(this);
|
|
|
|
|
|
|
|
m_manager = new QNetworkAccessManager(this);
|
|
|
|
connect(m_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
|
|
|
|
connect(m_manager, &QNetworkAccessManager::sslErrors, [this](QNetworkReply *rep, QList<QSslError>){
|
|
|
|
m_resut->setStatus(EetResult::SSL_ERROR);
|
|
|
|
emit sendFinished();
|
|
|
|
rep->deleteLater();
|
|
|
|
});
|
|
|
|
|
|
|
|
m_serviceUrl = PRODUCTION_URL;
|
|
|
|
m_timer = new QTimer(this);
|
|
|
|
m_timer->setInterval(1000);
|
|
|
|
m_timer->setSingleShot(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void EetSender::sendRequest(EetRequest *request)
|
|
|
|
{
|
|
|
|
if (m_signer == nullptr)
|
|
|
|
{
|
|
|
|
m_resut->setStatus(EetResult::USER_CERT_ERROR);
|
|
|
|
emit sendFinished();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
request->setUuidZpravy(QUuid::createUuid());
|
|
|
|
|
|
|
|
EetTemplate tempBody(BODY_TEMPLATE);
|
|
|
|
tempBody.setSigner(m_signer);
|
|
|
|
tempBody.setResult(m_resut);
|
|
|
|
QString strBody = tempBody.fillTemplate(request);
|
|
|
|
|
|
|
|
QByteArray digest = m_signer->sha256HashData(strBody.toUtf8());
|
|
|
|
QMap<QString, QString> val;
|
|
|
|
val["digest"] = QString(digest.toBase64());
|
|
|
|
EetTemplate tempSignature(SIGNATURE_TEMPLATE);
|
|
|
|
QString strSignature = tempSignature.fillTemplate(val);
|
|
|
|
|
|
|
|
QByteArray sign = m_signer->signData(strSignature.toUtf8());
|
|
|
|
val["signature"] = QString(sign.toBase64());
|
|
|
|
val["soap:Body"] = strBody;
|
|
|
|
val["certb64"] = m_signer->getCertificate();
|
|
|
|
EetTemplate tempRequest(REQUEST_TEMPLATE);
|
|
|
|
|
|
|
|
QString strRequest = tempRequest.fillTemplate(val);
|
|
|
|
|
|
|
|
QNetworkRequest req(QUrl(m_serviceUrl.toStdString().c_str()));
|
|
|
|
|
|
|
|
qDebug() << strRequest;
|
|
|
|
|
|
|
|
if (m_online)
|
|
|
|
{
|
|
|
|
QNetworkReply *rep = m_manager->post(req, strRequest.toUtf8());
|
|
|
|
m_timer->start();
|
|
|
|
connect(m_timer, &QTimer::timeout, [this, rep](){
|
|
|
|
rep->abort();
|
|
|
|
m_resut->setStatus(EetResult::TIMEDOUT);
|
|
|
|
emit sendFinished();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_resut->setStatus(EetResult::OFFLINE);
|
|
|
|
m_timer->stop();
|
|
|
|
emit sendFinished();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EetSender::setupSigner(const QString &certPath, const QString &passwd)
|
|
|
|
{
|
|
|
|
if (m_signer != nullptr)
|
|
|
|
{
|
|
|
|
delete m_signer;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_signer = new EetSigner(this);
|
|
|
|
m_signer->setup(certPath, QCA::SecureArray(passwd.toUtf8()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void EetSender::setCheckSignature(bool checkSignature)
|
|
|
|
{
|
|
|
|
m_checkSignature = checkSignature;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool EetSender::checkSignature() const
|
|
|
|
{
|
|
|
|
return m_checkSignature;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EetSender::setPlayground(bool pg)
|
|
|
|
{
|
|
|
|
m_serviceUrl = (pg ? PLAYGROUND_URL : PRODUCTION_URL);
|
|
|
|
}
|
|
|
|
|
|
|
|
EetResult *EetSender::resut() const
|
|
|
|
{
|
|
|
|
return m_resut;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EetSender::setTimeout(int timeout)
|
|
|
|
{
|
|
|
|
m_timer->setInterval(timeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool EetSender::verifySignature(const QByteArray &repData)
|
|
|
|
{
|
|
|
|
QString queryString("//senv:Envelope/senv:Header/wsse:Security/ds:Signature/ds:SignedInfo");
|
|
|
|
QString signedInfo, certB64, signatureB64;
|
|
|
|
QXmlQuery q;
|
|
|
|
|
|
|
|
q.setFocus(QString(repData));
|
|
|
|
q.setQuery(ms_nsDef + queryString);
|
|
|
|
q.evaluateTo(&signedInfo);
|
|
|
|
|
|
|
|
QStringList list = signedInfo.split("\n");
|
|
|
|
signedInfo = "<SignedInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">\n";
|
|
|
|
for (int i = 1; i < list.length(); i++)
|
|
|
|
{
|
|
|
|
QString line = list[i];
|
|
|
|
line = line.replace(QRegExp("^(\\s+)<([A-Za-z]+)([A-Za-z0-9-\"=/#:\\.\\ ]+)/>"), "\\1<\\2\\3></\\2>");
|
|
|
|
signedInfo += line + "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
signedInfo = signedInfo.trimmed();
|
|
|
|
signedInfo = signedInfo.replace(" ", " ");
|
|
|
|
|
|
|
|
queryString = "//senv:Envelope/senv:Header/wsse:Security/wsse:BinarySecurityToken/text()";
|
|
|
|
q.setQuery(ms_nsDef + queryString);
|
|
|
|
q.evaluateTo(&certB64);
|
|
|
|
|
|
|
|
queryString = "//senv:Envelope/senv:Header/wsse:Security/ds:Signature/ds:SignatureValue/text()";
|
|
|
|
q.setQuery(ms_nsDef + queryString);
|
|
|
|
q.evaluateTo(&signatureB64);
|
|
|
|
|
|
|
|
QCA::ConvertResult res;
|
|
|
|
QCA::Certificate cert = QCA::Certificate::fromDER(QByteArray::fromBase64(certB64.toUtf8()), &res);
|
|
|
|
|
|
|
|
if (res != QCA::ConvertGood)
|
|
|
|
{
|
|
|
|
m_resut->setStatus(EetResult::SERVICE_CERT_ERROR);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
QCA::PublicKey pubKey = cert.subjectPublicKey();
|
|
|
|
|
|
|
|
if (!pubKey.canVerify())
|
|
|
|
{
|
|
|
|
m_resut->setStatus(EetResult::SERVICE_CERT_ERROR);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool signValid = pubKey.verifyMessage(QCA::MemoryRegion(signedInfo.toUtf8()), QByteArray::fromBase64(signatureB64.toUtf8()), QCA::EMSA3_SHA256);
|
|
|
|
|
|
|
|
if (!signValid)
|
|
|
|
{
|
|
|
|
m_resut->setStatus(EetResult::INVALID_SIGNATURE);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EetSender::replyFinished(QNetworkReply *reply)
|
|
|
|
{
|
|
|
|
m_timer->stop();
|
|
|
|
if (reply->error() != QNetworkReply::NoError)
|
|
|
|
{
|
|
|
|
m_resut->setStatus(EetResult::SERVER_ERROR);
|
|
|
|
emit sendFinished();
|
|
|
|
reply->deleteLater();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray repData = reply->readAll();
|
|
|
|
|
|
|
|
qDebug() << QString(repData);
|
|
|
|
|
|
|
|
QXmlQuery q;
|
|
|
|
q.setFocus(QString(repData));
|
|
|
|
|
|
|
|
QXmlResultItems items;
|
|
|
|
QString result;
|
|
|
|
QString queryString = "//senv:Envelope/senv:Body/eet:Odpoved/eet:Chyba";
|
|
|
|
q.setQuery(ms_nsDef + queryString);
|
|
|
|
q.evaluateTo(&items);
|
|
|
|
|
|
|
|
EetMessageList errors;
|
|
|
|
QXmlItem item = items.next();
|
|
|
|
while (!item.isNull())
|
|
|
|
{
|
|
|
|
EetMessage *mesg = new EetMessage(m_resut);
|
|
|
|
queryString = "./@kod/data(.)";
|
|
|
|
q.setQuery(ms_nsDef + queryString);
|
|
|
|
q.setFocus(item);
|
|
|
|
q.evaluateTo(&result);
|
|
|
|
result = result.trimmed();
|
|
|
|
|
|
|
|
mesg->setCode(result.toInt());
|
|
|
|
|
|
|
|
queryString = "./string()";
|
|
|
|
q.setQuery(ms_nsDef + queryString);
|
|
|
|
q.setFocus(item);
|
|
|
|
q.evaluateTo(&result);
|
|
|
|
result = result.trimmed();
|
|
|
|
|
|
|
|
mesg->setMessage(result);
|
|
|
|
errors.append(mesg);
|
|
|
|
|
|
|
|
item = items.next();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_resut->setErrors(errors);
|
|
|
|
|
|
|
|
if (m_checkSignature && errors.isEmpty() && !verifySignature(repData))
|
|
|
|
{
|
|
|
|
emit sendFinished();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
queryString = "//senv:Envelope/senv:Body/eet:Odpoved/eet:Hlavicka/@uuid_zpravy/data(.)";
|
|
|
|
q.setQuery(ms_nsDef + queryString);
|
|
|
|
q.evaluateTo(&result);
|
|
|
|
result = result.trimmed();
|
|
|
|
|
|
|
|
m_resut->setUuid(QUuid(result));
|
|
|
|
|
|
|
|
queryString = "//senv:Envelope/senv:Body/eet:Odpoved/eet:Hlavicka/@dat_prij/data(.)";
|
|
|
|
q.setQuery(ms_nsDef + queryString);
|
|
|
|
q.evaluateTo(&result);
|
|
|
|
result = result.trimmed();
|
|
|
|
m_resut->setReciveDate(QDateTime::fromString(result));
|
|
|
|
|
|
|
|
queryString = "//senv:Envelope/senv:Body/eet:Odpoved/eet:Potvrzeni/@fik/data(.)";
|
|
|
|
q.setQuery(ms_nsDef + queryString);
|
|
|
|
q.evaluateTo(&result);
|
|
|
|
result = result.trimmed();
|
|
|
|
m_resut->setFik(result);
|
|
|
|
|
|
|
|
queryString = "//senv:Envelope/senv:Body/eet:Odpoved/eet:Varovani";
|
|
|
|
q.setQuery(ms_nsDef + queryString);
|
|
|
|
q.evaluateTo(&items);
|
|
|
|
|
|
|
|
EetMessageList warnings;
|
|
|
|
item = items.next();
|
|
|
|
while (!item.isNull())
|
|
|
|
{
|
|
|
|
EetMessage *mesg = new EetMessage(m_resut);
|
|
|
|
queryString = "./@kod_varov/data(.)";
|
|
|
|
q.setQuery(ms_nsDef + queryString);
|
|
|
|
q.setFocus(item);
|
|
|
|
q.evaluateTo(&result);
|
|
|
|
result = result.trimmed();
|
|
|
|
|
|
|
|
mesg->setCode(result.toInt());
|
|
|
|
|
|
|
|
queryString = "./string()";
|
|
|
|
q.setQuery(ms_nsDef + queryString);
|
|
|
|
q.setFocus(item);
|
|
|
|
q.evaluateTo(&result);
|
|
|
|
result = result.trimmed();
|
|
|
|
|
|
|
|
mesg->setMessage(result);
|
|
|
|
warnings.append(mesg);
|
|
|
|
|
|
|
|
item = items.next();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_resut->setWarnings(warnings);
|
|
|
|
|
|
|
|
if (errors.isEmpty())
|
|
|
|
{
|
|
|
|
m_resut->setReciveDate(QDateTime::currentDateTime());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_resut->setStatus(EetResult::DATA_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
emit sendFinished();
|
|
|
|
|
|
|
|
reply->deleteLater();
|
|
|
|
}
|