summaryrefslogtreecommitdiffhomepage
path: root/applications/luci-app-radicale/luasrc/controller/radicale.lua
blob: 662c60d5a697034f44f372d5a97b70ba76510afc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
-- Copyright 2014 Christian Schoenebeck <christian dot schoenebeck at gmail dot com>
-- Licensed under the Apache License, Version 2.0

module("luci.controller.radicale", package.seeall)

local NX    = require("nixio")
local NXFS  = require("nixio.fs")
local DISP  = require "luci.dispatcher"
local HTTP  = require("luci.http")
local I18N = require("luci.i18n")	-- not globally avalible here
local UTIL  = require("luci.util")
local SYS   = require("luci.sys")

function index()
	entry( {"admin", "services", "radicale"}, alias("admin", "services", "radicale", "edit"), _("CalDAV/CardDAV"), 58)
	entry( {"admin", "services", "radicale", "edit"}, cbi("radicale") ).leaf = true
	entry( {"admin", "services", "radicale", "logview"}, call("_logread") ).leaf = true
	entry( {"admin", "services", "radicale", "startstop"}, call("_startstop") ).leaf = true
	entry( {"admin", "services", "radicale", "status"}, call("_status") ).leaf = true
end

-- called by XHR.get from detail_logview.htm
function _logread()
	-- read application settings
	local uci     = UCI.cursor()
	local logfile = uci:get("radicale", "radicale", "logfile") or "/var/log/radicale"
	uci:unload("radicale")

	local ldata=NXFS.readfile(logfile)
	if not ldata or #ldata == 0 then
		ldata="_nodata_"
	end
	HTTP.write(ldata)
end

-- called by XHR.get from detail_startstop.htm
function _startstop()
	local pid = get_pid()
	if pid > 0 then
		SYS.call("/etc/init.d/radicale stop")
		NX.nanosleep(1)		-- sleep a second
		if NX.kill(pid, 0) then	-- still running
			NX.kill(pid, 9)	-- send SIGKILL
		end
		pid = 0
	else
		SYS.call("/etc/init.d/radicale start")
		NX.nanosleep(1)		-- sleep a second
		pid = get_pid()
		if pid > 0 and not NX.kill(pid, 0) then
			pid = 0		-- process did not start
		end
	end
	HTTP.write(tostring(pid))	-- HTTP needs string not number
end

-- called by XHR.poll from detail_startstop.htm
function _status()
	local pid = get_pid()
	HTTP.write(tostring(pid))	-- HTTP needs string not number
end

-- Application / Service specific information functions ########################
function luci_app_name()
	return	"luci-app-radicale"
end

function service_name()
	return	"radicale"
end
function service_required()
	return	"0.10-1"
end
function service_installed()
	local v = ipkg_ver_installed("radicale-py2")
	if not v or #v == 0 then v = ipkg_ver_installed("radicale-py3") end
	if not v or #v == 0 then v = "0" end
	return v
end
function service_ok()
	return	ipkg_ver_compare(service_installed(),">=",service_required())
end

function app_title_main()
	return	[[</a><a href="javascript:alert(']]
			.. I18N.translate("Version Information")
			.. [[\n\n]] .. luci_app_name()
			.. [[\n\t]] .. I18N.translate("Version") .. [[:\t]]
				.. (ipkg_ver_installed(luci_app_name()) == ""
					and I18N.translate("NOT installed")
					or ipkg_ver_installed(luci_app_name()) )
			.. [[\n\n]] .. service_name() .. [[ ]] .. I18N.translate("required") .. [[:]]
			.. [[\n\t]] .. I18N.translate("Version") .. [[:\t]]
				.. service_required() .. [[ ]] .. I18N.translate("or higher")
			.. [[\n\n]] .. service_name() .. [[ ]] .. I18N.translate("installed") .. [[:]]
			.. [[\n\t]] .. I18N.translate("Version") .. [[:\t]]
				.. (service_installed() == "0"
					and I18N.translate("NOT installed")
					or service_installed())
			.. [[\n\n]]
	 	.. [[')">]]
		.. I18N.translate("Radicale CalDAV/CardDAV Server")
end
function app_title_back()
	return	[[</a><a href="]]
			.. DISP.build_url("admin", "services", "radicale")
		.. [[">]]
		.. I18N.translate("Radicale CalDAV/CardDAV Server")
end
function app_description()
	return	I18N.translate("The Radicale Project is a complete CalDAV (calendar) and CardDAV (contact) server solution.") .. [[<br />]]
	     .. I18N.translate("Calendars and address books are available for both local and remote access, possibly limited through authentication policies.") .. [[<br />]]
	     .. I18N.translate("They can be viewed and edited by calendar and contact clients on mobile phones or computers.")
end

-- other multiused functions ###################################################

--return pid of running process
function get_pid()
	return tonumber(SYS.exec([[ps | grep "[p]ython.*[r]adicale" 2>/dev/null | awk '{print $1}']])) or 0
end

-- compare versions using "<=" "<" ">" ">=" "=" "<<" ">>"
function ipkg_ver_compare(ver1, comp, ver2)
	if not ver1 or not (#ver1 > 0)
	or not ver2 or not (#ver2 > 0)
	or not comp or not (#comp > 0) then return nil end
	-- correct compare string
	if comp == "<>" or comp == "><" or comp == "!=" or comp == "~=" then comp = "~="
	elseif comp == "<=" or comp == "<" or comp == "=<" then comp = "<="
	elseif comp == ">=" or comp == ">" or comp == "=>" then comp = ">="
	elseif comp == "="  or comp == "==" then comp = "=="
	elseif comp == "<<" then comp = "<"
	elseif comp == ">>" then comp = ">"
	else return nil end

	local av1 = UTIL.split(ver1, "[%.%-]", nil, true)
	local av2 = UTIL.split(ver2, "[%.%-]", nil, true)

	for i = 1, math.max(table.getn(av1),table.getn(av2)), 1  do
		local s1 = av1[i] or ""
		local s2 = av2[i] or ""
		local n1 = tonumber(s1)
		local n2 = tonumber(s2)

		-- one numeric and other empty string then set other to 0
		if n1 and not n2 and (not s2 or #s2 == 0) then n2 = 0 end
		if n2 and not n1 and (not s1 or #s1 == 0) then n1 = 0 end

		local nc = (n1 and n2)	-- numeric compare

		if nc then
			-- first "not equal" found return true
			if comp == "~=" and (n1 ~= n2) then return true end
			-- first "lower" found return true
			if (comp == "<" or comp == "<=") and (n1 < n2) then return true end
			-- first "greater" found return true
			if (comp == ">" or comp == ">=") and (n1 > n2) then return true end
			-- not equal then return false
			if (n1 ~= n2) then return false end
		else
			if comp == "~=" and (s1 ~= s2) then return true end
			if (comp == "<" or comp == "<=") and (s1 < s2) then return true end
			if (comp == ">" or comp == ">=") and (s1 > s2) then return true end
			if (s1 ~= s2) then return false end
		end
	end
	-- all equal then true
	return true
end

-- read version information for given package if installed
function ipkg_ver_installed(pkg)
	local version = ""
	local control = io.open("/usr/lib/opkg/info/%s.control" % pkg, "r")
	if control then
		local ln
		repeat
			ln = control:read("*l")
			if ln and ln:match("^Version: ") then
				version = ln:gsub("^Version: ", "")
				break
			end
		until not ln
		control:close()
	end
	return version
end

-- replacement of build-in Flag.parse of cbi.lua
-- modified to mark section as changed if value changes
-- current parse did not do this, but it is done AbstaractValue.parse()
function flag_parse(self, section)
	local fexists = self.map:formvalue(
		luci.cbi.FEXIST_PREFIX .. self.config .. "." .. section .. "." .. self.option)

	if fexists then
		local fvalue = self:formvalue(section) and self.enabled or self.disabled
		local cvalue = self:cfgvalue(section)
		if fvalue ~= self.default or (not self.optional and not self.rmempty) then
			self:write(section, fvalue)
		else
			self:remove(section)
		end
		if (fvalue ~= cvalue) then self.section.changed = true end
	else
		self:remove(section)
		self.section.changed = true
	end
end