Helpviewer/AdBlock/AdBlockSubscription.py

changeset 0
de9c2efb9d02
child 7
c679fb30c8f3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Helpviewer/AdBlock/AdBlockSubscription.py	Mon Dec 28 16:03:33 2009 +0000
@@ -0,0 +1,405 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the AdBlock subscription class.
+"""
+
+import os
+
+from PyQt4.QtCore import *
+from PyQt4.QtNetwork import QNetworkRequest, QNetworkReply
+from PyQt4.QtGui import QMessageBox
+
+from AdBlockRule import AdBlockRule
+
+import Helpviewer.HelpWindow
+
+import Utilities
+
+class AdBlockSubscription(QObject):
+    """
+    Class implementing the AdBlock subscription.
+    
+    @signal changed() emitted after the subscription has changed
+    @signal rulesChanged() emitted after the subscription's rules have changed
+    """
+    def __init__(self, url, parent = None):
+        """
+        Constructor
+        
+        @param url AdBlock URL for the subscription (QUrl)
+        @param parent reference to the parent object (QObject)
+        """
+        QObject.__init__(self, parent)
+        
+        self.__url = url.toEncoded()
+        self.__enabled = False
+        self.__downloading = None
+        
+        self.__title = ""
+        self.__location = QByteArray()
+        self.__lastUpdate = QDateTime()
+        
+        self.__rules = []   # list containing all AdBlock rules
+        
+        self.__networkExceptionRules = []
+        self.__networkBlockRules = []
+        self.__pageRules = []
+        
+        self.__parseUrl(url)
+    
+    def __parseUrl(self, url):
+        """
+        Private method to parse the AdBlock URL for the subscription.
+        
+        @param url AdBlock URL for the subscription (QUrl)
+        """
+        if url.scheme() != "abp":
+            return
+        
+        if url.path() != "subscribe":
+            return
+        
+        self.__title = \
+            QUrl.fromPercentEncoding(url.encodedQueryItemValue("title"))
+        self.__enabled = \
+            QUrl.fromPercentEncoding(url.encodedQueryItemValue("enabled")) != "false"
+        self.__location = \
+            QByteArray(QUrl.fromPercentEncoding(url.encodedQueryItemValue("location")))
+        
+        lastUpdateByteArray = url.encodedQueryItemValue("lastUpdate")
+        lastUpdateString = QUrl.fromPercentEncoding(lastUpdateByteArray)
+        self.__lastUpdate = QDateTime.fromString(lastUpdateString, Qt.ISODate)
+        
+        self.__loadRules()
+    
+    def url(self):
+        """
+        Public method to generate the url for this subscription.
+        
+        @return AdBlock URL for the subscription (QUrl)
+        """
+        url = QUrl()
+        url.setScheme("abp")
+        url.setPath("subscribe")
+        
+        queryItems = []
+        queryItems.append(("location", unicode(self.__location)))
+        queryItems.append(("title", self.__title))
+        if self.__enabled:
+            queryItems.append(("enabled", "false"))
+        if self.__lastUpdate.isValid():
+            queryItems.append(("lastUpdate", 
+                               self.__lastUpdate.toString(Qt.ISODate)))
+        url.setQueryItems(queryItems)
+        return url
+    
+    def isEnabled(self):
+        """
+        Public method to check, if the subscription is enabled.
+        
+        @return flag indicating the enabled status (boolean)
+        """
+        return self.__enabled
+    
+    def setEnabled(self, enabled):
+        """
+        Public method to set the enabled status.
+        
+        @param enabled flag indicating the enabled status (boolean)
+        """
+        if self.__enabled == enabled:
+            return
+        
+        self.__enabled = enabled
+        self.__populateCache()
+        self.emit(SIGNAL("changed()"))
+    
+    def title(self):
+        """
+        Public method to get the subscription title.
+        
+        @return subscription title (string)
+        """
+        return self.__title
+    
+    def setTitle(self, title):
+        """
+        Public method to set the subscription title.
+        
+        @param title subscription title (string)
+        """
+        if self.__title == title:
+            return
+        
+        self.__title = title
+        self.emit(SIGNAL("changed()"))
+    
+    def location(self):
+        """
+        Public method to get the subscription location.
+        
+        @return URL of the subscription location (QUrl)
+        """
+        return QUrl.fromEncoded(self.__location)
+    
+    def setLocation(self, url):
+        """
+        Public method to set the subscription location.
+        
+        @param url URL of the subscription location (QUrl)
+        """
+        if url == self.location():
+            return
+        
+        self.__location = url.toEncoded()
+        self.__lastUpdate = QDateTime()
+        self.emit(SIGNAL("changed()"))
+    
+    def lastUpdate(self):
+        """
+        Public method to get the date and time of the last update.
+        
+        @return date and time of the last update (QDateTime)
+        """
+        return self.__lastUpdate
+    
+    def rulesFileName(self):
+        """
+        Public method to get the name of the rules file.
+        
+        @return name of the rules file (string)
+        """
+        if self.location().scheme() == "file":
+            return self.location().toLocalFile()
+        
+        if self.__location.isEmpty():
+            return ""
+        
+        sha1 = QCryptographicHash.hash(self.__location, QCryptographicHash.Sha1).toHex()
+        dataDir = os.path.join(Utilities.getConfigDir(), "browser", "subscriptions")
+        if not os.path.exists(dataDir):
+            os.makedirs(dataDir)
+        fileName = os.path.join(dataDir, "adblock_subscription_%s" % sha1)
+        return fileName
+    
+    def __loadRules(self):
+        """
+        Private method to load the rules of the subscription.
+        """
+        fileName = self.rulesFileName()
+        f = QFile(fileName)
+        if f.exists():
+            if not f.open(QIODevice.ReadOnly):
+                QMessageBox.warning(None,
+                    self.trUtf8("Load subscription rules"),
+                    self.trUtf8("""Unable to open adblock file '{0}' for reading.""")\
+                        .format(fileName))
+            else:
+                textStream = QTextStream(f)
+                header = textStream.readLine(1024)
+                if not header.startswith("[Adblock"):
+                    QMessageBox.warning(None,
+                        self.trUtf8("Load subscription rules"),
+                        self.trUtf8("""Adblock file '{0}' does not start"""
+                                    """ with [Adblock.""")\
+                            .format(fileName))
+                    f.close()
+                    f.remove()
+                    self.__lastUpdate = QDateTime()
+                else:
+                    self.__rules = []
+                    while not textStream.atEnd():
+                        line = textStream.readLine()
+                        self.__rules.append(AdBlockRule(line))
+                    self.__populateCache()
+                    self.emit(SIGNAL("changed()"))
+        
+        if not self.__lastUpdate.isValid() or \
+           self.__lastUpdate.addDays(7) < QDateTime.currentDateTime():
+            self.updateNow()
+    
+    def updateNow(self):
+        """
+        Public method to update the subscription immediately.
+        """
+        if self.__downloading is not None:
+            return
+        
+        if not self.location().isValid():
+            return
+        
+        if self.location().scheme() == "file":
+            self.__lastUpdate = QDateTime.currentDateTime()
+            self.__loadRules()
+            self.emit(SIGNAL("changed()"))
+            return
+        
+        request = QNetworkRequest(self.location())
+        self.__downloading = \
+            Helpviewer.HelpWindow.HelpWindow.networkAccessManager().get(request)
+        self.connect(self.__downloading, SIGNAL("finished()"), self.__rulesDownloaded)
+    
+    def __rulesDownloaded(self):
+        """
+        Private slot to deal with the downloaded rules.
+        """
+        reply = self.sender()
+        
+        response = reply.readAll()
+        redirect = reply.attribute(QNetworkRequest.RedirectionTargetAttribute).toUrl()
+        reply.close()
+        reply.deleteLater()
+        
+        if reply.error() != QNetworkReply.NoError:
+            QMessageBox.warning(None,
+                self.trUtf8("Downloading subscription rules"),
+                self.trUtf8("""<p>Subscription rules could not be downloaded.</p>"""
+                            """<p>Error: {0}</p>""").format(reply.errorString()))
+            return
+        
+        if redirect.isValid():
+            request = QNetworkRequest(redirect)
+            self.__downloading = \
+                Helpviewer.HelpWindow.HelpWindow.networkAccessManager().get(request)
+            self.connect(self.__downloading, SIGNAL("finished()"), self.__rulesDownloaded)
+            return
+        
+        if response.isEmpty():
+            QMessageBox.warning(None,
+                self.trUtf8("Downloading subscription rules"),
+                self.trUtf8("""Got empty subscription rules."""))
+            return
+        
+        fileName = self.rulesFileName()
+        f = QFile(fileName)
+        if not f.open(QIODevice.ReadWrite):
+            QMessageBox.warning(None,
+                self.trUtf8("Downloading subscription rules"),
+                self.trUtf8("""Unable to open adblock file '{0}' for writing.""")\
+                    .file(fileName))
+            return
+        f.write(response)
+        self.__lastUpdate = QDateTime.currentDateTime()
+        self.__loadRules()
+        self.emit(SIGNAL("changed()"))
+        self.__downloading = None
+    
+    def saveRules(self):
+        """
+        Public method to save the subscription rules.
+        """
+        fileName = self.rulesFileName()
+        if not fileName:
+            return
+        
+        f = QFile(fileName)
+        if not f.open(QIODevice.ReadWrite | QIODevice.Truncate):
+            QMessageBox.warning(None,
+                self.trUtf8("Saving subscription rules"),
+                self.trUtf8("""Unable to open adblock file '{0}' for writing.""")\
+                    .format(fileName))
+            return
+        
+        textStream = QTextStream(f)
+        textStream << "[Adblock Plus 0.7.1]\n"
+        for rule in self.__rules:
+            textStream << rule.filter() << "\n"
+    
+    def pageRules(self):
+        """
+        Public method to get the page rules of the subscription.
+        
+        @return list of rule objects (list of AdBlockRule)
+        """
+        return self.__pageRules[:]
+    
+    def allow(self, urlString):
+        """
+        Public method to check, if the given URL is allowed.
+        
+        @return reference to the rule object or None (AdBlockRule)
+        """
+        for rule in self.__networkExceptionRules:
+            if rule.networkMatch(urlString):
+                return rule
+        
+        return None
+    
+    def block(self, urlString):
+        """
+        Public method to check, if the given URL should be blocked.
+        
+        @return reference to the rule object or None (AdBlockRule)
+        """
+        for rule in self.__networkBlockRules:
+            if rule.networkMatch(urlString):
+                return rule
+        
+        return None
+    
+    def allRules(self):
+        """
+        Public method to get the list of rules.
+        
+        @return list of rules (list of AdBlockRule)
+        """
+        return self.__rules[:]
+    
+    def addRule(self, rule):
+        """
+        Public method to add a rule.
+        
+        @param rule reference to the rule to add (AdBlockRule)
+        """
+        self.__rules.append(rule)
+        self.__populateCache()
+        self.emit(SIGNAL("rulesChanged()"))
+    
+    def removeRule(self, offset):
+        """
+        Public method to remove a rule given the offset.
+        
+        @param offset offset of the rule to remove (integer)
+        """
+        if offset < 0 or offset > len(self.__rules):
+            return
+        
+        del self.__rules[offset]
+        self.__populateCache()
+        self.emit(SIGNAL("rulesChanged()"))
+    
+    def replaceRule(self, rule, offset):
+        """
+        Public method to replace a rule given the offset.
+        
+        @param rule reference to the rule to set (AdBlockRule)
+        @param offset offset of the rule to remove (integer)
+        """
+        self.__rules[offset] = rule
+        self.__populateCache()
+        self.emit(SIGNAL("rulesChanged()"))
+    
+    def __populateCache(self):
+        """
+        Private method to populate the various rule caches.
+        """
+        self.__networkBlockRules = []
+        self.__networkExceptionRules = []
+        self.__pageRules = []
+        if not self.isEnabled():
+            return
+        
+        for rule in self.__rules:
+            if not rule.isEnabled():
+                continue
+            
+            if rule.isCSSRule():
+                self.__pageRules.append(rule)
+            elif rule.isException():
+                self.__networkExceptionRules.append(rule)
+            else:
+                self.__networkBlockRules.append(rule)

eric ide

mercurial