summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTed Trask <ttrask01@yahoo.com>2008-08-29 17:48:19 +0000
committerTed Trask <ttrask01@yahoo.com>2008-08-29 17:48:19 +0000
commit7675a3125618bad993398d89e2ef91eb238c8bc3 (patch)
tree3ca00a9c5c7b43b9b4a5664a84cdc18a30640838
parent0441e224f891306f238eac938692a0088eda1e43 (diff)
downloadacf-fetchmail-7675a3125618bad993398d89e2ef91eb238c8bc3.tar.bz2
acf-fetchmail-7675a3125618bad993398d89e2ef91eb238c8bc3.tar.xz
Added config to fetchmail to configure globals and multiple entries. Also added ability to run once and test. Still need support for ETRN and SSL.
git-svn-id: svn://svn.alpinelinux.org/acf/fetchmail/trunk@1437 ab2d0c66-481e-0410-8bed-d214d4d58bed
-rw-r--r--fetchmail-config-html.lsp169
-rw-r--r--fetchmail-controller.lua22
l---------fetchmail-createentry-html.lsp1
l---------fetchmail-editconfig-html.lsp1
-rw-r--r--fetchmail-editentry-html.lsp13
-rw-r--r--fetchmail-listentries-html.lsp40
-rw-r--r--fetchmail-model.lua657
l---------fetchmail-startstop-html.lsp1
-rw-r--r--fetchmail.menu2
-rw-r--r--fetchmail.roles4
10 files changed, 544 insertions, 366 deletions
diff --git a/fetchmail-config-html.lsp b/fetchmail-config-html.lsp
index ac6dbd6..b686400 100644
--- a/fetchmail-config-html.lsp
+++ b/fetchmail-config-html.lsp
@@ -1,160 +1,25 @@
-<% local form = ...
-require("viewfunctions")
-%>
-<%
---[[ DEBUG INFORMATION
-io.write("<H1>DEBUGGING</H1><span style='color:red'><H2>DEBUG INFO: CFE</H2>")
-io.write(html.cfe_unpack(form))
-io.write("</span>")
---]]
-%>
+<% local view, viewlibrary, page_info, session = ... %>
+<% require("viewfunctions") %>
-<H1>SYSTEM INFO</H1>
-<DL>
-<%
-local myform = form.status
-local tags = { "status", "version", "autostart", }
-displayinfo(myform,tags,"viewonly")
-%>
-</DL>
-
-<H1>CONFIGURATION</H1>
-<% local myform = form.config %>
-<form action="<%= form.option.script .. "/" .. form.option.prefix ..
- form.option.controller .. "/" .. form.option.action %>" method="POST">
+<% displaycommandresults({"deleteentry"}, session) %>
-<H2>GUIDED CONFIGURATION</H2>
-<% --[[ %>
-<H3>FREQUENCY</H3>
-<P>Select how often the mailboxes will be checked. For ETRN transfers (below) this specifies how often transfers are initiated</P>
-<%
-local tags = { "freq", }
-displayinfo(myform,tags)
-%>
-<% --]] %>
+<% if viewlibrary and viewlibrary.dispatch_component then
+ viewlibrary.dispatch_component("status")
+end %>
-<H3>MAILBOXES</H3>
-<P>You will need to specify the remote mailbox and password, as well as the local (internal) mailbox to forward the mail to. The SMTP host is the name or address of the SMTP server to send the mail to.</P>
-<P>If you want to use POP3 account for the entire domain, selecting the <U>pop3domain</U> method will setup the appropriate configuration for retrieving and distributing the mail appropriately. The <B>domain</B> information should be your internet smtp domain. The <B>mailbox</B> field is ignored when this method is selected.</P>
+<H1>Global Settings</H1>
-<P>When selecting the <U>pop3</U> or <U>imap</U> methods, the <B>domain</B> field should be left blank.</P>
-<% -- START - SPECIAL VIEW FOR SHOWING AVAILABLE MAILBOXES %>
-<%
-local tags = "mailboxes"
-local mailboxform = myform[tags]["value"]
-%>
<DL>
-<%
- io.write("\n\t<CENTER><TABLE STYLE='border:1px solid #ccc;background:#eee'>\n\t\t<TR>\n\t\t\t<TD CLASS='header' WIDTH='150px'>")
- io.write("<CENTER>METHOD</CENTER></TD>\n\t\t\t<TD CLASS='header' COLSPAN=2>")
- io.write("<CENTER>REMOTE</CENTER></TD>\n\t\t\t<TD CLASS='header' COLSPAN=2>")
- io.write("<CENTER>LOCAL</CENTER></TD>\n\t\t</TR>")
-for i=1, table.maxn(mailboxform) do
- leftpadd="30px"
- rightpadd="5px"
-
- io.write("\n\t\t<TR>\n\t\t\t<TD>")
- val = "method"
- io.write(html.form[mailboxform[i][val]["type"]](mailboxform[i][val]))
- io.write("</TD>\n\t\t\t<TD style='padding-left:".. leftpadd .. ";padding-right:".. rightpadd .. ";' WIDTH='5%'>")
- val = "remotehost"
- io.write(mailboxform[i][val]["label"])
- io.write(":</TD>\n\t\t\t<TD>")
- io.write(html.form[mailboxform[i][val]["type"]](mailboxform[i][val]))
- io.write("</TD>\n\t\t\t<TD style='padding-left:".. leftpadd .. ";padding-right:".. rightpadd .. ";' WIDTH='5%'>")
- val = "localhost"
- io.write(mailboxform[i][val]["label"])
- io.write(":</TD>\n\t\t\t<TD>")
- io.write(html.form[mailboxform[i][val]["type"]](mailboxform[i][val]))
- io.write("</TD>\n\t\t</TR>")
- io.write("\n\t\t<TR>\n\t\t\t<TD>")
- val = "disabled"
- io.write(html.form[mailboxform[i][val]["type"]](mailboxform[i][val]))
- io.write(" " .. mailboxform[i][val]["label"])
- io.write("</TD>\n\t\t\t<TD style='padding-left:".. leftpadd .. ";padding-right:".. rightpadd .. ";'>")
- val = "remotemailbox"
- io.write(mailboxform[i][val]["label"])
- io.write(":</TD>\n\t\t\t<TD>")
- io.write(html.form[mailboxform[i][val]["type"]](mailboxform[i][val]))
- io.write("</TD>\n\t\t\t<TD style='padding-left:".. leftpadd .. ";padding-right:".. rightpadd .. ";'>")
- val = "localmailbox"
- io.write(mailboxform[i][val]["label"])
- io.write(":</TD>\n\t\t\t<TD>")
- io.write(html.form[mailboxform[i][val]["type"]](mailboxform[i][val]))
- io.write("</TD>\n\t\t</TR>")
- io.write("\n\t\t<TR>\n\t\t\t<TD>")
- io.write("</TD>\n\t\t\t<TD style='padding-left:".. leftpadd .. ";padding-right:".. rightpadd .. ";'>")
- val = "remotepassword"
- io.write(mailboxform[i][val]["label"])
- io.write(":</TD>\n\t\t\t<TD>")
- io.write(html.form[mailboxform[i][val]["type"]](mailboxform[i][val]))
- io.write("</TD>\n\t\t\t<TD style='padding-left:".. leftpadd .. ";padding-right:".. rightpadd .. ";'>")
- val = "localdomain"
- io.write(mailboxform[i][val]["label"])
- io.write(":</TD>\n\t\t\t<TD>")
- io.write(html.form[mailboxform[i][val]["type"]](mailboxform[i][val]))
- io.write("</TD>\n\t\t</TR>")
- io.write("\n\t\t<TR>\n\t\t\t<TD COLSPAN=5><HR></TD>\n\t\t</TR>")
-end
-io.write("</TABLE></CENTER>")
-%>
+<dt>Edit global settings</dt>
+<dd><form action="<%= page_info.script .. page_info.prefix .. page_info.controller .. "/editconfig" %>" method="POST">
+<input type=submit value="Edit" class="submit">
+</form></dd>
</DL>
-<% -- START - SPECIAL VIEW FOR SHOWING AVAILABLE MAILBOXES %>
-
-<H3>LAST RESORT</H3>
-<P>The Postmaster address is the address of last resort when fetchmail cannot determine how to send a mail message. If left blank, undelieverable mail may be discarded.</P>
-<%
-local tags = { "postmaster", }
-displayinfo(myform,tags)
-%>
-
-<H3>ETRN</H3>
-<P>This is for branches that have DNS mail domains hosted by an ISP that allows for ETRN dequeueing. ETRN dequeueing causes the SMTP mail exchanger at the ISP to forward mail to you via SMTP. You will need to make sure that DNS is setup appropriately and that your ISP supports ETRN. If you have not arranged with your ISP to have ETRN support, leave these fields blank.</P>
-<%
-local tags = { "etrnremote","etrnquedomain", }
-displayinfo(myform,tags)
-%>
-
-
-<% --[[ %>
-<H2>ADVANCED CONFIGURATION</H2>
-<H3>GENERAL</H3>
-<%
-local tags = { "logfile", "loglevel", "smallerlogs", }
-displayinfo(myform,tags)
-%>
-
-<% --]] %>
-
-<H2>SAVE AND APPLY ABOVE SETTINGS</H2>
-<%
-local tags = { "cmdsave","cmdtest", }
-displayinfo(myform,tags)
-%>
-</form>
-
-<%
-local cmdform = form.management
-local tags = { "start", "stop", "restart" }
-if (cmdform) and (cmdform[tags[1]]) then
-%>
- <form name="management" action="" method="POST">
- <H1>MANAGEMENT</H1>
- <dl>
- <dt><%= cmdform[tags[1]]["label"] %></dt>
- <dd>
- <% for k,v in pairs(tags) do %>
- <% if (cmdform[v]) then %>
- <% io.write(html.form[cmdform[v].type](cmdform[v])) %>
- <% end %>
- <% end %>
- </dd>
- <% if (form.cmdmanagement) and (#form.cmdmanagement.descr > 0) then %>
- <dt>Previous action result</dt>
- <dd><pre><%= form.cmdmanagement.descr %></pre></dd>
- <% end %>
- </dl>
- </form>
+<% if viewlibrary and viewlibrary.dispatch_component then
+ viewlibrary.dispatch_component("listentries")
+end %>
-<% end %>
+<% if viewlibrary and viewlibrary.dispatch_component then
+ viewlibrary.dispatch_component("startstop")
+end %>
diff --git a/fetchmail-controller.lua b/fetchmail-controller.lua
index 8adb959..b70712e 100644
--- a/fetchmail-controller.lua
+++ b/fetchmail-controller.lua
@@ -11,11 +11,27 @@ end
function startstop(self)
return controllerfunctions.handle_startstop(self, self.model.startstop_service, self.model.getstatus, self.clientdata)
end
---[[
-function config(self)
+
+function editconfig(self)
return controllerfunctions.handle_form(self, self.model.getconfig, self.model.updateconfig, self.clientdata, "Save", "Edit Config", "Configuration Set")
end
---]]
+
function expert(self)
return controllerfunctions.handle_form(self, self.model.get_filedetails, self.model.update_filecontent, self.clientdata, "Save", "Edit Config", "Configuration Set")
end
+
+function listentries(self)
+ return self.model.readentries()
+end
+
+function editentry(self)
+ return controllerfunctions.handle_form(self, function() return self.model.readentry(self.clientdata.entry) end, self.model.updateentry, self.clientdata, "Save", "Edit Entry", "Entry Saved")
+end
+
+function createentry(self)
+ return controllerfunctions.handle_form(self, self.model.readentry, self.model.createentry, self.clientdata, "Create", "Create Entry", "Entry Created")
+end
+
+function deleteentry(self)
+ return self:redirect_to_referrer(self.model.deleteentry(self.clientdata.entry))
+end
diff --git a/fetchmail-createentry-html.lsp b/fetchmail-createentry-html.lsp
new file mode 120000
index 0000000..544c106
--- /dev/null
+++ b/fetchmail-createentry-html.lsp
@@ -0,0 +1 @@
+fetchmail-editentry-html.lsp \ No newline at end of file
diff --git a/fetchmail-editconfig-html.lsp b/fetchmail-editconfig-html.lsp
new file mode 120000
index 0000000..4b6b762
--- /dev/null
+++ b/fetchmail-editconfig-html.lsp
@@ -0,0 +1 @@
+../form-html.lsp \ No newline at end of file
diff --git a/fetchmail-editentry-html.lsp b/fetchmail-editentry-html.lsp
new file mode 100644
index 0000000..410b3ef
--- /dev/null
+++ b/fetchmail-editentry-html.lsp
@@ -0,0 +1,13 @@
+<% local form, viewlibrary, page_info = ...
+require("viewfunctions")
+%>
+
+<H1><%= form.label %></H1>
+<%
+ form.action = page_info.script .. page_info.prefix .. page_info.controller .. "/" .. page_info.action
+ if page_info.action == "editentry" then
+ form.value.remotehost.contenteditable = false
+ end
+ local order = { "remotehost", "enabled", "method", "remotemailbox", "remotepassword", "localhost", "localmailbox", "localdomain" }
+ displayform(form, order)
+%>
diff --git a/fetchmail-listentries-html.lsp b/fetchmail-listentries-html.lsp
new file mode 100644
index 0000000..2b2e54e
--- /dev/null
+++ b/fetchmail-listentries-html.lsp
@@ -0,0 +1,40 @@
+<% local data, viewlibrary, page_info, session = ...
+require("viewfunctions")
+%>
+<%
+--[[ DEBUG INFORMATION
+io.write("<H1>DEBUGGING</H1><span style='color:red'><H2>DEBUG INFO: CFE</H2>")
+io.write(html.cfe_unpack(data))
+io.write("</span>")
+--]]
+%>
+
+<% displaycommandresults({"deleteentry"}, session) %>
+
+<h1>Edit/View Fetchmail Entries</h1>
+<DL>
+<TABLE>
+ <TR style="background:#eee;font-weight:bold;">
+ <TD style="padding-right:20px;white-space:nowrap;text-align:left;" class="header">Action</TD>
+ <TD style="padding-right:20px;white-space:nowrap;text-align:left;" class="header">Entry</TD>
+ <TD style="padding-right:20px;white-space:nowrap;text-align:left;" class="header">Enabled</TD>
+ <TD style="white-space:nowrap;text-align:left;" class="header">Method</TD>
+ </TR>
+<% for i,entry in ipairs(data.value) do %>
+ <TR>
+ <TD style="padding-right:20px;white-space:nowrap;">
+ <% io.write(html.link{value = "editentry?entry=" .. entry.entry, label="Edit " }) %>
+ <% io.write(html.link{value = "deleteentry?entry=" .. entry.entry, label="Delete " }) %>
+ </TD>
+ <TD style="padding-right:20px;white-space:nowrap;"><%= entry.entry %></TD>
+ <TD style="padding-right:20px;white-space:nowrap;"><%= entry.enabled %></TD>
+ <TD style="white-space:nowrap;" width="90%"><%= entry.method %></TD>
+ </TR>
+<% end %>
+</TABLE>
+
+<DT>Add new entry</DT>
+<DD><form action="<%= page_info.script .. page_info.prefix .. page_info.controller .. "/createentry" %>" method="POST">
+<input type=submit value="New" class="submit">
+</form></DD>
+</DL>
diff --git a/fetchmail-model.lua b/fetchmail-model.lua
index d619f0e..8c3fd84 100644
--- a/fetchmail-model.lua
+++ b/fetchmail-model.lua
@@ -2,186 +2,304 @@ module(..., package.seeall)
-- Load libraries
require("modelfunctions")
+require("posix")
require("fs")
+require("getopts")
+require("validator")
-- Set variables
local packagename = "fetchmail"
local processname = "fetchmail"
-local configfile = "/root/.fetchmailrc"
-local config = {}
+local configfile = "/etc/fetchmailrc"
+local confdfile = "/etc/conf.d/fetchmail"
+local config
+
+local methods = {"pop3","imap","pop3domain", }
-- ################################################################################
-- LOCAL FUNCTIONS
-local function getloglevels()
- local loglevels = {}
- for i=1,8 do
- table.insert(loglevels,i)
- end
- return loglevels
-end
-
-local function getmethods()
- local methods = {"pop3","imap","pop3domain", }
- return methods
-end
-
-local function getmailboxes(t)
- local objects = cfe({})
- objects.label = "Mailbox " .. tostring(t["RBOX"])
- objects.method = cfe({
- name="method",
- label = "Method",
- type = "select",
- value = t["METHOD"],
- option = getmethods(),
- })
- objects.disabled = cfe({
- name="disabled",
- type="checkbox",
- label = "Disabled",
- checked = t["DISABLED"],
- })
- objects.remotehost = cfe({
- name="remotehost",
- label = "RemoteHost",
- value = t["RHOST"],
- })
- objects.remotemailbox = cfe({
- name="remotemailbox",
- label = "Mailbox",
- value = t["RBOX"],
- })
- objects.remotepassword = cfe({
- name="remotepassword",
- label = "Password",
- type = "passwd",
- value = t["PASSWD"],
- })
- objects.localhost = cfe({
- name="localhost",
- label = "LocalHost",
- value = t["LHOST"],
- })
- objects.localmailbox = cfe({
- name="localmailbox",
- label = "LocalMailbox",
- value = t["LBOX"],
- })
- objects.localdomain = cfe({
- name="localdomain",
- label = "LocalDomain",
- value = t["DOMAIN"],
- })
- return objects
-end
-
-local function read_config()
---[[
-imap/pop3 lines are:
-1 2 3 4 5 6 7 8 9 10
-poll <host> protocol <proto> no dns username <uname> password <passwd>
-11 12 13 14 15 16 17
-is <localmailbox> smtphost <desthost> no rewrite fetchall
-
-pop3domain lines are:
-1 2 3 4 5 6 7 8 9 10 11 12
-poll <host> localdomainst <domain> protocol pop3 no dns username <uname> password <passwd>
-13 14 15 16 17 18 19 20 21 22
-to * here smtphost <desthost> smtpaddress <domain> no rewrite fetchall
-
-etrn is:
-1 2 3 4 5 6
-pool <host> protocol etrn smtpdomain <mydomain>
---]]
- local mailboxes = {}
- local configcontent_postmaster = {}
- local configcontent_etrn = {}
- local path = configfile
- local valid = nil
- configcontenttable = fs.read_file_as_array(path) or {}
- for k,v in pairs(configcontenttable) do
- if (string.match(v, "^%s*#Begin Fetchmail")) then valid=1 end
- if (valid) then
- --
- local DISABLED = string.match(v, "^%s*(#)")
-
- -- Set parameters for POP3 or IMAP
- local val = {RHOST=2, CHECK=9 ,METHOD=4, RBOX=8, PASSWD=10, LBOX=12, LHOST=14}
- local configcontent = {}
- configcontent[k] = {}
- configcontent[k]["DISABLED"] = DISABLED
- for kk,vv in pairs(val) do
- configcontent[k][kk] = tostring(string.match(v,"^%s*" .. string.rep("%S*%s*",vv-1) .. "(%S*)%s*"))
- end
- -- Remove quotes from passwords
- if (configcontent[k]["PASSWD"]) then
- configcontent[k]["PASSWD"] = string.match(configcontent[k]["PASSWD"], "^\"(.-)\"")
- end
- -- Check if row is valid config
- if (configcontent[k]["CHECK"]) and (string.lower(configcontent[k]["CHECK"]) == "password") then
- table.insert(mailboxes,getmailboxes(configcontent[k]))
- end
-
- -- Set parameters for POP3domain
- local val = {RHOST=2, METHOD=6, RBOX=10, CHECK=11, PASSWD=12, LHOST=17, DOMAIN=19,}
- local configcontent = {}
- configcontent[k] = {}
- configcontent[k]["DISABLED"] = DISABLED
- for kk,vv in pairs(val) do
- configcontent[k][kk] = tostring(string.match(v,"^%s*" .. string.rep("%S*%s*",vv-1) .. "(%S*)%s*"))
- end
- -- Display this config as Method=pop3domain (as the current options in the view)
- configcontent[k]["METHOD"] = "pop3domain"
- -- Remove quotes from passwords
- if (configcontent[k]["PASSWD"]) then
- configcontent[k]["PASSWD"] = string.match(configcontent[k]["PASSWD"], "^\"(.-)\"")
- end
- -- Check if row is valid config
- if (configcontent[k]["CHECK"]) and (string.lower(configcontent[k]["CHECK"]) == "password") then
- table.insert(mailboxes,getmailboxes(configcontent[k]))
- end
-
- -- Set parameters for etrn
- local val = {ETRNSMTPHOST=2, CHECK=4, ETRNDOMAIN=6}
- local configcontent = {}
- configcontent[k] = {}
- configcontent[k]["DISABLED"] = DISABLED
- for kk,vv in pairs(val) do
- configcontent[k][kk] = tostring(string.match(v,"^%s*" .. string.rep("%S*%s*",vv-1) .. "(%S*)%s*"))
- end
- -- Check if row is valid config
- if (configcontent[k]["CHECK"]) and (string.lower(configcontent[k]["CHECK"]) == "etrn") then
- configcontent_etrn=configcontent[k]
- end
-
- -- Set parameters for postmaster
- local val = {CHECK=2, POSTMASTER=3}
- local configcontent = {}
- configcontent[k] = {}
- configcontent[k]["DISABLED"] = DISABLED
- for kk,vv in pairs(val) do
- configcontent[k][kk] = tostring(string.match(v,"^%s*" .. string.rep("%S*%s*",vv-1) .. "(%S*)%s*"))
- end
- -- Check if row is valid config
- if (configcontent[k]["CHECK"]) and (string.lower(configcontent[k]["CHECK"]) == "postmaster") then
- configcontent_postmaster=configcontent[k]
- end
-
- end
- if (string.match(v, "^%s*#End Fetchmail")) then valid=nil end
- end
- -- Create one empty record so that user can add settings
- table.insert(mailboxes,getmailboxes({}))
- return mailboxes,configcontent_postmaster,configcontent_etrn
+local function parseconfigfile(file)
+ file = file or ""
+ local retval = {}
+ local linenum=0
+ for line in string.gmatch(file, "([^\n]*)\n?") do
+ linenum=linenum+1
+ if not string.match(line, "^%s*$") and not string.match(line, "^%s*#") then
+ table.insert(retval, {linenum=linenum})
+ -- Iterate through each word, being careful about quoted strings and comments
+ local offset = 1
+ while string.find(line, "%S+", offset) do
+ local word = string.match(line, "%S+", offset)
+ local endword = select(2, string.find(line, "%S+", offset))
+ if string.find(word, "^#") then
+ break
+ elseif string.find(word, "^\"") then
+ endword = select(2, string.find(line, "\"[^\"]*\"", offset))
+ word = string.sub(line, string.find(line, "\"", offset), endword)
+ end
+ table.insert(retval[#retval], word)
+ offset = endword + 1
+ end
+ end
+ end
+ return retval
+end
+
+local function findentryline(entryname)
+ if entryname and entryname ~= "" then
+ config = config or parseconfigfile(fs.read_file(configfile))
+ for i,entry in ipairs(config or {}) do
+ if (entry[1] == "server" or entry[1] == "poll" or entry[1] == "skip") and entry[2] == entryname then
+ return entry
+ end
+ end
+ end
+ return nil
+end
+
+local function writeentryline(entrystruct, entryline)
+ if not entrystruct and not entryline then
+ return
+ end
+
+ -- If there is a structure, create the entryline array
+ if entrystruct then
+ entryline = entryline or {}
+ if entrystruct.value.enabled.value then
+ entryline[1] = "poll"
+ else
+ entryline[1] = "skip"
+ end
+ entryline[2] = entrystruct.value.remotehost.value
+ local reverseentry = {}
+ for i,word in ipairs(entryline) do reverseentry[word] = i end
+ -- server options must come before user options, so add user option to end
+ -- and add server options just after remotehost (3)
+ -- start with the user options so we can add to the end and not mess up reverseentry
+ -- this means that we do method last
+ if reverseentry["user"] or reverseentry["username"] then
+ entryline[(reverseentry["user"] or reverseentry["username"])+1] = entrystruct.value.remotemailbox.value
+ else
+ entryline[#entryline+1] = "username"
+ entryline[#entryline+1] = entrystruct.value.remotemailbox.value
+ end
+ if reverseentry["pass"] or reverseentry["password"] then
+ entryline[(reverseentry["pass"] or reverseentry["password"])+1] = '"'..entrystruct.value.remotepassword.value..'"'
+ else
+ entryline[#entryline+1] = "password"
+ entryline[#entryline+1] = '"'..entrystruct.value.remotepassword.value..'"'
+ end
+ if reverseentry["smtphost"] then
+ entryline[reverseentry["smtphost"]+1] = entrystruct.value.localhost.value
+ else
+ entryline[#entryline+1] = "smtphost"
+ entryline[#entryline+1] = entrystruct.value.localhost.value
+ end
+ -- add in some user options
+ if not reverseentry["rewrite"] then
+ entryline[#entryline+1] = "no"
+ entryline[#entryline+1] = "rewrite"
+ end
+ if not reverseentry["fetchall"] then
+ entryline[#entryline+1] = "fetchall"
+ end
+ -- now handle the method, localmailbox, and localdomain
+ if entrystruct.value.method.value == "pop3domain" then
+ if reverseentry["to"] then
+ entryline[reverseentry["to"]+1] = "*"
+ else
+ entryline[#entryline+1] = "to"
+ entryline[#entryline+1] = "*"
+ end
+ if reverseentry["smtpaddress"] then
+ entryline[reverseentry["smtpaddress"]+1] = entrystruct.value.localdomain.value
+ else
+ entryline[#entryline+1] = "smtpaddress"
+ entryline[#entryline+1] = entrystruct.value.localdomain.value
+ end
+ if reverseentry["is"] then
+ -- THIS MESSES UP reverseentry FOR THE USER OPTIONS
+ table.remove(entryline, reverseentry["is"])
+ table.remove(entryline, reverseentry["is"])
+ end
+ if reverseentry["proto"] or reverseentry["protocol"] then
+ entryline[(reverseentry["proto"] or reverseentry["protocol"])+1] = "pop3"
+ else
+ -- THIS MESSES UP reverseentry FOR EVERYTHING
+ table.insert(entryline, 3, "pop3")
+ table.insert(entryline, 3, "protocol")
+ -- FIX reverseentry
+ reverseentry = {}
+ for i,word in ipairs(entryline) do reverseentry[word] = i end
+ end
+ if reverseentry["local"] or reverseentry["localdomains"] then
+ entryline[(reverseentry["local"] or reverseentry["localdomains"])+1] = entrystruct.value.localdomain.value
+ else
+ -- THIS MESSES UP reverseentry FOR EVERYTHING
+ table.insert(entryline, 3, entrystruct.value.localdomain.value)
+ table.insert(entryline, 3, "localdomains")
+ end
+ if not reverseentry["dns"] then
+ -- THIS MESSES UP reverseentry FOR EVERYTHING
+ table.insert(entryline, 3, "dns")
+ table.insert(entryline, 3, "no")
+ end
+ else
+ if reverseentry["is"] then
+ entryline[reverseentry["is"]+1] = entrystruct.value.localmailbox.value
+ else
+ entryline[#entryline+1] = "is"
+ entryline[#entryline+1] = entrystruct.value.localmailbox.value
+ end
+ if reverseentry["to"] then
+ -- THIS MESSES UP reverseentry FOR THE USER OPTIONS
+ table.remove(entryline, reverseentry["to"])
+ table.remove(entryline, reverseentry["to"])
+ -- FIX reverseentry
+ reverseentry = {}
+ for i,word in ipairs(entryline) do reverseentry[word] = i end
+ end
+ if reverseentry["smtpaddress"] then
+ -- THIS MESSES UP reverseentry FOR THE USER OPTIONS
+ table.remove(entryline, reverseentry["smtpaddress"])
+ table.remove(entryline, reverseentry["smtpaddress"])
+ end
+ if reverseentry["proto"] or reverseentry["protocol"] then
+ entryline[(reverseentry["proto"] or reverseentry["protocol"])+1] = entrystruct.value.method.value
+ else
+ -- THIS MESSES UP reverseentry FOR EVERYTHING
+ table.insert(entryline, 3, entrystruct.value.method.value)
+ table.insert(entryline, 3, "protocol")
+ -- FIX reverseentry
+ reverseentry = {}
+ for i,word in ipairs(entryline) do reverseentry[word] = i end
+ end
+ if reverseentry["local"] or reverseentry["localdomains"] then
+ -- THIS MESSES UP reverseentry FOR EVERYTHING
+ table.remove(entryline, (reverseentry["local"] or reverseentry["localdomains"]))
+ table.remove(entryline, (reverseentry["local"] or reverseentry["localdomains"]))
+ end
+ if not reverseentry["dns"] then
+ -- THIS MESSES UP reverseentry FOR EVERYTHING
+ table.insert(entryline, 3, "dns")
+ table.insert(entryline, 3, "no")
+ end
+ end
+
+ -- remove here and there
+ for i=#entryline,1,-1 do
+ if entryline[i] == "here" or entryline[i] == "there" then
+ table.remove(entryline, i)
+ end
+ end
+ end
+
+ local file = fs.read_file(configfile)
+ local lines = {file}
+
+ if entryline and entryline.linenum then
+ -- Split the file to remove the line
+ local startchar, endchar = string.match(file, string.rep("[^\n]*\n", entryline.linenum-1) .. "()[^\n]*\n()")
+ if startchar and endchar then
+ lines[1] = string.sub(file, 1, startchar-1)
+ lines[2] = string.sub(file, endchar, -1)
+ end
+ end
+ if entryline and entrystruct then
+ table.insert(lines, 2, table.concat(entryline," ").."\n")
+ end
+
+ fs.write_file(configfile, string.gsub(table.concat(lines), "\n+$", ""))
+ posix.chmod(configfile, "rwx--x---")
+ config = nil
+end
+
+local function validateentry(entry)
+ local success = true
+ success = modelfunctions.validateselect(entry.value.method) and success
+ if entry.value.remotehost.value == "" then
+ entry.value.remotehost.errtxt = "Invalid entry - cannot be blank"
+ success = false
+ elseif string.find(entry.value.remotehost.value, "[^%w.-]") then
+ entry.value.remotehost.errtxt = "Invalid entry - may only contain alphanumeric, '.', or '-'"
+ success = false
+ end
+ if entry.value.remotemailbox.value == "" then
+ entry.value.remotemailbox.errtxt = "Invalid entry - cannot be blank"
+ success = false
+ elseif string.find(entry.value.remotemailbox.value, "[^%w.-_@]") then
+ entry.value.remotemailbox.errtxt = "Invalid entry"
+ success = false
+ end
+ if entry.value.remotepassword.value == "" then
+ entry.value.remotepassword.errtxt = "Invalid entry - cannot be blank"
+ success = false
+ elseif string.find(entry.value.remotepassword.value, "%s") then
+ entry.value.remotepassword.errtxt = "Invalid entry - cannot contain whitespace"
+ success = false
+ end
+ if entry.value.localhost.value == "" then
+ entry.value.localhost.errtxt = "Invalid entry - cannot be blank"
+ success = false
+ elseif string.find(entry.value.localhost.value, "[^%w.-]") then
+ entry.value.localhost.errtxt = "Invalid entry - may only contain alphanumeric, '.', or '-'"
+ success = false
+ end
+ if entry.value.method.value == "pop3domain" and entry.value.localmailbox.value ~= "" then
+ entry.value.localmailbox.errtxt = "Cannot define local mailbox for pop3domain method"
+ success = false
+ elseif entry.value.method.value ~= "pop3domain" and entry.value.localmailbox.value == "" then
+ entry.value.localmailbox.errtxt = "Invalid entry - cannot be blank"
+ success = false
+ elseif string.find(entry.value.localmailbox.value, "[^%w.-_@]") then
+ entry.value.localmailbox.errtxt = "Invalid entry"
+ success = false
+ end
+ if entry.value.method.value ~= "pop3domain" and entry.value.localdomain.value ~= "" then
+ entry.value.localdomain.errtxt = "Cannot define local domain unless pop3domain method"
+ success = false
+ elseif entry.value.method.value == "pop3domain" and entry.value.localdomain.value == "" then
+ entry.value.localdomain.errtxt = "Invalid entry - cannot be blank"
+ success = false
+ elseif string.find(entry.value.localdomain.value, "[^%w.-]") then
+ entry.value.localdomain.errtxt = "Invalid entry - may only contain alphanumeric, '.', or '-'"
+ success = false
+ end
+
+ return success, entry
+end
+
+local function validateconfig(conf)
+ local success = true
+ if not validator.is_integer(conf.value.interval.value) then
+ conf.value.interval.errtxt = "Invalid entry - must be an integer number"
+ success = false
+ end
+ if string.find(conf.value.postmaster.value, "[^%w%.%-_@]") then
+ conf.value.postmaster.errtxt = "Invalid entry"
+ success = false
+ end
+ return success, conf
end
-- ################################################################################
-- PUBLIC FUNCTIONS
function startstop_service(action)
- return modelfunctions.startstop_service(processname, action)
+ local cmd
+ if action:lower() == "run" then
+ cmd = "/usr/bin/fetchmail -d0 -v 2>&1"
+ elseif action:lower() == "test" then
+ cmd = "/usr/bin/fetchmail -d0 -v -k 2>&1"
+ else
+ return modelfunctions.startstop_service(processname, action)
+ end
+ local f = io.popen(cmd)
+ cmdresult = f:read("*a")
+ f:close()
+ return cfe({ value=cmdresult, label="Start/Stop result" })
end
function getstatus()
@@ -196,52 +314,175 @@ end
function update_filecontent(filedetails)
filedetails.value.filename.value = configfile
-- FIXME - validation
- return modelfunctions.setfiledetails(filedetails)
+ local retvel = modelfunctions.setfiledetails(filedetails)
+ posix.chmod(configfile, "rwx--x---")
+ config = nil
+ return retval
end
function getconfig()
- local config = {}
- local mailboxes,configcontent_postmaster,configcontent_etrn = read_config()
- if not (fs.is_file(configfile)) then
- config["configfile"] = "Config file '".. configfile .. "' is missing!"
- end
-
- config["debug"] = cfe({
- name="debug",
- label = "Debug info",
- type = "longtext",
- value = configcontent,
- })
-
- -- Next section selects which configurations we should show to the user
- config["freq"] = cfe({
- name="freq",
- label = "Check mail once every",
- type = "select",
- value = "123",
- option = {"15min", "hour","day",},
- })
- config["mailboxes"] = cfe({
- name="mailboxes",
- label = "Mailboxes",
- value = mailboxes,
- })
- config["postmaster"] = cfe({
- name="postmaster",
- label = "Postmaster",
- value = configcontent_postmaster["POSTMASTER"],
- })
- config["etrnremote"] = cfe({
- name="etrnremote",
- label = "Remote server",
- value = configcontent_etrn["ETRNSMTPHOST"],
- })
- config["etrnquedomain"] = cfe({
- name="etrnquedomain",
- label = "Queued domain",
- value = configcontent_etrn["ETRNDOMAIN"],
- })
-
- return config
+ local interval = cfe({ value=60, label="Polling Interval", descr="Interval in seconds" })
+ local postmaster = cfe({ label="Postmaster", descr="If defined, undeliverable mail is sent to this account, otherwise it is discarded" })
+ local bounceerrors = cfe({ type="boolean", value=true, label="Bounce Errors", descr="Bounce errors back to the sender or send them to the postmaster" })
+
+ config = config or parseconfigfile(fs.read_file(configfile))
+ for i,entry in ipairs(config or {}) do
+ if entry[2] == "postmaster" and entry[1] == "set" then
+ postmaster.value = entry[3] or ""
+ elseif entry[3] == "bouncemail" and entry[2] == "no" and entry[1] == "set" then
+ bounceerrors.value = false
+ end
+ end
+
+ local confd = getopts.getoptsfromfile(confdfile, "", "polling_period")
+ if confd then
+ interval.value = confd
+ end
+
+ return cfe({ type="group", value={interval=interval, postmaster=postmaster, bounceerrors=bounceerrors}, label="Fetchmail Global Config" })
+end
+
+function updateconfig(conf)
+ local success, conf = validateconfig(conf)
+
+ if success then
+ local file = fs.read_file(configfile)
+ local foundpostmaster, foundbounceerrors
+ local lines = {}
+ for line in string.gmatch(file, "([^\n]*\n?)") do
+ if not foundpostmaster and string.match(line, "^%s*set%s+postmaster%s") then
+ foundpostmaster = true
+ line = "set postmaster "..conf.value.postmaster.value.."\n"
+ elseif not foundbounceerrors and string.match(line, "^%s*set%s+no%s+bouncemail%s") then
+ foundbounceerrors = true
+ if conf.value.bounceerrors.value then
+ line = nil
+ end
+ end
+ lines[#lines + 1] = line
+ end
+ if not foundpostmaster then
+ table.insert(lines, 1, "set postmaster "..conf.value.postmaster.value.."\n")
+ end
+ if not foundbounceerrors and not conf.value.bounceerrors.value then
+ table.insert(lines, 1, "set no bouncemail\n")
+ end
+
+ fs.write_file(configfile, table.concat(lines))
+ posix.chmod(configfile, "rwx--x---")
+ config = nil
+
+ getopts.setoptsinfile(confdfile, "", "polling_period", '"'..conf.value.interval.value..'"')
+ else
+ conf.errtxt = "Failed to set configuration"
+ end
+
+ return conf
+end
+
+function readentries()
+ local entries = cfe({ type="structure", value={}, label="List of Fetchmail entries" })
+ config = config or parseconfigfile(fs.read_file(configfile))
+ for i,entry in ipairs(config or {}) do
+ if (entry[1] == "server" or entry[1] == "poll" or entry[1] == "skip") and entry[2] then
+ local reverseentry = {}
+ for i,word in ipairs(entry) do reverseentry[word] = i end
+ local method = "error"
+ if reverseentry["local"] or reverseentry["localdomains"] then
+ method = "pop3domain"
+ elseif reverseentry["proto"] or reverseentry["protocol"] then
+ method = entry[(reverseentry["proto"] or reverseentry["protocol"])+1] or method
+ end
+ local enabled = true
+ if entry[1] == "skip" then enabled=false end
+ table.insert(entries.value, {entry=entry[2], method=method, enabled=enabled})
+ end
+ end
+ return entries
end
+function readentry(entryname)
+ local enabled = cfe({ type="boolean", value=true, label="Enable" })
+ local method = cfe({ type="select", value="pop3", label="Method", option=methods })
+ local remotehost = cfe({ value=entryname, label="Remote Host" })
+ local remotemailbox = cfe({ label="Remote Mailbox" })
+ local remotepassword = cfe({ label="Password" })
+ local localhost = cfe({ label="Local Host" })
+ local localmailbox = cfe({ label="Local Mailbox" })
+ local localdomain = cfe({ label="Local Domain" })
+
+ local entry = findentryline(entryname)
+ if entry then
+ if entry[1] == "skip" then
+ enabled.value = false
+ end
+ local reverseentry = {}
+ for i,word in ipairs(entry) do reverseentry[word] = i end
+ if reverseentry["local"] or reverseentry["localdomains"] then
+ localdomain.value = entry[(reverseentry["local"] or reverseentry["localdomains"])+1] or localdomain.value
+ method.value = "pop3domain"
+ elseif reverseentry["proto"] or reverseentry["protocol"] then
+ method.value = entry[(reverseentry["proto"] or reverseentry["protocol"])+1] or method.value
+ end
+ if reverseentry["user"] or reverseentry["username"] then
+ remotemailbox.value = entry[(reverseentry["user"] or reverseentry["username"])+1] or remotemailbox.value
+ end
+ if reverseentry["pass"] or reverseentry["password"] then
+ remotepassword.value = string.sub(entry[(reverseentry["pass"] or reverseentry["password"])+1] or "", 2, -2) or remotepassword.value
+ end
+ if reverseentry["smtphost"] then
+ localhost.value = entry[reverseentry["smtphost"]+1] or localhost.value
+ end
+ if reverseentry["is"] then
+ localmailbox.value = entry[reverseentry["is"]+1] or localmailbox.value
+ end
+ end
+
+ return cfe({ type="group", value={enabled=enabled, method=method, remotehost=remotehost, remotemailbox=remotemailbox, remotepassword=remotepassword, localhost=localhost, localmailbox=localmailbox, localdomain=localdomain}, label="Fetchmail Entry" })
+end
+
+function updateentry(entrystruct)
+ local success, entrystruct = validateentry(entrystruct)
+ local entry = findentryline(entrystruct.value.remotehost.value)
+ if not entry then
+ entrystruct.value.remotehost.errtxt = "Entry not found"
+ success = false
+ end
+
+ if success then
+ writeentryline(entrystruct, entry)
+ else
+ entrystruct.errtxt = "Failed to update entry"
+ end
+
+ return entrystruct
+end
+
+function createentry(entrystruct)
+ local success, entrystruct = validateentry(entrystruct)
+ local entry = findentryline(entrystruct.value.remotehost.value)
+ if entry then
+ entrystruct.value.remotehost.errtxt = "Entry already exists"
+ success = false
+ end
+
+ if success then
+ writeentryline(entrystruct)
+ else
+ entrystruct.errtxt = "Failed to update entry"
+ end
+
+ return entrystruct
+end
+
+function deleteentry(entryname)
+ local retval = cfe({ value="Deleted entry", label="Delete Fetchmail Entry Result" })
+ local entry = findentryline(entryname)
+ if entry then
+ writeentryline(nil, entry)
+ else
+ retval.value = "Failed to delete entry - not found"
+ end
+
+ return retval
+end
diff --git a/fetchmail-startstop-html.lsp b/fetchmail-startstop-html.lsp
deleted file mode 120000
index 0ea2627..0000000
--- a/fetchmail-startstop-html.lsp
+++ /dev/null
@@ -1 +0,0 @@
-../startstop-html.lsp \ No newline at end of file
diff --git a/fetchmail.menu b/fetchmail.menu
index 1ebbda9..e7d8933 100644
--- a/fetchmail.menu
+++ b/fetchmail.menu
@@ -1,4 +1,4 @@
#CAT GROUP/DESC TAB ACTION
Networking 30Fetchmail Status status
-#Networking 30Fetchmail Config config
+Networking 30Fetchmail Config config
Networking 30Fetchmail Expert expert
diff --git a/fetchmail.roles b/fetchmail.roles
index a88ba43..2f61cd3 100644
--- a/fetchmail.roles
+++ b/fetchmail.roles
@@ -1,2 +1,4 @@
READ=fetchmail:status
-UPDATE=fetchmail:expert,fetchmail:startstop
+UPDATE=fetchmail:expert,fetchmail:startstop,fetchmail:config,fetchmail:editconfig,fetchmail:listentries,fetchmail:editentry
+DELETE=fetchmail:deleteentry
+CREATE=fetchmail:createentry