#!/usr/bin/python3 """ this script is a simple GUI for managing Wireguard Tunnels """ import gettext import locale import os import shutil import subprocess import sys import tkinter as tk import webbrowser from pathlib import Path from subprocess import check_call from tkinter import TclError, filedialog, ttk from cls_mth_fc import (Create, GiteaUpdate, Tunnel, Tooltip, LxTools) from wp_app_config import AppConfig, Msg LxTools.uos() Create.dir_and_files() Create.make_dir() Create.decrypt() # 1 = 1. Year, 09 = Month of the Year, 2924 = Day and Year of the Year VERSION: str = "v. 2.04.1725" res = GiteaUpdate.api_down("https://git.ilunix.de/api/v1/repos/punix/Wire-Py/releases", VERSION, AppConfig.SETTINGS_FILE) class Wirepy(tk.Tk): """ Class Wirepy this is the Main Window of wirepy """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.my_tool_tip = None self.x_width = AppConfig.UI_CONFIG["window_size"][0] self.y_height = AppConfig.UI_CONFIG["window_size"][1] self.monitor_center_x = int(self.winfo_screenwidth() / 2 - (self.x_width / 2)) self.monitor_center_y = int(self.winfo_screenheight() / 2 - (self.y_height / 2)) self.resizable(AppConfig.UI_CONFIG["resizable_window"][0], AppConfig.UI_CONFIG["resizable_window"][1]) self.title(AppConfig.UI_CONFIG["window_title"]) self.geometry(f"{self.x_width}x{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", f"{AppConfig.SYSTEM_PATHS["tcl_path"]}/water.tcl") lines = AppConfig.SETTINGS_FILE.read_text() if "light\n" in lines: self.tk.call("set_theme", "light") else: self.tk.call("set_theme", "dark") # Load the image file from the disk self.wg_icon = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_vpn"]) # Set it as the window icon self.iconphoto(True, self.wg_icon) tips = LxTools.if_tip(AppConfig.SETTINGS_FILE) FrameWidgets(self, tips_enabled=tips).grid() class FrameWidgets(ttk.Frame): """ ttk frame class for better structure """ def __init__(self, container, tips_enabled=None, **kwargs): super().__init__(container, **kwargs) self.tunnel = Tunnel() 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=AppConfig.IMAGE_PATHS["icon_start"]) self.wg_vpn_stop = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_stop"]) self.imp_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_import"]) self.tr_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_trash"]) self.exp_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_export"]) self.warning_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_error"]) self.tips_enabled = tips_enabled if tips_enabled is not None else LxTools.if_tip(AppConfig.SETTINGS_FILE) # 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) Tooltip(self.version_lb, f"Version: {VERSION[2:]}", self.tips_enabled) self.options_btn = ttk.Menubutton(self.menu_frame, text=_("Options")) self.options_btn.grid(column=1, columnspan=1, row=0) Tooltip(self.options_btn, _("Click for Settings"), self.tips_enabled) 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=lambda: self.update_setting(set_update.get()), variable=set_update) self.settings.add_checkbutton(label=_("Disable Tooltips"), command=lambda: self.tooltip(set_tip.get()), variable=set_tip) self.settings.add_command(label=_("Light"), command=self.theme_change_light) self.settings.add_command(label=_("Dark"), command=self.theme_change_dark) # About BTN Menu / Label self.about_btn = ttk.Button( self.menu_frame, text=_("About"), style="Toolbutton", command=self.about) self.about_btn.grid(column=2, columnspan=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=4, columnspan=3, row=0, padx=10) # View Checkbox to 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")) Tooltip(self.updates_lb, _("Updates you have disabled"), self.tips_enabled) 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")) Tooltip(self.updates_lb, _("Congratulations! Wire-Py is up to date"), self.tips_enabled) 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, columnspan=3, row=0, padx=0) Tooltip(self.update_btn, _("Click to download new version"), self.tips_enabled) self.download = tk.Menu(self, relief="flat") self.update_btn.configure(menu=self.download, style="Toolbutton") self.download.add_command( label=_("Download"), command=lambda: GiteaUpdate.download(f"https://git.ilunix.de/punix/Wire-Py/archive/{res}.zip", res, AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_vpn"], AppConfig.IMAGE_PATHS["icon_error"], AppConfig.IMAGE_PATHS["icon_msg"])) # 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) # Bottom Frame 5 self.lb_frame4 = ttk.Frame(self) self.lb_frame4.configure(relief="flat") self.lb_frame4.grid(column=2, row=5, columnspan=3, sticky="e", padx=15) # 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.str_var = 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 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("<>", self.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 = LxTools.get_file_name(AppConfig.TEMP_DIR) for tunnels in self.tl: self.l_box.insert("end", tunnels) self.l_box.update() # Button Vpn if self.a != "": self.stop() data = self.handle_tunnel_data(self.a) else: self.start() # Address Label self.add = tk.StringVar() self.DNS = tk.StringVar() self.enp = tk.StringVar() self.reset_fields() 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) Tooltip(self.btn_i, _("Click to import a Wireguard Tunnel"), self.tips_enabled) # Button Trash self.btn_tr = ttk.Button(self.lb_frame_btn_lbox, image=self.tr_pic, command=self.delete, padding=0, style="CButton.TButton") self.btn_tr.grid(column=0, row=2, padx=15, pady=8) if self.l_box.size() == 0: Tooltip(self.btn_tr, _("No tunnels to delete in the list"), self.tips_enabled) else: Tooltip(self.btn_tr, _("Click to delete a Wireguard Tunnel\nSelect from the list!"), self.tips_enabled) # Button Export self.btn_exp = ttk.Button(self.lb_frame_btn_lbox, image=self.exp_pic, command=lambda: Tunnel.export(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_vpn"], AppConfig.IMAGE_PATHS["icon_error"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["sel_tl"], Msg.STR["tl_first"]), padding=0) self.btn_exp.grid(column=0, row=3, padx=15, pady=8) if self.l_box.size() == 0: Tooltip(self.btn_exp, _("No Tunnels in List for Export"), self.tips_enabled) else: Tooltip(self.btn_exp, _("Click to export all\nWireguard Tunnel to Zipfile"), self.tips_enabled) # Label Entry self.lb_rename = ttk.Entry(self.lb_frame4, 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") if self.l_box.size() != 0: Tooltip(self.lb_rename, _("To rename a tunnel, you need to\nselect a tunnel from the list"), self.tips_enabled) else: Tooltip(self.lb_rename, _("To rename a tunnel, at least one must be in the list"), self.tips_enabled) # Button Rename self.btn_rename = ttk.Button(self.lb_frame4, text=_("Rename"), state="disable", command=self.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(f"{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") if self.l_box.size() >= 1 and len(self.l_box.curselection()) >= 1: Tooltip(self.wg_autostart, Msg.TTIP["autostart"], tself.tips_enabled) if self.l_box.size() == 0: Tooltip(self.wg_autostart, Msg.TTIP["autostart_info"], self.tips_enabled) else: Tooltip(self.wg_autostart, Msg.TTIP["autostart"], self.tips_enabled) self.on_off() @staticmethod def update_setting(update_res) -> None: """ write off or on in file Args: update_res (int): argument that is passed contains 0 or 1 """ if update_res == 1: lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True) lines[1] = 'off\n' Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8") else: lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True) lines[1] = 'on\n' Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8") @staticmethod def tooltip(tip) -> None: """ write True or False in a file Args: tip (bool): argument that is passed contains True or False """ if tip: lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True) lines[5] = 'False\n' Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8") else: lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True) lines[5] = 'True\n' Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8") @staticmethod def about() -> None: """ a tk.Toplevel window """ def link_btn() -> str | None: webbrowser.open("https://git.ilunix.de/punix/Wire-Py") 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") LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_vpn"], AppConfig.IMAGE_PATHS["icon_vpn"], _("Info"), msg_t, _("Go to Wire-Py git"), link_btn) def theme_change_light(self) -> None: """ Set a light theme """ if self.tk.call("ttk::style", "theme", "use") == "water-dark": self.tk.call("set_theme", "light") lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True) # (keepends=True) = not changed lines[3] = 'light\n' Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8") self.color_label() def theme_change_dark(self) -> None: """ Set a dark theme """ if not self.tk.call("ttk::style", "theme", "use") == "water-dark": self.tk.call("set_theme", "dark") lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True) lines[3] = 'dark\n' Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8") self.color_label() def start(self) -> None: """ Start Button """ self.btn_stst = ttk.Button(self.lb_frame_btn_lbox, image=self.wg_vpn_start, command=lambda: self.wg_switch("start"), padding=0) self.btn_stst.grid(column=0, row=0, padx=5, pady=8) tl = LxTools.get_file_name(AppConfig.TEMP_DIR) if len(self.tl) == 0: Tooltip(self.btn_stst, Msg.TTIP["empty_list"], self.tips_enabled) else: Tooltip(self.btn_stst, Msg.TTIP["start_tl"], self.tips_enabled) def handle_tunnel_data(self, tunnel_name: str) -> tuple[str, str, str, str | None]: """_summary_ Args: tunnel_name (str): name of a tunnel Returns: tuple[str, str]: tuple with tunnel data """ wg_read = f"/tmp/tlecdcwg/{tunnel_name}.conf" with open(wg_read, "r", encoding="utf-8") as file: data = Tunnel.con_to_dict(file) self.init_and_report(data) self.show_data() return data def color_label(self) -> None: """ View activ Tunnel in the color green or yellow """ lines = AppConfig.SETTINGS_FILE.read_text() if "light\n" in lines: self.lb_tunnel = ttk.Label(self, textvariable=self.str_var, foreground="green") else: self.lb_tunnel = ttk.Label(self, textvariable=self.str_var, foreground="yellow") self.lb_tunnel.config(font=("Ubuntu", 11, "bold")) self.lb_tunnel.grid(column=2, padx=10, row=1) def stop(self) -> None: """ Stop Button """ self.btn_stst = ttk.Button(self.lb_frame_btn_lbox, image=self.wg_vpn_stop, command=lambda: self.wg_switch("stop"), padding=0) self.btn_stst.grid(column=0, row=0, padx=5, pady=8) Tooltip(self.btn_stst, Msg.TTIP["stop_tl"], self.tips_enabled) def reset_fields(self) -> None: """ reset data from labels """ fields = [self.add, self.DNS, self.enp] for field in fields: field.set("") def import_sl(self) -> None: """validity check of wireguard config files""" Create.dir_and_files() try: filepath = filedialog.askopenfilename( initialdir=f"{Path.home()}", title=_("Select Wireguard config File"), filetypes=[(_("WG config files"), "*.conf")] ) # Überprüfe, ob der Benutzer den Dialog abgebrochen hat if not filepath: print("File import: abort by user...") return with open(filepath, "r", encoding="utf-8") as file: read = file.read() path_split = filepath.split("/") path_split1 = path_split[-1] if "PrivateKey = " in read and "PublicKey = " in read and "Endpoint =" in read: with open(filepath, "r", encoding="utf-8") as file: key = Tunnel.con_to_dict(file) pre_key = key[3] if len(pre_key) != 0: p_key = AppConfig.KEYS_FILE.read_text(encoding="utf-8") if pre_key in p_key or f"{pre_key}\n" in p_key: LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_error"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["imp_err"], Msg.STR["tl_exist"]) else: with open(AppConfig.KEYS_FILE, "a", encoding="utf-8") as keyfile: keyfile.write(f"{pre_key}\r") if len(path_split1) > 17: p1 = shutil.copy(filepath, AppConfig.TEMP_DIR) path_split = path_split1[len(path_split1) - 17:] os.rename(p1, f"{AppConfig.TEMP_DIR}/{path_split}") new_conf = f"{AppConfig.TEMP_DIR}/{path_split}" if self.a != "": check_call(["nmcli", "connection", "down", self.a]) self.reset_fields() subprocess.check_output(["nmcli", "connection", "import", "type", "wireguard", "file", new_conf], text=True) Create.encrypt() else: shutil.copy(filepath, f"{AppConfig.TEMP_DIR}/") if self.a != "": check_call(["nmcli", "connection", "down", self.a]) self.reset_fields() subprocess.check_output(["nmcli", "connection", "import", "type", "wireguard", "file", filepath], text=True) Create.encrypt() self.str_var.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) Tooltip(self.wg_autostart, Msg.TTIP["autostart"], self.tips_enabled) Tooltip(self.btn_tr, Msg.TTIP["trash_tl"], self.tips_enabled) Tooltip(self.btn_exp, Msg.TTIP["export_tl"], self.tips_enabled) Tooltip(self.btn_rename, Msg.TTIP["rename_tl"], self.tips_enabled) self.lb_rename.insert(0, "Max. 12 characters!") self.str_var = tk.StringVar() self.str_var.set(self.a) self.color_label() self.stop() data = self.handle_tunnel_data(self.a) check_call(["nmcli", "con", "mod", self.a, "connection.autoconnect", "no"]) elif ("PrivateKey = " in read) and ("Endpoint = " in read): pass else: LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_error"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["imp_err"], Msg.STR["no_valid_file"]) except EOFError as e: print(e) except TypeError: print("File import: abort by user...") except FileNotFoundError: print("File import: abort by user...") except subprocess.CalledProcessError: print("Tunnel exist!") def delete(self) -> None: """ delete Wireguard Tunnel """ try: self.select_tunnel = self.l_box.curselection() select_tl = self.l_box.get(self.select_tunnel[0]) with open(f"/tmp/tlecdcwg/{select_tl}.conf", "r+", encoding="utf-8") 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(AppConfig.SETTINGS_FILE, "r", encoding="utf-8") as set_f6: lines6 = set_f6.readlines() if (select_tl == lines6[7].strip() and "off\n" not in lines6[7].strip()): lines6[7] = "off\n" with open(AppConfig.SETTINGS_FILE, "w", encoding="utf-8") as set_f7: set_f7.writelines(lines6) self.selected_option.set(0) self.autoconnect_var.set(_("no Autoconnect")) is_encrypt = Path.home() / f".config/wire_py/{select_tl}.dat" if is_encrypt.is_file(): Path.unlink(f"{Path.home()}/.config/wire_py/{select_tl}.dat") Path.unlink(f"/tmp/tlecdcwg/{select_tl}.conf") with open(AppConfig.KEYS_FILE, "r", encoding="utf-8") as readfile: with open(f"{Path.home()}/.config/wire_py/keys2", "w", encoding="utf-8") as writefile: for line in readfile: if pre_key not in line.strip("\n"): writefile.write(line) file_one = Path(f"{Path.home()}/.config/wire_py/keys2") file_two = file_one.with_name("keys") file_one.replace(file_two) self.wg_autostart.configure(state="disabled") # for disabling checkbox when Listbox empty if self.l_box.size() == 0: self.wg_autostart.configure(state="disabled") self.lb_rename.configure(state="disabled") Tooltip(self.wg_autostart, _("You must have at least one\ntunnel in the list,to use the autostart") , self.tips_enabled) Tooltip(self.btn_exp, _("No Tunnels in List for Export"), self.tips_enabled) Tooltip(self.btn_stst, _("No tunnels to start in the list"), self.tips_enabled) Tooltip(self.lb_rename, _("To rename a tunnel, at least one must be in the list"), tips, ) self.lb_rename.insert(0, _("Max. 12 characters!")) if self.a != "" and self.a == select_tl: self.str_var.set(value="") self.start() self.l_box.update() self.reset_fields() except IndexError: if self.l_box.size() != 0: LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["sel_tl"], Msg.STR["sel_list"]) else: LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["sel_tl"], Msg.STR["tl_first"]) def enable_check_box(self, _) -> None: """ checkbox for enable autostart Tunnel """ Create.files_for_autostart() 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") def on_off(self) -> None: """ 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 """ lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True) if lines[7] != "off\n": print(f"{lines[7]} starts automatically when the system starts.") self.selected_option.set(1) self.autoconnect_var.set("") self.auto_con = lines[7] else: self.selected_option.set(0) self.auto_con = _("no Autoconnect") print("Autostart disabled.") 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 box_set(self) -> None: """ Configures the autostart for a selected tunnel. This method is called when the user changes the autostart checkbox. It saves the selected tunnel in the configuration file so that it will be automatically connected at system startup. If the checkbox is deactivated, 'off' is written to the configuration file to disable the autostart. """ try: select_tunnel = self.l_box.curselection() select_tl = self.l_box.get(select_tunnel[0]) if self.selected_option.get() == 0: lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True) lines[7] = 'off\n' Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8") tl = LxTools.get_file_name(AppConfig.TEMP_DIR) if len(tl) == 0: self.wg_autostart.configure(state="disabled") if self.selected_option.get() >= 1: lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True) lines[7] = select_tl Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8") except IndexError: self.selected_option.set(1) self.on_off() def tl_rename(self) -> None: """ method to rename a tunnel """ name_of_file = LxTools.get_file_name(AppConfig.TEMP_DIR) special_characters = ["\\", "/", "{", "}", " "] if len(self.lb_rename.get()) > 12: LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["ren_err"], Msg.STR["sign_len"]) elif len(self.lb_rename.get()) == 0: LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["ren_err"], Msg.STR["zero_signs"]) elif any(ch in special_characters for ch in self.lb_rename.get()): LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["ren_err"], Msg.STR["false_signs"]) elif self.lb_rename.get() in name_of_file: LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["ren_err"], Msg.STR["is_in_use"]) 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 subprocess.check_output(["nmcli", "connection", "modify", select_tl, "connection.id", self.lb_rename.get()], text=True) source = Path(f"/tmp/tlecdcwg/{select_tl}.conf") destination = source.with_name(f"{self.lb_rename.get()}.conf") source.replace(destination) Path.unlink(f"{Path.home()}/.config/wire_py/{select_tl}.dat") 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) with open(AppConfig.SETTINGS_FILE, "r", encoding="utf-8") as set_f5: lines5 = set_f5.readlines() if select_tl == lines5[7].strip() and "off\n" not in lines5[7].strip(): lines5[7] = new_a_connect with open(AppConfig.SETTINGS_FILE, "w", encoding="utf-8") as theme_set5: theme_set5.writelines(lines5) self.autoconnect_var.set(value=new_a_connect) self.update_connection_display() Create.encrypt() except IndexError: LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["ren_err"], Msg.STR["sel_list"]) except subprocess.CalledProcessError: pass except EOFError as e: print(e) def activate_tunnel(self, tunnel_name): """Activates a tunnel after a delay""" try: # First check if the tunnel exists in NetworkManager nm_connections = subprocess.run( ["nmcli", "-t", "-f", "NAME", "connection", "show"], check=True, stdout=subprocess.PIPE, text=True ).stdout.strip().split('\n') # Find the actual connection name (it might have been modified) actual_name = None for conn in nm_connections: if tunnel_name in conn: actual_name = conn break if actual_name: # Use the actual connection name subprocess.run(["nmcli", "connection", "up", actual_name], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) else: # Use the original name as fallback subprocess.run(["nmcli", "connection", "up", tunnel_name], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # After successful activation, update the display self.a = Tunnel.active() self.str_var.set(self.a) self.color_label() # Try to load the tunnel data try: data = self.handle_tunnel_data(self.a) self.init_and_report(data) self.show_data() self.stop() except Exception as e: print(f"Error loading tunnel data: {e}") except subprocess.CalledProcessError as e: print(f"Error activating tunnel: {e}", "hier simma") def init_and_report(self, data=None) -> None: """ Displays the value address, DNS and peer in the labels or empty it again """ # Address Label self.add = tk.StringVar() self.add.set(f"{_("Address: ")}{data[0]}") self.DNS = tk.StringVar() self.DNS.set(f" DNS: {data[1]}") self.enp = tk.StringVar() self.enp.set(f"{_("Endpoint: ")}{data[2]}") def show_data(self) -> None: """ shows data in the label """ # 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 wg_switch(self, event=None) -> None: """ Deals with switching the VPN connection """ try: if self.a == "": self.select_tunnel = self.l_box.curselection() select_tl = self.l_box.get(self.select_tunnel[0]) self.handle_connection_state("start", select_tl) else: data = self.handle_tunnel_data(self.a) if data: self.handle_connection_state("stop") except IndexError: if self.l_box.size() != 0: LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["sel_tl"], Msg.STR["sel_list"]) else: LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["sel_tl"], Msg.STR["tl_first"]) def handle_connection_state(self, action: str, tunnel_name: str = None) -> None: """ central management for connection states Args: action (str): "start", "stop" or "toggle" tunnel_name (str, optional): name of a tunnel for a start-option. defaults to None. """ if action == "stop": if self.a: check_call(["nmcli", "connection", "down", self.a]) self.update_connection_display() self.reset_fields() self.start() elif action == "start": if tunnel_name or self.a: target_tunnel = tunnel_name or self.a check_call(["nmcli", "connection", "up", target_tunnel]) self.update_connection_display() data = self.handle_tunnel_data(self.a) self.init_and_report(data) self.show_data() self.color_label() self.stop() elif action == "toggle": if self.a: self.handle_connection_state("stop") else: self.handle_connection_state("start") def update_connection_display(self) -> None: """ Updated the display after connection changes """ self.a = Tunnel.active() if not hasattr(self, "str_var"): self.str_var = tk.StringVar() self.str_var.set(self.a) self.color_label() self.show_data() if __name__ == "__main__": _ = AppConfig.setup_translations() tips = LxTools.if_tip(AppConfig.SETTINGS_FILE) LxTools.sigi(AppConfig.TEMP_DIR, AppConfig.USER_FILE) window = Wirepy() """ 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() LxTools.clean_files(AppConfig.TEMP_DIR, AppConfig.USER_FILE) sys.exit(0)