/* packet-pres.c
*
* Routine to dissect ISO 8823 OSI Presentation Protocol packets
*
* $Id: packet-pres.c 14307 2005-05-04 19:55:17Z gerald $
*
* Yuriy Sidelnikov <YSidelnikov@hotmail.com>
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
* Copyright 1998 Gerald Combs
*
* 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, Boston, MA  02111-1307, USA.
*/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <glib.h>
#include <epan/packet.h>

#include <stdio.h>
#include <string.h>

#include "packet-pres.h"
#include "packet-frame.h"
#include <epan/prefs.h>

#include <epan/strutil.h>

#include <epan/asn1.h>
#include "format-oid.h"

#include "packet-ses.h"
extern const value_string ses_vals[];

/* pres header fields             */
static int proto_pres          = -1;
/*   type of session envelop */
static struct SESSION_DATA_STRUCTURE* session = NULL;
static int hf_pres_rc_type			= -1;
static int hf_pres_ms_type			= -1;
static int hf_pres_seq_type			= -1;
static int hf_pres_protocol_version =-1;
/* pres fields defining a sub tree */
static gint ett_pres           = -1;
static gint ett_pres_param     = -1;
static gint ett_pres_rc           = -1;
static gint ett_pres_ms           = -1;
static gint ett_pres_itm           = -1;

/*
----------------------------------------------------------------------------------------------------------*/
static dissector_handle_t acse_handle = NULL;

static int hf_pres_type        = -1;
static int hf_pres_length      = -1;
static int hf_value			   = -1;


static int hf_cp_type_message_length = -1;

static int hf_protocol_version       = -1;
static int hf_context_management       = -1;
static int hf_restoration       = -1;


static const value_string pres_vals[] =
{
  {PRES_CONNECTION_REQUEST_CONFIRM,  "Connection request/confirm PDU" },
  {PRES_CONNECTION_REFUSE,    "Connection refuse PDU"   },
  {0,             NULL           }
};

static const value_string cr_vals[] =
{
  {MODE_SELECTOR, "Mode Selector"},
  {SEQUENCE_TOP, "Sequence"},
  {SET_TOP, "Set"},
  {0, NULL}
};

static const value_string sequence_top_vals[] =
{
  {CALLED_PRESENTATION_SELECTOR, "Called presentation selector"},
  {CALLING_PRESENTATION_SELECTOR, "Calling presentation selector"},
  {PRESENTATION_CONTEXT_DEFINITION_LIST, "Presentation context definition list"},
  {RESPONDING_PRESENTATION_SELECTOR, "Responding presentation selector"},
  {PROTOCOL_VERSION, "Protocol version"},
  {PRESENTATION_CONTEXT_DEFINITION_RESULT_LIST, "Presentation context definition result list"},
  {PRESENTATION_REQUIREMENTS,"Presentation requirements"},
  {DEFAULT_CONTEXT_NAME,"Default context name"},
  {USER_SESSION_REQUIREMENTS,"User session requirements"},
  {DEFAULT_CONTEXT_RESULT,"Default context result"},
  {PROVIDER_REASON,"Provider reason"},
  {0, NULL}
};
static const value_string sequence_list_vals[] =
{
  {PRESENTATION_CONTEXT_IDENTIFIER,"Presentation context identifier"},
  {ABSTRACT_SYNTAX_NAME,"Abstract syntax name"},
  {TRANSFER_SYNTAX_NAMES,"Transfer syntax names"},
  {0, NULL}
};
static const value_string sequence_list_result_vals[] =
{
  {PRESENTATION_RESULT,"Result"},
  {PRESENTATION_RESULT_INTEGER,"Integer"},
  {PRESENTATION_RESULT_TRANSFER_SYNTAX_NAME,"Transfer syntax name"},
  {0, NULL}
};
static const value_string presentation_context_definition_vals[] =
{
  {SEQUENCE, "Sequence"},
  {0, NULL}
};
static const value_string sequence_list_result_values_vals[] =
{
  {PRESENTATION_RESULT_ACCEPTANCE,"Acceptance"},
  {PRESENTATION_RESULT_USER_REJECTION,"User rejection"},
  {PRESENTATION_RESULT_PROVIDER_REJECTION,"Provider rejection"},
  {0, NULL}
};
static const value_string provider_reason_values_vals[] =
{
  {REASON_NOT_SPECIFIED,"Reason not specified"},
  {TEMPORARY_CONGESTION,"Temporary congestion"},
  {LOCAL_LIMIT_EXCEEDED,"Local limit exceeded"},
  {CALLED_PRESENTATION_ADDRESS_UNKNOWN,"Called presentation address unknown"},
  {PROTOCOL_VERSION_NOT_SUPPORTED,"Protocol version not supported"},
  {DEFAULT_CONTEXT_NOT_SUPPORTED,"Default context not supported"},
  {USER_DATA_NOT_READABLE,"User data not readable"},
  {NO_PSAP_AVAILABLE,"No PSAP available"},
  {0, NULL}
};

static const value_string user_data_values_vals[] =
{
  {SIMPLY_ENCODED_DATA,"Simply encoded data"},
  {FULLY_ENCODED_DATA,"Fully encoded data "},
  {0, NULL}
};
static const value_string presentation_data_values[] =
{
  {PRESENTATION_CONTEXT_IDENTIFIER,"Presentation context identifier"},
  {SINGLE_ASN1_TYPE,"Single ASN.1 type"},
  {OCTET_ALIGNED,"Octet aligned"},
  {ARBITRARY,"Arbitrary"},
  {DATA_BLOCK,"Data block"},
  {0, NULL}
};
static const value_string provider_abort_values_vals[] =
{
  {PR_REASON_NOT_SPECIFIED,"Reason not specified"},
  {UNRECOGNIZED_PDU,"Unrecognized ppdu"},
  {UNEXPECTED_PDU,"Unexpected ppdu"},
  {UNEXPECTED_SESSION_SERVICE_PRIMITIVE,"Unexpected session service primitive"},
  {UNRECOGNIZED_PPDU_PARAMETER,"Unrecognized ppdu parameter"},
  {UNEXPECTED_PPDU_PARAMETER,"Unexpected ppdu parameter"},
  {INVALID_PPDU_PARAMETER_VALUE,"Invalid ppdu parameter value"},
  {0, NULL}
};
static const value_string event_identifier_values_vals[] =
{
  {REASON_CP_PPDU,"cp PPDU"},
  {REASON_CPA_PPDU,"cpa PPDU"},
  {REASON_CPR_PPDU,"cpr PPDU"},
  {REASON_ARU_PPDU,"aru PPDU"},
  {REASON_ARP_PPDU,"arp PPDU"},
  {REASON_AC_PPDU,"ac PPDU"},
  {REASON_ACA_PPDU,"aca PPDU"},
  {REASON_TD_PPDU,"td PPDU"},
  {REASON_TTD_PPDU,"td PPDU"},
  {REASON_TE_PPDU,"te PPDU"},
  {REASON_TC_PPDU,"tc PPDU"},
  {REASON_TCC_PPDU,"tcc PPDU"},
  {REASON_RS_PPDU,"rs PPDU"},
  {REASON_RSA_PPDU,"rsa PPDU"},
  {S_RELEASE_INDICATION,"s release indication"},
  {S_RELEASE_CONFIRM,"s release confirm"},
  {S_TOKEN_GIVE_INDICATION,"s token give indication"},
  {S_TOKEN_PLEASE_INDICATION,"s token please indication"},
  {S_CONTROL_GIVE_INDICATION,"s control give indication"},
  {S_SYNC_MINOR_INDICATION,"s sync minor indication"},
  {S_SYNC_MINOR_CONFIRM,"s sync minor confirm"},
  {S_SYNC_MAJOR_INDICATION,"s sync major indication"},
  {S_SYNC_MAJOR_CONFIRM,"s sync major confirm"},
  {S_P_EXCEPTION_REPORT_INDICATION,"s p exception report indication"},
  {S_U_EXCEPTION_REPORT_INDICATION,"s u exception report indication"},
  {S_ACTIVITY_START_INDICATION,"s activity start indication"},
  {S_ACTIVITY_RESUME_INDICATION,"s activity resume indication"},
  {S_ACTIVITY_INTERRUPT_INDICATION,"s activity interrupt indication"},
  {S_ACTIVITY_INTERRUPT_CONFIRM,"s activity interrupt confirm"},
  {S_ACTIVITY_DISCARD_INDICATION,"s activity discard indication"},
  {S_ACTIVITY_DISCARD_CONFIRM,"s activity discard confirm"},
  {S_ACTIVITY_END_INDICATION,"s activity end indication"},
  {S_ACTIVITY_END_CONFIRM,"s activity end confirm"},
  {0, NULL}
};
/*      pointers for acse dissector  */
proto_tree *global_tree  = NULL;
packet_info *global_pinfo = NULL;
/* dissector for data */
static dissector_handle_t data_handle;

static void
call_acse_dissector(tvbuff_t *tvb, gint offset, gint param_len,
    packet_info *pinfo, proto_tree *tree, proto_tree *param_tree)
{
	/* do we have OSI acse/rose packet dissector ? */
	if(!acse_handle)
	{
		/* No - display as data */
		if (tree)
		{
			proto_tree_add_text(param_tree, tvb, offset, param_len,
			    "No ACSE dissector available");
		}
	}
	else
	{
		/* Yes - call app dissector */
		tvbuff_t *next_tvb;

		next_tvb = tvb_new_subset(tvb, offset, param_len, param_len);
		TRY
		{
			call_dissector(acse_handle, next_tvb, pinfo, tree);
		}
		CATCH_ALL
		{
			show_exception(tvb, pinfo, tree, EXCEPT_CODE, GET_MESSAGE);
		}
		ENDTRY;
	}
}

static int read_length(ASN1_SCK *a, proto_tree *tree, int hf_id, guint *len)
{
  guint length = 0;
  gboolean def = FALSE;
  int start = a->offset;
  int ret;

  ret = asn1_length_decode(a, &def, &length);
  if (ret != ASN1_ERR_NOERROR)
  {
    if (tree)
	{
      proto_tree_add_text(tree, a->tvb, start, 0,
        "%s: ERROR: Couldn't parse length: %s",
        proto_registrar_get_name(hf_id), asn1_err_to_str(ret));
    }
    return ret;
  }

  if (len)
    *len = length;

  if (hf_id)
    proto_tree_add_uint(tree, hf_id, a->tvb, start, a->offset-start, 
length);

  return ASN1_ERR_NOERROR;
}
static int read_integer_value(ASN1_SCK *a, proto_tree *tree, int hf_id,
	proto_item **new_item, guint *i, int start, guint length)
{
  guint integer = 0;
  proto_item *temp_item = NULL;
  int ret;

  ret = asn1_uint32_value_decode(a, length, &integer);
  if (ret != ASN1_ERR_NOERROR)
  {
    if (tree)
	{
      proto_tree_add_text(tree, a->tvb, start, 0,
       "%s: ERROR: Couldn't parse value: %s",
        proto_registrar_get_name(hf_id), asn1_err_to_str(ret));
    }
    return ret;
  }

  if (i)
    *i = integer;

  if (hf_id)
    temp_item = proto_tree_add_uint(tree, hf_id, a->tvb, start, 
a->offset-start, integer);

  if (new_item)
    *new_item = temp_item;

  return ASN1_ERR_NOERROR;
}

static int read_integer(ASN1_SCK *a, proto_tree *tree, int hf_id,
	proto_item **new_item, guint *i)
{
  guint cls, con, tag;
  gboolean def;
  guint length;
  int start = a->offset;
  int ret;

  ret = asn1_header_decode(a, &cls, &con, &tag, &def, &length);
  if (ret != ASN1_ERR_NOERROR)
  {
    if (tree)
	{
      proto_tree_add_text(tree, a->tvb, start, 0,
        "%s: ERROR: Couldn't parse header: %s",
        (hf_id != -1) ? proto_registrar_get_name(hf_id) : "LDAP message",
        asn1_err_to_str(ret));
    }
    return ret;
  }

  return read_integer_value(a, tree, hf_id, new_item, i, start, length);
}
/*   display asn.1 Integer type */
static void
show_integer(ASN1_SCK *asn,proto_tree *pres_tree,tvbuff_t *tvb,int 
*offset,int item_len)
{
	  proto_tree *pres_tree_itm = NULL;
	  proto_item *itm;
	  int       ret;
	  int		save_len = item_len;
	  int		off      = *offset;
	  itm = proto_tree_add_text(pres_tree, tvb, *offset, item_len,
										"Integer");
	  pres_tree_itm = proto_item_add_subtree(itm, ett_pres_itm);
	  ret = read_integer(asn,pres_tree_itm,0,NULL,&item_len);
	  if (ret == ASN1_ERR_NOERROR )
			{
				*offset = asn->offset;
				itm = proto_tree_add_text(pres_tree_itm, tvb, (*offset)-item_len, 
item_len,
											"Integer value: %u",item_len);
			}
	  else
			{
		  /* can't dissect item. Skip it. */
		  *offset = off+ save_len;
			}

}
static void
show_presentation_requirements(ASN1_SCK *asn,proto_tree *pres_tree,tvbuff_t 
*tvb,int *offset,int item_len,int tag)
{
	  proto_tree *pres_tree_itm = NULL;
	  proto_item *itm;
	  guint16       flags;
	  gint length;
/* do we have enough bytes to dissect this item ? */
			if( ( length = tvb_reported_length_remaining(tvb, *offset))  < 
(asn->offset -*offset)+ item_len )
			{
					proto_tree_add_text(pres_tree, tvb, *offset, item_len,
							"Wrong Item.Need %u bytes but have %u", item_len,length);
					return;
			}

	  itm = proto_tree_add_text(pres_tree, tvb, *offset,(asn->offset -*offset)+ 
item_len,
											val_to_str(tag, sequence_top_vals,"Unknown item (0x%02x)"));
	  pres_tree_itm = proto_item_add_subtree(itm, ett_pres_itm);
	  *offset = asn->offset;
	  flags = tvb_get_ntohs(tvb, *offset);
	  proto_tree_add_boolean(pres_tree_itm,hf_context_management, tvb, *offset, 
2, flags);
	  proto_tree_add_boolean(pres_tree_itm,hf_restoration, tvb, *offset, 2, 
flags);
	  *offset = *offset + item_len;
	  asn->offset = *offset;
}

static void
show_protocol_version(ASN1_SCK *asn,proto_tree *pres_tree,tvbuff_t *tvb,int 
*offset,int item_len,int tag)
{
	  proto_tree *pres_tree_itm = NULL;
	  proto_item *itm;
	  guint16       flags;
	  gint length;
/* do we have enough bytes to dissect this item ? */
			if( ( length = tvb_reported_length_remaining(tvb, *offset))  < 
(asn->offset -*offset)+ item_len )
			{
					proto_tree_add_text(pres_tree, tvb, *offset, item_len,
							"Wrong Item.Need %u bytes but have %u", item_len,length);
					return;
			}

	  itm = proto_tree_add_text(pres_tree, tvb, *offset,(asn->offset -*offset)+ 
item_len,
											val_to_str(tag, sequence_top_vals,"Unknown item (0x%02x)"));
	  pres_tree_itm = proto_item_add_subtree(itm, ett_pres_itm);
	  *offset = asn->offset;
	  flags = tvb_get_ntohs(tvb, *offset);
	  proto_tree_add_boolean(pres_tree_itm,hf_protocol_version, tvb, *offset, 
2, flags);
	  *offset = *offset + item_len;
	  asn->offset = *offset;
}
static void
print_oid_value(ASN1_SCK *asn,proto_tree *pres_tree,tvbuff_t *tvb,int 
*offset,int item_len)
{
	guint		ret;
	subid_t		*oid;
	guint		len;
	gchar		*display_string;
	guint		length;
	guint		start=*offset;

		ret = asn1_oid_value_decode (asn, item_len, &oid, &len);
		if (ret != ASN1_ERR_NOERROR)
		{
			return ;
		}
		length = asn->offset - start;
		display_string = format_oid(oid, len);
		proto_tree_add_text(pres_tree, tvb, *offset,length,"Value:%s", 
display_string);
		g_free(display_string);
		(*offset)=start+item_len;
		asn->offset = (*offset);
}
static void
print_oid(ASN1_SCK *asn,proto_tree *pres_tree,tvbuff_t *tvb,int *offset,int 
item_len)
{
	guint		ret;
	subid_t		*oid;
	guint		len;
	guint		nbytes;
	gchar		*display_string;
	guint		length;
	guint		start=*offset;

		ret = asn1_oid_decode ( asn, &oid, &len, &nbytes);

		if (ret != ASN1_ERR_NOERROR)
		{
			return ;
		}
		length = asn->offset - start;
		display_string = format_oid(oid, len);
		proto_tree_add_text(pres_tree, tvb, *offset,length,"Value:%s", 
display_string);
		g_free(display_string);
		(*offset)=start+item_len;
		asn->offset = (*offset);
}

static void
print_value(ASN1_SCK *asn,proto_tree *pres_tree, tvbuff_t *tvb, int *offset, int item_len)
{
    gint    start = *offset;
    gchar  *tmp;

    *offset = asn->offset;  /* align to data*/
    tmp = tvb_bytes_to_str(tvb, *offset, item_len);
    proto_tree_add_text(pres_tree, tvb, *offset, item_len, tmp);
    (*offset) = start+item_len;
    asn->offset = (*offset);
}

static int
get_integer_value(ASN1_SCK *asn,int length,int *offset)
{
	int off = *offset;
	int asn_off = asn->offset;
	int item_len = -1;
	int ret;
	/* align   pointers */
	*offset=asn->offset;
	ret = asn1_uint32_value_decode(asn, length, &item_len);
	/* return to present position */
	*offset = off;
	asn->offset = asn_off;

    if (ret != ASN1_ERR_NOERROR )
	{
		return -1;
	}
	else
	{
		return item_len;
	}

}
static void
show_presentation_context_definition_result_seq(ASN1_SCK *asn,proto_tree 
*pres_tree,tvbuff_t *tvb,int *offset,int item_len)
{
	  proto_tree *pres_tree_ms = NULL;
	  guint length;
	  guint   type;
	  proto_item *ms;
	  guint   new_item_len;
	  guint   start = *offset;
	  guint   header_len;
	  int		old_offset;

			/*  print seq */
			while ( item_len > 0 && tvb_reported_length_remaining(tvb, *offset) > 0 )
			{
				old_offset = *offset ;
			/*  get item type  */
			type = tvb_get_guint8(tvb, *offset);
			/* skip type */
			(*offset)++;
			asn->offset = *offset;
			/* get length  */
				if (read_length(asn, pres_tree, 0, &new_item_len) != ASN1_ERR_NOERROR)
				{
					(*offset)=start+item_len;
					asn->offset = (*offset);
					return  ;
				}

			/* do we have enough bytes to dissect ? */
			if( ( length =tvb_reported_length_remaining(tvb, *offset))  < 
new_item_len )
			{
					proto_tree_add_text(pres_tree, tvb, *offset, new_item_len,
							"Wrong item.Need %u bytes but have %u", new_item_len,length);
					(*offset)=start+item_len;
					asn->offset = (*offset);
				    return ;
			}
				header_len = asn->offset - (*offset) +1;
				ms = proto_tree_add_text(pres_tree, tvb, *offset-1, 
new_item_len+(asn->offset-*offset)+1,
										val_to_str(type, sequence_list_result_vals,
										"Unknown item (0x%02x)"));
				pres_tree_ms = proto_item_add_subtree(ms, ett_pres_ms);
				*offset = asn->offset;


			switch(type)
				{
			case PRESENTATION_RESULT:
				{
					proto_tree *pres_tree_pr = NULL;
					proto_item *pr;
					int        value	=	get_integer_value(asn,new_item_len,offset);
				pr = proto_tree_add_text(pres_tree_ms, tvb, *offset, 
new_item_len+(asn->offset-*offset),
								val_to_str(value ,sequence_list_result_values_vals,
								"Unknown item (0x%02x)"));
				pres_tree_pr = proto_item_add_subtree(pr, ett_pres_ms);

				print_value(asn,pres_tree_pr,tvb,offset,new_item_len);
				}
					break;
			case PRESENTATION_RESULT_INTEGER:
				print_value(asn,pres_tree_ms,tvb,offset,new_item_len);
					break;
			case PRESENTATION_RESULT_TRANSFER_SYNTAX_NAME:
				print_oid_value(asn,pres_tree_ms,tvb,offset,new_item_len);
					break;

			default:
					proto_tree_add_text(pres_tree, tvb, *offset, 
new_item_len+(asn->offset-*offset),
					"Unknown asn.1 parameter: (0x%02x)", type);
				}
				*offset = old_offset+new_item_len+header_len;
				item_len-=new_item_len+header_len;
			}
			/*   align the pointer */
			(*offset)=start+item_len;
			asn->offset = (*offset);
}

static void
show_presentation_context_definition_seq(ASN1_SCK *asn,proto_tree 
*pres_tree,tvbuff_t *tvb,int *offset,int item_len)
{
	  proto_tree *pres_tree_ms = NULL;
	  guint length;
	  guint   type;
	  proto_item *ms;
	  guint   new_item_len;
	  guint   start = *offset;
	  guint   header_len;
	  int		old_offset;

			/*  print seq */
			while ( item_len > 0 && tvb_reported_length_remaining(tvb, *offset) > 0 )
			{
				old_offset = *offset ;
			/*  get item type  */
			type = tvb_get_guint8(tvb, *offset);
			/* skip type */
			(*offset)++;
			asn->offset = *offset;
			/* get length  */
				if (read_length(asn, pres_tree, 0, &new_item_len) != ASN1_ERR_NOERROR)
				{
					(*offset)=start+item_len;
					asn->offset = (*offset);
					return  ;
				}

			/* do we have enough bytes to dissect ? */
			if( ( length =tvb_reported_length_remaining(tvb, *offset))  < 
new_item_len )
			{
					proto_tree_add_text(pres_tree, tvb, *offset, new_item_len,
							"Wrong item.Need %u bytes but have %u", new_item_len,length);
					(*offset)=start+item_len;
					asn->offset = (*offset);
				    return ;
			}
				header_len = asn->offset - (*offset) +1;
				ms = proto_tree_add_text(pres_tree, tvb, *offset-1, 
new_item_len+(asn->offset-*offset)+1,
										val_to_str(type, sequence_list_vals,
										"Unknown item (0x%02x)"));
				pres_tree_ms = proto_item_add_subtree(ms, ett_pres_ms);
				*offset = asn->offset;


			switch(type)
				{
			case PRESENTATION_CONTEXT_IDENTIFIER:
				print_value(asn,pres_tree_ms,tvb,offset,new_item_len);
					break;
			case ABSTRACT_SYNTAX_NAME:
				print_oid_value(asn,pres_tree_ms,tvb,offset,new_item_len);
					break;
			case TRANSFER_SYNTAX_NAMES:
				print_oid(asn,pres_tree_ms,tvb,offset,new_item_len);
					break;
			default:
					proto_tree_add_text(pres_tree, tvb, *offset, 
new_item_len+(asn->offset-*offset),
					"Unknown asn.1 parameter: (0x%02x)", type);
				}
				*offset = old_offset+new_item_len+header_len;
				item_len-=new_item_len+header_len;
			}
			/*   align the pointer */
			(*offset)=start+item_len;
			asn->offset = (*offset);
}
static void
show_fully_encoded_seq(ASN1_SCK *asn,proto_tree *pres_tree,tvbuff_t *tvb,int 
*offset,int item_len)
{
	  proto_tree *pres_tree_ms = NULL;
	  guint length;
	  guint   type;
	  proto_item *ms;
	  guint   new_item_len;
	  guint   start = *offset;
	  guint   header_len;
	  guint   acse = 0;      /*   no acse   id   */
	  int		old_offset;
			/*  print seq */
			while ( item_len > 0 && tvb_reported_length_remaining(tvb, *offset) > 0 )
			{
				old_offset = *offset ;
			/*  get item type  */
			type = tvb_get_guint8(tvb, *offset);
			/* skip type */
			(*offset)++;
			asn->offset = *offset;
			/* get length  */
				if (read_length(asn, pres_tree, 0, &new_item_len) != ASN1_ERR_NOERROR)
				{
					(*offset)=start+item_len;
					asn->offset = (*offset);
					return  ;
				}
			/* do we have enough bytes to dissect ? */
			if( ( length =tvb_reported_length_remaining(tvb, *offset))  < 
new_item_len )
			{
					proto_tree_add_text(pres_tree, tvb, *offset, new_item_len,
							"Wrong item.Need %u bytes but have %u", new_item_len,length);
					(*offset)=start+item_len;
					asn->offset = (*offset);
				    return ;
			}
			if(!new_item_len && length>2)
			{
					new_item_len = length-1;  /* can't get length from asn1 tag. Use rest of the pdu len. */
			}
				header_len = asn->offset - (*offset) +1;
				ms = proto_tree_add_text(pres_tree, tvb, *offset-1, 
new_item_len+(asn->offset-*offset)+1,
										val_to_str(type, presentation_data_values,
										"Unknown item (0x%02x)"));
				pres_tree_ms = proto_item_add_subtree(ms, ett_pres_ms);
				*offset = asn->offset;


			switch(type)
				{
			case PRESENTATION_CONTEXT_IDENTIFIER:
				{
				acse	=	get_integer_value(asn,new_item_len,offset);
				print_value(asn,pres_tree_ms,tvb,offset,new_item_len);
				}
					break;
			case OCTET_ALIGNED:
			case DATA_BLOCK:
				{
						proto_item *acse_ms;
					/*  yes, we have to call ACSE dissector    */
				acse_ms = proto_tree_add_text(pres_tree_ms, tvb, *offset,new_item_len+(asn->offset-*offset),
										"User data");

				session->abort_type = DATA_BLOCK;
					/*  call acse dissector  */
				call_acse_dissector(tvb,*offset,new_item_len,global_pinfo,global_tree,pres_tree_ms);
				}
					break;
			case SINGLE_ASN1_TYPE:

				{
						proto_item *acse_ms;
					/*  yes, we have to call ACSE dissector    */
				acse_ms = proto_tree_add_text(pres_tree_ms, tvb, *offset, 
new_item_len+(asn->offset-*offset),
										"User data");
					/*  call acse dissector  */
				call_acse_dissector(tvb,*offset,new_item_len,global_pinfo,global_tree,pres_tree_ms);
				acse = 0;
				}
					break;
			case ARBITRARY:
				print_value(asn,pres_tree_ms,tvb,offset,new_item_len);
					break;
			default:
					proto_tree_add_text(pres_tree, tvb, *offset, 
new_item_len+(asn->offset-*offset),
					"Unknown asn.1 parameter: (0x%02x)", type);
				}
				*offset = old_offset+new_item_len+header_len;
				item_len-=new_item_len+header_len;
			}
			/*   align the pointer */
			(*offset)=start+item_len;
			asn->offset = (*offset);

}
static void
show_fully_encoded_data(ASN1_SCK *asn,proto_tree *pres_tree,tvbuff_t 
*tvb,int *offset,int item_len)
{
	  proto_tree *pres_tree_ms = NULL;
	  proto_tree *pres_tree_pc = NULL;
	  gint    length;
	  guint   type;
	  guint   header_len;
	  proto_item *ms;
	  gint   new_item_len;
	  guint   start = asn->offset;
	  guint	  item_length = item_len;
	  pres_tree_pc = pres_tree;

/* do we have enough bytes to dissect ? */
			if( ( length =tvb_reported_length_remaining(tvb, *offset))  < item_len )
			{
					proto_tree_add_text(pres_tree_pc, tvb, *offset, item_len,
							"Wrong item.Need %u bytes but have %u", item_len,length);
					/*   align the pointer */
					(*offset)=start+item_length;
					asn->offset = (*offset);
					return ;
			}
			*offset =asn->offset;
			start = *offset;
			/*  read the rest */
			while ( item_len > 0 && tvb_reported_length_remaining(tvb, *offset) > 0 )
			{
				int old_offset = *offset;
			/*  get item type  */
			type = tvb_get_guint8(tvb, *offset);
			/* skip type */
			(*offset)++;
			asn->offset = *offset;
			/* get length  */
				if (read_length(asn, pres_tree_pc, 0, &new_item_len) != 
ASN1_ERR_NOERROR)
				{
					/*   align the pointer */
					(*offset)=start+item_length;
					asn->offset = (*offset);
					return  ;
				}
				header_len = asn->offset - (*offset) +1;
			/* do we have enough bytes to dissect ? */
			if( ( length =tvb_reported_length_remaining(tvb, *offset))  < 
new_item_len )
			{
					proto_tree_add_text(pres_tree_pc, tvb, *offset, new_item_len,
							"Wrong item.Need %u bytes but have %u", new_item_len,length);
					/*   align the pointer */
					(*offset)=start+item_length;
					asn->offset = (*offset);
					return ;
			}
			if(!new_item_len && length>2)
			{
				/* check if really do have what to dissect */
				new_item_len = length-1;
			}
				ms = proto_tree_add_text(pres_tree_pc, tvb, *offset-1, 
new_item_len+(asn->offset-*offset)+1,
										val_to_str(type, presentation_context_definition_vals,
										"Unknown item (0x%02x)"));
				pres_tree_ms = proto_item_add_subtree(ms, ett_pres_ms);
				*offset = asn->offset;


			switch(type)
				{

			case SEQUENCE:
			show_fully_encoded_seq(asn,pres_tree_ms,tvb,offset,new_item_len);
			*offset = old_offset+(new_item_len+header_len);
					break;

			default:
					proto_tree_add_text(pres_tree_ms, tvb, *offset, 
new_item_len+(asn->offset-*offset),
					"Unknown asn.1 parameter: (0x%02x)", type);
					*offset = old_offset+(new_item_len+header_len);
				}
				item_len = item_len -  (new_item_len+header_len);

			}


					/*   align the pointer */
					(*offset)=start+item_length;
					asn->offset = (*offset);
}
static void
show_session_provider_abort(ASN1_SCK *asn,proto_tree *pres_tree,tvbuff_t 
*tvb,int *offset,int item_len)
{
	gint length;
	gint		value;
    proto_tree *pres_tree_pc = NULL;
    proto_tree *pres_tree_pp = NULL;
	proto_item *itu;
	gint   new_item_len;


/* do we have enough bytes to dissect this item ? */
			if( ( length =tvb_reported_length_remaining(tvb, *offset))  < item_len )
			{
					proto_tree_add_text(pres_tree, tvb, *offset, item_len,
							"Wrong Item.Need %u bytes but have %u", item_len,length);
							*offset = asn->offset;
							return;
			}
			itu = proto_tree_add_text(pres_tree, tvb, *offset,item_len,
					"Provider abort");
			pres_tree_pp = proto_item_add_subtree(itu, ett_pres_ms);

			if(item_len <= 0)
			{
					proto_tree_add_text(pres_tree_pc, tvb, *offset, item_len,
							"Provider reason not specified");
							*offset = asn->offset;
							return;
			}
			itu = proto_tree_add_text(pres_tree_pp, tvb, *offset,ABORT_REASON_LEN,
					"Abort reason");
			pres_tree_pc = proto_item_add_subtree(itu, ett_pres_ms);
			(*offset)++; /* skip type  */
			asn->offset =  *offset;
			item_len--;
			/* get length  */
				if (read_length(asn, pres_tree_pc, 0, &new_item_len) != 
ASN1_ERR_NOERROR)
				{
							*offset = asn->offset;
							return;
				}
			/*  try to get Abort reason  */
			value	=	get_integer_value(asn,new_item_len,offset);


			proto_tree_add_text(pres_tree_pc, tvb, *offset+1,new_item_len,
					val_to_str(value, provider_abort_values_vals,"Unknown item (0x%02x)"));
			item_len-=(asn->offset-*offset)+new_item_len;
			*offset = asn->offset+new_item_len;
			asn->offset = *offset;
			/*  do we have Event identifier  ?  */
			if(item_len > 0)
			{
			itu = proto_tree_add_text(pres_tree_pp, tvb, *offset,item_len,
					"Event identifier");
			pres_tree_pc = proto_item_add_subtree(itu, ett_pres_ms);

			(*offset)++; /* skip type  */
			asn->offset = *offset;
			item_len--;
			/* get length  */
				if (read_length(asn, pres_tree_pc, 0, &new_item_len) != 
ASN1_ERR_NOERROR)
				{
							*offset = asn->offset;
							return;
				}
			/*  try to get Event identifier  */
			value	=	get_integer_value(asn,new_item_len,offset);

			proto_tree_add_text(pres_tree_pc, tvb, *offset+1,new_item_len,
					val_to_str(value, event_identifier_values_vals,"Unknown item (0x%02x)"));
			item_len-=(asn->offset-*offset)+new_item_len;
			*offset = asn->offset+new_item_len;
						asn->offset = *offset;
			}
}
static void
show_user_data(ASN1_SCK *asn,proto_tree *pres_tree,tvbuff_t *tvb,int 
*offset,int item_len,int tag)
{
	  proto_tree *pres_tree_ud = NULL;
	  proto_tree *pres_tree_pc = NULL;
	  proto_item *itm;
	  proto_item *itu;
	  guint   start = asn->offset;
	  guint	  item_length = item_len;

			itm = proto_tree_add_text(pres_tree, tvb, *offset,(asn->offset -*offset)+ 
item_len,	"User data");
			pres_tree_ud = proto_item_add_subtree(itm, ett_pres_ms);
			itu = proto_tree_add_text(pres_tree_ud, tvb, 
*offset,item_len+(asn->offset-*offset),
					val_to_str(tag, user_data_values_vals,"Unknown item (0x%02x)"));
			pres_tree_pc = proto_item_add_subtree(itu, ett_pres_ms);
			switch(tag)
			{
			case SIMPLY_ENCODED_DATA:
				break;
			case FULLY_ENCODED_DATA:
				show_fully_encoded_data(asn,pres_tree_pc,tvb,offset,item_len);
				break;
			default:
				break;
			}


			/*   align the pointer */
			(*offset)=start+item_length;
			asn->offset = (*offset);

}

static void
show_presentation_context_definition(ASN1_SCK *asn,proto_tree 
*pres_tree,tvbuff_t *tvb,int *offset,int item_len,int tag)
{
	  proto_tree *pres_tree_ms = NULL;
	  proto_tree *pres_tree_pc = NULL;
	  proto_item *itm;
	  gint    length;
	  guint   type;
	  guint   header_len;
	  proto_item *ms;
	  gint   new_item_len;
	  guint   start = asn->offset;
	  guint	  item_length = item_len;

			itm = proto_tree_add_text(pres_tree, tvb, 
*offset,item_len+(asn->offset-*offset),
					val_to_str(tag, sequence_top_vals,"Unknown item (0x%02x)"));
			pres_tree_pc = proto_item_add_subtree(itm, ett_pres_ms);

/* do we have enough bytes to dissect ? */
			if( ( length =tvb_reported_length_remaining(tvb, *offset))  < item_len )
			{
					proto_tree_add_text(pres_tree_pc, tvb, *offset, item_len,
							"Wrong item.Need %u bytes but have %u", item_len,length);
					/*   align the pointer */
					(*offset)=start+item_length;
					asn->offset = (*offset);
					return ;
			}
			*offset =asn->offset;
			start = *offset;
			/*  read the rest */
			while ( item_len > 0 && tvb_reported_length_remaining(tvb, *offset) > 0 )
			{
				int old_offset = *offset;
			/*  get item type  */
			type = tvb_get_guint8(tvb, *offset);
			/* skip type */
			(*offset)++;
			asn->offset = *offset;
			/* get length  */
				if (read_length(asn, pres_tree_pc, 0, &new_item_len) != 
ASN1_ERR_NOERROR)
				{
					/*   align the pointer */
					(*offset)=start+item_length;
					asn->offset = (*offset);
					return  ;
				}
				header_len = asn->offset - (*offset) +1;
			/* do we have enough bytes to dissect ? */
			if( ( length =tvb_reported_length_remaining(tvb, *offset))  < 
new_item_len )
			{
					proto_tree_add_text(pres_tree_pc, tvb, *offset, new_item_len,
							"Wrong item.Need %u bytes but have %u", new_item_len,length);
					/*   align the pointer */
					(*offset)=start+item_length;
					asn->offset = (*offset);
					return ;
			}
				ms = proto_tree_add_text(pres_tree_pc, tvb, *offset-1, 
new_item_len+(asn->offset-*offset)+1,
										val_to_str(type, presentation_context_definition_vals,
										"Unknown item (0x%02x)"));
				pres_tree_ms = proto_item_add_subtree(ms, ett_pres_ms);
				*offset = asn->offset;


			switch(type)
				{

			case SEQUENCE:
				if(tag == PRESENTATION_CONTEXT_DEFINITION_RESULT_LIST
					|| tag == DEFAULT_CONTEXT_RESULT)
				{
			show_presentation_context_definition_result_seq(asn,pres_tree_ms,tvb,offset,new_item_len);
				}
				else
				{
			show_presentation_context_definition_seq(asn,pres_tree_ms,tvb,offset,new_item_len);
				}
			*offset = old_offset+(new_item_len+header_len);
					break;

			default:
					proto_tree_add_text(pres_tree_ms, tvb, *offset, 
new_item_len+(asn->offset-*offset),
					"Unknown asn.1 parameter: (0x%02x)", type);
					*offset = old_offset+(new_item_len+header_len);
				}
				item_len = item_len -  (new_item_len+header_len);

			}


					/*   align the pointer */
					(*offset)=start+item_length;
					asn->offset = (*offset);
}
/* if we can't dissect */
static void
dissect_parse_error(tvbuff_t *tvb, int offset, packet_info *pinfo,
		    proto_tree *tree, const char *field_name, int ret)
{
	char *errstr;
	errstr = asn1_err_to_str(ret);

	if (tree != NULL)
	{
		proto_tree_add_text(tree, tvb, offset, 0,
		    "ERROR: Couldn't parse %s: %s", field_name, errstr);
		call_dissector(data_handle,
		    tvb_new_subset(tvb, offset, -1, -1), pinfo, tree);
	}
}

static int read_string_value(ASN1_SCK *a, proto_tree *tree, int hf_id,
	proto_item **new_item, char **s, int start, guint length)
{
  guchar *string;
  proto_item *temp_item = NULL;
  int ret;

  if (length)
  {
    ret = asn1_string_value_decode(a, length, &string);
    if (ret != ASN1_ERR_NOERROR)
	{
      if (tree)
	  {
        proto_tree_add_text(tree, a->tvb, start, 0,
          "%s: ERROR: Couldn't parse value: %s",
          proto_registrar_get_name(hf_id), asn1_err_to_str(ret));
      }
      return ret;
    }
    string = g_realloc(string, length + 1);
    string[length] = '\0';
  }
  else
    string = "(null)";

  if (hf_id)
    temp_item = proto_tree_add_string(tree, hf_id, a->tvb, start, a->offset 
- start, string);
  if (new_item)
    *new_item = temp_item;

  if (s && length)
    *s = string;
  else
	  if (length)
			g_free(string);
  return ASN1_ERR_NOERROR;
}
static void
show_provider_reason(ASN1_SCK *asn,proto_tree *pres_tree,tvbuff_t *tvb,int 
*offset,int item_len,int type)
{
	  proto_item *ms;
	  proto_item *pr;
	  proto_tree *pres_tree_ms = NULL;
	  proto_tree *pres_tree_pr = NULL;
	  int		off      = *offset;
	  int		value=0;
	  int		new_item_len = item_len+(asn->offset-*offset);

				ms = proto_tree_add_text(pres_tree, tvb, *offset, new_item_len,
										val_to_str(type, sequence_top_vals,
										"Unknown item (0x%02x)"));
				pres_tree_ms = proto_item_add_subtree(ms, ett_pres_ms);
				value	=	get_integer_value(asn,item_len,offset);
				pr = proto_tree_add_text(pres_tree_ms, tvb, *offset, new_item_len,
										val_to_str(value, provider_reason_values_vals,
										"Unknown item (0x%02x)"));
				pres_tree_pr = proto_item_add_subtree(pr, ett_pres_ms);
				print_value(asn,pres_tree_pr,tvb,offset,item_len);
				asn->offset = *offset = off+new_item_len;
}
static void
show_presentation_selector (ASN1_SCK *asn,proto_tree *pres_tree,tvbuff_t 
*tvb,int *offset,int item_len,int type)
{
	  proto_item *ms;
	  proto_tree *pres_tree_ms = NULL;
	  int ret;
	  char *s;

				ms = proto_tree_add_text(pres_tree, tvb, *offset, 
item_len+(asn->offset-*offset),
										val_to_str(type, sequence_top_vals,
										"Unknown item (0x%02x)"));


				pres_tree_ms = proto_item_add_subtree(ms, ett_pres_ms);

				ret = read_string_value(asn, pres_tree,0,NULL, &s, 
*offset+(asn->offset-*offset), item_len);
				if(ret == ASN1_ERR_NOERROR)
				{
					if( item_len)
					{
							proto_tree_add_text(pres_tree_ms, tvb, *offset+2, item_len,
											"String:%s",s);
							g_free(s);
					}
					else
					{
							proto_tree_add_text(pres_tree_ms, tvb, *offset+2, item_len,
											"Zero selector length");

					}
				}



}
/* display top sequence  */
static void
show_sequence_top(ASN1_SCK *asn,proto_tree *pres_tree,tvbuff_t 
*tvb,packet_info *pinfo,int *offset,int item_len)
{
	int ret;
	guint cls, con, tag,len1;
	gint  type;
	gboolean def;
	proto_item *itm;
	gint length;
	while(item_len > 0 )
	{
/* do we have enough bytes to dissect this item ? */
			if( ( length =tvb_reported_length_remaining(tvb, *offset))  < item_len )
			{
					proto_tree_add_text(pres_tree, tvb, *offset, item_len,
							"Wrong Item.Need %u bytes but have %u", item_len,length);
					break;
			}
		/*   get  tag     */
			type = tvb_get_guint8(tvb, *offset);
		/* decode header  */
			ret = asn1_header_decode(asn, &cls, &con, &tag, &def, &len1);

			if (ret != ASN1_ERR_NOERROR)
			{
							dissect_parse_error(tvb, *offset, pinfo, pres_tree,
									"sequence error", ret);
				break;
			}
			item_len = item_len - (asn->offset - *offset);
						/*
						* [APPLICATION <tag>]
						*/
						switch (tag)
							{
								case TAG_01:
									/* Calling-presentation-selector and
									   User data have the same tag number
									   Try to recognize which one do we really have */
									if( con == ASN1_CON)
									{
										/* it is User data */
										/* print it                 */
										show_user_data(asn,pres_tree,tvb,offset,len1,type);
										break;
									}
									/*  it is Calling-presentation-selector  */
									/*  simply go below, we don't need to break here */

								case CALLED_PRESENTATION_SELECTOR:
								case RESPONDING_PRESENTATION_SELECTOR:
								/*case CALLING_PRESENTATION_SELECTOR:*/
								/*
								* [Called-presentation-selector]
								* [Calling-presentation-selector]
								* [Responding-presentation-selector]
								*/
									show_presentation_selector(asn,pres_tree,tvb,offset,len1,tag);
									break;
								case DEFAULT_CONTEXT_NAME:
								case PRESENTATION_CONTEXT_DEFINITION_LIST:
									show_presentation_context_definition(asn,pres_tree,tvb,offset,len1,tag);
									break;
								case PROTOCOL_VERSION:
								/*case TAG_00:  */
									if(cls == ASN1_APL)
									{
										/* yes, it is application */
											 *offset = asn->offset;
											item_len = len1;
											continue;
									}
									show_protocol_version(asn,pres_tree,tvb,offset,len1,tag);
									break;
								case PRESENTATION_CONTEXT_DEFINITION_RESULT_LIST:
								case DEFAULT_CONTEXT_RESULT:
									show_presentation_context_definition(asn,pres_tree,tvb,offset,len1,tag);
									break;
								case PRESENTATION_REQUIREMENTS:
									show_presentation_requirements(asn,pres_tree,tvb,offset,len1,tag);
									break;
								case PROVIDER_REASON:
									show_provider_reason(asn,pres_tree,tvb,offset,len1,tag);
									break;
								/* to do */

								case USER_SESSION_REQUIREMENTS:

									itm = proto_tree_add_text(pres_tree, tvb, *offset,(asn->offset 
-*offset)+ len1,
											val_to_str(tag, sequence_top_vals,"Unknown item (0x%02x)"));
								(asn->offset)+=len1;
									break;

								default:
									itm = proto_tree_add_text(pres_tree, tvb, *offset,(asn->offset 
-*offset)+ len1,
											"Unknown tag: %x",tag);
									(asn->offset)+=len1;
							}

		item_len-=len1;
		*offset = asn->offset;
	}

}
static void
show_connection_request_confirm(ASN1_SCK *asn,proto_tree *pres_tree,tvbuff_t 
*tvb,packet_info *pinfo,int *offset,int* item_len)
{
  guint8 type;
  guint length;
  proto_tree *pres_tree_ms = NULL;
  proto_item *ms;
			/*  get type of set  */
			while ( tvb_reported_length_remaining(tvb, *offset) > 0 )
			{
				int asn1_tag;
			/*  get item type  */
			type = tvb_get_guint8(tvb, *offset);
			asn1_tag = type & 0x1f;
			/* skip type */
			(*offset)++;
			asn->offset = *offset;
			/* get length  */
				if (read_length(asn, pres_tree, 0, item_len) != ASN1_ERR_NOERROR)
				{
					return;
				}
			/* do we have enough bytes to dissect ? */
			if( ( length =tvb_reported_length_remaining(tvb, *offset))  < 
(guint)*item_len )
			{
					proto_tree_add_text(pres_tree, tvb, *offset, -1,
							"Wrong item.Need %u bytes but have %u", *item_len,length);
				return;
			}
				ms = proto_tree_add_text(pres_tree, tvb, *offset-1, 
*item_len+(asn->offset-*offset)+1,
										val_to_str(asn1_tag, cr_vals,
										"Unknown item (0x%02x)"));
				pres_tree_ms = proto_item_add_subtree(ms, ett_pres_ms);

			switch(asn1_tag)
				{
			case MODE_SELECTOR:
				proto_tree_add_uint(pres_tree_ms, hf_pres_ms_type, tvb, (*offset)-1, 1, 
type);
				proto_tree_add_text(pres_tree_ms, tvb, *offset, (asn->offset-*offset),
										"Length:%u",*item_len);
				*offset=asn->offset;
				show_integer(asn,pres_tree_ms,tvb,offset,*item_len);
					break;
			case SET_TOP:
			case SEQUENCE_TOP:
				proto_tree_add_uint(pres_tree_ms, hf_pres_seq_type, tvb, (*offset)-1, 1, 
type);
				proto_tree_add_text(pres_tree_ms, tvb, *offset, (asn->offset-*offset),
										"Length:%u",*item_len);
				*offset=asn->offset;
				show_sequence_top(asn,pres_tree_ms,tvb,pinfo,offset,*item_len);
					break;
			default:
					proto_tree_add_text(pres_tree, tvb, (*offset)-1, 
*item_len+(asn->offset-*offset)+1,
					"Unknown asn.1 parameter: (0x%02x).Tag :(0x%02x)", type,asn1_tag);
					(*offset)+=*item_len+(asn->offset-*offset);
					asn->offset = *offset;
				}

			}
}


/*
* Dissect an Ppdu.
*/
static int
dissect_ppdu(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree 
*tree)
{
  proto_item *ti;
  proto_tree *pres_tree = NULL;
  guint length;
  guint       rest_len;
  guint  s_type;
  ASN1_SCK asn;
  guint cp_type_len;
/* do we have spdu type from the session dissector?  */
	if( !pinfo->private_data )
				{
				if(tree)
						{
					proto_tree_add_text(tree, tvb, offset, -1,
							"Internal error:can't get spdu type from session dissector.");
					return  FALSE;
						}
				}
	else
				{
					session  = ( (struct SESSION_DATA_STRUCTURE*)(pinfo->private_data) );
					if(session->spdu_type == 0 )
					{
						if(tree)
						{
					proto_tree_add_text(tree, tvb, offset, -1,
							"Internal error:wrong spdu type %x from session dissector.",session->spdu_type);
					return  FALSE;
						}
					}
				}
/* get type of tag      */
	s_type = tvb_get_guint8(tvb, offset);
		/*  set up type of Ppdu */
  	if (check_col(pinfo->cinfo, COL_INFO))
					col_add_str(pinfo->cinfo, COL_INFO,
						val_to_str(session->spdu_type, ses_vals, "Unknown Ppdu type (0x%02x)"));
  if (tree)
	{
		ti = proto_tree_add_item(tree, proto_pres, tvb, offset, -1,
		    FALSE);
		pres_tree = proto_item_add_subtree(ti, ett_pres);
	}
	offset++;
/*    open asn.1 stream    */
	asn1_open(&asn, tvb, offset);

	switch(session->spdu_type)
	{
		case SES_REFUSE:
			proto_tree_add_uint(pres_tree, hf_pres_type, tvb, offset-1, 1, s_type);
		if (read_length(&asn, pres_tree, hf_cp_type_message_length, &cp_type_len) 
!= ASN1_ERR_NOERROR)
					{
					return  FALSE;
					}
			/* skip length   */
			offset = asn.offset;
			/* do we have enough bytes to dissect ? */
			if( ( length =tvb_reported_length_remaining(tvb, offset))  < cp_type_len 
)
			{
				if(tree)
				{
					proto_tree_add_text(pres_tree, tvb, offset, -1,
							"Wrong Ppdu.Need %u bytes but have %u", cp_type_len,length);
				}
			return FALSE;
			}
				{
				asn.offset = offset;
				show_sequence_top(&asn,pres_tree,tvb,pinfo,&offset,cp_type_len);
				offset=asn.offset;
				}
			break;
		case SES_CONNECTION_REQUEST:
		case SES_CONNECTION_ACCEPT:
			proto_tree_add_uint(pres_tree, hf_pres_type, tvb, offset-1, 1, s_type);

			if (read_length(&asn, pres_tree, hf_cp_type_message_length, &cp_type_len) 
!= ASN1_ERR_NOERROR)
					{
					return  FALSE;
					}
			/* skip length   */
			offset = asn.offset;
			/* do we have enough bytes to dissect ? */
			if( ( length =tvb_reported_length_remaining(tvb, offset))  < cp_type_len 
)
			{
				if(tree)
				{
					proto_tree_add_text(pres_tree, tvb, offset, -1,
							"Wrong Ppdu.Need %u bytes but have %u", cp_type_len,length);
				}
			return FALSE;
			}
			{
			show_connection_request_confirm(&asn,pres_tree,tvb,pinfo,&offset,&cp_type_len);
			}
				break;
		case SES_ABORT:
	  			/* get length  */
				if (read_length(&asn, pres_tree, 0, &rest_len) != ASN1_ERR_NOERROR)
						{
					return  FALSE;
						}
				/* skip length   */
				offset = asn.offset;
				/* do we have enough bytes to dissect ? */
				if( ( length =tvb_reported_length_remaining(tvb, offset))  < rest_len )
				{
					if(tree)
					{
					proto_tree_add_text(pres_tree, tvb, offset, -1,
							"Wrong Ppdu.Need %u bytes but have %u", rest_len,length);
					}
				return FALSE;
				}
				if(session->abort_type == SESSION_USER_ABORT )
				{
					/* is it PC */
					if(s_type == ASN1_CLASS_PC+ASN1_CLASS_CONTEXT_SPECIFIC)
					{
							{
							offset=asn.offset;
							show_sequence_top(&asn,pres_tree,tvb,pinfo,&offset,rest_len);
							offset=asn.offset;
							}
					}
					else
					{
							{
							offset=asn.offset;
							show_session_provider_abort(&asn,pres_tree,tvb,&offset,rest_len);
							offset=asn.offset;
							}
					}
				}
				else
				{
							{
							offset=asn.offset;
							show_sequence_top(&asn,pres_tree,tvb,pinfo,&offset,rest_len);
							offset=asn.offset;
							}
				}
			break;
		default:
			{
				proto_item *ms;
				proto_tree *pres_tree_ms = NULL;
				/* back to length  */
				  offset--;
	  			/* get length  */
				if (read_length(&asn, pres_tree, 0, &rest_len) != ASN1_ERR_NOERROR)
						{
					return  FALSE;
						}
				if(!rest_len)
				{
					guint       rest_pdu_len = 0;
					/* do we really haven't any more bytes ? */
					if(	(rest_pdu_len = tvb_reported_length_remaining(tvb, offset)) )
					{
						/*
						 * we have but can't say how many from asn1 information.
						 * use pdu len instead
						 */
						if(rest_pdu_len > 2)
						{
						rest_len = rest_pdu_len;
						}
					}
				}
                if( ((gint)rest_len) > 0) {
				ms = proto_tree_add_text(pres_tree, tvb, offset, rest_len,
										val_to_str(session->spdu_type, ses_vals, "Unknown Ppdu type (0x%02x)"));
				pres_tree_ms = proto_item_add_subtree(ms, ett_pres_ms);
				show_user_data(&asn,pres_tree_ms,tvb,&offset,rest_len,s_type);
                }
			}
	}
/*    close asn.1 stream    */
	  asn1_close(&asn, &offset);

	return offset;
}

/*
* Dissect PPDUs inside a SPDU.
*/
static void
dissect_pres(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
	int offset = 0;
/* first, try to check length   */
/* do we have at least 4 bytes  */
	if (!tvb_bytes_exist(tvb, 0, 4))
	{
			proto_tree_add_text(tree, tvb, offset, tvb_reported_length_remaining(tvb, 
offset),
								"User data");
			return;  /* no, it isn't a presentation PDU */
	}

	/*  we can't make any additional checking here   */
	/*  postpone it before dissector will have more information */

	if (check_col(pinfo->cinfo, COL_PROTOCOL))
		col_set_str(pinfo->cinfo, COL_PROTOCOL, "PRES");
  	if (check_col(pinfo->cinfo, COL_INFO))
  		col_clear(pinfo->cinfo, COL_INFO);
	/* save pointers for calling the acse dissector  */
	global_tree = tree;
	global_pinfo = pinfo;

	while (tvb_reported_length_remaining(tvb, offset) > 0)
			{
		offset = dissect_ppdu(tvb, offset, pinfo, tree);
		if(offset == FALSE )
							{
									proto_tree_add_text(tree, tvb, offset, -1,"Internal error");
									offset = tvb_length(tvb);
									break;
							}
			}
}

void
proto_register_pres(void)
{
	static hf_register_info hf[] =
	{
		{
			&hf_pres_type,
			{
				"PPDU Type",
				"pres.type",
				FT_UINT8,
				BASE_DEC,
				VALS(pres_vals),
				0x0,
				"", HFILL
			}
		},
		{
			&hf_pres_length,
			{
				"Length",
				"pres.length",
				FT_UINT16,
				BASE_DEC,
				NULL,
				0x0,
				"", HFILL
			}
		},
		{
			&hf_cp_type_message_length,
			{
				"Message Length",
				"cp_type.message_length",
				FT_UINT32,
				BASE_DEC,
				NULL,
				0x0,
				"CP type Message Length",
				HFILL
			}
		},
		{
			&hf_pres_rc_type,
			{
				"Connection reqiest/confirm",
				"pres.type",
				FT_UINT8,
				BASE_DEC,
				VALS(pres_vals),
				0x0,
				"Connection reqiest/confirm",
				HFILL
			}
		},

		{
			&hf_pres_ms_type,
			{
				"Mode selector",
				"pres.mode.selector",
				FT_UINT8,
				BASE_DEC,
				NULL,
				0x0,
				"Mode select",
				HFILL
			}
		},
		{
			&hf_pres_protocol_version,
			{
				"Protocol version",
				"pres.protocol.version",
				FT_UINT16,
				BASE_HEX,
				NULL,
				0x0,
				"Protocol version",
				HFILL
			}
		},
		{
			&hf_value,
			{
				"Value",
				"pres.value",
				FT_UINT8,
				BASE_DEC,
				NULL,
				0x0,
				"Value",
				HFILL
			}
		},

		{
			&hf_pres_seq_type,
			{
				"Sequence",
				"pres.sequence",
				FT_UINT8,
				BASE_DEC,
				NULL,
				0x0,
				"Mode select",
				HFILL
			}
		},

		{
			&hf_protocol_version,
			{
				"Protocol version 1",
				"pres.protocol.version",
				FT_BOOLEAN, 16,
				NULL,
				PRES_PROTOCOL_VERGION,
				"Protocol version 1",
				HFILL
			}
		},
		{
			&hf_context_management,
			{
				"Context management",
				"pres.context.management",
				FT_BOOLEAN, 16,
				NULL,
				PRES_CONTEXT_MANAGEMENT,
				"Context management",
				HFILL
			}
		},
		{
			&hf_restoration,
			{
				"Restoration",
				"pres.restoration",
				FT_BOOLEAN, 16,
				NULL,
				PRES_RESTORATION,
				"Restoration",
				HFILL
			}
		},


	};

	static gint *ett[] =
	{
		&ett_pres,
		&ett_pres_param,
		&ett_pres_rc,
		&ett_pres_ms,
		&ett_pres_itm,
	};
	module_t *pres_module;


	proto_pres = proto_register_protocol(PROTO_STRING_PRES, "PRES", "pres");
	proto_register_field_array(proto_pres, hf, array_length(hf));
	proto_register_subtree_array(ett, array_length(ett));

	pres_module = prefs_register_protocol(proto_pres, NULL);

	/*
	 * Register the dissector by name, so other dissectors can
	 * grab it by name rather than just referring to it directly
	 * (you can't refer to it directly from a plugin dissector
	 * on Windows without stuffing it into the Big Transfer Vector).
	 */
	register_dissector("pres", dissect_pres, proto_pres);
}

void
proto_reg_handoff_pres(void)
{
	/*   find data dissector  */
	data_handle = find_dissector("data");
	/* define acse sub dissector */
	acse_handle = find_dissector("acse");
}
