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