/*

Copyright 1993, 1994, Cornell University

Cornell hereby grants permission to use, copy, modify, and distribute this program for any purpose 
and without fee, provided that these copyright and permission notices appear on all copies and 
supporting documentation, the name of Cornell not be used in advertising or publicity pertaining 
to distribution of the program without specific prior permission, notice be given in supporting 
documentation that copying and distribution is by permission of Cornell.  CORNELL MAKES NO 
REPRESENTATIONS OR WARRANTEES, EXPRESS OR IMPLIED.  By way of example, but not limitation, 
CORNELL MAKES NO REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR 
PURPOSE OR THAT THE USE OF THIS SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS, 
TRADEMARKS, OR OTHER RIGHTS.  Cornell shall not be held liable for any liability with respect to 
any claim by the user or any other party arising from use of the program.

This material is partially based on work sponsored by the National Science Foundation under Cooperative 
Agreement No. NCR-9318337.  The government has certain rights in this material.

*/

#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>

#ifndef LINUX
#include <sys/socketvar.h>
#endif

#include <sys/time.h>
#include <netinet/in.h>

#include "reflect.h"
#include "refmon.h"
#include "rtp.h"

#include "globals.h"


void mbone_pkt(msg,msglen,csock,type)
   unsigned char *msg;
   int msglen;
   struct sockaddr_in csock;
   short type;
{

    struct CtrlMsgHdr   *cmhptr;
    struct IDMsgHdr     *idh;
    vat_client          *mcltptr,*mtmp;
    client              *ctmp;
    vat_hdr_t           *vat_hdr;
    VideoPacketHeader   *vtmp;
    unsigned char       nsid;
    unsigned long       *ulptr,ul;
    unsigned short      *usptr;
    SiteId              *siptr;
    char                *tmp,buf[50];
    int                 i;
    unsigned char       *cptr,*cptr1;
    struct sockaddr_in  tmp1;
    struct in_addr      in;
    client              *nvcptr;

    switch (type)
    {

       case VAT_CNTL:
       case MAVEN_CNTL:

          /* check what kind of vat/maven cntrl message it is, act accordingly */ 

          cmhptr = (struct CtrlMsgHdr *) msg;

          if ((vat_confid != 0) && (ntohs(cmhptr->confid) != vat_confid)) 
             return;

          switch (cmhptr->type)
          {
             case CTRL_TYPE_ID:
#ifdef DEBUG
                if (debug)
                   printf("type = CTRL_TYPE_ID\n");
#endif        

                if ((mcltptr = find_vat_client(csock.sin_addr.s_addr)) == NULL) 
                {
                   if ((mcltptr = new_vat_client(csock.sin_addr.s_addr)) == NULL)
                      return;
        
                   strncpy(mcltptr->mvn_name,cmhptr->idmsg,MAX_MAVEN_NAME_LEN);

                   mcltptr->mvn_flags = VAT_CLIENT;

                   dolog("New Maven client %s at %s\n", mcltptr->mvn_name,inet_ntoa(csock.sin_addr));

                   if (type == VAT_CNTL)
                      mcltptr->mvn_recv_type = MCAST;
                   else
                      mcltptr->mvn_recv_type = UCAST;
            
                   mcltptr->mvn_idlist = NULL;
      
                   send_vat_idlist();
                }
                else 
                   if (strncmp(mcltptr->mvn_name,cmhptr->idmsg,MAX_MAVEN_NAME_LEN)) 
                   {
                      strncpy(mcltptr->mvn_name,cmhptr->idmsg,MAX_MAVEN_NAME_LEN);
#ifdef DEBUG
                      if (debug)
                         printf("Maven client at %s changing name to %s\n", inet_ntoa(csock.sin_addr),mcltptr->mvn_name);
#endif
                   }

                mcltptr->mvn_rtimer = 0;
                break;

             case CTRL_TYPE_DONE:
#ifdef DEBUG
                if (debug)
                   printf("type = CTRL_TYPE_DONE\n");
#endif
                if ((mcltptr = find_vat_client(csock.sin_addr.s_addr)) == NULL)
                   return;

                delete_vat_client(mcltptr);
                return;

             case CTRL_TYPE_IDLIST:
#ifdef DEBUG
                if (debug)
                   printf("type = CTRL_TYPE_IDLIST\n");
#endif
                if ((mcltptr = find_vat_client(csock.sin_addr.s_addr)) == NULL) 
                {
                   if ((mcltptr = new_vat_client(csock.sin_addr.s_addr)) == NULL)
                      return;
          
                   /* 
                      this is the first idlist message from this mixer, so set the speaker
                      list to NULL because we haven't gotten any actual audio data yet
                   */

                   dolog("New Maven client (VAT MIXER) at %s\n", inet_ntoa(csock.sin_addr));
                   mcltptr->mvn_flags = VAT_MIXER;

                   if (type == VAT_CNTL)
                      mcltptr->mvn_recv_type = MCAST;
                   else
                      mcltptr->mvn_recv_type = UCAST;

                   if ((mcltptr->mvn_idlist = (struct IDMsgHdr *) malloc(msglen)) == NULL) 
                   {
                      dolog("OUT OF MEMORY...ABORTING\n");
                      exit(-1);
                   }

                   bcopy((char *) msg, (char *) mcltptr->mvn_idlist, msglen);
                   mcltptr->mvn_idlist_len = msglen;
                   send_vat_idlist();
                }
                else 
                {
                   /* 
                      make this message the new idlist for this maven client, update the
                      client's vms_head list, and then free up the old space.  This
                      must be done every time because even if the idlist contains the
                      same information (which may or may not be true), it may contain
                      it in a different order.  Better safe than sorry. 
                   */
             
                   mcltptr->mvn_idlist_len = msglen;

                   /* 
                      idh is a temp pointer.  It points to where the new idlist is 
                      put into memory.  Eventually the space pointed to by the old
                      mcltptr->idlist is freed and the pointer updated to point to
                      the new list 
                   */

                   if ((idh = (struct IDMsgHdr *) malloc(msglen)) == NULL) 
                   {
                      dolog("OUT OF MEMORY...ABORTING\n");
                      exit(-1);
                   }
          
                   bcopy((char *) msg, (char *) idh, msglen);
                   free((void *) mcltptr->mvn_idlist);
                   mcltptr->mvn_idlist = idh;
                }
                return;
   
             default:
                dolog("unrecognized vat ctrl type %d \n",cmhptr->type);
          }
  
          mcltptr->mvn_rtimer = 0;
          return;
          
       case VAT:
       case MAVEN:

          if ((mcltptr = find_vat_client(csock.sin_addr.s_addr)) == NULL)
             break;

          mcltptr->mvn_rtimer = 0;

          /*
             if this vat/maven packet doesn't have the right confid, then we can
             just drop it.  Make sure to reset msg first! 
          */

          vat_hdr = (vat_hdr_t *) msg;
          
          if (ntohs(vat_hdr->confid) != vat_confid)
          {
             dolog("Wrong vat conf_id.  Dropping packet.\n");
             break;          
          }
          
          if ((vat_hdr->nsid & NSID_MASK) == 0) 
          {
             /* 
                this code puts a 4-byte speaker id in after the vat header in
                order for the receiver to be able to distinguish it from the
                other streams coming from the reflector 
             */
        
             tmp = (char *) msg;
             tmp -= 4;
        
             bcopy((char *)msg, tmp, sizeof(vat_hdr_t));
        
             msg += (sizeof(vat_hdr_t) - 4);
        
             ulptr = (unsigned long *) msg;
             *ulptr = mcltptr->mvn_addr.addr;
        
             msg = (unsigned char *) tmp;
             
             vat_hdr = (vat_hdr_t *) msg;
        
             /* 
                set the number of speaker id's to 1 
             */

             vat_hdr->nsid = (vat_hdr->nsid & ~NSID_MASK) | 1;
        
             /* 
                increase the length of the packet by four bytes because we
                inserted one four-byte site_id 
             */
        
             msglen += 4;
          }
          else 
          {
             /* 
                Ok, we've received an audio packet with a non-zero nsid field.  So
                we need to check whether this is a speaker (or speakers...note that
                this may be coming from more than one speaker at once if it is a
                true vat_mixer) who is currently on the vms list of this maven client.  
                If not, add him to the list and send an initial open continue packet.  
                If so, get on with the business of sending the packet out. 
             */ 
        
             nsid = (vat_hdr->nsid & NSID_MASK);

             cptr = (unsigned char *) msg + sizeof(vat_hdr_t);

             for (i = 0; i < nsid; i++) 
             {
                ulptr = (unsigned long *) cptr;
                sprintf(buf,"%d.%d.%d.%d\0",
                        (unsigned short)*cptr,(unsigned short)*(cptr+1),(unsigned short)*(cptr+2),(unsigned short)*(cptr+3));
                ul = inet_addr(buf);

                if ((siptr = find_site_idlist(mcltptr->mvn_idlist, *ulptr)) != NULL)
                {
                   /* indicate that this person is a current speaker */
                   if ((mtmp = find_vat_client(ul)) == NULL)
                   {
                      if ((mtmp = new_vat_client(ul)) == NULL)
                         return;

                      mtmp->mvn_flags = VAT_MIXER_CLIENT;
                      mtmp->mvn_pptr = mcltptr;
                      strncpy(mtmp->mvn_name,siptr->id_string,MAX_MAVEN_NAME_LEN);
                      dolog("New Maven client (VAT MIXER CLIENT) at %s\n", inet_ntoa(csock.sin_addr));
                   }

                   if ((mtmp->mvn_spoke == 0) || ((tick_cnt - mtmp->mvn_spoke) >= VAT_SPEAKER_INTERVAL))
		      send_vat_ocp(mtmp);

                   mtmp->mvn_spoke = tick_cnt;
                   mtmp->mvn_rtimer = 0;
                }
                else
                {
                   dolog("Audio packet received with unknown speaker id.  Dropping packet.\n");
                   break;
                }
                cptr += 4;
             }
          }

          mcltptr->mvn_rtimer = 0;
          if ((mcltptr->mvn_spoke == 0) || ((tick_cnt - mcltptr->mvn_spoke) >= VAT_SPEAKER_INTERVAL))
	     send_vat_ocp(mcltptr);
          mcltptr->mvn_spoke = tick_cnt;
          mcltptr->mvn_seq++;

          if (mcltptr->mvn_talker == 0)
             dolog("maven at %s is speaking\n",inet_ntoa(csock.sin_addr));

          if (mcltptr->mvn_talker++ > 50)
             mcltptr->mvn_talker = 0;

#ifdef MULTI
          if (((vat_out_mcast_sock) || (vat_inout_mcast_sock)) && (type != VAT)) 
          {
             tmp1.sin_family = AF_INET;
             tmp1.sin_addr.s_addr = htonl(vat_out_mcast.sin_addr.s_addr);
             tmp1.sin_port = htons(vat_port);
#ifdef DEBUG
             if (debug)
                printf("reflecting vat unicast pkt onto vat mcast address.\n");
#endif
             if (vat_inout_mcast_sock)
	     {
                if (sendto(vat_out_mcast_sock,msg,msglen,0,&tmp1,sizeof(struct sockaddr_in)) != msglen)
                   dolog("mcast sendto error");
             }
	     else
	     {
                if (sendto(vat_inout_mcast_sock,msg,msglen,0,&tmp1,sizeof(struct sockaddr_in)) != msglen)
                   dolog("mcast sendto error");
             }
          }
#endif

          mtmp = mhead;
          while (mtmp != NULL) 
          {
             if ((mtmp != mcltptr) || (self_reflect == 1)) 
             {
                if ((mtmp->mvn_recv_type == UCAST) && ((mtmp->mvn_flags & VAT_MIXER_CLIENT) == 0)) 
                {
                   tmp1.sin_family = AF_INET;
                   tmp1.sin_port = htons(mtmp->mvn_addr.port);
                   tmp1.sin_addr.s_addr = htonl(mtmp->mvn_addr.addr);
#ifdef DEBUG
                   if (debug)
                      printf("reflecting MAVEN pkt to ucast maven %s \n", inet_ntoa(tmp1.sin_addr));
#endif
                   if (sendto(maven_sock,msg,msglen,0,&tmp1,sizeof(struct sockaddr_in)) != msglen)
                      dolog("maven_cntl sendto error\n");
                }
             }
             mtmp = mtmp->mvn_nptr;
          }
          
          vtmp = (VideoPacketHeader *) (((unsigned char *) msg) - HEADERLEN);

          vtmp->routing.dest.family = htons(kGroup);           
          vtmp->routing.dest.port = htons(VID_PORT);           
          vtmp->routing.src.family = htons(kGroup);          
          vtmp->routing.src.port  = htons(VID_PORT);          

          cptr = (unsigned char *) &vtmp->routing.src.addr;
          cptr1 = (unsigned char *) &csock.sin_addr;
          *cptr++ = *cptr1++;
          *cptr++ = *cptr1++;
          *cptr++ = *cptr1++;
          *cptr++ = *cptr1++;

          cptr = (unsigned char *) &vtmp->seqNum;
          cptr1 = (unsigned char *) &mcltptr->mvn_seq;
          *cptr++ = *cptr1++;
          *cptr++ = *cptr1++;
          *cptr++ = *cptr1++;
          *cptr++ = *cptr1++;

          vtmp->message = htons(kAudio);
          vtmp->dataType = htons(kAudio);
          vtmp->len = htons(msglen + HEADERLEN);

          ctmp = chead;
          while (ctmp != NULL)
          {
             if ((ctmp->clnt_config.flags & REC_AUDIO) && (ctmp->clnt_config.flags & WANT_LURCKERS) && 
                (ctmp->clnt_flags & HOLD_DOWN) == 0)
#ifdef DEBUG
                if (debug)
                {
                   in.s_addr = ctmp->clnt_addr.addr;
                   printf("reflecting MAVEN pkt to CUME client at %s\n",inet_ntoa(in));
                }
#endif
                if (sendto(vid_sock,(char *)vtmp,msglen+HEADERLEN,0,&ctmp->clnt_addr,sizeof(struct sockaddr_in)) != msglen+HEADERLEN)
                   dolog("Maven send to error in reflect\n");
             ctmp = ctmp->clnt_nptr;
          }
          
          msg = &buffer[40];

          break;

       case NV_UCAST:
       case NV_MCAST:

          if ((nvcptr = find_client(csock.sin_addr.s_addr)) == NULL)
          {
             dolog("NV client at %s is opening a connection\n", inet_ntoa(csock.sin_addr));

             if ((nvcptr = get_client()) == NULL)
             {
                dolog("unable to allocate a new nv client\n");
                return;
             }
         
             if ((nvcptr->clnt_id = get_client_id()) == -1)
             {
                dolog("maximum # of clients exceeded\n");
                return;
             }

	     /* 
		was this new nv client also and old vat client, if so
		keep the OCP sequence # space correct
             */

             if ((mtmp = find_vat_client(csock.sin_addr.s_addr)) != NULL)
		nvcptr->clnt_ocp_cnt = mtmp->mvn_ocp_seq;

             nvcptr->clnt_addr.family = AF_INET;
             bcopy(&csock.sin_addr,&nvcptr->clnt_addr.addr,4);

             if (type == NV_UCAST)
             {
                nvcptr->clnt_flags = NV_UCLIENT;
                nvcptr->clnt_addr.port = nv_ucast_port;
             }
             else
             {
                nvcptr->clnt_flags = NV_MCLIENT;
                nvcptr->clnt_addr.port = nv_mcast_port;
             }

             nvcptr->clnt_nptr = chead;
             chead = nvcptr;

             nv_client_cnt++;
          }

          nvcptr->clnt_bytecnt += msglen;

          nvcptr->clnt_rtimer = 0;

          distribute_nv(&msg,&msglen,nvcptr);

          if ((vtmp = nv_to_cuseeme(nvcptr,(struct rtphdr *) msg,msglen)) == NULL)
          {
             in.s_addr =  nvcptr->clnt_addr.addr;
             dolog("Unable to convert NV encoding for %s\n",inet_ntoa(in));
             delete_client(nvcptr);
             return;
          } 

          distribute(vtmp,nvcptr,FALSE);

          return;
    }   
}

/* 
   takes a nv packet with an rtp (v1) header and creates (in the same buffer) a
   packet with a VideoPacketHeader to transmit to cusm clients.  Note that if
   the nv packet contains an RTP SDESC option (or any option, for that matter,
   but SDESC is all nv sends right now) then it ignores it and just takes
   care of the data.  Deals with either cusm or nv encoded video but no others. 
*/

VideoPacketHeader *nv_to_cuseeme(nvcltptr,rtp_hdr_ptr,msglen)
     client *nvcltptr;
     struct rtphdr *rtp_hdr_ptr;
     int msglen;
{

    struct rtcpsdeschdr *sdesc_ptr;
    VideoPacketHeader   vph;
    char                *cptr,*cptr1,fin;
    struct rtpopthdr    *optr;
    unsigned short      *usptr,cnt;

    /* move cptr to after the rtp header */
    cptr = (char *) (rtp_hdr_ptr + 1);
 
    vph.len = msglen - sizeof(struct rtphdr) + HEADERLEN;

    /* 
       if there are rtp options, move cptr to after the options.
       Remember that the option length is in 4-byte words! 
    */

    if (rtp_hdr_ptr->rh_opts)
    {
       optr = (struct rtpopthdr *) cptr;
       vph.len = vph.len - optr->roh_optlen*4;

       if (optr->roh_type == RTPOPT_SDESC)
       {
          sdesc_ptr = (struct rtcpsdeschdr *) optr;

          cptr1 = (char *) (sdesc_ptr + 1);

          for (cnt = 0; cnt < 19; cnt++)
          {
             if ((nvcltptr->clnt_config.name[cnt] = *(cptr1+cnt)) == 0)
                break;

             if (nvcltptr->clnt_config.name[cnt] == '\n')
                nvcltptr->clnt_config.name[cnt] = ' ';

             if (nvcltptr->clnt_config.name[cnt] == '\r')
                nvcltptr->clnt_config.name[cnt] = ' ';
          }
       }

       fin = optr->roh_fin;
       cptr += 4*(optr->roh_optlen);
       optr = (struct rtpopthdr *) cptr;

       while (!fin) 
       {
          fin = optr->roh_fin;
          vph.len = vph.len - optr->roh_optlen*4;
          cptr += 4*(optr->roh_optlen);
          optr = (struct rtpopthdr *) cptr;
       } 
    }

    if (rtp_hdr_ptr->rh_content == RTPCONT_NV)
    {
       return(NULL);
    }
    else
       if (rtp_hdr_ptr->rh_content == RTPCONT_CELLB)
       {
          return(NULL);
       }
       else
          if (rtp_hdr_ptr->rh_content != RTPCONT_CUSEEME)
             return(NULL);


    /* 
       point usptr to the unsigned short type field, which we will need
       to fill into the video packet header.  This two-byte field will
       be kept for later, where the length of video data will be
       stored before going out to cusm clients 
    */

    usptr = (unsigned short *) cptr;

    /* 
       move cptr to point to where the video packet header will start 
    */

    cptr -= HEADERLEN;
   
    /* 
       now fill in the video-packet header in with all the information we
       presently possess. 
    */

    vph.dataType = *usptr;

    if (rtp_hdr_ptr->rh_sync)
       vph.message = htons(kFrameEndMessage);
    else
       vph.message = htons(kNoMessage);


    nvcltptr->clnt_seq = ntohs(rtp_hdr_ptr->rh_seq);

    vph.seqNum = htonl(nvcltptr->clnt_seq + nvcltptr->clnt_ocp_cnt);

   /*
      dolog("nv_to_cuseeme seq # %D\n",vph.seqNum);
   */

    vph.routing.dest.port = htons(VID_PORT);
    vph.routing.dest.family = htons(kGroup);
    vph.routing.src.family = htons(kGroup);
    vph.routing.src.port = htons(nv_mcast_port);
   
    /* 
       usptr now points to the unsigned short after the video packet header.
       this field is where nv had put the type...now we want to put in
       the actual length (in bytes) of the compressed squares.  This is
       equal to the length of the packet minus the header length, minus two
       bytes for this field itself 
    */
 
    *usptr = htons(vph.len - HEADERLEN - 2);

    vph.len = htons(vph.len);
 
    vph.routing.src.addr = htonl(nvcltptr->clnt_addr.addr);
   
    cptr1 = (char *) &vph;

    bcopy(cptr1,cptr,HEADERLEN);

    return((VideoPacketHeader *) cptr);
}

/* 
   returns a pointer to the SiteId structure which contains the given
   unsigned long id.  Takes a pointer to a struct IDMsgHdr.  Returns
   NULL if that id is not found.  
*/

SiteId *find_site_idlist(idlist,id)
     struct IDMsgHdr *idlist;
     unsigned long id;
{
    char *cptr;
    SiteId *siptr;
    int i,temp;
  
    if (idlist->nsids == 0)
      return(NULL);
  
    cptr = (char *) idlist;
    cptr += sizeof(struct IDMsgHdr);

    siptr = (SiteId *) cptr;

    for (i = 0; i < idlist->nsids; i++) 
    {
       if (siptr->site_id == id)
         return(siptr);
    
       temp = strlen(siptr->id_string) + 1;
    
       cptr = &(siptr->id_string[temp]);
    
       while ((temp % 4) != 0) 
       {
         temp++;
         cptr++;
       }
       siptr = (SiteId *) cptr;
    }

    return(NULL);
}

send_vat_ocp(mtmp)
    vat_client  *mtmp;
{

    VideoPacketHeader *vtmp;
    client            *ctmp;

    vtmp = make_open_continue(mtmp,0,kOpenConnection,NULL);

    ctmp = chead;
    while (ctmp != NULL)
    {
       if ((ctmp->clnt_flags & CLIENT) && ((ctmp->clnt_flags & HOLD_DOWN) == 0))
       {
          vtmp->routing.dest.addr = htonl(ctmp->clnt_addr.addr);
          if (sendto(vid_sock,(char *)vtmp,vtmp->len,0,&ctmp->clnt_addr,sizeof(struct sockaddr_in)) != vtmp->len)
             dolog("Maven send to error in reflect\n");
       }
       ctmp = ctmp->clnt_nptr;
    }
}



/* 
   construct a generic OpenContinuePacket that will represent mptr
   to the various CU-SeeMe clients.  If mptr is a vat mixer (and thus
   siptr is non-NULL, use that site_id as the string to send
   in the OCP.  
*/

VideoPacketHeader *make_open_continue(mptr,confid,message,siptr)
     vat_client   *mptr;
     short        confid;
     short        message;
     SiteId       *siptr;
{
  
    VideoPacketHeader *vidptr;
    unsigned char buffer[MAXMSG];
    OpenContinueData ocd;
    char *string;
    unsigned char *msg = &buffer[40];


    if (siptr != NULL) 
    {
       string = (char *) siptr;
       string += sizeof(unsigned long);
    }
    else 
       string = (char *) &mptr->mvn_name[0];
  
    
    vidptr = (VideoPacketHeader *) msg;
  
    vidptr->conferenceid = htons(confid);
    vidptr->routing.dest.family = htons(kGroup);           
  
    vidptr->routing.src.family = htons(kGroup);          
    vidptr->routing.src.port  = htons(VID_PORT);          
  
    vidptr->routing.src.addr = htonl(mptr->mvn_addr.addr);
                      
    vidptr->seqNum = htonl(mptr->mvn_ocp_seq);


    vidptr->message = htons(message);
    vidptr->dataType = htons(kConfigVideoType);
    vidptr->len = htons(HEADERLEN + sizeof(OpenContinueData));
  
    ocd.clientCount = 0;
    ocd.seqNum = htonl(mptr->mvn_ocp_seq++);

    /*
    dolog("make_open_continue seq # %D\n",vidptr->seqNum);
    */

    msg = (unsigned char *) ocd.name;
    if (strlen(string) > 19)
       *msg++ = 19;
    else
       *msg++ = strlen(string);

    strncpy((char *) msg,string,19);
  
    ocd.sendMode = 0;
    ocd.recvMode = 0;
    ocd.flags = REC_AUDIO | AUDIO_CAPABLE | WANT_LURCKERS;
    ocd.version = NV_CLIENT_VERS;
  
    msg = (unsigned char *) vidptr;
  
    /* 
       This gets rid of the padding put between the first two members of ocd 
    */

    bcopy((char *) &ocd.seqNum,(char *) &ocd.clientCount + sizeof(ocd.clientCount),sizeof(ocd));
  
    msg += HEADERLEN;
  
    bcopy((char *) &ocd, (char *) msg, sizeof(OpenContinueData));
  
    return(vidptr);  
}

void delete_vat_client(mptr)
    vat_client *mptr;
{
 
    vat_client *mtmp1;
    struct in_addr in;
 
    in.s_addr =  mptr->mvn_addr.addr;

    dolog("Deleting Maven client at %s\n", inet_ntoa(in));
 
    if (mptr == mhead)
    {
       mhead = mptr->mvn_nptr;
       mtmp1 = mptr;
       mptr = mptr->mvn_nptr;
       free(mtmp1);
    }
    else
    {
       mtmp1 = mhead;
       while (mtmp1->mvn_nptr != mptr)
          mtmp1 = mtmp1->mvn_nptr;
       mtmp1->mvn_nptr = mptr->mvn_nptr;
       mtmp1 = mptr;
       mptr = mptr->mvn_nptr;
       free(mtmp1);
    }
    vat_client_cnt--;
}

vat_client *new_vat_client(caddr)
    unsigned long caddr;
{
    vat_client *ctmp;

    if ((ctmp = (vat_client *) calloc(1,sizeof(vat_client))) == NULL)
       return(NULL);

    ctmp->mvn_addr.addr = caddr;
    ctmp->mvn_addr.port = maven_port;
    ctmp->mvn_addr.family = AF_INET;

    ctmp->mvn_nptr = mhead;
    mhead = ctmp;
    vat_client_cnt++;

    return(ctmp);
}

char num_vat_clients(type)
   char type;
{
    vat_client *mptr;
    char num;

    mptr = mhead;
    num = 0;

    while (mptr != NULL)
    {
       if (mptr->mvn_recv_type == type)
          num++;
       mptr = mptr->mvn_nptr;
    }
    return(num);
}



void vat_id(mptr)
    vat_client *mptr;
{
    static char         buf[MAXMSG];
    struct CtrlMsgHdr   *vatIdmsg;
    int                 msglen;
    struct in_addr      in;
    struct sockaddr_in  mvnskt;

#ifdef DEBUG
    if (debug)
    {
       in.s_addr = mptr->mvn_addr.addr;
       printf("sending id message to maven at %s\n",inet_ntoa(in));
    }
#endif

    vatIdmsg = (struct CtrlMsgHdr *) buf;
    vatIdmsg->flags = 0;
    vatIdmsg->type = CTRL_TYPE_ID;
    vatIdmsg->confid = 0;
    strcpy(vatIdmsg->idmsg,"CU-Reflector");
    vatIdmsg->idmsg[strlen("CU-Reflector")] = 0;
    msglen = CTRLMSGSIZE + strlen("CU-Reflector") + 1;

    mvnskt.sin_family = AF_INET;
    mvnskt.sin_port = mptr->mvn_addr.port + 1;
    mvnskt.sin_addr.s_addr = mptr->mvn_addr.addr;

    pkts_out++;
    bytes_out += msglen;

    if (sendto(maven_cntl_sock_out,buf,msglen,0,&mvnskt,sizeof(struct sockaddr_in)) != msglen)
       dolog("Maven id sendto error\n");

}


/* 
   constructs and sends a vat CTRL_TYPE_IDLIST message to all vat and
   maven clients.  The function takes care of sending the appropriate
   message to the mcast address and to the ucast vat/mavens that
   are connected... note that these are usually different.  
*/

void send_vat_idlist()
{
    vat_client           *mptr,*mptr1;
    char                 buffer[MAXMSG],*cptr,temp;
    client               *cltptr;
    struct IDMsgHdr      *idhptr;
    SiteId               *sidptr;
    int                  msglen,i;
    struct sockaddr_in   tmp1;
    char                 num_mcast,num_ucast,count;

    num_mcast = num_vat_clients(MCAST);
    num_ucast = num_vat_clients(UCAST);

    mptr = mhead;

    cptr = buffer + 40 + sizeof(struct IDMsgHdr);

    sidptr = (SiteId *) cptr;
    count = 0;

    /* 
       construct the idlist that represents all ucast mavens to the mcast
       vats.  If there are no ucast maven/vats and no cusm clients, send nothing  
    */

    if (vat_out_mcast.sin_addr.s_addr)
    {
       while (mptr != NULL) 
       {
          if (mptr->mvn_recv_type == UCAST)
          {
             if (mptr->mvn_idlist == NULL)
             {
                sidptr->site_id = mptr->mvn_addr.addr;
                strcpy(sidptr->id_string,mptr->mvn_name);
   
                temp = strlen(sidptr->id_string) + 1;
   
                cptr = &(sidptr->id_string[temp]);
   
                while ((temp % 4) != 0) 
                {
                   temp++;
                   cptr++;
                }
                sidptr = (SiteId *) cptr;
             }
             else 
             {
                /* 
                   copy the idlist that this maven client has been sending (see reflect.c)
                   into the idlist we are presently building.  mptr->idlist points to a
                   struct IDMsgHdr, which we don't want, so skip over it. 
                */
   
                count += mptr->mvn_idlist->nsids;
   
                cptr = (char *) mptr->mvn_idlist;
                cptr += sizeof(struct IDMsgHdr);
                temp = mptr->mvn_idlist_len - sizeof(struct IDMsgHdr);
   
                bcopy(cptr, (char *) sidptr, temp);
                cptr = (char *) sidptr;
                cptr += temp;
   
                while ((temp % 4) != 0) 
                {
                   temp++;
                   cptr++;
                }
   
                sidptr = (SiteId *) cptr;
   
             }
          }
          mptr = mptr->mvn_nptr;
       }
   
       cltptr = chead;
       while (cltptr != NULL) 
       {
          if (cltptr->clnt_config.flags & AUDIO_CAPABLE) 
          {
             count++;
   
             sidptr->site_id = cltptr->clnt_addr.addr;
             strncpy(sidptr->id_string,cltptr->clnt_config.name,20);
   
             temp = strlen(sidptr->id_string) + 1;
             cptr = &(sidptr->id_string[temp]);
   
             while ((temp % 4) != 0) 
             {
                temp++;
                cptr++;
             }
   
             sidptr = (SiteId *) cptr;
          }
   
          cltptr = cltptr->clnt_nptr;
       }

       count++;
  
       sidptr->site_id = myaddr.sin_addr.s_addr;
       strncpy(sidptr->id_string,"CU-Reflector        ",20);

       temp = strlen("CU-Reflector") + 1;
       cptr = &(sidptr->id_string[temp]);

       while ((temp % 4) != 0)
       {
          temp++;
          cptr++;
       }

       idhptr = (struct IDMsgHdr *) &buffer[40];
       idhptr->flags = 0;
       idhptr->type = CTRL_TYPE_IDLIST;
       idhptr->confid = htons(vat_confid);
       idhptr->nsids = num_ucast + count;
       idhptr->ainfo = 0;
       idhptr->blksize = 0;

       msglen = cptr - &buffer[40];
       cptr = &buffer[40];

       tmp1.sin_family = AF_INET;
       tmp1.sin_addr.s_addr = htonl(vat_out_mcast.sin_addr.s_addr);
       tmp1.sin_port = htons(vat_port + 1);

#ifdef DEBUG
       if (debug)
          printf("Sending Idlist of UCAST mavens to mcast address \n");
#endif
       if (sendto(vat_cntl_mcast_sock,cptr,msglen,0,&tmp1,sizeof(struct sockaddr_in)) != msglen)
          dolog("mcast sendto error");
    }

    /* 
       construct the idlist to send to ucast maven/vat clients.  Do not send one if there is
       one or zero clients.  (vat_id() takes care of 1, and obviously if there are no
       clients then we don't want to send anything... 
    */
 
    if ((mptr = mhead) == NULL)
       return;

    if ((mptr->mvn_nptr == NULL) && (chead == NULL))
       vat_id(mptr);
 
    cptr = buffer + 40 + sizeof(struct IDMsgHdr);

    sidptr = (SiteId *) cptr;

    count = 0;

    while (mptr != NULL) 
    {
       if (mptr->mvn_idlist == NULL)
       {
          sidptr->site_id = mptr->mvn_addr.addr;
          strcpy(sidptr->id_string,mptr->mvn_name);

          temp = strlen(sidptr->id_string) + 1;

          cptr = &(sidptr->id_string[temp]);

          while ((temp % 4) != 0) 
          {
             temp++;
             cptr++;
          }

          sidptr = (SiteId *) cptr;
       }
       else 
       {
          /* 
             copy the idlist that this maven client has been sending (see reflect.c)
             into the idlist we are presently building.  mptr->mvn_idlist points to a
             struct IDMsgHdr, which we don't want, so skip over it. 
          */

          count += mptr->mvn_idlist->nsids;

          cptr = (char *) mptr->mvn_idlist;
          cptr += sizeof(struct IDMsgHdr);
          temp = mptr->mvn_idlist_len - sizeof(struct IDMsgHdr);

          bcopy(cptr, (char *) sidptr, temp);
          cptr = (char *) sidptr;
          cptr += temp;

          while ((temp % 4) != 0) 
          {
             temp++;
             cptr++;
          }

          sidptr = (SiteId *) cptr;
       }
       mptr = mptr->mvn_nptr;
    }

    cltptr = chead;
    while (cltptr != NULL) 
    {
       if (cltptr->clnt_config.flags & AUDIO_CAPABLE) 
       {
          count++;

          sidptr->site_id = cltptr->clnt_addr.addr;
          strncpy(sidptr->id_string,cltptr->clnt_config.name,20);

          temp = strlen(sidptr->id_string) + 1;
          cptr = &(sidptr->id_string[temp]);

          while ((temp % 4) != 0) 
          {
             temp++;
             cptr++;
          }

          sidptr = (SiteId *) cptr;
       }
       cltptr = cltptr->clnt_nptr;
    }

    count++;
  
    sidptr->site_id = myaddr.sin_addr.s_addr;
    strncpy(sidptr->id_string,"CU-Reflector        ",20);

    temp = strlen("CU-Reflector") + 1;
    cptr = &(sidptr->id_string[temp]);

    while ((temp % 4) != 0)
    {
       temp++;
       cptr++;
    }

    idhptr = (struct IDMsgHdr *) &buffer[40];
    idhptr->flags = 0;
    idhptr->type = CTRL_TYPE_IDLIST;
    idhptr->confid = htons(vat_confid);
    idhptr->nsids = num_ucast + num_mcast + count;
    idhptr->ainfo = 0;
    idhptr->blksize = 0;

    msglen = cptr - &buffer[40];
    cptr = &buffer[40];

    mptr = mhead;

    while (mptr != NULL) 
    {
       if (mptr->mvn_recv_type == UCAST)
       {
          tmp1.sin_family = AF_INET;
          tmp1.sin_addr.s_addr = htonl(mptr->mvn_addr.addr);
          tmp1.sin_port = htons(maven_port + 1);

          dolog("Sending Idlist of %d clients to UCAST maven %s \n",count, inet_ntoa(tmp1.sin_addr));

          if (sendto(maven_cntl_sock_out,cptr,msglen,0,&tmp1,sizeof(struct sockaddr_in)) != msglen)
             dolog("maven_cntl_sock_out sendto error");
       }
       mptr = mptr->mvn_nptr;
    }
}


void send_nv_ocp(nvcltptr)
     client *nvcltptr; 
{

    VideoPacketHeader *vidptr;
    client *ctmp;
    short audio = 0;

    unsigned char buffer[MAXMSG];
    unsigned char *msg = &buffer[40],*cptr;

    vidptr = (VideoPacketHeader *) msg;

    vidptr->conferenceid = conference_id;

    vidptr->routing.dest.family = htons(kReflector);

    bcopy(&myaddr.sin_addr.s_addr,&vidptr->routing.dest.addr,4);

    vidptr->routing.src.family = htons(kClient);
    vidptr->routing.src.port  = htons(VID_PORT);

    bcopy(&nvcltptr->clnt_addr.addr,&vidptr->routing.src.addr,4);

    vidptr->seqNum = htonl(nvcltptr->clnt_seq + ++nvcltptr->clnt_ocp_cnt);

    /*
    dolog("send_nv_ocp seq # %D\n",vidptr->seqNum);
    */

    vidptr->message = htons(kOpenConnection);
    vidptr->dataType = htons(kConfigVideoType);
    vidptr->len = htons(HEADERLEN + OCDLEN);

    cptr = (unsigned char *) ((unsigned char *) vidptr + HEADERLEN);

    bcopy(&client_cnt,cptr,2);
    cptr += 2;

    bcopy(&nvcltptr->clnt_ocp_cnt,cptr,4);
    cptr += 4;

    *cptr++ = strlen(nvcltptr->clnt_config.name);

    strncpy(cptr,nvcltptr->clnt_config.name,19);

    cptr += 19;

    *cptr++ = 1;
    *cptr++ = 1;

    if (find_vat_client(nvcltptr->clnt_addr.addr) != NULL)
    {
       *cptr++ = REC_AUDIO | AUDIO_CAPABLE | WANT_LURCKERS;
       audio = 1;
    }
    else
       *cptr++ = 0;

    *cptr++ = NV_CLIENT_VERS;

    msg += HEADERLEN + OCDLEN;

    ctmp = chead;
    while (ctmp != NULL)
    {
       if (ctmp->clnt_flags & CLIENT)
       {
          bcopy(&ctmp->clnt_addr.addr,msg,4);
          msg += 4;
          if (audio)
             *msg++ = IWillRecAudio;
          else
             *msg++ = 0;

          *msg++ = 0;
          *msg++ = 1;
          *msg++ = 1;
          msg += 4;
          vidptr->len += 12;
       }
       ctmp = ctmp->clnt_nptr;
    }
    
    ctmp = chead;
    while (ctmp != NULL) 
    {
       if ((ctmp->clnt_flags & (CLIENT | BCC_CLIENT)) && (ctmp->clnt_flags & HOLD_DOWN) == 0)
       {
          vidptr->routing.dest.addr = htonl(ctmp->clnt_addr.addr);

          if (sendto(vid_sock,(char *)vidptr,vidptr->len,0,&ctmp->clnt_addr,sizeof(struct sockaddr_in)) != vidptr->len)
             dolog("NV send to error in reflect\n");
       }

       ctmp = ctmp->clnt_nptr;
    }
 
}

vat_client *find_vat_client(caddr)
    unsigned long caddr;
{
    vat_client *ctmp;

    ctmp = mhead;

    while (ctmp != NULL)
    {
      if (ctmp->mvn_addr.addr == caddr)
         return(ctmp);
      ctmp = ctmp->mvn_nptr;
    }
    return(NULL);
}

nv_wrt(srcptr,vidptr)
    client              *srcptr;
    VideoPacketHeader   *vidptr;
{
    client *ctmp;
    unsigned char *cptr,*tmp;
    unsigned char id;
    unsigned short datatype,seqnum,message,client_id;
    unsigned long timestamp,ltmp;
    struct in_addr in;
    short len,wlen,err;

    cptr = ((unsigned char *) vidptr) + HEADERLEN;

    datatype = vidptr->dataType;
    bcopy(&datatype,cptr,sizeof(datatype));

    client_id = htons(srcptr->clnt_id);

    bcopy(&vidptr->seqNum,&ltmp,4);
    seqnum = ltmp;                             /* truncate to 16 bits for RTP header */

    message = ntohs(vidptr->message);
    len = ntohs(vidptr->len) - HEADERLEN + MHEADERLEN + SSRCLEN;

    timestamp = RTPTime();
    if (((timestamp - srcptr->clnt_sdesc_time) >> 16) >= NV_SDESC_TIMER)
    {
       srcptr->clnt_sdesc_time = timestamp;

       wlen = (strlen(srcptr->clnt_config.name) >> 2) + 3;
       cptr -= (wlen << 2);

       tmp = cptr;
       *tmp++ = SDESC | FINAL;                           /* RTP SDESC option is FINAL option */
       *tmp++ = wlen;                                    /* option length */
       *((unsigned short *) tmp) = client_id;            /* client id */
       tmp += sizeof(client_id);
       bcopy(&srcptr->clnt_addr.addr,tmp,sizeof(struct in_addr));

       /* client addr */
       tmp += sizeof(struct in_addr);
       strcpy(tmp,srcptr->clnt_config.name);             /* client name */

       *(cptr - SSRCLEN) = SSRC;                         /* RTP SSRC option */
    }
    else
    {
       wlen = 0;
       *(cptr - SSRCLEN) = SSRC | FINAL;  /* RTP SSRC option is FINAL option */
    }

    len += (wlen << 2);

    cptr -= MHEADERLEN + SSRCLEN;
    tmp = cptr;

    /* start of RTP header */

    /*
    *tmp++ = NVVERSION | client_id;                        /* nv version & channel id */
    

    *tmp++ = NVVERSION | NV_CHAN;                        /* nv version & channel id */

    *tmp = NVCONTENT | NVOBIT;                           /* nv content with RTP options */

    if (message == kFrameEndMessage)
       *tmp |= NVSBIT;                                   /* nv framing bit */

    tmp++;

    *((unsigned short *) tmp) = seqnum;                  /* seq */
    tmp += sizeof(seqnum);
    timestamp = htonl(timestamp);
    bcopy(&timestamp,tmp,sizeof(timestamp));
    tmp += sizeof(timestamp) + 1;

    /* RTP SSRC option */
    /* already set RTP option type */

    *tmp++ = SSRCLEN >> 2;                               /* RTP option length */
    *((unsigned short *) tmp) = client_id;               /* client id */
    tmp += sizeof(client_id);
    bcopy(&srcptr->clnt_addr.addr,tmp,sizeof(struct in_addr)); /* client addr */

    if (send_to_nv(srcptr))
    {
       ctmp = chead;
       while (ctmp != NULL)
       {
          if (ctmp->clnt_flags & NV_UCLIENT)
             if (err = (sendto(nv_ucast_sock,cptr,len,0,&ctmp->clnt_addr,sizeof(struct sockaddr_in))) != len)
                dolog("nv ucast sendto error %d\n",err);

          ctmp = ctmp->clnt_nptr;
       }
    }

    if (nv_out_mcast_sock)
       if (err = (sendto(nv_out_mcast_sock,cptr,len,0,&nv_out_mcast,sizeof(struct sockaddr_in))) != len)
          dolog("nv mcast sendto error %d on nv_out_mcast_sock\n",err);

    if (nv_inout_mcast_sock)
       if (err = (sendto(nv_inout_mcast_sock,cptr,len,0,&nv_inout_mcast,sizeof(struct sockaddr_in))) != len)
          dolog("nv mcast sendto error %d\n on nv_inout_mcast_sock",err);
}

unsigned long RTPTime()
{
    struct timeval t;

    gettimeofday(&t, NULL);
    return ((t.tv_sec + RTP_EPOCH_OFFSET) << 16) | ((t.tv_usec << 10) / 15625);
}

short send_to_nv(cltptr)
    client *cltptr;
{
    short cnt;

    if (nv_client_cnt == 0)
       return(0);

    for (cnt=0; cnt<send_to_nv_cnt; cnt++)
       if (send_to_nv_list[cnt] == cltptr->clnt_addr.addr)
       {
          send_to_nv_timer[cnt] = 0;
          return(1);
       }
    
    if (send_to_nv_cnt < nv_streams)
    {
       send_to_nv_list[send_to_nv_cnt] = cltptr->clnt_addr.addr;
       send_to_nv_timer[cnt] = 0;
       send_to_nv_cnt++;
       return(1);
    }
    return(0);
}
