From 9c733881f9aabaec4b9e7f97b1b3129a7020956d Mon Sep 17 00:00:00 2001 From: Aitzol Berasategi Date: Sun, 26 Aug 2018 11:32:13 +0200 Subject: [PATCH] Initial commit --- .gitignore | 3 + README.md | 14 ++ __init__.py | 2 + app.py | 423 ++++++++++++++++++++++++++++++++++++++++++++++++++ manifest.json | 9 ++ parser.py | 41 +++++ 6 files changed, 492 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 __init__.py create mode 100644 app.py create mode 100644 manifest.json create mode 100644 parser.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b219768 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/__pycache__/* +macDB +source.txt diff --git a/README.md b/README.md new file mode 100644 index 0000000..e1bdfd6 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +**What is a MAC address OUI?** + +An OUI or Organizationally Unique Identifier is +the first 24 bits out of an 48 bit MAC address +which uniquely identifies a vendor, manufacturer, +or other organization. + +**Usage** + +Enter any MAC address, OUI or IAB and find its vendor or manufacturer. + +**License** + +GNU General Public License version 3 diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..24670d2 --- /dev/null +++ b/__init__.py @@ -0,0 +1,2 @@ +from . import parser +__all__ = [parser] diff --git a/app.py b/app.py new file mode 100644 index 0000000..e5caaa4 --- /dev/null +++ b/app.py @@ -0,0 +1,423 @@ +from io import open +from time import sleep +import os.path +import urllib.request +import sqlite3 +import parser +import re +import gi +from gi.repository import Gtk, Gdk, GLib +gi.require_version('Gtk', '3.0') + + +class AppWindow(Gtk.ApplicationWindow): + + mode = "OUI" + repeatFactor = "1" + isValid = False + result = "" + UI_INFO = """ + + + + + + + + + + + + + + + + """ + + def __init__(self): + + Gtk.Window.__init__(self, title=parser.manifest("name")) + self.set_default_size(300, 200) + + # --- menu --- # + self.action_group = Gtk.ActionGroup("my_actions") + + self.add_file_menu_actions(self.action_group) + self.add_help_menu_actions(self.action_group) + + self.uimanager = self.create_ui_manager() + self.uimanager.insert_action_group(self.action_group) + + self.menubar = self.uimanager.get_widget("/MenuBar") + + self.menuBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=50) + self.menuBox.pack_start(self.menubar, False, False, 0) + + # --- Label --- # + self.label = Gtk.Label() + self.label.set_text("Enter any MAC address, OUI, or IAB:") + self.label.set_justify(Gtk.Justification.LEFT) + self.label.set_hexpand(True) + + # --- Result Treeview --- # + self.history = Gtk.ListStore(str, str) + self.treeview = Gtk.TreeView(model=self.history) + + self.renderer_mac = Gtk.CellRendererText() + self.mac_column = Gtk.TreeViewColumn("MAC/OUI", self.renderer_mac, text=0) + self.treeview.append_column(self.mac_column) + + self.renderer_vendor = Gtk.CellRendererText() + self.renderer_vendor.set_property("editable", False) + self.vendor_column = Gtk.TreeViewColumn("Vendor", self.renderer_vendor, text=1) + self.treeview.append_column(self.vendor_column) + self.treeview.set_margin_start(10) + self.treeview.set_margin_end(10) + self.treeview.set_margin_bottom(10) + + # --- Entry --- # + self.entry = Gtk.Entry() + self.entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, "edit-clear") + self.entry.set_max_length(8) + self.entry.set_placeholder_text("00:00:00") + self.entry.connect("key-release-event", self.on_key_release) + self.entry.connect("icon-press", self.entry_icon_event) + self.entry.set_margin_start(10) + self.entry.set_margin_end(10) + + # --- Radio buttons --- # + self.box = Gtk.Box(spacing=10) + self.radioButton1 = Gtk.RadioButton.new_with_label_from_widget(None, "OUI") + self.radioButton1.connect("toggled", self.on_button_toggled, "OUI") + self.box.pack_start(self.radioButton1, False, False, 0) + self.box.set_margin_start(10) + self.box.set_margin_end(10) + + self.radioButton2 = Gtk.RadioButton.new_from_widget(self.radioButton1) + self.radioButton2.set_label("MAC") + self.radioButton2.connect("toggled", self.on_button_toggled, "MAC") + self.box.pack_start(self.radioButton2, False, False, 0) + + # --- Buttons --- # + self.search_button = Gtk.Button(label="Search") + self.search_button.connect("clicked", self.on_search_clicked) + self.search_button.set_margin_start(10) + self.search_button.set_margin_end(10) + + # --- ProgressBar --- # + self.progressbar = Gtk.ProgressBar() + + # --- Grid --- # + self.grid = Gtk.Grid() + self.grid.set_row_spacing(5) + + self.grid.add(self.menuBox) + self.grid.attach(self.label, 0, 2, 1, 1) + self.grid.attach_next_to(self.entry, self.label, Gtk.PositionType.BOTTOM, 1, 1) + self.grid.attach_next_to(self.box, self.entry, Gtk.PositionType.BOTTOM, 1, 1) + self.grid.attach_next_to(self.search_button, self.box, Gtk.PositionType.BOTTOM, 1, 1) + self.grid.attach_next_to(self.treeview, self.search_button, Gtk.PositionType.BOTTOM, 1, 1) + self.grid.attach_next_to(self.progressbar, self.treeview, Gtk.PositionType.BOTTOM, 1, 1) + + self.add(self.grid) + + def on_key_release(self, widget, event): + self.colon = False + print(event.keyval) + keyname = Gdk.keyval_name(event.keyval) + print(keyname) + + if len(self.entry.get_text()) == 2: + self.entry.set_text(self.entry.get_text() + ":") + self.colon = True + self.entry.set_position(-1) + elif len(self.entry.get_text()) == 5: + self.entry.set_text(self.entry.get_text() + ":") + self.colon = True + self.entry.set_position(-1) + elif len(self.entry.get_text()) == 8 and (self.mode == "MAC"): + self.entry.set_text(self.entry.get_text() + ":") + self.colon = True + self.entry.set_position(-1) + elif len(self.entry.get_text()) == 11 and (self.mode == "MAC"): + self.entry.set_text(self.entry.get_text() + ":") + self.colon = True + self.entry.set_position(-1) + elif len(self.entry.get_text()) == 14 and (self.mode == "MAC"): + self.entry.set_text(self.entry.get_text() + ":") + self.colon = True + self.entry.set_position(-1) + + if ((keyname == 'BackSpace') or (event.keyval == 65288)) and self.colon: + self.entry.set_text(self.entry.get_text()[:-2]) + self.entry.set_position(-1) + + for i in range(len(self.entry.get_text())): + self.check_mac(self.entry.get_text()) + + def check_mac(self, mac): + if re.match("[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){" + self.repeatFactor + "}$", mac.lower()): + self.entry.modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse("green")) + self.isValid = True + else: + self.entry.modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse("")) + self.isValid = False + + def entry_icon_event(self, widget, icon, event): + self.entry.set_text("") + + def on_search_clicked(self, widget): + if self.isValid: + if self.entry.get_text().find('-') != -1: + self.search = self.entry.get_text().replace('-', ':') + else: + self.search = self.entry.get_text() + + self.dbCursor.execute("SELECT * FROM data WHERE mac=?", (self.search.upper(),)) + self.rows = self.dbCursor.fetchall() + + if self.rows: + for row in self.rows: + print(row) + self.result = row + else: + if self.mode == "MAC": + self.search = self.search[:8] + self.dbCursor.execute("SELECT * FROM data WHERE mac=?", (self.search.upper(),)) + self.rows = self.dbCursor.fetchall() + if self.rows: + for row in self.rows: + print(row) + self.result = row + else: + self.result = (self.search.upper(), 'Sorry, No results found!') + print(self.result) + + else: + self.result = (self.search.upper(), 'Sorry, No results found!') + print(self.result) + + self.history.append(self.result) + print(self.history) + + def on_button_toggled(self, button, name): + if button.get_active(): + self.mode = name + if self.mode == "OUI": + self.entry.set_max_length(8) + self.entry.set_placeholder_text("00:00:00") + self.repeatFactor = "1" + self.check_mac(self.entry.get_text()) + + else: + self.entry.set_max_length(17) + self.entry.set_placeholder_text("00:00:00:00:00:00") + self.repeatFactor = "4" + self.check_mac(self.entry.get_text()) + if (len(self.entry.get_text()) == 8) and (self.mode == "MAC"): + if self.entry.get_text().find('-') != -1: + self.search = self.entry.get_text().replace('-', ':') + else: + self.search = self.entry.get_text() + + self.entry.set_text(self.search + ":") + self.colon = True + self.entry.set_position(-1) + + print("Search mode: ", self.mode) + print("Repeat: ", self.repeatFactor) + + def file_check(self): + if not os.path.exists('source.txt'): + print("file does not exist") + download = self.on_update_clicked(self) + if download: + self.save_data() + else: + print(download) + + else: + return True + + def db_connect(self): + # database connection + self.dbConnection = sqlite3.connect("macDB") + self.dbCursor = self.dbConnection.cursor() + self.dbCursor.execute("CREATE TABLE IF NOT EXISTS data(mac VARCHAR(20), vendor VARCHAR(50), UNIQUE('mac'))") + + def save_data(self): + # read source file using "r"(read) parameter + source_file = open("source.txt", "r") + line = source_file.readlines() + source_file.close() + + data = [] + for i in range(len(line)): + try: + if(line[i][0] != "#") and (line[i][0] != "\n"): + mac = line[i].split('\t', -1)[0][:17] + if mac == "00:00:00": + vendor = "Xerox Corporation" + else: + vendor = (line[i].split('\t', -1)[-1])[:-1] + data.append((mac, vendor)) + except Exception as e: + print(e) + print(data) + + self.dbCursor.executemany("INSERT OR REPLACE INTO data VALUES(?,?)", data) + self.dbConnection.commit() + + # --- menu actions --- # + def add_file_menu_actions(self, action_group): + self.action_filemenu = Gtk.Action("FileMenu", "File", None, None) + action_group.add_action(self.action_filemenu) + + self.action_database = Gtk.Action("Database", "Database", None, None) + action_group.add_action(self.action_database) + + action_group.add_actions([ + ("Update", None, "Update", "U", "Update data base", + self.on_update_clicked) + ]) + + self.action_filequit = Gtk.Action("FileQuit", None, None, Gtk.STOCK_QUIT) + self.action_filequit.connect("activate", self.on_menu_file_quit) + action_group.add_action(self.action_filequit) + + def add_help_menu_actions(self, action_group): + action_group.add_actions([ + ("HelpMenu", None, "Help"), + ("? help", None, "? help", "H", None, + lambda x: self.launch_generic_dialog(self, "Help")), + ("About", None, "About", "A", None, + lambda x:self.launch_generic_dialog(self, "About")) + ]) + + def create_ui_manager(self): + self.uimanager = Gtk.UIManager() + + # Throws exception if something went wrong + self.uimanager.add_ui_from_string(self.UI_INFO) + + # Add the accelerator group to the toplevel window + self.accelgroup = self.uimanager.get_accel_group() + self.add_accel_group(self.accelgroup) + return self.uimanager + + def on_menu_file_quit(self, widget): + Gtk.main_quit() + + # --- Database source update --- # + def on_update_clicked(self, widget): + + print("Updating...") + self.progressbar.set_visible(True) + self.progressbar.set_fraction(0.10) + url = "https://gitlab.com/aitzol76/mac-vendors/raw/master/manuf" + # url = "https://github.com/wireshark/wireshark/raw/master/manuf" + try: + r = urllib.request.urlretrieve(url, "source.txt") + remote_file_size = r[1]["Content-Length"] + size = round(int(remote_file_size)) + fraction = round(size/10) + print(size) + print(fraction) + + def progress_bar(): + i = 0 + while i < size: + value = round(self.progressbar.get_fraction() + 0.1, 1) + self.progressbar.set_fraction(value) + print(self.progressbar.get_fraction()) + sleep(0.3) + i = i + fraction + print(i) + yield True + # while Gtk.events_pending(): + # Gtk.main_iteration() + if self.progressbar.get_fraction() == 1.0: + print("...updated!") + self.launch_dialog(self, "Database succesfully updated!", "Success") + break + + def run_progressbar_generator(function): + gen = function() + GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW) + + run_progressbar_generator(progress_bar) + return True + + except Exception as e: + self.launch_dialog(self, str(e), "Error") + return e + + def launch_dialog(self, widget, text, title): + dialog = UpdaterDialog(self, title) + dialog.label.set_text(text) + response = dialog.run() + + if response == Gtk.ResponseType.OK: + print("The OK button was clicked") + self.progressbar.set_visible(False) + dialog.destroy() + + def launch_generic_dialog(self, widget, title): + dialog = GenericDialog(self, title) + # dialog.label.set_text(text) + response = dialog.run() + + if response == Gtk.ResponseType.OK: + print("The OK button was clicked") + dialog.destroy() + + +# --- Dialogs --- # + + +class UpdaterDialog(Gtk.Dialog): + + def __init__(self, parent, title): + + Gtk.Dialog.__init__(self, title, parent, 0, + (Gtk.STOCK_OK, Gtk.ResponseType.OK)) + + self.set_default_size(150, 100) + self.label = Gtk.Label() + box = self.get_content_area() + box.set_border_width(10) + box.add(self.label) + self.show_all() + + +class GenericDialog(Gtk.Dialog): + + def __init__(self, parent, title): + + Gtk.Dialog.__init__(self, title, parent, 0, + (Gtk.STOCK_OK, Gtk.ResponseType.OK)) + + self.set_default_size(150, 100) + self.label = Gtk.Label() + self.label.set_justify(Gtk.Justification.LEFT) + self.label.set_margin_bottom(20) + box = self.get_content_area() + box.set_border_width(10) + box.add(self.label) + + if title == "Help": + self.label.set_markup(parser.file_parser("md", "README.md")) + elif title == "About": + self.label.set_markup(parser.file_parser("json", "manifest.json")) + + self.show_all() + + +win = AppWindow() +win.connect("destroy", Gtk.main_quit) +win.db_connect() +win.show_all() +if win.file_check(): + win.save_data() + win.progressbar.hide() +Gtk.main() diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..d29b81c --- /dev/null +++ b/manifest.json @@ -0,0 +1,9 @@ +{ + "name": "macBank", + "description": "Application that allows to search for manufacturers by MAC or OUI", + "title": "macBank", + "version": "1.0.0", + "author": "Aitzol Berasategi", + "maintainer": "wproject ", + "license":"GPL3" +} diff --git a/parser.py b/parser.py new file mode 100644 index 0000000..c69bf06 --- /dev/null +++ b/parser.py @@ -0,0 +1,41 @@ +from io import open +import json + +def file_parser(format, file): + + string = "" + if format=="md": + + with open(file, 'r') as myfile: + #data = myfile.read() + line=myfile.readlines() + myfile.close() + for i in range(len(line)): + if (line[i][0] == "*") and (line[i][-2] == "*"): + titleLine = "\n"+line[i][2:][:-3]+"\n" + string=string+titleLine + + elif (line[i][0]!="\n") and (line[i][0]!=" "): + regularLine = line[i] + string = string + regularLine + + return string + + elif format=="json": + + with open(file) as f: + jsonData = json.load(f) + + string = "" + jsonData['name'] + \ + "\n\nver: " + jsonData['version'] + \ + "\nLicense: " + jsonData['license'] + "\n" + jsonData['author'] + + return string + +def manifest(key): + + with open("manifest.json") as f: + jsonData = json.load(f) + + value = jsonData[key] + return value