#!/usr/bin/env bash
#
# lib/common
#
# Copyright (c) 2011-2013 LibreVPN <vpn@hackcoop.com.ar>
#
# See AUTHORS for a list of contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Affero 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 Affero General
# Public License along with this program.  If not, see
# <http://www.gnu.org/licenses/>.
#
#

# Terminar ante el menor error
set -e

# Habilitar debug tan pronto como sea posible si se usa -d en algún lado
echo "$*" | grep -q " -\([a-z]\+\)\?d" && set -x

# Comprueba si los programas necesarios existen en el sistema
requires() {
    local missing=()
    local bin
    for bin in "$@"; do
        if ! type "$bin" &>/dev/null; then
            missing+=("$bin")
        fi
    done

    if [[ ${#missing[@]} -ne 0 ]]; then
        printf "Los siguientes programas no se encuentran en PATH:%s\n" "${missing[*]}"
        exit 1
    fi
}

trap_exit() {
    msg "ARGH! Algo pasó" 1>&2
    exit 1
}

trap 'trap_exit' TERM HUP QUIT INT ERR

. "${LVPN_LIBDIR}/msg"

self="$(basename $0)"

# Agrega una linea a un archivo
add_to_file(){
    f=$1; shift

    echo "$*" >>"$f"
}

# Obtiene el directorio del nodo
# $1 nombre del nodo
get_node_dir() {
    node="$(get_node_name "$1")"
    dir="${LVPN_DIR}/nodos/${node}"

    if [ ! -d "${dir}" ] || [ ! -f "${dir}/tinc.conf" ]; then
        fatal_error "El nodo no existe o es inválido"
    fi

    echo "${dir}"
}

# Obtiene el archivo del nodo en el directorio del nodo
get_node_file() {
    node="$(get_node_name "$1")"
    dir="$(get_node_dir "$1")"
    file="${dir}/hosts/${node}"

    if [ ! -f "${file}" ]; then
        fatal_error "El archivo host de %s no existe" "$1"
    fi

    echo "${file}"
}

# Limpia el hostname, convierte todo lo no alfanumérico a _
# Ver tincd.conf(5)
get_node_name() {
  echo "$1" | sed -e "s/[^a-z0-9_]/_/gi"
}

get_host_file() {
  node="$(get_node_name "${1}")"
  test -f "${LVPN_HOSTS}/${node}" || \
    error "El archivo host de %s no existe" "$node" || \
    return 1

  echo "${LVPN_HOSTS}/${node}"
}

# Obtiene un evento válido
get_event() {
  echo "$1" | grep -qE "^host|subnet|tinc$" && echo "$1"
}

# Obtener un script
get_script() {
  script_dir="${LVPN_LIBDIR}/skel/scripts"

  test -f "${script_dir}/$1" && echo "${script_dir}/$1"
}

# Encontrar el tipo de init que se va a usar para tinc
# Devuelve "tipo ubicacion"
# Ver lib/lvpn-install
find_init_system() {
  if [ -f /etc/debian_version ]; then
    echo "deb /etc/tinc/nets.boot"
  elif [ -f /etc/os-release ]; then
    echo "systemd /etc/systemd/system/multi-user.target.wants/tincd@${NETWORK}.service"
  else
    echo "unknown unknown"
  fi
}

# Obtiene la id del responsable del nodo
get_id() {
  if [ -n "$(git config --get user.name)" ]; then
    echo "# $(git config --get user.name) <$(git config --get user.email)>"
  else
    echo "# $USER@$HOSTNAME"
  fi
}

# Encuentra una IP libre entre los archivos de host
get_ipv4() {
# Las subnets encontradas
  local subnets=/tmp/$$.subnets
  local ip=""
  local fourth=0
  local tries=0

# Armar la lista de IPs
  grep -i "subnet\s*=" ${LVPN_HOSTS}/* | cut -d'=' -f2 | sort > ${subnets}

# Fallar si hay 250 nodos...
  if [ $(wc -l ${subnets} | cut -d' ' -f1 2>/dev/null) -ge 250 ]; then
# No detecta si hay varios rangos en uso pero por ahora sirve
    error "Este rango está agotado"
    return 1
  fi

# Hasta que encontremos una IP libre elegir el cuarto octeto al azar
  until [ -n "${ip}" ]; do
    let fourth=${RANDOM}%250
    local tmpip="${LVPN_SUBNET%.*}.${fourth}/32"

# Está en la lista?
    if ! grep -q "${tmpip}" "${subnets}"; then
      ip="${tmpip}"
    fi

# Aumentar la cantidad de intentos
# Por alguna razon let devuelve 1
    let tries++ || true

    if [ ${tries} -ge 250 ]; then
      error "No hay IPs libres"
      return 1
    fi
  done

  echo "${ip}"
}

# Genera una dirección IPv6 /128
get_ipv6() {
# Compatibilidad para versión standalone
  arch=$(uname -m)

  # probar los posibles nombres de generate-ipv6-address y no devolver
  # nada si no hay
  for gia in {${arch}-,}generate-ipv6-address; do
    if type ${gia} &>/dev/null; then
      echo "$(${gia} ${LVPN_SUBNET6%/*})/128"
      return
    fi
  done
}

if $root ; then
# Salir si no se es root y no existe sudo, sino usarlo
  if [ ! -w / ]; then
    if ! type sudo &>/dev/null; then
      fatal_error "Correr como root"
    else
      export sudo=sudo
    fi
  fi
fi
