#!/usr/bin/python3 import os import shutil import subprocess import tkinter as tk from pathlib import Path from subprocess import check_call from tkinter import filedialog, ttk, TclError from wg_func import (Tunnel, msg_window, WirePyUpdate, res, _u, version, path_to_file2, tips) tcl_path = Path('/usr/share/TK-Themes') class MainWindow(tk.Tk): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.my_tool_tip = None self.x_width = 600 self.y_height = 383 self.monitor_center_x = self.winfo_screenwidth() / 2 - (self.x_width / 2) self.monitor_center_y = self.winfo_screenheight() / 2 - (self.y_height / 2) self.resizable(width=True, height=True) self.title('Wire-Py') self.geometry('%dx%d+%d+%d' % (self.x_width, self.y_height, self.monitor_center_x, self.monitor_center_y)) self.columnconfigure(0, weight=1) self.rowconfigure(0, weight=1) self.style = ttk.Style(self) self.tk.call('source', str(tcl_path) + '/water.tcl') ''' self.tk.call('source', 'TK-Themes/water.tcl') ''' with open(path_to_file2, 'r') as read_file: lines = read_file.readlines() if 'light\n' in lines: self.tk.call('set_theme', 'light') else: self.tk.call('set_theme', 'dark') ''' Load the image file from disk. ''' self.wg_icon = tk.PhotoImage(file=r'/usr/share/icons/wp-icons/48/wg_vpn.png') ''' Set it as the window icon ''' self.iconphoto(True, self.wg_icon) FrameWidgets(self).grid() class FrameWidgets(ttk.Frame): def __init__(self, container, **kwargs): super().__init__(container, **kwargs) self.lb_tunnel = None self.btn_stst = None self.endpoint = None self.dns = None self.address = None self.auto_con = 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') self.imp_pic = tk.PhotoImage(file=r'/usr/share/icons/wp-icons/48/wg_import.png') self.tr_pic = tk.PhotoImage(file=r'/usr/share/icons/wp-icons/48/wg_trash.png') self.exp_pic = tk.PhotoImage(file=r'/usr/share/icons/wp-icons/48/wg_export.png') self.warning_pic = tk.PhotoImage(file=r'/usr/share/icons/wp-icons/64/error.png') 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') ''' Set on or off in file ''' def update(): if set_update.get() == 1: with open(path_to_file2, 'r') as set_file2: lines2 = set_file2.readlines() lines2[1] = 'off\n' with open(path_to_file2, 'w') as set_file2: set_file2.writelines(lines2) if set_update.get() == 0: with open(path_to_file2, 'r') as set_file2: lines2 = set_file2.readlines() lines2[1] = 'on\n' with open(path_to_file2, 'w') as set_file2: set_file2.writelines(lines2) ''' Set True or False in file ''' def tooltip(): if set_tip.get(): with open(path_to_file2, 'r') as set_file2: lines2 = set_file2.readlines() lines2[5] = 'False\n' with open(path_to_file2, 'w') as set_file2: set_file2.writelines(lines2) else: with open(path_to_file2, 'r') as set_file2: lines2 = set_file2.readlines() lines2[5] = 'True\n' with open(path_to_file2, 'w') as set_file2: set_file2.writelines(lines2) ''' Set dark or light ''' def theme_change_light(): if self.tk.call("ttk::style", "theme", "use") == "water-dark": ''' Set light theme ''' self.tk.call('set_theme', 'light') with open(path_to_file2, 'r') as theme_set2: lines3 = theme_set2.readlines() lines3[3] = 'light\n' with open(path_to_file2, 'w') as theme_set2: theme_set2.writelines(lines3) self.color_label() def theme_change_dark(): if not self.tk.call("ttk::style", "theme", "use") == "water-dark": ''' Set dark theme ''' self.tk.call('set_theme', 'dark') with open(path_to_file2, 'r') as theme_set2: lines4 = theme_set2.readlines() lines4[3] = 'dark\n' with open(path_to_file2, 'w') as theme_set2: theme_set2.writelines(lines4) self.color_label() def info(): def link_btn(): _u1 = str(_u[6:]) path_to_file = Path('/usr/bin/./run_as') check_call(['su', _u1, path_to_file]) """img_w, img_i, w_title, w_txt , txt2, com hand over""" iw = r'/usr/share/icons/wp-icons/48/wg_vpn.png' ii = r'/usr/share/icons/wp-icons/48/wg_msg.png' wt = 'Info' msg_t = ('Wire-Py a simple Wireguard Gui for Linux systems.\n\n' 'Wire-Py is open source software written in Python.\n\n' 'Email: polunga40@unity-mail.de also likes for donation.\n\n' 'Use without warranty!\n') txt2 = 'Go to Wire-Py git' com = link_btn msg_window(iw, ii, wt, msg_t, txt2, com) ''' Frame for Menu ''' self.menu_frame = ttk.Frame(self) self.menu_frame.configure(relief='flat') self.menu_frame.grid(column=0, row=0, columnspan=4, sticky='w') ''' App Menu ''' self.version_lb = ttk.Label(self.menu_frame, text=version) self.version_lb.config(font=('Ubuntu', 11), foreground='#00c4ff') self.version_lb.grid(column=0, row=0, rowspan=4, padx=10) def version_enter(event): """ The mouse moves into the Version widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, f'Version: {version[2:]}') def version_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() self.options_btn = ttk.Menubutton(self.menu_frame, text='Options') self.options_btn.grid(column=1, row=0) def sets_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, 'Click for Settings') def sets_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() self.version_lb.bind('', version_enter) self.version_lb.bind('', version_leave) self.options_btn.bind('', sets_enter) self.options_btn.bind('', sets_leave) set_update = tk.IntVar() set_tip = tk.BooleanVar() self.settings = tk.Menu(self, relief='flat') self.options_btn.configure(menu=self.settings, style='Toolbutton') self.settings.add_checkbutton(label='Disable Updates', command=update, variable=set_update) self.settings.add_checkbutton(label='Disable Tooltips', command=tooltip, variable=set_tip) self.settings.add_command(label='Light', command=theme_change_light) self.settings.add_command(label='Dark', command=theme_change_dark) ''' About BTN Menu / Label ''' self.about_btn = ttk.Button(self.menu_frame, text='About', style='Toolbutton', command=info) self.about_btn.grid(column=2, row=0) self.readme = tk.Menu(self) ''' Update and Tooltip Label ''' self.updates_lb = ttk.Label(self.menu_frame) self.updates_lb.grid(column=3, row=0, padx=10) '''View Checkbox for enable or disable Tooltip ''' if tips: set_tip.set(value=False) else: set_tip.set(value=True) '''View Checkbox for enable or disable Updates ''' if res == 'False': set_update.set(value=1) self.updates_lb.configure(text='Update search off') def disable_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, 'Updates you have disabled') def disable_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() self.updates_lb.bind('', disable_enter) self.updates_lb.bind('', disable_leave) elif res == 'No Internet Connection!': self.updates_lb.configure(text='No Server Connection!', foreground='red') elif res == 'No Updates': self.updates_lb.configure(text='No Updates') def congratulations_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, 'Congratulations! Wire-Py is up to date') def congratulations_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() self.updates_lb.bind('', congratulations_enter) self.updates_lb.bind('', congratulations_leave) else: set_update.set(value=0) text = f'Update {res} available!' ''' Update BTN Menu''' self.update_btn = ttk.Menubutton(self.menu_frame, text=text) self.update_btn.grid(column=4, row=0, padx=0) def download_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, 'Click to download new version') def download_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() self.update_btn.bind('', download_enter) self.update_btn.bind('', download_leave) self.download = tk.Menu(self, relief='flat') self.update_btn.configure(menu=self.download, style='Toolbutton') self.download.add_command(label='Download', command=WirePyUpdate.download) ''' Show active Tunnel ''' self.a = Tunnel.active() ''' Label Frame 1 ''' self.lb_frame_btn_lbox = ttk.Frame(self) self.lb_frame_btn_lbox.configure(relief='flat') self.lb_frame_btn_lbox.grid(column=0, rowspan=3, row=1) ''' Label Frame 2 ''' self.lb_frame = ttk.Frame(self) self.lb_frame.configure(relief='solid') self.lb_frame.grid(column=2, row=2, sticky='snew', padx=20, pady=5) ''' Label Frame 3 ''' self.lb_frame2 = ttk.Frame(self) self.lb_frame2.configure(relief='solid') self.lb_frame2.grid(column=2, row=3, sticky='snew', padx=20, pady=5) ''' Bottom Frame 4 ''' self.lb_frame3 = ttk.Frame(self) self.lb_frame3.configure(relief='flat') self.lb_frame3.grid(column=0, row=5, columnspan=4, sticky='snew', padx=2, pady=2) ''' Show active Label ''' self.select_tunnel = None self.lb = ttk.Label(self, text='Active: ') self.lb.config(font=('Ubuntu', 11, 'bold')) self.lb.grid(column=2, row=1, padx=15, pady=4, sticky='w') ''' Label to Show active Tunnel ''' self.StrVar = tk.StringVar(value=self.a) self.color_label() ''' Interface Label ''' self.interface = ttk.Label(self.lb_frame, text='Interface') self.interface.grid(column=0, row=3, sticky='we', padx=120) self.interface.config(font=('Ubuntu', 9)) ''' Peer Label ''' self.peer = ttk.Label(self.lb_frame2, text='Peer') self.peer.config(font=('Ubuntu', 9)) self.peer.grid(column=0, row=4, sticky='we', padx=130) ''' Listbox with Scrollbar ''' def enable_check_box(_): if self.l_box.size() != 0: self.wg_autostart.configure(state='normal') self.lb_rename.config(state='normal') self.lb_rename.delete(0, tk.END) self.btn_rename.config(state='normal') self.l_box = tk.Listbox(self.lb_frame_btn_lbox, selectmode='single') self.l_box.config(relief='ridge', font=('Ubuntu', 12, 'bold')) self.l_box.grid(column=1, rowspan=4, row=0, sticky='ns') self.l_box.event_add('<>', '') self.l_box.bind('<>', enable_check_box) self.scrollbar = ttk.Scrollbar(self.lb_frame_btn_lbox, orient='vertical', command=self.l_box.yview) self.scrollbar.grid(column=1, rowspan=4, row=0, sticky='nse') self.l_box.configure(yscrollcommand=self.scrollbar.set) ''' Tunnel List ''' self.tl = Tunnel.list() for tunnels in self.tl: self.l_box.insert("end", tunnels) self.l_box.update() def list_empty_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, 'You must first import\na Wireguard tunnel') def list_empty_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() def list_not_empty_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, 'Select a Tunnel') def list_not_empty_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() if self.l_box.size() == 0: self.l_box.bind('', list_empty_enter) self.l_box.bind('', list_empty_leave) else: self.l_box.bind('', list_not_empty_enter) self.l_box.bind('', list_not_empty_leave) ''' Button Vpn ''' if self.a != '': self.stop() wg_read = Path('/etc/wire_py') / str(self.a + '.conf') with open(wg_read, 'r') as file: data = Tunnel.con_to_dict(file) ''' Address Label ''' self.init_and_report(data) self.show_data() else: self.start() ''' Address Label ''' self.add = tk.StringVar() self.DNS = tk.StringVar() self.enp = tk.StringVar() self.label_empty() self.show_data() ''' Button Import ''' self.btn_i = ttk.Button(self.lb_frame_btn_lbox, image=self.imp_pic, command=self.import_sl, padding=0) self.btn_i.grid(column=0, row=1, padx=15, pady=8) def imp_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, 'Click to import a Wireguard Tunnel') def imp_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() self.btn_i.bind('', imp_enter) self.btn_i.bind('', imp_leave) def delete(): try: self.select_tunnel = self.l_box.curselection() select_tl = self.l_box.get(self.select_tunnel[0]) with open('/etc/wire_py/' + select_tl + '.conf', 'r+') as file2: key = Tunnel.con_to_dict(file2) pre_key = key[3] check_call(['nmcli', 'connection', 'delete', select_tl]) self.l_box.delete(self.select_tunnel[0]) with open(path_to_file2, 'r') as set_file6: lines6 = set_file6.readlines() if select_tl == lines6[7].strip() and 'off' not in lines6[7].strip(): lines6[7] = 'off' with open(path_to_file2, 'w') as set_file7: set_file7.writelines(lines6) self.selected_option.set(0) self.autoconnect_var.set('no Autoconnect') Path.unlink(Path('/etc/wire_py') / str(select_tl + '.conf')) with open('/etc/wire_py/.keys', 'r') as readfile: with open('/etc/wire_py/.keys2', 'w') as writefile: for line in readfile: if pre_key not in line.strip("\n"): writefile.write(line) file_one = Path('/etc/wire_py/.keys2') file_two = file_one.with_name('.keys') file_one.replace(file_two) self.wg_autostart.configure(state='disabled') ''' for disable checkbox when Listbox empty ''' def empty_list_start_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, 'No tunnels to start in the list') def empty_list_start_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() if self.l_box.size() == 0: self.wg_autostart.configure(state='disabled') self.lb_rename.configure(state='disabled') self.l_box.bind('', list_empty_enter) self.l_box.bind('', list_empty_leave) self.wg_autostart.bind('', chk_enter) self.wg_autostart.bind('', chk_leave) self.btn_tr.bind('', empty_list_enter) self.btn_tr.bind('', empty_list_leave) self.btn_exp.bind('', empty_list_enter) self.btn_exp.bind('', empty_list_leave) self.btn_stst.bind('', empty_list_start_enter) self.btn_stst.bind('', empty_list_start_leave) self.lb_rename.bind('', rename_no_active_enter) self.lb_rename.bind('', rename_no_active_leave) self.lb_rename.insert(0, 'Max. 12 characters!') if self.a != '' and self.a == select_tl: self.StrVar.set(value='') self.start() self.l_box.update() ''' Address Label ''' self.add.set('') self.DNS.set('') self.enp.set('') return select_tl except IndexError: if self.l_box.size() != 0: """img_w, img_i, w_title, w_txt hand over""" iw = r'/usr/share/icons/wp-icons/64/info.png' ii = r'/usr/share/icons/wp-icons/48/wg_msg.png' wt = 'Select tunnel' msg_t = 'Please select a tunnel from the list.' msg_window(iw, ii, wt, msg_t) else: """img_w, img_i, w_title, w_txt hand over""" iw = r'/usr/share/icons/wp-icons/64/info.png' ii = r'/usr/share/icons/wp-icons/48/wg_msg.png' wt = 'Select tunnel' msg_t = 'Please first import tunnel.' msg_window(iw, ii, wt, msg_t) ''' Button Trash ''' self.btn_tr = ttk.Button(self.lb_frame_btn_lbox, image=self.tr_pic, command=delete, padding=0, style='CButton.TButton') self.btn_tr.grid(column=0, row=2, padx=15, pady=8) def empty_list_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, 'No tunnels to delete in the list') def empty_list_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() def del_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, 'Click to delete a Wireguard Tunnel\nSelect from the list!') def del_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() if self.l_box.size() == 0: self.btn_tr.bind('', empty_list_enter) self.btn_tr.bind('', empty_list_leave) else: self.btn_tr.bind('', del_enter) self.btn_tr.bind('', del_leave) ''' Button Export ''' self.btn_exp = ttk.Button(self.lb_frame_btn_lbox, image=self.exp_pic, command=Tunnel.export, padding=0) self.btn_exp.grid(column=0, row=3, padx=15, pady=8) def empty_list_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, 'No Tunnels in List for Export') def empty_list_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() def exp_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, ' Click to export all\nWireguard Tunnel to Zipfile') def exp_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() if self.l_box.size() == 0: self.btn_exp.bind('', empty_list_enter) self.btn_exp.bind('', empty_list_leave) else: self.btn_exp.bind('', exp_enter) self.btn_exp.bind('', exp_leave) ''' Label Entry ''' self.lb_rename = ttk.Entry(self.lb_frame3, width=20) self.lb_rename.grid(column=2, row=0, padx=8, pady=10, sticky='ne') self.lb_rename.insert(0, 'Max. 12 characters!') self.lb_rename.config(state='disable') def rename_no_active_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() def rename_no_active_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, 'To rename a tunnel, at least one must be in the list') def rename_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, 'To rename a tunnel, you need to\nselect a tunnel from the list') def rename_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() if self.l_box.size() != 0: self.lb_rename.bind('', rename_enter) self.lb_rename.bind('', rename_leave) else: self.lb_rename.bind('', rename_no_active_enter) self.lb_rename.bind('', rename_no_active_leave) def tl_rename(): special_characters = ['\\', '/', '{', '}', ' '] if len(self.lb_rename.get()) > 12: """img_w, img_i, w_title, w_txt hand over""" iw = r'/usr/share/icons/wp-icons/64/info.png' ii = r'/usr/share/icons/wp-icons/48/wg_msg.png' wt = 'Renaming not possible' msg_t = 'The new name may contain only 12 characters.' msg_window(iw, ii, wt, msg_t) elif len(self.lb_rename.get()) == 0: """img_w, img_i, w_title, w_txt hand over""" iw = r'/usr/share/icons/wp-icons/64/info.png' ii = r'/usr/share/icons/wp-icons/48/wg_msg.png' wt = 'Renaming not possible' msg_t = 'At least one character must be entered.' msg_window(iw, ii, wt, msg_t) elif any(ch in special_characters for ch in self.lb_rename.get()): """img_w, img_i, w_title, w_txt hand over""" iw = r'/usr/share/icons/wp-icons/64/info.png' ii = r'/usr/share/icons/wp-icons/48/wg_msg.png' wt = 'Renaming not possible' msg_t = 'No valid sign. These must not be used.\nBlank, Slash, Backslash and { }\n' msg_window(iw, ii, wt, msg_t) else: try: self.select_tunnel = self.l_box.curselection() select_tl = self.l_box.get(self.select_tunnel[0]) ''' nmcli connection modify old connection.id iphone ''' check_call(['nmcli', 'connection', 'modify', select_tl, 'connection.id', self.lb_rename.get()]) source = Path('/etc/wire_py') / str(select_tl + '.conf') destination = source.with_name(str(self.lb_rename.get() + '.conf')) source.replace(destination) self.l_box.delete(self.select_tunnel[0]) self.l_box.insert("end", self.lb_rename.get()) self.l_box.update() new_a_connect = self.lb_rename.get() self.lb_rename.delete(0, tk.END) if self.a != '' and self.a == select_tl: self.a = Tunnel.active() self.StrVar.set(value=self.a) with open(path_to_file2, 'r') as set_file5: lines5 = set_file5.readlines() if select_tl == lines5[7].strip() and 'off' not in lines5[7].strip(): lines5[7] = new_a_connect with open(path_to_file2, 'w') as theme_set5: theme_set5.writelines(lines5) self.autoconnect_var.set(value=new_a_connect) return select_tl except IndexError: """img_w, img_i, w_title, w_txt hand over""" iw = r'/usr/share/icons/wp-icons/64/info.png' ii = r'/usr/share/icons/wp-icons/48/wg_msg.png' wt = 'Renaming not possible' msg_t = 'Please select a tunnel from the list.' msg_window(iw, ii, wt, msg_t) ''' Button Rename ''' self.btn_rename = ttk.Button(self.lb_frame3, text='Rename', state='disable', command=tl_rename, padding=4, style='RnButton.TButton') self.btn_rename.grid(column=3, row=0, padx=5, pady=10, sticky='ne') ''' Check Buttons ''' self.selected_option = tk.IntVar() self.autoconnect_var = tk.StringVar() self.autoconnect_var.set(self.auto_con) ''' Frame for Labels, Entry and Button''' self.autoconnect = ttk.Label(self.lb_frame3, textvariable=self.autoconnect_var, width=15) self.autoconnect.config(font=('Ubuntu', 11)) self.autoconnect.grid(column=1, row=0, sticky='e', pady=19) self.wg_autostart = ttk.Checkbutton(self.lb_frame3, text='Autoconnect on:', variable=self.selected_option, command=self.box_set) self.wg_autostart.grid(column=0, row=0, pady=15, padx=15, sticky='nw') def chk_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, 'You must have at least one\n' 'tunnel in the list,to use the autostart') def chk_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() if self.l_box.size() >= 1 and len(self.l_box.curselection()) >= 1: def chk_a_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, 'To use the autostart, enable this Checkbox') def chk_a_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() self.wg_autostart.bind('', chk_a_enter) self.wg_autostart.bind('', chk_a_leave) if self.l_box.size() == 0: self.wg_autostart.bind('', chk_enter) self.wg_autostart.bind('', chk_leave) else: def chk_a_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, 'To use the autostart, a tunnel must be selected from the list') def chk_a_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() self.wg_autostart.bind('', chk_a_enter) self.wg_autostart.bind('', chk_a_leave) self.on_off() def import_sl(self): """ Import Methode 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. """ 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 = Tunnel.active() if 'PrivateKey = ' in read and 'PublicKey = ' in read and 'Endpoint =' in read: with open(filepath, 'r') as file: key = Tunnel.con_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', Tunnel.active()]) self.label_empty() 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', Tunnel.active()]) self.label_empty() subprocess.check_output(['nmcli', 'connection', 'import', 'type', 'wireguard', 'file', filepath], text=True) self.StrVar.set('') self.a = Tunnel.active() self.l_box.insert(0, self.a) self.wg_autostart.configure(state='normal') self.l_box.selection_clear(0, tk.END) self.l_box.update() self.l_box.selection_set(0) def chk_a_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, 'To use the autostart, enable this ' 'Checkbox') def chk_a_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() def list_info_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, 'List of available tunnels') def list_info_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() def del_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, 'Click to delete a Wireguard ' 'Tunnel\nSelect from the list!') def del_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() def exp_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, ' Click to export ' 'all\nWireguard Tunnel to Zipfile') def exp_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() def rename_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, 'To rename a tunnel, you need to\n' 'select a tunnel from the list') def rename_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() self.wg_autostart.bind('', chk_a_enter) self.wg_autostart.bind('', chk_a_leave) self.l_box.bind('', list_info_enter) self.l_box.bind('', list_info_leave) self.btn_tr.bind('', del_enter) self.btn_tr.bind('', del_leave) self.btn_exp.bind('', exp_enter) self.btn_exp.bind('', exp_leave) self.lb_rename.bind('', rename_enter) self.lb_rename.bind('', rename_leave) self.lb_rename.insert(0, 'Max. 12 characters!') self.StrVar = tk.StringVar() self.StrVar.set(self.a) self.color_label() self.stop() wg_read = Path('/etc/wire_py') / str(self.a + '.conf') with open(wg_read, 'r') as file_for_key: data = Tunnel.con_to_dict(file_for_key) ''' Address Label ''' self.init_and_report(data) self.show_data() 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!') def box_set(self): """ This Method will display the autostart label which Tunnel is automatically started regardless of the active tunnel. The selected tunnel is written into a file to read it after the start of the system. """ try: select_tunnel = self.l_box.curselection() select_tl = self.l_box.get(select_tunnel[0]) if self.selected_option.get() == 0: with open(path_to_file2, 'r') as set_file3: lines3 = set_file3.readlines() lines3[7] = 'off' with open(path_to_file2, 'w') as set_file3: set_file3.writelines(lines3) tl = Tunnel.list() if len(tl) == 0: self.wg_autostart.configure(state='disabled') if self.selected_option.get() >= 1: with open(path_to_file2, 'r') as set_file3: lines3 = set_file3.readlines() lines3[7] = select_tl with open(path_to_file2, 'w') as set_file3: set_file3.writelines(lines3) except IndexError: self.selected_option.set(1) self.on_off() def on_off(self): """ Here it is checked whether the path to the file is there if not it is created. Set (on), the selected tunnel is displayed in the label. At (off) the label is first emptied then filled with No Autoconnect """ with open(path_to_file2, 'r') as set_file4: lines4 = set_file4.readlines() if lines4[7] != 'off': self.selected_option.set(1) self.autoconnect_var.set('') self.auto_con = lines4[7] else: self.wg_autostart.configure(state='disabled') self.auto_con = 'no Autoconnect' self.autoconnect_var.set('') self.autoconnect_var = tk.StringVar() self.autoconnect_var.set(self.auto_con) self.autoconnect = ttk.Label(self.lb_frame3, textvariable=self.autoconnect_var, foreground='#0071ff', width=15) self.autoconnect.config(font=('Ubuntu', 11)) self.autoconnect.grid(column=1, row=0, sticky='e', pady=19) def init_and_report(self, data=None): """ Displays the value address, DNS and peer in the labels or empty it again """ """ Address Label """ self.add = tk.StringVar() self.add.set('Address: ' + data[0]) self.DNS = tk.StringVar() self.DNS.set(' DNS: ' + data[1]) self.enp = tk.StringVar() self.enp.set('Endpoint: ' + data[2]) def label_empty(self): self.add.set('') self.DNS.set('') self.enp.set('') def show_data(self): """ Address Label """ self.address = ttk.Label(self.lb_frame, textvariable=self.add, foreground='#0071ff') self.address.grid(column=0, row=5, sticky='w', padx=10, pady=6) self.address.config(font=('Ubuntu', 9)) ''' DNS Label ''' self.dns = ttk.Label(self.lb_frame, textvariable=self.DNS, foreground='#0071ff') self.dns.grid(column=0, row=7, sticky='w', padx=10, pady=6) self.dns.config(font=('Ubuntu', 9)) ''' Endpoint Label ''' self.endpoint = ttk.Label(self.lb_frame2, textvariable=self.enp, foreground='#0071ff') self.endpoint.grid(column=0, row=8, sticky='w', padx=10, pady=20) self.endpoint.config(font=('Ubuntu', 9)) def stop(self): """ Stop Button """ 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 stop_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, 'Click to stop selected Wireguard Tunnel') def stop_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() self.btn_stst.bind('', stop_enter) self.btn_stst.bind('', stop_leave) def start(self): """ Start Button """ 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) def empty_list_start_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, 'No tunnels to start in the list') def empty_list_start_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() def start_enter(event): """ The mouse moves into the entry widget """ window.my_tool_tip = MyToolTip(event.x_root, event.y_root, 'Click to start selected Wireguard Tunnel') def start_leave(_): """ The mouse moves from the entry widget """ ''' Remove Tool-Tip ''' window.my_tool_tip.destroy() tl = Tunnel.list() if len(tl) == 0: self.btn_stst.bind('', empty_list_start_enter) self.btn_stst.bind('', empty_list_start_leave) else: self.btn_stst.bind('', start_enter) self.btn_stst.bind('', start_leave) def color_label(self): """ View activ Tunnel in color green or yellow """ with open(path_to_file2, 'r') as read_file: lines = read_file.readlines() if 'light\n' in lines: self.lb_tunnel = ttk.Label(self, textvariable=self.StrVar, foreground='green') else: self.lb_tunnel = ttk.Label(self, textvariable=self.StrVar, foreground='yellow') self.lb_tunnel.config(font=('Ubuntu', 11, 'bold')) self.lb_tunnel.grid(column=2, padx=10, row=1) def wg_switch(self): self.a = Tunnel.active() try: if self.a == '': self.start() self.select_tunnel = self.l_box.curselection() select_tl = self.l_box.get(self.select_tunnel[0]) check_call(['nmcli', 'connection', 'up', select_tl]) wg_read = Path('/etc/wire_py') / str(select_tl + '.conf') with open(wg_read, 'r') as file: data = Tunnel.con_to_dict(file) ''' Address Label ''' self.init_and_report(data) self.show_data() ''' Button Start/Stop ''' self.stop() self.a = Tunnel.active() self.StrVar = tk.StringVar() self.StrVar.set(self.a) self.color_label() elif self.a != '': ''' Button Start/Stop ''' self.stop() check_call(['nmcli', 'connection', 'down', self.a]) ''' Button Start/Stop ''' self.start() self.a = Tunnel.active() self.StrVar.set('') self.color_label() ''' Address Label ''' self.add.set('') self.DNS.set('') self.enp.set('') self.show_data() except IndexError: if self.l_box.size() != 0: """img_w, img_i, w_title, w_txt hand over""" iw = r'/usr/share/icons/wp-icons/64/info.png' ii = r'/usr/share/icons/wp-icons/48/wg_msg.png' wt = 'Select tunnel' msg_t = 'Please select a tunnel from the list.' msg_window(iw, ii, wt, msg_t) else: """img_w, img_i, w_title, w_txt hand over""" iw = r'/usr/share/icons/wp-icons/64/info.png' ii = r'/usr/share/icons/wp-icons/48/wg_msg.png' wt = 'Select tunnel' msg_t = 'Please first import tunnel.' msg_window(iw, ii, wt, msg_t) class MyToolTip(tk.Toplevel): TIP_X_OFFSET = 20 TIP_Y_OFFSET = 20 if not tips: AUTO_CLEAR_TIME = 0 else: AUTO_CLEAR_TIME = 900 # Millisecond. (1/90 sec.) def __init__(self, x_pos, y_pos, message=None, auto_clear=True): 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=6) 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() """ the hidden files are hidden in Filedialog """ try: window.tk.call('tk_getOpenFile', '-foobarbaz') except TclError: pass window.tk.call('set', '::tk::dialog::file::showHiddenBtn', '0') window.tk.call('set', '::tk::dialog::file::showHiddenVar', '0') window.mainloop()