summaryrefslogblamecommitdiff
path: root/pluginvalidator/validate.py
blob: 796bd26b48a3e95ae88416b630a648fe5ad9739b (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

















                                           


                
















































                                                                       


                                                            




                                                       
                                                           


                                               
                                                   


























                                                   



                                                                                   





                                             
                             










































                                                                                          
                                              


                                                                       
                        






                                                                                               
                                       






                                                                                       
                                   











                                                                                 
                                       
                                                        
















                                                                                
                                    














                                                      

                     
#! /usr/bin/env python2.7
# -*- coding: utf8 -*-
#
# Copyright (C) 2015  Evol Online
# Author: Andrei Karas (4144)

import os
import re

serverSrcPath = "../../server-code/src/"
pluginSrcPath = "../../server-plugin/src/"
filtC = re.compile(".+[.]c", re.IGNORECASE)
filtH = re.compile(".+[.]h", re.IGNORECASE)
comaSplit = re.compile(",")

class Session:
    pass

class Status:
    exitCode = 0

def walkFiles(parentDir, session, filt):
    files = os.listdir(parentDir) 
    for file1 in files:
        if file1[0] == ".":
            continue
        file2 = os.path.abspath(parentDir + os.path.sep + file1)
        if not os.path.isfile(file2):
            walkFiles(file2, session, filt)
        elif filt.search(file1):
            session.func(file2, session)


def extractPlugFuncions(path, session):
    with open(path, "r") as f:
        for line in f:
            for s in session.funcList:
                idx = line.find(" " + s)
                if idx < 0:
                    idx = line.find("*" + s)
                if idx > 0 and line.find("__attribute__((nonnull") < 0:
                    if len(line) < idx + len(s) + 1:
                        continue
                    ch = line[idx + len(s) + 1]
                    if ch != '(' and ch != ' ' and ch != '\t':
                        continue
                    data = line
                    if data.find(");") < 0:
                        for line in f:
                            data = data + line
                            if data.find(");") > 1:
                                session.decls[s] = data
                                break
                    else:
                        session.decls[s] = data

def extractFuncFuncions(path, session):
    with open(path, "r") as f:
        for line in f:
            for s in session.funcList:
                idx = line.find(" " + s)
                if idx < 0:
                    idx = line.find("*" + s)
                if idx > 0 and line.find("__attribute__((nonnull") < 0:
                    if len(line) < idx + len(s) + 1:
                        continue
                    ch = line[idx + len(s) + 1]
                    if ch != '(' and ch != ' ' and ch != '\t':
                        continue
                    data = line
                    line2 = line.strip()
                    if len(line2) > 2 and line2[:2] == "//":
                        continue
                    if data.find(")") < 0:
                        for line in f:
                            data = data + line
                            if data.find(")") > 1:
                                session.decls[s] = data
#                                print "'{0}'".format(data)
                                break
                    else:
                        session.decls[s] = data
#                        print "'{0}'".format(data)

def loadHookedList(name):
    funcToPlug = dict()
    plugToFunc = dict()
    with open(name, "r") as f:
        for line in f:
            idx = line.find("addHook")
            if idx > 0:
                idx = line.find(",")
                idx2 = line.find(")")
                plug = line[idx + 1 : idx2].strip()
                func = plug[1:]
                if func[-5:] == "_post":
                    func = func[:-5]
                if func[-4:] == "_pre":
                    func = func[:-4]
                if func not in funcToPlug:
                    funcToPlug[func] = set()
                funcToPlug[func].add(plug);
                if plug not in plugToFunc:
                    plugToFunc[plug] = set()
                plugToFunc[plug].add(func);
    return (funcToPlug, plugToFunc)

def checkMissingFunctions(session):
    for f in session.funcList:
        if f not in session.decls:
            # ignore false positive
            if f != "login_parse_ping":
                print "Missing function {0} and {1}".format(f, session.funcList[f])
                Status.exitCode = 1

def decodeTypes(funcs):
    arr = dict()
    for f in funcs:
        #print "{0}: {1}".format(f, funcs[f])
        fstr = funcs[f]
#        idx = fstr.find("(")
        fstr = fstr[fstr.find("(") + 1 : fstr.rfind(")")]
        parts = comaSplit.split(fstr)
        lst = []
        cnt = 0
        if funcs[f][:5] == "void " and f[-5:] == "_post":
            lst.append(("-", "-"))
            cnt = cnt + 1
        for part in parts:
            part = part.strip()
            idx1 = part.rfind(" ")
            idx2 = part.rfind("*")
            if idx2 > idx1:
                idx1 = idx2
            if idx1 < 0:
                old = part
            else:
                # skip retVal first parameters
                if cnt == 0 and part[idx1 + 1:].lower() == "retval" and f[-5:] == "_post":
                    lst.append(("-", "-"))
                    continue
                old = part[:idx1 + 1]
            old = old.replace("\r", "")
            old = old.replace("\n", "")
            part = old.replace(" ", "")
            lst.append((old, part))
            cnt = cnt + 1
            #print "'{0}' -> '{1}'".format(old, part)
        arr[f] = lst
    return arr

def fixParam(param):
    if param[-6:] == "*const":
        param = param[:-5]
    if param == "structmap_session_data*":
        param = "TBL_PC*"
    if param == "structflooritem_data*":
        param = "TBL_ITEM*"
    return param

def compareFuncs(session):
    for func in session.plugDecls:
        pfunc = session.plugDecls[func]
        for func2 in session.plugToFunc[func]:
            if func2 not in session.funcDecls:
                if func2 != "login_parse_ping":
                    print "Error: function {0} not found".format(func2)
                    Status.exitCode = 1
                continue
            ffunc = session.funcDecls[func2]
#            print "{0} - {1}".format(func, func2)
#            print pfunc
#            print ffunc
            if func[-5:] == "_post":
                if pfunc[0][1] != "-":
                    print "Error: missing first retVal parameter in function {0}.".format(func)
                    Status.exitCode = 1
                else:
                    pfunc = pfunc[1:]
            sz1 = len(pfunc)
            sz2 = len(ffunc)
#            print "{0} - {1}".format(sz1, sz2)
            if sz1 != sz2:
                print "Error: wrong number of parameters in function {0}.".format(func)
                Status.exitCode = 1
                continue
            for idx in range(0, sz1):
                pf = pfunc[idx][1]
                ff = ffunc[idx][1]
                pf = fixParam(pf)
                ff = fixParam(ff)
                if ff[-1] != "*" and ff != "void" and ff != "va_list":
                    ff = ff + "*"
#                print pf
#                print ff
                if pf != ff:
                    print "Error: wrong parameters in function {0}.".format(func)
                    Status.exitCode = 1
##                    print "{0} vs {1}".format(pf, ff);
                    break

def processServer(name1, name2):
    (funcToPlug, plugToFunc) = loadHookedList(pluginSrcPath + name2 + "/init.c")
    session = Session()
    session.func = extractPlugFuncions
    session.funcList = plugToFunc
    session.altList = funcToPlug
    session.funcToPlug = funcToPlug
    session.plugToFunc = plugToFunc
    session.decls = dict()
    walkFiles(pluginSrcPath + name2, session, filtH)
    checkMissingFunctions(session)
    session.plugDecls = session.decls
    session.plugDecls = decodeTypes(session.plugDecls)
    #print session.plugDecls

#    pluginfunctions = session.decls
    session.func = extractFuncFuncions
    session.funcList = funcToPlug
    session.altList = plugToFunc
    session.decls = dict()
    walkFiles(serverSrcPath + name1, session, filtC)
    checkMissingFunctions(session)
    session.funcDecls = session.decls
    session.funcDecls = decodeTypes(session.funcDecls)
    #print session.funcDecls

    compareFuncs(session)

processServer("login", "elogin")
processServer("char", "echar")
processServer("map", "emap")

exit(Status.exitCode)