diff options
author | Jonas Smedegaard <dr@jones.dk> | 2010-07-04 02:29:45 +0200 |
---|---|---|
committer | Jonas Smedegaard <dr@jones.dk> | 2010-07-04 02:29:45 +0200 |
commit | 7f129819411be1341692e45694a45e99e8a1f42f (patch) | |
tree | 9a4a1fb286692840f322af8718f97895e357d0a4 /localsmsadmin | |
parent | 966b29c496ef6851496641334f922584633b0a01 (diff) | |
parent | f8322721903e0c049ddd4ee681eb8b9691f56b46 (diff) |
Merge coreander:/srv/git/source.jones.dk/sbin
Diffstat (limited to 'localsmsadmin')
-rwxr-xr-x | localsmsadmin | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/localsmsadmin b/localsmsadmin new file mode 100755 index 0000000..054db55 --- /dev/null +++ b/localsmsadmin @@ -0,0 +1,323 @@ +#!/bin/sh + +set -e + +# 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() { + [ -z "$1" ] || echo "Error: $1" || echo "Internal error!" + 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 + 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 + 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 + 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 + ;; + list|show|view) + case "$1" in + default|user|group|session) + type="$1"; shift + response="$(listobject "$type" "$@")" || exit1 "Failed listing $type${response+: $response}" + exit0 "Listing $type: $response." + ;; + *) + exit1 "Cannot list \"$1\" (try \"/help\" to see supported commands)!" + ;; + esac + ;; + help) + cat <<EOF +Admin commands: +/add user NICK [+PREFIX]NUMBER [GROUP ...] +/add group GROUP [USER ...] +/add session SESSION [PREFIX] [GROUP] +/add default prefix|session VALUE +/del user NICK|NUMBER +/del group GROUP +/del session SESSION +/del default prefix|session +EOF + exit 0 + ;; + status) + response="$($sbindir/localshowkannelstatus)" + exit0 "$response" + ;; + *) + exit1 "unknown command \"$cmd\"!" + ;; +esac |