diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index fcd88dc..16447c5 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,9 +4,13 @@
-
+
+
+
+
+
@@ -29,6 +33,10 @@
+
+
+
+
{
"associatedIndex": 3
}
@@ -596,4 +604,20 @@
+
+
+
+
+ file://$PROJECT_DIR$/wg_func.py
+ 341
+
+
+
+ file://$PROJECT_DIR$/wg_main.py
+ 273
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 125cfa4..d5b9052 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,14 @@
# Wire-Py
+Wire-Py is an easy-to-use Gui for nmcli.
+
+Before the first use of Wire-Py, all previous tunnels with "nmcli connection delete example" should be removed.
+
+Wire-Py can easily be imported, exported, started, popped as well as renamed and deleted.
+
+Wire-Py runs on many distros.
+Tested on Arch Linux, OpenSuse Tumbleweed,
+Fedora, Debian12, Linux Mint 22 Cinnamon
+
+With the desktops:
+Xfce4, Cinnamon, Kde, and Mate, LXDE, LXQT
diff --git a/wg_func.py b/wg_func.py
index 1527833..9f07af3 100755
--- a/wg_func.py
+++ b/wg_func.py
@@ -8,12 +8,12 @@ import zipfile
from datetime import datetime
from pathlib import Path
from subprocess import check_call
-from tkinter import filedialog, ttk
+from tkinter import ttk
import requests
''' 1 = 1. Year, 09 = Month of the Year, 2924 = Day and Year of the Year '''
-version = 'v. 1.10.2024'
+version = 'v. 1.10.2124'
path_to_file = Path('/etc/wire_py/wg_py')
path_to_file2 = Path('/etc/wire_py/settings')
@@ -135,25 +135,6 @@ class GreenLabel:
def rowconfigure(self, param, weight):
pass
-class StartStopBTN:
- """
- Show Start and Stop Button in Label
- """
-
- def __init__(self):
- self.lb_frame_btn_lbox = None
- self.wg_switch = None
- self.btn_stst = None
- self.wg_vpn_start = tk.PhotoImage(file=r'/usr/share/icons/wp-icons/48/wg_vpn-start.png')
- self.wg_vpn_stop = tk.PhotoImage(file=r'/usr/share/icons/wp-icons/48/wg_vpn-stop.png')
-
- def button_stop(self):
- self.btn_stst = ttk.Button(self.lb_frame_btn_lbox, image=self.wg_vpn_stop, command=self.wg_switch, padding=0)
- self.btn_stst.grid(column=0, row=0, padx=5, pady=8)
-
- def button_start(self):
- self.btn_stst = ttk.Button(self.lb_frame_btn_lbox, image=self.wg_vpn_start, command=self.wg_switch, padding=0)
- self.btn_stst.grid(column=0, row=0, padx=5, pady=8)
class ConToDict:
"""
@@ -281,117 +262,6 @@ class ListTunnels:
return tl
-class ImportTunnel:
- """
- Import Class for Wireguard config Files.
- Before importing, it is checked whether PrivateKey and PublicKey are in the file.
- If True then it is checked whether the PreSharedKey is already in the key file
- to avoid an import error so that no double wgconf are imported.
- Thus, tunnels can be renamed without the problems arise. If False then the key is written into the file.
- Furthermore, it is checked whether the name is longer than 12 characters.
- If True then the name is automatically shortened to 12 characters and then imported.
- If in each case false comes out, a corresponding window comes to inform the user that something is wrong.
- """
-
- def __init__(self):
-
- self.select_tunnel = None
- self.wg_switch = None
- self.btn_stst = None
- self.lb_tunnel = None
- self.StrVar = None
- self.a = None
- self.l_box = None
-
- def wg_import_select(self):
-
- try:
- filepath = filedialog.askopenfilename(initialdir=str(_u), title='Select Wireguard config File',
- filetypes=[('WG config files', '*.conf')], )
-
- with open(filepath, 'r') as file:
- read = file.read()
- path_split = filepath.split('/')
- path_split1 = path_split[-1]
- self.a = TunnelActiv.active()
-
- if 'PrivateKey = ' in read and 'PublicKey = 'in read and 'Endpoint =' in read:
- with open(filepath, 'r') as file:
- key = ConToDict.covert_to_dict(file)
- pre_key = key[3]
- if len(pre_key) != 0:
- with open('/etc/wire_py/.keys', 'r') as readfile:
- p_key = readfile.readlines()
- if pre_key in p_key or pre_key + '\n' in p_key:
- """img_w, img_i, w_title, w_txt hand over"""
- iw = r'/usr/share/icons/wp-icons/64/error.png'
- ii = r'/usr/share/icons/wp-icons/48/wg_msg.png'
- wt = 'Import Error'
- msg_t = 'Tunnel already available!\nPlease use another file for import'
- msg_window(iw, ii, wt, msg_t)
-
- else:
-
- with open('/etc/wire_py/.keys', 'a') as keyfile:
- keyfile.write(pre_key + '\r')
- if len(path_split1) > 17:
- p1 = shutil.copy(filepath, Path('/etc/wire_py/'))
- path_split = path_split1[len(path_split1) - 17:]
- os.rename(p1, Path('/etc/wire_py') / str(path_split))
- new_conf = '/etc/wire_py/' + path_split
- if self.a != '':
- check_call(['nmcli', 'connection', 'down', TunnelActiv.active()])
- ShowAddress.label_empty(self)
-
- subprocess.check_output(['nmcli', 'connection', 'import', 'type',
- 'wireguard', 'file', new_conf], text=True)
-
- else:
- shutil.copy(filepath, Path('/etc/wire_py/'))
- if self.a != '':
- check_call(['nmcli', 'connection', 'down', TunnelActiv.active()])
- ShowAddress.label_empty(self)
-
- subprocess.check_output(['nmcli', 'connection', 'import', 'type',
- 'wireguard', 'file', filepath], text=True)
-
- self.StrVar.set('')
- self.a = TunnelActiv.active()
- self.l_box.insert(0, self.a)
- self.l_box.update()
- self.StrVar = tk.StringVar()
- self.StrVar.set(self.a)
- GreenLabel.green_show_label(self)
- StartStopBTN.button_stop(self)
- wg_read = Path('/etc/wire_py') / str(self.a + '.conf')
- with open(wg_read, 'r') as file_for_key:
- data = ConToDict.covert_to_dict(file_for_key)
-
- ''' Address Label '''
- ShowAddress.init_and_report(self, data)
- ShowAddress.show_data(self)
- check_call(['nmcli', 'con', 'mod', self.a, 'connection.autoconnect', 'no'])
- Path.chmod(wg_read, 0o600)
-
- if 'PrivateKey = ' and 'Endpoint = ' not in read:
- """img_w, img_i, w_title, w_txt hand over"""
- iw = r'/usr/share/icons/wp-icons/64/error.png'
- ii = r'/usr/share/icons/wp-icons/48/wg_msg.png'
- wt = 'Import Error'
- msg_t = 'Oh... no valid Wireguard File!\nPlease select a valid Wireguard File'
- msg_window(iw, ii, wt, msg_t)
-
- except EOFError:
- pass
- except TypeError:
- pass
- except FileNotFoundError:
- pass
- except subprocess.CalledProcessError:
-
- print('Tunnel exist!')
-
-
class FileHandle:
"""
This class will display the autostart label which
@@ -527,29 +397,4 @@ class ExportTunnels:
pass
-class MyToolTip(tk.Toplevel):
- TIP_X_OFFSET = 8
- TIP_Y_OFFSET = 8
- AUTO_CLEAR_TIME = 10 # Millisecond. (1/100 sec.)
- def __init__(self, x_pos, y_pos, message=None, auto_clear=False):
- self.x_pos = x_pos
- self.y_pos = y_pos
- self.message = message
- self.auto_clear = auto_clear
-
- tk.Toplevel.__init__(self)
- self.overrideredirect(True)
-
- self.message_label = ttk.Label(self, compound='left', text=self.message, padding=4)
- self.message_label.pack()
-
- self.geometry("+%d+%d" % (self.x_pos + self.TIP_X_OFFSET,
- self.y_pos + self.TIP_X_OFFSET))
-
- if self.auto_clear:
- self.after(self.AUTO_CLEAR_TIME, self.clear_tip)
-
- def clear_tip(self):
- """Remove Tool-Tip"""
- self.destroy()
diff --git a/wg_main.py b/wg_main.py
index 337c69a..cdf1c67 100755
--- a/wg_main.py
+++ b/wg_main.py
@@ -1,14 +1,16 @@
#!/usr/bin/python3
-
+import os
+import shutil
+import subprocess
import tkinter as tk
-from select import select
+from pathlib import Path
from subprocess import check_call
from tkinter import *
-from pathlib import Path
-from tkinter import ttk
-from wg_func import (TunnelActiv, ListTunnels, ImportTunnel, ConToDict, GreenLabel, ShowAddress, FileHandle,
- ExportTunnels, OnOff, msg_window, WirePyUpdate, res, version, path_to_file2,
- path_to_file3, MyToolTip)
+from tkinter import filedialog, ttk
+
+from wg_func import (TunnelActiv, ListTunnels, ConToDict, GreenLabel, ShowAddress, FileHandle,
+ ExportTunnels, OnOff, msg_window, WirePyUpdate, res, _u, version, path_to_file2,
+ path_to_file3)
tcl_path = Path('/usr/share/TK-Themes')
@@ -269,6 +271,117 @@ class StartStopBTN:
self.btn_stst.bind('', start_mouse_leave)
+class ImportTunnel:
+ """
+ Import Class for Wireguard config Files.
+ Before importing, it is checked whether PrivateKey and PublicKey are in the file.
+ If True then it is checked whether the PreSharedKey is already in the key file
+ to avoid an import error so that no double wgconf are imported.
+ Thus, tunnels can be renamed without the problems arise. If False then the key is written into the file.
+ Furthermore, it is checked whether the name is longer than 12 characters.
+ If True then the name is automatically shortened to 12 characters and then imported.
+ If in each case false comes out, a corresponding window comes to inform the user that something is wrong.
+ """
+
+ def __init__(self):
+
+ self.select_tunnel = None
+ self.wg_switch = None
+ self.btn_stst = None
+ self.lb_tunnel = None
+ self.StrVar = None
+ self.a = None
+ self.l_box = None
+
+ def wg_import_select(self):
+
+ try:
+ filepath = filedialog.askopenfilename(initialdir=str(_u), title='Select Wireguard config File',
+ filetypes=[('WG config files', '*.conf')], )
+
+ with open(filepath, 'r') as file:
+ read = file.read()
+ path_split = filepath.split('/')
+ path_split1 = path_split[-1]
+ self.a = TunnelActiv.active()
+
+ if 'PrivateKey = ' in read and 'PublicKey = 'in read and 'Endpoint =' in read:
+ with open(filepath, 'r') as file:
+ key = ConToDict.covert_to_dict(file)
+ pre_key = key[3]
+ if len(pre_key) != 0:
+ with open('/etc/wire_py/.keys', 'r') as readfile:
+ p_key = readfile.readlines()
+ if pre_key in p_key or pre_key + '\n' in p_key:
+ """img_w, img_i, w_title, w_txt hand over"""
+ iw = r'/usr/share/icons/wp-icons/64/error.png'
+ ii = r'/usr/share/icons/wp-icons/48/wg_msg.png'
+ wt = 'Import Error'
+ msg_t = 'Tunnel already available!\nPlease use another file for import'
+ msg_window(iw, ii, wt, msg_t)
+
+ else:
+
+ with open('/etc/wire_py/.keys', 'a') as keyfile:
+ keyfile.write(pre_key + '\r')
+ if len(path_split1) > 17:
+ p1 = shutil.copy(filepath, Path('/etc/wire_py/'))
+ path_split = path_split1[len(path_split1) - 17:]
+ os.rename(p1, Path('/etc/wire_py') / str(path_split))
+ new_conf = '/etc/wire_py/' + path_split
+ if self.a != '':
+ check_call(['nmcli', 'connection', 'down', TunnelActiv.active()])
+ ShowAddress.label_empty(self)
+
+ subprocess.check_output(['nmcli', 'connection', 'import', 'type',
+ 'wireguard', 'file', new_conf], text=True)
+
+ else:
+ shutil.copy(filepath, Path('/etc/wire_py/'))
+ if self.a != '':
+ check_call(['nmcli', 'connection', 'down', TunnelActiv.active()])
+ ShowAddress.label_empty(self)
+
+ subprocess.check_output(['nmcli', 'connection', 'import', 'type',
+ 'wireguard', 'file', filepath], text=True)
+
+ self.StrVar.set('')
+ self.a = TunnelActiv.active()
+ self.l_box.insert(0, self.a)
+ self.l_box.update()
+ self.StrVar = tk.StringVar()
+ self.StrVar.set(self.a)
+ GreenLabel.green_show_label(self)
+ StartStopBTN.button_stop(self)
+ wg_read = Path('/etc/wire_py') / str(self.a + '.conf')
+ with open(wg_read, 'r') as file_for_key:
+ data = ConToDict.covert_to_dict(file_for_key)
+
+ ''' Address Label '''
+ ShowAddress.init_and_report(self, data)
+ ShowAddress.show_data(self)
+ check_call(['nmcli', 'con', 'mod', self.a, 'connection.autoconnect', 'no'])
+ Path.chmod(wg_read, 0o600)
+
+ if 'PrivateKey = ' and 'Endpoint = ' not in read:
+ """img_w, img_i, w_title, w_txt hand over"""
+ iw = r'/usr/share/icons/wp-icons/64/error.png'
+ ii = r'/usr/share/icons/wp-icons/48/wg_msg.png'
+ wt = 'Import Error'
+ msg_t = 'Oh... no valid Wireguard File!\nPlease select a valid Wireguard File'
+ msg_window(iw, ii, wt, msg_t)
+
+ except EOFError:
+ pass
+ except TypeError:
+ pass
+ except FileNotFoundError:
+ pass
+ except subprocess.CalledProcessError:
+
+ print('Tunnel exist!')
+
+
class FrameWidgets(ttk.Frame):
def __init__(self, container, **kwargs):
super().__init__(container, **kwargs)
@@ -751,6 +864,34 @@ class FrameWidgets(ttk.Frame):
msg_window(iw, ii, wt, msg_t)
+class MyToolTip(tk.Toplevel):
+ TIP_X_OFFSET = 8
+ TIP_Y_OFFSET = 8
+ AUTO_CLEAR_TIME = 10 # Millisecond. (1/100 sec.)
+
+ def __init__(self, x_pos, y_pos, message=None, auto_clear=False):
+ self.x_pos = x_pos
+ self.y_pos = y_pos
+ self.message = message
+ self.auto_clear = auto_clear
+
+ tk.Toplevel.__init__(self)
+ self.overrideredirect(True)
+
+ self.message_label = ttk.Label(self, compound='left', text=self.message, padding=4)
+ self.message_label.pack()
+
+ self.geometry("+%d+%d" % (self.x_pos + self.TIP_X_OFFSET,
+ self.y_pos + self.TIP_X_OFFSET))
+
+ if self.auto_clear:
+ self.after(self.AUTO_CLEAR_TIME, self.clear_tip)
+
+ def clear_tip(self):
+ """Remove Tool-Tip"""
+ self.destroy()
+
+
if __name__ == '__main__':
window = MainWindow()
"""