class ftp_server: def __init__(self): authorizer = DummyAuthorizer() authorizer.add_user('user', '12345', 'collected/', perm='elradfmwMT') authorizer.add_anonymous("collected/", perm='elradfmwMT') self.handler = MyHandler self.handler.authorizer = authorizer self.obj = ThreadedFTPServer(("0.0.0.0", 21), self.handler) self.filename = "" # set a limit for connections #self.max_cons = 256 #self.max_cons_per_ip = 5 def get_filename(self): global filename return filename def _run_server(self): self.obj.serve_forever() def start(self): srv = threading.Thread(target=self._run_server) srv.deamon = True srv.start() def stop(self): self.obj.close_all()
def ftpserver(): """Provide a ftp server with virtual files""" handler = FTPTestHandler handler.abstracted_fs = MyTestFS authorizer = MyDummyAuthorizer() home = os.curdir authorizer.add_user('user', '12345', home, perm='elrmwM') handler.authorizer = authorizer server = ThreadedFTPServer(('localhost', 12345), handler) createThread('FTP', server.serve_forever) yield handler server.close_all()
class FTPService(): def __init__(self, *args, **kwargs): self.ftp_root = kodi.vfs.translate_path( kodi.get_setting('root_directory')) self.ftp_log = kodi.vfs.join(kodi.get_profile(), "pyftpd.log") self.cert_file = vfs.join(kodi.get_profile(), "pyftpd.pem") if not vfs.exists(self.cert_file): vfs.cp(vfs.join(kodi.get_path(), "pyftpd.pem"), self.cert_file) def start(self): class Monitor(xbmc.Monitor): def onSettingsChanged(self): pass monitor = Monitor() kodi.log("Service Starting...") authorizer = DummyAuthorizer() authorizer.add_user(kodi.get_setting('ftp_user'), kodi.get_setting('ftp_pass'), self.ftp_root, perm='elradfmwMT') if kodi.get_setting('use_ssl') == 'true': from pyftpdlib.handlers import TLS_FTPHandler as handler handler.certfile = self.cert_file else: from pyftpdlib.handlers import FTPHandler as handler handler.banner = "PyFTPd ready." handler.authorizer = authorizer address = ('', kodi.get_setting('server_port')) import logging logging.basicConfig(filename=self.ftp_log, level=logging.INFO) self.ftpd = ThreadedFTPServer(address, handler) self.ftpd.max_cons = 256 self.ftpd.max_cons_per_ip = 5 self.server = Thread(target=self.ftpd.serve_forever) self.server.start() if is_depricated: while not xbmc.abortRequested: xbmc.sleep(1000) else: while not monitor.abortRequested(): xbmc.sleep(1000) self.shutdown() def shutdown(self): kodi.log("Service Stopping...") self.ftpd.close_all() self.server.join()
class PyFtpServer(Thread): def __init__(self, path, user, pwd, port): super(PyFtpServer, self).__init__(name="PyFtpServer") self._stop_event = threading.Event() self._path = path self._user = user self._pwd = pwd self._port = port self._server = None self._lock = Lock() def run(self): with self._lock: # Instantiate a dummy authorizer for managing 'virtual' users authorizer = DummyAuthorizer() # Define a new user having full r/w permissions and a read-only anonymous user authorizer.add_user(self._user, self._pwd, self._path, perm='elradfmwMT') # Instantiate FTP handler class handler = FTPHandler handler.authorizer = authorizer # Define a customized banner (string returned when client connects) handler.banner = "pyftpdlib based ftpd ready." # Instantiate FTP server class and listen on 0.0.0.0:self._port address = ('0.0.0.0', int(self._port)) try: self._server = ThreadedFTPServer(address, handler) except Exception as e: logging.error(f"Exception while start PyFtpServer: {e}") if self._server is not None: # set a limit for connections self._server.max_cons = 256 self._server.max_cons_per_ip = 5 # start ftp server self._server.serve_forever() def terminate(self): self._server.close_all() def success(self): with self._lock: return self._server is not None
class FTPServerWrapper(object): class TestHandler(FTPHandler): def on_incomplete_file_received(self, file): pass print('on_incomplete_file_received({})'.format(file)) # TODO: delete incomplete file, mark job as unfinished class FTPServerRunner(Thread): def __init__(self, server): self.server = server super(FTPServerWrapper.FTPServerRunner, self).__init__() def run(self): self.server.serve_forever() def __init__(self, port=('', FTP_SERVER_PORT)): self.auth = DummyAuthorizer() self.handler = FTPServerWrapper.TestHandler self.handler.authorizer = self.auth self.server = ThreadedFTPServer(port, self.handler) self.port = port self.users = [] self.t = FTPServerWrapper.FTPServerRunner(self.server) self.t.start() def __del__(self): self.server.close_all() self.server.close() del self.server def add_user(self, name, r_dir, w_dir): logging.info('adding ftp user {}'.format(name)) self.users.append(name) self.auth.add_user('r-' + str(name), name, r_dir, perm='rl') self.auth.add_user('w-' + str(name), name, w_dir, perm='wl') def rem_user(self, name): logging.info('removing ftp user {}'.format(name)) try: self.users.remove(name) self.auth.remove_user('r-' + str(name)) self.auth.remove_user('w-' + str(name)) except ValueError: pass
class Example(QtWidgets.QMainWindow): def __init__(self): super(Example, self).__init__() uic.loadUi('gui.ui', self) self.pushButtonStart.clicked.connect(self.onClick) self.pushButtonStop.clicked.connect(self.onStop) self.authorizer = DummyAuthorizer() self.authorizer.add_anonymous(os.getcwd()) self.handler = FTPHandler self.handler.authorizer = self.authorizer self.handler.banner = "pyftpdlib based ftpd ready." # set a limit for connections self.max_cons = 256 self.max_cons_per_ip = 5 def onClick(self): print('start') user = self.lineEditUser.text() passw = self.lineEditPassword.text() self.authorizer.add_user(user, passw, '.', perm='elrw') self.address = ('127.0.0.1', 2100) self.server = ThreadedFTPServer(self.address, self.handler) QMessageBox.information(self, "FTP Server started", "FTP Server started") self.start() def _run_server(self): self.server.serve_forever() def start(self): srv = threading.Thread(target=self._run_server) srv.deamon = True srv.start() def onStop(self): print('stop') self.server.close_all() QMessageBox.information(self, "FTP Server stopped", "FTP Server stopped")
class FTPThread(Thread): def __init__(self): super().__init__() authorizer = DummyAuthorizer() authorizer.add_user("ons", "ons", "./ftp", perm="elradfmw") handler = FTPHandler handler.authorizer = authorizer handler.abstracted_fs = UnixFilesystem self.server = ThreadedFTPServer( (settings.FTP_HOST, str(settings.FTP_PORT)), handler) def run(self): logger.debug("Calling enter") try: self.server.serve_forever() except OSError: logger.debug("Thrown during shut down") exit() def stop(self): logger.debug("Calling exit") self.server.close_all()
class FTPListener(object): def taste(self, data, dport): # See RFC5797 for full command list. Many of these commands are not likely # to be used but are included in case malware uses FTP in unexpected ways base_ftp_commands = [ 'abor', 'acct', 'allo', 'appe', 'cwd', 'dele', 'help', 'list', 'mode', 'nlst', 'noop', 'pass', 'pasv', 'port', 'quit', 'rein', 'rest', 'retr', 'rnfr', 'rnto', 'site', 'stat', 'stor', 'stru', 'type', 'user' ] opt_ftp_commands = [ 'cdup', 'mkd', 'pwd', 'rmd', 'smnt', 'stou', 'syst' ] confidence = 1 if dport == 21 else 0 data = data.lstrip().lower() for command in base_ftp_commands + opt_ftp_commands: if data.startswith(command): return confidence + 1 return confidence def __init__(self, config, name='FTPListener', logging_level=logging.INFO, running_listeners=None, diverter=None ): self.logger = logging.getLogger(name) self.logger.setLevel(logging_level) self.config = config self.name = name self.local_ip = config.get('ipaddr') self.server = None self.running_listeners = running_listeners self.diverter = diverter self.name = 'FTP' self.port = self.config.get('port', 21) self.logger.debug('Starting...') self.logger.debug('Initialized with config:') for key, value in config.iteritems(): self.logger.debug(' %10s: %s', key, value) # Initialize ftproot directory path = self.config.get('ftproot','defaultFiles') self.ftproot_path = ListenerBase.abs_config_path(path) if self.ftproot_path is None: self.logger.error('Could not locate ftproot directory: %s', path) sys.exit(1) def expand_ports(self, ports_list): ports = [] for i in ports_list.split(','): if '-' not in i: ports.append(int(i)) else: l,h = map(int, i.split('-')) ports+= range(l,h+1) return ports def start(self): self.authorizer = DummyAuthorizer() if self.config.get('usessl') == 'Yes': self.logger.debug('Using SSL socket.') keyfile_path = 'listeners/ssl_utils/privkey.pem' keyfile_path = ListenerBase.abs_config_path(keyfile_path) if keyfile_path is None: self.logger.error('Could not locate %s', keyfile_path) sys.exit(1) self.handler = TLS_FakeFTPHandler self.handler.certfile = keyfile_path else: self.handler = FakeFTPHandler self.handler.banner = self.genBanner() self.handler.ftproot_path = self.ftproot_path self.handler.abstracted_fs = FakeFS self.handler.authorizer = self.authorizer self.handler.passive_ports = self.expand_ports(self.config.get('pasvports', '60000-60010')) self.server = ThreadedFTPServer((self.local_ip, int(self.config['port'])), self.handler) # Override pyftpdlib logger name logging.getLogger('pyftpdlib').name = self.name self.server_thread = threading.Thread(target=self.server.serve_forever) self.server_thread.daemon = True self.server_thread.start() def stop(self): self.logger.debug('Stopping...') if self.server: self.server.close_all() def genBanner(self): bannerfactory = BannerFactory.BannerFactory() return bannerfactory.genBanner(self.config, BANNERS)
class FTPListener(): def __init__(self, config, name = 'FTPListener', logging_level = logging.INFO): self.logger = logging.getLogger(name) self.logger.setLevel(logging_level) self.config = config self.name = name self.local_ip = '0.0.0.0' self.server = None self.logger.info('Starting...') self.logger.debug('Initialized with config:') for key, value in config.iteritems(): self.logger.debug(' %10s: %s', key, value) # Initialize webroot directory self.ftproot_path = self.config.get('ftproot','defaultFiles') def expand_ports(self, ports_list): ports = [] for i in ports_list.split(','): if '-' not in i: ports.append(int(i)) else: l,h = map(int, i.split('-')) ports+= range(l,h+1) return ports def start(self): self.authorizer = DummyAuthorizer() if self.config.get('usessl') == 'Yes': self.logger.debug('Using SSL socket.') keyfile_path = 'privkey.pem' self.handler = TLS_FakeFTPHandler self.handler.certfile = keyfile_path else: self.handler = FakeFTPHandler self.handler.ftproot_path = self.ftproot_path self.handler.abstracted_fs = FakeFS self.handler.authorizer = self.authorizer self.handler.passive_ports = self.expand_ports(self.config.get('pasvports', '60000-60010')) self.server = ThreadedFTPServer((self.local_ip, int(self.config['port'])), self.handler) # Override pyftpdlib logger name logging.getLogger('pyftpdlib').name = self.name self.server_thread = threading.Thread(target=self.server.serve_forever) self.server_thread.daemon = True self.server_thread.start() def stop(self): self.logger.debug('Stopping...') if self.server: self.server.close_all()
class CrestronDevice(object): def __init__(self, args): self.args = args self.console_prompt = "" self.device_ip_address = self.args.ip_address self.dry_run = self.args.dry_run self.forcessh = self.args.force_ssh self.ftp_output_path = self.args.ftp_dir self.ftp_server = None self.ftp_server_ip_address = self.args.ftp_server self.local_path = self.args.ftp_dir self.sshclient = None self.sock = None def start_ftp_server(self): if not self.dry_run and self.args.local_ftp_server: self.ftp_server = None try: handler = FTPHandler authorizer = DummyAuthorizer() if(self.args.ftp_username and self.args.ftp_username != ""): authorizer.add_user(self.args.ftp_username, self.args.ftp_password, self.ftp_output_path, perm='elradfmwMT') else: authorizer.add_anonymous(self.ftp_output_path, perm='elradfmwMT') handler.authorizer = authorizer self.ftp_server = ThreadedFTPServer((self.args.local_ftp_interface, self.args.local_ftp_port), handler) self.ftp_server.serve_forever(blocking=False) if self.args.local_ftp_interface == "": print("Started local FTP server: ::%s" % (self.args.local_ftp_port)) else: print("Started local FTP server: %s:%s" % (self.args.local_ftp_interface, self.args.local_ftp_port)) except: if self.args.local_ftp_interface == "": print("Failed to start local FTP server on: ::%s" % (self.args.local_ftp_port)) else: print("Failed to start local FTP server on: %s:%s" % (self.args.local_ftp_interface, self.args.local_ftp_port)) exit() def stop_ftp_server(self): if self.ftp_server: print("[*] Stopping local FTP server...") try: self.ftp_server.close_all() print("[+] Local FTP server stopped.") except: print("[-] Failed to stop local FTP server.") pass def open_device_connection(self): if self.forcessh: print("[*] Establishing Paramiko SSH session with %s:%s ..." % (self.device_ip_address, SSH_PORT)) self.sshclient = paramiko.client.SSHClient() try: self.sshclient.set_missing_host_key_policy(paramiko.AutoAddPolicy()) self.sshclient.load_system_host_keys() self.sshclient.connect(self.device_ip_address, port=22, username=self.args.username, password=self.args.password, timeout=SOCKET_TIMEOUT) print("[+] Successfully established Paramiko SSH session.") return True except: self.sshclient = None print("[-] Error: Unable to establish Paramiko SSH session with device.") return False else: print("[*] Establishing CTP session with %s:%s ..." % (self.device_ip_address, CTP_PORT)) try: self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.settimeout(SOCKET_TIMEOUT) self.sock.connect((self.device_ip_address, CTP_PORT)) print("[+] Successfully established CTP session.") return True except: self.sock = None print("[-] Error: Unable to establish CTP session with device.") return False def close_device_connection(self): print("[*] Shutting down device connections...") if self.sshclient: try: self.sshclient.close() print("[+] Closed Paramiko SSH session.") except: print("[-] Failed to close Paramiko SSH session.") pass if self.sock: try: self.sock.close() print("[+] Closed CTP session.") except: print("[-] Failed to close CTP session.") pass def get_console_prompt(self): """ Determine the device console prompt """ data = "" for _unused in range(0, MAX_RETRIES): if self.sshclient: stdin,stdout,stderr=self.sshclient.exec_command("ver") data = str(stdout.readlines()) search = re.findall("([\w-]{3,30})\ ", data, re.MULTILINE) else: self.sock.sendall(CR+CR) data += self.sock.recv(BUFF_SIZE) search = re.findall("[\n\r]([\w-]{3,30})>", data, re.MULTILINE) sleep(GLOBAL_SLEEP_VALUE) if search: self.console_prompt = search[0] print("[!] Console prompt is: %s" % (self.console_prompt)) if self.console_prompt == "MERCURY": print("[-] Mercury currently unsupported due to Crestron engin.err.uity (I guess...).") return False self.ftp_output_path = os.path.join(self.local_path, ("Crestron_Device_%s" % (self.console_prompt))) if not os.path.isdir(self.ftp_output_path) and not self.dry_run and self.args.local_ftp_server: try: os.makedirs(self.ftp_output_path) except: self.ftp_output_path = os.path.join(self.local_path, ("Crestron_Device_%s" % (self.device_ip_address))) try: os.makedirs(self.ftp_output_path) except: self.ftp_output_path = self.local_path print("[+] Crestron local FTP working directory set to:\r\n\t%s\r\n" % (self.ftp_output_path)) return True print("[-] Console prompt not found on device.") return False def find_console_prompt(self, data, key_value="", minimum_next_prompt_location=0, maximum_next_prompt_location=0, return_position=False, reverse=False): """ Description: Monolithic function for locating and verifying the presence of a supplied key value. Parameters: data - the string value to check for the key_value. key_value - either a string to look for or an empty string. If a non-empty string is supplied, data is checked for the supplied value. If an empty string is supplied, data is checked for the presence of the console_prompt. minimum_next_prompt_location - the position to start searching for the key_value. Default value of 0 means the whole string is checked. maximum_next_prompt_location - the position to stop searching for the key_value. Default value of 0 means everything past the minimum_next_prompt_location is checked. return_position - returns the position of the key_value instead of a bool. reverse - use rfind instead of find, to find the highest index of key_value. Output: Returns True if the key_value is found in data, at the minimum_next_prompt_location supplied. """ # If the minimum_next_prompt_location specifies a location outside of the data string, return -1 if looking for a position or # return False, signifying that the key_value was not found. if minimum_next_prompt_location >= len(data) or minimum_next_prompt_location < 0: if return_position: return -1 else: return False # If the maximum_next_prompt_location comes before the minimum_next_prompt_location or is less than 0, set it to the length of the data. if maximum_next_prompt_location <= minimum_next_prompt_location or maximum_next_prompt_location < 0: # Purposefully overshooting the end of the string in case minimum_next_prompt_location is or is near the end of the string. # Doesn't really matter if this value is larger than the actual data value. maximum_next_prompt_location = len(data) # If no key_value supplied, use the console prompt as the key_value. if not key_value or key_value == "": key_value = ("%s>" % (self.console_prompt)) if return_position: if reverse: return data.rfind(key_value, minimum_next_prompt_location, maximum_next_prompt_location) else: return data.find(key_value, minimum_next_prompt_location, maximum_next_prompt_location) else: return data.find(key_value, minimum_next_prompt_location, maximum_next_prompt_location) != -1 def remove_prompt(self, data, char_limit=0): """ Remove the console prompt from the text within the specified character limit. """ # If 0, remove the first occurrence of the prompt. if char_limit == 0: return data.replace(self.console_prompt+">", "", 1) # If less than 0, remove all instances of the prompt. elif char_limit <= 0: return data.replace(self.console_prompt+">", "") # Else if the first console prompt is found within the first char_limit characters, remove it. elif data.find(self.console_prompt+">") < char_limit: return data.replace(self.console_prompt+">", "", 1) return data def send_command_wait_prompt(self, command, minimum_next_prompt_location=0, done_string=""): """ Send a command and wait for the following console prompt or done_string to appear. """ if self.sshclient: stdin,stdout,stderr=self.sshclient.exec_command(command) data = stdout.readlines() data = "".join(data) else: data = "" is_checking_for_data = False wait_count = 0 self.sock.sendall((CR+command+CR)) sleep(GLOBAL_SLEEP_VALUE) data = (self.sock.recv(BUFF_SIZE)).replace((CR+command+CR), "") sleep(GLOBAL_SLEEP_VALUE) last_data_length = len(data) data_check_count = 0 if done_string and done_string != "": is_checking_for_data = (not self.find_console_prompt(data, done_string)) else: is_checking_for_data = (not self.find_console_prompt(data, minimum_next_prompt_location=minimum_next_prompt_location)) while is_checking_for_data: # "Deal with newer firmware that executes commands / doesn't return a prompt instead of printing help" # I'm not entirely sure what this means or looks like. It seems we're waiting for data that may or may not show up. # If the recv errors, we send a return, though we also send a return every 5 iterations, regardless... # We were also stuck in this while loop, waiting for a string that may never appear in the received data... try: data += self.sock.recv(BUFF_SIZE) except: self.sock.sendall(CR) sleep(GLOBAL_SLEEP_VALUE) wait_count += 1 if wait_count == 5: self.sock.sendall(CR) sleep(GLOBAL_SLEEP_VALUE) wait_count = 0 # A shitty attempt at breaking the while loop. # Every 5 loops, this segment execs, checking if the size of data has changed since the last 5 loops. # If the size of the data hasn't changed in the last 15 loops, break out of the while loop. # Results will vary. data_check_count += 1 if data_check_count > 3 and last_data_length == len(data): break elif last_data_length != len(data): data_check_count = 0 last_data_length = len(data) if done_string and done_string != "": is_checking_for_data = (not self.find_console_prompt(data, done_string)) else: is_checking_for_data = (not self.find_console_prompt(data, minimum_next_prompt_location=minimum_next_prompt_location)) return data def get_dir_listing(self, path=''): """ Get a directory list for the supplied path. If no path supplied, lists out the root. """ if path == '': path = '\\' data = self.send_command_wait_prompt(("dir %s" % (path)), 40) if not self.sshclient: return self.remove_prompt(data) else: return data.strip() def get_file(self, ftp_path, remote_path): """ Get a file from the device using the FPUTfile command. This tells the device to FTP the file at remote_path to the supplied FTP server, at the ftp_path. """ ftp_url = "ftp://%s%s" % (self.ftp_server_ip_address, urllib.quote(ftp_path)) command = "FPUTfile %s \"%s\"" % (ftp_url, remote_path) if remote_path.find(" ") >= 0: command = "%s \"%s\"" % (command, remote_path) else: command = "%s %s" % (command, remote_path) if(self.args.ftp_username and self.args.ftp_username != ""): command = "%s %s:%s" % (command, self.args.ftp_username, self.args.ftp_password) print(command) if not self.dry_run: data = self.send_command_wait_prompt(command, done_string="End Progress") def replicate_filesystem(self, current_path=''): """ Replicate the device's filesystem. Recursively lists and gets files from the device using get_dir_listing and get_file. Creates any necessary local directories, along the way. """ if not os.path.isdir(self.ftp_output_path+current_path) and not self.dry_run and self.args.local_ftp_server: os.makedirs(self.ftp_output_path+current_path) data = self.get_dir_listing(current_path) directory_re = "\[DIR\]\s+\d+-\d+-\d+ \d+:\d+:\d+ (?P<directory_name>.+?)\r\n" file_re = "\d+\s+\d+-\d+-\d+ \d+:\d+:\d+ (?P<file_name>.+?)\r\n" for directory_name in re.findall(directory_re, data, re.MULTILINE): remote_directory_path = '\\'.join([current_path,directory_name]) self.replicate_filesystem(remote_directory_path) for file_name in re.findall(file_re, data, re.MULTILINE): remote_file_path = '\\'.join([current_path,file_name]) ftp_path = '/'.join([current_path.replace('\\','/'),file_name]) data = self.get_file(ftp_path, remote_file_path)
class ftp_server(): def __init__(self): self.root_dir = None self.user = None self.password = None self.port = None self.server = None self.ftp_server_thread = None self.running = False self.last_error = None def get_root_dir(self): return self.root_dir def set_root_dir(self, full_path): self.root_dir = full_path def setup_ftp(self, server_id=1): ftp_settings = None mc_settings = None try: ftp_settings = Ftp_Srv.get_by_id(1) mc_settings = MC_settings.get_by_id(server_id) except Exception as e: logging.exception("Error Loading FTP. Traceback:") self.last_error = e return False pemfile = os.path.join(helper.crafty_root, "app", 'web', 'certs', 'crafty.pem') if not helper.check_file_exists(pemfile): helper.create_ftp_pem() if ftp_settings is not None and mc_settings is not None: self.user = ftp_settings.user self.password = ftp_settings.password self.port = ftp_settings.port self.root_dir = mc_settings.server_path logger.info("FTP server is now setup - Port: {}, Dir: {}".format( self.port, self.root_dir)) def _ftp_serve(self, server_id=1): self.setup_ftp(server_id) authorizer = DummyAuthorizer() authorizer.add_user(self.user, self.password, self.root_dir, perm='elradfmwMT') handler = TLS_FTPHandler crafty_root = os.path.abspath(helper.crafty_root) certfile = os.path.join(crafty_root, 'app', 'web', 'certs', 'crafty.pem') handler.certfile = certfile handler.authorizer = authorizer self.server = ThreadedFTPServer(('0.0.0.0', self.port), handler) self.running = True self.server.serve_forever() def run_threaded_ftp_server(self, server_id=1): self.running = True logger.info("Ftp Server Started for server ID: {}".format(server_id)) self.ftp_server_thread = threading.Thread(target=self._ftp_serve, args=[server_id], daemon=True) self.ftp_server_thread.start() def stop_threaded_ftp_server(self): self.running = False self.server.close_all() def check_running(self): return self.running
class TestCapture(unittest.TestCase): def setUp(self) -> None: self.fake = Faker() self.fake.add_provider(FakeCamera) self.temp_folder = tempfile.mkdtemp() self.temp_upload_file_path = None self.ftp_auth = DummyAuthorizer() self.ftp_auth.add_user('test', 'test', self.temp_folder, 'elrmw') self.ftp_handler = FTPHandler self.ftp_handler.authorizer = self.ftp_auth self.ftp_server = None while not self.ftp_server: self.ftp_port = random.randint(50000, 60000) try: self.ftp_server = ThreadedFTPServer( ('localhost', self.ftp_port), self.ftp_handler) except Exception as exc: print('{} error creating FTP server: {}; retrying...'.format( type(exc), exc)) self.ftp_server = None self.ftp_thread = Thread(target=self.ftp_server.serve_forever) self.ftp_thread.start() logging.getLogger('').setLevel(logging.DEBUG) def tearDown(self) -> None: self.ftp_server.close_all() shutil.rmtree(self.temp_folder) if self.temp_upload_file_path: os.unlink(self.temp_upload_file_path) def test_capture_upload(self): frame_jpg = image_to_jpeg(self.fake.random_image(640, 480)) # pylint: disable=no-member self.temp_upload_file_path = tempfile.mktemp(suffix='.jpg') with open(self.temp_upload_file_path, 'wb') as temp_file: temp_file.write(frame_jpg) nowdate = datetime.now() nowstamp = nowdate.strftime('%Y-%m-%d-%H-%M-%S-%f') ftp_path = 'ftp://*****:*****@localhost:{}/dev/%date%'.format( self.ftp_port) print(ftp_path) capturer_args = {'path': ftp_path} capturer = CaptureWriter('test_capture', nowstamp, 640, 480, **capturer_args) capturer.upload_ftp(self.temp_upload_file_path) # Verify file was uploaded correctly. upload_local_dest = os.path.join( self.temp_folder, 'dev', nowdate.strftime('%Y-%m-%d'), os.path.basename(self.temp_upload_file_path)) self.assertTrue(os.path.exists(upload_local_dest)) local_stat = os.stat(self.temp_upload_file_path) remote_stat = os.stat(upload_local_dest) self.assertEqual(local_stat.st_size, remote_stat.st_size) def test_capture_photo(self): with self.fake.directory() as capture_path: # pylint: disable=no-member config = { 'enable': 'true', 'backuppath': capture_path, 'path': capture_path, 'multiproc': 'false', 'camera': 'test' } capturer = PhotoCapture('test_capture', **config) for i in range(10): frame = None frame = self.fake.random_image(640, 480) # pylint: disable=no-member capturer.handle_motion_frame(frame) capturer.finalize_motion(None) self.assertEqual(len(os.listdir(capture_path)), 10) @memunit.assert_lt_mb(300) def test_capture_video(self): with self.fake.directory() as capture_path: # pylint: disable=no-member config = { 'enable': 'true', 'graceframes': '10', 'backuppath': capture_path, 'path': capture_path, 'fps': '5.0', 'fourcc': 'mp4v', 'container': 'mp4', 'multiproc': 'false', 'camera': 'test', } capturer = VideoCapture('test_capture', **config) for i in range(5): frame = None self.assertEqual(capturer.frames_count, 0) fc_check = 0 for j in range(0, 200): frame = self.fake.random_image(640, 480) # pylint: disable=no-member capturer.handle_motion_frame(frame) fc_check += 1 if fc_check > 100: fc_check = 0 else: self.assertEqual(capturer.frames_count, fc_check) capturer.finalize_motion(None) self.assertEqual(capturer.frames_count, 0) # Verify the video files. video_count: int = 0 for entry in os.scandir(capture_path): if entry.name.endswith('.mp4'): width: int = 0 height: int = 0 sec: int = 0 #ms : int = 0 video_count += 1 filename = os.path.join(capture_path, entry.name) parser = createParser(filename) metadata = extractMetadata(parser) text = metadata.exportPlaintext() for line in text: match = RE_DURATION.match(line) if match: sec = int(match.group('sec')) #ms = int( match.group('ms') ) continue match = RE_WIDTH.match(line) if match: width = int(match.group('width')) continue match = RE_HEIGHT.match(line) if match: height = int(match.group('height')) self.assertEqual(width, 640) self.assertEqual(height, 480) self.assertIn(sec, [19, 20]) #self.assertEqual( ms, 190 ) self.assertEqual(video_count, 10)
class DeployAgent(object): def __init__(self, ftpaddr, ftpport, ftphome, rpcaddr, rpcport): self.ftpaddr = ftpaddr self.ftpport = ftpport self.ftphome = ftphome self.rpcaddr = rpcaddr self.rpcport = rpcport logger.info("--- xagent startup ---") XOR.File.mkpath(self.ftphome) authorizer = DummyAuthorizer() authorizer.add_anonymous(self.ftphome, perm='elradfmwM') handler = FTPHandler handler.authorizer = authorizer handler.banner = "xagent ftp server ready" self.ftpserver = ThreadedFTPServer((self.ftpaddr, self.ftpport), handler) self.rpcserver = XOR.Net.XMLRPCServer(addr=(self.rpcaddr, self.rpcport), logRequests=True, allow_none=True, encoding="UTF-8") self.rpcserver.reg_function(XOR.OS.runex, "os.") self.rpcserver.reg_function(XOR.OS.type, "os.") self.rpcserver.reg_function(XOR.Zip.extract, "zip.") self.rpcserver.reg_function(XOR.File.remove, "file.") self.rpcserver.reg_function(self.reg_package) self.rpcserver.reg_function(self.shutdown) self.rpcserver.reg_function(self.set_logger) self.rpcserver.reg_function(self.del_logger) self.rpcserver.reg_function(self.get_ftphomedir) self.rpcserver.register_introspection_functions() self.rpcserver.register_multicall_functions() def startup(self): self.ftpthread = threading.Thread(target=lambda:self.ftpserver.serve_forever()) self.ftpthread.daemon = True self.ftpthread.start() self.rpcthread = threading.Thread(target=lambda:self.rpcserver.serve_forever()) self.rpcthread.daemon = True self.rpcthread.start() def shutdown(self): self.ftpserver.close_all() self.rpcserver.shutdown() def join(self): self.ftpthread.join() self.rpcthread.join() def set_logger(self, logurl): logger.info("set httphandler: %s", logurl) if hasattr(self, "httphandler"): self.del_logger() match = re.match("http://(.*:\d{1,5})/(.*)", logurl) host = match.group(1) path = "/%s" % match.group(2) logger.info("log host: %s", host) logger.info("log path: %s", path) self.httphandler = logging.handlers.HTTPHandler(host, path, "POST") self.httphandler.setLevel(logging.DEBUG) logger.info("set httphandler: %s", logurl) rootlogger.addHandler(self.httphandler) def del_logger(self): logger.info("delete logging httphandler: %s" % self.httphandler) rootlogger.removeHandler(self.httphandler) def reg_package(self, name, path=[], clss=[]): found = imp.find_module( name, path ) imp.load_module(name, *found) self.rpcserver.reg_module(sys.modules[name], clss, "%s." % name) def get_ftphomedir(self): return self.ftphome @staticmethod def parse_args(): parser = argparse.ArgumentParser(add_help=True) parser.add_argument('--host', action='store', default="0.0.0.0") parser.add_argument('--rpcport', action='store', type=int, default=3333) parser.add_argument('--ftpport', action='store', type=int, default=6121) parser.add_argument('--ftpusername', action='store', default="xor") parser.add_argument('--ftppassword', action='store', default="xor") parser.add_argument('--ftphomedir', action='store', default= os.path.join( os.path.dirname(bindir), "dist" )) args = parser.parse_known_args() return vars(args[0])
class FTPServerApp(tkinter.Frame): root_dir = dict() root_dir_tree = dict() dir_tree_frame = dict() def __init__(self, master=None): tkinter.Frame.__init__(self, master) self.grid(row=0, column=0) # Main Frame master.minsize(480,640) self.local_ip_addr = socket.gethostbyname(socket.getfqdn()) self.local_port = int(LOWEST_PORT_NO) master.title("FTP Server by TP031319 at %s" % (self.local_ip_addr,)) self.authorizer = DummyAuthorizer() self.initialise() self.create_server_control_frame(rw=0, cl=0) self.create_input_frame(rw=0, cl=3) self.create_state_frame(rw=1, cl=0) self.create_dir_frame(rw=4, cl=0) self.create_dir_tree_frame(rw=7, cl=0, tit="Local") self.create_browse_button(rw=5, cl=4) self.create_share_button(rw=5, cl=5) self.create_stderr_frame(rw=12, cl=0) self.handler = FTPHandler self.handler.authorizer = self.authorizer self.handler.banner = "FTP Server ver %s is ready" % VERSION #does this work in Python3? def create_server_control_frame(self, rw, cl): # Server Control Frame self.server_control_frame = ttk.Frame(self, relief=constants.SOLID, borderwidth=1) self.server_control_frame.grid(row=rw, column=cl, columnspan=4, sticky=constants.W, pady=4, padx=5) ttk.Label(self.server_control_frame, text="Server Control ").grid(row=rw, column=cl) self.start_button = ttk.Button(self.server_control_frame, text="Start", command=self.start_server) self.start_button.grid(row=rw, column=cl+1) self.stop_button = ttk.Button(self.server_control_frame, text="Stop", state=['disabled'], command=self.stop_server) self.stop_button.grid(row=rw, column=cl+2) def create_state_frame(self, rw, cl): # State Frame state_frame = ttk.Frame(self, relief=constants.SOLID, borderwidth=1) state_frame.grid(row=rw, column=cl, columnspan=3, sticky=constants.W, pady=4, padx=5) ttk.Label(state_frame, text="Server State").grid(row=rw, column=cl) state_value = ttk.Label(state_frame, textvariable=self.current_state, foreground='blue') state_value.grid(row=rw, column=cl+1) def create_input_frame(self, rw, cl): self.input_frame = ttk.Frame(self, relief=constants.SOLID, borderwidth=1) self.input_frame.grid(row=rw, column=cl, columnspan=3, sticky=constants.W, pady=4, padx=5) port_input_label = ttk.Label(self.input_frame, text="Server Port ({0}~{1})".format(LOWEST_PORT_NO, HIGHEST_PORT_NO)) port_input_label.grid(row=rw, column=cl+1, sticky=constants.W) self.listen_port_input = ttk.Entry(self.input_frame, width=8, textvariable=self.listen_port) self.listen_port_input.grid(row=rw+1, column=cl+1) def create_dir_frame(self, rw, cl): self.dir_frame = ttk.Frame(self, relief=constants.SOLID, borderwidth=1) self.dir_frame.grid(row=rw, column=cl, columnspan=3, sticky=constants.W, pady=4, padx=5) ttk.Label(self.dir_frame, text="Shared Directory").grid(row=rw, column=cl, sticky=constants.W) self.root_dir_input = ttk.Entry(self.dir_frame, width=64, textvariable=self.root_dir['Local']) self.root_dir_input.grid(row=rw+1, column=cl) def create_browse_button(self, rw, cl): self.browse_button = ttk.Button(self.dir_frame, text="Browse", command=partial(self.select_dir, self.root_dir_tree['Local'])) self.browse_button.grid(row=rw, column=cl) def create_share_button(self, rw, cl): self.share_button = ttk.Button(self.dir_frame, text="Share", command=partial(self.share_dir, self.root_dir_tree['Local'])) self.share_button.grid(row=rw, column=cl) def create_dir_tree_frame(self, rw, cl, tit): self.dir_tree_frame[tit] = ttk.Frame(self, relief=constants.SOLID, borderwidth=1) self.root_dir_tree[tit] = RootTree(self, columns=('fullpath','type','size'), displaycolumns='size', root_dir=self.root_dir[tit], conn=self.ftp_conn if tit=='Remote' else None) self.root_dir_tree[tit].heading('#0', text='Directory', anchor=constants.W) self.root_dir_tree[tit].heading('size', text='Size', anchor=constants.W) self.root_dir_tree[tit].column('#0', stretch=0, minwidth=200, width=440) self.root_dir_tree[tit].column('size', stretch=1, minwidth=40, width=80) self.dir_tree_frame[tit].grid(row=rw, column=cl, sticky=constants.W, pady=4, padx=5) ttk.Label(self.dir_tree_frame[tit], text=tit).grid(row=rw, column=cl, sticky=constants.W) self.root_dir_tree[tit].grid(in_=self.dir_tree_frame[tit], row=rw+1, column=cl, sticky=constants.NSEW) yScrollBar = ttk.Scrollbar(self.dir_tree_frame[tit], orient=constants.VERTICAL, command=self.root_dir_tree[tit].yview) xScrollBar = ttk.Scrollbar(self.dir_tree_frame[tit], orient=constants.HORIZONTAL, command=self.root_dir_tree[tit].xview) self.root_dir_tree[tit]['yscroll'] = yScrollBar.set self.root_dir_tree[tit]['xscroll'] = xScrollBar.set yScrollBar.grid(row=rw, column=cl+2, rowspan=3, sticky=constants.NS) xScrollBar.grid(row=rw+3, column=cl, rowspan=1, sticky=constants.EW) # set frame resizing priorities self.dir_tree_frame[tit].rowconfigure(0, weight=1) self.dir_tree_frame[tit].columnconfigure(0, weight=1) def create_stderr_frame(self, rw, cl): self.stderr_frame = ttk.Frame(self, relief=constants.SOLID, borderwidth=1) self.stderr_frame.grid(row=rw, column=cl) self.old_stderr = sys.stderr self.err = tkinter.Text(self, width=64, height=12, wrap='none') self.err.grid(row=rw+1, column=cl, pady=4, padx=5) sys.stderr = StdoutRedirector(self.err) def initialise(self): # Initial values self.username = tkinter.StringVar() self.username.set("user") self.password = tkinter.StringVar() self.password.set("passwd") self.listen_ip = tkinter.StringVar() self.listen_ip.set(self.local_ip_addr) self.listen_port = tkinter.StringVar() self.listen_port.set(self.local_port) self.root_dir['Local'] = tkinter.StringVar() self.root_dir['Local'].set(os.getcwd() + os.sep) self.current_state = tkinter.StringVar() self.current_state.set("NOT RUNNING") self.root_dir['Remote'] = tkinter.StringVar() self.root_dir['Remote'].set(os.sep) # This can be set up only once and saved in a database self.authorizer.add_user(self.username.get(), self.password.get(), self.root_dir['Local'].get(), 'elradfmw') def start_server(self): port_no = 0 msg = "Please type a port number between 1025 and 65533 inclusive." try: port_no = int(self.listen_port.get()) if port_no < LOWEST_PORT_NO or port_no > HIGHEST_PORT_NO: msg += " Port {0} is not valid.".format(port_no) raise Exception(msg) except: mbox.showinfo(message=msg) return self.address = (self.listen_ip.get(), port_no) self.server = ThreadedFTPServer(self.address, self.handler) self.server.max_cons = 256 self.server.max_cons_per_ip = 5 self.share_dir(self.root_dir_tree['Local']) self.start_button.state(['disabled']) self.stop_button.state(['!disabled']) self.share_button.state(['disabled']) self.current_state.set("RUNNING") threading.Thread(target=self.server.serve_forever).start() def stop_server(self): self.server.close_all() self.start_button.state(['!disabled']) self.stop_button.state(['disabled']) self.share_button.state(['!disabled']) self.current_state.set("NOT RUNNING") def select_dir(self, dir_tree_view): if isinstance(dir_tree_view, RootTree): children = dir_tree_view.get_children('') if children: dir_tree_view.delete(children) old_dir_tree_view_root_dir = dir_tree_view.root_directory.get() dir_tree_view.root_directory.set(filedialog.askdirectory().replace("/" , str(os.sep))) if not dir_tree_view.root_directory.get(): dir_tree_view.root_directory.set(old_dir_tree_view_root_dir) def share_dir(self, dir_tree_view): if isinstance(dir_tree_view, RootTree): try: os.chdir(self.root_dir['Local'].get()) dir_tree_view.root_directory = self.root_dir['Local'] # No need to reconnect because this is only for local dir dir_tree_view.populate_parent() # Open up the directory for transferring out/receiving in files # For use with WindowsAuthorizer or UnixAuthorizer: # For simplicity's sake, update the homedir everytime Share button is pressed # self.authorizer.override_user(self.username.get(), # homedir=self.root_dir['Local'].get()) # For now the workaround: self.authorizer.remove_user(self.username.get()) self.authorizer.add_user(self.username.get(), self.password.get(), self.root_dir['Local'].get(), 'elradfmw') except FileNotFoundError: mbox.showinfo(message="Invalid Directory!")