pep.py/events/loginEvent.py

268 lines
10 KiB
Python
Raw Normal View History

import sys
2016-10-02 20:48:14 +00:00
import time
import traceback
2016-10-02 20:48:14 +00:00
from common.constants import privileges
from common.log import logUtils as log
from common.ripple import userUtils
from constants import exceptions
from constants import serverPackets
from helpers import chatHelper as chat
2016-10-02 20:48:14 +00:00
from helpers import countryHelper
from helpers import locationHelper
from objects import glob
2016-04-19 17:40:59 +00:00
def handle(tornadoRequest):
2016-04-19 17:40:59 +00:00
# Data to return
responseTokenString = "ayy"
responseData = bytes()
# Get IP from tornado request
requestIP = tornadoRequest.getRequestIP()
2016-04-19 17:40:59 +00:00
# Avoid exceptions
clientData = ["unknown", "unknown", "unknown", "unknown", "unknown"]
osuVersion = "unknown"
2016-04-19 17:40:59 +00:00
# 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")
2016-04-19 17:40:59 +00:00
try:
# If true, print error to console
err = False
# 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]
2016-08-01 18:38:26 +00:00
timeOffset = int(splitData[1])
clientData = splitData[3].split(":")[:5]
if len(clientData) < 4:
raise exceptions.forceUpdateException()
2016-04-19 17:40:59 +00:00
# Try to get the ID from username
username = str(loginData[0])
2016-10-02 20:48:14 +00:00
userID = userUtils.getID(username)
2016-04-19 17:40:59 +00:00
2016-09-02 15:45:10 +00:00
if not userID:
2016-04-19 17:40:59 +00:00
# Invalid username
raise exceptions.loginFailedException()
2016-10-02 20:48:14 +00:00
if not userUtils.checkLogin(userID, loginData[1]):
2016-04-19 17:40:59 +00:00
# Invalid password
raise exceptions.loginFailedException()
# Make sure we are not banned or locked
2016-10-02 20:48:14 +00:00
priv = userUtils.getPrivileges(userID)
if userUtils.isBanned(userID) == True and priv & privileges.USER_PENDING_VERIFICATION == 0:
2016-04-19 17:40:59 +00:00
raise exceptions.loginBannedException()
2016-10-02 20:48:14 +00:00
if userUtils.isLocked(userID) == True and priv & privileges.USER_PENDING_VERIFICATION == 0:
raise exceptions.loginLockedException()
2016-04-19 17:40:59 +00:00
2016-06-11 16:43:27 +00:00
# 2FA check
2016-10-02 20:48:14 +00:00
if userUtils.check2FA(userID, requestIP):
2016-06-11 16:43:27 +00:00
log.warning("Need 2FA check for user {}".format(loginData[0]))
raise exceptions.need2FAException()
2016-06-10 11:15:42 +00:00
# No login errors!
# Verify this user (if pending activation)
firstLogin = False
2016-10-02 20:48:14 +00:00
if priv & privileges.USER_PENDING_VERIFICATION > 0 or userUtils.hasVerifiedHardware(userID) == False:
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
2016-10-02 20:48:14 +00:00
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
2016-09-02 15:45:10 +00:00
if not hwAllowed:
raise exceptions.haxException()
# Log user IP
2016-10-02 20:48:14 +00:00
userUtils.logIP(userID, requestIP)
2016-04-19 17:40:59 +00:00
# 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)
2016-04-19 17:40:59 +00:00
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:
2016-10-02 20:48:14 +00:00
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 donor tag expires in {}! When your donor tag expires, you won't have any of the donor privileges, like yellow username, custom badge and discord custom role and username color! If you wish to keep supporting Ripple and you don't want to lose your donor privileges, you can donate again by clicking on 'Support us' on Ripple's website.".format(expireIn)))
# Set silence end UNIX time in token
2016-10-02 20:48:14 +00:00
responseToken.silenceEndTime = userUtils.getSilenceEnd(userID)
# Get only silence remaining seconds
silenceSeconds = responseToken.getSilenceSecondsLeft()
2016-04-19 17:40:59 +00:00
# Get supporter/GMT
userGMT = False
userSupporter = True
userTournament = False
2016-09-02 15:45:10 +00:00
if responseToken.admin:
2016-04-19 17:40:59 +00:00
userGMT = True
if responseToken.privileges & privileges.USER_TOURNAMENT_STAFF > 0:
userTournament = True
2016-04-19 17:40:59 +00:00
# Server restarting check
2016-09-02 15:45:10 +00:00
if glob.restarting:
2016-04-19 17:40:59 +00:00
raise exceptions.banchoRestartingException()
# Send login notification before maintenance message
if glob.banchoConf.config["loginNotification"] != "":
responseToken.enqueue(serverPackets.notification(glob.banchoConf.config["loginNotification"]))
2016-04-19 17:40:59 +00:00
# Maintenance check
2016-09-02 15:45:10 +00:00
if glob.banchoConf.config["banchoMaintenance"]:
if not userGMT:
2016-04-19 17:40:59 +00:00
# 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("Bancho 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))
2016-04-19 17:40:59 +00:00
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))
2016-04-19 17:40:59 +00:00
# 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")
# Join admin channel if we are an admin
2016-09-02 15:45:10 +00:00
if responseToken.admin:
chat.joinChannel(token=responseToken, channel="#admin")
2016-04-19 17:40:59 +00:00
# Output channels info
for key, value in glob.channels.channels.items():
2016-07-15 09:46:44 +00:00
if value.publicRead == True and value.hidden == False:
2016-04-19 17:40:59 +00:00
responseToken.enqueue(serverPackets.channelInfo(key))
# Send friends list
2016-04-19 17:40:59 +00:00
responseToken.enqueue(serverPackets.friendList(userID))
# Send main menu icon
2016-04-19 17:40:59 +00:00
if glob.banchoConf.config["menuIcon"] != "":
responseToken.enqueue(serverPackets.mainMenuIcon(glob.banchoConf.config["menuIcon"]))
# Send online users IDs array
responseToken.enqueue(serverPackets.onlineUsers())
# Get location and country from ip.zxq.co or database
2016-09-02 15:45:10 +00:00
if glob.localize:
# Get location and country from IP
latitude, longitude = locationHelper.getLocation(requestIP)
countryLetters = locationHelper.getCountry(requestIP)
country = countryHelper.getCountryID(countryLetters)
2016-04-19 17:40:59 +00:00
else:
# Set location to 0,0 and get country from db
log.warning("Location skipped")
latitude = 0
longitude = 0
countryLetters = "XX"
2016-10-02 20:48:14 +00:00
country = countryHelper.getCountryID(userUtils.getCountry(userID))
2016-04-19 17:40:59 +00:00
# Set location and country
responseToken.setLocation(latitude, longitude)
responseToken.country = country
2016-04-19 17:40:59 +00:00
2016-05-01 16:09:35 +00:00
# Set country in db if user has no country (first bancho login)
2016-10-02 20:48:14 +00:00
if userUtils.getCountry(userID) == "XX":
userUtils.setCountry(userID, countryLetters)
# Send to everyone our userpanel if we are not restricted or tournament
2016-09-02 15:45:10 +00:00
if not responseToken.restricted:
glob.streams.broadcast("main", serverPackets.userPanel(userID))
2016-04-19 17:40:59 +00:00
# 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)
err = True
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)
err = True
responseData += serverPackets.loginFailed()
responseData += serverPackets.notification("I see what you're doing...")
2016-04-19 17:40:59 +00:00
except exceptions.loginBannedException:
# Login banned error packet
err = True
responseData += serverPackets.loginBanned()
except exceptions.loginLockedException:
# Login banned error packet
err = True
responseData += serverPackets.loginLocked()
2016-04-19 17:40:59 +00:00
except exceptions.banchoMaintenanceException:
# Bancho is in maintenance mode
responseData = responseToken.queue
2016-04-19 17:40:59 +00:00
responseData += serverPackets.notification("Our bancho server is in maintenance mode. Please try to login again later.")
2016-06-11 16:43:27 +00:00
responseData += serverPackets.loginFailed()
2016-04-19 17:40:59 +00:00
except exceptions.banchoRestartingException:
# Bancho is restarting
responseData += serverPackets.notification("Bancho is restarting. Try again in a few minutes.")
2016-06-11 16:43:27 +00:00
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)
err = True
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()))
2016-04-19 17:40:59 +00:00
finally:
# Console and discord log
if len(loginData) < 3:
msg = "Invalid bancho login request from **{}** (insufficient POST data)".format(requestIP)
else:
msg = "Bancho login request from **{}** for user **{}** ({})".format(requestIP, loginData[0], "failed" if err == True else "success")
log.info(msg, "bunker")
2016-06-02 17:22:02 +00:00
# Return token string and data
2016-09-02 15:45:10 +00:00
return responseTokenString, responseData