Пример #1
0
class ClientHandler(threading.Thread):
    """
    Handles communication;
    Communication is divided in two parts:
        1.authentication
        2.actual data transfer
    Main logic is in the `run` function
    """
    FIRT_MESSAGE = '200 LOGIN\r\n'
    TIMEOUUT = '501 SYNTAX ERROR\r\n'
    PASSWORD_MESSAGE = '201 PASSWORD\r\n'
    SECOND_MESSAGE = '202 OK\r\n'
    LOGIN_FAILED = '500 LOGIN FAILED\r\n'
    SYNTAX_ERROR = '501 SYNTAX ERROR\r\n'
    TIMEOUT = '502 TIMEOUT\r\n'

    BAD_CHECKSUM = '300 BAD CHECKSUM\r\n'

    def __init__(self, connection: socket):
        self.time_start = time.time()
        self.connection = connection
        self.buffer = Buffer(connection)
        self.step = 0
        self.username = None
        self.stop_event = threading.Event()
        self.username_wrong = None
        self.start_time = time.time()
        t = threading.Timer(30, self.after_done)
        t.start()
        super().__init__()

    def after_done(self):
        """
        Callback, gets called 45 sec after start of communication
        """
        if self.stop_event.is_set():
            return
        self.end_with_message(self.TIMEOUUT)

    def end_with_message(self, message):
        """
        :param message: msg to be sent
        send message, close connection
        """
        self.stop_event.set()
        self.connection.sendall(message.encode())
        self.connection.close()
        self.stop_event.set()

    def send_message(self, message: str):
        self.connection.sendall(message.encode())

    def run(self, ):
        """
        main logic, close thread on return
        :return:
        """
        try:
            if not self.handle_login():
                self.end_with_message(self.LOGIN_FAILED)
                return
        except ConnectionLost:
            print('---[CONNECTION LOST]-----' + str(self.ident) + '---' * 5)
            return
        while not self.stop_event.is_set():
            if time.time() - self.start_time >= (45
                                                 if settings.AWS else 1000000):
                time.sleep(0.5)
                self.end_with_message(self.TIMEOUT)
                break
            try:
                time.sleep(0.5)
                self.handle_command()
            except FotoException:
                time.sleep(0.5)
                self.end_with_message(self.SYNTAX_ERROR)
                break
            except PhotoLengthNotNumber:
                time.sleep(0.5)
                self.end_with_message(self.SYNTAX_ERROR)
                break
            except BadCheckSum:
                self.send_message(self.BAD_CHECKSUM)
            except WrongSyntax:
                time.sleep(0.5)
                self.end_with_message(self.SYNTAX_ERROR)
                break
            except ConnectionLost:
                print('---[CONNECTION LOST]' + str(self.ident) + '---' * 5)
                break
        return

    def handle_login(self):
        """
        reads username and password; performs validation based on the sum of the username
        :return: True on username == password
        """
        try:
            self.send_message(self.FIRT_MESSAGE)
            username = self.buffer.read_username()
            print(str(username) + '+++++' + str(self.ident))
            self.send_message(self.PASSWORD_MESSAGE)
            password = self.buffer.read_password(
                aprox_length=len(str(username)))
            print(str(password) + '!!!!!!' + str(self.ident))
            try:
                if password == username and username >= 518:  #Robot not in username
                    self.send_message(self.SECOND_MESSAGE)
                    return True
                else:
                    return False
            except WrongPassword:
                return False
        except OSError or TypeError:
            # print('---' * 5 + str(self.ident) + '---' * 5)
            raise ConnectionLost

    def handle_command(self):
        """
        handle according command, throw Exception on wrong syntax
        """
        while True:
            curr_byte = self.buffer.read_byte()
            if curr_byte == '':
                raise WrongSyntax()
            if not self.buffer.possible_start_info(
            ) and not self.buffer.possible_start_photo():
                raise WrongSyntax()
            elif self.buffer == b'INFO ':
                try:
                    self.handle_info()
                except BlockingIOError:
                    self.end_with_message(self.SYNTAX_ERROR)
                break
            elif self.buffer == b'FOTO ':
                self.handle_photo()
                break
            elif len(self.buffer) > 5:
                raise WrongSyntax()

    def handle_photo(self):
        """
        Reads FOTO msg: number of bytes to read, data, checksum
        Performs validation of checksum and data checksum;
        saves the photo in the file named according to thread ident
        """
        with open(f'out{self.ident}', 'wb') as f:
            bytes_to_read = self.buffer.read_photo_length()
            read_bytes = 0
            checksum = 0
            while read_bytes < bytes_to_read:
                try:
                    byte = self.buffer.read_byte(MSG_DONTWAIT, fake=True)
                except BlockingIOError:
                    self.end_with_message(self.SYNTAX_ERROR)

                f.write(byte)
                checksum += ord(byte)
                read_bytes += 1

        sent_checksum = bytearray()

        for i in range(4):
            try:
                byte = self.buffer.read_byte(fake=True)
            except BlockingIOError:
                raise BadCheckSum()
            sent_checksum.extend(byte)
        parsed_checksum = struct.unpack('>HH', sent_checksum)
        parsed_checksum = str(parsed_checksum[0]) + str(parsed_checksum[1])
        parsed_checksum = int(parsed_checksum)

        # if not settings.AWS:  # this thingy is in settings module, telnet sends \r\n at the end of every message
        #     for i in range(2):
        #         self.buffer.read_byte(fake=True)

        if parsed_checksum == checksum:
            self.send_message(self.SECOND_MESSAGE)
        else:
            raise BadCheckSum()

    def handle_info(self):
        """
        read info msg
        saves the msg in the file named according to thread ident
        """
        try:
            self.buffer.read_line()
            print((f'[INFO] -- {self.ident}', self.buffer.buffer))
            with open(f'out{self.ident}', 'wb') as f:
                f.write(self.buffer.buffer + b'  [INFO]')
            self.buffer.buffer = bytearray()
            self.send_message(self.SECOND_MESSAGE)
        except PhotoLengthNotNumber:
            self.end_with_message(self.SYNTAX_ERROR)
        except OSError:
            print(f'---exc in read info---')
            self.send_message(self.SECOND_MESSAGE)