/relevation/trunk/GNUmakefile |
---|
File deleted |
Property changes: |
Deleted: svn:keywords |
-Rev Id Date |
\ No newline at end of property |
/relevation/trunk/CHANGELOG |
---|
1,17 → 1,5 |
$Date$ |
1.2 (2013-10-21): |
- Bugfix: Make case-insensitive search work on uppercase search |
strings |
- Print "notes" |
- Allow searches with "AND" operator: |
- OR is used by default, as in previous versions |
- New command-line flags: --and / -A and --or / -O |
- New config file option: mode (possibles values 'and' and 'or') |
- Use getpass when asking for a password to suspend echoing it (suggested |
by Jorge Gallegos) |
- Explicitly filter out folder entries when filtering by other types |
1.1 (2011-07-13): |
- Support cryptopy if PyCrypto is not available. Enhances |
cross-platform support. |
/relevation/trunk/Makefile |
---|
0,0 → 1,73 |
# $Id$ |
prefix:=/usr/local |
DESTDIR:= |
PKG=relevation |
VERSION=$(shell echo -e 'import relevation\nprint relevation.__version__' | python -) |
PKGVER=$(PKG)-$(VERSION) |
INSTALLROOT=$(DESTDIR)$(prefix) |
MANROOT=$(INSTALLROOT)/share/man |
all: $(PKG).1 |
testman: |
docbook-to-man manpage.sgml | nroff -man | less |
clean: |
-$(RM) *.pyc *.pyo manpage.html manpage.pdf |
distclean: clean |
-$(RM) $(PKGVER).tar.gz $(PKGVER).zip |
-$(RM) -r dist build |
install: |
install -D -m755 $(PKG).py $(INSTALLROOT)/bin/$(PKG) |
install -D -m644 $(PKG).1 $(MANROOT)/man1/$(PKG).1 |
# Extra tools |
install -d $(INSTALLROOT)/share/doc/$(PKG)/extra |
for tool in gui.py devtools/*.py; do \ |
install -D -m755 $$tool $(INSTALLROOT)/share/doc/$(PKG)/extra/ ; \ |
done |
uninstall: |
-$(RM) $(INSTALLROOT)/bin/$(PKG) $(INSTALLROOT)/share/man/man1/$(PKG).1 |
-for tool in gui.py devtools/*.py; do \ |
$(RM) $(INSTALLROOT)/share/doc/$(PKG)/extra/`basename $$tool` ; \ |
done |
-rmdir --parents $(INSTALLROOT)/share/doc/$(PKG)/extra/ \ |
$(MANROOT)/man1/ \ |
$(INSTALLROOT)/bin/ |
$(PKG).1: manpage_source.sgml |
docbook-to-man $< > $@ |
manpage.html: $(PKG).1 |
man2html $< | sed '1,2d' > $@ |
manpage.pdf: $(PKG).1 |
man -t ./$(PKG).1 | ps2pdf14 - > $@ |
TAR_EXCLUDES=--exclude-vcs --exclude=$(PKGVER) \ |
--exclude=*.swp --exclude=*.pyo --exclude=*.pyc |
is_release: |
# Only allowed if RELEASE |
echo -e 'import relevation\nif not relevation.RELEASE:\n\traise Exception("RELEASE is False")' | python - |
package_copy: |
@# Make a temporary copy to package |
-mkdir $(PKGVER) |
tar c . $(TAR_EXCLUDES) | ( cd $(PKGVER) && tar x ) |
dist: is_release distclean package_copy |
tar cv $(PKGVER) | gzip -c9 > $(PKGVER).tar.gz |
-$(RM) -r $(PKGVER) |
zip: is_release distclean manpage.pdf manpage.html package_copy |
zip -9 -r $(PKGVER).zip $(PKGVER) |
-$(RM) -r $(PKGVER) |
exe: |
python setup_py2exe.py py2exe |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/relevation/trunk/devtools/genpw.py |
---|
5,7 → 5,7 |
""" |
# Relevation Password Printer |
# |
# Copyright (c) 2011,2012 Toni Corvera |
# Copyright (c) 2011, Toni Corvera |
# All rights reserved. |
# |
# Redistribution and use in source and binary forms, with or without |
32,7 → 32,6 |
import random |
import string |
import locale |
import os |
import sys |
try: |
48,14 → 47,9 |
STDSET = string.uppercase + string.punctuation |
DEFAULT_CSET = FAVOURED_SET * 8 + STDSET |
# Taken from pwgen's list ("B8G6I1l0OQDS5Z2") |
REJECTS_SET = [ 'B','8', 'G','6', 'I','1','l', '0','O','Q','D', 'S','5', 'Z','2' ] |
REJECTS_SET = [ 'l', '1', 'I', '0', 'O' ] # FIXME: What else? |
DEFAULT_FORCE = '*' # Symbolic |
# The number of tries, when strong passwords are requested, before giving up |
# e.g. with length below 7 there won't be strong passwords |
STRIKES = 2048 |
def pwgen(length=DEFAULT_LENGTH, possible=DEFAULT_CSET, |
reject_ambiguous=False, force=DEFAULT_FORCE): |
''' |
125,7 → 119,7 |
if len(positional) > 1: |
rounds = int(positional[1]) |
except ValueError: |
sys.stderr.write('Usage: %s [-B] [length] [num pw]\n' % os.path.basename(sys.argv[0])); |
sys.stderr.write('Usage: pwgen [-B] [length] [num pw]\n'); |
sys.exit(2); |
def newpw(): |
136,14 → 130,9 |
if DO_CHECK: |
( score, verdict, _ ) = checkpw.check(pw) |
if secure: |
strikes_left = STRIKES |
while strikes_left > 0 and score < checkpw.STRONG_THRESHOLD: |
while score < checkpw.STRONG_THRESHOLD: |
pw = newpw() |
( score, verdict, _ ) = checkpw.check(pw) |
strikes_left -= 1 |
if strikes_left == 0: |
print "Giving up: Too many weak passwords" |
sys.exit(1) |
print '%s\t%d\t%s' % ( pw, score, verdict) |
#print _ |
else: |
/relevation/trunk/relevation.py |
---|
12,11 → 12,10 |
http://oss.wired-networks.net/bugzilla/show_bug.cgi?id=111 |
-> http://web.archive.org/http://oss.wired-networks.net/bugzilla/show_bug.cgi?id=111 |
(ref3) http://docs.python.org/library/zlib.html |
(ref4) http://pymotw.com/2/getpass/ |
""" |
# Relevation Password Printer |
# |
# Copyright (c) 2011,2012,2013 Toni Corvera |
# Copyright (c) 2011, Toni Corvera |
# All rights reserved. |
# |
# Redistribution and use in source and binary forms, with or without |
42,11 → 41,9 |
import ConfigParser |
import getopt |
import getpass |
from lxml import etree |
import os |
import stat |
import string |
import sys |
import zlib |
# Help py2exe in packaging lxml |
64,13 → 61,13 |
from crypto.cipher import rijndael, cbc |
from crypto.cipher.base import noPadding |
except ImportError: |
sys.stderr.write('Either PyCrypto or cryptopy is required\n') |
sys.stderr.write('Either PyCrypto or cryptopy are required\n') |
raise |
__author__ = 'Toni Corvera' |
__date__ = '$Date$' |
__revision__ = '$Rev$' |
__version_info__ = ( 1, 2 ) #, 0 ) |
__version_info__ = ( 1, 1 ) #, 0 ) |
__version__ = '.'.join(map(str, __version_info__)) |
RELEASE=True |
101,13 → 98,15 |
'generic-pin': 'PIN', |
'generic-port': 'Port' |
} |
MODE_AND='and' |
MODE_OR='or' |
def printe(s): |
' Print to stderr ' |
sys.stderr.write(s+'\n') |
def printen(s): |
' Print to stderr without added newline ' |
sys.stderr.write(s) |
def usage(channel): |
' Print help message ' |
def p(s): |
114,8 → 113,6 |
channel.write(s) |
p('%s {-f passwordfile} {-p password | -0} [search] [search2] [...]\n' % sys.argv[0]) |
p('\nOptions:\n') |
# Reference: 80 characters |
# ------------------------------------------------------------------------------- |
p(' -f FILE, --file=FILE Revelation password file.\n') |
p(' -p PASS, --password=PASS Master password.\n') |
p(' -s SEARCH, --search=SEARCH Search for string.\n') |
127,10 → 124,6 |
p(' -t TYPE, --type=TYPE Print only entries of type TYPE.\n') |
p(' With no search string, prints all entries of\n') |
p(' type TYPE.\n') |
p(' -A, --and When multiple search terms are used, use an AND\n') |
p(' operator to combine them.\n') |
p(' -O, --or When multiple search terms are used, use an OR\n') |
p(' operator to combine them.\n') |
p(' -x, --xml Dump unencrypted XML document.\n') |
p(' -0, --stdin Read password from standard input.\n') |
p(' -h, --help Print help (this message).\n') |
140,11 → 133,6 |
def make_xpath_query(search_text=None, type_filter=None, ignore_case=True, negate_filter=False): |
''' Construct the actual XPath expression |
make_xpath_query(str, str, bool, bool) -> str |
or |
make_xpath_query(list, str, bool, bool) -> str |
Passing a list as the second argument implies combining its elements |
in the search (AND) |
''' |
xpath = '/revelationdata//entry' |
if type_filter: |
152,29 → 140,12 |
if negate_filter: |
sign = '!=' |
xpath = '%s[@type%s"%s"]' % ( xpath, sign, type_filter ) |
if type_filter != 'folder': |
# Avoid printing folders since all their children are printed |
# alongside |
xpath += '[@type!="folder"]' |
if search_text: |
#xpath = xpath + '//text()' |
needles = [] |
if type(search_text) == str: |
needles = [ search_text, ] |
xpath = xpath + '//text()' |
if ignore_case: |
xpath = '%s[contains(translate(., "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"), "%s")]/../..' % ( xpath, search_text ) |
else: |
needles = search_text |
selector = '' |
for search in needles: |
if ignore_case: |
# must pass lowercase to actually be case insensitive |
search = string.lower(search) |
# XPath 2.0 has lower-case, upper-case, matches(..., -i) etc. |
selector += '//text()[contains(translate(., "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"), "%s")]/../..' % search |
else: |
selector += '//text()[contains(., "%s")]/../..' % search |
xpath = '%s%s' % ( xpath, selector ) |
if not RELEASE: |
printe("> Xpath: %s\n" % xpath) |
xpath = '%s[contains(., "%s")]/../..' % ( xpath, search_text ) |
return xpath |
def dump_all_entries(xmldata): |
184,11 → 155,7 |
return dump_result(res, 'all') |
def dump_entries(xmldata, search_text=None, type_filter=None, ignore_case=True, negate_filter=False): |
''' Dump entries from xmldata that match criteria |
dump_entries(str, str, str, bool, bool) -> int |
or |
dump_entries(str, list, str, bool, bool) -> int |
''' |
' Dump entries from xmldata that match criteria ' |
tree = etree.fromstring(xmldata) |
xpath = make_xpath_query(search_text, type_filter, ignore_case, negate_filter) |
try: |
211,19 → 178,10 |
nr = dump_result(res, query_desc) |
return nr |
def dump_single_result(typeName, name, descr, notes, fields): |
printe('-------------------------------------------------------------------------------') |
s = '\n' |
s += 'Type: %s\n' % typeName |
s += 'Name: %s\n' % name |
s += 'Description: %s\n' % descr |
s += 'Notes: %s\n' % notes |
for field in fields: |
s += '%s %s\n' % field # field, value |
#s += '\n' |
def print_wrapper(s): |
print s |
def dump_result(res, query_desc, dumpfn=dump_single_result): |
def dump_result(res, query_desc, printfn=print_wrapper): |
''' Print query results. |
dump_result(list of entries, query description) -> int |
''' |
233,32 → 191,23 |
return False |
print '%d matches' % len(res) |
for x in res: |
typeName = x.get('type') |
name = None |
descr = None |
fields = [] |
notes = None |
printe('-------------------------------------------------------------------------------') |
s = '\n' |
s += 'Type: %s\n' % x.get('type') |
for chld in x.getchildren(): |
n = chld.tag |
val = chld.text |
if val is None: |
val = '' |
if n == 'name': |
name = val |
s += 'Name: %s\n' % val |
elif n == 'description': |
descr = val |
s += 'Description: %s\n' % val |
elif n == 'field': |
idv = chld.get('id') |
if idv in TAGNAMES: |
idv = TAGNAMES[idv] |
val = chld.text |
if val is None: |
val = '' |
# Maintain order => list |
fields += [ ( idv, val ), ] |
elif n == 'notes': |
notes = val |
dumpfn(typeName, name, descr, notes, fields) |
s += '%s %s\n' % ( idv, chld.text ) |
#s += '\n' |
printfn(s) |
# / for chld in x.children |
nr = len(res) |
plural = '' |
283,7 → 232,6 |
cfg = os.path.join(os.path.expanduser('~'), '.relevation.conf') |
pw = None |
fl = None |
mode = MODE_OR |
if os.path.isfile(cfg): |
if os.access(cfg, os.R_OK): |
wr = world_readable(cfg) |
298,14 → 246,9 |
if wr: # TODO: how to check in windows? |
printe('Your password can be read by anyone!!!') |
pw = parser.get('relevation', 'password') |
if 'mode' in ops: |
mode = parser.get('relevation', 'mode') |
if mode not in [ MODE_AND, MODE_OR ]: |
printe('Warning: Unknown mode \'%s\' set in configuration' % mode) |
mode=MODE_OR |
else: # exists but not readable |
printe('Configuration file (~/.relevation.conf) is not readable!') |
return ( fl, pw, mode ) |
return ( fl, pw ) |
def decrypt_gz(key, cipher_text): |
''' Decrypt cipher_text using key. |
339,19 → 282,17 |
# individual search: ( 'value to search', 'type of search', 'type of entry to filter' ) |
searchTypes = [] |
dump_xml = False |
mode = None |
printe('Relevation v%s, (c) 2011-2013 Toni Corvera\n' % __version__) |
printe('Relevation v%s, (c) 2011 Toni Corvera\n' % __version__) |
# ---------- OPTIONS ---------- # |
( datafile, password, mode ) = load_config() |
( datafile, password ) = load_config() |
try: |
# gnu_getopt requires py >= 2.3 |
ops, args = getopt.gnu_getopt(argv, 'f:p:s:0ciaht:xAO', |
ops, args = getopt.gnu_getopt(argv, 'f:p:s:0ciaht:x', |
[ 'file=', 'password=', 'search=', 'stdin', |
'case-sensitive', 'case-insensitive', 'ask', |
'help', 'version', 'type=', 'xml', |
'and', 'or' ]) |
'help', 'version', 'type=', 'xml' ]) |
except getopt.GetoptError, err: |
print str(err) |
usage(sys.stderr) |
382,16 → 323,10 |
elif opt in ( '-p', '--password' ): |
password = arg |
elif opt in ( '-a', '--ask', '-0', '--stdin' ): |
prompt = '' |
if opt in ( '-a', '--ask' ): |
prompt = 'File password: ' |
# see [ref4] |
if sys.stdin.isatty(): |
password = getpass.getpass(prompt=prompt, stream=sys.stderr) |
else: |
# Not a terminal, getpass won't work |
password = sys.stdin.readline(); |
password = password[:-1] # XXX: would .rstrip() be safe enough? |
printen('File password: ') |
password = sys.stdin.readline() |
password = password[:-1] |
elif opt in ( '-s', '--search' ): |
needles.append(arg) |
elif opt in ( '-i', '--case-insensitive' ): |
410,10 → 345,6 |
searchTypes.append( ( iarg, neg ) ) |
elif opt in ( '-x', '--xml' ): |
dump_xml = True |
elif opt in ( '-A', '--and' ): |
mode = MODE_AND |
elif opt in ( '-O', '--or' ): |
mode = MODE_OR |
else: |
printe('Unhandled option: %s' % opt) |
assert False, "internal error parsing options" |
455,23 → 386,13 |
if not ( needles or searchTypes ): # No search nor filters, print all |
numhits = dump_all_entries(xmldata) |
elif not searchTypes: # Simple case, all searches are text searches |
if mode == MODE_OR: |
for text in needles: |
numhits += dump_entries(xmldata, text, 'folder', caseInsensitive, True) |
else: |
assert mode == MODE_AND, "Unknown boolean operation mode" |
numhits += dump_entries(xmldata, needles, 'folder', caseInsensitive, True) |
elif needles: |
if mode == MODE_OR: # Do a search filtered for each type |
for text in needles: |
for ( sfilter, negate ) in searchTypes: |
numhits += dump_entries(xmldata, text, sfilter, caseInsensitive, |
negate_filter=negate) |
else: # Do a combined search, filter for each type |
assert mode == MODE_AND, "Unknown boolean operation mode" |
for text in needles: |
numhits += dump_entries(xmldata, text, 'folder', caseInsensitive, True) |
elif needles: # Do a search filtered for each type |
for text in needles: |
for ( sfilter, negate ) in searchTypes: |
numhits += dump_entries(xmldata, needles, sfilter, caseInsensitive, |
negate_filter=negate) |
numhits += dump_entries(xmldata, text, sfilter, caseInsensitive, |
negate_filter=negate) |
else: # Do a search only of types |
for ( sfilter, negate ) in searchTypes: |
numhits += dump_entries(xmldata, None, sfilter, negate_filter=negate) |
/relevation/trunk/debian/changelog |
---|
1,9 → 1,3 |
relevation (1.2-pon.1) unstable; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Sat, 21 Oct 2013 00:52:41 +0100 |
relevation (1.1-upstream.1) unstable; urgency=low |
* New version |
/relevation/trunk/relevation.1 |
---|
42,12 → 42,8 |
If the search string contains special/non-English characters this is likely to fail. |
.IP "\fB-c\fP, \fB\-\-case-sensitive\fP " 10 |
When searching for text, obey case. |
.IP "\fB-A\fP, \fB\-\-and\fP " 10 |
When multiple search terms are used, use an AND operator to combine them. All search terms will be combined in a single search, only entries that match every search term will be selected. |
.IP "\fB-O\fP, \fB\-\-or\fP " 10 |
When multiple search terms are used, use an OR operator to combine them. A different search will be issued for each search term. This is the default and the original mode of operation. |
.IP "\fB-s \fIsearch string\fR\fP, \fB\-\-search=\fIsearch string\fR\fP, \fB\fIsearch string\fR\fP " 10 |
Search the file for a pice of text. All fields will be searched. There's no need to use the \fB-s\fP or \fB\-\-search\fP option names, any unnamed argument will be considered a search term. |
Search the file for a pice of text. All fields will be searched. |
.IP "\fB-h\fP, \fB\-\-help\fP " 10 |
Show summary of options. |
.IP "\fB\-\-version\fP " 10 |
62,15 → 58,12 |
\ [relevation] |
\ file=~/passwords.revelation |
\ password=my secret password |
\ mode=and |
.fi |
.PP |
.PP |
Both \fBfile\fP and \fBpassword\fP are optional, so you can store the filename without storing the password. |
Both file and password are optional, so you can store the filename without storing the password. |
.PP |
Please understand your password is stored in this file in clear text, modify the file permissions appropriately so that only your user can read it, otherwise your master password might be compromised and hence all your stored password will be too. |
.PP |
\fBmode\fP is optional ("\fIor\fR" by default), only the values \fIand\fR and \fIor\fR are recognized, corresponding to the matching \fB\-\-and\fP and \fB\-\-or\fP options (any other value will be ignored). |
.SH "SEE ALSO" |
.PP |
revelation (1) |
79,4 → 72,4 |
This manual page was written by Toni Corvera <outlyer@gmail.com>. |
Permission is granted to copy, distribute and/or modify this document under the terms of a BSD 2-clause license. |
.\" created by instant / docbook-to-man, Mon 21 Oct 2013, 06:49 |
.\" created by instant / docbook-to-man, Tue 05 Jul 2011, 02:25 |
/relevation/trunk/manpage_source.sgml |
---|
15,7 → 15,7 |
<!ENTITY dhfirstname "<firstname>Toni</firstname>"> |
<!ENTITY dhsurname "<surname>Corvera</surname>"> |
<!-- Please adjust the date whenever revising the manpage. --> |
<!ENTITY dhdate "<date>October 21, 2013</date>"> |
<!ENTITY dhdate "<date>June 28, 2011</date>"> |
<!ENTITY dhsection "<manvolnum>1</manvolnum>"> |
<!ENTITY dhemail "<email>outlyer@gmail.com</email>"> |
<!ENTITY dhusername "Toni Corvera"> |
34,7 → 34,7 |
&dhsurname; |
</author> |
<copyright> |
<year>2011-2013</year> |
<year>2011</year> |
<holder>&dhusername;</holder> |
</copyright> |
&dhdate; |
121,26 → 121,12 |
<listitem> |
<para>When searching for text, obey case.</para> |
</listitem> |
</varlistentry> |
</varlistentry> |
<varlistentry> |
<term><option>-A</option>, <option>--and</option> |
</term> |
<listitem> |
<para>When multiple search terms are used, use an AND operator to combine them. All search terms will be combined in a single search, only entries that match every search term will be selected.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-O</option>, <option>--or</option> |
</term> |
<listitem> |
<para>When multiple search terms are used, use an OR operator to combine them. A different search will be issued for each search term. This is the default and the original mode of operation.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-s <replaceable>search string</replaceable></option>, <option>--search=<replaceable>search string</replaceable></option>, <option><replaceable>search string</replaceable></option> |
</term> |
<listitem> |
<para>Search the file for a pice of text. All fields will be searched. There's no need to use the <option>-s</option> or <option>--search</option> option names, any unnamed argument will be considered a search term.</para> |
<para>Search the file for a pice of text. All fields will be searched.</para> |
</listitem> |
</varlistentry> |
167,11 → 153,9 |
<para>Example `~/.relevation.conf':</para> |
<programlisting> [relevation] |
file=~/passwords.revelation |
password=my secret password |
mode=and</programlisting> |
<para>Both <option>file</option> and <option>password</option> are optional, so you can store the filename without storing the password.</para> |
password=my secret password</programlisting> |
<para>Both file and password are optional, so you can store the filename without storing the password.</para> |
<para>Please understand your password is stored in this file in clear text, modify the file permissions appropriately so that only your user can read it, otherwise your master password might be compromised and hence all your stored password will be too.</para> |
<para><option>mode</option> is optional ("<replaceable>or</replaceable>" by default), only the values <replaceable>and</replaceable> and <replaceable>or</replaceable> are recognized, corresponding to the matching <option>--and</option> and <option>--or</option> options (any other value will be ignored).</para> |
</refsect1> |
<refsect1> |
<title>SEE ALSO</title> |
/relevation/trunk/. |
---|
Property changes: |
Modified: svn:mergeinfo |
Reverse-merged /relevation/branches/1.2:r207-215 |