cookiejar.cpp Example File

webenginewidgets/demobrowser/cookiejar.cpp
/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** BSD License Usage ** Alternatively, you may use this file under the terms of the BSD license ** as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of The Qt Company Ltd nor the names of its ** contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/
#include "cookiejar.h" #include "autosaver.h" #include <QtCore/QDateTime> #include <QtCore/QDir> #include <QtCore/QFile> #include <QtCore/QMetaEnum> #include <QtCore/QSettings> #include <QtCore/QUrl> #include <QtWidgets/QCompleter> #include <QtGui/QDesktopServices> #include <QtGui/QFont> #include <QtGui/QFontMetrics> #include <QtWidgets/QHeaderView> #include <QtGui/QKeyEvent> #include <QtCore/QSortFilterProxyModel> #include <QtNetwork/QNetworkCookie> #include <QWebEngineSettings> #include <QtCore/QDebug> static const unsigned int JAR_VERSION = 23; QDataStream &operator<<(QDataStream &stream, const QList<QNetworkCookie> &list) { stream << JAR_VERSION; stream << quint32(list.size()); for (int i = 0; i < list.size(); ++i) stream << list.at(i).toRawForm(); return stream; } QDataStream &operator>>(QDataStream &stream, QList<QNetworkCookie> &list) { list.clear(); quint32 version; stream >> version; if (version != JAR_VERSION) return stream; quint32 count; stream >> count; for (quint32 i = 0; i < count; ++i) { QByteArray value; stream >> value; QList<QNetworkCookie> newCookies = QNetworkCookie::parseCookies(value); if (newCookies.count() == 0 && value.length() != 0) { qWarning() << "CookieJar: Unable to parse saved cookie:" << value; } for (int j = 0; j < newCookies.count(); ++j) list.append(newCookies.at(j)); if (stream.atEnd()) break; } return stream; } CookieJar::CookieJar(QObject *parent) : QNetworkCookieJar(parent) , m_loaded(false) , m_saveTimer(new AutoSaver(this)) , m_acceptCookies(AcceptOnlyFromSitesNavigatedTo) { } CookieJar::~CookieJar() { if (m_keepCookies == KeepUntilExit) clear(); m_saveTimer->saveIfNeccessary(); } void CookieJar::clear() { setAllCookies(QList<QNetworkCookie>()); m_saveTimer->changeOccurred(); emit cookiesChanged(); } void CookieJar::load() { if (m_loaded) return; // load cookies and exceptions qRegisterMetaTypeStreamOperators<QList<QNetworkCookie> >("QList<QNetworkCookie>"); QSettings cookieSettings(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1String("/cookies.ini"), QSettings::IniFormat); setAllCookies(qvariant_cast<QList<QNetworkCookie> >(cookieSettings.value(QLatin1String("cookies")))); cookieSettings.beginGroup(QLatin1String("Exceptions")); m_exceptions_block = cookieSettings.value(QLatin1String("block")).toStringList(); m_exceptions_allow = cookieSettings.value(QLatin1String("allow")).toStringList(); m_exceptions_allowForSession = cookieSettings.value(QLatin1String("allowForSession")).toStringList(); qSort(m_exceptions_block.begin(), m_exceptions_block.end()); qSort(m_exceptions_allow.begin(), m_exceptions_allow.end()); qSort(m_exceptions_allowForSession.begin(), m_exceptions_allowForSession.end()); loadSettings(); } void CookieJar::loadSettings() { QSettings settings; settings.beginGroup(QLatin1String("cookies")); QByteArray value = settings.value(QLatin1String("acceptCookies"), QLatin1String("AcceptOnlyFromSitesNavigatedTo")).toByteArray(); QMetaEnum acceptPolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("AcceptPolicy")); m_acceptCookies = acceptPolicyEnum.keyToValue(value) == -1 ? AcceptOnlyFromSitesNavigatedTo : static_cast<AcceptPolicy>(acceptPolicyEnum.keyToValue(value)); value = settings.value(QLatin1String("keepCookiesUntil"), QLatin1String("KeepUntilExpire")).toByteArray(); QMetaEnum keepPolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("KeepPolicy")); m_keepCookies = keepPolicyEnum.keyToValue(value) == -1 ? KeepUntilExpire : static_cast<KeepPolicy>(keepPolicyEnum.keyToValue(value)); if (m_keepCookies == KeepUntilExit) setAllCookies(QList<QNetworkCookie>()); m_loaded = true; emit cookiesChanged(); } void CookieJar::save() { if (!m_loaded) return; purgeOldCookies(); QString directory = QStandardPaths::writableLocation(QStandardPaths::DataLocation); if (directory.isEmpty()) directory = QDir::homePath() + QLatin1String("/.") + QCoreApplication::applicationName(); if (!QFile::exists(directory)) { QDir dir; dir.mkpath(directory); } QSettings cookieSettings(directory + QLatin1String("/cookies.ini"), QSettings::IniFormat); QList<QNetworkCookie> cookies = allCookies(); for (int i = cookies.count() - 1; i >= 0; --i) { if (cookies.at(i).isSessionCookie()) cookies.removeAt(i); } cookieSettings.setValue(QLatin1String("cookies"), QVariant::fromValue<QList<QNetworkCookie> >(cookies)); cookieSettings.beginGroup(QLatin1String("Exceptions")); cookieSettings.setValue(QLatin1String("block"), m_exceptions_block); cookieSettings.setValue(QLatin1String("allow"), m_exceptions_allow); cookieSettings.setValue(QLatin1String("allowForSession"), m_exceptions_allowForSession); // save cookie settings QSettings settings; settings.beginGroup(QLatin1String("cookies")); QMetaEnum acceptPolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("AcceptPolicy")); settings.setValue(QLatin1String("acceptCookies"), QLatin1String(acceptPolicyEnum.valueToKey(m_acceptCookies))); QMetaEnum keepPolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("KeepPolicy")); settings.setValue(QLatin1String("keepCookiesUntil"), QLatin1String(keepPolicyEnum.valueToKey(m_keepCookies))); } void CookieJar::purgeOldCookies() { QList<QNetworkCookie> cookies = allCookies(); if (cookies.isEmpty()) return; int oldCount = cookies.count(); QDateTime now = QDateTime::currentDateTime(); for (int i = cookies.count() - 1; i >= 0; --i) { if (!cookies.at(i).isSessionCookie() && cookies.at(i).expirationDate() < now) cookies.removeAt(i); } if (oldCount == cookies.count()) return; setAllCookies(cookies); emit cookiesChanged(); } QList<QNetworkCookie> CookieJar::cookiesForUrl(const QUrl &url) const { CookieJar *that = const_cast<CookieJar*>(this); if (!m_loaded) that->load(); QWebEngineSettings *globalSettings = QWebEngineSettings::globalSettings(); if (globalSettings->testAttribute(QWebEngineSettings::PrivateBrowsingEnabled)) { QList<QNetworkCookie> noCookies; return noCookies; } return QNetworkCookieJar::cookiesForUrl(url); } bool CookieJar::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url) { if (!m_loaded) load(); QWebEngineSettings *globalSettings = QWebEngineSettings::globalSettings(); if (globalSettings->testAttribute(QWebEngineSettings::PrivateBrowsingEnabled)) return false; QString host = url.host(); bool eBlock = qBinaryFind(m_exceptions_block.begin(), m_exceptions_block.end(), host) != m_exceptions_block.end(); bool eAllow = qBinaryFind(m_exceptions_allow.begin(), m_exceptions_allow.end(), host) != m_exceptions_allow.end(); bool eAllowSession = qBinaryFind(m_exceptions_allowForSession.begin(), m_exceptions_allowForSession.end(), host) != m_exceptions_allowForSession.end(); bool addedCookies = false; // pass exceptions bool acceptInitially = (m_acceptCookies != AcceptNever); if ((acceptInitially && !eBlock) || (!acceptInitially && (eAllow || eAllowSession))) { // pass url domain == cookie domain QDateTime soon = QDateTime::currentDateTime(); soon = soon.addDays(90); foreach (QNetworkCookie cookie, cookieList) { QList<QNetworkCookie> lst; if (m_keepCookies == KeepUntilTimeLimit && !cookie.isSessionCookie() && cookie.expirationDate() > soon) { cookie.setExpirationDate(soon); } lst += cookie; if (QNetworkCookieJar::setCookiesFromUrl(lst, url)) { addedCookies = true; } else { // finally force it in if wanted if (m_acceptCookies == AcceptAlways) { QList<QNetworkCookie> cookies = allCookies(); cookies += cookie; setAllCookies(cookies); addedCookies = true; } #if 0 else qWarning() << "setCookiesFromUrl failed" << url << cookieList.value(0).toRawForm(); #endif } } } if (addedCookies) { m_saveTimer->changeOccurred(); emit cookiesChanged(); } return addedCookies; } CookieJar::AcceptPolicy CookieJar::acceptPolicy() const { if (!m_loaded) (const_cast<CookieJar*>(this))->load(); return m_acceptCookies; } void CookieJar::setAcceptPolicy(AcceptPolicy policy) { if (!m_loaded) load(); if (policy == m_acceptCookies) return; m_acceptCookies = policy; m_saveTimer->changeOccurred(); } CookieJar::KeepPolicy CookieJar::keepPolicy() const { if (!m_loaded) (const_cast<CookieJar*>(this))->load(); return m_keepCookies; } void CookieJar::setKeepPolicy(KeepPolicy policy) { if (!m_loaded) load(); if (policy == m_keepCookies) return; m_keepCookies = policy; m_saveTimer->changeOccurred(); } QStringList CookieJar::blockedCookies() const { if (!m_loaded) (const_cast<CookieJar*>(this))->load(); return m_exceptions_block; } QStringList CookieJar::allowedCookies() const { if (!m_loaded) (const_cast<CookieJar*>(this))->load(); return m_exceptions_allow; } QStringList CookieJar::allowForSessionCookies() const { if (!m_loaded) (const_cast<CookieJar*>(this))->load(); return m_exceptions_allowForSession; } void CookieJar::setBlockedCookies(const QStringList &list) { if (!m_loaded) load(); m_exceptions_block = list; qSort(m_exceptions_block.begin(), m_exceptions_block.end()); m_saveTimer->changeOccurred(); } void CookieJar::setAllowedCookies(const QStringList &list) { if (!m_loaded) load(); m_exceptions_allow = list; qSort(m_exceptions_allow.begin(), m_exceptions_allow.end()); m_saveTimer->changeOccurred(); } void CookieJar::setAllowForSessionCookies(const QStringList &list) { if (!m_loaded) load(); m_exceptions_allowForSession = list; qSort(m_exceptions_allowForSession.begin(), m_exceptions_allowForSession.end()); m_saveTimer->changeOccurred(); } CookieModel::CookieModel(CookieJar *cookieJar, QObject *parent) : QAbstractTableModel(parent) , m_cookieJar(cookieJar) { connect(m_cookieJar, SIGNAL(cookiesChanged()), this, SLOT(cookiesChanged())); m_cookieJar->load(); } QVariant CookieModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::SizeHintRole) { QFont font; font.setPointSize(10); QFontMetrics fm(font); int height = fm.height() + fm.height()/3; int width = fm.width(headerData(section, orientation, Qt::DisplayRole).toString()); return QSize(width, height); } if (orientation == Qt::Horizontal) { if (role != Qt::DisplayRole) return QVariant(); switch (section) { case 0: return tr("Website"); case 1: return tr("Name"); case 2: return tr("Path"); case 3: return tr("Secure"); case 4: return tr("Expires"); case 5: return tr("Contents"); default: return QVariant(); } } return QAbstractTableModel::headerData(section, orientation, role); } QVariant CookieModel::data(const QModelIndex &index, int role) const { QList<QNetworkCookie> lst; if (m_cookieJar) lst = m_cookieJar->allCookies(); if (index.row() < 0 || index.row() >= lst.size()) return QVariant(); switch (role) { case Qt::DisplayRole: case Qt::EditRole: { QNetworkCookie cookie = lst.at(index.row()); switch (index.column()) { case 0: return cookie.domain(); case 1: return cookie.name(); case 2: return cookie.path(); case 3: return cookie.isSecure(); case 4: return cookie.expirationDate(); case 5: return cookie.value(); } } case Qt::FontRole:{ QFont font; font.setPointSize(10); return font; } } return QVariant(); } int CookieModel::columnCount(const QModelIndex &parent) const { return (parent.isValid()) ? 0 : 6; } int CookieModel::rowCount(const QModelIndex &parent) const { return (parent.isValid() || !m_cookieJar) ? 0 : m_cookieJar->allCookies().count(); } bool CookieModel::removeRows(int row, int count, const QModelIndex &parent) { if (parent.isValid() || !m_cookieJar) return false; int lastRow = row + count - 1; beginRemoveRows(parent, row, lastRow); QList<QNetworkCookie> lst = m_cookieJar->allCookies(); for (int i = lastRow; i >= row; --i) { lst.removeAt(i); } m_cookieJar->setAllCookies(lst); endRemoveRows(); return true; } void CookieModel::cookiesChanged() { beginResetModel(); endResetModel(); } CookiesDialog::CookiesDialog(CookieJar *cookieJar, QWidget *parent) : QDialog(parent) { setupUi(this); setWindowFlags(Qt::Sheet); CookieModel *model = new CookieModel(cookieJar, this); m_proxyModel = new QSortFilterProxyModel(this); connect(search, SIGNAL(textChanged(QString)), m_proxyModel, SLOT(setFilterFixedString(QString))); connect(removeButton, SIGNAL(clicked()), cookiesTable, SLOT(removeOne())); connect(removeAllButton, SIGNAL(clicked()), cookiesTable, SLOT(removeAll())); m_proxyModel->setSourceModel(model); cookiesTable->verticalHeader()->hide(); cookiesTable->setSelectionBehavior(QAbstractItemView::SelectRows); cookiesTable->setModel(m_proxyModel); cookiesTable->setAlternatingRowColors(true); cookiesTable->setTextElideMode(Qt::ElideMiddle); cookiesTable->setShowGrid(false); cookiesTable->setSortingEnabled(true); QFont f = font(); f.setPointSize(10); QFontMetrics fm(f); int height = fm.height() + fm.height()/3; cookiesTable->verticalHeader()->setDefaultSectionSize(height); cookiesTable->verticalHeader()->setMinimumSectionSize(-1); for (int i = 0; i < model->columnCount(); ++i){ int header = cookiesTable->horizontalHeader()->sectionSizeHint(i); switch (i) { case 0: header = fm.width(QLatin1String("averagehost.domain.com")); break; case 1: header = fm.width(QLatin1String("_session_id")); break; case 4: header = fm.width(QDateTime::currentDateTime().toString(Qt::LocalDate)); break; } int buffer = fm.width(QLatin1String("xx")); header += buffer; cookiesTable->horizontalHeader()->resizeSection(i, header); } cookiesTable->horizontalHeader()->setStretchLastSection(true); } CookieExceptionsModel::CookieExceptionsModel(CookieJar *cookiejar, QObject *parent) : QAbstractTableModel(parent) , m_cookieJar(cookiejar) { m_allowedCookies = m_cookieJar->allowedCookies(); m_blockedCookies = m_cookieJar->blockedCookies(); m_sessionCookies = m_cookieJar->allowForSessionCookies(); } QVariant CookieExceptionsModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::SizeHintRole) { QFont font; font.setPointSize(10); QFontMetrics fm(font); int height = fm.height() + fm.height()/3; int width = fm.width(headerData(section, orientation, Qt::DisplayRole).toString()); return QSize(width, height); } if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case 0: return tr("Website"); case 1: return tr("Status"); } } return QAbstractTableModel::headerData(section, orientation, role); } QVariant CookieExceptionsModel::data(const QModelIndex &index, int role) const { if (index.row() < 0 || index.row() >= rowCount()) return QVariant(); switch (role) { case Qt::DisplayRole: case Qt::EditRole: { int row = index.row(); if (row < m_allowedCookies.count()) { switch (index.column()) { case 0: return m_allowedCookies.at(row); case 1: return tr("Allow"); } } row = row - m_allowedCookies.count(); if (row < m_blockedCookies.count()) { switch (index.column()) { case 0: return m_blockedCookies.at(row); case 1: return tr("Block"); } } row = row - m_blockedCookies.count(); if (row < m_sessionCookies.count()) { switch (index.column()) { case 0: return m_sessionCookies.at(row); case 1: return tr("Allow For Session"); } } } case Qt::FontRole:{ QFont font; font.setPointSize(10); return font; } } return QVariant(); } int CookieExceptionsModel::columnCount(const QModelIndex &parent) const { return (parent.isValid()) ? 0 : 2; } int CookieExceptionsModel::rowCount(const QModelIndex &parent) const { return (parent.isValid() || !m_cookieJar) ? 0 : m_allowedCookies.count() + m_blockedCookies.count() + m_sessionCookies.count(); } bool CookieExceptionsModel::removeRows(int row, int count, const QModelIndex &parent) { if (parent.isValid() || !m_cookieJar) return false; int lastRow = row + count - 1; beginRemoveRows(parent, row, lastRow); for (int i = lastRow; i >= row; --i) { if (i < m_allowedCookies.count()) { m_allowedCookies.removeAt(row); continue; } i = i - m_allowedCookies.count(); if (i < m_blockedCookies.count()) { m_blockedCookies.removeAt(row); continue; } i = i - m_blockedCookies.count(); if (i < m_sessionCookies.count()) { m_sessionCookies.removeAt(row); continue; } } m_cookieJar->setAllowedCookies(m_allowedCookies); m_cookieJar->setBlockedCookies(m_blockedCookies); m_cookieJar->setAllowForSessionCookies(m_sessionCookies); endRemoveRows(); return true; } CookiesExceptionsDialog::CookiesExceptionsDialog(CookieJar *cookieJar, QWidget *parent) : QDialog(parent) , m_cookieJar(cookieJar) { setupUi(this); setWindowFlags(Qt::Sheet); connect(removeButton, SIGNAL(clicked()), exceptionTable, SLOT(removeOne())); connect(removeAllButton, SIGNAL(clicked()), exceptionTable, SLOT(removeAll())); exceptionTable->verticalHeader()->hide(); exceptionTable->setSelectionBehavior(QAbstractItemView::SelectRows); exceptionTable->setAlternatingRowColors(true); exceptionTable->setTextElideMode(Qt::ElideMiddle); exceptionTable->setShowGrid(false); exceptionTable->setSortingEnabled(true); m_exceptionsModel = new CookieExceptionsModel(cookieJar, this); m_proxyModel = new QSortFilterProxyModel(this); m_proxyModel->setSourceModel(m_exceptionsModel); connect(search, SIGNAL(textChanged(QString)), m_proxyModel, SLOT(setFilterFixedString(QString))); exceptionTable->setModel(m_proxyModel); CookieModel *cookieModel = new CookieModel(cookieJar, this); domainLineEdit->setCompleter(new QCompleter(cookieModel, domainLineEdit)); connect(domainLineEdit, SIGNAL(textChanged(QString)), this, SLOT(textChanged(QString))); connect(blockButton, SIGNAL(clicked()), this, SLOT(block())); connect(allowButton, SIGNAL(clicked()), this, SLOT(allow())); connect(allowForSessionButton, SIGNAL(clicked()), this, SLOT(allowForSession())); QFont f = font(); f.setPointSize(10); QFontMetrics fm(f); int height = fm.height() + fm.height()/3; exceptionTable->verticalHeader()->setDefaultSectionSize(height); exceptionTable->verticalHeader()->setMinimumSectionSize(-1); for (int i = 0; i < m_exceptionsModel->columnCount(); ++i){ int header = exceptionTable->horizontalHeader()->sectionSizeHint(i); switch (i) { case 0: header = fm.width(QLatin1String("averagebiglonghost.domain.com")); break; case 1: header = fm.width(QLatin1String("Allow For Session")); break; } int buffer = fm.width(QLatin1String("xx")); header += buffer; exceptionTable->horizontalHeader()->resizeSection(i, header); } } void CookiesExceptionsDialog::textChanged(const QString &text) { bool enabled = !text.isEmpty(); blockButton->setEnabled(enabled); allowButton->setEnabled(enabled); allowForSessionButton->setEnabled(enabled); } void CookiesExceptionsDialog::block() { if (domainLineEdit->text().isEmpty()) return; m_exceptionsModel->m_blockedCookies.append(domainLineEdit->text()); m_cookieJar->setBlockedCookies(m_exceptionsModel->m_blockedCookies); m_exceptionsModel->beginResetModel(); m_exceptionsModel->endResetModel(); } void CookiesExceptionsDialog::allow() { if (domainLineEdit->text().isEmpty()) return; m_exceptionsModel->m_allowedCookies.append(domainLineEdit->text()); m_cookieJar->setAllowedCookies(m_exceptionsModel->m_allowedCookies); m_exceptionsModel->beginResetModel(); m_exceptionsModel->endResetModel(); } void CookiesExceptionsDialog::allowForSession() { if (domainLineEdit->text().isEmpty()) return; m_exceptionsModel->m_sessionCookies.append(domainLineEdit->text()); m_cookieJar->setAllowForSessionCookies(m_exceptionsModel->m_sessionCookies); m_exceptionsModel->beginResetModel(); m_exceptionsModel->endResetModel(); }