rubeepass 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|