/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\

File: octvc1_plugin.c

Copyright (c) 2014 Octasic Inc. All rights reserved.

Description: 

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation; either version 3 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 Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

Release: Octasic Application Development Framework OCTADF-01.00.01-B497 (2014/01/09)

$Octasic_Revision: $

\*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/

#include <string.h>

#include <config.h>
#include <epan/packet.h>
#include <epan/prefs.h>
#include <epan/expert.h>

#include "../include/plugin_info.h"
#include "../include/module.h"
#define OCT_DECLARE_COMMON
#include "../include/octvc1_common.h"
#include <gsm/octvc1_gsm_api.h>

static int proto_octvc1 = -1;
static int fUserRegistered = 0;

char * pszOctvc1_user_name=NULL;
char * pszOctvc1_user_id=NULL;
int g_fIsRegistered = 0;
static char l_szRegisteredUser[128]={0};
static int l_iRegisteredUserLen = 0;
static int l_id_code_Registered = 0;
static gchar l_szAPIStr[256];
static int l_fIsGod = 0;

dissector_handle_t data_handle;

const gchar* octvc1_chck_private( guint32 f_id_code, const value_string *f_PrivateApi, gint32 *f_piRegistered )
{
	const gchar* pszValueString;

	//CHck if same as last request
	if( l_iRegisteredUserLen && ( l_id_code_Registered == f_id_code ) )
	{
		*f_piRegistered = 1;
		return l_szAPIStr;
	}

	*f_piRegistered = 0;
	pszValueString = val_to_str( f_id_code, f_PrivateApi, cOCTVC1_UNKNOWN_STRING );
	if( strcmp( pszValueString, cOCTVC1_UNKNOWN_STRING ) )
	{
		// Expect API ID STRING:user,user2,user3
		gchar *pszColumn = strchr( pszValueString, ':' );
		if( pszColumn )
		{
			gchar *pszStr;

			// Keep API string
			strncpy( l_szAPIStr, pszValueString, (pszColumn-pszValueString) );
			l_szAPIStr[(pszColumn-pszValueString)]=0;

			// Chck if god is register
			if( !l_fIsGod )
				pszStr = strstr( pszColumn+1, l_szRegisteredUser );

			// Find if register for this
			if( l_fIsGod ||
				( pszStr && ((pszStr==(pszColumn+1)) || (*(pszStr-1)==',') ) && 
				  ( (*(pszStr+l_iRegisteredUserLen)==',') || (*(pszStr+l_iRegisteredUserLen)==0 ) ) ) )
			{
				l_id_code_Registered = f_id_code;
				*f_piRegistered = 1;
				return l_szAPIStr;
			}
		}
	}
	return cOCTVC1_UNKNOWN_STRING;
}

void pref_proto_octvc1_apply_callback( void )
{
	char *pszPath;

	// by default this callback is called at startup ... skip the first call
	if( fUserRegistered )
	{
		int fReguser=0;

		if( ( strcmp( pszOctvc1_user_name, cOCTVC1_GOD_USER ) == 0 ) &&
			( strcmp( pszOctvc1_user_id, cOCTVC1_GOD_USER_ID ) == 0 ) )
		{
			fReguser = 1;
			l_fIsGod = 1;
		}
		else
		{
			int i;

			for( i=0; ; i++ )
			{
				if( aOCTVC1_user_list[i][0] == NULL )
					break;

				if( ( strcmp( aOCTVC1_user_list[i][0], pszOctvc1_user_name ) == 0 ) &&
					( strcmp( aOCTVC1_user_list[i][1], pszOctvc1_user_id ) == 0 ) )
				{
					fReguser = 1;
					l_fIsGod = 0;
					break;
				}
			}
		}

		if( fReguser )
		{
			g_fIsRegistered = 1;
			strcpy( l_szRegisteredUser, pszOctvc1_user_name  );
			l_iRegisteredUserLen = strlen( l_szRegisteredUser );
		}
		else
		{
			g_fIsRegistered = 0;
			l_fIsGod = 0;
		}
	}
	fUserRegistered = 1;

	prefs_set_pref( "octvc1.user_name:user" );
	prefs_set_pref( "octvc1.user_id:****" );
	write_prefs( &pszPath );

	// Reset last check
	l_id_code_Registered = 0;
	l_szAPIStr[0] = 0;



}

extern tModuleColInfo aOCTVC1ModuleColInfo[];
#define mWD_OCTVC1_CID_CODE(uint32)					((uint32) & 0x00000FFF)


void dissect_OCTVC1_GSM(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    int temp_data, ulMsgType, ulCommandId, ulOctvc1MsgType, remaining_length, module_index;
    int offset = 0;
    tvbuff_t *message_tvb;
    const gchar*	pszValueString = NULL;
    tModuleColInfo GsmModule;

	/* Find Tbl Module Index */
	match_strval_idx( cOCTVC1_GSM_UID, vals_OCTVC1_module_UID, &module_index );
    
    /*Get the proper module info */
    GsmModule = aOCTVC1ModuleColInfo[module_index];
    
    temp_data = tvb_get_ntohs( tvb, offset );
    proto_tree_add_text(tree, tvb, offset, 2, "reserved %d", temp_data );
    offset += 2;
    ulMsgType           = tvb_get_guint8( tvb, offset );
    ulCommandId         = (ulMsgType & 0x7F) | cOCTVC1_GSM_CID_BASE;
    ulOctvc1MsgType     = ((ulMsgType & 0x80) == 0 )? cOCTVC1_MSG_TYPE_RESPONSE: cOCTVC1_MSG_TYPE_COMMAND;
    
    /* Hardcoded event mapping */
    if( ulMsgType == 0x01 ||  ulMsgType == 0x02 || ulMsgType == 0x07 ||  ulMsgType == 0x0e || ulMsgType == 0x16 )
    {
        ulOctvc1MsgType = cOCTVC1_MSG_TYPE_NOTIFICATION;
        ulCommandId     = ulMsgType | cOCTVC1_GSM_EID_BASE;
    }
    
    proto_tree_add_text(tree, tvb, offset, 1, "MsgType 0x%02x (CID:0x%08x, type:%d)", ulMsgType, ulCommandId, ulOctvc1MsgType );
    offset += 1;
    temp_data = tvb_get_guint8( tvb, offset );
    proto_tree_add_text(tree, tvb, offset, 1, "size: %d", temp_data );
    offset += 1; 
    temp_data = tvb_get_ntohs( tvb, offset );
    proto_tree_add_text(tree, tvb, offset, 2, "sequence %d", temp_data );
    offset += 2;

   if( (ulMsgType & 0x7F) == 0x00 )
   {
		ulCommandId |= 0x100;
   }
   /*API is not orthogonal!! remap response to proper Ids */
   if( ulOctvc1MsgType == cOCTVC1_MSG_TYPE_RESPONSE )
   {
        if( ulMsgType == 0x08 ||  ulMsgType == 0x09 )
             ulCommandId         = ((ulMsgType-1) & 0x7F) | cOCTVC1_GSM_CID_BASE;

        if( ulMsgType == 0x0a )
            ulCommandId         = (0x11) | cOCTVC1_GSM_CID_BASE;
   }
   
   if( ulOctvc1MsgType == cOCTVC1_MSG_TYPE_COMMAND )
   {
        if( ulMsgType == 0x82 || ulMsgType == 0x81 )
            ulCommandId |= cOCTVC1_MSG_NO_RESPONSE_FLAG;

   }
   
   if( ulOctvc1MsgType == cOCTVC1_MSG_TYPE_NOTIFICATION )
   {
       pszValueString = val_to_str( mWD_OCTVC1_CID_CODE( ulCommandId), GsmModule.apEvtIdValueString, cOCTVC1_UNKNOWN_STRING );
       
   }else {
   
       pszValueString = val_to_str( mWD_OCTVC1_CID_CODE( ulCommandId), GsmModule.apCodeIdValueString, cOCTVC1_UNKNOWN_STRING );
   }
   
   if ( check_col(pinfo->cinfo, COL_INFO) )
   {
        col_add_fstr( pinfo->cinfo, COL_INFO,
                        "%s_MSG_%s(0x%04X)",
                        GsmModule.pszModuleName,
                        pszValueString,
                        ulCommandId );
    }
      
   
   if ( check_col(pinfo->cinfo, COL_PROTOCOL) )
        col_set_str(pinfo->cinfo, COL_PROTOCOL, "oct_gsm");
       
        
   remaining_length = tvb_reported_length_remaining(tvb, offset);
   message_tvb = tvb_new_subset(tvb, offset, remaining_length, remaining_length);
   GsmModule.Dissector_fnc( ulOctvc1MsgType, ulCommandId, message_tvb, pinfo, tree);

	/* We can call the amr plugin */
	/* think it can support IF2 Frame (GPP TS 26.101), need to double check */
}


#ifdef cOCTVC1_WCDMA_UID
void dissect_OCTVC1_WCDMA(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
    int temp_data, ulMsgType, ulCommandId, ulOctvc1MsgType, remaining_length, module_index;
    int offset = 0;
    tvbuff_t *message_tvb;
    const gchar*	pszValueString = NULL;
    tModuleColInfo WcdmaModule;

	/* Find Tbl Module Index */
	match_strval_idx( cOCTVC1_WCDMA_UID, vals_OCTVC1_module_UID, &module_index );
    
    /*Get the proper module info */
    WcdmaModule = aOCTVC1ModuleColInfo[module_index];
	ulMsgType	= 0;

    ulOctvc1MsgType     = cOCTVC1_MSG_TYPE_COMMAND;
    
	temp_data = tvb_get_ntohs( tvb, offset );
    proto_tree_add_text(tree, tvb, offset, 1, "L1c etherType 0x%02x", temp_data );
    offset += 2;
    temp_data = tvb_get_ntohs( tvb, offset );
    proto_tree_add_text(tree, tvb, offset, 1, "Body Size: %d", temp_data );
    offset += 2; 
    temp_data = tvb_get_ntohs( tvb, offset );
	proto_tree_add_text(tree, tvb, offset, 2, "Message Type: %d", temp_data );
    offset += 2; 

    temp_data = tvb_get_ntohs( tvb, offset );
	proto_tree_add_text(tree, tvb, offset, 2, "Message Id: %d", temp_data );
	ulCommandId         = (temp_data & 0x7F) | cOCTVC1_WCDMA_CID_BASE;

    offset += 2; 
    temp_data = tvb_get_guint8( tvb, offset );
	proto_tree_add_text(tree, tvb, offset, 2, "FP Header size: %d", temp_data );
    offset += 2; 
	temp_data = tvb_get_guint8( tvb, offset );
	proto_tree_add_text(tree, tvb, offset, 2, "Transaction Id: %d", temp_data );
    offset += 2; 
  
   /*API is not orthogonal!! remap response to proper Ids */
   if( ulOctvc1MsgType == cOCTVC1_MSG_TYPE_RESPONSE )
   {
        if( ulMsgType == 0x08 ||  ulMsgType == 0x09 )
             ulCommandId         = ((ulMsgType-1) & 0x7F) | cOCTVC1_GSM_CID_BASE;

        if( ulMsgType == 0x0a )
            ulCommandId         = (0x11) | cOCTVC1_GSM_CID_BASE;
   }

   if( ulOctvc1MsgType == cOCTVC1_MSG_TYPE_NOTIFICATION )
   {
       pszValueString = val_to_str( mWD_OCTVC1_CID_CODE( ulCommandId), WcdmaModule.apEvtIdValueString, cOCTVC1_UNKNOWN_STRING );
       
   }else {
   
       pszValueString = val_to_str( mWD_OCTVC1_CID_CODE( ulCommandId), WcdmaModule.apCodeIdValueString, cOCTVC1_UNKNOWN_STRING );
   }
   
   if ( check_col(pinfo->cinfo, COL_INFO) )
   {
        col_add_fstr( pinfo->cinfo, COL_INFO,
                        "%s_MSG_%s(0x%04X)",
                        WcdmaModule.pszModuleName,
                        pszValueString,
                        ulCommandId );
    }
      
   
   if ( check_col(pinfo->cinfo, COL_PROTOCOL) )
        col_set_str(pinfo->cinfo, COL_PROTOCOL, "oct_wcdma");
       
        
   remaining_length = tvb_reported_length_remaining(tvb, offset);
   message_tvb = tvb_new_subset(tvb, offset, remaining_length, remaining_length);
   WcdmaModule.Dissector_fnc( ulOctvc1MsgType, ulCommandId, message_tvb, pinfo, tree);

	/* We can call the amr plugin */
	/* think it can support IF2 Frame (GPP TS 26.101), need to double check */
}
#endif

/*************************************************************************
*
*  Code to register the protocol with Wireshark
*
**************************************************************************/
void proto_register_octvc1(void)
{                 
	module_t *octvc1_module;
    int proto_gsm = -1;
	int proto_wcdma = -1;

	proto_octvc1 = proto_register_protocol(	"OCTVC1 Packets", "Octasic OCTVC1", "octvc1");
	octvc1_module = prefs_register_protocol(proto_octvc1, pref_proto_octvc1_apply_callback);
	prefs_register_string_preference(octvc1_module, "user_name", "User name", "OCTVC1 user name", &pszOctvc1_user_name);
	prefs_register_string_preference(octvc1_module, "user_id", "User id", "OCTVC1 user id", &pszOctvc1_user_id);

	/* register ctrl dissector */
	proto_register_octvc1_ctrl();

	/* register event dissector */
	proto_register_octvc1_event();

	/* Call module register fnc */
	ws_register_dissector_module();
    
    
    proto_gsm = proto_register_protocol(	"OCTASIC GSM MODULE", "OCTGSM", "oct_gsm");
    register_dissector("OCTGSM", dissect_OCTVC1_GSM, proto_gsm);   

#ifdef cOCTVC1_WCDMA_UID
    proto_wcdma = proto_register_protocol(	"OCTASIC WCDMA MODULE", "OCTWCDMA", "oct_wcdma");
    register_dissector("OCTWCDMA", dissect_OCTVC1_WCDMA, proto_wcdma);   
#endif

    
}



  

/*************************************************************************
*
*  If this dissector uses sub-dissector registration add a registration routine.
*  This format is required because a script is used to find these routines and
*  create the code that calls these routines.
*
**************************************************************************/
void proto_reg_handoff_octvc1(void)
{
	extern int proto_octvc1_event;
	extern int proto_octvc1_ctrl;

	extern int dissect_octvc1_ctrl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
	extern int dissect_octvc1_event(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);

	dissector_handle_t octvc1_ctrl_handle;
	dissector_handle_t octvc1_event_handle;

	register_dissector_table( "vocallonet.api_type", "Vocallonet API", FT_UINT32, BASE_HEX);

	octvc1_ctrl_handle = new_create_dissector_handle(dissect_octvc1_ctrl, proto_octvc1_ctrl);
	dissector_add_uint("vocallonet.api_type", 0, octvc1_ctrl_handle);
	
	octvc1_event_handle = new_create_dissector_handle(dissect_octvc1_event, proto_octvc1_event);
	dissector_add_uint("vocallonet.api_type", 0, octvc1_event_handle);

	data_handle = find_dissector("data");

}

