Skip to content
This repository was archived by the owner on Nov 5, 2025. It is now read-only.
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 39 additions & 95 deletions protonvpn_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

Usage:
protonvpn init
protonvpn usage
protonvpn (c | connect) [<servername>] [-p <protocol>]
protonvpn (c | connect) [-f | --fastest] [-p <protocol>]
protonvpn (c | connect) [--cc <code>] [-p <protocol>]
Expand Down Expand Up @@ -32,6 +33,7 @@

Commands:
init Initialize a ProtonVPN profile.
usage Open the ProtonVPN-CLI usage guide.
c, connect Connect to a ProtonVPN server.
r, reconnect Reconnect to the last server.
d, disconnect Disconnect the current session.
Expand All @@ -51,15 +53,15 @@
import getpass
import shutil
import time
import webbrowser
# External Libraries
from docopt import docopt
# protonvpn-cli Functions
from . import connection
from .logger import logger
from .utils import (
check_root, change_file_owner, pull_server_data, make_ovpn_template,
check_init, set_config_value, get_config_value, is_valid_ip,
wait_for_network
check_init, set_config_value, get_config_value, is_valid_ip
)
# Constants
from .constants import (
Expand All @@ -78,34 +80,30 @@ def main():

def cli():
"""Run user's input command."""
args = docopt(__doc__, version=VERSION)

# Initial log values
change_file_owner(os.path.join(CONFIG_DIR, "pvpn-cli.log"))
logger.debug("###########################")
logger.debug("### NEW PROCESS STARTED ###")
logger.debug("###########################")
logger.debug(sys.argv)
logger.debug("Arguments\n{0}".format(args))
logger.debug("USER: {0}".format(USER))
logger.debug("CONFIG_DIR: {0}".format(CONFIG_DIR))

args = docopt(__doc__, version="ProtonVPN-CLI v{0}".format(VERSION))
logger.debug("Arguments\n{0}".format(str(args).replace("\n", "")))

# Parse arguments
if args.get("init"):
init_cli()
elif args.get("usage"):
webbrowser.open(
"https://github.com/ProtonVPN/protonvpn-cli-ng/blob/master/USAGE.md" #noqa
)
print("Usage guide has been opened in your web browser.")
elif args.get("c") or args.get("connect"):
check_root()
check_init()

# Wait until a connection to the ProtonVPN API can be made
# As this is mainly for automatically connecting on boot, it only
# activates when the environment variable PVPN_WAIT is 1
# Otherwise it wouldn't connect when a VPN process without
# internet access exists or the Kill Switch is active
if int(os.environ.get("PVPN_WAIT", 0)) > 0:
wait_for_network(int(os.environ["PVPN_WAIT"]))

protocol = args.get("-p")
if protocol is not None and protocol.lower().strip() in ["tcp", "udp"]:
protocol = protocol.lower().strip()
Expand Down Expand Up @@ -139,7 +137,7 @@ def cli():
connection.status()
elif args.get("configure"):
check_root()
check_init()
check_init(check_props=False)
configure_cli()
elif args.get("refresh"):
pull_server_data(force=True)
Expand Down Expand Up @@ -190,9 +188,6 @@ def init_config_file():
if overwrite.strip().lower() != "y":
print("Quitting...")
sys.exit(1)
# Disconnect, so every setting (Kill Switch, IPv6, ...)
# will be reverted (See #62)
connection.disconnect(passed=True)
except KeyError:
pass

Expand Down Expand Up @@ -224,11 +219,11 @@ def init_config_file():

print()
print(
"You entered the following information:\n" +
"Username: {0}\n".format(ovpn_username) +
"Password: {0}\n".format("*" * len(ovpn_password)) +
"Tier: {0}\n".format(protonvpn_plans[user_tier]) +
"Default protocol: {0}".format(user_protocol.upper())
"You entered the following information:\n",
"Username: {0}\n".format(ovpn_username),
"Password: {0}\n".format("*" * len(ovpn_password)),
"Tier: {0}\n".format(protonvpn_plans[user_tier]),
"Default protocol: {0}\n".format(user_protocol.upper()),
)
print()

Expand Down Expand Up @@ -306,7 +301,8 @@ def print_examples():
def configure_cli():
"""Change single configuration values"""

while True:
exit_cli = False
while exit_cli is not True:
print(
"What do you want to change?\n"
"\n"
Expand All @@ -317,6 +313,7 @@ def configure_cli():
"5) Kill Switch\n"
"6) Split Tunneling\n"
"7) Purge Configuration\n"
"8) Exit Application\n"
)

user_choice = input(
Expand All @@ -326,27 +323,20 @@ def configure_cli():
user_choice = user_choice.lower().strip()
if user_choice == "1":
set_username_password(write=True)
break
elif user_choice == "2":
set_protonvpn_tier(write=True)
break
elif user_choice == "3":
set_default_protocol(write=True)
break
elif user_choice == "4":
set_dns_protection()
break
elif user_choice == "5":
set_killswitch()
break
set_killswitch(write=True)
elif user_choice == "6":
set_split_tunnel()
break
# Make sure this is always the last option
elif user_choice == "7":
purge_configuration()
break
elif user_choice == "":
elif user_choice == "8":
print("Quitting configuration.")
sys.exit(0)
else:
Expand Down Expand Up @@ -484,7 +474,7 @@ def set_default_protocol(write=False):
)

if write:
set_config_value("USER", "default_protocol", user_protocol)
set_config_value("USER", "default_protocl", user_protocol)
print("Default protocol has been updated.")

return user_protocol
Expand Down Expand Up @@ -549,56 +539,25 @@ def set_dns_protection():
print("DNS Management updated.")


def set_killswitch():
def set_killswitch(write=False):
"""Enable or disable the Kill Switch."""

while True:
print()
print(
"The Kill Switch will block all network traffic\n"
"if the VPN connection drops unexpectedly.\n"
"\n"
"Please note that the Kill Switch assumes only one network interface being active.\n" # noqa
"\n"
"1) Enable Kill Switch (Block access to/from LAN)\n"
"2) Enable Kill Switch (Allow access to/from LAN)\n"
"3) Disable Kill Switch"
)
print()
user_choice = input(
"Please enter your choice or leave empty to quit: "
)
user_choice = user_choice.lower().strip()
if user_choice == "1":
killswitch = 1
break
elif user_choice == "2":
killswitch = 2
break
elif user_choice == "3":
killswitch = 0
break
elif user_choice == "":
print("Quitting configuration.")
sys.exit(0)
else:
print(
"[!] Invalid choice. Please enter the number of your choice.\n"
)
time.sleep(0.5)
print(
"The Kill Switch will block all network traffic\n"
"if the VPN connection drops unexpectedly."
)
print()
user_choice = input("Enable VPN Kill Switch? [y/N]: ")

if killswitch and int(get_config_value("USER", "split_tunnel")):
set_config_value("USER", "split_tunnel", 0)
print()
print(
"[!] Kill Switch can't be used with Split Tunneling.\n" +
"[!] Split Tunneling has been disabled."
)
time.sleep(1)
if user_choice.strip().lower() == "y":
killswitch = 1
else:
killswitch = 0

set_config_value("USER", "killswitch", killswitch)
print()
print("Kill Switch configuration updated.")
if write:
set_config_value("USER", "killswitch", killswitch)
print()
print("Kill Switch configuration updated.")


def set_split_tunnel():
Expand All @@ -608,15 +567,6 @@ def set_split_tunnel():
user_choice = input("Enable split tunneling? [y/N]: ")

if user_choice.strip().lower() == "y":
if int(get_config_value("USER", "killswitch")):
set_config_value("USER", "killswitch", 0)
print()
print(
"[!] Split Tunneling can't be used with Kill Switch.\n" +
"[!] Kill Switch has been disabled.\n"
)
time.sleep(1)

set_config_value("USER", "split_tunnel", 1)

while True:
Expand All @@ -636,13 +586,7 @@ def set_split_tunnel():
with open(SPLIT_TUNNEL_FILE, "a") as f:
f.write("\n{0}".format(ip))

if os.path.isfile(SPLIT_TUNNEL_FILE):
change_file_owner(SPLIT_TUNNEL_FILE)
else:
# If no no config file exists,
# split tunneling should be disabled again
logger.debug("No split tunneling file existing.")
set_config_value("USER", "split_tunnel", 0)
change_file_owner(SPLIT_TUNNEL_FILE)

else:
set_config_value("USER", "split_tunnel", 0)
Expand Down