diff --git a/_init.lua b/_init.lua new file mode 100644 index 0000000..b6f6bc5 --- /dev/null +++ b/_init.lua @@ -0,0 +1,91 @@ + +-- +-- File: _init.lua +--[[ + This is a template for the LFS equivalent of the SPIFFS init.lua. + It is a good idea to such an _init.lua module to your LFS and do most of the LFS + module related initialisaion in this. This example uses standard Lua features to + simplify the LFS API. + The first section adds a 'LFS' table to _G and uses the __index metamethod to + resolve functions in the LFS, so you can execute the main function of module + 'fred' by executing LFS.fred(params), etc. It also implements some standard + readonly properties: + LFS._time The Unix Timestamp when the luac.cross was executed. This can be + used as a version identifier. + LFS._config This returns a table of useful configuration parameters, hence + print (("0x%6x"):format(LFS._config.lfs_base)) + gives you the parameter to use in the luac.cross -a option. + LFS._list This returns a table of the LFS modules, hence + print(table.concat(LFS._list,'\n')) + gives you a single column listing of all modules in the LFS. +---------------------------------------------------------------------------------]] + +local index = node.flashindex + +local lfs_t = { + __index = function(_, name) + local fn_ut, ba, ma, size, modules = index(name) + if not ba then + return fn_ut + elseif name == '_time' then + return fn_ut + elseif name == '_config' then + local fs_ma, fs_size = file.fscfg() + return {lfs_base = ba, lfs_mapped = ma, lfs_size = size, + fs_mapped = fs_ma, fs_size = fs_size} + elseif name == '_list' then + return modules + else + return nil + end + end, + + __newindex = function(_, name, value) -- luacheck: no unused + error("LFS is readonly. Invalid write to LFS." .. name, 2) + end, + + } + +local G=getfenv() +G.LFS = setmetatable(lfs_t,lfs_t) + +--[[------------------------------------------------------------------------------- + The second section adds the LFS to the require searchlist, so that you can + require a Lua module 'jean' in the LFS by simply doing require "jean". However + note that this is at the search entry following the FS searcher, so if you also + have jean.lc or jean.lua in SPIFFS, then this SPIFFS version will get loaded into + RAM instead of using. (Useful, for development). + See docs/en/lfs.md and the 'loaders' array in app/lua/loadlib.c for more details. +---------------------------------------------------------------------------------]] + +package.loaders[3] = function(module) -- loader_flash + local fn, ba = index(module) + return ba and "Module not in LFS" or fn +end + +--[[------------------------------------------------------------------------------- + You can add any other initialisation here, for example a couple of the globals + are never used, so setting them to nil saves a couple of global entries +---------------------------------------------------------------------------------]] + +G.module = nil -- disable Lua 5.0 style modules to save RAM +package.seeall = nil + +--[[------------------------------------------------------------------------------- + These replaces the builtins loadfile & dofile with ones which preferentially + loads the corresponding module from LFS if present. Flipping the search order + is an exercise left to the reader.- +---------------------------------------------------------------------------------]] + +local lf, df = loadfile, dofile +G.loadfile = function(n) + local mod, ext = n:match("(.*)%.(l[uc]a?)"); + local fn, ba = index(mod) + if ba or (ext ~= 'lc' and ext ~= 'lua') then return lf(n) else return fn end +end + +G.dofile = function(n) + local mod, ext = n:match("(.*)%.(l[uc]a?)"); + local fn, ba = index(mod) + if ba or (ext ~= 'lc' and ext ~= 'lua') then return df(n) else return fn() end +end diff --git a/aps.lua b/aps.lua new file mode 100644 index 0000000..9479f21 --- /dev/null +++ b/aps.lua @@ -0,0 +1,13 @@ +aplist = {} + +wifi.sta.getap( + function(t) + local k, v + local i = 0 + for k,v in pairs(t) do + cprint("cfgsvr", k, v) + aplist[i] = k + i = i + 1 + end + end + ) \ No newline at end of file diff --git a/cfg.lua b/cfg.lua new file mode 100644 index 0000000..e19669f --- /dev/null +++ b/cfg.lua @@ -0,0 +1,10 @@ +cfg = {} +--cfg.Mode = "AP" +cfg.Mode = "Station" +cfg.APServerSSID = "espfilemgr" +cfg.APServerChannel = 6 +cfg.APServerPwd = nil +cfg.APServerIP = "192.168.4.1" +cfg.StationWiFiSSID = "YourWiFiSSID" +cfg.StationWiFiPwd = "password" +cfg.DebugLevel = 1 \ No newline at end of file diff --git a/error404.html b/error404.html new file mode 100644 index 0000000..17bce84 --- /dev/null +++ b/error404.html @@ -0,0 +1,11 @@ + + + + + + + + + File not found (name is case sensitive) + + \ No newline at end of file diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000..b326df9 Binary files /dev/null and b/favicon.ico differ diff --git a/files.html b/files.html new file mode 100644 index 0000000..300466f --- /dev/null +++ b/files.html @@ -0,0 +1,151 @@ + + + + + + + + + + ESP IP or host: + Help

+ + + + + + + +
+ + + + +

+
+ + + + + + + + + + + + + +
+ Files to send: + + If file exists: +
+ + + +
+ + + Progress:
+
+
+
+ +
+ +
+
+ + +     + +

+ + + + + + +
+
+
+
+
+
+

+
+ +
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/files.js b/files.js new file mode 100644 index 0000000..88dcacd --- /dev/null +++ b/files.js @@ -0,0 +1,311 @@ +// This file depends rather heavily on elements within the html file +// that contains it. These dependencies are resolved when script in +// the container calls fileJsInit(). +var CHUNKSIZE = 511 +var xhr; +var xhr2; +var files; +var file; +var reader; +var currentFileNdx = -1; +var currentPos = 0; +var currentChunk = 0; +var buf = []; +var urlPrefix; +var option; +var page; +var isESP; + +// option possibilities are: +// Overwrite : overwrites file automatically +// Ignore : ignores existing file, setting-up failure +// Abort : aborts transfer, fails with message "exists" +// Backup : renames existing file by renaming with prefix "bu_(incremental_number)_" + +// Called by containing page when it loads to initialize element dependencies. +function fileJsInit(obj) { + page = obj; +} + +function sendFiles(fileInputObj) { + files = fileInputObj.files; + currentFileNdx = -1; + xhr = new XMLHttpRequest(); + option = page.optionsListElement.value; + urlPrefix = location.protocol + "//" + page.serverElement.value; + sendNextFile(); +} + +function sendNextFile() { + currentFileNdx++; + file = files[currentFileNdx]; + if (file == null) + return false; + currentChunk = 0; + buf = []; + reader = file.stream().getReader(); + xhr.onreadystatechange = xhrReadyStateChange; + xhr.open("GET", urlPrefix + "/api/send/" + file.name + "/" + option, true); + xhr.send(); +} + +function xhrReadyStateChange() { + if (xhr.readyState == 4) { + var freeHeap = xhr.getResponseHeader("FreeHeap"); + if (freeHeap) + page.heapElement.innerText = freeHeap; + + if (xhr.status > 299) { + alert("HTTP status " + xhr.status.toString() + " " + xhr.statusText); + return; + } + if ((xhr.responseText.indexOf("success") < 0) && (xhr.status != 100)) { + alert(xhr.responseText); + return; + } + + var respObj = JSON.parse(xhr.responseText); + isESP = (xhr.getResponseHeader("Server") == "ESPServer"); + + // buf is initialized to an empty array in sendNextFile() + // so it works the first time this function is called + if (currentPos >= buf.length) { + var promise = reader.read(); + if (promise == undefined) + return; + promise.then(function (value) { + if ((value == undefined) || (value.value == undefined)) { + xhr.onreadystatechange = null; + xhr.open("GET", urlPrefix + "/api/persist/" + file.name + "/" + option, false); + xhr.send(); + setTimeout(sendNextFile, 50); + getFileList(); + return; + } + if (buf.length) + currentChunk++; + buf = value.value; + currentPos = 0; + }) + } + + if (respObj.bytes < file.size) { + var temp = buf.slice(currentPos, currentPos + CHUNKSIZE); + currentPos += temp.length; + + xhr.open("POST", urlPrefix + "/api/append/" + file.name + "/" + option, true); + if (isESP) { + xhr.send(temp.buffer); + } + else { + var obj = createPostJson(file.name, temp); + xhr.setRequestHeader("Content-Type", "application/json"); + xhr.send(JSON.stringify(obj)); + } + } + if (page.statusElement) { + var bytesTransferred = (currentChunk * 65536) + currentPos; + var percentComplete = parseInt((bytesTransferred / file.size) * 100); + page.statusElement.value = "File: " + (currentFileNdx + 1).toString() + " of " + files.length.toString() + " " + + bytesTransferred.toString() + " bytes sent"; + page.progressElement.style.width = percentComplete.toString() + "%"; + page.progressElement.innerText = percentComplete.toString() + "%"; + } + + } +} + +function createPostJson(fileName, data) { + return { + fileName: fileName, + data: data + } +} + + +function sendCfg() { + var xhrCfg = new XMLHttpRequest(); + var cfgBuf = page.configElement.value; + urlPrefix = location.protocol + "//" + page.serverElement.value; + xhrCfg.onreadystatechange = null; + xhrCfg.open("GET", urlPrefix + "/api/send/cfg.lua/Overwrite", false); + xhrCfg.send(); + xhrCfg.open("POST", urlPrefix + "/api/append/cfg.lua/Overwrite", false); + xhrCfg.send(cfgBuf); + xhrCfg.open("GET", urlPrefix + "/api/persist/cfg.lua/Overwrite", false); + xhrCfg.send(); + page.configDiv.style.display = "none"; +} + +function getCfg() { + page.configDiv.style.display = "block"; + var xhrCfg = new XMLHttpRequest(); + urlPrefix = location.protocol + "//" + page.serverElement.value; + xhrCfg.onreadystatechange = null; + xhrCfg.open("GET", urlPrefix + "/cfg.lua", false); + xhrCfg.send(); + page.configElement.value = xhrCfg.responseText; +} + + +function getFileList() { + urlPrefix = location.protocol + "//" + page.serverElement.value; + xhr2 = new XMLHttpRequest(); + xhr2.onreadystatechange = xhrReadyStateChange2; + xhr2.open("GET", urlPrefix + "/api/list", true); + xhr2.send(); +} + +function restartESP() { + urlPrefix = location.protocol + "//" + page.serverElement.value; + xhr2 = new XMLHttpRequest(); + xhr2.open("GET", urlPrefix + "/api/restart", false); + xhr2.send(); +} + +function getheap() { + if (page) { + urlPrefix = location.protocol + "//" + page.serverElement.value; + xhr2 = new XMLHttpRequest(); + xhr2.onreadystatechange = null; + xhr2.open("GET", urlPrefix + "/api/heap", false); + xhr2.send(); + page.heapElement.innerText = xhr2.responseText; + } +} + +function updateHeap() { + window.setTimeout(updateHeap, 60000); + getheap(); +} + +//updateHeap(); + +function xhrReadyStateChange2() { + if (xhr2.readyState == 4) { + var freeHeap = xhr2.getResponseHeader("FreeHeap"); + if (freeHeap) + page.heapElement.innerText = freeHeap; + + var list = JSON.parse(xhr2.responseText); + for (i = page.fileListElement.childNodes.length - 1; i >= 0; i--) { + page.fileListElement.removeChild(page.fileListElement.childNodes[i]); + } + var tr = document.createElement("tr"); + tr = page.fileListElement.appendChild(tr); + var th = document.createElement("th"); + th = tr.appendChild(th); + th.innerText = "File Name"; + th = document.createElement("th"); + th = tr.appendChild(th); + th.innerText = "Size"; + + // enumerate returned objects + for (i = 0; i < list.length; i++) { + var item = list[i]; + + tr = document.createElement("tr"); + tr = page.fileListElement.appendChild(tr); + var td = document.createElement("td"); + td = tr.appendChild(td); + var a = document.createElement("a"); + a.href = item.name; + a.target = "_blank"; + a.innerText = item.name + td.appendChild(a); + td = document.createElement("td"); + td.align = "right"; + td = tr.appendChild(td); + td.innerText = item.size; + } + populateFilesDropdown(list); + } +} + +function populateFilesDropdown(list) { + for (i = page.fileDropdownElement.childNodes.length - 1; i >= 0; i--) { + page.fileDropdownElement.removeChild(page.fileDropdownElement.childNodes[i]); + } + + // enumerate returned objects + for (i = 0; i < list.length; i++) { + var item = list[i]; + var optionEl = document.createElement("option"); + optionEl.value = item.name + optionEl.innerText = item.name + optionEl = page.fileDropdownElement.appendChild(optionEl); + } +} + +function renameSelectedFile(that) { + urlPrefix = location.protocol + "//" + page.serverElement.value; + option = page.optionsListElement.value; + var oldName = page.fileDropdownElement.value; + if (page.newNameDivElement.style.display == "none") { + page.newNameDivElement.style.display = "block"; + page.cancelFileOpBtnElement.style.display = "inline"; + page.deleteFileBtnElement.style.display = "none"; + that.innerText = "Confirm Rename"; + return; + } + else { + that.innerText = "Rename Selected File"; + page.cancelFileOpBtnElement.style.display = "none"; + page.deleteFileBtnElement.style.display = "inline"; + } + var newName = page.newFileNameInputElement.value; + if (newName == "") { + page.newNameDivElement.style.display = "none"; + return; + } + xhr3 = new XMLHttpRequest(); + xhr3.open("GET", urlPrefix + "/api/rename/" + oldName + "/" + newName + "/" + option, false); + xhr3.send(); + page.newNameDivElement.style.display = "none"; + page.newFileNameInputElement.value = ""; + getFileList(); +} + +function deleteSelectedFile(that) { + option = page.optionsListElement.value; + urlPrefix = location.protocol + "//" + page.serverElement.value; + var name = page.fileDropdownElement.value; + + if (page.deleteConfirmDivElement.style.display == "none") { + page.deleteConfirmDivElement.style.display = "block"; + page.cancelFileOpBtnElement.style.display = "inline"; + page.renameFileBtnElement.style.display = "none"; + that.innerText = "Confirm Delete"; + return; + } + else { + that.innerText = "Delete Selected File"; + page.cancelFileOpBtnElement.style.display = "none"; + page.renameFileBtnElement.style.display = "inline"; + } + + var appFilesList = ["error404.html", "files.js", "server.lua", "wificfgsvr.lua", "files.html", "help.html", "favicon.ico", "_init.lua", "index.html", "cfg.lua", "init.lua"]; + if (appFilesList.indexOf(name) >= 0) { + if (!confirm("The file " + name + " is part of the FileMgr application, deleting it may break the app.\r\n\r\nAre you sure?")) { + page.deleteConfirmDivElement.style.display = "none"; + return; + } + } + + xhr3 = new XMLHttpRequest(); + xhr3.open("GET", urlPrefix + "/api/delete/" + name + "/" + option, false); + xhr3.send(); + getFileList(); + page.deleteConfirmDivElement.style.display = "none"; +} + +function cancelDeleteRename(that) { + page.cancelFileOpBtnElement.style.display = "none"; + page.deleteFileBtnElement.innerText = "Delete Selected File"; + page.deleteFileBtnElement.style.display = "inline"; + page.renameFileBtnElement.innerText = "Rename Selected File"; + page.renameFileBtnElement.style.display = "inline"; + page.newNameDivElement.style.display = "none"; + page.deleteConfirmDivElement.style.display = "none"; +} + diff --git a/fileupload.lua b/fileupload.lua new file mode 100644 index 0000000..ca1cfe6 --- /dev/null +++ b/fileupload.lua @@ -0,0 +1,75 @@ +-- {version: "1.0.0"} +local module =... + return function(conn, pname) + CHUNKSIZE = 256 + MAXNAMELEN = 18 + local buf + local fname = string.sub(pname, 1, MAXNAMELEN) + tmr.wdclr() + if file.exists(fname) then + s, e = string.find(string.reverse(fname), ".", 1, true) + local ext = string.lower(string.sub(fname, -(s - 1))) + cprint(ext, 1) + local contentType + + if (ext == "html" or ext == "htm" or ext == "js" or ext == "json" or ext == "txt") then + contentType = contentTypeHtml + elseif (ext == "jpg" or ext == "png" or ext == "bmp" or ext == "ico" or ext == "gif" or ext == "jpeg") then + contentType = contentTypeImage + else + contentType = contentTypeBin + end + + buf = "HTTP/1.1 200 OK" .. contentType .. headerBlock + else + fname = "error404.html" + buf = "HTTP/1.1 404 FILE NOT FOUND" .. contentTypeHtml .. headerBlock + end + + local function unloadModule() + if module ~= nil then + package.loaded[module] = nil + cprint("unloading fileupload", 0) + end + module = nil + end + + conn:on ("sent", + function(sck) + function sendfile(sck) + local f = getFileObject(sck, fname, "r") + buf = f:read(CHUNKSIZE) + if buf ~= nil then + cprint("sent " .. #buf .. " bytes, heap: " .. node.heap(), 4) + sck:send(buf) + else + closeFileObject(sck, fname) + cprint("file:read returned nil, closed file heap: " .. node.heap(), 2) + sck:close() + unloadModule() + return + end + end + sck:on("sent", sendfile) + sck:on("disconnection", + function(sck) + cprint("disconnection fileupload.sendfile, heap: " .. node.heap(), 0) + closeFileObject(sck, fname) + unloadModule() + return + end + ) + sendfile(sck) + end + ) + conn:on ("receive", + function(sck, pl) + cprint("received data closing connection, heap: " .. node.heap(), 0) + sck:close() + end + ) + if buf == nil then + buf = "" + end + conn:send(buf) + end \ No newline at end of file diff --git a/help.html b/help.html new file mode 100644 index 0000000..7fd799f --- /dev/null +++ b/help.html @@ -0,0 +1,208 @@ + + + + + + + + +

ESP File Manager for NodeMCU

+

(Developed using NodeMCU 3.0-master, complete build details below)

+

+ This app allows files of any size or type to be uploaded to and downloaded from an ESP-8266, + using any browser [within reason.]. +

+ +

+ When uploading a file, if a file of that name already exists, behavior is dictated by the "if file exists..." + pulldown: +

+ + +

Capabilities:

+ + +

Limitations:

+ + +

Setup:

+ + +

Configuration:

+ + + +

Usage:

+ + + +

To determine which IP was assigned to the ESP by your network:

+ + +

+ If you want to incorporate this functionality in another app, and you have already configured/connected to a network, + copy all of the code from init.lua except for the last two lines, to your init file. You will need to assign either the variable ip or serverip, + depending on mode. Then when you want this functionality to be available execute server.lua, e.g., dofile("server.lua") To disable the + functionality and recover the memory used by server.lua, reset the ESP. +

+ +

LFS:

+ + een, doeseen, does +

API Documentation

+ + +

Build details:

+
+NodeMCU 3.0.0.0 built on nodemcu-build.com provided by frightanic.com
+	branch: master
+	commit: 310faf7fcc9130a296f7f17021d48c6d717f5fb6
+	release: 3.0-master_20190907
+	release DTS: 201909070945
+	SSL: false
+	build type: integer
+	LFS: 0x0
+	modules: file,gpio,net,node,tmr,uart,wifi
+build 2019-12-04 04:30 powered by Lua 5.1.4 on SDK 3.0.1-dev(fce080e)
+    
+ + diff --git a/index.html b/index.html new file mode 100644 index 0000000..d3bb488 --- /dev/null +++ b/index.html @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..f609356 --- /dev/null +++ b/init.lua @@ -0,0 +1,27 @@ +--,init,lua,1,0,1,0 + +dofile("_init.lua") + +dofile("cfg.lua") + +-- Last argument MUST be numeric, debug level +function cprint(...) + local arglist = {...} + local level = arglist[#arglist] + if level <= cfg.DebugLevel then + print(...) + end +end + +ip = nil +serverip = nil +initStage = 0 +failureCnt = 0 +versions = nil + +--aplist = {} +cfg.APMac = wifi.ap.getmac() +tmrSvrCfg = tmr.create() +tmrSvrCfg:alarm(4000, tmr.ALARM_AUTO, require("wificfgsvr")) + + diff --git a/lfs.img b/lfs.img new file mode 100644 index 0000000..c967fd5 Binary files /dev/null and b/lfs.img differ diff --git a/server.lua b/server.lua new file mode 100644 index 0000000..eaef63d --- /dev/null +++ b/server.lua @@ -0,0 +1,298 @@ +-- {"version": "1.0.0"} +contentTypeHtml = "\r\nContent-type: text/html" +contentTypeBin = "\r\nContent-type: application/octet-stream" +contentTypeImage = "\r\nContent-type: image/jpg" +headerBlock = "\r\nConnection: close\r\nServer: ESPServer\r\nAccess-Control-Allow-Origin: *\r\nCache-Control: no-cache\r\n\r\n" +local currentFileName = "" +local isPostData = false +local retval = "" +local success = "{ \"status\": \"success\", \"bytes\": " +local option +local isResetting = false +print("filexfer server") +activeClients = {} + +function getFreeHeapHeader() + return "\r\nFreeHeap: " .. node.heap() +end + +function getFileObject(sck, fileName, mode) + local port, ip = sck:getpeer() + if (activeClients[ip] == nil) then + activeClients[ip] = {} + cprint("creating active client for " .. ip, 2) + end + if (activeClients[ip][fileName] == nil) then + activeClients[ip][fileName] = file.open(fileName, mode) + cprint("opening file handle " .. fileName, 3) + end + return activeClients[ip][fileName] +end + +function closeFileObject(sck, fileName) + local port, ip = sck:getpeer() + if (activeClients[ip] ~= nil) then + if (activeClients[ip][fileName] ~= nil) then + activeClients[ip][fileName]:close() + activeClients[ip][fileName] = nil + cprint("closing file handle " .. fileName, 3) + end + end +end + +local srv=net.createServer(net.TCP, 60) +srv:listen(80, + function(conn) + assignHandlers(conn) + end +) + +function assignHandlers(conn) + conn:on("disconnection", disconnection) + conn:on("sent", sent) + conn:on("receive", receive) +end + +local function getfilesize(name) + local stat = file.stat(name) + local tempbuf = "" + if stat then + tempbuf = stat.size + cprint(name .. " " .. stat.size, 2) + end + return tempbuf +end + +local function writefile(sck, name, mode, data) + local f = getFileObject(sck, "t_" .. name, mode) + if (f == nil) then + return -1 + end + f:write(data) + closeFileObject(sck, "t_" .. name) + f = nil + return success.. getfilesize("t_" .. name).. "}" +end + +local function processOption(name, opt, isComplete) + if (#name > 18) then + return false + end + if (file.exists(name) == false) then + retval = success .. "}" + return true + end + + if (opt == "Backup") then + if (isComplete == true) then + s, e = string.find(string.reverse(name), ".", 1, true) + local ext = string.sub(name, -s) + local buName = string.sub(name, 1, -(s + 1)) .. "(1)" .. ext + cprint(buName, 2) + + if (file.exists(buName)) then + file.remove(buName) + end + file.rename(name, buName) + end + retval = success .. getfilesize(name) .. "}" + return true + end + if (opt == "Overwrite") then + if (isComplete == true) then + file.remove(name) + end + retval = success .. "}" + return true + end + if (opt == "Ignore") then + retval = success .. "}" + return true + end + if (opt == "Abort") then + retval = "\"error file exists\"}" + return false + end +end + +function disconnection(conn) + cprint("disconnection", 1) + isPostData = false +end + +function sent(conn) + if (isPostData ~= true) then + currentFileName = "" + isPostData = false + conn:close() + cprint("onsent closing connection", 1) + end +end + +function receive(conn, payload) + tmr.wdclr(); + local s, e, m, buf, k, v + local tbl = {} + local i = 1 + local method + + s, e = string.find(payload, "HTTP", 1, true) + if (isPostData and (e == nil)) then + retval = writefile(conn, currentFileName, "a+", payload) + cprint("ispostdata raw data" .. #payload, 4) + payload = nil + --isPostData = false + cprint("sending status 100", 4) + buf = "HTTP/1.1 100 CONTINUE" .. contentTypeHtml .. getFreeHeapHeader() .. headerBlock + conn:send(buf) + return + else + if e ~= nil then + buf = string.sub(payload, 1, s - 2) + for m in string.gmatch(buf, "/?([%w+%p+][^/+]*)") do + tbl[i] = m + cprint(i .. " " .. m, 5) + i = i + 1 + end + m = nil + method = tbl[1] + cprint(#tbl .. " " .. method, 1) + + if tbl[2] == "api" then + local cmd = tbl[3] + if (tbl[4] ~= nil) and (tbl[4] ~= "/") then + currentFileName = tbl[4] + end + if (tbl[5] ~= nil) then + option = tbl[5] + end + -- option is always the last parameter, + -- for rename it will be at index 5 + if (tbl[6] ~= nil) then + option = tbl[6] + end + + cprint("cmd " .. cmd, 1) + if (cmd == "restart") then + retval = "apparent failure" + isResetting = true + node.restart() + return + end + + if (cmd == "heap") then + retval = node.heap() + end + + if (cmd == "dofile") then + retval = dofile(currentFileName) + end + + if (cmd == "send") then + if (#currentFileName > 18) then + buf = "HTTP/1.1 409 INVALID FILE NAME" .. contentTypeHtml .. getFreeHeapHeader() .. headerBlock + conn:send(buf) + return + end + if (processOption(currentFileName, option, false)) then + retval = writefile(conn, currentFileName, "w+", "") + cprint(retval, 2) + else + buf = "HTTP/1.1 403 FILE EXISTS" .. contentTypeHtml .. getFreeHeapHeader() .. headerBlock + conn:send(buf) + return + end + end + + if (cmd == "append") then + s, e = string.find(payload, "\r\n\r\n", 1, true) + cprint("payload length " .. #payload, 4) + isPostData = true + if e ~= nil then + buf = string.sub(payload, s + 4) + cprint("data length " .. #buf .. " " .. s .. ' ' .. e, 5) + if #buf > 0 then + retval = writefile(conn, currentFileName, "a+", buf) + cprint(retval, 3) + else + isPostData = false + end + --else + -- retval = writefile(conn, currentFileName, "a+", payload) + end + end + + if (cmd == "persist") then + if (processOption(currentFileName, option, true)) then + file.rename("t_" .. currentFileName, currentFileName) + retval = success.. getfilesize(currentFileName).. "}" + end + end + if (cmd == "rename") then + if (processOption(tbl[5], option, true)) then + file.rename(currentFileName, tbl[5]) + end + end + + if (cmd == "delete") then + file.remove(currentFileName) + end + + if (cmd == "list") then + -- get list of files and send to client + local listBuf = "[{\"name\":\".\",\"size\": 0}" + l = file.list(); + for k,v in pairs(l) do + listBuf = listBuf..",{\"name\":\""..k.."\",\"size\":"..v.."}" + end + listBuf = listBuf.."]" + + buf = "HTTP/1.1 200 OK" .. contentTypeHtml .. getFreeHeapHeader() .. headerBlock .. listBuf + conn:send(buf) + payload = nil + tbl = nil + l = nil + listBuf = nil + return + end + + if (cmd == "version") then + local f = getFileObject(conn, currentFileName, "r") + f:seek("set", 2) + buf = "HTTP/1.1 200 OK" .. contentTypeHtml .. getFreeHeapHeader() .. headerBlock .. f:readline() + closeFileObject(conn, currentFileName) + f = nil + conn:send(buf) + payload = nil + tbl = nil + return + end + + buf = "" + if retval == nil then + retval = "[nil]" + end + else + -- if no command was present the client wants to download an existing file. + -- default document name is hard-coded in the line below + local filename = "index.html" + if tbl[2] ~= nil and tbl[2] ~= "/" then + filename = tbl[2] + end + closeFileObject(conn, filename) + cprint("calling upload " .. filename, 1) + require("fileupload")(conn, filename) + buf = nil + payload = nil + tbl = nil + return + end + end + end + buf = "HTTP/1.1 200 OK" .. contentTypeHtml.. getFreeHeapHeader() .. headerBlock .. retval + payload = nil + tbl = nil + if isResetting == false then + conn:send(buf) + end +end \ No newline at end of file diff --git a/wificfgsvr.lua b/wificfgsvr.lua new file mode 100644 index 0000000..58952bd --- /dev/null +++ b/wificfgsvr.lua @@ -0,0 +1,128 @@ +-- {"version": "1.0.0"} +local module =... + return function() + cprint("[wificfgsvr.lua]", initStage, 1) + ----cprint(node.heap(), 2) + if (initStage == 0) then + --cprint("[wifi.setmode]", 3) + wifi.setmode(wifi.STATIONAP) + if (cfg.Mode == "AP") then + nextCfgStep = 5 + else + nextCfgStep = 1 + end + initStage = nextCfgStep + return + end + if (initStage == 1) then + if cfg.WiFiPwd == nil then + cfg.WiFiPwd = "" + end + local staconfig = {} + staconfig.ssid = cfg.StationWiFiSSID + staconfig.pwd = cfg.StationWiFiPwd + wifi.sta.config(staconfig) + initStage = 2 + return + end + if (initStage == 2) then + --cprint("[wifi.connect]", 3) + wifi.sta.connect() + initStage = 3 + return + end + if (initStage == 3) then + ip = wifi.sta.getip() + initStage = 4 + return + end + + if initStage == 4 then + if ip ~= nil then + initStage = 5 + cprint(ip, 0) + tmrSvrCfg:unregister() + node.task.post(function() + dofile("server.lua") + end) + return + else + --cprint("[no sta ip]", 5) + initStage = 1 + failureCnt = failureCnt + 1 + if failureCnt > 10 then + node.restart() + end + return + end + end + + if initStage == 5 then + --cprint("[AP config]", 5) + local wificfg={ + ssid = cfg.APServerSSID, + auth = AUTH_OPEN, + channel = cfg.APServerChannel} + + if cfg.APServerPwd ~= nil then + if #cfg.APServerPwd >= 10 then + wificfg.pwd = cfg.APServerPwd + end + end + wifi.ap.config(wificfg) + initStage = 6 + wificfg = nil + return + end + + if initStage == 6 then + --cprint("[server ip]", 5) + local ipcfg = { + ip=cfg.APServerIP, + netmask="255.255.255.0", + gateway=cfg.APServerIP} + wifi.ap.setip(ipcfg) + initStage = 7 + return + end + + if initStage == 7 then + serverip= wifi.ap.getip() + cprint("[server ip]: ",serverip, 1) + if serverip ~= nil then + tmrSvrCfg:unregister() + initStage = 0 + cprint("[ready to connect]", node.heap(), 1) + + failureCnt = 0 + node.task.post(function() + dofile("server.lua") + end) + if module then + package.loaded[module] = nil + module = nil + end + + return + + -- wifi.sta.getap( + -- function(t) + -- local k, v + -- local i = 0 + -- for k,v in pairs(t) do + -- cprint("cfgsvr", k, v, 5) + -- aplist[i] = k + -- i = i + 1 + -- end + -- end + -- ) + else + initStage = 5 + failureCnt = failureCnt + 1 + if failureCnt > 10 then + node.restart() + end + --cprint("no server ip", 5) + end + end + end