/***************************************************************************
 *                                                                         *
 *   FILTCP.C                                                              *
 *                                                                         *
 *   Copyright (c) 1994-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   Sending & receiving files across TCP links.                           *
 *                                                                         *
 *                                        - RNStein  7/30/94               *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "tcpip.h"
#include "filtcp.h"

#define FILREV "$Revision: 5 $"

static VOID ftcsend(struct filtcp *ftcp);
static VOID ftcrecv(struct filtcp *ftcp);
static VOID ftccbk(INT code);
static VOID ftcrst(VOID);

#define RCHUNK vdasiz              /* max bytes to recv & store each cycle */
struct filtcp **filtcp;            /* list of file xfer SCB's for each chan*/
INT numfsnd;                       /* cntftc() sets:  number sending       */
INT numfrcv;                       /* cntftc() sets:  number receiving     */
VOID (*oldrst)(VOID);              /* reset vector saved                   */

VOID
initfiltcp(VOID)                   /* initialize filtcps stuff             */
{
     filtcp=(struct filtcp **)alczer(nterms*sizeof(struct filtcp *));
     oldrst=hdlrst;
     hdlrst=ftcrst;
}

INT
sndfiltcp(                         /* begin sending a file                 */
struct filtcp *ftcp)               /* caller fills in filpath, socket,     */
                                   /* flags, callbk, buffer, bufsiz fields */
{                                  /* returns 1=trying, 0=immed failure    */
     filtcp[usrnum]=ftcp;
     ftcp->nbytes=0L;
     ftcp->bufcnt=0;
     ftcp->flags|=FTCFSND;
     ftcp->sttime=hrtval();
     if ((ftcp->fp=fopen(ftcp->filpath,FOPRB)) == NULL) {
          ftccbk(FTCOPN);
          return(0);
     }
     else {
          sktnfy(TNFSEND,ftcp->socket,(VOID (*)(VOID *))ftcsend,ftcp,usrnum);
          return(1);
     }
}

INT
rcvfiltcp(                         /* begin receiving a file               */
struct filtcp *ftcp)               /* caller fills in filpath, socket,     */
                                   /* flags, and callbk fields             */
{                                  /* returns 1=trying, 0=immed failure    */
     filtcp[usrnum]=ftcp;
     ftcp->nbytes=0L;
     ftcp->bufcnt=0;
     ftcp->flags&=~FTCFSND;
     ftcp->sttime=hrtval();
     if ((ftcp->fp=fopen(ftcp->filpath,ftcp->flags&FTCAPND ? FOPAB
                                                           : FOPWB)) == NULL) {
          ftccbk(FTCOPN);
          return(0);
     }
     else {
          sktnfy(TNFRECV,ftcp->socket,(VOID (*)(VOID *))ftcrecv,ftcp,usrnum);
          return(1);
     }
}

INT
cntftc(VOID)                       /* how many are receiving/transmitting? */
{                                  /* also sets numfrcv, numfsnd           */
     INT i;
     struct filtcp *ftcp;

     numfsnd=numfrcv=0;
     for (i=0 ; i < nterms ; i++) {
          if ((ftcp=filtcp[i]) != NULL) {
               if (ftcp->flags&FTCFSND) {
                    numfsnd++;
               }
               else {
                    numfrcv++;
               }
          }
     }
     return(numfsnd+numfrcv);
}

static VOID
ftcsend(                           /* send whatever you can thru the socket*/
struct filtcp *ftcp)
{
     INT nroom,nactual;

     if ((nroom=ftcp->bufsiz-ftcp->bufcnt) > 0) {
          nactual=(INT)fread(ftcp->buffer+ftcp->bufcnt,1,nroom,ftcp->fp);
          if (nactual > 0) {
               ftcp->bufcnt+=nactual;
          }
          else if (nactual < 0) {
               ftccbk(FTCFER);
               return;
          }
     }
     switch(sndmgr(ftcp->buffer,&ftcp->bufcnt,ftcp->socket)) {
     case -1:
          ftccbk(FTCSER);
          break;
     case 0:
          if (nroom > 0 && nactual == 0 && btuoba(usrnum) == OUTSIZ-1) {
               ftcp->nbytes+=sndact;
               if (ferror(ftcp->fp)) {
                    ftccbk(FTCFER);
               }
               else {
                    ftccbk(FTCEOF);
               }
               break;
          }
     case 1:
          if (sndact > 0) {
               ftcp->nbytes+=sndact;
               ftcp->sttime=hrtval();
          }
          ftccbk(FTCPRG);
          break;
     }
}

static VOID
ftcrecv(                           /* receive whatever you can thru socket */
struct filtcp *ftcp)
{
     INT nactual,nwrit;

     if ((nactual=recv(ftcp->socket,vdatmp,RCHUNK,0)) <= 0) {
          if (nactual != 0 && tcpip_errno != ECONNRESET) {
               ftccbk(FTCSER);
          }
          else {
               ftccbk(FTCEOF);
          }
     }
     else {
          if ((ftcp->nbytes+=nactual) > ftcp->maxbyt) {
               ftccbk(FTCBST);
          }
          else {
               if ((nwrit=fwrite(vdatmp,1,nactual,ftcp->fp)) != nactual) {
                    ftcp->nbytes-=nactual-nwrit;
                    ftccbk(FTCFER);
               }
               else {
                    ftcp->sttime=hrtval();
                    ftccbk(FTCPRG);
               }
          }
     }
}

VOID
ftcabt(VOID)                       /* abort transfer (either direction)    */
{
     ftccbk(FTCABT);
}

static VOID
ftccbk(INT code)                   /* call the call-back vector, close the */
                                   /* socket, close the file, delete       */
{                                  /* fragment of aborted receive          */
     struct filtcp *ftcp;

     if ((ftcp=filtcp[usrnum]) != NULL) {
          if (ftcp->callbk != NULL) {
               ftcp->callbk(code);
          }
          if (code != FTCPRG) {
               clsskt(ftcp->socket);
               ftcp->socket=-1;
               if (ftcp->fp != NULL) {
                    fclose(ftcp->fp);
                    ftcp->fp=NULL;
                    if (!(ftcp->flags&FTCFSND) && code != FTCEOF) {
                         unlink(ftcp->filpath);
                    }
               }
               filtcp[usrnum]=NULL;
          }
     }
}

static VOID
ftcrst(VOID)                       /* reset time (one last check)          */
{
     ftccbk(FTCRST);
     (*oldrst)();
}
