1"""
2Generates packet or reconstructed stream output as a HTML page.
3
4Based on colorout module originally written by amm
5"""
6
7from dshell.output.output import Output
8import dshell.util
9import dshell.core
10from xml.sax.saxutils import escape
11
12class HTMLOutput(Output):
13    _DESCRIPTION = "HTML format output"
14    _PACKET_FORMAT = """<h1>Packet %(counter)s (%(protocol)s)</h1><h2>Start: %(ts)s
15%(sip)s:%(sport)s -> %(dip)s:%(dport)s (%(bytes)s bytes)
16</h2>
17%(data)s
18"""
19    _CONNECTION_FORMAT = """<h1>Connection %(counter)s (%(protocol)s)</h1><h2>Start: %(starttime)s
20End: %(endtime)s
21%(clientip)s:%(clientport)s -> %(serverip)s:%(serverport)s (%(clientbytes)s bytes)
22%(serverip)s:%(serverport)s -> %(clientip)s:%(clientport)s (%(serverbytes)s bytes)
23</h2>
24%(data)s
25"""
26    _DEFAULT_FORMAT = _PACKET_FORMAT
27    _DEFAULT_DELIM = "<br />"
28
29    _HTML_HEADER = """
30<html>
31<head>
32    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
33    <title>Dshell Output</title>
34    <style>
35        body {
36            font-family: monospace;
37            font-size: 10pt;
38            white-space: pre;
39        }
40        h1 {
41            font-family: helvetica;
42            font-size: 13pt;
43            font-weight: bolder;
44            white-space: pre;
45        }
46        h2 {
47            font-family: helvetica;
48            font-size: 12pt;
49            font-weight: bolder;
50            margin: 0 0;
51            white-space: pre;
52        }
53    </style>
54</head>
55<body>
56"""
57
58    _HTML_FOOTER = """
59</body>
60</html>
61"""
62
63    def __init__(self, *args, **kwargs):
64        "Can be called with an optional 'hex' argument to display output in hex"
65        super().__init__(*args, **kwargs)
66        self.counter = 1
67        self.colors = {
68            'cs': 'red',   # client-to-server is red
69            'sc': 'green',   # server-to-client is green
70            '--': 'blue',   # everything else is blue
71        }
72        self.hexmode = kwargs.get('hex', False)
73        self.format_is_set = False
74
75    def setup(self):
76        self.fh.write(self._HTML_HEADER)
77
78    def write(self, *args, **kwargs):
79        if not self.format_is_set:
80            if 'clientip' in kwargs:
81                self.set_format(self._CONNECTION_FORMAT)
82            else:
83                self.set_format(self._PACKET_FORMAT)
84            self.format_is_set = True
85
86        # a template string for data output
87        colorformat = '<span style="color:%s;">%s</span>'
88
89        # Iterate over the args and try to parse out any raw data strings
90        rawdata = []
91        for arg in args:
92            if type(arg) == dshell.core.Blob:
93                if arg.data:
94                    rawdata.append((arg.data, arg.direction))
95            elif type(arg) == dshell.core.Connection:
96                for blob in arg.blobs:
97                    if blob.data:
98                        rawdata.append((blob.data, blob.direction))
99            elif type(arg) == dshell.core.Packet:
100                rawdata.append((arg.pkt.body_bytes, kwargs.get('direction', '--')))
101            elif type(arg) == tuple:
102                rawdata.append(arg)
103            else:
104                rawdata.append((arg, kwargs.get('direction', '--')))
105
106        # Clean up the rawdata into something more presentable
107        if self.hexmode:
108            cleanup_func = dshell.util.hex_plus_ascii
109        else:
110            cleanup_func = dshell.util.printable_text
111        for k, v in enumerate(rawdata):
112            newdata = cleanup_func(v[0])
113            newdata = escape(newdata)
114            rawdata[k] = (newdata, v[1])
115
116        # Convert the raw data strings into color-coded output
117        data = []
118        for arg in rawdata:
119            datastring = colorformat % (self.colors.get(arg[1], ''), arg[0])
120            data.append(datastring)
121
122        super().write(counter=self.counter, *data, **kwargs)
123        self.counter += 1
124
125    def close(self):
126        self.fh.write(self._HTML_FOOTER)
127        Output.close(self)
128
129obj = HTMLOutput
130