diff options
Diffstat (limited to 'kamailio-model.lua')
-rw-r--r-- | kamailio-model.lua | 338 |
1 files changed, 317 insertions, 21 deletions
diff --git a/kamailio-model.lua b/kamailio-model.lua index 8d4ec97..63be94a 100644 --- a/kamailio-model.lua +++ b/kamailio-model.lua @@ -11,8 +11,120 @@ require("validator") local processname = "kamailio" local packagename = "kamailio" local baseurl = "/etc/kamailio" +local kamctlrc_file = "/etc/kamailio/kamctlrc" local path = "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin " +local env +local con +local DBENGINE + +-- ################################################################################ +-- DATABASE FUNCTIONS + +local function assert (v, m) + if not v then + m = m or "Assertion failed!" + error(m, 0) + end + return v, m +end + +-- Escape special characters in sql statements +local escape = function(sql) + sql = sql or "" + sql = string.gsub(sql, "'", "''") + return string.gsub(sql, "\\", "\\\\") +end + +local databaseconnect = function() + if not con then + -- parse the kamctlrc file + local config = format.parse_ini_file(fs.read_file(kamctlrc_file), "") or {} + if not config.DBENGINE then + error("Database engine not specified, please setup one in the config script "..kamctlrc_file) + end + + -- create environment object + if config.DBENGINE == "MYSQL" or config.DBENGINE == "mysql" or config.DBENGINE == "MySQL" then + error("MYSQL database not supported") + elseif config.DBENGINE == "PGSQL" or config.DBENGINE == "pgsql" or config.DBENGINE == "postgres" or config.DBENGINE == "postgresql" or config.DBENGINE == "POSTGRESQL" then + require("luasql.postgres") + env = assert (luasql.postgres()) + DBENGINE = "PGSQL" + elseif config.DBENGINE == "ORACLE" or config.DBENGINE == "oracle" or config.DBENGINE == "Oracle" then + error("ORACLE database not supported") + elseif config.DBENGINE == "DBTEXT" or config.DBENGINE == "dbtext" or config.DBENGINE == "textdb" then + error("DBTEXT database not supported") + elseif config.DBENGINE == "DB_BERKELEY" or config.DBENGINE == "db_berkeley" or config.DBENGINE == "BERKELEY" or config.DBENGINE == "berkeley" then + error("BERKELEY database not supported") + else + error("Unknown database engine "..config.DBENGINE) + end + + -- connect to data source + con = assert(env:connect(config.DBNAME or "", config.DBRWUSER or "", config.DBRWPW or "")) + return true + end + return false +end + +local databasedisconnect = function() + if env then + env:close() + env = nil + end + if con then + con:close() + con = nil + end +end + +local runsqlcommand = function(sql) +logevent(sql) + assert(con:execute(sql)) +end + +local getselectresponse = function(sql) + local retval = {} +logevent(sql) + local cur = assert (con:execute(sql)) + local row = cur:fetch ({}, "a") + while row do + local tmp = {} + for name,val in pairs(row) do + tmp[name] = val + end + retval[#retval + 1] = tmp + row = cur:fetch (row, "a") + end + cur:close() + return retval +end + +local listtables = function() + local result = {} + if DBENGINE == "PGSQL" then + local tab = getselectresponse("SELECT tablename FROM pg_tables WHERE tablename !~* 'pg_*' ORDER BY tablename ASC") + for i,t in ipairs(tab) do + result[#result+1] = t.tablename + end + else + -- untested + result = con:tables() + end + return result +end + +local listcolumns = function(table) + local result = {} + if DBENGINE == "PGSQL" then + local col = getselectresponse("SELECT a.attname AS field FROM pg_class c, pg_attribute a, pg_type t WHERE c.relname = '"..table.."' AND a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid ORDER BY a.attnum") + for i,c in ipairs(col) do + result[#result+1] = c.field + end + end + return result +end -- ################################################################################ -- LOCAL FUNCTIONS @@ -71,36 +183,47 @@ function list_files() return cfe({ type="structure", value=retval, label="List of Kamailio files" }) end -function list_users() - -- Database format: id | username | domain | password | email_address | ha1 | ha1b | rpid - local cmd = path .. "kamctl db show subscriber" +local function parse_db_show(table) + local cmd = path .. "kamctl db show "..(table or "") local f = io.popen(cmd) - -- These settings work for Postgres database - local skiplines = 2 - local delimiter = "%s*|%s*" - local dbengine = format.parse_ini_file(fs.read_file("/etc/kamailio/kamctlrc") or "", "", "DBENGINE") - if dbengine == "DBTEXT" then - skiplines = 0 - delimiter = "\'?%s*,%s*\'?" - end - + -- These settings work for Postgres and DBTEXT database + local delimiter = "\'?%s*[,|]%s*\'?" local results = {} + local errtxt for line in f:lines() do - if skiplines > 0 then - skiplines = skiplines-1 + if #results == 0 and string.match(line, "^ERROR:") then + errtxt = line + results = nil + break + end + if string.match(line, "^[+-]+$") then + results = {} else local words = format.string_to_table(line, delimiter) - if #words > 1 then - local temp = {username = words[2], - --domain = words[3], - password = words[4], - --email_address = words[5] - } - results[#results+1] = temp + if words and #words > 0 then + results[#results+1] = words end end end f:close() + return results, errtxt +end + +function list_users() + -- Database format: id | username | domain | password | email_address | ha1 | ha1b | rpid + local results = {} + local r, errtxt + r, errtxt = parse_db_show("subscriber") + for i,words in ipairs(r or {}) do + if #words > 1 then + local temp = {username = words[2], + --domain = words[3], + password = words[4], + --email_address = words[5] + } + results[#results+1] = temp + end + end table.sort(results, function(a,b) return a.username < b.username end) return cfe({type="list", value=results, label="Kamailio Users"}) end @@ -166,3 +289,176 @@ function update_user(user) return user end + +function list_tables() + local retval = {} + local errtxt + -- Get the devices from the DB + local res, err = pcall(function() + local connected = databaseconnect() + retval = listtables() + if connected then databasedisconnect() end + end) + if not res and err then + errtxt = err + end + + return cfe({ type="list", value=retval, label="List of Database Tables", errtxt=errtxt }) +end + +function list_table_entries(table) + local retval = {} + retval.table = cfe({ value=table or "", label="Table" }) + retval.fields = cfe({ type="list", value={}, label="List of Table Fields" }) + retval.entries = cfe({ type="structure", value={}, label="List of Database Entries" }) + local errtxt + -- Get the devices from the DB + local res, err = pcall(function() + local connected = databaseconnect() + retval.entries.value = getselectresponse("SELECT * FROM "..table.." ORDER BY id ASC") or {} + retval.fields.value = listcolumns(table) or {} + if connected then databasedisconnect() end + end) + if not res and err then + errtxt = err + end + + return cfe({ type="group", value=retval, label="Database Table Entries", errtxt=errtxt }) +end + +function get_table_entry(table, id) + local retval = {} + retval.table = cfe({ value=table or "", label="Table", errtxt="Table does not exist" }) + local errtxt = "Table does not exist" + if table and table ~= "" then + local res, err = pcall(function() + local connected = databaseconnect() + local tables = listtables() + for i,t in ipairs(tables) do + if t == table then + retval.table.errtxt = nil + errtxt = nil + break + end + end + if not errtxt then + local fields = listcolumns(table) + for i,f in ipairs(fields) do + retval[f] = cfe({ label=f, seq=i }) + end + if id and id ~= "" then + local entry = getselectresponse("SELECT * FROM "..table.." WHERE id='"..escape(id).."'") + if entry and #entry > 0 then + for n,v in pairs(entry[1]) do + if retval[n] then retval[n].value = v end + end + end + end + end + if connected then databasedisconnect() end + end) + if not res and err then + errtxt = err + end + end + + return cfe({ type="group", value=retval, label="Database Table Entry", errtxt=errtxt }) +end + +function create_table_entry(entry) + return update_table_entry(entry, true) +end + +function update_table_entry(entry, create) + local success = true + local errtxt + -- Validate the settings + -- relying on get_table_entry to do the validation of table + if entry.value.table.value == "" or entry.value.table.errtxt then + success = false + entry.value.table.errtxt = "Table does not exist" + end + if not create then + if not entry.value.id then + success = false + elseif not entry.value.id.value or entry.value.id.value == "" then + success = false + entry.value.id.errtxt = "Invalid id" + end + end + if success then + local res, err = pcall(function() + local connected = databaseconnect() + if success and not create then + local sql = "SELECT * FROM "..entry.value.table.value.." WHERE id='"..escape(entry.value.id.value).."'" + local tmp = getselectresponse(sql) + if not tmp or #tmp == 0 then + success = false + errtxt = "Entry does not exist" + end + end + if success then + local names = {} + local values = {} + for n,v in pairs(entry.value) do + if n ~= "table" and n ~= "id" then + names[#names+1] = n + values[#values+1] = escape(v.value) + end + end + if create then + sql = "INSERT INTO "..entry.value.table.value.." ("..table.concat(names, ", ")..") VALUES('"..table.concat(values, "', '").."')" + else + sql = "UPDATE "..entry.value.table.value.." SET ("..table.concat(names, ", ")..") = ('"..table.concat(values, "', '").."') WHERE id='"..escape(entry.value.id.value).."'" + end + runsqlcommand(sql) + end + if connected then databasedisconnect() end + end) + if not res and err then + success = false + errtxt = err + end + end + if not success then + if create then + entry.errtxt = errtxt or "Failed to create entry" + else + entry.errtxt = errtxt or "Failed to save entry" + end + end + return entry +end + +function delete_table_entry(table, id) + local result = "" + local errtxt + if not table or table == "" then + errtxt = "Invalid table" + elseif not id or id == "" then + errtxt = "Invalid entry" + else + local res, err = pcall(function() + local connected = databaseconnect() + errtxt = "Invalid table" + local tables = listtables() + for i,t in ipairs(tables) do + if t == table then + errtxt = nil + break + end + end + if not errtxt then + local sql = "DELETE FROM "..table.." WHERE id='"..escape(id).."'" + runsqlcommand(sql) + result = "Entry Deleted" + end + if connected then databasedisconnect() end + end) + if not res and err then + errtxt = err + end + end + + return cfe({ value=result, errtxt=errtxt, label="Delete Entry Result" }) +end |