* Merged with upstream

* Changed debian/watch to run svn-upgrade
debian-releases/etch
Yaroslav Halchenko 2005-07-23 19:15:22 +00:00
parent c3909b8ec4
commit 120a1d9fbc
17 changed files with 263 additions and 109 deletions

View File

@ -4,9 +4,20 @@
|_| \__,_|_|_/___|_.__/\__,_|_||_|
=============================================================
Fail2Ban (version 0.5.0) 2005/07/12
Fail2Ban (version 0.5.1) 2005/07/23
=============================================================
ver. 0.5.1 (2005/07/23) - beta
----------
- Fixed bugs #1241756, #1239557
- Added log targets in configuration file. Removed -l option
- Changed iptables rules in order to create a separated chain
for each section
- Fixed static banList in firewall.py
- Added an initd script for Debian. Thanks to Yaroslav
Halchenko
- Check for obsolete files after install
ver. 0.5.0 (2005/07/12) - beta
----------
- Added support for CIDR mask in ignoreip

View File

@ -1,6 +1,6 @@
Metadata-Version: 1.0
Name: fail2ban
Version: 0.5.0
Version: 0.5.1
Summary: Ban IPs that make too many password failure
Home-page: http://fail2ban.sourceforge.net
Author: Cyril Jaquier

25
README
View File

@ -4,7 +4,7 @@
|_| \__,_|_|_/___|_.__/\__,_|_||_|
=============================================================
Fail2Ban (version 0.5.0) 2005/07/12
Fail2Ban (version 0.5.1) 2005/07/23
=============================================================
Fail2Ban scans log files like /var/log/pwdfail and bans IP
@ -55,34 +55,36 @@ Installation:
-------------
Require: python-2.3 (http://www.python.org)
log4py-1.1 (http://sourceforge.net/projects/log4py)
log4py-1.3 (http://sourceforge.net/projects/log4py)
To install, just do:
> tar xvfj fail2ban-0.5.0.tar.bz2
> cd fail2ban-0.5.0
> tar xvfj fail2ban-0.5.1.tar.bz2
> cd fail2ban-0.5.1
> python setup.py install
This will install Fail2Ban into /usr/lib/fail2ban. The
fail2ban.py executable is placed into /usr/bin.
This will install Fail2Ban into /usr/lib/fail2ban. The fail2ban
executable is placed into /usr/bin.
Gentoo: an ebuild is available on the website.
Debian: a package is available on the website.
Fail2Ban should now be correctly installed. Just type:
> fail2ban.py -h
> fail2ban -h
to see if everything is alright. You can configure fail2ban
with a config file. Copy config/fail2ban.conf.default to
/etc/fail2ban.conf.
Gentoo users can use the initd script available in config/.
Copy gentoo-initd to /etc/init.d/fail2ban and gentoo-confd
to /etc/conf.d/fail2ban. You can start fail2ban and add it
to your default runlevel:
You can use the initd script available in config/. Copy
<dist>-initd to /etc/init.d/fail2ban. Gentoo users must copy
gentoo-confd to /etc/conf.d/fail2ban. You can start fail2ban:
> /etc/init.d/fail2ban start
Gentoo users can add it to the default runlevel:
> rc-update add fail2ban default
Configuration:
@ -100,7 +102,6 @@ options:
-h display this help message
-i <IP(s)> IP(s) to ignore
-k kill a currently running Fail2Ban instance
-l <FILE> log messages in FILE
-r <VALUE> allow a max of VALUE password failure
-t <TIME> ban IP for TIME seconds
-v verbose. Use twice for greater effect

10
TODO
View File

@ -7,9 +7,7 @@
ToDo
=============================================================
- cleanup fail2ban.py
- improve configuration file and command line options
handling
- improve installation process
- add init script
- use FAM (inotify, gamin, ...)
See Feature Request Tracking System at SourceForge.net
- improve installation process (better prefix support)
- add better documentation (man page)

62
config/debian-initd Normal file
View File

@ -0,0 +1,62 @@
#! /bin/sh
#
# Fail2Ban init.d file - to be launched on boot
#
# Written by Miquel van Smoorenburg <miquels@cistron.nl>.
# Modified for Debian
# by Ian Murdock <imurdock@gnu.ai.mit.edu>.
# Adjusted for Fail2Ban
# by Yaroslav Halchenko <debian@onerussian.com>.
#
# Version: $Id: $
#
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/bin/fail2ban
NAME=fail2ban
DESC=fail2ban
PIDFILE=/var/run/$NAME.pid
test -x $DAEMON || exit 0
# Include fail2ban defaults if available
if [ -f /etc/default/fail2ban ] ; then
. /etc/default/fail2ban
fi
DAEMON_OPTS=$FAIL2BAN_OPTS
set -e
case "$1" in
start)
echo -n "Starting $DESC: "
[ -f $PIDFILE ] && [ ! -d /proc/`cat $PIDFILE` ] && rm -f $PIDFILE
start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \
-b --exec $DAEMON -- $DAEMON_OPTS
echo "$NAME."
;;
stop)
echo -n "Stopping $DESC: "
start-stop-daemon --stop --quiet --pidfile /var/run/$NAME.pid
rm -f $PIDFILE
echo "$NAME."
;;
restart|force-reload)
#
# If the "reload" option is implemented, move the "force-reload"
# option to the "reload" entry above. If not, "force-reload" is
# just the same as "restart".
#
echo -n "Restarting $DESC: "
( $0 stop )
sleep 1
$0 start
;;
*)
N=/etc/init.d/$NAME
# echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2
echo "Usage: $N {start|stop|restart|force-reload}" >&2
exit 1
;;
esac
exit 0

View File

@ -1,6 +1,6 @@
# Fail2Ban configuration file
#
# $Revision: 1.8.2.5 $
# $Revision: 1.8.2.7 $
#
# 2005.06.21 modified for readability Iain Lea iain@bricbrac.de
@ -17,18 +17,18 @@ background = true
#
debug = false
# Option: logtargets
# Notes.: log targets. Space separated list of logging targets.
# Values: STDOUT STDERR SYSLOG file Default: STDOUT /var/log/fail2ban.log
#
logtargets = STDOUT /var/log/fail2ban.log
# Option: pidlock
# Notes.: path of the PID lock file (must be able to write to file).
# Values: FILE Default: /var/run/fail2ban.pid
#
pidlock = /var/run/fail2ban.pid
# Option: logfile
# Notes.: logfile for logging fail2ban messages.
# Values: FILE Default: /var/log/fail2ban.log
#
logfile = /var/log/fail2ban.log
# Option: maxretry
# Notes.: number of retrys before IP gets banned.
# Values: NUM Default: 3
@ -45,9 +45,12 @@ bantime = 600
# Notes.: space separated list of IP's to be ignored by fail2ban.
# You can use CIDR mask in order to specify a range.
# Example: ignoreip = 192.168.0.1/24 123.45.235.65
# Values: IP Default: 192.168.0.0/24
# Values: IP Default: empty
# Examples
# ignoreip = 192.168.0.0/24
#
ignoreip = 192.168.0.0/16
ignoreip =
# Option: cmdstart
# Notes.: command executed once at the start of Fail2Ban
@ -93,7 +96,8 @@ port = 25
from = fail2ban
# Option: to
# Notes.: e-mail address of the receiver.
# Notes.: e-mail addresses of the receiver. Addresses are space
# separated.
# Values: MAIL Default: root
#
to = root
@ -116,10 +120,10 @@ subject = [Fail2Ban] Banned <ip>
# Values: TEXT Default:
#
message = Hi,<br>
The IP <ip> has just been banned by Fail2Ban after
<failures> attempts.<br>
Regards,<br>
Fail2Ban
The IP <ip> has just been banned by Fail2Ban after
<failures> attempts.<br>
Regards,<br>
Fail2Ban
# You can define a new section for each log file to check for
# password failure. Each section has to define the following
@ -143,13 +147,17 @@ logfile = /var/log/apache/access.log
# Notes.: command executed once at the start of Fail2Ban
# Values: CMD Default:
#
fwstart =
fwstart = iptables -N FAIL2BAN-HTTP
iptables -I INPUT -i eth0 -p tcp --dport http -j FAIL2BAN-HTTP
iptables -A FAIL2BAN-HTTP -j RETURN
# Option: fwend
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD Default:
#
fwend =
fwend = iptables -D INPUT -i eth0 -p tcp --dport http -j FAIL2BAN-HTTP
iptables -D FAIL2BAN-HTTP -j RETURN
iptables -X FAIL2BAN-HTTP
# Option: fwban
# Notes.: command executed when banning an IP. Take care that the
@ -161,7 +169,7 @@ fwend =
# Values: CMD
# Default: iptables -I INPUT 1 -i eth0 -s <ip> -j DROP
#
fwban = iptables -I INPUT 1 -i eth0 -s <ip> -j DROP
fwban = iptables -I FAIL2BAN-HTTP 1 -i eth0 -s <ip> -j DROP
# Option: fwunban
# Notes.: command executed when unbanning an IP. Take care that the
@ -172,7 +180,7 @@ fwban = iptables -I INPUT 1 -i eth0 -s <ip> -j DROP
# Values: CMD
# Default: iptables -D INPUT -i eth0 -s <ip> -j DROP
#
fwunban = iptables -D INPUT -i eth0 -s <ip> -j DROP
fwunban = iptables -D FAIL2BAN-HTTP -i eth0 -s <ip> -j DROP
# Option: timeregex
# Notes.: regex to match timestamp in Apache logfile.
@ -211,13 +219,17 @@ logfile = /var/log/auth.log
# Notes.: command executed once at the start of Fail2Ban
# Values: CMD Default:
#
fwstart =
fwstart = iptables -N FAIL2BAN-SSH
iptables -I INPUT -i eth0 -p tcp --dport ssh -j FAIL2BAN-SSH
iptables -A FAIL2BAN-SSH -j RETURN
# Option: fwend
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD Default:
#
fwend =
fwend = iptables -D INPUT -i eth0 -p tcp --dport ssh -j FAIL2BAN-SSH
iptables -D FAIL2BAN-SSH -j RETURN
iptables -X FAIL2BAN-SSH
# Option: fwbanrule
# Notes.: command executed when banning an IP. Take care that the
@ -229,7 +241,7 @@ fwend =
# Values: CMD
# Default: iptables -I INPUT 1 -i eth0 -s <ip> -j DROP
#
fwban = iptables -I INPUT 1 -i eth0 -s <ip> -j DROP
fwban = iptables -I FAIL2BAN-SSH 1 -i eth0 -s <ip> -j DROP
# Option: fwunbanrule
# Notes.: command executed when unbanning an IP. Take care that the
@ -240,7 +252,7 @@ fwban = iptables -I INPUT 1 -i eth0 -s <ip> -j DROP
# Values: CMD
# Default: iptables -D INPUT -i eth0 -s <ip> -j DROP
#
fwunban = iptables -D INPUT -i eth0 -s <ip> -j DROP
fwunban = iptables -D FAIL2BAN-SSH -i eth0 -s <ip> -j DROP
# Option: timeregex
# Notes.: regex to match timestamp in SSH logfile.

14
debian/README.Debian vendored
View File

@ -2,12 +2,18 @@ fail2ban for Debian
-------------------
This package is nearly 100% identical to the upstream version. It was
merely packaged to be installed on a Debian system.
merely packaged to be installed on a Debian system and due to tight
collaboration with upstream author most of the Debian modifications
penetrate into the next upstream.
Module log4py installed into lib/fail2ban directory because there is no
package for not-developed-in-a-long-time fail2ban
Currently the main difference with upstream: python libraries are
placed under /usr/share/fail2ban insteadh of /usr/lib/fail2ban to
comply with policy regarding architecture independent resources.
Module log4py installed along into fail2ban directory because there is
no package for not-developed-in-a-long-time fail2ban
See the file TODO.Debian for more details, as well as the Debian Bug
Tracking system.
-- Yaroslav Halchenko <debian@onerussian.com>, Tue, 4 Jul 2005 00:00:00 -1000
-- Yaroslav O. Halchenko <debian@onerussian.com>, Sat Jul 23 09:09:51 2005

7
debian/changelog vendored
View File

@ -1,6 +1,13 @@
fail2ban (0.5.1-1) unstable; urgency=low
* New upstream release
-- Yaroslav Halchenko <debian@onerussian.com> Sat, 23 Jul 2005 08:50:00 -1000
fail2ban (0.5.0-1) unstable; urgency=low
* New upstream release
* Libraries placed under /usr/share/fail2ban instead of /usr/lib/fail2ban
* Corrections to the description of the package
-- Yaroslav Halchenko <debian@onerussian.com> Tue, 12 Jul 2005 23:33:20 -1000

6
debian/watch vendored
View File

@ -2,7 +2,5 @@
# Run the "uscan" command to check for upstream updates and more.
# Site Directory Pattern Version Script
version=3
http://sf.net/fail2ban/fail2ban-(.*)\.tar\.bz2
#http://voxel.dl.sourceforge.net/sourceforge/fail2ban/ \
# fail2ban-([0-9]+\.[0-9]+\.[0-9]*)\.tar\.bz2
# debian uupdate
http://sf.net/fail2ban/fail2ban-(.*)\.tar\.bz2 debian svn-upgrade

View File

@ -18,21 +18,21 @@
# Author: Cyril Jaquier
#
# $Revision: 1.4.2.2 $
# $Revision: 1.4.2.3 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 1.4.2.2 $"
__date__ = "$Date: 2005/07/08 10:21:52 $"
__version__ = "$Revision: 1.4.2.3 $"
__date__ = "$Date: 2005/07/15 14:11:21 $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
from sys import exit, path
#yoh: We do need to load this path first if we ship log4py with fail2ban
# Appends our own modules path
# Appends our own modules path. Added before log4py import
# because log4py could be distributed with Fail2Ban.
path.append('/usr/share/fail2ban')
# Checks for required libs
# Checks if log4py is present.
try:

View File

@ -16,23 +16,24 @@
# Author: Cyril Jaquier
#
# $Revision: 1.20.2.5 $
# $Revision: 1.20.2.8 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 1.20.2.5 $"
__date__ = "$Date: 2005/07/12 13:11:58 $"
__version__ = "$Revision: 1.20.2.8 $"
__date__ = "$Date: 2005/07/22 21:13:19 $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
import time, sys, getopt, os, string, signal, log4py
from ConfigParser import *
from version import version
from firewall.firewall import Firewall
from logreader.logreader import LogReader
from confreader.configreader import ConfigReader
from utils.process import *
from utils.mail import Mail
from version import version
from utils.dns import *
from utils.process import *
# Gets the instance of log4py.
logSys = log4py.Logger().get_instance()
@ -56,7 +57,6 @@ def dispUsage():
print " -h display this help message"
print " -i <IP(s)> IP(s) to ignore"
print " -k kill a currently running Fail2Ban instance"
print " -l <FILE> log messages in FILE"
print " -r <VALUE> allow a max of VALUE password failure"
print " -t <TIME> ban IP for TIME seconds"
print " -v verbose. Use twice for greater effect"
@ -119,8 +119,6 @@ def getCmdLineOptions(optList):
conf["background"] = True
if opt[0] == "-d":
conf["debug"] = True
if opt[0] == "-l":
conf["logfile"] = opt[1]
if opt[0] == "-t":
try:
conf["bantime"] = int(opt[1])
@ -153,7 +151,7 @@ def main():
# Reads the command line options.
try:
cmdOpts = 'hvVbdkc:l:t:i:r:p:'
cmdOpts = 'hvVbdkc:t:i:r:p:'
cmdLongOpts = ['help','version']
optList, args = getopt.getopt(sys.argv[1:], cmdOpts, cmdLongOpts)
except getopt.GetoptError:
@ -171,8 +169,8 @@ def main():
# Options
optionValues = (["bool", "background", False],
["str", "logtargets", "STDOUT /var/log/fail2ban.log"],
["bool", "debug", False],
["str", "logfile", "/var/log/fail2ban.log"],
["str", "pidlock", "/var/run/fail2ban.pid"],
["int", "maxretry", 3],
["int", "bantime", 600],
@ -188,6 +186,31 @@ def main():
getCmdLineOptions(optList)
# Process some options
# Log targets
# Bug fix for #1234699
os.umask(0077)
# Remove all the targets before setting our own
logSys.remove_all_targets()
for target in conf["logtargets"].split():
if target == "STDOUT":
logSys.add_target(log4py.TARGET_SYS_STDOUT)
elif target == "STDERR":
logSys.add_target(log4py.TARGET_SYS_STDERR)
elif target == "SYSLOG":
logSys.add_target(log4py.TARGET_SYSLOG)
else:
# Target should be a file
try:
open(target, "a")
logSys.add_target(target)
except IOError:
logSys.error("Unable to log to " + target)
# Check if at least one target exists
if len(logSys.get_targets()) == 0:
logSys.add_target(log4py.TARGET_SYS_STDOUT)
logSys.error("No valid logging target found. Logging to STDOUT")
# Verbose level
if conf["verbose"]:
logSys.warn("Verbose level is "+`conf["verbose"]`)
@ -210,14 +233,6 @@ def main():
if not retCode:
logSys.error("Unable to start daemon")
sys.exit(-1)
# Bug fix for #1234699
os.umask(0077)
try:
open(conf["logfile"], "a")
logSys.set_target(conf["logfile"])
except IOError:
logSys.error("Unable to log to " + conf["logfile"])
logSys.warn("Using default output for logging")
# Ignores IP list
ignoreIPList = conf["ignoreip"].split(' ')
@ -291,8 +306,10 @@ def main():
element[1].addIgnoreIP("127.0.0.1")
while len(ignoreIPList) > 0:
ip = ignoreIPList.pop()
for element in logFwList:
element[1].addIgnoreIP(ip)
# Bug fix for #1239557
if isValidIP(ip):
for element in logFwList:
element[1].addIgnoreIP(ip)
logSys.info("Fail2Ban v"+version+" is running")
# Execute global start command

View File

@ -16,11 +16,11 @@
# Author: Cyril Jaquier
#
# $Revision: 1.8.2.4 $
# $Revision: 1.8.2.5 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 1.8.2.4 $"
__date__ = "$Date: 2005/07/12 13:08:24 $"
__version__ = "$Revision: 1.8.2.5 $"
__date__ = "$Date: 2005/07/15 14:07:08 $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
@ -37,12 +37,11 @@ class Firewall:
the IP.
"""
banList = dict()
def __init__(self, banRule, unBanRule, banTime):
self.banRule = banRule
self.unBanRule = unBanRule
self.banTime = banTime
self.banList = dict()
def addBanIP(self, aInfo, debug):
""" Bans an IP.

View File

@ -16,11 +16,11 @@
# Author: Cyril Jaquier
#
# $Revision: 1.13.2.2 $
# $Revision: 1.13.2.4 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 1.13.2.2 $"
__date__ = "$Date: 2005/07/12 13:09:09 $"
__version__ = "$Revision: 1.13.2.4 $"
__date__ = "$Date: 2005/07/23 09:07:53 $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
@ -186,6 +186,12 @@ class LogReader:
"""
date = list(time.strptime(value, self.timepattern))
if date[0] < 2000:
# There is probably no year field in the logs
date[0] = time.gmtime()[0]
# Bug fix for #1241756
# If the date is greater than the current time, we suppose
# that the log is not from this year but from the year before
if time.mktime(date) > time.time():
date[0] -= 1
unixTime = time.mktime(date)
return unixTime

View File

@ -18,25 +18,55 @@
# Author: Cyril Jaquier
#
# $Revision: 1.4.2.1 $
# $Revision: 1.4.2.2 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 1.4.2.1 $"
__date__ = "$Date: 2005/07/07 16:57:05 $"
__version__ = "$Revision: 1.4.2.2 $"
__date__ = "$Date: 2005/07/15 14:14:12 $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
from distutils.core import setup
from version import version
from os.path import isfile, join
from sys import exit, argv
setup(
name = "fail2ban",
version = version,
description = "Ban IPs that make too many password failure",
author = "Cyril Jaquier",
author_email = "lostcontrol@users.sourceforge.net",
url = "http://fail2ban.sourceforge.net",
scripts = ['fail2ban'],
py_modules = ['fail2ban', 'version','log4py'],
packages = ['firewall', 'logreader', 'confreader', 'utils']
name = "fail2ban",
version = version,
description = "Ban IPs that make too many password failure",
author = "Cyril Jaquier",
author_email = "lostcontrol@users.sourceforge.net",
url = "http://fail2ban.sourceforge.net",
scripts = ['fail2ban'],
py_modules = ['fail2ban', 'version', 'log4py'],
packages = ['firewall', 'logreader', 'confreader', 'utils']
)
# Do some checks after installation
# Search for obsolete files.
obsoleteFiles = []
elements = {"/usr/bin/": ["fail2ban.py"],
"/usr/lib/fail2ban/firewall/": ["iptables.py", "ipfwadm.py",
"ipfw.py"]}
for dir in elements:
for f in elements[dir]:
path = join(dir, 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
# Update config file
if argv[1] == "install":
print
print "Please do not forget to update your configuration file."
print "Use config/fail2ban.conf.default as example."
print

View File

@ -16,11 +16,11 @@
# Author: Cyril Jaquier
#
# $Revision: 1.7.2.1 $
# $Revision: 1.7.2.2 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 1.7.2.1 $"
__date__ = "$Date: 2005/07/12 13:10:14 $"
__version__ = "$Revision: 1.7.2.2 $"
__date__ = "$Date: 2005/07/22 21:11:42 $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
@ -55,6 +55,15 @@ def searchIP(text):
else:
return []
def isValidIP(str):
""" Return true if str is a valid IP
"""
try:
socket.inet_aton(str)
return True
except socket.error:
return False
def textToIp(text):
""" Return the IP of DNS found in a given text.
"""
@ -62,7 +71,8 @@ def textToIp(text):
# Search for plain IP
plainIP = searchIP(text)
for element in plainIP:
ipList.append(element)
if isValidIP(element):
ipList.append(element)
if not ipList:
# Try to get IP from possible DNS
dnsList = textToDns(text)

View File

@ -16,11 +16,11 @@
# Author: Cyril Jaquier
#
# $Revision: 1.1.2.1 $
# $Revision: 1.1.2.2 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 1.1.2.1 $"
__date__ = "$Date: 2005/07/07 16:53:47 $"
__version__ = "$Revision: 1.1.2.2 $"
__date__ = "$Date: 2005/07/15 14:08:17 $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
@ -76,9 +76,6 @@ def createDaemon():
# Ensure that the daemon doesn't keep any directory in use. Failure
# to do this could make a filesystem unmountable.
os.chdir("/")
# Give the child complete control over permissions.
# yoh: evil evil evil!
# os.umask(0)
else:
os._exit(0) # Exit parent (the first child) of the second child.
else:

View File

@ -16,12 +16,12 @@
# Author: Cyril Jaquier
#
# $Revision: 1.12.2.2 $
# $Revision: 1.12.2.4 $
__author__ = "Cyril Jaquier"
__version__ = "$Revision: 1.12.2.2 $"
__date__ = "$Date: 2005/07/12 13:12:40 $"
__version__ = "$Revision: 1.12.2.4 $"
__date__ = "$Date: 2005/07/23 09:31:12 $"
__copyright__ = "Copyright (c) 2004 Cyril Jaquier"
__license__ = "GPL"
version = "0.5.0"
version = "0.5.1"