summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPedro Algarvio <pedro@algarvio.me>2012-02-15 14:21:02 +0000
committerPedro Algarvio <pedro@algarvio.me>2012-02-15 14:21:02 +0000
commitc8718ad643d00c7d166dd1314f385572dc4788a9 (patch)
tree4bba0655e4e732ef8c783625a22e40f2e911551f
parentda868347cf8da28882292c963bd798e72cd41377 (diff)
downloaddeluge-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--ChangeLog8
-rw-r--r--deluge/plugins/Blocklist/deluge/__init__.py3
-rw-r--r--deluge/plugins/Blocklist/deluge/plugins/__init__.py3
-rw-r--r--deluge/plugins/Blocklist/deluge/plugins/blocklist/common.py100
-rw-r--r--deluge/plugins/Blocklist/deluge/plugins/blocklist/core.py112
-rw-r--r--deluge/plugins/Blocklist/deluge/plugins/blocklist/data/blocklist_pref.glade178
-rw-r--r--deluge/plugins/Blocklist/deluge/plugins/blocklist/gtkui.py92
-rw-r--r--deluge/plugins/Blocklist/deluge/plugins/blocklist/readers.py13
-rw-r--r--deluge/plugins/Blocklist/setup.py1
9 files changed, 472 insertions, 38 deletions
diff --git a/ChangeLog b/ChangeLog
index deb3cc29b..a9ff6e9bc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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">&#x25CF;</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">&lt;b&gt;General&lt;/b&gt;</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">&lt;b&gt;Settings&lt;/b&gt;</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">&lt;b&gt;Options&lt;/b&gt;</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">&lt;b&gt;Info&lt;/b&gt;</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">&lt;b&gt;Whitelist&lt;/b&gt;</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(),