Ejemplo n.º 1
0
class DSClientServerResponseProcessor:
    """
    This class in charge of processing server replies
    """

    is_logged_in = False

    def __init__(self):
        self.message_type = ''
        self.timestamp = ''
        self.error_code = ''
        self.flag = ''
        self.changed_section = ''
        self.section_id = ''
        self.reserved_1 = ''
        self.reserved_2 = ''
        self.reserved_3 = ''

        self.data = ''
        self.data_size = ''
        self.checksum = ''

        self.response = ''
        self.count = 0
        self.null_byte = b'\x00'
        self.server_processor_pdu = DSPdu()
        self.client_socket = ''
        self.message_type_index, self.timestamp_index, self.error_code_index, self.flag_index, self.changed_section_index, self.section_id_index, self.reserved_1_index, self.reserved_2_index, self.reserved_3_index, self.data_index, self.data_size_index, self.checksum_index = self.server_processor_pdu.get_pdu_parts_index(
        )

        self.color = Color()
        self.style = Style()
        self.error = DSCode()
        self.is_commit_message = False

    def process_response(self, response, client_socket, client_obj):
        """
        This is the main response processor
        :param client_obj:
        :param response: pdu array
        :param client_socket: client's socket object
        :return:
        """

        # assign various pdu parts from the response parameter
        self.response = response
        self.client_socket = client_socket
        self.message_type = self.response[self.message_type_index]
        self.timestamp = self.response[self.timestamp_index]
        self.error_code = self.response[self.error_code_index]
        self.flag = self.response[self.flag_index]
        self.changed_section = self.response[self.changed_section_index]
        self.section_id = self.response[self.section_id_index]
        self.reserved_1 = self.response[self.reserved_1_index]
        self.reserved_2 = self.response[self.reserved_2_index]
        self.reserved_3 = self.response[self.reserved_3_index]

        self.data = self.response[self.data_index]
        self.data_size = self.response[self.data_size_index]
        self.checksum = self.response[self.checksum_index]
        self.client_socket = client_socket

        if client_obj.is_authenticated is False and self.error_code == DSCode.LOGIN_SUCCESS and self.flag == DSFlags.begin:
            # we only need to print this once.
            print(self.color.red(self.error.dscode_print(self.error_code)))
            client_obj.is_authenticated = True

        elif client_obj.is_authenticated is True and self.error_code != DSCode.LOGIN_SUCCESS:
            print(self.color.red('checking this out'))
            print(self.error_code)
            print(self.color.red(self.error.dscode_print(self.error_code)))

        elif self.error_code == DSCode.LOGIN_NOT_SUCCESS:
            client_obj.is_authenticated = False
            print(self.color.red(self.error.dscode_print(self.error_code)))

        elif self.error_code == DSCode.RELEASE_SECTION_SUCCESSFUL:
            print(self.color.green(self.error.dscode_print(self.error_code)))

        elif self.error_code == DSCode.SECTION_DENIED:
            print(self.color.red(self.error.dscode_print(self.error_code)))

        elif self.error_code == DSCode.USER_NOT_AUTHENTICATED:
            print(self.color.red(self.error.dscode_print(self.error_code)))

        elif self.error_code == DSCode.CONNECTED:
            pass
            # print('{:^70}'.format(color.yellow(error.dscode_print(self.error_code))))
            # print()

        elif self.error_code == DSCode.LOGIN_SUCCESS and self.flag.encode(
        ) == DSFlags.begin:
            print(self.color.green(self.error.dscode_print(self.error_code)))
            pass

        elif self.error_code == DSCode.LOGIN_SUCCESS and self.flag.encode(
        ) == DSFlags.more:
            pass

        elif self.error_code == DSCode.LOGIN_SUCCESS and self.flag.encode(
        ) == DSFlags.finish:
            pass

        elif self.error_code == DSCode.COMMIT_UPDATE:
            pass

        elif self.error_code == DSCode.COMMIT_DENIED:
            print(self.color.red(self.error.dscode_print(self.error_code)))

        else:
            print(self.color.green(self.error.dscode_print(self.error_code)))

        if self.response[
                self.message_type_index] == DSMessageType.CONNECT.decode():
            self.connect()
            return True

        elif self.response[
                self.message_type_index] == DSMessageType.CAUTH.decode():
            self.cauth()
            return True

        elif self.response[
                self.message_type_index] == DSMessageType.S_EDIT.decode():
            self.s_edit()
            return True

        elif self.response[
                self.message_type_index] == DSMessageType.S_RELEASE.decode():
            self.s_release()
            return True

        elif self.response[
                self.message_type_index] == DSMessageType.LOGOFF.decode():
            self.abort()
            return True

        elif self.response[
                self.message_type_index] == DSMessageType.S_COMMIT.decode():
            self.commit()
            return True
        else:
            return False

    def connect(self):
        message_type = DSMessageType.CONNECT
        timestamp = self.server_processor_pdu.get_time()  # get timestamp
        error_code = DSCode.OK  # assign error code
        flag = DSFlags.finish
        changed_section = 0
        section_id = 0
        reserved_1 = self.null_byte
        reserved_2 = self.null_byte
        reserved_3 = self.null_byte
        data = b'HELLO SERVER'
        data_size = len(data)
        checksum = self.server_processor_pdu.get_checksum(timestamp, data)

        pdu_array = [
            message_type, timestamp, error_code, flag, changed_section,
            section_id, reserved_1, reserved_2, reserved_3, data, data_size,
            checksum
        ]
        pdu = self.server_processor_pdu.pack(pdu_array)

        self.client_socket.send(pdu)
        print(
            self.color.green('{:^60}'.format(
                '--Welcome to Document Sharing Software 1.0--')))
        print('------------------------COMMANDS----------------------------\n'
              'LOGIN: LOGIN, USERNAME, PASSWORD, DOCUMENT_NAME             |\n'
              'CREATE NEW DOCUMENT: CREATE, DOCUMENT_NAME                  |\n'
              'REQUEST SECTION: SECTION, SECTION_ID,                       |\n'
              'COMMIT: COMMIT, SECTION_ID, DATA                            |\n'
              'LOGOFF: LOGOFF                                              |\n'
              '------------------------------------------------------------\n')

        return 0

    def cauth(self):

        if self.flag == DSFlags.begin.decode(
        ) and self.error_code == DSCode.COMMIT_UPDATE:
            print(self.color.yellow(self.error.dscode_print(self.error_code)))

        if self.flag == DSFlags.begin.decode(
        ) or self.flag == DSFlags.more.decode():
            if self.changed_section == 1:
                print('{}: {}'.format(self.color.yellow(str(self.count)),
                                      self.color.yellow(self.data)))

            else:
                print('{}: {}'.format(str(self.count), self.data))

            self.count += 1

        elif self.flag == DSFlags.finish.decode():
            if '\n' == self.data.strip():
                pass

            elif self.count >= 1 and len(self.data.strip()) > 1:
                if self.changed_section == 1:
                    print('{}: {}'.format(self.color.yellow(str(self.count)),
                                          self.color.yellow(self.data)))

                else:
                    print('{}: {}'.format(self.count, self.data))

            else:
                print(self.data)

            self.count = 0  # reset the count

    def s_edit(self):
        if self.error_code == DSCode.CLIENT_IS_THE_CURRENT_SECTION_OWNER:
            print('client is the current owner of the section id: {}'.format(
                self.section_id))

        elif self.error_code == DSCode.SECTION_NOT_AVAILABLE:
            print(self.data)

        elif self.error_code == DSCode.SECTION_DENIED:
            print(self.data)

        elif self.error_code == DSCode.SECTION_ID_NOT_VALID:
            print(self.data)

        else:
            print('{}: {} '.format(self.section_id, self.data))

    def s_revoke(self):
        print('Permission revoked for section #{}'.format(self.section_id))
        print(self.error_code)
        print(self.data)

    def s_release(self):
        # if self.error_code == DSCode.RELEASE_SECTION_NOT_SUCCESSFUL:
        #     print('section #{} not released'.format(self.section_id))
        #
        # elif self.error_code == DSCode.SECTION_ID_NOT_VALID:
        #     print('client presented an invalid section id')
        #
        # elif self.error_code == DSCode.CLIENT_IS_THE_CURRENT_SECTION_OWNER:
        #     print('client is not the current section owner for section id: {}'.format(self.section_id))
        #
        # elif self.error_code == DSCode.SECTION_TOKEN_NOT_FOUND_SECTION_IS_FREE:
        #     print('client is not the current section owner, however, the section id #{} is free'.format(self.section_id))
        #
        # elif self.error_code == DSCode.RELEASE_SECTION_SUCCESSFUL:
        #     print('Section #{} successfully released'.format(self.section_id))
        pass

    def s_denied(self):
        print('Section #{} request was denied'.format(self.section_id))
        print(self.error_code)
        print(self.data)

    def abort(self):
        print('The server have successfully disconnected')
        print(self.error_code)
        print(self.data)

    def commit(self):
        # the server is not meant to respond with this messagetype since once the client is done updating a section
        # server is supposed to move the state back to S_DATA
        pass
Ejemplo n.º 2
0
class DSInput:
    def __init__(self):
        self.user_input = ''
        self.isValid = False
        self.ds_pdu = DSPdu()
        self.ds_code = DSCode()
        self.null_byte = b'\x00'
        self.data = ''
        self.array = ''
        self.color = Color()
        self.style = Style()

        self.message_type = ''
        self.timestamp = ''
        self.error_code = ''
        self.flag = ''
        self.changed_section = ''
        self.section_id = ''
        self.reserved_1 = self.null_byte
        self.reserved_2 = self.null_byte
        self.reserved_3 = self.null_byte

        self.data = ''
        self.data_size = ''
        self.checksum = ''

        self.pdu_array = ''

    def get_user_input(self):
        """
        Gets user input and then formats then return them as string arrays
        :return: list
        """
        # if the didn't enter any value continue to prompt until a value is entered

        while True:
            self.user_input = input()
            array = self.get_array(self.user_input)
            if array[0] == 'LOGIN' and len(array) == 4:
                # reset self.user_input to ''

                self.reset_user_input()
                return array, ','.join(array)
            elif array[0] == 'SECTION' and len(array) == 2:
                try:
                    int(array[1])
                    self.reset_user_input()
                    return array, ','.join(array)
                except ValueError as err:
                    print(err.args)
                    print('User input is not valid')
                    continue

            elif array[0] == 'COMMIT' and len(array) >= 3:
                try:
                    int(array[1])
                    self.reset_user_input()
                    test_string = ','.join(array)
                    print('in commit section in dsinput')
                    print('array: {}'.format(array))
                    print('string sent: {}'.format(test_string))
                    return array, test_string
                except ValueError as err:
                    print(err.args)
                    print('User input is not valid')
                    continue

            elif array[0] == 'CREATE' and len(
                    array) == 2:  # I will create this functionality later
                print(
                    'this functionality will be created later. As of now, it is non-existent'
                )
                self.reset_user_input()
                pass

            elif array[0] == 'LOGOFF' and len(array) == 1:
                self.reset_user_input()
                return array, ','.join(array)

            elif array[0] == 'RELEASE' and len(array) == 2:
                self.reset_user_input()
                return array, ','.join(array)

            else:
                print(self.color.red('User input is not valid'), end='')
                print(array, len(array))
                print(
                    'Follow the instruction and enter input using the format on the instruction'
                )

    def get_array(self, user_input):
        """
        Gets returns an array of the user_input
        :param string user_input:
        :return: list
        """

        self.user_input = user_input.split(',')
        array = []
        for item in self.user_input:
            array.append(item.strip())

        # reset self.user_input to ''
        self.reset_user_input()
        return array

    def reset_user_input(self):
        self.user_input = ''

    def process_user_input(self, array, data):
        """
        Process user input and return the array for packing
        :param list array:
        :param string data:
        :return: array
        """
        # print(array, data)
        self.data = data
        self.array = array

        if array[0] == 'LOGIN':
            return self.login()

        elif array[0] == 'CREATE':
            return self.create()

        elif array[0] == 'SECTION':
            return self.section()

        elif array[0] == 'RELEASE':
            return self.release()

        elif array[0] == 'COMMIT':
            return self.commit()

        elif array[0] == 'LOGOFF':
            return self.logoff()

        else:
            print('User input not valid')

    def login(self):
        self.message_type = DSMessageType.CAUTH
        self.timestamp = self.ds_pdu.get_time()  # get timestamp
        self.error_code = self.ds_code.OK  # assign error code
        self.flag = DSFlags.finish
        self.changed_section = 0
        self.section_id = 0
        self.reserved_1 = self.null_byte
        self.reserved_2 = self.null_byte
        self.reserved_3 = self.null_byte

        self.data = self.data.encode(
        )  # data should be encoded i.e LOGIN, Admin, root, example1 in bytes
        self.data_size = len(self.data)
        self.checksum = self.ds_pdu.get_checksum(self.timestamp, self.data)

        pdu_array = [
            self.message_type, self.timestamp, self.error_code, self.flag,
            self.changed_section, self.section_id, self.reserved_1,
            self.reserved_2, self.reserved_3, self.data, self.data_size,
            self.checksum
        ]

        return pdu_array

    def section(self):
        self.message_type = DSMessageType.S_EDIT
        self.timestamp = self.ds_pdu.get_time()  # get timestamp
        self.error_code = self.ds_code.OK  # assign error code
        self.flag = DSFlags.finish
        self.reserved_1 = self.null_byte
        self.reserved_2 = self.null_byte
        self.reserved_3 = self.null_byte
        self.section_id = int(self.array[1])  # obtain the section id
        self.data = self.null_byte
        self.data_size = len(self.data)
        self.checksum = self.ds_pdu.get_checksum(self.timestamp, self.data)

        pdu_array = [
            self.message_type, self.timestamp, self.error_code, self.flag,
            self.changed_section, self.section_id, self.reserved_1,
            self.reserved_2, self.reserved_3, self.data, self.data_size,
            self.checksum
        ]

        return pdu_array

    def create(self):
        pass

    def release(self):
        self.message_type = DSMessageType.S_RELEASE
        self.timestamp = self.ds_pdu.get_time()  # get timestamp
        self.error_code = self.ds_code.OK  # assign error code
        self.flag = DSFlags.finish
        self.reserved_1 = self.null_byte
        self.reserved_2 = self.null_byte
        self.reserved_3 = self.null_byte
        self.section_id = int(self.array[1])  # obtain the section id
        self.data = self.null_byte
        self.data_size = len(self.data)
        self.checksum = self.ds_pdu.get_checksum(self.timestamp, self.data)

        pdu_array = [
            self.message_type, self.timestamp, self.error_code, self.flag,
            self.changed_section, self.section_id, self.reserved_1,
            self.reserved_2, self.reserved_3, self.data, self.data_size,
            self.checksum
        ]
        return pdu_array

    def commit(self):
        print('in input commit section')
        print(self.array[2:])
        self.data = self.array[
            2:]  # this will ensure that other sentences separated by comma's are used as data
        # data is retrieved as list, we need to extract it as string
        string_builder = ''
        for text in self.data:
            string_builder += text

        self.data = string_builder
        data_array = self.break_data(
            self.data)  # this is the section that contains the data
        print(self.color.biege('in the client commit method'))
        print('data to be committed: {}'.format(data_array))
        print(self.color.biege('--------------------------------'))
        freq_to_send = len(
            data_array
        )  # number of times we will send in order to complete send
        print('after the data is been broken: {}'.format(
            data_array, freq_to_send))
        count = 0
        arr = []  # this represents all the pdu_array to be sent
        # print('Data array : {}'.format(data_array))
        for item in data_array:
            count += 1

            self.message_type = DSMessageType.S_COMMIT
            self.timestamp = self.ds_pdu.get_time()  # get timestamp
            self.error_code = self.ds_code.OK  # assign error code
            # in order to set flags
            if count == 1:
                self.flag = DSFlags.begin

            elif count < freq_to_send:
                self.flag = DSFlags.more

            elif count == freq_to_send:
                self.flag = DSFlags.finish

            else:
                print('an error has occurred!!!')

            self.changed_section = 0
            self.reserved_1 = self.null_byte
            self.reserved_2 = self.null_byte
            self.reserved_3 = self.null_byte

            self.section_id = int(self.array[1])
            try:
                self.data = item.encode()
            except AttributeError as err:
                self.data = item

            self.checksum = self.ds_pdu.get_checksum(self.timestamp, self.data)

            pdu_array = [
                self.message_type, self.timestamp, self.error_code, self.flag,
                self.changed_section, self.section_id, self.reserved_1,
                self.reserved_2, self.reserved_3, self.data, self.data_size,
                self.checksum
            ]

            arr.append(pdu_array)

        print(arr)
        return arr  # we are only returning array during the commit

    def logoff(self):
        self.message_type = DSMessageType.LOGOFF
        self.timestamp = self.ds_pdu.get_time()  # get timestamp
        self.error_code = self.ds_code.OK  # assign error code
        self.flag = DSFlags.finish
        self.reserved_1 = self.null_byte
        self.reserved_2 = self.null_byte
        self.reserved_3 = self.null_byte
        self.changed_section = 0
        self.section_id = 0
        self.data = self.null_byte
        self.data_size = len(self.data)
        self.checksum = self.ds_pdu.get_checksum(self.timestamp, self.data)

        pdu_array = [
            self.message_type, self.timestamp, self.error_code, self.flag,
            self.changed_section, self.section_id, self.reserved_1,
            self.reserved_2, self.reserved_3, self.data, self.data_size,
            self.checksum
        ]
        return pdu_array

    def break_data(self, data):
        print('WE are in break data')
        print('DATA : {}'.format(data))

        allowed_data_size = self.ds_pdu.get_data_size()
        arr = []
        print('allowed data size: {}, data size {}'.format(
            allowed_data_size, len(data)))
        if len(
                data
        ) > allowed_data_size:  # if the size is bigger than the size allocated in struct
            # break data
            print('We are in break data: {}'.format(data))
            data_size = len(data)
            number_of_breakdowns = data_size / allowed_data_size

            stop = allowed_data_size
            start = 0
            for i in range(math.ceil(number_of_breakdowns)):
                data_chunk = data[start:stop].encode('utf-8')
                arr.append(data_chunk)
                start = stop
                stop = stop + allowed_data_size

            return arr

        else:
            print('before')
            print(arr)
            arr.append(data)
            return arr
Ejemplo n.º 3
0
class DSServer:
    """
    Used to create our server object
    """
    __BUFFER_SIZE = None
    __PORT = 5005
    __TCP_IP = '127.0.0.1'
    __SERVER_SOCKET = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    __username = '******'
    __password = '******'
    __document_name = 'example1'

    def __init__(self):
        self.server_pdu = DSPdu()
        self.__BUFFER_SIZE = self.server_pdu.get_buffer_size()
        self.__SERVER_SOCKET.bind((self.__TCP_IP, self.__PORT))
        self.ds_document = DSDocument(DSPdu().get_data_size(
        ))  # we are going to change this into some variable later
        self.null_byte = b'\x00'
        self.message_type_index, self.timestamp_index, self.error_code_index, self.flag_index, self.changed_section_index, self.section_id_index, self.reserved_1_index, self.reserved_2_index, self.reserved_3_index, self.data_index, self.data_size_index, self.checksum_index = DSPdu(
        ).get_pdu_parts_index()
        self.server_log_manager = DSServerLogManagement()
        self.string_builder = ''
        self.control_ack = {}
        self.color = Color()
        self.style = Style()
        self.authentication = DSAuthentication()

    def start(self):
        self.__SERVER_SOCKET.listen(1)
        str_to_print = 'SERVER started @ {} {}\n'.format(
            self.__TCP_IP, self.__PORT)
        print(self.color.biege(str_to_print))
        self.server_log_manager.log_server_start(str_to_print)

        while True:
            client_socket, client_address = self.__SERVER_SOCKET.accept()
            self.server_log_manager.add_client_connection(
                client_socket, client_address)
            print('connected to: {}'.format(
                self.color.green(str(client_address))))

            try:
                c_thread = threading.Thread(target=self.client_thread,
                                            args=(client_socket,
                                                  client_address)).start()
            except (OSError) as err:
                print("Thread:  ", c_thread.__name__, "CLOSED")

    def client_thread(self, client_socket, client_address):
        # create pdu object for the client, this happens only once for each client

        authorized = False  # now the client have no authorization
        client_state = DSState()  # create client state object
        client_pdu = DSPdu()  # create pdu object
        client_string_builder = DSStringBuilder()
        self.__BUFFER_SIZE = client_pdu.get_buffer_size(
        )  # set the buffer size

        client_error_correction = DSErrorCorrection()

        ###############################
        # send the first hello message#
        ###############################

        # assign the pdu parameters

        client_state.token_issued = False
        token_id = ''

        # send connect message to the client and then start the timer immediately

        connect_thread = threading.Thread(target=self.connect,
                                          args=(client_state, client_pdu,
                                                client_socket, client_address,
                                                client_error_correction))
        connect_thread.start()

        # self.connect(client_state, client_pdu, client_socket, client_address, client_timer)

        ###################################
        # Now receive message and process #
        ###################################
        client_state.is_client_alive = True
        while client_state.is_client_alive:
            try:
                pdu = client_socket.recv(self.__BUFFER_SIZE)
                # print('buffer size: {}'.format(self.__BUFFER_SIZE))
                print(pdu)
                unpacked_pdu = client_pdu.unpack(pdu)
                unpacked_pdu_no_pad = client_pdu.remove_padding(unpacked_pdu)

            except ConnectionResetError as err:
                print(err.args)
                str_to_print = 'The client @ {} closed connection'.format(
                    client_address)
                print(str_to_print)
                self.server_log_manager.remove_client_connection(
                    client_socket, client_address)
                break

                # CHECK STATE, IF THE CLIENT IS NOT FOLLOWING STATE, REJECT#

            current_state = client_state.get_current_state()
            # print('unpacked pdu without padding: {}'.format(unpacked_pdu_no_pad))

            message_type = unpacked_pdu_no_pad[
                self.message_type_index].encode()
            timestamp = unpacked_pdu_no_pad[self.timestamp_index]
            error_code = unpacked_pdu_no_pad[self.error_code_index]
            flag = unpacked_pdu_no_pad[self.flag_index].encode()
            data_size = unpacked_pdu_no_pad[self.data_size_index]
            changed_section = unpacked_pdu_no_pad[self.changed_section_index]
            section_id = unpacked_pdu_no_pad[self.section_id_index]
            reserved_1 = unpacked_pdu_no_pad[self.reserved_1_index]
            reserved_2 = unpacked_pdu_no_pad[self.reserved_2_index]
            reserved_3 = unpacked_pdu_no_pad[self.reserved_3_index]
            data = unpacked_pdu_no_pad[self.data_index]

            # print(current_state, message_type)
            if message_type == DSMessageType.CONNECT:
                pass
            elif message_type == DSMessageType.CAUTH:
                client_error_code = None
                self.cauth(data, client_state, client_pdu, client_socket,
                           client_address, client_error_correction)

            elif message_type == DSMessageType.CREATE:
                pass

            elif message_type == DSMessageType.S_EDIT:
                self.s_edit(section_id, client_state, client_pdu,
                            client_socket, client_address,
                            client_error_correction)

            elif message_type == DSMessageType.S_COMMIT:
                # print(data, flag)
                # print('data before the concatenation: {}'.format(data))
                # print(client_pdu, unpacked_pdu, unpacked_pdu_no_pad)

                if flag == DSFlags.more or flag == DSFlags.begin:
                    client_string_builder.append(data)
                    print(self.color.yellow('string after concatenation'))
                    print('string {}'.format(client_string_builder.text))
                    print('----------------------------------------------')

                else:  # flag == DSFlags.finish:
                    if client_string_builder.text != '':  # if client string builder contains something
                        client_string_builder.append(data)
                        data = client_string_builder.text
                        print(
                            self.color.biege(
                                'data received from the client to be committed: {}'
                                .format(data)))
                        client_string_builder.reset()  # reset string builder

                    else:
                        # if string builder is None, then data that was sent for commit is equal to the size of struct
                        pass

                self.s_commit(section_id, data, client_state, client_pdu,
                              client_socket, client_address,
                              client_error_correction)

            elif message_type == DSMessageType.S_RELEASE:
                self.release(section_id, client_pdu, client_state,
                             client_socket, client_address)

            elif message_type == DSMessageType.LOGOFF:
                client_state.is_client_alive = False
                self.close(client_pdu, client_state, client_socket,
                           client_address)

            else:
                print('an error have occured!!!')

    def connect(self,
                client_state,
                client_pdu,
                client_socket,
                client_address,
                client_error_correction,
                client_error_code=None):
        # assign the resigning parameters
        message_type = DSMessageType.CONNECT
        timestamp = client_pdu.get_time()
        error_code = ''
        changed_section = 0
        section_id = 0
        reserved_1 = self.null_byte
        reserved_2 = self.null_byte
        reserved_3 = self.null_byte
        data = b'HELLO CLIENT'
        data_size = len(data)
        checksum = client_pdu.get_checksum(timestamp, data)
        flag = DSFlags.finish
        if client_error_code is None:
            error_code = DSCode.CONNECTED
        else:
            error_code = client_error_code

        # add recently sent data to the error-correction-tracker

        pdu_array = [
            message_type, timestamp, error_code, flag, changed_section,
            section_id, reserved_1, reserved_2, reserved_3, data, data_size,
            checksum
        ]
        client_error_correction.add_recently_sent_data(data, checksum,
                                                       message_type, flag)

        ##################################
        # set state#
        ##################################
        # print(pdu_array)

        pdu = client_pdu.pack(pdu_array)
        # print(pdu)
        client_state.set_state(DSState.CONNECTED)
        client_socket.send(pdu)
        # # start timer for ack
        # timer_thread = threading.Thread(target=client_timer.start_timer()).start()

    def cauth(self,
              data,
              client_state,
              client_pdu,
              client_socket,
              client_address,
              client_error_correction,
              client_error_code=None):
        # verify if the client presented a valid credentials
        if client_error_code is None:
            error_code = DSCode.LOGIN_SUCCESS
            _, username, password, document_name = data.split(',')

            if username == self.authentication.get_username(
            ) and password == self.authentication.get_password(
            ) and document_name == self.authentication.get_document_name():

                self.server_log_manager.add_authenticated_client_connection(
                    client_socket, client_address)
                # Now send the document.txt to the client
                # when we start the server, we want the server to read the document once, and then update with the latest
                # using the dictionary, otherwise, the server won't be able to keep track of which client have the section

                freq_to_send = len(self.ds_document.document_as_dic)
                print('data to be sent')
                print('---------------')
                for item in self.ds_document.document_as_dic.values():
                    print(self.color.yellow(item[0].decode()))

                count = 1
                self.server_log_manager.document_sent_to_client.update(
                    {client_address: dict(self.ds_document.document_as_dic)})

                client_state.set_as_previous_document(
                    self.ds_document.document_as_dic)

                for section_details, section_id in zip(
                        self.ds_document.document_as_dic.values(),
                        self.ds_document.document_as_dic.keys()
                ):  # we don't care about the document.txt flags about sections taken/free
                    message_type = DSMessageType.CAUTH
                    timestamp = client_pdu.get_time()  # get timestamp
                    error_code = DSCode.LOGIN_SUCCESS  # assign error code
                    data = section_details[0]
                    changed_section = 0
                    section_id = str(section_id).encode()

                    print('count: {}, freq: {}'.format(count, freq_to_send))

                    if count == 1:
                        flag = DSFlags.begin

                    elif count < freq_to_send:
                        flag = DSFlags.more

                    elif count == freq_to_send:
                        flag = DSFlags.finish

                    else:
                        print("Error have occurred!!!")

                    count += 1

                    reserved_1 = self.null_byte
                    reserved_2 = self.null_byte
                    reserved_3 = self.null_byte
                    section_id = (count - 1)
                    data = data  # verify if the data is already encoded
                    data_size = len(data)
                    checksum = client_pdu.get_checksum(timestamp, data)
                    pdu_array = [
                        message_type, timestamp, error_code, flag,
                        changed_section, section_id, reserved_1, reserved_2,
                        reserved_3, data, data_size, checksum
                    ]

                    # print(pdu_array)
                    pdu = client_pdu.pack(pdu_array)
                    #############################################
                    # SEND THE DOCUMENT
                    #############################################
                    client_socket.send(pdu)
                    ##################################
                    # set state#
                    ##################################
                    client_state.set_state(DSState.AUTHENTICATED)
                    # print('pdu before send: {}'.format(pdu))

            else:
                client_state.set_state(DSState.CONNECTED)
                print(self.color.red('Client presented wrong credentials'))
                self.connect(client_state, client_pdu, client_socket,
                             client_address, client_error_correction,
                             DSCode.LOGIN_NOT_SUCCESS)
        else:
            message_type = DSMessageType.CAUTH
            timestamp = client_pdu.get_time()  # get timestamp
            error_code = client_error_code
            flag = DSFlags.finish
            changed_section = 0
            section_id = 0
            reserved_1 = self.null_byte
            reserved_2 = self.null_byte
            reserved_3 = self.null_byte
            data = data  # verify if the data is already encoded
            data_size = len(data)
            checksum = client_pdu.get_checksum(timestamp, data)

            pdu_array = [
                message_type, timestamp, error_code, flag, changed_section,
                section_id, reserved_1, reserved_2, reserved_3, data,
                data_size, checksum
            ]

            pdu = client_pdu.pack(pdu_array)
            #############################################
            # SEND THE DOCUMENT
            #############################################
            client_socket.send(pdu)
            ##################################
            # set state#
            ##################################
            client_state.set_state(DSState.AUTHENTICATED)

    def s_edit(self,
               section_id,
               client_state,
               client_pdu,
               client_socket,
               client_address,
               client_error_correction,
               client_error_code=None):
        authenticated_clients = self.server_log_manager.get_authenticated_clients(
        )
        # print('in s_edit')
        # print('authenticated clients: {}'.format(authenticated_clients))

        # verify if the user is authenticated
        if client_socket in authenticated_clients:
            section_id = int(section_id)
            error_code = ''
            if client_error_code is None:
                error_code = DSCode.SECTION_RETRIEVED

            else:
                error_code = error_code
            # print(self.ds_document.get_document_sections())

            # if the client is currently in possession of a section, don't issue another section
            if client_state.token_issued:
                message_type = b'S_EDIT'
                timestamp = client_pdu.get_time()  # get timestamp

                current_section_owners = self.server_log_manager.get_section_owners(
                )

                if client_address is current_section_owners.get(section_id):
                    data = b'You are the current section owner'
                    error_code = DSCode.CLIENT_IS_THE_CURRENT_SECTION_OWNER  # assign error code
                    section_id = section_id

                else:
                    for key, value in current_section_owners.items():
                        if key == section_id:
                            data = str(
                                value).encode() + b' is the current owner'
                            error_code = DSCode.SECTION_NOT_AVAILABLE  # assign error code
                            section_id = section_id

                        else:
                            data = self.null_byte
                            error_code = DSCode.SECTION_DENIED
                            section_id = 0

                flag = DSFlags.finish
                changed_section = 0

                reserved_1 = self.null_byte
                reserved_2 = self.null_byte
                reserved_3 = self.null_byte

                data_size = len(data)
                checksum = client_pdu.get_checksum(timestamp, data)

                pdu_array = [
                    message_type, timestamp, error_code, flag, changed_section,
                    section_id, reserved_1, reserved_2, reserved_3, data,
                    data_size, checksum
                ]

                pdu = client_pdu.pack(pdu_array)
                ##################################
                # set state#
                ##################################
                client_state.set_state(DSState.AUTHENTICATED)
                self.server_log_manager.log(client_address,
                                            DSState.EDITING_DOCUMENT)
                client_socket.send(pdu)
                pass
            else:
                if section_id in self.ds_document.get_document_sections():
                    # print(self.ds_document.get_document_sections())
                    section_data_tuple = self.ds_document.get_document_sections(
                    ).get(section_id)
                    section_data = section_data_tuple[0]
                    is_free = section_data_tuple[1]

                    # find out if the section the user requested for is free.
                    if is_free:
                        # print(self.ds_document.get_document_sections())
                        self.server_log_manager.add_section_owners(
                            client_address, section_id)
                        self.ds_document.get_document_sections().update(
                            {section_id:
                             (section_data, False)})  # set flag on data
                        # print('the section data : {}'.format(section_data))
                        # print('After dictionary flags')
                        # print(self.ds_document.get_document_sections())

                        message_type = b'S_EDIT'
                        timestamp = client_pdu.get_time()  # get timestamp\
                        flag = DSFlags.finish
                        changed_section = 0
                        section_id = section_id

                        reserved_1 = self.null_byte
                        reserved_2 = self.null_byte
                        reserved_3 = self.null_byte

                        data = section_data
                        data_size = len(data)
                        checksum = client_pdu.get_checksum(timestamp, data)

                        pdu_array = [
                            message_type, timestamp, error_code, flag,
                            changed_section, section_id, reserved_1,
                            reserved_2, reserved_3, data, data_size, checksum
                        ]

                        pdu = client_pdu.pack(pdu_array)
                        ##################################
                        # set state#
                        ##################################
                        client_state.set_state(DSState.EDITING_DOCUMENT)
                        client_state.token_issued = True
                        self.server_log_manager.log(client_address,
                                                    DSState.EDITING_DOCUMENT)
                        #############################################
                        # SEND THE DOCUMENT
                        #############################################
                        client_socket.send(pdu)

                    else:  # data is not free
                        # find out who is the current owner of the document.txt
                        #
                        current_section_owners = self.server_log_manager.get_section_owners(
                        )
                        data = b''
                        if client_address is current_section_owners.get(
                                section_id):
                            data = b'You are the current section owner'
                            error_code = DSCode.CLIENT_IS_THE_CURRENT_SECTION_OWNER  # assign error code

                        else:
                            for key, value in current_section_owners.items():
                                if key == section_id:
                                    data = str(value).encode(
                                    ) + b' is the current owner'
                                    error_code = DSCode.SECTION_NOT_AVAILABLE  # assign error code

                        message_type = b'S_EDIT'
                        timestamp = client_pdu.get_time()  # get timestamp\
                        flag = DSFlags.finish
                        changed_section = 0
                        section_id = 0

                        reserved_1 = self.null_byte
                        reserved_2 = self.null_byte
                        reserved_3 = self.null_byte

                        data_size = len(data)
                        checksum = client_pdu.get_checksum(timestamp, data)

                        pdu_array = [
                            message_type, timestamp, error_code, flag,
                            changed_section, section_id, reserved_1,
                            reserved_2, reserved_3, data, data_size, checksum
                        ]

                        pdu = client_pdu.pack(pdu_array)
                        # print(data)
                        ##################################
                        # set state#
                        ##################################
                        client_state.set_state(DSState.BLOCKED)
                        #############################################
                        # SEND THE DOCUMENT
                        #############################################
                        client_socket.send(pdu)
                        # print(pdu)
                        issued_token = False
                        token_id = None
                        return issued_token, token_id

                        # if the section id presented by the client is invalid or they don't possess a token for it
                else:
                    error_code = DSCode.SECTION_ID_NOT_VALID
                    message_type = b'S_EDIT'
                    timestamp = client_pdu.get_time()  # get timestamp\
                    flag = DSFlags.finish
                    changed_section = 0
                    section_id = 0

                    reserved_1 = self.null_byte
                    reserved_2 = self.null_byte
                    reserved_3 = self.null_byte
                    data = b'invalid section id was presented'

                    data_size = len(data)
                    checksum = client_pdu.get_checksum(timestamp, data)

                    pdu_array = [
                        message_type, timestamp, error_code, flag,
                        changed_section, section_id, reserved_1, reserved_2,
                        reserved_3, data, data_size, checksum
                    ]

                    pdu = client_pdu.pack(pdu_array)
                    print(data)
                    ##################################
                    # set state#
                    ##################################
                    client_state.set_state(DSState.BLOCKED)
                    #############################################
                    # SEND THE DOCUMENT
                    #############################################
                    client_socket.send(pdu)
                    issued_token = False
                    token_id = None
                    return issued_token, token_id

        # if the user is not authenticated
        else:
            self.connect(client_state, client_pdu, client_socket,
                         client_address, client_error_correction,
                         DSCode.USER_NOT_AUTHENTICATED)

    def s_commit(self,
                 section_id,
                 data,
                 client_state,
                 client_pdu,
                 client_socket,
                 client_address,
                 client_error_correction,
                 client_error_code=None):

        print('in commit, section')

        section_id = int(section_id)
        print('authenticated clients: {}'.format(
            self.server_log_manager.get_authenticated_clients()))
        print('section owners: {}'.format(
            self.server_log_manager.get_section_owners().values()))
        print(self.server_log_manager.get_section_owners())
        # we have to verify if the client have token for the section to which they are committing

        # if the client exists in the list of authenticated clients and the client possesses the token for the section

        if client_socket in self.server_log_manager.authenticated_clients and client_address in self.server_log_manager.get_section_owners(
        ).values():
            # print(self.color.yellow('client possess a token for this commit'))
            print('client address: {} section id: {}'.format(
                client_address, section_id))
            print('client currently with  the section token : ', end='')
            print(self.server_log_manager.get_section_owners().get(section_id))

            # we send only when the flag is now finish

            if client_address == self.server_log_manager.get_section_owners(
            ).get(int(section_id)):
                # print('data: {}'.format(data))
                self.ds_document.update_document(section_id, data)
                ##################################
                # set state#
                ##################################
                client_state.set_state(DSState.COMMITTING_CHANGES)
                self.server_log_manager.log(client_address,
                                            client_state.get_current_state())
                client_state.token_issued = False
                #############################################
                print('authenticated clients: {}'.format(
                    self.server_log_manager.authenticated_clients))

                for auth_client_socket in self.server_log_manager.authenticated_clients:
                    # send the update to the authenticated clients, but first inform them that an update is coming

                    message_type = DSMessageType.CAUTH
                    timestamp = client_pdu.get_time()  # get timestamp
                    error_code = DSCode.COMMIT_UPDATE
                    flag = DSFlags.finish
                    changed_section = 0
                    section_id = section_id
                    reserved_1 = self.null_byte
                    reserved_2 = self.null_byte
                    reserved_3 = self.null_byte

                    data = self.null_byte
                    data_size = len(data)
                    checksum = client_pdu.get_checksum(timestamp, data)

                    pdu_array = [
                        message_type, timestamp, error_code, flag,
                        changed_section, section_id, reserved_1, reserved_2,
                        reserved_3, data, data_size, checksum
                    ]

                    pdu = client_pdu.pack(pdu_array)
                    print('forwarding updates to : {}'.format(
                        auth_client_socket))
                    auth_client_socket.send(pdu)
                    self.server_log_manager.add_authenticated_client_connection(
                        client_socket, client_address)

                    # Now send the document.txt to the client
                    data_string = self.ds_document.get_document_as_string(
                    )  # get the entire document.txt as string
                    data_break_down = self.ds_document.break_data(data_string)

                    freq_to_send = len(data_break_down)
                    count = 0

                    previous_data_sent_to_client = client_state.received_document

                    for new_item, old_item in zip(
                            data_break_down,
                            previous_data_sent_to_client.values()
                    ):  # we need to make provision for when

                        count += 1
                        message_type = DSMessageType.CAUTH
                        timestamp = client_pdu.get_time()  # get timestamp
                        error_code = DSCode.COMMIT_UPDATE  # assign error code

                        if count == freq_to_send:
                            flag = DSFlags.finish
                        else:
                            flag = DSFlags.more

                        if new_item == old_item[0]:
                            changed_section = 0
                        else:
                            print('----------------------------------')
                            print('new item: {}'.format(new_item))
                            print('old_item: {}'.format(old_item[0]))
                            print('----------------------------------')
                            changed_section = 1

                        reserved_1 = self.null_byte
                        reserved_2 = self.null_byte
                        reserved_3 = self.null_byte
                        section_id = count - 1
                        data = new_item
                        data_size = len(data)
                        checksum = client_pdu.get_checksum(timestamp, data)
                        pdu_array = [
                            message_type, timestamp, error_code, flag,
                            changed_section, section_id, reserved_1,
                            reserved_2, reserved_3, data, data_size, checksum
                        ]
                        pdu = client_pdu.pack(pdu_array)

                        client_state.set_state(DSState.COMMITTING_CHANGES)

                        # SEND THE DOCUMENT
                        #############################################
                        client_socket.send(pdu)

                    self.server_log_manager.document_sent_to_client.update({
                        client_address:
                        dict(self.ds_document.document_as_dic)
                    })
                    client_state.set_as_previous_document(
                        self.ds_document.document_as_dic)
            else:
                print('client presented an invalid commit id')
                self.s_edit(section_id, client_state, client_pdu,
                            client_socket, client_address,
                            DSCode.SECTION_ID_NOT_VALID)

        # if the client exist in the authenticated client list but the client doesn't possess the token for the section which they are requesting
        elif client_socket in self.server_log_manager.get_authenticated_clients(
        ) and client_address not in self.server_log_manager.get_section_owners(
        ).values():
            #
            # print(self.color.red('testing'))
            # print('testing for the keys')
            # x = [value for value in self.server_log_manager.get_section_owners().values()]
            # print(x)
            # print('testing for keys end')
            # print('client_address: ' + str(client_address))
            # print(self.server_log_manager.get_section_owners().keys())
            # print(self.server_log_manager.get_section_owners())
            # print(self.server_log_manager.get_authenticated_clients())
            print(
                'if the client exist in the authenticated client list but the client doesn\'t possess the token for the section which they are requesting'
            )
            data = self.null_byte
            self.cauth(data, client_state, client_pdu, client_socket,
                       client_address, client_error_correction,
                       DSCode.SECTION_DENIED)

        # if the client is neither authenticated nor authorized
        else:
            print('client is neither authenticated nor authorized')
            self.connect(client_state, client_pdu, client_socket,
                         client_address, client_error_correction,
                         DSCode.USER_NOT_AUTHENTICATED)

    @staticmethod
    def close(client_pdu, client_state, client_socket, client_address):
        print("CLOSING CLIENT'S CONNECTION: ")
        client_state.is_alive = False
        client_socket.close()
        client_state.set_state(DSState.CLOSE)

    def release(self, section_id, client_pdu, client_state, client_socket,
                client_address):
        # first, before, the client requests for a section to be released, we need to verify if the client have the
        # token for the section

        # set the non-changing parameters
        message_type = DSMessageType.S_RELEASE
        timestamp = client_pdu.get_time()  # get timestamp
        flag = DSFlags.finish
        changed_section = 0
        reserved_1 = self.null_byte
        reserved_2 = self.null_byte
        reserved_3 = self.null_byte
        section_id = int(section_id)

        data = self.null_byte
        data_size = len(data)

        checksum = client_pdu.get_checksum(timestamp, data)

        # set a placeholder for the changing parameters
        error_code = ''

        # if the client is authenticated and the client have any section token
        if client_socket in self.server_log_manager.authenticated_clients and client_address == self.server_log_manager.get_section_owners(
        ).get(int(section_id)):
            client_state.set_state(DSState.RELEASE)
            self.server_log_manager.log(client_address,
                                        client_state.get_current_state())
            successful = self.ds_document.release(section_id)
            print('outcome of the release: {}'.format(successful))

            if successful:
                error_code = DSCode.RELEASE_SECTION_SUCCESSFUL
                self.server_log_manager.update_section_owners(
                    client_address, section_id)
                client_state.token_issued = False

            else:
                error_code = DSCode.RELEASE_SECTION_NOT_SUCCESSFUL

        else:  # if client is authenticated, but they don't have a token for the session
            if client_socket in self.server_log_manager.authenticated_clients:
                section_id = int(section_id)

                # if the section requested to be released by the client is free
                if section_id in self.ds_document.get_document_sections():
                    print(self.ds_document.get_document_sections())
                    section_data_tuple = self.ds_document.get_document_sections(
                    ).get(section_id)
                    section_data = section_data_tuple[0]
                    is_free = section_data_tuple[1]

                    # find out if the section the user requested for is free.
                    if is_free:
                        error_code = DSCode.SECTION_TOKEN_NOT_FOUND_SECTION_IS_FREE

                    else:
                        # if the section is not free
                        error_code = DSCode.RELEASE_SECTION_NOT_SUCCESSFUL

                else:
                    # if the section requested by the client doesn't exist, we have to deny the request
                    error_code = DSCode.SECTION_ID_NOT_VALID

                    pass

        print('section owners: {}'.format(
            self.server_log_manager.get_section_owners()))

        section_id = int(section_id)
        pdu_array = [
            message_type, timestamp, error_code, flag, changed_section,
            section_id, reserved_1, reserved_2, reserved_3, data, data_size,
            checksum
        ]
        #
        # for item in pdu_array:
        #     print('{} : type: {}'.format(item, type(item)))

        pdu = client_pdu.pack(pdu_array)
        # print(data)
        ##################################
        # set state#
        ##################################
        client_state.set_state(DSState.AUTHENTICATED)
        self.server_log_manager.log(client_address,
                                    client_state.get_current_state())
        #############################################
        # SEND THE DOCUMENT
        #############################################
        client_socket.send(pdu)

    @staticmethod
    def get_time():
        return str(datetime.now()).encode('utf-8')

    @staticmethod
    def get_checksum(timestamp, data):
        try:
            return binascii.crc32(timestamp + data)
        except TypeError as err:
            print('This value {} is not a byte'.format(data))

    @staticmethod
    def remove_space(array):
        new_array = []
        for item in array:
            new_array.append(item.strip())

        return new_array

    def response(self, request, error_code, data):
        """
        This is used to break server response to be sent into chucks of pdu
        ie if data = 'Access denied'
        the output is similar to [[b'S_DATA', 3434343, b'timestamp, b'Access ], [b'S_DATA', 36674545, b'timestamp, b'Denied']]
        :param error_code:
        :param byte request:
        :param string data:
        :return: list
        """
        array = []
        if request == b'CAUTH' and data != self.__null_byte:
            # process differently
            data_array = self.ds_document.break_data(data)
            # print('after data is broken: {}'.format(data_array))
            for item in data_array:  # for all the items we have to generate a different timestamp and checkum
                timestamp = self.get_time()
                checksum = self.get_checksum(timestamp, item)
                array.append([request, checksum, timestamp, error_code, item])
            # print(array)
            # print(array)
            return array

        else:  # if we are sending a generic response, then
            timestamp = self.get_time()
            checksum = self.get_checksum(timestamp, data)

            array = [request, checksum, timestamp, error_code, data]
            return array

    def response_document(self, request, error_code, data_array):
        """
        Used for sending documents only
        :param request:
        :param data_array:
        :return:
        """

        if (request == b'CAUTH'
                or request == b'S_DATA') and type(data_array) is list:
            array = []
            for item in data_array:  # for all the items we have to generate a different timestamp and checkum
                timestamp = self.get_time()
                checksum = self.get_checksum(timestamp, item)
                array.append([request, checksum, timestamp, error_code, item])
            # print(array)
            # print(array)
            return array