cutelyst 3.9.1
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
session.cpp
1/*
2 * SPDX-FileCopyrightText: (C) 2013-2022 Daniel Nicoletti <dantti12@gmail.com>
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5#include "session_p.h"
6#include "sessionstorefile.h"
7
8#include <Cutelyst/Application>
9#include <Cutelyst/Context>
10#include <Cutelyst/Engine>
11#include <Cutelyst/Response>
12
13#include <QCoreApplication>
14#include <QHostAddress>
15#include <QLoggingCategory>
16#include <QUuid>
17
18using namespace Cutelyst;
19
20Q_LOGGING_CATEGORY(C_SESSION, "cutelyst.plugin.session", QtWarningMsg)
21
22#define SESSION_VALUES QStringLiteral("_c_session_values")
23#define SESSION_EXPIRES QStringLiteral("_c_session_expires")
24#define SESSION_TRIED_LOADING_EXPIRES QStringLiteral("_c_session_tried_loading_expires")
25#define SESSION_EXTENDED_EXPIRES QStringLiteral("_c_session_extended_expires")
26#define SESSION_UPDATED QStringLiteral("_c_session_updated")
27#define SESSION_ID QStringLiteral("_c_session_id")
28#define SESSION_TRIED_LOADING_ID QStringLiteral("_c_session_tried_loading_id")
29#define SESSION_DELETED_ID QStringLiteral("_c_session_deleted_id")
30#define SESSION_DELETE_REASON QStringLiteral("_c_session_delete_reason")
31
32static thread_local Session *m_instance = nullptr;
33
35 : Plugin(parent)
36 , d_ptr(new SessionPrivate(this))
37{
38}
39
40Cutelyst::Session::~Session()
41{
42 delete d_ptr;
43}
44
46{
47 Q_D(Session);
48 d->sessionName = QCoreApplication::applicationName() + QLatin1String("_session");
49
50 const QVariantMap config = app->engine()->config(QLatin1String("Cutelyst_Session_Plugin"));
51 d->sessionExpires = config.value(QLatin1String("expires"), 7200).toLongLong();
52 d->expiryThreshold = config.value(QLatin1String("expiry_threshold"), 0).toLongLong();
53 d->verifyAddress = config.value(QLatin1String("verify_address"), false).toBool();
54 d->verifyUserAgent = config.value(QLatin1String("verify_user_agent"), false).toBool();
55 d->cookieHttpOnly = config.value(QLatin1String("cookie_http_only"), true).toBool();
56 d->cookieSecure = config.value(QLatin1String("cookie_secure"), false).toBool();
57 const QString _sameSite =
58 config.value(QLatin1String("cookie_same_site"), QStringLiteral("strict")).toString();
59#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
60 if (_sameSite.compare(u"default", Qt::CaseInsensitive) == 0) {
61 d->cookieSameSite = QNetworkCookie::SameSite::Default;
62 } else if (_sameSite.compare(u"none", Qt::CaseInsensitive) == 0) {
63 d->cookieSameSite = QNetworkCookie::SameSite::None;
64 } else if (_sameSite.compare(u"lax", Qt::CaseInsensitive) == 0) {
65 d->cookieSameSite = QNetworkCookie::SameSite::Lax;
66 } else {
67 d->cookieSameSite = QNetworkCookie::SameSite::Strict;
68 }
69#else
70 if (_sameSite.compare(u"default", Qt::CaseInsensitive) == 0) {
71 d->cookieSameSite = Cookie::SameSite::Default;
72 } else if (_sameSite.compare(u"none", Qt::CaseInsensitive) == 0) {
73 d->cookieSameSite = Cookie::SameSite::None;
74 } else if (_sameSite.compare(u"lax", Qt::CaseInsensitive) == 0) {
75 d->cookieSameSite = Cookie::SameSite::Lax;
76 } else {
77 d->cookieSameSite = Cookie::SameSite::Strict;
78 }
79#endif
80
81 connect(app, &Application::afterDispatch, this, &SessionPrivate::_q_saveSession);
82 connect(app, &Application::postForked, this, [=] { m_instance = this; });
83
84 if (!d->store) {
85 d->store = new SessionStoreFile(this);
86 }
87
88 return true;
89}
90
92{
93 Q_D(Session);
94 Q_ASSERT_X(d->store, "Cutelyst::Session::setStorage", "Session Storage is alread defined");
95 store->setParent(this);
96 d->store = store;
97}
98
100{
101 Q_D(const Session);
102 return d->store;
103}
104
106{
107 QString ret;
108 const QVariant sid = c->stash(SESSION_ID);
109 if (sid.isNull()) {
110 if (Q_UNLIKELY(!m_instance)) {
111 qCCritical(C_SESSION) << "Session plugin not registered";
112 return ret;
113 }
114
115 ret = SessionPrivate::loadSessionId(c, m_instance->d_ptr->sessionName);
116 } else {
117 ret = sid.toString();
118 }
119
120 return ret;
121}
122
124{
125 QVariant expires = c->stash(SESSION_EXTENDED_EXPIRES);
126 if (!expires.isNull()) {
127 return expires.toULongLong();
128 }
129
130 if (Q_UNLIKELY(!m_instance)) {
131 qCCritical(C_SESSION) << "Session plugin not registered";
132 return 0;
133 }
134
135 expires = SessionPrivate::loadSessionExpires(m_instance, c, id(c));
136 if (!expires.isNull()) {
137 return quint64(SessionPrivate::extendSessionExpires(m_instance, c, expires.toLongLong()));
138 }
139
140 return 0;
141}
142
144{
145 const QString sid = Session::id(c);
146 const qint64 timeExp = QDateTime::currentMSecsSinceEpoch() / 1000 + qint64(expires);
147
148 if (Q_UNLIKELY(!m_instance)) {
149 qCCritical(C_SESSION) << "Session plugin not registered";
150 return;
151 }
152
153 m_instance->d_ptr->store->storeSessionData(c, sid, QStringLiteral("expires"), timeExp);
154}
155
157{
158 if (Q_UNLIKELY(!m_instance)) {
159 qCCritical(C_SESSION) << "Session plugin not registered";
160 return;
161 }
162 SessionPrivate::deleteSession(m_instance, c, reason);
163}
164
166{
167 return c->stash(SESSION_DELETE_REASON).toString();
168}
169
170QVariant Session::value(Cutelyst::Context *c, const QString &key, const QVariant &defaultValue)
171{
172 QVariant ret = defaultValue;
173 QVariant session = c->stash(SESSION_VALUES);
174 if (session.isNull()) {
175 session = SessionPrivate::loadSession(c);
176 }
177
178 if (!session.isNull()) {
179 ret = session.toHash().value(key, defaultValue);
180 }
181
182 return ret;
183}
184
186{
187 QVariant session = c->stash(SESSION_VALUES);
188 if (session.isNull()) {
189 session = SessionPrivate::loadSession(c);
190 if (session.isNull()) {
191 if (Q_UNLIKELY(!m_instance)) {
192 qCCritical(C_SESSION) << "Session plugin not registered";
193 return;
194 }
195
196 SessionPrivate::createSessionIdIfNeeded(
197 m_instance, c, m_instance->d_ptr->sessionExpires);
198 session = SessionPrivate::initializeSessionData(m_instance, c);
199 }
200 }
201
202 QVariantHash data = session.toHash();
203 data.insert(key, value);
204
205 c->setStash(SESSION_VALUES, data);
206 c->setStash(SESSION_UPDATED, true);
207}
208
210{
211 QVariant session = c->stash(SESSION_VALUES);
212 if (session.isNull()) {
213 session = SessionPrivate::loadSession(c);
214 if (session.isNull()) {
215 if (Q_UNLIKELY(!m_instance)) {
216 qCCritical(C_SESSION) << "Session plugin not registered";
217 return;
218 }
219
220 SessionPrivate::createSessionIdIfNeeded(
221 m_instance, c, m_instance->d_ptr->sessionExpires);
222 session = SessionPrivate::initializeSessionData(m_instance, c);
223 }
224 }
225
226 QVariantHash data = session.toHash();
227 data.remove(key);
228
229 c->setStash(SESSION_VALUES, data);
230 c->setStash(SESSION_UPDATED, true);
231}
232
234{
235 QVariant session = c->stash(SESSION_VALUES);
236 if (session.isNull()) {
237 session = SessionPrivate::loadSession(c);
238 if (session.isNull()) {
239 if (Q_UNLIKELY(!m_instance)) {
240 qCCritical(C_SESSION) << "Session plugin not registered";
241 return;
242 }
243
244 SessionPrivate::createSessionIdIfNeeded(
245 m_instance, c, m_instance->d_ptr->sessionExpires);
246 session = SessionPrivate::initializeSessionData(m_instance, c);
247 }
248 }
249
250 QVariantHash data = session.toHash();
251 for (const QString &key : keys) {
252 data.remove(key);
253 }
254
255 c->setStash(SESSION_VALUES, data);
256 c->setStash(SESSION_UPDATED, true);
257}
258
260{
261 return !SessionPrivate::loadSession(c).isNull();
262}
263
264QString SessionPrivate::generateSessionId()
265{
266 return QString::fromLatin1(QUuid::createUuid().toRfc4122().toHex());
267}
268
269QString SessionPrivate::loadSessionId(Context *c, const QString &sessionName)
270{
271 QString ret;
272 if (!c->stash(SESSION_TRIED_LOADING_ID).isNull()) {
273 return ret;
274 }
275 c->setStash(SESSION_TRIED_LOADING_ID, true);
276
277 const QString sid = getSessionId(c, sessionName);
278 if (!sid.isEmpty()) {
279 if (!validateSessionId(sid)) {
280 qCCritical(C_SESSION) << "Tried to set invalid session ID" << sid;
281 return ret;
282 }
283 ret = sid;
284 c->setStash(SESSION_ID, sid);
285 }
286
287 return ret;
288}
289
290QString SessionPrivate::getSessionId(Context *c, const QString &sessionName)
291{
292 QString ret;
293 bool deleted = !c->stash(SESSION_DELETED_ID).isNull();
294
295 if (!deleted) {
296 const QVariant property = c->stash(SESSION_ID);
297 if (!property.isNull()) {
298 ret = property.toString();
299 return ret;
300 }
301
302 const QString cookie = c->request()->cookie(sessionName);
303 if (!cookie.isEmpty()) {
304 qCDebug(C_SESSION) << "Found sessionid" << cookie << "in cookie";
305 ret = cookie;
306 }
307 }
308
309 return ret;
310}
311
312QString SessionPrivate::createSessionIdIfNeeded(Session *session, Context *c, qint64 expires)
313{
314 QString ret;
315 const QVariant sid = c->stash(SESSION_ID);
316 if (!sid.isNull()) {
317 ret = sid.toString();
318 } else {
319 ret = createSessionId(session, c, expires);
320 }
321 return ret;
322}
323
324QString SessionPrivate::createSessionId(Session *session, Context *c, qint64 expires)
325{
326 Q_UNUSED(expires)
327 const QString sid = generateSessionId();
328
329 qCDebug(C_SESSION) << "Created session" << sid;
330
331 c->setStash(SESSION_ID, sid);
332 resetSessionExpires(session, c, sid);
333 setSessionId(session, c, sid);
334
335 return sid;
336}
337
338void SessionPrivate::_q_saveSession(Context *c)
339{
340 // fix cookie before we send headers
341 saveSessionExpires(c);
342
343 // Force extension of session_expires before finalizing headers, so a pos
344 // up to date. First call to session_expires will extend the expiry, methods
345 // just return the previously extended value.
347
348 // Persist data
349 if (Q_UNLIKELY(!m_instance)) {
350 qCCritical(C_SESSION) << "Session plugin not registered";
351 return;
352 }
353 saveSessionExpires(c);
354
355 if (!c->stash(SESSION_UPDATED).toBool()) {
356 return;
357 }
358 SessionStore *store = m_instance->d_ptr->store;
359 QVariantHash sessionData = c->stash(SESSION_VALUES).toHash();
360 sessionData.insert(QStringLiteral("__updated"), QDateTime::currentMSecsSinceEpoch() / 1000);
361
362 const QString sid = c->stash(SESSION_ID).toString();
363 store->storeSessionData(c, sid, QStringLiteral("session"), sessionData);
364}
365
366void SessionPrivate::deleteSession(Session *session, Context *c, const QString &reason)
367{
368 qCDebug(C_SESSION) << "Deleting session" << reason;
369
370 const QVariant sidVar = c->stash(SESSION_ID).toString();
371 if (!sidVar.isNull()) {
372 const QString sid = sidVar.toString();
373 session->d_ptr->store->deleteSessionData(c, sid, QStringLiteral("session"));
374 session->d_ptr->store->deleteSessionData(c, sid, QStringLiteral("expires"));
375 session->d_ptr->store->deleteSessionData(c, sid, QStringLiteral("flash"));
376
377 deleteSessionId(session, c, sid);
378 }
379
380 // Reset the values in Context object
381 c->setStash(SESSION_VALUES, QVariant());
382 c->setStash(SESSION_ID, QVariant());
383 c->setStash(SESSION_EXPIRES, QVariant());
384
385 c->setStash(SESSION_DELETE_REASON, reason);
386}
387
388void SessionPrivate::deleteSessionId(Session *session, Context *c, const QString &sid)
389{
390 c->setStash(SESSION_DELETED_ID, true); // to prevent get_session_id from returning it
391
392#if (QT_VERSION >= QT_VERSION_CHECK(6, 1, 0))
393 updateSessionCookie(c, makeSessionCookie(session, c, sid, QDateTime::currentDateTimeUtc()));
394#else
395 updateSessionCuteCookie(
396 c, makeSessionCuteCookie(session, c, sid, QDateTime::currentDateTimeUtc()));
397#endif
398}
399
400QVariant SessionPrivate::loadSession(Context *c)
401{
402 QVariant ret;
403 const QVariant property = c->stash(SESSION_VALUES);
404 if (!property.isNull()) {
405 ret = property.toHash();
406 return ret;
407 }
408
409 if (Q_UNLIKELY(!m_instance)) {
410 qCCritical(C_SESSION) << "Session plugin not registered";
411 return ret;
412 }
413
414 const QString sid = Session::id(c);
415 if (!loadSessionExpires(m_instance, c, sid).isNull()) {
416 if (SessionPrivate::validateSessionId(sid)) {
417
418 const QVariantHash sessionData =
419 m_instance->d_ptr->store->getSessionData(c, sid, QStringLiteral("session"))
420 .toHash();
421 c->setStash(SESSION_VALUES, sessionData);
422
423 if (m_instance->d_ptr->verifyAddress &&
424 sessionData.contains(QStringLiteral("__address")) &&
425 sessionData.value(QStringLiteral("__address")).toString() !=
426 c->request()->address().toString()) {
427 qCWarning(C_SESSION) << "Deleting session" << sid << "due to address mismatch:"
428 << sessionData.value(QStringLiteral("__address")).toString()
429 << "!=" << c->request()->address().toString();
430 deleteSession(m_instance, c, QStringLiteral("address mismatch"));
431 return ret;
432 }
433
434 if (m_instance->d_ptr->verifyUserAgent &&
435 sessionData.contains(QStringLiteral("__user_agent")) &&
436 sessionData.value(QStringLiteral("__user_agent")).toString() !=
437 c->request()->userAgent()) {
438 qCWarning(C_SESSION) << "Deleting session" << sid << "due to user agent mismatch:"
439 << sessionData.value(QStringLiteral("__user_agent")).toString()
440 << "!=" << c->request()->userAgent();
441 deleteSession(m_instance, c, QStringLiteral("user agent mismatch"));
442 return ret;
443 }
444
445 qCDebug(C_SESSION) << "Restored session" << sid;
446
447 ret = sessionData;
448 }
449 }
450
451 return ret;
452}
453
454bool SessionPrivate::validateSessionId(const QString &id)
455{
456 auto it = id.constBegin();
457 auto end = id.constEnd();
458 while (it != end) {
459 QChar c = *it;
460 if ((c >= u'a' && c <= u'f') || (c >= u'0' && c <= u'9')) {
461 ++it;
462 continue;
463 }
464 return false;
465 }
466
467 return id.size();
468}
469
470qint64 SessionPrivate::extendSessionExpires(Session *session, Context *c, qint64 expires)
471{
472 const qint64 threshold = qint64(session->d_ptr->expiryThreshold);
473
474 const QString sid = Session::id(c);
475 if (!sid.isEmpty()) {
476 const qint64 current = getStoredSessionExpires(session, c, sid);
477 const qint64 cutoff = current - threshold;
478 const qint64 time = QDateTime::currentMSecsSinceEpoch() / 1000;
479
480 if (!threshold || cutoff <= time || c->stash(SESSION_UPDATED).toBool()) {
481 qint64 updated = calculateInitialSessionExpires(session, c, sid);
482 c->setStash(SESSION_EXTENDED_EXPIRES, updated);
483 extendSessionId(session, c, sid, updated);
484
485 return updated;
486 } else {
487 return current;
488 }
489 } else {
490 return expires;
491 }
492}
493
494qint64
495 SessionPrivate::getStoredSessionExpires(Session *session, Context *c, const QString &sessionid)
496{
497 const QVariant expires =
498 session->d_ptr->store->getSessionData(c, sessionid, QStringLiteral("expires"), 0);
499 return expires.toLongLong();
500}
501
502QVariant SessionPrivate::initializeSessionData(Session *session, Context *c)
503{
504 QVariantHash ret;
505 const qint64 now = QDateTime::currentMSecsSinceEpoch() / 1000;
506 ret.insert(QStringLiteral("__created"), now);
507 ret.insert(QStringLiteral("__updated"), now);
508
509 if (session->d_ptr->verifyAddress) {
510 ret.insert(QStringLiteral("__address"), c->request()->address().toString());
511 }
512
513 if (session->d_ptr->verifyUserAgent) {
514 ret.insert(QStringLiteral("__user_agent"), c->request()->userAgent());
515 }
516
517 return ret;
518}
519
520void SessionPrivate::saveSessionExpires(Context *c)
521{
522 const QVariant expires = c->stash(SESSION_EXPIRES);
523 if (!expires.isNull()) {
524 const QString sid = Session::id(c);
525 if (!sid.isEmpty()) {
526 if (Q_UNLIKELY(!m_instance)) {
527 qCCritical(C_SESSION) << "Session plugin not registered";
528 return;
529 }
530
531 const qint64 current = getStoredSessionExpires(m_instance, c, sid);
532 const qint64 extended = qint64(Session::expires(c));
533 if (extended > current) {
534 m_instance->d_ptr->store->storeSessionData(
535 c, sid, QStringLiteral("expires"), extended);
536 }
537 }
538 }
539}
540
541QVariant SessionPrivate::loadSessionExpires(Session *session, Context *c, const QString &sessionId)
542{
543 QVariant ret;
544 if (c->stash(SESSION_TRIED_LOADING_EXPIRES).toBool()) {
545 ret = c->stash(SESSION_EXPIRES);
546 return ret;
547 }
548 c->setStash(SESSION_TRIED_LOADING_EXPIRES, true);
549
550 if (!sessionId.isEmpty()) {
551 const qint64 expires = getStoredSessionExpires(session, c, sessionId);
552
553 if (expires >= QDateTime::currentMSecsSinceEpoch() / 1000) {
554 c->setStash(SESSION_EXPIRES, expires);
555 ret = expires;
556 } else {
557 deleteSession(session, c, QStringLiteral("session expired"));
558 ret = 0;
559 }
560 }
561 return ret;
562}
563
564qint64 SessionPrivate::initialSessionExpires(Session *session, Context *c)
565{
566 Q_UNUSED(c)
567 const qint64 expires = qint64(session->d_ptr->sessionExpires);
568 return QDateTime::currentMSecsSinceEpoch() / 1000 + expires;
569}
570
571qint64 SessionPrivate::calculateInitialSessionExpires(Session *session,
572 Context *c,
573 const QString &sessionId)
574{
575 const qint64 stored = getStoredSessionExpires(session, c, sessionId);
576 const qint64 initial = initialSessionExpires(session, c);
577 return qMax(initial, stored);
578}
579
580qint64 SessionPrivate::resetSessionExpires(Session *session, Context *c, const QString &sessionId)
581{
582 const qint64 exp = calculateInitialSessionExpires(session, c, sessionId);
583
584 c->setStash(SESSION_EXPIRES, exp);
585
586 // since we're setting _session_expires directly, make loadSessionExpires
587 // actually use that value.
588 c->setStash(SESSION_TRIED_LOADING_EXPIRES, true);
589 c->setStash(SESSION_EXTENDED_EXPIRES, exp);
590
591 return exp;
592}
593
594void SessionPrivate::updateSessionCookie(Context *c, const QNetworkCookie &updated)
595{
596 c->response()->setCookie(updated);
597}
598
599#if (QT_VERSION < QT_VERSION_CHECK(6, 1, 0))
600void SessionPrivate::updateSessionCuteCookie(Context *c, const Cookie &updated)
601{
602 c->response()->setCuteCookie(updated);
603}
604#endif
605
606QNetworkCookie SessionPrivate::makeSessionCookie(Session *session,
607 Context *c,
608 const QString &sid,
609 const QDateTime &expires)
610{
611 Q_UNUSED(c)
612 QNetworkCookie cookie(session->d_ptr->sessionName.toLatin1(), sid.toLatin1());
613 cookie.setPath(QStringLiteral("/"));
614 cookie.setExpirationDate(expires);
615 cookie.setHttpOnly(session->d_ptr->cookieHttpOnly);
616 cookie.setSecure(session->d_ptr->cookieSecure);
617#if (QT_VERSION >= QT_VERSION_CHECK(6, 1, 0))
618 cookie.setSameSitePolicy(session->d_ptr->cookieSameSite);
619#endif
620
621 return cookie;
622}
623
624#if (QT_VERSION < QT_VERSION_CHECK(6, 1, 0))
625Cookie SessionPrivate::makeSessionCuteCookie(Session *session,
626 Context *c,
627 const QString &sid,
628 const QDateTime &expires)
629{
630 Q_UNUSED(c)
631 Cookie cookie(session->d_ptr->sessionName.toLatin1(), sid.toLatin1());
632 cookie.setPath(QStringLiteral("/"));
633 cookie.setExpirationDate(expires);
634 cookie.setHttpOnly(session->d_ptr->cookieHttpOnly);
635 cookie.setSecure(session->d_ptr->cookieSecure);
636 cookie.setSameSitePolicy(session->d_ptr->cookieSameSite);
637
638 return cookie;
639}
640#endif
641
642void SessionPrivate::extendSessionId(Session *session,
643 Context *c,
644 const QString &sid,
645 qint64 expires)
646{
647#if (QT_VERSION >= QT_VERSION_CHECK(6, 1, 0))
648 updateSessionCookie(
649 c, makeSessionCookie(session, c, sid, QDateTime::fromMSecsSinceEpoch(expires * 1000)));
650#else
651 updateSessionCuteCookie(
652 c, makeSessionCuteCookie(session, c, sid, QDateTime::fromMSecsSinceEpoch(expires * 1000)));
653#endif
654}
655
656void SessionPrivate::setSessionId(Session *session, Context *c, const QString &sid)
657{
658#if (QT_VERSION >= QT_VERSION_CHECK(6, 1, 0))
659 updateSessionCookie(c,
660 makeSessionCookie(session,
661 c,
662 sid,
664 initialSessionExpires(session, c) * 1000)));
665#else
666 updateSessionCuteCookie(c,
667 makeSessionCuteCookie(session,
668 c,
669 sid,
671 initialSessionExpires(session, c) * 1000)));
672#endif
673}
674
679
680#include "moc_session.cpp"
The Cutelyst Application.
Definition application.h:43
Engine * engine() const noexcept
void afterDispatch(Cutelyst::Context *c)
void postForked(Cutelyst::Application *app)
The Cutelyst Context.
Definition context.h:39
void stash(const QVariantHash &unite)
Definition context.cpp:566
void setStash(const QString &key, const QVariant &value)
Definition context.cpp:217
Response * response() const noexcept
Definition context.cpp:96
The Cutelyst Cookie.
Definition cookie.h:29
QVariantMap config(const QString &entity) const
user configuration for the application
Definition engine.cpp:290
Plugin(Application *parent)
Definition plugin.cpp:12
QString cookie(const QString &name) const
Definition request.cpp:274
QHostAddress address() const noexcept
Definition request.cpp:33
void setCookie(const QNetworkCookie &cookie)
Definition response.cpp:232
SessionStore(QObject *parent=nullptr)
Definition session.cpp:675
virtual bool storeSessionData(Context *c, const QString &sid, const QString &key, const QVariant &value)=0
static void deleteSession(Context *c, const QString &reason=QString())
Definition session.cpp:156
static QString deleteReason(Context *c)
Definition session.cpp:165
virtual bool setup(Application *app) final
Definition session.cpp:45
Session(Application *parent)
Definition session.cpp:34
static QString id(Context *c)
Definition session.cpp:105
static bool isValid(Context *c)
Definition session.cpp:259
static QVariant value(Context *c, const QString &key, const QVariant &defaultValue=QVariant())
Definition session.cpp:170
static void setValue(Context *c, const QString &key, const QVariant &value)
Definition session.cpp:185
static void changeExpires(Context *c, quint64 expires)
Definition session.cpp:143
SessionStore * storage() const
Definition session.cpp:99
void setStorage(SessionStore *store)
Definition session.cpp:91
static void deleteValue(Context *c, const QString &key)
Definition session.cpp:209
static quint64 expires(Context *c)
Definition session.cpp:123
static void deleteValues(Context *c, const QStringList &keys)
Definition session.cpp:233
The Cutelyst namespace holds all public Cutelyst API.
Definition Mainpage.dox:8
QDateTime currentDateTimeUtc()
qint64 currentMSecsSinceEpoch()
QDateTime fromMSecsSinceEpoch(qint64 msecs)
QString toString() const const
QObject(QObject *parent)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
void setParent(QObject *parent)
int compare(const QString &other, Qt::CaseSensitivity cs) const const
QString fromLatin1(const char *str, int size)
bool isEmpty() const const
QByteArray toLatin1() const const
CaseInsensitive
QUuid createUuid()
bool isNull() const const
QHash< QString, QVariant > toHash() const const
qlonglong toLongLong(bool *ok) const const
QString toString() const const