3 {$B-} { Enable partial boolean evaluation }
4 {$T-} { Untyped pointers }
5 {$X+} { Enable extended syntax }
6 {$IFNDEF VER80} { Not for Delphi 1 }
7 {$H+} { Use long strings }
8 {$J+} { Allow typed constant to be modified }
10 {$IFDEF VER110} { C++ Builder V3.0 }
13 {$IFDEF VER125} { C++ Builder V4.0 }
20 SysUtils
, WinTypes
, WinProcs
, Messages
{ , Classes } , Controls
, Forms
,
25 CopyRight
: String = ' TTnCnx (c) 1996-2002 F. Piette V2.09 ';
27 { Telnet command characters }
28 TNCH_EOR
= #239; { $EF End Of Record (preceded by IAC) }
29 TNCH_SE
= #240; { $F0 End of subnegociation parameters }
30 TNCH_NOP
= #241; { $F1 No operation }
31 TNCH_DATA_MARK
= #242; { $F2 Data stream portion of a Synch }
32 TNCH_BREAK
= #243; { $F3 NVT charcater break }
33 TNCH_IP
= #244; { $F4 Interrupt process }
34 TNCH_AO
= #245; { $F5 Abort output }
35 TNCH_AYT
= #246; { $F6 Are you there }
36 TNCH_EC
= #247; { $F7 Erase character }
37 TNCH_EL
= #248; { $F8 Erase line }
38 TNCH_GA
= #249; { $F9 Go ahead }
39 TNCH_SB
= #250; { $FA Subnegociation }
40 TNCH_WILL
= #251; { $FB Will }
41 TNCH_WONT
= #252; { $FC Wont }
42 TNCH_DO
= #253; { $FD Do }
43 TNCH_DONT
= #254; { $FE Dont }
44 TNCH_IAC
= #255; { $FF IAC }
47 TN_TRANSMIT_BINARY
= #0; { $00 }
49 TN_RECONNECTION
= #2; { $02 }
50 TN_SUPPRESS_GA
= #3; { $03 }
51 TN_MSG_SZ_NEGOC
= #4; { $04 }
52 TN_STATUS
= #5; { $05 }
53 TN_TIMING_MARK
= #6; { $06 }
54 TN_NOPTIONS
= #6; { $06 }
56 TN_SEND_LOC
= #23; { $17 }
57 TN_TERMTYPE
= #24; { $18 }
59 TN_NAWS
= #31; { $1F }
60 TN_TERMSPEED
= #32; { $20 }
62 TN_XDISPLOC
= #35; { $23 }
63 TN_EXOPL
= #255; { $FF }
69 TTnCnx
= object(TObj
);
70 PTnCnx
=^TTnCnx
; type MyStupid0
=DWord
;
72 TTnSessionConnected
= procedure (Sender
: TTnCnx
; Error
: word) of object;
73 TTnSessionClosed
= procedure (Sender
: TTnCnx
; Error
: word) of object;
74 TTnDataAvailable
= procedure (Sender
: TTnCnx
; Buffer
: Pointer; Len
: Integer) of object;
75 TTnDisplay
= procedure (Sender
: TTnCnx
; Str
: String) of object;
85 RemoteBinMode
: Boolean;
86 LocalBinMode
: Boolean;
90 FBuffer
: array [0..2048] of char;
93 FOnSessionConnected
: TTnSessionConnected
;
94 FOnSessionClosed
: TTnSessionClosed
;
95 FOnDataAvailable
: TTnDataAvailable
;
96 FOnDisplay
: TTnDisplay
;
97 FOnEOR
: TNotifyEvent
;
98 FOnSendLoc
: TNotifyEvent
;
99 FOnTermType
: TNotifyEvent
;
100 FOnLocalEcho
: TNotifyEvent
;
101 procedure WndProc(var MsgRec
: TMessage
);
102 procedure SocketSessionConnected(Sender
: TObject
; Error
: word);
103 procedure SocketSessionClosed(Sender
: TObject
; Error
: word);
104 procedure SocketDataAvailable(Sender
: TObject
; Error
: word);
105 procedure Display(Str
: String);
106 procedure AddChar(Ch
: Char);
107 procedure ReceiveChar(Ch
: Char);
108 procedure Answer(chAns
: Char; chOption
: Char);
109 procedure NegociateSubOption(strSubOption
: String);
110 procedure NegociateOption(chAction
: Char; chOption
: Char);
111 procedure FlushBuffer
;
112 function GetState
: TSocketState
;
114 { constructor Create(AOwner: TComponent); override;
115 } destructor Destroy
;
116 virtual; function Send(Data
: Pointer; Len
: Integer) : integer;
117 function SendStr(Data
: String) : integer;
119 function IsConnected
: Boolean;
120 procedure WillOption(chOption
: Char);
121 procedure WontOption(chOption
: Char);
122 procedure DontOption(chOption
: Char);
123 procedure DoOption(chOption
: Char);
124 procedure Notification(AComponent
: TComponent
; Operation
: TOperation
); override;
128 property State
: TSocketState read GetState
;
129 property Handle
: HWND read FWindowHandle
;
131 property Port
: String read FPort
133 property Host
: String read FHost
135 property Location
: String read FLocation
137 property TermType
: String read FTermType
139 property LocalEcho
: Boolean read FLocalEcho
141 property OnSessionConnected
: TTnSessionConnected read FOnSessionConnected
142 write FOnSessionConnected
;
143 property OnSessionClosed
: TTnSessionClosed read FOnSessionClosed
144 write FOnSessionClosed
;
145 property OnDataAvailable
: TTnDataAvailable read FOnDataAvailable
146 write FOnDataAvailable
;
147 property OnDisplay
: TTnDisplay read FOnDisplay
149 property OnEndOfRecord
: TNotifyEvent read FOnEOR
151 property OnSendLoc
: TNotifyEvent read FOnSendLoc
153 property OnTermType
: TNotifyEvent read FOnTermType
155 property OnLocalEcho
: TNotifyEvent read FOnLocalEcho
159 function NewTnCnx(AOwner
: TComponent
):PTnCnx
; type MyStupid3137
=DWord
;
165 {$DEFINE Debug} { Add or remove minus sign before dollar sign to }
166 { generate code for debug message output }
168 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
171 RegisterComponents('FPiette', [TTnCnx
]);
175 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
176 procedure DebugString(Msg
: String);
184 Buf
:= IntToHex(Cnt
, 4) + ' ' + #0;
185 OutputDebugString(@Buf
[1]);
188 if Length(Msg
) < High(Msg
) then
189 Msg
[Length(Msg
) + 1] := #0;
192 OutputDebugString(@Msg
[1]);
197 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
198 procedure TTnCnx
.WndProc(var MsgRec
: TMessage
);
201 Result
:= DefWindowProc(Handle
, Msg
, wParam
, lParam
);
205 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
206 constructor TTnCnx
.Create(AOwner
: TComponent
);
208 inherited Create(AOwner
);
209 FWindowHandle
:= WSocket
.AllocateHWnd(WndProc
);
210 FLocation
:= 'TNCNX';
211 FTermType
:= 'VT100';
213 Socket
:= TWSocket
.Create(Self
);
214 Socket
.OnSessionConnected
:= SocketSessionConnected
;
215 Socket
.OnDataAvailable
:= SocketDataAvailable
;
216 Socket
.OnSessionClosed
:= SocketSessionClosed
;
220 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
221 destructor TTnCnx
.Destroy
;
223 if Assigned(Socket
) then begin
227 WSocket
.DeallocateHWnd(FWindowHandle
);
232 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
233 procedure TTnCnx
.Notification(AComponent
: TComponent
; Operation
: TOperation
);
235 inherited Notification(AComponent
, Operation
);
236 if (AComponent
= Socket
) and (Operation
= opRemove
) then
241 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
242 procedure TTnCnx
.Pause
;
244 if not Assigned(Socket
) then
250 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
251 procedure TTnCnx
.Resume
;
253 if not Assigned(Socket
) then
259 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
260 procedure TTnCnx
.Connect
;
262 if not Assigned(Socket
) then
265 if Socket
.State
<> wsClosed
then
268 Socket
.Proto
:= 'tcp';
269 Socket
.Port
:= FPort
;
270 Socket
.Addr
:= FHost
;
275 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
276 function TTnCnx
.IsConnected
: Boolean;
278 Result
:= Socket
.State
= wsConnected
;
282 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
283 procedure TTnCnx
.Close
;
285 if Assigned(Socket
) then
290 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
291 procedure TTnCnx
.Display(Str
: String);
293 if Assigned(FOnDisplay
) then
294 FOnDisplay(Self
, Str
);
298 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
299 function TTnCnx
.GetState
: TSocketState
;
301 if Assigned(Socket
) then
302 Result
:= Socket
.State
304 Result
:= wsInvalidState
;
308 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
309 procedure TTnCnx
.SocketSessionConnected(Sender
: TObject
; Error
: word);
311 if Assigned(FOnSessionConnected
) then
312 FOnSessionConnected(Self
, Error
);
316 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
317 procedure TTnCnx
.SocketSessionClosed(Sender
: TObject
; Error
: word);
319 if Socket
.State
<> wsClosed
then
321 if Assigned(FOnSessionClosed
) then
322 FOnSessionClosed(Self
, Error
);
326 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
327 procedure TTnCnx
.SocketDataAvailable(Sender
: TObject
; Error
: word);
330 Buffer
: array [1..2048] of char;
333 Socket
:= Sender
as TWSocket
;
334 Len
:= Socket
.Receive(@Buffer
[1], High(Buffer
));
335 if Len
= 0 then begin
336 { Remote has closed }
337 Display(#13 + #10 + '**** Remote has closed ****' + #13 + #10);
339 else if Len
< 0 then begin
340 { An error has occured }
341 if Socket
.LastError
<> WSAEWOULDBLOCK
then
342 Display(#13 + #10 + '**** ERROR: ' + IntToStr(Socket
.LastError
) +
343 ' ****' + #13 + #10);
347 ReceiveChar(Buffer
[I
]);
353 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
354 function TTnCnx
.Send(Data
: Pointer; Len
: Integer) : integer;
356 if Assigned(Socket
) then
357 Result
:= Socket
.Send(Data
, Len
)
363 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
364 function TTnCnx
.SendStr(Data
: String) : integer;
366 Result
:= Send(@Data
[1], Length(Data
));
370 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
371 procedure TTnCnx
.Answer(chAns
: Char; chOption
: Char);
375 { DebugString('Answer ' + IntToHex(ord(chAns), 2) + ' ' + IntToHex(ord(ChOption), 2) + #13 + #10); }
376 Buf
:= TNCH_IAC
+ chAns
+ chOption
;
377 Socket
.Send(@Buf
[1], Length(Buf
));
381 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
382 procedure TTnCnx
.WillOption(chOption
: Char);
384 Answer(TNCH_WILL
, chOption
);
388 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
389 procedure TTnCnx
.WontOption(chOption
: Char);
391 Answer(TNCH_WONT
, chOption
);
395 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
396 procedure TTnCnx
.DontOption(chOption
: Char);
398 Answer(TNCH_DONT
, chOption
);
402 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
403 procedure TTnCnx
.DoOption(chOption
: Char);
405 Answer(TNCH_DO
, chOption
);
409 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
410 procedure TTnCnx
.NegociateSubOption(strSubOption
: String);
414 { DebugString('SubNegociation ' +
415 IntToHex(ord(strSubOption[1]), 2) + ' ' +
416 IntToHex(ord(strSubOption[2]), 2) + #13 + #10); }
418 case strSubOption
[1] of
421 if strSubOption
[2] = TN_TTYPE_SEND
then begin
422 { DebugString('Send TermType' + #13 + #10); }
423 if Assigned(FOnTermType
) then
425 Buf
:= TNCH_IAC
+ TNCH_SB
+ TN_TERMTYPE
+ TN_TTYPE_IS
+ FTermType
+ TNCH_IAC
+ TNCH_SE
;
426 Socket
.Send(@Buf
[1], Length(Buf
));
430 { DebugString('Unknown suboption' + #13 + #10); }
435 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
436 procedure TTnCnx
.NegociateOption(chAction
: Char; chOption
: Char);
440 { DebugString('Negociation ' + IntToHex(ord(chAction), 2) + ' ' +
441 IntToHex(ord(ChOption), 2) + #13 + #10); }
446 if chAction
= TNCH_WILL
then begin
447 Answer(TNCH_DO
, chOption
);
448 RemoteBinMode
:= TRUE;
449 LocalBinMode
:= TRUE;
451 else if chAction
= TNCH_WONT
then begin
452 if RemoteBinMode
then begin
453 RemoteBinMode
:= FALSE;
454 LocalBinMode
:= FALSE;
460 if chAction
= TNCH_WILL
then begin
461 Answer(TNCH_DO
, chOption
);
464 else if chAction
= TNCH_WONT
then begin
467 if Assigned(FOnLocalEcho
) then
472 if chAction
= TNCH_WILL
then begin
473 Answer(TNCH_DO
, chOption
);
479 if chAction
= TNCH_DO
then begin
480 Answer(TNCH_WILL
, chOption
);
486 if chAction
= TNCH_DO
then begin
487 Answer(TNCH_WILL
, chOption
);
488 if Assigned(FOnSendLoc
) then
490 Buf
:= TNCH_IAC
+ TNCH_SB
+ TN_SEND_LOC
+ FLocation
+ TNCH_IAC
+ TNCH_SE
;
491 Socket
.Send(@Buf
[1], Length(Buf
));
496 if chAction
= TNCH_DO
then begin
497 Answer(TNCH_WILL
, chOption
);
502 { Answer(TNCH_WONT, chOption); }
503 { Jan Tomasek <xtomasej@feld.cvut.cz> }
504 if chAction
= TNCH_WILL
then
505 Answer(TNCH_DONT
, chOption
)
507 Answer(TNCH_WONT
, chOption
);
512 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
513 procedure TTnCnx
.FlushBuffer
;
519 if FBufferCnt
> 0 then begin
520 if Assigned(FOnDataAvailable
) then begin
521 { We need to make a copy for the data because we can reenter }
522 { during the event processing }
523 Count
:= FBufferCnt
; { How much we received }
525 GetMem(Buffer
, Count
+ 1); { Alloc memory for the copy }
529 if Buffer
<> nil then begin
531 Move(FBuffer
, Buffer
^, Count
); { Actual copy }
532 Buffer
[Count
] := #0; { Add a nul byte }
533 FBufferCnt
:= 0; { Reset receivecounter }
534 FOnDataAvailable(Self
, Buffer
, Count
); { Call event handler }
536 FreeMem(Buffer
, Count
+ 1); { Release the buffer }
550 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
551 procedure TTnCnx
.AddChar(Ch
: Char);
553 FBuffer
[FBufferCnt
] := Ch
;
555 if FBufferCnt
>= SizeOf(FBuffer
) then
560 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
561 procedure TTnCnx
.ReceiveChar(Ch
: Char);
563 bIAC
: Boolean = FALSE;
565 strSubOption
: String = '';
566 bSubNegoc
: Boolean = FALSE;
568 if chVerb
<> #0 then begin
569 NegociateOption(chVerb
, Ch
);
575 if bSubNegoc
then begin
576 if Ch
= TNCH_SE
then begin
578 NegociateSubOption(strSubOption
);
582 strSubOption
:= strSubOption
+ Ch
;
592 TNCH_DO
, TNCH_WILL
, TNCH_DONT
, TNCH_WONT
:
599 DebugString('TNCH_EOR' + #13 + #10);
601 if Assigned(FOnEOR
) then
606 { DebugString('Subnegociation' + #13 + #10); }
611 DebugString('Unknown ' + IntToHex(ord(Ch
), 2) + ' ''' + Ch
+ '''' + #13 + #10);
621 DebugString('TNCH_EL' + #13 + #10);
626 DebugString('TNCH_EC' + #13 + #10);
631 DebugString('TNCH_AYT' + #13 + #10);
636 DebugString('TNCH_IP' + #13 + #10);
641 DebugString('TNCH_AO' + #13 + #10);
654 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}