12 import os |
12 import os |
13 import re |
13 import re |
14 import hashlib |
14 import hashlib |
15 import base64 |
15 import base64 |
16 |
16 |
17 from PyQt4.QtCore import pyqtSignal, Qt, QObject, QByteArray, QDateTime, QUrl, \ |
17 from PyQt4.QtCore import pyqtSignal, Qt, QObject, QByteArray, QDateTime, \ |
18 QCryptographicHash, QFile, QIODevice, QTextStream, QDate, QTime |
18 QUrl, QCryptographicHash, QFile, QIODevice, QTextStream, QDate, QTime |
19 from PyQt4.QtNetwork import QNetworkReply |
19 from PyQt4.QtNetwork import QNetworkReply |
20 |
20 |
21 from E5Gui import E5MessageBox |
21 from E5Gui import E5MessageBox |
22 |
22 |
23 import Utilities |
23 import Utilities |
69 self.__domainRestrictedCssRules = [] |
69 self.__domainRestrictedCssRules = [] |
70 self.__elementHidingRules = "" |
70 self.__elementHidingRules = "" |
71 self.__documentRules = [] |
71 self.__documentRules = [] |
72 self.__elemhideRules = [] |
72 self.__elemhideRules = [] |
73 |
73 |
74 self.__checksumRe = re.compile(r"""^\s*!\s*checksum[\s\-:]+([\w\+\/=]+).*\n""", |
74 self.__checksumRe = re.compile( |
|
75 r"""^\s*!\s*checksum[\s\-:]+([\w\+\/=]+).*\n""", |
75 re.IGNORECASE | re.MULTILINE) |
76 re.IGNORECASE | re.MULTILINE) |
76 self.__expiresRe = re.compile( |
77 self.__expiresRe = re.compile( |
77 r"""(?:expires:|expires after)\s*(\d+)\s*(hour|h)?""", |
78 r"""(?:expires:|expires after)\s*(\d+)\s*(hour|h)?""", |
78 re.IGNORECASE) |
79 re.IGNORECASE) |
79 self.__remoteModifiedRe = re.compile( |
80 self.__remoteModifiedRe = re.compile( |
111 if url.path() != "subscribe": |
112 if url.path() != "subscribe": |
112 return |
113 return |
113 |
114 |
114 self.__title = \ |
115 self.__title = \ |
115 QUrl.fromPercentEncoding(url.encodedQueryItemValue("title")) |
116 QUrl.fromPercentEncoding(url.encodedQueryItemValue("title")) |
116 self.__enabled = \ |
117 self.__enabled = QUrl.fromPercentEncoding( |
117 QUrl.fromPercentEncoding(url.encodedQueryItemValue("enabled")) != "false" |
118 url.encodedQueryItemValue("enabled")) != "false" |
118 self.__location = \ |
119 self.__location = QByteArray(QUrl.fromPercentEncoding( |
119 QByteArray(QUrl.fromPercentEncoding(url.encodedQueryItemValue("location"))) |
120 url.encodedQueryItemValue("location"))) |
120 |
121 |
121 # Check for required subscription |
122 # Check for required subscription |
122 self.__requiresLocation = \ |
123 self.__requiresLocation = QUrl.fromPercentEncoding( |
123 QUrl.fromPercentEncoding(url.encodedQueryItemValue("requiresLocation")) |
124 url.encodedQueryItemValue("requiresLocation")) |
124 self.__requiresTitle = \ |
125 self.__requiresTitle = QUrl.fromPercentEncoding( |
125 QUrl.fromPercentEncoding(url.encodedQueryItemValue("requiresTitle")) |
126 url.encodedQueryItemValue("requiresTitle")) |
126 if self.__requiresLocation and self.__requiresTitle: |
127 if self.__requiresLocation and self.__requiresTitle: |
127 import Helpviewer.HelpWindow |
128 import Helpviewer.HelpWindow |
128 Helpviewer.HelpWindow.HelpWindow.adBlockManager().loadRequiredSubscription( |
129 Helpviewer.HelpWindow.HelpWindow.adBlockManager()\ |
129 self.__requiresLocation, self.__requiresTitle) |
130 .loadRequiredSubscription(self.__requiresLocation, |
|
131 self.__requiresTitle) |
130 |
132 |
131 lastUpdateByteArray = url.encodedQueryItemValue("lastUpdate") |
133 lastUpdateByteArray = url.encodedQueryItemValue("lastUpdate") |
132 lastUpdateString = QUrl.fromPercentEncoding(lastUpdateByteArray) |
134 lastUpdateString = QUrl.fromPercentEncoding(lastUpdateByteArray) |
133 self.__lastUpdate = QDateTime.fromString(lastUpdateString, Qt.ISODate) |
135 self.__lastUpdate = QDateTime.fromString(lastUpdateString, Qt.ISODate) |
134 |
136 |
245 return self.location().toLocalFile() |
247 return self.location().toLocalFile() |
246 |
248 |
247 if self.__location.isEmpty(): |
249 if self.__location.isEmpty(): |
248 return "" |
250 return "" |
249 |
251 |
250 sha1 = bytes( |
252 sha1 = bytes(QCryptographicHash.hash( |
251 QCryptographicHash.hash(self.__location, QCryptographicHash.Sha1).toHex())\ |
253 self.__location, QCryptographicHash.Sha1).toHex()).decode() |
252 .decode() |
254 dataDir = os.path.join( |
253 dataDir = os.path.join(Utilities.getConfigDir(), "browser", "subscriptions") |
255 Utilities.getConfigDir(), "browser", "subscriptions") |
254 if not os.path.exists(dataDir): |
256 if not os.path.exists(dataDir): |
255 os.makedirs(dataDir) |
257 os.makedirs(dataDir) |
256 fileName = os.path.join(dataDir, "adblock_subscription_{0}".format(sha1)) |
258 fileName = os.path.join( |
|
259 dataDir, "adblock_subscription_{0}".format(sha1)) |
257 return fileName |
260 return fileName |
258 |
261 |
259 def __loadRules(self): |
262 def __loadRules(self): |
260 """ |
263 """ |
261 Private method to load the rules of the subscription. |
264 Private method to load the rules of the subscription. |
264 f = QFile(fileName) |
267 f = QFile(fileName) |
265 if f.exists(): |
268 if f.exists(): |
266 if not f.open(QIODevice.ReadOnly): |
269 if not f.open(QIODevice.ReadOnly): |
267 E5MessageBox.warning(None, |
270 E5MessageBox.warning(None, |
268 self.trUtf8("Load subscription rules"), |
271 self.trUtf8("Load subscription rules"), |
269 self.trUtf8("""Unable to open adblock file '{0}' for reading.""")\ |
272 self.trUtf8( |
|
273 """Unable to open adblock file '{0}' for reading.""")\ |
270 .format(fileName)) |
274 .format(fileName)) |
271 else: |
275 else: |
272 textStream = QTextStream(f) |
276 textStream = QTextStream(f) |
273 header = textStream.readLine(1024) |
277 header = textStream.readLine(1024) |
274 if not header.startswith("[Adblock"): |
278 if not header.startswith("[Adblock"): |
299 else: |
303 else: |
300 # days |
304 # days |
301 self.__updatePeriod = int(period) * 24 |
305 self.__updatePeriod = int(period) * 24 |
302 remoteModified = self.__remoteModifiedRe.search(line) |
306 remoteModified = self.__remoteModifiedRe.search(line) |
303 if remoteModified: |
307 if remoteModified: |
304 day, month, year, time, hour, minute = remoteModified.groups() |
308 day, month, year, time, hour, minute = \ |
|
309 remoteModified.groups() |
305 self.__remoteModified.setDate( |
310 self.__remoteModified.setDate( |
306 QDate(int(year), self.__monthNameToNumber[month], |
311 QDate(int(year), |
307 int(day))) |
312 self.__monthNameToNumber[month], |
|
313 int(day)) |
|
314 ) |
308 if time: |
315 if time: |
309 self.__remoteModified.setTime( |
316 self.__remoteModified.setTime( |
310 QTime(int(hour), int(minute))) |
317 QTime(int(hour), int(minute))) |
311 self.__populateCache() |
318 self.__populateCache() |
312 self.changed.emit() |
319 self.changed.emit() |
365 if reply.error() != QNetworkReply.NoError: |
372 if reply.error() != QNetworkReply.NoError: |
366 if not self.__defaultSubscription: |
373 if not self.__defaultSubscription: |
367 # don't show error if we try to load the default |
374 # don't show error if we try to load the default |
368 E5MessageBox.warning(None, |
375 E5MessageBox.warning(None, |
369 self.trUtf8("Downloading subscription rules"), |
376 self.trUtf8("Downloading subscription rules"), |
370 self.trUtf8("""<p>Subscription rules could not be downloaded.</p>""" |
377 self.trUtf8( |
371 """<p>Error: {0}</p>""").format(reply.errorString())) |
378 """<p>Subscription rules could not be""" |
|
379 """ downloaded.</p><p>Error: {0}</p>""") |
|
380 .format(reply.errorString())) |
372 else: |
381 else: |
373 # reset after first download attempt |
382 # reset after first download attempt |
374 self.__defaultSubscription = False |
383 self.__defaultSubscription = False |
375 return |
384 return |
376 |
385 |
384 QFile.remove(fileName) |
393 QFile.remove(fileName) |
385 f = QFile(fileName) |
394 f = QFile(fileName) |
386 if not f.open(QIODevice.ReadWrite): |
395 if not f.open(QIODevice.ReadWrite): |
387 E5MessageBox.warning(None, |
396 E5MessageBox.warning(None, |
388 self.trUtf8("Downloading subscription rules"), |
397 self.trUtf8("Downloading subscription rules"), |
389 self.trUtf8("""Unable to open adblock file '{0}' for writing.""")\ |
398 self.trUtf8( |
|
399 """Unable to open adblock file '{0}' for writing.""")\ |
390 .file(fileName)) |
400 .file(fileName)) |
391 return |
401 return |
392 f.write(response) |
402 f.write(response) |
393 f.close() |
403 f.close() |
394 self.__lastUpdate = QDateTime.currentDateTime() |
404 self.__lastUpdate = QDateTime.currentDateTime() |
427 data = re.sub(self.__checksumRe, "", data) # remove checksum line |
437 data = re.sub(self.__checksumRe, "", data) # remove checksum line |
428 |
438 |
429 # calculate checksum |
439 # calculate checksum |
430 md5 = hashlib.md5() |
440 md5 = hashlib.md5() |
431 md5.update(data.encode("utf-8")) |
441 md5.update(data.encode("utf-8")) |
432 calculatedChecksum = base64.b64encode(md5.digest()).decode().rstrip("=") |
442 calculatedChecksum = base64.b64encode(md5.digest()).decode()\ |
|
443 .rstrip("=") |
433 if calculatedChecksum == expectedChecksum: |
444 if calculatedChecksum == expectedChecksum: |
434 return True |
445 return True |
435 else: |
446 else: |
436 res = E5MessageBox.yesNo(None, |
447 res = E5MessageBox.yesNo(None, |
437 self.trUtf8("Downloading subscription rules"), |
448 self.trUtf8("Downloading subscription rules"), |
438 self.trUtf8("""<p>AdBlock subscription <b>{0}</b> has a wrong""" |
449 self.trUtf8( |
439 """ checksum.<br/>""" |
450 """<p>AdBlock subscription <b>{0}</b> has a wrong""" |
440 """Found: {1}<br/>""" |
451 """ checksum.<br/>""" |
441 """Calculated: {2}<br/>""" |
452 """Found: {1}<br/>""" |
442 """Use it anyway?</p>""")\ |
453 """Calculated: {2}<br/>""" |
443 .format(self.__title, expectedChecksum, calculatedChecksum)) |
454 """Use it anyway?</p>""")\ |
|
455 .format(self.__title, expectedChecksum, |
|
456 calculatedChecksum)) |
444 return res |
457 return res |
445 |
458 |
446 def saveRules(self): |
459 def saveRules(self): |
447 """ |
460 """ |
448 Public method to save the subscription rules. |
461 Public method to save the subscription rules. |
453 |
466 |
454 f = QFile(fileName) |
467 f = QFile(fileName) |
455 if not f.open(QIODevice.ReadWrite | QIODevice.Truncate): |
468 if not f.open(QIODevice.ReadWrite | QIODevice.Truncate): |
456 E5MessageBox.warning(None, |
469 E5MessageBox.warning(None, |
457 self.trUtf8("Saving subscription rules"), |
470 self.trUtf8("Saving subscription rules"), |
458 self.trUtf8("""Unable to open adblock file '{0}' for writing.""")\ |
471 self.trUtf8( |
|
472 """Unable to open adblock file '{0}' for writing.""")\ |
459 .format(fileName)) |
473 .format(fileName)) |
460 return |
474 return |
461 |
475 |
462 textStream = QTextStream(f) |
476 textStream = QTextStream(f) |
463 if not self.__rules or not self.__rules[0].isHeader(): |
477 if not self.__rules or not self.__rules[0].isHeader(): |
497 |
511 |
498 return False |
512 return False |
499 |
513 |
500 def elemHideDisabledForUrl(self, url): |
514 def elemHideDisabledForUrl(self, url): |
501 """ |
515 """ |
502 Public method to check, if element hiding is disabled for the given URL. |
516 Public method to check, if element hiding is disabled for the given |
|
517 URL. |
503 |
518 |
504 @param url URL to check (QUrl) |
519 @param url URL to check (QUrl) |
505 @return flag indicating disabled state (boolean) |
520 @return flag indicating disabled state (boolean) |
506 """ |
521 """ |
507 if self.adBlockDisabledForUrl(url): |
522 if self.adBlockDisabledForUrl(url): |
658 rule = self.__rules[offset] |
673 rule = self.__rules[offset] |
659 rule.setEnabled(enabled) |
674 rule.setEnabled(enabled) |
660 if rule.isCSSRule(): |
675 if rule.isCSSRule(): |
661 import Helpviewer.HelpWindow |
676 import Helpviewer.HelpWindow |
662 self.__populateCache() |
677 self.__populateCache() |
663 Helpviewer.HelpWindow.HelpWindow.mainWindow().reloadUserStyleSheet() |
678 Helpviewer.HelpWindow.HelpWindow.mainWindow()\ |
|
679 .reloadUserStyleSheet() |
664 |
680 |
665 return rule |
681 return rule |