Skip to content

Commit 298adc4

Browse files
committed
- Added ISO 8601 date/time format.
git-svn-id: https://fail2ban.svn.sourceforge.net/svnroot/fail2ban/branches/FAIL2BAN-0_8@692 a942ae1a-1317-0410-a47c-b1dcaea8d605
1 parent 7f37df4 commit 298adc4

File tree

6 files changed

+181
-4
lines changed

6 files changed

+181
-4
lines changed

COPYING

+29
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
The following copyright applies to all files present in the Fail2ban package,
2+
except if a different copyright is explicitly defined in this file.
3+
4+
15
GNU GENERAL PUBLIC LICENSE
26
Version 2, June 1991
37

@@ -337,3 +341,28 @@ proprietary programs. If your program is a subroutine library, you may
337341
consider it more useful to permit linking proprietary applications with the
338342
library. If this is what you want to do, use the GNU Lesser General
339343
Public License instead of this License.
344+
345+
---------------------------------
346+
The file server/iso8601.py is licensed under the following terms.
347+
348+
349+
Copyright (c) 2007 Michael Twomey
350+
351+
Permission is hereby granted, free of charge, to any person obtaining a
352+
copy of this software and associated documentation files (the
353+
"Software"), to deal in the Software without restriction, including
354+
without limitation the rights to use, copy, modify, merge, publish,
355+
distribute, sublicense, and/or sell copies of the Software, and to
356+
permit persons to whom the Software is furnished to do so, subject to
357+
the following conditions:
358+
359+
The above copyright notice and this permission notice shall be included
360+
in all copies or substantial portions of the Software.
361+
362+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
363+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
364+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
365+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
366+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
367+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
368+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

ChangeLog

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ ver. 0.8.3 (2008/??/??) - stable
2323
Thanks to Dennis Winter.
2424
- Fixed ignoreregex processing in fail2ban-client. Thanks to
2525
René Berber.
26+
- Added ISO 8601 date/time format.
2627

2728
ver. 0.8.2 (2008/03/06) - stable
2829
----------

MANIFEST

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ server/asyncserver.py
2121
server/filter.py
2222
server/filtergamin.py
2323
server/filterpoll.py
24+
server/iso8601.py
2425
server/server.py
2526
server/actions.py
2627
server/faildata.py

server/datedetector.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,7 @@
2626

2727
import time, logging
2828

29-
from datetemplate import DateStrptime
30-
from datetemplate import DateTai64n
31-
from datetemplate import DateEpoch
29+
from datetemplate import DateStrptime, DateTai64n, DateEpoch, DateISO8601
3230
from threading import Lock
3331

3432
# Gets the instance of the logger.
@@ -99,6 +97,10 @@ def addDefaultTemplate(self):
9997
template = DateEpoch()
10098
template.setName("Epoch")
10199
self.__templates.append(template)
100+
# ISO 8601
101+
template = DateISO8601()
102+
template.setName("ISO 8601")
103+
self.__templates.append(template)
102104
finally:
103105
self.__lock.release()
104106

server/datetemplate.py

+22-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import re, time
2929

3030
from mytime import MyTime
31+
import iso8601
3132

3233
class DateTemplate:
3334

@@ -163,4 +164,24 @@ def getDate(self, line):
163164
value = dateMatch.group()
164165
seconds_since_epoch = value[2:17]
165166
date = list(time.gmtime(int(seconds_since_epoch, 16)))
166-
return date
167+
return date
168+
169+
170+
class DateISO8601(DateTemplate):
171+
172+
def __init__(self):
173+
DateTemplate.__init__(self)
174+
date_re = "[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}" \
175+
".[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?" \
176+
"(Z|(([-+])([0-9]{2}):([0-9]{2})))?"
177+
self.setRegex(date_re)
178+
179+
def getDate(self, line):
180+
date = None
181+
dateMatch = self.matchDate(line)
182+
if dateMatch:
183+
# Parses the date.
184+
value = dateMatch.group()
185+
print value
186+
date = list(iso8601.parse_date(value).utctimetuple())
187+
return date

server/iso8601.py

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# Copyright (c) 2007 Michael Twomey
2+
#
3+
# Permission is hereby granted, free of charge, to any person obtaining a
4+
# copy of this software and associated documentation files (the
5+
# "Software"), to deal in the Software without restriction, including
6+
# without limitation the rights to use, copy, modify, merge, publish,
7+
# distribute, sublicense, and/or sell copies of the Software, and to
8+
# permit persons to whom the Software is furnished to do so, subject to
9+
# the following conditions:
10+
#
11+
# The above copyright notice and this permission notice shall be included
12+
# in all copies or substantial portions of the Software.
13+
#
14+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15+
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20+
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
22+
"""ISO 8601 date time string parsing
23+
24+
Basic usage:
25+
>>> import iso8601
26+
>>> iso8601.parse_date("2007-01-25T12:00:00Z")
27+
datetime.datetime(2007, 1, 25, 12, 0, tzinfo=<iso8601.iso8601.Utc ...>)
28+
>>>
29+
30+
"""
31+
32+
from datetime import datetime, timedelta, tzinfo
33+
import re
34+
35+
__all__ = ["parse_date", "ParseError"]
36+
37+
# Adapted from http://delete.me.uk/2005/03/iso8601.html
38+
ISO8601_REGEX = re.compile(r"(?P<year>[0-9]{4})(-(?P<month>[0-9]{1,2})(-(?P<day>[0-9]{1,2})"
39+
r"((?P<separator>.)(?P<hour>[0-9]{2}):(?P<minute>[0-9]{2})(:(?P<second>[0-9]{2})(\.(?P<fraction>[0-9]+))?)?"
40+
r"(?P<timezone>Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?"
41+
)
42+
TIMEZONE_REGEX = re.compile("(?P<prefix>[+-])(?P<hours>[0-9]{2}).(?P<minutes>[0-9]{2})")
43+
44+
class ParseError(Exception):
45+
"""Raised when there is a problem parsing a date string"""
46+
47+
# Yoinked from python docs
48+
ZERO = timedelta(0)
49+
class Utc(tzinfo):
50+
"""UTC
51+
52+
"""
53+
def utcoffset(self, dt):
54+
return ZERO
55+
56+
def tzname(self, dt):
57+
return "UTC"
58+
59+
def dst(self, dt):
60+
return ZERO
61+
UTC = Utc()
62+
63+
class FixedOffset(tzinfo):
64+
"""Fixed offset in hours and minutes from UTC
65+
66+
"""
67+
def __init__(self, offset_hours, offset_minutes, name):
68+
self.__offset = timedelta(hours=offset_hours, minutes=offset_minutes)
69+
self.__name = name
70+
71+
def utcoffset(self, dt):
72+
return self.__offset
73+
74+
def tzname(self, dt):
75+
return self.__name
76+
77+
def dst(self, dt):
78+
return ZERO
79+
80+
def __repr__(self):
81+
return "<FixedOffset %r>" % self.__name
82+
83+
def parse_timezone(tzstring, default_timezone=UTC):
84+
"""Parses ISO 8601 time zone specs into tzinfo offsets
85+
86+
"""
87+
if tzstring == "Z":
88+
return default_timezone
89+
# This isn't strictly correct, but it's common to encounter dates without
90+
# timezones so I'll assume the default (which defaults to UTC).
91+
# Addresses issue 4.
92+
if tzstring is None:
93+
return default_timezone
94+
m = TIMEZONE_REGEX.match(tzstring)
95+
prefix, hours, minutes = m.groups()
96+
hours, minutes = int(hours), int(minutes)
97+
if prefix == "-":
98+
hours = -hours
99+
minutes = -minutes
100+
return FixedOffset(hours, minutes, tzstring)
101+
102+
def parse_date(datestring, default_timezone=UTC):
103+
"""Parses ISO 8601 dates into datetime objects
104+
105+
The timezone is parsed from the date string. However it is quite common to
106+
have dates without a timezone (not strictly correct). In this case the
107+
default timezone specified in default_timezone is used. This is UTC by
108+
default.
109+
"""
110+
if not isinstance(datestring, basestring):
111+
raise ParseError("Expecting a string %r" % datestring)
112+
m = ISO8601_REGEX.match(datestring)
113+
if not m:
114+
raise ParseError("Unable to parse date string %r" % datestring)
115+
groups = m.groupdict()
116+
tz = parse_timezone(groups["timezone"], default_timezone=default_timezone)
117+
if groups["fraction"] is None:
118+
groups["fraction"] = 0
119+
else:
120+
groups["fraction"] = int(float("0.%s" % groups["fraction"]) * 1e6)
121+
return datetime(int(groups["year"]), int(groups["month"]), int(groups["day"]),
122+
int(groups["hour"]), int(groups["minute"]), int(groups["second"]),
123+
int(groups["fraction"]), tz)

0 commit comments

Comments
 (0)