root/branch/BTSniff/PacketManipulator/PM/Gui/Tabs/OperationsTab.py @ 5395

Revision 5395, 22.3 kB (checked in by qsy, 4 years ago)

Old PM for testing BtSniffer?. Pre-renaming to namespace

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           
385class BtSniffOperation(Backend.BtSniffContext, Operation):
386   
387    def __init__(self, iface, capfile = None, scount = 0, stime = 0, 
388                 master_add = None, slave_add = None, crack_pin = False, 
389                 set_timeout = 30):
390       
391        Operation.__init__(self)
392        Backend.BtSniffContext.__init__(self, iface, capfile, scount, stime,
393                                      master_add, slave_add, crack_pin, 
394                                      set_timeout)
395       
396        nb = PMApp().main_window.get_tab('MainTab').session_notebook
397        self.session = nb.create_btsniff_session(self)
398        log.debug('BtSniffOperation__init__: isinstance BtSniffContext? %s' % 
399                  str(isinstance(self.session.context, Backend.BtSniffContext)))
400   
401    def _start(self):
402        log.debug('BtSniffOperation.start')
403        Backend.BtSniffContext._start(self)
404       
405        if self.session:
406            self.session.sniff_page.clear()
407            self.session.sniff_page.reload()
408   
409    def activate(self):
410        if not self.session:
411            nb = PMApp().main_window.get_tab('MainTab').session_notebook
412            self.session = nb.create_btsniff_session(self)
413       
414    def __recv_callback(self, packet, udata):
415        if not self.SKIP_UPDATE:
416            self.notify_parent()
417
418class SequenceOperation(Backend.SequenceContext, Operation):
419    def __init__(self, seq, count, inter, iface=None, strict=True, \
420                 report_recv=False, report_sent=True):
421
422        capmethod = Prefs()['backend.system.sequence.capmethod'].value
423
424        if capmethod < 0 or capmethod > 2:
425            Prefs()['backend.system.sendreceive.capmethod'].value = 0
426            capmethod = 0
427
428        log.debug('Using %d as capmethod for SendReceiveContext' % capmethod)
429
430        Operation.__init__(self)
431        Backend.SequenceContext.__init__(self, seq, count, inter, iface,   \
432                                         strict, report_recv, report_sent, \
433                                         capmethod,                        \
434                                         self.__send_callback,             \
435                                         self.__receive_callback)
436
437        nb = PMApp().main_window.get_tab('MainTab').session_notebook
438        self.session = nb.create_sniff_session(self)
439
440    def __send_callback(self, packet, want_reply, loop, count, udata):
441        if not self.SKIP_UPDATE:
442            self.notify_parent()
443
444    def __receive_callback(self, packet, reply, udata):
445        if not self.SKIP_UPDATE:
446            self.notify_parent()
447
448    def _start(self):
449        ret = Backend.SequenceContext._start(self)
450
451        if ret and self.session:
452            self.session.sniff_page.clear()
453            self.session.sniff_page.reload()
454
455        return ret
456
457class AttackOperation(Backend.AttackContext, Operation):
458    def __init__(self, dev1, dev2, bpf_filter):
459        capmethod = Prefs()['backend.system.attack.capmethod'].value
460
461        if capmethod < 0 or capmethod > 2:
462            Prefs()['backend.system.sendreceive.capmethod'].value = 0
463            capmethod = 0
464
465        Operation.__init__(self)
466        Backend.AttackContext.__init__(self, dev1, dev2, bpf_filter, capmethod)
467
468        nb = PMApp().main_window.get_tab('MainTab').session_notebook
469        self.session = nb.create_attack_session(self)
470
471class OperationTree(gtk.TreeView):
472    def __init__(self):
473        self.store = gtk.ListStore(object)
474        super(OperationTree, self).__init__(self.store)
475
476        # We have only one column with a progress bar
477        # showing a text
478
479        col = gtk.TreeViewColumn(_('Operation'))
480
481        col.set_expand(True)
482        col.set_resizable(True)
483        col.set_resizable(True)
484
485        col.set_sizing(gtk.TREE_VIEW_COLUMN_GROW_ONLY)
486
487        rend = gtk.CellRendererPixbuf()
488        col.pack_start(rend, False)
489        col.set_cell_data_func(rend, self.__pix_data_func)
490
491        rend = gtk.CellRendererText()
492        col.pack_start(rend)
493        col.set_cell_data_func(rend, self.__text_data_func)
494
495        self.append_column(col)
496
497        rend = gtk.CellRendererProgress()
498        col = gtk.TreeViewColumn(_('Status'), rend)
499
500        col.set_expand(False)
501        col.set_resizable(True)
502        col.set_fixed_width(150)
503        col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
504
505        col.set_cell_data_func(rend, self.__progress_data_func)
506
507        self.append_column(col)
508
509        self.set_rules_hint(True)
510
511        self.icon_operation = get_pixbuf('operation_small')
512        self.connect('button-release-event', self.__on_button_release)
513
514        self.timeout_id = None
515        self.timeout_update()
516
517    def is_someone_running(self):
518        def check_running(model, path, iter, lst):
519            op = model.get_value(iter, 0)
520
521            if op.state == op.RUNNING:
522                lst[0] = True
523                return True
524
525        lst = [False]
526        self.store.foreach(check_running, lst)
527
528        return lst[0]
529
530    def timeout_update(self):
531        if Prefs()['gui.operationstab.uniqueupdate'].value == True and \
532           not self.timeout_id and self.is_someone_running():
533
534            # We're not empty so we can set a timeout callback to update all
535            # actives iters at the same time reducing the CPU usage.
536
537            self.timeout_id = gobject.timeout_add(
538                Prefs()['gui.operationstab.updatetimeout'].value or 500,
539                self.__timeout_cb
540            )
541
542            log.debug('Adding a timeout function to update the OperationsTab')
543
544    def __timeout_cb(self):
545        def update(model, path, iter, idx):
546            operation = model.get_value(iter, 0)
547
548            if operation.state == operation.RUNNING:
549                operation.notify_parent()
550                idx[0] += 1
551
552        idx = [0]
553        self.store.foreach(update, idx)
554
555        if not idx[0]:
556            log.debug('Removing the timeout callback for OperationsTab updates')
557            self.timeout_id = None
558            return False
559
560        return True
561
562    def __pix_data_func(self, col, cell, model, iter):
563        cell.set_property('pixbuf', self.icon_operation)
564
565    def __text_data_func(self, col, cell, model, iter):
566        operation = model.get_value(iter, 0)
567        cell.set_property('text', operation.get_summary())
568
569    def __progress_data_func(self, col, cell, model, iter):
570        operation = model.get_value(iter, 0)
571
572        if operation.get_percentage() != None:
573            cell.set_property('value', operation.get_percentage())
574            cell.set_property('pulse', -1)
575        else:
576            cell.set_property('value', 0)
577            cell.set_property('pulse', operation.percentage)
578
579    def __on_button_release(self, widget, evt):
580        if evt.button != 3:
581            return
582
583        model, iter = self.get_selection().get_selected()
584
585        if not iter:
586            return
587
588        menu = gtk.Menu()
589        operation = model.get_value(iter, 0)
590
591        labels = (
592            _('Open'),
593            _('Resume operation'),
594            _('Pause operation'),
595            _('Stop operation'),
596            _('Restart operation'),
597            _('Remove finished')
598        )
599
600        stocks = (
601            gtk.STOCK_OPEN,
602            gtk.STOCK_MEDIA_PLAY,
603            gtk.STOCK_MEDIA_PAUSE,
604            gtk.STOCK_MEDIA_STOP,
605            gtk.STOCK_REFRESH,
606            gtk.STOCK_CLEAR
607        )
608
609        callbacks = (
610            self.__on_open,
611            self.__on_resume,
612            self.__on_pause,
613            self.__on_stop,
614            self.__on_restart,
615            self.__on_clear
616        )
617
618        idx = 0
619        for lbl, stock, cb in zip(labels, stocks, callbacks):
620            action =  gtk.Action(None, lbl, None, stock)
621            action.connect('activate', cb, operation)
622            item = action.create_menu_item()
623
624            if idx in (1, 2) and not operation.has_pause or \
625               idx == 3 and not operation.has_stop or \
626               idx == 4 and not operation.has_restart:
627                item.set_sensitive(False)
628
629            menu.append(item)
630
631            idx += 1
632
633        menu.show_all()
634        menu.popup(None, None, None, evt.button, evt.time)
635
636    # Public functions
637
638    def append_operation(self, operation, start=True):
639        """
640        Append an operation to the store
641
642        @param operation an Operation object
643        @param start if the operation should be started
644        """
645
646        assert (isinstance(operation, Operation))
647
648        iter = self.store.append([operation])
649        # This is for managing real-time updates
650        operation.set_iter(self.store, iter)
651
652        if start:
653            operation.start()
654
655        self.timeout_update()
656
657    def remove_operation(self, operation):
658        """
659        Remove an operation from the store
660
661        @param operation the Operation to remove
662        """
663
664        if not isinstance(operation, Operation):
665            return
666
667        def remove(model, path, iter, operation):
668            if model.get_value(iter, 0) is operation:
669                model.remove(iter)
670                return True
671
672        self.store.foreach(remove, operation)
673
674    # Callbacks section
675
676    def __on_open(self, action, operation):
677        operation.activate()
678
679    def __on_resume(self, action, operation):
680        operation.resume()
681
682    def __on_pause(self, action, operation):
683        operation.pause()
684
685    def __on_stop(self, action, operation):
686        operation.stop()
687
688    def __on_restart(self, action, operation):
689        operation.restart()
690
691    def __on_clear(self, action, operation):
692        def scan(model, path, iter, lst):
693            op = model.get_value(iter, 0)
694
695            if op.state == op.NOT_RUNNING:
696                lst.append(gtk.TreeRowReference(model, path))
697
698        lst = []
699        self.store.foreach(scan, lst)
700
701        for ref in lst:
702            self.store.remove(self.store.get_iter(ref.get_path()))
703
704class OperationsTab(UmitView):
705    icon_name = 'operation_small'
706    tab_position = gtk.POS_BOTTOM
707    label_text = _('Operations')
708    name = 'OperationsTab'
709
710    def create_ui(self):
711        self.tree = OperationTree()
712
713        sw = gtk.ScrolledWindow()
714        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
715        sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
716        sw.add(self.tree)
717
718        self._main_widget.add(sw)
719        self._main_widget.show_all()
Note: See TracBrowser for help on using the browser.