From f4a51f005000b6f586cb1c45f91d8ea4ca2dcb09 Mon Sep 17 00:00:00 2001 From: punix Date: Wed, 7 May 2025 20:25:09 +0200 Subject: [PATCH] fix message window a path errors --- common_tools.py | 302 +++++++++++++++++--------- manage_tunnel.py | 15 -- ssl_decrypt.py | 32 ++- ssl_encrypt.py | 53 ++++- start_wg.py | 3 +- wirepy.py | 536 +++++++++++++++++++++++++++++++++++------------ wp_app_config.py | 82 ++++---- 7 files changed, 724 insertions(+), 299 deletions(-) delete mode 100644 manage_tunnel.py diff --git a/common_tools.py b/common_tools.py index b87fc1a..4655ef4 100755 --- a/common_tools.py +++ b/common_tools.py @@ -1,7 +1,5 @@ """ Classes Method and Functions for lx Apps """ -import gettext -import locale import os import shutil import signal @@ -20,6 +18,7 @@ import requests # Translate _ = AppConfig.setup_translations() + class Create: """ This class is for the creation of the folders and files @@ -43,7 +42,9 @@ class Create: else: sett.touch() - sett.write_text("[UPDATES]\non\n[THEME]\nlight\n[TOOLTIP]\nTrue\n[AUTOSTART ON]\noff\n") + sett.write_text( + "[UPDATES]\non\n[THEME]\nlight\n[TOOLTIP]\nTrue\n[AUTOSTART ON]\noff\n" + ) if AppConfig.KEYS_FILE.exists(): pass @@ -66,9 +67,11 @@ class Create: else: wg_ser.touch() - wg_ser.write_text("[Unit]\nDescription=Automatic Tunnel Start\nAfter=network-online.target\n\n[Service]\n" - "Type=oneshot\nExecStartPre=/bin/sleep 5\nExecStart=/usr/local/bin/start_wg.py\n[Install]" - "\nWantedBy=default.target") + wg_ser.write_text( + "[Unit]\nDescription=Automatic Tunnel Start\nAfter=network-online.target\n\n[Service]\n" + "Type=oneshot\nExecStartPre=/bin/sleep 5\nExecStart=/usr/local/bin/start_wg.py\n[Install]" + "\nWantedBy=default.target" + ) check_call(["systemctl", "--user", "enable", "wg_start.service"]) @staticmethod @@ -85,8 +88,12 @@ class Create: """ Starts SSL dencrypt """ - process: CompletedProcess[str] = subprocess.run(["pkexec", "/usr/local/bin/ssl_decrypt.py"], - stdout=subprocess.PIPE, text=True, check=True) + process: CompletedProcess[str] = subprocess.run( + ["pkexec", "/usr/local/bin/ssl_decrypt.py"], + stdout=subprocess.PIPE, + text=True, + check=True, + ) path: Path = Path.home() / ".config/wire_py/" file_in_path: list[Path] = list(path.rglob("*.dat")) if file_in_path: @@ -102,8 +109,12 @@ class Create: """ Starts SSL encryption """ - process: CompletedProcess[str] = subprocess.run(["pkexec", "/usr/local/bin/ssl_encrypt.py"], - stdout=subprocess.PIPE, text=True, check=True) + process: CompletedProcess[str] = subprocess.run( + ["pkexec", "/usr/local/bin/ssl_encrypt.py"], + stdout=subprocess.PIPE, + text=True, + check=True, + ) print(process.stdout) if process.returncode == 0: print("All Files successfully encrypted...") @@ -123,40 +134,44 @@ class LxTools(tk.Tk): def center_window_cross_platform(window, width, height): """ Centers a window on the primary monitor in a way that works on both X11 and Wayland - + Args: window: The tkinter window to center width: Window width height: Window height """ # Calculate the position before showing the window - + # First attempt: Try to use GDK if available (works on both X11 and Wayland) try: import gi - gi.require_version('Gdk', '3.0') + + gi.require_version("Gdk", "3.0") from gi.repository import Gdk - + display = Gdk.Display.get_default() monitor = display.get_primary_monitor() or display.get_monitor(0) geometry = monitor.get_geometry() scale_factor = monitor.get_scale_factor() - + # Calculate center position on primary monitor x = geometry.x + (geometry.width - width // scale_factor) // 2 y = geometry.y + (geometry.height - height // scale_factor) // 2 - + # Set window geometry window.geometry(f"{width}x{height}+{x}+{y}") return except (ImportError, AttributeError): pass - + # Second attempt: Try xrandr for X11 try: import subprocess - output = subprocess.check_output(["xrandr", "--query"], universal_newlines=True) - + + output = subprocess.check_output( + ["xrandr", "--query"], universal_newlines=True + ) + # Parse the output to find the primary monitor primary_info = None for line in output.splitlines(): @@ -167,7 +182,7 @@ class LxTools(tk.Tk): primary_info = part break break - + if primary_info: # Parse the geometry: WIDTHxHEIGHT+X+Y geometry = primary_info.split("+") @@ -176,32 +191,33 @@ class LxTools(tk.Tk): primary_height = int(dimensions[1]) primary_x = int(geometry[1]) primary_y = int(geometry[2]) - + # Calculate center position on primary monitor x = primary_x + (primary_width - width) // 2 y = primary_y + (primary_height - height) // 2 - + # Set window geometry window.geometry(f"{width}x{height}+{x}+{y}") return except (subprocess.SubprocessError, ImportError, IndexError, ValueError): pass - + # Final fallback: Use standard Tkinter method screen_width = window.winfo_screenwidth() screen_height = window.winfo_screenheight() - + # Try to make an educated guess for multi-monitor setups # If screen width is much larger than height, assume multiple monitors side by side - if screen_width > screen_height * 1.8: # Heuristic for detecting multiple monitors + if ( + screen_width > screen_height * 1.8 + ): # Heuristic for detecting multiple monitors # Assume primary monitor is on the left half screen_width = screen_width // 2 - + x = (screen_width - width) // 2 y = (screen_height - height) // 2 window.geometry(f"{width}x{height}+{x}+{y}") - @staticmethod def get_file_name(path: Path, i: int = 5) -> List[str]: """ @@ -253,8 +269,14 @@ class LxTools(tk.Tk): Path.unlink(file) @staticmethod - def msg_window(image_path: Path, image_path2: Path, w_title: str, w_txt: str, txt2: Optional[str] = None, - com: Optional[str] = None) -> None: + def msg_window( + image_path: Path, + image_path2: Path, + w_title: str, + w_txt: str, + txt2: Optional[str] = None, + com: Optional[str] = None, + ) -> None: """ Creates message windows @@ -269,29 +291,44 @@ class LxTools(tk.Tk): msg.resizable(width=False, height=False) msg.title(w_title) msg.configure(pady=15, padx=15) - msg.img = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_info"]) - msg.i_window = tk.Label(msg, image=msg.img) + + # Lade das erste Bild für das Fenster + try: + msg.img = tk.PhotoImage(file=image_path) + msg.i_window = tk.Label(msg, image=msg.img) + except Exception as e: + print(f"Fehler beim Laden des Fensterbildes: {e}") + msg.i_window = tk.Label(msg, text="Bild nicht gefunden") label: tk.Label = tk.Label(msg, text=w_txt) - label.grid(column=1, row=0) if txt2 is not None and com is not None: label.config(font=("Ubuntu", 11), padx=15, justify="left") msg.i_window.grid(column=0, row=0, sticky="nw") - button2: ttk.Button = ttk.Button(msg, text=f"{txt2}", command=com, padding=4) + button2: ttk.Button = ttk.Button( + msg, text=f"{txt2}", command=com, padding=4 + ) button2.grid(column=0, row=1, sticky="e", columnspan=2) - button: ttk.Button = ttk.Button(msg, text="OK", command=msg.destroy, padding=4) + button: ttk.Button = ttk.Button( + msg, text="OK", command=msg.destroy, padding=4 + ) button.grid(column=0, row=1, sticky="w", columnspan=2) - else: label.config(font=("Ubuntu", 11), padx=15) msg.i_window.grid(column=0, row=0) - button: ttk.Button = ttk.Button(msg, text="OK", command=msg.destroy, padding=4) + button: ttk.Button = ttk.Button( + msg, text="OK", command=msg.destroy, padding=4 + ) button.grid(column=0, columnspan=2, row=1) - AppConfig.IMAGE_PATHS["icon_vpn"]: tk.PhotoImage = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_vpn"]) - msg.iconphoto(True, AppConfig.IMAGE_PATHS["icon_vpn"]) + # Lade das Icon für das Fenster + try: + icon = tk.PhotoImage(file=image_path2) + msg.iconphoto(True, icon) + except Exception as e: + print(f"Fehler beim Laden des Fenstericons: {e}") + msg.columnconfigure(0, weight=1) msg.rowconfigure(0, weight=1) msg.winfo_toplevel() @@ -317,15 +354,22 @@ class LxTools(tk.Tk): NoReturn since the function either exits the program or continues execution """ - signals_to_names_dict: Dict[int, str] = dict((getattr(signal, n), n) for n in dir(signal) - if n.startswith("SIG") and "_" not in n) + signals_to_names_dict: Dict[int, str] = dict( + (getattr(signal, n), n) + for n in dir(signal) + if n.startswith("SIG") and "_" not in n + ) - signal_name: str = signals_to_names_dict.get(signum, f"Unnamed signal: {signum}") + signal_name: str = signals_to_names_dict.get( + signum, f"Unnamed signal: {signum}" + ) # End program for certain signals, report to others only reception if signum in (signal.SIGINT, signal.SIGTERM): exit_code: int = 1 - print(f"\nSignal {signal_name} {signum} received. => Aborting with exit code {exit_code}.") + print( + f"\nSignal {signal_name} {signum} received. => Aborting with exit code {exit_code}." + ) LxTools.clean_files(file_path, file) print("Breakdown by user...") sys.exit(exit_code) @@ -343,7 +387,8 @@ class LxTools(tk.Tk): class Tunnel: """ Class of Methods for Wire-Py - """ + """ + @classmethod def con_to_dict(cls, file: TextIO) -> Tuple[str, str, str, Optional[str]]: """ @@ -394,7 +439,11 @@ class Tunnel: """ Shows the Active Tunnel """ - active = (os.popen('nmcli con show --active | grep -iPo "(.*)(wireguard)"').read().split()) + active = ( + os.popen('nmcli con show --active | grep -iPo "(.*)(wireguard)"') + .read() + .split() + ) if not active: active = "" else: @@ -413,8 +462,14 @@ class Tunnel: return wg_s @staticmethod - def export(image_path: Path = None, image_path2: Path = None, image_path3: Path = None, image_path4: Path = None, - title: Dict = None, window_msg: Dict = None) -> None: + def export( + image_path: Path = None, + image_path2: Path = None, + image_path3: Path = None, + image_path4: Path = None, + title: Dict = None, + window_msg: Dict = None, + ) -> None: """ This will export the tunnels. A zipfile with the current date and time is created @@ -439,15 +494,30 @@ class Tunnel: with zipfile.ZipFile(f"{wg_tar}.zip", "r") as zf: if len(zf.namelist()) != 0: - LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_vpn"], Msg.STR["exp_succ"], Msg.STR["exp_in_home"]) + LxTools.msg_window( + AppConfig.IMAGE_PATHS["icon_info"], + AppConfig.IMAGE_PATHS["icon_vpn"], + Msg.STR["exp_succ"], + Msg.STR["exp_in_home"], + ) else: - LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_error"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["exp_err"], Msg.STR["exp_try"]) + LxTools.msg_window( + AppConfig.IMAGE_PATHS["icon_error"], + AppConfig.IMAGE_PATHS["icon_msg"], + Msg.STR["exp_err"], + Msg.STR["exp_try"], + ) else: - LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["sel_tl"], Msg.STR["tl_first"]) + LxTools.msg_window( + AppConfig.IMAGE_PATHS["icon_info"], + AppConfig.IMAGE_PATHS["icon_msg"], + Msg.STR["sel_tl"], + Msg.STR["tl_first"], + ) except TypeError: pass @@ -459,15 +529,16 @@ class ConfigManager: Universal class for managing configuration files with caching. Can be reused in different projects. """ + _config = None _config_file = None - + @classmethod def init(cls, config_file): """Initial the Configmanager with the given config file""" cls._config_file = config_file cls._config = None # Reset the cache - + @classmethod def load(cls): """Load the config file and return the config as dict""" @@ -475,44 +546,45 @@ class ConfigManager: try: lines = Path(cls._config_file).read_text(encoding="utf-8").splitlines() cls._config = { - 'updates': lines[1].strip(), - 'theme': lines[3].strip(), - 'tooltips': lines[5].strip() == "True", # is converted here to boolean!!! - 'autostart': lines[7].strip() if len(lines) > 7 else 'off' + "updates": lines[1].strip(), + "theme": lines[3].strip(), + "tooltips": lines[5].strip() + == "True", # is converted here to boolean!!! + "autostart": lines[7].strip() if len(lines) > 7 else "off", } except (IndexError, FileNotFoundError): # DeDefault values in case of error cls._config = { - 'updates': 'on', - 'theme': 'light', - 'tooltips': "True", # Default Value as string ! - 'autostart': 'off' + "updates": "on", + "theme": "light", + "tooltips": "True", # Default Value as string ! + "autostart": "off", } return cls._config - + @classmethod def save(cls): """Save the config to the config file""" if cls._config: lines = [ - '# Configuration\n', + "# Configuration\n", f"{cls._config['updates']}\n", - '# Theme\n', + "# Theme\n", f"{cls._config['theme']}\n", - '# Tooltips\n', + "# Tooltips\n", f"{str(cls._config['tooltips'])}\n", - '# Autostart\n', - f"{cls._config['autostart']}\n" + "# Autostart\n", + f"{cls._config['autostart']}\n", ] - Path(cls._config_file).write_text(''.join(lines), encoding="utf-8") - + Path(cls._config_file).write_text("".join(lines), encoding="utf-8") + @classmethod def set(cls, key, value): """Sets a configuration value and saves the change""" cls.load() cls._config[key] = value cls.save() - + @classmethod def get(cls, key, default=None): """Returns a configuration value""" @@ -545,42 +617,48 @@ class GiteaUpdate: update_api_url: Update API URL version: Current version update_setting: Update setting from ConfigManager (on/off) - + Returns: New version or status message """ # If updates are disabled, return immediately if update_setting != "on": return "False" - + try: response: requests.Response = requests.get(update_api_url, timeout=10) response.raise_for_status() # Raise exception for HTTP errors - + response_data = response.json() if not response_data: return "No Updates" - + latest_version = response_data[0].get("tag_name") if not latest_version: return "Invalid API Response" - + # Compare versions (strip 'v. ' prefix if present) current_version = version[3:] if version.startswith("v. ") else version - + if current_version != latest_version: return latest_version else: return "No Updates" - + except requests.exceptions.RequestException: return "No Internet Connection!" except (ValueError, KeyError, IndexError): return "Invalid API Response" @staticmethod - def download(urld: str, res: str, image_path: Path = None, image_path2: Path = None, image_path3: Path = None, - image_path4: Path = None) -> None: + def download( + urld: str, + res: str, + image_path: Path = None, + image_path2: Path = None, + image_path3: Path = None, + image_path4: Path = None, + ) -> None: """ Downloads new version of wirepy @@ -600,35 +678,57 @@ class GiteaUpdate: wt: str = _("Download Successful") msg_t: str = _("Your zip file is in home directory") - LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_vpn"], wt, msg_t) + LxTools.msg_window( + AppConfig.IMAGE_PATHS["icon_info"], + AppConfig.IMAGE_PATHS["icon_vpn"], + wt, + msg_t, + ) else: wt: str = _("Download error") msg_t: str = _("Download failed! Please try again") - LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_error"], AppConfig.IMAGE_PATHS["icon_msg"], wt, msg_t) + LxTools.msg_window( + AppConfig.IMAGE_PATHS["icon_error"], + AppConfig.IMAGE_PATHS["icon_msg"], + wt, + msg_t, + ) except subprocess.CalledProcessError: wt: str = _("Download error") msg_t: str = _("Download failed! No internet connection!") - LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_error"], AppConfig.IMAGE_PATHS["icon_msg"], wt, msg_t) + LxTools.msg_window( + AppConfig.IMAGE_PATHS["icon_error"], + AppConfig.IMAGE_PATHS["icon_msg"], + wt, + msg_t, + ) -class Tooltip(): +class Tooltip: """Class for Tooltip from common_tools.py import Tooltip example: Tooltip(label, "Show tooltip on label") example: Tooltip(button, "Show tooltip on button") example: Tooltip(widget, "Text", state_var=tk.BooleanVar()) example: Tooltip(widget, "Text", state_var=tk.BooleanVar(), x_offset=20, y_offset=10) - + info: label and button are parent widgets. NOTE: When using with state_var, pass the tk.BooleanVar object directly, NOT its value. For example: use state_var=my_bool_var, NOT state_var=my_bool_var.get() """ - def __init__(self, widget: Any, text: str, state_var: Optional[tk.BooleanVar] = None, - x_offset: int = 65, y_offset: int = 40) -> None: + + def __init__( + self, + widget: Any, + text: str, + state_var: Optional[tk.BooleanVar] = None, + x_offset: int = 65, + y_offset: int = 40, + ) -> None: """Tooltip Class""" self.widget: Any = widget self.text: str = text @@ -636,55 +736,59 @@ class Tooltip(): self.state_var = state_var self.x_offset = x_offset self.y_offset = y_offset - + # Initial binding based on current state self.update_bindings() - + # Add trace to the state_var if provided if self.state_var is not None: self.state_var.trace_add("write", self.update_bindings) - + def update_bindings(self, *args) -> None: """Updates the bindings based on the current state""" # Remove existing bindings first self.widget.unbind("") self.widget.unbind("") - + # Add new bindings if tooltips are enabled if self.state_var is None or self.state_var.get(): self.widget.bind("", self.show_tooltip) self.widget.bind("", self.hide_tooltip) - + def show_tooltip(self, event: Optional[Any] = None) -> None: """Shows the tooltip""" if self.tooltip_window or not self.text: return - + x: int y: int cx: int cy: int - + x, y, cx, cy = self.widget.bbox("insert") x += self.widget.winfo_rootx() + self.x_offset y += self.widget.winfo_rooty() + self.y_offset - + self.tooltip_window = tw = tk.Toplevel(self.widget) tw.wm_overrideredirect(True) tw.wm_geometry(f"+{x}+{y}") - + label: tk.Label = tk.Label( - tw, text=self.text, background="lightgreen", foreground="black", - relief="solid", borderwidth=1, padx=5, pady=5 + tw, + text=self.text, + background="lightgreen", + foreground="black", + relief="solid", + borderwidth=1, + padx=5, + pady=5, ) label.grid() - + self.tooltip_window.after(2200, lambda: tw.destroy()) - + def hide_tooltip(self, event: Optional[Any] = None) -> None: """Hides the tooltip""" if self.tooltip_window: self.tooltip_window.destroy() self.tooltip_window = None - - \ No newline at end of file diff --git a/manage_tunnel.py b/manage_tunnel.py deleted file mode 100644 index 7199260..0000000 --- a/manage_tunnel.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/python3 -from pathlib import Path -from subprocess import check_call -from tkinter import filedialog, ttk -from common_tools import Create, LxTools -from wp_app_config import AppConfig, Msg -import gettext -import locale -import os -import shutil -import subprocess -from typing import Optional, Dict, Any, NoReturn, TextIO, Tuple, List -# Translate -_ = AppConfig.setup_translations() - diff --git a/ssl_decrypt.py b/ssl_decrypt.py index 66c5902..0587e7f 100755 --- a/ssl_decrypt.py +++ b/ssl_decrypt.py @@ -12,11 +12,24 @@ uname: Path = Path("/tmp/.log_user") log_name = Path(uname).read_text(encoding="utf-8") keyfile: Path = Path(f"/home/{log_name}/.config/wire_py/pbwgk.pem") -#PKEYFILE: Path = "/usr/local/etc/ssl/pwgk.pem" + +# PKEYFILE: Path = "/usr/local/etc/ssl/pwgk.pem" if not keyfile.is_file(): - check_call(["openssl", "rsa", "-in", AppConfig.SYSTEM_PATHS["pkey_path"], "-out", keyfile, "-outform", "PEM", "-pubout"]) + check_call( + [ + "openssl", + "rsa", + "-in", + AppConfig.SYSTEM_PATHS["pkey_path"], + "-out", + keyfile, + "-outform", + "PEM", + "-pubout", + ] + ) shutil.chown(keyfile, 1000, 1000) AppConfig.TEMP_DIR2 = f"/home/{log_name}/.config/wire_py/" @@ -29,6 +42,17 @@ if os.path.exists(f"{AppConfig.TEMP_DIR2}pbwgk.pem"): for detunnels in detl: tlname2 = f"{detunnels[:-4]}.conf" extpath = f"{AppConfig.TEMP_DIR}/{tlname2}" - check_call(["openssl", "pkeyutl", "-decrypt", "-inkey", AppConfig.SYSTEM_PATHS["pkey_path"], "-in", detunnels, - "-out", extpath]) + check_call( + [ + "openssl", + "pkeyutl", + "-decrypt", + "-inkey", + AppConfig.SYSTEM_PATHS["pkey_path"], + "-in", + detunnels, + "-out", + extpath, + ] + ) shutil.chown(extpath, 1000, 1000) diff --git a/ssl_encrypt.py b/ssl_encrypt.py index 9710ece..f924b26 100755 --- a/ssl_encrypt.py +++ b/ssl_encrypt.py @@ -8,15 +8,26 @@ from subprocess import check_call from common_tools import LxTools from wp_app_config import AppConfig -#uname: Path = Path("/tmp/.log_user") -#log_name = AppConfig.USER_FILE.read_text(encoding="utf-8") - -keyfile: Path = Path(f"/home/{AppConfig.USER_FILE.read_text(encoding="utf-8")}/.config/wire_py/pbwgk.pem") +keyfile: Path = Path( + f"/home/{AppConfig.USER_FILE.read_text(encoding="utf-8")}/.config/wire_py/pbwgk.pem" +) if not keyfile.is_file(): - check_call(["openssl", "rsa", "-in", AppConfig.SYSTEM_PATHS["pkey_path"], "-out", keyfile, "-outform", "PEM", "-pubout"]) + check_call( + [ + "openssl", + "rsa", + "-in", + AppConfig.SYSTEM_PATHS["pkey_path"], + "-out", + keyfile, + "-outform", + "PEM", + "-pubout", + ] + ) shutil.chown(keyfile, 1000, 1000) if AppConfig.TEMP_DIR.exists(): @@ -28,8 +39,20 @@ if not keyfile.is_file(): for tunnels in tl: sourcetl: str = f"{AppConfig.TEMP_DIR}/{tunnels}" tlname: str = f"{CRYPTFILES}{tunnels[:-5]}.dat" - check_call(["openssl", "pkeyutl", "-encrypt", "-inkey", keyfile, "-pubin", "-in", sourcetl, "-out", - tlname,]) + check_call( + [ + "openssl", + "pkeyutl", + "-encrypt", + "-inkey", + keyfile, + "-pubin", + "-in", + sourcetl, + "-out", + tlname, + ] + ) else: @@ -42,5 +65,17 @@ else: for tunnels in tl: sourcetl: str = f"{AppConfig.TEMP_DIR}/{tunnels}" tlname: str = f"{CRYPTFILES}{tunnels[:-5]}.dat" - check_call(["openssl", "pkeyutl", "-encrypt", "-inkey", keyfile, "-pubin", "-in", sourcetl, "-out", - tlname]) + check_call( + [ + "openssl", + "pkeyutl", + "-encrypt", + "-inkey", + keyfile, + "-pubin", + "-in", + sourcetl, + "-out", + tlname, + ] + ) diff --git a/start_wg.py b/start_wg.py index decf670..0262a49 100755 --- a/start_wg.py +++ b/start_wg.py @@ -1,7 +1,8 @@ #!/usr/bin/python3 """ - This script belongs to wirepy and is for the auto start of the tunnel + This script belongs to wirepy and is for the auto start of the tunnel """ + from pathlib import Path from subprocess import check_call diff --git a/wirepy.py b/wirepy.py index 9e7114d..0231db8 100755 --- a/wirepy.py +++ b/wirepy.py @@ -2,8 +2,6 @@ """ this script is a simple GUI for managing Wireguard Tunnels """ -import gettext -import locale import os import shutil import subprocess @@ -14,7 +12,15 @@ from pathlib import Path from subprocess import check_call from tkinter import TclError, filedialog, ttk -from common_tools import (ConfigManager, ThemeManager, Create, GiteaUpdate, Tunnel, Tooltip, LxTools) +from common_tools import ( + ConfigManager, + ThemeManager, + Create, + GiteaUpdate, + Tunnel, + Tooltip, + LxTools, +) from wp_app_config import AppConfig, Msg LxTools.uos() @@ -22,26 +28,30 @@ Create.dir_and_files() Create.make_dir() Create.decrypt() + class Wirepy(tk.Tk): """ Class Wirepy this is the Main Window of wirepy """ + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Hide the window initially self.withdraw() - + self.my_tool_tip = None self.x_width = AppConfig.UI_CONFIG["window_size"][0] self.y_height = AppConfig.UI_CONFIG["window_size"][1] - + # Set the window size self.geometry(f"{self.x_width}x{self.y_height}") - self.resizable(AppConfig.UI_CONFIG["resizable_window"][0], - AppConfig.UI_CONFIG["resizable_window"][1]) + self.resizable( + AppConfig.UI_CONFIG["resizable_window"][0], + AppConfig.UI_CONFIG["resizable_window"][1], + ) self.title(AppConfig.UI_CONFIG["window_title"]) - + self.columnconfigure(0, weight=1) self.rowconfigure(0, weight=1) self.tk.call("source", f"{AppConfig.SYSTEM_PATHS['tcl_path']}/water.tcl") @@ -57,10 +67,10 @@ class Wirepy(tk.Tk): # Add the widgets FrameWidgets(self).grid() - + # Center the window on the primary monitor LxTools.center_window_cross_platform(self, self.x_width, self.y_height) - + # Now show the window after it has been positioned self.after(10, self.deiconify) @@ -69,9 +79,10 @@ class FrameWidgets(ttk.Frame): """ ttk frame class for better structure """ + def __init__(self, container, **kwargs): super().__init__(container, **kwargs) - + self.lb_tunnel = None self.btn_stst = None self.endpoint = None @@ -100,13 +111,16 @@ class FrameWidgets(ttk.Frame): else: # If it's a string or something else self.tooltip_state.set(str(state) == "True") - - self.tooltip_label = tk.StringVar() # StringVar-Variable for tooltip label for view Disabled/Enabled - self.tooltip_update_label() - self.update_label = tk.StringVar() # StringVar-Variable for update label - self.update_tooltip = tk.StringVar() # StringVar-Variable for update tooltip please not remove! - self.update_foreground = tk.StringVar(value="red") + self.tooltip_label = ( + tk.StringVar() + ) # StringVar-Variable for tooltip label for view Disabled/Enabled + self.tooltip_update_label() + self.update_label = tk.StringVar() # StringVar-Variable for update label + self.update_tooltip = ( + tk.StringVar() + ) # StringVar-Variable for update tooltip please not remove! + self.update_foreground = tk.StringVar(value="red") # Frame for Menu self.menu_frame = ttk.Frame(self) @@ -118,7 +132,9 @@ class FrameWidgets(ttk.Frame): 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: {AppConfig.VERSION[2:]}", self.tooltip_state) + Tooltip( + self.version_lb, f"Version: {AppConfig.VERSION[2:]}", self.tooltip_state + ) self.options_btn = ttk.Menubutton(self.menu_frame, text=_("Options")) self.options_btn.grid(column=1, columnspan=1, row=0) @@ -128,27 +144,37 @@ class FrameWidgets(ttk.Frame): self.set_update = tk.IntVar() 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(self.set_update.get()), variable=self.set_update) + self.settings.add_checkbutton( + label=_("Disable Updates"), + command=lambda: self.update_setting(self.set_update.get()), + variable=self.set_update, + ) self.updates_lb = ttk.Label(self.menu_frame, textvariable=self.update_label) self.updates_lb.grid(column=4, columnspan=3, row=0, padx=10) self.updates_lb.grid_remove() self.update_label.trace_add("write", self.update_label_display) self.update_foreground.trace_add("write", self.update_label_display) - res = GiteaUpdate.api_down(AppConfig.UPDATE_URL, AppConfig.VERSION, ConfigManager.get("updates")) + res = GiteaUpdate.api_down( + AppConfig.UPDATE_URL, AppConfig.VERSION, ConfigManager.get("updates") + ) self.update_ui_for_update(res) # Tooltip Menu - self.settings.add_command(label=self.tooltip_label.get(), command=self.tooltips_toggle) - # Label show dark or light + self.settings.add_command( + label=self.tooltip_label.get(), command=self.tooltips_toggle + ) + # Label show dark or light self.theme_label = tk.StringVar() - self.update_theme_label() - self.settings.add_command(label=self.theme_label.get(), command=self.on_theme_toggle) + self.update_theme_label() + self.settings.add_command( + label=self.theme_label.get(), command=self.on_theme_toggle + ) # About BTN Menu / Label self.about_btn = ttk.Button( - self.menu_frame, text=_("About"), style="Toolbutton", command=self.about) + 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) @@ -172,7 +198,9 @@ class FrameWidgets(ttk.Frame): # 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) + 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) @@ -205,7 +233,9 @@ class FrameWidgets(ttk.Frame): 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 = 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) @@ -230,14 +260,24 @@ class FrameWidgets(ttk.Frame): 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 = 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, Msg.TTIP["import_tl"], self.tooltip_state) # 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 = 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: @@ -246,12 +286,20 @@ class FrameWidgets(ttk.Frame): Tooltip(self.btn_tr, Msg.TTIP["trash_tl"], self.tooltip_state) # 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 = 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: @@ -266,13 +314,31 @@ class FrameWidgets(ttk.Frame): self.lb_rename.config(state="disable") if self.l_box.size() != 0: - Tooltip(self.lb_rename, Msg.TTIP["rename_tl"], self.tooltip_state, x_offset= -120, y_offset=-70) + Tooltip( + self.lb_rename, + Msg.TTIP["rename_tl"], + self.tooltip_state, + x_offset=-120, + y_offset=-70, + ) else: - Tooltip(self.lb_rename, Msg.TTIP["rename_tl_info"], self.tooltip_state, x_offset=-180, y_offset=-50) + Tooltip( + self.lb_rename, + Msg.TTIP["rename_tl_info"], + self.tooltip_state, + x_offset=-180, + y_offset=-50, + ) # 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 = 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 @@ -281,22 +347,46 @@ class FrameWidgets(ttk.Frame): 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 = 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 = 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"], self.tooltip_state, x_offset=-10, y_offset=-40) + Tooltip( + self.wg_autostart, + Msg.TTIP["autostart"], + self.tooltip_state, + x_offset=-10, + y_offset=-40, + ) if self.l_box.size() == 0: - Tooltip(self.wg_autostart, Msg.TTIP["autostart_info"], self.tooltip_state, x_offset=30, y_offset=-50) + Tooltip( + self.wg_autostart, + Msg.TTIP["autostart_info"], + self.tooltip_state, + x_offset=30, + y_offset=-50, + ) else: - Tooltip(self.wg_autostart, Msg.TTIP["autostart"], self.tooltip_state, x_offset=-10, y_offset=-40) + Tooltip( + self.wg_autostart, + Msg.TTIP["autostart"], + self.tooltip_state, + x_offset=-10, + y_offset=-40, + ) self.on_off() @@ -316,10 +406,10 @@ class FrameWidgets(ttk.Frame): def update_ui_for_update(self, res): """Update UI elements based on update check result""" # First, remove the update button if it exists to avoid conflicts - if hasattr(self, 'update_btn'): + if hasattr(self, "update_btn"): self.update_btn.grid_forget() - delattr(self, 'update_btn') - + delattr(self, "update_btn") + if res == "False": self.set_update.set(value=1) self.update_label.set(_("Update search off")) @@ -328,44 +418,50 @@ class FrameWidgets(ttk.Frame): self.update_foreground.set("") # Set tooltip for the label Tooltip(self.updates_lb, self.update_tooltip.get(), self.tooltip_state) - + elif res == "No Internet Connection!": self.update_label.set(_("No Server Connection!")) self.update_foreground.set("red") # Set tooltip for "No Server Connection" - Tooltip(self.updates_lb, _("Could not connect to update server"), self.tooltip_state) - + Tooltip( + self.updates_lb, + _("Could not connect to update server"), + self.tooltip_state, + ) + elif res == "No Updates": self.update_label.set(_("No Updates")) self.update_tooltip.set(_("Congratulations! Wire-Py is up to date")) self.update_foreground.set("") # Set tooltip for the label Tooltip(self.updates_lb, self.update_tooltip.get(), self.tooltip_state) - + else: self.set_update.set(value=0) update_text = f"Update {res} {_('available!')}" # Clear the label text since we'll show the button instead self.update_label.set("") - + # Create the update button self.update_btn = ttk.Menubutton(self.menu_frame, text=update_text) self.update_btn.grid(column=4, columnspan=3, row=0, padx=0) - Tooltip(self.update_btn, _("Click to download new version"), self.tooltip_state) - + Tooltip( + self.update_btn, _("Click to download new version"), self.tooltip_state + ) + 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"{AppConfig.DOWNLOAD_URL}/{res}.zip", - res, + res, AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_vpn"], AppConfig.IMAGE_PATHS["icon_error"], - AppConfig.IMAGE_PATHS["icon_msg"] - ) + AppConfig.IMAGE_PATHS["icon_msg"], + ), ) @staticmethod @@ -373,15 +469,26 @@ class FrameWidgets(ttk.Frame): """ 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") + 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, + ) - LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_vpn"], AppConfig.IMAGE_PATHS["icon_vpn"], _("Info"), msg_t, _("Go to Wire-Py git"), link_btn) def update_setting(self, update_res) -> None: """write off or on in file Args: @@ -398,14 +505,16 @@ class FrameWidgets(ttk.Frame): # When enabling updates, we need to actually check for updates try: # Force a fresh check by passing "on" as the update setting - res = GiteaUpdate.api_down(AppConfig.UPDATE_URL, AppConfig.VERSION, "on") - + res = GiteaUpdate.api_down( + AppConfig.UPDATE_URL, AppConfig.VERSION, "on" + ) + # Make sure UI is updated regardless of previous state - if hasattr(self, 'update_btn'): + if hasattr(self, "update_btn"): self.update_btn.grid_forget() - if hasattr(self, 'updates_lb'): + if hasattr(self, "updates_lb"): self.updates_lb.grid_forget() - + # Now update the UI with the fresh result self.update_ui_for_update(res) except Exception as e: @@ -431,10 +540,10 @@ class FrameWidgets(ttk.Frame): ConfigManager.set("tooltips", str(new_bool_state)) # Update the tooltip_state variable for immediate effect self.tooltip_state.set(new_bool_state) - + # Update the menu label self.tooltip_update_label() - + # Update the menu entry - find the correct index # This assumes it's the third item (index 2) in your menu self.settings.entryconfigure(1, label=self.tooltip_label.get()) @@ -461,8 +570,12 @@ class FrameWidgets(ttk.Frame): """ 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 = 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) @@ -491,12 +604,16 @@ class FrameWidgets(ttk.Frame): """ View activ Tunnel in the color green or yellow """ - if ConfigManager.get("theme") == "light": - - self.lb_tunnel = ttk.Label(self, textvariable=self.str_var, foreground="green") + if ConfigManager.get("theme") == "light": + + 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 = 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) @@ -505,8 +622,12 @@ class FrameWidgets(ttk.Frame): """ 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 = 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.tooltip_state) @@ -525,58 +646,91 @@ class FrameWidgets(ttk.Frame): Create.dir_and_files() try: filepath = filedialog.askopenfilename( - initialdir=f"{Path.home()}", + initialdir=f"{Path.home()}", title=_("Select Wireguard config File"), - filetypes=[(_("WG config files"), "*.conf")] + 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: + + 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"]) + 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: + 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:] + 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) + + 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) + + 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) @@ -584,23 +738,45 @@ class FrameWidgets(ttk.Frame): 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.tooltip_state, x_offset=-10, y_offset=-40) + + Tooltip( + self.wg_autostart, + Msg.TTIP["autostart"], + self.tooltip_state, + x_offset=-10, + y_offset=-40, + ) Tooltip(self.btn_tr, Msg.TTIP["trash_tl"], self.tooltip_state) Tooltip(self.btn_exp, Msg.TTIP["export_tl"], self.tooltip_state) - Tooltip(self.btn_rename, Msg.TTIP["rename_tl"], self.tooltip_state) - + Tooltip( + self.btn_rename, Msg.TTIP["rename_tl"], self.tooltip_state + ) + 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"]) + 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"]) + 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) @@ -618,15 +794,16 @@ class FrameWidgets(ttk.Frame): 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: + 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()): + 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) @@ -637,7 +814,9 @@ class FrameWidgets(ttk.Frame): 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: + 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) @@ -650,8 +829,13 @@ class FrameWidgets(ttk.Frame): if self.l_box.size() == 0: self.wg_autostart.configure(state="disabled") self.lb_rename.configure(state="disabled") - Tooltip(self.wg_autostart, Msg.TTIP["autostart_info"] - , self.tooltip_state, x_offset=30, y_offset=-50) + Tooltip( + self.wg_autostart, + Msg.TTIP["autostart_info"], + self.tooltip_state, + x_offset=30, + y_offset=-50, + ) Tooltip(self.btn_exp, Msg.TTIP["export_tl_info"], self.tooltip_state) Tooltip(self.btn_stst, Msg.TTIP["empty_list"], self.tooltip_state) @@ -668,11 +852,21 @@ class FrameWidgets(ttk.Frame): 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"]) + 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"]) + 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: """ @@ -691,7 +885,11 @@ class FrameWidgets(ttk.Frame): 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) + 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.") @@ -707,7 +905,12 @@ class FrameWidgets(ttk.Frame): 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 = 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) @@ -727,9 +930,15 @@ class FrameWidgets(ttk.Frame): 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") + 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) @@ -737,9 +946,15 @@ class FrameWidgets(ttk.Frame): 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 = ( + 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") + Path(AppConfig.SETTINGS_FILE).write_text( + "".join(lines), encoding="utf-8" + ) except IndexError: self.selected_option.set(1) @@ -755,19 +970,39 @@ class FrameWidgets(ttk.Frame): 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"]) + 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"]) + 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"]) + 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"]) + LxTools.msg_window( + AppConfig.IMAGE_PATHS["icon_info"], + AppConfig.IMAGE_PATHS["icon_msg"], + Msg.STR["ren_err"], + Msg.STR["is_in_use"], + ) else: @@ -776,7 +1011,17 @@ class FrameWidgets(ttk.Frame): 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) + 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) @@ -787,12 +1032,14 @@ class FrameWidgets(ttk.Frame): 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: + 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() @@ -800,14 +1047,19 @@ class FrameWidgets(ttk.Frame): except IndexError: - LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["ren_err"], Msg.STR["sel_list"]) + 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 init_and_report(self, data=None) -> None: """ Displays the value address, DNS and peer in the labels @@ -826,7 +1078,9 @@ class FrameWidgets(ttk.Frame): shows data in the label """ # Address Label - self.address = ttk.Label(self.lb_frame, textvariable=self.add, foreground="#0071ff") + 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)) @@ -836,7 +1090,9 @@ class FrameWidgets(ttk.Frame): self.dns.config(font=("Ubuntu", 9)) # Endpoint Label - self.endpoint = ttk.Label(self.lb_frame2, textvariable=self.enp, foreground="#0071ff") + 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)) @@ -851,7 +1107,7 @@ class FrameWidgets(ttk.Frame): self.handle_connection_state("start", select_tl) else: - + data = self.handle_tunnel_data(self.a) if data: @@ -861,11 +1117,21 @@ class FrameWidgets(ttk.Frame): 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"]) + 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"]) + 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: """ diff --git a/wp_app_config.py b/wp_app_config.py index f0b24d4..52363cb 100644 --- a/wp_app_config.py +++ b/wp_app_config.py @@ -6,9 +6,10 @@ import locale from pathlib import Path from typing import Dict, Any + class AppConfig: """Central configuration class for Wire-Py application""" - + # Localization APP_NAME: str = "wirepy" LOCALE_DIR: Path = Path("/usr/share/locale/") @@ -23,32 +24,30 @@ class AppConfig: SETTINGS_FILE: Path = CONFIG_DIR / "settings" KEYS_FILE: Path = CONFIG_DIR / "keys" AUTOSTART_SERVICE: Path = Path.home() / ".config/systemd/user/wg_start.service" - + # Updates # 1 = 1. Year, 09 = Month of the Year, 2924 = Day and Year of the Year VERSION: str = "v. 2.04.1725" UPDATE_URL: str = "https://git.ilunix.de/api/v1/repos/punix/Wire-Py/releases" DOWNLOAD_URL: str = "https://git.ilunix.de/punix/Wire-Py/archive" - # UI configuration UI_CONFIG: Dict[str, Any] = { "window_title": "Wire-Py", "window_size": (600, 383), "font_family": "Ubuntu", "font_size": 11, - "resizable_window": (False, False) + "resizable_window": (False, False), } - + # System-dependent paths - SYSTEM_PATHS: Dict[str, str]= { + SYSTEM_PATHS: Dict[str, str] = { "ssl_decrypt": "/usr/local/bin/ssl_decrypt.py", "ssl_encrypt": "/usr/local/bin/ssl_encrypt.py", "tcl_path": "/usr/share/TK-Themes", - "pkey_path": "/usr/local/etc/ssl/pwgk.pem" - + "pkey_path": "/usr/local/etc/ssl/pwgk.pem", } - + # Images and icons paths IMAGE_PATHS: Dict[str, str] = { "icon_vpn": "/usr/share/icons/lx-icons/48/wg_vpn.png", @@ -59,16 +58,15 @@ class AppConfig: "icon_start": "/usr/share/icons/lx-icons/48/wg_vpn-start.png", "icon_stop": "/usr/share/icons/lx-icons/48/wg_vpn-stop.png", "icon_info": "/usr/share/icons/lx-icons/64/info.png", - "icon_error": "/usr/share/icons/lx-icons/64/error.png" - - } + "icon_error": "/usr/share/icons/lx-icons/64/error.png", + } @staticmethod def setup_translations() -> gettext.gettext: """ Initialize translations and set the translation function Special method for translating strings in this file - + Returns: The gettext translation function """ @@ -82,14 +80,16 @@ class AppConfig: """Ensures that all required directories exist""" cls.CONFIG_DIR.mkdir(parents=True, exist_ok=True) cls.TEMP_DIR.mkdir(parents=True, exist_ok=True) - + @classmethod def create_default_settings(cls) -> None: """Creates default settings if they don't exist""" if not cls.SETTINGS_FILE.exists(): - content = "\n".join(f"[{k.upper()}]\n{v}" for k, v in cls.DEFAULT_SETTINGS.items()) + content = "\n".join( + f"[{k.upper()}]\n{v}" for k, v in cls.DEFAULT_SETTINGS.items() + ) cls.SETTINGS_FILE.write_text(content) - + @classmethod def get_image_paths(cls) -> Dict[str, Path]: """Returns paths to UI images""" @@ -97,14 +97,14 @@ class AppConfig: "main_icon": cls.SYSTEM_PATHS["image_path"] / "48/wg_vpn.png", "warning": cls.CONFIG_DIR / "images/warning.png", "success": cls.CONFIG_DIR / "images/success.png", - "error": cls.CONFIG_DIR / "images/error.png" + "error": cls.CONFIG_DIR / "images/error.png", } - + @classmethod def get_autostart_content(cls) -> str: """Returns the content for the autostart service file""" - return """[Unit] -Description=Automatic Tunnel Start + + return """[Unit]Description=Automatic Tunnel Start After=network-online.target [Service] @@ -114,29 +114,32 @@ ExecStart=/usr/local/bin/start_wg.py [Install] WantedBy=default.target""" + # here is inizialize the class for translate strrings _ = AppConfig.setup_translations() + class Msg: """ A utility class that provides centralized access to translated message strings. - + This class contains a dictionary of message strings used throughout the Wire-Py application. All strings are prepared for translation using gettext. The short key names make the code more concise while maintaining readability. - + Attributes: STR (dict): A dictionary mapping short keys to translated message strings. Keys are abbreviated for brevity but remain descriptive. - + Usage: Import this class and access messages using the dictionary: `Msg.STR["sel_tl"]` returns the translated "Select tunnel" message. - + Note: Ensure that gettext translation is properly initialized before accessing these strings to ensure correct localization. """ + STR: Dict[str, str] = { # Strings for messages "sel_tl": _("Select tunnel"), @@ -150,31 +153,38 @@ class Msg: "sel_list": _("Please select a tunnel from the list"), "sign_len": _("The new name may contain only 12 characters"), "zero_signs": _("At least one character must be entered"), - "false signs": _("No valid sign. These must not be used.\nBlank, Slash, Backslash and { }\n"), + "false signs": _( + "No valid sign. These must not be used.\nBlank, Slash, Backslash and { }\n" + ), "is_in_use": _("The tunnel is already in use"), - "no_valid_file": _("Oh... no valid Wireguard File!\nPlease select a valid Wireguard File"), - "tl_exist": _("Tunnel already available!\nPlease use another file for import") - - } + "no_valid_file": _( + "Oh... no valid Wireguard File!\nPlease select a valid Wireguard File" + ), + "tl_exist": _("Tunnel already available!\nPlease use another file for import"), + } TTIP: Dict[str, str] = { - #Strings for Tooltips + # Strings for Tooltips "settings": _("Click for Settings"), "import_tl": _("Click to import a Wireguard Tunnel"), "start_tl": _("Click to start selected Wireguard Tunnel"), "empty_list": _("No tunnels to start in the list"), "stop_tl": _("Click to stop selected Wireguard Tunnel"), "del_tl": _("Click to delete selected Wireguard Tunnel"), - "rename_tl": _("To rename a tunnel, you need to\nselect a tunnel from the list"), + "rename_tl": _( + "To rename a tunnel, you need to\nselect a tunnel from the list" + ), "export_tl": _(" Click to export all\nWireguard Tunnel to Zipfile"), "trash_tl": _("Click to delete a Wireguard Tunnel\nSelect from the list!"), "autostart": _("To use the autostart, enable this Checkbox"), - "autostart_info": _("You must have at least one\ntunnel in the list,to use the autostart"), + "autostart_info": _( + "You must have at least one\ntunnel in the list,to use the autostart" + ), "export_tl_info": _("No Tunnels in List for Export"), "start_tl_info": _("Click to start selected Wireguard Tunnel"), "rename_tl_info": _("To rename a tunnel, at least one must be in the list"), "trash_tl_info": _("No tunnels to delete in the list"), - "list_auto_info": _("To use the autostart, a tunnel must be selected from the list"), - "download": _("Click to download new version") - + "list_auto_info": _( + "To use the autostart, a tunnel must be selected from the list" + ), + "download": _("Click to download new version"), } - \ No newline at end of file