dcerpc-nt: add UNION_ALIGN_TO... helpers
[wireshark-sm.git] / tools / msnchat
blobc2fcaabe9e0212c56d6ad39fbeb7084c896d7177
1 #!/usr/bin/env python
2 """
3 Process packet capture files and produce a nice HTML
4 report of MSN Chat sessions.
6 Copyright (c) 2003 by Gilbert Ramirez <gram@alumni.rice.edu>
8 SPDX-License-Identifier: GPL-2.0-or-later
9 """
11 import os
12 import re
13 import sys
14 import array
15 import string
16 import WiresharkXML
17 import getopt
19 # By default we output the HTML to stdout
20 out_fh = sys.stdout
22 class MSNMessage:
23 pass
25 class MSN_MSG(MSNMessage):
26 def __init__(self, timestamp, user, message):
27 self.timestamp = timestamp
28 self.user = user
29 self.message = message
32 class Conversation:
33 """Keeps track of a single MSN chat session"""
35 re_MSG_out = re.compile("MSG (?P<TrID>\d+) (?P<ACKTYPE>[UNA]) (?P<len>\d+)")
36 re_MSG_in = re.compile("MSG (?P<user>\S+)@(?P<domain>\S+) (?P<alias>\S+) (?P<len>\d+)")
38 USER_NOT_FOUND = -1
39 DEFAULT_USER = None
42 DEFAULT_USER_COLOR = "#0000ff"
43 USER_COLORS = [ "#ff0000", "#00ff00",
44 "#800000", "#008000", "#000080" ]
46 DEFAULT_USER_TEXT_COLOR = "#000000"
47 USER_TEXT_COLOR = "#000080"
49 def __init__(self):
50 self.packets = []
51 self.messages = []
53 def AddPacket(self, packet):
54 self.packets.append(packet)
56 def Summarize(self):
57 for packet in self.packets:
58 msg = self.CreateMSNMessage(packet)
59 if msg:
60 self.messages.append(msg)
61 else:
62 #XXX
63 pass
66 def CreateMSNMessage(self, packet):
67 msnms = packet.get_items("msnms")[0]
69 # Check the first line in the msnms transmission for the user
70 child = msnms.children[0]
71 user = self.USER_NOT_FOUND
73 m = self.re_MSG_out.search(child.show)
74 if m:
75 user = self.DEFAULT_USER
77 else:
78 m = self.re_MSG_in.search(child.show)
79 if m:
80 user = m.group("alias")
82 if user == self.USER_NOT_FOUND:
83 print >> sys.stderr, "No match for", child.show
84 sys.exit(1)
85 return None
87 msg = ""
89 i = 5
90 check_trailing = 0
91 if len(msnms.children) > 5:
92 check_trailing = 1
94 while i < len(msnms.children):
95 msg += msnms.children[i].show
96 if check_trailing:
97 j = msg.find("MSG ")
98 if j >= 0:
99 msg = msg[:j]
100 i += 5
101 else:
102 i += 6
103 else:
104 i += 6
106 timestamp = packet.get_items("frame.time")[0].get_show()
107 i = timestamp.rfind(".")
108 timestamp = timestamp[:i]
110 return MSN_MSG(timestamp, user, msg)
112 def MsgToHTML(self, text):
113 bytes = array.array("B")
115 new_string = text
116 i = new_string.find("\\")
118 while i > -1:
119 # At the end?
120 if i == len(new_string) - 1:
121 # Just let the default action
122 # copy everything to 'bytes'
123 break
125 if new_string[i+1] in string.digits:
126 left = new_string[:i]
127 bytes.fromstring(left)
129 right = new_string[i+4:]
131 oct_string = new_string[i+1:i+4]
132 char = int(oct_string, 8)
133 bytes.append(char)
135 new_string = right
137 # ignore \r and \n
138 elif new_string[i+1] in "rn":
139 copy_these = new_string[:i]
140 bytes.fromstring(copy_these)
141 new_string = new_string[i+2:]
143 else:
144 copy_these = new_string[:i+2]
145 bytes.fromstring(copy_these)
146 new_string = new_string[i+2:]
148 i = new_string.find("\\")
151 bytes.fromstring(new_string)
153 return bytes
155 def CreateHTML(self, default_user):
156 if not self.messages:
157 return
159 print >> out_fh, """
160 <HR><BR><H3 Align=Center> ---- New Conversation @ %s ----</H3><BR>""" \
161 % (self.messages[0].timestamp)
163 user_color_assignments = {}
165 for msg in self.messages:
166 # Calculate 'user' and 'user_color' and 'user_text_color'
167 if msg.user == self.DEFAULT_USER:
168 user = default_user
169 user_color = self.DEFAULT_USER_COLOR
170 user_text_color = self.DEFAULT_USER_TEXT_COLOR
171 else:
172 user = msg.user
173 user_text_color = self.USER_TEXT_COLOR
174 if user_color_assignments.has_key(user):
175 user_color = user_color_assignments[user]
176 else:
177 num_assigned = len(user_color_assignments.keys())
178 user_color = self.USER_COLORS[num_assigned]
179 user_color_assignments[user] = user_color
181 # "Oct 6, 2003 21:45:25" --> "21:45:25"
182 timestamp = msg.timestamp.split()[-1]
184 htmlmsg = self.MsgToHTML(msg.message)
186 print >> out_fh, """
187 <FONT COLOR="%s"><FONT SIZE="2">(%s) </FONT><B>%s:</B></FONT> <FONT COLOR="%s">""" \
188 % (user_color, timestamp, user, user_text_color)
190 htmlmsg.tofile(out_fh)
192 print >> out_fh, "</FONT><BR>"
195 class CaptureFile:
196 """Parses a single a capture file and keeps track of
197 all chat sessions in the file."""
199 def __init__(self, capture_filename, tshark):
200 """Run tshark on the capture file and parse
201 the data."""
202 self.conversations = []
203 self.conversations_map = {}
205 pipe = os.popen(tshark + " -Tpdml -n -R "
206 "'msnms contains \"X-MMS-IM-Format\"' "
207 "-r " + capture_filename, "r")
209 WiresharkXML.parse_fh(pipe, self.collect_packets)
211 for conv in self.conversations:
212 conv.Summarize()
214 def collect_packets(self, packet):
215 """Collect the packets passed back from WiresharkXML.
216 Sort them by TCP/IP conversation, as there could be multiple
217 clients per machine."""
218 # Just in case we're looking at tunnelling protocols where
219 # more than one IP or TCP header exists, look at the last one,
220 # which would be the one inside the tunnel.
221 src_ip = packet.get_items("ip.src")[-1].get_show()
222 dst_ip = packet.get_items("ip.dst")[-1].get_show()
223 src_tcp = packet.get_items("tcp.srcport")[-1].get_show()
224 dst_tcp = packet.get_items("tcp.dstport")[-1].get_show()
226 key_params = [src_ip, dst_ip, src_tcp, dst_tcp]
227 key_params.sort()
228 key = '|'.join(key_params)
230 if not self.conversations_map.has_key(key):
231 conv = self.conversations_map[key] = Conversation()
232 self.conversations.append(conv)
233 else:
234 conv = self.conversations_map[key]
236 conv.AddPacket(packet)
239 def CreateHTML(self, default_user):
240 if not self.conversations:
241 return
243 for conv in self.conversations:
244 conv.CreateHTML(default_user)
247 def run_filename(filename, default_user, tshark):
248 """Process one capture file."""
250 capture = CaptureFile(filename, tshark)
251 capture.CreateHTML(default_user)
254 def run(filenames, default_user, tshark):
255 # HTML Header
256 print >> out_fh, """
257 <HTML><TITLE>MSN Conversation</TITLE>
258 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
259 <BODY>
261 for filename in filenames:
262 run_filename(filename, default_user, tshark)
264 # HTML Footer
265 print >> out_fh, """
266 <HR>
267 </BODY>
268 </HTML>
272 def usage():
273 print >> sys.stderr, "msnchat [OPTIONS] CAPTURE_FILE [...]"
274 print >> sys.stderr, " -o FILE name of output file"
275 print >> sys.stderr, " -t TSHARK location of tshark binary"
276 print >> sys.stderr, " -u USER name for unknown user"
277 sys.exit(1)
279 def main():
280 default_user = "Unknown"
281 tshark = "tshark"
283 optstring = "ho:t:u:"
284 longopts = ["help"]
286 try:
287 opts, args = getopt.getopt(sys.argv[1:], optstring, longopts)
288 except getopt.GetoptError:
289 usage()
291 for opt, arg in opts:
292 if opt == "-h" or opt == "--help":
293 usage()
295 elif opt == "-o":
296 filename = arg
297 global out_fh
298 try:
299 out_fh = open(filename, "w")
300 except IOError:
301 sys.exit("Could not open %s for writing." % (filename,))
303 elif opt == "-u":
304 default_user = arg
306 elif opt == "-t":
307 tshark = arg
309 else:
310 sys.exit("Unhandled command-line option: " + opt)
312 run(args, default_user, tshark)
314 if __name__ == '__main__':
315 main()