radicale.lua 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. -- Copyright 2014 Christian Schoenebeck <christian dot schoenebeck at gmail dot com>
  2. -- Licensed under the Apache License, Version 2.0
  3. module("luci.controller.radicale", package.seeall)
  4. local NX = require("nixio")
  5. local NXFS = require("nixio.fs")
  6. local DISP = require "luci.dispatcher"
  7. local HTTP = require("luci.http")
  8. local I18N = require("luci.i18n") -- not globally avalible here
  9. local UTIL = require("luci.util")
  10. local SYS = require("luci.sys")
  11. function index()
  12. entry( {"admin", "services", "radicale"}, alias("admin", "services", "radicale", "edit"), _("CalDAV/CardDAV"), 58)
  13. entry( {"admin", "services", "radicale", "edit"}, cbi("radicale") ).leaf = true
  14. entry( {"admin", "services", "radicale", "logview"}, call("_logread") ).leaf = true
  15. entry( {"admin", "services", "radicale", "startstop"}, call("_startstop") ).leaf = true
  16. entry( {"admin", "services", "radicale", "status"}, call("_status") ).leaf = true
  17. end
  18. -- called by XHR.get from detail_logview.htm
  19. function _logread()
  20. -- read application settings
  21. local uci = UCI.cursor()
  22. local logfile = uci:get("radicale", "radicale", "logfile") or "/var/log/radicale"
  23. uci:unload("radicale")
  24. local ldata=NXFS.readfile(logfile)
  25. if not ldata or #ldata == 0 then
  26. ldata="_nodata_"
  27. end
  28. HTTP.write(ldata)
  29. end
  30. -- called by XHR.get from detail_startstop.htm
  31. function _startstop()
  32. local pid = get_pid()
  33. if pid > 0 then
  34. SYS.call("/etc/init.d/radicale stop")
  35. NX.nanosleep(1) -- sleep a second
  36. if NX.kill(pid, 0) then -- still running
  37. NX.kill(pid, 9) -- send SIGKILL
  38. end
  39. pid = 0
  40. else
  41. SYS.call("/etc/init.d/radicale start")
  42. NX.nanosleep(1) -- sleep a second
  43. pid = get_pid()
  44. if pid > 0 and not NX.kill(pid, 0) then
  45. pid = 0 -- process did not start
  46. end
  47. end
  48. HTTP.write(tostring(pid)) -- HTTP needs string not number
  49. end
  50. -- called by XHR.poll from detail_startstop.htm
  51. function _status()
  52. local pid = get_pid()
  53. HTTP.write(tostring(pid)) -- HTTP needs string not number
  54. end
  55. -- Application / Service specific information functions ########################
  56. function luci_app_name()
  57. return "luci-app-radicale"
  58. end
  59. function service_name()
  60. return "radicale"
  61. end
  62. function service_required()
  63. return "0.10-1"
  64. end
  65. function service_installed()
  66. local v = ipkg_ver_installed("radicale-py2")
  67. if not v or #v == 0 then v = ipkg_ver_installed("radicale-py3") end
  68. if not v or #v == 0 then v = "0" end
  69. return v
  70. end
  71. function service_ok()
  72. return ipkg_ver_compare(service_installed(),">=",service_required())
  73. end
  74. function app_title_main()
  75. return [[</a><a href="javascript:alert(']]
  76. .. I18N.translate("Version Information")
  77. .. [[\n\n]] .. luci_app_name()
  78. .. [[\n\t]] .. I18N.translate("Version") .. [[:\t]]
  79. .. (ipkg_ver_installed(luci_app_name()) == ""
  80. and I18N.translate("NOT installed")
  81. or ipkg_ver_installed(luci_app_name()) )
  82. .. [[\n\n]] .. service_name() .. [[ ]] .. I18N.translate("required") .. [[:]]
  83. .. [[\n\t]] .. I18N.translate("Version") .. [[:\t]]
  84. .. service_required() .. [[ ]] .. I18N.translate("or higher")
  85. .. [[\n\n]] .. service_name() .. [[ ]] .. I18N.translate("installed") .. [[:]]
  86. .. [[\n\t]] .. I18N.translate("Version") .. [[:\t]]
  87. .. (service_installed() == "0"
  88. and I18N.translate("NOT installed")
  89. or service_installed())
  90. .. [[\n\n]]
  91. .. [[')">]]
  92. .. I18N.translate("Radicale CalDAV/CardDAV Server")
  93. end
  94. function app_title_back()
  95. return [[</a><a href="]]
  96. .. DISP.build_url("admin", "services", "radicale")
  97. .. [[">]]
  98. .. I18N.translate("Radicale CalDAV/CardDAV Server")
  99. end
  100. function app_description()
  101. return I18N.translate("The Radicale Project is a complete CalDAV (calendar) and CardDAV (contact) server solution.") .. [[<br />]]
  102. .. I18N.translate("Calendars and address books are available for both local and remote access, possibly limited through authentication policies.") .. [[<br />]]
  103. .. I18N.translate("They can be viewed and edited by calendar and contact clients on mobile phones or computers.")
  104. end
  105. -- other multiused functions ###################################################
  106. --return pid of running process
  107. function get_pid()
  108. return tonumber(SYS.exec([[ps | grep "[p]ython.*[r]adicale" 2>/dev/null | awk '{print $1}']])) or 0
  109. end
  110. -- compare versions using "<=" "<" ">" ">=" "=" "<<" ">>"
  111. function ipkg_ver_compare(ver1, comp, ver2)
  112. if not ver1 or not ver2
  113. or not comp or not (#comp > 0) then return nil end
  114. -- correct compare string
  115. if comp == "<>" or comp == "><" or comp == "!=" or comp == "~=" then comp = "~="
  116. elseif comp == "<=" or comp == "<" or comp == "=<" then comp = "<="
  117. elseif comp == ">=" or comp == ">" or comp == "=>" then comp = ">="
  118. elseif comp == "=" or comp == "==" then comp = "=="
  119. elseif comp == "<<" then comp = "<"
  120. elseif comp == ">>" then comp = ">"
  121. else return nil end
  122. local av1 = UTIL.split(ver1, "[%.%-]", nil, true)
  123. local av2 = UTIL.split(ver2, "[%.%-]", nil, true)
  124. for i = 1, math.max(table.getn(av1),table.getn(av2)), 1 do
  125. local s1 = av1[i] or ""
  126. local s2 = av2[i] or ""
  127. -- first "not equal" found return true
  128. if comp == "~=" and (s1 ~= s2) then return true end
  129. -- first "lower" found return true
  130. if (comp == "<" or comp == "<=") and (s1 < s2) then return true end
  131. -- first "greater" found return true
  132. if (comp == ">" or comp == ">=") and (s1 > s2) then return true end
  133. -- not equal then return false
  134. if (s1 ~= s2) then return false end
  135. end
  136. -- all equal and not compare greater or lower then true
  137. return not (comp == "<" or comp == ">")
  138. end
  139. -- read version information for given package if installed
  140. function ipkg_ver_installed(pkg)
  141. local version = ""
  142. local control = io.open("/usr/lib/opkg/info/%s.control" % pkg, "r")
  143. if control then
  144. local ln
  145. repeat
  146. ln = control:read("*l")
  147. if ln and ln:match("^Version: ") then
  148. version = ln:gsub("^Version: ", "")
  149. break
  150. end
  151. until not ln
  152. control:close()
  153. end
  154. return version
  155. end
  156. -- replacement of build-in Flag.parse of cbi.lua
  157. -- modified to mark section as changed if value changes
  158. -- current parse did not do this, but it is done AbstaractValue.parse()
  159. function flag_parse(self, section)
  160. local fexists = self.map:formvalue(
  161. luci.cbi.FEXIST_PREFIX .. self.config .. "." .. section .. "." .. self.option)
  162. if fexists then
  163. local fvalue = self:formvalue(section) and self.enabled or self.disabled
  164. local cvalue = self:cfgvalue(section)
  165. if fvalue ~= self.default or (not self.optional and not self.rmempty) then
  166. self:write(section, fvalue)
  167. else
  168. self:remove(section)
  169. end
  170. if (fvalue ~= cvalue) then self.section.changed = true end
  171. else
  172. self:remove(section)
  173. self.section.changed = true
  174. end
  175. end