/*--------------------------------------------------------------------------

	Connection management for SICS. This is one the core files for
	SICS. Does a lot. See the descriptions with individual functions
	below.
	


	Mark Koennecke,    October 1996

        SMInvoke added. Mark Koennecke, April 1997

        Seriously revised and extended for new structure with Tasker:
        Mark Koennecke, September 1997
        
        Support for writing telnet compatible strings ins SCWrite added.
        Mark Koennecke, January 1998 
                
        SCWriteBinary added. Mark Koennecke, April 1998

	Revamped login to non telnet connection.
        Added compressed writing method. 
        Mark Koennecke, October 2000
                
	Copyright: see copyright.h
-----------------------------------------------------------------------------*/
#include "fortify.h"
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <zlib.h>
#include <tcl.h>
#include <time.h>
#include "lld.h"
#include "sics.h"
#include "passwd.h"
#include "splitter.h"
#include "macro.h"
#include "servlog.h"
#include "status.h"
#include "interrupt.h"
#include "ifile.h"
#include "token.h"
#include "uubuffer.h"
#include "commandlog.h"

/*
#define UUDEB 1
 define UUDEB , for buffer writing for checking encoding */

extern pServer pServ;

/*------ Max Size of Command Stack */
#define MAXSTACK 100
/*---------- Magic ID Header */
#define CONMAGIC 26051958
/*-------------------------------------------------------------------------
  a structure for holding callback info 
  */
  typedef struct {
                 long lID;
                 pICallBack pInterface;
                 } Item;                                                  

/*------------- a number for generating automatic names --------------------*/
  static int iName = 0;
  static int SCNormalWrite(SConnection *self, char *buffer, int iOut);

/*===========================================================================*/
   SConnection *SCreateConnection(SicsInterp *pSics,mkChannel *pSock, int iUser)
  {
     int i;
     SConnection *pRes = NULL;
     char pBueffel[253];
     char pHost[132];
     
     pRes = (SConnection *)malloc(sizeof(SConnection));
     if(!pRes)
     {
       /* This is a serious, very serious error! */
       SICSLogWrite("ERROR: No memory to allocate connection!!",eInternal);
       return NULL;
     }
     memset(pRes,0,sizeof(SConnection));
     
     /* a descriptor */
     pRes->pDes = CreateDescriptor("Connection");
     if(!pRes->pDes)
     {
       /* This is a serious, very serious error! */
       SICSLogWrite("ERROR: No memory to allocate connection!!",eInternal);
       free(pRes);
       return NULL;
     }

      /* new name */
      sprintf(pBueffel,"CON%4.4d",iName);
      iName++;
      if(iName > 9999)
      {
        iName = 0;
      }
     
     /* the callback registry */
     pRes->iList = LLDcreate(sizeof(Item));
     
     /* the command stack */
     pRes->pStack = CreateCommandStack();
     if( (pRes->iList <0) || (!pRes->pStack))
     {
       /* This is a serious, very serious error! */
       SICSLogWrite("ERROR: No memory to allocate connection!!",eInternal);
       DeleteDescriptor(pRes->pDes);
       free(pRes);
       return NULL;
     }
     SetCommandStackMaxSize(pRes->pStack,MAXSTACK);

     pRes->pName = strdup(pBueffel);     
     pRes->pSock = pSock;
     pRes->iUserRights = iUser;
     pRes->iOutput = eInError; /* gets everything except internal messages */
     pRes->iFiles = 0;  /* default: no logfiles */
     pRes->inUse = 0;
     pRes->iMacro = 0;
     pRes->iGrab = TokenGrabActive();
     pRes->iTelnet = 0;
     pRes->pSics = pSics;
     pRes->eInterrupt = eContinue;
     pRes->lMagic = CONMAGIC;
     pRes->iDummy = 0;
     pRes->iLogin = 0;
     pRes->conStart = time(NULL);
     pRes->write = SCNormalWrite;
     for(i = 0; i < 10; i++)
     {
       pRes->pFiles[i] = NULL;
     }   
     if(pRes->pSock)
     {
       NETInfo(pRes->pSock,pHost,131);
       sprintf(pBueffel,"Accepted connection on socket %d from %s",
               pRes->pSock->sockid, pHost);
       SICSLogWrite(pBueffel,eInternal);
       WriteToCommandLog("SYS >", pBueffel);
     }
     else
     {
       SICSLogWrite("Accepted dummy connection ",eInternal);
     }
     
     /* install command */
     AddCommand(pRes->pSics, pRes->pName, ConSicsAction, NULL,pRes);
     return pRes;   
     
  }
/*--------------------------------------------------------------------------*/
  SConnection *SCCreateDummyConnection(SicsInterp *pSics)
  {
     int i;
     SConnection *pRes = NULL;
     char pBueffel[132];
     
     pRes = (SConnection *)malloc(sizeof(SConnection));
     if(!pRes)
     {
       /* This is a serious, very serious error! */
       SICSLogWrite("ERROR: No memory to allocate connection!!",eInternal);
       return NULL;
     }
     memset(pRes,0,sizeof(SConnection));
     
     /* a descriptor */
     pRes->pDes = CreateDescriptor("Connection");
     if(!pRes->pDes)
     {
       /* This is a serious, very serious error! */
       SICSLogWrite("ERROR: No memory to allocate connection!!",eInternal);
       free(pRes);
       return NULL;
     }

      /* new name */
      sprintf(pBueffel,"CON%4.4d",iName);
      iName++;
      if(iName > 9999)
      {
        iName = 0;
      }
     
     /* the callback registry */
     pRes->iList = LLDcreate(sizeof(Item));
     
     /* the command stack */
     pRes->pStack = CreateCommandStack();
     if( (pRes->iList <0) || (!pRes->pStack))
     {
       /* This is a serious, very serious error! */
       SICSLogWrite("ERROR: No memory to allocate connection!!",eInternal);
       DeleteDescriptor(pRes->pDes);
       free(pRes);
       return NULL;
     }

     pRes->pName = strdup(pBueffel);     
     pRes->pSock = NULL;
     pRes->iUserRights = usInternal;
     pRes->iOutput = eInError; /* gets everything except internal messages */
     pRes->iFiles = 0;  /* default: no logfiles */
     pRes->inUse = 0;
     pRes->iTelnet = 0;
     pRes->iGrab = 0;
     pRes->iMacro = 0;
     pRes->pSics = pSics;
     pRes->lMagic = CONMAGIC;
     pRes->eInterrupt = eContinue;
     pRes->iDummy = 0;
     pRes->iLogin = 0;
     pRes->conStart = time(NULL);
     pRes->write = SCNormalWrite;
     for(i = 0; i < 10; i++)
     {
       pRes->pFiles[i] = NULL;
     }   
     if(pRes->pSock)
     {
       sprintf(pBueffel,"Accepted connection on socket %d",pRes->pSock->sockid);
       SICSLogWrite(pBueffel,eInternal);
     }
     else
     {
       SICSLogWrite("Accepted dummy connection ",eInternal);
     }
     
     /* install command */
     AddCommand(pRes->pSics, pRes->pName, ConSicsAction, NULL,pRes);
     return pRes;   
  }    
/*--------------------------------------------------------------------------*/
  static int VerifyConnection(SConnection *self)
  {
    if(!self)
    {
      SICSLogWrite("MAGICERROR: Invalid call to NULL connection",eError);
      return 0;
    }
    if(self->lMagic != CONMAGIC)
    {
      SICSLogWrite("MAGICERROR: corrupted connection object",eError);
      return 0;
    }
    return 1;
  }  
/*----------------------------------------------------------------------------*/
  int SCAddLogFile(SConnection *self, char *name)
  {
     char pBueffel[256];
     int iNum, i;

     if(!VerifyConnection(self))
     {
       return 0;
     }
     
     
     /* find an empty number */
     if(self->iFiles < MAXLOGFILES)
     {
       iNum = self->iFiles;
       self->iFiles++;
     }
     else /* scan for an empty slot */
     {
       iNum = -1;
       for(i = 0; i < self->iFiles; i++)
       {
         if(self->pFiles[i] == NULL)
         {
           iNum = i;
           break;
         }
       }
     }
     /* nothing found ? */
     if(iNum < 0)
     {
       SCWrite(self,"ERROR: maximum number of logfiles exhausted",eError);
       return -1;
     }
     
     /* do the job */
     self->pFiles[iNum] = fopen(name,"a+");
     if(self->pFiles[iNum] == NULL)
     {
       sprintf(pBueffel,"ERROR Could not open logfile - %s -",name);
       SCWrite(self,pBueffel, 10);
       return -1; /* false */
     }
     else
     {
       return iNum; /* success */
     }
  } 
/*--------------------------------------------------------------------------*/
  int SCDelLogFile(SConnection *self, int iNum)
  {
     if(!VerifyConnection(self))
     {
       return 0;
     }

     if( (iNum >= 0) && (iNum < MAXLOGFILES)  )
     {
        if(self->pFiles[iNum])
        {
          fclose(self->pFiles[iNum]);
          self->pFiles[iNum] = NULL;
          return 1;
        }
     }      
     return 0;
  }  
/*----------------------------------------------------------------------------*/
  void SCSetOutputClass(SConnection *self, int iClass)
  {
     if(!VerifyConnection(self))
     {
       return;
     }
    self->iOutput = iClass;
  }
/*---------------------------------------------------------------------------*/
  int SCinMacro(SConnection *self)
  {
     if(!VerifyConnection(self))
     {
       return 0;
     }
    
    if(self->iMacro)
    {
      return 1;
    }
    else
    {
      return 0;
    }
  }  
/*---------------------------------------------------------------------------*/
  int SCsetMacro(SConnection *self, int iMode)
  {
     if(!VerifyConnection(self))
     {
       return 0;
     }
    assert( (iMode == 0) || (iMode == 1));
    self->iMacro = iMode;
    return 1;
  }   
/*---------------------------------------------------------------------------*/
  void SCDeleteConnection(void *pData)
  {
    int i, iRet;
    char pBueffel[132];
    SConnection *pVictim = NULL;
    Item sItem;
    
    pVictim = (SConnection *)pData;
     if(!VerifyConnection(pVictim))
     {
       return;
     }
    
     if(pVictim->inUse  > 0)
     {
       SCnoSock(pVictim);
       if(pVictim->pSock)
       {
         NETClosePort(pVictim->pSock);
         free(pVictim->pSock);
         pVictim->pSock = NULL;
       } 
       WriteToCommandLog("SYS> ",
              "ERROR: Erraneous deletion of used Connection stopped");
       return;
     }
     
     
     /* remove the connection from the server log if it has captured
        something
     */
    KillCapture(pVictim);

    /* 
       If we have a grab, release it !
    */
    if(!pVictim->iGrab)
    {
      if(pServ->pTasker)
      {
        TaskSignal(pServ->pTasker,TOKENRELEASE,NULL);
        TokenRelease();
      }
    }   

    /* log the kill */ 
    if(pVictim->pSock)
    {
      sprintf(pBueffel,"Deleting connection %d",pVictim->pSock->sockid);
      WriteToCommandLog("SYS>",pBueffel);
      SICSLogWrite(pBueffel,eInternal);
    }
    
    /* close all open files and sockets */
    if(pVictim->pSock)
    {
       NETWrite(pVictim->pSock,"SICSCLOSE",sizeof("SICSCLOSE"));
       NETClosePort(pVictim->pSock);
       free(pVictim->pSock);
    }
    for(i = 0; i < pVictim->iFiles; i++)
    {
      fclose(pVictim->pFiles[i]);
    }    

    if(pVictim->pName)
    {
      RemoveCommand(pVictim->pSics,pVictim->pName);
      free(pVictim->pName);
    }
    
     if(pVictim->pDes)
     {
       DeleteDescriptor(pVictim->pDes);
     }
     
     /* remove all callbacks on this connection */
     iRet = LLDnodePtr2First(pVictim->iList);
     while(iRet != 0)
     {
        LLDnodeDataTo(pVictim->iList,&sItem);
        RemoveCallback(sItem.pInterface, sItem.lID);
        iRet = LLDnodePtr2Next(pVictim->iList);
     }
     LLDdelete(pVictim->iList);
     
     /* remove standing data connections */
     if(pVictim->pDataSock)
     {
        NETClosePort(pVictim->pDataSock);
        free(pVictim->pDataSock);
        free(pVictim->pDataComp);
     }

    /* remove command stack */
    if(pVictim->pStack)
    {
      DeleteCommandStack(pVictim->pStack);
    }
    /* finally free pVictim*/
    free(pVictim);   
  }
/*---------------------------------------------------------------------------*/
  static int HasNL(char *buffer)
  {
    int i; 
     for(i = strlen(buffer); i > 0; i--)
     {
       if(isprint(buffer[i]))
       {
         break;
       }
       if(buffer[i] == '\n')
       {
         return 1;
       }
     } 
     return 0;
  }   
/*-------------------------------------------------------------------------
  TelnetWrite makes sure, that all lines are properly terminated with a
  <cr><lf> as required by the telnet protocoll. 
  
  There may be a problem here at long messages. 7.5.1998 MK
--------------------------------------------------------------------------*/  
#define TXT 0
#define LF  1

  int TelnetWrite(mkChannel *pSock, char *pBuffer)
  {
     char *pStart = NULL, *pPtr;
     int iCount, iState;
     int iRet = 1;
     
     pStart = pBuffer;
     pPtr = pStart;
     iState = TXT;
     iCount = 0;
     while(*pPtr != '\0')
     {
       switch(iState)
       {
           case TXT:
                   if( (*pPtr == '\r') || (*pPtr == '\n') )
                   {
                      iState = LF;
                      iRet = NETWrite(pSock,pStart,iCount);        
                      iRet = NETWrite(pSock,"\r\n",2);
                      iCount = 0;
                   } 
                   else
                   {
                      iCount++;
                   }
                   break;
          case LF:
                   if( (*pPtr != '\r') && (*pPtr != '\n') )
                   {
                      pStart = pPtr;
                      iCount = 1;
                      iState = TXT;
                   }         
                   else
                   { 
                      /* do nothing */
                   }
                   break;   
       }
       pPtr++;
     }
     if(iCount > 0)
     {
         iRet = NETWrite(pSock,pStart,iCount);        
         iRet = NETWrite(pSock,"\r\n",2);
     }
     return iRet;
  }  
/*-------------------------------------------------------------------------*/
  int SCWrite(SConnection *self, char *pBuffer, int iOut)
  {
      if(!VerifyConnection(self))
      {
        return 0;
      }
      return self->write(self,pBuffer,iOut);
  }
/*--------------------------------------------------------------------------*/
  static int SCNormalWrite(SConnection *self, char *buffer, int iOut)
  {
       int i, iPtr, iRet;
       char pBueffel[80];

      if(!VerifyConnection(self))
      {
        return 0;
      }

     /* log it for any case */
       if(self->pSock)
       { 
         iRet = self->pSock->sockid;
       }
       else
       {
         iRet = 0;
       } 
       sprintf(pBueffel,"Next line intended for socket: %d",iRet);
       SICSLogWrite(pBueffel,eInternal);
       SICSLogWrite(buffer,iOut);
       
     /* write to commandlog if user or manager privilege */
     if(SCGetRights(self) <= usUser)
     {
          sprintf(pBueffel,"To sock %d :",iRet);
          WriteToCommandLog(pBueffel,buffer);
     }  
     
     /* put it into the interpreter if present */
     if(SCinMacro(self))
     {
       InterpWrite(self->pSics,buffer);
       /* print it to client if error message */
       if((iOut== eError) || (iOut == eWarning) )
       {
         if(self->pSock)
         {
           if(self->iTelnet)
           {
             iRet = TelnetWrite(self->pSock,buffer);
           }
           else
           {
             iRet = NETWrite(self->pSock,buffer,strlen(buffer));
             if(!HasNL(buffer))
             {
               iRet = NETWrite(self->pSock,"\n",sizeof("\n"));
             }  
           }
           if(!iRet)
           {
              SCnoSock(self);
              WriteToCommandLog("SYS> ","Connection broken on send");
           }
         }
         else
         {
           puts(buffer);
         }
       }
     }
     else /* not in interpreter, normal logic */
     {
       /* is this really to be printed ? */
       if(iOut < self->iOutput)
         return 0;
      
       /* first the socket */
       if(self->pSock)
       {
         if(self->iTelnet)
         {
           iRet = TelnetWrite(self->pSock,buffer);
         }
         else
         {
           iRet = NETWrite(self->pSock,buffer,strlen(buffer));
           if(!HasNL(buffer))
           {
             iRet = NETWrite(self->pSock,"\n",sizeof("\n"));
           }  
         }
         if(!iRet)
         {
           SCnoSock(self);
           WriteToCommandLog("SYS> ","Send broken to connection");
         }
       }
       else
       {
         printf("%s \n",buffer);
       }
     
       /* now all the possible logfiles */
       for(i = 0; i < self->iFiles; i++)
       {
         if(self->pFiles[i])
         {
           fputs(buffer,self->pFiles[i]);
           if(! HasNL(buffer))
           { 
             fputs("\n",self->pFiles[i]);
             fflush(self->pFiles[i]);
           }
         }  
       }
     }      
     return 1;    
  }
/*--------------------------------------------------------------------------
  This version writes only to configured log files but not to sockets.
  Used for automatic file execution for the WWW interface
*/
  static int SCFileWrite(SConnection *self, char *buffer, int iOut)
  {
       int i, iPtr, iRet;
       char pBueffel[80];

      if(!VerifyConnection(self))
      {
        return 0;
      }

      /* put into Serverlog */
       sprintf(pBueffel,"Next line intended for socket: %d",-10);
       SICSLogWrite(pBueffel,eInternal);
       SICSLogWrite(buffer,iOut);

     /* write to commandlog if user or manager privilege */
     if(SCGetRights(self) <= usUser)
     {
          sprintf(pBueffel,"To sock %d :",-10);
          WriteToCommandLog(pBueffel,buffer);
     }  
     
     /* put it into the interpreter if present */
     if(SCinMacro(self))
     {
       InterpWrite(self->pSics,buffer);
     }
     else /* not in interpreter, normal logic */
     {
       /* is this really to be printed ? */
       if(iOut < self->iOutput)
         return 0;
      
       /* now all the possible logfiles */
       for(i = 0; i < self->iFiles; i++)
       {
         if(self->pFiles[i])
         {
           fputs(buffer,self->pFiles[i]);
           if(! HasNL(buffer))
           { 
             fputs("\n",self->pFiles[i]);
             fflush(self->pFiles[i]);
           }
         }  
       }
     }      
     return 1;    
  }
/*-----------------------------------------------------------------------*/
  int SCnoSock(SConnection *self)
  {
      if(!VerifyConnection(self))
      {
        return 0;
      }
      self->write = SCFileWrite;
  }
/*-------------------------------------------------------------------------*/
  int SCWriteBinary(SConnection *pCon, char *pComputer, int iPort, 
                    void *pData, int iDataLen)
  {
     int iRet;
     char pBueffel[1024];
     mkChannel *pChan = NULL;
     
     assert(pCon);

     /* do we have an identical connection already */
     if(pCon->pDataSock)
     {
        if(strcmp(pComputer,pCon->pDataComp) == 0 && (pCon->iDataPort == iPort))
        {
           pChan = pCon->pDataSock;
        }
        else /* rubbish, kill it */
        {
           NETClosePort(pCon->pDataSock);
           free(pCon->pDataSock);
           free(pCon->pDataComp);
           pCon->pDataSock = NULL;
           pCon->pDataComp = NULL;
        }
     }
     
     /* we have none, open it! */
     if(!pChan)
     {
       pChan = NETConnect(pComputer, iPort);
       if(!pChan)
       {
         sprintf(pBueffel,"ERROR: cannot connect to %s %d",pComputer, iPort);
         SCWrite(pCon,pBueffel,eError);
         SCWrite(pCon,"EOFBINARYERROR",eValue);
         return 0;
       }
       pCon->pDataSock = pChan;
       pCon->pDataComp = strdup(pComputer);
       pCon->iDataPort = iPort;
     }  
     
     /* do the writing */
     iRet = NETWrite(pChan,pData,iDataLen);
     if(iRet != 1)
     {
       sprintf(pBueffel,"ERROR: failed to write data to %s %d",pComputer, iPort);
       SCWrite(pCon,pBueffel,eError);
       SCWrite(pCon,"EOFBINARYERROR",eValue);
       NETClosePort(pChan);
       free(pCon->pDataSock);
       free(pCon->pDataComp);
       pCon->pDataSock = NULL;
       pCon->pDataComp = NULL;
       return 0;
     }
     
     SCWrite(pCon,"EOFBINARYOK",eValue);
     return 1;
  } 
/*------------------------------------------------------------------------*/
  int SCWriteUUencoded(SConnection *pCon, char *pName, void *pData, 
                       int iDataLen)
  {
     void *pPtr = NULL;
     int iLength = 0;
     int iRet = 1;
     FILE *fd;
     char *pTest;
     
     iRet = UUencodeBuffer(pData,iDataLen,pName, &pPtr, &iLength);
     if(iRet != 1)
     {
       SCWrite(pCon,"ERROR: no memory for uuencoder",eError);
       return 0;
     }
     pTest = (char *)pPtr;     

     /* the uuencoder ensures proper telnet <cr><lf> */
     if(pCon->iTelnet)
     {
       NETWrite(pCon->pSock,pPtr,iLength);
     }
     else
     {
       NETWrite(pCon->pSock,pPtr,iLength);
     }
      
#ifdef UUDEB
      fd = fopen("uubuffer.uu","w");
      fputs(pPtr,fd);
      fclose(fd);
#endif      
     
     free(pPtr);
     return iRet;
  }                     
/*------------------------------------------------------------------------*/
#define ZIPBUF 8192
  int SCWriteZipped(SConnection *self, char *pName, void *pData, int iDataLen)
  {
     char outBuf[65546], *pBuf = NULL, noutBuf[ZIPBUF], *pHeader = NULL;
     int compressedLength, iRet, iRet2, iCount;
     z_stream compStream;
     
     /* check for a valid connection */
     if(!VerifyConnection(self))
     {
        return 0;
     }

     /* a telnet connection will corrupt the compressed stream, so
        stop it!
     */
     if(self->iTelnet)
     {
       SCWrite(self,
	       "ERROR: the telnet protocoll will currupt compressed data!",
               eError);
        return 0;
     }

     /* initialize the compression stuff */
     compStream.zalloc =  (alloc_func)NULL;
     compStream.zfree  =  (free_func)NULL;
     compStream.opaque = (voidpf)NULL;

     iRet = deflateInit(&compStream,Z_DEFAULT_COMPRESSION);
     if(iRet !=  Z_OK)
     {
        sprintf(outBuf,"ERROR: zlib error: %d",iRet);
        SCWrite(self,outBuf,eError);
        return 0;
     }  

     /* first pass: find out how the long the compressed buffer will be */
     compressedLength = 0;
     compStream.next_in = (Bytef *)pData;
     compStream.next_out = (Bytef *)outBuf;
     compStream.avail_in = iDataLen;     
     compStream.avail_out = 65536;
     while(compStream.total_in < iDataLen)
     {
        iRet = deflate(&compStream,Z_NO_FLUSH);
	if(iRet !=  Z_OK)
	{
	   sprintf(outBuf,"ERROR: zlib error: %d",iRet);
	   SCWrite(self,outBuf,eError);
	   return 0;
	}  
        compStream.next_out = (Bytef *)outBuf;
        compStream.avail_out = 65536;
     }
     for(;;)
     {
       iRet = deflate(&compStream,Z_FINISH);
       if(iRet == Z_STREAM_END) break;
       if(iRet !=  Z_OK)
       {
	   sprintf(outBuf,"ERROR: zlib error: %d",iRet);
	   SCWrite(self,outBuf,eError);
	   return 0;
       }  
     }
     compressedLength = compStream.total_out;
     deflateEnd(&compStream);

     /* write header line */
     memset(outBuf,0,65536);
     sprintf(outBuf,"SICSBIN ZIP %s %d\r\n",pName,compressedLength);
     pHeader = strdup(outBuf);
     if(pHeader == NULL)
     {
       SCWrite(self,"ERROR: out of memory in SCWriteZipped",eError);
       return 0;
     } 

     /* now reset the deflater and do the same with writing data */
     compStream.zalloc =  (alloc_func)NULL;
     compStream.zfree  =  (free_func)NULL;
     compStream.opaque = (voidpf)NULL;


     /* 
        This is writing everything in one go as I found that writing in
        several chunks did not ensure that all the data arrived at the
        Java side.
     */
    
     iRet = deflateInit(&compStream,Z_DEFAULT_COMPRESSION);
     if(iRet !=  Z_OK)
     {
        sprintf(outBuf,"ERROR: zlib error: %d",iRet);
        SCWrite(self,outBuf,eError);
        return 0;
     }  

     pBuf = (char *)malloc((iDataLen + iDataLen/10 + 50)*sizeof(char));
     memset(pBuf,0,(iDataLen + iDataLen/10 + 50)*sizeof(char) );
     compStream.next_in = (Bytef *)pData;
     compStream.next_out = (Bytef *)pBuf;
     compStream.avail_in = iDataLen;     
     compStream.avail_out = iDataLen + iDataLen/10 + 50;
     iRet = deflate(&compStream,Z_FINISH);
     if(iRet != Z_STREAM_END) 
     {
	   sprintf(outBuf,"ERROR: zlib error: %d",iRet);
	   SCWrite(self,outBuf,eError);
	   return 0;
     }
     iRet = NETWrite(self->pSock,pHeader,strlen(pHeader));
     iRet = NETWrite(self->pSock,pBuf,compStream.total_out);
     if(iRet != 1)
     {
           sprintf(outBuf,"ERROR: network error %d on zipped send",iRet);
           SCWrite(self,outBuf,eError);
           return 0;
     }
     deflateEnd(&compStream);
     free(pHeader);
     free(pBuf);


     /*
        Writing smaller buffers. Seems not to be working properly
        with Java.        
     */        
     /*
     compStream.next_in = (Bytef *)pData;
     compStream.avail_in = iDataLen;     
     compStream.avail_out = ZIPBUF;
     compStream.next_out = (Bytef *)noutBuf;
     iCount = 0;
     while(compStream.total_in < iDataLen)
     {
        iRet = deflate(&compStream,Z_NO_FLUSH);
	if(iRet !=  Z_OK)
	{
	   sprintf(outBuf,"ERROR: zlib error: %d",iRet);
	   SCWrite(self,outBuf,eError);
	   return 0;
	}  
        iRet = NETWrite(self->pSock,noutBuf,ZIPBUF - compStream.avail_out);
        if(iRet != 1)
	{
           sprintf(outBuf,"ERROR: network error %d on zipped send",iRet);
           SCWrite(self,outBuf,eError);
           return 0;
	}
        iCount +=  ZIPBUF - compStream.avail_out;
	compStream.next_out = (Bytef *)noutBuf;
        compStream.avail_out = ZIPBUF;
     }
     for(;;)
     {
        iRet = deflate(&compStream,Z_FINISH);
        iRet2 = NETWrite(self->pSock,noutBuf,ZIPBUF - compStream.avail_out);
        if(iRet2 != 1)
	{
           sprintf(outBuf,"ERROR: network error %d on zipped send",iRet);
           SCWrite(self,outBuf,eError);
           return 0;
	}
       iCount += ZIPBUF - compStream.avail_out;
       if(iRet == Z_STREAM_END) break;
       if(iRet !=  Z_OK)
       {
	   sprintf(outBuf,"ERROR: zlib error: %d",iRet);
	   SCWrite(self,outBuf,eError);
	   return 0;
       }  
       compStream.next_out = (Bytef *)noutBuf;
       compStream.avail_out = ZIPBUF;
     }
     deflateEnd(&compStream);
     */

     return 1;
  }
/*-------------------------------------------------------------------------*/
  int SCSendOK(SConnection *self)
  {
     return SCWrite(self,"OK",eStatus);
  }  
/*--------------------------------------------------------------------------*/
  int SCRead(SConnection *self, char *buffer, int iLen)
  {
    
    int iRet;

    if(!VerifyConnection(self))
    {
        return 0;
    }
    
    if(self->pSock == NULL)
    {
      printf("SICS>> ");
      fgets(buffer,iLen-1,stdin);
      return 1;
    }
    
    if(self->pSock)
    {
        iRet = NETRead(self->pSock,buffer,iLen,10);
        if(iRet == 0) /* no data */ 
        {
           return 0;
        }
        else if(iRet < 0) /* eof */
        {
          return EOF;
        } 
        else /* data */
        {
          return 1;
        }
    }
    else
    {
       return EOF;
       /* fgets(buffer,iLen,stdin); */
    }
    return 1;
  }
/*----------------------------------------------------------------------------*/
   int SCMatchRights(SConnection *pCon, int iCode)
   {
      char pBueffel[132];

      if(!VerifyConnection(pCon))
      {
        return 0;
      }
    
      if(iCode < SCGetRights(pCon))
      {
         sprintf(pBueffel,"ERROR: you are not authorised for this operation");
         SCWrite(pCon, pBueffel, eError);
         return 0;
       }
       if(pCon->iGrab)
       {
         sprintf(pBueffel,"ERROR: Request refused, control has been grabbed by somebody else");
         SCWrite(pCon,pBueffel,eError);
         return 0;
       }
       return 1;
   }

/*----------------------------------------------------------------------------*/
 int SCPrompt(SConnection *pCon, char *pPrompt, char *pResult, int iLen)
 {
        
        int iRet, i;
        char *pPtr = NULL;
        char pFrom[50];
        Status eOld;

        if(!VerifyConnection(pCon))
        {
          return 0;
        }

        SCWrite(pCon,pPrompt,eWarning);
        eOld = GetStatus();
        SetStatus(eInput);
        CostaUnlock(pCon->pStack);
        while(1)
	{
          /* wait a second */
    	  SicsWait(1); 
          /* is there an interrupt pending ? */
          if(SCGetInterrupt(pCon) != eContinue)
	  {
            break;
          }         
          /* do we have data ? */
          iRet = CostaPop(pCon->pStack,&pPtr);
          if(iRet == 1)
	  {
             SetStatus(eOld);
             CostaLock(pCon->pStack);
             strncpy(pResult,pPtr,iLen);
             sprintf(pFrom,"Prompted from sock %2.2d: ", pCon->pSock->sockid);
             WriteToCommandLog(pFrom,pPtr);
             return 1;
          }
        }
        SetStatus(eOld);
        CostaLock(pCon->pStack);
        return 0;      
 }       
/*---------------------------------------------------------------------------*/
  int SCGetRights(SConnection *self)
  {
        if(!VerifyConnection(self))
        {
          return 0;
        }
    
        return self->iUserRights;
  }
/*---------------------------------------------------------------------------*/
  int SCGetGrab(SConnection *self)
  {
    if(!VerifyConnection(self))
    {
          return 0;
    }
    
    return self->iGrab;
  }
/*--------------------------------------------------------------------------*/
  int SCSetRights(SConnection *self, int iNew)
  {
    if(!VerifyConnection(self))
    {
          return 0;
    }
    assert(iNew >= usInternal);
    assert(iNew <= usSpy);
    
    self->iUserRights = iNew;
    return 1;
  }  
/*---------------------------------------------------------------------------*/
  int SCGetOutClass(SConnection *self)
  {
    if(!VerifyConnection(self))
    {
          return 0;
    }
      return self->iOutput;
  }
/*--------------------------------------------------------------------------*/
  void SCSetInterrupt(SConnection *self, int eCode)
  {
    if(!VerifyConnection(self))
    {
          return;
    }
     
     self->eInterrupt = eCode;
  }  
/*---------------------------------------------------------------------------*/
  int SCGetInterrupt(SConnection *self)
  {
    if(!VerifyConnection(self))
    {
          return 0;
    }
     
     return self->eInterrupt;
  }
/*--------------------------------------------------------------------------*/    
  void SCSetError(SConnection *self, int eCode)
  {
    if(!VerifyConnection(self))
    {
          return;
    }
     
     self->iErrCode = eCode;
  }  
/*---------------------------------------------------------------------------*/
  int SCGetError(SConnection *self)
  {
    if(!VerifyConnection(self))
    {
          return 0;
    }
     
     return self->iErrCode;
  }
/* --------------------------------------------------------------------------*/
  int SCInvoke(SConnection *self, SicsInterp *pInter, char *pCommand)
  {
     int iRet;
     long lLen;
     const char *pResult = NULL;
     char *pBuffer = NULL;
     char pBueffel[80];
     int i, iSpace;
     
     if(!VerifyConnection(self))
     {
          return 0;
     }
     assert(pInter);
     
     /* print command to log files */
     for( i = 0; i < self->iFiles; i++)
     {
       if(self->pFiles[i])
       {
         fprintf(self->pFiles[i],"SICS>> %s\n",pCommand);
       }
     }
     
     /* print to command log if user or manager */
     if(SCGetRights(self) <= usUser)
     {
        if(self->pSock != NULL)
	{
          sprintf(pBueffel,"sock %d>>",self->pSock->sockid);
        }
        else
	{
          strcat(pBueffel,"CONT or CRON>> ");
	}
        WriteToCommandLog(pBueffel,pCommand);
     }
     
     /* invoke */
     self->inUse++;
     self->eInterrupt = eContinue;
     self->iErrCode = OKOK;
     iRet = InterpExecute(pInter,self,pCommand);
     self->inUse--;
     return iRet;
  }
/*---------------------------------------------------------------------------
   For configuring connections. Syntax:
      config OutCode val             sets an new output code
      config Rights  User Password   sets and verifies new user rights
      config File    Filename        Logs to another file
*/
#include "outcode.c" /* text for OutCode */
   
   int ConfigCon(SConnection *pCon, SicsInterp *pSics, void *pData, 
                 int argc, char *argv[])
   {
       char pBueffel[512];
       int i, iRet;
       int iNum;
       
       if(!VerifyConnection(pCon))
       {
          return 0;
       }
       assert(pSics);
       
       /* check no of args */
       if(argc < 2)
       {
         sprintf(pBueffel,"Insufficient number of args to %s",argv[0]);
         SCWrite(pCon,pBueffel,eError);
         return 0;       
       }
       
       /* handle list*/
       strtolower(argv[1]);
       if(strcmp(argv[1],"list") == 0)
       {
         sprintf(pBueffel,"OutCode = %s",pCode[pCon->iOutput]);
         SCWrite(pCon,pBueffel,eStatus);
         sprintf(pBueffel,"UserRights = %d",SCGetRights(pCon));
         SCWrite(pCon,pBueffel,eStatus);
         return 1;
       }
       else if(strcmp(argv[1],"myname") == 0)
       {
         sprintf(pBueffel,"MyName = %s",pCon->pName);
         SCWrite(pCon,pBueffel,eValue);
         return 1;
       }
       else if(strcmp(argv[1],"myrights") == 0)
       {
          sprintf(pBueffel,"UserRights = %d",SCGetRights(pCon));
          SCWrite(pCon,pBueffel,eValue);
          return 1;
       }
       
       /* check no or args */
       if(argc < 3)
       {
         sprintf(pBueffel,"Insufficient number of args to %s",argv[0]);
         SCWrite(pCon,pBueffel,eInError);
         return 0;
       }
       
       /* decide what to do */
       if(strcmp(argv[1],"file") == 0)
       {
          iRet = SCAddLogFile(pCon,argv[2]);
          if(iRet >= 0 )
          {
            sprintf(pBueffel,"File = %d",iRet);
            SCWrite(pCon,pBueffel,eStatus);
            return 1;
          }          
       }
       if(strcmp(argv[1],"close") == 0) /* close file */
       {
         iNum = atoi(argv[2]);
         if( (iNum >= 0) && (iNum < MAXLOGFILES))
         {
           if(pCon->pFiles[iNum])
           {
             fclose(pCon->pFiles[iNum]);
             pCon->pFiles[iNum] = NULL;
             SCSendOK(pCon);
             return 1;
           }
         }
         else
         {
           SCWrite(pCon, "Invalid file number specified ",eError);
           return 0;
         }
       }
       else if(strcmp(argv[1],"outcode") == 0)
       {
         i = 0;
         strtolower(argv[2]);
         while(pCode[i] != NULL)
         {
           if(strcmp(pCode[i],argv[2]) == 0)
           {
             break;
           }
           i++;
         }
         if( i > iNoCodes)
         {
           sprintf(pBueffel,"OutCode %s not recognized",argv[2]);
           SCWrite(pCon,pBueffel,eInError);
           return 0;
         }
         pCon->iOutput = i;
         SCSendOK(pCon);
         return 1; 
       }
       else if(strcmp(argv[1],"rights") == 0)
       {
          if(argc < 4)
          {
             sprintf(pBueffel,"Insufficient number of args to %s",argv[0]);
             SCWrite(pCon,pBueffel,eInError);
             return 0;
          }
          i = IsValidUser(argv[2],argv[3]);
          if(i < 0)
          {
            sprintf(pBueffel," %s with password ****** is NO valid User on SICS",
                    argv[2]);
             SCWrite(pCon,pBueffel,eError);
             return 0;
          }
          pCon->iUserRights = i;
          sprintf(pBueffel,"User %s socket %d switched to %d privilege",
                  argv[2],pCon->pSock->sockid,i);
          WriteToCommandLog("SYS>",pBueffel);        
          SCWrite(pCon,"Change of Authorisation Acknowledged",eWarning);
          return 1; 
       }
       SCWrite(pCon,"Command not recognized",eError);
       return 0;
   }
/*----------------------------------------------------------------------*/
  int SCRegister(SConnection *pCon, SicsInterp *pSics,
                      void  *pData, long lID)
  {
     pICallBack pInter = NULL;
     Item sItem;
     
     pInter = (pICallBack)pData;
     if(!VerifyConnection(pCon))
     {
          return 0;
     }
     assert(pSics);
     assert(pInter);
     
     sItem.lID = lID;
     sItem.pInterface = pInter;
     LLDnodeAppendFrom(pCon->iList,&sItem);
     return 1;
  }
/*----------------------------------------------------------------------*/  
  int SCUnregister(SConnection *pCon, void *pData)
  {
    int iRet;
    Item sItem;
    pICallBack pInter;
    
    if(!VerifyConnection(pCon))
    {
          return 0;
    }
    pInter = (pICallBack)pData;
    iRet = LLDnodePtr2First(pCon->iList);
    while(iRet != 0)
    {
      LLDnodeDataTo(pCon->iList,&sItem);
      if(sItem.pInterface == pInter)
      {
        LLDnodeDelete(pCon->iList);
        LLDnodePtr2Prev(pCon->iList);
      }
      iRet = LLDnodePtr2Next(pCon->iList);
    }
    return 1;
  }
/*---------------------- The callback data structure --------------------*/
  typedef struct {
                   SConnection *pCon;
                   SicsInterp *pSics;
                   char *pAction;
                 } CBAction, *pCBAction;
/*---------------------- CBKill -----------------------------------------*/
  static void CBKill(void *pData)
  {
     pCBAction self = NULL;
     self = (pCBAction)pData;
     
     if(self == NULL)
     {
       return;
     }
     
     if(self->pAction)
     {
       free(self->pAction);
     }
     free(self);
  }                   

/*-------------------------------------------------------------------------
   The callback function for connection callbacks. Invokes command
   given at registration time.
*/
   static int ConCallBack(int iEvent, void *pEventData, void *pUserData)
   {
        pCBAction self = NULL;
        
        self = (pCBAction)pUserData;
        assert(self);
        
        if(self->pAction)
        {
               InterpExecute(self->pSics,self->pCon,self->pAction); 
        }
        return 1; 
   }  
/*--------------------------------------------------------------------------
   The only command currently understood is: put args
     writes the args to the client
*/      
     
     
   int ConSicsAction(SConnection *pCon, SicsInterp *pSics, void *pData,
                        int argc, char *argv[])
   {
      SConnection *self = NULL;
      pICallBack pInterface = NULL;
      char pBueffel[1024];
      pDummy pDum;
      int iEvent;
      Item sItem;
      pCBAction pCB = NULL;
      CommandList *pCom = NULL;
      int iMacro;  
        
      self = (SConnection *)pData;
      if(!VerifyConnection(self))
      {
          return 0;
      }
      
      if(argc > 1)
      {
        /* put */
        if(strcmp(argv[1],"put") == 0)
        {
           Arg2Text(argc-2,&argv[2],pBueffel,1023);
           iMacro = SCinMacro(pCon);
           SCsetMacro(pCon,0);
           SCWrite(self,pBueffel,eWarning);
           SCsetMacro(pCon,iMacro);
           return 1;
        }
        /* register object event action */
        if(strcmp(argv[1],"register") == 0)
        {
           if(argc < 5)
           {
             SCWrite(pCon,"ERROR: Insufficient arguments to register",eError);
             return 0;
           }
           
           /* get object */
           pCom = FindCommand(pSics,argv[2]);
           if(!pCom)
           {
             sprintf(pBueffel,"ERROR: object %s NOT found",argv[2]);
             SCWrite(pCon,pBueffel,eError);
             return 0;
           }
           
           /* get CallBack interface */
           pDum = (pDummy)pCom->pData;
           assert(pDum);
           pInterface = (pICallBack)pDum->pDescriptor->GetInterface(pDum,CALLBACKINTERFACE);
           if(!pInterface)
           {
             sprintf(pBueffel,"ERROR: %s does not support CallBacks",
                     argv[2]);
             SCWrite(pCon,pBueffel,eError);
             return 0;        
           }
           
           /* get Event */
           iEvent = Text2Event(argv[3]);
           if(iEvent < 0)
           {
             sprintf(pBueffel,"ERROR: Unknown event code %s",argv[3]);
             SCWrite(pCon,pBueffel,eError);
             return 0;
           }
           
           /* now we can install the callback */
           pCB = (pCBAction)malloc(sizeof(CBAction));
           if(!pCB)
           {
             SCWrite(pCon,"ERROR: memory exhausted in SConnection",eError);
             return 0;
           }
           Arg2Text(argc-4, &argv[4],pBueffel,1023);
           pCB->pCon = pCon;
           pCB->pSics = pSics;
           pCB->pAction = strdup(pBueffel);
           sItem.pInterface = pInterface;
           sItem.lID = RegisterCallback(pInterface, iEvent, ConCallBack,
                                        pCB, CBKill);
           LLDnodeAppendFrom(self->iList,&sItem);
           SCSendOK(pCon);
           return 1;                             
        }
      }  
      return 0;
   }
/*--------------------------------------------------------------------------*/
   int SCTaskFunction(void *pData)
   {
      SConnection *self = NULL;
      char *pPtr = NULL;
      int iRet;
      char *pUser = NULL, *pPassword = NULL;
      
      self = (SConnection *)pData;
      if(!VerifyConnection(self))
      {
          return 0;
      }
      
      if(self->iEnd)  
      {
        if(self->inUse != 0)
        {
          return 1;
        }
        else
        {   
          return 0;
        }
      }

      /* a timeout check on logins */
      if(!self->iLogin && time(NULL) > self->conStart + 120)
      {
        NetReadRemove(pServ->pReader,self->pSock);
	SCWrite(self, "No valid login in two minutes, closing..",eError);
        self->iEnd = 1;
        return 1;
      }

      /* pop and execute */
      iRet = CostaPop(self->pStack,&pPtr);
      if(iRet)
      {
        if(pPtr)
        {
          if(self->iLogin)
	  {
	    /* 
              normal processing, logged in
              but check for logoff
	    */
             if(strstr(pPtr,"logoff") != NULL)
	     {
               NetReadRemove(pServ->pReader,self->pSock);
	       self->iEnd = 1;
               free(pPtr);
               return 1;
             }
             /* invoke command */
             CostaLock(self->pStack);
             SCInvoke(self,self->pSics,pPtr);
             CostaUnlock(self->pStack);
      /*     SCWrite(self,"\b",eError); */
          }
          else
	  {
	    /* check for username and password */
            pUser = strtok(pPtr," \t");
            pPassword = strtok(NULL," \t\r\n");
            iRet = IsValidUser(pUser,pPassword);
            if(iRet >= 0)
	    {
              SCWrite(self,"Login OK",eError);
	      self->iLogin = 1;
	      SCSetRights(self,iRet);
              free(pPtr);
              return 1;
            }
            else
	    {
	      SCWrite(self,"ERROR: Bad login",eError);

            }	    
          }
          free(pPtr);
        }
      }  


      if(self->iEnd)  
      {
        if(self->inUse != 0)
        {
          return 1;
        }
        else
        {   
          return 0;
        }
      }
      
      return 1;
   }
/*---------------------------------------------------------------------------*/
   void SCSignalFunction(void *pData, int iSignal, void *pSigData)
   {
     SConnection *self = NULL;
     int *iInt;
     char *pPtr;
     
     self = (SConnection *)pData;
     if(!VerifyConnection(self))
     {
          return;
     }
     
     if(iSignal == SICSINT)
     {
       iInt = (int *)pSigData;
       SCSetInterrupt(self,*iInt);
       if(*iInt == eEndServer)
       {
         self->iEnd = 1;
       }
     }
     else if(iSignal == SICSBROADCAST)
     {
       pPtr = (char *)pSigData;
       if(pPtr)
       {
         SCWrite(self,pPtr,eWarning);
       }
     }
     else if(iSignal == TOKENRELEASE)
     {
        self->iGrab = 0;
     }
     else if(iSignal == TOKENGRAB)
     {
       self->iGrab = 1;
     }
   }  
