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