def __create_socket(self) -> None: """ Create the passive socket :return: None """ try: # Create the socket self.ss = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) except OSError as e: shell_colors.print_red(f'\nCan\'t create the socket: {e}\n') sys.exit(socket.error) try: # Set the SO_REUSEADDR flag in order to tell the kernel to reuse the socket even if it's in a TIME_WAIT state, # without waiting for its natural timeout to expire. # This is because sockets in a TIME_WAIT state can’t be immediately reused. self.ss.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.ss.setsockopt(41, socket.IPV6_V6ONLY, 0) # Bind the local address (sockaddr) to the socket (ss) self.ss.bind(('', self.port)) # Transform the socket in a passive socket and # define a queue of SOMAXCONN possible connection requests self.ss.listen(socket.SOMAXCONN) except OSError: shell_colors.print_red(f'\nCan\'t handle the socket: {OSError}\n') sys.exit(socket.error)
def show(self) -> None: """ Shows the menu that interacts with the user :return: None """ choice = '' while choice != 'q': print('\n- Main Menù ------------------------------') print('| <1> List files |') print('| <2> List peer\'s file parts |') print('| <3> List logged peers |') print('------------------------------------------') choice = input('Select an option (q to exit): ') if choice in {'1', '2', '3'}: if choice == '1': command = "LISTFILES" elif choice == '2': command = "SHOWPARTS" elif choice == '3': command = "LISTPEERS" self.handler.serve(command) elif choice != 'q': shell_colors.print_red( 'Input code is wrong. Choose one action!\n') shell_colors.print_blue('\nBye!\n')
def show(self) -> None: """ Shows the menu that interacts with the user :return: None """ choice = '' while choice != 'q': print('\n- Main Menù -----------------------') print('| <1> Search a file to download |') print('| <2> Share a file |') print('| <3> Delete a shared file |') print('| <4> Show your superpeer |') print('-----------------------------------') choice = input('Select an option (q to logout): ') if choice in {'1', '2', '3', '4'}: if choice == '1': command = 'FIND' elif choice == '2': command = 'ADFF' elif choice == '3': command = 'DEFF' elif choice == '4': command = 'SHOWSUPER' self.handler.serve(command) elif choice != 'q': shell_colors.print_red('Input code is wrong. Choose one action!\n') self.handler.serve('LOGO') shell_colors.print_blue('\nBye!\n')
def show(self) -> None: """ Shows the menu that interacts with the user :return: None """ choice = '' while choice != 'q': print('\n- Main Menù -----------------------') print('| <1> Search a file to download |') print('| <2> Share a file |') print('| <3> Files in sharing |') print('| <4> Your tracker |') print('-----------------------------------') choice = input('Select an option (q to logout): ') if choice in {'1', '2', '3', '4'}: if choice == '1': command = 'LOOK' elif choice == '2': command = 'ADDR' elif choice == '3': command = 'SHAR' elif choice == '4': command = 'TRAC' self.handler.serve(command) elif choice == 'q': self.handler.serve('LOGO') if LocalData.tracker_is_empty(): break else: choice = '' continue elif choice != 'q': shell_colors.print_red('Input code is wrong. Choose one action!\n') # waiting 60s for any upload running spinner = SpinnerThread('Waiting for any upload in progress', '') spinner.start() timer = Timer(60, lambda: (self.server.stop(), spinner.stop())) timer.start() timer.join() spinner.join() LocalData.clear_shared_files() shell_colors.print_blue('\nYou leaved the Network\nBye!\n')
def startup(): while True: shell.print_blue('\nThis process will allow you to add a tracker.\n') tracker = net_utils.prompt_host_request('Insert a known host') LocalData.set_tracker(tracker) # LocalData.set_tracker(('172.16.1.', 'fc00::1:', 3000)) # tenta login ip = net_utils.get_local_ip_for_response() port = str(net_utils.get_peer_port()).zfill(5) packet = "LOGI" + ip + port tracker_ip4 = LocalData.get_tracker_ip4() tracker_ip6 = LocalData.get_tracker_ip6() tracker_port = LocalData.get_tracker_port() try: sock = net_utils.send_packet(tracker_ip4, tracker_ip6, tracker_port, packet) response = sock.recv(50).decode() if len(response) != 20: shell.print_red(f'There was an error in the login process: unexpected: {response}.\nPlease retry.') continue session_id = response[4:20] if session_id == '0' * 16: shell.print_red( f'There was an error in the login process: unexpected session_id: {session_id}.\nPlease retry.') continue LocalData.session_id = session_id break except (socket.error, AttributeError): shell.print_yellow(f'Unable to contact {tracker_ip4}|{tracker_ip6} [{tracker_port}]') if sock is not None: sock.close() continue shell.print_green(f'Successfully logged to the tracker: {tracker_ip4}|{tracker_ip6} [{tracker_port}]\n') log = Logger.Logger('peer/peer.log') server = ServerThread(net_utils.get_peer_port(), UploadHandler.UploadHandler(log)) server.daemon = True server.start() Menu(MenuHandler.MenuHandler(), server).show()
def __broadcast(self, packet: str) -> None: """ Send the packet to a pool of hosts :param packet: packet to be sent :return: None """ superfriends = LocalData.get_super_friends() for superfriend in superfriends: try: net_utils.send_packet_and_close( LocalData.get_super_friend_ip4(superfriend), LocalData.get_super_friend_ip6(superfriend), LocalData.get_super_friend_port(superfriend), packet) except socket.error as e: shell.print_red(f'\nUnable to send the packet on the socket: {e}') return
def prompt_parameters_request() -> None: """ Guide the user to insert his local ip adresses and port in case there are not/they are wrong :return: None """ if '' in (config['ipv4'], config['ipv6']): shell_colors.print_blue( '\nYou need to add your own network configuration to get started.\n' ) while True: if get_local_ipv4() == '': ip4 = input('Insert your local IPv4 address: ') try: ipaddress.IPv4Address(ip4) except ipaddress.AddressValueError: shell_colors.print_red( f'\n{ip4} is not a valid IPv4 address, please retry.\n') continue set_local_ipv4(ip4) break else: try: ipaddress.IPv4Address(get_local_ipv4()) break except ipaddress.AddressValueError: shell_colors.print_red( f'\n{get_local_ipv4()} is not a valid IPv4 address, please reinsert it.\n' ) set_local_ipv4('') continue while True: if get_local_ipv6() == '': ip6 = input('Insert your local IPv6 address: ') try: ipaddress.IPv6Address(ip6) except ipaddress.AddressValueError: shell_colors.print_red( f'\n{ip6} is not a valid IPv6 address, please retry.\n') continue set_local_ipv6(ip6) break else: try: ipaddress.IPv6Address(get_local_ipv6()) break except ipaddress.AddressValueError as e: shell_colors.print_red( f'\n{get_local_ipv6()} is not a valid IPv6 address, please reinsert it.\n' ) set_local_ipv6('') continue
def show(self) -> None: """ Shows the menu that interacts with the user :return: None """ choice = '' while choice != 'q': print('\n- Main Menù ----------------------------') print('| <1> Search a file to download |') print('| <2> Search other superpeers around you |') print('| <3> Add a file |') print('| <4> Delete a file |') print('| <5> List your known superpeers |') print('| <6> List your known peers |') print('| <7> List all files |') print('------------------------------------------') choice = input('Select an option (q to exit): ') if choice in {'1', '2', '3', '4', '5', '6', '7'}: if choice == '1': command = "QUER" elif choice == '2': command = "SUPE" elif choice == '3': command = "ADFF" elif choice == '4': command = "DEFF" elif choice == '5': command = "LISTSUPERPEERS" elif choice == '6': command = "LISTPEERS" elif choice == '7': command = "LISTFILES" self.handler.serve(command) elif choice != 'q': shell_colors.print_red( 'Input code is wrong. Choose one action!\n') shell_colors.print_blue('\nBye!\n')
def child(self, sd, clientaddr): (client, client_port) = socket.getnameinfo(clientaddr, socket.NI_NUMERICHOST) self.ss.close() request = sd.recv(self.BUFF_SIZE) shell.print_green(f'{client} [{client_port}] -> ', end='') print(f'{request.decode()}', end='') response = handler.serve(request) sd.send(response.encode()) shell.print_red(' -> ', end='') print(f'{response}') if response[0:4] == "ALGO": shell.print_blue( f'Client {client} [{client_port}] said goodbye! {int(response[4:])} files deleted.' ) sd.close() os._exit(0)
def show(self) -> None: """ Shows the menu that interacts with the user :return: None """ choice = '' while choice != 'q': print('\n- Main Menù -----------------------') print('| <1> Search a file to download |') print('| <2> Search all peers around you |') print('| <3> List peers |') print('| <4> Add a peer |') print('| <5> Remove a peer |') print('-----------------------------------') choice = input('Select an option (q to exit): ') if choice in {'1', '2', '3', '4', '5'}: if len(AppData.get_neighbours()) == 0: shell_colors.print_red( '\n! You need to have at least one peer to get started !\n' ) command = 'ADDPEER' elif choice == '1': command = 'QUER' elif choice == '2': command = 'NEAR' elif choice == '3': command = 'LISTPEERS' elif choice == '4': command = 'ADDPEER' elif choice == '5': command = 'REMOVEPEER' self.handler.serve(command) elif choice != 'q': shell_colors.print_red( 'Input code is wrong. Choose one action!\n') shell_colors.print_blue('\nBye!\n')
def start(self) -> None: """ Start file download :return: None """ try: sock = self.__connect(self.host_ip4, self.host_ip6, self.host_port, self.packet) except socket.error as e: shell_colors.print_red( f'\nImpossible to send data to {self.host_ip4}|{self.host_ip6} [{self.host_port}]: {e}\n' ) return ack = sock.recv(4).decode() if ack != "ARET": shell_colors.print_red( f'\nInvalid command received: {ack}. Expected: ARET\n') sock.close() return total_chunks = int(sock.recv(6).decode()) shell_colors.print_blue(f'\n#chunk: {total_chunks}') try: f_obj = open('shared/' + self.file_name, 'wb') except OSError as e: shell_colors.print_red(f'\nSomething went wrong: {e}\n') raise e progress_bar.print_progress_bar(0, total_chunks, prefix='Downloading:', suffix='Complete', length=50) for i in range(total_chunks): chunk_size = sock.recv(5) # if not all the 5 expected bytes has been received while len(chunk_size) < 5: chunk_size += sock.recv(1) chunk_size = int(chunk_size) data = sock.recv(chunk_size) # if not all the expected bytes has been received while len(data) < chunk_size: data += sock.recv(1) f_obj.write(data) progress_bar.print_progress_bar(i + 1, total_chunks, prefix='Downloading:', suffix='Complete', length=50) f_obj.close()
def prompt_host_request(message: str) -> tuple: """ Guide the user to manually insert peers in the data structure :param message: the message to be printed :return: tuple containing the added host """ while True: ip4 = input(f'{message} (IPv4): ') if ip4 == 'q': break try: ipaddress.IPv4Address(ip4) except ipaddress.AddressValueError: shell_colors.print_red( f'{ip4} is not a valid IPv4 address, please retry.') continue break while True: ip6 = input(f'{message} (IPv6): ') try: ipaddress.IPv6Address(ip6) except ipaddress.AddressValueError: shell_colors.print_red( f'{ip6} is not a valid IPv6 address, please retry.') continue break while True: port = input(f'{message} (port): ') try: port = int(port) if not 1024 < port < 65535: shell_colors.print_red(f'{port} must be in range 1025 - 65535') continue except ValueError: shell_colors.print_red( f'{port} is not a valid port number, please retry.') continue break return ip4, ip6, port
def prompt_friend_request() -> tuple: """ Guide the user to manually insert peers in the data structure :return: None """ while True: ip4 = input('Insert a known peer (IPv4): ') if ip4 == 'q': break try: ipaddress.IPv4Address(ip4) except ipaddress.AddressValueError: shell_colors.print_red( f'{ip4} is not a valid IPv4 address, please retry.') continue break while True: ip6 = input('Insert a known peer (IPv6): ') try: ipaddress.IPv6Address(ip6) except ipaddress.AddressValueError: shell_colors.print_red( f'{ip6} is not a valid IPv6 address, please retry.') continue break while True: port = input('Insert a known peer (port): ') try: port = int(port) if not 1024 < port < 65535: shell_colors.print_red(f'{port} must be in range 1025 - 65535') continue except ValueError: shell_colors.print_red( f'{port} is not a valid port number, please retry.') continue break return ip4, ip6, port
def prompt_neighbours_request() -> None: """ Guide the user to manually insert peers in the data structure :return: None """ while True: ip4 = input('Insert a known peer (IPv4): ') if ip4 == 'q': break try: ipaddress.IPv4Address(ip4) except ipaddress.AddressValueError: shell_colors.print_red(f'{ip4} is not a valid IPv4 address, please retry.') continue break while True: ip6 = input('Insert a known peer (IPv6): ') try: ipaddress.IPv6Address(ip6) except ipaddress.AddressValueError: shell_colors.print_red(f'{ip6} is not a valid IPv6 address, please retry.') continue break while True: port = input('Insert a known peer (port): ') try: port = int(port) if not 1024 < port < 65535: shell_colors.print_red(f'{port} must be in range 1025 - 65535') continue except ValueError: shell_colors.print_red(f'{port} is not a valid port number, please retry.') continue break AppData.add_neighbour(ip4, ip6, port) shell_colors.print_green(f'\nSuccessfully added the new peer: {ip4}|{ip6} [{port}]\n')
def serve(self, choice: str) -> None: """ Handle the peer packet :param choice: the choice to handle :return: None """ if choice == "QUER": # codice che manda il pkt sulla socket pktid = str(uuid.uuid4().hex[:16].upper()) ip = net_utils.get_local_ip_for_response() port = net_utils.get_aque_port() ttl = '50' while True: search = input('\nEnter the file name: ') if not 0 <= len(search) <= 20: shell_colors.print_red( '\nFile name must be between 1 and 20 chars long.\n') continue break packet = choice + pktid + ip + str(port).zfill( 5) + ttl + search.ljust(20) AppData.set_sent_packet(pktid) # avvio il server di ricezione delle response, lo faccio prima del broadcast # per evitare che i primi client che rispondono non riescano a connettersi server = ServerThread(port, SelfHandler()) server.daemon = True server.start() spinner = SpinnerThread('Searching files (ENTER to continue)', 'Research done!') spinner.start() timer = Timer(300, lambda: (spinner.stop(), server.stop())) timer.start() self.__broadcast(packet) input() print('\033[1A', end='\r') if timer.is_alive(): spinner.stop() spinner.join() timer.cancel() timer.join() server.stop() server.join() else: spinner.join() timer.join() server.join() files = AppData.get_peer_files() if len(files) < 1: shell_colors.print_red('\nNo matching results.\n') return # for count, file in enumerate(files, start=1): # print(f'{count}]', file) while True: index = input('\nPlease select a file to download: ') try: index = int(index) if 1 <= index <= len(files): break else: shell_colors.print_red( f'Index chosen must be in the correct range: 1 - {len(files)}.\n' ) except ValueError: shell_colors.print_red( f'Your choice must be a valid one: number in range 1 - {len(files)} expected.\n' ) host_ip4 = AppData.get_file_owner_ip4(files[index - 1]) host_ip6 = AppData.get_file_owner_ip6(files[index - 1]) host_port = AppData.get_file_owner_port(files[index - 1]) file_md5 = AppData.get_file_md5(files[index - 1]) file_name = AppData.get_file_name(files[index - 1]) # preparo packet per retr, faccio partire server in attesa download, invio packet e attendo packet = 'RETR' + file_md5 try: Downloader(host_ip4, host_ip6, host_port, packet, file_name).start() shell_colors.print_green( f'\nDownload of {file_name} completed.\n') AppData.clear_peer_files() AppData.add_shared_file(file_name, file_md5, os.stat('shared/' + file_name).st_size) except OSError: shell_colors.print_red( f'\nError while downloading {file_name}\n') elif choice == "NEAR": # NEAR[4B].Packet_Id[16B].IP_Peer[55B].Port_Peer[5B].TTL[2B] pktid = str(uuid.uuid4().hex[:16].upper()) ip = net_utils.get_local_ip_for_response() port = net_utils.get_anea_port() ttl = '03' packet = choice + pktid + ip + str(port).zfill(5) + ttl AppData.set_sent_packet(pktid) old_neighbours_len = len(AppData.get_neighbours()) # avvio il server di ricezione delle response, lo faccio prima del broadcast # per evitare che i primi client che rispondono non riescano a connettersi server = ServerThread(port, SelfHandler()) server.daemon = True server.start() spinner = SpinnerThread('Searching peers (ENTER to continue)', 'Research done!') spinner.start() timer = Timer(300, lambda: (spinner.stop(), server.stop())) timer.start() self.__broadcast(packet) input() print('\033[1A', end='\r') if timer.is_alive(): spinner.stop() spinner.join() timer.cancel() timer.join() server.stop() server.join() else: spinner.join() timer.join() server.join() if len(AppData.get_neighbours()) == old_neighbours_len: shell_colors.print_red('\nNo new peer found.\n') elif choice == 'ADDPEER': net_utils.prompt_neighbours_request() elif choice == 'LISTPEERS': shell_colors.print_green('\nList of known peers:') for count, neighbour in enumerate(AppData.get_neighbours(), start=1): shell_colors.print_blue( f'{count}] {AppData.get_peer_ip4(neighbour)} {AppData.get_peer_ip6(neighbour)} {str(AppData.get_peer_port(neighbour))}' ) elif choice == 'REMOVEPEER': neighbours = AppData.get_neighbours() shell_colors.print_green('\nList of known peers:') for count, neighbour in enumerate(neighbours, start=1): shell_colors.print_blue( f'{count}] {AppData.get_peer_ip4(neighbour)} {AppData.get_peer_ip6(neighbour)} {str(AppData.get_peer_port(neighbour))}' ) while True: index = input( '\nPlease select a peer to delete (q to cancel): ') if index == 'q': break try: index = int(index) if 1 <= index <= len(neighbours): AppData.remove_neighbour(index - 1) break else: shell_colors.print_red( f'Index chosen must be in the correct range: 1 - {len(neighbours)}.\n' ) except ValueError: shell_colors.print_red( f'Your choice must be a valid one: number in range 1 - {len(neighbours)} expected.\n' ) else: pass
def serve(self, choice: str) -> None: """ Handle the peer packet :param choice: the choice to handle :return: None """ if choice == "LISTFILES": try: conn = database.get_connection(self.db_file) conn.row_factory = database.sqlite3.Row except database.Error as e: print(f'Error: {e}') print( 'The server has encountered an error while trying to serve the request.' ) return try: files = file_repository.find_all(conn) shell.print_green('\nLogged peers files:') if not files: shell.print_red('There are not logged peers files.') for count, shared_file in enumerate(files, 1): print(f'{count}] {shared_file["file_name"]} ', end='') shell.print_yellow(f'{shared_file["file_md5"]}\n') except database.Error as e: conn.rollback() conn.close() print(f'Error: {e}') print( 'The server has encountered an error while trying to serve the request.' ) return elif choice == "SHOWPARTS": try: conn = database.get_connection(self.db_file) conn.row_factory = database.sqlite3.Row except database.Error as e: print(f'Error: {e}') print( 'The server has encountered an error while trying to serve the request.' ) return peer_list = peer_repository.find_all(conn) if not peer_list: shell.print_red('There are no logged peers.') conn.close() return else: shell.print_green('\nList of known peers:') for count, peer_row in enumerate(peer_list, 1): shell.print_blue(f'{count}]' + peer_row['ip'] + peer_row['port'] + '\n') while True: index = input('\nPlease select a peer(q to cancel): ') if index == 'q': print('\n') return try: index = int(index) - 1 except ValueError: shell.print_red( f'\nWrong index: number in range 1 - {len(peer_list)} expected.' ) continue if 0 <= index <= len(peer_list): chosen_peer = peer_list.pop(index) peer_session_id = chosen_peer['session_id'] file_rows = file_repository.get_all_peer_files( conn, peer_session_id) if not file_rows: shell.print_red('This peer has no file\'s parts') conn.close() return for count, file_row in enumerate(file_rows, 1): file_name = file_row['file_name'] file_md5 = file_row['file_md5'] part_list = bytearray( file_repository.get_part_list_by_file_and_owner( conn, file_md5, peer_session_id)) print(f'{count}] ', end='') shell.print_blue(f'{file_name}|{file_md5} -> ', end='') for byte_index in range(len(part_list)): print( f'{bin(part_list[byte_index])[2:].zfill(8)} ', end='') print('') return () else: shell.print_red( f'\nWrong index: number in range 1 - {len(peer_list)} expected.' ) continue elif choice == "LISTPEERS": try: conn = database.get_connection(self.db_file) conn.row_factory = database.sqlite3.Row except database.Error as e: print(f'Error: {e}') print( 'The server has encountered an error while trying to serve the request.' ) return try: peer_list = peer_repository.find_all(conn) if not peer_list: shell.print_red('There are not logged peers.') conn.close() return else: shell.print_green('\nList of known peers:') for count, peer_row in enumerate(peer_list, 1): shell.print_blue(f'{count}]' + peer_row['ip'] + peer_row['port'] + '\n') except database.Error as e: conn.rollback() conn.close() print(f'Error: {e}') print( 'The server has encountered an error while trying to serve the request.' ) return else: pass
def serve(self, sd: socket.socket) -> None: """ Handle the peer request :param sd: the socket descriptor used for read the request :return None """ try: response = sd.recv(300).decode() except socket.error as e: shell.print_red(f'Unable to read the response from the socket: {e}\n') sd.close() return sd.close() command = response[0:4] if command == "AQUE": if len(response) != 212: shell.print_red(f"Invalid response: : {command} -> {response}. Expected: AQUE<pkt_id><ip_peer><port_peer><file_md5><filename>") return pktid = response[4:20] ip_peer = response[20:75] ip4_peer, ip6_peer = net_utils.get_ip_pair(ip_peer) port_peer = int(response[75:80]) file_md5 = response[80:112] filename = response[112:212].lstrip().rstrip() if pktid != LocalData.get_sent_menu_quer_packet(): return if not LocalData.exist_menu_peer_file(ip4_peer, ip6_peer, port_peer, file_md5, filename): LocalData.add_menu_peer_file(ip4_peer, ip6_peer, port_peer, file_md5, filename) index = LocalData.menu_peer_file_index(ip4_peer, ip6_peer, port_peer, file_md5, filename) print(f'{index +1}] ', end='') print(f'{filename} ', end='') shell.print_yellow(f'md5={file_md5} ', end='') print(f'({ip4_peer}|{ip6_peer} [{port_peer}])') elif command == "ASUP": if len(response) != 80: shell.print_red(f"Invalid response: : {command} -> {response}. Expected: ASUP<pkt_id><ip_peer><port_peer>") return pktid = response[4:20] ip_peer = response[20:75] ip4_peer, ip6_peer = net_utils.get_ip_pair(ip_peer) port_peer = int(response[75:80]) if pktid != LocalData.get_sent_supe_packet(): return if not LocalData.is_super_friend(ip4_peer, ip6_peer, port_peer): LocalData.add_super_friend(ip4_peer, ip6_peer, port_peer) shell.print_green('New superfriend found: ', end='') print(f'{ip4_peer}|{ip6_peer} [{port_peer}]') else: shell.print_red(f"\nInvalid response: {command} -> {response}\n") return
shell.print_blue(' ') shell.print_blue(' / \ | | __ _', end='') shell.print_yellow('| \' / __ _ ______ _ __ _', end='') shell.print_blue(' _ __ ___ ') shell.print_blue(' / /\ \ | |/ _` ', end='') shell.print_yellow('| < / _` |_ / _` |/ _` |', end='') shell.print_blue(' \'_ ` _ \ ') shell.print_blue(' / ____ \| | (_| ', end='') shell.print_yellow('| . \ (_| |/ / (_| | (_| |', end='') shell.print_blue(' | | | | | ') shell.print_blue(' /_/ \_\_|\__,_', end='') shell.print_yellow('|_|\_\__,_/___\__,_|\__,_|', end='') shell.print_blue('_| |_| |_| ') if not os.path.exists('shared'): os.mkdir('shared') net_utils.prompt_parameters_request() choice = '' while choice != 'q': choice = input('Are you a super peer? (y/n): ') if choice == 'y': superpeer.startup() break elif choice == 'n': peer.startup() break else: shell.print_red('Input code is wrong. Choose y or n!\n')
def run(self) -> None: """ Start file download :return: None """ try: packet = 'RETP' + self.file_md5 + str(self.part_num).zfill(8) sock = self.__connect(self.owner_ip4, self.owner_ip6, self.owner_port, packet) except socket.error as e: shell_colors.print_red(f'\nImpossible to send data to {self.owner_ip4}|{self.owner_ip6} [{self.owner_port}]: {e}\n') return ack = sock.recv(4).decode() if ack != "AREP": shell_colors.print_red(f'Invalid command received: {ack}. Expected: AREP') sock.close() return try: total_chunks = int(sock.recv(6).decode()) except ValueError: shell_colors.print_red('Impossible to retrieve the part. Trying later.') return for i in range(total_chunks): chunk_size = sock.recv(5) # if not all the 5 expected bytes has been received while len(chunk_size) < 5: chunk_size += sock.recv(1) chunk_size = int(chunk_size) data = sock.recv(chunk_size) # if not all the expected bytes has been received while len(data) < chunk_size: data += sock.recv(1) self.f_obj.write(data) self.f_obj.close() try: packet = 'RPAD' + LocalData.session_id + self.file_md5 + str(self.part_num).zfill(8) sock = self.__connect(LocalData.get_tracker_ip4(), LocalData.get_tracker_ip6(), LocalData.get_tracker_port(), packet) except socket.error as e: shell_colors.print_red(f'\nImpossible to send data to {self.owner_ip4}|{self.owner_ip6} [{self.owner_port}]: {e}\n') return ack = sock.recv(4).decode() if ack != "APAD": shell_colors.print_red(f'\nInvalid command received: {ack}. Expected: APAD\n') sock.close() return try: num_parts_owned = int(sock.recv(8).decode()) except ValueError: shell_colors.print_red('Error while retrieving the parts owned from the tracker.') return binary_utils.update_owned_parts(self.part_num) LocalData.set_num_parts_owned(num_parts_owned) progress_bar.print_progress_bar(num_parts_owned, self.total_file_parts, prefix='Downloading:', suffix='Complete', length=50)
def serve(self, choice: str) -> None: """ Handle the peer packet :param choice: the choice to handle :return: None """ if choice == "SUPE": # SUPE[4B].Packet_Id[16B].IP_Peer[55B].Port_Peer[5B].TTL[2B] pktid = str(uuid.uuid4().hex[:16].upper()) ip = net_utils.get_local_ip_for_response() port = net_utils.get_asup_port() ttl = '04' packet = choice + pktid + ip + str(port).zfill(5) + ttl LocalData.set_sent_supe_packet(pktid) old_superfriends_len = len(LocalData.get_super_friends()) server = ServerThread(port, MenuTimedResponseHandler()) server.daemon = True server.start() spinner = SpinnerThread('Searching superpeers (ENTER to continue)', 'Research done! (ENTER to continue)') spinner.start() timer = Timer(20, lambda: (spinner.stop(), server.stop())) timer.start() self.__broadcast(packet) input() print('\033[1A', end='\r') if timer.is_alive(): spinner.stop() spinner.join() timer.cancel() timer.join() server.stop() server.join() else: spinner.join() timer.join() server.join() if len(LocalData.get_super_friends()) == old_superfriends_len: shell.print_yellow('\nNo new superpeer found.\n') elif choice == "ADFF": if not os.path.exists('shared'): shell.print_red('\nCannot find the shared folder.') return dir_file = list() if not os.scandir('shared'): shell.print_yellow('\nNo file in the shared folder.') return for count, dir_entry in enumerate(os.scandir('shared'), 1): dir_file.append((dir_entry.name, hasher.get_md5(dir_entry.path), dir_entry.stat().st_size)) print(f'\n{count}) {dir_entry.name}', end='') shell.print_yellow(f' {hasher.get_md5(dir_entry.path)}', end='') print(f' size: {dir_entry.stat().st_size}') while True: index = input('\nPlease select a file to share(pres q to exit): ') if index == 'q': print('\n') return try: index = int(index) - 1 except ValueError: shell.print_red(f'\nWrong index: number in range 1 - {count} expected.') continue if 0 <= index <= count - 1: chosen_file = dir_file.pop(index) filename = LocalData.get_shared_filename(chosen_file) filemd5 = LocalData.get_shared_filemd5(chosen_file) filedim = LocalData.get_shared_dim(chosen_file) if not LocalData.exist_shared_file(filename, filemd5, filedim): LocalData.add_shared_file(filename, filemd5, filedim) shell.print_green('\nThe file is now shared.') else: shell.print_yellow('\nThe file is already shared.') break else: shell.print_red(f'\nWrong index: number in range 1 - {count} expected.') continue elif choice == "DEFF": if not LocalData.get_shared_files(): shell.print_yellow('\nNo file in sharing.') return for count, file in enumerate(LocalData.get_shared_files(), 1): print(f'\n{count}) {LocalData.get_shared_filename(file)}', end='') shell.print_yellow(f' {LocalData.get_shared_filemd5(file)}', end='') print(f' size: {LocalData.get_shared_dim(file)}') while True: index = input('\nPlease select a file to delete from sharing(pres q to exit): ') if index == 'q': print('\n') return try: index = int(index) - 1 except ValueError: shell.print_red(f'\nWrong index: number in range 1 - {count} expected.') continue if 0 <= index <= count - 1: deleted_file = LocalData.get_shared_file_by_index(index) shell.print_blue(f'\n{LocalData.get_shared_filename(deleted_file)}', end='') shell.print_yellow(f' {LocalData.get_shared_filemd5(deleted_file)}', end='') shell.print_blue(' removed from sharing.') break else: shell.print_red(f'\nWrong index: number in range 1 - {count} expected.') continue elif choice == "QUER": while True: search = input('\nEnter the file name: ') if search != '*': search = '%' + search + '%' if not 0 < len(search) <= 20: shell.print_red('\nFile name must be between 1 and 20 chars long.\n') continue break # Read matching files from DB try: conn = database.get_connection(db_file) conn.row_factory = database.sqlite3.Row except database.Error as e: shell.print_red(f'\nError while getting connection from database: {e}') return try: total_db_file = file_repository.get_files_count_by_querystring(conn, search) if total_db_file == 0: shell.print_yellow('\nNo matching results from your peers.\n') else: shell.print_green('\nFiles from your logged peers:\n') file_rows = file_repository.get_files_by_querystring(conn, search) # print('\nFile from peers: ') for file_row in file_rows: file_md5 = file_row['file_md5'] file_name = file_row['file_name'] owner_rows = peer_repository.get_peers_by_file(conn, file_md5) for owner_row in owner_rows: owner_ip = owner_row['ip'] owner_port = int(owner_row['port']) ip4_peer, ip6_peer = net_utils.get_ip_pair(owner_ip) # stampa di debug # print(f'\n{LocalData.menu_peer_file_index(ip4_peer, ip6_peer, peer.port, file.file_md5, file.file_name)}', end='') # shell.print_yellow(f' {file.file_md5}', end='') # print(f' {file.file_name} from {ip4_peer|ip6_peer} on port {peer.port}') LocalData.add_menu_peer_file(ip4_peer, ip6_peer, owner_port, file_md5, file_name) index = LocalData.menu_peer_file_index(ip4_peer, ip6_peer, owner_port, file_md5, file_name) print(f'{index +1}] ', end='') print(f'{file_name} ', end='') shell.print_yellow(f'md5={file_md5} ', end='') print(f'({ip4_peer}|{ip6_peer} [{owner_port}])') conn.commit() conn.close() except database.Error as e: conn.rollback() conn.close() LocalData.clear_menu_peer_files() shell.print_red(f'\nError while retrieving data from database: {e}') # Send a search for a file on the superpeer network shell.print_green('\nFiles from the network:\n') pktid = str(uuid.uuid4().hex[:16].upper()) ip = net_utils.get_local_ip_for_response() port = net_utils.get_aque_port() ttl = '05' packet = choice + pktid + ip + str(port).zfill(5) + ttl + search.ljust(20) LocalData.set_sent_menu_quer_packet(pktid) server = ServerThread(port, MenuTimedResponseHandler()) server.daemon = True server.start() spinner = SpinnerThread('Searching files (ENTER to continue)', 'Research done! (ENTER to continue)') spinner.start() timer = Timer(20, lambda: (spinner.stop(), server.stop())) timer.start() self.__broadcast(packet) input() print('\033[1A', end='\r') if timer.is_alive(): spinner.stop() spinner.join() timer.cancel() timer.join() server.stop() server.join() else: spinner.join() timer.join() server.join() # Retrieving the list of database's files and superpeer network's files peer_files = LocalData.get_menu_peer_files() if len(peer_files) == total_db_file: shell.print_yellow('\nNo matching results from the superpeer network.\n') if len(peer_files) < 1: shell.print_yellow('\nNo file matching the keyword.\n') return while True: index = input('\nPlease select a file to download: ') if index == 'q': print('\n') LocalData.clear_menu_peer_files() return try: index = int(index) - 1 except ValueError: shell.print_red(f'\nWrong index: number in range 1 - {len(peer_files)} expected.') continue if 0 <= index <= len(peer_files): chosen_peer_file = LocalData.get_menu_peer_file_by_index(index) host_ip4 = LocalData.get_menu_file_owner_ip4(chosen_peer_file) host_ip6 = LocalData.get_menu_file_owner_ip6(chosen_peer_file) host_port = LocalData.get_menu_file_owner_port(chosen_peer_file) file_md5 = LocalData.get_menu_file_md5(chosen_peer_file) file_name = LocalData.get_menu_file_name(chosen_peer_file) # preparo packet per retr, faccio partire server in attesa download, invio packet e attendo packet = 'RETR' + file_md5 try: Downloader(host_ip4, host_ip6, host_port, packet, file_name).start() shell.print_green(f'\nDownload of {file_name} completed.\n') LocalData.clear_menu_peer_files() except OSError: shell.print_red(f'\nError while downloading {file_name}\n') break else: shell.print_red(f'\nWrong index: number in range 1 - {len(peer_files)} expected.') continue elif choice == "LISTSUPERPEERS": shell.print_green('\nList of known superpeers:') if not LocalData.get_super_friends(): shell.print_red('You do not know any superpeers.') else: for count, friend in enumerate(LocalData.get_super_friends(), 1): friend_ip4 = LocalData.get_super_friend_ip4(friend) friend_ip6 = LocalData.get_super_friend_ip6(friend) friend_port = LocalData.get_super_friend_port(friend) shell.print_blue(f'{count}] {friend_ip4} {friend_ip6} {str(friend_port)}') elif choice == "LISTPEERS": try: conn = database.get_connection(db_file) conn.row_factory = database.sqlite3.Row except database.Error as e: print(f'Error: {e}') print('The server has encountered an error while trying to serve the request.') return try: peer_list = peer_repository.find_all(conn) if not peer_list: shell.print_red('You do not know any peers.') conn.close() return else: shell.print_green('\nList of known peers:') for count, peer_row in enumerate(peer_list, 1): shell.print_blue(f'{count}]' + peer_row['ip'] + peer_row['port'] + '\n') except database.Error as e: conn.rollback() conn.close() print(f'Error: {e}') print('The server has encountered an error while trying to serve the request.') return elif choice == "LISTFILES": try: conn = database.get_connection(db_file) conn.row_factory = database.sqlite3.Row except database.Error as e: print(f'Error: {e}') print('The server has encountered an error while trying to serve the request.') return try: files = file_repository.find_all(conn) shell.print_green('\nYour shared files:') if not LocalData.get_shared_files(): shell.print_red('You do not have shared files.') for count, shared_file in enumerate(LocalData.get_shared_files(), 1): print(f'{count}] {LocalData.get_shared_filename(shared_file)} ', end='') shell.print_yellow(f'{LocalData.get_shared_filemd5(shared_file)}\n') shell.print_green('\nLogged peers files:') if not files: shell.print_red('You do not have logged peers files.') conn.close() return for count, file_row in enumerate(files, 1): print(f'{count}] {file_row["file_name"]} ', end='') shell.print_yellow(f'{file_row["file_md5"]}') except database.Error as e: conn.rollback() conn.close() print(f'Error: {e}') print('The server has encountered an error while trying to serve the request.') return else: pass
def startup(): while True: # verifico se ho superpeer e file in sharing nel json (ossia se il peer è crashato) if LocalData.superpeer_is_empty(): while True: shell.print_blue( '\nThis process will allow you to add your known peer.\n') superpeer = net_utils.prompt_friend_request() LocalData.set_superpeer(superpeer) # 1) Lancia una SUPE al nodo conosciuto pktid = str(uuid.uuid4().hex[:16].upper()) ip = net_utils.get_local_ip_for_response() port = str(net_utils.get_network_port()).zfill(5) ttl = '04' packet = 'SUPE' + pktid + ip + port + ttl super_ip4 = LocalData.get_superpeer_ip4() super_ip6 = LocalData.get_superpeer_ip6() super_port = LocalData.get_superpeer_port() LocalData.set_sent_packet(pktid) server = ServerThread( net_utils.get_network_port(), TimedResponseHandler.TimedResponseHandler()) server.start() spinner = SpinnerThread('Trying to login', '') spinner.start() try: net_utils.send_packet_and_close(super_ip4, super_ip6, super_port, packet) except socket.error as e: shell.print_red( f'There was an error in the login process: {e}') continue # 2) Attende ASUP per 20 sec timer = Timer(20, lambda: (server.stop(), spinner.stop())) timer.start() timer.join() spinner.join() # 3) Se non è possibile agganciarsi ad un super, devo far reinserire il peer all'utente if len(LocalData.get_superpeer_candidates()) == 0: shell.print_red( 'Cannot contact a superpeer from the peer you provide, please retry.' ) continue # 3) Se il peer aggiunto era veramente un superpeer, allora non lo cambio elif LocalData.get_superpeer( ) in LocalData.get_superpeer_candidates(): break # 3) Se invece era un superpeer falso, pesco un super a random dalla lista dei candidati else: index = random.randint( 0, len(LocalData.get_superpeer_candidates()) - 1) superpeer = LocalData.get_superpeer_candidate_by_index( index) LocalData.set_superpeer(superpeer) break # Lancio una LOGI al superpeer scelto ip = net_utils.get_local_ip_for_response() port = str(net_utils.get_network_port()).zfill(5) packet = "LOGI" + ip + port super_ip4 = LocalData.get_superpeer_ip4() super_ip6 = LocalData.get_superpeer_ip6() super_port = LocalData.get_superpeer_port() try: sock = net_utils.send_packet(super_ip4, super_ip6, super_port, packet) response = sock.recv(100).decode() if len(response) != 20: shell.print_red( f'There was an error in the login process: unexpected: {response}.\nPlease retry.' ) LocalData.clear_backup_data() continue session_id = response[4:20] if session_id == '0' * 16: shell.print_red( f'There was an error in the login process: unexpected session_id: {session_id}.\nPlease retry.' ) LocalData.clear_backup_data() continue LocalData.session_id = response[4:20] break except (socket.error, AttributeError): shell.print_yellow( f'Unable to contact {super_ip4}|{super_ip6} [{super_port}]') # pulisco il file json da file in sharing e superpeer vecchio LocalData.clear_backup_data() if sock is not None: sock.close() continue shell.print_green( f'Successfully logged to the superpeer: {super_ip4}|{super_ip6} [{super_port}]\n' ) log = Logger.Logger('peer/peer.log') server = ServerThread(net_utils.get_network_port(), NetworkHandler.NetworkHandler(log)) server.daemon = True server.start() Menu(MenuHandler.MenuHandler()).show()
#!/usr/bin/env python from service.ServerThread import ServerThread from service.Menu import Menu from handler.NeighboursHandler import NeighboursHandler from handler.MenuHandler import MenuHandler from utils import shell_colors as shell import os from utils import net_utils, Logger, hasher from service.AppData import AppData if __name__ == '__main__': shell.print_red(' ____ ___ ___ _ ', end='') shell.print_yellow(' _ _ ') shell.print_red('| _ \ / _ \ / _ \| |_ ', end='') shell.print_yellow('___| | | __ _ ') shell.print_red('| |_) | | | | | | | __/', end='') shell.print_yellow(' _ \ | |/ _` |') shell.print_red('| _ <| |_| | |_| | |', end='') shell.print_yellow('| __/ | | (_| |') shell.print_red('|_| \__\\___/ \___/ \__', end='') shell.print_yellow('\___|_|_|\__,_|') if not os.path.exists('shared'): os.mkdir('shared') for dir_entry in os.scandir('shared'): AppData.add_shared_file(dir_entry.name, hasher.get_md5(dir_entry.path), dir_entry.stat().st_size)
def serve(self, sd: socket.socket) -> None: """ Handle a network packet :param sd: the socket descriptor used for read the packet :return: None """ try: packet = sd.recv(200).decode() except socket.error as e: self.log.write_red(f'Unable to read the packet from the socket: {e}') sd.close() return # log the packet received socket_ip_sender = sd.getpeername()[0] if ipaddress.IPv6Address(socket_ip_sender).ipv4_mapped is None: socket_ip_sender = ipaddress.IPv6Address(socket_ip_sender).compressed else: socket_ip_sender = ipaddress.IPv6Address(socket_ip_sender).ipv4_mapped.compressed socket_port_sender = sd.getpeername()[1] self.log.write_green(f'{socket_ip_sender} [{socket_port_sender}] -> ', end='') self.log.write(f'{packet}') command = packet[:4] if command == "RETP": if len(packet) != 44: self.log.write_red(f'Invalid packet received: {packet}') return file_md5 = packet[4:36] try: num_part = int(packet[36:44]) except ValueError: shell.print_red(f'Invalid packet received: Part-Num must be an integer -> {num_part}\n') return file_name = LocalData.get_shared_file_name_from_md5(file_md5) if file_name is None: error_packet = 'Sorry, the requested file is not available anymore.' self.send_packet(sd, socket_ip_sender, socket_port_sender, error_packet) sd.close() return try: f_obj = open('shared/' + file_name, 'rb') except OSError as e: self.log.write_red(f'Cannot open the file to upload: {e}') error_packet = 'Sorry, the peer encountered a problem while uploading the file.' self.send_packet(sd, socket_ip_sender, socket_port_sender, error_packet) sd.close() return try: Uploader(sd, f_obj, num_part, self.log).start() self.log.write_blue(f'Sent {socket_ip_sender} [{socket_port_sender}] -> ', end='') self.log.write(f'{file_name}') sd.close() except OSError as e: self.log.write_red(f'Error while sending the file: {e}') sd.close() return else: sd.close() self.log.write_red(f'Invalid packet received from {socket_ip_sender} [{socket_port_sender}]: ', end='') self.log.write(f'{packet}') return
def serve(self, sd: socket.socket) -> None: """ Handle the peer request :param sd: the socket descriptor used for read the request :return None """ try: command = sd.recv(4).decode() except OSError as e: shell_colors.print_red( f'\nUnable to read the command from the socket: {e}\n') sd.close() return if command == "AQUE": try: response = sd.recv(300).decode() except socket.error as e: shell_colors.print_red( f'\nUnable to read the {command} response from the socket: {e}\n' ) sd.close() return sd.close() if len(response) != 208: print( f"\nInvalid response: {command} -> {response}. Expected: AQUE<pkt_id><ip_peer><port_peer><fileMD5><filename>\n" ) return pktid = response[0:16] ip_peer = response[16:71] ip4_peer, ip6_peer = net_utils.get_ip_pair(ip_peer) port_peer = int(response[71:76]) filemd5 = response[76:108] filename = response[108:208].lower().lstrip().rstrip() if pktid != AppData.get_sent_packet(): sd.close() return if not AppData.exist_peer_files(ip4_peer, ip6_peer, port_peer, filemd5, filename): AppData.add_peer_files(ip4_peer, ip6_peer, port_peer, filemd5, filename) index = AppData.peer_file_index(ip4_peer, ip6_peer, port_peer, filemd5, filename) print(f'{index +1}] ', end='') shell_colors.print_blue(f'{filename} ', end='') shell_colors.print_yellow(f'md5={filemd5} ', end='') print(f'({ip4_peer}|{ip6_peer} [{port_peer}])') elif command == "ANEA": try: response = sd.recv(300).decode() except socket.error as e: shell_colors.print_red( f'\nUnable to read the {command} response from the socket: {e}\n' ) sd.close() return sd.close() if len(response) != 76: shell_colors.print_red( f"\nInvalid response: : {command} -> {response}. Expected: ANEA<pkt_id><ip_peer><port_peer>" ) return pktid = response[0:16] ip_peer = response[16:71] ip4_peer, ip6_peer = net_utils.get_ip_pair(ip_peer) port_peer = int(response[71:76]) if pktid != AppData.get_sent_packet(): return if len(AppData.get_neighbours()) >= 5: return if not AppData.is_neighbour(ip4_peer, ip6_peer, port_peer): AppData.add_neighbour(ip4_peer, ip6_peer, port_peer) shell_colors.print_green('New neighbour found: ', end='') print(f'{ip4_peer}|{ip6_peer} [{port_peer}]') else: wrong_response = sd.recv(300).decode() sd.close() shell_colors.print_red( f"\nInvalid response: {command} -> {wrong_response}\n") return
def serve(choice: str) -> None: """ Handle the peer packet :param choice: the choice to handle :return: None """ tracker_ip4 = LocalData.get_tracker_ip4() tracker_ip6 = LocalData.get_tracker_ip6() tracker_port = LocalData.get_tracker_port() ssid = LocalData.get_session_id() log = Logger.Logger('peer/peer.log') if choice == "LOOK": # Search a file while True: search = input('\nInsert file\'s name (q to cancel): ') if search == "q": print('\n') return if not 0 < len(search) <= 20: shell.print_red( '\nQuery string must be a valid value (1 - 20 chars).') continue break packet = choice + ssid + search.ljust(20) sd = None try: sd = net_utils.send_packet(tracker_ip4, tracker_ip6, tracker_port, packet) except net_utils.socket.error as e: shell.print_red( f'\nError while sending the request to the tracker: {tracker_ip4}|{tracker_ip6} [{tracker_port}].' ) shell.print_red(f'Error: {e}') if sd is not None: sd.close() return # Receiving the response list try: command = sd.recv(4).decode() except net_utils.socket.error as e: shell.print_red( f'Unable to read the command from the socket: {e}\n') sd.close() return if command != 'ALOO': shell.print_red( f'\nReceived a packet with a wrong command ({command}).') return try: num_files = int(sd.recv(3).decode()) except net_utils.socket.error as e: shell.print_red( f'Unable to read the command from the socket: {e}\n') sd.close() return if num_files == 0: shell.print_yellow(f'{search} not found.\n') sd.close() return downloadables = list() for i in range(num_files): try: file_md5 = sd.recv(32).decode() file_name = sd.recv(100).decode().lstrip().rstrip() len_file = int(sd.recv(10)) len_part = int(sd.recv(6)) except net_utils.socket.error as e: shell.print_red( f'\nError while receiving the response from the tracker: {e}\n' ) continue except ValueError: shell.print_red(f'\nInvalid packet from tracker: {e}\n') continue downloadables.append((file_md5, file_name, len_file, len_part)) sd.close() if not downloadables: shell.print_red( f'\nSomething went wrong while retrieving {search}\n') return shell.print_green(f'\nFiles found:') for count, downloadable in enumerate(downloadables, 1): print( f'{count}] {LocalData.get_downloadable_file_name(downloadable)} | ', end='') shell.print_yellow( f'{LocalData.get_downloadable_file_md5(downloadable)}') # Download choice while True: file_index = input( '\nChoose a file to download (q to cancel): ') if file_index == "q": return try: file_index = int(file_index) - 1 except ValueError: shell.print_red( f'\nWrong index: number in range 1 - {len(downloadables)} expected\n' ) continue if not 0 <= file_index <= len(downloadables) - 1: shell.print_red( f'\nWrong index: number in range 1 - {len(downloadables)} expected\n' ) continue else: choosed_file_md5 = LocalData.get_downloadable_file_md5( downloadables[file_index]) choosed_file_name = LocalData.get_downloadable_file_name( downloadables[file_index]) if LocalData.is_shared_file(choosed_file_md5, choosed_file_name): shell.print_green( f'\nYou already have downloaded {choosed_file_name}.' ) continue choosed_file_lenght = LocalData.get_downloadable_file_length( downloadables[file_index]) choosed_file_part_lenght = LocalData.get_downloadable_file_part_length( downloadables[file_index]) break choosed_file_parts = int( math.ceil(choosed_file_lenght / choosed_file_part_lenght)) choosed_file_part_list_length = int( math.ceil(choosed_file_parts / 8)) # Download phase LocalData.create_downloading_part_list( choosed_file_part_list_length) # 1) Initiating the thread responsible for update of the part_list_table in background update_event = Event() updater_thread = UpdaterThread.UpdaterThread( choosed_file_md5, choosed_file_part_list_length, update_event, log) updater_thread.start() # 2) Create the new file f_obj = open(f'shared/{choosed_file_name}', 'wb') f_obj.close() LocalData.set_num_parts_owned(0) progress_bar.print_progress_bar(0, choosed_file_parts, prefix='Downloading:', suffix='Complete', length=50) # Adding the file to the shared file list LocalData.add_shared_file(choosed_file_md5, choosed_file_name) # Until all the parts hasn't been downloaded while choosed_file_parts != LocalData.get_num_parts_owned(): update_event.wait(70) update_event.clear() # We can use a with statement to ensure threads are cleaned up promptly with ThreadPoolExecutor(max_workers=10) as executor: # Get the file parts we don't have yet downloadable_parts = LocalData.get_downloadable_parts() for part_num in downloadable_parts: try: # Start the load operations executor.submit(download_task.run, choosed_file_md5, choosed_file_name, part_num, choosed_file_part_lenght, choosed_file_parts, log) except OSError as e: log.write_red( f'\nError while downloading {choosed_file_name}: {e}' ) executor.shutdown() updater_thread.stop() shell.print_green('\nDownload completed.') elif choice == "ADDR": # check if shared directory exist if not os.path.exists('shared'): shell.print_red('\nCannot find the shared folder.') return # check for file available in shared directory if not os.listdir('shared'): shell.print_yellow( 'No file available for sharing.\nAdd files to shared dir to get started.\n' ) return temp_files = [] shell.print_blue('\nFiles available for sharing:') for count, file in enumerate(os.scandir('shared'), 1): # print scandir results file_md5 = hasher.get_md5(f'shared/{file.name}') print(f'{count}] {file.name} | {file_md5}') temp_files.append((file_md5, file.name)) while True: file_index = input('\nChoose a file to share (q to cancel): ') if file_index == "q": return try: file_index = int(file_index) - 1 except ValueError: shell.print_red( f'\nWrong index: number in range 1 - {len(temp_files)} expected\n' ) continue if 0 <= file_index <= len(temp_files) - 1: file_name = LocalData.get_shared_file_name( temp_files[file_index]) file_md5 = LocalData.get_shared_file_md5( temp_files[file_index]) # check if file is already in sharing if not LocalData.is_shared_file(file_md5, file_name): try: f_obj = open('shared/' + file_name, 'rb') except OSError as e: shell.print_red( f'Cannot open the file to upload: {e}') return try: filesize = str(os.fstat(f_obj.fileno()).st_size) except OSError as e: shell.print_red(f'Something went wrong: {e}') return except ValueError as e: shell.print_red( f'\nUnable to convert filesize in string: {e}\n' ) return part_size = str(net_utils.get_part_size()) # build packet and sent to tracker packet = choice + ssid + filesize.zfill( 10) + part_size.zfill(6) + file_name.ljust( 100) + file_md5 sd = None try: sd = net_utils.send_packet(tracker_ip4, tracker_ip6, tracker_port, packet) except net_utils.socket.error as e: shell.print_red( f'\nError while sending the request to the tracker: {tracker_ip4}|{tracker_ip6} [{tracker_port}].' ) shell.print_red(f'Error: {e}') if sd is not None: sd.close() return try: response = sd.recv(200).decode() except net_utils.socket.error as e: shell.print_red( f'Unable to read the response from the socket: {e}\n' ) sd.close() return sd.close() if len(response) != 12: shell.print_red( f"Invalid response: : {response}. Expected: AADR<num_part>" ) return command = response[0:4] if command != 'AADR': shell.print_red( f'\nReceived a packet with a wrong command ({command}).' ) return num_part = response[4:12] # add file to shared_files LocalData.add_shared_file(file_md5, file_name) shell.print_blue( f'\nNew shared file added {file_name} | {file_md5}', end='') shell.print_green(f' #parts: {num_part}') break else: # if not in sharing shell.print_yellow( f'\n{file_name} | {file_md5} already in sharing.\n' ) break else: shell.print_red( f'\nWrong index: number in range 1 - {len(temp_files)} expected\n' ) continue elif choice == "LOGO": packet = choice + ssid sd = None try: sd = net_utils.send_packet(tracker_ip4, tracker_ip6, tracker_port, packet) except net_utils.socket.error as e: shell.print_red( f'\nError while sending the request to the tracker: {tracker_ip4}|{tracker_ip6} [{tracker_port}].' ) shell.print_red(f'{e}') if sd is not None: sd.close() return try: response = sd.recv(50).decode() except net_utils.socket.error as e: shell.print_red( f'Unable to read the response from the socket: {e}\n') sd.close() return sd.close() if len(response) != 14: shell.print_red( f"Invalid response form the socket: {response}.") return command = response[0:4] if command == "ALOG": part_own = int(response[4:14]) shell.print_green('\nSuccessfully logged out') shell.print_blue( f'{part_own} parts has been removed from sharing.\n') LocalData.clear_tracker() elif command == "NLOG": part_down = int(response[4:14]) shell.print_yellow( f'\nUnable to logout:\nYou have shared only {part_down} parts with other peer.\n' ) else: shell.print_red( f'\nReceived a packet with a wrong command from the socket: {command} -> {response}' ) elif choice == 'SHAR': files = LocalData.get_shared_files() if not files: shell.print_red('\nYou currently have no files in sharing.') print('\nFiles actually in sharing:') for count, file in enumerate(files, 1): print(f'{count}] {LocalData.get_downloadable_file_name(file)}') elif choice == 'TRAC': shell.print_green( f'\nYour tracker is: {LocalData.get_tracker_ip4()}|{LocalData.get_tracker_ip6()} [{LocalData.get_tracker_port()}]' ) else: shell.print_yellow( f'\nInvalid input code: "{choice}".\nPlease retry.\n') return
def serve(choice: str) -> None: """ Handle the peer packet :param choice: the choice to handle :return: None """ sp_ip4 = LocalData.get_superpeer_ip4() sp_ip6 = LocalData.get_superpeer_ip6() sp_port = LocalData.get_superpeer_port() p_ssid = LocalData.session_id if choice == "ADFF": # check se shared dir esiste if not os.path.exists('shared'): shell.print_red('\nCannot find the shared folder.') return # se non ci sono file nella shared dir avvisa ed esce if not os.scandir('shared'): shell.print_yellow( 'No file available for sharing. Add files to shared dir to get started.\n' ) return temp_files = [] shell.print_blue('\nFiles available for sharing:') for count, file in enumerate(os.scandir('shared'), 1): # stampa i risultati della scandir file_md5 = hasher.get_md5(f'shared/{file.name}') print(f'{count}] {file.name} | {file_md5}') temp_files.append((file_md5, file.name)) while True: index = input('Choose a file to share (q to cancel): ') if index == "q": print('\n') return try: index = int(index) - 1 except ValueError: shell.print_red( f'\nWrong index: number in range 1 - {len(temp_files)} expected\n' ) continue if 0 <= index <= len(temp_files) - 1: file_name = LocalData.get_shared_file_name( temp_files[index]) file_md5 = LocalData.get_shared_file_md5(temp_files[index]) # controlla se il file è già in condivisione if not LocalData.is_shared_file(temp_files[index]): # crea il pacchetto e lo invia al super peer packet = choice + p_ssid + file_md5 + file_name.ljust( 100) try: net_utils.send_packet_and_close( sp_ip4, sp_ip6, sp_port, packet) shell.print_green( f'Packet sent to supernode: {sp_ip4}|{sp_ip6} [{sp_port}]' ) except net_utils.socket.error: shell.print_red( f'\nError while sending packet to supernode: {sp_ip4}|{sp_ip6} [{sp_port}]\n' ) # aggiunge il file alla struttura dati ed esce LocalData.add_shared_file(file_md5, file_name) shell.print_blue( f'\nNew shared file added {file_name} | {file_md5}' ) break else: # il file è già in condivisione shell.print_yellow( f'\n{file_name} | {file_md5} already in sharing.\n' ) break else: shell.print_red( f'\nWrong index: number in range 1 - {len(temp_files)} expected\n' ) continue elif choice == "DEFF": # recupera i files in sharing files = LocalData.get_shared_files() # check se ci sono file in sharing if not files: shell.print_yellow( 'No files currently in sharing. Add files choosing the command from the menu.\n' ) return # scelta del file da rimuovere shell.print_blue('\nFile currently in sharing:') for count, file in enumerate(files, 1): print( f'{count}] {LocalData.get_shared_file_name(file)} | {LocalData.get_shared_file_md5(file)}' ) while True: index = input( 'Choose a file to remove from sharing (q to cancel): ') if index == "q": print('\n') return try: index = int(index) - 1 except ValueError: shell.print_red( f'\nWrong index: number in range 1 - {len(files)} expected\n' ) continue if 0 <= index <= len(files) - 1: # recupera il file dalla DS file = LocalData.get_shared_file(index) # crea ed invia il pacchetto al supernode packet = choice + p_ssid + LocalData.get_shared_file_md5( file) try: net_utils.send_packet_and_close( sp_ip4, sp_ip6, sp_port, packet) except net_utils.socket.error: shell.print_red( f'\nError while sending packet to supernode: {sp_ip4}|{sp_ip6} [{sp_port}]\n' ) # rimuove il file dalla DS LocalData.remove_shared_file(file) shell.print_green( f'\n{LocalData.get_shared_file_name(file)} | {LocalData.get_shared_file_md5(file)} removed successfully.' ) break else: shell.print_red( f'\nWrong index: number in range 1 - {len(files)} expected\n' ) continue elif choice == "FIND": while True: search = input('\nInsert file\'s name (q to cancel): ') if search == "q": print('\n') return if not 0 < len(search) <= 20: shell.print_red( '\nQuery string must be a valid value (1 - 20 chars).') continue break packet = choice + p_ssid + search.ljust(20) try: socket = net_utils.send_packet(sp_ip4, sp_ip6, sp_port, packet) except (net_utils.socket.error, AttributeError): shell.print_red( f'\nError while sending the request to the superpeer: {sp_ip4}|{sp_ip6} [{sp_port}].\n' ) if socket is not None: socket.close() return spinner = SpinnerThread('Searching files', 'Research done!') spinner.start() try: socket.settimeout(25) command = socket.recv(4).decode() spinner.stop() spinner.join() print('\033[1A', end='\r') if command != "AFIN": shell.print_red( f'\nReceived a packet with a wrong command ({command}).\n' ) socket.close() return except net_utils.socket.error: shell.print_red( f'\nError while receiving the response from the superpeer: {sp_ip4}|{sp_ip6} [{sp_port}].\n' ) return except ValueError: shell.print_red( f'\nInvalid packet from superpeer: {sp_ip4}|{sp_ip6} [{sp_port}].\n' ) return # ('file_name', 'file_md5', owners[]) downloadables = list() try: num_downloadables = int(socket.recv(3)) # check se ci sono file da scaricare if num_downloadables == 0: shell.print_yellow(f'{search} not found.\n') socket.close() return except net_utils.socket.error: shell.print_red( f'\nError while receiving the response from the superpeer: {sp_ip4}|{sp_ip6} [{sp_port}].\n' ) return for i in range(num_downloadables): # ('owner_ip4', 'owner_ip6', 'owner_port') owners = list() try: file_md5 = socket.recv(32).decode() file_name = socket.recv(100).decode().lstrip().rstrip() num_copies = int(socket.recv(3)) except net_utils.socket.error: shell.print_red( f'\nError while receiving the response from the superpeer: {sp_ip4}|{sp_ip6} [{sp_port}].\n' ) continue except ValueError: shell.print_red( f'\nInvalid packet from superpeer: {sp_ip4}|{sp_ip6} [{sp_port}].\n' ) continue for j in range(num_copies): try: (owner_ip4, owner_ip6) = net_utils.get_ip_pair( socket.recv(55).decode()) owner_port = int(socket.recv(5)) owners.append((owner_ip4, owner_ip6, owner_port)) except net_utils.socket.error: shell.print_red( f'\nError while receiving the response from the superpeer: {sp_ip4}|{sp_ip6} [{sp_port}].\n' ) continue except ValueError: shell.print_red( f'\nInvalid packet from superpeer: {sp_ip4}|{sp_ip6} [{sp_port}].\n' ) continue if owners: # prepara una lista con tutti i file che possono essere scaricati downloadables.append((file_name, file_md5, owners)) if not downloadables: shell.print_red( f'\nSomething went wrong while retrieving {search}\n') socket.close() return shell.print_green(f'\nFiles found:') for count, downloadable in enumerate(downloadables, 1): print(f'{count}] {downloadable[0]} | {downloadable[1]}') while True: choosed_file_index = input( 'Choose a file to download (q to cancel): ') if choosed_file_index == "q": print('\n') socket.close() return try: choosed_file_index = int(choosed_file_index) - 1 except ValueError: shell.print_red( f'\nWrong index: number in range 1 - {len(downloadables)} expected\n' ) continue if not 0 <= choosed_file_index <= len(downloadables) - 1: shell.print_red( f'\nWrong index: number in range 1 - {len(downloadables)} expected\n' ) continue else: choosed_file_name = downloadables[choosed_file_index][0] choosed_file_md5 = downloadables[choosed_file_index][1] choosed_file_owners = downloadables[choosed_file_index][2] shell.print_green( f'\nAvailable sources for {choosed_file_name}:') for count, choosed_file_owner in enumerate( choosed_file_owners, 1): print( f'{count}] {choosed_file_owner[0]}|{choosed_file_owner[1]} [{choosed_file_owner[2]}]' ) while True: choosed_owner_index = input( f'Choose a source for {choosed_file_name} (q to cancel): ' ) if choosed_owner_index == "q": print('\n') return try: choosed_owner_index = int(choosed_owner_index) - 1 except ValueError: shell.print_red( f'\nWrong index: number in range 1 - {len(choosed_file_owners)} expected\n' ) continue if 0 <= choosed_owner_index <= len( choosed_file_owners) - 1: packet = "RETR" + choosed_file_md5 try: Downloader( choosed_file_owners[choosed_owner_index] [0], choosed_file_owners[choosed_owner_index] [1], choosed_file_owners[choosed_owner_index] [2], packet, choosed_file_name).start() shell.print_green( f'\n{choosed_file_name} downloaded successfully.\n' ) except OSError: shell.print_red( f'\nError while downloading {choosed_file_name}.\n' ) break else: shell.print_red( f'\nWrong index: number in range 1 - {len(choosed_file_owners)} expected\n' ) continue break elif choice == "SHOWSUPER": print( f'\nYour current superpeer is: {sp_ip4}|{sp_ip6} [{sp_port}]\n' ) elif choice == "LOGO": packet = choice + p_ssid sock = net_utils.send_packet(sp_ip4, sp_ip6, sp_port, packet) try: command = sock.recv(4).decode() if command != "ALGO": shell.print_red( f'\nWrong response code "{command}": "{choice}" expected.\n' ) num_files = int(sock.recv(3)) shell.print_blue( f'\n{num_files} files has been removed from sharing.\n') except (net_utils.socket.error, AttributeError): shell.print_red( f'\nError while receiving the response for "{choice}".\n') LocalData.clear_backup_data() else: pass