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