/*
**  GXSNMP -- An snmp management application
**  Copyright (C) 1998 Gregory McLean
**
**  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 2 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.,  59 Temple Place - Suite 330, Cambridge, MA 02139, USA.
**
**  plugin-sample.c -- A sample plugin, it don't do anything but its useful
**                     for testing.
*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <gnome.h>
#include "menus.h"
#include "main.h"
#include "plugins.h"
#include "gxsnmp_map.h"
#include "gxsnmp_host.h"
#include "route_dialog.h"
#include "rfc1213.h"

#include "debug.h"

extern GtkWidget * host_popup_menu;

/****************************************************************************
 * Forward references
 ***************************************************************************/
static void       menu_callback            (GtkWidget     *widget,
					    gpointer      data);
/*static gboolean   start_request            (rt_data       *rt); */
static void       row_cb                   (GHashTable    *objs,
                                            guint         indlen,
					    gpointer      data);
static void       error_cb                 (gpointer      data);
static void       finish_cb                (gpointer      data);
/****************************************************************************
 *  Static data
 ***************************************************************************/

static gchar * menu_location;

static gulong destination[] = { IPROUTEDEST };
static gulong protocol[]    = { IPROUTEPROTO };
static gulong netmask[]     = { IPROUTEMASK };
static gulong nexthop[]     = { IPROUTENEXTHOP };
static gulong ifindex[]     = { IPROUTEIFINDEX };
static gulong metric1[]     = { IPROUTEMETRIC1 };
static gulong age[]         = { IPROUTEAGE };
static gulong type[]        = { IPROUTETYPE };
static gulong ifdescr[]     = { IFDESCR, 0 };

#define oidlen(val) (sizeof(val)/sizeof(gulong))

/******************************************************************************
**
**  The plugin load routine
**
**  This function is invoked at the very start of GXSNMP.  It should
**  set one or more of the following flags:
**
**  PLUGIN_DATABASE	-- for plugins that provide a database service
**  PLUGIN_COLLECTOR	-- for plugins that do not require GTK services
**  PLUGIN_APPLICATION	-- for plugins that require GTK services
**
**  The flags determine at what point the start function is called during
**  initialization.  The database plugins need to be started before the
**  database is read in (for obvious reasons), and the collector and 
**  application plugins are not started until the database is completely
**  read in and set up.
**
******************************************************************************/

gint
load_plugin (PluginData * pd)
{
  pd->type = PLUGIN_APPLICATION;
  return 0;
}

/****************************************************************************
 *
 *  The plugin unload routine
 *
 *  This function is invoked during the shutdown callback in main.c
 *  It should free any storage the plugin might have allocated, and
 *  generally clean itself up.
 *
 ***************************************************************************/
void
unload_plugin (PluginData * pd)
{
}

/******************************************************************************
**
**  The plugin start routine
**
**  This function is invoked during initialization.  It should perform      
**  initialization and start the plugin running.
**
**  Included here is sample code to install the plugin in the 'plugins' menu.
**  A collector-only plugin would NOT install a menu entry, and would not
**  have a menu callback routine (below).
**
******************************************************************************/

gint 
start_plugin (PluginData * pd)
{
  GnomeUIInfo   * menu;
  d_print (DEBUG_PLUGINS, "Starting RouteTable plugin.\n");

  menu_location         = g_strdup_printf ("%s/", gettext("_Plugins"));

  menu                  = g_malloc0 (2 * sizeof(GnomeUIInfo));
  menu->type            = GNOME_APP_UI_ITEM;
  menu->label           = g_strdup ("Route Table");
  menu->hint            = NULL;
  menu->moreinfo        = menu_callback;       /* Menu activation cb routine */
  menu->user_data       = NULL;                /* User data for the callback */
  menu->unused_data     = NULL;                /* Reserved for future use */
  menu->pixmap_type     = GNOME_APP_PIXMAP_STOCK;
  menu->pixmap_info     = GNOME_STOCK_MENU_BLANK;
  menu->accelerator_key = 0;
  (menu + 1)->type      = GNOME_APP_UI_ENDOFINFO;

  gxsnmp_add_to_host_popup (menu);
 
  return 0;
}

/******************************************************************************
**
**  Menu callback routine for the plugin
**
**  This function is invoked when the user selects the plugin from the
**  plugins menu.
**
******************************************************************************/

static void
menu_callback (GtkWidget * widget, gpointer data)
{
  GtkWidget       *dialog_widget;
  DB_host         *dbh;
  DB_interface    *dbi;
  DB_snmp         *dbs;
  GList           *gl;
  GXsnmp_map_item *item;
  
  item = GXSNMP_MAP_ITEM (data);
  dbh = item->DB_graph->DB_host;
  if (!(gl = dbh->DB_interfaces))
    {
      notice_dlg (_("The selected host has no interfaces defined on it,\n"
		    "there must be at least one defined to load the route\n"
		    "table from.\n\nThe route table can not be loaded.\n"));
      return;
    }
  dbi = (DB_interface *)gl->data;
  if (dbi)
    {
      rt_data    *rt;

      dbs = g_sqldb_row_find (snmp_sqldb, "_rowid", &dbi->snmp);
      if (!dbs)
	{
	  notice_dlg (_("The selected host has interfaces defined, but\n"
			"there appears to be a problem with the SNMP\n"
			"configuration for the interface.\n"
			"\nThe route table can not be loaded.\n"));
	  return;
	}
      
      g_print ("Load route table from %s using the %s snmp config.\n", 
	       dbh->dns_name, dbs->name);
      rt          = g_new0 (rt_data, 1);
      rt->if_desc = NULL;
      rt->rowid   = dbi->rowid;
      rt->table   = NULL;
      if (!gxsnmp_dialog_check ("route_table", NULL))
	{
	  dialog_widget = gxsnmp_route_dialog_new (rt);
	  if (dialog_widget)
	    {
	      gxsnmp_dialog_add (dialog_widget, "Route Table", NULL, 
				 g_strdup ("Route Table"));
	      rt->dialog = dialog_widget;
	      start_request (rt);
	    }
	}
      
    }
}
/****************************************************************************
 * Start loading the route table from the specified node.
 ***************************************************************************/
gboolean
start_request (rt_data *rt)
{
  DB_interface    *dbi;
  DB_snmp         *dbs;
  GSList          *objs;
  D_FUNC_START;
  if (!(dbi = g_sqldb_row_find (interface_sqldb, "_rowid", &rt->rowid)))
    {
      notice_dlg (_("No interfaces on the selected host.\n\nRoute Table will "
		  "not be loaded.\n"));
      return FALSE;
    }
  if (!(dbs = (DB_snmp *)dbi->DB_snmp))
    {
      notice_dlg (_("Interface defined but no snmp configuration found.\n\n"
		    "Route table will not be loaded."));
      return FALSE;
    }
  if (!dbi->address)
    {
      notice_dlg (_("There was no address associated with this interface.\n"
		    "An address must be specified in order to load the\n"
		    "route table from the host.\n\nRoute table will not be "
		    "loaded."));
      return FALSE;
    }
  objs = NULL;
  g_pdu_add_oid (&objs, protocol, oidlen(protocol), SNMP_NULL, NULL);
  g_pdu_add_oid (&objs, destination, oidlen(destination), SNMP_NULL, NULL);
  g_pdu_add_oid (&objs, netmask, oidlen(netmask), SNMP_NULL, NULL);
  g_pdu_add_oid (&objs, nexthop, oidlen(nexthop), SNMP_NULL, NULL);
  g_pdu_add_oid (&objs, ifindex, oidlen(ifindex), SNMP_NULL, NULL);
  g_pdu_add_oid (&objs, metric1, oidlen(metric1), SNMP_NULL, NULL);
  g_pdu_add_oid (&objs, age, oidlen(age), SNMP_NULL, NULL);
  g_pdu_add_oid (&objs, type, oidlen(type), SNMP_NULL, NULL);
  rt->host.domain         = dbi->transport;
  rt->host.rcomm          = dbs->read_c ? g_strdup (dbs->read_c) : "public";
  rt->host.wcomm          = dbs->write_c ? g_strdup (dbs->write_c) : "private";
  rt->host.retries        = dbs->retries;
  rt->host.name           = g_strdup (dbi->address);
  rt->host.status         = 0;
  rt->host.port           = dbs->port;
  rt->host.timeout        = dbs->timeout;
  rt->host.version        = dbs->version;

  rt->table               = g_snmp_table_new(&rt->host, objs, 
			      error_cb, row_cb, finish_cb, rt);
  g_snmp_table_get(rt->table);
  route_dialog_set_state (GXSNMP_ROUTE_DIALOG (rt->dialog));
  D_FUNC_END;
  return TRUE;
}
/****************************************************************************
 * Helper function 
 ***************************************************************************/
static void
free_hash_element (void *key, void *val, void *data)
{
  if (val)
    g_free (val);
  if (key)
    g_free (key);
}

static gchar*
match_strval(guint32 val, const value_string *vs) {
  gint i = 0;

  while (vs[i].strptr) {
    if (vs[i].value == val)
      return(vs[i].strptr);
    i++;
  }
  return(NULL);
}

/****************************************************************************
 * SNMP Data callback
 ***************************************************************************/
static void
row_cb (GHashTable *objs, guint indlen, gpointer data)
{
  gchar                buf[512];
  rt_data              *rt;
  GSList               *sync_objs;
  GSList               *sync_vars;
  SNMP_OBJECT          *obj;
  gint                 i;
  gchar                *kludge[8];        /* should do this dynamicly */
  gchar                *ptr;
  gint 	               if_index;
  
  D_FUNC_START;
  rt = (rt_data *)data;
  d_print (DEBUG_DUMP, "Completed request %x\n", (guint)rt->table);
  /* Set up the ifDescr cache, we have to get this from a diffrent table,
   * which means another get based on the data that the request we are 
   * processing returns. We do this in a sync fashion within the async
   * callback. So its a potential mystifying UI locker..
   */
  if (!rt->if_desc)
    rt->if_desc = g_hash_table_new (g_int_hash, g_int_equal);
  for (i = 0; i < 8; i++)
    {
      obj = (SNMP_OBJECT *) g_hash_table_lookup(objs, &i);
      if (!obj)
	{
	  g_warning("Problems getting obj pointer. Smells fishy here...\n");
	  kludge[i]=g_strdup("");
	  continue;
	}
      g_snmp_printf (buf, sizeof (buf), obj);
      switch(i)
	{
	  case 0: /* Protocol */
	    if ((ptr = match_strval(obj->syntax.ul[0], ipRouteProto)))
	      kludge[i] = g_strdup(ptr);
	    else
	      kludge[i] = g_strdup(buf);
	    break;
	  case 4: /* Index */
 	    if_index = obj->syntax.ul[0];
	    if ( (ptr = (gchar *)g_hash_table_lookup(rt->if_desc, &if_index)))
	      {
	        /* Great! we got this if descr already. */
	        kludge[i] = g_strdup (ptr);
	      }
	    else
	      {
	        /* Lets load it. */
	        d_print (DEBUG_DUMP, "Fetching interface description.\n");
	        sync_vars = NULL;
	        sync_objs = NULL;
	        /* Get the ifDescr from the interfaces table */
	        ifdescr[(oidlen(ifdescr))-1] = if_index;
	        g_pdu_add_oid (&sync_vars, ifdescr, oidlen(ifdescr), 
			       SNMP_NULL, NULL);
	        sync_objs = g_sync_get ( &rt->host, sync_vars);
	        if (!sync_objs)
		  {
		    g_warning ("Well hell. sync get from %s "
			       "failed.\n", rt->host.name);
		    g_slist_free (sync_vars);
		    sync_vars = NULL;
		  }
	        else
		  {
		    gint *ptri;
		    /* Good the host responded lets cache it. */
		    g_snmp_printf (buf, sizeof (buf), sync_objs->data);
		    kludge[i] = g_strdup (buf);
		    ptri = g_malloc (sizeof (gint));
		    *ptri = if_index;
		    g_hash_table_insert (rt->if_desc, ptri, g_strdup(buf));
		  }
	      }
	    break;
	  case 7: /* Type */
	    if ((ptr = match_strval(obj->syntax.ul[0], ipRouteType)))
	      kludge[i] = g_strdup(ptr);
	    else
	      kludge[i] = g_strdup(buf);
	    break;
	  default:
	    kludge[i] = g_strdup(buf);
        }
    }
  gtk_clist_append (GXSNMP_ROUTE_DIALOG(rt->dialog)->clist, kludge);
  D_FUNC_END;
}


static void
finish_cb (gpointer data)
{
  rt_data              *rt;
  D_FUNC_START;
  rt = (rt_data *)data;
  d_print (DEBUG_TRACE, "Done!\n");
  /* we are done loading the table clean up.*/
  if (rt->if_desc)
    {
      g_hash_table_foreach (rt->if_desc, free_hash_element, NULL);
      g_hash_table_destroy (rt->if_desc);
    }
  rt->if_desc = NULL;
  g_snmp_table_destroy(rt->table);
  rt->table   = NULL;
  route_dialog_set_state (GXSNMP_ROUTE_DIALOG (rt->dialog));
  D_FUNC_END;
}

/****************************************************************************
 * SNMP timeout 
 **************************************************************************/
static void
error_cb (gpointer data)
{
  rt_data      *rt;
  D_FUNC_START;
  g_return_if_fail (data != NULL);
  rt = (rt_data *)data;
  d_print (DEBUG_DUMP, "Request timed out %x\n", (guint)rt->table);
  g_snmp_table_destroy(rt->table);
  rt->table   = NULL;
  D_FUNC_END;
}
/* EOF */
