def handle(userToken, _=None, deleteToken=True): # get usertoken data userID = userToken.userID username = userToken.username requestToken = userToken.token # Big client meme here. If someone logs out and logs in right after, # the old logout packet will still be in the queue and will be sent to # the server, so we accept logout packets sent at least 5 seconds after login # if the user logs out before 5 seconds, he will be disconnected later with timeout check if int(time.time() - userToken.loginTime) >= 5 or userToken.irc: # Stop spectating userToken.stopSpectating() # Part matches userToken.leaveMatch() # Check if a users login/logouts are being tracked. If so, log to discord tracked = userUtils.getUserTracked(userID) if tracked: log.cmyui( 'Tracked user {} ({}) has logged out.'.format( username, userID), 'cm') # Part all joined channels for i in userToken.joinedChannels: chat.partChannel(token=userToken, channel=i) # Leave all joined streams userToken.leaveAllStreams() # Enqueue our disconnection to everyone else glob.streams.broadcast("main", serverPackets.userLogout(userID)) # Disconnect from IRC if needed if userToken.irc and glob.irc: glob.ircServer.forceDisconnection(userToken.username) # Delete token if deleteToken: glob.tokens.deleteToken(requestToken) else: userToken.kicked = True # Change username if needed newUsername = glob.redis.get( "ripple:change_username_pending:{}".format(userID)) if newUsername is not None: log.debug( "Sending username change request for user {}".format(userID)) glob.redis.publish( "peppy:change_username", json.dumps({ "userID": userID, "newUsername": newUsername.decode("utf-8") })) # Console output log.info("{} has been disconnected. (logout)".format(username))
def logHardware(userID, hashes, activation = False): """ Hardware log USED FOR MULTIACCOUNT DETECTION :param userID: user id :param hashes: Peppy's botnet (client data) structure (new line = "|", already split) [0] osu! version [1] plain mac addressed, separated by "." [2] mac addresses hash set [3] unique ID [4] disk ID :param activation: if True, set this hash as used for activation. Default: False. :return: True if hw is not banned, otherwise false """ # Make sure the strings are not empty for i in hashes[2:5]: if i == "": log.warning("Invalid hash set ({}) for user {} in HWID check".format(hashes, userID), "bunk") return False # Run some HWID checks on that user if he is not restricted if not isRestricted(userID): # Get username username = getUsername(userID) # cmyui vars usualMAC = glob.conf.config['cmyui']['mac'] usualUniqueId = glob.conf.config['cmyui']['unique'] usualDiskId = glob.conf.config['cmyui']['disk'] if userID == 1001: # Remove cmyui permissions if on a HWID different than usual.. Just safety procautions.. TODO: make this for all admins if usualMAC == hashes[2] and usualUniqueId == hashes[3] and usualDiskId == hashes[4]: annmsg = "{}: Valid login.".format(username) params = urlencode({"k": glob.conf.config['server']['cikey'], "to": "#admin", "msg": annmsg}) requests.get("http://127.0.0.1:5001/api/v1/fokabotMessage?{}".format(params)) else: annmsg = "{}: Invalid login.".format(username) params = urlencode({"k": glob.conf.config['server']['cikey'], "to": "#admin", "msg": annmsg}) requests.get("http://127.0.0.1:5001/api/v1/fokabotMessage?{}".format(params)) log.cmyui("{}: Unusual login detected.\n\nHashes: {}|{}|{}".format(userID, hashes[2], hashes[3], hashes[4]), discord="cm") ban(userID) # Get the list of banned or restricted users that have logged in from this or similar HWID hash set if hashes[2] == "b4ec3c4334a0249dae95c284ec5983df": # Running under wine, check by unique id log.debug("Logging Linux/Mac hardware") banned = glob.db.fetchAll("""SELECT users.id as userid, hw_user.occurencies, users.username FROM hw_user LEFT JOIN users ON users.id = hw_user.userid WHERE hw_user.userid != %(userid)s AND hw_user.unique_id = %(uid)s AND (users.privileges & 3 != 3)""", { "userid": userID, "uid": hashes[3], }) else: # Running under windows, do all checks log.debug("Logging Windows hardware") banned = glob.db.fetchAll("""SELECT users.id as userid, hw_user.occurencies, users.username FROM hw_user LEFT JOIN users ON users.id = hw_user.userid WHERE hw_user.userid != %(userid)s AND hw_user.mac = %(mac)s AND hw_user.unique_id = %(uid)s AND hw_user.disk_id = %(diskid)s AND (users.privileges & 3 != 3)""", { "userid": userID, "mac": hashes[2], "uid": hashes[3], "diskid": hashes[4], }) for i in banned: # Get the total numbers of logins total = glob.db.fetch("SELECT COUNT(*) AS count FROM hw_user WHERE userid = %s LIMIT 1", [userID]) # and make sure it is valid if total is None: continue total = total["count"] # Calculate 10% of total perc = (total*10)/100 if i["occurencies"] >= perc: # If the banned user has logged in more than 10% of the times from this user, restrict this user restrict(userID) appendNotes(userID, "Logged in from HWID ({hwid}) used more than 10% from user {banned} ({bannedUserID}), who is banned/restricted.".format( hwid=hashes[2:5], banned=i["username"], bannedUserID=i["userid"] )) log.warning("**{user}** ({userID}) has been restricted because he has logged in from HWID _({hwid})_ used more than 10% from banned/restricted user **{banned}** ({bannedUserID}), **possible multiaccount**.".format( user=username, userID=userID, hwid=hashes[2:5], banned=i["username"], bannedUserID=i["userid"] ), "cm") # Update hash set occurencies glob.db.execute(""" INSERT INTO hw_user (id, userid, mac, unique_id, disk_id, occurencies) VALUES (NULL, %s, %s, %s, %s, 1) ON DUPLICATE KEY UPDATE occurencies = occurencies + 1 """, [userID, hashes[2], hashes[3], hashes[4]]) # Optionally, set this hash as 'used for activation' if activation: glob.db.execute("UPDATE hw_user SET activated = 1 WHERE userid = %s AND mac = %s AND unique_id = %s AND disk_id = %s", [userID, hashes[2], hashes[3], hashes[4]]) # Access granted, abbiamo impiegato 3 giorni # We grant access even in case of login from banned HWID # because we call restrict() above so there's no need to deny the access. return True
def handle(tornadoRequest): # Data to return responseToken = None responseTokenString = "ayy" responseData = bytes() # Get IP from tornado request requestIP = tornadoRequest.getRequestIP() # Avoid exceptions clientData = ["unknown", "unknown", "unknown", "unknown", "unknown"] osuVersion = "unknown" # Split POST body so we can get username/password/hardware data # 2:-3 thing is because requestData has some escape stuff that we don't need loginData = str(tornadoRequest.request.body)[2:-3].split("\\n") try: # Make sure loginData is valid if len(loginData) < 3: raise exceptions.invalidArgumentsException() # Get HWID, MAC address and more # Structure (new line = "|", already split) # [0] osu! version # [1] plain mac addressed, separated by "." # [2] mac addresses hash set # [3] unique ID # [4] disk ID splitData = loginData[2].split("|") osuVersion = splitData[0] timeOffset = int(splitData[1]) clientData = splitData[3].split(":")[:5] if len(clientData) < 4: raise exceptions.forceUpdateException() # Try to get the ID from username username = str(loginData[0]) userID = userUtils.getID(username) if not userID: # Invalid username raise exceptions.loginFailedException() if not userUtils.checkLogin(userID, loginData[1]): # Invalid password raise exceptions.loginFailedException() # Make sure we are not banned or locked priv = userUtils.getPrivileges(userID) if userUtils.isBanned(userID) and priv & privileges.USER_PENDING_VERIFICATION == 0: raise exceptions.loginBannedException() if userUtils.isLocked(userID) and priv & privileges.USER_PENDING_VERIFICATION == 0: raise exceptions.loginLockedException() # 2FA check if userUtils.check2FA(userID, requestIP): log.warning("Need 2FA check for user {}.".format(loginData[0])) raise exceptions.need2FAException() # No login errors! # Verify this user (if pending activation) firstLogin = False if priv & privileges.USER_PENDING_VERIFICATION > 0 or not userUtils.hasVerifiedHardware(userID): if userUtils.verifyUser(userID, clientData): # Valid account log.info("Account {} verified successfully!".format(userID)) glob.verifiedCache[str(userID)] = 1 firstLogin = True else: # Multiaccount detected log.info("Account {} NOT verified!".format(userID)) glob.verifiedCache[str(userID)] = 0 raise exceptions.loginBannedException() # Save HWID in db for multiaccount detection hwAllowed = userUtils.logHardware(userID, clientData, firstLogin) # This is false only if HWID is empty # if HWID is banned, we get restricted so there's no # need to deny bancho access if not hwAllowed: raise exceptions.haxException() # Log user IP userUtils.logIP(userID, requestIP) # Delete old tokens for that user and generate a new one isTournament = "tourney" in osuVersion if not isTournament: glob.tokens.deleteOldTokens(userID) responseToken = glob.tokens.addToken(userID, requestIP, timeOffset=timeOffset, tournament=isTournament) responseTokenString = responseToken.token # Check restricted mode (and eventually send message) responseToken.checkRestricted() userFlags = userUtils.getUserFlags(userID) if userFlags > 0: # Pending public bans. Such as chargebacks, etc. flagReason = userUtils.getFlagReason(userID) if userFlags-int(time.time()) < 0: responseToken.enqueue(serverPackets.notification("Your account has been automatically restricted due to a pending restriction not being having been dealt with.\n\nReason: {}".format(flagReason))) userUtils.restrict(userID) userUtils.setUserFlags(userID, 0) log.cmyui("{} has been automatically restricted due to not dealing with pending restriction. Reason: {}.".format(username, flagReason), discord="cm") log.rap(userID, "has been restricted due to a pending restriction. Reason: {}.".format(flagReason)) else: if "charge" in flagReason: responseToken.enqueue(serverPackets.notification("Your account has been flagged with an automatic restriction.\n\nIt will occur at {time} if not dealt with.\n" "Reason: {reason}\n\nTo avoid being restricted for this behaviour, you can cancel or revert your chargeback before your restriction date.".format(time=datetime.utcfromtimestamp(int(userFlags)).strftime('%Y-%m-%d %H:%M:%S'), reason=flagReason))) elif "live" in flagReason: responseToken.enqueue(serverPackets.notification("Your account has been flagged with an automatic restriction.\n\nIt will occur at {time} if not dealt with.\n" "Reason: {reason}\n\nThis means you are required to submit a liveplay to avoid this. This only happens in cases when we are confident in foul play; and are offering you this opportunity as a final stance to prove your legitimacy, against all the odds.".format(time=datetime.utcfromtimestamp(int(userFlags)).strftime('%Y-%m-%d %H:%M:%S'), reason=flagReason))) else: responseToken.enqueue(serverPackets.notification("Your account has been flagged with an automatic restriction.\n\nIt will occur at {time} if not dealt with.\n" "Reason: {reason}\n\nYou have until the restriction to deal with the issue.".format(time=datetime.utcfromtimestamp(int(userFlags)).strftime('%Y-%m-%d %H:%M:%S'), reason=flagReason))) # Send message if premium / donor expires soon # ok spaghetti code time if responseToken.privileges & privileges.USER_DONOR: donorType = 'premium' if responseToken.privileges & privileges.USER_PREMIUM else 'donor' expireDate = userUtils.getDonorExpire(responseToken.userID) if expireDate-int(time.time()) < 0: userUtils.setPrivileges(userID, 3) log.cmyui("{}'s donation perks have been removed as their time has run out.".format(username), discord="cm") log.rap(userID, "User's donor perks have been removed as their time has run out.") responseToken.enqueue(serverPackets.notification("Your {donorType} tag has expired! Thank you so much for the support, it really means everything to us. If you wish to keep supporting Akatsuki and you don't want to lose your {donorType} privileges, you can donate again by clicking on 'Support us' on Akatsuki's website.".format(donorType=donorType))) elif expireDate-int(time.time()) <= 86400*3: expireDays = round((expireDate-int(time.time()))/86400) expireIn = "{} days".format(expireDays) if expireDays > 1 else "less than 24 hours" responseToken.enqueue(serverPackets.notification("Your {donorType} tag expires in {expireIn}! When your {donorType} tag expires, you won't have any of the {donorType} privileges, like yellow username, custom badge and discord custom role and username color! If you wish to keep supporting Akatsuki and you don't want to lose your {donorType} privileges, you can donate again by clicking on 'Support us' on Akatsuki's website.".format(donorType=donorType, expireIn=expireIn))) """ Akatsuki does not use 2fa! we suck! if userUtils.deprecateTelegram2Fa(userID): responseToken.enqueue(serverPackets.notification("As stated on our blog, Telegram 2FA has been deprecated on 29th June 2018. Telegram 2FA has just been disabled from your account. If you want to keep your account secure with 2FA, please enable TOTP-based 2FA from our website https://akatsuki.pw. Thank you for your patience.")) """ # Set silence end UNIX time in token responseToken.silenceEndTime = userUtils.getSilenceEnd(userID) # Get only silence remaining seconds silenceSeconds = responseToken.getSilenceSecondsLeft() # Get supporter/GMT userGMT = False userSupporter = True userTournament = False if responseToken.admin: userGMT = True if responseToken.privileges & privileges.USER_TOURNAMENT_STAFF > 0: userTournament = True # Server restarting check if glob.restarting: raise exceptions.banchoRestartingException() # Send login notification before maintenance message if glob.banchoConf.config["loginNotification"] != "": responseToken.enqueue(serverPackets.notification(glob.banchoConf.config["loginNotification"])) # Maintenance check if glob.banchoConf.config["banchoMaintenance"]: if not userGMT: # We are not mod/admin, delete token, send notification and logout glob.tokens.deleteToken(responseTokenString) raise exceptions.banchoMaintenanceException() else: # We are mod/admin, send warning notification and continue responseToken.enqueue(serverPackets.notification("Akatsuki is currently in maintenance mode. Only admins have full access to the server.\nType '!system maintenance off' in chat to turn off maintenance mode.")) # Send all needed login packets responseToken.enqueue(serverPackets.silenceEndTime(silenceSeconds)) responseToken.enqueue(serverPackets.userID(userID)) responseToken.enqueue(serverPackets.protocolVersion()) responseToken.enqueue(serverPackets.userSupporterGMT(userSupporter, userGMT, userTournament)) responseToken.enqueue(serverPackets.userPanel(userID, True)) responseToken.enqueue(serverPackets.userStats(userID, True)) # Channel info end (before starting!?! wtf bancho?) responseToken.enqueue(serverPackets.channelInfoEnd()) # Default opened channels # TODO: Configurable default channels chat.joinChannel(token=responseToken, channel="#osu") chat.joinChannel(token=responseToken, channel="#announce") #Akatsuki extra channels chat.joinChannel(token=responseToken, channel="#nowranked") chat.joinChannel(token=responseToken, channel="#request") # Join admin channel if we are an admin if responseToken.admin or responseToken.privileges & privileges.USER_PREMIUM: chat.joinChannel(token=responseToken, channel="#admin") # Output channels info for key, value in glob.channels.channels.items(): if value.publicRead and not value.hidden: responseToken.enqueue(serverPackets.channelInfo(key)) # Send friends list responseToken.enqueue(serverPackets.friendList(userID)) # Send main menu icon if glob.banchoConf.config["menuIcon"] != "": responseToken.enqueue(serverPackets.mainMenuIcon(glob.banchoConf.config["menuIcon"])) # Send online users' panels with glob.tokens: for _, token in glob.tokens.tokens.items(): if not token.restricted: responseToken.enqueue(serverPackets.userPanel(token.userID)) # Get location and country from ip.zxq.co or database. If the user is a donor, then yee if glob.localize and (firstLogin == True or responseToken.privileges & privileges.USER_DONOR <= 0): # Get location and country from IP latitude, longitude = locationHelper.getLocation(requestIP) countryLetters = locationHelper.getCountry(requestIP) country = countryHelper.getCountryID(countryLetters) else: # Set location to 0,0 and get country from db log.warning("Location skipped") latitude = 0 longitude = 0 countryLetters = "XX" country = countryHelper.getCountryID(userUtils.getCountry(userID)) # Set location and country responseToken.setLocation(latitude, longitude) responseToken.country = country # Set country in db if user has no country (first bancho login) if userUtils.getCountry(userID) == "XX": userUtils.setCountry(userID, countryLetters) # Send to everyone our userpanel if we are not restricted or tournament if not responseToken.restricted: glob.streams.broadcast("main", serverPackets.userPanel(userID)) # Set reponse data to right value and reset our queue responseData = responseToken.queue responseToken.resetQueue() except exceptions.loginFailedException: # Login failed error packet # (we don't use enqueue because we don't have a token since login has failed) responseData += serverPackets.loginFailed() except exceptions.invalidArgumentsException: # Invalid POST data # (we don't use enqueue because we don't have a token since login has failed) responseData += serverPackets.loginFailed() responseData += serverPackets.notification("We see what you're doing..") log.cmyui("User {} has triggered invalidArgumentsException in loginEvent.py".format(userID), discord="cm") except exceptions.loginBannedException: # Login banned error packet responseData += serverPackets.loginBanned() except exceptions.loginLockedException: # Login banned error packet responseData += serverPackets.loginLocked() except exceptions.banchoMaintenanceException: # Bancho is in maintenance mode responseData = bytes() if responseToken is not None: responseData = responseToken.queue responseData += serverPackets.notification("Akatsuki is currently in maintenance mode. Please try to login again later.") responseData += serverPackets.loginFailed() except exceptions.banchoRestartingException: # Bancho is restarting responseData += serverPackets.notification("Akatsuki is restarting. Try again in a few minutes.") responseData += serverPackets.loginFailed() except exceptions.need2FAException: # User tried to log in from unknown IP responseData += serverPackets.needVerification() except exceptions.haxException: # Using oldoldold client, we don't have client data. Force update. # (we don't use enqueue because we don't have a token since login has failed) responseData += serverPackets.forceUpdate() responseData += serverPackets.notification("Custom clients of ANY kind are NOT PERMITTED on Akatsuki. Please login using the current osu! client.") log.cmyui("User {} has logged in with a VERY old client".format(userID), discord="cm") except: log.error("Unknown error!\n```\n{}\n{}```".format(sys.exc_info(), traceback.format_exc())) finally: # Console and discord log if len(loginData) < 3: log.info("Invalid bancho login request from **{}** (insufficient POST data)".format(requestIP), "bunker") # Return token string and data return responseTokenString, responseData
def handle(tornadoRequest): # Data to return responseToken = None responseTokenString = "ayy" responseData = bytes() # Get IP from tornado request requestIP = tornadoRequest.getRequestIP() # Avoid exceptions clientData = ["unknown", "unknown", "unknown", "unknown", "unknown"] osuVersion = "unknown" # Split POST body so we can get username/password/hardware data # 2:-3 thing is because requestData has some escape stuff that we don't need loginData = str(tornadoRequest.request.body)[2:-3].split("\\n") try: # Make sure loginData is valid if len(loginData) < 3: raise exceptions.invalidArgumentsException() # Get HWID, MAC address and more # Structure (new line = "|", already split) # [0] osu! version # [1] plain mac addressed, separated by "." # [2] mac addresses hash set # [3] unique ID # [4] disk ID splitData = loginData[2].split("|") osuVersion = splitData[0] timeOffset = int(splitData[1]) clientData = splitData[3].split(":")[:5] if len(clientData) < 4: raise exceptions.forceUpdateException() # Try to get the ID from username username = str(loginData[0]) userID = userUtils.getID(username) if not userID: # Invalid username raise exceptions.loginFailedException() if not userUtils.checkLogin(userID, loginData[1]): # Invalid password raise exceptions.loginFailedException() # Make sure we are not banned or locked priv = userUtils.getPrivileges(userID) if userUtils.isBanned( userID) and priv & privileges.USER_PENDING_VERIFICATION == 0: raise exceptions.loginBannedException() if userUtils.isLocked( userID) and priv & privileges.USER_PENDING_VERIFICATION == 0: raise exceptions.loginLockedException() # 2FA check if userUtils.check2FA(userID, requestIP): log.warning("Need 2FA check for user {}".format(loginData[0])) raise exceptions.need2FAException() # No login errors! # Check if a users login/logouts are being tracked. If so, log to discord tracked = userUtils.getUserTracked(userID) if tracked: log.cmyui( 'Tracked user {} ({}) has logged in.'.format(username, userID), 'cm') # Verify this user (if pending activation) firstLogin = False if priv & privileges.USER_PENDING_VERIFICATION > 0 or not userUtils.hasVerifiedHardware( userID): if userUtils.verifyUser(userID, clientData): # Valid account log.info("Account {} verified successfully!".format(userID)) glob.verifiedCache[str(userID)] = 1 firstLogin = True else: # Multiaccount detected log.info("Account {} NOT verified!".format(userID)) glob.verifiedCache[str(userID)] = 0 raise exceptions.loginBannedException() # Save HWID in db for multiaccount detection hwAllowed = userUtils.logHardware(userID, clientData, firstLogin) # This is false only if HWID is empty # if HWID is banned, we get restricted so there's no # need to deny bancho access if not hwAllowed: raise exceptions.haxException() # Log user IP userUtils.logIP(userID, requestIP) # Delete old tokens for that user and generate a new one isTournament = "tourney" in osuVersion if not isTournament: glob.tokens.deleteOldTokens(userID) responseToken = glob.tokens.addToken(userID, requestIP, timeOffset=timeOffset, tournament=isTournament) responseTokenString = responseToken.token # Check restricted mode (and eventually send message) responseToken.checkRestricted() # Send message if donor expires soon if responseToken.privileges & privileges.USER_DONOR > 0: expireDate = userUtils.getDonorExpire(responseToken.userID) if expireDate - int(time.time()) <= 86400 * 3: expireDays = round((expireDate - int(time.time())) / 86400) expireIn = "{} days".format( expireDays) if expireDays > 1 else "less than 24 hours" responseToken.enqueue( serverPackets.notification( "Your Donator tag expires in {}!".format(expireIn))) # Check if a users login/logouts are being tracked tracked = userUtils.getUserTracked(userID) if tracked: log.cmyui('Tracked user {} ({}) has logged in.'.format( username, userID)) # Set silence end UNIX time in token responseToken.silenceEndTime = userUtils.getSilenceEnd(userID) # Get only silence remaining seconds silenceSeconds = responseToken.getSilenceSecondsLeft() # Get supporter/GMT userGMT = False userSupporter = True userTournament = False if responseToken.admin: userGMT = True if responseToken.privileges & privileges.USER_TOURNAMENT_STAFF > 0: userTournament = True # Server restarting check if glob.restarting: raise exceptions.banchoRestartingException() # Send login notification before maintenance message if glob.banchoConf.config["loginNotification"] != "": responseToken.enqueue( serverPackets.notification( glob.banchoConf.config["loginNotification"])) # Maintenance check if glob.banchoConf.config["banchoMaintenance"]: if not userGMT: # We are not mod/admin, delete token, send notification and logout glob.tokens.deleteToken(responseTokenString) raise exceptions.banchoMaintenanceException() else: # We are mod/admin, send warning notification and continue responseToken.enqueue( serverPackets.notification( "Vipsu is in maintenance mode. Only mods/admins have full access to the server.\nType !system maintenance off in chat to turn off maintenance mode." )) # Send all needed login packets responseToken.enqueue(serverPackets.silenceEndTime(silenceSeconds)) responseToken.enqueue(serverPackets.userID(userID)) responseToken.enqueue(serverPackets.protocolVersion()) responseToken.enqueue( serverPackets.userSupporterGMT(userSupporter, userGMT, userTournament)) responseToken.enqueue(serverPackets.userPanel(userID, True)) responseToken.enqueue(serverPackets.userStats(userID, True)) # Channel info end (before starting!?! wtf bancho?) responseToken.enqueue(serverPackets.channelInfoEnd()) # Default opened channels # TODO: Configurable default channels chat.joinChannel(token=responseToken, channel="#osu") chat.joinChannel(token=responseToken, channel="#announce") chat.joinChannel(token=responseToken, channel="#nowranked") chat.joinChannel(token=responseToken, channel="#request") # Join admin channel if we are an admin if responseToken.admin: chat.joinChannel(token=responseToken, channel="#admin") clan = glob.db.fetch("SELECT clan FROM user_clans WHERE user = %s", [userID]) if clan is not None: chat.joinChannel(token=responseToken, channel="#clan_{}".format(clan["clan"])) # Output channels info for key, value in glob.channels.channels.items(): if value.publicRead and not value.hidden: responseToken.enqueue(serverPackets.channelInfo(key)) # Send friends list responseToken.enqueue(serverPackets.friendList(userID)) # Send main menu icon #if glob.banchoConf.config["menuIcon"] != "": #responseToken.enqueue(serverPackets.mainMenuIcon(glob.banchoConf.config["menuIcon"])) # Send online users' panels with glob.tokens: for _, token in glob.tokens.tokens.items(): if not token.restricted: responseToken.enqueue(serverPackets.userPanel( token.userID)) # Get location and country from ip.zxq.co or database if glob.localize: # Set location to 0,0 and get country from db log.warning("Location skipped") latitude = 0 longitude = 0 countryLetters = "XX" country = countryHelper.getCountryID(userUtils.getCountry(userID)) else: # Get location and country from IP latitude, longitude = locationHelper.getLocation(requestIP) countryLetters = locationHelper.getCountry(requestIP) country = countryHelper.getCountryID(countryLetters) # Set location and country responseToken.setLocation(latitude, longitude) responseToken.country = country # Set country in db if user has no country (first bancho login) if userUtils.getCountry(userID) == "XX": userUtils.setCountry(userID, countryLetters) # Send to everyone our userpanel if we are not restricted or tournament if not responseToken.restricted: glob.streams.broadcast("main", serverPackets.userPanel(userID)) # Set reponse data to right value and reset our queue responseData = responseToken.queue responseToken.resetQueue() except exceptions.loginFailedException: # Login failed error packet # (we don't use enqueue because we don't have a token since login has failed) responseData += serverPackets.loginFailed() except exceptions.invalidArgumentsException: # Invalid POST data # (we don't use enqueue because we don't have a token since login has failed) responseData += serverPackets.loginFailed() responseData += serverPackets.notification( "I see what you're doing...") except exceptions.loginBannedException: # Login banned error packet responseData += serverPackets.loginBanned() except exceptions.loginLockedException: # Login banned error packet responseData += serverPackets.loginLocked() except exceptions.banchoMaintenanceException: # Bancho is in maintenance mode responseData = bytes() if responseToken is not None: responseData = responseToken.queue responseData += serverPackets.notification( "Our Vipsu server is in maintenance mode. Please try to login again later." ) responseData += serverPackets.loginFailed() except exceptions.banchoRestartingException: # Bancho is restarting responseData += serverPackets.notification( "Vipsu is restarting. Try again in a few minutes.") responseData += serverPackets.loginFailed() except exceptions.need2FAException: # User tried to log in from unknown IP responseData += serverPackets.needVerification() except exceptions.haxException: # Using oldoldold client, we don't have client data. Force update. # (we don't use enqueue because we don't have a token since login has failed) responseData += serverPackets.forceUpdate() responseData += serverPackets.notification( "Hory shitto, your client is TOO old! Nice prehistory! Please turn update it from the settings!" ) except: log.error("Unknown error!\n```\n{}\n{}```".format( sys.exc_info(), traceback.format_exc())) finally: # Console and discord log if len(loginData) < 3: log.info( "Invalid bancho login request from **{}** (insufficient POST data)" .format(requestIP), "bunker") # Return token string and data return responseTokenString, responseData