# -*- coding: utf-8 -*-# # Copyright 2015 Weber Yann # # This file is part of pyMapTraceroute. # # pyMapTraceroute is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # pyMapTraceroute is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with pyMapTraceroute. If not, see . # control_summary = [ "+----------------------+----------------------------------+", "| Controls summary |", "+----------------------+----------------------------------+", "+----------------------+----------------------------------+", "| ^h, ? | display this help |", "+----------------------+----------------------------------+", "| esc, ^q | exit the programm or this help |", "+----------------------+----------------------------------+", "+----------------------+----------------------------------+", "| [a-zA-Z0-9\.] | Host input |", "+----------------------+----------------------------------+", "| mid-mouse btn | paste clipboard into host input |", "+----------------------+----------------------------------+", "| enter | validate host input and |", "| | run traceroute |", "+----------------------+----------------------------------+", "| ^c | clear host input |", "+----------------------+----------------------------------+", "+----------------------+----------------------------------+", "| mouse scroll, +, - : zoom in/out |", "+----------------------+----------------------------------+", "| arrow keys : move on the map |", "+----------------------+----------------------------------+", "| left mouse btn press : select hops on the map |", "+----------------------+----------------------------------+" ] import random import time import os import pygame from pygame import gfxdraw import tracer import vtracemap class Vtracert: ## Instanciate a visual tracert # @param mapdatfile The filename containing the borders coordinates # @param width Window width # @param height Window height # @param cols Override default colors, its a dict of tuple (r,g,b) (r,g,b,a), valid keys are : bg, map, prompt, status, trace_init, trace_hop, trace_trace # @param fonts Override default font type and size its a dict of tuple (fontType, fontSize), valid keys are : prompt, status # @param promptText The prompt when reading the host to traceroute # @param defaultInput Default host to traceroute def __init__(self, mapdatfile, width = 1024, height = 768, cols = dict(), fonts = dict(), promptText = "Enter an ip or an hostname and press enter to traceroute : %s_", defaultInput = "gnu.org"): pygame.init() #Pygame init #Options self.mapfile = mapdatfile self.width = width self.height = height #Colors self.col = dict() self.col['alpha'] = pygame.Color(0,0,0,0) self.col['bg'] = pygame.Color(0,0,0) self.col['map'] = pygame.Color(15,8,63) self.col['prompt'] = pygame.Color(200,200,200) self.col['status'] = pygame.Color(200,200,200) self.col['cur_status'] = self.col['status'] self.col['cur_statusline'] = self.col['status'] self.col['trace_init'] = pygame.Color(0,0xf0,0) self.col['trace_end'] = pygame.Color(0xee,0x33,0xee) self.col['trace_hop'] = pygame.Color(0xf0,0xf0,0) self.col['trace_trace'] = pygame.Color(0xf0,0,0) self.col['trace_txt'] = pygame.Color(200, 200, 200, 125) self.col['trace_txtsel'] = self.col['trace_hop'] self.col['trace_progress'] = pygame.Color(0,0xf0,0) self.col['help_txt'] = pygame.Color(200,200,200) self.col['help_title'] = pygame.Color(120,30,230) self.col['help_bg'] = pygame.Color(0,0,0) for n, c in cols: self.col[n] = pygame.Color(c) #Fonts self.font = dict() self.font['prompt'] = pygame.font.Font(None, 20) self.font['status'] = pygame.font.Font(None,20) self.font['cur_status'] = self.col['status'] self.font['trace_txt'] = pygame.font.Font(None,20) self.font['help_title'] = pygame.font.Font(None,48) self.font['help_title'].set_underline(True); self.font['help_title'].set_bold(True); self.font['help'] = pygame.font.SysFont('monospace', 16) for n, (ft,fs) in fonts: self.font[n] = pygame.font.Font(ft,fs) #Prompt options self.promptText = promptText self.inputText = defaultInput self.inputText = '1.202.15.14' #Event filtering pygame.event.set_allowed(None) pygame.event.set_allowed([pygame.MOUSEBUTTONUP,pygame.KEYDOWN, pygame.KEYUP, pygame.QUIT]) #Surface creation self.surf = dict() self.surf['screen'] = pygame.display.set_mode((width, height), pygame.DOUBLEBUF ) self.surf['map'] = pygame.Surface((width, height),pygame.HWSURFACE) self.surf['trace'] = pygame.Surface((width, height),pygame.HWSURFACE | pygame.SRCALPHA) self.surf['tracetxt'] = pygame.Surface((width, height),pygame.HWSURFACE | pygame.SRCALPHA) self.surf['status'] = None for s in self.surf: if self.surf[s] != None: self.surf[s].fill(self.col['alpha']) self.help(0) self.drawStatus('Loading...'); pygame.display.flip() #Clipboard handling module initialisation pygame.scrap.init() #Keyboard vars self.caps = False self.ctrl = False #Map info self.trmap = vtracemap.TraceMap(self) self.tracer = tracer.Tracer(self) ## Color accessor # @param cname Color name # @param v The value to set or False to get # @return The color named cname # @see Vtracert::opt() def Col(self, cname, v = False): return Vtracert.opt(cname, self.col, v) ## Surface accessor # @param sname surface name # @param v The value to set or False to get # @return The surface named sname # @see Vtracert::opt() def Surf(self, sname, v = False): return Vtracert.opt(sname, self.surf, v) ## Font accessor # @param fname font name # @param v The value to set or False to get # @return The font named fname # @see Vtracert::opt() def Font(self, fname, v = False): return Vtracert.opt(sname, self.font, v) ## A generic class to handle dict properties # @param cname The dict key # @param dopt The dict # @param v The value or False for get and not set # @return dopt[cname] # @see Vtracert::Col(), Vtracert::Surf(), Vtracert::Font() @classmethod def opt(c, cname, dopt, v): if not (not v and isinstance(v,bool)): dopt[cname] = v return dopt[cname] ## Return the unicode representation of the combiation of ctrl+a letter key @classmethod def ctrlUnicode(classo, c): return unicode(chr(ord(c.lower())-ord('a')+1)) pass ##Display an help on the screen and wait for an KEYDOWN or QUIT event def help(self, ttl = None): scr = self.surf['screen'] helpTxts = [ "Vtraceroute help", "", "Vtraceroute is a traceroute program ( more info : man traceroute )", "that displays hops on a map using geoipLookup.", "", "Controls summary :", "", "^h, ? : display this help", "esc, ^q : exit the programm or this help", "", "[a-zA-Z0-9\.] : Host input", "mid-mouse btn : paste clipboard into host input", "enter : validate host input and", " run traceroute", "^c : clear host input", "", "", "mouse scroll, +, - : zoom in/out", "arrow keys : move on the map", "left mouse btn press : select hops on the map", "","", "All borders data extracted from data provided by http://thematicmapping.org and", "Licenced under Creatice Commons Attribution-Share Alike License 3.0" "", "Press any key or mouse button to exit this help" ] tFont = self.font['help_title'] hFont = self.font['help'] hCol = self.col['help_txt'] tCol = self.col['help_title'] ty = 50 lspacing = 3 hy = ty + tFont.get_linesize() + lspacing hx = 40 #Fill screen scr.fill(self.col['help_bg']) #Print title titleTxt = helpTxts.pop(0) tsurf = tFont.render(titleTxt,1,tCol) tsc = tsurf.get_rect() tsc.centerx = self.width/2 #center the title tsc.y = ty scr.blit(tsurf,tsc) #Print help text for line in helpTxts: if len(line) > 0: tsurf = hFont.render(line,1,hCol) scr.blit(tsurf,(hx, hy)) hy += hFont.get_linesize() hy += lspacing #pygame.display.flip();time.sleep(0.05); pygame.display.flip() if ttl == None: wait = True while wait: evts = pygame.event.get() for evt in evts: if evt.type in (pygame.QUIT, pygame.KEYDOWN, pygame.MOUSEBUTTONUP): wait = False break; else: time.sleep(ttl) pass """ ## Return True # @c1 unicode1 # @c2 unicode2 def isCtrlUnicode(self, c, cu): return (self.ctrl and Vtracert.ctrlUnicode(c) == cu) """ ## Blit a surface on the screen surface # @param sname The surface name # @param pos A tuple (x,y) for the blit position # @return self.surf['screen'].blit(self.surf[sname], pos) def blitScreen(self, sname, pos = (0,0) ): return self.surf['screen'].blit(self.surf[sname], pos) ## Draw the map again and call Vtracert::flip() # Call Vtracert::fillScreen() # @see Vtracert::fillScreen(), Vtracert::flip() def mapRedraw(self): self.fillScreen() self.trmap.draw() self.flip() ## Fill the screen with the 'bg' color def fillScreen(self): self.surf['screen'].fill(self.col['bg']) pass ## Blit all the surface is the right order but don't call pygame.display.flip() # The status bar horizontal ruler is a draw and not a blit # @see Vtracert::flip() def blit(self): self.blitScreen('map') self.blitScreen('status') self.drawStatusLine() self.blitScreen('trace') self.blitScreen('tracetxt') pass ## Blit all surface and flip display # @see Vtracert::blit() def flip(self, fill = True): if fill: self.fillScreen() self.blit() pygame.display.flip() pass ## Draw the prompt in the status surface # @see Vtracert::drawStatus() def drawPrompt(self): self.drawStatus(self.promptText%(self.inputText), self.font['prompt'], self.col['prompt']) ## Draw the status bar on the screen # @param statusText The text to display in the status bar # @param font The font used to render the text # @param col The text color # @param antialiasing A boolean telling if the text is rendered antialiased # @param line A boolean telling if the status bar line has to be drawn # @param lineCol The status bar line color (by default same as col) def drawStatus(self, statusText, font = None, col = None, antialiasing = True, line = True, lineCol = None): if font == None: font = self.font['status'] self.font['cur_status'] = font if col == None: col = self.col['status'] self.col['cur_status'] = col if lineCol == None: lineCol = col self.col['cur_statusline'] = lineCol self.surf['status'] = font.render(statusText, antialiasing, col) self.blitScreen('status') if line: self.drawStatusLine() pass ## Draw the status bar horizontal ruler # @see Vtracert::drawStatus() def drawStatusLine(self): ly = self.font['cur_status'].get_linesize() + 2 pygame.gfxdraw.line(self.surf['screen'], 0, ly, self.width - 1, ly, self.col['cur_statusline']) pass ## Handle a KEYUP event # @param evt Event def keyupEvt(self, evt): if evt.key == 303 or evt.key == 304: #caps release self.caps = False elif evt.key == 305 or evt.key == 306: #ctrl release self.ctrl = False pass ## Handles a KEYDOWN event # @param evt Event def keydownEvt(self, evt): ccode = evt.key c2u = Vtracert.ctrlUnicode allowedChar = range(ord('a'),ord('z')+1) allowedChar += range(ord('A'),ord('Z')+1) allowedChar += range(ord('0'),ord('9')+1) allowedChar += [ord('.'),ord('\b'),ord('\r'),ord('\n')] allowedChar = [ chr(cc) for cc in allowedChar ] ctrlChar = [ c2u('q'), #^q c2u('h'), #^h c2u('n'), #^n c2u('c'), #^n '?', u'\x1b' #escape button ] #print evt #print self.ctrl if ccode in [273,274,275,276]: #Arrow keys if ccode == 273: d = 'up' elif ccode == 274: d = 'down' elif ccode == 275: d = 'right' else: d = 'left' self.trmap.zoom( (None, None), d) self.tracer.drawTrace(0) self.flip() pygame.event.clear(pygame.MOUSEBUTTONUP) elif ccode == 303 or ccode == 304: #caps self.caps = True elif evt.key == 305 or evt.key == 306: #ctrl release self.ctrl = True elif self.ctrl or cc in ctrlChar: # ctrl + key handle cc = evt.unicode if cc == u'\x1b' or cc == c2u('q'): #quit (esc or ^q) pygame.event.post(pygame.event.Event(pygame.QUIT)) elif cc == c2u('h') or cc == '?': #help ^h ? ^? print "Help !!!!!!!!" self.help() self.flip() elif cc == c2u('c'): self.inputText = '' self.drawPrompt() elif cc == c2u('n'): for cname in self.col: c = self.col[cname] c.r = 255 - c.r c.g = 255 - c.g c.b = 255 - c.b self.col[cname] = c self.mapRedraw() elif evt.unicode in allowedChar: cc = evt.unicode if cc == '\b': #backspace if len(self.inputText) > 0: self.inputText = self.inputText[:-1] elif cc == '\r' or cc == '\n': print "OK, tracerouting %s ..."%self.inputText dest = self.inputText self.inputText = "" self.surf['trace'].fill((0,0,0,0)) self.tracer.liveTrace(dest) #self.tracer.trace(dest) self.flip() else: self.inputText += cc self.drawPrompt() pass ## Handles a MOUSEBUTTONUP event # @param evt Event def mouseboutonupEvt(self, evt): if evt.button == 4 or evt.button == 5: #scrollzoom if evt.button == 4 or evt.button == 1: zoom = True elif evt.button == 5 or evt.button == 3: zoom = False self.trmap.zoom(evt.pos,zoom) self.tracer.drawTrace(0) self.blit() pygame.event.clear(pygame.MOUSEBUTTONUP) #end scrollzoom elif evt.button == 1: #Trying to select hops if self.tracer.select(evt.pos): print 'Selected : \n',self.tracer.selectedIp() self.tracer.drawTrace(0) elif evt.button == 2: clipTxt = pygame.scrap.get('TEXT') if clipTxt: inputTxt += clipTxt self.drawPrompt() else: print "\n\n" types = pygame.scrap.get_types() for t in types: print t,pygame.scrap.get(t) pass ## Listen for pygame events def run(self): self.drawPrompt(); self.mapRedraw(); self.flip() #Display small onetime help text htext = self.font['status'].render("Type ^h or ? for help.",1, self.col['status']) self.surf['screen'].blit(htext, (5, self.font['status'].get_linesize()+2)) pygame.display.flip() again = True while again: evt = pygame.event.wait() if evt != pygame.NOEVENT: if evt.type == pygame.QUIT: again = False break elif evt.type == pygame.KEYUP: self.keyupEvt(evt) elif evt.type == pygame.KEYDOWN: self.keydownEvt(evt) elif evt.type == pygame.MOUSEBUTTONUP: self.mouseboutonupEvt(evt) self.drawPrompt(); self.flip()