#!/bin/sh set -e vardir=/var/lib/kannel/hello sbindir=/usr/local/sbin exit0() { [ -n "$1" ] && echo "$1" exit 0 } exit1() { response="${1:+Error: }${1:-Internal error!}" # FIXME: pass error messages via stderr (not stdout) # echo >&2 "$response" echo "$response" [ -z "$SMS_SMSC$SMS_URL" ] || $sbindir/localsendsms "$to" "$response" exit 1 } grepescape() { echo "$1" | perl -pe 's/(?=[+])/\\/g' } # Based on Text::Unidecode bug#8017: http://rt.cpan.org/Ticket/Display.html?id=8017#txn-322351 lctransliterate() { echo "$1" | perl -n \ -e 'use Text::Unidecode;' \ -e 'use Encode 2.12 qw(encode decode _utf8_off);' \ -e 'print lc(decode("GSM0338", encode("GSM0338", decode("UTF-8", $_),' \ -e 'sub {$a=unidecode(chr $_[0]); _utf8_off($a); $a;}' \ -e ')));' return $res; } uriunescape() { if [ 0 = "$urldecode" ]; then echo "$@" else echo "$@" | perl -e 'use URI::Escape; print uri_unescape();' fi } # Resolve var from "variable = value" pair in file below /etc/local getfilevar() { file="/etc/local/$1" var="$2" grep -m1 "^$var" "$file" | awk -F '(= )' '{print $2}' } lastservicephone() { logfile=/var/log/kannel/smsbox.log tac $logfile $logfile.1 \ | perl -ne "/INFO: Starting to service <.*> from <$1> to <([^<>]*)>/ and print \$1 and exit 0" } #from=`uriunescape "$1" | sed -e 's/\+/ /g'`; shift from=`uriunescape "$1"`; shift #to=`uriunescape "$1" | sed -e 's/\+/ /g'`; shift to=`uriunescape "$1"`; shift # FIXME: avoid stripping ALL plusses set -- `uriunescape "$@" | sed -e 's/\+/ /g'` #set -- `uriunescape "$@"` app_raw="$1" app=`lctransliterate "$1"`; shift export debug SMS_PHONE="$from" urldecode=0 export SMS_PHONE urldecode case "$SMS_REALM" in test) ccc="${ccc:-99}" provider="dummy" mdpath="/home/hearth/public_webdata/hearth/content/sms" export mdpath ;; hello) ccc="45" provider="local" # provider="coolsms" # provider="clickatell" mdpath="/home/hearth/public_webdata/hearth/content/sms" export mdpath ;; hellobudapest) ccc="36" # provider="clickatell" provider="local" addtestphones="+36307418279" # Jonas addphones="" gotestphones="+36307418279" # Jonas gophones="" SMS_ERRFROM="+36307418279" # Jonas SMS_ERRTO1="+36307418279" # Jonas SMS_ERRTO2="+36307418241" # Emma # SMS_ERRTO2="+36703782127" # Emma mdpath="/home/www-hearth/public_webdata/hellobudapest/content/sms" export SMS_ERRFROM SMS_ERRTO1 SMS_ERRTO2 mdpath ;; hellotorino) ccc="39" provider="clickatell" gotestphones="+393453915741 +393453805531 +393453801457" # Jonas, Jacob, Vera gophones="+393407574815 +393407574813 +393381467977 +393478617029" # Alessandra, Vela, Marco, Silvia SMS_ERRFROM="+393453915741" # Jonas SMS_ERRTO1="+393453801457" # Vera SMS_ERRTO2="+393483281187" # Rescue mdpath="/home/www-hearth/public_webdata/hellotorino/content/sms" export SMS_ERRFROM SMS_ERRTO1 SMS_ERRTO2 mdpath ;; cyber) ccc="45" provider="local" mdpath="/home/cyberhus/public_webdata/mdsms/content/sms" export mdpath ;; hpm) ccc="84" provider="local" mdpath="/home/www-sms/public_webdata/hpm/content/sms" export mdpath ;; *) exit1 "unknown realm \"$SMS_REALM\"!" ;; esac case "$provider" in dummy) dummy=1 ADMIN_OK=1 export ADMIN_OK dummy ;; local) export SMS_SMSC ;; coolsms) unset SMS_SMSC SMS_URL="https://sms.coolsmsc.dk:8081/" SMS_USER=$(getfilevar coolsms user) SMS_PW=$(getfilevar coolsms pw) SMS_CP="cp1252" SMS_MSGTAG="message" export SMS_URL SMS_USER SMS_PW SMS_CP SMS_MSGTAG ;; # supports concatenation and req_feat (e.g 48: sender ID) # does not support DLR, and only concatenates max. 3 smses clickatell) unset SMS_SMSC SMS_URL="https://api.clickatell.com/http/sendmsg" SMS_USER=$(getfilevar clickatell user) SMS_PW=$(getfilevar clickatell pw) SMS_CP="iso8859-1" SMS_USERTAG="user" SMS_API=$(getfilevar clickatell api) SMS_CALLBACK="3" SMS_CONCAT="3" SMS_ESCALATE="1" SMS_VALIDITY="1" SMS_REQ_FEAT="24611" # 1+2+32+8192+16384 = text+8bit+numeric_src+dlr+concat stripprefix="1" export SMS_URL SMS_USER SMS_PW SMS_CP SMS_USERTAG SMS_API SMS_CALLBACK SMS_CONCAT SMS_ESCALATE SMS_VALIDITY SMS_REQ_FEAT stripprefix ;; # supports DLR # Kannel clickatell profile supports concatenation but not req_feat # Kannel generic profile supports req_feat but not concatenation clickatell_via_kannel) SMS_SMSC="X" SMS_CONCATENATION="1" SMS_DLR_MASK="7" SMS_DLR_URL="http://helloearth.jones.dk/test.cgi?type=dlr&msgid=XXX&smsid=%I&from=%p&to=%P&time=%t&unixtime=%T&dlr=%d&dlrmsg=%A" SMS_CP="iso8859-1" SMS_VALIDITY="1" SMS_BINFO="req_feat=24611&concat=3&escalate=1%validity=1" # 1+2+32+8192+16384 = text+8bit+numeric_src+dlr+concat stripprefix="1" export SMS_SMSC SMS_CONCATENATION SMS_DLR_MASK SMS_DLR_URL SMS_CP SMS_VALIDITY SMS_BINFO stripprefix ;; routo_via_kannel) SMS_SMSC="Y" export SMS_SMSC ;; *) exit1 "unknown provider \"$provider\"!" ;; esac # FIXME: some safety net against abusing this imposter feature #if [ -n "$REDIRECT_OK" ]; then case "$app" in @*) # FIXME: Avoid hardcoding country code targetphone="$(echo "$app" | perl -pe 's/^@//;' -e 's/^([^+])/+'"$ccc"'\1/')" # TODO: silence errors - or better: bounce those back to original sender targetfrom=$(lastservicephone "$targetphone") [ -n "$targetfrom" ] || exit1 "refusing to redirect: unknown target phone number \"$targetphone\"." SMS_PHONE="$targetfrom" $sbindir/localmarkdown2sms "$targetphone" "$@" exit 0 ;; esac #fi # FIXME: some safety net against abuse #if [ -n "$GROUP_OK" ]; then case "$app" in !*) group="$(echo "$app" | perl -pe 's/^!//;')" members=$(cd "$vardir/user" && grep -lF "$group" */group | perl -pe "s,/group$,,") || members= # FIXME: check that sender is member # TODO: permit non-member as sender if $REDIRECT_OK [ -n "$members" ] || exit1 "Message not sent: no members found in group \"$group\"!" for member in $members; do phone=$(head -n 1 "$vardir/user/$member/phone") || nophone="${nophone+$nophone }$member" [ -z "$phone" ] || $sbindir/localsendsms "$phone" "$@" done membercount=$(echo "$members" | wc --word) [ -z "$nophone" ] || nophonecount=$(echo "$nophone" | wc --word) [ -z "$nophone" ] || exit1 "Message sent to $membercount members of group \"$group\", but failed for $nophonecount of them (could not resolve phone number)!" exit0 "Message sent to all $membercount members of group \"$group\"." ;; esac #fi case "$app" in /*) cmd="$(echo "$app" | perl -pe 's/^\///;')" case "$cmd" in ping) if [ "help" = "$1" ]; then $sbindir/localsendsms "$to" "Usage: /ping [...] Respond \"pong\" and echo back any addition input." exit 0 fi $sbindir/localsendsms "$to" pong "$@" exit 0 ;; add|addtest) if [ "help" = "$1" ]; then $sbindir/localsendsms "$to" "Usage: /add ID PHONE [MSG]...] Register PHONE as ID, and (if included) send MSG to subscribers." exit 0 fi id="$(echo "$1" | perl -ne '/^(\d\d)$/ and print $1;')" [ -n "$id" ] || exit1 "wrong or missing id: must be 2 digits." shift # FIXME: avoid juggling with leading plus here when no longer stripped from input # FIXME: make country code optional when plus no longer stripped from input # FIXME: Avoid hardcoding country code # phone="$(echo "$1" | perl -ne 's/^(?=[^+])/+'"$ccc"'/;' -e '/^(\+\d\d\d+)$/ and print $1;')" phone="$(echo "$1" | perl -ne '/^(\d\d\d+)$/ and print $1;')" [ -n "$phone" ] || exit1 "wrong or missing phone number: must be only digits with leading + and at least 3 digits." targetfrom=$(lastservicephone "\+$phone") [ -n "$targetfrom" ] || exit1 "unknown phone +$phone: it must have been used with the system recently." phone="+$phone" shift case "$cmd" in addtest) msgphones="$addtestphones" ;; add) msgphones="$addphones" ;; esac str=$($sbindir/localsmsadmin add user "$id" "$phone") $sbindir/localsendsms "$to" "[info] \"$cmd\": $str." if [ -n "$*" ]; then for msgphone in $msgphones; do $sbindir/localsendsms "$msgphone" "[info] $id $phone $@" done $sbindir/localsendsms "$to" "[info] $id $phone added/updated. trailing message forwarded to subscribers." else $sbindir/localsendsms "$to" "[info] $id $phone added/updated. (no trailing message passed to subscribers)." fi exit 0 ;; # FIXME: generalize this ugly hacks somehow, and secure against random use go|gotest|so|sotest) case "$cmd" in go*) basecmd="go" realcmd="hello" ;; so*) basecmd="so" realcmd="szia" ;; esac if [ "help" = "$1" ]; then $sbindir/localsendsms "$to" "Usage: /$basecmd PHONE... [MSG...] /${basecmd}test PHONE... [MSG...] /$basecmd help Start thread \"$realcmd\" on behalf of each PHONE, and (if included) send MSG to subscribers." exit 0 fi [ -n "$1" ] || exit1 "target phone number missing (try \"/$cmd help\" for usage)" case "$cmd" in gotest|sotest) phones="$gotestphones" ;; go|so) phones="$gophones" ;; esac # FIXME: avoid juggling with leading plus here when no longer stripped from input # FIXME: make country code optional again when plus no longer stripped from input # FIXME: Avoid hardcoding country code while [ -n "$1" ]; do # targetphone="$(echo "$1" | perl -ne 's/^(?=[^+])/+'"$ccc"'/;' -e '/^(\+\d\d\d+)$/ and print $1;')" targetphone="$(echo "$1" | perl -ne '/^(\d\d\d+)$/ and print $1;')" [ -n "$targetphone" ] || continue # Sanity check targetfrom=$(lastservicephone "\+$targetphone") [ -n "$targetfrom" ] || exit1 "refusing to redirect: unknown target phone number +$targetphone." targetphones="${targetphones:+$targetphones }\+$targetphone" shift done # send responses (threaded one last, to work in debug mode) if [ -n "$*" ]; then for phone in $phones; do $sbindir/localsendsms "$phone" "[$cmd] $@" done $sbindir/localsendsms "$to" "[info] \"$cmd\" thread started for $targetphones and trailing message forwarded to subscribers." else $sbindir/localsendsms "$to" "[info] \"$cmd\" thread started for $targetphones (no trailing message passed to subscribers)." fi for phone in $targetphones; do SMS_PHONE=$(lastservicephone "$phone") $sbindir/localmarkdown2sms "$phone" "$realcmd" done exit 0 ;; esac candidates=$(cd "$vardir/user" && grep -lF "$to" */phone | perl -pe "s,/phone$,,") || candidates= [ -z "$candidates" ] || $(grep -vqF "$candidates" "$vardir/group/admin/user") || admin=true if [ -n "$ADMIN_OK" ] && [ -n "$admin" ]; then str="$($sbindir/localsmsadmin "$cmd" "$@" 2>&1)" || exit1 "$str" $sbindir/localsendsms "$to" $str exit 0 elif [ "help" = "$cmd" ]; then $sbindir/localsendsms "$to" "Commands: /ping [...] /COMMAND help /help try e.g. \"/del help\"." exit 0 fi ;; esac #FIXME: if sender has a mission, check for "done": process missionpending and report result to mission members # yb) # s="$(links -dump "http://www.yubnub.org/parser/parse?command=$@")" # $sbindir/localsendsms "$to" "$s" # exit 0 # ;; # Bail out if another thread already active targeted same user if pgrep -f "$(grepescape "$to")"; then for errorto in $SMS_ERRTO1 $SMS_ERRTO2; do SMS_PHONE="$SMS_ERRFROM" $sbindir/localsendsms "$errorto" "[warning] silently suppressed \"$app_raw\" for $to busy already" done exit 0 fi if [ -d /etc/local/sms.d ]; then run-parts --exit-on-error --arg="$from" --arg="$to" --arg="$app" --arg="$*" /etc/local/sms.d || exit 0 fi $sbindir/localmarkdown2sms "$to" "$app_raw" "$@"