initial commit
[rofl0r-KOL.git] / units / ics / KOLTnCnx.pas
blob0c4b07ea87eadbc94c0d99de09a131be386b57ab
1 unit TnCnx;
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 }
9 {$ENDIF}
10 {$IFDEF VER110} { C++ Builder V3.0 }
11 {$ObjExportAll On}
12 {$ENDIF}
13 {$IFDEF VER125} { C++ Builder V4.0 }
14 {$ObjExportAll On}
15 {$ENDIF}
17 interface
19 uses KOL,
20 SysUtils, WinTypes, WinProcs, Messages { , Classes } , Controls, Forms,
21 WSocket, Winsock;
23 const
24 TnCnxVersion = 209;
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 }
46 { Telnet options }
47 TN_TRANSMIT_BINARY = #0; { $00 }
48 TN_ECHO = #1; { $01 }
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 }
55 TN_DET = #20; { $14 }
56 TN_SEND_LOC = #23; { $17 }
57 TN_TERMTYPE = #24; { $18 }
58 TN_EOR = #25; { $19 }
59 TN_NAWS = #31; { $1F }
60 TN_TERMSPEED = #32; { $20 }
61 TN_TFC = #33; { $21 }
62 TN_XDISPLOC = #35; { $23 }
63 TN_EXOPL = #255; { $FF }
65 TN_TTYPE_SEND = #1;
66 TN_TTYPE_IS = #0;
68 type
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;
77 TTnCnx= object(TObj)
78 public
79 Socket : TWSocket;
80 private
81 FPort : String;
82 FHost : String;
83 FLocation : String;
84 FTermType : String;
85 RemoteBinMode : Boolean;
86 LocalBinMode : Boolean;
87 FLocalEcho : Boolean;
88 Spga : Boolean;
89 FTType : Boolean;
90 FBuffer : array [0..2048] of char;
91 FBufferCnt : Integer;
92 FWindowHandle : HWND;
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;
113 public
114 { constructor Create(AOwner: TComponent); override;
115 } destructor Destroy;
116 virtual; function Send(Data : Pointer; Len : Integer) : integer;
117 function SendStr(Data : String) : integer;
118 procedure Connect;
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;
125 procedure Close;
126 procedure Pause;
127 procedure Resume;
128 property State : TSocketState read GetState;
129 property Handle : HWND read FWindowHandle;
130 { published }
131 property Port : String read FPort
132 write FPort;
133 property Host : String read FHost
134 write FHost;
135 property Location : String read FLocation
136 write FLocation;
137 property TermType : String read FTermType
138 write FTermType;
139 property LocalEcho : Boolean read FLocalEcho
140 write 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
148 write FOnDisplay;
149 property OnEndOfRecord : TNotifyEvent read FOnEOR
150 write FOnEOR;
151 property OnSendLoc : TNotifyEvent read FOnSendLoc
152 write FOnSendLoc;
153 property OnTermType : TNotifyEvent read FOnTermType
154 write FOnTermType;
155 property OnLocalEcho : TNotifyEvent read FOnLocalEcho
156 write FOnLocalEcho;
157 end;
158 PTnCnx=^TTnCnx;
159 function NewTnCnx(AOwner: TComponent):PTnCnx; type MyStupid3137=DWord;
161 procedure Register;
163 implementation
165 {$DEFINE Debug} { Add or remove minus sign before dollar sign to }
166 { generate code for debug message output }
168 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
169 procedure Register;
170 begin
171 RegisterComponents('FPiette', [TTnCnx]);
172 end;
175 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
176 procedure DebugString(Msg : String);
177 const
178 Cnt : Integer = 0;
180 Buf : String[20];
181 begin
182 {$IFDEF Debug}
183 Cnt := Cnt + 1;
184 Buf := IntToHex(Cnt, 4) + ' ' + #0;
185 OutputDebugString(@Buf[1]);
187 {$IFNDEF WIN32}
188 if Length(Msg) < High(Msg) then
189 Msg[Length(Msg) + 1] := #0;
190 {$ENDIF}
192 OutputDebugString(@Msg[1]);
193 {$ENDIF}
194 end;
197 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
198 procedure TTnCnx.WndProc(var MsgRec: TMessage);
199 begin
200 with MsgRec do
201 Result := DefWindowProc(Handle, Msg, wParam, lParam);
202 end;
205 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
206 constructor TTnCnx.Create(AOwner: TComponent);
207 begin
208 inherited Create(AOwner);
209 FWindowHandle := WSocket.AllocateHWnd(WndProc);
210 FLocation := 'TNCNX';
211 FTermType := 'VT100';
212 FPort := '23';
213 Socket := TWSocket.Create(Self);
214 Socket.OnSessionConnected := SocketSessionConnected;
215 Socket.OnDataAvailable := SocketDataAvailable;
216 Socket.OnSessionClosed := SocketSessionClosed;
217 end;
220 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
221 destructor TTnCnx.Destroy;
222 begin
223 if Assigned(Socket) then begin
224 Socket.Free;
225 Socket := nil;
226 end;
227 WSocket.DeallocateHWnd(FWindowHandle);
228 inherited Destroy;
229 end;
232 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
233 procedure TTnCnx.Notification(AComponent: TComponent; Operation: TOperation);
234 begin
235 inherited Notification(AComponent, Operation);
236 if (AComponent = Socket) and (Operation = opRemove) then
237 Socket := nil;
238 end;
241 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
242 procedure TTnCnx.Pause;
243 begin
244 if not Assigned(Socket) then
245 Exit;
246 Socket.Pause;
247 end;
250 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
251 procedure TTnCnx.Resume;
252 begin
253 if not Assigned(Socket) then
254 Exit;
255 Socket.Resume;
256 end;
259 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
260 procedure TTnCnx.Connect;
261 begin
262 if not Assigned(Socket) then
263 Exit;
265 if Socket.State <> wsClosed then
266 Socket.Close;
268 Socket.Proto := 'tcp';
269 Socket.Port := FPort;
270 Socket.Addr := FHost;
271 Socket.Connect;
272 end;
275 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
276 function TTnCnx.IsConnected : Boolean;
277 begin
278 Result := Socket.State = wsConnected;
279 end;
282 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
283 procedure TTnCnx.Close;
284 begin
285 if Assigned(Socket) then
286 Socket.Close;
287 end;
290 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
291 procedure TTnCnx.Display(Str : String);
292 begin
293 if Assigned(FOnDisplay) then
294 FOnDisplay(Self, Str);
295 end;
298 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
299 function TTnCnx.GetState : TSocketState;
300 begin
301 if Assigned(Socket) then
302 Result := Socket.State
303 else
304 Result := wsInvalidState;
305 end;
308 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
309 procedure TTnCnx.SocketSessionConnected(Sender: TObject; Error : word);
310 begin
311 if Assigned(FOnSessionConnected) then
312 FOnSessionConnected(Self, Error);
313 end;
316 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
317 procedure TTnCnx.SocketSessionClosed(Sender: TObject; Error : word);
318 begin
319 if Socket.State <> wsClosed then
320 Socket.Close;
321 if Assigned(FOnSessionClosed) then
322 FOnSessionClosed(Self, Error);
323 end;
326 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
327 procedure TTnCnx.SocketDataAvailable(Sender: TObject; Error : word);
329 Len, I : Integer;
330 Buffer : array [1..2048] of char;
331 Socket : TWSocket;
332 begin
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);
345 else begin
346 for I := 1 to Len do
347 ReceiveChar(Buffer[I]);
348 FlushBuffer;
349 end;
350 end;
353 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
354 function TTnCnx.Send(Data : Pointer; Len : Integer) : integer;
355 begin
356 if Assigned(Socket) then
357 Result := Socket.Send(Data, Len)
358 else
359 Result := -1;
360 end;
363 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
364 function TTnCnx.SendStr(Data : String) : integer;
365 begin
366 Result := Send(@Data[1], Length(Data));
367 end;
370 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
371 procedure TTnCnx.Answer(chAns : Char; chOption : Char);
373 Buf : String[3];
374 begin
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));
378 end;
381 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
382 procedure TTnCnx.WillOption(chOption : Char);
383 begin
384 Answer(TNCH_WILL, chOption);
385 end;
388 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
389 procedure TTnCnx.WontOption(chOption : Char);
390 begin
391 Answer(TNCH_WONT, chOption);
392 end;
395 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
396 procedure TTnCnx.DontOption(chOption : Char);
397 begin
398 Answer(TNCH_DONT, chOption);
399 end;
402 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
403 procedure TTnCnx.DoOption(chOption : Char);
404 begin
405 Answer(TNCH_DO, chOption);
406 end;
409 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
410 procedure TTnCnx.NegociateSubOption(strSubOption : String);
412 Buf : String;
413 begin
414 { DebugString('SubNegociation ' +
415 IntToHex(ord(strSubOption[1]), 2) + ' ' +
416 IntToHex(ord(strSubOption[2]), 2) + #13 + #10); }
418 case strSubOption[1] of
419 TN_TERMTYPE:
420 begin
421 if strSubOption[2] = TN_TTYPE_SEND then begin
422 { DebugString('Send TermType' + #13 + #10); }
423 if Assigned(FOnTermType) then
424 FOnTermType(Self);
425 Buf := TNCH_IAC + TNCH_SB + TN_TERMTYPE + TN_TTYPE_IS + FTermType + TNCH_IAC + TNCH_SE;
426 Socket.Send(@Buf[1], Length(Buf));
427 end;
428 end;
429 else
430 { DebugString('Unknown suboption' + #13 + #10); }
431 end;
432 end;
435 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
436 procedure TTnCnx.NegociateOption(chAction : Char; chOption : Char);
438 Buf : String;
439 begin
440 { DebugString('Negociation ' + IntToHex(ord(chAction), 2) + ' ' +
441 IntToHex(ord(ChOption), 2) + #13 + #10); }
443 case chOption of
444 TN_TRANSMIT_BINARY:
445 begin
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;
455 end;
456 end;
457 end;
458 TN_ECHO:
459 begin
460 if chAction = TNCH_WILL then begin
461 Answer(TNCH_DO, chOption);
462 FLocalEcho := FALSE;
464 else if chAction = TNCH_WONT then begin
465 FLocalEcho := TRUE;
466 end;
467 if Assigned(FOnLocalEcho) then
468 FOnLocalEcho(self);
469 end;
470 TN_SUPPRESS_GA:
471 begin
472 if chAction = TNCH_WILL then begin
473 Answer(TNCH_DO, chOption);
474 spga := TRUE;
475 end;
476 end;
477 TN_TERMTYPE:
478 begin
479 if chAction = TNCH_DO then begin
480 Answer(TNCH_WILL, chOption);
481 FTType := TRUE;
482 end;
483 end;
484 TN_SEND_LOC:
485 begin
486 if chAction = TNCH_DO then begin
487 Answer(TNCH_WILL, chOption);
488 if Assigned(FOnSendLoc) then
489 FOnSendLoc(Self);
490 Buf := TNCH_IAC + TNCH_SB + TN_SEND_LOC + FLocation + TNCH_IAC + TNCH_SE;
491 Socket.Send(@Buf[1], Length(Buf));
492 end;
493 end;
494 TN_EOR:
495 begin
496 if chAction = TNCH_DO then begin
497 Answer(TNCH_WILL, chOption);
498 FTType := TRUE;
499 end;
500 end;
501 else
502 { Answer(TNCH_WONT, chOption); }
503 { Jan Tomasek <xtomasej@feld.cvut.cz> }
504 if chAction = TNCH_WILL then
505 Answer(TNCH_DONT, chOption)
506 else
507 Answer(TNCH_WONT, chOption);
508 end;
509 end;
512 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
513 procedure TTnCnx.FlushBuffer;
515 Buffer : PChar;
516 Count : Integer;
517 begin
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 }
526 except
527 Buffer := nil;
528 end;
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 }
535 finally
536 FreeMem(Buffer, Count + 1); { Release the buffer }
537 end;
538 end;
540 else begin
541 FBufferCnt := 0
542 end;
543 end;
544 except
545 raise;
546 end;
547 end;
550 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
551 procedure TTnCnx.AddChar(Ch : Char);
552 begin
553 FBuffer[FBufferCnt] := Ch;
554 Inc(FBufferCnt);
555 if FBufferCnt >= SizeOf(FBuffer) then
556 FlushBuffer;
557 end;
560 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
561 procedure TTnCnx.ReceiveChar(Ch : Char);
562 const
563 bIAC : Boolean = FALSE;
564 chVerb : Char = #0;
565 strSubOption : String = '';
566 bSubNegoc : Boolean = FALSE;
567 begin
568 if chVerb <> #0 then begin
569 NegociateOption(chVerb, Ch);
570 chVerb := #0;
571 strSubOption := '';
572 Exit;
573 end;
575 if bSubNegoc then begin
576 if Ch = TNCH_SE then begin
577 bSubNegoc := FALSE;
578 NegociateSubOption(strSubOption);
579 strSubOption := '';
581 else
582 strSubOption := strSubOption + Ch;
583 Exit;
584 end;
586 if bIAC then begin
587 case Ch of
588 TNCH_IAC: begin
589 AddChar(Ch);
590 bIAC := FALSE;
591 end;
592 TNCH_DO, TNCH_WILL, TNCH_DONT, TNCH_WONT:
593 begin
594 bIAC := FALSE;
595 chVerb := Ch;
596 end;
597 TNCH_EOR:
598 begin
599 DebugString('TNCH_EOR' + #13 + #10);
600 bIAC := FALSE;
601 if Assigned(FOnEOR) then
602 FOnEOR(Self);
603 end;
604 TNCH_SB:
605 begin
606 { DebugString('Subnegociation' + #13 + #10); }
607 bSubNegoc := TRUE;
608 bIAC := FALSE;
609 end;
610 else
611 DebugString('Unknown ' + IntToHex(ord(Ch), 2) + ' ''' + Ch + '''' + #13 + #10);
612 bIAC := FALSE;
613 end;
615 Exit;
616 end;
618 case Ch of
619 TNCH_EL:
620 begin
621 DebugString('TNCH_EL' + #13 + #10);
622 AddChar(Ch);
623 end;
624 TNCH_EC:
625 begin
626 DebugString('TNCH_EC' + #13 + #10);
627 AddChar(Ch);
628 end;
629 TNCH_AYT:
630 begin
631 DebugString('TNCH_AYT' + #13 + #10);
632 AddChar(Ch);
633 end;
634 TNCH_IP:
635 begin
636 DebugString('TNCH_IP' + #13 + #10);
637 AddChar(Ch);
638 end;
639 TNCH_AO:
640 begin
641 DebugString('TNCH_AO' + #13 + #10);
642 AddChar(Ch);
643 end;
644 TNCH_IAC:
645 begin
646 bIAC := TRUE
647 end;
648 else
649 AddChar(Ch);
650 end;
651 end;
654 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
656 end.