1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
|
local mymodule = {}
-- Load libraries
modelfunctions = require("modelfunctions")
posix = require("posix")
fs = require("acf.fs")
format = require("acf.format")
validator = require("acf.validator")
-- Set variables
local packagename = "fetchmail"
local processname = "fetchmail"
local configfile = "/etc/fetchmailrc"
local confdfile = "/etc/conf.d/fetchmail"
local config
local methods = {"pop3","imap","pop3domain", "etrn", }
-- ################################################################################
-- LOCAL FUNCTIONS
local function findkeywordsinentry(entry)
local reverseentry = {}
-- we can't just do a simple reverse table in case keywords are used as parameters (ie. password = password)
--for i,word in ipairs(entry) do reverseentry[word] = i end
-- so, have to parse word by word
-- the following is a list of the keywords we know about in this ACF
-- dns, fetchall, fetchdomains, is, local, localdomains, pass, password, proto, protocol, rewrite, smtphost, ssl, user, username, envelope
-- array of keywords that take at least one parameter (don't know how to handle multiple parameters)
local keywords = { "fetchdomains", "is", "local", "localdomains", "pass", "password", "proto", "protocol", "smtphost", "user", "username" }
local reversekeywords = {}
for i,word in ipairs(keywords) do reversekeywords[word] = i end
local i=0
while i<#entry do
i = i+1
reverseentry[entry[i]] = i
if reversekeywords[entry[i]] then
i = i+1
end
end
return reverseentry
end
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)+1, endword-1)
end
table.insert(retval[#retval], word)
offset = endword + 1
end
end
end
return retval
end
local function findentryline(entryname, method, remotemailbox, localdomain)
if entryname and entryname ~= "" then
config = config or parseconfigfile(fs.read_file(configfile) or "")
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
local reverseentry = findkeywordsinentry(entry)
-- For pop3domain, check the localdomain
if method == "pop3domain" and (reverseentry["local"] or reverseentry["localdomains"]) then
if entry[(reverseentry["local"] or reverseentry["localdomains"])+1] == localdomain then
return entry
end
elseif reverseentry["proto"] or reverseentry["protocol"] then
local protocol = entry[(reverseentry["proto"] or reverseentry["protocol"])+1]
-- For etrn, no further check
if method == "etrn" and protocol == "etrn" then
return entry
-- For pop3 and imap, check the username
elseif protocol == method and (method == "pop3" or method == "imap") then
if reverseentry["username"] and entry[reverseentry["username"]+1] == remotemailbox then
return entry
end
end
end
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
-- We'll use a reverseentry array to tell where entries are in entryline
local reverseentry = {}
if entryline then
reverseentry = findkeywordsinentry(entryline)
-- to handle entries that have multiple names
function equateentries(option1,option2)
if reverseentry[option1] then
reverseentry[option2] = reverseentry[option1]
else
reverseentry[option1] = reverseentry[option2]
end
end
equateentries("proto", "protocol")
equateentries("local", "localdomains")
equateentries("auth", "authenticate")
equateentries("user", "username")
equateentries("pass", "password")
else
entryline = {}
end
-- From http://fetchmail.berlios.de/fetchmail-man.html:
-- "All user options must begin with a user description (user or username option)
-- and follow all server descriptions and options."
-- So, what we'll do is build two new server/user option arrays.
-- Then, we'll add in the options we don't understand and combine the arrays.
-- So, our options will be in order and unknown ones will be at the end of each section.
local serveroptions = {}
local useroptions = {}
-- Here are some helper functions to set option values and delete from entryline
function setserveroption(option, value)
serveroptions[#serveroptions+1] = option
serveroptions[#serveroptions+1] = value
deleteoptionandvalue(option)
end
function setuseroption(option, value)
useroptions[#useroptions+1] = option
useroptions[#useroptions+1] = value
deleteoptionandvalue(option)
end
function deleteoption(option)
if reverseentry[option] then
entryline[reverseentry[option]] = nil
end
end
function deleteoptionandvalue(option)
if reverseentry[option] then
entryline[reverseentry[option]] = nil
entryline[reverseentry[option]+1] = nil
end
end
function deletenooption(option)
if reverseentry[option] then
entryline[reverseentry[option]] = nil
local test = entryline[reverseentry[option]-1]
if test and test == "no" then
entryline[reverseentry[option]-1] = nil
end
end
end
-- Now we can start to set stuff
-- First, the first two entries
if entrystruct.value.enabled.value then
serveroptions[1] = "poll"
else
serveroptions[1] = "skip"
end
serveroptions[2] = entrystruct.value.remotehost.value
entryline[1] = nil
entryline[2] = nil
-- remove here and there
if reverseentry.here then
entryline[reverseentry.here] = nil
end
if reverseentry.there then
entryline[reverseentry.there] = nil
end
-- Now we get to the interesting stuff
if entrystruct.value.method.value == "etrn" then
deleteoptionandvalue("username")
deleteoptionandvalue("password")
deleteoptionandvalue("is")
deleteoptionandvalue("smtphost")
deleteoption("ssl")
deletenooption("rewrite")
deleteoption("fetchall")
deletenooption("dns")
else -- Method not etrn
setuseroption("username", entrystruct.value.remotemailbox.value)
setuseroption("password", '"'..entrystruct.value.remotepassword.value..'"')
if entrystruct.value.method.value == "pop3domain" then
deleteoptionandvalue("is")
else
setuseroption("is", entrystruct.value.localmailbox.value)
end
setuseroption("smtphost", entrystruct.value.localhost.value)
if entrystruct.value.ssl.value and not reverseentry["ssl"] then
useroptions[#useroptions+1] = "ssl"
elseif not entrystruct.value.ssl.value and reverseentry["ssl"] then
entryline[reverseentry["ssl"]] = nil
end
if not reverseentry["rewrite"] then
useroptions[#useroptions+1] = "no"
useroptions[#useroptions+1] = "rewrite"
end
if not reverseentry["fetchall"] then
useroptions[#useroptions+1] = "fetchall"
end
if not reverseentry["dns"] then
serveroptions[#serveroptions+1] = "no"
serveroptions[#serveroptions+1] = "dns"
end
end
if entrystruct.value.method.value == "pop3domain" then
setserveroption("protocol", "pop3")
setserveroption("localdomains", entrystruct.value.localdomain.value)
setuseroption("to", "*")
setuseroption("smtpaddress", entrystruct.value.localdomain.value)
deleteoptionandvalue("fetchdomains")
elseif entrystruct.value.method.value == "etrn" then
setserveroption("protocol", entrystruct.value.method.value)
deleteoptionandvalue("localdomains")
deleteoptionandvalue("to")
deleteoptionandvalue("smtpaddress")
setuseroption("fetchdomains", entrystruct.value.localdomain.value)
else -- Method not pop3domain or etrn
setserveroption("protocol", entrystruct.value.method.value)
deleteoptionandvalue("localdomains")
deleteoptionandvalue("to")
deleteoptionandvalue("smtpaddress")
deleteoptionandvalue("fetchdomains")
end
-- envelope is tough because it may have a no before or one or two values after
-- first, delete any envelope option (preserving the count)
local envelopecount
if reverseentry["envelope"] then
if entryline[reverseentry["envelope"]-1] == "no" then
entryline[reverseentry["envelope"]-1] = nil
else
if validator.is_integer(entryline[reverseentry["envelope"]+1] or "") then
-- Keep the number if not changing envelope option
if entryline[reverseentry["envelope"]+2] == entrystruct.value.envelope.value then
envelopecount = entryline[reverseentry["envelope"]+1]
end
entryline[reverseentry["envelope"]+2] = nil
end
entryline[reverseentry["envelope"]+1] = nil
end
entryline[reverseentry["envelope"]] = nil
end
if entrystruct.value.method.value == "pop3domain" then
if entrystruct.value.envelope.value == "disabled" then
serveroptions[#serveroptions+1] = "no"
serveroptions[#serveroptions+1] = "envelope"
else
serveroptions[#serveroptions+1] = "envelope"
serveroptions[#serveroptions+1] = envelopecount
serveroptions[#serveroptions+1] = entrystruct.value.envelope.value
end
end
-- Now, insert the remaining options
for i=1,reverseentry["username"] or table.maxn(entryline) do
serveroptions[#serveroptions+1] = entryline[i]
end
for i=reverseentry["username"] or table.maxn(entryline), table.maxn(entryline) do
useroptions[#useroptions+1] = entryline[i]
end
local linenum = entryline.linenum
entryline = serveroptions
for i,val in ipairs(useroptions) do
entryline[#entryline+1] = val
end
entryline.linenum = linenum
end
local file = fs.read_file(configfile) or ""
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, "rw-------")
posix.chown(configfile, posix.getpasswd("fetchmail", "uid") or 0)
config = nil
end
local function validateentry(entry)
local success = true
function cannotbeblank(value)
if value.value == "" then
value.errtxt = "Invalid entry - cannot be blank"
success = false
end
end
function mustbeblank(value)
if value.value ~= "" then
value.errtxt = "Invalid entry - must be blank for this method"
success = false
end
end
success = modelfunctions.validateselect(entry.value.method) and success
success = modelfunctions.validateselect(entry.value.envelope) and success
if string.find(entry.value.remotehost.value, "[^%w%.%-]") then
entry.value.remotehost.errtxt = "Invalid entry - may only contain alphanumeric, '.', or '-'"
success = false
end
if string.find(entry.value.remotemailbox.value, "[^%w%.%-_@]") then
entry.value.remotemailbox.errtxt = "Invalid entry"
success = false
end
if string.find(entry.value.remotepassword.value, "%s") then
entry.value.remotepassword.errtxt = "Invalid entry - cannot contain whitespace"
success = false
end
if string.find(entry.value.localhost.value, "[^%w%.%-]") then
entry.value.localhost.errtxt = "Invalid entry - may only contain alphanumeric, '.', or '-'"
success = false
end
if string.find(entry.value.localmailbox.value, "[^%w%.%-_@]") then
entry.value.localmailbox.errtxt = "Invalid entry"
success = false
end
if string.find(entry.value.localdomain.value, "[^%w%.%-]") then
entry.value.localdomain.errtxt = "Invalid entry - may only contain alphanumeric, '.', or '-'"
success = false
end
cannotbeblank(entry.value.remotehost)
if entry.value.method.value == "etrn" then
mustbeblank(entry.value.remotemailbox)
mustbeblank(entry.value.remotepassword)
mustbeblank(entry.value.localhost)
mustbeblank(entry.value.localmailbox)
cannotbeblank(entry.value.localdomain)
else
cannotbeblank(entry.value.remotemailbox)
cannotbeblank(entry.value.remotepassword)
cannotbeblank(entry.value.localhost)
if entry.value.method.value == "pop3domain" then
mustbeblank(entry.value.localmailbox)
cannotbeblank(entry.value.localdomain)
else
cannotbeblank(entry.value.localmailbox)
mustbeblank(entry.value.localdomain)
end
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 mymodule.get_startstop(self, clientdata)
local actions = {"Run", "Test"}
return cfe({ type="group", label="Management", value={}, option=actions })
end
function mymodule.startstop_service(self, startstop, action)
if action and (action:lower() == "run" or action:lower() == "test") then
local cmd
if action:lower() == "run" then
cmd = "/usr/bin/fetchmail -d0 -v --nosyslog -f "..configfile
elseif action:lower() == "test" then
cmd = "/usr/bin/fetchmail -d0 -v -k --nosyslog -f "..configfile
end
startstop.descr, startstop.errtxt = modelfunctions.run_executable({"su", "-s", "/bin/sh", "-c", cmd, "-", "fetchmail"}, true)
else
startstop.errtxt = "Invalid action"
end
return startstop
end
function mymodule.getstatus()
return modelfunctions.getstatus(processname, packagename, "Fetchmail Status")
end
function mymodule.get_filedetails()
-- FIXME - validation
return modelfunctions.getfiledetails(configfile)
end
function mymodule.update_filecontent(self, filedetails)
-- FIXME - validation
local retval = modelfunctions.setfiledetails(self, filedetails, {configfile})
posix.chmod(configfile, "rw-------")
posix.chown(configfile, posix.getpasswd("fetchmail", "uid") or 0)
config = nil
return retval
end
function mymodule.getconfig()
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) or "")
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 = format.parse_ini_file(fs.read_file(confdfile) or "", "", "polling_period")
if confd then
interval.value = string.sub(confd, 2, -2)
end
return cfe({ type="group", value={interval=interval, postmaster=postmaster, bounceerrors=bounceerrors}, label="Fetchmail Global Config" })
end
function mymodule.updateconfig(self, conf)
local success, conf = validateconfig(conf)
if success then
local file = fs.read_file(configfile) or ""
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
if conf.value.postmaster.value ~= "" then
line = "set postmaster "..conf.value.postmaster.value.."\n"
else
line = nil
end
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, "rw-------")
posix.chown(configfile, posix.getpasswd("fetchmail", "uid") or 0)
config = nil
fs.write_file(confdfile, format.update_ini_file(fs.read_file(confdfile) or "", "", "polling_period", '"'..conf.value.interval.value..'"'))
else
conf.errtxt = "Failed to set configuration"
end
return conf
end
function mymodule.readentries()
local entries = cfe({ type="structure", value={}, label="List of Fetchmail entries" })
config = config or parseconfigfile(fs.read_file(configfile) or "")
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 = findkeywordsinentry(entry)
local method = "error"
local localdomain = ""
if reverseentry["local"] or reverseentry["localdomains"] then
method = "pop3domain"
if entry[(reverseentry["local"] or reverseentry["localdomains"])+1] then
localdomain = entry[(reverseentry["local"] or reverseentry["localdomains"])+1]
end
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
local username = ""
if reverseentry["username"] and entry[reverseentry["username"]+1] then
username = entry[reverseentry["username"]+1]
end
table.insert(entries.value, {remotehost=entry[2], method=method, enabled=enabled, remotemailbox=username, localdomain=localdomain})
end
end
return entries
end
function mymodule.readentry(entryname, meth, remotemailbx, localdom)
local enabled = cfe({ type="boolean", value=true, label="Enable", seq=2 })
local method = cfe({ type="select", value="pop3", label="Method", option=methods, seq=3 })
local remotehost = cfe({ value=entryname, label="Remote Host", seq=1 })
local remotemailbox = cfe({ label="Remote Mailbox", seq=4 })
local remotepassword = cfe({ label="Password", seq=5 })
local localhost = cfe({ label="Local Host", seq=7 })
local localmailbox = cfe({ label="Local Mailbox", seq=8 })
local localdomain = cfe({ label="Local Domain", seq=9 })
local ssl = cfe({ type="boolean", value=false, label="SSL Encryption", seq=6 })
local envelope = cfe({ type="select", value="X-Envelope-To", label="Envelope Mode", option={"X-Original-To", "Delivered-To", "X-Envelope-To", "Received", "disabled"}, seq=10 })
local entry = findentryline(entryname, meth, remotemailbx, localdom)
if entry then
remotehost.readonly = true
method.type = "text"
method.readonly = true
remotemailbox.readonly = true
localdomain.readonly = true
if entry[1] == "skip" then
enabled.value = false
end
local reverseentry = findkeywordsinentry(entry)
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 = entry[(reverseentry["pass"] or reverseentry["password"])+1] 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
if reverseentry["fetchdomains"] then
localdomain.value = entry[reverseentry["fetchdomains"]+1] or localdomain.value
end
if reverseentry["ssl"] then
ssl.value = true
end
if reverseentry["envelope"] then
if entry[reverseentry["envelope"]-1] == "no" then
envelope.value = "disabled"
elseif validator.is_integer(entry[reverseentry["envelope"]+1] or "") then
envelope.value = entry[reverseentry["envelope"]+2]
else
envelope.value = entry[reverseentry["envelope"]+1]
end
end
end
return cfe({ type="group", value={enabled=enabled, method=method, remotehost=remotehost, remotemailbox=remotemailbox, remotepassword=remotepassword, localhost=localhost, localmailbox=localmailbox, localdomain=localdomain, ssl=ssl, envelope=envelope}, label="Fetchmail Entry" })
end
function mymodule.updateentry(self, entrystruct)
local success, entrystruct = validateentry(entrystruct)
local entry = findentryline(entrystruct.value.remotehost.value, entrystruct.value.method.value, entrystruct.value.remotemailbox.value, entrystruct.value.localdomain.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 mymodule.createentry(self, entrystruct)
local success, entrystruct = validateentry(entrystruct)
local entry = findentryline(entrystruct.value.remotehost.value, entrystruct.value.method.value, entrystruct.value.remotemailbox.value, entrystruct.value.localdomain.value)
if entry then
entrystruct.value.remotehost.errtxt = "Entry already exists"
success = false
end
if success then
writeentryline(entrystruct)
else
entrystruct.errtxt = "Failed to create entry"
end
return entrystruct
end
function mymodule.get_deleteentry(self, clientdata)
local retval = {}
retval.remotehost = cfe({ value=clientdata.remotehost or "", label="Remote Host" })
retval.method = cfe({ type="select", value=clientdata.method or "pop3", label="Method", option=methods })
retval.remotemailbox = cfe({ value=clientdata.remotemailbox or "", label="Remote Mailbox" })
retval.localdomain = cfe({ value=clientdata.localdomain or "", label="Local Domain" })
return cfe({ type="group", value=retval, label="Delete Entry" })
end
function mymodule.deleteentry(self, ent)
local entry = findentryline(ent.value.remotehost.value, ent.value.method.value, ent.value.remotemailbox.value, ent.value.localdomain.value)
if entry then
writeentryline(nil, entry)
else
ent.errtxt = "Failed to delete entry - not found"
end
return ent
end
return mymodule
|