# Copyright 2018 Wproject - Aitzol Berasategi - https://aitzol.eus # # This file is part of macBank Ver.1.0.0. # # This project is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 3. # # PyCalculator is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or ANY OTHER COMMERCIAL PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . from io import open from time import sleep import os.path import urllib.request import sqlite3 from development import parser import re import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, Gdk, GLib 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.mac48 = self.search[:8] # mac address first 3 octets self.dbCursor.execute("SELECT * FROM data WHERE mac=?", (self.mac48.upper(),)) self.rows = self.dbCursor.fetchall() if self.rows: for row in self.rows: if row[1] == "IEEE Registration Authority": self.iab_base = self.mac48+":00:00:00" self.dbCursor.execute("SELECT * FROM data WHERE mac<=? AND mac>=? ORDER BY mac DESC LIMIT 1", (self.search.upper(), self.iab_base.upper())) self.rows = self.dbCursor.fetchall() if self.rows: for row in self.rows: print(row) self.result = (self.search.upper(), row[1]) else: self.result = (self.search.upper(), 'Sorry, No results found!') print(self.result) else: self.result = (self.search.upper(), row[1]) 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()