IdTelnet.pas 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741
  1. {
  2. $Project$
  3. $Workfile$
  4. $Revision$
  5. $DateUTC$
  6. $Id$
  7. This file is part of the Indy (Internet Direct) project, and is offered
  8. under the dual-licensing agreement described on the Indy website.
  9. (http://www.indyproject.org/)
  10. Copyright:
  11. (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  12. }
  13. {
  14. $Log$
  15. }
  16. {
  17. Rev 1.15 2/13/05 7:21:30 PM RLebeau
  18. Updated TIdTelnetReadThread.Run() to use a timeout when calling the
  19. IOHandler's CheckForDataOnSource() method.
  20. Rev 1.14 10/07/2004 10:00:28 ANeillans
  21. Fixed compile bug
  22. Rev 1.13 7/8/04 4:12:06 PM RLebeau
  23. Updated calls to Write() to use the IOHandler
  24. Rev 1.12 7/4/04 1:38:36 PM RLebeau
  25. Updated Negotiate() to trigger the OnDataAvailable event only when data is
  26. actually available.
  27. Rev 1.11 5/16/04 3:14:06 PM RLebeau
  28. Added destructor to terminate the reading thread
  29. Rev 1.10 3/29/04 11:47:00 AM RLebeau
  30. Updated to support new ThreadedEvent property
  31. Rev 1.9 2004.03.06 1:31:56 PM czhower
  32. To match Disconnect changes to core.
  33. Rev 1.8 2004.02.03 5:44:32 PM czhower
  34. Name changes
  35. Rev 1.7 1/21/2004 4:20:48 PM JPMugaas
  36. InitComponent
  37. Rev 1.6 2003.11.29 10:20:16 AM czhower
  38. Updated for core change to InputBuffer.
  39. Rev 1.5 3/6/2003 5:08:50 PM SGrobety
  40. Updated the read buffer methodes to fit the new core (InputBuffer ->
  41. InputBufferAsString + call to CheckForDataOnSource)
  42. Rev 1.4 2/24/2003 10:32:46 PM JPMugaas
  43. Rev 1.3 12/8/2002 07:26:10 PM JPMugaas
  44. Added published host and port properties.
  45. Rev 1.2 12/7/2002 06:43:30 PM JPMugaas
  46. These should now compile except for Socks server. IPVersion has to be a
  47. property someplace for that.
  48. Rev 1.1 12/6/2002 05:30:40 PM JPMugaas
  49. Now decend from TIdTCPClientCustom instead of TIdTCPClient.
  50. Rev 1.0 11/13/2002 08:02:50 AM JPMugaas
  51. 03-01-2002 Andrew P.Rybin Renamings and standardization
  52. 26-05-2000 SG: Converted to Indy, no other change
  53. 07-Mar-2000 Mark Added a bunch of stuff... it's very much a work in progress
  54. 05-Mar-2000 Mark Added constants for telnet implememtation.
  55. 13-JAN-2000 MTL: Moved to new Palette Scheme (Winshoes Servers)
  56. }
  57. unit IdTelnet;
  58. {
  59. Author: Mark Holmes
  60. This is the telnet client component. I'm still testing
  61. There is no real terminal emulation other than dumb terminal
  62. }
  63. interface
  64. {$i IdCompilerDefines.inc}
  65. uses
  66. Classes,
  67. IdAssignedNumbers,
  68. IdGlobal,
  69. IdException,
  70. IdStack,
  71. IdTCPClient, IdThread;
  72. const
  73. { These are the telnet command constansts from RFC 854 }
  74. TNC_EOR = $EF; // End of Record RFC 885
  75. TNC_SE = $F0; // End of subnegotiation parameters.
  76. TNC_NOP = $F1; // No operation.
  77. TNC_DATA_MARK = $F2; // The data stream portion of a Synch.
  78. // This should always be accompanied
  79. // by a TCP Urgent notification.
  80. TNC_BREAK = $F3; // NVT character BRK.
  81. TNC_IP = $F4; // The function IP.
  82. TNC_AO = $F5; // The function ABORT OUTPUT.
  83. TNC_AYT = $F6; // The function ARE YOU THERE.
  84. TNC_EC = $F7; // The function ERASE CHARACTER.
  85. TNC_EL = $F8; // The function ERASE LINE.
  86. TNC_GA = $F9; // The GO AHEAD signal.
  87. TNC_SB = $FA; // Indicates that what follows is
  88. // subnegotiation of the indicated
  89. // option.
  90. TNC_WILL = $FB; // Indicates the desire to begin
  91. // performing, or confirmation that
  92. // you are now performing, the
  93. // indicated option.
  94. TNC_WONT = $FC; // Indicates the refusal to perform,
  95. // or continue performing, the
  96. // indicated option.
  97. TNC_DO = $FD; // Indicates the request that the
  98. // other party perform, or
  99. // confirmation that you are expecting
  100. // the other party to perform, the
  101. // indicated option.
  102. TNC_DONT = $FE; // Indicates the demand that the
  103. // other party stop performing,
  104. // or confirmation that you are no
  105. // longer expecting the other party
  106. // to perform, the indicated option.
  107. TNC_IAC = $FF; // Data Byte 255.
  108. { Telnet options registered with IANA }
  109. TNO_BINARY = $00; // Binary Transmission
  110. TNO_ECHO = $01; // Echo
  111. TNO_RECONNECT = $02; // Reconnection
  112. TNO_SGA = $03; // Suppress Go Ahead
  113. TNO_AMSN = $04; // Approx Message Size Negotiation
  114. TNO_STATUS = $05; // Status
  115. TNO_TIMING_MARK = $06; // Timing Mark
  116. TNO_RCTE = $07; // Remote Controlled Trans and Echo -BELL
  117. TNO_OLW = $08; // Output Line Width
  118. TNO_OPS = $09; // Output Page Size
  119. TNO_OCRD = $0A; // Output Carriage-Return Disposition
  120. TNO_OHTS = $0B; // Output Horizontal Tab Stops
  121. TNO_OHTD = $0C; // Output Horizontal Tab Disposition
  122. TNO_OFD = $0D; // Output Formfeed Disposition
  123. TNO_OVT = $0E; // Output Vertical Tabstops
  124. TNO_OVTD = $0F; // Output Vertical Tab Disposition
  125. TNO_OLD = $10; // Output Linefeed Disposition
  126. TNO_EA = $11; // Extended ASCII
  127. TNO_LOGOUT = $12; // Logout
  128. TNO_BYTE_MACRO = $13; // Byte Macro
  129. TNO_DET = $14; // Data Entry Terminal
  130. TNO_SUPDUP = $15; // SUPDUP
  131. TNO_SUPDUP_OUTPUT = $16; // SUPDUP Output
  132. TNO_SL = $17; // Send Location
  133. TNO_TERMTYPE = $18; // Terminal Type
  134. TNO_EOR = $19; // End of Record
  135. TNO_TACACS_ID = $1A; // TACACS User Identification
  136. TNO_OM = $1B; // Output Marking
  137. TNO_TLN = $1C; // Terminal Location Number
  138. TNO_3270REGIME = $1D; // 3270 regime
  139. TNO_X3PAD = $1E; // X.3 PAD
  140. TNO_NAWS = $1F; // Window size
  141. TNO_TERM_SPEED = $20; // Terminal speed
  142. TNO_RFLOW = $21; // Remote flow control
  143. TNO_LINEMODE = $22; // Linemode option
  144. TNO_XDISPLOC = $23; // X Display Location
  145. TNO_ENV = $24; // Environment
  146. TNO_AUTH = $25; // Authenticate
  147. TNO_ENCRYPT = $26; // Encryption option
  148. TNO_NEWENV = $27;
  149. TNO_TN3270E = $28;
  150. TNO_XAUTH = $29;
  151. TNO_CHARSET = $2A;
  152. TNO_RSP = $2B;
  153. TNO_COMPORT = $2C;
  154. TNO_SUPLOCALECHO = $2D;
  155. TNO_STARTTLS = $2E;
  156. TNO_KERMIT = $2F;
  157. TNO_SEND_URL = $30;
  158. TNO_FORWARD_X = $31;
  159. // 50-137 = Unassigned
  160. TNO_PRAGMA_LOGON = $8A;
  161. TNO_SSPI_LOGON = $8B;
  162. TNO_PRAGMA_HEARTBEAT = $8C;
  163. TNO_EOL = $FF; // Extended-Options-List
  164. // Sub options
  165. TNOS_TERM_IS = $00;
  166. TNOS_TERMTYPE_SEND = $01; // Sub option
  167. TNOS_REPLY = $02;
  168. TNOS_NAME = $03;
  169. //Auth commands
  170. TNOAUTH_IS = $00;
  171. TNOAUTH_SEND = $01;
  172. TNOAUTH_REPLY = $02;
  173. TNOAUTH_NAME = $03;
  174. // Auth options $25
  175. TNOAUTH_NULL = $00;
  176. TNOAUTH_KERBEROS_V4 = $01;
  177. TNOAUTH_KERBEROS_V5 = $02;
  178. TNOAUTH_SPX = $03;
  179. TNOAUTH_MINK = $04;
  180. TNOAUTH_SRP = $05;
  181. TNOAUTH_RSA = $06;
  182. TNOAUTH_SSL = $07;
  183. TNOAUTH_LOKI = $0A;
  184. TNOAUTH_SSA = $0B;
  185. TNOAUTH_KEA_SJ = $0C;
  186. TNOAUTH_KEA_SJ_INTEG = $0D;
  187. TNOAUTH_DSS = $0E;
  188. TNOAUTH_NTLM = $0F;
  189. //Kerberos4 Telnet Authentication suboption commands
  190. TNOAUTH_KRB4_AUTH = $00;
  191. TNOAUTH_KRB4_REJECT = $01;
  192. TNOAUTH_KRB4_ACCEPT = $02;
  193. TNOAUTH_KRB4_CHALLENGE = $03;
  194. TNOAUTH_KRB4_RESPONSE = $04;
  195. TNOAUTH_KRB4_FORWARD = $05;
  196. TNOAUTH_KRB4_FORWARD_ACCEPT = $06;
  197. TNOAUTH_KRB4_FORWARD_REJECT = $07;
  198. TNOAUTH_KRB4_EXP = $08;
  199. TNOAUTH_KRB4_PARAMS = $09;
  200. //Kerberos5 Telnet Authentication suboption commands
  201. TNOAUTH_KRB5_AUTH = $00;
  202. TNOAUTH_KRB5_REJECT = $01;
  203. TNOAUTH_KRB5_ACCEPT = $02;
  204. TNOAUTH_KRB5_RESPONSE = $03;
  205. TNOAUTH_KRB5_FORWARD = $04;
  206. TNOAUTH_KRB5_FORWARD_ACCEPT = $05;
  207. TNOAUTH_KRB5_FORWARD_REJECT = $06;
  208. //DSS Telnet Authentication suboption commands
  209. TNOAUTH_DSS_INITIALIZE = $01;
  210. TNOAUTH_DSS_TOKENBA = $02;
  211. TNOAUTH_DSS_CERTA_TOKENAB = $03;
  212. TNOAUTH_DSS_CERTB_TOKENBA2 = $04;
  213. //SRP Telnet Authentication suboption commands
  214. TNOAUTH_SRP_AUTH = $00;
  215. TNOAUTH_SRP_REJECT = $01;
  216. TNOAUTH_SRP_ACCEPT = $02;
  217. TNOAUTH_SRP_CHALLENGE = $03;
  218. TNOAUTH_SRP_RESPONSE = $04;
  219. TNOAUTH_SRP_EXP = $08;
  220. TNOAUTH_SRP_PARAMS = $09;
  221. // KEA_SJ and KEA_SJ_INTEG Telnet Authenticatio suboption commands
  222. TNOAUTH_KEA_CERTA_RA = $01;
  223. TNOAUTH_KEA_CERTB_RB_IVB_NONCEB = $02;
  224. TNOAUTH_KEA_IVA_RESPONSEB_NONCEA = $03;
  225. TNOAUTH_KEA_RESPONSEA = $04;
  226. //Telnet Encryption Types (Option 38)
  227. // commands
  228. TNOENC_IS = $00;
  229. TNOENC_SUPPORT = $01;
  230. TNOENC_REPLY = $02;
  231. TNOENC_START = $03;
  232. TNOENC_END = $04;
  233. TNOENC_REQUEST_START = $05;
  234. TNOENC_REQUEST_END = $06;
  235. TNOENC_ENC_KEYID = $07;
  236. TNOENC_DEC_KEYID = $08;
  237. // types
  238. TNOENC_NULL = $00;
  239. TNOENC_DES_CFB64 = $01;
  240. TNOENC_DES_OFB64 = $02;
  241. TNOENC_DES3_CFB64 = $03;
  242. TNOENC_DES3_OFB64 = $04;
  243. TNOENC_CAST5_40_CFB64 = $08;
  244. TNOENC_CAST5_40_OFB64 = $09;
  245. TNOENC_CAST128_CFB64 = $0A;
  246. TNOENC_CAST128_OFB64 = $0B;
  247. TNOENC_AES_CCM = $0C;
  248. //DES3_CFB64 Telnet Encryption type suboption commands
  249. TNOENC_CFB64_IV = $01;
  250. TNOENC_CFB64_IV_OK = $02;
  251. TNOENC_CFB64_IV_BAD = $03;
  252. //CAST5_40_OFB64 and CAST128_OFB64 Telnet Encryption types suboption commands
  253. TNOENC_OFB64_IV = $01;
  254. TNOENC_OFB64_IV_OK = $02;
  255. TNOENC_OFB64_IV_BAD = $03;
  256. //CAST5_40_CFB64 and CAST128_CFB64 Telnet Encryption types suboption commands
  257. //same as DES3_CFB64 Telnet Encryption type suboption commands
  258. //DES_CFB64 Telnet Encryption type
  259. //same as DES3_CFB64 Telnet Encryption type suboption commands
  260. //DES_OFB64 Telnet Encryption type
  261. //same as CAST5_40_OFB64 and CAST128_OFB64 Telnet Encryption types suboption commands
  262. type
  263. TIdTelnet = class;
  264. {Commands to telnet client from server}
  265. TIdTelnetCommand = (tncNoLocalEcho, tncLocalEcho, tncEcho);
  266. TIdTelnetDataAvailEvent = procedure (Sender: TIdTelnet; const Buffer: TIdBytes) of object;
  267. TIdTelnetCommandEvent = procedure(Sender: TIdTelnet; Status: TIdTelnetCommand) of object;
  268. {This object is for the thread that listens for the telnet server responses
  269. to key input and initial protocol negotiations }
  270. TIdTelnetReadThread = class(TIdThread)
  271. protected
  272. FClient: TIdTelnet;
  273. //
  274. procedure Run; override;
  275. public
  276. constructor Create(AClient: TIdTelnet); reintroduce;
  277. property Client: TIdTelnet read FClient;
  278. end; //TIdTelnetReadThread
  279. TIdTelnet = class(TIdTCPClientCustom)
  280. protected
  281. fTerminal : String;
  282. fThreadedEvent: Boolean;
  283. FOnDataAvailable: TIdTelnetDataAvailEvent;
  284. fIamTelnet: Boolean;
  285. FOnTelnetCommand: TIdTelnetCommandEvent;
  286. FTelnetThread: TIdTelnetReadThread;
  287. //
  288. procedure DoOnDataAvailable(const Buf: TIdBytes);
  289. // Are we connected to a telnet server or some other server?
  290. property IamTelnet: Boolean read fIamTelnet write fIamTelnet;
  291. // Protocol negotiation begins here
  292. procedure Negotiate;
  293. // Handle the termtype request
  294. procedure Handle_SB(const SbType: Byte; const SbData: TIdBytes);
  295. // Send the protocol resp to the server based on what's in Reply {Do not Localize}
  296. procedure SendNegotiationResp(const Response: Byte; const ResponseData: Byte);
  297. procedure SendSubNegotiationResp(const SbType: Byte; const ResponseData: TIdBytes);
  298. // Update the telnet status
  299. procedure DoTelnetCommand(Status: TIdTelnetCommand);
  300. procedure InitComponent; override;
  301. public
  302. //
  303. {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS}
  304. constructor Create(AOwner: TComponent); reintroduce; overload;
  305. {$ENDIF}
  306. destructor Destroy; override;
  307. procedure Connect; override;
  308. procedure Disconnect(ANotifyPeer: Boolean); override;
  309. procedure SendCh(Ch: Char);
  310. procedure SendString(const S: String);
  311. property TelnetThread: TIdTelnetReadThread read FTelnetThread;
  312. published
  313. property Host;
  314. property Port default IdPORT_TELNET;
  315. property OnTelnetCommand: TIdTelnetCommandEvent read FOnTelnetCommand write FOnTelnetCommand;
  316. property OnDataAvailable: TIdTelnetDataAvailEvent read FOnDataAvailable write FOnDataAvailable;
  317. property Terminal: string read fTerminal write fTerminal;
  318. property ThreadedEvent: Boolean read fThreadedEvent write fThreadedEvent default False;
  319. end;
  320. EIdTelnetError = class(EIdException);
  321. EIdTelnetClientConnectError = class(EIdTelnetError);
  322. EIdTelnetServerOnDataAvailableIsNil = class(EIdTelnetError);
  323. implementation
  324. uses
  325. IdResourceStringsCore,
  326. IdResourceStringsProtocols,
  327. SysUtils;
  328. constructor TIdTelnetReadThread.Create(AClient: TIdTelnet);
  329. begin
  330. FClient := AClient;
  331. inherited Create(False);
  332. end;
  333. procedure TIdTelnetReadThread.Run;
  334. begin
  335. // if we have data run it through the negotiation routine. If we aren't
  336. // connected to a telnet server then the data just passes through the
  337. // negotiate routine unchanged.
  338. // RLebeau 3/29/04 - made Negotiate() get called by Synchronize() to
  339. // ensure that the OnTelnetCommand event handler is synchronized when
  340. // ThreadedEvent is false
  341. if FClient.IOHandler.InputBufferIsEmpty then begin
  342. FClient.IOHandler.CheckForDataOnSource(IdTimeoutInfinite);
  343. end;
  344. if not FClient.IOHandler.InputBufferIsEmpty then begin
  345. if FClient.ThreadedEvent then begin
  346. FClient.Negotiate;
  347. end else begin
  348. Synchronize(FClient.Negotiate);
  349. end;
  350. end;
  351. FClient.IOHandler.CheckForDisconnect;
  352. end;
  353. { TIdTelnet }
  354. procedure TIdTelnet.SendCh(Ch : Char);
  355. begin
  356. // this code is necessary to allow the client to receive data properly
  357. // from a non-telnet server
  358. if Connected then begin
  359. if (Ch <> CR) or IamTelnet then begin
  360. IOHandler.Write(Ch);
  361. end else begin
  362. IOHandler.Write(EOL);
  363. end;
  364. end;
  365. end;
  366. procedure TIdTelnet.SendString(const S : String);
  367. var
  368. I: Integer;
  369. Ch: Char;
  370. begin
  371. // this code is necessary to allow the client to receive data properly
  372. // from a non-telnet server
  373. for I := 1 to Length(S) do begin
  374. try
  375. Ch := S[I];
  376. if (Ch <> CR) or IamTelnet then begin
  377. IOHandler.Write(Ch);
  378. end else begin
  379. IOHandler.Write(EOL);
  380. end;
  381. except
  382. end;
  383. end;
  384. end;
  385. {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS}
  386. constructor TIdTelnet.Create(AOwner: TComponent);
  387. begin
  388. inherited Create(AOwner);
  389. end;
  390. {$ENDIF}
  391. procedure TIdTelnet.InitComponent;
  392. begin
  393. inherited InitComponent;
  394. Terminal := 'dumb'; {Do not Localize}
  395. ThreadedEvent := False;
  396. IamTelnet := False;
  397. Port := IdPORT_TELNET;
  398. end;
  399. destructor TIdTelnet.Destroy;
  400. begin
  401. Disconnect;
  402. inherited Destroy;
  403. end;
  404. procedure TIdTelnet.Disconnect(ANotifyPeer: Boolean);
  405. begin
  406. if Assigned(FTelnetThread) then begin
  407. FTelnetThread.Terminate;
  408. end;
  409. try
  410. inherited Disconnect(ANotifyPeer);
  411. finally
  412. if Assigned(FTelnetThread) and (not IsCurrentThread(FTelnetThread)) then begin
  413. FTelnetThread.WaitFor;
  414. FreeAndNil(FTelnetThread);
  415. end;
  416. end;
  417. end;
  418. procedure TIdTelnet.DoOnDataAvailable(const Buf: TIdBytes);
  419. begin
  420. if Assigned(FOnDataAvailable) then begin
  421. OnDataAvailable(Self, Buf);
  422. end else begin
  423. raise EIdTelnetServerOnDataAvailableIsNil.Create(RSTELNETSRVOnDataAvailableIsNil);
  424. end;
  425. end;
  426. procedure TIdTelnet.Connect;
  427. begin
  428. inherited Connect;
  429. try
  430. // create the reading thread and assign the current Telnet object to it
  431. IAmTelnet := False;
  432. FTelnetThread := TIdTelnetReadThread.Create(Self);
  433. except
  434. Disconnect(True);
  435. IndyRaiseOuterException(EIdTelnetClientConnectError.Create(RSNoCreateListeningThread)); // translate
  436. end;
  437. end;
  438. procedure TIdTelnet.SendNegotiationResp(const Response: Byte; const ResponseData: Byte);
  439. var
  440. Resp: TIdBytes;
  441. begin
  442. SetLength(Resp, 3);
  443. Resp[0] := TNC_IAC;
  444. Resp[1] := Response;
  445. Resp[2] := ResponseData;
  446. IOHandler.Write(Resp);
  447. end;
  448. procedure TIdTelnet.SendSubNegotiationResp(const SbType: Byte; const ResponseData: TIdBytes);
  449. var
  450. Resp: TIdBytes;
  451. begin
  452. SetLength(Resp, 3 + Length(ResponseData) + 2);
  453. Resp[0] := TNC_IAC;
  454. Resp[1] := TNC_SB;
  455. Resp[2] := SbType;
  456. CopyTIdBytes(ResponseData, 0, Resp, 3, Length(ResponseData));
  457. Resp[Length(Resp)-2] := TNC_IAC;
  458. Resp[Length(Resp)-1] := TNC_SE;
  459. IOHandler.Write(Resp);
  460. end;
  461. procedure TIdTelnet.Handle_SB(const SbType: Byte; const SbData: TIdBytes);
  462. var
  463. Resp: TIdBytes;
  464. LTerminal: String;
  465. begin
  466. Resp := nil;
  467. case SbType of
  468. TNO_TERMTYPE:
  469. if (Length(SbData) > 0) and (SbData[0] = TNOS_TERMTYPE_SEND) then
  470. begin
  471. // if someone inadvertantly sets Terminal to null
  472. // You can set terminal to anything you want I suppose but be
  473. // prepared to handle the data emulation yourself
  474. LTerminal := Terminal;
  475. if LTerminal = '' then begin
  476. Terminal := 'UNKNOWN'; {Do not Localize}
  477. end;
  478. SetLength(Resp, 1);
  479. Resp[0] := TNOS_TERM_IS;
  480. AppendString(Resp, LTerminal);
  481. SendSubNegotiationResp(TNO_TERMTYPE, Resp);
  482. end;
  483. end;
  484. // add authentication code here
  485. end;
  486. procedure TIdTelnet.Negotiate;
  487. var
  488. b : Byte;
  489. nBuf : TIdBytes;
  490. sbBuf : TIdBytes;
  491. CurrentSb : Byte;
  492. Reply : Byte;
  493. begin
  494. nBuf := nil;
  495. sbBuf := nil;
  496. repeat
  497. b := IOHandler.ReadByte;
  498. if b <> TNC_IAC then
  499. begin
  500. AppendByte(nBuf, b);
  501. Continue;
  502. end;
  503. { start of command sequence }
  504. IamTelnet := True;
  505. b := IOHandler.ReadByte;
  506. if b = TNC_IAC then
  507. begin
  508. AppendByte(nBuf, TNC_IAC);
  509. Continue;
  510. end;
  511. case b of
  512. TNC_WILL:
  513. begin
  514. b := IOHandler.ReadByte;
  515. case b of
  516. TNO_ECHO:
  517. begin
  518. Reply := TNC_DO;
  519. DoTelnetCommand(tncNoLocalEcho);
  520. //doStatus('NOLOCALECHO'); {Do not Localize}
  521. end;
  522. TNO_EOR:
  523. Reply := TNC_DO;
  524. else
  525. Reply := TNC_DONT;
  526. end;
  527. SendNegotiationResp(Reply, b);
  528. end;
  529. TNC_WONT:
  530. begin
  531. b := IOHandler.ReadByte;
  532. case b of
  533. TNO_ECHO:
  534. begin
  535. Reply := TNC_DONT;
  536. DoTelnetCommand(tncLocalEcho);
  537. //Dostatus('LOCALECHO'); {Do not Localize}
  538. end;
  539. else
  540. Reply := TNC_DONT;
  541. end;
  542. SendNegotiationResp(Reply, b);
  543. end;
  544. TNC_DONT:
  545. begin
  546. b := IOHandler.ReadByte;
  547. case b of
  548. TNO_ECHO:
  549. begin
  550. DoTelnetCommand(tncEcho);
  551. //DoStatus('ECHO'); {Do not Localize}
  552. Reply := TNC_WONT;
  553. end;
  554. else
  555. Reply := TNC_WONT;
  556. end;
  557. SendNegotiationResp(Reply, b);
  558. end;
  559. TNC_DO:
  560. begin
  561. b := IOHandler.ReadByte;
  562. case b of
  563. TNO_ECHO:
  564. begin
  565. Reply := TNC_WILL;
  566. DoTelnetCommand(tncLocalEcho);
  567. end;
  568. TNO_TERMTYPE:
  569. Reply := TNC_WILL;
  570. //TNO_NAWS:
  571. TNO_AUTH:
  572. begin
  573. {
  574. if (Authentication) then begin
  575. Reply := TNC_WILL;
  576. end else
  577. }
  578. begin
  579. Reply := TNC_WONT;
  580. end;
  581. end;
  582. else
  583. Reply := TNC_WONT;
  584. end;
  585. SendNegotiationResp(Reply, b);
  586. end;
  587. TNC_EOR:
  588. begin
  589. // send any current data to the app
  590. if Length(nBuf) > 0 then
  591. begin
  592. DoOnDataAvailable(nBuf);
  593. SetLength(nBuf, 0);
  594. end;
  595. end;
  596. TNC_SB:
  597. begin
  598. SetLength(sbBuf, 0);
  599. // send any current data to the app, as the sub-negotiation
  600. // may affect how subsequent data needs to be processed...
  601. if Length(nBuf) > 0 then
  602. begin
  603. DoOnDataAvailable(nBuf);
  604. SetLength(nBuf, 0);
  605. end;
  606. CurrentSB := IOHandler.ReadByte;
  607. repeat
  608. b := IOHandler.ReadByte;
  609. if b = TNC_IAC then
  610. begin
  611. b := IOHandler.ReadByte;
  612. case b of
  613. TNC_IAC:
  614. begin
  615. AppendByte(sbBuf, TNC_IAC);
  616. end;
  617. TNC_SE:
  618. begin
  619. Handle_Sb(CurrentSB, sbBuf);
  620. SetLength(sbBuf, 0);
  621. Break;
  622. end;
  623. TNC_SB:
  624. begin
  625. Handle_Sb(CurrentSB, sbBuf);
  626. SetLength(sbBuf, 0);
  627. CurrentSB := IOHandler.ReadByte;
  628. end;
  629. end;
  630. end else begin
  631. AppendByte(sbBuf, b);
  632. end;
  633. until False;
  634. end;
  635. end;
  636. until IOHandler.InputBufferIsEmpty;
  637. // if any data remains then send this data to the app
  638. if Length(nBuf) > 0 then begin
  639. DoOnDataAvailable(nBuf);
  640. end;
  641. end;
  642. procedure TIdTelnet.DoTelnetCommand(Status: TIdTelnetCommand);
  643. begin
  644. if Assigned(FOnTelnetCommand) then
  645. FOnTelnetCommand(Self, Status);
  646. end;
  647. END.