aboutsummaryrefslogtreecommitdiff
path: root/litterbox
diff options
context:
space:
mode:
Diffstat (limited to 'litterbox')
-rwxr-xr-xlitterbox465
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