src/eric7/WebBrowser/CookieJar/CookieJar.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 8881
54e42bc2437a
child 9221
bf71ee032bb4
equal deleted inserted replaced
9208:3fc8dfeb6ebe 9209:b99e7fd55fd3
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2009 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a QNetworkCookieJar subclass with various accept policies.
8 """
9
10 import os
11
12 from PyQt6.QtCore import pyqtSignal, pyqtSlot, QSettings
13 from PyQt6.QtNetwork import QNetworkCookieJar, QNetworkCookie
14
15 from WebBrowser.WebBrowserWindow import WebBrowserWindow
16
17 from Utilities.AutoSaver import AutoSaver
18 import Utilities
19 import Preferences
20
21
22 class CookieJar(QNetworkCookieJar):
23 """
24 Class implementing a QNetworkCookieJar subclass with various accept
25 policies.
26
27 @signal cookiesChanged() emitted after the cookies have been changed
28 """
29 cookiesChanged = pyqtSignal()
30
31 AcceptAlways = 0
32 AcceptNever = 1
33 AcceptOnlyFromSitesNavigatedTo = 2
34 AcceptMax = 2
35
36 KeepUntilExpire = 0
37 KeepUntilExit = 1
38 KeepMax = 1
39
40 Allow = 0
41 Block = 1
42 AllowForSession = 2
43
44 def __init__(self, parent=None):
45 """
46 Constructor
47
48 @param parent reference to the parent object (QObject)
49 """
50 super().__init__(parent)
51
52 self.__loaded = False
53 self.__acceptCookies = self.AcceptOnlyFromSitesNavigatedTo
54 self.__saveTimer = AutoSaver(self, self.__save)
55
56 self.__cookiesFile = os.path.join(Utilities.getConfigDir(),
57 "web_browser", "cookies.ini")
58
59 self.__store = WebBrowserWindow.webProfile().cookieStore()
60 self.__store.setCookieFilter(self.__cookieFilter)
61 self.__store.cookieAdded.connect(self.__cookieAdded)
62 self.__store.cookieRemoved.connect(self.__cookieRemoved)
63
64 self.__load()
65 self.__store.loadAllCookies()
66
67 def close(self):
68 """
69 Public slot to close the cookie jar.
70 """
71 if not self.__loaded:
72 self.__load()
73
74 if self.__keepCookies == self.KeepUntilExit:
75 self.clear()
76 self.__saveTimer.saveIfNeccessary()
77
78 def clear(self):
79 """
80 Public method to clear all cookies.
81 """
82 if not self.__loaded:
83 self.__load()
84
85 self.setAllCookies([])
86 self.__store.deleteAllCookies()
87 self.cookiesChanged.emit()
88
89 def removeCookies(self, cookies):
90 """
91 Public method to remove a list of cookies.
92
93 @param cookies list of cookies to be removed
94 @type list of QNetworkCookie
95 """
96 wasBlocked = self.blockSignals(True)
97 for cookie in cookies:
98 self.__store.deleteCookie(cookie)
99 self.blockSignals(wasBlocked)
100
101 self.cookiesChanged.emit()
102
103 def removeCookie(self, cookie):
104 """
105 Public method to remove a cookie.
106
107 @param cookie cookie to be removed
108 @type QNetworkCookie
109 """
110 self.__store.deleteCookie(cookie)
111 self.cookiesChanged.emit()
112
113 def __load(self):
114 """
115 Private method to load the cookies settings.
116 """
117 if self.__loaded:
118 return
119
120 cookieSettings = QSettings(self.__cookiesFile,
121 QSettings.Format.IniFormat)
122
123 # load exceptions
124 self.__exceptionsBlock = Preferences.toList(
125 cookieSettings.value("Exceptions/block"))
126 self.__exceptionsAllow = Preferences.toList(
127 cookieSettings.value("Exceptions/allow"))
128 self.__exceptionsAllowForSession = Preferences.toList(
129 cookieSettings.value("Exceptions/allowForSession"))
130 self.__exceptionsBlock.sort()
131 self.__exceptionsAllow.sort()
132 self.__exceptionsAllowForSession.sort()
133
134 self.__acceptCookies = Preferences.getWebBrowser("AcceptCookies")
135 self.__keepCookies = Preferences.getWebBrowser("KeepCookiesUntil")
136 if self.__keepCookies == self.KeepUntilExit:
137 self.clear()
138
139 self.__filterTrackingCookies = Preferences.toBool(
140 Preferences.getWebBrowser("FilterTrackingCookies"))
141
142 self.__loaded = True
143 self.cookiesChanged.emit()
144
145 def __save(self):
146 """
147 Private method to save the cookies settings.
148 """
149 if not self.__loaded:
150 return
151
152 cookieSettings = QSettings(self.__cookiesFile,
153 QSettings.Format.IniFormat)
154
155 cookieSettings.setValue("Exceptions/block", self.__exceptionsBlock)
156 cookieSettings.setValue("Exceptions/allow", self.__exceptionsAllow)
157 cookieSettings.setValue("Exceptions/allowForSession",
158 self.__exceptionsAllowForSession)
159
160 Preferences.setWebBrowser("AcceptCookies", self.__acceptCookies)
161 Preferences.setWebBrowser("KeepCookiesUntil", self.__keepCookies)
162 Preferences.setWebBrowser("FilterTrackingCookies",
163 self.__filterTrackingCookies)
164
165 @pyqtSlot(QNetworkCookie)
166 def __cookieAdded(self, cookie):
167 """
168 Private slot handling the addition of a cookie.
169
170 @param cookie cookie which was added
171 @type QNetworkCookie
172 """
173 if self.__rejectCookie(cookie, cookie.domain()):
174 self.__store.deleteCookie(cookie)
175 return
176
177 self.insertCookie(cookie)
178 self.cookiesChanged.emit()
179
180 @pyqtSlot(QNetworkCookie)
181 def __cookieRemoved(self, cookie):
182 """
183 Private slot handling the removal of a cookie.
184
185 @param cookie cookie which was removed
186 @type QNetworkCookie
187 """
188 if self.deleteCookie(cookie):
189 self.cookiesChanged.emit()
190
191 def __cookieFilter(self, request):
192 """
193 Private method to filter cookies.
194
195 Note: This method is used for Qt 5.11+ only.
196
197 @param request reference to the cookie filter request object
198 @type QWebEngineCookieStore.FilterRequest
199 @return flag indicating cookie access is allowed
200 @rtype bool
201 """
202 if not self.__loaded:
203 self.__load()
204
205 if self.__acceptCookies == self.AcceptNever:
206 res = self.__isOnDomainList(self.__exceptionsAllow,
207 request.origin.host())
208 if not res:
209 return False
210
211 if self.__acceptCookies == self.AcceptAlways:
212 res = self.__isOnDomainList(self.__exceptionsBlock,
213 request.origin.host())
214 if res:
215 return False
216
217 if (
218 self.__acceptCookies == self.AcceptOnlyFromSitesNavigatedTo and
219 request.thirdParty
220 ):
221 return False
222
223 return True
224
225 def __rejectCookie(self, cookie, cookieDomain):
226 """
227 Private method to test, if a cookie shall be rejected.
228
229 @param cookie cookie to be tested
230 @type QNetworkCookie
231 @param cookieDomain domain of the cookie
232 @type str
233 @return flag indicating the cookie shall be rejected
234 @rtype bool
235 """
236 if not self.__loaded:
237 self.__load()
238
239 if self.__acceptCookies == self.AcceptNever:
240 res = self.__isOnDomainList(self.__exceptionsAllow, cookieDomain)
241 if not res:
242 return True
243
244 if self.__acceptCookies == self.AcceptAlways:
245 res = self.__isOnDomainList(self.__exceptionsBlock, cookieDomain)
246 if res:
247 return True
248
249 if self.__acceptCookies == self.AcceptOnlyFromSitesNavigatedTo:
250 mainWindow = WebBrowserWindow.mainWindow()
251 if mainWindow is not None:
252 browser = mainWindow.getWindow().currentBrowser()
253 if browser is not None:
254 url = browser.url()
255 if url.isValid():
256 host = url.host()
257 else:
258 host = ""
259 res = self.__matchDomain(cookieDomain, host)
260 if not res:
261 return True
262
263 if self.__filterTrackingCookies and cookie.name().startsWith(b"__utm"):
264 return True
265
266 return False
267
268 def acceptPolicy(self):
269 """
270 Public method to get the accept policy.
271
272 @return current accept policy
273 """
274 if not self.__loaded:
275 self.__load()
276
277 return self.__acceptCookies
278
279 def setAcceptPolicy(self, policy):
280 """
281 Public method to set the accept policy.
282
283 @param policy accept policy to be set
284 """
285 if not self.__loaded:
286 self.__load()
287
288 if policy > self.AcceptMax:
289 return
290 if policy == self.__acceptCookies:
291 return
292
293 self.__acceptCookies = policy
294 self.__saveTimer.changeOccurred()
295
296 def keepPolicy(self):
297 """
298 Public method to get the keep policy.
299
300 @return keep policy
301 """
302 if not self.__loaded:
303 self.__load()
304
305 return self.__keepCookies
306
307 def setKeepPolicy(self, policy):
308 """
309 Public method to set the keep policy.
310
311 @param policy keep policy to be set
312 """
313 if not self.__loaded:
314 self.__load()
315
316 if policy > self.KeepMax:
317 return
318 if policy == self.__keepCookies:
319 return
320
321 self.__keepCookies = policy
322 self.__saveTimer.changeOccurred()
323
324 def blockedCookies(self):
325 """
326 Public method to return the list of blocked domains.
327
328 @return list of blocked domains (list of strings)
329 """
330 if not self.__loaded:
331 self.__load()
332
333 return self.__exceptionsBlock
334
335 def allowedCookies(self):
336 """
337 Public method to return the list of allowed domains.
338
339 @return list of allowed domains (list of strings)
340 """
341 if not self.__loaded:
342 self.__load()
343
344 return self.__exceptionsAllow
345
346 def allowForSessionCookies(self):
347 """
348 Public method to return the list of allowed session cookie domains.
349
350 @return list of allowed session cookie domains (list of strings)
351 """
352 if not self.__loaded:
353 self.__load()
354
355 return self.__exceptionsAllowForSession
356
357 def setBlockedCookies(self, list_):
358 """
359 Public method to set the list of blocked domains.
360
361 @param list_ list of blocked domains (list of strings)
362 """
363 if not self.__loaded:
364 self.__load()
365
366 self.__exceptionsBlock = list_[:]
367 self.__exceptionsBlock.sort()
368 self.__saveTimer.changeOccurred()
369
370 def setAllowedCookies(self, list_):
371 """
372 Public method to set the list of allowed domains.
373
374 @param list_ list of allowed domains (list of strings)
375 """
376 if not self.__loaded:
377 self.__load()
378
379 self.__exceptionsAllow = list_[:]
380 self.__exceptionsAllow.sort()
381 self.__saveTimer.changeOccurred()
382
383 def setAllowForSessionCookies(self, list_):
384 """
385 Public method to set the list of allowed session cookie domains.
386
387 @param list_ list of allowed session cookie domains (list of strings)
388 """
389 if not self.__loaded:
390 self.__load()
391
392 self.__exceptionsAllowForSession = list_[:]
393 self.__exceptionsAllowForSession.sort()
394 self.__saveTimer.changeOccurred()
395
396 def filterTrackingCookies(self):
397 """
398 Public method to get the filter tracking cookies flag.
399
400 @return filter tracking cookies flag (boolean)
401 """
402 return self.__filterTrackingCookies
403
404 def setFilterTrackingCookies(self, filterTrackingCookies):
405 """
406 Public method to set the filter tracking cookies flag.
407
408 @param filterTrackingCookies filter tracking cookies flag (boolean)
409 """
410 if filterTrackingCookies == self.__filterTrackingCookies:
411 return
412
413 self.__filterTrackingCookies = filterTrackingCookies
414 self.__saveTimer.changeOccurred()
415
416 def __isOnDomainList(self, rules, domain):
417 """
418 Private method to check, if either the rule matches the domain exactly
419 or the domain ends with ".rule".
420
421 @param rules list of rules (list of strings)
422 @param domain domain name to check (string)
423 @return flag indicating a match (boolean)
424 """
425 for rule in rules:
426 if rule.startswith("."):
427 if domain.endswith(rule):
428 return True
429
430 withoutDot = rule[1:]
431 if domain == withoutDot:
432 return True
433 else:
434 domainEnding = domain[-(len(rule) + 1):]
435 if (
436 domainEnding and
437 domainEnding[0] == "." and
438 domain.endswith(rule)
439 ):
440 return True
441
442 if rule == domain:
443 return True
444
445 return False
446
447 def __matchDomain(self, cookieDomain, siteDomain):
448 """
449 Private method to check, if a URLs host matches a cookie domain
450 according to RFC 6265.
451
452 @param cookieDomain domain of the cookie
453 @type str
454 @param siteDomain domain or host of an URL
455 @type str
456 @return flag indicating a match
457 @rtype bool
458 """
459 if not siteDomain:
460 # empty URLs always match
461 return True
462
463 if cookieDomain.startswith("."):
464 cookieDomain = cookieDomain[1:]
465 if siteDomain.startswith("."):
466 siteDomain = siteDomain[1:]
467
468 if cookieDomain == siteDomain:
469 return True
470
471 if not siteDomain.endswith(cookieDomain):
472 return False
473
474 index = siteDomain.find(cookieDomain)
475 return index > 0 and siteDomain[index - 1] == "."
476
477 def cookies(self):
478 """
479 Public method to get the cookies of the cookie jar.
480
481 @return list of all cookies (list of QNetworkCookie)
482 """
483 if not self.__loaded:
484 self.__load()
485
486 return self.allCookies()
487
488 def cookieDomains(self):
489 """
490 Public method to get a list of all domains used by the cookies.
491
492 @return list of domain names
493 @rtype list of str
494 """
495 domains = []
496 for cookie in self.cookies():
497 domain = cookie.domain()
498 if domain not in domains:
499 domains.append(domain)
500
501 return domains

eric ide

mercurial