#!/bin/sh
#
# $Id: shtter.sh,v 1.3 2011/09/25 17:52:29 tsutsui Exp tsutsui $
#
# shtter.sh - print timeline and post tweet via command line shell
#  http://lostman-worlds-end.blogspot.com/search/label/Twitter
#  See this home page for details, including original license (GPLv3) etc.
#
# Required commands:
#  From NetBSD base:
#   dd md5 cut date grep sort uniq hexdump rm iconv openssl
#  possibly sh builtins:
#   [ echo printf read
#  From pkgsrc:
#   gsed wget
#
# Modified for NetBSD by Izumi Tsutsui
#  - use md5(1) instead of md5sum
#  - use gsed (from pkgsrc) for GNU sed extentions (gsed -i etc.)
#   - note wget(1) (from pkgsrc) is also required
#     (can we eventually have --post-data in lukem ftp(1)!?)
#  - use utf-16le instead of utf-16 to decode timeline on big endian machines
#  - move "shift 1" where it's actually necessary
#    (XXX: sh(1) complains if shift is called without args)
#  - use "=" instead of "==" for test(1) (sh(1) builtin complains it)
#  - use gsed -e for multiple commands to improve performance
#    (XXX: multiple commands with ; separator seem awfully slow on x68k)
#  - also decode " > < & in timeline strings
#  - add ENCODING environment to set input/output encondings on terminal
#    and explicitly set LANG in script for each command
#  - some more cosmetics
# 
# Notice (as original shtter.sh):
# - AKEY and ASECRET are stored in this script itself if they are empty
#   (by gsed -i)
# - modify SUFFIX and PREFIX (which are added on posts) as you like

LANG=ja_JP.UTF-8

ENCODING=shift_jis
#ENCODING=euc-jp
#ENCODING=iso-2022-jp
# for NetBSD/x68k Japanese capable console
if [ "$TERM" = "x68k" ]; then
 ENCODING=iso-2022-jp
fi

CKEY="U3DhP7CpPx0bNCzdAFShg"
CSECRET="eAQ13OTo7BhroXQc5dA5oOqEuXhAf7yABVKpkbR7so"
AKEY=""
ASECRET=""

HTTP_GET="wget -q -O -"
HTTP_POST="wget -q -O - --post-data"
#HTTP_GET="ftp -V -o -"
#HTTP_GET="curl -s"
#HTTP_POST="curl -s --data"

MD5SUM=md5
SED=gsed

TMPDIR="/tmp"

PREFIX=""
#SUFFIX=" http://bit.ly/dsKezn #shtter"
SUFFIX=""

GenerateNonce()
{
dd if=/dev/urandom bs=1024 count=1 2>/dev/null | ${MD5} | cut -c1-32
}

GetTimeStamp()
{
date +%s
}

Encode()
{
echo "$@" | ${SED} 's/./\0\n/g' | grep '[^-._~0-9a-zA-Z]' | sort | uniq | while read l
do
 if [ "$l" = "" ]; then l=" "; fi
 
 HEX="`echo -n \"$l\" | hexdump -e '16/1 "%02X" "\n"'`"
 
 if [ "$l" = "/" ]; then l="\/"; fi
 echo "s/$l/$HEX"
done | ${SED} -e 's/ *$//' -e 's/\([0-9A-Z]\{2\}\)/%\1/g' -e 's/$/\/g/' >"$TMPDIR/rep.sed"

echo "$@" | ${SED} -f "$TMPDIR/rep.sed"

rm "$TMPDIR/rep.sed"
}

Decode()
{
HEX="`echo -e "$@" | ${SED} 's/&#[0-9]\+;/\n\0\n/g' | ${SED} -e '/^&#[0-9]\+;$/!d' -e 's/[&#;]//g' | sort | uniq`"
HEX="`echo -e \"$HEX\" | while read l; do echo -n \"$l\" | hexdump -e '8/1 "%02x00"'; printf '%04x\n' "$l"; done`"
HEX="`echo -e \"$HEX\" | ${SED} -e 's/  00//g' -e 's/^/73002f0026002300/' -e 's/\(..\)\(..\)$/3b002f00\2\12f0067000a00/'`"
HEX="`echo $HEX | ${SED} -e 's/ //g' -e 's/../\\\x\0/g'`"
printf "$HEX" | iconv -f utf-16le -t utf-8 >"$TMPDIR/rep.sed"

echo -e "$@" | ${SED} -f "$TMPDIR/rep.sed" | ${SED} -e 's/\&quot;/\"/g' -e 's/\&amp;/\&/g' -e 's/\&gt;/>/g' -e 's/\&lt;/</g'| iconv -s -f utf-8 -t $ENCODING

rm "$TMPDIR/rep.sed"
}

GenerateHash()
{
EURL="`Encode $2`"
EPARAM="`Encode $3`"
QUERY="$1&$EURL&$EPARAM"

HASH="`echo -n \"$QUERY\" | openssl sha1 -hmac \"$CSECRET&$ASECRET\" -binary | openssl base64`"
Encode "$HASH"
}

GetRequestToken()
{
URL="http://twitter.com/oauth/request_token"
PARAM="oauth_consumer_key=$CKEY&oauth_nonce=`GenerateNonce`&oauth_signature_method=HMAC-SHA1&oauth_timestamp=`GetTimeStamp`&oauth_token=&oauth_version=1.0"
HASH="`GenerateHash \"GET\" \"$URL\" \"$PARAM\"`"

RTOKEN="`$HTTP_GET \"$URL?$PARAM&oauth_signature=$HASH\"`"
if [ "$RTOKEN" = "" ]; then
 echo "can not get request token" >&2
 exit 1
fi

RKEY="`echo \"$RTOKEN\" | ${SED} 's/.*oauth_token=\([^&]*\).*/\1/'`"
RSECRET="`echo \"$RTOKEN\" | ${SED} 's/.*oauth_token_secret=\([^&]*\).*/\1/'`"

echo "open this url in your browsser and input pin" >&2
echo "http://twitter.com/oauth/authorize?oauth_token=$RKEY" >&2
echo -n "pin > " >&2
read PIN

echo "$RKEY $RSECRET $PIN"
}

GetAccessToken()
{
RKEY="$1"
RSECRET="$2"
PIN="$3"

URL="http://twitter.com/oauth/access_token"
PARAM="oauth_consumer_key=$CKEY&oauth_nonce=`GenerateNonce`&oauth_signature_method=HMAC-SHA1&oauth_timestamp=`GetTimeStamp`&oauth_token=$RKEY&oauth_verifier=$PIN&oauth_version=1.0"
HASH="`GenerateHash \"GET\" \"$URL\" \"$PARAM\"`"

ATOKEN="`$HTTP_GET \"$URL?$PARAM&oauth_signature=$HASH\"`"
if [ "$ATOKEN" = "" ]; then
 echo "can not get access token" >&2
 exit 1
fi

AKEY="`echo $ATOKEN | ${SED} 's/.*oauth_token=\([^&]*\).*/\1/'`"
ASECRET="`echo $ATOKEN | ${SED} 's/.*oauth_token_secret=\([^&]*\).*/\1/'`"

${SED} -i "1,/^AKEY/ s/^\(AKEY=\).*/\1\"$AKEY\"/" "$0"
${SED} -i "1,/^ASECRET/ s/^\(ASECRET=\).*/\1\"$ASECRET\"/" "$0"
}

GetTimeLine()
{
URL="http://api.twitter.com/1/statuses/home_timeline.xml"
PARAM="oauth_consumer_key=$CKEY&oauth_nonce=`GenerateNonce`&oauth_signature_method=HMAC-SHA1&oauth_timestamp=`GetTimeStamp`&oauth_token=$AKEY&oauth_version=1.0&status="
HASH="`GenerateHash \"GET\" \"$URL\" \"$PARAM\"`"

XML="`$HTTP_GET \"$URL?$PARAM&oauth_signature=$HASH\"`"
if [ "$XML" = "" ]
then
 echo "can not get TimeLine" >&2
 exit 1
fi

XML="`echo $XML | ${SED} -e 's/<text>/\n\0/g' -e 's/<\/screen_name>/\0\n/g' | ${SED} -n -e '/^<text>.*<\/screen_name>$/!d' -e 's/<text>\([^<]*\).*<screen_name>\([^<]*\).*/\2: \1/' -e 'p'`"
Decode "$XML"
}

UpdateTimeLine()
{
TWEET="`echo -e "$@" | iconv -f $ENCODING -t utf-8`"
TWEET="`Encode \"$PREFIX$TWEET$SUFFIX\"`"
if [ "$TWEET" = "" ]
then
 echo "can not encode tweet" >&2
 exit 1
fi

URL="http://api.twitter.com/1/statuses/update.xml"
PARAM="oauth_consumer_key=$CKEY&oauth_nonce=`GenerateNonce`&oauth_signature_method=HMAC-SHA1&oauth_timestamp=`GetTimeStamp`&oauth_token=$AKEY&oauth_version=1.0&status=$TWEET"
HASH="`GenerateHash \"POST\" \"$URL\" \"$PARAM\"`"

XML="`$HTTP_POST \"$PARAM&oauth_signature=$HASH\" \"$URL\"`"
if [ "$XML" = "" ]
then
 echo "can not post tweet" >&2
 exit 1
fi
}

if [ "$AKEY" = "" -o "$ASECRET" = "" ]
then
 RTOKEN="`GetRequestToken`"
 GetAccessToken $RTOKEN
else
 ARG="$1"
 
 case "$ARG" in
  init)
   ${SED} -i "1,/^AKEY/ s/^\(AKEY=\).*/\1/" "$0"
   ${SED} -i "1,/^ASECRET/ s/^\(ASECRET=\).*/\1/" "$0"
   ;;
  update)
   shift 1
   UpdateTimeLine "$@"
   ;;
  *)
   GetTimeLine
   ;;
 esac
fi