rubeepass 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/rpass +238 -0
- data/lib/builtins/cd_wish.rb +56 -0
- data/lib/builtins/clear_wish.rb +24 -0
- data/lib/builtins/copy_wish.rb +134 -0
- data/lib/builtins/ls_wish.rb +60 -0
- data/lib/builtins/pwd_wish.rb +24 -0
- data/lib/builtins/show_wish.rb +79 -0
- data/lib/rubeepass/entry.rb +74 -0
- data/lib/rubeepass/error/invalid_gzip_error.rb +7 -0
- data/lib/rubeepass/error/invalid_header_error.rb +7 -0
- data/lib/rubeepass/error/invalid_magic_error.rb +7 -0
- data/lib/rubeepass/error/invalid_password_error.rb +7 -0
- data/lib/rubeepass/error/invalid_protected_data_error.rb +7 -0
- data/lib/rubeepass/error/invalid_protected_stream_key_error.rb +7 -0
- data/lib/rubeepass/error/invalid_version_error.rb +7 -0
- data/lib/rubeepass/error/not_aes_error.rb +7 -0
- data/lib/rubeepass/error/not_salsa20_error.rb +7 -0
- data/lib/rubeepass/error.rb +12 -0
- data/lib/rubeepass/group.rb +133 -0
- data/lib/rubeepass/protected_decryptor.rb +30 -0
- data/lib/rubeepass.rb +491 -0
- data/lib/string.rb +39 -0
- metadata +167 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8635dc28cb56a0bc98a6c2d8ac0f0029a1d16005
|
4
|
+
data.tar.gz: 83416771d1bf5a6ebd2d66cde4603b9de3d55cd5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ee4d0478d32fa683fbde3a7e80f9c42b48ce4e4248285e7ef7b094ee66c60918fd1c1563403fa7d59b6682a7a4670055853cbb8bae0fa7e66f453e5bce5d68a8
|
7
|
+
data.tar.gz: ee51c7575168d1727527690cb6d064f2d21fc2f6ff8ee877360744ec71590569dd14320ffb8467e6446e98878af97eb6bc2e8328da81a6e3e067b73311c7e5e1
|
data/bin/rpass
ADDED
@@ -0,0 +1,238 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "djinni"
|
4
|
+
require "io/console"
|
5
|
+
require "json"
|
6
|
+
require "optparse"
|
7
|
+
require "pathname"
|
8
|
+
require "rubeepass"
|
9
|
+
require "string"
|
10
|
+
|
11
|
+
class RPassExit
|
12
|
+
GOOD = 0
|
13
|
+
INVALID_OPTION = 1
|
14
|
+
INVALID_ARGUMENT = 2
|
15
|
+
MISSING_ARGUMENT = 3
|
16
|
+
EXTRA_ARGUMENTS = 4
|
17
|
+
KDBX_NOT_FOUND = 5
|
18
|
+
KDBX_NOT_READABLE = 6
|
19
|
+
KEYFILE_NOT_FOUND = 7
|
20
|
+
KEYFILE_NOT_READABLE = 8
|
21
|
+
KDBX_NOT_OPENED = 9
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_password
|
25
|
+
print "Enter password: "
|
26
|
+
passwd = STDIN.noecho(&:gets)
|
27
|
+
puts
|
28
|
+
return passwd.chomp
|
29
|
+
end
|
30
|
+
|
31
|
+
def parse(args)
|
32
|
+
options = Hash.new
|
33
|
+
options["export_file"] = nil
|
34
|
+
options["export_format"] = "xml"
|
35
|
+
options["password"] = nil
|
36
|
+
options["keyfile"] = nil
|
37
|
+
options["timeout"] = nil
|
38
|
+
|
39
|
+
parser = OptionParser.new do |opts|
|
40
|
+
opts.banner = "Usage: #{File.basename($0)} [OPTIONS] [kdbx]"
|
41
|
+
|
42
|
+
opts.on(
|
43
|
+
"-e",
|
44
|
+
"--export=FILE",
|
45
|
+
"Export database to file"
|
46
|
+
) do |file|
|
47
|
+
options["export_file"] = file
|
48
|
+
end
|
49
|
+
|
50
|
+
opts.on(
|
51
|
+
"-f",
|
52
|
+
"--format=FORMAT",
|
53
|
+
[ "gzip", "xml" ],
|
54
|
+
"Specify format to use when exporting (default: xml)"
|
55
|
+
) do |format|
|
56
|
+
options["export_format"] = format
|
57
|
+
end
|
58
|
+
|
59
|
+
opts.on("-h", "--help", "Display this help message") do
|
60
|
+
puts opts
|
61
|
+
exit RPassExit::GOOD
|
62
|
+
end
|
63
|
+
|
64
|
+
opts.on(
|
65
|
+
"-k",
|
66
|
+
"--keyfile=KEYFILE",
|
67
|
+
"Use specified keyfile"
|
68
|
+
) do |keyfile|
|
69
|
+
options["keyfile"] = Pathname.new(keyfile).expand_path
|
70
|
+
end
|
71
|
+
|
72
|
+
opts.on(
|
73
|
+
"-p",
|
74
|
+
"--password=PASSWORD",
|
75
|
+
"Use specified password (will prompt if not provided)"
|
76
|
+
) do |password|
|
77
|
+
options["password"] = password
|
78
|
+
end
|
79
|
+
|
80
|
+
opts.on("-t", "--timeout=TIMEOUT", "Clipboard timeout") do |t|
|
81
|
+
options["timeout"] = t.to_i
|
82
|
+
end
|
83
|
+
|
84
|
+
opts.on(
|
85
|
+
"",
|
86
|
+
"FORMATS",
|
87
|
+
"\tgzip",
|
88
|
+
"\txml"
|
89
|
+
)
|
90
|
+
end
|
91
|
+
|
92
|
+
begin
|
93
|
+
parser.parse!
|
94
|
+
rescue OptionParser::InvalidOption => e
|
95
|
+
puts e.message
|
96
|
+
puts parser
|
97
|
+
exit RPassExit::INVALID_OPTION
|
98
|
+
rescue OptionParser::InvalidArgument => e
|
99
|
+
puts e.message
|
100
|
+
puts parser
|
101
|
+
exit RPassExit::INVALID_ARGUMENT
|
102
|
+
rescue OptionParser::MissingArgument => e
|
103
|
+
puts e.message
|
104
|
+
puts parser
|
105
|
+
exit RPassExit::MISSING_ARGUMENT
|
106
|
+
end
|
107
|
+
|
108
|
+
if (args.length > 1)
|
109
|
+
puts parser
|
110
|
+
exit RPassExit::EXTRA_ARGUMENTS
|
111
|
+
end
|
112
|
+
|
113
|
+
# Read config
|
114
|
+
rc = read_rpassrc
|
115
|
+
|
116
|
+
# Determine kdbx and keyfile
|
117
|
+
if (args.length == 1)
|
118
|
+
# Use specified kdbx (and keyfile if specified)
|
119
|
+
options["kdbx"] = Pathname.new(args[0]).expand_path
|
120
|
+
else
|
121
|
+
# Use kdbx from config if stored
|
122
|
+
if (rc["last_kdbx"] && !rc["last_kdbx"].empty?)
|
123
|
+
options["kdbx"] = Pathname.new(
|
124
|
+
rc["last_kdbx"]
|
125
|
+
).expand_path
|
126
|
+
end
|
127
|
+
|
128
|
+
# Use keyfile from config if stored and not specified already
|
129
|
+
if (options["keyfile"].nil?)
|
130
|
+
if (rc["last_keyfile"] && !rc["last_keyfile"].empty?)
|
131
|
+
options["keyfile"] = Pathname.new(
|
132
|
+
rc["last_keyfile"]
|
133
|
+
).expand_path
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Determine timeout
|
139
|
+
if (options["timeout"].nil?)
|
140
|
+
if (rc["timeout"])
|
141
|
+
options["timeout"] = rc["timeout"]
|
142
|
+
else
|
143
|
+
options["timeout"] = 7
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Throw error if kdbx not specified or in config
|
148
|
+
if (options["kdbx"].nil?)
|
149
|
+
puts parser
|
150
|
+
exit RPassExit::MISSING_ARGUMENT
|
151
|
+
end
|
152
|
+
|
153
|
+
# Throw error if kdbx does not exist or is not readable
|
154
|
+
if (!options["kdbx"].exist?)
|
155
|
+
puts parser
|
156
|
+
exit RPassExit::KDBX_NOT_FOUND
|
157
|
+
elsif (!options["kdbx"].readable?)
|
158
|
+
puts parser
|
159
|
+
exit RPassExit::KDBX_NOT_READABLE
|
160
|
+
end
|
161
|
+
|
162
|
+
# Throw error if keyfile does not exist or is not readable
|
163
|
+
if (options["keyfile"])
|
164
|
+
if (!options["keyfile"].exist?)
|
165
|
+
puts parser
|
166
|
+
exit RPassExit::KEYFILE_NOT_FOUND
|
167
|
+
elsif (!options["keyfile"].readable?)
|
168
|
+
puts parser
|
169
|
+
exit RPassExit::KEYFILE_NOT_READABLE
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Store data in config
|
174
|
+
rc["last_kdbx"] = options["kdbx"]
|
175
|
+
rc["last_keyfile"] = options["keyfile"]
|
176
|
+
rc["timeout"] = options["timeout"]
|
177
|
+
write_rpassrc(rc)
|
178
|
+
|
179
|
+
return options
|
180
|
+
end
|
181
|
+
|
182
|
+
def read_rpassrc
|
183
|
+
default = Hash.new
|
184
|
+
default["last_kdbx"] = nil
|
185
|
+
default["last_keyfile"] = nil
|
186
|
+
default["timeout"] = 7
|
187
|
+
|
188
|
+
rc_file = Pathname.new("~/.rpassrc").expand_path
|
189
|
+
return default if (!rc_file.exist? && !rc_file.symlink?)
|
190
|
+
return JSON.parse(File.read(rc_file))
|
191
|
+
end
|
192
|
+
|
193
|
+
def write_rpassrc(rc)
|
194
|
+
rc_file = Pathname.new("~/.rpassrc").expand_path
|
195
|
+
File.open(rc_file, "w") do |f|
|
196
|
+
f.write(JSON.pretty_generate(rc))
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
options = parse(ARGV)
|
201
|
+
|
202
|
+
kdbx = options["kdbx"]
|
203
|
+
password = options["password"] if (options["password"])
|
204
|
+
password = get_password if (options["password"].nil?)
|
205
|
+
keyfile = options["keyfile"]
|
206
|
+
|
207
|
+
keepass = nil
|
208
|
+
begin
|
209
|
+
keepass = RubeePass.new(kdbx, password, keyfile).open
|
210
|
+
rescue RubeePass::Error => e
|
211
|
+
puts e.message
|
212
|
+
exit RPassExit::KDBX_NOT_OPENED
|
213
|
+
end
|
214
|
+
exit RPassExit::KDBX_NOT_OPENED if (keepass.nil?)
|
215
|
+
|
216
|
+
if (options["export_file"])
|
217
|
+
File.open(options["export_file"], "w") do |f|
|
218
|
+
case options["export_format"]
|
219
|
+
when "gzip"
|
220
|
+
f.write(keepass.gzip)
|
221
|
+
when "xml"
|
222
|
+
f.write(keepass.xml)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
exit RPassExit::GOOD
|
226
|
+
end
|
227
|
+
|
228
|
+
djinni = Djinni.new
|
229
|
+
djinni.load_wishes("#{File.dirname(__FILE__)}/../lib/builtins")
|
230
|
+
djinni.prompt(
|
231
|
+
{
|
232
|
+
"keepass" => keepass,
|
233
|
+
"cwd" => keepass.db,
|
234
|
+
"clipboard_timeout" => options["timeout"]
|
235
|
+
},
|
236
|
+
"rpass:/> ".white
|
237
|
+
)
|
238
|
+
exit RPassExit::GOOD
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "djinni"
|
2
|
+
require "string"
|
3
|
+
|
4
|
+
class CDWish < Djinni::Wish
|
5
|
+
def aliases
|
6
|
+
return [ "cd" ]
|
7
|
+
end
|
8
|
+
|
9
|
+
def description
|
10
|
+
return "Change to new group"
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute(args, djinni_env = {})
|
14
|
+
keepass = djinni_env["keepass"]
|
15
|
+
cwd = djinni_env["cwd"]
|
16
|
+
|
17
|
+
args = keepass.absolute_path(args, cwd.path)
|
18
|
+
new_cwd = keepass.find_group(args)
|
19
|
+
|
20
|
+
if (new_cwd)
|
21
|
+
djinni_env["cwd"] = new_cwd
|
22
|
+
prompt = "rpass:#{new_cwd.name}> ".white
|
23
|
+
djinni_env["djinni_prompt"] = prompt
|
24
|
+
else
|
25
|
+
puts "Group \"#{args}\" doesn't exist!"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def tab_complete(input, djinni_env = {})
|
30
|
+
cwd = djinni_env["cwd"]
|
31
|
+
input, groups = cwd.fuzzy_find(input)
|
32
|
+
return input.gsub(%r{^#{cwd.path}/?}, "") if (groups.empty?)
|
33
|
+
|
34
|
+
path, dest = input.rsplit("/")
|
35
|
+
|
36
|
+
if (dest.empty?)
|
37
|
+
if (groups.length == 1)
|
38
|
+
input = "#{path}/#{groups.first}/"
|
39
|
+
return input.gsub(%r{^#{cwd.path}/?}, "")
|
40
|
+
end
|
41
|
+
puts
|
42
|
+
groups.each do |group|
|
43
|
+
puts "#{group}/"
|
44
|
+
end
|
45
|
+
return input.gsub(%r{^#{cwd.path}/?}, "")
|
46
|
+
end
|
47
|
+
|
48
|
+
input = "#{path}/#{groups.first}/"
|
49
|
+
return input.gsub(%r{^#{cwd.path}/?}, "")
|
50
|
+
end
|
51
|
+
|
52
|
+
def usage
|
53
|
+
puts "#{aliases.join(", ")} [group]"
|
54
|
+
puts "\t#{description}."
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "djinni"
|
2
|
+
|
3
|
+
class ClearWish < Djinni::Wish
|
4
|
+
def aliases
|
5
|
+
return [ "clear", "cls" ]
|
6
|
+
end
|
7
|
+
|
8
|
+
def description
|
9
|
+
return "Clear the screen"
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(args, djinni_env = {})
|
13
|
+
if (args.nil? || args.empty?)
|
14
|
+
system("clear")
|
15
|
+
else
|
16
|
+
usage
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def usage
|
21
|
+
puts aliases.join(", ")
|
22
|
+
puts "\t#{description}."
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require "djinni"
|
2
|
+
require "string"
|
3
|
+
|
4
|
+
class CopyWish < Djinni::Wish
|
5
|
+
def aliases
|
6
|
+
return [ "copy", "cp" ]
|
7
|
+
end
|
8
|
+
|
9
|
+
def description
|
10
|
+
return "Copy specified field to the clipboard"
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute(args, djinni_env = {})
|
14
|
+
if (args.nil? || args.empty?)
|
15
|
+
puts usage
|
16
|
+
return
|
17
|
+
end
|
18
|
+
|
19
|
+
field, args = args.split(" ", 2)
|
20
|
+
if (!@fields.include?(field))
|
21
|
+
puts usage
|
22
|
+
return
|
23
|
+
end
|
24
|
+
|
25
|
+
keepass = djinni_env["keepass"]
|
26
|
+
cwd = djinni_env["cwd"]
|
27
|
+
args = cwd.path if (args.nil? || args.empty?)
|
28
|
+
|
29
|
+
args = keepass.absolute_path(args, cwd.path)
|
30
|
+
path, target = args.rsplit("/")
|
31
|
+
new_cwd = keepass.find_group(path)
|
32
|
+
|
33
|
+
if (new_cwd)
|
34
|
+
if (target.empty?)
|
35
|
+
usage
|
36
|
+
elsif (new_cwd.has_entry?(target))
|
37
|
+
target = new_cwd.entry_titles.select do |entry|
|
38
|
+
target.downcase == entry.downcase
|
39
|
+
end.first
|
40
|
+
|
41
|
+
timeout = djinni_env["clipboard_timeout"]
|
42
|
+
|
43
|
+
case field
|
44
|
+
when "pass"
|
45
|
+
new_cwd.entries[target].copy_password_to_clipboard
|
46
|
+
keepass.send(
|
47
|
+
"clear_clipboard_after_#{timeout}_seconds"
|
48
|
+
)
|
49
|
+
when "url"
|
50
|
+
new_cwd.entries[target].copy_url_to_clipboard
|
51
|
+
keepass.send(
|
52
|
+
"clear_clipboard_after_#{timeout}_seconds"
|
53
|
+
)
|
54
|
+
when "user"
|
55
|
+
new_cwd.entries[target].copy_username_to_clipboard
|
56
|
+
keepass.send(
|
57
|
+
"clear_clipboard_after_#{timeout}_seconds"
|
58
|
+
)
|
59
|
+
end
|
60
|
+
else
|
61
|
+
puts "Entry \"#{args}\" doesn't exist!"
|
62
|
+
end
|
63
|
+
else
|
64
|
+
puts "Entry \"#{args}\" doesn't exist!"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def initialize
|
69
|
+
@fields = [ "pass", "url", "user" ]
|
70
|
+
end
|
71
|
+
|
72
|
+
def tab_complete(input, djinni_env = {})
|
73
|
+
if (input.nil? || input.empty?)
|
74
|
+
puts
|
75
|
+
puts @fields
|
76
|
+
return ""
|
77
|
+
end
|
78
|
+
|
79
|
+
field, input = input.split(" ", 2)
|
80
|
+
|
81
|
+
if (input.nil? || input.empty?)
|
82
|
+
@fields.each do |f|
|
83
|
+
break if (f == field)
|
84
|
+
if (f.start_with?(field))
|
85
|
+
return "#{f} "
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
input = "" if (input.nil?)
|
91
|
+
|
92
|
+
cwd = djinni_env["cwd"]
|
93
|
+
input, groups, entries = cwd.fuzzy_find(input)
|
94
|
+
if (groups.empty? && entries.empty?)
|
95
|
+
return "#{field} #{input.gsub(%r{^#{cwd.path}/?}, "")}"
|
96
|
+
end
|
97
|
+
|
98
|
+
path, target = input.rsplit("/")
|
99
|
+
|
100
|
+
if (target.empty?)
|
101
|
+
if ((groups.length == 1) && entries.empty?)
|
102
|
+
input = "#{path}/#{groups.first}/"
|
103
|
+
return "#{field} #{input.gsub(%r{^#{cwd.path}/?}, "")}"
|
104
|
+
elsif (groups.empty? && (entries.length == 1))
|
105
|
+
input = "#{path}/#{entries.first}"
|
106
|
+
return "#{field} #{input.gsub(%r{^#{cwd.path}/?}, "")}"
|
107
|
+
end
|
108
|
+
puts
|
109
|
+
groups.each do |group|
|
110
|
+
puts "#{group}/"
|
111
|
+
end
|
112
|
+
puts entries
|
113
|
+
return "#{field} #{input.gsub(%r{^#{cwd.path}/?}, "")}"
|
114
|
+
end
|
115
|
+
|
116
|
+
if (!groups.empty?)
|
117
|
+
input = "#{path}/#{groups.first}/"
|
118
|
+
elsif (!entries.empty?)
|
119
|
+
input = "#{path}/#{entries.first}"
|
120
|
+
end
|
121
|
+
|
122
|
+
return "#{field} #{input.gsub(%r{^#{cwd.path}/?}, "")}"
|
123
|
+
end
|
124
|
+
|
125
|
+
def usage
|
126
|
+
puts "#{aliases.join(", ")} <field> <entry>"
|
127
|
+
puts "\t#{description}."
|
128
|
+
puts
|
129
|
+
puts "FIELDS"
|
130
|
+
@fields.each do |field|
|
131
|
+
puts "\t#{field}"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require "djinni"
|
2
|
+
require "string"
|
3
|
+
|
4
|
+
class LSWish < Djinni::Wish
|
5
|
+
def aliases
|
6
|
+
return [ "ls", "dir" ]
|
7
|
+
end
|
8
|
+
|
9
|
+
def description
|
10
|
+
return "List groups and entries in current group"
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute(args, djinni_env = {})
|
14
|
+
keepass = djinni_env["keepass"]
|
15
|
+
cwd = djinni_env["cwd"]
|
16
|
+
args = cwd.path if (args.nil? || args.empty?)
|
17
|
+
|
18
|
+
args = keepass.absolute_path(args, cwd.path)
|
19
|
+
new_cwd = keepass.find_group(args)
|
20
|
+
|
21
|
+
if (new_cwd)
|
22
|
+
new_cwd.group_names.each do |group|
|
23
|
+
puts "#{group}/"
|
24
|
+
end
|
25
|
+
new_cwd.entry_titles.each do |entry|
|
26
|
+
puts "#{entry}"
|
27
|
+
end
|
28
|
+
else
|
29
|
+
puts "Group \"#{args}\" doesn't exist!"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def tab_complete(input, djinni_env = {})
|
34
|
+
cwd = djinni_env["cwd"]
|
35
|
+
input, groups = cwd.fuzzy_find(input)
|
36
|
+
return input.gsub(%r{^#{cwd.path}/?}, "") if (groups.empty?)
|
37
|
+
|
38
|
+
path, dest = input.rsplit("/")
|
39
|
+
|
40
|
+
if (dest.empty?)
|
41
|
+
if (groups.length == 1)
|
42
|
+
input = "#{path}/#{groups.first}/"
|
43
|
+
return input.gsub(%r{^#{cwd.path}/?}, "")
|
44
|
+
end
|
45
|
+
puts
|
46
|
+
groups.each do |group|
|
47
|
+
puts "#{group}/"
|
48
|
+
end
|
49
|
+
return input.gsub(%r{^#{cwd.path}/?}, "")
|
50
|
+
end
|
51
|
+
|
52
|
+
input = "#{path}/#{groups.first}/"
|
53
|
+
return input.gsub(%r{^#{cwd.path}/?}, "")
|
54
|
+
end
|
55
|
+
|
56
|
+
def usage
|
57
|
+
puts "#{aliases.join(", ")} [group]"
|
58
|
+
puts "\t#{description}."
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "djinni"
|
2
|
+
|
3
|
+
class PwdWish < Djinni::Wish
|
4
|
+
def aliases
|
5
|
+
return [ "pwd" ]
|
6
|
+
end
|
7
|
+
|
8
|
+
def description
|
9
|
+
return "Show path of current group"
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(args, djinni_env = {})
|
13
|
+
if (args.nil? || args.empty?)
|
14
|
+
puts djinni_env["cwd"].path
|
15
|
+
else
|
16
|
+
usage
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def usage
|
21
|
+
puts aliases.join(", ")
|
22
|
+
puts "\t#{description}."
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require "djinni"
|
2
|
+
require "string"
|
3
|
+
|
4
|
+
class ShowWish < Djinni::Wish
|
5
|
+
def aliases
|
6
|
+
return [ "cat", "show" ]
|
7
|
+
end
|
8
|
+
|
9
|
+
def description
|
10
|
+
return "Show group contents"
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute(args, djinni_env = {})
|
14
|
+
keepass = djinni_env["keepass"]
|
15
|
+
cwd = djinni_env["cwd"]
|
16
|
+
args = cwd.path if (args.nil? || args.empty?)
|
17
|
+
|
18
|
+
args = keepass.absolute_path(args, cwd.path)
|
19
|
+
path, target = args.rsplit("/")
|
20
|
+
new_cwd = keepass.find_group(path)
|
21
|
+
|
22
|
+
if (new_cwd)
|
23
|
+
if (target.empty?)
|
24
|
+
puts new_cwd
|
25
|
+
elsif (new_cwd.has_group?(target))
|
26
|
+
puts new_cwd.groups[target]
|
27
|
+
elsif (new_cwd.has_entry?(target))
|
28
|
+
new_cwd.entry_titles.select do |entry|
|
29
|
+
target.downcase == entry.downcase
|
30
|
+
end.each do |entry|
|
31
|
+
puts new_cwd.entries[entry]
|
32
|
+
end
|
33
|
+
else
|
34
|
+
puts "Group/entry \"#{args}\" doesn't exist!"
|
35
|
+
end
|
36
|
+
else
|
37
|
+
puts "Group/entry \"#{args}\" doesn't exist!"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def tab_complete(input, djinni_env = {})
|
42
|
+
cwd = djinni_env["cwd"]
|
43
|
+
input, groups, entries = cwd.fuzzy_find(input)
|
44
|
+
if (groups.empty? && entries.empty?)
|
45
|
+
return input.gsub(%r{^#{cwd.path}/?}, "")
|
46
|
+
end
|
47
|
+
|
48
|
+
path, target = input.rsplit("/")
|
49
|
+
|
50
|
+
if (target.empty?)
|
51
|
+
if ((groups.length == 1) && entries.empty?)
|
52
|
+
input = "#{path}/#{groups.first}/"
|
53
|
+
return input.gsub(%r{^#{cwd.path}/?}, "")
|
54
|
+
elsif (groups.empty? && (entries.length == 1))
|
55
|
+
input = "#{path}/#{entries.first}"
|
56
|
+
return input.gsub(%r{^#{cwd.path}/?}, "")
|
57
|
+
end
|
58
|
+
puts
|
59
|
+
groups.each do |group|
|
60
|
+
puts "#{group}/"
|
61
|
+
end
|
62
|
+
puts entries
|
63
|
+
return input.gsub(%r{^#{cwd.path}/?}, "")
|
64
|
+
end
|
65
|
+
|
66
|
+
if (!groups.empty?)
|
67
|
+
input = "#{path}/#{groups.first}/"
|
68
|
+
elsif (!entries.empty?)
|
69
|
+
input = "#{path}/#{entries.first}"
|
70
|
+
end
|
71
|
+
|
72
|
+
return input.gsub(%r{^#{cwd.path}/?}, "")
|
73
|
+
end
|
74
|
+
|
75
|
+
def usage
|
76
|
+
puts "#{aliases.join(", ")} [group]"
|
77
|
+
puts "\t#{description}."
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require "string"
|
2
|
+
|
3
|
+
class RubeePass::Entry
|
4
|
+
include Comparable
|
5
|
+
|
6
|
+
attr_accessor :group
|
7
|
+
attr_accessor :keepass
|
8
|
+
attr_accessor :notes
|
9
|
+
attr_accessor :path
|
10
|
+
attr_accessor :title
|
11
|
+
attr_accessor :url
|
12
|
+
attr_accessor :username
|
13
|
+
attr_accessor :uuid
|
14
|
+
|
15
|
+
def ==(other)
|
16
|
+
return (self.uuid == other.uuid)
|
17
|
+
end
|
18
|
+
|
19
|
+
def <=>(other)
|
20
|
+
return (self.title.downcase <=> other.title.downcase)
|
21
|
+
end
|
22
|
+
|
23
|
+
def details(level = 0)
|
24
|
+
lvl = Array.new(level, " ").join
|
25
|
+
|
26
|
+
return [
|
27
|
+
"#{lvl}Title : #{@title}".green,
|
28
|
+
# "#{lvl}UUID : #{@uuid}",
|
29
|
+
"#{lvl}Username : #{@username}",
|
30
|
+
# "#{lvl}Password : #{password}".red,
|
31
|
+
"#{lvl}Url : #{@url}",
|
32
|
+
"#{lvl}Notes : #{@notes}"
|
33
|
+
].join("\n")
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(params)
|
37
|
+
@group = params.fetch("Group", nil)
|
38
|
+
@keepass = params.fetch("Keepass", nil)
|
39
|
+
@password = params.fetch("Notes", "")
|
40
|
+
@password = params.fetch("Password", "")
|
41
|
+
@title = params.fetch("Title", "")
|
42
|
+
@url = params.fetch("URL", "")
|
43
|
+
@username = params.fetch("UserName", "")
|
44
|
+
@uuid = params.fetch("UUID", "")
|
45
|
+
|
46
|
+
@path = @title
|
47
|
+
@path = "#{@group.path}/#{@title}" if (@group)
|
48
|
+
@path.gsub!(%r{^//}, "/")
|
49
|
+
end
|
50
|
+
|
51
|
+
def method_missing(method_name, *args)
|
52
|
+
super if (@keepass.nil?)
|
53
|
+
|
54
|
+
case method_name.to_s.gsub(/^copy_(.+)_to_clipboard$/, "\\1")
|
55
|
+
when "password"
|
56
|
+
@keepass.copy_to_clipboard(password)
|
57
|
+
when "url"
|
58
|
+
@keepass.copy_to_clipboard(@url)
|
59
|
+
when "username"
|
60
|
+
@keepass.copy_to_clipboard(@username)
|
61
|
+
else
|
62
|
+
super
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def password
|
67
|
+
return nil if (@keepass.nil?)
|
68
|
+
return @keepass.protected_decryptor.get_password(@password)
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_s
|
72
|
+
return details
|
73
|
+
end
|
74
|
+
end
|