#!/bin/bash
# A Script that automates pasting to a number of pastebin services
# relying only on bash v3, sed, coreutils (mktemp/sort/wc/whoami) and wget
# Author: Bo ?rsted Andresen, bo.andresen@zlin.dk
##########################################################################
VERSION="1.9"
### helper functions
# show an error message and die
die() {
echo "$*" 1>&2
exit 1
}
# show that an option ${1} is not supported, run function that shows valid options ${3} and die
fail() {
if [[ "${2}" == "service" ]]; then
echo "\"$1\" is not a supported $2." 1>&2
else
echo "\"$1\" is not a supported $2 for ${SERVICE}: $(get_recipient)." 1>&2
fi
echo 1>&2
${3} 1>&2
exit 1
}
# escape % (used for escaping), & (used as separator in POST data), + (used as space in POST data) and space
escape() {
echo "$*" | sed -e 's|%|%25|g' -e 's|&|%26|g' -e 's|+|%2b|g' -e 's| |+|g'
}
### defaults
# The following defaults can be overridden in either /etc/wgetpaste or ~/.wgetpaste.
# Only those four variables should be set in those files.
#
# If add_to_clipboard() is defined as a function in one of those files it will be called with
# the url where your paste can be seen as an argument. You may use xclip, xcut, klipper or
# whatever your window manager provides for adding it to your clipboard.
#
# Likewise if get_from_clipboard() is defined as a funciont in one of those files it will be
# called to retrieve input from your clipboard when --xcut is used.
[[ -f /etc/wgetpaste ]] && . /etc/wgetpaste
[[ -f ~/.wgetpaste ]] && . ~/.wgetpaste
DEFAULT_NICK="${DEFAULT_NICK:-$(whoami)}"
DEFAULT_SERVICE="${DEFAULT_SERVICE:-rafb}"
DEFAULT_LANGUAGE="${DEFAULT_LANGUAGE:-Plain Text}"
### usage
show_usage() {
echo "Usage: $0 [options] [file]"
echo
echo "Options:"
echo " -l, --language LANG set language (defaults to \"${DEFAULT_LANGUAGE}\")"
echo " -d, --description DESCRIPTION set description (defaults to \"stdin\" or filename)"
echo " -n, --nick NICK set nick (defaults to your username))"
echo " -s, --service SERVICE set service to use (defaults to \"${DEFAULT_SERVICE}\")"
echo
echo " -S, --list-services list supported pastebin services"
echo " -L, --list-languages list languages supported by the specified service"
echo
echo " -x, --xcut read input from clipboard (requires configuration)"
echo
echo " -v, --verbose show wget stderr output if no url is received"
echo " --debug be *very* verbose (implies -v)"
echo
echo " -h, --help show this help"
echo " --version show version information"
echo
echo "Defaults can be overridden globally in /etc/wgetpaste or per user in ~/.wgetpaste."
echo "Only the vars DEFAULT_{NICK,SERVICE,LANGUAGE} should be set in those files."
}
### services
SERVICES=(ca rafb sh)
SERVICE_URLS=(http://pastebin.ca http://rafb.net/paste/paste.php http://sh.nu/p/)
# 4 (base indentation) + max service length + 2 (space + dash)
INDENTATION=10
show_services() {
echo 'Services supported (case sensitive):'
for index in ${!SERVICES[*]}; do
echo " ${SERVICES[index]} "$'\e'"[${INDENTATION}G- ${SERVICE_URLS[index]}"
done
}
verify_service() {
for index in ${!SERVICES[*]}; do
[[ "$*" == "${SERVICES[index]}" ]] && return 0
done
fail "$*" "service" "show_services"
}
### languages
# rafb
rafb_LANGUAGES=(C C89 C99 C++ C\# Java Pascal Perl PHP PL\/I Python Ruby SQL VB Plain\ Text)
# ca - ordering is important here as their value is set as their number in the order
ca_LANGUAGES=(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\ 6\.0\ Strict JavaScript \
LISP Lua Microprocessor\ ASM Objective\ C VB\.NET)
show_languages() {
echo "Languages supported by ${SERVICE}: $(get_recipient) (case sensitive):"
case "${SERVICE}" in
ca | rafb )
for index in $(eval "echo \${!${SERVICE}_LANGUAGES[*]}"); do
echo " $(eval "echo \${${SERVICE}_LANGUAGES[index]}")"
done | sort
;;
* )
echo 1>&2
echo "\"${SERVICE}\" has no support for any specific languages." 1>&2
esac
}
# this is in place because rafb.net (probably others too) rejects any paste with an invalid language
verify_language() {
case "${SERVICE}" in
ca )
let "i = 0"
for index in ${!ca_LANGUAGES[*]}; do
let "i = i +1"
[[ "${LANGUAGE}" == "${ca_LANGUAGES[index]}" ]] && LANGUAGE="${i}" && return 0
done
;;
rafb )
for index in ${!rafb_LANGUAGES[*]}; do
[[ "${LANGUAGE}" == "${rafb_LANGUAGES[index]}" ]] && return 0
done
;;
* )
[[ ! ${LANGUAGE_SET} ]] && return 0
;;
esac
fail "${LANGUAGE}" "language" "show_languages"
}
### Posting helper functions
# get the url to post to for any given service
get_recipient() {
for index in ${!SERVICES[*]}; do
[[ "${SERVICE}" == "${SERVICES[index]}" ]] && echo "${SERVICE_URLS[index]}" && return 0
done
echo "Failed to get url for \"${SERVICE}\"." 1>&2
exit 1
}
# print a warning if failure is predictable due to the mere size of the paste. sh seems to be the most reliable
# service in that regard. for now no warning will be printed for that. note that this is only a warning printed.
# it doesn't abort or anything.
warn_size() {
print_warning() {
echo "Pasting > ${1} 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 like e.g. sh."
}
case "${SERVICE}" in
rafb )
[[ ${SIZE} -gt 512000 ]] && print_warning "512 kb"
;;
ca )
[[ ${SIZE} -gt 1024000 ]] && print_warning "1 MB"
;;
esac
}
# POST data
post_data() {
case "${SERVICE}" in
ca )
echo "name=${NICK}&type=${LANGUAGE}&description=${DESCRIPTION}&expiry=${EXPIRATION}&s=Submit+Post&content=${INPUT}"
;;
rafb )
echo "nick=${NICK}&lang=${LANGUAGE}&desc=${DESCRIPTION}&cvt_tabs=${CVT_TABS}&text=${INPUT}"
;;
sh )
echo "poster=${NICK}&code=${INPUT}"
;;
* )
echo "\"${SERVICE}\" is not supported by ${FUNCNAME}()." 1>&2
exit 1
;;
esac
}
# indicate if ${SERVICE} needs stdout output from wget to get the resulting url
need_stdout() {
case "${SERVICE}" in
# this service needs to use a regex to retrieve the url from stdout
ca )
return 0
;;
# these services read the reply from Location: which goes to stderr
rafb | sh )
return 1
;;
* )
echo "\"${SERVICE}\" is not supported by ${FUNCNAME}()." 1>&2
exit 1
;;
esac
}
# get the url
get_url() {
case "${SERVICE}" in
ca )
echo "$*" | sed -n 's|^.*content="[0-9]*;\(http://pastebin.ca/[0-9]*\)".*$|\1|p'
;;
rafb | sh )
echo "$*" | sed -n 's|^.*Location:\ \(http://[^\ ]\+\).*$|\1|p'
;;
* )
echo "\"${SERVICE}\" is not supported by ${FUNCNAME}()." 1>&2
exit 1
;;
esac
}
# verify that the pastebin service didn't return a known error url such as toofast.html from rafb
verify_url() {
case "${SERVICE}" in
rafb )
if [[ "${URL}" == "http://rafb.net/p/toofast.html" ]]; then
echo "You must wait at least 10 seconds between each paste! Try again in 10 seconds." 1>&2
exit 1
fi
;;
esac
}
### read cli options
# if you know a better option (like getopts) which provides the same features
# please let me know how.
while [[ ! -z "${1}" ]]; do
case "${1}" in
--debug )
DEBUG=true
set -x
shift
;;
--description=* )
[[ -z "${1#*=}" ]] && show_usage 1>&2 && exit 1
DESCRIPTION="$(escape "${1#*=}")"
shift
;;
-d | --description )
[[ -z "${2}" ]] && show_usage 1>&2 && exit 1
DESCRIPTION="$(escape "${2}")"
shift 2
;;
-h | --help )
show_usage
exit 0
;;
--language=* )
[[ -z "${1#*=}" ]] && show_usage 1>&2 && exit 1
LANGUAGE_SET=true
LANGUAGE="${1#*=}"
shift
;;
-l | --language )
[[ -z "${2}" ]] && show_usage 1>&2 && exit 1
LANGUAGE_SET=true
LANGUAGE="${2}"
shift 2
;;
-L | --list-languages )
LIST_LANGUAGES=true
shift
;;
--nick=* )
[[ -z "${1#*=}" ]] && show_usage 1>&2 && exit 1
NICK="$(escape "${1#*=}")"
shift
;;
-n | --nick )
[[ -z "${2}" ]] && show_usage 1>&2 && exit 1
NICK="$(escape "${2}")"
shift 2
;;
--service=* )
[[ -z "${1#*=}" ]] && show_usage 1>&2 && exit 1
verify_service "${1#*=}"
SERVICE="$(escape "${1#*=}")"
shift
;;
-s | --service )
[[ -z "${2}" ]] && show_usage 1>&2 && exit 1
verify_service "${2}"
SERVICE="$(escape "${2}")"
shift 2
;;
-S | --list-services )
show_services
exit 0
;;
-v | --verbose )
VERBOSE=true
shift
;;
--version )
echo "$0, version ${VERSION}"
exit 0
;;
-x | --xcut )
XCUT=true
SOURCE="xcut"
shift
;;
*)
if [[ -f "${1}" ]]; then
SOURCE="${1}"
shift
else
show_usage 1>&2
exit 1
fi
;;
esac
done
### everything below this should be independent of which service is being used...
# set default service, nick, source and tabs convertion
SERVICE="${SERVICE:-${DEFAULT_SERVICE}}"
NICK="${NICK:-$(escape "${DEFAULT_NICK}")}"
SOURCE="${SOURCE:-/dev/stdin}"
CVT_TABS="No"
# show languages if requested (needs to be done after the right service is selected)
[[ ${LIST_LANGUAGES} ]] && show_languages && exit 0
# language needs to be verified before it is escaped but after service is selected
LANGUAGE="${LANGUAGE:-${DEFAULT_LANGUAGE}}"
verify_language
LANGUAGE="$(escape "${LANGUAGE}")"
# set default description
if [[ -z "${DESCRIPTION}" ]]; then
if [[ "${SOURCE}" == "/dev/stdin" ]]; then
DESCRIPTION="stdin"
else
DESCRIPTION="$(escape "${SOURCE}")"
fi
fi
# handle the case where the input source (defaulting to /dev/stdin) isn't readable verbosely
if [[ ! ${XCUT} ]] && [[ ! -r "${SOURCE}" ]]; then
echo "The input source: \"${SOURCE}\" is not readable. Please specify a readable input source." 1>&2
echo 1>&2
show_usage 1>&2
exit 1
fi
# read input
if [[ ${XCUT} ]]; then
if [[ "$(type -t get_from_clipboard)" == "function" ]]; then
INPUT="$(escape "$(get_from_clipboard)")"
else
echo "You need to define get_from_clipboard() in /etc/wgetpaste or ~/.wgetpaste to use --xcut." 1>&2
echo "If you want to use e.g. xclip simply emerge xclip and define it like this:" 1>&2
echo 1>&2
echo -e "get_from_clipboard() {\n xclip -o\n}\n" 1>&2
echo "Likewise if you want the resulting url stored in your clipboard using e.g. xclip" 1>&2
echo "define it like this:" 1>&2
echo 1>&2
echo -e "add_to_clipboard() {\n xclip \"\$*\"\n}\n" 1>&2
echo "Use whatever your window manager provides to alter your clipboard." 1>&2
exit 1
fi
else
INPUT="$(escape "$( < "${SOURCE}" )")"
fi
[[ -z "${INPUT}" ]] && die "No input read. Nothing to paste. Aborting."
# print a friendly warning if the size makes failure predictable for the specified pastebin service.
SIZE=$(echo "${INPUT}" | wc -c)
warn_size 1>&2
# create temp file (wget is much more reliable reading large input from a file than from the cli directly
TEMPFILE="$(mktemp /tmp/wgetpaste.XXXXXX)"
[[ -f "${TEMPFILE}" ]] || die "Failed to create a temporary file." 1>&2
# write paste data to the temporary file
post_data > "${TEMPFILE}" || die "Failed to write to temporary file: \"${TEMPFILE}\"."
# set recipient
RECIPIENT="$(get_recipient)"
# paste it
WGET_ARGS="--tries=5 --timeout=60 --post-file=${TEMPFILE}"
if ! need_stdout && [[ ! ${DEBUG} ]] && [[ -w /dev/null ]]; then
OUTPUT="$(wget -O /dev/null ${WGET_ARGS} ${RECIPIENT} 2>&1)"
else
OUTPUT="$(wget -O - ${WGET_ARGS} ${RECIPIENT} 2>&1)"
fi
# clean temporary file
if [[ ! ${DEBUG} ]]; then
rm "${TEMPFILE}" || echo "Failed to remove temporary file: \"${TEMPFILE}\"." 1>&2
else
echo "Left temporary file: \"${TEMPFILE}\" alone for debugging purposes."
fi
# get the url
URL="$(get_url "${OUTPUT}")"
# verify that the pastebin service didn't return a known error url such as toofast.html from rafb
# uses ${SERVICE} and ${URL}.
verify_url
# handle the case when there was no location returned
if [[ -z "${URL}" ]]; then
if [[ ${DEBUG} ]] || [[ ${VERBOSE} ]]; then
echo "Apparently nothing was received. Perhaps the connection failed." 1>&2
echo "${OUTPUT}" 1>&2
else
echo "Apparently nothing was received. Perhaps the connection failed. Enable --verbose or" 1>&2
echo "--debug to get the output from wget that can help diagnose it correctly." 1>&2
fi
exit 1
fi
# add_to_clipboard() may be defined in /etc/wgetpaste or ~/.wgetpaste and can be used to add
# ${URL} to your clipboard using xclip, xcut, klipper or whatever your window manager provides
# for that task.
[[ "$(type -t add_to_clipboard)" == "function" ]] && add_to_clipboard "${URL}"
echo "Your paste can be seen here: ${URL}"
exit 0