You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

269 lines
6.7 KiB

#!/usr/bin/env bash
declare verbose=0
declare expire_length
declare request_deletion_key
declare clipboard
declare -r conf_dir="${XDG_CONFIG_DIR:-$HOME/.config}/tekup"
declare -r log_file="${conf_dir}/log"
declare -r config_file="${conf_dir}/conf"
declare -A colors
colors[red]=$(tput setaf 1)
colors[reset]=$(tput sgr0)
err() {
printf "${colors[red]}%s${colors[reset]}\n" "$*" >&2
}
die() {
[[ -n "$1" ]] && err "$1"
exit 1
}
usage() {
LESS=-FEXR less <<-HELP
Usage: tekup [OPTIONS] [FILES...]
Upload files to https://teknik.io
OPTIONS:
-d request a deletion key for images
-e <STRING> expiration time. only has an effect on text pastes.
must be in the form of 'N UNIT' where N is a number
and UNIT is one of the following:
view minute hour day month year
-N disables expiring pastes (if enabled in config file, for example)
-c save the url to the clipboard
-h print this help
-v print verbose output. can be stacked. there are three levels of
verbosity:
first will show progress from curl
second will print the json response
third will show verbose output from curl
CONFIGURATION:
A configuration file can be defined at ${config_file/$HOME/\~}
If a line begins with '#' it is treated as a comment and ignored.
A configuration file has the following options:
api_key teknik.io api key
expire_length same as -e flag above
do_not_track boolean, asks teknik.io not to log the upload
verbose_level must be a number
clipboard boolean
If ${log_file/$HOME/\~} is a writeable file tekup will always ask for a
deletion key and log it in that file.
HELP
}
has() {
local _verbose
if [[ $1 = '-v' ]]; then
_verbose=1
shift
fi
for c; do c="${c%% *}"
if ! command -v "$c" &> /dev/null; then
(( _verbose > 0 )) && err "$c not found"
return 1
fi
done
}
select_from() {
local cmd='command -v'
for a; do
case "$a" in
-c) cmd="$2"; shift 2 ;;
esac
done
for c; do
if $cmd "${c%% *}" &> /dev/null; then
echo "$c"
return 0
fi
done
return 1
}
parse_config_file() {
local line key val nr=0
while IFS= read -r line; do
(( ++nr ))
[[ -z "$line" || "$line" = '#'* ]] && continue
read -r key <<< "${line%% *}"
read -r val <<< "${line#* }"
if [[ -z "$val" ]]; then
config_err+=( "missing value for \"$key\" in config file on line $nr" )
continue
fi
case "$key" in
expire_length) expire_length="$val" ;;
api_key) api_key="$val" ;;
do_not_track) do_not_track="$val" ;;
verbose_level) verbose="$val" ;;
clipboard) [[ $val = 'true' ]] && clipboard=1 ;;
*) config_err+=( "unknown key \"$key\" in config file on line $nr" )
esac
done < "$config_file"
if (( ${#config_err[@]} > 0 )); then
err 'there were errors parsing config file:'
for e in "${config_err[@]}"; do
err " $e"
done
fi
}
upload_text() {
local file curl_opts expire_unit
file="$1"
shift
curl_opts=()
case "$verbose" in
0) curl_opts+=( -s ) ;;
1|2) curl_opts+=( -# ) ;;
*) curl_opts+=( -v ) ;;
esac
[[ -n "$api_key" ]] && curl_opts+=( -H "Authorization: AuthToken ${api_key}" )
if [[ -n "$expire_length" ]]; then
printf -v expire_l '%d' "${expire_length% *}"
expire_unit="${expire_length#* }"
curl_opts+=( --data "expireLength=${expire_l}&expireUnit=${expire_unit/%s}")
fi
curl_opts+=( ${do_not_track:+ --data "doNotTrack=${do_not_track}"} )
curl_opts+=(
--data "title=${file##*/}"
--data-urlencode "code=$(< "$file")"
https://api.teknik.io/v1/Paste )
curl "${curl_opts[@]}" | parse_response
}
upload_file() {
local file mime curl_opts
file="$1"
mime="$2"
shift 2
curl_opts=()
case "$verbose" in
0) curl_opts+=( -s ) ;;
1|2) curl_opts+=( -# ) ;;
*) curl_opts+=( -v ) ;;
esac
[[ -n "$api_key" ]] && curl_opts+=( -H "Authorization: AuthToken ${api_key}" )
curl_opts+=( ${do_not_track:+ -F "doNotTrack=${do_not_track}"} )
[[ -w "$log_file" || -n "$request_deletion_key" ]] &&
curl_opts+=( -F 'genDeletionKey=true' )
curl_opts+=( -F "contentType=${mime%%;*}"
-F "file=@\"${file}\""
https://api.teknik.io/v1/Upload )
curl "${curl_opts[@]}" | parse_response
}
upload_files() {
local f mime
for f; do
if [[ "$f" = '-' ]]; then
echo 'reading from stdin'
tee /tmp/tekup_paste &> /dev/null
f=/tmp/tekup_paste
fi
if [[ ! -e "$f" ]]; then
err "$f does not exist"
continue
fi
mime=$(file -Lib "$f")
if [[ "${mime%%/*}" = 'text' ]]; then
upload_text "$f"
else
upload_file "$f" "$mime"
fi
done
}
parse_response() {
local url deletion_key expire_date log_msg
read -r response
(( verbose > 1 )) && jq <<< "$response"
if [[ "$response" != *'http'* || -z "$response" || "$response" = *'error'* ]]; then
err "error uploading $file"
die "$(jq -r '.error.message' <<< "$response")"
fi
url=$(jq -r '.result.url' <<< "$response")
url="${url/paste.teknik.io/p.teknik.io\/Raw}"
url="${url/upload/u}"
file="${file/\/tmp\/tekup_paste/stdin}"
printf '%s: %s\n' "$file" "$url"
if [[ -n "$clipboard" ]]; then
prg=$(select_from 'xclip -r ' 'xsel -b')
[[ -z "$prg" ]] && err 'xclip or xsel required for writing to clipboard'
$prg <<< "$url"
fi
if [[ -w "$log_file" || -n "$request_deletion_key" ]]; then
deletion_key=$(jq -r '.result.deletionKey' <<< "$response")
[[ "$deletion_key" = null ]] && deletion_key=''
[[ -n "$deletion_key" ]] && printf 'deletion key: %s/%s\n' "$url" "$deletion_key"
fi
if [[ -n "$expire_length" ]]; then
expire_date=$(jq -r '.result.expiration | (match("[0-9]+").string)? // .result.expiration' <<< "$response")
[[ "$expire_date" = null ]] && expire_date=''
[[ -n "$expire_date" ]] &&
printf 'expires at: %s\n' "$(date -d "@$expire_date")"
fi
if [[ -w "$log_file" ]]; then
log_msg="$(date +%s) | file: $file | url: $url"
[[ -n "$expire_date" ]] &&
log_msg+=" | expires: $expire_date"
[[ -n "$deletion_key" ]] &&
log_msg+=" | delete: $url/$deletion_key"
echo "$log_msg" >> "$log_file"
fi
}
finish() {
[[ -e /tmp/tekup_paste ]] && rm /tmp/tekup_paste
}
trap finish SIGHUP SIGINT SIGTERM
has -v curl jq || die
[[ -s "$config_file" ]] && parse_config_file
OPTERR=0
while getopts 'hve:dNc' opt; do
case "$opt" in
h) usage; exit 0 ;;
v) (( ++verbose )) ;;
d) request_deletion_key=1 ;;
e) expire_length="$OPTARG" ;;
N) expire_length='' ;;
c) clipboard=1 ;;
esac
done
shift $(( OPTIND - 1 ))
unset opt OPTARG OPTIND
(( $# > 0 )) || die 'No files to upload.'
upload_files "$@"
finish