/* GAdmin-Rsync - An easy to use GTK+ frontend for the rsync backup client and server.
 * Copyright (C) 2007-2011 Magnus Loef <magnus-swe@telia.com> 
 *
 * 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, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
*/



#include "../config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <gtk/gtk.h>
#include "gettext.h"
#include "widgets.h"
#include "allocate.h"
#include "show_info.h"
#include "commands.h"
#include "key_handling.h"
#include "handle_ssh.h"

extern gchar *global_key_path;
extern G_CONST_RETURN gchar *global_home_dir;


/* Create keys and on success install them to the remote computer */
int setup_keys(gchar *user, gchar *password, gchar *host, gchar *port, gchar *type, gchar *len, struct w *widgets)
{
    gchar *info;
    int retval = 0;

    if( is_empty(user) || is_empty(password) || is_empty(host)
    ||  is_empty(port) || is_empty(type) || is_empty(len) )
    {
	info = g_strdup_printf(_("Every field must be filled in.\n"));	
	show_info(info);
	if( info!=NULL )
	  g_free(info);

	return retval;    
    }

    if( ! generate_keys(user, host, type, len, widgets) )
    {
	// Naah... printf("Key already exists or keygen failed.\n");

	return retval;
    }
    else
    {
        /* Upload key to remote host and install them there */
        if( ! install_remote_key(user, host, port, password, widgets) )
	{
	    // Naah...  printf("Key upload failed.\n");
	    return retval;
	}
    }

    return 1;
}


int is_empty(gchar *input)
{
    int retval = 0;

    if( input == NULL || strlen(input) < 1 )
      retval = 1;

    return retval;
}


gchar * mk_key_name(gchar *user, gchar *host, char type[128])
{
    gchar *key_name;
    
    if( strstr(type, "priv") )
      key_name = g_strdup_printf("%s.%s.key", user, host);
    else
      key_name = g_strdup_printf("%s.%s.key.pub", user, host);
    
    return key_name;
}


int key_exists(gchar *key_name)
{
    int retval = 0;
    gchar *key_path = g_strdup_printf("%s/%s", global_key_path, key_name);
    
    if( file_exists(key_path) )
      retval = 1;

    g_free(key_path);

    return retval;
}


/* Generate keys for use with remote transfers in /USERsHOME/.gadmin-rsync/keys/... */
int generate_keys(gchar *user, gchar *host, gchar *key_type, gchar *key_len, struct w *widgets)
{
    /* DSA can only be max 1024 bits on some dists. RSA 2048 is said to be ok by some. */
    char **new_argv;
    gchar *key_name, *key_path_priv, *key_path_pub, *info;
    int retval = 0;

    key_name = mk_key_name(user, host, "priv");

    key_path_priv = g_strdup_printf("%s/%s", global_key_path, key_name);
    key_path_pub  = g_strdup_printf("%s/%s.pub", global_key_path, key_name);

    /* If we have both local keys for this site, dont generate new ones */
/*  New: I think it would be better to just generate new keys instead...  
    if( file_exists(key_path_priv) && file_exists(key_path_pub) )
    {
// Not needed	printf("The local private and public keys exists.\nNot generating new local keys.\n");
	info = g_strdup_printf(_("Keys for this remote host already exists.\nNew keys where not generated.\n"));
	show_info(info);
	if( info!=NULL )
	  g_free(info);

	g_free(key_path_priv);
	g_free(key_path_pub);
	g_free(key_name);

	return retval;
    }
*/

    if( file_exists(key_path_priv) )
    {
	printf("The private key already exists, removing it.\n");
	unlink(key_path_priv);
    }

    if( file_exists(key_path_pub) )
    {
	printf("The public key already exists, removing it.\n");
	unlink(key_path_pub);
    }

    printf("Generating private key: %s\n", key_path_priv);
    printf("Generating public  key: %s\n", key_path_pub);


    new_argv = create_gen_key_cmd(key_type, key_len, key_path_priv);

    if( ! run_ssh_cmd(new_argv, NULL) )
    {
	info = g_strdup_printf(_("Error: Can not generate keys for this host.\n"));	
	show_info(info);
	if( info!=NULL )
	  g_free(info);

        printf("Error: Key generation failed.\n");
        retval = 0;
    }
    else
    {
//        printf("Key generation successful.\n");
	retval = 1;
    }
    free(new_argv);

    g_free(key_name);
    g_free(key_path_priv);
    g_free(key_path_pub);

    return retval;
}


/* Upload public key to remote host and append it to file: ~/.ssh/authorized_keys */
int install_remote_key(gchar *user, gchar *host, gchar *port, gchar *password, struct w *widgets)
{
    FILE *fp;
    char **new_argv;
    gchar *key_name_priv, *key_name_pub, *key_path_pub, *key_path_priv;
    gchar *script, *script_name, *script_path, *info;
    int retval = 0;

    key_name_priv = mk_key_name(user, host, "priv");
    key_name_pub  = mk_key_name(user, host, "pub");
    
    key_path_priv = g_strdup_printf("%s/%s", global_key_path, key_name_priv);
    key_path_pub  = g_strdup_printf("%s/%s", global_key_path, key_name_pub);

    if( ! file_exists(key_path_priv) )
    {
	info = g_strdup_printf(_("Error: The local private key does not exist.\n"));	
	show_info(info);
	if( info!=NULL )
	  g_free(info);

	printf("Error: The local private key doesnt exist: %s\n", key_path_priv);

	g_free(key_name_priv);
	g_free(key_name_pub);
	g_free(key_path_priv);
	g_free(key_path_pub);

	return retval;
    }

    g_free(key_name_priv);
    g_free(key_path_priv);


    if( ! file_exists(key_path_pub) )
    {
	info = g_strdup_printf(_("Error: The local public key does not exist.\n"));	
	show_info(info);
	if( info!=NULL )
	  g_free(info);

	printf("Error: The local public key doesnt exist: %s\n", key_path_pub);

	g_free(key_name_pub);
	g_free(key_path_pub);

	return retval;
    }


    /* Make the key install script */
    script_name = g_strdup_printf("gadmin-rsync-ssh.sh");

    script = g_strconcat("#!/bin/bash\n",
    "### Gadmin-rsync ssh key installer ###\n\n",
    "if [ ! -e .ssh ]; then\n",
    "   mkdir .ssh && chmod 700 .ssh\n"
    "fi\n\n",

    "cd .ssh || exit 1\n\n",
	
    "if [ ! -f authorized_keys ]; then\n",
    "   touch authorized_keys && chmod 600 authorized_keys\n"
    "fi\n\n"
		
    "cat ../", key_name_pub, " >> authorized_keys\n",
    
    "rm -f ../", key_name_pub, "\n",
    "rm -f ../", script_name, "\n",
    NULL);

    g_free(key_name_pub);


    /* Write the pub key install script */
    script_path = g_strdup_printf("%s/%s", global_home_dir, script_name);

    if((fp=fopen(script_path, "w+"))==NULL)
    {
	printf("Error writing install script file here: %s\n", script_path);

	g_free(script);
	g_free(script_name);
	g_free(script_path);
	g_free(key_path_pub);

	return retval;
    }
    fputs(script, fp);
    fclose(fp);    

    g_free(script);


    /* Upload public key and key install script to the remote server */
    gchar *user_host_dir = g_strdup_printf("%s@%s:~", user, host);

    new_argv = create_scp_upload_cmd(user, port, key_path_pub, script_path, user_host_dir);

    if( ! run_ssh_cmd(new_argv, password) )
    {
	printf("Error uploading key.\n");
	g_free(script_path);
	g_free(key_path_pub);
	g_free(user_host_dir);
	g_free(script_name);
	free(new_argv);

	return retval;
    }

    free(new_argv);
    g_free(user_host_dir);


    /* Create the key install command */
    gchar *ssh_cmd = g_strdup_printf("chmod 755 ~/%s && sh ~/%s && rm -f ~/%s", script_name, script_name, script_name);
    
    new_argv = create_ssh_cmd(user, host, port, ssh_cmd);

    if( ! run_ssh_cmd(new_argv, password) )
    {
	info = g_strdup_printf(_("Error: Could not install the public key on the remote host.\n"));	
	show_info(info);
	if( info!=NULL )
	  g_free(info);

        printf("Error installing key on the remote host.\n");
        
	g_free(script_path);
	g_free(key_path_pub);
	g_free(script_name);
	g_free(ssh_cmd);
	free(new_argv);

	return retval;
    }
    else
    {
	/* This info is good. Will be shown in the progress bar later */
	info = g_strdup_printf(_("Public key installation on remote host successful.\n"));	
	show_info(info);
	if( info!=NULL )
	  g_free(info);

        printf("Key installation successful.\n");

	retval = 1;
    }

    free(new_argv);

    g_free(ssh_cmd);
    g_free(script_name);

    /* Remove the local script */
    unlink(script_path);
    g_free(script_path);

    g_free(key_path_pub);


    return retval;
}
