scyther/gui/Gui/Attackwindow.py
2020-10-27 23:34:25 +01:00

341 lines
10 KiB
Python

#!/usr/bin/python
# 2.2+-only
"""
Scyther : An automatic verifier for security protocols.
Copyright (C) 2007-2013 Cas Cremers
This program 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 2
of the License, or (at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
"""
#---------------------------------------------------------------------------
""" Import externals """
import wx
import os
from .Misc import *
#---------------------------------------------------------------------------
""" Import scyther-gui components """
from . import Icon
from . import Preference
from . import Error
#---------------------------------------------------------------------------
try:
import Image
except ImportError:
pass
#---------------------------------------------------------------------------
class AttackDisplay(wx.ScrolledWindow):
"""
Display an attack (inside a tab or not)
"""
def __init__(self, daddy, parent, attack):
self.win = daddy
self.attack = attack
wx.ScrolledWindow.__init__(self,parent,id=-1)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Image = wx.StaticBitmap(self, -1, wx.Bitmap(1,1))
self.box = wx.BoxSizer(wx.VERTICAL)
self.box.Add(self.Image,1,wx.ALIGN_CENTER)
self.hbox = wx.BoxSizer(wx.HORIZONTAL)
self.hbox.Add(self.box,1,wx.ALIGN_CENTER)
self.SetSizer(self.hbox)
self.original = None
filename = attack.file
if attack.filetype == "png":
self.original = wx.Image(filename,wx.BITMAP_TYPE_PNG)
elif attack.filetype == "ps":
# depends on PIL lib
try:
self.original = Image.open(filename)
except:
Preference.doNotUsePIL()
raise Error.PILError
else:
print("Unknown file type %s." % (self.filetype))
# TODO self.Bind(wxSizeEvent
self.update(True)
self.Fit()
def OnSize(self,event):
self.update(False)
event.Skip()
def update(self,force=True):
if not force:
if not self.win.fit:
return
# This is needed, don't ask me why.
self.SetScrollbars(0,0,0,0,0,0)
(framewidth,frameheight) = self.GetClientSize()
(virtualwidth,virtualheight) = (framewidth,frameheight)
def makefit(width,height):
if self.win.fit:
# determine scaling factors for fitting
wfactor = float(framewidth) / width
hfactor = float(frameheight) / height
# select smallest factor (so it will fit)
if hfactor < wfactor:
factor = hfactor
else:
factor = wfactor
# apply scaling factor
width = width * factor
height = height * factor
else:
factor = 1.0
return (factor, int(width), int(height))
if self.attack.filetype == "png":
bmp = self.original
if not bmp.IsOk():
bmp = wx.Image(1,1)
else:
(originalwidth,originalheight) = (bmp.GetWidth(), bmp.GetHeight())
if self.win.fit:
(factor, virtualwidth, virtualheight) = makefit(originalwidth,originalheight)
bmp = self.original.Scale(virtualwidth,virtualheight)
self.Image.SetBitmap(wx.Bitmap(bmp))
elif self.attack.filetype == "ps":
pil = self.original.copy()
(originalwidth,originalheight) = pil.size
(factor, virtualwidth, virtualheight) = makefit(originalwidth,originalheight)
# we really only want antialias when it's smaller
if factor < 1.0:
pil.thumbnail((virtualwidth,virtualheight),Image.ANTIALIAS)
else:
pil.thumbnail((virtualwidth,virtualheight))
image = wx.EmptyImage(pil.size[0],pil.size[1])
image.SetData(pil.convert('RGB').tostring())
self.Image.SetBitmap(image.ConvertToBitmap())
else:
print("Unknown file type %s." % (self.attack.filetype))
self.SetVirtualSize((virtualwidth,virtualheight))
#self.box.SetItemMinSize(self.Image.GetContainingSizer())
self.box.Layout()
step = 20
xn = int(virtualwidth // step) + 1
yn = int(virtualheight // step) + 1
self.SetScrollbars(step,step,xn,yn,0,0)
"""
Pop up menu
"""
self.popupmenu = wx.Menu()
item = self.popupmenu.Append(-1,"Export image (.png)")
self.Bind(wx.EVT_MENU, self.OnExportPng, item)
item = self.popupmenu.Append(-1,"Export image (.ps)")
self.Bind(wx.EVT_MENU, self.OnExportPs, item)
item = self.popupmenu.Append(-1,"Export image (.pdf)")
self.Bind(wx.EVT_MENU, self.OnExportPdf, item)
item = self.popupmenu.Append(-1,"Export image (.svg)")
self.Bind(wx.EVT_MENU, self.OnExportSvg, item)
item = self.popupmenu.Append(-1,"Export image (.fig)")
self.Bind(wx.EVT_MENU, self.OnExportFig, item)
item = self.popupmenu.Append(-1,"Export graphviz data (.dot)")
self.Bind(wx.EVT_MENU, self.OnExportDot, item)
self.Bind(wx.EVT_CONTEXT_MENU, self.OnShowPopup)
self.Refresh()
def OnShowPopup(self, event):
pos = event.GetPosition()
pos = self.Image.ScreenToClient(pos)
self.PopupMenu(self.popupmenu, pos)
def OnPopupItemSelected(self, event):
item = self.popupmenu.FindItemById(event.GetId())
text = item.GetText()
wx.MessageBox("You selected item %s" % text)
def askUserForFilename(self, **dialogOptions):
dialog = wx.FileDialog(self, **dialogOptions)
if dialog.ShowModal() == wx.ID_OK:
res = "%s/%s" % (dialog.GetDirectory(), dialog.GetFilename())
else:
res = None
dialog.Destroy()
return res
def saveFileName(self, ext):
(p,r,l) = self.win.claim.triplet()
prefix = "pattern-%s_%s_%s-%s" % (p,r,l,self.attack.id)
suggested = "%s.%s" % (prefix,ext)
res = self.askUserForFilename(style=wx.FD_SAVE, wildcard="*.%s" % (ext), defaultFile = "%s" % (suggested))
return res
def exportImage(self, type,ext=None):
if ext == None:
ext = type
res = self.saveFileName(ext)
if res != None:
cmd = "dot -T%s" % (type)
cmdpushwrite(cmd,self.attack.scytherDot,res)
def OnExportPng(self, event):
self.exportImage("png")
def OnExportPs(self, event):
self.exportImage("ps")
def OnExportPdf(self, event):
self.exportImage("pdf")
def OnExportSvg(self, event):
self.exportImage("svg")
def OnExportFig(self, event):
self.exportImage("fig")
def OnExportDot(self, event):
res = self.saveFileName("dot")
if res != None:
fp = open(res,'w')
fp.write(self.attack.scytherDot)
fp.close()
#---------------------------------------------------------------------------
class AttackWindow(wx.Frame):
def __init__(self,cl):
super(AttackWindow, self).__init__(None, size=(800,800))
self.claim = cl
# TODO maybe fitting defaults should come from Preferences.
# Now, it is default no even if we have PIL, for performance
# reasons.
self.fit = False
aTable = wx.AcceleratorTable([
(wx.ACCEL_CTRL, ord('W'), wx.ID_CLOSE),
(wx.ACCEL_NORMAL, wx.WXK_ESCAPE, wx.ID_CLOSE),
])
self.SetAcceleratorTable(aTable)
self.Bind(wx.EVT_CLOSE, self.onCloseWindow)
self.Bind(wx.EVT_MENU, self.onCloseWindow, id=wx.ID_CLOSE)
self.CreateInteriorWindowComponents()
Icon.ScytherIcon(self)
self.SetTitle()
def SetTitle(self):
tstr = self.claim.stateName(len(self.claim.attacks),True)
tstr += " for claim %s" % self.claim.id
super(AttackWindow, self).SetTitle(tstr)
def CreateInteriorWindowComponents(self):
''' Create "interior" window components. In this case it is the
attack picture. '''
sizer = wx.BoxSizer(wx.VERTICAL)
# Make zoom buttons
if Preference.usePIL():
buttons = wx.BoxSizer(wx.HORIZONTAL)
bt = wx.ToggleButton(self,-1,"Fit to window")
bt.SetValue(self.fit)
buttons.Add(bt,0)
self.Bind(wx.EVT_TOGGLEBUTTON, self.OnFit, bt)
sizer.Add(buttons, 0, wx.ALIGN_LEFT)
# Add attacks (possible with tabs)
self.displays=[]
attacks = self.claim.attacks
n = len(attacks)
if n <= 1:
# Just a single window
dp = AttackDisplay(self, self, attacks[0])
self.displays.append(dp)
else:
# Multiple tabs
dp = wx.Notebook(self,-1)
for i in range(0,n):
disp = AttackDisplay(self,dp,attacks[i])
classname = "%s %i" % (self.claim.stateName(1,True),(i+1))
dp.AddPage(disp, classname)
self.displays.append(disp)
sizer.Add(dp, 1, wx.EXPAND,1)
self.SetSizer(sizer)
def update(self,force=False):
for t in self.displays:
t.update(force)
def OnFit(self,event):
if self.fit:
self.fit = False
else:
self.fit = True
self.update(True)
def OnRealSize(self):
self.fit = False
self.update(True)
def OnSize(self):
self.Refresh()
def OnZoom100(self,evt):
self.fit = False
self.update(True)
self.Refresh()
def OnZoomFit(self,evt):
self.fit = True
self.update(True)
self.Refresh()
def onCloseWindow(self,evt):
self.Destroy()
#---------------------------------------------------------------------------
# vim: set ts=4 sw=4 et list lcs=tab\:>-: