/*  Project Tec3270

	 SUBSYSTEM:    Tec3270.exe (Tec3270 application)
	 FILE:         alffnttl.cpp
	 AUTHOR:       Alcindo Ferreira Filho

	 OVERVIEW:

    ALFFTelnet Class Implementation, the Tec3270 Telnet class
    (That encode and decode byte sequences transmitted and
    received from host server).

    COPYRIGHT:    © 1998-2007
                  Alcindo Ferreira Filho
                  <alcindo@inttec.com.br>

    LICENSE:      Tec3270_License (docs/Tec3270_License.txt)
*/
#include "alffnall.h"

//{{ALFFTelnet Implementation}}
//
//////////////////////////////////////////////////////////
// ALFFTelnet
// ==========
// Construction/Destruction handling.
ALFFTelnet::ALFFTelnet(TWindow* pWindow, ALFFConnData* cnnData,
                             bool printerLine) :
									  nTelnetRecvState(nTelnet_BEGIN)
{
 isPrinter  = printerLine;

 /* Tec3270 Session Descriptor */
 connData = cnnData;

 modeList[0].modedescriptions = "Telnet Command Mode";
 modeList[0].modetype         = COMMAND_LINE;
 modeList[1].modedescriptions = "Character-At-A-Time Mode";
 modeList[1].modetype         = 0;
 modeList[2].modedescriptions = "Character-At-A-Time Mode (Local Echo)";
 modeList[2].modetype         = LOCAL_ECHO | LOCAL_CHARS;
 modeList[3].modedescriptions = "Line-By-Line Mode (Remote Echo)";
 modeList[3].modetype         = LINE | LOCAL_CHARS;
 modeList[4].modedescriptions = "Line-By-Line Mode";
 modeList[4].modetype         = LINE | LOCAL_ECHO | LOCAL_CHARS;
 modeList[5].modedescriptions = "Line-By-Line Mode (Local Echoing Suppressed)";
 modeList[5].modetype         = LINE | LOCAL_CHARS;
 modeList[6].modedescriptions = "3270 Mode";
 modeList[6].modetype         = 0;

 char wdoopt[5] = { IAC, DO, '%', 'c', 0 };
 char wdont[5]  = { IAC, DONT, '%', 'c', 0 };
 char wwill[5]  = { IAC, WILL, '%', 'c', 0 };
 char wwont[5]  = { IAC, WONT, '%', 'c', 0 };

 /* Store Parent Window Pointer */
 pCallWindow = pWindow;

 /* Initialize ALFF Messages Manager */
 pMsgs  =  new ALFFMsgs(pCallWindow);

 //
 // Terminal Type and Model Definition
 //
 terminalType = new char[MAXTERMTYPELEN+1];
 terminalModel = new char[MAXTERMTYPELEN+1];
 tn3270Mode    = new char[8];
 strcpy(tn3270Mode,"");  // Initially  we are not on any Mode.

 /* Terminal and Printer Netnames on TN3270E */
 terminalNetname[0] = 0x00;
 printerNetname[0]  = 0x00;

 // Format of TELNET options
 memcpy(doopt,wdoopt,5);
 memcpy(dont,wdont,5);
 memcpy(will,wwill,5);
 memcpy(wont,wwont,5);

 // Clear OOB Buffer and SubOption buffer pointer
 subbuffer = new char[SUBBUFSIZE];
 subpointer = subbuffer;

 // Initialize TELNET Options Arrays
 ClearArray (hisopts);
 ClearArray (myopts);

 // Initialize TN3270E Functions Arrays
 ClearArray (myFunctions);
 ClearArray (hisFunctions);

 if (isPrinter)
  {
   // Set supported TN3270E Functions
   myFunctions[DATA_STREAM_CTL] = true;
   myFunctions[RESPONSES] = true;   
   myFunctions[SCS_CTL_CODES] = true;
  }
 else
  {
   // Terminal hasn't yet any functions
   myFunctions[RESPONSES] = true;
  }
 sequenceNumber = 0;

 /*
  * Initialize Terminal Controls
  */
 globalMode = 4;      /* Line by Line - Local Echo */
 tn3270EMode = TNOTHING;
 connected  = in3270 =  in3270E =
 doneBinaryToggle = dontLEcho =
 termTypeChanged = sentTermType = recvEOR   = false;
 SetModeSwitched(false);
 pMsgs->LoadString(IDS_TELNETNVT,terminalType, STRINGMSGLEN-1);
 terminalModel [0] = NULL;
 isSYNCHing = false;
 autoFlush  = false;
 flushLine  = true;
 dataSent   = false;
 crlf       = true;

 /*
  * Initialize Clocks
  */
 clocks.system = 0;
 clocks.echotoggle = 0;
 clocks.modenegotiated = 0;
 clocks.didnetreceive = 0;
 clocks.gotDM = 0;
}

ALFFTelnet::~ALFFTelnet ()
{
 delete   [] terminalType;
 delete   [] terminalModel;
 delete   [] tn3270Mode;
 delete   [] subbuffer;  // era delete [] subbuffer;
 delete   pMsgs;
}

bool
ALFFTelnet::CloseAll()
{
 /*
  * Clear All TELNET Options
  */
 ClearArray (hisopts);
 ClearArray (myopts);

 // Initialize TN3270E Functions Arrays
 ClearArray (myFunctions);
 ClearArray (hisFunctions);

 if (isPrinter)
  {
   // Set supported TN3270E Functions
   myFunctions[DATA_STREAM_CTL] = true;
   myFunctions[RESPONSES] = true;
   myFunctions[SCS_CTL_CODES] = true;
  }
 else
  {
   // Terminal hasn't yet any functions
   myFunctions[RESPONSES] = true;
  }
 sequenceNumber = 0;

 /*
  * Initialize Terminal Controls
  */
 globalMode = 4;      /* Line by Line - Local Echo */
 tn3270EMode = TNOTHING;
 connected  = in3270 = in3270E =
 doneBinaryToggle = dontLEcho =
 termTypeChanged = sentTermType = recvEOR   = false;
 SetModeSwitched(false);
 strcpy(terminalType,TELNETNVT);
 terminalType [0] = NULL;
 terminalModel [0] = NULL;
 isSYNCHing = false;
 autoFlush  = false;
 flushLine  = true;
 dataSent   = false;

 /*
  * Initialize Clocks
  */
 clocks.system = 0;
 clocks.echotoggle = 0;
 clocks.modenegotiated = 0;
 clocks.didnetreceive = 0;
 clocks.gotDM = 0;

 // Reset TN3270 Mode Indicator
 strcpy(tn3270Mode,"");  // Initially  we are not on any Mode.

 /* Reset PTerminal and Printer Netnames on TN3270E */
 terminalNetname[0] = 0x00;
 printerNetname[0]  = 0x00;

 return true;
}

bool
ALFFTelnet::GetEchoMode()
{
 return hisopts[TN_ECHO];
}

bool
ALFFTelnet::EorReceived()
{
 return recvEOR;
}

void
ALFFTelnet::ResetEor()
{
 recvEOR = false;
}

void
ALFFTelnet::BuildOOBData(char** oobPtr, ...)
{
 char *ptr, *format, *string;
 int  strglen;
 char *buffer = new char[100];
 char ch;

 //
 // Current OOB Work Buffer position
 ptr = buffer;

 va_list ap;
 va_start(ap,oobPtr);
 format = va_arg(ap, char*);

 while ((ch = *format++) != 0)
 {
  if (ch == '%')
  {
	ch = *format++;
	switch (ch)
	{
	 case 'c': *ptr++ = va_arg(ap, int);
				  break;
	 case 's': string = va_arg(ap, char*);
				  strglen = va_arg(ap, int);
				  memcpy(*oobPtr,buffer,((size_t)(ptr-buffer)));
				  *oobPtr = *oobPtr + ((size_t)(ptr-buffer));
				  memcpy(*oobPtr,string,strglen);
				  *oobPtr = *oobPtr + strglen;
				  ptr = buffer;
				  break;
	 case   0: delete [] buffer;
              return;
	 default : string = (char*)"UnKnow Format Character";
				  memcpy(*oobPtr,string,strlen(string));
				  *oobPtr = *oobPtr + strlen(string);
              delete [] buffer;
				  return;
	}
  }
  else *ptr++ = ch;
 }

 memcpy(*oobPtr,buffer,((size_t)(ptr-buffer)));
 *oobPtr = *oobPtr + ((size_t)(ptr-buffer));
 delete [] buffer;
}

void
ALFFTelnet::DoOption(char** oobPtr, int option)
{
 char *fmt;
 switch (option)
  {
	case TN_TIMING_MARK:
		  {
			fmt = will;
			myopts[option] = true;         // Set Option true
			break;
		  }
   case TN3270E:
        {
         /* On TN3270E we must assume EOR and TRANSMIT_BINARY implicitly */
     		fmt = will;
			myopts[option] = true;         // Set Option true
			hisopts[option] = true;         // Set Option true
         strcpy(tn3270Mode,"TN3270E");  // Now we are on TN3270E Mode.
			break;
		  }
   case TN_EOR:
	case TN_TRANSMIT_BINARY:
	case TN_TERMINAL_TYPE:
	case TN_SUPPRESS_GA:
		  {
			fmt = will;
			myopts[option] = true;   // Set Option true
			break;
		  }
   case START_TLS:
			fmt = will;
			myopts[option] = true;   // Set Option true
        break;
	case TN_ECHO:
	default:
		  {
			fmt = wont;
			myopts[option] = false;   // Tec3270 Does No Remote Echo
			break;
		  }
  }

  BuildOOBData(oobPtr, fmt, option);   // Format OOB data to return

  // Start TLS must send subnegotiation in follow "WILL"
  if (option == START_TLS)
     BuildOOBStartTLS(oobPtr);
}

void
ALFFTelnet::WillOption(char** oobPtr, int option)
{
 char *fmt;
 switch (option)
  {
   case TN3270E:
        {
         /* On TN3270E we must assume EOR and TRANSMIT_BINARY implicitly */
       	hisopts[option] = true;     // Set Option true
			fmt = doopt;
			break;
        }
	case TN_ECHO:
	case TN_EOR:
	case TN_TRANSMIT_BINARY:
	case TN_SUPPRESS_GA:
		  {
			hisopts[option] = true;     // Set Option true
			fmt = doopt;
			break;
		  }
	case TN_TIMING_MARK:
		  { return; }             // Never process Timing Mark !!

	default:
		  {
			fmt = dont;
			hisopts[option] = false;     // Set Option false
			myopts[option] = false;     // Set Option false
			break;
		  }
  }

  BuildOOBData(oobPtr, fmt, option);   // Format OOB data to return

}

void
ALFFTelnet::WontOption(char** oobPtr, int option)
{
 char *fmt;
 switch (option)
  {
	case TN_SUPPRESS_GA:
		  {
			myopts[option] = false;     // Set Option false
			myopts[option] = false;     // Set Option false
			fmt = dont;
			break;
		  }
	case TN_TIMING_MARK:
		  { return; }             // Never process Timing Mark !!

	case TN_ECHO:
		  {                       // Tec3270 Needs No Remote Echoe
			myopts[option] = false;     // Set Option false
			myopts[option] = false;     // Set Option false
			return;                // Do Not Process WONT as ECHO is disabled.
		  }

	default:
		  {
			myopts[option] = false;     // Set Option false
			myopts[option] = false;     // Set Option false
			break;
		  }
  }

  BuildOOBData(oobPtr, fmt, option);   // Format OOB data to return
}

void
ALFFTelnet::BuildOOBTerminal(char** oobPtr, int termId, char* oobTermFmt,
                                int fmtLength, int termTypePos, char* tn3270ENetName)
{
 /* Prepare Terminal Type and Model */
 char termEmulated[STRINGMSGLEN];
 BYTE subCommand;
 int  termModelLen = 0;

 /* Set DEVICE_TYPE Request format */
 memcpy(sb_terminal, oobTermFmt, fmtLength);
 if (isPrinter)
  {
   strcpy(termEmulated,"IBM-3287-1");
   termModelLen = 1;

   if (myopts[TN3270E] && hisopts[TN3270E]) //(in3270E)
    {
     // Send TN3270E Terminal Type Command (ASSOCIATE or CONNECT)   
     if (connData->printerAssociated)
        memset(&subCommand,ASSOCIATE,1);
     else memset(&subCommand,CONNECT,1);
    }
   else
    {
     /*--------------------------------------------------------------------*
      * We are on TN3270 Extensions for LUName :                           *
      *  <IAC SB TERM TYPE IS IBM-3287-1 IAC SE>                           *
      *   Sender is stating the name of his terminal-type.                 *
      *   The code for <IS> is 0.  Optionally, a  specific Logical Unit    *
      *   (LU) can be requested by using the TERMINAL-TYPE string below.   *
      *   If no LUname is specified, the first available 3287 LU is        *
      *   selected.                                                        *
      *            IAC SB TERM-TYPE IS IBM-3287-1 @ LUNAME IAC SE          *
      *--------------------------------------------------------------------*/
      memset(&subCommand,'@',1);
    }
  }
 else
  {
   pMsgs->LoadString(termId, termEmulated, STRINGMSGLEN-1);
   memset(&subCommand,CONNECT,1);
   termModelLen = 3;
  }

 /* Build OOB Data For Sending To Server */
 int termLen = strlen(termEmulated);
 memcpy(&sb_terminal[termTypePos],termEmulated,termLen);
 if (tn3270ENetName)
  {
   /* This an connection with netname specified (TN3270E or TN3287)
    *  May be terminal or printer
    */
   int netNameLen = strlen(tn3270ENetName);
   if (netNameLen > 0)
    {
     memcpy(&sb_terminal[termTypePos+termLen], &subCommand, 1);
     memcpy(&sb_terminal[termTypePos+termLen+1], tn3270ENetName, netNameLen);
     memset(&sb_terminal[termTypePos+termLen+1+netNameLen],IAC,1);
     memset(&sb_terminal[termTypePos+termLen+1+netNameLen+1],SE,1);
     // Need To compute TELNET commands
     //(See above definitions on SubOption Method)
     termLen+=termTypePos+1+netNameLen+2; // +2 = IAC + SE
    }
   else
    {
     /* There is no netname :
      * No CONNECT or ASSOCIATE Neither '@' command need to be inserted */
     memset(&sb_terminal[termTypePos+termLen],IAC,1);
     memset(&sb_terminal[termTypePos+termLen+1],SE,1);
     // Need To compute TELNET commands
     //(See above definitions on SubOption Method)
     termLen+=termTypePos+2; // +2 = IAC + SE
    }
  }
 else  // No netname was specified, so we need no CONNECT, ASSOCIATE or '@'
  {
   /* TN3270 does not support terminal netname */
   memset(&sb_terminal[termTypePos+termLen],IAC,1);
   memset(&sb_terminal[termTypePos+termLen+1],SE,1);
   // Need To compute TELNET commands
   //(See above definitions on SubOption Method)
   termLen+=termTypePos+2; // +2 = IAC + SE
  }

 /* Save Terminal Type and Model For Further Processing */
 memset(&terminalType[0],NULL,MAXTERMTYPELEN);
 memset(&terminalModel[0],NULL,MAXTERMTYPELEN);

 /* Check For Being an IBM Terminal or Printer*/
 memcpy(&terminalType[0],&sb_terminal[termTypePos],8);
 if (!strcmp(terminalType,IBM3278)
  || !strcmp(terminalType,IBM3279)
  || !strcmp(terminalType,IBM3287))
  memcpy(&terminalModel[0],&sb_terminal[termTypePos+8+1],termModelLen);
 else
  {
   // This an NVT Terminal
   memset(&terminalType[0],NULL,MAXTERMTYPELEN);
   memcpy(&terminalType[0],TELNETNVT,TELNETNVTLEN);
  }

 BuildOOBData(oobPtr,"%s",sb_terminal,termLen);
}

void
ALFFTelnet::BuildOOBStartTLS(char** oobPtr)
{
 /* TELNET START_TLS FUNCTION sub option - 6 bytes at all */
 // < IAC SB START_TLS FOLLOWS IAC SE >
 char TN3270E_function_start_tls[] = { IAC, SB, START_TLS, FOLLOWS, IAC, SE };
 BuildOOBData(oobPtr, "%s", TN3270E_function_start_tls, 6);
}


int // length of buffer
ALFFTelnet::BuildMyFunctions(char* buffer, int type)
{
 unsigned char i, baseLen;

 /* TELNET TN3270E FUNCTIONS REQUEST sub option - 7 bytes minimum */
 char TN3270E_functions_request[] = { IAC, SB, TN3270E, FUNCTIONS, REQUEST,
                              // List of Functions Supported by requester
								      IAC, SE };

 /* TELNET TN3270E FUNCTIONS IS sub option - 7 bytes minimum */
 char TN3270E_functions_is[] = { IAC, SB, TN3270E, FUNCTIONS, IS_E,
                              // List of Functions Supported by requested part
								      IAC, SE };

 if (type == REQUEST)
  {
   /* Set Functions Request format for printer */
   memcpy(buffer, TN3270E_functions_request, 5);
  }
 else if (type == IS_E)
  {
   /* Set Functions Request format for printer */
   memcpy(buffer, TN3270E_functions_is, 5);
  }
 else return -1;

 baseLen = 5;
 for (i = 0; i < LASTFUNCTION; i++)
  {
   if (myFunctions[i] == true)
    {
     memset(buffer+baseLen,i,1);
     baseLen++;
    }
  }

 // Close the sub negotiation message
 memset (buffer+baseLen, IAC, 1);
 memset (buffer+baseLen+1, SE, 1);

 return baseLen+2;
}

bool
ALFFTelnet::ParseHisFunctions(char* subbuffer, int begin)
{
  bool endFunction = false;
  int i =begin;
  unsigned char byte_1, byte_2;

  // Take server accepted functions
  while (!endFunction)
   {
    byte_1 = subbuffer[i];
    byte_2 = subbuffer[i+1];
    if ((byte_1 == 0x00) && (byte_2 == EM))
       endFunction = true;
    else hisFunctions[byte_1] = true;
    i++;
   }

  // Compare my and his functions
  bool equal = true;
  for (i=0; i<LASTFUNCTION; i++)
   {
    if (myFunctions[i] != hisFunctions[i])
      equal = false;
   }
 return equal;
}

bool
ALFFTelnet::IsHisFunctionsSupported()
{
  bool supported = true;
  for (int i = 0; i < LASTFUNCTION; i++)
   {
     if ((hisFunctions[i] == true)
      && (myFunctions[i] == false) )
       supported = false;
   }
  return supported;
}

bool
ALFFTelnet::IsAnyOfHisFunctionsSupported()
{
  bool supported = false;
  for (int i = 0; i < LASTFUNCTION; i++)
   {
    if (hisFunctions[i] == true)
     {
       if (myFunctions[i] == true)
         supported = true;
       else hisFunctions[i] = false;
     }
   }
  return supported;
}

int
ALFFTelnet::SubOption(char** oobPtr, char* subbuffer)
{
 int i;
 HWND caller;
 char termErrorMsg[64];
 char netName[9];
 int telnetError = 0;

 /* IAC WILL/WONT/DO/DONT format */
 char* fmt;

 /* TELNET TN3270 TERMINAL sub option  - 18 bytes max */
 char TN3270_terminal[] = { IAC, SB, TN_TERMINAL_TYPE, IS,
								 'I','B','M','-','3','2','7','8','-','2',
								 '-','E',IAC, SE };
 /* TELNET TN3270E DEVICE TYPE sub option  - 19 bytes max */
 char TN3270E_terminal[] = { IAC, SB, TN3270E, DEVICE_TYPE, REQUEST,
								 'I','B','M','-','3','2','7','8','-','2',
								 '-','E',IAC, SE };

 // This the reason code of the DEVICE REJECT sub command ....
 BYTE reasonCode;
 char errTerminal[STRINGMSGLEN+1];

 switch (HEX (subbuffer[0]))
	{
	 case  TN_TERMINAL_TYPE:
    /*
	  *   Search sub option buffer and try to respond to the
	  *      other side that we are a 3278 Model 2 Terminal,
	  *      when received Terminal Type, Send Request sub-option.
	  */
      	  {
				if (HEX (subbuffer[1]) == SEND)
				 {
				  /* If Terminal Type was already sent then try an lower
					* model until NVT is reached */
              if (!isPrinter)
               {
				    if (sentTermType)
					  {
					   if (connData->termType > 0)
					     --connData->termType;
                  //else: We are already at NVT Terminal: Do nothing ...//
					  }
				    else sentTermType = true;
                strcpy(netName, connData->terminalNetName);
               }
              else  // is Printer !!
               {
                if (sentTermType)
                  //  printer not available !!
                 {
                  strcpy(errTerminal, "IBM-3287-1 (");
                  strcat(errTerminal,connData->printerNetName);
                  strcat(errTerminal,")");
                  pMsgs->StrMessage(errTerminal,IDS_TERMINALTYPEERROR,
                                          IDS_TITTELNETERR);

						// Terminate Terminal Type Negotiation and close connection
                  fmt = dont;
                  // Format OOB data to return
                  BuildOOBData(oobPtr, fmt, TN_TERMINAL_TYPE);

                  // Set error return code
                  telnetError = IDS_TERMINALTYPEERROR;

                  break;
                 }
                else
                 {
                   strcpy(netName, connData->printerNetName);
                   sentTermType = true;
                 }
               }

              /* Build TELNET TN_TERMINAL command */
              BuildOOBTerminal(oobPtr, IDS_ALFFTERMBASE+connData->termType+1,
                               &TN3270_terminal[0], SBTERMLEN, 4, netName);

				  termTypeChanged = true;
              strcpy(tn3270Mode,"TN3270");  // Now  we are on TN3270 Mode.
				 }
            else
             {
              pMsgs->LoadString(IDS_UNKNOWTELNETCOMM,errTerminal,STRINGMSGLEN);
              pMsgs->StrMessage(errTerminal,IDS_TERMINALTYPEERROR,
                                          IDS_TITTELNETERR);
   			  // Terminate Terminal Type Negotiation and close connection
              fmt = dont;
              // Format OOB data to return
              BuildOOBData(oobPtr, fmt, TN_TERMINAL_TYPE);

              // Set error return code
              telnetError = IDS_TERMINALTYPEERROR;
             }
            break;
			  }
	 case  TN3270E:
    /*
	  *   Search sub option buffer and try to respond to the
	  *   other side depending upon the suboption requested
	  */
			  {
             switch (HEX(subbuffer[1]))
	           {
               case SEND_E:
                {
				     if (HEX(subbuffer[2]) == DEVICE_TYPE)
				      {
				       /* This the first time Terminal Type is sent ....
					     * So we must send the first option !!
                    */
                   if ((isPrinter) && (!connData->printerAssociated))
                    strcpy(netName, connData->printerNetName);
                   else strcpy(netName, connData->terminalNetName);

                   /* Build TELNET TN3270E DEVICE REQUEST command */
                   BuildOOBTerminal(oobPtr, IDS_ALFFTERMBASE+connData->termType+1,
                               &TN3270E_terminal[0], SBTERMLEN_E, 5, netName);

                   /*
                    * Terminal type is not yet accepted by server ...
                    * We must wait for IAC SB TN3270E DEVICE-TYPE IS ....
                    */
                   sentTermType = true;

                   strcpy(tn3270Mode,"TN3270E");  // Now  we are on TN3270E Mode.
                  }
                 else
                  {
                   fmt = dont;   // Reject TN3270E Negotiation
                   // Format OOB data to return
                   BuildOOBData(oobPtr, fmt, TN3270E);

                   // Set error return code
                   telnetError = IDS_TERMINALTYPEERROR;
                  }
                 break;
				    }
               case DEVICE_TYPE:
                {
				     if (HEX(subbuffer[2]) == IS_E)
				      {
                   /* Terminal Type & Model Is Already Defined on
                    * terminalType and terminalModel vars
                    * Now we must get the terminal Netname:
                    */
                   i = 3;
                   while ((subbuffer[i] != CONNECT) && (subbuffer[i] != ASSOCIATE))
                    {
                     if (subbuffer[i] != EM)
                       i++;
                     else break;
                    }
                   /* Now we are on Terminal Netname or EM (End of Message) */
                   if (subbuffer[i++] != EM) // we have the terminal/printer netname
                    {
                     if (isPrinter)
                      {
                       strcpy(printerNetname,&subbuffer[i]);
                       strcpy(connData->printerNetName,printerNetname);
                       strcpy(terminalNetname,"");
                      }
                     else
                      {
                       strcpy(terminalNetname,&subbuffer[i]);
                       strcpy(printerNetname,"");
                       strcpy(connData->terminalNetName,terminalNetname);
                      }

                     /* Set Functions Request format for printer */
                     int reqLen = BuildMyFunctions(sb_terminal, REQUEST);
      				   BuildOOBData(oobPtr,"%s",sb_terminal,reqLen);
                    }
                   else
                    {
                     /* No Terminal Netname was Found - Send message to user
                      *  and disconnect
                      */
                     strcpy(printerNetname,"");
                     strcpy(terminalNetname,"");
                     pMsgs->Message(IDS_NETNAMENOTFOUND, IDS_TITTELNETERR);
                    }
                  }
                 else if (HEX(subbuffer[2]) == REJECT)
                   {
                    if (HEX(subbuffer[3]) == REASON) // it MUST be equal !!
                     {
                      reasonCode = subbuffer[4];
                      if ( !isPrinter && ((reasonCode == INV_DEVICE_TYPE)
                         ||(reasonCode == UNSUPPORTED_REQ)
                         ||(reasonCode == UNKNOWN_ERROR)) )
                       {
				            /* As Terminal Type was already sent,
                         * then try an lower model until NVT
                         * is reached
                         */
                        if (sentTermType)
					          {
                          if (connData->termType > 0)
					             --connData->termType;
                          //else: This already at NVT Terminal: Do nothing ...//
					          }

                        /* Set the correct netname:  terminal or printer */
                        if ((isPrinter) && (!connData->printerAssociated))
                            strcpy(netName, connData->printerNetName);
                        else strcpy(netName, connData->terminalNetName);

                        /* Build TELNET TN3270E DEVICE REQUEST command */
                        BuildOOBTerminal(oobPtr,
                               IDS_ALFFTERMBASE+connData->termType+1,
                               &TN3270E_terminal[0], SBTERMLEN_E, 5, netName);
                        sentTermType = true;
                       }
                      else
                       {
                        if (isPrinter)
                         {
                          strcpy(netName, connData->printerNetName);
                          caller = pCallWindow->Parent->HWindow;
                         }
                        else
                         {
                          strcpy(netName, connData->terminalNetName);
                          caller = pCallWindow->HWindow;
                         }
                        sprintf(termErrorMsg,"%s-%s / %s - <%i>",
                                terminalType,
                                terminalModel,
                                netName,
                                reasonCode);
                        pMsgs->StrMessage(termErrorMsg,
                                          IDS_TERMINALTYPEERROR,
                                          IDS_TITTELNETERR);
                        ::SendMessage(caller, MSG_REMPRT_NOTIFY,
                                      0, (LPARAM)&termErrorMsg);

                        // Terminate TN3270E Negotiation
                        fmt = dont;
                        // Format OOB data to return
                        BuildOOBData(oobPtr, fmt, TN3270E);

                        // Set error return code
                        telnetError = IDS_TERMINALTYPEERROR;
                       }
                     }
                    else // Out of TN3270E specification (RFC 2355)
                     {
                      fmt = dont;   // Reject TN3270E Negotiation
                      // Format OOB data to return
                      BuildOOBData(oobPtr, fmt, TN3270E);

                      // Set error return code
                      telnetError = IDS_TERMINALTYPEERROR;
                     }
                   }
                 else
                   {
                    fmt = dont;   // Reject TN3270E Negotiation
                    // Format OOB data to return
                    BuildOOBData(oobPtr, fmt, TN3270E);

                    // Set error return code
                    telnetError = IDS_TERMINALTYPEERROR;
                   }
                 break;
                }
               case FUNCTIONS:
                {
				     if (HEX(subbuffer[2]) == IS_E)
				      {
                   // Parse and Compare his functions with mine
                   bool equal = ParseHisFunctions(subbuffer, 3);

                   if (equal)
                    {
                     // Start 3270 data stream communications (printer)
                     termTypeChanged = true;
                     sentTermType = true;
                     myopts[TN_EOR]=hisopts[TN_EOR]=true;
                     myopts[TN_TRANSMIT_BINARY]=hisopts[TN_TRANSMIT_BINARY]=true;
                    }
                   else
                    {
                     // List of functions on FUNCTIONS IS must be
                     // equal to the list on previous FUNCTIONS REQUEST
                     // that was sent.  If it isn't there is an wrong use
                     // of FUNCTIONS IS: it should be FUNCTIONS REQUEST !
                     // So, Reject TN3270E Negotiation because it disagrees
                     // with stated on RFC 2355 !!
                     fmt = dont;
                     // Format OOB data to return
                     BuildOOBData(oobPtr, fmt, TN3270E);

                     // Set error return code
                     telnetError = IDS_TN3270EFUNCTIONSERR;
                    }
                  }
                 else if (HEX(subbuffer[2]) == REQUEST)
                   {
                    // Parse and Compare his functions with mine
                    bool equal = ParseHisFunctions(subbuffer, 3);
                    if ( equal || IsHisFunctionsSupported() )
                     {
                      if (!equal)
                       {
                        // Change my functions for an subset (his functions)
                        memcpy (myFunctions, hisFunctions, 256);
                       }
                      // Start 3270 data stream communications
                      termTypeChanged = true;
                      sentTermType = true;
                      myopts[TN_EOR]=hisopts[TN_EOR]=true;
                      myopts[TN_TRANSMIT_BINARY]=hisopts[TN_TRANSMIT_BINARY]=true;

                      /* Set Functions Is format */
                      int isLen = BuildMyFunctions(sb_terminal, IS_E);
    				       BuildOOBData(oobPtr,"%s",sb_terminal,isLen);
                     }
                    else if (IsAnyOfHisFunctionsSupported())
                     {
                      // Change my functions for an subset of them)
                      memcpy (myFunctions, hisFunctions, 256);

                      /* Set Functions Request format  */
                      int reqLen = BuildMyFunctions(sb_terminal, REQUEST);
      				    BuildOOBData(oobPtr,"%s",sb_terminal,reqLen);
                     }
                    else // Now we don't accept Functions ....
                     {
                      fmt = dont;   // Reject TN3270E Negotiation
                      // Format OOB data to return
                      BuildOOBData(oobPtr, fmt, TN3270E);

                      // Set error return code
                      telnetError = IDS_TN3270EFUNCTIONSNOT;
                     }
                   }
                 else
                   {
                    fmt = dont;   // Reject TN3270E Negotiation
                    // Format OOB data to return
                    BuildOOBData(oobPtr, fmt, TN3270E);

                    // Set error return code
                    telnetError = IDS_TN3270EFUNCTIONSERR;
                   }
                 break;
                }
               default:
                   {
                    /*
                     * Unknow subcommand or sub option:
                     */
                    fmt = dont;   // Reject TN3270E Negotiation
                    // Format OOB data to return
                    BuildOOBData(oobPtr, fmt, TN3270E);

                    // Set error return code
                    telnetError = IDS_UNKNOWTELNETCOMM;

                    break;
                   }
              }
            break;
			  }
    case  START_TLS:
           {
            // Must Start TLS Negotiation from caller
            ::SendMessage(pCallWindow->HWindow, MSG_START_TLS,0,0);
            break;
           }
	 default:
      {
       /*
        * Unknow subcommand or sub option:
        */
       fmt = dont;   // Reject Sub-Negotiation
       // Format OOB data to return
       BuildOOBData(oobPtr, fmt, HEX(subbuffer[0]) );

       // Set error return code
       telnetError = IDS_UNKNOWTELNETCOMM;
       break;
      }
	}
 return telnetError;
}

bool
ALFFTelnet::TermTypeChanged()
{
 if (termTypeChanged)
  {
	termTypeChanged = false;
	return true;
  }
 else return false;
}

void
ALFFTelnet::SetIn3270()
{
 if ((sentTermType)
	 &&(myopts[TN_TRANSMIT_BINARY])
	 &&(hisopts[TN_TRANSMIT_BINARY]))
  {
	 if (!in3270)
	  {
		 /* Set Communications to 3270 State */
		 in3270 = true;
       if (myopts[TN3270E])
        {
         in3270E = true;
         tn3270EMode = T3270MODE;
         SetModeSwitched(false);
        }
     if (!isPrinter)
        ::SendMessage(pCallWindow->HWindow, MSG_START_PRINTER,0,0);
	  }
  }
 else
  {
	 if (in3270)
	  {
		 /* Set Communications to Not 3270 State */
		 in3270 = false;
       in3270E = false;
       tn3270EMode = TNOTHING;
       SetModeSwitched(false);
		 sentTermType = false;
		 terminalType [0] = NULL;
		 terminalModel[0] = NULL;

       /* Reset PTerminal and Printer Netnames on TN3270E */
       terminalNetname[0] = 0x00;
       printerNetname[0]  = 0x00;
	  }
  }
}

#ifdef ALFFDebug
void
ALFFTelnet::OOBInfo(char* texto)
{
 char message[50];
 sprintf(message,"TELNET Command Received : %s",texto);
 MessageBox(NULL,message,"Tec3270 Received Data",MB_OK | MB_ICONEXCLAMATION);
}
#endif

bool
ALFFTelnet::CheckBuf(char* bufName, LPALFFBUF buffer, char* bufPtr,
								int bufCount, int bufMaxLength, bool receive)
{
	if (bufCount > (bufMaxLength-1))
	 {
	  bufPtr--;
	  if (receive)
		{
		 *bufPtr = EM;
		  recvEOR = true;
		}
		else
		 {
		  bufPtr--;
		  *bufPtr++ = IAC;
		  *bufPtr   = EOR;
		 }
	  buffer->ring_supplied(bufMaxLength-1);

	  /* Inform User That Some Data Was Lost */
	  pMsgs->StrMessage(bufName,IDS_RINGBUFFERFULL,IDS_TITTELNETERR);

	  /* Post Message to start DataFromNetWork process */
	  PostMessage(pCallWindow->HWindow,MSG_NET_INPUT_NOTIFY,0,0);

	  return false;
	 }
	 else return true;
}

int
ALFFTelnet::MessageRecv(LPALFFBUF pLineInBuf,
									LPALFFBUF pNetInBuf,
									LPALFFBUF pOOBData)
{
 //
 // Define special controls for parsing TelNet Data:
 //
 register unsigned char byte;
 register unsigned char *readBufPtr;
 char *netBufSave, *netBufPtr, *oobBufSave, *oobBufPtr;
 int recvLength, recvLenSave,
	  netMaxLength, netLength, netCount,
	  oobMaxLength, oobLength;

 /*
  * Local Screen and OOB Data Buffers
  */

 /*
  * Set Network Input Buffer Controls
  */
 netBufSave = netBufPtr = pNetInBuf->supply;
 netMaxLength = pNetInBuf->ring_empty_consecutive();
 netCount = 0;                                         // No Terminal Input Processed Yet

 //
 // Initialize OOB Response Data Buffer Controls
 //
 oobBufSave = oobBufPtr = pOOBData->supply;
 oobMaxLength  = pOOBData->ring_empty_consecutive();

 //
 // Set Input From Line Controls To Initial Values
 //
 recvLenSave = recvLength = pLineInBuf->ring_full_consecutive();
 readBufPtr = (unsigned char*)pLineInBuf->consume;

 char msgDataType[64];
 int  telnetError = 0;

 //
 // Scan Data Received for parsing TELNET controls
 //
 int logicalUnit = LU2;
 while (recvLength)
 {
  byte = *readBufPtr++; recvLength--;
  switch (nTelnetRecvState)
	{

   case  nTelnet_BEGIN:
      {
       // Checking for any TELNET command received between TN3270E messages
		 if (byte == IAC)
		  {
			nTelnetRecvState = nTelnet_IAC;
			break;
		  }

       nTelnetRecvState = nTelnet_DATA;
       /* Process TN3270E Msg Header on the first cycle of message receiving:
        *  in3270E = true means in3270 = true, that is, we are in 3270 mode
        *  with an IBM Terminal type already defined.  TELNET protocol must
        *  be TN3270E  and terminal may be on 3270 Mode or NVT-Mode depending
        *  upon dataType on TN3270E Header.
        */
       if (in3270E)
        {
         /* We are in TN3270E mode, so we have TN3270E header....
          *
          *--------------------------------------------------------------
          *  DATA-TYPE   |  REQUEST-FLAG  |  RESPONSE_FLAG  | SEQ-NUMBER |
          *     (1)      |      (2)       |       (3)       |     (4)    |
          *--------------------------------------------------------------
          *   1 byte        1 byte           1 byte           2 bytes
          */
         dataType = byte;  // (1)
         requestFlag   = *readBufPtr++; recvLength--; // (2)
         responseFlag  = *readBufPtr++; recvLength--; // (3)
         /*
          * As stated on RFC 2355, section 8.1.4, SEQ-NUMBER Field may be
          * doubled if one of it's bytes is 0xff (IAC escaping). So we MUST
          * verify it and stripped it back to original value:
          */
         seqNumber[0]  = *readBufPtr++; recvLength--; // (4)
         if (seqNumber[0] == 0xff)
          {
           readBufPtr++;
           recvLength--;
          }
         seqNumber[1]  = *readBufPtr++; recvLength--; // (5)
         if (seqNumber[1] == 0xff)
          {
           readBufPtr++;
           recvLength--;
          }
         /*--------------------End of TN3270E Header Parsing ------------*/

         // Defining Logical Unit depending upon dataType and this device type
         if (isPrinter)
          {
           if ((dataType == T3270_DATA) || (dataType == NVT_DATA))
             logicalUnit = LU3;
           else if (dataType == SCS_DATA)
             logicalUnit = LU1;
          }
         else logicalUnit = LU2;
         break; // resume from first while
        }
       else if (in3270)
        {
         if (isPrinter)
          {
           if (byte == 0x00) // RFC 1646: LU1 Data is always preceeded by 0x00
            {
             logicalUnit = LU1;
             break;  // Skip this byte and resume from first while
            }
           else logicalUnit = LU3;
          }
         else logicalUnit = LU2;
         dataType = T3270_DATA;
        }
       else dataType = NVT_DATA;
       readBufPtr--;  // back process Byte
       recvLength++;
       break;
      }

	case  nTelnet_DATA:
		{
		 if (byte == IAC)
		  {
			nTelnetRecvState = nTelnet_IAC;
			break;
		  }

		 if ( (in3270E && ((dataType == T3270_DATA) || (dataType == SCS_DATA))) //TN3270E
          || (in3270 && !in3270E ) ) // TN3270
		  {
         // Verify if the mode must be switched
         if (in3270E && tn3270EMode != T3270MODE)
          {
            SetModeSwitched(true);
            tn3270EMode = T3270MODE;
          }
			/* This Is Data Received when Terminal is in 3270 Mode */
			*netBufPtr++ = byte;
			netCount++;
			if (!CheckBuf("NetWork Input",pNetInBuf,netBufPtr,netCount,netMaxLength))
			  return IDS_RINGBUFFERFULL;

			while (recvLength)
			{
			 byte = *readBufPtr++; recvLength--;
			 if (byte == IAC)
			  {
				nTelnetRecvState = nTelnet_IAC;
				break;
			  }
			 else
			  {
			   *netBufPtr++ = byte;
			   netCount++;
			   if (!CheckBuf("NetWork Input",pNetInBuf,netBufPtr,netCount,netMaxLength))
					return IDS_RINGBUFFERFULL;
			  }
			}
		  }

///////////  NEXT DATA TYPES NOT SUPPORTED IN THIS VERSION ////////////////
       // BIND-Image Message - See data portion for processing it:
       //      Data portion is the SNA Bind image from the session
       //      established between the server and the host application.
       /*
       else if ((in3270E && (dataType == BIND_IMAGE_DATA)) )
		  {
        }

       // UNBIND Message - See data portion for processing it:
       //        Data portion is the Unbind Reason Code.
       else if ((in3270E && (dataType == UNBIND_DATA)) )
		  {
        }*/
///////////////////////////////////////////////////////////////////////////

       else if (!in3270 || (in3270E && (dataType == NVT_DATA)) )
		  {
			/* This Is Data Received when Terminal is in NVT Mode, either on
          *  TN3270 (ASCII encoded), or   TN3270E (ASCII encoded too!).
          */

         // Verify if the mode must be switched
         if (in3270E && tn3270EMode != TNVTMODE)
          {
            SetModeSwitched(true);
            tn3270EMode = TNVTMODE;
          }

         /* NVT Data on               TN3270     or              TN3270E
          *              -------  ASCII encoded -----------
          */
			if ( (byte == CR) && ((!hisopts[TN_TRANSMIT_BINARY]) || in3270E ))
			  {
				if (recvLength)
				 {
				  byte = *readBufPtr;
				  if (byte == 0)
				  // NUL after CR and not transmiting in binary mode
					{
					/* Not 3270 Mode nor Transmit Binary mode
					 *  and received CR followed by a NUL:
					 *     >> Ignore NUL and Insert CR <<
					 */
					 readBufPtr++; recvLength--;
					 *netBufPtr++ = CR;  netCount++;
					 if (!CheckBuf("NetWork Input",pNetInBuf,netBufPtr,netCount,netMaxLength))
						return IDS_RINGBUFFERFULL;
					}
					else if ( (byte == LF) && (!hisopts[TN_ECHO]) )
					// LF after CR and not transmiting in Echo mode
							{
							 /* Not 3270 Mode nor Transmit Binary mode,
							  *  and not in Echo Mode and received
							  *  CR followed by a LF:
							  *     >> Maintain CR and Insert LF <<
							  */
							 readBufPtr++; recvLength--;
							 *netBufPtr++ = CR;
							 *netBufPtr++ = LF;
							 netCount+=2;
							 if (!CheckBuf("NetWork Input",pNetInBuf,netBufPtr,netCount,netMaxLength))
								return IDS_RINGBUFFERFULL;
							}
						  else
							{
							  /*
								* Else must include CR. If CR option
								*  was set to CR+LF (crlf) so must
								*  include an LF after CR.
								*/
							  *netBufPtr++ = CR;
							  netCount++;
							  if (!CheckBuf("NetWork Input",pNetInBuf,netBufPtr,netCount,netMaxLength))
								 return IDS_RINGBUFFERFULL;
							  if (crlf)
								{
								 *netBufPtr++ = LF;
								 netCount++;
								 if (!CheckBuf("NetWork Input",pNetInBuf,netBufPtr,netCount,netMaxLength))
									return IDS_RINGBUFFERFULL;
								}
							}
				 }
				else
				 {
				 /*
				  * No more characters after CR, so
				  *  must include CR or CR+LF depending
				  *  upon options set.
				  */
				  nTelnetRecvState = nTelnet_CR;
				  *netBufPtr++ = CR;
				  netCount++;
				  if (!CheckBuf("NetWork Input",pNetInBuf,netBufPtr,netCount,netMaxLength))
					return IDS_RINGBUFFERFULL;
				  if (crlf)
					{
					 *netBufPtr++ = LF;
					 netCount++;
					 if (!CheckBuf("NetWork Input",pNetInBuf,netBufPtr,netCount,netMaxLength))
						return IDS_RINGBUFFERFULL;
					}
				  break;
				 }

			  }

			else
			  {
            /* Byte isn't CR so must be included */
				*netBufPtr++ = byte; netCount++;
				if (!CheckBuf("NetWork Input",pNetInBuf,netBufPtr,netCount,netMaxLength))
					return IDS_RINGBUFFERFULL;
			  }
        }

       // REQUEST Function - See requestFlag field for answering it:
       //                    No data portion in this message.
       else if ((in3270E && (dataType == REQUEST)) )
		  {
        }

///////////  NEXT DATA TYPES NOT SUPPORTED ON THIS VERSION ////////////////
       // VTAM SSCP - LU Data: Data portion is from SSCP-LU Session.
       /*
       else if ((in3270E && (dataType == SSCP_LU_DATA)) )
		  {
        }*/
///////////////////////////////////////////////////////////////////////////

       // PRINT_EOJ:  End of Printer JOB: No data portion in this message
       else if ((in3270E && (dataType == PRINT_EOJ)) )
		  {
         //  End  Of  Printing JOB or File   ///
         if (isPrinter)
			   PostMessage(pCallWindow->HWindow, MSG_NET_END_OF_PRINTING_NOTIFY,1,1);
         nTelnetRecvState = nTelnet_BEGIN;
         break;
        }

		 else
		  {
         // Verificar se pode acontecer a possibilidade de cair aqui sem estar
         //  em TN3270E mode ....
         sprintf(msgDataType,"<%i>",dataType);
	      pMsgs->StrMessage(msgDataType,IDS_TN3270EDATATYPEUNSUPP,IDS_TITTELNETERR);
         /* Freeing Consumed Line Data */
         pLineInBuf->ring_consumed(recvLenSave);
         return IDS_TN3270EDATATYPEUNSUPP;
        }
		 break;
		}

	case nTelnet_IAC:
		{
      //firstCycle = false;
		switch (byte)
		 {
			case WILL:      nTelnetRecvState = nTelnet_WILL;
								 continue;
			case WONT:      nTelnetRecvState = nTelnet_WONT;
								 continue;
			case DO  :      nTelnetRecvState = nTelnet_DO;
								 continue;
			case DONT:      nTelnetRecvState = nTelnet_DONT;
								 continue;
			case SYNC:      /* (Data Mark)
								  * We may have missed an urgent notification, so
								  * make sure we flush whatever is in the buffer
								  * currently.
								  */
								  isSYNCHing = true;
								  /* Terminal Flush Output */
								  pNetInBuf->ring_reset();
								  netBufSave = netBufPtr = pNetInBuf->supply;
								  netMaxLength = pNetInBuf->ring_empty_consecutive();
								  netCount = 0;  // No Terminal Input Processed Yet

								  /*
									* Ignore all OOB Data already processed
									*/
								  pOOBData->ring_reset();
								  oobBufSave = oobBufPtr = pOOBData->supply;
								  oobMaxLength  = pOOBData->ring_empty_consecutive();

								  /*
									* Register Time when DATA MARK was Catched
									*/
								  time (&clocks.gotDM);
								  nTelnetRecvState = nTelnet_BEGIN; //nTelnet_DATA;
								  break;
			case NOP :      /*
								  * No Operation: Nothing to do
								  */
                          memset(oobBufPtr++,IAC,1);
                          memset(oobBufPtr++,NOP,1);
								  nTelnetRecvState = nTelnet_BEGIN; //nTelnet_DATA;
								  break;
			case GA  :      /*
								  * Telnet Go Ahead Command: Nothing to do
								  */
								  nTelnetRecvState = nTelnet_BEGIN; //nTelnet_DATA;
								  break;
			case SB  :      /*
								  * Sub Option Negotiation Started
								  *   Reset sub-Option Buffer
								  */
								  subpointer = subbuffer;
                          memset(subbuffer,0x00,SUBBUFSIZE);
								  nTelnetRecvState = nTelnet_SB;
								  break;

         case   IP:      /*  (Receiving IP is not supported on Terminals,
                          *   nor Printers, so it is ignored).
                          */
                         nTelnetRecvState = nTelnet_BEGIN;
                         break;

         case   AO:      /*  (Receiving AO is just for printers. It is not
                          *   supported on Terminals, so it is ignored).
                          *  End of SNA RU and end of Bracket.
                          *  This signifies the end of the print output
                          *  or file by the IBM host application and
                          *  possibly a change of LU type.
                          */
                         ///  End  Of  Printing JOB or File   ///
                         if (isPrinter)  // should restrict just to TN3270 ??
									PostMessage(pCallWindow->HWindow, MSG_NET_END_OF_PRINTING_NOTIFY,0,0);
                         // else ignore it.
                         nTelnetRecvState = nTelnet_BEGIN;
                         break;

			case EOR :      if (in3270)
								  {
									/*
									 *  End of Terminal Stream Sent By Host:
									 *   insert EM (End of Stream) indicator,
									 *   add stream received to terminal input buffer
									 *     & start Terminal Stream Parsing Process
									 */
                           if (netCount > 0)
                            {
									  *netBufPtr++ = EM;
									  netCount+=1;

									  if (!CheckBuf("NetWork Input",
													  pNetInBuf,netBufPtr,
													  netCount,netMaxLength))
									     return IDS_RINGBUFFERFULL;

									  pNetInBuf->ring_supplied(netCount);
									  pNetInBuf->ring_mark();
									  recvEOR = true;

									  // Post Message to start DataFromNetWork process
                             //  depending upon LU type
                             unsigned char temp[4];
                             WPARAM dtaType = dataType;
                             if (IsModeSwitched())
                              {
                               dtaType |= 0x80;
                               SetModeSwitched(false);
                              }
                             LPARAM messageCtrl;
                             memcpy (&temp[0], &requestFlag, 1);
                             memcpy (&temp[1], &responseFlag, 1);
                             memcpy (&temp[2], &seqNumber[0], 2);
                             memcpy (&messageCtrl,&temp[0],4);
                             if (logicalUnit == LU1) // SCS-CTL-CODES
										  PostMessage(pCallWindow->HWindow,
													MSG_NET_INPUT_LU1_NOTIFY,dtaType,messageCtrl);
                             else // LU2 && LU3 are 3270 data stream
									     PostMessage(pCallWindow->HWindow,
													MSG_NET_INPUT_NOTIFY,dtaType,messageCtrl);
                             netCount = 0;  // No Terminal Input Processed Yet
                            }
									// Restart Telnet Data Receiving Process
									netBufSave = netBufPtr = pNetInBuf->supply;
									netMaxLength = pNetInBuf->ring_empty_consecutive();
                           nTelnetRecvState = nTelnet_BEGIN;
								  }
								 else nTelnetRecvState = nTelnet_DATA;
                       break;
                       
			case IAC :    if (in3270 || in3270E)
								{
								 *netBufPtr++ = IAC;
								 netCount++;

								 if (!CheckBuf("NetWork Input",
													pNetInBuf,netBufPtr,
													netCount,netMaxLength))
									return IDS_RINGBUFFERFULL;
								}
								nTelnetRecvState = nTelnet_DATA;
								break;

			default  :      /*
								  * UnKnow Telnet Command
								  *   Inform what is its value and ignore it
								  */
								 char errMessage[15];
								 sprintf(errMessage,"<< 0x%.2x >>", byte);
								 pMsgs->StrMessage(errMessage,IDS_UNKNOWTELNETCOMM,
																      IDS_TITTELNETERR);
								 nTelnetRecvState = nTelnet_DATA;
								 break;
		 }
		break;
	  }

	case nTelnet_WILL:
		if (byte == TN_TIMING_MARK)
		 {
		  if (flushOut)
			  flushOut = false;
		 }
		 else if (!hisopts[byte])
				 { WillOption(&oobBufPtr,byte); }
		/*
		 * Check If Option Should Alter Terminal Status
		 */
		SetIn3270();
		nTelnetRecvState = nTelnet_BEGIN; //nTelnet_DATA;
		break;

	case nTelnet_WONT:
		if (byte == TN_TIMING_MARK)
		 {
		  if (flushOut)
			  flushOut = false;
		 }
		 else if (!hisopts[byte])
				 { WontOption(&oobBufPtr,byte); }
		/*
		 * Check If Option Should Alter Terminal Status
		 */
		SetIn3270();
		nTelnetRecvState = nTelnet_BEGIN; //nTelnet_DATA;
		break;

	case nTelnet_DO  :
		if ((!myopts[byte]) || (byte == TN_TIMING_MARK)) 
			 DoOption(&oobBufPtr,byte);
		SetIn3270();
		nTelnetRecvState = nTelnet_BEGIN; //nTelnet_DATA;
		break;

	case nTelnet_DONT:
		if (myopts[byte])
		 {
		  myopts[byte] = false;
		  BuildOOBData(&oobBufPtr,wont,byte);
		  flushLine = true;
		 }
		/*
		 * Check If Option Should Alter Terminal Status
		 */
		SetIn3270();
		nTelnetRecvState = nTelnet_BEGIN; //nTelnet_DATA;
		break;

	case nTelnet_SB  :
		if (byte == IAC)
			nTelnetRecvState = nTelnet_SE;
		else
       {
        SB_ACCUM (byte);
       }
		break;

	case nTelnet_SE  :
		if (byte != SE)
		 {
		  if (byte != IAC)
         {
			 SB_ACCUM (IAC);
         }
		  SB_ACCUM (byte);
		  nTelnetRecvState = nTelnet_SB;
		  break;
		 }
		else
		 {
         SB_ACCUM(0x00);

         // set End of Sub Option Message
         SB_ACCUM(EM);

			SB_TERM ();
			telnetError = SubOption(&oobBufPtr, subbuffer);

			/*
			 * Check If SubOption Should Alter Terminal Status
			 */
			SetIn3270();
			nTelnetRecvState = nTelnet_BEGIN; //nTelnet_DATA;
			break;
		 }

	case  nTelnet_CR:
		{
		 if ( (byte == LF)
			 && (!hisopts[TN_ECHO])
			 && (crlf) )
		  {
			*netBufPtr++ = LF;   // byte contains LF
			netCount++;
			if (!CheckBuf("NetWork Input",pNetInBuf,netBufPtr,netCount,netMaxLength))
				return IDS_RINGBUFFERFULL;
			}
		 nTelnetRecvState = nTelnet_DATA;
		 break;
		}


	} // end switch (nTelnetRecvState)


 }  // end while (recvLength)

 /* Freeing Consumed Line Data */
 pLineInBuf->ring_consumed(recvLenSave);


 /* Check For OOB Data Returning */
 oobLength   = (int)(oobBufPtr - oobBufSave);
 if (oobLength)
  {
	if (CheckBuf("OOB Data",pOOBData, oobBufPtr, oobLength, oobMaxLength, true))
		pOOBData->ring_supplied(oobLength);
  }

 /* Check For Network Stream Data Received */
 netLength   = (int)(netBufPtr - netBufSave);
 if (netLength)
  {
	if (CheckBuf("NetWork Input",pNetInBuf,netBufPtr,netLength,netMaxLength))
	 pNetInBuf->ring_supplied(netLength);

	if ((!in3270)
	 &&(!myopts[TN_EOR])
	 &&(!hisopts[TN_EOR]))
	 {
	  /* Supply End of Message Indicator */
	  netBufPtr = pNetInBuf->supply;

	  /* End of Terminal Stream :
		*   insert EM (End of Stream) indicator.
		*/
	  *netBufPtr++ = EM;
	  pNetInBuf->ring_supplied(1);
	  pNetInBuf->ring_mark();
	  recvEOR = true;

	  // Post Message to start DataFromNetWork process
     //  depending upon LU type
     if (logicalUnit == LU1) // SCS-CTL-CODES
	  	  PostMessage(pCallWindow->HWindow,MSG_NET_INPUT_LU1_NOTIFY,0,0);
     else // LU2 && LU3 are 3270 data stream
	     PostMessage(pCallWindow->HWindow,MSG_NET_INPUT_NOTIFY,0,0);

     nTelnetRecvState = nTelnet_BEGIN;  //Next message must be checked
	 }
   else if (in3270E)
    {
     // This block continues on next, but first of all we must process its
     // Header and, after this, process next block header too !
    }
  }

 return telnetError;

}

bool
ALFFTelnet::SendEcho(LPALFFBUF pLineOutBuf, char* byte)
{
 register int lineCount = 0;
 /*
  * Initialize Line Output Controls
  */
 int lineMaxLen = pLineOutBuf->ring_empty_consecutive();
 register char *pLineSupply = pLineOutBuf->supply;

 if (byte)
  {
	*pLineSupply++ = *byte;
	lineCount++;
	if (!CheckBuf("Line Output", pLineOutBuf,pLineSupply,
					  lineCount, lineMaxLen, false))
	  return false;
  }
 else return false;

 /* Closing supplied data on Line Out Buffer */
 if (lineCount)
  {
	pLineOutBuf->ring_supplied(lineCount);
	return true;
  }
 else return false;
}

bool
ALFFTelnet::SendControl(LPALFFBUF pLineOutBuf, uint control)
{
 register char *pLineSupply;
 register int lineCount;
 int lineMaxLen;

 /*
  * Initialize Line Output Controls
  */
 lineCount = 0;
 lineMaxLen = pLineOutBuf->ring_empty_consecutive();
 pLineSupply = pLineOutBuf->supply;

 if (control)
  {
	switch (control)
	 {
	  case   IDS_ATTN: /* 3270 Attention - Interrupt this LU-LU session */
							 *pLineSupply++ = IAC;
							 lineCount++;
							 *pLineSupply++ = BRK;
							 lineCount++;
							 if (!CheckBuf("Line Output", pLineOutBuf,pLineSupply,
									  lineCount, lineMaxLen, false))
								 return false;
							 break;

	  case IDS_SYSREQ: /* 3270 System Request - Ends this LU-LU session */
							*pLineSupply++ = IAC;
							lineCount++;
							*pLineSupply++ = IP;
							lineCount++;
							if (!CheckBuf("Line Output", pLineOutBuf,pLineSupply,
										lineCount, lineMaxLen, false))
							  return false;
							break;

	  case        IP: /* This part of Telnet Sync process to clear all data */
							*pLineSupply++ = IAC;
							lineCount++;
							*pLineSupply++ = IP;
							lineCount++;
							if (!CheckBuf("Line Output", pLineOutBuf,pLineSupply,
										lineCount, lineMaxLen, false))
							  return false;
							break;

	  case      SYNC: /* This is the Telnet Sync process to clear all data */
							*pLineSupply++ = SYNC; //(Data-Mark)
							lineCount++;
							if (!CheckBuf("Line Output", pLineOutBuf,pLineSupply,
										lineCount, lineMaxLen, false))
							  return false;
							break;
			 default  : pMsgs->Message(IDS_UNKNOWTELNETCOMM,IDS_TITTELNETERR);
							break;
	 }
  }
 else return false;

 if (myopts[TN_TRANSMIT_BINARY] && (control != SYNC)
   && !myopts[TN3270E])
  {
	*pLineSupply++ = IAC;
	*pLineSupply++ = EOR;
	 lineCount +=2;
	if (!CheckBuf("Line Output", pLineOutBuf,pLineSupply,
					  lineCount, lineMaxLen, false))
	  return false;
  }

 /* Closing supplied data on Line Out Buffer */
 if (lineCount)
  {
	pLineOutBuf->ring_supplied(lineCount);
	return true;
  }
 else return false;
}

bool
ALFFTelnet::MessageSend(LPALFFBUF pLineOutBuf,
									LPALFFBUF pOutBuf, bool noTN3270EHdr)
{
 register char *pOutBuffer, *pLineSupply, byte;
 register int lineCount, outCount, saveOutCount;
 int lineMaxLen;

 /*
  * Initialize Line Output Controls
  */
 lineCount = 0;
 lineMaxLen = pLineOutBuf->ring_empty_consecutive();
 pLineSupply = pLineOutBuf->supply;

 pOutBuffer  = pOutBuf->consume;
 outCount    = pOutBuf->ring_full_consecutive();
 saveOutCount = outCount;

 /*
  * Start NetWork Output Stream Parsing
  */
 if (in3270E && !noTN3270EHdr)
  {
   // We are in TN3270E Mode and transmiting 3270 data stream ..
   // We must prefix any message with TN3270E Header ...
   if (GetTN3270EMode() == T3270MODE)
      *pLineSupply++ = T3270_DATA; //3270 DATA_TYPE
   else *pLineSupply++ = NVT_DATA; //NVT DATA_TYPE
	lineCount++;
	*pLineSupply++ = ERR_COND_CLEARED;   //REQUEST_FLAG  (0x00)
	lineCount++;
	*pLineSupply++ = NO_RESPONSE;       //RESPONSE_FLAG  (0x00)
	lineCount++;
   unsigned char seq;
   unsigned char  pSeqDefault[2] = {0x00,0x00};
   unsigned char* pSeq;

   // If connections is an TN3270E it needs
   // to send sequence number only if RESPONSES function
   // was negotiated and server agrees, else sequence number
   // must be set to 0x00,0x00.
   if (IsMyFunctionActive(RESPONSES))
      pSeq = (unsigned char*)&sequenceNumber;
   else pSeq = &pSeqDefault[0];

   memcpy (&seq, pSeq+1, 1);
	*pLineSupply++ = seq;       //SEQ_NUMBER Part 1
  	lineCount++;
   if (seq == IAC)
    {
     *pLineSupply++ = seq;      // FF not IAC must be doubled
     	lineCount++;
    }
   memcpy (&seq, pSeq, 1);
	*pLineSupply++ = seq;       //SEQ_NUMBER Part 2
 	lineCount++;
   if (seq == IAC)
    {
     *pLineSupply++ = seq;      // FF not IAC must be doubled
     	lineCount++;
    }
   if (sequenceNumber >= 32767)
     sequenceNumber = 0;
   else sequenceNumber++;
  }

 while (outCount)
	 {
	  byte = *pOutBuffer++;   outCount--;
	  if (!myopts[TN_TRANSMIT_BINARY] )
		{
        // NVT Mode and not binary
		  switch (byte)
			{
			 case    LF:   /*
								 * If we are in CRMOD mode (i.e, CR = LF)
								 * on our local terminal, then probably
								 * a newline (LF) is CRLF (Telnet).
								 */
								if (MODE_LOCAL_CHARS(globalMode))
								 {
									*pLineSupply++ = CR;
									lineCount++;
									if (!CheckBuf("Line Output", pLineOutBuf,pLineSupply,
													lineCount, lineMaxLen, false))
									  return false;
								 }
								*pLineSupply++ = LF;
								lineCount++;
								if (!CheckBuf("Line Output", pLineOutBuf,pLineSupply,
												  lineCount, lineMaxLen, false))
								  return false;
								flushLine     = true;
								break;

			 case    CR:   if (crlf)
								 {
								  *pLineSupply++ = CR;
								  lineCount++;
								  *pLineSupply++ = LF;
								  lineCount++;
								  if (!CheckBuf("Line Output", pLineOutBuf,pLineSupply,
														lineCount, lineMaxLen, false))
									  return false;
								 }
								else
								 {
								  *pLineSupply++ = CR;
								  lineCount++;
								  *pLineSupply++ = 0;
								  lineCount++;
								  if (!CheckBuf("Line Output", pLineOutBuf,pLineSupply,
													lineCount, lineMaxLen, false))
									 return false;
								 }
								flushLine     = true;
								break;

			 case    IAC:
								if (!oobData)
								 {
								  *pLineSupply++ = IAC;
								  lineCount++;
								 }
								*pLineSupply++ = IAC;
								lineCount++;
								if (!CheckBuf("Line Output", pLineOutBuf,pLineSupply,
													lineCount, lineMaxLen, false))
								  return false;
								break;

			 default    :  *pLineSupply++ = byte;
								lineCount++;
								if (!CheckBuf("Line Output", pLineOutBuf,pLineSupply,
												lineCount, lineMaxLen, false))
								  return false;
								break;
			}

		}
      // 3270 Mode (assume Transmit Binary)
		else if ((byte == IAC) && (!oobData))
				{
				 *pLineSupply++ = IAC;
				 lineCount++;
				 *pLineSupply++ = IAC;
				 lineCount++;
				 if (!CheckBuf("Line Output", pLineOutBuf,pLineSupply,
								 lineCount, lineMaxLen, false))
					 return false;
				}
			  else
				{
				 *pLineSupply++ = byte;
				 lineCount++;
				 if (!CheckBuf("Line Output", pLineOutBuf,pLineSupply,
									lineCount, lineMaxLen, false))
				  return false;
				}
	 }

 /* Freeing consumed data on OutBuf ring */
 pOutBuf->ring_consumed(saveOutCount);

 /* If we are in Binary Mode,  Data Stream MUST be Terminated by IAC,EOR */
 if ((myopts[TN_TRANSMIT_BINARY] && myopts[TN_EOR]) && (!oobData))
  {
	*pLineSupply++ = IAC;
	*pLineSupply++ = EOR;
	 lineCount +=2;
  }

 /* Closing supplied data on Line Out Buffer */
 if (lineCount)
  {
	pLineOutBuf->ring_supplied(lineCount);
	return true;
  }
  else return false;
}

bool
ALFFTelnet::SetNeededResponse(LPALFFBUF pNetOutBuf, LPARAM messageCtrl,
                                 int deviceStatus, int senseCode)
{
 /* RESPONSE type message
  */
#define RESPONSEMESSAGELENGTH      6
 char responseMessage [] = {RESPONSE,
                                0x00, // REQUEST_FLAG (not used)
                                0x00, // RESPONSE_FLAG (POSITIVE or NEGATIVE)
                           0x00,0x00, // SEQUENCE_NUMBER OF RELATED SERVER MESSAGE
                                0x00}; // Data Portion : set depending upon errors
 char actualMessage[RESPONSEMESSAGELENGTH];

 unsigned char requestFlag, responseFlag, seqNumber[2];

 if (!IsMyFunctionActive(RESPONSES))
  return false;

 /* Message Ctrl Decoding  */
 unsigned char temp[4];
 memcpy (&temp[0], &messageCtrl, 4);
 memcpy (&requestFlag, &temp[0], 1);
 memcpy (&responseFlag, &temp[1], 1);
 memcpy (&seqNumber[0], &temp[2], 2);

 if ((responseFlag ==  NO_RESPONSE)
   || ((responseFlag ==  ERROR_RESPONSE)
     &&(deviceStatus == DEVICE_READY)) )
   {
    // Need no RESPONSE type message sent back
    return false;
   }

 // Building response message
 memcpy (actualMessage, responseMessage, RESPONSEMESSAGELENGTH);

 // Set the sequence number on message
 memcpy (&actualMessage[3], seqNumber, 2);

 if ((responseFlag == ALWAYS_RESPONSE)
   ||(responseFlag == ERROR_RESPONSE))
  {
   if (deviceStatus == DEVICE_READY)
    {
     if (responseFlag == ALWAYS_RESPONSE)
      {
       memset(&actualMessage[2], POSITIVE_RESPONSE, 1);
       memset(&actualMessage[5], DEVICE_END, 1);
       // Data portion is already defined as "Device End"
      }
     else return false; // Need no response
    }
   else
    {
     memset(&actualMessage[2], NEGATIVE_RESPONSE, 1);
     memcpy(&actualMessage[5], &(char*)senseCode, 1);
    }
  }
 else return false;

 char* netOutBuf = pNetOutBuf->supply;
 int maxDataLen = pNetOutBuf->ring_empty_consecutive();
 if (RESPONSEMESSAGELENGTH <= maxDataLen)
  {
   memcpy (netOutBuf, actualMessage, RESPONSEMESSAGELENGTH);
   pNetOutBuf->ring_supplied(RESPONSEMESSAGELENGTH);
   return true;
  }
 else
  {
   pMsgs->Message(IDS_REMPRTSTATUSOUTOFMEM,IDS_TIT3270PRTERR);
   return false;
  }
}

bool
ALFFTelnet::SendRequestClearError(LPALFFBUF pLineOutBuf)
{
 register char *pLineSupply;
 register int lineCount;
 int lineMaxLen;

 /* This DATA_Type (REQUEST) is valid only when RESPONSES
  * was negotiated
  */
 if (!IsMyFunctionActive(RESPONSES))
  return false;

 /*
  * Initialize Line Output Controls
  */
 lineCount = 0;
 lineMaxLen = pLineOutBuf->ring_empty_consecutive();
 pLineSupply = pLineOutBuf->supply;

 /* REQUEST Data_Type with ERR_COND_CLEARED Status */
 *pLineSupply++ = REQUEST_TYPE;
 lineCount++;
 *pLineSupply++ = ERR_COND_CLEARED;
 lineCount++;
 *pLineSupply++ = NULL;  //RESPONSE_FLAG Not used
 lineCount++;

 // If connections is an TN3270E it needs
 // to send sequence number only if RESPONSES function
 // was negotiated and server agrees, else sequence number
 // must be set to 0x00,0x00.
 unsigned char seq;
 unsigned char* pSeq;

 pSeq = (unsigned char*)&sequenceNumber;

 //SEQ_NUMBER Part 1
 memcpy (&seq, pSeq+1, 1);
 *pLineSupply++ = seq;
 lineCount++;
 if (seq == IAC)
  {
   *pLineSupply++ = seq;      // FF not IAC must be doubled
  	lineCount++;
  }

 //SEQ_NUMBER Part 2
 memcpy (&seq, pSeq, 1);
 *pLineSupply++ = seq;
 lineCount++;
 if (seq == IAC)
  {
   *pLineSupply++ = seq;      // FF not IAC must be doubled
  	lineCount++;
  }

 if (sequenceNumber >= 32767)
   sequenceNumber = 0;
 else sequenceNumber++;

 if (!CheckBuf("Line Output", pLineOutBuf,pLineSupply,
 					  lineCount, lineMaxLen, false))
    	 return false;

 /* Insert the needing IAC + EOR */
 *pLineSupply++ = IAC;
 *pLineSupply++ = EOR;
  lineCount +=2;
 if (!CheckBuf("Line Output", pLineOutBuf,pLineSupply,
					  lineCount, lineMaxLen, false))
   return false;


 /* Closing supplied data on Line Out Buffer */
 if (lineCount)
  {
	pLineOutBuf->ring_supplied(lineCount);
	return true;
  }
 else return false;
}

void
ALFFTelnet::SetOOBData(bool option)
{
 if (option)
  oobData = true;
  else oobData = false;
}

char*
ALFFTelnet::GetTerminalType()
{
 if (sentTermType && (strlen(terminalType)))
  {
	return (char*) terminalType;
  }
 else return 0;
}

char*
ALFFTelnet::GetTerminalModel()
{
 if (sentTermType && (strlen(terminalModel)))
  {
	return (char*) terminalModel;
  }
 else return 0;
}

