/*
 * GXSNMP -- An snmp mangament application
 * Copyright (C) 1998 Gregory McLean & Jochen Friedrich
 * Beholder RMON ethernet network monitor, Copyright (C) 1993 DNPAP group
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */
int debug_level;

/*
 * MODULE INFORMATION
 * ------------------ 
 *     FILE     NAME:       g_asn1.c
 *     SYSTEM   NAME:       ASN1 Basic Encoding
 *     ORIGINAL AUTHOR(S):  Dirk Wisse
 *     VERSION  NUMBER:     1
 *     CREATION DATE:       1990/11/22
 *
 * DESCRIPTION: ASN1 Basic Encoding Rules.
 *              Encoding takes place from end to begin. A normal
 *              procedure for definite encoding is:
 *
 *              g_asn1_open (asn1, buf, sizeof (buf), ASN1_ENC);
 *              g_asn1_eoc_encode (asn1, &end_of_seq);
 *              g_asn1_int_encode (asn1, &end_of_int, 3);
 *              g_asn1_header_encode (asn1, end_of_int, 
 *                                    ASN1_UNI, ASN1_PRI, ASN1_INT);
 *              g_asn1_octets_encode (asn1, &end_of_octs, "String", 6);
 *              g_asn1_header_encode (asn1, end_of_octets, 
 *                                    ASN1_UNI, ASN1_PRI, ASN1_OTS);
 *              g_asn1_header_encode (asn1, end_of_seq, 
 *                                    ASN1_UNI, ASN1_CON, ASN1_SEQ);
 *              g_asn1_close (asn1, &buf_start, &buf_len);
 *
 *              To decode this we must do:
 *
 *              g_asn1_open (asn1, buf_start, buf_len, ASN1_DEC);
 *              g_asn1_header_decode (asn1, &end_of_seq, cls, con, tag);
 *              g_asn1_header_decode (asn1, &end_of_octs, cls, con, tag);
 *              g_asn1_octets_decode (asn1, end_of_octs, str, len);
 *              g_asn1_header_decode (asn1, &end_of_int, cls, con, tag);
 *              g_asn1_int_decode (asn1, end_of_int, &integer);
 *              g_asn1_eoc_decode (asn1, end_of_seq);
 *              g_asn1_close (asn1, &buf_start, &buf_len);
 *              
 *              For indefinite encoding end_of_seq and &end_of_seq in the
 *              example above should be replaced by NULL.
 *              For indefinite decoding nothing has to be changed.
 *              This can be very useful if you want to decode both
 *              definite and indefinite encodings.
 */

#include "g_snmp.h"

/*
 * NAME:        g_asn1_open                                   [API]
 * SYNOPSIS:    void g_asn1_open
 *                  (
 *                      ASN1_SCK *asn1,
 *                      guchar   *buf,
 *                      guint     len,
 *                      guint     mode
 *                  )
 * DESCRIPTION: Opens an ASN1 socket.
 *              Parameters:
 *              asn1: pointer to ASN1 socket.
 *              buf:  Character buffer for encoding.
 *              len:  Length of character buffer.
 *              mode: Encoding, Decoding (ASN1_ENC, ASN1_DEC).
 *              Encoding starts at the end of the buffer, and
 *              proceeds to the beginning.
 * RETURNS:     void
 */

void
g_asn1_open(ASN1_SCK *asn1, guchar *buf, guint len, guint mode)
{
    asn1->begin = buf;
    asn1->end = buf + len;
    asn1->pointer = (mode == ASN1_ENC) ? buf + len : buf;
    asn1->error = ASN1_ERR_NOERROR;
}

/*
 * NAME:        g_asn1_close                                  [API]
 * SYNOPSIS:    void g_asn1_close
 *                  (
 *                      ASN1_SCK   *asn1,
 *                      guchar    **buf,
 *                      guint      *len
 *                  )
 * DESCRIPTION: Closes an ASN1 socket.
 *              Parameters:
 *              asn1: pointer to ASN1 socket.
 *              buf: pointer to beginning of encoding.
 *              len: Length of encoding.
 * RETURNS:     void
 */

void 
g_asn1_close(ASN1_SCK *asn1, guchar **buf, guint *len)
{
    *buf   = asn1->pointer;
    *len   = asn1->end - asn1->pointer;
}



/*
 * NAME:        g_asn1_octet_encode
 * SYNOPSIS:    gboolean g_asn1_octet_encode
 *                  (
 *                      ASN1_SCK     *asn1,
 *                      guchar        ch
 *                  )
 * DESCRIPTION: Encodes an octet.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_octet_encode(ASN1_SCK *asn1, guchar ch)
{
    if (asn1->pointer <= asn1->begin)
	{
		asn1->error = ASN1_ERR_ENC_FULL;
        return FALSE;
	}
    *--(asn1->pointer) = ch;
    return TRUE;
}

/*
 * NAME:        g_asn1_octet_decode
 * SYNOPSIS:    gboolean g_asn1_octet_decode
 *                  (
 *                      ASN1_SCK *asn1,
 *                      guchar   *ch
 *                  )
 * DESCRIPTION: Decodes an octet.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_octet_decode(ASN1_SCK *asn1, guchar *ch)
{
    if (asn1->pointer >= asn1->end)
	{
		asn1->error = ASN1_ERR_DEC_EMPTY;
        return FALSE;
	}
    *ch = *(asn1->pointer)++;
    return TRUE;
}

/*
 * NAME:        g_asn1_tag_encode
 * SYNOPSIS:    gboolean g_asn1_tag_encode
 *                  (
 *                      ASN1_SCK *asn1,
 *                      guint     tag
 *                  )
 * DESCRIPTION: Encodes a tag.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_tag_encode(ASN1_SCK *asn1, guint tag)
{
    guchar ch;

    ch = (guchar) (tag & 0x7F);
    tag >>= 7;
    if (!g_asn1_octet_encode(asn1, ch))
        return FALSE;
    while (tag > 0)
    {
        ch = (guchar) (tag | 0x80);
        tag >>= 7;
        if (!g_asn1_octet_encode(asn1, ch))
            return FALSE;
    }
    return TRUE;
}

/*
 * NAME:        g_asn1_tag_decode
 * SYNOPSIS:    gboolean g_asn1_tag_decode
 *                  (
 *                      ASN1_SCK *asn1,
 *                      guint    *tag
 *                  )
 * DESCRIPTION: Decodes a tag.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_tag_decode(ASN1_SCK *asn1, guint *tag)
{
    guchar ch;

    *tag = 0;
    do
    {
        if (!g_asn1_octet_decode(asn1, &ch))
            return FALSE;
        *tag <<= 7;
        *tag |= ch & 0x7F;
    }
    while ((ch & 0x80) == 0x80);
    return TRUE;
}

/*
 * NAME:        g_asn1_id_encode
 * SYNOPSIS:    gboolean g_asn1_id_encode
 *                  (
 *                      ASN1_SCK *asn1,
 *                      guint     cls,
 *                      guint     con,
 *                      guint     tag
 *                  )
 * DESCRIPTION: Encodes an identifier.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_id_encode(ASN1_SCK *asn1, guint cls, guint con, guint tag)
{
    guint ch;

    if (tag >= 0x1F)
    {
        if (!g_asn1_tag_encode (asn1, tag))
            return FALSE;
        tag = 0x1F;
    }
    ch = (guchar) ((cls << 6) | (con << 5) | (tag));
    if (!g_asn1_octet_encode (asn1, ch))
        return FALSE;
    return TRUE;
}

/*
 * NAME:        g_asn1_id_decode
 * SYNOPSIS:    gboolean g_asn1_id_decode
 *                  (
 *                      ASN1_SCK *asn1,
 *                      guint    *cls,
 *                      guint    *con,
 *                      guint    *tag
 *                  )
 * DESCRIPTION: Decodes an identifier.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_id_decode(ASN1_SCK *asn1, guint *cls, guint *con, guint *tag)
{
    guchar ch;

    if (!g_asn1_octet_decode (asn1, &ch))
        return FALSE;
    *cls = (ch & 0xC0) >> 6;
    *con = (ch & 0x20) >> 5;
    *tag = (ch & 0x1F);
    if (*tag == 0x1F)
    {
        if (!g_asn1_tag_decode (asn1, tag))
            return FALSE;
    }
    return TRUE;
}

/*
 * NAME:        g_asn1_length_encode
 * SYNOPSIS:    gboolean g_asn1_len_encode
 *                  (
 *                      ASN1_SCK *asn1,
 *                      guint     def,
 *                      guint     len
 *                  )
 * DESCRIPTION: Encodes a definite or indefinite length.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_length_encode(ASN1_SCK *asn1, guint def, guint len)
{
    guchar ch, cnt;

    if (!def)
        ch = 0x80;
    else
    {
        if (len < 0x80)
            ch = (guchar) len;
        else
        {
            cnt = 0;
            while (len > 0)
            {
                ch = (guchar) len;
                len >>= 8;
                if (!g_asn1_octet_encode (asn1, ch))
                    return FALSE;
                cnt++;
            }
            ch = (guchar) (cnt | 0x80);
        }
    }
    if (!g_asn1_octet_encode (asn1, ch))
        return FALSE;
    return TRUE;
}

/*
 * NAME:        g_asn1_length_decode
 * SYNOPSIS:    gboolean g_asn1_length_decode
 *                  (
 *                      ASN1_SCK *asn1,
 *                      guint     def,
 *                      guint     len
 *                  )
 * DESCRIPTION: Decodes a definite or indefinite length.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_length_decode(ASN1_SCK *asn1, guint *def, guint *len)
{
    guchar ch, cnt;

    if (!g_asn1_octet_decode (asn1, &ch))
        return FALSE;
    if (ch == 0x80)
        *def = 0;
    else
    {
        *def = 1;
        if (ch < 0x80)
            *len = ch;
        else
        {
            cnt = (guchar) (ch & 0x7F);
            *len = 0;
            while (cnt > 0)
            {
                if (!g_asn1_octet_decode (asn1, &ch))
                    return FALSE;
                *len <<= 8;
                *len |= ch;
                cnt--;
            }
        }
    }
    return TRUE;
}

/*
 * NAME:        g_asn1_header_encode                             [API]
 * SYNOPSIS:    gboolean g_asn1_header_encode
 *                  (
 *                      ASN1_SCK *asn1,
 *                      guchar   *eoc,
 *                      guint     cls,
 *                      guint     con,
 *                      guint     tag
 *                  )
 * DESCRIPTION: Encodes an ASN1 header.
 *              Parameters:
 *              asn1: pointer to ASN1 socket.
 *              eoc: pointer to end of encoding or NULL if
 *                   indefinite.
 *              cls: Class (see asn1.h)
 *              con: Primitive, Constructed (ASN_PRI, ASN_CON)
 *              tag: Tag (see asn1.h)
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_header_encode(ASN1_SCK *asn1, guchar *eoc, guint cls,
		     guint con, guint tag)
{
    guint def, len;

    if (eoc == 0)
    {
        def = 0;
        len = 0;
    }
    else
    {
        def = 1;
        len = eoc - asn1->pointer;
    }
    if (!g_asn1_length_encode (asn1, def, len))
        return FALSE;
    if (!g_asn1_id_encode (asn1, cls, con, tag))
        return FALSE;
    return TRUE;
}

/*
 * NAME:        g_asn1Hdr_decode                                [API]
 * SYNOPSIS:    gboolean g_asn1Hdr_decode
 *                  (
 *                      ASN1_SCK *asn1,
 *                      guchar  **eoc,
 *                      guint    *cls,
 *                      guint    *con,
 *                      guint    *tag
 *                  )
 * DESCRIPTION: Decodes an ASN1 header.
 *              Parameters:
 *              asn1: pointer to ASN1 socket.
 *              eoc: pointer to end of encoding or 0 if
 *                   indefinite.
 *              cls: Class (see asn1.h)
 *              con: Primitive, Constructed (ASN_PRI, ASN_CON)
 *              tag: Tag (see asn1.h)
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_header_decode(ASN1_SCK *asn1, guchar **eoc, guint *cls,
		     guint *con, guint *tag)
{
    guint def, len;

    if (!g_asn1_id_decode (asn1, cls, con, tag))
        return FALSE;
    if (!g_asn1_length_decode (asn1, &def, &len))
        return FALSE;
    if (def)
        *eoc = asn1->pointer + len;
    else
        *eoc = 0;
    return TRUE;
}


/*
 * NAME:        g_asn1_eoc                                   [API]
 * SYNOPSIS:    gboolean g_asn1_eoc
 *                  (
 *                      ASN1_SCK *asn1,
 *                      guchar   *eoc
 *                  )
 * DESCRIPTION: Checks if decoding is at End Of Contents.
 *              Parameters:
 *              asn1: pointer to ASN1 socket.
 *              eoc: pointer to end of encoding or 0 if
 *                   indefinite.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_eoc ( ASN1_SCK *asn1, guchar *eoc)
{
  if (eoc == 0)
    return (asn1->pointer [0] == 0x00 && asn1->pointer [1] == 0x00);
  else
    return (asn1->pointer >= eoc);
}

/*
 * NAME:        g_asn1_eoc_encode                                [API]
 * SYNOPSIS:    gboolean g_asn1_eoc_encode
 *                  (
 *                      ASN1_SCK  *asn1,
 *                      guchar   **eoc
 *                  )
 * DESCRIPTION: Encodes End Of Contents.
 *              Parameters:
 *              asn1: pointer to ASN1 socket.
 *              eoc: pointer to end of encoding or 0 if
 *                   indefinite.
 *              If eoc is 0 it encodes an ASN1 End Of
 *              Contents (0x00 0x00), so it produces an
 *              indefinite length encoding. If eoc points to
 *              a character pointer, eoc is filled with the
 *              pointer to the last encoded octet. This pointer
 *              can be used in the next asn1_header_encode to determine
 *              the length of the encoding. This produces a
 *              definite length encoding.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_eoc_encode ( ASN1_SCK *asn1, guchar **eoc )        
{
  if (eoc == 0)
    {
      if (!g_asn1_octet_encode (asn1, 0x00))
	return FALSE;
      if (!g_asn1_octet_encode (asn1, 0x00))
	return FALSE;
      return TRUE;
    }
  else
    {
      *eoc = asn1->pointer;
      return TRUE;
    }
}

/*
 * NAME:        g_asn1_eoc_decode                                [API]
 * SYNOPSIS:    gboolean g_asn1_eoc_decode
 *                  (
 *                      ASN1_SCK  *asn1,
 *                      guchar    *eoc
 *                  )
 * DESCRIPTION: Decodes End Of Contents.
 *              Parameters:
 *              asn1: pointer to ASN1 socket.
 *              eoc: pointer to end of encoding or 0 if
 *                   indefinite.
 *              If eoc is 0 it decodes an ASN1 End Of
 *              Contents (0x00 0x00), so it has to be an
 *              indefinite length encoding. If eoc is a
 *              character pointer, it probably was filled by
 *              asn1_header_decode, and should point to the octet
 *              after the last of the encoding. It is checked
 *              if this pointer points to the octet to be
 *              decoded. This only takes place in decoding a
 *              definite length encoding.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_eoc_decode (ASN1_SCK *asn1, guchar   *eoc)
{
  guchar ch;
    
  if (eoc == 0)
    {
      if (!g_asn1_octet_decode (asn1, &ch))
	return FALSE;
      if (ch != 0x00)
	{
	  asn1->error = ASN1_ERR_DEC_EOC_MISMATCH;
	  return FALSE;
	}
      if (!g_asn1_octet_decode (asn1, &ch))
	return FALSE;
      if (ch != 0x00)
	{
	  asn1->error = ASN1_ERR_DEC_EOC_MISMATCH;
	  return FALSE;
	}
      return TRUE;
    }
  else
    {
      if (asn1->pointer != eoc)
	{
	  asn1->error = ASN1_ERR_DEC_LENGTH_MISMATCH;
	  return FALSE;
	}
      return TRUE;
    }
}

/*
 * NAME:        g_asn1_null_encode                                [API]
 * SYNOPSIS:    gboolean g_asn1_null_encode
 *                  (
 *                      ASN1_SCK  *asn1,
 *                      guchar   **eoc
 *                  )
 * DESCRIPTION: Encodes Null.
 *              Parameters:
 *              asn1: pointer to ASN1 socket.
 *              eoc: pointer to end of encoding.
 *              Encodes nothing but can be used to fill eoc.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_null_encode ( ASN1_SCK *asn1, guchar **eoc )
{
  *eoc = asn1->pointer;
  return TRUE;
}

/*
 * NAME:        g_asn1_null_decode                                [API]
 * SYNOPSIS:    gboolean g_asn1_null_decode
 *                  (
 *                      ASN1_SCK  *asn1,
 *                      guchar    *eoc
 *                  )
 * DESCRIPTION: Decodes Null.
 *              Parameters:
 *              asn1: pointer to ASN1 socket.
 *              eoc: pointer to end of encoding or 0 if
 *                   indefinite.
 *              Decodes anything up to eoc.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_null_decode ( ASN1_SCK *asn1, guchar *eoc)
{
  asn1->pointer = eoc;
  return TRUE;
}

/*
 * NAME:        g_asn1_bool_encode                                [API]
 * SYNOPSIS:    gboolean g_asn1_bool_encode
 *                  (
 *                      ASN1_SCK   *asn1,
 *                      guchar    **eoc,
 *                      gboolean    bool
 *                  )
 * DESCRIPTION: Encodes Boolean.
 *              Parameters:
 *              asn1: pointer to ASN1 socket.
 *              eoc:  pointer to end of encoding.
 *              bool: FALSE, TRUE (0, !0).
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_bool_encode ( ASN1_SCK *asn1, guchar **eoc, gboolean bool)
{
    guchar ch;

    *eoc = asn1->pointer;
    ch = (guchar) (bool ? 0xFF : 0x00);
    if (!g_asn1_octet_encode (asn1, ch))
        return FALSE;
    return TRUE;
}

/*
 * NAME:        g_asn1_bool_decode                                [API]
 * SYNOPSIS:    gboolean g_asn1_bool_decode
 *                  (
 *                      ASN1_SCK *asn1,
 *                      guchar   *eoc
 *                      gboolean *bool
 *                  )
 * DESCRIPTION: Decodes Boolean.
 *              Parameters:
 *              asn1: pointer to ASN1 socket.
 *              eoc:  pointer to end of encoding or 0 if
 *                    indefinite.
 *              bool: False, True (0, !0).
 * RETURNS:     gboolean success.
 */
gboolean 
g_asn1_bool_decode ( ASN1_SCK *asn1, guchar *eoc, gboolean *bool)
{
    guchar ch;

    if (!g_asn1_octet_decode (asn1, &ch))
        return FALSE;
    *bool = ch ? 1 : 0;
    if (asn1->pointer != eoc)
	{
		asn1->error = ASN1_ERR_DEC_LENGTH_MISMATCH;
        return FALSE;
	}
    return TRUE;
}

/*
 * NAME:        g_asn1_int_encode                                [API]
 * SYNOPSIS:    gboolean g_asn1_int_encode
 *                  (
 *                      ASN1_SCK  *asn1,
 *                      guchar   **eoc,
 *                      gint       integer
 *                  )
 * DESCRIPTION: Encodes Integer.
 *              Parameters:
 *              asn1:    pointer to ASN1 socket.
 *              eoc:     pointer to end of encoding.
 *              integer: Integer.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_int_encode ( ASN1_SCK *asn1, guchar **eoc, int integer)
{
    guchar ch,sign;
    int    lim;

    *eoc = asn1->pointer;
    if (integer < 0)
    {
        lim  = -1;
        sign = 0x80;
    }
    else
    {
        lim  = 0;
        sign = 0x00;
    }
    do
    {
        ch = (guchar) integer;
        integer >>= 8;
        if (!g_asn1_octet_encode (asn1, ch))
            return FALSE;
    }
    while ((integer != lim) || (guchar) (ch & 0x80) != sign);
    return TRUE;
}

/*
 * NAME:        g_asn1_int_decode                                [API]
 * SYNOPSIS:    gboolean g_asn1_int_decode
 *                  (
 *                      ASN1_SCK *asn1,
 *                      guchar   *eoc
 *                      gint     *integer
 *                  )
 * DESCRIPTION: Decodes Integer.
 *              Parameters:
 *              asn1:    pointer to ASN1 socket.
 *              eoc:     pointer to end of encoding or 0 if
 *                       indefinite.
 *              integer: Integer.
 * RETURNS:     gboolean success.
 */

gboolean 
asn1_int_decode ( ASN1_SCK *asn1, guchar *eoc, gint *integer)
{
    guchar ch;
    guint  len;

    if (!g_asn1_octet_decode (asn1, &ch))
        return FALSE;
    *integer = (gint) ch;
    len = 1;
    while (asn1->pointer < eoc)
    {
        if (++len > sizeof (gint))
		{
			asn1->error = ASN1_ERR_DEC_BADVALUE;
            return FALSE;
		}
        if (!g_asn1_octet_decode (asn1, &ch))
            return FALSE;
        *integer <<= 8;
        *integer |= ch;
    }
    return TRUE;
}

/*
 * NAME:        g_asn1_long_encode                             [API]
 * SYNOPSIS:    gboolean g_asn1_long_encode
 *                  (
 *                      ASN1_SCK  *asn1,
 *                      guchar   **eoc,
 *                      glong      integer
 *                  )
 * DESCRIPTION: Encodes Long Integer.
 *              Parameters:
 *              asn1:    pointer to ASN1 socket.
 *              eoc:     pointer to end of encoding.
 *              integer: Integer.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_long_encode ( ASN1_SCK *asn1, guchar **eoc, glong integer)
{
    guchar ch, sign;
    glong  lim;

    *eoc = asn1->pointer;
    if (integer < 0)
    {
        lim  = -1;
        sign = 0x80;
    }
    else
    {
        lim  = 0;
        sign = 0x00;
    }
    do
    {
        ch = (guchar) integer;
        integer >>= 8;
        if (!g_asn1_octet_encode (asn1, ch))
            return FALSE;
    }
    while ((integer != lim) || (guchar) (ch & 0x80) != sign);
    return TRUE;
}

/*
 * NAME:        g_asn1_long_decode                             [API]
 * SYNOPSIS:    gboolean g_asn1_long_decode
 *                  (
 *                      ASN1_SCK  *asn1,
 *                      guchar    *eoc,
 *                      glong     *integer
 *                  )
 * DESCRIPTION: Decodes Long Integer.
 *              Parameters:
 *              asn1:    pointer to ASN1 socket.
 *              eoc:     pointer to end of encoding or 0 if
 *                       indefinite.
 *              integer: Integer.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_long_decode ( ASN1_SCK *asn1, guchar *eoc, glong *integer)
{
    guchar ch;
    guint  len;

    if (!g_asn1_octet_decode (asn1, &ch))
        return FALSE;
    *integer = (signed char) ch;
    len = 1;
    while (asn1->pointer < eoc)
    {
      if (++len > sizeof (glong))
	{
	  asn1->error = ASN1_ERR_DEC_BADVALUE;
	  return FALSE;
	}
      if (!g_asn1_octet_decode (asn1, &ch))
	return FALSE;
      *integer <<= 8;
      *integer |= ch;
    }
    return TRUE;
}

/*
 * NAME:        g_asn1_uint_encode                             [API]
 * SYNOPSIS:    gboolean g_asn1_uint_encode
 *                  (
 *                      ASN1_SCK  *asn1,
 *                      guchar   **eoc,
 *                      guint      integer
 *                  )
 * DESCRIPTION: Encodes Unsigned Integer.
 *              Parameters:
 *              asn1:    pointer to ASN1 socket.
 *              eoc:     pointer to end of encoding.
 *              integer: Integer.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_uint_encode ( ASN1_SCK *asn1, guchar **eoc, guint integer)
{
  guchar ch;
  
  *eoc = asn1->pointer;
  do
    {
      ch = (guchar) integer;
      integer >>= 8;
      if (!g_asn1_octet_encode (asn1, ch))
	return FALSE;
    }
  while ((integer != 0) || (ch & 0x80) != 0x00);
  return TRUE;
}

/*
 * NAME:        g_asn1_uint_decode                             [API]
 * SYNOPSIS:    gboolean g_asn1_uint_decode
 *                  (
 *                      ASN1_SCK  *asn1,
 *                      guchar    *eoc,
 *                      guint     *integer
 *                  )
 * DESCRIPTION: Decodes Unsigned Integer.
 *              Parameters:
 *              asn1:    pointer to ASN1 socket.
 *              eoc:     pointer to end of encoding or 0 if
 *                       indefinite.
 *              integer: Integer.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_uint_decode ( ASN1_SCK *asn1, guchar *eoc, guint *integer)
{
    guchar ch;
    guint  len;

    if (!g_asn1_octet_decode (asn1, &ch))
        return FALSE;
    *integer = ch;
    if (ch == 0) len = 0;
    else len = 1;
    while (asn1->pointer < eoc)
    {
        if (++len > sizeof (guint))
		{
			asn1->error = ASN1_ERR_DEC_BADVALUE;
            return FALSE;
		}
        if (!g_asn1_octet_decode (asn1, &ch))
            return FALSE;
        *integer <<= 8;
        *integer |= ch;
    }
    return TRUE;
}

/*
 * NAME:        asn1_ulong_encode                          [API]
 * SYNOPSIS:    gboolean g_asn1_ulong_encode
 *                  (
 *                      ASN1_SCK  *asn1,
 *                      guchar   **eoc,
 *                      gulong     integer
 *                  )
 * DESCRIPTION: Encodes Long Unsigned Integer.
 *              Parameters:
 *              asn1:    pointer to ASN1 socket.
 *              eoc:     pointer to end of encoding.
 *              integer: Integer.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_ulong_encode ( ASN1_SCK *asn1, guchar **eoc, gulong integer)
{
    guchar ch;

    *eoc = asn1->pointer;
    do
    {
        ch = (guchar) integer;
        integer >>= 8;
        if (!g_asn1_octet_encode (asn1, ch))
            return FALSE;
    }
    while ((integer != 0) || (ch & 0x80) != 0x00);
    return TRUE;
}

/*
 * NAME:        g_asn1_ulong_decode                          [API]
 * SYNOPSIS:    gboolean g_asn1_ulong_decode
 *                  (
 *                      ASN1_SCK  *asn1,
 *                      guchar    *eoc,
 *                      gulong    *integer
 *                  )
 * DESCRIPTION: Decodes Long Unsigned Integer.
 *              Parameters:
 *              asn1:    pointer to ASN1 socket.
 *              eoc:     pointer to end of encoding or 0 if
 *                       indefinite.
 *              integer: Integer.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_ulong_decode ( ASN1_SCK *asn1, guchar *eoc, gulong *integer)
{
    guchar ch;
    guint  len;

    if (!g_asn1_octet_decode (asn1, &ch))
        return FALSE;
    *integer = ch;
    if (ch == 0)
        len = 0;
    else
        len = 1;
    while (asn1->pointer < eoc)
    {
        if (++len > sizeof (gulong))
		{
			asn1->error = ASN1_ERR_DEC_BADVALUE;
            return FALSE;
		}
        if (!g_asn1_octet_decode (asn1, &ch))
            return FALSE;
        *integer <<= 8;
        *integer |= ch;
    }
    return TRUE;
}

/*
 * NAME:        g_asn1_bits_encode                                [API]
 * SYNOPSIS:    gboolean g_asn1_bits_encode
 *                  (
 *                      ASN1_SCK  *asn1,
 *                      guchar   **eoc,
 *                      guchar    *bits,
 *                      guint      len,
 *                      guchar     unused
 *                  )
 * DESCRIPTION: Encodes Bit String.
 *              Parameters:
 *              asn1:   pointer to ASN1 socket.
 *              eoc:    pointer to end of encoding.
 *              bits:   pointer to begin of Bit String.
 *              len:    Length of Bit String in characters.
 *              unused: Number of unused bits in last character.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_bits_encode ( ASN1_SCK *asn1, guchar **eoc, guchar *bits,
		     guint len, guchar unused)
{
    *eoc = asn1->pointer;
    bits += len;
    while (len-- > 0)
    {
        if (!g_asn1_octet_encode (asn1, *--bits))
            return FALSE;
    }
    if (!g_asn1_octet_encode (asn1, unused))
        return FALSE;
    return TRUE;
}

/*
 * NAME:        g_asn1_bits_decode                                [API]
 * SYNOPSIS:    gboolean g_asn1_bits_decode
 *                  (
 *                      ASN1_SCK  *asn1,
 *                      guchar    *eoc,
 *                      guchar    *bits,
 *                      guint      size,
 *                      guint      len,
 *                      guchar     unused
 *                  )
 * DESCRIPTION: Decodes Bit String.
 *              Parameters:
 *              asn1:   pointer to ASN1 socket.
 *              eoc:    pointer to end of encoding or 0 if
 *                      indefinite.
 *              bits:   pointer to begin of Bit String.
 *              size:   Size of Bit String in characters.
 *              len:    Length of Bit String in characters.
 *              unused: Number of unused bits in last character.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_bits_decode ( ASN1_SCK *asn1, guchar *eoc, guchar **bits,
		     guint *len, guchar *unused)

{
    *bits = NULL;
    if (!g_asn1_octet_decode (asn1, unused))
        return FALSE;
    *len = 0;
    *bits = g_malloc(eoc - asn1->pointer);
    while (asn1->pointer < eoc)
    {
        if (!g_asn1_octet_decode (asn1, (guchar *)bits++))
          {
            g_free(*bits);
            *bits = NULL;
	    return FALSE;
          }
    }
    return TRUE;
}

/*
 * NAME:        g_asn1_octets_encode                                [API]
 * SYNOPSIS:    gboolean asn1_octets_encode
 *                  (
 *                      ASN1_SCK  *asn1,
 *                      guchar   **eoc,
 *                      guchar    *octets,
 *                      guint      len
 *                  )
 * DESCRIPTION: Encodes Octet String.
 *              Parameters:
 *              asn1:   pointer to ASN1 socket.
 *              eoc:    pointer to end of encoding.
 *              octets: pointer to begin of Octet GString.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_octets_encode ( ASN1_SCK *asn1, guchar **eoc, guchar *octets, guint len)
{
  guchar *ptr;

  *eoc = asn1->pointer;
  ptr = octets + len;

  while (len-- > 0)
    {
      if (!g_asn1_octet_encode (asn1, *--ptr))
        return FALSE;
    }
  return TRUE;
}

/*
 * NAME:        asn1Ots_decode                                [API]
 * SYNOPSIS:    gboolean asn1Ots_decode
 *                  (
 *                      ASN1_SCK *asn1,
 *                      guchar   *eoc,
 *                      guchar   *octets,
 *                      guint    *len
 *                  )
 * DESCRIPTION: Decodes Octet String.
 *              Parameters:
 *              asn1: pointer to ASN1 socket.
 *              Eoc: pointer to end of encoding or 0 if
 *                   indefinite.
 *              Ots: pointer to Object GString*.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_octets_decode ( ASN1_SCK *asn1, guchar *eoc, guchar **octets, guint *len)
{
  guchar *ptr;

  *octets = NULL;
  *len = 0;
  *octets = g_malloc (eoc - asn1->pointer);
  ptr = *octets;
  while (asn1->pointer < eoc)
    {
      if (!g_asn1_octet_decode (asn1, (guchar *)ptr++))
        {
          g_free(*octets);
          *octets = NULL;
	  return FALSE;
        }
      (*len)++;
    }
  return TRUE;
}

/*
 * NAME:        g_asn1_subid_encode
 * SYNOPSIS:    gboolean g_asn1_subid_encode
 *                  (
 *                      ASN1_SCK *asn1,
 *                      gulong    subid
 *                  )
 * DESCRIPTION: Encodes Sub Identifier.
 *              Parameters:
 *              asn1:  pointer to ASN1 socket.
 *              subid: Sub Identifier.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_subid_encode ( ASN1_SCK *asn1, gulong subid)
{
  guchar ch;
  
  ch = (guchar) (subid & 0x7F);
  subid >>= 7;
  if (!g_asn1_octet_encode (asn1, ch))
    return FALSE;
  while (subid > 0)
    {
      ch = (guchar) (subid | 0x80);
      subid >>= 7;
      if (!g_asn1_octet_encode (asn1, ch))
	return FALSE;
    }
  return TRUE;
}

/*
 * NAME:        g_asn1_subid_decode
 * SYNOPSIS:    gboolean g_asn1_subid_decode
 *                  (
 *                      ASN1_SCK *asn1,
 *                      gulong   *subid
 *                  )
 * DESCRIPTION: Encodes Sub Identifier.
 *              Parameters:
 *              asn1:  pointer to ASN1 socket.
 *              subid: Sub Identifier.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_subid_decode ( ASN1_SCK *asn1, gulong *subid)
{
    guchar ch;

    *subid = 0;
    do
    {
        if (!g_asn1_octet_decode(asn1, &ch))
            return FALSE;
        *subid <<= 7;
        *subid |= ch & 0x7F;
    }
    while ((ch & 0x80) == 0x80);
    return TRUE;
}

/*
 * NAME:        g_asn1_oid_encode                                [API]
 * SYNOPSIS:    gboolean g_asn1_oid_encode
 *                  (
 *                      ASN1_SCK *asn1,
 *                      char    **eoc,
 *                      gulong   *oid,
 *                      guint     len
 *                  )
 * DESCRIPTION: Encodes Obect Identifier.
 *              Parameters:
 *              asn1: pointer to ASN1 socket.
 *              eoc: pointer to end of encoding.
 *              oid: pointer to begin of Object Identifier.
 *              len: Length of Object Identifier in gulongs.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_oid_encode ( ASN1_SCK *asn1, guchar **eoc, gulong *oid,
		    guint len)
{
  gulong subid;
  
  *eoc = asn1->pointer;
  if (len < 2)
    {
      asn1->error = ASN1_ERR_ENC_BADVALUE;
      return FALSE;
    }
  subid = oid [1] + oid [0] * 40;
  oid += len;
  while (len-- > 2)
    {
      if (!g_asn1_subid_encode (asn1, *--oid))
	return FALSE;
    }
  if (!g_asn1_subid_encode (asn1, subid))
    return FALSE;
  return TRUE;
}

/*
 * NAME:        g_asn1_oid_decode                                [API]
 * SYNOPSIS:    gboolean g_asn1_oid_decode
 *                  (
 *                      ASN1_SCK *asn1,
 *                      char     *eoc,
 *                      gulong   *oid,
 *                      guint    *len
 *                  )
 * DESCRIPTION: Decodes Obect Identifier.
 *              Parameters:
 *              asn1: pointer to ASN1 socket.
 *              eoc:  pointer to end of encoding or 0 if
 *                    indefinite.
 *              oid:  pointer to begin of Object Identifier.
 *              size: Size of Obect Identifier in gulongs
 *              len:  Length of Object Identifier in gulongs.
 * RETURNS:     gboolean success.
 */

gboolean 
g_asn1_oid_decode ( ASN1_SCK *asn1, guchar *eoc, gulong **oid, 
		    guint *len)
{
  gulong subid;
  guint  size;
  gulong *optr;

  size = eoc - asn1->pointer + 1;
  *oid = g_malloc(size * sizeof(gulong));
  optr = *oid;
 
  if (!g_asn1_subid_decode (asn1, &subid))
    {
      g_free(*oid);
      *oid = NULL;
      return FALSE;
    }
  if (subid < 40)
    {
      optr [0] = 0;
      optr [1] = subid;
    }
  else if (subid < 80)
    {
      optr [0] = 1;
      optr [1] = subid - 40;
    }
  else
    {
      optr [0] = 2;
      optr [1] = subid - 80;
    }
  *len = 2;
  optr += 2;
  while (asn1->pointer < eoc)
    {
      if (++(*len) > size)
	{
	  asn1->error = ASN1_ERR_DEC_BADVALUE;
          g_free(*oid);
          *oid = NULL;
	  return FALSE;
	}
      if (!g_asn1_subid_decode (asn1, optr++))
        {
          g_free(*oid);
          *oid = NULL;
	  return FALSE;
        }
    }
  return TRUE;
}

/* EOF */
