eric6/WebBrowser/CookieJar/CookieJar.py

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