From 0d47952611198ef6b1163f366dc03922d20b1475 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 09:42:04 +0200 Subject: Adding upstream version 7.94+git20230807.3be01efb1+dfsg. Signed-off-by: Daniel Baumann --- zenmap/radialnet/CHANGELOG | 149 ++ zenmap/radialnet/README | 10 + zenmap/radialnet/__init__.py | 58 + zenmap/radialnet/bestwidgets/__init__.py | 72 + zenmap/radialnet/bestwidgets/boxes.py | 202 +++ zenmap/radialnet/bestwidgets/buttons.py | 91 ++ zenmap/radialnet/bestwidgets/comboboxes.py | 135 ++ zenmap/radialnet/bestwidgets/expanders.py | 97 ++ zenmap/radialnet/bestwidgets/frames.py | 96 ++ zenmap/radialnet/bestwidgets/labels.py | 94 ++ zenmap/radialnet/bestwidgets/textview.py | 209 +++ zenmap/radialnet/bestwidgets/windows.py | 103 ++ zenmap/radialnet/core/ArgvHandle.py | 98 ++ zenmap/radialnet/core/Coordinate.py | 208 +++ zenmap/radialnet/core/Graph.py | 247 +++ zenmap/radialnet/core/Info.py | 63 + zenmap/radialnet/core/Interpolation.py | 157 ++ zenmap/radialnet/core/XMLHandler.py | 324 ++++ zenmap/radialnet/core/__init__.py | 56 + zenmap/radialnet/gui/Application.py | 159 ++ zenmap/radialnet/gui/ControlWidget.py | 1270 +++++++++++++++ zenmap/radialnet/gui/Dialogs.py | 89 ++ zenmap/radialnet/gui/HostsViewer.py | 231 +++ zenmap/radialnet/gui/Image.py | 164 ++ zenmap/radialnet/gui/LegendWindow.py | 242 +++ zenmap/radialnet/gui/NodeNotebook.py | 742 +++++++++ zenmap/radialnet/gui/NodeWindow.py | 129 ++ zenmap/radialnet/gui/RadialNet.py | 2020 ++++++++++++++++++++++++ zenmap/radialnet/gui/SaveDialog.py | 169 ++ zenmap/radialnet/gui/Toolbar.py | 309 ++++ zenmap/radialnet/gui/__init__.py | 56 + zenmap/radialnet/radialnet.pyw | 116 ++ zenmap/radialnet/share/sample/nmap_example.xml | 480 ++++++ zenmap/radialnet/util/__init__.py | 56 + zenmap/radialnet/util/drawing.py | 67 + zenmap/radialnet/util/geometry.py | 152 ++ zenmap/radialnet/util/integration.py | 269 ++++ zenmap/radialnet/util/misc.py | 115 ++ 38 files changed, 9304 insertions(+) create mode 100644 zenmap/radialnet/CHANGELOG create mode 100644 zenmap/radialnet/README create mode 100644 zenmap/radialnet/__init__.py create mode 100644 zenmap/radialnet/bestwidgets/__init__.py create mode 100644 zenmap/radialnet/bestwidgets/boxes.py create mode 100644 zenmap/radialnet/bestwidgets/buttons.py create mode 100644 zenmap/radialnet/bestwidgets/comboboxes.py create mode 100644 zenmap/radialnet/bestwidgets/expanders.py create mode 100644 zenmap/radialnet/bestwidgets/frames.py create mode 100644 zenmap/radialnet/bestwidgets/labels.py create mode 100644 zenmap/radialnet/bestwidgets/textview.py create mode 100644 zenmap/radialnet/bestwidgets/windows.py create mode 100644 zenmap/radialnet/core/ArgvHandle.py create mode 100644 zenmap/radialnet/core/Coordinate.py create mode 100644 zenmap/radialnet/core/Graph.py create mode 100644 zenmap/radialnet/core/Info.py create mode 100644 zenmap/radialnet/core/Interpolation.py create mode 100644 zenmap/radialnet/core/XMLHandler.py create mode 100644 zenmap/radialnet/core/__init__.py create mode 100644 zenmap/radialnet/gui/Application.py create mode 100644 zenmap/radialnet/gui/ControlWidget.py create mode 100644 zenmap/radialnet/gui/Dialogs.py create mode 100644 zenmap/radialnet/gui/HostsViewer.py create mode 100644 zenmap/radialnet/gui/Image.py create mode 100644 zenmap/radialnet/gui/LegendWindow.py create mode 100644 zenmap/radialnet/gui/NodeNotebook.py create mode 100644 zenmap/radialnet/gui/NodeWindow.py create mode 100644 zenmap/radialnet/gui/RadialNet.py create mode 100644 zenmap/radialnet/gui/SaveDialog.py create mode 100644 zenmap/radialnet/gui/Toolbar.py create mode 100644 zenmap/radialnet/gui/__init__.py create mode 100755 zenmap/radialnet/radialnet.pyw create mode 100644 zenmap/radialnet/share/sample/nmap_example.xml create mode 100644 zenmap/radialnet/util/__init__.py create mode 100644 zenmap/radialnet/util/drawing.py create mode 100644 zenmap/radialnet/util/geometry.py create mode 100644 zenmap/radialnet/util/integration.py create mode 100644 zenmap/radialnet/util/misc.py (limited to 'zenmap/radialnet') diff --git a/zenmap/radialnet/CHANGELOG b/zenmap/radialnet/CHANGELOG new file mode 100644 index 0000000..cb2c4af --- /dev/null +++ b/zenmap/radialnet/CHANGELOG @@ -0,0 +1,149 @@ +RadialNet 0.44 +-------------- + +o Fix bug caused by gtk.gdk.PixbufLoader that don't recognize image type in + Windows. Now gtk.gdk.pixbuf_new_from_file() is used (thanks to IndianZ). + +o Added new version of XMLTreeParser.py called XMLHandler.py. + +o Added new version of bestwidgets. + +o Change radialnet.py file name to radialnet.pyw to application starts with no + command prompt on Windows. + + +RadialNet 0.43 +-------------- + +o Clean __init__.py files. + +o Added vendor to address field when addrtype is MAC. + +o Added new version of bestwidgets. + +o Fixed bug caused by list key error on NodeNotebook.py. + + +RadialNet 0.42 +-------------- + +o Fixed bug caused when tcptssequence xml node has no values information. + +o Improve the coherence in animation. + + +RadialNet 0.41 +-------------- + +o Create a solution to fix the gtk issue that don't show text in BWTextEditor + class correctly (thanks to Luís Bastião). + +o Due to encoding problems the encoding of all file was changed to utf-8. + +o Created a new logo and About dialog. New logo was used as window icon too. + +o Added a Info module that contains the program information. + +o Added a Image superclass in gui/Icons.py file. Change gui/Icons.py filename to + gui/Image.py. + +o Added a Path class to hold directory base. This make possible RadialNet + runnable from any directory. + + +RadialNet 0.4 +------------- + +o Incorporated a HostsViewer class that lists the hosts in a left list and show + its information in a NodeNotebook in the right side. It can be accessed in the + new Tools button on toolbar. + +o Added a complete OS Fingerprint and Sequences view with other general + informations in the NodeNotebook class. + +o Create a set of class to make more easy follow the Gnome HIG 2.0 + specifications and the creation of complex composed widgets. This set is + called bestwidgets. + +o Create a class and file named NodeNotebook and move node noetebook pages to + it. + +o Added a Application class to solve user interface issues like hide and show of + widgets. + +o Improve command line options with ArgvHandle class. And add a file chooser + dialog to interface to open Nmap XML files from the GUI. + +o Enable visualization of host with no traceroute information. These nodes are + link to localhost and has black dashed connections. This make possible for + non-privileged users use the RadialNet. + +o Remove statusbar from node's window and put hostname in title. + +o Added a new notebook to node's window with all traceroute information. + +o By default latency numbers are hidden, ring gap is changed to 30 and fisheye + spread factor to 0.5. + +o Convert frames in control panel to expanders to grant to user see only he + need. + +o Move actions toolbar menu to right control panel. And a information button + was added. They are placed in a "Action" expander. + +o Include "Option" expander in "View" expander. + + +RadialNet 0.31 +-------------- + +o Improve the animation by coherent calculus of children positions. Using some + angle conditions we can perform a better animation. + +o Reduce excessive processing on some update methods that check values in + RadialNet class periodically. + + +RadialNet 0.3 +------------- + +o New services viewer on pop-up windows. With NSE script output support with a + text viewer. + +o Clean the interface hiding some controllers if no needed using toggle buttons. + +o Added option to enable/disable slow-in/slow-out animation in options list. + +o Fixes wrong English messages in the program (thanks to DePriest, Jasey). + + +RadialNet 0.2 +------------- + +o Improve icons apparency, size and position. + +o Added latency mean for each edge as text and width of edge. The edge's width + is normalized between 1 (min value) to 5 (max value) pixels. + +o When a node is a group we draw a black circle if localhost (127.0.0.1) is + grouped on it. + +o Change mouse button events to a toolbar. The options. We include buttons to + guide mouse actions. Three options are added, one to change the center of + visualization, another to group node's children, and more one to fill node's + draw region. + +o Added region fill feature. When clicking in a node with this option the + hierarchical region that a node use to draw its children is filled with a + chosen color (red, yellow or green). + +o Added different draw properties for unknown nodes and edges latencies. If a + node is unknown we draw a white node with blue stroke, and if the edge latency + is unknown we draw it dashed. + +o Decrease draw complexity. Reducing the number of draw method and making the + methods for draw node and edges more intelligent. + +o Fixed division by zero error when number of frames is less than 3. This + happens because the interpolation method need a number of pass great or equal + to 3 (thanks to Hiroshi). diff --git a/zenmap/radialnet/README b/zenmap/radialnet/README new file mode 100644 index 0000000..54d1a7a --- /dev/null +++ b/zenmap/radialnet/README @@ -0,0 +1,10 @@ +To start the application you may just double-click the radialnet.py file or +start it with the following command: + + $ python radialnet.py + +To view command line list of arguments type '--help': + + $ python radialnet.py --help + +Att, João Medeiros . diff --git a/zenmap/radialnet/__init__.py b/zenmap/radialnet/__init__.py new file mode 100644 index 0000000..98e2c1e --- /dev/null +++ b/zenmap/radialnet/__init__.py @@ -0,0 +1,58 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +__all__ = ["bestwidgets", "core", "gui", "util"] diff --git a/zenmap/radialnet/bestwidgets/__init__.py b/zenmap/radialnet/bestwidgets/__init__.py new file mode 100644 index 0000000..03877f0 --- /dev/null +++ b/zenmap/radialnet/bestwidgets/__init__.py @@ -0,0 +1,72 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk + +gtk_version_major, gtk_version_minor, gtk_version_release = gi.version_info + +#from boxes import BWHBox, BWVBox, BWTable, BWStatusbar, BWScrolledWindow +#from buttons import BWStockButton, BWToggleStockButton +#from comboboxes import BWChangeableComboBoxEntry +#from expanders import BWExpander +#from frames import BWFrame +#from labels import BWLabel, BWSectionLabel +#from textview import BWTextView, BWTextEditor +#from windows import BWWindow, BWMainWindow, BWAlertDialog diff --git a/zenmap/radialnet/bestwidgets/boxes.py b/zenmap/radialnet/bestwidgets/boxes.py new file mode 100644 index 0000000..5b43a90 --- /dev/null +++ b/zenmap/radialnet/bestwidgets/boxes.py @@ -0,0 +1,202 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk + +__all__ = ('BWBox', 'BWHBox', 'BWVBox', + 'BWStatusbar', 'BWTable', 'BWScrolledWindow') + + +class BWBox(Gtk.Box): + """ + """ + def bw_pack_start_expand_fill(self, widget, padding=0): + """ + """ + self.pack_start(widget, True, True, padding) + + def bw_pack_start_expand_nofill(self, widget, padding=0): + """ + """ + self.pack_start(widget, True, False, padding) + + def bw_pack_start_noexpand_nofill(self, widget, padding=0): + """ + """ + self.pack_start(widget, False, False, padding) + + def bw_pack_end_expand_fill(self, widget, padding=0): + """ + """ + self.pack_end(widget, True, True, padding) + + def bw_pack_end_expand_nofill(self, widget, padding=0): + """ + """ + self.pack_end(widget, True, False, padding) + + def bw_pack_end_noexpand_nofill(self, widget, padding=0): + """ + """ + self.pack_end(widget, False, False, padding) + + +class BWHBox(BWBox): + """ + """ + def __init__(self, homogeneous=False, spacing=12): + """ + """ + Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL, + homogeneous=homogeneous, spacing=spacing) + + +class BWVBox(BWBox): + """ + """ + def __init__(self, homogeneous=False, spacing=12): + """ + """ + Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL, + homogeneous=homogeneous, spacing=spacing) + + +class BWStatusbar(Gtk.Statusbar, BWBox): + """ + """ + def __init__(self, homogeneous=False, spacing=12): + """ + """ + Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL, + homogeneous=homogeneous, spacing=spacing) + + +class BWTable(Gtk.Table, BWBox): + """ + """ + def __init__(self, rows=1, columns=1, homogeneous=False): + """ + """ + Gtk.Table.__init__(self, n_rows=rows, n_columns=columns, + homogeneous=homogeneous) + self.bw_set_spacing(12) + + self.__rows = rows + self.__columns = columns + + self.__last_point = (0, 0) + + def bw_set_spacing(self, spacing): + """ + """ + self.set_row_spacings(spacing) + self.set_col_spacings(spacing) + + def bw_resize(self, rows, columns): + """ + """ + self.__rows = rows + self.__columns = columns + + self.resize(rows, columns) + + def bw_attach_next(self, + child, + xoptions=Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, + yoptions=Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, + xpadding=0, + ypadding=0): + """ + """ + row, column = self.__last_point + + if row != self.__rows: + + self.attach(child, + column, + column + 1, + row, + row + 1, + xoptions, + yoptions, + xpadding, + ypadding) + + if column + 1 == self.__columns: + + column = 0 + row += 1 + + else: + column += 1 + + self.__last_point = (row, column) + + +class BWScrolledWindow(Gtk.ScrolledWindow): + """ + """ + def __init__(self): + """ + """ + Gtk.ScrolledWindow.__init__(self) + self.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + self.set_shadow_type(Gtk.ShadowType.NONE) + self.set_border_width(6) diff --git a/zenmap/radialnet/bestwidgets/buttons.py b/zenmap/radialnet/bestwidgets/buttons.py new file mode 100644 index 0000000..3d3239d --- /dev/null +++ b/zenmap/radialnet/bestwidgets/buttons.py @@ -0,0 +1,91 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk + + +class BWStockButton(Gtk.Button): + """ + """ + def __init__(self, stock, text=None, size=Gtk.IconSize.BUTTON): + """ + """ + Gtk.Button.__init__(self, label=text) + + self.__size = size + + self.__image = Gtk.Image() + self.__image.set_from_stock(stock, self.__size) + self.set_image(self.__image) + + +class BWToggleStockButton(Gtk.ToggleButton): + """ + """ + def __init__(self, stock, text=None, size=Gtk.IconSize.BUTTON): + """ + """ + Gtk.ToggleButton.__init__(self, label=text) + + self.__size = size + + self.__image = Gtk.Image() + self.__image.set_from_stock(stock, self.__size) + self.set_image(self.__image) diff --git a/zenmap/radialnet/bestwidgets/comboboxes.py b/zenmap/radialnet/bestwidgets/comboboxes.py new file mode 100644 index 0000000..e10d7e6 --- /dev/null +++ b/zenmap/radialnet/bestwidgets/comboboxes.py @@ -0,0 +1,135 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, GObject + + +class BWChangeableComboBoxEntry(Gtk.ComboBoxText): + """ + """ + def __init__(self): + """ + """ + self.__liststore = Gtk.ListStore.new([str]) + + Gtk.ComboBoxText.__init__(self, model=self.__liststore, has_entry=True) + + self.connect("changed", self.__changed) + self.get_child().connect("changed", self.__entry_changed) + + self.__last_active = None + + def __changed(self, widget): + """ + """ + if self.get_active() != -1: + self.__last_active = self.get_active() + + def bw_get_length(self): + """ + """ + return len(self.__liststore) + + def __entry_changed(self, widget): + """ + """ + if len(self.__liststore) > 0 and\ + self.__last_active is not None and\ + self.get_active() == -1: + + iter = self.get_model().get_iter((self.__last_active,)) + self.__liststore.set_value(iter, 0, widget.get_text().strip()) + + def bw_get_active(self): + """ + """ + if self.get_active() == -1: + return self.__last_active + + return self.get_active() + + +# testing widget +if __name__ == "__main__": + + def button_clicked(widget, combo): + """ + """ + combo.append_text('New') + + window = Gtk.Window() + window.connect("destroy", lambda w: Gtk.main_quit()) + + box = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0) + + combo = BWChangeableComboBoxEntry() + combo.append_text('New') + combo.set_active(0) + + button = Gtk.Button.new_with_label('More') + button.connect("clicked", button_clicked, combo) + + box.pack_start(button, False, False, 0) + box.pack_start(combo, True, True, 0) + + window.add(box) + window.show_all() + + Gtk.main() diff --git a/zenmap/radialnet/bestwidgets/expanders.py b/zenmap/radialnet/bestwidgets/expanders.py new file mode 100644 index 0000000..a89e08b --- /dev/null +++ b/zenmap/radialnet/bestwidgets/expanders.py @@ -0,0 +1,97 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk +from radialnet.bestwidgets.labels import BWSectionLabel + + +class BWExpander(Gtk.Expander): + """ + """ + def __init__(self, label=''): + """ + """ + Gtk.Expander.__init__(self) + + self.__label = BWSectionLabel(label) + self.set_label_widget(self.__label) + + self.__alignment = Gtk.Alignment.new(0, 0, 1, 1) + self.__alignment.set_padding(12, 0, 24, 0) + + self.add(self.__alignment) + + def bw_set_label_text(self, text): + """ + """ + self.__label.bw_set_text(text) + + def bw_add(self, widget): + """ + """ + if len(self.__alignment.get_children()) > 0: + self.__alignment.remove(self.__alignment.get_children()[0]) + + self.__alignment.add(widget) + + def bw_no_padding(self): + """ + """ + self.__alignment.set_padding(0, 0, 0, 0) diff --git a/zenmap/radialnet/bestwidgets/frames.py b/zenmap/radialnet/bestwidgets/frames.py new file mode 100644 index 0000000..3a35021 --- /dev/null +++ b/zenmap/radialnet/bestwidgets/frames.py @@ -0,0 +1,96 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, Gdk + + +class BWFrame(Gtk.Frame): + """ + """ + def __init__(self, label=''): + """ + """ + Gtk.Frame.__init__(self) + + self.set_border_width(3) + self.set_shadow_type(Gtk.ShadowType.NONE) + + self.__alignment = Gtk.Alignment.new(0, 0, 1, 1) + self.__alignment.set_padding(12, 0, 24, 0) + + self.add(self.__alignment) + + self.bw_set_label(label) + + def bw_set_label(self, label): + """ + """ + self.set_label("" + label + "") + self.get_label_widget().set_use_markup(True) + + def bw_add(self, widget): + """ + """ + self.__alignment.add(widget) + + def bw_remove(self, widget): + """ + """ + self.__alignment.remove(widget) diff --git a/zenmap/radialnet/bestwidgets/labels.py b/zenmap/radialnet/bestwidgets/labels.py new file mode 100644 index 0000000..c3ec94f --- /dev/null +++ b/zenmap/radialnet/bestwidgets/labels.py @@ -0,0 +1,94 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk + + +class BWLabel(Gtk.Label): + """ + """ + def __init__(self, text=''): + """ + """ + Gtk.Label.__init__(self) + + self.set_text(text) + self.set_justify(Gtk.Justification.LEFT) + self.set_alignment(0, 0.50) + self.set_line_wrap(True) + + +class BWSectionLabel(Gtk.Label): + """ + """ + def __init__(self, text=''): + """ + """ + Gtk.Label.__init__(self) + + self.set_markup('' + text + '') + self.set_justify(Gtk.Justification.LEFT) + self.set_alignment(0, 0.50) + self.set_line_wrap(True) + + def bw_set_text(self, text): + """ + """ + self.set_markup('' + text + '') diff --git a/zenmap/radialnet/bestwidgets/textview.py b/zenmap/radialnet/bestwidgets/textview.py new file mode 100644 index 0000000..9deda93 --- /dev/null +++ b/zenmap/radialnet/bestwidgets/textview.py @@ -0,0 +1,209 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk +from radialnet.bestwidgets.boxes import * + + +class BWTextView(BWScrolledWindow): + """ + """ + def __init__(self): + """ + """ + BWScrolledWindow.__init__(self) + + self.__auto_scroll = False + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__textbuffer = Gtk.TextBuffer() + self.__textview = Gtk.TextView.new_with_buffer(self.__textbuffer) + + self.add_with_viewport(self.__textview) + + def bw_set_auto_scroll(self, value): + """ + """ + self.__auto_scroll = value + + def bw_set_editable(self, editable): + """ + """ + self.__textview.set_editable(False) + + def bw_modify_font(self, font): + """ + """ + self.__textview.modify_font(font) + + def bw_set_text(self, text): + """ + """ + self.__textbuffer.set_text(text) + + if self.__auto_scroll: + self.bw_set_scroll_down() + + def bw_get_text(self): + """ + """ + return self.__textbuffer.get_text(self.__textbuffer.get_start_iter(), + self.__textbuffer.get_end_iter()) + + def bw_set_scroll_down(self): + """ + """ + self.get_vadjustment().set_value(self.get_vadjustment().upper) + + def bw_get_textbuffer(self): + """ + """ + return self.__textbuffer + + +class BWTextEditor(BWScrolledWindow): + """ + """ + def __init__(self): + """ + """ + BWScrolledWindow.__init__(self) + self.connect('draw', self.__draw) + + self.__auto_scroll = False + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__hbox = BWHBox(spacing=6) + + self.__textbuffer = Gtk.TextBuffer() + self.__textview = Gtk.TextView.new_with_buffer(self.__textbuffer) + + self.__linebuffer = Gtk.TextBuffer() + self.__lineview = Gtk.TextView.new_with_buffer(self.__linebuffer) + self.__lineview.set_justification(Gtk.Justification.RIGHT) + self.__lineview.set_editable(False) + self.__lineview.set_sensitive(False) + + self.__hbox.bw_pack_start_noexpand_nofill(self.__lineview) + self.__hbox.bw_pack_start_expand_fill(self.__textview) + + self.add_with_viewport(self.__hbox) + + def __draw(self, widget, event): + """ + """ + # code to fix a gtk issue that don't show text correctly + self.__hbox.check_resize() + + def bw_set_auto_scroll(self, value): + """ + """ + self.__auto_scroll = value + + def bw_set_editable(self, editable): + """ + """ + self.__textview.set_editable(False) + + def bw_modify_font(self, font): + """ + """ + self.__textview.modify_font(font) + self.__lineview.modify_font(font) + + def bw_set_text(self, text): + """ + """ + if text != "": + + count = text.count('\n') + text.count('\r') + + lines = range(1, count + 2) + lines = [str(i).strip() for i in lines] + + self.__textbuffer.set_text(text) + self.__linebuffer.set_text('\n'.join(lines)) + + if self.__auto_scroll: + self.bw_set_scroll_down() + + else: + + self.__textbuffer.set_text("") + self.__linebuffer.set_text("") + + def bw_get_text(self): + """ + """ + return self.__textbuffer.get_text(self.__textbuffer.get_start_iter(), + self.__textbuffer.get_end_iter()) + + def bw_set_scroll_down(self): + """ + """ + self.get_vadjustment().set_value(self.get_vadjustment().upper) diff --git a/zenmap/radialnet/bestwidgets/windows.py b/zenmap/radialnet/bestwidgets/windows.py new file mode 100644 index 0000000..fe61162 --- /dev/null +++ b/zenmap/radialnet/bestwidgets/windows.py @@ -0,0 +1,103 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk + +PRIMARY_TEXT_MARKUP = '%s' + + +class BWAlertDialog(Gtk.MessageDialog): + """ + """ + def __init__(self, parent=None, flags=0, type=Gtk.MessageType.INFO, + buttons=Gtk.ButtonsType.OK, + primary_text=None, + secondary_text=None): + + Gtk.MessageDialog.__init__(self, parent=parent, flags=flags, + message_type=type, buttons=buttons) + + self.connect('response', self.__destroy) + + self.set_resizable(False) + + self.set_title(_("Alert")) + self.set_markup(PRIMARY_TEXT_MARKUP % primary_text) + + if secondary_text: + self.format_secondary_text(secondary_text) + + def __destroy(self, dialog, id): + """ + """ + self.destroy() + + +class BWWindow(Gtk.Window): + """ + """ + def __init__(self, type=Gtk.WindowType.TOPLEVEL): + """ + """ + Gtk.Window.__init__(self, type=type) + self.set_border_width(5) + + +BWMainWindow = Gtk.Window diff --git a/zenmap/radialnet/core/ArgvHandle.py b/zenmap/radialnet/core/ArgvHandle.py new file mode 100644 index 0000000..2e72d93 --- /dev/null +++ b/zenmap/radialnet/core/ArgvHandle.py @@ -0,0 +1,98 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import sys + + +class ArgvHandle: + """ + """ + def __init__(self, argv): + """ + """ + self.__argv = argv + + def get_option(self, option): + """ + """ + if option in self.__argv: + + index = self.__argv.index(option) + + if index + 1 < len(self.__argv): + return self.__argv[index + 1] + + return None + + def has_option(self, option): + """ + """ + return option in self.__argv + + def get_last_value(self): + """ + """ + return self.__argv[-1] + + +if __name__ == '__main__': + + import sys + + h = ArgvHandle(sys.argv) + + print(h.get_last_value()) diff --git a/zenmap/radialnet/core/Coordinate.py b/zenmap/radialnet/core/Coordinate.py new file mode 100644 index 0000000..5c82212 --- /dev/null +++ b/zenmap/radialnet/core/Coordinate.py @@ -0,0 +1,208 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import math + + +class PolarCoordinate: + """ + Class to implement a polar coordinate object + """ + + def __init__(self, r=0, t=0): + """ + Constructor method of PolarCoordinate class + @type r: number + @param r: The radius of coordinate + @type t: number + @param t: The angle (theta) of coordinate in radians + """ + + self.__r = r + """Radius of polar coordinate""" + self.__t = t + """Angle (theta) of polar coordinate in radians""" + + def get_theta(self): + """ + """ + return math.degrees(self.__t) + + def get_radius(self): + """ + """ + return self.__r + + def set_theta(self, t): + """ + """ + self.__t = math.radians(t) + + def set_radius(self, r): + """ + """ + self.__r = r + + def get_coordinate(self): + """ + Set polar coordinate + @rtype: tuple + @return: Polar coordinates (r, t) + """ + return (self.__r, math.degrees(self.__t)) + + def set_coordinate(self, r, t): + """ + Set polar coordinate + @type r: number + @param r: The radius of coordinate + @type t: number + @param t: The angle (theta) of coordinate + """ + self.__r = r + self.__t = math.radians(t) + + def to_cartesian(self): + """ + Convert polar in cartesian coordinate + @rtype: tuple + @return: cartesian coordinates (x, y) + """ + x = self.__r * math.cos(self.__t) + y = self.__r * math.sin(self.__t) + + return (x, y) + + +class CartesianCoordinate: + """ + Class to implement a cartesian coordinate object + """ + def __init__(self, x=0, y=0): + """ + Constructor method of CartesianCoordinate class + @type x: number + @param x: The x component of coordinate + @type y: number + @param y: The y component of coordinate + """ + self.__x = x + """X component of cartesian coordinate""" + self.__y = y + """Y component of cartesian coordinate""" + + def get_coordinate(self): + """ + Get cartesian coordinate + @rtype: tuple + @return: Cartesian coordinates (x, y) + """ + return (self.__x, self.__y) + + def set_coordinate(self, x, y): + """ + Set cartesian coordinate + @type x: number + @param x: The x component of coordinate + @type y: number + @param y: The y component of coordinate + """ + self.__x = x + self.__y = y + + def to_polar(self): + """ + Convert cartesian in polar coordinate + @rtype: tuple + @return: polar coordinates (r, t) + """ + r = math.sqrt(self.__x ** 2 + self.__y ** 2) + + if self.__x > 0: + + if self.__y >= 0: + t = math.atan(self.__y / self.__x) + + else: + t = math.atan(self.__y / self.__x) + 2 * math.pi + + elif self.__x < 0: + t = math.atan(self.__y / self.__x) + math.pi + + elif self.__x == 0: + + if self.__y == 0: + t = 0 + + if self.__y > 0: + t = math.pi / 2 + + else: + t = -math.pi / 2 + + return (r, t) + + +if __name__ == "__main__": + + # Testing application + + polar = PolarCoordinate(1, math.pi) + cartesian = CartesianCoordinate(-1, 0) + + print(polar.to_cartesian()) + print(cartesian.to_polar()) diff --git a/zenmap/radialnet/core/Graph.py b/zenmap/radialnet/core/Graph.py new file mode 100644 index 0000000..1e0e8b5 --- /dev/null +++ b/zenmap/radialnet/core/Graph.py @@ -0,0 +1,247 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + + +class Node(object): + """ + Node class + """ + def __init__(self): + """ + Constructor method of Node class + @type : integer + @param : Node identifier + """ + self.__data = None + """User-controlled data pointer""" + self.__edges = [] + """List of edges to other nodes""" + + def get_data(self): + return self.__data + + def set_data(self, data): + self.__data = data + + def get_edge(self, dest): + """ + Return the edge connecting to dest, or None if none + """ + for edge in self.__edges: + if dest in edge.get_nodes(): + return edge + return None + + def get_edges(self): + """ + Return the list of edges + """ + return self.__edges + + def add_edge(self, edge): + self.__edges.append(edge) + + +class Edge: + """ + """ + def __init__(self, nodes): + """ + """ + self.__weights = [] + self.__nodes = nodes + self.__weights_mean = None + + def get_nodes(self): + """ + """ + return self.__nodes + + def get_weights(self): + """ + """ + return self.__weights + + def set_weights(self, weights): + """ + """ + self.__weights = weights + self.__weights_mean = sum(self.__weights) / len(self.__weights) + + def add_weight(self, weight): + """ + """ + self.__weights.append(weight) + self.__weights_mean = sum(self.__weights) / len(self.__weights) + + def get_weights_mean(self): + """ + """ + return self.__weights_mean + + +class Graph: + """ + Network Graph class + """ + + def __init__(self): + """ + Constructor method of Graph class + @type : list + @param : List of nodes + """ + self.__main_node = None + self.__nodes = [] + self.__max_edge_mean_value = None + self.__min_edge_mean_value = None + + def set_nodes(self, nodes): + """ + """ + self.__nodes = nodes + + def get_nodes(self): + """ + """ + return self.__nodes + + def get_number_of_nodes(self): + """ + Get the number of nodes in graph + @rtype: number + @return: The number of nodes in the graph + """ + return len(self.__nodes) + + def set_main_node(self, node): + """ + Set the main node + @type : number + @param : The node + """ + self.__main_node = node + + def get_main_node(self): + """ + Get the main node + @rtype: Node + @return: The main node + """ + return self.__main_node + + def set_connection(self, a, b, weight=None): + """ + Set node connections + @type : list + @param : List of connections + """ + + # if is a new connection make it + edge = a.get_edge(b) + if edge is None: + edge = Edge((a, b)) + a.add_edge(edge) + b.add_edge(edge) + + # then add new weight value + if weight is not None: + + edge.add_weight(weight) + + mean_weight = edge.get_weights_mean() + if (self.__min_edge_mean_value is None or + mean_weight < self.__min_edge_mean_value): + self.__min_edge_mean_value = mean_weight + if (self.__max_edge_mean_value is None or + mean_weight > self.__max_edge_mean_value): + self.__max_edge_mean_value = mean_weight + + def get_edges(self): + """ + An iterator that yields all edges + """ + for node in self.__nodes: + for edge in node.get_edges(): + if edge.get_nodes()[0] == node: + yield edge + + def get_node_connections(self, node): + """ + """ + connections = [] + + for edge in node.get_edges(): + + (a, b) = edge.get_nodes() + + if a == node: + connections.append(b) + if b == node: + connections.append(a) + + return connections + + def get_max_edge_mean_weight(self): + """ + """ + return self.__max_edge_mean_value + + def get_min_edge_mean_weight(self): + """ + """ + return self.__min_edge_mean_value diff --git a/zenmap/radialnet/core/Info.py b/zenmap/radialnet/core/Info.py new file mode 100644 index 0000000..d04b31a --- /dev/null +++ b/zenmap/radialnet/core/Info.py @@ -0,0 +1,63 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + + +INFO = {'name': 'RadialNet', + 'version': '0.44', + 'website': 'http://www.dca.ufrn.br/~joaomedeiros/radialnet/', + 'authors': ['João Paulo de Souza Medeiros'], + 'copyright': 'Copyright (C) 2007, 2008 Insecure.Com LLC'} diff --git a/zenmap/radialnet/core/Interpolation.py b/zenmap/radialnet/core/Interpolation.py new file mode 100644 index 0000000..1d89498 --- /dev/null +++ b/zenmap/radialnet/core/Interpolation.py @@ -0,0 +1,157 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + + +class Linear2DInterpolator: + """ + Implements a bidimensional linear interpolator. + """ + + def __init__(self): + """ + Constructor method of Linear2DInterpolator class + """ + self.__start_point = (0, 0) + """Initial point of interpolation""" + self.__final_point = (0, 0) + """Final point of interpolation""" + self.__interpolated_points = [] + """Interpolated points vector""" + + def set_start_point(self, a, b): + """ + Set initial coordinate + Set final coordinate + @type a: number + @param a: The first component of final point + @type b: number + @param b: The second component of final point + """ + self.__start_point = (a, b) + + def set_final_point(self, a, b): + """ + Set final coordinate + @type a: number + @param a: The first component of final point + @type b: number + @param b: The second component of final point + """ + self.__final_point = (a, b) + + def get_weighed_points(self, number_of_pass, pass_vector): + """ + Return the vector of coordinates between the initial and final + coordinates with the specified size + @type number_of_pass: number + @param number_of_pass: The number of pass of interpolation + @rtype: list + @return: A list of tuples with interpolated points + """ + (ai, bi) = self.__start_point + (af, bf) = self.__final_point + + a_conversion_factor = float(af - ai) / sum(pass_vector) + b_conversion_factor = float(bf - bi) / sum(pass_vector) + + a_pass = 0 + b_pass = 0 + + self.__interpolated_points = list(range(number_of_pass)) + + for i in range(0, number_of_pass): + + a_pass += pass_vector[i] * a_conversion_factor + b_pass += pass_vector[i] * b_conversion_factor + self.__interpolated_points[i] = (ai + a_pass, bi + b_pass) + + return self.__interpolated_points + + def get_points(self, number_of_pass): + """ + Return the vector of coordinates between the initial and final + coordinates with the specified size + @type number_of_pass: number + @param number_of_pass: The number of pass of interpolation + @rtype: list + @return: A list of tuples with interpolated points + """ + (ai, bi) = self.__start_point + (af, bf) = self.__final_point + + a_pass = float(af - ai) / number_of_pass + b_pass = float(bf - bi) / number_of_pass + + self.__interpolated_points = list(range(number_of_pass)) + + for i in range(1, number_of_pass + 1): + self.__interpolated_points[i - 1] = (ai + a_pass * i, + bi + b_pass * i) + + return self.__interpolated_points + + +if __name__ == "__main__": + + # Testing application + + i = Linear2DInterpolator() + + i.set_start_point(0, 0) + i.set_final_point(1, 1) + + print(len(i.get_points(10)), i.get_points(10)) diff --git a/zenmap/radialnet/core/XMLHandler.py b/zenmap/radialnet/core/XMLHandler.py new file mode 100644 index 0000000..36eb2e1 --- /dev/null +++ b/zenmap/radialnet/core/XMLHandler.py @@ -0,0 +1,324 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + + +# Prevent loading PyXML +import xml +xml.__path__ = [x for x in xml.__path__ if "_xmlplus" not in x] + +import xml.sax +import xml.sax.saxutils +from xml.sax.xmlreader import AttributesImpl as Attributes + + +class XMLNode: + """ + """ + def __init__(self, name): + """ + """ + self.__name = name + self.__text = "" + self.__attrs = dict() + self.__children = [] + + def set_text(self, text): + """ + """ + self.__text = text + + def get_text(self): + """ + """ + return self.__text + + def set_name(self, name): + """ + """ + self.__name = name + + def get_name(self): + """ + """ + return self.__name + + def add_attr(self, key, value): + """ + """ + self.__attrs[key] = value + + def add_child(self, child): + """ + """ + self.__children.append(child) + + def get_keys(self): + """ + """ + return self.__attrs.keys() + + def get_attr(self, attr): + """ + """ + return self.__attrs.get(attr) + + def get_attrs(self): + """ + """ + return self.__attrs + + def get_children(self): + """ + """ + return self.__children + + def query_children(self, name, attr, value, first=False, deep=False): + """ + """ + result = [] + + for child in self.__children: + + if child.get_name() == name: + + if attr in child.get_attrs(): + + c_value = child.get_attr(attr) + + if c_value == value or c_value == str(value): + result.append(child) + + if deep: + + c_result = child.query_children(name, attr, value, first, deep) + + if c_result is not None: + + if first: + return c_result + + else: + result.extend(c_result) + + if first and len(result) > 0: + return result[0] + + if first: + return None + + return result + + def search_children(self, name, first=False, deep=False): + """ + """ + result = [] + + for child in self.__children: + + if child.get_name() == name: + + result.append(child) + + if first: + return result[0] + + if deep: + + c_result = child.search_children(name, first, deep) + + if c_result is not None and c_result != []: + + if first: + return c_result + + else: + result.extend(c_result) + + if first: + return None + + return result + + +class XMLWriter(xml.sax.saxutils.XMLGenerator): + """ + """ + def __init__(self, file, root=None, encoding="utf-8"): + """ + """ + xml.sax.saxutils.XMLGenerator.__init__(self, file, encoding) + + self.__root = root + + def set_root(self, root): + """ + """ + self.__root = root + + def write(self): + """ + """ + self.startDocument() + self.write_xml_node([self.__root]) + self.endDocument() + + def write_xml_node(self, root): + """ + """ + for child in root: + + self.startElement(child.get_name(), Attributes(child.get_attrs())) + + if child.get_text() != "": + self.characters(child.get_text()) + + self.write_xml_node(child.get_children()) + + self.endElement(child.get_name()) + + +class XMLReader(xml.sax.ContentHandler): + """ + """ + def __init__(self, file=None): + """ + """ + xml.sax.ContentHandler.__init__(self) + self.__text = "" + self.__status = [] + + self.__file = file + self.__root = None + + self.__parser = xml.sax.make_parser() + self.__parser.setContentHandler(self) + + def set_file(self, file, root): + """ + """ + self.__file = file + + def get_file(self): + """ + """ + return self.__file + + def get_root(self): + """ + """ + return self.__root + + def parse(self): + """ + """ + if self.__file is not None: + self.__parser.parse(self.__file) + + def startDocument(self): + """ + """ + pass + + def startElement(self, name, attrs): + """ + """ + # create new node + node = XMLNode(name) + + # putting attributes and values in node + for attr in attrs.getNames(): + node.add_attr(attr, attrs.get(attr).strip()) + + # who is my father? + if len(self.__status) > 0: + self.__status[-1].add_child(node) + + if self.__root is None: + self.__root = node + + self.__status.append(node) + + def endElement(self, name): + """ + """ + self.__status[-1].set_text(self.__text.strip()) + + self.__text = "" + self.__status.pop() + + def endDocument(self): + """ + """ + pass + + def characters(self, text): + """ + """ + self.__text += text + + +if __name__ == "__main__": + + import sys + + reader = XMLReader(sys.argv[1]) + reader.parse() + + root = reader.get_root() + + writer = XMLWriter(open("test.xml", 'w'), root) + writer.write() diff --git a/zenmap/radialnet/core/__init__.py b/zenmap/radialnet/core/__init__.py new file mode 100644 index 0000000..c314dd7 --- /dev/null +++ b/zenmap/radialnet/core/__init__.py @@ -0,0 +1,56 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ diff --git a/zenmap/radialnet/gui/Application.py b/zenmap/radialnet/gui/Application.py new file mode 100644 index 0000000..dbc6fe2 --- /dev/null +++ b/zenmap/radialnet/gui/Application.py @@ -0,0 +1,159 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk + +from radialnet.util.integration import make_graph_from_nmap_parser +from radialnet.core.Info import INFO +from radialnet.core.XMLHandler import XMLReader +from radialnet.gui.ControlWidget import ControlWidget, ControlFisheye +from radialnet.gui.Toolbar import Toolbar +from radialnet.gui.Image import Pixmaps +import radialnet.gui.RadialNet as RadialNet +from radialnet.bestwidgets.windows import BWMainWindow, BWAlertDialog +from radialnet.bestwidgets.boxes import BWHBox, BWVBox, BWStatusbar + + +DIMENSION = (640, 480) + + +class Application(BWMainWindow): + """ + """ + def __init__(self): + """ + """ + BWMainWindow.__init__(self) + self.set_default_size(DIMENSION[0], DIMENSION[1]) + + self.set_icon(Pixmaps().get_pixbuf('logo')) + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__hbox = BWHBox(spacing=0) + self.__vbox = BWVBox(spacing=0) + + self.__radialnet = RadialNet.RadialNet(RadialNet.LAYOUT_WEIGHTED) + self.__control = ControlWidget(self.__radialnet) + self.__fisheye = ControlFisheye(self.__radialnet) + self.__toolbar = Toolbar(self.__radialnet, + self, + self.__control, + self.__fisheye) + self.__statusbar = BWStatusbar() + + self.__hbox.bw_pack_start_expand_fill(self.__radialnet) + self.__hbox.bw_pack_start_noexpand_nofill(self.__control) + + self.__vbox.bw_pack_start_noexpand_nofill(self.__toolbar) + self.__vbox.bw_pack_start_expand_fill(self.__hbox) + self.__vbox.bw_pack_start_noexpand_nofill(self.__fisheye) + self.__vbox.bw_pack_start_noexpand_nofill(self.__statusbar) + + self.add(self.__vbox) + self.set_title(" ".join([INFO['name'], INFO['version']])) + self.set_position(Gtk.WindowPosition.CENTER) + self.show_all() + self.connect('destroy', Gtk.main_quit) + + self.__radialnet.set_no_show_all(True) + self.__control.set_no_show_all(True) + self.__fisheye.set_no_show_all(True) + + self.__radialnet.hide() + self.__control.hide() + self.__fisheye.hide() + self.__toolbar.disable_controls() + + def parse_nmap_xml_file(self, file): + """ + """ + try: + + self.__parser = XMLReader(file) + self.__parser.parse() + + except Exception as e: + + text = 'It is not possible open file %s: %s' % (file, e) + + alert = BWAlertDialog(self, + primary_text='Error opening file.', + secondary_text=text) + + alert.show_all() + + return False + + self.__radialnet.set_empty() + self.__radialnet.set_graph(make_graph_from_nmap_parser(self.__parser)) + self.__radialnet.show() + + self.__toolbar.enable_controls() + + return True + + def start(self): + """ + """ + Gtk.main() diff --git a/zenmap/radialnet/gui/ControlWidget.py b/zenmap/radialnet/gui/ControlWidget.py new file mode 100644 index 0000000..99be773 --- /dev/null +++ b/zenmap/radialnet/gui/ControlWidget.py @@ -0,0 +1,1270 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, GLib, Gdk + +import math + +import radialnet.util.geometry as geometry + +from radialnet.bestwidgets.boxes import * +from radialnet.core.Coordinate import PolarCoordinate +import radialnet.gui.RadialNet as RadialNet +from radialnet.bestwidgets.expanders import BWExpander + + +OPTIONS = ['address', + 'hostname', + 'icon', + 'latency', + 'ring', + 'region', + 'slow in/out'] + +REFRESH_RATE = 500 + + +class ControlWidget(BWVBox): + """ + """ + def __init__(self, radialnet): + """ + """ + BWVBox.__init__(self) + self.set_border_width(6) + + self.radialnet = radialnet + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__action = ControlAction(self.radialnet) + self.__interpolation = ControlInterpolation(self.radialnet) + self.__layout = ControlLayout(self.radialnet) + self.__view = ControlView(self.radialnet) + + self.bw_pack_start_noexpand_nofill(self.__action) + self.bw_pack_start_noexpand_nofill(self.__interpolation) + self.bw_pack_start_noexpand_nofill(self.__layout) + self.bw_pack_start_noexpand_nofill(self.__view) + + +class ControlAction(BWExpander): + """ + """ + def __init__(self, radialnet): + """ + """ + BWExpander.__init__(self, _('Action')) + self.set_expanded(True) + + self.radialnet = radialnet + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__tbox = BWTable(1, 4) + self.__tbox.bw_set_spacing(0) + self.__vbox = BWVBox() + + self.__jump_to = Gtk.RadioToolButton(group=None, + stock_id=Gtk.STOCK_JUMP_TO) + self.__jump_to.set_tooltip_text('Change focus') + self.__jump_to.connect('toggled', + self.__change_pointer, + RadialNet.POINTER_JUMP_TO) + + self.__info = Gtk.RadioToolButton(group=self.__jump_to, + stock_id=Gtk.STOCK_INFO) + self.__info.set_tooltip_text('Show information') + self.__info.connect('toggled', + self.__change_pointer, + RadialNet.POINTER_INFO) + + self.__group = Gtk.RadioToolButton(group=self.__jump_to, + stock_id=Gtk.STOCK_ADD) + self.__group.set_tooltip_text('Group children') + self.__group.connect('toggled', + self.__change_pointer, + RadialNet.POINTER_GROUP) + + self.__region = Gtk.RadioToolButton(group=self.__jump_to, + stock_id=Gtk.STOCK_SELECT_COLOR) + self.__region.set_tooltip_text('Fill region') + self.__region.connect('toggled', + self.__change_pointer, + RadialNet.POINTER_FILL) + + self.__region_color = Gtk.ComboBoxText.new() + self.__region_color.append_text(_('Red')) + self.__region_color.append_text(_('Yellow')) + self.__region_color.append_text(_('Green')) + self.__region_color.connect('changed', self.__change_region) + self.__region_color.set_active(self.radialnet.get_region_color()) + + self.__tbox.bw_attach_next(self.__jump_to) + self.__tbox.bw_attach_next(self.__info) + self.__tbox.bw_attach_next(self.__group) + self.__tbox.bw_attach_next(self.__region) + + self.__vbox.bw_pack_start_noexpand_nofill(self.__tbox) + self.__vbox.bw_pack_start_noexpand_nofill(self.__region_color) + + self.bw_add(self.__vbox) + + self.__jump_to.set_active(True) + self.__region_color.set_no_show_all(True) + self.__region_color.hide() + + def __change_pointer(self, widget, pointer): + """ + """ + if pointer != self.radialnet.get_pointer_status(): + self.radialnet.set_pointer_status(pointer) + + if pointer == RadialNet.POINTER_FILL: + self.__region_color.show() + else: + self.__region_color.hide() + + def __change_region(self, widget): + """ + """ + self.radialnet.set_region_color(self.__region_color.get_active()) + + +class ControlVariableWidget(Gtk.DrawingArea): + """ + """ + def __init__(self, name, value, update, increment=1): + """ + """ + Gtk.DrawingArea.__init__(self) + + self.__variable_name = name + self.__value = value + self.__update = update + self.__increment_pass = increment + + self.__radius = 6 + self.__increment_time = 100 + + self.__pointer_position = 0 + self.__active_increment = False + + self.__last_value = self.__value() + + self.connect('draw', self.draw) + self.connect('button_press_event', self.button_press) + self.connect('button_release_event', self.button_release) + self.connect('motion_notify_event', self.motion_notify) + + self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK | + Gdk.EventMask.BUTTON_RELEASE_MASK | + Gdk.EventMask.POINTER_MOTION_HINT_MASK | + Gdk.EventMask.POINTER_MOTION_MASK) + + GLib.timeout_add(REFRESH_RATE, self.verify_value) + + def verify_value(self): + """ + """ + if self.__value() != self.__last_value: + self.__last_value = self.__value() + + self.queue_draw() + + return True + + def button_press(self, widget, event): + """ + """ + self.__active_increment = False + pointer = self.get_pointer() + + if self.__button_is_clicked(pointer) and event.button == 1: + + event.window.set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2)) + self.__active_increment = True + self.__increment_value() + + def button_release(self, widget, event): + """ + """ + event.window.set_cursor(Gdk.Cursor(Gdk.CursorType.LEFT_PTR)) + + self.__active_increment = False + self.__pointer_position = 0 + + self.queue_draw() + + def motion_notify(self, widget, event): + """ + Drawing callback + @type widget: GtkWidget + @param widget: Gtk widget superclass + @type event: GtkEvent + @param event: Gtk event of widget + @rtype: boolean + @return: Indicator of the event propagation + """ + if self.__active_increment: + + xc, yc = self.__center_of_widget + x, _ = self.get_pointer() + + if x - self.__radius > 0 and x + self.__radius < 2 * xc: + self.__pointer_position = x - xc + + self.queue_draw() + + def draw(self, widget, context): + """ + Drawing callback + @type widget: GtkWidget + @param widget: Gtk widget superclass + @type context: cairo.Context + @param context: cairo context class + @rtype: boolean + @return: Indicator of the event propagation + """ + self.set_size_request(100, 30) + + self.__draw(context) + + return True + + def __draw(self, context): + """ + """ + allocation = self.get_allocation() + + self.__center_of_widget = (allocation.width // 2, + allocation.height // 2) + + xc, yc = self.__center_of_widget + + # draw line + context.set_line_width(1) + context.set_dash([1, 2]) + context.move_to(self.__radius, + yc + self.__radius) + context.line_to(2 * xc - 5, + yc + self.__radius) + context.stroke() + + # draw text + context.set_dash([1, 0]) + context.set_font_size(10) + + context.move_to(5, yc - self.__radius) + context.show_text(self.__variable_name) + + width = context.text_extents(str(self.__value()))[2] + context.move_to(2 * xc - width - 5, yc - self.__radius) + context.show_text(str(self.__value())) + + context.set_line_width(1) + context.stroke() + + # draw node + context.arc(xc + self.__pointer_position, + yc + self.__radius, + self.__radius, 0, 2 * math.pi) + if self.__active_increment: + context.set_source_rgb(0.0, 0.0, 0.0) + else: + context.set_source_rgb(1.0, 1.0, 1.0) + context.fill_preserve() + context.set_source_rgb(0.0, 0.0, 0.0) + context.stroke() + + def __button_is_clicked(self, pointer): + """ + """ + xc, yc = self.__center_of_widget + center = (xc, yc + self.__radius) + + return geometry.is_in_circle(pointer, 6, center) + + def __increment_value(self): + """ + """ + self.__update(self.__value() + self.__pointer_position / 4) + + self.queue_draw() + + if self.__active_increment: + + GLib.timeout_add(self.__increment_time, + self.__increment_value) + + def set_value_function(self, value): + """ + """ + self.__value = value + + def set_update_function(self, update): + """ + """ + self.__update = update + + +class ControlVariable(BWHBox): + """ + """ + def __init__(self, name, get_function, set_function, increment=1): + """ + """ + BWHBox.__init__(self, spacing=0) + + self.__increment_pass = increment + self.__increment_time = 200 + self.__increment = False + + self.__name = name + self.__get_function = get_function + self.__set_function = set_function + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__control = ControlVariableWidget(self.__name, + self.__get_function, + self.__set_function, + self.__increment_pass) + + self.__left_button = Gtk.Button() + self.__left_button.set_size_request(20, 20) + self.__left_arrow = Gtk.Image.new_from_icon_name("pan-start-symbolic", + Gtk.IconSize.BUTTON); + self.__left_button.add(self.__left_arrow) + self.__left_button.connect('pressed', + self.__pressed, + -self.__increment_pass) + self.__left_button.connect('released', self.__released) + + self.__right_button = Gtk.Button() + self.__right_button.set_size_request(20, 20) + self.__right_arrow = Gtk.Image.new_from_icon_name("pan-end-symbolic", + Gtk.IconSize.BUTTON); + self.__right_button.add(self.__right_arrow) + self.__right_button.connect('pressed', + self.__pressed, + self.__increment_pass) + self.__right_button.connect('released', self.__released) + + self.bw_pack_start_noexpand_nofill(self.__left_button) + self.bw_pack_start_expand_fill(self.__control) + self.bw_pack_start_noexpand_nofill(self.__right_button) + + def __pressed(self, widget, increment): + """ + """ + self.__increment = True + self.__increment_function(increment) + + def __increment_function(self, increment): + """ + """ + if self.__increment: + + self.__set_function(self.__get_function() + increment) + self.__control.verify_value() + + GLib.timeout_add(self.__increment_time, + self.__increment_function, + increment) + + def __released(self, widget): + """ + """ + self.__increment = False + + +class ControlFisheye(BWVBox): + """ + """ + def __init__(self, radialnet): + """ + """ + BWVBox.__init__(self) + self.set_border_width(6) + + self.radialnet = radialnet + self.__ring_max_value = self.radialnet.get_number_of_rings() + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__params = BWHBox() + + self.__fisheye_label = Gtk.Label.new(_('Fisheye on ring')) + self.__fisheye_label.set_use_markup(True) + + self.__ring = Gtk.Adjustment.new(0, 0, self.__ring_max_value, 0.01, 0.01, 0) + + self.__ring_spin = Gtk.SpinButton(adjustment=self.__ring) + self.__ring_spin.set_digits(2) + + self.__ring_scale = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL, adjustment=self.__ring) + self.__ring_scale.set_size_request(100, -1) + self.__ring_scale.set_digits(2) + self.__ring_scale.set_value_pos(Gtk.PositionType.LEFT) + self.__ring_scale.set_draw_value(False) + + self.__interest_label = Gtk.Label.new(_('with interest factor')) + self.__interest = Gtk.Adjustment.new(0, 0, 10, 0.01, 0, 0) + self.__interest_spin = Gtk.SpinButton(adjustment=self.__interest) + self.__interest_spin.set_digits(2) + + self.__spread_label = Gtk.Label.new(_('and spread factor')) + self.__spread = Gtk.Adjustment.new(0, -1.0, 1.0, 0.01, 0.01, 0) + self.__spread_spin = Gtk.SpinButton(adjustment=self.__spread) + self.__spread_spin.set_digits(2) + + self.__params.bw_pack_start_noexpand_nofill(self.__fisheye_label) + self.__params.bw_pack_start_noexpand_nofill(self.__ring_spin) + self.__params.bw_pack_start_expand_fill(self.__ring_scale) + self.__params.bw_pack_start_noexpand_nofill(self.__interest_label) + self.__params.bw_pack_start_noexpand_nofill(self.__interest_spin) + self.__params.bw_pack_start_noexpand_nofill(self.__spread_label) + self.__params.bw_pack_start_noexpand_nofill(self.__spread_spin) + + self.bw_pack_start_noexpand_nofill(self.__params) + + self.__ring.connect('value_changed', self.__change_ring) + self.__interest.connect('value_changed', self.__change_interest) + self.__spread.connect('value_changed', self.__change_spread) + + GLib.timeout_add(REFRESH_RATE, self.__update_fisheye) + + def __update_fisheye(self): + """ + """ + # adjust ring scale to radialnet number of nodes + ring_max_value = self.radialnet.get_number_of_rings() - 1 + + if ring_max_value != self.__ring_max_value: + + value = self.__ring.get_value() + + if value == 0 and ring_max_value != 0: + value = 1 + + elif value > ring_max_value: + value = ring_max_value + + self.__ring.configure(value, 1, ring_max_value, 0.01, 0.01, 0) + self.__ring_max_value = ring_max_value + + self.__ring_scale.queue_draw() + + # check ring value + ring_value = self.radialnet.get_fisheye_ring() + + if self.__ring.get_value() != ring_value: + self.__ring.set_value(ring_value) + + # check interest value + interest_value = self.radialnet.get_fisheye_interest() + + if self.__interest.get_value() != interest_value: + self.__interest.set_value(interest_value) + + # check spread value + spread_value = self.radialnet.get_fisheye_spread() + + if self.__spread.get_value() != spread_value: + self.__spread.set_value(spread_value) + + return True + + def active_fisheye(self): + """ + """ + self.radialnet.set_fisheye(True) + self.__change_ring() + self.__change_interest() + + def deactive_fisheye(self): + """ + """ + self.radialnet.set_fisheye(False) + + def __change_ring(self, widget=None): + """ + """ + if not self.radialnet.is_in_animation(): + self.radialnet.set_fisheye_ring(self.__ring.get_value()) + else: + self.__ring.set_value(self.radialnet.get_fisheye_ring()) + + def __change_interest(self, widget=None): + """ + """ + if not self.radialnet.is_in_animation(): + self.radialnet.set_fisheye_interest(self.__interest.get_value()) + else: + self.__interest.set_value(self.radialnet.get_fisheye_interest()) + + def __change_spread(self, widget=None): + """ + """ + if not self.radialnet.is_in_animation(): + self.radialnet.set_fisheye_spread(self.__spread.get_value()) + else: + self.__spread.set_value(self.radialnet.get_fisheye_spread()) + + +class ControlInterpolation(BWExpander): + """ + """ + def __init__(self, radialnet): + """ + """ + BWExpander.__init__(self, _('Interpolation')) + + self.radialnet = radialnet + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__vbox = BWVBox() + + self.__cartesian_radio = Gtk.RadioButton(group=None, + label=_('Cartesian')) + self.__polar_radio = Gtk.RadioButton(group=self.__cartesian_radio, + label=_('Polar')) + self.__cartesian_radio.connect('toggled', + self.__change_system, + RadialNet.INTERPOLATION_CARTESIAN) + self.__polar_radio.connect('toggled', + self.__change_system, + RadialNet.INTERPOLATION_POLAR) + + self.__system_box = BWHBox() + self.__system_box.bw_pack_start_noexpand_nofill(self.__polar_radio) + self.__system_box.bw_pack_start_noexpand_nofill(self.__cartesian_radio) + + self.__frames_box = BWHBox() + self.__frames_label = Gtk.Label.new(_('Frames')) + self.__frames_label.set_alignment(0.0, 0.5) + self.__frames = Gtk.Adjustment.new( + self.radialnet.get_number_of_frames(), 1, 1000, 1, 0, 0) + self.__frames.connect('value_changed', self.__change_frames) + self.__frames_spin = Gtk.SpinButton(adjustment=self.__frames) + self.__frames_box.bw_pack_start_expand_fill(self.__frames_label) + self.__frames_box.bw_pack_start_noexpand_nofill(self.__frames_spin) + + self.__vbox.bw_pack_start_noexpand_nofill(self.__frames_box) + self.__vbox.bw_pack_start_noexpand_nofill(self.__system_box) + + self.bw_add(self.__vbox) + + GLib.timeout_add(REFRESH_RATE, self.__update_animation) + + def __update_animation(self): + """ + """ + active = self.radialnet.get_interpolation() + + if active == RadialNet.INTERPOLATION_CARTESIAN: + self.__cartesian_radio.set_active(True) + + else: + self.__polar_radio.set_active(True) + + return True + + def __change_system(self, widget, value): + """ + """ + if not self.radialnet.set_interpolation(value): + + active = self.radialnet.get_interpolation() + + if active == RadialNet.INTERPOLATION_CARTESIAN: + self.__cartesian_radio.set_active(True) + + else: + self.__polar_radio.set_active(True) + + def __change_frames(self, widget): + """ + """ + if not self.radialnet.set_number_of_frames(self.__frames.get_value()): + self.__frames.set_value(self.radialnet.get_number_of_frames()) + + +class ControlLayout(BWExpander): + """ + """ + def __init__(self, radialnet): + """ + """ + BWExpander.__init__(self, _('Layout')) + + self.radialnet = radialnet + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__hbox = BWHBox() + + self.__layout = Gtk.ComboBoxText() + self.__layout.append_text(_('Symmetric')) + self.__layout.append_text(_('Weighted')) + self.__layout.set_active(self.radialnet.get_layout()) + self.__layout.connect('changed', self.__change_layout) + self.__force = Gtk.ToolButton(stock_id=Gtk.STOCK_REFRESH) + self.__force.connect('clicked', self.__force_update) + + self.__hbox.bw_pack_start_expand_fill(self.__layout) + self.__hbox.bw_pack_start_noexpand_nofill(self.__force) + + self.bw_add(self.__hbox) + + self.__check_layout() + + def __check_layout(self): + """ + """ + if self.__layout.get_active() == RadialNet.LAYOUT_WEIGHTED: + self.__force.set_sensitive(True) + + else: + self.__force.set_sensitive(False) + + return True + + def __force_update(self, widget): + """ + """ + self.__fisheye_ring = self.radialnet.get_fisheye_ring() + self.radialnet.update_layout() + + def __change_layout(self, widget): + """ + """ + if not self.radialnet.set_layout(self.__layout.get_active()): + self.__layout.set_active(self.radialnet.get_layout()) + + else: + self.__check_layout() + + +class ControlRingGap(BWVBox): + """ + """ + def __init__(self, radialnet): + """ + """ + BWVBox.__init__(self) + + self.radialnet = radialnet + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__radius = ControlVariable(_('Ring gap'), + self.radialnet.get_ring_gap, + self.radialnet.set_ring_gap) + + self.__label = Gtk.Label.new(_('Lower ring gap')) + self.__label.set_alignment(0.0, 0.5) + self.__adjustment = Gtk.Adjustment.new( + self.radialnet.get_min_ring_gap(), 0, 50, 1, 0, 0) + self.__spin = Gtk.SpinButton(adjustment=self.__adjustment) + self.__spin.connect('value_changed', self.__change_lower) + + self.__lower_hbox = BWHBox() + self.__lower_hbox.bw_pack_start_expand_fill(self.__label) + self.__lower_hbox.bw_pack_start_noexpand_nofill(self.__spin) + + self.bw_pack_start_noexpand_nofill(self.__radius) + self.bw_pack_start_noexpand_nofill(self.__lower_hbox) + + def __change_lower(self, widget): + """ + """ + if not self.radialnet.set_min_ring_gap(self.__adjustment.get_value()): + self.__adjustment.set_value(self.radialnet.get_min_ring_gap()) + + +class ControlOptions(BWScrolledWindow): + """ + """ + def __init__(self, radialnet): + """ + """ + BWScrolledWindow.__init__(self) + + self.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.ALWAYS) + self.set_shadow_type(Gtk.ShadowType.NONE) + + self.radialnet = radialnet + + self.enable_labels = True + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__liststore = Gtk.ListStore.new([bool, str]) + + self.__liststore.append([None, OPTIONS[0]]) + self.__liststore.append([None, OPTIONS[1]]) + self.__liststore.append([None, OPTIONS[2]]) + self.__liststore.append([None, OPTIONS[3]]) + self.__liststore.append([None, OPTIONS[4]]) + self.__liststore.append([None, OPTIONS[5]]) + self.__liststore.append([None, OPTIONS[6]]) + + self.__cell_toggle = Gtk.CellRendererToggle() + self.__cell_toggle.set_property('activatable', True) + self.__cell_toggle.connect('toggled', + self.__change_option, + self.__liststore) + + self.__column_toggle = Gtk.TreeViewColumn(cell_renderer=self.__cell_toggle) + self.__column_toggle.add_attribute(self.__cell_toggle, 'active', 0) + self.__column_toggle.set_cell_data_func(self.__cell_toggle, self.__cell_toggle_data_method) + + self.__cell_text = Gtk.CellRendererText() + + self.__column_text = Gtk.TreeViewColumn(title=None, + cell_renderer=self.__cell_text, + text=1) + self.__column_text.set_cell_data_func(self.__cell_text, self.__cell_text_data_method) + + self.__treeview = Gtk.TreeView.new_with_model(self.__liststore) + self.__treeview.set_enable_search(True) + self.__treeview.set_search_column(1) + self.__treeview.set_headers_visible(False) + self.__treeview.append_column(self.__column_toggle) + self.__treeview.append_column(self.__column_text) + + self.add_with_viewport(self.__treeview) + + GLib.timeout_add(REFRESH_RATE, self.__update_options) + + def __cell_toggle_data_method(self, column, cell, model, it, data): + if not self.enable_labels and model.get_value(it, 1) == 'hostname': + cell.set_property('activatable', False) + else: + cell.set_property('activatable', True) + + def __cell_text_data_method(self, column, cell, model, it, data): + if not self.enable_labels and model.get_value(it, 1) == 'hostname': + cell.set_property('strikethrough', True) + else: + cell.set_property('strikethrough', False) + + def __update_options(self): + """ + """ + model = self.__liststore + + model[OPTIONS.index('address')][0] = self.radialnet.get_show_address() + model[OPTIONS.index('hostname')][0] = self.enable_labels and \ + self.radialnet.get_show_hostname() + model[OPTIONS.index('icon')][0] = self.radialnet.get_show_icon() + model[OPTIONS.index('latency')][0] = self.radialnet.get_show_latency() + model[OPTIONS.index('ring')][0] = self.radialnet.get_show_ring() + model[OPTIONS.index('region')][0] = self.radialnet.get_show_region() + model[OPTIONS.index('slow in/out')][0] = \ + self.radialnet.get_slow_inout() + + return True + + def __change_option(self, cell, option, model): + """ + """ + option = int(option) + model[option][0] = not model[option][0] + + if OPTIONS[option] == 'address': + self.radialnet.set_show_address(model[option][0]) + if model[option][0]: + model[OPTIONS.index('hostname')][0] = self.radialnet.get_show_hostname() + else: + model[OPTIONS.index('hostname')][0] = False + self.enable_labels = model[option][0] + + elif OPTIONS[option] == 'hostname': + self.radialnet.set_show_hostname(model[option][0]) + + elif OPTIONS[option] == 'icon': + self.radialnet.set_show_icon(model[option][0]) + + elif OPTIONS[option] == 'latency': + self.radialnet.set_show_latency(model[option][0]) + + elif OPTIONS[option] == 'ring': + self.radialnet.set_show_ring(model[option][0]) + + elif OPTIONS[option] == 'region': + self.radialnet.set_show_region(model[option][0]) + + elif OPTIONS[option] == 'slow in/out': + self.radialnet.set_slow_inout(model[option][0]) + + +class ControlView(BWExpander): + """ + """ + def __init__(self, radialnet): + """ + """ + BWExpander.__init__(self, _('View')) + self.set_expanded(True) + + self.radialnet = radialnet + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__vbox = BWVBox(spacing=0) + + self.__zoom = ControlVariable(_('Zoom'), + self.radialnet.get_zoom, + self.radialnet.set_zoom) + + self.__ring_gap = ControlRingGap(self.radialnet) + self.__navigation = ControlNavigation(self.radialnet) + + self.__options = ControlOptions(self.radialnet) + self.__options.set_border_width(0) + + self.__vbox.bw_pack_start_expand_nofill(self.__options) + self.__vbox.bw_pack_start_noexpand_nofill(self.__navigation) + self.__vbox.bw_pack_start_noexpand_nofill(self.__zoom) + self.__vbox.bw_pack_start_noexpand_nofill(self.__ring_gap) + + self.bw_add(self.__vbox) + + +class ControlNavigation(Gtk.DrawingArea): + """ + """ + def __init__(self, radialnet): + """ + """ + Gtk.DrawingArea.__init__(self) + + self.radialnet = radialnet + + self.__rotate_node = PolarCoordinate() + self.__rotate_node.set_coordinate(40, 90) + self.__center_of_widget = (50, 50) + self.__moving = None + self.__centering = False + self.__rotating = False + self.__move_pass = 100 + + self.__move_position = (0, 0) + self.__move_addition = [(-1, 0), + (-1, -1), + (0, -1), + (1, -1), + (1, 0), + (1, 1), + (0, 1), + (-1, 1)] + + self.__move_factor = 1 + self.__move_factor_limit = 20 + + self.__rotate_radius = 6 + self.__move_radius = 6 + + self.__rotate_clicked = False + self.__move_clicked = None + + self.connect('draw', self.draw) + self.connect('button_press_event', self.button_press) + self.connect('button_release_event', self.button_release) + self.connect('motion_notify_event', self.motion_notify) + self.connect('enter_notify_event', self.enter_notify) + self.connect('leave_notify_event', self.leave_notify) + self.connect('key_press_event', self.key_press) + self.connect('key_release_event', self.key_release) + + self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK | + Gdk.EventMask.BUTTON_RELEASE_MASK | + Gdk.EventMask.ENTER_NOTIFY_MASK | + Gdk.EventMask.LEAVE_NOTIFY_MASK | + Gdk.EventMask.KEY_PRESS_MASK | + Gdk.EventMask.KEY_RELEASE_MASK | + Gdk.EventMask.POINTER_MOTION_HINT_MASK | + Gdk.EventMask.POINTER_MOTION_MASK) + + self.__rotate_node.set_coordinate(40, self.radialnet.get_rotation()) + + def key_press(self, widget, event): + """ + """ + # key = Gdk.keyval_name(event.keyval) + + self.queue_draw() + + return True + + def key_release(self, widget, event): + """ + """ + # key = Gdk.keyval_name(event.keyval) + + self.queue_draw() + + return True + + def enter_notify(self, widget, event): + """ + """ + return False + + def leave_notify(self, widget, event): + """ + """ + self.queue_draw() + + return False + + def button_press(self, widget, event): + """ + Drawing callback + @type widget: GtkWidget + @param widget: Gtk widget superclass + @type event: GtkEvent + @param event: Gtk event of widget + @rtype: boolean + @return: Indicator of the event propagation + """ + pointer = self.get_pointer() + + direction = False + + if self.__rotate_is_clicked(pointer): + + event.window.set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2)) + self.__rotating = True + + direction = self.__move_is_clicked(pointer) + + if direction is not None and self.__moving is None: + + event.window.set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2)) + self.__moving = direction + self.__move_in_direction(direction) + + if self.__center_is_clicked(pointer): + + event.window.set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2)) + self.__centering = True + self.__move_position = (0, 0) + self.radialnet.set_translation(self.__move_position) + + self.queue_draw() + + return False + + def button_release(self, widget, event): + """ + Drawing callback + @type widget: GtkWidget + @param widget: Gtk widget superclass + @type event: GtkEvent + @param event: Gtk event of widget + @rtype: boolean + @return: Indicator of the event propagation + """ + self.__moving = None # stop moving + self.__centering = False + self.__rotating = False # stop rotate + self.__move_factor = 1 + + event.window.set_cursor(Gdk.Cursor(Gdk.CursorType.LEFT_PTR)) + + self.queue_draw() + + return False + + def motion_notify(self, widget, event): + """ + Drawing callback + @type widget: GtkWidget + @param widget: Gtk widget superclass + @type event: GtkEvent + @param event: Gtk event of widget + @rtype: boolean + @return: Indicator of the event propagation + """ + xc, yc = self.__center_of_widget + x, y = self.get_pointer() + + status = not self.radialnet.is_in_animation() + status = status and not self.radialnet.is_empty() + + if self.__rotating and status: + + r, t = self.__rotate_node.get_coordinate() + t = math.degrees(math.atan2(yc - y, x - xc)) + + if t < 0: + t = 360 + t + + self.radialnet.set_rotation(t) + self.__rotate_node.set_coordinate(r, t) + + self.queue_draw() + + return False + + def draw(self, widget, context): + """ + Drawing callback + @type widget: GtkWidget + @param widget: Gtk widget superclass + @type context: cairo.Context + @param context: cairo context class + @rtype: boolean + @return: Indicator of the event propagation + """ + self.set_size_request(120, 130) + + self.__draw(context) + + return False + + def __draw_rotate_control(self, context): + """ + """ + xc, yc = self.__center_of_widget + r, t = self.__rotate_node.get_coordinate() + x, y = self.__rotate_node.to_cartesian() + + # draw text + context.set_font_size(10) + context.move_to(xc - 49, yc - 48) + context.show_text(_("Navigation")) + + width = context.text_extents(str(int(t)))[2] + context.move_to(xc + 49 - width - 2, yc - 48) + context.show_text(str(round(t, 1))) + context.set_line_width(1) + context.stroke() + + # draw arc + context.set_dash([1, 2]) + context.arc(xc, yc, 40, 0, 2 * math.pi) + context.set_source_rgb(0.0, 0.0, 0.0) + context.set_line_width(1) + context.stroke() + + # draw node + context.set_dash([1, 0]) + context.arc(xc + x, yc - y, self.__rotate_radius, 0, 2 * math.pi) + + if self.__rotating: + context.set_source_rgb(0.0, 0.0, 0.0) + else: + context.set_source_rgb(1.0, 1.0, 1.0) + + context.fill_preserve() + context.set_source_rgb(0.0, 0.0, 0.0) + context.set_line_width(1) + context.stroke() + + return False + + def __draw_move_control(self, context): + """ + """ + xc, yc = self.__center_of_widget + pc = PolarCoordinate() + + context.set_dash([1, 1]) + context.arc(xc, yc, 23, 0, 2 * math.pi) + context.set_source_rgb(0.0, 0.0, 0.0) + context.set_line_width(1) + context.stroke() + + for i in range(8): + + pc.set_coordinate(23, 45 * i) + x, y = pc.to_cartesian() + + context.set_dash([1, 1]) + context.move_to(xc, yc) + context.line_to(xc + x, yc - y) + context.stroke() + + context.set_dash([1, 0]) + context.arc( + xc + x, yc - y, self.__move_radius, 0, 2 * math.pi) + + if i == self.__moving: + context.set_source_rgb(0.0, 0.0, 0.0) + else: + context.set_source_rgb(1.0, 1.0, 1.0) + context.fill_preserve() + context.set_source_rgb(0.0, 0.0, 0.0) + context.set_line_width(1) + context.stroke() + + context.arc(xc, yc, 6, 0, 2 * math.pi) + + if self.__centering: + context.set_source_rgb(0.0, 0.0, 0.0) + else: + context.set_source_rgb(1.0, 1.0, 1.0) + context.fill_preserve() + context.set_source_rgb(0.0, 0.0, 0.0) + context.set_line_width(1) + context.stroke() + + return False + + def __draw(self, context): + """ + Drawing method + """ + # Getting allocation reference + allocation = self.get_allocation() + + self.__center_of_widget = (allocation.width // 2, + allocation.height // 2) + + self.__draw_rotate_control(context) + self.__draw_move_control(context) + + return False + + def __move_in_direction(self, direction): + """ + """ + if self.__moving is not None: + + bx, by = self.__move_position + ax, ay = self.__move_addition[direction] + + self.__move_position = (bx + self.__move_factor * ax, + by + self.__move_factor * ay) + self.radialnet.set_translation(self.__move_position) + + if self.__move_factor < self.__move_factor_limit: + self.__move_factor += 1 + + GLib.timeout_add(self.__move_pass, + self.__move_in_direction, + direction) + + return False + + def __rotate_is_clicked(self, pointer): + """ + """ + xn, yn = self.__rotate_node.to_cartesian() + xc, yc = self.__center_of_widget + + center = (xc + xn, yc - yn) + return geometry.is_in_circle(pointer, self.__rotate_radius, center) + + def __center_is_clicked(self, pointer): + """ + """ + return geometry.is_in_circle(pointer, self.__move_radius, + self.__center_of_widget) + + def __move_is_clicked(self, pointer): + """ + """ + xc, yc = self.__center_of_widget + pc = PolarCoordinate() + + for i in range(8): + + pc.set_coordinate(23, 45 * i) + x, y = pc.to_cartesian() + + center = (xc + x, yc - y) + if geometry.is_in_circle(pointer, self.__move_radius, center): + return i + + return None diff --git a/zenmap/radialnet/gui/Dialogs.py b/zenmap/radialnet/gui/Dialogs.py new file mode 100644 index 0000000..01d97f2 --- /dev/null +++ b/zenmap/radialnet/gui/Dialogs.py @@ -0,0 +1,89 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk + +from radialnet.core.Info import INFO +from radialnet.gui.Image import Pixmaps + + +class AboutDialog(Gtk.AboutDialog): + """ + """ + def __init__(self): + """ + """ + Gtk.AboutDialog.__init__(self) + + self.set_name(INFO['name']) + self.set_version(INFO['version']) + self.set_website(INFO['website']) + self.set_authors(INFO['authors']) + self.set_license(INFO['license']) + self.set_copyright(INFO['copyright']) + + self.set_logo(Pixmaps().get_pixbuf('logo')) + + self.connect('response', self.__destroy) + + def __destroy(self, dialog, id): + """ + """ + self.destroy() diff --git a/zenmap/radialnet/gui/HostsViewer.py b/zenmap/radialnet/gui/HostsViewer.py new file mode 100644 index 0000000..feb0ae8 --- /dev/null +++ b/zenmap/radialnet/gui/HostsViewer.py @@ -0,0 +1,231 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk + +import re + +from radialnet.bestwidgets.windows import BWMainWindow + +from radialnet.gui.NodeNotebook import NodeNotebook +from radialnet.util.misc import ipv4_compare + + +HOSTS_COLORS = ['#d5ffd5', '#ffffd5', '#ffd5d5'] + +HOSTS_HEADER = ['ID', '#', 'Hosts'] + +DIMENSION = (700, 400) + +IP_RE = re.compile(r'^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$') + + +class HostsViewer(BWMainWindow): + """ + """ + def __init__(self, nodes): + """ + """ + BWMainWindow.__init__(self) + self.set_title(_('Hosts Viewer')) + self.set_default_size(DIMENSION[0], DIMENSION[1]) + + self.__nodes = nodes + self.__default_view = Gtk.Label.new(_("No node selected")) + self.__view = self.__default_view + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__panel = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL) + self.__panel.set_border_width(6) + + self.__list = HostsList(self, self.__nodes) + + self.__panel.add1(self.__list) + self.__panel.add2(self.__view) + self.__panel.set_position(int(DIMENSION[0] / 5)) + + self.add(self.__panel) + + def change_notebook(self, node): + """ + """ + if self.__view is not None: + self.__view.destroy() + + if node is not None: + self.__view = NodeNotebook(node) + else: + self.__view = self.__default_view + self.__view.show_all() + + self.__panel.add2(self.__view) + + +class HostsList(Gtk.ScrolledWindow): + """ + """ + def __init__(self, parent, nodes): + """ + """ + super(HostsList, self).__init__() + self.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + self.set_shadow_type(Gtk.ShadowType.NONE) + + self.__parent = parent + self.__nodes = nodes + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__cell = Gtk.CellRendererText() + + self.__hosts_store = Gtk.ListStore.new([int, int, str, str, bool]) + + self.__hosts_treeview = Gtk.TreeView.new_with_model(self.__hosts_store) + self.__hosts_treeview.connect('cursor-changed', self.__cursor_callback) + + for i in range(len(self.__nodes)): + + node = self.__nodes[i] + + ports = node.get_info('number_of_open_ports') + color = HOSTS_COLORS[node.get_info('vulnerability_score')] + + host = node.get_info('hostname') or node.get_info('ip') or "" + + self.__hosts_store.append([i, + ports, + host, + color, + True]) + + self.__hosts_column = list() + + for i in range(0, len(HOSTS_HEADER)): + + column = Gtk.TreeViewColumn(title=HOSTS_HEADER[i], + cell_renderer=self.__cell, + text=i) + + self.__hosts_column.append(column) + + self.__hosts_column[i].set_reorderable(True) + self.__hosts_column[i].set_resizable(True) + self.__hosts_column[i].set_attributes(self.__cell, + text=i, + background=3, + editable=4) + + self.__hosts_treeview.append_column(self.__hosts_column[2]) + + self.__hosts_store.set_sort_func(2, self.__host_sort) + + self.__hosts_column[2].set_sort_column_id(2) + + self.add_with_viewport(self.__hosts_treeview) + + if len(self.__hosts_treeview.get_model()) > 0: + self.__hosts_treeview.set_cursor((0,)) + self.__cursor_callback(self.__hosts_treeview) + + def __cursor_callback(self, widget): + """ + """ + path = widget.get_cursor()[0] + if path is None: + return + + iter = self.__hosts_store.get_iter(path) + + node = self.__nodes[self.__hosts_store.get_value(iter, 0)] + + self.__parent.change_notebook(node) + + def __host_sort(self, treemodel, iter1, iter2, *_): + """ + """ + value1 = treemodel.get_value(iter1, 2) + value2 = treemodel.get_value(iter2, 2) + + value1_is_ip = IP_RE.match(value1) + value2_is_ip = IP_RE.match(value2) + + if value1_is_ip and value2_is_ip: + return ipv4_compare(value1, value2) + + if value1_is_ip: + return -1 + + if value2_is_ip: + return 1 + + if value1 < value2: + return -1 + + if value1 > value2: + return 1 + + return 0 diff --git a/zenmap/radialnet/gui/Image.py b/zenmap/radialnet/gui/Image.py new file mode 100644 index 0000000..72020ca --- /dev/null +++ b/zenmap/radialnet/gui/Image.py @@ -0,0 +1,164 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import GdkPixbuf + +import os +import array + +from zenmapCore.Paths import Path + + +FORMAT_RGBA = 4 +FORMAT_RGB = 3 + + +def get_pixels_for_cairo_image_surface(pixbuf): + """ + This method return the image stride and a python array.ArrayType + containing the icon pixels of a gtk.gdk.Pixbuf that can be used by + cairo.ImageSurface.create_for_data() method. + """ + data = array.array('B') + image_format = pixbuf.get_rowstride() // pixbuf.get_width() + + i = 0 + j = 0 + while i < len(pixbuf.get_pixels()): + + b, g, r = pixbuf.get_pixels()[i:i + FORMAT_RGB] + + if image_format == FORMAT_RGBA: + a = pixbuf.get_pixels()[i + FORMAT_RGBA - 1] + elif image_format == FORMAT_RGB: + a = 255 + else: + raise TypeError('unknown image format') + + data[j:j + FORMAT_RGBA] = array.array('B', [r, g, b, a]) + + i += image_format + j += FORMAT_RGBA + + return (FORMAT_RGBA * pixbuf.get_width(), data) + + +class Image: + """ + """ + def __init__(self, path=None): + """ + """ + self.__path = path + self.__cache = dict() + + def set_path(self, path): + """ + """ + self.__path = path + + def get_pixbuf(self, icon, image_type='png'): + """ + """ + if self.__path is None: + return False + + if icon + image_type not in self.__cache.keys(): + + file = self.get_icon(icon, image_type) + self.__cache[icon + image_type] = \ + GdkPixbuf.Pixbuf.new_from_file(file) + + return self.__cache[icon + image_type] + + def get_icon(self, icon, image_type='png'): + """ + """ + if self.__path is None: + return False + + return os.path.join(self.__path, icon + "." + image_type) + + +class Pixmaps(Image): + """ + """ + def __init__(self): + """ + """ + Image.__init__(self, os.path.join(Path.pixmaps_dir, "radialnet")) + + +class Icons(Image): + """ + """ + def __init__(self): + """ + """ + Image.__init__(self, os.path.join(Path.pixmaps_dir, "radialnet")) + + +class Application(Image): + """ + """ + def __init__(self): + """ + """ + Image.__init__(self, os.path.join(Path.pixmaps_dir, "radialnet")) diff --git a/zenmap/radialnet/gui/LegendWindow.py b/zenmap/radialnet/gui/LegendWindow.py new file mode 100644 index 0000000..3401e43 --- /dev/null +++ b/zenmap/radialnet/gui/LegendWindow.py @@ -0,0 +1,242 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, Gdk, Pango + +import math +import cairo + +import zenmapCore.I18N # lgtm[py/unused-import] + +from radialnet.gui.Image import Pixmaps +DIMENSION_NORMAL = (350, 450) + + +def draw_pixmap(context, x, y, name, label): + # This is pretty hideous workaround + Gdk.cairo_set_source_pixbuf(context, Pixmaps().get_pixbuf(name), x, y) + #context.set_source_pixbuf() + context.paint() + context.move_to(x + 50, y + 10) + context.set_source_rgb(0, 0, 0) + context.show_text(label) + + +def reset_font(context): + context.select_font_face( + "Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_SLANT_NORMAL) + context.set_font_size(11) + + +def draw_heading(context, x, y, label): + context.select_font_face( + "Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) + context.set_font_size(13) + context.move_to(x - 15, y) + context.set_source_rgb(0, 0, 0) + context.show_text(label) + reset_font(context) + + +def draw_circle(context, x, y, size, color, label): + context.set_source_rgb(0, 0, 0) + context.move_to(x, y) + context.arc(x, y, size, 0, 2 * math.pi) + context.stroke_preserve() + context.set_source_rgb(*color) + context.fill() + context.set_source_rgb(0, 0, 0) + context.move_to(x + 50, y + 5) + context.show_text(label) + + +def draw_square(context, x, y, size, color): + context.set_source_rgb(0, 0, 0) + context.rectangle(x, y - size / 2, size, size) + context.stroke_preserve() + context.set_source_rgb(*color) + context.fill() + + +def draw_line(context, x, y, dash, color, label): + context.set_source_rgb(*color) + context.move_to(x - 20, y) + context.set_dash(dash) + context.line_to(x + 25, y) + context.stroke() + context.set_dash([]) + context.set_source_rgb(0, 0, 0) + context.move_to(x + 50, y + 5) + context.show_text(label) + + +class LegendWindow(Gtk.Window): + """ + """ + def __init__(self): + """ + """ + Gtk.Window.__init__(self, type=Gtk.WindowType.TOPLEVEL) + self.set_default_size(DIMENSION_NORMAL[0], DIMENSION_NORMAL[1]) + self.__title_font = Pango.FontDescription("Monospace Bold") + self.set_title(_("Topology Legend")) + + self.vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0) + self.add(self.vbox) + + self.drawing_area = Gtk.DrawingArea() + self.vbox.pack_start(self.drawing_area, True, True, 0) + self.drawing_area.connect("draw", self.draw_event_handler) + self.more_uri = Gtk.LinkButton.new_with_label( + "https://nmap.org/book/zenmap-topology.html#zenmap-topology-legend", + _("View full legend online")) + self.vbox.pack_start(self.more_uri, False, False, 0) + + def draw_event_handler(self, widget, graphic_context): + """ + """ + x, y = 45, 20 + draw_heading(graphic_context, x, y, _("Hosts")) + + # white circle + y += 20 + draw_circle(graphic_context, x, y, 3, (1, 1, 1), + _("host was not port scanned")) + # green circle + y += 20 + draw_circle(graphic_context, x, y, 4, (0, 1, 0), + _("host with fewer than 3 open ports")) + # yellow circle + y += 20 + draw_circle(graphic_context, x, y, 5, (1, 1, 0), + _("host with 3 to 6 open ports")) + # red circle + y += 20 + draw_circle(graphic_context, x, y, 6, (1, 0, 0), + _("host with more than 6 open ports")) + + # green square + y += 20 + rx = x - 20 + draw_square(graphic_context, rx, y, 10, (0, 1, 0)) + rx += 10 + 5 + # yellow square + draw_square(graphic_context, rx, y, 12, (1, 1, 0)) + rx += 12 + 5 + # red square + draw_square(graphic_context, rx, y, 14, (1, 0, 0)) + + graphic_context.move_to(x + 50, y + 5) + graphic_context.set_source_rgb(0, 0, 0) + graphic_context.show_text(_("host is a router, switch, or WAP")) + + # connections between hosts + y += 30 + draw_heading(graphic_context, x, y, _("Traceroute connections")) + + y += 20 + graphic_context.move_to(x, y) + graphic_context.show_text(_("Thicker line means higher round-trip time")) + # primary traceroute (blue line) + y += 20 + draw_line(graphic_context, x, y, [], (0, 0, 1), + _("primary traceroute connection")) + # Alternate route (orange line) + y += 20 + draw_line(graphic_context, x, y, [], (1, 0.5, 0), + _("alternate path")) + # no traceroute + y += 20 + draw_line(graphic_context, x, y, [4.0, 2.0], (0, 0, 0), + _("no traceroute information")) + # missing traceroute + y += 20 + graphic_context.set_source_rgb(0.5, 0.7, 0.95) + graphic_context.move_to(x - 15, y) + graphic_context.arc(x - 25, y, 5, 0, 2 * math.pi) + graphic_context.stroke_preserve() + draw_line(graphic_context, x, y, [4.0, 2.0], (0.5, 0.7, 0.95), + _("missing traceroute hop")) + + # special purpose hosts + y += 30 + draw_heading(graphic_context, x, y, _("Additional host icons")) + + # router image + y += 20 + draw_pixmap(graphic_context, x, y, "router", _("router")) + + # switch image + y += 20 + draw_pixmap(graphic_context, x, y, "switch", _("switch")) + + # wap image + y += 20 + draw_pixmap(graphic_context, x, y, "wireless", + _("wireless access point")) + + # firewall image + y += 20 + draw_pixmap(graphic_context, x, y, "firewall", _("firewall")) + + # host with filtered ports + y += 20 + draw_pixmap(graphic_context, x, y, "padlock", + _("host with some filtered ports")) diff --git a/zenmap/radialnet/gui/NodeNotebook.py b/zenmap/radialnet/gui/NodeNotebook.py new file mode 100644 index 0000000..ea208ec --- /dev/null +++ b/zenmap/radialnet/gui/NodeNotebook.py @@ -0,0 +1,742 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, GObject, Pango + +from radialnet.bestwidgets.boxes import BWVBox, BWHBox, BWScrolledWindow, BWTable +from radialnet.bestwidgets.expanders import BWExpander +from radialnet.bestwidgets.labels import BWLabel, BWSectionLabel +from radialnet.bestwidgets.textview import BWTextEditor +import zenmapCore.I18N # lgtm[py/unused-import] + + +PORTS_HEADER = [ + _('Port'), _('Protocol'), _('State'), _('Service'), _('Method')] +EXTRAPORTS_HEADER = [_('Count'), _('State'), _('Reasons')] + +SERVICE_COLORS = {'open': '#ffd5d5', # noqa + 'closed': '#d5ffd5', # noqa + 'filtered': '#ffffd5', # noqa + 'unfiltered': '#ffd5d5', # noqa + 'open|filtered': '#ffd5d5', # noqa + 'closed|filtered': '#d5ffd5'} # noqa +UNKNOWN_SERVICE_COLOR = '#d5d5d5' + +TRACE_HEADER = ['TTL', 'RTT', 'IP', _('Hostname')] + +TRACE_TEXT = _( + "Traceroute on port %s/%s totalized %d known hops.") + +NO_TRACE_TEXT = _("No traceroute information available.") + +HOP_COLOR = {'known': '#ffffff', # noqa + 'unknown': '#cccccc'} # noqa + +SYSTEM_ADDRESS_TEXT = "[%s] %s" + +OSMATCH_HEADER = ['%', _('Name'), _('DB Line')] +OSCLASS_HEADER = ['%', _('Vendor'), _('Type'), _('Family'), _('Version')] + +USED_PORTS_TEXT = "%d/%s %s" + +TCP_SEQ_NOTE = _("""\ +* TCP sequence index equal to %d and difficulty is "%s".\ +""") + + +def get_service_color(state): + color = SERVICE_COLORS.get(state) + if color is None: + color = UNKNOWN_SERVICE_COLOR + return color + + +class NodeNotebook(Gtk.Notebook): + """ + """ + def __init__(self, node): + """ + """ + Gtk.Notebook.__init__(self) + self.set_tab_pos(Gtk.PositionType.TOP) + + self.__node = node + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + # create body elements + self.__services_page = ServicesPage(self.__node) + self.__system_page = SystemPage(self.__node) + self.__trace_page = TraceroutePage(self.__node) + + # packing notebook elements + self.append_page(self.__system_page, BWLabel(_('General'))) + self.append_page(self.__services_page, BWLabel(_('Services'))) + self.append_page(self.__trace_page, BWLabel(_('Traceroute'))) + + +class ServicesPage(Gtk.Notebook): + """ + """ + def __init__(self, node): + """ + """ + Gtk.Notebook.__init__(self) + self.set_border_width(6) + self.set_tab_pos(Gtk.PositionType.TOP) + + self.__node = node + self.__font = Pango.FontDescription('Monospace') + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__cell = Gtk.CellRendererText() + + # texteditor widgets + self.__texteditor = BWTextEditor() + self.__texteditor.bw_modify_font(self.__font) + self.__texteditor.bw_set_editable(False) + self.__texteditor.set_border_width(0) + + self.__select_combobox = Gtk.ComboBoxText() + self.__select_combobox.connect('changed', self.__change_text_value) + + self.__viewer = BWVBox(spacing=6) + self.__viewer.set_border_width(6) + + self.__viewer.bw_pack_start_noexpand_nofill(self.__select_combobox) + self.__viewer.bw_pack_start_expand_fill(self.__texteditor) + + self.__text = list() + + # ports information + number_of_ports = len(self.__node.get_info('ports')) + self.__ports_label = BWLabel(_('Ports (%s)') % number_of_ports) + + self.__ports_scroll = BWScrolledWindow() + + self.__ports_store = Gtk.TreeStore.new([GObject.TYPE_INT, + GObject.TYPE_STRING, + GObject.TYPE_STRING, + GObject.TYPE_STRING, + GObject.TYPE_STRING, + GObject.TYPE_STRING, + GObject.TYPE_BOOLEAN]) + + self.__ports_treeview = Gtk.TreeView.new_with_model(self.__ports_store) + + for port in self.__node.get_info('ports'): + + color = get_service_color(port['state']['state']) + + service_name = port['service'].get('name', _('')) + + service_method = port['service'].get('method', _('')) + + reference = self.__ports_store.append(None, + [port['id'], + port['protocol'], + port['state']['state'], + service_name, + service_method, + color, + True]) + + for key in port['state']: + self.__ports_store.append(reference, + [port['id'], + 'state', + key, + port['state'][key], + '', + 'white', + True]) + + for key in port['service']: + + if key in ['servicefp']: + + text = _('[%d] service: %s') % (port['id'], key) + + self.__select_combobox.append_text(text) + self.__text.append(port['service'][key]) + + value = _('') + + else: + value = port['service'][key] + + self.__ports_store.append(reference, + [port['id'], + 'service', + key, + value, + '', + 'white', + True]) + + #for script in port['scripts']: + # text = _('[%d] script: %s') % (port['id'], script['id']) + # self.__select_combobox.append_text(text) + # self.__text.append(script['output']) + # + # self.__ports_store.append(reference, + # [port['id'], + # 'script', + # 'id', + # script['id'], + # _(''), + # 'white', + # True]) + + self.__ports_column = list() + + for i in range(len(PORTS_HEADER)): + + column = Gtk.TreeViewColumn(title=PORTS_HEADER[i], + cell_renderer=self.__cell, + text=i) + + self.__ports_column.append(column) + + self.__ports_column[i].set_reorderable(True) + self.__ports_column[i].set_resizable(True) + self.__ports_column[i].set_sort_column_id(i) + self.__ports_column[i].set_attributes(self.__cell, + text=i, + background=5, + editable=6) + + self.__ports_treeview.append_column(self.__ports_column[i]) + + self.__ports_scroll.add_with_viewport(self.__ports_treeview) + + # extraports information + number_of_xports = 0 + + self.__xports_scroll = BWScrolledWindow() + + self.__xports_store = Gtk.TreeStore.new([GObject.TYPE_INT, + GObject.TYPE_STRING, + GObject.TYPE_STRING, + GObject.TYPE_STRING, + GObject.TYPE_BOOLEAN]) + + self.__xports_treeview = Gtk.TreeView.new_with_model(self.__xports_store) + + for xports in self.__node.get_info('extraports'): + + color = get_service_color(xports['state']) + number_of_xports += xports['count'] + + reference = self.__xports_store.append( + None, [xports['count'], xports['state'], + ", ".join(xports['reason']), color, True]) + + for xreason in xports['all_reason']: + self.__xports_store.append(reference, + [xreason['count'], + xports['state'], + xreason['reason'], + 'white', + True]) + + self.__xports_column = list() + + for i in range(len(EXTRAPORTS_HEADER)): + + column = Gtk.TreeViewColumn(title=EXTRAPORTS_HEADER[i], + cell_renderer=self.__cell, + text=i) + + self.__xports_column.append(column) + + self.__xports_column[i].set_reorderable(True) + self.__xports_column[i].set_resizable(True) + self.__xports_column[i].set_sort_column_id(i) + self.__xports_column[i].set_attributes(self.__cell, + text=i, + background=3, + editable=4) + + self.__xports_treeview.append_column(self.__xports_column[i]) + + xports_label_text = _('Extraports (%s)') % number_of_xports + self.__xports_label = BWLabel(xports_label_text) + + self.__xports_scroll.add_with_viewport(self.__xports_treeview) + + self.append_page(self.__ports_scroll, self.__ports_label) + self.append_page(self.__xports_scroll, self.__xports_label) + self.append_page(self.__viewer, BWLabel(_('Special fields'))) + + if len(self.__text) > 0: + self.__select_combobox.set_active(0) + + def __change_text_value(self, widget): + """ + """ + id = self.__select_combobox.get_active() + + self.__texteditor.bw_set_text(self.__text[id]) + + +class SystemPage(BWScrolledWindow): + """ + """ + def __init__(self, node): + """ + """ + BWScrolledWindow.__init__(self) + + self.__node = node + self.__font = Pango.FontDescription('Monospace') + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__vbox = BWVBox() + self.__vbox.set_border_width(6) + + self.__cell = Gtk.CellRendererText() + + self.__general_frame = BWExpander(_('General information')) + self.__sequences_frame = BWExpander(_('Sequences')) + self.__os_frame = BWExpander(_('Operating System')) + + self.__sequences_frame.bw_add(Gtk.Label.new(_('No sequence information.'))) + self.__os_frame.bw_add(Gtk.Label.new(_('No OS information.'))) + + # general information widgets + self.__general = BWTable(3, 2) + + self.__address_label = BWSectionLabel(_('Address:')) + self.__address_list = Gtk.ComboBoxText.new_with_entry() + self.__address_list.get_child().set_editable(False) + + for address in self.__node.get_info('addresses'): + + params = address['type'], address['addr'] + address_text = SYSTEM_ADDRESS_TEXT % params + + if address['vendor'] is not None and address['vendor'] != '': + address_text += " (%s)" % address['vendor'] + + self.__address_list.append_text(address_text) + + self.__address_list.set_active(0) + + self.__general.bw_attach_next(self.__address_label, + yoptions=Gtk.AttachOptions.FILL, + xoptions=Gtk.AttachOptions.FILL) + self.__general.bw_attach_next(self.__address_list, yoptions=Gtk.AttachOptions.FILL) + + if self.__node.get_info('hostnames') is not None: + + self.__hostname_label = BWSectionLabel(_('Hostname:')) + self.__hostname_list = Gtk.ComboBoxText.new_with_entry() + self.__hostname_list.get_child().set_editable(False) + + for hostname in self.__node.get_info('hostnames'): + + params = hostname['type'], hostname['name'] + self.__hostname_list.append_text(SYSTEM_ADDRESS_TEXT % params) + + self.__hostname_list.set_active(0) + + self.__general.bw_attach_next(self.__hostname_label, + yoptions=Gtk.AttachOptions.FILL, + xoptions=Gtk.AttachOptions.FILL) + self.__general.bw_attach_next(self.__hostname_list, + yoptions=Gtk.AttachOptions.FILL) + + if self.__node.get_info('uptime') is not None: + + self.__uptime_label = BWSectionLabel(_('Last boot:')) + + seconds = self.__node.get_info('uptime')['seconds'] + lastboot = self.__node.get_info('uptime')['lastboot'] + + text = _('%s (%s seconds).') % (lastboot, seconds) + + self.__uptime_value = BWLabel(text) + self.__uptime_value.set_selectable(True) + self.__uptime_value.set_line_wrap(False) + + self.__general.bw_attach_next(self.__uptime_label, + yoptions=Gtk.AttachOptions.FILL, + xoptions=Gtk.AttachOptions.FILL) + self.__general.bw_attach_next(self.__uptime_value, + yoptions=Gtk.AttachOptions.FILL) + + self.__general_frame.bw_add(self.__general) + self.__general_frame.set_expanded(True) + + sequences = self.__node.get_info('sequences') + if len(sequences) > 0: + self.__sequences_frame.bw_add( + self.__create_sequences_widget(sequences)) + + # operating system information widgets + self.__os = Gtk.Notebook() + + os = self.__node.get_info('os') + + if os is not None: + + if 'matches' in os: + + self.__match_scroll = BWScrolledWindow() + + self.__match_store = Gtk.ListStore.new([str, str, int, bool]) + self.__match_treeview = Gtk.TreeView.new_with_model(self.__match_store) + + for os_match in os['matches']: + + self.__match_store.append([os_match['accuracy'], + os_match['name'], + #os_match['db_line'], + 0, # unsupported + True]) + + self.__match_column = list() + + for i in range(len(OSMATCH_HEADER)): + + column = Gtk.TreeViewColumn(title=OSMATCH_HEADER[i], + cell_renderer=self.__cell, + text=i) + + self.__match_column.append(column) + + self.__match_column[i].set_reorderable(True) + self.__match_column[i].set_resizable(True) + self.__match_column[i].set_attributes(self.__cell, + text=i, + editable=3) + + self.__match_column[i].set_sort_column_id(i) + self.__match_treeview.append_column(self.__match_column[i]) + + self.__match_scroll.add_with_viewport(self.__match_treeview) + + self.__os.append_page(self.__match_scroll, BWLabel(_('Match'))) + + if 'classes' in os: + + self.__class_scroll = BWScrolledWindow() + + self.__class_store = Gtk.ListStore.new([str, str, str, str, str, bool]) + self.__class_treeview = Gtk.TreeView.new_with_model(self.__class_store) + + for os_class in os['classes']: + + os_gen = os_class.get('os_gen', '') + + self.__class_store.append([os_class['accuracy'], + os_class['vendor'], + os_class['type'], + os_class['os_family'], + os_gen, + True]) + + self.__class_column = list() + + for i in range(len(OSCLASS_HEADER)): + + column = Gtk.TreeViewColumn(title=OSCLASS_HEADER[i], + cell_renderer=self.__cell, + text=i) + + self.__class_column.append(column) + + self.__class_column[i].set_reorderable(True) + self.__class_column[i].set_resizable(True) + self.__class_column[i].set_attributes(self.__cell, + text=i, + editable=5) + + self.__class_column[i].set_sort_column_id(i) + self.__class_treeview.append_column(self.__class_column[i]) + + self.__class_scroll.add_with_viewport(self.__class_treeview) + + self.__os.append_page(self.__class_scroll, BWLabel(_('Class'))) + + self.__fp_viewer = BWTextEditor() + self.__fp_viewer.bw_modify_font(self.__font) + self.__fp_viewer.bw_set_editable(False) + self.__fp_viewer.bw_set_text(os['fingerprint']) + + self.__fp_ports = BWHBox() + self.__fp_label = BWSectionLabel(_('Used ports:')) + + self.__fp_ports_list = Gtk.ComboBoxText.new_with_entry() + self.__fp_ports_list.get_child().set_editable(False) + + self.__fp_vbox = BWVBox() + + if 'used_ports' in os: + + used_ports = os['used_ports'] + + for port in used_ports: + + params = port['id'], port['protocol'], port['state'] + self.__fp_ports_list.append_text(USED_PORTS_TEXT % params) + + self.__fp_ports_list.set_active(0) + + self.__fp_ports.bw_pack_start_noexpand_nofill(self.__fp_label) + self.__fp_ports.bw_pack_start_expand_fill(self.__fp_ports_list) + + self.__fp_vbox.bw_pack_start_noexpand_nofill(self.__fp_ports) + + self.__os.append_page(self.__fp_viewer, BWLabel(_('Fingerprint'))) + self.__fp_vbox.bw_pack_start_expand_fill(self.__os) + + self.__os_frame.bw_add(self.__fp_vbox) + self.__os_frame.set_expanded(True) + + self.__vbox.bw_pack_start_noexpand_nofill(self.__general_frame) + self.__vbox.bw_pack_start_expand_fill(self.__os_frame) + self.__vbox.bw_pack_start_noexpand_nofill(self.__sequences_frame) + + self.add_with_viewport(self.__vbox) + + def __create_sequences_widget(self, sequences): + """Return a widget representing various OS detection sequences. The + sequences argument is a dict with zero or more of the keys 'tcp', + 'ip_id', and 'tcp_ts'.""" + # sequences information widgets + table = BWTable(5, 3) + + table.attach(BWSectionLabel(_('Class')), 1, 2, 0, 1) + table.attach(BWSectionLabel(_('Values')), 2, 3, 0, 1) + + table.attach(BWSectionLabel('TCP *'), 0, 1, 1, 2) + table.attach(BWSectionLabel('IP ID'), 0, 1, 2, 3) + table.attach(BWSectionLabel(_('TCP Timestamp')), 0, 1, 3, 4) + + tcp = sequences.get('tcp') + if tcp is not None: + tcp_class = BWLabel(tcp['class']) + tcp_class.set_selectable(True) + + table.attach(tcp_class, 1, 2, 1, 2) + + tcp_values = Gtk.ComboBoxText.new_with_entry() + + for value in tcp['values']: + tcp_values.append_text(value) + + tcp_values.set_active(0) + + table.attach(tcp_values, 2, 3, 1, 2) + + tcp_note = BWLabel() + tcp_note.set_selectable(True) + tcp_note.set_line_wrap(False) + tcp_note.set_alignment(1.0, 0.5) + tcp_note.set_markup( + TCP_SEQ_NOTE % (tcp['index'], tcp['difficulty'])) + + table.attach(tcp_note, 0, 3, 4, 5) + + ip_id = sequences.get('ip_id') + if ip_id is not None: + ip_id_class = BWLabel(ip_id['class']) + ip_id_class.set_selectable(True) + + table.attach(ip_id_class, 1, 2, 2, 3) + + ip_id_values = Gtk.ComboBoxText.new_with_entry() + + for value in ip_id['values']: + ip_id_values.append_text(value) + + ip_id_values.set_active(0) + + table.attach(ip_id_values, 2, 3, 2, 3) + + tcp_ts = sequences.get('tcp_ts') + if tcp_ts is not None: + tcp_ts_class = BWLabel(tcp_ts['class']) + tcp_ts_class.set_selectable(True) + + table.attach(tcp_ts_class, 1, 2, 3, 4) + + if tcp_ts['values'] is not None: + + tcp_ts_values = Gtk.ComboBoxText.new_with_entry() + + for value in tcp_ts['values']: + tcp_ts_values.append_text(value) + + tcp_ts_values.set_active(0) + + table.attach(tcp_ts_values, 2, 3, 3, 4) + + return table + + +class TraceroutePage(BWVBox): + """ + """ + def __init__(self, node): + """ + """ + BWVBox.__init__(self) + self.set_border_width(6) + + self.__node = node + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + trace = self.__node.get_info('trace') + hops = None + if trace is not None: + hops = trace.get("hops") + if hops is None or len(hops) == 0: + + self.__trace_label = Gtk.Label.new(NO_TRACE_TEXT) + self.pack_start(self.__trace_label, True, True, 0) + + else: + + # add hops + hops = self.__node.get_info('trace')['hops'] + ttls = [int(i['ttl']) for i in hops] + + self.__cell = Gtk.CellRendererText() + + self.__trace_scroll = BWScrolledWindow() + self.__trace_scroll.set_border_width(0) + + self.__trace_store = Gtk.ListStore.new([int, str, str, str, str, bool]) + self.__trace_treeview = Gtk.TreeView.new_with_model(self.__trace_store) + + count = 0 + + for i in range(1, max(ttls) + 1): + + if i in ttls: + + hop = hops[count] + count += 1 + + self.__trace_store.append([hop['ttl'], + hop['rtt'], + hop['ip'], + hop['hostname'], + HOP_COLOR['known'], + True]) + + else: + self.__trace_store.append([i, + '', + _(''), + '', + HOP_COLOR['unknown'], + True]) + + self.__trace_column = list() + + for i in range(len(TRACE_HEADER)): + + column = Gtk.TreeViewColumn(title=TRACE_HEADER[i], + cell_renderer=self.__cell, + text=i) + + self.__trace_column.append(column) + + self.__trace_column[i].set_reorderable(True) + self.__trace_column[i].set_resizable(True) + self.__trace_column[i].set_attributes(self.__cell, + text=i, + background=4, + editable=5) + + self.__trace_treeview.append_column(self.__trace_column[i]) + + self.__trace_column[0].set_sort_column_id(0) + + self.__trace_scroll.add_with_viewport(self.__trace_treeview) + + self.__trace_info = (self.__node.get_info('trace')['port'], + self.__node.get_info('trace')['protocol'], + len(self.__node.get_info('trace')['hops'])) + + self.__trace_label = BWLabel(TRACE_TEXT % self.__trace_info) + self.__trace_label.set_use_markup(True) + + self.bw_pack_start_expand_fill(self.__trace_scroll) + self.bw_pack_start_noexpand_nofill(self.__trace_label) diff --git a/zenmap/radialnet/gui/NodeWindow.py b/zenmap/radialnet/gui/NodeWindow.py new file mode 100644 index 0000000..7c38078 --- /dev/null +++ b/zenmap/radialnet/gui/NodeWindow.py @@ -0,0 +1,129 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, Gdk, Pango + +import radialnet.util.drawing as drawing + +from radialnet.bestwidgets.windows import BWWindow +from radialnet.bestwidgets.boxes import BWVBox, BWHBox +from radialnet.bestwidgets.labels import BWSectionLabel +from radialnet.gui.Image import Application +from radialnet.gui.NodeNotebook import NodeNotebook + + +DIMENSION_NORMAL = (600, 400) + + +class NodeWindow(BWWindow): + """ + """ + def __init__(self, node, position): + """ + """ + BWWindow.__init__(self, Gtk.WindowType.TOPLEVEL) + self.move(position[0], position[1]) + self.set_default_size(DIMENSION_NORMAL[0], DIMENSION_NORMAL[1]) + + self.__node = node + + self.__title_font = Pango.FontDescription('Monospace Bold') + + self.__icon = Application() + self.__create_widgets() + + def __create_widgets(self): + """ + """ + self.__content = BWVBox() + self.__head = BWHBox(spacing=2) + + self.__notebook = NodeNotebook(self.__node) + + # create head elements + + # icon with node's score color + self.__color_box = Gtk.EventBox() + self.__color_image = Gtk.Image() + self.__color_image.set_from_file(self.__icon.get_icon('border')) + self.__color_box.add(self.__color_image) + self.__color_box.set_size_request(15, 15) + r, g, b = drawing.cairo_to_gdk_color( + self.__node.get_draw_info('color')) + self.__color_box.modify_bg(Gtk.StateType.NORMAL, Gdk.Color(r, g, b)) + + # title with the node ip and hostname + self.__title = self.__node.get_host().get_hostname() + + self.set_title(self.__title) + + self.__title_label = BWSectionLabel(self.__title) + self.__title_label.modify_font(self.__title_font) + + # packing head elements + self.__head.bw_pack_start_noexpand_nofill(self.__color_box) + self.__head.bw_pack_start_expand_fill(self.__title_label) + + # packing all to content + self.__content.bw_pack_start_noexpand_nofill(self.__head) + self.__content.bw_pack_start_expand_fill(self.__notebook) + + # add content to window + self.add(self.__content) diff --git a/zenmap/radialnet/gui/RadialNet.py b/zenmap/radialnet/gui/RadialNet.py new file mode 100644 index 0000000..1a395dd --- /dev/null +++ b/zenmap/radialnet/gui/RadialNet.py @@ -0,0 +1,2020 @@ +# vim: set encoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, GLib, Gdk + +import math +import cairo + +from functools import reduce + +import radialnet.util.geometry as geometry +import radialnet.util.misc as misc + +from radialnet.core.Coordinate import PolarCoordinate, CartesianCoordinate +from radialnet.core.Interpolation import Linear2DInterpolator +from radialnet.core.Graph import Node +from radialnet.gui.NodeWindow import NodeWindow +from radialnet.gui.Image import Icons, get_pixels_for_cairo_image_surface + +REGION_COLORS = [(1.0, 0.0, 0.0), (1.0, 1.0, 0.0), (0.0, 1.0, 0.0)] +REGION_RED = 0 +REGION_YELLOW = 1 +REGION_GREEN = 2 + +SQUARE_TYPES = ['router', 'switch', 'wap'] + +ICON_DICT = {'router': 'router', + 'switch': 'switch', + 'wap': 'wireless', + 'firewall': 'firewall'} + +POINTER_JUMP_TO = 0 +POINTER_INFO = 1 +POINTER_GROUP = 2 +POINTER_FILL = 3 + +LAYOUT_SYMMETRIC = 0 +LAYOUT_WEIGHTED = 1 + +INTERPOLATION_CARTESIAN = 0 +INTERPOLATION_POLAR = 1 + +FILE_TYPE_PDF = 1 +FILE_TYPE_PNG = 2 +FILE_TYPE_PS = 3 +FILE_TYPE_SVG = 4 + + +class RadialNet(Gtk.DrawingArea): + """ + Radial network visualization widget + """ + def __init__(self, layout=LAYOUT_SYMMETRIC): + """ + Constructor method of RadialNet widget class + @type number_of_rings: number + @param number_of_rings: Number of rings in radial layout + """ + self.__center_of_widget = (0, 0) + self.__graph = None + + self.__number_of_rings = 0 + self.__ring_gap = 30 + self.__min_ring_gap = 10 + + self.__layout = layout + self.__interpolation = INTERPOLATION_POLAR + self.__interpolation_slow_in_out = True + + self.__animating = False + self.__animation_rate = 1000 // 60 # 60Hz (human perception factor) + self.__number_of_frames = 60 + + self.__scale = 1.0 + # rotated so that single-host traceroute doesn't have overlapping hosts + self.__rotate = 225 + self.__translation = (0, 0) + + self.__button1_press = False + self.__button2_press = False + self.__button3_press = False + + self.__last_motion_point = None + + self.__fisheye = False + self.__fisheye_ring = 0 + self.__fisheye_spread = 0.5 + self.__fisheye_interest = 2 + + self.__show_address = True + self.__show_hostname = True + self.__show_icon = True + self.__show_latency = False + self.__show_ring = True + self.__show_region = True + self.__region_color = REGION_RED + + self.__node_views = dict() + self.__last_group_node = None + + self.__pointer_status = POINTER_JUMP_TO + + self.__sorted_nodes = list() + + self.__icon = Icons() + + super(RadialNet, self).__init__() + + self.connect('draw', self.draw) + self.connect('button_press_event', self.button_press) + self.connect('button_release_event', self.button_release) + self.connect('motion_notify_event', self.motion_notify) + self.connect('enter_notify_event', self.enter_notify) + self.connect('leave_notify_event', self.leave_notify) + self.connect('key_press_event', self.key_press) + self.connect('key_release_event', self.key_release) + self.connect('scroll_event', self.scroll_event) + + self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK | + Gdk.EventMask.BUTTON_RELEASE_MASK | + Gdk.EventMask.ENTER_NOTIFY_MASK | + Gdk.EventMask.LEAVE_NOTIFY_MASK | + Gdk.EventMask.KEY_PRESS_MASK | + Gdk.EventMask.KEY_RELEASE_MASK | + Gdk.EventMask.POINTER_MOTION_HINT_MASK | + Gdk.EventMask.POINTER_MOTION_MASK | + Gdk.EventMask.SCROLL_MASK) + + self.set_can_focus(True) + self.grab_focus() + + def graph_is_not_empty(function): + """ + Decorator function to prevent the execution when graph not is set + @type function: function + @param function: Protected function + """ + def check_graph_status(*args): + if args[0].__graph is None: + return False + return function(*args) + + return check_graph_status + + def not_is_in_animation(function): + """ + Decorator function to prevent the execution when graph is animating + @type function: function + @param function: Protected function + """ + def check_animation_status(*args): + if args[0].__animating: + return False + return function(*args) + + return check_animation_status + + def save_drawing_to_file(self, file, type=FILE_TYPE_PNG): + """ + """ + allocation = self.get_allocation() + + if type == FILE_TYPE_PDF: + self.surface = cairo.PDFSurface(file, + allocation.width, + allocation.height) + elif type == FILE_TYPE_PNG: + self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, + allocation.width, + allocation.height) + elif type == FILE_TYPE_PS: + self.surface = cairo.PSSurface(file, + allocation.width, + allocation.height) + elif type == FILE_TYPE_SVG: + self.surface = cairo.SVGSurface(file, + allocation.width, + allocation.height) + else: + raise TypeError('unknown surface type') + + context = cairo.Context(self.surface) + + context.rectangle(0, 0, allocation.width, allocation.height) + context.set_source_rgb(1.0, 1.0, 1.0) + context.fill() + + self.__draw(context) + + if type == FILE_TYPE_PNG: + self.surface.write_to_png(file) + + self.surface.flush() + self.surface.finish() + + return True + + def get_slow_inout(self): + """ + """ + return self.__interpolation_slow_in_out + + def set_slow_inout(self, value): + """ + """ + self.__interpolation_slow_in_out = value + + def get_region_color(self): + """ + """ + return self.__region_color + + def set_region_color(self, value): + """ + """ + self.__region_color = value + + def get_show_region(self): + """ + """ + return self.__show_region + + def set_show_region(self, value): + """ + """ + self.__show_region = value + self.queue_draw() + + def get_pointer_status(self): + """ + """ + return self.__pointer_status + + def set_pointer_status(self, pointer_status): + """ + """ + self.__pointer_status = pointer_status + + def get_show_address(self): + """ + """ + return self.__show_address + + def get_show_hostname(self): + """ + """ + return self.__show_hostname + + def get_show_ring(self): + """ + """ + return self.__show_ring + + def set_show_address(self, value): + """ + """ + self.__show_address = value + self.queue_draw() + + def set_show_hostname(self, value): + """ + """ + self.__show_hostname = value + self.queue_draw() + + def set_show_ring(self, value): + """ + """ + self.__show_ring = value + self.queue_draw() + + def get_min_ring_gap(self): + """ + """ + return self.__min_ring_gap + + @graph_is_not_empty + @not_is_in_animation + def set_min_ring_gap(self, value): + """ + """ + self.__min_ring_gap = int(value) + + if self.__ring_gap < self.__min_ring_gap: + self.__ring_gap = self.__min_ring_gap + + self.__update_nodes_positions() + self.queue_draw() + + return True + + def get_number_of_frames(self): + """ + """ + return self.__number_of_frames + + @not_is_in_animation + def set_number_of_frames(self, number_of_frames): + """ + """ + if number_of_frames > 2: + + self.__number_of_frames = int(number_of_frames) + return True + + self.__number_of_frames = 3 + return False + + @not_is_in_animation + def update_layout(self): + """ + """ + if self.__graph is None: + return + self.__animating = True + self.__calc_interpolation(self.__graph.get_main_node()) + self.__livens_up() + + @not_is_in_animation + def set_layout(self, layout): + """ + """ + if self.__layout != layout: + + self.__layout = layout + + if self.__graph is not None: + + self.__animating = True + self.__calc_interpolation(self.__graph.get_main_node()) + self.__livens_up() + + return True + + return False + + def get_layout(self): + """ + """ + return self.__layout + + @not_is_in_animation + def set_interpolation(self, interpolation): + """ + """ + self.__interpolation = interpolation + + return True + + def get_interpolation(self): + """ + """ + return self.__interpolation + + def get_number_of_rings(self): + """ + """ + return self.__number_of_rings + + def get_fisheye_ring(self): + """ + """ + return self.__fisheye_ring + + def get_fisheye_interest(self): + """ + """ + return self.__fisheye_interest + + def get_fisheye_spread(self): + """ + """ + return self.__fisheye_spread + + def get_fisheye(self): + """ + """ + return self.__fisheye + + def set_fisheye(self, enable): + """ + """ + self.__fisheye = enable + + self.__update_nodes_positions() + self.queue_draw() + + def set_fisheye_ring(self, value): + """ + """ + self.__fisheye_ring = value + self.__check_fisheye_ring() + + self.__update_nodes_positions() + self.queue_draw() + + def set_fisheye_interest(self, value): + """ + """ + self.__fisheye_interest = value + + self.__update_nodes_positions() + self.queue_draw() + + def set_fisheye_spread(self, value): + """ + """ + self.__fisheye_spread = value + + self.__update_nodes_positions() + self.queue_draw() + + def get_show_icon(self): + """ + """ + return self.__show_icon + + def set_show_icon(self, value): + """ + """ + self.__show_icon = value + self.queue_draw() + + def get_show_latency(self): + """ + """ + return self.__show_latency + + def set_show_latency(self, value): + """ + """ + self.__show_latency = value + self.queue_draw() + + def get_scale(self): + """ + """ + return self.__scale + + def get_zoom(self): + """ + """ + return int(round(self.__scale * 100)) + + def set_scale(self, scale): + """ + """ + if scale >= 0.01: + + self.__scale = scale + self.queue_draw() + + def set_zoom(self, zoom): + """ + """ + if float(zoom) >= 1: + + self.set_scale(float(zoom) / 100.0) + self.queue_draw() + + def get_ring_gap(self): + """ + """ + return self.__ring_gap + + @not_is_in_animation + def set_ring_gap(self, ring_gap): + """ + """ + if ring_gap >= self.__min_ring_gap: + + self.__ring_gap = ring_gap + self.__update_nodes_positions() + self.queue_draw() + + def scroll_event(self, widget, event): + """ + """ + if event.direction == Gdk.ScrollDirection.UP: + self.set_scale(self.__scale + 0.01) + + if event.direction == Gdk.ScrollDirection.DOWN: + self.set_scale(self.__scale - 0.01) + + self.queue_draw() + + @graph_is_not_empty + @not_is_in_animation + def key_press(self, widget, event): + """ + """ + key = Gdk.keyval_name(event.keyval) + + if key == 'KP_Add': + self.set_ring_gap(self.__ring_gap + 1) + + elif key == 'KP_Subtract': + self.set_ring_gap(self.__ring_gap - 1) + + elif key == 'Page_Up': + self.set_scale(self.__scale + 0.01) + + elif key == 'Page_Down': + self.set_scale(self.__scale - 0.01) + + self.queue_draw() + + return True + + @graph_is_not_empty + def key_release(self, widget, event): + """ + """ + key = Gdk.keyval_name(event.keyval) + + if key == 'c': + self.__translation = (0, 0) + + elif key == 'r': + self.__show_ring = not self.__show_ring + + elif key == 'a': + self.__show_address = not self.__show_address + + elif key == 'h': + self.__show_hostname = not self.__show_hostname + + elif key == 'i': + self.__show_icon = not self.__show_icon + + elif key == 'l': + self.__show_latency = not self.__show_latency + + self.queue_draw() + + return True + + @graph_is_not_empty + @not_is_in_animation + def enter_notify(self, widget, event): + """ + """ + self.grab_focus() + return False + + @graph_is_not_empty + @not_is_in_animation + def leave_notify(self, widget, event): + """ + """ + for node in self.__graph.get_nodes(): + node.set_draw_info({'over': False}) + + self.queue_draw() + + return False + + @graph_is_not_empty + def button_press(self, widget, event): + """ + Drawing callback + @type widget: GtkWidget + @param widget: Gtk widget superclass + @type event: GtkEvent + @param event: Gtk event of widget + @rtype: boolean + @return: Indicator of the event propagation + """ + result = self.__get_node_by_coordinate(self.get_pointer()) + + if event.button == 1: + self.__button1_press = True + + # animate if node is pressed + if self.__pointer_status == POINTER_JUMP_TO and event.button == 1: + + # prevent double animation + if self.__animating: + return False + + if result is not None: + + node, point = result + main_node = self.__graph.get_main_node() + + if node != main_node: + + if node.get_draw_info('group'): + + node.set_draw_info({'group': False}) + node.set_subtree_info({'grouped': False, + 'group_node': None}) + + self.__animating = True + self.__calc_interpolation(node) + self.__livens_up() + + # group node if it's pressed + elif self.__pointer_status == POINTER_GROUP and event.button == 1: + + # prevent group on animation + if self.__animating: + return False + + if result is not None: + + node, point = result + main_node = self.__graph.get_main_node() + + if node != main_node: + + if node.get_draw_info('group'): + + node.set_draw_info({'group': False}) + node.set_subtree_info({'grouped': False, + 'group_node': None}) + + else: + + self.__last_group_node = node + + node.set_draw_info({'group': True}) + node.set_subtree_info({'grouped': True, + 'group_node': node}) + + self.__animating = True + self.__calc_interpolation(self.__graph.get_main_node()) + self.__livens_up() + + # setting to show node's region + elif self.__pointer_status == POINTER_FILL and event.button == 1: + + if result is not None: + + node, point = result + + if node.get_draw_info('region') == self.__region_color: + node.set_draw_info({'region': None}) + + else: + node.set_draw_info({'region': self.__region_color}) + + self.queue_draw() + + # show node details + elif event.button == 3 or self.__pointer_status == POINTER_INFO: + + if event.button == 3: + self.__button3_press = True + + if result is not None: + + # first returned value is not meaningful and should be ignored + _, xw, yw = self.get_window().get_origin() + node, point = result + x, y = point + + if node in self.__node_views.keys(): + + self.__node_views[node].present() + + elif node.get_draw_info('scanned'): + + view = NodeWindow(node, (int(xw + x), int(yw + y))) + + def close_view(view, event, node): + view.destroy() + del self.__node_views[node] + + view.connect("delete-event", close_view, node) + view.show_all() + self.__node_views[node] = view + + return False + + @graph_is_not_empty + def button_release(self, widget, event): + """ + Drawing callback + @type widget: GtkWidget + @param widget: Gtk widget superclass + @type event: GtkEvent + @param event: Gtk event of widget + @rtype: boolean + @return: Indicator of the event propagation + """ + if event.button == 1: + self.__button1_press = False + + if event.button == 2: + self.__button2_press = False + + if event.button == 3: + self.__button3_press = False + + self.grab_focus() + + return False + + @graph_is_not_empty + def motion_notify(self, widget, event): + """ + Drawing callback + @type widget: GtkWidget + @param widget: Gtk widget superclass + @type event: GtkEvent + @param event: Gtk event of widget + @rtype: boolean + @return: Indicator of the event propagation + """ + pointer = self.get_pointer() + + for node in self.__graph.get_nodes(): + node.set_draw_info({'over': False}) + + result = self.__get_node_by_coordinate(self.get_pointer()) + + if result is not None: + result[0].set_draw_info({'over': True}) + + elif self.__button1_press and self.__last_motion_point is not None: + + ax, ay = pointer + ox, oy = self.__last_motion_point + tx, ty = self.__translation + + self.__translation = (tx + ax - ox, ty - ay + oy) + + self.__last_motion_point = pointer + + self.grab_focus() + self.queue_draw() + + return False + + def draw(self, widget, context): + """ + Drawing callback + @type widget: GtkWidget + @param widget: Gtk widget superclass + @type context: cairo.Context + @param context: cairo context class + @rtype: boolean + @return: Indicator of the event propagation + """ + context.set_source_rgb(1.0, 1.0, 1.0) + context.fill() + + self.__draw(context) + + return False + + @graph_is_not_empty + def __draw(self, context): + """ + Drawing method + """ + # getting allocation reference + allocation = self.get_allocation() + + self.__center_of_widget = (allocation.width // 2, + allocation.height // 2) + + xc, yc = self.__center_of_widget + + ax, ay = self.__translation + + # xc = 320 yc = 240 + + # -1.5 | -0.5 ( 480, 360) + # -1.0 | 0.0 ( 320, 240) + # -0.5 | 0.5 ( 160, 120) + # 0.0 | 1.0 ( 0, 0) + # 0.5 | 1.5 (-160, -120) + # 1.0 | 2.0 (-320, -240) + # 1.5 | 2.5 (-480, -360) + + # scaling and translate + factor = -(self.__scale - 1) + + context.translate(xc * factor + ax, yc * factor - ay) + + if self.__scale != 1.0: + context.scale(self.__scale, self.__scale) + + # drawing over node's region + if self.__show_region and not self.__animating: + + for node in self.__sorted_nodes: + + not_grouped = not node.get_draw_info('grouped') + + if node.get_draw_info('region') is not None and not_grouped: + + xc, yc = self.__center_of_widget + r, g, b = REGION_COLORS[node.get_draw_info('region')] + + start, final = node.get_draw_info('range') + + i_radius = node.get_coordinate_radius() + f_radius = self.__calc_radius(self.__number_of_rings - 1) + + is_fill_all = abs(final - start) == 360 + + final = math.radians(final + self.__rotate) + start = math.radians(start + self.__rotate) + + context.move_to(xc, yc) + context.set_source_rgba(r, g, b, 0.1) + context.new_path() + context.arc(xc, yc, i_radius, -final, -start) + context.arc_negative(xc, yc, f_radius, -start, -final) + context.close_path() + context.fill() + context.stroke() + + if not is_fill_all: + + context.set_source_rgb(r, g, b) + context.set_line_width(1) + + xa, ya = PolarCoordinate( + i_radius, final).to_cartesian() + xb, yb = PolarCoordinate( + f_radius, final).to_cartesian() + + context.move_to(xc + xa, yc - ya) + context.line_to(xc + xb, yc - yb) + context.stroke() + + xa, ya = PolarCoordinate( + i_radius, start).to_cartesian() + xb, yb = PolarCoordinate( + f_radius, start).to_cartesian() + + context.move_to(xc + xa, yc - ya) + context.line_to(xc + xb, yc - yb) + context.stroke() + + # drawing network rings + if self.__show_ring and not self.__animating: + + for i in range(1, self.__number_of_rings): + + radius = self.__calc_radius(i) + + context.arc(xc, yc, radius, 0, 2 * math.pi) + context.set_source_rgb(0.8, 0.8, 0.8) + context.set_line_width(1) + context.stroke() + + # drawing nodes and your connections + for edge in self.__graph.get_edges(): + + # check group constraints for edges + a, b = edge.get_nodes() + + a_is_grouped = a.get_draw_info('grouped') + b_is_grouped = b.get_draw_info('grouped') + + a_is_group = a.get_draw_info('group') + b_is_group = b.get_draw_info('group') + + a_group = a.get_draw_info('group_node') + b_group = b.get_draw_info('group_node') + + a_is_child = a in b.get_draw_info('children') + b_is_child = b in a.get_draw_info('children') + + last_group = self.__last_group_node + groups = [a_group, b_group] + + if last_group in groups and last_group is not None: + self.__draw_edge(context, edge) + + elif not a_is_grouped or not b_is_grouped: + + if not (a_is_group and b_is_child or + b_is_group and a_is_child): + self.__draw_edge(context, edge) + + elif a_group != b_group: + self.__draw_edge(context, edge) + + for node in reversed(self.__sorted_nodes): + + # check group constraints for nodes + group = node.get_draw_info('group_node') + grouped = node.get_draw_info('grouped') + + if group == self.__last_group_node or not grouped: + self.__draw_node(context, node) + + def __draw_edge(self, context, edge): + """ + Draw the connection between two nodes + @type : Edge + @param : The second node that will be connected + """ + a, b = edge.get_nodes() + + xa, ya = a.get_cartesian_coordinate() + xb, yb = b.get_cartesian_coordinate() + xc, yc = self.__center_of_widget + + a_children = a.get_draw_info('children') + b_children = b.get_draw_info('children') + + latency = edge.get_weights_mean() + + # check if isn't an hierarchy connection + if a not in b_children and b not in a_children: + context.set_source_rgba(1.0, 0.6, 0.1, 0.8) + + elif a.get_draw_info('no_route') or b.get_draw_info('no_route'): + context.set_source_rgba(0.0, 0.0, 0.0, 0.8) + + else: + context.set_source_rgba(0.1, 0.5, 1.0, 0.8) + + # calculating line thickness by latency + if latency is not None: + + min = self.__graph.get_min_edge_mean_weight() + max = self.__graph.get_max_edge_mean_weight() + + if max != min: + thickness = (latency - min) * 4 / (max - min) + 1 + + else: + thickness = 1 + + context.set_line_width(thickness) + + else: + + context.set_dash([2, 2]) + context.set_line_width(1) + + context.move_to(xc + xa, yc - ya) + context.line_to(xc + xb, yc - yb) + context.stroke() + + context.set_dash([1, 0]) + + if not self.__animating and self.__show_latency: + + if latency is not None: + + context.set_font_size(8) + context.set_line_width(1) + context.move_to(xc + (xa + xb) / 2 + 1, + yc - (ya + yb) / 2 + 4) + context.show_text(str(round(latency, 2))) + context.stroke() + + def __draw_node(self, context, node): + """ + Draw nodes and your information + @type : NetNode + @param : The node to be drawn + """ + x, y = node.get_cartesian_coordinate() + xc, yc = self.__center_of_widget + r, g, b = node.get_draw_info('color') + radius = node.get_draw_info('radius') + + type = node.get_info('device_type') + + x_gap = radius + 2 + y_gap = 0 + + # draw group indication + if node.get_draw_info('group'): + + x_gap += 5 + + if type in SQUARE_TYPES: + context.rectangle(xc + x - radius - 5, + yc - y - radius - 5, + 2 * radius + 10, + 2 * radius + 10) + + else: + context.arc(xc + x, yc - y, radius + 5, 0, 2 * math.pi) + + context.set_source_rgb(1.0, 1.0, 1.0) + context.fill_preserve() + + if node.deep_search_child(self.__graph.get_main_node()): + context.set_source_rgb(0.0, 0.0, 0.0) + + else: + context.set_source_rgb(0.1, 0.5, 1.0) + + context.set_line_width(2) + context.stroke() + + # draw over node + if node.get_draw_info('over'): + + context.set_line_width(0) + + if type in SQUARE_TYPES: + context.rectangle(xc + x - radius - 5, + yc - y - radius - 5, + 2 * radius + 10, + 2 * radius + 10) + + else: + context.arc(xc + x, yc - y, radius + 5, 0, 2 * math.pi) + + context.set_source_rgb(0.1, 0.5, 1.0) + context.fill_preserve() + context.stroke() + + # draw node + if type in SQUARE_TYPES: + context.rectangle(xc + x - radius, + yc - y - radius, + 2 * radius, + 2 * radius) + + else: + context.arc(xc + x, yc - y, radius, 0, 2 * math.pi) + + # draw icons + if not self.__animating and self.__show_icon: + + icons = list() + + if type in ICON_DICT.keys(): + icons.append(self.__icon.get_pixbuf(ICON_DICT[type])) + + if node.get_info('filtered'): + icons.append(self.__icon.get_pixbuf('padlock')) + + for icon in icons: + + stride, data = get_pixels_for_cairo_image_surface(icon) + + # Cairo documentation says that the correct way to obtain a + # legal stride value is using the function + # cairo.ImageSurface.format_stride_for_width(). + # But this method is only available since cairo 1.6. So we are + # using the stride returned by + # get_pixels_for_cairo_image_surface() function. + surface = cairo.ImageSurface.create_for_data(data, + cairo.FORMAT_ARGB32, + icon.get_width(), + icon.get_height(), + stride) + + context.set_source_surface(surface, + round(xc + x + x_gap), + round(yc - y + y_gap - 6)) + context.paint() + + x_gap += 13 + + # draw node text + context.set_source_rgb(r, g, b) + context.fill_preserve() + + if node.get_draw_info('valid'): + context.set_source_rgb(0.0, 0.0, 0.0) + + else: + context.set_source_rgb(0.1, 0.5, 1.0) + + if not self.__animating and self.__show_address: + + context.set_font_size(8) + context.move_to(round(xc + x + x_gap), + round(yc - y + y_gap + 4)) + + hostname = node.get_info('hostname') + + if hostname is not None and self.__show_hostname: + context.show_text(hostname) + + elif node.get_info('ip') is not None: + context.show_text(node.get_info('ip')) + + context.set_line_width(1) + context.stroke() + + def __check_fisheye_ring(self): + """ + """ + if self.__fisheye_ring >= self.__number_of_rings: + self.__fisheye_ring = self.__number_of_rings - 1 + + def __set_number_of_rings(self, value): + """ + """ + self.__number_of_rings = value + self.__check_fisheye_ring() + + def __fisheye_function(self, ring): + """ + """ + distance = abs(self.__fisheye_ring - ring) + level_of_detail = self.__ring_gap * self.__fisheye_interest + spread_distance = distance - distance * self.__fisheye_spread + + value = level_of_detail / (spread_distance + 1) + + if value < self.__min_ring_gap: + value = self.__min_ring_gap + + return value + + @graph_is_not_empty + @not_is_in_animation + def __update_nodes_positions(self): + """ + """ + for node in self.__sorted_nodes: + + if node.get_draw_info('grouped'): + + # deep group check + group = node.get_draw_info('group_node') + + while group.get_draw_info('group_node') is not None: + group = group.get_draw_info('group_node') + + ring = group.get_draw_info('ring') + node.set_coordinate_radius(self.__calc_radius(ring)) + + else: + ring = node.get_draw_info('ring') + node.set_coordinate_radius(self.__calc_radius(ring)) + + @graph_is_not_empty + def __get_node_by_coordinate(self, point): + """ + """ + xc, yc = self.__center_of_widget + + for node in self.__graph.get_nodes(): + + if node.get_draw_info('grouped'): + continue + + ax, ay = self.__translation + + xn, yn = node.get_cartesian_coordinate() + center = (xc + xn * self.__scale + ax, yc - yn * self.__scale - ay) + radius = node.get_draw_info('radius') * self.__scale + + type = node.get_info('device_type') + + if type in SQUARE_TYPES: + if geometry.is_in_square(point, radius, center): + return node, center + + else: + if geometry.is_in_circle(point, radius, center): + return node, center + + return None + + def __calc_radius(self, ring): + """ + """ + if self.__fisheye: + + radius = 0 + + while ring > 0: + + radius += self.__fisheye_function(ring) + ring -= 1 + + else: + radius = ring * self.__ring_gap + + return radius + + @graph_is_not_empty + def __arrange_nodes(self): + """ + """ + new_nodes = set([self.__graph.get_main_node()]) + old_nodes = set() + + number_of_needed_rings = 1 + ring = 0 + + # while new nodes were found + while len(new_nodes) > 0: + + tmp_nodes = set() + + # for each new nodes + for node in new_nodes: + + old_nodes.add(node) + + # set ring location + node.set_draw_info({'ring': ring}) + + # check group constraints + if (node.get_draw_info('group') or + node.get_draw_info('grouped')): + children = node.get_draw_info('children') + + else: + + # getting connections and fixing multiple fathers + children = set() + for child in self.__graph.get_node_connections(node): + if child in old_nodes or child in new_nodes: + continue + if child.get_draw_info('grouped'): + continue + children.add(child) + + # setting father foreign + for child in children: + child.set_draw_info({'father': node}) + + node.set_draw_info( + {'children': misc.sort_children(children, node)}) + tmp_nodes.update(children) + + # check group influence in number of rings + for node in tmp_nodes: + + if not node.get_draw_info('grouped'): + + number_of_needed_rings += 1 + break + + # update new nodes set + new_nodes.update(tmp_nodes) + new_nodes.difference_update(old_nodes) + + ring += 1 + + self.__set_number_of_rings(number_of_needed_rings) + + def __weighted_layout(self): + """ + """ + # calculating the space needed by each node + self.__graph.get_main_node().set_draw_info({'range': (0, 360)}) + new_nodes = set([self.__graph.get_main_node()]) + + self.__graph.get_main_node().calc_needed_space() + + while len(new_nodes) > 0: + + node = new_nodes.pop() + + # add only no grouped nodes + children = set() + for child in node.get_draw_info('children'): + + if not child.get_draw_info('grouped'): + children.add(child) + new_nodes.add(child) + + if len(children) > 0: + + min, max = node.get_draw_info('range') + + node_total = max - min + children_need = node.get_draw_info('children_need') + + for child in children: + + child_need = child.get_draw_info('space_need') + child_total = node_total * child_need / children_need + + theta = child_total / 2 + min + self.__rotate + + child.set_coordinate_theta(theta) + child.set_draw_info({'range': (min, min + child_total)}) + + min += child_total + + def __symmetric_layout(self): + """ + """ + self.__graph.get_main_node().set_draw_info({'range': (0, 360)}) + new_nodes = set([self.__graph.get_main_node()]) + + while len(new_nodes) > 0: + + node = new_nodes.pop() + + # add only no grouped nodes + children = set() + for child in node.get_draw_info('children'): + + if not child.get_draw_info('grouped'): + children.add(child) + new_nodes.add(child) + + if len(children) > 0: + + min, max = node.get_draw_info('range') + factor = float(max - min) / len(children) + + for child in children: + + theta = factor / 2 + min + self.__rotate + + child.set_coordinate_theta(theta) + child.set_draw_info({'range': (min, min + factor)}) + + min += factor + + @graph_is_not_empty + def __calc_layout(self, reference): + """ + """ + # selecting layout algorithm + if self.__layout == LAYOUT_SYMMETRIC: + self.__symmetric_layout() + + elif self.__layout == LAYOUT_WEIGHTED: + self.__weighted_layout() + + # rotating focus' children to keep orientation + if reference is not None: + + father, angle = reference + theta = father.get_coordinate_theta() + factor = theta - angle + + for node in self.__graph.get_nodes(): + + theta = node.get_coordinate_theta() + node.set_coordinate_theta(theta - factor) + + a, b = node.get_draw_info('range') + node.set_draw_info({'range': (a - factor, b - factor)}) + + @graph_is_not_empty + def __calc_node_positions(self, reference=None): + """ + """ + # set nodes' hierarchy + self.__arrange_nodes() + self.calc_sorted_nodes() + + # set nodes' coordinate radius + for node in self.__graph.get_nodes(): + + ring = node.get_draw_info('ring') + node.set_coordinate_radius(self.__calc_radius(ring)) + + # set nodes' coordinate theta + self.__calc_layout(reference) + + def __calc_interpolation(self, focus): + """ + """ + old_main_node = self.__graph.get_main_node() + self.__graph.set_main_node(focus) + + # getting initial coordinates + for node in self.__graph.get_nodes(): + + if self.__interpolation == INTERPOLATION_POLAR: + coordinate = node.get_polar_coordinate() + + elif self.__interpolation == INTERPOLATION_CARTESIAN: + coordinate = node.get_cartesian_coordinate() + + node.set_draw_info({'start_coordinate': coordinate}) + + father = focus.get_draw_info('father') + + # calculate nodes positions (and father orientation)? + if father is not None: + + xa, ya = father.get_cartesian_coordinate() + xb, yb = focus.get_cartesian_coordinate() + + angle = math.atan2(yb - ya, xb - xa) + angle = math.degrees(angle) + + self.__calc_node_positions((father, 180 + angle)) + + else: + self.__calc_node_positions() + + # steps for slow-in/slow-out animation + steps = list(range(self.__number_of_frames)) + + for i in range(len(steps) // 2): + steps[self.__number_of_frames - 1 - i] = steps[i] + + # normalize angles and calculate interpolated points + for node in self.__sorted_nodes: + + l2di = Linear2DInterpolator() + + # change grouped nodes coordinate + if node.get_draw_info('grouped'): + + group_node = node.get_draw_info('group_node') + a, b = group_node.get_draw_info('final_coordinate') + + if self.__interpolation == INTERPOLATION_POLAR: + node.set_polar_coordinate(a, b) + + elif self.__interpolation == INTERPOLATION_CARTESIAN: + node.set_cartesian_coordinate(a, b) + + # change interpolation method + if self.__interpolation == INTERPOLATION_POLAR: + + coordinate = node.get_polar_coordinate() + node.set_draw_info({'final_coordinate': coordinate}) + + # adjusting polar coordinates + ri, ti = node.get_draw_info('start_coordinate') + rf, tf = node.get_draw_info('final_coordinate') + + # normalization [0, 360] + ti = geometry.normalize_angle(ti) + tf = geometry.normalize_angle(tf) + + # against longest path + ti, tf = geometry.calculate_short_path(ti, tf) + + # main node goes direct to center (no arc) + if node == self.__graph.get_main_node(): + tf = ti + + # old main node goes direct to new position (no arc) + if node == old_main_node: + ti = tf + + node.set_draw_info({'start_coordinate': (ri, ti)}) + node.set_draw_info({'final_coordinate': (rf, tf)}) + + elif self.__interpolation == INTERPOLATION_CARTESIAN: + + coordinate = node.get_cartesian_coordinate() + node.set_draw_info({'final_coordinate': coordinate}) + + # calculate interpolated points + ai, bi = node.get_draw_info('start_coordinate') + af, bf = node.get_draw_info('final_coordinate') + + l2di.set_start_point(ai, bi) + l2di.set_final_point(af, bf) + + if self.__interpolation_slow_in_out: + points = l2di.get_weighed_points( + self.__number_of_frames, steps) + + else: + points = l2di.get_points(self.__number_of_frames) + + node.set_draw_info({'interpolated_coordinate': points}) + + return True + + def __livens_up(self, index=0): + """ + """ + if self.__graph is None: + # Bail out if the graph became empty during an animation. + self.__last_group_node = None + self.__animating = False + return False + + # prepare interpolated points + if index == 0: + + # prevent unnecessary animation + no_need_to_move = True + + for node in self.__graph.get_nodes(): + + ai, bi = node.get_draw_info('start_coordinate') + af, bf = node.get_draw_info('final_coordinate') + + start_c = round(ai), round(bi) + final_c = round(af), round(bf) + + if start_c != final_c: + no_need_to_move = False + + if no_need_to_move: + + self.__animating = False + return False + + # move all nodes for pass 'index' + for node in self.__graph.get_nodes(): + + a, b = node.get_draw_info('interpolated_coordinate')[index] + + if self.__interpolation == INTERPOLATION_POLAR: + node.set_polar_coordinate(a, b) + + elif self.__interpolation == INTERPOLATION_CARTESIAN: + node.set_cartesian_coordinate(a, b) + + self.queue_draw() + + # animation continue condition + if index < self.__number_of_frames - 1: + GLib.timeout_add(self.__animation_rate, # time to recall + self.__livens_up, # recursive call + index + 1) # next iteration + else: + self.__last_group_node = None + self.__animating = False + + return False + + @not_is_in_animation + def set_graph(self, graph): + """ + Set graph to be displayed in layout + @type : Graph + @param : Set the graph used in visualization + """ + if graph.get_number_of_nodes() > 0: + + self.__graph = graph + + self.__calc_node_positions() + self.queue_draw() + + else: + self.__graph = None + + def get_scanned_nodes(self): + """ + """ + nodes = list() + if self.__graph is None: + return nodes + + for node in self.__graph.get_nodes(): + + if node.get_draw_info('scanned'): + nodes.append(node) + + return nodes + + def get_graph(self): + """ + """ + return self.__graph + + def set_empty(self): + """ + """ + del(self.__graph) + self.__graph = None + + self.queue_draw() + + def get_rotation(self): + """ + """ + return self.__rotate + + @graph_is_not_empty + def set_rotation(self, angle): + """ + """ + delta = angle - self.__rotate + self.__rotate = angle + + for node in self.__graph.get_nodes(): + + theta = node.get_coordinate_theta() + node.set_coordinate_theta(theta + delta) + + self.queue_draw() + + def get_translation(self): + """ + """ + return self.__translation + + @graph_is_not_empty + def set_translation(self, translation): + """ + """ + self.__translation = translation + self.queue_draw() + + def is_empty(self): + """ + """ + return self.__graph is None + + def is_in_animation(self): + """ + """ + return self.__animating + + def calc_sorted_nodes(self): + """ + """ + self.__sorted_nodes = list(self.__graph.get_nodes()) + self.__sorted_nodes.sort(key=lambda n: n.get_draw_info('ring')) + + +class NetNode(Node): + """ + Node class for radial network widget + """ + def __init__(self): + """ + """ + self.__draw_info = dict() + """Hash with draw information""" + self.__coordinate = PolarCoordinate() + + super(NetNode, self).__init__() + + def get_host(self): + """ + Set the HostInfo that this node represents + """ + return self.get_data() + + def set_host(self, host): + """ + Set the HostInfo that this node represents + """ + self.set_data(host) + + def get_info(self, info): + """Return various information extracted from the host set with + set_host.""" + host = self.get_data() + if host is not None: + if info == "number_of_open_ports": + return host.get_port_count_by_states(["open"]) + elif info == "vulnerability_score": + num_open_ports = host.get_port_count_by_states(["open"]) + if num_open_ports < 3: + return 0 + elif num_open_ports < 7: + return 1 + else: + return 2 + elif info == "addresses": + addresses = [] + if host.ip is not None: + addresses.append(host.ip) + if host.ipv6 is not None: + addresses.append(host.ipv6) + if host.mac is not None: + addresses.append(host.mac) + return addresses + elif info == "ip": + for addr in (host.ip, host.ipv6, host.mac): + if addr: + return addr.get("addr") + elif info == "hostnames": + hostnames = [] + for hostname in host.hostnames: + copy = {} + copy["name"] = hostname.get("hostname", "") + copy["type"] = hostname.get("hostname_type", "") + hostnames.append(copy) + return hostnames + elif info == "hostname": + return host.get_hostname() + elif info == "uptime": + if host.uptime.get("seconds") or host.uptime.get("lastboot"): + return host.uptime + elif info == "device_type": + osmatch = host.get_best_osmatch() + if osmatch is None: + return None + osclasses = osmatch['osclasses'] + if len(osclasses) == 0: + return None + types = ["router", "wap", "switch", "firewall"] + for type in types: + if type in osclasses[0].get("type", "").lower(): + return type + elif info == "os": + os = {} + + # osmatches + if len(host.osmatches) > 0 and \ + host.osmatches[0]["accuracy"] != "" and \ + host.osmatches[0]["name"] != "": + if os is None: + os = {} + os["matches"] = host.osmatches + os["matches"][0]["db_line"] = 0 # not supported + + os_classes = [] + for osclass in host.osmatches[0]["osclasses"]: + os_class = {} + + os_class["type"] = osclass.get("type", "") + os_class["vendor"] = osclass.get("vendor", "") + os_class["accuracy"] = osclass.get("accuracy", "") + os_class["os_family"] = osclass.get("osfamily", "") + os_class["os_gen"] = osclass.get("osgen", "") + + os_classes.append(os_class) + os["classes"] = os_classes + + # ports_used + if len(host.ports_used) > 0: + if os is None: + os = {} + os_portsused = [] + + for portused in host.ports_used: + os_portused = {} + + os_portused["state"] = portused.get("state", "") + os_portused["protocol"] = portused.get("proto", "") + os_portused["id"] = int(portused.get("portid", "0")) + + os_portsused.append(os_portused) + + os["used_ports"] = os_portsused + + if len(os) > 0: + os["fingerprint"] = "" + return os + elif info == "sequences": + # getting sequences information + sequences = {} + # If all fields are empty, we don't put it into the sequences + # list + if reduce(lambda x, y: x + y, + host.tcpsequence.values(), "") != "": + tcp = {} + if host.tcpsequence.get("index", "") != "": + tcp["index"] = int(host.tcpsequence["index"]) + else: + tcp["index"] = 0 + tcp["class"] = "" # not supported + tcp["values"] = host.tcpsequence.get( + "values", "").split(",") + tcp["difficulty"] = host.tcpsequence.get("difficulty", "") + sequences["tcp"] = tcp + if reduce(lambda x, y: x + y, + host.ipidsequence.values(), "") != "": + ip_id = {} + ip_id["class"] = host.ipidsequence.get("class", "") + ip_id["values"] = host.ipidsequence.get( + "values", "").split(",") + sequences["ip_id"] = ip_id + if reduce(lambda x, y: x + y, + host.tcptssequence.values(), "") != "": + tcp_ts = {} + tcp_ts["class"] = host.tcptssequence.get("class", "") + tcp_ts["values"] = host.tcptssequence.get( + "values", "").split(",") + sequences["tcp_ts"] = tcp_ts + return sequences + elif info == "filtered": + if (len(host.extraports) > 0 and + host.extraports[0]["state"] == "filtered"): + return True + else: + for port in host.ports: + if port["port_state"] == "filtered": + return True + return False + elif info == "ports": + ports = list() + for host_port in host.ports: + port = dict() + state = dict() + service = dict() + + port["id"] = int(host_port.get("portid", "")) + port["protocol"] = host_port.get("protocol", "") + + state["state"] = host_port.get("port_state", "") + state["reason"] = "" # not supported + state["reason_ttl"] = "" # not supported + state["reason_ip"] = "" # not supported + + service["name"] = host_port.get("service_name", "") + service["conf"] = host_port.get("service_conf", "") + service["method"] = host_port.get("service_method", "") + service["version"] = host_port.get("service_version", "") + service["product"] = host_port.get("service_product", "") + service["extrainfo"] = host_port.get( + "service_extrainfo", "") + + port["state"] = state + port["scripts"] = None # not supported + port["service"] = service + + ports.append(port) + return ports + elif info == "extraports": + # extraports + all_extraports = list() + for extraport in host.extraports: + extraports = dict() + extraports["count"] = int(extraport.get("count", "")) + extraports["state"] = extraport.get("state", "") + extraports["reason"] = list() # not supported + extraports["all_reason"] = list() # not supported + + all_extraports.append(extraports) + return all_extraports + elif info == "trace": + # getting traceroute information + if len(host.trace) > 0: + trace = {} + hops = [] + + for host_hop in host.trace.get("hops", []): + hop = {} + hop["ip"] = host_hop.get("ipaddr", "") + hop["ttl"] = int(host_hop.get("ttl", "")) + hop["rtt"] = host_hop.get("rtt", "") + hop["hostname"] = host_hop.get("host", "") + + hops.append(hop) + + trace["hops"] = hops + trace["port"] = host.trace.get("port", "") + trace["protocol"] = host.trace.get("proto", "") + + return trace + else: # host is None + pass + + return None + + def get_coordinate_theta(self): + """ + """ + return self.__coordinate.get_theta() + + def get_coordinate_radius(self): + """ + """ + return self.__coordinate.get_radius() + + def set_coordinate_theta(self, value): + """ + """ + self.__coordinate.set_theta(value) + + def set_coordinate_radius(self, value): + """ + """ + self.__coordinate.set_radius(value) + + def set_polar_coordinate(self, r, t): + """ + Set polar coordinate + @type r: number + @param r: The radius of coordinate + @type t: number + @param t: The angle (theta) of coordinate in radians + """ + self.__coordinate.set_coordinate(r, t) + + def get_polar_coordinate(self): + """ + Get cartesian coordinate + @rtype: tuple + @return: Cartesian coordinates (x, y) + """ + return self.__coordinate.get_coordinate() + + def set_cartesian_coordinate(self, x, y): + """ + Set cartesian coordinate + """ + cartesian = CartesianCoordinate(x, y) + r, t = cartesian.to_polar() + + self.set_polar_coordinate(r, math.degrees(t)) + + def get_cartesian_coordinate(self): + """ + Get cartesian coordinate + @rtype: tuple + @return: Cartesian coordinates (x, y) + """ + return self.__coordinate.to_cartesian() + + def get_draw_info(self, info=None): + """ + Get draw information about node + @type : string + @param : Information name + @rtype: mixed + @return: The requested information + """ + if info is None: + return self.__draw_info + + return self.__draw_info.get(info) + + def set_draw_info(self, info): + """ + Set draw information + @type : dict + @param : Draw information dictionary + """ + for key in info: + self.__draw_info[key] = info[key] + + def deep_search_child(self, node): + """ + """ + for child in self.get_draw_info('children'): + + if child == node: + return True + + elif child.deep_search_child(node): + return True + + return False + + def set_subtree_info(self, info): + """ + """ + for child in self.get_draw_info('children'): + + child.set_draw_info(info) + + if not child.get_draw_info('group'): + child.set_subtree_info(info) + + def calc_needed_space(self): + """ + """ + number_of_children = len(self.get_draw_info('children')) + + sum_angle = 0 + own_angle = 0 + + if number_of_children > 0 and not self.get_draw_info('group'): + + for child in self.get_draw_info('children'): + + child.calc_needed_space() + sum_angle += child.get_draw_info('space_need') + + distance = self.get_coordinate_radius() + size = self.get_draw_info('radius') * 2 + own_angle = geometry.angle_from_object(distance, size) + + self.set_draw_info({'children_need': sum_angle}) + self.set_draw_info({'space_need': max(sum_angle, own_angle)}) diff --git a/zenmap/radialnet/gui/SaveDialog.py b/zenmap/radialnet/gui/SaveDialog.py new file mode 100644 index 0000000..60cfbbe --- /dev/null +++ b/zenmap/radialnet/gui/SaveDialog.py @@ -0,0 +1,169 @@ +# vim: set encoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk + +import os.path +import radialnet.gui.RadialNet as RadialNet +import zenmapGUI.FileChoosers + +from zenmapGUI.higwidgets.higboxes import HIGHBox +from zenmapGUI.higwidgets.higdialogs import HIGAlertDialog + + +TYPES = ((_("By extension"), None, None), + ("PDF", RadialNet.FILE_TYPE_PDF, ".pdf"), + ("PNG", RadialNet.FILE_TYPE_PNG, ".png"), + ("PostScript", RadialNet.FILE_TYPE_PS, ".ps"), + ("SVG", RadialNet.FILE_TYPE_SVG, ".svg")) +# Build a reverse index of extensions to file types, for the "By extension" +# file type. +EXTENSIONS = {} +for type in TYPES: + if type[2] is not None: + EXTENSIONS[type[2]] = type[1] + + +class SaveDialog(Gtk.FileChooserDialog): + def __init__(self): + """ + """ + super(SaveDialog, self).__init__(title=_("Save Topology"), + action=Gtk.FileChooserAction.SAVE, + buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, + Gtk.STOCK_SAVE, Gtk.ResponseType.OK)) + + types_store = Gtk.ListStore.new([str, object, str]) + for type in TYPES: + types_store.append(type) + + self.__combo = Gtk.ComboBox.new_with_model(types_store) + cell = Gtk.CellRendererText() + self.__combo.pack_start(cell, True) + self.__combo.add_attribute(cell, "text", 0) + + self.__combo.connect("changed", self.__combo_changed_cb) + self.__combo.set_active(0) + + self.connect("response", self.__response_cb) + + hbox = HIGHBox() + label = Gtk.Label.new(_("Select File Type:")) + hbox.pack_end(self.__combo, False, True, 0) + hbox.pack_end(label, False, True, 0) + + self.set_extra_widget(hbox) + self.set_do_overwrite_confirmation(True) + + hbox.show_all() + + def __combo_changed_cb(self, widget): + filename = self.get_filename() or "" + dir, basename = os.path.split(filename) + if dir != self.get_current_folder(): + self.set_current_folder(dir) + + # Find the recommended extension. + new_ext = self.__combo.get_model().get_value( + self.__combo.get_active_iter(), 2) + if new_ext is not None: + # Change the filename to use the recommended extension. + root, ext = os.path.splitext(basename) + if len(ext) == 0 and root.startswith("."): + root = "" + self.set_current_name(root + new_ext) + + def __response_cb(self, widget, response_id): + """Intercept the "response" signal to check if someone used the "By + extension" file type with an unknown extension.""" + if response_id == Gtk.ResponseType.OK and self.get_filetype() is None: + ext = self.__get_extension() + if ext == "": + filename = self.get_filename() or "" + dir, basename = os.path.split(filename) + alert = HIGAlertDialog( + message_format=_("No filename extension"), + secondary_text=_("""\ +The filename "%s" does not have an extension, \ +and no specific file type was chosen. +Enter a known extension or select the file type from the list.""" % basename)) + + else: + alert = HIGAlertDialog( + message_format=_("Unknown filename extension"), + secondary_text=_("""\ +There is no file type known for the filename extension "%s". +Enter a known extension or select the file type from the list.\ +""") % self.__get_extension()) + alert.run() + alert.destroy() + # Go back to the dialog. + self.emit_stop_by_name("response") + + def __get_extension(self): + return os.path.splitext(self.get_filename())[1] + + def get_filetype(self): + filetype = self.__combo.get_model().get_value( + self.__combo.get_active_iter(), 1) + if filetype is None: + # Guess based on extension. + return EXTENSIONS.get(self.__get_extension()) + return filetype diff --git a/zenmap/radialnet/gui/Toolbar.py b/zenmap/radialnet/gui/Toolbar.py new file mode 100644 index 0000000..23b7077 --- /dev/null +++ b/zenmap/radialnet/gui/Toolbar.py @@ -0,0 +1,309 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk + +from radialnet.bestwidgets.buttons import BWStockButton, BWToggleStockButton +from radialnet.gui.SaveDialog import SaveDialog +from radialnet.gui.Dialogs import AboutDialog +from radialnet.gui.LegendWindow import LegendWindow +from radialnet.gui.HostsViewer import HostsViewer +from zenmapGUI.higwidgets.higdialogs import HIGAlertDialog + + +SHOW = True +HIDE = False + +REFRESH_RATE = 500 + + +class ToolsMenu(Gtk.Menu): + """ + """ + def __init__(self, radialnet): + """ + """ + Gtk.Menu.__init__(self) + + self.radialnet = radialnet + + self.__create_items() + + def __create_items(self): + """ + """ + self.__hosts = Gtk.ImageMenuItem.new_with_label(_('Hosts viewer')) + self.__hosts.connect("activate", self.__hosts_viewer_callback) + self.__hosts_image = Gtk.Image() + self.__hosts_image.set_from_stock(Gtk.STOCK_INDEX, Gtk.IconSize.MENU) + self.__hosts.set_image(self.__hosts_image) + + self.append(self.__hosts) + + self.__hosts.show_all() + + def __hosts_viewer_callback(self, widget): + """ + """ + window = HostsViewer(self.radialnet.get_scanned_nodes()) + window.show_all() + window.set_keep_above(True) + + def enable_dependents(self): + """ + """ + self.__hosts.set_sensitive(True) + + def disable_dependents(self): + """ + """ + self.__hosts.set_sensitive(False) + + +class Toolbar(Gtk.Box): + """ + """ + def __init__(self, radialnet, window, control, fisheye): + """ + """ + Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL) + #self.set_style(gtk.TOOLBAR_BOTH_HORIZ) + #self.set_tooltips(True) + + self.radialnet = radialnet + + self.__window = window + self.__control_widget = control + self.__fisheye_widget = fisheye + + self.__control_widget.show_all() + self.__control_widget.set_no_show_all(True) + self.__control_widget.hide() + + self.__fisheye_widget.show_all() + self.__fisheye_widget.set_no_show_all(True) + self.__fisheye_widget.hide() + + self.__save_chooser = None + + self.__create_widgets() + + def __create_widgets(self): + """ + """ + # self.__tooltips = gtk.Tooltips() + + #self.__tools_menu = ToolsMenu(self.radialnet) + + #self.__tools_button = gtk.MenuToolButton(gtk.STOCK_PREFERENCES) + #self.__tools_button.set_label(_('Tools')) + #self.__tools_button.set_is_important(True) + #self.__tools_button.set_menu(self.__tools_menu) + #self.__tools_button.connect('clicked', self.__tools_callback) + + self.__save_button = BWStockButton(Gtk.STOCK_SAVE, _("Save Graphic")) + self.__save_button.connect("clicked", self.__save_image_callback) + + self.__hosts_button = BWStockButton(Gtk.STOCK_INDEX, _("Hosts Viewer")) + self.__hosts_button.connect("clicked", self.__hosts_viewer_callback) + + self.__control = BWToggleStockButton( + Gtk.STOCK_PROPERTIES, _("Controls")) + self.__control.connect('clicked', self.__control_callback) + self.__control.set_active(False) + + self.__fisheye = BWToggleStockButton(Gtk.STOCK_ZOOM_FIT, _("Fisheye")) + self.__fisheye.connect('clicked', self.__fisheye_callback) + self.__fisheye.set_active(False) + + self.__legend_button = BWStockButton(Gtk.STOCK_INDEX, _("Legend")) + self.__legend_button.connect('clicked', self.__legend_callback) + + #self.__fullscreen = gtk.ToggleToolButton(gtk.STOCK_FULLSCREEN) + #self.__fullscreen.set_label(_('Fullscreen')) + #self.__fullscreen.set_is_important(True) + #self.__fullscreen.connect('clicked', self.__fullscreen_callback) + #self.__fullscreen.set_tooltip(self.__tooltips, _('Toggle fullscreen')) + + #self.__about = gtk.ToolButton(gtk.STOCK_ABOUT) + #self.__about.set_label(_('About')) + #self.__about.set_is_important(True) + #self.__about.connect('clicked', self.__about_callback) + #self.__about.set_tooltip(self.__tooltips, _('About RadialNet')) + + self.__separator = Gtk.SeparatorToolItem() + self.__expander = Gtk.SeparatorToolItem() + self.__expander.set_expand(True) + self.__expander.set_draw(False) + + #self.insert(self.__open, 0) + #self.insert(self.__separator, 1) + #self.insert(self.__tools_button, 2) + #self.insert(self.__expander, 3) + #self.insert(self.__control, 4) + #self.insert(self.__fisheye, 5) + #self.insert(self.__fullscreen, 6) + #self.insert(self.__about, 7) + + #self.pack_start(self.__tools_button, False) + self.pack_start(self.__hosts_button, False, True, 0) + self.pack_start(self.__fisheye, False, True, 0) + self.pack_start(self.__control, False, True, 0) + self.pack_end(self.__save_button, False, True, 0) + self.pack_end(self.__legend_button, False, True, 0) + + def disable_controls(self): + """ + """ + self.__control.set_sensitive(False) + self.__fisheye.set_sensitive(False) + self.__hosts_button.set_sensitive(False) + self.__legend_button.set_sensitive(False) + #self.__tools_menu.disable_dependents() + + def enable_controls(self): + """ + """ + self.__control.set_sensitive(True) + self.__fisheye.set_sensitive(True) + self.__hosts_button.set_sensitive(True) + self.__legend_button.set_sensitive(True) + #self.__tools_menu.enable_dependents() + + def __tools_callback(self, widget): + """ + """ + self.__tools_menu.popup(None, None, None, 1, 0) + + def __hosts_viewer_callback(self, widget): + """ + """ + window = HostsViewer(self.radialnet.get_scanned_nodes()) + window.show_all() + window.set_keep_above(True) + + def __save_image_callback(self, widget): + """ + """ + if self.__save_chooser is None: + self.__save_chooser = SaveDialog() + + response = self.__save_chooser.run() + + if response == Gtk.ResponseType.OK: + filename = self.__save_chooser.get_filename() + filetype = self.__save_chooser.get_filetype() + + try: + self.radialnet.save_drawing_to_file(filename, filetype) + except Exception as e: + alert = HIGAlertDialog(parent=self.__save_chooser, + type=Gtk.MessageType.ERROR, + message_format=_("Error saving snapshot"), + secondary_text=str(e)) + alert.run() + alert.destroy() + + self.__save_chooser.hide() + + def __control_callback(self, widget=None): + """ + """ + if self.__control.get_active(): + self.__control_widget.show() + + else: + self.__control_widget.hide() + + def __fisheye_callback(self, widget=None): + """ + """ + if not self.radialnet.is_in_animation(): + + if self.__fisheye.get_active(): + + self.__fisheye_widget.active_fisheye() + self.__fisheye_widget.show() + + else: + + self.__fisheye_widget.deactive_fisheye() + self.__fisheye_widget.hide() + + def __legend_callback(self, widget): + """ + """ + self.__legend_window = LegendWindow() + self.__legend_window.show_all() + + def __about_callback(self, widget): + """ + """ + self.__about_dialog = AboutDialog() + self.__about_dialog.show_all() + + def __fullscreen_callback(self, widget=None): + """ + """ + if self.__fullscreen.get_active(): + self.__window.fullscreen() + + else: + self.__window.unfullscreen() diff --git a/zenmap/radialnet/gui/__init__.py b/zenmap/radialnet/gui/__init__.py new file mode 100644 index 0000000..c314dd7 --- /dev/null +++ b/zenmap/radialnet/gui/__init__.py @@ -0,0 +1,56 @@ +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ diff --git a/zenmap/radialnet/radialnet.pyw b/zenmap/radialnet/radialnet.pyw new file mode 100755 index 0000000..ff15924 --- /dev/null +++ b/zenmap/radialnet/radialnet.pyw @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +# vim: set fileencoding=utf-8 : + +# ***********************IMPORTANT NMAP LICENSE TERMS************************ +# * +# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap +# * Project"). Nmap is also a registered trademark of the Nmap Project. +# * +# * This program is distributed under the terms of the Nmap Public Source +# * License (NPSL). The exact license text applying to a particular Nmap +# * release or source code control revision is contained in the LICENSE +# * file distributed with that version of Nmap or source code control +# * revision. More Nmap copyright/legal information is available from +# * https://nmap.org/book/man-legal.html, and further information on the +# * NPSL license itself can be found at https://nmap.org/npsl/ . This +# * header summarizes some key points from the Nmap license, but is no +# * substitute for the actual license text. +# * +# * Nmap is generally free for end users to download and use themselves, +# * including commercial use. It is available from https://nmap.org. +# * +# * The Nmap license generally prohibits companies from using and +# * redistributing Nmap in commercial products, but we sell a special Nmap +# * OEM Edition with a more permissive license and special features for +# * this purpose. See https://nmap.org/oem/ +# * +# * If you have received a written Nmap license agreement or contract +# * stating terms other than these (such as an Nmap OEM license), you may +# * choose to use and redistribute Nmap under those terms instead. +# * +# * The official Nmap Windows builds include the Npcap software +# * (https://npcap.com) for packet capture and transmission. It is under +# * separate license terms which forbid redistribution without special +# * permission. So the official Nmap Windows builds may not be redistributed +# * without special permission (such as an Nmap OEM license). +# * +# * Source is provided to this software because we believe users have a +# * right to know exactly what a program is going to do before they run it. +# * This also allows you to audit the software for security holes. +# * +# * Source code also allows you to port Nmap to new platforms, fix bugs, and add +# * new features. You are highly encouraged to submit your changes as a Github PR +# * or by email to the dev@nmap.org mailing list for possible incorporation into +# * the main distribution. Unless you specify otherwise, it is understood that +# * you are offering us very broad rights to use your submissions as described in +# * the Nmap Public Source License Contributor Agreement. This is important +# * because we fund the project by selling licenses with various terms, and also +# * because the inability to relicense code has caused devastating problems for +# * other Free Software projects (such as KDE and NASM). +# * +# * The free version of Nmap is distributed in the hope that it will be +# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, +# * indemnification and commercial support are all available through the +# * Npcap OEM program--see https://nmap.org/oem/ +# * +# ***************************************************************************/ + +import os +import sys + +from core.ArgvHandle import ArgvHandle +from gui.Application import Application + + +USAGE = """\ +Description: Show RadialNet application from Nmap XML file. It's better if Nmap +XML input file has traceroute information. If you don't know what is this, see +Nmap documentation. You can pass the Nmap xml file by command line or open it +later. + +Usage: radialnet.py [options] + +Options: + + -h, --help Show this help text. + -f Use as Nmap XML input. + +Suggestions or bug reports can be send to ignotus21_at_gmail_dot_com.\ +""" + +# check for psyco +try: + + import psyco + psyco.full() + +except ImportError: + print 'Running without psyco (http://psyco.sourceforge.net/).' + + + +if __name__ == '__main__': + + argvh = ArgvHandle(sys.argv) + + if argvh.has_option('-h') or argvh.has_option('--help'): + + print USAGE + sys.exit(0) + + application = Application() + + if argvh.has_option('-f'): + + file = argvh.get_option('-f') + + if file is None: + + print USAGE + sys.exit(0) + + else: + application.parse_nmap_xml_file(os.path.abspath(file)) + + application.start() diff --git a/zenmap/radialnet/share/sample/nmap_example.xml b/zenmap/radialnet/share/sample/nmap_example.xml new file mode 100644 index 0000000..5e689e8 --- /dev/null +++ b/zenmap/radialnet/share/sample/nmap_example.xml @@ -0,0 +1,480 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +