├── .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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------