diff options
-rw-r--r-- | Makefile | 44 | ||||
-rw-r--r-- | README | 1 | ||||
-rw-r--r-- | config.mk | 10 | ||||
-rw-r--r-- | kamailio-controller.lua | 83 | ||||
-rw-r--r-- | kamailio-createtableentry-html.lsp | 14 | ||||
-rw-r--r-- | kamailio-createuser-html.lsp | 15 | ||||
l--------- | kamailio-edit-html.lsp | 1 | ||||
-rw-r--r-- | kamailio-listfiles-html.lsp | 24 | ||||
-rw-r--r-- | kamailio-listtables-html.lsp | 27 | ||||
-rw-r--r-- | kamailio-listusers-html.lsp | 43 | ||||
-rw-r--r-- | kamailio-model.lua | 588 | ||||
-rw-r--r-- | kamailio-searchdatabase-html.lsp | 28 | ||||
l--------- | kamailio-startstop-html.lsp | 1 | ||||
-rw-r--r-- | kamailio-status-html.lsp | 46 | ||||
-rw-r--r-- | kamailio-updatetableentry-html.lsp | 14 | ||||
-rw-r--r-- | kamailio-updateuser-html.lsp | 15 | ||||
-rw-r--r-- | kamailio-viewtable-html.lsp | 54 | ||||
-rw-r--r-- | kamailio.menu | 7 | ||||
-rw-r--r-- | kamailio.roles | 4 |
19 files changed, 1019 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..80e34f1 --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +APP_NAME=kamailio +PACKAGE=acf-$(APP_NAME) +VERSION=0.4.3 + +APP_DIST=\ + kamailio* \ + + +EXTRA_DIST=README Makefile config.mk + +DISTFILES=$(APP_DIST) $(EXTRA_DIST) + +TAR=tar + +P=$(PACKAGE)-$(VERSION) +tarball=$(P).tar.bz2 +install_dir=$(DESTDIR)/$(appdir)/$(APP_NAME) + +all: +clean: + rm -rf $(tarball) $(P) + +dist: $(tarball) + +install: + mkdir -p "$(install_dir)" + cp -a $(APP_DIST) "$(install_dir)" + +$(tarball): $(DISTFILES) + rm -rf $(P) + mkdir -p $(P) + cp -a $(DISTFILES) $(P) + $(TAR) -jcf $@ $(P) + rm -rf $(P) + +# target that creates a tar package, unpacks is and install from package +dist-install: $(tarball) + $(TAR) -jxf $(tarball) + $(MAKE) -C $(P) install DESTDIR=$(DESTDIR) + rm -rf $(P) + +include config.mk + +.PHONY: all clean dist install dist-install @@ -0,0 +1 @@ +ACF for kamailio diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..45f4d21 --- /dev/null +++ b/config.mk @@ -0,0 +1,10 @@ +prefix=/usr +datadir=${prefix}/share +sysconfdir=${prefix}/etc +localstatedir=${prefix}/var +acfdir=${datadir}/acf +wwwdir=${acfdir}/www +cgibindir=${acfdir}/cgi-bin +appdir=${acfdir}/app +acflibdir=${acfdir}/lib +sessionsdir=${localstatedir}/lib/acf/sessions diff --git a/kamailio-controller.lua b/kamailio-controller.lua new file mode 100644 index 0000000..7a98ae3 --- /dev/null +++ b/kamailio-controller.lua @@ -0,0 +1,83 @@ +module(..., package.seeall) + +-- Load libraries +require("controllerfunctions") + +default_action = "status" + +function status(self) + return self.model.getstatus() +end + +function startstop(self) + return controllerfunctions.handle_startstop(self, self.model.startstop_service, self.clientdata) +end + +function listfiles(self) + return self.model.list_files() +end + +function edit(self) + return controllerfunctions.handle_form(self, function() return self.model.get_filedetails(self.clientdata.filename) end, self.model.update_filedetails, self.clientdata, "Save", "Edit File", "File Saved") +end + +function listusers(self) + return self.model.list_users() +end + +function createuser(self) + return controllerfunctions.handle_form(self, self.model.get_new_user, self.model.create_new_user, self.clientdata, "Create", "Create New User") +end + +function deleteuser(self) + return self:redirect_to_referrer(self.model.delete_user(self.clientdata.username)) +end + +function updateuser(self) + return controllerfunctions.handle_form(self, function() return self.model.get_user(self.clientdata.username) end, self.model.update_user, self.clientdata, "Update", "Update User") +end + +function listtables(self) + return self.model.list_tables() +end + +function viewtable(self) + local req_table + if self.clientdata.schema then + req_table = self.clientdata.schema.."."..self.clientdata.table + else + req_table = self.clientdata.table + end + return self.model.list_table_entries(req_table) +end + +function deletetableentry(self) + return self:redirect_to_referrer(self.model.delete_table_entry(self.clientdata.table, self.clientdata.id)) +end + +function updatetableentry(self) + return controllerfunctions.handle_form(self, function() return self.model.get_table_entry(self.clientdata.table, self.clientdata.id) end, self.model.update_table_entry, self.clientdata, "Update", "Update Table Entry", "Entry updated") +end + +function createtableentry(self) + local req_table + if self.clientdata.schema then + req_table = self.clientdata.schema.."."..self.clientdata.table + else + req_table = self.clientdata.table + end + return controllerfunctions.handle_form(self, function() return self.model.get_table_entry(req_table) end, self.model.create_table_entry, self.clientdata, "Create", "Create New Table Entry", "Entry created") +end + +function createdatabase(self) + return self:redirect_to_referrer(self.model.create_database()) +end + +function reloadplan(self) + return self:redirect_to_referrer(self.model.reloadplan()) +end + + +searchdatabase = function( self ) + return self.model.search_database(self.clientdata.id, self.clientdata.value, self.clientdata.comparison) +end diff --git a/kamailio-createtableentry-html.lsp b/kamailio-createtableentry-html.lsp new file mode 100644 index 0000000..14ffd0a --- /dev/null +++ b/kamailio-createtableentry-html.lsp @@ -0,0 +1,14 @@ +<% local form, viewlibrary, page_info = ... +require("viewfunctions") +%> + +<H1><%= html.html_escape(form.label) %></H1> +<% + form.value.table.readonly = true + if page_info.action == "updatetableentry" and form.value.id then + form.value.id.readonly = true + elseif form.value.id then + form.value.id.type = "hidden" + end + displayform(form, nil, nil, page_info, 2) +%> diff --git a/kamailio-createuser-html.lsp b/kamailio-createuser-html.lsp new file mode 100644 index 0000000..fb05750 --- /dev/null +++ b/kamailio-createuser-html.lsp @@ -0,0 +1,15 @@ +<% local form, viewlibrary, page_info = ... +require("viewfunctions") +%> + +<H1><%= html.html_escape(form.label) %></H1> +<% + form.action = page_info.script .. page_info.prefix .. page_info.controller .. "/" .. page_info.action + form.value.password.type = "password" + form.value.password_confirm.type = "password" + if page_info.action == "updateuser" then + form.value.username.readonly = true + end + local order = {"username", "password", "password_confirm", "email_address"} + displayform(form, order) +%> diff --git a/kamailio-edit-html.lsp b/kamailio-edit-html.lsp new file mode 120000 index 0000000..15b1930 --- /dev/null +++ b/kamailio-edit-html.lsp @@ -0,0 +1 @@ +../filedetails-html.lsp
\ No newline at end of file diff --git a/kamailio-listfiles-html.lsp b/kamailio-listfiles-html.lsp new file mode 100644 index 0000000..93a00fc --- /dev/null +++ b/kamailio-listfiles-html.lsp @@ -0,0 +1,24 @@ +<% local view, viewlibrary, page_info, session = ... +require("viewfunctions") +%> + +<% displaycommandresults({"edit"}, session) %> + +<% if viewlibrary and viewlibrary.dispatch_component then + viewlibrary.dispatch_component("status") +end %> + +<h1>Configuration</h1> +<DL><TABLE> + <TR style="background:#eee;font-weight:bold;"> + <TD style="padding-right:20px;white-space:nowrap;text-align:left;" class="header">File</TD> + <TD style="padding-right:20px;white-space:nowrap;text-align:left;" class="header">Size</TD> + <TD style="white-space:nowrap;text-align:left;" class="header">Last Modified</TD> + </TR> + +<% + for k,v in ipairs( view.value ) do + io.write( "<tr><td><a href=\"" .. html.html_escape(page_info.script .. page_info.prefix .. page_info.controller) .. "/edit?filename=" .. html.html_escape(v.filename) .. "&redir=" .. html.html_escape(page_info.orig_action) .. "\">" .. html.html_escape(v.filename) .. "</a></td><td>" .. html.html_escape(v.size) .."</td><td>" .. html.html_escape(v.mtime) .."</td></tr>\n" ) + end +%> +</TABLE></DL> diff --git a/kamailio-listtables-html.lsp b/kamailio-listtables-html.lsp new file mode 100644 index 0000000..38dd4f0 --- /dev/null +++ b/kamailio-listtables-html.lsp @@ -0,0 +1,27 @@ +<% local form, viewlibrary, page_info, session = ... %> +<% require("viewfunctions") %> + +<% displaycommandresults({"createdatabase"}, session) %> + +<H1><%= html.html_escape(form.label) %></H1> +<DL> +<% for i,v in ipairs(form.value) do %> + <li> + <% if viewlibrary.check_permission("viewtable") then %> + <% if v.schema ~= "public" then %> + <%= html.link{value = "viewtable?table=" .. v.table .. "&schema=" .. v.schema, label="("..v.schema..") "..v.table} %> + <% else %> + <%= html.link{value = "viewtable?table=" .. v.table, label=v.table} %> + <% end %> + <% else %> + <%= html.html_escape("("..v.schema..") "..v.table) %> + <% end %> +<% end %> +<% if #form.value == 0 and viewlibrary.check_permission("createdatabase") then %> +<DT>Create Database</DT><DD> +<form action="<%= html.html_escape(page_info.script .. page_info.prefix .. page_info.controller .. "/createdatabase") %>"> +<input class="submit" type="submit" value="Create"></DD> +</form> +</DD> +<% end %> +</DL> diff --git a/kamailio-listusers-html.lsp b/kamailio-listusers-html.lsp new file mode 100644 index 0000000..5994b79 --- /dev/null +++ b/kamailio-listusers-html.lsp @@ -0,0 +1,43 @@ +<% local form, viewlibrary, page_info, session = ... %> +<% require("viewfunctions") %> + +<% displaycommandresults({"deleteuser", "updateuser"}, session) %> +<% displaycommandresults({"createuser"}, session, true) %> + +<H1><%= html.html_escape(form.label) %></H1> +<DL> +<TABLE> + <TR style="background:#eee;font-weight:bold;"> + <% if viewlibrary.check_permission("deleteuser") or viewlibrary.check_permission("updateuser") then %> + <TD style="padding-right:20px;white-space:nowrap;" class="header">Action</TD> + <% end %> + <TD style="padding-right:20px;white-space:nowrap;" class="header">User Name</TD> + <TD style="white-space:nowrap;" class="header">Password</TD> + </TR> + +<% for i,user in ipairs(form.value) do %> + <TR> + <% if viewlibrary.check_permission("deleteuser") or viewlibrary.check_permission("updateuser") then %> + <TD style="padding-right:20px;white-space:nowrap;"> + <% if viewlibrary.check_permission("updateuser") then %> + <%= html.link{value = "updateuser?username=" .. user.username.."&redir="..page_info.orig_action, label="Update "} %> + <% end %> + <% if viewlibrary.check_permission("deleteuser") then %> + <%= html.link{value = "deleteuser?username=" .. user.username, label="Delete "} %> + <% end %> + </TD> + <% end %> + <TD><%= html.html_escape(user.username) %></TD> + <% if viewlibrary.check_permission("updateuser") then %> + <TD><%= html.html_escape(user.password) %></TD> + <% else %> + <TD>******</TD> + <% end %> + </TR> +<% end %> +</TABLE> +</DL> + +<% if viewlibrary and viewlibrary.dispatch_component and viewlibrary.check_permission("createuser") then + viewlibrary.dispatch_component("createuser") +end %> diff --git a/kamailio-model.lua b/kamailio-model.lua new file mode 100644 index 0000000..ed05f85 --- /dev/null +++ b/kamailio-model.lua @@ -0,0 +1,588 @@ +module(..., package.seeall) + +-- Load libraries +require("posix") +require("modelfunctions") +require("fs") +require("format") +require("validator") + +-- Set variables +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 "" + 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 "", config.DBHOST, config.DBPORT)) + 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, schemaname FROM pg_tables WHERE tablename !~* 'pg_*' ORDER BY schemaname, tablename ASC") + for i,t in ipairs(tab) do + result[#result+1] = {schema=t.schemaname, table=t.tablename} + end + else + -- untested + result = con:tables() + end + return result +end + +local listcolumns = function(table, schema) + local result = {} + if DBENGINE == "PGSQL" then + local col + if schema then + col = getselectresponse("SELECT column_name AS field FROM information_schema.columns WHERE table_schema='"..schema.."' AND table_name='"..table.."' ORDER BY ordinal_position") + else + col = getselectresponse("SELECT column_name AS field FROM information_schema.columns WHERE table_name='"..table.."' ORDER BY ordinal_position") + end + + for i,c in ipairs(col) do + result[#result+1] = c.field + end + end + return result +end + +-- ################################################################################ +-- LOCAL FUNCTIONS + +local is_valid_filename = function(filename) + local dirname = posix.dirname(filename) + return validator.is_valid_filename(filename) and string.match(dirname, baseurl) and not string.match(dirname, "%.%.") +end + +local function validate_user(user) + local success = true + if user.value.username.value == "" then + user.value.username.errtxt = "Cannot be empty" + success = false + end + if user.value.password.value == "" then + user.value.password.errtxt = "Cannot be empty" + success = false + end + if user.value.password.value ~= user.value.password_confirm.value then + user.value.password_confirm.errtxt = "Must match password" + success = false + end + return success, user +end + +-- ################################################################################ +-- PUBLIC FUNCTIONS + +function startstop_service(action) + return modelfunctions.startstop_service(processname, action) +end + +function getstatus() + return modelfunctions.getstatus(processname, packagename, "Kamailio Status") +end + +function get_filedetails(filename) + return modelfunctions.getfiledetails(filename, is_valid_filename) +end + +function update_filedetails(filedetails) + return modelfunctions.setfiledetails(filedetails, is_valid_filename) +end + +function reloadplan() + local cmd = path .. " sercmd mi_dg dp_reload" + local f = io.popen(cmd) + local result = f:read("*a") + f:close() + return cfe({value=result, label="Reloading Dial Plan Result"}) +end + +function list_files() + local retval = {} + for file in fs.find(null, baseurl) do + local details = fs.stat(file) + if details.type == "regular" then + details.filename = file + table.insert(retval, details) + end + end + table.sort(retval, function(a,b) return a.filename < b.filename end) + return cfe({ type="structure", value=retval, label="List of Kamailio files" }) +end + +local function parse_db_show(table) + local cmd = path .. "kamctl db show "..(table or "") + local f = io.popen(cmd) + -- These settings work for Postgres and DBTEXT database + local delimiter = "\'?%s*[,|]%s*\'?" + local results = {} + local errtxt + for line in f:lines() do + 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 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 + +function get_new_user() + local user = {} + user.username = cfe({label="User Name"}) + user.password = cfe({label="Password"}) + user.password_confirm = cfe({label="Password (confirm)"}) + --user.email_address = cfe({label="E-mail Address"}) + return cfe({type="group", value=user, label="Kamailio User"}) +end + +function create_new_user(user) + local success = validate_user(user) + if success then + local cmd = path .. "kamctl add "..format.escapespecialcharacters(user.value.username.value).." "..format.escapespecialcharacters(user.value.password.value) + --if user.value.email_address.value ~= "" then + -- cmd = cmd.." "..format.escapespecialcharacters(user.value.email_address.value) + --end + local f = io.popen(cmd) + user.descr = f:read("*a") + f:close() + else + user.errtxt = "Failed to create new user" + end + + return user +end + +function delete_user(username) + local cmd = path .. "kamctl rm "..format.escapespecialcharacters(username) + local f = io.popen(cmd) + local result = f:read("*a") + f:close() + return cfe({value=result, label="Delete User Result"}) +end + +function get_user(username) + local user = get_new_user() + user.value.username.value = username + user.value.username.errtxt = "Invalid user" + local users = list_users() + for i,u in ipairs(users.value) do + if u.username == username then + user.value.username.errtxt = nil + break + end + end + return user +end + +function update_user(user) + local success = validate_user(user) + if success then + local cmd = path .. "kamctl passwd "..format.escapespecialcharacters(user.value.username.value).." "..format.escapespecialcharacters(user.value.password.value) + local f = io.popen(cmd) + user.descr = f:read("*a") + f:close() + else + user.errtxt = "Failed to update user" + end + + 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(req_table) + local retval = {} + -- split up the schema from the table + local schema, table + schema, table = string.match(req_table, "(.+)%.(.+)") + -- if there's no schema with it then grab the req_table value + if not schema then + table = req_table + end + retval.table = cfe({ value=req_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() + local tables = listtables() + retval.table.errtxt = "Table does not exist" + errtxt = "Table does not exist" + for i,t in ipairs(tables) do + if t.table == table then + retval.table.errtxt = nil + errtxt = nil + -- if there's a schema, include it in the select statement + if schema then + retval.entries.value = getselectresponse("SELECT * FROM "..schema.."."..table) or {} + else + retval.entries.value = getselectresponse("SELECT * FROM "..table) or {} + end + retval.fields.value = listcolumns(table, schema) or {} + end + end + 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(req_table, id) + local retval = {} + -- split up the schema from the table + local schema, table + schema, table = string.match(req_table, "(.+)%.(.+)") + -- if there's no schema with it then grab the req_table value + if not schema then + table = req_table + end + retval.table = cfe({ value=req_table or "", label="Table", errtxt="Table does not exist", seq=0 }) + local errtxt = "Table does not exist" + if req_table and req_table ~= "" then + local res, err = pcall(function() + local connected = databaseconnect() + local tables = listtables() + for i,t in ipairs(tables) do + if t.table == table then + retval.table.errtxt = nil + errtxt = nil + break + end + end + if not errtxt then + local fields = listcolumns(table, schema) + for i,f in ipairs(fields) do + retval[f] = cfe({ label=f, seq=i }) + end + if id and id ~= "" then + local entry + if schema then + entry = getselectresponse("SELECT * FROM "..schema.."."..table.." WHERE id='"..escape(id).."'") + else + entry = getselectresponse("SELECT * FROM "..table.." WHERE id='"..escape(id).."'") + end + 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() + local tables = listtables() + local schema, tablename + schema, tablename = string.match(entry.value.table.value, "(.+)%.(.+)") + success = false + if not schema then + tablename = entry.value.table.value + end + entry.value.table.errtxt = "Table does not exist" + for i,t in ipairs(tables) do + if t.table == tablename then + success = true + entry.value.table.errtxt = nil + break + end + end + 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 + --- !!! HACK !!! --- + --- Fix for DISTributary Phone System Tool: ASHP + --- + --- Need to allow for insertion of NULL rather than + --- simply '' so that integers and booleans can be + --- added with empty values. + ---- ----------- --- + if n == "role_id" or n == "seq" then + if v.value == "" then + v.value = "0" + end + end + 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(req_table, id) + local result = "" + local errtxt + -- split up the schema from the table + local schema, table + schema, table = string.match(req_table, "(.+)%.(.+)") + -- if there's no schema with it then grab the req_table value + if not schema then + table = req_table + end + if not req_table or req_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 == table then + errtxt = nil + break + end + end + if not errtxt then + local sql + if schema then + sql = "DELETE FROM "..schema.."."..table.." WHERE id='"..escape(id).."'" + else + sql = "DELETE FROM "..table.." WHERE id='"..escape(id).."'" + end + 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 + +function create_database() + local cmd = path.."echo -e 'y\ny\n' | "..path.."kamdbctl create 2>&1" + local f = io.popen(cmd) + local result = f:read("*a") + return cfe({ value=result, label="Create database result" }) +end + +function search_database(id, value, comparison) + local errtxt + retval = {} + retval.id = cfe({type="select", value=id or "", label="Table.Column", option={}, seq=1}) + retval.comparison = cfe({type="select", value=comparison or "=", label="Comparison", option={"=", "!=", "~", "!~", "~*", "!*~"}, seq=2}) + retval.value = cfe({label="Value", value=value or "", descr="Value or SQL regular expression", seq=3}) + local res, err = pcall(function() + local connected = databaseconnect() + local tables = listtables() or {} + for i,t in ipairs(tables) do + local columns = listcolumns(t.table) or {} + for i,c in ipairs(columns) do + retval.id.option[#retval.id.option + 1] = t.table.."."..c + end + end + -- Get the rows from the DB + if id and modelfunctions.validateselect(retval.id) and modelfunctions.validateselect(retval.comparison) then + retval.result = cfe({type="structure", value={}, label="List of Rows", seq=4 }) + local table, column = string.match(id, "^([^.]*)%.(.*)") + if table then + local sql = "SELECT * FROM "..table.." WHERE "..column..comparison.."'"..value.."'" + retval.result.value = getselectresponse(sql) + end + end + if connected then databasedisconnect() end + end) + if not res and err then + errtxt = err + end + return cfe({type="group", value=retval, label="Database Search", errtxt=errtxt}) +end + diff --git a/kamailio-searchdatabase-html.lsp b/kamailio-searchdatabase-html.lsp new file mode 100644 index 0000000..87f0a9d --- /dev/null +++ b/kamailio-searchdatabase-html.lsp @@ -0,0 +1,28 @@ +<% local form, viewlibrary, page_info = ... +require("viewfunctions") +%> + +<% if form.value.result then + local func = haserl.loadfile(page_info.viewfile:gsub("searchdatabase", "viewtable")) + local table, column = string.match(form.value.id.value, "^([^.]*)%.(.*)") + form.value.table = cfe({ value=table }) + form.value.fields = cfe({ value={} }) + for i,o in ipairs(form.value.id.option) do + local t,c = string.match(o, "^([^.]*)%.(.*)") + if t == table then + form.value.fields.value[#form.value.fields.value + 1] = c + end + end + form.value.entries = form.value.result + func(form, viewlibrary, page_info, session) + form.value.entries = nil + form.value.table = nil + form.value.fields = nil +end %> + +<H1><%= html.html_escape(form.label) %></H1> +<% + form.value.result = nil + form.option = "Search" + displayform(form, nil, nil, page_info, 2) +%> diff --git a/kamailio-startstop-html.lsp b/kamailio-startstop-html.lsp new file mode 120000 index 0000000..0ea2627 --- /dev/null +++ b/kamailio-startstop-html.lsp @@ -0,0 +1 @@ +../startstop-html.lsp
\ No newline at end of file diff --git a/kamailio-status-html.lsp b/kamailio-status-html.lsp new file mode 100644 index 0000000..9a8bce3 --- /dev/null +++ b/kamailio-status-html.lsp @@ -0,0 +1,46 @@ +<% local data, viewlibrary, page_info, session = ... +require("viewfunctions") +%> + +<% displaycommandresults({"install","edit"}, session) %> +<% displaycommandresults({"startstop"}, session) %> +<% displaycommandresults({"reloadplan"}, session) %> + +<H1>System Info</H1> +<DL> +<% +displayitem(data.value.status) + +displayitem(data.value.version) +if data.value.version and data.value.version.errtxt and viewlibrary.check_permission("apk-tools/apk/install") then +%> + <DT>Install package</DT> + <DD><form action="<%= html.html_escape(page_info.script .. "/apk-tools/apk/install") %>" method="POST"> + <input type='hidden' name='package' value='<%= html.html_escape(data.value.version.name) %>'> + <input class='submit' type='submit' value='Install'></form></DD> +<% +end + +displayitem(data.value.autostart) +if not (data.value.version and data.value.version.errtxt) and data.value.autostart and data.value.autostart.errtxt and viewlibrary.check_permission("alpine-baselayout/rc/edit") then +%> + <DT>Enable autostart</DT> + <DD><form action="<%= html.html_escape(page_info.script .. "/alpine-baselayout/rc/edit") %>" method="POST"> + <input type='hidden' name='servicename' value='<%= html.html_escape(data.value.autostart.name) %>'> + <input type='hidden' name='redir' value='<%= html.html_escape(page_info.orig_action) %>'> + <input class='submit' type='submit' value='Enable'></form></DD> +<% end %> +</DL> + +<% if viewlibrary and viewlibrary.dispatch_component and viewlibrary.check_permission("startstop") then + viewlibrary.dispatch_component("startstop") +end %> + +<% if viewlibrary.check_permission("reloadplan") then %> +<H1>Reload Dial Plan</H1> +<DL> + <DT>Reload dial plan into memory</DT> + <DD><form action="<%= html.html_escape(page_info.script .. page_info.prefix) %>kamailio/reloadplan" method="POST"> + <input class="submit" type="submit" name="reloadplan" value="Reload"></form></DD> +</DL> +<% end %> diff --git a/kamailio-updatetableentry-html.lsp b/kamailio-updatetableentry-html.lsp new file mode 100644 index 0000000..14ffd0a --- /dev/null +++ b/kamailio-updatetableentry-html.lsp @@ -0,0 +1,14 @@ +<% local form, viewlibrary, page_info = ... +require("viewfunctions") +%> + +<H1><%= html.html_escape(form.label) %></H1> +<% + form.value.table.readonly = true + if page_info.action == "updatetableentry" and form.value.id then + form.value.id.readonly = true + elseif form.value.id then + form.value.id.type = "hidden" + end + displayform(form, nil, nil, page_info, 2) +%> diff --git a/kamailio-updateuser-html.lsp b/kamailio-updateuser-html.lsp new file mode 100644 index 0000000..fb05750 --- /dev/null +++ b/kamailio-updateuser-html.lsp @@ -0,0 +1,15 @@ +<% local form, viewlibrary, page_info = ... +require("viewfunctions") +%> + +<H1><%= html.html_escape(form.label) %></H1> +<% + form.action = page_info.script .. page_info.prefix .. page_info.controller .. "/" .. page_info.action + form.value.password.type = "password" + form.value.password_confirm.type = "password" + if page_info.action == "updateuser" then + form.value.username.readonly = true + end + local order = {"username", "password", "password_confirm", "email_address"} + displayform(form, order) +%> diff --git a/kamailio-viewtable-html.lsp b/kamailio-viewtable-html.lsp new file mode 100644 index 0000000..28d4e13 --- /dev/null +++ b/kamailio-viewtable-html.lsp @@ -0,0 +1,54 @@ +<% local form, viewlibrary, page_info, session = ... %> +<% require("viewfunctions") %> + +<% displaycommandresults({"deletetableentry", "updatetableentry"}, session) %> +<% displaycommandresults({"createtableentry"}, session, true) %> + +<H1><%= html.html_escape(form.label) %> - <%= html.html_escape(form.value.table.value) %></H1> +<DL> +<TABLE> + <TR style="background:#eee;font-weight:bold;"> + <% if viewlibrary.check_permission("deletetableentry") or viewlibrary.check_permission("updatetableentry") then %> + <TD style="padding-right:20px;white-space:nowrap;" class="header">Action</TD> + <% end %> + <% for i,f in ipairs(form.value.fields.value) do %> + <TD style="padding-right:20px;white-space:nowrap;" class="header"><%= html.html_escape(f) %></TD> + <% end %> + </TR> + +<% for i,tableentry in ipairs(form.value.entries.value) do %> + <TR> + <% if viewlibrary.check_permission("deletetableentry") or viewlibrary.check_permission("updatetableentry") then %> + <TD style="padding-right:20px;white-space:nowrap;"> + <% if viewlibrary.check_permission("updatetableentry") then %> + <form action="updatetableentry" method="POST"> + <input class="hidden" type="hidden" name="table" value="<%= html.html_escape(form.value.table.value) %>"> + <input class="hidden" type="hidden" name="id" value="<%= html.html_escape(tableentry.id) %>"> + <input class="hidden" type="hidden" name="redir" value="<%= html.html_escape(page_info.orig_action.."?table="..form.value.table.value) %>"> + <input class="submit" type="submit" value="Update"></form> + <% end %> + <% if viewlibrary.check_permission("deletetableentry") then %> + <form action="deletetableentry" method="POST"> + <input class="hidden" type="hidden" name="table" value="<%= html.html_escape(form.value.table.value) %>"> + <input class="hidden" type="hidden" name="id" value="<%= html.html_escape(tableentry.id) %>"> + <input class="submit" type="submit" value="Delete"></form> + <% end %> + </TD> + <% end %> + <% for i,f in ipairs(form.value.fields.value) do %> + <TD><%= html.html_escape(tableentry[f]) %></TD> + <% end %> + </TR> +<% end %> +</TABLE> +<% if form.errtxt then %> +<p class="error"><%= html.html_escape(form.errtxt) %></p> +<% end %> +<% if #form.value.entries.value == 0 then %> +<p>No entries found</p> +<% end %> +</DL> + +<% if page_info.action == "viewtable" and viewlibrary and viewlibrary.dispatch_component and viewlibrary.check_permission("createtableentry") then + viewlibrary.dispatch_component("createtableentry", {table=form.value.table.value}) +end %> diff --git a/kamailio.menu b/kamailio.menu new file mode 100644 index 0000000..9d51be7 --- /dev/null +++ b/kamailio.menu @@ -0,0 +1,7 @@ +# Prefix and controller are already known at this point +# Cat Group Tab Action +Applications 86Kamailio Status status +Applications 86Kamailio Users listusers +Applications 86Kamailio Database listtables +Applications 86Kamailio Search_Database searchdatabase +Applications 86Kamailio Expert listfiles diff --git a/kamailio.roles b/kamailio.roles new file mode 100644 index 0000000..c5f2ba3 --- /dev/null +++ b/kamailio.roles @@ -0,0 +1,4 @@ +USER=kamailio:status,kamailio:startstop,kamailio:listusers,kamailio:reloadplan +EDITOR=kamailio:createuser,kamailio:updateuser,kamailio:deleteuser,kamailio:listtables,kamailio:viewtable,kamailio:deletetableentry,kamailio:updatetableentry,kamailio:createtableentry,kamailio:searchdatabase,kamailio:reloadplan +EXPERT=kamailio:listfiles,kamailio:edit,kamailio:createdatabase,kamailio:reloadplan +ADMIN=kamailio:status,kamailio:startstop,kamailio:listusers,kamailio:createuser,kamailio:updateuser,kamailio:deleteuser,kamailio:listfiles,kamailio:edit,kamailio:listtables,kamailio:viewtable,kamailio:deletetableentry,kamailio:updatetableentry,kamailio:createtableentry,kamailio:createdatabase,kamailio:searchdatabase,kamailio:reloadplan |