mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
311 lines
9.4 KiB
C++
311 lines
9.4 KiB
C++
/**
|
|
* RobloxCookieJar.cpp
|
|
* Copyright (c) 2013 ROBLOX Corp. All Rights Reserved.
|
|
*/
|
|
|
|
|
|
#include "stdafx.h"
|
|
#include "RobloxCookieJar.h"
|
|
|
|
// Qt headers
|
|
#include <QDateTime>
|
|
#include <QSettings>
|
|
#include <QStringList>
|
|
#include <QTimer>
|
|
|
|
// Roblox headers
|
|
#include "AuthenticationHelper.h"
|
|
#include "Util/Http.h"
|
|
#include "FastLog.h"
|
|
|
|
typedef NetworkCookieList::const_iterator CookieListIterator;
|
|
typedef CookieMap::const_iterator CookieMapIterator;
|
|
typedef NameToCookieMap::const_iterator NameToCookieMapIterator;
|
|
|
|
CookieMap RobloxCookieJar::m_cookieMap;
|
|
CookieReadStatusMap RobloxCookieJar::m_cookieReadMap;
|
|
|
|
static const int kExpirationDateStringLength = 20;
|
|
|
|
// Lazy Evaluation for faster browser loading
|
|
// Do not Load any Cookie from QSettings Persistence to Memory in ctor
|
|
|
|
RobloxCookieJar::RobloxCookieJar()
|
|
{
|
|
QTimer::singleShot(1000, this, SLOT(lazyInitialization()));
|
|
}
|
|
|
|
// Store the Cookie from Memory into QSettings Persistence
|
|
// This has the downside that if the application crash, the settings will not be persisted
|
|
RobloxCookieJar::~RobloxCookieJar()
|
|
{
|
|
saveCookiesToDisk();
|
|
}
|
|
void RobloxCookieJar::saveCookiesToDisk()
|
|
{
|
|
// Do Not mix the RobloxStudio Settings with Cookies, create a separate plist file for StudioBrowser
|
|
QSettings settings("Roblox", "RobloxStudioBrowser");
|
|
|
|
for (CookieMapIterator outerIter = m_cookieMap.constBegin(); outerIter != m_cookieMap.constEnd(); ++outerIter)
|
|
{
|
|
// Begin a Settings Group with URL
|
|
settings.beginGroup(outerIter.key());
|
|
NameToCookieMap nameToCookieMap = outerIter.value();
|
|
for (NameToCookieMapIterator innerIter = nameToCookieMap.begin(); innerIter != nameToCookieMap.end(); ++innerIter)
|
|
{
|
|
// Construct the Value Part with following syntax:
|
|
// SEC::<YES>,EXP::<YYYY-MM-DDTHH:MM:SS>,COOK::<COOKIE>
|
|
QNetworkCookie cookie = innerIter.value();
|
|
|
|
// Do not store Session Cookie
|
|
if (!cookie.isSessionCookie())
|
|
{
|
|
QDateTime cookieExpTime = cookie.expirationDate().toUTC();
|
|
if (cookieExpTime.isValid() && cookieExpTime > QDateTime::currentDateTimeUtc())
|
|
{
|
|
QString cookieValue;
|
|
if(cookie.isSecure()) cookieValue+="SEC::<YES>,";
|
|
cookieValue+="EXP::<" + cookieExpTime.toString(Qt::ISODate) + ">,";
|
|
cookieValue+="COOK::<" + QString(cookie.value()) + ">";
|
|
settings.setValue(innerIter.key(), cookieValue);
|
|
}
|
|
}
|
|
}
|
|
// End the Setting Group for URL
|
|
settings.endGroup();
|
|
settings.sync();
|
|
}
|
|
}
|
|
|
|
bool RobloxCookieJar::setCookiesFromUrl(const NetworkCookieList& cookieList, const QUrl & url)
|
|
{
|
|
if(!url.isValid())
|
|
return false;
|
|
|
|
QString hostName = url.host().mid(url.host().indexOf(QString(".")) + 1);
|
|
|
|
// Lazy Eval
|
|
loadCookiesFromDisk(hostName);
|
|
NameToCookieMap nameToCookieMap;
|
|
|
|
// See if we already have a map entry for the url passed in
|
|
CookieMapIterator foundUrl = m_cookieMap.constFind(hostName);
|
|
if(foundUrl != m_cookieMap.constEnd())
|
|
nameToCookieMap = foundUrl.value();
|
|
|
|
bool authenticationChanged = false;
|
|
bool newAuthenticationValue = false;
|
|
|
|
// Get the Cookies, Prepare the NameToCookieMap & assign it to the Main Cookie Map
|
|
for (CookieListIterator iter = cookieList.constBegin(); iter != cookieList.constEnd(); ++iter)
|
|
{
|
|
QNetworkCookie cookie = *iter;
|
|
// Check if the Cookie passed in has expired
|
|
if (!cookie.isSessionCookie())
|
|
{
|
|
QByteArray ba = iter->name();
|
|
QString str(ba);
|
|
|
|
/// This will be a case when the user logs out
|
|
QDateTime cookieExpTime = cookie.expirationDate().toUTC();
|
|
if (!cookieExpTime.isValid() || cookieExpTime < QDateTime::currentDateTimeUtc())
|
|
{
|
|
// Logout the user from HTTP session
|
|
if (str == ".ROBLOSECURITY")
|
|
{
|
|
authenticationChanged = true;
|
|
newAuthenticationValue = false;
|
|
}
|
|
|
|
// If expired, we will need to delete the cookie from memory
|
|
nameToCookieMap.erase(nameToCookieMap.find((*iter).name()));
|
|
|
|
// Also delete immedietly from persistence for cookies that have expired
|
|
QSettings settings("Roblox", "RobloxStudioBrowser");
|
|
settings.beginGroup(hostName);
|
|
settings.remove((*iter).name());
|
|
settings.endGroup();
|
|
settings.sync();
|
|
continue;
|
|
|
|
}
|
|
// user logged in successfully
|
|
else if (str == ".ROBLOSECURITY")
|
|
{
|
|
authenticationChanged = true;
|
|
newAuthenticationValue = true;
|
|
}
|
|
}
|
|
// If all is good then save the QNetworkCookie in the NameToCookieMap
|
|
nameToCookieMap[(*iter).name()] = cookie;
|
|
|
|
transferCookieToNativeHttpLayer(cookie.domain(), cookie);
|
|
}
|
|
|
|
// Finally assign the NameToCookieMap to our Main CookieMap
|
|
m_cookieMap[hostName] = nameToCookieMap;
|
|
m_cookieReadMap[hostName] = true;
|
|
|
|
if (authenticationChanged)
|
|
{
|
|
authenticateHttpLayer(newAuthenticationValue);
|
|
|
|
saveCookiesToDisk(); // Persist
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
NetworkCookieList RobloxCookieJar::cookiesForUrl(const QUrl & url) const
|
|
{
|
|
if(!url.isValid())
|
|
return NetworkCookieList();
|
|
|
|
QString hostName = url.host().mid(url.host().indexOf(QString(".")) + 1);
|
|
// Lazy Eval
|
|
loadCookiesFromDisk(hostName);
|
|
|
|
NetworkCookieList cookieList;
|
|
NameToCookieMap nameToCookieMap = m_cookieMap[hostName];
|
|
|
|
for (NameToCookieMapIterator iter = nameToCookieMap.constBegin(); iter != nameToCookieMap.constEnd(); ++iter)
|
|
{
|
|
// if found pump in the QNetworkCookie into the NetworkCookieList
|
|
cookieList.push_back(iter.value());
|
|
}
|
|
m_cookieReadMap[hostName] = true;
|
|
return cookieList;
|
|
}
|
|
|
|
// This fn is responsible for loading cookies from persistence to memory
|
|
void RobloxCookieJar::loadCookiesFromDisk(const QString &url, bool authenticate) const
|
|
{
|
|
/// Get the NameToCookieMap from the Main Cookie Map
|
|
NameToCookieMap nameToCookieMap = m_cookieMap[url];
|
|
|
|
if(m_cookieReadMap[url] != true)
|
|
{
|
|
// Do Not mix the RobloxStudio Settings with Cookies, create a separate plist file for StudioBrowser
|
|
QSettings settings("Roblox", "RobloxStudioBrowser");
|
|
QString cookieName, valueStr, cookieStr, expDateStr;
|
|
|
|
settings.beginGroup(url);
|
|
QStringList childKeys = settings.childKeys();
|
|
bool foundBadFormat = false;
|
|
|
|
for (int j = 0; j < childKeys.size(); ++j)
|
|
{
|
|
cookieName = childKeys.at(j);
|
|
valueStr = settings.value(cookieName).toString();
|
|
|
|
int indexExpDate = valueStr.indexOf("EXP::<");
|
|
int indexCookie = valueStr.indexOf("COOK::<");
|
|
if (indexExpDate == -1 || indexCookie == -1)
|
|
{
|
|
foundBadFormat = true;
|
|
break;
|
|
}
|
|
expDateStr = indexExpDate != -1 ? valueStr.mid(indexExpDate + 6, kExpirationDateStringLength) : "";
|
|
cookieStr = indexCookie != -1 ? valueStr.right(valueStr.length() - indexCookie - 7) : "";
|
|
cookieStr = cookieStr.left(cookieStr.length() - 1);
|
|
|
|
if (!expDateStr.isEmpty())
|
|
{
|
|
QDateTime cookieExpTime = QDateTime::fromString(expDateStr, Qt::ISODate);
|
|
if (cookieExpTime > QDateTime::currentDateTimeUtc())
|
|
{
|
|
QNetworkCookie cookie(childKeys.at(j).toLocal8Bit(), cookieStr.toLocal8Bit());
|
|
cookie.setExpirationDate(cookieExpTime);
|
|
nameToCookieMap[childKeys.at(j)] = cookie;
|
|
|
|
// User is logged in from previous Roblox Studio session
|
|
if (cookieName == ".ROBLOSECURITY" && authenticate)
|
|
authenticateHttpLayer(true);
|
|
|
|
transferCookieToNativeHttpLayer(url, cookie);
|
|
}
|
|
}
|
|
}
|
|
settings.endGroup();
|
|
if (foundBadFormat)
|
|
{
|
|
settings.clear();
|
|
}
|
|
|
|
m_cookieMap[url] = nameToCookieMap;
|
|
m_cookieReadMap[url] = true;
|
|
}
|
|
}
|
|
|
|
void RobloxCookieJar::transferCookieToNativeHttpLayer(const QString& domain, const QNetworkCookie& cookie)
|
|
{
|
|
RBX::Http::setCookiesForDomain(domain.toStdString(),
|
|
QString("%1=%2").arg(QString(cookie.name())).arg(QString(cookie.value())).toStdString());
|
|
}
|
|
|
|
/// Establish the channel for authentication by calling requestAuth
|
|
void RobloxCookieJar::authenticateHttpLayer(bool isAuthenticated) const
|
|
{
|
|
AuthenticationHelper::Instance().authenticateHttpSessionAsync(isAuthenticated);
|
|
}
|
|
|
|
void RobloxCookieJar::lazyInitialization()
|
|
{
|
|
connect(&AuthenticationHelper::Instance(), SIGNAL(authenticationChanged(bool)), this, SLOT(saveCookiesToDisk()));
|
|
}
|
|
|
|
void RobloxCookieJar::reloadCookies(bool authenticate)
|
|
{
|
|
for (CookieReadStatusMap::iterator cookiesReadMapIter = m_cookieReadMap.begin(); cookiesReadMapIter != m_cookieReadMap.end(); ++cookiesReadMapIter)
|
|
{
|
|
//just set the read status to false to reloaded again from disk
|
|
m_cookieReadMap[cookiesReadMapIter.key()] = false;
|
|
//reload cookies for the URL (we do not want any authentication while loading cookies)
|
|
loadCookiesFromDisk(cookiesReadMapIter.key(), authenticate);
|
|
}
|
|
}
|
|
|
|
QString RobloxCookieJar::getCookieValue(const QString &hostURL, const QString& cookieKey)
|
|
{
|
|
QUrl url(hostURL);
|
|
if (!url.isValid())
|
|
return QString();
|
|
|
|
QString result;
|
|
QString hostName = url.host().mid(url.host().indexOf(QString(".")) + 1);
|
|
|
|
QSettings settings("Roblox", "RobloxStudioBrowser");
|
|
settings.beginGroup(hostName);
|
|
QStringList childKeys = settings.childKeys();
|
|
|
|
for (int j = 0; j < childKeys.size(); ++j)
|
|
{
|
|
if (childKeys.at(j) == cookieKey)
|
|
{
|
|
QString valueStr = settings.value(cookieKey).toString();
|
|
|
|
int indexExpDate = valueStr.indexOf("EXP::<");
|
|
QString expDateStr = indexExpDate != -1 ? valueStr.mid(indexExpDate + 6, kExpirationDateStringLength) : "";
|
|
|
|
int indexCookie = valueStr.indexOf("COOK::<");
|
|
QString cookieStr = indexCookie != -1 ? valueStr.right(valueStr.length() - indexCookie - 7) : "";
|
|
cookieStr = cookieStr.left(cookieStr.length() - 1);
|
|
|
|
if (!cookieStr.isEmpty() && !expDateStr.isEmpty())
|
|
{
|
|
QDateTime cookieExpTime = QDateTime::fromString(expDateStr, Qt::ISODate);
|
|
if (cookieExpTime > QDateTime::currentDateTimeUtc())
|
|
result = cookieStr;
|
|
}
|
|
else
|
|
{
|
|
result = cookieStr;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|