Board path now always on command line.
authorplugd <plugd@thelambdalab.xyz>
Sun, 11 Aug 2024 13:37:41 +0000 (15:37 +0200)
committerplugd <plugd@thelambdalab.xyz>
Sun, 11 Aug 2024 13:37:41 +0000 (15:37 +0200)
qwikboard

index a808233..e9e8bc3 100755 (executable)
--- a/qwikboard
+++ b/qwikboard
@@ -1,5 +1,5 @@
 #!/usr/bin/env lua
--- OBBS: An Offline-first BBS Utility
+-- QWiKBoard: A QWK-powered message board system
 -- Copyright (C) 2024 plugd
 
 -- This program is free software: you can redistribute it and/or modify
 
 local posix = require "posix"
 
-local usage = [[Usage: obbs [-c config_file] COMMAND arguments
+local usage = [[Usage: qwikboard DIR COMMAND arguments
 
-Here COMMAND is one of:
+Here DIR is the path to the message board directory and COMMAND is one of:
   newcfg
   init
   qwkout
   repin
   nncp_handler
+]]
 
-You can also use the -c option to specify an alternative configuration
-file.  (The default is the file "config.lua" in the current directory.)]]
-
-obbs = {}
-local config_file = "config.lua"
+qb = {}
 local function load_config ()
+   local config_file = qb.path .. "/config.lua"
    io.write("Loading config from file '" .. config_file .. "'... ")
    dofile(config_file)
    print("done.")
@@ -64,6 +62,10 @@ function fs.exists(filename)
    return io.open(filename, "r")
 end
 
+function fs.direxists(dirname)
+   return pcall(posix.dir, dirname)
+end
+
 function fs.dir(path)
    return posix.dir(path)
 end
@@ -96,7 +98,7 @@ function fs.touch(filename)
 end
 
 function fs.mktempdir()
-   return posix.mkdtemp("/tmp/obbs-XXXXXX")
+   return posix.mkdtemp("/tmp/qwikboard-XXXXXX")
 end
 
 --- Message database parsing ---
@@ -105,26 +107,26 @@ function Message(msg)
    msgs[msg.number] = msg
 end
 
-local function get_next_msg_number(obbs, conf_num)
-   local msgnum_filename = obbs.path .. "/conferences/" ..
-      obbs.conferences[conf_num+1] .. ".next"
+local function get_next_msg_number(conf_num)
+   local msgnum_filename = qb.path .. "/conferences/" ..
+      qb.conferences[conf_num+1] .. ".next"
    local mnf = assert(io.open(msgnum_filename, "r"))
    local next_num = tonumber(mnf:read("*all"))
    mnf:close()  
    return next_num
 end
 
-local function set_next_msg_number(obbs, conf_num, next_num)
-   local msgnum_filename = obbs.path .. "/conferences/" ..
-      obbs.conferences[conf_num+1] .. ".next"
+local function set_next_msg_number(conf_num, next_num)
+   local msgnum_filename = qb.path .. "/conferences/" ..
+      qb.conferences[conf_num+1] .. ".next"
    local mnf = assert(io.open(msgnum_filename, "w"))
    mnf:write(next_num)
    mnf:close()  
 end
 
-local function append_new_msg(obbs, conf_num, msg)
-   local cf = assert(io.open(obbs.path .. "/conferences/" ..
-                             obbs.conferences[conf_num+1] .. ".msgs", "a+"))
+local function append_new_msg(conf_num, msg)
+   local cf = assert(io.open(qb.path .. "/conferences/" ..
+                             qb.conferences[conf_num+1] .. ".msgs", "a+"))
    cf:write("\n")
    cf:write("Message{\n")
    cf:write("\tnumber=" .. msg.number .. ",\n")
@@ -148,21 +150,21 @@ end
 
 local qwk = {}
 
-function qwk.write_control(target_dir, obbs, user_name)
+function qwk.write_control(target_dir, user_name)
    local cf = assert(io.open(target_dir .. "/CONTROL.DAT", "w"))
 
-   cf:write(obbs.name .. "\r\n")
+   cf:write(qb.name .. "\r\n")
    cf:write("\r\n") -- BBS location
    cf:write("\r\n") -- BBS phone number
-   cf:write(obbs.sysop .. "\r\n")
-   cf:write("12345," .. obbs.bbsid .. "\r\n")
+   cf:write(qb.sysop .. "\r\n")
+   cf:write("12345," .. qb.bbsid .. "\r\n")
    cf:write(os.date("%d-%m-%Y,%X") .. "\r\n") -- packet creation time
    cf:write(user_name .. "\r\n")
    cf:write("\r\n")
    cf:write(0 .. "\r\n")
    cf:write(0 .. "\r\n") -- TODO: Number of messages in packet
-   cf:write(#obbs.conferences-1 .. "\r\n") -- Index of final conference
-   for i,v in ipairs(obbs.conferences) do
+   cf:write(#qb.conferences-1 .. "\r\n") -- Index of final conference
+   for i,v in ipairs(qb.conferences) do
       cf:write(i-1 .. "\r\n")
       cf:write(v .. "\r\n")
    end
@@ -174,27 +176,27 @@ function qwk.write_control(target_dir, obbs, user_name)
    cf:close()
 end
 
-function qwk.write_message (target_dir, obbs)
+function qwk.write_message (target_dir)
    local mf = assert(io.open(target_dir .. "/MESSAGES.DAT", "w"))
 
    local pkt_msg_num = 0
 
-   mf:write(space_pad("Produced by OBBS.", 128))
+   mf:write(space_pad("Produced by QWiKBoard.", 128))
 
-   for cnum,cname in ipairs(obbs.conferences) do
+   for cnum,cname in ipairs(qb.conferences) do
       msgs = {}
-      dofile(obbs.path .. "/conferences/" .. cname .. ".msgs")
+      dofile(qb.path .. "/conferences/" .. cname .. ".msgs")
       for i,msg in ipairs(msgs) do
          pkt_msg_num = pkt_msg_num + 1
-         qwk.write_message_header_block(mf, obbs, cnum, pkt_msg_num, msg)
-         qwk.write_message_text(mf, obbs, msg)
+         qwk.write_message_header_block(mf, qb, cnum, pkt_msg_num, msg)
+         qwk.write_message_text(mf, qb, msg)
       end
    end
 
    mf:close()
 end
 
-function qwk.write_message_header_block(mf, obbs, conf_num, pkt_msg_num, msg)
+function qwk.write_message_header_block(mf, conf_num, pkt_msg_num, msg)
    mf:write(" ") -- Message status flag
    mf:write(space_pad(tostring(msg.number), 7))
    mf:write(msg.date) -- mm-dd-yy
@@ -219,16 +221,16 @@ function qwk.write_message_header_block(mf, obbs, conf_num, pkt_msg_num, msg)
    mf:write(" ") -- No network tag-line present
 end
 
-function qwk.write_message_text(mf, obbs, msg)
+function qwk.write_message_text(mf, msg)
    mf:write((string.gsub(msg.text, "\n", "\xe3")))
    
    local padding = (128 - (string.len(msg.text) % 128)) % 128
    mf:write(string.rep("\0",padding))
 end
 
-function qwk.process_replies(archive_dir, obbs, user_name)
+function qwk.process_replies(archive_dir, user_name)
 
-   local msg_filename = archive_dir .. "/" .. obbs.bbsid .. ".MSG"
+   local msg_filename = archive_dir .. "/" .. qb.bbsid .. ".MSG"
 
    if not fs.exists(msg_filename) then
       print("Error: MSG file not found.")
@@ -238,17 +240,17 @@ function qwk.process_replies(archive_dir, obbs, user_name)
    local mf = assert(io.open(msg_filename, "r"))
    local block = mf:read(128)
 
-   if not string.find(block, obbs.bbsid .. " *") then
+   if not string.find(block, qb.bbsid .. " *") then
       print("Error: Failed to match BBSID in first block.")
       return false
    end
 
    while mf:read(0) do
-      qwk.process_reply(mf, obbs, user_name)
+      qwk.process_reply(mf, qb, user_name)
    end
 end
 
-function qwk.process_reply(mf, obbs, user_name)
+function qwk.process_reply(mf, user_name)
    local msg = {}
 
    mf:read(1) -- Message status (ignore for now)
@@ -275,12 +277,12 @@ function qwk.process_reply(mf, obbs, user_name)
    end
    msg.text = string.gsub(msg.text, "\xe3", "\n")
 
-   msg.number = get_next_msg_number(obbs, conf_num)
-   set_next_msg_number(obbs, conf_num, msg.number+1)
+   msg.number = get_next_msg_number(qb, conf_num)
+   set_next_msg_number(qb, conf_num, msg.number+1)
 
-   append_new_msg(obbs, conf_num, msg)
+   append_new_msg(qb, conf_num, msg)
 
-   print("Processed message for conference " .. obbs.conferences[conf_num+1] .. ".")
+   print("Processed message for conference " .. qb.conferences[conf_num+1] .. ".")
 
    -- print("Message{")
    -- for k,v in pairs(msg) do
@@ -295,47 +297,58 @@ local cmd = {}
 
 -- Send default configuration to stdout
 function cmd.newcfg ()
-   print [[
--- OBBS Configuration File
 
-obbs.path = "." -- Path to OBBS root
+   local config_file = qb.path .. "/config.lua"
+
+   if fs.exists(config_file) then
+      print("Configuration file '" .. config_file .. "' already exists.  Aborting.")
+      return false
+   end
+
+   print("Writing new configuration file " .. config_file .. "...")
 
-obbs.name = "My OBBS Name"
-obbs.bbsid = "MYOBBSID" -- upper case, max 8 char
-obbs.sysop = "Sysop Name"
+   local cf = assert(io.open(config_file, "w"))
+   cf:write([[
+-- QWiKBoard Configuration File
 
-obbs.conferences = {
+qb.name = "My BBS Name"
+qb.bbsid = "MYBBSID" -- upper case, max 8 char
+qb.sysop = "Sysop Name"
+
+qb.conferences = {
    "Announcements",
    "General",
    "Meta"
 }
 
 -- Ensure these point to zip and unzip on your system
-obbs.zip = "/usr/bin/zip"
-obbs.unzip = "/usr/bin/unzip"
-]]
+qb.zip = "/usr/bin/zip"
+qb.unzip = "/usr/bin/unzip"
+]])
+   cf:close()
+   
 end
 
 -- Initialise conference directory structure
 function cmd.init ()
    load_config()
 
-   if fs.exists(obbs.path .. "/conferences") or
-      fs.exists(obbs.path .. "/notices") then
-      print("One or more OBBS directories already exist. Aborting.")
+   if fs.exists(qb.path .. "/conferences") or
+      fs.exists(qb.path .. "/notices") then
+      print("One or more QWiKBoard directories already exist. Aborting.")
       return false
    end
 
-   fs.mkdir(obbs.path .. "/conferences")
-   for i,v in ipairs(obbs.conferences) do
-      fs.touch(obbs.path .. "/conferences/" .. v .. ".msgs")
-      io.open(obbs.path .. "/conferences/" .. v .. ".next", "w"):write(1)
+   fs.mkdir(qb.path .. "/conferences")
+   for i,v in ipairs(qb.conferences) do
+      fs.touch(qb.path .. "/conferences/" .. v .. ".msgs")
+      io.open(qb.path .. "/conferences/" .. v .. ".next", "w"):write(1)
    end
 
-   fs.mkdir(obbs.path .. "/notices")
-   fs.touch(obbs.path .. "/notices/hello")
-   fs.touch(obbs.path .. "/notices/news")
-   fs.touch(obbs.path .. "/notices/goodbye")
+   fs.mkdir(qb.path .. "/notices")
+   fs.touch(qb.path .. "/notices/hello")
+   fs.touch(qb.path .. "/notices/news")
+   fs.touch(qb.path .. "/notices/goodbye")
 end
 
 -- qwkout: generate QWK file
@@ -343,7 +356,7 @@ function cmd.qwkout ()
    load_config()
 
    if not arg[2] then
-      print [[Usage: obbs qwkout USER OUTFILE
+      print [[Usage: qwikboard DIR qwkout USER OUTFILE
 
 Here OUTFILE is is the name of the output QWK file relative to
 the current directory.  The QWK file is traditionally named
@@ -357,22 +370,22 @@ BBSID.QWK, with BBSID being the 8 character ID of the BBS.]]
    local work_dir = fs.mktempdir()
 
    -- CONTROL.DAT
-   qwk.write_control(work_dir, obbs, user_name)
+   qwk.write_control(work_dir, qb, user_name)
 
    -- Copy BBS welcome, news and goodbye files
 
-   fs.copy(obbs.path .. "/notices/hello", work_dir .. "/HELLO")
-   fs.copy(obbs.path .. "/notices/news", work_dir .. "/NEWS")
-   fs.copy(obbs.path .. "/notices/goodbye", work_dir .. "/GOODBYE")
+   fs.copy(qb.path .. "/notices/hello", work_dir .. "/HELLO")
+   fs.copy(qb.path .. "/notices/news", work_dir .. "/NEWS")
+   fs.copy(qb.path .. "/notices/goodbye", work_dir .. "/GOODBYE")
 
    -- Pack messages
 
-   qwk.write_message(work_dir, obbs)
+   qwk.write_message(work_dir, qb)
 
    -- Create archive in outgoing
 
-   os.execute(obbs.zip .. " -rj " ..
-              obbs.path .. "/" .. qwk_filename ..
+   os.execute(qb.zip .. " -rj " ..
+              qb.path .. "/" .. qwk_filename ..
               " " .. work_dir)
 
    fs.rmdir(work_dir, true)
@@ -384,11 +397,12 @@ function cmd.repin()
     load_config()
 
    if not arg[2] then
-      print [[Usage: obbs repin INFILE USER
+      print [[Usage: qwikboard DIR repin INFILE USER
 
 Here INFILE is the name of the REP file to import new messages
 from. This is traditionally named BBSID.rep, where BBSID is
-the 8 character max ID of the BBS, but obbs doesn't care about this.]]
+the 8 character max ID of the BBS, but QWiKBoard doesn't care
+about this.]]
       return
    end
 
@@ -403,9 +417,9 @@ the 8 character max ID of the BBS, but obbs doesn't care about this.]]
    local work_dir = fs.mktempdir()
 
    fs.copy(rep_filename, work_dir .. "/repfile.rep")
-   os.execute(obbs.unzip .. " " .. work_dir .. "/repfile.rep -d " .. work_dir)
+   os.execute(qb.unzip .. " " .. work_dir .. "/repfile.rep -d " .. work_dir)
 
-   qwk.process_replies(work_dir, obbs, user_name)
+   qwk.process_replies(work_dir, qb, user_name)
    
    fs.rmdir(work_dir, true)
   
@@ -423,30 +437,31 @@ be made accessible by user (node) NODENAME by adding the following
 to their neigh entry in the /etc/nncp.hjson file
 
 exec: {
-  obbs: ["/usr/bin/obbs -c /var/share/obbs/config.lua nncp"]
+  offline: ["/usr/local/bin/qwikboard /var/qb/ nncp_handler"]
 }]]
+   end
 
 end
 
 -- Main
 
 local function main()
-   if #arg > 2 and arg[1]=="-c" then
-      config_file = arg[2]
-      table.remove(arg, 1)
-      table.remove(arg, 1)
-   end
-   
-   if #arg < 1 then
+   if #arg < 2 then
       print(usage)
    else
-      if cmd[arg[1]] then
-         local f = cmd[arg[1]]
-         table.remove(arg,1)
-         f()
+      if not fs.direxists(arg[1]) then
+         print("Directory '" .. arg[1] .. "' does not exist. Aborting.")
       else
-         print("Unknown command '" .. arg[1] .. "'")
-         print(usage)
+         qb.path = arg[1]
+         table.remove(arg, 1)
+         if cmd[arg[1]] then
+            local f = cmd[arg[1]]
+            table.remove(arg,1)
+            f()
+         else
+            print("Unknown command '" .. arg[1] .. "'")
+            print(usage)
+         end
       end
    end
 end