#!/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 die() { echo "$SELF: fatal: $@" 1>&2 exit 1 } warn() { echo "$SELF: warning: $@" 1>&2 } info() { [ "$VERBOSE" = "1" ] && echo "$SELF: info: $@" 1>&2 } usage() { if ! type -p perldoc &>/dev/null; then echo "$SELF: can't find perldoc on PATH, printing raw POD." 1>&2 exec sed -n '/^=pod/,/^=cut/p' $0 fi exec perldoc "$0" } manpage() { exec pod2man --stderr -s1 -c"UrchlaysStuff" -r$VERSION "$0" } 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 ! xsel --version &>/dev/null; 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() { 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:-/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 ;; *) use_compression="$COMPRESS" ;; esac if [ "$use_compression" = 1 ]; then # can't easily zip stdin, write it to a file if [ "$FILE" = "-" ]; then FILE="${TMP:-/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 info "sleeping $DELAY sec between uploads" sleep "$DELAY" 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 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 shift $(($OPTIND - 1)) [ "$GZIP" = 1 -a "$ZIP" = 1 ] && die "can't give both -g and -z" [ "$GZIP" = 1 -o "$ZIP" = 1 ] && COMPRESS=1 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 download_and_decrypt "$arg" done exit 0 else [ "$DLOUT" != "" ] && warn "-o option ignored when not using -d" fi info "expire time set to $EXPIRE hour(s)" [ "$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 warn "$arg not a regular file, ignoring" continue fi upload_file 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 "$UPLOAD_COUNT file(s) uploaded" exit 0 # Rest of the file is perldoc. : < - upload files to litterbox.catbox.moe =head1 SYNOPSIS B [-e I] [-g] [-z] [-c] [-v] I > B -d [-o I] I > =head1 DESCRIPTION B uploads files to (or downloads files from) the temporary file storage site B. 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 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. If B(1) is installed, 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. Non-encrypted files can simply be downloaded with B(1), B(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 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 .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 -o option. The upload options above (-e B<-g> B<-z> B<-t> B<-c>) are ignored in download mode. =item B<-o> I 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 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. =back Note: With the -c and -d option, you will be prompted interactively for the encryption key (password), on the process's controlling tty. =head1 AUTHOR B. Watson =head1 COPYRIGHT B is licensed under the WTFPL: do WTF you want to with this. See http://www.wtfpl.net/txt/copying/ for details. =cut EOF