How to set QNetworkCookieJar in QWebEngine? - c++

In QWebView it was possible to set a QNetworkCookieJar via QNetworkAccessManager.
QNetworkAccessManager *nam = new QNetworkAccessManager();
nam->setCookieJar(cookieJar);
webView->page()->setNetworkAccessManager(nam);
This was working like a charm.
How can I set a QNetworkCookieJar in new QWebEngine class introduced in Qt5.4?

I found a solution, to share Cookies with QWebEngine and QNetworkAccesManager by using QWebEngineCookieStore. Subclass QNetworkCookieJar:
class CookieWebEngine : public QNetworkCookieJar
{
.....
protected:
// Reimplement this functions to work with your _cookies list;
bool insertCookie(const QNetworkCookie &cookie);
bool deleteCookie(const QNetworkCookie &cookie);
bool updateCookie(const QNetworkCookie &cookie);
bool validateCookie(const QNetworkCookie &cookie, const QUrl &url) const;
private:
// Save Chromium Cookies
QWebEngineCookieStore *_store;
// Save available cookies
QList<QNetworkCookie> _cookies;
}
Now, lets implement a function to load/save cookies in a file:
// Load Chromium Cookies
void CookieWebEngine::loadStore() {
// Save cookies profile shared
QWebEngineProfile::defaultProfile()->setPersistentCookiesPolicy(QWebEngineProfile::ForcePersistentCookies);
_store = WebEngineProfile::defaultProfile()->cookieStore();
connect(_store, &QWebEngineCookieStore::cookieAdded, this, &CookieWebEngine::handleCookieAdded);
_store->loadAllCookies();
}
// Load/Save cookies in arraylist in a file
void CookieWebEngine::load() {
// load cookies and exceptions
qRegisterMetaTypeStreamOperators<QList<QNetworkCookie> >("QList<QNetworkCookie>");
const QString location = cookiesDirectory() + COOKIES_FILE;
QSettings cookieSettings(location, QSettings::IniFormat);
_cookies = qvariant_cast<QList<QNetworkCookie> >(cookieSettings.value(QLatin1String("cookies")));
setAllCookies(_cookies);
// Now user iterate and add it to chromium
for (auto cookie : _cookies) {
_store->setCookie(cookie);
}
cookieSettings.sync();
}
void CookieWebEngine::save()
{
QString directory = cookiesDirectory();
if (!QFile::exists(directory)) {
QDir dir;
dir.mkpath(directory);
}
const QString location = directory + COOKIES_FILE;
QSettings cookieSettings(location, QSettings::IniFormat);
cookieSettings.setValue(QLatin1String("cookies"), QVariant::fromValue<QList<QNetworkCookie>>(_cookies));
cookieSettings.sync();
}
Now, just connect and handle cookies loaded from the webview:
void CookieWebEngine::handleCookieAdded(const QNetworkCookie &cookie)
{
if (insertCookie(cookie)) {
qDebug() << "Handle cookie " << cookie;
}
}
Its working well for me. Now, i use chromium to sign in. After, i save my session cookie in the customized cookiejar and I use it in my customized QNetworkAccesManager.

Related

Problem with website using QNetworkAccessManager Investing.com

I am trying to read automatically some information from investing.com using QNetworkAccessManager. I can read from other sites but this site gives some webmaster tools which I want to access.
https://www.investing.com/webmaster-tools/
I use this query which works in a browser.
Here is my request code
class InvestingAPI: public QObject
{
Q_OBJECT
public:
InvestingAPI();
QueryTechnicals(QString Symbol, int TF1Minites);
signals:
// void NewTechnicalSummary(int Timeframe, QString Symbol, QString Summary);
private slots:
void onData(QNetworkReply *reply);
private:
QNetworkAccessManager qnam ;
};
InvestingAPI::InvestingAPI()
{
connect(&qnam,SIGNAL(finished(QNetworkReply*)),this,SLOT(onData(QNetworkReply*));
connect(&qnam,SIGNAL(encrypted(QNetworkReply*)),this,SLOT(onData(QNetworkReply*))
);
}
InvestingAPI::QueryTechnicals(QString Symbol, int TF1Minites)
{
QString Query;
Query = "http://ssltsw.forexprostools.com/index.php?timeframe=300&lang=1&forex=1&commodities=8830,8836,8831,8849,8833,8862,8832&indices=175,166,172,27,179,170,174&stocks=334,345,346,347,348,349,350&tabs=1,2,3,4%22%20width=%22317%22%20height=%22467%22%3E%3C/iframe%3E%3Cdiv%20class=%22poweredBy%22%20style=%22font-family:arial,helvetica,sans-serif;%20direction:ltr;%22%3E%3Cspan%20style=%22font-size:%2011px;color:%20&selectedTabId=QBS_1";
QNetworkRequest Request;
Request.setSslConfiguration(QSslConfiguration::defaultConfiguration());
connect(&qnam,SIGNAL(finished(QNetworkReply*)),this,SLOT(onData(QNetworkReply*)));
Request.setUrl(QUrl(Query));
Request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");
qnam.get(Request);
}
And I have event
void InvestingAPI::onData(QNetworkReply *reply){
// find data type
// decode and return data to caller
if(reply->error() != QNetworkReply::NoError){
qDebug() << "Error";
qDebug() << reply->errorString();
}
QString html = QString::fromUtf8(reply->readAll());
qDebug() << html;
QString SubData;
}
I do not get an error but I get an empty string rather than the html response.
Please help as I have no idea why this is not working here but is working in the browser.
By default Qt Network does not handle redirects like other tools, so that is why you get an empty data (if you check the "Location" header you will see the redirected url). In this case it is to enable the redirection:
Request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);

QWebEngineView request body interception

A user using QWebEngineView in my application fills some form. This form uses post method to submit data to server. How can I get params from user's body request?
I've found such thing as QWebEngineUrlRequestInterceptor, but it works only for urls.
You can use QWebEnginePage::acceptNavigationRequest.
Whenever a form is submitted, you can get the contents of input by using JavaScript and then accept the request to proceed as usual.
Like Anmol Gautam said, you need to reimplement QWebEnginePage::acceptNavigationRequest function and get needed data using JavaScript.
Here is an example how to do it:
mywebpage.h
#include <QWebEnginePage>
class MyWebPage : public QWebEnginePage
{
Q_OBJECT
public:
explicit MyWebPage(QWebEngineProfile *profile = Q_NULLPTR, QObject *parent = Q_NULLPTR);
protected:
bool acceptNavigationRequest(const QUrl & url, QWebEnginePage::NavigationType type, bool isMainFrame);
}
mywebpage.cpp
MyWebPage::MyWebPage(QWebEngineProfile *profile, QObject *parent):QWebEnginePage(profile, parent),
{
//...
}
bool MyWebPage::acceptNavigationRequest(const QUrl & url, QWebEnginePage::NavigationType type, bool isMainFrame)
{
if(type == QWebEnginePage::NavigationTypeFormSubmitted)
{
qDebug() << "[FORMS] Submitted" << url.toString();
QString jsform = "function getformsvals()"
"{var result;"
"for(var i = 0; i < document.forms.length; i++){"
"for(var x = 0; x < document.forms[i].length; x++){"
"result += document.forms[i].elements[x].name + \" = \" +document.forms[i].elements[x].value;"
"}}"
"return result;} getformsvals();";
this->runJavaScript(jsform, [](const QVariant &result){ qDebug() << "[FORMS] found: " << result; });
}
return true;
}
use QWebEngineView::setPage to set your WebPage subclass to WebView before you call WebViews load function.
Here is a link for more info about HTML DOM forms Collection

How to set redirect_uri using QOAuth2AuthorizationCodeFlow and QOAuthHttpServerReplyHandler

For OAuth 2.0 using QT's networkauth and the new QOAuth2AuthorizationCodeFlow object, how can I set the redirect_uri? My code is below. It results in the following authenticate url being sent:
QOAuth2AuthorizationCodeFlow::buildAuthenticateUrl: https://accounts.google.com/o/oauth2/auth?client_id=123-abc.apps.googleusercontent.com&redirect_uri=http://localhost:65535/cb&response_type=code&scope=email&state=iEIYn5sN
The setting of redirect_uri to "http://localhost", results in an Error 400 redirect_uri_mismatch from google which is obviously expecting the actual redirect hostname to be provided.
GoogleGateway::GoogleGateway() {
auto google = new QOAuth2AuthorizationCodeFlow;
google->setScope("email");
this->connect(google, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, &QDesktopServices::openUrl);
QString val;
QFile file;
file.setFileName("/home/me/client_secret.json");
file.open(QIODevice::ReadOnly | QIODevice::Text);
val = file.readAll();
file.close();
QJsonDocument document = QJsonDocument::fromJson(val.toUtf8());
QJsonObject object = document.object();
const auto settingsObject = object["web"].toObject();
const QUrl authUri(settingsObject["auth_uri"].toString());
const auto clientId = settingsObject["client_id"].toString();
const QUrl tokenUri(settingsObject["token_uri"].toString());
const auto clientSecret(settingsObject["client_secret"].toString());
const auto redirectUris = settingsObject["redirect_uris"].toArray();
const QUrl redirectUri(redirectUris[0].toString());
const auto port = static_cast<quint16>(redirectUri.port());
google->setAuthorizationUrl(authUri);
google->setClientIdentifier(clientId);
google->setAccessTokenUrl(tokenUri);
google->setClientIdentifierSharedKey(clientSecret);
auto replyHandler = new QOAuthHttpServerReplyHandler(port, this);
google->setReplyHandler(replyHandler);
google->grant();
}
To set the redirect_uri, I've tried replacing:
auto replyHandler = new QOAuthHttpServerReplyHandler(port, this);
with
QHostAddress hostaddress = QHostAddress(quint32(1233...));
auto replyHandler = new QOAuthHttpServerReplyHandler(hostaddress, port, this);
with no change in the result.
Have also tried inserting:
replyHandler->setProperty("redirect_uri", "http://abc.xyz.com:65535/cb");
also with no change in the result.
In Qt/5.8/Src/qtnetworkauth/src/oauth/qoauthhttpserverreplyhandler.cpp, we see that the callback address looks suspiciously hard-coded:
QString QOAuthHttpServerReplyHandler::callback() const
{
Q_D(const QOAuthHttpServerReplyHandler);
Q_ASSERT(d->httpServer.isListening());
const QUrl url(QString::fromLatin1("http://localhost:%1/cb").arg(d->httpServer.serverPort()));
return url.toString(QUrl::EncodeDelimiters);
}
This callback() is in turn used in Qt/5.8/Src/qtnetworkauth/src/oauth/qoauth2authorizationcodeflow.cpp to set the redirectUri value:
QUrl QOAuth2AuthorizationCodeFlow::buildAuthenticateUrl(const QVariantMap &parameters)
{
Q_D(QOAuth2AuthorizationCodeFlow);
using Key = QAbstractOAuth2Private::OAuth2KeyString;
if (d->state.isEmpty())
setState(QAbstractOAuth2Private::generateRandomState());
Q_ASSERT(!d->state.isEmpty());
const QString state = d->state;
QVariantMap p(parameters);
QUrl url(d->authorizationUrl);
p.insert(Key::responseType, responseType());
p.insert(Key::clientIdentifier, d->clientCredentials.first);
p.insert(Key::redirectUri, callback());
p.insert(Key::scope, d->scope);
p.insert(Key::state, state);
if (d->modifyParametersFunction)
d->modifyParametersFunction(Stage::RequestingAuthorization, &p);
url.setQuery(d->createQuery(p));
connect(d->replyHandler.data(), &QAbstractOAuthReplyHandler::callbackReceived, this,
&QOAuth2AuthorizationCodeFlow::authorizationCallbackReceived, Qt::UniqueConnection);
setStatus(QAbstractOAuth::Status::NotAuthenticated);
qDebug("QOAuth2AuthorizationCodeFlow::buildAuthenticateUrl: %s", qPrintable(url.toString()));
return url;
}
Is this a bug?
I just solved this by subclassing MyOAuthHttpServerReplyHandler and overriding the definition of callback() to return the URI I wanted.
I'm using Qt 5.15, the redirect_uri could be change form QAbstractOAuth::modifyParametersFunction
m_google = new QOAuth2AuthorizationCodeFlow(m_manager,this);
// set other parameters...
m_google->setModifyParametersFunction(buildModifyParametersFunction());
// return ModifyParametersFunction()
QAbstractOAuth::ModifyParametersFunction GoogleOAuth2Wrapper::buildModifyParametersFunction()
{
const QUrl clientIdentifier = m_google->clientIdentifier();
const QUrl clientIdentifierSharedKey = m_google->clientIdentifierSharedKey();
return [clientIdentifier,clientIdentifierSharedKey]
(QAbstractOAuth::Stage stage, QVariantMap *parameters){
if(stage == QAbstractOAuth::Stage::RequestingAuthorization){
parameters->insert("redirect_uri","https://127.0.0.1:8080/cb"); /*change redirect uri*/
}
};
}
QOAuth2AuthorizationCodeFlow class use QUrl buildAuthenticateUrl(const QVariantMap &parameters) method to send browser for access token,here's source:
QUrl QOAuth2AuthorizationCodeFlow::buildAuthenticateUrl(const QVariantMap &parameters)
{
Q_D(QOAuth2AuthorizationCodeFlow);
using Key = QAbstractOAuth2Private::OAuth2KeyString;
if (d->state.isEmpty())
setState(QAbstractOAuth2Private::generateRandomState());
Q_ASSERT(!d->state.isEmpty());
const QString state = d->state;
QVariantMap p(parameters);
QUrl url(d->authorizationUrl);
p.insert(Key::responseType, responseType());
p.insert(Key::clientIdentifier, d->clientIdentifier);
p.insert(Key::redirectUri, callback());
p.insert(Key::scope, d->scope);
p.insert(Key::state, state);
if (d->modifyParametersFunction) /** Here's what we take part **/
d->modifyParametersFunction(Stage::RequestingAuthorization, &p);
url.setQuery(d->createQuery(p));
connect(d->replyHandler.data(), &QAbstractOAuthReplyHandler::callbackReceived, this,
&QOAuth2AuthorizationCodeFlow::authorizationCallbackReceived, Qt::UniqueConnection);
setStatus(QAbstractOAuth::Status::NotAuthenticated);
qCDebug(d->loggingCategory, "Generated URL: %s", qPrintable(url.toString()));
return url;
}
I hope that is helpful.
Reference
QAbstractOAuth::modifyParametersFunction
QOAuth2AuthorizationCodeFlow::buildAuthenticateUrl source
Using Qt 5.15, having your redirect URI overridden is no longer a problem, but there are two others that you might be running into:
If you use the first URL provided in Google's credential JSON, your app might not hearing back from the user's browser at the end of authorization flow. Instead, just use http://127.0.0.1:1234/ which is reliably the user's machine.
Depending on your luck, the login code returned by Google is URL-encoded and it may contain characters that need to be URL-decoded before requesting a login token from Google itself. This is something that I'd expect Qt to take care of for you, but instead you have to inject a parameter modifier.
Here is the key block, taken from our post on authenticating a Qt app with Google SSO:
this->google->setModifyParametersFunction([](QAbstractOAuth::Stage stage, QVariantMap* parameters) {
// Percent-decode the "code" parameter so Google can match it
if (stage == QAbstractOAuth::Stage::RequestingAccessToken) {
QByteArray code = parameters->value("code").toByteArray();
(*parameters)["code"] = QUrl::fromPercentEncoding(code);
}
});

How to make QNetworkReply to return custom data?

I am going to use QNetworkAccessManager to make requests to HTTP server by my mobile app to the server. The question is, how do you link custom data to each request ? I tried to subclass QNetworkReply but I found out that I have to implement virtual methods close() and isSequential() but I don't know what those should return so I am afraid I am going to break network request functionality.
For example, when my app does the log in procedure, it has to store the email address of the account:
class MyApp : public QObject
{
Q_OBJECT
private:
QNetworkRequest request;
QNetworkReply *reply;
QNetworkAccessManager *manager;
...
}
void MyApp::do_log_in(QString email, QString password) {
QString s;
someobject.email=email; // <-- I have to store email address before sending request to server, but where do I store it?
s.append("http://myapp.com/do-auth.php?email=");
s.append(QUrl::toPercentEncoding(email));
s.append("&password=");
s.append(QUrl::toPercentEncoding(password));
connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(login_finished(QNetworkReply*)));
request.setUrl(QUrl(s));
manager->get(request);
}
void MyApp::login_finished(QNetworkReply *rep) {
DepservReply *reply;
QString email;
....
email= ...... // <-- I need to get the email address from QNetworkReply object somehow
///my code here handling server reply
....
}
So, how do I implement storage and retrieval of email in my case, what classes should I subclass and what methods should I re-implement ?
You can leverage the dynamic property system available in each QObject and hold the data in the reply:
// https://github.com/KubaO/stackoverflown/tree/master/questions/network-reply-tracking-40707025
#include <QtNetwork>
class MyCtl : public QObject
{
Q_OBJECT
QNetworkAccessManager manager{this};
// ...
void reply_finished(QNetworkReply *reply);
public:
MyCtl(QObject *parent = nullptr);
void do_log_in(const QString &email, const QString &password);
};
static const char kAuthGetSalt[] = "req_auth-get-salt";
static const char kDoAuth[] = "req_do-auth";
static const char kEmail[] = "req_email";
static const char kPassword[] = "req_password";
static const auto authGetSaltUrl = QStringLiteral("https://myapp.com/auth-get-salt.php?email=%1");
static const auto doAuthUrl = QStringLiteral("https://myapp.com/do-auth.php?email=%1&passwordHash=%2");
MyCtl::MyCtl(QObject *parent) : QObject{parent}
{
connect(&manager, &QNetworkAccessManager::finished, this, &MyCtl::reply_finished);
}
void MyCtl::do_log_in(const QString &email, const QString &password) {
auto url = authGetSaltUrl.arg(email);
auto reply = manager.get(QNetworkRequest{url});
reply->setProperty(kAuthGetSalt, true);
reply->setProperty(kEmail, email);
reply->setProperty(kPassword, password);
}
void MyCtl::reply_finished(QNetworkReply *reply) {
if (!reply->property(kAuthGetSalt).isNull()) {
reply->deleteLater(); // let's not leak the reply
if (reply->error() == QNetworkReply::NoError) {
auto salt = reply->readAll();
auto email = reply->property(kEmail).toString();
auto password = reply->property(kPassword).toString();
Q_ASSERT(!password.isEmpty() && !email.isEmpty());
QCryptographicHash hasher{QCryptographicHash::Sha1};
hasher.addData(salt); // the server must hash the same way
hasher.addData("----");
hasher.addData(password.toUtf8());
auto hash = hasher.result().toBase64(QByteArray::Base64UrlEncoding);
auto url = doAuthUrl.arg(email).arg(QString::fromLatin1(hash));
auto reply = manager.get(QNetworkRequest{url});
reply->setProperty(kDoAuth, true);
reply->setProperty(kEmail, email);
}
}
else if (!reply->property(kDoAuth).isNull()) {
if (reply->error() == QNetworkReply::NoError) {
auto email = reply->property(kEmail).toString();
// ...
}
}
}
Use a constant for a property name to avoid typos by letting the compiler check that you're using a valid identifier.
The example above rectifies the following critical safety issues in your code:
Sending security credentials over a clear connection: use https://, not http://.
Sending a password in cleartext: instead, send a salted hash of it. Your server should generate a random salt for each account when the accounts are created. Existing accounts can be left unsalted, but they should acquire a salt as soon as the user changes the password.
Also note that a QString to QUrl conversion will automatically percent-encode the string, so doing it explicitly is unnecessary.
In this case email is part of the request's URL so you could just extract it from there (the QNetworkReply has access to the QNetworkRequest it is handling, see QNetworkReply::request()).
You an also store more or less any kind of data as a dynamic property because QNetworkReply is a QObject derived class, see QObject::setProperty().
You can subclass QNAM to get more control of it.
network.h
class QNAMNetwork : public QNetworkAccessManager
{
Q_OBJECT
public:
explicit QNAMNetwork(QObject *parent = 0);
~QNAMNetwork();
inline void insertUserValue(const QString & key, const QString & value){this->m_user_values.insert(key,value);}
inline QString getUserValue(const QString & key){return this->m_user_values.value(key);}
signals:
void requestFinished(ExNetwork *, QNetworkReply *);
private slots:
void _sslErrors(QNetworkReply *, const QList<QSslError> &);
void _finished(QNetworkReply *);
private:
QMap<QString, QString> m_user_values;
};
network.cpp
QNAMNetwork::QNAMNetwork(QObject *parent):QNetworkAccessManager(parent)
{
connect(this, &QNAMNetwork::sslErrors, this, &QNAMNetwork::_sslErrors);
connect(this, &QNAMNetwork::finished, this, &QNAMNetwork::_finished);
}
QNAMNetwork::~QNAMNetwork()
{
//qDebug() << __FUNCTION__ << QString::number((long)this,16);
}
void QNAMNetwork::_sslErrors(QNetworkReply * reply, const QList<QSslError> & errors)
{
reply->ignoreSslErrors(errors);
}
void QNAMNetwork::_finished(QNetworkReply * reply)
{
emit requestFinished(this, reply);
}
usecase:
QNAMNetwork * network = new QNAMNetwork(this);
network->insertUserValue("email","yourmail#mail.com");
connect(network, SIGNAL(requestFinished(QNAMNetwork*,QNetworkReply*)), this, SLOT(requestFinished(QNAMNetwork*,QNetworkReply*)));
QNetworkRequest req(QUrl::fromUserInput(query)); //get url
network->get(req);
...
void YourClass::requestFinished(QNAMNetwork * net, QNetworkReply * rep)
{
QString email = net->getUserValue("email");
net->deleteLater();
rep->deleteLater();
}

How do I save cookies with Qt?

I am trying to save cookies that are produced by my app to disk location such as C:\Users\Username\AppData\Local\MyCompany\MyApp. I have implemented a webview and have pretty much finished coding my simple browser the final thing to do is save cookies.
I am can qDebug() the cookies I get from the webapp and they show the cookies are formed correctly but I am a)unsure where to go from there and b) not 100% sure on how to make a subclass of the cookiejar class?
Below I create my cookiejar object in my MainWindow constructor
view = new QWebView(this);
jar = new QNetworkCookieJar;
view->page()->networkAccessManager()->setCookieJar(jar);
And in my replyfinished slot I can see the cookie contained in the reply and I attempt to save it but nothing happens and I receive no run time errors. There isn't a great deal of stuff out there on this and have seen a few posts where the instruction was to make a subclass QNetworkCookieJar but have not made a subclass in Qt/C++ before.
Is there a simple way to store cookies, I am not looking for anything fancy. The cookies just make sure some check boxes are ticked on the login page.
// SLOT that accepts the read data from the webpage
void MainWindow::slotReplyFinished(QNetworkReply *reply){
if(reply->isFinished()){
QVariant variantCookies = reply->header(QNetworkRequest::SetCookieHeader);
QList<QNetworkCookie> cookies = qvariant_cast<QList<QNetworkCookie> >(variantCookies);
qDebug() << "Cookies reply: " << cookies;
QNetworkCookie cookie; //Create a cookie
jar = new QNetworkCookieJar;
//view->page()->networkAccessManager()->setCookieJar(jar);
jar->setCookiesFromUrl(cookies, reply->request().url());
//qDebug() << "Saved cookies: " << jar->getAllCookies();
}
qDebug() << "Network reply: " << reply->errorString() << reply->error() << reply->request().url();
}
You will need to subclass QNetworkCookieJar and in that class you should implement your own persistent storage.
class MyNetworkCookieJar : public QNetworkCookieJar {
public:
bool saveCookiesToDisk() {
// .. my implementation
return true; // if i did
}
bool loadCookiesFromDisk() {
// .. load from disk
return false; // if unable to.
}
}
The sample application from Qt project implements a persistent cookie store, it could be a good starting point for you: http://qt.gitorious.org/qt/qt/trees/4.8/demos/browser
look at cookiejar.h and cookiejar.cpp
Base of qt example, http://qt.gitorious.org/qt/qt/trees/4.8/demos/browser, i wrote this class that save and use one cookie for me. Perhaps it helps you too. Note that it just save one cookie and not list of cookies.
#include "cookiejar.h"
CookieJar::CookieJar(QObject *parent)
: QNetworkCookieJar(parent)
, m_loaded(false)
{
}
void CookieJar::load()
{
if (m_loaded)
return;
QSettings settings;
settings.beginGroup(QLatin1String("cookies"));
QList<QNetworkCookie> savedCookies = QNetworkCookie::parseCookies(settings.value("cookies").toByteArray());
for (int j = 0; j < savedCookies.count(); j++)
insertCookie(savedCookies.at(j));
m_loaded = true;
emit cookiesChanged();
}
void CookieJar::save()
{
if (!m_loaded)
return;
QList<QNetworkCookie> cookies = allCookies();
QSettings settings;
settings.beginGroup(QLatin1String("cookies"));
settings.setValue("cookies", cookies[0].toRawForm());
}
QList<QNetworkCookie> CookieJar::cookiesForUrl(const QUrl &url) const
{
// This function is called by the default QNetworkAccessManager::createRequest(),
// which adds the cookies returned by this function to the request being sent.
CookieJar *that = const_cast<CookieJar*>(this);
if (!m_loaded)
that->load();
return QNetworkCookieJar::cookiesForUrl(url);
}
bool CookieJar::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url)
{
if (!m_loaded)
load();
QNetworkCookieJar::setCookiesFromUrl(cookieList, url);
save(); //Save cookie permanently in setting file.
emit cookiesChanged();
return true;
}
#Musa's answer is good but it only saves one cookie. I recommend using the Qt folk's implementation from the old qmlviewer located here: http://code.qt.io/cgit/qt/qt.git/tree/tools/qml/qmlruntime.cpp?h=4.7#n438
Here's the code:
class PersistentCookieJar : public QNetworkCookieJar {
public:
PersistentCookieJar(QObject *parent) : QNetworkCookieJar(parent) { load(); }
~PersistentCookieJar() { save(); }
virtual QList<QNetworkCookie> cookiesForUrl(const QUrl &url) const
{
QMutexLocker lock(&mutex);
return QNetworkCookieJar::cookiesForUrl(url);
}
virtual bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url)
{
QMutexLocker lock(&mutex);
return QNetworkCookieJar::setCookiesFromUrl(cookieList, url);
}
private:
void save()
{
QMutexLocker lock(&mutex);
QList<QNetworkCookie> list = allCookies();
QByteArray data;
foreach (QNetworkCookie cookie, list) {
if (!cookie.isSessionCookie()) {
data.append(cookie.toRawForm());
data.append("\n");
}
}
QSettings settings;
settings.setValue("Cookies",data);
}
void load()
{
QMutexLocker lock(&mutex);
QSettings settings;
QByteArray data = settings.value("Cookies").toByteArray();
setAllCookies(QNetworkCookie::parseCookies(data));
}
mutable QMutex mutex;
};