Compare commits
	
		
			13 Commits
		
	
	
		
			09-05-2025
			...
			04-06-2025
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| fa0fb7ae31 | |||
| c509fdbd18 | |||
| fd957d9742 | |||
| 4e1c3a8e9c | |||
| 7b8ec10e6c | |||
| 47aa3ac749 | |||
| ddae246d46 | |||
| c8d439d428 | |||
| 2463d63c12 | |||
| 70d973e9d7 | |||
| 54b62fd5d5 | |||
| 794dda346a | |||
| 298a3da73b | 
							
								
								
									
										50
									
								
								Changelog
									
									
									
									
									
								
							
							
						
						@@ -6,12 +6,54 @@ My standard System: Linux Mint 22 Cinnamon
 | 
			
		||||
 - os import in common_tools.py replaced by other methods
 | 
			
		||||
 - If Wire-Py already runs, prevent further start
 | 
			
		||||
 - for loops with lists replaced by List Comprehensions
 | 
			
		||||
 - Replace Download Button with Lx Tools installer
 | 
			
		||||
 | 
			
		||||
   ### Added
 | 
			
		||||
04-06-2025
 | 
			
		||||
02-07-2025
 | 
			
		||||
 | 
			
		||||
- Complete code for faulty f" string configuration dur addicted and fixed
 | 
			
		||||
 | 
			
		||||
- updater replace Downloadbutton with Lx Tools installer
 | 
			
		||||
 | 
			
		||||
- remove logviewer icon 
 | 
			
		||||
 | 
			
		||||
- add settings.png icon for update button
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   ### Added
 | 
			
		||||
27-06-2025
 | 
			
		||||
  
 | 
			
		||||
 - Header added for more modern desing
 | 
			
		||||
 | 
			
		||||
 - Sizes adjust the frames and labels improve
 | 
			
		||||
 | 
			
		||||
 - More modern desing for listbox, Address, Dns and Endpoint
 | 
			
		||||
 | 
			
		||||
 - ui works now better with rename button
 | 
			
		||||
 | 
			
		||||
 - add Image class for manage Images
 | 
			
		||||
 | 
			
		||||
   ### Added
 | 
			
		||||
23-06-2025
 | 
			
		||||
 | 
			
		||||
 - all msg_window with MassageDialog replaced
 | 
			
		||||
 | 
			
		||||
   ### Added
 | 
			
		||||
14-06-2025
 | 
			
		||||
 | 
			
		||||
 - replace msg_window with MassageDialog
 | 
			
		||||
 - vpn stop icon corrected
 | 
			
		||||
 | 
			
		||||
   ### Added
 | 
			
		||||
07-06-2025
 | 
			
		||||
 | 
			
		||||
 - ssl_decrypt now directly checks whether encrypted files are located in the specified directory. 
 | 
			
		||||
 - This method has been removed from common_tools. The path has been adjusted ssl_decrypt likely 
 | 
			
		||||
   had an incorrect path. No files were decrypted. This has been fixed. 
 | 
			
		||||
 - Key generation has been removed from ssl_decrypt, as it is only needed in ssl_encrypt.
 | 
			
		||||
 - Logviewer now in the settings menu. Moduls now as libs in share_libs.
 | 
			
		||||
 - The Lx Tools installer is there to ensure that everything required for the selected app is installed.
 | 
			
		||||
 | 
			
		||||
 - large update in the module were outsourced and can now be used as
 | 
			
		||||
   dependencies to wirepy us other apps from git.ilunix.de
 | 
			
		||||
 | 
			
		||||
   ### Added
 | 
			
		||||
13-04-20255
 | 
			
		||||
@@ -51,12 +93,14 @@ My standard System: Linux Mint 22 Cinnamon
 | 
			
		||||
 - Tunnels are now read from the directory to view them in the list. 
 | 
			
		||||
   To display only own tunnels, and read errors are minimized.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  ### Added
 | 
			
		||||
10-11-2024
 | 
			
		||||
 | 
			
		||||
 - Fix Checkbutton Autostart when first install Wire-Py
 | 
			
		||||
 - Update Translate Files
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  ### Added
 | 
			
		||||
10-11-2024
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								lx-icons/128/Lunix_Tools.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 15 KiB  | 
| 
		 Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 6.9 KiB  | 
| 
		 Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.5 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								lx-icons/128/question_mark.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 7.0 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								lx-icons/128/warning.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 3.5 KiB  | 
| 
		 Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 10 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								lx-icons/16/settings.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 757 B  | 
							
								
								
									
										
											BIN
										
									
								
								lx-icons/16/wg_vpn.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 846 B  | 
							
								
								
									
										
											BIN
										
									
								
								lx-icons/256/Lunix_Tools.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 40 KiB  | 
| 
		 Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 13 KiB  | 
| 
		 Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 9.8 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								lx-icons/256/question_mark.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 12 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								lx-icons/256/warning.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 6.2 KiB  | 
| 
		 Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 20 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								lx-icons/32/Lunix_Tools.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.9 KiB  | 
| 
		 Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 1.3 KiB  | 
| 
		 Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								lx-icons/32/question_mark.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.5 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								lx-icons/32/warning.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 838 B  | 
| 
		 Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 1.9 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								lx-icons/32/wg_vpn.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 5.8 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								lx-icons/48/Lunix_Tools.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 3.5 KiB  | 
| 
		 Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 2.2 KiB  | 
| 
		 Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.9 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								lx-icons/48/question_mark.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 2.3 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								lx-icons/48/warning.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.4 KiB  | 
| 
		 Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 3.1 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								lx-icons/64/Lunix_Tools.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 5.3 KiB  | 
| 
		 Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.2 KiB  | 
| 
		 Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.5 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								lx-icons/64/question_mark.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 3.1 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								lx-icons/64/warning.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.6 KiB  | 
| 
		 Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 4.5 KiB  | 
@@ -5,55 +5,34 @@ from pathlib import Path
 | 
			
		||||
import pwd
 | 
			
		||||
import shutil
 | 
			
		||||
from subprocess import CompletedProcess, run
 | 
			
		||||
from shared_libs.wp_app_config import AppConfig, logging
 | 
			
		||||
from shared_libs.wp_app_config import AppConfig
 | 
			
		||||
from shared_libs.message import MessageDialog
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
parser = argparse.ArgumentParser()
 | 
			
		||||
parser.add_argument("--user", required=True, help="Username of the target file system")
 | 
			
		||||
args = parser.parse_args()
 | 
			
		||||
 | 
			
		||||
_ = AppConfig.setup_translations()
 | 
			
		||||
try:
 | 
			
		||||
    # Retrieve UID and GID
 | 
			
		||||
    user_info = pwd.getpwnam(args.user)
 | 
			
		||||
    uid = user_info.pw_uid  # User ID (e.g., 1000)
 | 
			
		||||
    gid = user_info.pw_gid  # Group ID (e.g., 1000)
 | 
			
		||||
except KeyError:
 | 
			
		||||
    logging.error(f"User '{args.user}' not found.", exc_info=True)
 | 
			
		||||
    MessageDialog(
 | 
			
		||||
        "error", _(f"User '{args.user}' not found."), title="Error decrypt"
 | 
			
		||||
    ).show()
 | 
			
		||||
    exit(1)
 | 
			
		||||
 | 
			
		||||
keyfile: Path = Path(f"/home/{args.user}/.config/wire_py/pbwgk.pem")
 | 
			
		||||
path_of_crypted_tunnel: Path = Path(f"/home/{args.user}/.config/wire_py")
 | 
			
		||||
crypted_tunnel: Path = Path(f"/home/{args.user}/.config/wire_py")
 | 
			
		||||
 | 
			
		||||
if not keyfile.is_file():
 | 
			
		||||
    process: CompletedProcess[str] = run(
 | 
			
		||||
        [
 | 
			
		||||
            "openssl",
 | 
			
		||||
            "rsa",
 | 
			
		||||
            "-in",
 | 
			
		||||
            AppConfig.SYSTEM_PATHS["pkey_path"],
 | 
			
		||||
            "-out",
 | 
			
		||||
            keyfile,
 | 
			
		||||
            "-outform",
 | 
			
		||||
            "PEM",
 | 
			
		||||
            "-pubout",
 | 
			
		||||
        ],
 | 
			
		||||
        capture_output=True,
 | 
			
		||||
        text=True,
 | 
			
		||||
        check=False,
 | 
			
		||||
    )
 | 
			
		||||
if len([str(file) for file in crypted_tunnel.glob("*.dat")]) == 0:
 | 
			
		||||
    pass
 | 
			
		||||
else:
 | 
			
		||||
    crypted_tunnel = [str(file) for file in crypted_tunnel.glob("*.dat")]
 | 
			
		||||
 | 
			
		||||
    if process.returncode == 0:
 | 
			
		||||
        logging.info("Public key generated successfully.", exc_info=True)
 | 
			
		||||
    else:
 | 
			
		||||
        logging.error(
 | 
			
		||||
            f"Error with the following code... {process.returncode}", exc_info=True
 | 
			
		||||
        )
 | 
			
		||||
    shutil.chown(keyfile, uid, gid)
 | 
			
		||||
 | 
			
		||||
if AppConfig.PUBLICKEY.exists():
 | 
			
		||||
 | 
			
		||||
    crypted__tunnel = [str(file) for file in path_of_crypted_tunnel.glob("*.dat")]
 | 
			
		||||
 | 
			
		||||
    for tunnel_path in crypted__tunnel:
 | 
			
		||||
    for tunnel_path in crypted_tunnel:
 | 
			
		||||
 | 
			
		||||
        base_name = Path(tunnel_path).stem
 | 
			
		||||
 | 
			
		||||
@@ -71,14 +50,16 @@ if AppConfig.PUBLICKEY.exists():
 | 
			
		||||
            ],
 | 
			
		||||
            capture_output=True,
 | 
			
		||||
            text=True,
 | 
			
		||||
            check=False,
 | 
			
		||||
            check=True,
 | 
			
		||||
        )
 | 
			
		||||
        shutil.chown(f"{AppConfig.TEMP_DIR}/{base_name}.conf", uid, gid)
 | 
			
		||||
        logging.info(f"Processing of the file: {tunnel_path}", exc_info=True)
 | 
			
		||||
 | 
			
		||||
        # Output from Openssl Error
 | 
			
		||||
        if process.stderr:
 | 
			
		||||
            logging.error(
 | 
			
		||||
                f"{process.stderr} Error by [{tunnel_path}] Code: {process.returncode}",
 | 
			
		||||
                exc_info=True,
 | 
			
		||||
            )
 | 
			
		||||
            MessageDialog(
 | 
			
		||||
                "error",
 | 
			
		||||
                _(
 | 
			
		||||
                    f"{process.stderr} Error by [{tunnel_path}] Code: {process.returncode}"
 | 
			
		||||
                ),
 | 
			
		||||
                title="Error decrypt",
 | 
			
		||||
            ).show()
 | 
			
		||||
 
 | 
			
		||||
@@ -2,16 +2,19 @@
 | 
			
		||||
""" This Script encrypt Wireguardfiles for Wirepy users for more Security """
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
import logging
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
import pwd
 | 
			
		||||
import shutil
 | 
			
		||||
from subprocess import CompletedProcess, run
 | 
			
		||||
from shared_libs.wp_app_config import AppConfig, logging
 | 
			
		||||
from shared_libs.wp_app_config import AppConfig
 | 
			
		||||
from shared_libs.common_tools import LogConfig
 | 
			
		||||
 | 
			
		||||
parser = argparse.ArgumentParser()
 | 
			
		||||
parser.add_argument("--user", required=True, help="Username of the target file system")
 | 
			
		||||
args = parser.parse_args()
 | 
			
		||||
 | 
			
		||||
LogConfig.logger(f"/home/{args.user}/.local/share/lxlogs/wirepy.log")
 | 
			
		||||
try:
 | 
			
		||||
    # Retrieve UID and GID
 | 
			
		||||
    user_info = pwd.getpwnam(args.user)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										32
									
								
								tunnel.py
									
									
									
									
									
								
							
							
						
						@@ -9,6 +9,7 @@ from subprocess import run, CompletedProcess
 | 
			
		||||
import secrets
 | 
			
		||||
from shared_libs.wp_app_config import AppConfig, Msg
 | 
			
		||||
from shared_libs.common_tools import LxTools, CryptoUtil
 | 
			
		||||
from shared_libs.message import MessageDialog
 | 
			
		||||
 | 
			
		||||
# Translate
 | 
			
		||||
_ = AppConfig.setup_translations()
 | 
			
		||||
@@ -172,12 +173,8 @@ class Tunnel:
 | 
			
		||||
            CryptoUtil.decrypt(getpass.getuser())
 | 
			
		||||
            if len([file.name for file in AppConfig.TEMP_DIR.glob("*.conf")]) == 0:
 | 
			
		||||
 | 
			
		||||
                LxTools.msg_window(
 | 
			
		||||
                    AppConfig.IMAGE_PATHS["icon_info"],
 | 
			
		||||
                    AppConfig.IMAGE_PATHS["icon_msg"],
 | 
			
		||||
                    Msg.STR["sel_tl"],
 | 
			
		||||
                    Msg.STR["tl_first"],
 | 
			
		||||
                )
 | 
			
		||||
                MessageDialog("info", Msg.STR["tl_first"], title=Msg.STR["sel_tl"])
 | 
			
		||||
 | 
			
		||||
                return False
 | 
			
		||||
            else:
 | 
			
		||||
                wg_tar: str = f"{AppConfig.BASE_DIR}/{now_datetime}"
 | 
			
		||||
@@ -186,22 +183,20 @@ class Tunnel:
 | 
			
		||||
                    with zipfile.ZipFile(f"{wg_tar}.zip", "r") as zf:
 | 
			
		||||
                        if zf.namelist():
 | 
			
		||||
 | 
			
		||||
                            LxTools.msg_window(
 | 
			
		||||
                                AppConfig.IMAGE_PATHS["icon_info"],
 | 
			
		||||
                                AppConfig.IMAGE_PATHS["icon_vpn"],
 | 
			
		||||
                                Msg.STR["exp_succ"],
 | 
			
		||||
                            MessageDialog(
 | 
			
		||||
                                "info",
 | 
			
		||||
                                Msg.STR["exp_in_home"],
 | 
			
		||||
                                title=Msg.STR["exp_succ"],
 | 
			
		||||
                            )
 | 
			
		||||
 | 
			
		||||
                        else:
 | 
			
		||||
                            logging.error(
 | 
			
		||||
                                "There was a mistake at creating the Zip file. File is empty."
 | 
			
		||||
                            )
 | 
			
		||||
                            LxTools.msg_window(
 | 
			
		||||
                                AppConfig.IMAGE_PATHS["icon_error"],
 | 
			
		||||
                                AppConfig.IMAGE_PATHS["icon_msg"],
 | 
			
		||||
                                Msg.STR["exp_err"],
 | 
			
		||||
                                Msg.STR["exp_zip"],
 | 
			
		||||
                            MessageDialog(
 | 
			
		||||
                                "error", Msg.STR["exp_zip"], title=Msg.STR["exp_err"]
 | 
			
		||||
                            )
 | 
			
		||||
 | 
			
		||||
                            return False
 | 
			
		||||
                        return True
 | 
			
		||||
                except PermissionError:
 | 
			
		||||
@@ -217,12 +212,7 @@ class Tunnel:
 | 
			
		||||
            pass
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            logging.error(f"Export failed: {str(e)}")
 | 
			
		||||
            LxTools.msg_window(
 | 
			
		||||
                AppConfig.IMAGE_PATHS["icon_error"],
 | 
			
		||||
                AppConfig.IMAGE_PATHS["icon_msg"],
 | 
			
		||||
                Msg.STR["exp_err"],
 | 
			
		||||
                Msg.STR["exp_try"],
 | 
			
		||||
            )
 | 
			
		||||
            MessageDialog("error", Msg.STR["exp_try"], title=Msg.STR["exp_err"])
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
        finally:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										528
									
								
								wirepy.py
									
									
									
									
									
								
							
							
						
						@@ -6,13 +6,16 @@ import logging
 | 
			
		||||
import getpass
 | 
			
		||||
import shutil
 | 
			
		||||
import sys
 | 
			
		||||
import os
 | 
			
		||||
import subprocess
 | 
			
		||||
import tkinter as tk
 | 
			
		||||
import webbrowser
 | 
			
		||||
from functools import partial
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from subprocess import CompletedProcess, run
 | 
			
		||||
from tkinter import TclError, filedialog, ttk
 | 
			
		||||
from tunnel import Tunnel
 | 
			
		||||
 | 
			
		||||
from shared_libs.message import MessageDialog
 | 
			
		||||
from shared_libs.gitea import GiteaUpdate
 | 
			
		||||
from shared_libs.common_tools import (
 | 
			
		||||
    LxTools,
 | 
			
		||||
@@ -23,7 +26,7 @@ from shared_libs.common_tools import (
 | 
			
		||||
    Tooltip,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
from shared_libs.wp_app_config import AppConfig, Msg
 | 
			
		||||
from shared_libs.wp_app_config import AppConfig, Image, Msg
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Wirepy(tk.Tk):
 | 
			
		||||
@@ -38,6 +41,7 @@ class Wirepy(tk.Tk):
 | 
			
		||||
        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]
 | 
			
		||||
 | 
			
		||||
@@ -47,23 +51,29 @@ class Wirepy(tk.Tk):
 | 
			
		||||
            AppConfig.UI_CONFIG["resizable_window"][0],
 | 
			
		||||
            AppConfig.UI_CONFIG["resizable_window"][1],
 | 
			
		||||
        )
 | 
			
		||||
        self.minsize(
 | 
			
		||||
            AppConfig.UI_CONFIG["window_size"][0],
 | 
			
		||||
            AppConfig.UI_CONFIG["window_size"][1],
 | 
			
		||||
        )
 | 
			
		||||
        self.title(AppConfig.UI_CONFIG["window_title"])
 | 
			
		||||
 | 
			
		||||
        self.columnconfigure(0, weight=1)
 | 
			
		||||
        self.rowconfigure(0, weight=1)
 | 
			
		||||
        self.image_manager = Image()
 | 
			
		||||
        self.tk.call("source", f"{AppConfig.SYSTEM_PATHS['tcl_path']}/water.tcl")
 | 
			
		||||
        ConfigManager.init(AppConfig.SETTINGS_FILE)
 | 
			
		||||
        theme = ConfigManager.get("theme")
 | 
			
		||||
        ThemeManager.change_theme(self, theme)
 | 
			
		||||
 | 
			
		||||
        # 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)
 | 
			
		||||
        # Try to set icon
 | 
			
		||||
        try:
 | 
			
		||||
            icon = self.image_manager.load_image("icon_vpn")
 | 
			
		||||
            if icon:
 | 
			
		||||
                self.iconphoto(True, icon)
 | 
			
		||||
        except:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        # Add the widgets
 | 
			
		||||
        FrameWidgets(self).grid()
 | 
			
		||||
        self.columnconfigure(0, weight=1)
 | 
			
		||||
        self.rowconfigure(0, weight=1)
 | 
			
		||||
 | 
			
		||||
        # Center the window on the primary monitor
 | 
			
		||||
        LxTools.center_window_cross_platform(self, self.x_width, self.y_height)
 | 
			
		||||
@@ -86,13 +96,14 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
        self.dns = None
 | 
			
		||||
        self.address = None
 | 
			
		||||
        self.auto_con = None
 | 
			
		||||
        self.style = ttk.Style()
 | 
			
		||||
        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.image_manager = Image()
 | 
			
		||||
        self.columnconfigure(0, weight=1)
 | 
			
		||||
        self.rowconfigure(0, weight=1)
 | 
			
		||||
        self.columnconfigure(1, weight=1)
 | 
			
		||||
        self.rowconfigure(1, weight=1)
 | 
			
		||||
        self.columnconfigure(2, weight=18)
 | 
			
		||||
        self.rowconfigure(2, weight=1)
 | 
			
		||||
        self.rowconfigure(3, weight=1)
 | 
			
		||||
 | 
			
		||||
        # StringVar-Variables initialization
 | 
			
		||||
        self.tooltip_state = tk.BooleanVar()
 | 
			
		||||
@@ -121,20 +132,10 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
 | 
			
		||||
        # 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=AppConfig.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: {AppConfig.VERSION[2:]}", self.tooltip_state
 | 
			
		||||
        )
 | 
			
		||||
        self.menu_frame.grid(column=0, columnspan=3, row=1, sticky="we")
 | 
			
		||||
 | 
			
		||||
        self.options_btn = ttk.Menubutton(self.menu_frame, text=_("Options"))
 | 
			
		||||
        self.options_btn.grid(column=1, columnspan=1, row=0)
 | 
			
		||||
        self.options_btn.grid(column=0, row=0)
 | 
			
		||||
 | 
			
		||||
        Tooltip(self.options_btn, Msg.TTIP["settings"], self.tooltip_state)
 | 
			
		||||
 | 
			
		||||
@@ -146,9 +147,8 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
            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(column=2, row=0)
 | 
			
		||||
        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)
 | 
			
		||||
@@ -176,69 +176,148 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
        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)
 | 
			
		||||
        self.about_btn.grid(column=1, row=0)
 | 
			
		||||
 | 
			
		||||
        self.a = Tunnel.get_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
 | 
			
		||||
        # Header Frame
 | 
			
		||||
        # Festlegen der Farbe
 | 
			
		||||
        self.header_frame = tk.Frame(self, bg="#2c3e50")
 | 
			
		||||
        self.wg_icon_header_frame = tk.Frame(self.header_frame, bg="#2c3e50")
 | 
			
		||||
        self.header_label = tk.Label(
 | 
			
		||||
            self.header_frame,
 | 
			
		||||
            text=_("Lx Tools Wire-Py"),
 | 
			
		||||
            font=("Helvetica", 12, "bold"),
 | 
			
		||||
            fg="#ffffff",
 | 
			
		||||
            bg="#2c3e50",
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # 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)
 | 
			
		||||
        self.version_label = tk.Label(
 | 
			
		||||
            self.header_frame,
 | 
			
		||||
            text=f"{AppConfig.VERSION} • {Msg.STR['header_left_bottom']}",
 | 
			
		||||
            font=("Helvetica", 9),
 | 
			
		||||
            fg="#bdc3c7",
 | 
			
		||||
            bg="#2c3e50",
 | 
			
		||||
        )
 | 
			
		||||
        self.info_label = tk.Label(
 | 
			
		||||
            self.header_frame,
 | 
			
		||||
            text=Msg.STR["header_right_top"],
 | 
			
		||||
            font=("Helvetica", 10),
 | 
			
		||||
            fg="#ecf0f1",
 | 
			
		||||
            bg="#2c3e50",
 | 
			
		||||
        )
 | 
			
		||||
        self.header_frame.grid(column=0, columnspan=3, row=0, sticky="nsew")
 | 
			
		||||
        self.wg_icon_header_frame.grid(column=0, row=0, rowspan=2, sticky="w")
 | 
			
		||||
 | 
			
		||||
        self.wg_icon_header_label = tk.Label(
 | 
			
		||||
            self.wg_icon_header_frame,
 | 
			
		||||
            image=self.image_manager.load_image("icon_header"),
 | 
			
		||||
            bg="#2c3e50",
 | 
			
		||||
        )
 | 
			
		||||
        self.wg_icon_header_label.grid(column=0, row=0, sticky="e", ipadx=10)
 | 
			
		||||
 | 
			
		||||
        self.header_label.grid(
 | 
			
		||||
            column=1,
 | 
			
		||||
            row=0,
 | 
			
		||||
            sticky="w",
 | 
			
		||||
            padx=(5, 20),
 | 
			
		||||
            pady=(15, 5),
 | 
			
		||||
            ipady=4,
 | 
			
		||||
        )
 | 
			
		||||
        self.version_label.grid(column=1, row=1, sticky="w", padx=(5, 20), pady=(0, 10))
 | 
			
		||||
        self.info_label.grid(column=2, row=0, sticky="ne", padx=(10, 10), pady=(10, 0))
 | 
			
		||||
        self.header_frame.columnconfigure(1, weight=1, pad=2)
 | 
			
		||||
        self.header_frame.rowconfigure(0, weight=1)
 | 
			
		||||
 | 
			
		||||
        # Frame for Control Buttons (Start, Stop, Import, Trash, Export)
 | 
			
		||||
        self.control_buttons_frame = ttk.Frame(self)
 | 
			
		||||
        self.control_buttons_frame.grid(column=0, row=2, sticky="w", padx=(15, 0))
 | 
			
		||||
        self.control_buttons_frame.columnconfigure(0, weight=1)
 | 
			
		||||
        self.control_buttons_frame.rowconfigure(2, weight=1)
 | 
			
		||||
 | 
			
		||||
        # Frame for Listbox and Scrollbar
 | 
			
		||||
        self.list_container_frame = ttk.Frame(self)
 | 
			
		||||
        self.list_container_frame.grid(column=1, row=2, sticky="nsew")
 | 
			
		||||
        self.list_container_frame.columnconfigure(1, weight=1)
 | 
			
		||||
        self.list_container_frame.rowconfigure(2, weight=1)
 | 
			
		||||
        self.list_frame = ttk.LabelFrame(self.list_container_frame, text=_("Tunnels"))
 | 
			
		||||
        self.list_frame.grid(column=0, row=0, sticky="nsew", padx=10, ipady=20)
 | 
			
		||||
        # Listbox with Scrollbar
 | 
			
		||||
        self.list_box = tk.Listbox(self.list_frame, selectmode="single")
 | 
			
		||||
        self.list_box.config(
 | 
			
		||||
            relief="flat",
 | 
			
		||||
            font=("Ubuntu", 12, "bold"),
 | 
			
		||||
        )
 | 
			
		||||
        self.list_box.grid(column=0, row=0, sticky="nsew")
 | 
			
		||||
        self.list_box.event_add("<<ClickEvent>>", "<Button-1>")
 | 
			
		||||
        self.list_box.bind("<<ClickEvent>>", self.enable_check_box)
 | 
			
		||||
        self.scrollbar = ttk.Scrollbar(
 | 
			
		||||
            self.list_frame, orient="vertical", command=self.list_box.yview
 | 
			
		||||
        )
 | 
			
		||||
        self.scrollbar.grid(column=1, row=0, sticky="ns")
 | 
			
		||||
        self.list_box.configure(yscrollcommand=self.scrollbar.set)
 | 
			
		||||
        self.scrollbar.columnconfigure(1, weight=1)
 | 
			
		||||
        self.scrollbar.rowconfigure(0, weight=1)
 | 
			
		||||
 | 
			
		||||
        # Frame for Active Tunnel, Interface and Peer
 | 
			
		||||
        # Right Side Frame
 | 
			
		||||
        self.right_side_frame = ttk.Frame(self)
 | 
			
		||||
        self.right_side_frame.grid(column=2, row=2, sticky="nsew")
 | 
			
		||||
        self.right_side_frame.columnconfigure(2, weight=1)
 | 
			
		||||
        self.right_side_frame.rowconfigure(2, weight=1)
 | 
			
		||||
 | 
			
		||||
        # 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")
 | 
			
		||||
        self.active_frame = ttk.LabelFrame(
 | 
			
		||||
            self.right_side_frame, text=_("Active Tunnel")
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.active_frame.grid(
 | 
			
		||||
            column=0, row=0, sticky="nsew", padx=10, pady=5, columnspan=3
 | 
			
		||||
        )
 | 
			
		||||
        self.active_frame.columnconfigure(0, weight=1)
 | 
			
		||||
        self.active_frame.rowconfigure(0, weight=1)
 | 
			
		||||
        # Interface Label Frame
 | 
			
		||||
        self.interface_frame = ttk.LabelFrame(
 | 
			
		||||
            self.right_side_frame, text=_("Interface")
 | 
			
		||||
        )
 | 
			
		||||
        self.interface_frame.grid(
 | 
			
		||||
            column=0, row=1, sticky="nsew", padx=10, pady=5, columnspan=3
 | 
			
		||||
        )
 | 
			
		||||
        self.interface_frame.columnconfigure(0, weight=1)
 | 
			
		||||
        self.interface_frame.rowconfigure(1, weight=1)
 | 
			
		||||
 | 
			
		||||
        # Peer Label Frame
 | 
			
		||||
        self.peer_frame = ttk.LabelFrame(self.right_side_frame, text=_("Peer"))
 | 
			
		||||
        self.peer_frame.grid(
 | 
			
		||||
            column=0, row=2, sticky="nsew", padx=10, pady=5, columnspan=3
 | 
			
		||||
        )
 | 
			
		||||
        self.peer_frame.columnconfigure(0, weight=1)
 | 
			
		||||
        self.peer_frame.rowconfigure(2, weight=1)
 | 
			
		||||
 | 
			
		||||
        # Auto Start Label Frame
 | 
			
		||||
        self.autoconnect_frame = ttk.Frame(self)
 | 
			
		||||
        self.autoconnect_frame.grid(column=0, row=3, columnspan=2, sticky="w")
 | 
			
		||||
 | 
			
		||||
        # Rename Frame
 | 
			
		||||
        self.rename_frame = ttk.Frame(self)
 | 
			
		||||
        self.rename_frame.grid(column=2, padx=10, row=3, sticky="nsew")
 | 
			
		||||
        self.rename_frame.columnconfigure(0, weight=2)
 | 
			
		||||
        self.rename_frame.columnconfigure(1, weight=1)
 | 
			
		||||
 | 
			
		||||
        # 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 = ttk.Label(self.interface_frame)
 | 
			
		||||
        self.interface.grid(column=0, row=4, sticky="we")
 | 
			
		||||
        self.interface.config(font=("Ubuntu", 9))
 | 
			
		||||
 | 
			
		||||
        # Peer Label
 | 
			
		||||
        self.peer = ttk.Label(self.lb_frame2, text=_("Peer"))
 | 
			
		||||
        self.peer = ttk.Label(self.peer_frame)
 | 
			
		||||
        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("<<ClickEvent>>", "<Button-1>")
 | 
			
		||||
        self.l_box.bind("<<ClickEvent>>", 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)
 | 
			
		||||
        self.peer.grid(column=0, row=5, sticky="we")
 | 
			
		||||
 | 
			
		||||
        # Tunnel List
 | 
			
		||||
        self.tl = Tunnel.parse_files_to_dictionary(directory=AppConfig.TEMP_DIR)
 | 
			
		||||
@@ -246,8 +325,8 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
        AppConfig.ensure_directories()
 | 
			
		||||
 | 
			
		||||
        for tunnels, values in self.tl.items():
 | 
			
		||||
            self.l_box.insert("end", tunnels)
 | 
			
		||||
            self.l_box.update()
 | 
			
		||||
            self.list_box.insert("end", tunnels)
 | 
			
		||||
            self.list_box.update()
 | 
			
		||||
 | 
			
		||||
        # Button Vpn
 | 
			
		||||
        if self.a != "":
 | 
			
		||||
@@ -266,52 +345,55 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
 | 
			
		||||
        # Button Import
 | 
			
		||||
        self.btn_i = ttk.Button(
 | 
			
		||||
            self.lb_frame_btn_lbox,
 | 
			
		||||
            image=self.imp_pic,
 | 
			
		||||
            self.control_buttons_frame,
 | 
			
		||||
            image=self.image_manager.load_image("icon_import"),
 | 
			
		||||
            command=self.import_sl,
 | 
			
		||||
            padding=0,
 | 
			
		||||
        )
 | 
			
		||||
        self.btn_i.grid(column=0, row=1, padx=15, pady=8)
 | 
			
		||||
        self.btn_i.grid(column=0, row=1, 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,
 | 
			
		||||
            self.control_buttons_frame,
 | 
			
		||||
            image=self.image_manager.load_image("icon_trash"),
 | 
			
		||||
            command=self.delete,
 | 
			
		||||
            padding=0,
 | 
			
		||||
            style="CButton.TButton",
 | 
			
		||||
        )
 | 
			
		||||
        self.btn_tr.grid(column=0, row=2, padx=15, pady=8)
 | 
			
		||||
        self.btn_tr.grid(column=0, row=2, pady=8)
 | 
			
		||||
 | 
			
		||||
        if self.l_box.size() == 0:
 | 
			
		||||
        if self.list_box.size() == 0:
 | 
			
		||||
            Tooltip(self.btn_tr, Msg.TTIP["trash_tl_info"], self.tooltip_state)
 | 
			
		||||
        else:
 | 
			
		||||
            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,
 | 
			
		||||
            self.control_buttons_frame,
 | 
			
		||||
            image=self.image_manager.load_image("icon_export"),
 | 
			
		||||
            command=lambda: Tunnel.export(),
 | 
			
		||||
            padding=0,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.btn_exp.grid(column=0, row=3, padx=15, pady=8)
 | 
			
		||||
        self.btn_exp.grid(column=0, row=3, pady=8)
 | 
			
		||||
        self.btn_exp.columnconfigure(0, weight=1)
 | 
			
		||||
        self.btn_exp.rowconfigure(3, weight=1)
 | 
			
		||||
 | 
			
		||||
        if self.l_box.size() == 0:
 | 
			
		||||
        if self.list_box.size() == 0:
 | 
			
		||||
            Tooltip(self.btn_exp, Msg.TTIP["export_tl_info"], self.tooltip_state)
 | 
			
		||||
        else:
 | 
			
		||||
            Tooltip(self.btn_exp, Msg.TTIP["export_tl"], self.tooltip_state)
 | 
			
		||||
 | 
			
		||||
        # 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 = ttk.Entry(self.rename_frame)
 | 
			
		||||
        self.lb_rename.grid(column=0, row=0, padx=8, pady=10, sticky="ne")
 | 
			
		||||
        self.lb_rename.config(width=15)
 | 
			
		||||
 | 
			
		||||
        self.lb_rename.insert(0, _("Max. 12 characters!"))
 | 
			
		||||
        self.lb_rename.config(state="disable")
 | 
			
		||||
 | 
			
		||||
        if self.l_box.size() != 0:
 | 
			
		||||
        if self.list_box.size() != 0:
 | 
			
		||||
            Tooltip(
 | 
			
		||||
                self.lb_rename,
 | 
			
		||||
                Msg.TTIP["rename_tl"],
 | 
			
		||||
@@ -330,14 +412,13 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
 | 
			
		||||
        # Button Rename
 | 
			
		||||
        self.btn_rename = ttk.Button(
 | 
			
		||||
            self.lb_frame4,
 | 
			
		||||
            self.rename_frame,
 | 
			
		||||
            text=_("Rename"),
 | 
			
		||||
            state="disable",
 | 
			
		||||
            command=self.tl_rename,
 | 
			
		||||
            padding=4,
 | 
			
		||||
            style="RnButton.TButton",
 | 
			
		||||
            width=15,
 | 
			
		||||
        )
 | 
			
		||||
        self.btn_rename.grid(column=3, row=0, padx=5, pady=10, sticky="ne")
 | 
			
		||||
        self.btn_rename.grid(column=1, row=0, pady=10, sticky="nsew")
 | 
			
		||||
 | 
			
		||||
        # Check Buttons
 | 
			
		||||
        self.selected_option = tk.IntVar()
 | 
			
		||||
@@ -346,19 +427,21 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
 | 
			
		||||
        # Frame for Labels, Entry and Button
 | 
			
		||||
        self.autoconnect = ttk.Label(
 | 
			
		||||
            self.lb_frame3, textvariable=self.autoconnect_var, width=15
 | 
			
		||||
            self.autoconnect_frame, textvariable=self.autoconnect_var
 | 
			
		||||
        )
 | 
			
		||||
        self.autoconnect.config(font=("Ubuntu", 11))
 | 
			
		||||
        self.autoconnect.grid(column=1, row=0, sticky="e", pady=19)
 | 
			
		||||
        self.autoconnect.grid(column=1, row=0, pady=10, sticky="nsew")
 | 
			
		||||
        self.autoconnect.columnconfigure(1, weight=1)
 | 
			
		||||
        self.autoconnect.rowconfigure(0, weight=1)
 | 
			
		||||
        self.wg_autostart = ttk.Checkbutton(
 | 
			
		||||
            self.lb_frame3,
 | 
			
		||||
            self.autoconnect_frame,
 | 
			
		||||
            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")
 | 
			
		||||
        self.wg_autostart.grid(column=0, row=0, pady=10, padx=(10, 0), sticky="ew")
 | 
			
		||||
 | 
			
		||||
        if self.l_box.size() >= 1 and len(self.l_box.curselection()) >= 1:
 | 
			
		||||
        if self.list_box.size() >= 1 and len(self.list_box.curselection()) >= 1:
 | 
			
		||||
            Tooltip(
 | 
			
		||||
                self.wg_autostart,
 | 
			
		||||
                Msg.TTIP["autostart"],
 | 
			
		||||
@@ -367,7 +450,7 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
                y_offset=-40,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        if self.l_box.size() == 0:
 | 
			
		||||
        if self.list_box.size() == 0:
 | 
			
		||||
            Tooltip(
 | 
			
		||||
                self.wg_autostart,
 | 
			
		||||
                Msg.TTIP["autostart_info"],
 | 
			
		||||
@@ -400,6 +483,15 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
        else:
 | 
			
		||||
            self.updates_lb.grid_remove()
 | 
			
		||||
 | 
			
		||||
    def updater(self):
 | 
			
		||||
        """Start the lxtools_installer"""
 | 
			
		||||
        tmp_dir = Path("/tmp/lxtools")
 | 
			
		||||
        Path.mkdir(tmp_dir, exist_ok=True)
 | 
			
		||||
        os.chdir(tmp_dir)
 | 
			
		||||
        result = subprocess.run(["/usr/local/bin/lxtools_installer"], check=False)
 | 
			
		||||
        if result.returncode != 0:
 | 
			
		||||
            MessageDialog("error", result.stderr)
 | 
			
		||||
 | 
			
		||||
    # Update the labels based on the result
 | 
			
		||||
    def update_ui_for_update(self, res):
 | 
			
		||||
        """Update UI elements based on an update check result"""
 | 
			
		||||
@@ -436,25 +528,20 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
            self.update_btn = ttk.Button(
 | 
			
		||||
                self.menu_frame,
 | 
			
		||||
                image=self.image_manager.load_image("update"),
 | 
			
		||||
                style="Toolbutton",
 | 
			
		||||
                command=self.updater,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            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
 | 
			
		||||
                ),
 | 
			
		||||
            self.update_btn.grid(column=5, row=0, padx=0)
 | 
			
		||||
            Tooltip(
 | 
			
		||||
                self.update_btn, _("Click to install new version"), self.tooltip_state
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
@@ -462,10 +549,6 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
        """
 | 
			
		||||
        a tk.Toplevel window
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        def link_btn() -> 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"
 | 
			
		||||
@@ -473,13 +556,17 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
            "Use without warranty!\n"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        LxTools.msg_window(
 | 
			
		||||
            AppConfig.IMAGE_PATHS["icon_vpn"],
 | 
			
		||||
            AppConfig.IMAGE_PATHS["icon_vpn"],
 | 
			
		||||
            _("Info"),
 | 
			
		||||
        MessageDialog(
 | 
			
		||||
            "info",
 | 
			
		||||
            msg_t,
 | 
			
		||||
            _("Go to Wire-Py git"),
 | 
			
		||||
            link_btn,
 | 
			
		||||
            buttons=["OK", _("Go to Wire-Py git")],
 | 
			
		||||
            title=_("Info"),
 | 
			
		||||
            commands=[
 | 
			
		||||
                None,
 | 
			
		||||
                partial(webbrowser.open, "https://git.ilunix.de/punix/Wire-Py"),
 | 
			
		||||
            ],
 | 
			
		||||
            icon="/usr/share/icons/lx-icons/64/wg_vpn.png",
 | 
			
		||||
            wraplength=420,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def update_setting(self, update_res) -> None:
 | 
			
		||||
@@ -561,7 +648,9 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
        new_theme = "dark" if current_theme == "light" else "light"
 | 
			
		||||
        ThemeManager.change_theme(self, new_theme, new_theme)
 | 
			
		||||
        self.color_label()
 | 
			
		||||
        self.header_label.config(fg="#ffffff")
 | 
			
		||||
        self.update_theme_label()  # Update the theme label
 | 
			
		||||
 | 
			
		||||
        # Update Menulfield
 | 
			
		||||
        self.settings.entryconfigure(2, label=self.theme_label.get())
 | 
			
		||||
 | 
			
		||||
@@ -570,14 +659,16 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
        Start Button
 | 
			
		||||
        """
 | 
			
		||||
        self.btn_stst = ttk.Button(
 | 
			
		||||
            self.lb_frame_btn_lbox,
 | 
			
		||||
            image=self.wg_vpn_start,
 | 
			
		||||
            self.control_buttons_frame,
 | 
			
		||||
            image=self.image_manager.load_image("icon_start"),
 | 
			
		||||
            command=lambda: self.wg_switch("start"),
 | 
			
		||||
            padding=0,
 | 
			
		||||
        )
 | 
			
		||||
        self.btn_stst.grid(column=0, row=0, padx=5, pady=8)
 | 
			
		||||
        self.btn_stst.grid(column=0, row=0, pady=8)
 | 
			
		||||
        self.btn_stst.columnconfigure(0, weight=1)
 | 
			
		||||
        self.btn_stst.rowconfigure(0, weight=1)
 | 
			
		||||
 | 
			
		||||
        if self.l_box.size() == 0:
 | 
			
		||||
        if self.list_box.size() == 0:
 | 
			
		||||
            Tooltip(self.btn_stst, Msg.TTIP["empty_list"], self.tooltip_state)
 | 
			
		||||
        else:
 | 
			
		||||
            Tooltip(self.btn_stst, Msg.TTIP["start_tl"], self.tooltip_state)
 | 
			
		||||
@@ -589,28 +680,32 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
        if ConfigManager.get("theme") == "light":
 | 
			
		||||
 | 
			
		||||
            self.lb_tunnel = ttk.Label(
 | 
			
		||||
                self, textvariable=self.str_var, foreground="green"
 | 
			
		||||
                self.active_frame, textvariable=self.str_var, foreground="green"
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            self.lb_tunnel = ttk.Label(
 | 
			
		||||
                self, textvariable=self.str_var, foreground="yellow"
 | 
			
		||||
                self.active_frame, textvariable=self.str_var, foreground="yellow"
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        self.lb_tunnel.config(font=("Ubuntu", 11, "bold"))
 | 
			
		||||
        self.lb_tunnel.grid(column=2, padx=10, row=1)
 | 
			
		||||
        self.lb_tunnel.grid(column=0, row=0, padx=10, pady=(0, 10), sticky="n")
 | 
			
		||||
        self.lb_tunnel.columnconfigure(0, weight=1)
 | 
			
		||||
        self.lb_tunnel.rowconfigure(0, weight=1)
 | 
			
		||||
 | 
			
		||||
    def stop(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Stop Button
 | 
			
		||||
        """
 | 
			
		||||
        self.btn_stst = ttk.Button(
 | 
			
		||||
            self.lb_frame_btn_lbox,
 | 
			
		||||
            image=self.wg_vpn_stop,
 | 
			
		||||
            self.control_buttons_frame,
 | 
			
		||||
            image=self.image_manager.load_image("icon_stop"),
 | 
			
		||||
            command=lambda: self.wg_switch("stop"),
 | 
			
		||||
            padding=0,
 | 
			
		||||
        )
 | 
			
		||||
        self.btn_stst.grid(column=0, row=0, padx=5, pady=8)
 | 
			
		||||
        self.btn_stst.grid(column=0, row=0, pady=8)
 | 
			
		||||
        self.btn_stst.columnconfigure(0, weight=1)
 | 
			
		||||
        self.btn_stst.rowconfigure(0, weight=1)
 | 
			
		||||
 | 
			
		||||
        Tooltip(self.btn_stst, Msg.TTIP["stop_tl"], self.tooltip_state)
 | 
			
		||||
 | 
			
		||||
@@ -634,22 +729,16 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
            )
 | 
			
		||||
            data_import, key_name = Tunnel.parse_files_to_dictionary(filepath=filepath)
 | 
			
		||||
 | 
			
		||||
            if CryptoUtil.find_key(f"{data_import[key_name]["PrivateKey"]}="):
 | 
			
		||||
                LxTools.msg_window(
 | 
			
		||||
                    AppConfig.IMAGE_PATHS["icon_error"],
 | 
			
		||||
                    AppConfig.IMAGE_PATHS["icon_msg"],
 | 
			
		||||
                    Msg.STR["imp_err"],
 | 
			
		||||
                    Msg.STR["tl_exist"],
 | 
			
		||||
                )
 | 
			
		||||
            if CryptoUtil.find_key(f"{data_import[key_name]['PrivateKey']}="):
 | 
			
		||||
                MessageDialog("error", Msg.STR["tl_exist"], title=Msg.STR["imp_err"])
 | 
			
		||||
 | 
			
		||||
            elif not CryptoUtil.is_valid_base64(
 | 
			
		||||
                f"{data_import[key_name]["PrivateKey"]}="
 | 
			
		||||
                f"{data_import[key_name]['PrivateKey']}="
 | 
			
		||||
            ):  # 2. Second check: Is it valid Base64?
 | 
			
		||||
                LxTools.msg_window(
 | 
			
		||||
                    AppConfig.IMAGE_PATHS["icon_error"],
 | 
			
		||||
                    AppConfig.IMAGE_PATHS["icon_msg"],
 | 
			
		||||
                    Msg.STR["imp_err"],
 | 
			
		||||
                MessageDialog(
 | 
			
		||||
                    "error",
 | 
			
		||||
                    Msg.STR["invalid_base64"],
 | 
			
		||||
                    title=Msg.STR["imp_err"],
 | 
			
		||||
                )
 | 
			
		||||
            else:
 | 
			
		||||
                filepath = Path(filepath)
 | 
			
		||||
@@ -704,11 +793,11 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
                AppConfig.ensure_directories()
 | 
			
		||||
                self.str_var.set("")
 | 
			
		||||
                self.a = Tunnel.get_active()
 | 
			
		||||
                self.l_box.insert(0, self.a)
 | 
			
		||||
                self.list_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)
 | 
			
		||||
                self.list_box.selection_clear(0, tk.END)
 | 
			
		||||
                self.list_box.update()
 | 
			
		||||
                self.list_box.selection_set(0)
 | 
			
		||||
 | 
			
		||||
                Tooltip(
 | 
			
		||||
                    self.wg_autostart,
 | 
			
		||||
@@ -742,12 +831,7 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
                    print(f">> {import_file.stem} << autostart is disabled by default")
 | 
			
		||||
 | 
			
		||||
        except UnboundLocalError:
 | 
			
		||||
            LxTools.msg_window(
 | 
			
		||||
                AppConfig.IMAGE_PATHS["icon_error"],
 | 
			
		||||
                AppConfig.IMAGE_PATHS["icon_msg"],
 | 
			
		||||
                Msg.STR["imp_err"],
 | 
			
		||||
                Msg.STR["no_valid_file"],
 | 
			
		||||
            )
 | 
			
		||||
            MessageDialog("error", Msg.STR["no_valid_file"], title=Msg.STR["imp_err"])
 | 
			
		||||
        except (IsADirectoryError, TypeError, FileNotFoundError):
 | 
			
		||||
            print("File import: abort by user...")
 | 
			
		||||
        except EOFError as e:
 | 
			
		||||
@@ -758,8 +842,8 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
        delete Wireguard Tunnel
 | 
			
		||||
        """
 | 
			
		||||
        try:
 | 
			
		||||
            self.select_tunnel = self.l_box.curselection()
 | 
			
		||||
            select_tl = self.l_box.get(self.select_tunnel[0])
 | 
			
		||||
            self.select_tunnel = self.list_box.curselection()
 | 
			
		||||
            select_tl = self.list_box.get(self.select_tunnel[0])
 | 
			
		||||
 | 
			
		||||
            process: CompletedProcess[str] = run(
 | 
			
		||||
                ["nmcli", "connection", "delete", select_tl],
 | 
			
		||||
@@ -773,7 +857,7 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
                    f"{process.stderr} Code: {process.returncode}", exc_info=True
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
            self.l_box.delete(self.select_tunnel[0])
 | 
			
		||||
            self.list_box.delete(self.select_tunnel[0])
 | 
			
		||||
            Path.unlink(f"{AppConfig.CONFIG_DIR}/{select_tl}.dat")
 | 
			
		||||
 | 
			
		||||
            if select_tl == ConfigManager.get("autostart"):
 | 
			
		||||
@@ -784,7 +868,7 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
            self.wg_autostart.configure(state="disabled")
 | 
			
		||||
 | 
			
		||||
            # for disabling checkbox when Listbox empty
 | 
			
		||||
            if self.l_box.size() == 0:
 | 
			
		||||
            if self.list_box.size() == 0:
 | 
			
		||||
                self.wg_autostart.configure(state="disabled")
 | 
			
		||||
                self.lb_rename.configure(state="disabled")
 | 
			
		||||
                Tooltip(
 | 
			
		||||
@@ -803,35 +887,25 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
            if self.a != "" and self.a == select_tl:
 | 
			
		||||
                self.str_var.set(value="")
 | 
			
		||||
                self.start()
 | 
			
		||||
                self.l_box.update()
 | 
			
		||||
                self.list_box.update()
 | 
			
		||||
                self.reset_fields()
 | 
			
		||||
 | 
			
		||||
        except IndexError:
 | 
			
		||||
 | 
			
		||||
            if self.l_box.size() != 0:
 | 
			
		||||
            if self.list_box.size() != 0:
 | 
			
		||||
 | 
			
		||||
                LxTools.msg_window(
 | 
			
		||||
                    AppConfig.IMAGE_PATHS["icon_info"],
 | 
			
		||||
                    AppConfig.IMAGE_PATHS["icon_msg"],
 | 
			
		||||
                    Msg.STR["sel_tl"],
 | 
			
		||||
                    Msg.STR["sel_list"],
 | 
			
		||||
                )
 | 
			
		||||
                MessageDialog("info", Msg.STR["sel_list"], title=Msg.STR["sel_tl"])
 | 
			
		||||
 | 
			
		||||
            else:
 | 
			
		||||
 | 
			
		||||
                LxTools.msg_window(
 | 
			
		||||
                    AppConfig.IMAGE_PATHS["icon_info"],
 | 
			
		||||
                    AppConfig.IMAGE_PATHS["icon_msg"],
 | 
			
		||||
                    Msg.STR["sel_tl"],
 | 
			
		||||
                    Msg.STR["tl_first"],
 | 
			
		||||
                )
 | 
			
		||||
                MessageDialog("info", Msg.STR["tl_first"], title=Msg.STR["sel_tl"])
 | 
			
		||||
 | 
			
		||||
    def enable_check_box(self, _) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        checkbox for enable autostart Tunnel
 | 
			
		||||
        """
 | 
			
		||||
        AppConfig.get_autostart_content()
 | 
			
		||||
        if self.l_box.size() != 0:
 | 
			
		||||
        if self.list_box.size() != 0:
 | 
			
		||||
            self.wg_autostart.configure(state="normal")
 | 
			
		||||
            self.lb_rename.config(state="normal")
 | 
			
		||||
            self.lb_rename.delete(0, tk.END)
 | 
			
		||||
@@ -848,6 +922,7 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
            self.selected_option.set(1)
 | 
			
		||||
            self.autoconnect_var.set("")
 | 
			
		||||
            self.auto_con = ConfigManager.get("autostart")
 | 
			
		||||
            AppConfig.get_autostart_content()
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            self.selected_option.set(0)
 | 
			
		||||
@@ -857,13 +932,14 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
        self.autoconnect_var.set(self.auto_con)
 | 
			
		||||
 | 
			
		||||
        self.autoconnect = ttk.Label(
 | 
			
		||||
            self.lb_frame3,
 | 
			
		||||
            self.autoconnect_frame,
 | 
			
		||||
            textvariable=self.autoconnect_var,
 | 
			
		||||
            foreground="#0071ff",
 | 
			
		||||
            width=15,
 | 
			
		||||
            width=18,
 | 
			
		||||
        )
 | 
			
		||||
        self.autoconnect.config(font=("Ubuntu", 11))
 | 
			
		||||
        self.autoconnect.grid(column=1, row=0, sticky="e", pady=19)
 | 
			
		||||
        self.autoconnect.grid(column=1, row=0, sticky="ew", pady=19)
 | 
			
		||||
        self.autoconnect.rowconfigure(0, weight=1)
 | 
			
		||||
 | 
			
		||||
    def box_set(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
@@ -877,13 +953,13 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
        to disable the autostart.
 | 
			
		||||
        """
 | 
			
		||||
        try:
 | 
			
		||||
            select_tunnel = self.l_box.curselection()
 | 
			
		||||
            select_tl = self.l_box.get(select_tunnel[0])
 | 
			
		||||
            select_tunnel = self.list_box.curselection()
 | 
			
		||||
            select_tl = self.list_box.get(select_tunnel[0])
 | 
			
		||||
 | 
			
		||||
            if self.selected_option.get() == 0:
 | 
			
		||||
                ConfigManager.set("autostart", "off")
 | 
			
		||||
 | 
			
		||||
                if self.l_box.size() == 0:
 | 
			
		||||
                if self.list_box.size() == 0:
 | 
			
		||||
                    self.wg_autostart.configure(state="disabled")
 | 
			
		||||
 | 
			
		||||
            if self.selected_option.get() >= 1:
 | 
			
		||||
@@ -906,47 +982,27 @@ 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"],
 | 
			
		||||
            )
 | 
			
		||||
            MessageDialog("info", Msg.STR["sign_len"], title=Msg.STR["ren_err"])
 | 
			
		||||
 | 
			
		||||
        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"],
 | 
			
		||||
            )
 | 
			
		||||
            MessageDialog("info", Msg.STR["zero_signs"], title=Msg.STR["ren_err"])
 | 
			
		||||
 | 
			
		||||
        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"],
 | 
			
		||||
            )
 | 
			
		||||
            MessageDialog("info", Msg.STR["false_signs"], title=Msg.STR["ren_err"])
 | 
			
		||||
 | 
			
		||||
        elif self.lb_rename.get() in [
 | 
			
		||||
            file.stem for file in AppConfig.CONFIG_DIR.glob("*.dat")
 | 
			
		||||
        ]:
 | 
			
		||||
 | 
			
		||||
            LxTools.msg_window(
 | 
			
		||||
                AppConfig.IMAGE_PATHS["icon_info"],
 | 
			
		||||
                AppConfig.IMAGE_PATHS["icon_msg"],
 | 
			
		||||
                Msg.STR["ren_err"],
 | 
			
		||||
                Msg.STR["is_in_use"],
 | 
			
		||||
            )
 | 
			
		||||
            MessageDialog("info", Msg.STR["is_in_use"], title=Msg.STR["ren_err"])
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
 | 
			
		||||
            try:
 | 
			
		||||
                self.select_tunnel = self.l_box.curselection()
 | 
			
		||||
                select_tl = self.l_box.get(self.select_tunnel[0])
 | 
			
		||||
                self.select_tunnel = self.list_box.curselection()
 | 
			
		||||
                select_tl = self.list_box.get(self.select_tunnel[0])
 | 
			
		||||
 | 
			
		||||
                # nmcli connection modify old connection.id iphone
 | 
			
		||||
                process: CompletedProcess[str] = run(
 | 
			
		||||
@@ -974,20 +1030,15 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
                if select_tl == ConfigManager.get("autostart"):
 | 
			
		||||
                    ConfigManager.set("autostart", self.lb_rename.get())
 | 
			
		||||
                    self.autoconnect_var.set(value=self.lb_rename.get())
 | 
			
		||||
                self.l_box.delete(self.select_tunnel[0])
 | 
			
		||||
                self.l_box.insert("end", self.lb_rename.get())
 | 
			
		||||
                self.l_box.update()
 | 
			
		||||
                self.list_box.delete(self.select_tunnel[0])
 | 
			
		||||
                self.list_box.insert("end", self.lb_rename.get())
 | 
			
		||||
                self.list_box.update()
 | 
			
		||||
                self.lb_rename.delete(0, tk.END)
 | 
			
		||||
                self.update_connection_display()
 | 
			
		||||
 | 
			
		||||
            except IndexError:
 | 
			
		||||
 | 
			
		||||
                LxTools.msg_window(
 | 
			
		||||
                    AppConfig.IMAGE_PATHS["icon_info"],
 | 
			
		||||
                    AppConfig.IMAGE_PATHS["icon_msg"],
 | 
			
		||||
                    Msg.STR["ren_err"],
 | 
			
		||||
                    Msg.STR["sel_list"],
 | 
			
		||||
                )
 | 
			
		||||
                MessageDialog("info", Msg.STR["sel_list"], title=Msg.STR["ren_err"])
 | 
			
		||||
 | 
			
		||||
            except EOFError as e:
 | 
			
		||||
                logging.error(e, exc_info=True)
 | 
			
		||||
@@ -1012,34 +1063,36 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
        in the UI using ttk.Label widgets.
 | 
			
		||||
        Creates three labels for address, DNS, and endpoint with
 | 
			
		||||
        specific styling (color, font), positioning them in a
 | 
			
		||||
         grid layout (`lb_frame` and `lb_frame2`).
 | 
			
		||||
         grid layout (`lb_frame` and `peer_frame`).
 | 
			
		||||
        Each label is linked to a corresponding text variable
 | 
			
		||||
        (`self.add`, `self.DNS`, `self.enp`) for dynamic data updates.
 | 
			
		||||
        """
 | 
			
		||||
        # Address Label
 | 
			
		||||
        self.address = ttk.Label(
 | 
			
		||||
            self.lb_frame, textvariable=self.add, foreground="#0071ff"
 | 
			
		||||
            self.interface_frame, textvariable=self.add, foreground="#0071ff"
 | 
			
		||||
        )
 | 
			
		||||
        self.address.grid(column=0, row=5, sticky="w", padx=10, pady=6)
 | 
			
		||||
        self.address.grid(column=0, row=5, sticky="w", padx=10, pady=(0, 20))
 | 
			
		||||
        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 = ttk.Label(
 | 
			
		||||
            self.interface_frame, textvariable=self.DNS, foreground="#0071ff"
 | 
			
		||||
        )
 | 
			
		||||
        self.dns.grid(column=0, row=7, sticky="w", padx=10, pady=(0, 20))
 | 
			
		||||
        self.dns.config(font=("Ubuntu", 9))
 | 
			
		||||
 | 
			
		||||
        # Endpoint Label
 | 
			
		||||
        self.endpoint = ttk.Label(
 | 
			
		||||
            self.lb_frame2, textvariable=self.enp, foreground="#0071ff"
 | 
			
		||||
            self.peer_frame, textvariable=self.enp, foreground="#0071ff"
 | 
			
		||||
        )
 | 
			
		||||
        self.endpoint.grid(column=0, row=8, sticky="w", padx=10, pady=20)
 | 
			
		||||
        self.endpoint.grid(column=0, row=8, sticky="w", padx=10, pady=(0, 30))
 | 
			
		||||
        self.endpoint.config(font=("Ubuntu", 9))
 | 
			
		||||
 | 
			
		||||
    def wg_switch(self, event=None) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Manages switching between active and inactiveVPN connections.
 | 
			
		||||
        If no tunnel is selected (`self.a == ""`), it starts a new connection
 | 
			
		||||
        with the selected tunnel from the listbox (`l_box`).
 | 
			
		||||
        with the selected tunnel from the listbox (`list_box`).
 | 
			
		||||
        Otherwise, it stops the current connection and updates
 | 
			
		||||
        tunnel data using `handle_tunnel_data`.
 | 
			
		||||
        Handles errors like `IndexError` by displaying appropriate
 | 
			
		||||
@@ -1047,8 +1100,8 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
        """
 | 
			
		||||
        try:
 | 
			
		||||
            if self.a == "":
 | 
			
		||||
                self.select_tunnel = self.l_box.curselection()
 | 
			
		||||
                select_tl = self.l_box.get(self.select_tunnel[0])
 | 
			
		||||
                self.select_tunnel = self.list_box.curselection()
 | 
			
		||||
                select_tl = self.list_box.get(self.select_tunnel[0])
 | 
			
		||||
                self.handle_connection_state("start", select_tl)
 | 
			
		||||
 | 
			
		||||
            else:
 | 
			
		||||
@@ -1058,23 +1111,13 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
 | 
			
		||||
        except IndexError:
 | 
			
		||||
 | 
			
		||||
            if self.l_box.size() != 0:
 | 
			
		||||
            if self.list_box.size() != 0:
 | 
			
		||||
 | 
			
		||||
                LxTools.msg_window(
 | 
			
		||||
                    AppConfig.IMAGE_PATHS["icon_info"],
 | 
			
		||||
                    AppConfig.IMAGE_PATHS["icon_msg"],
 | 
			
		||||
                    Msg.STR["sel_tl"],
 | 
			
		||||
                    Msg.STR["sel_list"],
 | 
			
		||||
                )
 | 
			
		||||
                MessageDialog("info", Msg.STR["sel_list"], title=Msg.STR["sel_tl"])
 | 
			
		||||
 | 
			
		||||
            else:
 | 
			
		||||
 | 
			
		||||
                LxTools.msg_window(
 | 
			
		||||
                    AppConfig.IMAGE_PATHS["icon_info"],
 | 
			
		||||
                    AppConfig.IMAGE_PATHS["icon_msg"],
 | 
			
		||||
                    Msg.STR["sel_tl"],
 | 
			
		||||
                    Msg.STR["tl_first"],
 | 
			
		||||
                )
 | 
			
		||||
                MessageDialog("info", Msg.STR["tl_first"], title=Msg.STR["sel_tl"])
 | 
			
		||||
 | 
			
		||||
    def handle_connection_state(self, action: str, tunnel_name: str = None) -> None:
 | 
			
		||||
        """
 | 
			
		||||
@@ -1144,10 +1187,9 @@ class FrameWidgets(ttk.Frame):
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    AppConfig.ensure_directories()
 | 
			
		||||
    AppConfig.create_default_settings()
 | 
			
		||||
    CryptoUtil.decrypt(getpass.getuser(), AppConfig.CONFIG_DIR)
 | 
			
		||||
    _ = AppConfig.setup_translations()
 | 
			
		||||
    LxTools.sigi(AppConfig.TEMP_DIR)
 | 
			
		||||
 | 
			
		||||
    CryptoUtil.decrypt(getpass.getuser())
 | 
			
		||||
    window = Wirepy()
 | 
			
		||||
    LogConfig.logger(ConfigManager.get("logfile"))
 | 
			
		||||
    """
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,8 @@
 | 
			
		||||
import logging
 | 
			
		||||
import gettext
 | 
			
		||||
import locale
 | 
			
		||||
import tkinter as tk
 | 
			
		||||
import os
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from subprocess import CompletedProcess, run
 | 
			
		||||
from typing import Dict, Any
 | 
			
		||||
@@ -57,18 +59,18 @@ class AppConfig:
 | 
			
		||||
 | 
			
		||||
    # Updates
 | 
			
		||||
    # 1 = 1. Year, 09 = Month of the Year, 2924 = Day and Year of the Year
 | 
			
		||||
    VERSION: str = "v. 2.06.0425"
 | 
			
		||||
    VERSION: str = "v. 2.07.0225"
 | 
			
		||||
    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_title": "",
 | 
			
		||||
        "window_title2": "LogViewer",
 | 
			
		||||
        "window_size": (600, 383),
 | 
			
		||||
        "window_size": (590, 460),
 | 
			
		||||
        "font_family": "Ubuntu",
 | 
			
		||||
        "font_size": 11,
 | 
			
		||||
        "resizable_window": (False, False),
 | 
			
		||||
        "resizable_window": (True, True),
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    # System-dependent paths
 | 
			
		||||
@@ -79,20 +81,6 @@ class AppConfig:
 | 
			
		||||
        "pkey_path": "/usr/local/etc/ssl/pwgk.pem",
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    # Images and icons paths
 | 
			
		||||
    IMAGE_PATHS: Dict[str, Path] = {
 | 
			
		||||
        "icon_vpn": "/usr/share/icons/lx-icons/48/wg_vpn.png",
 | 
			
		||||
        "icon_msg": "/usr/share/icons/lx-icons/48/wg_msg.png",
 | 
			
		||||
        "icon_import": "/usr/share/icons/lx-icons/48/wg_import.png",
 | 
			
		||||
        "icon_export": "/usr/share/icons/lx-icons/48/wg_export.png",
 | 
			
		||||
        "icon_trash": "/usr/share/icons/lx-icons/48/wg_trash.png",
 | 
			
		||||
        "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_log": "/usr/share/icons/lx-icons/48/log.png",
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def setup_translations() -> gettext.gettext:
 | 
			
		||||
        """
 | 
			
		||||
@@ -153,6 +141,8 @@ class AppConfig:
 | 
			
		||||
            text=True,
 | 
			
		||||
            check=False,
 | 
			
		||||
        )
 | 
			
		||||
        if process.returncode == 0:
 | 
			
		||||
            logging.info(process.stdout, exc_info=True)
 | 
			
		||||
 | 
			
		||||
        if process.stderr:
 | 
			
		||||
            logging.error(f"{process.stderr} Code: {process.returncode}", exc_info=True)
 | 
			
		||||
@@ -168,6 +158,65 @@ class AppConfig:
 | 
			
		||||
_ = AppConfig.setup_translations()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Image:
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.images = {}
 | 
			
		||||
 | 
			
		||||
    def load_image(self, image_key, fallback_paths=None) -> None | tk.PhotoImage:
 | 
			
		||||
        """Load PNG image using tk.PhotoImage with fallback options"""
 | 
			
		||||
        if image_key in self.images:
 | 
			
		||||
            return self.images[image_key]
 | 
			
		||||
 | 
			
		||||
        # Define image paths based on key
 | 
			
		||||
        image_paths = {
 | 
			
		||||
            "icon_header": [
 | 
			
		||||
                "/usr/share/icons/lx-icons/32/wg_vpn.png",
 | 
			
		||||
            ],
 | 
			
		||||
            "icon_vpn": [
 | 
			
		||||
                "/usr/share/icons/lx-icons/48/wg_vpn.png",
 | 
			
		||||
            ],
 | 
			
		||||
            "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_import": [
 | 
			
		||||
                "/usr/share/icons/lx-icons/48/wg_import.png",
 | 
			
		||||
            ],
 | 
			
		||||
            "icon_export": [
 | 
			
		||||
                "/usr/share/icons/lx-icons/48/wg_export.png",
 | 
			
		||||
            ],
 | 
			
		||||
            "icon_trash": [
 | 
			
		||||
                "/usr/share/icons/lx-icons/48/wg_trash.png",
 | 
			
		||||
            ],
 | 
			
		||||
            "update": [
 | 
			
		||||
                "/usr/share/icons/lx-icons/16/settings.png",
 | 
			
		||||
            ],
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        # Get paths to try
 | 
			
		||||
        paths_to_try = image_paths.get(image_key, [])
 | 
			
		||||
 | 
			
		||||
        # Add fallback paths if provided
 | 
			
		||||
        if fallback_paths:
 | 
			
		||||
            paths_to_try.extend(fallback_paths)
 | 
			
		||||
 | 
			
		||||
        # Try to load image from paths
 | 
			
		||||
        for path in paths_to_try:
 | 
			
		||||
            try:
 | 
			
		||||
                if os.path.exists(path):
 | 
			
		||||
                    photo = tk.PhotoImage(file=path)
 | 
			
		||||
                    self.images[image_key] = photo
 | 
			
		||||
                    return photo
 | 
			
		||||
            except tk.TclError as e:
 | 
			
		||||
                print(f"Fail to load image {path}: {e}")
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
        # Return None if no image found
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Msg:
 | 
			
		||||
    """
 | 
			
		||||
    A utility class that provides centralized access to translated message strings.
 | 
			
		||||
@@ -191,6 +240,8 @@ class Msg:
 | 
			
		||||
 | 
			
		||||
    STR: Dict[str, str] = {
 | 
			
		||||
        # Strings for messages
 | 
			
		||||
        "header_left_bottom": _("Simple GUI for Wireguard"),
 | 
			
		||||
        "header_right_top": _("Wireguard VPN Manager"),
 | 
			
		||||
        "sel_tl": _("Select tunnel"),
 | 
			
		||||
        "ren_err": _("Renaming not possible"),
 | 
			
		||||
        "exp_succ": _("Export successful"),
 | 
			
		||||
 
 | 
			
		||||