.BANCHO. Merge branch tornado-gevent into master

This commit is contained in:
Nyo 2016-08-23 20:35:47 +02:00
commit de47c46d25
12 changed files with 506 additions and 378 deletions

View File

@ -10,14 +10,14 @@ This is Ripple's bancho server. It handles:
## Requirements
- Python 3.5
- MySQLdb (`mysqlclient` or `mysql-python`)
- Bottle
- Tornado
- Gevent
- Bcrypt
## How to set up pep.py
First of all, install all the dependencies
```
$ pip install mysqlclient bottle gevent bcrypt
$ pip install mysqlclient tornado gevent bcrypt
```
then, run pep.py once to create the default config file and edit it
```

View File

@ -586,7 +586,6 @@ def tillerinoLast(fro, chan, message):
stars = oppaiData["stars"]
msg += " | {0:.2f} stars".format(stars)
return msg
except Exception as a:
log.error(a)

View File

@ -2,22 +2,27 @@ from helpers import userHelper
from constants import serverPackets
from constants import exceptions
from objects import glob
from helpers import consoleHelper
from constants import bcolors
from helpers import locationHelper
from helpers import countryHelper
import time
from helpers import generalFunctions
import sys
import traceback
from helpers import requestHelper
from helpers import discordBotHelper
from helpers import logHelper as log
from helpers import chatHelper as chat
from constants import privileges
from helpers import requestHelper
def handle(bottleRequest):
def handle(tornadoRequest):
# Data to return
responseTokenString = "ayy"
responseData = bytes()
# Get IP from tornado request
requestIP = requestHelper.getRequestIP(bottleRequest)
requestIP = tornadoRequest.getRequestIP()
# Avoid exceptions
clientData = ["unknown", "unknown", "unknown", "unknown", "unknown"]
@ -25,8 +30,7 @@ def handle(bottleRequest):
# 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
postBody = bottleRequest.body.read()
loginData = str(postBody)[2:-3].split("\\n")
loginData = str(tornadoRequest.request.body)[2:-3].split("\\n")
try:
# If true, print error to console
err = False
@ -244,4 +248,4 @@ def handle(bottleRequest):
log.info(msg, "bunker")
# Return token string and data
return (responseTokenString, responseData)
return (responseTokenString, responseData)

View File

@ -1,37 +1,39 @@
from helpers import requestHelper
from constants import exceptions
import json
from objects import glob
from helpers import chatHelper
import bottle
from helpers import logHelper as log
@bottle.route("/api/v1/fokabotMessage")
def GETApiFokabotMessage():
statusCode = 400
data = {"message": "unknown error"}
try:
# Check arguments
if "k" not in bottle.request.query or "to" not in bottle.request.query or "msg" not in bottle.request.query:
raise exceptions.invalidArgumentsException()
# Check ci key
key = bottle.request.query["k"]
if key is None or key != glob.conf.config["server"]["cikey"]:
raise exceptions.invalidArgumentsException()
# Send chat message
chatHelper.sendMessage("FokaBot", bottle.request.query["to"], bottle.request.query["msg"])
# Status code and message
statusCode = 200
data["message"] = "ok"
except exceptions.invalidArgumentsException:
class handler(requestHelper.asyncRequestHandler):
def asyncGet(self):
statusCode = 400
data["message"] = "invalid parameters"
finally:
# Add status code to data
data["status"] = statusCode
data = {"message": "unknown error"}
try:
# Check arguments
if requestHelper.checkArguments(self.request.arguments, ["k", "to", "msg"]) == False:
raise exceptions.invalidArgumentsException()
# Send response
bottle.response.status = statusCode
bottle.response.add_header("Content-Type", "application/json")
yield json.dumps(data)
# Check ci key
key = self.get_argument("k")
if key is None or key != glob.conf.config["server"]["cikey"]:
raise exceptions.invalidArgumentsException()
log.info("API REQUEST FOR FOKABOT MESSAGE AAAAAAA")
chatHelper.sendMessage("FokaBot", self.get_argument("to"), self.get_argument("msg"))
# Status code and message
statusCode = 200
data["message"] = "ok"
except exceptions.invalidArgumentsException:
statusCode = 400
data["message"] = "invalid parameters"
finally:
# Add status code to data
data["status"] = statusCode
# Send response
#self.clear()
self.write(json.dumps(data))
self.set_status(statusCode)
#self.finish(json.dumps(data))

View File

@ -1,39 +1,48 @@
from helpers import requestHelper
from constants import exceptions
import json
from objects import glob
import bottle
@bottle.route("/api/v1/isOnline")
def GETApiIsOnline():
statusCode = 400
data = {"message": "unknown error"}
try:
# Check arguments
if "u" not in bottle.request.query and "id" not in bottle.request.query:
raise exceptions.invalidArgumentsException()
# Get online staus
if "u" in bottle.request.query:
username = bottle.request.query["u"]
data["result"] = True if glob.tokens.getTokenFromUsername(username) != None else False
else:
try:
userID = int(bottle.request.query["id"])
data["result"] = True if glob.tokens.getTokenFromUserID(userID) != None else False
except:
class handler(requestHelper.asyncRequestHandler):
def asyncGet(self):
statusCode = 400
data = {"message": "unknown error"}
try:
# Check arguments
if "u" not in self.request.arguments and "id" not in self.request.arguments:
raise exceptions.invalidArgumentsException()
# Status code and message
statusCode = 200
data["message"] = "ok"
except exceptions.invalidArgumentsException:
statusCode = 400
data["message"] = "missing required arguments"
finally:
# Add status code to data
data["status"] = statusCode
# Get online staus
username = None
userID = None
if "u" in self.request.arguments:
username = self.get_argument("u")
else:
try:
userID = int(self.get_argument("id"))
except:
raise exceptions.invalidArgumentsException()
# Send response
bottle.response.status = statusCode
bottle.response.add_header("Content-Type", "application/json")
yield json.dumps(data)
if username == None and userID == None:
data["result"] = False
else:
if username != None:
data["result"] = True if glob.tokens.getTokenFromUsername(username) != None else False
else:
data["result"] = True if glob.tokens.getTokenFromUserID(userID) != None else False
# Status code and message
statusCode = 200
data["message"] = "ok"
except exceptions.invalidArgumentsException:
statusCode = 400
data["message"] = "missing required arguments"
finally:
# Add status code to data
data["status"] = statusCode
# Send response
#self.clear()
self.write(json.dumps(data))
self.set_status(statusCode)
#self.finish(json.dumps(data))

View File

@ -1,23 +1,24 @@
from helpers import requestHelper
import json
from objects import glob
import bottle
@bottle.route("/api/v1/onlineUsers")
def GETApiOnlineUsers():
statusCode = 400
data = {"message": "unknown error"}
try:
# Get online users count
data["result"] = len(glob.tokens.tokens)
class handler(requestHelper.asyncRequestHandler):
def asyncGet(self):
statusCode = 400
data = {"message": "unknown error"}
try:
# Get online users count
data["result"] = len(glob.tokens.tokens)
# Status code and message
statusCode = 200
data["message"] = "ok"
finally:
# Add status code to data
data["status"] = statusCode
# Status code and message
statusCode = 200
data["message"] = "ok"
finally:
# Add status code to data
data["status"] = statusCode
# Send response
bottle.response.status = statusCode
bottle.response.add_header("Content-Type", "application/json")
yield json.dumps(data)
# Send response
#self.clear()
self.write(json.dumps(data))
self.set_status(statusCode)
#self.finish(json.dumps(data))

View File

@ -1,23 +1,24 @@
from helpers import requestHelper
import json
from objects import glob
import bottle
@bottle.route("/api/v1/serverStatus")
def GETApiServerStatus():
statusCode = 400
data = {"message": "unknown error"}
try:
# Get online users count
data["result"] = -1 if glob.restarting == True else 1
class handler(requestHelper.asyncRequestHandler):
def asyncGet(self):
statusCode = 400
data = {"message": "unknown error"}
try:
# Get online users count
data["result"] = -1 if glob.restarting == True else 1
# Status code and message
statusCode = 200
data["message"] = "ok"
finally:
# Add status code to data
data["status"] = statusCode
# Status code and message
statusCode = 200
data["message"] = "ok"
finally:
# Add status code to data
data["status"] = statusCode
# Send response
bottle.response.status = statusCode
bottle.response.add_header("Content-Type", "application/json")
yield json.dumps(data)
# Send response
#self.clear()
self.write(json.dumps(data))
self.set_status(statusCode)
#self.finish(json.dumps(data))

View File

@ -1,48 +1,46 @@
from helpers import requestHelper
from helpers import logHelper as log
import json
from objects import glob
from constants import exceptions
import bottle
@bottle.route("/api/v1/verifiedStatus")
def GETApiVerifiedStatus():
statusCode = 400
data = {"message": "unknown error"}
try:
# Check arguments
if "u" not in bottle.request.query:
raise exceptions.invalidArgumentsException()
# Get userID and its verified cache thing
# -1: Not in cache
# 0: Not verified (multiacc)
# 1: Verified
userID = bottle.request.query["u"]
callback = None
if "callback" in bottle.request.query:
callback = bottle.request.query["callback"]
data["result"] = -1 if userID not in glob.verifiedCache else glob.verifiedCache[userID]
# Status code and message
statusCode = 200
data["message"] = "ok"
except exceptions.invalidArgumentsException:
class handler(requestHelper.asyncRequestHandler):
def asyncGet(self):
statusCode = 400
data["message"] = "missing required arguments"
finally:
# Add status code to data
data["status"] = statusCode
data = {"message": "unknown error"}
try:
# Check arguments
if requestHelper.checkArguments(self.request.arguments, ["u"]) == False:
raise exceptions.invalidArgumentsException()
# Send response
bottle.response.add_header("Access-Control-Allow-Origin", "*")
bottle.response.add_header("Content-Type", "application/json")
# Get userID and its verified cache thing
# -1: Not in cache
# 0: Not verified (multiacc)
# 1: Verified
userID = self.get_argument("u")
data["result"] = -1 if userID not in glob.verifiedCache else glob.verifiedCache[userID]
# jquery meme
output = ""
if callback != None:
output += callback+"("
output += json.dumps(data)
if callback != None:
output += ")"
# Status code and message
statusCode = 200
data["message"] = "ok"
except exceptions.invalidArgumentsException:
statusCode = 400
data["message"] = "missing required arguments"
finally:
# Add status code to data
data["status"] = statusCode
bottle.response.status = statusCode
yield output
# Send response
self.add_header("Access-Control-Allow-Origin", "*")
self.add_header("Content-Type", "application/json")
# jquery meme
output = ""
if "callback" in self.request.arguments:
output += self.get_argument("callback")+"("
output += json.dumps(data)
if "callback" in self.request.arguments:
output += ")"
self.write(output)
self.set_status(statusCode)

View File

@ -1,38 +1,39 @@
from helpers import requestHelper
from constants import exceptions
import json
from objects import glob
from helpers import systemHelper
from helpers import logHelper as log
import bottle
@bottle.route("/api/v1/ciTrigger")
def GETCiTrigger():
statusCode = 400
data = {"message": "unknown error"}
try:
# Check arguments
if "k" not in bottle.request.query:
raise exceptions.invalidArgumentsException()
class handler(requestHelper.asyncRequestHandler):
def asyncGet(self):
statusCode = 400
data = {"message": "unknown error"}
try:
# Check arguments
if requestHelper.checkArguments(self.request.arguments, ["k"]) == False:
raise exceptions.invalidArgumentsException()
# Check ci key
key = bottle.request.query["k"]
if key != glob.conf.config["server"]["cikey"]:
raise exceptions.invalidArgumentsException()
# Check ci key
key = self.get_argument("k")
if key is None or key != glob.conf.config["server"]["cikey"]:
raise exceptions.invalidArgumentsException()
log.info("Ci event triggered!!")
systemHelper.scheduleShutdown(5, False, "A new Bancho update is available and the server will be restarted in 5 seconds. Thank you for your patience.")
log.info("Ci event triggered!!")
systemHelper.scheduleShutdown(5, False, "A new Bancho update is available and the server will be restarted in 5 seconds. Thank you for your patience.")
# Status code and message
statusCode = 200
data["message"] = "ok"
except exceptions.invalidArgumentsException:
statusCode = 403
data["message"] = "invalid ci key"
finally:
# Add status code to data
data["status"] = statusCode
# Status code and message
statusCode = 200
data["message"] = "ok"
except exceptions.invalidArgumentsException:
statusCode = 400
data["message"] = "invalid ci key"
finally:
# Add status code to data
data["status"] = statusCode
# Send response
bottle.response.status = statusCode
bottle.response.add_header("Content-Type", "application/json")
yield json.dumps(data)
# Send response
#self.clear()
self.write(json.dumps(data))
self.set_status(statusCode)
#self.finish(json.dumps(data))

View File

@ -1,11 +1,10 @@
import bottle
import datetime
import gzip
from helpers import requestHelper
from objects import glob
from helpers import packetHelper
from helpers import logHelper as log
from constants import exceptions
from constants import packetIDs
from helpers import packetHelper
from constants import serverPackets
from events import sendPublicMessageEvent
from events import sendPrivateMessageEvent
@ -47,199 +46,219 @@ from events import userStatsRequestEvent
from events import requestStatusUpdateEvent
from events import userPanelRequestEvent
@bottle.route("/", method="POST")
def POSTMain():
# Track time if needed
if glob.outputRequestTime == True:
# Start time
st = datetime.datetime.now()
# Exception tracking
import tornado.web
import tornado.gen
import sys
import traceback
from raven.contrib.tornado import SentryMixin
from helpers import logHelper as log
# Client's token string and request data
#requestTokenString = bottle.request.headers.get("osu-token")
requestTokenString = bottle.request.get_header("osu-token")
requestData = bottle.request.body.read()
# Server's token string and request data
responseTokenString = "ayy"
responseData = bytes()
if requestTokenString == None:
# No token, first request. Handle login.
responseTokenString, responseData = loginEvent.handle(bottle.request)
else:
userToken = None # default value
class handler(SentryMixin, requestHelper.asyncRequestHandler):
@tornado.web.asynchronous
@tornado.gen.engine
def asyncPost(self):
try:
# This is not the first packet, send response based on client's request
# Packet start position, used to read stacked packets
pos = 0
# Track time if needed
if glob.outputRequestTime == True:
# Start time
st = datetime.datetime.now()
# Make sure the token exists
if requestTokenString not in glob.tokens.tokens:
raise exceptions.tokenNotFoundException()
# Client's token string and request data
requestTokenString = self.request.headers.get("osu-token")
requestData = self.request.body
# Token exists, get its object and lock it
userToken = glob.tokens.tokens[requestTokenString]
userToken.lock.acquire()
# Server's token string and request data
responseTokenString = "ayy"
responseData = bytes()
# Keep reading packets until everything has been read
while pos < len(requestData):
# Get packet from stack starting from new packet
leftData = requestData[pos:]
if requestTokenString == None:
# No token, first request. Handle login.
responseTokenString, responseData = loginEvent.handle(self)
else:
userToken = None # default value
try:
# This is not the first packet, send response based on client's request
# Packet start position, used to read stacked packets
pos = 0
# Get packet ID, data length and data
packetID = packetHelper.readPacketID(leftData)
dataLength = packetHelper.readPacketLength(leftData)
packetData = requestData[pos:(pos+dataLength+7)]
# Make sure the token exists
if requestTokenString not in glob.tokens.tokens:
raise exceptions.tokenNotFoundException()
# Console output if needed
if glob.outputPackets == True and packetID != 4:
log.debug("Incoming packet ({})({}):\n\nPacket code: {}\nPacket length: {}\nSingle packet data: {}\n".format(requestTokenString, userToken.username, str(packetID), str(dataLength), str(packetData)))
# Token exists, get its object and lock it
userToken = glob.tokens.tokens[requestTokenString]
userToken.lock.acquire()
# Event handler
def handleEvent(ev):
def wrapper():
ev.handle(userToken, packetData)
return wrapper
# Keep reading packets until everything has been read
while pos < len(requestData):
# Get packet from stack starting from new packet
leftData = requestData[pos:]
eventHandler = {
# TODO: Rename packets and events
# TODO: Host check for multi
packetIDs.client_changeAction: handleEvent(changeActionEvent),
packetIDs.client_logout: handleEvent(logoutEvent),
packetIDs.client_friendAdd: handleEvent(friendAddEvent),
packetIDs.client_friendRemove: handleEvent(friendRemoveEvent),
packetIDs.client_userStatsRequest: handleEvent(userStatsRequestEvent),
packetIDs.client_requestStatusUpdate: handleEvent(requestStatusUpdateEvent),
packetIDs.client_userPanelRequest: handleEvent(userPanelRequestEvent),
packetIDs.client_channelJoin: handleEvent(channelJoinEvent),
packetIDs.client_channelPart: handleEvent(channelPartEvent),
packetIDs.client_sendPublicMessage: handleEvent(sendPublicMessageEvent),
packetIDs.client_sendPrivateMessage: handleEvent(sendPrivateMessageEvent),
packetIDs.client_setAwayMessage: handleEvent(setAwayMessageEvent),
# Get packet ID, data length and data
packetID = packetHelper.readPacketID(leftData)
dataLength = packetHelper.readPacketLength(leftData)
packetData = requestData[pos:(pos+dataLength+7)]
packetIDs.client_startSpectating: handleEvent(startSpectatingEvent),
packetIDs.client_stopSpectating: handleEvent(stopSpectatingEvent),
packetIDs.client_cantSpectate: handleEvent(cantSpectateEvent),
packetIDs.client_spectateFrames: handleEvent(spectateFramesEvent),
# Console output if needed
if glob.outputPackets == True and packetID != 4:
log.debug("Incoming packet ({})({}):\n\nPacket code: {}\nPacket length: {}\nSingle packet data: {}\n".format(requestTokenString, userToken.username, str(packetID), str(dataLength), str(packetData)))
packetIDs.client_joinLobby: handleEvent(joinLobbyEvent),
packetIDs.client_partLobby: handleEvent(partLobbyEvent),
packetIDs.client_createMatch: handleEvent(createMatchEvent),
packetIDs.client_joinMatch: handleEvent(joinMatchEvent),
packetIDs.client_partMatch: handleEvent(partMatchEvent),
packetIDs.client_matchChangeSlot: handleEvent(changeSlotEvent),
packetIDs.client_matchChangeSettings: handleEvent(changeMatchSettingsEvent),
packetIDs.client_matchChangePassword: handleEvent(changeMatchPasswordEvent),
packetIDs.client_matchChangeMods: handleEvent(changeMatchModsEvent),
packetIDs.client_matchReady: handleEvent(matchReadyEvent),
packetIDs.client_matchNotReady: handleEvent(matchReadyEvent),
packetIDs.client_matchLock: handleEvent(matchLockEvent),
packetIDs.client_matchStart: handleEvent(matchStartEvent),
packetIDs.client_matchLoadComplete: handleEvent(matchPlayerLoadEvent),
packetIDs.client_matchSkipRequest: handleEvent(matchSkipEvent),
packetIDs.client_matchScoreUpdate: handleEvent(matchFramesEvent),
packetIDs.client_matchComplete: handleEvent(matchCompleteEvent),
packetIDs.client_matchNoBeatmap: handleEvent(matchNoBeatmapEvent),
packetIDs.client_matchHasBeatmap: handleEvent(matchHasBeatmapEvent),
packetIDs.client_matchTransferHost: handleEvent(matchTransferHostEvent),
packetIDs.client_matchFailed: handleEvent(matchFailedEvent),
packetIDs.client_matchChangeTeam: handleEvent(matchChangeTeamEvent),
packetIDs.client_invite: handleEvent(matchInviteEvent),
}
# Event handler
def handleEvent(ev):
def wrapper():
ev.handle(userToken, packetData)
return wrapper
# Packets processed if in restricted mode.
# All other packets will be ignored if the user is in restricted mode
packetsRestricted = [
packetIDs.client_logout,
packetIDs.client_userStatsRequest,
packetIDs.client_requestStatusUpdate,
packetIDs.client_userPanelRequest,
packetIDs.client_changeAction,
packetIDs.client_channelJoin,
packetIDs.client_channelPart,
]
eventHandler = {
# TODO: Rename packets and events
# TODO: Host check for multi
packetIDs.client_changeAction: handleEvent(changeActionEvent),
packetIDs.client_logout: handleEvent(logoutEvent),
packetIDs.client_friendAdd: handleEvent(friendAddEvent),
packetIDs.client_friendRemove: handleEvent(friendRemoveEvent),
packetIDs.client_userStatsRequest: handleEvent(userStatsRequestEvent),
packetIDs.client_requestStatusUpdate: handleEvent(requestStatusUpdateEvent),
packetIDs.client_userPanelRequest: handleEvent(userPanelRequestEvent),
packetIDs.client_channelJoin: handleEvent(channelJoinEvent),
packetIDs.client_channelPart: handleEvent(channelPartEvent),
packetIDs.client_sendPublicMessage: handleEvent(sendPublicMessageEvent),
packetIDs.client_sendPrivateMessage: handleEvent(sendPrivateMessageEvent),
packetIDs.client_setAwayMessage: handleEvent(setAwayMessageEvent),
# Process/ignore packet
if packetID != 4:
if packetID in eventHandler:
if userToken.restricted == False or (userToken.restricted == True and packetID in packetsRestricted):
eventHandler[packetID]()
else:
log.warning("Ignored packet id from {} ({}) (user is restricted)".format(requestTokenString, packetID))
else:
log.warning("Unknown packet id from {} ({})".format(requestTokenString, packetID))
packetIDs.client_startSpectating: handleEvent(startSpectatingEvent),
packetIDs.client_stopSpectating: handleEvent(stopSpectatingEvent),
packetIDs.client_cantSpectate: handleEvent(cantSpectateEvent),
packetIDs.client_spectateFrames: handleEvent(spectateFramesEvent),
# Update pos so we can read the next stacked packet
# +7 because we add packet ID bytes, unused byte and data length bytes
pos += dataLength+7
packetIDs.client_joinLobby: handleEvent(joinLobbyEvent),
packetIDs.client_partLobby: handleEvent(partLobbyEvent),
packetIDs.client_createMatch: handleEvent(createMatchEvent),
packetIDs.client_joinMatch: handleEvent(joinMatchEvent),
packetIDs.client_partMatch: handleEvent(partMatchEvent),
packetIDs.client_matchChangeSlot: handleEvent(changeSlotEvent),
packetIDs.client_matchChangeSettings: handleEvent(changeMatchSettingsEvent),
packetIDs.client_matchChangePassword: handleEvent(changeMatchPasswordEvent),
packetIDs.client_matchChangeMods: handleEvent(changeMatchModsEvent),
packetIDs.client_matchReady: handleEvent(matchReadyEvent),
packetIDs.client_matchNotReady: handleEvent(matchReadyEvent),
packetIDs.client_matchLock: handleEvent(matchLockEvent),
packetIDs.client_matchStart: handleEvent(matchStartEvent),
packetIDs.client_matchLoadComplete: handleEvent(matchPlayerLoadEvent),
packetIDs.client_matchSkipRequest: handleEvent(matchSkipEvent),
packetIDs.client_matchScoreUpdate: handleEvent(matchFramesEvent),
packetIDs.client_matchComplete: handleEvent(matchCompleteEvent),
packetIDs.client_matchNoBeatmap: handleEvent(matchNoBeatmapEvent),
packetIDs.client_matchHasBeatmap: handleEvent(matchHasBeatmapEvent),
packetIDs.client_matchTransferHost: handleEvent(matchTransferHostEvent),
packetIDs.client_matchFailed: handleEvent(matchFailedEvent),
packetIDs.client_matchChangeTeam: handleEvent(matchChangeTeamEvent),
packetIDs.client_invite: handleEvent(matchInviteEvent),
}
# Token queue built, send it
responseTokenString = userToken.token
responseData = userToken.queue
userToken.resetQueue()
# Packets processed if in restricted mode.
# All other packets will be ignored if the user is in restricted mode
packetsRestricted = [
packetIDs.client_logout,
packetIDs.client_userStatsRequest,
packetIDs.client_requestStatusUpdate,
packetIDs.client_userPanelRequest,
packetIDs.client_changeAction,
packetIDs.client_channelJoin,
packetIDs.client_channelPart,
]
# Update ping time for timeout
userToken.updatePingTime()
except exceptions.tokenNotFoundException:
# Token not found. Disconnect that user
responseData = serverPackets.loginError()
responseData += serverPackets.notification("Whoops! Something went wrong, please login again.")
log.warning("Received packet from unknown token ({}).".format(requestTokenString))
log.info("{} has been disconnected (invalid token)".format(requestTokenString))
finally:
# Unlock token
if userToken != None:
userToken.lock.release()
# Process/ignore packet
if packetID != 4:
if packetID in eventHandler:
if userToken.restricted == False or (userToken.restricted == True and packetID in packetsRestricted):
eventHandler[packetID]()
else:
log.warning("Ignored packet id from {} ({}) (user is restricted)".format(requestTokenString, packetID))
else:
log.warning("Unknown packet id from {} ({})".format(requestTokenString, packetID))
if glob.outputRequestTime == True:
# End time
et = datetime.datetime.now()
# Update pos so we can read the next stacked packet
# +7 because we add packet ID bytes, unused byte and data length bytes
pos += dataLength+7
# Total time:
tt = float((et.microsecond-st.microsecond)/1000)
log.debug("Request time: {}ms".format(tt))
# Token queue built, send it
responseTokenString = userToken.token
responseData = userToken.queue
userToken.resetQueue()
# Send server's response to client
# We don't use token object because we might not have a token (failed login)
if glob.gzip == True:
# First, write the gzipped response
responseData = gzip.compress(responseData, int(glob.conf.config["server"]["gziplevel"]))
# Update ping time for timeout
userToken.updatePingTime()
except exceptions.tokenNotFoundException:
# Token not found. Disconnect that user
responseData = serverPackets.loginError()
responseData += serverPackets.notification("Whoops! Something went wrong, please login again.")
log.warning("Received packet from unknown token ({}).".format(requestTokenString))
log.info("{} has been disconnected (invalid token)".format(requestTokenString))
finally:
# Unlock token
if userToken != None:
userToken.lock.release()
# Then, add gzip headers
bottle.response.add_header("Vary", "Accept-Encoding")
bottle.response.add_header("Content-Encoding", "gzip")
else:
# First, write the response
responseData = responseData
if glob.outputRequestTime == True:
# End time
et = datetime.datetime.now()
# Add all the headers AFTER the response has been written
bottle.response.status = 200
bottle.response.add_header("cho-token", responseTokenString)
bottle.response.add_header("cho-protocol", "19")
bottle.response.add_header("Content-Type", "text/html; charset=UTF-8")
yield responseData
# Total time:
tt = float((et.microsecond-st.microsecond)/1000)
log.debug("Request time: {}ms".format(tt))
@bottle.route("/", method="GET")
def GETMain():
html = "<html><head><title>MA MAURO ESISTE?</title><style type='text/css'>body{width:30%}</style></head><body><pre>"
html += " _ __<br>"
html += " (_) / /<br>"
html += " ______ __ ____ ____ / /____<br>"
html += " / ___/ / _ \\/ _ \\/ / _ \\<br>"
html += " / / / / /_) / /_) / / ____/<br>"
html += "/__/ /__/ .___/ .___/__/ \\_____/<br>"
html += " / / / /<br>"
html += " /__/ /__/<br>"
html += "<b>PYTHON > ALL VERSION</b><br><br>"
html += "<marquee style='white-space:pre;'><br>"
html += " .. o .<br>"
html += " o.o o . o<br>"
html += " oo...<br>"
html += " __[]__<br>"
html += " phwr--> _\\:D/_/o_o_o_|__ <span style=\"font-family: 'Comic Sans MS'; font-size: 8pt;\">u wot m8</span><br>"
html += " \\\"\"\"\"\"\"\"\"\"\"\"\"\"\"/<br>"
html += " \\ . .. .. . /<br>"
html += "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^<br>"
html += "</marquee><br><strike>reverse engineering a protocol impossible to reverse engineer since always</strike><br>we are actually reverse engineering bancho successfully. for the third time.<br><br><i>&copy; Ripple team, 2016</i></pre></body></html>"
yield html
# Send server's response to client
# We don't use token object because we might not have a token (failed login)
if glob.gzip == True:
# First, write the gzipped response
self.write(gzip.compress(responseData, int(glob.conf.config["server"]["gziplevel"])))
# Then, add gzip headers
self.add_header("Vary", "Accept-Encoding")
self.add_header("Content-Encoding", "gzip")
else:
# First, write the response
self.write(responseData)
# Add all the headers AFTER the response has been written
self.set_status(200)
self.add_header("cho-token", responseTokenString)
self.add_header("cho-protocol", "19")
#self.add_header("Keep-Alive", "timeout=5, max=100")
#self.add_header("Connection", "keep-alive")
self.add_header("Content-Type", "text/html; charset=UTF-8")
except:
log.error("Unknown error!\n```\n{}\n{}```".format(sys.exc_info(), traceback.format_exc()))
if glob.sentry:
yield tornado.gen.Task(self.captureException, exc_info=True)
#finally:
# self.finish()
@tornado.web.asynchronous
@tornado.gen.engine
def asyncGet(self):
html = "<html><head><title>MA MAURO ESISTE?</title><style type='text/css'>body{width:30%}</style></head><body><pre>"
html += " _ __<br>"
html += " (_) / /<br>"
html += " ______ __ ____ ____ / /____<br>"
html += " / ___/ / _ \\/ _ \\/ / _ \\<br>"
html += " / / / / /_) / /_) / / ____/<br>"
html += "/__/ /__/ .___/ .___/__/ \\_____/<br>"
html += " / / / /<br>"
html += " /__/ /__/<br>"
html += "<b>PYTHON > ALL VERSION</b><br><br>"
html += "<marquee style='white-space:pre;'><br>"
html += " .. o .<br>"
html += " o.o o . o<br>"
html += " oo...<br>"
html += " __[]__<br>"
html += " phwr--> _\\:D/_/o_o_o_|__ <span style=\"font-family: 'Comic Sans MS'; font-size: 8pt;\">u wot m8</span><br>"
html += " \\\"\"\"\"\"\"\"\"\"\"\"\"\"\"/<br>"
html += " \\ . .. .. . /<br>"
html += "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^<br>"
html += "</marquee><br><strike>reverse engineering a protocol impossible to reverse engineer since always</strike><br>we are actually reverse engineering bancho successfully. for the third time.<br><br><i>&copy; Ripple team, 2016</i></pre></body></html>"
self.write(html)
#yield tornado.gen.Task(self.captureMessage, "test")
#self.finish()

View File

@ -1,7 +1,89 @@
import tornado
import tornado.web
import tornado.gen
from tornado.ioloop import IOLoop
from objects import glob
from raven.contrib.tornado import SentryMixin
from raven.contrib.tornado import AsyncSentryClient
import gevent
def getRequestIP(bottleRequest):
realIP = bottleRequest.headers.get("X-Forwarded-For") if glob.cloudflare == True else bottleRequest.headers.get("X-Real-IP")
if realIP != None:
return realIP
return bottleRequest.environ.get("REMOTE_ADDR")
class asyncRequestHandler(tornado.web.RequestHandler):
"""
Tornado asynchronous request handler
create a class that extends this one (requestHelper.asyncRequestHandler)
use asyncGet() and asyncPost() instad of get() and post().
Done. I'm not kidding.
"""
@tornado.web.asynchronous
@tornado.gen.engine
def get(self, *args, **kwargs):
try:
yield tornado.gen.Task(runBackground, (self.asyncGet, tuple(args), dict(kwargs)))
except Exception as e:
yield tornado.gen.Task(self.captureException, exc_info=True)
finally:
if not self._finished:
self.finish()
@tornado.web.asynchronous
@tornado.gen.engine
def post(self, *args, **kwargs):
try:
yield tornado.gen.Task(runBackground, (self.asyncPost, tuple(args), dict(kwargs)))
except Exception as e:
yield tornado.gen.Task(self.captureException, exc_info=True)
finally:
if not self._finished:
self.finish()
def asyncGet(self, *args, **kwargs):
self.send_error(405)
self.finish()
def asyncPost(self, *args, **kwargs):
self.send_error(405)
self.finish()
def getRequestIP(self):
realIP = self.request.headers.get("X-Forwarded-For") if glob.cloudflare == True else self.request.headers.get("X-Real-IP")
if realIP != None:
return realIP
return self.request.remote_ip
def runBackground(data, callback):
"""
Run a function in the background.
Used to handle multiple requests at the same time
"""
func, args, kwargs = data
def _callback(result):
IOLoop.instance().add_callback(lambda: callback(result))
#glob.pool.apply_async(func, args, kwargs, _callback)
g = gevent.Greenlet(func, *args, **kwargs)
g.link(_callback)
g.start()
def checkArguments(arguments, requiredArguments):
"""
Check that every requiredArguments elements are in arguments
arguments -- full argument list, from tornado
requiredArguments -- required arguments list es: ["u", "ha"]
handler -- handler string name to print in exception. Optional
return -- True if all arguments are passed, none if not
"""
for i in requiredArguments:
if i not in arguments:
return False
return True
def printArguments(t):
"""
Print passed arguments, for debug purposes
t -- tornado object (self)
"""
print("ARGS::")
for i in t.request.arguments:
print ("{}={}".format(i, t.get_argument(i)))

54
pep.py
View File

@ -3,11 +3,17 @@ import sys
import os
import threading
# Bottle
import bottle
# Tornado
import tornado.ioloop
import tornado.web
import tornado.httpserver
import tornado.gen
from gevent import monkey as brit_monkey
brit_monkey.patch_all()
# Raven
from raven.contrib.tornado import AsyncSentryClient
# pep.py files
from constants import bcolors
from helpers import configHelper
@ -20,13 +26,27 @@ from helpers import databaseHelperNew
from helpers import generalFunctions
from helpers import logHelper as log
# Raven
from raven import Client
from raven.contrib.bottle import Sentry
from handlers import mainHandler
from handlers import apiIsOnlineHandler
from handlers import apiOnlineUsersHandler
from handlers import apiServerStatusHandler
from handlers import ciTriggerHandler
from handlers import apiVerifiedStatusHandler
from handlers import apiFokabotMessageHandler
# IRC
from irc import ircserver
def make_app():
return tornado.web.Application([
(r"/", mainHandler.handler),
(r"/api/v1/isOnline", apiIsOnlineHandler.handler),
(r"/api/v1/onlineUsers", apiOnlineUsersHandler.handler),
(r"/api/v1/serverStatus", apiServerStatusHandler.handler),
(r"/api/v1/ciTrigger", ciTriggerHandler.handler),
(r"/api/v1/verifiedStatus", apiVerifiedStatusHandler.handler),
(r"/api/v1/fokabotMessage", apiFokabotMessageHandler.handler)
])
if __name__ == "__main__":
# Server start
consoleHelper.printServerStartHeader(True)
@ -140,22 +160,13 @@ if __name__ == "__main__":
consoleHelper.printColored("[!] Warning! Server running in debug mode!", bcolors.YELLOW)
# Make app
app = bottle.app()
app.catchall = False
from handlers import mainHandler
from handlers import apiIsOnlineHandler
from handlers import apiOnlineUsersHandler
from handlers import apiServerStatusHandler
from handlers import ciTriggerHandler
from handlers import apiVerifiedStatusHandler
from handlers import apiFokabotMessageHandler
application = make_app()
# Set up sentry
try:
glob.sentry = generalFunctions.stringToBool(glob.conf.config["sentry"]["enable"])
if glob.sentry == True:
client = Client(glob.conf.config["sentry"]["banchodns"], release=glob.VERSION)
app = Sentry(app, client)
application.sentry_client = AsyncSentryClient(glob.conf.config["sentry"]["banchodns"], release=glob.VERSION)
else:
consoleHelper.printColored("[!] Warning! Sentry logging is disabled!", bcolors.YELLOW)
except:
@ -185,8 +196,9 @@ if __name__ == "__main__":
consoleHelper.printColored("[!] Invalid server port! Please check your config.ini and run the server again", bcolors.RED)
# Server start message and console output
log.logMessage("Server started!", discord=True, stdout=False)
consoleHelper.printColored("> Bottle listening for HTTP(s) clients on 127.0.0.1:{}...".format(serverPort), bcolors.GREEN)
log.logMessage("Server started!", discord=True, of="info.txt", stdout=False)
consoleHelper.printColored("> Tornado listening for HTTP(s) clients on 127.0.0.1:{}...".format(serverPort), bcolors.GREEN)
# Start bottle
bottle.run(app=app, host="0.0.0.0", port=serverPort, server="gevent")
# Start tornado
application.listen(serverPort)
tornado.ioloop.IOLoop.instance().start()