def setUp(self): config.data_dir = os.path.dirname(os.path.realpath(__file__)) config.filename = os.path.join(config.data_dir, "temp_config") config.load_config() self.transfers = Transfers(Mock(), config, deque(), Mock()) self.transfers.privileged_users = {"puser1", "puser2"}
def setUp(self): config.data_dir = os.path.dirname(os.path.realpath(__file__)) config.filename = os.path.join(config.data_dir, "temp_config") config.load_config() self.transfers = Transfers(Mock(), config, deque(), {}, Mock()) self.transfers.server_login()
def setUp(self): config.data_dir = os.path.dirname(os.path.realpath(__file__)) config.filename = os.path.join(config.data_dir, "temp_config") config.load_config() self.transfers = Transfers(MagicMock(), config, deque(), Mock()) self.transfers.init_transfers() self.transfers.server_login() self.transfers.allow_saving_transfers = False
class TransfersTest(unittest.TestCase): def setUp(self): config.data_dir = os.path.dirname(os.path.realpath(__file__)) config.filename = os.path.join(config.data_dir, "temp_config") config.load_config() self.transfers = Transfers(Mock(), config, deque(), {}, Mock()) self.transfers.server_login() def test_load_downloads(self): """ Test loading a downloads.json file """ self.assertEqual(len(self.transfers.queue), 2) self.assertEqual(len(self.transfers.downloads), 13) transfer = self.transfers.downloads[0] self.assertEqual(transfer.user, "user13") self.assertEqual(transfer.filename, "Downloaded\\Song13.mp3") self.assertEqual(transfer.status, "Getting status") self.assertIsNone(transfer.size) self.assertIsNone(transfer.currentbytes) self.assertIsNone(transfer.bitrate) self.assertIsNone(transfer.length) transfer = self.transfers.downloads[12] self.assertEqual(transfer.user, "user1") self.assertEqual(transfer.filename, "Downloaded\\Song1.mp3") self.assertEqual(transfer.status, "Aborted") self.assertEqual(transfer.size, 10093741) self.assertEqual(transfer.currentbytes, 5000) self.assertEqual(transfer.bitrate, "320") self.assertEqual(transfer.length, "4:12") def test_save_downloads(self): """ Verify that the order of the download list at the end of the sesson is identical to the one we loaded. Ignore transfer 13, since its missing properties will be added at the end of the session. """ self.transfers.abort_transfers() old_transfers = self.transfers.load_current_transfers_format( self.transfers.downloads_file_name)[:12] saved_transfers = self.transfers.get_downloads()[:12] self.assertEqual(old_transfers, saved_transfers) def test_load_uploads(self): """ Test loading a uploads.json file """ # Only finished uploads are loaded, other types should never be stored self.assertEqual(len(self.transfers.uploads), 3) self.assertEqual(len(self.transfers.queue), 2) transfer = self.transfers.uploads[0] self.assertEqual(transfer.user, "user5") self.assertEqual(transfer.filename, "Junk\\Song5.mp3") self.assertEqual(transfer.status, "Finished") self.assertEqual(transfer.size, 11733776) self.assertEqual(transfer.currentbytes, 11733776) self.assertEqual(transfer.bitrate, "319") self.assertEqual(transfer.length, "4:53") transfer = self.transfers.uploads[2] self.assertEqual(transfer.user, "user3") self.assertEqual(transfer.filename, "Junk\\Song3.flac") self.assertEqual(transfer.status, "Finished") self.assertEqual(transfer.size, 27231044) self.assertEqual(transfer.currentbytes, 27231044) self.assertEqual(transfer.bitrate, "792") self.assertEqual(transfer.length, "4:28") def test_queue_download(self): """ Verify that new downloads are prepended to the list """ self.transfers.get_file("newuser", "Hello\\Path\\File.mp3", "") transfer = self.transfers.downloads[0] self.assertEqual(transfer.user, "newuser") self.assertEqual(transfer.filename, "Hello\\Path\\File.mp3") self.assertEqual(transfer.path, "") def test_push_upload(self): """ Verify that new uploads are prepended to the list """ self.transfers.push_file("newuser2", "Hello\\Upload\\File.mp3", "/home/test") self.transfers.push_file("newuser99", "Home\\None.mp3", "") transfer = self.transfers.uploads[1] self.assertEqual(transfer.user, "newuser2") self.assertEqual(transfer.filename, "Hello\\Upload\\File.mp3") self.assertEqual(transfer.path, "/home/test")
class GetUploadCandidateTest(unittest.TestCase): def setUp(self): config.data_dir = os.path.dirname(os.path.realpath(__file__)) config.filename = os.path.join(config.data_dir, "temp_config") config.load_config() self.transfers = Transfers(Mock(), config, deque(), Mock()) self.transfers.privileged_users = {"puser1", "puser2"} def add_transfers(self, users, status): transfer_list = [] for user in users: filename = "%s/%i" % (user, len(self.transfers.uploads)) transfer = Transfer(user=user, path=filename, status=status) transfer_list.append(transfer) self.transfers.append_upload(user, filename, transfer) return transfer_list def set_finished(self, transfer): transfer.status = "Finished" self.transfers.update_user_counter(transfer.user) self.transfers.uploads.remove(transfer) def consume_transfers(self, queued, in_progress, clear_first=False): """Call self.transfers.get_upload_candidate until no uploads are left. Transfers should be added to self.transfers in the desired starting states already. One in progress upload will be removed each time get_upload_candidate is called. `queued` and `in_progress` should contain the transfers in those states. `clear_first` indicates whether the upload candidate should be generated after the in progress upload is marked finished or before. All candidates received are returned in a list. """ candidates = [] none_count = 0 # prevent infinite loop in case of bug or bad test setup num_allowed_nones = 2 while len( self.transfers.uploads) > 0 and none_count < num_allowed_nones: # "finish" one in progress transfer, if any if clear_first and in_progress: self.set_finished(in_progress.pop(0)) candidate = self.transfers.get_upload_candidate() if not clear_first and in_progress: self.set_finished(in_progress.pop(0)) if not candidate: none_count += 1 print("none_count: %i" % none_count) if queued: print( "Found no transfer candidates out of %i queued uploads" % len(queued)) candidates.append(None) continue none_count = 0 print("Transfer candidate: %s" % candidate.user) candidates.append(candidate) queued.remove(candidate) in_progress.append(candidate) candidate.status = "Getting status" return candidates def base_test(self, queued, in_progress, expected, round_robin=False, clear_first=False): config.sections["transfers"]["fifoqueue"] = not round_robin queued_transfers = self.add_transfers(queued, status="Queued") in_progress_transfers = self.add_transfers(in_progress, status="Getting status") candidates = self.consume_transfers(queued_transfers, in_progress_transfers, clear_first=clear_first) users = [ transfer.user if transfer else None for transfer in candidates ] # `expected` should contain `None` in cases where there aren't # expected to be any queued users without existing in progress uploads self.assertEqual(users, expected) def test_round_robin_basic(self): self.base_test( queued=["user1", "user1", "user2", "user2", "user3", "user3"], in_progress=[], expected=[ "user1", "user2", "user3", "user1", "user2", "user3", None ], round_robin=True) def test_round_robin_no_contention(self): self.base_test( queued=["user1", "user1", "user2", "user2", "user3", "user3"], in_progress=[], expected=[ "user1", "user2", "user3", "user1", "user2", "user3", None ], round_robin=True, clear_first=True) def test_round_robin_one_user(self): self.base_test(queued=["user1", "user1"], in_progress=[], expected=["user1", None, "user1", None], round_robin=True) def test_round_robin_returning_user(self): self.base_test(queued=[ "user1", "user1", "user2", "user2", "user2", "user3", "user3", "user3", "user1", "user1" ], in_progress=[], expected=[ "user1", "user2", "user3", "user1", "user2", "user3", "user1", "user2", "user3", "user1", None ], round_robin=True) def test_round_robin_in_progress(self): self.base_test(queued=["user1", "user1", "user2", "user2"], in_progress=["user1"], expected=["user2", "user1", "user2", "user1", None], round_robin=True) def test_round_robin_privileged(self): self.base_test( queued=["user1", "user2", "puser1", "puser1", "puser2"], in_progress=[], expected=["puser1", "puser2", "puser1", "user1", "user2", None], round_robin=True) def test_fifo_basic(self): self.base_test( queued=["user1", "user1", "user2", "user2", "user3", "user3"], in_progress=[], expected=[ "user1", "user2", "user1", "user2", "user3", None, "user3", None ]) def test_fifo_robin_no_contention(self): self.base_test( queued=["user1", "user1", "user2", "user2", "user3", "user3"], in_progress=[], expected=[ "user1", "user1", "user2", "user2", "user3", "user3", None ], clear_first=True) def test_fifo_one_user(self): self.base_test(queued=["user1", "user1"], in_progress=[], expected=["user1", None, "user1", None]) def test_fifo_returning_user(self): self.base_test(queued=[ "user1", "user1", "user2", "user2", "user2", "user3", "user3", "user3", "user1", "user1" ], in_progress=[], expected=[ "user1", "user2", "user1", "user2", "user3", "user2", "user3", "user1", "user3", "user1", None ]) def test_fifo_in_progress(self): self.base_test(queued=["user1", "user1", "user2", "user2"], in_progress=["user1"], expected=["user2", "user1", "user2", "user1", None]) def test_fifo_privileged(self): self.base_test( queued=["user1", "user2", "puser1", "puser1", "puser2"], in_progress=[], expected=["puser1", "puser2", "puser1", "user1", "user2", None])
class NicotineCore: """ NicotineCore contains handlers for various messages from the networking thread. This class links the networking thread and user interface. """ def __init__(self, bindip, port): self.ui_callback = None self.network_callback = None self.network_filter = None self.statistics = None self.shares = None self.search = None self.transfers = None self.interests = None self.userbrowse = None self.userinfo = None self.userlist = None self.privatechats = None self.chatrooms = None self.pluginhandler = None self.now_playing = None self.protothread = None self.upnp = None self.geoip = None self.notifications = None # Handle Ctrl+C and "kill" exit gracefully for signal_type in (signal.SIGINT, signal.SIGTERM): signal.signal(signal_type, self.quit) self.bindip = bindip self.port = port self.shutdown = False self.away = False self.logged_in = False self.login_username = None # Only present while logged in self.user_ip_address = None self.privileges_left = None self.ban_message = "You are banned from downloading my shared files. Ban message: \"%s\"" self.queue = deque() self.user_statuses = {} self.watched_users = set() self.ip_requested = set() self.requested_info_times = {} self.requested_share_times = {} # Callback handlers for messages self.events = { slskmessages.ServerDisconnect: self.server_disconnect, slskmessages.Login: self.login, slskmessages.ChangePassword: self.change_password, slskmessages.MessageUser: self.message_user, slskmessages.PMessageUser: self.p_message_user, slskmessages.ExactFileSearch: self.dummy_message, slskmessages.RoomAdded: self.dummy_message, slskmessages.RoomRemoved: self.dummy_message, slskmessages.UserJoinedRoom: self.user_joined_room, slskmessages.SayChatroom: self.say_chat_room, slskmessages.JoinRoom: self.join_room, slskmessages.UserLeftRoom: self.user_left_room, slskmessages.CantCreateRoom: self.dummy_message, slskmessages.QueuedDownloads: self.dummy_message, slskmessages.GetPeerAddress: self.get_peer_address, slskmessages.UserInfoReply: self.user_info_reply, slskmessages.UserInfoRequest: self.user_info_request, slskmessages.PierceFireWall: self.dummy_message, slskmessages.ConnectToPeer: self.connect_to_peer, slskmessages.CantConnectToPeer: self.dummy_message, slskmessages.MessageProgress: self.message_progress, slskmessages.SharedFileList: self.shared_file_list, slskmessages.GetSharedFileList: self.get_shared_file_list, slskmessages.FileSearchRequest: self.dummy_message, slskmessages.FileSearchResult: self.file_search_result, slskmessages.GetUserStatus: self.get_user_status, slskmessages.GetUserStats: self.get_user_stats, slskmessages.Relogged: self.relogged, slskmessages.PeerInit: self.dummy_message, slskmessages.CheckDownloadQueue: self.check_download_queue, slskmessages.CheckUploadQueue: self.check_upload_queue, slskmessages.DownloadFile: self.file_download, slskmessages.UploadFile: self.file_upload, slskmessages.FileDownloadInit: self.file_download_init, slskmessages.FileUploadInit: self.file_upload_init, slskmessages.TransferRequest: self.transfer_request, slskmessages.TransferResponse: self.transfer_response, slskmessages.QueueUpload: self.queue_upload, slskmessages.UploadDenied: self.upload_denied, slskmessages.UploadFailed: self.upload_failed, slskmessages.PlaceInQueue: self.place_in_queue, slskmessages.DownloadFileError: self.download_file_error, slskmessages.UploadFileError: self.upload_file_error, slskmessages.DownloadConnClose: self.download_conn_close, slskmessages.UploadConnClose: self.upload_conn_close, slskmessages.FolderContentsResponse: self.folder_contents_response, slskmessages.FolderContentsRequest: self.folder_contents_request, slskmessages.RoomList: self.room_list, slskmessages.LeaveRoom: self.leave_room, slskmessages.GlobalUserList: self.dummy_message, slskmessages.AddUser: self.add_user, slskmessages.PrivilegedUsers: self.privileged_users, slskmessages.AddToPrivileged: self.add_to_privileged, slskmessages.CheckPrivileges: self.check_privileges, slskmessages.ServerPing: self.dummy_message, slskmessages.ParentMinSpeed: self.dummy_message, slskmessages.ParentSpeedRatio: self.dummy_message, slskmessages.ParentInactivityTimeout: self.dummy_message, slskmessages.SearchInactivityTimeout: self.dummy_message, slskmessages.MinParentsInCache: self.dummy_message, slskmessages.WishlistInterval: self.wishlist_interval, slskmessages.DistribAliveInterval: self.dummy_message, slskmessages.DistribChildDepth: self.dummy_message, slskmessages.DistribBranchLevel: self.dummy_message, slskmessages.DistribBranchRoot: self.dummy_message, slskmessages.AdminMessage: self.admin_message, slskmessages.TunneledMessage: self.dummy_message, slskmessages.PlaceholdUpload: self.dummy_message, slskmessages.PlaceInQueueRequest: self.place_in_queue_request, slskmessages.UploadQueueNotification: self.dummy_message, slskmessages.FileSearch: self.search_request, slskmessages.RoomSearch: self.search_request, slskmessages.UserSearch: self.search_request, slskmessages.RelatedSearch: self.dummy_message, slskmessages.PossibleParents: self.dummy_message, slskmessages.DistribAlive: self.dummy_message, slskmessages.DistribSearch: self.distrib_search, slskmessages.ResetDistributed: self.dummy_message, slskmessages.ServerTimeout: self.server_timeout, slskmessages.TransferTimeout: self.transfer_timeout, slskmessages.SetConnectionStats: self.set_connection_stats, slskmessages.GlobalRecommendations: self.global_recommendations, slskmessages.Recommendations: self.recommendations, slskmessages.ItemRecommendations: self.item_recommendations, slskmessages.SimilarUsers: self.similar_users, slskmessages.ItemSimilarUsers: self.item_similar_users, slskmessages.UserInterests: self.user_interests, slskmessages.RoomTickerState: self.room_ticker_state, slskmessages.RoomTickerAdd: self.room_ticker_add, slskmessages.RoomTickerRemove: self.room_ticker_remove, slskmessages.UserPrivileged: self.dummy_message, slskmessages.AckNotifyPrivileges: self.dummy_message, slskmessages.NotifyPrivileges: self.dummy_message, slskmessages.PrivateRoomUsers: self.private_room_users, slskmessages.PrivateRoomOwned: self.private_room_owned, slskmessages.PrivateRoomAddUser: self.private_room_add_user, slskmessages.PrivateRoomRemoveUser: self.private_room_remove_user, slskmessages.PrivateRoomAdded: self.private_room_added, slskmessages.PrivateRoomRemoved: self.private_room_removed, slskmessages.PrivateRoomDisown: self.private_room_disown, slskmessages.PrivateRoomToggle: self.private_room_toggle, slskmessages.PrivateRoomSomething: self.dummy_message, slskmessages.PrivateRoomOperatorAdded: self.private_room_operator_added, slskmessages.PrivateRoomOperatorRemoved: self.private_room_operator_removed, slskmessages.PrivateRoomAddOperator: self.private_room_add_operator, slskmessages.PrivateRoomRemoveOperator: self.private_room_remove_operator, slskmessages.PublicRoomMessage: self.public_room_message, slskmessages.ShowConnectionErrorMessage: self.show_connection_error_message, slskmessages.UnknownPeerMessage: self.ignore } def start(self, ui_callback=None, network_callback=None): self.ui_callback = ui_callback self.network_callback = network_callback if network_callback else self.network_event script_dir = os.path.dirname(__file__) log.add(_("Loading %(program)s %(version)s"), { "program": "Python", "version": config.python_version }) log.add_debug("Using %(program)s executable: %(exe)s", { "program": "Python", "exe": str(sys.executable) }) log.add_debug("Using %(program)s executable: %(exe)s", { "program": config.application_name, "exe": script_dir }) log.add(_("Loading %(program)s %(version)s"), { "program": config.application_name, "version": config.version }) self.geoip = GeoIP(os.path.join(script_dir, "geoip/ipcountrydb.bin")) self.notifications = Notifications(config, ui_callback) self.network_filter = NetworkFilter(self, config, self.queue, self.geoip) self.now_playing = NowPlaying(config) self.statistics = Statistics(config, ui_callback) self.shares = Shares(self, config, self.queue, self.network_callback, ui_callback) self.search = Search(self, config, self.queue, self.shares.share_dbs, self.geoip, ui_callback) self.transfers = Transfers(self, config, self.queue, self.network_callback, ui_callback) self.interests = Interests(self, config, self.queue, ui_callback) self.userbrowse = UserBrowse(self, config, ui_callback) self.userinfo = UserInfo(self, config, self.queue, ui_callback) self.userlist = UserList(self, config, self.queue, ui_callback) self.privatechats = PrivateChats(self, config, self.queue, ui_callback) self.chatrooms = ChatRooms(self, config, self.queue, ui_callback) self.transfers.init_transfers() self.privatechats.load_users() port_range = config.sections["server"]["portrange"] interface = config.sections["server"]["interface"] self.protothread = slskproto.SlskProtoThread(self.network_callback, self.queue, self.bindip, interface, self.port, port_range, self) self.upnp = UPnP(self, config) self.pluginhandler = PluginHandler(self, config) self.set_connection_stats(slskmessages.SetConnectionStats()) connect_ready = not config.need_config() if not connect_ready: log.add( _("You need to specify a username and password before connecting…" )) return connect_ready def quit(self, signal_type=None, _frame=None): log.add( _("Quitting %(program)s %(version)s, %(status)s…"), { "program": config.application_name, "version": config.version, "status": _("terminating") if signal_type == signal.SIGTERM else _("application closing") }) # Indicate that a shutdown has started, to prevent UI callbacks from networking thread self.shutdown = True if self.pluginhandler: self.pluginhandler.quit() # Shut down networking thread if self.protothread: if not self.protothread.server_disconnected: self.protothread.manual_server_disconnect = True self.server_disconnect() self.protothread.abort() # Save download/upload list to file if self.transfers: self.transfers.quit() # Closing up all shelves db if self.shares: self.shares.quit() if self.ui_callback: self.ui_callback.quit() config.write_configuration() log.add( _("Quit %(program)s %(version)s, %(status)s!"), { "program": config.application_name, "version": config.version, "status": _("terminated") if signal_type == signal.SIGTERM else _("done") }) def connect(self): if not self.protothread.server_disconnected: return True valid_network_interface = self.protothread.validate_network_interface() if not valid_network_interface: message = _( "The network interface you specified, '%s', does not exist. Change or remove the specified " "network interface and restart Nicotine+.") log.add_important_error(message, self.protothread.interface) return False valid_listen_port = self.protothread.validate_listen_port() if not valid_listen_port: message = _( "The range you specified for client connection ports was " "{}-{}, but none of these were usable. Increase and/or ". format(self.protothread.portrange[0], self.protothread.portrange[1]) + "move the range and restart Nicotine+.") if self.protothread.portrange[0] < 1024: message += "\n\n" + _( "Note that part of your range lies below 1024, this is usually not allowed on" " most operating systems with the exception of Windows.") log.add_important_error(message) return False self.protothread.server_connect() # Clear any potential messages queued up while offline self.queue.clear() addr = config.sections["server"]["server"] login = config.sections["server"]["login"] password = config.sections["server"]["passw"] log.add(_("Connecting to %(host)s:%(port)s"), { 'host': addr[0], 'port': addr[1] }) self.queue.append( slskmessages.ServerConnect(addr, login=(login, password))) return True def disconnect(self): self.queue.append(slskmessages.ServerDisconnect()) def server_disconnect(self, msg=None): self.logged_in = False # Clean up connections self.user_statuses.clear() self.watched_users.clear() self.pluginhandler.server_disconnect_notification( msg.manual_disconnect if msg else True) self.transfers.server_disconnect() self.search.server_disconnect() self.userlist.server_disconnect() self.chatrooms.server_disconnect() self.privatechats.server_disconnect() self.userinfo.server_disconnect() self.userbrowse.server_disconnect() self.interests.server_disconnect() if self.ui_callback: self.ui_callback.server_disconnect() self.login_username = None def send_message_to_peer(self, user, message, address=None): """ Sends message to a peer. Used when we know the username of a peer, but don't have/know an active connection. """ self.queue.append( slskmessages.SendNetworkMessage(user, message, address)) def set_away_mode(self, is_away, save_state=False): if save_state: config.sections["server"]["away"] = is_away self.away = is_away self.request_set_status(is_away and 1 or 2) # Reset away message users self.privatechats.set_away_mode(is_away) if self.ui_callback: self.ui_callback.set_away_mode(is_away) def request_change_password(self, password): self.queue.append(slskmessages.ChangePassword(password)) def request_check_privileges(self): self.queue.append(slskmessages.CheckPrivileges()) def request_give_privileges(self, user, days): self.queue.append(slskmessages.GivePrivileges(user, days)) def request_ip_address(self, username): self.ip_requested.add(username) self.queue.append(slskmessages.GetPeerAddress(username)) def request_set_status(self, status): self.queue.append(slskmessages.SetStatus(status)) def get_user_country(self, user): """ Retrieve a user's country code if previously cached, otherwise request user's IP address to determine country """ user_address = self.protothread.user_addresses.get(user) if user_address and user != self.protothread.server_username: ip_address, _port = user_address country_code = self.geoip.get_country_code(ip_address) return country_code if user not in self.ip_requested: self.queue.append(slskmessages.GetPeerAddress(user)) return None def watch_user(self, user, force_update=False): """ Tell the server we want to be notified of status/stat updates for a user """ if not isinstance(user, str): return if not force_update and user in self.watched_users: # Already being watched, and we don't need to re-fetch the status/stats return self.queue.append(slskmessages.AddUser(user)) # Get privilege status self.queue.append(slskmessages.GetUserStatus(user)) @staticmethod def dummy_message(msg): log.add_msg_contents(msg) def ignore(self, msg): # Ignore received message pass def network_event(self, msgs): for i in msgs: if self.shutdown: return if i.__class__ in self.events: self.events[i.__class__](i) else: log.add("No handler for class %s %s", (i.__class__, dir(i))) msgs.clear() def show_connection_error_message(self, msg): """ Request UI to show error messages related to connectivity """ for i in msg.msgs: if i.__class__ in (slskmessages.TransferRequest, slskmessages.FileUploadInit): self.transfers.get_cant_connect_upload(i.token) elif i.__class__ is slskmessages.QueueUpload: self.transfers.get_cant_connect_queue_file(msg.user, i.file) elif i.__class__ is slskmessages.GetSharedFileList: self.userbrowse.show_connection_error(msg.user) elif i.__class__ is slskmessages.UserInfoRequest: self.userinfo.show_connection_error(msg.user) def message_progress(self, msg): if msg.msg_type is slskmessages.SharedFileList: self.userbrowse.message_progress(msg) elif msg.msg_type is slskmessages.UserInfoReply: self.userinfo.message_progress(msg) def server_timeout(self, *_args): if not config.need_config(): self.connect() def check_download_queue(self, _msg): self.transfers.check_download_queue() def check_upload_queue(self, _msg): self.transfers.check_upload_queue() def file_download(self, msg): log.add_msg_contents(msg) self.transfers.file_download(msg) def file_upload(self, msg): log.add_msg_contents(msg) self.transfers.file_upload(msg) def download_file_error(self, msg): log.add_msg_contents(msg) self.transfers.download_file_error(msg) def upload_file_error(self, msg): log.add_msg_contents(msg) self.transfers.upload_file_error(msg) def download_conn_close(self, msg): log.add_msg_contents(msg) self.transfers.download_conn_close(msg) def upload_conn_close(self, msg): log.add_msg_contents(msg) self.transfers.upload_conn_close(msg) def transfer_timeout(self, msg): self.transfers.transfer_timeout(msg) def set_connection_stats(self, msg): if self.ui_callback: self.ui_callback.set_connection_stats(msg) """ Incoming Server Messages """ def login(self, msg): """ Server code: 1 """ log.add_msg_contents(msg) if msg.success: self.logged_in = True self.login_username = msg.username self.set_away_mode(config.sections["server"]["away"]) self.watch_user(msg.username) if msg.ip_address is not None: self.user_ip_address = msg.ip_address self.transfers.server_login() self.search.server_login() self.userbrowse.server_login() self.userinfo.server_login() self.userlist.server_login() self.privatechats.server_login() self.chatrooms.server_login() if self.ui_callback: self.ui_callback.server_login() if msg.banner: log.add(msg.banner) self.interests.server_login() self.shares.send_num_shared_folders_files() self.queue.append( slskmessages.PrivateRoomToggle( config.sections["server"]["private_chatrooms"])) self.pluginhandler.server_connect_notification() else: if msg.reason == "INVALIDPASS": self.ui_callback.invalid_password() return log.add_important_error( _("Unable to connect to the server. Reason: %s"), msg.reason) def get_peer_address(self, msg): """ Server code: 3 """ log.add_msg_contents(msg) user = msg.user # User seems to be offline, don't update IP if msg.ip_address != "0.0.0.0": # If the IP address changed, make sure our IP block/ignore list reflects this self.network_filter.update_saved_user_ip_filters(user) if self.network_filter.block_unblock_user_ip_callback(user): return if self.network_filter.ignore_unignore_user_ip_callback(user): return country_code = self.geoip.get_country_code(msg.ip_address) self.chatrooms.set_user_country(user, country_code) self.userinfo.set_user_country(user, country_code) self.userlist.set_user_country(user, country_code) # From this point on all paths should call # self.pluginhandler.user_resolve_notification precisely once self.privatechats.private_message_queue_process(user) if user not in self.ip_requested: self.pluginhandler.user_resolve_notification( user, msg.ip_address, msg.port) return self.ip_requested.remove(user) self.pluginhandler.user_resolve_notification(user, msg.ip_address, msg.port, country_code) if country_code: country = " (%(cc)s / %(country)s)" % { 'cc': country_code, 'country': self.geoip.country_code_to_name(country_code) } else: country = "" if msg.ip_address == "0.0.0.0": log.add( _("Cannot retrieve the IP of user %s, since this user is offline" ), user) return log.add( _("IP address of user %(user)s is %(ip)s, port %(port)i%(country)s" ), { 'user': user, 'ip': msg.ip_address, 'port': msg.port, 'country': country }) def add_user(self, msg): """ Server code: 5 """ log.add_msg_contents(msg) self.watched_users.add(msg.user) if msg.userexists and msg.status is None: # Legacy support (Soulfind server) self.queue.append(slskmessages.GetUserStatus(msg.user)) if msg.files is not None: self.get_user_stats(msg, log_contents=False) def get_user_status(self, msg, log_contents=True): """ Server code: 7 """ if log_contents: log.add_msg_contents(msg) if msg.status is None: msg.status = -1 self.user_statuses[msg.user] = msg.status if msg.privileged == 1: self.transfers.add_to_privileged(msg.user) elif msg.privileged == 0: self.transfers.remove_from_privileged(msg.user) self.interests.get_user_status(msg) self.transfers.get_user_status(msg) self.userbrowse.get_user_status(msg) self.userinfo.get_user_status(msg) self.userlist.get_user_status(msg) self.privatechats.get_user_status(msg) self.chatrooms.get_user_status(msg) self.pluginhandler.user_status_notification(msg.user, msg.status, bool(msg.privileged)) def say_chat_room(self, msg): """ Server code: 13 """ log.add_msg_contents(msg) log.add_chat( _("Chat message from user '%(user)s' in room '%(room)s': %(message)s" ), { "user": msg.user, "room": msg.room, "message": msg.msg }) self.chatrooms.say_chat_room(msg) def join_room(self, msg): """ Server code: 14 """ log.add_msg_contents(msg) self.chatrooms.join_room(msg) def leave_room(self, msg): """ Server code: 15 """ log.add_msg_contents(msg) self.chatrooms.leave_room(msg) def user_joined_room(self, msg): """ Server code: 16 """ log.add_msg_contents(msg) self.chatrooms.user_joined_room(msg) def user_left_room(self, msg): """ Server code: 17 """ log.add_msg_contents(msg) self.chatrooms.user_left_room(msg) def connect_to_peer(self, msg): """ Server code: 18 """ log.add_msg_contents(msg) if msg.privileged == 1: self.transfers.add_to_privileged(msg.user) elif msg.privileged == 0: self.transfers.remove_from_privileged(msg.user) def message_user(self, msg): """ Server code: 22 """ log.add_msg_contents(msg) log.add_chat(_("Private message from user '%(user)s': %(message)s"), { "user": msg.user, "message": msg.msg }) self.privatechats.message_user(msg) def search_request(self, msg): """ Server code: 26, 42 and 120 """ log.add_msg_contents(msg) self.search.process_search_request(msg.searchterm, msg.user, msg.token, direct=True) self.pluginhandler.search_request_notification(msg.searchterm, msg.user, msg.token) def get_user_stats(self, msg, log_contents=True): """ Server code: 36 """ if log_contents: log.add_msg_contents(msg) if msg.user == self.login_username: self.transfers.upload_speed = msg.avgspeed self.interests.get_user_stats(msg) self.userinfo.get_user_stats(msg) self.userlist.get_user_stats(msg) self.chatrooms.get_user_stats(msg) stats = { 'avgspeed': msg.avgspeed, 'uploadnum': msg.uploadnum, 'files': msg.files, 'dirs': msg.dirs, } self.pluginhandler.user_stats_notification(msg.user, stats) @staticmethod def relogged(_msg): """ Server code: 41 """ log.add_important_info( _("Someone logged in to your Soulseek account elsewhere")) def recommendations(self, msg): """ Server code: 54 """ log.add_msg_contents(msg) self.interests.recommendations(msg) def global_recommendations(self, msg): """ Server code: 56 """ log.add_msg_contents(msg) self.interests.global_recommendations(msg) def user_interests(self, msg): """ Server code: 57 """ log.add_msg_contents(msg) self.userinfo.user_interests(msg) def room_list(self, msg): """ Server code: 64 """ log.add_msg_contents(msg) self.chatrooms.room_list(msg) @staticmethod def admin_message(msg): """ Server code: 66 """ log.add_important_info(msg.msg) def privileged_users(self, msg): """ Server code: 69 """ log.add_msg_contents(msg) self.transfers.set_privileged_users(msg.users) log.add(_("%i privileged users"), (len(msg.users))) def add_to_privileged(self, msg): """ Server code: 91 """ """ DEPRECATED """ log.add_msg_contents(msg) self.transfers.add_to_privileged(msg.user) def check_privileges(self, msg): """ Server code: 92 """ log.add_msg_contents(msg) mins = msg.seconds // 60 hours = mins // 60 days = hours // 24 if msg.seconds == 0: log.add( _("You have no privileges. Privileges are not required, but allow your downloads " "to be queued ahead of non-privileged users.")) else: log.add( _("%(days)i days, %(hours)i hours, %(minutes)i minutes, %(seconds)i seconds of " "download privileges left."), { 'days': days, 'hours': hours % 24, 'minutes': mins % 60, 'seconds': msg.seconds % 60 }) self.privileges_left = msg.seconds def wishlist_interval(self, msg): """ Server code: 104 """ log.add_msg_contents(msg) self.search.set_wishlist_interval(msg) def similar_users(self, msg): """ Server code: 110 """ log.add_msg_contents(msg) self.interests.similar_users(msg) def item_recommendations(self, msg): """ Server code: 111 """ log.add_msg_contents(msg) self.interests.item_recommendations(msg) def item_similar_users(self, msg): """ Server code: 112 """ log.add_msg_contents(msg) self.interests.item_similar_users(msg) def room_ticker_state(self, msg): """ Server code: 113 """ log.add_msg_contents(msg) self.chatrooms.ticker_set(msg) def room_ticker_add(self, msg): """ Server code: 114 """ log.add_msg_contents(msg) self.chatrooms.ticker_add(msg) def room_ticker_remove(self, msg): """ Server code: 115 """ log.add_msg_contents(msg) self.chatrooms.ticker_remove(msg) def private_room_users(self, msg): """ Server code: 133 """ log.add_msg_contents(msg) self.chatrooms.private_room_users(msg) def private_room_add_user(self, msg): """ Server code: 134 """ log.add_msg_contents(msg) self.chatrooms.private_room_add_user(msg) def private_room_remove_user(self, msg): """ Server code: 135 """ log.add_msg_contents(msg) self.chatrooms.private_room_remove_user(msg) def private_room_disown(self, msg): """ Server code: 137 """ log.add_msg_contents(msg) self.chatrooms.private_room_disown(msg) def private_room_added(self, msg): """ Server code: 139 """ log.add_msg_contents(msg) self.chatrooms.private_room_added(msg) def private_room_removed(self, msg): """ Server code: 140 """ log.add_msg_contents(msg) self.chatrooms.private_room_removed(msg) def private_room_toggle(self, msg): """ Server code: 141 """ log.add_msg_contents(msg) self.chatrooms.private_room_toggle(msg) @staticmethod def change_password(msg): """ Server code: 142 """ log.add_msg_contents(msg) password = msg.password config.sections["server"]["passw"] = password config.write_configuration() log.add_important_info( _("Your password has been changed. Password is %s"), password) def private_room_add_operator(self, msg): """ Server code: 143 """ log.add_msg_contents(msg) self.chatrooms.private_room_add_operator(msg) def private_room_remove_operator(self, msg): """ Server code: 144 """ log.add_msg_contents(msg) self.chatrooms.private_room_remove_operator(msg) def private_room_operator_added(self, msg): """ Server code: 145 """ log.add_msg_contents(msg) self.chatrooms.private_room_operator_added(msg) def private_room_operator_removed(self, msg): """ Server code: 146 """ log.add_msg_contents(msg) self.chatrooms.private_room_operator_removed(msg) def private_room_owned(self, msg): """ Server code: 148 """ log.add_msg_contents(msg) self.chatrooms.private_room_owned(msg) def public_room_message(self, msg): """ Server code: 152 """ log.add_msg_contents(msg) self.chatrooms.public_room_message(msg) """ Incoming Peer Messages """ def get_shared_file_list(self, msg): """ Peer code: 4 """ log.add_msg_contents(msg) user = msg.init.target_user request_time = time.time() if user in self.requested_share_times and request_time < self.requested_share_times[ user] + 0.4: # Ignoring request, because it's less than half a second since the # last one by this user return self.requested_share_times[user] = request_time log.add(_("User %(user)s is browsing your list of shared files"), {'user': user}) ip_address, _port = msg.init.addr checkuser, reason = self.network_filter.check_user(user, ip_address) if not checkuser: message = self.ban_message % reason self.privatechats.send_automatic_message(user, message) shares_list = None if checkuser == 1: # Send Normal Shares shares_list = self.shares.get_compressed_shares_message("normal") elif checkuser == 2: # Send Buddy Shares shares_list = self.shares.get_compressed_shares_message("buddy") if not shares_list: # Nyah, Nyah shares_list = slskmessages.SharedFileList(msg.init, {}) shares_list.init = msg.init self.queue.append(shares_list) def shared_file_list(self, msg): """ Peer code: 5 """ username = msg.init.target_user self.userbrowse.shared_file_list(username, msg) def file_search_result(self, msg): """ Peer message: 9 """ log.add_msg_contents(msg) self.search.file_search_result(msg) def user_info_request(self, msg): """ Peer code: 15 """ log.add_msg_contents(msg) user = msg.init.target_user ip_address, _port = msg.init.addr request_time = time.time() if user in self.requested_info_times and request_time < self.requested_info_times[ user] + 0.4: # Ignoring request, because it's less than half a second since the # last one by this user return self.requested_info_times[user] = request_time if self.login_username != user: log.add(_("User %(user)s is reading your user info"), {'user': user}) status, reason = self.network_filter.check_user(user, ip_address) if not status: pic = None descr = self.ban_message % reason descr += "\n\n----------------------------------------------\n\n" descr += unescape(config.sections["userinfo"]["descr"]) else: try: userpic = config.sections["userinfo"]["pic"] with open(userpic, 'rb') as file_handle: pic = file_handle.read() except Exception: pic = None descr = unescape(config.sections["userinfo"]["descr"]) totalupl = self.transfers.get_total_uploads_allowed() queuesize = self.transfers.get_upload_queue_size() slotsavail = self.transfers.allow_new_uploads() if config.sections["transfers"]["remotedownloads"]: uploadallowed = config.sections["transfers"]["uploadallowed"] else: uploadallowed = 0 self.queue.append( slskmessages.UserInfoReply(msg.init, descr, pic, totalupl, queuesize, slotsavail, uploadallowed)) def user_info_reply(self, msg): """ Peer code: 16 """ log.add_msg_contents(msg) username = msg.init.target_user self.userinfo.user_info_reply(username, msg) def p_message_user(self, msg): """ Peer code: 22 """ log.add_msg_contents(msg) username = msg.init.target_user if username != msg.user: msg.msg = _( "(Warning: %(realuser)s is attempting to spoof %(fakeuser)s) " ) % { "realuser": username, "fakeuser": msg.user } + msg.msg msg.user = username self.privatechats.message_user(msg) def folder_contents_request(self, msg): """ Peer code: 36 """ log.add_msg_contents(msg) init = msg.init ip_address, _port = msg.init.addr username = msg.init.target_user checkuser, reason = self.network_filter.check_user( username, ip_address) if not checkuser: message = self.ban_message % reason self.privatechats.send_automatic_message(username, message) normalshares = self.shares.share_dbs.get("streams") buddyshares = self.shares.share_dbs.get("buddystreams") if checkuser == 1 and normalshares is not None: shares = normalshares elif checkuser == 2 and buddyshares is not None: shares = buddyshares else: shares = {} if checkuser: try: if msg.dir in shares: self.queue.append( slskmessages.FolderContentsResponse( init, msg.dir, shares[msg.dir])) return if msg.dir.rstrip('\\') in shares: self.queue.append( slskmessages.FolderContentsResponse( init, msg.dir, shares[msg.dir.rstrip('\\')])) return except Exception as error: log.add( _("Failed to fetch the shared folder %(folder)s: %(error)s" ), { "folder": msg.dir, "error": error }) self.queue.append( slskmessages.FolderContentsResponse(init, msg.dir, None)) def folder_contents_response(self, msg): """ Peer code: 37 """ file_list = msg.list # Check for a large number of files many = False folder = "" for i in file_list: for j in file_list[i]: if os.path.commonprefix([i, j]) == j: numfiles = len(file_list[i][j]) if numfiles > 100: many = True folder = j if many: username = msg.init.target_user self.transfers.downloadsview.download_large_folder( username, folder, numfiles, msg) else: self.transfers.folder_contents_response(msg) def transfer_request(self, msg): """ Peer code: 40 """ log.add_msg_contents(msg) self.transfers.transfer_request(msg) def transfer_response(self, msg): """ Peer code: 41 """ log.add_msg_contents(msg) self.transfers.transfer_response(msg) def queue_upload(self, msg): """ Peer code: 43 """ log.add_msg_contents(msg) self.transfers.queue_upload(msg) def place_in_queue(self, msg): """ Peer code: 44 """ log.add_msg_contents(msg) self.transfers.place_in_queue(msg) def upload_failed(self, msg): """ Peer code: 46 """ log.add_msg_contents(msg) self.transfers.upload_failed(msg) def upload_denied(self, msg): """ Peer code: 50 """ log.add_msg_contents(msg) self.transfers.upload_denied(msg) def place_in_queue_request(self, msg): """ Peer code: 51 """ log.add_msg_contents(msg) self.transfers.place_in_queue_request(msg) """ Incoming File Messages """ def file_download_init(self, msg): log.add_msg_contents(msg) self.transfers.file_download_init(msg) def file_upload_init(self, msg): log.add_msg_contents(msg) self.transfers.file_upload_init(msg) """ Incoming Distributed Messages """ def distrib_search(self, msg): """ Distrib code: 3 """ # Verbose: log.add_msg_contents(msg) self.search.process_search_request(msg.searchterm, msg.user, msg.token, direct=False) self.pluginhandler.distrib_search_notification(msg.searchterm, msg.user, msg.token)
def start(self, ui_callback=None, network_callback=None): self.ui_callback = ui_callback self.network_callback = network_callback if network_callback else self.network_event script_dir = os.path.dirname(__file__) log.add(_("Loading %(program)s %(version)s"), { "program": "Python", "version": config.python_version }) log.add_debug("Using %(program)s executable: %(exe)s", { "program": "Python", "exe": str(sys.executable) }) log.add_debug("Using %(program)s executable: %(exe)s", { "program": config.application_name, "exe": script_dir }) log.add(_("Loading %(program)s %(version)s"), { "program": config.application_name, "version": config.version }) self.geoip = GeoIP(os.path.join(script_dir, "geoip/ipcountrydb.bin")) self.notifications = Notifications(config, ui_callback) self.network_filter = NetworkFilter(self, config, self.queue, self.geoip) self.now_playing = NowPlaying(config) self.statistics = Statistics(config, ui_callback) self.shares = Shares(self, config, self.queue, self.network_callback, ui_callback) self.search = Search(self, config, self.queue, self.shares.share_dbs, self.geoip, ui_callback) self.transfers = Transfers(self, config, self.queue, self.network_callback, ui_callback) self.interests = Interests(self, config, self.queue, ui_callback) self.userbrowse = UserBrowse(self, config, ui_callback) self.userinfo = UserInfo(self, config, self.queue, ui_callback) self.userlist = UserList(self, config, self.queue, ui_callback) self.privatechats = PrivateChats(self, config, self.queue, ui_callback) self.chatrooms = ChatRooms(self, config, self.queue, ui_callback) self.transfers.init_transfers() self.privatechats.load_users() port_range = config.sections["server"]["portrange"] interface = config.sections["server"]["interface"] self.protothread = slskproto.SlskProtoThread(self.network_callback, self.queue, self.bindip, interface, self.port, port_range, self) self.upnp = UPnP(self, config) self.pluginhandler = PluginHandler(self, config) self.set_connection_stats(slskmessages.SetConnectionStats()) connect_ready = not config.need_config() if not connect_ready: log.add( _("You need to specify a username and password before connecting…" )) return connect_ready