diff options
Diffstat (limited to 'litterbox')
| -rwxr-xr-x | litterbox | 465 |
1 files changed, 465 insertions, 0 deletions
diff --git a/litterbox b/litterbox new file mode 100755 index 0000000..ad730ab --- /dev/null +++ b/litterbox @@ -0,0 +1,465 @@ +#!/bin/bash + +# based on the API spec: https://litterbox.catbox.moe/tools.php + +VERSION="0.0.1" +SELF="$( basename $0 )" +APIURL="https://litterbox.catbox.moe/resources/internals/api.php" +URLS="" +EXPIRE="1" +DELAY="${DELAY:-2}" +UPLOAD_COUNT=0 +TMP="$( mktemp -t -d $SELF.XXXXXXXXXX )" +trap cleanup EXIT + +die() { + echo "$SELF: fatal: $@" 1>&2 + exit 1 +} + +warn() { + echo "$SELF: warning: $@" 1>&2 +} + +info() { + [ "$VERBOSE" = "1" ] && echo "$SELF: info: $@" 1>&2 +} + +check_path() { + type -p "$1" &>/dev/null +} + +cleanup() { + [ "$TMP" != "" ] && rm -rf "$TMP" +} + +usage() { + if ! check_path perldoc; then + warn "can't find perldoc on PATH, printing raw POD." + exec sed -n '/^=pod/,/^=cut/p' $0 + fi + + exec perldoc "$0" +} + +manpage() { + exec pod2man --stderr -s1 -c"UrchlaysStuff" -r$VERSION "$0" +} + +pluralize() { + echo -n "$1 $2" + [ "$1" != 1 ] && echo -n "s" +} + +delay() { + info "sleeping $DELAY sec between $1" + sleep "$DELAY" +} + +set_expiration() { + case "$1" in + 1|12|24|72) ;; # OK + *) die "invalid expiration time, allowed values are: 1 12 24 72" ;; + esac + EXPIRE="$1" +} + +add_url() { + if [ "$URLS" = "" ]; then + URLS="$1" + else + URLS+=" $1" + fi + : $(( UPLOAD_COUNT++ )) +} + +copy_urls() { + if ! check_path xsel; then + warn "xsel not found in PATH, not copying URL(s) to clipboard" + return + fi + + if [ "$DISPLAY" = "" ]; then + warn "DISPLAY not set in env, not copying URL(s) to clipboard" + return + fi + + echo -n "$URLS" | xsel -i +} + +# compress_file() gets called on both files and directories. +compress_file() { + local ext + local basefile + local is_dir + + [ -d "$FILE" ] && is_dir=1 + + if [ "$ZIP" = "1" ]; then + ext=.zip + else + ext=.gz + fi + + basefile="$( basename "$FILE" )" + + # TODO: create a random tmp dir, don't use /tmp! + TMPFILE="$TMP/$basefile$ext" + rm -f "$TMPFILE" + if [ "$ZIP" = "1" ]; then + zip -qr "$TMPFILE" "$FILE" + info "zipped $FILE as $TMPFILE" + else + if [ "$is_dir" = 1 ]; then + tar cfz "$TMPFILE" "$FILE" + info "tarred up $FILE as $TMPFILE" + else + gzip -9 < "$FILE" > "$TMPFILE" + info "gzipped $FILE as $TMPFILE" + fi + fi + FILE="$TMPFILE" +} + +# TODO: curl options: +# --max-time --retry --retry-max-time +upload_file() { + local url + local use_compression + + info "checking $FILE" + + case "$FILE" in + *.exe|*.scr|*.cpl|*.doc*|*.jar) + warn "forbidden file extension, compression forced" + use_compression=1 ;; + *.zip|*.gz) + use_compression=0 ;; + *) + use_compression="$COMPRESS" ;; + esac + + if [ "$use_compression" = 1 ]; then + # can't easily zip stdin, write it to a file + if [ "$FILE" = "-" ]; then + FILE="$TMP/$SELF.stdin" + cat > "$FILE" + if [ "$?" != 0 ]; then + rm -f "$FILE" + return + fi + fi + compress_file # resets FILE, sets TMPFILE + fi + + # at this point, we have an actual file, even if input was stdin, + # so we can encrypt it. + if [ "$ENC" = "1" ]; then + CRYPTFILE="$FILE.aes" + openssl enc -pbkdf2 -aes-256-cbc < "$FILE" > "$CRYPTFILE" + if [ "$?" != "0" ]; then + warn "encryption failed, skipping file" + return + fi + FILE="$CRYPTFILE" + fi + + if [ "$URLS" != "" ]; then + delay "uploads" + fi + + info "uploading $FILE" + + url="$( + cat "$FILE" | \ + curl --silent \ + -F "reqtype=fileupload" \ + -F "time=${EXPIRE}h" \ + -F "fileToUpload=@-" \ + "$APIURL" + )" + # TODO: check curl's exit status! + + case "$url" in + https://litter.catbox.moe/*) add_url "$url" ;; + *) warn "upload failed" ;; + esac +} + +download_and_decrypt() { + local url="$1" + info "downloading and decrypting $url" + curl --silent "$url" | openssl enc -d -pbkdf2 -aes-256-cbc + [ "$?" != 0 ] && warn "download/decrypt failed" +} + +# main() +if [ "$1" = "--man" ]; then + manpage ; exit 0 +fi + +if [ "$1" = "--help" ]; then + usage ; exit 0 +fi + +if ! check_path curl; then + die "can't find curl on PATH (PATH is \"$PATH\")" +fi + +# option string starts with a :, meaning we're responsible for +# printing our own error messages. +while getopts ":e:gzcvdo:" OPT; do + case "$OPT" in + e) set_expiration "$OPTARG"; opt_e=1 ;; + o) DLOUT="$OPTARG" ;; + g) GZIP=1 ;; + z) ZIP=1 ;; + c) ENC=1 ;; + v) VERBOSE=1 ;; + d) DOWNLOAD=1 ;; + *) die "invalid option -$OPTARG, try --help" ;; + esac +done + +# getopts doesn't remove the args/options for us, so: +shift $(($OPTIND - 1)) + +# sanity check options and external binaries' existence. +[ "$GZIP" = 1 -a "$ZIP" = 1 ] && die "can't give both -g and -z" + +if [ "$ZIP" = 1 ] && ! check_path zip; then + warn "can't find zip on PATH, disabling -z option" + ZIP=0 +fi + +if [ "$GZIP" = 1 ] && ! check_path gzip; then + warn "can't find gzip on PATH, disabling -g option" + GZIP=0 +fi + +[ "$GZIP" = 1 -o "$ZIP" = 1 ] && COMPRESS=1 + +if [ "$DOWNLOAD" = 1 -o "$ENC" = 1 ]; then + check_path openssl || die "can't find openssl on path, -c/-d won't work" +fi + +# download mode (-d) +if [ "$DOWNLOAD" = 1 ]; then + [ "$GZIP" = 1 ] && warn "-g option ignored when using -d" + [ "$ZIP" = 1 ] && warn "-z option ignored when using -d" + [ "$ENC" = 1 ] && warn "-c option ignored when using -d" + [ "$opt_e" = 1 ] && warn "-e option ignored when using -d" + [ "$DLOUT" != "" ] && exec 1>"$DLOUT" || die "can't redirect stdout" + for arg; do + [ "$need_delay" = "1" ] && delay "downloads" + download_and_decrypt "$arg" + need_delay=1 + done + exit 0 +else + [ "$DLOUT" != "" ] && warn "-o option ignored when not using -d" +fi + +info "expire time set to $( pluralize $EXPIRE hour )" + +# upload mode +[ "$1" = "" ] && set -- - +for arg; do + TMPFILE="" + CRYPTFILE="" + + if [ "$arg" = "-" -o -f "$arg" ]; then + FILE="$arg" + elif [ -d "$arg" ]; then + FILE="$arg" + compress_file # sets FILE + else + # broken symlink, device node, FIFO, ??? + warn "$arg not a regular file, ignoring" + continue + fi + + upload_file + + # clean up if needed + if [ "$TMPFILE" != "" ]; then + info "removing temp file $TMPFILE" + rm -f "$TMPFILE" + fi + + if [ "$TMPFILE" != "" ]; then + info "removing temp encrypted file $CRYPTFILE" + rm -f "$CRYPTFILE" + fi +done + +if [ "$UPLOAD_COUNT" = "0" ]; then + die "no files uploaded" +fi + +echo "$URLS" +copy_urls + +info "$( pluralize $UPLOAD_COUNT file ) uploaded" + +exit 0 + +# Rest of the file is perldoc. +: <<EOF +=pod + +=head1 NAME + +B<litterbox> - upload files to litterbox.catbox.moe + +=head1 SYNOPSIS + +B<litterbox> [-e I<hours>] [-g] [-z] [-c] [-v] I<file> <I<file ...>> + +B<litterbox> -d [-o I<output>] I<url> <I<url ...>> + +=head1 DESCRIPTION + +B<litterbox> uploads files to (or downloads files from) the temporary +file storage site B<https://litterbox.catbox.moe>. The site is +similar to a "pastebin" site, except that it allows any type of +file (not just text), and uploads can be up to 1GB in size. + +=head2 Uploading + +Each I<file> can be a filename, a directory name, or "-" for standard +input. If no files are given, litterbox reads from standard input. +Directories are uploaded as B<.tar.gz> archives, by default. Files +can be optionally encrypted with AES-256. + +Once a file is uploaded, its URL is printed on stdout (one per +line). If B<xsel>(1) is installed and X is running, URLs are also +copied to the X paste buffer (separated by spaces). + +When uploading multiple files, a delay of 2 seconds is added between +uploads, to avoid hammering the site. + +=head2 Downloading + +Download mode is only for downloading AES-256 encrypted files that +were previously uploaded with B<litterbox>. Non-encrypted files can +simply be downloaded with B<wget>(1), B<curl>(1), a web browser, etc. +You will, of course, have to provide the correct decryption key +(password). + +=head1 OPTIONS + +=head2 Upload Options + +=over 4 + +=item B<-e> I<hours> + +Set expiration time in hours. Allowed values are B<1>, B<12>, +B<24>, or B<72>. Default is B<1>. + +=item B<-g> + +Upload gzipped files. This is automatically enabled +when uploading files whose names end in .exe, .scr, +.cpl, .doc*, .jar (unless B<-z> is given). Also, directories +are automatically tarred/gzipped (unless B<-z> is given). + +=item B<-z> + +Upload zipped files or dirs, rather than .gz or .tar.gz. + +=item B<-c> + +Upload encrypted file. Currently this means the file +is encrypted with 'openssl enc -pbkdf2 -aes-256-cbc', and +you're prompted for an encryption key. Whoever downloads +the encrypted file will have to decrypt it with the B<-d> +option, or else do it manually. + +=back + +=head2 Download Options + +=over 4 + +=item B<-d> + +Download and decrypt previously uploaded file(s) from URL(s). +Equivalent to downloading the file normally, then piping it +through: + + openssl enc -d -pbkdf2 -aes-256-cbc + +...which means the file will be printed to your terminal, unless you +use shell redirection or the B<-o> option. The upload options above (B<-e> +B<-g> B<-z> B<-t> B<-c>) are ignored in download mode. + +=item B<-o> I<file> + +Set output file for B<-d> option. Note that giving multiple URLs +will result in all the data being written to the same file. If you need +separate files, run B<litterbox> multiple times. + +The B<-o> option is ignored in upload mode (when B<-d> is not used). + +=back + +=head2 General Options + +=over 4 + +=item B<-v> + +Verbose operation. + +=item B<--help> + +Print this help to standard output and exit. Since the help is long, +it will be passed through your pager. + +=item B<--man> + +Print this help in man page (troff) format to standard output and exit. + +=back + +=head1 NOTES + +=over 2 + +=item B<-> + +B<litter.catbox.moe> is an amazingly useful service. B<Please> be +respectful of the site's owner, B<follow> the site's rules, and don't +abuse the service with excessive requests. You should also consider +supporting the site financially, by buying merch from its store, or +simply making a donation. + +=item B<-> + +With the B<-c> and B<-d> options, you will be prompted interactively +for the encryption key (password), on the process's controlling tty. +This could pose a problem if there's no controlling tty. With +multiple files, you will be prompted separately for each. + +=item B<-> + +Copying URLs to the copy/paste buffer only works under X11. It might +be possible to support gpm(8) for the console. If Wayland has an +xsel-like utility, it could be supported too (the author doesn't use +Wayland so it's not high on the priority list). + +=back + +=head1 AUTHOR + +B. Watson <urchlay@slackware.uk> + +=head1 COPYRIGHT + +B<litterbox> is licensed under the WTFPL: do WTF you want to with this. +See http://www.wtfpl.net/txt/copying/ for details. + +=cut +EOF |
