def fetch(self, request): '''Fulfill a request. Args: request (:class:`.ftp.request.Request`): Request. Returns: .ftp.request.Response: A Response populated with the initial data connection reply. Once the response is received, call :meth:`read_content`. Coroutine. ''' response = Response() yield From(self._prepare_fetch(request, response)) response.file_transfer_size = yield From(self._fetch_size(request)) if request.restart_value: try: yield From(self._commander.restart(request.restart_value)) response.restart_value = request.restart_value except FTPServerError: _logger.debug('Could not restart file.', exc_info=1) yield From(self._open_data_stream()) command = Command('RETR', request.file_path) yield From(self._begin_stream(command)) raise Return(response)
def test_control_stream(self): def log_cb(data_type, data): _logger.debug(__('{0}={1}', data_type, data)) connection = Connection(('127.0.0.1', self.server_port())) yield From(connection.connect()) control_stream = ControlStream(connection) control_stream.data_observer.add(log_cb) reply = yield From(control_stream.read_reply()) self.assertEqual(220, reply.code) yield From(control_stream.write_command(Command('USER', 'smaug'))) reply = yield From(control_stream.read_reply()) self.assertEqual(331, reply.code) yield From(control_stream.write_command(Command('PASS', 'gold1'))) reply = yield From(control_stream.read_reply()) self.assertEqual(230, reply.code) yield From(control_stream.write_command(Command('PASV'))) reply = yield From(control_stream.read_reply()) self.assertEqual(227, reply.code) address = parse_address(reply.text) data_connection = Connection(address) yield From(data_connection.connect()) data_stream = DataStream(data_connection) yield From( control_stream.write_command(Command('RETR', 'example (copy).txt'))) reply = yield From(control_stream.read_reply()) self.assertEqual(150, reply.code) my_file = io.BytesIO() yield From(data_stream.read_file(my_file)) reply = yield From(control_stream.read_reply()) self.assertEqual(226, reply.code) self.assertEqual('The real treasure is in Smaug’s heart 💗.\n', my_file.getvalue().decode('utf-8'))
def login(self, username='******', password='******'): '''Log in. Coroutine. ''' yield From( self._control_stream.write_command(Command('USER', username))) reply = yield From(self._control_stream.read_reply()) self.raise_if_not_match('Login username', ReplyCodes.user_name_okay_need_password, reply) yield From( self._control_stream.write_command(Command('PASS', password))) reply = yield From(self._control_stream.read_reply()) self.raise_if_not_match('Login password', ReplyCodes.user_logged_in_proceed, reply)
def test_command(self): command = Command('User', '*****@*****.**') self.assertEqual('USER', command.name) self.assertEqual('*****@*****.**', command.argument) self.assertEqual('USER', command.to_dict()['name']) self.assertEqual('*****@*****.**', command.to_dict()['argument']) command = Command('Poke') self.assertEqual('POKE', command.name) self.assertEqual('', command.argument) self.assertEqual('POKE', command.to_dict()['name']) self.assertEqual('', command.to_dict()['argument'])
def fetch_file_listing(self, request): '''Fetch a file listing. Returns: .ftp.request.ListingResponse Once the response is received, call :meth:`read_listing_content`. Coroutine. ''' response = ListingResponse() yield From(self._prepare_fetch(request, response)) yield From(self._open_data_stream()) mlsd_command = Command('MLSD', self._request.file_path) list_command = Command('LIST', self._request.file_path) try: yield From(self._begin_stream(mlsd_command)) self._listing_type = 'mlsd' except FTPServerError as error: if error.reply_code in ( ReplyCodes.syntax_error_command_unrecognized, ReplyCodes.command_not_implemented): self._listing_type = None else: raise if not self._listing_type: # This code not in exception handler to avoid incorrect # exception chaining yield From(self._begin_stream(list_command)) self._listing_type = 'list' _logger.debug('Listing type is %s', self._listing_type) raise Return(response)
def restart(self, offset): '''Send restart command. Coroutine. ''' yield From( self._control_stream.write_command(Command('REST', str(offset)))) reply = yield From(self._control_stream.read_reply()) self.raise_if_not_match( 'Restart', ReplyCodes.requested_file_action_pending_further_information, reply)
def test_parse_command(self): command = Command() command.parse(b'User [email protected]\r\n') self.assertEqual('USER', command.name) self.assertEqual('*****@*****.**', command.argument) self.assertRaises(AssertionError, command.parse, b'OOPS\r\n') command = Command() command.parse(b'POKE\r\n') self.assertEqual('POKE', command.name) self.assertEqual('', command.argument) self.assertRaises(AssertionError, command.parse, b'OOPS\r\n')
def size(self, filename): '''Get size of file. Coroutine. ''' yield From( self._control_stream.write_command(Command('SIZE', filename))) reply = yield From(self._control_stream.read_reply()) self.raise_if_not_match('File size', ReplyCodes.file_status, reply) try: raise Return(int(reply.text.strip())) except ValueError: return
def passive_mode(self): '''Enable passive mode. Returns: tuple: The address (IP address, port) of the passive port. Coroutine. ''' yield From(self._control_stream.write_command(Command('PASV'))) reply = yield From(self._control_stream.read_reply()) self.raise_if_not_match('Passive mode', ReplyCodes.entering_passive_mode, reply) try: raise Return(wpull.ftp.util.parse_address(reply.text)) except ValueError as error: raise ProtocolError(str(error)) from error
def test_command(self): command = Command('User', '*****@*****.**') self.assertEqual('USER', command.name) self.assertEqual('*****@*****.**', command.argument) self.assertEqual('USER', command.to_dict()['name']) self.assertEqual( '*****@*****.**', command.to_dict()['argument']) command = Command('Poke') self.assertEqual('POKE', command.name) self.assertEqual('', command.argument) self.assertEqual('POKE', command.to_dict()['name']) self.assertEqual('', command.to_dict()['argument'])
def setup_data_stream(self, connection_factory, data_stream_factory=DataStream): '''Create and setup a data stream. This function will set up passive and binary mode and handle connecting to the data connection. Args: connection_factory: A coroutine callback that returns :class:`.connection.Connection`. stream_callback: A callback that will be provided an instance of :class:`.ftp.stream.DataStream`. Coroutine. Returns: DataStream ''' yield From(self._control_stream.write_command(Command('TYPE', 'I'))) reply = yield From(self._control_stream.read_reply()) self.raise_if_not_match('Binary mode', ReplyCodes.command_okay, reply) address = yield From(self.passive_mode()) connection = yield From(connection_factory(address)) # TODO: unit test for following line for connections that have # the same port over time but within pool cleaning intervals connection.reset() yield From(connection.connect()) data_stream = data_stream_factory(connection) raise Return(data_stream)
def override_func(): yield From(original_func()) yield From( session._control_stream.write_command( Command('EVIL_BAD_PASV_ADDR'))) print('Evil awaits')