/* Nessus
 * Copyright (C) 1998 - 2001 Renaud Deraison
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2,
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * In addition, as a special exception, Renaud Deraison
 * gives permission to link the code of this program with any
 * version of the OpenSSL library which is distributed under a
 * license identical to that listed in the included COPYING.OpenSSL
 * file, 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
 * Functions related to target files.
 *
 */

#include <includes.h>
#include <errno.h>

#include "nessus_i18n.h"

#ifdef USE_GTK 
#include <gtk/gtk.h>
#include "read_target_file.h"
#endif /* USE_GTK */

#include "error_dlg.h"
#include "globals.h"

#ifdef HAVE__STAT
typedef struct _stat struct_stat ;
# ifndef S_ISREG
# define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
# endif
# define lstat(x,y) _stat(x,y)
#else /* HAVE__STAT */
typedef struct stat struct_stat ;
# ifndef HAVE_LSTAT
#  define lstat(x,y) stat(x,y)
# endif
#endif

#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif

#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif

#ifndef MAP_FAILED
#define MAP_FAILED ((__ptr_t) -1)
#endif

#ifdef USE_GTK

/**
 * @brief Shows a file selection dialog and attempts to read the selected file
 * if OK is clicked.
 * 
 * Callback for click on "Read file" in the target frame.
 * Shows up a file selection dialog and attempts to read that target file if
 * OK is clicked.
 */
void target_file_select()
{
 GtkWidget * file;
 file = gtk_file_selection_new (_("Load file"));
 gtk_file_selection_set_filename (GTK_FILE_SELECTION(file), "");
 g_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (file)->ok_button),
                               "clicked", (GtkSignalFunc) read_target_file,(void *) file );

 g_signal_connect_swapped(GTK_FILE_SELECTION (file)->cancel_button, "clicked",
                          G_CALLBACK(gtk_widget_destroy), file);

 gtk_widget_show(file);
}

/**
 * Sets the string parameter target as text to the TARGET text entry of the
 * TARGET dialog.
 * 
 * @param target String to be displayed in the TARGET text entry.
 */
static void
target_file_apply(char* target)
{
 struct arglist * ctrls = arg_get_value(MainDialog, "TARGET");
 GtkWidget * entry;
 if(!ctrls)return;
 entry = arg_get_value(ctrls, "TARGET");
 if(!entry)return;
 gtk_entry_set_text(GTK_ENTRY(entry), estrdup(target));
}
#endif /* USE_GTK */


/**
 * @brief Returns a string assembled out of the lines in file filename.
 * 
 * The string will contain all the content of the file where newlines have been
 * replaced with commata and carriage returns replaced with spaces.
 * 
 * @param filename File to read.
 * 
 * @return Content of file with newlines being replaced by commata or NULL if an
 *         error occured.
 */
char *
target_file_to_list(const char* filename)
{
 HANDLE fd = open(filename, O_RDONLY);
 char * ret,*t;
 int len;
 struct_stat sb;
 int n, offs, left ;

 if(fd < 0)
 {
   show_error(_("Could not open %s\nopen(%s): %s"),
       filename, filename, strerror(errno));
   return(NULL);
 }

 if (lstat (filename, &sb) != 0) {
   show_error(_("Cannot stat %s (%s)\n"), filename, strerror(errno));
   return 0;
 }
 len = (int)sb.st_size;
 ret = emalloc (len) ;
 offs =   0 ;
 left = len ;

 do {
   if ((n = read (fd, ret + offs, left)) < 0) {
     efree(&ret);
     if (n == 0)
       show_error(_("file mapping failed: unexpected end-of-file\n"));
     else  
       show_error(_("file mapping failed: %s\n"), strerror(errno));
     return 0;
   }
 } while (offs += n, (left -= n) > 0) ;


 t = ret;
 while((t=strchr(t, '\n')))t[0]=',';
 t = ret;
 while((t=strchr(t, '\r')))t[0]=' ';

 /*
  * trailing garbage
  */

 len = strlen(ret);
 while(len > 0 && ( ret[len-1]==',' || ret[len-1] == ' ' ) )
 {
   ret[len-1]='\0'; 
   len--;
 }
 return(ret);
}


#ifdef USE_GTK 
/**
 * @brief Returns target specification string (conditionally read from a file).
 * 
 * If the string starts with "file:", returns the file as string with newlines
 * replaced by commata. If it does not, return a copy of it.
 * In both cases, spaces and tabstops will be removed.
 * 
 * @param target Either path to file (prepended with "file:") or target string.
 * 
 * @return Copy of the string target if it does not start with "file:", file
 *         content as string otherwise, in both cases whitespaces-free.
 */
char *
target_translate (const char* target)
{
  char* untouched    = NULL;
  gchar* s_wo_white  = NULL;
  gchar** v_wo_white;

  // Get untouched list of targets
  if (!strncmp (target, "file:", 5) )
    untouched = target_file_to_list (target + strlen ("file:") ); /* RATS: ignore, string literal is nul terminated */
  else
    untouched = strdup (target);

  // Transform into string vector, whitespace-free (split at whitespaces)
  v_wo_white = g_strsplit_set (untouched, "\t ", -1);

  // Collapse the vector.
  s_wo_white = g_strjoinv (NULL, v_wo_white);

  // Free and return
  g_strfreev (v_wo_white);
  return s_wo_white;
 }

 /**
  * @brief Prepends "file:" to a string.
  * 
  * @return String argument file with "file:" prepended.
  */
gchar*
target_file_name (const char* file)
{
  return g_strdup_printf ("file:%s", file);
}

/**
 * Callback for click on okay on target-file selection dialog.
 * Sets the string in GUI via call to target_file_apply.
 * 
 * @param bidon Ignored (callback).
 * @param gtkw File Selection dialog.
 */
void
read_target_file(GtkWidget* bidon, GtkWidget* gtkw)
{
 const char * filename = gtk_file_selection_get_filename(GTK_FILE_SELECTION(gtkw));
 gchar * ret = target_file_name(filename);
 target_file_apply(ret);
 g_free (ret);
 gtk_widget_hide(gtkw);
 gtk_widget_destroy(gtkw);
}
#endif /* USE_GTK */
