diff options
author | Pedro Algarvio <pedro@algarvio.me> | 2012-02-15 14:21:02 +0000 |
---|---|---|
committer | Pedro Algarvio <pedro@algarvio.me> | 2012-02-15 14:21:02 +0000 |
commit | c8718ad643d00c7d166dd1314f385572dc4788a9 (patch) | |
tree | 4bba0655e4e732ef8c783625a22e40f2e911551f | |
parent | da868347cf8da28882292c963bd798e72cd41377 (diff) | |
download | deluge-c8718ad643d00c7d166dd1314f385572dc4788a9.tar.gz deluge-c8718ad643d00c7d166dd1314f385572dc4788a9.tar.bz2 deluge-c8718ad643d00c7d166dd1314f385572dc4788a9.zip |
Implemented #1382:
* Implemented whitelist support to both core and GTK UI.
* Implemented ip filter cleaning before each update. Restarting the deluge
daemon is no longer needed.
* If "check_after_days" is 0(zero), the timer is not started anymore. It would
keep updating one call after the other. If the value changed, the timer is
now stopped and restarted using the new value.
-rw-r--r-- | ChangeLog | 8 | ||||
-rw-r--r-- | deluge/plugins/Blocklist/deluge/__init__.py | 3 | ||||
-rw-r--r-- | deluge/plugins/Blocklist/deluge/plugins/__init__.py | 3 | ||||
-rw-r--r-- | deluge/plugins/Blocklist/deluge/plugins/blocklist/common.py | 100 | ||||
-rw-r--r-- | deluge/plugins/Blocklist/deluge/plugins/blocklist/core.py | 112 | ||||
-rw-r--r-- | deluge/plugins/Blocklist/deluge/plugins/blocklist/data/blocklist_pref.glade | 178 | ||||
-rw-r--r-- | deluge/plugins/Blocklist/deluge/plugins/blocklist/gtkui.py | 92 | ||||
-rw-r--r-- | deluge/plugins/Blocklist/deluge/plugins/blocklist/readers.py | 13 | ||||
-rw-r--r-- | deluge/plugins/Blocklist/setup.py | 1 |
9 files changed, 472 insertions, 38 deletions
@@ -33,6 +33,14 @@ * #378: Allow showing a pieces bar instead of a regular progress bar in a torrent's status tab. +=== Blocklist Plugin === + * #1382: Implemented whitelist support to both core and GTK UI. + * Implemented ip filter cleaning before each update. Restarting the deluge + daemon is no longer needed. + * If "check_after_days" is 0(zero), the timer is not started anymore. It + would keep updating one call after the other. If the value changed, the + timer is now stopped and restarted using the new value. + === Deluge 1.3.4 (In Development) === ==== Core ==== * #1921: Free disk space reporting incorrectly in FreeBSD diff --git a/deluge/plugins/Blocklist/deluge/__init__.py b/deluge/plugins/Blocklist/deluge/__init__.py index 94033e829..0786b820c 100644 --- a/deluge/plugins/Blocklist/deluge/__init__.py +++ b/deluge/plugins/Blocklist/deluge/__init__.py @@ -1,3 +1,2 @@ # this is a namespace package -import pkg_resources -pkg_resources.declare_namespace(__name__) +__import__('pkg_resources').declare_namespace(__name__) diff --git a/deluge/plugins/Blocklist/deluge/plugins/__init__.py b/deluge/plugins/Blocklist/deluge/plugins/__init__.py index 94033e829..0786b820c 100644 --- a/deluge/plugins/Blocklist/deluge/plugins/__init__.py +++ b/deluge/plugins/Blocklist/deluge/plugins/__init__.py @@ -1,3 +1,2 @@ # this is a namespace package -import pkg_resources -pkg_resources.declare_namespace(__name__) +__import__('pkg_resources').declare_namespace(__name__) diff --git a/deluge/plugins/Blocklist/deluge/plugins/blocklist/common.py b/deluge/plugins/Blocklist/deluge/plugins/blocklist/common.py index 147f77d5e..9a74d40da 100644 --- a/deluge/plugins/Blocklist/deluge/plugins/blocklist/common.py +++ b/deluge/plugins/Blocklist/deluge/plugins/blocklist/common.py @@ -85,3 +85,103 @@ def remove_zeros(ip): """ return ".".join([part.lstrip("0").zfill(1) for part in ip.split(".")]) + +class BadIP(Exception): + _message = None + def __init__(self, message): + self.message = message + + def __set_message(self, message): + self._message = message + + def __get_message(self): + return self._message + + message = property(__get_message, __set_message) + del __get_message, __set_message + +class IP(object): + __slots__ = ('q1', 'q2', 'q3', 'q4', '_long') + def __init__(self, q1, q2, q3, q4): + self.q1 = q1 + self.q2 = q2 + self.q3 = q3 + self.q4 = q4 + self._long = 0 + for q in self.quadrants(): + self._long = (self._long << 8) | int(q) + + @property + def address(self): + return '.'.join([str(q) for q in [self.q1, self.q2, self.q3, self.q4]]) + + @property + def long(self): + return self._long + + @classmethod + def parse(cls, ip): + try: + q1, q2, q3, q4 = [int(q) for q in ip.split('.')] + except ValueError: + raise BadIP(_("The IP address \"%s\" is badly formed" % ip)) + if q1<0 or q2<0 or q3<0 or q4<0: + raise BadIP(_("The IP address \"%s\" is badly formed" % ip)) + elif q1>255 or q2>255 or q3>255 or q4>255: + raise BadIP(_("The IP address \"%s\" is badly formed" % ip)) + return cls(q1, q2, q3, q4) + + def quadrants(self): + return (self.q1, self.q2, self.q3, self.q4) + +# def next_ip(self): +# (q1, q2, q3, q4) = self.quadrants() +# if q4 >= 255: +# if q3 >= 255: +# if q2 >= 255: +# if q1 >= 255: +# raise BadIP(_("There ain't a next IP address")) +# q1 += 1 +# else: +# q2 += 1 +# else: +# q3 += 1 +# else: +# q4 += 1 +# return IP(q1, q2, q3, q4) +# +# def previous_ip(self): +# (q1, q2, q3, q4) = self.quadrants() +# if q4 <= 1: +# if q3 <= 1: +# if q2 <= 1: +# if q1 <= 1: +# raise BadIP(_("There ain't a previous IP address")) +# q1 -= 1 +# else: +# q2 -= 1 +# else: +# q3 -= 1 +# else: +# q4 -= 1 +# return IP(q1, q2, q3, q4) + + def __lt__(self, other): + if isinstance(other, basestring): + other = IP.parse(other) + return self.long < other.long + + def __gt__(self, other): + if isinstance(other, basestring): + other = IP.parse(other) + return self.long > other.long + + def __eq__(self, other): + if isinstance(other, basestring): + other = IP.parse(other) + return self.long == other.long + + def __repr__(self): + return '<%s long=%s address="%s">' % ( + self.__class__.__name__, self.long, self.address + ) diff --git a/deluge/plugins/Blocklist/deluge/plugins/blocklist/core.py b/deluge/plugins/Blocklist/deluge/plugins/blocklist/core.py index 897333113..26be958f9 100644 --- a/deluge/plugins/Blocklist/deluge/plugins/blocklist/core.py +++ b/deluge/plugins/Blocklist/deluge/plugins/blocklist/core.py @@ -46,13 +46,13 @@ from twisted.internet.task import LoopingCall from twisted.internet import threads, defer from twisted.web import error - from deluge.plugins.pluginbase import CorePluginBase import deluge.component as component import deluge.configmanager from deluge.common import is_url from deluge.core.rpcserver import export from deluge.httpdownloader import download_file +from common import IP, BadIP from detect import detect_compression, detect_format, create_reader, UnknownFormatError from readers import ReaderParseError @@ -71,14 +71,16 @@ DEFAULT_PREFS = { "list_size": 0, "timeout": 180, "try_times": 3, + "whitelisted": [], } # Constants +ALLOW_RANGE = 0 BLOCK_RANGE = 1 class Core(CorePluginBase): def enable(self): - log.debug('Blocklist: Plugin enabled..') + log.debug('Blocklist: Plugin enabled...') self.is_url = True self.is_downloading = False @@ -86,11 +88,14 @@ class Core(CorePluginBase): self.has_imported = False self.up_to_date = False self.need_to_resume_session = False + self.num_whited = 0 self.num_blocked = 0 self.file_progress = 0.0 self.core = component.get("Core") self.config = deluge.configmanager.ConfigManager("blocklist.conf", DEFAULT_PREFS) + if "whitelisted" not in self.config: + self.config["whitelisted"] = [] self.reader = create_reader(self.config["list_type"], self.config["list_compression"]) @@ -114,10 +119,17 @@ class Core(CorePluginBase): # This function is called every 'check_after_days' days, to download # and import a new list if needed. self.update_timer = LoopingCall(self.check_import) - self.update_timer.start(self.config["check_after_days"] * 24 * 60 * 60, update_now) + if self.config["check_after_days"] > 0: + self.update_timer.start( + self.config["check_after_days"] * 24 * 60 * 60, update_now + ) def disable(self): self.config.save() + log.debug("Reset IP filter") + self.core.session.get_ip_filter().add_rule( + "0.0.0.0", "255.255.255.255", ALLOW_RANGE + ) log.debug('Blocklist: Plugin disabled') def update(self): @@ -136,7 +148,6 @@ class Core(CorePluginBase): :rtype: Deferred """ - # Reset variables self.filename = None self.force_download = force @@ -178,9 +189,67 @@ class Core(CorePluginBase): :param config: config to set :type config: dictionary """ + needs_blocklist_import = False for key in config.keys(): + if key == 'whitelisted': + saved = set(self.config[key]) + update = set(config[key]) + diff = saved.symmetric_difference(update) + if diff: + log.debug("Whitelist changed. Updating...") + added = update.intersection(diff) + removed = saved.intersection(diff) + if added: + for ip in added: + try: + ip = IP.parse(ip) + self.blocklist.add_rule( + ip.address, ip.address, ALLOW_RANGE + ) + saved.add(ip.address) + log.debug("Added %s to whitelisted", ip) + self.num_whited += 1 + except BadIP, e: + log.error("Bad IP: %s", e) + continue + if removed: + needs_blocklist_import = True + for ip in removed: + try: + ip = IP.parse(ip) + saved.remove(ip.address) + log.debug("Removed %s from whitelisted", ip) + except BadIP, e: + log.error("Bad IP: %s", e) + continue + + self.config[key] = list(saved) + continue + elif key == "check_after_days": + if self.config[key] != config[key]: + self.config[key] = config[key] + update_now = False + if self.config["last_update"]: + last_update = datetime.fromtimestamp(self.config["last_update"]) + check_period = timedelta(days=self.config["check_after_days"]) + if not self.config["last_update"] or last_update + check_period < datetime.now(): + update_now = True + self.update_timer.running and self.update_timer.stop() + if self.config["check_after_days"] > 0: + self.update_timer.start( + self.config["check_after_days"] * 24 * 60 * 60, update_now + ) + continue self.config[key] = config[key] + if needs_blocklist_import: + log.debug("IP addresses were removed from the whitelist. Since we " + "don't know if they were blocked before. Re-import " + "current blocklist and re-add whitelisted.") + self.has_imported = False + d = self.import_list(deluge.configmanager.get_config_dir("blocklist.cache")) + d.addCallbacks(self.on_import_complete, self.on_import_error) + @export def get_status(self): """ @@ -198,15 +267,16 @@ class Core(CorePluginBase): status["state"] = "Idle" status["up_to_date"] = self.up_to_date + status["num_whited"] = self.num_whited status["num_blocked"] = self.num_blocked status["file_progress"] = self.file_progress status["file_url"] = self.config["url"] status["file_size"] = self.config["list_size"] status["file_date"] = self.config["last_update"] status["file_type"] = self.config["list_type"] + status["whitelisted"] = self.config["whitelisted"] if self.config["list_compression"]: status["file_type"] += " (%s)" % self.config["list_compression"] - return status #### @@ -258,7 +328,10 @@ class Core(CorePluginBase): log.debug("Attempting to download blocklist %s", url) log.debug("Sending headers: %s", headers) self.is_downloading = True - return download_file(url, deluge.configmanager.get_config_dir("blocklist.download"), on_retrieve_data, headers) + return download_file( + url, deluge.configmanager.get_config_dir("blocklist.download"), + on_retrieve_data, headers + ) def on_download_complete(self, blocklist): """ @@ -318,13 +391,24 @@ class Core(CorePluginBase): :returns: a Deferred that fires when the blocklist has been imported :rtype: Deferred """ + log.trace("on import_list") def on_read_ip_range(start, end): """Add ip range to blocklist""" - self.blocklist.add_rule(start, end, BLOCK_RANGE) +# log.trace("Adding ip range %s - %s to ipfilter as blocked", start, end) + self.blocklist.add_rule(start.address, end.address, BLOCK_RANGE) self.num_blocked += 1 def on_finish_read(result): - """Add blocklist to session""" + """Add any whitelisted IP's and add the blocklist to session""" + # White listing happens last because the last rules added have + # priority + log.info("Added %d ranges to ipfilter as blocked", self.num_blocked) + for ip in self.config["whitelisted"]: + ip = IP.parse(ip) + self.blocklist.add_rule(ip.address, ip.address, ALLOW_RANGE) + self.num_whited += 1 + log.trace("Added %s to the ipfiler as white-listed", ip.address) + log.info("Added %d ranges to ipfilter as white-listed", self.num_whited) self.core.session.set_ip_filter(self.blocklist) return result @@ -335,6 +419,7 @@ class Core(CorePluginBase): self.is_importing = True self.num_blocked = 0 + self.num_whited = 0 self.blocklist = self.core.session.get_ip_filter() if not blocklist: @@ -344,10 +429,16 @@ class Core(CorePluginBase): self.auto_detect(blocklist) self.auto_detected = True + def on_reader_failure(failure): + log.error("Failed to read!!!!!!") + log.exception(failure) + log.debug("Importing using reader: %s", self.reader) log.debug("Reader type: %s compression: %s", self.config["list_type"], self.config["list_compression"]) + log.debug("Clearing current ip filtering") +# self.blocklist.add_rule("0.0.0.0", "255.255.255.255", ALLOW_RANGE) d = threads.deferToThread(self.reader(blocklist).read, on_read_ip_range) - d.addCallback(on_finish_read) + d.addCallback(on_finish_read).addErrback(on_reader_failure) return d @@ -360,6 +451,7 @@ class Core(CorePluginBase): :returns: a Deferred that fires when clean up is done :rtype: Deferred """ + log.trace("on_import_list_complete") d = blocklist self.is_importing = False self.has_imported = True @@ -384,6 +476,7 @@ class Core(CorePluginBase): else the original failure :rtype: Deferred or Failure """ + log.trace("on_import_error: %s", f) d = f self.is_importing = False try_again = False @@ -424,6 +517,7 @@ class Core(CorePluginBase): else: self.reader = create_reader(self.config["list_type"], self.config["list_compression"]) + def pause_session(self): if not self.core.session.is_paused(): self.core.session.pause() diff --git a/deluge/plugins/Blocklist/deluge/plugins/blocklist/data/blocklist_pref.glade b/deluge/plugins/Blocklist/deluge/plugins/blocklist/data/blocklist_pref.glade index 98de5500c..914587204 100644 --- a/deluge/plugins/Blocklist/deluge/plugins/blocklist/data/blocklist_pref.glade +++ b/deluge/plugins/Blocklist/deluge/plugins/blocklist/data/blocklist_pref.glade @@ -1,29 +1,34 @@ -<?xml version="1.0"?> +<?xml version="1.0" encoding="UTF-8"?> <glade-interface> - <!-- interface-requires gtk+ 2.12 --> + <!-- interface-requires gtk+ 2.16 --> <!-- interface-naming-policy toplevel-contextual --> <widget class="GtkWindow" id="window1"> + <property name="can_focus">False</property> <child> <widget class="GtkVBox" id="blocklist_prefs_box"> <property name="visible">True</property> - <property name="orientation">vertical</property> + <property name="can_focus">False</property> <property name="spacing">5</property> <child> <widget class="GtkFrame" id="frame1"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="label_xalign">0</property> <property name="shadow_type">none</property> <child> <widget class="GtkAlignment" id="alignment1"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="left_padding">12</property> <child> <widget class="GtkHBox" id="hbox2"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="spacing">5</property> <child> <widget class="GtkLabel" id="label3"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="label" translatable="yes">URL:</property> </widget> <packing> @@ -36,9 +41,15 @@ <widget class="GtkEntry" id="entry_url"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> + <property name="primary_icon_activatable">False</property> + <property name="secondary_icon_activatable">False</property> + <property name="primary_icon_sensitive">True</property> + <property name="secondary_icon_sensitive">True</property> </widget> <packing> + <property name="expand">True</property> + <property name="fill">True</property> <property name="position">1</property> </packing> </child> @@ -49,6 +60,7 @@ <child> <widget class="GtkLabel" id="label1"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="ypad">5</property> <property name="label" translatable="yes"><b>General</b></property> <property name="use_markup">True</property> @@ -67,26 +79,30 @@ <child> <widget class="GtkFrame" id="frame2"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="label_xalign">0</property> <property name="shadow_type">none</property> <child> <widget class="GtkAlignment" id="alignment2"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="left_padding">12</property> <child> <widget class="GtkVBox" id="vbox1"> <property name="visible">True</property> - <property name="orientation">vertical</property> + <property name="can_focus">False</property> <property name="spacing">5</property> <child> <widget class="GtkTable" id="table1"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="n_columns">3</property> <property name="column_spacing">5</property> <property name="row_spacing">5</property> <child> <widget class="GtkLabel" id="label8"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="xalign">0</property> <property name="label" translatable="yes">Days</property> </widget> @@ -101,7 +117,11 @@ <widget class="GtkSpinButton" id="spin_check_days"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="adjustment">2 0 100 1 10 0</property> + <property name="primary_icon_activatable">False</property> + <property name="secondary_icon_activatable">False</property> + <property name="primary_icon_sensitive">True</property> + <property name="secondary_icon_sensitive">True</property> + <property name="adjustment">1 1 100 1 10 0</property> </widget> <packing> <property name="left_attach">1</property> @@ -113,6 +133,7 @@ <child> <widget class="GtkLabel" id="label4"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="xalign">0</property> <property name="label" translatable="yes">Check for new list every:</property> </widget> @@ -134,6 +155,7 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">False</property> + <property name="use_action_appearance">False</property> <property name="draw_indicator">True</property> </widget> <packing> @@ -149,6 +171,7 @@ <child> <widget class="GtkLabel" id="label10"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="ypad">5</property> <property name="label" translatable="yes"><b>Settings</b></property> <property name="use_markup">True</property> @@ -167,35 +190,41 @@ <child> <widget class="GtkFrame" id="frame3"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="label_xalign">0</property> <property name="shadow_type">none</property> <child> <widget class="GtkAlignment" id="alignment3"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="xalign">0</property> <property name="xscale">0</property> <property name="left_padding">12</property> <child> <widget class="GtkHBox" id="hbox3"> <property name="visible">True</property> + <property name="can_focus">False</property> <child> <widget class="GtkVBox" id="vbox3"> <property name="visible">True</property> - <property name="orientation">vertical</property> + <property name="can_focus">False</property> <child> <widget class="GtkButton" id="button_check_download"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="tooltip" translatable="yes">Download the blocklist file if necessary and import the file.</property> - <signal name="clicked" handler="on_button_check_download_clicked"/> + <property name="use_action_appearance">False</property> + <signal name="clicked" handler="on_button_check_download_clicked" /> <child> <widget class="GtkHBox" id="hbox4"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="spacing">5</property> <child> <widget class="GtkImage" id="image_download"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="stock">gtk-missing-image</property> </widget> <packing> @@ -207,6 +236,7 @@ <child> <widget class="GtkLabel" id="label12"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="label" translatable="yes">Check Download and Import</property> </widget> <packing> @@ -230,14 +260,17 @@ <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="tooltip" translatable="yes">Download a new blocklist file and import it.</property> - <signal name="clicked" handler="on_button_force_download_clicked"/> + <property name="use_action_appearance">False</property> + <signal name="clicked" handler="on_button_force_download_clicked" /> <child> <widget class="GtkHBox" id="hbox5"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="spacing">5</property> <child> <widget class="GtkImage" id="image_import"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="stock">gtk-missing-image</property> </widget> <packing> @@ -249,6 +282,7 @@ <child> <widget class="GtkLabel" id="label7"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="label" translatable="yes">Force Download and Import</property> </widget> <packing> @@ -268,17 +302,22 @@ </child> </widget> <packing> + <property name="expand">True</property> + <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <widget class="GtkImage" id="image_up_to_date"> + <property name="can_focus">False</property> <property name="tooltip" translatable="yes">Blocklist is up to date</property> <property name="yalign">0.15000000596046448</property> <property name="xpad">2</property> <property name="stock">gtk-yes</property> </widget> <packing> + <property name="expand">True</property> + <property name="fill">True</property> <property name="position">1</property> </packing> </child> @@ -289,6 +328,7 @@ <child> <widget class="GtkLabel" id="label11"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="ypad">5</property> <property name="label" translatable="yes"><b>Options</b></property> <property name="use_markup">True</property> @@ -307,34 +347,41 @@ <child> <widget class="GtkFrame" id="frame4"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="label_xalign">0</property> <property name="shadow_type">none</property> <child> <widget class="GtkAlignment" id="alignment4"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="top_padding">5</property> <property name="left_padding">12</property> <child> <widget class="GtkVBox" id="vbox4"> <property name="visible">True</property> - <property name="orientation">vertical</property> + <property name="can_focus">False</property> <child> <widget class="GtkProgressBar" id="progressbar"> <property name="visible">True</property> + <property name="can_focus">False</property> </widget> <packing> + <property name="expand">True</property> + <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <widget class="GtkTable" id="table_info"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="n_rows">4</property> <property name="n_columns">2</property> <property name="column_spacing">5</property> <child> <widget class="GtkLabel" id="label_url"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="xalign">0</property> </widget> <packing> @@ -347,6 +394,7 @@ <child> <widget class="GtkLabel" id="label_type"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="xalign">0</property> </widget> <packing> @@ -359,6 +407,7 @@ <child> <widget class="GtkLabel" id="label_modified"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="xalign">0</property> </widget> <packing> @@ -371,6 +420,7 @@ <child> <widget class="GtkLabel" id="label_filesize"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="xalign">0</property> </widget> <packing> @@ -381,6 +431,7 @@ <child> <widget class="GtkLabel" id="label17"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="xalign">0</property> <property name="label" translatable="yes">URL:</property> </widget> @@ -393,6 +444,7 @@ <child> <widget class="GtkLabel" id="label16"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="xalign">0</property> <property name="label" translatable="yes">Type:</property> </widget> @@ -405,6 +457,7 @@ <child> <widget class="GtkLabel" id="label15"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="xalign">0</property> <property name="label" translatable="yes">Date:</property> </widget> @@ -417,6 +470,7 @@ <child> <widget class="GtkLabel" id="label14"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="xalign">0</property> <property name="label" translatable="yes">File Size:</property> </widget> @@ -438,6 +492,7 @@ <child> <widget class="GtkLabel" id="label13"> <property name="visible">True</property> + <property name="can_focus">False</property> <property name="label" translatable="yes"><b>Info</b></property> <property name="use_markup">True</property> </widget> @@ -452,6 +507,109 @@ <property name="position">3</property> </packing> </child> + <child> + <widget class="GtkFrame" id="whitelist_frame"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <widget class="GtkAlignment" id="alignment5"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="left_padding">12</property> + <child> + <widget class="GtkHBox" id="hbox1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">automatic</property> + <property name="vscrollbar_policy">automatic</property> + <child> + <widget class="GtkTreeView" id="whitelist_treeview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">False</property> + <property name="headers_clickable">False</property> + </widget> + </child> + </widget> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <widget class="GtkVButtonBox" id="vbuttonbox1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="homogeneous">True</property> + <property name="layout_style">start</property> + <child> + <widget class="GtkButton" id="whitelist_add"> + <property name="label">gtk-add</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_action_appearance">False</property> + <property name="use_stock">True</property> + <signal name="clicked" handler="on_whitelist_add_clicked" /> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <widget class="GtkButton" id="whitelist_delete"> + <property name="label">gtk-delete</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_action_appearance">False</property> + <property name="use_stock">True</property> + <signal name="clicked" handler="on_whitelist_remove_clicked" /> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes"><b>Whitelist</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">4</property> + </packing> + </child> </widget> </child> </widget> diff --git a/deluge/plugins/Blocklist/deluge/plugins/blocklist/gtkui.py b/deluge/plugins/Blocklist/deluge/plugins/blocklist/gtkui.py index 7ac58370f..5bfcab410 100644 --- a/deluge/plugins/Blocklist/deluge/plugins/blocklist/gtkui.py +++ b/deluge/plugins/Blocklist/deluge/plugins/blocklist/gtkui.py @@ -51,13 +51,18 @@ class GtkUI(GtkPluginBase): log.debug("Blocklist GtkUI enable..") self.plugin = component.get("PluginManager") - self.load_preferences_page() + try: + self.load_preferences_page() + except Exception, err: + log.exception(err) + raise self.status_item = component.get("StatusBar").add_item( image=common.get_resource("blocklist16.png"), text="", callback=self._on_status_item_clicked, - tooltip="Blocked IP Ranges") + tooltip=_("Blocked IP Ranges /Whitelisted IP Ranges") + ) # Register some hooks self.plugin.register_hook("on_apply_prefs", self._on_apply_prefs) @@ -115,7 +120,8 @@ class GtkUI(GtkPluginBase): self.glade.get_widget("image_up_to_date").hide() self.table_info.show() - self.status_item.set_text(str(status["num_blocked"])) + self.status_item.set_text("%(num_blocked)s/%(num_whited)s" % status) + self.glade.get_widget("label_filesize").set_text( deluge.common.fsize(status["file_size"])) self.glade.get_widget("label_modified").set_text( @@ -128,14 +134,11 @@ class GtkUI(GtkPluginBase): def _on_show_prefs(self): def _on_get_config(config): - self.glade.get_widget("entry_url").set_text( - config["url"]) - - self.glade.get_widget("spin_check_days").set_value( - config["check_after_days"]) - - self.glade.get_widget("chk_import_on_start").set_active( - config["load_on_start"]) + log.trace("Loaded config: %s", config) + self.glade.get_widget("entry_url").set_text(config["url"]) + self.glade.get_widget("spin_check_days").set_value(config["check_after_days"]) + self.glade.get_widget("chk_import_on_start").set_active(config["load_on_start"]) + self.populate_whitelist(config["whitelisted"]) client.blocklist.get_config().addCallback(_on_get_config) @@ -144,6 +147,7 @@ class GtkUI(GtkPluginBase): config["url"] = self.glade.get_widget("entry_url").get_text() config["check_after_days"] = self.glade.get_widget("spin_check_days").get_value_as_int() config["load_on_start"] = self.glade.get_widget("chk_import_on_start").get_active() + config["whitelisted"] = [ip[0] for ip in self.whitelist_model if ip[0]!='IP HERE'] client.blocklist.set_config(config) def _on_button_check_download_clicked(self, widget): @@ -162,6 +166,7 @@ class GtkUI(GtkPluginBase): # Load the preferences page self.glade = gtk.glade.XML(common.get_resource("blocklist_pref.glade")) + self.whitelist_frame = self.glade.get_widget("whitelist_frame") self.progress_bar = self.glade.get_widget("progressbar") self.table_info = self.glade.get_widget("table_info") @@ -169,9 +174,16 @@ class GtkUI(GtkPluginBase): self.progress_bar.hide() self.table_info.show() + # Create the whitelisted model + self.build_whitelist_model_treeview() + self.glade.signal_autoconnect({ "on_button_check_download_clicked": self._on_button_check_download_clicked, - "on_button_force_download_clicked": self._on_button_force_download_clicked + "on_button_force_download_clicked": self._on_button_force_download_clicked, + 'on_whitelist_add_clicked': (self.on_add_button_clicked, + self.whitelist_treeview), + 'on_whitelist_remove_clicked': (self.on_delete_button_clicked, + self.whitelist_treeview), }) # Set button icons @@ -188,3 +200,59 @@ class GtkUI(GtkPluginBase): self.plugin.add_preferences_page( _("Blocklist"), self.glade.get_widget("blocklist_prefs_box")) + + def build_whitelist_model_treeview(self): + self.whitelist_treeview = self.glade.get_widget("whitelist_treeview") + treeview_selection = self.whitelist_treeview.get_selection() + treeview_selection.connect( + "changed", self.on_whitelist_treeview_selection_changed + ) + self.whitelist_model = gtk.ListStore(str, bool) + renderer = gtk.CellRendererText() + renderer.connect("edited", self.on_cell_edited, self.whitelist_model) + renderer.set_data("ip", 0) + + column = gtk.TreeViewColumn("IPs", renderer, text=0, editable=1) + column.set_expand(True) + self.whitelist_treeview.append_column(column) + self.whitelist_treeview.set_model(self.whitelist_model) + + def on_cell_edited(self, cell, path_string, new_text, model): +# iter = model.get_iter_from_string(path_string) +# path = model.get_path(iter)[0] + try: + ip = common.IP.parse(new_text) + model.set(model.get_iter_from_string(path_string), 0, ip.address) + except common.BadIP, e: + model.remove(model.get_iter_from_string(path_string)) + from deluge.ui.gtkui import dialogs + d = dialogs.ErrorDialog(_("Bad IP address"), e.message) + d.run() + + + def on_whitelist_treeview_selection_changed(self, selection): + model, selected_connection_iter = selection.get_selected() + if selected_connection_iter: + self.glade.get_widget("whitelist_delete").set_property('sensitive', + True) + else: + self.glade.get_widget("whitelist_delete").set_property('sensitive', + False) + + def on_add_button_clicked(self, widget, treeview): + model = treeview.get_model() + model.set(model.append(), 0, "IP HERE", 1, True) + + def on_delete_button_clicked(self, widget, treeview): + selection = treeview.get_selection() + model, iter = selection.get_selected() + if iter: +# path = model.get_path(iter)[0] + model.remove(iter) + + def populate_whitelist(self, whitelist): + self.whitelist_model.clear() + for ip in whitelist: + self.whitelist_model.set( + self.whitelist_model.append(),0, ip, 1, True + ) diff --git a/deluge/plugins/Blocklist/deluge/plugins/blocklist/readers.py b/deluge/plugins/Blocklist/deluge/plugins/blocklist/readers.py index f1d9a4da7..9cc5e2b55 100644 --- a/deluge/plugins/Blocklist/deluge/plugins/blocklist/readers.py +++ b/deluge/plugins/Blocklist/deluge/plugins/blocklist/readers.py @@ -33,9 +33,12 @@ # # -from common import raisesErrorsAs, remove_zeros +import logging +from common import raisesErrorsAs, IP, BadIP import re +log = logging.getLogger(__name__) + class ReaderParseError(Exception): pass @@ -51,12 +54,16 @@ class BaseReader(object): def parse(self, line): """Extracts ip range from given line""" - raise NotYetImplemented + raise NotImplementedError def read(self, callback): """Calls callback on each ip range in the file""" for start, end in self.readranges(): - callback(remove_zeros(start), remove_zeros(end)) + try: + callback(IP.parse(start), IP.parse(end)) + except BadIP, e: + log.error("Failed to parse IP: %s", e) +# log.exception(e) return self.file def is_ignored(self, line): diff --git a/deluge/plugins/Blocklist/setup.py b/deluge/plugins/Blocklist/setup.py index ee44297f4..d43edc2bd 100644 --- a/deluge/plugins/Blocklist/setup.py +++ b/deluge/plugins/Blocklist/setup.py @@ -51,6 +51,7 @@ setup( author_email=__author_email__, url=__url__, license=__license__, + zip_safe=False, long_description=__long_description__, packages=find_packages(), |