module(..., package.seeall) -- Load libraries require("modelfunctions") require("validator") require("fs") require("posix") -- Set variables local configfile = "/etc/tcpproxy.conf" local processname = "tcpproxy" local packagename = "tcpproxy" local smtppackagename = "rxmtp" local smtpdirectory = "/etc/rxmtp/" local keywords = { "standalone", "port", "interface", "rotate", "server", "uid", "gid", "user", "exec", "acp", "logname", "setenv", "timeout", "writefile" } local config -- ################################################################################ -- LOCAL FUNCTIONS local function parseconfigfile(file) file = file or "" local retval = {} for line in string.gmatch(file, "([^\n]+)\n?") do line = string.gsub(line, "#.*$", "") if line and line ~= "" then table.insert(retval, {}) for word in string.gmatch(line, "%S+") do table.insert(retval[#retval], word) end end end return retval end -- ################################################################################ -- PUBLIC FUNCTIONS function startstop_service(action) return modelfunctions.startstop_service(processname, action) end function getstatus() return modelfunctions.getstatus(processname, packagename, "TCP Proxy Status") end function getconfigfile() return modelfunctions.getfiledetails(configfile) end function setconfigfile(filedetails) filedetails.value.filename.value = configfile return modelfunctions.setfiledetails(filedetails) end local function getsmtpconfig(config) -- parse the TCP proxy config file for smtp entries local retval = {} local port25 = false local currentint for i,entry in ipairs(config) do if entry[1] == "port" then if entry[2] == "25" then port25 = true currentint = nil else port25 = false currentint = nil end elseif port25 then if entry[1] == "interface" then currentint = entry[2] elseif entry[1] == "exec" and string.find(entry[2], "rxmtp$") then if currentint then table.insert(retval, {ipaddr=currentint, cmd=table.concat(entry, " ", 2)}) else -- bad config - exec command without interface end end end end return retval end local function setsmtpcmd(ipaddr, cmd) if cmd then cmd = "exec "..cmd end local file = fs.read_file(configfile) local inport25 = false local ininterface = false local done = false local lines = {} for line in string.gmatch(file, "([^\n]*)\n?") do if not done then if ininterface and string.find(line, "^%s*exec%s") then -- We found the line, replace it line = cmd done = true elseif ininterface and string.find(line, "^%s*interface%s") then -- We're leaving the interface and we haven't written line yet if cmd then if string.find(lines[#lines], "^%s*$") then lines[#lines] = nil end lines[#lines + 1] = "exec "..cmd lines[#lines + 1] = "" end done = true elseif inport25 and string.find(line, "^%s*interface%s+"..ipaddr) then ininterface = true elseif inport25 and string.find(line, "^%s*port%s") then -- we're leaving port 25 and we haven't written line yet if cmd then lines[#lines + 1] = "interface "..ipaddr lines[#lines + 1] = "exec "..cmd lines[#lines + 1] = "" end done = true elseif string.find(line, "^%s*port%s+25") then inport25 = true end end lines[#lines + 1] = line end if not done and cmd then if not inport25 then lines[#lines + 1] = "port 25" end if not ininterface then lines[#lines + 1] = "interface "..ipaddr end lines[#lines + 1] = "exec "..cmd end fs.write_file(configfile, table.concat(lines, "\n")) end function getsmtpstatus() local value, errtxt = processinfo.package_version(smtppackagename) local version = cfe({ value=value, label="Program version" }) return cfe({ type="group", value={version=version, entries=entries}, label="SMTP Proxy Status" }) end function listsmtpentries(self) local entries = cfe({ type="structure", value={}, label="SMTP Command Entries" }) if self then local interfacescontroller = self:new("alpine-baselayout/interfaces") local interfaces = interfacescontroller.model:get_addresses() interfacescontroller:destroy() -- add in entries for interfaces (w/o ipaddr) local interface for i,entry in ipairs(interfaces.value) do if interface ~= entry.interface then interface = entry.interface table.insert(entries.value, {interface=interface}) end table.insert(entries.value, entry) end end local reverseaddress = {} for i,int in ipairs(entries.value) do reverseaddress[int.ipaddr or int.interface] = i end config = config or parseconfigfile(fs.read_file(configfile)) local smtpconfig = getsmtpconfig(config) for i,int in ipairs(smtpconfig) do local pos = reverseaddress[int.ipaddr] if pos then entries.value[pos].cmd = int.cmd else if not validator.is_ipv4(int.ipaddr) then int.interface = int.ipaddr int.ipaddr = nil end table.insert(entries.value, int) end end return entries end function readsmtpentry(ipaddr) config = config or parseconfigfile(fs.read_file(configfile)) local smtpconfig = getsmtpconfig(config) local exec = "" for i,entry in ipairs(smtpconfig) do if entry.ipaddr == ipaddr then exec = entry.cmd break end end local ipaddrcfe = cfe({ value=ipaddr, label="Interface / IP Address" }) local execcfe = cfe({ value=exec, label="Command" }) return cfe({ type="group", value={exec=execcfe, ipaddr=ipaddrcfe}, label="SMTP Proxy Entry" }) end function updatesmtpentry(entry) -- validate? setsmtpcmd(entry.value.ipaddr.value, entry.value.exec.value) return entry end function delsmtpentry(ipaddr) setsmtpcmd(ipaddr, nil) return cfe({ value="Deleted SMTP Proxy Entry", label="Delete SMTP Entry result" }) end function listsmtpfiles() local retval = cfe({ type="list", value={}, label="SMTP Proxy Files" }) if not fs.is_dir(smtpdirectory) then posix.mkdir(smtpdirectory) end for file in posix.files(smtpdirectory) do if fs.is_file(smtpdirectory .. file) then table.insert(retval.value, smtpdirectory .. file) end end return retval end function getnewsmtpfile() local filename = cfe({ label="File Name", descr="Must be in "..smtpdirectory }) return cfe({ type="group", value={filename=filename}, label="SMTP Proxy File" }) end function createsmtpfile(filedetails) local success = true if not validator.is_valid_filename(filedetails.value.filename.value, smtpdirectory) then success = false filedetails.value.filename.errtxt = "Invalid filename" else if not fs.is_dir(smtpdirectory) then posix.mkdir(smtpdirectory) end if posix.stat(filedetails.value.filename.value) then success = false filedetails.value.filename.errtxt = "Filename already exists" end end if success then fs.create_file(filedetails.value.filename.value) else filedetails.errtxt = "Failed to Create File" end return filedetails end function readsmtpfile(filename) if validator.is_valid_filename(filename, smtpdirectory) and fs.is_file(filename) then return modelfunctions.getfiledetails(filename) end local retval = modelfunctions.getfiledetails("") retval.value.filename.value = filename return retval end function updatesmtpfile(filedetails) if validator.is_valid_filename(filedetails.value.filename.value, smtpdirectory) and fs.is_file(filedetails.value.filename.value) then return modelfunctions.setfiledetails(filedetails) end filedetails.value.filename.errtxt = "Invalid Filename" filedetails.errtxt = "Failed to set file" return filedetails end function delsmtpfile(filename) local retval = cfe({ value="Deleted SMTP Proxy File", label="Delete SMTP File result" }) if validator.is_valid_filename(filename, smtpdirectory) and fs.is_file(filename) then os.remove(filename) else retval.value = "Failed to delete SMTP Proxy File - invalid filename" end return retval end