diff options
| author | Guilhem Moulin <guilhem@fripost.org> | 2015-03-28 22:01:22 +0100 | 
|---|---|---|
| committer | Guilhem Moulin <guilhem@fripost.org> | 2015-03-29 06:23:10 +0200 | 
| commit | deace009885a44e9802009578593f85ede060253 (patch) | |
| tree | fe4a56277b013bf1dd15bf7034edb1f06a426441 /cli | |
| parent | 4db324d6089cca44478fbeab594512f4b51540ef (diff) | |
icevault [COMMAND] [OPTION ...] [ARG ...]
Diffstat (limited to 'cli')
| -rwxr-xr-x | cli/icevault | 146 | ||||
| -rw-r--r-- | cli/icevault.1 | 93 | 
2 files changed, 140 insertions, 99 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";  } diff --git a/cli/icevault.1 b/cli/icevault.1 index 7db6be9..0768b68 100644 --- a/cli/icevault.1 +++ b/cli/icevault.1 @@ -4,17 +4,7 @@  IceVault \- IceVault client user interface  .SH SYNOPSIS -.B icevault\fR [\fIOPTIONS\fR] [\fBfill\fR] \fIscheme\fR://\fIhostname\fR/\fIidentity\fR -.br -.B icevault\fR [\fIOPTIONS\fR] \fBinsert\fR [\fIidentity\fR] -.br -.B icevault\fR [\fIOPTIONS\fR] \fBdump\fR \fIscheme\fR://\fIhostname\fR/\fIidentity\fR -.br -.B icevault\fR [\fIOPTIONS\fR] \fBclip\fR \fIscheme\fR://\fIhostname\fR/\fIidentity\fR -.br -.B icevault\fR [\fIOPTIONS\fR] \fBedit\fR \fIscheme\fR://\fIhostname\fR/\fIidentity\fR -.br -.B icevault\fR [\fIOPTIONS\fR] \fBls\fR [\fIscheme\fR://[\fIhostname\fR/[\fIidentity\fR]]] +.B icevault\fR [\fICOMMAND\fR] [\fIOPTION\fR ...] [\fIARG\fR ...]  .SH DESCRIPTION @@ -46,8 +36,10 @@ using \fIpwgen\fR(1).  .SH COMMANDS +If \fICOMMAND\fR is omitted, \fBfill\fR is assumed. +  .TP -.B fill\fR \fIscheme\fR://\fIhostname\fR/\fIidentity\fR +.B fill\fR [\fB-f\fR, \fB--force\fR] [\fB-p\fR, \fB--show-passwords\fR] [\fB-s\fR, \fB--socket=\fR\fIPATH\fR] \fIscheme\fR://\fIhostname\fR/\fIidentity\fR  If the scheme (resp. hostname) of the active tab of the active window is  not \fIscheme\fR (resp. \fIhostname\fR) the program assumes a phishing  attempt and aborts.  Otherwise, the \fIidentity\fR file is decrypted and @@ -60,11 +52,34 @@ If \fIidentity\fR has a single password whereas the webpage has 2 (resp.  3), a signup (resp. password changing) page is assumed, and a new  password is randomly generated using \fIpwgen\fR(1) if the fields are  left blank. +Use \fB--socket=\fR\fIPATH\fR to specify the path to the IceVault +socket.  If \fB-f\fR is set, existing values on the browser are ignored. +Passwords are redacted unless the flag \fB-p\fR is set. + +.TP +.B clip\fR \fIscheme\fR://\fIhostname\fR/\fIidentity\fR +Decrypt the \fIidentity\fR file and copy its first password to the +clipboard using \fIxclip\fR(1), with a maximum number of pastes of 1. + +.TP +.B dump\fR [\fB-p\fR, \fB--show-passwords\fR] \fIscheme\fR://\fIhostname\fR/\fIidentity\fR +Decrypt the \fIidentity\fR file and dump its content on the standard +output.  Note that while the output is a valid YAML document, original +formatting may not be preserved; in particular, comments and empty lines +are stripped.  Passwords are redacted unless the flag \fB-p\fR is set.  .TP -.B insert\fR [\fIidentity\fR] +.B edit\fR \fIscheme\fR://\fIhostname\fR/\fIidentity\fR +Decrypt the \fIidentity\fR file to a temporary file and open it using +the editor specified by the EDITOR environment variable (or \fIeditor\fR +if EDITOR is unset).  Upon exit, the file is reencrypted if the SHA-256 +digest of its content differs.  Note that formatting and comments may +not be preserved by subsequent updates of the \fIidentity\fR file. + +.TP +.B insert\fR [\fB-f\fR, \fB--force\fR] [\fB-s\fR, \fB--socket=\fR\fIPATH\fR] [\fIidentity\fR]  Create a new \fIscheme\fR://\fIhostname\fR/\fIidentity\fR URI available -for further \fBfill\fR and other commands. +for further commands.  Store the first visible form on the active tab of the active window which  contains a password (or the first visible form with a non-empty field if  no visible form has a password).  If \fIidentity\fR is omitted, it @@ -74,33 +89,17 @@ password).  If the webpage has 2 (resp. 3), a signup (resp. password changing) page  is assumed, and a new password is randomly generated using  \fIpwgen\fR(1) if the fields are left blank. +Use \fB--socket=\fR\fIPATH\fR to specify the path to the IceVault +socket.  If the flag \fB-f\fR is set, override the \fIidentity\fR file +if it already exists (the default is to abort).  .TP -.B dump\fR \fIscheme\fR://\fIhostname\fR/\fIidentity\fR -Decrypt the \fIidentity\fR file and dump its content on the standard -output.  Note that while the output is a valid YAML document, original -formatting may not be preserved; in particular, comments and empty lines -are stripped. +.B ls\fR [\fB-0\fR, \fB--zero\fR] [\fIscheme\fR://[\fIhostname\fR/[\fIidentity\fR]]] +List content of the given identity prefix.  If the flag \fB-0\fR is set, +use NUL as line separator. -.TP -.B clip\fR \fIscheme\fR://\fIhostname\fR/\fIidentity\fR -Decrypt the \fIidentity\fR file and copy the first password to the -clipboard using \fIxclip\fR(1), with a maximum number of pastes of 1. -.TP -.B edit\fR \fIscheme\fR://\fIhostname\fR/\fIidentity\fR -Decrypt the \fIidentity\fR file to a temporary file and opens it using -the editor specified by the EDITOR environment variable.  When the -editor exits, the file is reencrypted if the SHA-256 digest of its -content differs.  Note that formatting and comments may not be preserved -by subsequent updates of the \fIidentity\fR file. - -.TP -.B ls\fR [\fIscheme\fR://[\fIhostname\fR/[\fIidentity\fR]]] -List content of the given identity prefix. - - -.SH OPTIONS +.SH GLOBAL OPTIONS  .TP  .B \-\-debug  Turn on debug mode. @@ -110,29 +109,9 @@ Turn on debug mode.  Output a brief help and exit.  .TP -.B \-p\fR, \fB\-\-show\-passwords\fR -By default passwords are redacted when printing forms to the standard -output.  This flags turns off this behavior. - -.TP -.B \-s\fR \fIsockpath\fR, \fB\-\-socket=\fR\fIsockpath\fR -Specify the path of the UNIX socket used to communicate with the -browser.  Can be an absolute path or a path relative to the default -Firefox profile (or first profile found if there is no default profile) -in the "~/.mozilla/firefox" directory. -The socket path and permissions can be configured on the -Iceweasel/Firefox side with the "extensions.icevault.socketPath" and -"extensions.icevault.socketPerms" preferences in "about:config", -respectively. - -.TP  .B \-\-version  Show the version number and exit. -.TP -.B \-0\fR, \fB\-\-zero -With the \fBls\fR command, use NUL instead of newline as line delimiter. -  .SH CONFIGURATION FILE  | 
