8ea2496173b6404f5e9b6c1192c31f41ea58c0ef
[project/luci.git] / applications / luci-app-privoxy / luasrc / model / cbi / privoxy.lua
1 -- Copyright 2014-2015 Christian Schoenebeck <christian dot schoenebeck at gmail dot com>
2 -- Licensed under the Apache License, Version 2.0
3
4 local NXFS = require "nixio.fs"
5 local SYS  = require "luci.sys"
6 local UTIL = require "luci.util"
7 local DISP = require "luci.dispatcher"
8 local DTYP = require "luci.cbi.datatypes"
9 local CTRL = require "luci.controller.privoxy"  -- this application's controller
10
11 -- Bootstrap theme needs 2 or 3 additional linefeeds for tab description for better optic
12 local HELP = [[<a href="http://www.privoxy.org/user-manual/config.html#%s" target="_blank">%s</a>]]
13
14 local VERINST = CTRL.ipkg_ver_installed("privoxy")
15 local VEROK   = CTRL.ipkg_ver_compare(VERINST,">=",CTRL.PRIVOXY_MIN)
16
17 local TITLE = [[<a href="javascript:alert(']]
18                 .. translate("Version Information")
19                 .. [[\n\nluci-app-privoxy]]
20                 .. [[\n\t]] .. translate("Version") .. [[:\t]]
21                 .. CTRL.ipkg_ver_installed("luci-app-privoxy")
22                 .. [[\n\nprivoxy ]] .. translate("required") .. [[:]]
23                 .. [[\n\t]] .. translate("Version") .. [[:\t]] .. CTRL.PRIVOXY_MIN .. [[ ]] .. translate("or higher")
24                 .. [[\n\nprivoxy ]] .. translate("installed") .. [[:]]
25                 .. [[\n\t]] .. translate("Version") .. [[:\t]] .. VERINST
26                 .. [[\n\n]]
27         .. [[')">]]
28         .. translate("Privoxy WEB proxy")
29         .. [[</a>]]
30
31 local DESC = translate("Privoxy is a non-caching web proxy with advanced filtering "
32                 .. "capabilities for enhancing privacy, modifying web page data and HTTP headers, "
33                 .. "controlling access, and removing ads and other obnoxious Internet junk.")
34                 .. [[<br /><strong>]]
35                 .. translate("For help use link at the relevant option")
36                 .. [[</strong>]]
37
38 -- Error handling if wrong privoxy version installed -- ########################
39 if not nixio.fs.access("/etc/config/privoxy") or not VEROK then
40         local f = SimpleForm("_no_config")
41         f.title = TITLE
42         f.description = DESC
43         f.embedded = true
44         f.submit = false
45         f.reset  = false
46
47         local s = f:section(SimpleSection)
48         s.title = [[<font color="red">]] .. [[<strong>]]
49                 .. translate("Software update required")
50                 .. [[</strong>]] .. [[</font>]]
51
52         local v    = s:option(DummyValue, "_update_needed")
53         v.titleref = DISP.build_url("admin", "system", "packages")
54         v.rawhtml  = true
55         v.value    = [[<h3><strong><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;]]
56                    .. translate("The currently installed 'privoxy' package is not supported by LuCI application.")
57                    .. [[<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;]]
58                    .. translate("required") .. ": " .. CTRL.PRIVOXY_MIN .. " *** ".. translate("installed") .. ": " .. VERINST
59                    .. [[<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;]]
60                    .. translate("Please update to the current version!")
61                    .. [[<br /><br /></strong></h3>]]
62         return f
63 end
64
65 -- cbi-map -- ##################################################################
66 local m = Map("privoxy")
67 m.title = TITLE
68 m.description = DESC
69 function m.commit_handler(self)
70         if self.changed then    -- changes ?
71                 os.execute("/etc/init.d/privoxy reload &")      -- reload configuration
72         end
73 end
74
75 -- cbi-section -- ##############################################################
76 local ns = m:section( NamedSection, "privoxy", "privoxy")
77
78 ns:tab("local",
79         translate("Local Set-up"),
80         translate("If you intend to operate Privoxy for more users than just yourself, "
81                 .. "it might be a good idea to let them know how to reach you, what you block "
82                 .. "and why you do that, your policies, etc.") )
83 local function err_tab_local(title, msg)
84         return string.format(translate("Local Set-up") .. " - %s: %s", title, msg )
85 end
86
87 ns:tab("filter",
88         translate("Files and Directories"),
89         translate("Privoxy can (and normally does) use a number of other files "
90                 .. "for additional configuration, help and logging. This section of "
91                 .. "the configuration file tells Privoxy where to find those other files.") )
92 local function err_tab_filter(title, msg)
93         return string.format(translate("Files and Directories") .. " - %s: %s", title, msg )
94 end
95
96 ns:tab("access",
97         translate("Access Control"),
98         translate("This tab controls the security-relevant aspects of Privoxy's configuration.") )
99 local function err_tab_access(title, msg)
100         return string.format(translate("Access Control") .. " - %s: %s", title, msg )
101 end
102
103 ns:tab("forward",
104         translate("Forwarding"),
105         translate("Configure here the routing of HTTP requests through a chain of multiple proxies. "
106                 .. "Note that parent proxies can severely decrease your privacy level. "
107                 .. "Also specified here are SOCKS proxies.") )
108
109 ns:tab("misc",
110         translate("Miscellaneous"),
111         nil)
112 local function err_tab_misc(self, msg)
113         return string.format(translate("Miscellaneous") .. " - %s: %s", self.title_base, msg )
114 end
115
116 ns:tab("debug",
117         translate("Logging"),
118         nil )
119
120 ns:tab("logview",
121         translate("Log File Viewer"),
122         nil )
123
124 -- tab: local -- ###############################################################
125
126 -- start/stop button -----------------------------------------------------------
127 local btn       = ns:taboption("local", Button, "_startstop")
128 btn.title       = translate("Start / Stop")
129 btn.description = translate("Start/Stop Privoxy WEB Proxy")
130 btn.template    = "privoxy/detail_startstop"
131 function btn.cfgvalue(self, section)
132         local pid = CTRL.get_pid(true)
133         if pid > 0 then
134                 btn.inputtitle  = "PID: " .. pid
135                 btn.inputstyle  = "reset"
136                 btn.disabled    = false
137         else
138                 btn.inputtitle  = translate("Start")
139                 btn.inputstyle  = "apply"
140                 btn.disabled    = false
141         end
142         return true
143 end
144
145 -- enabled ---------------------------------------------------------------------
146 local ena       = ns:taboption("local", Flag, "_enabled")
147 ena.title       = translate("Enabled")
148 ena.description = translate("Enable/Disable autostart of Privoxy on system startup and interface events")
149 ena.orientation = "horizontal" -- put description under the checkbox
150 ena.rmempty     = false
151 function ena.cfgvalue(self, section)
152         return (SYS.init.enabled("privoxy")) and "1" or "0"
153 end
154 function ena.validate(self, value)
155         error("Validate " .. value)
156 end
157 function ena.write(self, section, value)
158         --error("Write " .. value)
159         if value == "1" then
160                 return SYS.init.enable("privoxy")
161         else
162                 return SYS.init.disable("privoxy")
163         end
164 end
165
166 -- hostname --------------------------------------------------------------------
167 local hn        = ns:taboption("local", Value, "hostname")
168 hn.title        = string.format(HELP, "HOSTNAME", "Hostname" )
169 hn.description  = translate("The hostname shown on the CGI pages.")
170 hn.placeholder  = SYS.hostname()
171 hn.optional     = true
172 hn.rmempty      = true
173
174 -- user-manual -----------------------------------------------------------------
175 local um        = ns:taboption("local", Value, "user_manual")
176 um.title        = string.format(HELP, "USER-MANUAL", "User Manual" )
177 um.description  = translate("Location of the Privoxy User Manual.")
178 um.placeholder  = "http://www.privoxy.org/user-manual/"
179 um.optional     = true
180 um.rmempty      = true
181
182 -- admin-address ---------------------------------------------------------------
183 local aa        = ns:taboption("local", Value, "admin_address")
184 aa.title_base   = "Admin Email"
185 aa.title        = string.format(HELP, "ADMIN-ADDRESS", aa.title_base )
186 aa.description  = translate("An email address to reach the Privoxy administrator.")
187 aa.placeholder  = "privoxy.admin@example.com"
188 aa.optional     = true
189 aa.rmempty      = true
190 function aa.validate(self, value)
191         if not value or #value == 0 then
192                 return ""
193         end
194         if not (value:match("[A-Za-z0-9%.%%%+%-]+@[A-Za-z0-9%.%%%+%-]+%.%w%w%w?%w?")) then
195                 return nil, err_tab_local(self.title_base, translate("Invalid email address") )
196         end
197         return value
198 end
199
200 -- proxy-info-url --------------------------------------------------------------
201 local piu       = ns:taboption("local", Value, "proxy_info_url")
202 piu.title       = string.format(HELP, "PROXY-INFO-URL", "Proxy Info URL" )
203 piu.description = translate("A URL to documentation about the local Privoxy setup, configuration or policies.")
204 piu.optional    = true
205 piu.rmempty     = true
206
207 -- trust-info-url --------------------------------------------------------------
208 local tiu       = ns:taboption("local", Value, "trust_info_url")
209 tiu.title       = string.format(HELP, "TRUST-INFO-URL", "Trust Info URLs" )
210 tiu.description = translate("A URL to be displayed in the error page that users will see if access to an untrusted page is denied.")
211                 .. [[<br /><strong>]]
212                 .. translate("The value of this option only matters if the experimental trust mechanism has been activated.")
213                 .. [[</strong>]]
214 tiu.optional    = true
215 tiu.rmepty      = true
216
217 -- tab: filter -- ##############################################################
218
219 -- logdir ----------------------------------------------------------------------
220 local ld        = ns:taboption("filter", Value, "logdir")
221 ld.title_base   = "Log Directory"
222 ld.title        = string.format(HELP, "LOGDIR", ld.title_base )
223 ld.description  = translate("The directory where all logging takes place (i.e. where the logfile is located).")
224                 .. [[<br />]]
225                 .. translate("No trailing '/', please.")
226 ld.default      = "/var/log"
227 ld.rmempty      = false
228 function ld.validate(self, value)
229         if not value or #value == 0 then
230                 return nil, err_tab_filter(self.title_base, translate("Mandatory Input: No Directory given!") )
231         elseif not NXFS.access(value) then
232                 return nil, err_tab_filter(self.title_base, translate("Directory does not exist!") )
233         else
234                 return value
235         end
236 end
237
238 -- logfile ---------------------------------------------------------------------
239 local lf        = ns:taboption("filter", Value, "logfile")
240 lf.title_base   = "Log File"
241 lf.title        = string.format(HELP, "LOGFILE", lf.title_base )
242 lf.description  = translate("The log file to use. File name, relative to log directory.")
243 lf.default      = "privoxy.log"
244 lf.rmempty      = false
245 function lf.validate(self, value)
246         if not value or #value == 0 then
247                 return nil, err_tab_filter(self.title_base, translate("Mandatory Input: No File given!") )
248         else
249                 return value
250         end
251 end
252
253 -- confdir ---------------------------------------------------------------------
254 local cd        = ns:taboption("filter", Value, "confdir")
255 cd.title_base   = "Configuration Directory"
256 cd.title        = string.format(HELP, "CONFDIR", cd.title_base )
257 cd.description  = translate("The directory where the other configuration files are located.")
258                 .. [[<br />]]
259                 .. translate("No trailing '/', please.")
260 cd.default      = "/etc/privoxy"
261 cd.rmempty      = false
262 function cd.validate(self, value)
263         if not value or #value == 0 then
264                 return nil, err_tab_filter(self.title_base, translate("Mandatory Input: No Directory given!") )
265         elseif not NXFS.access(value) then
266                 return nil, err_tab_filter(self.title_base, translate("Directory does not exist!") )
267         else
268                 return value
269         end
270 end
271
272 -- templdir --------------------------------------------------------------------
273 local tld       = ns:taboption("filter", Value, "templdir")
274 tld.title_base  = "Template Directory"
275 tld.title       = string.format(HELP, "TEMPLDIR", tld.title_base )
276 tld.description = translate("An alternative directory where the templates are loaded from.")
277                 .. [[<br />]]
278                 .. translate("No trailing '/', please.")
279 tld.placeholder = "/etc/privoxy/templates"
280 tld.rmempty     = true
281 function tld.validate(self, value)
282         if not NXFS.access(value) then
283                 return nil, err_tab_filter(self.title_base, translate("Directory does not exist!") )
284         else
285                 return value
286         end
287 end
288
289 -- temporary-directory ---------------------------------------------------------
290 local td        = ns:taboption("filter", Value, "temporary_directory")
291 td.title_base   = "Temporary Directory"
292 td.title        = string.format(HELP, "TEMPORARY-DIRECTORY", td.title_base )
293 td.description  = translate("A directory where Privoxy can create temporary files.")
294                 .. [[<br /><strong>]]
295                 .. translate("Only when using 'external filters', Privoxy has to create temporary files.")
296                 .. [[</strong>]]
297 td.rmempty      = true
298
299 -- actionsfile -----------------------------------------------------------------
300 local af        = ns:taboption("filter", DynamicList, "actionsfile")
301 af.title_base   = "Action Files"
302 af.title        = string.format(HELP, "ACTIONSFILE", af.title_base)
303 af.description  = translate("The actions file(s) to use. Multiple actionsfile lines are permitted, and are in fact recommended!")
304                 .. [[<br /><strong>match-all.action := </strong>]]
305                 .. translate("Actions that are applied to all sites and maybe overruled later on.")
306                 .. [[<br /><strong>default.action := </strong>]]
307                 .. translate("Main actions file")
308                 .. [[<br /><strong>user.action := </strong>]]
309                 .. translate("User customizations")
310 af.rmempty      = false
311 function af.validate(self, value)
312         if not value or #value == 0 then
313                 return nil, err_tab_access(self.title_base, translate("Mandatory Input: No files given!") )
314         end
315         local confdir = cd:formvalue(ns.section)
316         local err     = false
317         local file    = ""
318         if type(value) == "table" then
319                 local x
320                 for _, x in ipairs(value) do
321                         if x and #x > 0 then
322                                 if not NXFS.access(confdir .."/".. x) then
323                                         err  = true
324                                         file = x
325                                         break   -- break/leave for on error
326                                 end
327                         end
328                 end
329         else
330                 if not NXFS.access(confdir .."/".. value) then
331                         err  = true
332                         file = value
333                 end
334         end
335         if err then
336                 return nil, string.format(err_tab_filter(self.title_base, translate("File '%s' not found inside Configuration Directory") ), file)
337         end
338         return value
339 end
340
341 -- filterfile ------------------------------------------------------------------
342 local ff        = ns:taboption("filter", DynamicList, "filterfile")
343 ff.title_base   = "Filter files"
344 ff.title        = string.format(HELP, "FILTERFILE", ff.title_base )
345 ff.description  = translate("The filter files contain content modification rules that use regular expressions.")
346 ff.rmempty      = false
347 function ff.validate(self, value)
348         if not value or #value == 0 then
349                 return nil, err_tab_access(self.title_base, translate("Mandatory Input: No files given!") )
350         end
351         local confdir = cd:formvalue(ns.section)
352         local err     = false
353         local file    = ""
354         if type(value) == "table" then
355                 local x
356                 for _, x in ipairs(value) do
357                         if x and #x > 0 then
358                                 if not NXFS.access(confdir .."/".. x) then
359                                         err  = true
360                                         file = x
361                                         break   -- break/leave for on error
362                                 end
363                         end
364                 end
365         else
366                 if not NXFS.access(confdir .."/".. value) then
367                         err  = true
368                         file = value
369                 end
370         end
371         if err then
372                 return nil, string.format(err_tab_filter(self.title_base, translate("File '%s' not found inside Configuration Directory") ), file )
373         end
374         return value
375 end
376
377 -- trustfile -------------------------------------------------------------------
378 local tf        = ns:taboption("filter", Value, "trustfile")
379 tf.title_base   = "Trust file"
380 tf.title        = string.format(HELP, "TRUSTFILE", tf.title_base )
381 tf.description  = translate("The trust mechanism is an experimental feature for building white-lists "
382                 .."and should be used with care.")
383                 .. [[<br /><strong>]]
384                 .. translate("It is NOT recommended for the casual user.")
385                 .. [[</strong>]]
386 tf.placeholder  = "user.trust"
387 tf.rmempty      = true
388 function tf.validate(self, value)
389         local confdir = cd:formvalue(ns.section)
390         local err     = false
391         local file    = ""
392         if type(value) == "table" then
393                 local x
394                 for _, x in ipairs(value) do
395                         if x and #x > 0 then
396                                 if not NCFS.access(confdir .."/".. x) then
397                                         err  = true
398                                         file = x
399                                         break   -- break/leave for on error
400                                 end
401                         end
402                 end
403         else
404                 if not NXFS.access(confdir .."/".. value) then
405                         err  = true
406                         file = value
407                 end
408         end
409         if err then
410                 return nil, string.format(err_tab_filter(self.title_base, translate("File '%s' not found inside Configuration Directory") ), file )
411         end
412         return value
413 end
414
415 -- tab: access -- ##############################################################
416
417 -- listen-address --------------------------------------------------------------
418 local la        = ns:taboption("access", DynamicList, "listen_address")
419 la.title_base   = "Listen addresses"
420 la.title        = string.format(HELP, "LISTEN-ADDRESS", la.title_base )
421 la.description  = translate("The address and TCP port on which Privoxy will listen for client requests.")
422                 .. [[<br />]]
423                 .. translate("Syntax: ")
424                 .. "IPv4:Port / [IPv6]:Port / Host:Port"
425 la.default      = "127.0.0.1:8118"
426 la.rmempty      = false
427 function la.validate(self, value)
428         if not value or #value == 0 then
429                 return nil, err_tab_access(self.title_base, translate("Mandatory Input: No Data given!") )
430         end
431
432         local function check_value(v)
433                 local _ret = UTIL.split(v, "]:")
434                 local _ip
435                 if _ret[2] then -- ip6 with port
436                         _ip   = string.gsub(_ret[1], "%[", "")  -- remove "[" at beginning
437                         if not DTYP.ip6addr(_ip) then
438                                 return translate("Mandatory Input: No valid IPv6 address given!")
439                         elseif not DTYP.port(_ret[2]) then
440                                 return translate("Mandatory Input: No valid Port given!")
441                         else
442                                 return nil
443                         end
444                 end
445                 _ret = UTIL.split(v, ":")
446                 if not _ret[2] then
447                         return translate("Mandatory Input: No Port given!")
448                 end
449                 if #_ret[1] > 0 and not DTYP.host(_ret[1]) then -- :8118 is valid address
450                         return translate("Mandatory Input: No valid IPv4 address or host given!")
451                 elseif not DTYP.port(_ret[2]) then
452                         return translate("Mandatory Input: No valid Port given!")
453                 else
454                         return nil
455                 end
456         end
457
458         local err   = ""
459         local entry = ""
460         if type(value) == "table" then
461                 local x
462                 for _, x in ipairs(value) do
463                         if x and #x > 0 then
464                                 err = check_value(x)
465                                 if err then
466                                         entry = x
467                                         break
468                                 end
469                         end
470                 end
471         else
472                 err = check_value(value)
473                 entry = value
474         end
475         if err then
476                 return nil, string.format(err_tab_access(self.title_base, err .. " - %s"), entry )
477         end
478         return value
479 end
480
481 -- permit-access ---------------------------------------------------------------
482 local pa        = ns:taboption("access", DynamicList, "permit_access")
483 pa.title        = string.format(HELP, "ACLS", "Permit access" )
484 pa.description  = translate("Who can access what.")
485                 .. [[<br /><strong>]]
486                 .. translate("Please read Privoxy manual for details!")
487                 .. [[</strong>]]
488 pa.rmempty      = true
489
490 -- deny-access -----------------------------------------------------------------
491 local da        = ns:taboption("access", DynamicList, "deny_access")
492 da.title        = string.format(HELP, "ACLS", "Deny Access" )
493 da.description  = translate("Who can access what.")
494                 .. [[<br /><strong>]]
495                 .. translate("Please read Privoxy manual for details!")
496                 .. [[</strong>]]
497 da.rmempty      = true
498
499 -- buffer-limit ----------------------------------------------------------------
500 local bl        = ns:taboption("access", Value, "buffer_limit")
501 bl.title_base   = "Buffer Limit"
502 bl.title        = string.format(HELP, "BUFFER-LIMIT", bl.title_base )
503 bl.description  = translate("Maximum size (in KB) of the buffer for content filtering.")
504                 .. [[<br />]]
505                 .. translate("Value range 1 to 4096, no entry defaults to 4096")
506 bl.default      = 4096
507 bl.rmempty      = true
508 function bl.validate(self, value)
509         local v = tonumber(value)
510         if not v then
511                 return nil, err_tab_access(self.title_base, translate("Value is not a number") )
512         elseif v < 1 or v > 4096 then
513                 return nil, err_tab_access(self.title_base, translate("Value not between 1 and 4096") )
514         elseif v == self.default then
515                 return ""       -- dont need to save default
516         end
517         return value
518 end
519
520 -- toggle ----------------------------------------------------------------------
521 local tgl       = ns:taboption("access", Flag, "toggle")
522 tgl.title       = string.format(HELP, "TOGGLE", "Toggle Status" )
523 tgl.description = translate("Enable/Disable filtering when Privoxy starts.")
524                 .. [[<br />]]
525                 .. translate("Disabled == Transparent Proxy Mode")
526 tgl.orientation = "horizontal"
527 tgl.default     = "1"
528 tgl.rmempty     = false
529 function tgl.parse(self, section)
530         CTRL.flag_parse(self, section)
531 end
532
533 -- enable-remote-toggle --------------------------------------------------------
534 local ert       = ns:taboption("access", Flag, "enable_remote_toggle")
535 ert.title       = string.format(HELP, "ENABLE-REMOTE-TOGGLE", "Enable remote toggle" )
536 ert.description = translate("Whether or not the web-based toggle feature may be used.")
537 ert.orientation = "horizontal"
538 ert.rmempty     = true
539 function ert.parse(self, section)
540         CTRL.flag_parse(self, section)
541 end
542
543 -- enable-remote-http-toggle ---------------------------------------------------
544 local eht       = ns:taboption("access", Flag, "enable_remote_http_toggle")
545 eht.title       = string.format(HELP, "ENABLE-REMOTE-HTTP-TOGGLE", "Enable remote toggle via HTTP" )
546 eht.description = translate("Whether or not Privoxy recognizes special HTTP headers to change toggle state.")
547                 .. [[<br /><strong>]]
548                 .. translate("This option will be removed in future releases as it has been obsoleted by the more general header taggers.")
549                 .. [[</strong>]]
550 eht.orientation = "horizontal"
551 eht.rmempty     = true
552 function eht.parse(self, section)
553         CTRL.flag_parse(self, section)
554 end
555
556 -- enable-edit-actions ---------------------------------------------------------
557 local eea       = ns:taboption("access", Flag, "enable_edit_actions")
558 eea.title       = string.format(HELP, "ENABLE-EDIT-ACTIONS", "Enable action file editor" )
559 eea.description = translate("Whether or not the web-based actions file editor may be used.")
560 eea.orientation = "horizontal"
561 eea.rmempty     = true
562 function eea.parse(self, section)
563         CTRL.flag_parse(self, section)
564 end
565
566 -- enforce-blocks --------------------------------------------------------------
567 local eb        = ns:taboption("access", Flag, "enforce_blocks")
568 eb.title        = string.format(HELP, "ENFORCE-BLOCKS", "Enforce page blocking" )
569 eb.description  = translate("If enabled, Privoxy hides the 'go there anyway' link. "
570                 .. "The user obviously should not be able to bypass any blocks.")
571 eb.orientation  = "horizontal"
572 eb.rmempty      = true
573 function eb.parse(self, section)
574         CTRL.flag_parse(self, section)
575 end
576
577 -- tab: forward -- #############################################################
578
579 -- enable-proxy-authentication-forwarding --------------------------------------
580 local paf       = ns:taboption("forward", Flag, "enable_proxy_authentication_forwarding")
581 paf.title       = string.format(HELP, "ENABLE-PROXY-AUTHENTICATION-FORWARDING",
582                 translate("Enable proxy authentication forwarding") )
583 paf.description = translate("Whether or not proxy authentication through Privoxy should work.")
584                 .. [[<br /><strong>]]
585                 .. translate("Enabling this option is NOT recommended if there is no parent proxy that requires authentication!")
586                 .. [[</strong>]]
587 --paf.orientation       = "horizontal"
588 paf.rmempty     = true
589 function paf.parse(self, section)
590         CTRL.flag_parse(self, section)
591 end
592
593 -- forward ---------------------------------------------------------------------
594 local fwd       = ns:taboption("forward", DynamicList, "forward")
595 fwd.title       = string.format(HELP, "FORWARD", "Forward HTTP" )
596 fwd.description = translate("To which parent HTTP proxy specific requests should be routed.")
597                 .. [[<br />]]
598                 .. translate("Syntax: target_pattern http_parent[:port]")
599 fwd.rmempty     = true
600
601 -- forward-socks4 --------------------------------------------------------------
602 local fs4       = ns:taboption("forward", DynamicList, "forward_socks4")
603 fs4.title       = string.format(HELP, "SOCKS", "Forward SOCKS 4" )
604 fs4.description = translate("Through which SOCKS proxy (and optionally to which parent HTTP proxy) specific requests should be routed.")
605                 .. [[<br />]]
606                 .. translate("Syntax: target_pattern socks_proxy[:port] http_parent[:port]")
607 fs4.rmempty     = true
608
609 -- forward-socks4a -------------------------------------------------------------
610 local f4a       = ns:taboption("forward", DynamicList, "forward_socks4a")
611 f4a.title       = string.format(HELP, "SOCKS", "Forward SOCKS 4A" )
612 f4a.description = fs4.description
613 f4a.rmempty     = true
614
615 -- forward-socks5 --------------------------------------------------------------
616 local fs5       = ns:taboption("forward", DynamicList, "forward_socks5")
617 fs5.title       = string.format(HELP, "SOCKS", "Forward SOCKS 5" )
618 fs5.description = fs4.description
619 fs5.rmempty     = true
620
621 -- forward-socks5t -------------------------------------------------------------
622 local f5t       = ns:taboption("forward", DynamicList, "forward_socks5t")
623 f5t.title       = string.format(HELP, "SOCKS", "Forward SOCKS 5t" )
624 f5t.description = fs4.description
625 f5t.rmempty     = true
626
627 -- tab: misc -- ################################################################
628
629 -- accept-intercepted-requests -------------------------------------------------
630 local air       = ns:taboption("misc", Flag, "accept_intercepted_requests")
631 air.title       = string.format(HELP, "ACCEPT-INTERCEPTED-REQUESTS", "Accept intercepted requests" )
632 air.description = translate("Whether intercepted requests should be treated as valid.")
633 air.orientation = "horizontal"
634 air.rmempty     = true
635 function air.parse(self, section)
636         CTRL.flag_parse(self, section)
637 end
638
639 -- allow-cgi-request-crunching -------------------------------------------------
640 local crc       = ns:taboption("misc", Flag, "allow_cgi_request_crunching")
641 crc.title       = string.format(HELP, "ALLOW-CGI-REQUEST-CRUNCHING", "Allow CGI request crunching" )
642 crc.description = translate("Whether requests to Privoxy's CGI pages can be blocked or redirected.")
643 crc.orientation = "horizontal"
644 crc.rmempty     = true
645 function crc.parse(self, section)
646         CTRL.flag_parse(self, section)
647 end
648
649 -- split-large-forms -----------------------------------------------------------
650 local slf       = ns:taboption("misc", Flag, "split_large_forms")
651 slf.title       = string.format(HELP, "SPLIT-LARGE-FORMS", "Split large forms" )
652 slf.description = translate("Whether the CGI interface should stay compatible with broken HTTP clients.")
653 slf.orientation = "horizontal"
654 slf.rmempty     = true
655 function slf.parse(self, section)
656         CTRL.flag_parse(self, section)
657 end
658
659 -- keep-alive-timeout ----------------------------------------------------------
660 local kat       = ns:taboption("misc", Value, "keep_alive_timeout")
661 kat.title_base  = "Keep-alive timeout"
662 kat.title       = string.format(HELP, "KEEP-ALIVE-TIMEOUT", kat.title_base)
663 kat.description = translate("Number of seconds after which an open connection will no longer be reused.")
664 kat.rmempty     = true
665 function kat.validate(self, value)
666         local v = tonumber(value)
667         if not v then
668                 return nil, err_tab_misc(self.title_base, translate("Value is not a number") )
669         elseif v < 1 then
670                 return nil, err_tab_misc(self.title_base, translate("Value not greater 0 or empty") )
671         end
672         return value
673 end
674
675 -- tolerate-pipelining ---------------------------------------------------------
676 local tp        = ns:taboption("misc", Flag, "tolerate_pipelining")
677 tp.title        = string.format(HELP, "TOLERATE-PIPELINING", "Tolerate pipelining" )
678 tp.description  = translate("Whether or not pipelined requests should be served.")
679 tp.orientation  = "horizontal"
680 tp.rmempty      = true
681 function tp.parse(self, section)
682         CTRL.flag_parse(self, section)
683 end
684
685 -- default-server-timeout ------------------------------------------------------
686 local dst       = ns:taboption("misc", Value, "default_server_timeout")
687 dst.title_base  = "Default server timeout"
688 dst.title       = string.format(HELP, "DEFAULT-SERVER-TIMEOUT", dst.title_base)
689 dst.description = translate("Assumed server-side keep-alive timeout (in seconds) if not specified by the server.")
690 dst.rmempty     = true
691 function dst.validate(self, value)
692         local v = tonumber(value)
693         if not v then
694                 return nil, err_tab_misc(self.title_base, translate("Value is not a number") )
695         elseif v < 1 then
696                 return nil, err_tab_misc(self.title_base, translate("Value not greater 0 or empty") )
697         end
698         return value
699 end
700
701 -- connection-sharing ----------------------------------------------------------
702 local cs        = ns:taboption("misc", Flag, "connection_sharing")
703 cs.title        = string.format(HELP, "CONNECTION-SHARING", "Connection sharing" )
704 cs.description  = translate("Whether or not outgoing connections that have been kept alive should be shared between different incoming connections.")
705 cs.orientation  = "horizontal"
706 cs.rmempty      = true
707 function cs.parse(self, section)
708         CTRL.flag_parse(self, section)
709 end
710
711 -- socket-timeout --------------------------------------------------------------
712 local st        = ns:taboption("misc", Value, "socket_timeout")
713 st.title_base   = "Socket timeout"
714 st.title        = string.format(HELP, "SOCKET-TIMEOUT", st.title_base )
715 st.description  = translate("Number of seconds after which a socket times out if no data is received.")
716 st.default      = 300
717 st.rmempty      = true
718 function st.validate(self, value)
719         local v = tonumber(value)
720         if not v then
721                 return nil, err_tab_misc(self.title_base, translate("Value is not a number") )
722         elseif v < 1 then
723                 return nil, err_tab_misc(self.title_base, translate("Value not greater 0 or empty") )
724         elseif v == self.default then
725                 return ""       -- dont need to save default
726         end
727         return value
728 end
729
730 -- max-client-connections ------------------------------------------------------
731 local mcc       = ns:taboption("misc", Value, "max_client_connections")
732 mcc.title_base  = "Max. client connections"
733 mcc.title       = string.format(HELP, "MAX-CLIENT-CONNECTIONS", mcc.title_base )
734 mcc.description = translate("Maximum number of client connections that will be served.")
735 mcc.default     = 128
736 mcc.rmempty     = true
737 function mcc.validate(self, value)
738         local v = tonumber(value)
739         if not v then
740                 return nil, err_tab_misc(self.title_base, translate("Value is not a number") )
741         elseif v < 1 then
742                 return nil, err_tab_misc(self.title_base, translate("Value not greater 0 or empty") )
743         elseif v == self.default then
744                 return ""       -- dont need to save default
745         end
746         return value
747 end
748
749 -- handle-as-empty-doc-returns-ok ----------------------------------------------
750 local her       = ns:taboption("misc", Flag, "handle_as_empty_doc_returns_ok")
751 her.title       = string.format(HELP, "HANDLE-AS-EMPTY-DOC-RETURNS-OK", "Handle as empty doc returns ok" )
752 her.description = translate("The status code Privoxy returns for pages blocked with +handle-as-empty-document.")
753 her.orientation = "horizontal"
754 her.rmempty     = true
755 function her.parse(self, section)
756         CTRL.flag_parse(self, section)
757 end
758
759 -- enable-compression ----------------------------------------------------------
760 local ec        = ns:taboption("misc", Flag, "enable_compression")
761 ec.title        = string.format(HELP, "ENABLE-COMPRESSION", "Enable compression" )
762 ec.description  = translate("Whether or not buffered content is compressed before delivery.")
763 ec.orientation  = "horizontal"
764 ec.rmempty      = true
765 function ec.parse(self, section)
766         CTRL.flag_parse(self, section)
767 end
768
769 -- compression-level -----------------------------------------------------------
770 local cl        = ns:taboption("misc", Value, "compression_level")
771 cl.title_base   = "Compression level"
772 cl.title        = string.format(HELP, "COMPRESSION-LEVEL", cl.title_base )
773 cl.description  = translate("The compression level that is passed to the zlib library when compressing buffered content.")
774 cl.default      = 1
775 cl.rmempty      = true
776 function cl.validate(self, value)
777         local v = tonumber(value)
778         if not v then
779                 return nil, err_tab_misc(self.title_base, translate("Value is not a number") )
780         elseif v < 0 or v > 9 then
781                 return nil, err_tab_misc(self.title_base, translate("Value not between 0 and 9") )
782         elseif v == self.default then
783                 return ""       -- don't need to save default
784         end
785         return value
786 end
787
788 -- client-header-order ---------------------------------------------------------
789 local cho       = ns:taboption("misc", Value, "client_header_order")
790 cho.title       = string.format(HELP, "CLIENT-HEADER-ORDER", "Client header order" )
791 cho.description = translate("The order in which client headers are sorted before forwarding them.")
792                 .. [[<br />]]
793                 .. translate("Syntax: Client header names delimited by spaces.")
794 cho.rmempty     = true
795
796 -- "debug"-tab definition -- ###################################################
797
798 -- single-threaded -------------------------------------------------------------
799 local st        = ns:taboption("debug", Flag, "single_threaded")
800 st.title        = string.format(HELP, "SINGLE-THREADED", "Single Threaded" )
801 st.description  = translate("Whether to run only one server thread.")
802                 .. [[<br /><strong>]]
803                 .. translate("This option is only there for debugging purposes. It will drastically reduce performance.")
804                 .. [[</strong>]]
805 st.rmempty      = true
806 function st.parse(self, section)
807         CTRL.flag_parse(self, section)
808 end
809
810 -- debug 1 ---------------------------------------------------------------------
811 local d0        = ns:taboption("debug", Flag, "debug_1")
812 d0.title        = string.format(HELP, "DEBUG", "Debug 1" )
813 d0.description  = translate("Log the destination for each request Privoxy let through. See also 'Debug 1024'.")
814 d0.rmempty      = true
815 function d0.parse(self, section)
816         CTRL.flag_parse(self, section)
817 end
818
819 -- debug 2 ---------------------------------------------------------------------
820 local d1        = ns:taboption("debug", Flag, "debug_2")
821 d1.title        = string.format(HELP, "DEBUG", "Debug 2" )
822 d1.description  = translate("Show each connection status")
823 d1.rmempty      = true
824 function d1.parse(self, section)
825         CTRL.flag_parse(self, section)
826 end
827
828 -- debug 4 ---------------------------------------------------------------------
829 local d2        = ns:taboption("debug", Flag, "debug_4")
830 d2.title        = string.format(HELP, "DEBUG", "Debug 4" )
831 d2.description  = translate("Show I/O status")
832 d2.rmempty      = true
833 function d2.parse(self, section)
834         CTRL.flag_parse(self, section)
835 end
836
837 -- debug 8 ---------------------------------------------------------------------
838 local d3        = ns:taboption("debug", Flag, "debug_8")
839 d3.title        = string.format(HELP, "DEBUG", "Debug 8" )
840 d3.description  = translate("Show header parsing")
841 d3.rmempty      = true
842 function d3.parse(self, section)
843         CTRL.flag_parse(self, section)
844 end
845
846 -- debug 16 --------------------------------------------------------------------
847 local d4        = ns:taboption("debug", Flag, "debug_16")
848 d4.title        = string.format(HELP, "DEBUG", "Debug 16" )
849 d4.description  = translate("Log all data written to the network")
850 d4.rmempty      = true
851 function d4.parse(self, section)
852         CTRL.flag_parse(self, section)
853 end
854
855 -- debug 32 --------------------------------------------------------------------
856 local d5        = ns:taboption("debug", Flag, "debug_32")
857 d5.title        = string.format(HELP, "DEBUG", "Debug 32" )
858 d5.description  = translate("Debug force feature")
859 d5.rmempty      = true
860 function d5.parse(self, section)
861         CTRL.flag_parse(self, section)
862 end
863
864 -- debug 64 --------------------------------------------------------------------
865 local d6        = ns:taboption("debug", Flag, "debug_64")
866 d6.title        = string.format(HELP, "DEBUG", "Debug 64" )
867 d6.description  = translate("Debug regular expression filters")
868 d6.rmempty      = true
869 function d6.parse(self, section)
870         CTRL.flag_parse(self, section)
871 end
872
873 -- debug 128 -------------------------------------------------------------------
874 local d7        = ns:taboption("debug", Flag, "debug_128")
875 d7.title        = string.format(HELP, "DEBUG", "Debug 128" )
876 d7.description  = translate("Debug redirects")
877 d7.rmempty      = true
878 function d7.parse(self, section)
879         CTRL.flag_parse(self, section)
880 end
881
882 -- debug 256 -------------------------------------------------------------------
883 local d8        = ns:taboption("debug", Flag, "debug_256")
884 d8.title        = string.format(HELP, "DEBUG", "Debug 256" )
885 d8.description  = translate("Debug GIF de-animation")
886 d8.rmempty      = true
887 function d8.parse(self, section)
888         CTRL.flag_parse(self, section)
889 end
890
891 -- debug 512 -------------------------------------------------------------------
892 local d9        = ns:taboption("debug", Flag, "debug_512")
893 d9.title        = string.format(HELP, "DEBUG", "Debug 512" )
894 d9.description  = translate("Common Log Format")
895 d9.rmempty      = true
896 function d9.parse(self, section)
897         CTRL.flag_parse(self, section)
898 end
899
900 -- debug 1024 ------------------------------------------------------------------
901 local d10       = ns:taboption("debug", Flag, "debug_1024")
902 d10.title       = string.format(HELP, "DEBUG", "Debug 1024" )
903 d10.description = translate("Log the destination for requests Privoxy didn't let through, and the reason why.")
904 d10.rmempty     = true
905 function d10.parse(self, section)
906         CTRL.flag_parse(self, section)
907 end
908
909 -- debug 2048 ------------------------------------------------------------------
910 local d11       = ns:taboption("debug", Flag, "debug_2048")
911 d11.title       = string.format(HELP, "DEBUG", "Debug 2048" )
912 d11.description = translate("CGI user interface")
913 d11.rmempty     = true
914 function d11.parse(self, section)
915         CTRL.flag_parse(self, section)
916 end
917
918 -- debug 4096 ------------------------------------------------------------------
919 local d12       = ns:taboption("debug", Flag, "debug_4096")
920 d12.title       = string.format(HELP, "DEBUG", "Debug 4096" )
921 d12.description = translate("Startup banner and warnings.")
922 d12.rmempty     = true
923 function d12.parse(self, section)
924         CTRL.flag_parse(self, section)
925 end
926
927 -- debug 8192 ------------------------------------------------------------------
928 local d13       = ns:taboption("debug", Flag, "debug_8192")
929 d13.title       = string.format(HELP, "DEBUG", "Debug 8192" )
930 d13.description = translate("Non-fatal errors - *we highly recommended enabling this*")
931 d13.rmempty     = true
932 function d13.parse(self, section)
933         CTRL.flag_parse(self, section)
934 end
935
936 -- debug 16384 -----------------------------------------------------------------
937 --[[ TODO ???
938 local d14       = ns:taboption("debug", Flag, "debug_16384")
939 d14.title       = string.format(HELP, "DEBUG", "Debug 16384" )
940 d14.description = translate("")
941 d14.rmempty     = true
942 function d14.parse(self, section)
943         CTRL.flag_parse(self, section)
944 end
945 ]]--
946
947 -- debug 32768 -----------------------------------------------------------------
948 local d15       = ns:taboption("debug", Flag, "debug_32768")
949 d15.title       = string.format(HELP, "DEBUG", "Debug 32768" )
950 d15.description = translate("Log all data read from the network")
951 d15.rmempty     = true
952 function d15.parse(self, section)
953         CTRL.flag_parse(self, section)
954 end
955
956 -- debug 65536 -----------------------------------------------------------------
957 local d16       = ns:taboption("debug", Flag, "debug_65536")
958 d16.title       = string.format(HELP, "DEBUG", "Debug 65536" )
959 d16.description = translate("Log the applying actions")
960 d16.rmempty     = true
961 function d16.parse(self, section)
962         CTRL.flag_parse(self, section)
963 end
964
965 -- tab: logview -- #############################################################
966
967 local lv        = ns:taboption("logview", DummyValue, "_logview")
968 lv.template     = "privoxy/detail_logview"
969 lv.inputtitle   = translate("Read / Reread log file")
970 lv.rows         = 50
971 function lv.cfgvalue(self, section)
972         local lfile=self.map:get(ns.section, "logdir") .. "/" .. self.map:get(ns.section, "logfile")
973         if NXFS.access(lfile) then
974                 return lfile .. "\n" .. translate("Please press [Read] button")
975         end
976         return lfile .. "\n" .. translate("File not found or empty")
977 end
978
979 return m