You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

942 lines
35 KiB

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