[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Date: Sat, 2 Jun 2012 02:01:21 +0200
From: Frank Dittrich <frank_dittrich@...mail.com>
To: john-users@...ts.openwall.com
Subject: Re: bash completion for john and unique
On 05/21/2012 10:24 AM, Frank Dittrich wrote:
> I created a bash completion script for John the Ripper which supports
> bash completion for john (official releases and jumbo versions)
Unfortunately, nobody replied to that mail.
This either means, nobody is interested in bash completion for john, my
last mail was too confusing, or everybody was happy with my script and
didn't find any problems.
Anyway, meanwhile a lot has been changed.
Attached to this mail is the newest version of the script.
(This version is not yet available in magnum's git repository, but I
hope this will not take very long - unless magnum finds bugs in my code.)
The bash completion script now not only supports completion for
--option=val, but also for abbreviated forms --opt=val or -opt=val as
well as options with a colon instead of an equal sign as a separator:
--option:val, -opt:val, and so on.
The location of the script will also change when the next jumbo is released.
My previous mail in this thread describes how to enable bash completion,
and how to adjust completion according to your preferences:
http://openwall.com/lists/john-users/2012/05/21/1
Please test the bash completion, so that I can fix bugs prior to the
next jumbo release.
Frank
# bash completion for john and unique commands (John the Ripper)
#
# This software is Copyright © 2012 Frank Dittrich
# and hereby released to the general public under the following terms:
# Redistribution and use in source and binary forms, with or without
# modification, are permitted.
#
# Minor improvements suggested by Aleksey Cherepanov have been
# incorporated.
#
#
# This file needs to be copied into the /etc/bash_completion.d/.
# To make the new completion rules work, you need to logout and login,
# or source /etc/bash_completion instead.
#
# Alternatively, just add a line
# . <path_to_john's_source_directory>/john.bash_completion
# to your ~/.bashrc and logout/login.
#
# To use the same completion rules not just for john, but for
# differently named binaries (say john-omp, john-sse2i, john-avx,
# john-cuda, john-gpu, ...),
# just use this command to get the current completion rule settings:
# complete -p john
#
# If the output is
# complete -F _john john
# you can use this command to activate the same completion rules
# for john-omp:
# complete -F _john john-omp
#
# To use these completion rules permanently, you might add
# complete -F _john john-omp
# to your ~/.bashrc file.
#
#
# The code is still ugly, many things can probably be done more efficiently.
# Currently, grep, tr, and sed are the only external commands used.
# Trying to build a perfect completion script will be hard.
#
# If possible, I'd like to avoid the need to maintain this script whenever
# john gets a new option.
#
# john can either be globally installed (e.g. in /usr/bin/john),
# or it can be installed locally somewhere in a user's home directory.
# It can be an official version, or a community encanced (jumbo) version,
# with a variety of patches.
#
# FIXME: Is using __expand_tilde_by_ref OK?
#
# FIXME: For some reason completion for --option= only works
# if the cursor is at the end of the command,
# i.e. [[ ${COMP_POINT} -eq ${#COMP_LINE} ]]
# not if some words follow the one to be completed...
# If ${cur#*=} is not empty, completion works even in the middle
# of the command line
# This is annoying if I want to complete --rules= in
# ./john --rules= --config=test.conf
#
# FIXME: If there is a word -- preceding the current word
# which is to be completed, it cannot be an option, so file names
# should be used for completion.
#
# FIXME: Should completion for --make-charset really list existing .chr files?
#
# FIXME: Should I generally use LC_ALL=C, not just in a few places?
# (This could also be a little bit faster.)
#
# TODO:
# --wordlist=~user/filename or --wordlist=~/dir/file doesn't work,
# but pressing [tab] expands this to something useful
# Where to fix this? In john? Some bash config option?
#
# --external should not use all names of [List.External:..-.] sections, but
# just sections without a generate() function, if --wordlist, --incremental or --single
# is present on the command line; and just those with a generate() function,
# if none of these options is used - WHAT IF the user adds a --wordlist option later?
#
# Should expanding an abbreviated option to its long form also be done by john itself
# (requires new john option)?
# different implementations for completion logic for these options:
# --rules --single --incremental --restore --status
# for __john_completion=[2|any other value]
#
# john
## on my system, have() is a dummy function which always return "yes", so get rid of calling it...
## have grep && have sed && have tr
_john()
{
local first cur options valopts compreplya compreplyb encodings formats subformats list hidden dir cmd i ver ver1 ver2 ver3 prev words
COMPREPLY=()
if [[ "${COMP_WORDBREAKS}" == *:* ]] ; then
_get_comp_words_by_ref -n := cur prev words
else
_get_comp_words_by_ref -n = cur prev words
# If the colon is not part of COMP_WORDBREAKS, e.g., due to
# including this line into your ~/.bashrc, as mentioned in
# /etc/bash_completion ...
# export COMP_WORDBREAKS="${COMP_WORDBREAKS//:}"
# just replace : with = in -opt:val
if [[ "${cur}" == -*:* ]] ; then
cur="${cur//:/=}"
COMPREPLY=( $(compgen -W "${cur}" -- ${cur}) )
compopt -o nospace
return 0
fi
fi
# We need to make sure we run the correct program, not some other program
# called john which is located somewhere in $PATH
first="${COMP_WORDS[0]}"
# Most options are listed at the begin of the line, but the line with the --pipe option
# does have trailing spaces, and --stdin is mentioned after --wordlist=FILE.
#
# All options (the '=' will be removed for options with an optional value)
options=""
# FIXME: How do I suppress the error message if someone tries to be clever: cd run; ./john --[tab] ???
options="`${first} 2>/dev/null|sed -n '{ s#^ *\(--[a-z-]*=\?\(LIST\)\?\).*$#\1# }; /^--/ p'` --stdin"
if [[ "_${options}" == "_ --stdin" ]] ; then
_filedir_xspec 2> /dev/null
return 0
fi
# Just those options that can be used together with a value, even if that value is optional:
valopts=`${first} 2>/dev/null|grep '^ *--[a-z\[-]*='|grep -v '^ *--subformat='|sed 's#^ *\([a-z=-]*\).*$#\1#'`
# This is used to decide whether or not the completion should add a trailing space.
# (That means, for a jumbo build, --rules doesn't get a trailing space, but for the john version
# distributed by fedora16, --rules does get a trailing space during completion.
# The same applies for --show and single)
# now add the "hidden options" (not mentioned in the usage output, but in doc/OPTIONS and
# with --list=hidden-options
# Currently, all hidden options do have mandatory values (--option=value), this makes
# addition of these easier
hidden=""
hidden=`${first} --list=hidden-options 2>/dev/null|sed 's#^\(--[a-z-]*=\?\).*$#\1#'`
case "${cur}" in
-?(-)fo?(r|rm|rma|rmat)+(=|:)dynamic*)
if [[ "${options}" == *--subformat* ]] ; then
subformats=`${first} --subformat=LIST|sed 's#^\(User\)\?Format = \(dynamic_[0-9]*\).*$#\2#'`
cur=${cur#*[=:]}
COMPREPLY=( $(compgen -W "${subformats}" -- ${cur}) )
fi
return 0
;;
-?(-)fo?(r|rm|rma|rmat)+(=|:)dy?(n|na|nam|nami))
if [[ "${options}" == *--subformat* ]] ; then
if [[ "${cur#*f}" == o[=:]* || "${cur#*f}" == or[=:]* ]] ; then
if [[ `echo "${valopts}"|grep -c "^-*${cur%[=:]*}"` -ne 1 ]] ; then
return 0
fi
fi
cur=${cur#*[=:]}
COMPREPLY=( $(compgen -W "dynamic" -- ${cur}) )
compopt -o nospace
fi
return 0
;;
-?(-)fo?(r|rm|rma|rmat)+(=|:)*)
if [[ "${cur#*f}" == o[=:]* || "${cur#*f}" == or[=:]* ]] ; then
if [[ `echo "${valopts}"|grep -c "^-*${cur%[=:]*}"` -ne 1 ]] ; then
return 0
fi
fi
cur=${cur#*[=:]}
formats=`${first} |sed -n '/^--format/,$ { s#^--format=[ A-Za-z]*:##; /^--/ b; s#^ *##; s#\<dynamic_n\>#dynamic#; s#[/ ]#\n#g; p }'`
COMPREPLY=( $(compgen -W "${formats}" -- ${cur}) )
return 0
;;
--restore|--status)
if [[ "_${__john_completion}" == "_2" ]] ; then
COMPREPLY=( $(compgen -W "${cur}=" -- ${cur}) )
compopt -o nospace
else
prev="${cur}"
cur=""
_filedir "rec"
for (( i=0; i < ${#COMPREPLY[@... i++)); do
COMPREPLY[$i]="${prev}=${COMPREPLY[$i]%*.rec}"
done
COMPREPLY[${#COMPREPLY[@...="${prev}"
fi
return 0
;;
-?(-)re?(s|st|sto|stor|store)+(=|:)*|-?(-)sta?(t|tu|tus)+(=|:)*)
if [[ "${cur}" == -re[=:]* || "${cur}" == --re[=:]* ]] ; then
if [[ `echo "${valopts}"|grep -c "^--re"` -ne 1 ]] ; then
return 0
fi
fi
# If there is no .rec file in the current directory, the old completion logic will show all files:
##echo _`for f in *.rec; do echo ${f%.rec};done`_
cur=${cur#*[=:]}
# cd $JOHN/ or Private home for system-wide builds, if ./john --list=build-info works?
# NO, this would be wrong!
# .rec files are stored in the current directory (or a subdirectory if the session name contains a slash)
__expand_tilde_by_ref cur 2>/dev/null
_filedir "rec"
for (( i=0; i < ${#COMPREPLY[@... i++)); do
# Do I have to add the trailing / for directories? Apparently not!
COMPREPLY[$i]="${COMPREPLY[$i]%*.rec}"
done
return 0
;;
-?(-)w?(o|or|ord|ordl|ordli|ordlis|ordlist)+(=|:)*)
cur=${cur#*[=:]}
__expand_tilde_by_ref cur 2>/dev/null
_filedir
return 0
;;
--rules|--single)
if [[ "${valopts}" == *${cur}* ]] ; then
if [[ "_${__john_completion}" == "_2" ]] ; then
COMPREPLY=( $(compgen -W "${cur}=" -- ${cur}) )
compopt -o nospace
else
cmd=`echo ${COMP_LINE}|sed "s# ${cur}# --list=rules #"`
list=`${cmd} 2>/dev/null`
if [[ $? -ne 0 ]] ; then
list=`${first} --list=rules 2>/dev/null`
fi
if [[ $? -ne 0 ]] ; then
list="single wordlist NT"
fi
list=`echo "${list}"|sed 's# #\n#g'|sed "s#^\(.\)#${cur}=\1#"`
list="${list} ${cur}"
COMPREPLY=( $(compgen -W "${list}" -- ${cur}) )
fi
else
COMPREPLY=( $(compgen -W "${cur}" -- ${cur}) )
fi
return 0
;;
-?(-)ru?(l|le|les)+(=|:)*|-?(-)si?(n|ng|ngl|ngle)+(=|:)*)
# let's assume every john version which supports --single=
# also supports --rules=, and vice versa
if [[ "${valopts}" == *--rules* ]] ; then
cmd=`echo ${COMP_LINE}|sed "s# ${cur}# --list=rules #"`
list=`${cmd} 2>/dev/null`
if [[ $? -ne 0 ]] ; then
list=`${first} --list=rules 2>/dev/null`
fi
if [[ $? -eq 0 ]] ; then
cur=`echo ${cur#*[=:]}|LC_ALL=C tr A-Z a-z`
COMPREPLY=( $(compgen -W "${list}" -- ${cur}) )
else
cur=${cur#*[=:]}
COMPREPLY=( $(compgen -W "NT single wordlist" -- ${cur}) )
fi
fi
return 0
;;
-?(-)ex?(t|te|ter|tern|terna|ternal)+(=|:)*)
cmd=`echo ${COMP_LINE}|sed "s# ${cur}# --list=externals #"`
list=`${cmd} 2>/dev/null`
if [[ $? -ne 0 ]] ; then
list=`${first} --list=externals 2>/dev/null`
fi
if [[ $? -ne 0 ]] ; then
list="Filter_Alpha Filter_Digits Filter_Alnum Filter_LanMan LanMan Double Parallel Strip Keyboard"
ver=`${first} 2>/dev/null|sed -n '/^John the Ripper password cracker, ver/ s#^John the Ripper password cracker, ver[a-z :]*\([0-9.]*\).*$#\1#p'`
ver1=`echo $ver|sed 's#^\([0-9]*\).*$#\1#'`
ver2=`echo $ver|sed 's#^[0-9]*.\([0-9]*\).*$#\1#'`
ver3=`echo $ver|sed 's#^[0-9]*.[0-9]*.\([0-9]*\).*$#\1#'`
if [[ "_${ver3}" == "_" ]] ; then
ver3=0
fi
if [[ $ver1 -eq 1 && $ver2 -eq 7 ]] ; then
if [[ $ver3 -ge 3 ]] ; then
list="${list} DumbForce KnownForce"
fi
if [[ $ver3 -ge 7 ]] ; then
list="${list} DateTime Repeats Subsets AtLeast1-Simple AtLeast1-Generic Policy"
fi
if [[ $ver3 -ge 8 ]] ; then
list="${list} AppendLuhn"
fi
if [[ $ver3 -ge 9 ]] ; then
list="${list} AutoAbort AutoStatus"
fi
else
if [[ $ver1 -gt 1 || $ver1 -eq 1 && ver2 -gt 7 ]] ; then
list="${list} DumbForce KnownForce DateTime Repeats Subsets AtLeast1-Simple AtLeast1-Generic Policy AppendLuhn AutoAbort AutoStatus"
fi
fi
cur=${cur#*[=:]}
else
cur=`echo ${cur#*[=:]}|LC_ALL=C tr A-Z a-z`
fi
COMPREPLY=( $(compgen -W "${list}" -- ${cur}) )
return 0
;;
--incremental)
if [[ "_${__john_completion}" == "_2" ]] ; then
COMPREPLY=( $(compgen -W "${cur}=" -- ${cur}) )
compopt -o nospace
else
cmd=`echo ${COMP_LINE}|sed "s# ${cur}# --list=inc-modes #"`
list=`${cmd} 2>/dev/null`
if [[ $? -ne 0 ]] ; then
list=`${first} --list=inc-modes 2>/dev/null`
fi
if [[ $? -ne 0 ]] ; then
list="All Alpha Digits Alnum LanMan"
fi
list=`echo "${list}"|sed 's# #\n#g'|sed "s#^\(.\)#${cur}=\1#"`
list="${list} ${cur}"
COMPREPLY=( $(compgen -W "${list}" -- ${cur}) )
fi
return 0
;;
-?(-)i?(n|nc|ncr|ncre|ncrem|ncreme|ncremen|ncrement|ncrementa|ncremental)+(=|:)*)
cmd=`echo ${COMP_LINE}|sed "s# ${cur}# --list=inc-modes #"`
list=`${cmd} 2>/dev/null`
if [[ $? -ne 0 ]] ; then
list=`${first} --list=inc-modes 2>/dev/null`
fi
if [[ $? -eq 0 ]] ; then
cur=`echo ${cur#*[=:]}|LC_ALL=C tr A-Z a-z`
COMPREPLY=( $(compgen -W "${list}" -- ${cur}) )
else
cur=${cur#*[=:]}
COMPREPLY=( $(compgen -W "All Alpha Digits Alnum LanMan" -- ${cur}) )
fi
return 0
;;
-?(-)mak?(e|e-|e-c|e-ch|e-cha|e-char|e-chars|e-charse|e-charset)+(=|:)*)
cur=${cur#*[=:]}
#redirect stderr just in case __expand_tilde_by_ref
#doesn't exist everywhere
#(I'm a bit worried because of the __ at the begin.
#May be this function isn't part of an "official" API.)
__expand_tilde_by_ref cur 2>/dev/null
# FIXME: should I just use directories for completion, not files,
# FIXME: to make overwriting existing files harder?
_filedir "chr"
return 0
;;
--stdout)
COMPREPLY=( $(compgen -W "--stdout --stdout=LENGTH" -- ${cur}) )
return 0
;;
--markov)
if [[ "${valopts}" == *${cur}* ]] ; then
COMPREPLY=( $(compgen -W "--markov --markov=LEVEL[:START[:END[:LENGTH]]]" -- ${cur}) )
fi
return 0
;;
--test)
if [[ "${valopts}" == *${cur}* ]] ; then
COMPREPLY=( $(compgen -W "--test --test=SECONDS" -- ${cur}) )
else
COMPREPLY=( $(compgen -W "${cur}" -- ${cur}) )
fi
return 0
;;
--show)
if [[ "${valopts}" == *${cur}* ]] ; then
COMPREPLY=( $(compgen -W "--show --show=LEFT" -- ${cur}) )
else
COMPREPLY=( $(compgen -W "--show" -- ${cur}) )
fi
return 0
;;
-?(-)sho?(w)+(=|:)l*)
if [[ "${valopts}" == *--show* ]] ; then
cur=${cur#*[=:]}
COMPREPLY=( $(compgen -W "left" -- ${cur}) )
fi
return 0
;;
-?(-)sho?(w)+(=|:)*)
if [[ "${valopts}" == *--show* ]] ; then
cur=${cur#*[=:]}
COMPREPLY=( $(compgen -W "LEFT" -- ${cur}) )
fi
return 0
;;
--users=?(-)+(L|U)*|--groups=+(-|G)*|--shells=+(-|S)*|--salts=+(-|C)*)
return 0
;;
--users=?(-))
cur=${cur#*=}
COMPREPLY=( $(compgen -W "LOGIN,... UID,... -LOGIN,... -UID,..." -- ${cur}) )
return 0
;;
--groups=*)
cur=${cur#*=}
COMPREPLY=( $(compgen -W "GID,... -GID,..." -- ${cur}) )
return 0
;;
--shells=*)
cur=${cur#*=}
COMPREPLY=( $(compgen -W "SHELL,... -SHELL,..." -- ${cur}) )
return 0
;;
--salts=*)
cur=${cur#*=}
COMPREPLY=( $(compgen -W "COUNT -COUNT" -- ${cur}) )
return 0
;;
-?(-)en?(c|co|cod|codi|codin|coding)+(=|:)*)
if [[ "${valopts}" == *--encoding=* ]] ; then
# --encoding=LIST writes to stderr
encodings=`${first} --encoding=LIST 2>&1|grep -v 'Supported encodings'|sed 's#[,)]##g'|sed 's#(or ##g'`
cur=${cur#*[=:]}
if [[ ${COMP_CWORD} -eq 2 || ${COMP_CWORD} -eq 3 && "_${cur}" != "_" ]] ; then
encodings="${encodings} LIST"
# make sure LIST will be the first option:
LC_ALL=C
fi
COMPREPLY=( $(compgen -W "${encodings}" -- ${cur}) )
fi
return 0
;;
-?(-)po?(t)+(=|:)*)
if [[ "${valopts}" == *--pot=* ]] ; then
# if --pot= is used, john always looks for the file $PWD
# (tested with system-wide and local build of john)
cur=${cur#*[=:]}
#redirect stderr just in case __expand_tilde_by_ref
#doesn't exist everywhere
#(I'm a bit worried because of the __ at the begin.
#May be this function isn't part of an "official" API.)
__expand_tilde_by_ref cur 2>/dev/null
_filedir "pot"
fi
return 0
;;
-?(-)co?(n|nf|nfi|nfig)+(=|:)*)
if [[ "${valopts}" == *--config=* ]] ; then
# if --config= is used, john always looks for files in $PWD
# (tested for system-wide and local builds)
cur=${cur#*[=:]}
__expand_tilde_by_ref cur 2>/dev/null
_filedir '@...nf|ini)'
fi
return 0
;;
-?(-)sav?(e|e-|e-m|e-me|e-mem|e-memo|e-memor|e-memory)+(=|:)*)
cur=${cur#*[=:]}
COMPREPLY=( $(compgen -W "1 2 3" -- ${cur}) )
return 0
;;
-?(-)reg?(e|en|en-|en-l|en-lo|en-los|en-lost|en-lost-|en-lost-s|en-lost-sa|en-lost-sal|en-lost-salt|en-lost-salts)+(=|:)*)
if [[ "${valopts}" == *--regen-lost-salts=* ]] ; then
cur=${cur#*[=:]}
COMPREPLY=( $(compgen -W "1 2 3 4 5" -- ${cur}) )
fi
return 0
;;
-?(-)su?(b|bf|bfo|bfor|bform|bforma|bformat)+(=|:)l*)
if [[ "${options}" == *--subformat=* ]] ; then
cur=${cur#*[=:]}
COMPREPLY=( $(compgen -W "list" -- ${cur}) )
fi
return 0
;;
-?(-)su?(b|bf|bfo|bfor|bform|bforma|bformat)+(=|:)*)
if [[ "${options}" == *--subformat=* ]] ; then
cur=${cur#*[=:]}
COMPREPLY=( $(compgen -W "LIST" -- ${cur}) )
fi
return 0
;;
-?(-)pla?(t|tf|tfo|tfor|tform)+(=|:)+(L|l)*|-?(-)dev?(i|ic|ice)+(=|:)+(L|l)*)
# CUDA doesn't allow --device=LIST
# workaround: check if --platform= is allowed
if [[ "${valopts}" == *--platform=* ]] ; then
cur=${cur#*[=:]}
COMPREPLY=( $(compgen -W "LIST list" -- ${cur}) )
fi
return 0
;;
-?(-)platform+(=|:)|-?(-)device+(=|:))
# --device=LIST isn't supported for CUDA, but for CUDA
# --platform= is not a valid option
if [[ "${valopts}" == *--platform=* ]] ; then
# Calling john --platform=LIST just to find possible completions
# will take too long
cur=${cur#*[=:]}
COMPREPLY=( $(compgen -W "LIST N" -- ${cur}) )
fi
return 0
;;
-?(-)l?(i|is|ist)+(=|:)*)
if [[ "${hidden}" == *--list=* ]] ; then
cur=${cur#*[=:]}
# the --list=? output changed, that's why a more complex regex is used
# to cover all cases
list=`${first} --list=? 2>/dev/null|sed 's#\(,\)\?\( or\)\?[ ]*[<].*$##; s#,##g'`
if [[ $? -eq 0 ]] ; then
# add "?" to the list of possible completions, but don't add any
# section names like "Options"...
COMPREPLY=( $(compgen -W "${list} ?" -- ${cur}) )
fi
fi
return 0
;;
-*+(=|:))
return 0;
;;
-*)
compreplya=`compgen -W "${options} ${hidden}" -- ${cur}`
if [[ "_${compreplya}_" == "__" ]] ; then
cur="-${cur}"
compreplya=`compgen -W "${options} ${hidden}" -- ${cur}`
fi
compreplyb=`compgen -W "${valopts} ${hidden}" -- ${cur}`
COMPREPLY=( $(compgen -W "${options} ${hidden}" -- ${cur}) )
if [[ "_${compreplya}" == "_${compreplyb}" ]] ; then
compopt -o nospace
fi
return 0
;;
*)
_filedir
return 0
;;
esac
} &&
complete -F _john john
# unique
## have grep && have sed &&
_unique()
{
local first cur usage options valopts compreplya compreplyb
COMPREPLY=()
_get_comp_words_by_ref -n = cur
# we need to make sure we run the correct program, not some other program
# called unique which is located somewhere in $PATH
first="${COMP_WORDS[0]}"
usage=`${first}|grep '^Usage:'|sed 's#^Usage:\? \?[^ ]*unique *##'`
case "_${cur}" in
_|_${first})
if [[ "_${usage}" != "_OUTPUT-FILE" ]] ; then
COMPREPLY=( $(compgen -W "${usage}" -- "") )
fi
return 0
;;
_-cut=*|_-mem=*)
return 0
;;
_-inp=*|_-ex_file=*|_-ex_file_only=*)
cur=${cur#*=}
__expand_tilde_by_ref cur 2>/dev/null
_filedir
return 0
;;
_-*)
if [[ "_${usage}_" != "_OUTPUT-FILE_" ]] ; then
options=`echo ${usage}|sed 's# #\n#g'|grep '^\[.*\]$'|sed 's#^.\(.*\).$#\1#'|sed 's#=.*$#=#'`
valopts=`echo "${options}"|grep '='`
compreplya=`compgen -W "${options}" -- ${cur}`
compreplyb=`compgen -W "${valopts}" -- ${cur}`
if [[ "_${compreplya}" == "_${compreplyb}" ]] ; then
COMPREPLY=( $(compgen -W "${valopts}" -- "${cur}") )
compopt -o nospace
else
COMPREPLY=( $(compgen -W "${options}" -- "${cur}") )
fi
fi
return 0
;;
_*)
return 0
;;
esac
} &&
complete -F _unique unique
Powered by blists - more mailing lists
Powered by Openwall GNU/*/Linux -
Powered by OpenVZ