From 1c7d6584a7811b7785ae5c1e378f14b5ba0971cf Mon Sep 17 00:00:00 2001 From: takeshi_hoshina Date: Mon, 2 Nov 2020 11:07:33 +0900 Subject: basesystem-jj recipes --- .../0001-python3-fail2ban-2-3-conversion.patch | 2527 ++++++++++++++++++++ 1 file changed, 2527 insertions(+) create mode 100644 external/meta-security/recipes-security/fail2ban/files/0001-python3-fail2ban-2-3-conversion.patch (limited to 'external/meta-security/recipes-security/fail2ban/files/0001-python3-fail2ban-2-3-conversion.patch') diff --git a/external/meta-security/recipes-security/fail2ban/files/0001-python3-fail2ban-2-3-conversion.patch b/external/meta-security/recipes-security/fail2ban/files/0001-python3-fail2ban-2-3-conversion.patch new file mode 100644 index 00000000..ee872ec4 --- /dev/null +++ b/external/meta-security/recipes-security/fail2ban/files/0001-python3-fail2ban-2-3-conversion.patch @@ -0,0 +1,2527 @@ +From abaa20435bac7decffa69e6f965aac9ce29aff6a Mon Sep 17 00:00:00 2001 +From: Armin Kuster +Date: Wed, 12 Feb 2020 17:19:15 +0000 +Subject: [PATCH] python3-fail2ban: 2-3 conversion + +Upstream-Status: OE specific. + +fail2ban handles py3 via a 2-3 conversion utility. + +Signed-off-by: Armin Kuster +--- + fail2ban/client/actionreader.py | 4 +- + fail2ban/client/configparserinc.py | 10 +- + fail2ban/client/configreader.py | 4 +- + fail2ban/client/csocket.py | 4 +- + fail2ban/client/fail2banclient.py | 4 +- + fail2ban/client/fail2banregex.py | 20 +- + fail2ban/client/filterreader.py | 2 +- + fail2ban/client/jailreader.py | 4 +- + fail2ban/helpers.py | 15 +- + fail2ban/server/action.py | 19 +- + fail2ban/server/actions.py | 24 +- + fail2ban/server/asyncserver.py | 4 +- + fail2ban/server/banmanager.py | 18 +- + fail2ban/server/database.py | 6 +- + fail2ban/server/failmanager.py | 8 +- + fail2ban/server/failregex.py | 9 +- + fail2ban/server/filter.py | 12 +- + fail2ban/server/filterpoll.py | 2 +- + fail2ban/server/filterpyinotify.py | 6 +- + fail2ban/server/ipdns.py | 16 +- + fail2ban/server/jail.py | 14 +- + fail2ban/server/mytime.py | 2 +- + fail2ban/server/server.py | 18 +- + fail2ban/server/strptime.py | 6 +- + fail2ban/server/ticket.py | 14 +- + fail2ban/server/transmitter.py | 2 +- + fail2ban/server/utils.py | 6 +- + fail2ban/tests/action_d/test_badips.py | 2 +- + fail2ban/tests/actiontestcase.py | 4 +- + fail2ban/tests/clientreadertestcase.py | 4 +- + fail2ban/tests/databasetestcase.py | 16 +- + fail2ban/tests/datedetectortestcase.py | 6 +- + fail2ban/tests/fail2banclienttestcase.py | 8 +- + fail2ban/tests/failmanagertestcase.py | 10 +- + .../tests/files/config/apache-auth/digest.py | 20 +- + fail2ban/tests/filtertestcase.py | 92 ++--- + fail2ban/tests/misctestcase.py | 22 +- + fail2ban/tests/observertestcase.py | 34 +- + fail2ban/tests/samplestestcase.py | 8 +- + fail2ban/tests/servertestcase.py | 28 +- + fail2ban/tests/sockettestcase.py | 2 +- + fail2ban/tests/utils.py | 22 +- + setup.py | 326 ------------------ + 43 files changed, 264 insertions(+), 593 deletions(-) + delete mode 100755 setup.py + +diff --git a/fail2ban/client/actionreader.py b/fail2ban/client/actionreader.py +index 80617a50..ecf323c5 100644 +--- a/fail2ban/client/actionreader.py ++++ b/fail2ban/client/actionreader.py +@@ -90,11 +90,11 @@ class ActionReader(DefinitionInitConfigReader): + stream = list() + stream.append(head + ["addaction", self._name]) + multi = [] +- for opt, optval in opts.iteritems(): ++ for opt, optval in opts.items(): + if opt in self._configOpts and not opt.startswith('known/'): + multi.append([opt, optval]) + if self._initOpts: +- for opt, optval in self._initOpts.iteritems(): ++ for opt, optval in self._initOpts.items(): + if opt not in self._configOpts and not opt.startswith('known/'): + multi.append([opt, optval]) + if len(multi) > 1: +diff --git a/fail2ban/client/configparserinc.py b/fail2ban/client/configparserinc.py +index e0f39579..45c77437 100644 +--- a/fail2ban/client/configparserinc.py ++++ b/fail2ban/client/configparserinc.py +@@ -62,7 +62,7 @@ if sys.version_info >= (3,2): + parser, option, accum, rest, section, map, *args, **kwargs) + + else: # pragma: no cover +- from ConfigParser import SafeConfigParser, \ ++ from configparser import SafeConfigParser, \ + InterpolationMissingOptionError, NoOptionError, NoSectionError + + # Interpolate missing known/option as option from default section +@@ -327,7 +327,7 @@ after = 1.conf + # mix it with defaults: + return set(opts.keys()) | set(self._defaults) + # only own option names: +- return opts.keys() ++ return list(opts.keys()) + + def read(self, filenames, get_includes=True): + if not isinstance(filenames, list): +@@ -356,7 +356,7 @@ after = 1.conf + ret += i + # merge defaults and all sections to self: + alld.update(cfg.get_defaults()) +- for n, s in cfg.get_sections().iteritems(): ++ for n, s in cfg.get_sections().items(): + # conditional sections + cond = SafeConfigParserWithIncludes.CONDITIONAL_RE.match(n) + if cond: +@@ -366,7 +366,7 @@ after = 1.conf + del(s['__name__']) + except KeyError: + pass +- for k in s.keys(): ++ for k in list(s.keys()): + v = s.pop(k) + s[k + cond] = v + s2 = alls.get(n) +@@ -399,7 +399,7 @@ after = 1.conf + sec.update(options) + return + sk = {} +- for k, v in options.iteritems(): ++ for k, v in options.items(): + if not k.startswith(pref) and k != '__name__': + sk[pref+k] = v + sec.update(sk) +diff --git a/fail2ban/client/configreader.py b/fail2ban/client/configreader.py +index 20709b72..b5167409 100644 +--- a/fail2ban/client/configreader.py ++++ b/fail2ban/client/configreader.py +@@ -26,7 +26,7 @@ __license__ = "GPL" + + import glob + import os +-from ConfigParser import NoOptionError, NoSectionError ++from configparser import NoOptionError, NoSectionError + + from .configparserinc import sys, SafeConfigParserWithIncludes, logLevel + from ..helpers import getLogger, _as_bool, _merge_dicts, substituteRecursiveTags +@@ -197,7 +197,7 @@ class ConfigReaderUnshared(SafeConfigParserWithIncludes): + config_files += sorted(glob.glob('%s/*.local' % config_dir)) + + # choose only existing ones +- config_files = filter(os.path.exists, config_files) ++ config_files = list(filter(os.path.exists, config_files)) + + if len(config_files): + # at least one config exists and accessible +diff --git a/fail2ban/client/csocket.py b/fail2ban/client/csocket.py +index ab3e294b..9417cde9 100644 +--- a/fail2ban/client/csocket.py ++++ b/fail2ban/client/csocket.py +@@ -47,7 +47,7 @@ class CSocket: + + def send(self, msg, nonblocking=False, timeout=None): + # Convert every list member to string +- obj = dumps(map(CSocket.convert, msg), HIGHEST_PROTOCOL) ++ obj = dumps(list(map(CSocket.convert, msg)), HIGHEST_PROTOCOL) + self.__csock.send(obj + CSPROTO.END) + return self.receive(self.__csock, nonblocking, timeout) + +@@ -71,7 +71,7 @@ class CSocket: + @staticmethod + def convert(m): + """Convert every "unexpected" member of message to string""" +- if isinstance(m, (basestring, bool, int, float, list, dict, set)): ++ if isinstance(m, (str, bool, int, float, list, dict, set)): + return m + else: # pragma: no cover + return str(m) +diff --git a/fail2ban/client/fail2banclient.py b/fail2ban/client/fail2banclient.py +index 7c90ca40..7eb11684 100755 +--- a/fail2ban/client/fail2banclient.py ++++ b/fail2ban/client/fail2banclient.py +@@ -45,7 +45,7 @@ def _thread_name(): + return threading.current_thread().__class__.__name__ + + def input_command(): # pragma: no cover +- return raw_input(PROMPT) ++ return input(PROMPT) + + ## + # +@@ -444,7 +444,7 @@ class Fail2banClient(Fail2banCmdLine, Thread): + return False + finally: + self._alive = False +- for s, sh in _prev_signals.iteritems(): ++ for s, sh in _prev_signals.items(): + signal.signal(s, sh) + + +diff --git a/fail2ban/client/fail2banregex.py b/fail2ban/client/fail2banregex.py +index 513b765d..4a71b3c0 100644 +--- a/fail2ban/client/fail2banregex.py ++++ b/fail2ban/client/fail2banregex.py +@@ -41,10 +41,10 @@ import shlex + import sys + import time + import time +-import urllib ++import urllib.request, urllib.parse, urllib.error + from optparse import OptionParser, Option + +-from ConfigParser import NoOptionError, NoSectionError, MissingSectionHeaderError ++from configparser import NoOptionError, NoSectionError, MissingSectionHeaderError + + try: # pragma: no cover + from ..server.filtersystemd import FilterSystemd +@@ -68,7 +68,7 @@ def debuggexURL(sample, regex, multiline=False, useDns="yes"): + 'flavor': 'python' + } + if multiline: args['flags'] = 'm' +- return 'https://www.debuggex.com/?' + urllib.urlencode(args) ++ return 'https://www.debuggex.com/?' + urllib.parse.urlencode(args) + + def output(args): # pragma: no cover (overriden in test-cases) + print(args) +@@ -244,7 +244,7 @@ class Fail2banRegex(object): + + def __init__(self, opts): + # set local protected members from given options: +- self.__dict__.update(dict(('_'+o,v) for o,v in opts.__dict__.iteritems())) ++ self.__dict__.update(dict(('_'+o,v) for o,v in opts.__dict__.items())) + self._opts = opts + self._maxlines_set = False # so we allow to override maxlines in cmdline + self._datepattern_set = False +@@ -304,7 +304,7 @@ class Fail2banRegex(object): + realopts = {} + combopts = reader.getCombined() + # output all options that are specified in filter-argument as well as some special (mostly interested): +- for k in ['logtype', 'datepattern'] + fltOpt.keys(): ++ for k in ['logtype', 'datepattern'] + list(fltOpt.keys()): + # combined options win, but they contain only a sub-set in filter expected keys, + # so get the rest from definition section: + try: +@@ -424,7 +424,7 @@ class Fail2banRegex(object): + self.output( "Use %11s line : %s" % (regex, shortstr(value)) ) + regex_values = {regextype: [RegexStat(value)]} + +- for regextype, regex_values in regex_values.iteritems(): ++ for regextype, regex_values in regex_values.items(): + regex = regextype + 'regex' + setattr(self, "_" + regex, regex_values) + for regex in regex_values: +@@ -523,10 +523,10 @@ class Fail2banRegex(object): + output(ret[1]) + elif self._opts.out == 'msg': + for ret in ret: +- output('\n'.join(map(lambda v:''.join(v for v in v), ret[3].get('matches')))) ++ output('\n'.join([''.join(v for v in v) for v in ret[3].get('matches')])) + elif self._opts.out == 'row': + for ret in ret: +- output('[%r,\t%r,\t%r],' % (ret[1],ret[2],dict((k,v) for k, v in ret[3].iteritems() if k != 'matches'))) ++ output('[%r,\t%r,\t%r],' % (ret[1],ret[2],dict((k,v) for k, v in ret[3].items() if k != 'matches'))) + else: + for ret in ret: + output(ret[3].get(self._opts.out)) +@@ -565,9 +565,9 @@ class Fail2banRegex(object): + ans = [[]] + for arg in [l, regexlist]: + ans = [ x + [y] for x in ans for y in arg ] +- b = map(lambda a: a[0] + ' | ' + a[1].getFailRegex() + ' | ' + ++ b = [a[0] + ' | ' + a[1].getFailRegex() + ' | ' + + debuggexURL(self.encode_line(a[0]), a[1].getFailRegex(), +- multiline, self._opts.usedns), ans) ++ multiline, self._opts.usedns) for a in ans] + pprint_list([x.rstrip() for x in b], header) + else: + output( "%s too many to print. Use --print-all-%s " \ +diff --git a/fail2ban/client/filterreader.py b/fail2ban/client/filterreader.py +index 413f125e..4f0cc4cf 100644 +--- a/fail2ban/client/filterreader.py ++++ b/fail2ban/client/filterreader.py +@@ -71,7 +71,7 @@ class FilterReader(DefinitionInitConfigReader): + @staticmethod + def _fillStream(stream, opts, jailName): + prio0idx = 0 +- for opt, value in opts.iteritems(): ++ for opt, value in opts.items(): + if opt in ("failregex", "ignoreregex"): + if value is None: continue + multi = [] +diff --git a/fail2ban/client/jailreader.py b/fail2ban/client/jailreader.py +index 50c1d047..969d0bc0 100644 +--- a/fail2ban/client/jailreader.py ++++ b/fail2ban/client/jailreader.py +@@ -117,7 +117,7 @@ class JailReader(ConfigReader): + } + _configOpts.update(FilterReader._configOpts) + +- _ignoreOpts = set(['action', 'filter', 'enabled'] + FilterReader._configOpts.keys()) ++ _ignoreOpts = set(['action', 'filter', 'enabled'] + list(FilterReader._configOpts.keys())) + + def getOptions(self): + +@@ -236,7 +236,7 @@ class JailReader(ConfigReader): + stream.extend(self.__filter.convert()) + # and using options from jail: + FilterReader._fillStream(stream, self.__opts, self.__name) +- for opt, value in self.__opts.iteritems(): ++ for opt, value in self.__opts.items(): + if opt == "logpath": + if self.__opts.get('backend', '').startswith("systemd"): continue + found_files = 0 +diff --git a/fail2ban/helpers.py b/fail2ban/helpers.py +index 6f2bcdd7..7e563696 100644 +--- a/fail2ban/helpers.py ++++ b/fail2ban/helpers.py +@@ -31,6 +31,7 @@ import traceback + from threading import Lock + + from .server.mytime import MyTime ++import importlib + + try: + import ctypes +@@ -63,7 +64,7 @@ if sys.version_info < (3,): # pragma: 3.x no cover + from imp import load_dynamic as __ldm + _sys = __ldm('_sys', 'sys') + except ImportError: # pragma: no cover - only if load_dynamic fails +- reload(sys) ++ importlib.reload(sys) + _sys = sys + if hasattr(_sys, "setdefaultencoding"): + _sys.setdefaultencoding(encoding) +@@ -101,7 +102,7 @@ if sys.version_info >= (3,): # pragma: 2.x no cover + else: # pragma: 3.x no cover + def uni_decode(x, enc=PREFER_ENC, errors='strict'): + try: +- if isinstance(x, unicode): ++ if isinstance(x, str): + return x.encode(enc, errors) + return x + except (UnicodeDecodeError, UnicodeEncodeError): # pragma: no cover - unsure if reachable +@@ -110,7 +111,7 @@ else: # pragma: 3.x no cover + return x.encode(enc, 'replace') + if sys.getdefaultencoding().upper() != 'UTF-8': # pragma: no cover - utf-8 is default encoding now + def uni_string(x): +- if not isinstance(x, unicode): ++ if not isinstance(x, str): + return str(x) + return x.encode(PREFER_ENC, 'replace') + else: +@@ -118,7 +119,7 @@ else: # pragma: 3.x no cover + + + def _as_bool(val): +- return bool(val) if not isinstance(val, basestring) \ ++ return bool(val) if not isinstance(val, str) \ + else val.lower() in ('1', 'on', 'true', 'yes') + + +@@ -326,7 +327,7 @@ def splitwords(s): + """ + if not s: + return [] +- return filter(bool, map(lambda v: v.strip(), re.split('[ ,\n]+', s))) ++ return list(filter(bool, [v.strip() for v in re.split('[ ,\n]+', s)])) + + if sys.version_info >= (3,5): + eval(compile(r'''if 1: +@@ -436,7 +437,7 @@ def substituteRecursiveTags(inptags, conditional='', + while True: + repFlag = False + # substitute each value: +- for tag in tags.iterkeys(): ++ for tag in tags.keys(): + # ignore escaped or already done (or in ignore list): + if tag in ignore or tag in done: continue + # ignore replacing callable items from calling map - should be converted on demand only (by get): +@@ -476,7 +477,7 @@ def substituteRecursiveTags(inptags, conditional='', + m = tre_search(value, m.end()) + continue + # if calling map - be sure we've string: +- if not isinstance(repl, basestring): repl = uni_string(repl) ++ if not isinstance(repl, str): repl = uni_string(repl) + value = value.replace('<%s>' % rtag, repl) + #logSys.log(5, 'value now: %s' % value) + # increment reference count: +diff --git a/fail2ban/server/action.py b/fail2ban/server/action.py +index 5c817fc0..81d50689 100644 +--- a/fail2ban/server/action.py ++++ b/fail2ban/server/action.py +@@ -111,9 +111,9 @@ class CallingMap(MutableMapping, object): + def _asdict(self, calculated=False, checker=None): + d = dict(self.data, **self.storage) + if not calculated: +- return dict((n,v) for n,v in d.iteritems() \ ++ return dict((n,v) for n,v in d.items() \ + if not callable(v) or n in self.CM_REPR_ITEMS) +- for n,v in d.items(): ++ for n,v in list(d.items()): + if callable(v): + try: + # calculate: +@@ -179,7 +179,7 @@ class CallingMap(MutableMapping, object): + return self.__class__(_merge_copy_dicts(self.data, self.storage)) + + +-class ActionBase(object): ++class ActionBase(object, metaclass=ABCMeta): + """An abstract base class for actions in Fail2Ban. + + Action Base is a base definition of what methods need to be in +@@ -209,7 +209,6 @@ class ActionBase(object): + Any additional arguments specified in `jail.conf` or passed + via `fail2ban-client` will be passed as keyword arguments. + """ +- __metaclass__ = ABCMeta + + @classmethod + def __subclasshook__(cls, C): +@@ -420,7 +419,7 @@ class CommandAction(ActionBase): + if not callable(family): # pragma: no cover + return self.__substCache.get(key, {}).get(family) + # family as expression - use it to filter values: +- return [v for f, v in self.__substCache.get(key, {}).iteritems() if family(f)] ++ return [v for f, v in self.__substCache.get(key, {}).items() if family(f)] + cmd = args[0] + if cmd: # set: + try: +@@ -432,7 +431,7 @@ class CommandAction(ActionBase): + try: + famd = self.__substCache[key] + cmd = famd.pop(family) +- for family, v in famd.items(): ++ for family, v in list(famd.items()): + if v == cmd: + del famd[family] + except KeyError: # pragma: no cover +@@ -448,7 +447,7 @@ class CommandAction(ActionBase): + res = True + err = 'Script error' + if not family: # all started: +- family = [famoper for (famoper,v) in self.__started.iteritems() if v] ++ family = [famoper for (famoper,v) in self.__started.items() if v] + for famoper in family: + try: + cmd = self._getOperation(tag, famoper) +@@ -617,7 +616,7 @@ class CommandAction(ActionBase): + and executes the resulting command. + """ + # collect started families, may be started on demand (conditional): +- family = [f for (f,v) in self.__started.iteritems() if v & 3 == 3]; # started and contains items ++ family = [f for (f,v) in self.__started.items() if v & 3 == 3]; # started and contains items + # if nothing contains items: + if not family: return True + # flush: +@@ -642,7 +641,7 @@ class CommandAction(ActionBase): + """ + # collect started families, if started on demand (conditional): + if family is None: +- family = [f for (f,v) in self.__started.iteritems() if v] ++ family = [f for (f,v) in self.__started.items() if v] + # if no started (on demand) actions: + if not family: return True + self.__started = {} +@@ -676,7 +675,7 @@ class CommandAction(ActionBase): + ret = True + # for each started family: + if self.actioncheck: +- for (family, started) in self.__started.items(): ++ for (family, started) in list(self.__started.items()): + if started and not self._invariantCheck(family, beforeRepair): + # reset started flag and command of executed operation: + self.__started[family] = 0 +diff --git a/fail2ban/server/actions.py b/fail2ban/server/actions.py +index 24fea838..94b9c3ed 100644 +--- a/fail2ban/server/actions.py ++++ b/fail2ban/server/actions.py +@@ -156,11 +156,11 @@ class Actions(JailThread, Mapping): + else: + if hasattr(self, '_reload_actions'): + # reload actions after all parameters set via stream: +- for name, initOpts in self._reload_actions.iteritems(): ++ for name, initOpts in self._reload_actions.items(): + if name in self._actions: + self._actions[name].reload(**(initOpts if initOpts else {})) + # remove obsolete actions (untouched by reload process): +- delacts = OrderedDict((name, action) for name, action in self._actions.iteritems() ++ delacts = OrderedDict((name, action) for name, action in self._actions.items() + if name not in self._reload_actions) + if len(delacts): + # unban all tickets using removed actions only: +@@ -289,7 +289,7 @@ class Actions(JailThread, Mapping): + """ + if actions is None: + actions = self._actions +- revactions = actions.items() ++ revactions = list(actions.items()) + revactions.reverse() + for name, action in revactions: + try: +@@ -314,7 +314,7 @@ class Actions(JailThread, Mapping): + True when the thread exits nicely. + """ + cnt = 0 +- for name, action in self._actions.iteritems(): ++ for name, action in self._actions.items(): + try: + action.start() + except Exception as e: +@@ -474,7 +474,7 @@ class Actions(JailThread, Mapping): + Observers.Main.add('banFound', bTicket, self._jail, btime) + logSys.notice("[%s] %sBan %s", self._jail.name, ('' if not bTicket.restored else 'Restore '), ip) + # do actions : +- for name, action in self._actions.iteritems(): ++ for name, action in self._actions.items(): + try: + if ticket.restored and getattr(action, 'norestored', False): + continue +@@ -511,13 +511,13 @@ class Actions(JailThread, Mapping): + if bTicket.banEpoch == self.banEpoch and diftm > 3: + # avoid too often checks: + if not rebanacts and MyTime.time() > self.__lastConsistencyCheckTM + 3: +- for action in self._actions.itervalues(): ++ for action in self._actions.values(): + action.consistencyCheck() + self.__lastConsistencyCheckTM = MyTime.time() + # check epoch in order to reban it: + if bTicket.banEpoch < self.banEpoch: + if not rebanacts: rebanacts = dict( +- (name, action) for name, action in self._actions.iteritems() ++ (name, action) for name, action in self._actions.items() + if action.banEpoch > bTicket.banEpoch) + cnt += self.__reBan(bTicket, actions=rebanacts) + else: # pragma: no cover - unexpected: ticket is not banned for some reasons - reban using all actions: +@@ -542,8 +542,8 @@ class Actions(JailThread, Mapping): + ip = ticket.getIP() + aInfo = self.__getActionInfo(ticket) + if log: +- logSys.notice("[%s] Reban %s%s", self._jail.name, aInfo["ip"], (', action %r' % actions.keys()[0] if len(actions) == 1 else '')) +- for name, action in actions.iteritems(): ++ logSys.notice("[%s] Reban %s%s", self._jail.name, aInfo["ip"], (', action %r' % list(actions.keys())[0] if len(actions) == 1 else '')) ++ for name, action in actions.items(): + try: + logSys.debug("[%s] action %r: reban %s", self._jail.name, name, ip) + if not aInfo.immutable: aInfo.reset() +@@ -567,7 +567,7 @@ class Actions(JailThread, Mapping): + if not self.__banManager._inBanList(ticket): return + # do actions : + aInfo = None +- for name, action in self._actions.iteritems(): ++ for name, action in self._actions.items(): + try: + if ticket.restored and getattr(action, 'norestored', False): + continue +@@ -616,7 +616,7 @@ class Actions(JailThread, Mapping): + cnt = 0 + # first we'll execute flush for actions supporting this operation: + unbactions = {} +- for name, action in (actions if actions is not None else self._actions).iteritems(): ++ for name, action in (actions if actions is not None else self._actions).items(): + try: + if hasattr(action, 'flush') and (not isinstance(action, CommandAction) or action.actionflush): + logSys.notice("[%s] Flush ticket(s) with %s", self._jail.name, name) +@@ -671,7 +671,7 @@ class Actions(JailThread, Mapping): + aInfo = self.__getActionInfo(ticket) + if log: + logSys.notice("[%s] Unban %s", self._jail.name, aInfo["ip"]) +- for name, action in unbactions.iteritems(): ++ for name, action in unbactions.items(): + try: + logSys.debug("[%s] action %r: unban %s", self._jail.name, name, ip) + if not aInfo.immutable: aInfo.reset() +diff --git a/fail2ban/server/asyncserver.py b/fail2ban/server/asyncserver.py +index e3400737..f5f9740b 100644 +--- a/fail2ban/server/asyncserver.py ++++ b/fail2ban/server/asyncserver.py +@@ -178,7 +178,7 @@ def loop(active, timeout=None, use_poll=False, err_count=None): + elif err_count['listen'] > 100: # pragma: no cover - normally unreachable + if ( + e.args[0] == errno.EMFILE # [Errno 24] Too many open files +- or sum(err_count.itervalues()) > 1000 ++ or sum(err_count.values()) > 1000 + ): + logSys.critical("Too many errors - critical count reached %r", err_count) + break +@@ -220,7 +220,7 @@ class AsyncServer(asyncore.dispatcher): + elif self.__errCount['accept'] > 100: + if ( + (isinstance(e, socket.error) and e.args[0] == errno.EMFILE) # [Errno 24] Too many open files +- or sum(self.__errCount.itervalues()) > 1000 ++ or sum(self.__errCount.values()) > 1000 + ): + logSys.critical("Too many errors - critical count reached %r", self.__errCount) + self.stop() +diff --git a/fail2ban/server/banmanager.py b/fail2ban/server/banmanager.py +index 5770bfd7..9bb44971 100644 +--- a/fail2ban/server/banmanager.py ++++ b/fail2ban/server/banmanager.py +@@ -105,9 +105,9 @@ class BanManager: + def getBanList(self, ordered=False, withTime=False): + with self.__lock: + if not ordered: +- return self.__banList.keys() ++ return list(self.__banList.keys()) + lst = [] +- for ticket in self.__banList.itervalues(): ++ for ticket in self.__banList.values(): + eob = ticket.getEndOfBanTime(self.__banTime) + lst.append((ticket,eob)) + lst.sort(key=lambda t: t[1]) +@@ -126,7 +126,7 @@ class BanManager: + + def __iter__(self): + with self.__lock: +- return self.__banList.itervalues() ++ return iter(self.__banList.values()) + + ## + # Returns normalized value +@@ -165,7 +165,7 @@ class BanManager: + return return_dict + # get ips in lock: + with self.__lock: +- banIPs = [banData.getIP() for banData in self.__banList.values()] ++ banIPs = [banData.getIP() for banData in list(self.__banList.values())] + # get cymru info: + try: + for ip in banIPs: +@@ -341,7 +341,7 @@ class BanManager: + # Gets the list of ticket to remove (thereby correct next unban time). + unBanList = {} + nextUnbanTime = BanTicket.MAX_TIME +- for fid,ticket in self.__banList.iteritems(): ++ for fid,ticket in self.__banList.items(): + # current time greater as end of ban - timed out: + eob = ticket.getEndOfBanTime(self.__banTime) + if time > eob: +@@ -357,15 +357,15 @@ class BanManager: + if len(unBanList): + if len(unBanList) / 2.0 <= len(self.__banList) / 3.0: + # few as 2/3 should be removed - remove particular items: +- for fid in unBanList.iterkeys(): ++ for fid in unBanList.keys(): + del self.__banList[fid] + else: + # create new dictionary without items to be deleted: +- self.__banList = dict((fid,ticket) for fid,ticket in self.__banList.iteritems() \ ++ self.__banList = dict((fid,ticket) for fid,ticket in self.__banList.items() \ + if fid not in unBanList) + + # return list of tickets: +- return unBanList.values() ++ return list(unBanList.values()) + + ## + # Flush the ban list. +@@ -375,7 +375,7 @@ class BanManager: + + def flushBanList(self): + with self.__lock: +- uBList = self.__banList.values() ++ uBList = list(self.__banList.values()) + self.__banList = dict() + return uBList + +diff --git a/fail2ban/server/database.py b/fail2ban/server/database.py +index ed736a7a..0e8c9aec 100644 +--- a/fail2ban/server/database.py ++++ b/fail2ban/server/database.py +@@ -67,13 +67,13 @@ if sys.version_info >= (3,): # pragma: 2.x no cover + else: # pragma: 3.x no cover + def _normalize(x): + if isinstance(x, dict): +- return dict((_normalize(k), _normalize(v)) for k, v in x.iteritems()) ++ return dict((_normalize(k), _normalize(v)) for k, v in x.items()) + elif isinstance(x, (list, set)): + return [_normalize(element) for element in x] +- elif isinstance(x, unicode): ++ elif isinstance(x, str): + # in 2.x default text_factory is unicode - so return proper unicode here: + return x.encode(PREFER_ENC, 'replace').decode(PREFER_ENC) +- elif isinstance(x, basestring): ++ elif isinstance(x, str): + return x.decode(PREFER_ENC, 'replace') + return x + +diff --git a/fail2ban/server/failmanager.py b/fail2ban/server/failmanager.py +index 93c028fb..a9c6b5f6 100644 +--- a/fail2ban/server/failmanager.py ++++ b/fail2ban/server/failmanager.py +@@ -57,7 +57,7 @@ class FailManager: + def getFailCount(self): + # may be slow on large list of failures, should be used for test purposes only... + with self.__lock: +- return len(self.__failList), sum([f.getRetry() for f in self.__failList.values()]) ++ return len(self.__failList), sum([f.getRetry() for f in list(self.__failList.values())]) + + def getFailTotal(self): + with self.__lock: +@@ -125,7 +125,7 @@ class FailManager: + # in case of having many active failures, it should be ran only + # if debug level is "low" enough + failures_summary = ', '.join(['%s:%d' % (k, v.getRetry()) +- for k,v in self.__failList.iteritems()]) ++ for k,v in self.__failList.items()]) + logSys.log(logLevel, "Total # of detected failures: %d. Current failures from %d IPs (IP:count): %s" + % (self.__failTotal, len(self.__failList), failures_summary)) + +@@ -138,7 +138,7 @@ class FailManager: + + def cleanup(self, time): + with self.__lock: +- todelete = [fid for fid,item in self.__failList.iteritems() \ ++ todelete = [fid for fid,item in self.__failList.items() \ + if item.getLastTime() + self.__maxTime <= time] + if len(todelete) == len(self.__failList): + # remove all: +@@ -152,7 +152,7 @@ class FailManager: + del self.__failList[fid] + else: + # create new dictionary without items to be deleted: +- self.__failList = dict((fid,item) for fid,item in self.__failList.iteritems() \ ++ self.__failList = dict((fid,item) for fid,item in self.__failList.items() \ + if item.getLastTime() + self.__maxTime > time) + self.__bgSvc.service() + +diff --git a/fail2ban/server/failregex.py b/fail2ban/server/failregex.py +index f7dafbef..fb75187d 100644 +--- a/fail2ban/server/failregex.py ++++ b/fail2ban/server/failregex.py +@@ -128,10 +128,7 @@ class Regex: + self._regexObj = re.compile(regex, re.MULTILINE if multiline else 0) + self._regex = regex + self._altValues = {} +- for k in filter( +- lambda k: len(k) > len(ALTNAME_PRE) and k.startswith(ALTNAME_PRE), +- self._regexObj.groupindex +- ): ++ for k in [k for k in self._regexObj.groupindex if len(k) > len(ALTNAME_PRE) and k.startswith(ALTNAME_PRE)]: + n = ALTNAME_CRE.match(k).group(1) + self._altValues[k] = n + self._altValues = list(self._altValues.items()) if len(self._altValues) else None +@@ -211,7 +208,7 @@ class Regex: + # + @staticmethod + def _tupleLinesBuf(tupleLines): +- return "\n".join(map(lambda v: "".join(v[::2]), tupleLines)) + "\n" ++ return "\n".join(["".join(v[::2]) for v in tupleLines]) + "\n" + + ## + # Searches the regular expression. +@@ -223,7 +220,7 @@ class Regex: + + def search(self, tupleLines, orgLines=None): + buf = tupleLines +- if not isinstance(tupleLines, basestring): ++ if not isinstance(tupleLines, str): + buf = Regex._tupleLinesBuf(tupleLines) + self._matchCache = self._regexObj.search(buf) + if self._matchCache: +diff --git a/fail2ban/server/filter.py b/fail2ban/server/filter.py +index 998fe298..d181fd38 100644 +--- a/fail2ban/server/filter.py ++++ b/fail2ban/server/filter.py +@@ -292,7 +292,7 @@ class Filter(JailThread): + dd = DateDetector() + dd.default_tz = self.__logtimezone + if not isinstance(pattern, (list, tuple)): +- pattern = filter(bool, map(str.strip, re.split('\n+', pattern))) ++ pattern = list(filter(bool, list(map(str.strip, re.split('\n+', pattern))))) + for pattern in pattern: + dd.appendTemplate(pattern) + self.dateDetector = dd +@@ -987,7 +987,7 @@ class FileFilter(Filter): + # @return log paths + + def getLogPaths(self): +- return self.__logs.keys() ++ return list(self.__logs.keys()) + + ## + # Get the log containers +@@ -995,7 +995,7 @@ class FileFilter(Filter): + # @return log containers + + def getLogs(self): +- return self.__logs.values() ++ return list(self.__logs.values()) + + ## + # Get the count of log containers +@@ -1021,7 +1021,7 @@ class FileFilter(Filter): + + def setLogEncoding(self, encoding): + encoding = super(FileFilter, self).setLogEncoding(encoding) +- for log in self.__logs.itervalues(): ++ for log in self.__logs.values(): + log.setEncoding(encoding) + + def getLog(self, path): +@@ -1183,7 +1183,7 @@ class FileFilter(Filter): + """Status of Filter plus files being monitored. + """ + ret = super(FileFilter, self).status(flavor=flavor) +- path = self.__logs.keys() ++ path = list(self.__logs.keys()) + ret.append(("File list", path)) + return ret + +@@ -1191,7 +1191,7 @@ class FileFilter(Filter): + """Stop monitoring of log-file(s) + """ + # stop files monitoring: +- for path in self.__logs.keys(): ++ for path in list(self.__logs.keys()): + self.delLogPath(path) + # stop thread: + super(Filter, self).stop() +diff --git a/fail2ban/server/filterpoll.py b/fail2ban/server/filterpoll.py +index 228a2c8b..d49315cc 100644 +--- a/fail2ban/server/filterpoll.py ++++ b/fail2ban/server/filterpoll.py +@@ -176,4 +176,4 @@ class FilterPoll(FileFilter): + return False + + def getPendingPaths(self): +- return self.__file404Cnt.keys() ++ return list(self.__file404Cnt.keys()) +diff --git a/fail2ban/server/filterpyinotify.py b/fail2ban/server/filterpyinotify.py +index ca6b253f..b683b860 100644 +--- a/fail2ban/server/filterpyinotify.py ++++ b/fail2ban/server/filterpyinotify.py +@@ -158,7 +158,7 @@ class FilterPyinotify(FileFilter): + except KeyError: pass + + def getPendingPaths(self): +- return self.__pending.keys() ++ return list(self.__pending.keys()) + + def _checkPending(self): + if not self.__pending: +@@ -168,7 +168,7 @@ class FilterPyinotify(FileFilter): + return + found = {} + minTime = 60 +- for path, (retardTM, isDir) in self.__pending.iteritems(): ++ for path, (retardTM, isDir) in self.__pending.items(): + if ntm - self.__pendingChkTime < retardTM: + if minTime > retardTM: minTime = retardTM + continue +@@ -184,7 +184,7 @@ class FilterPyinotify(FileFilter): + self.__pendingChkTime = time.time() + self.__pendingMinTime = minTime + # process now because we've missed it in monitoring: +- for path, isDir in found.iteritems(): ++ for path, isDir in found.items(): + self._delPending(path) + # refresh monitoring of this: + self._refreshWatcher(path, isDir=isDir) +diff --git a/fail2ban/server/ipdns.py b/fail2ban/server/ipdns.py +index 6648dac6..fe8f8db8 100644 +--- a/fail2ban/server/ipdns.py ++++ b/fail2ban/server/ipdns.py +@@ -275,7 +275,7 @@ class IPAddr(object): + raise ValueError("invalid ipstr %r, too many plen representation" % (ipstr,)) + if "." in s[1] or ":" in s[1]: # 255.255.255.0 resp. ffff:: style mask + s[1] = IPAddr.masktoplen(s[1]) +- s[1] = long(s[1]) ++ s[1] = int(s[1]) + return s + + def __init(self, ipstr, cidr=CIDR_UNSPEC): +@@ -309,7 +309,7 @@ class IPAddr(object): + + # mask out host portion if prefix length is supplied + if cidr is not None and cidr >= 0: +- mask = ~(0xFFFFFFFFL >> cidr) ++ mask = ~(0xFFFFFFFF >> cidr) + self._addr &= mask + self._plen = cidr + +@@ -321,13 +321,13 @@ class IPAddr(object): + + # mask out host portion if prefix length is supplied + if cidr is not None and cidr >= 0: +- mask = ~(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFL >> cidr) ++ mask = ~(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF >> cidr) + self._addr &= mask + self._plen = cidr + + # if IPv6 address is a IPv4-compatible, make instance a IPv4 + elif self.isInNet(IPAddr.IP6_4COMPAT): +- self._addr = lo & 0xFFFFFFFFL ++ self._addr = lo & 0xFFFFFFFF + self._family = socket.AF_INET + self._plen = 32 + else: +@@ -445,7 +445,7 @@ class IPAddr(object): + elif self.isIPv6: + # convert network to host byte order + hi = self._addr >> 64 +- lo = self._addr & 0xFFFFFFFFFFFFFFFFL ++ lo = self._addr & 0xFFFFFFFFFFFFFFFF + binary = struct.pack("!QQ", hi, lo) + if self._plen and self._plen < 128: + add = "/%d" % self._plen +@@ -503,9 +503,9 @@ class IPAddr(object): + if self.family != net.family: + return False + if self.isIPv4: +- mask = ~(0xFFFFFFFFL >> net.plen) ++ mask = ~(0xFFFFFFFF >> net.plen) + elif self.isIPv6: +- mask = ~(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFL >> net.plen) ++ mask = ~(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF >> net.plen) + else: + return False + +@@ -517,7 +517,7 @@ class IPAddr(object): + m4 = (1 << 32)-1 + mmap = {m6: 128, m4: 32, 0: 0} + m = 0 +- for i in xrange(0, 128): ++ for i in range(0, 128): + m |= 1 << i + if i < 32: + mmap[m ^ m4] = 32-1-i +diff --git a/fail2ban/server/jail.py b/fail2ban/server/jail.py +index ce9968a8..5fa5ef10 100644 +--- a/fail2ban/server/jail.py ++++ b/fail2ban/server/jail.py +@@ -26,7 +26,7 @@ __license__ = "GPL" + import logging + import math + import random +-import Queue ++import queue + + from .actions import Actions + from ..helpers import getLogger, _as_bool, extractOptions, MyTime +@@ -76,7 +76,7 @@ class Jail(object): + "might not function correctly. Please shorten" + % name) + self.__name = name +- self.__queue = Queue.Queue() ++ self.__queue = queue.Queue() + self.__filter = None + # Extra parameters for increase ban time + self._banExtra = {}; +@@ -127,25 +127,25 @@ class Jail(object): + "Failed to initialize any backend for Jail %r" % self.name) + + def _initPolling(self, **kwargs): +- from filterpoll import FilterPoll ++ from .filterpoll import FilterPoll + logSys.info("Jail '%s' uses poller %r" % (self.name, kwargs)) + self.__filter = FilterPoll(self, **kwargs) + + def _initGamin(self, **kwargs): + # Try to import gamin +- from filtergamin import FilterGamin ++ from .filtergamin import FilterGamin + logSys.info("Jail '%s' uses Gamin %r" % (self.name, kwargs)) + self.__filter = FilterGamin(self, **kwargs) + + def _initPyinotify(self, **kwargs): + # Try to import pyinotify +- from filterpyinotify import FilterPyinotify ++ from .filterpyinotify import FilterPyinotify + logSys.info("Jail '%s' uses pyinotify %r" % (self.name, kwargs)) + self.__filter = FilterPyinotify(self, **kwargs) + + def _initSystemd(self, **kwargs): # pragma: systemd no cover + # Try to import systemd +- from filtersystemd import FilterSystemd ++ from .filtersystemd import FilterSystemd + logSys.info("Jail '%s' uses systemd %r" % (self.name, kwargs)) + self.__filter = FilterSystemd(self, **kwargs) + +@@ -213,7 +213,7 @@ class Jail(object): + try: + ticket = self.__queue.get(False) + return ticket +- except Queue.Empty: ++ except queue.Empty: + return False + + def setBanTimeExtra(self, opt, value): +diff --git a/fail2ban/server/mytime.py b/fail2ban/server/mytime.py +index 98b69bd4..24bba5cf 100644 +--- a/fail2ban/server/mytime.py ++++ b/fail2ban/server/mytime.py +@@ -162,7 +162,7 @@ class MyTime: + + @returns number (calculated seconds from expression "val") + """ +- if isinstance(val, (int, long, float, complex)): ++ if isinstance(val, (int, float, complex)): + return val + # replace together standing abbreviations, example '1d12h' -> '1d 12h': + val = MyTime._str2sec_prep.sub(r" \1", val) +diff --git a/fail2ban/server/server.py b/fail2ban/server/server.py +index 159f6506..fc948e8c 100644 +--- a/fail2ban/server/server.py ++++ b/fail2ban/server/server.py +@@ -97,7 +97,7 @@ class Server: + + def start(self, sock, pidfile, force=False, observer=True, conf={}): + # First set the mask to only allow access to owner +- os.umask(0077) ++ os.umask(0o077) + # Second daemonize before logging etc, because it will close all handles: + if self.__daemon: # pragma: no cover + logSys.info("Starting in daemon mode") +@@ -190,7 +190,7 @@ class Server: + + # Restore default signal handlers: + if _thread_name() == '_MainThread': +- for s, sh in self.__prev_signals.iteritems(): ++ for s, sh in self.__prev_signals.items(): + signal.signal(s, sh) + + # Give observer a small chance to complete its work before exit +@@ -268,10 +268,10 @@ class Server: + logSys.info("Stopping all jails") + with self.__lock: + # 1st stop all jails (signal and stop actions/filter thread): +- for name in self.__jails.keys(): ++ for name in list(self.__jails.keys()): + self.delJail(name, stop=True, join=False) + # 2nd wait for end and delete jails: +- for name in self.__jails.keys(): ++ for name in list(self.__jails.keys()): + self.delJail(name, stop=False, join=True) + + def reloadJails(self, name, opts, begin): +@@ -302,7 +302,7 @@ class Server: + if "--restart" in opts: + self.stopAllJail() + # first set all affected jail(s) to idle and reset filter regex and other lists/dicts: +- for jn, jail in self.__jails.iteritems(): ++ for jn, jail in self.__jails.items(): + if name == '--all' or jn == name: + jail.idle = True + self.__reload_state[jn] = jail +@@ -313,7 +313,7 @@ class Server: + # end reload, all affected (or new) jails have already all new parameters (via stream) and (re)started: + with self.__lock: + deljails = [] +- for jn, jail in self.__jails.iteritems(): ++ for jn, jail in self.__jails.items(): + # still in reload state: + if jn in self.__reload_state: + # remove jails that are not reloaded (untouched, so not in new configuration) +@@ -513,7 +513,7 @@ class Server: + jails = [self.__jails[name]] + else: + # in all jails: +- jails = self.__jails.values() ++ jails = list(self.__jails.values()) + # unban given or all (if value is None): + cnt = 0 + ifexists |= (name is None) +@@ -551,7 +551,7 @@ class Server: + def isAlive(self, jailnum=None): + if jailnum is not None and len(self.__jails) != jailnum: + return 0 +- for jail in self.__jails.values(): ++ for jail in list(self.__jails.values()): + if not jail.isAlive(): + return 0 + return 1 +@@ -759,7 +759,7 @@ class Server: + return "flushed" + + def setThreadOptions(self, value): +- for o, v in value.iteritems(): ++ for o, v in value.items(): + if o == 'stacksize': + threading.stack_size(int(v)*1024) + else: # pragma: no cover +diff --git a/fail2ban/server/strptime.py b/fail2ban/server/strptime.py +index 498d284b..a5579fdc 100644 +--- a/fail2ban/server/strptime.py ++++ b/fail2ban/server/strptime.py +@@ -79,7 +79,7 @@ timeRE['ExY'] = r"(?P%s\d)" % _getYearCentRE(cent=(0,3), distance=3) + timeRE['Exy'] = r"(?P%s\d)" % _getYearCentRE(cent=(2,3), distance=3) + + def getTimePatternRE(): +- keys = timeRE.keys() ++ keys = list(timeRE.keys()) + patt = (r"%%(%%|%s|[%s])" % ( + "|".join([k for k in keys if len(k) > 1]), + "".join([k for k in keys if len(k) == 1]), +@@ -134,7 +134,7 @@ def zone2offset(tz, dt): + """ + if isinstance(tz, int): + return tz +- if isinstance(tz, basestring): ++ if isinstance(tz, str): + return validateTimeZone(tz) + tz, tzo = tz + if tzo is None or tzo == '': # without offset +@@ -171,7 +171,7 @@ def reGroupDictStrptime(found_dict, msec=False, default_tz=None): + year = month = day = hour = minute = tzoffset = \ + weekday = julian = week_of_year = None + second = fraction = 0 +- for key, val in found_dict.iteritems(): ++ for key, val in found_dict.items(): + if val is None: continue + # Directives not explicitly handled below: + # c, x, X +diff --git a/fail2ban/server/ticket.py b/fail2ban/server/ticket.py +index f67e0d23..f0b727c2 100644 +--- a/fail2ban/server/ticket.py ++++ b/fail2ban/server/ticket.py +@@ -55,7 +55,7 @@ class Ticket(object): + self._time = time if time is not None else MyTime.time() + self._data = {'matches': matches or [], 'failures': 0} + if data is not None: +- for k,v in data.iteritems(): ++ for k,v in data.items(): + if v is not None: + self._data[k] = v + if ticket: +@@ -89,7 +89,7 @@ class Ticket(object): + + def setIP(self, value): + # guarantee using IPAddr instead of unicode, str for the IP +- if isinstance(value, basestring): ++ if isinstance(value, str): + value = IPAddr(value) + self._ip = value + +@@ -181,7 +181,7 @@ class Ticket(object): + if len(args) == 1: + # todo: if support >= 2.7 only: + # self._data = {k:v for k,v in args[0].iteritems() if v is not None} +- self._data = dict([(k,v) for k,v in args[0].iteritems() if v is not None]) ++ self._data = dict([(k,v) for k,v in args[0].items() if v is not None]) + # add k,v list or dict (merge): + elif len(args) == 2: + self._data.update((args,)) +@@ -192,7 +192,7 @@ class Ticket(object): + # filter (delete) None values: + # todo: if support >= 2.7 only: + # self._data = {k:v for k,v in self._data.iteritems() if v is not None} +- self._data = dict([(k,v) for k,v in self._data.iteritems() if v is not None]) ++ self._data = dict([(k,v) for k,v in self._data.items() if v is not None]) + + def getData(self, key=None, default=None): + # return whole data dict: +@@ -201,17 +201,17 @@ class Ticket(object): + # return default if not exists: + if not self._data: + return default +- if not isinstance(key,(str,unicode,type(None),int,float,bool,complex)): ++ if not isinstance(key,(str,type(None),int,float,bool,complex)): + # return filtered by lambda/function: + if callable(key): + # todo: if support >= 2.7 only: + # return {k:v for k,v in self._data.iteritems() if key(k)} +- return dict([(k,v) for k,v in self._data.iteritems() if key(k)]) ++ return dict([(k,v) for k,v in self._data.items() if key(k)]) + # return filtered by keys: + if hasattr(key, '__iter__'): + # todo: if support >= 2.7 only: + # return {k:v for k,v in self._data.iteritems() if k in key} +- return dict([(k,v) for k,v in self._data.iteritems() if k in key]) ++ return dict([(k,v) for k,v in self._data.items() if k in key]) + # return single value of data: + return self._data.get(key, default) + +diff --git a/fail2ban/server/transmitter.py b/fail2ban/server/transmitter.py +index f83e9d5f..80726cb4 100644 +--- a/fail2ban/server/transmitter.py ++++ b/fail2ban/server/transmitter.py +@@ -475,7 +475,7 @@ class Transmitter: + opt = command[1][len("bantime."):] + return self.__server.getBanTimeExtra(name, opt) + elif command[1] == "actions": +- return self.__server.getActions(name).keys() ++ return list(self.__server.getActions(name).keys()) + elif command[1] == "action": + actionname = command[2] + actionvalue = command[3] +diff --git a/fail2ban/server/utils.py b/fail2ban/server/utils.py +index d4461a7d..13c24e76 100644 +--- a/fail2ban/server/utils.py ++++ b/fail2ban/server/utils.py +@@ -57,7 +57,7 @@ _RETCODE_HINTS = { + + # Dictionary to lookup signal name from number + signame = dict((num, name) +- for name, num in signal.__dict__.iteritems() if name.startswith("SIG")) ++ for name, num in signal.__dict__.items() if name.startswith("SIG")) + + class Utils(): + """Utilities provide diverse static methods like executes OS shell commands, etc. +@@ -109,7 +109,7 @@ class Utils(): + break + else: # pragma: 3.x no cover (dict is in 2.6 only) + remlst = [] +- for (ck, cv) in cache.iteritems(): ++ for (ck, cv) in cache.items(): + # if expired: + if cv[1] <= t: + remlst.append(ck) +@@ -152,7 +152,7 @@ class Utils(): + if not isinstance(realCmd, list): + realCmd = [realCmd] + i = len(realCmd)-1 +- for k, v in varsDict.iteritems(): ++ for k, v in varsDict.items(): + varsStat += "%s=$%s " % (k, i) + realCmd.append(v) + i += 1 +diff --git a/fail2ban/tests/action_d/test_badips.py b/fail2ban/tests/action_d/test_badips.py +index 013c0fdb..3c35e4d7 100644 +--- a/fail2ban/tests/action_d/test_badips.py ++++ b/fail2ban/tests/action_d/test_badips.py +@@ -32,7 +32,7 @@ from ..utils import LogCaptureTestCase, CONFIG_DIR + if sys.version_info >= (3, ): # pragma: 2.x no cover + from urllib.error import HTTPError, URLError + else: # pragma: 3.x no cover +- from urllib2 import HTTPError, URLError ++ from urllib.error import HTTPError, URLError + + def skip_if_not_available(f): + """Helper to decorate tests to skip in case of timeout/http-errors like "502 bad gateway". +diff --git a/fail2ban/tests/actiontestcase.py b/fail2ban/tests/actiontestcase.py +index 1a00c040..ecd09246 100644 +--- a/fail2ban/tests/actiontestcase.py ++++ b/fail2ban/tests/actiontestcase.py +@@ -244,14 +244,14 @@ class CommandActionTest(LogCaptureTestCase): + setattr(self.__action, 'ab', "") + setattr(self.__action, 'x?family=inet6', "") + # produce self-referencing properties except: +- self.assertRaisesRegexp(ValueError, r"properties contain self referencing definitions", ++ self.assertRaisesRegex(ValueError, r"properties contain self referencing definitions", + lambda: self.__action.replaceTag("", + self.__action._properties, conditional="family=inet4") + ) + # remore self-referencing in props: + delattr(self.__action, 'ac') + # produce self-referencing query except: +- self.assertRaisesRegexp(ValueError, r"possible self referencing definitions in query", ++ self.assertRaisesRegex(ValueError, r"possible self referencing definitions in query", + lambda: self.__action.replaceTag(">>>>>>>>>>>>>>>>>>>>", + self.__action._properties, conditional="family=inet6") + ) +diff --git a/fail2ban/tests/clientreadertestcase.py b/fail2ban/tests/clientreadertestcase.py +index 2c1d0a0e..aa7908c4 100644 +--- a/fail2ban/tests/clientreadertestcase.py ++++ b/fail2ban/tests/clientreadertestcase.py +@@ -390,7 +390,7 @@ class JailReaderTest(LogCaptureTestCase): + # And multiple groups (`][` instead of `,`) + result = extractOptions(option.replace(',', '][')) + expected2 = (expected[0], +- dict((k, v.replace(',', '][')) for k, v in expected[1].iteritems()) ++ dict((k, v.replace(',', '][')) for k, v in expected[1].items()) + ) + self.assertEqual(expected2, result) + +@@ -975,7 +975,7 @@ filter = testfilter1 + self.assertEqual(add_actions[-1][-1], "{}") + + def testLogPathFileFilterBackend(self): +- self.assertRaisesRegexp(ValueError, r"Have not found any log file for .* jail", ++ self.assertRaisesRegex(ValueError, r"Have not found any log file for .* jail", + self._testLogPath, backend='polling') + + def testLogPathSystemdBackend(self): +diff --git a/fail2ban/tests/databasetestcase.py b/fail2ban/tests/databasetestcase.py +index 9a5e9fa1..562461a6 100644 +--- a/fail2ban/tests/databasetestcase.py ++++ b/fail2ban/tests/databasetestcase.py +@@ -67,7 +67,7 @@ class DatabaseTest(LogCaptureTestCase): + + @property + def db(self): +- if isinstance(self._db, basestring) and self._db == ':auto-create-in-memory:': ++ if isinstance(self._db, str) and self._db == ':auto-create-in-memory:': + self._db = getFail2BanDb(self.dbFilename) + return self._db + @db.setter +@@ -159,7 +159,7 @@ class DatabaseTest(LogCaptureTestCase): + self.db = Fail2BanDb(self.dbFilename) + self.assertEqual(self.db.getJailNames(), set(['DummyJail #29162448 with 0 tickets'])) + self.assertEqual(self.db.getLogPaths(), set(['/tmp/Fail2BanDb_pUlZJh.log'])) +- ticket = FailTicket("127.0.0.1", 1388009242.26, [u"abc\n"]) ++ ticket = FailTicket("127.0.0.1", 1388009242.26, ["abc\n"]) + self.assertEqual(self.db.getBans()[0], ticket) + + self.assertEqual(self.db.updateDb(Fail2BanDb.__version__), Fail2BanDb.__version__) +@@ -185,9 +185,9 @@ class DatabaseTest(LogCaptureTestCase): + self.assertEqual(len(bans), 2) + # compare first ticket completely: + ticket = FailTicket("1.2.3.7", 1417595494, [ +- u'Dec 3 09:31:08 f2btest test:auth[27658]: pam_unix(test:auth): authentication failure; logname= uid=0 euid=0 tty=test ruser= rhost=1.2.3.7', +- u'Dec 3 09:31:32 f2btest test:auth[27671]: pam_unix(test:auth): authentication failure; logname= uid=0 euid=0 tty=test ruser= rhost=1.2.3.7', +- u'Dec 3 09:31:34 f2btest test:auth[27673]: pam_unix(test:auth): authentication failure; logname= uid=0 euid=0 tty=test ruser= rhost=1.2.3.7' ++ 'Dec 3 09:31:08 f2btest test:auth[27658]: pam_unix(test:auth): authentication failure; logname= uid=0 euid=0 tty=test ruser= rhost=1.2.3.7', ++ 'Dec 3 09:31:32 f2btest test:auth[27671]: pam_unix(test:auth): authentication failure; logname= uid=0 euid=0 tty=test ruser= rhost=1.2.3.7', ++ 'Dec 3 09:31:34 f2btest test:auth[27673]: pam_unix(test:auth): authentication failure; logname= uid=0 euid=0 tty=test ruser= rhost=1.2.3.7' + ]) + ticket.setAttempt(3) + self.assertEqual(bans[0], ticket) +@@ -286,11 +286,11 @@ class DatabaseTest(LogCaptureTestCase): + # invalid + valid, invalid + valid unicode, invalid + valid dual converted (like in filter:readline by fallback) ... + tickets = [ + FailTicket("127.0.0.1", 0, ['user "test"', 'user "\xd1\xe2\xe5\xf2\xe0"', 'user "\xc3\xa4\xc3\xb6\xc3\xbc\xc3\x9f"']), +- FailTicket("127.0.0.2", 0, ['user "test"', u'user "\xd1\xe2\xe5\xf2\xe0"', u'user "\xc3\xa4\xc3\xb6\xc3\xbc\xc3\x9f"']), ++ FailTicket("127.0.0.2", 0, ['user "test"', 'user "\xd1\xe2\xe5\xf2\xe0"', 'user "\xc3\xa4\xc3\xb6\xc3\xbc\xc3\x9f"']), + FailTicket("127.0.0.3", 0, ['user "test"', b'user "\xd1\xe2\xe5\xf2\xe0"', b'user "\xc3\xa4\xc3\xb6\xc3\xbc\xc3\x9f"']), +- FailTicket("127.0.0.4", 0, ['user "test"', 'user "\xd1\xe2\xe5\xf2\xe0"', u'user "\xe4\xf6\xfc\xdf"']), ++ FailTicket("127.0.0.4", 0, ['user "test"', 'user "\xd1\xe2\xe5\xf2\xe0"', 'user "\xe4\xf6\xfc\xdf"']), + FailTicket("127.0.0.5", 0, ['user "test"', 'unterminated \xcf']), +- FailTicket("127.0.0.6", 0, ['user "test"', u'unterminated \xcf']), ++ FailTicket("127.0.0.6", 0, ['user "test"', 'unterminated \xcf']), + FailTicket("127.0.0.7", 0, ['user "test"', b'unterminated \xcf']) + ] + for ticket in tickets: +diff --git a/fail2ban/tests/datedetectortestcase.py b/fail2ban/tests/datedetectortestcase.py +index 458f76ef..49ada60d 100644 +--- a/fail2ban/tests/datedetectortestcase.py ++++ b/fail2ban/tests/datedetectortestcase.py +@@ -279,7 +279,7 @@ class DateDetectorTest(LogCaptureTestCase): + self.assertEqual(logTime, mu) + self.assertEqual(logMatch.group(1), '2012/10/11 02:37:17') + # confuse it with year being at the end +- for i in xrange(10): ++ for i in range(10): + ( logTime, logMatch ) = self.datedetector.getTime('11/10/2012 02:37:17 [error] 18434#0') + self.assertEqual(logTime, mu) + self.assertEqual(logMatch.group(1), '11/10/2012 02:37:17') +@@ -505,7 +505,7 @@ class CustomDateFormatsTest(unittest.TestCase): + date = dd.getTime(line) + if matched: + self.assertTrue(date) +- if isinstance(matched, basestring): ++ if isinstance(matched, str): + self.assertEqual(matched, date[1].group(1)) + else: + self.assertEqual(matched, date[0]) +@@ -537,7 +537,7 @@ class CustomDateFormatsTest(unittest.TestCase): + date = dd.getTime(line) + if matched: + self.assertTrue(date) +- if isinstance(matched, basestring): # pragma: no cover ++ if isinstance(matched, str): # pragma: no cover + self.assertEqual(matched, date[1].group(1)) + else: + self.assertEqual(matched, date[0]) +diff --git a/fail2ban/tests/fail2banclienttestcase.py b/fail2ban/tests/fail2banclienttestcase.py +index 95f73ed3..bba354fa 100644 +--- a/fail2ban/tests/fail2banclienttestcase.py ++++ b/fail2ban/tests/fail2banclienttestcase.py +@@ -367,10 +367,10 @@ def with_foreground_server_thread(startextra={}): + # several commands to server in body of decorated function: + return f(self, tmp, startparams, *args, **kwargs) + except Exception as e: # pragma: no cover +- print('=== Catch an exception: %s' % e) ++ print(('=== Catch an exception: %s' % e)) + log = self.getLog() + if log: +- print('=== Error of server, log: ===\n%s===' % log) ++ print(('=== Error of server, log: ===\n%s===' % log)) + self.pruneLog() + raise + finally: +@@ -440,7 +440,7 @@ class Fail2banClientServerBase(LogCaptureTestCase): + ) + except: # pragma: no cover + if _inherited_log(startparams): +- print('=== Error by wait fot server, log: ===\n%s===' % self.getLog()) ++ print(('=== Error by wait fot server, log: ===\n%s===' % self.getLog())) + self.pruneLog() + log = pjoin(tmp, "f2b.log") + if isfile(log): +@@ -1610,6 +1610,6 @@ class Fail2banServerTest(Fail2banClientServerBase): + self.stopAndWaitForServerEnd(SUCCESS) + + def testServerStartStop(self): +- for i in xrange(2000): ++ for i in range(2000): + self._testServerStartStop() + +diff --git a/fail2ban/tests/failmanagertestcase.py b/fail2ban/tests/failmanagertestcase.py +index a5425286..2a94cc82 100644 +--- a/fail2ban/tests/failmanagertestcase.py ++++ b/fail2ban/tests/failmanagertestcase.py +@@ -45,11 +45,11 @@ class AddFailure(unittest.TestCase): + super(AddFailure, self).tearDown() + + def _addDefItems(self): +- self.__items = [[u'193.168.0.128', 1167605999.0], +- [u'193.168.0.128', 1167605999.0], +- [u'193.168.0.128', 1167605999.0], +- [u'193.168.0.128', 1167605999.0], +- [u'193.168.0.128', 1167605999.0], ++ self.__items = [['193.168.0.128', 1167605999.0], ++ ['193.168.0.128', 1167605999.0], ++ ['193.168.0.128', 1167605999.0], ++ ['193.168.0.128', 1167605999.0], ++ ['193.168.0.128', 1167605999.0], + ['87.142.124.10', 1167605999.0], + ['87.142.124.10', 1167605999.0], + ['87.142.124.10', 1167605999.0], +diff --git a/fail2ban/tests/files/config/apache-auth/digest.py b/fail2ban/tests/files/config/apache-auth/digest.py +index 03588594..e2297ab3 100755 +--- a/fail2ban/tests/files/config/apache-auth/digest.py ++++ b/fail2ban/tests/files/config/apache-auth/digest.py +@@ -41,7 +41,7 @@ def auth(v): + response="%s" + """ % ( username, algorithm, realm, url, nonce, qop, response ) + # opaque="%s", +- print(p.method, p.url, p.headers) ++ print((p.method, p.url, p.headers)) + s = requests.Session() + return s.send(p) + +@@ -76,18 +76,18 @@ r = auth(v) + + # [Sun Jul 28 21:41:20 2013] [error] [client 127.0.0.1] Digest: unknown algorithm `super funky chicken' received: /digest/ + +-print(r.status_code,r.headers, r.text) ++print((r.status_code,r.headers, r.text)) + v['algorithm'] = algorithm + + + r = auth(v) +-print(r.status_code,r.headers, r.text) ++print((r.status_code,r.headers, r.text)) + + nonce = v['nonce'] + v['nonce']=v['nonce'][5:-5] + + r = auth(v) +-print(r.status_code,r.headers, r.text) ++print((r.status_code,r.headers, r.text)) + + # [Sun Jul 28 21:05:31.178340 2013] [auth_digest:error] [pid 24224:tid 139895539455744] [client 127.0.0.1:56906] AH01793: invalid qop `auth' received: /digest/qop_none/ + +@@ -95,7 +95,7 @@ print(r.status_code,r.headers, r.text) + v['nonce']=nonce[0:11] + 'ZZZ' + nonce[14:] + + r = auth(v) +-print(r.status_code,r.headers, r.text) ++print((r.status_code,r.headers, r.text)) + + #[Sun Jul 28 21:18:11.769228 2013] [auth_digest:error] [pid 24752:tid 139895505884928] [client 127.0.0.1:56964] AH01776: invalid nonce b9YAiJDiBAZZZ1b1abe02d20063ea3b16b544ea1b0d981c1bafe received - hash is not d42d824dee7aaf50c3ba0a7c6290bd453e3dd35b + +@@ -107,7 +107,7 @@ import time + time.sleep(1) + + r = auth(v) +-print(r.status_code,r.headers, r.text) ++print((r.status_code,r.headers, r.text)) + + # Obtained by putting the following code in modules/aaa/mod_auth_digest.c + # in the function initialize_secret +@@ -137,7 +137,7 @@ s = sha.sha(apachesecret) + + v=preauth() + +-print(v['nonce']) ++print((v['nonce'])) + realm = v['Digest realm'][1:-1] + + (t,) = struct.unpack('l',base64.b64decode(v['nonce'][1:13])) +@@ -156,13 +156,13 @@ print(v) + + r = auth(v) + #[Mon Jul 29 02:12:55.539813 2013] [auth_digest:error] [pid 9647:tid 139895522670336] [client 127.0.0.1:58474] AH01777: invalid nonce 59QJppTiBAA=b08983fd166ade9840407df1b0f75b9e6e07d88d received - user attempted time travel +-print(r.status_code,r.headers, r.text) ++print((r.status_code,r.headers, r.text)) + + url='/digest_onetime/' + v=preauth() + + # Need opaque header handling in auth + r = auth(v) +-print(r.status_code,r.headers, r.text) ++print((r.status_code,r.headers, r.text)) + r = auth(v) +-print(r.status_code,r.headers, r.text) ++print((r.status_code,r.headers, r.text)) +diff --git a/fail2ban/tests/filtertestcase.py b/fail2ban/tests/filtertestcase.py +index 35785a58..8eeb6902 100644 +--- a/fail2ban/tests/filtertestcase.py ++++ b/fail2ban/tests/filtertestcase.py +@@ -22,7 +22,7 @@ + __copyright__ = "Copyright (c) 2004 Cyril Jaquier; 2012 Yaroslav Halchenko" + __license__ = "GPL" + +-from __builtin__ import open as fopen ++from builtins import open as fopen + import unittest + import os + import re +@@ -204,7 +204,7 @@ def _copy_lines_between_files(in_, fout, n=None, skip=0, mode='a', terminal_line + else: + fin = in_ + # Skip +- for i in xrange(skip): ++ for i in range(skip): + fin.readline() + # Read + i = 0 +@@ -244,7 +244,7 @@ def _copy_lines_to_journal(in_, fields={},n=None, skip=0, terminal_line=""): # p + # Required for filtering + fields.update(TEST_JOURNAL_FIELDS) + # Skip +- for i in xrange(skip): ++ for i in range(skip): + fin.readline() + # Read/Write + i = 0 +@@ -306,18 +306,18 @@ class BasicFilter(unittest.TestCase): + def testTest_tm(self): + unittest.F2B.SkipIfFast() + ## test function "_tm" works correct (returns the same as slow strftime): +- for i in xrange(1417512352, (1417512352 // 3600 + 3) * 3600): ++ for i in range(1417512352, (1417512352 // 3600 + 3) * 3600): + tm = MyTime.time2str(i) + if _tm(i) != tm: # pragma: no cover - never reachable + self.assertEqual((_tm(i), i), (tm, i)) + + def testWrongCharInTupleLine(self): + ## line tuple has different types (ascii after ascii / unicode): +- for a1 in ('', u'', b''): +- for a2 in ('2016-09-05T20:18:56', u'2016-09-05T20:18:56', b'2016-09-05T20:18:56'): ++ for a1 in ('', '', b''): ++ for a2 in ('2016-09-05T20:18:56', '2016-09-05T20:18:56', b'2016-09-05T20:18:56'): + for a3 in ( + 'Fail for "g\xc3\xb6ran" from 192.0.2.1', +- u'Fail for "g\xc3\xb6ran" from 192.0.2.1', ++ 'Fail for "g\xc3\xb6ran" from 192.0.2.1', + b'Fail for "g\xc3\xb6ran" from 192.0.2.1' + ): + # join should work if all arguments have the same type: +@@ -435,7 +435,7 @@ class IgnoreIP(LogCaptureTestCase): + + def testAddAttempt(self): + self.filter.setMaxRetry(3) +- for i in xrange(1, 1+3): ++ for i in range(1, 1+3): + self.filter.addAttempt('192.0.2.1') + self.assertLogged('Attempt 192.0.2.1', '192.0.2.1:%d' % i, all=True, wait=True) + self.jail.actions._Actions__checkBan() +@@ -472,7 +472,7 @@ class IgnoreIP(LogCaptureTestCase): + # like both test-cases above, just cached (so once per key)... + self.filter.ignoreCache = {"key":""} + self.filter.ignoreCommand = 'if [ "" = "10.0.0.1" ]; then exit 0; fi; exit 1' +- for i in xrange(5): ++ for i in range(5): + self.pruneLog() + self.assertTrue(self.filter.inIgnoreIPList("10.0.0.1")) + self.assertFalse(self.filter.inIgnoreIPList("10.0.0.0")) +@@ -483,7 +483,7 @@ class IgnoreIP(LogCaptureTestCase): + # by host of IP: + self.filter.ignoreCache = {"key":""} + self.filter.ignoreCommand = 'if [ "" = "test-host" ]; then exit 0; fi; exit 1' +- for i in xrange(5): ++ for i in range(5): + self.pruneLog() + self.assertTrue(self.filter.inIgnoreIPList(FailTicket("2001:db8::1"))) + self.assertFalse(self.filter.inIgnoreIPList(FailTicket("2001:db8::ffff"))) +@@ -495,7 +495,7 @@ class IgnoreIP(LogCaptureTestCase): + self.filter.ignoreCache = {"key":"", "max-count":"10", "max-time":"1h"} + self.assertEqual(self.filter.ignoreCache, ["", 10, 60*60]) + self.filter.ignoreCommand = 'if [ "" = "tester" ]; then exit 0; fi; exit 1' +- for i in xrange(5): ++ for i in range(5): + self.pruneLog() + self.assertTrue(self.filter.inIgnoreIPList(FailTicket("tester", data={'user': 'tester'}))) + self.assertFalse(self.filter.inIgnoreIPList(FailTicket("root", data={'user': 'root'}))) +@@ -644,7 +644,7 @@ class LogFileFilterPoll(unittest.TestCase): + fc = FileContainer(fname, self.filter.getLogEncoding()) + fc.open() + # no time - nothing should be found : +- for i in xrange(10): ++ for i in range(10): + f.write("[sshd] error: PAM: failure len 1\n") + f.flush() + fc.setPos(0); self.filter.seekToTime(fc, time) +@@ -718,14 +718,14 @@ class LogFileFilterPoll(unittest.TestCase): + # variable length of file (ca 45K or 450K before and hereafter): + # write lines with smaller as search time: + t = time - count - 1 +- for i in xrange(count): ++ for i in range(count): + f.write("%s [sshd] error: PAM: failure\n" % _tm(t)) + t += 1 + f.flush() + fc.setPos(0); self.filter.seekToTime(fc, time) + self.assertEqual(fc.getPos(), 47*count) + # write lines with exact search time: +- for i in xrange(10): ++ for i in range(10): + f.write("%s [sshd] error: PAM: failure\n" % _tm(time)) + f.flush() + fc.setPos(0); self.filter.seekToTime(fc, time) +@@ -734,8 +734,8 @@ class LogFileFilterPoll(unittest.TestCase): + self.assertEqual(fc.getPos(), 47*count) + # write lines with greater as search time: + t = time+1 +- for i in xrange(count//500): +- for j in xrange(500): ++ for i in range(count//500): ++ for j in range(500): + f.write("%s [sshd] error: PAM: failure\n" % _tm(t)) + t += 1 + f.flush() +@@ -1488,10 +1488,10 @@ def get_monitor_failures_journal_testcase(Filter_): # pragma: systemd no cover + # Add direct utf, unicode, blob: + for l in ( + "error: PAM: Authentication failure for \xe4\xf6\xfc\xdf from 192.0.2.1", +- u"error: PAM: Authentication failure for \xe4\xf6\xfc\xdf from 192.0.2.1", ++ "error: PAM: Authentication failure for \xe4\xf6\xfc\xdf from 192.0.2.1", + b"error: PAM: Authentication failure for \xe4\xf6\xfc\xdf from 192.0.2.1".decode('utf-8', 'replace'), + "error: PAM: Authentication failure for \xc3\xa4\xc3\xb6\xc3\xbc\xc3\x9f from 192.0.2.2", +- u"error: PAM: Authentication failure for \xc3\xa4\xc3\xb6\xc3\xbc\xc3\x9f from 192.0.2.2", ++ "error: PAM: Authentication failure for \xc3\xa4\xc3\xb6\xc3\xbc\xc3\x9f from 192.0.2.2", + b"error: PAM: Authentication failure for \xc3\xa4\xc3\xb6\xc3\xbc\xc3\x9f from 192.0.2.2".decode('utf-8', 'replace') + ): + fields = self.journal_fields +@@ -1520,7 +1520,7 @@ class GetFailures(LogCaptureTestCase): + + # so that they could be reused by other tests + FAILURES_01 = ('193.168.0.128', 3, 1124013599.0, +- [u'Aug 14 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128']*3) ++ ['Aug 14 11:59:59 [sshd] error: PAM: Authentication failure for kevin from 193.168.0.128']*3) + + def setUp(self): + """Call before every test case.""" +@@ -1595,8 +1595,8 @@ class GetFailures(LogCaptureTestCase): + + def testGetFailures02(self): + output = ('141.3.81.106', 4, 1124013539.0, +- [u'Aug 14 11:%d:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:141.3.81.106 port 51332 ssh2' +- % m for m in 53, 54, 57, 58]) ++ ['Aug 14 11:%d:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:141.3.81.106 port 51332 ssh2' ++ % m for m in (53, 54, 57, 58)]) + + self.filter.addLogPath(GetFailures.FILENAME_02, autoSeek=0) + self.filter.addFailRegex(r"Failed .* from ") +@@ -1691,17 +1691,17 @@ class GetFailures(LogCaptureTestCase): + # We should still catch failures with usedns = no ;-) + output_yes = ( + ('93.184.216.34', 2, 1124013539.0, +- [u'Aug 14 11:54:59 i60p295 sshd[12365]: Failed publickey for roehl from example.com port 51332 ssh2', +- u'Aug 14 11:58:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:93.184.216.34 port 51332 ssh2'] ++ ['Aug 14 11:54:59 i60p295 sshd[12365]: Failed publickey for roehl from example.com port 51332 ssh2', ++ 'Aug 14 11:58:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:93.184.216.34 port 51332 ssh2'] + ), + ('2606:2800:220:1:248:1893:25c8:1946', 1, 1124013299.0, +- [u'Aug 14 11:54:59 i60p295 sshd[12365]: Failed publickey for roehl from example.com port 51332 ssh2'] ++ ['Aug 14 11:54:59 i60p295 sshd[12365]: Failed publickey for roehl from example.com port 51332 ssh2'] + ), + ) + + output_no = ( + ('93.184.216.34', 1, 1124013539.0, +- [u'Aug 14 11:58:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:93.184.216.34 port 51332 ssh2'] ++ ['Aug 14 11:58:59 i60p295 sshd[12365]: Failed publickey for roehl from ::ffff:93.184.216.34 port 51332 ssh2'] + ) + ) + +@@ -1807,9 +1807,9 @@ class DNSUtilsTests(unittest.TestCase): + self.assertTrue(c.get('a') is None) + self.assertEqual(c.get('a', 'test'), 'test') + # exact 5 elements : +- for i in xrange(5): ++ for i in range(5): + c.set(i, i) +- for i in xrange(5): ++ for i in range(5): + self.assertEqual(c.get(i), i) + # remove unavailable key: + c.unset('a'); c.unset('a') +@@ -1817,30 +1817,30 @@ class DNSUtilsTests(unittest.TestCase): + def testCacheMaxSize(self): + c = Utils.Cache(maxCount=5, maxTime=60) + # exact 5 elements : +- for i in xrange(5): ++ for i in range(5): + c.set(i, i) +- self.assertEqual([c.get(i) for i in xrange(5)], [i for i in xrange(5)]) +- self.assertNotIn(-1, (c.get(i, -1) for i in xrange(5))) ++ self.assertEqual([c.get(i) for i in range(5)], [i for i in range(5)]) ++ self.assertNotIn(-1, (c.get(i, -1) for i in range(5))) + # add one - too many: + c.set(10, i) + # one element should be removed : +- self.assertIn(-1, (c.get(i, -1) for i in xrange(5))) ++ self.assertIn(-1, (c.get(i, -1) for i in range(5))) + # test max size (not expired): +- for i in xrange(10): ++ for i in range(10): + c.set(i, 1) + self.assertEqual(len(c), 5) + + def testCacheMaxTime(self): + # test max time (expired, timeout reached) : + c = Utils.Cache(maxCount=5, maxTime=0.0005) +- for i in xrange(10): ++ for i in range(10): + c.set(i, 1) + st = time.time() + self.assertTrue(Utils.wait_for(lambda: time.time() >= st + 0.0005, 1)) + # we have still 5 elements (or fewer if too slow test mashine): + self.assertTrue(len(c) <= 5) + # but all that are expiered also: +- for i in xrange(10): ++ for i in range(10): + self.assertTrue(c.get(i) is None) + # here the whole cache should be empty: + self.assertEqual(len(c), 0) +@@ -1861,7 +1861,7 @@ class DNSUtilsTests(unittest.TestCase): + c = count + while c: + c -= 1 +- s = xrange(0, 256, 1) if forw else xrange(255, -1, -1) ++ s = range(0, 256, 1) if forw else range(255, -1, -1) + if random: shuffle([i for i in s]) + for i in s: + IPAddr('192.0.2.'+str(i), IPAddr.FAM_IPv4) +@@ -1983,15 +1983,15 @@ class DNSUtilsNetworkTests(unittest.TestCase): + + def testAddr2bin(self): + res = IPAddr('10.0.0.0') +- self.assertEqual(res.addr, 167772160L) ++ self.assertEqual(res.addr, 167772160) + res = IPAddr('10.0.0.0', cidr=None) +- self.assertEqual(res.addr, 167772160L) +- res = IPAddr('10.0.0.0', cidr=32L) +- self.assertEqual(res.addr, 167772160L) +- res = IPAddr('10.0.0.1', cidr=32L) +- self.assertEqual(res.addr, 167772161L) +- res = IPAddr('10.0.0.1', cidr=31L) +- self.assertEqual(res.addr, 167772160L) ++ self.assertEqual(res.addr, 167772160) ++ res = IPAddr('10.0.0.0', cidr=32) ++ self.assertEqual(res.addr, 167772160) ++ res = IPAddr('10.0.0.1', cidr=32) ++ self.assertEqual(res.addr, 167772161) ++ res = IPAddr('10.0.0.1', cidr=31) ++ self.assertEqual(res.addr, 167772160) + + self.assertEqual(IPAddr('10.0.0.0').hexdump, '0a000000') + self.assertEqual(IPAddr('1::2').hexdump, '00010000000000000000000000000002') +@@ -2067,9 +2067,9 @@ class DNSUtilsNetworkTests(unittest.TestCase): + '93.184.216.34': 'ip4-test', + '2606:2800:220:1:248:1893:25c8:1946': 'ip6-test' + } +- d2 = dict([(IPAddr(k), v) for k, v in d.iteritems()]) +- self.assertTrue(isinstance(d.keys()[0], basestring)) +- self.assertTrue(isinstance(d2.keys()[0], IPAddr)) ++ d2 = dict([(IPAddr(k), v) for k, v in d.items()]) ++ self.assertTrue(isinstance(list(d.keys())[0], str)) ++ self.assertTrue(isinstance(list(d2.keys())[0], IPAddr)) + self.assertEqual(d.get(ip4[2], ''), 'ip4-test') + self.assertEqual(d.get(ip6[2], ''), 'ip6-test') + self.assertEqual(d2.get(str(ip4[2]), ''), 'ip4-test') +diff --git a/fail2ban/tests/misctestcase.py b/fail2ban/tests/misctestcase.py +index 9b986f53..94f7a8de 100644 +--- a/fail2ban/tests/misctestcase.py ++++ b/fail2ban/tests/misctestcase.py +@@ -29,9 +29,9 @@ import tempfile + import shutil + import fnmatch + from glob import glob +-from StringIO import StringIO ++from io import StringIO + +-from utils import LogCaptureTestCase, logSys as DefLogSys ++from .utils import LogCaptureTestCase, logSys as DefLogSys + + from ..helpers import formatExceptionInfo, mbasename, TraceBack, FormatterWithTraceBack, getLogger, \ + splitwords, uni_decode, uni_string +@@ -67,7 +67,7 @@ class HelpersTest(unittest.TestCase): + self.assertEqual(splitwords(' 1\n 2'), ['1', '2']) + self.assertEqual(splitwords(' 1\n 2, 3'), ['1', '2', '3']) + # string as unicode: +- self.assertEqual(splitwords(u' 1\n 2, 3'), ['1', '2', '3']) ++ self.assertEqual(splitwords(' 1\n 2, 3'), ['1', '2', '3']) + + + if sys.version_info >= (2,7): +@@ -197,11 +197,11 @@ class TestsUtilsTest(LogCaptureTestCase): + + def testUniConverters(self): + self.assertRaises(Exception, uni_decode, +- (b'test' if sys.version_info >= (3,) else u'test'), 'f2b-test::non-existing-encoding') +- uni_decode((b'test\xcf' if sys.version_info >= (3,) else u'test\xcf')) ++ (b'test' if sys.version_info >= (3,) else 'test'), 'f2b-test::non-existing-encoding') ++ uni_decode((b'test\xcf' if sys.version_info >= (3,) else 'test\xcf')) + uni_string(b'test\xcf') + uni_string('test\xcf') +- uni_string(u'test\xcf') ++ uni_string('test\xcf') + + def testSafeLogging(self): + # logging should be exception-safe, to avoid possible errors (concat, str. conversion, representation failures, etc) +@@ -213,7 +213,7 @@ class TestsUtilsTest(LogCaptureTestCase): + if self.err: + raise Exception('no represenation for test!') + else: +- return u'conv-error (\xf2\xf0\xe5\xf2\xe8\xe9), unterminated utf \xcf' ++ return 'conv-error (\xf2\xf0\xe5\xf2\xe8\xe9), unterminated utf \xcf' + test = Test() + logSys.log(logging.NOTICE, "test 1a: %r", test) + self.assertLogged("Traceback", "no represenation for test!") +@@ -261,7 +261,7 @@ class TestsUtilsTest(LogCaptureTestCase): + func_raise() + + try: +- print deep_function(3) ++ print(deep_function(3)) + except ValueError: + s = tb() + +@@ -278,7 +278,7 @@ class TestsUtilsTest(LogCaptureTestCase): + self.assertIn(':', s) + + def _testAssertionErrorRE(self, regexp, fun, *args, **kwargs): +- self.assertRaisesRegexp(AssertionError, regexp, fun, *args, **kwargs) ++ self.assertRaisesRegex(AssertionError, regexp, fun, *args, **kwargs) + + def testExtendedAssertRaisesRE(self): + ## test _testAssertionErrorRE several fail cases: +@@ -316,13 +316,13 @@ class TestsUtilsTest(LogCaptureTestCase): + self._testAssertionErrorRE(r"'a' unexpectedly found in 'cba'", + self.assertNotIn, 'a', 'cba') + self._testAssertionErrorRE(r"1 unexpectedly found in \[0, 1, 2\]", +- self.assertNotIn, 1, xrange(3)) ++ self.assertNotIn, 1, range(3)) + self._testAssertionErrorRE(r"'A' unexpectedly found in \['C', 'A'\]", + self.assertNotIn, 'A', (c.upper() for c in 'cba' if c != 'b')) + self._testAssertionErrorRE(r"'a' was not found in 'xyz'", + self.assertIn, 'a', 'xyz') + self._testAssertionErrorRE(r"5 was not found in \[0, 1, 2\]", +- self.assertIn, 5, xrange(3)) ++ self.assertIn, 5, range(3)) + self._testAssertionErrorRE(r"'A' was not found in \['C', 'B'\]", + self.assertIn, 'A', (c.upper() for c in 'cba' if c != 'a')) + ## assertLogged, assertNotLogged positive case: +diff --git a/fail2ban/tests/observertestcase.py b/fail2ban/tests/observertestcase.py +index 8e944454..ed520286 100644 +--- a/fail2ban/tests/observertestcase.py ++++ b/fail2ban/tests/observertestcase.py +@@ -69,7 +69,7 @@ class BanTimeIncr(LogCaptureTestCase): + a.setBanTimeExtra('multipliers', multipliers) + # test algorithm and max time 24 hours : + self.assertEqual( +- [a.calcBanTime(600, i) for i in xrange(1, 11)], ++ [a.calcBanTime(600, i) for i in range(1, 11)], + [1200, 2400, 4800, 9600, 19200, 38400, 76800, 86400, 86400, 86400] + ) + # with extra large max time (30 days): +@@ -81,38 +81,38 @@ class BanTimeIncr(LogCaptureTestCase): + if multcnt < 11: + arr = arr[0:multcnt-1] + ([arr[multcnt-2]] * (11-multcnt)) + self.assertEqual( +- [a.calcBanTime(600, i) for i in xrange(1, 11)], ++ [a.calcBanTime(600, i) for i in range(1, 11)], + arr + ) + a.setBanTimeExtra('maxtime', '1d') + # change factor : + a.setBanTimeExtra('factor', '2'); + self.assertEqual( +- [a.calcBanTime(600, i) for i in xrange(1, 11)], ++ [a.calcBanTime(600, i) for i in range(1, 11)], + [2400, 4800, 9600, 19200, 38400, 76800, 86400, 86400, 86400, 86400] + ) + # factor is float : + a.setBanTimeExtra('factor', '1.33'); + self.assertEqual( +- [int(a.calcBanTime(600, i)) for i in xrange(1, 11)], ++ [int(a.calcBanTime(600, i)) for i in range(1, 11)], + [1596, 3192, 6384, 12768, 25536, 51072, 86400, 86400, 86400, 86400] + ) + a.setBanTimeExtra('factor', None); + # change max time : + a.setBanTimeExtra('maxtime', '12h') + self.assertEqual( +- [a.calcBanTime(600, i) for i in xrange(1, 11)], ++ [a.calcBanTime(600, i) for i in range(1, 11)], + [1200, 2400, 4800, 9600, 19200, 38400, 43200, 43200, 43200, 43200] + ) + a.setBanTimeExtra('maxtime', '24h') + ## test randomization - not possibe all 10 times we have random = 0: + a.setBanTimeExtra('rndtime', '5m') + self.assertTrue( +- False in [1200 in [a.calcBanTime(600, 1) for i in xrange(10)] for c in xrange(10)] ++ False in [1200 in [a.calcBanTime(600, 1) for i in range(10)] for c in range(10)] + ) + a.setBanTimeExtra('rndtime', None) + self.assertFalse( +- False in [1200 in [a.calcBanTime(600, 1) for i in xrange(10)] for c in xrange(10)] ++ False in [1200 in [a.calcBanTime(600, 1) for i in range(10)] for c in range(10)] + ) + # restore default: + a.setBanTimeExtra('multipliers', None) +@@ -124,7 +124,7 @@ class BanTimeIncr(LogCaptureTestCase): + # this multipliers has the same values as default formula, we test stop growing after count 9: + self.testDefault('1 2 4 8 16 32 64 128 256') + # this multipliers has exactly the same values as default formula, test endless growing (stops by count 31 only): +- self.testDefault(' '.join([str(1<= 0: +- line1 = f.next() ++ line1 = next(f) + self.assertTrue(line1.endswith("Before file moved\n")) +- line2 = f.next() ++ line2 = next(f) + self.assertTrue(line2.endswith("After file moved\n")) + try: +- n = f.next() ++ n = next(f) + if n.find("Command: ['flushlogs']") >=0: +- self.assertRaises(StopIteration, f.next) ++ self.assertRaises(StopIteration, f.__next__) + else: + self.fail("Exception StopIteration or Command: ['flushlogs'] expected. Got: %s" % n) + except StopIteration: + pass # on higher debugging levels this is expected + with open(fn,'r') as f: +- line1 = f.next() ++ line1 = next(f) + if line1.find('rollover performed on') >= 0: +- line1 = f.next() ++ line1 = next(f) + self.assertTrue(line1.endswith("After flushlogs\n")) +- self.assertRaises(StopIteration, f.next) ++ self.assertRaises(StopIteration, f.__next__) + f.close() + finally: + os.remove(fn2) +@@ -1185,7 +1185,7 @@ class LoggingTests(LogCaptureTestCase): + os.remove(f) + + +-from clientreadertestcase import ActionReader, JailsReader, CONFIG_DIR ++from .clientreadertestcase import ActionReader, JailsReader, CONFIG_DIR + + class ServerConfigReaderTests(LogCaptureTestCase): + +diff --git a/fail2ban/tests/sockettestcase.py b/fail2ban/tests/sockettestcase.py +index 69bf8d8b..60f49e57 100644 +--- a/fail2ban/tests/sockettestcase.py ++++ b/fail2ban/tests/sockettestcase.py +@@ -153,7 +153,7 @@ class Socket(LogCaptureTestCase): + org_handler = RequestHandler.found_terminator + try: + RequestHandler.found_terminator = lambda self: self.close() +- self.assertRaisesRegexp(RuntimeError, r"socket connection broken", ++ self.assertRaisesRegex(RuntimeError, r"socket connection broken", + lambda: client.send(testMessage, timeout=unittest.F2B.maxWaitTime(10))) + finally: + RequestHandler.found_terminator = org_handler +diff --git a/fail2ban/tests/utils.py b/fail2ban/tests/utils.py +index fcfddba7..cb234e0d 100644 +--- a/fail2ban/tests/utils.py ++++ b/fail2ban/tests/utils.py +@@ -35,7 +35,7 @@ import time + import threading + import unittest + +-from cStringIO import StringIO ++from io import StringIO + from functools import wraps + + from ..helpers import getLogger, str2LogLevel, getVerbosityFormat, uni_decode +@@ -174,8 +174,8 @@ def initProcess(opts): + + # Let know the version + if opts.verbosity != 0: +- print("Fail2ban %s test suite. Python %s. Please wait..." \ +- % (version, str(sys.version).replace('\n', ''))) ++ print(("Fail2ban %s test suite. Python %s. Please wait..." \ ++ % (version, str(sys.version).replace('\n', '')))) + + return opts; + +@@ -322,7 +322,7 @@ def initTests(opts): + c = DNSUtils.CACHE_ipToName + # increase max count and max time (too many entries, long time testing): + c.setOptions(maxCount=10000, maxTime=5*60) +- for i in xrange(256): ++ for i in range(256): + c.set('192.0.2.%s' % i, None) + c.set('198.51.100.%s' % i, None) + c.set('203.0.113.%s' % i, None) +@@ -541,8 +541,8 @@ def gatherTests(regexps=None, opts=None): + import difflib, pprint + if not hasattr(unittest.TestCase, 'assertDictEqual'): + def assertDictEqual(self, d1, d2, msg=None): +- self.assert_(isinstance(d1, dict), 'First argument is not a dictionary') +- self.assert_(isinstance(d2, dict), 'Second argument is not a dictionary') ++ self.assertTrue(isinstance(d1, dict), 'First argument is not a dictionary') ++ self.assertTrue(isinstance(d2, dict), 'Second argument is not a dictionary') + if d1 != d2: + standardMsg = '%r != %r' % (d1, d2) + diff = ('\n' + '\n'.join(difflib.ndiff( +@@ -560,7 +560,7 @@ def assertSortedEqual(self, a, b, level=1, nestedOnly=True, key=repr, msg=None): + # used to recognize having element as nested dict, list or tuple: + def _is_nested(v): + if isinstance(v, dict): +- return any(isinstance(v, (dict, list, tuple)) for v in v.itervalues()) ++ return any(isinstance(v, (dict, list, tuple)) for v in v.values()) + return any(isinstance(v, (dict, list, tuple)) for v in v) + # level comparison routine: + def _assertSortedEqual(a, b, level, nestedOnly, key): +@@ -573,7 +573,7 @@ def assertSortedEqual(self, a, b, level=1, nestedOnly=True, key=repr, msg=None): + return + raise ValueError('%r != %r' % (a, b)) + if isinstance(a, dict) and isinstance(b, dict): # compare dict's: +- for k, v1 in a.iteritems(): ++ for k, v1 in a.items(): + v2 = b[k] + if isinstance(v1, (dict, list, tuple)) and isinstance(v2, (dict, list, tuple)): + _assertSortedEqual(v1, v2, level-1 if level != 0 else 0, nestedOnly, key) +@@ -608,14 +608,14 @@ if not hasattr(unittest.TestCase, 'assertRaisesRegexp'): + self.fail('\"%s\" does not match \"%s\"' % (regexp, e)) + else: + self.fail('%s not raised' % getattr(exccls, '__name__')) +- unittest.TestCase.assertRaisesRegexp = assertRaisesRegexp ++ unittest.TestCase.assertRaisesRegex = assertRaisesRegexp + + # always custom following methods, because we use atm better version of both (support generators) + if True: ## if not hasattr(unittest.TestCase, 'assertIn'): + def assertIn(self, a, b, msg=None): + bb = b + wrap = False +- if msg is None and hasattr(b, '__iter__') and not isinstance(b, basestring): ++ if msg is None and hasattr(b, '__iter__') and not isinstance(b, str): + b, bb = itertools.tee(b) + wrap = True + if a not in b: +@@ -626,7 +626,7 @@ if True: ## if not hasattr(unittest.TestCase, 'assertIn'): + def assertNotIn(self, a, b, msg=None): + bb = b + wrap = False +- if msg is None and hasattr(b, '__iter__') and not isinstance(b, basestring): ++ if msg is None and hasattr(b, '__iter__') and not isinstance(b, str): + b, bb = itertools.tee(b) + wrap = True + if a in b: +diff --git a/setup.py b/setup.py +deleted file mode 100755 +index ce1eedf6..00000000 +--- a/setup.py ++++ /dev/null +@@ -1,326 +0,0 @@ +-#!/usr/bin/env python +-# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*- +-# vi: set ft=python sts=4 ts=4 sw=4 noet : +- +-# This file is part of Fail2Ban. +-# +-# Fail2Ban is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 2 of the License, or +-# (at your option) any later version. +-# +-# Fail2Ban is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. +-# +-# You should have received a copy of the GNU General Public License +-# along with Fail2Ban; if not, write to the Free Software +-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +- +-__author__ = "Cyril Jaquier, Steven Hiscocks, Yaroslav Halchenko" +-__copyright__ = "Copyright (c) 2004 Cyril Jaquier, 2008-2016 Fail2Ban Contributors" +-__license__ = "GPL" +- +-import platform +- +-try: +- import setuptools +- from setuptools import setup +- from setuptools.command.install import install +- from setuptools.command.install_scripts import install_scripts +-except ImportError: +- setuptools = None +- from distutils.core import setup +- +-# all versions +-from distutils.command.build_py import build_py +-from distutils.command.build_scripts import build_scripts +-if setuptools is None: +- from distutils.command.install import install +- from distutils.command.install_scripts import install_scripts +-try: +- # python 3.x +- from distutils.command.build_py import build_py_2to3 +- from distutils.command.build_scripts import build_scripts_2to3 +- _2to3 = True +-except ImportError: +- # python 2.x +- _2to3 = False +- +-import os +-from os.path import isfile, join, isdir, realpath +-import re +-import sys +-import warnings +-from glob import glob +- +-from fail2ban.setup import updatePyExec +- +- +-source_dir = os.path.realpath(os.path.dirname( +- # __file__ seems to be overwritten sometimes on some python versions (e.g. bug of 2.6 by running under cProfile, etc.): +- sys.argv[0] if os.path.basename(sys.argv[0]) == 'setup.py' else __file__ +-)) +- +-# Wrapper to install python binding (to current python version): +-class install_scripts_f2b(install_scripts): +- +- def get_outputs(self): +- outputs = install_scripts.get_outputs(self) +- # setup.py --dry-run install: +- dry_run = not outputs +- self.update_scripts(dry_run) +- if dry_run: +- #bindir = self.install_dir +- bindir = self.build_dir +- print('creating fail2ban-python binding -> %s (dry-run, real path can be different)' % (bindir,)) +- print('Copying content of %s to %s' % (self.build_dir, self.install_dir)); +- return outputs +- fn = None +- for fn in outputs: +- if os.path.basename(fn) == 'fail2ban-server': +- break +- bindir = os.path.dirname(fn) +- print('creating fail2ban-python binding -> %s' % (bindir,)) +- updatePyExec(bindir) +- return outputs +- +- def update_scripts(self, dry_run=False): +- buildroot = os.path.dirname(self.build_dir) +- install_dir = self.install_dir +- try: +- # remove root-base from install scripts path: +- root = self.distribution.command_options['install']['root'][1] +- if install_dir.startswith(root): +- install_dir = install_dir[len(root):] +- except: # pragma: no cover +- print('WARNING: Cannot find root-base option, check the bin-path to fail2ban-scripts in "fail2ban.service".') +- print('Creating %s/fail2ban.service (from fail2ban.service.in): @BINDIR@ -> %s' % (buildroot, install_dir)) +- with open(os.path.join(source_dir, 'files/fail2ban.service.in'), 'r') as fn: +- lines = fn.readlines() +- fn = None +- if not dry_run: +- fn = open(os.path.join(buildroot, 'fail2ban.service'), 'w') +- try: +- for ln in lines: +- ln = re.sub(r'@BINDIR@', lambda v: install_dir, ln) +- if dry_run: +- sys.stdout.write(' | ' + ln) +- continue +- fn.write(ln) +- finally: +- if fn: fn.close() +- if dry_run: +- print(' `') +- +- +-# Wrapper to specify fail2ban own options: +-class install_command_f2b(install): +- user_options = install.user_options + [ +- ('disable-2to3', None, 'Specify to deactivate 2to3, e.g. if the install runs from fail2ban test-cases.'), +- ('without-tests', None, 'without tests files installation'), +- ] +- def initialize_options(self): +- self.disable_2to3 = None +- self.without_tests = None +- install.initialize_options(self) +- def finalize_options(self): +- global _2to3 +- ## in the test cases 2to3 should be already done (fail2ban-2to3): +- if self.disable_2to3: +- _2to3 = False +- if _2to3: +- cmdclass = self.distribution.cmdclass +- cmdclass['build_py'] = build_py_2to3 +- cmdclass['build_scripts'] = build_scripts_2to3 +- if self.without_tests: +- self.distribution.scripts.remove('bin/fail2ban-testcases') +- +- self.distribution.packages.remove('fail2ban.tests') +- self.distribution.packages.remove('fail2ban.tests.action_d') +- +- del self.distribution.package_data['fail2ban.tests'] +- install.finalize_options(self) +- def run(self): +- install.run(self) +- +- +-# Update fail2ban-python env to current python version (where f2b-modules located/installed) +-updatePyExec(os.path.join(source_dir, 'bin')) +- +-if setuptools and "test" in sys.argv: +- import logging +- logSys = logging.getLogger("fail2ban") +- hdlr = logging.StreamHandler(sys.stdout) +- fmt = logging.Formatter("%(asctime)-15s %(message)s") +- hdlr.setFormatter(fmt) +- logSys.addHandler(hdlr) +- if set(["-q", "--quiet"]) & set(sys.argv): +- logSys.setLevel(logging.CRITICAL) +- warnings.simplefilter("ignore") +- sys.warnoptions.append("ignore") +- elif set(["-v", "--verbose"]) & set(sys.argv): +- logSys.setLevel(logging.DEBUG) +- else: +- logSys.setLevel(logging.INFO) +-elif "test" in sys.argv: +- print("python distribute required to execute fail2ban tests") +- print("") +- +-longdesc = ''' +-Fail2Ban scans log files like /var/log/pwdfail or +-/var/log/apache/error_log and bans IP that makes +-too many password failures. It updates firewall rules +-to reject the IP address or executes user defined +-commands.''' +- +-if setuptools: +- setup_extra = { +- 'test_suite': "fail2ban.tests.utils.gatherTests", +- 'use_2to3': True, +- } +-else: +- setup_extra = {} +- +-data_files_extra = [] +-if os.path.exists('/var/run'): +- # if we are on the system with /var/run -- we are to use it for having fail2ban/ +- # directory there for socket file etc. +- # realpath is used to possibly resolve /var/run -> /run symlink +- data_files_extra += [(realpath('/var/run/fail2ban'), '')] +- +-# Installing documentation files only under Linux or other GNU/ systems +-# (e.g. GNU/kFreeBSD), since others might have protective mechanisms forbidding +-# installation there (see e.g. #1233) +-platform_system = platform.system().lower() +-doc_files = ['README.md', 'DEVELOP', 'FILTERS', 'doc/run-rootless.txt'] +-if platform_system in ('solaris', 'sunos'): +- doc_files.append('README.Solaris') +-if platform_system in ('linux', 'solaris', 'sunos') or platform_system.startswith('gnu'): +- data_files_extra.append( +- ('/usr/share/doc/fail2ban', doc_files) +- ) +- +-# Get version number, avoiding importing fail2ban. +-# This is due to tests not functioning for python3 as 2to3 takes place later +-exec(open(join("fail2ban", "version.py")).read()) +- +-setup( +- name = "fail2ban", +- version = version, +- description = "Ban IPs that make too many password failures", +- long_description = longdesc, +- author = "Cyril Jaquier & Fail2Ban Contributors", +- author_email = "cyril.jaquier@fail2ban.org", +- url = "http://www.fail2ban.org", +- license = "GPL", +- platforms = "Posix", +- cmdclass = { +- 'build_py': build_py, 'build_scripts': build_scripts, +- 'install_scripts': install_scripts_f2b, 'install': install_command_f2b +- }, +- scripts = [ +- 'bin/fail2ban-client', +- 'bin/fail2ban-server', +- 'bin/fail2ban-regex', +- 'bin/fail2ban-testcases', +- # 'bin/fail2ban-python', -- link (binary), will be installed via install_scripts_f2b wrapper +- ], +- packages = [ +- 'fail2ban', +- 'fail2ban.client', +- 'fail2ban.server', +- 'fail2ban.tests', +- 'fail2ban.tests.action_d', +- ], +- package_data = { +- 'fail2ban.tests': +- [ join(w[0], f).replace("fail2ban/tests/", "", 1) +- for w in os.walk('fail2ban/tests/files') +- for f in w[2]] + +- [ join(w[0], f).replace("fail2ban/tests/", "", 1) +- for w in os.walk('fail2ban/tests/config') +- for f in w[2]] + +- [ join(w[0], f).replace("fail2ban/tests/", "", 1) +- for w in os.walk('fail2ban/tests/action_d') +- for f in w[2]] +- }, +- data_files = [ +- ('/etc/fail2ban', +- glob("config/*.conf") +- ), +- ('/etc/fail2ban/filter.d', +- glob("config/filter.d/*.conf") +- ), +- ('/etc/fail2ban/filter.d/ignorecommands', +- [p for p in glob("config/filter.d/ignorecommands/*") if isfile(p)] +- ), +- ('/etc/fail2ban/action.d', +- glob("config/action.d/*.conf") + +- glob("config/action.d/*.py") +- ), +- ('/etc/fail2ban/fail2ban.d', +- '' +- ), +- ('/etc/fail2ban/jail.d', +- '' +- ), +- ('/var/lib/fail2ban', +- '' +- ), +- ] + data_files_extra, +- **setup_extra +-) +- +-# Do some checks after installation +-# Search for obsolete files. +-obsoleteFiles = [] +-elements = { +- "/etc/": +- [ +- "fail2ban.conf" +- ], +- "/usr/bin/": +- [ +- "fail2ban.py" +- ], +- "/usr/lib/fail2ban/": +- [ +- "version.py", +- "protocol.py" +- ] +-} +- +-for directory in elements: +- for f in elements[directory]: +- path = join(directory, f) +- if isfile(path): +- obsoleteFiles.append(path) +- +-if obsoleteFiles: +- print("") +- print("Obsolete files from previous Fail2Ban versions were found on " +- "your system.") +- print("Please delete them:") +- print("") +- for f in obsoleteFiles: +- print("\t" + f) +- print("") +- +-if isdir("/usr/lib/fail2ban"): +- print("") +- print("Fail2ban is not installed under /usr/lib anymore. The new " +- "location is under /usr/share. Please remove the directory " +- "/usr/lib/fail2ban and everything under this directory.") +- print("") +- +-# Update config file +-if sys.argv[1] == "install": +- print("") +- print("Please do not forget to update your configuration files.") +- print("They are in \"/etc/fail2ban/\".") +- print("") +- print("You can also install systemd service-unit file from \"build/fail2ban.service\"") +- print("resp. corresponding init script from \"files/*-initd\".") +- print("") +-- +2.17.1 + -- cgit 1.2.3-korg