summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--applications/luci-app-radicale2/Makefile14
-rw-r--r--applications/luci-app-radicale2/luasrc/controller/radicale2.lua38
-rw-r--r--applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua209
-rw-r--r--applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua40
-rw-r--r--applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua144
-rw-r--r--applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua50
-rw-r--r--applications/luci-app-radicale2/luasrc/view/cbi/raduigo.htm25
-rw-r--r--applications/luci-app-radicale2/po/en/radicale2.po496
-rw-r--r--applications/luci-app-radicale2/po/templates/radicale2.pot475
-rw-r--r--libs/rpcd-mod-rad2-enc/Makefile49
-rwxr-xr-xlibs/rpcd-mod-rad2-enc/files/rad2-enc50
11 files changed, 1590 insertions, 0 deletions
diff --git a/applications/luci-app-radicale2/Makefile b/applications/luci-app-radicale2/Makefile
new file mode 100644
index 0000000000..83a2e7125e
--- /dev/null
+++ b/applications/luci-app-radicale2/Makefile
@@ -0,0 +1,14 @@
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=Radicale v2.x CalDAV/CardDAV Server
+LUCI_DEPENDS:=+radicale2 +rpcd-mod-rad2-enc
+LUCI_PKGARCH:=all
+
+PKG_LICENSE:=Apache-2.0
+PKG_MAINTAINER:=Daniel Dickinson <cshored@thecshore.com>
+
+LUA_TARGET:=source
+
+include ../../luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/applications/luci-app-radicale2/luasrc/controller/radicale2.lua b/applications/luci-app-radicale2/luasrc/controller/radicale2.lua
new file mode 100644
index 0000000000..7b94552ed6
--- /dev/null
+++ b/applications/luci-app-radicale2/luasrc/controller/radicale2.lua
@@ -0,0 +1,38 @@
+-- Licensed to the public under the Apache License 2.0.
+
+module("luci.controller.radicale2", package.seeall)
+
+function index()
+ local page
+
+ -- no config create an empty one
+ if not nixio.fs.access("/etc/config/radicale2") then
+ nxfs.writefile("/etc/config/radicale2", "")
+ end
+
+ page = entry({"admin", "services", "radicale2"}, alias("admin", "services", "radicale2", "server"), _("Radicale 2.x"))
+ page.leaf = false
+
+ page = entry({"admin", "services", "radicale2", "server"}, cbi("radicale2/server"), _("Server Settings"))
+ page.leaf = true
+ page.order = 10
+
+ page = entry({"admin", "services", "radicale2", "auth"}, cbi("radicale2/auth"), _("Authentication / Users"))
+ page.leaf = true
+ page.order = 20
+
+ page = entry({"admin", "services", "radicale2", "storage"}, cbi("radicale2/storage"), _("Storage"))
+ page.leaf = true
+ page.order = 30
+
+ page = entry({"admin", "services", "radicale2", "logging"}, cbi("radicale2/logging"), _("Logging"))
+ page.leaf = true
+ page.order = 40
+end
+
+function pymodexists(module)
+ retfun = luci.util.execi('python3 -c \'import importlib.util as util;found_module = util.find_spec("' .. module .. '");print(found_module is not None);print("\\n")\'')
+ retval = retfun() == "True"
+ while retfun() do end
+ return retval
+end
diff --git a/applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua b/applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua
new file mode 100644
index 0000000000..71fd3a32ee
--- /dev/null
+++ b/applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua
@@ -0,0 +1,209 @@
+-- Licensed to the public under the Apache License 2.0.
+
+local rad2 = luci.controller.radicale2
+local fs = require("nixio.fs")
+local util = require("luci.util")
+
+local m = Map("radicale2", translate("Radicale 2.x"),
+ translate("A lightweight CalDAV/CardDAV server"))
+
+local s = m:section(NamedSection, "auth", "section", translate("Authentication"))
+s.addremove = true
+s.anonymous = false
+
+local at = s:option(ListValue, "type", translate("Authentication Type"))
+at:value("", translate("Default (htpasswd file from users below)"))
+at:value("htpasswd", translate("htpasswd file (manually populated)"))
+at:value("none", translate("No authentication"))
+at:value("remote_user", translate("REMOTE_USER from web server"))
+at:value("http_x_remote_user", translate("X-Remote-User from web server"))
+at.default = ""
+at.rmempty = true
+
+local o = s:option(Value, "htpasswd_filename", translate("Filename"), translate("htpasswd-formatted file filename"))
+o:depends("type", "htpasswd")
+o.rmempty = true
+o.placeholder = "/etc/radicale2/users"
+o.default = ""
+
+local hte = s:option(ListValue, "htpasswd_encryption", translate("Encryption"), translate("Password encryption method"))
+hte:depends("type", "htpasswd")
+hte:depends("type", "")
+hte:value("plain", translate("Plaintext"))
+hte:value("sha1", translate("SHA1"))
+hte:value("ssha", translate("SSHA"))
+hte:value("crypt", translate("crypt"))
+if rad2.pymodexists("passlib") then
+ hte:value("md5", translate("md5"))
+ if rad2.pymodexists("bcrypt") then
+ hte:value("bcrypt", translate("bcrypt"))
+ end
+end
+hte.default = "plain"
+hte.rmempty = true
+
+if not rad2.pymodexists("bcrypt") then
+ o = s:option(DummyValue, "nobcrypt", translate("Insecure hashes"), translate("Install python3-passlib and python3-bcrypt to enable a secure hash"))
+else
+ o = s:option(DummyValue, "nobcrypt", translate("Insecure hashes"), translate("Select bcrypt above to enable a secure hash"))
+ o:depends("htpasswd_encrypt","")
+ o:depends("htpasswd_encrypt","plain")
+ o:depends("htpasswd_encrypt","sha1")
+ o:depends("htpasswd_encrypt","ssha")
+ o:depends("htpasswd_encrypt","crypt")
+ o:depends("htpasswd_encrypt","md5")
+end
+
+o = s:option(Value, "delay", translate("Retry Delay"), translate("Required time between a failed authentication attempt and trying again"))
+o.rmempty = true
+o.default = 1
+o.datatype = "uinteger"
+o:depends("type", "")
+o:depends("type", "htpasswd")
+o:depends("type", "remote_user")
+o:depends("type", "http_x_remote_user")
+
+s = m:section(TypedSection, "user", translate("User"), translate("Users and Passwords"))
+s.addremove = true
+s.anonymous = true
+
+o = s:option(Value, "name", translate("Username"))
+o.rmempty = true
+o.placeholder = "johndoe"
+
+if rad2.pymodexists("passlib") then
+
+local plainpass = s:option(Value, "plain_pass", translate("Plaintext Password"))
+plainpass.placeholder = "Example password"
+plainpass.password = true
+
+local ppconfirm = s:option(Value, "plain_pass_confirm", translate("Confirm Plaintext Password"))
+ppconfirm.placeholder = "Example password"
+ppconfirm.password = true
+
+plainpass.cfgvalue = function(self, section)
+ return self:formvalue(section)
+end
+
+plainpass.write = function(self, section)
+ return true
+end
+
+
+ppconfirm.cfgvalue = plainpass.cfgvalue
+ppconfirm.write = plainpass.write
+
+plainpass.validate = function(self, value, section)
+ if self:cfgvalue(section) ~= ppconfirm:cfgvalue(section) then
+ return nil, translate("Password and confirmation do not match")
+ end
+ return AbstractValue.validate(self, value, section)
+end
+
+ppconfirm.validate = function(self, value, section)
+ if self:cfgvalue(section) ~= plainpass:cfgvalue(section) then
+ return nil, translate("Password and confirmation do not match")
+ end
+ return AbstractValue.validate(self, value, section)
+end
+
+local pass = s:option(Value, "password", translate("Encrypted Password"), translate("If 'Plaintext Password' filled and matches 'Confirm Plaintext Password' then this field becomes of hash of that password, otherwise this field remains the existing hash (you can also put your own hash value for the type of hash listed above)."))
+pass.password = true
+pass.rmempty = false
+
+function encpass(self, section)
+ local plainvalue = plainpass:cfgvalue(section)
+ local pvc = ppconfirm:cfgvalue(section)
+ local encvalue, err
+
+ if not plainvalue or not pvc or plainvalue == "" or pvc == "" or plainvalue ~= pvc then
+ return nil
+ end
+ local enctype = hte:formvalue("auth")
+ if not enctype then
+ enctype = hte:cfgvalue("auth")
+ end
+ if not enctype or enctype == "" or enctype == "plain" then
+ return plainvalue
+ end
+
+ encvalue, err = util.ubus("rad2-enc", "encrypt", { type = enctype, plainpass = plainvalue })
+ if not encvalue then
+ return nil
+ end
+
+ return encvalue and encvalue.encrypted_password
+end
+
+pass.parse = function(self, section, novld)
+ local encvalue
+ if self:cfgvalue(section) then
+ if not plainpass:cfgvalue(section) then
+ return Value.parse(self, section)
+ else
+ encvalue = encpass(self, section)
+ if encvalue then
+ self.formvalue = function(self, section)
+ return encvalue
+ end
+ return Value.parse(self, section, novld)
+ else
+ self.formvalue = self.cfgvalue
+ return Value.parse(self, section, novld)
+ end
+ end
+ else
+ encvalue = encpass(self, section)
+ if encvalue then
+ self.formvalue = function(self, section)
+ return encvalue
+ end
+ return Value.parse(self, section, novld)
+ else
+ return nil
+ end
+ end
+end
+
+else
+local pass = s:option(Value, "password", translate("Encrypted Password"), translate("Generate this field using an generator for Apache htpasswd-style authentication files (for the hash format you have chosen above), or install python3-passlib to enable the ability to create the hash by entering the plaintext in a field that will appear on this page if python3-passlib is installed."))
+pass.password = true
+pass.rmempty = false
+
+end -- python3-passlib installed
+
+-- TODO: Allow configuration of rights file from this page
+local s = m:section(NamedSection, "section", "rights", translate("Rights"), translate("User-based ACL Settings"))
+s.addremove = true
+s.anonymous = false
+
+o = s:option(ListValue, "type", translate("Rights Type"))
+o:value("", translate("Default (owner only)"))
+o:value("owner_only", translate("RO: None, RW: Owner"))
+o:value("authenticated", translate("RO: None, RW: Authenticated Users"))
+o:value("owner_write", translate("RO: Authenticated Users, RW: Owner"))
+o:value("from_file", translate("Based on settings in 'Rights File'"))
+o:value("none", translate("RO: All, RW: All"))
+o.default = ""
+o.rmempty = true
+
+rights_file = s:option(FileUpload, "file", translate("Rights File"))
+rights_file.rmempty = true
+rights_file:depends("type", "from_file")
+
+o = s:option(Button, "remove_conf",
+ translate("Remove configuration for rights file"),
+ translate("This permanently deletes the rights file and configuration to use same."))
+o.inputstyle = "remove"
+o:depends("type", "from_file")
+
+function o.write(self, section)
+ if cert_file:cfgvalue(section) and fs.access(o:cfgvalue(section)) then fs.unlink(rights_file:cfgvalue(section)) end
+ self.map:del(section, "file")
+ self.map:del(section, "rights_file")
+ luci.http.redirect(luci.dispatcher.build_url("admin", "services", "radicale2", "auth"))
+end
+
+-- TODO: Allow configuration rights file from this page
+
+return m
diff --git a/applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua b/applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua
new file mode 100644
index 0000000000..779bef8591
--- /dev/null
+++ b/applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua
@@ -0,0 +1,40 @@
+-- Licensed to the public under the Apache License 2.0.
+
+local m = Map("radicale2", translate("Radicale 2.x"),
+ translate("A lightweight CalDAV/CardDAV server"))
+
+local s = m:section(NamedSection, "logging", "section", translate("Logging"))
+s.addremove = true
+s.anonymous = false
+
+local logging_file = nil
+
+logging_file = s:option(FileUpload, "config", translate("Logging File"), translate("Log configuration file (no file means default procd which ends up in syslog"))
+logging_file.rmempty = true
+logging_file.default = ""
+
+o = s:option(Button, "remove_conf", translate("Remove configuration for logging"),
+ translate("This permanently deletes configuration for logging"))
+o.inputstyle = "remove"
+
+function o.write(self, section)
+ if logging_file:cfgvalue(section) and fs.access(logging_file:cfgvalue(section)) then fs.unlink(loggin_file:cfgvalue(section)) end
+ self.map:del(section, "config")
+ luci.http.redirect(luci.dispatcher.build_url("admin", "services", "radicale2", "logging"))
+end
+
+o = s:option(Flag, "debug", translate("Debug"), translate("Send debug information to logs"))
+o.rmempty = true
+o.default = o.disabled
+
+o = s:option(Flag, "full_environment", translate("Dump Environment"), translate("Include full environment in logs"))
+o.rmempty = true
+o.default = o.disabled
+
+o = s:option(Flag, "mask_passwords", translate("Mask Passwords"), translate("Redact passwords in logs"))
+o.rmempty = true
+o.default = o.enabled
+
+-- TODO: Allow configuration logging file from this page
+
+return m
diff --git a/applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua b/applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua
new file mode 100644
index 0000000000..47ef868b8c
--- /dev/null
+++ b/applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua
@@ -0,0 +1,144 @@
+-- Licensed to the public under the Apache License 2.0.
+
+local fs = require("nixio.fs")
+local rad2 = require "luci.controller.radicale2"
+local http = require("luci.http")
+
+local m = Map("radicale2", translate("Radicale 2.x"),
+ translate("A lightweight CalDAV/CardDAV server"))
+
+s = m:section(SimpleSection, translate("Radicale v2 Web UI"))
+s.addremove = false
+s.anonymous = true
+
+o = s:option(DummyValue, "radicale2_webui_go", translate("Go to Radicale v2 Web UI"))
+o.template = "cbi/raduigo"
+o.section = "cbi-radicale2_webui"
+
+local s = m:section(NamedSection, "server", "section", translate("Server Settings"))
+s.addremove = true
+s.anonymous = false
+
+o.section = "cbi-radicale2_web_ui"
+
+local lhttp = nil
+local certificate_file = nil
+local key_file = nil
+local certificate_authority_file = nil
+
+s:tab("general", translate("General Settings"))
+s:tab("advanced", translate("Advanced Settings"))
+
+lhttp = s:taboption("general", DynamicList, "host", translate("HTTP(S) Listeners (address:port)"))
+lhttp.datatype = "list(ipaddrport(1))"
+lhttp.placeholder = "127.0.0.1:5232"
+
+o = s:taboption("advanced", Value, "max_connection", translate("Max Connections"), translate("Maximum number of simultaneous connections"))
+o.rmempty = true
+o.placeholder = 20
+o.datatype = "uinteger"
+
+
+o = s:taboption("advanced", Value, "max_content_length", translate("Max Content Length"), translate("Maximum size of request body (bytes)"))
+o.rmempty = true
+o.datatype = "uinteger"
+o.placeholder = 100000000
+
+o = s:taboption("advanced", Value, "timeout", translate("Timeout"), translate("Socket timeout (seconds)"))
+o.rmempty = true
+o.placeholder = 30
+o.datatype = "uinteger"
+
+sslon = s:taboption("general", Flag, "ssl", translate("SSL"), translate("Enable SSL connections"))
+sslon.rmempty = true
+sslon.default = o.disabled
+sslon.formvalue = function(self, section)
+ if not rad2.pymodexists('ssl') then
+ return false
+ end
+ return Flag.formvalue(self, section)
+end
+
+cert_file = s:taboption("general", FileUpload, "certificate", translate("Certificate"))
+cert_file.rmempty = true
+cert_file:depends("ssl", true)
+
+key_file = s:taboption("general", FileUpload, "key", translate("Private Key"))
+key_file.rmempty = true
+key_file:depends("ssl", true)
+
+ca_file = s:taboption("general", FileUpload, "certificate_authority", translate("Client Certificate Authority"), translate("For verifying client certificates"))
+ca_file.rmempty = true
+ca_file:depends("ssl", true)
+
+o = s:taboption("advanced", Value, "ciphers", translate("Allowed Ciphers"), translate("See python3-openssl documentation for available ciphers"))
+o.rmempty = true
+o:depends("ssl", true)
+
+o = s:taboption("advanced", Value, "protocol", translate("Use Protocol"), translate("See python3-openssl documentation for available protocols"))
+o.rmempty = true
+o:depends("ssl", true)
+o.placeholder = "PROTOCOL_TLSv1_2"
+
+o = s:taboption("general", Button, "remove_conf",
+ translate("Remove configuration for certificate, key, and CA"),
+ translate("This permanently deletes the cert, key, and configuration to use same."))
+o.inputstyle = "remove"
+o:depends("ssl", true)
+
+function o.write(self, section)
+ if cert_file:cfgvalue(section) and fs.access(cert_file:cfgvalue(section)) then fs.unlink(cert_file:cfgvalue(section)) end
+ if key_file:cfgvalue(section) and fs.access(key_file:cfgvalue(section)) then fs.unlink(key_file:cfgvalue(section)) end
+ if ca_file:cfgvalue(section) and fs.access(key_file:cfgvalue(section)) then fs.unlink(ca_file:cfgvalue(section)) end
+ self.map:del(section, "certificate")
+ self.map:del(section, "key")
+ self.map:del(section, "certificate_authority")
+ self.map:del(section, "protocol")
+ self.map:del(section, "ciphers")
+ luci.http.redirect(luci.dispatcher.build_url("admin", "services", "radicale2", "server"))
+end
+
+if not rad2.pymodexists('ssl') then
+ o = s:taboption("general", DummyValue, "sslnotpreset", translate("SSL not available"), translate("Install package python3-openssl to support SSL connections"))
+end
+
+o = s:taboption("advanced", Flag, "dns_lookup", translate("DNS Lookup"), translate("Lookup reverse DNS for clients for logging"))
+o.rmempty = true
+o.default = o.enabled
+
+o = s:taboption("advanced", Value, "realm", translate("Realm"), translate("HTTP(S) Basic Authentication Realm"))
+o.rmempty = true
+o.placeholder = "Radicale - Password Required"
+
+local s = m:section(NamedSection, "web", "section", translate("Web UI"))
+s.addremove = true
+s.anonymous = false
+
+o = s:option(ListValue, "type", translate("Web UI Type"))
+o:value("", "Default (Built-in)")
+o:value("internal", "Built-in")
+o:value("none", "None")
+o.default = ""
+o.rmempty = true
+
+local s = m:section(NamedSection, "headers", "section", translate("Headers"), translate("HTTP(S) Headers"))
+s.addremove = true
+s.anonymous = false
+
+o = s:option(Value, "cors", translate("CORS"), translate("Header: X-Access-Control-Allow-Origin"))
+o.rmempty = true
+o.placeholder = "*"
+
+local s = m:section(NamedSection, "encoding", "section", translate("Document Encoding"))
+s.addremove = true
+s.anonymous = false
+
+o = s:option(Value, "request", translate("Request"), translate("Encoding for responding to requests/events"))
+o.rmempty = true
+o.placeholder = "utf-8"
+
+o = s:option(Value, "stock", translate("Storage"), translate("Encoding for storing local collections"))
+o.rmempty = true
+o.placeholder = "utf-8"
+
+return m
diff --git a/applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua b/applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua
new file mode 100644
index 0000000000..3440296edf
--- /dev/null
+++ b/applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua
@@ -0,0 +1,50 @@
+-- Licensed to the public under the Apache License 2.0.
+
+local rad2 = luci.controller.radicale2
+local fs = require("nixio.fs")
+
+local m = Map("radicale2", translate("Radicale 2.x"),
+ translate("A lightweight CalDAV/CardDAV server"))
+
+local s = m:section(NamedSection, "storage", "section", translate("Storage"))
+s.addremove = true
+s.anonymous = false
+
+o = s:option(ListValue, "type", translate("Storage Type"))
+o:value("", translate("Default (multifilesystem)"))
+o:value("multifilesystem", translate("Multiple files on filesystem"))
+o.default = ""
+o.rmempty = true
+
+o = s:option(Value, "filesystem_folder", translate("Folder"), translate("Folder in which to store collections"))
+o:depends("type", "")
+o:depends("type", "multifilesystem")
+o.rmempty = true
+o.placeholder = "/srv/radicale2/data"
+
+o = s:option(Flag, "filesystem_locking", translate("Use File Locks"), translate("Prevent other instances or processes from modifying collections while in use"))
+o:depends("type", "")
+o:depends("type", "multifilesystem")
+o.rmempty = true
+o.default = o.enabled
+
+o = s:option(Value, "max_sync_token_age", translate("Max Sync Token Age"), translate("Delete sync token that are older (seconds)"))
+o:depends("type", "")
+o:depends("type", "multifilesystem")
+o.rmempty = true
+o.placeholder = 2592000
+o.datatype = "uinteger"
+
+o = s:option(Flag, "filesystem_close_lock_file", translate("Close Lock File"), translate("Close the lock file when no more clients are waiting"))
+o:depends("type", "")
+o:depends("type", "multifilesystem")
+o.rmempty = true
+o.default = o.disabled
+
+o = s:option(Value, "hook", translate("Hook"), translate("Command that is run after changes to storage"))
+o:depends("type", "")
+o:depends("type", "multifilesystem")
+o.rmempty = true
+o.placeholder = ("Example: ([ -d .git ] || git init) && git add -A && (git diff --cached --quiet || git commit -m \"Changes by \"%(user)s")
+
+return m
diff --git a/applications/luci-app-radicale2/luasrc/view/cbi/raduigo.htm b/applications/luci-app-radicale2/luasrc/view/cbi/raduigo.htm
new file mode 100644
index 0000000000..2f449d7b0b
--- /dev/null
+++ b/applications/luci-app-radicale2/luasrc/view/cbi/raduigo.htm
@@ -0,0 +1,25 @@
+<%
+local uci = require "luci.model.uci".cursor()
+local http_port = uci:get("radicale2", "server", "host")
+local usessl = uci:get("radicale2", "server", "ssl")
+if type(http_port) == "table" then
+ http_port = http_port[1]
+end
+
+ if http_port then
+ http_port = http_port:match("(%d+)$")
+ end
+ if not http_port then
+ http_port = "5232"
+ end
+%>
+<script type="text/javascript">
+<%
+if usessl then
+%>
+ var protocol = 'https'
+<% else %>
+ var protocol = 'http'
+<% end %>
+document.write('<a href="' + protocol + '://' + window.location.hostname + ':' + <%=http_port%> + '/"><%=luci.i18n.translate("Go to Radicale 2.x Web UI")%></a>');
+</script>
diff --git a/applications/luci-app-radicale2/po/en/radicale2.po b/applications/luci-app-radicale2/po/en/radicale2.po
new file mode 100644
index 0000000000..7a1bb2527d
--- /dev/null
+++ b/applications/luci-app-radicale2/po/en/radicale2.po
@@ -0,0 +1,496 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Project-Id-Version: luci-app-radicale 2\n"
+"PO-Revision-Date: 2019-01-09 06:32-0500\n"
+"Last-Translator: Daniel F. Dickinson <cshored@thecshore.com>\n"
+"Language-Team: English\n"
+"Language: en\n"
+"MIME-Version: 1.0\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:8
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:4
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:8
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:7
+msgid "A lightweight CalDAV/CardDAV server"
+msgstr "A lightweight CalDAV/CardDAV server"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:30
+msgid "Advanced Settings"
+msgstr "Advanced Settings"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:74
+msgid "Allowed Ciphers"
+msgstr "Allowed Ciphers"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:10
+msgid "Authentication"
+msgstr "Authentication"
+
+#: applications/luci-app-radicale2/luasrc/controller/radicale2.lua:20
+msgid "Authentication / Users"
+msgstr "Authentication / Users"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:14
+msgid "Authentication Type"
+msgstr "Authentication Type"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:185
+msgid "Based on settings in 'Rights File'"
+msgstr "Based on settings in 'Rights File'"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:128
+msgid "CORS"
+msgstr "CORS"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:62
+msgid "Certificate"
+msgstr "Certificate"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:70
+msgid "Client Certificate Authority"
+msgstr "Client Certificate Authority"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:38
+msgid "Close Lock File"
+msgstr "Close Lock File"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:38
+msgid "Close the lock file when no more clients are waiting"
+msgstr "Close the lock file when no more clients are waiting"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:44
+msgid "Command that is run after changes to storage"
+msgstr "Command that is run after changes to storage"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:80
+msgid "Confirm Plaintext Password"
+msgstr "Confirm Plaintext Password"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:105
+msgid "DNS Lookup"
+msgstr "DNS Lookup"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:26
+msgid "Debug"
+msgstr "Debug"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:15
+msgid "Default (htpasswd file from users below)"
+msgstr "Default (htpasswd file from users below)"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:14
+msgid "Default (multifilesystem)"
+msgstr "Default (multifilesystem)"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:181
+msgid "Default (owner only)"
+msgstr "Default (owner only)"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:31
+msgid "Delete sync token that are older (seconds)"
+msgstr "Delete sync token that are older (seconds)"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:132
+msgid "Document Encoding"
+msgstr "Document Encoding"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:30
+msgid "Dump Environment"
+msgstr "Dump Environment"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:52
+msgid "Enable SSL connections"
+msgstr "Enable SSL connections"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:136
+msgid "Encoding for responding to requests/events"
+msgstr "Encoding for responding to requests/events"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:140
+msgid "Encoding for storing local collections"
+msgstr "Encoding for storing local collections"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:110
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:169
+msgid "Encrypted Password"
+msgstr "Encrypted Password"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:29
+msgid "Encryption"
+msgstr "Encryption"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:23
+msgid "Filename"
+msgstr "Filename"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:19
+msgid "Folder"
+msgstr "Folder"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:19
+msgid "Folder in which to store collections"
+msgstr "Folder in which to store collections"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:70
+msgid "For verifying client certificates"
+msgstr "For verifying client certificates"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:29
+msgid "General Settings"
+msgstr "General Settings"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:169
+msgid ""
+"Generate this field using an generator for Apache htpasswd-style "
+"authentication files (for the hash format you have chosen above), or install "
+"python3-passlib to enable the ability to create the hash by entering the "
+"plaintext in a field that will appear on this page if python3-passlib is "
+"installed."
+msgstr ""
+"Generate this field using an generator for Apache htpasswd-style "
+"authentication files (for the hash format you have chosen above), or install "
+"python3-passlib to enable the ability to create the hash by entering the "
+"plaintext in a field that will appear on this page if python3-passlib is "
+"installed."
+
+#: applications/luci-app-radicale2/luasrc/view/cbi/raduigo.htm:24
+msgid "Go to Radicale 2.x Web UI"
+msgstr "Go to Radicale 2.x Web UI"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:14
+msgid "Go to Radicale v2 Web UI"
+msgstr "Go to Radicale v2 Web UI"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:109
+msgid "HTTP(S) Basic Authentication Realm"
+msgstr "HTTP(S) Basic Authentication Realm"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:124
+msgid "HTTP(S) Headers"
+msgstr "HTTP(S) Headers"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:32
+msgid "HTTP(S) Listeners (address:port)"
+msgstr "HTTP(S) Listeners (address:port)"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:128
+msgid "Header: X-Access-Control-Allow-Origin"
+msgstr "Header: X-Access-Control-Allow-Origin"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:124
+msgid "Headers"
+msgstr "Headers"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:44
+msgid "Hook"
+msgstr "Hook"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:110
+msgid ""
+"If 'Plaintext Password' filled and matches 'Confirm Plaintext Password' then "
+"this field becomes of hash of that password, otherwise this field remains "
+"the existing hash (you can also put your own hash value for the type of hash "
+"listed above)."
+msgstr ""
+"If 'Plaintext Password' filled and matches 'Confirm Plaintext Password' then "
+"this field becomes of hash of that password, otherwise this field remains "
+"the existing hash (you can also put your own hash value for the type of hash "
+"listed above)."
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:30
+msgid "Include full environment in logs"
+msgstr "Include full environment in logs"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:46
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:48
+msgid "Insecure hashes"
+msgstr "Insecure hashes"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:102
+msgid "Install package python3-openssl to support SSL connections"
+msgstr "Install package python3-openssl to support SSL connections"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:46
+msgid "Install python3-passlib and python3-bcrypt to enable a secure hash"
+msgstr "Install python3-passlib and python3-bcrypt to enable a secure hash"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:12
+msgid ""
+"Log configuration file (no file means default procd which ends up in syslog"
+msgstr ""
+"Log configuration file (no file means default procd which ends up in syslog"
+
+#: applications/luci-app-radicale2/luasrc/controller/radicale2.lua:28
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:6
+msgid "Logging"
+msgstr "Logging"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:12
+msgid "Logging File"
+msgstr "Logging File"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:105
+msgid "Lookup reverse DNS for clients for logging"
+msgstr "Lookup reverse DNS for clients for logging"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:34
+msgid "Mask Passwords"
+msgstr "Mask Passwords"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:36
+msgid "Max Connections"
+msgstr "Max Connections"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:42
+msgid "Max Content Length"
+msgstr "Max Content Length"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:31
+msgid "Max Sync Token Age"
+msgstr "Max Sync Token Age"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:36
+msgid "Maximum number of simultaneous connections"
+msgstr "Maximum number of simultaneous connections"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:42
+msgid "Maximum size of request body (bytes)"
+msgstr "Maximum size of request body (bytes)"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:15
+msgid "Multiple files on filesystem"
+msgstr "Multiple files on filesystem"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:17
+msgid "No authentication"
+msgstr "No authentication"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:98
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:105
+msgid "Password and confirmation do not match"
+msgstr "Password and confirmation do not match"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:29
+msgid "Password encryption method"
+msgstr "Password encryption method"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:32
+msgid "Plaintext"
+msgstr "Plaintext"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:76
+msgid "Plaintext Password"
+msgstr "Plaintext Password"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:25
+msgid ""
+"Prevent other instances or processes from modifying collections while in use"
+msgstr ""
+"Prevent other instances or processes from modifying collections while in use"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:66
+msgid "Private Key"
+msgstr "Private Key"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:18
+msgid "REMOTE_USER from web server"
+msgstr "REMOTE_USER from web server"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:186
+msgid "RO: All, RW: All"
+msgstr "RO: All, RW: All"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:184
+msgid "RO: Authenticated Users, RW: Owner"
+msgstr "RO: Authenticated Users, RW: Owner"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:183
+msgid "RO: None, RW: Authenticated Users"
+msgstr "RO: None, RW: Authenticated Users"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:182
+msgid "RO: None, RW: Owner"
+msgstr "RO: None, RW: Owner"
+
+#: applications/luci-app-radicale2/luasrc/controller/radicale2.lua:13
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:7
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:3
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:7
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:6
+msgid "Radicale 2.x"
+msgstr "Radicale 2.x"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:10
+msgid "Radicale v2 Web UI"
+msgstr "Radicale v2 Web UI"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:109
+msgid "Realm"
+msgstr "Realm"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:34
+msgid "Redact passwords in logs"
+msgstr "Redact passwords in logs"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:84
+msgid "Remove configuration for certificate, key, and CA"
+msgstr "Remove configuration for certificate, key, and CA"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:16
+msgid "Remove configuration for logging"
+msgstr "Remove configuration for logging"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:195
+msgid "Remove configuration for rights file"
+msgstr "Remove configuration for rights file"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:136
+msgid "Request"
+msgstr "Request"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:57
+msgid "Required time between a failed authentication attempt and trying again"
+msgstr "Required time between a failed authentication attempt and trying again"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:57
+msgid "Retry Delay"
+msgstr "Retry Delay"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:176
+msgid "Rights"
+msgstr "Rights"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:190
+msgid "Rights File"
+msgstr "Rights File"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:180
+msgid "Rights Type"
+msgstr "Rights Type"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:33
+msgid "SHA1"
+msgstr "SHA1"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:34
+msgid "SSHA"
+msgstr "SSHA"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:52
+msgid "SSL"
+msgstr "SSL"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:102
+msgid "SSL not available"
+msgstr "SSL not available"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:74
+msgid "See python3-openssl documentation for available ciphers"
+msgstr "See python3-openssl documentation for available ciphers"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:78
+msgid "See python3-openssl documentation for available protocols"
+msgstr "See python3-openssl documentation for available protocols"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:48
+msgid "Select bcrypt above to enable a secure hash"
+msgstr "Select bcrypt above to enable a secure hash"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:26
+msgid "Send debug information to logs"
+msgstr "Send debug information to logs"
+
+#: applications/luci-app-radicale2/luasrc/controller/radicale2.lua:16
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:18
+msgid "Server Settings"
+msgstr "Server Settings"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:47
+msgid "Socket timeout (seconds)"
+msgstr "Socket timeout (seconds)"
+
+#: applications/luci-app-radicale2/luasrc/controller/radicale2.lua:24
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:140
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:9
+msgid "Storage"
+msgstr "Storage"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:13
+msgid "Storage Type"
+msgstr "Storage Type"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:17
+msgid "This permanently deletes configuration for logging"
+msgstr "This permanently deletes configuration for logging"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:85
+msgid "This permanently deletes the cert, key, and configuration to use same."
+msgstr "This permanently deletes the cert, key, and configuration to use same."
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:196
+msgid "This permanently deletes the rights file and configuration to use same."
+msgstr ""
+"This permanently deletes the rights file and configuration to use same."
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:47
+msgid "Timeout"
+msgstr "Timeout"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:25
+msgid "Use File Locks"
+msgstr "Use File Locks"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:78
+msgid "Use Protocol"
+msgstr "Use Protocol"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:66
+msgid "User"
+msgstr "User"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:176
+msgid "User-based ACL Settings"
+msgstr "User-based ACL Settings"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:70
+msgid "Username"
+msgstr "Username"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:66
+msgid "Users and Passwords"
+msgstr "Users and Passwords"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:113
+msgid "Web UI"
+msgstr "Web UI"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:117
+msgid "Web UI Type"
+msgstr "Web UI Type"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:19
+msgid "X-Remote-User from web server"
+msgstr "X-Remote-User from web server"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:39
+msgid "bcrypt"
+msgstr "bcrypt"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:35
+msgid "crypt"
+msgstr "crypt"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:16
+msgid "htpasswd file (manually populated)"
+msgstr "htpasswd file (manually populated)"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:23
+msgid "htpasswd-formatted file filename"
+msgstr "htpasswd-formatted file filename"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:37
+msgid "md5"
+msgstr "md5"
diff --git a/applications/luci-app-radicale2/po/templates/radicale2.pot b/applications/luci-app-radicale2/po/templates/radicale2.pot
new file mode 100644
index 0000000000..be11ff1fe0
--- /dev/null
+++ b/applications/luci-app-radicale2/po/templates/radicale2.pot
@@ -0,0 +1,475 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:8
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:4
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:8
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:7
+msgid "A lightweight CalDAV/CardDAV server"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:30
+msgid "Advanced Settings"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:74
+msgid "Allowed Ciphers"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:10
+msgid "Authentication"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/controller/radicale2.lua:20
+msgid "Authentication / Users"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:14
+msgid "Authentication Type"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:185
+msgid "Based on settings in 'Rights File'"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:128
+msgid "CORS"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:62
+msgid "Certificate"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:70
+msgid "Client Certificate Authority"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:38
+msgid "Close Lock File"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:38
+msgid "Close the lock file when no more clients are waiting"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:44
+msgid "Command that is run after changes to storage"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:80
+msgid "Confirm Plaintext Password"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:105
+msgid "DNS Lookup"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:26
+msgid "Debug"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:15
+msgid "Default (htpasswd file from users below)"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:14
+msgid "Default (multifilesystem)"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:181
+msgid "Default (owner only)"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:31
+msgid "Delete sync token that are older (seconds)"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:132
+msgid "Document Encoding"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:30
+msgid "Dump Environment"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:52
+msgid "Enable SSL connections"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:136
+msgid "Encoding for responding to requests/events"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:140
+msgid "Encoding for storing local collections"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:110
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:169
+msgid "Encrypted Password"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:29
+msgid "Encryption"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:23
+msgid "Filename"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:19
+msgid "Folder"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:19
+msgid "Folder in which to store collections"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:70
+msgid "For verifying client certificates"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:29
+msgid "General Settings"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:169
+msgid ""
+"Generate this field using an generator for Apache htpasswd-style "
+"authentication files (for the hash format you have chosen above), or install "
+"python3-passlib to enable the ability to create the hash by entering the "
+"plaintext in a field that will appear on this page if python3-passlib is "
+"installed."
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/view/cbi/raduigo.htm:24
+msgid "Go to Radicale 2.x Web UI"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:14
+msgid "Go to Radicale v2 Web UI"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:109
+msgid "HTTP(S) Basic Authentication Realm"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:124
+msgid "HTTP(S) Headers"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:32
+msgid "HTTP(S) Listeners (address:port)"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:128
+msgid "Header: X-Access-Control-Allow-Origin"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:124
+msgid "Headers"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:44
+msgid "Hook"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:110
+msgid ""
+"If 'Plaintext Password' filled and matches 'Confirm Plaintext Password' then "
+"this field becomes of hash of that password, otherwise this field remains "
+"the existing hash (you can also put your own hash value for the type of hash "
+"listed above)."
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:30
+msgid "Include full environment in logs"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:46
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:48
+msgid "Insecure hashes"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:102
+msgid "Install package python3-openssl to support SSL connections"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:46
+msgid "Install python3-passlib and python3-bcrypt to enable a secure hash"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:12
+msgid ""
+"Log configuration file (no file means default procd which ends up in syslog"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/controller/radicale2.lua:28
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:6
+msgid "Logging"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:12
+msgid "Logging File"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:105
+msgid "Lookup reverse DNS for clients for logging"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:34
+msgid "Mask Passwords"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:36
+msgid "Max Connections"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:42
+msgid "Max Content Length"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:31
+msgid "Max Sync Token Age"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:36
+msgid "Maximum number of simultaneous connections"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:42
+msgid "Maximum size of request body (bytes)"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:15
+msgid "Multiple files on filesystem"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:17
+msgid "No authentication"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:98
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:105
+msgid "Password and confirmation do not match"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:29
+msgid "Password encryption method"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:32
+msgid "Plaintext"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:76
+msgid "Plaintext Password"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:25
+msgid ""
+"Prevent other instances or processes from modifying collections while in use"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:66
+msgid "Private Key"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:18
+msgid "REMOTE_USER from web server"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:186
+msgid "RO: All, RW: All"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:184
+msgid "RO: Authenticated Users, RW: Owner"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:183
+msgid "RO: None, RW: Authenticated Users"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:182
+msgid "RO: None, RW: Owner"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/controller/radicale2.lua:13
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:7
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:3
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:7
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:6
+msgid "Radicale 2.x"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:10
+msgid "Radicale v2 Web UI"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:109
+msgid "Realm"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:34
+msgid "Redact passwords in logs"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:84
+msgid "Remove configuration for certificate, key, and CA"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:16
+msgid "Remove configuration for logging"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:195
+msgid "Remove configuration for rights file"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:136
+msgid "Request"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:57
+msgid "Required time between a failed authentication attempt and trying again"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:57
+msgid "Retry Delay"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:176
+msgid "Rights"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:190
+msgid "Rights File"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:180
+msgid "Rights Type"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:33
+msgid "SHA1"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:34
+msgid "SSHA"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:52
+msgid "SSL"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:102
+msgid "SSL not available"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:74
+msgid "See python3-openssl documentation for available ciphers"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:78
+msgid "See python3-openssl documentation for available protocols"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:48
+msgid "Select bcrypt above to enable a secure hash"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:26
+msgid "Send debug information to logs"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/controller/radicale2.lua:16
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:18
+msgid "Server Settings"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:47
+msgid "Socket timeout (seconds)"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/controller/radicale2.lua:24
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:140
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:9
+msgid "Storage"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:13
+msgid "Storage Type"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/logging.lua:17
+msgid "This permanently deletes configuration for logging"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:85
+msgid "This permanently deletes the cert, key, and configuration to use same."
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:196
+msgid "This permanently deletes the rights file and configuration to use same."
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:47
+msgid "Timeout"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/storage.lua:25
+msgid "Use File Locks"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:78
+msgid "Use Protocol"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:66
+msgid "User"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:176
+msgid "User-based ACL Settings"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:70
+msgid "Username"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:66
+msgid "Users and Passwords"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:113
+msgid "Web UI"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/server.lua:117
+msgid "Web UI Type"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:19
+msgid "X-Remote-User from web server"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:39
+msgid "bcrypt"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:35
+msgid "crypt"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:16
+msgid "htpasswd file (manually populated)"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:23
+msgid "htpasswd-formatted file filename"
+msgstr ""
+
+#: applications/luci-app-radicale2/luasrc/model/cbi/radicale2/auth.lua:37
+msgid "md5"
+msgstr ""
diff --git a/libs/rpcd-mod-rad2-enc/Makefile b/libs/rpcd-mod-rad2-enc/Makefile
new file mode 100644
index 0000000000..4cffc01cd3
--- /dev/null
+++ b/libs/rpcd-mod-rad2-enc/Makefile
@@ -0,0 +1,49 @@
+#
+# Copyright (C) 2016-2017 Jo-Philipp Wich <jo@mein.io>
+#
+# Licensed under the Apache License, Version 2.0.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=rpcd-mod-rad2-enc
+PKG_VERSION:=20190109
+PKG_MAINTAINER:=Daniel Dickinson <cshored@thecshore.com>
+
+PKG_LICENSE:=Apache-2.0
+
+PKG_BUILD_PARALLEL:=1
+
+include $(INCLUDE_DIR)/package.mk
+
+define Build/Prepare
+ true
+endef
+
+define Build/Compile
+ true
+endef
+
+define Package/rpcd-mod-rad2-enc
+ SECTION:=libs
+ CATEGORY:=Libraries
+ TITLE:=Radicale 2.x Hashing RPC module
+ DEPENDS:=+rpcd +python3 +python3-passlib
+endef
+
+define Package/rpcd-mod-rad2-enc/description
+ Python3 password hashing module for use Radicale 2.x LuCI app
+endef
+
+define Package/rpcd-mod-rad2-enc/install
+ $(INSTALL_DIR) $(1)/usr/libexec/rpcd
+ $(INSTALL_BIN) ./files/rad2-enc $(1)/usr/libexec/rpcd
+endef
+
+define Package/rpcd-mod-rad2-enc/postinst
+#!/bin/sh
+killall -HUP rpcd 2>/dev/null
+exit 0
+endef
+
+$(eval $(call BuildPackage,rpcd-mod-rad2-enc))
diff --git a/libs/rpcd-mod-rad2-enc/files/rad2-enc b/libs/rpcd-mod-rad2-enc/files/rad2-enc
new file mode 100755
index 0000000000..43bc49325c
--- /dev/null
+++ b/libs/rpcd-mod-rad2-enc/files/rad2-enc
@@ -0,0 +1,50 @@
+#!/usr/bin/python3
+
+import base64
+import sys
+import json
+from passlib import hash
+
+def main():
+
+ if len(sys.argv) < 2:
+ return -1
+
+ if sys.argv[1] == 'list':
+ print('{ "encrypt": { "type": "str", "plainpass": "str" } }\n')
+ return 0
+
+ if sys.argv[1] == 'call':
+ if len(sys.argv) < 3:
+ return -1
+
+ if sys.argv[2] != 'encrypt':
+ return -1
+
+ encpass = ""
+ try:
+ jsonin = json.loads(sys.stdin.readline())
+ enctype = jsonin['type'].strip()
+ plainpass = jsonin['plainpass']
+
+ if enctype == 'ssha':
+ encpass = hash.ldap_salted_sha1.hash(plainpass)
+ elif enctype == 'sha1':
+ encpass = hash.ldap_sha1.hash(plainpass)
+ elif enctype == 'plain':
+ encpass = plainpass
+ elif enctype == 'md5':
+ encpass = hash.apr_md5_crypt.hash(plainpass)
+ elif enctype == 'bcrypt':
+ encpass = hash.bcrypt.hash(plainpass)
+ elif enctype == 'crypt':
+ encpass = hash.des_crypt.hash(plainpass)
+
+ except:
+ encpass = ""
+
+ print(json.dumps({ "encrypted_password": encpass}))
+
+ return 0
+
+main()