diff --git a/.github/workflows/build-packages.yaml b/.github/workflows/build-packages.yaml index 2a0c345..c5c92f4 100644 --- a/.github/workflows/build-packages.yaml +++ b/.github/workflows/build-packages.yaml @@ -19,18 +19,18 @@ jobs: echo "build_version=$version" >> $GITHUB_OUTPUT - build-meshchat-package: + build-meshchat-openwrt-package: needs: calculate-version uses: - ./.github/workflows/workflow-meshchat-package.yaml + ./.github/workflows/workflow-meshchat-openwrt-package.yaml with: build_version: ${{ needs.calculate-version.outputs.build_version }} build_dir: package/meshchat-ipkg - build-meshchat-api-package: + build-meshchat-debian-package: needs: calculate-version uses: - ./.github/workflows/workflow-meshchat-api-package.yaml + ./.github/workflows/workflow-meshchat-debian-package.yaml with: build_version: ${{ needs.calculate-version.outputs.build_version }} - build_dir: package/meshchat-ipkg + build_dir: package/meshchat-deb diff --git a/.github/workflows/workflow-meshchat-debian-package.yaml b/.github/workflows/workflow-meshchat-debian-package.yaml new file mode 100644 index 0000000..114a2c2 --- /dev/null +++ b/.github/workflows/workflow-meshchat-debian-package.yaml @@ -0,0 +1,42 @@ +name: Build MeshChat Package +on: + workflow_call: + inputs: + build_version: + required: true + type: string + build_dir: + required: true + type: string + ref: + required: false + type: string + default: ${{ github.ref_name }} + +jobs: + create-meshchat-debian-package: + runs-on: ubuntu-latest + # container: + # image: registry.gitlab.com/wt0f/gitlab-runner-images/shell:latest + outputs: + package_file: ${{ steps.detect-package-file.outputs.file }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ inputs.ref }} + # - run: info "Populating the filesystem with MeshChat files" + - run: echo ${{ inputs.build_version }} > VERSION + - run: package/populate-meshchat-fs.sh ${{ inputs.build_dir }} debian + # - run: info "Updating version numbers to " + - run: package/update-version.sh ${{ inputs.build_dir }} + # - run: info "Packing up MeshChat files" + - run: dpkg-deb --root-owner-group --build ${{ inputs.build_dir }} + - run: mv package/meshchat-deb.deb meshchat_${{ inputs.build_version }}_all.deb + - id: detect-package-file + run: echo "file=meshchat_${{inputs.build_version }}_all.deb" >> $GITHUB_OUTPUT + - run: echo "${{ steps.detect-package-file.outputs.file }}" + - uses: actions/upload-artifact@v4 + with: + name: ${{ steps.detect-package-file.outputs.file }} + path: ${{ steps.detect-package-file.outputs.file }} diff --git a/.github/workflows/workflow-meshchat-package.yaml b/.github/workflows/workflow-meshchat-openwrt-package.yaml similarity index 96% rename from .github/workflows/workflow-meshchat-package.yaml rename to .github/workflows/workflow-meshchat-openwrt-package.yaml index bee92b0..46ea64c 100644 --- a/.github/workflows/workflow-meshchat-package.yaml +++ b/.github/workflows/workflow-meshchat-openwrt-package.yaml @@ -14,7 +14,7 @@ on: default: ${{ github.ref_name }} jobs: - create-meshchat-package: + create-meshchat-openwrt-package: runs-on: ubuntu-latest # container: # image: registry.gitlab.com/wt0f/gitlab-runner-images/shell:latest @@ -27,7 +27,7 @@ jobs: ref: ${{ inputs.ref }} # - run: info "Populating the filesystem with MeshChat files" - run: echo ${{ inputs.build_version }} > VERSION - - run: package/populate-meshchat-fs.sh ${{ inputs.build_dir }} + - run: package/populate-meshchat-fs.sh ${{ inputs.build_dir }} openwrt # - run: info "Updating version numbers to " - run: package/update-version.sh ${{ inputs.build_dir }} # - run: info "Packing up MeshChat files" diff --git a/lib/json.lua b/lib/json.lua new file mode 100644 index 0000000..711ef78 --- /dev/null +++ b/lib/json.lua @@ -0,0 +1,388 @@ +-- +-- json.lua +-- +-- Copyright (c) 2020 rxi +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy of +-- this software and associated documentation files (the "Software"), to deal in +-- the Software without restriction, including without limitation the rights to +-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +-- of the Software, and to permit persons to whom the Software is furnished to do +-- so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in all +-- copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. +-- + +local json = { _version = "0.1.2" } + +------------------------------------------------------------------------------- +-- Encode +------------------------------------------------------------------------------- + +local encode + +local escape_char_map = { + [ "\\" ] = "\\", + [ "\"" ] = "\"", + [ "\b" ] = "b", + [ "\f" ] = "f", + [ "\n" ] = "n", + [ "\r" ] = "r", + [ "\t" ] = "t", +} + +local escape_char_map_inv = { [ "/" ] = "/" } +for k, v in pairs(escape_char_map) do + escape_char_map_inv[v] = k +end + + +local function escape_char(c) + return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte())) +end + + +local function encode_nil(val) + return "null" +end + + +local function encode_table(val, stack) + local res = {} + stack = stack or {} + + -- Circular reference? + if stack[val] then error("circular reference") end + + stack[val] = true + + if rawget(val, 1) ~= nil or next(val) == nil then + -- Treat as array -- check keys are valid and it is not sparse + local n = 0 + for k in pairs(val) do + if type(k) ~= "number" then + error("invalid table: mixed or invalid key types") + end + n = n + 1 + end + if n ~= #val then + error("invalid table: sparse array") + end + -- Encode + for i, v in ipairs(val) do + table.insert(res, encode(v, stack)) + end + stack[val] = nil + return "[" .. table.concat(res, ",") .. "]" + + else + -- Treat as an object + for k, v in pairs(val) do + if type(k) ~= "string" then + error("invalid table: mixed or invalid key types") + end + table.insert(res, encode(k, stack) .. ":" .. encode(v, stack)) + end + stack[val] = nil + return "{" .. table.concat(res, ",") .. "}" + end +end + + +local function encode_string(val) + return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"' +end + + +local function encode_number(val) + -- Check for NaN, -inf and inf + if val ~= val or val <= -math.huge or val >= math.huge then + error("unexpected number value '" .. tostring(val) .. "'") + end + return string.format("%.14g", val) +end + + +local type_func_map = { + [ "nil" ] = encode_nil, + [ "table" ] = encode_table, + [ "string" ] = encode_string, + [ "number" ] = encode_number, + [ "boolean" ] = tostring, +} + + +encode = function(val, stack) + local t = type(val) + local f = type_func_map[t] + if f then + return f(val, stack) + end + error("unexpected type '" .. t .. "'") +end + + +function json.encode(val) + return ( encode(val) ) +end + + +------------------------------------------------------------------------------- +-- Decode +------------------------------------------------------------------------------- + +local parse + +local function create_set(...) + local res = {} + for i = 1, select("#", ...) do + res[ select(i, ...) ] = true + end + return res +end + +local space_chars = create_set(" ", "\t", "\r", "\n") +local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",") +local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u") +local literals = create_set("true", "false", "null") + +local literal_map = { + [ "true" ] = true, + [ "false" ] = false, + [ "null" ] = nil, +} + + +local function next_char(str, idx, set, negate) + for i = idx, #str do + if set[str:sub(i, i)] ~= negate then + return i + end + end + return #str + 1 +end + + +local function decode_error(str, idx, msg) + local line_count = 1 + local col_count = 1 + for i = 1, idx - 1 do + col_count = col_count + 1 + if str:sub(i, i) == "\n" then + line_count = line_count + 1 + col_count = 1 + end + end + error( string.format("%s at line %d col %d", msg, line_count, col_count) ) +end + + +local function codepoint_to_utf8(n) + -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa + local f = math.floor + if n <= 0x7f then + return string.char(n) + elseif n <= 0x7ff then + return string.char(f(n / 64) + 192, n % 64 + 128) + elseif n <= 0xffff then + return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128) + elseif n <= 0x10ffff then + return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128, + f(n % 4096 / 64) + 128, n % 64 + 128) + end + error( string.format("invalid unicode codepoint '%x'", n) ) +end + + +local function parse_unicode_escape(s) + local n1 = tonumber( s:sub(1, 4), 16 ) + local n2 = tonumber( s:sub(7, 10), 16 ) + -- Surrogate pair? + if n2 then + return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000) + else + return codepoint_to_utf8(n1) + end +end + + +local function parse_string(str, i) + local res = "" + local j = i + 1 + local k = j + + while j <= #str do + local x = str:byte(j) + + if x < 32 then + decode_error(str, j, "control character in string") + + elseif x == 92 then -- `\`: Escape + res = res .. str:sub(k, j - 1) + j = j + 1 + local c = str:sub(j, j) + if c == "u" then + local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1) + or str:match("^%x%x%x%x", j + 1) + or decode_error(str, j - 1, "invalid unicode escape in string") + res = res .. parse_unicode_escape(hex) + j = j + #hex + else + if not escape_chars[c] then + decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string") + end + res = res .. escape_char_map_inv[c] + end + k = j + 1 + + elseif x == 34 then -- `"`: End of string + res = res .. str:sub(k, j - 1) + return res, j + 1 + end + + j = j + 1 + end + + decode_error(str, i, "expected closing quote for string") +end + + +local function parse_number(str, i) + local x = next_char(str, i, delim_chars) + local s = str:sub(i, x - 1) + local n = tonumber(s) + if not n then + decode_error(str, i, "invalid number '" .. s .. "'") + end + return n, x +end + + +local function parse_literal(str, i) + local x = next_char(str, i, delim_chars) + local word = str:sub(i, x - 1) + if not literals[word] then + decode_error(str, i, "invalid literal '" .. word .. "'") + end + return literal_map[word], x +end + + +local function parse_array(str, i) + local res = {} + local n = 1 + i = i + 1 + while 1 do + local x + i = next_char(str, i, space_chars, true) + -- Empty / end of array? + if str:sub(i, i) == "]" then + i = i + 1 + break + end + -- Read token + x, i = parse(str, i) + res[n] = x + n = n + 1 + -- Next token + i = next_char(str, i, space_chars, true) + local chr = str:sub(i, i) + i = i + 1 + if chr == "]" then break end + if chr ~= "," then decode_error(str, i, "expected ']' or ','") end + end + return res, i +end + + +local function parse_object(str, i) + local res = {} + i = i + 1 + while 1 do + local key, val + i = next_char(str, i, space_chars, true) + -- Empty / end of object? + if str:sub(i, i) == "}" then + i = i + 1 + break + end + -- Read key + if str:sub(i, i) ~= '"' then + decode_error(str, i, "expected string for key") + end + key, i = parse(str, i) + -- Read ':' delimiter + i = next_char(str, i, space_chars, true) + if str:sub(i, i) ~= ":" then + decode_error(str, i, "expected ':' after key") + end + i = next_char(str, i + 1, space_chars, true) + -- Read value + val, i = parse(str, i) + -- Set + res[key] = val + -- Next token + i = next_char(str, i, space_chars, true) + local chr = str:sub(i, i) + i = i + 1 + if chr == "}" then break end + if chr ~= "," then decode_error(str, i, "expected '}' or ','") end + end + return res, i +end + + +local char_func_map = { + [ '"' ] = parse_string, + [ "0" ] = parse_number, + [ "1" ] = parse_number, + [ "2" ] = parse_number, + [ "3" ] = parse_number, + [ "4" ] = parse_number, + [ "5" ] = parse_number, + [ "6" ] = parse_number, + [ "7" ] = parse_number, + [ "8" ] = parse_number, + [ "9" ] = parse_number, + [ "-" ] = parse_number, + [ "t" ] = parse_literal, + [ "f" ] = parse_literal, + [ "n" ] = parse_literal, + [ "[" ] = parse_array, + [ "{" ] = parse_object, +} + + +parse = function(str, idx) + local chr = str:sub(idx, idx) + local f = char_func_map[chr] + if f then + return f(str, idx) + end + decode_error(str, idx, "unexpected character '" .. chr .. "'") +end + + +function json.decode(str) + if type(str) ~= "string" then + error("expected argument of type string, got " .. type(str)) + end + local res, idx = parse(str, next_char(str, 1, space_chars, true)) + idx = next_char(str, idx, space_chars, true) + if idx <= #str then + decode_error(str, idx, "trailing garbage") + end + return res +end + + +return json diff --git a/meshchat b/meshchat index 9a3c507..f0bad74 100755 --- a/meshchat +++ b/meshchat @@ -37,27 +37,29 @@ package.path = package.path .. ";/www/cgi-bin/?.lua" -require('luci.http') -local json = require("luci.jsonc") -require("nixio") +local json = require("json") +local lib = require("meshchatlib") +local url = require("net/url") require("meshchatconfig") -require("meshchatlib") --- -- @module meshchat local query = {} local uploadfilename -if os.getenv("QUERY_STRING") ~= "" or os.getenv("REQUEST_METHOD") == "POST" then - local request = luci.http.Request(nixio.getenv(), - function() - local v = io.read(1024) - if not v then - io.close() - end - return v - end - ) + +if os.getenv("QUERY_STRING") ~= "" then + -- Process a GET request + query = url.parseQuery(os.getenv("QUERY_STRING")) +elseif os.getenv("REQUEST_METHOD") == "POST" then + -- Process a POST request + local request = '' + while v = io.read(1024) do + request = request .. v + end + + query = url.parseQuery(request) + local fp request:setfilehandler( function(meta, chunk, eof) @@ -65,7 +67,7 @@ if os.getenv("QUERY_STRING") ~= "" or os.getenv("REQUEST_METHOD") == "POST" then if meta and meta.file then uploadfilename = meta.file end - nixio.fs.mkdir(tmp_upload_dir) + mkdir(tmp_upload_dir) fp = io.open(tmp_upload_dir .. "/file", "w") end if chunk then @@ -113,13 +115,13 @@ function config() local settings = { version = app_version, protocol_verison = protocol_version, - node = node_name(), - zone = zone_name(), + node = lib.node_name(), + zone = lib.zone_name(), default_channel = default_channel, debug = debug, } - print(json.stringify(settings)) + print(json.encode(settings)) end --- Send a message to the MeshChat instance. @@ -166,21 +168,21 @@ function send_message() epoch = query.epoch end - get_lock() + lib.get_lock(lock_file) local f = io.open(messages_db_file, "a") if not f then - release_lock() + lib.release_lock(lock_file) -- TODO return a proper error code on failure die("Cannot send message") end - f:write(id .. "\t" .. epoch .. "\t" .. message .. "\t" .. query.call_sign .. "\t" .. node_name() .. "\t" .. platform .. "\t" .. query.channel .. "\n") + f:write(id .. "\t" .. epoch .. "\t" .. message .. "\t" .. query.call_sign .. "\t" .. lib.node_name() .. "\t" .. platform .. "\t" .. query.channel .. "\n") f:close() - sort_and_trim_db() - save_messages_db_version() + lib.sort_and_trim_db() + lib.save_messages_db_version() - release_lock() + lib.release_lock(lock_file) print([[{"status":200, "response":"OK"}]]) end @@ -220,9 +222,9 @@ function messages() print("\r") io.flush() - get_lock() + lib.get_lock(lock_file) - local node = node_name() + local node = lib.node_name() -- read in message DB and parse the contents local messages = {} @@ -246,7 +248,7 @@ function messages() local users = {} -- read the users status file - if nixio.fs.stat(local_users_status_file) then + if lib.file_exists(local_users_status_file) then for line in io.lines(local_users_status_file) do local call_sign = line:match("^([^\t]+)\t") @@ -283,12 +285,12 @@ function messages() end end - release_lock() + lib.release_lock(lock_file) -- order messages according to time table.sort(messages, function(a, b) return a.epoch > b.epoch end) - output:write(json.stringify(messages)) + output:write(json.encode(messages)) output:flush() end @@ -312,10 +314,10 @@ function sync_status() print("Content-type: application/json\r") print("\r") - get_lock() + lib.get_lock(lock_file) local status = {} - if nixio.fs.stat(sync_status_file) then + if lib.file_exists(sync_status_file) then for line in io.lines(sync_status_file) do local node, epoch = line:match("^(.*)\t(.*)$") @@ -326,16 +328,16 @@ function sync_status() end end - release_lock() + lib.release_lock(lock_file) table.sort(status, function(a, b) return a.epoch > b.epoch end) - print(json.stringify(status)) + print(json.encode(status)) end --- Return a list of messages as text. function messages_raw() - get_lock() + lib.get_lock(lock_file) local md5 = file_md5(messages_db_file) local lines = {} @@ -344,7 +346,7 @@ function messages_raw() lines[#lines + 1] = line end - release_lock() + lib.release_lock(lock_file) print("Content-MD5: " .. md5 .. "\r") print("Content-type: text/plain\r") @@ -358,11 +360,11 @@ end --- Return the current MD5 has of the messages database. function messages_md5() - get_lock() + lib.get_lock(lock_file) - local md5 = file_md5(messages_db_file) + local md5 = lib.file_md5(messages_db_file) - release_lock() + lib.release_lock(lock_file) print("Content-type: text/plain\r") print("\r") @@ -371,16 +373,16 @@ end --- Package the raw messages as the messages.txt file. function messages_download() - get_lock() + lib.get_lock() - local md5 = file_md5(messages_db_file) + local md5 = lib.file_md5(messages_db_file) local lines = {} for line in io.lines(messages_db_file) do lines[#lines + 1] = line end - release_lock() + lib.release_lock(lock_file) print("Content-MD5: " .. md5 .. "\r") print("Content-Disposition: attachment; filename=messages.txt;\r") @@ -395,16 +397,16 @@ end --- Return the list of users as raw text. function users_raw() - get_lock() + lib.get_lock(lock_file) - local md5 = file_md5(local_users_status_file) + local md5 = lib.file_md5(local_users_status_file) local lines = {} for line in io.lines(local_users_status_file) do lines[#lines + 1] = line end - release_lock() + lib.release_lock(lock_file) print("Content-MD5: " .. md5 .. "\r") print("Content-type: text/plain\r") @@ -438,7 +440,7 @@ function users() print("Content-type: application/json\r") print("\r") - get_lock() + lib.get_lock(lock_file) local users = {} for line in io.lines(local_users_status_file) @@ -468,33 +470,33 @@ function users() end end - release_lock() + lib.release_lock(lock_file) table.sort(users, function(a, b) return a.epoch > b.epoch end) - print(json.stringify(users)) + print(json.encode(users)) end --- Return a list of files as plain text. function local_files_raw() - get_lock() + lib.get_lock(lock_file) - local tmp_file = meshchat_path .. "/meshchat_files_local." .. nixio.getpid() + local tmp_file = meshchat_path .. "/meshchat_files_local." .. math.random(5000) local f = io.open(tmp_file, "w") if not f then die("Cannot list local files") end - local name = node_name() .. ":" .. os.getenv("SERVER_PORT") - for file in nixio.fs.dir(local_files_dir) + local name = lib.node_name() .. ":" .. os.getenv("SERVER_PORT") + for file in lib.filelist(local_files_dir) do - local stat = nixio.fs.stat(local_files_dir .. "/" .. file) + local stat = lib.file_exists(local_files_dir .. "/" .. file) f:write(file .. "\t" .. name .. "\t" .. stat.size .. "\t" .. stat.mtime .. platform .. "\n") end f:close() - local md5 = file_md5(tmp_file) + local md5 = lib.file_md5(tmp_file) - release_lock() + lib.release_lock(lock_file) print("Content-MD5: " .. md5 .. "\r") print("Content-type: text/plain\r") @@ -505,7 +507,7 @@ function local_files_raw() print(line) end - nixio.fs.remove(tmp_file) + lib.unlink(tmp_file) end --- Return a specified file as a download. @@ -525,17 +527,17 @@ function file_download() local file = query.file local file_path = local_files_dir .. "/" .. file - if file == "" or not nixio.fs.stat(file_path) then + if file == "" or not lib.file_exists(file_path) then error("no file") return end - get_lock() + lib.get_lock(lock_file) - local md5 = file_md5(file_path) + local md5 = lib.file_md5(file_path) local f = io.open(file_path, "rb") - release_lock() + lib.release_lock(lock_file) print("Content-MD5: " .. md5 .. "\r") print("Content-Disposition: attachment; filename=\"" .. file .. "\";\r") @@ -570,13 +572,13 @@ function files() print("Content-type: application/json\r") print("\r") - get_lock() + lib.get_lock(lock_file) local files = {} - local node = node_name() .. ":" .. os.getenv("SERVER_PORT") - for file in nixio.fs.dir(local_files_dir) + local node = lib.node_name() .. ":" .. os.getenv("SERVER_PORT") + for file in lib.filelist(local_files_dir) do - local stat = nixio.fs.stat(local_files_dir .. "/" .. file) + local stat = lib.file_exists(local_files_dir .. "/" .. file) files[#files + 1] = { file = file, epoch = stat.mtime, @@ -586,7 +588,7 @@ function files() } files[#files]["local"] = 1 end - for file in nixio.fs.dir(meshchat_path) + for file in lib.filelist(meshchat_path) do if file:match("^remote_files%.") then for line in io.lines(meshchat_path .. "/" .. file) @@ -606,13 +608,13 @@ function files() end end - local stats = file_storage_stats() + local stats = lib.file_storage_stats() - release_lock() + lib.release_lock(lock_file) table.sort(files, function(a, b) return a.epoch > b.epoch end) - print(json.stringify({ + print(json.encode({ stats = stats, files = files })) @@ -620,7 +622,7 @@ end --- Delete the specified file. function delete_file() - nixio.fs.remove(local_files_dir .. "/" .. query.file) + lib.unlink(local_files_dir .. "/" .. query.file) print("Content-type: application/json\r") print("\r") print([[{"status":200, "response":"OK"}]]) @@ -630,7 +632,7 @@ end function messages_version() print("Content-type: text/plain\r") print("\r") - print(get_messages_db_version()) + print(lib.get_messages_db_version()) end --- Return a JSON document of the messages database. @@ -638,9 +640,9 @@ function messages_version_ui() print("Content-type: application/json\r") print("\r") - print(string.format([[{"messages_version":%s}]], get_messages_db_version())) + print(string.format([[{"messages_version":%s}]], lib.get_messages_db_version())) - get_lock() + lib.get_lock(lock_file) local users = {} for line in io.lines(local_users_status_file) @@ -651,7 +653,7 @@ function messages_version_ui() end end - local node = node_name() + local node = lib.node_name() local epoch = os.time() if tonumber(query.epoch) > epoch then epoch = query.epoch @@ -676,7 +678,7 @@ function messages_version_ui() f:close() end - release_lock() + lib.release_lock(lock_file) end --- Return a JSON document describing all the hosts. @@ -699,7 +701,7 @@ function hosts() print("Content-type: application/json\r") print("\r") - local node = node_name() + local node = lib.node_name() local hosts = {} for line in io.lines("/var/dhcp.leases") do @@ -711,10 +713,10 @@ function hosts() } end - for line in io.lines("/etc/config.mesh/_setup.dhcp.dmz") + for line in io.lines("/etc/mesh/_setup.dhcp.dmz") do local mac, num, hostname = line:match("^(%S+)%s(%S+)%s(%S+)$") - local ip = gethostbyname(hostname) + local ip = lib.gethostbyname(hostname) hosts[#hosts + 1] = { ip = ip, hostname = hostname, @@ -722,7 +724,7 @@ function hosts() } end - for _, remote_node in ipairs(node_list()) + for _, remote_node in ipairs(lib.node_list()) do local f = io.popen("/usr/bin/curl --retry 0 --connect-timeout " .. connect_timeout .. " --speed-time " .. speed_time .. " --speed-limit " .. speed_limit .. " http://" .. remote_node .. ":8080/cgi-bin/meshchat?action=hosts_raw 2> /dev/null") if f then @@ -743,7 +745,7 @@ function hosts() table.sort(hosts, function(a, b) return a.hostname < b.hostname end) - print(json.stringify(hosts)) + print(json.encode(hosts)) end --- Return a list of hosts as plain text. @@ -761,10 +763,10 @@ function hosts_raw() } end - for line in io.lines("/etc/config.mesh/_setup.dhcp.dmz") + for line in io.lines("/etc/mesh/_setup.dhcp.dmz") do local mac, num, hostname = line:match("^(%S+)%s(%S+)%s(%S+)$") - local ip = gethostbyname(hostname) + local ip = lib.gethostbyname(hostname) hosts[#hosts + 1] = { ip = ip, hostname = hostname @@ -779,19 +781,19 @@ end --- Store a file into the file directory. function upload_file() - local new_file_size = nixio.fs.stat(tmp_upload_dir .. "/file").size + local new_file_size = lib.file_exists(tmp_upload_dir .. "/file").size - get_lock() + lib.get_lock(lock_file) - local stats = file_storage_stats() + local stats = lib.file_storage_stats() - release_lock() + lib.release_lock(lock_file) print("Content-type: application/json\r") print("\r") if new_file_size > stats.files_free then - nixio.fs.remove(tmp_upload_dir .. "/file") + lib.unlink(tmp_upload_dir .. "/file") print([[{"status":500, "response":"Not enough storage, delete some files"}]]) else local fi = io.open(tmp_upload_dir .. "/file", "r") @@ -799,7 +801,7 @@ function upload_file() fo:write(fi:read("*a")) fi:close() fo:close() - nixio.fs.remove(tmp_upload_dir .. "/file") + lib.unlink(tmp_upload_dir .. "/file") print([[{"status":200, "response":"OK"}]]) end end diff --git a/meshchatlib.lua b/meshchatlib.lua index f409ea2..6b69be4 100755 --- a/meshchatlib.lua +++ b/meshchatlib.lua @@ -34,8 +34,10 @@ --]] -require("nixio") require("uci") +require("socket") + +local M = {} --- @module meshchatlib @@ -43,7 +45,7 @@ require("uci") -- -- @tparam string msg Message to display -- -function die(msg) +function M:die(msg) os.exit(-1) end @@ -52,7 +54,7 @@ end -- @tparam string cmd Command line to execute -- @treturn string stdout of the command -- -function capture(cmd) +function M:capture(cmd) local f = io.popen(cmd) if not f then return "" @@ -62,6 +64,55 @@ function capture(cmd) return output end +-- TODO remove AAMM specific parts +function M:filelist(dir) + local files = {} + for file in io.popen("ls -1 " .. dir .. "/*.txt"):lines() do + file = file:gsub(Config.aam_dir .. "/", "") + table.insert(files, file) + end + return files +end + +function M:file_exists(path) + local f = io.open(path, "r") + return f ~= nil and io.close(f) +end + +function M:file_size(path) + local f = io.open(path, "r") + local size = f:seek("end") + f:close() + return size +end + +function M:file_mtime(path) + local proc = io.popen("ls --full-time " .. path, "r") + local file_info = proc:read("*all") + proc:close() + -- -rw-r--r-- 1 root root 0 2023-09-29 00:32:22 -0400 foo + + local yr, mo, da, hr, mi, se, tz = file_info:match("[-rwx]+%s+%d+%s%w+%s+%w+%s+%d+%s(%d+)-(%d+)-(%d+)%s(%d+):(%d+):(%d+)%s([-+%d]+)%s") + local offset = (tz / 100) * 3600 + return os.time({year=yr, month=mo, day=da, hour=hr, min=mi, sec=se}) - offset +end + +function M:mkdir(path) + os.execute('mkdir ' .. path .. ' >/dev/null 2>&1') +end + +function M:unlink(path) + os.execute('rm ' .. path .. ' >/dev/null 2>&1') +end + +function M:chmod(path, mode) + os.execute('chmod ' .. mode .. ' ' .. path .. ' >/dev/null 2>&1') +end + +function M:rename(src, dest) + os.execute('mv ' .. src .. ' ' .. dest .. ' >/dev/null 2>&1') +end + --- -- Retrieve the current node name. -- @@ -70,7 +121,7 @@ end -- -- @treturn string Name of current node -- -function node_name() +function M:node_name() return uci.cursor("/etc/local/uci"):get("hsmmmesh", "settings", "node") or "" end @@ -79,14 +130,14 @@ end -- -- @treturn string Name of MeshChat zone -- -function zone_name() +function M:zone_name() local dmz_mode = uci.cursor("/etc/config.mesh"):get("aredn", "@dmz[0]", "mode") local servfile = "/etc/config.mesh/_setup.services.nat" -- LAN mode is not set to NAT if dmz_mode ~= "0" then servfile = "/etc/config.mesh/_setup.services.dmz" end - if nixio.fs.access(servfile) then + if M.file_exists(servfile) then for line in io.lines(servfile) do local zone = line:match("^(.*)|.*|.*|.*|.*|meshchat$") @@ -98,18 +149,21 @@ function zone_name() return "MeshChat" end -messages_db_file = messages_db_file_orig .. "." .. zone_name() +messages_db_file = messages_db_file_orig .. "." .. M.zone_name() -local lock_fd -function get_lock() - if not lock_fd then - lock_fd = nixio.open(lock_file, "w", "666") +local lock_fd = false +function M:get_lock(lock_file) + while (not os.execute("mkdir " .. lock_file .. " >/dev/null 2>&1")) do + socket.select(nil, nil, 0.2) end - lock_fd:lock("lock") + lock_fd = true end -function release_lock() - lock_fd:lock("ulock") +function M:release_lock(lock_file) + if (lock_fd) then + os.execute("rmdir " .. lock_file) + lock_fd = false + end end --- Generate the MD5 sum of a file. @@ -124,15 +178,15 @@ end -- @tparam string file Path to file -- @treturn string Result of `md5sum` of the file -- -function file_md5(file) - if not nixio.fs.stat(file) then +function M:file_md5(file) + if not file_exists(file) then return "" end local output = capture("md5sum " .. file:gsub(" ", "\\ ")):match("^(%S+)%s") return output and output or "" end -function get_messages_db_version() +function M:get_messages_db_version() for line in io.lines(messages_version_file) do line = line:gsub("\n$", "") @@ -140,14 +194,14 @@ function get_messages_db_version() end end -function save_messages_db_version() +function M:save_messages_db_version() local f = io.open(messages_version_file, "w") - f:write(get_messages_version_file() .. "\n") + f:write(M.get_messages_version_file() .. "\n") f:close() - nixio.fs.chmod(messages_version_file, "666") + M.chmod(messages_version_file, "666") end -function get_messages_version_file() +function M:get_messages_version_file() local sum = 0 for line in io.lines(messages_db_file) do @@ -170,11 +224,11 @@ end -- -- @treturn string Generated hash value -- -function hash() - return capture("echo " .. os.time() .. math.random(99999) .. " | md5sum"):sub(1, 8) +function M:hash() + return M.capture("echo " .. os.time() .. math.random(99999) .. " | md5sum"):sub(1, 8) end -function sort_and_trim_db() +function M:sort_and_trim_db() local valid_time = os.time() + valid_future_message_time local unused_count = max_messages_db_size local messages = {} @@ -212,7 +266,7 @@ function sort_and_trim_db() f:close() end -function file_storage_stats() +function M:file_storage_stats() local lines = capture("df -k " .. local_files_dir) local blocks, used, available, perc = lines:match("(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%%") used = tonumber(used) * 1024 @@ -220,9 +274,9 @@ function file_storage_stats() local total = used + available local local_files_bytes = 0 - for file in nixio.fs.dir(local_files_dir) + for file in M.filelist(local_files_dir) do - local_files_bytes = local_files_bytes + nixio.fs.stat(local_files_dir .. "/" .. file).size + local_files_bytes = local_files_bytes + M.file_size(local_files_dir .. "/" .. file) end if max_file_storage - local_files_bytes < 0 then @@ -238,19 +292,19 @@ function file_storage_stats() } end -function gethostbyname(hostname) +function M:gethostbyname(hostname) return capture("nslookup " .. hostname):match("Address 1:%s*([%d%.]+)") end -function node_list() - if not nixio.fs.stat("/var/run/services_olsr") then +function M:node_list() + if not M.file_exists("/var/run/services_olsr") then return {} end - local local_node = node_name():lower() - local zone = zone_name() + local local_node = M.node_name():lower() + local zone = M.zone_name() local nodes = {} - local pattern = "http://(%S+):(%d+)/meshchat|tcp|" .. str_escape(zone) .. "%s" + local pattern = "http://(%S+):(%d+)/meshchat|tcp|" .. M.str_escape(zone) .. "%s" for line in io.lines("/var/run/services_olsr") do local node, port = line:match(pattern) @@ -280,6 +334,20 @@ end -- @tparam string str String to encode -- @treturn string Encoded string -- -function str_escape(str) +function M:str_escape(str) return str:gsub("%(", "%%("):gsub("%)", "%%)"):gsub("%%", "%%%%"):gsub("%.", "%%."):gsub("%+", "%%+"):gsub("-", "%%-"):gsub("%*", "%%*"):gsub("%[", "%%["):gsub("%?", "%%?"):gsub("%^", "%%^"):gsub("%$", "%%$") end + + +function M:getenv() + local osEnv = {} + + for line in io.popen("set"):lines() do + envName = line:match("^[^=]+") + osEnv[envName] = os.getenv(envName) + end + + return osEnv +end + +return M diff --git a/package/debian/control b/package/debian/control new file mode 100644 index 0000000..6718924 --- /dev/null +++ b/package/debian/control @@ -0,0 +1,9 @@ +Package: meshchat +Version: +Depends: lua5.4, nginx +Source: package/meshchat +Section: net +Priority: optional +Maintainer: Gerard Hickey +Architecture: all +Description: P2P distributed chat for mesh networks diff --git a/package/debian/postinst b/package/debian/postinst new file mode 100755 index 0000000..7394745 --- /dev/null +++ b/package/debian/postinst @@ -0,0 +1,6 @@ +#!/bin/sh + +/etc/init.d/meshchatsync enable +/etc/init.d/meshchatsync start + +exit 0 diff --git a/package/debian/preinst b/package/debian/preinst new file mode 100755 index 0000000..29acc87 --- /dev/null +++ b/package/debian/preinst @@ -0,0 +1,19 @@ +#!/bin/sh + +/etc/init.d/meshchatsync stop > /dev/null 2> /dev/null + +mkdir -p /var/www/html/meshchat + +# if there is not a meshchat_local.lua, then prepare one +if [ ! -f /usr/lib/cgi-bin/meshchat_local.lua ]; then + if [ -f /usr/lib/cgi-bin/meshchatconfig.lua ]; then + cp /usr/lib/cgi-bin/meshchatconfig.lua /usr/lib/cgi-bin/meshchat_local.lua + + # remove vars that should not be in meshchat_local.lua + sed -i "/^protocol_version/d; /^app_version/d" /usr/lib/cgi-bin/meshchat_local.lua + else + touch /usr/lib/cgi-bin/meshchat_local.lua + fi +fi + +exit 0 diff --git a/package/meshchat/prerm b/package/debian/prerm similarity index 100% rename from package/meshchat/prerm rename to package/debian/prerm diff --git a/package/meshchat-api/control b/package/meshchat-api/control deleted file mode 100755 index 0761bf2..0000000 --- a/package/meshchat-api/control +++ /dev/null @@ -1,10 +0,0 @@ -Package: meshchat-api -Version: -Depends: lua -Provides: -Source: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY -Section: net -Priority: optional -Maintainer: Tim Wilkinson (KN6PLV) and Trevor Paskett (K7FPV) -Architecture: all -Description: P2P distributed chat for mesh networks diff --git a/package/meshchat/control b/package/openwrt/control similarity index 74% rename from package/meshchat/control rename to package/openwrt/control index d78266e..0791ad5 100644 --- a/package/meshchat/control +++ b/package/openwrt/control @@ -5,6 +5,6 @@ Provides: Source: package/meshchat Section: net Priority: optional -Maintainer: Tim Wilkinson (KN6PLV) and Trevor Paskett (K7FPV) +Maintainer: Gerard Hickey Architecture: all Description: P2P distributed chat for mesh networks diff --git a/package/meshchat/postinst b/package/openwrt/postinst similarity index 100% rename from package/meshchat/postinst rename to package/openwrt/postinst diff --git a/package/meshchat/preinst b/package/openwrt/preinst similarity index 100% rename from package/meshchat/preinst rename to package/openwrt/preinst diff --git a/package/openwrt/prerm b/package/openwrt/prerm new file mode 100755 index 0000000..eb6c0f7 --- /dev/null +++ b/package/openwrt/prerm @@ -0,0 +1,8 @@ +#!/bin/sh + +/etc/init.d/meshchatsync disable +/etc/init.d/meshchatsync stop + +rm -rf /tmp/meshchat + +exit 0 diff --git a/package/populate-meshchat-api-fs.sh b/package/populate-meshchat-api-fs.sh deleted file mode 100755 index f68f50a..0000000 --- a/package/populate-meshchat-api-fs.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -# This script runs from the top of the project directory and -# creates the filesystem image for the MeshChat API package. - -IPK_DIR=$1 - -# Populate the CONTROL portion of the package -mkdir -p $IPK_DIR/CONTROL -cp -p package/meshchat-api/* $IPK_DIR/CONTROL/ -sed -i "s%\$GITHUB_SERVER_URL%$GITHUB_SERVER_URL%" $IPK_DIR/CONTROL/control -sed -i "s%\$GITHUB_REPOSITORY%$GITHUB_REPOSITORY%" $IPK_DIR/CONTROL/control - -# Populate the filesystem image for the package -install -D api/meshchat -m 755 $IPK_DIR/www/cgi-bin/meshchat diff --git a/package/populate-meshchat-fs.sh b/package/populate-meshchat-fs.sh index 656df9b..5d5d177 100755 --- a/package/populate-meshchat-fs.sh +++ b/package/populate-meshchat-fs.sh @@ -4,19 +4,42 @@ # creates the filesystem image for the MeshChat API package. IPK_DIR=$1 +OSNAME=$2 -# Populate the CONTROL portion of the package -mkdir -p $IPK_DIR/CONTROL -cp -p package/meshchat/* $IPK_DIR/CONTROL/ -sed -i "s%\$GITHUB_SERVER_URL%$GITHUB_SERVER_URL%" $IPK_DIR/CONTROL/control -sed -i "s%\$GITHUB_REPOSITORY%$GITHUB_REPOSITORY%" $IPK_DIR/CONTROL/control # Populate the filesystem image for the package -install -d $IPK_DIR/www/meshchat -install www/* $IPK_DIR/www/meshchat -install -d $IPK_DIR/www/cgi-bin -install -m 755 meshchat $IPK_DIR/www/cgi-bin -install -m 644 meshchatlib.lua $IPK_DIR/www/cgi-bin -install -m 644 meshchatconfig.lua $IPK_DIR/www/cgi-bin -install -D support/meshchatsync-init.d -m 755 $IPK_DIR/etc/init.d/meshchatsync -install -D support/meshchatsync -m 755 $IPK_DIR/usr/local/bin/meshchatsync +if [[ "$OSNAME" == "debian" ]]; then + # Populate the CONTROL portion of the package + mkdir -p $IPK_DIR/DEBIAN + cp -p package/debian/* $IPK_DIR/DEBIAN/ + sed -i "s%\$GITHUB_SERVER_URL%$GITHUB_SERVER_URL%" $IPK_DIR/DEBIAN/control + sed -i "s%\$GITHUB_REPOSITORY%$GITHUB_REPOSITORY%" $IPK_DIR/DEBIAN/control + + install -d $IPK_DIR/var/www/html/meshchat + install www/* $IPK_DIR/var/www/html/meshchat + install -d $IPK_DIR/usr/lib/cgi-bin + install -m 755 meshchat $IPK_DIR/usr/lib/cgi-bin + install -m 644 meshchatlib.lua $IPK_DIR/usr/lib/cgi-bin + install -m 644 meshchatconfig.lua $IPK_DIR/usr/lib/cgi-bin + install -D support/meshchatsync-init.d -m 755 $IPK_DIR/etc/init.d/meshchatsync + install -D support/meshchatsync -m 755 $IPK_DIR/usr/local/bin/meshchatsync + install -d $IPK_DIR/usr/local/lib/lua/5.4/net + install -m 644 lib/json.lua $IPK_DIR/usr/local/lib/lua/5.4/net +elif [[ "$OSNAME" == "openwrt" ]]; then + # Populate the CONTROL portion of the package + mkdir -p $IPK_DIR/CONTROL + cp -p package/openwrt/* $IPK_DIR/CONTROL/ + sed -i "s%\$GITHUB_SERVER_URL%$GITHUB_SERVER_URL%" $IPK_DIR/CONTROL/control + sed -i "s%\$GITHUB_REPOSITORY%$GITHUB_REPOSITORY%" $IPK_DIR/CONTROL/control + + install -d $IPK_DIR/www/meshchat + install www/* $IPK_DIR/www/meshchat + install -d $IPK_DIR/www/cgi-bin + install -m 755 meshchat $IPK_DIR/www/cgi-bin + install -m 644 meshchatlib.lua $IPK_DIR/www/cgi-bin + install -m 644 meshchatconfig.lua $IPK_DIR/www/cgi-bin + install -D support/meshchatsync-init.d -m 755 $IPK_DIR/etc/init.d/meshchatsync + install -D support/meshchatsync -m 755 $IPK_DIR/usr/local/bin/meshchatsync + install -d $IPK_DIR/usr/lib/lua + install -m 644 lib/json.lua $IPK_DIR/usr/lib/lua +fi diff --git a/package/update-version.sh b/package/update-version.sh index 9e169c8..76f8d04 100755 --- a/package/update-version.sh +++ b/package/update-version.sh @@ -39,6 +39,7 @@ echo "${version}" > VERSION echo "Updating code references to version ${version}" sed -i "s/^Version:.*/Version: $version/" $IPK_DIR/CONTROL/control +sed -i "s/^Version:.*/Version: $version/" $IPK_DIR/DEBIAN/control # Update the version in meshchatconfig.lua if present if [[ -f $IPK_DIR/www/cgi-bin/meshchatconfig.lua ]]; then diff --git a/support/meshchatsync b/support/meshchatsync index b10631b..96fb3ec 100755 --- a/support/meshchatsync +++ b/support/meshchatsync @@ -36,42 +36,41 @@ --]] package.path = package.path .. ";/www/cgi-bin/?.lua" -require("nixio") require("meshchatconfig") -require("meshchatlib") +local lib = require("meshchatlib") local sync_status = {} local non_mesh_chat_nodes = {} -local node = node_name() +local node = lib.node_name() -if not nixio.fs.stat(meshchat_path) then - nixio.fs.mkdir(meshchat_path) - nixio.fs.mkdir(local_files_dir) +if not lib.file_exists(meshchat_path) then + lib.mkdir(meshchat_path) + lib.mkdir(local_files_dir) end -if not nixio.fs.stat(messages_db_file) then +if not lib.file_exists(messages_db_file) then io.open(messages_db_file, "w"):close() - nixio.fs.chmod(messages_db_file, "666") + lib.chmod(messages_db_file, "666") end io.open(local_users_status_file, "a"):close() io.open(remote_users_status_file, "a"):close() -save_messages_db_version() +lib.save_messages_db_version() -nixio.fs.chmod(meshchat_path, "666") +lib.chmod(meshchat_path, "666") io.open(lock_file, "a"):close() function log_status() local cur_status = {} - if not nixio.fs.stat(sync_status_file) then + if not lib.file_exists(sync_status_file) then io.open(sync_status_file, "w"):close() end - get_lock() + lib.get_lock() for line in io.lines(sync_status_file) do @@ -94,7 +93,7 @@ function log_status() f:close() end - release_lock() + lib.release_lock() end function merge_messages() @@ -107,7 +106,7 @@ function merge_messages() rmsg[key] = line end - get_lock() + lib.get_lock() for line in io.lines(messages_db_file) do @@ -126,11 +125,11 @@ function merge_messages() f:close() end - sort_and_trim_db() + lib.sort_and_trim_db() - save_messages_db_version() + lib.save_messages_db_version() - release_lock() + lib.release_lock() end function merge_users() @@ -145,7 +144,7 @@ function merge_users() end end - get_lock() + lib.get_lock() for line in io.lines(remote_users_status_file) do @@ -174,12 +173,12 @@ function merge_users() f:close() end - release_lock() + lib.release_lock() end while true do - local nodes = node_list() + local nodes = lib.node_list() sync_status = {} @@ -200,14 +199,14 @@ do port = ":8080" end - local version = get_messages_db_version() + local version = lib.get_messages_db_version() -- Poll non mesh chat nodes at a longer interval if non_mesh_chat_nodes[remote_node] and os.time() < non_mesh_chat_nodes[remote_node] then break end - nixio.fs.remove(meshchat_path .. "/remote_users") + lib.unlink(meshchat_path .. "/remote_users") -- Get remote users file local f = io.popen("/usr/bin/curl --retry 0 --connect-timeout " .. connect_timeout .. " --speed-time " .. speed_time .. " --speed-limit " .. speed_limit .. " -sD - \"http://" .. remote_node .. port .. "/cgi-bin/meshchat?action=users_raw&platform=" .. platform .. "&node=" .. node .. "\" -o " .. meshchat_path .. "/remote_users 2>&1") @@ -222,9 +221,9 @@ do local md5 = output:match("Content%-MD5:%s([0-9a-f]+)\r\n") if md5 then - local f_md5 = file_md5(meshchat_path .. "/remote_users") + local f_md5 = lib.file_md5(meshchat_path .. "/remote_users") if md5 == f_md5 then - local cur_size = nixio.fs.stat(meshchat_path .. "/remote_users").size + local cur_size = lib.file_exists(meshchat_path .. "/remote_users").size if cur_size > 0 then merge_users() end @@ -232,25 +231,25 @@ do end -- Get remote files file - nixio.fs.remove(meshchat_path .. "/remote_files") + lib.unlink(meshchat_path .. "/remote_files") f = io.popen("/usr/bin/curl --retry 0 --connect-timeout " .. connect_timeout .. " --speed-time " .. speed_time .. " --speed-limit " .. speed_limit .. " -sD - \"http://" .. remote_node .. port .. "/cgi-bin/meshchat?action=local_files_raw\" -o " .. meshchat_path .. "/remote_files 2>&1") output = f:read("*a") f:close() md5 = output:match("Content%-MD5:%s([0-9a-f]+)\r\n") if md5 then - local f_md5 = file_md5(meshchat_path .. "/remote_files") - nixio.fs.remove(meshchat_path .. "/remote_files." .. remote_node) + local f_md5 = lib.file_md5(meshchat_path .. "/remote_files") + lib.unlink(meshchat_path .. "/remote_files." .. remote_node) if md5 == f_md5 then - local cur_size = nixio.fs.stat(meshchat_path .. "/remote_files").size + local cur_size = lib.file_exists(meshchat_path .. "/remote_files").size if cur_size > 0 then - nixio.fs.rename(meshchat_path .. "/remote_files", meshchat_path .. "/remote_files." .. remote_node) + lib.rename(meshchat_path .. "/remote_files", meshchat_path .. "/remote_files." .. remote_node) end end end -- Get remote messages - nixio.fs.remove(meshchat_path .. "/remote_messages") + lib.unlink(meshchat_path .. "/remote_messages") f = io.popen("/usr/bin/curl --retry 0 --connect-timeout " .. connect_timeout .. " --speed-time " .. speed_time .. " --speed-limit " .. speed_limit .. " \"http://" .. remote_node .. port .. "/cgi-bin/meshchat?action=messages_version\" -o - 2> /dev/null") local remote_version = f:read("*a") @@ -267,12 +266,12 @@ do local output = f:read("*a") f:close() - if nixio.fs.stat(meshchat_path .. "/remote_messages") then + if lib.file_exists(meshchat_path .. "/remote_messages") then local md5 = output:match("Content%-MD5:%s([0-9a-f]+)\r\n") if md5 then - local f_md5 = file_md5(meshchat_path .. "/remote_messages") + local f_md5 = lib.file_md5(meshchat_path .. "/remote_messages") if md5 == f_md5 then - local cur_size = nixio.fs.stat(meshchat_path .. "/remote_messages").size + local cur_size = lib.file_exists(meshchat_path .. "/remote_messages").size if cur_size > 0 then sync_status[remote_node] = os.time() merge_messages() @@ -285,9 +284,10 @@ do log_status() - nixio.fs.remove(meshchat_path .. "/remote_messages") - nixio.fs.remove(meshchat_path .. "/remote_users") - nixio.fs.remove(meshchat_path .. "/remote_files") + lib.unlink(meshchat_path .. "/remote_messages") + lib.unlink(meshchat_path .. "/remote_users") + lib.unlink(meshchat_path .. "/remote_files") - nixio.nanosleep(poll_interval, 0) + -- sleep for poll_interval seconds + os.execute('sleep ' .. poll_interval .. ' >/dev/null 2>&1') end