File indexing completed on 2024-12-22 04:43:19

0001 /*
0002     This file is part of the KDE project.
0003 
0004     SPDX-FileCopyrightText: 2018 Stefano Crocco <stefano.crocco@alice.it>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-or-later
0007 */
0008 
0009 #ifndef WEBENGINEPARTCOOKIEJAR_H
0010 #define WEBENGINEPARTCOOKIEJAR_H
0011 
0012 #include "interfaces/cookiejar.h"
0013 #include "qtwebengine6compat.h"
0014 
0015 #include <QObject>
0016 #include <QNetworkCookie>
0017 #include <QHash>
0018 #include <QUrl>
0019 #include <QWebEngineCookieStore>
0020 #include <QStringList>
0021 #include <QVariant>
0022 #include <QVector>
0023 #include <QDBusInterface>
0024 #include <QSet>
0025 
0026 #include "kwebenginepartlib_export.h"
0027 
0028 class QDebug;
0029 class QWidget;
0030 class QWebEngineProfile;
0031 class QDBusPendingCallWatcher;
0032 
0033 /**
0034  * @brief Class which enforces the Cookie user settings and allows access to the cookies in the store
0035  *
0036  * @note All functions which access the cookie store are asynchronous
0037  */
0038 class KWEBENGINEPARTLIB_EXPORT WebEnginePartCookieJar6 : public KonqInterfaces::CookieJar
0039 {
0040     Q_OBJECT
0041 
0042 public:
0043     
0044     /**
0045     * @brief Constructor
0046     * 
0047     * @param [in,out] prof the profile containing the store to synchronize with
0048     * @param parent the parent object
0049     */
0050     WebEnginePartCookieJar6(QWebEngineProfile* prof, QObject* parent = nullptr);
0051 
0052     /**
0053      * @brief Destructor
0054      */
0055     ~WebEnginePartCookieJar6() override;
0056 
0057     /**
0058      * @brief A set with all cookies in the cookie store
0059      * @return a set with all cookies in the cookie store
0060      * @note This function is synchronous, as it doesn't access the store but returns the cookies already inserted in #m_cookies.
0061      * It is possible that a cookie has been added to the store but the store hasn't yet emitted the corresponding `cookieAdded()`
0062      * signal: in this case, that cookie won't be included in the returned value.
0063      */
0064     QSet<QNetworkCookie> cookies() const override;
0065 
0066 public slots:
0067 
0068     /**
0069      * @brief Asks the store to remove all cookies
0070      *
0071      * @note This will also reset the choices made by the user for all cookies
0072      */
0073     void removeAllCookies() override;
0074 
0075     /**
0076      * @brief Asks the store to remove all cookies from a domain
0077      * @param domain the domain whose cookies should be removed. A leading dot is ignored
0078      * @note This will also reset the choices made by the user for the removed cookies
0079      */
0080     void removeCookiesWithDomain(const QString &domain) override;
0081 
0082     /**
0083      * @brief Asks the store to remove the given cookies
0084      *
0085      * If you need to remove multiple cookies, it's better to call this rather than
0086      * calling removeCookie() multiple times, as this will only update the cookie advice
0087      * file once (if needed)
0088      * @note This will also reset the choices made by the user for the removed cookies
0089      */
0090     void removeCookies(const QVector<QNetworkCookie> & cookies) override;
0091 
0092     /**
0093      * @brief Asks the store to remove the given cookie
0094      * @param cookie the cookie to remove
0095      * @param origin only remove the cookie if it originates from this URL
0096      * @note This will also reset the choices made by the user for the removed cookie
0097      */
0098     void removeCookie(const QNetworkCookie &cookie, const QUrl &origin=QUrl()) override;
0099 
0100     /**
0101      * @brief Asks the cookie store to remove all session cookies
0102      */
0103     void removeSessionCookies() override;
0104 
0105 private slots:
0106 
0107     /**
0108      * @brief Read the cookie configuration settings from the configuration file
0109      *
0110      * If cookies have been disabled, the cookie store is emptied. All other
0111      * configuration changes won't be applied to cookies already in the store.
0112      * They will be applied next time the cookie jar is created (most likely, when
0113      * the application is next started).
0114      */
0115     void applyConfiguration();
0116 
0117     /**
0118      * @brief Slot called in response to the `QWebEngineCookieStore::cookieAdded` signal
0119      *
0120      * It checks whether the policy chosen by the user allows accepting the cookie and
0121      * removes it if it doesn't. If, instead, the cookie can be accepted, it adds it
0122      * to #m_cookies.
0123      * @note Theoretically, most of what this method does should be done by filterCookie().
0124      * Unfortunately, that function doesn't have access to the cookie details it would
0125      * need, so we need to do it here. This means that even cookies which should be rejected
0126      * are actually added and then removed.
0127     */
0128     void handleCookieAdditionToStore(const QNetworkCookie &cookie);
0129 
0130     /**
0131     * @brief Removes the given cookie from the list of cookies.
0132     *
0133     * This slot is called in response to `QWebEngineCookieStore::cookieRemoved` signal.
0134     * 
0135     * @param cookie the cookie to remove
0136     */
0137     void removeCookieFromSet(const QNetworkCookie &cookie);
0138 
0139     /**
0140      * @brief The path of the file where to save the choices made by the user for single cookies
0141      * @return The path of the file where to save the choices made by the user for single cookies
0142      */
0143     static QString cookieAdvicePath();
0144 
0145     static QString cookieDataPath();
0146 
0147     /**
0148      * @brief Saves to file the choices the user made for individual cookies
0149      */
0150     void saveCookieAdvice();
0151 
0152     /**
0153      * @brief Reads from file the choices the user made for individual cookies
0154      */
0155     void readCookieAdvice();
0156 
0157     void loadCookies();
0158     
0159 private:
0160     
0161     /**
0162     * @brief An identifier for a cookie
0163     * 
0164     * The identifier is made by the cookie's name, domain and path
0165     */
0166     struct CookieIdentifier{
0167         
0168         /**
0169         * @brief Default constructor
0170         */
0171         CookieIdentifier(){}
0172         
0173         /**
0174         * @brief Constructor
0175         * 
0176         * @param cookie the cookie to create the identifier for
0177         */
0178         CookieIdentifier(const QNetworkCookie &cookie);
0179         
0180         /**
0181         * @brief Constructor
0182         * 
0183         * @param n the cookie's name
0184         * @param d the cookie's domain
0185         * @param p the cookie's path
0186         */
0187         CookieIdentifier(const QString &n, const QString &d, const QString &p);
0188        
0189         /**
0190          * Comparison operator
0191          * 
0192          * Two cookies are equal if all their fields are equal
0193          * @param other the identifier to compare this identifier to
0194          * @return `true` if the two identifiers' name, domain and path are equal and `false` if at least one of them is different
0195          */
0196         bool operator== (const CookieIdentifier &other) const {return name == other.name && domain == other.domain && path == other.path;}
0197 
0198         QString name; ///< The name of the cookie
0199         QString domain; ///< The domain of the cookie
0200         QString path; ///< The path of the cookie
0201         
0202     };
0203 
0204     /**
0205      * @brief Decides what to do with a cookie according to the policy chosen by the user
0206      *
0207      * This method compares the cookie with the settings in #m_policy and returns the appropriate action.
0208      * If needed, it calls askCookieQuestion() to ask the user what to do.
0209      * @param cookie the cookie
0210      * @return the CookieAdvice describing what to do with the cookie
0211      */
0212     CookieAdvice decideCookieAction(const QNetworkCookie cookie);
0213 
0214     /**
0215      * @brief Asks the user what to do with a given cookie
0216      *
0217      * It displays a dialog where the user can choose whether to accept the cookie, reject it or accept it
0218      * only for the current session.
0219      *
0220      * In the dialog, the user can also decide to apply his choice to all cookies or to the cookies from
0221      * the same domain. If he does, then the configuration file is changed accordingly.
0222      * @param cookie the cookie
0223      * @return what to do with the cookie
0224      */
0225     CookieAdvice askCookieQuestion(const QNetworkCookie cookie);
0226 
0227     /**
0228      * @brief Updates the policy in the configuration file
0229      *
0230      * This is only called if, when asked what to do about a cookie, the user chooses to apply its choice
0231      * to all cookies or to the cookies from the same domain.
0232      */
0233     void writeConfig();
0234     
0235     friend QDebug operator<<(QDebug, const CookieIdentifier &);
0236     friend QDataStream& operator>>(QDataStream &ds, WebEnginePartCookieJar6::CookieIdentifier &id);
0237     friend QDataStream& operator<<(QDataStream &ds, const WebEnginePartCookieJar6::CookieIdentifier &id);
0238 
0239     using CookieIdentifierList = QList<CookieIdentifier>;
0240 
0241     /**
0242     * @brief Decides whether to accept or not a cookie
0243     *
0244     * In theory, this function should use the policy chosen by the user to decide whether to accept or reject the cookie.
0245     * However, to do so, it would need details about the cookie which aren't provided by @p req. Because of this, this
0246     * function only rejects the cookie in two circumstances:
0247     * - if cookies are completely disabled
0248     * - if it's a third party cookie and the policy forbids accepting third party cookies
0249     * In all other cases, this function accepts the cookie, leaving to handleCookieAdditionToStore() the task of removing
0250     * it if needed.
0251     * @note It's not clear whether accepting the cookie and then removing it has the same effect as rejecting it in the first
0252     * place. However, it's the best that can be done without reducing the range of policies the user can choose from.
0253     * 
0254     * @param req the request to filter
0255     * @return `false` if cookies are disabled or if @p cookie is a third party cookie and third party cookies have been disabled
0256     * and `true` otherwise
0257     * @sa handleCookieAdditionToStore()
0258     */
0259     bool filterCookie(const QWebEngineCookieStore::FilterRequest &req);
0260 
0261     /**
0262     * @brief The `QWebEngineCookieStore` to use
0263     */
0264     QWebEngineCookieStore* m_cookieStore;
0265     
0266     /**
0267     * @brief Overload of `qHash` for a CookieIdentifier
0268     * 
0269     * @param id: the other identifier
0270     * @param seed: the seed
0271     * @return The hash value of the identifier
0272     */
0273     friend qHashReturnType qHash(const CookieIdentifier &id, uint seed){return qHash(QStringList{id.name, id.domain, id.path}, seed);};
0274     
0275     /**
0276      * @brief The cookies stored in #m_cookieStore
0277      *
0278      * This is needed to implement CookieJar::cookies(), since `QWebEngineCookieStore` doesn't allow to retrieve the list of
0279      * cookies.
0280      */
0281     QSet<QNetworkCookie> m_cookies;
0282 
0283 
0284     /**
0285      * @brief Struct describing the cookie policy chosen by the user
0286      */
0287     struct CookiePolicy {
0288         bool cookiesEnabled = true; ///< Whether cookies are enabled or should all be rejected
0289         bool rejectThirdPartyCookies = true; ///< Whether third party cookies should all be rejected
0290         bool acceptSessionCookies = true; ///< Whether session cookies should be accepted by default
0291         CookieAdvice defaultPolicy = CookieAdvice::Accept; ///< What to do for cookies which don't match any other rule
0292         /**
0293          * @brief What to do for cookies belonging to specific domains
0294          *
0295          * Each entry has the domain as key and the action to carry out for cookies of that domain as value
0296          */
0297         QHash<QString, CookieAdvice> domainExceptions;
0298         /**
0299          * @brief What to do for specific cookies
0300          *
0301          * Each entry has the cookie identifier as key and the action as value
0302          */
0303         QHash<CookieIdentifier, CookieAdvice> cookieExceptions;
0304     };
0305     CookiePolicy m_policy; ///< The policy to apply to cookies
0306 };
0307 
0308 /**
0309 * @brief Overload of `qHash` for a `QNetworkCookie`
0310 *
0311 * @param cookie: the cookie
0312 * @param seed: the seed
0313 * @return The hash value of the cookie
0314 */
0315 qHashReturnType qHash(const QNetworkCookie &cookie, uint seed);
0316 
0317 /**
0318 * @brief Overload of operator `<<` to allow a WebEnginePartCookieJar6::CookieIdentifier to be written to a `QDebug`
0319 * 
0320 * @param deb the debug object
0321 * @param id the identifier to write
0322 * @return the debug object
0323 */
0324 QDebug operator<<(QDebug deb, const WebEnginePartCookieJar6::CookieIdentifier &id);
0325 
0326 /**
0327  * @brief override of operator `>>` allowing to read a CookieIdentifier from a `QDataStream`
0328  * @param ds the `QDataStream` to read the CookieIdentifier from
0329  * @param id the CookieIdentifier to read to @p ds
0330  * @return @p ds
0331  */
0332 QDataStream& operator>>( QDataStream &ds, WebEnginePartCookieJar6::CookieIdentifier &id);
0333 
0334 /**
0335  * @brief override of operator `<<` allowing to write a CookieIdentifier to a `QDataStream`
0336  * @param ds the `QDataStream` to write the CookieIdentifier to
0337  * @param id the CookieIdentifier to write to @p ds
0338  * @return @p ds
0339  */
0340 QDataStream& operator<< (QDataStream &ds, const WebEnginePartCookieJar6::CookieIdentifier &id);
0341 
0342 QDataStream& operator<< (QDataStream &ds, const QNetworkCookie &cookie);
0343 QDataStream& operator>> (QDataStream &ds, QNetworkCookie &cookie);
0344 
0345 #endif // WEBENGINEPARTCOOKIEJAR_H