root/branch/PacketManipulator/PM/Gui/Core/MainWindow.py @ 4872

Revision 4872, 23.9 kB (checked in by nopper, 4 years ago)
  • Fixed text label.
  • PM/Gui/Core/MainWindow.py:
    • Fixing the size of the application by suggesting a default_size
    • Various fix to avoid exceptions if page remove are not a Session.
  • PM/Gui/Tabs/MainTab.py:

UI improvements.

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
21"""
22This module contains the MainWindow class
23"""
24
25import gtk
26import os
27
28from PM import Backend
29from PM.Core.Logger import log
30from PM.Manager.PreferenceManager import Prefs
31
32from PM.Gui.Widgets.StatusBar import StatusBar
33from PM.higwidgets.higdialogs import HIGAlertDialog
34
35__paned_imported = False
36
37if Prefs()['gui.docking'].value.lower() == 'moo':
38    try:
39        from MooPaned import *
40        __paned_imported = True
41    except ImportError:
42        log.info("moo library is not installed.")
43
44elif Prefs()['gui.docking'].value.lower() == 'gdl':
45    try:
46        from GdlPaned import *
47        __paned_imported = True
48    except ImportError:
49        log.info("GDL is not installed. Using fallback paned.")
50
51if Prefs()['gui.docking'].value.lower() == 'standard' or not __paned_imported:
52    from FallbackPaned import *
53    __paned_imported = True
54
55    log.info('Using fallback paned')
56
57from PM.Gui.Tabs.VteTab import VteTab
58from PM.Gui.Tabs.MainTab import MainTab
59from PM.Gui.Tabs.HackTab import HackTab
60from PM.Gui.Tabs.StatusTab import StatusTab
61from PM.Gui.Tabs.ConsoleTab import ConsoleTab
62from PM.Gui.Tabs.PropertyTab import PropertyTab
63from PM.Gui.Tabs.OperationsTab import OperationsTab, SniffOperation
64from PM.Gui.Tabs.OperationsTab import FileOperation
65from PM.Gui.Tabs.ProtocolSelectorTab import ProtocolSelectorTab
66
67from PM.Gui.Dialogs.Interface import InterfaceDialog
68from PM.Gui.Dialogs.Preferences import PreferenceDialog
69from PM.Gui.Dialogs.Routes import RoutesDialog
70from PM.Gui.Plugins.Window import PluginWindow
71
72from PM.Gui.Pages import PerspectiveType
73
74from PM.Gui.Sessions.Base import Session
75from PM.Gui.Sessions import SessionType
76
77from PM.Core.I18N import _
78from PM.Core.Const import PM_DEVELOPMENT, PM_SVN_REVISION
79from PM.Core.Const import PIXMAPS_DIR, PM_VERSION, PM_SITE
80
81class MainWindow(gtk.Window):
82    def __init__(self):
83        gtk.Window.__init__(self)
84
85        self.set_title("Packet Manipulator")
86        self.set_icon_from_file(os.path.join(PIXMAPS_DIR, 'pm-logo.png'))
87        self.set_default_size(600, 400)
88
89        self.registered_tabs = {}
90
91        # Binders for plugins
92        self.perspective_binder = []
93        self.session_binder = []
94
95        for i in PerspectiveType.types:
96            self.perspective_binder.append(list())
97
98        for i in SessionType.types:
99            self.session_binder.append(list())
100
101        self.perspective_binder = tuple(self.perspective_binder)
102        self.session_binder = tuple(self.session_binder)
103
104        self.__create_widgets()
105        self.__pack_widgets()
106        self.__connect_signals()
107
108        self.statusbar.push(_("Ready."), image=gtk.STOCK_YES)
109
110        self.show_all()
111
112    def __create_widgets(self):
113        "Create widgets"
114
115        self.main_actions = [
116            ('File', None, _('File'), None),
117
118            ('NewSequence', gtk.STOCK_NEW, _('_New sequence'), '<Control>n',
119                _('Create a new sequence'), self.__on_new_sequence),
120
121            ('Open', gtk.STOCK_OPEN, _('_Open'), '<Control>o',
122                _('Open session'), self.__on_open_session),
123
124            ('Save', gtk.STOCK_SAVE, _('_Save'), '<Control>s',
125                _('Save session'), self.__on_save_session),
126
127            ('SaveAs', gtk.STOCK_SAVE_AS, _('_Save as'), '<Control><Shift>s',
128                _('Save session as'), self.__on_save_session_as),
129
130            ('Quit', gtk.STOCK_QUIT, _('_Quit'), '<Control>q',
131                _('Quit from application'), self.__on_quit),
132
133            ('Capture', None, _('Capture'), None),
134
135            ('Interface', gtk.STOCK_CONNECT, _('_Interface'), '<Control>i',
136                _('Capture from interface'), self.__on_select_iface),
137
138            ('Options', None, _('Options'), None),
139
140            ('Routes', gtk.STOCK_NETWORK, _('Routing table'), '<Control>r',
141                _('Routes editor'), self.__on_routing),
142
143            ('Plugins', 'extension_small', _('Plugins'), None,
144                _('Plugin manager'), self.__on_plugins),
145
146            ('Preferences', gtk.STOCK_PREFERENCES, _('_Preferences'),
147                '<Control>p', _('Preferences'), self.__on_preferences),
148
149            ('Views', None, _('Views'), None),
150
151            ('Help', None, _('Help'), None),
152
153            ('About', gtk.STOCK_ABOUT, _('About'), None, None, self.__on_about),
154        ]
155
156        self.default_ui = """<menubar>
157            <menu action='File'>
158                <menuitem action='NewSequence'/>
159                <menuitem action='Open'/>
160                <menuitem action='Save'/>
161                <menuitem action='SaveAs'/>
162                <separator/>
163                <menuitem action='Quit'/>
164            </menu>
165            <menu action='Capture'>
166                <menuitem action='Interface'/>
167            </menu>
168            <menu action='Options'>
169                <menuitem action='Routes'/>
170                <separator/>
171                <menuitem action='Plugins'/>
172                <separator/>
173                <menuitem action='Preferences'/>
174            </menu>
175            <menu action='Views'/>
176            <menu action='Help'>
177                <menuitem action='About'/>
178            </menu>
179            </menubar>
180
181            <toolbar>
182                <toolitem action='Open'/>
183                <toolitem action='Save'/>
184                <toolitem action='Interface'/>
185                <separator/>
186                <toolitem action='Routes'/>
187                <toolitem action='Preferences'/>
188                <separator/>
189            </toolbar>
190            """
191
192        self.ui_manager = gtk.UIManager()
193
194        self.main_accel_group = gtk.AccelGroup()
195        self.main_action_group = gtk.ActionGroup('MainActionGroup')
196        self.main_action_group.add_actions(self.main_actions)
197
198        self.add_accel_group(self.main_accel_group)
199
200        for action in self.main_action_group.list_actions():
201            action.set_accel_group(self.main_accel_group)
202            action.connect_accelerator()
203
204        self.ui_manager.insert_action_group(self.main_action_group, 0)
205        self.ui_manager.add_ui_from_string(self.default_ui)
206
207        # Central widgets
208        self.main_paned = UmitPaned()
209
210        self.vbox = gtk.VBox(False, 2)
211        self.statusbar = StatusBar()
212
213        self.plugin_window = PluginWindow()
214
215    def get_tab(self, name):
216        """
217        Get a tab from its name
218
219        @param name the name of the tab
220        """
221
222        return self.registered_tabs[name]
223
224    def deregister_tab(self, tab):
225        """
226        Deregister a tab deleting his CheckMenu and tab from the paned
227
228        @param tab the tab to deregister
229        @return True if is ok or False
230        """
231
232        item = self.ui_manager.get_widget('/menubar/Views')
233        menu = item.get_submenu()
234
235        def find_tab(item, udata):
236            tab, find = udata
237
238            if item.get_data('tab-object') is tab:
239                find = item
240                return True
241
242        find = None
243        menu.foreach(find_tab, (tab, find))
244
245        if find is not None:
246            menu.remove(find)
247            self.main_paned.remove_view(tab)
248            del self.registered_tabs[tab.name]
249
250            return True
251
252        return False
253
254    def register_tab(self, tab, show=True):
255        """
256        Register a tab
257
258        @param tab the Tab object
259        @param show if the Tab should be showed
260        """
261
262        item = self.ui_manager.get_widget('/menubar/Views')
263        menu = item.get_submenu()
264
265        item.show()
266
267        if not menu:
268            menu = gtk.Menu()
269            item.set_submenu(menu)
270
271        if tab.name in self.registered_tabs:
272            raise Exception("Tab already present")
273
274        # Ok we should add a CheckMenuItem to this fucking menu
275        self.registered_tabs[tab.name] = tab
276
277        log.debug("Tab %s registered as %s" % (tab.label_text, tab.name))
278
279        if tab.tab_position is None:
280            # This is the central widget so it should be added
281            # with no MenuItem
282            self.main_paned.add_view(tab)
283            return
284
285        new_item = gtk.CheckMenuItem(tab.label_text)
286        new_item.set_data('tab-object', tab)
287        new_item.connect('toggled', self.__on_toggle_tab_menu, tab)
288
289        if show:
290            new_item.set_active(True)
291
292        new_item.show()
293        menu.append(new_item)
294
295    def create_session(self, menu, tuple):
296        """
297        Create a new session using ctxklass and sessklass
298
299        @param menu gtk.MenuItem
300        @param tuple a tuple containing (sessklass, ctxklass)
301        """
302        sessklass, ctxklass = tup
303        maintab = self.get_tab("MainTab")
304        maintab.session_notebook.create_session(sessklass, ctxklass)
305
306    def register_session(self, sessklass, ctxklass=None):
307        """
308        Register a custom session class and returns the new id
309        of the SessionType
310
311        @param sessklass the custom session class
312        @param ctxklass the context class to use
313        @return id
314        """
315
316        if sessklass.session_menu is not None:
317            item = self.ui_manager.get_widget('/menubar/File')
318            menu = item.get_submenu()
319
320            item = gtk.MenuItem(sessklass.session_menu)
321            item.connect('activate', self.create_session, (sessklass, ctxklass))
322            item.show()
323
324            menu.insert(item, 2)
325
326            sessklass.session_menu_object = item
327
328        return SessionType.add_session(sessklass)
329
330    def deregister_session(self, sessklass):
331        if sessklass.session_menu_object:
332            menu = self.ui_manager.get_widget('/menubar/File').get_submenu()
333            sessklass.session_menu_object.hide()
334
335            menu.remove(sessklass.session_menu_object)
336
337        return SessionType.remove_session(sessklass)
338
339    def bind_session(self, ptype, persp_klass, show_pers=True, resize=False, \
340                     shrink=True):
341        """
342        Bind the perspective 'pers_klass' to Session 'ptype'
343
344        @param ptype the Session type to customize
345        @param persp_klass the perspective class to add to the selected Session
346        @param show_pers choose to show the perspective
347        @param resize if True child should resize when the paned is resized
348        @param shrink if True child can be made smaller than its minimum size
349                      request
350        """
351
352        log.debug(
353            "Binding perspective %s to Session %s" % \
354            (persp_klass, SessionType.types[ptype])
355        )
356
357        self.session_binder[ptype].append((persp_klass, show_pers, \
358                                           resize, shrink))
359
360        klass = SessionType.types[ptype]
361        maintab = self.get_tab("MainTab")
362
363        for page in maintab.session_notebook:
364            if isinstance(page, klass):
365                self.apply_bindings(page, ptype)
366
367    def unbind_session(self, type, persp_klass):
368        try:
369            for i in range(len(self.session_binder[ptype])):
370                (klass, show, resize, shrink) = self.session_binder[ptype][i]
371
372                if klass is not persp_klass:
373                    continue
374
375                del self.session_binder[type][i]
376
377                klass = SessionType.types[type]
378                maintab = self.get_tab("MainTab")
379
380                for page in maintab.session_notebook:
381                    if isinstance(page, klass):
382                        page.remove_perspective(klass)
383
384                log.debug(
385                    "Binding method %s for perspective of type %s removed" % \
386                    (persp_klass, SessionType.types[type])
387                )
388
389                return True
390        except:
391            log.error(
392                "Failed to remove binding method %s for session of type %s" % \
393                (persp_klass, SessionType.type[ptype])
394            )
395
396        return False
397
398    def apply_bindings(self, page, ptype):
399        for persp_klass, show, resize, shrink in self.session_binder[ptype]:
400            page.add_perspective(persp_klass, show, resize, shrink)
401
402    def bind_perspective(self, ptype, callback):
403        """
404        Bind the perspective 'type'
405
406        The callback should be of the type
407          def perspective_cb(perspective, type, already_present, added)
408
409        @param type the perspective's type (see also PerspectiveType)
410        @param callback the callback to execute when a new
411               perspective of type 'type' is created
412        """
413
414        log.debug(
415            "Binding method %s for perspective of type %s" % \
416            (callback, PerspectiveType.types[ptype])
417        )
418
419        self.perspective_binder[ptype].append(callback)
420
421        maintab = self.get_tab("MainTab")
422
423        for page in maintab.session_notebook:
424            for perspective in page.perspectives:
425                idx = PerspectiveType.types[type(perspective)]
426
427                callback(perspective, idx, True, True)
428
429    def debind_perspective(self, type, callback):
430        """
431        Remove the binding callback for perspective of type 'type'
432
433        @param type the perspective type
434        @param callback the callback to remove
435        @return True if the callback is removed correctly
436        """
437
438        try:
439            self.perspective_binder[type].remove(callback)
440
441            maintab = self.get_tab("MainTab")
442
443            for page in maintab.session_notebook:
444                for perspective in page.perspectives:
445                    idx = PerspectiveType.types[type(perspective)]
446
447                    callback(perspective, idx, True, False)
448
449            log.debug(
450                "Binding method %s for perspective of type %s removed" % \
451                (callback, PerspectiveType.types[type])
452            )
453
454            return True
455        except:
456            log.error(
457                "Failed to remove binding method %s "
458                "for perspective of type %s" % \
459                (callback, PerspectiveType.types[type])
460            )
461
462        return False
463
464    def __pack_widgets(self):
465        "Pack widgets"
466
467        self.menubar = self.ui_manager.get_widget("/menubar")
468        self.vbox.pack_start(self.menubar, False, False, 0)
469
470        self.toolbar = self.ui_manager.get_widget("/toolbar")
471        self.toolbar.set_style(gtk.TOOLBAR_ICONS)
472
473        self.vbox.pack_start(self.toolbar, False, False, 0)
474
475        item = self.ui_manager.get_widget('/menubar/Views')
476        item.remove_submenu()
477
478        self.vbox.pack_start(self.main_paned)
479        self.vbox.pack_start(self.statusbar, False, False)
480
481        # Tabs
482        self.register_tab(MainTab())
483
484        self.register_tab(ProtocolSelectorTab(),
485                          Prefs()['gui.views.protocol_selector_tab'].value)
486        self.register_tab(PropertyTab(),
487                          Prefs()['gui.views.property_tab'].value)
488        self.register_tab(StatusTab(),
489                          Prefs()['gui.views.status_tab'].value)
490        self.register_tab(OperationsTab(),
491                          Prefs()['gui.views.operations_tab'].value)
492        self.register_tab(VteTab(),
493                          Prefs()['gui.views.vte_tab'].value)
494        self.register_tab(HackTab(),
495                          Prefs()['gui.views.hack_tab'].value)
496        self.register_tab(ConsoleTab(),
497                          Prefs()['gui.views.console_tab'].value)
498
499        self.add(self.vbox)
500
501    def __connect_signals(self):
502        "Connect signals"
503        self.connect('delete-event', self.__on_quit)
504
505        # Ok we need also to connect signals from main notebook
506        # so we could manage easilly the bind_perspective calls
507
508        maintab = self.get_tab("MainTab")
509        maintab.session_notebook.connect('page-added',
510                                         self.__on_maintab_page_added)
511        maintab.session_notebook.connect('page-removed',
512                                         self.__on_maintab_page_removed)
513
514    def connect_tabs_signals(self):
515        "Used to connect signals between tabs"
516
517        for key, tab in self.registered_tabs.items():
518            tab.connect_tab_signals()
519
520    def __on_maintab_page_added(self, notebook, page, pagenum):
521        for perspective in page.perspectives:
522            try:
523                idx = PerspectiveType.types[type(perspective)]
524
525                for callback in self.perspective_binder[idx]:
526                    callback(perspective, idx, False, True)
527            except:
528                pass
529
530    def __on_maintab_page_removed(self, notebook, page, pagenum):
531        if not isinstance(page, Session):
532            return
533
534        for perspective in page.perspectives:
535            try:
536                idx = PerspectiveType.types[type(perspective)]
537
538                for callback in self.perspective_binder[idx]:
539                    callback(perspective, idx, True, False)
540            except:
541                pass
542
543    def __on_toggle_tab_menu(self, menuitem, tab):
544        if menuitem.get_active():
545            self.main_paned.add_view(tab)
546        else:
547            self.main_paned.remove_view(tab)
548
549    def __on_routing(self, action):
550        dialog = RoutesDialog(self)
551
552        if dialog.run() == gtk.RESPONSE_ACCEPT:
553            dialog.save()
554
555        dialog.hide()
556        dialog.destroy()
557
558    def __on_plugins(self, action):
559        self.plugin_window.show()
560
561    def __on_preferences(self, action):
562        dialog = PreferenceDialog(self)
563        dialog.show()
564
565    def __on_about(self, action):
566        dialog = gtk.AboutDialog()
567
568        dialog.set_logo(
569            gtk.gdk.pixbuf_new_from_file(os.path.join(PIXMAPS_DIR,
570                                                      'pm-logo.png'))
571        )
572        dialog.set_name("PacketManipulator")
573        dialog.set_version(PM_VERSION)
574
575        dialog.set_website(PM_SITE)
576        dialog.set_website_label(PM_SITE)
577
578        dialog.set_comments(_("Packet manipulation made easy%s" \
579                              "\n«Audaces fortuna adiuvat»") % \
580                            ((PM_DEVELOPMENT) and \
581                                (' (SVN revision %s)' % PM_SVN_REVISION) or \
582                                ('')))
583
584        dialog.set_authors(['Francesco Piccinno <stack.box@gmail.com>'])
585
586        dialog.set_copyright('\n'.join(
587            ('Copyright (C) 2008 Francesco Piccinno' \
588                     ' <stack.box at gmail dot com>',
589             'Copyright (C) 2008 Adriano Monteiro Marques' \
590                          ' <py.adriano at gmail dot com>')))
591
592        dialog.set_license(_('This program is relased '
593                             'under the terms of GPLv2'))
594
595        dialog.run()
596        dialog.hide()
597        dialog.destroy()
598
599    def __on_select_iface(self, action):
600        dialog = InterfaceDialog(self)
601
602        if dialog.run() == gtk.RESPONSE_ACCEPT:
603            iface = dialog.get_selected()
604            args = dialog.get_options()
605
606            if iface or args['capmethod'] == 1:
607                tab = self.get_tab("OperationsTab")
608                tab.tree.append_operation(SniffOperation(iface, **args))
609
610        dialog.hide()
611        dialog.destroy()
612
613    def __on_new_sequence(self, action):
614        tab = self.get_tab("MainTab")
615        tab.session_notebook.create_sequence_session([])
616
617    def __on_open_session(self, action):
618        types = {}
619        sessions = (Backend.StaticContext,
620                    Backend.SequenceContext,
621                    Backend.SniffContext)
622
623        for ctx in sessions:
624            for name, pattern in ctx.file_types:
625                types[pattern] = (name, ctx)
626
627        dialog = gtk.FileChooserDialog(_("Select a session"), self,
628                               buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
629                                        gtk.STOCK_OPEN, gtk.RESPONSE_ACCEPT))
630
631        filterall = gtk.FileFilter()
632        filterall.set_name(_('All supported files'))
633        [filterall.add_pattern(k) for k in types]
634        dialog.add_filter(filterall)
635
636        for pattern, (name, ctx) in types.items():
637            filter = gtk.FileFilter()
638            filter.set_name(name)
639            filter.add_pattern(pattern)
640            dialog.add_filter(filter)
641
642        if dialog.run() == gtk.RESPONSE_ACCEPT:
643            ctx = None
644            fname = dialog.get_filename()
645
646            try:
647                find = fname.split('.')[-1]
648
649                for pattern in types:
650                    if pattern.split('.')[-1] == find:
651                        ctx = types[pattern][1]
652            except:
653                pass
654
655            if ctx is not Backend.SequenceContext and \
656               ctx is not Backend.SniffContext and \
657               ctx is not Backend.StaticContext:
658
659                d = HIGAlertDialog(type=gtk.MESSAGE_ERROR,
660                    message_format=_("Unable to open selected session"),
661                    secondary_text=_("PacketManipulator is unable to guess the "
662                                     "file type. Try to modify the extension "
663                                     "and to reopen the file."))
664                d.run()
665                d.destroy()
666            else:
667                self.open_generic_file_async(fname)
668
669        dialog.hide()
670        dialog.destroy()
671
672    def open_generic_file_async(self, fname):
673        """
674        Open a generic file (pcap/sequence and other supported file format)
675        @param fname the path to the file to open
676        """
677        tab = self.get_tab("OperationsTab")
678        tab.tree.append_operation(FileOperation(fname, FileOperation.TYPE_LOAD))
679
680    def open_generic_file(self, fname):
681        """
682        Open a generic file (pcap/sequence and other supported file format)
683        @param fname the path to the file to open
684        @return a PM.Session.Base.Session object or None on errors
685        """
686
687        if not os.path.isfile(fname):
688            return None
689
690        types = {}
691        sessions = (Backend.StaticContext,
692                    Backend.SequenceContext,
693                    Backend.SniffContext)
694
695        for ctx in sessions:
696            for name, pattern in ctx.file_types:
697                types[pattern] = (name, ctx)
698
699        try:
700            find = fname.split('.')[-1]
701
702            for pattern in types:
703                if pattern.split('.')[-1] == find:
704                    ctx = types[pattern][1]
705        except:
706            pass
707
708        tab = self.get_tab("MainTab")
709
710        if ctx is Backend.SequenceContext:
711            return tab.session_notebook.load_sequence_session(fname)
712
713        elif ctx is Backend.SniffContext:
714            return tab.session_notebook.load_sniff_session(fname)
715
716        elif ctx is Backend.StaticContext:
717            return tab.session_notebook.load_static_session(fname)
718
719    def __on_save_session(self, action):
720        maintab = self.get_tab("MainTab")
721        session = maintab.get_current_session()
722
723        if session:
724            session.save()
725
726    def __on_save_session_as(self, action):
727        maintab = self.get_tab("MainTab")
728        session = maintab.get_current_session()
729
730        if session:
731            session.save_as()
732
733    def __on_quit(self, *args):
734        self.hide()
735
736        # We need to stop the pending sniff threads
737        maintab = self.get_tab("MainTab")
738
739        lst = []
740
741        for page in maintab.session_notebook:
742            if isinstance(page, Session) and \
743               isinstance(page.context, Backend.TimedContext):
744                lst.append(page.context)
745
746        for ctx in lst:
747            ctx.stop()
748
749        # Avoids joining all threads are daemon
750        #for ctx in lst:
751        #    ctx.join()
752
753        log.debug("Saving options before exiting")
754        Prefs().write_options()
755
756        gtk.main_quit()
Note: See TracBrowser for help on using the browser.