9 #include <QScopedPointer>
13 #include "qjsonwebtoken/qjsonwebtoken.h"
21 using namespace swift::misc::network;
22 using namespace swift::config;
24 namespace swift::core::afv::connection
26 const QStringList &CApiServerConnection::getLogCategories()
32 CApiServerConnection::CApiServerConnection(
const QString &address, QObject *parent)
33 : QObject(parent), m_addressUrl(address)
41 if (isShuttingDown()) {
return; }
43 m_username = username;
44 m_password = password;
46 m_networkVersion = networkVersion;
47 m_isAuthenticated =
false;
49 QUrl url(m_addressUrl);
50 url.setPath(
"/api/v1/auth");
52 QJsonObject obj { {
"username", username },
53 {
"password", password },
54 {
"networkversion", networkVersion.toString() },
55 {
"client", client } };
57 QNetworkRequest request(url);
58 QPointer<CApiServerConnection> myself(
this);
59 request.setHeader(QNetworkRequest::ContentTypeHeader,
"application/json");
63 {
this, [=](QNetworkReply *nwReply) {
65 const QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> reply(nwReply);
66 if (!myself || isShuttingDown())
71 this->logRequestDuration(reply.data(),
"authentication");
72 if (reply->error() != QNetworkReply::NoError)
74 this->logReplyErrorMessage(reply.data(),
"authentication error");
80 m_serverToUserOffsetMs = 0;
81 m_expiryLocalUtc = QDateTime();
83 m_jwt = reply->readAll().trimmed();
84 qint64 lifeTimeSecs = -1;
85 qint64 serverToUserOffsetSecs = -1;
87 const QString jwtToken(m_jwt);
88 const QJsonWebToken token = QJsonWebToken::fromTokenAndSecret(jwtToken,
"");
93 const QJsonDocument doc = token.getPayloadJDoc();
94 if (doc.isEmpty() || !doc.isObject()) {
break; }
95 const qint64 validFromSecs = doc.object().value(
"nbf").toInt(-1);
96 if (validFromSecs < 0) {
break; }
97 const qint64 localSecsSinceEpoch = QDateTime::currentSecsSinceEpoch();
98 serverToUserOffsetSecs = validFromSecs - localSecsSinceEpoch;
99 const qint64 serverExpirySecs = doc.object().value(
"exp").toInt();
100 const qint64 expiryLocalUtc = serverExpirySecs - serverToUserOffsetSecs;
101 lifeTimeSecs = expiryLocalUtc - localSecsSinceEpoch;
105 if (lifeTimeSecs > 0)
107 m_serverToUserOffsetMs = serverToUserOffsetSecs * 1000;
108 m_expiryLocalUtc = QDateTime::currentDateTimeUtc().addSecs(lifeTimeSecs);
109 m_isAuthenticated =
true;
113 callback(m_isAuthenticated);
119 return this->postNoRequest<PostCallsignResponseDto>(
"/api/v1/users/" + m_username +
"/callsigns/" + callsign);
124 this->deleteResource(
"/api/v1/users/" + m_username +
"/callsigns/" + callsign);
129 if (!this->sendToNetworkIfAuthenticated()) {
return; }
131 for (
const TransceiverDto &tx : transceivers) { array.append(tx.toJson()); }
132 this->postNoResponse(
"/api/v1/users/" + m_username +
"/callsigns/" + callsign +
"/transceivers",
133 QJsonDocument(array));
138 m_isAuthenticated =
false;
144 const QVector<StationDto> stations = this->getAsVector<StationDto>(
"/api/v1/stations/aliased");
150 if (
stringCompare(m_addressUrl, url, Qt::CaseInsensitive)) {
return false; }
155 QByteArray CApiServerConnection::getWithResponse(
const QNetworkRequest &request)
157 if (isShuttingDown()) {
return {}; }
159 QPointer<QEventLoop> loop(this->newEventLoop());
160 QByteArray receivedData;
164 {
this, [=, &receivedData](QNetworkReply *nwReply) {
165 const QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> reply(nwReply);
168 if (loop && !isShuttingDown())
170 this->logRequestDuration(reply.data());
171 if (reply->error() == QNetworkReply::NoError) { receivedData = reply->readAll(); }
172 else { this->logReplyErrorMessage(reply.data()); }
174 if (loop) { loop->exit(); }
177 if (loop) { loop->exec(); }
181 QByteArray CApiServerConnection::postWithResponse(
const QNetworkRequest &request,
const QByteArray &data)
183 if (isShuttingDown()) {
return {}; }
185 QPointer<QEventLoop> loop(this->newEventLoop());
186 QByteArray receivedData;
190 {
this, [=, &receivedData](QNetworkReply *nwReply) {
191 const QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> reply(nwReply);
194 if (loop && !isShuttingDown())
196 this->logRequestDuration(reply.data());
197 if (reply->error() == QNetworkReply::NoError) { receivedData = reply->readAll(); }
198 else { this->logReplyErrorMessage(reply.data()); }
200 if (loop) { loop->exit(); }
203 if (loop) { loop->exec(); }
207 void CApiServerConnection::postNoResponse(
const QString &resource,
const QJsonDocument &json)
209 if (isShuttingDown()) {
return; }
212 QUrl url(m_addressUrl);
213 url.setPath(resource);
214 QNetworkRequest request(url);
215 request.setRawHeader(
"Authorization",
"Bearer " + m_jwt);
216 request.setHeader(QNetworkRequest::ContentTypeHeader,
"application/json");
220 { this, [=](QNetworkReply *nwReply) {
222 const QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> reply(nwReply);
223 if (isShuttingDown()) { return; }
224 this->logRequestDuration(reply.data());
225 if (reply->error() != QNetworkReply::NoError)
227 this->logReplyErrorMessage(reply.data());
232 void CApiServerConnection::deleteResource(
const QString &resource)
234 if (isShuttingDown()) {
return; }
236 QUrl url(m_addressUrl);
237 url.setPath(resource);
239 QNetworkRequest request(url);
240 request.setRawHeader(
"Authorization",
"Bearer " + m_jwt);
244 {
this, [=](QNetworkReply *nwReply) {
246 const QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> reply(
248 if (isShuttingDown()) {
return; }
249 this->logRequestDuration(reply.data());
250 if (reply->error() != QNetworkReply::NoError)
252 this->logReplyErrorMessage(reply.data());
257 void CApiServerConnection::checkExpiry()
259 if (!m_expiryLocalUtc.isValid() || QDateTime::currentDateTimeUtc() > m_expiryLocalUtc.addSecs(-5 * 60))
261 QPointer<CApiServerConnection> myself(
this);
262 this->connectTo(m_username, m_password, m_client, m_networkVersion,
263 {
this, [=](
bool authenticated) {
264 if (!myself) {
return; }
271 void CApiServerConnection::logReplyErrorMessage(
const QNetworkReply *reply,
const QString &addMsg)
273 if (!reply) {
return; }
274 if (addMsg.isEmpty())
277 << reply->url().toString() << CNetworkUtils::networkOperationToString(reply->operation())
278 << reply->errorString();
283 << addMsg << reply->url().toString() << CNetworkUtils::networkOperationToString(reply->operation())
284 << reply->errorString();
288 void CApiServerConnection::logRequestDuration(
const QNetworkReply *reply,
const QString &addMsg)
290 if (!CBuildConfig::isLocalDeveloperDebugBuild()) {
return; }
291 if (!reply) {
return; }
293 const qint64 d = CNetworkUtils::requestDuration(reply);
294 if (d < 0) {
return; }
295 if (addMsg.isEmpty())
297 CLogMessage(
this).
info(u
"AFV network request for '%1': %2ms") << reply->url().toString() << d;
302 << addMsg << reply->url().toString() << d;
306 QEventLoop *CApiServerConnection::newEventLoop()
308 QEventLoop *loop =
new QEventLoop(
this);
316 bool CApiServerConnection::sendToNetworkIfAuthenticated()
const {
return m_isAuthenticated && !isShuttingDown(); }
SWIFT_CORE_EXPORT swift::core::CApplication * sApp
Single instance of application object.
void aboutToShutdown()
About to shutdown.
QNetworkReply * getFromNetwork(const swift::misc::network::CUrl &url, const CallbackSlot &callback, int maxRedirects=DefaultMaxRedirects)
Request to get network reply.
static constexpr int NoLogRequestId
network request without logging
QNetworkReply * postToNetwork(const QNetworkRequest &request, int logId, const QByteArray &data, const CallbackSlot &callback)
Post to network.
bool isShuttingDown() const
Is application shutting down?
QNetworkReply * deleteResourceFromNetwork(const QNetworkRequest &request, int logId, const CallbackSlot &callback, int maxRedirects=DefaultMaxRedirects)
Request to delete a network resource from network, supporting swift::misc::network::CUrlLog.
void removeCallsign(const QString &callsign)
Remove callsign from network.
PostCallsignResponseDto addCallsign(const QString &callsign)
Add callsign to network.
void forceDisconnect()
Force disconnect from network.
QVector< StationDto > getAllAliasedStations()
All aliased stations.
void connectTo(const QString &username, const QString &password, const QString &client, const QUuid &networkVersion, ConnectionCallback callback)
Connect to network.
void updateTransceivers(const QString &callsign, const QVector< TransceiverDto > &transceivers)
Update transceivers.
bool setUrl(const QString &url)
Set the URL.
static const QString & vatsimSpecific()
VATSIM specific.
static const QString & audio()
Audio related.
Class for emitting a log message.
Derived & warning(const char16_t(&format)[N])
Set the severity to warning, providing a format string.
Derived & debug()
Set the severity to debug.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
Callable wrapper for a member function with function signature F.
Free functions in swift::misc.
SWIFT_MISC_EXPORT bool stringCompare(const QString &c1, const QString &c2, Qt::CaseSensitivity cs)
String compare.
const std::string & boolToYesNo(bool t)
Yes/no from bool.