#!/bin/bash

# Copyright (c) 2009 Florian Reitmeir
# Copyright (c) 2009,2011,2012,2013,2014 Peter Palfrader
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.

# Author: Florian Reitmeir
# E-mail: florian@reitmeir.org

# Publishes files on a webserver.
#
# This script copies files or directories as given on the command line,
# or stdin or the xclipboard, to a remote location using rsync.  That
# remote directory should be exposed online and ideally be o-r or have
# indexing disabled.
#
# The directory names chosen by this script are random(ish) but still
# contain a date string so one know how old things are when looking at them
# (Or one can just have cron clean out stuff older than a week or a month
# automagically).
#
# The random location chosen is printed to stdout when the script finishes,
# and is copied to the xclipboard if the xclip(1) or xsel(1) utility is
# installed.
#
# base_http and base_rsync can be overriden in a ~/.publish.cfg shell snippet.

base_http=http://www.example.com/pub
base_rsync=marvin.example.com:/var/www/www.example.com/htdocs/pub

history_file=~/.publish.history
history_lines=1000

ttl=180

date_format='%Y-%m-%d-'
rsync_args="--compress --times --chmod=u=rwX,go=rX"

rsync_pre_invoke() { true ;}
rsync_post_invoke() { true ;}
publish_pre_invoke() { true ;}
publish_post_invoke() { true ;}

[ -e ~/.publish.cfg ] && . ~/.publish.cfg

usage()
{
cat << EOF
usage: $0 [<src> [<src> ...]]

copy the file <src> to a server and report the URL.

OPTIONS:
   -8        Add a AddDefaultCharset UTF-8 .htaccess file.
   -c CF     Use config file CF.
   -H        Show the history.
   -s FN     When reading data from stdin, use FN as filename to be published.
   -S        Make a screenshot of one window and publish.
   -h        Show this message.
   -n        no-do.  Just print what would have been done.
   -q        Produce a QR code.
   -r        Add --relative option to rsync so that path names of the given
             files are preserved at the remote host.
   -t days   time to live in days
   -R        re-publish (re-use last directory token)
   -T tag    directory name on the server (use this to re-publish under that name)
   -x        Publish the contents of the xclipboard.
   -u        Unpublish directory (only useful together with -T)
EOF
}

uri_encode() {
	perl -MURI::Escape -F/ -ape '$_ = join "/", map {uri_escape($_)} grep {!/^\.?$/} @F'
}

get_random() {
	head -c 8 /dev/urandom | base64 | tr '/+' 'xx' | tr -d '='
}

history_append() {
    time=$(date --utc --rfc-3339='seconds')
    echo $time $1 $2 >>"$history_file"

    history_tmp=$( tempfile )
    cat "$history_file" | tail --lines="$history_lines" >"$history_tmp"
    mv "$history_tmp" "$history_file"
}

history_show() {
    cat "$history_file"
}

history_get_last_token() {
    tail -n1 "$history_file" | awk '{print $3}'
}

setup_tmpdir() {
    if [ -z "$tmpdir" ]; then
        tmpdir=`mktemp -d`
        trap "rm -rf '$tmpdir'" EXIT
    fi
}


NODO=""
inputxclip=0
do_screenshot=0
make_qrcode=0
add_default_cs=0
relative=""
d_server=""
unpublish=0

while getopts "rRhnq8HSxc:s:t:T:u" OPTION
do
     case $OPTION in
         h)
             usage
             exit
             ;;
         q)
             make_qrcode=1
             ;;
         n)
             NODO="echo"
             ;;
         x)
             inputxclip=1
             ;;
         8)
             add_default_cs=1
             ;;
         H)
             if [ -r "$history_file" ]; then
                history_show
                exit 0
             else
                echo "history file: '$history_file' not found"
                exit 3
             fi
             ;;
         S)
             do_screenshot=1
             ;;
         c)
             if [ -f "$OPTARG" ]; then
                . "$OPTARG"
             else
                echo "config file '$OPTARG' does not exist or is not a file"
                exit 3
             fi
             ;;
         s)
             name_stdin="$OPTARG"
             ;;
         r)
             relative="--relative"
             ;;
         R)
             d_server=$(history_get_last_token)
             ;;
         t)
             ttl="$OPTARG"
             ;;
         T)
             d_server="$OPTARG"
             ;;
         u)
             unpublish=1
             ;;
         *)
             usage >&2
             exit 1
             ;;
     esac
done
shift $(($OPTIND - 1))


tmpdir=""

if [ "$unpublish" -gt 0 ] ; then
	if [ -z "$d_server" ]; then
		echo "Option -u only makes sense together with -T."
		exit 1
	fi
	setup_tmpdir
	$NODO rsync --recursive $rsync_args $relative --delete -v "$tmpdir/." $base_rsync"/$d_server/"
	exit
fi


if [[ "$do_screenshot" -gt 0 ]]; then
	setup_tmpdir
	img="$tmpdir/screenshot.png"
	import "$img"
	chmod a+r "$img"
	set dummy "$img" "$@"
	shift
fi

if [[ "$#" = 0 ]]; then
	setup_tmpdir
	if [ "${name_stdin%-}" != "" ]; then
		stdin="$tmpdir/$(basename "$name_stdin")"
	else
		stdin="$tmpdir/stdin"
	fi

	if [ "$inputxclip" != 1 ] ; then
		cat > "$stdin"
	else
		if which xclip >/dev/null 2>&1; then
			echo "Publishing x clipboard:" >&2
			xclip -o > "$stdin"
			cat "$stdin" | sed -e 's#^#| #'
			echo
			echo "EOF"
		elif which xsel >/dev/null 2>&1; then
			echo "Publishing x clipboard:" >&2
			xsel -o > "$stdin"
			cat "$stdin" | sed -e 's#^#| #'
			echo
			echo "EOF"
		else
			echo "Neither xclip nor xsel installed?" >&2
			exit 1
		fi
	fi
	set dummy "$stdin"
	shift
elif [ "$inputxclip" = 1 ] ; then
	echo "Ignoring -x because there are files on the command line" >&2
fi

d_date=$(date +"$date_format")
d_random=$(get_random)
d_server="${d_server:-$d_date$d_random}"

d_server_http_base=$( echo -n "$d_server" | uri_encode )
base_uri="$base_http/$d_server_http_base/"
main_uri="$base_uri"

one_dir_only=""
if [ "$#" = 1 ]; then
	if [ -d "$1" ]; then
		set dummy "$1/"
		shift
		one_dir_only=1
	else
		main_uri="$base_uri$( echo -n "$(basename "$1")" | uri_encode )"
	fi
else
	echo "$main_uri"
fi

publish_pre_invoke

if [ "$make_qrcode" -gt 0 ] ; then
	if command -v qrencode >/dev/null 2>&1; then
		setup_tmpdir
		img="$tmpdir/.qr.png"
		echo -n "$main_uri" | qrencode -s 5 -l H -o "$img"
		echo "$base_uri"".qr.png"
		main_uri="$base_uri"".qr.png"
		set dummy "$img" "$@"
		shift
	else
		echo >&2 "Warning: qrencode not found."
	fi
fi

if [ "$add_default_cs" -gt 0 ] ; then
	setup_tmpdir
	echo 'AddDefaultCharset UTF-8' > "$tmpdir/.htaccess"
	set dummy "$tmpdir/.htaccess" "$@"
	shift
fi

if command -v xclip >/dev/null 2>&1 && [ -n "${DISPLAY:-}" ]; then
	echo -n "$main_uri" | xclip
elif command -v xsel >/dev/null 2>&1 && [ -n "${DISPLAY:-}" ]; then
	echo -n "$main_uri" | xsel
fi


if [ -n "${ttl}" ]; then
	setup_tmpdir
	echo "${ttl}" > "$tmpdir/.publish.ttl"
	set dummy "$tmpdir/.publish.ttl" "$@"
	shift
fi

while [ "$#" -gt 0 ]; do
	file="$1"
	shift

	if [ "${file%/.publish.ttl}" == "$file" ]; then
		if [ -z "$relative" ]; then
			if [ -z "$one_dir_only" ]; then
				uri="$base_uri""$( echo -n "`basename "$file"`" | uri_encode )"
			else
				uri="$base_uri"
			fi
		else
			uri="$base_uri""$( echo -n $file | uri_encode )"
		fi
		echo "$uri"
		history_append "$d_server" "$uri"
	fi

	rsync_pre_invoke
	$NODO rsync --recursive $rsync_args $relative "$file" $base_rsync"/$d_server/"
	rsync_post_invoke
done


publish_post_invoke