From 612b0e754176f1b7098c5c1d7973028a22ee902d Mon Sep 17 00:00:00 2001 From: Mika Havela Date: Tue, 15 Apr 2008 12:52:00 +0000 Subject: Creating a acf-gnats so that users can view/add bugreports to bugs@alpinelinux.org git-svn-id: svn://svn.alpinelinux.org/acf/gnats/trunk@985 ab2d0c66-481e-0410-8bed-d214d4d58bed --- Makefile | 54 +++++ README | 0 config.mk | 10 + gnats-controller.lua | 515 +++++++++++++++++++++++++++++++++++++++++++ gnats-edit-html.lsp | 58 +++++ gnats-expert-html.lsp | 52 +++++ gnats-model.lua | 498 +++++++++++++++++++++++++++++++++++++++++ gnats-query-html.lsp | 68 ++++++ gnats-queryresult-html.lsp | 120 ++++++++++ gnats-report-html.lsp | 77 +++++++ gnats-reportsuccess-html.lsp | 2 + gnats-status-html.lsp | 29 +++ gnats-summary-html.lsp | 81 +++++++ gnats.menu | 8 + 14 files changed, 1572 insertions(+) create mode 100644 Makefile create mode 100644 README create mode 100644 config.mk create mode 100644 gnats-controller.lua create mode 100644 gnats-edit-html.lsp create mode 100644 gnats-expert-html.lsp create mode 100644 gnats-model.lua create mode 100644 gnats-query-html.lsp create mode 100644 gnats-queryresult-html.lsp create mode 100644 gnats-report-html.lsp create mode 100644 gnats-reportsuccess-html.lsp create mode 100644 gnats-status-html.lsp create mode 100644 gnats-summary-html.lsp create mode 100644 gnats.menu diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6a0959d --- /dev/null +++ b/Makefile @@ -0,0 +1,54 @@ +APP_NAME=gnats +PACKAGE=acf-$(APP_NAME) +VERSION=0.1.0 + +APP_DIST=\ + gnats-controller.lua \ + gnats-edit-html.lsp \ + gnats-expert-html.lsp \ + gnats-model.lua \ + gnats-query-html.lsp \ + gnats-queryresult-html.lsp \ + gnats-report-html.lsp \ + gnats-reportsuccess-html.lsp \ + gnats-status-html.lsp \ + gnats-summary-html.lsp \ + gnats.menu \ + + +EXTRA_DIST=README Makefile config.mk + +DISTFILES=$(APP_DIST) $(EXTRA_DIST) + +TAR=tar + +P=$(PACKAGE)-$(VERSION) +tarball=$(P).tar.bz2 +install_dir=$(DESTDIR)/$(appdir)/$(APP_NAME) + +all: +clean: + rm -rf $(tarball) $(P) + +dist: $(tarball) + +install: + mkdir -p "$(install_dir)" + cp -a $(APP_DIST) "$(install_dir)" + +$(tarball): $(DISTFILES) + rm -rf $(P) + mkdir -p $(P) + cp $(DISTFILES) $(P) + $(TAR) -jcf $@ $(P) + rm -rf $(P) + +# target that creates a tar package, unpacks is and install from package +dist-install: $(tarball) + $(TAR) -jxf $(tarball) + $(MAKE) -C $(P) install DESTDIR=$(DESTDIR) + rm -rf $(P) + +include config.mk + +.PHONY: all clean dist install dist-install diff --git a/README b/README new file mode 100644 index 0000000..e69de29 diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..45f4d21 --- /dev/null +++ b/config.mk @@ -0,0 +1,10 @@ +prefix=/usr +datadir=${prefix}/share +sysconfdir=${prefix}/etc +localstatedir=${prefix}/var +acfdir=${datadir}/acf +wwwdir=${acfdir}/www +cgibindir=${acfdir}/cgi-bin +appdir=${acfdir}/app +acflibdir=${acfdir}/lib +sessionsdir=${localstatedir}/lib/acf/sessions diff --git a/gnats-controller.lua b/gnats-controller.lua new file mode 100644 index 0000000..4fa036c --- /dev/null +++ b/gnats-controller.lua @@ -0,0 +1,515 @@ +module(..., package.seeall) + +-- Load libraries +require("format") + +-- Set variables +local newrecordtxt = "[New]" +local anytxt = "Any" +local bugreportreceiver = "bugs@alpinelinux.org" + +local descr = { + labels = { + ['category'] = "Category", + ['severity'] = "Severity", + ['priority'] = "Priority", + ['class'] = "Class", + ['state'] = "State", + ['skipclosed'] = "Skip/Hide closed", + ['text'] = "Text in single-line fields", + ['multitext'] = "Text in multi-line fields", + ['responsible'] = "Responsible", + ['originator'] = "Originator", + ['release'] = "Alpine release", + ['environment']="Environment", + ['description']="Description", + ['howtorepeat']="How-to-repeat this bug", + ['fix']="Fix", + ['synopsis']="Short description (on line)", + ['from']="Your e-mail address", + ['reporter_name']="Your full name", + ['reporter_id']="Your ID (Submitter ID)", + ['reporter_org']="Your organization name", + + header = { + ['received'] = "Received", + ['to'] = "To", + ['from'] = "From", + ['date'] = "Date", + ['message_id'] = "Message ID", + ['subject'] = "Subject", + ['return_path'] = "Return path", + }, + sfields = { + ['number'] = "Number", + ['class'] = "Class", + ['responsible'] = "Responsible", + ['category'] = "Category", + ['priority'] = "Priority", + ['confidential'] = "Confidential", + ['originator'] = "Originator", + ['severity'] = "Severity", + ['keywords'] = "Keywords", + ['arrival_date'] = "Arrival date", + ['quarter'] = "Quarter", + ['synopsis'] = "Synopsis", + ['notify_list'] = "Notify list", + ['submitter_id'] = "Submitter id", + ['date_required'] = "Date required", + ['release'] = "Release", + ['last_modified'] = "Last modified", + ['closed_date'] = "Closed date", + ['state'] = "State", + }, + mfields = { + ['environment'] = "Environment", + ['release_note'] = "Release note", + ['audit_trail'] = "Audit trail", + ['howtorepeat'] = "How to repeat", + ['organization'] = "Organization", + ['fix'] = "Fix", + ['unformatted'] = "Unformatted", + }, + }, + audit_trail = { + ['Responsible-Changed-From-To'] = "Responsible Changed", + ['State-Changed-From-To'] = "State Changed", + ['From'] = "Reply via E-mail", + }, + maildescriptions = { + ['from'] = "From: ", + ['reporter_id'] = ">Submitter-Id: ", + ['xxx'] = ">Notify-List: ", + ['originator'] = ">Originator: ", + ['reporter_org'] = ">Organization: ", + ['synopsis'] = ">Synopsis: ", + ['xxx'] = ">Confidential: ", + ['severity'] = ">Severity: ", + ['priority'] = ">Priority: ", + ['category'] = ">Category: ", + ['class'] = ">Class: ", + ['release'] = ">Release: ", + ['environment'] = ">Environment: ", + ['description'] = ">Description: ", + ['howtorepeat'] = ">How-To-Repeat:", + ['fix'] = ">Fix: ", + }, + + } + +-- ################################################################################ +-- LOCAL FUNCTIONS + +local function list_redir(self) + self.conf.action = "status" + self.conf.type = "redir" + error (self.conf) +end + + +-- ################################################################################ +-- PUBLIC FUNCTIONS +mvc = {} +function mvc.on_load(self, parent) + if (self.worker[self.conf.action] == nil ) or ( self.conf.action == "init" ) then + self.worker[self.conf.action] = list_redir(self) + end +end + +function status(self) + return { status=self.model.getstatus() } +end + +function expert(self) + local modifications = self.clientdata.filecontent or "" + if ( self.clientdata.cmdsave ) then + modifications = self.model:update_filecontent(modifications) + end + local url = ENV["SCRIPT_NAME"] .. self.conf.prefix .. self.conf.controller + + local status=self.model.getstatus() + local filedetailsresult, file = self.model:get_filedetails() + + -- Add buttons + file.cmdsave = cfe ({ + name="cmdsave", + label="Apply settings", + value="Apply", + type="submit", + }) + if (self.clientdata.cmdsave) then + file.cmdsave.descr="* Changes has been saved!" + end + + return ( { + option={ script=ENV["SCRIPT_NAME"], + prefix=self.conf.prefix, + controller = self.conf.controller, + action = "update", + extra = "" }, + status = status, + file = file, + modifications = modifications, + url = url, } ) +end + +function edit(self) + + -- Save changes + local modifications = self.clientdata.filecontent or "" + if ( self.clientdata.cmdsave ) then + modifications = self.model:update_filecontent(modifications,self.clientdata.name) + end + + local status = self.model:getstatus(self) + local filedetailsresult, file = self.model:get_filedetails(self.clientdata.name) + + + if not (filedetailsresult) then + self.conf.action = "status" + self.conf.type = "redir" + error (self.conf) + end + + -- Add a cmd button to the view + file.cmdsave = cfe({ name="cmdsave", + label="Save/Apply above settings", + value="Save", + type="submit", + }) + if (modifications) then + file.cmdsave.descr="* Changes has been saved!" + end + + return { + option={ script=ENV["SCRIPT_NAME"], + prefix=self.conf.prefix, + controller = self.conf.controller, + action = "edit", + extra = "", + }, + modifications = modifications, + file = file, + status = status, + startstop = startstop, + } +end + +function query(self) + local query = {} + + query.pr = cfe({ + name="pr", + label="Query a specific PR number:", + }) + query.pr_cmd = cfe({ + name="pr_cmd", + label="Submit the query", + type="submit", + value="Submit", + }) + + local tags = { "category", "severity", "priority", "class", "state", } + for k,val in pairs(tags) do + query[val] = cfe({ + name=val, + label=(descr.labels[val] or val), + value=anytxt, + type="select", + option=self.model:get_select_opt(val), + }) + table.insert(query[val].option, 1, anytxt) + -- If there is only a limited amount records, then present as radio buttons. + if (#query[val].option < 6) then query[val].type="radio" end + end + + local val = "responsible" + query[val] = cfe({ + name=val, + label=(descr.labels[val] or val), + value=anytxt, + type="select", + option=self.model:list_responsible(), + }) + table.insert(query[val].option, 1, anytxt) + + local tags = { "skipclosed", "text", "multitext", } + for k,val in pairs(tags) do + query[val] = cfe({ + name=val, + label=(descr.labels[val] or val), + option=self.model:get_select_opt(val), + }) + end + + -- Show 'skipclosed' as a checkbox instead + query.skipclosed.type="checkbox" + query.skipclosed.checked="Yes" + + query.advanced_cmd = cfe({ + name="advanced_cmd", + label="Submit the advanced query", + type="submit", + value="Submit", + }) + + return { + option={ script=ENV["SCRIPT_NAME"], + prefix=self.conf.prefix, + controller = self.conf.controller, + action = "edit", + extra = "", + }, + query = query, + } +end + +function queryresult(self) + local query = {} + local pr_id = self.clientdata.pr + if (tonumber(pr_id)) then + query.header, query.sfields, query.mfields = self.model:read_pr(pr_id) + else + list_redir(self) + end + + local myform = query.header + for k,v in pairs(myform) do + myform[k] = cfe({ + name=k, + label=descr.labels.header[k] or k, + value=string.gsub(myform[k], "\"", ""), + }) + end + + local myform = query.sfields + for k,v in pairs(myform) do + myform[k] = cfe({ + name=k, + label=descr.labels.sfields[k] or k, + value=string.gsub(myform[k], "\"", ""), + }) + end + + local myform = query.mfields + for k,v in pairs(myform) do + myform[k] = cfe({ + name=k, + label=descr.labels.mfields[k] or k, + value=string.gsub(myform[k], "\"", ""), + }) + end + + local audit_trail_table = {} + local checkstring = "" + local checkstring2 = 0 + table.insert(audit_trail_table, cfe({class="", label=""})) + + for k,v in pairs(format.string_to_table(query.mfields.audit_trail.value, "\n")) do + if (v == "") then + checkstring2 = checkstring2 + 1 + end + if (checkstring2 == 99) then + table.insert(audit_trail_table, cfe({class="Unknown", label=""})) + checkstring = "" + checkstring2 = 0 + end + for k1, v1 in pairs(descr.audit_trail) do + if (string.match(v, "^".. string.gsub(k1, "-", "%%-"))) then + table.insert(audit_trail_table, cfe({class=k1, label=v1})) + checkstring = k1 + checkstring2 = 0 + break + end + end + if (#audit_trail_table > 0) then + table.insert(audit_trail_table[#audit_trail_table], string.match(v, "^.+")) --Strips empty lines + end + end + + query.mfields.audit_trail.option=audit_trail_table + + return { + option={ script=ENV["SCRIPT_NAME"], + prefix=self.conf.prefix, + controller = self.conf.controller, + action = "edit", + extra = "", + }, + query = query, + } + +end + +function summary(self) + -- parse the search criterias - select boxes + local action = "Query" + local s = {} + for i, f in ipairs({"class", "category", "severity", "priority", "state", "responsible"}) do + s[f] = self.model:validate_field(self.clientdata[f], f) + end + + local invalid = nil + -- parse the search criterias - input boxes + for i,f in ipairs({"text", "multitext", "originator"}) do + s[f] = self.model:validate_textfield(self.clientdata[f], f) + -- replace this with ajax stuff? + if action == "Query" and s[f] == nil then + self.model:set_invalid_field(f) + invalid = true + end + -- if field is blank, then ignore it + if action == "Query" and s[f] == "" then + s[f] = nil + end + end + + -- we have found atleast one invalid field. reset the action + if invalid then + action = nil + end + + if (self.clientdata.skipclosed) then + s["skip-closed"] = "" + else + s["skip-closed"] = nil + end + + -- Set the search criterias + self.model:set_search_criteria(s) + + return { + option={ script=ENV["SCRIPT_NAME"], + prefix=self.conf.prefix, + controller = self.conf.controller, + action = "edit", + extra = "", + }, + summary = self.model:summary(), + } +end + +function report(self) + local reportform = {} + local status = self.model:getstatus(self) + + local alltags = { "reporter_name", "from", "reporter_id", "reporter_org", + "synopsis","severity","priority","category","class","release","environment", + "description", "howtorepeat", "fix", } + + + for k,val in pairs(alltags) do + reportform[val] = cfe({ + name=val, + label=(descr.labels[val] or val), + value=(self.clientdata[val] or ""), + }) + end + + local tags = { "category", "priority","severity","class" } + for k,val in pairs(tags) do + reportform[val] = cfe({ + name=val, + label=(descr.labels[val] or val), + value=(self.clientdata[val] or ""), + type="select", + option=self.model:get_select_opt(val), + }) + -- If there is only a limited amount records, then present as radio buttons. + if (#reportform[val].option < 6) then + reportform[val].type="radio" + else + table.insert(reportform[val].option, 1, "") + end + end + + reportform.description.type="longtext" + reportform.howtorepeat.type="longtext" + reportform.fix.type="longtext" + reportform.environment.type="longtext" + reportform.release.value=status.alpinerelease.value or "" + + -- Add buttons + reportform.submit = cfe ({ + name="submit", + label="Submit this repport", + value="Submit", + type="submit", + }) + + if (self.clientdata.submit) then + + if not (self.clientdata.from) or (#self.clientdata.from == 0) then + reportform.from.errtxt = "You need to enter a valid e-mail address" + end + + if not (self.clientdata.synopsis) or (#self.clientdata.synopsis == 0) then + reportform.synopsis.errtxt = "You need to enter a valid online-description" + end + + if not (self.clientdata.severity) or (#self.clientdata.severity == 0) then + reportform.severity.errtxt = "You need to choose one of these options" + end + + if not (self.clientdata.priority) or (#self.clientdata.priority == 0) then + reportform.priority.errtxt = "You need to choose one of these options" + end + + if not (self.clientdata.category) or (#self.clientdata.category == 0) then + reportform.category.errtxt = "You need to choose one of these options" + end + + if not (self.clientdata.class) or (#self.clientdata.class == 0) then + reportform.class.errtxt = "You need to choose one of these options" + end + + local noerrorsexists + for k,v in pairs(alltags) do + noerrorsexists = false + if (#reportform[v]['errtxt'] > 0) then break end + noerrorsexists = true + end + + -- If we had no errors, then we proceed with creating/sending the bugreport + if (noerrorsexists) then + local bugreport = {} + table.insert(bugreport, "To: " .. (bugreportreceiver or "")) + table.insert(bugreport, "From: " .. (self.clientdata.from or "")) + table.insert(bugreport, "Reply-To: " .. (self.clientdata.from or "")) + table.insert(bugreport, "") + for k,v in pairs({"reporter_id", "notify_list", "originator", "synopsis", "confidential", "severity", + "priority", "category", "class", "release", }) do + if (v) and (descr.maildescriptions[v]) and (self.clientdata[v]) then + table.insert(bugreport, descr.maildescriptions[v] .. self.clientdata[v] ) + end + end + table.insert(bugreport, descr.maildescriptions.environment .. "\n" .. (self.clientdata.environment or "") .."\n" ) + table.insert(bugreport, descr.maildescriptions.howtorepeat .. "\n" .. (self.clientdata.howtorepeat or "") .."\n" ) + + table.insert(bugreport, descr.maildescriptions.fix .. "\n" .. (self.clientdata.fix or "") .."\n" ) + + local bugreportsuccess, bugreport = self.model:sendbug(bugreport) + + if (bugreportsuccess) then + self.conf.action = "reportsuccess" + self.conf.type = "redir" + error (self.conf) + end + reportform.submit.errtxt = "Something went wrong because the report could not be sent.\n" .. bugreport + end + end + + return { + option={ script=ENV["SCRIPT_NAME"], + prefix=self.conf.prefix, + controller = self.conf.controller, + action = "edit", + extra = "", + }, + reportform = reportform, + } +end + +function reportsuccess() + -- Just show that the report was successfully sent. +end diff --git a/gnats-edit-html.lsp b/gnats-edit-html.lsp new file mode 100644 index 0000000..08dee18 --- /dev/null +++ b/gnats-edit-html.lsp @@ -0,0 +1,58 @@ + +DEBUGGING

DEBUG INFO: CFE

") +io.write(html.cfe_unpack(form)) +io.write("
") +--]] +?> + +

SYSTEM INFO

+
+ +
+ +
+

CONFIGURATION

+

Expert config

+

File details

+
+ +
+ +

FILE CONTENT

+') +io.write(html.form[myform.filecontent.type](myform.filecontent)) +?> + +

SAVE AND APPLY ABOVE SETTINGS

+
+ +
+ +MANAGEMENT\n
") + displaymanagement(myform,tags) + io.write("
") +end +?> +
diff --git a/gnats-expert-html.lsp b/gnats-expert-html.lsp new file mode 100644 index 0000000..da4ac15 --- /dev/null +++ b/gnats-expert-html.lsp @@ -0,0 +1,52 @@ + +DEBUGGING

DEBUG INFO: CFE

") +io.write(html.cfe_unpack(form)) +io.write("
") +--]] +?> + + +

SYSTEM INFO

+
+ +
+ +

CONFIGURATION

+

Expert config

+

List of files

+
+ + + + + + +
+
+ + +
" method="POST"> +MANAGEMENT\n
") + displaymanagement(myform,tags) + io.write("
") +end +?> +
diff --git a/gnats-model.lua b/gnats-model.lua new file mode 100644 index 0000000..28b9d9e --- /dev/null +++ b/gnats-model.lua @@ -0,0 +1,498 @@ +module(..., package.seeall) + +-- Load libraries +require("fs") +require("procps") +require("date") +require("getopts") +require("format") +require("posix") +require("daemoncontrol") +require("validator") +require("socket") +require("processinfo") + +-- Set variables +local configfile = "" +local processname = "gnats" +local packagename = "gnats" +local baseurl = "/etc/gnats" --No trailing / +local gnatsopts = " -H dev.alpinelinux.org " +-- constants +local SECT_HEADER = 1 +local SECT_SFIELDS = 2 +local SECT_MFIELDS = 3 +local header = {} +local sfields = {} +local mfields = {} +local fields_single = { + ["number"] = "", ["category"] = "", ["synopsis"] = "", + ["confidential"] = "", ["severity"] = "", ["priority"] = "", + ["responsible"] = "", ["state"] = "", ["quarter"] = "", + ["keywords"] = "", ["date_required"] = "", ["class"] = "", + ["submitter_id"] = "", ["arrival_date"] = "", ["closed_date"] = "", + ["last_modified"] = "", ["originator"] = "", ["release"] = "", + ["notify_list"] = "" +} + +local fields_multiple = { + ["organization"]="", ["environment"]="", ["description"]="", + ["how_to_repeat"]="", ["fix"]="", ["release_note"]="", + ["audit_trail"]="", ["unformatted"]="" } + +local descr = { +} + +-- ################################################################################ +-- LOCAL FUNCTIONS + +-- Function to recursively inserts all filenames in a dir into an array +local function recursedir(path, filearray) + local k,v + for k,v in pairs(posix.dir(path) or {}) do + -- Ignore files that begins with a '.' + if not string.match(v, "^%.") then + local f = path .. "/" .. v + -- If subfolder exists, list files in this subfolder + if (posix.stat(f).type == "directory") then + recursedir(f, filearray) + else + table.insert(filearray, f) + end + end + end +end + +-- ################################################################################ +-- LOCAL GNATS SPECIFIC FUNCTIONS + +local function process_header(line) + local k, v + if string.find(line, "^%s*$") then + return 1 + end + + k, v = string.match(line, "^([%a%d_.-]+): (.*)$") + if k then + header[string.lower(k:gsub("-", "_"))] = v + end + return 0 +end + +local function process_sfields(line) + local k, v, i, j, key + local found = 0 + key, v = string.match(line, "^>([%a-]+):%s*(.*)$") + if key then + k = string.lower(key:gsub("-", "_")) + if fields_single[k] == nil then + return 1 + end + fields_single[k] = v + end + return 0 +end + +local current_mfield = 0 + +local function mfield_add_line(line) + if fields_multiple[current_mfield] == nil then + fields_multiple[current_mfield] = line or "" + else + fields_multiple[current_mfield] = fields_multiple[current_mfield].."\n"..(line or "") + end +end + +local function process_mfields(line) + local k, v = string.match(line, "^>([%a-]+):%s*(.*)$") + if k then + k = string.lower(k:gsub("-", "_")) + if fields_multiple[k] then + current_mfield = k + end + else + v = line + end + mfield_add_line(v) + return 0 +end + +local function get_array(name) + local a = {} + local f = assert(io.popen("/usr/bin/query-pr "..gnatsopts.." --valid-values " .. tostring(name))) + for line in f:lines() do + table.insert(a, line) + end + f:close() + return a +end + +local function get_various_info() + local f = assert(io.popen("/usr/bin/query-pr "..gnatsopts.." -x -q | wc -l")) + local count = f:read("*l") + f:close() + return count +end + +-- ################################################################################ +-- GNATS SPECIFIC FUNCTIONS + + +-- generate an array of select options +function get_select_opt(self, name) + return get_array(name) +end + +-- generate an array of select options +function list_responsible() + local a = {} + local f = assert(io.popen("/usr/bin/query-pr "..gnatsopts.." --list-responsible ")) + for line in f:lines() do + table.insert(a, string.match(line,"^(.-):.*$")) + end + f:close() + return a +end + +function summary() + -- TODO: this func should return an iterator instead of a table + local line + local pr = {} + local prs = {} + local i,k,v + local search_opts = "" + + -- create command line options for quer-pr. For example: + -- + -- --priority low --class sw-bug --category doc --skip-closed + -- + -- Note that --skip-closed has value = "" + -- + for k,v in pairs(search_criteria) do + if v then + search_opts = search_opts.." --"..k.." "..v + end + end + + local f = assert(io.popen("query-pr "..gnatsopts.. (search_opts or ""))) + + i = 0 + for line in f:lines() do + -- if line is empty, insert to array and reset record + if line == "" then + prs[i] = pr + pr = {} + end + -- extract the key/value: + -- >Number: 24 + -- >Category: pending + -- >Synopsis: bug report + -- >Confidential: no + -- >Severity: serious + -- >Priority: medium + -- >Responsible: gnats-admin + -- >State: open + -- >Class: sw-bug + -- >Submitter-Id: unknown + -- >Arrival-Date: Sat Apr 07 14:45:02 +0000 2007 + -- >Originator: Nathan Angelacos + -- >Release: + -- + -- >Number: 25 + -- ... + k,v = line:match(">(.-):%s+(.*)") + if k then + -- convert to lowercase and replace '-' with '_' + -- + -- Submitter-Id -> submitter_id + local key = string.lower(k:gsub("-", "_")) + if key == "number" then + i = tonumber(v) + end + if key == "arrival_date" then + local datetable = date.string_to_table(v) + if (tonumber(datetable.month) < 10) then datetable.month = "0" .. tostring(datetable.month) end +-- if (tonumber(datetable.day) < 10) then datetable.day = "0" .. tostring(datetable.day) end + pr['submit_date'] = string.sub(datetable.year,3) .. "/" .. datetable.month .. "/" .. datetable.day + pr['submit_date_in_seconds'] = os.time(datetable) + end + + pr[ key ] = v + end + end + + local prs_sorted = {} + for k,v in pairs(prs) do + table.insert(prs_sorted, v) + end + table.sort(prs_sorted, function(a,b) return (a.submit_date_in_seconds > b.submit_date_in_seconds) end) + + return prs_sorted +end + +-- validate if "value" is a valid field +function validate_field(self, value, field) + -- check if for or field is nil + if field == nil or value == nil then + return nil + end + + -- loop through the array + for k,v in pairs(get_array(field)) do + if value == v then + return value + end + end + return nil +end + +-- validate for insecure chars in field +function validate_textfield(self,value) + if value == nil or value == "" then + return "" + end + return string.match(value, "^[-_%a%d@.]*$") +end + +-- just set the private variable +function set_search_criteria(self, criteria) + search_criteria = criteria +end +-- append an n valid input message +function set_invalid_field(self,field) + table.insert(invalid_input, field) +end + +--[==[ +-- return a table of invald input fields +function get_invalid_inputs() + return invalid_input +end +--]==] + +--[=[ +-- dump the pr +function write_pr(id) + local cmd = "query-pr -F "..gnatsopts.." "..tostring(id) + local f = assert(io.popen(cmd)) + io.write(f:read("*all")) + f:close() +end +--]=] +-- read pr to header, sfields and mfields +function read_pr(self, id) + local cmd = "query-pr -F "..gnatsopts.." "..tostring(id) + local f = assert(io.popen(cmd)) + local line + local section = SECT_HEADER + + for line in f:lines() do + -- read header + if section == SECT_HEADER then + section = section + process_header(line) + end + if section == SECT_SFIELDS then + section = section + process_sfields(line) + end + if section == SECT_MFIELDS then + section = section + process_mfields(line) + end + end + return header, fields_single, fields_multiple +end + +-- ################################################################################ +-- PUBLIC FUNCTIONS + +function startstop_service ( self, action ) + local cmd = action.value + local cmdresult,cmdmessage,cmderror,cmdaction = daemoncontrol.daemoncontrol(processname, cmd) + action.descr=cmdmessage + action.errtxt=cmderror + return cmdresult,action +end + +function valid_filename(self, path) + local files = {} + recursedir(baseurl,files) + for k,v in pairs(files) do + if (v == path) then + return true + end + end + return false, "Not a valid filename!" +end + +function getstatus() + local status = {} + local config = getconfig() + local value, errtxt = processinfo.package_version(packagename) + status.version = cfe({ name = "version", + label="Program version", + value=value, + errtxt=errtxt, + }) + + local files = {} + recursedir(baseurl,files) + status.configfiles = cfe({ name="configfiles", + label="Config files", + type="select", + descr=table.concat(files,"\n"), + option=files, + }) + status.configfiles.size = #status.configfiles.option + + status.numbugs = cfe({ name="numbugs", + label="Still not closed reports", + value=get_various_info(), + }) + + local alpinerelease = fs.read_file_as_array("/etc/alpine-release") or {""} + status.alpinerelease = cfe({ name="alpinerelease", + label="Alpine release", + value=alpinerelease[1], + }) + + status.debug = cfe({ name="debug", + label="DEBUG output", + value=get_various_info(), + }) + + + return status +end + +function get_logfile () + local file = {} + local cmdtxt = "cat /var/log/messages | grep " .. processname + local cmd, error = io.popen(cmdtxt ,r) + local cmdoutput = cmd:read("*a") + cmd:close() + + file["filename"] = cfe({ + name="filename", + label="File name", + value=cmdtxt, + }) + + file["filecontent"] = cfe({ + type="longtext", + name="filecontent", + label="File content", + value=cmdoutput, + }) + + return file +end + +function getconfig() + if (fs.is_file(configfile)) then + return getopts.getoptsfromfile(configfile) + end + return false +end + +function get_filedetails(self,path) + if not (valid_filename(self,path)) then + return false, {['errtxt'] = "Not a valid filename!"} + end + local file = {} + local filedetails = {} + local config = {} + local filenameerrtxt + if (path) and (fs.is_file(path)) then + filedetails = fs.stat(path) + config = getconfig(path) + else + config = {} + config.filename = {} + config["filename"]["errtxt"]="Config file '".. path .. "' is missing!" + end + + file["filename" .. (num or "")] = cfe({ + name="filename" .. (num or ""), + label="File name", + value=path, + errtxt=filenameerrtxt + }) + file["filesize" .. (num or "")] = cfe({ + name="filesize" .. (num or ""), + label="File size", + value=filedetails.size or 0, + }) + file["mtime" .. (num or "")] = cfe({ + name="mtime" .. (num or ""), + label="File date", + value=filedetails.mtime or "---", + }) + file["filecontent" .. (num or "")] = cfe({ + type="longtext", + name="filecontent" .. (num or ""), + label="File content", + value=fs.read_file(path), + }) + + -- Sum all errors into one cfe + local sumerrors = "" + for k,v in pairs(config or {}) do + if (config[k]) and (config[k]["errtxt"]) and (config[k]["errtxt"] ~= "") then + sumerrors = sumerrors .. config[k]["errtxt"] .. "\n" + end + end + if (sumerrors ~= "") then + file["sumerrors" .. (num or "")] = cfe ({ + name="sumerrors" .. (num or ""), + label = "Configuration errors", + errtxt = string.match(sumerrors, "(.-)\n$"), + }) + end + + return true, file +end +function update_filecontent (self, modifications,path) + if not (valid_filename(self,path)) then + return false, {['errtxt'] = "Not a valid filename!"} + end + local file_result,err = fs.write_file(path, format.dostounix(modifications)) + if (err) and (#err > 0) then + return false, err + end + return true, file_result +end + +function sendbug (self, message) + if not (message) then + return false, "No valid message to send" + end + + if not (string.match(string.lower(tostring(message[1])), "^to:")) or + not (string.match(string.lower(tostring(message[2])), "^from:")) then + return false, "Message is malformatted." + end + + local cmdtxt = "/usr/bin/which sendmail" + local cmd, error = io.popen(cmdtxt ,r) + local cmdoutput = cmd:read("*a") + cmd:close() + if not (cmdoutput) then + return false, "Sendmail is not installed!" + end + + local mailtxt = "outgoing_mail" + fs.write_file(mailtxt, table.concat(message , "\n")) + + local cmdtxt = "/usr/sbin/sendmail -oi -t < " .. mailtxt .. " 2>&1" + local cmd, error = io.popen(cmdtxt ,r) + local cmdoutput = cmd:read("*a") + cmd:close() + + local cmd, error = io.popen("/bin/rm -f " .. mailtxt ,r) + cmd:close() + + if (#cmdoutput > 0) then + return false, cmdoutput + end + return true +end + diff --git a/gnats-query-html.lsp b/gnats-query-html.lsp new file mode 100644 index 0000000..5aeace6 --- /dev/null +++ b/gnats-query-html.lsp @@ -0,0 +1,68 @@ + +DEBUGGING

DEBUG INFO: CFE

") +io.write(html.cfe_unpack(form)) +io.write("
") +--]] +?> + + 0) then + val.class = "error" + io.write(" class='error'") + end + io.write(">" .. val.label .. "") + io.write("\n\t\t
") + if (viewtype == "viewonly") then + if (val.value == "") then val.value = " " end + io.write(val.value) + elseif (val.type == "radio") and (type(val.option) == "table") and (#val.option > 0) then + io.write("") + for k1,v1 in pairs(val.option) do + io.write("\n\t\t\t"..tostring(v1) .. ":") + io.write("") + end + io.write("\n\t\t\t") + else + io.write(html.form[val.type](val)) + end + if (val.descr) and (#val.descr > 0) then io.write("\n\t\t

" .. string.gsub(val.descr, "\n", "
") .. "

") end + if (#val.errtxt > 0) then io.write("\n\t\t

" .. string.gsub(val.errtxt, "\n", "
") .. "

") end + io.write("\n\t\t
\n") + end + end +end +?> + +

Query Alpine problem reports

+ +
+

Basic query

+
+ +
+
+ +
+

Advanced query

+
+ +
+
+ diff --git a/gnats-queryresult-html.lsp b/gnats-queryresult-html.lsp new file mode 100644 index 0000000..c47b919 --- /dev/null +++ b/gnats-queryresult-html.lsp @@ -0,0 +1,120 @@ + +DEBUGGING

DEBUG INFO: CFE

") +io.write(html.cfe_unpack(form.query)) +io.write("
") +--]] +?> + + 0) then + val.class = "error" + io.write(" class='error'") + end + io.write(">" .. val.label .. "") + io.write("\n\t\t
") + if (viewtype == "viewonly") then + if not (val.value) or (val.value == "") then val.value = " " end + io.write(val.value) + elseif (val.type == "radio") and (type(val.option) == "table") and (#val.option > 0) then + io.write("") + for k1,v1 in pairs(val.option) do + io.write("\n\t\t\t"..tostring(v1) .. ":") + io.write("") + end + io.write("\n\t\t\t") + else + io.write(html.form[val.type](val)) + end + if (val.descr) and (#val.descr > 0) then io.write("\n\t\t

" .. string.gsub(val.descr, "\n", "
") .. "

") end + if (#val.errtxt > 0) then io.write("\n\t\t

" .. string.gsub(val.errtxt, "\n", "
") .. "

") end + io.write("\n\t\t
\n") + end + end +end +?> + +

+ +

Header

+
+ +
+ +

Details

+
+ +
+ + +

+
+
+
+ + + + + + +

+
+") + if (#v.label > 0) then + io.write("" .. (v.label or "Uknown") .. "") + end + for i=1,#v do + io.write("
" .. (v[i] or "Uknown") .. "
") + end + io.write("\n") +end +?> +
+ + +

+
+
+
+ + + diff --git a/gnats-report-html.lsp b/gnats-report-html.lsp new file mode 100644 index 0000000..b438fa7 --- /dev/null +++ b/gnats-report-html.lsp @@ -0,0 +1,77 @@ + +DEBUGGING

DEBUG INFO: CFE

") +io.write(html.cfe_unpack(form)) +io.write("
") +--]] +?> + + 0) then + val.class = "error" + io.write(" class='error'") + end + io.write(">" .. val.label .. "") + io.write("\n\t\t
") + if (viewtype == "viewonly") then + if (val.value == "") then val.value = " " end + io.write(val.value) + elseif (val.type == "radio") and (type(val.option) == "table") and (#val.option > 0) then + io.write("") + for k1,v1 in pairs(val.option) do + io.write("\n\t\t\t"..tostring(v1) .. ":") + io.write("") + end + io.write("\n\t\t\t") + else + io.write(html.form[val.type](val)) + end + if (val.descr) and (#val.descr > 0) then io.write("\n\t\t

" .. string.gsub(val.descr, "\n", "
") .. "

") end + if (#val.errtxt > 0) then io.write("\n\t\t

" .. string.gsub(val.errtxt, "\n", "
") .. "

") end + io.write("\n\t\t
\n") + end + end +end +?> + +

REPORT A BUG

+ +
+ +

Info about you

+
+ +
+ +

Report description

+
+ +
+ +

Submit repport

+
+ +
+
+ diff --git a/gnats-reportsuccess-html.lsp b/gnats-reportsuccess-html.lsp new file mode 100644 index 0000000..9f6ba13 --- /dev/null +++ b/gnats-reportsuccess-html.lsp @@ -0,0 +1,2 @@ +

Thank you!

+


Your report was successfully sent!

diff --git a/gnats-status-html.lsp b/gnats-status-html.lsp new file mode 100644 index 0000000..1cf2963 --- /dev/null +++ b/gnats-status-html.lsp @@ -0,0 +1,29 @@ + +DEBUGGING

DEBUG INFO: CFE

") +io.write(html.cfe_unpack(form)) +io.write("
") +--]] +?> + +

SYSTEM INFO

+
+ +
+ +

PROGRAM SPECIFIC OPTIONS/INFORMATION

+
+ +
+ diff --git a/gnats-summary-html.lsp b/gnats-summary-html.lsp new file mode 100644 index 0000000..2c3f84a --- /dev/null +++ b/gnats-summary-html.lsp @@ -0,0 +1,81 @@ + +DEBUGGING

DEBUG INFO: CFE

") +io.write(html.cfe_unpack(form)) +io.write("
") +--]] +?> + + + +

Current Alpine problem reports

+

The following is a listing of current problems submitted by Alpine users. These represent problem reports covering all versions including experimental development code and obsolete releases.

+
+ +

Bugs can be in one of several states (S)

+ +
+
o - open
A problem report has been submitted, no sanity checking performed.
+
+
+
a - analyzed
The problem is understood and a solution is being sought.
+
+
+
f - feedback
Further work requires additional information from the originator or the community - possibly confirmation of the effectiveness of a proposed solution.
+
+
+
p - patched
A patch has been committed, but some issues (MFC and / or confirmation from originator) are still open.
+
+
+
r - repocopy
The resolution of the problem report is dependent on a repocopy operation within the CVS repository which is awaiting completion.
+
+
+
s - suspended
The problem is not being worked on, due to lack of information or resources. This is a prime candidate for somebody who is looking for a project to do. If the problem cannot be solved at all, it will be closed, rather than suspended.
+
+
+
c - closed
A problem report is closed when any changes have been integrated, documented, and tested -- or when fixing the problem is abandoned.
+
+ + +

SUMMARY

+ + + + + + + + + + + + + + + + + + + + + + +
IDSSubmittedSeverityCategoryDescription
+ diff --git a/gnats.menu b/gnats.menu new file mode 100644 index 0000000..b9c5d6a --- /dev/null +++ b/gnats.menu @@ -0,0 +1,8 @@ +#CAT GROUP/DESC TAB ACTION +Applications 98GNATS_bugreport Status status +Applications 98GNATS_bugreport Query query +Applications 98GNATS_bugreport Report_a_BUG report +#Applications 98GNATS_bugreport Statuslist summary +#Applications 98GNATS_bugreport QueryResult queryresult +Applications 98GNATS_bugreport Configuration expert + -- cgit v1.2.3