#!/bin/bash # barnard-ui # Description: Make managing servers with barnard easy. # # Copyright 2019, F123 Consulting, # Copyright 2019, Stormux, # Copyright 2019, Storm Dragon, # # This 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, or (at your option) any later # version. # # This software 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 package; see the file COPYING. If not, write to the Free # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301, USA. # #--code-- # the gettext essentials export TEXTDOMAIN=barnard-ui export TEXTDOMAINDIR=/usr/share/locale source gettext.sh # Log writing function log() { # Usage: command | log for just stdout. # Or command |& log for stderr and stdout. while read -r line ; do echo "$line" | tee -a "$logFile" &> /dev/null done } # Log file name is ~/.cache/scriptname.log logFile="$HOME/.cache/${0##*/}.log" # Clear previous logs echo -n | tee "$logFile" &> /dev/null # Settings to improve accessibility of dialog. export DIALOGOPTS='--insecure --no-lines --visit-items' inputbox() { # Returns: text entered by the user # Args 1, Instructions for box. # args: 2 initial text (optional) dialog --clear --backtitle "$(gettext "Enter text and press enter.")" \ --inputbox "$1" 0 0 "$2" --stdout } passwordbox() { # Returns: text entered by the user # Args 1, Instructions for box. # args: 2 initial text (optional) dialog --clear --backtitle "$(gettext "Enter text and press enter.")" \ --passwordbox "$1" 0 0 "$2" --stdout } msgbox() { # Returns: None # Shows the provided message on the screen with an ok button. dialog --clear --msgbox "$*" 10 72 } yesno() { # Returns: Yes or No # Args: Question to user. # Called in if $(yesno) == "Yes" # Or variable=$(yesno) dialog --clear --backtitle "$(gettext "Press 'Enter' for \"yes\" or 'Escape' for \"no\".")" --yesno "$*" 10 80 --stdout if [[ $? -eq 0 ]]; then echo "Yes" else echo "No" fi } menulist() { # Args: menu options. # returns: selected tag local i local menuList for i in "$@" ; do menuList+=("$i" "$i") done dialog --backtitle "$(gettext "Use the up and down arrow keys to find the option you want, then press enter to select it.")" \ --clear \ --no-tags \ --menu "$(gettext "Please select one")" 0 0 0 "${menuList[@]}" --stdout } [[ -d ~/.config/barnard ]] || mkdir ~/.config/barnard if [[ ! -r ~/.config/barnard/servers.conf ]]; then echo "Adding default mumble server." | log echo "declare -Ag mumbleServerList=(" > ~/.config/barnard/servers.conf echo "[Slint]=\"slint.fr:64738 -insecure\"" >> ~/.config/barnard/servers.conf echo ")" >> ~/.config/barnard/servers.conf fi source ~/.config/barnard/servers.conf function add-server() { local serverName="$(inputbox "$(gettext "Enter a name for the new server:")")" [[ $? -ne 0 ]] && return local serverAddress="$(inputbox "$(gettext "Enter the address of the server. If there is a password, do it in the form, password@address, if the port is not standard, add it after a :, address:port:")")" [[ $? -ne 0 ]] && return local serverPassword="${serverAddress%@*}" local serverAddress="${serverAddress#*@}" local serverPort="${serverAddress##*:}" local serverAddress="${serverAddress%:*}" if ! [[ "$serverPort" =~ ^[0-9]+ ]]; then serverPort=64738 fi mumbleServerList[$serverName]="${serverAddress}:${serverPort}${serverPassword:+ -password ${serverPassword}}" echo "declare -Ag mumbleServerList=(" > ~/.config/barnard/servers.conf for i in "${!mumbleServerList[@]}" ; do echo "[${i}]=\"${mumbleServerList[$i]}\"" >> ~/.config/barnard/servers.conf done echo ")" >> ~/.config/barnard/servers.conf echo "Added server $serverName ${serverAddress}:${serverPort}" | log msgbox "$(gettext "Added server") $serverName" } connect() { ifs="$IFS" IFS=$'\n' local serverName serverName="$(menulist "${!mumbleServerList[@]}" "Go Back")" [[ $? -eq 1 ]] && exit 0 IFS="$ifs" if [[ -z "$serverName" || "$serverName" == "Go Back" ]]; then return fi local username username="$(grep -m 1 '^Username = ' ~/.barnard.toml 2> /dev/null | cut -d '=' -f2- | sed "s/^[[:space:]]*//;s/[[:space:]]*$//;s/'//g")" username="${username//[[:space:]]/_}" username="${username:-${USER}-${HOSTNAME}}" local certArgs=() if [[ -f "$certFile" ]]; then certArgs=(-certificate "$certFile") fi # shellcheck disable=SC2086 command barnard -username "$username" -server ${mumbleServerList[$serverName]} "${certArgs[@]}" --fifo ~/.config/barnard/cmd --buffers 16 |& log } remove-server() { ifs="$IFS" IFS=$'\n' local serverName="$(menulist "${!mumbleServerList[@]}" "Go Back")" IFS="$ifs" if [[ -z "$serverName" || "$serverName" == "Go Back" ]]; then return fi unset "mumbleServerList[$serverName]" echo "declare -Ag mumbleServerList=(" > ~/.config/barnard/servers.conf for i in "${!mumbleServerList[@]}" ; do echo "[${i}]=\"${mumbleServerList[$i]}\"" >> ~/.config/barnard/servers.conf done echo ")" >> ~/.config/barnard/servers.conf echo "Removed server $serverName ${serverAddress}:${serverPort}" | log msgbox "$(gettext "Removed server") $serverName" } # Certificate configuration certDir="$HOME/.config/barnard" certFile="$certDir/barnard.pem" generate-certificate() { if [[ -f "$certFile" ]]; then if [[ "$(yesno "$(gettext "A certificate already exists. Do you want to replace it? This may affect your registered identity on servers.")")" != "Yes" ]]; then return fi fi local commonName commonName="$(inputbox "$(gettext "Enter a name for your certificate (e.g., your username):")" "barnard")" [[ $? -ne 0 ]] && return [[ -z "$commonName" ]] && commonName="barnard" if openssl req -x509 -newkey rsa:2048 -keyout "$certFile" -out "$certFile" -days 3650 -nodes -subj "/CN=$commonName" 2>/dev/null; then chmod 600 "$certFile" msgbox "$(gettext "Certificate generated successfully.")" else msgbox "$(gettext "Failed to generate certificate. Make sure openssl is installed.")" fi } view-certificate() { if [[ ! -f "$certFile" ]]; then msgbox "$(gettext "No certificate found.") $certFile" return fi local certInfo certInfo=$(openssl x509 -in "$certFile" -noout -subject -dates -fingerprint 2>/dev/null) if [[ -n "$certInfo" ]]; then msgbox "$certInfo" else msgbox "$(gettext "Could not read certificate information.")" fi } import-certificate() { local importPath importPath="$(inputbox "$(gettext "Enter the full path to your certificate file (PEM format with certificate and private key):")")" [[ $? -ne 0 ]] && return [[ -z "$importPath" ]] && return # Expand ~ if present importPath="${importPath/#\~/$HOME}" if [[ ! -f "$importPath" ]]; then msgbox "$(gettext "File not found:") $importPath" return fi # Verify it's a valid certificate if ! openssl x509 -in "$importPath" -noout 2>/dev/null; then msgbox "$(gettext "The file does not appear to be a valid PEM certificate.")" return fi # Verify it contains a private key if ! openssl rsa -in "$importPath" -check -noout 2>/dev/null && ! openssl ec -in "$importPath" -check -noout 2>/dev/null; then msgbox "$(gettext "The file does not appear to contain a valid private key. The certificate file must contain both the certificate and private key.")" return fi if [[ -f "$certFile" ]]; then if [[ "$(yesno "$(gettext "A certificate already exists. Do you want to replace it?")")" != "Yes" ]]; then return fi fi if cp "$importPath" "$certFile" && chmod 600 "$certFile"; then msgbox "$(gettext "Certificate imported successfully.")" else msgbox "$(gettext "Failed to import certificate.")" fi } manage-certificate() { while : ; do local certAction certAction="$(menulist "Generate" "View" "Import" "Go_Back")" [[ $? -eq 1 ]] && return case "$certAction" in "Generate") generate-certificate ;; "View") view-certificate ;; "Import") import-certificate ;; "Go_Back"|"") return ;; esac done } # main menu while : ; do action="$(menulist "Connect" "Add_server" "Remove_server" "Manage_Certificate")" [[ $? -eq 1 ]] && exit 0 action="${action,,}" action="${action//_/-}" if [[ "$action" == "exit" ]]; then exit 0 fi eval "$action" done