Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
Nodemcu Firmware
Commits
8d091c47
Commit
8d091c47
authored
Jun 03, 2020
by
Marcel Stör
Browse files
Make the telnet example an Lua module (#3133)
Also update ftp server
parent
f20591a8
Changes
6
Show whitespace changes
Inline
Side-by-side
lua_examp
les/telnet
/README
.md
→
docs/lua-modu
les/telnet.md
View file @
8d091c47
...
...
@@ -2,21 +2,15 @@
| Since | Origin / Contributor | Maintainer | Source |
| :----- | :-------------------- | :---------- | :------ |
| 2014-12-22 |
[
Zeroday
](
https://github.com/funshine
)
|
[
Terry Ellison
](
https://github.com/TerryE
)
|
[
simple_telnet.lua
](
./simple_telnet.lua
)
|
| 2018-05-24 |
[
Terry Ellison
](
https://github.com/TerryE
)
|
[
Terry Ellison
](
https://github.com/TerryE
)
|
[
telnet.lua
](
./telnet.lua
)
|
| 2018-05-24 |
[
Terry Ellison
](
https://github.com/TerryE
)
|
[
Terry Ellison
](
https://github.com/TerryE
)
|
[
telnet.lua
](
../../lua_modules/telnet/telnet.lua
)
|
This README discusses the packet marshalling versions of telnet. The first (fifosock)
version was written for SDK 2 implementations, with all of the marshalling imlemented
in Lua; the second (pipe) version uses the latest features added to the SDK 3 version
that have been added to prepare for the
`lua53`
implementation. These exploit the
stdin / stdout pipe functionality and task integration that is now build into the
NodeNMCU Lua core.
The current version of this module exploits the stdin / stdout pipe functionality and
task integration that is now build into the NodeNMCU Lua core.
There are two nice advantages of this core implementation:
-
Errors are now written to stdout in a s
p
earate task execution.
-
The pipes pretty much eliminate
uart
and telnet overrun.
-
Errors are now written to stdout in a se
p
arate task execution.
-
The pipes pretty much eliminate
UART
and telnet overrun.
Both have the same interface if required into the variable
`telnet`
...
...
@@ -31,13 +25,13 @@ Open a telnet server based on the provided parameters.
#### Parameters
`ssid`
and
`password`
. Strings. SSID and Password for the Wifi network. If these are
`nil`
then the wifi is assumed to be configured or autoconfigured.
`nil`
then the wifi is assumed to be configured or auto
-
configured.
`port`
. Integer TCP listen
t
ing port for the Telnet service. The default is 2323
`port`
. Integer TCP listening port for the Telnet service. The default is 2323
#### Returns
Nothing returned (this is evaluted as
`nil`
in a scalar context).
Nothing returned (this is evalu
a
ted as
`nil`
in a scalar context).
## telnet:close()
...
...
@@ -53,4 +47,4 @@ None
#### Returns
Nothing returned (this is evaluted as
`nil`
in a scalar context).
Nothing returned (this is evalu
a
ted as
`nil`
in a scalar context).
lua_examples/telnet/telnet_fifosock.lua
deleted
100644 → 0
View file @
f20591a8
--[[ A telnet server T. Ellison, May 2018
This version is more complex than the simple Lua example previously provided in our
distro. The main reason is that a single Lua command can produce a LOT of output,
and the server has to work within four constraints:
- The SDK rules are that you can only issue one send per task invocation, so any
overflow must be buffered, and the buffer emptied using an on:sent cb
- Since the interpeter invokes a node.output cb per field, you have a double
whammy that these fields are typically small, so using a simple array FIFO
would rapidly exhaust RAM.
- For network efficiency, you want to aggregate any FIFO buffered into sensible
sized packet, say 1024 bytes, but you also need to handle the case when larger
string span multiple packets. However, you must flush the buffer if necessary.
- The overall buffering strategy needs to be reasonably memory efficient and avoid
hitting the GC too hard, so where practical avoid aggregating small strings to
more than 256 chars (as NodeMCU handles <256 using stack buffers), and avoid
serial aggregation such as buf = buf .. str as this hammers the GC.
So this server adopts a simple buffering scheme using a two level FIFO. The node.output
cb adds cb records to the 1st level FIFO until the #recs is > 32 or the total size
would exceed 256 bytes. Once over this threashold, the contents of the FIFO are
concatenated into a 2nd level FIFO entry of upto 256 bytes, and the 1st level FIFO
cleared down to any residue.
]]
--luacheck: no unused args
local
node
,
tmr
,
wifi
,
uwrite
=
node
,
tmr
,
wifi
,
uart
.
write
local
function
telnet_listener
(
socket
)
local
queueLine
=
(
require
"fifosock"
).
wrap
(
socket
)
local
function
receiveLine
(
s
,
line
)
node
.
input
(
line
)
end
local
function
disconnect
(
s
)
socket
:
on
(
"disconnection"
,
nil
)
socket
:
on
(
"reconnection"
,
nil
)
socket
:
on
(
"connection"
,
nil
)
socket
:
on
(
"receive"
,
nil
)
socket
:
on
(
"sent"
,
nil
)
node
.
output
(
nil
)
end
socket
:
on
(
"receive"
,
receiveLine
)
socket
:
on
(
"disconnection"
,
disconnect
)
node
.
output
(
queueLine
,
0
)
print
((
"Welcome to NodeMCU world (%d mem free, %s)"
):
format
(
node
.
heap
(),
wifi
.
sta
.
getip
()))
end
local
listenerSocket
return
{
open
=
function
(
this
,
ssid
,
pwd
,
port
)
if
ssid
then
wifi
.
setmode
(
wifi
.
STATION
,
false
)
wifi
.
sta
.
config
{
ssid
=
ssid
,
pwd
=
pwd
,
save
=
false
}
end
local
t
=
tmr
.
create
()
t
:
alarm
(
500
,
tmr
.
ALARM_AUTO
,
function
()
if
(
wifi
.
sta
.
status
()
==
wifi
.
STA_GOTIP
)
then
t
:
unregister
()
t
=
nil
print
((
"Telnet server started (%d mem free, %s)"
):
format
(
node
.
heap
(),
wifi
.
sta
.
getip
()))
net
.
createServer
(
net
.
TCP
,
180
):
listen
(
port
or
23
,
telnet_listener
)
else
uwrite
(
0
,
"."
)
end
end
)
end
,
close
=
function
(
this
)
if
listenerSocket
then
listenerSocket
:
close
()
package.loaded
.
telnet
=
nil
listenerSocket
=
nil
collectgarbage
()
end
end
,
}
lua_modules/ftp/ftpserver.lua
View file @
8d091c47
--[[SPLIT MODULE ftp]]
--[[ A simple ftp server
This is my implementation of a FTP server using Github user Neronix's
example as inspriration, but as a cleaner Lua implementation that
has been
optimised
for use in LFS. The coding style adopted here is more similar to
example as inspriration, but as a cleaner Lua implementation that
is
suitable
for use in LFS. The coding style adopted here is more similar to
best practice for normal (PC) module implementations, as using LFS enables
me to bias towards clarity of coding over brevity. It includes extra logic
to handle some of the edge case issues more robustly. It also uses a
...
...
@@ -13,113 +15,116 @@
with any multiple calls requected, so FTP is a singleton static object.
However there is nothing to stop multiple clients connecting to the FTP
listener at the same time, and indeed some FTP clients do use multiple
connections, so this server can accept and create multiple
CON
objects.
Each
CON
object can also have a single DATA connection.
connections, so this server can accept and create multiple
cxt
objects.
Each
cxt
object can also have a single DATA connection.
Note that FTP also exposes a number of really private properties (which
could be stores in local / upvals) as FTP properties for debug purposes.
Note that this version has now been updated to allow the main methods to
be optionally loaded lazily, and SPILT comments allow the source to be
preprocessed for loading as either components in the "fast" Cmodule or as
LC files in SPIFFS.
]]
local
file
,
net
,
wifi
,
node
,
table
,
tmr
,
pairs
,
print
,
pcall
,
tostring
=
file
,
net
,
wifi
,
node
,
table
,
tmr
,
pairs
,
print
,
pcall
,
tostring
local
post
=
node
.
task
.
post
local
FTP
,
cnt
=
{
client
=
{}},
0
-- Local functions
local
processCommand
-- function(cxt, sock, data)
local
processBareCmds
-- function(cxt, cmd)
local
processSimpleCmds
-- function(cxt, cmd, arg)
local
processDataCmds
-- function(cxt, cmd, arg)
local
dataServer
-- function(cxt, n)
local
ftpDataOpen
-- function(dataSocket)
-- Note these routines all used hoisted locals such as table and debug as
-- upvals for performance (ROTable lookup is slow on NodeMCU Lua), but
-- data upvals (e.g. FTP) are explicitly list is -- "upval:" comments.
-- Note that the space between debug and the arglist is there for a reason
-- so that a simple global edit " debug(" -> "-- debug(" or v.v. to
-- toggle debug compiled into the module.
local
function
debug
(
fmt
,
...
)
-- upval: cnt (, print, node, tmr)
if
not
FTP
.
debug
then
return
end
if
(
...
)
then
fmt
=
fmt
:
format
(
...
)
end
print
(
node
.
heap
(),
fmt
)
cnt
=
cnt
+
1
if
cnt
%
10
then
tmr
.
wdclr
()
end
--luacheck: read globals fast file net node tmr uart wifi FAST_ftp SPIFFS_ftp
local
FTP
,
FTPindex
=
{
client
=
{}},
nil
if
FAST_ftp
then
function
FTPindex
(
_
,
name
)
return
fast
.
load
(
'ftp-'
..
name
)
end
elseif
SPIFFS_ftp
then
function
FTPindex
(
_
,
name
)
return
loadfile
(
'ftp-'
..
name
..
'.lc'
)
end
end
if
FTPindex
then
return
setmetatable
(
FTP
,{
__index
=
FTPindex
})
end
function
FTP
.
open
(
...
)
--[[SPLIT HERE ftp-open]]
--------------------------- Set up the FTP object ----------------------------
-- FTP has three static methods: open, createServer and close
------------------------------------------------------------------------------
-- optional wrapper around createServer() which also starts the wifi session
function
FTP
.
open
(
user
,
pass
,
ssid
,
pwd
,
dbgFlag
)
-- upval: FTP (, wifi, tmr, print)
-- Lua: FTP:open(user, pass, ssid, pwd[, dbgFlag])
local
this
,
user
,
pass
,
ssid
,
pwd
,
dbgFlag
=
...
if
ssid
then
wifi
.
setmode
(
wifi
.
STATION
,
false
)
wifi
.
sta
.
config
{
ssid
=
ssid
,
pwd
=
pwd
,
save
=
false
}
end
local
t
=
tmr
.
create
()
t
:
alarm
(
500
,
tmr
.
ALARM_AUTO
,
function
()
tmr
.
create
():
alarm
(
500
,
tmr
.
ALARM_AUTO
,
function
(
t
)
-- this: FTP, user, pass, dbgFlag
if
(
wifi
.
sta
.
status
()
==
wifi
.
STA_GOTIP
)
then
t
:
unregister
()
t
=
nil
print
(
"Welcome to NodeMCU world"
,
node
.
heap
(),
wifi
.
sta
.
getip
())
return
FTP
.
createServer
(
user
,
pass
,
dbgFlag
)
return
this
:
createServer
(
user
,
pass
,
dbgFlag
)
else
uart
.
write
(
0
,
"."
)
end
end
)
end
end
--[[SPLIT IGNORE]]
function
FTP
.
createServer
(
...
)
--[[SPLIT HERE ftp-createServer]]
-- Lua: FTP:createServer(user, pass[, dbgFlag])
local
this
,
user
,
pass
,
dbgFlag
=
...
local
cnt
=
0
this
.
user
,
this
.
pass
,
dbgFlag
=
user
,
pass
,
(
dbgFlag
and
true
or
false
)
function
FTP
.
createServer
(
user
,
pass
,
dbgFlag
)
-- upval: FTP (, debug, tostring, pcall, type, processCommand)
FTP
.
user
,
FTP
.
pass
,
FTP
.
debug
=
user
,
pass
,
dbgFlag
FTP
.
server
=
net
.
createServer
(
net
.
TCP
,
180
)
_G
.
FTP
=
FTP
debug
(
"Server created: (userdata) %s"
,
tostring
(
FTP
.
server
))
this
.
debug
=
(
not
dbgFlag
)
and
type
-- executing type(...) is in effect a NOOP
or
function
(
fmt
,
...
)
-- upval: cnt
if
(
...
)
then
fmt
=
fmt
:
format
(
...
)
end
print
(
node
.
heap
(),
fmt
)
cnt
=
cnt
+
1
if
cnt
%
10
then
tmr
.
wdclr
()
end
end
this
.
server
=
net
.
createServer
(
net
.
TCP
,
180
)
_G
.
FTP
=
this
this
.
debug
(
"Server created: (userdata) %s"
,
tostring
(
this
.
server
))
FTP
.
server
:
listen
(
21
,
function
(
sock
)
-- upval:
FTP (, debug, pcall, type, processCommand)
this
.
server
:
listen
(
21
,
function
(
sock
)
-- upval:
this
-- since a server can have multiple connections, each connection
-- has a CNX table to store connection-wide globals.
local
client
=
FTP
.
client
local
CNX
;
CNX
=
{
-- has its own CXN object (table) to store connection-wide globals.
local
CXN
;
CXN
=
{
validUser
=
false
,
cmdSocket
=
sock
,
send
=
function
(
rec
,
cb
)
-- upval: CNX (,debug)
-- debug("Sending: %s", rec)
return
CNX
.
cmdSocket
:
send
(
rec
..
"
\r\n
"
,
cb
)
end
,
--- send()
close
=
function
(
socket
)
-- upval: client, CNX (,debug, pcall, type)
-- debug("Closing CNX.socket=%s, sock=%s", tostring(CNX.socket), tostring(sock))
debug
=
this
.
debug
,
FTP
=
this
,
send
=
function
(
rec
,
cb
)
-- upval: CXN
CXN
.
debug
(
"Sending: %s"
,
rec
)
return
CXN
.
cmdSocket
:
send
(
rec
..
"
\r\n
"
,
cb
)
end
,
--- CXN. send()
close
=
function
(
socket
)
-- upval: CXN
CXN
.
debug
(
"Closing CXN.cmdSocket=%s"
,
tostring
(
CXN
.
cmdSocket
))
for
_
,
s
in
ipairs
{
'cmdSocket'
,
'dataServer'
,
'dataSocket'
}
do
local
sck
;
sck
,
CNX
[
s
]
=
CNX
[
s
],
nil
-- debug("closing CNX.%s=%s", s, tostring(sck))
if
type
(
sck
)
==
'userdata'
then
pcall
(
sck
.
close
,
sck
)
end
CXN
.
debug
(
"closing CXN.%s=%s"
,
s
,
tostring
(
CXN
[
s
]))
if
type
(
CXN
[
s
])
==
'userdata'
then
pcall
(
socket
.
close
,
CXN
[
s
])
CXN
[
s
]
=
nil
end
client
[
socket
]
=
nil
end
-- CNX.close()
end
CXN
.
FTP
.
client
[
socket
]
=
nil
end
-- CXN.close()
}
local
function
validateUser
(
socket
,
data
)
-- upval: CN
X, FTP (, debug, processCommand)
local
function
validateUser
(
socket
,
data
)
-- upval: C
X
N
-- validate the logon and if then switch to processing commands
-- debug("Authorising: %s", data)
CXN
.
debug
(
"Authorising: %s"
,
data
)
local
cmd
,
arg
=
data
:
match
(
'([A-Za-z]+) *([^\r\n]*)'
)
local
msg
=
"530 Not logged in, authorization required"
cmd
=
cmd
:
upper
()
if
cmd
==
'USER'
then
C
N
X
.
validUser
=
(
arg
==
FTP
.
user
)
msg
=
C
N
X
.
validUser
and
CX
N
.
validUser
=
(
arg
==
CXN
.
FTP
.
user
)
msg
=
CX
N
.
validUser
and
"331 OK. Password required"
or
"530 user not found"
elseif
CNX
.
validUser
and
cmd
==
'PASS'
then
if
arg
==
FTP
.
pass
then
CNX
.
cwd
=
'/'
socket
:
on
(
"receive"
,
function
(
socketObj
,
dataObj
)
processCommand
(
CNX
,
socketObj
,
dataObj
)
elseif
CXN
.
validUser
and
cmd
==
'PASS'
then
if
arg
==
CXN
.
FTP
.
pass
then
CXN
.
cwd
=
'/'
socket
:
on
(
"receive"
,
function
(
soc
,
rec
)
-- upval: CXN
assert
(
soc
==
CXN
.
cmdSocket
)
CXN
.
FTP
.
processCommand
(
CXN
,
rec
)
end
)
-- logged on so switch to command mode
msg
=
"230 Login successful. Username & password correct; proceed."
else
...
...
@@ -128,46 +133,45 @@ function FTP.createServer(user, pass, dbgFlag) -- upval: FTP (, debug, tostring
elseif
cmd
==
'AUTH'
then
msg
=
"500 AUTH not understood"
end
return
C
N
X
.
send
(
msg
)
return
CX
N
.
send
(
msg
)
end
local
port
,
ip
=
sock
:
getpeer
()
-- luacheck: no unused
--debug("Connection accepted: (userdata) %s client %s:%u", tostring(sock), ip, port)
--
cxt.
debug("Connection accepted: (userdata) %s client %s:%u", tostring(sock), ip, port)
sock
:
on
(
"receive"
,
validateUser
)
sock
:
on
(
"disconnection"
,
C
N
X
.
close
)
FTP
.
client
[
sock
]
=
C
N
X
sock
:
on
(
"disconnection"
,
CX
N
.
close
)
this
.
client
[
sock
]
=
CX
N
CNX
.
send
(
"220 FTP server ready"
);
end
)
-- FTP.server:listen()
end
-- FTP.createServer()
CXN
.
send
(
"220 FTP server ready"
);
end
)
-- this.server:listen()
end
--[[SPLIT IGNORE]]
function
FTP
.
close
(
...
)
--[[SPLIT HERE ftp-close]]
-- Lua: FTP:close()
local
this
=
...
function
FTP
.
close
()
-- upval: FTP (, debug, post, tostring)
local
svr
=
FTP
.
server
local
function
rollupClients
(
client
,
server
)
-- upval: FTP (,debug, post, tostring, rollupClients)
-- this is done recursively so that we only close one client per task
local
skt
,
cxt
=
next
(
client
)
-- this.client is a table of soc = cnx. The first (and usually only connection) is cleared
-- immediately and next() used to do a post chain so we only close one client per task
local
function
rollupClients
(
skt
,
cxt
)
-- upval: this, rollupClients
if
skt
then
--
debug("Client close: %s", tostring(skt))
this
.
debug
(
"Client close: %s"
,
tostring
(
skt
))
cxt
.
close
(
skt
)
post
(
function
()
return
rollupClients
(
client
,
server
)
end
)
-- upval: rollupClients, client, server
else
-- debug("Server close: %s", tostring(server))
server
:
close
()
server
:
__gc
()
FTP
,
_G
.
FTP
=
nil
,
nil
-- the upval FTP can only be zeroed once FTP.client is cleared.
this
.
client
[
skt
]
=
nil
node
.
task
.
post
(
function
()
return
rollupClients
(
next
(
this
.
client
,
skt
))
end
)
-- upval: rollupClients, this, skt
else
-- we have emptied the open socket table, so can now shut the server
this
.
debug
(
"Server close: %s"
,
tostring
(
this
.
server
))
this
.
server
:
close
()
this
.
server
:
__gc
()
_G
.
FTP
=
nil
end
end
rollupClients
(
next
(
this
.
client
))
package.loaded
.
ftpserver
=
nil
if
svr
then
rollupClients
(
FTP
.
client
,
svr
)
end
package.loaded
.
ftpserver
=
nil
end
-- FTP.close()
end
--[[SPLIT IGNORE]]
function
FTP
.
processCommand
(
...
)
--[[SPLIT HERE ftp-processCommand]]
----------------------------- Process Command --------------------------------
-- This splits the valid commands into one of three categories:
-- * bare commands (which take no arg)
...
...
@@ -177,28 +181,30 @@ end -- FTP.close()
--
-- Find strings are used do this lookup and minimise long if chains.
------------------------------------------------------------------------------
-- upvals: (, debug, processBareCmds, processSimpleCmds, processDataCmds)
processCommand
=
function
(
cxt
,
socket
,
data
)
-- luacheck: no unused
debug
(
"Command: %s"
,
data
)
local
cxt
,
data
=
...
cxt
.
debug
(
"Command: %s"
,
data
)
data
=
data
:
gsub
(
'[\r\n]+$'
,
''
)
-- chomp trailing CRLF
local
cmd
,
arg
=
data
:
match
(
'([a-zA-Z]+) *(.*)'
)
cmd
=
cmd
:
upper
()
local
_cmd_
=
'_'
..
cmd
..
'_'
if
(
'_CDUP_NOOP_PASV_PWD_QUIT_SYST_'
):
find
(
_cmd_
)
then
processBareCmds
(
cxt
,
cmd
)
cxt
.
FTP
.
processBareCmds
(
cxt
,
cmd
)
elseif
(
'_CWD_DELE_MODE_PORT_RNFR_RNTO_SIZE_TYPE_'
):
find
(
_cmd_
)
then
processSimpleCmds
(
cxt
,
cmd
,
arg
)
cxt
.
FTP
.
processSimpleCmds
(
cxt
,
cmd
,
arg
)
elseif
(
'_LIST_NLST_RETR_STOR_'
):
find
(
_cmd_
)
then
processDataCmds
(
cxt
,
cmd
,
arg
)
cxt
.
FTP
.
processDataCmds
(
cxt
,
cmd
,
arg
)
else
cxt
.
send
(
"500 Unknown error"
)
end
end
-- processCommand(sock, data)
end
--[[SPLIT IGNORE]]
function
FTP
.
processBareCmds
(
...
)
--[[SPLIT HERE ftp-processBareCmds]]
-------------------------- Process Bare Commands -----------------------------
processBareCmds
=
function
(
cxt
,
cmd
)
-- upval: (dataServer)
local
cxt
,
cmd
=
...
local
send
=
cxt
.
send
...
...
@@ -220,7 +226,7 @@ processBareCmds = function(cxt, cmd) -- upval: (dataServer)
pplo
=
port
%
256
pphi
=
(
port
-
pplo
)
/
256
i1
,
i2
,
i3
,
i4
=
ip
:
match
(
"(%d+).(%d+).(%d+).(%d+)"
)
dataServer
(
cxt
,
port
)
cxt
.
FTP
.
dataServer
(
cxt
,
port
)
return
send
(
(
'227 Entering Passive Mode(%d,%d,%d,%d,%d,%d)'
):
format
(
i1
,
i2
,
i3
,
i4
,
pphi
,
pplo
))
...
...
@@ -229,7 +235,7 @@ processBareCmds = function(cxt, cmd) -- upval: (dataServer)
return
send
(
'257 "/" is the current directory'
)
elseif
cmd
==
'QUIT'
then
send
(
"221 Goodbye"
,
function
()
cxt
.
close
(
cxt
.
cmdSocket
)
end
)
send
(
"221 Goodbye"
,
function
()
cxt
.
close
(
cxt
.
cmdSocket
)
end
)
-- upval: cxt
return
elseif
cmd
==
'SYST'
then
...
...
@@ -239,11 +245,14 @@ processBareCmds = function(cxt, cmd) -- upval: (dataServer)
else
error
(
'Oops. Missed '
..
cmd
)
end
end
-- processBareCmds(cmd, send)
end
--[[SPLIT IGNORE]]
function
FTP
.
processSimpleCmds
(
...
)
--[[SPLIT HERE ftp-processSimpleCmds]]
------------------------- Process Simple Commands ----------------------------
local
from
-- needs to persist between simple commands
processSimpleCmds
=
function
(
cxt
,
cmd
,
arg
)
-- upval: from (, file, tostring, dataServer, debug)
local
cxt
,
cmd
,
arg
=
...
local
send
=
cxt
.
send
...
...
@@ -252,7 +261,7 @@ processSimpleCmds = function(cxt, cmd, arg) -- upval: from (, file, tostring, d
"504 Only S(tream) is suported"
)
elseif
cmd
==
'PORT'
then
dataServer
(
cxt
,
nil
)
-- clear down any PASV setting
cxt
.
FTP
.
dataServer
(
cxt
,
nil
)
-- clear down any PASV setting
return
send
(
"502 Active mode not supported. PORT not implemented"
)
elseif
cmd
==
'TYPE'
then
...
...
@@ -269,7 +278,7 @@ processSimpleCmds = function(cxt, cmd, arg) -- upval: from (, file, tostring, d
-- The remaining commands take a filename as an arg. Strip off leading / and ./
arg
=
arg
:
gsub
(
'^%.?/'
,
''
):
gsub
(
'^%.?/'
,
''
)
debug
(
"Filename is %s"
,
arg
)
cxt
.
debug
(
"Filename is %s"
,
arg
)
if
cmd
==
'CWD'
then
if
arg
:
match
(
'^[%./]*$'
)
then
...
...
@@ -285,14 +294,14 @@ processSimpleCmds = function(cxt, cmd, arg) -- upval: from (, file, tostring, d
return
send
(
"550 Requested action not taken"
)
elseif
cmd
==
'RNFR'
then
from
=
arg
cxt
.
from
=
arg
send
(
"350 RNFR accepted"
)
return
elseif
cmd
==
'RNTO'
then
local
status
=
from
and
file
.
rename
(
from
,
arg
)
--
debug("rename('%s','%s')=%s", tostring(from), tostring(arg), tostring(status))
from
=
nil
local
status
=
cxt
.
from
and
file
.
rename
(
cxt
.
from
,
arg
)
cxt
.
debug
(
"rename('%s','%s')=%s"
,
tostring
(
cxt
.
from
),
tostring
(
arg
),
tostring
(
status
))
cxt
.
from
=
nil
return
send
(
status
and
"250 File renamed"
or
"550 Requested action not taken"
)
elseif
cmd
==
"SIZE"
then
...
...
@@ -303,16 +312,19 @@ processSimpleCmds = function(cxt, cmd, arg) -- upval: from (, file, tostring, d
else
error
(
'Oops. Missed '
..
cmd
)
end
end
-- processSimpleCmds(cmd, arg, send)
end
--[[SPLIT IGNORE]]
function
FTP
.
processDataCmds
(
...
)
--[[SPLIT HERE ftp-processDataCmds]]
-------------------------- Process Data Commands -----------------------------
processDataCmds
=
function
(
cxt
,
cmd
,
arg
)
-- upval: FTP (, pairs, file, tostring, debug, post)
local
send
=
cxt
.
send
local
cxt
,
cmd
,
arg
=
...
local
send
,
FTP
=
cxt
.
send
,
cxt
.
FTP
-- luacheck: ignore FTP
-- The data commands are only accepted if a PORT command is in scope
if
cxt
.
dataServer
==
nil
and
cxt
.
dataSocket
==
nil
then
if
FTP
.
dataServer
==
nil
and
cxt
.
dataSocket
==
nil
then
return
send
(
"502 Active mode not supported. "
..
cmd
..
" not implemented"
)
end
...
...
@@ -340,8 +352,8 @@ processDataCmds = function(cxt, cmd, arg) -- upval: FTP (, pairs, file, tostrin
end
table.sort
(
nameList
)
function
cxt
.
getData
()
-- upval: cmd, fileSize, nameList
(, table)
local
list
,
user
=
{},
FTP
.
user
function
cxt
.
getData
(
c
)
-- upval: cmd, fileSize, nameList
local
list
,
user
=
{},
c
.
FTP
.
user
for
i
=
1
,
10
do
-- luacheck: no unused
if
#
nameList
==
0
then
break
end
local
f
=
table.remove
(
nameList
,
1
)
...
...
@@ -355,7 +367,7 @@ processDataCmds = function(cxt, cmd, arg) -- upval: FTP (, pairs, file, tostrin
elseif
cmd
==
"RETR"
then
local
f
=
file
.
open
(
arg
,
"r"
)
if
f
then
-- define a getter to read the file
function
cxt
.
getData
()
-- upval: f
function
cxt
.
getData
(
c
)
-- luacheck: ignore c
-- upval: f
local
buf
=
f
:
read
(
1024
)
if
not
buf
then
f
:
close
();
f
=
nil
;
end
return
buf
...
...
@@ -365,13 +377,13 @@ processDataCmds = function(cxt, cmd, arg) -- upval: FTP (, pairs, file, tostrin
elseif
cmd
==
"STOR"
then
local
f
=
file
.
open
(
arg
,
"w"
)
if
f
then
-- define a setter to write the file
function
cxt
.
setData
(
rec
)
-- upval f, ar
g (, debu
g)
--
debug("writing %u bytes to %s", #rec, arg)
function
cxt
.
setData
(
c
,
rec
)
--
luacheck: ignore c --
upval
:
f
(
, arg)
cxt
.
debug
(
"writing %u bytes to %s"
,
#
rec
,
arg
)
return
f
:
write
(
rec
)
end
-- cxt.saveData(rec)
function
cxt
.
fileClose
()
--
upval cxt, f, arg (,debu
g)
--
debug("closing %s", arg)
f
:
close
();
cxt
.
fileClose
,
f
=
nil
,
nil
function
cxt
.
fileClose
(
c
)
--
luacheck: ignore c -- upval: f (,ar
g)
cxt
.
debug
(
"closing %s"
,
arg
)
f
:
close
();
f
=
nil
end
-- cxt.close()
end
...
...
@@ -380,13 +392,12 @@ processDataCmds = function(cxt, cmd, arg) -- upval: FTP (, pairs, file, tostrin
send
((
cxt
.
getData
or
cxt
.
setData
)
and
"150 Accepted data connection"
or
"451 Can't open/create "
..
arg
)
if
cxt
.
getData
and
cxt
.
dataSocket
then
debug
(
"poking sender to initiate first xfer"
)
post
(
function
()
cxt
.
sender
(
cxt
.
dataSocket
)
end
)
cxt
.
debug
(
"poking sender to initiate first xfer"
)
node
.
task
.
post
(
function
()
cxt
.
sender
(
cxt
.
dataSocket
)
end
)
-- upval: cxt
end
end
-- processDataCmds(cmd, arg, send)
end
--[[SPLIT IGNORE]]
function
FTP
.
dataServer
(
...
)
--[[SPLIT HERE ftp-dataServer]]
----------------------------- Data Port Routines -----------------------------
-- These are used to manage the data transfer over the data port. This is
-- set up lazily either by a PASV or by the first LIST NLST RETR or STOR
...
...
@@ -394,51 +405,53 @@ end -- processDataCmds(cmd, arg, send)
-- handle the actual xfer. Also note that the sending process can be primed in
--
---------------- Open a new data server and port ---------------------------
dataServer
=
function
(
cxt
,
n
)
-- upval: (pcall, net, ftpDataOpen, debug, tostring)
local
dataSrv
=
cxt
.
dataServer
if
dataSrv
then
-- close any existing listener
p
cal
l
(
dataS
rv
.
close
,
dataSrv
)
end
local
cxt
,
n
=
...
lo
cal
dataS
vr
=
cxt
.
dataS
e
rv
er
if
dataSvr
then
pcall
(
dataSvr
.
close
,
dataSrv
)
end
-- luacheck: ignore -- close any existing listener
if
n
then
-- Open a new listener if needed. Note that this is only used to establish
-- a single connection, so ftpDataOpen closes the server socket
cxt
.
dataServer
=
net
.
createServer
(
net
.
TCP
,
300
)
cxt
.
dataServer
:
listen
(
n
,
function
(
sock
)
-- upval: cxt, (ftpDataOpen)
ftpDataOpen
(
cxt
,
sock
)
dataSvr
=
net
.
createServer
(
net
.
TCP
,
300
)
cxt
.
dataServer
=
dataSvr
dataSvr
:
listen
(
n
,
function
(
sock
)
-- upval: cxt
cxt
.
FTP
.
ftpDataOpen
(
cxt
,
sock
)
end
)
--
debug("Listening on Data port %u, server %s",n, tostring(cxt.dataServer))
cxt
.
debug
(
"Listening on Data port %u, server %s"
,
n
,
tostring
(
cxt
.
dataServer
))
else
cxt
.
dataServer
=
nil
--
debug("Stopped listening on Data port",n)
cxt
.
debug
(
"Stopped listening on Data port"
,
n
)
end
end
-- dataServer(n)
end
--[[SPLIT IGNORE]]
function
FTP
.
ftpDataOpen
(
...
)
--[[SPLIT HERE ftp-ftpDataOpen]]
----------------------- Connection on FTP data port ------------------------
ftpDataOpen
=
function
(
cxt
,
dataSocket
)
-- upval: (debug, tostring, post, pcall)
local
cxt
,
dataSocket
=
...
local
sport
,
sip
=
dataSocket
:
getaddr
()
local
cport
,
cip
=
dataSocket
:
getpeer
()
debug
(
"Opened data socket %s from %s:%u to %s:%u"
,
tostring
(
dataSocket
),
sip
,
sport
,
cip
,
cport
)
cxt
.
debug
(
"Opened data socket %s from %s:%u to %s:%u"
,
tostring
(
dataSocket
),
sip
,
sport
,
cip
,
cport
)
cxt
.
dataSocket
=
dataSocket
cxt
.
dataServer
:
close
()
cxt
.
dataServer
=
nil
local
function
cleardown
(
skt
,
type
)
--
upval: cxt (, debug, tostring, post, pcall)
-- luacheck:
push no unused
type
=
type
==
1
and
"disconnection"
or
"reconnection"
function
cxt
.
cleardown
(
cxt
,
skt
,
cd
type
)
--
luacheck: ignore cxt -- shadowing
-- luacheck:
ignore cdtype which
cd
type
=
cd
type
==
1
and
"disconnection"
or
"reconnection"
local
which
=
cxt
.
setData
and
"setData"
or
(
cxt
.
getData
and
cxt
.
getData
or
"neither"
)
--debug("Cleardown entered from %s with %s", type, which)
-- luacheck: pop
cxt
.
debug
(
"Cleardown entered from %s with %s"
,
cdtype
,
which
)
if
cxt
.
setData
then
cxt
.
fileClose
()
cxt
:
fileClose
()
cxt
.
setData
=
nil
cxt
.
send
(
"226 Transfer complete."
)
else
cxt
.
getData
,
cxt
.
sender
=
nil
,
nil
end
--
debug("Clearing down data socket %s", tostring(skt))
post
(
function
()
-- upval:
skt, cxt, (, pcall)
cxt
.
debug
(
"Clearing down data socket %s"
,
tostring
(
skt
))
node
.
task
.
post
(
function
()
-- upval:
cxt, skt
pcall
(
skt
.
close
,
skt
);
skt
=
nil
cxt
.
dataSocket
=
nil
end
)
...
...
@@ -446,10 +459,10 @@ ftpDataOpen = function(cxt, dataSocket) -- upval: (debug, tostring, post, pcall)
local
on_hold
=
false
dataSocket
:
on
(
"receive"
,
function
(
skt
,
rec
)
--upval: cxt, on_hold
(, debug, tstring, post, node, pcall)
dataSocket
:
on
(
"receive"
,
function
(
skt
,
rec
)
--
upval: cxt, on_hold
local
which
=
cxt
.
setData
and
"setData"
or
(
cxt
.
getData
and
cxt
.
getData
or
"neither"
)
-- luacheck: no unused
--
debug("Received %u data bytes with %s", #rec,
which
)
local
rectype
=
cxt
.
setData
and
"setData"
or
(
cxt
.
getData
and
cxt
.
getData
or
"neither"
)
cxt
.
debug
(
"Received %u data bytes with %s"
,
#
rec
,
rectype
)
if
not
cxt
.
setData
then
return
end
...
...
@@ -458,33 +471,33 @@ ftpDataOpen = function(cxt, dataSocket) -- upval: (debug, tostring, post, pcall)
-- large file. As soon as a record arrives assert a flow control hold.
-- This can take up to 5 packets to come into effect at which point the
-- low priority unhold task is executed releasing the flow again.
--
debug("Issuing hold on data socket %s", tostring(skt))
cxt
.
debug
(
"Issuing hold on data socket %s"
,
tostring
(
skt
))
skt
:
hold
();
on_hold
=
true
post
(
node
.
task
.
LOW_PRIORITY
,
function
()
-- upval: skt, on_hold
(, debug, tostring))
--
debug("Issuing unhold on data socket %s", tostring(skt))
node
.
task
.
post
(
node
.
task
.
LOW_PRIORITY
,
function
()
-- upval: skt, on_hold
cxt
.
debug
(
"Issuing unhold on data socket %s"
,
tostring
(
skt
))
pcall
(
skt
.
unhold
,
skt
);
on_hold
=
false
end
)
end
if
not
cxt
.
setData
(
rec
)
then
--
debug("Error writing to SPIFFS")
cxt
.
fileClose
()
if
not
cxt
:
setData
(
rec
)
then
cxt
.
debug
(
"Error writing to SPIFFS"
)
cxt
:
fileClose
()
cxt
.
setData
=
nil
cxt
.
send
(
"552 Upload aborted. Exceeded storage allocation"
)
end
end
)
function
cxt
.
sender
(
skt
)
-- upval: cxt
(, debug)
debug
(
"entering sender"
)
function
cxt
.
sender
(
skt
)
-- upval: cxt
cxt
.
debug
(
"entering sender"
)
if
not
cxt
.
getData
then
return
end
skt
=
skt
or
cxt
.
dataSocket
local
rec
=
cxt
.
getData
()
local
rec
=
cxt
:
getData
()
if
rec
and
#
rec
>
0
then
--
debug("Sending %u data bytes", #rec)
cxt
.
debug
(
"Sending %u data bytes"
,
#
rec
)
skt
:
send
(
rec
)
else
--
debug("Send of data completed")
cxt
.
debug
(
"Send of data completed"
)
skt
:
close
()
cxt
.
send
(
"226 Transfer complete."
)
cxt
.
getData
,
cxt
.
dataSocket
=
nil
,
nil
...
...
@@ -492,14 +505,11 @@ ftpDataOpen = function(cxt, dataSocket) -- upval: (debug, tostring, post, pcall)
end
dataSocket
:
on
(
"sent"
,
cxt
.
sender
)
dataSocket
:
on
(
"disconnection"
,
function
(
skt
)
return
cleardown
(
skt
,
1
)
end
)
dataSocket
:
on
(
"reconnection"
,
function
(
skt
)
return
cleardown
(
skt
,
2
)
end
)
dataSocket
:
on
(
"disconnection"
,
function
(
skt
)
return
cxt
:
cleardown
(
skt
,
1
)
end
)
-- upval: cxt
dataSocket
:
on
(
"reconnection"
,
function
(
skt
)
return
cxt
:
cleardown
(
skt
,
2
)
end
)
-- upval: cxt
-- if we are sending to client then kick off the first send
if
cxt
.
getData
then
cxt
.
sender
(
cxt
.
dataSocket
)
end
end
-- ftpDataOpen(socket)
------------------------------------------------ -----------------------------
return
FTP
end
--[[SPLIT HERE]]
return
FTP
--[[SPLIT IGNORE]]
lua_modules/telnet/README.md
0 → 100644
View file @
8d091c47
# Telnet Module
Documentation for this Lua module is available in the
[
telnet.md
](
../../docs/lua-modules/telnet.md
)
file and in the
[
Official NodeMCU Documentation
](
https://nodemcu.readthedocs.io/
)
in
`Lua Modules`
section.
lua_
examp
les/telnet/telnet
_pipe
.lua
→
lua_
modu
les/telnet/telnet.lua
View file @
8d091c47
--[[SPLIT MODULE telnet]]
--[[ A telnet server T. Ellison, June 2019
This version of the telnet server demonstrates the use of the new stdin and stout
...
...
@@ -65,5 +67,4 @@ function M.close(this)
end
return
M
--[[SPLIT HERE]]
mkdocs.yml
View file @
8d091c47
...
...
@@ -58,6 +58,7 @@ pages:
-
'
lm92'
:
'
lua-modules/lm92.md'
-
'
mcp23008'
:
'
lua-modules/mcp23008.md'
-
'
redis'
:
'
lua-modules/redis.md'
-
'
telnet'
:
'
lua-modules/telnet.md'
-
'
yeelink'
:
'
lua-modules/yeelink.md'
-
C Modules
:
-
'
adc'
:
'
modules/adc.md'
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment