| 59 | | |
| 60 | | --- Manually compile a given template into an executable Lua function |
| 61 | | -- @param template LuCI template |
| 62 | | -- @return Lua template function |
| 63 | | function compile(template) |
| 64 | | local expr = {} |
| 65 | | |
| 66 | | -- Search all <% %> expressions |
| 67 | | local function expr_add(ws1, skip1, command, skip2, ws2) |
| 68 | | expr[#expr+1] = command |
| 69 | | return ( #skip1 > 0 and "" or ws1 ) .. |
| 70 | | "<%" .. tostring(#expr) .. "%>" .. |
| 71 | | ( #skip2 > 0 and "" or ws2 ) |
| 72 | | end |
| 73 | | |
| 74 | | -- Save all expressiosn to table "expr" |
| 75 | | template = template:gsub("(%s*)<%%(%-?)(.-)(%-?)%%>(%s*)", expr_add) |
| 76 | | |
| 77 | | local function sanitize(s) |
| 78 | | s = "%q" % s |
| 79 | | return s:sub(2, #s-1) |
| 80 | | end |
| 81 | | |
| 82 | | -- Escape and sanitize all the template (all non-expressions) |
| 83 | | template = sanitize(template) |
| 84 | | |
| 85 | | -- Template module header/footer declaration |
| 86 | | local header = 'write("' |
| 87 | | local footer = '")' |
| 88 | | |
| 89 | | template = header .. template .. footer |
| 90 | | |
| 91 | | -- Replacements |
| 92 | | local r_include = '")\ninclude("%s")\nwrite("' |
| 93 | | local r_i18n = '")\nwrite(translate("%1","%2"))\nwrite("' |
| 94 | | local r_i18n2 = '")\nwrite(translate("%1", ""))\nwrite("' |
| 95 | | local r_pexec = '")\nwrite(tostring(%s or ""))\nwrite("' |
| 96 | | local r_exec = '")\n%s\nwrite("' |
| 97 | | |
| 98 | | -- Parse the expressions |
| 99 | | for k,v in pairs(expr) do |
| 100 | | local p = v:sub(1, 1) |
| 101 | | v = v:gsub("%%", "%%%%") |
| 102 | | local re = nil |
| 103 | | if p == "+" then |
| 104 | | re = r_include:format(sanitize(string.sub(v, 2))) |
| 105 | | elseif p == ":" then |
| 106 | | if v:find(" ") then |
| 107 | | re = sanitize(v):gsub(":(.-) (.*)", r_i18n) |
| 108 | | else |
| 109 | | re = sanitize(v):gsub(":(.+)", r_i18n2) |
| 110 | | end |
| 111 | | elseif p == "=" then |
| 112 | | re = r_pexec:format(v:sub(2)) |
| 113 | | elseif p == "#" then |
| 114 | | re = "" |
| 115 | | else |
| 116 | | re = r_exec:format(v) |
| 117 | | end |
| 118 | | template = template:gsub("<%%"..tostring(k).."%%>", re) |
| 119 | | end |
| 120 | | |
| 121 | | return loadstring(template) |
| 122 | | end |
| 164 | | if self.template then |
| 165 | | return |
| 166 | | end |
| 167 | | |
| 168 | | -- Enforce cache security |
| 169 | | local cdir = compiledir .. "/" .. sys.process.info("uid") |
| 170 | | |
| 171 | | -- Compile and build |
| 172 | | local sourcefile = viewdir .. "/" .. name |
| 173 | | local compiledfile = cdir .. "/" .. _encode_filename(name) .. ".lua" |
| 174 | | local err |
| 175 | | |
| 176 | | if compiler_mode == "file" then |
| 177 | | local tplmt = fs.stat(sourcefile, "mtime") or fs.stat(sourcefile .. ".htm", "mtime") |
| 178 | | local commt = fs.stat(compiledfile, "mtime") |
| 179 | | |
| 180 | | if not fs.stat(cdir, "mtime") then |
| 181 | | fs.mkdirr(cdir) |
| 182 | | fs.chmod(fs.dirname(cdir), 777) |
| | 76 | if not self.template then |
| | 77 | |
| | 78 | -- Compile template |
| | 79 | local sourcefile = viewdir .. "/" .. name .. ".htm" |
| | 80 | self.template, _, err = tparser.parse(sourcefile) |
| | 81 | |
| | 82 | -- If we have no valid template throw error, otherwise cache the template |
| | 83 | if not self.template then |
| | 84 | error(err) |
| | 85 | else |
| | 86 | self.cache[name] = self.template |
| 184 | | |
| 185 | | assert(tplmt or commt, "No such template: " .. name) |
| 186 | | |
| 187 | | -- Build if there is no compiled file or if compiled file is outdated |
| 188 | | if not commt or (commt and tplmt and commt < tplmt) then |
| 189 | | local source |
| 190 | | source, err = fs.readfile(sourcefile) or fs.readfile(sourcefile .. ".htm") |
| 191 | | |
| 192 | | if source then |
| 193 | | local compiled, err = compile(source) |
| 194 | | |
| 195 | | local f = nixio.open(compiledfile, "w", 600) |
| 196 | | f:writeall(util.get_bytecode(compiled)) |
| 197 | | f:close() |
| 198 | | self.template = compiled |
| 199 | | end |
| 200 | | else |
| 201 | | assert( |
| 202 | | sys.process.info("uid") == fs.stat(compiledfile, "uid") |
| 203 | | and fs.stat(compiledfile, "modestr") == "rw-------", |
| 204 | | "Fatal: Cachefile is not sane!" |
| 205 | | ) |
| 206 | | self.template, err = loadfile(compiledfile) |
| 207 | | end |
| 208 | | |
| 209 | | elseif compiler_mode == "memory" then |
| 210 | | self.template, _, err = tparser.parse(sourcefile .. ".htm") |
| 211 | | end |
| 212 | | |
| 213 | | -- If we have no valid template throw error, otherwise cache the template |
| 214 | | if not self.template then |
| 215 | | error(err) |
| 216 | | else |
| 217 | | self.cache[name] = self.template |