diff --git a/pixo/FTP-Server-for-ESP8266-ESP32-and-PYBD/LICENSE b/pixo/FTP-Server-for-ESP8266-ESP32-and-PYBD/LICENSE deleted file mode 100644 index a8fc851..0000000 --- a/pixo/FTP-Server-for-ESP8266-ESP32-and-PYBD/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2016 - -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. diff --git a/pixo/FTP-Server-for-ESP8266-ESP32-and-PYBD/README.md b/pixo/FTP-Server-for-ESP8266-ESP32-and-PYBD/README.md deleted file mode 100644 index 20b72c9..0000000 --- a/pixo/FTP-Server-for-ESP8266-ESP32-and-PYBD/README.md +++ /dev/null @@ -1,138 +0,0 @@ -# uftpd: small FTP server for ESP8266, ESP32 and Pyboard D - -**Intro** - -Based on the work of chrisgp - Christopher Popp and pfalcon - Paul Sokolovsky -Christopher made a first uftp server script, which runs in foreground. -Paul made webrepl with the framework for background operations, which then was used -also by Christopher to implement his utelnetsever code. -My task was to put all these pieces together and assemble this uftpd.py script, -which runs in background and acts as ftp server. -Due to its size, for ESP8266 it either has to be integrated into the flash image as frozen -bytecode, by placing it into the esp8266/modules folder and performing a rebuild, -or it must be compiled into bytecode using mpy-cross and loaded as an .mpy file. -The frozen bytecode variant is preferred. - -The server has some limitations: -- Binary mode only -- Limited multi-session support. The server accepts multiple sessions, but only -one session command at a time is served while the other sessions receive a 'busy' -response, which still allows interleaved actions. -- No user authentication. Any user may log in without a password. User -authentication may be added easily, if required. -- Not all ftp commands are implemented. -- ESP8266 is **NOT** a multitasking platform and the system calls are NOT re-entrant. -Even when the ftp server sits in background and can serve requests, **no -foreground tasks should run at that time**, especially if they execute system calls. -The effects is hardly to predict, although most of the time the device simply -crashes. -- ESP32 The server is supported from version='v1.9.3-575 on. That is the version -which introduced webrepl. - - -## Start-up - -You'll start the server with: - -`import uftpd` - -The service will immediately be started at port 21 in silent mode. You may -stop the service then with: - -`utfpd.stop()` - -When stopped or not started yet, start it manually with: - -`uftpd.start([port = 21][, verbose = level])` -or -`uftpd.restart([port = 21][, verbose = level])` - -port is the port number (default 21) -verbose controls the level of printed activity messages, values 0 .. 2 - -You may use -`uftd.restart([port = 21][, verbose = level])` -as a shortcut for uftp.stop() and uftpd.start(). - -## Coverage -The server works well with most dedicated ftp clients, and most browsers and file -managers. These are test results with an arbitrary selected set: - -**Linux** - -- ftp: works for file & directory operations including support for the m* commands -- filezilla, fireftp: work fine, including loading into the editor & saving back. -Take care to limit the number of data session to 1. -- Nautilus: works mostly, including loading into the editor & saving back. -Copying multiple files at once to the esp8266 fails, because nautilus tries -to open multiple sessions for that purpose. -Configure Nautilus with dconf-editor to show directory count for local dirs only. -Once mounted, you can even open a terminal at that spot. -The path is something like: /run/user/1000/gvfs/ftp:host=x.y.y.z. -- Thunar: works fine, including loading & saving of files. -directly into e.g. an editor & saving back. -- Dolphin, Konqueror: work fine most of the time, including loading -directly into e.g. an editor & saving back. But no obvious disconnect. -- Chrome, Firefox: view/navigate directories & and view files - -**Mac OS X, various Versions** - -- ftp: works like on Linux -- Chrome, Firefox: view/navigate directories & and view files -- FileZilla, FireFtp, Cyberduck: Full operation, once proper configured (see above). -Configure Cyberduck to transfer data in the command session. -- Finder: Fails. It connects, but then locks in the attempt to display the -top level directory repeating attempts to open new sessions. Finder needs -full multi-session support, and never closes sessions properly. -- Mountainduck: Works well, including proper disconnect when closing. - - -**Windows 10** (and Windows XP) - -- ftp: supported. Be aware that the Windows variant of ftp differs slightly -from the Linux variant, but the most used commands are the same. -- File explorer: view/navigate directories & and copy files. For editing files you -have to copy them to your PC and back. Windows explorer does not always release the -connection when it is closed, which just results in a silent connection, which -is closed latest when Windows is shut down. -- FileZilla, FireFtp, Cyberduck: Full operation, once proper configured (see above). -Configure Cyberduck to transfer data in the command session. -- WinSCP: works fine -- NppFTP - FTP extension to Notepad++: Works fine and is very convenient. -- Mountainduck: Works to some extent, but sometimes stumbles and takes a long -time to open a file. - -**Android** - -- ftp inside the terminal emulator termux: full operation. -- ftp-express -- Chrome: view/navigate directories & and view files - -**IOS 9.1** - -- FTP Client lite: works flawless - -**Windows 10 mobile** - -- Metro file manager: Works with file/directory view & navigate, file download, -file upload, file delete, file rename. Slow and chaotic sequence of FTP commands. -Many unneeded re-login attempts. - -**Conclusion**: All dedicated ftp clients work fine, and most -of the file managers too. - -## Trouble shooting -The only trouble observed so far was clients not releasing the connections. You may tell -by the value of `uftp.client_list`, which should be empty if no client is connected, or by issuing the command rstat in ftp, which shows the number of connected clients. -In that case you may restart the server with uftpd.restart(). If `uftd.client_busy` -is `True` when no client is connected, then restart the server with with -`uftpd.restart()`. If you want to see what happens at the server, you may set verbose to 2. -Just restart it with `uftpd.restart(verbose = 1)`, or set `uftpd.verbose_l = 1`, and -`uftpd.verbose_l = 0` to stop control messages again. - -## Files -- uftpd.py: Server source file for ESP8266 and ESP32 from version='v1.9.3-575 on -- ftp.py: Simple version of the ftp server, which works in foreground. This -can be used with all Micorpython versions. It terminates when the client closes the -session. Only a single session is supported by this variant. -- README.md: This one diff --git a/pixo/FTP-Server-for-ESP8266-ESP32-and-PYBD/ftp.py b/pixo/FTP-Server-for-ESP8266-ESP32-and-PYBD/ftp.py deleted file mode 100644 index 7e068f0..0000000 --- a/pixo/FTP-Server-for-ESP8266-ESP32-and-PYBD/ftp.py +++ /dev/null @@ -1,331 +0,0 @@ -# -# Small ftp server for ESP8266 ans ESP32 Micropython -# -# Based on the work of chrisgp - Christopher Popp and pfalcon - Paul Sokolovsky -# -# The server accepts passive mode only. -# It runs in foreground and quits, when it receives a quit command -# Start the server with: -# -# import ftp -# -# Copyright (c) 2016 Christopher Popp (initial ftp server framework) -# Copyright (c) 2016 Robert Hammelrath (putting the pieces together -# and a few extensions) -# Distributed under MIT License -# -import socket -import network -import uos -import gc - - -def send_list_data(path, dataclient, full): - try: # whether path is a directory name - for fname in sorted(uos.listdir(path), key=str.lower): - dataclient.sendall(make_description(path, fname, full)) - except: # path may be a file name or pattern - pattern = path.split("/")[-1] - path = path[:-(len(pattern) + 1)] - if path == "": - path = "/" - for fname in sorted(uos.listdir(path), key=str.lower): - if fncmp(fname, pattern): - dataclient.sendall(make_description(path, fname, full)) - - -def make_description(path, fname, full): - if full: - stat = uos.stat(get_absolute_path(path, fname)) - file_permissions = ("drwxr-xr-x" - if (stat[0] & 0o170000 == 0o040000) - else "-rw-r--r--") - file_size = stat[6] - description = "{} 1 owner group {:>10} Jan 1 2000 {}\r\n".format( - file_permissions, file_size, fname) - else: - description = fname + "\r\n" - return description - - -def send_file_data(path, dataclient): - with open(path, "r") as file: - chunk = file.read(512) - while len(chunk) > 0: - dataclient.sendall(chunk) - chunk = file.read(512) - - -def save_file_data(path, dataclient): - with open(path, "w") as file: - chunk = dataclient.recv(512) - while len(chunk) > 0: - file.write(chunk) - chunk = dataclient.recv(512) - - -def get_absolute_path(cwd, payload): - # Just a few special cases "..", "." and "" - # If payload start's with /, set cwd to / - # and consider the remainder a relative path - if payload.startswith('/'): - cwd = "/" - for token in payload.split("/"): - if token == '..': - if cwd != '/': - cwd = '/'.join(cwd.split('/')[:-1]) - if cwd == '': - cwd = '/' - elif token != '.' and token != '': - if cwd == '/': - cwd += token - else: - cwd = cwd + '/' + token - return cwd - - -# compare fname against pattern. Pattern may contain -# wildcards ? and *. -def fncmp(fname, pattern): - pi = 0 - si = 0 - while pi < len(pattern) and si < len(fname): - if (fname[si] == pattern[pi]) or (pattern[pi] == '?'): - si += 1 - pi += 1 - else: - if pattern[pi] == '*': # recurse - if (pi + 1) == len(pattern): - return True - while si < len(fname): - if fncmp(fname[si:], pattern[pi+1:]): - return True - else: - si += 1 - return False - else: - return False - if pi == len(pattern.rstrip("*")) and si == len(fname): - return True - else: - return False - - -def ftpserver(): - - DATA_PORT = 13333 - - ftpsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - datasocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - - ftpsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - datasocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - - ftpsocket.bind(socket.getaddrinfo("0.0.0.0", 21)[0][4]) - datasocket.bind(socket.getaddrinfo("0.0.0.0", DATA_PORT)[0][4]) - - ftpsocket.listen(1) - ftpsocket.settimeout(None) - datasocket.listen(1) - datasocket.settimeout(None) - - msg_250_OK = '250 OK\r\n' - msg_550_fail = '550 Failed\r\n' - # check for an active interface, STA first - wlan = network.WLAN(network.STA_IF) - if wlan.active(): - addr = wlan.ifconfig()[0] - else: - wlan = network.WLAN(network.AP_IF) - if wlan.active(): - addr = wlan.ifconfig()[0] - else: - print("No active connection") - return - - print("FTP Server started on ", addr) - try: - dataclient = None - fromname = None - do_run = True - while do_run: - cl, remote_addr = ftpsocket.accept() - cl.settimeout(300) - cwd = '/' - try: - # print("FTP connection from:", remote_addr) - cl.sendall("220 Hello, this is the ESP8266/ESP32.\r\n") - while True: - gc.collect() - data = cl.readline().decode("utf-8").rstrip("\r\n") - if len(data) <= 0: - print("Client disappeared") - do_run = False - break - - command = data.split(" ")[0].upper() - payload = data[len(command):].lstrip() - - path = get_absolute_path(cwd, payload) - - print("Command={}, Payload={}".format(command, payload)) - - if command == "USER": - cl.sendall("230 Logged in.\r\n") - elif command == "SYST": - cl.sendall("215 UNIX Type: L8\r\n") - elif command == "NOOP": - cl.sendall("200 OK\r\n") - elif command == "FEAT": - cl.sendall("211 no-features\r\n") - elif command == "PWD" or command == "XPWD": - cl.sendall('257 "{}"\r\n'.format(cwd)) - elif command == "CWD" or command == "XCWD": - try: - files = uos.listdir(path) - cwd = path - cl.sendall(msg_250_OK) - except: - cl.sendall(msg_550_fail) - elif command == "CDUP": - cwd = get_absolute_path(cwd, "..") - cl.sendall(msg_250_OK) - elif command == "TYPE": - # probably should switch between binary and not - cl.sendall('200 Transfer mode set\r\n') - elif command == "SIZE": - try: - size = uos.stat(path)[6] - cl.sendall('213 {}\r\n'.format(size)) - except: - cl.sendall(msg_550_fail) - elif command == "QUIT": - cl.sendall('221 Bye.\r\n') - do_run = False - break - elif command == "PASV": - cl.sendall('227 Entering Passive Mode ({},{},{}).\r\n'. - format(addr.replace('.', ','), DATA_PORT >> 8, - DATA_PORT % 256)) - dataclient, data_addr = datasocket.accept() - print("FTP Data connection from:", data_addr) - DATA_PORT = 13333 - active = False - elif command == "PORT": - items = payload.split(",") - if len(items) >= 6: - data_addr = '.'.join(items[:4]) - # replace by command session addr - if data_addr == "127.0.1.1": - data_addr = remote_addr - DATA_PORT = int(items[4]) * 256 + int(items[5]) - dataclient = socket.socket(socket.AF_INET, - socket.SOCK_STREAM) - dataclient.settimeout(10) - dataclient.connect((data_addr, DATA_PORT)) - print("FTP Data connection with:", data_addr) - cl.sendall('200 OK\r\n') - active = True - else: - cl.sendall('504 Fail\r\n') - elif command == "LIST" or command == "NLST": - if not payload.startswith("-"): - place = path - else: - place = cwd - try: - cl.sendall("150 Here comes the directory listing.\r\n") - send_list_data(place, dataclient, - command == "LIST" or payload == "-l") - cl.sendall("226 Listed.\r\n") - except: - cl.sendall(msg_550_fail) - if dataclient is not None: - dataclient.close() - dataclient = None - elif command == "RETR": - try: - cl.sendall("150 Opening data connection.\r\n") - send_file_data(path, dataclient) - cl.sendall("226 Transfer complete.\r\n") - except: - cl.sendall(msg_550_fail) - if dataclient is not None: - dataclient.close() - dataclient = None - elif command == "STOR": - try: - cl.sendall("150 Ok to send data.\r\n") - save_file_data(path, dataclient) - cl.sendall("226 Transfer complete.\r\n") - except: - cl.sendall(msg_550_fail) - if dataclient is not None: - dataclient.close() - dataclient = None - elif command == "DELE": - try: - uos.remove(path) - cl.sendall(msg_250_OK) - except: - cl.sendall(msg_550_fail) - elif command == "RMD" or command == "XRMD": - try: - uos.rmdir(path) - cl.sendall(msg_250_OK) - except: - cl.sendall(msg_550_fail) - elif command == "MKD" or command == "XMKD": - try: - uos.mkdir(path) - cl.sendall(msg_250_OK) - except: - cl.sendall(msg_550_fail) - elif command == "RNFR": - fromname = path - cl.sendall("350 Rename from\r\n") - elif command == "RNTO": - if fromname is not None: - try: - uos.rename(fromname, path) - cl.sendall(msg_250_OK) - except: - cl.sendall(msg_550_fail) - else: - cl.sendall(msg_550_fail) - fromname = None - elif command == "MDTM": - try: - tm=localtime(uos.stat(path)[8]) - cl.sendall('213 {:04d}{:02d}{:02d}{:02d}{:02d}{:02d}\r\n'.format(*tm[0:6])) - except: - cl.sendall('550 Fail\r\n') - elif command == "STAT": - if payload == "": - cl.sendall("211-Connected to ({})\r\n" - " Data address ({})\r\n" - "211 TYPE: Binary STRU: File MODE:" - " Stream\r\n".format( - remote_addr[0], addr)) - else: - cl.sendall("213-Directory listing:\r\n") - send_list_data(path, cl, True) - cl.sendall("213 Done.\r\n") - else: - cl.sendall("502 Unsupported command.\r\n") - print("Unsupported command {} with payload {}".format( - command, payload)) - except Exception as err: - print(err) - - finally: - cl.close() - cl = None - finally: - datasocket.close() - ftpsocket.close() - if dataclient is not None: - dataclient.close() - - -ftpserver() diff --git a/pixo/FTP-Server-for-ESP8266-ESP32-and-PYBD/ftp_thread.py b/pixo/FTP-Server-for-ESP8266-ESP32-and-PYBD/ftp_thread.py deleted file mode 100644 index c791685..0000000 --- a/pixo/FTP-Server-for-ESP8266-ESP32-and-PYBD/ftp_thread.py +++ /dev/null @@ -1,334 +0,0 @@ -# -# Small ftp server for ESP8266 ans ESP32 Micropython -# -# Based on the work of chrisgp - Christopher Popp and pfalcon - Paul Sokolovsky -# -# The server accepts passive mode only. -# It runs in foreground and quits, when it receives a quit command -# Start the server with: -# -# import ftp -# -# Copyright (c) 2016 Christopher Popp (initial ftp server framework) -# Copyright (c) 2016 Robert Hammelrath (putting the pieces together -# and a few extensions) -# Distributed under MIT License -# -import socket -import network -import uos -import gc - - -def send_list_data(path, dataclient, full): - try: # whether path is a directory name - for fname in sorted(uos.listdir(path), key=str.lower): - dataclient.sendall(make_description(path, fname, full)) - except: # path may be a file name or pattern - pattern = path.split("/")[-1] - path = path[:-(len(pattern) + 1)] - if path == "": - path = "/" - for fname in sorted(uos.listdir(path), key=str.lower): - if fncmp(fname, pattern): - dataclient.sendall(make_description(path, fname, full)) - - -def make_description(path, fname, full): - if full: - stat = uos.stat(get_absolute_path(path, fname)) - file_permissions = "drwxr-xr-x"\ - if (stat[0] & 0o170000 == 0o040000)\ - else "-rw-r--r--" - file_size = stat[6] - description = "{} 1 owner group {:>10} Jan 1 2000 {}\r\n".format( - file_permissions, file_size, fname) - else: - description = fname + "\r\n" - return description - - -def send_file_data(path, dataclient): - with open(path, "r") as file: - chunk = file.read(512) - while len(chunk) > 0: - dataclient.sendall(chunk) - chunk = file.read(512) - - -def save_file_data(path, dataclient): - with open(path, "w") as file: - chunk = dataclient.recv(512) - while len(chunk) > 0: - file.write(chunk) - chunk = dataclient.recv(512) - - -def get_absolute_path(cwd, payload): - # Just a few special cases "..", "." and "" - # If payload start's with /, set cwd to / - # and consider the remainder a relative path - if payload.startswith('/'): - cwd = "/" - for token in payload.split("/"): - if token == '..': - if cwd != '/': - cwd = '/'.join(cwd.split('/')[:-1]) - if cwd == '': - cwd = '/' - elif token != '.' and token != '': - if cwd == '/': - cwd += token - else: - cwd = cwd + '/' + token - return cwd - - -# compare fname against pattern. Pattern may contain -# wildcards ? and *. -def fncmp(fname, pattern): - pi = 0 - si = 0 - while pi < len(pattern) and si < len(fname): - if (fname[si] == pattern[pi]) or (pattern[pi] == '?'): - si += 1 - pi += 1 - else: - if pattern[pi] == '*': # recurse - if (pi + 1) == len(pattern): - return True - while si < len(fname): - if fncmp(fname[si:], pattern[pi+1:]): - return True - else: - si += 1 - return False - else: - return False - if pi == len(pattern.rstrip("*")) and si == len(fname): - return True - else: - return False - - -def ftpserver(not_stop_on_quit): - - DATA_PORT = 13333 - - ftpsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - datasocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - - ftpsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - datasocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - - ftpsocket.bind(socket.getaddrinfo("0.0.0.0", 21)[0][4]) - datasocket.bind(socket.getaddrinfo("0.0.0.0", DATA_PORT)[0][4]) - - ftpsocket.listen(1) - ftpsocket.settimeout(None) - datasocket.listen(1) - datasocket.settimeout(None) - - msg_250_OK = '250 OK\r\n' - msg_550_fail = '550 Failed\r\n' - # check for an active interface, STA first - wlan = network.WLAN(network.STA_IF) - if wlan.active(): - addr = wlan.ifconfig()[0] - else: - wlan = network.WLAN(network.AP_IF) - if wlan.active(): - addr = wlan.ifconfig()[0] - else: - print("No active connection") - return - - print("FTP Server started on ", addr) - try: - dataclient = None - fromname = None - do_run = True - while do_run: - cl, remote_addr = ftpsocket.accept() - cl.settimeout(300) - cwd = '/' - try: - # print("FTP connection from:", remote_addr) - cl.sendall("220 Hello, this is the ESP8266/ESP32.\r\n") - while True: - gc.collect() - data = cl.readline().decode("utf-8").rstrip("\r\n") - if len(data) <= 0: - print("Client disappeared") - do_run = not_stop_on_quit - break - - command = data.split(" ")[0].upper() - payload = data[len(command):].lstrip() - - path = get_absolute_path(cwd, payload) - - print("Command={}, Payload={}".format(command, payload)) - - if command == "USER": - cl.sendall("230 Logged in.\r\n") - elif command == "SYST": - cl.sendall("215 UNIX Type: L8\r\n") - elif command == "NOOP": - cl.sendall("200 OK\r\n") - elif command == "FEAT": - cl.sendall("211 no-features\r\n") - elif command == "PWD" or command == "XPWD": - cl.sendall('257 "{}"\r\n'.format(cwd)) - elif command == "CWD" or command == "XCWD": - try: - files = uos.listdir(path) - cwd = path - cl.sendall(msg_250_OK) - except: - cl.sendall(msg_550_fail) - elif command == "CDUP": - cwd = get_absolute_path(cwd, "..") - cl.sendall(msg_250_OK) - elif command == "TYPE": - # probably should switch between binary and not - cl.sendall('200 Transfer mode set\r\n') - elif command == "SIZE": - try: - size = uos.stat(path)[6] - cl.sendall('213 {}\r\n'.format(size)) - except: - cl.sendall(msg_550_fail) - elif command == "QUIT": - cl.sendall('221 Bye.\r\n') - do_run = not_stop_on_quit - break - elif command == "PASV": - cl.sendall('227 Entering Passive Mode ({},{},{}).\r\n'.format( - addr.replace('.', ','), DATA_PORT >> 8, DATA_PORT % 256)) - dataclient, data_addr = datasocket.accept() - print("FTP Data connection from:", data_addr) - DATA_PORT = 13333 - active = False - elif command == "PORT": - items = payload.split(",") - if len(items) >= 6: - data_addr = '.'.join(items[:4]) - if data_addr == "127.0.1.1": - # replace by command session addr - data_addr = remote_addr - DATA_PORT = int(items[4]) * 256 + int(items[5]) - dataclient = socket.socket(socket.AF_INET, - socket.SOCK_STREAM) - dataclient.settimeout(10) - dataclient.connect((data_addr, DATA_PORT)) - print("FTP Data connection with:", data_addr) - cl.sendall('200 OK\r\n') - active = True - else: - cl.sendall('504 Fail\r\n') - elif command == "LIST" or command == "NLST": - if not payload.startswith("-"): - place = path - else: - place = cwd - try: - cl.sendall("150 Here comes the " - "directory listing.\r\n") - send_list_data(place, dataclient, - command == "LIST" or payload == "-l") - cl.sendall("226 Listed.\r\n") - except: - cl.sendall(msg_550_fail) - if dataclient is not None: - dataclient.close() - dataclient = None - elif command == "RETR": - try: - cl.sendall("150 Opening data connection.\r\n") - send_file_data(path, dataclient) - cl.sendall("226 Transfer complete.\r\n") - except: - cl.sendall(msg_550_fail) - if dataclient is not None: - dataclient.close() - dataclient = None - elif command == "STOR": - try: - cl.sendall("150 Ok to send data.\r\n") - save_file_data(path, dataclient) - cl.sendall("226 Transfer complete.\r\n") - except: - cl.sendall(msg_550_fail) - if dataclient is not None: - dataclient.close() - dataclient = None - elif command == "DELE": - try: - uos.remove(path) - cl.sendall(msg_250_OK) - except: - cl.sendall(msg_550_fail) - elif command == "RMD" or command == "XRMD": - try: - uos.rmdir(path) - cl.sendall(msg_250_OK) - except: - cl.sendall(msg_550_fail) - elif command == "MKD" or command == "XMKD": - try: - uos.mkdir(path) - cl.sendall(msg_250_OK) - except: - cl.sendall(msg_550_fail) - elif command == "RNFR": - fromname = path - cl.sendall("350 Rename from\r\n") - elif command == "RNTO": - if fromname is not None: - try: - uos.rename(fromname, path) - cl.sendall(msg_250_OK) - except: - cl.sendall(msg_550_fail) - else: - cl.sendall(msg_550_fail) - fromname = None - elif command == "MDTM": - try: - tm=localtime(uos.stat(path)[8]) - cl.sendall('213 {:04d}{:02d}{:02d}{:02d}{:02d}{:02d}\r\n'.format(*tm[0:6])) - except: - cl.sendall('550 Fail\r\n') - elif command == "STAT": - if payload == "": - cl.sendall("211-Connected to ({})\r\n" - " Data address ({})\r\n" - "211 TYPE: Binary STRU: File " - "MODE: Stream\r\n".format( - remote_addr[0], addr)) - else: - cl.sendall("213-Directory listing:\r\n") - send_list_data(path, cl, True) - cl.sendall("213 Done.\r\n") - else: - cl.sendall("502 Unsupported command.\r\n") - print("Unsupported command {} with payload {}". - format(command, payload)) - except Exception as err: - print(err) - - finally: - cl.close() - cl = None - finally: - datasocket.close() - ftpsocket.close() - if dataclient is not None: - dataclient.close() - -try: - import _thread - _thread.start_new_thread(ftpserver, ((True,))) -except: - ftpserver(False) diff --git a/pixo/FTP-Server-for-ESP8266-ESP32-and-PYBD/uftpd.py b/pixo/FTP-Server-for-ESP8266-ESP32-and-PYBD/uftpd.py deleted file mode 100644 index d9e40f9..0000000 --- a/pixo/FTP-Server-for-ESP8266-ESP32-and-PYBD/uftpd.py +++ /dev/null @@ -1,492 +0,0 @@ -# -# Small ftp server for ESP8266 Micropython -# Based on the work of chrisgp - Christopher Popp and pfalcon - Paul Sokolovsky -# -# The server accepts passive mode only. It runs in background. -# Start the server with: -# -# import uftpd -# uftpd.start([port = 21][, verbose = level]) -# -# port is the port number (default 21) -# verbose controls the level of printed activity messages, values 0, 1, 2 -# -# Copyright (c) 2016 Christopher Popp (initial ftp server framework) -# Copyright (c) 2016 Paul Sokolovsky (background execution control structure) -# Copyright (c) 2016 Robert Hammelrath (putting the pieces together and a -# few extensions) -# Distributed under MIT License -# -import socket -import network -import uos -import gc -from time import sleep_ms, localtime -from micropython import alloc_emergency_exception_buf - -# constant definitions -_CHUNK_SIZE = const(1024) -_SO_REGISTER_HANDLER = const(20) -_COMMAND_TIMEOUT = const(300) -_DATA_TIMEOUT = const(100) -_DATA_PORT = const(13333) - -# Global variables -ftpsocket = None -datasocket = None -client_list = [] -verbose_l = 0 -client_busy = False -# Interfaces: (IP-Address (string), IP-Address (integer), Netmask (integer)) -AP_addr = ("0.0.0.0", 0, 0xffffff00) -STA_addr = ("0.0.0.0", 0, 0xffffff00) - -_month_name = ("", "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec") - - -class FTP_client: - - def __init__(self, ftpsocket): - global AP_addr, STA_addr - self.command_client, self.remote_addr = ftpsocket.accept() - self.remote_addr = self.remote_addr[0] - self.command_client.settimeout(_COMMAND_TIMEOUT) - log_msg(1, "FTP Command connection from:", self.remote_addr) - self.command_client.setsockopt(socket.SOL_SOCKET, - _SO_REGISTER_HANDLER, - self.exec_ftp_command) - self.command_client.sendall("220 Hello, this is the ESP8266.\r\n") - self.cwd = '/' - self.fromname = None -# self.logged_in = False - self.act_data_addr = self.remote_addr - self.DATA_PORT = 20 - self.active = True - # check which interface was used by comparing the caller's ip - # adress with the ip adresses of STA and AP; consider netmask; - # select IP address for passive mode - if ((AP_addr[1] & AP_addr[2]) == - (num_ip(self.remote_addr) & AP_addr[2])): - self.pasv_data_addr = AP_addr[0] - elif ((STA_addr[1] & STA_addr[2]) == - (num_ip(self.remote_addr) & STA_addr[2])): - self.pasv_data_addr = STA_addr[0] - else: - self.pasv_data_addr = "0.0.0.0" # Ivalid value - - def send_list_data(self, path, data_client, full): - try: - for fname in uos.listdir(path): - data_client.sendall(self.make_description(path, fname, full)) - except: # path may be a file name or pattern - path, pattern = self.split_path(path) - try: - for fname in uos.listdir(path): - if self.fncmp(fname, pattern): - data_client.sendall( - self.make_description(path, fname, full)) - except: - pass - - def make_description(self, path, fname, full): - global _month_name - if full: - stat = uos.stat(self.get_absolute_path(path, fname)) - file_permissions = ("drwxr-xr-x" - if (stat[0] & 0o170000 == 0o040000) - else "-rw-r--r--") - file_size = stat[6] - tm = localtime(stat[7]) - if tm[0] != localtime()[0]: - description = "{} 1 owner group {:>10} {} {:2} {:>5} {}\r\n".\ - format(file_permissions, file_size, - _month_name[tm[1]], tm[2], tm[0], fname) - else: - description = "{} 1 owner group {:>10} {} {:2} {:02}:{:02} {}\r\n".\ - format(file_permissions, file_size, - _month_name[tm[1]], tm[2], tm[3], tm[4], fname) - else: - description = fname + "\r\n" - return description - - def send_file_data(self, path, data_client): - with open(path, "r") as file: - chunk = file.read(_CHUNK_SIZE) - while len(chunk) > 0: - data_client.sendall(chunk) - chunk = file.read(_CHUNK_SIZE) - data_client.close() - - def save_file_data(self, path, data_client, mode): - with open(path, mode) as file: - chunk = data_client.recv(_CHUNK_SIZE) - while len(chunk) > 0: - file.write(chunk) - chunk = data_client.recv(_CHUNK_SIZE) - data_client.close() - - def get_absolute_path(self, cwd, payload): - # Just a few special cases "..", "." and "" - # If payload start's with /, set cwd to / - # and consider the remainder a relative path - if payload.startswith('/'): - cwd = "/" - for token in payload.split("/"): - if token == '..': - cwd = self.split_path(cwd)[0] - elif token != '.' and token != '': - if cwd == '/': - cwd += token - else: - cwd = cwd + '/' + token - return cwd - - def split_path(self, path): # instead of path.rpartition('/') - tail = path.split('/')[-1] - head = path[:-(len(tail) + 1)] - return ('/' if head == '' else head, tail) - - # compare fname against pattern. Pattern may contain - # the wildcards ? and *. - def fncmp(self, fname, pattern): - pi = 0 - si = 0 - while pi < len(pattern) and si < len(fname): - if (fname[si] == pattern[pi]) or (pattern[pi] == '?'): - si += 1 - pi += 1 - else: - if pattern[pi] == '*': # recurse - if pi == len(pattern.rstrip("*?")): # only wildcards left - return True - while si < len(fname): - if self.fncmp(fname[si:], pattern[pi + 1:]): - return True - else: - si += 1 - return False - else: - return False - if pi == len(pattern.rstrip("*")) and si == len(fname): - return True - else: - return False - - def open_dataclient(self): - if self.active: # active mode - data_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - data_client.settimeout(_DATA_TIMEOUT) - data_client.connect((self.act_data_addr, self.DATA_PORT)) - log_msg(1, "FTP Data connection with:", self.act_data_addr) - else: # passive mode - data_client, data_addr = datasocket.accept() - log_msg(1, "FTP Data connection with:", data_addr[0]) - return data_client - - def exec_ftp_command(self, cl): - global datasocket - global client_busy - global my_ip_addr - - try: - gc.collect() - - data = cl.readline().decode("utf-8").rstrip("\r\n") - - if len(data) <= 0: - # No data, close - # This part is NOT CLEAN; there is still a chance that a - # closing data connection will be signalled as closing - # command connection - log_msg(1, "*** No data, assume QUIT") - close_client(cl) - return - - if client_busy: # check if another client is busy - cl.sendall("400 Device busy.\r\n") # tell so the remote client - return # and quit - client_busy = True # now it's my turn - - # check for log-in state may done here, like - # if self.logged_in == False and not command in\ - # ("USER", "PASS", "QUIT"): - # cl.sendall("530 Not logged in.\r\n") - # return - - command = data.split()[0].upper() - payload = data[len(command):].lstrip() # partition is missing - path = self.get_absolute_path(self.cwd, payload) - log_msg(1, "Command={}, Payload={}".format(command, payload)) - - if command == "USER": - # self.logged_in = True - cl.sendall("230 Logged in.\r\n") - # If you want to see a password,return - # "331 Need password.\r\n" instead - # If you want to reject an user, return - # "530 Not logged in.\r\n" - elif command == "PASS": - # you may check here for a valid password and return - # "530 Not logged in.\r\n" in case it's wrong - # self.logged_in = True - cl.sendall("230 Logged in.\r\n") - elif command == "SYST": - cl.sendall("215 UNIX Type: L8\r\n") - elif command in ("TYPE", "NOOP", "ABOR"): # just accept & ignore - cl.sendall('200 OK\r\n') - elif command == "QUIT": - cl.sendall('221 Bye.\r\n') - close_client(cl) - elif command == "PWD" or command == "XPWD": - cl.sendall('257 "{}"\r\n'.format(self.cwd)) - elif command == "CWD" or command == "XCWD": - try: - if (uos.stat(path)[0] & 0o170000) == 0o040000: - self.cwd = path - cl.sendall('250 OK\r\n') - else: - cl.sendall('550 Fail\r\n') - except: - cl.sendall('550 Fail\r\n') - elif command == "PASV": - cl.sendall('227 Entering Passive Mode ({},{},{}).\r\n'.format( - self.pasv_data_addr.replace('.', ','), - _DATA_PORT >> 8, _DATA_PORT % 256)) - self.active = False - elif command == "PORT": - items = payload.split(",") - if len(items) >= 6: - self.act_data_addr = '.'.join(items[:4]) - if self.act_data_addr == "127.0.1.1": - # replace by command session addr - self.act_data_addr = self.remote_addr - self.DATA_PORT = int(items[4]) * 256 + int(items[5]) - cl.sendall('200 OK\r\n') - self.active = True - else: - cl.sendall('504 Fail\r\n') - elif command == "LIST" or command == "NLST": - if payload.startswith("-"): - option = payload.split()[0].lower() - path = self.get_absolute_path( - self.cwd, payload[len(option):].lstrip()) - else: - option = "" - try: - data_client = self.open_dataclient() - cl.sendall("150 Directory listing:\r\n") - self.send_list_data(path, data_client, - command == "LIST" or 'l' in option) - cl.sendall("226 Done.\r\n") - data_client.close() - except: - cl.sendall('550 Fail\r\n') - if data_client is not None: - data_client.close() - elif command == "RETR": - try: - data_client = self.open_dataclient() - cl.sendall("150 Opened data connection.\r\n") - self.send_file_data(path, data_client) - # if the next statement is reached, - # the data_client was closed. - data_client = None - cl.sendall("226 Done.\r\n") - except: - cl.sendall('550 Fail\r\n') - if data_client is not None: - data_client.close() - elif command == "STOR" or command == "APPE": - try: - data_client = self.open_dataclient() - cl.sendall("150 Opened data connection.\r\n") - self.save_file_data(path, data_client, - "w" if command == "STOR" else "a") - # if the next statement is reached, - # the data_client was closed. - data_client = None - cl.sendall("226 Done.\r\n") - except: - cl.sendall('550 Fail\r\n') - if data_client is not None: - data_client.close() - elif command == "SIZE": - try: - cl.sendall('213 {}\r\n'.format(uos.stat(path)[6])) - except: - cl.sendall('550 Fail\r\n') - elif command == "MDTM": - try: - tm=localtime(uos.stat(path)[8]) - cl.sendall('213 {:04d}{:02d}{:02d}{:02d}{:02d}{:02d}\r\n'.format(*tm[0:6])) - except: - cl.sendall('550 Fail\r\n') - elif command == "STAT": - if payload == "": - cl.sendall("211-Connected to ({})\r\n" - " Data address ({})\r\n" - " TYPE: Binary STRU: File MODE: Stream\r\n" - " Session timeout {}\r\n" - "211 Client count is {}\r\n".format( - self.remote_addr, self.pasv_data_addr, - _COMMAND_TIMEOUT, len(client_list))) - else: - cl.sendall("213-Directory listing:\r\n") - self.send_list_data(path, cl, True) - cl.sendall("213 Done.\r\n") - elif command == "DELE": - try: - uos.remove(path) - cl.sendall('250 OK\r\n') - except: - cl.sendall('550 Fail\r\n') - elif command == "RNFR": - try: - # just test if the name exists, exception if not - uos.stat(path) - self.fromname = path - cl.sendall("350 Rename from\r\n") - except: - cl.sendall('550 Fail\r\n') - elif command == "RNTO": - try: - uos.rename(self.fromname, path) - cl.sendall('250 OK\r\n') - except: - cl.sendall('550 Fail\r\n') - self.fromname = None - elif command == "CDUP" or command == "XCUP": - self.cwd = self.get_absolute_path(self.cwd, "..") - cl.sendall('250 OK\r\n') - elif command == "RMD" or command == "XRMD": - try: - uos.rmdir(path) - cl.sendall('250 OK\r\n') - except: - cl.sendall('550 Fail\r\n') - elif command == "MKD" or command == "XMKD": - try: - uos.mkdir(path) - cl.sendall('250 OK\r\n') - except: - cl.sendall('550 Fail\r\n') - else: - cl.sendall("502 Unsupported command.\r\n") - # log_msg(2, - # "Unsupported command {} with payload {}".format(command, - # payload)) - # handle unexpected errors - except Exception as err: - log_msg(1, "Exception in exec_ftp_command: {}".format(err)) - # tidy up before leaving - client_busy = False - - -def log_msg(level, *args): - global verbose_l - if verbose_l >= level: - print(*args) - - -# close client and remove it from the list -def close_client(cl): - cl.setsockopt(socket.SOL_SOCKET, _SO_REGISTER_HANDLER, None) - cl.close() - for i, client in enumerate(client_list): - if client.command_client == cl: - del client_list[i] - break - - -def accept_ftp_connect(ftpsocket): - # Accept new calls for the server - try: - client_list.append(FTP_client(ftpsocket)) - except: - log_msg(1, "Attempt to connect failed") - # try at least to reject - try: - temp_client, temp_addr = ftpsocket.accept() - temp_client.close() - except: - pass - - -def num_ip(ip): - items = ip.split(".") - return (int(items[0]) << 24 | int(items[1]) << 16 | - int(items[2]) << 8 | int(items[3])) - - -def stop(): - global ftpsocket, datasocket - global client_list - global client_busy - - for client in client_list: - client.command_client.setsockopt(socket.SOL_SOCKET, - _SO_REGISTER_HANDLER, None) - client.command_client.close() - del client_list - client_list = [] - client_busy = False - if ftpsocket is not None: - ftpsocket.setsockopt(socket.SOL_SOCKET, _SO_REGISTER_HANDLER, None) - ftpsocket.close() - if datasocket is not None: - datasocket.close() - - -# start listening for ftp connections on port 21 -def start(port=21, verbose=0, splash=True): - global ftpsocket, datasocket - global verbose_l - global client_list - global client_busy - global AP_addr, STA_addr - - alloc_emergency_exception_buf(100) - verbose_l = verbose - client_list = [] - client_busy = False - - ftpsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - datasocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - - ftpsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - datasocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - - ftpsocket.bind(('0.0.0.0', port)) - datasocket.bind(('0.0.0.0', _DATA_PORT)) - - ftpsocket.listen(0) - datasocket.listen(0) - - datasocket.settimeout(10) - ftpsocket.setsockopt(socket.SOL_SOCKET, - _SO_REGISTER_HANDLER, accept_ftp_connect) - - wlan = network.WLAN(network.AP_IF) - if wlan.active(): - ifconfig = wlan.ifconfig() - # save IP address string and numerical values of IP adress and netmask - AP_addr = (ifconfig[0], num_ip(ifconfig[0]), num_ip(ifconfig[1])) - if splash: - print("FTP server started on {}:{}".format(ifconfig[0], port)) - wlan = network.WLAN(network.STA_IF) - if wlan.active(): - ifconfig = wlan.ifconfig() - # save IP address string and numerical values of IP adress and netmask - STA_addr = (ifconfig[0], num_ip(ifconfig[0]), num_ip(ifconfig[1])) - if splash: - print("FTP server started on {}:{}".format(ifconfig[0], port)) - - -def restart(port=21, verbose=0, splash=True): - stop() - sleep_ms(200) - start(port, verbose, splash) - - -start(splash=True)