eric6/Helpviewer/CookieJar/CookieJar.py

changeset 7220
5cf645f6daab
parent 7218
eaf2cf171f3a
parent 7211
1c97f3142fa8
child 7221
0485ccdf7877
equal deleted inserted replaced
7218:eaf2cf171f3a 7220:5cf645f6daab
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, QByteArray, QDataStream, QIODevice, \
15 QSettings, QDateTime
16 from PyQt5.QtNetwork import QNetworkCookieJar, QNetworkCookie
17 from PyQt5.QtWebKit import QWebSettings
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 JAR_VERSION = 23
34
35 AcceptAlways = 0
36 AcceptNever = 1
37 AcceptOnlyFromSitesNavigatedTo = 2
38
39 KeepUntilExpire = 0
40 KeepUntilExit = 1
41 KeepUntilTimeLimit = 2
42
43 Allow = 0
44 Block = 1
45 AllowForSession = 2
46
47 def __init__(self, parent=None):
48 """
49 Constructor
50
51 @param parent reference to the parent object (QObject)
52 """
53 super(CookieJar, self).__init__(parent)
54
55 self.__loaded = False
56 self.__acceptCookies = self.AcceptOnlyFromSitesNavigatedTo
57 self.__saveTimer = AutoSaver(self, self.save)
58
59 self.__cookiesFile = os.path.join(Utilities.getConfigDir(),
60 "browser", "cookies.ini")
61
62 def saveCookies(self, cookiesList):
63 """
64 Public method to save the cookies.
65
66 @param cookiesList list of cookies to be saved
67 @return saved cookies as a byte array (QByteArray)
68 """
69 data = QByteArray()
70 stream = QDataStream(data, QIODevice.WriteOnly)
71 stream.setVersion(QDataStream.Qt_4_6)
72 stream.writeUInt16(self.JAR_VERSION)
73 stream.writeUInt32(len(cookiesList))
74 for cookie in cookiesList:
75 stream << cookie.toRawForm()
76
77 return data
78
79 def loadCookies(self, cookies):
80 """
81 Public method to restore the saved cookies.
82
83 @param cookies byte array containing the saved cookies (QByteArray)
84 @return list of cookies
85 """
86 if cookies.isEmpty():
87 return []
88
89 cookiesList = []
90 data = QByteArray(cookies)
91 stream = QDataStream(data, QIODevice.ReadOnly)
92 stream.setVersion(QDataStream.Qt_4_6)
93
94 version = stream.readUInt16()
95 if version != self.JAR_VERSION:
96 return []
97
98 stream.readUInt32() # number of cookies
99
100 rawCookie = QByteArray()
101 while not stream.atEnd():
102 stream >> rawCookie
103 newCookies = QNetworkCookie.parseCookies(rawCookie)
104 for newCookie in newCookies:
105 cookiesList.append(newCookie)
106
107 return cookiesList
108
109 def close(self):
110 """
111 Public slot to close the cookie jar.
112 """
113 if self.__loaded and self.__keepCookies == self.KeepUntilExit:
114 self.clear()
115 self.__saveTimer.saveIfNeccessary()
116
117 def clear(self):
118 """
119 Public method to clear all cookies.
120 """
121 if not self.__loaded:
122 self.load()
123
124 self.setAllCookies([])
125 self.__saveTimer.changeOccurred()
126 self.cookiesChanged.emit()
127
128 def load(self):
129 """
130 Public method to load the cookies.
131 """
132 if self.__loaded:
133 return
134
135 cookieSettings = QSettings(self.__cookiesFile, QSettings.IniFormat)
136
137 # load cookies
138 cookies = cookieSettings.value("Cookies")
139 if cookies:
140 cookiesList = self.loadCookies(cookies)
141 else:
142 cookiesList = []
143 self.setAllCookies(cookiesList)
144
145 # load exceptions
146 self.__exceptionsBlock = Preferences.toList(
147 cookieSettings.value("Exceptions/block"))
148 self.__exceptionsAllow = Preferences.toList(
149 cookieSettings.value("Exceptions/allow"))
150 self.__exceptionsAllowForSession = Preferences.toList(
151 cookieSettings.value("Exceptions/allowForSession"))
152 self.__exceptionsBlock.sort()
153 self.__exceptionsAllow.sort()
154 self.__exceptionsAllowForSession.sort()
155
156 self.__acceptCookies = Preferences.getHelp("AcceptCookies")
157 self.__keepCookies = Preferences.getHelp("KeepCookiesUntil")
158 if self.__keepCookies == self.KeepUntilExit:
159 self.setAllCookies([])
160
161 self.__filterTrackingCookies = Preferences.toBool(
162 Preferences.getHelp("FilterTrackingCookies"))
163
164 self.__loaded = True
165 self.cookiesChanged.emit()
166
167 def save(self):
168 """
169 Public method to save the cookies.
170 """
171 if not self.__loaded:
172 return
173
174 self.__purgeOldCookies()
175
176 cookieSettings = QSettings(self.__cookiesFile, QSettings.IniFormat)
177
178 cookiesList = self.allCookies()
179 for index in range(len(cookiesList) - 1, -1, -1):
180 if cookiesList[index].isSessionCookie():
181 del cookiesList[index]
182 cookies = self.saveCookies(cookiesList)
183
184 cookieSettings.setValue("Cookies", cookies)
185 cookieSettings.setValue("Exceptions/block", self.__exceptionsBlock)
186 cookieSettings.setValue("Exceptions/allow", self.__exceptionsAllow)
187 cookieSettings.setValue("Exceptions/allowForSession",
188 self.__exceptionsAllowForSession)
189
190 Preferences.setHelp("AcceptCookies", self.__acceptCookies)
191 Preferences.setHelp("KeepCookiesUntil", self.__keepCookies)
192 Preferences.setHelp("FilterTrackingCookies",
193 self.__filterTrackingCookies)
194
195 def __purgeOldCookies(self):
196 """
197 Private method to purge old cookies.
198 """
199 cookies = self.allCookies()
200 if len(cookies) == 0:
201 return
202
203 oldCount = len(cookies)
204 now = QDateTime.currentDateTime()
205 for index in range(len(cookies) - 1, -1, -1):
206 if not cookies[index].isSessionCookie() and \
207 cookies[index].expirationDate() < now:
208 del cookies[index]
209 if oldCount == len(cookies):
210 return
211 self.setAllCookies(cookies)
212 self.cookiesChanged.emit()
213
214 def cookiesForUrl(self, url):
215 """
216 Public method to get the cookies for a URL.
217
218 @param url URL to get cookies for (QUrl)
219 @return list of cookies (list of QNetworkCookie)
220 """
221 if not self.__loaded:
222 self.load()
223
224 globalSettings = QWebSettings.globalSettings()
225 if globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled):
226 return []
227
228 return QNetworkCookieJar.cookiesForUrl(self, url)
229
230 def setCookiesFromUrl(self, cookieList, url):
231 """
232 Public method to set cookies for a URL.
233
234 @param cookieList list of cookies to set (list of QNetworkCookie)
235 @param url url to set cookies for (QUrl)
236 @return flag indicating cookies were set (boolean)
237 """
238 if not self.__loaded:
239 self.load()
240
241 globalSettings = QWebSettings.globalSettings()
242 if globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled):
243 return False
244
245 host = url.host()
246 eBlock = self.__isOnDomainList(self.__exceptionsBlock, host)
247 eAllow = not eBlock and \
248 self.__isOnDomainList(self.__exceptionsAllow, host)
249 eAllowSession = not eBlock and \
250 not eAllow and \
251 self.__isOnDomainList(
252 self.__exceptionsAllowForSession, host)
253
254 addedCookies = False
255 acceptInitially = self.__acceptCookies != self.AcceptNever
256 if (acceptInitially and not eBlock) or \
257 (not acceptInitially and (eAllow or eAllowSession)):
258 # url domain == cookie domain
259 soon = QDateTime.currentDateTime()
260 soon = soon.addDays(90)
261 for cookie in cookieList:
262 lst = []
263 if not (self.__filterTrackingCookies and
264 cookie.name().startsWith(b"__utm")):
265 if eAllowSession:
266 cookie.setExpirationDate(QDateTime())
267 if self.__keepCookies == self.KeepUntilTimeLimit and \
268 not cookie.isSessionCookie and \
269 cookie.expirationDate() > soon:
270 cookie.setExpirationDate(soon)
271 lst.append(cookie)
272 if QNetworkCookieJar.setCookiesFromUrl(self, lst, url):
273 addedCookies = True
274 elif self.__acceptCookies == self.AcceptAlways:
275 # force it in if wanted
276 cookies = self.allCookies()
277 for ocookie in cookies[:]:
278 # does the cookie exist already?
279 if cookie.name() == ocookie.name() and \
280 cookie.domain() == ocookie.domain() and \
281 cookie.path() == ocookie.path():
282 # found a match
283 cookies.remove(ocookie)
284
285 cookies.append(cookie)
286 self.setAllCookies(cookies)
287 addedCookies = True
288
289 if addedCookies:
290 self.__saveTimer.changeOccurred()
291 self.cookiesChanged.emit()
292
293 return addedCookies
294
295 def acceptPolicy(self):
296 """
297 Public method to get the accept policy.
298
299 @return current accept policy
300 """
301 if not self.__loaded:
302 self.load()
303 return self.__acceptCookies
304
305 def setAcceptPolicy(self, policy):
306 """
307 Public method to set the accept policy.
308
309 @param policy accept policy to be set
310 """
311 if not self.__loaded:
312 self.load()
313 if policy > self.AcceptOnlyFromSitesNavigatedTo:
314 return
315 if policy == self.__acceptCookies:
316 return
317 self.__acceptCookies = policy
318 self.__saveTimer.changeOccurred()
319
320 def keepPolicy(self):
321 """
322 Public method to get the keep policy.
323
324 @return keep policy
325 """
326 if not self.__loaded:
327 self.load()
328 return self.__keepCookies
329
330 def setKeepPolicy(self, policy):
331 """
332 Public method to set the keep policy.
333
334 @param policy keep policy to be set
335 """
336 if not self.__loaded:
337 self.load()
338 if policy > self.KeepUntilTimeLimit:
339 return
340 if policy == self.__keepCookies:
341 return
342 self.__keepCookies = policy
343 self.__saveTimer.changeOccurred()
344
345 def blockedCookies(self):
346 """
347 Public method to return the blocked cookies.
348
349 @return list of blocked cookies (list of strings)
350 """
351 if not self.__loaded:
352 self.load()
353 return self.__exceptionsBlock
354
355 def allowedCookies(self):
356 """
357 Public method to return the allowed cookies.
358
359 @return list of allowed cookies (list of strings)
360 """
361 if not self.__loaded:
362 self.load()
363 return self.__exceptionsAllow
364
365 def allowForSessionCookies(self):
366 """
367 Public method to return the allowed session cookies.
368
369 @return list of allowed session cookies (list of strings)
370 """
371 if not self.__loaded:
372 self.load()
373 return self.__exceptionsAllowForSession
374
375 def setBlockedCookies(self, list_):
376 """
377 Public method to set the list of blocked cookies.
378
379 @param list_ list of blocked cookies (list of strings)
380 """
381 if not self.__loaded:
382 self.load()
383 self.__exceptionsBlock = list_[:]
384 self.__exceptionsBlock.sort()
385 self.__applyRules()
386 self.__saveTimer.changeOccurred()
387
388 def setAllowedCookies(self, list_):
389 """
390 Public method to set the list of allowed cookies.
391
392 @param list_ list of allowed cookies (list of strings)
393 """
394 if not self.__loaded:
395 self.load()
396 self.__exceptionsAllow = list_[:]
397 self.__exceptionsAllow.sort()
398 self.__applyRules()
399 self.__saveTimer.changeOccurred()
400
401 def setAllowForSessionCookies(self, list_):
402 """
403 Public method to set the list of allowed session cookies.
404
405 @param list_ list of allowed session cookies (list of strings)
406 """
407 if not self.__loaded:
408 self.load()
409 self.__exceptionsAllowForSession = list_[:]
410 self.__exceptionsAllowForSession.sort()
411 self.__applyRules()
412 self.__saveTimer.changeOccurred()
413
414 def filterTrackingCookies(self):
415 """
416 Public method to get the filter tracking cookies flag.
417
418 @return filter tracking cookies flag (boolean)
419 """
420 return self.__filterTrackingCookies
421
422 def setFilterTrackingCookies(self, filterTrackingCookies):
423 """
424 Public method to set the filter tracking cookies flag.
425
426 @param filterTrackingCookies filter tracking cookies flag (boolean)
427 """
428 if filterTrackingCookies == self.__filterTrackingCookies:
429 return
430
431 self.__filterTrackingCookies = filterTrackingCookies
432 self.__saveTimer.changeOccurred()
433
434 def __isOnDomainList(self, rules, domain):
435 """
436 Private method to check, if either the rule matches the domain exactly
437 or the domain ends with ".rule".
438
439 @param rules list of rules (list of strings)
440 @param domain domain name to check (string)
441 @return flag indicating a match (boolean)
442 """
443 for rule in rules:
444 if rule.startswith("."):
445 if domain.endswith(rule):
446 return True
447
448 withoutDot = rule[1:]
449 if domain == withoutDot:
450 return True
451 else:
452 domainEnding = domain[-(len(rule) + 1):]
453 if domainEnding and \
454 domainEnding[0] == "." and \
455 domain.endswith(rule):
456 return True
457
458 if rule == domain:
459 return True
460
461 return False
462
463 def __applyRules(self):
464 """
465 Private method to apply the cookie rules.
466 """
467 cookiesList = self.allCookies()
468 changed = False
469
470 for index in range(len(cookiesList) - 1, -1, -1):
471 cookie = cookiesList[index]
472 if self.__isOnDomainList(self.__exceptionsBlock, cookie.domain()):
473 del cookiesList[index]
474 changed = True
475 elif self.__isOnDomainList(self.__exceptionsAllowForSession,
476 cookie.domain()):
477 cookie.setExpirationDate(QDateTime())
478 changed = True
479
480 if changed:
481 self.setAllCookies(cookiesList)
482 self.__saveTimer.changeOccurred()
483 self.cookiesChanged.emit()
484
485 def cookies(self):
486 """
487 Public method to get the cookies of the cookie jar.
488
489 @return list of all cookies (list of QNetworkCookie)
490 """
491 if not self.__loaded:
492 self.load()
493
494 return self.allCookies()
495
496 def setCookies(self, cookies):
497 """
498 Public method to set all cookies.
499
500 @param cookies list of cookies to be set (list of QNetworkCookie)
501 """
502 if not self.__loaded:
503 self.load()
504
505 self.setAllCookies(cookies)
506 self.__saveTimer.changeOccurred()
507 self.cookiesChanged.emit()

eric ide

mercurial