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 __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 __init__(self, allowed_data_size, path=__default_path): self.__path = self.__default_path self.document_as_dic = {} self.document = '' self.free = True self.not_Free = False self.data_broken = False self.__set_document_as_string() # set document.txt as string self.allowed_data_size = allowed_data_size self.__set_document_as_string() self.__set_document_as_sections() self.document_before_edit = {} self.color = Color()
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()
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
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
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
class DSDocument: __default_path = os.path.abspath("document.txt") def __init__(self, allowed_data_size, path=__default_path): self.__path = self.__default_path self.document_as_dic = {} self.document = '' self.free = True self.not_Free = False self.data_broken = False self.__set_document_as_string() # set document.txt as string self.allowed_data_size = allowed_data_size self.__set_document_as_string() self.__set_document_as_sections() self.document_before_edit = {} self.color = Color() def __set_document_as_string(self): """ Used to assign a value to self.document.txt :param data: :return: String """ try: with open(self.__path, 'r', encoding='utf-8') as file: self.document = file.read() except (FileNotFoundError, FileExistsError) as err: print(err.args, self.__path) def get_document_as_string(self): """ Used to get the document_string :return: string """ self.__set_document_as_string( ) # get the most updated copy of the document self.__set_document_as_sections( ) # arrange the document into sections in preparation for them to be sent. return self.document def __set_document_as_sections(self): """ Used to set the document.txt into sections :return: """ arr = [] if len( self.document ) > self.allowed_data_size: # if the size is bigger than the size allocated in struct # break data # print('We are in break data: {}'.format(self.document.txt)) data_size = len(self.document) number_of_breakdowns = data_size / self.allowed_data_size stop = self.allowed_data_size start = 0 for i in range(math.ceil(number_of_breakdowns)): data_chunk = self.document[start:stop].encode('utf-8') self.document_as_dic[i] = (data_chunk, self.free) arr.append(data_chunk) # print('length of data {}'.format(len(data_chunk))) start = stop stop = stop + self.allowed_data_size # print(self.document_as_dic) # print(arr) self.document_before_edit = self.document_as_dic return arr else: arr.append(self.document.encode('utf-8')) self.document_as_dic[0] = (arr[0], self.free) print(arr) return arr def get_document_sections(self): """ Used to get document.txt sections :return: dictionary """ return self.document_as_dic def update_document(self, section_id, data=None): """ Used to update the document.txt, and the dictionary :param int section_id: :param string data: :return: """ # Note: after the update, the length of the section may be greater that the allowed number section_id = int(section_id) if data is not None: print('data {}'.format(data)) self.document_as_dic.update( {section_id: (data.encode('utf-8'), self.free)}) # print(self.document_as_dic) print(self.document_as_dic) try: # write to the document.txt file print('Now writing to the document.txt') with open(self.__path, 'w', encoding='utf-8' ) as file: # update the main ds_document for item in self.document_as_dic.values(): item = item[0] file.write(item.decode('utf-8')) # Now read from file and convert the dictionary to the appropriate sizes self.__set_document_as_string() self.__set_document_as_sections() except (FileNotFoundError, FileExistsError) as err: print(err.args) print('------------------------------') print(self.color.biege(str(self.document_as_dic))) for item in self.document_as_dic.values(): print(self.color.yellow(str(item))) else: print( 'no data was provided, the client only wants to release the section !!!' ) section_id = int(section_id) section_data, _ = self.document_as_dic.get(section_id) print('section id {}'.format(section_id)) print('Performing a test---------') print(self.document_as_dic.get(0)) print('End of test') print('section_data {}'.format(section_data)) self.document_as_dic.update( {section_id: (section_data, self.free)}) print('document after update') print(self.document_as_dic.items()) section_data_tuple = self.document_as_dic.get(section_id) section_data = section_data_tuple[0] is_free = section_data_tuple[1] return is_free def break_data(self, data): arr = [] if len( data ) > self.allowed_data_size: # if the size is bigger than the size allocated in struct # break data # print('We are in break data: {}'.format(self.document.txt)) data_size = len(data) number_of_breakdowns = data_size / self.allowed_data_size stop = self.allowed_data_size start = 0 for i in range(math.ceil(number_of_breakdowns)): data_chunk = self.document[start:stop].encode('utf-8') print(data_chunk, len(data_chunk)) arr.append(data_chunk) start = stop stop = stop + self.allowed_data_size return arr else: arr.append(data) return data def release(self, section_id): is_release_successful = self.update_document(section_id) return is_release_successful