#!/usr/bin/python
#print "============  S T A R T ================"

# %Z% %M% Version %I% %H% %T%
import sys
from sys import stdin
from sys import stdout
import os
import re
import datetime
import time
import hashlib
import subprocess
import unicodedata

#############################################################
#                                                           #
#  Generate Federated Testing Report -- Forensic Media Prep #
#                                       (Disk Wipe)         #
#                                                           #
#############################################################


import codecs
import sys

def get_unit(x):
	u = ['bytes','KB','MB','GB','TB','XB']
	ix = 0
	z = x * 100
#	if x < 1000: return u
	while x > 1000 and ix < 5:
		x = x / 1000
		z = z / 1000
		ix = ix + 1
	return (float(z)/100.0,u[ix])

try:
	sd = codecs.open("/tmp/fmp-test-report-dribble.txt","wt",'utf-8')
except IOError as e:
	sd = open("/dev/null","wt")

def get_configs (root):
	configs = {}
	tool_file = root+'/fmp_config.txt'
#	print 'Config File: ',tool_file
	if os.access (tool_file,os.F_OK):
		fd = open (tool_file,"r")
		for line in fd:
			f = line.strip().split(':')
			if len(f) == 7:
				(fn,drive,port,path,hidden,method,bridge_desc) = f
				configs[fn] = f
		fd.close()
	ckeys = configs.keys()
	ckeys.sort()
	for c in ckeys:
		test_case = c
		tool_file = root+'/'+test_case+'-init.txt'
#		print tool_file
		ok_init = os.access (tool_file,os.F_OK)
		tool_file = root+'/'+test_case+'-analysis.txt'
#		print tool_file
		ok_analysis = os.access (tool_file,os.F_OK)
		tool_file = root+'/'+test_case+'-size.txt'
#		print tool_file
		ok_size = os.access (tool_file,os.F_OK)
#		print test_case,' look at',ok_init,ok_size,ok_analysis
		state = 'not run'
		if ok_init and ok_size and ok_analysis: state = 'finished'
		elif ok_init or ok_size or ok_analysis:
			state = 'partial'
			if not ok_init: state += ':init'
			if not ok_size: state += ':size'
			if not ok_analysis: state += ':analysis'
		configs[c].append(state)
#	print 'Config count: ',len(configs)
	return configs
# ['002', 'Big SATA > 140GB', 'USB2', 'bridge', 'none', 'secure erase']
#				(fn,drive,port,path,hidden,method) = f
#				return (fn,drive,port,path,hidden,method)
#		print "No current test configuration specified."
#		print "Go back to the browser interface and select a configuration"
#		exit (0)
#	else:
#		print "No current test configuration specified."
#		print "Go back to the browser interface and select a configuration"
#		exit (0)

class rng (object):
	def __init__(s,lba_from,lba_to):
		s.lba_from = int(lba_from)
		s.lba_to = int(lba_to)
	def __str__ (s):
		return format(s.lba_from,',')+' ==> '+format(s.lba_to,','
		)+ ' ('+format(1+s.lba_to-s.lba_from,',')+')'

class cat_rec (object):
	def __init__(s,cat,sectors):
		s.cat = cat
		s.sectors = int(sectors)
		s.runs = []
	
	
def get_drives (root,configs):
	pass
	drives = {}
	for c in configs:
		(fn,drive,port,path,hidden,method,bridge_desc,state) = configs[c]
#		print 'Config:',c,configs[c]
		if state == 'finished':
			drives[c] = ['0','0']
			tool_file = root+c+'-init.txt'
			ok_analysis = os.access (tool_file,os.F_OK)
#			print 'Open file: ',tool_file
			fd = open (tool_file,'r')
			for line in fd:
				f = line.strip().split(';')
				if f[0] == 'sectors':
					drives[c][0] = f[1]
					break
			fd.close()
			tool_file = root+c+'-size.txt'
			ok_analysis = os.access (tool_file,os.F_OK)
#			print 'Open file: ',tool_file
			fd = open (tool_file,'r')
			for line in fd:
				f = line.strip().split(';')
				if f[0] == 'sectors':
					drives[c][1] = f[1]
					break
			fd.close()
	return drives


def get_results (root,configs):
#	print '\n\nR E S U L T S    = = = = = = = = =\n'
	results = {}
	for c in configs:
		(fn,drive,port,path,hidden,method,bridge_desc,state) = configs[c]
#		print 'Config:',c,configs[c]
		if state == 'finished':
#			print 'do ',c
			tool_file = root+c+'-analysis.txt'
			ok_analysis = os.access (tool_file,os.F_OK)
#			print 'Open file: ',tool_file
			fd = open (tool_file,'r')
			results[c] = {}
			this_cat = ''

			for line in fd:
				f = line.strip().split(';')
#				print f
				if f[0] == 'cat':
					this_cat = f[1]
					koshka = cat_rec(f[1],f[2])
					results[c][this_cat] = koshka
				elif f[0] == 'runs':
					r = rng(f[2],f[3])
					results[c][this_cat].runs.append(r)
#					print r
	return results


def get_root ():
	
	if os.access ('/media/FT-LOGS',os.F_OK): return '/media/FT-LOGS'
	if os.access ('/media/cftt/FT-LOGS',os.F_OK): return '/media/cftt/FT-LOGS'
	if os.access ('/Volumes/FT-LOGS',os.F_OK): return '/Volumes/FT-LOGS'
	if not os.access ('/tmp/FT-LOGS',os.F_OK):
		os.mkdir ('/tmp/FT-LOGS')
	return '/tmp/FT-LOGS'

def get_tool (root):
	tf = open(root+'/fmp_tool.txt',"r")
	sd.write ('Open '+root+'/fmt_tool.txt\n')
	buff = tf.read()
	return buff.split()[0:2]

def walker(r):
	dlist = os.listdir (r)
	for f in dlist:
		if f[0] == '.': continue
		if os.path.isdir(r+'/'+f):
#			print 'Dir:',r+'/'+f
			walker (r+'/'+f)
		elif os.path.isfile(r+'/'+f): look(r+'/'+f)
root =  get_root ()
#print 'Root is:',root

start_time = str(datetime.datetime.now())
start_at = start_time
start_time = re.sub(r'\.[0-9]*','',start_time)
start_time = re.sub('[ :]','+',start_time)
html_file_name = "test-report-fmp-"+start_time+".html"
print html_file_name
html_file = root+'/'+html_file_name
try:
	html = open(html_file,"wt")
except IOError as e:
	print 'Failed to open:',html_file
	html = stdout

sd.write ('Open '+html_file+'\n')
sd.write ('html_file_name '+html_file_name+'\n')
#print html_file_name
os_info = os.uname()
ver = "Tool: %Z% %M% Version %I% created %G% at %U%\n"+"<br>OS: %s Version %s\n"%(
	os_info[0],os_info[2])
sd.write ('Generate Disk Wipe Test Report\n')
sd.write(ver+'\n')
sd.write ('Start: '+start_at+'\n')

sd.write("Args:"+str(sys.argv)+'\n')
sd.write('Full report\n'+html_file+'\n')

(tool_name,tool_version) = get_tool (root)

sd.write ('tool: %s  Version %s\n'%(tool_name,tool_version))
#walker(root)

head = """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
  <meta content="text/html; charset=UTF-8"
 http-equiv="Content-Type">
  <title>Test Results for Forensic Media Preparation Tool</title>
  <meta content="J Lyle NIST/CFTT" name="author">
  <meta content="Federated Testing Forensic Media Preparation Test Results"
 name="description">
	<style>
	em.x {color: green;font-size: 100%;font-style:italic;font-weight:bold;}
	em.p {bgcolor: green;font-size: 100%;font-weight:bold;}
	th {
	   border-collapse: collapse;
	   border: 1px solid black;
	   text-align: center;
	   } 
	table, th, td {
	   border-collapse: collapse;
	   border: 1px solid black;
	   } 
	</style>
</head>
<body>
"""
tail = """
</body>
</html>
"""

report_header_text_part_0 = '''
<em class="x">
<br>Text formatted like this (Bold, Italics and Green) should be
removed and replaced. These are instructions
to the report writer.
<br>
<br>You should begin with a cover page and an introductory section and maybe a table of contents.
</em>
<br>
<h1>How to Read This Report</h1>
<br><br>This report is organized into the following sections:
<br><em class=x>Of course, you can reorganize sections, but then you should update
this list.</em><br>
<ol>
<li><b>Tested Tool Description:</b> The tool name, version, and vendor information 
are listed.
<li><b>Testing Organization:</b> Contact information and approvals.
<li><b>Results Summary:</b> This section identifies any significant anomalies
observed in the test runs.
This section provides a narrative of key findings identifying where the tool meets
expectations and provides a summary of any ways the tool did not
meet expectations.
The section also provides any observations of interest about the tool or about testing
the tool including any observed limitations or organization imposed
restrictions on tool use.
<li><b>Test Environment & Selected Test Configurations:</b> Description of hardware, software and
support 
environment (e.g., version of Federated Testing used, device firmware version, etc) 
used in tool testing
 and a list identifying the applicable test configurations selected.
<li><b>Test Results:</b> Automatically generated test results that identify
anomalies.
</ol>
'''
report_header_text_part_1 = '''
<h1>Tested Tool Description</h1>
<p>Tool Name: %s
<br>Tool Version: %s
<br>Vendor: 
<font color="green"><b><i>Insert vendor name and contact information.
<font color="black"></b></i>
'''
report_header_text_part_2 = '''
<br><br>
<h1>Testing Organization</h1>
<font color="green"><b><i>The following items
in this section may be included or omitted as per organization's
policy for tool test reports.<font color="black"></b></i>
<br>Organization conducting test:
<font color="green"><b><i>Insert Organization name.<font color="black"></b></i>
<br>Contact:
<font color="green"><b><i>Insert contact name.<font color="black"></b></i>
<br>Report date:
<font color="green"><b><i>Insert date.<font color="black"></b></i>
<br>Authored by:
<font color="green"><b><i>Insert author name.<font color="black"></b></i>
<br>Reviewed by:
<font color="green"><b><i>Insert name of reviewer.<font color="black"></b></i>
<br>Reviewed by date:
<font color="green"><b><i>Insert date.<font color="black"></b></i>
<br>Approved:
<font color="green"><b><i>Insert name of approving official.<font color="black"></b></i>
<br>Approved by date:
<font color="green"><b><i>Insert date.<font color="black"></b></i>
<br>
<br>This test report was generated using CFTT's Federated Testing Forensic
Tool Testing Environment, see <a href="http://www.cftt.nist.gov/federated-testing.html">
Federated Testing Home Page</a>.
<h1>Results Summary</h1>
<em class=x>
Provide a narrative of key findings -- did the tool meet
expectations. If not provide a summary of how the tool did not
meet expectations.
Provide any observations of interest about the tool or about testing
the tool including any observed limitations or organization imposed
restrictions on tool use.
</em>
<h1>Test Environment & Test Drives</h1>
<p>This section describes test hardware, software, test data sets and test cases.
<h2>Test Hardware and Software</h2>
<p>
<font color="green"><b><i>List and describe any hardware of software 
used during testing in sufficient detail
to repeat the tests.<font color="black"></b></i><br><br>

<h3>Defined Test Configurations</h3>
The following table describes each defined configuration of test drive and wipe method.
<p>
The columns are as follows:
<ul>
<li><b>Config:</b>The configuration ID.
<li><b>Drive Type:</b>The drive size category and interface type.
<li><b>Host Interface:</b>The type of connection to the test computer
(for direct connection) or the bridge (for bridged connection) .
used to connect the test drive.
<li><b>Connection:</b>Either <i>direct</i> or <i>bridge</i> indicates if the test
drive is connected to the test computer directly or via a bridge. If connected via
a bridge, the bridge description is included.
<li><b>Hidden Sectors:</b>Indicates the presence and type of hidden sectors.
<li><b>Wipe Method:</b>The selected method for wiping a drive.
</ul>
'''
drive_layouts = '''
<h3>Defined Test Drive Layouts</h3>
<p>
The following table describes the layout of the test drive for each test configuration.
<ul>
<li><b>Drive Type:</b>The drive size category and interface type.
<li><b>Drive Size:</b>The drive size in sectors and bytes.
<li><b>Hidden Sectors:</b>The size in sectors of any hidden area and the type 
of hidden area.
</ul>
'''
main_section = '''
<p>
<h1>Test Results by Test Configuration</h1>
<p>
This section has two subsections: a summary of the test results and detailed results 
for each test configuration.
<h2>Results Summary</h2>
<p>
The following table reports the overall result for each tested configuration.
An entry of <i>Anomaly</i> means that some sectors were not wiped. An entry 
of <i>As Expected</i> means that all sectors were completely overwritten or erased.
'''


table_head = """
<table style="text-align: left;" border="2" cellpadding="2" cellspacing="2">
<tbody>
"""

table_tail = """
</tbody>
</table>
"""

#######################################################
#     main
#########################################################
#########################################################
########### S t a r t    H e r e ########################
#########################################################
#########################################################


conf = "fmp-config.txt"
sd.write("START\n")


html.write(head+'\n')
#html.write(head)
html.write('''<h1>Test Results for Forensic Media Preparation Tool:<br>
	%s Version %s</h1>\n'''%(tool_name,tool_version))
html.write(report_header_text_part_0)
html.write('''<h1>Test Results for Forensic Media Preparation Tool:<br>
	%s Version %s</h1>\n'''%(tool_name,tool_version))
html.write(report_header_text_part_1%(tool_name,tool_version))
html.write(report_header_text_part_2)

#cases_intro = '''
#<h2>Test Case Descriptions</h2>
#<p>
#The following table gives a brief description of available test cases in the data sets. Not 
#all test cases are used for all data sets.
#<br>

#<em class="x">
#You can delete the row in the table for any cases not used.
#</em>
#'''

#html.write (cases_intro)

config_file_name = root+'/fmp_config.txt'
if os.access (config_file_name,os.F_OK):
	cf = open(config_file_name,"r")
#	html.write ('<p>Configurations found<br>')
	html.write(table_head)
	html.write ('<tr>')
	html.write ('<th>Config</th>')
	html.write ('<th>Drive Type</th>')
	html.write ('<th>Host Interface</th>')
	html.write ('<th>Connection</th>')
	html.write ('<th>Hidden Sectors</th>')
	html.write ('<th>Wipe Method</th>')
	html.write ('</tr>')
	for line in cf:
#		html.write('<br>')
#		html.write(line)
		fields = line.strip().split(':')
		if len(fields) == 7:
			(cid,media_type,host_interface,connection,hidden_sectors,wipe_method,
				bridge_desc) = line.split(':')
#			print len(fields),fields
			ana_file_name = root+'/'+cid+'-analysis.txt'
			if os.access (ana_file_name,os.F_OK):
				html.write('<tr>')
				html.write('<td>')
				html.write(cid)
				html.write('</td>')
				html.write('<td>')
				html.write(media_type)
				html.write('</td>')
				html.write('<td>')
				html.write(host_interface)
				html.write('</td>')
				html.write('<td>')
				html.write(connection)
				if connection == 'bridge':
					html.write (': ')
					html.write(bridge_desc)
				html.write('</td>')
				html.write('<td>')
				html.write(hidden_sectors)
				html.write('</td>')
				html.write('<td>')
				html.write(wipe_method)
				html.write('</td>')
				html.write('</tr>')
	html.write(table_tail)
else:
	html.write ('<p>No configurations found<br>')


html.write(drive_layouts)
#html.write('<h1>Test Result Details by Case</h1>')
sub_dirs = os.listdir(root)
se_list = []
for item in sub_dirs:
#	print 'item ==> %s'%(item)
	if item.startswith('se_') == True:
		se_list.append(item)
start_results = '''
<p>
'''
table_desc = '''
<p>
The table columns contain the following information:
<ul>
<li>
<b>Case</b> The test case identifier.
<li>
<b>Expected String</b> The strings that should be reported by the search.
<li>
<b>Active Files</b> A group of three columns (<b>Expected, Hits and Misses</b>) giving
the number of hits and misses when searching for the expected string in an active file.
<li>
<b>Deleted Files</b> A group of three columns (<b>Expected, Hits and Misses</b>) giving
the number of hits and misses when searching for the expected string in a deleted file.
<li>
<b>Unallocated Space</b> A group of three columns (<b>Expected, Hits and Misses</b>) giving
the number of hits and misses when searching for the expected string in unallocated space.
<li>
<b>Expected</b> The number of instances of the expected string found in the group (i.e., Active files,
Deleted files or Unallocated space).
<li>
<b>Hits</b> The number of times the expected string was found in the group.
<li>
<b>Misses</b> The number of times the expected string was missed (not found) in the group.
</ul>
<p>
Notes:
If the row identifies a test case, then the results are a summary for all the strings that
should be found.
<p>
<br>
'''
html.write(start_results)

configs = get_configs(root)
results = get_results(root+'/',configs)
drives = get_drives(root+'/',configs)
dk = drives.keys()
dk.sort()
html.write('<p>'+table_head)
html.write ('<tr>')
html.write ('<th>Config</th>')
html.write ('<th>Drive Type</th>')
html.write ('<th>Drive Size</th>')
html.write ('<th>Hidden Sectors</th>')
html.write ('</tr>')
for cid in dk:
	html.write ('<tr>')
	html.write ('<td>%s</td>'%(cid))
	html.write ('<td>%s</td>'%(configs[cid][1]))
	html.write ('<td>%s'%(format(int(drives[cid][0]),',')))
	uz = get_unit(int(drives[cid][0]))
	html.write (' (%s)'%(str(uz[0])+uz[1]))
	html.write ('</td>')
	html.write ('<td>%s'%(format (int(drives[cid][0]) - int(drives[cid][1]),',')))
	if configs[cid][4] != 'none':
		html.write (' (%s)'%(configs[cid][4]))
	html.write ('</td>')
	html.write ('</tr>')

html.write(table_tail+'<p>')

html.write(main_section)
rk = results.keys()
rk.sort()
#print 'Result Keys:',rk
html.write('<p>'+table_head)
html.write ('<tr>')
html.write ('<th>Config</th>')
html.write ('<th>Drive Type</th>')
html.write ('<th>Host Interface</th>')
html.write ('<th>Connection</th>')
html.write ('<th>Hidden Sectors</th>')
html.write ('<th>Wipe Method</th>')
html.write ('<th>Results</th>')
html.write ('</tr>')
for cid in rk:
#	print '========== ',cid
#	print configs[cid]
	(fn,drive,port,path,hidden,method,bridge_desc,state) = configs[cid]
	html.write ('<tr>')
	html.write ('<td>%s</td>'%(cid))
	html.write ('<td>%s</td>'%(drive))
	html.write ('<td>%s</td>'%(port))
	if path == 'bridge':
		html.write ('<td>%s</td>'%(path+': '+bridge_desc))
	else:
		html.write ('<td>%s</td>'%(path))
	html.write ('<td>%s</td>'%(hidden))
	html.write ('<td>%s</td>'%(method))
	cat_keys = results[cid].keys()
	if len(cat_keys) == 1 and cat_keys[0] == 'overwritten':
		html.write ('<td>As Expected</td>')
	else:
		html.write ('<td>Anomaly</td>')
	html.write ('</tr>')
html.write(table_tail+'<p>')
details_head = '''
<h2>Test Result Details by Configuration</h2>
<p>
This sections presents the detailed analysis of each configuration test.
Each analysis is presented as a table of sector runs for sectors as identified as either
<i>unchanged</i> or <i>overwritten</i>. A successful test result is for all sectors
to be overwritten.
<p>
The columns of the tables of sector runs are as follows:
<ul>
<li><b>Result Type:</b>Category of result, either <i> overwritten</i>
or <i>unchanged</i>. Sectors that
have been relocated (still with original content) are classified as <i>shifted</i> and
considered as a variation on <i>unchanged</i>.
<li><b>N Sectors:</b> The number of sectors in the category.
<li><b>N Runs:</b> The number of sector runs in the category.
<li><b>Start LBA:</b> For each sector run, this is the LBA of the first sector of the run.
<li><b>End LBA:</b> For each sector run, this is the LBA of the last sector of the run.
<li><b>Run Length:</b> For each sector run, the number of sectors in the run.
</ul>
'''
html.write(details_head)
for cid in rk:
	html.write('<h3>Test Result Details for Configuration %s</h3>\n'%(cid))
	cat_keys = results[cid].keys()
	if len(cat_keys) == 1 and cat_keys[0] == 'overwritten':
		html.write ('<p>Expected Results: Configuration %s all sectors overwritten'%(cid))
		html.write ('<p>')
	else:
		html.write ('<p>Anomaly Detected: Configuration %s not all sectors overwritten'%(cid))
		html.write ('<p>')
	html.write(table_head)
	html.write ('<tr>')
	html.write ('<th>Result Type</th>')
	html.write ('<th>N Sectors</th>')
	html.write ('<th>N Runs</th>')
	html.write ('<th>Start LBA</th>')
	html.write ('<th>End LBA</th>')
	html.write ('<th>Run Length</th>')
	html.write ('</tr>')
	for cat in results[cid]:
		html.write ('<tr>')
		html.write ('<th>%s</th>'%(cat))
		html.write ('<th>%s</th>'%(format(results[cid][cat].sectors,',')))
		html.write ('<th>%s</th>'%(format(len(results[cid][cat].runs),',')))
		html.write ('<th>%s</th>'%(''))
		html.write ('<th>%s</th>'%(''))
		html.write ('<th>%s</th>'%(''))
		html.write ('</tr>')
		rlist = results[cid][cat].runs
		for r in rlist:
			html.write ('<tr>')
			html.write ('<th>%s</th>'%(''))
			html.write ('<th>%s</th>'%(''))
			html.write ('<th>%s</th>'%(''))
			html.write ('<th>%s</th>'%(format(int(r.lba_from),',')))
			html.write ('<th>%s</th>'%(format(int(r.lba_to),',')))
			html.write ('<th>%s</th>'%(format(1+int(r.lba_to)-int(r.lba_from),',')))
			html.write ('</tr>')
			
	html.write(table_tail)

html.write('<br><p>END of REPORT\n')
html.write(tail)

exit (0)
