summaryrefslogtreecommitdiff
path: root/localsmsadmin
blob: 3fb6d077629d3ac3e449dff12452a96bb7065339 (plain)
  1. #!/bin/sh
  2. set -e
  3. # TODO: convert data handling to DBD::AnyData
  4. # make data writable both from kannel and designated shell accounts
  5. umask 0007
  6. vardir=/var/lib/kannel/hello
  7. sbindir=/usr/local/sbin
  8. exit0() {
  9. [ -n "$1" ] && echo "$1"
  10. exit 0
  11. }
  12. exit1() {
  13. response="${1:+Error: }${1:-Internal error!}"
  14. # FIXME: pass error messages via stderr (not stdout)
  15. # echo >&2 "$response"
  16. echo "$response"
  17. exit 1
  18. }
  19. findobjects() {
  20. set -e
  21. type="$1"; [ -n "$type" ] || return1 "Internal error (findobjects: empty type)!"; shift
  22. limit="$@"
  23. regex="$(echo "$limit" | perl -ne '/^\/(.+)\/$/ and print $1')"
  24. args="$(echo "$limit" | perl -ne 's/.*?([a-z0-9]+).*?/ $1/g and print')"
  25. [ -d "$vardir/$type" ] || exit0
  26. if [ -z "$*" ]; then
  27. cd "$vardir/$type" && find -mindepth 1 -maxdepth 1 -type d -printf '%f\n'
  28. elif [ -n "$regex" ]; then
  29. cd "$vardir/$type" && find -mindepth 1 -maxdepth 1 -type d -regex "$regex"
  30. elif [ -n "$args" ]; then
  31. args_multiline="$(echo "$args" | perl -pe 's/[[:space:]]+/\n/g')"
  32. cd "$vardir/$type" && ! find -mindepth 1 -maxdepth 1 -type d -printf '%f\n' \
  33. | grep -Fx "$args_multiline" || true
  34. else
  35. exit1 "Internal error (findobjects: illegal limit)!"
  36. fi
  37. }
  38. finditems() {
  39. set -e
  40. type="$1"; [ -n "$type" ] || exit1 "Internal error (finditems: empty type)!"; shift
  41. name="$1"; [ -n "$name" ] || exit1 "Internal error (finditems: empty name)!"; shift
  42. limit="$@"
  43. regex="$(echo "$limit" | perl -ne '/^\/(.+)\/$/ and print $1')"
  44. args="$(echo "$limit" | perl -ne 's/.*?([a-z0-9]+).*?/ $1/g and print')"
  45. dir="$type/$name"
  46. [ -d "$vardir/$dir" ] || exit0
  47. if [ -z "$*" ]; then
  48. cd "$vardir/$dir" && find -mindepth 1 -maxdepth 1 -type f -printf '%f\n'
  49. elif [ -n "$regex" ]; then
  50. cd "$vardir/$dir" && find -mindepth 1 -maxdepth 1 -type f -regex "$regex"
  51. elif [ -n $args ]; then
  52. args_multiline="$(echo "$args" | perl -pe 's/[[:space:]]+/\n/g')"
  53. cd "$vardir/$dir" && ! find -mindepth 1 -maxdepth 1 -type f -printf '%f\n' \
  54. | grep -Fx "$args_multiline" || true
  55. else
  56. exit1 "Internal error (finditems: illegal limit)!"
  57. fi
  58. }
  59. getitem() {
  60. set -e
  61. type="$1"; [ -n "$type" ] || exit1 "Internal error (getitem: empty type)!"; shift
  62. name="$1"; [ -n "$name" ] || exit1 "Internal error (getitem: empty name)!"; shift
  63. item="$1"; [ -n "$item" ] || exit1 "Internal error (getitem: empty item)!"; shift
  64. file="$type/$name/$item"
  65. [ ! -s "$vardir/$file" ] || cat "$vardir/$file"
  66. }
  67. additem() {
  68. set -e
  69. type="$1"; [ -n "$type" ] || exit1 "Internal error (additem: empty type)!"; shift
  70. name="$1"; [ -n "$name" ] || exit1 "Internal error (additem: empty name)!"; shift
  71. item="$1"; [ -n "$item" ] || exit1 "Internal error (additem: empty item)!"; shift
  72. data="$*"; [ -n "$data" ] || exit1 "Internal error (additem: empty data)!"; shift
  73. data="$(echo "$data" | perl -0 -pe 's/\s+/ /g; s/^\s+//; s/\s+$//')"
  74. truename="$(findobjects "$type" "$name")" || exit1 "$truename"
  75. [ -n "$truename" ] || exit1 "No $type named \"$name\"!"
  76. file="$type/$truename/$item"
  77. # [ ! -s "$vardir/$file" ] || linecount="$(grep -c . "$vardir/$file")"
  78. # echo "$data" >> "$vardir/$file"
  79. # chgrp --reference="$vardir" "$vardir/$file"
  80. # echo "$data${linecount+ (on top of $linecount other values)}"
  81. [ ! -s "$vardir/$file" ] || linecount="$(grep -c . "$vardir/$file")"
  82. echo "$data" > "$vardir/$file"
  83. # chgrp --reference="$vardir" "$vardir/$file"
  84. echo "$data"
  85. }
  86. addobject() {
  87. set -e
  88. type="$1"; [ -n "$type" ] || exit1 "Internal error (addobject: empty type)!"; shift
  89. name="$1"; [ -n "$name" ] || exit1 "Internal error (addobject: empty name)!"; shift
  90. fallback="$1"; [ -n "$fallback" ] || exit1 "Internal error (addobject: empty fallback)!"; shift
  91. [ -z "$*" ] || exit1 "Internal error (addobject: too many options)!"
  92. case "$fallback" in
  93. keep|replace|fail) :;;
  94. *) exit1 "Internal error (addobject: unknown fallback)!";;
  95. esac
  96. # if [ -e "$vardir/$type/$name" ]; then
  97. oldname="$(findobjects "$type" "$name")" || exit1 "$oldname"
  98. if [ -n "$oldname" ]; then
  99. case "$fallback" in
  100. keep)
  101. action="kept"
  102. ;;
  103. replace)
  104. delopbject "$type" "$oldname"
  105. ;;
  106. # TODO rename)
  107. # TODO renameopbject "$type" "$oldname"
  108. # TODO ;;
  109. fail)
  110. exit1 "Adding $type failed ($name already exists)!"
  111. ;;
  112. esac
  113. else
  114. mkdir -p "$vardir/$type/$name"
  115. # chgrp -R --reference="$vardir" "$vardir/$type"
  116. fi
  117. }
  118. cmd="$1"; shift
  119. case "$cmd" in
  120. add|create)
  121. type="$1"; [ -n "$type" ] || exit1 "Internal error (empty type)!"; shift
  122. case "$type" in
  123. help)
  124. cat <<EOF
  125. Usage:
  126. /add user NICK [+PREFIX]NUMBER [GROUP ...]
  127. /add group GROUP [USER ...]
  128. /add session SESSION [PREFIX] [GROUP]
  129. /add default prefix|session VALUE
  130. Add or update object and embedded items
  131. EOF
  132. exit 0
  133. ;;
  134. esac
  135. name="$1"; [ -n "$name" ] || exit1 "Internal error (empty name)!"; shift
  136. oldname="$(findobjects "$type" "$name")" || exit1 "$oldname"
  137. case "$type" in
  138. default)
  139. case "$name" in
  140. session|prefix)
  141. item="$name"
  142. data="$1"
  143. ;;
  144. *)
  145. exit1 "unknown default \"$1\""
  146. ;;
  147. esac
  148. str=$(addobject "$type" "$name" keep) || exit1 "$str"
  149. str=$(additem "$type" "$name" "$item" "$data") || exit1 "$str"
  150. response="Added $type $name $data"
  151. ;;
  152. user)
  153. session="$(getitem "default" "session" "session")" || exit1 "Internal error: $session"
  154. [ -z "$session" ] || session_group="$(getitem "session" "$session" "group")" || exit1 "Internal error: $session_group"
  155. plural=
  156. for chunk in $@; do
  157. case $chunk in
  158. +[[:digit:]]*)
  159. [ -z "$phone" ] || exit1 "Too many phone numbers (use only a single number, e.g. 40843136 or +4540843136)!"
  160. phone="$chunk"
  161. ;;
  162. [[:digit:]][[:digit:]][[:digit:]][[:digit:]]*)
  163. [ -z "$phone" ] || exit1 "Too many phone numbers (use only a single number, e.g. 40843136 or +4540843136)!"
  164. [ -z "$session" ] || prefix="$(getitem "session" "$session" "prefix")" || exit1 "Internal error: $prefix"
  165. [ -n "$prefix" ] || exit1 "Missing prefix (use international phone numbering, e.g. +4540843136${session+, or add default prefix to session $session})!"
  166. # TODO: optionally validate $phone_min and $phone_max
  167. phone="$prefix$chunk"
  168. ;;
  169. [[:alpha:]][[:alnum:]]*)
  170. [ -z "$groups" ] || plural=true
  171. groups="${groups+$groups }$chunk"
  172. ;;
  173. *)
  174. exit1 "Internal error (unknown user item \"$1\")!"
  175. ;;
  176. esac
  177. done
  178. response="Added $type $name"
  179. fallback=fail
  180. if [ -z "$oldname" ]; then
  181. [ -n "$phone" ] || exit1 "Missing phone number (required)!"
  182. else
  183. [ -n "$phone$groups" ] || exit1 "No items to update (include one or more of phone number and groups)!"
  184. fallback=keep
  185. response="Updated $type $name"
  186. fi
  187. str=$(addobject "$type" "$name" "$fallback") || exit1 "$str"
  188. [ -z "$phone" ] || dummy="$(additem "$type" "$name" "phone" "$phone")"
  189. # TODO: distinguish session group from included ones (i.e. mention explicitly if added)
  190. [ -z "$session_group" ] || groups="$session_group $groups"
  191. for group in $groups; do
  192. # TODO: strip existing, duplicate and session groups, and mention stripping in response
  193. dummy="$(additem "$type" "$name" "group" "$group")"
  194. done
  195. if [ -n "$phone" ]; then
  196. if [ -n "$groups" ]; then
  197. response="$response with phone $phone and group${plural+s} $groups"
  198. else
  199. response="$response with phone $phone"
  200. fi
  201. elif [ -n "$groups" ]; then
  202. response="$response with group${plural+s} $groups"
  203. fi
  204. ;;
  205. group)
  206. for chunk in $@; do
  207. case $chunk in
  208. [[:alpha:]][[:alnum:]]*)
  209. [ -z "$session" ] || exit1 "Too many sessions (a group can be only in a single session)!"
  210. session="$chunk"
  211. ;;
  212. *)
  213. exit1 "Internal error (unknown group item \"$1\")!"
  214. ;;
  215. esac
  216. done
  217. response="Added $type $name"
  218. fallback=fail
  219. if [ -n "$oldname" ]; then
  220. [ -n "$session" ] || exit1 "Group already exists (and no session name to update was included)!"
  221. fallback=keep
  222. response="Updated $type $name"
  223. fi
  224. str=$(addobject "$type" "$name" "$fallback") || exit1 "$str"
  225. [ -z "$session" ] || dummy="$(additem "$type" "$name" "session" "$session")"
  226. [ -z "$session" ] || response="$response with session $session"
  227. ;;
  228. session)
  229. for chunk in $@; do
  230. case $chunk in
  231. +[[:digit:]]*)
  232. [ -z "$prefix" ] || exit1 "Too many country prefixes (use only one, e.g. +45)!"
  233. prefix="$chunk"
  234. ;;
  235. [[:alpha:]][[:alnum:]]*)
  236. [ -z "$group" ] || exit1 "Too many default groups (use only one)!"
  237. # FIXME: fail if group does not exist
  238. group="$chunk"
  239. ;;
  240. *)
  241. exit1 "Internal error (unknown session item \"$1\")!"
  242. ;;
  243. esac
  244. done
  245. # TODO: distinguish default prefix from included one (i.e. mention explicitly if added)
  246. [ -n "$prefix" ] || prefix="$(getitem "default" "prefix" "prefix")" || exit1 "Internal error: $prefix"
  247. response="Added $type $name"
  248. fallback=fail
  249. if [ -n "$oldname" ]; then
  250. [ -n "$prefix$group" ] || exit1 "Session already exists (and no default group or country prefix to add/update was included)!"
  251. fallback=keep
  252. response="Updated $type $name"
  253. fi
  254. str=$(addobject "$type" "$name" "$fallback") || exit1 "$str"
  255. [ -z "$prefix" ] || dummy="$(additem "$type" "$name" "prefix" "$prefix")"
  256. [ -z "$group" ] || dummy="$(additem "$type" "$name" "group" "$group")"
  257. if [ -n "$prefix" ]; then
  258. if [ -n "$group" ]; then
  259. response="$response with default country prefix $prefix and default group $group"
  260. else
  261. response="$response with default country prefix $prefix"
  262. fi
  263. elif [ -n "$group" ]; then
  264. response="$response with default group $group"
  265. fi
  266. ;;
  267. *)
  268. exit1 "Cannot add \"$1\" (try \"/help\" to see supported commands)!"
  269. ;;
  270. esac
  271. exit0 "$response"
  272. ;;
  273. del|delete|remove)
  274. case "$1" in
  275. help)
  276. cat <<EOF
  277. Usage:
  278. /del user NICK|NUMBER
  279. /del group GROUP
  280. /del session SESSION
  281. /del default prefix|session
  282. Remove object and embedded items
  283. EOF
  284. exit 0
  285. ;;
  286. default|user|group|session)
  287. type="$1"; shift
  288. name="$1"; [ -n "$name" ] || exit1 "Internal error (empty name)!"; shift
  289. [ -z "$*" ] || exit1 "Internal error (too many options)!"
  290. oldname="$(findobjects "$type" "$name")" || exit1 "Internal error${oldname+: $oldname}"
  291. [ -n "$oldname" ] || exit1 "No $type named \"$name\"!"
  292. items="$(finditems "$type" "$name")" || exit1 "Internal error${items+: $items}"
  293. # TODO: find user by phone number
  294. for item in $items; do
  295. rm "$vardir/$type/$name/$item" || exit1 "Internal error during $type $name item removal!"
  296. done
  297. # FIXME: remove items of other objects referencing this object
  298. rmdir "$vardir/$type/$name" || exit1 "Internal error during $type $name object removal!"
  299. exit0 "Deleted $type: $name."
  300. ;;
  301. *)
  302. exit1 "Cannot delete \"$1\" (try \"/help\" to see supported commands)!"
  303. ;;
  304. esac
  305. ;;
  306. # list|show|view)
  307. # case "$1" in
  308. # default|user|group|session)
  309. # type="$1"; shift
  310. # response="$(listobject "$type" "$@")" || exit1 "Failed listing $type${response+: $response}"
  311. # exit0 "Listing $type: $response."
  312. # ;;
  313. # *)
  314. # exit1 "Cannot list \"$1\" (try \"/help\" to see supported commands)!"
  315. # ;;
  316. # esac
  317. # ;;
  318. help)
  319. cat <<EOF
  320. Admin commands:
  321. /add user|group|session|default ...
  322. /del user|group|session|default ...
  323. /COMMAND help
  324. try e.g. "/del help".
  325. EOF
  326. exit 0
  327. ;;
  328. status)
  329. response="$($sbindir/localshowkannelstatus)"
  330. exit0 "$response"
  331. ;;
  332. *)
  333. exit1 "unknown command \"$cmd\"!"
  334. ;;
  335. esac