Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
10
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
Felix Eckhofer
kpcli
Commits
9a1f4536
Commit
9a1f4536
authored
Nov 25, 2012
by
Felix Eckhofer
💬
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Import v1.6
parent
0e6e3bf9
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
120 additions
and
53 deletions
+120
-53
kpcli.pl
kpcli.pl
+120
-53
No files found.
kpcli.pl
View file @
9a1f4536
...
...
@@ -24,6 +24,7 @@ use File::Basename; # core
use
Digest::
file
;
# core
use
Digest::
SHA
qw(sha256)
;
# core
use
Data::
Dumper
qw(Dumper)
;
# core
use
Term::
ANSIColor
;
# core
use
Crypt::
Rijndael
;
# non-core, libcrypt-rijndael-perl on Ubuntu
use
Sort::
Naturally
;
# non-core, libsort-naturally-perl on Ubuntu
use
Term::
ReadKey
;
# non-core, libterm-readkey-perl on Ubuntu
...
...
@@ -39,7 +40,7 @@ my $FOUND_DIR = '_found'; # The find command's results go in /_found/
my
$APP_NAME
=
basename
(
$
0
);
$APP_NAME
=~
s/\.pl$//
;
my
$VERSION
=
"
1.
5
";
my
$VERSION
=
"
1.
6
";
my
$opts
=
MyGetOpts
();
# Will only return with options we think we can use
...
...
@@ -173,7 +174,7 @@ my $term = new Term::ShellUI(
method
=>
\
&cli_rm
,
},
"
show
"
=>
{
desc
=>
"
Show an entry: show <path to entry|entry number>
",
desc
=>
"
Show an entry: show
[-f]
<path to entry|entry number>
",
doc
=>
"
\n
"
.
"
The show command tries to intelligently determine
\n
"
.
"
what you want to see and to make it easy to display.
\n
"
.
...
...
@@ -183,8 +184,12 @@ my $term = new Term::ShellUI(
"
When using entry numbers, they will refer to the last
\n
"
.
"
path when an ls was performed or pwd if ls has not
\n
"
.
"
yet been run.
\n
"
.
"
\n
"
.
"
By default, passwords are
\"
hidden
\"
by being displayed as
\n
"
.
"
\"
red on red
\"
where they can be copied to the clip board
\n
"
.
"
but not seen. Provide the -f option to show passwords.
\n
"
.
"",
minargs
=>
1
,
maxargs
=>
1
,
minargs
=>
1
,
maxargs
=>
2
,
args
=>
\
&complete_groups_and_entries
,
method
=>
\
&cli_show
,
},
...
...
@@ -723,9 +728,21 @@ sub scrub_unknown_values_from_all_groups {
}
}
sub
deny_if_readonly
{
our
%opts
;
if
(
defined
(
$opts
->
{
readonly
})
&&
int
(
$opts
->
{
readonly
}))
{
print
"
Function not available with --readonly set.
\n
";
return
1
;
}
return
0
;
}
sub
cli_save
($)
{
my
$self
=
shift
@_
;
my
$params
=
shift
@_
;
if
(
deny_if_readonly
())
{
return
;
}
our
$state
;
if
(
!
length
(
$state
->
{
kdb_file
}))
{
print
"
Please use the saveas command for new files.
\n
";
...
...
@@ -793,7 +810,7 @@ sub cli_rm($) {
my
$params
=
shift
@_
;
our
$state
;
if
(
warn_if_file_changed
())
{
if
(
deny_if_readonly
()
||
warn_if_file_changed
())
{
return
;
}
...
...
@@ -869,7 +886,7 @@ sub cli_rename($$) {
my
$params
=
shift
@_
;
our
$state
;
if
(
warn_if_file_changed
())
{
if
(
deny_if_readonly
()
||
warn_if_file_changed
())
{
return
;
}
...
...
@@ -905,7 +922,7 @@ sub cli_mv($$) {
my
$params
=
shift
@_
;
our
$state
;
if
(
warn_if_file_changed
())
{
if
(
deny_if_readonly
()
||
warn_if_file_changed
())
{
return
;
}
...
...
@@ -956,26 +973,38 @@ sub cli_show($$) {
my
$params
=
shift
@_
;
our
$state
;
my
$target
=
$params
->
{
args
}
->
[
0
];
# Users can provide a -f option to show the password. We use GetOptions
# to parse this command line, and $target holds that target.
my
$target
=
'';
my
%opts
=
();
{
local
@ARGV
=
@
{
$params
->
{
args
}};
my
$result
=
&GetOptions
(
\
%opts
,
"
f
");
if
(
scalar
(
@ARGV
)
!=
1
)
{
return
-
1
;
}
$target
=
$ARGV
[
0
];
}
my
$ent
=
find_target_entity_by_number_or_path
(
$target
);
if
(
!
defined
(
$ent
))
{
return
-
1
;
}
# my $path='unknown';
# if (defined($state->{all_ent_paths_rev}->{$ent->{id}})) {
# $path=humanize_path($state->{all_ent_paths_rev}->{$ent->{id}});
# }
$state
->
{
kdb
}
->
unlock
;
print
"
\n
";
if
(
defined
(
$ent
->
{
path
}))
{
print
show_format
("
Path
",
$ent
->
{
path
})
.
"
\n
";
}
# Unless -f is specified, we "hide" the password as red-on-red.
my
$password
=
$ent
->
{
password
};
if
(
!
defined
(
$opts
{
f
}))
{
$password
=
colored
(['
red on_red
'],
$password
);
}
print
show_format
("
Title
",
$ent
->
{
title
})
.
"
\n
"
.
show_format
("
Uname
",
$ent
->
{
username
})
.
"
\n
"
.
show_format
("
Pass
",
$
ent
->
{
password
}
)
.
"
\n
"
.
show_format
("
Pass
",
$password
)
.
"
\n
"
.
show_format
("
URL
",
$ent
->
{
url
})
.
"
\n
"
.
show_format
("
Icon#
",
$ent
->
{
icon
})
.
"
\n
"
.
show_format
("
Notes
",
$ent
->
{
comment
})
.
"
\n
"
.
...
...
@@ -990,7 +1019,7 @@ sub cli_edit($) {
my
$params
=
shift
@_
;
our
$state
;
if
(
warn_if_file_changed
())
{
if
(
deny_if_readonly
()
||
warn_if_file_changed
())
{
return
;
}
...
...
@@ -1008,7 +1037,9 @@ sub cli_edit($) {
if
(
$input
->
{
hide_entry
})
{
print
$input
->
{
txt
}
.
"
:
";
}
else
{
print
$input
->
{
txt
}
.
"
(
\"
"
.
$ent
->
{
$input
->
{
key
}}
.
"
\"
):
";
my
$val
=
$ent
->
{
$input
->
{
key
}};
if
(
$val
=~
m/\r|\n/
)
{
$val
=
"
\n
$val
\n
";
}
print
$input
->
{
txt
}
.
"
(
\"
"
.
$val
.
"
\"
):
";
}
if
(
$input
->
{
genpasswd
})
{
print
"
"
x25
.
'
("g" to generate a password)
'
.
"
\r
";
...
...
@@ -1016,20 +1047,11 @@ sub cli_edit($) {
if
(
$input
->
{
hide_entry
})
{
ReadMode
(
2
);
# Hide typing
}
my
$val
=
ReadLine
(
0
);
if
(
$input
->
{
hide_entry
})
{
print
"
\n
";
}
chomp
$val
;
if
(
$input
->
{
genpasswd
}
&&
$val
eq
'
g
')
{
$val
=
generatePassword
(
20
);
}
elsif
(
length
(
$val
)
&&
$input
->
{
double_entry_verify
})
{
print
"
Retype to verify:
";
my
$checkval
=
ReadLine
(
0
);
if
(
$input
->
{
hide_entry
})
{
print
"
\n
";
}
chomp
$checkval
;
if
(
$checkval
ne
$val
)
{
print
"
Entries mismatched. Please try again.
\n
";
redo
;
}
my
$val
=
'';
if
(
$input
->
{
multiline
})
{
$val
=
new_edit_multiline_input
(
$input
);
}
else
{
$val
=
new_edit_single_line_input
(
$input
);
}
# If the field was not empty, change it to the new $val
if
(
length
(
$val
))
{
...
...
@@ -1051,6 +1073,50 @@ sub cli_edit($) {
return
0
;
}
# Single line input helper function for cli_new and cli_edit.
sub
new_edit_single_line_input
($)
{
my
$input
=
shift
@_
;
my
$val
=
ReadLine
(
0
);
if
(
$input
->
{
hide_entry
})
{
print
"
\n
";
}
chomp
$val
;
if
(
$input
->
{
genpasswd
}
&&
$val
eq
'
g
')
{
$val
=
generatePassword
(
20
);
}
elsif
(
length
(
$val
)
&&
$input
->
{
double_entry_verify
})
{
print
"
Retype to verify:
";
my
$checkval
=
ReadLine
(
0
);
if
(
$input
->
{
hide_entry
})
{
print
"
\n
";
}
chomp
$checkval
;
if
(
$checkval
ne
$val
)
{
print
"
Entries mismatched. Please try again.
\n
";
redo
;
}
}
return
$val
;
}
# Multi-line input helper function for cli_new and cli_edit.
sub
new_edit_multiline_input
($)
{
my
$input
=
shift
@_
;
my
$bold
=
"
\e
[1m
";
my
$red
=
"
\e
[31m
";
my
$yellow
=
"
\e
[33m
";
my
$clear
=
"
\e
[0m
";
print
"
\n
$yellow
(end multi-line input with a single
\"
.
\"
on a line)
$clear
\n
";
my
$val
=
'';
my
$unfinished
=
1
;
while
(
$unfinished
)
{
my
$line
=
ReadLine
(
0
);
if
(
$line
=~
m/^\.[\r\n]*$/
)
{
# a lone "." ends our input
$unfinished
=
0
;
}
else
{
$val
.=
$line
;
if
(
$val
=~
m/^[\r\n]*$/
)
{
$val
=
'';
$unfinished
=
0
;
}
}
}
chomp
(
$val
);
# Remove extra line at the end
return
$val
;
}
# Formats an entry for display for cli_show()
sub
show_format
($$)
{
my
$title
=
shift
@_
;
...
...
@@ -1070,7 +1136,7 @@ sub get_entry_fields {
{
key
=>
'
password
',
txt
=>
'
Password
',
hide_entry
=>
1
,
double_entry_verify
=>
1
,
genpasswd
=>
1
},
{
key
=>
'
url
',
txt
=>
'
URL
'
},
{
key
=>
'
comment
',
txt
=>
'
Notes/Comments
'
},
{
key
=>
'
comment
',
txt
=>
'
Notes/Comments
'
,
'
multiline
'
=>
1
},
);
return
@fields
;
}
...
...
@@ -1151,7 +1217,7 @@ sub cli_new($) {
my
$params
=
shift
@_
;
our
$state
;
if
(
warn_if_file_changed
())
{
if
(
deny_if_readonly
()
||
warn_if_file_changed
())
{
return
;
}
...
...
@@ -1182,25 +1248,16 @@ sub cli_new($) {
if
(
$input
->
{
hide_entry
})
{
ReadMode
(
2
);
# Hide typing
}
my
$val
=
ReadLine
(
0
);
if
(
$input
->
{
hide_entry
})
{
print
"
\n
";
}
chomp
$val
;
my
$val
=
'';
if
(
$input
->
{
multiline
})
{
$val
=
new_edit_multiline_input
(
$input
);
}
else
{
$val
=
new_edit_single_line_input
(
$input
);
}
# If the user gave us an empty title, abort the new entry
if
(
$input
->
{
key
}
eq
'
title
'
&&
length
(
$val
)
==
0
)
{
return
;
}
if
(
$input
->
{
genpasswd
}
&&
$val
eq
'
g
')
{
$val
=
generatePassword
(
20
);
}
elsif
(
$input
->
{
double_entry_verify
})
{
print
"
Retype to verify:
";
my
$checkval
=
ReadLine
(
0
);
if
(
$input
->
{
hide_entry
})
{
print
"
\n
";
}
chomp
$checkval
;
if
(
$checkval
ne
$val
)
{
print
"
Entries mismatched. Please try again.
\n
";
redo
;
}
}
$new_entry
->
{
$input
->
{
key
}}
=
$val
;
ReadMode
(
0
);
# Return to normal
}
...
...
@@ -1225,6 +1282,8 @@ sub cli_import($$) {
my
$key_file
=
shift
@_
;
our
$state
;
if
(
deny_if_readonly
())
{
return
;
}
# If the user gave us a bogus file there's nothing to do
if
(
!
-
f
(
$file
))
{
print
"
File does not exist:
$file
\n
";
...
...
@@ -1458,7 +1517,7 @@ sub cli_rmdir($) {
my
$params
=
shift
@_
;
our
$state
;
if
(
warn_if_file_changed
())
{
if
(
deny_if_readonly
()
||
warn_if_file_changed
())
{
return
;
}
...
...
@@ -1509,7 +1568,7 @@ sub cli_mkdir($) {
my
$params
=
shift
@_
;
our
$state
;
if
(
warn_if_file_changed
())
{
if
(
deny_if_readonly
()
||
warn_if_file_changed
())
{
return
;
}
...
...
@@ -1756,7 +1815,7 @@ sub GetMasterPasswd {
sub
MyGetOpts
{
my
%opts
=
();
my
$result
=
&GetOptions
(
\
%opts
,
"
kdb=s
",
"
key=s
",
"
help
",
"
h
");
my
$result
=
&GetOptions
(
\
%opts
,
"
kdb=s
",
"
key=s
",
"
help
",
"
h
"
,
"
readonly
"
);
# If the user asked for help or GetOptions complained, give help and exit
if
(
$opts
{
help
}
||
$opts
{
h
}
||
(
!
int
(
$result
)))
{
...
...
@@ -1786,8 +1845,9 @@ sub GetUsageMessage {
my
$t
=
"
Usage:
$APP_NAME
[--kdb=<file.kdb>] [--key=<file.key>]
\n
"
.
"
\n
"
.
"
--help
\t
This message.
\n
"
.
"
--kdb
\t
Optional KeePass 1.x database file to open (must exist)
\n
"
.
"
--key
\t
Optional KeePass 1.x key file (must exist)
\n
"
.
"
--kdb
\t
Optional KeePass database file to open (must exist)
\n
"
.
"
--key
\t
Optional KeePass key file (must exist)
\n
"
.
"
--readonly
\t
Run in read-only mode; no changes will be allowed.
\n
"
.
"
\n
"
.
"
Run kpcli with no options and type 'help' at its command prompt to learn
\n
"
.
"
about kpcli's commands.
\n
";
...
...
@@ -1810,6 +1870,8 @@ sub my_help_call($) {
my
$help
=
$term
->
get_all_cmd_summaries
(
$term
->
commands
());
$help
=~
s/^ {12}//gm
;
# Trim some leading spaces off of each line of output
print
$help
;
print
"
\n
"
.
"
Type
\"
help <command>
\"
for more detailed help on a command.
\n
";
return
0
;
}
...
...
@@ -2045,12 +2107,12 @@ return($retval);
=head1 NAME
kpcli - A command line interface to KeePass
1.x
database files.
kpcli - A command line interface to KeePass database files.
=head1 DESCRIPTION
A command line interface (interactive shell) to work with KeePass
1.x
A command line interface (interactive shell) to work with KeePass
database files (http://http://en.wikipedia.org/wiki/KeePass). This
program was inspired by my use of "kedpm -c" combined with my need
to migrate to KeePass. The curious can read about the Ked Password
...
...
@@ -2200,6 +2262,11 @@ this work would never have been posible.
made minor changes that are possible with >=2.01.
With File::KeePass v2.03, kpcli should now support
KeePass v2 files (*.kdbx).
2012-Nov-25 - v1.6 - Hide passwords (red on red) in the show command
unless the -f option is given.
Added the --readonly command line option.
Added support for multi-line notes/comments;
input ends on a line holding a single ".".
=head1 TODO ITEMS
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment