;;; nordvpn.el --- NordVPN interface for Emacs -*- lexical-binding: t -*- ;;; Copyright (C) 2021 Phundrak ;; Author: Phundrak ;; Maintainer: Phundrak ;; Homepage: https://labs.phundrak.com/phundrak/nordvpn.el ;; Version: 0.1.0 ;; Keywords: nordvpn, vpn, convenience ;; Package-Requires: ((emacs "27") (s "1.12.0") (ido "1.0.1")) ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; Functions collection for interacting with NordVPN from Emacs. ;;; Code: (defcustom nordvpn-default-country nil "Default country to which NordVPN should connect" :type 'string :group 'nordvpn) (defcustom nordvpn-default-city nil "Default city to which NordVPN should connect" :type 'string :group 'nordvpn) (defun nordvpn--cut-weird-prefix (string) "Cut the prefix almost always found in NordVPN's returned string ending with some Ctrl-M char." (replace-regexp-in-string ".* " "" string)) (defun nordvpn--get-list (string) "Get a list from NordVPN's output `STRING', such as cities or countries." (s-split "\n" (nordvpn--cut-weird-prefix string) t)) (defun nordvpn--get-countries () "Returns the list of countries offered by NordVPN." (nordvpn--get-list (nordvpn--cut-weird-prefix (eshell-command-result "nordvpn countries")))) (defun nordvpn--get-country () "Get a country among the ones offered by NordVPN." (let ((countries (nordvpn--get-countries)) (country (ido-completing-read "Country: " countries))) (if (null country) nordvpn-default-country country))) (defun nordvpn--get-cities (&optional country) "Get the list of cities available in `COUNTRY'. If `COUNTRY' is nil, then it will be asked interactively to the user." (let ((country (if (null country) (nordvpn--get-country) country))) (nordvpn--get-list (eshell-command-result (format "nordvpn cities %s" country))))) (defun nordvpn--get-city (&optional country) "Get a specific city from the list of cities available for `COUNTRY'. If `COUNTRY' is nil, the user will have to choose through `nordvpn--get-country'. If the result is nil, the function will fallback to `nordvpn-default-country'." (let* ((country (if (null country) (nordvpn--get-country) country)) (command (format "nordvpn cities %s" country)) (cities (nordvpn--get-list (eshell-command-result command)))) (ido-completing-read "City: " cities))) (defun nordvpn--get-groups () "Get the list of server groups available." (nordvpn--get-list (eshell-command-result "nordvpn groups"))) (defun nordvpn--get-group () "Select one of the available server groups." (ido-completing-read "Group: " (nordvpn--get-groups))) (defun nordvpn--get-credentials () "Get user credentials from `auth-source' files." (let* ((found (car (auth-source-search :max 1 :host "nordvpn" :require '(:user :secret) :create t)))) (if found (list (plist-get found :user) (let ((password (plist-get found :secret))) (while (functionp password) (setf password (funcall password))) password)) nil))) (defun nordvpn-connect-to-country (&option country) "Connect with NordVPN to a specific country. If `COUNTRY' is nil, then \"nordvpn c\" is called, otherwise \"nordvpc c `COUNTRY'\" is called." (message "NordVPN: %s" (nordvpn--cut-weird-prefix (eshell-command-result (if country (format "nordvpn c %s" country) "nordvpn c"))))) (defun nordvpn-connect-to-city (city &optinal country) "Connect with NordVPN to a specific `CITY'. An additional `COUNTRY' may be specified. If no country is provided to the function, \"nordvpn c `CITY'\" will be called, otherwise \"nordvpn c `COUNTRY' `CITY'\" will be called." (message "NordVPN: %s" (nordvpn--cut-weird-prefix (eshell-command-result (if country (format "nordvpn c %s %s" country city) (format "nordvpn c %s" city)))))) (defun nordvpn-connect-to-group (group) "Connect with NordVPN to the specified `GROUP'." (message "NordVPN: %s" (nordvpn--cut-weird-prefix (eshell-command-result (format "nordvpn c %s"))))) (defun nordvpn-connect (&optional arg) "Connect to NordVPN. If `ARG' is non nil or if `nordvpn-default-country' is nil, will ask the user which country to connect to." (interactive "P") (nordvpn-connect-to-country (if arg (nordvpn--get-country) nordvpn-default-country))) ;; nordvpn-connect-to-city &option country city ;; C-u nordvpn-connect-to-city &option city country (defun nordvpn-disconnect () "Disconnect NordVPN from the current server." (interactive) (message "NordVPN: %s" (nordvpn--cut-weird-prefix (eshell-command-result "nordvpn d")))) (defun nordvpn-logout () "Logout from NordVPN." (interactive) (message "NordVPN: %s" (nordvpn--cut-weird-prefix (shell-command-to-string "nordvpn logout")))) (defun nordvpn-login () "Log into your NordVPN account. Credentials are read from `auth-source' files." (interactive) (let* ((credentials (nordvpn--get-credentials)) (username (car credentials)) (password (cadr credentials)) (command (format "nordvpn login -u %s -p \"%s\"" username password))) (message "NordVPN: %s" (nordvpn--cut-weird-prefix (shell-command-to-string command))))) (provide 'nordvpn) ;;; nordvpn.el ends here