/*

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 <varargs.h>
#include <signal.h>
#include <errno.h>
#include <malloc.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>

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

#include <net/if.h>
#include <netinet/in.h>

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

void write_cmsg(msg,cltptr)
    short       msg;
    client      *cltptr;

{
    VideoPacketHeader pkt;

    pkt.routing.src.family = htons(kReflector);
    bcopy(&myaddr.sin_addr.s_addr,&pkt.routing.src.addr,4);
    pkt.routing.src.addr = 0;
    pkt.routing.src.port = htons(VID_PORT);

    pkt.routing.dest.family = htons(kClient);
    bcopy(&cltptr->clnt_addr.addr,&pkt.routing.dest.addr,4);
    pkt.routing.dest.port = htons(VID_PORT);

    pkt.seqNum = 0;
    pkt.dataType = htons(kControlType);
    pkt.len = htons(sizeof(VideoPacketHeader));
    pkt.message = htons(msg);

    write_pkt(&pkt,cltptr);
}

void write_close(cltptr,clientIP)
    client      *cltptr;
    unsigned long clientIP;

{
    VideoPacketHeader pkt;
    struct in_addr in;

    in.s_addr = clientIP;
    dolog("sending a close to client %s on behalf of %s\n",cltptr->clnt_config.name,inet_ntoa(in));

    pkt.routing.src.family = htons(kReflector);
    bcopy(&clientIP,&pkt.routing.src.addr,4);
    pkt.routing.src.port = htons(VID_PORT);

    pkt.routing.dest.family = htons(kClient);
    bcopy(&cltptr->clnt_addr.addr,&pkt.routing.dest.addr,4);
    pkt.routing.dest.port = htons(VID_PORT);

    pkt.seqNum = 0xffffffff;
    pkt.dataType = htons(kConfigVideoType);
    pkt.len = htons(sizeof(VideoPacketHeader));
    pkt.message = htons(kCloseConnection);

    write_pkt(&pkt,cltptr);
}



void write_pkt(vidptr,cltptr)
    VideoPacketHeader        *vidptr;
    client                   *cltptr;
{
    struct in_addr in;

    if (cltptr->clnt_flags & REF3_ORIGIN)
       cltptr = cltptr->clnt_pptr;

    if (cltptr->clnt_flags & HOLD_DOWN)
       return;

#ifdef DEBUG
    if (debug)
    {
       in.s_addr = cltptr->clnt_addr.addr;
       printf("sending to %s\n",inet_ntoa(in));
    }
#endif

    cltptr->clnt_stimer = 0;

    pkts_out++;
    bytes_out += ntohs(vidptr->len);

    if (sendto(vid_sock,vidptr,ntohs(vidptr->len),0,&cltptr->clnt_addr,sizeof(struct sockaddr_in)) != ntohs(vidptr->len))
       dolog("sendto error %d client %s\n",errno,cltptr->clnt_config.name);
}


write_msg(clnt_addr,type,cptr)
    struct sockaddr_in  *clnt_addr;
    short               type;
    char                *cptr;
{
    static char         buf[MAXMSG];
    VideoPacketHeader   *pkt=(VideoPacketHeader *)buf;
    char                *tmp,clen;
    short               mlen,nlen,cnt;
    int                 len;

    pkt->routing.src.family = htons(kReflector);
    bcopy(&myaddr.sin_addr.s_addr,&pkt->routing.src.addr,4);
    pkt->routing.src.port = htons(VID_PORT);

    pkt->routing.dest.family = htons(kClient);
    bcopy(&clnt_addr->sin_addr,&pkt->routing.dest.addr,4);
    pkt->routing.dest.port = htons(VID_PORT);

    pkt->seqNum = 0;
    pkt->dataType = htons(type);
    pkt->message = 0;

    mlen = strlen(cptr);

    for (cnt = 0; cnt < mlen; cnt++)
    {

       if (*(cptr+cnt) == '\n')
          *(cptr+cnt) = '\r';
    }

    len = sizeof(VideoPacketHeader) + mlen + 2;

    pkt->len = htons(len);

    tmp = (char *) (pkt+1);

    if (type == kMessageType1)
    {
       len--;
       pkt->len = htons(len);
       clen = mlen;
       *tmp = clen;
       bcopy(cptr,(char *)(tmp+1),mlen);
    }
    else
    {
       nlen = htons(mlen);
       bcopy(&nlen,tmp,2);
       bcopy(cptr,(char *)(tmp+2),mlen);
    }

#ifdef DEBUG
    if (debug)
       printf("sending reflector message %s to client at %s\n",cptr,inet_ntoa(clnt_addr->sin_addr));
#endif

    pkts_out++;
    bytes_out += len;

    if (sendto(vid_sock,buf,len,0,clnt_addr,sizeof(struct sockaddr_in)) != len)
    {
       dolog("sendto error on old video socket");
    }
}

void distribute(vidptr,cltptr,all)
   VideoPacketHeader *vidptr;
   client *cltptr;
   char   all;
{
    client         *ctmp;
    slist          *sptr;
    struct in_addr in;
    int            len,cnt;
    short          ref_ucast_sent[MAXSRCREF];    
    short          mcast_sent = FALSE;

    cltptr->clnt_hdloop = 0;
    cltptr->clnt_flags &= ~HOLD_DOWN;

    if (all == FALSE)
    {
       for (cnt=0; cnt < MAXSRCREF; cnt++)
          ref_ucast_sent[cnt] = 0;

       sptr = cltptr->clnt_vlist;
       while (sptr)
       {
          if (sptr->snd_client->clnt_flags & REF3_ORIGIN)
          {
             if ((ctmp = sptr->snd_client->clnt_pptr) == NULL)
             {
                dolog("REF3_ORIGIN client without a parent in distribute\n");
                delete_client(cltptr);
                return;
             }

             if ((ctmp->clnt_flags & REF3_SERVER) == 0)
             {
                dolog("REF3_ORIGIN client's parent is not a REF3_SERVER\n");
                delete_client(cltptr);
                return;
             }

             if ((ref_ucast_sent[ctmp->clnt_id] == 0) && (cltptr->clnt_flags & REF3_ORIGIN) == 0)
             {
                ref_ucast_sent[ctmp->clnt_id] = 1;
                write_pkt(vidptr,ctmp);
             }

             sptr = sptr->snd_nptr;
             continue;
          }

          if (sptr->snd_client->clnt_flags & CLIENT)
             write_pkt(vidptr,sptr->snd_client);

          sptr = sptr->snd_nptr;
       }

       ctmp = chead;
       while (ctmp != NULL)
       {
          if (ctmp->clnt_flags & (BCC_CLIENT | BCC_GCLIENT))
             write_pkt(vidptr,ctmp);
          ctmp = ctmp->clnt_nptr;
       }

       /* if necessary, distribute this cu-seeme video packet to all the NV clients */
       if ((cltptr->clnt_flags & (NV_UCLIENT | NV_MCLIENT)) == 0)
       {
          if (nv_out_mcast_sock || nv_inout_mcast_sock || send_to_nv(cltptr)) 
             nv_wrt(cltptr,vidptr);
       }
    }
    else
    {
       ctmp = chead;
       while (ctmp != NULL)
       {
          if ((ctmp->clnt_flags & CLIENT) && ((self_reflect == 1) || (cltptr != ctmp)))
             write_pkt(vidptr,ctmp);
          else
             if ((ctmp->clnt_flags & REF3_SERVER) && ((cltptr->clnt_flags & (REF3_SERVER | REF3_ORIGIN)) == 0))
                write_pkt(vidptr,ctmp);
             else
                if ((ctmp->clnt_flags & (BCC_CLIENT | BCC_GCLIENT)) && (cltptr != ctmp))
                   write_pkt(vidptr,ctmp);
          ctmp = ctmp->clnt_nptr;
       }
    }

#ifdef MULTI
    if (rfout_mcast.sin_addr.s_addr)
       rf_mcast_wrt(vidptr);

    if ((cltptr->clnt_flags & (REF2_SERVER | REF2_ORIGIN)) == 0)
       if (inout_mcast.sin_addr.s_addr)
          inout_mcast_wrt(vidptr);
#endif
}


void distribute_audio(vidptr,cltptr)
   VideoPacketHeader *vidptr;
   client *cltptr;
{
    client         *ctmp;
    unsigned char  *cptr,*cptr1;
    vat_client     *mtmp;
    slist          *sptr;
    struct in_addr in;
    int            len,cnt;
    short          ref_ucast_sent[MAXSRCREF];    
    struct sockaddr_in tmp1;
    vat_hdr_t      *vat_hdr;
    unsigned long  ul;
    char          *tmp;


    cltptr->clnt_hdloop = 0;
    cltptr->clnt_flags &= ~HOLD_DOWN;

    if (cltptr->clnt_talker == 0)
       dolog("%s is speaking\n",cltptr->clnt_config.name);

    if (cltptr->clnt_talker++ > 50)
       cltptr->clnt_talker = 0;

    if (cltptr->clnt_config.sendMode == 0)
    {
       ctmp = chead;
       while (ctmp != NULL)
       {
          if (ctmp->clnt_flags & CLIENT)
             if ((ctmp->clnt_config.flags & REC_AUDIO) && (ctmp->clnt_config.flags & WANT_LURCKERS))
                write_pkt(vidptr,ctmp);
         ctmp = ctmp->clnt_nptr;
       }
    }
    else
    {
       for (cnt=0; cnt < MAXSRCREF; cnt++)
          ref_ucast_sent[cnt] = 0;

       sptr = cltptr->clnt_alist;
       while (sptr)
       {
          if (sptr->snd_client->clnt_flags & REF3_ORIGIN)
          {
             if ((ctmp = sptr->snd_client->clnt_pptr) == NULL)
             {
                dolog("REF3_ORIGIN client without a parent in distribute_audio\n");
                delete_client(cltptr);
                return;
             }

             if ((ctmp->clnt_flags & REF3_SERVER) == 0)
             {
                dolog("REF3_ORIGIN client's parent is not a REF3_SERVER\n");
                delete_client(cltptr);
                return;
             }

             if ((ref_ucast_sent[ctmp->clnt_id] == 0) && (cltptr->clnt_flags & REF3_ORIGIN) == 0)
             {
                ref_ucast_sent[ctmp->clnt_id] = 1;
                write_pkt(vidptr,ctmp);
             }

             sptr = sptr->snd_nptr;
             continue;
          }

          if (sptr->snd_client->clnt_flags & CLIENT)
             write_pkt(vidptr,sptr->snd_client);
          sptr = sptr->snd_nptr;
       }
    }

    ctmp = chead;
    while (ctmp != NULL)
    {
       if (ctmp->clnt_flags & (BCC_CLIENT | BCC_GCLIENT))
          write_pkt(vidptr,ctmp);
       ctmp = ctmp->clnt_nptr;
    }

#ifdef MULTI
    if (rfout_mcast.sin_addr.s_addr)
       rf_mcast_wrt(vidptr);

    if ((cltptr->clnt_flags & (REF2_SERVER | REF2_ORIGIN)) == 0)
       if (inout_mcast.sin_addr.s_addr)
          inout_mcast_wrt(vidptr);
#endif


    cptr = ((unsigned char *) vidptr) + HEADERLEN;
    len = vidptr->len - HEADERLEN;
    mtmp = mhead;
   
    if ((mtmp != NULL) || (vat_out_mcast_sock)) 
    {
       /* 
          the following code adds a speaker id to the maven/vat audio
          packet being sent.  See vat_hdr_t.   
       */

       tmp = (char *) cptr;
       tmp -= 4;

       bcopy((char *)cptr, tmp, sizeof(vat_hdr_t));

       cptr += (sizeof(vat_hdr_t) - 4);

       ul = cltptr->clnt_addr.addr;
       cptr1 = (unsigned char *) &ul;

       *cptr++ = *cptr1++;
       *cptr++ = *cptr1++;
       *cptr++ = *cptr1++;
       *cptr++ = *cptr1++;

       cptr = (unsigned char *) tmp;

       vat_hdr = (vat_hdr_t *) cptr;

       /* set the number of speaker id's to 1 */
       vat_hdr->nsid = (vat_hdr->nsid & ~NSID_MASK) | 1;

       /* set the conference id in this vat audio packet to the vat_confid */

      vat_hdr->confid = htons(vat_confid);

      len += 4;

      while (mtmp != NULL) 
      {
         if ((mtmp->mvn_recv_type == UCAST) && ((mtmp->mvn_flags & VAT_MIXER_CLIENT) == 0))
#ifdef DEBUG
           if (debug)
	   {
	      in.s_addr = mtmp->mvn_addr.addr; 
	      printf("reflecting CUSEEME audio to ucast maven %s \n", inet_ntoa(in));
           }
#endif
           if (sendto(maven_sock,cptr,len,0,&mtmp->mvn_addr,sizeof(struct sockaddr_in)) != len)
              dolog("maven sendto error in distribute\n");

         mtmp = mtmp->mvn_nptr;
      }

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

      /* 
         if there are any mcast clients then we want to distribute this audio
         to them if vat_inout_mcast_sock has been bound...
      */

      if (num_vat_clients(MCAST)) 
      {
         if (vat_inout_mcast_sock) 
         {
            if (sendto(vat_inout_mcast_sock,cptr,len,0,&tmp1,sizeof(struct sockaddr_in)) != len)
               dolog("mcast sendto error");
         }
      }
      else 
         if (vat_out_mcast_sock) 
         {
            /* 
               else, (ie if there are no mcast vat/maven clients) if vat_out_mcast_sock
               has been bound, blindly send this vat stream onto the mcast address 
            */

            if (sendto(vat_out_mcast_sock,cptr,len,0,&tmp1,sizeof(struct sockaddr_in)) != len)
               dolog("mcast sendto error");
         }
    }
}


void distribute_aux(vidptr,cltptr)
   VideoPacketHeader *vidptr;
   client *cltptr;
{
    client         *ctmp;
    slist          *sptr;
    unsigned char  *aux;
    struct in_addr in;
    int            len,cnt;
    unsigned long  aux_mask;
    short          ref_ucast_sent[MAXSRCREF];    
    char           tmp[100];

    aux = (unsigned char *) ((char *)vidptr+HEADERLEN+4);
    bcopy(aux,&aux_mask,4);
    aux_mask = ntohl(aux_mask); 
    dolog("AUX data packet mask is %ld\n",aux_mask);
    cltptr->clnt_hdloop = 0;
    cltptr->clnt_flags &= ~HOLD_DOWN;

    for (cnt=0; cnt < MAXSRCREF; cnt++)
       ref_ucast_sent[cnt] = 0;

    sptr = cltptr->clnt_xlist;
    while (sptr)
    {
       if ((sptr->snd_auxmask & aux_mask) != 0)
       {
          if (sptr->snd_client->clnt_flags & REF3_ORIGIN)
          {
             if ((ctmp = sptr->snd_client->clnt_pptr) == NULL)
             {
                dolog("REF3_ORIGIN client without a parent in distribute\n");
                delete_client(cltptr);
                return;
             }

             if ((ctmp->clnt_flags & REF3_SERVER) == 0)
             {
                dolog("REF3_ORIGIN client's parent is not a REF3_SERVER\n");
                delete_client(cltptr);
                return;
             }

             if ((ref_ucast_sent[ctmp->clnt_id] == 0) && (cltptr->clnt_flags & REF3_ORIGIN) == 0)
             {
                ref_ucast_sent[ctmp->clnt_id] = 1;
                write_pkt(vidptr,ctmp);
             }

             sptr = sptr->snd_nptr;
             continue;
          }

          if (sptr->snd_client->clnt_flags & CLIENT)
          {
             in.s_addr = vidptr->routing.src.addr;
             sprintf(tmp,"sending AUX data from %s ",inet_ntoa(in));
             in.s_addr = sptr->snd_client->clnt_addr.addr;
             dolog("%s to %s\n",tmp,inet_ntoa(in));
             write_pkt(vidptr,sptr->snd_client);
          }
       }
       sptr = sptr->snd_nptr;
    }

    ctmp = chead;
    while (ctmp != NULL)
    {
       if (ctmp->clnt_flags & (BCC_CLIENT | BCC_GCLIENT))
          write_pkt(vidptr,ctmp);
       ctmp = ctmp->clnt_nptr;
    }

#ifdef MULTI
    if (rfout_mcast.sin_addr.s_addr)
       rf_mcast_wrt(vidptr);

    if ((cltptr->clnt_flags & (REF2_SERVER | REF2_ORIGIN)) == 0)
       if (inout_mcast.sin_addr.s_addr)
          inout_mcast_wrt(vidptr);
#endif
}


/* distribute nv video to other nv's */

void distribute_nv(rtp_hdr_ptr,msglen,nvcptr)
   struct rtphdr **rtp_hdr_ptr;
   int           *msglen;
   client        *nvcptr;

{
    struct rtpopthdr    *optr;
    struct rtpssrchdr   *sptr;
    client              *ctmp;
    short               err,len;
    char                *cptr;

    cptr = (char *) (*rtp_hdr_ptr + 1);

    if ((*rtp_hdr_ptr)->rh_opts)
    {
       optr = (struct rtpopthdr *) cptr;

       if (!optr->roh_fin)
       {
	  dolog("In distribute_nv, received rtp packet with more then one option\n");
	  return;
       }
       optr->roh_fin = 0;
       cptr += 4*(optr->roh_optlen);
    }

    (*rtp_hdr_ptr)->rh_opts = 1;

    len = cptr - (char *) *rtp_hdr_ptr;
    bcopy((char *)*rtp_hdr_ptr,(char *)*rtp_hdr_ptr - sizeof(struct rtpssrchdr),len);

    *rtp_hdr_ptr = (struct rtphdr *) ((char *) *rtp_hdr_ptr - sizeof(struct rtpssrchdr));

    sptr = (struct rtpssrchdr *) ((char *) *rtp_hdr_ptr + len);

    sptr->rsh_fin = 1;
    sptr->rsh_type = RTPOPT_SSRC;
    sptr->rsh_optlen = 2;
    sptr->rsh_id = htons(nvcptr->clnt_id);
    sptr->rsh_addr = htonl(nvcptr->clnt_addr.addr);

    *msglen += sizeof(struct rtpssrchdr);

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

       ctmp = ctmp->clnt_nptr;
    }

    if ((nvcptr->clnt_flags & NV_MCLIENT) == 0)
    {
       if (nv_out_mcast_sock)
       {
          if (err = (sendto(nv_out_mcast_sock,*rtp_hdr_ptr,*msglen,0,&nv_out_mcast,sizeof(struct sockaddr_in))) != *msglen)
             dolog("mcast nv->nv sendto error %d\n",err);
       }

       if (nv_inout_mcast_sock)
       {
          if (err = (sendto(nv_inout_mcast_sock,*rtp_hdr_ptr,*msglen,0,&nv_inout_mcast,sizeof(struct sockaddr_in))) != *msglen)
             dolog("mcast nv->nv sendto error %d\n",err);
       }
    }
}


/*
void distribute_nv(msg,msglen,nvcptr)
   unsigned char *msg;
   int           msglen;
   client        *nvcptr;

{
    client         *ctmp;
    short          err;

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

       ctmp = ctmp->clnt_nptr;
    }

    if ((nvcptr->clnt_flags & NV_MCLIENT) == 0)
    {
       if (nv_out_mcast_sock)
       {
          if (err = (sendto(nv_out_mcast_sock,msg,msglen,0,&nv_out_mcast,sizeof(struct sockaddr_in))) != msglen)
             dolog("mcast nv->nv sendto error %d\n",err);
       }

       if (nv_inout_mcast_sock)
       {
          if (err = (sendto(nv_inout_mcast_sock,msg,msglen,0,&nv_inout_mcast,sizeof(struct sockaddr_in))) != msglen)
             dolog("mcast nv->nv sendto error %d\n",err);
       }
    }
}
*/
