.BANCHO. pep.py modules
This commit is contained in:
107
helpers/configHelper.py
Normal file
107
helpers/configHelper.py
Normal file
@@ -0,0 +1,107 @@
|
||||
import os
|
||||
import configparser
|
||||
|
||||
class config:
|
||||
"""
|
||||
config.ini object
|
||||
|
||||
config -- list with ini data
|
||||
default -- if true, we have generated a default config.ini
|
||||
"""
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
fileName = "" # config filename
|
||||
default = True
|
||||
|
||||
# Check if config.ini exists and load/generate it
|
||||
def __init__(self, __file):
|
||||
"""
|
||||
Initialize a config object
|
||||
|
||||
__file -- filename
|
||||
"""
|
||||
|
||||
self.fileName = __file
|
||||
if os.path.isfile(self.fileName):
|
||||
# config.ini found, load it
|
||||
self.config.read(self.fileName)
|
||||
self.default = False
|
||||
else:
|
||||
# config.ini not found, generate a default one
|
||||
self.generateDefaultConfig()
|
||||
self.default = True
|
||||
|
||||
|
||||
# Check if config.ini has all needed the keys
|
||||
def checkConfig(self):
|
||||
"""
|
||||
Check if this config has the required keys
|
||||
|
||||
return -- True if valid, False if not
|
||||
"""
|
||||
|
||||
try:
|
||||
# Try to get all the required keys
|
||||
self.config.get("db","host")
|
||||
self.config.get("db","username")
|
||||
self.config.get("db","password")
|
||||
self.config.get("db","database")
|
||||
self.config.get("db","pingtime")
|
||||
|
||||
self.config.get("server","server")
|
||||
self.config.get("server","host")
|
||||
self.config.get("server","port")
|
||||
self.config.get("server","localizeusers")
|
||||
self.config.get("server","outputpackets")
|
||||
self.config.get("server","outputrequesttime")
|
||||
self.config.get("server","timeouttime")
|
||||
self.config.get("server","timeoutlooptime")
|
||||
|
||||
if self.config["server"]["server"] == "flask":
|
||||
# Flask only config
|
||||
self.config.get("flask","threaded")
|
||||
self.config.get("flask","debug")
|
||||
self.config.get("flask","logger")
|
||||
|
||||
self.config.get("ci","key")
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
# Generate a default config.ini
|
||||
def generateDefaultConfig(self):
|
||||
"""Open and set default keys for that config file"""
|
||||
|
||||
# Open config.ini in write mode
|
||||
f = open(self.fileName, "w")
|
||||
|
||||
# Set keys to config object
|
||||
self.config.add_section("db")
|
||||
self.config.set("db", "host", "localhost")
|
||||
self.config.set("db", "username", "root")
|
||||
self.config.set("db", "password", "")
|
||||
self.config.set("db", "database", "ripple")
|
||||
self.config.set("db", "pingtime", "600")
|
||||
|
||||
self.config.add_section("server")
|
||||
self.config.set("server", "server", "tornado")
|
||||
self.config.set("server", "host", "0.0.0.0")
|
||||
self.config.set("server", "port", "5001")
|
||||
self.config.set("server", "localizeusers", "1")
|
||||
self.config.set("server", "outputpackets", "0")
|
||||
self.config.set("server", "outputrequesttime", "0")
|
||||
self.config.set("server", "timeoutlooptime", "100")
|
||||
self.config.set("server", "timeouttime", "100")
|
||||
|
||||
self.config.add_section("flask")
|
||||
self.config.set("flask", "threaded", "1")
|
||||
self.config.set("flask", "debug", "0")
|
||||
self.config.set("flask", "logger", "0")
|
||||
|
||||
self.config.add_section("ci")
|
||||
self.config.set("ci", "key", "changeme")
|
||||
|
||||
# Write ini to file and close
|
||||
self.config.write(f)
|
||||
f.close()
|
@@ -1,7 +1,7 @@
|
||||
"""Some console related functions"""
|
||||
|
||||
from constants import bcolors
|
||||
import glob
|
||||
from objects import glob
|
||||
|
||||
def printServerStartHeader(asciiArt):
|
||||
"""Print server start header with optional ascii art
|
||||
|
282
helpers/countryHelper.py
Normal file
282
helpers/countryHelper.py
Normal file
@@ -0,0 +1,282 @@
|
||||
"""Contains all country codes with their osu numeric code"""
|
||||
|
||||
countryCodes = {
|
||||
"LV": 132,
|
||||
"AD": 3,
|
||||
"LT": 130,
|
||||
"KM": 116,
|
||||
"QA": 182,
|
||||
"VA": 0,
|
||||
"PK": 173,
|
||||
"KI": 115,
|
||||
"SS": 0,
|
||||
"KH": 114,
|
||||
"NZ": 166,
|
||||
"TO": 215,
|
||||
"KZ": 122,
|
||||
"GA": 76,
|
||||
"BW": 35,
|
||||
"AX": 247,
|
||||
"GE": 79,
|
||||
"UA": 222,
|
||||
"CR": 50,
|
||||
"AE": 0,
|
||||
"NE": 157,
|
||||
"ZA": 240,
|
||||
"SK": 196,
|
||||
"BV": 34,
|
||||
"SH": 0,
|
||||
"PT": 179,
|
||||
"SC": 189,
|
||||
"CO": 49,
|
||||
"GP": 86,
|
||||
"GY": 93,
|
||||
"CM": 47,
|
||||
"TJ": 211,
|
||||
"AF": 5,
|
||||
"IE": 101,
|
||||
"AL": 8,
|
||||
"BG": 24,
|
||||
"JO": 110,
|
||||
"MU": 149,
|
||||
"PM": 0,
|
||||
"LA": 0,
|
||||
"IO": 104,
|
||||
"KY": 121,
|
||||
"SA": 187,
|
||||
"KN": 0,
|
||||
"OM": 167,
|
||||
"CY": 54,
|
||||
"BQ": 0,
|
||||
"BT": 33,
|
||||
"WS": 236,
|
||||
"ES": 67,
|
||||
"LR": 128,
|
||||
"RW": 186,
|
||||
"AQ": 12,
|
||||
"PW": 180,
|
||||
"JE": 250,
|
||||
"TN": 214,
|
||||
"ZW": 243,
|
||||
"JP": 111,
|
||||
"BB": 20,
|
||||
"VN": 233,
|
||||
"HN": 96,
|
||||
"KP": 0,
|
||||
"WF": 235,
|
||||
"EC": 62,
|
||||
"HU": 99,
|
||||
"GF": 80,
|
||||
"GQ": 87,
|
||||
"TW": 220,
|
||||
"MC": 135,
|
||||
"BE": 22,
|
||||
"PN": 176,
|
||||
"SZ": 205,
|
||||
"CZ": 55,
|
||||
"LY": 0,
|
||||
"IN": 103,
|
||||
"FM": 0,
|
||||
"PY": 181,
|
||||
"PH": 172,
|
||||
"MN": 142,
|
||||
"GG": 248,
|
||||
"CC": 39,
|
||||
"ME": 242,
|
||||
"DO": 60,
|
||||
"KR": 0,
|
||||
"PL": 174,
|
||||
"MT": 148,
|
||||
"MM": 141,
|
||||
"AW": 17,
|
||||
"MV": 150,
|
||||
"BD": 21,
|
||||
"NR": 164,
|
||||
"AT": 15,
|
||||
"GW": 92,
|
||||
"FR": 74,
|
||||
"LI": 126,
|
||||
"CF": 41,
|
||||
"DZ": 61,
|
||||
"MA": 134,
|
||||
"VG": 0,
|
||||
"NC": 156,
|
||||
"IQ": 105,
|
||||
"BN": 0,
|
||||
"BF": 23,
|
||||
"BO": 30,
|
||||
"GB": 77,
|
||||
"CU": 51,
|
||||
"LU": 131,
|
||||
"YT": 238,
|
||||
"NO": 162,
|
||||
"SM": 198,
|
||||
"GL": 83,
|
||||
"IS": 107,
|
||||
"AO": 11,
|
||||
"MH": 138,
|
||||
"SE": 191,
|
||||
"ZM": 241,
|
||||
"FJ": 70,
|
||||
"SL": 197,
|
||||
"CH": 43,
|
||||
"RU": 0,
|
||||
"CW": 0,
|
||||
"CX": 53,
|
||||
"TF": 208,
|
||||
"NL": 161,
|
||||
"AU": 16,
|
||||
"FI": 69,
|
||||
"MS": 147,
|
||||
"GH": 81,
|
||||
"BY": 36,
|
||||
"IL": 102,
|
||||
"VC": 0,
|
||||
"NG": 159,
|
||||
"HT": 98,
|
||||
"LS": 129,
|
||||
"MR": 146,
|
||||
"YE": 237,
|
||||
"MP": 144,
|
||||
"SX": 0,
|
||||
"RE": 183,
|
||||
"RO": 184,
|
||||
"NP": 163,
|
||||
"CG": 0,
|
||||
"FO": 73,
|
||||
"CI": 0,
|
||||
"TH": 210,
|
||||
"HK": 94,
|
||||
"TK": 212,
|
||||
"XK": 0,
|
||||
"DM": 59,
|
||||
"LC": 0,
|
||||
"ID": 100,
|
||||
"MG": 137,
|
||||
"JM": 109,
|
||||
"IT": 108,
|
||||
"CA": 38,
|
||||
"TZ": 221,
|
||||
"GI": 82,
|
||||
"KG": 113,
|
||||
"NU": 165,
|
||||
"TV": 219,
|
||||
"LB": 124,
|
||||
"SY": 0,
|
||||
"PR": 177,
|
||||
"NI": 160,
|
||||
"KE": 112,
|
||||
"MO": 0,
|
||||
"SR": 201,
|
||||
"VI": 0,
|
||||
"SV": 203,
|
||||
"HM": 0,
|
||||
"CD": 0,
|
||||
"BI": 26,
|
||||
"BM": 28,
|
||||
"MW": 151,
|
||||
"TM": 213,
|
||||
"GT": 90,
|
||||
"AG": 0,
|
||||
"UM": 0,
|
||||
"US": 225,
|
||||
"AR": 13,
|
||||
"DJ": 57,
|
||||
"KW": 120,
|
||||
"MY": 153,
|
||||
"FK": 71,
|
||||
"EG": 64,
|
||||
"BA": 0,
|
||||
"CN": 48,
|
||||
"GN": 85,
|
||||
"PS": 178,
|
||||
"SO": 200,
|
||||
"IM": 249,
|
||||
"GS": 0,
|
||||
"BR": 31,
|
||||
"GM": 84,
|
||||
"PF": 170,
|
||||
"PA": 168,
|
||||
"PG": 171,
|
||||
"BH": 25,
|
||||
"TG": 209,
|
||||
"GU": 91,
|
||||
"CK": 45,
|
||||
"MF": 252,
|
||||
"VE": 230,
|
||||
"CL": 46,
|
||||
"TR": 217,
|
||||
"UG": 223,
|
||||
"GD": 78,
|
||||
"TT": 218,
|
||||
"TL": 0,
|
||||
"MD": 0,
|
||||
"MK": 0,
|
||||
"ST": 202,
|
||||
"CV": 52,
|
||||
"MQ": 145,
|
||||
"GR": 88,
|
||||
"HR": 97,
|
||||
"BZ": 37,
|
||||
"UZ": 227,
|
||||
"DK": 58,
|
||||
"SN": 199,
|
||||
"ET": 68,
|
||||
"VU": 234,
|
||||
"ER": 66,
|
||||
"BJ": 27,
|
||||
"LK": 127,
|
||||
"NA": 155,
|
||||
"AS": 14,
|
||||
"SG": 192,
|
||||
"PE": 169,
|
||||
"IR": 0,
|
||||
"MX": 152,
|
||||
"TD": 207,
|
||||
"AZ": 18,
|
||||
"AM": 9,
|
||||
"BL": 0,
|
||||
"SJ": 195,
|
||||
"SB": 188,
|
||||
"NF": 158,
|
||||
"RS": 239,
|
||||
"DE": 56,
|
||||
"EH": 65,
|
||||
"EE": 63,
|
||||
"SD": 190,
|
||||
"ML": 140,
|
||||
"TC": 206,
|
||||
"MZ": 154,
|
||||
"BS": 32,
|
||||
"UY": 226,
|
||||
"SI": 194,
|
||||
"AI": 7
|
||||
}
|
||||
|
||||
|
||||
def getCountryID(code):
|
||||
"""
|
||||
Get country ID for osu client
|
||||
|
||||
code -- country name abbreviation (eg: US)
|
||||
return -- country code int
|
||||
"""
|
||||
|
||||
if code in countryCodes:
|
||||
return countryCodes[code]
|
||||
else:
|
||||
return 0
|
||||
|
||||
def getCountryLetters(code):
|
||||
"""
|
||||
Get country letters from osu country ID
|
||||
|
||||
code -- country code int
|
||||
return -- country name (2 letters) (XX if code not found)
|
||||
"""
|
||||
|
||||
for key, value in countryCodes.items():
|
||||
if value == code:
|
||||
return key
|
||||
|
||||
return "XX"
|
302
helpers/cryptHelper.py
Normal file
302
helpers/cryptHelper.py
Normal file
@@ -0,0 +1,302 @@
|
||||
# Huge thanks to Cairnarvon
|
||||
# https://gist.github.com/Cairnarvon/5075687
|
||||
|
||||
# Initial permutation
|
||||
IP = (
|
||||
58, 50, 42, 34, 26, 18, 10, 2,
|
||||
60, 52, 44, 36, 28, 20, 12, 4,
|
||||
62, 54, 46, 38, 30, 22, 14, 6,
|
||||
64, 56, 48, 40, 32, 24, 16, 8,
|
||||
57, 49, 41, 33, 25, 17, 9, 1,
|
||||
59, 51, 43, 35, 27, 19, 11, 3,
|
||||
61, 53, 45, 37, 29, 21, 13, 5,
|
||||
63, 55, 47, 39, 31, 23, 15, 7,
|
||||
)
|
||||
|
||||
# Final permutation, FP = IP^(-1)
|
||||
FP = (
|
||||
40, 8, 48, 16, 56, 24, 64, 32,
|
||||
39, 7, 47, 15, 55, 23, 63, 31,
|
||||
38, 6, 46, 14, 54, 22, 62, 30,
|
||||
37, 5, 45, 13, 53, 21, 61, 29,
|
||||
36, 4, 44, 12, 52, 20, 60, 28,
|
||||
35, 3, 43, 11, 51, 19, 59, 27,
|
||||
34, 2, 42, 10, 50, 18, 58, 26,
|
||||
33, 1, 41, 9, 49, 17, 57, 25,
|
||||
)
|
||||
|
||||
# Permuted-choice 1 from the key bits to yield C and D.
|
||||
# Note that bits 8,16... are left out: They are intended for a parity check.
|
||||
PC1_C = (
|
||||
57, 49, 41, 33, 25, 17, 9,
|
||||
1, 58, 50, 42, 34, 26, 18,
|
||||
10, 2, 59, 51, 43, 35, 27,
|
||||
19, 11, 3, 60, 52, 44, 36,
|
||||
)
|
||||
PC1_D = (
|
||||
63, 55, 47, 39, 31, 23, 15,
|
||||
7, 62, 54, 46, 38, 30, 22,
|
||||
14, 6, 61, 53, 45, 37, 29,
|
||||
21, 13, 5, 28, 20, 12, 4,
|
||||
)
|
||||
|
||||
# Permuted-choice 2, to pick out the bits from the CD array that generate the
|
||||
# key schedule.
|
||||
PC2_C = (
|
||||
14, 17, 11, 24, 1, 5,
|
||||
3, 28, 15, 6, 21, 10,
|
||||
23, 19, 12, 4, 26, 8,
|
||||
16, 7, 27, 20, 13, 2,
|
||||
)
|
||||
PC2_D = (
|
||||
41, 52, 31, 37, 47, 55,
|
||||
30, 40, 51, 45, 33, 48,
|
||||
44, 49, 39, 56, 34, 53,
|
||||
46, 42, 50, 36, 29, 32,
|
||||
)
|
||||
|
||||
# The C and D arrays are used to calculate the key schedule.
|
||||
C = [0] * 28
|
||||
D = [0] * 28
|
||||
|
||||
# The key schedule. Generated from the key.
|
||||
KS = [[0] * 48 for _ in range(16)]
|
||||
|
||||
# The E bit-selection table.
|
||||
E = [0] * 48
|
||||
e2 = (
|
||||
32, 1, 2, 3, 4, 5,
|
||||
4, 5, 6, 7, 8, 9,
|
||||
8, 9, 10, 11, 12, 13,
|
||||
12, 13, 14, 15, 16, 17,
|
||||
16, 17, 18, 19, 20, 21,
|
||||
20, 21, 22, 23, 24, 25,
|
||||
24, 25, 26, 27, 28, 29,
|
||||
28, 29, 30, 31, 32, 1,
|
||||
)
|
||||
|
||||
# S-boxes.
|
||||
S = (
|
||||
(
|
||||
14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
|
||||
0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
|
||||
4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
|
||||
15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13
|
||||
),
|
||||
(
|
||||
15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
|
||||
3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
|
||||
0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
|
||||
13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9
|
||||
),
|
||||
(
|
||||
10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
|
||||
13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
|
||||
13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
|
||||
1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12
|
||||
),
|
||||
(
|
||||
7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
|
||||
13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
|
||||
10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
|
||||
3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14
|
||||
),
|
||||
(
|
||||
2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
|
||||
14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
|
||||
4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
|
||||
11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3
|
||||
),
|
||||
(
|
||||
12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
|
||||
10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
|
||||
9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
|
||||
4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13
|
||||
),
|
||||
(
|
||||
4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
|
||||
13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
|
||||
1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
|
||||
6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12
|
||||
),
|
||||
(
|
||||
13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
|
||||
1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
|
||||
7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
|
||||
2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
|
||||
)
|
||||
)
|
||||
|
||||
# P is a permutation on the selected combination of the current L and key.
|
||||
P = (
|
||||
16, 7, 20, 21,
|
||||
29, 12, 28, 17,
|
||||
1, 15, 23, 26,
|
||||
5, 18, 31, 10,
|
||||
2, 8, 24, 14,
|
||||
32, 27, 3, 9,
|
||||
19, 13, 30, 6,
|
||||
22, 11, 4, 25,
|
||||
)
|
||||
|
||||
# The combination of the key and the input, before selection.
|
||||
preS = [0] * 48
|
||||
|
||||
|
||||
def __setkey(key):
|
||||
"""
|
||||
Set up the key schedule from the encryption key.
|
||||
"""
|
||||
global C, D, KS, E
|
||||
|
||||
shifts = (1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1)
|
||||
|
||||
# First, generate C and D by permuting the key. The lower order bit of each
|
||||
# 8-bit char is not used, so C and D are only 28 bits apiece.
|
||||
for i in range(28):
|
||||
C[i] = key[PC1_C[i] - 1]
|
||||
D[i] = key[PC1_D[i] - 1]
|
||||
|
||||
for i in range(16):
|
||||
# rotate
|
||||
for k in range(shifts[i]):
|
||||
temp = C[0]
|
||||
|
||||
for j in range(27):
|
||||
C[j] = C[j + 1]
|
||||
|
||||
C[27] = temp
|
||||
temp = D[0]
|
||||
for j in range(27):
|
||||
D[j] = D[j + 1]
|
||||
|
||||
D[27] = temp
|
||||
|
||||
# get Ki. Note C and D are concatenated
|
||||
for j in range(24):
|
||||
KS[i][j] = C[PC2_C[j] - 1]
|
||||
KS[i][j + 24] = D[PC2_D[j] - 28 - 1]
|
||||
|
||||
# load E with the initial E bit selections
|
||||
for i in range(48):
|
||||
E[i] = e2[i]
|
||||
|
||||
def __encrypt(block):
|
||||
global preS
|
||||
|
||||
left, right = [], [] # block in two halves
|
||||
f = [0] * 32
|
||||
|
||||
# First, permute the bits in the input
|
||||
for j in range(32):
|
||||
left.append(block[IP[j] - 1])
|
||||
|
||||
for j in range(32, 64):
|
||||
right.append(block[IP[j] - 1])
|
||||
|
||||
# Perform an encryption operation 16 times.
|
||||
for i in range(16):
|
||||
# Save the right array, which will be the new left.
|
||||
old = right[:]
|
||||
|
||||
# Expand right to 48 bits using the E selector and exclusive-or with
|
||||
# the current key bits.
|
||||
for j in range(48):
|
||||
preS[j] = right[E[j] - 1] ^ KS[i][j]
|
||||
|
||||
# The pre-select bits are now considered in 8 groups of 6 bits each.
|
||||
# The 8 selection functions map these 6-bit quantities into 4-bit
|
||||
# quantities and the results are permuted to make an f(R, K).
|
||||
# The indexing into the selection functions is peculiar; it could be
|
||||
# simplified by rewriting the tables.
|
||||
for j in range(8):
|
||||
temp = 6 * j
|
||||
k = S[j][(preS[temp + 0] << 5) +
|
||||
(preS[temp + 1] << 3) +
|
||||
(preS[temp + 2] << 2) +
|
||||
(preS[temp + 3] << 1) +
|
||||
(preS[temp + 4] << 0) +
|
||||
(preS[temp + 5] << 4)]
|
||||
|
||||
temp = 4 * j
|
||||
f[temp + 0] = (k >> 3) & 1
|
||||
f[temp + 1] = (k >> 2) & 1
|
||||
f[temp + 2] = (k >> 1) & 1
|
||||
f[temp + 3] = (k >> 0) & 1
|
||||
|
||||
# The new right is left ^ f(R, K).
|
||||
# The f here has to be permuted first, though.
|
||||
for j in range(32):
|
||||
right[j] = left[j] ^ f[P[j] - 1]
|
||||
|
||||
# Finally the new left (the original right) is copied back.
|
||||
left = old
|
||||
|
||||
# The output left and right are reversed.
|
||||
left, right = right, left
|
||||
|
||||
# The final output gets the inverse permutation of the very original
|
||||
for j in range(64):
|
||||
i = FP[j]
|
||||
if i < 33:
|
||||
block[j] = left[i - 1]
|
||||
else:
|
||||
block[j] = right[i - 33]
|
||||
|
||||
return block
|
||||
|
||||
def crypt(pw, salt):
|
||||
iobuf = []
|
||||
|
||||
# break pw into 64 bits
|
||||
block = []
|
||||
for c in pw:
|
||||
c = ord(c)
|
||||
for j in range(7):
|
||||
block.append((c >> (6 - j)) & 1)
|
||||
block.append(0)
|
||||
block += [0] * (64 - len(block))
|
||||
|
||||
# set key based on pw
|
||||
__setkey(block)
|
||||
|
||||
for i in range(2):
|
||||
# store salt at beginning of results
|
||||
iobuf.append(salt[i])
|
||||
c = ord(salt[i])
|
||||
|
||||
if c > ord('Z'):
|
||||
c -= 6
|
||||
|
||||
if c > ord('9'):
|
||||
c -= 7
|
||||
|
||||
c -= ord('.')
|
||||
|
||||
# use salt to effect the E-bit selection
|
||||
for j in range(6):
|
||||
if (c >> j) & 1:
|
||||
E[6 * i + j], E[6 * i + j + 24] = E[6 * i + j + 24], E[6 * i + j]
|
||||
|
||||
# call DES encryption 25 times using pw as key and initial data = 0
|
||||
block = [0] * 66
|
||||
for i in range(25):
|
||||
block = __encrypt(block)
|
||||
|
||||
# format encrypted block for standard crypt(3) output
|
||||
for i in range(11):
|
||||
c = 0
|
||||
for j in range(6):
|
||||
c <<= 1
|
||||
c |= block[6 * i + j]
|
||||
|
||||
c += ord('.')
|
||||
if c > ord('9'):
|
||||
c += 7
|
||||
|
||||
if c > ord('Z'):
|
||||
c += 6
|
||||
|
||||
iobuf.append(chr(c))
|
||||
|
||||
return ''.join(iobuf)
|
138
helpers/databaseHelper.py
Normal file
138
helpers/databaseHelper.py
Normal file
@@ -0,0 +1,138 @@
|
||||
import pymysql
|
||||
from constants import bcolors
|
||||
from helpers import consoleHelper
|
||||
import threading
|
||||
from objects import glob
|
||||
|
||||
class db:
|
||||
"""A MySQL database connection"""
|
||||
|
||||
connection = None
|
||||
disconnected = False
|
||||
pingTime = 600
|
||||
|
||||
def __init__(self, __host, __username, __password, __database, __pingTime = 600):
|
||||
"""
|
||||
Connect to MySQL database
|
||||
|
||||
__host -- MySQL host name
|
||||
__username -- MySQL username
|
||||
__password -- MySQL password
|
||||
__database -- MySQL database name
|
||||
__pingTime -- MySQL database ping time (default: 600)
|
||||
"""
|
||||
|
||||
self.connection = pymysql.connect(host=__host, user=__username, password=__password, db=__database, cursorclass=pymysql.cursors.DictCursor, autocommit=True)
|
||||
self.pingTime = __pingTime
|
||||
self.pingLoop()
|
||||
|
||||
|
||||
def bindParams(self, __query, __params):
|
||||
"""
|
||||
Replace every ? with the respective **escaped** parameter in array
|
||||
|
||||
__query -- query with ?s
|
||||
__params -- array with params
|
||||
|
||||
return -- new query
|
||||
"""
|
||||
|
||||
for i in __params:
|
||||
escaped = self.connection.escape(i)
|
||||
__query = __query.replace("?", str(escaped), 1)
|
||||
|
||||
return __query
|
||||
|
||||
|
||||
def execute(self, __query, __params = None):
|
||||
"""
|
||||
Execute a SQL query
|
||||
|
||||
__query -- query, can contain ?s
|
||||
__params -- array with params. Optional
|
||||
"""
|
||||
|
||||
|
||||
with self.connection.cursor() as cursor:
|
||||
try:
|
||||
# Bind params if needed
|
||||
if __params != None:
|
||||
__query = self.bindParams(__query, __params)
|
||||
|
||||
# Execute the query
|
||||
cursor.execute(__query)
|
||||
finally:
|
||||
# Close this connection
|
||||
cursor.close()
|
||||
|
||||
|
||||
def fetch(self, __query, __params = None, __all = False):
|
||||
"""
|
||||
Fetch the first (or all) element(s) of SQL query result
|
||||
|
||||
__query -- query, can contain ?s
|
||||
__params -- array with params. Optional
|
||||
__all -- if true, will fetch all values. Same as fetchAll
|
||||
|
||||
return -- dictionary with result data or False if failed
|
||||
"""
|
||||
|
||||
|
||||
with self.connection.cursor() as cursor:
|
||||
try:
|
||||
# Bind params if needed
|
||||
if __params != None:
|
||||
__query = self.bindParams(__query, __params)
|
||||
|
||||
# Execute the query with binded params
|
||||
cursor.execute(__query)
|
||||
|
||||
# Get first result and return it
|
||||
if __all == False:
|
||||
return cursor.fetchone()
|
||||
else:
|
||||
return cursor.fetchall()
|
||||
finally:
|
||||
# Close this connection
|
||||
cursor.close()
|
||||
|
||||
|
||||
def fetchAll(self, __query, __params = None):
|
||||
"""
|
||||
Fetch the all elements of SQL query result
|
||||
|
||||
__query -- query, can contain ?s
|
||||
__params -- array with params. Optional
|
||||
|
||||
return -- dictionary with result data
|
||||
"""
|
||||
|
||||
return self.fetch(__query, __params, True)
|
||||
|
||||
def pingLoop(self):
|
||||
"""
|
||||
Pings MySQL server. We need to ping/execute a query at least once every 8 hours
|
||||
or the connection will die.
|
||||
If called once, will recall after 30 minutes and so on, forever
|
||||
CALL THIS FUNCTION ONLY ONCE!
|
||||
"""
|
||||
|
||||
# Default loop time
|
||||
time = self.pingTime
|
||||
|
||||
# Make sure the connection is alive
|
||||
try:
|
||||
# Try to ping and reconnect if not connected
|
||||
self.connection.ping()
|
||||
if self.disconnected == True:
|
||||
# If we were disconnected, set disconnected to false and print message
|
||||
self.disconnected = False
|
||||
consoleHelper.printColored("> Reconnected to MySQL server!", bcolors.GREEN)
|
||||
except:
|
||||
# Can't ping MySQL server. Show error and call loop in 5 seconds
|
||||
consoleHelper.printColored("[!] CRITICAL!! MySQL connection died! Make sure your MySQL server is running! Checking again in 5 seconds...", bcolors.RED)
|
||||
self.disconnected = True
|
||||
time = 5
|
||||
|
||||
# Schedule a new check (endless loop)
|
||||
threading.Timer(time, self.pingLoop).start()
|
22
helpers/generalFunctions.py
Normal file
22
helpers/generalFunctions.py
Normal file
@@ -0,0 +1,22 @@
|
||||
"""Some functions that don't fit in any other file"""
|
||||
|
||||
def stringToBool(s):
|
||||
"""
|
||||
Convert a string (True/true/1) to bool
|
||||
|
||||
s -- string/int value
|
||||
return -- True/False
|
||||
"""
|
||||
|
||||
return (s == "True" or s== "true" or s == "1" or s == 1)
|
||||
|
||||
|
||||
def hexString(s):
|
||||
"""
|
||||
Output s' bytes in HEX
|
||||
|
||||
s -- string
|
||||
return -- string with hex value
|
||||
"""
|
||||
|
||||
return ":".join("{:02x}".format(ord(c)) for c in s)
|
48
helpers/locationHelper.py
Normal file
48
helpers/locationHelper.py
Normal file
@@ -0,0 +1,48 @@
|
||||
import urllib.request
|
||||
import json
|
||||
|
||||
from helpers import consoleHelper
|
||||
from constants import bcolors
|
||||
|
||||
# API URL
|
||||
URL = "http://ip.zxq.co/"
|
||||
|
||||
|
||||
def getCountry(ip):
|
||||
"""
|
||||
Get country from IP address
|
||||
|
||||
ip -- IP Address
|
||||
return -- Country code (2 letters)
|
||||
"""
|
||||
|
||||
# Default value, sent if API is memeing
|
||||
country = "XX"
|
||||
|
||||
try:
|
||||
# Try to get country from Pikolo Aul's Go-Sanic ip API
|
||||
result = json.loads(urllib.request.urlopen("{}/{}".format(URL, ip), timeout=3).read().decode())["country"]
|
||||
return result.upper()
|
||||
except:
|
||||
consoleHelper.printColored("[!] Error in get country", bcolors.RED)
|
||||
return "XX"
|
||||
|
||||
|
||||
def getLocation(ip):
|
||||
"""
|
||||
Get latitude and longitude from IP address
|
||||
|
||||
ip -- IP address
|
||||
return -- [latitude, longitude]
|
||||
"""
|
||||
|
||||
# Default value, sent if API is memeing
|
||||
data = [0,0]
|
||||
|
||||
try:
|
||||
# Try to get position from Pikolo Aul's Go-Sanic ip API
|
||||
result = json.loads(urllib.request.urlopen("{}/{}".format(URL, ip), timeout=3).read().decode())["loc"].split(",")
|
||||
return [float(result[0]), float(result[1])]
|
||||
except:
|
||||
consoleHelper.printColored("[!] Error in get position", bcolors.RED)
|
||||
return [0,0]
|
249
helpers/packetHelper.py
Normal file
249
helpers/packetHelper.py
Normal file
@@ -0,0 +1,249 @@
|
||||
import struct
|
||||
from constants import dataTypes
|
||||
|
||||
def uleb128Encode(num):
|
||||
"""
|
||||
Encode int -> uleb128
|
||||
|
||||
num -- int to encode
|
||||
return -- bytearray with encoded number
|
||||
"""
|
||||
|
||||
arr = bytearray()
|
||||
length = 0
|
||||
|
||||
if num == 0:
|
||||
return bytearray(b"\x00")
|
||||
|
||||
while num > 0:
|
||||
arr.append(num & 127)
|
||||
num = num >> 7
|
||||
if num != 0:
|
||||
arr[length] = arr[length] | 128
|
||||
length+=1
|
||||
|
||||
return arr
|
||||
|
||||
|
||||
def uleb128Decode(num):
|
||||
"""
|
||||
Decode uleb128 -> int
|
||||
|
||||
num -- encoded uleb128
|
||||
return -- list. [total, length]
|
||||
"""
|
||||
|
||||
shift = 0
|
||||
|
||||
arr = [0,0] #total, length
|
||||
|
||||
while True:
|
||||
b = num[arr[1]]
|
||||
arr[1]+=1
|
||||
arr[0] = arr[0] | (int(b & 127) << shift)
|
||||
if b & 128 == 0:
|
||||
break
|
||||
shift += 7
|
||||
|
||||
return arr
|
||||
|
||||
|
||||
def unpackData(__data, __dataType):
|
||||
"""
|
||||
Unpacks data according to dataType
|
||||
|
||||
__data -- bytes array to unpack
|
||||
__dataType -- data type. See dataTypes.py
|
||||
|
||||
return -- unpacked bytes
|
||||
"""
|
||||
|
||||
# Get right pack Type
|
||||
if __dataType == dataTypes.uInt16:
|
||||
unpackType = "<H"
|
||||
elif __dataType == dataTypes.sInt16:
|
||||
unpackType = "<h"
|
||||
elif __dataType == dataTypes.uInt32:
|
||||
unpackType = "<L"
|
||||
elif __dataType == dataTypes.sInt32:
|
||||
unpackType = "<l"
|
||||
elif __dataType == dataTypes.uInt64:
|
||||
unpackType = "<Q"
|
||||
elif __dataType == dataTypes.sInt64:
|
||||
unpackType = "<q"
|
||||
elif __dataType == dataTypes.string:
|
||||
unpackType = "<s"
|
||||
elif __dataType == dataTypes.ffloat:
|
||||
unpackType = "<f"
|
||||
else:
|
||||
unpackType = "<B"
|
||||
|
||||
# Unpack
|
||||
return struct.unpack(unpackType, bytes(__data))[0]
|
||||
|
||||
|
||||
def packData(__data, __dataType):
|
||||
"""
|
||||
Packs data according to dataType
|
||||
|
||||
data -- bytes to pack
|
||||
dataType -- data type. See dataTypes.py
|
||||
|
||||
return -- packed bytes
|
||||
"""
|
||||
|
||||
data = bytes() # data to return
|
||||
pack = True # if True, use pack. False only with strings
|
||||
|
||||
# Get right pack Type
|
||||
if __dataType == dataTypes.bbytes:
|
||||
# Bytes, do not use pack, do manually
|
||||
pack = False
|
||||
data = __data
|
||||
elif __dataType == dataTypes.string:
|
||||
# String, do not use pack, do manually
|
||||
pack = False
|
||||
if len(__data) == 0:
|
||||
# Empty string
|
||||
data += b"\x00"
|
||||
else:
|
||||
# Non empty string
|
||||
data += b"\x0B"
|
||||
data += uleb128Encode(len(__data))
|
||||
data += str.encode(__data, "latin_1")
|
||||
elif __dataType == dataTypes.uInt16:
|
||||
packType = "<H"
|
||||
elif __dataType == dataTypes.sInt16:
|
||||
packType = "<h"
|
||||
elif __dataType == dataTypes.uInt32:
|
||||
packType = "<L"
|
||||
elif __dataType == dataTypes.sInt32:
|
||||
packType = "<l"
|
||||
elif __dataType == dataTypes.uInt64:
|
||||
packType = "<Q"
|
||||
elif __dataType == dataTypes.sInt64:
|
||||
packType = "<q"
|
||||
elif __dataType == dataTypes.string:
|
||||
packType = "<s"
|
||||
elif __dataType == dataTypes.ffloat:
|
||||
packType = "<f"
|
||||
else:
|
||||
packType = "<B"
|
||||
|
||||
# Pack if needed
|
||||
if pack == True:
|
||||
data += struct.pack(packType, __data)
|
||||
|
||||
return data
|
||||
|
||||
# TODO: Wat dangerous
|
||||
def buildPacket(__packet, __packetData = []):
|
||||
"""
|
||||
Build a packet
|
||||
|
||||
packet -- packet id (int)
|
||||
packetData -- list [[data, dataType], [data, dataType], ...]
|
||||
|
||||
return -- packet bytes
|
||||
"""
|
||||
|
||||
# Set some variables
|
||||
packetData = bytes()
|
||||
packetLength = 0
|
||||
packetBytes = bytes()
|
||||
|
||||
# Pack packet data
|
||||
for i in __packetData:
|
||||
packetData += packData(i[0], i[1])
|
||||
|
||||
# Set packet length
|
||||
packetLength = len(packetData)
|
||||
|
||||
# Return packet as bytes
|
||||
packetBytes += struct.pack("<h", __packet) # packet id (int16)
|
||||
packetBytes += bytes(b"\x00") # unused byte
|
||||
packetBytes += struct.pack("<l", packetLength) # packet lenght (iint32)
|
||||
packetBytes += packetData # packet data
|
||||
return packetBytes
|
||||
|
||||
|
||||
def readPacketID(__stream):
|
||||
"""
|
||||
Read packetID from __stream (0-1 bytes)
|
||||
|
||||
__stream -- data stream
|
||||
return -- packet ID (int)
|
||||
"""
|
||||
|
||||
return unpackData(__stream[0:2], dataTypes.uInt16)
|
||||
|
||||
|
||||
def readPacketLength(__stream):
|
||||
"""
|
||||
Read packet length from __stream (3-4-5-6 bytes)
|
||||
|
||||
__stream -- data stream
|
||||
return -- packet length (int)
|
||||
"""
|
||||
|
||||
return unpackData(__stream[3:7], dataTypes.uInt32)
|
||||
|
||||
|
||||
def readPacketData(__stream, __structure = [], __hasFirstBytes = True):
|
||||
"""
|
||||
Read packet data from __stream according to __structure
|
||||
|
||||
__stream -- data stream
|
||||
__structure -- [[name, dataType], [name, dataType], ...]
|
||||
__hasFirstBytes -- if True, __stream has packetID and length bytes.
|
||||
if False, __stream has only packetData.
|
||||
Optional. Default: True
|
||||
return -- dictionary. key: name, value: read data
|
||||
"""
|
||||
|
||||
# Read packet ID (first 2 bytes)
|
||||
data = {}
|
||||
|
||||
# Skip packet ID and packet length if needed
|
||||
if __hasFirstBytes == True:
|
||||
end = 7
|
||||
start = 7
|
||||
else:
|
||||
end = 0
|
||||
start = 0
|
||||
|
||||
# Read packet
|
||||
for i in __structure:
|
||||
start = end
|
||||
unpack = True
|
||||
if i[1] == dataTypes.string:
|
||||
# String, don't unpack
|
||||
unpack = False
|
||||
|
||||
# Check empty string
|
||||
if __stream[start] == 0:
|
||||
# Empty string
|
||||
data[i[0]] = ""
|
||||
end = start+1
|
||||
else:
|
||||
# Non empty string
|
||||
# Read length and calculate end
|
||||
length = uleb128Decode(__stream[start+1:])
|
||||
end = start+length[0]+length[1]+1
|
||||
|
||||
# Read bytes
|
||||
data[i[0]] = ''.join(chr(j) for j in __stream[start+1+length[1]:end])
|
||||
elif i[1] == dataTypes.byte:
|
||||
end = start+1
|
||||
elif i[1] == dataTypes.uInt16 or i[1] == dataTypes.sInt16:
|
||||
end = start+2
|
||||
elif i[1] == dataTypes.uInt32 or i[1] == dataTypes.sInt32:
|
||||
end = start+4
|
||||
elif i[1] == dataTypes.uInt64 or i[1] == dataTypes.sInt64:
|
||||
end = start+8
|
||||
|
||||
# Unpack if needed
|
||||
if unpack == True:
|
||||
data[i[0]] = unpackData(__stream[start:end], i[1])
|
||||
|
||||
return data
|
36
helpers/passwordHelper.py
Normal file
36
helpers/passwordHelper.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from helpers import cryptHelper
|
||||
import base64
|
||||
import bcrypt
|
||||
|
||||
def checkOldPassword(password, salt, rightPassword):
|
||||
"""
|
||||
Check if password+salt corresponds to rightPassword
|
||||
|
||||
password -- input password
|
||||
salt -- password's salt
|
||||
rightPassword -- right password
|
||||
return -- bool
|
||||
"""
|
||||
|
||||
return (rightPassword == cryptHelper.crypt(password, "$2y$"+str(base64.b64decode(salt))))
|
||||
|
||||
def checkNewPassword(password, dbPassword):
|
||||
"""
|
||||
Check if a password (version 2) is right.
|
||||
|
||||
password -- input password
|
||||
dbPassword -- the password in the database
|
||||
return -- bool
|
||||
"""
|
||||
password = password.encode("utf8")
|
||||
dbPassword = dbPassword.encode("utf8")
|
||||
return bcrypt.hashpw(password, dbPassword) == dbPassword
|
||||
|
||||
def genBcrypt(password):
|
||||
"""
|
||||
Bcrypts a password.
|
||||
|
||||
password -- the password to hash.
|
||||
return -- bytestring
|
||||
"""
|
||||
return bcrypt.hashpw(password.encode("utf8"), bcrypt.gensalt(10, b'2a'))
|
47
helpers/responseHelper.py
Normal file
47
helpers/responseHelper.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import flask
|
||||
import gzip
|
||||
|
||||
def generateResponse(token, data = None):
|
||||
"""
|
||||
Return a flask response with required headers for osu! client, token and gzip compressed data
|
||||
|
||||
token -- user token
|
||||
data -- plain response body
|
||||
return -- flask response
|
||||
"""
|
||||
|
||||
resp = flask.Response(gzip.compress(data, 6))
|
||||
resp.headers['cho-token'] = token
|
||||
resp.headers['cho-protocol'] = '19'
|
||||
resp.headers['Keep-Alive'] = 'timeout=5, max=100'
|
||||
resp.headers['Connection'] = 'keep-alive'
|
||||
resp.headers['Content-Type'] = 'text/html; charset=UTF-8'
|
||||
resp.headers['Vary'] = 'Accept-Encoding'
|
||||
resp.headers['Content-Encoding'] = 'gzip'
|
||||
return resp
|
||||
|
||||
|
||||
def HTMLResponse():
|
||||
"""Return HTML bancho meme response"""
|
||||
|
||||
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.</pre></body></html>"
|
||||
return html
|
91
helpers/systemHelper.py
Normal file
91
helpers/systemHelper.py
Normal file
@@ -0,0 +1,91 @@
|
||||
from objects import glob
|
||||
from constants import serverPackets
|
||||
import psutil
|
||||
import os
|
||||
import sys
|
||||
|
||||
from helpers import consoleHelper
|
||||
from constants import bcolors
|
||||
import threading
|
||||
import signal
|
||||
|
||||
def runningUnderUnix():
|
||||
"""
|
||||
Get if the server is running under UNIX or NT
|
||||
|
||||
return --- True if running under UNIX, otherwise False
|
||||
"""
|
||||
|
||||
return True if os.name == "posix" else False
|
||||
|
||||
|
||||
def scheduleShutdown(sendRestartTime, restart, message = ""):
|
||||
"""
|
||||
Schedule a server shutdown/restart
|
||||
|
||||
sendRestartTime -- time (seconds) to wait before sending server restart packets to every client
|
||||
restart -- if True, server will restart. if False, server will shudown
|
||||
message -- if set, send that message to every client to warn about the shutdown/restart
|
||||
"""
|
||||
|
||||
# Console output
|
||||
consoleHelper.printColored("[!] Pep.py will {} in {} seconds!".format("restart" if restart else "shutdown", sendRestartTime+20), bcolors.PINK)
|
||||
consoleHelper.printColored("[!] Sending server restart packets in {} seconds...".format(sendRestartTime), bcolors.PINK)
|
||||
|
||||
# Send notification if set
|
||||
if message != "":
|
||||
glob.tokens.enqueueAll(serverPackets.notification(message))
|
||||
|
||||
# Schedule server restart packet
|
||||
threading.Timer(sendRestartTime, glob.tokens.enqueueAll, [serverPackets.banchoRestart(50000)]).start()
|
||||
glob.restarting = True
|
||||
|
||||
# Restart/shutdown
|
||||
if restart:
|
||||
action = restartServer
|
||||
else:
|
||||
action = shutdownServer
|
||||
|
||||
# Schedule actual server shutdown/restart 20 seconds after server restart packet, so everyone gets it
|
||||
threading.Timer(sendRestartTime+20, action).start()
|
||||
|
||||
|
||||
def restartServer():
|
||||
"""Restart pep.py script"""
|
||||
print("> Restarting pep.py...")
|
||||
os.execv(sys.executable, [sys.executable] + sys.argv)
|
||||
|
||||
|
||||
def shutdownServer():
|
||||
"""Shutdown pep.py"""
|
||||
print("> Shutting down pep.py...")
|
||||
sig = signal.SIGKILL if runningUnderUnix() else signal.CTRL_C_EVENT
|
||||
os.kill(os.getpid(), sig)
|
||||
|
||||
|
||||
def getSystemInfo():
|
||||
"""
|
||||
Get a dictionary with some system/server info
|
||||
|
||||
return -- ["unix", "connectedUsers", "webServer", "cpuUsage", "totalMemory", "usedMemory", "loadAverage"]
|
||||
"""
|
||||
|
||||
data = {}
|
||||
|
||||
# Get if server is running under unix/nt
|
||||
data["unix"] = runningUnderUnix()
|
||||
|
||||
# General stats
|
||||
data["connectedUsers"] = len(glob.tokens.tokens)
|
||||
data["webServer"] = glob.conf.config["server"]["server"]
|
||||
data["cpuUsage"] = psutil.cpu_percent()
|
||||
data["totalMemory"] = "{0:.2f}".format(psutil.virtual_memory()[0]/1074000000)
|
||||
data["usedMemory"] = "{0:.2f}".format(psutil.virtual_memory()[3]/1074000000)
|
||||
|
||||
# Unix only stats
|
||||
if data["unix"] == True:
|
||||
data["loadAverage"] = os.getloadavg()
|
||||
else:
|
||||
data["loadAverage"] = (0,0,0)
|
||||
|
||||
return data
|
299
helpers/userHelper.py
Normal file
299
helpers/userHelper.py
Normal file
@@ -0,0 +1,299 @@
|
||||
from helpers import passwordHelper
|
||||
from constants import gameModes
|
||||
from helpers import generalFunctions
|
||||
from objects import glob
|
||||
|
||||
def getID(username):
|
||||
"""
|
||||
Get username's user ID
|
||||
|
||||
db -- database connection
|
||||
username -- user
|
||||
return -- user id or False
|
||||
"""
|
||||
|
||||
# Get user ID from db
|
||||
userID = glob.db.fetch("SELECT id FROM users WHERE username = ?", [username])
|
||||
|
||||
# Make sure the query returned something
|
||||
if userID == None:
|
||||
return False
|
||||
|
||||
# Return user ID
|
||||
return userID["id"]
|
||||
|
||||
|
||||
def checkLogin(userID, password):
|
||||
"""
|
||||
Check userID's login with specified password
|
||||
|
||||
db -- database connection
|
||||
userID -- user id
|
||||
password -- plain md5 password
|
||||
return -- True or False
|
||||
"""
|
||||
|
||||
# Get password data
|
||||
passwordData = glob.db.fetch("SELECT password_md5, salt, password_version FROM users WHERE id = ?", [userID])
|
||||
|
||||
# Make sure the query returned something
|
||||
if passwordData == None:
|
||||
return False
|
||||
|
||||
|
||||
# Return valid/invalid based on the password version.
|
||||
if passwordData["password_version"] == 2:
|
||||
return passwordHelper.checkNewPassword(password, passwordData["password_md5"])
|
||||
if passwordData["password_version"] == 1:
|
||||
ok = passwordHelper.checkOldPassword(password, passwordData["salt"], passwordData["password_md5"])
|
||||
if not ok: return False
|
||||
newpass = passwordHelper.genBcrypt(password)
|
||||
glob.db.execute("UPDATE users SET password_md5=?, salt='', password_version='2' WHERE id = ?", [newpass, userID])
|
||||
|
||||
|
||||
def exists(userID):
|
||||
"""
|
||||
Check if userID exists
|
||||
|
||||
userID -- user ID to check
|
||||
return -- bool
|
||||
"""
|
||||
|
||||
result = glob.db.fetch("SELECT id FROM users WHERE id = ?", [userID])
|
||||
if result == None:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def getAllowed(userID):
|
||||
"""
|
||||
Get allowed status for userID
|
||||
|
||||
db -- database connection
|
||||
userID -- user ID
|
||||
return -- allowed int
|
||||
"""
|
||||
|
||||
return glob.db.fetch("SELECT allowed FROM users WHERE id = ?", [userID])["allowed"]
|
||||
|
||||
|
||||
def getRankPrivileges(userID):
|
||||
"""
|
||||
This returns rank **(PRIVILEGES)**, not game rank (like #1337)
|
||||
If you want to get that rank, user getUserGameRank instead
|
||||
"""
|
||||
|
||||
return glob.db.fetch("SELECT rank FROM users WHERE id = ?", [userID])["rank"]
|
||||
|
||||
|
||||
def getSilenceEnd(userID):
|
||||
"""
|
||||
Get userID's **ABSOLUTE** silence end UNIX time
|
||||
Remember to subtract time.time() to get the actual silence time
|
||||
|
||||
userID -- userID
|
||||
return -- UNIX time
|
||||
"""
|
||||
|
||||
return glob.db.fetch("SELECT silence_end FROM users WHERE id = ?", [userID])["silence_end"]
|
||||
|
||||
|
||||
def silence(userID, silenceEndTime, silenceReason):
|
||||
"""
|
||||
Set userID's **ABSOLUTE** silence end UNIX time
|
||||
Remember to add time.time() to the silence length
|
||||
|
||||
userID -- userID
|
||||
silenceEndtime -- UNIX time when the silence ends
|
||||
silenceReason -- Silence reason shown on website
|
||||
"""
|
||||
|
||||
glob.db.execute("UPDATE users SET silence_end = ?, silence_reason = ? WHERE id = ?", [silenceEndTime, silenceReason, userID])
|
||||
|
||||
def getRankedScore(userID, gameMode):
|
||||
"""
|
||||
Get userID's ranked score relative to gameMode
|
||||
|
||||
userID -- userID
|
||||
gameMode -- int value, see gameModes
|
||||
return -- ranked score
|
||||
"""
|
||||
|
||||
modeForDB = gameModes.getGameModeForDB(gameMode)
|
||||
return glob.db.fetch("SELECT ranked_score_"+modeForDB+" FROM users_stats WHERE id = ?", [userID])["ranked_score_"+modeForDB]
|
||||
|
||||
|
||||
def getTotalScore(userID, gameMode):
|
||||
"""
|
||||
Get userID's total score relative to gameMode
|
||||
|
||||
userID -- userID
|
||||
gameMode -- int value, see gameModes
|
||||
return -- total score
|
||||
"""
|
||||
|
||||
modeForDB = gameModes.getGameModeForDB(gameMode)
|
||||
return glob.db.fetch("SELECT total_score_"+modeForDB+" FROM users_stats WHERE id = ?", [userID])["total_score_"+modeForDB]
|
||||
|
||||
|
||||
def getAccuracy(userID, gameMode):
|
||||
"""
|
||||
Get userID's average accuracy relative to gameMode
|
||||
|
||||
userID -- userID
|
||||
gameMode -- int value, see gameModes
|
||||
return -- accuracy
|
||||
"""
|
||||
|
||||
modeForDB = gameModes.getGameModeForDB(gameMode)
|
||||
return glob.db.fetch("SELECT avg_accuracy_"+modeForDB+" FROM users_stats WHERE id = ?", [userID])["avg_accuracy_"+modeForDB]
|
||||
|
||||
|
||||
def getGameRank(userID, gameMode):
|
||||
"""
|
||||
Get userID's **in-game rank** (eg: #1337) relative to gameMode
|
||||
|
||||
userID -- userID
|
||||
gameMode -- int value, see gameModes
|
||||
return -- game rank
|
||||
"""
|
||||
|
||||
modeForDB = gameModes.getGameModeForDB(gameMode)
|
||||
result = glob.db.fetch("SELECT position FROM leaderboard_"+modeForDB+" WHERE user = ?", [userID])
|
||||
if result == None:
|
||||
return 0
|
||||
else:
|
||||
return result["position"]
|
||||
|
||||
|
||||
def getPlaycount(userID, gameMode):
|
||||
"""
|
||||
Get userID's playcount relative to gameMode
|
||||
|
||||
userID -- userID
|
||||
gameMode -- int value, see gameModes
|
||||
return -- playcount
|
||||
"""
|
||||
|
||||
modeForDB = gameModes.getGameModeForDB(gameMode)
|
||||
return glob.db.fetch("SELECT playcount_"+modeForDB+" FROM users_stats WHERE id = ?", [userID])["playcount_"+modeForDB]
|
||||
|
||||
|
||||
def getUsername(userID):
|
||||
"""
|
||||
Get userID's username
|
||||
|
||||
userID -- userID
|
||||
return -- username
|
||||
"""
|
||||
|
||||
return glob.db.fetch("SELECT username FROM users WHERE id = ?", [userID])["username"]
|
||||
|
||||
|
||||
def getFriendList(userID):
|
||||
"""
|
||||
Get userID's friendlist
|
||||
|
||||
userID -- userID
|
||||
return -- list with friends userIDs. [0] if no friends.
|
||||
"""
|
||||
|
||||
# Get friends from db
|
||||
friends = glob.db.fetchAll("SELECT user2 FROM users_relationships WHERE user1 = ?", [userID])
|
||||
|
||||
if friends == None or len(friends) == 0:
|
||||
# We have no friends, return 0 list
|
||||
return [0]
|
||||
else:
|
||||
# Get only friends
|
||||
friends = [i["user2"] for i in friends]
|
||||
|
||||
# Return friend IDs
|
||||
return friends
|
||||
|
||||
|
||||
def addFriend(userID, friendID):
|
||||
"""
|
||||
Add friendID to userID's friend list
|
||||
|
||||
userID -- user
|
||||
friendID -- new friend
|
||||
"""
|
||||
|
||||
# Make sure we aren't adding us to our friends
|
||||
if userID == friendID:
|
||||
return
|
||||
|
||||
# check user isn't already a friend of ours
|
||||
if glob.db.fetch("SELECT id FROM users_relationships WHERE user1 = ? AND user2 = ?", [userID, friendID]) != None:
|
||||
return
|
||||
|
||||
# Set new value
|
||||
glob.db.execute("INSERT INTO users_relationships (user1, user2) VALUES (?, ?)", [userID, friendID])
|
||||
|
||||
|
||||
def removeFriend(userID, friendID):
|
||||
"""
|
||||
Remove friendID from userID's friend list
|
||||
|
||||
userID -- user
|
||||
friendID -- old friend
|
||||
"""
|
||||
|
||||
# Delete user relationship. We don't need to check if the relationship was there, because who gives a shit,
|
||||
# if they were not friends and they don't want to be anymore, be it. ¯\_(ツ)_/¯
|
||||
glob.db.execute("DELETE FROM users_relationships WHERE user1 = ? AND user2 = ?", [userID, friendID])
|
||||
|
||||
|
||||
def getCountry(userID):
|
||||
"""
|
||||
Get userID's country **(two letters)**.
|
||||
Use countryHelper.getCountryID with what that function returns
|
||||
to get osu! country ID relative to that user
|
||||
|
||||
userID -- user
|
||||
return -- country code (two letters)
|
||||
"""
|
||||
|
||||
return glob.db.fetch("SELECT country FROM users_stats WHERE id = ?", [userID])["country"]
|
||||
|
||||
def getPP(userID, gameMode):
|
||||
"""
|
||||
Get userID's PP relative to gameMode
|
||||
|
||||
userID -- user
|
||||
return -- PP
|
||||
"""
|
||||
|
||||
modeForDB = gameModes.getGameModeForDB(gameMode)
|
||||
return glob.db.fetch("SELECT pp_{} FROM users_stats WHERE id = ?".format(modeForDB), [userID])["pp_{}".format(modeForDB)]
|
||||
|
||||
def setAllowed(userID, allowed):
|
||||
"""
|
||||
Set userID's allowed status
|
||||
|
||||
userID -- user
|
||||
allowed -- allowed status. 1: normal, 0: banned
|
||||
"""
|
||||
glob.db.execute("UPDATE users SET allowed = ? WHERE id = ?", [allowed, userID])
|
||||
|
||||
def setCountry(userID, country):
|
||||
"""
|
||||
Set userID's country (two letters)
|
||||
|
||||
userID -- userID
|
||||
country -- country letters
|
||||
"""
|
||||
glob.db.execute("UPDATE users_stats SET country = ? WHERE id = ?", [country, userID])
|
||||
|
||||
def getShowCountry(userID):
|
||||
"""
|
||||
Get userID's show country status
|
||||
|
||||
userID -- userID
|
||||
return -- True if country is shown, False if it's hidden
|
||||
"""
|
||||
country = glob.db.fetch("SELECT show_country FROM users_stats WHERE id = ?", [userID])
|
||||
if country == None:
|
||||
return False
|
||||
return generalFunctions.stringToBool(country)
|
Reference in New Issue
Block a user