0001 #!/usr/bin/env python 0002 0003 ### BITPIM 0004 ### 0005 ### Copyright (C) 2003-2004 Roger Binns <rogerb@rogerbinns.com> 0006 ### 0007 ### This program is free software; you can redistribute it and/or modify 0008 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0009 ### 0010 ### $Id: analyser.py 3037 2006-04-03 00:30:28Z rogerb $ 0011 0012 """Graphical view of protocol data and a decode of it""" 0013 0014 import sys 0015 import re 0016 import traceback 0017 import wx 0018 import StringIO 0019 import struct 0020 0021 import common 0022 import prototypes 0023 0024 import hexeditor 0025 0026 class Eventlist(wx.ListCtrl): 0027 "List control showing the various events" 0028 0029 def __init__(self, parent, id=-1, events=[]): 0030 self.events=events 0031 wx.ListCtrl.__init__(self, parent, id, style=wx.LC_REPORT|wx.LC_VIRTUAL) 0032 self.InsertColumn(0, "Time") 0033 self.InsertColumn(1, "Size") 0034 self.InsertColumn(2, "Class") 0035 self.InsertColumn(3, "Description") 0036 0037 self.SetColumnWidth(0, 100) 0038 self.SetColumnWidth(1, 50) 0039 self.SetColumnWidth(2, 200) 0040 self.SetColumnWidth(3, 1000) 0041 0042 self.SetItemCount(len(events)) 0043 0044 def newdata(self, events): 0045 self.DeleteAllItems() 0046 self.events=events 0047 self.SetItemCount(len(events)) 0048 0049 def OnGetItemText(self, index, col): 0050 curtime, curdesc, curclass, curdata=self.events[index] 0051 if col==0: 0052 return curtime 0053 if col==1: 0054 if len(curdata): 0055 return "%5d" % (len(curdata),) 0056 return "" 0057 if col==2: 0058 return curclass 0059 if col==3: 0060 return curdesc 0061 assert False 0062 0063 def OnGetItemImage(self, item): 0064 return -1 0065 0066 0067 0068 class Analyser(wx.Frame): 0069 """A top level frame for analysing protocol data""" 0070 _pane_widths=[-2, -3, -4] 0071 _pos_pane_index=0 0072 _sel_pane_index=1 0073 _val_pane_index=2 0074 0075 def __init__(self, parent=None, id=-1, title="BitPim Protocol Analyser", data=None): 0076 """Start the show 0077 0078 @param data: data to show. If None, then it will be obtained from the clipboard 0079 """ 0080 wx.Frame.__init__(self, parent, id, title, size=(800,750), 0081 style=wx.DEFAULT_FRAME_STYLE) 0082 # add a status bar to display various status items 0083 self.CreateStatusBar(len(self._pane_widths)) 0084 self.SetStatusWidths(self._pane_widths) 0085 0086 topsplit=wx.SplitterWindow(self, -1, style=wx.SP_3D|wx.SP_LIVE_UPDATE) 0087 0088 self.list=Eventlist(topsplit, 12) 0089 0090 botsplit=wx.SplitterWindow(topsplit, -1, style=wx.SP_3D|wx.SP_LIVE_UPDATE) 0091 topsplit.SplitHorizontally(self.list, botsplit, 300) 0092 0093 self.tree=wx.TreeCtrl(botsplit, 23, style=wx.TR_DEFAULT_STYLE) 0094 self.hex=hexeditor.HexEditor(botsplit, 0095 _set_pos=self.set_pos, 0096 _set_sel=self.set_sel, 0097 _set_val=self.set_val) 0098 botsplit.SplitHorizontally(self.tree, self.hex, 200) 0099 0100 if data is None: 0101 data=self.getclipboarddata() 0102 0103 self.newdata(data) 0104 0105 wx.EVT_LIST_ITEM_SELECTED(self, self.list.GetId(), self.OnListBoxItem) 0106 wx.EVT_LIST_ITEM_ACTIVATED(self, self.list.GetId(), self.OnListBoxItem) 0107 0108 wx.EVT_TREE_SEL_CHANGED(self, self.tree.GetId(), self.OnTreeSelection) 0109 0110 self.Show() 0111 0112 def newdata(self, data): 0113 "We have new data - the old data is tossed" 0114 self.parsedata(data) 0115 self.list.newdata(self.packets) 0116 0117 def OnListBoxItem(self,evt): 0118 "The user selected an event in the listbox" 0119 index=evt.m_itemIndex 0120 curtime, curdesc, curclass, curdata=self.packets[index] 0121 self.errorinfo="" 0122 self.hex.SetData("") 0123 self.hex.highlightrange(-1,-1) 0124 if len(curdata): 0125 self.hex.SetData(curdata) 0126 # self.hex.ShowPosition(self.hex.XYToPosition(0,0)) 0127 else: 0128 self.hex.SetData(curdesc) 0129 # self.hex.ShowPosition(self.hex.XYToPosition(0,0)) 0130 0131 self.tree.DeleteAllItems() 0132 if len(curclass): 0133 b=prototypes.buffer(curdata) 0134 try: 0135 klass=common.getfullname(curclass) 0136 except Exception,e: 0137 self.errorme("Finding class",e) 0138 wx.TipWindow(self.tree,self.errorinfo) 0139 return 0140 try: 0141 obj=klass() 0142 except Exception,e: 0143 self.errorme("Instantiating object",e) 0144 wx.TipWindow(self.tree,self.errorinfo) 0145 return 0146 0147 try: 0148 obj.readfrombuffer(b, autolog=False) 0149 except Exception,e: 0150 self.errorme("Reading from buffer",e) 0151 # no return, we persevere 0152 0153 root=self.tree.AddRoot(curclass) 0154 try: 0155 self.tree.SetPyData(root, obj.packetspan()) 0156 except: 0157 self.errorme("Object did not construct correctly") 0158 # no return, we persevere 0159 self.addtreeitems(obj, root) 0160 if len(self.errorinfo): 0161 wx.TipWindow(self.tree,self.errorinfo) 0162 0163 def addtreeitems(self, obj, parent): 0164 "Add fields from obj to parent node" 0165 try: 0166 for name,field,desc in obj.containerelements(): 0167 if desc is None: 0168 desc="" 0169 else: 0170 desc=" - "+desc 0171 iscontainer=False 0172 try: 0173 iscontainer=field.iscontainer() 0174 except: 0175 pass 0176 # Add ourselves 0177 s=field.__class__.__name__+" "+name 0178 if iscontainer: 0179 c=field.__class__ 0180 s+=": <%s.%s>" % (c.__module__, c.__name__) 0181 else: 0182 try: 0183 v=field.getvalue() 0184 except Exception,e: 0185 v="<Exception: "+e.__str__()+">" 0186 s+=": " 0187 if isinstance(v, int) and not isinstance(v, type(True)): 0188 s+="%d 0x%x" % (v,v) 0189 else: 0190 s+=`v` 0191 if len(desc): 0192 s+=desc 0193 node=self.tree.AppendItem(parent, s) 0194 try: 0195 self.tree.SetPyData(node, field.packetspan()) 0196 except: 0197 pass 0198 if iscontainer: 0199 self.addtreeitems(field, node) 0200 except Exception,e: 0201 str="<Exception: "+e.__str__()+">" 0202 self.tree.AppendItem(parent,str) 0203 0204 def OnTreeSelection(self, evt): 0205 "User selected an item in the tree" 0206 item=evt.GetItem() 0207 try: 0208 start,end=self.tree.GetPyData(item) 0209 except: 0210 self.hex.highlightrange(-1,-1) 0211 return 0212 self.hex.highlightrange(start,end) 0213 # self.hex.ShowPosition(begin) 0214 0215 def errorme(self, desc, exception=None): 0216 "Put exception information into the hex pane and output traceback to console" 0217 if exception is not None: 0218 x=StringIO.StringIO() 0219 print >>x,exception.__str__(), 0220 self.errorinfo+=x.getvalue()+" : " 0221 print >>sys.stderr, common.formatexception() 0222 self.errorinfo+=desc+"\n" 0223 0224 def getclipboarddata(self): 0225 """Gets text data on clipboard""" 0226 do=wx.TextDataObject() 0227 wx.TheClipboard.Open() 0228 success=wx.TheClipboard.GetData(do) 0229 wx.TheClipboard.Close() 0230 if not success: 0231 wx.MessageBox("Whatever is in the clipboard isn't text", "No way Dude") 0232 return "" 0233 return do.GetText() 0234 0235 patevent=re.compile(r"^(\d?\d:\d\d:\d\d\.\d\d\d)(.*)") 0236 patdataevent=re.compile(r"^(\d?\d:\d\d:\d\d\.\d\d\d)(.*)(Data - \d+ bytes.*)") 0237 patdatarow=re.compile(r"^([0-9A-Fa-f]{8})(.*)") 0238 patclass=re.compile(r"^<#!\s+(.*)\s+!#>") 0239 0240 def parsedata(self, data): 0241 """Fills in our internal data structures based on contents of data""" 0242 0243 # santise all the data by doing the eol nonsense 0244 data=data.replace("\r", "\n") 0245 lastlen=0 0246 while lastlen!=len(data): 0247 lastlen=len(data) 0248 data=data.replace("\n\n", "\n") 0249 0250 self.packets=[] 0251 0252 curtime=curdesc=curclass=curdata="" 0253 0254 indata=False 0255 0256 for line in data.split('\n'): 0257 # ignore blank lines 0258 if len(line.strip())==0: 0259 continue 0260 mo=self.patclass.match(line) 0261 if mo is not None: 0262 # found a class description 0263 curclass=mo.group(1) 0264 indata=True 0265 continue 0266 # if indata, try for some more 0267 if indata: 0268 mo=self.patdatarow.match(line) 0269 if mo is not None: 0270 # found another data row 0271 pos=int(mo.group(1), 16) 0272 assert pos==len(curdata) 0273 for i in range(9, min(len(line), 9+16*3), 3): # at most 16 bytes 0274 s=line[i:i+2] 0275 if len(s)!=2 or s==" ": 0276 # last line with trailing spaces 0277 continue 0278 b=int(s,16) 0279 curdata+=chr(b) 0280 continue 0281 # end of data, save it 0282 indata=False 0283 self.packets.append( (curtime, curdesc, curclass, curdata) ) 0284 curtime=curdesc=curclass=curdata="" 0285 # and move on 0286 # data event? 0287 mo=self.patdataevent.match(line) 0288 if mo is not None: 0289 self.packets.append( (curtime, curdesc, curclass, curdata) ) 0290 curtime=curdesc=curclass=curdata="" 0291 curtime=mo.group(1) 0292 curdesc=mo.group(2)+mo.group(3) 0293 indata=True 0294 continue 0295 # ordinary event? 0296 mo=self.patevent.match(line) 0297 if mo is not None: 0298 self.packets.append( (curtime, curdesc, curclass, curdata) ) 0299 curtime=curdesc=curclass=curdata="" 0300 curtime=mo.group(1) 0301 curdesc=mo.group(2) 0302 indata=True 0303 continue 0304 # No idea what it is, just add on end of desc 0305 if len(curdesc): 0306 curdesc+="\n" 0307 curdesc+=line 0308 0309 # Add whatever is in variables at end 0310 self.packets.append( (curtime, curdesc, curclass, curdata) ) 0311 0312 # remove all blank lines 0313 # filter, reduce, map and lambda all in one go! 0314 self.packets=filter(lambda item: reduce(lambda x,y: x+y, map(len, item)), self.packets) 0315 0316 def set_pos(self, pos): 0317 """Display the current buffer offset in the format of 0318 Pos: 0x12=18 0319 """ 0320 if pos is None: 0321 s='' 0322 else: 0323 s='Pos: 0x%X=%d'%(pos, pos) 0324 self.SetStatusText(s, self._pos_pane_index) 0325 def set_sel(self, sel_start, sel_end): 0326 if sel_start is None or sel_start==-1 or\ 0327 sel_end is None or sel_end==-1: 0328 s='' 0329 else: 0330 sel_len=sel_end-sel_start 0331 sel_end-=1 0332 s='Sel: 0x%X=%d to 0x%X=%d (0x%X=%d bytes)'%( 0333 sel_start, sel_start, sel_end, sel_end, 0334 sel_len, sel_len) 0335 self.SetStatusText(s, self._sel_pane_index) 0336 def set_val(self, v): 0337 if v: 0338 # char 0339 s='Val: 0x%02X=%d'%(ord(v[0]), ord(v[0])) 0340 if len(v)>1: 0341 # short 0342 u_s=struct.unpack('<H', v[:struct.calcsize('<H')])[0] 0343 s+=' 0x%04X=%d'%(u_s, u_s) 0344 if len(v)>3: 0345 # int/long 0346 u_i=struct.unpack('<I', v[:struct.calcsize('<I')])[0] 0347 s+=' 0x%08X=%d'%(u_i, u_i) 0348 else: 0349 s='' 0350 self.SetStatusText(s, self._val_pane_index) 0351 0352 if __name__=='__main__': 0353 app=wx.PySimpleApp() 0354 # Find the data source 0355 data=None 0356 if len(sys.argv)==2: 0357 # From a file 0358 data=common.opentextfile(sys.argv[1]).read() 0359 frame=Analyser(data=data) 0360 app.MainLoop() 0361
Generated by PyXR 0.9.4