#!/usr/bin/env bash
#
# lib/lvpn-init
#
# 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/>.
#
#
# Crea un nuevo nodo

. "${LVPN_LIBDIR}"/common

requires tincd

# Opciones
# ConnectTo
connectto=""
# Address
address=""
# Port
port=""
# Subnets extra (-s)
subnets=""
# Instalar
install=false
# Habilitar subnet-up (-r)
subnetup=false
# Modo verborrágico por defecto
verbose=true
force=false
# Soporte para android
android=false

# Procesar los flags (ver doc)
while getopts 'vhirfp:a:c:s:l:A' arg; do
    case $arg in
        h) help $self; exit 0 ;;
        q) verbose=false ;;
        r) subnetup=true ;;
        i) install=true;;
        f) force=true ;;
        p) port="$OPTARG" ;;
        l) localnet="${localnet} $OPTARG" ;;
        a) address="${address} $OPTARG" ;;
        c) connectto="$connectto $OPTARG" ;;
        s) subnets="$subnets $OPTARG" ;;
        A) android=true ;;
    esac
done ;
let OPTIND--; shift $OPTIND

# Obtener el nombre del host
nodename="$(get_node_name "${1:-$HOSTNAME}")"

if [ -z "${nodename}" ]; then
    fatal_error "Se necesita un nombre de nodo"
fi

# Podríamos usar get_node_dir pero chequea si ya existe, cosa que no
nodedir="${LVPN_DIR}/nodos/${nodename}"
nodefile="${nodedir}/hosts/${nodename}"
conffile="${nodedir}/tinc.conf"

# Con -f se eliminan los archivos anteriores
if $force; then
    rm -r "${nodefile}" "${nodedir}" || true
fi

$verbose && msg "Creando %s..." "${nodename}"
if [ -f "${nodefile}" ]; then
    fatal_error "El nodo ya existe! Utilice -f para re-crearlo"
fi

$verbose && msg "Creando %s..." "${nodedir}"
if [ -d "${nodedir}" ]; then
    fatal_error "El directorio del nodo ya existe"
fi

# Crea los directorios de hosts y scripts
mkdir -p ${nodedir}/{hosts,scripts} || fatal_error "No se pudo crear el directorio"

$verbose && msg "Generando %s..." "${nodefile}"
add_to_file "${nodefile}" "# ${nodename}"
get_id >>"${nodefile}"

if [ ! -z "${address}" ]; then
  for _a in ${address}; do
    add_to_file "${nodefile}" "Address = ${_a}"
  done
else
# Cuando no tenemos una dirección pública probablemente estemos detrás
# de un firewall, esto le indica a los demás nodos que no traten de
# conectarse directamente sino que nos envíen paquetes a través de un
# nodo al que estemos conectados.
  add_to_file "${nodefile}" "IndirectData = yes"
fi

add_to_file "${nodefile}" "Port = ${port:-${PORT}}"

# descubrir nodos en la misma LAN
add_to_file "${nodefile}" "LocalDiscovery = yes"

# Tinc para android necesita especificar la shell con la que se corren los
# scripts
${android} && add_to_file "${nodefile}" "ScriptsInterpreter = /system/bin/sh"
# Y además el dispositivo tun
${android} && add_to_file "${nodefile}" "Device = /dev/tun"

if [ ! -z "${LVPN_SUBNET}" ]; then
  $verbose && msg "Determinando la IPv4 del nodo..."
  if [ -z "${localnet}" ] ; then
      localnet="${localnet} $(get_ipv4)"

      if [ $? -ne 0 ]; then
          fatal_error "No se pudo determinar una IPv4 para este nodo"
      fi
  fi
fi

$verbose && msg "Generando IPv6..."
localnet="${localnet} $(get_ipv6)"

$verbose && msg "Añadiendo subredes..."
for _subnet in ${localnet} ${subnets}; do
    add_to_file "${nodefile}" "Subnet = ${_subnet}"
done

# Si se anuncia otra subnet, el nodo actua como router, pero se pierden
# servicios como avahi
if [ -z "${subnets}" ]; then
    mode="switch"
else
    mode="router"
fi
$verbose && msg "Este nodo es un %s" "${mode}"

$verbose && msg "Generando %s..." "${conffile}"
add_to_file "${conffile}" "Name = ${nodename}"
add_to_file "${conffile}" "Mode = ${mode}"
add_to_file "${conffile}" "GraphDumpFile = /tmp/${NETWORK}.dot"

$verbose && msg "Añadiendo hosts..."
if [ -n "${connectto}" ]; then
    ${LVPN} connectto ${nodename} ${connectto}
fi

# Copiar los scripts de inicio
$verbose && msg "Copiando el resto de archivos..."
if ${android} ; then
# El sistema por defecto de Android es muy limitado, si no tiene busybox no hay
# prácticamente nada
  cp "${LVPN_LIBDIR}"/skel/android/tinc-up "${nodedir}"
# Hardcodear la primera subnet sin notación cidr
  sed -e "s/{{LVPN_SUBNET}}/${localnet%%/*}/g" \
      -i "${nodedir}/tinc-up"
else
  cp "${LVPN_LIBDIR}"/skel/tinc-{up,down} "${nodedir}"
  sed -e "s,{{LVPN_SUBNET}},${LVPN_SUBNET},g" \
      -e "s,{{LVPN_SUBNET6}},${LVPN_SUBNET6},g" \
      -i "${nodedir}/tinc-up"
fi

if ${subnetup} ; then
    cp "${LVPN_LIBDIR}"/skel/subnet-{up,down} "${nodedir}"
fi

$verbose && msg "Generando llaves..."
echo -e "\n" | tincd -c "${nodedir}" --generate-keys=${KEYSIZE}

# Sólo guardar el host si podemos escribir en hosts
if test -w "${LVPN_HOSTS}"; then
  $verbose && msg "Guardando el archivo de host con los demás nodos"
  cp "${nodefile}" "${LVPN_HOSTS}"
fi

cp --force "${LVPN_LIBDIR}/skel/run-script" "${nodedir}/"

$install && ${LVPN} install ${nodename}

msg "El nodo se ha creado con éxito"
msg "*Importante*: "
msg "* Envíe el archivo que representa su nodo a vpn@hackcoop.com.ar (%s), o" "${nodefile}"
msg "* Comparta su nodo en la red local con \`lvpn announce\`"
msg "* Agregue %s al repositorio git y publíquelo." "${nodefile}"

exit 0
