1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3 4# ***********************IMPORTANT NMAP LICENSE TERMS************************ 5# * * 6# * The Nmap Security Scanner is (C) 1996-2020 Insecure.Com LLC ("The Nmap * 7# * Project"). Nmap is also a registered trademark of the Nmap Project. * 8# * * 9# * This program is distributed under the terms of the Nmap Public Source * 10# * License (NPSL). The exact license text applying to a particular Nmap * 11# * release or source code control revision is contained in the LICENSE * 12# * file distributed with that version of Nmap or source code control * 13# * revision. More Nmap copyright/legal information is available from * 14# * https://nmap.org/book/man-legal.html, and further information on the * 15# * NPSL license itself can be found at https://nmap.org/npsl. This header * 16# * summarizes some key points from the Nmap license, but is no substitute * 17# * for the actual license text. * 18# * * 19# * Nmap is generally free for end users to download and use themselves, * 20# * including commercial use. It is available from https://nmap.org. * 21# * * 22# * The Nmap license generally prohibits companies from using and * 23# * redistributing Nmap in commercial products, but we sell a special Nmap * 24# * OEM Edition with a more permissive license and special features for * 25# * this purpose. See https://nmap.org/oem * 26# * * 27# * If you have received a written Nmap license agreement or contract * 28# * stating terms other than these (such as an Nmap OEM license), you may * 29# * choose to use and redistribute Nmap under those terms instead. * 30# * * 31# * The official Nmap Windows builds include the Npcap software * 32# * (https://npcap.org) for packet capture and transmission. It is under * 33# * separate license terms which forbid redistribution without special * 34# * permission. So the official Nmap Windows builds may not be * 35# * redistributed without special permission (such as an Nmap OEM * 36# * license). * 37# * * 38# * Source is provided to this software because we believe users have a * 39# * right to know exactly what a program is going to do before they run it. * 40# * This also allows you to audit the software for security holes. * 41# * * 42# * Source code also allows you to port Nmap to new platforms, fix bugs, * 43# * and add new features. You are highly encouraged to submit your * 44# * changes as a Github PR or by email to the dev@nmap.org mailing list * 45# * for possible incorporation into the main distribution. Unless you * 46# * specify otherwise, it is understood that you are offering us very * 47# * broad rights to use your submissions as described in the Nmap Public * 48# * Source License Contributor Agreement. This is important because we * 49# * fund the project by selling licenses with various terms, and also * 50# * because the inability to relicense code has caused devastating * 51# * problems for other Free Software projects (such as KDE and NASM). * 52# * * 53# * The free version of Nmap is distributed in the hope that it will be * 54# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * 55# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, * 56# * indemnification and commercial support are all available through the * 57# * Npcap OEM program--see https://nmap.org/oem. * 58# * * 59# ***************************************************************************/ 60 61import gtk 62 63from zenmapGUI.higwidgets.higwindows import HIGWindow 64from zenmapGUI.higwidgets.higboxes import HIGVBox, HIGHBox, HIGSpacer, \ 65 hig_box_space_holder 66from zenmapGUI.higwidgets.higlabels import HIGSectionLabel, HIGEntryLabel 67from zenmapGUI.higwidgets.higscrollers import HIGScrolledWindow 68from zenmapGUI.higwidgets.higtextviewers import HIGTextView 69from zenmapGUI.higwidgets.higbuttons import HIGButton 70from zenmapGUI.higwidgets.higtables import HIGTable 71from zenmapGUI.higwidgets.higdialogs import HIGAlertDialog, HIGDialog 72from zenmapGUI.OptionBuilder import OptionBuilder 73from zenmapCore.Paths import Path 74from zenmapCore.UmitConf import CommandProfile 75from zenmapCore.UmitLogging import log 76import zenmapCore.I18N # lgtm[py/unused-import] 77from zenmapCore.NmapOptions import NmapOptions 78 79 80class ProfileEditor(HIGWindow): 81 def __init__(self, command=None, profile_name=None, 82 deletable=True, overwrite=False): 83 HIGWindow.__init__(self) 84 self.connect("delete_event", self.exit) 85 self.set_title(_('Profile Editor')) 86 self.set_position(gtk.WIN_POS_CENTER) 87 88 self.deletable = deletable 89 self.profile_name = profile_name 90 self.overwrite = overwrite 91 92 # Used to block recursive updating of the command entry when the 93 # command entry causes the OptionBuilder widgets to change. 94 self.inhibit_command_update = False 95 96 self.__create_widgets() 97 self.__pack_widgets() 98 99 self.profile = CommandProfile() 100 101 self.ops = NmapOptions() 102 if profile_name: 103 log.debug("Showing profile %s" % profile_name) 104 prof = self.profile.get_profile(profile_name) 105 106 # Interface settings 107 self.profile_name_entry.set_text(profile_name) 108 self.profile_description_text.get_buffer().set_text( 109 prof['description']) 110 111 command_string = prof['command'] 112 self.ops.parse_string(command_string) 113 if command: 114 self.ops.parse_string(command) 115 116 self.option_builder = OptionBuilder( 117 Path.profile_editor, self.ops, 118 self.update_command, self.help_field.get_buffer()) 119 log.debug("Option groups: %s" % str(self.option_builder.groups)) 120 log.debug("Option section names: %s" % str( 121 self.option_builder.section_names)) 122 #log.debug("Option tabs: %s" % str(self.option_builder.tabs)) 123 124 for tab in self.option_builder.groups: 125 self.__create_tab( 126 _(tab), 127 _(self.option_builder.section_names[tab]), 128 self.option_builder.tabs[tab]) 129 130 self.update_command() 131 132 def command_entry_changed_cb(self, widget): 133 command_string = self.command_entry.get_text().decode("UTF-8") 134 self.ops.parse_string(command_string) 135 self.inhibit_command_update = True 136 self.option_builder.update() 137 self.inhibit_command_update = False 138 139 def update_command(self): 140 """Regenerate and display the command.""" 141 if not self.inhibit_command_update: 142 # Block recursive updating of the OptionBuilder widgets when they 143 # cause a change in the command entry. 144 self.command_entry.handler_block(self.command_entry_changed_cb_id) 145 self.command_entry.set_text(self.ops.render_string()) 146 self.command_entry.handler_unblock( 147 self.command_entry_changed_cb_id) 148 149 def update_help_name(self, widget, extra): 150 self.help_field.get_buffer().set_text( 151 "Profile name\n\nThis is how the profile will be identified " 152 "in the drop-down combo box in the scan tab.") 153 154 def update_help_desc(self, widget, extra): 155 self.help_field.get_buffer().set_text( 156 "Description\n\nThe description is a full description of what " 157 "the scan does, which may be long.") 158 159 def __create_widgets(self): 160 161 ### 162 # Vertical box to keep 3 boxes 163 self.main_whole_box = HIGVBox() 164 165 self.upper_box = HIGHBox() 166 self.middle_box = HIGHBox() 167 self.lower_box = HIGHBox() 168 169 #self.main_vbox = HIGVBox() 170 self.command_entry = gtk.Entry() 171 self.command_entry_changed_cb_id = self.command_entry.connect( 172 "changed", self.command_entry_changed_cb) 173 174 self.scan_button = HIGButton(_("Scan")) 175 self.scan_button.connect("clicked", self.run_scan) 176 177 self.notebook = gtk.Notebook() 178 179 # Profile info page 180 self.profile_info_vbox = HIGVBox() 181 self.profile_info_label = HIGSectionLabel(_('Profile Information')) 182 self.profile_name_label = HIGEntryLabel(_('Profile name')) 183 self.profile_name_entry = gtk.Entry() 184 self.profile_name_entry.connect( 185 'enter-notify-event', self.update_help_name) 186 self.profile_description_label = HIGEntryLabel(_('Description')) 187 self.profile_description_scroll = HIGScrolledWindow() 188 self.profile_description_scroll.set_border_width(0) 189 self.profile_description_text = HIGTextView() 190 self.profile_description_text.connect( 191 'motion-notify-event', self.update_help_desc) 192 193 # Buttons 194 self.buttons_hbox = HIGHBox() 195 196 self.cancel_button = HIGButton(stock=gtk.STOCK_CANCEL) 197 self.cancel_button.connect('clicked', self.exit) 198 199 self.delete_button = HIGButton(stock=gtk.STOCK_DELETE) 200 self.delete_button.connect('clicked', self.delete_profile) 201 202 self.save_button = HIGButton(_("Save Changes"), stock=gtk.STOCK_SAVE) 203 self.save_button.connect('clicked', self.save_profile) 204 205 ### 206 self.help_vbox = HIGVBox() 207 self.help_label = HIGSectionLabel(_('Help')) 208 self.help_scroll = HIGScrolledWindow() 209 self.help_scroll.set_border_width(0) 210 self.help_field = HIGTextView() 211 self.help_field.set_cursor_visible(False) 212 self.help_field.set_left_margin(5) 213 self.help_field.set_editable(False) 214 self.help_vbox.set_size_request(200, -1) 215 ### 216 217 def __pack_widgets(self): 218 219 ### 220 self.add(self.main_whole_box) 221 222 # Packing command entry to upper box 223 self.upper_box._pack_expand_fill(self.command_entry) 224 self.upper_box._pack_noexpand_nofill(self.scan_button) 225 226 # Packing notebook (left) and help box (right) to middle box 227 self.middle_box._pack_expand_fill(self.notebook) 228 self.middle_box._pack_expand_fill(self.help_vbox) 229 230 # Packing buttons to lower box 231 self.lower_box.pack_end(self.buttons_hbox) 232 233 # Packing the three vertical boxes to the main box 234 self.main_whole_box._pack_noexpand_nofill(self.upper_box) 235 self.main_whole_box._pack_expand_fill(self.middle_box) 236 self.main_whole_box._pack_noexpand_nofill(self.lower_box) 237 ### 238 239 # Packing profile information tab on notebook 240 self.notebook.append_page( 241 self.profile_info_vbox, gtk.Label(_('Profile'))) 242 self.profile_info_vbox.set_border_width(5) 243 table = HIGTable() 244 self.profile_info_vbox._pack_noexpand_nofill(self.profile_info_label) 245 self.profile_info_vbox._pack_expand_fill(HIGSpacer(table)) 246 247 self.profile_description_scroll.add(self.profile_description_text) 248 249 vbox_desc = HIGVBox() 250 vbox_desc._pack_noexpand_nofill(self.profile_description_label) 251 vbox_desc._pack_expand_fill(hig_box_space_holder()) 252 253 vbox_ann = HIGVBox() 254 vbox_ann._pack_expand_fill(hig_box_space_holder()) 255 256 table.attach( 257 self.profile_name_label, 0, 1, 0, 1, xoptions=0, yoptions=0) 258 table.attach(self.profile_name_entry, 1, 2, 0, 1, yoptions=0) 259 table.attach(vbox_desc, 0, 1, 1, 2, xoptions=0) 260 table.attach(self.profile_description_scroll, 1, 2, 1, 2) 261 262 # Packing buttons on button_hbox 263 self.buttons_hbox._pack_expand_fill(hig_box_space_holder()) 264 if self.deletable: 265 self.buttons_hbox._pack_noexpand_nofill(self.delete_button) 266 self.buttons_hbox._pack_noexpand_nofill(self.cancel_button) 267 self.buttons_hbox._pack_noexpand_nofill(self.save_button) 268 269 self.buttons_hbox.set_border_width(5) 270 self.buttons_hbox.set_spacing(6) 271 272 ### 273 self.help_vbox._pack_noexpand_nofill(self.help_label) 274 self.help_vbox._pack_expand_fill(self.help_scroll) 275 self.help_scroll.add(self.help_field) 276 self.help_vbox.set_border_width(1) 277 self.help_vbox.set_spacing(1) 278 ### 279 280 def __create_tab(self, tab_name, section_name, tab): 281 log.debug(">>> Tab name: %s" % tab_name) 282 log.debug(">>>Creating profile editor section: %s" % section_name) 283 vbox = HIGVBox() 284 if tab.notscripttab: # if notscripttab is set 285 table = HIGTable() 286 table.set_row_spacings(2) 287 section = HIGSectionLabel(section_name) 288 vbox._pack_noexpand_nofill(section) 289 vbox._pack_noexpand_nofill(HIGSpacer(table)) 290 vbox.set_border_width(5) 291 tab.fill_table(table, True) 292 else: 293 hbox = tab.get_hmain_box() 294 vbox.pack_start(hbox, True, True, 0) 295 self.notebook.append_page(vbox, gtk.Label(tab_name)) 296 297 def save_profile(self, widget): 298 if self.overwrite: 299 self.profile.remove_profile(self.profile_name) 300 profile_name = self.profile_name_entry.get_text() 301 if profile_name == '': 302 alert = HIGAlertDialog( 303 message_format=_('Unnamed profile'), 304 secondary_text=_( 305 'You must provide a name for this profile.')) 306 alert.run() 307 alert.destroy() 308 309 self.profile_name_entry.grab_focus() 310 311 return None 312 313 command = self.ops.render_string() 314 315 buf = self.profile_description_text.get_buffer() 316 description = buf.get_text( 317 buf.get_start_iter(), buf.get_end_iter()) 318 319 try: 320 self.profile.add_profile( 321 profile_name, 322 command=command, 323 description=description) 324 except ValueError: 325 alert = HIGAlertDialog( 326 message_format=_('Disallowed profile name'), 327 secondary_text=_('Sorry, the name "%s" is not allowed due ' 328 'to technical limitations. (The underlying ' 329 'ConfigParser used to store profiles does not allow ' 330 'it.) Choose a different name.' % profile_name)) 331 alert.run() 332 alert.destroy() 333 return 334 335 self.scan_interface.toolbar.profile_entry.update() 336 self.destroy() 337 338 def clean_profile_info(self): 339 self.profile_name_entry.set_text('') 340 self.profile_description_text.get_buffer().set_text('') 341 342 def set_scan_interface(self, interface): 343 self.scan_interface = interface 344 345 def exit(self, *args): 346 self.destroy() 347 348 def delete_profile(self, widget=None, extra=None): 349 if self.deletable: 350 dialog = HIGDialog(buttons=(gtk.STOCK_OK, gtk.RESPONSE_OK, 351 gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) 352 alert = HIGEntryLabel('<b>' + _("Deleting Profile") + '</b>') 353 text = HIGEntryLabel(_( 354 'Your profile is going to be deleted! ClickOk to continue, ' 355 'or Cancel to go back to Profile Editor.')) 356 hbox = HIGHBox() 357 hbox.set_border_width(5) 358 hbox.set_spacing(12) 359 360 vbox = HIGVBox() 361 vbox.set_border_width(5) 362 vbox.set_spacing(12) 363 364 image = gtk.Image() 365 image.set_from_stock( 366 gtk.STOCK_DIALOG_WARNING, gtk.ICON_SIZE_DIALOG) 367 368 vbox.pack_start(alert) 369 vbox.pack_start(text) 370 hbox.pack_start(image) 371 hbox.pack_start(vbox) 372 373 dialog.vbox.pack_start(hbox) 374 dialog.vbox.show_all() 375 376 response = dialog.run() 377 dialog.destroy() 378 if response == gtk.RESPONSE_CANCEL: 379 return True 380 self.profile.remove_profile(self.profile_name) 381 382 self.update_profile_entry() 383 self.destroy() 384 385 def run_scan(self, widget=None): 386 command_string = self.command_entry.get_text().decode("UTF-8") 387 self.scan_interface.command_toolbar.command = command_string 388 self.scan_interface.start_scan_cb() 389 self.exit() 390 391 def update_profile_entry(self, widget=None, extra=None): 392 self.scan_interface.toolbar.profile_entry.update() 393 list = self.scan_interface.toolbar.profile_entry.get_model() 394 length = len(list) 395 if length > 0: 396 self.scan_interface.toolbar.profile_entry.set_active(0) 397 398 399if __name__ == '__main__': 400 p = ProfileEditor() 401 p.show_all() 402 gtk.main() 403