/* -*- Mode: C -*-
 * $Id: gxsnmp_database_pdu.c,v 1.3 2000/09/20 21:20:52 gregm Exp $
 *
 * Copyright 2000 Larry Liimatainen
 *  gxsnmp_database_pdu.c -- data encapsulation
 *
 *  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.
 *
 *  gxsnmp_database_pdu.c -- low level package transfer
 *
 */

/**
 * All these functions operate on a packet that is
 * sent between database daemon and the client API.
 *
 *
 * PROTOCOL LAYOUT
 *
 * +--------+--------+--------+--------+---------+-----+
 * | Action | Length | ObjLen | Object | Members | ... |
 * +--------+--------+--------+--------+---------+-----+
 * <------------- Header -------------><--- PDU ------->
 *                                        * We are here
 *
 * HEADER:
 *
 *    Action is a 32bit integer telling upper layer what
 *    to do with data in PDU.
 *    Length is a 32bit integer and has the size of header
 *    length and PDU length.
 *
 *    ObjLen is the length of Object in header.
 *
 *    Object is a single piece of data, used to represent
 *    a database table.
 *
 * PDU
 *
 *    The PDU consist of one or more Members. Each
 *    member has following layout:
 *
 *    +--------------+------------+------------+
 *    | memberLength | memberType | MemberData |
 *    +--------------+------------+------------+
 *
 *    memberLength is the length of the Member.
 *
 *    memberType tells upper layer how to read
 *    data in MemberData, ie a string, integer..
 *
 *    memberData holds actual data, that usually
 *    represent a field in a database row that is
 *    processed.
 *
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <glib.h>
#include <stdlib.h>

#include "gxsnmp_database_pdu.h"

/**
 * gxsnmp_db_decode_output:
 *
 * Use this to get a quick look at your packet, before entering gdb
 * offset -- offset
 * packet -- source of output
 * rewind -- offset to rewind
 * len    -- how much data to output
 *
 **/
void
gxsnmp_db_decode_output (gint *offset, unsigned char *packet,
			 gint rewind, gint len)
{
  unsigned char c;
  gint          i;

  g_print ("\nERROR_DATADUMP: offset is %d, start is (offset - %d), "
	   "printing %d bytes\n", *offset, rewind, len);
  i  = *offset;
  i -= rewind;
  for (;len;len--)
    {
      c = *(packet + i);
      g_print ("%x", c);
      i++;
    }
  g_print ("\n");
}
/**
 * gxsnmp_db_get_member:
 *
 **/
gint
gxsnmp_db_get_member (gint *n, gpointer packet, gpointer buffer)
{
  gint    ml, mt;

  ml = mt = 0;
  /* extract the MemberLen */
  memcpy (&ml, packet+*n, sizeof(gint));
  *n += sizeof (gint);
  if (ml < 8)
    {
      g_warning ("MemberLen is invalid\n");
      gxsnmp_db_decode_output (n, packet, 20, 40);
      return -1;
    }
  if (ml == 8)
    {
      switch(mt)
	{
        case PDUMEMTYPE_INT:
          *(gint *)buffer = 0;
          return PDUMEMTYPE_INT;
        case PDUMEMTYPE_STR:
          *(gchar *)buffer = 0;
	           return PDUMEMTYPE_STR;
        case PDUMEMTYPE_DOUBLE:
          *(gdouble *)buffer = 0;
          return PDUMEMTYPE_DOUBLE;
      }
  }
  /* Its a valid Non NULL packet */
  switch(mt)
    {
    case PDUMEMTYPE_INT:
      memcpy(buffer, packet+*n, sizeof(gint));
      *n += sizeof(gint);
      return PDUMEMTYPE_INT;
    case PDUMEMTYPE_DOUBLE:
      memcpy(buffer, packet+*n, sizeof(gdouble));
      *n += sizeof(gdouble);
      return PDUMEMTYPE_DOUBLE;
    case PDUMEMTYPE_STR:
      ml -= sizeof(gint) * 2;      
      memcpy(buffer, packet+*n, ml);
      *((gchar *)(buffer+ml)) = 0;       /* dont sure this works....*/
      *n += ml;    
      return PDUMEMTYPE_STR;
    default:     /*decode error*/
      g_warning ("gxsnmp_db_get_member(): APP_ERR packet decode error, "
		 "undefined structure member\n");
      gxsnmp_db_decode_output(n, packet, 20, 40);
    }
  return 0;
}
/**
 * gxsnmp_db_add_guint:
 *
 **/
void
gxsnmp_db_add_guint (gint *n, gpointer packet, guint data)
{
  gint tmp;
  
  tmp = sizeof(guint) * 3; 
  memcpy(packet+*n, &tmp, sizeof(guint));
  *n += sizeof(guint);
  tmp = PDUMEMTYPE_INT;
  memcpy(packet+*n, &tmp, sizeof(guint));
  *n += sizeof(guint);
  memcpy(packet+*n, &data, sizeof(guint));
  *n += sizeof(guint);
}
/**
 * gxsnmp_db_add_gdouble:
 *
 **/
void
gxsnmp_db_add_gdouble (gint *n, gpointer packet, gdouble data)
{
  gint tmp;
  
  tmp = sizeof(guint) * 2 + sizeof(gdouble);
  memcpy(packet+*n, &tmp, sizeof(guint));   
  *n += sizeof(guint);
  tmp = PDUMEMTYPE_DOUBLE;
  memcpy(packet+*n, &tmp, sizeof(guint));   
  *n += sizeof(guint);
  memcpy(packet+*n, &data, sizeof(gdouble));
  *n += sizeof(gdouble);
}

/**
 * gxsnmp_db_add_string:
 *
 **/
void
gxsnmp_db_add_string (gint *n, gpointer packet, gpointer str)
{
  static gchar nullstr[] = "";
  gint         j, tmp;

  j = 0;
  if (!str)
    str = &nullstr;
  else
    j = strlen ((gchar *)str);
  tmp = (sizeof (guint) * 2) + j;
  
  memcpy (packet+*n, &tmp, sizeof (guint));
  *n += sizeof (guint);
  tmp = PDUMEMTYPE_STR;
  memcpy (packet+*n, &tmp, sizeof (guint));
  *n += sizeof (guint);
  memcpy (packet+*n, str, j);
  *n += j;
}
 
/**
 * gxsnmp_db_set_pdu_header:
 *
 **/
gint
gxsnmp_db_set_pdu_header (gpointer packet, gint type, gchar *table_name)
{
  gint table_len;

  table_len = strlen (table_name);
  memcpy (packet, &type, sizeof (gint));
  memcpy (packet + sizeof (gint), &table_len, sizeof (gint));
  memcpy (packet + (sizeof (gint) * 2), table_name, table_len);
  return (sizeof (gint) * 3) + table_len;
}
