aboutsummaryrefslogtreecommitdiffstats
path: root/cli/icevault
diff options
context:
space:
mode:
Diffstat (limited to 'cli/icevault')
-rwxr-xr-xcli/icevault146
1 files changed, 104 insertions, 42 deletions
diff --git a/cli/icevault b/cli/icevault
index 3b69b3a..c93a608 100755
--- a/cli/icevault
+++ b/cli/icevault
@@ -20,6 +20,7 @@ use strict;
use warnings;
our $VERSION = '0.1';
+my $NAME = 'icevault';
use Getopt::Long qw/:config posix_default no_ignore_case gnu_compat
bundling auto_version/;
use Encode qw/decode_utf8 encode_utf8/;
@@ -66,20 +67,6 @@ sub warning($@) {
myprintf \*STDERR, shift, @_;
}
-# Print usage and exit.
-sub usage($) {
- my $rv = shift;
- my $fh = $rv ? \*STDERR : \*STDOUT;
- print $fh "Usage: $0 [OPTIONS] [fill] scheme://hostname/identity\n"
- ." or: $0 [OPTIONS] insert [identity]\n"
- ." or: $0 [OPTIONS] dump scheme://hostname/identity\n"
- ." or: $0 [OPTIONS] clip scheme://hostname/identity\n"
- ." or: $0 [OPTIONS] edit scheme://hostname/identity\n"
- ." or: $0 [OPTIONS] ls [scheme://[hostname/[identity]]]\n"
- . "Consult the manual page for more information.\n";
- exit $rv;
-}
-
sub mysystem(@) {
system {$_[0]} @_;
error "C<%s> exited with value %d", $_[0], ($? >> 8) if $? and $? != -1;
@@ -594,35 +581,96 @@ sub sha256_file($) {
}
+
#######################################################################
-usage(1) unless @ARGV;
+unless (@ARGV) {
+ print STDERR "Usage: $NAME [COMMAND] [OPTION ...] [ARG ...]\n";
+ error "Missing command. Try C<%s> or consult the manpage for more information.", "$NAME --help";
+}
+
+my @USAGE = (
+ fill => "[-f, --force] [-p, --show-passwords] [-s, --socket=PATH] scheme://hostname/identity",
+ clip => "scheme://hostname/identity",
+ dump => "[-p, --show-passwords] scheme://hostname/identity",
+ edit => "scheme://hostname/identity",
+ insert => "[-f, --force] [-s, --socket=PATH] [identity]",
+ ls => "[-0, --zero] [scheme://[hostname/[identity]]]",
+);
+
+if ($ARGV[0] eq '--help' or $ARGV[0] eq '-?') {
+ my $default_cmd = shift @USAGE;
+ my $default_usage = shift @USAGE;
+ print "Usage: $NAME [$default_cmd] $default_usage\n";
+ while (@USAGE) {
+ my $cmd = shift @USAGE;
+ my $usage = shift @USAGE;
+ print " or: $NAME $cmd $usage\n";
+ }
+ myprintf "Try C<%s> or consult the manpage for more information.", "$NAME COMMAND --help";
+ exit 0;
+}
+
@ARGV = map { $LOCALE->decode($_) } @ARGV;
-my $confFilename = ($ENV{XDG_CONFIG_HOME} // "$ENV{HOME}/.data") . "/icevault";
-GetOptions(\%CONFIG, qw/debug show-passwords|p socket|s=s help|? zero|0/) or usage(1);
-usage(0) if $CONFIG{help};
+my $COMMAND = ($ARGV[0] =~ /\A[A-Za-z0-9-]+:\/\//aa or $ARGV[0] =~ /\A--?[^-]/) ? 'fill' : shift;
+
+# Print $COMMAND usage (detailed if --help)
+sub usage(@) {
+ my @opts = @_;
+ my %usage = @USAGE;
+ print "$NAME $COMMAND $usage{$COMMAND} \n";
+ if ($CONFIG{help}) {
+ if (@opts) {
+ print "Options:\n";
+ while (@opts) {
+ shift @opts;
+ print " ".shift(@opts)."\n";
+ }
+ }
+ printf "Consult the manpage for more information.\n";
+ exit 0;
+ } else {
+ myprintf "Try C<%s> or consult the manpage for more information.", "$NAME $COMMAND --help";
+ exit 1;
+ }
+}
+
+# Get options, load and validate config
+sub getopts(%) {
+ my @opts = @_;
+ my %opts = @opts;
+ usage(@opts) unless GetOptions(\%CONFIG, qw/debug help|?/, keys %opts) and !$CONFIG{help};
+ loadConfig();
+}
-# Load configuration
-my $command = $ARGV[0] =~ /\A[A-Za-z0-9-]+:\/\//aa ? 'fill' : shift;
+#######################################################################
# Process the commands
-if ($command eq '_complete') {
+
+if ($COMMAND eq '_complete') {
# used internaly for auto-completion
- usage(1) unless $#ARGV == 0;
+ GetOptions(\%CONFIG, qw/zero|0/) or die;
+ die unless $#ARGV == 0;
my $delim = $CONFIG{zero} ? "\0" : "\n";
print $LOCALE->encode($_), $delim foreach complete(shift @ARGV);
exit;
}
-elsif ($command eq '_geturi') {
+elsif ($COMMAND eq '_geturi') {
# used internaly for auto-completion
- usage(1) if @ARGV;
+ GetOptions(\%CONFIG, qw/socket|s=s/) or die;
+ die if @ARGV;
print $LOCALE->encode( &connect($CONFIG{socket}) ), "\n";
sendCommand 'QUIT';
exit;
}
-elsif ($command eq 'insert') {
- usage(1) unless $#ARGV < 1;
+
+elsif ($COMMAND eq 'insert') {
+ getopts( 'force|f' => "-f, --force \tOverwrite preexisting identity"
+ , 'socket|s=s' => "-s, --socket=PATH\tSpecifiy the path to the Icevault socket"
+ );
+ usage() unless $#ARGV < 1;
+
my $uri = &connect($CONFIG{socket});
myprintf "Importing HTML form from URI C<%s>", $uri;
@@ -660,7 +708,7 @@ elsif ($command eq 'insert') {
if ($r !~ /\A[^\P{Print}\/]+\z/) {
myprintf \*STDERR, "Invalid identity: C<%s>", $r;
}
- elsif (-e getIdentityFile "$uri/$r") {
+ elsif (-e getIdentityFile "$uri/$r" and !$CONFIG{force}) {
myprintf \*STDERR, "Identity C<%s> already exists", "$uri/$r";
}
else {
@@ -671,7 +719,7 @@ elsif ($command eq 'insert') {
}
my $filename = getIdentityFile "$uri/$id";
- error "Identity C<%s> already exists", "$uri/$id" if -e $filename;
+ error "Identity C<%s> already exists", "$uri/$id" if -e $filename and !$CONFIG{force};
my @passIdx = grepIdx { $_->{type} eq 'password' } @{$form->{fields}};
my @dontsave;
@@ -725,8 +773,13 @@ elsif ($command eq 'insert') {
saveIdentityFile $form, $filename;
}
-elsif ($command eq 'fill') {
- usage(1) unless $#ARGV == 0;
+elsif ($COMMAND eq 'fill') {
+ getopts( 'force|f' => "-f, --force \tDon't ask before updating the form"
+ , 'show-passwords|p=s' => "-p, --show-passwords\tDon't redact passwords"
+ , 'socket|s=s' => "-s, --socket=PATH \tSpecifiy the path to the Icevault socket"
+ );
+ usage() unless $#ARGV == 0;
+
my $id = shift;
my $filename = getIdentityFile $id;
error "No such identity C<%s>", $id unless -f $filename;
@@ -770,7 +823,7 @@ elsif ($command eq 'fill') {
$changed = 1;
}
- if ($pass->{value} eq '') { # fill the password with the known value
+ if ($pass->{value} eq '' or $CONFIG{force}) { # fill the password with the known value
$fill[$passIdx[0]] = $mypass->{value};
}
elsif ($mypass->{value} ne $pass->{value}) { # update the password
@@ -819,7 +872,8 @@ elsif ($command eq 'fill') {
my $myidx = shift @{$myfields{$name}};
my $idx = shift @{$fields{$name}};
next unless defined $myidx and defined $idx; # was taken care of before
- if ($form->{fields}->[$idx]->{value} eq '' and $myform->{fields}->[$myidx]->{value} ne '') {
+ if (($form->{fields}->[$idx]->{value} eq '' or $CONFIG{force})
+ and $myform->{fields}->[$myidx]->{value} ne '') {
# fill with the known value
$fill[$idx] = $myform->{fields}->[$myidx]->{value};
}
@@ -870,8 +924,10 @@ elsif ($command eq 'fill') {
}
}
-elsif ($command eq 'dump') {
- usage(1) unless $#ARGV == 0;
+elsif ($COMMAND eq 'dump') {
+ getopts('show-passwords|p=s' => "-p, --show-passwords\tDon't redact passwords");
+ usage() unless $#ARGV == 0;
+
my $id = shift;
my $filename = getIdentityFile $id;
error "No such identity C<%s>", $id unless -f $filename;
@@ -883,8 +939,10 @@ elsif ($command eq 'dump') {
print STDOUT (defined $LOCALE ? $LOCALE->encode($str) : $str)
}
-elsif ($command eq 'edit') {
- usage(1) unless $#ARGV == 0;
+elsif ($COMMAND eq 'edit') {
+ getopts();
+ usage() unless $#ARGV == 0;
+
my $id = shift;
my $filename = getIdentityFile $id;
error "No such identity C<%s>", $id unless -f $filename;
@@ -932,8 +990,10 @@ elsif ($command eq 'edit') {
}
}
-elsif ($command eq 'clip') {
- usage(1) unless $#ARGV == 0;
+elsif ($COMMAND eq 'clip') {
+ getopts();
+ usage() unless $#ARGV == 0;
+
my $id = shift;
my $filename = getIdentityFile $id;
error "No such identity C<%s>", $id unless -f $filename;
@@ -951,8 +1011,10 @@ elsif ($command eq 'clip') {
exit 0;
}
-elsif ($command eq 'ls') {
- usage(1) if $#ARGV > 0;
+elsif ($COMMAND eq 'ls') {
+ getopts( 'zero|0' => "-0, --zero\tUse NUL instead of newline as line delimiter" );
+ usage() if $#ARGV > 0;
+
my $prefix = shift @ARGV;
my @matches = complete $prefix // '', 1;
@@ -978,6 +1040,6 @@ elsif ($command eq 'ls') {
}
else {
- myprintf "Unknown command: C<%s>", $command;
- usage(1);
+ print STDERR "Usage: $NAME [COMMAND] [OPTION ...] [ARG ...]\n";
+ error "Unknown command C<%s>. Try C<%s> for more information.", $COMMAND, "$NAME --help";
}