From 2da97abb9caf281e159267d4f6d17538a471253c Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Sat, 28 Mar 2015 22:01:22 +0100 Subject: icevault [COMMAND] [OPTION ...] [ARG ...] --- cli/icevault | 146 ++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 104 insertions(+), 42 deletions(-) (limited to 'cli/icevault') 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"; } -- cgit v1.2.3