123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519 |
- # -*- 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 <http://www.gnu.org/licenses/>.
- #
- 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()
-
|