├── .buildconfig ├── .gitignore ├── CHANGELOG ├── Library ├── __init__.py ├── generate_config.py ├── interface_control.py ├── netctl_functions.py ├── notifications.py ├── preferences.py ├── profile_editor.py ├── run_as_root.py └── scanning.py ├── MANIFEST.in ├── NetGUI.license ├── README.md ├── UI.glade ├── imgs ├── APScan.png ├── aboutLogo.png ├── connect.png ├── disconnect.png ├── exit.png ├── logo.png ├── newprofile.png └── preferences.png ├── main-old.py ├── main.py ├── scripts ├── .SRCINFO ├── PKGBUILD ├── UpdateVersions.py ├── netgui └── netgui.desktop └── setup.py /.buildconfig: -------------------------------------------------------------------------------- 1 | [default] 2 | name=Default 3 | runtime=host 4 | config-opts= 5 | run-opts= 6 | prefix=/home/codywd/.cache/gnome-builder/install/NetGUI/host 7 | app-id= 8 | postbuild= 9 | prebuild= 10 | default=true 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | [Dd]ebug/ 46 | [Rr]elease/ 47 | *_i.c 48 | *_p.c 49 | *.ilk 50 | *.meta 51 | *.obj 52 | *.pch 53 | *.pdb 54 | *.pgc 55 | *.pgd 56 | *.rsp 57 | *.sbr 58 | *.tlb 59 | *.tli 60 | *.tlh 61 | *.tmp 62 | *.vspscc 63 | .builds 64 | *.dotCover 65 | 66 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 67 | #packages/ 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | 76 | # Visual Studio profiler 77 | *.psess 78 | *.vsp 79 | 80 | # ReSharper is a .NET coding add-in 81 | _ReSharper* 82 | 83 | # Installshield output folder 84 | [Ee]xpress 85 | 86 | # DocProject is a documentation generator add-in 87 | DocProject/buildhelp/ 88 | DocProject/Help/*.HxT 89 | DocProject/Help/*.HxC 90 | DocProject/Help/*.hhc 91 | DocProject/Help/*.hhk 92 | DocProject/Help/*.hhp 93 | DocProject/Help/Html2 94 | DocProject/Help/html 95 | 96 | # Click-Once directory 97 | publish 98 | 99 | # Others 100 | [Bb]in 101 | [Oo]bj 102 | sql 103 | TestResults 104 | *.Cache 105 | ClientBin 106 | stylecop.* 107 | ~$* 108 | *.dbmdl 109 | Generated_Code #added for RIA/Silverlight projects 110 | 111 | # Backup & report files from converting an old project file to a newer 112 | # Visual Studio version. Backup files are not needed, because we have git ;-) 113 | _UpgradeReport_Files/ 114 | Backup*/ 115 | UpgradeLog*.XML 116 | 117 | 118 | 119 | ############ 120 | ## Windows 121 | ############ 122 | 123 | # Windows image file caches 124 | Thumbs.db 125 | 126 | # Folder config file 127 | Desktop.ini 128 | 129 | 130 | ############# 131 | ## Python 132 | ############# 133 | 134 | # Packages 135 | *.egg 136 | *.egg-info 137 | dist 138 | build 139 | eggs 140 | parts 141 | bin 142 | var 143 | sdist 144 | develop-eggs 145 | .installed.cfg 146 | 147 | # Installer logs 148 | pip-log.txt 149 | 150 | # Unit test / coverage reports 151 | .coverage 152 | .tox 153 | 154 | #Translations 155 | *.mo 156 | 157 | #Mr Developer 158 | .mr.developer.cfg 159 | 160 | # Mac crap 161 | .DS_Store 162 | 163 | #Other 164 | *.g2e 165 | *.nsi 166 | *.py[co] 167 | 168 | # Packages 169 | *.egg 170 | *.egg-info 171 | dist 172 | build 173 | eggs 174 | parts 175 | bin 176 | var 177 | sdist 178 | develop-eggs 179 | .installed.cfg 180 | 181 | # Installer logs 182 | pip-log.txt 183 | 184 | # Unit test / coverage reports 185 | .coverage 186 | .tox 187 | 188 | #Translations 189 | *.mo 190 | 191 | #Mr Developer 192 | .mr.developer.cfg 193 | 194 | .log 195 | *.log 196 | .cfg 197 | *.cfg 198 | .pwd 199 | *.pwd 200 | .pid 201 | *.pid 202 | *~ 203 | *.tar.gz 204 | .tar.gz 205 | .idea/ 206 | 207 | #vscode 208 | .vscode/ 209 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | 0.85 2 | 1. Proper multithreading (Will no longer freeze when scanning and connecting) 3 | 2. Fixed Issue #44 (Uncaught exception when wpa_supplicant fails or there are no networks) 4 | 3. Converted from os.path.join (or simple strings) to pathlib 5 | 4. Added Notification Center and Print notification methods. Follows preferences dialog choice. 6 | 7 | 0.8 8 | 1. Fixed "TypeError: onBtnExit() takes 2 positional arguments but 3 were given" when clicking "X" button. 9 | 2. Broke scanning routines out of GUI loop. 10 | 3. When in Profile View/NoWifiMode, now it will say whether or not 11 | you are connected. 12 | 4. Rewrote the --help/-h argument. 13 | 5. Added various tools. (NetCTL stop-all, ProfileView) 14 | 6. Now correctly skips the network that sometimes shows up labeled 15 | \x00\x00\x00\x00\x00... 16 | 7. Fixed bug where, if multiple profiles exist for same network 17 | (for example, MyNetwork and netgui_MyNetwork), it will 18 | connect to one, then instantly connect to the other. 19 | 8. Beginnings of profile editor. Will hopefully be much more 20 | advanced, with auto-completion, syntax highlighting, and 21 | attribute insertion in the future. 22 | 23 | 0.7.7 24 | 1. Fixed the various GI Import issues regarding require_version 25 | 2. Hardcoded image paths to ensure they always show. 26 | 3. Fixed GtkSourceView errors. 27 | 28 | 0.7.6 29 | 1. Fix the fact that NetGUI could no longer scan. 30 | 31 | 0.7.4 32 | 1. Fixed misspelling. Device spelt as deviec 33 | 2. Should now detect all wireless devices (it seems all wireless devices start with wl, instead of just wlp or wlan.) 34 | 35 | 0.7.3 36 | 1. Fixed issue #37 37 | 38 | 0.7.2 39 | 1. Fixed issue with setup.py, where it installed images to incorrect directory. 40 | 41 | 0.7 42 | 1. Fixed issue #15: "Failed to connect to non-global ctrl_ifname: (null) error: No such file or directory 43 | 2. Fixed issue where wpa_cli ignored the NetGUI selected interface, even if set through preferences 44 | 3. Changed dependency from notify-osd to notification-daemon. While I encounter issues if I DON'T use notify-osd, I will change it because it is a popular request. 45 | 4. If a profile already exists, use it, and don't make a new one. 46 | 5. Change to NoWifiMode through Menu (called Switch to Profile View) 47 | 6. Changed backend to internal routines of WPA. Killed two issues in one stone. 48 | 7. No longer shows blank networks. 49 | 8. If profile already exists for network, now it will correctly connect to it. 50 | 51 | Known issues for 0.7: 52 | 1. Preferences dialog does not work (except for preferred interface) 53 | 2. Help function does not work (has not been programmed at all) 54 | 3. On every scan, the following error occurs: ```ctrl_iface exists and seems to be in use - cannot override it``` 55 | 56 | 0.6.9 57 | 1. general version tick 58 | 2. package build updated for AUR 59 | 60 | v0.6.7 61 | 2 bugs fixed, #7 and an unsubmitted. 62 | 63 | 0.6.6 64 | 1. Finishing touches on No wifi Mode. 65 | 66 | 0.6.5 67 | 1. Manually change interface via preferences dialog. 68 | 2. Profiles menu now states all currently created profiles. 69 | 3. No wireless card? Disabled it? NetGUI still functions, just not with 70 | the same abilities. 71 | 72 | 0.6 73 | 1. Removed dependency for wireless-tools 74 | 2. Changed scanning method to wpa_cli 75 | 76 | Known Issues for 0.6: 77 | 1. Sometimes, a blank network will appear. I'm working on figuring out how to avoid this. 78 | 79 | 0.5 80 | 1. Updated PKGBUILD 81 | 2. Changed Quality from fractions to percenteges. 82 | 3. Fixed netgui script to actually work. 83 | 4. Fixed setup.py to correctly install the various items. 84 | 5. Added notifications to the program. 85 | 6. Added new dependency: notify-osd 86 | 87 | 0.4 88 | 1. Changed NetCTL to use "_" inbetween it's name and the name of the network when creating a profile, instead of "-", because of how systemd treats "-" as a special character. 89 | 2. Fixed a bug where every network was either "Open" or "WEP." It now correctly states "WPA" if that is what it is. 90 | 3. Fixed the column layout. Basically, "Connection Quality" listed the security type, and "Security Type" listed the connection quality. 91 | 4. Fixed the "Connected?" column to correctly state if you are connected to a network. 92 | -------------------------------------------------------------------------------- /Library/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codywd/NetGUI/9a2d0da393d7e81f257a769893edfca84627ce65/Library/__init__.py -------------------------------------------------------------------------------- /Library/generate_config.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | class GenConfig(): 4 | def __init__(self, profile_dir): 5 | self.profile_dir = profile_dir 6 | 7 | def create_wireless_config(self, name, interface, security, key, ip='dhcp'): 8 | print("Creating Profile! Don't interrupt!\n") 9 | filename = "netgui_" + name 10 | f = open(Path(self.profile_dir, filename), 'w+') 11 | f.write("Description='This profile was generated by netgui for " + str(name)+".'\n" + 12 | "Interface=" + str(interface) + "\n" + 13 | "Connection=wireless\n" + 14 | "Security=" + str(security) + "\n" + 15 | "ESSID='" + str(name) + "'\n") 16 | if key: 17 | f.write(r"Key='" + key + "'\n") 18 | else: 19 | f.write(r'Key=None') 20 | f.write("\nIP=%s\n" % ip) 21 | f.close() 22 | print("Alright, I have finished making the profile!") -------------------------------------------------------------------------------- /Library/interface_control.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | from gi.repository import Gio 3 | 4 | class InterfaceControl(): 5 | # Control the network interface. Examples are wlan0, wlp9s0, wlp2s0, etc... 6 | 7 | def __init__(self): 8 | super(InterfaceControl, self).__init__() 9 | 10 | @staticmethod 11 | def down(interface): 12 | print("interface:: down: " + interface) 13 | process = Gio.Subprocess.new(["ip", "link", "set", "down", "dev", interface], 0) 14 | process.wait() 15 | 16 | @staticmethod 17 | def up(interface): 18 | print("interface:: up: " + interface) 19 | process = Gio.Subprocess.new(["ip", "link", "set", "up", "dev", interface], 0) 20 | process.wait() 21 | -------------------------------------------------------------------------------- /Library/netctl_functions.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | from gi.repository import Gio 3 | 4 | 5 | class NetCTL(object): 6 | # These functions are to separate the Netctl code 7 | # from the GUI code. 8 | def __init__(self): 9 | super(NetCTL, self).__init__() 10 | 11 | @staticmethod 12 | def start(network): 13 | print("netctl:: start " + network) 14 | process = Gio.Subprocess.new(["netctl", "start", network], 0) 15 | process.wait_async() 16 | print("netctl:: started " + network) 17 | 18 | @staticmethod 19 | def stop(network): 20 | print("netctl:: stop " + network) 21 | process = Gio.Subprocess.new(["netctl", "stop", network], 0) 22 | process.wait() 23 | 24 | @staticmethod 25 | def stop_all(): 26 | print("netctl:: stop-all") 27 | process = Gio.Subprocess.new(["netctl", "stop-all"], 0) 28 | process.wait() 29 | 30 | @staticmethod 31 | def restart(network): 32 | print("netctl:: restart " + network) 33 | process = Gio.Subprocess.new(["netctl", "restart", network], 0) 34 | process.wait() 35 | 36 | @staticmethod 37 | def list(): 38 | print("netctl:: list") 39 | process = Gio.Subprocess.new(["netctl", "list"], 0) 40 | process.wait() 41 | 42 | @staticmethod 43 | def enable(network): 44 | print("netctl:: enable " + network) 45 | process = Gio.Subprocess.new(["netctl", "enable", network], 0) 46 | process.wait() 47 | 48 | @staticmethod 49 | def disable(network): 50 | print("netctl:: disable " + network) 51 | process = Gio.Subprocess.new(["netctl", "disable", network], 0) 52 | process.wait() -------------------------------------------------------------------------------- /Library/notifications.py: -------------------------------------------------------------------------------- 1 | import json 2 | from pathlib import Path 3 | from gi.repository import Notify 4 | 5 | class Notification(): 6 | def __init__(self): 7 | preferences_file = Path("/", "var", "lib", "netgui", "preferences.json") 8 | 9 | user_prefs = json.load(open(preferences_file)) 10 | 11 | if "Center" in user_prefs["notification_type"]: 12 | self.notification_type = "NotificationCenter" 13 | Notify.init("netgui") 14 | elif "Message" in user_prefs["notification_type"]: 15 | self.notification_type = "MessageBoxes" 16 | elif "Terminal" in user_prefs["notification_type"]: 17 | self.notification_type = "print" 18 | 19 | def show_notification(self, title, message): 20 | if self.notification_type == "NotificationCenter": 21 | n = Notify.Notification.new(title, message, "dialog-information") 22 | n.set_timeout(1000) 23 | n.show() 24 | elif self.notification_type == "MessageBoxes": 25 | pass 26 | elif self.notification_type == "print": 27 | print("{}: {}".format(title, message)) -------------------------------------------------------------------------------- /Library/preferences.py: -------------------------------------------------------------------------------- 1 | # Import Standard Libraries 2 | import json 3 | import os 4 | from pathlib import Path 5 | import sys 6 | 7 | # Import Third Party Libraries 8 | from gi.repository import Gtk, Gdk, GObject, GLib, GtkSource 9 | 10 | class Preferences(Gtk.Window): 11 | def __init__(self, program_loc): 12 | # Init Vars 13 | self.pref_file = Path("/", "var", "lib", "netgui", "preferences.json") 14 | self.builder = Gtk.Builder() 15 | self.program_loc = program_loc 16 | self.status_dir = Path("/", "var", "lib", "netgui") 17 | self.init_ui() 18 | 19 | def init_ui(self): 20 | GObject.type_register(GtkSource.View) 21 | self.builder.add_from_file(str(Path(self.program_loc, "UI.glade"))) 22 | 23 | # Get everything we need from UI.glade 24 | go = self.builder.get_object 25 | self.preferences_dialog = go("prefDialog") 26 | save_button = go("saveButton") 27 | cancel_button = go("cancelButton") 28 | self.interface_entry = go("wiInterface") 29 | self.default_profile = go("defaultProfilePath") 30 | self.unsecure_switch = go("unsecureSwitch") 31 | self.autoconnect_switch = go("autoconnectSwitch") 32 | self.notification_type = go("notification_type") 33 | filechooser = go("chooseDefaultProfile") 34 | 35 | # Connecting the "clicked" signals of each button to the relevant function. 36 | save_button.connect("clicked", self.save_clicked) 37 | cancel_button.connect("clicked", self.cancel_clicked) 38 | self.preferences_dialog.connect("show", self.on_load) 39 | filechooser.connect("clicked", self.choose_profile) 40 | # Opening the Preferences Dialog. 41 | self.preferences_dialog.run() 42 | 43 | def cancel_clicked(self, e): 44 | self.preferences_dialog.hide() 45 | 46 | def choose_profile(self, e): 47 | dialog = Gtk.FileChooserDialog("Choose your default profile.", None, 48 | Gtk.FileChooserAction.OPEN, 49 | (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, 50 | Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) 51 | dialog.set_current_folder("/etc/netctl") 52 | 53 | response = dialog.run() 54 | if response == Gtk.ResponseType.OK: 55 | self.default_profile.set_text(dialog.get_filename()) 56 | elif response == Gtk.ResponseType.CANCEL: 57 | print("Cancel clicked") 58 | 59 | dialog.destroy() 60 | 61 | def save_clicked(self, e): 62 | new_int = self.interface_entry.get_text() 63 | with open(Path(self.status_dir, "interface.cfg"), 'w+') as interface_file: 64 | interface_file.write(new_int) 65 | 66 | json_prefs = { 67 | "default_profile": self.default_profile.get_text(), 68 | "unsecure_status": self.unsecure_switch.get_active(), 69 | "autoconnect": self.autoconnect_switch.get_active(), 70 | "notification_type": self.notification_type.get_active_text() 71 | } 72 | 73 | with open(self.pref_file, 'w+') as outfile: 74 | json.dump(json_prefs, outfile) 75 | 76 | self.preferences_dialog.destroy() 77 | 78 | def on_load(self, e): 79 | f = open(Path(self.status_dir, "interface.cfg"), 'r+') 80 | data = json.load(open(self.pref_file)) 81 | self.interface_entry.set_text(str(f.read())) 82 | 83 | self.default_profile.set_text(data["default_profile"]) 84 | self.unsecure_switch.set_active(data["unsecure_status"]) 85 | self.autoconnect_switch.set_active(data["autoconnect"]) 86 | if "Center" in data["notification_type"]: 87 | self.notification_type.set_active_id("1") 88 | elif "Message" in data["notification_type"]: 89 | self.notification_type.set_active_id("2") 90 | elif "Terminal" in data["notification_type"]: 91 | self.notification_type.set_active_id("3") 92 | 93 | f.close() -------------------------------------------------------------------------------- /Library/profile_editor.py: -------------------------------------------------------------------------------- 1 | # Import Standard Libraries 2 | import csv 3 | import fcntl 4 | import fileinput 5 | import multiprocessing 6 | import os 7 | import shutil 8 | import subprocess 9 | import sys 10 | import webbrowser 11 | 12 | # Setting base app information, such as version, and configuration directories/files. 13 | profile_dir = "/etc/netctl/" 14 | #program_loc = "/usr/share/netgui/" 15 | program_loc = "./" 16 | status_dir = "/var/lib/netgui/" 17 | 18 | # Import Third Party Libraries 19 | from gi.repository import Gtk, Gdk, GObject, GLib, GtkSource 20 | from gi.repository import Notify 21 | 22 | # Import Personal Files 23 | 24 | class NetGUIProfileEditor(Gtk.Window): 25 | def __init__(self, profile_to_edit): 26 | # Init Vars 27 | self.scanning = False 28 | self.APindex = 0 29 | self.profile_to_edit = profile_to_edit 30 | self.builder = Gtk.Builder() 31 | 32 | def show(self): 33 | self.init_ui() 34 | 35 | def init_ui(self): 36 | # Create a "Builder", which basically allows me to import the Glade file for a complete interface. 37 | # I love Glade, btw. So much quicker than manually coding everything. 38 | GObject.type_register(GtkSource.View) 39 | self.builder.add_from_file(program_loc + "UI.glade") 40 | 41 | go = self.builder.get_object 42 | self.profile_editor = go("profileEditor") 43 | menu_prof_exit = go("exitProfWin") 44 | save_button = go("saveMenuProf") 45 | save_tool_button = go("saveToolBtn") 46 | open_tool_button = go("openToolBtn") 47 | clear_button = go("clearToolBtn") 48 | attributes_button = go("attributesToolBtn") 49 | exit_profile_button = go("exitToolBtn") 50 | self.netctlEditor = go("profileEditorView") 51 | self.buffer = go("gtksourcebuffer") 52 | 53 | # Connecting the "clicked" signals of each button to the relevant function. 54 | save_button.connect("activate", self.save_clicked) 55 | save_tool_button.connect("clicked", self.save_clicked) 56 | attributes_button.connect("clicked", self.attributes_clicked) 57 | clear_button.connect("clicked", self.clear_clicked) 58 | self.profile_editor.connect("show", self.on_load) 59 | exit_profile_button.connect("clicked", self.exit_prof_clicked) 60 | menu_prof_exit.connect("activate", self.exit_prof_clicked) 61 | open_tool_button.connect("clicked", self.open_clicked) 62 | # Opening the Prefereces Dialog. 63 | self.profile_editor.show_all() 64 | 65 | def open_clicked(self, e): 66 | pass 67 | 68 | def save_clicked(self, e): 69 | pass 70 | 71 | def attributes_clicked(self, e): 72 | pass 73 | 74 | def clear_clicked(self, e): 75 | pass 76 | 77 | def on_load(self, e): 78 | if self.profile_to_edit is None: 79 | pass 80 | else: 81 | try: 82 | txt=open(self.profile_to_edit, 'r').read() 83 | except e: 84 | print(e) 85 | self.buffer.set_text(txt) 86 | self.buffer.set_modified(False) 87 | self.buffer.place_cursor(self.buffer.get_start_iter()) 88 | 89 | def exit_prof_clicked(self, e): 90 | self.profile_editor.hide() 91 | -------------------------------------------------------------------------------- /Library/run_as_root.py: -------------------------------------------------------------------------------- 1 | import fcntl 2 | import subprocess 3 | import sys 4 | 5 | class run_as_root(): 6 | def __init__(self): 7 | self.pid_file = None 8 | 9 | @staticmethod 10 | def set_pid_file(self, pid_number, pid_file): 11 | # Let's also not allow any more than one instance of netgui. 12 | self.pid_file = open(pid_file, 'w') 13 | try: 14 | fcntl.lockf(self.pid_file, fcntl.LOCK_EX | fcntl.LOCK_NB) 15 | except IOError: 16 | print("We only allow one instance of netgui to be running at a time for precautionary reasons.") 17 | sys.exit(1) 18 | 19 | self.pid_file.write(str(pid_number)+"\n") 20 | self.pid_file.flush() 21 | 22 | def close_pid_file(self): 23 | fcntl.lockf(self.pid_file, fcntl.LOCK_UN) 24 | self.pid_file.close() -------------------------------------------------------------------------------- /Library/scanning.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | import subprocess 4 | 5 | class ScanRoutines(): 6 | def __init__(self, interface, scan_file, status_dir, scan_completion_queue): 7 | super(ScanRoutines, self).__init__() 8 | self.scan_file = scan_file 9 | self.status_dir = status_dir 10 | self.interface = interface 11 | self.scan_completion_queue = scan_completion_queue 12 | 13 | def scan(self): 14 | print("Scanning! Please wait...") 15 | if Path(self.scan_file).exists(): 16 | os.remove(self.scan_file) 17 | if Path(self.status_dir, "final_results.log").exists(): 18 | os.remove(Path(self.status_dir, "final_results.log")) 19 | # Huge thanks to joukewitteveen on GitHub for the following command!! Slightly modified from his comment 20 | subprocess.call('bash -c "source /usr/lib/netctl/globals; source /usr/lib/netctl/wpa; wpa_supplicant_scan ' + 21 | self.interface + ' 3,4,5" >> ' + str(self.scan_file), shell=True) 22 | print("Done scanning!") 23 | self.scan_completion_queue.put("Finished") -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include main.py README.md NetGUI.license UI.glade 2 | -------------------------------------------------------------------------------- /NetGUI.license: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Cody Dostal, Pivotraze 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation files 5 | (the "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. The 13 | end-user documentation included with the redistribution, if any, must 14 | include the following acknowledgment: "This product includes software 15 | developed by Cody Dostal https://github.com/codywd and his 16 | contributors", in the same place and form as other third-party 17 | acknowledgments. Alternatively, this acknowledgment may appear in the 18 | software itself, in the same form and location as other such third-party 19 | acknowledgments. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 25 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 26 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 27 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NetGUI v0.85 2 | 3 | NetGUI is an official fork of what was originally known as WiFiz. NetGUI is a GUI frontend to NetCTL, a network manager developed for Arch Linux. 4 | 5 | ## General Notes 6 | NetGUI is in beta state. NetGUI is a rewrite from scratch of WiFiz, to take advantage of Python3 and GTK+3. NetGUI is generally considered stable now, and should be as functional, if not more so, than WiFiz. We are still developing, so be prepared for a break in this program for now (I recommend you know netctl's syntax.) 7 | 8 | ## Why fork WiFiz into NetGUI? 9 | 1. Wifiz is not very clean. The code is jumbled and not easy to maintain. 10 | 2. Python 3 has many advantages over Python 2, namely speed. I want to use that, and I can't with WiFiz/wxPython. 11 | 3. I want to use PyGObject for further integration with GNOME/GTK-based-window-managers. 12 | 4. I love programming, and hate when my code is unreadable (see: point 1.) 13 | 14 | ## Dependencies 15 | 1. Python3 16 | 2. python-gobject 17 | 3. netctl 18 | 4. notification-daemon 19 | 20 | ## Known Issues 21 | 1. Issue #21 (Preferences dialog is not implemented.) 22 | 2. Issue #23 (Help function is not implemented.) 23 | 3. Issue #25 (No Tray Icon) 24 | 4. Issue #28 (Profile Editor is not implemented.) 25 | 5. Issue #45 (Enhancement: add support for 802.1x with certificates etc. on wire) 26 | 27 | ## General todo list / wish list 28 | 29 | - [ ] add tray icon 30 | - [ ] interface with auto roaming 31 | - [ ] mild network diagnostics 32 | - [ ] Incorporate surfatwork's Netctl icon/applet for Gnome shell (https://bbs.archlinux.org/viewtopic.php?id=182826) 33 | - [ ] Incorporate a launcher in Activities menu. 34 | -------------------------------------------------------------------------------- /UI.glade: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | False 6 | 5 7 | About NetGUI 8 | False 9 | True 10 | normal 11 | NetGUI 12 | 0.85 13 | Copyright (c) 2018. Cody Dostal 14 | NetGUI is a GUI frontend to NetCTL, a network manager developed for Arch Linux. 15 | https://github.com/codywd/NetGUI 16 | GitHub 17 | Copyright (c) 2018 Cody Dostal, Pivotraze 18 | 19 | Permission is hereby granted, free of charge, to any person 20 | obtaining a copy of this software and associated documentation files 21 | (the "Software"), to deal in the Software without restriction, including 22 | without limitation the rights to use, copy, modify, merge, publish, 23 | distribute, sublicense, and/or sell copies of the Software, and to 24 | permit persons to whom the Software is furnished to do so, subject to 25 | the following conditions: 26 | 27 | The above copyright notice and this permission notice shall be 28 | included in all copies or substantial portions of the Software. The 29 | end-user documentation included with the redistribution, if any, must 30 | include the following acknowledgment: "This product includes software 31 | developed by Cody Dostal https://github.com/codywd and its 32 | contributors", in the same place and form as other third-party 33 | acknowledgments. Alternatively, this acknowledgment may appear in the 34 | software itself, in the same form and location as other such third-party 35 | acknowledgments. 36 | 37 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 38 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 39 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 40 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 41 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 42 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 43 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 44 | Cody Dostal (a.k.a. Pivotraze) <cody@dostalsecurity.com> 45 | Gregory Mullen (a.k.a. GrayHatter) <greg@grayhatter.com> 46 | Sam Stuewe <halosghost@archlinux.info> 47 | imgs/aboutLogo.png 48 | True 49 | custom 50 | 51 | 52 | 53 | 54 | 55 | False 56 | vertical 57 | 2 58 | 59 | 60 | False 61 | end 62 | 63 | 64 | False 65 | True 66 | end 67 | 0 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | True 80 | False 81 | 82 | 83 | True 84 | False 85 | 86 | 87 | True 88 | False 89 | 90 | 91 | True 92 | False 93 | 94 | 95 | True 96 | False 97 | 98 | 99 | False 100 | NetGUI 101 | center 102 | 103 | 104 | 105 | 106 | 107 | 108 | True 109 | False 110 | vertical 111 | 112 | 113 | True 114 | False 115 | 116 | 117 | True 118 | False 119 | _File 120 | True 121 | 122 | 123 | True 124 | False 125 | 126 | 127 | True 128 | False 129 | Scan for New Networks within range. 130 | Scan for New Networks. 131 | True 132 | 133 | 134 | 135 | 136 | 137 | True 138 | False 139 | 140 | 141 | 142 | 143 | True 144 | False 145 | Exit the program. 146 | Exit 147 | True 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | True 158 | False 159 | _Edit 160 | True 161 | 162 | 163 | True 164 | False 165 | 166 | 167 | True 168 | False 169 | Edit the preferences for the program. 170 | Preferences 171 | True 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | True 182 | False 183 | Tools 184 | True 185 | 186 | 187 | True 188 | False 189 | 190 | 191 | True 192 | False 193 | Disconnect from all connected networks. 194 | Disconnect All 195 | True 196 | 197 | 198 | 199 | 200 | 201 | True 202 | False 203 | Profile Editor 204 | True 205 | 206 | 207 | 208 | 209 | 210 | True 211 | False 212 | Switch to Profile View 213 | True 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | True 224 | False 225 | _Profiles 226 | True 227 | 228 | 229 | 230 | 231 | True 232 | False 233 | _Help 234 | True 235 | 236 | 237 | True 238 | False 239 | 240 | 241 | True 242 | False 243 | Access the help documents. 244 | Help with NetGUI 245 | True 246 | 247 | 248 | 249 | 250 | 251 | True 252 | False 253 | 254 | 255 | 256 | 257 | True 258 | False 259 | Report an Issue with NetGUI. 260 | Report an Issue... 261 | True 262 | 263 | 264 | 265 | 266 | 267 | True 268 | False 269 | 270 | 271 | 272 | 273 | True 274 | False 275 | About NetGUI 276 | About NetGUI 277 | True 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | False 288 | True 289 | 0 290 | 291 | 292 | 293 | 294 | True 295 | False 296 | 297 | 298 | True 299 | False 300 | Scan for new wireless networks within range. 301 | Scan 302 | True 303 | image1 304 | 305 | 306 | 307 | False 308 | True 309 | 310 | 311 | 312 | 313 | True 314 | False 315 | Connect to selected network. 316 | Connect 317 | True 318 | image2 319 | 320 | 321 | 322 | False 323 | True 324 | 325 | 326 | 327 | 328 | True 329 | False 330 | Disconnect from selected network. 331 | dConnect 332 | True 333 | image3 334 | 335 | 336 | 337 | False 338 | True 339 | 340 | 341 | 342 | 343 | True 344 | False 345 | Preferences 346 | toolbutton1 347 | True 348 | image5 349 | 350 | 351 | 352 | False 353 | True 354 | 355 | 356 | 357 | 358 | True 359 | False 360 | 361 | 362 | False 363 | False 364 | 365 | 366 | 367 | 368 | True 369 | False 370 | Exit the program. 371 | Exit 372 | True 373 | image4 374 | 375 | 376 | 377 | False 378 | True 379 | 380 | 381 | 382 | 383 | False 384 | True 385 | 1 386 | 387 | 388 | 389 | 390 | True 391 | False 392 | never 393 | out 394 | 300 395 | False 396 | 397 | 398 | True 399 | True 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | True 408 | True 409 | 2 410 | 411 | 412 | 413 | 414 | True 415 | False 416 | vertical 417 | 2 418 | 419 | 420 | False 421 | True 422 | 3 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | False 432 | popup 433 | Network Password 434 | dialog 435 | 436 | 437 | 438 | 439 | 440 | False 441 | vertical 442 | 2 443 | 444 | 445 | False 446 | end 447 | 448 | 449 | gtk-cancel 450 | True 451 | True 452 | True 453 | True 454 | 455 | 456 | True 457 | True 458 | 0 459 | 460 | 461 | 462 | 463 | gtk-ok 464 | True 465 | True 466 | True 467 | half 468 | True 469 | 0.47999998927116394 470 | 471 | 472 | True 473 | True 474 | 1 475 | 476 | 477 | 478 | 479 | False 480 | True 481 | end 482 | 0 483 | 484 | 485 | 486 | 487 | True 488 | False 489 | vertical 490 | 491 | 492 | True 493 | False 494 | What is the password? 495 | 496 | 497 | False 498 | True 499 | 0 500 | 501 | 502 | 503 | 504 | True 505 | True 506 | False 507 | * 508 | 509 | 510 | False 511 | True 512 | 1 513 | 514 | 515 | 516 | 517 | False 518 | True 519 | 1 520 | 521 | 522 | 523 | 524 | 525 | pwdCancelBtn 526 | pwdOkBtn 527 | 528 | 529 | 530 | False 531 | 5 532 | Preferences 533 | dialog 534 | 535 | 536 | 537 | 538 | 539 | False 540 | vertical 541 | 2 542 | top 543 | 544 | 545 | False 546 | end 547 | 548 | 549 | gtk-cancel 550 | True 551 | True 552 | True 553 | True 554 | 555 | 556 | False 557 | True 558 | 0 559 | 560 | 561 | 562 | 563 | gtk-save 564 | True 565 | True 566 | True 567 | True 568 | 569 | 570 | False 571 | True 572 | 1 573 | 574 | 575 | 576 | 577 | False 578 | True 579 | end 580 | 0 581 | 582 | 583 | 584 | 585 | True 586 | False 587 | True 588 | True 589 | 590 | 591 | True 592 | False 593 | Default Profile: 594 | 595 | 596 | 0 597 | 0 598 | 599 | 600 | 601 | 602 | True 603 | False 604 | Autoconnect to Unsecured Networks. 605 | Auto-Unsecure: 606 | 607 | 608 | 0 609 | 1 610 | 611 | 612 | 613 | 614 | True 615 | False 616 | Autoconnect to created classes. 617 | Autoconnect: 618 | 619 | 620 | 0 621 | 2 622 | 623 | 624 | 625 | 626 | True 627 | False 628 | Interface: 629 | 630 | 631 | 0 632 | 3 633 | 634 | 635 | 636 | 637 | True 638 | True 639 | wlpXsX, wlanX, ethX, etc... 640 | 641 | 642 | 1 643 | 3 644 | 645 | 646 | 647 | 648 | True 649 | False 650 | Notification Type: 651 | 652 | 653 | 0 654 | 4 655 | 656 | 657 | 658 | 659 | True 660 | False 661 | 662 | 663 | True 664 | True 665 | 666 | 667 | 668 | False 669 | True 670 | 0 671 | 672 | 673 | 674 | 675 | ... 676 | True 677 | True 678 | True 679 | center 680 | center 681 | 682 | 683 | False 684 | True 685 | 1 686 | 687 | 688 | 689 | 690 | 1 691 | 0 692 | 693 | 694 | 695 | 696 | True 697 | True 698 | start 699 | center 700 | 701 | 702 | 1 703 | 1 704 | 705 | 706 | 707 | 708 | True 709 | True 710 | start 711 | center 712 | 713 | 714 | 1 715 | 2 716 | 717 | 718 | 719 | 720 | True 721 | False 722 | 1 723 | 724 | Notification Center 725 | GTK MessageBox 726 | Python "Print" (Terminal) 727 | 728 | 729 | 730 | 1 731 | 4 732 | 733 | 734 | 735 | 736 | True 737 | True 738 | 1 739 | 740 | 741 | 742 | 743 | 744 | cancelButton 745 | saveButton 746 | 747 | 748 | 749 | False 750 | NetGUI Profile Editor 751 | 752 | 753 | 754 | 755 | 756 | True 757 | False 758 | vertical 759 | 760 | 761 | True 762 | False 763 | 764 | 765 | True 766 | False 767 | _File 768 | True 769 | 770 | 771 | True 772 | False 773 | 774 | 775 | True 776 | False 777 | Save 778 | True 779 | 780 | 781 | 782 | 783 | True 784 | False 785 | 786 | 787 | 788 | 789 | True 790 | False 791 | Exit 792 | True 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | False 802 | True 803 | 0 804 | 805 | 806 | 807 | 808 | True 809 | False 810 | 811 | 812 | True 813 | False 814 | Open 815 | True 816 | gtk-open 817 | 818 | 819 | False 820 | True 821 | 822 | 823 | 824 | 825 | True 826 | False 827 | Save 828 | True 829 | gtk-save 830 | 831 | 832 | False 833 | True 834 | 835 | 836 | 837 | 838 | True 839 | False 840 | Clear 841 | True 842 | gtk-clear 843 | 844 | 845 | False 846 | True 847 | 848 | 849 | 850 | 851 | True 852 | False 853 | Attributes 854 | True 855 | gtk-properties 856 | 857 | 858 | False 859 | True 860 | 861 | 862 | 863 | 864 | True 865 | False 866 | 867 | 868 | False 869 | True 870 | 871 | 872 | 873 | 874 | True 875 | False 876 | Exit 877 | True 878 | gtk-quit 879 | 880 | 881 | False 882 | True 883 | 884 | 885 | 886 | 887 | False 888 | True 889 | 1 890 | 891 | 892 | 893 | 894 | True 895 | True 896 | 2 897 | 2 898 | True 899 | True 900 | 4 901 | True 902 | True 903 | True 904 | True 905 | 906 | 907 | True 908 | True 909 | 2 910 | 911 | 912 | 913 | 914 | True 915 | False 916 | 10 917 | 10 918 | 10 919 | 10 920 | 6 921 | 6 922 | vertical 923 | 2 924 | 925 | 926 | False 927 | True 928 | 3 929 | 930 | 931 | 932 | 933 | 934 | -------------------------------------------------------------------------------- /imgs/APScan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codywd/NetGUI/9a2d0da393d7e81f257a769893edfca84627ce65/imgs/APScan.png -------------------------------------------------------------------------------- /imgs/aboutLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codywd/NetGUI/9a2d0da393d7e81f257a769893edfca84627ce65/imgs/aboutLogo.png -------------------------------------------------------------------------------- /imgs/connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codywd/NetGUI/9a2d0da393d7e81f257a769893edfca84627ce65/imgs/connect.png -------------------------------------------------------------------------------- /imgs/disconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codywd/NetGUI/9a2d0da393d7e81f257a769893edfca84627ce65/imgs/disconnect.png -------------------------------------------------------------------------------- /imgs/exit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codywd/NetGUI/9a2d0da393d7e81f257a769893edfca84627ce65/imgs/exit.png -------------------------------------------------------------------------------- /imgs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codywd/NetGUI/9a2d0da393d7e81f257a769893edfca84627ce65/imgs/logo.png -------------------------------------------------------------------------------- /imgs/newprofile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codywd/NetGUI/9a2d0da393d7e81f257a769893edfca84627ce65/imgs/newprofile.png -------------------------------------------------------------------------------- /imgs/preferences.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codywd/NetGUI/9a2d0da393d7e81f257a769893edfca84627ce65/imgs/preferences.png -------------------------------------------------------------------------------- /main-old.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Basic TODO: 3 | 1. Write help file (html/css/js) 4 | 2. Finish preferences dialog (DONE!) 5 | 2a. Implement preference choices throughout the program. 6 | 3. Make notifications optional 7 | 4. Write webbrowser.open wrapper script, where gid and uid 8 | set to the user, so it correctly runs, and doesn't 9 | error because it is set as root. 10 | 5. Add tray icon 11 | 6. Auto roaming capabilities (Preferences default profile, maybe 12 | set for multiple default profiles? NetCTL enable) 13 | 7. Basic network diagnostics? 14 | 8. Incorporate surfatwork's NetCTL icon/applet for Gnome Shell 15 | (All actual coding is done for his, just incorporate it into 16 | ours somehow). (https://bbs.archlinux.org/viewtopic.php?id=182826) 17 | (Work in progress! Watch https://github.com/codywd/netctlgnome to watch the progress.) 18 | 19 | ''' 20 | 21 | #! /usr/bin/python3 22 | 23 | # Import Standard Libraries 24 | import argparse 25 | import csv 26 | import fcntl 27 | import fileinput 28 | import os 29 | from pathlib import Path 30 | import shutil 31 | import subprocess 32 | import sys 33 | 34 | # Setting base app information, such as version, and configuration directories/files. 35 | prog_ver = "0.8" 36 | profile_dir = Path("/", "etc", "netctl") 37 | status_dir = Path("/", "var", "lib", "netgui") 38 | program_loc = Path("/", "/usr", "share", "netgui") 39 | interface_conf_file = Path(status_dir, "interface.cfg") 40 | license_dir = Path("/", "usr", "share", "licenses", "netgui") 41 | scan_file = Path(status_dir, "scan_results.log") 42 | pid_file = Path(status_dir, "program.pid") 43 | img_loc = Path(program_loc, "imgs") 44 | pref_file = Path(status_dir, "preferences.cfg") 45 | pid_number = os.getpid() 46 | arg_no_wifi = 0 47 | 48 | file_to_pass = "" 49 | 50 | # Create Directories if needed 51 | if not Path(status_dir).exists(): 52 | os.makedirs(status_dir) 53 | if not Path(program_loc).exists(): 54 | os.makedirs(program_loc) 55 | if not Path(license_dir).exists(): 56 | os.makedirs(license_dir) 57 | 58 | # Import Third Party Libraries 59 | import gi 60 | gi.require_version('Gtk', '3.0') 61 | gi.require_version('GtkSource', '3.0') 62 | gi.require_version('Notify', '0.7') 63 | from gi.repository import Gtk, Gdk, GObject, GLib, GtkSource 64 | #from gi.repository import Notify 65 | # Checking for arguments in command line. We will never have a command line version of netgui (it's called netctl!) 66 | 67 | # argument parser 68 | parser = argparse.ArgumentParser(description='NetGUI; The NetCTL GUI! ' + 69 | 'We need root :)') 70 | parser.add_argument('-v', '--version', 71 | help='show the current version of NetGUI', 72 | action='store_true') 73 | parser.add_argument('-d', '--develop', 74 | help='run in development mode. ' + 75 | 'If you are not a developer, do not use this.', 76 | action='store_true') 77 | parser.add_argument('-n', '--nowifi', 78 | help='run in no-wifi-mode. ' + 79 | 'Does not scan for networks. ' + 80 | 'Uses profiles to connect.', 81 | action='store_true') 82 | args = parser.parse_args() 83 | if args.version: 84 | print('Your NetGUI version is ' + prog_ver + '.') 85 | sys.exit(0) 86 | 87 | if args.develop: 88 | print('Running in development mode. ' + 89 | 'All files are set to be in the development folder.') 90 | program_loc = './' 91 | img_loc = "./imgs" 92 | 93 | if args.nowifi: 94 | print('Running in No Wifi mode!') 95 | 96 | # Import Project Libraries 97 | from Library.profile_editor import NetGUIProfileEditor 98 | from Library.interface_control import InterfaceControl 99 | from Library.netctl_functions import NetCTL 100 | from Library.notifications import Notification 101 | from Library.scanning import ScanRoutines 102 | from Library.generate_config import GenConfig 103 | from Library.preferences import Preferences 104 | 105 | # If our directory for netgui does not exist, create it. 106 | if Path(status_dir).exists(): 107 | pass 108 | else: 109 | os.mkdir(status_dir) 110 | 111 | # Let's also not allow any more than one instance of netgui. 112 | fp = open(pid_file, 'w') 113 | try: 114 | fcntl.lockf(fp, fcntl.LOCK_EX | fcntl.LOCK_NB) 115 | except IOError: 116 | print("We only allow one instance of netgui to be running at a time for precautionary reasons.") 117 | sys.exit(1) 118 | 119 | fp.write(str(pid_number)+"\n") 120 | fp.flush() 121 | 122 | 123 | class NetGUI(Gtk.Window): 124 | def __init__(self): 125 | # Init Vars 126 | self.scanning = False 127 | self.APindex = 0 128 | self.p = None 129 | self.builder = Gtk.Builder() 130 | GObject.type_register(GtkSource.View) 131 | self.builder.add_from_file(str(Path(str(program_loc), "UI.glade"))) 132 | self.dialog = self.builder.get_object("passwordDialog") 133 | self.ap_list = self.builder.get_object("treeview1") 134 | self.ap_store = Gtk.ListStore(str, str, str, str) 135 | self.interface_name = "" 136 | self.NoWifiMode = 0 137 | self.interface_control = InterfaceControl() 138 | self.generate_config = GenConfig(profile_dir) 139 | self.init_ui() 140 | 141 | def init_ui(self): 142 | is_connected() 143 | # Create a "Builder", which basically allows me to import the Glade file for a complete interface. 144 | # I love Glade, btw. So much quicker than manually coding everything. 145 | 146 | # Grab the "window1" attribute from UI.glade, and set it to show everything. 147 | window = self.builder.get_object("mainWindow") 148 | window.connect("delete-event", Gtk.main_quit) 149 | 150 | # Get the OnScan button in case we are going to run in NoWifiMode 151 | scan_button = self.builder.get_object("scanAPsTool") 152 | 153 | # Setup the main area of netgui: The network list. 154 | 155 | self.ap_list.set_model(self.ap_store) 156 | 157 | # Set Up Columns 158 | # renderer1 = The Cell renderer. Basically allows for text to show. 159 | # column1 = The actual setup of the column. Arguments = title, CellRenderer, textIndex) 160 | # Actually append the column to the treeview. 161 | ssid_cell_renderer = Gtk.CellRendererText() 162 | ssid_column = Gtk.TreeViewColumn("SSID", ssid_cell_renderer, text=0) 163 | ssid_column.set_resizable(True) 164 | ssid_column.set_expand(True) 165 | self.ap_list.append_column(ssid_column) 166 | 167 | connect_quality_cell_renderer = Gtk.CellRendererText() 168 | connect_quality_column = Gtk.TreeViewColumn("Connection Quality", connect_quality_cell_renderer, text=1) 169 | connect_quality_column.set_resizable(True) 170 | connect_quality_column.set_expand(True) 171 | self.ap_list.append_column(connect_quality_column) 172 | 173 | security_type_cell_renderer = Gtk.CellRendererText() 174 | security_type_column = Gtk.TreeViewColumn("Security Type", security_type_cell_renderer, text=2) 175 | security_type_column.set_resizable(True) 176 | security_type_column.set_expand(True) 177 | self.ap_list.append_column(security_type_column) 178 | 179 | connected_cell_renderer = Gtk.CellRendererText() 180 | connected_column = Gtk.TreeViewColumn("Connected?", connected_cell_renderer, text=3) 181 | connected_column.set_resizable(True) 182 | connected_column.set_expand(True) 183 | self.ap_list.append_column(connected_column) 184 | 185 | # Set TreeView as Re-orderable 186 | self.ap_list.set_reorderable(True) 187 | 188 | # Set all the handlers I defined in glade to local functions. 189 | handlers = { 190 | "onSwitch": self.on_switch, 191 | "onExit": self.on_btn_exit, 192 | "onAboutClicked": self.about_clicked, 193 | "onScan": self.start_scan, 194 | "onConnect": self.profile_exists, 195 | "onDConnect": self.disconnect_clicked, 196 | "onPrefClicked": self.preferences_clicked, 197 | "onHelpClicked": self.on_help_clicked, 198 | "onIssueReport": self.report_issue, 199 | "onDAll": self.disconnect_all, 200 | "onEditorActivate": self.open_editor 201 | } 202 | # Connect all the above handlers to actually call the functions. 203 | self.builder.connect_signals(handlers) 204 | 205 | # Hardcode (relative) image paths 206 | APScanToolImg = self.builder.get_object("image1") 207 | APScanToolImg.set_from_file(str(Path(img_loc, "APScan.png"))) 208 | 209 | ConnectToolImg = self.builder.get_object("image2") 210 | ConnectToolImg.set_from_file(str(Path(img_loc, "connect.png"))) 211 | 212 | dConnectToolImg = self.builder.get_object("image3") 213 | dConnectToolImg.set_from_file(str(Path(img_loc, "disconnect.png"))) 214 | 215 | prefToolImg = self.builder.get_object("image5") 216 | prefToolImg.set_from_file(str(Path(img_loc, "preferences.png"))) 217 | 218 | exitToolImg = self.builder.get_object("image4") 219 | exitToolImg.set_from_file(str(Path(img_loc, "exit.png"))) 220 | 221 | # Populate profiles menu 222 | profile_menu = self.builder.get_object("profilesMenu") 223 | profile_menu.set_submenu(Gtk.Menu()) 224 | profiles = os.listdir("/etc/netctl/") 225 | # Iterate through profiles directory, and add to "Profiles" Menu # 226 | for i in profiles: 227 | if Path("/etc/netctl/" + i).is_file(): 228 | profile_menu.get_submenu().append(Gtk.MenuItem(label=i)) 229 | #This should automatically detect their wireless device name. I'm not 100% sure 230 | #if it works on every computer, but we can only know from multiple tests. If 231 | #it doesn't work, I will re-implement the old way. 232 | self.interface_name = get_interface() 233 | if self.interface_name == "": 234 | #n = Notify.Notification.new("Could not detect interface!", "No interface was detected. Now running in " + 235 | # "No-Wifi Mode. Scan Button is disabled.", "dialog-information") 236 | #n.show() 237 | self.no_wifi_scan() 238 | self.NoWifiMode = 1 239 | scan_button.props.sensitive = False 240 | print(str(self.NoWifiMode)) 241 | elif args.nowifi: 242 | self.no_wifi_scan() 243 | self.NoWifiMode = 1 244 | scan_button.props.sensitive = False 245 | else: 246 | #self.startScan(None) 247 | self.NoWifiMode = 0 248 | 249 | # Start initial scan 250 | #Notify.init("NetGUI") 251 | window.show_all() 252 | 253 | def open_editor(self, e): 254 | select = self.ap_list.get_selection() 255 | network_ssid = self.get_ssid(select) 256 | if network_ssid is None: 257 | profile_edit_window = NetGUIProfileEditor(None) 258 | profile_edit_window.show() 259 | else: 260 | profile = str(Path("/", "etc", "netctl", "netgui_" + network_ssid)) 261 | profile_edit_window = NetGUIProfileEditor(profile) 262 | profile_edit_window.show() 263 | 264 | def no_wifi_scan(self): 265 | aps = {} 266 | profiles = os.listdir(Path("/", "etc", "netctl")) 267 | i = 0 268 | self.NoWifiMode = 1 269 | global args 270 | args.nowifi = True 271 | for profile in profiles: 272 | if Path("/", "etc", "netctl" + profile).is_file(): 273 | aps["row" + str(i)] = self.ap_store.append([profile, "", "", ""]) 274 | self.ap_store.set(aps["row" + str(i)], 1, "N/A in No-Wifi mode.") 275 | self.ap_store.set(aps["row" + str(i)], 2, "N/A.") 276 | if is_connected() is False: 277 | self.ap_store.set(aps["row" + str(i)], 3, "No") 278 | else: 279 | connected_network = check_output(self, "netctl list | sed -n 's/^\* //p'").strip() 280 | if profile in connected_network: 281 | self.ap_store.set(aps["row" + str(i)], 3, "Yes") 282 | else: 283 | self.ap_store.set(aps["row" + str(i)], 3, "No") 284 | i += 1 285 | 286 | def start_scan(self, e): 287 | run_scan = ScanRoutines(self.interface_name, scan_file, status_dir) 288 | run_scan.scan() 289 | self.check_scan() 290 | 291 | def check_scan(self): 292 | sf = open(scan_file, 'r') 293 | real_dir = sf.readline() 294 | real_dir = real_dir.strip() 295 | sf.close() 296 | print(real_dir) 297 | shutil.move(real_dir, Path(status_dir, "final_results.log")) 298 | 299 | with open(Path(status_dir, "final_results.log")) as tsv: 300 | self.ap_store.clear() 301 | 302 | r = csv.reader(tsv, dialect='excel-tab') 303 | aps = {} 304 | i = 0 305 | for row in r: 306 | network = row[2] 307 | print(ascii(network)) 308 | if r"\x00" in network: 309 | continue 310 | elif network is "": 311 | continue 312 | else: 313 | aps["row" + str(i)] = self.ap_store.append([network, "", "", ""]) 314 | quality = row[0] 315 | if int(quality) <= -100: 316 | percent = "0%" 317 | elif int(quality) >= -50: 318 | percent = "100%" 319 | else: 320 | final_quality = (2 * (int(quality) + 100)) 321 | percent = str(final_quality) + "%" 322 | if network == "": 323 | pass 324 | else: 325 | self.ap_store.set(aps["row" + str(i)], 1, percent) 326 | 327 | security = row[1] 328 | if "WPA" in security: 329 | encryption = "WPA" 330 | elif "OPENSSID" in security: 331 | encryption = "Open" 332 | elif "WPS" in security: 333 | encryption = "WPS" 334 | elif "WEP" in security: 335 | encryption = "WEP" 336 | else: 337 | encryption = "Open" 338 | if network == "": 339 | pass 340 | else: 341 | self.ap_store.set(aps["row" + str(i)], 2, encryption) 342 | 343 | if is_connected() is False: 344 | if network == "": 345 | pass 346 | else: 347 | if network == "": 348 | pass 349 | else: 350 | self.ap_store.set(aps["row" + str(i)], 3, "No") 351 | else: 352 | connected_network = check_output(self, "netctl list | sed -n 's/^\* //p'").strip() 353 | if network in connected_network: 354 | if network == "": 355 | pass 356 | else: 357 | self.ap_store.set(aps["row" + str(i)], 3, "Yes") 358 | else: 359 | if network == "": 360 | pass 361 | else: 362 | self.ap_store.set(aps["row" + str(i)], 3, "No") 363 | 364 | i += 1 365 | 366 | def on_switch(self, e): 367 | self.ap_store.clear() 368 | self.no_wifi_scan() 369 | self.NoWifiMode = 1 370 | global args 371 | args.nowifi = True 372 | 373 | def on_btn_exit(self, e): 374 | if self.p is None: 375 | pass 376 | else: 377 | self.p.terminate() 378 | Gtk.main_quit() 379 | sys.exit() 380 | 381 | def about_clicked(self, e): 382 | # Getting the about dialog from UI.glade 383 | about_dialog = self.builder.get_object("aboutDialog") 384 | # Opening the about dialog. 385 | about_dialog.run() 386 | # Hiding the about dialog. Read in "prefDialog" for why we hide, not destroy. 387 | about_dialog.hide() 388 | 389 | def profile_exists(self, e): 390 | skip_no_prof_conn = 0 391 | found_profile = 0 392 | select = self.ap_list.get_selection() # Get selected network 393 | ssid = self.get_ssid(select) # Get SSID of selected network. 394 | for profile in os.listdir(Path("/", "etc", "netctl")): 395 | if Path("/", "etc", "netctl", profile).is_file(): # Is it a file, not dir? 396 | with open(Path("/", "etc", "netctl", profile), 'r') as current_profile: 397 | current_profile_name = profile 398 | for line in current_profile: 399 | if "ESSID" in line.strip(): 400 | essid_name = line[6:] 401 | if str(ssid).lower() in essid_name.lower(): 402 | skip_no_prof_conn = 1 403 | if found_profile is 1: 404 | break 405 | else: 406 | self.connect_clicked(1, current_profile_name) 407 | found_profile = 1 408 | else: 409 | pass 410 | else: 411 | pass 412 | else: 413 | pass 414 | if skip_no_prof_conn is 1: 415 | pass 416 | else: 417 | self.connect_clicked(0, None) 418 | 419 | def connect_clicked(self, does_profile_exist, profile_name): 420 | # process a connection request from the user 421 | if does_profile_exist is 1: 422 | select = self.ap_list.get_selection() 423 | network_ssid = self.get_ssid(select) 424 | #n = Notify.Notification.new("Found existing profile.", 425 | # "NetCTL found an existing profile for this network. Connecting to " + 426 | # network_ssid + " using profile " + profile_name) 427 | #n.show() 428 | net_interface = self.interface_name 429 | self.interface_control.down(net_interface) 430 | NetCTL.stop_all() 431 | NetCTL.start(profile_name) 432 | #n = Notify.Notification.new("Connected to new network!", "You are now connected to " + network_ssid, 433 | # "dialog-information") 434 | #n.show() 435 | 436 | elif does_profile_exist == 0: 437 | if self.NoWifiMode == 0: 438 | select = self.ap_list.get_selection() 439 | network_ssid = self.get_ssid(select) 440 | print("nSSID = " + network_ssid) 441 | profile = "netgui_" + network_ssid 442 | print("profile = " + profile) 443 | net_interface = self.interface_name 444 | if Path(profile_dir, profile).is_file(): 445 | self.interface_control.down(net_interface) 446 | NetCTL.stop_all(self) 447 | NetCTL.start(profile) 448 | # n = Notify.Notification.new("Connected to new network!", "You are now connected to " + 449 | # network_ssid, "dialog-information") 450 | # n.show() 451 | else: 452 | network_security = self.get_security(select) 453 | if network_security == "Open": 454 | key = "none" 455 | else: 456 | key = self.get_network_pw() 457 | self.generate_config.create_wireless_config(network_ssid, self.interface_name, network_security, key) 458 | try: 459 | InterfaceControl.down(self, net_interface) 460 | NetCTL.stop_all(self) 461 | NetCTL.start(self, profile) 462 | # n = Notify.Notification.new("Connected to new network!", "You are now connected to " + 463 | # network_ssid, "dialog-information") 464 | # n.show() 465 | except Exception as e: 466 | pass 467 | # n = Notify.Notification.new("Error!", "There was an error. The error was: " + str(e) + 468 | # ". Please report an issue at the github page if it persists.", 469 | # "dialog-information") 470 | # n.show() 471 | # Notify.uninit() 472 | elif self.NoWifiMode == 1: 473 | select = self.ap_list.get_selection() 474 | nwm_profile = self.get_ssid(select) 475 | net_interface = get_interface() 476 | try: 477 | InterfaceControl.down(self, net_interface) 478 | NetCTL.stop_all(self) 479 | NetCTL.start(self, nwm_profile) 480 | # n = Notify.Notification.new("Connected to new profile!", "You are now connected to " + nwm_profile, 481 | # "dialog-information") 482 | # n.show() 483 | except: 484 | pass 485 | # n = Notify.Notification.new("Error!", "There was an error. Please report an issue at the " 486 | # + "github page if it persists.", "dialog-information") 487 | # n.show() 488 | # Notify.uninit() 489 | self.start_scan(self) 490 | 491 | def get_network_pw(self): 492 | ret = self.dialog.run() 493 | self.dialog.hide() 494 | entry = self.builder.get_object("userEntry") 495 | if ret == 1: 496 | password = entry.get_text() 497 | return password 498 | 499 | 500 | def get_ssid(self, selection): 501 | model, treeiter = selection.get_selected() 502 | if treeiter is not None: 503 | return model[treeiter][0] 504 | 505 | @staticmethod 506 | def get_security(selection): 507 | model, treeiter = selection.get_selected() 508 | if treeiter is not None: 509 | security_type = model[treeiter][2] 510 | security_type = security_type.lower() 511 | return security_type 512 | 513 | def disconnect_clicked(self, e): 514 | select = self.ap_list.get_selection() 515 | network_ssid = self.get_ssid(select) 516 | profile = "netgui_" + network_ssid 517 | interface_name = get_interface() 518 | NetCTL.stop(self, profile) 519 | InterfaceControl.down(self, interface_name) 520 | self.start_scan(None) 521 | # n = Notify.Notification.new("Disconnected from network!", "You are now disconnected from " + network_ssid, 522 | # "dialog-information") 523 | # n.show() 524 | 525 | def disconnect_all(self, e): 526 | interface_name = get_interface() 527 | NetCTL.stop_all(None) 528 | InterfaceControl.down(self, interface_name) 529 | self.start_scan(None) 530 | # n = Notify.Notification.new("Disconnected from all networks!", "You are now disconnected from all networks.", 531 | # "dialog-information") 532 | # n.show() 533 | 534 | def preferences_clicked(self, e): 535 | Preferences(program_loc) 536 | 537 | #TODO: Write help file! 538 | def on_help_clicked(self, e): 539 | pass 540 | 541 | def report_issue(self, e): 542 | pass 543 | 544 | 545 | def is_connected(): 546 | # If we are connected to a network, it lists it. Otherwise, it returns nothing (or an empty byte). 547 | check = subprocess.check_output("netctl list | sed -n 's/^\* //p'", shell=True) 548 | if check == b'': 549 | return False 550 | else: 551 | return True 552 | 553 | 554 | def check_output(self, command): 555 | # Run a command, return what it's output was, and convert it from bytes to unicode 556 | p = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True) 557 | output = p.communicate()[0] 558 | output = output.decode("utf-8") 559 | return output 560 | 561 | 562 | def get_interface(): 563 | interface_name = "" 564 | if not Path(interface_conf_file).is_file(): 565 | 566 | devices = os.listdir("/sys/class/net") 567 | for device in devices: 568 | if "wl" in device: 569 | interface_name = device 570 | else: 571 | pass 572 | if interface_name == "": 573 | int_name_check = str(subprocess.check_output("cat /proc/net/wireless", shell=True)) 574 | interface_name = int_name_check[166:172] 575 | if interface_name == "": 576 | #interfaceName = Need the code here 577 | pass 578 | f = open(interface_conf_file, 'w') 579 | f.write(interface_name) 580 | f.close() 581 | return str(interface_name).strip() 582 | else: 583 | f = open(interface_conf_file, 'r') 584 | interface_name = f.readline() 585 | f.close() 586 | return str(interface_name).strip() 587 | 588 | 589 | def cleanup(): 590 | # Clean up time 591 | fcntl.lockf(fp, fcntl.LOCK_UN) 592 | fp.close() 593 | os.unlink(pid_file) 594 | try: 595 | os.unlink(scan_file) 596 | os.unlink(pref_file) 597 | os.unlink(interface_conf_file) 598 | except: 599 | pass 600 | 601 | 602 | if __name__ == "__main__": 603 | cleanup() 604 | Gdk.threads_init() 605 | Gdk.threads_enter() 606 | NetGUI() 607 | Gdk.threads_leave() 608 | Gtk.main() 609 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python3 2 | 3 | # Import Standard Libraries 4 | import argparse 5 | import csv 6 | import fcntl 7 | import json 8 | import os 9 | from pathlib import Path 10 | import shutil 11 | import subprocess 12 | import sys 13 | import threading 14 | import time 15 | from queue import Queue, Empty 16 | 17 | # Import third party libraries 18 | import gi 19 | gi.require_version('Gtk', '3.0') 20 | gi.require_version('GtkSource', '3.0') 21 | gi.require_version('Notify', '0.7') 22 | from gi.repository import Gtk, GObject, GLib, GtkSource 23 | 24 | # Importing project libraries 25 | from Library.profile_editor import NetGUIProfileEditor 26 | from Library.interface_control import InterfaceControl 27 | from Library.netctl_functions import NetCTL 28 | from Library.notifications import Notification 29 | from Library.scanning import ScanRoutines 30 | from Library.generate_config import GenConfig 31 | from Library.preferences import Preferences 32 | 33 | # Base App Info 34 | program_version = "0.85" 35 | profile_dir = Path("/", "etc", "netctl") 36 | status_dir = Path("/", "var", "lib", "netgui") 37 | program_loc = Path("/", "usr", "share", "netgui") 38 | interface_conf_file = Path(status_dir, "interface.cfg") 39 | license_dir = Path("/", "usr", "share", "licenses", "netgui") 40 | scan_file = Path(status_dir, "scan_results.log") 41 | pid_file = Path(status_dir, "program.pid") 42 | img_loc = Path(program_loc, "imgs") 43 | pref_file = Path(status_dir, "preferences.json") 44 | pid_number = os.getpid() 45 | arg_no_wifi = 0 46 | 47 | # Safety First! Do we have our directories? 48 | if not Path(status_dir).exists(): 49 | os.makedirs(status_dir) 50 | if not Path(program_loc).exists(): 51 | os.makedirs(program_loc) 52 | if not Path(license_dir).exists(): 53 | os.makedirs(license_dir) 54 | 55 | # Parse a variety of arguments 56 | parser = argparse.ArgumentParser(description='NetGUI; The NetCTL GUI! ' + 57 | 'We need root :)') 58 | parser.add_argument('-v', '--version', 59 | help='show the current version of NetGUI', 60 | action='store_true') 61 | parser.add_argument('-d', '--develop', 62 | help='run in development mode. ' + 63 | 'If you are not a developer, do not use this.', 64 | action='store_true') 65 | parser.add_argument('-n', '--nowifi', 66 | help='run in no-wifi-mode. ' + 67 | 'Does not scan for networks. ' + 68 | 'Uses profiles to connect.', 69 | action='store_true') 70 | args = parser.parse_args() 71 | if args.version: 72 | print('Your NetGUI version is ' + program_version + '.') 73 | sys.exit(0) 74 | 75 | if args.develop: 76 | print('Running in development mode. ' + 77 | 'All files are set to be in the development folder.') 78 | program_loc = './' 79 | img_loc = "./imgs" 80 | 81 | if args.nowifi: 82 | print('Running in No Wifi mode!') 83 | 84 | # We only can allow one instance of netgui for safety. 85 | with open(pid_file, 'w') as fp: 86 | try: 87 | fcntl.lockf(fp, fcntl.LOCK_EX | fcntl.LOCK_NB) 88 | except IOError: 89 | print("We only allow one instance of netgui to be running at a time for precautionary reasons.") 90 | sys.exit(1) 91 | 92 | fp.write(str(pid_number)+"\n") 93 | fp.flush 94 | 95 | class NetGUI(Gtk.Window): 96 | def __init__(self): 97 | self.scanning = False 98 | self.thread = None 99 | self.APindex = 0 100 | self.builder = Gtk.Builder() 101 | GObject.type_register(GtkSource.View) 102 | self.builder.add_from_file(str(Path(program_loc, "UI.glade"))) 103 | self.password_dialog = self.builder.get_object("passwordDialog") 104 | self.ap_list = self.builder.get_object("treeview1") 105 | self.ap_store = Gtk.ListStore(str, str, str, str) 106 | self.statusbar = self.builder.get_object("statusbar1") 107 | self.context = self.statusbar.get_context_id("netgui") 108 | self.interface_name = "" 109 | self.NoWifiMode = False 110 | self.interface_control = InterfaceControl() 111 | self.next_function = None 112 | self.generate_config = GenConfig(profile_dir) 113 | 114 | self.notifications = Notification() 115 | 116 | self.init_ui() 117 | 118 | def init_ui(self): 119 | is_connected() # Are we connected to a network? 120 | 121 | # Grab the "mainWindow" attribute from UI.glade, and set it to show everything. 122 | window = self.builder.get_object("mainWindow") 123 | window.connect("delete-event", Gtk.main_quit) 124 | 125 | # Get the buttons in case we need to disable them 126 | self.scan_button = self.builder.get_object("scanAPsTool") 127 | self.connect_button = self.builder.get_object("connectTool") 128 | self.disconnect_btn = self.builder.get_object("dConnectTool") 129 | self.preferences_btn = self.builder.get_object("prefToolBtn") 130 | self.exit_btn = self.builder.get_object("exitTool") 131 | 132 | # Setup the main area of netgui: The network list. 133 | 134 | self.ap_list.set_model(self.ap_store) 135 | 136 | # Set Up Columns 137 | # renderer1 = The Cell renderer. Basically allows for text to show. 138 | # column1 = The actual setup of the column. Arguments = title, CellRenderer, textIndex) 139 | # Actually append the column to the treeview. 140 | ssid_cell_renderer = Gtk.CellRendererText() 141 | ssid_column = Gtk.TreeViewColumn("SSID", ssid_cell_renderer, text=0) 142 | ssid_column.set_resizable(True) 143 | ssid_column.set_expand(True) 144 | self.ap_list.append_column(ssid_column) 145 | 146 | connect_quality_cell_renderer = Gtk.CellRendererText() 147 | connect_quality_column = Gtk.TreeViewColumn("Connection Quality", connect_quality_cell_renderer, text=1) 148 | connect_quality_column.set_resizable(True) 149 | connect_quality_column.set_expand(True) 150 | self.ap_list.append_column(connect_quality_column) 151 | 152 | security_type_cell_renderer = Gtk.CellRendererText() 153 | security_type_column = Gtk.TreeViewColumn("Security Type", security_type_cell_renderer, text=2) 154 | security_type_column.set_resizable(True) 155 | security_type_column.set_expand(True) 156 | self.ap_list.append_column(security_type_column) 157 | 158 | connected_cell_renderer = Gtk.CellRendererText() 159 | connected_column = Gtk.TreeViewColumn("Connected?", connected_cell_renderer, text=3) 160 | connected_column.set_resizable(True) 161 | connected_column.set_expand(True) 162 | self.ap_list.append_column(connected_column) 163 | 164 | # Set TreeView as Re-orderable 165 | self.ap_list.set_reorderable(True) 166 | 167 | # Set all the handlers I defined in glade to local functions. 168 | handlers = { 169 | "onSwitch": self.on_switch, 170 | "onExit": self.on_exit, 171 | "onAboutClicked": self.about_clicked, 172 | "onScan": self.start_scan, 173 | "onConnect": self.connect_clicked, 174 | "onDConnect": self.disconnect_clicked, 175 | "onPrefClicked": self.preferences_clicked, 176 | "onHelpClicked": self.help_clicked, 177 | "onIssueReport": self.report_issue, 178 | "onDAll": self.disconnect_all_clicked, 179 | "onEditorActivate": self.open_profile_editor 180 | } 181 | # Connect all the above handlers to actually call the functions. 182 | self.builder.connect_signals(handlers) 183 | 184 | # Hardcode (relative) image paths 185 | APScanToolImg = self.builder.get_object("image1") 186 | APScanToolImg.set_from_file(str(Path(img_loc, "APScan.png"))) 187 | 188 | ConnectToolImg = self.builder.get_object("image2") 189 | ConnectToolImg.set_from_file(str(Path(img_loc, "connect.png"))) 190 | 191 | dConnectToolImg = self.builder.get_object("image3") 192 | dConnectToolImg.set_from_file(str(Path(img_loc, "disconnect.png"))) 193 | 194 | prefToolImg = self.builder.get_object("image5") 195 | prefToolImg.set_from_file(str(Path(img_loc, "preferences.png"))) 196 | 197 | exitToolImg = self.builder.get_object("image4") 198 | exitToolImg.set_from_file(str(Path(img_loc, "exit.png"))) 199 | 200 | # Populate profiles menu 201 | profile_menu = self.builder.get_object("profilesMenu") 202 | profile_menu.set_submenu(Gtk.Menu()) 203 | profiles = os.listdir("/etc/netctl/") 204 | # Iterate through profiles directory, and add to "Profiles" Menu # 205 | for i in profiles: 206 | if Path("/etc/netctl/" + i).is_file(): 207 | profile_menu.get_submenu().append(Gtk.MenuItem(label=i)) 208 | #This should automatically detect their wireless device name. I'm not 100% sure 209 | #if it works on every computer, but we can only know from multiple tests. If 210 | #it doesn't work, I will re-implement the old way. 211 | self.interface_name = get_interface() 212 | global args 213 | if self.interface_name == "" or args.nowifi: 214 | self.notifications.show_notification("Could not detect interface!", "No interface was detected. Now running in " + 215 | "No-Wifi Mode. Scan Button is disabled.") 216 | self.no_wifi_scan_mode() 217 | self.NoWifiMode = True 218 | self.scan_button.props.sensitive = False 219 | else: 220 | self.NoWifiMode = False 221 | 222 | # Start initial scan 223 | self.start_scan(None) 224 | window.show_all() 225 | 226 | def open_profile_editor(self, e): 227 | select = self.ap_list.get_selection() 228 | network_ssid = self.get_ssid(select) 229 | if network_ssid is None: 230 | profile_edit_window = NetGUIProfileEditor(None) 231 | profile_edit_window.show() 232 | else: 233 | profile = str(Path("/", "etc", "netctl", self.get_profile())) 234 | profile_edit_window = NetGUIProfileEditor(profile) 235 | profile_edit_window.show() 236 | 237 | def no_wifi_scan_mode(self): 238 | aps = {} 239 | profiles = os.listdir(profile_dir) 240 | i = 0 241 | self.NoWifiMode = 1 242 | global args 243 | args.nowifi = True 244 | for profile in profiles: 245 | if Path("/", "etc", "netctl", profile).is_file(): 246 | aps["row" + str(i)] = self.ap_store.append([profile, "", "", ""]) 247 | self.ap_store.set(aps["row" + str(i)], 1, "N/A in No-Wifi mode.") 248 | self.ap_store.set(aps["row" + str(i)], 2, "N/A") 249 | if is_connected is False: 250 | self.ap_store.set(aps["row" + str(i)], 3, "No") 251 | else: 252 | connected_network = check_output(self, "netctl list | sed -n 's/^\* //p'").strip() 253 | if profile in connected_network: 254 | self.ap_store.set(aps["row" + str(i)], 3, "Yes") 255 | else: 256 | self.ap_store.set(aps["row" + str(i)], 3, "No") 257 | i += 1 258 | 259 | def start_scan(self, e): 260 | self.notifications.show_notification("Starting scan", "Scan is now beginning. Please wait for completion.") 261 | is_scan_done_queue = Queue() 262 | run_scan = ScanRoutines(self.interface_name, scan_file, status_dir, is_scan_done_queue) 263 | scan_thread = threading.Thread(target=run_scan.scan) 264 | scan_thread.daemon = True 265 | self.disable_buttons() 266 | self.statusbar.push(self.context, "Scanning...") 267 | scan_thread.start() 268 | self.is_thread_done(is_scan_done_queue, scan_thread) 269 | 270 | def is_thread_done(self, completion_queue, thread_to_join): 271 | try: 272 | completion_queue.get(False) 273 | thread_to_join.join() 274 | self.begin_check_scan() 275 | self.statusbar.push(self.context, "Scanning Complete.") 276 | self.enable_buttons() 277 | except Empty: 278 | timer = threading.Timer(0.5, self.is_thread_done, args=[completion_queue, thread_to_join, reason]) 279 | timer.start() 280 | 281 | def begin_check_scan(self): 282 | check_scan_thread = threading.Thread(target=self.check_scan) 283 | check_scan_thread.daemon = True 284 | check_scan_thread.start() 285 | 286 | def check_scan(self): 287 | try: 288 | with open(scan_file, 'r') as temp_file: 289 | real_dir = temp_file.readline() 290 | real_dir = real_dir.strip() 291 | try: 292 | shutil.move(real_dir, Path(status_dir, "final_results.log")) 293 | try: 294 | with open(Path(status_dir, "final_results.log")) as results_of_scan: 295 | self.ap_store.clear() 296 | 297 | reader = csv.reader(results_of_scan, dialect='excel-tab') 298 | aps = {} 299 | i = 0 300 | for row in reader: 301 | # Get network from scan, and filter out blank networks 302 | # and \x00 networks. 303 | network = row[2] 304 | if r"\x00" in network: 305 | continue 306 | elif network is "": 307 | continue 308 | else: 309 | aps["row" + str(i)] = self.ap_store.append([network, "", "", ""]) 310 | 311 | # Get quality from scan 312 | quality = int(row[0]) 313 | if quality <= -100: 314 | percent = "0%" 315 | elif quality >= -50: 316 | percent = "100%" 317 | else: 318 | final_quality = (2 * (quality + 100)) 319 | percent = str(final_quality) + "%" 320 | if network == "": 321 | pass 322 | else: 323 | self.ap_store.set(aps["row" + str(i)], 1, percent) 324 | 325 | # Get Security 326 | security = row[1] 327 | if "WPA" in security: 328 | encryption = "WPA" 329 | elif "OPENSSID" in security: 330 | encryption = "Open" 331 | elif "WPS" in security: 332 | encryption = "WPS" 333 | elif "WEP" in security: 334 | encryption = "WEP" 335 | else: 336 | encryption = "Open" 337 | 338 | if network == "": 339 | pass 340 | else: 341 | self.ap_store.set(aps["row" + str(i)], 2, encryption) 342 | 343 | if is_connected is False: 344 | if network != "": 345 | self.ap_store.set(aps["row" + str(i)], 3, "No") 346 | else: 347 | connected_network = check_output(self, "netctl list | sed -n 's/^\* //p'").strip() 348 | if network != "": 349 | if network in connected_network: 350 | self.ap_store.set(aps["row" + str(i)], 3, "Yes") 351 | else: 352 | self.ap_store.set(aps["row" + str(i)], 3, "No") 353 | i += 1 354 | except FileNotFoundError: 355 | self.notifications.show_notification("Error checking scan!", "Perhaps there were no networks nearby!") 356 | self.statusbar.push(self.context, "Error checking results. Perhaps there are no networks nearby.") 357 | except FileNotFoundError: 358 | self.notifications.show_notification("Error checking scan!", "Perhaps there were no networks nearby!") 359 | self.statusbar.push(self.context, "Error checking results. Perhaps there are no networks nearby.") 360 | except FileNotFoundError: 361 | self.notifications.show_notification("Error checking scan!", "Perhaps there were no networks nearby!") 362 | self.statusbar.push(self.context, "Error checking results. Perhaps there are no networks nearby.") 363 | 364 | def on_switch(self, e): 365 | if self.NoWifiMode == False: 366 | self.notifications.show_notification("Switching to No-Wifi mode.", "Switching to no-wifi mode.") 367 | self.ap_store.clear() 368 | self.no_wifi_scan_mode() 369 | self.NoWifiMode = True 370 | else: 371 | self.notifications.show_notification("Switching to wifi mode.", "Switching to wifi mode.") 372 | self.ap_store.clear() 373 | self.NoWifiMode = False 374 | 375 | def on_exit(self, e, d=None): 376 | if self.thread is None: 377 | pass 378 | else: 379 | self.thread.join() 380 | Gtk.main_quit() 381 | sys.exit(0) 382 | 383 | def about_clicked(self, e): 384 | about_dialog = self.builder.get_object("aboutDialog") 385 | about_dialog.run() 386 | about_dialog.hide() 387 | 388 | def get_profile(self): 389 | select = self.ap_list.get_selection() 390 | ssid = self.get_ssid(select) 391 | for profile in os.listdir(Path("/", "etc", "netctl")): 392 | if Path("/", "etc", "netctl", profile).is_file(): 393 | with open(Path("/", "etc", "netctl", profile), 'r') as current_profile: 394 | for line in current_profile: 395 | if "ESSID" in line.strip(): 396 | essid_name = line[6:] 397 | if str(ssid).lower() in essid_name.lower(): 398 | return profile 399 | return None 400 | 401 | def connect_clicked(self, e): 402 | self.disable_buttons() 403 | profile_name = self.get_profile() 404 | if profile_name is not None: 405 | self.notifications.show_notification("Profile found!", "Found profile {} for this network. Connecting " + 406 | "using pre-existing profile!".format(profile_name)) 407 | select = self.ap_list.get_selection() 408 | network_ssid = self.get_ssid(select) 409 | self.statusbar.push(self.context, "Connecting to {}".format(profile_name)) 410 | 411 | InterfaceControl.down(self.interface_name) 412 | NetCTL.stop_all() 413 | NetCTL.start(profile_name) 414 | self.statusbar.push(self.context, "Connected to {}".format(profile_name)) 415 | else: 416 | if self.NoWifiMode == False: 417 | self.notifications.show_notification("No profile found.", "There is no profile for this network. Creating one now!") 418 | select = self.ap_list.get_selection() 419 | network_ssid = self.get_ssid(select) 420 | profile = "netgui_" + network_ssid 421 | if Path(profile_dir, profile).is_file(): 422 | InterfaceControl.down(self.interface_name) 423 | NetCTL.stop_all() 424 | NetCTL.start(profile_name) 425 | else: 426 | network_security = self.get_security(select) 427 | if network_security == "Open": 428 | key = "none" 429 | else: 430 | key = self.get_network_password() 431 | self.generate_config.create_wireless_config(network_ssid, self.interface_name, network_security, key) 432 | try: 433 | InterfaceControl.down(self.interface_name) 434 | NetCTL.stop_all() 435 | NetCTL.start(profile_name) 436 | except Exception as e: 437 | pass 438 | elif self.NoWifiMode == 1: 439 | self.notifications.show_notification("We are in no-wifi mode.", "We are in no-wifi mode, connecting to existing profile.") 440 | select = self.ap_list.get_selection() 441 | nwm_profile = self.get_profile() 442 | try: 443 | InterfaceControl.down(self.interface_name) 444 | NetCTL.stop_all() 445 | NetCTL.start(nwm_profile) 446 | except: 447 | pass 448 | self.start_scan(None) 449 | GObject.timeout_add_seconds(5, self.enable_buttons) 450 | 451 | def disable_buttons(self): 452 | self.scan_button.set_sensitive(False) 453 | self.connect_button.set_sensitive(False) 454 | self.disconnect_btn.set_sensitive(False) 455 | self.exit_btn.set_sensitive(False) 456 | self.preferences_btn.set_sensitive(False) 457 | 458 | def enable_buttons(self): 459 | self.scan_button.set_sensitive(True) 460 | self.connect_button.set_sensitive(True) 461 | self.disconnect_btn.set_sensitive(True) 462 | self.exit_btn.set_sensitive(True) 463 | self.preferences_btn.set_sensitive(True) 464 | 465 | def get_network_password(self): 466 | ret = self.password_dialog.run() 467 | self.password_dialog.hide() 468 | entry = self.builder.get_object("userEntry") 469 | if ret == 1: 470 | password = entry.get_text() 471 | return password 472 | 473 | def get_ssid(self, selection): 474 | model, treeiter = selection.get_selected() 475 | if treeiter is not None: 476 | return model[treeiter][0] 477 | 478 | def get_security(self, selection): 479 | model, treeiter = selection.get_selected() 480 | if treeiter is not None: 481 | security_type = model[treeiter][2] 482 | security_type = security_type.lower() 483 | return security_type 484 | 485 | def disconnect_clicked(self, e): 486 | select = self.ap_list.get_selection() 487 | profile = self.get_profile() 488 | NetCTL.stop(profile) 489 | self.notifications.show_notification("Stopping profile.", "Stopping profile: {}".format(profile)) 490 | InterfaceControl.down(self.interface_name) 491 | self.start_scan(None) 492 | 493 | def disconnect_all_clicked(self, e): 494 | self.notifications.show_notification("Stopping all profiles.", "Stopping all profiles!") 495 | NetCTL.stop_all() 496 | InterfaceControl.down(self.interface_name) 497 | self.start_scan(None) 498 | 499 | def preferences_clicked(self, e): 500 | Preferences(program_loc) 501 | 502 | def help_clicked(self, e): 503 | self.notifications.show_notification("Not Implemented", "This function is not yet implemented.") 504 | 505 | def report_issue(self, e): 506 | self.notifications.show_notification("Not Implemented", "This function is not yet implemented.") 507 | 508 | def is_connected(): 509 | check = subprocess.check_output("netctl list | sed -n 's/^\* //p'", shell=True) 510 | if check == b'': 511 | return False 512 | else: 513 | return True 514 | 515 | def check_output(self, command): 516 | p = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True) 517 | output = p.communicate()[0] 518 | output = output.decode("utf-8") 519 | return output 520 | 521 | def get_interface(): 522 | interface_name = "" 523 | if not Path(interface_conf_file).is_file(): 524 | 525 | devices = os.listdir("/sys/class/net") 526 | for device in devices: 527 | if "wl" in device: 528 | interface_name = device 529 | else: 530 | pass 531 | if interface_name == "": 532 | int_name_check = str(subprocess.check_output("cat /proc/net/wireless", shell=True)) 533 | interface_name = int_name_check[166:172] 534 | if interface_name == "": 535 | #interfaceName = Need the code here 536 | pass 537 | f = open(interface_conf_file, 'w') 538 | f.write(interface_name) 539 | f.close() 540 | return str(interface_name).strip() 541 | else: 542 | f = open(interface_conf_file, 'r') 543 | interface_name = f.readline() 544 | f.close() 545 | return str(interface_name).strip() 546 | 547 | def cleanup(): 548 | fcntl.lockf(fp, fcntl.LOCK_UN) 549 | fp.close() 550 | os.unlink(pid_file) 551 | try: 552 | os.unlink(scan_file) 553 | os.unlink(pref_file) 554 | os.unlink(interface_conf_file) 555 | except: 556 | pass 557 | 558 | if __name__ == "__main__": 559 | NetGUI() 560 | Gtk.main() 561 | -------------------------------------------------------------------------------- /scripts/.SRCINFO: -------------------------------------------------------------------------------- 1 | pkgbase = netgui 2 | pkgdesc = GUI for netctl, stable but beta. Replaces WiFiz. 3 | pkgver = 0.8 4 | pkgrel = 1 5 | url = https://github.com/codywd/NetGUI 6 | arch = any 7 | license = custom 8 | makedepends = git 9 | depends = python 10 | depends = python-gobject 11 | depends = netctl 12 | depends = gtksourceview3 13 | depends = libnotify 14 | optdepends = notification-daemon: Desktop Notifications 15 | optdepends = notify-osd: Desktop Notifications (others dont work for me personally) 16 | provides = netgui 17 | conflicts = netcfg 18 | conflicts = wicd 19 | replaces = wifiz 20 | replaces = wifiz-git 21 | options = !emptydirs 22 | source = git+https://github.com/codywd/NetGUI.git 23 | md5sums = SKIP 24 | sha256sums = SKIP 25 | 26 | pkgname = netgui 27 | 28 | -------------------------------------------------------------------------------- /scripts/PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: Cody Dostal 2 | 3 | pkgname=netgui 4 | _gitname=NetGUI 5 | pkgver=0.85 6 | pkgrel=1 7 | pkgdesc="GUI for netctl, stable but beta. Replaces WiFiz." 8 | arch=('any') 9 | url="https://github.com/codywd/$_gitname" 10 | license=('custom') 11 | depends=('python' 'python-gobject' 'netctl' 'gtksourceview3' 'libnotify') 12 | optdepends=( 13 | 'notification-daemon: Desktop Notifications' 14 | 'notify-osd: Desktop Notifications (others dont work for me personally)' 15 | ) 16 | makedepends=('git') 17 | provides=('netgui') 18 | conflicts=('netcfg' 'wicd') 19 | replaces=('wifiz' 'wifiz-git') 20 | options=(!emptydirs) 21 | source=("git+https://github.com/codywd/$_gitname.git") 22 | md5sums=('SKIP') 23 | sha256sums=('SKIP') 24 | 25 | pkgver() { 26 | cd $srcdir/netgui 27 | # Use the tag of the last commit 28 | git describe --always --long | sed -E 's/([^-]*-g)/r\1/;s/-/./g' 29 | } 30 | 31 | package() { 32 | cd $srcdir/$_gitname 33 | python2 setup.py install --root="$pkgdir/" --optimize=1 34 | } 35 | -------------------------------------------------------------------------------- /scripts/UpdateVersions.py: -------------------------------------------------------------------------------- 1 | import xml.etree.ElementTree as ET 2 | import fileinput 3 | 4 | progVer = "0.85" 5 | # Automatic Glade Version Setter, thanks to Dane White 6 | # at the Stack Overflow forums! 7 | class ProgramProperties(object): 8 | 9 | def __init__(self, xmlfile): 10 | 11 | # Parse the XML file 12 | self.__xmlfile = xmlfile 13 | self.__xml_tree = ET.parse(xmlfile) 14 | self.__version_element = self.__xml_tree.getroot().find(".//property[@name='version']") 15 | 16 | # Get an in-memory copy of 'version' 17 | self.__version = self.__version_element.text 18 | 19 | @property 20 | def version(self): 21 | return self.__version 22 | 23 | @version.setter 24 | def version(self, vers): 25 | 26 | # Avoid unecessary file I/O 27 | if self.__version != vers: 28 | 29 | # Store in-memory 30 | self.__version = vers 31 | 32 | # Save the version to the file 33 | self.__version_element.text = str(vers) 34 | self.__xml_tree.write(self.__xmlfile) 35 | 36 | class ModifyOtherVers(): 37 | def __init__(self): 38 | ModifyOtherVers.updatePkgBuild() 39 | 40 | def updatePkgBuild(): 41 | PKGBUILD = ("scripts/PKGBUILD") 42 | for line in fileinput.input(PKGBUILD, inplace=True): 43 | if "pkgver=" in line: 44 | print(line.replace(line, "pkgver=" + str(prog.version))) 45 | else: 46 | print(line.strip()) 47 | ModifyOtherVers.updateSetupPy() 48 | 49 | def updateSetupPy(): 50 | SetupPy = ("setup.py") 51 | for line in fileinput.input(SetupPy, inplace=True): 52 | if "version" in line: 53 | print(line.replace(line, " version='" + str(prog.version) + "',")) 54 | else: 55 | print(line.rstrip("\n")) 56 | ModifyOtherVers.updateMainFile() 57 | 58 | def updateMainFile(): 59 | mainFile = ("main.py") 60 | for line in fileinput.input(mainFile, inplace=True): 61 | if "progVer =" in line: 62 | print(line.replace(line, 'progVer = "' + prog.version + '"')) 63 | else: 64 | print(line.rstrip("\n")) 65 | 66 | ModifyOtherVers.UpdateReadme() 67 | 68 | def UpdateReadme(): 69 | README = ("README.md") 70 | for line in fileinput.input(README, inplace=True): 71 | if "# NetGUI v" in line: 72 | print(line.replace(line, "# NetGUI v" + prog.version)) 73 | else: 74 | print(line.rstrip("\n")) 75 | 76 | prog = ProgramProperties('UI.glade') 77 | prog.version = progVer 78 | ModifyOtherVers.updatePkgBuild() 79 | -------------------------------------------------------------------------------- /scripts/netgui: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | # Hello, I launch NetGUI for you! 3 | 4 | # Good Luck, and god speed! 5 | 6 | exec /usr/bin/python -O /usr/share/netgui/main.py $@ 7 | -------------------------------------------------------------------------------- /scripts/netgui.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Encoding=UTF-8 4 | Name=NetGUI 5 | Comment=A Grapical Frontend for NetCTL 6 | Exec=su -c "netgui" 7 | Icon=/usr/share/netgui/imgs/logo.png 8 | Terminal=false 9 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | 3 | from distutils.core import setup 4 | 5 | setup(name='netgui', 6 | version='0.85', 7 | description = "More advanced GUI for NetCTL. Replaces WiFiz.", 8 | author = "Cody Dostal , Gregory Mullen ", 9 | url = "https://github.com/codywd/netgui", 10 | license = "Custom MIT (see NetGUI.license)", 11 | py_modules=['main'], 12 | #'runner' is in the root. 13 | scripts = ['scripts/netgui'], 14 | data_files=[ 15 | ('/usr/share/netgui/', 16 | ['main.py', 17 | 'UI.glade']), 18 | ('/usr/share/licenses/netgui', 19 | ['NetGUI.license']), 20 | ('/usr/share/netgui/imgs', 21 | ['imgs/APScan.png', 22 | 'imgs/connect.png', 23 | 'imgs/exit.png', 24 | 'imgs/newprofile.png', 25 | 'imgs/aboutLogo.png', 26 | 'imgs/disconnect.png', 27 | 'imgs/logo.png', 28 | 'imgs/preferences.png']), 29 | ('/usr/share/netgui/Library', 30 | ['Library/__init__.py', 31 | 'Library/generate_config.py', 32 | 'Library/interface_control.py', 33 | 'Library/netctl_functions.py', 34 | 'Library/notifications.py', 35 | 'Library/preferences.py', 36 | 'Library/profile_editor.py', 37 | 'Library/run_as_root.py', 38 | 'Library/scanning.py']), 39 | ('/usr/share/applications', 40 | ['scripts/netgui.desktop']) 41 | ] 42 | ) 43 | --------------------------------------------------------------------------------