Subversion Repositories pub

Compare Revisions

No changes between revisions

Ignore whitespace Rev 205 → Rev 216

/relevation/trunk/Makefile
File deleted
Property changes:
Deleted: svn:keywords
-Rev Id Date
\ No newline at end of property
/relevation/trunk/CHANGELOG
1,5 → 1,17
$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/relevation.1
42,8 → 42,12
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.
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.
.IP "\fB-h\fP, \fB\-\-help\fP " 10
Show summary of options.
.IP "\fB\-\-version\fP " 10
58,12 → 62,15
\ [relevation]
\ file=~/passwords.revelation
\ password=my secret password
\ mode=and
.fi
.PP
.PP
Both file and password are optional, so you can store the filename without storing the password.
Both \fBfile\fP and \fBpassword\fP 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)
72,4 → 79,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, Tue 05 Jul 2011, 02:25
.\" created by instant / docbook-to-man, Mon 21 Oct 2013, 06:49
/relevation/trunk/GNUmakefile
0,0 → 1,73
# $Id$
 
prefix:=/usr/local
DESTDIR:=
 
PKG=relevation
VERSION=$(shell printf '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
printf '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/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>June 28, 2011</date>">
<!ENTITY dhdate "<date>October 21, 2013</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</year>
<year>2011-2013</year>
<holder>&dhusername;</holder>
</copyright>
&dhdate;
121,12 → 121,26
<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.</para>
<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>
</listitem>
</varlistentry>
 
153,9 → 167,11
<para>Example `~/.relevation.conf':</para>
<programlisting>&nbsp;[relevation]
&nbsp;file=~/passwords.revelation
&nbsp;password=my secret password</programlisting>
<para>Both file and password are optional, so you can store the filename without storing the password.</para>
&nbsp;password=my secret password
&nbsp;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>
<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/devtools/genpw.py
5,7 → 5,7
"""
# Relevation Password Printer
#
# Copyright (c) 2011, Toni Corvera
# Copyright (c) 2011,2012 Toni Corvera
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
32,6 → 32,7
import random
import string
import locale
import os
import sys
 
try:
47,9 → 48,14
STDSET = string.uppercase + string.punctuation
DEFAULT_CSET = FAVOURED_SET * 8 + STDSET
 
REJECTS_SET = [ 'l', '1', 'I', '0', 'O' ] # FIXME: What else?
# Taken from pwgen's list ("B8G6I1l0OQDS5Z2")
REJECTS_SET = [ 'B','8', 'G','6', 'I','1','l', '0','O','Q','D', 'S','5', 'Z','2' ]
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):
'''
119,7 → 125,7
if len(positional) > 1:
rounds = int(positional[1])
except ValueError:
sys.stderr.write('Usage: pwgen [-B] [length] [num pw]\n');
sys.stderr.write('Usage: %s [-B] [length] [num pw]\n' % os.path.basename(sys.argv[0]));
sys.exit(2);
 
def newpw():
130,9 → 136,14
if DO_CHECK:
( score, verdict, _ ) = checkpw.check(pw)
if secure:
while score < checkpw.STRONG_THRESHOLD:
strikes_left = STRIKES
while strikes_left > 0 and 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,10 → 12,11
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, Toni Corvera
# Copyright (c) 2011,2012,2013 Toni Corvera
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
41,9 → 42,11
 
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
61,13 → 64,13
from crypto.cipher import rijndael, cbc
from crypto.cipher.base import noPadding
except ImportError:
sys.stderr.write('Either PyCrypto or cryptopy are required\n')
sys.stderr.write('Either PyCrypto or cryptopy is required\n')
raise
 
__author__ = 'Toni Corvera'
__date__ = '$Date$'
__revision__ = '$Rev$'
__version_info__ = ( 1, 1 ) #, 0 )
__version_info__ = ( 1, 2 ) #, 0 )
__version__ = '.'.join(map(str, __version_info__))
RELEASE=True
 
98,15 → 101,13
'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):
113,6 → 114,8
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')
124,6 → 127,10
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')
133,6 → 140,11
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:
140,12 → 152,29
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()'
if ignore_case:
xpath = '%s[contains(translate(., "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"), "%s")]/../..' % ( xpath, search_text )
#xpath = xpath + '//text()'
needles = []
if type(search_text) == str:
needles = [ search_text, ]
else:
xpath = '%s[contains(., "%s")]/../..' % ( xpath, search_text )
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)
return xpath
 
def dump_all_entries(xmldata):
155,7 → 184,11
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 from xmldata that match criteria
dump_entries(str, str, str, bool, bool) -> int
or
dump_entries(str, list, str, bool, bool) -> int
'''
tree = etree.fromstring(xmldata)
xpath = make_xpath_query(search_text, type_filter, ignore_case, negate_filter)
try:
178,10 → 211,19
nr = dump_result(res, query_desc)
return nr
 
def print_wrapper(s):
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'
print s
 
def dump_result(res, query_desc, printfn=print_wrapper):
def dump_result(res, query_desc, dumpfn=dump_single_result):
''' Print query results.
dump_result(list of entries, query description) -> int
'''
191,23 → 233,32
return False
print '%d matches' % len(res)
for x in res:
printe('-------------------------------------------------------------------------------')
s = '\n'
s += 'Type: %s\n' % x.get('type')
typeName = x.get('type')
name = None
descr = None
fields = []
notes = None
for chld in x.getchildren():
n = chld.tag
val = chld.text
if val is None:
val = ''
if n == 'name':
s += 'Name: %s\n' % val
name = val
elif n == 'description':
s += 'Description: %s\n' % val
descr = val
elif n == 'field':
idv = chld.get('id')
if idv in TAGNAMES:
idv = TAGNAMES[idv]
s += '%s %s\n' % ( idv, chld.text )
#s += '\n'
printfn(s)
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)
# / for chld in x.children
nr = len(res)
plural = ''
232,6 → 283,7
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)
246,9 → 298,14
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 )
return ( fl, pw, mode )
 
def decrypt_gz(key, cipher_text):
''' Decrypt cipher_text using key.
282,17 → 339,19
# 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 Toni Corvera\n' % __version__)
printe('Relevation v%s, (c) 2011-2013 Toni Corvera\n' % __version__)
 
# ---------- OPTIONS ---------- #
( datafile, password ) = load_config()
( datafile, password, mode ) = load_config()
try:
# gnu_getopt requires py >= 2.3
ops, args = getopt.gnu_getopt(argv, 'f:p:s:0ciaht:x',
ops, args = getopt.gnu_getopt(argv, 'f:p:s:0ciaht:xAO',
[ 'file=', 'password=', 'search=', 'stdin',
'case-sensitive', 'case-insensitive', 'ask',
'help', 'version', 'type=', 'xml' ])
'help', 'version', 'type=', 'xml',
'and', 'or' ])
except getopt.GetoptError, err:
print str(err)
usage(sys.stderr)
323,10 → 382,16
elif opt in ( '-p', '--password' ):
password = arg
elif opt in ( '-a', '--ask', '-0', '--stdin' ):
prompt = ''
if opt in ( '-a', '--ask' ):
printen('File password: ')
password = sys.stdin.readline()
password = password[:-1]
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?
elif opt in ( '-s', '--search' ):
needles.append(arg)
elif opt in ( '-i', '--case-insensitive' ):
345,6 → 410,10
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"
386,13 → 455,23
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
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:
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 ( sfilter, negate ) in searchTypes:
numhits += dump_entries(xmldata, text, sfilter, caseInsensitive,
negate_filter=negate)
numhits += dump_entries(xmldata, needles, 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,3 → 1,9
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/.
Property changes:
Modified: svn:mergeinfo
Merged /relevation/branches/1.2:r207-215