/* 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.
 *
 * Nessus Communication Manager -- it manages the NTP Protocol, version 1.1
 *
 */

#include <includes.h>

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

#include "auth.h"
#include "comm.h"
#include "nessus_plugin.h"
#include "context.h"
#include "preferences.h"
#include "parser.h"
#include "globals.h"
#include "error_dialog.h"

#ifndef MIN
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#endif


/* Currently active time-consuming task */
#define COMM_GET_PLUGINS 1
#define COMM_GET_DEPENDENCIES 2

/*
 * Update the UI while receiving plugin or dependency information.
 * For GTK this is displayed with a progress bar (maybe this should
 * be moved to a separate prefs_progressbar module)
 */
static void
comm_update_ui(context, current)
  struct context *context;
  int current;
{
#ifdef USE_GTK
  static int previous = 0;
  static int number;
  static int base;
  static int limit;
  static const char *fmt;
  gchar *pbar_text;
  /* maximum number of steps for the progress bar (plugins + dependencies) */
# define PBAR_MAX 12000

  if(F_quiet_mode)
    return; /* there is no UI to update in quiet mode */

  /* the task changes */
  if(previous != current)
  {
    previous = current;
    number = 0;
    switch(current)
    {
      case COMM_GET_PLUGINS:
	fmt = _("Receiving plugins: %d");
	base = 0;
	limit = PBAR_MAX;
	break;
      case COMM_GET_DEPENDENCIES:
	fmt = _("Receiving dependencies: %d");
	base = 0;
	limit = PBAR_MAX;
	break;
    }
  }

  /* Update progress bar every 10 plugins (every 100 on slow links) */
  if(++number % (F_show_pixmaps?100:1000) == 0)
  {
    /* if the progress bar is too short, step back 5% */
    if(base+number >= limit)
      base -= PBAR_MAX/20;

    /* update the progress bar */
    gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(context->pbar),
	1.0*(base+number)/PBAR_MAX);

    /* update text displayed in the progress bar */
    pbar_text = g_strdup_printf(fmt, number);
    gtk_progress_bar_set_text(GTK_PROGRESS_BAR(context->pbar), pbar_text);
    g_free(pbar_text);

    /* perform pending GUI events */
     gtk_main_iteration();
  }

#endif /* USE_GTK */
}

int comm_send_file(struct context *, char *);

/*
 * Parses a plugin description message, and returns an arglist with the
 * plugin in it.
 */
static struct nessus_plugin *
parse_plugin(buf)
  char *buf;
{
  char *str = NULL;
  char *t;
  size_t l;

  int    i_id;
  char id[32];
  char * name = NULL;
  char * category = NULL;
  char * copyright = NULL;
  char * description = NULL;
  char * summary = NULL;
  char * family = NULL;
  char * version = NULL;
  char * cve = NULL;
  char * bid = NULL;
  char * xref = NULL;
  struct nessus_plugin * ret;

  sscanf(buf, "%d", &i_id);

  
  snprintf(id, sizeof(id), "%d", i_id);
  l = strlen(id);

  str = parse_separator(buf);
  if(!str)
    return NULL;

  name = str;


  l += strlen(str) + 5;

  str = parse_separator(buf + l);
  if(!str) return NULL;
  category = str;

  l += strlen(str) + 5;
  str = parse_separator(buf + l);
  if(!str) return NULL;
  copyright = str;

  l += strlen(str) + 5;


  str = parse_separator(buf + l);
  if(!str) return NULL;
  t = str;
  while((t = strchr(t, ';')))
    t[0] = '\n';

  description = str;
  l += strlen(str) + 5;

  str = parse_separator(buf + l);
  if(!str) return NULL;
  summary = str;

  l += strlen(str) + 5;

  str = parse_separator(buf + l);
  if(!str) return NULL;
  family = str;


  l += strlen(str) + 5;
  str = parse_separator(buf + l);
  if(str)
  {
    version = str;


    l += strlen(str) + 5;
    str = parse_separator(buf + l);
    if(str != NULL)
    {
      cve = str;
      l += strlen(str) + 5;

      str = parse_separator(buf + l);
      if(str != NULL)
      {
	bid = str;
	l += strlen(str) + 5;
	str = parse_separator(buf + l);
	if(str != NULL) xref = str;
      }
    }
  }

  ret = nessus_plugin_new(id, name, category, copyright, description, summary, family, version, cve, bid, xref);   
  return ret;
}






/*
 * comm_init
 *
 * This function initializes the communication between 
 * the server and the client.
 * Its role is to check that the remote server is using NTP/1.1
 * 
 * Arguments :
 *  soc : a socket connected to the remote server
 * Returns :
 *  0 if the remote server is using NTP/1.1
 * -1 if it's not
 */

int
comm_init(soc, proto_name)
  int soc;
  char *proto_name;
{
  char *buf;
  int n = strlen(proto_name);

  /* What shall I do if it fails? */
  (void)write_stream_connection(soc, proto_name, n);

  buf = emalloc(15);
  recv_line(soc, buf, 14);
  if(strncmp(buf, proto_name, 11))
  {
    efree(&buf);
    return (-1);
  }
  efree(&buf);
  return (0);
}


/*
 * Retrieves the server preferences
 * we must make a difference between the prefs of the 
 * server itself, and the prefs of the plugins
 */
int
comm_get_preferences(context)
  struct context *context;
{
  char *buf = emalloc(32768);
  int finished = 0;
  struct arglist *serv_prefs, *serv_infos, *plugs_prefs = NULL;
  struct nessus_plugin * plugin = NULL;
  struct arglist *prefs = context->prefs;

#ifdef ENABLE_SAVE_TESTS
  context->sessions_saved = 0;
  context->detached_sessions_saved = 0;
#endif

  serv_prefs = arg_get_value(prefs, "SERVER_PREFS");
  if(!serv_prefs)
  {
    serv_prefs = emalloc(sizeof(struct arglist));
    arg_add_value(context->prefs, "SERVER_PREFS", ARG_ARGLIST, -1, serv_prefs);
  }

  serv_infos = emalloc(sizeof(struct arglist));
  if(arg_get_value(prefs, "SERVER_INFO"))
  {
    arg_free_all(arg_get_value(prefs, "SERVER_INFO"));
    arg_set_value(context->prefs, "SERVER_INFO", -1, serv_infos);
  }
  else
    arg_add_value(context->prefs, "SERVER_INFO", ARG_ARGLIST, -1, serv_infos);

  plugs_prefs = arg_get_value(prefs, "PLUGINS_PREFS");
  if(!plugs_prefs)
    {
      plugs_prefs = emalloc(sizeof(struct arglist));
      arg_add_value(context->prefs, "PLUGINS_PREFS", ARG_ARGLIST, -1, plugs_prefs);
    }

  network_gets(context->socket, buf, 32768);
  if(!strncmp(buf, "SERVER <|> PREFERENCES <|>", 26))
  {
    while(!finished)
    {
      bzero(buf, 32768);
      network_gets(context->socket, buf, 32768);;
      if(buf[strlen(buf) - 1] == '\n')
	buf[strlen(buf) - 1] = 0;
      if(!strncmp(buf, "<|> SERVER", 10))
	finished = 1;
      else
      {
	char *pref;
	char *value;
	char *v;
	char *a = NULL, *b = NULL, *c = NULL;

	pref = buf;
	v = strchr(buf, '<');
	if(!v)
	  continue;
	v -= 1;
	v[0] = 0;

	value = v + 5;
	v = emalloc(strlen(value) + 1);
	strncpy(v, value, strlen(value));
	a = strchr(pref, '[');
	if(a)
	  b = strchr(a, ']');
	if(b)
	  c = strchr(b, ':');
	if((!a) || (!b) || (!c))
	{
#ifdef ENABLE_SAVE_TESTS
	  if(!strcmp(pref, "ntp_save_sessions"))
	    context->sessions_saved = 1;
	  else if(!strcmp(pref, "ntp_detached_sessions"))
	    context->detached_sessions_saved = 1;
	  else
#endif
	  if(!strncmp(pref, "server_info_", strlen("server_info_")))
	  {
	    arg_add_value(serv_infos, pref, ARG_STRING, strlen(v), v);
	  }
	  else
	  {
	    /* Don't set the value if set already */
	    if(arg_get_type(serv_prefs, pref) < 0)
	      arg_add_value(serv_prefs, pref, ARG_STRING, strlen(v), v);
	  }
	}
	else if(F_quiet_mode)
	{
	  /*
	   * Note that when using the cli,
	   * the plugin prefs are not stored the same way in memory
	   */
	  if(arg_get_type(plugs_prefs, pref) < 0)
	  {
	    char *x = strchr(v, ';');

	    if(!ListOnly && x)
	      x[0] = '\0';
	    arg_add_value(plugs_prefs, pref, ARG_STRING, strlen(v), v);
	  }
	}
	else
	{
	  /* the format of the pref name is xxxx[xxxx] : this is a plugin pref */
	  char *plugname;
	  char *type;
	  char *name;
	  struct arglist *pprefs, *prf;
	  char *fullname = strdup(pref);

	  while(fullname[strlen(fullname) - 1] == ' ')
	    fullname[strlen(fullname) - 1] = '\0';
	  a[0] = 0;
	  plugname = emalloc(strlen(pref) + 1);
	  strncpy(plugname, pref, strlen(pref));

	  a[0] = '[';
	  a++;
	  b[0] = 0;
	  type = emalloc(strlen(a) + 1);
	  strncpy(type, a, strlen(a));
	  b[0] = ']';
	  c++;
	  name = emalloc(strlen(c) + 1);
	  strncpy(name, c, strlen(c));

	  plugin = nessus_plugin_get_by_name(context->plugins, plugname);
	  if( plugin == NULL )
	  {
	    plugin = nessus_plugin_get_by_name(context->scanners, plugname);
	    if(plugin == NULL )
	    {
	      fprintf(stderr,
		  _("Error : we received a preference (%s) for the plugin %s\n"),
		  name, plugname);
	      fprintf(stderr,
		  _("but apparently the server has not loaded it\n"));
		  continue;
	    }
	  }
	  pprefs = plugin->plugin_prefs;
	  if( pprefs == NULL )
	  {
	    pprefs = emalloc(sizeof(struct arglist));
	    plugin->plugin_prefs = pprefs;
	  }

	  if ( arg_get_value(pprefs, name) == NULL )
	  {
	    char * value = NULL;


	    prf = emalloc(sizeof(struct arglist));

	  /*
	   * No default value for files to upload (we don't want the
	   * server to suggest we upload /etc/shadow ;)
	   */

	  if ( arg_get_type(plugs_prefs, fullname) == ARG_INT )
	    {
	    int d = (int)arg_get_value(plugs_prefs, fullname);
	    if ( d == 0 ) value = "no";
	    else value = "yes";
	    }
	 else if ( arg_get_type(plugs_prefs, fullname) == ARG_STRING )
	     value = arg_get_value(plugs_prefs, fullname);
	 else
	  {
	  if(!strcmp(type, PREF_FILE))
	     value = "";
	  else
	     value = v;
	  }

     /* Check whether it is a radiobutton plugin preference and whether
      * it's value has no ';'. If so, it comes from an old, broken nessusrc file
      * and we have to repair it:
      * Make the found value the first one and add all others reported from the
      * server (in variable v) append to it, separated by semicolons.
      * This will keep the old value the selected one.
      */
     if (! strcmp(type, "radio") && ! strchr(value, ';')) {
       char * s = strstr(v, value);
       if (s) {
         if (s == v)
           /* it is the first in the list anyway, so simply take
            * the server string */
           value = estrdup(v);
         else {
	   char * p;
           /* append the stuff before the found substring to value */
           s[0] = 0;
	   p = emalloc( strlen(value) + 1 + strlen(v) + 1);
	   strncpy(p, value, strlen(value));
	   value = p;
           value = strcat(value, ";");
           value = strcat(value, v);

           /* take care of the stuff after the found substring and append
            * it also to value */
           v = strstr(s, ";");
           if (v) {
             v++;
             value = (char *) erealloc(value, strlen(value) + strlen(v) + 1);
             value = strcat(value, v);
           } else {
             /* in this case we a ; to much in value already */
             value[strlen(value)-1] = 0;
           }
         }
       } else
         /* should acutally not happen, but then take server string */
         value = estrdup(v);
     }

	  arg_add_value(prf, "value", ARG_STRING, strlen(value), value);

	  arg_add_value(prf, "type", ARG_STRING, strlen(type), type);
	  arg_add_value(prf, "fullname", ARG_STRING, strlen(fullname), fullname);
	  arg_add_value(pprefs, name, ARG_ARGLIST, -1, prf);
	  }
	}
      }
    }
  }
  efree(&buf);
  return (0);
}


static int
cli_send_prefs_arglist(context, pref, upload, pprefs)
  struct context * context;
  struct arglist *pref;
  harglst **upload;
  int pprefs;
{
  if(!pref)
    return -1;

  while(pref->next)
  {
    if(pref->type == ARG_STRING)
    {
      if(strstr(pref->name, "[" PREF_FILE "]:"))
      {
	if(!*upload)
	  *upload = harg_create(50);
	harg_add_int(*upload, pref->value, 1);
      }

    network_printf(context->socket, "%s <|> %s\n", pref->name, pref->value);
    }
    else if(pref->type == ARG_INT)
    {
      network_printf(context->socket, "%s <|> %s\n", pref->name, pref->value ? "yes" : "no");
    }
    pref = pref->next;
  }
  return 0;
}


static int
cli_comm_send_preferences(context)
  struct context *context;
{
  struct arglist *preferences = context->prefs;
  harglst *files_to_send = NULL;
  struct arglist *pref = arg_get_value(preferences, "SERVER_PREFS");
  struct arglist *pprefs = arg_get_value(preferences, "PLUGINS_PREFS");

  network_printf(context->socket, "CLIENT <|> PREFERENCES <|>\n");
  /*
   * workaround to use new features while keeping
   * backward compatibility
   */
  network_printf(context->socket, "ntp_opt_show_end <|> yes\n");
  network_printf(context->socket, "ntp_keep_communication_alive <|> yes\n");
  network_printf(context->socket, "ntp_short_status <|> yes\n");
  network_printf(context->socket, "ntp_client_accepts_notes <|> yes\n");
  network_printf(context->socket, "ntp_escape_crlf <|> yes\n");
  if(pref)
    cli_send_prefs_arglist(context, pref, &files_to_send, 0);
  if(pprefs)
    cli_send_prefs_arglist(context, pprefs, &files_to_send, 1);
  network_printf(context->socket, "<|> CLIENT\n");
  if(files_to_send)
  {
    hargwalk *hw;
    char *key;

    hw = harg_walk_init(files_to_send);
    while((key = (char *)harg_walk_next(hw)))
    {
      comm_send_file(context, key);
    }
    harg_close_all(files_to_send);	/* frees memory */
  }
  return (0);
}



static int
gui_comm_send_preferences(context)
  struct context *context;
{
  struct arglist *preferences = context->prefs;
  harglst *files_to_send = NULL;
  struct arglist *pref = arg_get_value(preferences, "SERVER_PREFS");
  struct nessus_plugin *plugins[2];
  int i;

  context_sync_plugin_prefs(context);

  plugins[0] = context->plugins;
  plugins[1] = context->scanners;

  network_printf(context->socket, "CLIENT <|> PREFERENCES <|>\n");
  /*
   * workaround to use new features while keeping
   * backward compatibility
   */
  network_printf(context->socket, "ntp_opt_show_end <|> yes\n");
  network_printf(context->socket, "ntp_keep_communication_alive <|> yes\n");
  network_printf(context->socket, "ntp_short_status <|> yes\n");
  network_printf(context->socket, "ntp_client_accepts_notes <|> yes\n");
  network_printf(context->socket, "ntp_escape_crlf <|> yes\n");
  while(pref && pref->next)
  {
    if(pref->type == ARG_STRING)
    {
      network_printf(context->socket, "%s <|> %s\n", pref->name, pref->value);
    }
    else if(pref->type == ARG_INT)
    {
      network_printf(context->socket, "%s <|> %s\n", pref->name, pref->value ? "yes" : "no");
    }
    pref = pref->next;
  }

  /* send the plugins prefs back to the server */
  for(i = 0; i < 2; i++)
  {
    struct nessus_plugin *plugs = plugins[i];

    while(plugs != NULL )
    {
      struct arglist *plugin_prefs = plugs->plugin_prefs;
      while(plugin_prefs && plugin_prefs->next)
      {
	char *name = plugin_prefs->name;
	char *type = arg_get_value(plugin_prefs->value, "type");
	char *value = arg_get_value(plugin_prefs->value, "value");

        /* This cuts down the list of alternatives (separated by ';')
         * down to just the first element, because the Nessus Server
         * does not handle cutting itself.
         * Background: The value of a "radio" preference is the set
         * of possible values at one point (sent by Server and managed
         * within the NessusClient) and the value chosen by the user at another
         * (execution time of a scan by Nessus Server).
         */
        if(!strcmp(type, PREF_RADIO))
        {
          char * t, * v;
          v = strdup(value);
          if (v && (t = strchr(v, ';')))
            t[0] = 0;
          network_printf(context->socket, "%s[%s]:%s <|> %s\n", plugs->name, type, name, v);
          free(v);
        } else
          network_printf(context->socket, "%s[%s]:%s <|> %s\n", plugs->name, type, name, value);

	if(!strcmp(type, PREF_FILE))
	{
	  if(!files_to_send)
	    files_to_send = harg_create(50);
	  harg_add_int(files_to_send, value, 1);
	}
	plugin_prefs = plugin_prefs->next;
      }
      plugs = plugs->next;
    }
  }
  network_printf(context->socket, "<|> CLIENT\n");
  if(files_to_send != NULL )
  {
    hargwalk *hw;
    char *key;

    hw = harg_walk_init(files_to_send);
    while((key = (char *)harg_walk_next(hw)))
    {
      comm_send_file(context, key);
    }
    harg_close_all(files_to_send);	/* frees memory */
  }
  return (0);
}



int
comm_send_preferences(context)
  struct context *context;
{
  if(F_quiet_mode)
    return cli_comm_send_preferences(context);
  else
    return gui_comm_send_preferences(context);
}

int
comm_send_file(context ,fname)
  struct context * context;
  char *fname;
{
  int fd;
  struct stat stt;
  long tot = 0;
  char buff[1024];
  int len;

  if(!fname || !strlen(fname))
    return 0;

  fd = open(fname, O_RDONLY);
  if(fd < 0)
  {
    show_error(_("Can't open %s: %s"), fname, strerror(errno));
    return -1;
  }

  fstat(fd, &stt);
  len = (int)stt.st_size;
  network_printf(context->socket, "CLIENT <|> ATTACHED_FILE\n");
  network_printf(context->socket, "name: %s\n", fname);
  network_printf(context->socket, "content: octet/stream\n");
  network_printf(context->socket, "bytes: %d\n", len);
  tot = len;
  while(tot > 0)
  {
    int m = 0, n;

    bzero(buff, sizeof(buff));
    n = read(fd, buff, MIN(tot, sizeof(buff)));
    while(m < n)
    {
      int e;

      e = nsend(context->socket, buff + m, n - m, 0);
      if(e < 0)
      {
	show_error(_("Error reading from %s: %s"), fname, strerror(errno));
	close(fd);
	return -1;
      }
      else
	m += e;
    }
    tot -= n;
  }
  network_gets(context->socket, buff, sizeof(buff) - 1);
  return 0;
}

int
comm_send_rules(context)
  struct context *context;
{
  struct arglist *rules = arg_get_value(context->prefs, "CLIENTSIDE_USERRULES");

  network_printf(context->socket, "CLIENT <|> RULES <|>\n");
  while(rules && rules->next)
  {
    network_printf(context->socket, "%s\n", rules->value);
    rules = rules->next;
  }
  network_printf(context->socket, "<|> CLIENT\n");
  return (0);
}


void
comm_get_preferences_errors(context)
  struct context *context;
{
  char buf[512];

  network_gets(context->socket, buf, sizeof(buf));
  network_gets(context->socket, buf, sizeof(buf));
}


/*
 * Retrieves the server rules and store them in
 * a subcategory in the preferences
 */
int
comm_get_rules(context)
  struct context *context;
{
  struct arglist *serv_prefs = arg_get_value(context->prefs, "SERVER_PREFS");
  struct arglist *rules = NULL;
  char *buf = emalloc(32768);
  int finished = 0;

  rules = arg_get_value(serv_prefs, "RULES");
  if(!rules)
  {
    rules = emalloc(sizeof(struct arglist));
    arg_add_value(serv_prefs, "RULES", ARG_ARGLIST, -1, rules);
  }

  network_gets(context->socket, buf, 32768);
  if(!strncmp(buf, "SERVER <|> RULES <|>", 20))
  {
    while(!finished)
    {
      char *rule;
      network_gets(context->socket, buf, 32768);
      if(strstr(buf, "<|> SERVER"))
      {
        finished = 1;
      }
      else
      {
        struct arglist *t = rules;
        int ok = 1;
        int i = 0;

        rule = emalloc(strlen(buf));
        strncpy(rule, buf, strlen(buf) - 1);
        while(t && t->next && ok)
        {
          if(!strcmp(t->value, rule))
            ok = 0;
          t = t->next;
        }
        if(ok)
        {
          char name[10];
          snprintf(name, sizeof(name), "%d", ++i);
          arg_add_value(rules, name, ARG_STRING, strlen(rule), rule);
        }
        else
          efree(&rule);
      }
    }
  }
  efree(&buf);
  return (0);
}


/*
 * Add plugin (or scanner) to plugins list and plugin set
 * XXX: do we need hashing for pluginset?
 */
static struct nessus_plugin *
comm_add_plugin(plugins, pluginset, plugin)
  struct nessus_plugin *plugins;
  struct arglist *pluginset;
  struct nessus_plugin *plugin;
{
  char *category = plugin->category;
  char *asc_id =   plugin->asc_id;
  int in_pluginset = (int)(arg_get_type(pluginset, asc_id) >= 0);
  int enabled = 0;

  if(in_pluginset)
  {
    if(arg_get_value(pluginset, asc_id))
      enabled = 1;
  }
  else if(strcmp(category, "scanner"))
    enabled = 1;

  plugin->enabled = enabled;
  plugin->next = plugins;

  if(!in_pluginset)
    arg_add_value(pluginset, asc_id, ARG_INT, sizeof(int), (void *)enabled);

  return plugin;
}

int
comm_get_plugins(context)
  struct context *context;
{
  char *buf;
  int bufsz;
  struct arglist *plugin_set;
  struct arglist *scanner_set;
#ifdef USE_GTK
  context_reset_plugin_tree(context);
#endif
  context->plugins = NULL;
  context->scanners = NULL;
  plugin_set = prefs_get_pluginset(context, "PLUGIN_SET", NULL);
  scanner_set = prefs_get_pluginset(context, "SCANNER_SET", NULL);

  bufsz = 1024 * 1024;
  buf = emalloc(bufsz);
  network_gets(context->socket, buf, 27);
  if(strncmp(buf, "SERVER <|> PLUGIN_LIST <|>", 26))
  {
    efree(&buf);
    return (-1);
  }

  for(;;)
  {
    comm_update_ui(context, COMM_GET_PLUGINS);
    network_gets(context->socket, buf, bufsz);
    if(buf[0] == '\0')
    {
      show_error(_("The daemon shut down the communication"));
      break;
    }
    else if(!strncmp(buf, "<|> SERVER", 10))
      break;
    else
    {
      struct nessus_plugin *plugin = parse_plugin(buf);

      if ( plugin == NULL )
      {
	fprintf(stderr, "Could not parse %s\n", buf);
	continue;
      }

      if( strcmp(plugin->category, "scanner" ) == 0 )
	context->scanners = comm_add_plugin(context->scanners, scanner_set, plugin);
      else
	context->plugins = comm_add_plugin(context->plugins, plugin_set, plugin);
    }
  }

  efree(&buf);
  return (0);
}








/*-------------------------------------------------------------------------

			Sessions management
			
---------------------------------------------------------------------------*/

/*
 * Does the server support sessions saving ?
 */
int
comm_server_restores_sessions(context)
  struct context *context;
{
#ifdef ENABLE_SAVE_TESTS
  return context->sessions_saved;
#else
  return 0;
#endif
}

int
comm_server_detached_sessions(context)
  struct context *context;
{
#ifdef ENABLE_SAVE_TESTS
  return context->detached_sessions_saved;
#else
  return 0;
#endif
}

harglst *
comm_get_sessions(struct context * context)
{
  char buff[32768];
  harglst *ret = NULL;

  network_printf(context->socket, "CLIENT <|> SESSIONS_LIST <|> CLIENT\n");
  network_gets(context->socket, buff, sizeof(buff));
  if(!strcmp(buff, "SERVER <|> SESSIONS_LIST\n"))
  {
    ret = harg_create(15000);
    while(!strstr(buff, "<|> SERVER"))
    {
      char *t;

      network_gets(context->socket, buff, sizeof(buff));
      t = strchr(buff, ' ');
      if(t && !strstr(buff, "<|> SERVER"))
      {
	if(buff[strlen(buff) - 1] == '\n')
	  buff[strlen(buff) - 1] = '\0';
	t[0] = 0;
	t++;
	harg_add_string(ret, buff, t);
      }
    }
  }
  return ret;
}


void
comm_delete_session(context, name)
  struct context * context;
  char *name;
{
  network_printf(context->socket, "CLIENT <|> SESSION_DELETE <|> %s <|> CLIENT\n", name);
}

void
comm_restore_session(context, name)
  struct context * context;
  char *name;
{
  network_printf(context->socket, "CLIENT <|> SESSION_RESTORE <|> %s <|> CLIENT\n", name);
}

void
comm_stop_detached_session(context ,name)
  struct context * context;
  char *name;
{
  network_printf(context->socket, "CLIENT <|> STOP_DETACHED <|> %s <|> CLIENT\n", name);
}

harglst *
comm_get_detached_sessions(struct context * context)
{
  char buff[32768];
  harglst *ret = NULL;

  network_printf(context->socket, "CLIENT <|> DETACHED_SESSIONS_LIST <|> CLIENT\n");
  network_gets(context->socket, buff, sizeof(buff));
  if(!strcmp(buff, "SERVER <|> DETACHED_SESSIONS_LIST\n"))
  {
    ret = harg_create(15000);
    while(!strstr(buff, "<|> SERVER"))
    {
      char *t;

      network_gets(context->socket, buff, sizeof(buff));
      t = strchr(buff, ' ');
      if(t && !strstr(buff, "<|> SERVER"))
      {
	if(buff[strlen(buff) - 1] == '\n')
	  buff[strlen(buff) - 1] = '\0';
	t[0] = 0;
	t++;
	harg_add_string(ret, buff, t);
      }
    }
  }
  return ret;
}





int
comm_get_dependencies(context)
  struct context *context;
{
  char buff[32768];

  buff[0] = '\0';
  network_gets(context->socket, buff, sizeof(buff) - 1);
  if(context->dependencies)
    arg_free_all(context->dependencies);
  context->dependencies = emalloc(sizeof(struct arglist));
  if(!strcmp(buff, "SERVER <|> PLUGINS_DEPENDENCIES\n"))
  {
    network_gets(context->socket, buff, sizeof(buff) - 1);
    while(strcmp(buff, "<|> SERVER\n"))
    {
      struct arglist *deps;
      char *name;
      char *t = strstr(buff, " <|> ");
      char *s;

      comm_update_ui(context, COMM_GET_DEPENDENCIES);
      if(t)
      {
	s = t + 5;
	t[0] = '\0';
	name = buff;
	deps = emalloc(sizeof(struct arglist));
	while(s)
	{
	  t = strstr(s, " <|> ");
	  if(t)
	  {
	    t[0] = '\0';
	    arg_add_value(deps, s, ARG_INT, (sizeof(int)), (void *)1);
	    s = t + 5;
	  }
	  else
	    s = NULL;
	}
	arg_add_value(context->dependencies, name, ARG_ARGLIST, -1, deps);
      }
      network_gets(context->socket, buff, sizeof(buff) - 1);
    }
  }
  /* XXX: this resetting should be moved to another place
   * once there is a separate module for handling the progress bar. */
  comm_update_ui(context, 0); /* reset progress bar */
  return 0;
}
