root/branch/PacketManipulator/PM/Gui/Widgets/Plotter.py @ 3618

Revision 3618, 11.3 kB (checked in by nopper, 5 years ago)

Fixing

Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# Copyright (C) 2008 Adriano Monteiro Marques
4#
5# Author: Francesco Piccinno <stack.box@gmail.com>
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
21import gtk
22import cairo
23import pango
24import gobject
25import pangocairo
26
27from PM import Backend
28
29from math import pi
30from collections import defaultdict
31
32class Plotter(gtk.DrawingArea):
33    __gtype_name__ = "Plotter"
34
35    def __init__(self, packet):
36        self.colors = (
37            gtk.gdk.color_parse('#FFFA99'),
38            gtk.gdk.color_parse('#8DFF7F'),
39            gtk.gdk.color_parse('#FFE3E5'),
40            gtk.gdk.color_parse('#C797FF'),
41            gtk.gdk.color_parse('#A0A0A0'),
42            gtk.gdk.color_parse('#D6E8FF'),
43            gtk.gdk.color_parse('#C2C2FF'),
44        )
45
46        self.packet = packet
47        self.protocols = [] # (protocol, color)
48        self.fields = {}
49
50        self.hex_font = "Monospace 10"
51        self.title_font = "Monospace 8"
52        self.attr_font = "Monospace 8"
53
54        super(Plotter, self).__init__()
55
56    def draw_text(self, cr, layout, txt):
57        cr.save()
58
59        layout.set_text(txt)
60        cr.show_layout(layout)
61
62        cr.restore()
63
64    def draw_boxed(self, cr, layout, txt, desc=None):
65        cr.save()
66
67        layout.set_text(txt)
68
69        cr.show_layout(layout)
70
71        w, h = layout.get_pixel_size()
72        x, y = cr.get_current_point()
73
74        cr.set_source_rgba(1, 0, 1, 0.3)
75        cr.rectangle(x-2, y-2, w+4, h+4)
76        cr.fill()
77
78        cr.set_source_rgb(0, 0, 0)
79        cr.rectangle(x-2, y-2, w+4, h+4)
80        cr.stroke()
81
82        cr.restore()
83
84        return w+4, h+4
85
86    def __get_color(self, name):
87        return self.colors[hash(name) % len(self.colors)]
88
89    def __cairo_draw(self, cr):
90        cr.save()
91
92        self.fields = {}
93        self.protocols = []
94       
95        for protocol in self.packet.get_protocols():
96            self.protocols.append(
97                    (protocol,
98                     self.__get_color(Backend.get_proto_name(protocol)))
99            )
100
101        self.draw_left(cr)
102        self.draw_payload(cr)
103
104        cr.restore()
105
106    def export_to(self, fname):
107        WIDTH, HEIGHT = 900, 600
108
109        if '.ps' in fname:
110            surface = cairo.PSSurface(fname, WIDTH, HEIGHT)
111            cr = pangocairo.CairoContext(cairo.Context(surface))
112
113            self.__cairo_draw(cr)
114
115            surface.flush()
116        elif '.pdf' in fname:
117            surface = cairo.PDFSurface(fname, WIDTH, HEIGHT)
118            cr = pangocairo.CairoContext(cairo.Context(surface))
119
120            self.__cairo_draw(cr)
121
122            surface.flush()
123        else:
124            surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT)
125            cr = pangocairo.CairoContext(cairo.Context(surface))
126
127            self.__cairo_draw(cr)
128
129            surface.write_to_png(fname)
130
131    def do_expose_event(self, evt):
132        cr = self.window.cairo_create()
133        self.__cairo_draw(cr)
134        return True
135   
136    def draw_line(self, cr, proto, fieldname):
137        points = self.fields[(proto, fieldname)]
138
139        if len(points) < 2:
140            return
141
142        cr.save()
143        cr.set_line_width(1)
144
145        old_x, old_y = cr.get_current_point()
146
147        sx, sy = points[0]
148        ex, ey = points[1]
149
150        mx, my = (ex + sx) / 2, (ey + sy) / 2
151
152        self.set_color(cr, self.__get_color(fieldname), 0.5, 0.7)
153
154        cr.move_to(sx, sy)
155        cr.curve_to(ex, sy, ex, my, ex, ey)
156        cr.stroke()
157
158        # Draw the arrow
159        self.draw_arrow(cr, ex, ey)
160
161        cr.stroke()
162
163        cr.restore()
164
165        cr.move_to(old_x, old_y)
166
167    def draw_arrow(self, cr, x, y):
168        cr.move_to(x, y - 6)
169        cr.rel_line_to(4, 6)
170        cr.rel_line_to(-8, 0)
171        cr.close_path ()
172
173        cr.fill()
174
175    def draw_payload(self, cr):
176        payload = self.packet.get_raw()
177
178        layout = self.create_pango_layout('')
179        layout.set_font_description(pango.FontDescription(self.hex_font))
180
181        layout.set_text("FF")
182        atom_x, atom_y = layout.get_pixel_size()
183
184        atom_x += atom_x / 2.0
185        atom_y += atom_y / 2.0
186
187        start_x = self.allocation.width - (atom_x * 16) - 10
188
189        cr.move_to(start_x, 4)
190
191        # We should have space to place 16 bytes in hex
192
193        dct = defaultdict(list)
194
195        protocol_idx = 0
196
197        for protocol, color in self.protocols:
198
199            for field in Backend.get_proto_fields(protocol):
200                start = Backend.get_field_offset(self.packet, protocol, field)
201                end = Backend.get_field_size(protocol, field)
202
203                start /= 8
204
205                dct[start].append((end, field))
206
207            # Now we have the dict so we have to transform to
208            # a sorted list
209
210            lst = dct.items()
211            lst.sort()
212
213            tot_w = 0
214            tot_h = 0
215
216            for offset, child_list in lst:
217
218                # We have also a child list to iterate
219                size = 0
220
221                if len(child_list) == 1 and child_list[0][0] == 0:
222                    continue
223                else:
224                    for end, field in child_list:
225                        size += end
226
227                    size /= 8
228
229                current_field = child_list[-1][1]
230                field_name = Backend.get_field_name(current_field)
231                fill_color = self.__get_color(field_name)
232                border_color = color
233
234                line_x = offset % 16
235                line_y = offset / 16
236
237                txt = payload[offset:offset + size]
238
239                if size + line_x > 16:
240                    start = 16 - line_x
241
242                    top_right = txt[0:start]
243
244                    top_right = " ".join(["%02X" % ord(x) for x in top_right])
245                    layout.set_text(top_right)
246
247                    cr.move_to(start_x + (line_x * atom_x), (line_y + protocol_idx) * atom_y + 4)
248
249                    # Here we shoul write <==
250                    self.draw_box(cr, layout, fill=fill_color, border=border_color, right=False)
251
252                    w, h = 0, 0
253                    txt = txt[start:]
254                    lines = (len(txt) / 16) + 1
255
256                    for i in xrange(lines):
257                        right, left = False, False
258
259                        if len(txt) > 16:
260                            # here ===
261                            part = txt[i * 16:(i * 16) + 16]
262                        else:
263                            right = True
264                            part = txt[i * 16:]
265
266                        if i == lines - 1:
267                            right = True
268
269                        cr.move_to(start_x, (line_y + protocol_idx + i + 1) * atom_y + 4)
270                       
271                        part = " ".join(["%02X" % ord(x) for x in part])
272                        layout.set_text(part)
273
274
275                        w, h = self.draw_box(cr, layout, fill=fill_color, border=border_color, 
276                                             right=right, left=left)
277
278                    # End point
279                    self.fields[(protocol, field_name)].append((start_x + (w / 2.0),
280                                                               (line_y + protocol_idx + lines + 1) * atom_y + 8))
281                else:
282                    txt = " ".join(["%02X" % ord(x) for x in txt])
283                    layout.set_text(txt)
284
285                    cr.move_to(start_x + (line_x * atom_x), (line_y + protocol_idx) * atom_y + 4)
286
287                    w, h = self.draw_box(cr, layout, fill=fill_color, border=border_color)
288
289                    tot_w += w + 4
290                    tot_h = max(tot_h, h)
291
292                    # End point
293                    self.fields[(protocol, field_name)].append((start_x + (line_x * atom_x) + (w / 2.0),
294                                                               (tot_h + (protocol_idx + line_y) * atom_y + 8)))
295
296                self.draw_line(cr, protocol, field_name)
297
298            cr.rel_move_to(0, tot_h + 4)
299
300            dct = defaultdict(list)
301
302            protocol_idx += 1
303
304    def draw_left(self, cr):
305        layout = self.create_pango_layout('')
306        layout.set_font_description(pango.FontDescription(self.title_font))
307
308        attr_layout = self.create_pango_layout('')
309        attr_layout.set_font_description(pango.FontDescription(self.attr_font))
310
311        cr.move_to(4, 4)
312
313        for protocol, color in self.protocols:
314            name = Backend.get_proto_name(protocol)
315            layout.set_text(name)
316
317            w, h = self.draw_box(cr, layout, fill=color)
318
319            cr.rel_move_to(0, h + 4)
320
321            x, y = cr.get_current_point()
322            cr.move_to(x + 10, y)
323
324            for field in Backend.get_proto_fields(protocol):
325                name = Backend.get_field_name(field)
326                value = str(Backend.get_field_value_repr(protocol, field))
327
328                attr_layout.set_text('%s: %s' % (name, value))
329
330                f_w, f_h = self.draw_text(cr, attr_layout)
331
332                # Start point
333                x, y = cr.get_current_point()
334                self.fields[(protocol, name)] = [(x + f_w + 4, y + (f_h / 2.0))]
335
336                cr.rel_move_to(0, f_h)
337
338            cr.rel_move_to(-10, 10)
339
340    def draw_text(self, cr, layout):
341        cr.show_layout(layout)
342        return layout.get_pixel_size()
343
344    def set_color(self, cr, color, offset=0.05, rgba=1.0):
345        cr.set_source_rgba(float(color.red) / 65535 - offset, 
346                          float(color.green) / 65535 - offset,
347                          float(color.blue) / 65535 - offset, rgba)
348
349    def draw_box(self, cr, layout, fill=None, border=None, bottom=True, top=True, right=True, left=True):
350
351        w, h = layout.get_pixel_size()
352        x, y = cr.get_current_point()
353
354        if fill:
355            cr.save()
356
357            self.set_color(cr, fill)
358            cr.rectangle(x, y, w + 4, h + 4)
359            cr.fill()
360
361            cr.restore()
362
363        cr.save()
364
365        if border:
366            self.set_color(cr, border)
367            cr.set_line_width(2)
368        else:
369            cr.set_source_rgb(0, 0, 0)
370
371        if top:
372            cr.move_to(x, y)
373            cr.line_to(x + w + 4, y)
374            cr.stroke()
375
376        if bottom:
377            cr.move_to(x, y + h + 4)
378            cr.line_to(x + w + 4, y + h + 4)
379            cr.stroke()
380
381        if right:
382            cr.move_to(x + w + 4, y)
383            cr.line_to(x + w + 4, y + h + 4)
384            cr.stroke()
385
386        if left:
387            cr.move_to(x, y)
388            cr.line_to(x, y + h + 4)
389            cr.stroke()
390
391        cr.restore()
392
393        cr.move_to(x + 2, y + 2)
394        cr.show_layout(layout)
395
396        cr.move_to(x, y)
397
398        return w+4, h+4
399
400gobject.type_register(Plotter)
401
402if __name__ == "__main__":
403    packet = Backend.rdpcap("flow.pcap")[22]
404
405    w = gtk.Window()
406    w.add(Plotter(Backend.MetaPacket(packet)))
407    w.show_all()
408
409    gtk.main()
Note: See TracBrowser for help on using the browser.