/*      CAP public function c file                      */
/*      Written by Liu Zhiguo 2003-08-26        */
/*      Version 1.0                                                     */
/*      --------------------------------        */

#include "../../../public/src/include/pub_include.h"
#include "../../../sccp/src/include/sccp.h"
#include "../mapp/map_const.h"
#include "../mapp/map_code.h"
#include "../mapp/map_struct.h"
#include "cap_head.h"

void cap_get_sccpadd(struct SCCP_ADDRESS *add_ptr, u8 * data)
{
     u8   temp_len;

     add_ptr->ip = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3];
     add_ptr->DPC = (data[4] << 24) + (data[5] << 16) + (data[6] << 8) + data[7];
     add_ptr->NetID = data[8];
     add_ptr->SSN = data[9];
     add_ptr->RI = data[10];
     add_ptr->GTI = data[11];
     add_ptr->TT = data[12];
     add_ptr->NP = data[13];
     add_ptr->ES = data[14];
     add_ptr->NAI = data[15];
     add_ptr->len = data[16];
     temp_len = (data[16] + 1) / 2;
     memcpy(add_ptr->GTAI, data + 17, temp_len);
}

void cap_set_sccpadd(struct SCCP_ADDRESS *add_ptr, u8 * data)
{
     u8   temp_len;

     data[0] = add_ptr->ip >> 24;
     data[1] = add_ptr->ip >> 16;
     data[2] = add_ptr->ip >> 8;
     data[3] = add_ptr->ip;
     data[4] = add_ptr->DPC >> 24;
     data[5] = add_ptr->DPC >> 16;
     data[6] = add_ptr->DPC >> 8;
     data[7] = add_ptr->DPC;
     data[8] = add_ptr->NetID;
     data[9] = add_ptr->SSN;
     data[10] = add_ptr->RI;
     data[11] = add_ptr->GTI;
     data[12] = add_ptr->TT;
     data[13] = add_ptr->NP;
     data[14] = add_ptr->ES;
     data[15] = add_ptr->NAI;
     data[16] = add_ptr->len;
     temp_len = (add_ptr->len + 1) / 2;
     memcpy(data + 17, add_ptr->GTAI, temp_len);
}

u8 cap_com_ftos(struct MapComSrv_struct *map_com, u8 * data_flow)
{                               // common service data flow to structure
     u32  aa;
     u32  len;
     u8   ii;
     u8   delimiter_flag = 0;
     struct MapOpen_Arg *open_arg;
     struct MapOpen_Res *open_res;
     struct MapClose_Arg *close_ptr;
     struct MapUAbort_Arg *uabrt_ptr;
     struct MapPAbort_Arg *pabrt_ptr;

     len = (data_flow[0] << 8) + data_flow[1];
     if(len < 9)                // data flow is too short
          return 0;
     if(data_flow[2] != CAP_FLAG)
          return 0;
     map_com->port_id = (data_flow[3] << 8) + data_flow[4];
     map_com->dialogue_id = (data_flow[6] << 8) + data_flow[7];
     map_com->message_type = data_flow[9];
     map_com->message_flag = data_flow[10];
     switch (map_com->message_type)
     {
     case MAP_OPEN:
          if(map_com->message_flag == MAP_REQUEST || map_com->message_flag == MAP_INDICATE)
          {
               open_arg = (MapOpen_Arg *) & map_com->dlg_list.open_arg;
               open_arg->acn_data.acn = data_flow[11];
               open_arg->acn_data.acn_ver = data_flow[12];
               cap_get_sccpadd(&open_arg->peer_add, data_flow + 13);
               cap_get_sccpadd(&open_arg->local_add, data_flow + 41);
               open_arg->param_flag = 0x0b;
               aa = 70;
               for(ii = 0; ii < 3; ii++)
               {
                    if(data_flow[aa] == 0x00)
                         break;
                    else if(data_flow[aa] == 0x01)      // peer reference
                    {
                         len = data_flow[aa + 1];
                         if(len > ISDN_LEN)
                              return 0;
                         open_arg->peerref_len = len;
                         memcpy(open_arg->peer_reference, &data_flow[aa + 2], len);
                         aa += len + 2;
                         open_arg->param_flag |= 0x04;
                    }
                    else if(data_flow[aa] == 0x02)      // local reference
                    {
                         len = data_flow[aa + 1];
                         if(len > ISDN_LEN)
                              return 0;
                         open_arg->localref_len = len;
                         memcpy(open_arg->local_reference, &data_flow[aa + 2], len);
                         aa += len + 2;
                         open_arg->param_flag |= 0x10;
                    }
                    else if(data_flow[aa] == 0x03)      // specific info
                    {
                         len = data_flow[aa + 1];
                         if(len >= MAX_SPCINFO_LEN)
                              return 0;
                         open_arg->specific_info.info_len = len;
                         memcpy(open_arg->specific_info.info_data, &data_flow[aa + 2], len);
                         open_arg->param_flag |= 0x20;
                         aa += 2 + len;
                    }
                    else if(data_flow[aa] == 0x10)
                    {
                         delimiter_flag = 1;
                         aa += 2;
                    }
               }
          }
          else if(map_com->message_flag == MAP_RESPONSE || map_com->message_flag == MAP_CONFIRM)
          {
               open_res = (MapOpen_Res *) & map_com->dlg_list.open_res;
               open_res->result = data_flow[11];
               open_res->param_flag = 0x08;
               aa = 13;
               for(ii = 0; ii < 4; ii++)
               {
                    if(data_flow[aa] == 0x00)
                         break;
                    else if(data_flow[aa] == 0x01)      // acn
                    {
                         open_res->acn_data.acn = data_flow[aa + 2];
                         open_res->acn_data.acn_ver = data_flow[aa + 3];
                         aa += 4;
                         open_res->param_flag |= 0x01;
                    }
                    else if(data_flow[aa] == 0x02)      // specific info
                    {
                         len = data_flow[aa + 1];
                         if(len >= MAX_SPCINFO_LEN)
                              return 0;
                         open_res->specific_info.info_len = len;
                         memcpy(open_res->specific_info.info_data, &data_flow[aa + 2], len);
                         open_res->param_flag |= 0x02;
                         aa += 2 + len;
                    }
                    else if(data_flow[aa] == 0x03)      // refuse reason
                    {
                         open_res->refuse_reason = data_flow[aa + 2];
                         open_res->param_flag |= 0x10;
                         aa += 3;
                    }
                    else if(data_flow[aa] == 0x04)      // provider error
                    {
                         open_res->provider_error = data_flow[aa + 2];
                         open_res->param_flag |= 0x20;
                         aa += 3;
                    }
                    else if(data_flow[aa] == 0x10)
                    {
                         delimiter_flag = 1;
                         aa += 2;
                    }
               }
          }
          else
               return 0;
          break;
     case MAP_CLOSE:
          close_ptr = (MapClose_Arg *) & map_com->dlg_list.close_arg;
          close_ptr->release_method = data_flow[11];
          close_ptr->param_flag = 0x01;
          if(data_flow[13] == 0x01)
          {
               len = data_flow[14];
               if(len >= MAX_SPCINFO_LEN)
                    return 0;
               close_ptr->specific_info.info_len = len;
               memcpy(close_ptr->specific_info.info_data, &data_flow[15], len);
               close_ptr->param_flag |= 0x02;
          }
          break;
     case MAP_U_ABORT:
          uabrt_ptr = (MapUAbort_Arg *) & map_com->dlg_list.uabort_arg;
          uabrt_ptr->user_reason = data_flow[11];
          uabrt_ptr->param_flag = 0x01;
          aa = 13;
          for(ii = 0; ii < 2; ii++)
          {
               if(data_flow[aa] == 0)
                    break;
               else if(data_flow[aa] == 0x01)   // diagnostic flag
               {
                    uabrt_ptr->diag_info = data_flow[aa + 2];
                    uabrt_ptr->param_flag |= 0x02;
                    aa += 3;
               }
               else if(data_flow[aa] == 0x02)   // specific info
               {
                    len = data_flow[aa + 1];
                    if(len >= MAX_SPCINFO_LEN)
                         return 0;
                    uabrt_ptr->specific_info.info_len = len;
                    memcpy(uabrt_ptr->specific_info.info_data, &data_flow[aa + 2], len);
                    uabrt_ptr->param_flag |= 0x04;
                    aa += 2 + len;
               }
          }
          break;
     case MAP_P_ABORT:
          pabrt_ptr = (MapPAbort_Arg *) & map_com->dlg_list.pabort_arg;
          pabrt_ptr->provider_reason = data_flow[11];
          pabrt_ptr->source = data_flow[12];
          pabrt_ptr->param_flag = 0x03;
          break;
     case MAP_NOTICE:
          map_com->dlg_list.notice_arg.problem = data_flow[11];
          map_com->dlg_list.notice_arg.param_flag = 0x01;
          break;
     case MAP_DELIMITER:
     case MAP_LINK:
          break;
     default:
          return 0;
          break;
     }
     if(delimiter_flag == 1)
          return 2;
     else
          return 1;
}

void cap_com_stof(struct MapComSrv_struct *com_ptr, u8 * data_flow, u8 delimiter_flag)
{                               // transfer common structure to data flow
     u32  ii;
     u32  len;
     struct MapOpen_Arg *open_arg;
     struct MapOpen_Res *open_res;
     struct MapClose_Arg *close_arg;
     struct MapUAbort_Arg *uabrt_arg;

     data_flow[2] = CAP_FLAG;
     data_flow[3] = com_ptr->port_id >> 8;
     data_flow[4] = com_ptr->port_id;
     data_flow[6] = com_ptr->dialogue_id >> 8;
     data_flow[7] = com_ptr->dialogue_id;
     data_flow[9] = com_ptr->message_type;
     data_flow[10] = com_ptr->message_flag;
     ii = 11;
     switch (com_ptr->message_type)
     {
     case MAP_OPEN:
          if(com_ptr->message_flag == MAP_INDICATE || com_ptr->message_flag == MAP_REQUEST)
          {
               open_arg = (MapOpen_Arg *) & com_ptr->dlg_list.open_arg;
               data_flow[ii++] = open_arg->acn_data.acn;
               data_flow[ii++] = open_arg->acn_data.acn_ver;
               cap_set_sccpadd(&open_arg->peer_add, data_flow + ii);
               ii += 28;
               cap_set_sccpadd(&open_arg->local_add, data_flow + ii);
               ii += 28;
               data_flow[ii++] = 1;     // optional point
               if((open_arg->param_flag & 0x04))        // has peer reference address
               {
                    data_flow[ii++] = 0x01;
                    len = open_arg->peerref_len;
                    data_flow[ii++] = len;
                    memcpy(data_flow + ii, open_arg->peer_reference, len);
                    ii += len;
               }
               if((open_arg->param_flag & 0x10))        // has local reference address
               {
                    data_flow[ii++] = 0x02;
                    len = open_arg->localref_len;
                    data_flow[ii++] = len;
                    memcpy(data_flow + ii, open_arg->local_reference, len);
                    ii += len;
               }
               if((open_arg->param_flag & 0x20))
               {
                    data_flow[ii++] = 0x03;
                    len = open_arg->specific_info.info_len;
                    data_flow[ii++] = len;
                    memcpy(data_flow + ii, open_arg->specific_info.info_data, len);
                    ii += len;
               }
               if(delimiter_flag == 1)
               {
                    data_flow[ii++] = 0x10;
                    data_flow[ii++] = 0x00;
               }
               data_flow[ii++] = 0x00;
          }
          else if(com_ptr->message_flag == MAP_RESPONSE || com_ptr->message_flag == MAP_CONFIRM)
          {
               open_res = (MapOpen_Res *) & com_ptr->dlg_list.open_res;
               data_flow[ii++] = open_res->result;
               data_flow[ii++] = 0x01;  // optional point
               if((open_res->param_flag & 0x01))        // has ACN
               {
                    data_flow[ii++] = 0x01;
                    data_flow[ii++] = 2;
                    data_flow[ii++] = open_res->acn_data.acn;
                    data_flow[ii++] = open_res->acn_data.acn_ver;
               }
               if((open_res->param_flag & 0x02))        // has specific info
               {
                    data_flow[ii++] = 0x02;
                    len = open_res->specific_info.info_len;
                    data_flow[ii++] = len;
                    memcpy(data_flow + ii, open_res->specific_info.info_data, len);
                    ii += len;
               }
               if((open_res->param_flag & 0x10))        // has refuse reason
               {
                    data_flow[ii++] = 0x03;
                    data_flow[ii++] = 1;
                    data_flow[ii++] = open_res->refuse_reason;
               }
               if((open_res->param_flag & 0x20))        // has provider error
               {
                    data_flow[ii++] = 0x04;
                    data_flow[ii++] = 1;
                    data_flow[ii++] = open_res->provider_error;
               }
               if(delimiter_flag == 1)
               {
                    data_flow[ii++] = 0x10;
                    data_flow[ii++] = 0x00;
               }
               data_flow[ii++] = 0x00;
          }
          break;
     case MAP_CLOSE:
          close_arg = (MapClose_Arg *) & com_ptr->dlg_list.close_arg;
          data_flow[ii++] = close_arg->release_method;
          data_flow[ii++] = 0x01;       // optional point
          if((close_arg->param_flag & 0x02))    // has specific info
          {
               data_flow[ii++] = 0x01;
               len = close_arg->specific_info.info_len;
               data_flow[ii++] = len;
               memcpy(data_flow + ii, close_arg->specific_info.info_data, len);
               ii += len;
          }
          data_flow[ii++] = 0x00;
          break;
     case MAP_U_ABORT:
          uabrt_arg = (MapUAbort_Arg *) & com_ptr->dlg_list.uabort_arg;
          data_flow[ii++] = uabrt_arg->user_reason;
          data_flow[ii++] = 0x01;       // optional point
          if((uabrt_arg->param_flag & 0x02))
          {
               data_flow[ii++] = 0x01;
               data_flow[ii++] = 1;
               data_flow[ii++] = uabrt_arg->diag_info;
          }
          if((uabrt_arg->param_flag & 0x04))    // has specific info
          {
               data_flow[ii++] = 0x02;
               len = uabrt_arg->specific_info.info_len;
               data_flow[ii++] = len;
               memcpy(data_flow + ii, uabrt_arg->specific_info.info_data, len);
               ii += len;
          }
          data_flow[ii++] = 0x00;
          break;
     case MAP_P_ABORT:
          data_flow[ii++] = com_ptr->dlg_list.pabort_arg.provider_reason;
          data_flow[ii++] = com_ptr->dlg_list.pabort_arg.source;
          break;
     case MAP_NOTICE:
          data_flow[ii++] = com_ptr->dlg_list.notice_arg.problem;
          break;
     default:
          break;
     }
     ii -= 2;
     data_flow[0] = ii >> 8;
     data_flow[1] = ii;
}

u8 cap_opr_ftos(struct MapOprData_struct *opr_ptr, u8 * data_flow)
{                               // transfer opration data flow to structure
     u32  aa;
     u32  len;
     u8   ii;
     u8   delimiter_flag = 0;

     len = (data_flow[0] << 8) + data_flow[1];
     if(len < 10)
          return 0;
     if(data_flow[2] != CAP_FLAG)
          return 0;
     opr_ptr->port_id = (data_flow[3] << 8) + data_flow[4];
     opr_ptr->dialogue_id = (data_flow[6] << 8) + data_flow[7];
     opr_ptr->message_type = data_flow[9];
     opr_ptr->message_flag = data_flow[10];
     opr_ptr->invoke_id = data_flow[11];
     opr_ptr->param_flag = 0x1f;
     opr_ptr->param_len = 0;
     aa = 13;
     for(ii = 0; ii < 2; ii++)
     {
          if(data_flow[aa] == 0x00)
               break;
          else if(data_flow[aa] == 0x01)        // parameter
          {
               len = (data_flow[aa + 1] << 8) + data_flow[aa + 2];
               if(len > MAX_MAPPOPR_LEN)
                    return 0;
               opr_ptr->param_len = len;
               memcpy(opr_ptr->param, &data_flow[aa + 3], len);
               aa += len + 3;
               opr_ptr->param_flag |= 0x20;
          }
          else if(data_flow[aa] == 0x02)        // user error
          {
               opr_ptr->user_error = data_flow[aa + 2];
               opr_ptr->param_flag |= 0x40;
               aa += 3;
          }
          else if(data_flow[aa] == 0x03)        // provider error
          {
               opr_ptr->provider_error = data_flow[aa + 2];
               opr_ptr->param_flag |= 0x80;
               aa += 3;
          }
          else if(data_flow[aa] == 0x10)
          {
               delimiter_flag = 1;
               aa += 2;
          }
     }
     if(delimiter_flag == 1)
          return 2;
     else
          return 1;
}

void cap_opr_stof(struct MapOprData_struct *opr_ptr, u8 * data_flow, u8 delimiter_flag)
{                               // transfer opration structure to data flow
     u32  ii;
     u32  len;

     data_flow[2] = CAP_FLAG;
     data_flow[3] = opr_ptr->port_id >> 8;
     data_flow[4] = opr_ptr->port_id;
     data_flow[6] = opr_ptr->dialogue_id >> 8;
     data_flow[7] = opr_ptr->dialogue_id;
     data_flow[9] = opr_ptr->message_type;
     data_flow[10] = opr_ptr->message_flag;
     data_flow[11] = opr_ptr->invoke_id;
     data_flow[12] = 1;
     ii = 13;
     if((opr_ptr->param_flag & 0x20))   // has component
     {
          data_flow[ii++] = 0x01;
          len = opr_ptr->param_len;
          data_flow[ii++] = len >> 8;
          data_flow[ii++] = len;
          memcpy(data_flow + ii, opr_ptr->param, len);
          ii += len;
     }
     if((opr_ptr->param_flag & 0x40))   // has user error
     {
          data_flow[ii++] = 0x02;
          data_flow[ii++] = 1;
          data_flow[ii++] = opr_ptr->user_error;
     }
     if((opr_ptr->param_flag & 0x80))   // has provider error
     {
          data_flow[ii++] = 0x03;
          data_flow[ii++] = 1;
          data_flow[ii++] = opr_ptr->provider_error;
     }
     if(delimiter_flag == 1)
     {
          data_flow[ii++] = 0x10;
          data_flow[ii++] = 0x00;
     }
     data_flow[ii++] = 0;
     ii -= 2;
     data_flow[0] = ii >> 8;
     data_flow[1] = ii;
}
