#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <winsock.h>
/*
#include <netinet/in.h>
#include <arpa/inet.h>
*/

#include "parser_s.h"


static void free_tent_list(struct tent *l); 
static void free_media_list(struct media *l);
static void free_string(char *c);
static void free_cache_data(struct cache *c);
static void free_connect_data(struct connect *c);
static void nextline(char **ad);
static char *storethisline(char *ad);

static int check_net_type(char *in, char *ip);

unsigned long ntptounix(unsigned long ntp);
unsigned long unixtontp(unsigned long time);

struct session *parse_SDP_buffer(char *ad)
{
  int ttl;
  char maddr[80];
  struct session *sess;
  static char namestr[MAXADSIZE];
  char creator[256], modtime[256], createtime[256], createaddr[256],
       in[256], ip[256];
  int i;


  sess=malloc(sizeof(struct session));
  memset(sess, 0, sizeof(struct session));
/* init_sess(sess); */

  if(ad==NULL) 
  {
      free_session(sess);
      return NULL;
  }

  if(strncmp(ad, "n=", 2)==0)
  {
      char aux_sd_addr[20];
      char aux_origin[20];

      sess->cache=malloc(sizeof(struct cache));
      memset(sess->cache, 0, sizeof(struct cache));
      sscanf(&ad[2], "%lu %lu %lu %s %u %u %s", &(sess->cache->origsrc),
             &(sess->cache->src), (long unsigned int*)&(sess->cache->t), 
             aux_sd_addr, &(sess->cache->sd_port), 
             &(sess->cache->ttl), aux_origin);
      sess->cache->sd_addr = strdup(aux_sd_addr);
      sess->cache->origin = strdup(aux_origin);
#ifdef DEBUG
      printf("Got a sdpfile entry\n");
#endif
  }
  else
  {
      free_session(sess);
      return NULL;
  }
  nextline(&ad);

  /* MANGEL Added for k= parsing */
  if(strncmp(ad, "k=", 2) == 0) 
  {
      char aux_key[MAXKEYLEN];

      /* No encryption keys */
      if(ad[2] == '\n')
          sess->key = NULL;
      else
      {
          sscanf(&ad[2], "%s", aux_key);
          sess->key = strdup(aux_key);
      }
  }
  else
  {
      free_session(sess);
      return NULL;
  }
  nextline(&ad);
  if (strncmp(ad, "v=0", 3)!=0)
  {
      free_session(sess);
      return NULL;
  }
  nextline(&ad);
  if(ad==NULL) 
  {
      free_session(sess);
      return NULL;
  }

  if(strncmp(ad, "o=", 2)!=0)
  {
      free_session(sess);
      return NULL;
  }
  sscanf(ad+2, "%s %s %s %s %s %s", creator, createtime, modtime, in, ip,
           createaddr);
  if (check_net_type(in,ip)<0)
  {
      free_session(sess);
      return NULL;
  }

  /* MANGEL Added more fields */
  sess->username = strdup(creator);
  sess->sessid = (unsigned int) atoi(createtime);
  sess->version = (unsigned int) atoi(modtime);
  sess->orighost.s_addr=inet_addr(createaddr);
  /* MANGEL end */

  /*Create a hash of originator data as advert ID*/
  sprintf(namestr, "%s%s%s", creator, createtime, createaddr);
  for(i = 0; i <= 4; i++)
    namestr[strlen(namestr)+i]='\0';
  for(i = 0; i <= (int)strlen(namestr)/4; i++)
  {
      unsigned int tmp;
/*      bcopy(&namestr[i*4],(char *)&tmp,4); */
      memcpy((void *)&tmp, (void *)&namestr[i*4], 4);
  
      sess->aid=(sess->aid<<1|sess->aid>>31)^tmp;
  }

  nextline(&ad);
  if(ad==NULL)
  {
      free_session(sess);
      return NULL;
  }

  if(strncmp(ad, "s=", 2)!=0)
  {
      free_session(sess);
      return NULL;
  }
  ad+=2;
  sess->sname=storethisline(ad);
  nextline(&ad);
  if(ad==NULL)
  {
      free_session(sess);
      return NULL;
  }

  if(strncmp(ad, "i=", 2)!=0)
  {
      free_session(sess);
      return NULL;
  }
  ad+=2;
  sess->desc=storethisline(ad);
  nextline(&ad);
  if(ad==NULL)
  {
      free_session(sess);
      return NULL;
  }
  if(strncmp(ad, "u=", 2)==0)
  {
      ad+=2;
      sess->uri=storethisline(ad);
      nextline(&ad);
  }

  while((strncmp(ad, "e=", 2)==0)||(strncmp(ad, "p=", 2)==0))
  {
      if(strncmp(ad, "e=", 2)==0)
      {
          struct lent *tmp;
          ad+=2;
          sess->no_of_emails++;
          tmp=sess->email;
          sess->email=malloc(sizeof(struct lent));
/* MANGEL Initializate memory allocated */
memset((void *)sess->email, 0, sizeof(struct lent));
          if(sess->no_of_emails == 1)
             sess->tail_email = sess->email;
          else
             tmp->prev = sess->email;
            
          sess->email->next=tmp;
          sess->email->data=storethisline(ad);
          nextline(&ad);
      }
      if(strncmp(ad, "p=", 2)==0)
      {
          struct lent *tmp;
          ad+=2;
          sess->no_of_phones++;
          tmp=sess->phone;
          sess->phone=malloc(sizeof(struct lent));
/* MANGEL Initializate memory allocated */
memset((void *)sess->phone, 0, sizeof(struct lent));
          if(sess->no_of_phones == 1)
             sess->tail_phone = sess->phone;
          else
             tmp->prev = sess->phone;

          sess->phone->next=tmp;
          sess->phone->data=storethisline(ad);
          nextline(&ad);
      }
  }
  /* Connection applies to all media */
  if(strncmp(ad, "c=", 2)==0)
  {
      char net[10],proto[10], *ttlstr;
      ad+=2;

      sscanf(ad, "%s %s %s", net, proto, maddr);
      sess->connect=malloc(sizeof(struct connect));
      memset((void *)sess->connect, 0, sizeof(struct connect));
      sess->connect->net = strdup(net);
      sess->connect->proto = strdup(proto);
      sess->connect->maddr = strdup(maddr);

      ttlstr=strchr(maddr, '/');
      *ttlstr='\0';
      ttlstr++;
      ttl=atoi(ttlstr);

      nextline(&ad);
  }
  while(strncmp(ad, "t=", 2)==0)
  {
      unsigned int t1, t2;
      struct tent *tmp;
      ad+=2;

      sscanf(ad, "%u %u", &t1, &t2);
      t1=ntptounix(t1);
      t2=ntptounix(t2);
      if((t1 < sess->starttime)||(sess->starttime==0))
      sess->starttime=t1;
      if(t2 > sess->endtime)
      sess->endtime=t2;

      sess->no_of_times++;
      tmp=sess->time;
      sess->time=malloc(sizeof(struct tent));
/* MANGEL Initializate memory allocated */
memset((void *)sess->time, 0, sizeof(struct tent));

      if(sess->no_of_times == 1)
         sess->tail_time = sess->time;
      else
         tmp->prev = sess->time;

      sess->time->starttime=t1;
      sess->time->endtime=t2;
      sess->time->next=tmp;
      nextline(&ad);
      if(strncmp(ad, "z=", 2)==0)
      {
          nextline(&ad);
      }
   }
   /* MANGEL added for sessions atributes */
   while(strncmp(ad, "a=", 2)==0)
   {
       struct lent *tmp2;
       ad+=2;
       sess->no_of_attrs++;
       tmp2=sess->attr;
       sess->attr=malloc(sizeof(struct lent));
/* MANGEL Initializate memory allocated */
memset((void *)sess->attr, 0, sizeof(struct lent));

      if(sess->no_of_attrs == 1)
         sess->tail_attr = sess->attr;
      else
         tmp2->prev = sess->attr;

       sess->attr->next=tmp2;
       sess->attr->data=storethisline(ad);
       nextline(&ad);
   }
   /* End MANGEL */
   while(strncmp(ad, "m=", 2)==0)
   {
      char media[80], fmt[80], proto[80], mmaddr[80];
      unsigned int port;
      struct media *tmp;
      int mttl=-1;
      ad+=2;
      sess->no_of_media++;
      tmp=sess->media;
      sess->media=malloc(sizeof(struct media));
/* MANGEL Initializate memory allocated */
memset((void *)sess->media, 0, sizeof(struct media));

      if(sess->no_of_media == 1)
         sess->tail_media = sess->media;
      else
         tmp->prev = sess->media;

      sess->media->no_of_attrs=0;
      sess->media->next=tmp;
      sscanf(ad, "%s %u %s %s", media, &port, proto, fmt);
      nextline(&ad);
      if(strncmp(ad, "c=", 2)==0)
      {
         char net[10], prot[10], *ttlstr;
         ad+=2;
         sscanf(ad, "%s %s %s", net, prot, mmaddr);
         ttlstr=strchr(mmaddr, '/');
         *ttlstr='\0';
         ttlstr++;
         mttl=atoi(ttlstr);
         nextline(&ad);
      }
      while(strncmp(ad, "a=", 2)==0)
      {
          struct lent *tmp2;
          ad+=2;
          sess->media->no_of_attrs++;
          tmp2=sess->media->attr;
          /* 
           * MANGEL modified
           * sess->media->attr=malloc(sizeof(struct media));
           *
           */
          sess->media->attr=malloc(sizeof(struct lent));
/* MANGEL Initializate memory allocated */
memset((void *)sess->media->attr, 0, sizeof(struct lent));


          if(sess->media->no_of_attrs == 1)
             sess->media->tail_attr = sess->media->attr;
          else
             tmp2->prev = sess->media->attr;

          sess->media->attr->next=tmp2;
          sess->media->attr->data=storethisline(ad);
          nextline(&ad);
     }
     if (mttl==-1)
     {
         sess->media->addr.s_addr=inet_addr(maddr);
         sess->media->ttl=ttl;
     }
     else
     {
         sess->media->addr.s_addr=inet_addr(mmaddr);
         sess->media->ttl=mttl;
     }
     sess->media->media=storethisline(media);
     sess->media->port=port;
     sess->media->proto=storethisline(proto);
     sess->media->fmt=storethisline(fmt);
  }
  return sess;
}


static void nextline(char **ad) 
{ 
  *ad=strchr(*ad, '\n'); 
  if(*ad==NULL) 
      return;
  (*ad)++;
}

static char *storethisline(char *ad)
{
  char *nl, *line;
  int len;

  nl=strchr(ad, '\n');
  if(nl!=NULL)
    len=(long)nl-(long)ad;
  else
    len=strlen(ad);
  line=malloc(len+1);
  strncpy(line, ad, len);
  line[len]='\0';
  return(line);
}

int extract_ttl(char *addrstr)
{
  char *ttlstr;
  ttlstr=strchr(addrstr, '/');
  if (ttlstr==NULL) return 0;
  *ttlstr='\0';
  return(atoi(ttlstr+1));
}

void init_sess(struct session *sess)
{
  sess->sname=NULL;
  sess->desc=NULL;
  sess->username=NULL;
  sess->orighost.s_addr=0;
  sess->uri=NULL;
  sess->no_of_emails=0;
  sess->email=NULL;
  sess->no_of_phones=0;
  sess->phone=NULL;
  sess->no_of_times=0;
  sess->time=NULL;
  sess->no_of_attrs=0;
  sess->attr=NULL;
  sess->no_of_media=0;
  sess->media=NULL;

  /* MANGEL added */
  sess->aid=0;
  sess->sessid=0;
  sess->version=0;
  sess->starttime=0;
  sess->endtime=0;
  sess->cache=NULL;
  sess->key = NULL;
  sess->connect = NULL;
}

void free_session(struct session *sess)
{
  if(sess != NULL)
  {
     free_string(sess->username);
     free_string(sess->sname);
     free_string(sess->desc);
     free_string(sess->uri);
     free_lent_list(sess->email);
     free_lent_list(sess->phone);
     free_lent_list(sess->attr);

     /* MANGEL Added */
     free_tent_list(sess->time);
     free_media_list(sess->media);
     free_cache_data(sess->cache);
     free_connect_data(sess->connect);
     free_string(sess->key);
     free(sess);
  }
}

void free_lent_list(struct lent *l)
{
  struct lent *tmp1, *tmp2;
  tmp1=l;
  while(tmp1!=NULL)
  {
      tmp2=tmp1->next;
      free_string(tmp1->data);
      free(tmp1);
      tmp1=tmp2;
  }
}

static void free_tent_list(struct tent *l)
{
  struct tent *tmp1, *tmp2;
  tmp1=l;
  while(tmp1!=NULL)
  {
      tmp2=tmp1->next;
      free(tmp1);
      tmp1=tmp2;
  }
}

static void free_media_list(struct media *l)
{
  struct media *tmp1, *tmp2;
  tmp1=l;
  while(tmp1!=NULL)
  {
      tmp2=tmp1->next;
      free_string(tmp1->media);
      free_string(tmp1->proto);
      free_string(tmp1->fmt);
      free_lent_list(tmp1->attr);
      free(tmp1);
      tmp1=tmp2;
  }
}

static void free_cache_data(struct cache *c)
{
   if(c != NULL)
   {
      free_string(c->sd_addr);
      free_string(c->origin);
      free(c);
   }
}

static void free_connect_data(struct connect *c)
{
   if(c != NULL)
   {
      free_string(c->net);
      free_string(c->proto);
      free_string(c->maddr);
      free(c);
   }
}

static void free_string(char *str)
{
  if(str!=NULL)
    free(str);
}


char *make_session_description(struct session *sess)
{
  char sdp[2048], tmp[2048], *rtn;
  struct media *media;
  struct lent *email;
  struct lent *phone;
  struct lent *attr;
  struct tent *time;
  int i, m;

  memset(sdp, 0, sizeof(sdp));
  memset(tmp, 0, sizeof(tmp));

  sprintf(sdp, "n=%lu %lu %lu %s %u %u %s\n", sess->cache->origsrc,
          sess->cache->src, (long unsigned int)sess->cache->t, 
          sess->cache->sd_addr, sess->cache->sd_port,
          sess->cache->ttl, sess->cache->origin);

  if(sess->key != NULL)
     sprintf(tmp, "k=%s\n",  sess->key);
  else
     sprintf(tmp, "k=\n");
  strcat(sdp, tmp);

  sprintf(tmp, "v=0\n");
  strcat(sdp, tmp);

  sprintf(tmp, "o=%s %u %u IN IP4 %s\n", sess->username,
  sess->sessid, sess->version, inet_ntoa(sess->orighost));
  strcat(sdp, tmp);

  sprintf(tmp, "s=%s\n", sess->sname);
  strcat(sdp, tmp);

  sprintf(tmp, "i=%s\n", sess->desc);
  strcat(sdp, tmp);
  if(sess->uri!=NULL)
  {
      sprintf(tmp, "u=%s\n", sess->uri);
      strcat(sdp, tmp);
  }
  email=sess->tail_email;
  for(i=0;i<sess->no_of_emails; i++)
  {
      sprintf(tmp, "e=%s\n", email->data);
      strcat(sdp, tmp);
      email=email->prev;
  }
  phone=sess->tail_phone;
  for(i=0;i<sess->no_of_phones; i++)
  {
      sprintf(tmp, "p=%s\n", phone->data);
      strcat(sdp, tmp);
      phone=phone->prev;
  }
  if(sess->connect != NULL)
  {
     sprintf(tmp, "c=%s %s %s\n", sess->connect->net, sess->connect->proto, sess->connect->maddr);
     strcat(sdp, tmp);
  }
  time=sess->tail_time;
  for(i=0;i<sess->no_of_times; i++)
  {
      sprintf(tmp, "t=%u %u\n", (unsigned int)unixtontp(time->starttime), (unsigned int)unixtontp(time->endtime));
      strcat(sdp, tmp);
      time=time->prev;
  }
  attr=sess->tail_attr;
  for(i=0;i<sess->no_of_attrs;i++)
  {
      sprintf(tmp, "a=%s\n", attr->data);
      strcat(sdp, tmp);
      attr=attr->prev;
  }
  media=sess->tail_media;
  for(m=0;m<sess->no_of_media;m++)
  {
      sprintf(tmp, "m=%s %u %s %s\n", media->media, media->port,
      media->proto, media->fmt);
      strcat(sdp, tmp);

      sprintf(tmp, "c=IN IP4 %s/%u\n", inet_ntoa(media->addr), media->ttl);
      strcat(sdp, tmp);
      attr=media->tail_attr;
      for(i=0;i<media->no_of_attrs;i++)
      {
         sprintf(tmp, "a=%s\n", attr->data);
         strcat(sdp, tmp);
         attr=attr->prev;
      }
      media=media->prev;
  }
  rtn=malloc(strlen(sdp)+1);
  strcpy(rtn, sdp);
  return(rtn);
}

char *
set_interface_get_cmd(struct session *sess)
{
  char sdp[2048], tmp[2048], *rtn;
  struct lent *email;
  struct lent *phone;
  int i;
 
  memset(sdp, 0, sizeof(sdp));
  memset(tmp, 0, sizeof(tmp));

  sprintf(tmp, "s=%s&&", sess->sname);
  strcat(sdp, tmp);
 
  sprintf(tmp, "i=%s&&", sess->desc);
  strcat(sdp, tmp);
 
/* Excluded sess->version
  sprintf(tmp, "o=%s&%u&%u&IN&IP4&%s&&", sess->username,
  sess->sessid, sess->version, inet_ntoa(sess->orighost));
*/
  sprintf(tmp, "o=%s&%u&IN&IP4&%s&&", sess->username,
  sess->sessid, inet_ntoa(sess->orighost));
  strcat(sdp, tmp);

  phone=sess->tail_phone;
  for(i=0;i<sess->no_of_phones; i++)
  {
      if(i == 0)
         sprintf(tmp, "p=%s", phone->data);
      else
         sprintf(tmp, "&%s", phone->data);
      strcat(sdp, tmp);
      phone=phone->prev;
  }
  strcat(sdp, "&&");

  email=sess->tail_email;
  for(i=0;i<sess->no_of_emails; i++)
  {
      if(i == 0)
         sprintf(tmp, "e=%s", email->data);
      else
         sprintf(tmp, "&%s", email->data);
      strcat(sdp, tmp);
      email=email->prev;
  }
  strcat(sdp, "&&");

  sprintf(tmp, "t=0&%ld&&", duration(sess));
  strcat(sdp, tmp);

  rtn=malloc(strlen(sdp)+1);
  strcpy(rtn, sdp);
  return(rtn);
}


static int 
check_net_type(char *in, char *ip)
{
  if (strncmp(in, "IN", 2)!=0)
  {
      fprintf(stderr, "sdr: expected network type IN, got %s\n",
      in);
      return -1;
  }
  if (strncmp(ip, "IP4", 3)!=0)
  {
      fprintf(stderr, "sdr: expected address type IP4, got %s\n", ip);
      return -1;
  }
  return 0;
}

unsigned long 
ntptounix(unsigned long ntp)
{
  if(ntp==0) return(ntp);
  return(ntp-0x83aa7e80);
}
 
unsigned long 
unixtontp(unsigned long time)
{
  if(time==0) return(time);
  return(time+0x83aa7e80);
}

long
duration(struct session *sess)
{
    struct tent *tmp;

    long duration = 0;

    tmp = sess->time;

    while(tmp != NULL)
    {
        duration += (tmp->endtime - tmp->starttime);
        tmp = tmp->next;
    } 
#ifdef DEBUG
printf("Videoconference duration %ld seconds \n", duration);
#endif
    return duration;
}

