Subversion Repositories pub

Compare Revisions

No changes between revisions

Regard whitespace Rev 611 → Rev 610

/relevation/trunk/MANIFEST.in
File deleted
Property changes:
Deleted: svn:keywords
-Rev Id Date
\ No newline at end of property
/relevation/trunk/src/relevation/PBKDF2.py
File deleted
/relevation/trunk/src/gui.py
File deleted
Property changes:
Deleted: svn:executable
-*
\ No newline at end of property
Deleted: svn:keywords
-Rev Id Date
\ No newline at end of property
/relevation/trunk/src/relevation.py
File deleted
Property changes:
Deleted: svn:executable
Deleted: svn:keywords
-Rev Id Date
\ No newline at end of property
/relevation/trunk/GNUmakefile
4,7 → 4,7
DESTDIR:=
 
PKG=relevation
VERSION=$(shell bash devtools/relver.bash 2>/dev/null | awk '{ print $$1 }')
VERSION=$(shell printf 'import relevation\nprint relevation.__version__' | python - && $(RM) $(PKG).pyc)
PKGVER=$(PKG)-$(VERSION)
PACKAGER:=$(shell finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3)
ifeq ($(PACKAGER),)
14,101 → 14,43
--exclude=*.swp --exclude=*.pyo --exclude=*.pyc \
--exclude=manpage.html --exclude=manpage.pdf
 
IS_RELEASE=$(shell printf 'import relevation\nif not relevation.RELEASE:\n\traise Exception("RELEASE is False")' | python - )
 
 
INSTALLROOT=$(DESTDIR)$(prefix)
MANROOT=$(INSTALLROOT)/share/man
# Where to install additional packages
PYTHONROOT=$(DESTDIR)$(shell python -c "import sys;from distutils import sysconfig; print sysconfig.get_python_lib(0,0,prefix='$(prefix)')")
DUSETUP=python setup.py # Distutils' setup
# Debian derived systems need special treatment for clean uninstalls
# since it uses different than standard directories
INSTALL_LAYOUT=
ifeq ($(findstring dist-packages,$(PYTHONROOT)),dist-packages)
INSTALL_LAYOUT=--install-layout=deb
endif
 
 
all: $(PKG).1
@echo
@echo "########################################################"
@echo "# TARGETS:"
@echo "# install"
@echo "# uninstall"
@echo "# zip (-> $(PKGVER).zip)"
@echo "# tarball (-> $(PKGVER).tar.gz)"
@echo "# rpm"
@echo "# dist (-> tarball + zip)"
@echo "# clean"
@echo "# distclean"
@echo "# testman Displays compiled manpage"
@echo "# test_min_python Find min. Python version required"
@echo "########################################################"
@echo
 
testman:
docbook-to-man manpage_source.sgml | nroff -man | less
docbook-to-man manpage.sgml | nroff -man | less
 
testmake:
@echo VERSION=$(VERSION)
@echo PKGVER=$(PKGVER)
@echo PACKAGER=$(PACKAGER)
@echo prefix=$(prefix)
@echo DESTDIR=$(DESTDIR)
@echo INSTALLROOT=$(INSTALLROOT)
@echo MANROOT=$(MANROOT)
@echo PYTHONROOT=$(PYTHONROOT)
@echo INSTALL_LAYOUT=$(INSTALL_LAYOUT)
clean:
-$(RM) *.pyc *.pyo manpage.html manpage.pdf $(PKG).spec
 
test_min_python: pyqver2.py
python pyqver2.py -v -m 2.4 src/$(PKG).py src/$(PKG)/*.py
distclean: clean
-$(RM) $(PKGVER).tar.gz $(PKGVER).zip
-$(RM) -r dist build $(PKGVER)/
 
install: setup.py
$(DUSETUP) install --prefix=$(DESTDIR)$(prefix) $(INSTALL_LAYOUT)
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
 
# There's no distutils uninstall
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
-$(RM) $(PYTHONROOT)/$(PKG)/*
-$(RM) $(PYTHONROOT)/$(PKGVER).egg-info
-rmdir --parents $(INSTALLROOT)/share/doc/$(PKG)/extra/ \
$(MANROOT)/man1/ \
$(INSTALLROOT)/bin/ \
$(PYTHONROOT)/$(PKG)/
$(INSTALLROOT)/bin/
 
clean:
-$(RM) *.pyc *.pyo manpage.html manpage.pdf $(PKG).spec setup.py
 
distclean: clean
-$(RM) $(PKGVER).tar.gz $(PKGVER).zip
-$(RM) -r dist build $(PKGVER)/ pyqver2.py
 
dist: tarball zip
-$(RM) -r $(PKGVER)
 
tarball: $(PKGVER).tar.gz
 
zip: $(PKGVER).zip
 
rpm: $(PKGVER).tar.gz $(PKG).spec
@# XXX: ??? Combined doesn't catch Build-Requires
rpmbuild -tb $< && rpmbuild -ts $<
 
$(PKGVER).tar.gz: is_release distclean $(PKG).spec setup.py
$(DUSETUP) sdist -u root -g root --formats=gztar && mv dist/$@ .
-@rmdir dist
 
$(PKGVER).zip: is_release distclean manpage.pdf manpage.html $(PKG).spec setup.py
@# Specifial manifest with additional files
cp MANIFEST.in MANIFEST.in.tmp
echo include manpage.pdf manpage.html >> MANIFEST.in.tmp
$(DUSETUP) sdist --formats=zip --template MANIFEST.in.tmp && mv dist/$@ .
-$(RM) MANIFEST.in.tmp MANIFEST
-@rmdir dist
 
setup.py: devtools/setup.py.in
sed 's/@VERSION@/$(VERSION)/g' < $< > $@
 
$(PKG).1: manpage_source.sgml
docbook-to-man $< > $@
 
120,7 → 62,7
 
is_release:
# Only allowed if RELEASE
bash devtools/relver.bash | grep -q -v 'DEBUG'
printf 'import relevation\nif not relevation.RELEASE:\n\traise Exception("RELEASE is False")' | python -
 
package_copy:
@# Make a temporary copy to package
127,6 → 69,19
-mkdir $(PKGVER)
tar c . $(TAR_EXCLUDES) | ( cd $(PKGVER) && tar x )
 
dist: $(PKGVER).tar.gz $(PKGVER).zip
-$(RM) -r $(PKGVER)
 
$(PKGVER).tar.gz: is_release distclean $(PKG).spec package_copy
tar cv $(PKGVER) | gzip -c9 > $(PKGVER).tar.gz
 
zip: $(PKGVER).zip
 
$(PKGVER).zip: is_release distclean manpage.pdf manpage.html $(PKG).spec package_copy
cp manpage.pdf manpage.html $(PKGVER)
zip -9 -r $(PKGVER).zip $(PKGVER)
-$(RM) $(PKGVER)/manpage.pdf $(PKGVER)/manpage.html
 
$(PKG).spec: $(PKG).spec.in
test -n "$(VERSION)" # Version (=$(VERSION)) must be defined
@echo "[creating $@]"
133,10 → 88,7
@cat "$<" | sed -e 's/@VERSION@/$(VERSION)/g' \
-e 's/@PACKAGER@/$(PACKAGER)/g' > "$@"
 
pyqver2.py:
wget https://github.com/ghewgill/pyqver/raw/master/pyqver2.py
 
exe:
python win/setup_py2exe.py py2exe
python setup_py2exe.py py2exe
 
.PHONY: testman clean distclean dist exe is_release zip
/relevation/trunk/CHANGELOG
1,11 → 1,5
$Date$
 
1.3 (?):
- Check file magic [#230] and reject unsupported data formats
- Support the new data file format [#228]
- Added extra checks for data integrity
- Actually import traceback in pre-releases
 
1.2.1 (2013-11-05):
- Minimal GUI Fixes:
- Updated to the changes in 1.2
/relevation/trunk/relevation.py
0,0 → 1,500
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
 
"""
Relevation Password Printer
a command line interface to Revelation Password Manager.
 
Code based on Revelation's former BTS (no longer online, not archived?):
(ref1) code:
http://oss.wired-networks.net/bugzilla/attachment.cgi?id=13&action=view
(ref2) bug report:
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/
 
$Id$
"""
# Relevation Password Printer
#
# Copyright (c) 2011,2012,2013 Toni Corvera
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
 
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
# <http://www.py2exe.org/index.cgi/WorkingWithVariousPackagesAndModules>
import lxml._elementpath as _dummy
import gzip # py2exe again
 
USE_PYCRYPTO = True
 
try:
from Crypto.Cipher import AES
except ImportError:
USE_PYCRYPTO = False
try:
from crypto.cipher import rijndael, cbc
from crypto.cipher.base import noPadding
except ImportError:
sys.stderr.write('Either PyCrypto or cryptopy is required\n')
raise
 
__author__ = 'Toni Corvera'
__date__ = '$Date$'
__revision__ = '$Rev$'
__version_info__ = ( 1, 2 , 1 ) # Note: For x.y.0, only x and y are kept
__version__ = '.'.join(map(str, __version_info__))
RELEASE=True
 
# These are pseudo-standardized exit codes, in Linux (*NIX?) they are defined
#+in the header </usr/include/sysexits.h> and available as properties of 'os'
#+In windows they aren't defined at all
 
if 'EX_OK' not in dir(os):
# If not defined set them manually
codes = { 'EX_OK': 0, 'EX_USAGE': 64, 'EX_DATAERR': 65,
'EX_NOINPUT': 66, 'EX_SOFTWARE': 70, 'EX_IOERR': 74,
}
for (k,v) in codes.items():
setattr(os, k, v)
del codes, k, v
 
TAGNAMES ={ 'generic-url': 'Url:',
'generic-username': 'Username:',
'generic-password': 'Password:',
'generic-email': 'Email:',
'generic-hostname': 'Hostname:',
'generic-location': 'Location:',
'generic-code': 'Code:',
'generic-certificate': 'Certificate:',
'generic-database': 'Database:',
'generic-domain': 'Domain:',
'generic-keyfile': 'Key file:',
'generic-pin': 'PIN',
'generic-port': 'Port'
}
MODE_AND='and'
MODE_OR='or'
 
def printe(s):
' Print to stderr '
sys.stderr.write(s+'\n')
 
def usage(channel):
' Print help message '
def p(s):
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')
p(' -i, --case-insensitive Case insensitive search (default).\n')
p(' -c, --case-sensitive Case sensitive search.\n')
p(' -a, --ask Interactively ask for password.\n')
p(' Note it will be displayed in clear as you\n')
p(' type it.\n')
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')
p(' --version Print the program\'s version information.\n')
p('\n')
 
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:
sign = '='
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, ]
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)
return xpath
 
def dump_all_entries(xmldata):
' Dump all entries from xmldata, with no filter at all '
tree = etree.fromstring(xmldata)
res = tree.xpath('//entry')
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
'''
tree = etree.fromstring(xmldata)
xpath = make_xpath_query(search_text, type_filter, ignore_case, negate_filter)
try:
res = tree.xpath(xpath)
except etree.XPathEvalError:
if not RELEASE:
printe('Failed with xpath expression: %s' % xpath)
raise
query_desc = ''
if search_text:
query_desc = '"%s"' % search_text
if type_filter:
neg = ''
if negate_filter:
neg = 'not '
if search_text:
query_desc = '%s (\'%s%s\' entries)' % ( query_desc, neg, type_filter )
else:
query_desc = '%s%s entries' % ( neg, type_filter )
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'
print s
 
def dump_result(res, query_desc, dumpfn=dump_single_result):
''' Print query results.
dump_result(list of entries, query description) -> int
'''
print '-> Search %s: ' % query_desc,
if not len(res):
print 'No results'
return False
print '%d matches' % len(res)
for x in res:
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':
name = val
elif n == 'description':
descr = 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)
# / for chld in x.children
nr = len(res)
plural = ''
if nr > 1:
plural = 's'
printe('-------------------------------------------------------------------------------')
printe('<- (end of %d result%s for {%s})\n' % ( nr, plural, query_desc ))
return nr
 
def world_readable(path):
' Check if a file is readable by everyone '
assert os.path.exists(path)
if sys.platform == 'win32':
return True
st = os.stat(path)
return bool(st.st_mode & stat.S_IROTH)
 
def load_config():
''' Load configuration file is one is found
load_config() -> ( str file, str pass )
'''
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)
if wr and sys.platform != 'win32':
printe('Configuration (~/.relevation.conf) is world-readable!!!')
parser = ConfigParser.ConfigParser()
parser.read(cfg)
ops = parser.options('relevation')
if 'file' in ops:
fl = os.path.expanduser(parser.get('relevation', 'file'))
if 'password' in ops:
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 )
 
def decrypt_gz(key, cipher_text):
''' Decrypt cipher_text using key.
decrypt(str, str) -> cleartext (gzipped xml)
This function will use the underlying, available, cipher module.
'''
if USE_PYCRYPTO:
# Extract IV
c = AES.new(key)
iv = c.decrypt(cipher_text[12:28])
# Decrypt data, CBC mode
c = AES.new(key, AES.MODE_CBC, iv)
ct = c.decrypt(cipher_text[28:])
else:
# Extract IV
c = rijndael.Rijndael(key, keySize=len(key), padding=noPadding())
iv = c.decrypt(cipher_text[12:28])
# Decrypt data, CBC mode
bc = rijndael.Rijndael(key, keySize=len(key), padding=noPadding())
c = cbc.CBC(bc, padding=noPadding())
ct = c.decrypt(cipher_text[28:], iv=iv)
return ct
 
def main(argv):
datafile = None
password = None
# values to search for
needles = []
caseInsensitive = True
# 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__)
 
# ---------- OPTIONS ---------- #
( datafile, password, mode ) = load_config()
try:
# gnu_getopt requires py >= 2.3
ops, args = getopt.gnu_getopt(argv, 'f:p:s:0ciaht:xAO',
[ 'file=', 'password=', 'search=', 'stdin',
'case-sensitive', 'case-insensitive', 'ask',
'help', 'version', 'type=', 'xml',
'and', 'or' ])
except getopt.GetoptError, err:
print str(err)
usage(sys.stderr)
sys.exit(os.EX_USAGE)
if args:
needles = args
if ( '-h', '' ) in ops or ( '--help', '' ) in ops:
usage(sys.stdout)
sys.exit(os.EX_OK)
if ( '--version', '' ) in ops:
release=''
if not RELEASE:
release=' [DEBUG]'
print 'Relevation version %s%s' % ( __version__, release )
print 'Python version %s' % sys.version
if USE_PYCRYPTO:
import Crypto
print 'PyCrypto version %s' % Crypto.__version__
else:
# AFAIK cryptopy doesn't export version info
print 'cryptopy'
sys.exit(os.EX_OK)
for opt, arg in ops:
if opt in ( '-f', '--file' ):
datafile = arg
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?
elif opt in ( '-s', '--search' ):
needles.append(arg)
elif opt in ( '-i', '--case-insensitive' ):
caseInsensitive = True
elif opt in ( '-c', '--case-sensitive' ):
caseInsensitive = False
elif opt in ( '-t', '--type' ):
iarg = arg.lower()
neg = False
if iarg.startswith('-'):
iarg = iarg[1:]
neg = True
if not iarg in ( 'creditcard', 'cryptokey', 'database', 'door', 'email',
'folder', 'ftp', 'generic', 'phone', 'shell', 'website' ):
printe('Warning: Type "%s" is not known by relevation.' % arg)
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"
if not datafile or not password:
usage(sys.stderr)
if not datafile:
printe('Input password filename is required')
if not password:
printe('Password is required')
sys.exit(os.EX_USAGE)
# ---------- PASSWORDS FILE DECRYPTION AND DECOMPRESSION ---------- #
f = None
try:
if not os.access(datafile, os.R_OK):
raise IOError('File \'%s\' not accessible' % datafile)
f = open(datafile, "rb")
# Encrypted data
data = f.read()
finally:
if f:
f.close()
# Pad password
password += (chr(0) * (32 - len(password)))
# Decrypt. Decrypted data is compressed
cleardata_gz = decrypt_gz(password, data)
# Length of data padding
padlen = ord(cleardata_gz[-1])
# Decompress actual data (15 is wbits [ref3] DON'T CHANGE, 2**15 is the (initial) buf size)
xmldata = zlib.decompress(cleardata_gz[:-padlen], 15, 2**15)
# ---------- QUERIES ---------- #
if dump_xml:
print xmldata
sys.exit(os.EX_OK)
# Multiply values to search by type of searches
numhits = 0
 
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 ( sfilter, negate ) in searchTypes:
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)
if numhits == 0:
sys.exit(80)
 
if __name__ == '__main__':
try:
main(sys.argv[1:])
except zlib.error:
printe('Failed to decompress decrypted data. Wrong password?')
sys.exit(os.EX_DATAERR)
except etree.XMLSyntaxError as e:
printe('XML parsing error')
if not RELEASE:
traceback.print_exc()
sys.exit(os.EX_DATAERR)
except IOError as e:
if not RELEASE:
traceback.print_exc()
printe(str(e))
sys.exit(os.EX_IOERR)
 
# vim:set ts=4 et ai fileencoding=utf-8: #
Property changes:
Added: svn:executable
Added: svn:keywords
+Rev Id Date
\ No newline at end of property
/relevation/trunk/debian/changelog
1,9 → 1,3
relevation (1.3-pon.1) unstable; urgency=medium
 
* New version
 
-- Toni Corvera <outlyer@gmail.com> Wed, 21 May 2014 19:02:49 +0200
 
relevation (1.2.1-pon.1) unstable; urgency=low
 
* New version
/relevation/trunk/debian/control
2,16 → 2,15
Section: contrib/utils
Priority: extra
Maintainer: Toni Corvera <outlyer@gmail.com>
Build-Depends: debhelper (>= 7.0.50~), python-support (>= 0.90)
X-Python-Version: >= 2.5
Build-Depends: debhelper (>= 7.0.50~), python (>= 2.3)
Standards-Version: 3.9.1
Homepage: http://p.outlyer.net/relevation/
Vcs-Svn: https://svn.outlyer.net/svn/pub/relevation
Vcs-Browser: https://svn.outlyer.net/websvn/wsvn/pub/relevation/
#Vcs-Git: git://git.debian.org/collab-maint/relevation.git
#Vcs-Browser: http://git.debian.org/?p=collab-maint/relevation.git;a=summary
 
Package: relevation
Architecture: all
Depends: ${shlibs:Depends}, ${misc:Depends}, python-lxml, python-crypto, ${python:Depends}
Depends: ${shlibs:Depends}, ${misc:Depends}, python (>= 2.3), python-lxml, python-crypto
Recommends: revelation
Description: Command-line interface to query Revelation files
This is a command-line tool capable of retrieving passwords from
/relevation/trunk/debian/rules
9,12 → 9,9
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
 
# install dir shorthand
RLVINST=$(CURDIR)/debian/relevation
 
%:
dh $@
 
override_dh_auto_install:
$(MAKE) DESTDIR=$(RLVINST) prefix=/usr install
$(MAKE) DESTDIR=$(CURDIR)/debian/relevation prefix=/usr install
 
/relevation/trunk/debian/README.Debian
5,7 → 5,4
packaging scripts. Those are not written by a Debian developer
so they may inadvertently break packaging rules or conventions.
 
The minimum required Python version is set based on pyqver
(https://github.com/ghewgill/pyqver/tree/master)
 
-- Toni Corvera <outlyer@gmail.com> Fri, 23 May 2014 01:04:59 +0200
-- Toni Corvera <outlyer@gmail.com> Mon, 27 Jun 2011 01:44:10 +0200
/relevation/trunk/relevation.spec.in
1,14 → 1,3
# $Id$
# See http://fedoraproject.org/wiki/Packaging:Python
# Downstream specfile can be found at
# http://pkgs.fedoraproject.org/cgit/relevation.git/
 
%if 0%{?rhel} && 0%{?rhel} <= 6
%{!?__python2: %global __python2 /usr/bin/python2}
%{!?python2_sitelib: %global python2_sitelib %(%{__python2} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")}
%{!?python2_sitearch: %global python2_sitearch %(%{__python2} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(1))")}
%endif
 
Summary: Command-line search for Revelation Password Manager files
Name: relevation
Version: @VERSION@
20,7 → 9,6
Source: http://p.outlyer.net/relevation/files/relevation-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
BuildArch: noarch
BuildRequires: python2-devel
Requires: libxml2-python
Requires: python-crypto
Requires: python-lxml
29,33 → 17,34
Relevation is a tool to retrieve passwords stored in a password file in the
format used by Revelation, from the command-line instead of through a GUI.
 
 
%prep
%setup -q
 
 
%build
 
 
%install
rm -rf %{buildroot}
make install prefix=%{_prefix} DESTDIR=%{buildroot} INSTALL_LAYOUT=
make install prefix=%{_prefix} DESTDIR=%{buildroot}
# We include the extra tools as %%doc, remove from here
rm -rf %{buildroot}%{_prefix}/share/doc/relevation/extra/
 
 
%clean
rm -rf %{buildroot}
 
 
%files
%defattr(-,root,root,-)
%doc CHANGELOG LICENSE
%doc src/gui.py devtools/*.py
%doc gui.py devtools/*.py
%{_bindir}/relevation
%{_mandir}/man1/relevation.1*
%{python2_sitelib}/relevation/
%{python2_sitelib}/relevation-*.egg-info
 
 
%changelog
* Fri May 23 2014 Toni Corvera <outlyer@gmail.com> 1.3-1.pon
- Handle installation of new module
 
* Wed Oct 30 2013 Toni Corvera <outlyer@gmail.com> 1.2.1-1.pon
- Integrated spec from Fedora into upstream source
 
Property changes:
Deleted: svn:keywords
-Rev Id Date
\ No newline at end of property
/relevation/trunk/gui.py
0,0 → 1,194
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
 
"""
Relevation Password Printer
a command line interface to Revelation Password Manager.
 
Simplistic Graphical User Interface.
This GUI is mainly intended to be used in systems where command-lines
are less common, like Windows.
 
$Id$
"""
# Relevation Password Printer
#
# Copyright (c) 2011, 2013, Toni Corvera
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
 
import sys
import re
import Tkinter as tk
from Tkinter import Frame, Button, Entry, Listbox, Scrollbar, Label, Radiobutton, StringVar
 
import relevation
 
__author__ = 'Toni Corvera'
__date__ = '$Date$'
__revision__ = '$Rev$'
__version_info__ = ( 1, 2, 1 )
__version__ = '.'.join(map(str, __version_info__))
 
old_fn = relevation.dump_result
 
def append_result(typeName, name, descr, notes, fields):
global gui
gui.lst.insert(tk.END, name)
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
gui.items.append(s)
 
def dump_result_override(res, query_desc):
return old_fn(res, query_desc, append_result)
 
relevation.dump_result = dump_result_override
 
class ResultDialog:
def __init__(self, parent, result):
top = self.top = tk.Toplevel(parent)
 
self.value = tk.Text(top)
self.value.insert(tk.END, result)
self.value.config(state=tk.DISABLED)
self.value.pack()
b = Button(top, text='OK', command=self.ok)
b.pack(pady=5)
 
top.bind('<Return>', lambda event: self.ok())
top.focus_set()
 
def ok(self):
self.top.destroy()
 
class GUI(object):
def do_find(self):
global rootw
search = self.search_text.get()
self.lst.delete(0, tk.END)
self.items = []
mode = '-O'
args = sys.argv[1:] + [ '-s', ]
if self.mode.get() == relevation.MODE_AND:
mode = '-A'
search = search.split(' ')
args += search
args += [ mode, ]
else:
args += [ search, mode ]
print args
try:
relevation.main(args)
except SystemExit:
# No matches -> Exit with 80
gui.lst.insert(tk.END, '<No matches>')
self.items.append('<No passwords matched search>')
 
def display(self):
global rootw
selected = self.lst.curselection()
if not selected:
return
selected = int(selected[0])
item = self.items[selected]
print item
dlg = ResultDialog(rootw, item)
rootw.wait_window(dlg.top)
 
def __init__(self, master=None):
self.master = master
frame = Frame(master)
frame.pack(expand=1, fill=tk.BOTH)
self.items = []
self.frame = frame
#top = master
top = frame.winfo_toplevel()
top.rowconfigure(0, weight=1)
top.columnconfigure(0, weight=1, pad=5)
frame.rowconfigure(0, weight=1, pad=5)
frame.columnconfigure(0, weight=1)
 
# Avoid printing to stderr
def ignoreme(s):
pass
relevation.printe = ignoreme
FILL = tk.N+tk.S+tk.E+tk.W
BTNROW = 3
MODEROW = 2
RESROW = 1
# Populate
self.search_text = Entry(self.frame)
#self.search_text.pack({'expand': 1, 'side': 'top'})
self.search_text.grid(row=0, column=0, columnspan=4, padx=5, sticky=tk.N+tk.E+tk.W)
self.search_text.bind('<Return>', lambda event: self.do_find())
self.quit = Button(self.frame, text='Quit', fg='red', command=frame.quit)
#self.quit.pack(side=tk.LEFT)
self.quit.grid(row=BTNROW, column=0, padx=10)
self.search = Button(self.frame, text='Search', command=self.do_find)
#self.search.pack(side=tk.RIGHT)
self.search.grid(row=BTNROW, column=2, padx=5)
 
self.mode = StringVar()
 
mode = relevation.load_config()[2]
self.mode.set(mode)
 
rbO = Radiobutton(self.frame, text="OR/Literal", variable=self.mode, value=relevation.MODE_OR)
rbA = Radiobutton(self.frame, text="AND", variable=self.mode, value=relevation.MODE_AND)
rbO.grid(row=MODEROW, column=0)
rbA.grid(row=MODEROW, column=1)
self.view = Button(self.frame, text='View', command=self.display)
#self.view.pack(side=tk.RIGHT)
self.view.grid(row=BTNROW, column=1)
 
## FIXME
scrollbar = Scrollbar(self.frame, orient=tk.VERTICAL)
scrollbar.grid(row=RESROW, column=4, sticky=FILL)
self.lst = Listbox(self.frame)
#self.lst.pack()
self.lst.grid(row=RESROW, column=0, columnspan=3, sticky=FILL)
self.lst.bind('<Double-Button-1>', lambda event: self.display())
 
self.lst.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=self.lst.yview)
 
self.search_text.focus_set()
 
if __name__ == '__main__':
rootw = tk.Tk()
rootw.title('Relevation search v' + __version__)
gui = GUI(master=rootw)
rootw.mainloop()
#rootw.destroy()
 
# vim:set ts=4 et ai fileencoding=utf-8: #
Property changes:
Added: svn:executable
+*
\ No newline at end of property
Added: svn:keywords
+Rev Id Date
\ No newline at end of property
/relevation/trunk/devtools/relver.bash
File deleted
Property changes:
Deleted: svn:executable
-*
\ No newline at end of property
Deleted: svn:keywords
-Rev Id Date
\ No newline at end of property
/relevation/trunk/devtools/README
File deleted
/relevation/trunk/devtools/setup.py.in
File deleted
Property changes:
Deleted: svn:keywords
-Rev Id Date
\ No newline at end of property
/relevation/trunk/devtools/sample.datav1.revelation
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes:
Deleted: svn:mime-type
-application/octet-stream
\ No newline at end of property
/relevation/trunk/devtools/sample.datav2.revelation
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes:
Deleted: svn:mime-type
-application/octet-stream
\ No newline at end of property
/relevation/trunk
Property changes:
Modified: svn:ignore
relevation.spec
-setup.py
-MANIFEST
Modified: svn:mergeinfo
## -0,1 +0,0 ##
Reverse-merged /relevation/branches/1.3:r587-610