root/trunk/umit/gui/DiffCompare.py @ 4479

Revision 4479, 40.7 kB (checked in by getxsick, 4 years ago)

dict.keys() is no more needed for iterate. fix for #260

Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Copyright (C) 2005-2006 Insecure.Com LLC.
5# Copyright (C) 2007-2008 Adriano Monteiro Marques
6#
7# Author: Adriano Monteiro Marques <adriano@umitproject.org>
8#
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 2 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program; if not, write to the Free Software
21# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22
23import gtk
24import pango
25import os
26import os.path
27import webbrowser
28
29from higwidgets.higdialogs import HIGAlertDialog, HIGDialog
30from higwidgets.higboxes import HIGVBox, HIGHBox, hig_box_space_holder
31from higwidgets.higlabels import HIGEntryLabel, HIGSectionLabel
32from higwidgets.higtables import HIGTable
33from higwidgets.higbuttons import HIGButton
34
35from umit.core.Diff import Diff
36from umit.core.UmitConf import UmitConf, DiffColors
37from umit.core.NmapParser import NmapParser, HostInfo
38from umit.core.Paths import check_access, Path
39from umit.core.UmitLogging import log
40from umit.core.I18N import _
41
42from tempfile import mktemp
43from types import StringTypes
44from umit.gui.FileChoosers import RegularDiffiesFileFilter, HtmlDiffiesFileFilter
45
46try:
47    from umit.core.DiffHtml import DiffHtml
48    from umit.gui.FileChoosers import AllFilesFileChooserDialog,\
49         ResultsFileChooserDialog,\
50         FullDiffiesFileChooserDialog as DiffiesFileChooserDialog
51    use_html = True
52except:
53    from umit.gui.FileChoosers import AllFilesFileChooserDialog,\
54         ResultsFileChooserDialog,\
55         SingleDiffiesFileChooserDialog as DiffiesFileChooserDialog
56    use_html = False
57
58
59class ScanChooser(HIGVBox):
60    def __init__(self, scan_dict, num=""):
61        HIGVBox.__init__(self)
62        self.num = num
63        self.scan_dict = scan_dict
64       
65        # Setting HIGVBox
66        self.set_border_width(5)
67        self.set_spacing(6)
68       
69        self._create_widgets()
70        self._pack_hbox()
71        self._attaching_widgets()
72        self._set_scrolled()
73        self._set_text_view()
74        self._set_open_button()
75       
76        for scan in scan_dict:
77            self.list_scan.append([scan])
78       
79        self.combo_scan.connect('changed', self.show_scan)
80       
81        self._pack_noexpand_nofill(self.lbl_scan)
82        self._pack_expand_fill(self.hbox)
83   
84    def _create_widgets(self):
85        self.lbl_scan = HIGSectionLabel("%s %s"%(_("Scan Result"),
86                                                 str(self.num)))
87        self.hbox = HIGHBox()
88        self.table = HIGTable()
89        self.list_scan = gtk.ListStore(str)
90        self.combo_scan = gtk.ComboBoxEntry(self.list_scan, 0)
91        self.btn_open_scan = gtk.Button(stock=gtk.STOCK_OPEN)
92        self.exp_scan = gtk.Expander(_("Scan Result Visualization"))
93        self.scrolled = gtk.ScrolledWindow()
94        self.txt_scan_result = gtk.TextView()
95        self.txg_tag = gtk.TextTag("scan_style")
96
97    def get_buffer(self):
98        return self.txt_scan_result.get_buffer()
99
100    def show_scan (self, widget):
101        try:
102            self.txt_scan_result.get_buffer().\
103                 set_text(self.normalize_output(\
104                     self.scan_dict[widget.child.get_text()].nmap_output))
105        except KeyError:
106            # Avoid to raise an error if the user writes within
107            # the entry and the scan doesn't exits
108            pass
109
110    def normalize_output(self, output):
111        return "\n".join(output.split("\\n"))
112
113    def _pack_hbox (self):
114        self.hbox._pack_noexpand_nofill(hig_box_space_holder())
115        self.hbox._pack_expand_fill(self.table)
116
117    def _attaching_widgets (self):
118        self.table.attach(self.combo_scan, 0,1,0,1, yoptions=0)
119        self.table.attach(self.btn_open_scan, 1,2,0,1, yoptions=0, xoptions=0)
120        self.table.attach(self.exp_scan, 0,2,1,2)
121   
122    def _set_scrolled(self):
123        self.scrolled.set_border_width(5)
124        self.scrolled.set_size_request(-1, 160)
125       
126        # Packing scrolled window into expander
127        self.exp_scan.add(self.scrolled)
128       
129        # Packing text view into scrolled window
130        self.scrolled.add_with_viewport(self.txt_scan_result)
131       
132        # Setting scrolled window
133        self.scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
134   
135    def _set_text_view (self):
136        self.txg_table = self.txt_scan_result.get_buffer().get_tag_table()
137        self.txg_table.add(self.txg_tag)
138        self.txg_tag.set_property("family", "Monospace")
139       
140        self.txt_scan_result.set_wrap_mode(gtk.WRAP_WORD)
141        self.txt_scan_result.set_editable(False)
142        self.txt_scan_result.get_buffer().connect("changed",
143                                                  self._text_changed_cb)
144
145    def _set_open_button (self):
146        self.btn_open_scan.connect('clicked', self.open_file)
147   
148    def open_file (self, widget):
149        file_chooser = ResultsFileChooserDialog(_("Select Scan Result"))
150       
151        file_chooser.run()
152        file_chosen = file_chooser.get_filename()
153        file_chooser.destroy()
154
155        if check_access(file_chosen, os.R_OK):
156            try:
157                parser = NmapParser(file_chosen)
158                parser.parse()
159            except:
160                alert = HIGAlertDialog(
161                    message_format='<b>%s</b>' % _('File is not a Umit \
162Scan Result'),
163                    secondary_text=_("Selected file is not a Umit Scan \
164Result file. Umit can not parse this file. Please, select another."))
165                alert.run()
166                alert.destroy()
167                return False
168
169            scan_name = os.path.split(file_chosen)[-1]
170            self.add_scan(scan_name, parser)
171           
172            self.combo_scan.set_active(len(self.list_scan) - 1)
173        else:
174            alert = HIGAlertDialog(
175                    message_format='<b>%s</b>' % \
176                                   _('Can not open selected file'),
177                    secondary_text=_("Umit can not open selected file. Please, \
178select another."))
179            alert.run()
180            alert.destroy()
181
182    def add_scan(self, scan_name, parser):
183        scan_id = 1
184        new_scan_name = scan_name
185        while new_scan_name in self.scan_dict:
186            new_scan_name = "%s (%s)" % (scan_name, scan_id)
187            scan_id += 1
188               
189        self.list_scan.append([new_scan_name])
190        self.scan_dict[new_scan_name] = parser
191   
192    def _text_changed_cb (self, widget):
193        buff = self.txt_scan_result.get_buffer ()
194        buff.apply_tag(self.txg_tag, buff.get_start_iter(), buff.get_end_iter())
195
196    def get_nmap_output(self):
197        parsed = self.parsed_scan
198        if parsed:
199            return parsed.nmap_output
200        return False
201
202    def get_parsed_scan(self):
203        selected_scan = self.combo_scan.child.get_text()
204        if selected_scan:
205            return self.scan_dict[selected_scan]
206        return False
207
208    nmap_output = property(get_nmap_output)
209    parsed_scan = property(get_parsed_scan)
210
211
212class DiffWindow(gtk.Window):
213    def __init__(self, scans):
214        """scans in the format: {"scan_title":parsed_scan}
215        """
216        gtk.Window.__init__(self)
217        self.set_title(_("Compare Results"))
218        self.scans = scans
219
220        self.umit_conf = UmitConf()
221        self.colors = Colors()
222       
223        # Diff views
224        self.text_view = DiffText(self.colors, self.umit_conf.colored_diff)
225        self.compare_view = DiffTree(self.colors)
226
227        self._create_widgets()
228        self._pack_widgets()
229        self._connect_widgets()
230
231
232        # Settings
233        if self.umit_conf.diff_mode == "text":
234            self.text_mode.set_active(True)
235        else:
236            self.compare_mode.set_active(True)
237        self.check_color.set_active(self.umit_conf.colored_diff)
238
239        # Initial Size Request
240        self.initial_size = self.size_request()
241
242    def _show_help(self, action):
243        webbrowser.open("file://%s" % os.path.join(Path.docs_dir, "index.html"),
244                        new=2)
245       
246    def _create_widgets(self):
247        self.main_vbox = HIGVBox()
248        self.hbox_mode = HIGHBox()
249        self.hbox_settings = HIGHBox()
250        self.hbox_buttons = HIGHBox()
251        self.hbox_result = HIGHBox()
252        self.btn_open_browser = HIGButton(_("Open in Browser"),
253                                          stock=gtk.STOCK_EXECUTE)
254        self.btn_help = HIGButton(stock=gtk.STOCK_HELP)
255        self.btn_close = HIGButton(stock=gtk.STOCK_CLOSE)
256        self.check_color = gtk.CheckButton(_("Enable colored diffies"))
257        self.btn_legend = HIGButton(_("Color Descriptions"),
258                                    stock=gtk.STOCK_SELECT_COLOR)
259        self.text_mode = gtk.ToggleButton(_("Text Mode"))
260        self.compare_mode = gtk.ToggleButton(_("Compare Mode"))
261        self.vpaned = gtk.VPaned()
262        self.hpaned = gtk.HPaned()
263        self.scan_chooser1 = ScanChooser(self.scans, "1")
264        self.scan_chooser2 = ScanChooser(self.scans, "2")
265        self.scan_buffer1 = self.scan_chooser1.get_buffer()
266        self.scan_buffer2 = self.scan_chooser2.get_buffer()
267
268    def _pack_widgets(self):
269        self.main_vbox.set_border_width(6)
270       
271        self.vpaned.pack1(self.hpaned, True, False)
272        self.vpaned.pack2(self.hbox_result)
273        self.hpaned.pack1(self.scan_chooser1, True, False)
274        self.hpaned.pack2(self.scan_chooser2, True, False)
275
276        self.hbox_buttons._pack_expand_fill(self.btn_help)
277        self.hbox_buttons._pack_expand_fill(self.btn_legend)
278        self.hbox_buttons._pack_expand_fill(self.btn_open_browser)
279        self.hbox_buttons._pack_expand_fill(self.btn_close)
280        self.hbox_buttons.set_homogeneous(True)
281
282        self.hbox_mode.set_homogeneous(True)
283        self.hbox_mode.pack_start(self.text_mode)
284        self.hbox_mode.pack_start(self.compare_mode)
285        self.hbox_settings._pack_noexpand_nofill(self.hbox_mode)
286        self.hbox_settings._pack_expand_fill(self.check_color)
287
288        self.main_vbox._pack_expand_fill(self.vpaned)
289        self.main_vbox._pack_noexpand_nofill(self.hbox_settings)
290        self.main_vbox._pack_noexpand_nofill(self.hbox_buttons)
291
292        self.add(self.main_vbox)
293
294    def _connect_widgets(self):
295        self.connect("delete-event", self.close)
296        self.btn_legend.connect("clicked", self.show_legend_window)
297        self.btn_help.connect("clicked", self._show_help)
298        self.btn_close.connect("clicked", self.close)
299        self.btn_open_browser.connect("clicked", self.open_browser)
300        self.check_color.connect("toggled", self._set_color)
301        self.text_mode.connect("clicked", self._change_to_text)
302        self.compare_mode.connect("clicked", self._change_to_compare)
303        self.scan_chooser1.exp_scan.connect('activate', self.resize_vpane)
304        self.scan_chooser2.exp_scan.connect('activate', self.resize_vpane)
305        self.scan_buffer1.connect('changed', self.text_changed)
306        self.scan_buffer2.connect('changed', self.text_changed)
307
308    def open_browser(self, widget):
309        text1=self.scan_buffer1.get_text(self.scan_buffer1.get_start_iter(),\
310                                self.scan_buffer1.get_end_iter())
311        text2=self.scan_buffer2.get_text(self.scan_buffer2.get_start_iter(),\
312                                self.scan_buffer2.get_end_iter())
313
314        if not text1 or not text2:
315            alert = HIGAlertDialog(
316                    message_format='<b>'+_('Select Scan')+'</b>',
317                    secondary_text=_("You must select two different scans to \
318generate diff."))
319            alert.run()
320            alert.destroy()
321            return False
322       
323        text1 = text1.split('\n')
324        text2 = text2.split('\n')
325       
326        self.temp_view = mktemp('.html')
327       
328        text1 = [text+'\n' for text in text1]
329        text2 = [text+'\n' for text in text2]
330       
331        if use_html:
332            diff = DiffHtml(text1, text2)
333            diff = diff.generate()
334
335            file_desc = open(self.temp_view, 'w')
336            file_desc.write(''.join(diff))
337
338            # Closing file to avoid problems with file descriptors
339            file_desc.close()
340        else:
341            diff = Diff(text1, text2)
342            diff = diff.generate ()
343            diff.insert(0, '''<pre>(This diff is been shown in pure text \
344because you dont have Python 2.4 or higher.)\n''')
345            diff.append('</pre>')
346
347            file_desc = open(self.temp_view, 'w')
348            file_desc.writelines(diff)
349
350            # Closing file to avoid problems with file descriptors
351            file_desc.close()
352       
353        webbrowser.open("file://" + self.temp_view, autoraise=1)
354
355    def show_legend_window(self, widget):
356        legend_window = DiffLegendWindow(self.colors)
357        legend_window.run()
358        legend_window.destroy()
359        self.text_changed(None)
360
361    def text_changed (self, widget):
362        text1 = self.scan_buffer1.get_text(self.scan_buffer1.get_start_iter(),\
363                                           self.scan_buffer1.get_end_iter())
364        text2 = self.scan_buffer2.get_text(self.scan_buffer2.get_start_iter(),\
365                                           self.scan_buffer2.get_end_iter())
366
367        if text1 != '' and text2 != '':
368            if self.compare_mode.get_active():
369                self.compare_view.make_diff(self.scan_chooser1.parsed_scan,
370                                            self.scan_chooser2.parsed_scan)
371                self.compare_view.activate_color(self.check_color.get_active())
372            else:
373                self.text1 = text1.split ('\n')
374                self.text2 = text2.split ('\n')
375           
376                self.diff = Diff(self.text1, self.text2)
377                self.text_view.txt_diff_result.get_buffer().set_text\
378                               ('\n'.join(self.diff.generate_without_banner()))
379                self.text_view.activate_color(self.check_color.get_active())
380                self.text_view._text_changed(None)
381
382    def resize_vpane(self, widget):
383        exp1 = not widget.get_expanded()
384        if widget == self.scan_chooser1.exp_scan:
385            exp2 = self.scan_chooser2.exp_scan.get_expanded()
386        else:
387            exp2 = self.scan_chooser1.exp_scan.get_expanded()
388
389        if not exp1 and not exp2:
390            self.vpaned.compute_position(-1, 0, 500)
391            self.size_allocate(gtk.gdk.Rectangle(width=self.initial_size[0],
392                                                 height=self.initial_size[1]))
393            self.queue_resize()
394
395    def _change_to_text(self, widget):
396        if not widget.get_active():
397            return
398
399        self.umit_conf.diff_mode = "text"
400
401        children = self.hbox_result.get_children()
402        if children:
403            self.hbox_result.remove(children[0])
404            self.compare_view.hide()
405       
406        self.hbox_result._pack_expand_fill(self.text_view)
407        self.text_view.show_all()
408       
409        self.compare_mode.set_active(False)
410        self.text_changed(None)
411
412    def _change_to_compare(self, widget):
413        if not widget.get_active():
414            return
415
416        self.umit_conf.diff_mode = "compare"
417
418        children = self.hbox_result.get_children()
419        if children:
420            self.hbox_result.remove(children[0])
421            self.text_view.hide()
422       
423        self.hbox_result._pack_expand_fill(self.compare_view)
424        self.compare_view.show_all()
425       
426        self.text_mode.set_active(False)
427        self.text_changed(None)
428
429    def _set_color(self, widget):
430        activate = widget.get_active()
431        self.umit_conf.colored_diff = activate
432        self.compare_view.activate_color(activate)
433        self.text_view.activate_color(activate)
434
435    def close(self, widget=None, extra=None):
436        self.destroy()
437
438class DiffText(HIGVBox, object):
439    def __init__ (self, colors, check_color):
440        HIGVBox.__init__(self)
441        self.set_border_width(5)
442        self.set_spacing(6)
443
444        self.colors = colors
445        self.check_color = check_color
446       
447        self._create_widgets()
448        self._pack_hbox()
449        self._set_text_view()
450        self._set_scrolled()
451       
452        self._pack_noexpand_nofill(self.lbl_diff)
453        self._pack_expand_fill(self.hbox)
454   
455    def _create_widgets (self):
456        self.hbox = HIGHBox ()
457        self.lbl_diff = HIGSectionLabel ("<b>%s</b>" % _("Diff Result"))
458        self.scrolled = gtk.ScrolledWindow()
459        self.txt_diff_result = gtk.TextView()
460        self.txg_tag = gtk.TextTag ("diff_style")
461        self.txg_added = gtk.TextTag ('added style')
462        self.txg_removed = gtk.TextTag ('removed style')
463
464    def _pack_hbox (self):
465        self.hbox.set_border_width(5)
466        self.hbox._pack_noexpand_nofill (hig_box_space_holder())
467        self.hbox._pack_expand_fill(self.scrolled)
468
469    def _set_scrolled (self):
470        self.scrolled.set_size_request(-1, 250)
471       
472        # Packing text view into scrolled window
473        self.scrolled.add_with_viewport(self.txt_diff_result)
474       
475        # Setting scrolled window
476        self.scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
477
478    def activate_color(self, activate):
479        self.check_color = activate
480        self._text_changed(None)
481   
482    def _set_text_view (self):
483        self.txg_table = self.txt_diff_result.get_buffer().get_tag_table()
484        self.txg_table.add(self.txg_tag)
485        self.txg_table.add(self.txg_added)
486        self.txg_table.add(self.txg_removed)
487        self.txg_tag.set_property("family", "Monospace")
488        self.txg_added.set_property("background-gdk", self.colors.added)
489        self.txg_removed.set_property("background-gdk", self.colors.not_present)
490       
491        self.txt_diff_result.set_wrap_mode(gtk.WRAP_WORD)
492        self.txt_diff_result.set_editable(False)
493        self.txt_diff_result.get_buffer().connect("changed", self._text_changed)
494       
495    def _text_changed (self, widget):
496        self.txg_added.set_property("background-gdk", self.colors.added)
497        self.txg_removed.set_property("background-gdk", self.colors.not_present)
498       
499        buff = self.txt_diff_result.get_buffer()
500
501        buff.apply_tag(self.txg_tag, buff.get_start_iter(), buff.get_end_iter())
502
503        if self.check_color:
504            positions = self._take_changes(buff)
505           
506            for i in positions['added']:
507                buff.apply_tag(self.txg_added, i[0],i[1])
508           
509            for i in positions['removed']:
510                buff.apply_tag(self.txg_removed, i[0], i[1])
511        else:
512            buff.remove_tag(self.txg_added, buff.get_start_iter(),
513                            buff.get_end_iter())
514            buff.remove_tag(self.txg_removed, buff.get_start_iter(),
515                            buff.get_end_iter())
516
517    def _take_changes (self, buffer):
518        positions = {'added':[], 'removed':[]}
519
520        in_line = 0
521        type = ''
522
523        iter = buffer.get_start_iter ()
524        last_char = '\n'
525
526        while iter.forward_char():
527            char = iter.get_char ()
528            offset = iter.get_offset()
529            if not in_line:
530                pos = []
531                if char == '+' and last_char == '\n':
532                    pos.append (buffer.get_iter_at_offset(offset))
533                    type = 'added'
534                    in_line = 1
535                elif char == '-' and last_char == '\n':
536                    pos.append (buffer.get_iter_at_offset(offset))
537                    type = 'removed'
538                    in_line = 1
539            else:
540                if char == '\n':
541                    pos.append (buffer.get_iter_at_offset(offset))
542                    positions [type].append (pos)
543                    in_line = 0
544                    type = ''
545            last_char = char
546       
547        return positions
548
549
550class DiffTree(HIGVBox, object):
551    def __init__(self, colors):
552        HIGVBox.__init__(self)
553
554        self.colors = colors
555        self.set_border_width(5)
556        self.set_spacing(6)
557
558        self._create_widgets()
559        self._set_diff_view()
560        self._pack_widgets()
561
562    def _create_widgets(self):
563        self.diff_box = HIGHBox()
564        self.diff_title = HIGSectionLabel("Comparison")
565        self.diff_scrolled = gtk.ScrolledWindow()
566        self.diff_tree = gtk.TreeStore(str, str, str, str, str, str)
567        self.diff_view = gtk.TreeView(self.diff_tree)
568        self.diff_column1 = gtk.TreeViewColumn("")
569        self.diff_column2 = gtk.TreeViewColumn(_("Section"))
570        self.diff_column3 = gtk.TreeViewColumn(_("Property"))
571        self.diff_column4 = gtk.TreeViewColumn(_("Original value"))
572        self.diff_column5 = gtk.TreeViewColumn(_("Current value"))
573        self.diff_cell = gtk.CellRendererText()
574
575
576    def activate_color(self, activate):
577        if activate:
578            self.diff_column1.set_attributes(self.diff_cell,
579                                             text=0,
580                                             background=5)
581            self.diff_column2.set_attributes(self.diff_cell,
582                                             text=1,
583                                             background=5)
584            self.diff_column3.set_attributes(self.diff_cell,
585                                             text=2,
586                                             background=5)
587            self.diff_column4.set_attributes(self.diff_cell,
588                                             text=3,
589                                             background=5)
590            self.diff_column5.set_attributes(self.diff_cell,
591                                             text=4,
592                                             background=5)
593        else:
594            self.diff_column1.clear_attributes(self.diff_cell)
595            self.diff_column2.clear_attributes(self.diff_cell)
596            self.diff_column3.clear_attributes(self.diff_cell)
597            self.diff_column4.clear_attributes(self.diff_cell)
598            self.diff_column5.clear_attributes(self.diff_cell)
599           
600            self.diff_column1.set_attributes(self.diff_cell, text=0)
601            self.diff_column2.set_attributes(self.diff_cell, text=1)
602            self.diff_column3.set_attributes(self.diff_cell, text=2)
603            self.diff_column4.set_attributes(self.diff_cell, text=3)
604            self.diff_column5.set_attributes(self.diff_cell, text=4)
605
606            self.diff_cell.set_property("background", "white")
607
608    def _set_diff_view(self):
609        font_desc = pango.FontDescription("monospace normal 9")
610        self.diff_cell.set_property("font-desc", font_desc)
611       
612        self.diff_scrolled.set_size_request(-1, 260)
613        self.diff_view.set_enable_search(True)
614        self.diff_view.set_search_column(2)
615
616        selection = self.diff_view.get_selection()
617        selection.set_mode(gtk.SELECTION_MULTIPLE)
618
619        self.diff_view.append_column(self.diff_column1)
620        self.diff_view.append_column(self.diff_column2)
621        self.diff_view.append_column(self.diff_column3)
622        self.diff_view.append_column(self.diff_column4)
623        self.diff_view.append_column(self.diff_column5)
624
625        # Diff Column 1
626        self.diff_column1.set_reorderable(True)
627        self.diff_column1.set_resizable(True)
628        self.diff_column1.connect("clicked", self.search_column, 0)
629        self.diff_column1.set_sort_column_id(0)
630        self.diff_column1.pack_start(self.diff_cell, True)
631        self.diff_column1.set_attributes(self.diff_cell, text=0)
632       
633
634        # Diff Column 2
635        self.diff_column2.set_reorderable(True)
636        self.diff_column2.set_resizable(True)
637        self.diff_column2.connect("clicked", self.search_column, 1)
638        self.diff_column2.set_sort_column_id(1)
639        self.diff_column2.pack_start(self.diff_cell, True)
640        self.diff_column2.set_attributes(self.diff_cell, text=1)
641
642        # Diff Column 3
643        self.diff_column3.set_reorderable(True)
644        self.diff_column3.set_resizable(True)
645        self.diff_column3.connect("clicked", self.search_column, 2)
646        self.diff_column3.set_sort_column_id(2)
647        self.diff_column3.pack_start(self.diff_cell, True)
648        self.diff_column3.set_attributes(self.diff_cell, text=2)
649
650        # Diff Column 4
651        self.diff_column4.set_reorderable(True)
652        self.diff_column4.set_resizable(True)
653        self.diff_column4.connect("clicked", self.search_column, 3)
654        self.diff_column4.set_sort_column_id(3)
655        self.diff_column4.pack_start(self.diff_cell, True)
656        self.diff_column4.set_attributes(self.diff_cell, text=3)
657
658        # Diff Column 5
659        self.diff_column5.set_reorderable(True)
660        self.diff_column5.set_resizable(True)
661        self.diff_column5.connect("clicked", self.search_column, 4)
662        self.diff_column5.set_sort_column_id(4)
663        self.diff_column5.pack_start(self.diff_cell, True)
664        self.diff_column5.set_attributes(self.diff_cell, text=4)
665
666        self.diff_scrolled.set_policy(gtk.POLICY_AUTOMATIC,
667                                      gtk.POLICY_AUTOMATIC)
668
669    def clear_diff_tree(self):
670        for i in range(len(self.diff_tree)):
671            iter = self.diff_tree.get_iter_root()
672            del(self.diff_tree[iter])
673
674    def change_status(self, iter, status):
675        self.diff_tree[iter][0] = status
676        self.diff_tree[iter][5] = self.colors.get_hex_color(status)
677
678    def set_parent_status(self, parent, status):
679        if status != "" and status.upper() != "U":
680            while parent:
681                self.change_status(parent, "M")
682                parent = self.diff_tree.iter_parent(parent)
683       
684    def make_diff(self, parsed1, parsed2):
685        self.clear_diff_tree()
686
687        section = _("Umit Info")
688        parent = self.append_parent(None, section, "")
689        self.diff_it(parent, "", _("Profile"), parsed1.profile, parsed2.profile)
690        self.diff_it(parent, "", _("Profile Name"), parsed1.profile_name,
691                     parsed2.profile_name)
692        self.diff_it(parent, "", _("Profile Options"), parsed1.profile_options,
693                     parsed2.profile_options)
694        self.diff_it(parent, "", _("Target"), parsed1.target, parsed2.target)
695
696        section = _("Nmap Info")
697        parent = self.append_parent(None, section, "")
698       
699        self.diff_it(parent, "", _("Debugging"), parsed1.debugging_level,
700                     parsed2.debugging_level)
701        self.diff_it(parent, "", _("Verbosity"), parsed1.verbose_level,
702                     parsed2.verbose_level)
703        self.diff_it(parent, "", _("Command"), parsed1.nmap_command,
704                     parsed2.nmap_command)
705        self.diff_it(parent, "", _("Scanner version"), parsed1.scanner_version,
706                     parsed2.scanner_version)
707
708        section = _("Scan Info")
709        parent = self.append_parent(None, section, "")
710       
711        self.diff_it(parent, "", _("Open Ports"), parsed1.open_ports,
712                     parsed2.open_ports)
713        self.diff_it(parent, "", _("Filtered Ports"), parsed1.filtered_ports,
714                     parsed2.filtered_ports)
715        self.diff_it(parent, "", _("Closed Ports"), parsed1.closed_ports,
716                     parsed2.closed_ports)
717        self.diff_it(parent, "", _("Hosts Up"), parsed1.hosts_up,
718                     parsed2.hosts_up)
719        self.diff_it(parent, "", _("Hosts Down"), parsed1.hosts_down,
720                     parsed2.hosts_down)
721        self.diff_it(parent, "", _("Hosts Scanned"), parsed1.hosts_total,
722                     parsed2.hosts_total)
723        self.diff_it(parent, "", _("Finish date"), parsed1.formated_finish_date,
724                     parsed2.formated_finish_date)
725
726        hosts1 = parsed1.hosts[:]
727        hosts2 = parsed2.hosts[:]
728        while hosts1:
729            host = hosts1.pop()
730           
731            second_host = HostInfo(0)
732            host_state = "N"
733            for host2 in hosts2:
734                if (host.mac and host.mac == host2.mac) or \
735                       (host.ip and host.ip == host2.ip) or \
736                       (host.ipv6 and host.ipv6 == host2.ipv6):
737                    second_host = host2
738                    host_state = ""
739                   
740                    del(hosts2[hosts2.index(host2)]) # Remove it from the hosts2
741                    break
742
743            self.add_host_diff(host_state, host, second_host)
744
745        for host in hosts2:
746            self.add_host_diff("A", host, host)
747
748    def add_host_diff(self, host_state, host, host2=None):
749            section = _("Host")
750            if host.ip:
751                section = _("Host %s") % (host.ip["addr"])
752            elif host.ipv6:
753                section = _("Host %s") % (host.ipv6["addr"])
754            elif host.mac:
755                section = _("Host %s") % (host.mac["addr"])
756
757            parent = self.append_parent(None, section, host_state)
758
759            self.diff_it(parent, "", _("Comment"), host.comment,
760                         host2.comment)
761            self.diff_it(parent,
762                         "",
763                         _("LastBoot"),
764                         host.uptime.get("lastboot", ""),
765                         host2.uptime.get("lastboot", ""))
766            # XXX Comparing only the last os match
767            h_match = {}
768            h2_match = {}
769            if host.osmatch:
770                h_match = host.osmatch[-1]
771            if host2.osmatch:
772                h2_match = host2.osmatch[-1]
773            self.diff_it(parent,
774                         "",
775                         _("OS Match"),
776                         h_match.get("name", ""),
777                         h2_match.get("name", ""))
778
779
780            # XXX only the first extraports are being compared
781            extraports1 = {}
782            extraports2 = {}
783            if host.extraports:
784                extraports1 = host.extraports[0]
785            if host2.extraports:
786                extraports2 = host2.extraports[0]
787
788            if extraports1 and extraports2:
789                self.add_extraports_diff(parent, "",
790                        extraports1, extraports2)
791            elif extraports1 and not extraports2:
792                self.add_extraports_diff(parent, "N",
793                        extraports1, extraports2)
794            elif not extraports1 and extraports2:
795                self.add_extraports_diff(parent, "A",
796                        extraports1, extraports2)
797
798            section =  _("Ports")
799            parent = self.append_parent(parent, section, "")
800
801            ports1 = host.ports[:]
802            ports2 = host2.ports[:]
803
804            for p1 in ports1:
805                if not p1:
806                    continue
807
808                p2 = [port2 for port2 in ports2 \
809                        if port2.get("portid", "a") == p1.get("portid", "b")]
810
811                if p2: # Removing found port
812                    ports2.remove(p2[0])
813
814                if p1 and p2:
815                    self.add_port_diff(parent, "", p1, p2[0])
816                elif p1 and not p2:
817                    self.add_port_diff(parent, "N", p1, {})
818
819            for p2 in ports2: # If there is something left...
820                self.add_port_diff(parent, "A", {}, p2)
821
822
823    def add_port_diff(self, port_parent, state, port1, port2):
824        if (port1 or port2) and (type(port1) == type({})) and\
825           (type(port2) == type({})):
826            section = port1.get("portid", False)
827            if not section: # If port1 is empty, then, try port2
828                section = port2.get("portid", "")
829           
830            parent = self.append_parent(port_parent, section, state)
831
832            self.diff_it(parent, "",
833                         _("State"), port1.get("state", ""),
834                         port2.get("state", ""))
835           
836            self.diff_it(parent, "",
837                         _("Service Name"), port1.get("name", ""),
838                         port2.get("name", ""))
839               
840            self.diff_it(parent, "",
841                         _("Product"), port1.get("product", ""),
842                         port2.get("product", ""))
843           
844            self.diff_it(parent, "",
845                         _("Service Version"), port1.get("version", ""),
846                         port2.get("version", ""))
847           
848            self.diff_it(parent, "",
849                         _("Protocol"), port1.get("protocol", ""),
850                         port2.get("protocol", ""))
851               
852            self.diff_it(parent, "",
853                         _("Extra Info"), port1.get("extrainfo", ""),
854                         port2.get("extrainfo", ""))
855           
856            self.diff_it(parent, "",
857                         _("Service Conf"), port1.get("conf", ""),
858                         port2.get("conf", ""))
859
860            # Last parent status modification
861            if state.upper() == "A":
862                self.change_status(parent, "A")
863
864    def add_extraports_diff(self, host_parent, state, extraports1, extraports2):
865        if extraports1 or extraports2:
866            section =  _("Extraports")
867            parent = self.append_parent(host_parent, section, state)
868            self.set_parent_status(parent, state)
869           
870            self.diff_it(parent, "", _("Count"), extraports1.get("count"),
871                         extraports2.get("count"))
872            self.diff_it(parent, "", _("State"), extraports1.get("state"),
873                         extraports2.get("state"))
874
875
876    def diff_it(self, parent, section, prop_name, prop1, prop2):
877        if prop1 or prop2:
878            state = diff_state(prop1, prop2)
879            self.set_parent_status(parent, state)
880            self.diff_tree.append(parent, [state,
881                                           section,
882                                           prop_name,
883                                           prop1,
884                                           prop2,
885                                           self.colors.get_hex_color(state)])
886            return state
887
888    def append_parent(self, parent, section, state):
889        self.set_parent_status(parent, state)
890        return self.diff_tree.append(parent, [state, section, "", "", "",
891                                              self.colors.get_hex_color(state)])
892
893    def search_column(self, widget, column_id):
894        self.diff_view.set_search_column(column_id)
895
896    def _pack_widgets(self):
897        self._pack_noexpand_nofill(self.diff_title)
898        self._pack_expand_fill(self.diff_box)
899        self.diff_box._pack_noexpand_nofill(hig_box_space_holder())
900        self.diff_box._pack_expand_fill(self.diff_scrolled)
901
902        self.diff_scrolled.add(self.diff_view)
903
904class DiffLegendWindow(HIGDialog, object):
905    def __init__(self, colors):
906        # Shows colors and chars legend
907        HIGDialog.__init__(self, title=_('Color Descriptions'),
908                          buttons=(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
909
910        self.colors = colors
911        self._create_widgets()
912        self._pack_widgets()
913        self._connect_widgets()
914
915    def _create_widgets(self):
916        self.table = HIGTable()
917       
918        self.unchanged_button = gtk.ColorButton(self.colors.unchanged)
919        self.unchanged_label = gtk.Label(_("Property remained <b>U</b>nchanged"))
920       
921        self.added_button = gtk.ColorButton(self.colors.added)
922        self.added_label = gtk.Label(_("Property was <b>A</b>dded"))
923       
924        self.modified_button = gtk.ColorButton(self.colors.modified)
925        self.modified_label = gtk.Label(_("Property was <b>M</b>odified"))
926       
927        self.not_present_button = gtk.ColorButton(self.colors.not_present)
928        self.not_present_label = gtk.Label(_("Property is <b>N</b>ot present"))
929
930    def _pack_widgets(self):
931        self.unchanged_label.set_use_markup(True)
932        self.added_label.set_use_markup(True)
933        self.modified_label.set_use_markup(True)
934        self.not_present_label.set_use_markup(True)
935
936        self.table.attach_label(self.unchanged_button, 0, 1, 0, 1)
937        self.table.attach_entry(self.unchanged_label, 1, 2, 0, 1)
938
939        self.table.attach_label(self.added_button, 0, 1, 1, 2)
940        self.table.attach_entry(self.added_label, 1, 2, 1, 2)
941
942        self.table.attach_label(self.modified_button, 0, 1, 2, 3)
943        self.table.attach_entry(self.modified_label, 1, 2, 2, 3)
944
945        self.table.attach_label(self.not_present_button, 0, 1, 3, 4)
946        self.table.attach_entry(self.not_present_label, 1, 2, 3, 4)
947
948        self.vbox.pack_start(self.table)
949        self.vbox.show_all()
950
951    def _connect_widgets(self):
952        self.unchanged_button.connect("color-set",
953                                      self.set_color,
954                                      "unchanged")
955        self.added_button.connect("color-set",
956                                  self.set_color,
957                                  "added")
958        self.modified_button.connect("color-set",
959                                     self.set_color,
960                                     "modified")
961        self.not_present_button.connect("color-set",
962                                        self.set_color,
963                                        "not_present")
964
965    def set_color(self, widget, prop):
966        self.colors.__setattr__(prop, widget.get_color())
967
968class Colors(object):
969    def __init__(self):
970        self.diff_colors = DiffColors()
971
972    def get_unchanged(self):
973        return gtk.gdk.Color(*self.diff_colors.unchanged)
974
975    def set_unchanged(self, color):
976        if type(color) == type([]) or type(color) in StringTypes:
977            self.diff_colors.unchanged = color
978        else:
979            self.diff_colors.unchanged = [color.red, color.green, color.blue]
980
981    def get_added(self):
982        return gtk.gdk.Color(*self.diff_colors.added)
983
984    def set_added(self, color):
985        if type(color) == type([]) or type(color) in StringTypes:
986            self.diff_colors.added = color
987        else:
988            self.diff_colors.added = [color.red, color.green, color.blue]
989
990    def get_modified(self):
991        return gtk.gdk.Color(*self.diff_colors.modified)
992
993    def set_modified(self, color):
994        if type(color) == type([]) or type(color) in StringTypes:
995            self.diff_colors.modified = color
996        else:
997            self.diff_colors.modified = [color.red, color.green, color.blue]
998
999    def get_not_present(self):
1000        return gtk.gdk.Color(*self.diff_colors.not_present)
1001
1002    def set_not_present(self, color):
1003        if type(color) == type([]) or type(color) in StringTypes:
1004            self.diff_colors.not_present = color
1005        else:
1006            self.diff_colors.not_present = [color.red, color.green, color.blue]
1007
1008    def get_hex_unchanged(self):
1009        return self._get_hex(self.unchanged)
1010
1011    def get_hex_added(self):
1012        return self._get_hex(self.added)
1013
1014    def get_hex_modified(self):
1015        return self._get_hex(self.modified)
1016
1017    def get_hex_not_present(self):
1018        return self._get_hex(self.not_present)
1019
1020    def _get_hex(self, color):
1021        if type(color) == type([]):
1022            return "#%4s%4s%4s" % (hex(color[0])[2:].zfill(4),
1023                                   hex(color[1])[2:].zfill(4),
1024                                   hex(color[2])[2:].zfill(4))
1025        else:
1026            return "#%4s%4s%4s" % (hex(color.red)[2:].zfill(4),
1027                                   hex(color.green)[2:].zfill(4),
1028                                   hex(color.blue)[2:].zfill(4))
1029
1030    def get_hex_color(self, state):
1031        state = state.upper()
1032       
1033        if state == "A":
1034            return self.hex_added
1035        elif state == "M":
1036            return self.hex_modified
1037        elif state == "N":
1038            return self.hex_not_present
1039        else:
1040            return self.hex_unchanged
1041   
1042    unchanged = property(get_unchanged, set_unchanged)
1043    added = property(get_added, set_added)
1044    modified = property(get_modified, set_modified)
1045    not_present = property(get_not_present, set_not_present)
1046
1047    hex_unchanged = property(get_hex_unchanged)
1048    hex_added = property(get_hex_added)
1049    hex_modified = property(get_hex_modified)
1050    hex_not_present = property(get_hex_not_present)
1051
1052
1053def diff_state(prop1, prop2):
1054    if prop1 == prop2:
1055        return "U" # Property remained "Unchanged" at the second scan
1056    elif prop1 == "" and prop2 != "":
1057        return "A" # Property "Added" at the second scan
1058    elif prop1 != "" and prop2 != "":
1059        return "M" # Property "Modified" at the second scan
1060    else:
1061        return "N" # Property "Not present" at the second scan
1062
1063
1064if __name__ == "__main__":
1065    from umit.core.NmapParser import NmapParser
1066
1067    parsed1 = NmapParser("test/xml_test1.xml")
1068    parsed2 = NmapParser("test/xml_test2.xml")
1069    parsed3 = NmapParser("test/xml_test3.xml")
1070    parsed4 = NmapParser("test/xml_test4.xml")
1071
1072    parsed1.parse()
1073    parsed2.parse()
1074    parsed3.parse()
1075    parsed4.parse()
1076   
1077    dw = DiffWindow({"Parsed 1": parsed1,
1078                     "Parsed 2": parsed2,
1079                     "Parsed 3": parsed3,
1080                     "Parsed 4": parsed4})
1081
1082    dw.show_all()
1083    dw.connect("delete-event", lambda x,y: gtk.main_quit())
1084
1085    gtk.main()
Note: See TracBrowser for help on using the browser.