Wire-Py/wirepy.py

932 lines
39 KiB
Python
Executable File

#!/usr/bin/python3
"""
this script is a simple GUI for managing Wireguard Tunnels
"""
import gettext
import locale
import os
import shutil
import subprocess
import sys
import tkinter as tk
import webbrowser
from pathlib import Path
from subprocess import check_call
from tkinter import TclError, filedialog, ttk
from common_tools import (ConfigManager, ThemeManager, Create, GiteaUpdate, Tunnel, Tooltip, LxTools)
from wp_app_config import AppConfig, Msg
LxTools.uos()
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.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")
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)
# 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)
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
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"])
# StringVar-Variables initialization
self.tooltip_state = tk.BooleanVar()
# Get value from configuration
state = ConfigManager.get("tooltips")
# NOTE: ConfigManager.get("tooltips") can return either a boolean value or a string,
# depending on whether the value was loaded from the file (bool) or the default value is used (string).
# The expression 'lines[5].strip() == "True"' in ConfigManager.load() converts the string to a boolean.
# Convert to boolean and set
if isinstance(state, bool):
# If it's already a boolean, use directly
self.tooltip_state.set(state)
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")
# 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.options_btn = ttk.Menubutton(self.menu_frame, text=_("Options"))
self.options_btn.grid(column=1, columnspan=1, row=0)
Tooltip(self.options_btn, Msg.TTIP["settings"], self.tooltip_state)
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.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"))
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.theme_label = tk.StringVar()
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.about_btn.grid(column=2, columnspan=2, row=0)
self.readme = tk.Menu(self)
self.a = Tunnel.active()
# Label Frame 1
self.lb_frame_btn_lbox = ttk.Frame(self)
self.lb_frame_btn_lbox.configure(relief="flat")
self.lb_frame_btn_lbox.grid(column=0, rowspan=3, row=1)
# Label Frame 2
self.lb_frame = ttk.Frame(self)
self.lb_frame.configure(relief="solid")
self.lb_frame.grid(column=2, row=2, sticky="snew", padx=20, pady=5)
# Label Frame 3
self.lb_frame2 = ttk.Frame(self)
self.lb_frame2.configure(relief="solid")
self.lb_frame2.grid(column=2, row=3, sticky="snew", padx=20, pady=5)
# Bottom Frame 4
self.lb_frame3 = ttk.Frame(self)
self.lb_frame3.configure(relief="flat")
self.lb_frame3.grid(column=0, row=5, columnspan=4, sticky="snew", padx=2, pady=2)
# Bottom Frame 5
self.lb_frame4 = ttk.Frame(self)
self.lb_frame4.configure(relief="flat")
self.lb_frame4.grid(column=2, row=5, columnspan=3, sticky="e", padx=15)
# Show active Label
self.select_tunnel = None
self.lb = ttk.Label(self, text=_("Active: "))
self.lb.config(font=("Ubuntu", 11, "bold"))
self.lb.grid(column=2, row=1, padx=15, pady=4, sticky="w")
# Label to Show active Tunnel
self.str_var = tk.StringVar(value=self.a)
self.color_label()
# Interface Label
self.interface = ttk.Label(self.lb_frame, text=_("Interface"))
self.interface.grid(column=0, row=3, sticky="we", padx=120)
self.interface.config(font=("Ubuntu", 9))
# Peer Label
self.peer = ttk.Label(self.lb_frame2, text=_("Peer"))
self.peer.config(font=("Ubuntu", 9))
self.peer.grid(column=0, row=4, sticky="we", padx=130)
# Listbox with Scrollbar
self.l_box = tk.Listbox(self.lb_frame_btn_lbox, selectmode="single")
self.l_box.config(relief="ridge", font=("Ubuntu", 12, "bold"))
self.l_box.grid(column=1, rowspan=4, row=0, sticky="ns")
self.l_box.event_add("<<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)
# Tunnel List
self.tl = LxTools.get_file_name(AppConfig.TEMP_DIR)
for tunnels in self.tl:
self.l_box.insert("end", tunnels)
self.l_box.update()
# Button Vpn
if self.a != "":
self.stop()
data = self.handle_tunnel_data(self.a)
else:
self.start()
# Address Label
self.add = tk.StringVar()
self.DNS = tk.StringVar()
self.enp = tk.StringVar()
self.reset_fields()
self.show_data()
# Button Import
self.btn_i = ttk.Button(self.lb_frame_btn_lbox, image=self.imp_pic, command=self.import_sl, padding=0)
self.btn_i.grid(column=0, row=1, padx=15, pady=8)
Tooltip(self.btn_i, 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.grid(column=0, row=2, padx=15, pady=8)
if self.l_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,
command=lambda: Tunnel.export(AppConfig.IMAGE_PATHS["icon_info"],
AppConfig.IMAGE_PATHS["icon_vpn"], AppConfig.IMAGE_PATHS["icon_error"],
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["sel_tl"], Msg.STR["tl_first"]), padding=0)
self.btn_exp.grid(column=0, row=3, padx=15, pady=8)
if self.l_box.size() == 0:
Tooltip(self.btn_exp, 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.insert(0, _("Max. 12 characters!"))
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)
else:
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.grid(column=3, row=0, padx=5, pady=10, sticky="ne")
# Check Buttons
self.selected_option = tk.IntVar()
self.autoconnect_var = tk.StringVar()
self.autoconnect_var.set(f"{self.auto_con}")
# Frame for Labels, Entry and Button
self.autoconnect = ttk.Label(self.lb_frame3, textvariable=self.autoconnect_var, width=15)
self.autoconnect.config(font=("Ubuntu", 11))
self.autoconnect.grid(column=1, row=0, sticky="e", pady=19)
self.wg_autostart = ttk.Checkbutton(self.lb_frame3, text=_("Autoconnect on:"), variable=self.selected_option,
command=self.box_set)
self.wg_autostart.grid(column=0, row=0, pady=15, padx=15, sticky="nw")
if self.l_box.size() >= 1 and len(self.l_box.curselection()) >= 1:
Tooltip(self.wg_autostart, Msg.TTIP["autostart"], 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)
else:
Tooltip(self.wg_autostart, Msg.TTIP["autostart"], self.tooltip_state, x_offset=-10, y_offset=-40)
self.on_off()
# Method that is called when the variable changes
def update_label_display(self, *args):
# Set the foreground color
self.updates_lb.configure(foreground=self.update_foreground.get())
# Show or hide the label based on whether it contains text
if self.update_label.get():
# Make sure the label is in the correct position every time it's shown
self.updates_lb.grid(column=4, columnspan=3, row=0, padx=10)
else:
self.updates_lb.grid_remove()
# Update the labels based on the result
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'):
self.update_btn.grid_forget()
delattr(self, 'update_btn')
if res == "False":
self.set_update.set(value=1)
self.update_label.set(_("Update search off"))
self.update_tooltip.set(_("Updates you have disabled"))
# Clear the foreground color as requested
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)
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)
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,
AppConfig.IMAGE_PATHS["icon_info"],
AppConfig.IMAGE_PATHS["icon_vpn"],
AppConfig.IMAGE_PATHS["icon_error"],
AppConfig.IMAGE_PATHS["icon_msg"]
)
)
@staticmethod
def about() -> None:
"""
a tk.Toplevel window
"""
def link_btn() -> str | None:
webbrowser.open("https://git.ilunix.de/punix/Wire-Py")
msg_t = _("Wire-Py a simple Wireguard Gui for Linux systems.\n\n"
"Wire-Py is open source software written in Python.\n\n"
"Email: polunga40@unity-mail.de also likes for donation.\n\n"
"Use without warranty!\n")
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_vpn"], AppConfig.IMAGE_PATHS["icon_vpn"], _("Info"), msg_t, _("Go to Wire-Py git"), link_btn)
def update_setting(self, update_res) -> None:
"""write off or on in file
Args:
update_res (int): argument that is passed contains 0 or 1
"""
if update_res == 1:
# Disable updates
ConfigManager.set("updates", "off")
# When updates are disabled, we know the result should be "False"
self.update_ui_for_update("False")
else:
# Enable updates
ConfigManager.set("updates", "on")
# 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")
# Make sure UI is updated regardless of previous state
if hasattr(self, 'update_btn'):
self.update_btn.grid_forget()
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:
print(f"Error checking for updates: {e}")
# Fallback to a default message if there's an error
self.update_ui_for_update("No Internet Connection!")
def tooltip_update_label(self) -> None:
"""Updates the tooltip menu label based on the current tooltip status"""
# Set the menu text based on the current status
if self.tooltip_state.get():
# If tooltips are enabled, the menu option should be to disable them
self.tooltip_label.set(_("Disable Tooltips"))
else:
# If tooltips are disabled, the menu option should be to enable them
self.tooltip_label.set(_("Enable Tooltips"))
def tooltips_toggle(self):
"""Toggles tooltips on/off and updates the menu label"""
# Toggle the boolean state
new_bool_state = not self.tooltip_state.get()
# Save the converted value in the configuration
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())
def update_theme_label(self) -> str:
"""Update the theme label based on current theme"""
current_theme = ConfigManager.get("theme")
if current_theme == "light":
self.theme_label.set(_("Dark"))
else:
self.theme_label.set(_("Light"))
def on_theme_toggle(self) -> None:
"""Toggle between light and dark theme"""
current_theme = ConfigManager.get("theme")
new_theme = "dark" if current_theme == "light" else "light"
ThemeManager.change_theme(self, new_theme, new_theme)
self.color_label()
self.update_theme_label() # Update the theme label
# Update Menulfield
self.settings.entryconfigure(2, label=self.theme_label.get())
def start(self) -> None:
"""
Start Button
"""
self.btn_stst = ttk.Button(self.lb_frame_btn_lbox, image=self.wg_vpn_start,
command=lambda: self.wg_switch("start"), padding=0)
self.btn_stst.grid(column=0, row=0, padx=5, pady=8)
tl = LxTools.get_file_name(AppConfig.TEMP_DIR)
if len(self.tl) == 0:
Tooltip(self.btn_stst, Msg.TTIP["empty_list"], self.tooltip_state)
else:
Tooltip(self.btn_stst, Msg.TTIP["start_tl"], self.tooltip_state)
def handle_tunnel_data(self, tunnel_name: str) -> tuple[str, str, str, str | None]:
"""_summary_
Args:
tunnel_name (str): name of a tunnel
Returns:
tuple[str, str]: tuple with tunnel data
"""
wg_read = f"/tmp/tlecdcwg/{tunnel_name}.conf"
with open(wg_read, "r", encoding="utf-8") as file:
data = Tunnel.con_to_dict(file)
self.init_and_report(data)
self.show_data()
return data
def color_label(self) -> None:
"""
View activ Tunnel in the color green or yellow
"""
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.config(font=("Ubuntu", 11, "bold"))
self.lb_tunnel.grid(column=2, padx=10, row=1)
def stop(self) -> None:
"""
Stop Button
"""
self.btn_stst = ttk.Button(self.lb_frame_btn_lbox, image=self.wg_vpn_stop,
command=lambda: self.wg_switch("stop"), padding=0)
self.btn_stst.grid(column=0, row=0, padx=5, pady=8)
Tooltip(self.btn_stst, Msg.TTIP["stop_tl"], self.tooltip_state)
def reset_fields(self) -> None:
"""
reset data from labels
"""
fields = [self.add, self.DNS, self.enp]
for field in fields:
field.set("")
def import_sl(self) -> None:
"""validity check of wireguard config files"""
Create.dir_and_files()
try:
filepath = filedialog.askopenfilename(
initialdir=f"{Path.home()}",
title=_("Select Wireguard config File"),
filetypes=[(_("WG config files"), "*.conf")]
)
# Überprüfe, ob der Benutzer den Dialog abgebrochen hat
if not filepath:
print("File import: abort by user...")
return
with open(filepath, "r", encoding="utf-8") as file:
read = file.read()
path_split = filepath.split("/")
path_split1 = path_split[-1]
if "PrivateKey = " in read and "PublicKey = " in read and "Endpoint =" in read:
with open(filepath, "r", encoding="utf-8") as file:
key = Tunnel.con_to_dict(file)
pre_key = key[3]
if len(pre_key) != 0:
p_key = AppConfig.KEYS_FILE.read_text(encoding="utf-8")
if pre_key in p_key or f"{pre_key}\n" in p_key:
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_error"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["imp_err"], Msg.STR["tl_exist"])
else:
with open(AppConfig.KEYS_FILE, "a", encoding="utf-8") as keyfile:
keyfile.write(f"{pre_key}\r")
if len(path_split1) > 17:
p1 = shutil.copy(filepath, AppConfig.TEMP_DIR)
path_split = path_split1[len(path_split1) - 17:]
os.rename(p1, f"{AppConfig.TEMP_DIR}/{path_split}")
new_conf = f"{AppConfig.TEMP_DIR}/{path_split}"
if self.a != "":
check_call(["nmcli", "connection", "down", self.a])
self.reset_fields()
subprocess.check_output(["nmcli", "connection", "import", "type", "wireguard", "file", new_conf], text=True)
Create.encrypt()
else:
shutil.copy(filepath, f"{AppConfig.TEMP_DIR}/")
if self.a != "":
check_call(["nmcli", "connection", "down", self.a])
self.reset_fields()
subprocess.check_output(["nmcli", "connection", "import", "type", "wireguard", "file", filepath], text=True)
Create.encrypt()
self.str_var.set("")
self.a = Tunnel.active()
self.l_box.insert(0, self.a)
self.wg_autostart.configure(state="normal")
self.l_box.selection_clear(0, tk.END)
self.l_box.update()
self.l_box.selection_set(0)
Tooltip(self.wg_autostart, Msg.TTIP["autostart"], self.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)
self.lb_rename.insert(0, "Max. 12 characters!")
self.str_var = tk.StringVar()
self.str_var.set(self.a)
self.color_label()
self.stop()
data = self.handle_tunnel_data(self.a)
check_call(["nmcli", "con", "mod", self.a, "connection.autoconnect", "no"])
elif ("PrivateKey = " in read) and ("Endpoint = " in read):
pass
else:
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_error"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["imp_err"], Msg.STR["no_valid_file"])
except EOFError as e:
print(e)
except TypeError:
print("File import: abort by user...")
except FileNotFoundError:
print("File import: abort by user...")
except subprocess.CalledProcessError:
print("Tunnel exist!")
def delete(self) -> None:
"""
delete Wireguard Tunnel
"""
try:
self.select_tunnel = self.l_box.curselection()
select_tl = self.l_box.get(self.select_tunnel[0])
with open(f"/tmp/tlecdcwg/{select_tl}.conf", "r+", encoding="utf-8") as file2:
key = Tunnel.con_to_dict(file2)
pre_key = key[3]
check_call(["nmcli", "connection", "delete", select_tl])
self.l_box.delete(self.select_tunnel[0])
with open(AppConfig.SETTINGS_FILE, "r", encoding="utf-8") as set_f6:
lines6 = set_f6.readlines()
if (select_tl == lines6[7].strip()
and "off\n" not in lines6[7].strip()):
lines6[7] = "off\n"
with open(AppConfig.SETTINGS_FILE, "w", encoding="utf-8") as set_f7:
set_f7.writelines(lines6)
self.selected_option.set(0)
self.autoconnect_var.set(_("no Autoconnect"))
is_encrypt = Path.home() / f".config/wire_py/{select_tl}.dat"
if is_encrypt.is_file():
Path.unlink(f"{Path.home()}/.config/wire_py/{select_tl}.dat")
Path.unlink(f"/tmp/tlecdcwg/{select_tl}.conf")
with open(AppConfig.KEYS_FILE, "r", encoding="utf-8") as readfile:
with open(f"{Path.home()}/.config/wire_py/keys2", "w", encoding="utf-8") as writefile:
for line in readfile:
if pre_key not in line.strip("\n"):
writefile.write(line)
file_one = Path(f"{Path.home()}/.config/wire_py/keys2")
file_two = file_one.with_name("keys")
file_one.replace(file_two)
self.wg_autostart.configure(state="disabled")
# for disabling checkbox when Listbox empty
if self.l_box.size() == 0:
self.wg_autostart.configure(state="disabled")
self.lb_rename.configure(state="disabled")
Tooltip(self.wg_autostart, 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)
Tooltip(self.lb_rename, Msg.TTIP["rename_tl_info"], self.tooltip_state)
self.lb_rename.insert(0, _("Max. 12 characters!"))
if self.a != "" and self.a == select_tl:
self.str_var.set(value="")
self.start()
self.l_box.update()
self.reset_fields()
except IndexError:
if self.l_box.size() != 0:
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["sel_tl"], Msg.STR["sel_list"])
else:
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["sel_tl"], Msg.STR["tl_first"])
def enable_check_box(self, _) -> None:
"""
checkbox for enable autostart Tunnel
"""
Create.files_for_autostart()
if self.l_box.size() != 0:
self.wg_autostart.configure(state="normal")
self.lb_rename.config(state="normal")
self.lb_rename.delete(0, tk.END)
self.btn_rename.config(state="normal")
def on_off(self) -> None:
"""
Here it is checked whether the path to the file is there, if not, it is created.
Set (on), the selected tunnel is displayed in the label.
At (off) the label is first emptied then filled with No Autoconnect
"""
lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True)
if lines[7] != "off\n":
print(f"{lines[7]} starts automatically when the system starts.")
self.selected_option.set(1)
self.autoconnect_var.set("")
self.auto_con = lines[7]
else:
self.selected_option.set(0)
self.auto_con = _("no Autoconnect")
print("Autostart disabled.")
self.autoconnect_var.set("")
self.autoconnect_var = tk.StringVar()
self.autoconnect_var.set(self.auto_con)
self.autoconnect = ttk.Label(self.lb_frame3, textvariable=self.autoconnect_var, foreground="#0071ff", width=15)
self.autoconnect.config(font=("Ubuntu", 11))
self.autoconnect.grid(column=1, row=0, sticky="e", pady=19)
def box_set(self) -> None:
"""
Configures the autostart for a selected tunnel.
This method is called when the user changes the autostart checkbox.
It saves the selected tunnel in the configuration file so that it
will be automatically connected at system startup.
If the checkbox is deactivated, 'off' is written to the configuration file
to disable the autostart.
"""
try:
select_tunnel = self.l_box.curselection()
select_tl = self.l_box.get(select_tunnel[0])
if self.selected_option.get() == 0:
lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True)
lines[7] = 'off\n'
Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8")
tl = LxTools.get_file_name(AppConfig.TEMP_DIR)
if len(tl) == 0:
self.wg_autostart.configure(state="disabled")
if self.selected_option.get() >= 1:
lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True)
lines[7] = select_tl
Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8")
except IndexError:
self.selected_option.set(1)
self.on_off()
def tl_rename(self) -> None:
"""
method to rename a tunnel
"""
name_of_file = LxTools.get_file_name(AppConfig.TEMP_DIR)
special_characters = ["\\", "/", "{", "}", " "]
if len(self.lb_rename.get()) > 12:
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["ren_err"], Msg.STR["sign_len"])
elif len(self.lb_rename.get()) == 0:
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["ren_err"], Msg.STR["zero_signs"])
elif any(ch in special_characters for ch in self.lb_rename.get()):
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["ren_err"], Msg.STR["false_signs"])
elif self.lb_rename.get() in name_of_file:
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["ren_err"], Msg.STR["is_in_use"])
else:
try:
self.select_tunnel = self.l_box.curselection()
select_tl = self.l_box.get(self.select_tunnel[0])
# nmcli connection modify old connection.id iphone
subprocess.check_output(["nmcli", "connection", "modify", select_tl, "connection.id", self.lb_rename.get()], text=True)
source = Path(f"/tmp/tlecdcwg/{select_tl}.conf")
destination = source.with_name(f"{self.lb_rename.get()}.conf")
source.replace(destination)
Path.unlink(f"{Path.home()}/.config/wire_py/{select_tl}.dat")
self.l_box.delete(self.select_tunnel[0])
self.l_box.insert("end", self.lb_rename.get())
self.l_box.update()
new_a_connect = self.lb_rename.get()
self.lb_rename.delete(0, tk.END)
with open(AppConfig.SETTINGS_FILE, "r", encoding="utf-8") as set_f5:
lines5 = set_f5.readlines()
if select_tl == lines5[7].strip() and "off\n" not in lines5[7].strip():
lines5[7] = new_a_connect
with open(AppConfig.SETTINGS_FILE, "w", encoding="utf-8") as theme_set5:
theme_set5.writelines(lines5)
self.autoconnect_var.set(value=new_a_connect)
self.update_connection_display()
Create.encrypt()
except IndexError:
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["ren_err"], Msg.STR["sel_list"])
except subprocess.CalledProcessError:
pass
except EOFError as e:
print(e)
def init_and_report(self, data=None) -> None:
"""
Displays the value address, DNS and peer in the labels
or empty it again
"""
# Address Label
self.add = tk.StringVar()
self.add.set(f"{_("Address: ")}{data[0]}")
self.DNS = tk.StringVar()
self.DNS.set(f" DNS: {data[1]}")
self.enp = tk.StringVar()
self.enp.set(f"{_("Endpoint: ")}{data[2]}")
def show_data(self) -> None:
"""
shows data in the label
"""
# Address Label
self.address = ttk.Label(self.lb_frame, textvariable=self.add, foreground="#0071ff")
self.address.grid(column=0, row=5, sticky="w", padx=10, pady=6)
self.address.config(font=("Ubuntu", 9))
# DNS Label
self.dns = ttk.Label(self.lb_frame, textvariable=self.DNS, foreground="#0071ff")
self.dns.grid(column=0, row=7, sticky="w", padx=10, pady=6)
self.dns.config(font=("Ubuntu", 9))
# Endpoint Label
self.endpoint = ttk.Label(self.lb_frame2, textvariable=self.enp, foreground="#0071ff")
self.endpoint.grid(column=0, row=8, sticky="w", padx=10, pady=20)
self.endpoint.config(font=("Ubuntu", 9))
def wg_switch(self, event=None) -> None:
"""
Deals with switching the VPN connection
"""
try:
if self.a == "":
self.select_tunnel = self.l_box.curselection()
select_tl = self.l_box.get(self.select_tunnel[0])
self.handle_connection_state("start", select_tl)
else:
data = self.handle_tunnel_data(self.a)
if data:
self.handle_connection_state("stop")
except IndexError:
if self.l_box.size() != 0:
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["sel_tl"], Msg.STR["sel_list"])
else:
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["sel_tl"], Msg.STR["tl_first"])
def handle_connection_state(self, action: str, tunnel_name: str = None) -> None:
"""
central management for connection states
Args:
action (str): "start", "stop" or "toggle"
tunnel_name (str, optional): name of a tunnel for a start-option. defaults to None.
"""
if action == "stop":
if self.a:
check_call(["nmcli", "connection", "down", self.a])
self.update_connection_display()
self.reset_fields()
self.start()
elif action == "start":
if tunnel_name or self.a:
target_tunnel = tunnel_name or self.a
check_call(["nmcli", "connection", "up", target_tunnel])
self.update_connection_display()
data = self.handle_tunnel_data(self.a)
self.init_and_report(data)
self.show_data()
self.color_label()
self.stop()
elif action == "toggle":
if self.a:
self.handle_connection_state("stop")
else:
self.handle_connection_state("start")
def update_connection_display(self) -> None:
"""
Updated the display after connection changes
"""
self.a = Tunnel.active()
if not hasattr(self, "str_var"):
self.str_var = tk.StringVar()
self.str_var.set(self.a)
self.color_label()
self.show_data()
if __name__ == "__main__":
_ = AppConfig.setup_translations()
LxTools.sigi(AppConfig.TEMP_DIR, AppConfig.USER_FILE)
window = Wirepy()
"""
the hidden files are hidden in Filedialog
"""
try:
window.tk.call("tk_getOpenFile", "-foobarbaz")
except TclError:
pass
window.tk.call("set", "::tk::dialog::file::showHiddenBtn", "0")
window.tk.call("set", "::tk::dialog::file::showHiddenVar", "0")
window.mainloop()
LxTools.clean_files(AppConfig.TEMP_DIR, AppConfig.USER_FILE)
sys.exit(0)