#!/bin/sh set -e exit0() { echo "$1" exit 0 } exit1() { # FIXME: pass error messages via stderr (not stdout) # echo >&2 "Error: $1" [ -z "$1" ] || echo "$1" exit 1 } vardir="/var/lib/kannel/hello" 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)}" } 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 oldname="$(findobjects "$type" "$name")" || exit1 "$oldname" [ -z "$oldname" ] || exit1 "$type \"$name\" already exist!" case "$type" in user) session="$(findobjects "session" "default")" || exit1 "$truename" [ -z "$session" ] || phone_ccc="$(finditems "session" "$session" "phone_ccc")" || exit1 "$phone_ccc" for chunk in $@; do case $chunk in +[[:digit:]]*) [ -z "$phone" ] || exit1 "Multiple phone numbers (use only a single number, e.g. 40843136 or +4540843136)!" phone_ccc="$chunk" ;; [[:digit:]][[:digit:]][[:digit:]][[:digit:]]*) [ -n "$phone_ccc" ] || [ -n "$phone_ccc_default" ] || exit1 "Unknown country calling code (use international phone numbering, e.g. +4540843136)!" [ -z "$phone" ] || exit1 "Multiple phone numbers (use only a single number, e.g. 40843136 or +4540843136)!" # TODO: optionally validate $phone_min and $phone_max phone="$phone_ccc$chunk" ;; [[:alpha:]][[:alnum:]]*) [ -z "$nick" ] || exit1 "Multiple nick names (use only a single nick name)!" nick="$chunk" ;; *) exit1 "Internal error (additems: unknown item)!" ;; esac done [ -n "$phone" ] || exit1 "Missing phone number (use a single full number, e.g. 40843136 or +4540843136)!" [ -n "$nick" ] || exit1 "Missing nick name!" ;; esac mkdir -p "$vardir/$type/$name" chgrp --reference="$vardir" "$vardir/$type/$name" case "$type" in user) additem "$type" "$name" "nick" "$nick" additem "$type" "$name" "phone" "$phone" details="with nick and phone number" ;; esac echo "$name${details+: ($details)}" } delobject() { set -e type="$1"; [ -n "$type" ] || exit1 "Internal error (delobject: empty type)!"; shift name="$1"; [ -n "$name" ] || exit1 "Internal error (delobject: empty name)!"; shift oldname="$(findobjects "$type" "$name")" || exit1 "$oldname" [ -n "$oldname" ] || exit1 "No $type named \"$name\"!" items="$(finditems "$type" "$name")" || exit1 "$items" for item in $items; do rm "$vardir/$type/$name/$item" || exit1 "Internal error!" done rmdir "$vardir/$type/$name" || exit1 "Internal error!" echo "$name" } cmd="$1"; shift case "$cmd" in add|create) case "$1" in user|group) type="$1"; shift response="$(addobject "$type" "$@")" || exit1 "Failed adding $type${response+: $response}" exit0 "Added $type: $response." ;; *) exit1 "Cannot add \"$1\" (try \"add user\" or \"add group\" instead)!" ;; esac ;; del|delete|remove) case "$1" in user|group) type="$1"; shift response="$(delobject "$type" "$@")" || exit1 "Failed deleting $type${response+: $response}" exit0 "Deleted $type: $response." ;; *) exit1 "Cannot delete \"$1\" (try \"del user\" or \"del group\" instead)!" ;; esac ;; list|show|view) case "$1" in user|group) type="$1"; shift response="$(listobject "$type" "$@")" || exit1 "Failed listing $type${response+: $response}" exit0 "Listing $type: $response." ;; *) exit1 "Cannot list \"$1\" (try \"list user\" or \"list group\" instead)!" ;; esac ;; status) msg="$(/usr/local/sbin/localshowkannelstatus)" exit0 "$msg" ;; *) exit1 "unknown command \"$cmd\"!" ;; esac