forked from brettviren/python-keepass
-
Notifications
You must be signed in to change notification settings - Fork 1
/
keepassc
executable file
·127 lines (108 loc) · 4.16 KB
/
keepassc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
Command line interface to read keepass v4 (.kdbx) files
'''
import sys
import os
from keepasslib import baker
def get_password(prompt, twice=False):
"""Get password with getpass(), asking for it twice if needed."""
import getpass
password1 = None
if twice:
password2 = None
while password1 is None or (password1 != password2):
password1 = getpass.getpass(prompt+': ')
password2 = getpass.getpass(prompt+' (again):')
if password1 != password2:
sys.stderr.write("Error: Your passwords didn't match\n")
else:
password1 = getpass.getpass(prompt+': ')
return password1
def get_masterkey():
return get_password('Keepass masterkey')
def open_keepass_db(filename):
"""Open a keepass database with password."""
import libkeepass
if not os.path.isfile(filename):
raise baker.CommandError("keepass database %r not found" % filename)
if not filename.lower().endswith('.kdbx'):
raise baker.CommandError("filename %r has no .kdbx extension for keepass v4 file" % filename)
print "Opening keepass database", os.path.basename(filename), "..."
return libkeepass.open(filename, password=get_masterkey())
shortopts = {'show-passwords': 'p'}
params = {'show-passwords': 'Show passwords as plain text.'}
@baker.command(shortopts=shortopts, params=params)
def dump(dbfile, *dbfiles, **kwargs):
"""Print contents of keepass databases."""
allfiles = (dbfile,) + dbfiles
show_passwords = kwargs.get('show-passwords')
for filename in allfiles:
with open_keepass_db(filename) as db:
if not show_passwords:
db.protect()
print db.pretty_print()
@baker.command(default=True, shortopts=shortopts, params=params)
def search(key, dbfile, *dbfiles, **kwargs):
"""Searches given key in all entries in keepass databases."""
allfiles = (dbfile,) + dbfiles
show_passwords = kwargs.get('show-passwords')
for filename in allfiles:
with open_keepass_db(filename) as db:
for entry in db.obj_root.findall('.//Entry'):
if entry_matches(entry, key):
print format_entry(entry, show_passwords)
def get_entry_value(entry, key):
"""Return matching value of given String key."""
for node in entry.findall('String'):
if node.find('Key').text == key:
value = node.find('Value').text
return value if value else ''
raise KeyError('no key %s in %s' % (key, entry))
def get_folder_name(entry):
"""Return folder name of given entry.
The parent XML folder node always has <Name> sibling above the current
entry.
"""
for node in entry.itersiblings(preceding=True):
if node.tag == u'Name':
return node.text
raise KeyError('no folder for entry %s' % entry)
def entry_matches(entry, substring):
"""An entry is an lxml node object with the following XML structure:
<Entry>
<UUID>...</UUID>
<String>
<Key>Title</Key>
<Value>...</Value>
</String>
<String>
<Key>Notes</Key>
<Value>...</Value>
</String>
</Entry>
We search in the title, notes and folder name for the given substring.
The search is case insensitive. Deleted entries in the Backup folder are
ignored.
"""
if get_folder_name(entry) == 'Backup':
return False
key = substring.lower()
return key in get_entry_value(entry, 'Title').lower() or \
key in get_entry_value(entry, 'Notes').lower() or \
key in get_folder_name(entry).lower()
def format_entry(entry, show_passwords):
title = get_entry_value(entry, 'Title')
username = get_entry_value(entry, 'UserName')
notes = get_entry_value(entry, 'Notes')
password = get_entry_value(entry, 'Password') if show_passwords else ''
folder = get_folder_name(entry)
template = "%(folder)s/%(title)s: user=%(username)r pass=%(password)r notes=%(notes)r"
return template % locals()
try:
sys.exit(baker.run())
except baker.CommandError, msg:
print >>sys.stderr, "keepassc error:", msg
baker.help(sys.argv[0])
sys.exit(1)