/*     
      SCOL ENVIRONMENT . Magma 1.0 . 1996 . Sylvain HUET

         fifo.c : gestion des fifo de l'environnement SCOL
*/
// Modification history:
//$ FA(01/06/2001): Comment MMpull() after call to SCexeccomm()
//$ ER(01/07/2001): Update of the SERVERM and SERVERS calls to the SERVER and MUTUALISE variable. SERVERS and SERVERM constants no longer exist
//
//$ LB(18/07/2001) : add the process of two internal defcom in SCreadchar, in order to  : 
//							- allow a scol machine to ask the main machine the informations for http proxy authentication
//							- make the main machine communicate back those informations to the client machine
//
//$ LB(25/01/2002) : in function SCreadchar : have moved the http proxy messages check after the "#ifdef SERVER"
//
//$LB (25/01/2002) : in function SCreadchar : reload scol pointer in orderto be aware of the f.... gc.
//

#include <stdio.h>
#include <string.h>
#include <time.h>

#include "mmemory.h"
#include "mbytec.h"
#include "mbytec2.h"
#include "listlab.h"
#include "loadpak.h"
#include "scol.h"
#include "scolsys.h"
#include "fifo.h"
#include "include/vscol.h"
#include "include/socket.h"
//$ LB(18/07/2001) : for SCreadchar
#include "include/proxyhttp.h"

/* etats en sortie */
#define SC_NOTCON  0
#define SC_BUSY    1
#define SC_READY   2
#define SC_PROXY   3
#define SC_UDP     4

/* etats en entree */
#define SC_SIZE0   0
#define SC_SIZE1   1
#define SC_DATA    2













/* creation d'un bloc fifo vide */
int SCmakefifo(mmachine m)
{
  int k;
  time_t t;

  time(&t);
  if (MMpush(m,NIL)) return MERRMEM;            /* sock= NIL */
  if (MMpush(m,NIL)) return MERRMEM;            /* name= NIL */
  if (MMpush(m,NIL)) return MERRMEM;            /* fifout= NIL */
  if (MMpush(m,0)) return MERRMEM;              /* cptout= NIL */
  if (MMpush(m,SC_NOTCON*2)) return MERRMEM;    /* stout= NIL */
  if (MMpush(m,NIL)) return MERRMEM;            /* messin= NIL */
  if (MMpush(m,0)) return MERRMEM;              /* cptin= NIL */
  if (MMpush(m,0)) return MERRMEM;              /* sizin= NIL */
  if (MMpush(m,SC_SIZE0*2)) return MERRMEM;     /* stin= NIL */
  if (MMpush(m,t*2)) return MERRMEM;            /* time */
  if (MMpush(m,NIL)) return MERRMEM;            /* max fifo=NIL : inactif */
  if (MMpush(m,0)) return MERRMEM;              /* capacite fifo= 0 */
  if (MMpush(m,SIZE_FIFO*2)) return MERRMEM;
  if (k=MBdeftab(m)) return k;
  return 0;
}

/* initialisation d'une socket proxy associee a un canal
   (channel est un pointeur vers le canal, avec bit 0 a 1) */
int SCsetproxy(mmachine m, int channel)
{
    int r;
    
    r=m->tape[(channel>>1)+SizeHeader+OFFCHANFIFO]>>1;
    if (r==NIL) return 0;
    m->tape[r+SizeHeader+FIFOSTOUT]=SC_PROXY*2;     /* ecriture handler socket */
    return 0;
}

/* initialisation d'une socket udp
   (channel est un pointeur vers le canal, avec bit 0 a 1) */
int SCsetudp(mmachine m, int channel)
{
    int r;
    
    r=m->tape[(channel>>1)+SizeHeader+OFFCHANFIFO]>>1;
    if (r==NIL) return 0;
    m->tape[r+SizeHeader+FIFOSTOUT]=SC_UDP*2;     /* ecriture handler socket */
    return 0;
}

/* retourne 1 si le canal est une socket udp
   (channel est un pointeur vers le canal, avec bit 0 a 1) */
int SCtestudp(mmachine m, int channel)
{
    int r;
    
    r=m->tape[(channel>>1)+SizeHeader+OFFCHANFIFO]>>1;
    if (r==NIL) return 0;
    if (m->tape[r+SizeHeader+FIFOSTOUT]==SC_UDP*2) return 1;
    return 0;
}

/* lecture du numero de socket associe a un serveur
   (server est un pointeur vers le serveur, avec bit 0 a 1) */
int SCgetsrvsocket(mmachine m, int server)
{
  return m->tape[(server>>1)+SizeHeader+OFFSRVSOCK]>>1;
}

/* lecture du numero de socket associe a un canal
   (channel est un pointeur vers le canal, avec bit 0 a 1) */
int SCgetsocket(mmachine m, int channel)
{
    int r,s;
    
    r=m->tape[(channel>>1)+SizeHeader+OFFCHANFIFO]>>1;
    if (r==NIL) return -1;
    s=m->tape[r+SizeHeader+FIFOSOCK]>>1;     /* lecture handler socket */
    return s;
}

/* reglage du numero de socket associe a un canal
   (channel est un pointeur vers le canal, avec bit 0 a 1) */
int SCsetsocket(mmachine m, int channel, int socket)
{
    int r;
    r=m->tape[(channel>>1)+SizeHeader+OFFCHANFIFO]>>1;
    if (r==NIL) return 0;
    m->tape[r+SizeHeader+FIFOSOCK]=socket*2;     /* ecriture handler socket */
    return 0;
}

/* lecturere du nom de socket d'un canal,
   le nom est mis dans la pile */
int SCgetsockname(mmachine m,int channel)
{
    int r;
    
    r=m->tape[(channel>>1)+SizeHeader+OFFCHANFIFO]>>1;
    if (r==NIL) return MMpush(m,NIL);
    return MMpush(m,m->tape[r+SizeHeader+FIFONAME]);
}

/* reglagle du nom de socket d'un canal,
   le nom est dans la pile */
int SCsetsockname(mmachine m,int channel)
{
    int r;
    
    r=m->tape[(channel>>1)+SizeHeader+OFFCHANFIFO]>>1;
    if (r==NIL) return 0;
    m->tape[r+SizeHeader+FIFONAME]=MMpull(m);
    return 0;
}

/* lecture du temps de vie en secondes d'un canal
   (channel est un pointeur vers le canal, avec bit 0 a 1) */
int SCgetchanneltime(mmachine m, int channel)
{
    int r,s;
    time_t t;

    time(&t);
    r=m->tape[(channel>>1)+SizeHeader+OFFCHANFIFO]>>1;
    if (r==NIL) return -1;
    s=t-(m->tape[r+SizeHeader+FIFOTIME]>>1);
    return s;
}

/* lecture du temps de vie en secondes d'un serveur
   (channel est un pointeur vers le canal, avec bit 0 a 1) */
int SCgetservertime(mmachine m, int srv)
{
    int s;
    time_t t;

    time(&t);
    s=t-(m->tape[(srv>>1)+SizeHeader+OFFSRVTIME]>>1);
    return s;
}

#ifdef SERVER
/* lecture de la capacité associee a un canal
   (channel est un pointeur vers le canal, avec bit 0 a 1) */
int SCgetcapa(mmachine m, int channel)
{
    int r;
    
    r=m->tape[(channel>>1)+SizeHeader+OFFCHANFIFO]>>1;
    if (r==NIL) return 0;
    return m->tape[r+SizeHeader+FIFOCAPA]>>1;     /* lecture handler socket */
}
extern int maxsock;

/* teste la somme des capacités */
int SCchecksum(mmachine m)
{
  int p,k;
  char *c;

  k=0;
  p=MMgetglobal(m,OFFSCCHAN);
  while(p!=NIL)
  {
      p>>=1;
      k+=SCgetcapa(m,MMfetch(m,p,OFFLVAL));
      p=MMfetch(m,p,OFFLNEXT);
  }

  if (k>maxsock)
  {
	  MMechostr(MSKDEBUG,"capacity reached %d/%d\n",k,maxsock-1);
//$ ER(09/07/01) : turn of #ifdef SERVERM by the following test
	  if (MUTUALISE)
	  {
		SCchanneldown(m);
		return 1;
	  } else
	  {
		MMechostr(MSKDEBUG,"send killLast\n");
		if (Mpushstring(m,"__@k")) return MERRMEM;
		c=(char*)MMstart(m,MMget(m,0)>>1);
		p=strlen(c)+1;
		c[0]=(p-2)&255;
		c[1]=((p-2)>>8)&255;
		return SCsendpile(m);
	  }
  }
  return 0;
}

int testlocal(char *s)
{
	char buf[128];

	if (forcedIP[0]) strcpy(buf,forcedIP);
	else strcpy(buf,"127.0.0.1");
	//	MMechostr(MSKDEBUG,"testlocal %s %s\n",s,buf);
	if (!strcmp(s,buf)) return 1;
	return 0;
}
#endif

//$ ER(09/07/01) : turn #ifdef SERVERS in #ifdef SERVER. SERVERS exists no longer and we cant test MUTUALISE during the compiling
//					since it isnot a CONSTANTE. The 2 following functions are added in the binary code even when we are not a
//					mutualised server;
#ifdef SERVER
/* retourne le dernier canal non unplugged */
int SCgetlast(mmachine m)
{
  int p,s;

  p=MMgetglobal(m,OFFSCCHAN);
  while(p!=NIL)
  {
      p>>=1;
      s=SCgetsocket(m,MMfetch(m,p,OFFLVAL));
      if ((s>=0)&&(s!=socklife)&&(!SCtestudp(m,MMfetch(m,p,OFFLVAL)))) return s;
      p=MMfetch(m,p,OFFLNEXT);
  }
  return -1;
}

int SCkilllast(mmachine m)
{
	int s;

	s=SCgetlast(m);
	if (s<0) return 0;
	if (MMpush(m,MMgetglobal(m,OFFSCCUR))) return MERRMEM;
	if (!SCselectcanal(m,s))
	{
		MMechostr(MSKTRACE,"full machine, destroy %d\n",s);
		SCchanneldown(m);
	}
	MMsetglobal(m,OFFSCCUR,MMpull(m));       /* repositionne environnement */
	return 0;
}
#endif





/* traitement d'un message Readable
  (on suppose que le canal courant a ete regle) 
  (le message recu est dans le buffer avec la taille indiquee)
  ne retourne rien dans la pile */
int SCreadchar(mmachine m,int c)
{
  int x,t,s,l,k;
  char *p;

  t=m->top[m->pp]>>1;
  x=m->tape[t+SizeHeader+FIFOSTIN]>>1;

  if (x==SC_SIZE0)
    {
      m->tape[t+SizeHeader+FIFOSZIN]=c*2;
      m->tape[t+SizeHeader+FIFOSTIN]=SC_SIZE1*2;
      return 0;
    }
  if (x==SC_SIZE1)
    {
      m->tape[t+SizeHeader+FIFOSZIN]+=(c<<8)*2+2*2;
      s=m->tape[t+SizeHeader+FIFOSZIN];
      if ((s>=MAXCOM*2)||(s<=2*2))
	{
	  MMechostr(MSKFOO,"#HACK : bad size %x\n",s);
	  SCchanneldown(m);
	  return 1;
	}
      l=((m->tape[t+SizeHeader+FIFOSZIN]>>1)+3)>>2;   /* prepare le buffer */
      s=MMmalloc(m,l,TYPEBUF); if (s==NIL) return MERRMEM;
      t=m->top[m->pp]>>1;
      p=(char*)&m->tape[s+SizeHeader];
      p[0]=p[1]='_';
      m->tape[t+SizeHeader+FIFOIN]=s+s+1;
      m->tape[t+SizeHeader+FIFOCIN]=2*2;
      m->tape[t+SizeHeader+FIFOSTIN]=SC_DATA*2;
      return 0;
    }
  p=(char*)&m->tape[(m->tape[t+SizeHeader+FIFOIN]>>1)+SizeHeader];
  p[m->tape[t+SizeHeader+FIFOCIN]>>1]=c;
  m->tape[t+SizeHeader+FIFOCIN]+=1*2;
  if (m->tape[t+SizeHeader+FIFOCIN]!=m->tape[t+SizeHeader+FIFOSZIN]) return 0;

  if (!strncmp(p,"__@",3))
  {

//  MMechostr(MSKDEBUG,"received %s\n",p);
	  if (!strcmp(p,"__@check"))	  
	  {
		  if (k=SCchecksign(m)) return k;

	  }


//$ ER
#ifdef SERVER
	  else if ((ScolSuper)&&(!strncmp(p,"__@c",4))&&(testlocal(MMstartstr(m,m->tape[t+SizeHeader+FIFONAME]>>1))))
	  {
		  m->tape[t+SizeHeader+FIFOCAPA]=atoi(&p[4])<<1;
		  if (k=SCchecksum(m)) return k;
	  }
//$ ER(09/07/01) turn of #ifdef SERVERM to (!MUTUALISE)&& 
	  else if ((!MUTUALISE)&&(!ScolSuper)&&(!strcmp(p,"__@k"))&&((m->tape[t+SizeHeader+FIFOSOCK]>>1)==socklife))
	  {
		  if (k=SCkilllast(m)) return k;
	  }
#endif


	  //$LB (25/01/2002) : be aware of the f.... gc.
	  t=m->top[m->pp]>>1;
      p=(char*)&m->tape[(m->tape[t+SizeHeader+FIFOIN]>>1)+SizeHeader];


	  //$ LB(18/07/2001)
	  // a client scol machine ask the main machine the informations to authenticate the user by the http proxy
	  if ( ScolSuper && (!strncmp (p, PROXYHTTP_DEFCOM_AUTHENTICATION_REQUEST, strlen(PROXYHTTP_DEFCOM_AUTHENTICATION_REQUEST))))
		PROXYHTTP_GetAuthInfos(m, p);


	  //$LB (25/01/2002) : be aware of the f.... gc.
	  t=m->top[m->pp]>>1;
      p=(char*)&m->tape[(m->tape[t+SizeHeader+FIFOIN]>>1)+SizeHeader];

	  
	  //$ LB(18/07/2001)
	  // receiving http proxy authentication informations from the main machine
	  if (!strncmp (p, PROXYHTTP_DEFCOM_AUTHENTICATION, strlen(PROXYHTTP_DEFCOM_AUTHENTICATION)))
		PROXYHTTP_SetAuthInfos(m, p);



	  t=m->top[m->pp]>>1;
	  m->tape[t+SizeHeader+FIFOIN]=NIL;
	  m->tape[t+SizeHeader+FIFOSTIN]=SC_SIZE0*2;
	  return 0;
  }

  if (k=SCexeccomm(m,p)) return k;
//  MMpull(m);
  t=m->top[m->pp]>>1;
  m->tape[t+SizeHeader+FIFOIN]=NIL;
  m->tape[t+SizeHeader+FIFOSTIN]=SC_SIZE0*2;
  return 0;
}

int SCreadbis(mmachine m,char *buf,int size,int ip,int port)
{
  int i,i0,k,t,l;
  char bufn[128];

//MMechostr(MSKTRACE, "=> SCreadbis(): len = %d\n", size); 

  t=m->top[m->maxpp+OFFSCCUR];  /* lire l'environnement courant */
  if (t==NIL)
    {
      MMechostr(MSKRUNTIME,"Nil environment\n");
      return MERRTYP;
    }
  t>>=1;
  t=m->tape[t+SizeHeader+OFFCHANFIFO]; /* pas de fifo : pas de reception */
  if (t==NIL) return 0;

  if (m->tape[(t>>1)+SizeHeader+FIFOSTOUT]==SC_UDP*2)
    {
      if (k=Mpushstrbloc(m,SCKgetstringbyip(bufn,ip,port))) return k;
      SCsetsockname(m,MMgetglobal(m,OFFSCCUR));

	  l=(buf[0]&255)+((buf[1]&255)<<8)+2;
	  if (l!=size) return 0;
	  buf[0]='_';
	  buf[1]='_';
	  buf[size]=0;
      if (k=SCexeccomm(m,buf)) return k;
//      MMpull(m);
	  return 0;
    }

  if (m->tape[(t>>1)+SizeHeader+FIFOSTOUT]==SC_NOTCON*2)
    {
      if (k=SCexeccomm(m,"_connected")) return k;
//      MMpull(m);
      t=m->top[m->maxpp+OFFSCCUR]>>1;
      t=m->tape[t+SizeHeader+OFFCHANFIFO];
	  if (m->tape[(t>>1)+SizeHeader+FIFOSOCK]==-2) return 0;
      m->tape[(t>>1)+SizeHeader+FIFOSTOUT]=SC_READY*2;
    }
  if (MMpush(m,t)) return MERRMEM; /* empilement du bloc fifo */
  t=MMget(m,0);
  i0=0;
  if (m->tape[(t>>1)+SizeHeader+FIFOSTOUT]==SC_PROXY*2)
    {
	  m->tape[(t>>1)+SizeHeader+FIFOSTOUT]=SC_NOTCON*2;
	  if ((size>=8)&&(buf[1]==90)) SCwrite(m);
	  i0=8;
    }
  for(i=i0;i<size;i++)
  {
	  k=SCreadchar(m,buf[i]&255);
	  if (k)
	  {
		  if (k<0) return k;
		  return 0;
	  }
  }
	  
  MMpull(m);
  return 0;
}

int SCread(mmachine m,char *buf,int size,int ip,int port)
{
  int pp,k;

  pp=m->pp;
  k=SCreadbis(m,buf,size,ip,port);
  m->err=k;
  m->pp=pp;
  return k;
}


/* gestion d'un probleme de socket : postage d'un message close */
int SCproblemSock(mmachine m,int t)
{
  int s;

  s=m->tape[t+SizeHeader+FIFOSOCK]>>1;
  MMechostr(MSKFOO,"problemSock %d\n",s);
  SCKpostclose(s);
//  m->tape[t+SizeHeader+FIFOSOCK]=(-s-1)*2;
  return 0;
}

/* traitement d'un message Writable
  (on suppose que le canal courant a ete regle)
  (ne retourne rien dans la pile)
   retourne 1 si le canal est rompu */
int SCwritebis(mmachine m)
{
  int c,k,t,u,n;
  char *p;

  t=m->top[m->maxpp+OFFSCCUR]>>1;  /* lire l'environnement courant */
  if (t==NIL)
    {
      MMechostr(MSKRUNTIME,"Nil environment\n");
      return MERRTYP;
    }
  t=m->tape[t+SizeHeader+OFFCHANFIFO]>>1; /* pas de fifo : pas d'emission */
  if (t==NIL) return 0;
  if (m->tape[t+SizeHeader+FIFOSOCK]<0) return 0;
  if (m->tape[t+SizeHeader+FIFOSTOUT]==SC_PROXY*2) return 0;
  if (m->tape[t+SizeHeader+FIFOSTOUT]==SC_UDP*2) return 0;

  if (m->tape[t+SizeHeader+FIFOSTOUT]==SC_NOTCON*2)
    {
      if (k=SCexeccomm(m,"_connected")) return k;
//      MMpull(m);
      t=m->top[m->maxpp+OFFSCCUR]>>1;
      t=m->tape[t+SizeHeader+OFFCHANFIFO]>>1;
	  if (m->tape[t+SizeHeader+FIFOSOCK]==-2) return 0;
    }
  m->tape[t+SizeHeader+FIFOSTOUT]=SC_READY*2;
  SCKclrwrite(m->tape[t+SizeHeader+FIFOSOCK]>>1);

  do
  {
      u=m->tape[t+SizeHeader+FIFOOUT];
      if (u==NIL) return 0;
      u>>=1;
      p=(char*)&m->tape[(m->tape[u+SizeHeader+OFFLVAL]>>1)+SizeHeader];
      n=(p[0]&255)+((p[1]&255)<<8)+2;

      c=m->tape[t+SizeHeader+FIFOCOUT]>>1;
      k=SCKsend(m->tape[t+SizeHeader+FIFOSOCK]>>1,&p[c],n-c);
	   /* Gerer la deconnexion lors de l'ecriture */
      if (k<0) return SCproblemSock(m,t);
      if ((c+k)==n)
      {
          m->tape[t+SizeHeader+FIFOCOUT]=0;
          m->tape[t+SizeHeader+FIFOOUT]=m->tape[u+SizeHeader+OFFLNEXT];
      }
      else
          m->tape[t+SizeHeader+FIFOCOUT]+=k*2;
  } while(k);

  MMechostr(MSKWARNING,"UNCOMPLETED WRITE\n");  
  m->tape[t+SizeHeader+FIFOSTOUT]=SC_BUSY*2;
  SCKsetwrite(m->tape[t+SizeHeader+FIFOSOCK]>>1);
  return 0;
}

int SCwrite(mmachine m)
{
  int pp,k;

  pp=m->pp;
  k=SCwritebis(m);
  m->pp=pp;
  if (k==1) k=SCchanneldown(m);
  m->err=k;
  return k;
}

/* envoyer un message de type Comm (present dans la pile) dans la fifo
  (on suppose que le canal courant a ete regle) 
  retourne 1 si le canal est rompu */
int SCsendpile(mmachine m)
{
  int t,k,a,b,n,max,s;

  if (MMpush(m,NIL)) return MERRMEM;
  if (MMpush(m,2*2)) return MERRMEM;
  if (k=MBdeftab(m)) return k;

  t=m->top[m->maxpp+OFFSCCUR];  /* lire l'environnement courant */
  if (t==NIL)
    {
      MMechostr(MSKRUNTIME,"Nil environment\n");
      return MERRTYP;
    }
  t>>=1;
  t=m->tape[t+SizeHeader+OFFCHANFIFO]; /* pas de fifo : pas d'emission */
  if (t==NIL)
    {
      MMpull(m);
      return 0;
    }
  t>>=1;
  s=m->tape[t+SizeHeader+FIFOSOCK]>>1;
  if (s<0)
    {
       a=MMget(m,0)>>1;
       b=MMfetch(m,a,0)>>1;
       if ((s==FirstUnplugged)&&(LocalMessage))
		   (*LocalMessage)((char*)MMstartstr(m,b)-2);
       MMpull(m);
       return 0;
    } 

  n=MMsize(m,MMget(m,0)>>1);
  a=t+SizeHeader+FIFOOUT;
  while((b=m->tape[a])!=NIL)
  {
	n+=MMsize(m,MMfetch(m,b>>1,OFFLVAL)>>1);
	a=(b>>1)+SizeHeader+OFFLNEXT;
  }
  m->tape[a]=MMpull(m);
  max=m->tape[t+SizeHeader+FIFOMAX]>>1;
  if ((max>=0)&&(n>max)) return SCproblemSock(m,t);

  if (m->tape[t+SizeHeader+FIFOSTOUT]==SC_READY*2) return SCwrite(m);
  return 0;
}






