root/network-scanner/branches/NSEFacilitator/umit/nsefacilitator/ScriptManager.py @ 5751

Revision 5751, 44.0 kB (checked in by diogo, 3 years ago)

Restructuring: second step

Line 
1# Copyright (C) 2007 Adriano Monteiro Marques <py.adriano@gmail.com>
2#
3# Author: Maxim Gavrilov <lovelymax@gmail.com>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19import os, os.path
20import sys
21import threading
22
23#from umit.core.Paths import Path
24#Path.set_umit_conf(os.path.join(os.path.split(__file__)[0], 'config', 'umit.conf'))
25
26from umit.core.I18N import _
27from umit.core.UmitConf import NSEManagerConfig
28from umit.core.Paths import Path
29from umit.gui.FileChoosers import DirectoryChooserDialog, AllFilesFileChooserDialog
30from umit.gui.BugReport import BugReport
31
32import gobject
33import gtk
34from higwidgets.higboxes import HIGVBox, HIGHBox, HIGSpacer
35from higwidgets.hignotebooks import HIGNotebook
36from higwidgets.higentries import HIGTextEntry
37from higwidgets.higbuttons import HIGButton
38from higwidgets.higtables import HIGTable
39from higwidgets.higdialogs import HIGDialog
40from higwidgets.higframe import HIGFrame
41from higwidgets.higprogressbars import HIGLabeledProgressBar
42from higwidgets.higwindows import HIGWindow
43from higwidgets.higlabels import HIGEntryLabel, HIGSectionLabel
44
45from NmapFetch import NmapFetchScripts
46from nseBase import ScriptBase, ScriptItem
47from nseConfig import ScriptConfig
48
49from ScriptWindow import ScriptWindow
50from ScriptEditor import ScriptEditorWindow, get_templates, DEFAULT_TEMPLATE_PATH
51from Utils import *
52
53
54# Filters
55class NoneFilter(object):
56    def get_name(self):
57        return "None"
58
59    def __call__(self, script):
60        return False
61   
62class AllFilter(object):
63    def get_name(self):
64        return "<b>" + _("All") + "</b>"
65   
66    def __call__(self, script):
67        return True
68
69class CategoryFilter(object):
70    def __init__(self, category):
71        self.category = category
72
73    def get_name(self):
74        return self.category
75
76    def __call__(self, script):
77        return self.category in  script.categories
78
79class SearchFilter(object):
80    def __init__(self, pattern):
81        self.pattern = pattern
82
83    def get_name(self):
84        return "<i>" + _("Search") + " '" + self.pattern + "'</i>"
85
86    def __call__(self, script):
87        text = "\n".join([
88            script.name,
89            script.id,
90            script.description,
91            script.type,
92            script.author,
93            script.license,
94            ",".join(script.categories)
95            ])
96        return self.pattern.lower() in text.lower()
97   
98# GUI classes
99class StatusPixbuf(object):
100    def __init__(self):
101        self.pixbufs = dict(
102            [(ScriptItem.STATE_NOT_INSTALLED, 'notinstalled'),
103            (ScriptItem.STATE_INSTALLED, 'installed'),
104            (ScriptItem.STATE_UPGRADABLE, 'upgradable')]
105        )
106        self.revers = dict([(v, k) for k, v in self.pixbufs.items()])
107
108    def get_status_pixbuf(self, item, script):
109        return self.pixbufs[item.get_state()]
110
111    def compare(self, model, it1, it2, column):
112        # FIX: 'get' are workaround for strange behavior (px2 == None) when appends new rows
113        state1 = self.revers.get(model[it1][column], -1)
114        state2 = self.revers.get(model[it2][column], -1)
115        return state1.__cmp__(state2)
116
117_status_pixbuf = StatusPixbuf()
118
119class ScriptManagerWindow(ScriptWindow):
120    columns_dict = {
121        'I' : (gtk.CellRendererPixbuf, str, _status_pixbuf.get_status_pixbuf),
122        'Name' : (gtk.CellRendererText, str, lambda i, s: s.name),
123        'ID': (gtk.CellRendererText, str, lambda i, s: s.id),
124        'Description': (gtk.CellRendererText, str, lambda i, s: s.description),
125        'Type'  : (gtk.CellRendererText, str, lambda i, s: s.type),
126        'Author' : (gtk.CellRendererText, str, lambda i, s: s.author),
127        'License' : (gtk.CellRendererText, str, lambda i, s: s.license),
128        'Categories' : (gtk.CellRendererText, str, lambda i, s: ",".join(s.categories)),
129        'Rule' : (gtk.CellRendererText, str, lambda i, s: s.rule),
130        'Version' : (gtk.CellRendererText, str, lambda i, s: "%d.%d.%d" % s.version),
131        'Path' : (gtk.CellRendererText, str, lambda i, s: s.path),
132        'URL' : (gtk.CellRendererText, str, lambda i, s: s.url),
133        'Size' : (gtk.CellRendererText, int, lambda i, s: s.size),
134        'MD5' : (gtk.CellRendererText, str, lambda i, s: s.md5),
135        'SHA1' : (gtk.CellRendererText, str, lambda i, s: s.sha1),
136        'GPG' : (gtk.CellRendererText, str, lambda i, s: s.gpg)
137        }
138   
139    def __init__(self):
140        ScriptWindow.__init__(self)
141
142        self.preferences = NSEManagerConfig()
143        # update to full column list
144        columns_order = self.preferences.columns_order
145        columns_order.extend(set(self.columns_dict.keys()) - set(columns_order))
146        self.preferences.columns_order = columns_order
147
148        self.config = ScriptConfig()
149        self.config.load()
150        self.base = ScriptBase(self.config)
151        self.base.load()
152        self.search_filter = None
153       
154        self.set_title(_("Script Manager"))
155        self.set_position(gtk.WIN_POS_CENTER)
156        self.set_size_request(600, 400)
157        self.create_widgets()
158        self.update_model(0)
159
160    def create_widgets(self):
161        # ui_manager
162        actions = [
163            ('Script', None, _('_Script'), None),
164
165            ('New', gtk.STOCK_NEW,
166             _('New'), "<Ctrl>N",
167             _('Create new script file'), self._new_file_cb),
168
169            ('New Wizard', gtk.STOCK_NEW,
170             _('_New Wizard...'), "",
171             _('Create new script file with wizard'), self._new_wizard_file_cb),
172
173            ('Install', gtk.STOCK_CONVERT,
174             _('_Install'), "<Control>I",
175             _('Install script from site'), self._install_cb),
176
177            ('Upgrade', gtk.STOCK_EXECUTE,
178             _('_Upgrade'), "<Control>U",
179             _('Upgrade script to newest version'), self._upgrade_cb),
180
181            ('Remove', gtk.STOCK_DELETE,
182             _('_Remove'), "<Control>R",
183             _('Remove script from computer'), self._remove_cb),
184
185            ('Install All', gtk.STOCK_CONVERT,
186             _('Install All'), None,
187             _('Install all new scripts from site'), self._install_all_cb),
188
189            ('Upgrade All', gtk.STOCK_EXECUTE,
190             _('Upgrade All'), None,
191             _('Upgrade all script to newest versions'), self._upgrade_all_cb),
192
193            ('Tools', None, _('_Tools'), None),
194
195            ('Add Source', gtk.STOCK_ADD,
196             _('_Add Source...'), "<Control>A",
197             _('Add new source to configuration'), self._add_source_cb),
198
199            ('Quit', gtk.STOCK_QUIT,
200             _('_Quit'), "<Ctrl>Q",
201             _('Quit from Script Manager'), self._quit_cb),
202           
203            ('Edit Templates', gtk.STOCK_PREFERENCES,
204             _('_Edit Templates'), None,
205             _('Edit User Templates'), self._templates_cb),
206
207            ('View', None, _('_View'), None),
208
209            ('Settings', None, _('Se_ttings'), None),
210
211            ('Reload', gtk.STOCK_REFRESH,
212             _('_Reload'), "<Ctrl>R",
213             _('Reload scripts list'), self._reload_cb),
214
215            ('Search', gtk.STOCK_FIND,
216             _('_Search'), "<Ctrl>S",
217             _('Search necessory scripts'), self._search_cb),
218
219            ('Edit', gtk.STOCK_EDIT,
220             _('_Edit'), "<Ctrl>E",
221             _('Edit selected script'), self._edit_cb),
222
223            ('Preferences', gtk.STOCK_PREFERENCES,
224             _('_Preferences'), "<Ctrl>P",
225             _('Script Manager settings'), self._preferences_cb),
226           
227            ('Help', None, _('_Help'), None),
228
229            ('Report a bug', gtk.STOCK_DIALOG_INFO,
230             _('_Report a bug'), "<Ctrl>b",
231             _('Report a bug'), self._show_bug_report_cb),
232
233            ('Show Help', gtk.STOCK_HELP,
234             _('_Help'), None,
235             _('Show the application help'), self._show_help_cb)
236           
237        ]
238
239        toggle_actions = [
240            ('Toolbar', None,
241             _('Toolbar'), None,
242             _('Show/hide toolbar'), self._toolbar_cb, True),
243
244            ('Statusbar', None,
245             _('Statusbar'), None,
246             _('Show/hide statusbar'), self._statusbar_cb, True),
247
248            ('Categories', None,
249             _('Categories'), None,
250             _('Show/hide Categories sidebar'), self._categories_cb, True),
251
252            ('Description', None,
253             _('Description'), None,
254             _('Show/hide Description sidebar'), self._description_cb, True)
255        ]
256
257        ui = """<menubar>
258        <menu action='Script'>
259            <menuitem action='Install'/>
260            <menuitem action='Upgrade'/>
261            <menuitem action='Remove'/>
262            <separator/>
263            <menuitem action='New'/>
264            <menuitem action='New Wizard'/>
265            <menuitem action='Edit'/>
266            <separator/>
267            <menuitem action='Quit'/>
268        </menu>
269        <menu action='Tools'>
270            <menuitem action='Reload'/>
271            <menuitem action='Install All'/>
272            <menuitem action='Upgrade All'/>
273            <separator/>
274            <menuitem action='Search'/>
275            <separator/>
276            <menuitem action='Add Source'/>
277            <separator/>
278            <menuitem action='Edit Templates'/>
279        </menu>
280        <menu action='View'>
281            <menuitem action='Categories'/>
282            <menuitem action='Description'/>
283            <menuitem action='Toolbar'/>
284            <menuitem action='Statusbar'/>
285        </menu>
286        <menu action='Settings'>
287            <menuitem action='Preferences'/>
288        </menu>
289        <menu action='Help'>
290            <menuitem action='Show Help'/>
291            <menuitem action='Report a bug'/>
292        </menu>
293        </menubar>
294
295        <toolbar>
296            <toolitem action='Reload'/>
297            <toolitem action='Install All'/>
298            <toolitem action='Upgrade All'/>
299            <separator/>
300            <toolitem action='Search'/>
301            <toolitem action='Edit'/>
302            <separator/>
303            <toolitem action='Preferences'/>
304        </toolbar>
305        """
306
307        self.create_layout(actions, toggle_actions, ui)
308
309        self.action_group.get_action('Categories').set_active(self.preferences.view_categories)
310        self.action_group.get_action('Description').set_active(self.preferences.view_description)
311        self.action_group.get_action('Toolbar').set_active(self.preferences.view_toolbar)
312        self.action_group.get_action('Statusbar').set_active(self.preferences.view_statusbar)
313
314        # popup menu
315        self.popup_menu = gtk.Menu()
316        install_item = gtk.ImageMenuItem(_("Install"))
317        install_item.set_image(gtk.image_new_from_stock(gtk.STOCK_CONVERT, gtk.ICON_SIZE_MENU))
318        install_item.connect("activate", self._install_cb)
319        self.popup_menu.append(install_item)
320        upgrade_item = gtk.ImageMenuItem(_("Upgrade"))
321        upgrade_item.set_image(gtk.image_new_from_stock(gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU))
322        upgrade_item.connect("activate", self._upgrade_cb)
323        self.popup_menu.append(upgrade_item)
324        remove_item = gtk.ImageMenuItem(_("Remove"))
325        remove_item.set_image(gtk.image_new_from_stock(gtk.STOCK_DELETE, gtk.ICON_SIZE_MENU))
326        remove_item.connect("activate", self._remove_cb)
327        self.popup_menu.append(remove_item)
328        # TODO: Properties dialog
329        #self.popup_menu.append(gtk.SeparatorMenuItem())
330        #properties_item = gtk.ImageMenuItem(_("Properties"))
331        #properties_item.set_image(gtk.image_new_from_stock(gtk.STOCK_PROPERTIES, gtk.ICON_SIZE_MENU))
332        #self.popup_menu.append(properties_item)
333        self.popup_menu.show_all()
334
335    def show_all(self):
336        ScriptWindow.show_all(self)
337        (self.categories_scroll.hide,
338         self.categories_scroll.show)[self.preferences.view_categories]()
339        (self.desc_scroll.hide,
340         self.desc_scroll.show)[self.preferences.view_description]()
341        (self.toolbar.hide,
342         self.toolbar.show)[self.preferences.view_toolbar]()
343        (self.statusbar.hide,
344         self.statusbar.show)[self.preferences.view_statusbar]()
345       
346    def create_categories(self):
347        self.categories_model = gtk.ListStore(object, str, int)
348        self.categories_view = gtk.TreeView(self.categories_model)
349        self.categories_model.set_sort_column_id(2, gtk.SORT_ASCENDING)
350        self.categories_view.set_headers_visible(False)
351        cell = gtk.CellRendererText()
352        col = gtk.TreeViewColumn('Category', cell, markup=1)
353        self.categories_view.append_column(col)
354        self.categories_view.set_search_column(1)
355        self.categories_view.connect('cursor-changed', self._select_category_cb)
356        self.categories_view.set_size_request(100, -1)
357        self.categories_scroll = scroll_wrap(self.categories_view)
358        return self.categories_scroll
359
360    def create_list(self):
361        self.list_model = gtk.ListStore(object)
362        self.list_view = gtk.TreeView(self.list_model)
363        #self.list_view.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
364        self.list_view.set_property('rules-hint', True)
365        self.list_columns = []
366        self.list_view.connect('cursor-changed', self._select_script_cb)
367        self.list_view.connect('button-press-event', self._button_press_script_cb)
368        return scroll_wrap(self.list_view)
369
370    def create_desc(self):
371        self.desc_buffer = gtk.TextBuffer()
372        desc_view = gtk.TextView(self.desc_buffer)
373        desc_view.set_editable(False)
374        desc_view.set_wrap_mode(gtk.WRAP_WORD)
375        desc_view.set_size_request(-1, 100);
376        self.desc_scroll = scroll_wrap(desc_view)
377        return self.desc_scroll
378   
379    def create_main(self):
380        hpaned = gtk.HPaned()
381        hpaned.pack1(self.create_categories(), False)
382        vpaned = gtk.VPaned()
383        vpaned.pack1(self.create_list(), True)
384        vpaned.pack2(self.create_desc(), False)
385        hpaned.pack2(vpaned, True)
386        return hpaned
387
388    def update_model(self, category = -1):
389        self.update_categories(category)
390        self.update_columns()
391        self.update_list(self.get_selected_filter())
392
393    def update_columns(self):
394        for col in self.list_columns:
395            self.list_view.remove_column(col)
396        self.list_columns = []
397        model_list = [object]
398        i = 1
399        pixbuf_id = None
400        search_id = None
401        for col in self.preferences.columns_order:
402            if col in self.preferences.columns_visible:
403                col_data = self.columns_dict[col]
404                cell = col_data[0]()
405                if col_data[0] == gtk.CellRendererToggle:
406                    column = gtk.TreeViewColumn(col, cell, active=i)
407                elif col_data[0] == gtk.CellRendererPixbuf:
408                    column = gtk.TreeViewColumn(col, cell, icon_name=i)
409                    pixbuf_id = i
410                else:
411                    if not search_id:
412                        search_id = i
413                    column = gtk.TreeViewColumn(col, cell, text=i)
414                column.set_sort_column_id(i)
415                self.list_view.append_column(column)
416                self.list_columns.append(column)
417                i += 1
418                model_list.append(col_data[1])
419        self.list_model = gtk.ListStore(*tuple(model_list))
420        self.list_view.set_model(self.list_model)
421        self.list_view.set_headers_visible(True)
422        self.list_model.set_sort_column_id(1, gtk.SORT_ASCENDING)
423        if search_id:
424            self.list_view.set_search_column(search_id)
425        if pixbuf_id:
426            self.list_model.set_sort_func(pixbuf_id, _status_pixbuf.compare, pixbuf_id)
427
428    def append_filter(self, i, filter_):
429        self.categories_model.append((filter_, filter_.get_name(), i))
430       
431    def update_categories(self, selected = -1):
432        self.categories_model.clear()
433        self.append_filter(0, AllFilter())
434        shift = 1
435        if self.search_filter:
436            self.append_filter(1, self.search_filter)
437            shift += 1
438        for i, name in enumerate(sorted(self.base.categories)):
439            self.append_filter(i + shift, CategoryFilter(name))
440        if selected == -1:
441            self.update_list(NoneFilter())
442        else:
443            self.categories_view.get_selection().select_path((selected,))
444
445    def update_list(self, filter_):
446        self.list_model.clear()
447        for item in self.base.get_script_items():
448            s = item.get_last_installed()
449            if s is None:
450                s = item.get_last_version()
451            if filter_(s):
452                model_list = [(item, s)]
453                for col in self.preferences.columns_order:
454                    if col in self.preferences.columns_visible:
455                        col_data = self.columns_dict[col]
456                        model_list.append(col_data[2](item, s))
457                self.list_model.append(tuple(model_list))
458        self.update_desc(None)
459        self.update_statusbar()
460
461    def update_statusbar(self):
462        context = self.statusbar.get_context_id("statusbar")
463        self.statusbar.pop(context)
464        self.statusbar.push(context, _("%d scripts listened") % len(self.list_model))
465
466    _state_choose = {
467        #state -> (Installable, Upgradable, Removable)
468        ScriptItem.STATE_NOT_INSTALLED: (True, False, False),
469        ScriptItem.STATE_UPGRADABLE: (False, True, True),
470        ScriptItem.STATE_INSTALLED: (False, False, True)
471        }
472   
473    def update_avaliables(self):
474        item = self.get_selected_item()
475        if not item:
476            res = (False, False, False)
477        else:
478            res = self._state_choose[item.get_state()]
479        self.action_group.get_action('Install').set_sensitive(res[0])
480        self.action_group.get_action('Upgrade').set_sensitive(res[1])
481        self.action_group.get_action('Remove').set_sensitive(res[2])
482
483    def update_desc(self, script):
484        self.action_group.get_action('Edit').set_sensitive(bool(script))
485        self.update_avaliables()
486        if script:
487            self.desc_buffer.set_text(script.description)
488        else:
489            self.desc_buffer.set_text("")
490
491    def get_selected_item(self):
492        (model, it) = self.list_view.get_selection().get_selected()
493        if not it:
494            return None
495        return self.list_model[it][0][0]
496       
497    def get_selected_script(self):
498        (model, it) = self.list_view.get_selection().get_selected()
499        if not it:
500            return None
501        return self.list_model[it][0][1]
502
503    def get_selected_filter(self):
504        (model, it) = self.categories_view.get_selection().get_selected()
505        if not it:
506            return NoneFilter()
507        return self.categories_model[it][0]
508
509    def _new_wizard_file_cb(self, p):
510        editor = ScriptEditorWindow()
511        editor.new_file_wizard()
512        editor.show_all()
513
514    def _new_file_cb(self, p):
515        editor = ScriptEditorWindow()
516        editor.open_file()
517        editor.show_all()
518
519    def _install_cb(self, p):
520        script = self.get_selected_script()
521        if script:
522            self.base.install(script.name)
523            self.update_list(self.get_selected_filter())
524            self.base.save()
525           
526    def _upgrade_cb(self, p):
527        script = self.get_selected_script()
528        if script:
529            self.base.upgrade(script.name)
530            self.update_list(self.get_selected_filter())
531            self.base.save()
532
533    def _remove_cb(self, p):
534        script = self.get_selected_script()
535        if script:
536            self.base.remove(script.name)
537            self.update_list(self.get_selected_filter())
538            self.base.save()
539   
540    def _install_all_cb(self, p):
541        self.base.install_all()
542        self.update_list(self.get_selected_filter())
543        self.base.save()
544
545    def _upgrade_all_cb(self, p):
546        self.base.upgrade_all()
547        self.update_list(self.get_selected_filter())
548        self.base.save()
549   
550    def _add_source_cb(self, p):
551        dialog = AddSourceDialog()
552        result = dialog.run()
553        dialog.destroy()
554        if result == gtk.RESPONSE_OK:
555            result = dialog.get_result()
556            if result:
557                (type_, path) = result
558                self.config.add_item(type_, path)
559                self.config.save()
560
561    def _toolbar_cb(self, toggle):
562        (self.toolbar.hide, self.toolbar.show)[toggle.get_active()]()
563        self.preferences.view_toolbar = toggle.get_active()
564
565    def _statusbar_cb(self, toggle):
566        (self.statusbar.hide, self.statusbar.show)[toggle.get_active()]()
567        self.preferences.view_statusbar = toggle.get_active()
568
569    def _categories_cb(self, toggle):
570        if toggle.get_active():
571            self.update_list(self.get_selected_filter())
572            self.categories_scroll.show()
573            self.preferences.view_categories = True
574        else:
575            self.update_list(AllFilter())
576            self.categories_scroll.hide()
577            self.preferences.view_categories = False
578
579    def _description_cb(self, toggle):
580        if toggle.get_active():
581            self.update_desc(self.get_selected_script())
582            self.desc_scroll.show()
583            self.preferences.view_description = True
584        else:
585            self.desc_scroll.hide()
586            self.preferences.view_description = False
587
588    def _reload_cb(self, p):
589        self.base.reload()
590        self.base.save()
591        self.update_model(0)
592        # TODO: testing and bugfixing
593        #progress = ScriptManagerProgressWindow(self)
594        #progress.show_all()
595
596    def _search_cb(self, p):
597        dialog = SearchFilterDialog(self)
598        if dialog.run() == gtk.RESPONSE_OK:
599            self.search_filter = SearchFilter(dialog.get_pattern())
600            self.update_model(1)
601        dialog.destroy()
602   
603    def _edit_cb(self, p):
604        script = self.get_selected_script()
605        if script and script.path:
606            if self.preferences.use_internal_editor:
607                editor = ScriptEditorWindow()
608                editor.open_file(script.path)
609                editor.show_all()
610            else:
611                # XXX : find better way to run shell command from Python
612                if not os.fork():
613                    os.system(self.preferences.external_command % script.path)
614                    sys.exit(0)
615
616    def _preferences_cb(self, p):
617        dialog = ScriptManagerPreferencesDialog(self)
618        dialog.run()
619        dialog.destroy()
620        #self.preferences.save_changes()
621        self.config.save()
622        self.update_columns()
623        self.update_list(self.get_selected_filter())
624
625    def _show_bug_report_cb(self, p):
626        BugReport().show_all()
627
628    def _show_help_cb(self, p):
629        import webbrowser
630        webbrowser.open("file://%s" % os.path.join(Path.docs_dir, "nse_facilitator.html#script_manager"), new=2)
631
632    def _select_category_cb(self, categories_view):
633        self.update_list(self.get_selected_filter())
634
635    def _button_press_script_cb(self, list_view, e):
636        if e.type != gtk.gdk.BUTTON_PRESS or e.button != 3:
637            return
638        path = self.list_view.get_path_at_pos(int(e.x), int(e.y))[0]
639        item = self.list_model[path][0][0]
640        state = self._state_choose[item.get_state()]
641        for menu_item, sensitive in zip(self.popup_menu.get_children(), state):
642            menu_item.set_sensitive(sensitive)
643        self.popup_menu.popup(None, None, None, e.button, e.get_time())
644       
645    def _select_script_cb(self, list_view):
646        self.update_desc(self.get_selected_script())
647   
648    def _quit_cb(self, p):
649        self.destroy()
650       
651    def _templates_cb(self, p):
652        dialog = ScriptManagerTemplatesDialog(self)
653        dialog.run()
654        dialog.destroy()
655
656class ScriptManagerReloadThread(threading.Thread):
657    def __init__(self, base):
658        gtk.gdk.threads_init()
659        self.event = threading.Event()
660        threading.Thread.__init__(self)
661        self.base = base
662        self.status = (None, None, None)
663
664    def run(self):
665        self.base.reload(callback=self.callback_impl)
666        #gtk.gdk.threads_enter()
667        #for i in range(100):
668        #    print i
669        #gtk.gdk.threads_leave()
670
671    def callback_impl(self, src, all, current):
672        self.status = (src, all, current)
673        print "cb_impl:", self.status
674        return not self.event.isSet()
675       
676    def join(self, timeout=None):
677        self.event.set()
678        threading.Thread.join(self, timeout)
679
680def reload_thread(base, progress, lock):
681    def callback_impl(src, all, current):
682        progress.status = (src, all, current)
683        print "cb_impl:", progress.status
684        return not lock.locked()
685    base.reload(callback_impl)
686   
687class ScriptManagerProgressWindow(HIGWindow):
688    def __init__(self, parent):
689        HIGWindow.__init__(self)
690        self.set_title(_("Script Manager Progress"))
691        self.set_position(gtk.WIN_POS_CENTER)
692        self.set_size_request(300, 200)
693        self.vbox = HIGVBox()
694        self.label = gtk.Label()
695        self.vbox.pack_start(self.label)
696        self.progress_all = HIGLabeledProgressBar(_("Overall progress"))
697        self.vbox.pack_start(self.progress_all)
698        self.progress_current = HIGLabeledProgressBar(_("Current source"))
699        self.vbox.pack_start(self.progress_current)
700        self.btn_cancel = HIGButton(stock=gtk.STOCK_CANCEL)
701        self.btn_cancel.connect("clicked", self._cancel_cb)
702        self.vbox.pack_start(self.btn_cancel)
703        self.add(self.vbox)
704        self.show_all()
705        self.timeout_id = gobject.timeout_add(100, self.callback)
706        self.status = (None, None, None)
707        import thread
708        self.lock = thread.allocate_lock()
709        reload_thread(parent.base, self, self.lock)
710        #thread.start_new_thread(reload_thread, (parent.base, self, self.lock))
711        #self.thread = ScriptManagerReloadThread(parent.base)
712        #self.thread.start()
713
714    def _cancel_cb(self, widget):
715        if self.timeout_id:
716            gobject.source_remove(self.timeout_id)
717        self.lock.acquire()
718        self.destroy()
719       
720    def callback(self):
721        src, all, current = self.status
722        print "cb:", self.status
723        if src:
724            self.label.set_text(src.path)
725        if all:
726            if all[0] == all[1]:
727                gobject.source_remove(self.timeout_id)
728                self.timeout_id = None
729            self.progress_all.progress_bar.set_fraction(float(all[0])/all[1])
730            self.progress_all.label.set_text("%d/%d" % all)
731        else:
732            self.progress_all.progress_bar.set_fraction(0)
733            self.progress_all.label.set_text("")
734        if current:
735            self.progress_current.progress_bar.set_fraction(float(current[0])/current[1])
736            self.progress_current.label.set_text("%d/%d" % current)
737        else:
738            self.progress_current.progress_bar.set_fraction(0)
739            self.progress_current.label.set_text("")
740
741class SearchFilterDialog(HIGDialog):
742    def __init__(self, parent = None):
743        HIGDialog.__init__(self, _("Find"), parent,
744                           gtk.DIALOG_MODAL | gtk.DIALOG_NO_SEPARATOR,
745                           (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
746                            gtk.STOCK_FIND, gtk.RESPONSE_OK))
747        self.set_default_response(gtk.RESPONSE_OK)
748        hbox = HIGHBox()
749        hbox.pack_start(gtk.Label(_("Search:")))
750        self.entry = HIGTextEntry()
751        self.entry.set_activates_default(True)
752        hbox.pack_start(self.entry)
753        self.vbox.add(hbox)
754        self.show_all()
755
756    def get_pattern(self):
757        return self.entry.get_text()
758
759class AddSourceDialog(HIGDialog):
760    def __init__(self, type_ = None, path = None):
761        if type_ and path:
762            title = _("Edit Source")
763        else:
764            type_ = "FILE"
765            path = ""
766            title = _("Add Source")
767        HIGDialog.__init__(self, title, None,
768                           gtk.DIALOG_MODAL,
769                           (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
770                            gtk.STOCK_OK, gtk.RESPONSE_OK))
771        hbox = HIGHBox()
772        self.combo = gtk.combo_box_new_text()
773        types = sorted(self.types.keys())
774        for t in types:
775            self.combo.append_text(t)
776        self.combo.set_active(types.index(type_))
777        self.combo.connect("changed", self._changed_cb)
778        hbox.pack_start(self.combo, False, False)
779        self.entry = HIGTextEntry()
780        self.entry.set_text(path)
781        hbox.pack_start(self.entry)
782        self.btn = HIGButton(_("Browse..."), stock=gtk.STOCK_OPEN)
783        self.btn.connect("clicked", self._clicked_cb)
784        hbox.pack_start(self.btn, False, False)
785        self.vbox.add(hbox)
786        self.show_all()
787        self.update()
788
789    def update(self):
790         callback, = self.types[self.combo.get_active_text()]
791         self.btn.set_sensitive(not callback is None)
792
793    def _browse_file(self, p):
794        file_chooser = AllFilesFileChooserDialog(_("Select Script"))
795        response = file_chooser.run()
796        filename = file_chooser.get_filename()
797        file_chooser.destroy()
798        if not response == gtk.RESPONSE_OK:
799            return
800        self.entry.set_text(filename)
801
802    def _browse_folder(self, p):
803        file_chooser = DirectoryChooserDialog(_("Select Scripts Directory"))
804        response = file_chooser.run()
805        dirname = file_chooser.get_filename()
806        file_chooser.destroy()
807        if not response == gtk.RESPONSE_OK:
808            return False
809        self.entry.set_text(dirname)
810
811    types = {
812        "FILE" : (_browse_file, ),
813        "DIR" : (_browse_folder, ),
814        "URL" : (None, ), 
815        "URLBASE" : (None, ),
816        "INSTALLDIR" : (_browse_folder, )
817    }
818
819    def _clicked_cb(self, p):
820         callback, = self.types[self.combo.get_active_text()]
821         if callback:
822             callback(self, p)
823
824    def _changed_cb(self, p):
825        self.update()
826
827    def get_result(self):
828        (type_, path) = (self.combo.get_active_text(), self.entry.get_text())
829        if type_ not in ('FILE', 'DIR'):
830            return (type_, path)
831        # test existence
832        if os.path.exists(path):
833            return (type_, path)
834        else:
835            message = gtk.MessageDialog(type=gtk.MESSAGE_QUESTION,
836                                        buttons=gtk.BUTTONS_YES_NO,
837                                        message_format=_("The path don't exists. Append it?"))
838            message.set_title(_("No such path"))
839            response = message.run()
840            message.destroy()
841            if response == gtk.RESPONSE_YES:
842                return (type_, path)
843        return None
844       
845class ScriptManagerPreferencesDialog(HIGDialog):
846    def __init__(self, parent):
847        HIGDialog.__init__(self, _("Script Manager Preferences"), parent,
848                           gtk.DIALOG_MODAL,
849                           (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE,
850                            gtk.STOCK_HELP, gtk.RESPONSE_HELP))
851        self.config = parent.config
852        self.preferences = parent.preferences
853        self.set_size_request(450, 400)
854        self.create_widgets()
855        self.update_model()
856        self.connect("destroy", self.update_preferences)
857
858    def create_src_list(self):
859        # list
860        self.config_model = gtk.ListStore(str, str)
861        self.config_view = gtk.TreeView(self.config_model)
862        self.config_view.set_headers_visible(True)
863
864        cell = gtk.CellRendererText()
865        col = gtk.TreeViewColumn(_('Type'), cell, text=0)
866        self.config_view.append_column(col)
867
868        cell = gtk.CellRendererText()
869        col = gtk.TreeViewColumn(_('Path'), cell, text=1)
870        self.config_view.append_column(col)
871       
872        self.config_view.set_search_column(1)
873        return scroll_wrap(self.config_view)
874   
875    def create_tab_catalog(self):
876        mainvbox = HIGVBox()
877        mainvbox.set_border_width(8)
878        mainvbox.pack_start(HIGSectionLabel(_("Script Sources")), False, False)
879       
880        hbox = HIGHBox()
881        hbox.pack_start(self.create_src_list(), True, True)
882       
883        # buttons
884        vbox = HIGVBox()
885
886        btn = HIGButton(_("Add Source..."), gtk.STOCK_ADD)
887        vbox.pack_start(btn, False, False)
888        btn.connect("clicked", self._add_source_cb)
889
890        btn = HIGButton(_("Edit..."), gtk.STOCK_EDIT)
891        vbox.pack_start(btn, False, False)
892        btn.connect("clicked", self._edit_source_cb)
893       
894        btn = HIGButton(_("Remove"), gtk.STOCK_REMOVE)
895        vbox.pack_start(btn, False, False)
896        btn.connect("clicked", self._remove_cb)
897
898        hbox.pack_start(vbox, False, False)
899
900        mainvbox.pack_start(hbox)
901        return mainvbox
902
903    def create_tab_editor(self):
904        mainvbox = HIGVBox()
905        mainvbox.set_border_width(8)
906        mainvbox.pack_start(HIGSectionLabel(_("Text Editor")), False, False)
907
908        vbox = HIGVBox()
909
910        self.btn_internal = gtk.RadioButton(None, _("Internal Editor"))
911        vbox.pack_start(self.btn_internal, False, False)
912
913        self.btn_external = gtk.RadioButton(self.btn_internal, _("External Editor"))
914        vbox.pack_start(self.btn_external, False, False)
915
916        self.external_cmd = gtk.Entry()
917        self.external_cmd.set_text(self.preferences.external_command)
918        vbox.pack_start(HIGSpacer(self.external_cmd), False, False)
919
920        self.btn_external.connect("toggled",
921                                  lambda b: self.external_cmd.set_sensitive(b.get_active()))
922
923        if self.preferences.use_internal_editor:
924            self.btn_internal.set_active(True)
925            self.external_cmd.set_sensitive(False)
926        else:
927            self.btn_external.set_active(True)
928            self.external_cmd.set_sensitive(True)
929       
930        mainvbox.pack_start(HIGSpacer(vbox), False, False)
931        return mainvbox
932
933    def create_columns_list(self):
934        self.columns_model = gtk.ListStore(bool, str)
935        self.columns_view = gtk.TreeView(self.columns_model)
936        self.columns_view.set_headers_visible(True)
937
938        cell = gtk.CellRendererToggle()
939        col = gtk.TreeViewColumn(_('Visible'), cell, active=0)
940        cell.connect('toggled', self._toggled_cb, self.columns_model)
941        self.columns_view.append_column(col)
942
943        cell = gtk.CellRendererText()
944        col = gtk.TreeViewColumn(_('Name'), cell, text=1)
945        self.columns_view.append_column(col)
946       
947        self.columns_view.set_search_column(1)
948        return scroll_wrap(self.columns_view)
949   
950    def create_tab_columns(self):
951        mainvbox = HIGVBox()
952        mainvbox.set_border_width(8)
953        mainvbox.pack_start(HIGSectionLabel(_("Columns")), False, False)
954       
955        hbox = HIGHBox()
956        hbox.pack_start(self.create_columns_list(), True, True)
957       
958        # buttons
959        vbox = HIGVBox()
960
961        btn = HIGButton(_("Move Up"), gtk.STOCK_GO_UP)
962        vbox.pack_start(btn, False, False)
963        btn.connect("clicked", self._move_up_down, -1)
964
965        btn = HIGButton(_("Move Down"), gtk.STOCK_GO_DOWN)
966        vbox.pack_start(btn, False, False)
967        btn.connect("clicked", self._move_up_down, 1)
968
969        hbox.pack_start(vbox, False, False)
970       
971        mainvbox.pack_start(hbox)
972        return mainvbox
973       
974    def create_tab_network(self):
975        def parse_proxy(s):
976            return (s + ":").split(":")[0:2]
977       
978        def create_proxy_row(table, level, name, default = ""):
979            host, port = parse_proxy(default)
980            try:
981                port = float(port)
982            except ValueError:
983                port = 3128
984
985            lbl = HIGEntryLabel(name + " " + _("proxy") + ":")
986            lbl.set_justify(gtk.JUSTIFY_LEFT)
987            host_entry = HIGTextEntry()
988            host_entry.set_text(host)
989            lbl.set_mnemonic_widget(host_entry)
990            table.attach(lbl, 0, 1, level, level + 1, 0, 0)
991            table.attach(host_entry, 1, 2, level, level + 1)
992           
993            lbl = HIGEntryLabel(_("Port") + ":")
994            lbl.set_justify(gtk.JUSTIFY_LEFT)
995            port_entry = gtk.SpinButton(gtk.Adjustment(port, 0, 65536, 1, 100), 1.0, 0)
996            lbl.set_mnemonic_widget(port_entry)
997            table.attach(lbl, 2, 3, level, level + 1, 0, 0)
998            table.attach(port_entry, 3, 4, level, level + 1, 0, 0)
999            return host_entry, port_entry
1000           
1001        proxies = self.config.get_proxies()
1002       
1003        mainvbox = HIGVBox()
1004        mainvbox.set_border_width(8)
1005        mainvbox.pack_start(HIGSectionLabel(_("Proxy Server")), False, False)
1006
1007        vbox = HIGVBox()
1008       
1009        self.no_proxy_radio = gtk.RadioButton(None, _("Direct connection to the Internet"))
1010        vbox.pack_start(self.no_proxy_radio, False, False)
1011        self.proxy_radio = gtk.RadioButton(self.no_proxy_radio, _("Manual proxy configuration"))
1012        vbox.pack_start(self.proxy_radio, False, False)
1013        if proxies:
1014            http_proxy = proxies.get('http', ":3128")
1015            ftp_proxy = proxies.get('ftp', ":3128") 
1016        else:
1017            http_proxy = self.preferences.http_proxy
1018            ftp_proxy = self.preferences.ftp_proxy
1019
1020        self.proxy_table = HIGTable(2, 4)
1021        self.http_host, self.http_port = create_proxy_row(self.proxy_table, 0, "HTTP", http_proxy)
1022        self.ftp_host, self.ftp_port = create_proxy_row(self.proxy_table, 1, "FTP", ftp_proxy)
1023        vbox.pack_start(HIGSpacer(self.proxy_table), False, False)
1024
1025        if proxies:
1026            self.proxy_radio.set_active(True)
1027            self.proxy_table.set_sensitive(True)
1028        else:
1029            self.no_proxy_radio.set_active(True)
1030            self.proxy_table.set_sensitive(False)
1031
1032        self.proxy_radio.connect("toggled",
1033                                 lambda b: self.proxy_table.set_sensitive(b.get_active()))
1034       
1035        mainvbox.pack_start(HIGSpacer(vbox), False, False)
1036        return mainvbox
1037   
1038    def create_widgets(self):
1039        notebook = HIGNotebook()
1040        notebook.set_tab_pos(gtk.POS_TOP)
1041        notebook.append_page(self.create_tab_catalog(), gtk.Label(_("Sources")))
1042        notebook.append_page(self.create_tab_editor(), gtk.Label(_("Editor")))
1043        notebook.append_page(self.create_tab_columns(), gtk.Label(_("Columns")))
1044        notebook.append_page(self.create_tab_network(), gtk.Label(_("Network")))
1045        self.vbox.add(notebook)
1046        self.connect("response", self._response_cb)
1047        self.show_all()
1048
1049    def update_model(self):
1050        self.config_model.clear()
1051        for src in self.config.get_sources():
1052            self.config_model.append((src.type, src.path))
1053
1054        self.columns_model.clear()
1055        for col in self.preferences.columns_order:
1056            self.columns_model.append((col in self.preferences.columns_visible, col))
1057
1058    def update_preferences(self, config):
1059        order = []
1060        visible = []
1061        for raw in self.columns_model:
1062            order.append(raw[1])
1063            if raw[0]:
1064                visible.append(raw[1])
1065        self.preferences.use_internal_editor = self.btn_internal.get_active()
1066        self.preferences.external_command = self.external_cmd.get_text()
1067        self.preferences.columns_order = order
1068        self.preferences.columns_visible = visible
1069        self.preferences.http_proxy = self.http_host.get_text() + ":" + self.http_port.get_text()
1070        self.preferences.ftp_proxy = self.ftp_host.get_text() + ":" + self.ftp_port.get_text()
1071        if self.proxy_radio.get_active():
1072            proxies = {'http' : self.preferences.http_proxy,
1073                       'ftp'  : self.preferences.ftp_proxy}
1074            self.config.set_proxies(proxies)
1075        else:
1076            self.config.set_proxies(dict())
1077
1078    def _add_source_cb(self, p):
1079        dialog = AddSourceDialog()
1080        result = dialog.run()
1081        dialog.destroy()
1082        if result == gtk.RESPONSE_OK:
1083            result = dialog.get_result()
1084            if result:
1085                (type_, path) = result
1086                self.config_model.append((type_, path))
1087                self.config.add_item(type_, path)
1088                self.config.save()
1089
1090    def _edit_source_cb(self, p):
1091        (model, it) = self.config_view.get_selection().get_selected()
1092        type_, path = model[it]
1093        dialog = AddSourceDialog(type_, path)
1094        result = dialog.run()
1095        dialog.destroy()
1096        if result == gtk.RESPONSE_OK:
1097            result = dialog.get_result()
1098            if result:
1099                self.config.remove_item(type_, path)
1100                (type_, path) = result
1101                model[it] = (type_, path)
1102                self.config.add_item(type_, path)
1103                self.config.save()
1104               
1105    def _remove_cb(self, p):
1106        (model, it) = self.config_view.get_selection().get_selected()
1107        type_, path = model[it]
1108        model.remove(it)
1109        self.config.remove_item(type_, path)
1110        self.config.save()
1111
1112    def _move_up_down(self, p, direction):
1113        (model, it) = self.columns_view.get_selection().get_selected()
1114        if not it:
1115            return None
1116        path = self.columns_model.get_path(it)
1117        path2 = (path[0] + direction, )
1118        if path2[0] >= 0 and path2[0] < len(self.columns_model):
1119            it2 = self.columns_model.get_iter((path[0] + direction,))
1120            self.columns_model.swap(it, it2)
1121
1122    def _toggled_cb(self, cell, path, model):
1123        model[path][0] = not model[path][0]
1124
1125    def _response_cb(self, dialog, response_id):
1126        if response_id != gtk.RESPONSE_HELP:
1127            return
1128        import webbrowser
1129        webbrowser.open("file://%s" %
1130                        os.path.join(Path.docs_dir, "nse_facilitator.html#preferences"), new=2)
1131        self.stop_emission("response")
1132       
1133       
1134class ScriptManagerTemplatesDialog(HIGDialog):
1135    def __init__(self, parent):
1136        HIGDialog.__init__(self, _("Script Manager User Templates"), parent,
1137                           gtk.DIALOG_MODAL,
1138                           (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
1139        self.set_size_request(450, 400)
1140        self.create_widgets()
1141       
1142    def create_widgets(self):
1143        sw = gtk.ScrolledWindow()
1144        sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
1145        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
1146       
1147        self.store = gtk.ListStore(str) 
1148        self.update_model()
1149        self.treeView = gtk.TreeView(self.store)
1150        #treeView.connect("row-activated", self.on_activated)
1151        #treeView.set_rules_hint(True)
1152        self.create_columns(self.treeView)
1153        sw.add(self.treeView)
1154               
1155        # buttons
1156        vbox = HIGVBox()
1157
1158        btn = HIGButton(_("Edit..."), gtk.STOCK_EDIT)
1159        vbox.pack_start(btn, False, False)
1160        btn.connect("clicked", self._edit_template)
1161       
1162        btn = HIGButton(_("Remove"), gtk.STOCK_REMOVE)
1163        vbox.pack_start(btn, False, False)
1164        btn.connect("clicked", self._remove_template)
1165       
1166        hbox = HIGHBox()
1167        hbox.pack_start(sw, True, True)
1168        hbox.pack_start(vbox, False, False)
1169        self.vbox.pack_start(hbox, True, True, 0)
1170        self.show_all() 
1171       
1172    def update_model(self):
1173        self.store.clear()
1174        for template in get_templates():
1175            self.store.append([template])
1176   
1177    def create_columns(self, treeView):   
1178        rendererText = gtk.CellRendererText()
1179        column = gtk.TreeViewColumn("Template Name", rendererText, text=0)
1180        column.set_sort_column_id(0)   
1181        treeView.append_column(column)
1182       
1183    def get_selected_template(self):
1184        (model, it) = self.treeView.get_selection().get_selected()
1185        if not it:
1186            return None
1187        return self.store[it][0]
1188       
1189    def _edit_template(self, p):
1190        template = self.get_selected_template()
1191        if template:
1192            template_path = DEFAULT_TEMPLATE_PATH + '/' + template
1193            editor = ScriptEditorWindow()
1194            editor.open_file(template_path)
1195            editor.show_all()
1196       
1197    def _remove_template(self, p):
1198        template = self.get_selected_template()
1199        if template:
1200            template_path = DEFAULT_TEMPLATE_PATH + '/' + template
1201            os.remove(template_path)
1202            self.update_model()
1203   
1204
1205if __name__ == "__main__":
1206    #sm = ScriptManagerWindow()
1207    #sm.show_all()
1208    #sm.connect("destroy", gtk.main_quit)
1209    conf = ScriptConfig()
1210    conf.load()
1211    base = ScriptBase(conf)
1212    thread = ScriptManagerReloadThread(base)
1213    thread.start()
1214    gtk.main()
1215
Note: See TracBrowser for help on using the browser.