import os, sys, socket, random from urllib import quote_plus from panda3d.core import HTTPClient, HTTPCookie, URLSpec, Ramfile, Ostream, HTTPDate, DocumentSpec from direct.task.Task import Task from direct.directnotify.DirectNotifyGlobal import directNotify notify = directNotify.newCategory('UserFunnel') class UserFunnel: def __init__(self): self.hitboxAcct = 'DM53030620EW' self.language = 'en-us' self.cgRoot = 'ToonTown_Online' self.cgBeta = 'Beta' self.cgRelease = 'Release' self.cgLocation = 'US' self.campaignID = '' self.cfCookieFile = 'cf.txt' self.dynamicVRFunnel = 'http://download.toontown.com/' self.hostDict = {0: 'Internal Disney PHP Collector Site', 1: 'ehg-dig.hitbox.com/HG?', 2: 'ehg-dig.hitbox.com/HG?', 3: 'build64.online.disney.com:5020/index.php?'} self.CurrentHost = '' self.URLtoSend = '' self.gameName = 'ToonTown' self.browserName = 'Panda3D%20(' + self.gameName + ';%20' + sys.platform + ')' self.HTTPUserHeader = [('User-agent', 'Panda3D')] self.osMajorver = '' self.osMinorver = '' self.osRevver = '' self.osBuild = '' self.osType = '' self.osComments = '' self.msWinTypeDict = {0: 'Win32s on Windows 3.1', 1: 'Windows 95/98/ME', 2: 'Windows NT/2000/XP', 3: 'Windows CE'} self.milestoneDict = {0: 'New User', 1: 'Create Account', 2: 'View EULA', 3: 'Accept EULA', 4: 'Download Start', 5: 'Download End', 6: 'Installer Run', 7: 'Launcher Start', 8: 'Launcher Login', 9: 'Client Opens', 10: 'Create Pirate Loads', 11: 'Create Pirate Exit', 12: 'Cutscene One Start', 13: 'Cutscene One Ends', 14: 'Cutscene Two Start', 15: 'Cutscene Thee Start', 16: 'Cutscene Three Ends', 17: 'Access Cannon', 18: 'Cutscene Four Starts', 19: 'Cutscene Four Ends', 20: 'Dock - Start Game'} self.macTypeDict = {2: 'Jaguar', 1: 'Puma', 3: 'Panther', 4: 'Tiger', 5: 'Lepard'} self.milestone = '' self.pandaHTTPClientVarWSS = [] self.pandaHTTPClientVarCTG = [] self.pandaHTTPClientVarDM = [] self.checkForCFfile() self.httpSession = HTTPClient() self.whatOSver() def checkForCFfile(self): if firstRun() == True: pass elif os.path.isfile(self.cfCookieFile) == False: firstRun('write', True) def whatOSver(self): if sys.platform == 'win32': self.osMajorver = str(sys.getwindowsversion()[0]) self.osMinorver = str(sys.getwindowsversion()[1]) self.osBuild = str(sys.getwindowsversion()[2]) self.osType = str(sys.getwindowsversion()[3]) self.osComments = str(sys.getwindowsversion()[4]) return if sys.platform == 'darwin': self.osMajorver = '10' osxcmd = '/usr/sbin/system_profiler SPSoftwareDataType |/usr/bin/grep "System Version"' infopipe = os.popen(osxcmd, 'r') parseLine = infopipe.read() infopipe.close() del infopipe notify.info('parseLine = %s' % str(parseLine)) versionStringStart = parseLine.find('10.') notify.info('versionStringStart = %s' % str(versionStringStart)) testPlist = False try: self.osMinorver = parseLine[versionStringStart + 3] self.osRevver = parseLine[versionStringStart + 5:versionStringStart + 7].strip(' ') self.osBuild = parseLine[int(parseLine.find('(')) + 1:parseLine.find(')')] except: notify.info("couldn't parse the system_profiler output, using zeros") self.osMinorver = '0' self.osRevver = '0' self.osBuild = '0000' testPlist = True del versionStringStart del parseLine del osxcmd if testPlist: try: import plistlib pl = plistlib.readPlist('/System/Library/CoreServices/SystemVersion.plist') notify.info('pl=%s' % str(pl)) parseLine = pl['ProductVersion'] numbers = parseLine.split('.') notify.info('parseline =%s numbers =%s' % (parseLine, numbers)) self.osMinorver = numbers[1] self.osRevver = numbers[2] self.osBuild = pl['ProductBuildVersion'] except: notify.info('tried plist but still got exception') self.osMinorver = '0' self.osRevver = '0' self.osBuild = '0000' return def setmilestone(self, ms): if firstRun() == False: self.milestone = ms else: self.milestone = '%s_INITIAL' % ms def setgamename(self, gamename): self.gameName = gamename def printosComments(self): return self.osComments def setHost(self, hostID): self.CurrentHost = hostID def getFunnelURL(self): if patcherVer() == ['OFFLINE']: return if patcherVer() == []: patcherHTTP = HTTPClient() if checkParamFile() == None: patcherDoc = patcherHTTP.getDocument(URLSpec('http://download.toontown.com/english/currentVersion/content/patcher.ver')) vconGroup('w', self.cgRelease) else: patcherDoc = patcherHTTP.getDocument(URLSpec(checkParamFile())) vconGroup('w', self.cgBeta) rf = Ramfile() patcherDoc.downloadToRam(rf) self.patcherURL = rf.getData() if self.patcherURL.find('FUNNEL_LOG') == -1: patcherVer('w', 'OFFLINE') return self.patcherURL = self.patcherURL.split('\n') del rf del patcherDoc del patcherHTTP while self.patcherURL: self.confLine = self.patcherURL.pop() if self.confLine.find('FUNNEL_LOG=') != -1 and self.confLine.find('#FUNNEL_LOG=') == -1: self.dynamicVRFunnel = self.confLine[11:].strip('\n') patcherVer('w', self.confLine[11:].strip('\n')) else: self.dynamicVRFunnel = patcherVer()[0] return def isVarSet(self, varInQuestion): try: tempvar = type(varInQuestion) return 1 except NameError: return 0 def buildURL(self): if sys.platform == 'win32': hitboxOSType = 'c3' else: hitboxOSType = 'c4' if self.CurrentHost == 1: self.URLtoSend = 'http://' + self.hostDict[self.CurrentHost] + 'hb=' + str(self.hitboxAcct) + '&n=' + str(self.milestone) + '&ln=' + self.language + '&gp=STARTGAME&fnl=TOONTOWN_FUNNEL&vcon=/' + self.cgRoot + '/' + self.cgLocation + '/' + str(vconGroup()) + '&c1=' + str(sys.platform) + '&' + str(hitboxOSType) + '=' + str(self.osMajorver) + '_' + str(self.osMinorver) + '_' + str(self.osRevver) + '_' + str(self.osBuild) if self.CurrentHost == 2: self.URLtoSend = 'http://' + self.hostDict[self.CurrentHost] + 'hb=' + str(self.hitboxAcct) + '&n=' + str(self.milestone) + '&ln=' + self.language + '&vcon=/' + self.cgRoot + '/' + self.cgLocation + '/' + str(vconGroup()) + '&c1=' + str(sys.platform) + '&' + str(hitboxOSType) + '=' + str(self.osMajorver) + '_' + str(self.osMinorver) + '_' + str(self.osRevver) + '_' + str(self.osBuild) if self.CurrentHost == 0: localMAC = str(getMAC()) self.URLtoSend = str(self.dynamicVRFunnel) + '?funnel=' + str(self.milestone) + '&platform=' + str(sys.platform) + '&sysver=' + str(self.osMajorver) + '_' + str(self.osMinorver) + '_' + str(self.osRevver) + '_' + str(self.osBuild) + '&mac=' + localMAC + '&username=' + str(loggingSubID()) + '&id=' + str(loggingAvID()) def readInPandaCookie(self): thefile = open(self.cfCookieFile, 'r') thedata = thefile.read().split('\n') thefile.close() del thefile if thedata[0].find('Netscape HTTP Cookie File') != -1: return thedata.pop() try: while thedata: temp = thedata.pop() temp = temp.split('\t') domain = temp[0] loc = temp[1] variable = temp[2] value = temp[3] if variable == 'CTG': self.pandaHTTPClientVarCTG = [domain, loc, variable, value] self.setTheHTTPCookie(self.pandaHTTPClientVarCTG) if variable == self.hitboxAcct + 'V6': self.pandaHTTPClientVarDM = [domain, loc, variable, value] self.setTheHTTPCookie(self.pandaHTTPClientVarDM) if variable == 'WSS_GW': self.pandaHTTPClientVarWSS = [domain, loc, variable, value] self.setTheHTTPCookie(self.pandaHTTPClientVarWSS) except IndexError: print 'UserFunnel(Warning): Cookie Data file bad' del thedata def updateInstanceCookieValues(self): a = self.httpSession.getCookie(HTTPCookie('WSS_GW', '/', '.hitbox.com')) if a.getName(): self.pandaHTTPClientVarWSS = ['.hitbox.com', '/', 'WSS_GW', a.getValue()] b = self.httpSession.getCookie(HTTPCookie('CTG', '/', '.hitbox.com')) if b.getName(): self.pandaHTTPClientVarCTG = ['.hitbox.com', '/', 'CTG', b.getValue()] c = self.httpSession.getCookie(HTTPCookie(self.hitboxAcct + 'V6', '/', 'ehg-dig.hitbox.com')) if c.getName(): self.pandaHTTPClientVarDM = ['ehg-dig.hitbox.com', '/', self.hitboxAcct + 'V6', c.getValue()] del a del b del c def setTheHTTPCookie(self, cookieParams): c = HTTPCookie(cookieParams[2], cookieParams[1], cookieParams[0]) c.setValue(cookieParams[3]) self.httpSession.setCookie(c) def writeOutPandaCookie(self): try: thefile = open(self.cfCookieFile, 'w') if len(self.pandaHTTPClientVarWSS) == 4: thefile.write(self.pandaHTTPClientVarWSS[0] + '\t' + self.pandaHTTPClientVarWSS[1] + '\t' + self.pandaHTTPClientVarWSS[2] + '\t' + self.pandaHTTPClientVarWSS[3] + '\n') if len(self.pandaHTTPClientVarCTG) == 4: thefile.write(self.pandaHTTPClientVarCTG[0] + '\t' + self.pandaHTTPClientVarCTG[1] + '\t' + self.pandaHTTPClientVarCTG[2] + '\t' + self.pandaHTTPClientVarCTG[3] + '\n') if len(self.pandaHTTPClientVarDM) == 4: thefile.write(self.pandaHTTPClientVarDM[0] + '\t' + self.pandaHTTPClientVarDM[1] + '\t' + self.pandaHTTPClientVarDM[2] + '\t' + self.pandaHTTPClientVarDM[3] + '\n') thefile.close() except IOError: return def prerun(self): self.getFunnelURL() self.buildURL() if os.path.isfile(self.cfCookieFile) == True: if self.CurrentHost == 1 or self.CurrentHost == 2: self.readInPandaCookie() def run(self): if self.CurrentHost == 0 and patcherVer() == ['OFFLINE']: return self.nonBlock = self.httpSession.makeChannel(False) self.nonBlock.beginGetDocument(DocumentSpec(self.URLtoSend)) instanceMarker = str(random.randint(1, 1000)) instanceMarker = 'FunnelLoggingRequest-%s' % instanceMarker self.startCheckingAsyncRequest(instanceMarker) def startCheckingAsyncRequest(self, name): taskMgr.remove(name) taskMgr.doMethodLater(0.5, self.pollFunnelTask, name) def stopCheckingFunnelTask(self, name): taskMgr.remove('pollFunnelTask') def pollFunnelTask(self, task): result = self.nonBlock.run() if result == 0: self.stopCheckingFunnelTask(task) if self.CurrentHost == 1 or self.CurrentHost == 2: self.updateInstanceCookieValues() self.writeOutPandaCookie() else: return Task.again def logSubmit(setHostID, setMileStone): if __dev__: return trackItem = UserFunnel() trackItem.setmilestone(quote_plus(setMileStone)) trackItem.setHost(setHostID) trackItem.prerun() trackItem.run() def getVRSFunnelURL(): a = UserFunnel() a.getFunnelURL() class HitBoxCookie: def __init__(self): self.ieCookieDir = os.getenv('USERPROFILE') + '\\Cookies' self.pythonCookieFile = 'cf.txt' self.hitboxCookieFile = None self.ehgdigCookieFile = None self.hitboxAcct = 'DM53030620EW' self.ctg = None self.wss_gw = None self.dmAcct = None self.pythonCookieHeader = ' # Netscape HTTP Cookie File\n # http://www.netscape.com/newsref/std/cookie_spec.html\n # This is a generated file! Do not edit.\n\n' return def returnIECookieDir(self): return self.ieCookieDir def findIECookieFiles(self): try: sdir = os.listdir(self.ieCookieDir) except WindowsError: print 'Dir does not exist, do nothing' return while sdir: temp = sdir.pop() if temp.find('@hitbox[') != -1: self.hitboxCookieFile = temp if temp.find('@ehg-dig.hitbox[') != -1: self.ehgdigCookieFile = temp if self.hitboxCookieFile != None and self.ehgdigCookieFile != None: return 1 if self.hitboxCookieFile == None and self.ehgdigCookieFile == None: return 0 else: return -1 return def openHitboxFile(self, filename, type = 'python'): if type == 'ie': fullfile = self.ieCookieDir + '\\' + filename else: fullfile = filename cf = open(fullfile, 'r') data = cf.read() cf.close() return data def splitIECookie(self, filestream): return filestream.split('*\n') def sortIECookie(self, filestreamListElement): return [filestreamListElement.split('\n')[2], filestreamListElement.split('\n')[0], filestreamListElement.split('\n')[1]] def sortPythonCookie(self, filestreamListElement): return [filestreamListElement.split('\t')[0], filestreamListElement.split('\t')[5], filestreamListElement.split('\t')[6]] def writeIEHitBoxCookies(self): if self.ctg == None or self.wss_gw == None or self.dmAcct == None: return if sys.platform != 'win32': return self.findIECookieFiles() iecData = self.openHitboxFile(self.ehgdigCookieFile, 'ie') iecData = iecData.split('*\n') x = 0 while x < len(iecData): if iecData[x].find(self.hitboxAcct) != -1: iecData.pop(x) print 'Removed it from the list' break x += 1 iecWrite = open(self.ieCookieDir + '\\' + self.ehgdigCookieFile, 'w') while iecData: iecBuffer = iecData.pop() + '*\n' iecBuffer = iecBuffer.strip('/') if iecBuffer[0] == '.': iecBuffer = iecBuffer[1:] iecWrite.write(iecBuffer) tempDMBUFFER = self.dmAcct[0] if tempDMBUFFER[0].find('.') == 0: tempDMBUFFER = tempDMBUFFER[1:] iecWrite.write(self.dmAcct[1] + '\n' + self.dmAcct[2] + '\n' + tempDMBUFFER + '/\n*\n') iecWrite.close() del iecData del iecWrite del iecBuffer iecWrite = open(self.ieCookieDir + '\\' + self.hitboxCookieFile, 'w') iecBuffer = self.ctg[0] if iecBuffer[0] == '.': iecBuffer = iecBuffer[1:] if iecBuffer.find('/') == -1: iecBuffer = iecBuffer + '/' iecWrite.write(self.ctg[1] + '\n' + self.ctg[2] + '\n' + iecBuffer + '\n*\n') iecWrite.write(self.wss_gw[1] + '\n' + self.wss_gw[2] + '\n' + iecBuffer + '\n*\n') iecWrite.close() return def OLDwritePythonHitBoxCookies(self, filename = 'cf.txt'): if self.ctg == None or self.wss_gw == None or self.dmAcct == None: return outputfile = open(filename, 'w') outputfile.write(self.pythonCookieHeader) outputfile.write('.' + self.dmAcct[0].strip('/') + '\tTRUE\t/\tFALSE\t9999999999\t' + self.dmAcct[1] + '\t' + self.dmAcct[2] + '\n') outputfile.write('.' + self.ctg[0].strip('/') + '\tTRUE\t/\tFALSE\t9999999999\t' + self.ctg[1] + '\t' + self.ctg[2] + '\n') outputfile.write('.' + self.wss_gw[0].strip('/') + '\tTRUE\t/\tFALSE\t9999999999\t' + self.wss_gw[1] + '\t' + self.wss_gw[2] + '\n') outputfile.close() return def writePythonHitBoxCookies(self, filename = 'cf.txt'): if self.ctg == None or self.wss_gw == None or self.dmAcct == None: return outputfile = open(filename, 'w') outputfile.write('.' + self.dmAcct[0].strip('/') + '\t/\t' + self.dmAcct[1] + '\t' + self.dmAcct[2] + '\n') outputfile.write('.' + self.ctg[0].strip('/') + '\t/\t' + self.ctg[1] + '\t' + self.ctg[2] + '\n') outputfile.write('.' + self.wss_gw[0].strip('/') + '\t/\t' + self.wss_gw[1] + '\t' + self.wss_gw[2] + '\n') outputfile.close() return def loadPythonHitBoxCookies(self): if os.path.isfile(self.pythonCookieFile) != 1: return pythonStandard = self.openHitboxFile(self.pythonCookieFile, 'python') pythonStandard = pythonStandard.split('\n\n').pop(1) pythonStandard = pythonStandard.split('\n') for x in pythonStandard: if x.find('\t' + self.hitboxAcct) != -1: self.dmAcct = self.sortPythonCookie(x) if x.find('\tCTG\t') != -1: self.ctg = self.sortPythonCookie(x) if x.find('\tWSS_GW\t') != -1: self.wss_gw = self.sortPythonCookie(x) def loadIEHitBoxCookies(self): if self.findIECookieFiles() != 1: return if sys.platform != 'win32': return hitboxStandard = self.openHitboxFile(self.hitboxCookieFile, 'ie') hitboxDIG = self.openHitboxFile(self.ehgdigCookieFile, 'ie') hitboxStandard = self.splitIECookie(hitboxStandard) hitboxDIG = self.splitIECookie(hitboxDIG) ctg = None wss = None for x in hitboxStandard: if x.find('CTG\n') != -1: ctg = x if x.find('WSS_GW\n') != -1: wss = x if ctg == None or wss == None: return DM = None for x in hitboxDIG: if x.find(self.hitboxAcct) != -1: DM = x if DM == None: return self.ctg = self.sortIECookie(ctg) self.wss_gw = self.sortIECookie(wss) self.dm560804E8WD = self.sortIECookie(DM) return def convertHitBoxIEtoPython(): if sys.platform != 'win32': print 'Cookie Converter: Warning: System is not MS-Windows. I have not been setup to work with other systems yet. Sorry ' + sys.platform + ' user. The game client will create a cookie.' return if __dev__: return a = HitBoxCookie() a.loadIEHitBoxCookies() a.writePythonHitBoxCookies() del a def convertHitBoxPythontoIE(): if sys.platform != 'win32': print 'System is not MS-Windows. I have not been setup to work with other systems yet. Sorry ' + sys.platform + ' user.' return if os.path.isfile('cf.txt') == True: return a = HitBoxCookie() a.loadPythonHitBoxCookies() a.writeIEHitBoxCookies() del a def getreg(regVar): if sys.platform != 'win32': print "System is not MS-Windows. I haven't been setup yet to work with systems other than MS-Win using MS-Internet Explorer Cookies" return '' siteName = 'toontown.online.disney' cookiedir = os.getenv('USERPROFILE') + '\\Cookies' sdir = os.listdir(cookiedir) wholeCookie = None while sdir: temp = sdir.pop() if temp.find(siteName) != -1: wholeCookie = temp break if wholeCookie == None: print 'Cookie not found for site name: ' + siteName return '' CompleteCookiePath = cookiedir + '\\' + wholeCookie cf = open(CompleteCookiePath, 'r') data = cf.read() cf.close() del cf data = data.replace('%3D', '=') data = data.replace('%26', '&') regNameStart = data.find(regVar) if regNameStart == -1: return '' regVarStart = data.find('=', regNameStart + 1) regVarEnd = data.find('&', regNameStart + 1) return data[regVarStart + 1:regVarEnd] def getMAC(staticMAC = [None]): if staticMAC[0] == None: if sys.platform == 'win32': correctSection = 0 try: ipconfdata = os.popen('/WINDOWS/SYSTEM32/ipconfig /all').readlines() except: staticMAC[0] = 'NO_MAC' return staticMAC[0] for line in ipconfdata: if line.find('Local Area Connection') >= 0: correctSection = 1 if line.find('Physical Address') >= 0 and correctSection == 1: pa = line.split(':')[-1].strip() correctSection = 0 staticMAC[0] = pa return pa if sys.platform == 'darwin': macconfdata = os.popen('/usr/sbin/system_profiler SPNetworkDataType |/usr/bin/grep MAC').readlines() result = '-1' if macconfdata: if macconfdata[0].find('MAC Address') != -1: pa = macconfdata[0][macconfdata[0].find(':') + 2:macconfdata[0].find(':') + 22].strip('\n') staticMAC[0] = pa.replace(':', '-') result = staticMAC[0] return result if sys.platform != 'darwin' and sys.platform != 'win32': print 'System is not running OSX or MS-Windows.' return '-2' else: return staticMAC[0] return def firstRun(operation = 'read', newPlayer = None, newPlayerBool = [False]): if operation != 'read': if len(newPlayerBool) != 0: newPlayerBool.pop() newPlayerBool.append(newPlayer) return newPlayerBool[0] def patcherVer(operation = 'read', url = None, patchfile = []): if operation != 'read': if len(patchfile) != 0: patchfile.pop() patchfile.append(url) return patchfile def loggingAvID(operation = 'read', newId = None, localAvId = [None]): if operation == 'write': localAvId[0] = newId else: return localAvId[0] def loggingSubID(operation = 'read', newId = None, localSubId = [None]): if operation == 'write': localSubId[0] = newId else: return localSubId[0] def vconGroup(operation = 'read', group = None, staticStore = []): if operation != 'read': if len(staticStore) != 0: staticStore.pop() staticStore.append(group) try: return staticStore[0] except IndexError: return None return None def printUnreachableLen(): import gc gc.set_debug(gc.DEBUG_SAVEALL) gc.collect() unreachableL = [] for it in gc.garbage: unreachableL.append(it) return len(str(unreachableL)) def printUnreachableNum(): import gc gc.set_debug(gc.DEBUG_SAVEALL) gc.collect() return len(gc.garbage) def reportMemoryLeaks(): if printUnreachableNum() == 0: return import bz2, gc gc.set_debug(gc.DEBUG_SAVEALL) gc.collect() uncompressedReport = '' for s in gc.garbage: try: uncompressedReport += str(s) + '&' except TypeError: pass reportdata = bz2.compress(uncompressedReport, 9) headers = {'Content-type': 'application/x-bzip2', 'Accept': 'text/plain'} try: baseURL = patcherVer()[0].split('/lo')[0] except IndexError: print 'Base URL not available for leak submit' return basePort = 80 if baseURL.count(':') == 2: basePort = baseURL[-4:] baseURL = baseURL[:-5] baseURL = baseURL[7:] if basePort != 80: finalURL = 'http://' + baseURL + ':' + str(basePort) + '/logging/memory_leak.php?leakcount=' + str(printUnreachableNum()) else: finalURL = 'http://' + baseURL + '/logging/memory_leak.php?leakcount=' + str(printUnreachableNum()) reporthttp = HTTPClient() reporthttp.postForm(URLSpec(finalURL), reportdata) def checkParamFile(): if os.path.exists('parameters.txt') == 1: paramfile = open('parameters.txt', 'r') contents = paramfile.read() paramfile.close() del paramfile contents = contents.split('\n') newURL = '' while contents: checkLine = contents.pop() if checkLine.find('PATCHER_BASE_URL=') != -1 and checkLine[0] == 'P': newURL = checkLine.split('=')[1] newURL = newURL.replace(' ', '') break if newURL == '': return else: return newURL + 'patcher.ver'