App-Raps2
view release on metacpan or search on metacpan
$xclip_cmd = 'xclip -l 2';
}
my ( $action, @args ) = @ARGV;
my $raps2 = App::Raps2->new(
cost => $default_cost,
pwgen_cmd => $pwgen_cmd,
xclip_cmd => $xclip_cmd,
);
sub file_must_exist {
my ( $file, $name ) = @_;
if ( not defined $file ) {
say STDERR "No such account: ${name}";
exit 2;
}
return;
}
sub file_must_not_exist {
my ( $file, $name ) = @_;
if ( -e $file ) {
say STDERR "Account already exists: ${name}";
exit 2;
}
return;
}
sub cmd_add {
my ($name) = @_;
if ( not $name ) {
cmd_help( 1, 'add <account>' );
}
my $pwfile = data_home('raps2') . "/${name}";
file_must_not_exist( $pwfile, $name );
$raps2->get_master_password();
my $url = $raps2->ui->read_line('URL');
my $login = $raps2->ui->read_line('Login');
my $pass = $raps2->ui->read_pw( 'Password', 1 );
my $extra = $raps2->ui->read_multiline('Additional content');
if ( length($pass) == 0 ) {
$pass = $raps2->generate_password();
if ( not $pass ) {
say STDERR "Password generation failed: ${!}: "
. $raps2->conf('pwgen_cmd');
exit 3;
}
if ($paste) {
$raps2->ui->to_clipboard( $pass, $raps2->conf('xclip_cmd') );
}
elsif ( not $no_echo ) {
$raps2->ui->output( [ 'Generated password', $pass ] );
}
}
$raps2->pw_save(
file => $pwfile,
url => $url,
login => $login,
password => $pass,
extra => $extra,
);
return;
}
sub cmd_dump {
my ($name) = @_;
if ( not $name ) {
cmd_help( 1, 'dump <account>' );
}
my $pwfile = data_files("raps2/${name}");
file_must_exist( $pwfile, $name );
$raps2->get_master_password();
my $key = $raps2->pw_load( file => $pwfile );
$raps2->ui->output(
[ 'URL', $key->{url} ],
[ 'Login', $key->{login} ],
[ 'Password', $key->{password} ],
);
if ( $key->{extra} ) {
print $key->{extra};
}
return;
}
sub cmd_dump_all {
my @files = read_dir( data_home('raps2') );
$raps2->get_master_password();
for my $file (@files) {
my $key = $raps2->pw_load( file => data_files("raps2/${file}") );
$raps2->ui->output(
[ 'Key', $file ],
[ 'URL', $key->{url} ],
[ 'Login', $key->{login} ],
[ 'Password', $key->{password} ],
);
if ( $key->{extra} ) {
print $key->{extra};
}
return;
}
sub cmd_edit {
my ($name) = @_;
if ( not $name ) {
cmd_help( 1, 'edit <account>' );
}
my $pwfile = data_files("raps2/${name}");
file_must_exist( $pwfile, $name );
$raps2->get_master_password();
my $key = $raps2->pw_load( file => $pwfile );
my $salt = $key->{salt};
my $cost = $key->{cost};
my $url = $raps2->ui->read_line( 'URL', $key->{url} );
my $login = $raps2->ui->read_line( 'Login', $key->{login} );
my $pass = $key->{password};
my $new_pass = $raps2->ui->read_pw( 'New password (empty to keep old)', 1 );
my $extra = $key->{extra} // q{};
if ( length($new_pass) ) {
$pass = $new_pass;
}
$raps2->pw_save(
file => $pwfile,
salt => $salt,
cost => $cost,
url => $url,
login => $login,
password => $pass,
extra => $extra,
);
return;
}
sub cmd_get {
my ($name) = @_;
if ( not $name ) {
cmd_help( 1, 'get <account>' );
}
my $pwfile = data_files("raps2/${name}");
file_must_exist( $pwfile, $name );
$raps2->get_master_password();
my $key = $raps2->pw_load( file => $pwfile );
$raps2->ui->to_clipboard( $key->{password}, $raps2->conf('xclip_cmd') )
or die("Could not place password in clipboard: ${!}\n");
if ( $key->{extra} ) {
print $key->{extra};
}
return;
}
sub cmd_help {
my ( $exit_status, $subcmd ) = @_;
$subcmd //= 'add|get|dump|... [account]';
say "Usage: raps2 ${subcmd}";
say 'See also: "man raps2"';
exit $exit_status;
}
sub cmd_info {
my ($name) = @_;
if ( not $name ) {
cmd_help( 1, 'info <account>' );
}
my $pwfile = data_files("raps2/${name}");
file_must_exist( $pwfile, $name );
my $key = $raps2->pw_load_info( file => $pwfile );
$raps2->ui->output( [ 'URL', $key->{url} ], [ 'Login', $key->{login} ], );
return;
}
sub cmd_list {
my @files = read_dir( data_home('raps2') );
for my $file ( sort @files ) {
my $key = $raps2->pw_load_info( name => $file );
$raps2->ui->list(
[ 'Account', $file ],
[ 'Login', $key->{login} ],
[ 'URL', $key->{url} ],
);
}
return;
}
sub cmd_remove {
my ($name) = @_;
if ( not $name ) {
cmd_help( 1, 'del <account>' );
}
my $pwfile = data_files("raps2/${name}");
=item B<-P>, B<--paste>
When using the pwgen functionality of B<raps2 add>, do not print the generated
password on stdout. Place it in the X Clipboard instead.
Note that it can only be pasted once, unless B<-x> or B<-C> is used.
=item B<-p>, B<--pwgen-cmd> I<command>
When the user does not enter a password in B<raps2 add>, it will execute
I<command> to create one. The first line of output is taken as password.
If the output contains spaces, anything after the first space (plus the space
itself) is discarded.
Default: pwgen -s 23 1
=item B<-V>, B<--version>
Show version information.
=item B<-x>, B<--xclip-cmd> I<command>
Command to run for B<raps2 get>. The password will be available on
I<command>'s stdin.
Default: xclip -l 1
=back
=head1 EXIT STATUS
zero on success, non-zero otherwise.
=head1 CONFIGURATION
raps2 saves the master password hash in F<~/.config/raps2/password>.
The configuration (key setup cost and pwgen command) is stored in
F<~/.config/raps2/defaults> in an INI-like format.
Additional encrypted passwords are stored in F<~/.local/share/raps2/>.
These directories can be changed by setting the B<XDG_CONFIG_HOME> and
B<XDG_DATA_HOME> environment variables.
The following settings are available in F<~/.config/raps2/defaults>:
=over
=item B<cost> = 12
Default key setup cost. See the B<-c> option.
=item B<pwgen_cmd> = pwgen -s 23 1
Command used to generate passwords. See the B<-p> option.
=item B<xclip_cmd> = xclip -l 1
Command used to place passwords in the clipboard. See the B<-x> option.
=back
Note that commandline arguments always override options set in the
configuration file.
=head1 DEPENDENCIES
=over
=item * Config::Tiny
=item * Crypt::CBC
=item * Crypt::Eksblowfish
=item * File::BaseDir
=item * File::Path (usually included with perl core)
=item * File::Slurp
=item * pwgen (if you want C<< raps2 add >> to generate passwords)
=item * xclip (for C<< raps2 get >>)
=back
=head1 BUGS AND LIMITATIONS
This is alpha software, the store format may change without further notice.
Backwards-compatibility is not guaranteed.
When running for the first time, raps2 will ask for the master passphrase
three times. Two would be better.
=head1 AUTHOR
Copyright (C) 2011-2015 by Daniel Friesel E<lt>derf@finalrewind.orgE<gt>
=head1 LICENSE
0. You just DO WHAT THE FUCK YOU WANT TO.
( run in 1.306 second using v1.01-cache-2.11-cpan-2398b32b56e )