commit - ca70700ed21b39d87999d27df889121d54dfaf35
commit + 20a755b977dcfaf5be88243847c1730720fbf79f
blob - 372418686f1931995eb2d6be7e1dca9a342d4cdc (mode 755)
blob + /dev/null
--- examples/igpm
+++ /dev/null
-#!/bin/sh
-
-selector=${SELECTOR:-fzf}
-justprint=false
-allenable=true
-tmux=tmux
-primary=primary
-clipboard=clipboard
-out=out
-
-while getopts COPTcopt name; do
- case $name in
- C) clipboard= ;;
- O) out= ;;
- P) primary= ;;
- T) tmux= ;;
- [copt])
- if $allenable; then
- allenable=false
- tmux=
- primary=
- clipboard=
- out=
- fi
-
- case $name in
- c) clipboard=clipboard ;;
- o) out=out ;;
- p) primary=primary ;;
- t) tmux=tmux ;;
- esac
- ;;
- ?) echo "usage: igpm [-COPTcopt] [file ...]" >&2; exit 1 ;;
- esac
-done
-shift $((OPTIND - 1))
-
-test -n "$TMUX" || tmux=
-test -n "$DISPLAY" || { primary=; clipboard=; }
-
-clip="$tmux $primary $clipboard $out"
-nclip=0
-for c in $clip; do nclip=$((nclip + 1)); done
-[ $nclip -gt 0 ] || { echo "nowhere to copy" 2>&1; exit 1; }
-[ $nclip -eq 1 ] || clip="$(printf %s\\n $clip | $selector)"
-
-case $clip in
-tmux)
- test "$tmux" || exit 1
- GPM_COPY_INCMD='tmux loadb -b _gpm -'
- GPM_COPY_OUTCMD='tmux showb -b _gpm'
- GPM_COPY_DELCMD='tmux deleteb -b _gpm'
- ;;
-primary)
- test "$primary" || exit 1
- GPM_COPY_INCMD=xclip
- GPM_COPY_OUTCMD='xclip -o'
- GPM_COPY_DELCMD='xclip </dev/null'
- ;;
-clipboard)
- test "$clipboard" || exit 1
- GPM_COPY_INCMD='xclip -selection c'
- GPM_COPY_OUTCMD='xclip -selection c -o'
- GPM_COPY_DELCMD='xclip -selection c </dev/null'
- ;;
-out)
- test "$out" || exit 1
- justprint=true
- ;;
-*)
- exit 1
- ;;
-esac
-export GPM_COPY_INCMD GPM_COPY_OUTCMD GPM_COPY_DELCMD
-
-if [ "$#" -eq 1 ]; then
- pw="$1"
-else
- pw="$(
- if [ "$#" -gt 0 ]; then
- for i; do printf %s\\n "$i"; done
- else
- gpm ls
- fi | $selector
- )"
-fi
-
-if $justprint; then
- gpm show "$pw"
-else
- gpm copy "$pw"
-fi
blob - /dev/null
blob + 372418686f1931995eb2d6be7e1dca9a342d4cdc (mode 755)
--- /dev/null
+++ examples/isecstore
+#!/bin/sh
+
+selector=${SELECTOR:-fzf}
+justprint=false
+allenable=true
+tmux=tmux
+primary=primary
+clipboard=clipboard
+out=out
+
+while getopts COPTcopt name; do
+ case $name in
+ C) clipboard= ;;
+ O) out= ;;
+ P) primary= ;;
+ T) tmux= ;;
+ [copt])
+ if $allenable; then
+ allenable=false
+ tmux=
+ primary=
+ clipboard=
+ out=
+ fi
+
+ case $name in
+ c) clipboard=clipboard ;;
+ o) out=out ;;
+ p) primary=primary ;;
+ t) tmux=tmux ;;
+ esac
+ ;;
+ ?) echo "usage: igpm [-COPTcopt] [file ...]" >&2; exit 1 ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+test -n "$TMUX" || tmux=
+test -n "$DISPLAY" || { primary=; clipboard=; }
+
+clip="$tmux $primary $clipboard $out"
+nclip=0
+for c in $clip; do nclip=$((nclip + 1)); done
+[ $nclip -gt 0 ] || { echo "nowhere to copy" 2>&1; exit 1; }
+[ $nclip -eq 1 ] || clip="$(printf %s\\n $clip | $selector)"
+
+case $clip in
+tmux)
+ test "$tmux" || exit 1
+ GPM_COPY_INCMD='tmux loadb -b _gpm -'
+ GPM_COPY_OUTCMD='tmux showb -b _gpm'
+ GPM_COPY_DELCMD='tmux deleteb -b _gpm'
+ ;;
+primary)
+ test "$primary" || exit 1
+ GPM_COPY_INCMD=xclip
+ GPM_COPY_OUTCMD='xclip -o'
+ GPM_COPY_DELCMD='xclip </dev/null'
+ ;;
+clipboard)
+ test "$clipboard" || exit 1
+ GPM_COPY_INCMD='xclip -selection c'
+ GPM_COPY_OUTCMD='xclip -selection c -o'
+ GPM_COPY_DELCMD='xclip -selection c </dev/null'
+ ;;
+out)
+ test "$out" || exit 1
+ justprint=true
+ ;;
+*)
+ exit 1
+ ;;
+esac
+export GPM_COPY_INCMD GPM_COPY_OUTCMD GPM_COPY_DELCMD
+
+if [ "$#" -eq 1 ]; then
+ pw="$1"
+else
+ pw="$(
+ if [ "$#" -gt 0 ]; then
+ for i; do printf %s\\n "$i"; done
+ else
+ gpm ls
+ fi | $selector
+ )"
+fi
+
+if $justprint; then
+ gpm show "$pw"
+else
+ gpm copy "$pw"
+fi
blob - 97ee7d297f1c176c3003c6c17fa32c6fe05c0b01 (mode 755)
blob + /dev/null
--- gpm
+++ /dev/null
-#!/usr/bin/perl
-
-# Copyright (c) 2023 Alex Arx <aa@manpager.org>
-#
-# Permission to use, copy, modify, and distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-use strict;
-use warnings;
-
-use File::Basename;
-use File::Path 'make_path';
-use Getopt::Std;
-use IPC::Open2;
-use POSIX qw(setsid);
-use Text::ParseWords;
-
-use Digest::SHA 'sha256';
-
-our ($opt_g, $opt_d, $opt_r);
-my $gpg;
-
-# usage: print usage information to stderr and exit with error.
-sub usage {
- die
-"usage: gpm [-g command] [-d dir] add [-Nnm] name\n" .
-" gpm [-g command] [-d dir] copy [-Nnm] [-d delcmd] [-i incmd]\n" .
-" [-o outcmd] [-s time] name\n" .
-" gpm [-d dir] rm name ...\n" .
-" gpm [-g command] [-d dir] show [-Nn] name\n" .
-" gpm [-d dir] mv from to\n" .
-" gpm [-d dir] ls\n";
-}
-
-# getflags: put environment flags at the start of @ARGV
-sub getflags {
- my ($var) = @_;
- unshift(@ARGV, shellwords $ENV{$var} // '') if defined $var && $var ne '';
-}
-
-# shellquote: return string suitable for using as an argument for sh
-sub shellquote {
- my ($s) = @_;
- $s =~ s/'/'\\''/g;
- return "'" . $s . "'";
-}
-
-# getrecipient: return string to be used with gpg's -r option.
-#
-# Past versions of gpm (written in shell) required explicit recipient, set by
-# option -r, or the GPM_RECIPIENT environment variable. This is completely
-# unnecessary now, due to GPM_GPG and -g, but legacy syntax is still maintained.
-sub getrecipient {
- return shellquote($opt_r // $ENV{GPM_RECIPIENT});
-}
-
-# ckpath $path
-#
-# Return 1 if $path doesn't start with /, or contain .. files, and 0 otherwise.
-sub ckpath {
- my ($p) = @_;
- return $p !~ m,^/, && $p ne ".." && $p !~ m,^\.\./, && $p !~ m,/\.\./,
- && $p !~ m,/\.\.$,;
-}
-
-# cklegacy $f
-#
-# Returns the correct path for $f (with or without the .gpg suffix), or dies
-# if no such file exists.
-sub cklegacy {
- my ($f) = @_;
- if (!-f $f) {
- my $f2 = $f . ".gpg";
- -f $f2 or die "neither $f, nor $f2 exist as regular files\n";
- return $f2;
- }
- return $f;
-}
-
-# prunetree $d: remove empty directories, starting from $d, and going up.
-sub prunetree {
- for (my ($d) = @_; $d ne "."; $d = dirname $d) {
- rmdir $d or last;
- }
-}
-
-# add
-#
-# Encrypt the secret from stdin, and store the ciphertext in file specified
-# on the command line.
-sub add {
- our ($opt_N, $opt_n, $opt_m);
- my ($r, $sec) = (getrecipient(), '');
-
- getopts('Nnm') or usage();
- $#ARGV >= 0 or usage();
-
- my $cmd = $ENV{GPM_ENCCMD} //
- "$gpg -e" . (defined $r ? " -r $r" : "") . " --";
-
- my $outfile = $ARGV[0];
- ckpath $outfile or die "bad path: $outfile\n";
-
- -e $outfile and die "$outfile already exists\n";
-
- if (-t STDIN && !$opt_m) {
- open TTY, "/dev/tty" || die "couldn't open /dev/tty: $!\n";
- system "stty -echo";
-
- print "Secret:";
- $sec = <TTY>;
- print "\n";
- print "Repeat:";
- my $sec2 = <TTY>;
- print "\n";
- if ($opt_n && !$opt_N) {
- chomp $sec;
- chomp $sec2;
- }
-
- system "stty echo";
- close TTY;
- die "Sorry\n" if $sec ne $sec2;
- undef $sec2;
- } else {
- while (<STDIN>) { $sec .= $_; }
- }
-
- my $pid = open2(my $reader, my $writer, $cmd);
- print $writer $sec;
- undef $sec;
- close $writer;
- my $out = '';
- while (<$reader>) { $out .= $_; }
- waitpid $pid, 0;
- $? == 0 or exit 1;
-
- my $d = dirname $outfile;
- make_path $d, {mode => 0700};
- umask 0377;
- unless (open FH, ">$outfile") {
- prunetree $d;
- die "couldn't open $outfile for writing: $!\n";
- }
- unless (print FH $out) {
- prunetree $d;
- die "couldn't write to $outfile: $!\n";
- }
-}
-
-# ls
-#
-# Produce a listing of files in the gpm directory, using the GPM_LSCMD
-# environment variable if defined.
-sub ls {
- my $cmd = $ENV{GPM_LSCMD};
-
- unless (defined $cmd) {
- opendir my $dh, "." or die "couldn't open directory .: $!\n";
- while (readdir $dh) {
- if ($_ ne "." && $_ ne ".." && -d $_) {
- $cmd = "find . -type f | sed 's,^\./,,'";
- last;
- }
- }
- $cmd = "ls" unless defined $cmd;
- }
- system $cmd;
- $? == 0 or exit 1;
-}
-
-# mv: safely rename $ARGV[0] to $ARGV[1].
-sub mv {
- $#ARGV >= 1 || usage();
- my ($from, $to) = @ARGV;
-
- ckpath $from or die "bad path $from\n";
- ckpath $to or die "bad path $to\n";
- -e $to and die "$to already existst\n";
- $from = cklegacy $from;
-
- make_path(dirname $to, {mode => 0700});
- rename $from, $to;
- prunetree(dirname $from);
-}
-
-# rm: unlink arguments, asking each time.
-sub rm {
- $#ARGV >= 0 || usage();
- for (@ARGV) {
- my $f = $_;
- unless (ckpath $f) {
- print STDERR "bad path $f\n";
- next;
- }
- $f = cklegacy $f;
- print "Really remove $f? ";
- <STDIN> =~ m/^[Yy]/ and unlink $f;
- prunetree(dirname $f);
- }
-}
-
-# get: decrypt file, and return plaintext.
-sub get {
- my ($Nflag, $nflag) = @_;
- $#ARGV >= 0 or usage();
- my $file = $ARGV[0];
- my $cmd = $ENV{GPM_DECCMD} // "$gpg -d --";
-
- ckpath $file or die "bad path $file\n";
-
- open(my $freader, "<", $file) or die "Can't open < $file: $!";
- my $pid = open2(my $cmdreader, my $cmdwriter, $cmd);
- print $cmdwriter $_ while (<$freader>);
- close $cmdwriter;
- close $freader;
- my $out = '';
- while (<$cmdreader>) { $out .= $_; }
- waitpid $pid, 0;
- $? == 0 or exit 1;
- chomp($out) if ($nflag && !$Nflag);
-
- return $out;
-}
-
-# show: decrypt file, and print plaintext to stdout.
-sub show {
- our ($opt_N, $opt_n);
- getopts('Nn') or usage();
- print(get $opt_N, $opt_n);
-}
-
-# copy: decrypt file, and copy to GPM_COPY_INCMD, delete with GPM_COPY_DELCMD
-# after GPM_COPY_SLEEP seconds if necessary.
-sub copy {
- our ($opt_N, $opt_n, $opt_d, $opt_i, $opt_o, $opt_s);
-
- getopts('Nnd:i:o:s:') or usage();
- $#ARGV >= 0 or usage();
-
- my $delcmd = $opt_d // $ENV{GPM_COPY_DELCMD} // "xclip </dev/null";
- my $incmd = $opt_i // $ENV{GPM_COPY_INCMD} // "xclip";
- my $outcmd = $opt_o // $ENV{GPM_COPY_OUTCMD} // "xclip -o";
- my $sleep = $opt_s // $ENV{GPM_COPY_SLEEP} // 60;
-
- my $pw = get $opt_N, $opt_n;
-
- # This is a huge cludge. The reason we have to do copying inside a detached
- # process is because otherwise the following doesn't work (assuming xclip):
- #
- # $ tmux popup -E 'GPG_TTY=`tty` igpm' && sleep 1 && xclip -o
- my $pid = fork();
- if (not defined $pid) {
- die "Fork failed: $!\n";
- } elsif ($pid == 0) {
- POSIX::setsid();
-
- open(FH, "|-", $incmd) or
- die "Could not open command '$incmd': $!\n";
- print FH $pw;
- close FH;
- if ($? != 0) {
- system $delcmd;
- exit 1;
- }
-
- if ($sleep < 1) {
- exit 0;
- }
-
- $pw = sha256 $pw;
- $pid = fork();
- if (not defined $pid) {
- system $delcmd;
- die "Fork failed: $!\n";
- } elsif ($pid == 0) {
- open(STDOUT, '>/dev/null');
- open(STDERR, '>/dev/null');
- POSIX::setsid();
- sleep $sleep;
- system($delcmd) if
- (sha256(`$outcmd`) eq $pw);
- }
- } else {
- waitpid $pid, 0;
- exit $?;
- }
-}
-
-getopts('g:d:r') or usage();
-
-$#ARGV >= 0 or usage();
-my $cmd = $ARGV[0];
-shift @ARGV;
-
-$gpg = $opt_g // $ENV{GPM_GPG} // "gpg";
-$gpg = $opt_g // $ENV{GPM_GPG} // "gpg";
-
-my $gpmd = $opt_d // $ENV{GPM_DIR};
-unless (defined $gpmd) {
- if (defined $ENV{XDG_DATA_HOME}) {
- $gpmd = $ENV{XDG_DATA_HOME} . "/gpm";
- } elsif (defined $ENV{HOME}) {
- $gpmd = $ENV{HOME} . "/.gpm";
- } else {
- die "couldn't determine the gpm directory\n";
- }
-}
-
-make_path $gpmd, {mode => 0700};
-chdir $gpmd or die "couldn't change directory to $gpmd: $!\n";
-
-for ($cmd) {
-if (/^a/) { add(getflags "GPM_ADD"); last; }
-if (/^c/) { copy(getflags "GPM_COPY"); last; }
-if (/^l/) { ls(getflags "GPM_LS"); last; }
-if (/^m/) { mv(getflags "GPM_MV"); last; }
-if (/^r/) { rm(getflags "GPM_RM"); last; }
-if (/^s/) { show(getflags "GPM_SHOW"); last; }
- usage();
-}
blob - /dev/null
blob + 8937fb813de98de4c00a4fdeb9eeea1c86aab67b (mode 755)
--- /dev/null
+++ secstore
+#!/usr/bin/perl
+
+# Copyright (c) 2023-2025 Alex Arch <aa@manpager.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+
+use File::Basename;
+use File::Path 'make_path';
+use Getopt::Std;
+use IPC::Open2;
+use POSIX qw(setsid);
+use Text::ParseWords;
+
+use Digest::SHA 'sha256';
+
+our $opt_d;
+
+# usage: print usage information to stderr and exit with error.
+sub usage {
+ die "usage: secstore [-d dir] command [arg ...]\n";
+}
+
+# getflags: put environment flags at the start of @ARGV
+sub getflags {
+ my ($var) = @_;
+ unshift(@ARGV, shellwords $ENV{$var} // '') if defined $var && $var ne '';
+}
+
+# shellquote: return string suitable for using as an argument for sh
+sub shellquote {
+ my ($s) = @_;
+ $s =~ s/'/'\\''/g;
+ return "'" . $s . "'";
+}
+
+# ckpath $path
+#
+# Return 1 if $path is not empty, doesn't start with /, or contain .. or .,
+# and 0 otherwise.
+sub ckpath {
+ my ($p) = @_;
+ return $p ne '' && $p !~ m,^/, && $p !~ m/\.\.?/ && $p !~ m,^\.\.?/, &&
+ $p !~ m,/\.\.?/, && $p !~ m,/\.\.?$,;
+}
+
+# prunetree $d: remove empty directories, starting from $d, and going up.
+sub prunetree {
+ for (my ($d) = @_; $d ne "."; $d = dirname $d) {
+ rmdir $d or last;
+ }
+}
+
+# secstore_add
+#
+# Encrypt the secret from stdin, and store the ciphertext in file specified
+# on the command line.
+sub secstore_add {
+ our ($opt_N, $opt_n);
+ my ($cmd, $sec) = ($ENV{SECSTORE_ENCCMD} // 'gpg -e --', '');
+
+ local *usage = sub {
+ die "usage: secstore add [-Nn] name\n";
+ };
+
+ getopts('Nn') && scalar(@ARGV) == 1 or usage();
+
+ my $outfile = $ARGV[0];
+ ckpath $outfile or die "bad path: $outfile\n";
+
+ -e $outfile and die "$outfile already exists\n";
+
+ if (-t STDIN) {
+ open TTY, "/dev/tty" || die "couldn't open /dev/tty: $!\n";
+ system "stty -echo";
+
+ print "Secret:";
+ $sec = <TTY>;
+ print "\n";
+ print "Repeat:";
+ my $sec2 = <TTY>;
+ print "\n";
+ if ($opt_n && !$opt_N) {
+ chomp $sec;
+ chomp $sec2;
+ }
+
+ system "stty echo";
+ close TTY;
+ die "Sorry\n" if $sec ne $sec2;
+ undef $sec2;
+ } else {
+ while (<STDIN>) { $sec .= $_; }
+ }
+
+ my $pid = open2(my $reader, my $writer, $cmd);
+ print $writer $sec;
+ undef $sec;
+ close $writer;
+ my $out = '';
+ while (<$reader>) { $out .= $_; }
+ waitpid $pid, 0;
+ $? == 0 or exit 1;
+
+ my $d = dirname $outfile;
+ make_path $d, {mode => 0700};
+ umask 0377;
+ unless (open(FH, ">$outfile") && print(FH $out)) {
+ prunetree $d;
+ die "couldn't write to $outfile: $!\n";
+ }
+}
+
+# secstore_list
+#
+# Produce a listing of files in the gpm directory, using the SECSTORE_LSCMD
+# environment variable if defined.
+sub secstore_list {
+ my $cmd = $ENV{SECSTORE_LSCMD};
+
+ unless (defined $cmd) {
+ opendir my $dh, "." or die "couldn't open directory .: $!\n";
+ while (readdir $dh) {
+ if ($_ ne "." && $_ ne ".." && -d $_) {
+ $cmd = "find . -type f | sed 's,^\./,,'";
+ last;
+ }
+ }
+ $cmd = "ls" unless defined $cmd;
+ }
+ system $cmd;
+ $? == 0 or exit 1;
+}
+
+# secstore_move: safely rename $ARGV[0] to $ARGV[1].
+sub secstore_move {
+ local *usage = sub {
+ die "usage: secstore move from to\n";
+ };
+ scalar(@ARGV) == 2 || usage();
+ my ($from, $to) = @ARGV;
+
+ ckpath $from or die "bad path $from\n";
+ ckpath $to or die "bad path $to\n";
+ -e $to and die "$to already existst\n";
+
+ make_path(dirname $to, {mode => 0700});
+ rename $from, $to;
+ prunetree(dirname $from);
+}
+
+# secstore_remove: unlink arguments, asking each time.
+sub secstore_remove {
+ local *usage = sub {
+ die "usage: secstore remove name [name ...]\n";
+ };
+ scalar(@ARGV) >= 1 || usage();
+ for (@ARGV) {
+ my $f = $_;
+ unless (ckpath $f) {
+ print STDERR "bad path $f\n";
+ next;
+ }
+ print "Really remove $f? ";
+ <STDIN> =~ m/^[Yy]/ and unlink $f;
+ prunetree(dirname $f);
+ }
+}
+
+# get: decrypt file, and return plaintext.
+sub get {
+ my ($Nflag, $nflag, $file) = @_;
+ my $cmd = $ENV{SECSTORE_DECCMD} // "gpg -dq --";
+
+ ckpath $file or die "bad path: $file\n";
+ -f $file or die "no such file: $file\n";
+
+ open(my $freader, "<$file") or die "Couldn't open <$file: $!\n";
+ my $pid = open2(my $cmdreader, my $cmdwriter, $cmd);
+ print $cmdwriter $_ while (<$freader>);
+ close $cmdwriter;
+ close $freader;
+ my $out = '';
+ while (<$cmdreader>) { $out .= $_; }
+ waitpid $pid, 0;
+ $? == 0 or exit 1;
+ chomp($out) if ($nflag && !$Nflag);
+
+ return $out;
+}
+
+# secstore_print: decrypt file, and print plaintext to stdout.
+sub secstore_print {
+ our ($opt_N, $opt_n);
+ local *usage = sub {
+ die "usage: secstore print [-Nn] name ...";
+ };
+ getopts('Nn') && scalar(@ARGV) == 1 or usage();
+ print(get $opt_N, $opt_n, $ARGV[0]);
+}
+
+# copy: decrypt file, and copy to SECSTORE_COPY_INCMD, delete with
+# SECSTORE_COPY_DELCMD after SECSTORE_COPY_SLEEP seconds if necessary.
+sub secstore_copy {
+ our ($opt_N, $opt_n, $opt_d, $opt_i, $opt_o, $opt_s);
+ local *usage = sub {
+ die
+"usage: secstore copy [-Nn] [-d delcmd] [-i incmd] [-o outcmd] [-s sleeptime]\n" .
+" name\n"
+ };
+
+ getopts('Nnd:i:o:s:') && scalar(@ARGV) == 1 or usage();
+
+ my $delcmd = $opt_d // $ENV{SECSTORE_COPY_DELCMD} // "xclip </dev/null";
+ my $incmd = $opt_i // $ENV{SECSTORE_COPY_INCMD} // "xclip";
+ my $outcmd = $opt_o // $ENV{SECSTORE_COPY_OUTCMD} // "xclip -o";
+ my $sleep = $opt_s // $ENV{SECSTORE_COPY_SLEEP} // 60;
+
+ my $pw = get $opt_N, $opt_n, $ARGV[0];
+
+ # This is a huge cludge. The reason we have to do copying inside a detached
+ # process is because otherwise the following doesn't work (assuming xclip):
+ #
+ # $ tmux popup -E 'GPG_TTY=`tty` isecstore' && sleep 1 && xclip -o
+ my $pid = fork();
+ if (not defined $pid) {
+ $pw = '';
+ die "Fork failed: $!\n";
+ } elsif ($pid == 0) {
+ POSIX::setsid();
+
+ open(FH, "|-", $incmd) or
+ die "Could not open command '$incmd': $!\n";
+ print FH $pw;
+ close FH;
+ if ($? != 0) {
+ system $delcmd;
+ exit 1;
+ }
+
+ if ($sleep < 1) {
+ exit 0;
+ }
+
+ $pw = sha256 $pw;
+ $pid = fork();
+ if (not defined $pid) {
+ system $delcmd;
+ die "Fork failed: $!\n";
+ } elsif ($pid == 0) {
+ open(STDOUT, '>/dev/null');
+ open(STDERR, '>/dev/null');
+ POSIX::setsid();
+ sleep $sleep;
+ system($delcmd) if
+ (sha256(`$outcmd`) eq $pw);
+ }
+ } else {
+ $pw = '';
+ waitpid $pid, 0;
+ exit $?;
+ }
+}
+
+getopts('d:') or usage();
+
+scalar(@ARGV) >= 1 or usage();
+my $cmd = $ARGV[0];
+shift @ARGV;
+
+my $secstore_dir = $opt_d // $ENV{SECSTORE_DIR};
+unless (defined $secstore_dir) {
+ if (defined $ENV{XDG_DATA_HOME}) {
+ $secstore_dir = $ENV{XDG_DATA_HOME} . "/secstore";
+ } elsif (defined $ENV{HOME}) {
+ $secstore_dir = $ENV{HOME} . "/.secstore";
+ } else {
+ die "couldn't determine the secstore directory\n";
+ }
+}
+
+make_path $secstore_dir, {mode => 0700};
+chdir $secstore_dir or die "couldn't change directory to $secstore_dir: $!\n";
+
+for ($cmd) {
+if (/^add$/) {
+ secstore_add(getflags "SECSTORE_ADD");
+} elsif (/^(cp|copy)$/) {
+ secstore_copy(getflags "SECSTORE_COPY");
+} elsif (/^(ls|list)$/) {
+ secstore_list(getflags "SECSTORE_LIST");
+} elsif (/^(mv|move)$/) {
+ secstore_move(getflags "SECSTORE_MOVE");
+} elsif (/^print$/) {
+ secstore_print(getflags "SECSTORE_PRINT");
+} elsif (/^(rm|remove)$/) {
+ secstore_remove(getflags "SECSTORE_REMOVE");
+} else {
+ die "unknown command: " . $cmd . "\n"
+}
+}
blob - c76d3c2a3348814bae7bf9a3eab0a09a73b04e45 (mode 644)
blob + /dev/null
--- gpm.1
+++ /dev/null
-.\" Copyright (c) 2023 Alex Arx <aa@manpager.org>
-.\"
-.\" Permission to use, copy, modify, and distribute this software for any
-.\" purpose with or without fee is hereby granted, provided that the above
-.\" copyright notice and this permission notice appear in all copies.
-.\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.\"
-.Dd November 16, 2024
-.Dt GPM 1
-.Os
-.Sh NAME
-.Nm gpm
-.Nd general secret (or password) manager
-.Sh SYNOPSIS
-.Nm
-.Op Fl d Ar dir
-.Ar command
-.Op Ar arg ...
-.Sh DESCRIPTION
-The utility
-.Nm
-is a secret manager.
-Secrets are stored in a directory tree as files, encrypted with
-.Xr gpg 1
-or a custom command (see
-.Sx ENVIRONMENT ) .
-.Nm
-provides several commands for manipulating secrets.
-Commands may be specified by their shortest unique prefix (all characters
-after are ignored).
-Commands may accept additional arguments.
-Commands may be preceeded by global options as follows:
-.Bl -tag -width Ds
-.It Fl d Ar dir
-The directory to store and retrieve secrets from.
-Overrides
-.Ev GPM_DIR .
-.El
-.Pp
-The
-.Nm
-commands are as follows:
-.Bl -tag -width Ds
-.It Xo
-.Cm add
-.Op Fl Nnm
-.Ar name
-.Xc
-Create a new secret
-.Ar name .
-The new secret is read from stdin.
-If used from a TTY without the
-.Fl m
-flag, a single line is read twice, and not echoed.
-Otherwise, an arbitrary amount of lines is read normally once.
-If
-.Fl n
-is specified without
-.Fl N
-chop off any last newline character of input.
-.Pp
-Options in the
-.Ev GPM_ADD
-environment variable are passed to
-.Nm
-.Cm add
-automatically, before any options specified on the command line.
-.It Xo
-.Cm copy
-.Op Fl Nn
-.Op Fl d Ar delcmd
-.Op Fl i Ar incmd
-.Op Fl o Ar outcmd
-.Op Fl s Ar time
-.Ar name
-.Xc
-Copy the secret
-.Ar name
-by piping it to
-.Ar incmd .
-After
-.Ar time
-seconds, run
-.Ar delcmd
-if
-.Ar outcmd
-prints plaintext secret
-.Ar name
-on standard output.
-Without
-.Fl d , i , o
-or
-.Fl s ,
-the values of
-.Ev GPM_COPY_DELCMD , GPM_COPY_INCMD , GPM_COPY_OUTCMD
-and
-.Ev GPM_COPY_SLEEP
-are used.
-The default values are
-.Ql xclip </dev/null ,
-.Ql xclip ,
-.Ql xclip -o
-and 60.
-If
-.Ar time
-is 0,
-.Ar delcmd
-is not run.
-If
-.Fl n
-is specified without
-.Fl N ,
-chop off any last newline character before passing the secret to
-.Ar incmd .
-.Pp
-Options in the
-.Ev GPM_COPY
-environment variable are passed to
-.Nm
-.Cm copy
-automatically, before any options specified on the command line.
-.It Cm ls
-List existing secrets, using the command in
-.Ev GPM_LSCMD
-if set.
-.Pp
-Options in the
-.Ev GPM_LS
-environment variable are passed to
-.Nm
-.Cm ls
-automatically, before any options specified on the command line.
-.It Cm mv Ar from Ar to
-Rename secret
-.Ar from
-to
-.Ar to .
-.Pp
-Options in the
-.Ev GPM_MV
-environment variable are passed to
-.Nm
-.Cm mv
-automatically, before any options specified on the command line.
-.It Cm rm Ar name ...
-Remove secrets specified on the command line.
-.Nm
-will ask for confirmation before each removal.
-.Pp
-Options in the
-.Ev GPM_RM
-environment variable are passed to
-.Nm
-.Cm rm
-automatically, before any options specified on the command line.
-.It Xo
-.Cm show
-.Op Fl Nn
-.Ar name
-.Xc
-Decrypt the secret
-.Ar name ,
-and print plaintext to stdout.
-If
-.Fl n
-is specified without
-.Fl N ,
-chop off any last newline character of output.
-.Pp
-Options in the
-.Ev GPM_SHOW
-environment variable are passed to
-.Nm
-.Cm show
-automatically, before any options specified on the command line.
-.El
-.Sh ENVIRONMENT
-.Bl -tag -width XDG_DATA_HOME
-.It Ev GPM_ENCCMD
-Command used for encryption.
-.Dq gpg -e \-\-
-if not set.
-.It Ev GPM_DECCMD
-Command used for decryption.
-.Dq gpg -d \-\-
-if not set.
-.It Ev GPM_DIR
-Directory in which the secrets are stored.
-.It Ev XDG_DATA_HOME
-If
-.Ev XDG_DATA_HOME
-is set, but
-.Ev GPM_DIR
-isn't, the default secret directory is
-.Pa $XDG_DATA_HOME/gpm .
-.El
-.Sh FILES
-.Bl -tag -width Ds
-.It Pa $HOME/.gpm
-The default secret directory if neither
-.Ev GPM_DIR ,
-nor
-.Ev XDG_DATA_HOME
-are set.
-.El
-.Sh EXIT STATUS
-.Ex -std
-.Sh EXAMPLES
-Tab-completion may be set, e.g. with
-.Xr ksh 1 :
-.Bd -literal -offset indent
-set -A complete_gpm_1 -- add copy ls mv rm show
-set -A complete_gpm -- $(gpm ls)
-.Ed
-.Sh SEE ALSO
-.Xr gpg 1 ,
-.Xr gpg2 1 ,
-.Xr pm 1 ,
-.Xr xclip 1
-.Sh AUTHORS
-.An Alex Arx Aq Mt aa@manpager.org .
blob - /dev/null
blob + 3f4c6215578c0088749e90727d005c3ac6259377 (mode 644)
--- /dev/null
+++ secstore.1
+.\" Copyright (c) 2023-2025 Alex Arch <aa@manpager.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd February 15, 2025
+.Dt SECSTORE 1
+.Os
+.Sh NAME
+.Nm secstore
+.Nd manage secure (or secret) storage
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar dir
+.Ar command
+.Op Ar arg ...
+.Sh DESCRIPTION
+The utility
+.Nm
+is a secret manager.
+Secrets are stored in a directory tree as files, encrypted with
+.Xr gpg 1
+or a custom command (see
+.Sx ENVIRONMENT ) .
+.Nm
+provides several commands for manipulating secrets.
+Commands may be specified by their shortest unique prefix (all characters
+after are ignored).
+Commands may accept additional arguments.
+Commands may be preceeded by global options as follows:
+.Bl -tag -width Ds
+.It Fl d Ar dir
+The directory to store and retrieve secrets from.
+Overrides
+.Ev GPM_DIR .
+.El
+.Pp
+The
+.Nm
+commands are as follows:
+.Bl -tag -width Ds
+.It Xo
+.Cm add
+.Op Fl Nnm
+.Ar name
+.Xc
+Create a new secret
+.Ar name .
+The new secret is read from stdin.
+If used from a TTY without the
+.Fl m
+flag, a single line is read twice, and not echoed.
+Otherwise, an arbitrary amount of lines is read normally once.
+If
+.Fl n
+is specified without
+.Fl N
+chop off any last newline character of input.
+.Pp
+Options in the
+.Ev SECSTORE_ADD
+environment variable are passed to
+.Nm
+.Cm add
+automatically, before any options specified on the command line.
+.It Xo
+.Cm copy
+.Op Fl Nn
+.Op Fl d Ar delcmd
+.Op Fl i Ar incmd
+.Op Fl o Ar outcmd
+.Op Fl s Ar time
+.Ar name
+.Xc
+Copy the secret
+.Ar name
+by piping it to
+.Ar incmd .
+After
+.Ar time
+seconds, run
+.Ar delcmd
+if
+.Ar outcmd
+prints plaintext secret
+.Ar name
+on standard output.
+Without
+.Fl d , i , o
+or
+.Fl s ,
+the values of
+.Ev SECSTORE_COPY_DELCMD , SECSTORE_COPY_INCMD , SECSTORE_COPY_OUTCMD
+and
+.Ev SECSTORE_COPY_SLEEP
+are used.
+The default values are
+.Ql xclip </dev/null ,
+.Ql xclip ,
+.Ql xclip -o
+and 60.
+If
+.Ar time
+is 0,
+.Ar delcmd
+is not run.
+If
+.Fl n
+is specified without
+.Fl N ,
+chop off any last newline character before passing the secret to
+.Ar incmd .
+.Pp
+Options in the
+.Ev SECSTORE_COPY
+environment variable are passed to
+.Nm
+.Cm copy
+automatically, before any options specified on the command line.
+.It Cm ls
+List existing secrets, using the command in
+.Ev SECSTORE_LSCMD
+if set.
+.Pp
+Options in the
+.Ev SECSTORE_LS
+environment variable are passed to
+.Nm
+.Cm ls
+automatically, before any options specified on the command line.
+.It Cm mv Ar from Ar to
+Rename secret
+.Ar from
+to
+.Ar to .
+.Pp
+Options in the
+.Ev SECSTORE_MV
+environment variable are passed to
+.Nm
+.Cm mv
+automatically, before any options specified on the command line.
+.It Cm rm Ar name ...
+Remove secrets specified on the command line.
+.Nm
+will ask for confirmation before each removal.
+.Pp
+Options in the
+.Ev SECSTORE_RM
+environment variable are passed to
+.Nm
+.Cm rm
+automatically, before any options specified on the command line.
+.It Xo
+.Cm show
+.Op Fl Nn
+.Ar name
+.Xc
+Decrypt the secret
+.Ar name ,
+and print plaintext to stdout.
+If
+.Fl n
+is specified without
+.Fl N ,
+chop off any last newline character of output.
+.Pp
+Options in the
+.Ev SECSTORE_SHOW
+environment variable are passed to
+.Nm
+.Cm show
+automatically, before any options specified on the command line.
+.El
+.Sh ENVIRONMENT
+.Bl -tag -width XDG_DATA_HOME
+.It Ev SECSTORE_ENCCMD
+Command used for encryption.
+.Dq gpg -e \-\-
+if not set.
+.It Ev SECSTORE_DECCMD
+Command used for decryption.
+.Dq gpg -d \-\-
+if not set.
+.It Ev SECSTORE_DIR
+Directory in which the secrets are stored.
+.It Ev XDG_DATA_HOME
+If
+.Ev XDG_DATA_HOME
+is set, but
+.Ev SECSTORE_DIR
+isn't, the default secret directory is
+.Pa $XDG_DATA_HOME/secstore .
+.El
+.Sh FILES
+.Bl -tag -width Ds
+.It Pa $HOME/.secstore
+The default secret directory if neither
+.Ev SECSTORE_DIR ,
+nor
+.Ev XDG_DATA_HOME
+is set.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+Tab-completion may be set, e.g. with
+.Xr ksh 1 :
+.Bd -literal -offset indent
+set -A complete_secstore_1 -- add copy ls mv rm show
+set -A complete_secstore -- $(secstore ls)
+.Ed
+.Sh SEE ALSO
+.Xr gpass 1 ,
+.Xr gpg 1 ,
+.Xr gpg2 1 ,
+.Xr xclip 1
+.Sh AUTHORS
+.An Alex Arch Aq Mt aa@manpager.org .
blob - 2aeffa86852e766a8f3396cf8e063ad4f080e24d (mode 755)
blob + /dev/null
--- pm2gpm
+++ /dev/null
-#!/bin/sh
-
-files=$(pm l | tr \\n ' ')
-awk -vfiles="$files" -vstty="$(stty -g)" '
- BEGIN {
- system("stty -echo")
- printf("pass:")
- getline pass
- system("stty " stty)
-
- split(files, a)
- for (f in a)
- print pass
- }
-' | for f in $files; do
- pm s "$f" | gpm "$@" add "$f"
-done