From 5cfabb68dbd5038e78cada766b911b5aaa31d6f7 Mon Sep 17 00:00:00 2001 From: Marcel Engelke Date: Sun, 24 Mar 2024 02:40:18 +0100 Subject: [PATCH 1/4] fix(#367): decode server feature bit flags --- protonvpn_cli/connection.py | 8 +++----- protonvpn_cli/constants.py | 6 ++++++ protonvpn_cli/utils.py | 11 +++++++++-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/protonvpn_cli/connection.py b/protonvpn_cli/connection.py index f93cd95..4c63aa4 100644 --- a/protonvpn_cli/connection.py +++ b/protonvpn_cli/connection.py @@ -14,7 +14,7 @@ # protonvpn-cli Functions from .logger import logger from .utils import ( - check_init, pull_server_data, is_connected, + check_init, get_server_features, pull_server_data, is_connected, get_servers, get_server_value, get_config_value, set_config_value, get_ip_info, get_country_name, get_fastest_server, check_update, get_default_nic, @@ -412,14 +412,12 @@ def status(): ip, isp = get_ip_info() # Collect Information - all_features = {0: "Normal", 1: "Secure-Core", 2: "Tor", 4: "P2P"} - logger.debug("Collecting status information") country_code = get_server_value(connected_server, "ExitCountry", servers) country = get_country_name(country_code) city = get_server_value(connected_server, "City", servers) load = get_server_value(connected_server, "Load", servers) - feature = get_server_value(connected_server, "Features", servers) + features = get_server_features(connected_server, servers) last_connection = get_config_value("metadata", "connected_time") connection_time = time.time() - int(last_connection) @@ -441,7 +439,7 @@ def status(): + "Time: {0}\n".format(connection_time) + "IP: {0}\n".format(ip) + "Server: {0}\n".format(connected_server) - + "Features: {0}\n".format(all_features[feature]) + + "Features: {0}\n".format(', '.join(features)) + "Protocol: {0}\n".format(connected_protocol.upper()) + "Kill Switch: {0}\n".format(killswitch_status) + "Country: {0}\n".format(country) diff --git a/protonvpn_cli/constants.py b/protonvpn_cli/constants.py index c29eec7..54c7fc6 100644 --- a/protonvpn_cli/constants.py +++ b/protonvpn_cli/constants.py @@ -14,6 +14,12 @@ CONFIG_DIR = os.path.join(os.path.expanduser("~{0}".format(USER)), ".pvpn-cli") CONFIG_FILE = os.path.join(CONFIG_DIR, "pvpn-cli.cfg") SERVER_INFO_FILE = os.path.join(CONFIG_DIR, "serverinfo.json") +SERVER_FEATURES = { + 1: "Secure-Core", + 2: "Tor", + 4: "P2P", + 8: "Streaming", +} SPLIT_TUNNEL_FILE = os.path.join(CONFIG_DIR, "split_tunnel.txt") OVPN_FILE = os.path.join(CONFIG_DIR, "connect.ovpn") PASSFILE = os.path.join(CONFIG_DIR, "pvpnpass") diff --git a/protonvpn_cli/utils.py b/protonvpn_cli/utils.py index 41a6bc2..7fee1b1 100644 --- a/protonvpn_cli/utils.py +++ b/protonvpn_cli/utils.py @@ -16,7 +16,7 @@ from .logger import logger # Constants from .constants import ( - USER, CONFIG_FILE, SERVER_INFO_FILE, SPLIT_TUNNEL_FILE, + USER, CONFIG_FILE, SERVER_INFO_FILE, SERVER_FEATURES, SPLIT_TUNNEL_FILE, VERSION, OVPN_FILE, CLIENT_SUFFIX ) import distro @@ -115,12 +115,19 @@ def get_servers(): # Sort server IDs by Tier return [server for server in servers if server["Tier"] <= user_tier and server["Status"] == 1] # noqa - def get_server_value(servername, key, servers): """Return the value of a key for a given server.""" value = [server[key] for server in servers if server['Name'] == servername] return value[0] +def get_server_features(servername, servers): + """Decode server feature bit flags and return feature strings in list""" + feat = int(get_server_value(servername, "Features", servers)) + server_features = [] + for bit_flag in SERVER_FEATURES: + if (feat & bit_flag): + server_features.append(SERVER_FEATURES[bit_flag]) + return server_features def get_config_value(group, key): """Return specific value from CONFIG_FILE as string""" From 92353fc4335f04ba7beb07951e614713a58f7a15 Mon Sep 17 00:00:00 2001 From: Marcel Engelke Date: Sun, 24 Mar 2024 02:48:34 +0100 Subject: [PATCH 2/4] refactor(#367): use new fn with dialog --- protonvpn_cli/connection.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/protonvpn_cli/connection.py b/protonvpn_cli/connection.py index 4c63aa4..77bc85f 100644 --- a/protonvpn_cli/connection.py +++ b/protonvpn_cli/connection.py @@ -80,11 +80,10 @@ def show_dialog(headline, choices, stop=False): for country in sorted(countries.keys()): country_features = [] for server in countries[country]: - feat = int(get_server_value(server, "Features", servers)) - for bit_flag in features: - if (feat & bit_flag) != 0: - if not features[bit_flag] in country_features: - country_features.append(features[bit_flag]) + server_features = get_server_features(server, servers) + for feat in server_features: + if not feat in country_features: + country_features.append(feat) if len(country_features) == 0: country_features.append("Normal") From d5b8465e7c488bc39dc1ea0ed580e1f92656113e Mon Sep 17 00:00:00 2001 From: Marcel Engelke Date: Sun, 24 Mar 2024 03:03:25 +0100 Subject: [PATCH 3/4] refactor(#367): use new fn with 2nd dialog --- protonvpn_cli/connection.py | 18 ++++-------------- protonvpn_cli/utils.py | 3 +++ 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/protonvpn_cli/connection.py b/protonvpn_cli/connection.py index 77bc85f..284c8c2 100644 --- a/protonvpn_cli/connection.py +++ b/protonvpn_cli/connection.py @@ -57,12 +57,6 @@ def show_dialog(headline, choices, stop=False): pull_server_data() - features = { - 1: "Secure-Core", - 2: "Tor", - 4: "P2P", - 8: "Streaming", - } server_tiers = {0: "F", 1: "B", 2: "P"} servers = get_servers() @@ -106,21 +100,17 @@ def show_dialog(headline, choices, stop=False): get_server_value(servername, "Load", servers) ).rjust(3, " ") - servers_features = [] - feat = int(get_server_value(servername, 'Features', servers)) - for bit_flag in features: - if (feat & bit_flag) != 0: - servers_features.append(features[bit_flag]) + server_features = get_server_features(servername, servers) - if len(servers_features) == 0: - servers_features.append("Normal") + if len(server_features) == 0: + server_features.append("Normal") tier = server_tiers[ get_server_value(servername, "Tier", servers) ] choices.append((servername, "Load: {0}% | {1} | {2}".format( - load, tier, ", ".join(servers_features) + load, tier, ", ".join(server_features) ))) server_result = show_dialog("Choose the server to connect:", choices) diff --git a/protonvpn_cli/utils.py b/protonvpn_cli/utils.py index 7fee1b1..49b0da4 100644 --- a/protonvpn_cli/utils.py +++ b/protonvpn_cli/utils.py @@ -115,11 +115,13 @@ def get_servers(): # Sort server IDs by Tier return [server for server in servers if server["Tier"] <= user_tier and server["Status"] == 1] # noqa + def get_server_value(servername, key, servers): """Return the value of a key for a given server.""" value = [server[key] for server in servers if server['Name'] == servername] return value[0] + def get_server_features(servername, servers): """Decode server feature bit flags and return feature strings in list""" feat = int(get_server_value(servername, "Features", servers)) @@ -129,6 +131,7 @@ def get_server_features(servername, servers): server_features.append(SERVER_FEATURES[bit_flag]) return server_features + def get_config_value(group, key): """Return specific value from CONFIG_FILE as string""" config = configparser.ConfigParser() From ca6261bb9fc98028286a998eb14fd9c37d22c96c Mon Sep 17 00:00:00 2001 From: zPhoeniqz <72757232+zPhoeniqz@users.noreply.github.com> Date: Mon, 9 Dec 2024 23:29:14 +0100 Subject: [PATCH 4/4] Add IPv6 to server features --- protonvpn_cli/constants.py | 1 + 1 file changed, 1 insertion(+) diff --git a/protonvpn_cli/constants.py b/protonvpn_cli/constants.py index 54c7fc6..5227898 100644 --- a/protonvpn_cli/constants.py +++ b/protonvpn_cli/constants.py @@ -19,6 +19,7 @@ 2: "Tor", 4: "P2P", 8: "Streaming", + 16: "IPv6" } SPLIT_TUNNEL_FILE = os.path.join(CONFIG_DIR, "split_tunnel.txt") OVPN_FILE = os.path.join(CONFIG_DIR, "connect.ovpn")