diff options
Diffstat (limited to '')
-rwxr-xr-x | apt-panopticon-report-email-web.lua | 250 |
1 files changed, 47 insertions, 203 deletions
diff --git a/apt-panopticon-report-email-web.lua b/apt-panopticon-report-email-web.lua index 1144550..ef61183 100755 --- a/apt-panopticon-report-email-web.lua +++ b/apt-panopticon-report-email-web.lua | |||
@@ -1,143 +1,22 @@ | |||
1 | #!/usr/bin/env luajit | 1 | #!/usr/bin/env luajit |
2 | 2 | ||
3 | local args = {...} | 3 | local APT = require 'apt-panopticommon' |
4 | local D = APT.D | ||
5 | local I = APT.I | ||
6 | local W = APT.W | ||
7 | local E = APT.E | ||
8 | local C = APT.C | ||
9 | local arg, sendArgs = APT.parseArgs({...}) | ||
4 | 10 | ||
5 | verbosity = -1 | ||
6 | local logFile | ||
7 | local html = false | ||
8 | |||
9 | |||
10 | --[[ Ordered table iterator, allow to iterate on the natural order of the keys of a table. | ||
11 | From http://lua-users.org/wiki/SortedIteration | ||
12 | ]] | ||
13 | function __genOrderedIndex( t ) | ||
14 | local orderedIndex = {} | ||
15 | for key in pairs(t) do | ||
16 | table.insert( orderedIndex, key ) | ||
17 | end | ||
18 | table.sort( orderedIndex ) | ||
19 | return orderedIndex | ||
20 | end | ||
21 | function orderedNext(t, state) | ||
22 | -- Equivalent of the next function, but returns the keys in the alphabetic | ||
23 | -- order. We use a temporary ordered key table that is stored in the | ||
24 | -- table being iterated. | ||
25 | |||
26 | local key = nil | ||
27 | --print("orderedNext: state = "..tostring(state) ) | ||
28 | if state == nil then | ||
29 | -- the first time, generate the index | ||
30 | t.__orderedIndex = __genOrderedIndex( t ) | ||
31 | key = t.__orderedIndex[1] | ||
32 | else | ||
33 | -- fetch the next value | ||
34 | for i = 1,table.getn(t.__orderedIndex) do | ||
35 | if t.__orderedIndex[i] == state then | ||
36 | key = t.__orderedIndex[i+1] | ||
37 | end | ||
38 | end | ||
39 | end | ||
40 | |||
41 | if key then | ||
42 | return key, t[key] | ||
43 | end | ||
44 | |||
45 | -- no more value to return, cleanup | ||
46 | t.__orderedIndex = nil | ||
47 | return | ||
48 | end | ||
49 | function orderedPairs(t) | ||
50 | -- Equivalent of the pairs() function on tables. Allows to iterate | ||
51 | -- in order | ||
52 | return orderedNext, t, nil | ||
53 | end | ||
54 | |||
55 | -- Use this to dump a table to a string, with HTML. | ||
56 | dumpTableHTML = function (table, space, name) | ||
57 | local r = name .. "\n" | ||
58 | r = r .. dumpTableHTMLSub(table, space .. " ") | ||
59 | r = r .. space .. "" | ||
60 | return r | ||
61 | end | ||
62 | dumpTableHTMLSub = function (table, space) | ||
63 | local r = "" | ||
64 | for k, v in orderedPairs(table) do | ||
65 | if type(v) == "table" then | ||
66 | if " " == space then | ||
67 | r = r .. space .. dumpTableHTML(v, space, k .. "<ul>") .. "</ul>\n" | ||
68 | else | ||
69 | r = r .. "<li>" .. space .. dumpTableHTML(v, space, k .. "<ul>") .. "</ul></li>\n" | ||
70 | end | ||
71 | else | ||
72 | r = r .. space .. "<li>" .. k .. "</li>\n" | ||
73 | end | ||
74 | end | ||
75 | return r | ||
76 | end | ||
77 | |||
78 | local checkFile = function(f) | ||
79 | local h, e = io.open(f, "r") | ||
80 | if nil == h then return false else h:close(); return true end | ||
81 | end | ||
82 | |||
83 | local plurals = function(e, w) | ||
84 | local result = "" | ||
85 | if 1 == e then | ||
86 | result = e .. " error" | ||
87 | elseif e ~= 0 then | ||
88 | result = e .. " errors" | ||
89 | end | ||
90 | if ("" ~= result) and html then result = "<font color='red'><b>" .. result .. "</b></font>" end | ||
91 | -- result = " " .. result | ||
92 | if 0 < w then | ||
93 | if 0 < e then result = result .. ", " end | ||
94 | if 1 == w then | ||
95 | result = result .. w .. " warning" | ||
96 | else | ||
97 | result = result .. w .. " warnings" | ||
98 | end | ||
99 | if ("" ~= result) and html then result = "<font color='yellow'><b>" .. result .. "</b></font>" end | ||
100 | -- result = " " .. result | ||
101 | end | ||
102 | if "" ~= result then result = " (" .. result .. ")" end | ||
103 | return result | ||
104 | end | ||
105 | 11 | ||
106 | local results = {} | 12 | local results = {} |
107 | 13 | APT.mirrors = loadfile("results/mirrors.lua")() | |
108 | local log = function(v, t, s, prot, test, host) | ||
109 | local x = "" | ||
110 | if nil == prot then prot = "" end | ||
111 | if nil ~= test then x = x .. test else test = "" end | ||
112 | if nil ~= host then | ||
113 | if #x > 0 then x = x .. " " end | ||
114 | x = x .. host | ||
115 | end | ||
116 | if #x > 0 then | ||
117 | t = t .. "(" .. x .. ")" | ||
118 | end | ||
119 | if v <= verbosity then | ||
120 | if 3 <= verbosity then t = os.date() .. " " .. t end | ||
121 | print(t .. ": " .. s) | ||
122 | end | ||
123 | if nil ~= logFile then | ||
124 | logFile:write(os.date() .. " " .. t .. ": " .. s .. "\n") | ||
125 | logFile:flush() | ||
126 | end | ||
127 | end | ||
128 | local D = function(s) log(3, "DEBUG ", s) end | ||
129 | local I = function(s) log(2, "INFO ", s) end | ||
130 | local W = function(s, p, t, h) log(1, "WARNING ", s, p, t, h) end | ||
131 | local E = function(s, p, t, h) log(0, "ERROR ", s, p, t, h) end | ||
132 | local C = function(s) log(-1, "CRITICAL", s) end | ||
133 | |||
134 | local mirrors = loadfile("results/mirrors.lua")() | ||
135 | 14 | ||
136 | local revDNS = function(dom, IP) | 15 | local revDNS = function(dom, IP) |
137 | if "deb.devuan.org" ~= dom then | 16 | if "deb.devuan.org" ~= dom then |
138 | if nil ~= mirrors["deb.devuan.org"] then | 17 | if nil ~= APT.mirrors["deb.devuan.org"] then |
139 | if nil ~= mirrors["deb.devuan.org"].IPs["deb.roundr.devuan.org"][IP] then | 18 | if nil ~= APT.mirrors["deb.devuan.org"].IPs["deb.roundr.devuan.org"][IP] then |
140 | if html then | 19 | if APT.html then |
141 | return "<font color='purple'><b>DNS-RR</b></font>" | 20 | return "<font color='purple'><b>DNS-RR</b></font>" |
142 | else | 21 | else |
143 | return "DNS-RR" | 22 | return "DNS-RR" |
@@ -145,7 +24,7 @@ local revDNS = function(dom, IP) | |||
145 | end | 24 | end |
146 | end | 25 | end |
147 | else | 26 | else |
148 | for k, v in pairs(mirrors) do | 27 | for k, v in pairs(APT.mirrors) do |
149 | if "deb.devuan.org" ~= k then | 28 | if "deb.devuan.org" ~= k then |
150 | local IPs = v.IPs | 29 | local IPs = v.IPs |
151 | for i, u in pairs(IPs) do | 30 | for i, u in pairs(IPs) do |
@@ -168,7 +47,7 @@ local status = function(host, results, typ) | |||
168 | local result = "" | 47 | local result = "" |
169 | local e = 0 | 48 | local e = 0 |
170 | local w = 0 | 49 | local w = 0 |
171 | local s = nil ~= mirrors[host].Protocols[typ] | 50 | local s = nil ~= APT.mirrors[host].Protocols[typ] |
172 | local to = results.timeout | 51 | local to = results.timeout |
173 | if ('http' ~= typ) and ('https' ~= typ) and ('ftp' ~= typ) and ('rsync' ~= typ) then s = true end | 52 | if ('http' ~= typ) and ('https' ~= typ) and ('ftp' ~= typ) and ('rsync' ~= typ) then s = true end |
174 | if nil ~= results[typ] then | 53 | if nil ~= results[typ] then |
@@ -198,7 +77,7 @@ local status = function(host, results, typ) | |||
198 | if to then | 77 | if to then |
199 | result = "[TIMEOUT" | 78 | result = "[TIMEOUT" |
200 | if not s then result = result .. "*" end | 79 | if not s then result = result .. "*" end |
201 | if html then | 80 | if APT.html then |
202 | if s then | 81 | if s then |
203 | result = "[<font color='blue'><b>TIMEOUT</b></font>" | 82 | result = "[<font color='blue'><b>TIMEOUT</b></font>" |
204 | else | 83 | else |
@@ -208,14 +87,14 @@ local status = function(host, results, typ) | |||
208 | elseif 0 < e then | 87 | elseif 0 < e then |
209 | result = "[FAILED" | 88 | result = "[FAILED" |
210 | if not s then result = result .. "*" end | 89 | if not s then result = result .. "*" end |
211 | if html then | 90 | if APT.html then |
212 | if s then | 91 | if s then |
213 | result = "[<font color='red'><b>FAILED</b></font>" | 92 | result = "[<font color='red'><b>FAILED</b></font>" |
214 | else | 93 | else |
215 | result = "[<font color='darkred'><b>FAILED*</b></font>" | 94 | result = "[<font color='darkred'><b>FAILED*</b></font>" |
216 | end | 95 | end |
217 | end | 96 | end |
218 | if html then | 97 | if APT.html then |
219 | faulty = faulty .. host .. " (" .. typ .. ")<br>\n" | 98 | faulty = faulty .. host .. " (" .. typ .. ")<br>\n" |
220 | else | 99 | else |
221 | faulty = faulty .. host .. " (" .. typ .. ")\n" | 100 | faulty = faulty .. host .. " (" .. typ .. ")\n" |
@@ -223,7 +102,7 @@ local status = function(host, results, typ) | |||
223 | else | 102 | else |
224 | result = "[OK" | 103 | result = "[OK" |
225 | if not s then result = result .. "*" end | 104 | if not s then result = result .. "*" end |
226 | if html then | 105 | if APT.html then |
227 | if s then | 106 | if s then |
228 | result = "[<font color='lime'><b>OK</b></font>" | 107 | result = "[<font color='lime'><b>OK</b></font>" |
229 | else | 108 | else |
@@ -231,43 +110,7 @@ local status = function(host, results, typ) | |||
231 | end | 110 | end |
232 | end | 111 | end |
233 | end | 112 | end |
234 | return result .. plurals(e, w) .. "]" | 113 | return result .. APT.plurals(e, w) .. "]" |
235 | end | ||
236 | |||
237 | local collate = function(host, ip, results) | ||
238 | local f = "results/" .. host .. "_" .. ip .. ".lua" | ||
239 | local rfile, e = io.open(f, "r") | ||
240 | if nil == rfile then I("opening " .. f .. " file - " .. e) else | ||
241 | rfile:close() | ||
242 | local rs = loadfile(f)() | ||
243 | for k, v in pairs(rs) do | ||
244 | if "table" == type(v) then | ||
245 | if "speed" == k then | ||
246 | if v.min < results.speed.min then results.speed.min = v.min end | ||
247 | if v.max > results.speed.max then results.speed.max = v.max end | ||
248 | else | ||
249 | for i, u in pairs(v) do | ||
250 | if "table" == type(u) then | ||
251 | for h, t in pairs(u) do | ||
252 | local a = results[k][i][h] | ||
253 | if nil == a then a = 0 end | ||
254 | results[k][i][h] = a + t | ||
255 | end | ||
256 | else | ||
257 | local a = results[k][i] | ||
258 | if nil == a then a = 0 end | ||
259 | results[k][i] = a + u | ||
260 | end | ||
261 | end | ||
262 | end | ||
263 | elseif "timeout" ~= k then | ||
264 | local a = results[k] | ||
265 | if nil == a then a = 0 end | ||
266 | results[k] = a + v | ||
267 | end | ||
268 | end | ||
269 | end | ||
270 | return results | ||
271 | end | 114 | end |
272 | 115 | ||
273 | local m = {} | 116 | local m = {} |
@@ -287,20 +130,20 @@ local logCount = function(domain, ip) | |||
287 | if nil ~= l:match("><b>WARNING ") then warnings = warnings + 1 end | 130 | if nil ~= l:match("><b>WARNING ") then warnings = warnings + 1 end |
288 | end | 131 | end |
289 | rfile:close() | 132 | rfile:close() |
290 | if html then | 133 | if APT.html then |
291 | if nil == ip then | 134 | if nil == ip then |
292 | log = "<a href='" .. nm .. "'>" .. domain .. "</a>" | 135 | log = "<a href='" .. nm .. "'>" .. domain .. "</a>" |
293 | else | 136 | else |
294 | log = "<a href='" .. nm .. "'>" .. ip .. "</a>" | 137 | log = "<a href='" .. nm .. "'>" .. ip .. "</a>" |
295 | end | 138 | end |
296 | end | 139 | end |
297 | log = log .. plurals(errors, warnings) | 140 | log = log .. APT.plurals(errors, warnings) |
298 | end | 141 | end |
299 | return log | 142 | return log |
300 | end | 143 | end |
301 | 144 | ||
302 | 145 | ||
303 | html = false | 146 | APT.html = false |
304 | local email, e = io.open("results/Report-email.txt", "w+") | 147 | local email, e = io.open("results/Report-email.txt", "w+") |
305 | if nil == email then C("opening mirrors file - " .. e) else | 148 | if nil == email then C("opening mirrors file - " .. e) else |
306 | email:write( "Dear Mirror Admins,\n\n" .. | 149 | email:write( "Dear Mirror Admins,\n\n" .. |
@@ -316,17 +159,17 @@ if nil == email then C("opening mirrors file - " .. e) else | |||
316 | "Please see below the current status of the Devuan Package Mirror \nnetwork:\n\n" .. | 159 | "Please see below the current status of the Devuan Package Mirror \nnetwork:\n\n" .. |
317 | "==== package mirror status " .. os.date("!%Y-%m-%d %H:%M") .. " GMT ====\n" .. | 160 | "==== package mirror status " .. os.date("!%Y-%m-%d %H:%M") .. " GMT ====\n" .. |
318 | "[skip] means that the test hasn't been written yet.\n\n") | 161 | "[skip] means that the test hasn't been written yet.\n\n") |
319 | for k, v in orderedPairs(mirrors) do | 162 | for k, v in APT.orderedPairs(APT.mirrors) do |
320 | local results = loadfile("results/" .. k .. ".lua")() | 163 | local results = loadfile("results/" .. k .. ".lua")() |
321 | email:write(k .. "....\n") | 164 | email:write(k .. "....\n") |
322 | local IPs = v.IPs | 165 | local IPs = v.IPs |
323 | for i, u in pairs(IPs) do | 166 | for i, u in pairs(IPs) do |
324 | if "table" == type(u) then | 167 | if "table" == type(u) then |
325 | for h, t in pairs(u) do | 168 | for h, t in pairs(u) do |
326 | results = collate(k, h, results) | 169 | results = APT.collate(k, h, results) |
327 | end | 170 | end |
328 | else | 171 | else |
329 | results = collate(k, i, results) | 172 | results = APT.collate(k, i, results) |
330 | end | 173 | end |
331 | end | 174 | end |
332 | local ftp = "[skip]" | 175 | local ftp = "[skip]" |
@@ -340,11 +183,11 @@ if nil == email then C("opening mirrors file - " .. e) else | |||
340 | local updated = status(k, results, "Updated") | 183 | local updated = status(k, results, "Updated") |
341 | 184 | ||
342 | -- DNS-RR test. | 185 | -- DNS-RR test. |
343 | if ("deb.devuan.org" ~= k) and (nil ~= mirrors["deb.devuan.org"]) then | 186 | if ("deb.devuan.org" ~= k) and (nil ~= APT.mirrors["deb.devuan.org"]) then |
344 | for l, w in pairs(mirrors[k].IPs) do | 187 | for l, w in pairs(APT.mirrors[k].IPs) do |
345 | if type(w) == "table" then | 188 | if type(w) == "table" then |
346 | for i, u in pairs(w) do | 189 | for i, u in pairs(w) do |
347 | if nil ~= mirrors["deb.devuan.org"].IPs["deb.roundr.devuan.org"][i] then | 190 | if nil ~= APT.mirrors["deb.devuan.org"].IPs["deb.roundr.devuan.org"][i] then |
348 | local log = logCount("deb.devuan.org", i) | 191 | local log = logCount("deb.devuan.org", i) |
349 | if "" ~= log then | 192 | if "" ~= log then |
350 | if "" == dns then dns = " " else dns = dns .. " " end | 193 | if "" == dns then dns = " " else dns = dns .. " " end |
@@ -356,7 +199,7 @@ if nil == email then C("opening mirrors file - " .. e) else | |||
356 | end | 199 | end |
357 | end | 200 | end |
358 | else | 201 | else |
359 | if nil ~= mirrors["deb.devuan.org"].IPs["deb.roundr.devuan.org"][l] then | 202 | if nil ~= APT.mirrors["deb.devuan.org"].IPs["deb.roundr.devuan.org"][l] then |
360 | local log = logCount("deb.devuan.org", l) | 203 | local log = logCount("deb.devuan.org", l) |
361 | if "" ~= log then | 204 | if "" ~= log then |
362 | if "" == dns then dns = " " else dns = dns .. " " end | 205 | if "" == dns then dns = " " else dns = dns .. " " end |
@@ -391,7 +234,7 @@ end | |||
391 | results = {} | 234 | results = {} |
392 | m = {} | 235 | m = {} |
393 | faulty = "" | 236 | faulty = "" |
394 | html = true | 237 | APT.html = true |
395 | local web, e = io.open("results/Report-web.html", "w+") | 238 | local web, e = io.open("results/Report-web.html", "w+") |
396 | if nil == web then C("opening mirrors file - " .. e) else | 239 | if nil == web then C("opening mirrors file - " .. e) else |
397 | web:write( "<html><head><title>apt-panopticon results</title>\n" .. | 240 | web:write( "<html><head><title>apt-panopticon results</title>\n" .. |
@@ -420,7 +263,7 @@ if nil == web then C("opening mirrors file - " .. e) else | |||
420 | "<table>\n<tr><th></th><th>FTP</th><th>HTTP</th><th>HTTPS</th><th>RSYNC</th><th>DNS round robin</th>" .. | 263 | "<table>\n<tr><th></th><th>FTP</th><th>HTTP</th><th>HTTPS</th><th>RSYNC</th><th>DNS round robin</th>" .. |
421 | "<th>Protocol</th><th>URL sanity</th><th>Integrity</th><th>Updated</th><th colspan='2'>Speed range</th></tr>\n" | 264 | "<th>Protocol</th><th>URL sanity</th><th>Integrity</th><th>Updated</th><th colspan='2'>Speed range</th></tr>\n" |
422 | ) | 265 | ) |
423 | for k, v in orderedPairs(mirrors) do | 266 | for k, v in APT.orderedPairs(APT.mirrors) do |
424 | local results = loadfile("results/" .. k .. ".lua")() | 267 | local results = loadfile("results/" .. k .. ".lua")() |
425 | local active = "" | 268 | local active = "" |
426 | if "yes" == v.Active then | 269 | if "yes" == v.Active then |
@@ -433,10 +276,10 @@ if nil == web then C("opening mirrors file - " .. e) else | |||
433 | for i, u in pairs(IPs) do | 276 | for i, u in pairs(IPs) do |
434 | if "table" == type(u) then | 277 | if "table" == type(u) then |
435 | for h, t in pairs(u) do | 278 | for h, t in pairs(u) do |
436 | results = collate(k, h, results) | 279 | results = APT.collate(k, h, results) |
437 | end | 280 | end |
438 | else | 281 | else |
439 | results = collate(k, i, results) | 282 | results = APT.collate(k, i, results) |
440 | end | 283 | end |
441 | end | 284 | end |
442 | local ftp = "[<font color='grey'><b>skip</b></font>]" | 285 | local ftp = "[<font color='grey'><b>skip</b></font>]" |
@@ -455,11 +298,11 @@ if nil == web then C("opening mirrors file - " .. e) else | |||
455 | local spd = '' | 298 | local spd = '' |
456 | 299 | ||
457 | -- DNS-RR test. | 300 | -- DNS-RR test. |
458 | if ("deb.devuan.org" ~= k) and (nil ~= mirrors["deb.devuan.org"]) then | 301 | if ("deb.devuan.org" ~= k) and (nil ~= APT.mirrors["deb.devuan.org"]) then |
459 | for l, w in pairs(mirrors[k].IPs) do | 302 | for l, w in pairs(APT.mirrors[k].IPs) do |
460 | if type(w) == "table" then | 303 | if type(w) == "table" then |
461 | for i, u in pairs(w) do | 304 | for i, u in pairs(w) do |
462 | if nil ~= mirrors["deb.devuan.org"].IPs["deb.roundr.devuan.org"][i] then | 305 | if nil ~= APT.mirrors["deb.devuan.org"].IPs["deb.roundr.devuan.org"][i] then |
463 | local log = logCount("deb.devuan.org", i) | 306 | local log = logCount("deb.devuan.org", i) |
464 | if "" ~= log then | 307 | if "" ~= log then |
465 | if "" == dns then dns = " " else dns = dns .. " " end | 308 | if "" == dns then dns = " " else dns = dns .. " " end |
@@ -471,7 +314,7 @@ if nil == web then C("opening mirrors file - " .. e) else | |||
471 | end | 314 | end |
472 | end | 315 | end |
473 | else | 316 | else |
474 | if nil ~= mirrors["deb.devuan.org"].IPs["deb.roundr.devuan.org"][l] then | 317 | if nil ~= APT.mirrors["deb.devuan.org"].IPs["deb.roundr.devuan.org"][l] then |
475 | local log = logCount("deb.devuan.org", l) | 318 | local log = logCount("deb.devuan.org", l) |
476 | if "" ~= log then | 319 | if "" ~= log then |
477 | if "" == dns then dns = " " else dns = dns .. " " end | 320 | if "" == dns then dns = " " else dns = dns .. " " end |
@@ -502,18 +345,18 @@ if nil == web then C("opening mirrors file - " .. e) else | |||
502 | web:write( "</table>\n<br>\n<h2>==== faulty mirrors: ====</h2>\n" .. faulty) | 345 | web:write( "</table>\n<br>\n<h2>==== faulty mirrors: ====</h2>\n" .. faulty) |
503 | web:write( "<br>\n<br>\n<h2>==== DNS and logs: ====</h2>\n") | 346 | web:write( "<br>\n<br>\n<h2>==== DNS and logs: ====</h2>\n") |
504 | 347 | ||
505 | for k, v in pairs(mirrors) do | 348 | for k, v in pairs(APT.mirrors) do |
506 | local log = k | 349 | local log = k |
507 | local n = {} | 350 | local n = {} |
508 | log = logCount(k) | 351 | log = logCount(k) |
509 | mirrors[k].Protocols = nil | 352 | APT.mirrors[k].Protocols = nil |
510 | mirrors[k].FQDN = nil | 353 | APT.mirrors[k].FQDN = nil |
511 | mirrors[k].Active = nil | 354 | APT.mirrors[k].Active = nil |
512 | mirrors[k].Rate = nil | 355 | APT.mirrors[k].Rate = nil |
513 | mirrors[k].BaseURL = nil | 356 | APT.mirrors[k].BaseURL = nil |
514 | mirrors[k].Country = nil | 357 | APT.mirrors[k].Country = nil |
515 | mirrors[k].Bandwidth = nil | 358 | APT.mirrors[k].Bandwidth = nil |
516 | for l, w in pairs(mirrors[k].IPs) do | 359 | for l, w in pairs(APT.mirrors[k].IPs) do |
517 | if type(w) == "table" then | 360 | if type(w) == "table" then |
518 | n[l] = {} | 361 | n[l] = {} |
519 | for i, u in pairs(w) do | 362 | for i, u in pairs(w) do |
@@ -535,11 +378,12 @@ if nil == web then C("opening mirrors file - " .. e) else | |||
535 | "pkgmaster.devuan.org is the master mirror, all the others sync to it. " .. | 378 | "pkgmaster.devuan.org is the master mirror, all the others sync to it. " .. |
536 | "</p>\n" | 379 | "</p>\n" |
537 | ) | 380 | ) |
538 | web:write(dumpTableHTML(m, "", "")) | ||
539 | web:write( "\n<br>\n<hr>\n\n" .. | 381 | web:write( "\n<br>\n<hr>\n\n" .. |
382 | web:write(APT.dumpTableHTML(m, "", "")) | ||
540 | "<p>The <a href='Report-email.txt'>email report</a>. " .. | 383 | "<p>The <a href='Report-email.txt'>email report</a>. " .. |
541 | "All <a href='../results'>the logs and other output</a>. " .. | 384 | "All <a href='../results'>the logs and other output</a>. " .. |
542 | "You can get the <a href='https://sledjhamr.org/cgit/apt-panopticon/about/'>source code here</a>.</p>" .. | 385 | "You can get the <a href='https://sledjhamr.org/cgit/apt-panopticon/about/'>source code here</a>.</p>" .. |
543 | "</body></html>\n") | 386 | "</body></html>\n") |
544 | web:close() | 387 | web:close() |
545 | end | 388 | end |
389 | |||