/* OpenVAS-Client
 * $Id$
 * Description: Create user install executables for ms windows systems using
 *              nsis.
 *
 * Authors:
 * Felix Wolfsteller <felix.wolfsteller@intevation.de>
 *
 * Copyright:
 * Copyright (C) 2009 Intevation GmbH
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2,
 * or, at your option, any later version as published by the Free
 * Software Foundation
 *
 * 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 St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * In addition, as a special exception, you have
 * permission to link the code of this program with the OpenSSL
 * library (or with modified versions of OpenSSL that use the same
 * license as OpenSSL), and distribute linked combinations including
 * the two. You must obey the GNU General Public License in all
 * respects for all of the code used other than OpenSSL. If you
 * modify this file, you may extend this exception to your version
 * of the file, but you are not obligated to do so. If you do not
 * wish to do so, delete this exception statement from your version.
 */

/**
 * @file
 * 
 * The OpenVAS Credentials Manager allows the user to create "accounts" that
 * are used to perform local checks.
 * 
 * For *nix- systems RPM packages can be created that install the public key of
 * the 'account'.
 * 
 * For ms windows systems an executable can be created that installs the user.
 * To arrive at a windows executable, the nsis (Nullsoft scriptable installer
 * system) is used.
 * With nsis executables are created from .nsi files with the tool makensis.
 * 
 * Users are managed on ms windos systems using 'net':
@verbatim
  net user <username> <password> /add /active:yes
  net user <username> <password> /delete
  // Currently not done:
  net localgroup <local group> <local account> /<ADD>
@endverbatim
 */

#include "openvas_ssh_login.h"
#include "file_utils.h"
#include "nessus_i18n.h"
#include "error_dlg.h"
#include <glib/gstdio.h>
#include "includes.h" /* For stdio (function: remove) */
#include "preferences.h" /* For prefs_get_nessushome */
#include "context.h" /* For check_is_dir (!) and context_remove_directory (!): @TODO */
#include "openvas_lsc_target_prep.h"
#include "openvas_lsc_user_makensis.h"

/**
 * @brief Writes a nsi file to be used with the 'Nullsoft scriptable installer
 * @brief system'.
 */
static gboolean
create_nsi_file (const gchar* nsifilename, const openvas_ssh_login* loginfo,
                 const gchar* outfilename)
{
  FILE* fd;

  fd = fopen (nsifilename, "w");
  if (fd <= 0)
    return FALSE;

  // Write part about default section
  fprintf (fd, "#Installer filename\n");
  fprintf (fd, "outfile ");
  fprintf (fd, outfilename);
  fprintf (fd, "\n\n");

  fprintf (fd, "# Set desktop as install directory\n");
  fprintf (fd, "installDir $DESKTOP\n\n");

  fprintf (fd, "# Put some text\n");
  fprintf (fd, "BrandingText \"OpenVAS Local Security Checks User\"\n\n");

  // For ms vista installers we need the UAC plugin and use the following lines:
  // This requires the user to have the UAC plugin installed and to provide the
  // the path to it.
  //fprintf (fd, "# Request application privileges for Windows Vista\n");
  //fprintf (fd, "RequestExecutionLevel admin\n\n");

  fprintf (fd, "#\n# Default (installer) section.\n#\n");
  fprintf (fd, "section\n\n");

  fprintf (fd, "# Define output path\n");
  fprintf (fd, "setOutPath $INSTDIR\n\n");

  fprintf (fd, "# Uninstaller name\n");
  fprintf (fd, "writeUninstaller $INSTDIR\\openvas_lsc_remove_%s.exe\n\n",
               loginfo->username);

  // Need to find localized Administrators group name, create a
  // GetAdminGroupName - vb script (Thanks to Thomas Rotter)
  fprintf (fd, "# Create Thomas Rotters GetAdminGroupName.vb script\n");
  fprintf (fd, "ExecWait \"cmd /C Echo Set objWMIService = GetObject($\\\"winmgmts:\\\\.\\root\\cimv2$\\\") > $\\\"%%temp%%\\GetAdminGroupName.vbs$\\\" \"\n");
  fprintf (fd, "ExecWait \"cmd /C Echo Set colAccounts = objWMIService.ExecQuery ($\\\"Select * From Win32_Group Where SID = 'S-1-5-32-544'$\\\")  >> $\\\"%%temp%%\\GetAdminGroupName.vbs$\\\"\"\n");
  fprintf (fd, "ExecWait \"cmd /C Echo For Each objAccount in colAccounts >> $\\\"%%temp%%\\GetAdminGroupName.vbs$\\\"\"\n");
  fprintf (fd, "ExecWait \"cmd /C Echo Wscript.Echo objAccount.Name >> $\\\"%%temp%%\\GetAdminGroupName.vbs$\\\"\"\n");
  fprintf (fd, "ExecWait \"cmd /C Echo Next >> $\\\"%%temp%%\\GetAdminGroupName.vbs$\\\"\"\n");
  fprintf (fd, "ExecWait \"cmd /C cscript //nologo $\\\"%%temp%%\\GetAdminGroupName.vbs$\\\" > $\\\"%%temp%%\\AdminGroupName.txt$\\\"\"\n\n");

  /** @TODO provide /comment:"OpenVAS User" /fullname:"OpenVAS Testuser" */
  fprintf (fd, "# Create batch script that installs the user\n");
  fprintf (fd, "ExecWait \"cmd /C Echo Set /P AdminGroupName= ^<$\\\"%%temp%%\\AdminGroupName.txt$\\\" > $\\\"%%temp%%\\AddUser.bat$\\\"\" \n");
  fprintf (fd, "ExecWait \"cmd /C Echo net user %s %s /add /active:yes >> $\\\"%%temp%%\\AddUser.bat$\\\"\"\n",
               loginfo->username, loginfo->userpassword);
  fprintf (fd, "ExecWait \"cmd /C Echo net localgroup %%AdminGroupName%% %%COMPUTERNAME%%\\%s /add >> $\\\"%%temp%%\\AddUser.bat$\\\"\"\n\n",
                loginfo->username);

  fprintf (fd, "# Execute AddUser script\n");
  fprintf (fd, "ExecWait \"cmd /C $\\\"%%temp%%\\AddUser.bat$\\\"\"\n\n");

  // Remove up temporary files for localized Administrators group names
  fprintf (fd, "# Remove temporary files for localized admin group names\n");
  fprintf (fd, "ExecWait \"del $\\\"%%temp%%\\AdminGroupName.txt$\\\"\"\n");
  fprintf (fd, "ExecWait \"del $\\\"%%temp%%\\GetAdminGroupName.vbs$\\\"\"\n\n");
  fprintf (fd, "ExecWait \"del $\\\"%%temp%%\\AddUser.bat$\\\"\"\n\n");

  /** @TODO Display note about NTLM and SMB signing and encryption, 'Easy Filesharing' in WIN XP */
  fprintf (fd, "# Display message that everything seems to be fine\n");
  fprintf (fd, "messageBox MB_OK \"A user has been added. An uninstaller is placed on your Desktop.\"\n\n");

  fprintf (fd, "# Default (install) section end\n");
  fprintf (fd, "sectionEnd\n\n");

  // Write part about uninstall section
  fprintf (fd, "#\n# Uninstaller section.\n#\n");
  fprintf (fd, "section \"Uninstall\"\n\n");

  fprintf (fd, "# Run cmd to remove user\n");
  fprintf (fd, "ExecWait \"net user %s /delete\"\n\n",
               loginfo->username);

  /** @TODO Uninstaller should remove itself */
  fprintf (fd, "# Unistaller should remove itself (from desktop/installdir)\n\n");

  fprintf (fd, "# Display message that everything seems to be fine\n");
  fprintf (fd, "messageBox MB_OK \"A user has been removed. You can now savely remmove the uninstaller from your Desktop.\"\n\n");

  fprintf (fd, "# Uninstaller section end\n");
  fprintf (fd, "sectionEnd\n\n");

  fclose (fd);

  return TRUE;
}

/**
 * @brief Execute makensis to create an executable installer from a .nsi file.
 */
static gboolean
execute_makensis (const gchar* filename)
{
  gchar** cmd;
  gint exit_status;

  cmd = (gchar **) g_malloc (3 * sizeof (gchar *));

  cmd[0] = g_strdup ("makensis");
  cmd[1] = g_strdup (filename);
  cmd[2] = NULL;
  printf ("--- executing makensis.\n");
  if (g_spawn_sync (".",
                    cmd,
                    NULL, // env
                    G_SPAWN_SEARCH_PATH,
                    NULL, // setup func
                    NULL,
                    NULL,
                    NULL,
                    &exit_status,
                    NULL                 ) == FALSE
      || exit_status != 0)
    {
      show_error(_("Error (%d) creating the executable.\n"
                   "For further information consult your shell."), exit_status);
      exit_status = -1;
    }

  g_free (cmd[0]);
  g_free (cmd[1]);
  g_free (cmd[2]);
  g_free (cmd);

  printf ("--- makensis returned %d.\n", exit_status);
  return (exit_status == 0);
}

/**
 * @brief Returns whether makensis could be found in the path.
 * @return true if makensis could be found in the path, false otherwise.
 */
gboolean
openvas_lsc_user_makensis_found ()
{
  gboolean found;
  // Check if makensis is found in path
  gchar* makensis_path = g_find_program_in_path ("makensis");

  found = (makensis_path != NULL);
  g_free (makensis_path);
  return found;
}

/**
 * @brief Returns the path to installer for a given openvas_ssh_login.
 *
 * Installers will be placed to or read from this location. It equals the path
 * to the public key but with a different file extension (.exe).
 *
 * @return Path to installer for given openvas_ssh_login, has to be freed using
 *         g_free, NULL in case of errors.
 */
gchar*
openvas_lsc_user_makensis_filename (const char* accountname)
{
  return openvas_lsc_target_prep_filename (accountname, "exe");
}

/**
 * @brief Attempts creation of RPM packages to install a users public key file.
 * 
 * @param loginfo openvas_ssh_login struct to create rpm for.
 * 
 * @return Path to rpm file if successfull, NULL otherwise.
 */
gboolean
openvas_lsc_user_makensis (openvas_ssh_login* loginfo, const gchar* to_filename)
{
  gboolean success   = FALSE;
  int rmdir_response = TRUE;

  gchar* tmpdir  = openvas_lsc_target_prep_create_tmp_dir ();
  gchar* nsifile = g_build_filename (tmpdir, "lsc_user_installer.nsi", NULL);
  success = create_nsi_file (nsifile, loginfo, to_filename);
  if (success)
      success = execute_makensis (nsifile);

  rmdir_response = file_utils_rmdir_rf (tmpdir);
  if (rmdir_response != 0 && success)
    show_error (_("The directory %s could not be deleted.\nIt might contain provided credential data as plain text, so removal is suggested."),
                tmpdir);
  g_free (tmpdir);

  return success;
}
