def exec_ftp_command(self, cl): global datasocket global client_busy global my_ip_addr try: 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": result = False try: data_client = self.open_dataclient() cl.sendall("150 Opened data connection.\r\n") if path == "/fpga": import ecp5 ecp5.prog_stream(data_client,_CHUNK_SIZE) result = ecp5.prog_close() data_client.close() elif path.startswith("/flash@"): import ecp5 dummy, addr = path.split("@") addr = int(addr) result = ecp5.flash_stream(data_client,addr) ecp5.flash_close() del addr, dummy data_client.close() elif path.startswith("/sd@"): import sdraw dummy, addr = path.split("@") addr = int(addr) sd_raw = sdraw.sdraw() result = sd_raw.sd_write_stream(data_client,addr) del sd_raw, addr, dummy data_client.close() else: self.save_file_data(path, data_client, "w" if command == "STOR" else "a") result = True # if the next statement is reached, # the data_client was closed. data_client = None except: if data_client is not None: data_client.close() if result: cl.sendall("226 Done.\r\n") else: cl.sendall('550 Fail\r\n') del result elif command == "SIZE": try: cl.sendall('213 {}\r\n'.format(uos.stat(path)[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') elif command == "SITE": if path == "/mount": if self.mount(): cl.sendall('250 OK\r\n') else: cl.sendall('550 Fail\r\n') elif path == "/umount": if self.umount(): cl.sendall('250 OK\r\n') else: cl.sendall('550 Fail\r\n') elif path == "/passthru": import ecp5 ecp5.passthru() cl.sendall('250 OK passthru\r\n') elif path.endswith(".bit") or path.endswith(".bit.gz"): try: import ecp5 if ecp5.prog(path, close=False): if path.startswith("/sd/"): try: self.umount() cl.sendall('111 umount /sd OK\r\n') except: cl.sendall('411 umount /sd Fail\r\n') if ecp5.prog_close(): cl.sendall('250 OK\r\n') else: cl.sendall('550 Fail\r\n') else: cl.sendall('550 Fail\r\n') except: cl.sendall('550 Fail\r\n') else: if path.startswith("/"): exe=path[1:] else: exe=path try: exec(exe) cl.sendall('250 OK '+exe+'\r\n') except: cl.sendall('550 Fail '+exe+'\r\n') del exe 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 passthru(): import ecp5 ecp5.passthru()