2006-08-02 13:59:57 +01:00
|
|
|
#!/usr/bin/python
|
|
|
|
#
|
|
|
|
# Scyther interface
|
|
|
|
#
|
|
|
|
|
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
""" Import externals """
|
|
|
|
import os
|
2006-08-07 11:42:34 +01:00
|
|
|
import os.path
|
2006-08-02 13:59:57 +01:00
|
|
|
import sys
|
|
|
|
import StringIO
|
2006-12-11 09:40:57 +00:00
|
|
|
import tempfile
|
2006-08-02 13:59:57 +01:00
|
|
|
|
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
""" Import scyther components """
|
|
|
|
import XMLReader
|
2006-12-14 14:06:50 +00:00
|
|
|
import Error
|
2006-08-02 13:59:57 +01:00
|
|
|
from Misc import *
|
|
|
|
|
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
2006-12-14 14:06:50 +00:00
|
|
|
"""
|
|
|
|
Globals
|
|
|
|
"""
|
|
|
|
|
|
|
|
FirstCheck = True
|
|
|
|
|
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
2006-11-23 11:28:51 +00:00
|
|
|
"""
|
|
|
|
The default path for the binaries is set in __init__.py in the (current)
|
|
|
|
directory 'Scyther'.
|
|
|
|
"""
|
2006-08-09 10:26:15 +01:00
|
|
|
|
2006-08-09 12:39:35 +01:00
|
|
|
def setBinDir(dir):
|
2006-08-09 10:26:15 +01:00
|
|
|
global bindir
|
|
|
|
|
|
|
|
bindir = dir
|
|
|
|
|
2006-08-09 12:54:37 +01:00
|
|
|
def getBinDir():
|
|
|
|
global bindir
|
|
|
|
|
|
|
|
return bindir
|
|
|
|
|
2006-08-09 10:26:15 +01:00
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
2006-12-14 14:06:50 +00:00
|
|
|
def Check():
|
|
|
|
"""
|
|
|
|
Various dynamic checks that can be performed before starting the
|
|
|
|
backend.
|
|
|
|
"""
|
|
|
|
|
|
|
|
global FirstCheck
|
|
|
|
|
|
|
|
# First time
|
|
|
|
if FirstCheck:
|
|
|
|
"""
|
|
|
|
Perform any checks that only need to be done the first time.
|
|
|
|
"""
|
|
|
|
FirstCheck = False
|
|
|
|
|
|
|
|
# Every time
|
|
|
|
|
|
|
|
# Check Scyther backend program availability
|
|
|
|
program = getScytherBackend()
|
|
|
|
CheckSanity(program)
|
|
|
|
|
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
def CheckSanity(program):
|
|
|
|
"""
|
|
|
|
This is where the existence is checked of the Scyther backend.
|
|
|
|
"""
|
|
|
|
|
|
|
|
if not os.path.isfile(program):
|
|
|
|
raise Error.BinaryError, program
|
|
|
|
|
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
2006-11-23 11:28:51 +00:00
|
|
|
def getScytherBackend():
|
|
|
|
# Where is my executable?
|
|
|
|
#
|
|
|
|
# Auto-detect platform and infer executable name from that
|
|
|
|
#
|
|
|
|
if "linux" in sys.platform:
|
|
|
|
|
|
|
|
""" linux """
|
|
|
|
scythername = "scyther-linux"
|
|
|
|
|
2006-12-20 17:20:09 +00:00
|
|
|
elif "darwin" in sys.platform:
|
2006-08-04 23:00:22 +01:00
|
|
|
|
2006-12-20 17:20:09 +00:00
|
|
|
""" OS X """
|
|
|
|
# Preferably, we test for architecture (PPC/Intel) until we
|
|
|
|
# know how to build a universal binary
|
|
|
|
scythername = "scyther-osx"
|
2006-09-21 14:19:22 +01:00
|
|
|
|
2006-11-23 11:28:51 +00:00
|
|
|
elif sys.platform.startswith('win'):
|
2006-09-21 14:19:22 +01:00
|
|
|
|
2006-11-23 11:28:51 +00:00
|
|
|
""" Windows """
|
2006-11-23 11:46:33 +00:00
|
|
|
scythername = "scyther-w32.exe"
|
2006-09-21 14:19:22 +01:00
|
|
|
|
2006-11-23 11:28:51 +00:00
|
|
|
else:
|
2006-09-21 14:19:22 +01:00
|
|
|
|
2006-11-23 11:28:51 +00:00
|
|
|
""" Unsupported"""
|
2006-12-14 14:06:50 +00:00
|
|
|
raise Error.UnknownPlatformError, sys.platform
|
2006-09-21 14:19:22 +01:00
|
|
|
|
2006-11-23 11:28:51 +00:00
|
|
|
program = os.path.join(getBinDir(),scythername)
|
|
|
|
return program
|
|
|
|
|
2006-12-14 19:46:36 +00:00
|
|
|
|
2006-11-23 11:28:51 +00:00
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
class Scyther(object):
|
|
|
|
def __init__ ( self):
|
2006-08-04 23:00:22 +01:00
|
|
|
|
2006-08-08 16:54:00 +01:00
|
|
|
# Init
|
2006-11-23 11:28:51 +00:00
|
|
|
self.program = getScytherBackend()
|
2006-08-02 13:59:57 +01:00
|
|
|
self.spdl = None
|
2006-08-02 14:44:45 +01:00
|
|
|
self.inputfile = None
|
2006-08-08 17:16:28 +01:00
|
|
|
self.options = ""
|
2006-08-02 13:59:57 +01:00
|
|
|
self.claims = None
|
2006-08-06 20:52:07 +01:00
|
|
|
self.errors = None
|
2006-08-06 22:16:14 +01:00
|
|
|
self.errorcount = 0
|
2006-08-07 11:52:48 +01:00
|
|
|
self.run = False
|
2006-08-08 16:54:00 +01:00
|
|
|
self.output = None
|
|
|
|
|
|
|
|
# defaults
|
|
|
|
self.xml = True # this results in a claim end, otherwise we simply get the output
|
2006-08-02 13:59:57 +01:00
|
|
|
|
|
|
|
def setInput(self,spdl):
|
|
|
|
self.spdl = spdl
|
2006-08-02 14:44:45 +01:00
|
|
|
self.inputfile = None
|
2006-08-02 13:59:57 +01:00
|
|
|
|
|
|
|
def setFile(self,filename):
|
2006-08-02 14:44:45 +01:00
|
|
|
self.inputfile = filename
|
2006-08-02 13:59:57 +01:00
|
|
|
self.spdl = ""
|
|
|
|
fp = open(filename,"r")
|
|
|
|
for l in fp.readlines():
|
|
|
|
self.spdl += l
|
|
|
|
fp.close()
|
|
|
|
|
2006-08-07 19:23:30 +01:00
|
|
|
def addFile(self,filename):
|
|
|
|
self.inputfile = None
|
|
|
|
if not self.spdl:
|
|
|
|
self.spdl = ""
|
|
|
|
fp = open(filename,"r")
|
|
|
|
for l in fp.readlines():
|
|
|
|
self.spdl += l
|
|
|
|
fp.close()
|
|
|
|
|
2006-12-14 19:46:36 +00:00
|
|
|
def doScytherCommand(self, spdl, args):
|
|
|
|
"""
|
|
|
|
Run Scyther backend on the input
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
spdl -- string describing the spdl text
|
|
|
|
args -- arguments for the command-line
|
|
|
|
Returns:
|
|
|
|
(output,errors)
|
|
|
|
output -- string which is the real output
|
|
|
|
errors -- string which captures the errors
|
|
|
|
"""
|
2006-11-23 11:28:51 +00:00
|
|
|
|
|
|
|
if self.program == None:
|
2006-12-14 19:46:36 +00:00
|
|
|
raise Error.NoBinaryError
|
2006-08-02 13:59:57 +01:00
|
|
|
|
2006-12-14 20:32:24 +00:00
|
|
|
# Sanitize input somewhat
|
|
|
|
if not spdl or spdl == "":
|
|
|
|
# Scyther hickups on completely empty input
|
|
|
|
spdl = " "
|
|
|
|
|
2006-12-14 20:08:34 +00:00
|
|
|
# Generate temporary files for the output
|
|
|
|
# Requires Python 2.3 though.
|
|
|
|
(fde,fne) = tempfile.mkstemp() # errors
|
|
|
|
(fdo,fno) = tempfile.mkstemp() # output
|
|
|
|
(fdi,fni) = tempfile.mkstemp() # input
|
|
|
|
|
|
|
|
# Write (input) file
|
|
|
|
fhi = os.fdopen(fdi,'w+b')
|
2006-12-14 20:32:24 +00:00
|
|
|
fhi.write(spdl)
|
2006-12-14 20:08:34 +00:00
|
|
|
fhi.close()
|
2006-12-11 09:40:57 +00:00
|
|
|
|
|
|
|
# Generate command line for the Scyther process
|
2006-12-14 19:46:36 +00:00
|
|
|
self.cmd = ""
|
|
|
|
self.cmd += "\"%s\"" % self.program
|
2006-12-14 20:08:34 +00:00
|
|
|
self.cmd += " --append-errors=%s" % fne
|
|
|
|
self.cmd += " --append-output=%s" % fno
|
2006-12-14 19:46:36 +00:00
|
|
|
self.cmd += " %s %s" % (self.options, args)
|
2006-12-14 20:08:34 +00:00
|
|
|
if spdl:
|
|
|
|
self.cmd += " %s" % fni
|
|
|
|
|
|
|
|
# Start the process
|
|
|
|
os.system(self.cmd)
|
|
|
|
|
|
|
|
# reseek
|
|
|
|
fhe = os.fdopen(fde)
|
|
|
|
fho = os.fdopen(fdo)
|
|
|
|
errors = fhe.read()
|
|
|
|
output = fho.read()
|
|
|
|
|
|
|
|
# clean up files
|
|
|
|
fhe.close()
|
|
|
|
fho.close()
|
|
|
|
os.remove(fne)
|
|
|
|
os.remove(fno)
|
|
|
|
os.remove(fni)
|
2006-12-14 19:46:36 +00:00
|
|
|
|
|
|
|
return (output,errors)
|
|
|
|
|
|
|
|
def verify(self):
|
|
|
|
""" Should return a list of results """
|
|
|
|
|
|
|
|
# prepare arguments
|
|
|
|
args = ""
|
|
|
|
if self.xml:
|
|
|
|
args += " --dot-output --xml-output --plain"
|
|
|
|
args += " %s" % self.options
|
|
|
|
|
|
|
|
# execute
|
|
|
|
(output,errors) = self.doScytherCommand(self.spdl, args)
|
|
|
|
self.run = True
|
|
|
|
|
|
|
|
# process errors
|
|
|
|
self.errors = []
|
|
|
|
for l in errors.splitlines():
|
|
|
|
# filter out any non-errors (say maybe only claim etc) and count
|
|
|
|
# them.
|
2006-08-07 12:02:14 +01:00
|
|
|
if not l.startswith("claim\t"):
|
|
|
|
self.errors.append(l.strip())
|
2006-08-06 22:16:14 +01:00
|
|
|
self.errorcount = len(self.errors)
|
2006-08-06 20:52:07 +01:00
|
|
|
|
2006-12-14 19:46:36 +00:00
|
|
|
# process output
|
|
|
|
self.output = output
|
|
|
|
self.validxml = False
|
|
|
|
self.claims = []
|
2006-08-08 16:54:00 +01:00
|
|
|
if self.xml:
|
|
|
|
if len(output) > 0:
|
2006-08-08 17:16:28 +01:00
|
|
|
if output.startswith("<scyther>"):
|
2006-12-14 19:46:36 +00:00
|
|
|
|
|
|
|
# whoohee, xml
|
2006-08-08 17:16:28 +01:00
|
|
|
self.validxml = True
|
|
|
|
|
2006-12-14 19:46:36 +00:00
|
|
|
xmlfile = StringIO.StringIO(output)
|
|
|
|
reader = XMLReader.XMLReader()
|
|
|
|
self.claims = reader.readXML(xmlfile)
|
2006-08-02 13:59:57 +01:00
|
|
|
|
2006-12-14 19:46:36 +00:00
|
|
|
# Determine what should be the result
|
|
|
|
if self.xml:
|
|
|
|
return self.claims
|
|
|
|
else:
|
|
|
|
return self.output
|
2006-08-02 13:59:57 +01:00
|
|
|
|
2006-08-07 19:23:30 +01:00
|
|
|
def getClaim(self,claimid):
|
|
|
|
if self.claims:
|
|
|
|
for cl in self.claims:
|
|
|
|
if cl.id == claimid:
|
|
|
|
return cl
|
|
|
|
return None
|
|
|
|
|
2006-08-02 13:59:57 +01:00
|
|
|
def __str__(self):
|
2006-08-07 11:52:48 +01:00
|
|
|
if self.run:
|
|
|
|
if self.errorcount > 0:
|
2006-08-07 17:40:46 +01:00
|
|
|
return "%i errors:\n%s" % (self.errorcount, "\n".join(self.errors))
|
2006-08-07 11:52:48 +01:00
|
|
|
else:
|
2006-08-08 17:16:28 +01:00
|
|
|
if self.xml and self.validxml:
|
|
|
|
s = "Verification results:\n"
|
2006-08-08 16:54:00 +01:00
|
|
|
for cl in self.claims:
|
|
|
|
s += str(cl) + "\n"
|
|
|
|
return s
|
|
|
|
else:
|
|
|
|
return self.output
|
2006-08-02 13:59:57 +01:00
|
|
|
else:
|
|
|
|
return "Scyther has not been run yet."
|
|
|
|
|
2006-12-14 19:46:36 +00:00
|
|
|
# vim: set ts=4 sw=4 et list lcs=tab\:>-:
|