def handleMove(environ, start_response): user = environ['user'] missionJsonString = utils.environToContents(environ) missionJson = json.loads(missionJsonString) missionId = missionJson['missionId'] versionId = missionJson['versionId'] # If you're a MM user and this is your mission, or you're a low admin if not (utils.checkUserPermissions( user, 1, missionId=missionId, collector=utils.AND) or utils.checkUserPermissions(user, 2)): start_response("403 Permission Denied", []) return ["Access Denied"] c = utils.getCursor() c.execute("select name from versions where id = ?", [versionId]) fileName = c.fetchone()[0] if Path(utils.missionMakerDir + "/" + fileName).is_file(): copyfile(utils.missionMakerDir + "/" + fileName, utils.missionMainDir + "/" + fileName) c.execute("update versions set existsOnMain=1 where id = ?", [versionId]) c.connection.commit() c.connection.close() start_response("200 OK", []) return []
def handleSaveMission(environ, start_response): c = utils.getCursor() missionJsonString = utils.environToContents(environ) missionJson = json.loads(missionJsonString) # create a blank row, then update that row so that we don't have 2 different statements if 'missionId' not in missionJson: c.execute("insert into missions (missionName) values (?)", [missionJson['missionName']]) c.execute("select max(id) from missions") missionId = c.fetchone()[0] hasPermissions = utils.checkUserPermissions(environ['user'], 0) else: missionId = missionJson['missionId'] c.execute("select * from missions where id = ?", [missionId]) mission = c.fetchone() if mission is None: start_response("500 Internal Server Response", []) return [ "Stop trying to edit a mission that doesn't exist".encode() ] hasPermissions = utils.checkUserPermissions(environ['user'], 2, missionId) if not hasPermissions: start_response("403 Permission Denied", []) return ["Access Denied"] query, params = constructQuery(missionJson) params.append(missionId) c.execute("update missions set " + query + "where id=?", params) c.connection.commit() c.connection.close() start_response("201 Created", []) return [(str(missionId)).encode()]
def handleComment(environ, start_response): user = environ['user'] missionJsonString = utils.environToContents(environ) missionJson = json.loads(missionJsonString) missionId = missionJson['missionId'] comment = html.escape(missionJson['comment']) rejection = True if 'rejection' in missionJson and missionJson['rejection'] == True else False # If you're a MM user and this is your mission, or you're a low admin if rejection and not ( utils.checkUserPermissions(user, 1, missionId=missionId, collector=utils.AND) or utils.checkUserPermissions( user, 2)): start_response("403 Permission Denied", []) return ["Access Denied"] c = utils.getCursor() c.execute("select name, id from versions where id = (select max(id) from versions where missionId = ?)", [missionId]) versionRow = c.fetchone() fileName = versionRow[0] versionId = versionRow[1] if rejection: c.execute("UPDATE missions SET status ='Broken' WHERE id = ?", [missionId]) c.execute("UPDATE versions SET requestedTransfer=0, requestedTesting=0 WHERE id = ?", [versionId]) c.execute("insert into comments (missionId, user, contents, createDate, versionId) values (?,?,?,?, ?)", [missionId, user.login, comment, datetime.now(), versionId]) if utils.discordHookUrl != '': c.execute("select missionName, missionAuthor from missions where id = ?", [missionId]) missionFromDb = c.fetchone() missionName = missionFromDb[0] unawareAuthors = filter(lambda author: author.strip() != user.login, [author for author in missionFromDb[1].split(",")]) missionAuthorDiscordIds = filter(None, [authorToUser(author) for author in unawareAuthors]) # Only send the message if there is at least one ID to send to if len(list(missionAuthorDiscordIds)) > 0: missionAuthorDiscordIds = ['<@' + discordId + ">" for discordId in missionAuthorDiscordIds] if rejection: payload = { 'content': 'Despair ' + ' '.join(missionAuthorDiscordIds) + '! ' + fileName + ' has been rejected'} else: payload = {'content': ' '.join(missionAuthorDiscordIds) + '! ' + missionName + ' has a new comment. '} r = requests.post(utils.discordHookUrl, data=payload) c.connection.commit() c.connection.close() start_response("200 OK", []) return []
def handleMove(environ, start_response): user = environ['user'] missionJsonString = utils.environToContents(environ) missionJson = json.loads(missionJsonString) missionId = missionJson['missionId'] versionId = missionJson['versionId'] # If you're a MM user and this is your mission, or you're a low admin if not (utils.checkUserPermissions( user, 1, missionId=missionId, collector=utils.AND) or utils.checkUserPermissions(user, 2)): start_response("403 Permission Denied", []) return ["Access Denied"] c = utils.getCursor() c.execute("select name from versions where id = ?", [versionId]) fileName = c.fetchone()[0] if Path(utils.missionMakerDir + "/" + fileName).exists(): copyfile(utils.missionMakerDir + "/" + fileName, utils.missionMainDir + "/" + fileName) c.execute( "UPDATE versions SET existsOnMain=1, requestedTransfer=0, requestedTesting=0 WHERE id = ?", [versionId]) c.execute("update missions set status ='Ready' where id = ?", [missionId]) if utils.discordHookUrl != '': c.execute("select missionAuthor from missions where id = ?", [missionId]) missionFromDb = c.fetchone() missionAuthorDiscordIds = filter( None, [authorToUser(author) for author in missionFromDb[0].split(",")]) missionAuthorDiscordIds = [ '<@' + discordId + ">" for discordId in missionAuthorDiscordIds ] payload = { 'content': 'Rejoice ' + ' '.join(missionAuthorDiscordIds) + '! ' + fileName + ' has been accepted' } r = requests.post(utils.discordHookUrl, data=payload) c.connection.commit() c.connection.close() start_response("200 OK", []) return []
def handleTesting(environ, start_response): missionJsonString = utils.environToContents(environ) missionJson = json.loads(missionJsonString) missionId = missionJson['missionId'] versionId = missionJson['versionId'] # If you're a MM user and this is your mission, or you're a low admin if not utils.checkUserPermissions(environ['user'], 1, missionId): start_response("403 Permission Denied", []) return ["Access Denied"] c = utils.getCursor() c.execute("SELECT name FROM versions WHERE id = ?", [versionId]) version = c.fetchone() fileName = version[0] if Path(utils.missionMakerDir + "/" + fileName).is_file(): c.execute("UPDATE missions SET status='Testing' WHERE id = ?", [missionId]) c.execute("UPDATE versions SET requestedTesting=1 WHERE id = ?", [versionId]) if utils.discordHookUrl != '': c.execute("SELECT missionName, missionAuthor FROM missions WHERE id = ?", [missionId]) missionStuff = c.fetchone() missionName = missionStuff[0] missionAuthor = missionStuff[1] payload = {'content': 'Rejoice Comrades! ' + missionAuthor + ' has prepared a new adventure for us!\n' + missionName + ' now has ' + fileName + ' requested for testing.'} r = requests.post(utils.discordHookUrl, data=payload) c.connection.commit() c.connection.close() start_response("200 OK", []) return []
def handlePermissionLevel(environ, start_response): c = utils.getCursor() permissionLevelString = utils.environToContents(environ) permissionLevelJson = json.loads(permissionLevelString) userId = permissionLevelJson['id'] permissionLevel = permissionLevelJson['permissionLevel'] if utils.checkUserPermissions(environ['user'], 3): c.execute("update users set permissionLevel = ? where id = ?", [permissionLevel, userId]) c.execute("select * from users where permissionLevel = 3") if c.fetchone() is None: start_response("500 Internal Server Error", []) return [ "No admin users found, there must be at least one high admin (3)" .encode() ] else: start_response("200 OK", []) c.connection.commit() c.connection.close() else: start_response("403 Permission Denied", []) return ["Access Denied".encode()] return []
def handleCleanup(environ, start_response): c = utils.getCursor() # if you're a low admin if not utils.checkUserPermissions(environ['user'], 2): start_response("403 Permission Denied", []) return ["Access Denied"] for origin in ['main', 'missionMaker']: if origin == 'main': missionDirPrefix = utils.missionMainDir missionArchivePrefix = utils.missionMainArchive toBeArchivedProperty = 'toBeArchivedMain' toBeDeletedProperty = 'toBeDeletedMain' existsProperty = 'existsOnMain' else: missionDirPrefix = utils.missionMakerDir missionArchivePrefix = utils.missionMakerArchive toBeArchivedProperty = 'toBeArchivedMM' toBeDeletedProperty = 'toBeDeletedMM' existsProperty = 'existsOnMM' c.execute('''select * from versions where ''' + toBeArchivedProperty + ''' = 1''') toBeArchived = c.fetchall() for forArchival in toBeArchived: with open(missionDirPrefix + "/" + forArchival['name'], 'rb') as f_in: with gzip.open( os.path.join(missionArchivePrefix, forArchival['name'] + ".gz"), 'wb') as f_out: shutil.copyfileobj(f_in, f_out) os.remove(f_in.name) c.execute('''select * from versions where ''' + toBeDeletedProperty + ''' = 1''') toBeDeleted = c.fetchall() for deleteMe in toBeDeleted: os.remove(os.path.join(missionDirPrefix, deleteMe['name'])) c.execute("update versions set " + existsProperty + " = 0, " + toBeArchivedProperty + " = 0, " + toBeDeletedProperty + " = 0 where " + toBeArchivedProperty + " = 1 or " + toBeDeletedProperty + " = 1") c.connection.commit() c.connection.close() start_response("200 OK", []) return []
def handleSessionDelete(environ, start_response): c = utils.getCursor() sessionJsonString = utils.environToContents(environ) sessionJson = json.loads(sessionJsonString) sessionId = sessionJson['sessionId'] if not utils.checkUserPermissions(environ['user'], 2): start_response("403 Permission Denied", []) return ["Access Denied"] c.execute("delete from sessions where id = ?", [sessionId]) c.connection.commit() c.connection.close() start_response("200 OK", []) return []
def handleEditSession(environ, start_response): c = utils.getCursor() editSessionString = utils.environToContents(environ) editSessionJson = json.loads(editSessionString) sessionDate = editSessionJson['date'] # if you're a low admin if not utils.checkUserPermissions(environ['user'], 2): start_response("403 Permission Denied", []) return ["Access Denied".encode()] if 'id' not in editSessionJson: c.execute("insert into sessions (date) values (?)", [sessionDate]) c.execute("select max(id) from sessions") sessionId = c.fetchone()[0] else: sessionId = editSessionJson['id'] c.execute("select missionNames from sessions where id = ?", [sessionId]) existingMissionNames = c.fetchone() c.execute( "update sessions set missionNames = ?, host = ?, name = ?, players = ?, date = ? where id = ? ", toParams(editSessionJson, sessionId)) missions = editSessionJson['missionNames'] newMissions = [] if existingMissionNames['missionNames'] is not None: existingMissionNames = existingMissionNames['missionNames'].split(",") for mission in missions: if mission in existingMissionNames: existingMissionNames.remove(mission) else: newMissions.append(mission) else: newMissions = missions for mission in newMissions: c.execute( "update missions set playedCounter = playedCounter+1, lastPlayed=? where missionName =?", [sessionDate, mission]) c.connection.commit() c.connection.close() start_response("201 Created", []) return [("location:" + str(sessionId)).encode()]
def handleMissionDelete(environ, start_response): c = utils.getCursor() missionJsonString = utils.environToContents(environ) missionJson = json.loads(missionJsonString) missionId = missionJson['missionId'] # if you're a low admin or this is your mission if not utils.checkUserPermissions(environ['user'], 2, missionId): start_response("403 Permission Denied", []) return ["Access Denied"] c.execute("delete from versions where missionId = ?", [missionId]) c.execute("delete from missions where id = ?", [missionId]) c.connection.commit() c.connection.close() start_response("200 OK", []) return []
def handleCleanup(environ, start_response): c = utils.getCursor() # if you're a low admin if not utils.checkUserPermissions(environ['user'], 2): start_response("403 Permission Denied", []) return ["Access Denied"] for origin in ['main', 'missionMaker']: if origin == 'main': missionDirPrefix = utils.missionMainDir toBeDeletedProperty = 'toBeDeletedMain' existsProperty = 'existsOnMain' else: missionDirPrefix = utils.missionMakerDir toBeDeletedProperty = 'toBeDeletedMM' existsProperty = 'existsOnMM' c.execute('''select * from versions where ''' + toBeDeletedProperty + ''' = 1''') toBeDeleted = c.fetchall() for deleteMe in toBeDeleted: try: os.remove(os.path.join(missionDirPrefix, deleteMe['name'])) except OSError: pass c.execute("update versions set " + existsProperty + " = 0, " + toBeDeletedProperty + " = 0 where " + toBeDeletedProperty + " = 1") c.execute( "SELECT id FROM versions WHERE existsOnMM = 0 AND existsOnMain = 0") versionRowsToBeDeleted = c.fetchall() ids = [str(x['id']) for x in versionRowsToBeDeleted] idParameter = ",".join(ids) c.execute( str.format('''DELETE FROM comments WHERE versionId IN ({})''', idParameter)) c.execute("delete from versions where existsOnMM = 0 and existsOnMain = 0") c.connection.commit() c.connection.close() start_response("200 OK", []) return []
def handleArchive(environ, start_response): c = utils.getCursor() missionJsonString = utils.environToContents(environ) missionJson = json.loads(missionJsonString) missionId = missionJson['missionId'] versionId = missionJson['versionId'] origin = missionJson['origin'] # if you're a low admin or this is your mission if not utils.checkUserPermissions(environ['user'], 2, missionId): start_response("403 Permission Denied", []) return ["Access Denied"] if origin == 'main': property = 'toBeArchivedMain' else: property = 'toBeArchivedMM' c.execute('''update versions set ''' + property + ''' = 1 where id = ?''', [versionId]) c.connection.commit() c.connection.close() start_response("200 OK", []) return []
def handleSync(environ, start_response): c = utils.getCursor() if utils.checkUserPermissions(environ['user'], 3): c.execute(str.format('''select * from versions order by missionId''')) versionsFromDb = c.fetchall() for version in versionsFromDb: existsOnMM = False if version['existsOnMM']: existsOnMM = True if not Path(utils.missionMakerDir + "/" + version['name']).is_file(): existsOnMM = False c.execute(str.format('''update versions set existsOnMM=0 where id = {}''', version['id'])) existsOnMain = False if version['existsOnMain']: existsOnMain = True if not Path(utils.missionMainDir + "/" + version['name']).is_file(): existsOnMain = False c.execute(str.format('''update versions set existsOnMain=0 where id = {}''', version['id'])) if not existsOnMain and not existsOnMM: c.execute(str.format('''delete from versions where id = {}''', version['id'])) c.connection.commit() c.connection.close() c = utils.getCursor() c.execute('''select id, missionName from missions''') missions = c.fetchall() fileNames = os.listdir(utils.missionMakerDir) filesGroupedByMissionName = {} for k, g in itertools.groupby(fileNames, lambda x: sanatizeFileName(x)): filesGroupedByMissionName[k] = list(g) c.execute('''select name from versions''') versionRows = c.fetchall() versions = [x['name'] for x in versionRows] # for each db mission # find all files that match # for each file # find out if file is in db # if not # add to db for mission in missions: sanitizedName = sanatizeMissionName(mission['missionName']) if sanitizedName in filesGroupedByMissionName: filesForThisMission = filesGroupedByMissionName[sanitizedName] for fileForThisMission in filesForThisMission: if not fileForThisMission in versions: c.execute( "insert into versions(missionId, name, createDate) values (?, ?, ?)", [mission['id'], fileForThisMission, date.today()]) c.execute("update versions set existsOnMM = 1 where name = ?", [fileForThisMission]) c.connection.commit() c.connection.close() c = utils.getCursor() fileNames = os.listdir(utils.missionMainDir) filesGroupedByMissionName = {} for k, g in itertools.groupby(fileNames, lambda x: sanatizeFileName(x)): filesGroupedByMissionName[k] = list(g) c.execute('''select name from versions''') versionRows = c.fetchall() versions = [x['name'] for x in versionRows] # for each db mission # find all files that match # for each file # find out if file is in db # if not # add to db for mission in missions: sanitizedName = sanatizeMissionName(mission['missionName']) if sanitizedName in filesGroupedByMissionName: filesForThisMission = filesGroupedByMissionName[sanitizedName] for fileForThisMission in filesForThisMission: if not fileForThisMission in versions: c.execute( "insert into versions(missionId, name, createDate, existsOnMM) values (?, ?, ?, ?)", [mission['id'], fileForThisMission, date.today(), False]) c.execute("update versions set existsOnMain = 1 where name = ?", [fileForThisMission]) c.connection.commit() c.connection.close() else: start_response("403 Permission Denied", []) return ["Access Denied".encode()] start_response("200 OK", []) return []
def handleUpload(environ, start_response): c = utils.getCursor() o = parse_qs(environ['QUERY_STRING']) missionId = o['missionId'][0] if not utils.checkUserPermissions(environ['user'], 2, missionId): start_response("403 Permission Denied", []) return ["Access Denied"] # This monstrosity of code was copied from the internet, I barely understand how it works content_type = environ.get('CONTENT_TYPE', '0') if not content_type: start_response("500 Internal Server Error", []) return ["Content-Type header doesn't contain boundary".encode()] boundary = content_type.split("=")[1].encode() remainbytes = int(environ.get('CONTENT_LENGTH', '0')) if remainbytes > (20 * 1024 * 1024): start_response("500 Internal Server Error", []) return ["20 MB is the max size".encode()] line = environ['wsgi.input'].readline() remainbytes -= len(line) if not boundary in line: start_response("500 Internal Server Error", []) return ["Content NOT begin with boundary".encode()] line = environ['wsgi.input'].readline() remainbytes -= len(line) decode = line.decode() regex = r'Content-Disposition.*name="upload_file"; filename="(.*)".*' fn = re.findall(regex, decode) if not fn: start_response("500 Internal Server Error", []) return ["Can't find out file name...".encode()] fileName = fn[0] # protect from filesystem roaming fileName = fileName.replace("..", "") if not fileName.endswith(".pbo"): start_response("500 Internal Server Error", []) return ["Only .pbo files are allowed".encode()] fullPath = os.path.join(utils.missionMakerDir, fileName).replace("\n", "") line = environ['wsgi.input'].readline() remainbytes -= len(line) line = environ['wsgi.input'].readline() remainbytes -= len(line) try: out = open(fullPath, 'wb') except IOError: start_response("500 Internal Server Error", []) return ["Can't create file to write, do you have permission to write?".encode()] preline = environ['wsgi.input'].readline() remainbytes -= len(preline) while remainbytes > 0: line = environ['wsgi.input'].readline() remainbytes -= len(line) if boundary in line: preline = preline[0:-1] if preline.endswith(b'\r'): preline = preline[0:-1] out.write(preline) out.close() else: out.write(preline) preline = line # rest of the properties are set by defaults in the table c.execute( "INSERT INTO versions(missionId, name, createDate) VALUES (?, ?, ?)", [missionId, fileName, date.today()]) c.connection.commit() c.connection.close() start_response("200 OK", []) return ["success".encode()]