blob: 9628ef7fd56f8d7a22b235fd0a886e2ddf0044ec (
plain)
- #!/bin/sh
- set -e
- # TODO: convert data handling to DBD::AnyData
- # make data writable both from kannel and designated shell accounts
- umask 0007
- 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"
- exit 1
- }
- findobjects() {
- set -e
- type="$1"; [ -n "$type" ] || return1 "Internal error (findobjects: empty type)!"; shift
- limit="$@"
- regex="$(echo "$limit" | perl -ne '/^\/(.+)\/$/ and print $1')"
- args="$(echo "$limit" | perl -ne 's/.*?([a-z0-9]+).*?/ $1/g and print')"
- [ -d "$vardir/$type" ] || exit0
- if [ -z "$*" ]; then
- cd "$vardir/$type" && find -mindepth 1 -maxdepth 1 -type d -printf '%f\n'
- elif [ -n "$regex" ]; then
- cd "$vardir/$type" && find -mindepth 1 -maxdepth 1 -type d -regex "$regex"
- elif [ -n "$args" ]; then
- args_multiline="$(echo "$args" | perl -pe 's/[[:space:]]+/\n/g')"
- cd "$vardir/$type" && ! find -mindepth 1 -maxdepth 1 -type d -printf '%f\n' \
- | grep -Fx "$args_multiline" || true
- else
- exit1 "Internal error (findobjects: illegal limit)!"
- fi
- }
- finditems() {
- set -e
- type="$1"; [ -n "$type" ] || exit1 "Internal error (finditems: empty type)!"; shift
- name="$1"; [ -n "$name" ] || exit1 "Internal error (finditems: empty name)!"; shift
- limit="$@"
- regex="$(echo "$limit" | perl -ne '/^\/(.+)\/$/ and print $1')"
- args="$(echo "$limit" | perl -ne 's/.*?([a-z0-9]+).*?/ $1/g and print')"
- dir="$type/$name"
- [ -d "$vardir/$dir" ] || exit0
- if [ -z "$*" ]; then
- cd "$vardir/$dir" && find -mindepth 1 -maxdepth 1 -type f -printf '%f\n'
- elif [ -n "$regex" ]; then
- cd "$vardir/$dir" && find -mindepth 1 -maxdepth 1 -type f -regex "$regex"
- elif [ -n $args ]; then
- args_multiline="$(echo "$args" | perl -pe 's/[[:space:]]+/\n/g')"
- cd "$vardir/$dir" && ! find -mindepth 1 -maxdepth 1 -type f -printf '%f\n' \
- | grep -Fx "$args_multiline" || true
- else
- exit1 "Internal error (finditems: illegal limit)!"
- fi
- }
- getitem() {
- set -e
- set -x
- type="$1"; [ -n "$type" ] || exit1 "Internal error (getitem: empty type)!"; shift
- name="$1"; [ -n "$name" ] || exit1 "Internal error (getitem: empty name)!"; shift
- item="$1"; [ -n "$item" ] || exit1 "Internal error (getitem: empty item)!"; shift
- file="$type/$name/$item"
- [ ! -s "$vardir/$file" ] || cat "$vardir/$file"
- }
- additem() {
- set -e
- type="$1"; [ -n "$type" ] || exit1 "Internal error (additem: empty type)!"; shift
- name="$1"; [ -n "$name" ] || exit1 "Internal error (additem: empty name)!"; shift
- item="$1"; [ -n "$item" ] || exit1 "Internal error (additem: empty item)!"; shift
- data="$*"; [ -n "$data" ] || exit1 "Internal error (additem: empty data)!"; shift
- data="$(echo "$data" | perl -0 -pe 's/\s+/ /g; s/^\s+//; s/\s+$//')"
- truename="$(findobjects "$type" "$name")" || exit1 "$truename"
- [ -n "$truename" ] || exit1 "No $type named \"$name\"!"
- file="$type/$truename/$item"
- # [ ! -s "$vardir/$file" ] || linecount="$(grep -c . "$vardir/$file")"
- # echo "$data" >> "$vardir/$file"
- # chgrp --reference="$vardir" "$vardir/$file"
- # echo "$data${linecount+ (on top of $linecount other values)}"
- [ ! -s "$vardir/$file" ] || linecount="$(grep -c . "$vardir/$file")"
- echo "$data" > "$vardir/$file"
- # chgrp --reference="$vardir" "$vardir/$file"
- echo "$data"
- }
- addobject() {
- set -e
- type="$1"; [ -n "$type" ] || exit1 "Internal error (addobject: empty type)!"; shift
- name="$1"; [ -n "$name" ] || exit1 "Internal error (addobject: empty name)!"; shift
- fallback="$1"; [ -n "$fallback" ] || exit1 "Internal error (addobject: empty fallback)!"; shift
- [ -z "$*" ] || exit1 "Internal error (addobject: too many options)!"
- case "$fallback" in
- keep|replace|fail) :;;
- *) exit1 "Internal error (addobject: unknown fallback)!";;
- esac
- # if [ -e "$vardir/$type/$name" ]; then
- oldname="$(findobjects "$type" "$name")" || exit1 "$oldname"
- if [ -n "$oldname" ]; then
- case "$fallback" in
- keep)
- action="kept"
- ;;
- replace)
- delopbject "$type" "$oldname"
- ;;
- # TODO rename)
- # TODO renameopbject "$type" "$oldname"
- # TODO ;;
- fail)
- exit1 "Adding $type failed ($name already exists)!"
- ;;
- esac
- else
- mkdir -p "$vardir/$type/$name"
- # chgrp -R --reference="$vardir" "$vardir/$type"
- fi
- }
- cmd="$1"; shift
- case "$cmd" in
- add|create)
- type="$1"; [ -n "$type" ] || exit1 "Internal error (empty type)!"; shift
- case "$type" in
- help)
- cat <<EOF
- Usage:
- /add user NICK [+PREFIX]NUMBER [GROUP ...]
- /add group GROUP [USER ...]
- /add session SESSION [PREFIX] [GROUP]
- /add default prefix|session VALUE
- Add or update object and embedded items
- EOF
- exit 0
- ;;
- esac
- name="$1"; [ -n "$name" ] || exit1 "Internal error (empty name)!"; shift
- oldname="$(findobjects "$type" "$name")" || exit1 "$oldname"
- case "$type" in
- default)
- case "$name" in
- session|prefix)
- item="$name"
- data="$1"
- ;;
- *)
- exit1 "unknown default \"$1\""
- ;;
- esac
- str=$(addobject "$type" "$name" keep) || exit1 "$str"
- str=$(additem "$type" "$name" "$item" "$data") || exit1 "$str"
- response="Added $type $name $data"
- ;;
- user)
- session="$(getitem "default" "session" "session")" || exit1 "Internal error: $session"
- [ -z "$session" ] || session_group="$(getitem "session" "$session" "group")" || exit1 "Internal error: $session_group"
- plural=
- for chunk in $@; do
- case $chunk in
- +[[:digit:]]*)
- [ -z "$phone" ] || exit1 "Too many phone numbers (use only a single number, e.g. 40843136 or +4540843136)!"
- phone="$chunk"
- ;;
- [[:digit:]][[:digit:]][[:digit:]][[:digit:]]*)
- [ -z "$phone" ] || exit1 "Too many phone numbers (use only a single number, e.g. 40843136 or +4540843136)!"
- [ -z "$session" ] || prefix="$(getitem "session" "$session" "prefix")" || exit1 "Internal error: $prefix"
- [ -n "$prefix" ] || exit1 "Missing prefix (use international phone numbering, e.g. +4540843136${session+, or add default prefix to session $session})!"
- # TODO: optionally validate $phone_min and $phone_max
- phone="$prefix$chunk"
- ;;
- [[:alpha:]][[:alnum:]]*)
- [ -z "$groups" ] || plural=true
- groups="${groups+$groups }$chunk"
- ;;
- *)
- exit1 "Internal error (unknown user item \"$1\")!"
- ;;
- esac
- done
- response="Added $type $name"
- fallback=fail
- if [ -z "$oldname" ]; then
- [ -n "$phone" ] || exit1 "Missing phone number (required)!"
- else
- [ -n "$phone$groups" ] || exit1 "No items to update (include one or more of phone number and groups)!"
- fallback=keep
- response="Updated $type $name"
- fi
- str=$(addobject "$type" "$name" "$fallback") || exit1 "$str"
- [ -z "$phone" ] || dummy="$(additem "$type" "$name" "phone" "$phone")"
- # TODO: distinguish session group from included ones (i.e. mention explicitly if added)
- [ -z "$session_group" ] || groups="$session_group $groups"
- for group in $groups; do
- # TODO: strip existing, duplicate and session groups, and mention stripping in response
- dummy="$(additem "$type" "$name" "group" "$group")"
- done
- if [ -n "$phone" ]; then
- if [ -n "$groups" ]; then
- response="$response with phone $phone and group${plural+s} $groups"
- else
- response="$response with phone $phone"
- fi
- elif [ -n "$groups" ]; then
- response="$response with group${plural+s} $groups"
- fi
- ;;
- group)
- for chunk in $@; do
- case $chunk in
- [[:alpha:]][[:alnum:]]*)
- [ -z "$session" ] || exit1 "Too many sessions (a group can be only in a single session)!"
- session="$chunk"
- ;;
- *)
- exit1 "Internal error (unknown group item \"$1\")!"
- ;;
- esac
- done
- response="Added $type $name"
- fallback=fail
- if [ -n "$oldname" ]; then
- [ -n "$session" ] || exit1 "Group already exists (and no session name to update was included)!"
- fallback=keep
- response="Updated $type $name"
- fi
- str=$(addobject "$type" "$name" "$fallback") || exit1 "$str"
- [ -z "$session" ] || dummy="$(additem "$type" "$name" "session" "$session")"
- [ -z "$session" ] || response="$response with session $session"
- ;;
- session)
- for chunk in $@; do
- case $chunk in
- +[[:digit:]]*)
- [ -z "$prefix" ] || exit1 "Too many country prefixes (use only one, e.g. +45)!"
- prefix="$chunk"
- ;;
- [[:alpha:]][[:alnum:]]*)
- [ -z "$group" ] || exit1 "Too many default groups (use only one)!"
- # FIXME: fail if group does not exist
- group="$chunk"
- ;;
- *)
- exit1 "Internal error (unknown session item \"$1\")!"
- ;;
- esac
- done
- # TODO: distinguish default prefix from included one (i.e. mention explicitly if added)
- [ -n "$prefix" ] || prefix="$(getitem "default" "prefix" "prefix")" || exit1 "Internal error: $prefix"
- response="Added $type $name"
- fallback=fail
- if [ -n "$oldname" ]; then
- [ -n "$prefix$group" ] || exit1 "Session already exists (and no default group or country prefix to add/update was included)!"
- fallback=keep
- response="Updated $type $name"
- fi
- str=$(addobject "$type" "$name" "$fallback") || exit1 "$str"
- [ -z "$prefix" ] || dummy="$(additem "$type" "$name" "prefix" "$prefix")"
- [ -z "$group" ] || dummy="$(additem "$type" "$name" "group" "$group")"
- if [ -n "$prefix" ]; then
- if [ -n "$group" ]; then
- response="$response with default country prefix $prefix and default group $group"
- else
- response="$response with default country prefix $prefix"
- fi
- elif [ -n "$group" ]; then
- response="$response with default group $group"
- fi
- ;;
- *)
- exit1 "Cannot add \"$1\" (try \"/help\" to see supported commands)!"
- ;;
- esac
- exit0 "$response"
- ;;
- del|delete|remove)
- case "$1" in
- help)
- cat <<EOF
- Usage:
- /del user NICK|NUMBER
- /del group GROUP
- /del session SESSION
- /del default prefix|session
- Remove object and embedded items
- EOF
- exit 0
- ;;
- default|user|group|session)
- type="$1"; shift
- name="$1"; [ -n "$name" ] || exit1 "Internal error (empty name)!"; shift
- [ -z "$*" ] || exit1 "Internal error (too many options)!"
- oldname="$(findobjects "$type" "$name")" || exit1 "Internal error${oldname:+ $oldname}"
- [ -n "$oldname" ] || exit1 "No $type named \"$name\"!"
- items="$(finditems "$type" "$name")" || exit1 "Internal error${items:+ $items}"
- # TODO: find user by phone number
- for item in $items; do
- rm "$vardir/$type/$name/$item" || exit1 "Internal error during $type $name item removal!"
- done
- # FIXME: remove items of other objects referencing this object
- rmdir "$vardir/$type/$name" || exit1 "Internal error during $type $name object removal!"
- exit0 "Deleted $type: $name."
- ;;
- *)
- exit1 "Cannot delete \"$1\" (try \"/help\" to see supported commands)!"
- ;;
- esac
- ;;
- find)
- case "$1" in
- help)
- cat <<EOF
- Usage:
- /find user keyword
- /find group keyword
- /find session keyword
- /find default keyword
- Find and return name of object if it exist
- EOF
- exit 0
- ;;
- default|user|group|session)
- type="$1"; shift
- name="$1"; [ -n "$name" ] || exit1 "Internal error (empty name)!"; shift
- [ -z "$*" ] || exit1 "Internal error (too many options)!"
- oldname="$(findobjects "$type" "$name")" || exit1 "Internal error${oldname:+ $oldname}"
- [ -n "$oldname" ] || exit1 "No $type named \"$name\"!"
- exit0 "$name"
- ;;
- *)
- exit1 "Cannot find \"$1\" (try \"/help\" to see supported commands)!"
- ;;
- esac
- ;;
- mission)
- case "$1" in
- help)
- cat <<EOF
- Usage:
- /mission AGENT @VICTIM msg...
- Send AGENT contact info on VICTIM.
- When AGENT responds "done", msg is sent on behalf of VICTIM.
- EOF
- #TODO: Store mission as an object:
- # Has an owner
- # Can have subscribers who get progress info
- # msg is stored in mission, is optional, and originator is mission owner (i.e. sending on behalf of victim is optional)
- exit 0
- ;;
- *)
- agent="$1"; [ -n "$agent" ] || exit1 "Missing agent (try \"/mission help\" for usage info)!"; shift
- #FIXME: strip leading @ from victim
- victim="$1"; [ -n "$victim" ] || exit1 "Missing victim (try \"/mission help\" for usage info)!"; shift
- [ -n "$*" ] || exit1 "Missing message (try \"/mission help\" for usage info)!"
- agentname="$(findobjects user "$agent")"; [ -n "$agentname" ] || exit1 "Unknown agent \"$agent\" (try \"/mission help\" for usage info)!"
- agentphone="$(getitem user "$agent" phone)"; [ -n "$agentphone" ] || exit1 "Failed resolving agent phone number."
- victimname="$(findobjects user "$victim")"; [ -n "$victimname" ] || exit1 "Unknown victim \"$victim\" (try \"/mission help\" for usage info)!"
- victimphone="$(getitem user "$victim" phone)"; [ -n "$victimphone" ] || exit1 "Failed resolving victim phone number."
- task="Contact $victimname ($victimphone). When done send text: done"
- str=$(additem user "$agentname" "pendingmission" "@$victimname $*") || exit1 "$str"
- $sbindir/localsendsms "$agentphone" "$task"
- cat <<EOF
- Mission initiated:
- $agentname: $task
- ($@)
- EOF
- exit 0
- ;;
- esac
- ;;
- help)
- cat <<EOF
- Commands:
- /ping [...]
- /add user|group|session|default ...
- /del user|group|session|default ...
- /mission AGENT @VICTIM msg...
- /COMMAND help
- /help
- try e.g. "/del help".
- EOF
- exit 0
- ;;
- status)
- response="$($sbindir/localshowkannelstatus)"
- exit0 "$response"
- ;;
- *)
- exit1 "unknown command \"$cmd\"!"
- ;;
- esac
|