New packages: - secubox-app-voip: Asterisk PBX in LXC container - luci-app-voip: Dashboard with extensions, trunks, click-to-call VoIP features: - voipctl CLI for container, extensions, trunks, calls, voicemail - OVH Telephony API auto-provisioning for SIP trunks - Click-to-call web interface with quick dial - RPCD backend with 15 methods Jabber VoIP integration: - Jingle VoIP support (STUN/TURN via mod_external_services) - SMS relay via OVH (messages to sms@domain) - Voicemail notifications via Asterisk AMI → XMPP - 9 new RPCD methods for VoIP features Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
764 lines
16 KiB
Bash
Executable File
764 lines
16 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
# RPCD backend for Jabber/XMPP LuCI app
|
|
|
|
. /usr/share/libubox/jshn.sh
|
|
|
|
JABBERCTL="/usr/sbin/jabberctl"
|
|
|
|
# Helper to get UCI value
|
|
uci_get() {
|
|
local section="$1"
|
|
local option="$2"
|
|
local default="$3"
|
|
local val
|
|
val=$(uci -q get "jabber.${section}.${option}")
|
|
echo "${val:-$default}"
|
|
}
|
|
|
|
# Get container status
|
|
get_container_status() {
|
|
local state="not_installed"
|
|
local running="false"
|
|
local lxc_info=""
|
|
|
|
if [ -d "/srv/lxc/jabber" ]; then
|
|
state="installed"
|
|
lxc_info=$(lxc-info -n jabber 2>/dev/null)
|
|
if echo "$lxc_info" | grep -q "State:.*RUNNING"; then
|
|
running="true"
|
|
fi
|
|
fi
|
|
|
|
echo "$state $running"
|
|
}
|
|
|
|
# Method: status
|
|
method_status() {
|
|
local enabled hostname c2s_port s2s_port http_port
|
|
local container_state running
|
|
local info user_count
|
|
|
|
enabled=$(uci_get main enabled 0)
|
|
hostname=$(uci_get server hostname "jabber.local")
|
|
c2s_port=$(uci_get server c2s_port "5222")
|
|
s2s_port=$(uci_get server s2s_port "5269")
|
|
http_port=$(uci_get server http_port "5280")
|
|
|
|
info=$(get_container_status)
|
|
container_state=$(echo "$info" | awk '{print $1}')
|
|
running=$(echo "$info" | awk '{print $2}')
|
|
|
|
# Get user count
|
|
user_count=0
|
|
if [ "$running" = "true" ]; then
|
|
user_count=$(lxc-attach -n jabber -- find /var/lib/prosody -name "*.dat" -path "*accounts*" 2>/dev/null | wc -l)
|
|
fi
|
|
|
|
# Get configured domain if emancipated
|
|
local domain haproxy muc_enabled s2s_enabled
|
|
domain=$(uci_get network domain "")
|
|
haproxy=$(uci_get network haproxy "0")
|
|
muc_enabled=$(uci_get muc enabled "1")
|
|
s2s_enabled=$(uci_get s2s enabled "0")
|
|
|
|
# Get admin info
|
|
local admin_email admin_user
|
|
admin_email=$(uci_get admin email "admin@localhost")
|
|
admin_user=$(uci_get admin initial_user "admin")
|
|
|
|
json_init
|
|
json_add_string "enabled" "$enabled"
|
|
json_add_string "container_state" "$container_state"
|
|
json_add_string "running" "$running"
|
|
json_add_string "hostname" "$hostname"
|
|
json_add_string "c2s_port" "$c2s_port"
|
|
json_add_string "s2s_port" "$s2s_port"
|
|
json_add_string "http_port" "$http_port"
|
|
json_add_int "user_count" "$user_count"
|
|
json_add_string "domain" "$domain"
|
|
json_add_string "haproxy" "$haproxy"
|
|
json_add_string "muc_enabled" "$muc_enabled"
|
|
json_add_string "s2s_enabled" "$s2s_enabled"
|
|
json_add_string "admin_email" "$admin_email"
|
|
json_add_string "admin_user" "$admin_user"
|
|
json_dump
|
|
}
|
|
|
|
# Method: start
|
|
method_start() {
|
|
local output
|
|
output=$($JABBERCTL start 2>&1)
|
|
local rc=$?
|
|
|
|
json_init
|
|
if [ $rc -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Jabber/XMPP started successfully"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "$output"
|
|
fi
|
|
json_dump
|
|
}
|
|
|
|
# Method: stop
|
|
method_stop() {
|
|
local output
|
|
output=$($JABBERCTL stop 2>&1)
|
|
local rc=$?
|
|
|
|
json_init
|
|
if [ $rc -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Jabber/XMPP stopped successfully"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "$output"
|
|
fi
|
|
json_dump
|
|
}
|
|
|
|
# Method: install
|
|
method_install() {
|
|
local output
|
|
output=$($JABBERCTL install 2>&1)
|
|
local rc=$?
|
|
|
|
json_init
|
|
if [ $rc -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Jabber/XMPP installed successfully"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "$output"
|
|
fi
|
|
json_dump
|
|
}
|
|
|
|
# Method: uninstall
|
|
method_uninstall() {
|
|
local output
|
|
output=$($JABBERCTL uninstall 2>&1)
|
|
local rc=$?
|
|
|
|
json_init
|
|
if [ $rc -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Jabber/XMPP uninstalled successfully"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "$output"
|
|
fi
|
|
json_dump
|
|
}
|
|
|
|
# Method: update
|
|
method_update() {
|
|
local output
|
|
output=$($JABBERCTL update 2>&1)
|
|
local rc=$?
|
|
|
|
json_init
|
|
if [ $rc -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Jabber/XMPP updated successfully"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "$output"
|
|
fi
|
|
json_dump
|
|
}
|
|
|
|
# Method: logs
|
|
method_logs() {
|
|
local lines="${1:-50}"
|
|
local output
|
|
|
|
if [ -d "/srv/lxc/jabber" ]; then
|
|
output=$($JABBERCTL logs "$lines" 2>&1 | tail -n "$lines")
|
|
else
|
|
output="Container not installed"
|
|
fi
|
|
|
|
json_init
|
|
json_add_string "logs" "$output"
|
|
json_dump
|
|
}
|
|
|
|
# Method: emancipate
|
|
method_emancipate() {
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var domain domain
|
|
|
|
if [ -z "$domain" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Domain is required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
local output
|
|
output=$($JABBERCTL emancipate "$domain" 2>&1)
|
|
local rc=$?
|
|
|
|
json_init
|
|
if [ $rc -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Jabber/XMPP emancipated to $domain"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "$output"
|
|
fi
|
|
json_dump
|
|
}
|
|
|
|
# Method: configure_haproxy
|
|
method_configure_haproxy() {
|
|
local output
|
|
output=$($JABBERCTL configure-haproxy 2>&1)
|
|
local rc=$?
|
|
|
|
json_init
|
|
if [ $rc -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "HAProxy configured for Jabber"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "$output"
|
|
fi
|
|
json_dump
|
|
}
|
|
|
|
# Method: user_add
|
|
method_user_add() {
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var jid jid
|
|
json_get_var password password
|
|
|
|
if [ -z "$jid" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "JID is required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
local output
|
|
if [ -n "$password" ]; then
|
|
output=$($JABBERCTL user add "$jid" "$password" 2>&1)
|
|
else
|
|
output=$($JABBERCTL user add "$jid" 2>&1)
|
|
fi
|
|
local rc=$?
|
|
|
|
json_init
|
|
if [ $rc -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "User $jid created"
|
|
# Extract password from output
|
|
local new_pass=$(echo "$output" | grep -oE 'Password: [^ ]+' | cut -d: -f2 | tr -d ' ')
|
|
json_add_string "password" "$new_pass"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "$output"
|
|
fi
|
|
json_dump
|
|
}
|
|
|
|
# Method: user_del
|
|
method_user_del() {
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var jid jid
|
|
|
|
if [ -z "$jid" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "JID is required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
local output
|
|
output=$($JABBERCTL user del "$jid" 2>&1)
|
|
local rc=$?
|
|
|
|
json_init
|
|
if [ $rc -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "User $jid deleted"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "$output"
|
|
fi
|
|
json_dump
|
|
}
|
|
|
|
# Method: user_passwd
|
|
method_user_passwd() {
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var jid jid
|
|
json_get_var password password
|
|
|
|
if [ -z "$jid" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "JID is required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
local output
|
|
if [ -n "$password" ]; then
|
|
output=$($JABBERCTL user passwd "$jid" "$password" 2>&1)
|
|
else
|
|
output=$($JABBERCTL user passwd "$jid" 2>&1)
|
|
fi
|
|
local rc=$?
|
|
|
|
json_init
|
|
if [ $rc -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Password changed for $jid"
|
|
local new_pass=$(echo "$output" | grep -oE 'New password: [^ ]+' | cut -d: -f2 | tr -d ' ')
|
|
json_add_string "password" "$new_pass"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "$output"
|
|
fi
|
|
json_dump
|
|
}
|
|
|
|
# Method: user_list
|
|
method_user_list() {
|
|
local users=""
|
|
|
|
if lxc-info -n jabber 2>/dev/null | grep -q "RUNNING"; then
|
|
users=$(lxc-attach -n jabber -- find /var/lib/prosody -name "*.dat" -path "*accounts*" 2>/dev/null | while read f; do
|
|
user=$(basename "$f" .dat)
|
|
domain=$(echo "$f" | grep -oE '[^/]+/accounts' | cut -d/ -f1 | tr '%' '.')
|
|
echo "${user}@${domain}"
|
|
done | paste -sd,)
|
|
fi
|
|
|
|
json_init
|
|
json_add_string "users" "$users"
|
|
json_dump
|
|
}
|
|
|
|
# Method: room_create
|
|
method_room_create() {
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var name name
|
|
|
|
if [ -z "$name" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Room name is required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
local output
|
|
output=$($JABBERCTL room create "$name" 2>&1)
|
|
local rc=$?
|
|
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Room $name available (created on first join)"
|
|
json_dump
|
|
}
|
|
|
|
# Method: room_delete
|
|
method_room_delete() {
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var name name
|
|
|
|
if [ -z "$name" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Room name is required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
local output
|
|
output=$($JABBERCTL room delete "$name" 2>&1)
|
|
local rc=$?
|
|
|
|
json_init
|
|
if [ $rc -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Room $name deleted"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "$output"
|
|
fi
|
|
json_dump
|
|
}
|
|
|
|
# Method: room_list
|
|
method_room_list() {
|
|
local rooms=""
|
|
|
|
if lxc-info -n jabber 2>/dev/null | grep -q "RUNNING"; then
|
|
rooms=$(lxc-attach -n jabber -- find /var/lib/prosody -name "*.dat" -path "*rooms*" 2>/dev/null | while read f; do
|
|
room=$(basename "$f" .dat)
|
|
echo "$room"
|
|
done | paste -sd,)
|
|
fi
|
|
|
|
json_init
|
|
json_add_string "rooms" "$rooms"
|
|
json_dump
|
|
}
|
|
|
|
# ---------- VoIP Integration Methods ----------
|
|
|
|
# Method: jingle_status
|
|
method_jingle_status() {
|
|
local enabled stun_server turn_server
|
|
|
|
enabled=$(uci_get jingle enabled 0)
|
|
stun_server=$(uci_get jingle stun_server "stun.l.google.com:19302")
|
|
turn_server=$(uci_get jingle turn_server "")
|
|
|
|
json_init
|
|
json_add_string "enabled" "$enabled"
|
|
json_add_string "stun_server" "$stun_server"
|
|
json_add_string "turn_server" "$turn_server"
|
|
json_dump
|
|
}
|
|
|
|
# Method: jingle_enable
|
|
method_jingle_enable() {
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var stun_server stun_server
|
|
|
|
local output
|
|
if [ -n "$stun_server" ]; then
|
|
output=$($JABBERCTL jingle enable "$stun_server" 2>&1)
|
|
else
|
|
output=$($JABBERCTL jingle enable 2>&1)
|
|
fi
|
|
local rc=$?
|
|
|
|
json_init
|
|
if [ $rc -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Jingle VoIP enabled"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "$output"
|
|
fi
|
|
json_dump
|
|
}
|
|
|
|
# Method: jingle_disable
|
|
method_jingle_disable() {
|
|
local output
|
|
output=$($JABBERCTL jingle disable 2>&1)
|
|
local rc=$?
|
|
|
|
json_init
|
|
if [ $rc -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Jingle VoIP disabled"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "$output"
|
|
fi
|
|
json_dump
|
|
}
|
|
|
|
# Method: sms_status
|
|
method_sms_status() {
|
|
local enabled sender provider
|
|
|
|
enabled=$(uci_get sms enabled 0)
|
|
sender=$(uci_get sms sender "SecuBox")
|
|
provider=$(uci_get sms provider "ovh")
|
|
|
|
# Check OVH API configured
|
|
local ovh_configured="0"
|
|
local ovh_key=$(uci -q get voip.ovh_telephony.app_key 2>/dev/null)
|
|
[ -n "$ovh_key" ] && ovh_configured="1"
|
|
|
|
json_init
|
|
json_add_string "enabled" "$enabled"
|
|
json_add_string "sender" "$sender"
|
|
json_add_string "provider" "$provider"
|
|
json_add_string "ovh_configured" "$ovh_configured"
|
|
json_dump
|
|
}
|
|
|
|
# Method: sms_config
|
|
method_sms_config() {
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var sender sender
|
|
|
|
local output
|
|
output=$($JABBERCTL sms config "$sender" 2>&1)
|
|
local rc=$?
|
|
|
|
json_init
|
|
if [ $rc -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "SMS relay configured"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "$output"
|
|
fi
|
|
json_dump
|
|
}
|
|
|
|
# Method: sms_send
|
|
method_sms_send() {
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var to to
|
|
json_get_var message message
|
|
|
|
if [ -z "$to" ] || [ -z "$message" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Phone number and message are required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
local output
|
|
output=$($JABBERCTL sms send "$to" "$message" 2>&1)
|
|
local rc=$?
|
|
|
|
json_init
|
|
if [ $rc -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "SMS sent to $to"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "$output"
|
|
fi
|
|
json_dump
|
|
}
|
|
|
|
# Method: voicemail_status
|
|
method_voicemail_status() {
|
|
local enabled ami_host ami_port notify_jid
|
|
|
|
enabled=$(uci_get voicemail enabled 0)
|
|
ami_host=$(uci_get voicemail ami_host "127.0.0.1")
|
|
ami_port=$(uci_get voicemail ami_port "5038")
|
|
notify_jid=$(uci_get voicemail notify_jid "")
|
|
|
|
json_init
|
|
json_add_string "enabled" "$enabled"
|
|
json_add_string "ami_host" "$ami_host"
|
|
json_add_string "ami_port" "$ami_port"
|
|
json_add_string "notify_jid" "$notify_jid"
|
|
json_dump
|
|
}
|
|
|
|
# Method: voicemail_config
|
|
method_voicemail_config() {
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var notify_jid notify_jid
|
|
|
|
if [ -z "$notify_jid" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Notification JID is required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
# Save to UCI first
|
|
uci set jabber.voicemail=voicemail_notify
|
|
uci set jabber.voicemail.notify_jid="$notify_jid"
|
|
uci commit jabber
|
|
|
|
local output
|
|
output=$($JABBERCTL voicemail-notify 2>&1)
|
|
local rc=$?
|
|
|
|
json_init
|
|
if [ $rc -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Voicemail notifications configured"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "$output"
|
|
fi
|
|
json_dump
|
|
}
|
|
|
|
# List available methods
|
|
list_methods() {
|
|
json_init
|
|
json_add_object "status"
|
|
json_close_object
|
|
json_add_object "start"
|
|
json_close_object
|
|
json_add_object "stop"
|
|
json_close_object
|
|
json_add_object "install"
|
|
json_close_object
|
|
json_add_object "uninstall"
|
|
json_close_object
|
|
json_add_object "update"
|
|
json_close_object
|
|
json_add_object "logs"
|
|
json_add_int "lines" 50
|
|
json_close_object
|
|
json_add_object "emancipate"
|
|
json_add_string "domain" ""
|
|
json_close_object
|
|
json_add_object "configure_haproxy"
|
|
json_close_object
|
|
json_add_object "user_add"
|
|
json_add_string "jid" ""
|
|
json_add_string "password" ""
|
|
json_close_object
|
|
json_add_object "user_del"
|
|
json_add_string "jid" ""
|
|
json_close_object
|
|
json_add_object "user_passwd"
|
|
json_add_string "jid" ""
|
|
json_add_string "password" ""
|
|
json_close_object
|
|
json_add_object "user_list"
|
|
json_close_object
|
|
json_add_object "room_create"
|
|
json_add_string "name" ""
|
|
json_close_object
|
|
json_add_object "room_delete"
|
|
json_add_string "name" ""
|
|
json_close_object
|
|
json_add_object "room_list"
|
|
json_close_object
|
|
json_add_object "jingle_status"
|
|
json_close_object
|
|
json_add_object "jingle_enable"
|
|
json_add_string "stun_server" ""
|
|
json_close_object
|
|
json_add_object "jingle_disable"
|
|
json_close_object
|
|
json_add_object "sms_status"
|
|
json_close_object
|
|
json_add_object "sms_config"
|
|
json_add_string "sender" ""
|
|
json_close_object
|
|
json_add_object "sms_send"
|
|
json_add_string "to" ""
|
|
json_add_string "message" ""
|
|
json_close_object
|
|
json_add_object "voicemail_status"
|
|
json_close_object
|
|
json_add_object "voicemail_config"
|
|
json_add_string "notify_jid" ""
|
|
json_close_object
|
|
json_dump
|
|
}
|
|
|
|
# Main dispatcher
|
|
case "$1" in
|
|
list)
|
|
list_methods
|
|
;;
|
|
call)
|
|
case "$2" in
|
|
status)
|
|
method_status
|
|
;;
|
|
start)
|
|
method_start
|
|
;;
|
|
stop)
|
|
method_stop
|
|
;;
|
|
install)
|
|
method_install
|
|
;;
|
|
uninstall)
|
|
method_uninstall
|
|
;;
|
|
update)
|
|
method_update
|
|
;;
|
|
logs)
|
|
read -r input
|
|
json_load "$input"
|
|
json_get_var lines lines
|
|
method_logs "${lines:-50}"
|
|
;;
|
|
emancipate)
|
|
method_emancipate
|
|
;;
|
|
configure_haproxy)
|
|
method_configure_haproxy
|
|
;;
|
|
user_add)
|
|
method_user_add
|
|
;;
|
|
user_del)
|
|
method_user_del
|
|
;;
|
|
user_passwd)
|
|
method_user_passwd
|
|
;;
|
|
user_list)
|
|
method_user_list
|
|
;;
|
|
room_create)
|
|
method_room_create
|
|
;;
|
|
room_delete)
|
|
method_room_delete
|
|
;;
|
|
room_list)
|
|
method_room_list
|
|
;;
|
|
jingle_status)
|
|
method_jingle_status
|
|
;;
|
|
jingle_enable)
|
|
method_jingle_enable
|
|
;;
|
|
jingle_disable)
|
|
method_jingle_disable
|
|
;;
|
|
sms_status)
|
|
method_sms_status
|
|
;;
|
|
sms_config)
|
|
method_sms_config
|
|
;;
|
|
sms_send)
|
|
method_sms_send
|
|
;;
|
|
voicemail_status)
|
|
method_voicemail_status
|
|
;;
|
|
voicemail_config)
|
|
method_voicemail_config
|
|
;;
|
|
*)
|
|
echo '{"error":"Method not found"}'
|
|
;;
|
|
esac
|
|
;;
|
|
*)
|
|
echo '{"error":"Invalid action"}'
|
|
;;
|
|
esac
|