Helpviewer/AdBlock/AdBlockSubscription.py

changeset 0
de9c2efb9d02
child 7
c679fb30c8f3
equal deleted inserted replaced
-1:000000000000 0:de9c2efb9d02
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2009 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the AdBlock subscription class.
8 """
9
10 import os
11
12 from PyQt4.QtCore import *
13 from PyQt4.QtNetwork import QNetworkRequest, QNetworkReply
14 from PyQt4.QtGui import QMessageBox
15
16 from AdBlockRule import AdBlockRule
17
18 import Helpviewer.HelpWindow
19
20 import Utilities
21
22 class AdBlockSubscription(QObject):
23 """
24 Class implementing the AdBlock subscription.
25
26 @signal changed() emitted after the subscription has changed
27 @signal rulesChanged() emitted after the subscription's rules have changed
28 """
29 def __init__(self, url, parent = None):
30 """
31 Constructor
32
33 @param url AdBlock URL for the subscription (QUrl)
34 @param parent reference to the parent object (QObject)
35 """
36 QObject.__init__(self, parent)
37
38 self.__url = url.toEncoded()
39 self.__enabled = False
40 self.__downloading = None
41
42 self.__title = ""
43 self.__location = QByteArray()
44 self.__lastUpdate = QDateTime()
45
46 self.__rules = [] # list containing all AdBlock rules
47
48 self.__networkExceptionRules = []
49 self.__networkBlockRules = []
50 self.__pageRules = []
51
52 self.__parseUrl(url)
53
54 def __parseUrl(self, url):
55 """
56 Private method to parse the AdBlock URL for the subscription.
57
58 @param url AdBlock URL for the subscription (QUrl)
59 """
60 if url.scheme() != "abp":
61 return
62
63 if url.path() != "subscribe":
64 return
65
66 self.__title = \
67 QUrl.fromPercentEncoding(url.encodedQueryItemValue("title"))
68 self.__enabled = \
69 QUrl.fromPercentEncoding(url.encodedQueryItemValue("enabled")) != "false"
70 self.__location = \
71 QByteArray(QUrl.fromPercentEncoding(url.encodedQueryItemValue("location")))
72
73 lastUpdateByteArray = url.encodedQueryItemValue("lastUpdate")
74 lastUpdateString = QUrl.fromPercentEncoding(lastUpdateByteArray)
75 self.__lastUpdate = QDateTime.fromString(lastUpdateString, Qt.ISODate)
76
77 self.__loadRules()
78
79 def url(self):
80 """
81 Public method to generate the url for this subscription.
82
83 @return AdBlock URL for the subscription (QUrl)
84 """
85 url = QUrl()
86 url.setScheme("abp")
87 url.setPath("subscribe")
88
89 queryItems = []
90 queryItems.append(("location", unicode(self.__location)))
91 queryItems.append(("title", self.__title))
92 if self.__enabled:
93 queryItems.append(("enabled", "false"))
94 if self.__lastUpdate.isValid():
95 queryItems.append(("lastUpdate",
96 self.__lastUpdate.toString(Qt.ISODate)))
97 url.setQueryItems(queryItems)
98 return url
99
100 def isEnabled(self):
101 """
102 Public method to check, if the subscription is enabled.
103
104 @return flag indicating the enabled status (boolean)
105 """
106 return self.__enabled
107
108 def setEnabled(self, enabled):
109 """
110 Public method to set the enabled status.
111
112 @param enabled flag indicating the enabled status (boolean)
113 """
114 if self.__enabled == enabled:
115 return
116
117 self.__enabled = enabled
118 self.__populateCache()
119 self.emit(SIGNAL("changed()"))
120
121 def title(self):
122 """
123 Public method to get the subscription title.
124
125 @return subscription title (string)
126 """
127 return self.__title
128
129 def setTitle(self, title):
130 """
131 Public method to set the subscription title.
132
133 @param title subscription title (string)
134 """
135 if self.__title == title:
136 return
137
138 self.__title = title
139 self.emit(SIGNAL("changed()"))
140
141 def location(self):
142 """
143 Public method to get the subscription location.
144
145 @return URL of the subscription location (QUrl)
146 """
147 return QUrl.fromEncoded(self.__location)
148
149 def setLocation(self, url):
150 """
151 Public method to set the subscription location.
152
153 @param url URL of the subscription location (QUrl)
154 """
155 if url == self.location():
156 return
157
158 self.__location = url.toEncoded()
159 self.__lastUpdate = QDateTime()
160 self.emit(SIGNAL("changed()"))
161
162 def lastUpdate(self):
163 """
164 Public method to get the date and time of the last update.
165
166 @return date and time of the last update (QDateTime)
167 """
168 return self.__lastUpdate
169
170 def rulesFileName(self):
171 """
172 Public method to get the name of the rules file.
173
174 @return name of the rules file (string)
175 """
176 if self.location().scheme() == "file":
177 return self.location().toLocalFile()
178
179 if self.__location.isEmpty():
180 return ""
181
182 sha1 = QCryptographicHash.hash(self.__location, QCryptographicHash.Sha1).toHex()
183 dataDir = os.path.join(Utilities.getConfigDir(), "browser", "subscriptions")
184 if not os.path.exists(dataDir):
185 os.makedirs(dataDir)
186 fileName = os.path.join(dataDir, "adblock_subscription_%s" % sha1)
187 return fileName
188
189 def __loadRules(self):
190 """
191 Private method to load the rules of the subscription.
192 """
193 fileName = self.rulesFileName()
194 f = QFile(fileName)
195 if f.exists():
196 if not f.open(QIODevice.ReadOnly):
197 QMessageBox.warning(None,
198 self.trUtf8("Load subscription rules"),
199 self.trUtf8("""Unable to open adblock file '{0}' for reading.""")\
200 .format(fileName))
201 else:
202 textStream = QTextStream(f)
203 header = textStream.readLine(1024)
204 if not header.startswith("[Adblock"):
205 QMessageBox.warning(None,
206 self.trUtf8("Load subscription rules"),
207 self.trUtf8("""Adblock file '{0}' does not start"""
208 """ with [Adblock.""")\
209 .format(fileName))
210 f.close()
211 f.remove()
212 self.__lastUpdate = QDateTime()
213 else:
214 self.__rules = []
215 while not textStream.atEnd():
216 line = textStream.readLine()
217 self.__rules.append(AdBlockRule(line))
218 self.__populateCache()
219 self.emit(SIGNAL("changed()"))
220
221 if not self.__lastUpdate.isValid() or \
222 self.__lastUpdate.addDays(7) < QDateTime.currentDateTime():
223 self.updateNow()
224
225 def updateNow(self):
226 """
227 Public method to update the subscription immediately.
228 """
229 if self.__downloading is not None:
230 return
231
232 if not self.location().isValid():
233 return
234
235 if self.location().scheme() == "file":
236 self.__lastUpdate = QDateTime.currentDateTime()
237 self.__loadRules()
238 self.emit(SIGNAL("changed()"))
239 return
240
241 request = QNetworkRequest(self.location())
242 self.__downloading = \
243 Helpviewer.HelpWindow.HelpWindow.networkAccessManager().get(request)
244 self.connect(self.__downloading, SIGNAL("finished()"), self.__rulesDownloaded)
245
246 def __rulesDownloaded(self):
247 """
248 Private slot to deal with the downloaded rules.
249 """
250 reply = self.sender()
251
252 response = reply.readAll()
253 redirect = reply.attribute(QNetworkRequest.RedirectionTargetAttribute).toUrl()
254 reply.close()
255 reply.deleteLater()
256
257 if reply.error() != QNetworkReply.NoError:
258 QMessageBox.warning(None,
259 self.trUtf8("Downloading subscription rules"),
260 self.trUtf8("""<p>Subscription rules could not be downloaded.</p>"""
261 """<p>Error: {0}</p>""").format(reply.errorString()))
262 return
263
264 if redirect.isValid():
265 request = QNetworkRequest(redirect)
266 self.__downloading = \
267 Helpviewer.HelpWindow.HelpWindow.networkAccessManager().get(request)
268 self.connect(self.__downloading, SIGNAL("finished()"), self.__rulesDownloaded)
269 return
270
271 if response.isEmpty():
272 QMessageBox.warning(None,
273 self.trUtf8("Downloading subscription rules"),
274 self.trUtf8("""Got empty subscription rules."""))
275 return
276
277 fileName = self.rulesFileName()
278 f = QFile(fileName)
279 if not f.open(QIODevice.ReadWrite):
280 QMessageBox.warning(None,
281 self.trUtf8("Downloading subscription rules"),
282 self.trUtf8("""Unable to open adblock file '{0}' for writing.""")\
283 .file(fileName))
284 return
285 f.write(response)
286 self.__lastUpdate = QDateTime.currentDateTime()
287 self.__loadRules()
288 self.emit(SIGNAL("changed()"))
289 self.__downloading = None
290
291 def saveRules(self):
292 """
293 Public method to save the subscription rules.
294 """
295 fileName = self.rulesFileName()
296 if not fileName:
297 return
298
299 f = QFile(fileName)
300 if not f.open(QIODevice.ReadWrite | QIODevice.Truncate):
301 QMessageBox.warning(None,
302 self.trUtf8("Saving subscription rules"),
303 self.trUtf8("""Unable to open adblock file '{0}' for writing.""")\
304 .format(fileName))
305 return
306
307 textStream = QTextStream(f)
308 textStream << "[Adblock Plus 0.7.1]\n"
309 for rule in self.__rules:
310 textStream << rule.filter() << "\n"
311
312 def pageRules(self):
313 """
314 Public method to get the page rules of the subscription.
315
316 @return list of rule objects (list of AdBlockRule)
317 """
318 return self.__pageRules[:]
319
320 def allow(self, urlString):
321 """
322 Public method to check, if the given URL is allowed.
323
324 @return reference to the rule object or None (AdBlockRule)
325 """
326 for rule in self.__networkExceptionRules:
327 if rule.networkMatch(urlString):
328 return rule
329
330 return None
331
332 def block(self, urlString):
333 """
334 Public method to check, if the given URL should be blocked.
335
336 @return reference to the rule object or None (AdBlockRule)
337 """
338 for rule in self.__networkBlockRules:
339 if rule.networkMatch(urlString):
340 return rule
341
342 return None
343
344 def allRules(self):
345 """
346 Public method to get the list of rules.
347
348 @return list of rules (list of AdBlockRule)
349 """
350 return self.__rules[:]
351
352 def addRule(self, rule):
353 """
354 Public method to add a rule.
355
356 @param rule reference to the rule to add (AdBlockRule)
357 """
358 self.__rules.append(rule)
359 self.__populateCache()
360 self.emit(SIGNAL("rulesChanged()"))
361
362 def removeRule(self, offset):
363 """
364 Public method to remove a rule given the offset.
365
366 @param offset offset of the rule to remove (integer)
367 """
368 if offset < 0 or offset > len(self.__rules):
369 return
370
371 del self.__rules[offset]
372 self.__populateCache()
373 self.emit(SIGNAL("rulesChanged()"))
374
375 def replaceRule(self, rule, offset):
376 """
377 Public method to replace a rule given the offset.
378
379 @param rule reference to the rule to set (AdBlockRule)
380 @param offset offset of the rule to remove (integer)
381 """
382 self.__rules[offset] = rule
383 self.__populateCache()
384 self.emit(SIGNAL("rulesChanged()"))
385
386 def __populateCache(self):
387 """
388 Private method to populate the various rule caches.
389 """
390 self.__networkBlockRules = []
391 self.__networkExceptionRules = []
392 self.__pageRules = []
393 if not self.isEnabled():
394 return
395
396 for rule in self.__rules:
397 if not rule.isEnabled():
398 continue
399
400 if rule.isCSSRule():
401 self.__pageRules.append(rule)
402 elif rule.isException():
403 self.__networkExceptionRules.append(rule)
404 else:
405 self.__networkBlockRules.append(rule)

eric ide

mercurial