privoxy.lua 37 KB

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