Skip to content

Skip update check if not on TTY #212

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ For more detailed information on installing, updating and uninstalling, please v

For the following Linux distribution(s), install the official `protonvpn-cli` package:

#### Guix

```sh
guix install protonvpn-cli
```

#### Fedora

```sh
Expand Down Expand Up @@ -68,6 +74,7 @@ Depending on your distribution, run the appropriate following command to install
|Ubuntu/Linux Mint/Debian and derivatives | `sudo apt install -y openvpn dialog python3-pip python3-setuptools`|
|OpenSUSE/SLES | `sudo zypper in -y openvpn dialog python3-pip python3-setuptools` |
|Arch Linux/Manjaro | `sudo pacman -S openvpn dialog python-pip python-setuptools` |
|Guix | `guix environment protonvpn-cli` |

#### Installing ProtonVPN-CLI

Expand Down
8 changes: 8 additions & 0 deletions USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This document provides an extensive guide on how to install and use ProtonVPN-CL
- [Table of Contents](#table-of-contents)
- [Installation & Updating](#installation--updating)
- [Installing from distribution repositories](#installing-from-distribution-repositories)
- [Guix](#guix)
- [Fedora](#fedora)
- [CentOS & RHEL](#centos--rhel)
- [Installing from PyPI](#installing-from-pypi)
Expand Down Expand Up @@ -43,6 +44,12 @@ This document provides an extensive guide on how to install and use ProtonVPN-CL

For the following Linux distribution(s), install the official `protonvpn-cli` package:

#### Guix

```sh
guix install protonvpn-cli
```

#### Fedora

```sh
Expand Down Expand Up @@ -85,6 +92,7 @@ Depending on your distribution, run the appropriate following command to install
|Ubuntu/Linux Mint/Debian and derivatives | `sudo apt install -y openvpn dialog python3-pip python3-setuptools`|
|OpenSUSE/SLES | `sudo zypper in -y openvpn dialog python3-pip python3-setuptools` |
|Arch Linux/Manjaro | `sudo pacman -S openvpn dialog python-pip python-setuptools` |
|Guix | `guix environment protonvpn-cli` |

#### Installing ProtonVPN-CLI

Expand Down
203 changes: 129 additions & 74 deletions protonvpn_cli/cli.py
Original file line number Diff line number Diff line change
@@ -1,48 +1,3 @@
"""
A CLI for ProtonVPN.

Usage:
protonvpn init
protonvpn (c | connect) [<servername>] [-p <protocol>]
protonvpn (c | connect) [-f | --fastest] [-p <protocol>]
protonvpn (c | connect) [--cc <code>] [-p <protocol>]
protonvpn (c | connect) [--sc] [-p <protocol>]
protonvpn (c | connect) [--p2p] [-p <protocol>]
protonvpn (c | connect) [--tor] [-p <protocol>]
protonvpn (c | connect) [-r | --random] [-p <protocol>]
protonvpn (r | reconnect)
protonvpn (d | disconnect)
protonvpn (s | status)
protonvpn configure
protonvpn refresh
protonvpn examples
protonvpn (-h | --help)
protonvpn (-v | --version)

Options:
-f, --fastest Select the fastest ProtonVPN server.
-r, --random Select a random ProtonVPN server.
--cc CODE Determine the country for fastest connect.
--sc Connect to the fastest Secure-Core server.
--p2p Connect to the fastest torrent server.
--tor Connect to the fastest Tor server.
-p PROTOCOL Determine the protocol (UDP or TCP).
-h, --help Show this help message.
-v, --version Display version.

Commands:
init Initialize a ProtonVPN profile.
c, connect Connect to a ProtonVPN server.
r, reconnect Reconnect to the last server.
d, disconnect Disconnect the current session.
s, status Show connection status.
configure Change ProtonVPN-CLI configuration.
refresh Refresh OpenVPN configuration and server data.
examples Print some example commands.

Arguments:
<servername> Servername (CH#4, CH-US-1, HK5-Tor).
"""
# Standard Libraries
import sys
import os
Expand All @@ -51,8 +6,7 @@
import getpass
import shutil
import time
# External Libraries
from docopt import docopt
import argparse
# protonvpn-cli Functions
from . import connection
from .logger import logger
Expand All @@ -63,7 +17,7 @@
)
# Constants
from .constants import (
CONFIG_DIR, CONFIG_FILE, PASSFILE, USER, VERSION, SPLIT_TUNNEL_FILE
CONFIG_DIR, CONFIG_FILE, PASSFILE, USER, VERSION, SPLIT_TUNNEL_FILE, USAGE
)


Expand All @@ -88,13 +42,62 @@ def cli():
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", "")))
ProtonVPNCLI()


class ProtonVPNCLI():
server_features_dict = dict(
p2p=4,
sc=1,
tor=2
)

def __init__(self):
parser = argparse.ArgumentParser(
prog="protonvpn",
add_help=False
)

parser.add_argument("command", nargs="?")
parser.add_argument("-v", "--version", required=False, action="store_true")
parser.add_argument("-h", "--help", required=False, action="store_true")

args = parser.parse_args(sys.argv[1:2])

logger.debug("Main argument\n{0}".format(args))

if args.version:
print("\nProtonVPN CLI v.{}".format(VERSION))
parser.exit(1)
elif not args.command or not hasattr(self, args.command) or args.help:
print(USAGE)
parser.exit(1)

getattr(self, args.command)()

def init(self):
"""CLI command that intialiazes ProtonVPN profile"""
parser = argparse.ArgumentParser(description="Initialize ProtonVPN profile", prog="protonvpn init")
parser.add_argument(
"-i", "--inline", nargs=3, required=False,
help="Inline intialize profile. (username password protocol)", metavar=""
)

args = parser.parse_args(sys.argv[2:])
logger.debug("Sub-arguments\n{0}".format(args))

if args.inline:
print("Please intialize without '-i/--inline' as it is not fully supported yet.")
sys.exit(1)

# Parse arguments
if args.get("init"):
init_cli()
elif args.get("c") or args.get("connect"):

def c(self):
"""Short CLI command for connecting to the VPN"""
self.connect()

def connect(self):
"""Full CLI command for connecting to the VPN"""
check_root()
check_init()

Expand All @@ -106,45 +109,97 @@ def cli():
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"]:
parser = argparse.ArgumentParser(description="Connect to ProtonVPN", prog="protonvpn c")
group = parser.add_mutually_exclusive_group()
group.add_argument("servername", nargs="?", help="Servername (CH#4, CH-US-1, HK5-Tor).", metavar="")
group.add_argument("-f", "--fastest", help="Connect to the fastest ProtonVPN server.", action="store_true")
group.add_argument("-r", "--random", help="Connect to a random ProtonVPN server.", action="store_true")
group.add_argument("--cc", help="Connect to the specified country code (SE, PT, BR, AR).", metavar="")
group.add_argument("--sc", help="Connect to the fastest Secure-Core server.", action="store_true")
group.add_argument("--p2p", help="Connect to the fastest torrent server.", action="store_true")
group.add_argument("--tor", help="Connect to the fastest Tor server.", action="store_true")
parser.add_argument(
"-p", "--protocol", help="Connect via specified protocol.",
choices=["udp", "tcp"], metavar="", type=str.lower
)

args = parser.parse_args(sys.argv[2:])
logger.debug("Sub-arguments:\n{0}".format(args))

protocol = args.protocol
if protocol and protocol.lower().strip() in ["tcp", "udp"]:
protocol = protocol.lower().strip()

if args.get("--random"):
if args.random:
connection.random_c(protocol)
elif args.get("--fastest"):
elif args.fastest:
connection.fastest(protocol)
elif args.get("<servername>"):
connection.direct(args.get("<servername>"), protocol)
elif args.get("--cc") is not None:
connection.country_f(args.get("--cc"), protocol)
# Features: 1: Secure-Core, 2: Tor, 4: P2P
elif args.get("--p2p"):
connection.feature_f(4, protocol)
elif args.get("--sc"):
connection.feature_f(1, protocol)
elif args.get("--tor"):
connection.feature_f(2, protocol)
elif args.servername:
connection.direct(args.servername, protocol)
elif args.cc:
connection.country_f(args.cc, protocol)
elif args.p2p:
connection.feature_f(self.server_features_dict.get("p2p", None), protocol)
elif args.sc:
connection.feature_f(self.server_features_dict.get("sc", None), protocol)
elif args.tor:
connection.feature_f(self.server_features_dict.get("tor", None), protocol)
else:
connection.dialog()
elif args.get("r") or args.get("reconnect"):

def r(self):
"""Short CLI command to reconnect to the last connected VPN Server"""
self.reconnect()

def reconnect(self):
"""Full CLI command to reconnect to the last connected VPN Server"""
check_root()
check_init()
connection.reconnect()
elif args.get("d") or args.get("disconnect"):

def d(self):
"""Short CLI command to disconnect the VPN if a connection is present"""
self.disconnect()

def disconnect(self):
"""Full CLI command to disconnect the VPN if a connection is present"""
check_root()
check_init()
connection.disconnect()
elif args.get("s") or args.get("status"):

def s(self):
"""Short CLI command to display the current VPN status"""
self.status()

def status(self):
"""Full CLI command to display the current VPN status"""
connection.status()
elif args.get("configure"):

def cf(self):
"""Short CLI command to change single configuration values"""
self.configure()

def configure(self):
"""Full CLI command to change single configuration values"""
check_root()
check_init()
configure_cli()
elif args.get("refresh"):

def rf(self):
"""Short CLI command to refresh server list"""
self.refresh()

def refresh(self):
"""Full CLI command to refresh server list"""
check_init()
pull_server_data(force=True)
elif args.get("examples"):

def ex(self):
"""Short CLI command to display usage examples"""
self.examples()

def examples(self):
"""Full CLI command to display usage examples"""
print_examples()


Expand Down
46 changes: 46 additions & 0 deletions protonvpn_cli/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,49 @@
OVPN_FILE = os.path.join(CONFIG_DIR, "connect.ovpn")
PASSFILE = os.path.join(CONFIG_DIR, "pvpnpass")
VERSION = "2.2.4"

USAGE = """
ProtonVPN CLI

Usage:
protonvpn init
protonvpn (c | connect) [<servername>] [-p <protocol>]
protonvpn (c | connect) [-f | --fastest] [-p <protocol>]
protonvpn (c | connect) [--cc <code>] [-p <protocol>]
protonvpn (c | connect) [--sc] [-p <protocol>]
protonvpn (c | connect) [--p2p] [-p <protocol>]
protonvpn (c | connect) [--tor] [-p <protocol>]
protonvpn (c | connect) [-r | --random] [-p <protocol>]
protonvpn (r | reconnect)
protonvpn (d | disconnect)
protonvpn (s | status)
protonvpn (cf | configure)
protonvpn (rf | refresh)
protonvpn (ex | examples)
protonvpn (-h | --help)
protonvpn (-v | --version)

Options:
-f, --fastest Select the fastest ProtonVPN server.
-r, --random Select a random ProtonVPN server.
--cc CODE Determine the country for fastest connect.
--sc Connect to the fastest Secure-Core server.
--p2p Connect to the fastest torrent server.
--tor Connect to the fastest Tor server.
-p PROTOCOL Determine the protocol (UDP or TCP).
-h, --help Show this help message.
-v, --version Display version.

Commands:
init Initialize a ProtonVPN profile.
c, connect Connect to a ProtonVPN server.
r, reconnect Reconnect to the last server.
d, disconnect Disconnect the current session.
s, status Show connection status.
cf, configure Change ProtonVPN-CLI configuration.
rf, refresh Refresh OpenVPN configuration and server data.
ex, examples Print some example commands.

Arguments:
<servername> Servername (CH#4, CH-US-1, HK5-Tor).
"""
5 changes: 5 additions & 0 deletions protonvpn_cli/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,11 @@ def check_root():
def check_update():
"""Return the download URL if an Update is available, False if otherwise"""

if not sys.stdout.isatty():
# There is no console to which our message may be printed. Skip update
# check for non-interactive usage.
return

def get_latest_version():
"""Return the latest version from pypi"""
logger.debug("Calling pypi API")
Expand Down
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# Call with pip install -r requirements.txt
docopt
requests
pythondialog
jinja2
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
},
install_requires=[
"requests",
"docopt",
"pythondialog",
"jinja2",
],
Expand Down