home *** CD-ROM | disk | FTP | other *** search
/ synchro.net / synchro.net.tar / synchro.net / main / COMM / CTA6_SRC.ZIP / TNCNX.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  2000-04-24  |  22.6 KB  |  678 lines

  1. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2.  
  3. Program:      TNCNX.PAS
  4. Object:       Delphi component which implement the TCP/IP telnet protocol
  5.               including some options negociations.
  6.               RFC854, RFC885, RFC779, RFC1091
  7. Author:       Franτois PIETTE
  8. EMail:        francois.piette@pophost.eunet.be    francois.piette@ping.be
  9.               francois.piette@rtfm.be             http://www.rtfm.be/fpiette
  10. Creation:     April, 1996
  11. Version:      2.05
  12. Support:      Use the mailing list twsocket@rtfm.be See website for details.
  13. Legal issues: Copyright (C) 1996, 1997, 1998 by Franτois PIETTE
  14.               Rue de Grady 24, 4053 Embourg, Belgium. Fax: +32-4-365.74.56
  15.               <francois.piette@pophost.eunet.be>
  16.  
  17.               This software is provided 'as-is', without any express or
  18.             implied warranty.  In no event will the author be held liable
  19.               for any  damages arising from the use of this software.
  20.  
  21.               Permission is granted to anyone to use this software for any
  22.               purpose, including commercial applications, and to alter it
  23.               and redistribute it freely, subject to the following
  24.               restrictions:
  25.  
  26.               1. The origin of this software must not be misrepresented,
  27.                  you must not claim that you wrote the original software.
  28.                  If you use this software in a product, an acknowledgment
  29.                  in the product documentation would be appreciated but is
  30.                  not required.
  31.  
  32.               2. Altered source versions must be plainly marked as such, and
  33.                  must not be misrepresented as being the original software.
  34.  
  35.               3. This notice may not be removed or altered from any source
  36.                  distribution.
  37.  
  38. Updates:
  39. Jul 22, 1997 Adapted to Delphi 3
  40. Sep 5, 1997  Added version information, removed old code, added OnTermType
  41.              Renamed some indentifier to be more standard.
  42. Sep 24, 1997 V2.03 Added procedures to negociate options
  43. May 12, 1998 V2.04 Changed NegociateOption to properly handle unwanted
  44.              option as Jan Tomasek <xtomasej@feld.cvut.cz> suggested.
  45. Aug 10, 1998 V2.05 Cleared strSubOption after NegociateSubOption as Jan
  46.              Tomasek <xtomasej@feld.cvut.cz> suggested.
  47.  
  48.  
  49.  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  50. unit TnCnx;
  51.  
  52. interface
  53.  
  54. uses
  55.   SysUtils, WinTypes, WinProcs, Messages, Classes, Controls, Forms,
  56.   WSocket, Winsock;
  57.  
  58. const
  59.   TnCnxVersion       = 205;
  60.   CopyRight : String = ' TTnCnx (c) 96-98 F. Piette V2.05 ';
  61.  
  62.   { Telnet command characters                                             }
  63.   TNCH_EOR        = #239;     { $EF End Of Record (preceded by IAC)       }
  64.   TNCH_SE         = #240;     { $F0 End of subnegociation parameters      }
  65.   TNCH_NOP        = #241;     { $F1 No operation                          }
  66.   TNCH_DATA_MARK  = #242;     { $F2 Data stream portion of a Synch        }
  67.   TNCH_BREAK      = #243;     { $F3 NVT charcater break                   }
  68.   TNCH_IP         = #244;     { $F4 Interrupt process                     }
  69.   TNCH_AO         = #245;     { $F5 Abort output                          }
  70.   TNCH_AYT        = #246;     { $F6 Are you there                         }
  71.   TNCH_EC         = #247;     { $F7 Erase character                       }
  72.   TNCH_EL         = #248;     { $F8 Erase line                            }
  73.   TNCH_GA         = #249;     { $F9 Go ahead                              }
  74.   TNCH_SB         = #250;     { $FA Subnegociation                        }
  75.   TNCH_WILL       = #251;     { $FB Will                                  }
  76.   TNCH_WONT       = #252;     { $FC Wont                                  }
  77.   TNCH_DO         = #253;     { $FD Do                                    }
  78.   TNCH_DONT       = #254;     { $FE Dont                                  }
  79.   TNCH_IAC        = #255;     { $FF IAC                                   }
  80.  
  81.   { Telnet options                                                        }
  82.   TN_TRANSMIT_BINARY      = #0;   { $00 }
  83.   TN_ECHO                 = #1;   { $01 }
  84.   TN_RECONNECTION         = #2;   { $02 }
  85.   TN_SUPPRESS_GA          = #3;   { $03 }
  86.   TN_MSG_SZ_NEGOC         = #4;   { $04 }
  87.   TN_STATUS               = #5;   { $05 }
  88.   TN_TIMING_MARK          = #6;   { $06 }
  89.   TN_NOPTIONS             = #6;   { $06 }
  90.   TN_DET                  = #20;  { $14 }
  91.   TN_SEND_LOC             = #23;  { $17 }
  92.   TN_TERMTYPE             = #24;  { $18 }
  93.   TN_EOR                  = #25;  { $19 }
  94.   TN_NAWS                 = #31;  { $1F }
  95.   TN_TERMSPEED            = #32;  { $20 }
  96.   TN_TFC                  = #33;  { $21 }
  97.   TN_XDISPLOC             = #35;  { $23 }
  98.   TN_EXOPL                = #255; { $FF }
  99.  
  100.   TN_TTYPE_SEND          = #1;
  101.   TN_TTYPE_IS          = #0;
  102.  
  103. type
  104.   TTnCnx = class;
  105.  
  106.   TTnSessionConnected = procedure (Sender: TTnCnx; Error : word) of object;
  107.   TTnSessionClosed    = procedure (Sender: TTnCnx; Error : word) of object;
  108.   TTnDataAvailable    = procedure (Sender: TTnCnx; Buffer : PChar; Len : Integer) of object;
  109.   TTnDisplay          = procedure (Sender: TTnCnx; Str : String) of object;
  110.  
  111.   TTnCnx= class(TComponent)
  112.   public
  113.     Socket              : TWSocket;
  114.   private
  115.     FPort               : String;
  116.     FHost               : String;
  117.     FLocation           : String;
  118.     FTermType           : String;
  119.     RemoteBinMode       : Boolean;
  120.     LocalBinMode        : Boolean;
  121.     FLocalEcho          : Boolean;
  122.     Spga                : Boolean;
  123.     FTType              : Boolean;
  124.     Ftransparent        : boolean;
  125.     FBufferSize         : word;
  126.     FBuffer             : array of char;
  127.     FBufferCnt          : Integer;
  128.     FWindowHandle       : HWND;
  129.     FOnSessionConnected : TTnSessionConnected;
  130.     FOnSessionClosed    : TTnSessionClosed;
  131.     FOnDataAvailable    : TTnDataAvailable;
  132.     FOnDisplay          : TTnDisplay;
  133.     FOnEOR              : TNotifyEvent;
  134.     FOnSendLoc          : TNotifyEvent;
  135.     FOnTermType         : TNotifyEvent;
  136.     FOnLocalEcho        : TNotifyEvent;
  137.     procedure WndProc(var MsgRec: TMessage);
  138.     procedure SocketSessionConnected(Sender: TObject; Error : word);
  139.     procedure SocketSessionClosed(Sender: TObject; Error : word);
  140.     procedure SocketDataAvailable(Sender: TObject; Error : word);
  141.     //tmr change me (display)
  142.     procedure Display(Str : String);
  143.     procedure AddChar(Ch : Char);
  144.     procedure ReceiveChar(Ch : Char);
  145.     procedure Answer(chAns : Char; chOption : Char);
  146.     procedure NegociateSubOption(strSubOption : String);
  147.     procedure NegociateOption(chAction : Char; chOption : Char);
  148.     procedure FlushBuffer;
  149.     function  GetState : TSocketState;
  150.     procedure CreateBuffer(newbuf : word);
  151.   public
  152.     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  153.     constructor Create(AOwner: TComponent); override;
  154.     destructor  Destroy; override;
  155.     function    Send(Data : Pointer; Len : Integer) : integer;
  156.     function    SendStr(Data : String) : integer;
  157.     procedure   Connect;
  158.     function    IsConnected : Boolean;
  159.     procedure   WillOption(chOption : Char);
  160.     procedure   WontOption(chOption : Char);
  161.     procedure   DontOption(chOption : Char);
  162.     procedure   DoOption(chOption : Char);
  163.     procedure   Close;
  164.     procedure   Pause;
  165.     procedure   Resume;
  166.     property    State : TSocketState                  read  GetState;
  167.     property    Handle : HWND                         read  FWindowHandle;
  168.   published
  169.     property buffersize : word                        read  FbufferSize
  170.                                                       write CreateBuffer;
  171.     property transparent : boolean                    read  Ftransparent
  172.                                                       write Ftransparent;
  173.     property Port : String                            read  FPort
  174.                                                       write FPort;
  175.     property Host : String                            read  FHost
  176.                                                       write FHost;
  177.     property Location : String                        read  FLocation
  178.                                                       write FLocation;
  179.     property TermType : String                        read  FTermType
  180.                                                       write FTermType;
  181.     property LocalEcho : Boolean                      read  FLocalEcho
  182.                                                       write FLocalEcho;
  183.     property OnSessionConnected : TTnSessionConnected read  FOnSessionConnected
  184.                                                       write FOnSessionConnected;
  185.     property OnSessionClosed :    TTnSessionClosed    read  FOnSessionClosed
  186.                                                       write FOnSessionClosed;
  187.     property OnDataAvailable :    TTnDataAvailable    read  FOnDataAvailable
  188.                                                       write FOnDataAvailable;
  189.     property OnDisplay :          TTnDisplay          read  FOnDisplay
  190.                                                       write FOnDisplay;
  191.     property OnEndOfRecord :      TNotifyEvent        read  FOnEOR
  192.                                                       write FOnEOR;
  193.     property OnSendLoc :          TNotifyEvent        read  FOnSendLoc
  194.                                                       write FOnSendLoc;
  195.     property OnTermType :         TNotifyEvent        read  FOnTermType
  196.                                                       write FOnTermType;
  197.     property OnLocalEcho :        TNotifyEvent        read  FOnLocalEcho
  198.                                                       write FOnLocalEcho;
  199.   end;
  200.  
  201. procedure Register;
  202.  
  203. implementation
  204.  
  205. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  206. procedure Register;
  207. begin
  208.   RegisterComponents('FPiette', [TTnCnx]);
  209. end;
  210.  
  211. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  212. Procedure TTnCnx.CreateBuffer(NewBuf:Word);
  213. begin
  214.   if NewBuf <> FBufferSize then
  215.   begin
  216.      if FBufferCnt > 0 then
  217.         FlushBuffer;
  218.      FbufferSize := Newbuf;
  219.      Socket.BufSize := FbufferSize;
  220.      setlength(Fbuffer,FbufferSize+1);
  221.   end;
  222. end;
  223.  
  224.  
  225. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  226. procedure TTnCnx.WndProc(var MsgRec: TMessage);
  227. begin
  228.      with MsgRec do
  229.          Result := DefWindowProc(Handle, Msg, wParam, lParam);
  230. end;
  231.  
  232.  
  233. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  234. constructor TTnCnx.Create(AOwner: TComponent);
  235. begin
  236.     inherited Create(AOwner);
  237.     FWindowHandle             := AllocateHWnd(WndProc);
  238.     FLocation                 := 'TNCNX';
  239.     FTermType                 := 'VT100';
  240.     FPort                     := '23';
  241.     FBufferSize               := 0;
  242.     FTransparent              := false;
  243.     Socket                    := TWSocket.Create(Self);
  244.     Socket.OnSessionConnected := SocketSessionConnected;
  245.     Socket.OnDataAvailable    := SocketDataAvailable;
  246.     Socket.OnSessionClosed    := SocketSessionClosed;
  247.     CreateBuffer(2048);
  248. end;
  249.  
  250.  
  251. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  252. destructor TTnCnx.Destroy;
  253. begin
  254.     if Assigned(Socket) then begin
  255.         Socket.Free;
  256.         Socket := nil;
  257.     end;
  258.     DeallocateHWnd(FWindowHandle);
  259.     inherited Destroy;
  260. end;
  261.  
  262.  
  263. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  264. procedure TTnCnx.Notification(AComponent: TComponent; Operation: TOperation);
  265. begin
  266.     inherited Notification(AComponent, Operation);
  267.     if (AComponent = Socket) and (Operation = opRemove) then
  268.         Socket := nil;
  269. end;
  270.  
  271.  
  272. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  273. procedure TTnCnx.Pause;
  274. begin
  275.     if not Assigned(Socket) then
  276.         Exit;
  277.     Socket.Pause;
  278. end;
  279.  
  280.  
  281. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  282. procedure TTnCnx.Resume;
  283. begin
  284.     if not Assigned(Socket) then
  285.         Exit;
  286.     Socket.Resume;
  287. end;
  288.  
  289.  
  290. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  291. procedure TTnCnx.Connect;
  292. begin
  293.     if not Assigned(Socket) then
  294.         Exit;
  295.  
  296.     if Socket.State <> wsClosed then
  297.         Socket.Close;
  298.  
  299.     Socket.Proto := 'tcp';
  300.     Socket.Port  := FPort;
  301.     Socket.Addr  := FHost;
  302.     Socket.Connect;
  303. end;
  304.  
  305.  
  306. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  307. function TTnCnx.IsConnected : Boolean;
  308. begin
  309.     Result := Socket.State = wsConnected;
  310. end;
  311.  
  312.  
  313. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  314. procedure TTnCnx.Close;
  315. begin
  316.     if Assigned(Socket) then
  317.         Socket.Close;
  318. end;
  319.  
  320.  
  321. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  322. procedure TTnCnx.Display(Str : String);
  323. begin
  324.     if Assigned(FOnDisplay) then
  325.         FOnDisplay(Self, Str);
  326. end;
  327.  
  328.  
  329. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  330. function TTnCnx.GetState : TSocketState;
  331. begin
  332.     if Assigned(Socket) then
  333.         Result := Socket.State
  334.     else
  335.         Result := wsInvalidState;
  336. end;
  337.  
  338.  
  339. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  340. procedure TTnCnx.SocketSessionConnected(Sender: TObject; Error : word);
  341. begin
  342.     if Assigned(FOnSessionConnected) then
  343.         FOnSessionConnected(Self, Error);
  344. end;
  345.  
  346.  
  347. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  348. procedure TTnCnx.SocketSessionClosed(Sender: TObject; Error : word);
  349. begin
  350.     if Socket.State <> wsClosed then
  351.         Socket.Close;
  352.     if Assigned(FOnSessionClosed) then
  353.         FOnSessionClosed(Self, Error);
  354. end;
  355.  
  356.  
  357. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  358. procedure TTnCnx.SocketDataAvailable(Sender: TObject; Error : word);
  359. var
  360.     Len, I,X : Integer;
  361.     Buffer : array of char;
  362.     Socket : TWSocket;
  363. begin
  364.     setlength(Buffer,FBufferSize+1);
  365.     Socket := Sender as TWSocket;
  366.     Len := Socket.Receive(@Buffer[0], High(Buffer));
  367.     if Len = 0 then begin
  368.         { Remote has closed }
  369.         //tmr raise error events insted of stupid display
  370.         Display(#13 + #10 + '**** Remote has closed ****' + #13 + #10);
  371.     end
  372.     else if Len < 0 then begin
  373.         { An error has occured }
  374.         if Socket.LastError <> WSAEWOULDBLOCK then
  375.             Display(#13 + #10 + '**** ERROR: ' + IntToStr(Socket.LastError) +
  376.                     ' ****' + #13 + #10);
  377.     end
  378.     else
  379.     begin
  380.     dec(len);
  381.     if not Ftransparent then
  382.     begin
  383.        for I := 0 to Len do
  384.            ReceiveChar(Buffer[I]);
  385.        FlushBuffer;
  386.     end
  387.     else
  388.     begin
  389.        X := 0;
  390.        repeat
  391.           if len > (Fbuffersize-FbufferCnt) then
  392.              I := FbufferSize-FbufferCnt
  393.           else
  394.              I := Len;
  395.  
  396.           move(Buffer[X],Fbuffer[Fbuffercnt],I);
  397.  
  398.           inc(FbufferCnt,I);
  399.           dec(len,I);
  400.           inc(X,I); //usless if small buffer
  401.  
  402.           if FbufferCnt = FbufferSize then
  403.              FlushBuffer;
  404.  
  405.        until Len = 0;
  406.  
  407.        FlushBuffer;
  408.     end;
  409.    end;
  410. end;
  411.  
  412.  
  413. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  414. function  TTnCnx.Send(Data : Pointer; Len : Integer) : integer;
  415. begin
  416.     if Assigned(Socket) then
  417.         Result := Socket.Send(Data, Len)
  418.     else
  419.         Result := -1;
  420. end;
  421.  
  422.  
  423. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  424. function TTnCnx.SendStr(Data : String) : integer;
  425. begin
  426.     Result := Send(@Data[1], Length(Data));
  427. end;
  428.  
  429.  
  430. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  431. procedure TTnCnx.Answer(chAns : Char; chOption : Char);
  432. var
  433.     Buf   : String[3];
  434. begin
  435.     Buf := TNCH_IAC + chAns + chOption;
  436.     Socket.Send(@Buf[1], Length(Buf));
  437. end;
  438.  
  439.  
  440. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  441. procedure TTnCnx.WillOption(chOption : Char);
  442. begin
  443.     Answer(TNCH_WILL, chOption);
  444. end;
  445.  
  446.  
  447. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  448. procedure TTnCnx.WontOption(chOption : Char);
  449. begin
  450.     Answer(TNCH_WONT, chOption);
  451. end;
  452.  
  453.  
  454. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  455. procedure TTnCnx.DontOption(chOption : Char);
  456. begin
  457.     Answer(TNCH_DONT, chOption);
  458. end;
  459.  
  460.  
  461. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  462. procedure TTnCnx.DoOption(chOption : Char);
  463. begin
  464.     Answer(TNCH_DO, chOption);
  465. end;
  466.  
  467.  
  468. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  469. procedure TTnCnx.NegociateSubOption(strSubOption : String);
  470. var
  471.     Buf   : String;
  472. begin
  473.     case strSubOption[1] of
  474.     TN_TERMTYPE:
  475.         begin
  476.             if strSubOption[2] = TN_TTYPE_SEND then begin
  477.                 if Assigned(FOnTermType) then
  478.                     FOnTermType(Self);
  479.                 Buf := TNCH_IAC + TNCH_SB + TN_TERMTYPE + TN_TTYPE_IS + FTermType + TNCH_IAC + TNCH_SE;
  480.                 Socket.Send(@Buf[1], Length(Buf));
  481.             end;
  482.         end;
  483.     end;
  484. end;
  485.  
  486.  
  487. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  488. procedure TTnCnx.NegociateOption(chAction : Char; chOption : Char);
  489. var
  490.     Buf : String;
  491. begin
  492.     case chOption of
  493.     TN_TRANSMIT_BINARY:
  494.         begin
  495.             if chAction = TNCH_WILL then begin
  496.                 Answer(TNCH_DO, chOption);
  497.                 RemoteBinMode := TRUE;
  498.                 LocalBinMode  := TRUE;
  499.             end
  500.             else if chAction = TNCH_WONT then begin
  501.                 if RemoteBinMode then begin
  502.                     RemoteBinMode := FALSE;
  503.                     LocalBinMode  := FALSE;
  504.                 end;
  505.             end;
  506.         end;
  507.     TN_ECHO:
  508.         begin
  509.             if chAction = TNCH_WILL then begin
  510.                 Answer(TNCH_DO, chOption);
  511.                 FLocalEcho := FALSE;
  512.             end
  513.             else if chAction = TNCH_WONT then begin
  514.                 FLocalEcho := TRUE;
  515.             end;
  516.             if Assigned(FOnLocalEcho) then
  517.                 FOnLocalEcho(self);
  518.         end;
  519.     TN_SUPPRESS_GA:
  520.         begin
  521.             if chAction = TNCH_WILL then begin
  522.                 Answer(TNCH_DO, chOption);
  523.                 spga := TRUE;
  524.             end;
  525.         end;
  526.     TN_TERMTYPE:
  527.         begin
  528.             if chAction = TNCH_DO then begin
  529.                 Answer(TNCH_WILL, chOption);
  530.                 FTType := TRUE;
  531.             end;
  532.         end;
  533.     TN_SEND_LOC:
  534.         begin
  535.             if chAction = TNCH_DO then begin
  536.                 Answer(TNCH_WILL, chOption);
  537.                 if Assigned(FOnSendLoc) then
  538.                     FOnSendLoc(Self);
  539.                 Buf := TNCH_IAC + TNCH_SB + TN_SEND_LOC + FLocation + TNCH_IAC + TNCH_SE;
  540.                 Socket.Send(@Buf[1], Length(Buf));
  541.             end;
  542.         end;
  543.     TN_EOR:
  544.         begin
  545.             if chAction = TNCH_DO then begin
  546.                 Answer(TNCH_WILL, chOption);
  547.                 FTType := TRUE;
  548.             end;
  549.         end;
  550.     else
  551.         if chAction = TNCH_WILL then
  552.             Answer(TNCH_DONT, chOption)
  553.         else
  554.             Answer(TNCH_WONT, chOption);
  555.     end;
  556. end;
  557.  
  558.  
  559. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  560. procedure TTnCnx.FlushBuffer;
  561. var
  562.     Buffer : PChar;
  563.     Count  : Integer;
  564. begin
  565.     try
  566.         if FBufferCnt > 0 then begin
  567.             if Assigned(FOnDataAvailable) then begin
  568.                 { We need to make a copy for the data because we can reenter   }
  569.                 { during the event processing                                  }
  570.                 Count := FBufferCnt;             { How much we received        }
  571.                 try
  572.                     GetMem(Buffer, Count + 1);       { Alloc memory for the copy   }
  573.                 except
  574.                     Buffer := nil;
  575.                 end;
  576.                 if Buffer <> nil then begin
  577.                     try
  578.                         Move(FBuffer[0], Buffer^, Count);   { Actual copy             }
  579.                         Buffer[Count] := #0;             { Add a nul byte          }
  580.                         FBufferCnt := 0;                 { Reset receivecounter    }
  581.                         FOnDataAvailable(Self, Buffer, Count); { Call event handler  }
  582.                     finally
  583.                         FreeMem(Buffer, Count + 1);      { Release the buffer      }
  584.                     end;
  585.                 end;
  586.             end
  587.             else begin
  588.                 FBufferCnt := 0
  589.             end;
  590.         end;
  591.     except
  592.         raise;
  593.     end;
  594. end;
  595.  
  596.  
  597. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  598. procedure TTnCnx.AddChar(Ch : Char);
  599. begin
  600.     FBuffer[FBufferCnt] := Ch;
  601.     Inc(FBufferCnt);
  602.     if FBufferCnt = FBufferSize then
  603.         FlushBuffer;
  604. end;
  605.  
  606.  
  607. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  608. procedure TTnCnx.ReceiveChar(Ch : Char);
  609. const
  610.     bIAC         : Boolean = FALSE;
  611.     chVerb       : Char    = #0;
  612.     strSubOption : String  = '';
  613.     bSubNegoc    : Boolean = FALSE;
  614. begin
  615.     if chVerb <> #0 then begin
  616.         NegociateOption(chVerb, Ch);
  617.         chVerb       := #0;
  618.         strSubOption := '';
  619.         Exit;
  620.     end;
  621.  
  622.     if bSubNegoc then begin
  623.         if Ch = TNCH_SE then begin
  624.             bSubNegoc    := FALSE;
  625.             NegociateSubOption(strSubOption);
  626.             strSubOption := '';
  627.         end
  628.         else
  629.             strSubOption := strSubOption + Ch;
  630.         Exit;
  631.     end;
  632.  
  633.     if bIAC then begin
  634.         case Ch of
  635.         TNCH_IAC: begin
  636.                       AddChar(Ch);
  637.                       bIAC := FALSE;
  638.                   end;
  639.         TNCH_DO, TNCH_WILL, TNCH_DONT, TNCH_WONT:
  640.                   begin
  641.                       bIAC   := FALSE;
  642.                       chVerb := Ch;
  643.                   end;
  644.         TNCH_EOR:
  645.             begin
  646.                 bIAC   := FALSE;
  647.                 if Assigned(FOnEOR) then
  648.                     FOnEOR(Self);
  649.             end;
  650.         TNCH_SB:
  651.             begin
  652.                 bSubNegoc := TRUE;
  653.                 bIAC      := FALSE;
  654.             end;
  655.         else
  656.             bIAC := FALSE;
  657.         end;
  658.         Exit;
  659.     end;
  660.  
  661.     case Ch of
  662.     TNCH_EL:  AddChar(Ch);
  663.     TNCH_EC:  AddChar(Ch);
  664.     TNCH_AYT: AddChar(Ch);
  665.     TNCH_IP:  AddChar(Ch);
  666.     TNCH_AO:  AddChar(Ch);
  667.     TNCH_IAC: bIAC := TRUE;
  668.     else
  669.         AddChar(Ch);
  670.     end;
  671. end;
  672.  
  673.  
  674. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  675.  
  676. end.
  677.  
  678.