Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions Common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@

#include "Common.h"

#include <QtCore/QCoreApplication>
#include <QtCore/QLocale>
#include <QtCore/QMutex>
#include <QtCore/QOperatingSystemVersion>
#include <QtCore/QRegularExpression>
#include <QtCore/QSettings>

#ifdef Q_OS_WIN
Expand Down Expand Up @@ -116,3 +120,60 @@ QStringList Common::drivers()
#endif
return list;
}

namespace
{
// Written when the user switches language, read when composing the header, which may
// happen on a worker thread (e.g. certificate fetches during validation).
QMutex languageMutex;
QString language;
}

void Common::setLanguage(const QString &lang)
{
QMutexLocker locker(&languageMutex);
language = lang;
}

QByteArray Common::userAgent(bool devices)
{
// RIA User-Agent Header Specification, schema 1:
// AppName/AppVersion (schema=1; key=value; key=value)
// libdigidocpp prefixes this with "LIB libdigidocpp/<version> (<arch>) APP ".
static const QRegularExpression disallowed(QStringLiteral("[;\\x00-\\x1F\\x7F]"));
auto value = [](QString value) {
value = value.remove(disallowed).simplified();
// The metadata block is an RFC 9110 comment. Quote characters that have
// structural meaning there so arbitrary system-provided values cannot
// terminate the comment or leave it syntactically incomplete.
value.replace(u'\\', QStringLiteral("\\\\"));
value.replace(u'(', QStringLiteral("\\("));
value.replace(u')', QStringLiteral("\\)"));
return value;
};
QStringList metadata {
QStringLiteral("schema=1"),
QStringLiteral("os=%1").arg(value(applicationOs())),
QStringLiteral("lang=%1").arg(value([] {
QMutexLocker locker(&languageMutex);
return language.isEmpty() ? QLocale().bcp47Name() : language;
}())),
QStringLiteral("devicetype=desktop"),
};
if(devices)
{
QStringList labels;
for(QString label: drivers())
{
// Comma is the devices sub-delimiter and cannot be represented inside
// an individual label in schema 1.
label.replace(u',', u' ');
if(label = value(label); !label.isEmpty())
labels.append(label);
}
if(!labels.isEmpty())
metadata.append(QStringLiteral("devices=%1").arg(labels.join(QLatin1String(", "))));
}
return QStringLiteral("%1/%2 (%3)").arg(QCoreApplication::applicationName(),
QCoreApplication::applicationVersion(), metadata.join(QLatin1String("; "))).toUtf8();
}
11 changes: 11 additions & 0 deletions Common.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,16 @@ class Common
public:
static QString applicationOs();
static QStringList drivers();
/**
* @brief Sets the application language reported by userAgent()
*
* When unset the locale is reported instead.
*/
static void setLanguage(const QString &lang);
/**
* @brief User-Agent header value following the RIA User-Agent Header Specification (schema 1)
* @param devices include the connected card readers in the metadata block
*/
static QByteArray userAgent(bool devices = false);

};
5 changes: 2 additions & 3 deletions Configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ QNetworkReply *Configuration::Private::limitedGet(const QNetworkRequest &req)
{
constexpr qint64 MAX_REPLY_SIZE = 1024 * 1024;
QNetworkRequest limitedReq = req;
// Set per request so that a language change after construction is reflected
limitedReq.setRawHeader("User-Agent", Common::userAgent(true));
limitedReq.setDecompressedSafetyCheckThreshold(MAX_REPLY_SIZE);
auto *reply = net->get(limitedReq);
reply->setReadBufferSize(MAX_REPLY_SIZE + 1);
Expand Down Expand Up @@ -186,9 +188,6 @@ Configuration::Configuration(QObject *parent)
d->eccurl = QStringLiteral("%1%2.ecc").arg(
d->url.adjusted(QUrl::RemoveFilename).toString(),
QFileInfo(d->url.fileName()).baseName());
d->req.setRawHeader("User-Agent", QStringLiteral("%1/%2 (%3) Lang: %4 Devices: %5")
.arg(QCoreApplication::applicationName(), QCoreApplication::applicationVersion(),
Common::applicationOs(), QLocale().uiLanguages().first(), Common::drivers().join('/')).toUtf8());
d->req.setTransferTimeout();
d->net = new QNetworkAccessManager(this);
connect(d->net, &QNetworkAccessManager::sslErrors, this,
Expand Down