#!/usr/bin/env bash # A Script that automates pasting to a number of pastebin services # relying only on bash, sed, coreutils (mktemp/sort/tr/wc/whoami/tee) and wget # Copyright (c) 2007 Bo Ørsted Andresen # Distributed as-is. With no warranties. VERSION="2.11" # don't inherit LANGUAGE from the env unset LANGUAGE # escape and new line characters E=$'\e' N=$'\n' ### services SERVICES="ca rafb osl" # ca ENGINE_ca=ca URL_ca="http://pastebin.ca/" SIZE_ca="1024000 1%MB" # rafb ENGINE_rafb=rafb URL_rafb="http://rafb.net/paste/ paste.php" SIZE_rafb="800000 800%kB" LINES_rafb=10000 TOOFAST_rafb="http://rafb.net/p/toofast.html You must wait at least 10 seconds between each paste! Try again in 10 seconds." TOOBIG_rafb="http://rafb.net/p/toobig.html The paste is too big. Try another service or paste smaller chunks of data." EXPIRATION_INFO_rafb="Pastes on rafb: http://rafb.net/paste/ expire after 24 hours.$N" # osl ENGINE_osl=osl URL_osl="http://pastebin.osuosl.org/ pastebin.php" # tinyurl ENGINE_tinyurl=tinyurl URL_tinyurl="http://tinyurl.com/ create.php" ### engines # ca LANGUAGES_ca="Plain%Text Asterisk%Configuration C C++ PHP Perl Java VB C# Ruby Python Pascal \ mIRC PL/I XML SQL Scheme ActionScript Ada Apache%Configuration Assembly%(NASM) ASP Bash CSS \ Delphi HTML%4.0%Strict JavaScript LISP Lua Microprocessor%ASM Objective%C VB.NET" LANGUAGE_COUNT_ca=0 EXPIRATIONS_ca="Never 5%minutes 10%minutes 15%minutes 30%minutes 45%minutes 1%hour 2%hours \ 4%hours 8%hours 12%hours 1%day 2%days 3%days 1%week 2%weeks 3%weeks 1%month 2%months \ 3%months 4%months 5%months 6%months 1%year" POST_ca="s=Submit+Post name description type expiry % content" REGEX_URL_ca='s|^.*content="[0-9]*;\(http://[^/]*/[0-9]*\)".*$|\1|p' REGEX_RAW_ca='s|^\(http://[^/]*/\)\([0-9]*\)$|\1raw/\2|' # rafb LANGUAGES_rafb="C C89 C99 C++ C# Java Pascal Perl PHP PL/I Python Ruby SQL VB Plain%Text" POST_rafb="% nick desc lang % cvt_tabs text" REGEX_RAW_rafb='s|html\?$|txt|' # osl LANGUAGES_osl="Plain%Text ActionScript Ada Apache%Log%File AppleScript Assembly%(NASM) \ ASP Bash C C%for%Macs CAD%DCL CAD%Lisp C++ C# ColdFusion CSS D Delphi Diff DOS Eiffel Fortran \ FreeBasic Game%Maker HTML%4.0%Strict INI%file Java Javascript Lisp Lua MatLab Microprocessor%ASM \ MySQL NullSoft%Installer Objective%C OCaml Openoffice.org%BASIC Oracle%8 Pascal Perl PHP Python \ QBasic Robots.txt Ruby Scheme Smarty SQL TCL VB VB.NET VisualFoxPro XML" LANGUAGE_VALUES_osl="text actionscript ada apache applescript asm asp bash c c_mac caddcl \ cadlisp cpp csharp cfm css d delphi diff dos eiffel fortran freebasic gml html4strict ini java \ javascript lisp lua matlab mpasm mysql nsis objc ocaml oobas oracle8 pascal perl php python \ qbasic robots ruby scheme smarty sql tcl vb vbnet visualfoxpro xml" EXPIRATIONS_osl="Never 1%day 1%month" EXPIRATION_VALUES_osl="f d m" POST_osl="paste=Send poster % format expiry % code2" REGEX_RAW_osl='s|^\(http://[^/]*/\)\([0-9]*\)$|\1pastebin.php?dl=\2|' # tinyurl POST_tinyurl="submit=Make+TinyURL! % % % % % url" REGEX_URL_tinyurl='s|^$|\1|p' ### errors die() { echo "$@" >&2 exit 1 } requiredarg() { [[ -z $2 ]] && die "$0: option $1 requires an argument" ((args++)) } notreadable() { die "The input source: \"$1\" is not readable. Please specify a readable input source." } noxclip() { cat <&2 Could not find xclip on your system. In order to use --x$1 you must either emerge x11-misc/xclip or define x_$1() globally in /etc/wgetpaste or per user in ~/.wgetpaste to use another program (such as e.g. xcut or klipper) to $2 your clipboard. EOF exit 1 } ### conversions # escape % (used for escaping), & (used as separator in POST data), + (used as space in POST data) and space escape() { sed -e 's|%|%25|g' -e 's|&|%26|g' -e 's|+|%2b|g' -e 's| |+|g' <<< "$*" || die "sed failed" } # if possible convert URL to raw converttoraw() { local regex regex=REGEX_RAW_$ENGINE if [[ -n ${!regex} ]]; then RAWURL=$(sed -e "${!regex}" <<< "$URL") [[ -n $RAWURL ]] && return 0 echo "Convertion to raw url failed." >&2 else echo "Raw download of pastes is not supported by $(getrecipient)." >&2 fi return 1 } ### verification verifyservice() { for s in $SERVICES; do [[ $s == $* ]] && return 0 done echo "\"$*\" is not a supported service.$N" >&2 showservices >&2 exit 1 } verifylanguage() { local i j l lang count v values lang=LANGUAGES_$ENGINE count=LANGUAGE_COUNT_$ENGINE values=LANGUAGE_VALUES_$ENGINE if [[ -n ${!lang} ]]; then ((i=0)) for l in ${!lang}; do if [[ $LANGUAGE == ${l//\%/ } ]]; then if [[ -n ${!count} ]]; then ((LANGUAGE=i+1)) elif [[ -n ${!values} ]]; then ((j=0)) for v in ${!values}; do [[ i -eq j ]] && LANGUAGE=${v//\%/ } && break ((j++)) done fi return 0 fi ((i++)) done else [[ $LANGUAGESET = 0 ]] || return 0 fi echo "\"$LANGUAGE\" is not a supported language for $(getrecipient).$N" >&2 showlanguages >&2 exit 1 } verifyexpiration() { local i j e expiration count v values expiration=EXPIRATIONS_$ENGINE count=EXPIRATION_COUNT_$ENGINE values=EXPIRATION_VALUES_$ENGINE if [[ -n ${!expiration} ]]; then ((i=0)) for e in ${!expiration}; do if [[ ${EXPIRATION} == ${e//\%/ } ]]; then if [[ -n ${!count} ]]; then ((EXPIRATION=i+1)) elif [[ -n {!values} ]]; then ((j=0)) for v in ${!values}; do [[ i -eq j ]] && EXPIRATION=${v//\%/ } && break ((j++)) done fi return 0 fi ((i++)) done else [[ $EXPIRATIONSET = 0 ]] || return 0 fi echo "\"$EXPIRATION\" is not a supported expiration option for $(getrecipient).$N" >&2 showexpirations >&2 exit 1 } # verify that the pastebin service did not return a known error url. otherwise print a helpful error message verifyurl() { dieifknown() { [[ -n ${!1%% *} && ${!1%% *} == $URL ]] && die "${!1#* }" } local t for t in ${!TOO*}; do [[ $t == TOO*_$SERVICE ]] && dieifknown "$t" done } # print a warning if failure is predictable due to the mere size of the paste. note that this is only a warning # printed. it does not abort. warnings() { warn() { if [[ -n $2 && $1 -gt $2 ]]; then echo "Pasting > ${3//\%/ } often tend to fail with $SERVICE. Use --verbose or --debug to see the" echo "error output from wget if it fails. Alternatively use another pastebin service." fi } local size lines size=SIZE_$SERVICE warn "$SIZE" "${!size% *}" "${!size#* }" lines=LINES_$SERVICE warn "$LINES" "${!lines}" "${!lines} lines" } ### input getfilenames() { for f in "$@"; do [[ -f $f ]] || die "$0: $f No such file found." SOURCE="files" FILES[${#FILES[*]}]="$f" done } x_cut() { if [[ -x $(type -P xclip) ]]; then xclip -o || die "xclip failed." else noxclip cut "read from" fi } ### output usage() { cat <&2 for l in ${!lang}; do [[ ${l//\%/ } = $DEFAULT_LANGUAGE ]] && d="*" || d=" " echo " $d${l//\%/ }" done | sort } showexpirations() { echo "Expiration options supported by $(getrecipient) (case sensitive):" local e expiration info d expiration=EXPIRATIONS_$ENGINE info=EXPIRATION_INFO_$SERVICE [[ -z ${!expiration} ]] && echo "$N${!info}\"$ENGINE\" has no support for setting expiration." >&2 for e in ${!expiration}; do [[ ${e//\%/ } = $DEFAULT_EXPIRATION ]] && d="*" || d=" " echo " $d${e//\%/ }" done } showurl() { echo "Your ${2}paste can be seen here: $1" [[ $XPASTE ]] && x_paste "$1" } x_paste() { if [[ -x $(type -P xclip) ]]; then xclip -loops 10 &>/dev/null <<< "$*" || die "xclip failed." else noxclip paste "write to" fi } ### Posting helper functions # get the url to post to getrecipient() { local urls target serv for s in $SERVICES tinyurl; do if [[ $s == $SERVICE ]]; then urls=URL_$SERVICE if [[ RAW == $1 ]]; then [[ ${!urls} = ${!urls#* } ]] || target=${!urls#* } else serv="$SERVICE: " fi echo "${serv}${!urls% *}${target}" return 0 fi done die "Failed to get url for \"$SERVICE\"." } # generate POST data postdata() { local post nr extra f post=POST_$ENGINE nr=${!post//[^ ]} [[ 6 = ${#nr} ]] || die "\"${SERVICE}\" is not supported by ${FUNCNAME}()." extra=${!post%% *} [[ '%' = $extra ]] || echo -n "$extra&" e() { post="$1" shift while [[ -n $1 ]]; do f=${post%% *} [[ '%' != $f ]] && echo -n "$f=${!1}" && [[ $# -gt 1 ]] && echo -n "&" shift post=${post#$f } done } e "${!post#$extra }" NICK DESCRIPTION LANGUAGE EXPIRATION CVT_TABS INPUT } # get url from response from server geturl() { local regex regex=REGEX_URL_$ENGINE if [[ -n ${!regex} ]]; then [[ needstdout = $1 ]] && return 0 sed -n -e "${!regex}" <<< "$*" else [[ needstdout = $1 ]] && return 1 sed -n -e 's|^.*Location: \(http://[^ ]*\).*$|\1|p' <<< "$*" fi } ### read cli options # separate groups of short options. replace --foo=bar with --foo bar while [[ -n $1 ]]; do case "$1" in -- ) for arg in "$@"; do ARGS[${#ARGS[*]}]="$arg" done break ;; --debug ) set -x DEBUG=0 ;; --*=* ) ARGS[${#ARGS[*]}]="${1%%=*}" ARGS[${#ARGS[*]}]="${1#*=}" ;; --* ) ARGS[${#ARGS[*]}]="$1" ;; -* ) for shortarg in $(sed -e 's|.| -&|g' <<< "${1#-}"); do ARGS[${#ARGS[*]}]="$shortarg" done ;; * ) ARGS[${#ARGS[*]}]="$1" esac shift done # set the separated options as input options. set -- "${ARGS[@]}" while [[ -n $1 ]]; do ((args=1)) case "$1" in -- ) shift && getfilenames "$@" && break ;; -c | --command ) requiredarg "$@" SOURCE="command" COMMANDS[${#COMMANDS[*]}]="$2" ;; -d | --description ) requiredarg "$@" DESCRIPTION="$2" ;; -e | --expiration ) requiredarg "$@" EXPIRATIONSET=0 EXPIRATION="$2" ;; -E | --list-expiration ) LISTEXPIRATION=0 ;; -h | --help ) USAGE=0 ;; -g | --ignore-configs ) IGNORECONFIGS=0 ;; -i | --info ) INFO=0 ;; -I | --info-only ) SOURCE=info ;; -l | --language ) requiredarg "$@" LANGUAGESET=0 LANGUAGE="$2" ;; -L | --list-languages ) LISTLANGUAGES=0 ;; -n | --nick ) requiredarg "$@" NICK=$(escape "$2") ;; -r | --raw ) RAW=0 ;; -s | --service ) requiredarg "$@" SERVICESET="$2" ;; -S | --list-services ) SHOWSERVICES=0 ;; -t | --tee ) TEE=0 ;; -u | --tinyurl ) SERVICE=tinyurl ;; -v | --verbose ) VERBOSE=0 ;; --version ) echo "$0, version $VERSION" && exit 0 ;; -x | --xcut ) SOURCE=xcut ;; -X | --xpaste ) XPASTE=0 ;; -* ) die "$0: unrecognized option \`$1'" ;; *) getfilenames "$1" ;; esac shift $args done ### defaults [[ $IGNORECONFIGS ]] || for f in {/etc/,~/.}wgetpaste{.d/*.bash,}; do if [[ -f $f ]]; then . "$f" || die "Failed to source $f" fi done [[ $SERVICESET ]] && verifyservice "$SERVICESET" && SERVICE=$(escape "$SERVICESET") DEFAULT_NICK=${DEFAULT_NICK:-$(whoami)} || die "whoami failed" DEFAULT_SERVICE=${DEFAULT_SERVICE:-rafb} DEFAULT_LANGUAGE=${DEFAULT_LANGUAGE:-Plain Text} DEFAULT_EXPIRATION=${DEFAULT_EXPIRATION:-1 month} SERVICE=${SERVICE:-${DEFAULT_SERVICE}} ENGINE=ENGINE_$SERVICE ENGINE="${!ENGINE}" default="DEFAULT_NICK_$SERVICE" && [[ -n ${!default} ]] && DEFAULT_NICK=${!default} default="DEFAULT_LANGUAGE_$SERVICE" && [[ -n ${!default} ]] && DEFAULT_LANGUAGE=${!default} default="DEFAULT_EXPIRATION_$SERVICE" && [[ -n ${!default} ]] && DEFAULT_EXPIRATION=${!default} NICK=${NICK:-$(escape "${DEFAULT_NICK}")} [[ -z $SOURCE ]] && SOURCE="stdin" && FILES[${#FILES[*]}]="/dev/stdin" CVT_TABS=No INFO_COMMAND=${INFO_COMMAND:-"emerge --info"} INFO_ARGS=${INFO_ARGS:-"--ignore-default-opts"} ### everything below this should be independent of which service is being used... # show listings if requested [[ $USAGE ]] && usage && exit 0 [[ $SHOWSERVICES ]] && showservices && exit 0 [[ $LISTLANGUAGES ]] && showlanguages && exit 0 [[ $LISTEXPIRATION ]] && showexpirations && exit 0 # language and expiration need to be verified before they are escaped but after service and defaults # have been selected LANGUAGE=${LANGUAGE:-${DEFAULT_LANGUAGE}} verifylanguage LANGUAGE=$(escape "$LANGUAGE") EXPIRATION=${EXPIRATION:-${DEFAULT_EXPIRATION}} verifyexpiration EXPIRATION=$(escape "$EXPIRATION") # set prompt if [[ 0 -eq $UID ]]; then PS1="#" else PS1=$ fi # set default description if [[ -z $DESCRIPTION ]]; then case "$SOURCE" in info ) DESCRIPTION="$PS1 $INFO_COMMAND;" ;; command ) DESCRIPTION="$PS1" for c in "${COMMANDS[@]}"; do DESCRIPTION="$DESCRIPTION $c;" done ;; files ) DESCRIPTION="${FILES[@]}" ;; * ) DESCRIPTION="$SOURCE" ;; esac fi # create tmpfile for use with tee if [[ $TEE ]]; then TMPF=$(mktemp /tmp/wgetpaste.XXXXXX) [[ -f $TMPF ]] || die "Could not create a temporary file for use with tee." fi # read input case "$SOURCE" in command ) for c in "${COMMANDS[@]}"; do if [[ $TEE ]]; then echo "$PS1 $c$N$(bash -c "$c" 2>&1)$N" | tee -a "$TMPF" else INPUT="$INPUT$PS1 $c$N$(bash -c "$c" 2>&1)$N$N" fi done ;; info ) if [[ $TEE ]]; then echo "$PS1 $INFO_COMMAND$N$($INFO_COMMAND $INFO_ARGS 2>&1)" | tee "$TMPF" else INPUT="$PS1 $INFO_COMMAND$N$($INFO_COMMAND $INFO_ARGS 2>&1)" fi ;; xcut ) if [[ $TEE ]]; then x_cut | tee "$TMPF" else INPUT="$(x_cut)" fi ;; files | stdin ) if [[ ${#FILES[@]} -gt 1 ]]; then for f in "${FILES[@]}"; do [[ -r $f ]] || notreadable "$f" if [[ $TEE ]]; then echo "$PS1 cat $f$N$(<"$f")$N" | tee -a "$TMPF" else INPUT="$INPUT$PS1 cat $f$N$(<"$f")$N$N" fi done else [[ -r $FILES ]] || notreadable "$FILES" if [[ $TEE ]]; then tee "$TMPF" < "$FILES" else INPUT=$(<"$FILES") fi fi ;; esac NOINPUT="No input read. Nothing to paste. Aborting." if [[ $TEE ]]; then [[ 0 -eq $(wc -c < "$TMPF") ]] && die "$NOINPUT" else [[ -z $INPUT ]] && die "$NOINPUT" fi # append info if needed if [[ $INFO ]]; then DESCRIPTION="$DESCRIPTION $PS1 $INFO_COMMAND;" if [[ $TEE ]]; then echo "$N$PS1 $INFO_COMMAND$N$($INFO_COMMAND $INFO_ARGS 2>&1)" | tee -a "$TMPF" else INPUT="$INPUT$N$PS1 $INFO_COMMAND$N$($INFO_COMMAND $INFO_ARGS 2>&1)" fi fi # now that tee has done its job read data into INPUT [[ $TEE ]] && INPUT=$(<"$TMPF") && echo # escape DESCRIPTION and INPUT DESCRIPTION=$(escape "$DESCRIPTION") INPUT=$(escape "$INPUT") # print friendly warnings if max sizes have been specified for the pastebin service and the size exceeds that SIZE=$(wc -c <<< "$INPUT") LINES=$(wc -l <<< "$INPUT") warnings >&2 # create temp file (wget is much more reliable reading large input via --post-file rather than --post-data) [[ -f $TMPF ]] || TMPF=$(mktemp /tmp/wgetpaste.XXXXXX) if [[ -f $TMPF ]]; then postdata > "$TMPF" || die "Failed to write to temporary file: \"$TMPF\"." WGETARGS="--post-file=$TMPF" else # fall back to using --post-data if the temporary file could not be created # TABs and new lines need to be escaped for wget to interpret it as one string WGETARGS="--post-data=$(postdata | sed -e 's|$|%0a|g' -e 's|\t|%09|g' | tr -d '\n')" fi # set recipient RECIPIENT=$(getrecipient RAW) # paste it WGETARGS="--tries=5 --timeout=60 $WGETARGS" if geturl needstdout || [[ $DEBUG || ! -w /dev/null ]]; then OUTPUT=$(LC_ALL=C wget -O - $WGETARGS $RECIPIENT 2>&1) else OUTPUT=$(LC_ALL=C wget -O /dev/null $WGETARGS $RECIPIENT 2>&1) fi # clean temporary file if it was created if [[ -f $TMPF ]]; then if [[ $DEBUG ]]; then echo "Left temporary file: \"$TMPF\" alone for debugging purposes." else rm "$TMPF" || echo "Failed to remove temporary file: \"$TMPF\"." >&2 fi fi # get the url URL=$(geturl "$OUTPUT") # verify that the pastebin service did not return a known error url such as toofast.html from rafb verifyurl # handle the case when there was no location returned if [[ -z $URL ]]; then if [[ $DEBUG || $VERBOSE ]]; then die "Apparently nothing was received. Perhaps the connection failed.$N$OUTPUT" else echo "Apparently nothing was received. Perhaps the connection failed. Enable --verbose or" >&2 die "--debug to get the output from wget that can help diagnose it correctly." fi fi # converttoraw() sets RAWURL upon success. if [[ $RAW ]] && converttoraw; then showurl "$RAWURL" "raw " else showurl "$URL" fi exit 0