#!/bin/sh

A simple titan upload utility as a POSIX shell script.

#

This script is dedicated to the public domain according to the terms of CC0:

https://creativecommons.org/publicdomain/zero/1.0/

#

History:

May 23: Original script

Sep 24: Support edit parameter

usage () {

echo "Usage: $(basename "$0") [-c CERT_FILE] [-k KEY_FILE] [-t TOKEN] [-m MIMETYPE] URI [FILE]"

}

args=$(getopt hc:k:m:t: "$@")

if [ $? -ne 0 ]; then

usage

exit 2

fi

set -- $args

while [ $# -gt 0 ]; do

case "$1" in

-h) usage; echo;

echo "Upload FILE to titan uri URI."

echo "If FILE is omitted, create/edit file in \$EDITOR then upload."

echo "If FILE is -, it will be read from standard input."

echo

echo "If -m is not used, the mimetype will be:"

echo "* text/gemini if FILE ends in .gmi or if FILE was omitted or was -;"

echo "* determined using the 'file' command otherwise."

echo

echo "Paths to a client certificate and its private key may optionally be provided,"

echo "using either environment variables CLIENT_CERT and CLIENT_KEY,"

echo "or the -c and -k options (which take priority over variables)."

exit 0;;

-c) cert="$2"; shift;;

-k) key="$2"; shift;;

-t) token="$2"; shift;;

-m) mimetype="$2"; shift;;

--) shift; break;;

esac

shift

done

if [ $# -lt 1 ] || [ $# -gt 2 ]; then

usage

exit 2

fi

cert="${cert:-$CLIENT_CERT}"

key="${key:-$CLIENT_KEY}"

if { [ -n "$cert" ] || [ -n "$key" ] ;} && { [ -z "$cert" ] || [ -z "$key" ] ;}; then

echo "Both certificate and key are required if either is given."

exit 2

fi

uri="$1"

file="$2"

args="$(printf "%s" "$uri" | sed -En 's/^(titan:\/\/)?([^\/:]+)(:([0-9]+))?\/(.*)$/\2:\4 \5/p')"

set -- $args

if [ $# -ne 1 ] && [ $# -ne 2 ]; then

echo "Failed to parse URI: $uri"

exit 1

fi

host="${1%:*}"

port="${1#*:}"

port="${port:-1965}"

path="$2"

connect () {

if [ -n "$cert" ]; then

openssl s_client -cert "$cert" -key "$key" -quiet -connect "$host:$port"

else

openssl s_client -quiet -connect "$host:$port"

fi 2>/dev/null

}

edit=

realpath="$(printf "%s" "$path" | sed -En 's/;edit$//p')"

if [ -n "$realpath" ]; then

edit=1

path="$realpath"

fi

tmpfile=

if [ -z "$file" ]; then

file="$(mktemp)" || { echo "Failed to make temp file" && exit 1; }

tmpfile="$file"

if [ -n "$edit" ]; then

strip20 () {

read header

case "$header" in

20*) cat;;

*) echo "Unexpected response: $header";;

esac

}

(printf "%s\r\n" "titan://$host:$port/$path;edit") | connect | strip20 > "$file"

fi

$EDITOR "$file"

if ! [ -s "$file" ] && [ -z "$edit" ]; then

echo "Empty file. Aborting."

exit 0

fi

mimetype=${mimetype:-text/gemini}

fi

if [ "$file" = - ]; then

file="$(mktemp)" || { echo "Failed to make temp file" && exit 1; }

tmpfile="$file"

cat>"$file"

mimetype=${mimetype:-text/gemini}

fi

[ -r "$file" ] || { echo "Can't open $file for reading." && exit 1; }

[ -d "$file" ] && { echo "$file is a directory." && exit 1; }

if [ -z "$mimetype" ]; then

if [ "${file%%.gmi}" != "$file" ]; then

mimetype=text/gemini

else

mimetype="$(file -b --mime-type "$file")"

fi

fi

size="$(wc -c < "$file")"

(printf "%s\r\n" "titan://$host:$port/$path;${token:+token=$token;}mime=$mimetype;size=$size"; cat "$file") | connect

[ -n "$tmpfile" ] && rm "$tmpfile"


Source