root/branch/PacketManipulator/PM/Gui/Tabs/OperationsTab.py @ 5124

Revision 5124, 21.0 kB (checked in by nopper, 4 years ago)

2009-06-24 Francesco Piccinno <stack.box@…>

  • PacketManipulator
    • Setting PM_NOPSYCO to avoid using psyco module while developing.
  • PM/Gui/Sessions/AttackSession.py:
  • PM/Gui/Pages/AttackPage.py:
  • PM/Gui/Dialogs/Preferences.py:
  • PM/Gui/Dialogs/NewAttack.py:
    • Avoid to return the second interface if the gtk.ComboBox? is not sensitive.
  • PM/Gui/Sessions/SniffSession.py PM/Gui/Sessions/SequenceSession.py:
    • Removing unused class attribute.
  • PM/Gui/Widgets/Interfaces.py:
    • Fixed a regression introduced by the addition of add_auto parameter.
  • PM/Gui/Widgets/MultiPaneds.py:
    • Implemented automatic resize of the paned's children. Really useful.
  • PM/Gui/Widgets/Expander.py:
    • Fixed indentation problems and introduce activate signal used in MultiPaneds?.py file (described above).
  • PM/Gui/Core/MainWindow.py:
    • Fixed a bug due to on_disconnect_proxy.
    • New attack menu entry is not dummy anymore.
  • PM/Gui/Core/Views.py:
    • Leaving a reasonable amount of free pixels as borders.
  • PM/Gui/Tabs/OperationsTab.py:
  • PM/Gui/Tabs/MainTab.py:
    • Introduced create_attack_session() method.
  • PM/Backend/Scapy/Context/Sniff.py:
    • Fixed _socketobject has no attribute datalink' bug.
  • PM/Manager/PreferenceManager.py PM/Backend/Abstract/BaseContext/Attack.py:
  • PM/Backend/Scapy/Context/Attack.py:
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 gobject
23
24from threading import Thread
25
26from PM import Backend
27from PM.Core.I18N import _
28from PM.Core.Tracing import trace
29from PM.Core.Logger import log
30
31from PM.Gui.Core.App import PMApp
32from PM.Gui.Core.Views import UmitView
33from PM.Gui.Core.Icons import get_pixbuf
34
35from PM.Manager.PreferenceManager import Prefs
36
37class Operation(object):
38    """
39    This is an abstract class representing a network operation
40    like sending packets or receiving packets and should be ovverriden
41    """
42
43    # CHECKME: Add this to avoid multiple request of that prefs.
44    SKIP_UPDATE = Prefs()['gui.operationstab.uniqueupdate'].value
45
46    def __init__(self):
47        self.iter = None
48        self.model = None
49
50    def set_iter(self, model, iter):
51        self.iter = iter
52        self.model = model
53
54    def notify_parent(self):
55        # emit a row-changed to update the model
56
57        if self.iter and self.model:
58            self.model.row_changed(self.model.get_path(self.iter), self.iter)
59
60    def activate(self):
61        "Called when the user clicks on the row"
62        pass
63
64    def get_summary(self):
65        return 'Implement me'
66
67    def get_percentage(self):
68        """
69        @return None if we should pulse or a int
70                if None you should provide percentage attribute
71        """
72        return None
73
74class FileOperation(Operation):
75    """
76    Abstract class to manage file operations like save/load
77    """
78    TYPE_LOAD, TYPE_SAVE = range(2)
79    RUNNING, NOT_RUNNING = range(2)
80
81    has_pause = False
82    has_stop = False
83    has_start = True
84    has_restart = False
85
86    def __init__(self, obj, type):
87        """
88        @param obj if type is TYPE_LOAD the path of the file to load or a
89                   Session to save.
90        @param type TYPE_LOAD or TYPE_SAVE
91        """
92        Operation.__init__(self)
93
94        self.file = file
95        self.type = type
96
97        if type == FileOperation.TYPE_LOAD:
98            self.file = obj
99        else:
100            self.session = obj
101            self.ctx = self.session.context
102
103        self.percentage = 0
104        self.thread = None
105        self.state = self.NOT_RUNNING
106        self.loading_view = False
107
108        if type == FileOperation.TYPE_LOAD:
109            self.summary = _('Loading of %s pending.') % self.file
110        else:
111            self.summary = _('Saving to %s pending.') % self.ctx.cap_file
112
113    def start(self):
114        if self.state == self.RUNNING:
115            return
116
117        self.state = self.RUNNING
118
119        if self.type == FileOperation.TYPE_LOAD:
120            self.summary = _('Loading %s') % self.file
121            self._read_file()
122        else:
123            self.summary = _('Saving to %s') % self.ctx.cap_file
124            self._save_file()
125
126    def get_percentage(self):
127        if self.loading_view:
128            self.percentage = (self.percentage + 536870911) % gobject.G_MAXINT
129            return None
130        else:
131            return self.percentage
132
133    def get_summary(self):
134        return self.summary
135
136    @trace
137    def _save_file(self):
138        """
139        @see PM.Gui.Sessions.Base.save_session
140        """
141
142        log.debug('Saving context %s to %s' % (self.ctx, self.ctx.cap_file))
143
144        if isinstance(self.ctx, Backend.StaticContext):
145            self.summary = _('Saving packets to %s') % self.ctx.cap_file
146        elif isinstance(self.ctx, Backend.SequenceContext):
147            self.summary = _('Saving sequence to %s') % self.ctx.cap_file
148
149        self.start_async_thread(())
150
151    @trace
152    def _read_file(self):
153        types = {}
154        sessions = (Backend.StaticContext,
155                    Backend.SequenceContext,
156                    Backend.SniffContext)
157
158        for ctx in sessions:
159            for name, pattern in ctx.file_types:
160                types[pattern] = (name, ctx)
161
162        try:
163            find = self.file.split('.')[-1]
164
165            for pattern in types:
166                if pattern.split('.')[-1] == find:
167                    ctx = types[pattern][1]
168        except:
169            pass
170
171        if ctx is not Backend.SequenceContext and \
172           ctx is not Backend.SniffContext and \
173           ctx is not Backend.StaticContext:
174
175            self.summary = _('Unable to recognize file type.')
176        else:
177            self.start_async_thread((ctx, ))
178
179    @trace
180    def start_async_thread(self, udata):
181        self.thread = Thread(target=self._thread_main, name='FileOperation',
182                             args=udata)
183        self.thread.setDaemon(True)
184        self.thread.start()
185
186    @trace
187    def __on_idle(self, udata):
188        if self.type == FileOperation.TYPE_LOAD:
189            ctx, rctx = udata
190
191            self.loading_view = True
192
193            log.debug('Creating a new session after loading for %s' % str(ctx))
194
195            tab = PMApp().main_window.get_tab('MainTab')
196
197            if ctx is Backend.SequenceContext:
198                from PM.Gui.Sessions.SequenceSession import SequenceSession
199                tab.session_notebook.bind_session(SequenceSession, rctx)
200
201            elif ctx is Backend.SniffContext or \
202                 ctx is Backend.StaticContext:
203
204                from PM.Gui.Sessions.SniffSession import SniffSession
205                tab.session_notebook.bind_session(SniffSession, rctx)
206
207        else:
208            from PM.Gui.Sessions.SniffSession import SniffSession
209
210            if isinstance(self.session, SniffSession):
211                self.session.sniff_page.statusbar.label = '<b>%s</b>' % \
212                                                          self.ctx.summary
213        self.loading_view = False
214        self.percentage = 100.0
215        self.state = self.NOT_RUNNING
216
217        # Force update.
218        self.notify_parent()
219        return False
220
221    @trace
222    def _thread_main(self, udata=None):
223        if self.type == FileOperation.TYPE_LOAD:
224            ctx = udata
225            rctx = None
226
227            log.debug('Loading file as %s' % str(ctx))
228
229            if ctx is Backend.SequenceContext:
230                rctx = Backend.SequenceContext(self.file)
231
232            elif ctx is Backend.SniffContext or \
233                 ctx is Backend.StaticContext:
234
235                rctx = Backend.StaticContext(self.file, self.file,
236                                 Prefs()['backend.system.static.attacks'].value)
237
238            if rctx is not None:
239                # Let's update our operation directly from load
240                if rctx.load(operation=self) == True:
241                    # Now let's add a callback to when
242                    gobject.idle_add(self.__on_idle, (ctx, rctx))
243                else:
244                    log.error('Error while loading context on %s.' % self.file)
245                    self.state = self.NOT_RUNNING
246        else:
247            log.debug('Saving %s to %s' % (self.ctx, self.ctx.cap_file))
248
249            if self.ctx.save(operation=self) == True:
250                gobject.idle_add(self.__on_idle, ())
251            else:
252                log.error('Error while saving context on %s.' % \
253                          self.ctx.cap_file)
254                self.state = self.NOT_RUNNING
255
256        self.thread = None
257
258class SendOperation(Backend.SendContext, Operation):
259    def __init__(self, packet, count, inter, iface):
260        Operation.__init__(self)
261        Backend.SendContext.__init__(self, packet, count, inter, iface, \
262                                     self.__send_callback, None)
263
264    def __send_callback(self, packet, udata=None):
265        if not self.SKIP_UPDATE:
266            self.notify_parent()
267
268
269class SendReceiveOperation(Backend.SendReceiveContext, Operation):
270    """
271    A send receive operation
272    """
273
274    def __init__(self, packet, count, inter, \
275                 iface=None, strict=True, report_recv=False, \
276                 report_sent=True, background=True):
277        """
278        Construct a SendReceive Operation
279
280        @param packet the packet to send
281        @param count how many times the packet will be sent
282        @param inter the interval between emission
283        @param iface the iface to listen to
284        @param strict strict checking for reply
285        @param report_recv report received packets
286        @param report_sent report sent packets
287        @param background if the operation should have a session when starts
288        """
289
290        capmethod = Prefs()['backend.system.sendreceive.capmethod'].value
291
292        if capmethod < 0 or capmethod > 2:
293            Prefs()['backend.system.sendreceive.capmethod'].value = 0
294            capmethod = 0
295
296        log.debug('Using %d as capmethod for SendReceiveContext' % capmethod)
297
298        Operation.__init__(self)
299        Backend.SendReceiveContext.__init__(self, packet, count, inter, iface,
300                                            strict, report_recv, report_sent,
301                                            capmethod,
302                                            self.__send_callback,
303                                            self.__receive_callback,
304                                            None, None)
305
306        if background:
307            self.session = None
308        else:
309            self.__create_session()
310
311    def _start(self):
312        ret = Backend.SendReceiveContext._start(self)
313
314        if ret and self.session:
315            self.session.sniff_page.reload()
316
317        return ret
318
319    def _restart(self):
320        ret = Backend.SendReceiveContext._restart(self)
321
322        if ret and self.session:
323            self.session.sniff_page.clear()
324
325        return ret
326
327    def __create_session(self):
328        nb = PMApp().main_window.get_tab("MainTab").session_notebook
329        self.session = nb.create_sniff_session(self)
330
331    def __send_callback(self, packet, idx, udata):
332        if not self.SKIP_UPDATE:
333            self.notify_parent()
334
335    def __receive_callback(self, reply, is_reply, udata):
336        if not self.SKIP_UPDATE:
337            self.notify_parent()
338
339    def activate(self):
340        if not self.session:
341            self.__create_session()
342
343
344class SniffOperation(Backend.SniffContext, Operation):
345    def __init__(self, iface, filter=None, minsize=0, maxsize=0, capfile=None, \
346                 scount=0, stime=0, ssize=0, real=True, scroll=True, \
347                 resmac=True, resname=False, restransport=True, promisc=True, \
348                 background=False, capmethod=0, attacks=True):
349
350        Operation.__init__(self)
351        Backend.SniffContext.__init__(self, iface, filter, minsize, maxsize,
352                                      capfile, scount, stime, ssize, real,
353                                      scroll, resmac, resname, restransport,
354                                      promisc, background, capmethod, attacks,
355                                      self.__recv_callback, None)
356
357        if not self.background:
358            nb = PMApp().main_window.get_tab('MainTab').session_notebook
359            self.session = nb.create_sniff_session(self)
360        else:
361            self.session = None
362
363    def _start(self):
364        ret = Backend.SniffContext._start(self)
365
366        if self.session:
367            if ret:
368                self.session.sniff_page.clear()
369
370            # We have to call it also in case of start failed to set up the
371            # correct summary on the label.
372            self.session.sniff_page.reload()
373
374        return ret
375
376    def activate(self):
377        if not self.session:
378            nb = PMApp().main_window.get_tab('MainTab').session_notebook
379            self.session = nb.create_sniff_session(self)
380
381    def __recv_callback(self, packet, udata):
382        if not self.SKIP_UPDATE:
383            self.notify_parent()
384
385
386class SequenceOperation(Backend.SequenceContext, Operation):
387    def __init__(self, seq, count, inter, iface=None, strict=True, \
388                 report_recv=False, report_sent=True):
389
390        capmethod = Prefs()['backend.system.sequence.capmethod'].value
391
392        if capmethod < 0 or capmethod > 2:
393            Prefs()['backend.system.sendreceive.capmethod'].value = 0
394            capmethod = 0
395
396        log.debug('Using %d as capmethod for SendReceiveContext' % capmethod)
397
398        Operation.__init__(self)
399        Backend.SequenceContext.__init__(self, seq, count, inter, iface,   \
400                                         strict, report_recv, report_sent, \
401                                         capmethod,                        \
402                                         self.__send_callback,             \
403                                         self.__receive_callback)
404
405        nb = PMApp().main_window.get_tab('MainTab').session_notebook
406        self.session = nb.create_sniff_session(self)
407
408    def __send_callback(self, packet, want_reply, loop, count, udata):
409        if not self.SKIP_UPDATE:
410            self.notify_parent()
411
412    def __receive_callback(self, packet, reply, udata):
413        if not self.SKIP_UPDATE:
414            self.notify_parent()
415
416    def _start(self):
417        ret = Backend.SequenceContext._start(self)
418
419        if ret and self.session:
420            self.session.sniff_page.clear()
421            self.session.sniff_page.reload()
422
423        return ret
424
425class AttackOperation(Backend.AttackContext, Operation):
426    def __init__(self, dev1, dev2, bpf_filter):
427        capmethod = Prefs()['backend.system.attack.capmethod'].value
428
429        if capmethod < 0 or capmethod > 2:
430            Prefs()['backend.system.sendreceive.capmethod'].value = 0
431            capmethod = 0
432
433        Operation.__init__(self)
434        Backend.AttackContext.__init__(self, dev1, dev2, bpf_filter, capmethod)
435
436        nb = PMApp().main_window.get_tab('MainTab').session_notebook
437        self.session = nb.create_attack_session(self)
438
439class OperationTree(gtk.TreeView):
440    def __init__(self):
441        self.store = gtk.ListStore(object)
442        super(OperationTree, self).__init__(self.store)
443
444        # We have only one column with a progress bar
445        # showing a text
446
447        col = gtk.TreeViewColumn(_('Operation'))
448
449        col.set_expand(True)
450        col.set_resizable(True)
451        col.set_resizable(True)
452
453        col.set_sizing(gtk.TREE_VIEW_COLUMN_GROW_ONLY)
454
455        rend = gtk.CellRendererPixbuf()
456        col.pack_start(rend, False)
457        col.set_cell_data_func(rend, self.__pix_data_func)
458
459        rend = gtk.CellRendererText()
460        col.pack_start(rend)
461        col.set_cell_data_func(rend, self.__text_data_func)
462
463        self.append_column(col)
464
465        rend = gtk.CellRendererProgress()
466        col = gtk.TreeViewColumn(_('Status'), rend)
467
468        col.set_expand(False)
469        col.set_resizable(True)
470        col.set_fixed_width(150)
471        col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
472
473        col.set_cell_data_func(rend, self.__progress_data_func)
474
475        self.append_column(col)
476
477        self.set_rules_hint(True)
478
479        self.icon_operation = get_pixbuf('operation_small')
480        self.connect('button-release-event', self.__on_button_release)
481
482        self.timeout_id = None
483        self.timeout_update()
484
485    def is_someone_running(self):
486        def check_running(model, path, iter, lst):
487            op = model.get_value(iter, 0)
488
489            if op.state == op.RUNNING:
490                lst[0] = True
491                return True
492
493        lst = [False]
494        self.store.foreach(check_running, lst)
495
496        return lst[0]
497
498    def timeout_update(self):
499        if Prefs()['gui.operationstab.uniqueupdate'].value == True and \
500           not self.timeout_id and self.is_someone_running():
501
502            # We're not empty so we can set a timeout callback to update all
503            # actives iters at the same time reducing the CPU usage.
504
505            self.timeout_id = gobject.timeout_add(
506                Prefs()['gui.operationstab.updatetimeout'].value or 500,
507                self.__timeout_cb
508            )
509
510            log.debug('Adding a timeout function to update the OperationsTab')
511
512    def __timeout_cb(self):
513        def update(model, path, iter, idx):
514            operation = model.get_value(iter, 0)
515
516            if operation.state == operation.RUNNING:
517                operation.notify_parent()
518                idx[0] += 1
519
520        idx = [0]
521        self.store.foreach(update, idx)
522
523        if not idx[0]:
524            log.debug('Removing the timeout callback for OperationsTab updates')
525            self.timeout_id = None
526            return False
527
528        return True
529
530    def __pix_data_func(self, col, cell, model, iter):
531        cell.set_property('pixbuf', self.icon_operation)
532
533    def __text_data_func(self, col, cell, model, iter):
534        operation = model.get_value(iter, 0)
535        cell.set_property('text', operation.get_summary())
536
537    def __progress_data_func(self, col, cell, model, iter):
538        operation = model.get_value(iter, 0)
539
540        if operation.get_percentage() != None:
541            cell.set_property('value', operation.get_percentage())
542            cell.set_property('pulse', -1)
543        else:
544            cell.set_property('value', 0)
545            cell.set_property('pulse', operation.percentage)
546
547    def __on_button_release(self, widget, evt):
548        if evt.button != 3:
549            return
550
551        model, iter = self.get_selection().get_selected()
552
553        if not iter:
554            return
555
556        menu = gtk.Menu()
557        operation = model.get_value(iter, 0)
558
559        labels = (
560            _('Open'),
561            _('Resume operation'),
562            _('Pause operation'),
563            _('Stop operation'),
564            _('Restart operation'),
565            _('Remove finished')
566        )
567
568        stocks = (
569            gtk.STOCK_OPEN,
570            gtk.STOCK_MEDIA_PLAY,
571            gtk.STOCK_MEDIA_PAUSE,
572            gtk.STOCK_MEDIA_STOP,
573            gtk.STOCK_REFRESH,
574            gtk.STOCK_CLEAR
575        )
576
577        callbacks = (
578            self.__on_open,
579            self.__on_resume,
580            self.__on_pause,
581            self.__on_stop,
582            self.__on_restart,
583            self.__on_clear
584        )
585
586        idx = 0
587        for lbl, stock, cb in zip(labels, stocks, callbacks):
588            action =  gtk.Action(None, lbl, None, stock)
589            action.connect('activate', cb, operation)
590            item = action.create_menu_item()
591
592            if idx in (1, 2) and not operation.has_pause or \
593               idx == 3 and not operation.has_stop or \
594               idx == 4 and not operation.has_restart:
595                item.set_sensitive(False)
596
597            menu.append(item)
598
599            idx += 1
600
601        menu.show_all()
602        menu.popup(None, None, None, evt.button, evt.time)
603
604    # Public functions
605
606    def append_operation(self, operation, start=True):
607        """
608        Append an operation to the store
609
610        @param operation an Operation object
611        @param start if the operation should be started
612        """
613
614        assert (isinstance(operation, Operation))
615
616        iter = self.store.append([operation])
617        # This is for managing real-time updates
618        operation.set_iter(self.store, iter)
619
620        if start:
621            operation.start()
622
623        self.timeout_update()
624
625    def remove_operation(self, operation):
626        """
627        Remove an operation from the store
628
629        @param operation the Operation to remove
630        """
631
632        if not isinstance(operation, Operation):
633            return
634
635        def remove(model, path, iter, operation):
636            if model.get_value(iter, 0) is operation:
637                model.remove(iter)
638                return True
639
640        self.store.foreach(remove, operation)
641
642    # Callbacks section
643
644    def __on_open(self, action, operation):
645        operation.activate()
646
647    def __on_resume(self, action, operation):
648        operation.resume()
649
650    def __on_pause(self, action, operation):
651        operation.pause()
652
653    def __on_stop(self, action, operation):
654        operation.stop()
655
656    def __on_restart(self, action, operation):
657        operation.restart()
658
659    def __on_clear(self, action, operation):
660        def scan(model, path, iter, lst):
661            op = model.get_value(iter, 0)
662
663            if op.state == op.NOT_RUNNING:
664                lst.append(gtk.TreeRowReference(model, path))
665
666        lst = []
667        self.store.foreach(scan, lst)
668
669        for ref in lst:
670            self.store.remove(self.store.get_iter(ref.get_path()))
671
672class OperationsTab(UmitView):
673    icon_name = 'operation_small'
674    tab_position = gtk.POS_BOTTOM
675    label_text = _('Operations')
676    name = 'OperationsTab'
677
678    def create_ui(self):
679        self.tree = OperationTree()
680
681        sw = gtk.ScrolledWindow()
682        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
683        sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
684        sw.add(self.tree)
685
686        self._main_widget.add(sw)
687        self._main_widget.show_all()
Note: See TracBrowser for help on using the browser.