/***************************************************************************
 *   copyright           : (C) 2004 by Hendrik Sattler                     *
 *   mail                : post@hendrik-sattler.de                         *
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

//own headers
#include "smsudh.h"

#include <helper.h>
#include <intincl.h>
#include <gtincl.h>

//standard headers
#include <stdio.h>
#include <string.h>

struct sms_pdu_ud_header*
sms_udh_multipart16_get (uint16_t seqnum,
			 uint8_t index,
			 uint8_t total)
{
  struct sms_pdu_ud_header* retval;
  
  retval = mem_alloc(sizeof(*retval),0);
  sms_pdu_ud_header_init(retval);
  retval->type = 0x08;
  retval->len = 4;
  retval->data[0] = (seqnum >>8)&0xFF;
  retval->data[1] = seqnum&0xFF;
  retval->data[2] = total;
  retval->data[3] = index;
  return retval;
}

static
void sms_udh_indication_print (FILE* fp, uint8_t* data) {
  char* type = NULL;

  fprintf(fp,"%s:",_("Indication"));
  switch (data[0]&0x4F) {
  case 0: type = _("voicemail"); break;
  case 1: type = _("fax"); break;
  case 2: type = _("e-mail"); break;
  case 3: type = _("misc."); break;
  }
  fprintf(fp,ngettext("%d %s message waiting","%d %s messages waiting",data[1]),data[1],type);
  
}

static
void sms_udh_wcmpdu_print (FILE* fp, uint8_t* data) {
  data = data;
  fprintf(fp,"%s\n",_("Wireless control message protocol data unit"));
}

static
void sms_udh_smstk_print (FILE* fp, uint8_t* data) {
  data = data;
  fprintf(fp,_("SIM toolkit security header"));
}

static
void sms_udh_multipart8_print (FILE* fp, uint8_t* data) {
  fprintf(fp,_("Multipart message: sequence number = %d, part %d of %d"),
	  data[0],data[2],data[1]);
}

static
void sms_udh_multipart16_print (FILE* fp, uint8_t* data) {
  fprintf(fp,_("Multipart message: sequence number = %d, part %d of %d"),
	  (data[0]<<8)+data[1],data[3],data[2]);
}

static
void sms_udh_application8_print (FILE* fp, uint8_t* data) {
  fprintf(fp,_("Application port: destination port = %d, originator port = %d"),
	  data[0],data[1]);
}

static
void sms_udh_application16_print (FILE* fp, uint8_t* data) {
  fprintf(fp,_("Application port: destination port = %d, originator port = %d"),
	  (data[0]<<8)+data[1],((uint16_t*)data)[1]);
}

static
void sms_udh_source_indicator_print (FILE* fp, uint8_t* data) {
  fprintf(fp,"%s ",_("The following headers were created by"));
  switch (data[0]) {
  case 1:
    fprintf(fp,"%s",_("original sender"));
    break;
  case 2:
    fprintf(fp,"%s",_("original receiver"));
    break;
  case 3:
    fprintf(fp,"%s","SMSC");
    break;
  default:
    fprintf(fp,_("(unknown value %d)"),data[0]);
    break;
  }
}

void sms_udh_print_header (FILE* fp, struct sms_pdu_ud_header* header) {
  int k = 0;

  while (sms_pdu_ud_header_defs[k].type != -1 &&
	 sms_pdu_ud_header_defs[k].type != header->type) {
    ++k;
  }

  if (sms_pdu_ud_header_defs[k].type != -1 &&
      sms_pdu_ud_header_defs[k].type == header->type) {
    fprintf(fp,"\t");
    if (sms_pdu_ud_header_defs[k].print_func != NULL) {
      sms_pdu_ud_header_defs[k].print_func(fp,header->data);
    } else {
      fprintf(fp,_("Ignored header type 0x%02x"),header->type);
    }
    fprintf(fp,"\n");
  }
}

void sms_udh_print (FILE* fp, struct sms_pdu_ud* plist, uint8_t parts) {
  struct sms_pdu_ud_header** udh = NULL;
  int i = 0;
  int h = 0;

  if (fp == NULL || plist == NULL || parts == 0) return;

  //look for at least one header
  for (i = 0; i < parts; ++i)
    if (plist[i].header != NULL &&
	plist[i].header[0] != NULL) {
      h = 1;
      break;
    }

  if (h) fprintf(fp,"%s:\n",_("Message header"));
  else return;

  for (h = 0; h < parts; ++h) {
    udh = plist[h].header;
    if (udh != NULL)
      for (i=0; udh[i] != NULL; ++i)
	sms_udh_print_header(fp, udh[i]);
  }
}

void sms_udh_fill (struct sms_pdu* sms, uint8_t* data) {
  uint8_t udhsize = 0;
  uint8_t offset = 0;
  unsigned int i = 0;
  unsigned int k;
  struct sms_pdu_ud_header** header;

  if (sms == NULL) {
    return;
  }
  
  if (sms->ud == NULL) {
    sms->ud =  mem_alloc(sizeof(*(sms->ud)),0);
    sms_pdu_ud_init(sms->ud);
  }

  if (sms->options.udh_present) {
    //extract the user data header data
    udhsize = data[0];
    data += 1;
    //split the udh into its parts
    sms->ud[0].header = mem_alloc(((udhsize/2)+1)*sizeof(*sms->ud[0].header),0);
    header = sms->ud[0].header;
    header[i] = NULL;
    while (offset+2 <= udhsize) {
      header[i] = mem_alloc(sizeof(**header),0);
      sms_pdu_ud_header_init(header[i]);
      //read fields that are mandatory for each user data header
      header[i]->type = data[offset];
      header[i]->len = data[offset+1];
      offset += 2;
      //find the udh type in the list of known headers
      k = 0;
      while (sms_pdu_ud_header_defs[k].type != -1 &&
	     sms_pdu_ud_header_defs[k].type != header[i]->type)
	++k;

      //check that everything is correct with the udh data
      if (offset+(header[i]->len) <= udhsize &&
	  sms_pdu_ud_header_defs[k].type != -1 &&
	  sms_pdu_ud_header_defs[k].minlen <= header[i]->len &&
	  header[i]->len <= sms_pdu_ud_header_defs[k].maxlen)
      {
	memcpy(header[i]->data,data+offset,header[i]->len);
	header[i+1] = NULL;
	offset += header[i]->len;
      } else {
	//bogus or unknown header, delete it and stop processing the headers
	mem_realloc(header[i],0);
	header[i] = NULL;
	break;
      }
      ++i;
    }
  }
  sms_udh_multipart_mark(sms);
}

void sms_udh_multipart_mark (struct sms_pdu* data) {
  int i = 0;
  int k;
  struct sms_pdu_ud_header** header = NULL;

  if (data == NULL) {
    return;
  }

  if (data->ud[0].header != NULL) {
    //save the headers
    header = data->ud[0].header;
    for (i=0; header[i] != NULL; ++i) {
      if (header[i]->type == 0x00 ||
	  header[i]->type == 0x08) {
	if (header[i]->type == 0x00) {
	  data->multipart_id = header[i]->data[0];
	  data->parts = header[i]->data[1];
	  data->partnum = header[i]->data[2]-1;
	} else if (header[i]->type == 0x08) {
	  data->multipart_id = *((uint16_t*)header[i]->data);
	  data->parts = header[i]->data[2];
	  data->partnum = header[i]->data[3]-1;
	}
	break;
      }
    }
  }
  data->ud = mem_realloc(data->ud,data->parts*sizeof(*(data->ud)));
  for (k=0; k < data->parts; ++k) {
    sms_pdu_ud_init(&data->ud[k]);
  }
  //restore the headers to their correct position
  data->ud[data->partnum].header = header;
}

struct sms_pdu_ud_header_def sms_pdu_ud_header_defs[] = {
  {0x00,  3,  3, sms_udh_multipart8_print}, //multipart message (8bit sequence)
  {0x01,  2,  2, sms_udh_indication_print}, //special sms indication (like in DCS)
  {0x04,  2,  2, sms_udh_application8_print}, //application port addressing 8bit address
  {0x05,  4,  4, sms_udh_application16_print}, //application port addressing 16bit address
  {0x06,  1,  1, NULL}, //SMSC control parameters
  {0x07,  1,  1, sms_udh_source_indicator_print}, //UDH source indicator
  {0x08,  4,  4, sms_udh_multipart16_print}, //multipart message (16bit sequence)
  {0x09,  1,137, sms_udh_wcmpdu_print}, //wireless control message protocol

  {0x0a,  4,  4, NULL}, //EMS: text formatting
  {0x0b,  2,  2, NULL}, //EMS: predefined sound
  {0x0c,  2,129, NULL}, //EMS: user defined sound
  {0x0d,  2,  2, NULL}, //EMS: predefined animation
  {0x0e,129,129, NULL}, //EMS: large animation
  {0x0f, 33, 33, NULL}, //EMS: small animation
  {0x10,129,129, NULL}, //EMS: large picture
  {0x11, 33, 33, NULL}, //EMS: small picture
  {0x12,  4,131, NULL}, //EMS: variable picture
  {0x13,  1,  1, NULL}, //EMS: user prompt indicator
  {0x14,  1,137, NULL}, //EMS: extended object
  {0x15,  3,  3, NULL}, //EMS: reused extended object
  {0x16,  1,137, NULL}, //EMS: compression control
  {0x17,  2,  2, NULL}, //EMS: object distribution indicator
  {0x18,  2,137, NULL}, //EMS: standard WVG object
  {0x19,  2,137, NULL}, //EMS: character size WVG object
  {0x1a,  0,  0, NULL}, //EMS: extended object data request command

  {0x20,  1,  1, NULL}, //RFC822 email header
  {0x21,  4,  4, NULL}, //hyperlink format element
  {0x22, 22, 22, NULL}, //reply address element

  {0x70,  0,  0, sms_udh_smstk_print}, //SIM toolkit security header
  {0x71,  0,  0, sms_udh_smstk_print}, //SIM toolkit security header
  {0x72,  0,  0, sms_udh_smstk_print}, //SIM toolkit security header
  {0x73,  0,  0, sms_udh_smstk_print}, //SIM toolkit security header
  {0x74,  0,  0, sms_udh_smstk_print}, //SIM toolkit security header
  {0x75,  0,  0, sms_udh_smstk_print}, //SIM toolkit security header
  {0x76,  0,  0, sms_udh_smstk_print}, //SIM toolkit security header
  {0x77,  0,  0, sms_udh_smstk_print}, //SIM toolkit security header
  {0x78,  0,  0, sms_udh_smstk_print}, //SIM toolkit security header
  {0x79,  0,  0, sms_udh_smstk_print}, //SIM toolkit security header
  {0x7a,  0,  0, sms_udh_smstk_print}, //SIM toolkit security header
  {0x7b,  0,  0, sms_udh_smstk_print}, //SIM toolkit security header
  {0x7c,  0,  0, sms_udh_smstk_print}, //SIM toolkit security header
  {0x7d,  0,  0, sms_udh_smstk_print}, //SIM toolkit security header
  {0x7e,  0,  0, sms_udh_smstk_print}, //SIM toolkit security header
  {0x7f,  0,  0, sms_udh_smstk_print}, //SIM toolkit security header
  
  {-1,0,0, NULL} //list end element (do not remove!)
};
