ltelnet.pp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. { lTelnet CopyRight (C) 2004-2008 Ales Katona
  2. This library is Free software; you can rediStribute it and/or modify it
  3. under the terms of the GNU Library General Public License as published by
  4. the Free Software Foundation; either version 2 of the License, or (at your
  5. option) any later version.
  6. This program is diStributed in the hope that it will be useful, but WITHOUT
  7. ANY WARRANTY; withOut even the implied warranty of MERCHANTABILITY or
  8. FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
  9. for more details.
  10. You should have received a Copy of the GNU Library General Public License
  11. along with This library; if not, Write to the Free Software Foundation,
  12. Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  13. This license has been modified. See File LICENSE for more inFormation.
  14. Should you find these sources withOut a LICENSE File, please contact
  15. me at [email protected]
  16. }
  17. unit lTelnet;
  18. {$mode objfpc}{$H+}
  19. //{$define debug}
  20. interface
  21. uses
  22. Classes, lNet, lControlStack;
  23. const
  24. // Telnet printer signals
  25. TS_NUL = #0;
  26. TS_ECHO = #1;
  27. TS_SGA = #3; // Surpass go-ahead
  28. TS_BEL = #7;
  29. TS_BS = #8;
  30. TS_HT = #9;
  31. TS_LF = #10;
  32. TS_VT = #11;
  33. TS_FF = #12;
  34. TS_CR = #13;
  35. // Telnet control signals
  36. TS_NAWS = #31;
  37. TS_DATA_MARK = #128;
  38. TS_BREAK = #129;
  39. TS_HYI = #133; // Hide Your Input
  40. // Data types codes
  41. TS_STDTELNET = #160;
  42. TS_TRANSPARENT = #161;
  43. TS_EBCDIC = #162;
  44. // Control bytes
  45. TS_SE = #240;
  46. TS_NOP = #241;
  47. TS_GA = #249; // go ahead currently ignored(full duplex)
  48. TS_SB = #250;
  49. TS_WILL = #251;
  50. TS_WONT = #252;
  51. TS_DO = #253;
  52. TS_DONT = #254;
  53. // Mother of all power
  54. TS_IAC = #255;
  55. type
  56. TLTelnetClient = class;
  57. TLTelnetControlChars = set of Char;
  58. TLHowEnum = (TE_WILL = 251, TE_WONT, TE_DO, TE_DONW);
  59. TLSubcommandCallback= function(command: char; const parameters, defaultResponse: string): string;
  60. TLSubcommandEntry= record
  61. callback: TLSubcommandCallback;
  62. defaultResponse: string
  63. end;
  64. TLSubcommandArray= array[#$00..#$ff] of TLSubcommandEntry;
  65. { TLTelnet }
  66. TLTelnet = class(TLComponent, ILDirect)
  67. protected
  68. FStack: TLControlStack;
  69. FConnection: TLTcp;
  70. FPossible: TLTelnetControlChars;
  71. FActiveOpts: TLTelnetControlChars;
  72. FOutput: TMemoryStream;
  73. FOperation: Char;
  74. FCommandCharIndex: Byte;
  75. FOnReceive: TLSocketEvent;
  76. FOnConnect: TLSocketEvent;
  77. FOnDisconnect: TLSocketEvent;
  78. FOnError: TLSocketErrorEvent;
  79. FCommandArgs: string[3];
  80. FOrders: TLTelnetControlChars;
  81. FBuffer: array of Char;
  82. FBufferIndex: Integer;
  83. FBufferEnd: Integer;
  84. FSubcommandCallbacks: TLSubcommandArray;
  85. procedure InflateBuffer;
  86. function AddToBuffer(const aStr: string): Boolean; inline;
  87. function Question(const Command: Char; const Value: Boolean): Char;
  88. function GetConnected: Boolean;
  89. function GetTimeout: Integer;
  90. procedure SetTimeout(const Value: Integer);
  91. function GetSocketClass: TLSocketClass;
  92. procedure SetSocketClass(Value: TLSocketClass);
  93. function GetSession: TLSession;
  94. procedure SetSesssion(const AValue: TLSession);
  95. procedure SetCreator(AValue: TLComponent); override;
  96. procedure StackFull;
  97. procedure DoubleIAC(var s: string);
  98. function TelnetParse(const msg: string): Integer;
  99. procedure React(const Operation, Command: Char); virtual; abstract;
  100. procedure SendCommand(const Command: Char; const Value: Boolean); virtual; abstract;
  101. procedure OnCs(aSocket: TLSocket);
  102. public
  103. constructor Create(aOwner: TComponent); override;
  104. destructor Destroy; override;
  105. function Get(out aData; const aSize: Integer; aSocket: TLSocket = nil): Integer; virtual; abstract;
  106. function GetMessage(out msg: string; aSocket: TLSocket = nil): Integer; virtual; abstract;
  107. function Send(const aData; const aSize: Integer; aSocket: TLSocket = nil): Integer; virtual; abstract;
  108. function SendMessage(const msg: string; aSocket: TLSocket = nil): Integer; virtual; abstract;
  109. function OptionIsSet(const Option: Char): Boolean;
  110. function RegisterOption(const aOption: Char; const aCommand: Boolean): Boolean;
  111. procedure SetOption(const Option: Char);
  112. procedure UnSetOption(const Option: Char);
  113. function RegisterSubcommand(aOption: char; callback: TLSubcommandCallback; const defaultResponse: string= ''): boolean;
  114. procedure Disconnect(const Forced: Boolean = True); override;
  115. procedure SendCommand(const aCommand: Char; const How: TLHowEnum); virtual;
  116. public
  117. property Output: TMemoryStream read FOutput;
  118. property Connected: Boolean read GetConnected;
  119. property Timeout: Integer read GetTimeout write SetTimeout;
  120. property OnReceive: TLSocketEvent read FOnReceive write FOnReceive;
  121. property OnDisconnect: TLSocketEvent read FOnDisconnect write FOnDisconnect;
  122. property OnConnect: TLSocketEvent read FOnConnect write FOnConnect;
  123. property OnError: TLSocketErrorEvent read FOnError write FOnError;
  124. property Connection: TLTCP read FConnection;
  125. property SocketClass: TLSocketClass read GetSocketClass write SetSocketClass;
  126. property Session: TLSession read GetSession write SetSesssion;
  127. end;
  128. { TLTelnetClient }
  129. TLTelnetClient = class(TLTelnet, ILClient)
  130. protected
  131. FLocalEcho: Boolean;
  132. procedure OnEr(const msg: string; aSocket: TLSocket);
  133. procedure OnDs(aSocket: TLSocket);
  134. procedure OnRe(aSocket: TLSocket);
  135. procedure OnCo(aSocket: TLSocket);
  136. procedure React(const Operation, Command: Char); override;
  137. procedure SendCommand(const Command: Char; const Value: Boolean); override;
  138. public
  139. constructor Create(aOwner: TComponent); override;
  140. function Connect(const anAddress: string; const aPort: Word): Boolean;
  141. function Connect: Boolean;
  142. function Get(out aData; const aSize: Integer; aSocket: TLSocket = nil): Integer; override;
  143. function GetMessage(out msg: string; aSocket: TLSocket = nil): Integer; override;
  144. function Send(const aData; const aSize: Integer; aSocket: TLSocket = nil): Integer; override;
  145. function SendMessage(const msg: string; aSocket: TLSocket = nil): Integer; override;
  146. procedure CallAction; override;
  147. public
  148. property LocalEcho: Boolean read FLocalEcho write FLocalEcho;
  149. end;
  150. function LTelnetSubcommandCallback(command: char; const parameters, defaultResponse: string): string;
  151. implementation
  152. uses
  153. SysUtils, Math;
  154. var
  155. zz: Char;
  156. TNames: array[Char] of string;
  157. //*******************************TLTelnetClient********************************
  158. constructor TLTelnet.Create(aOwner: TComponent);
  159. begin
  160. inherited Create(aOwner);
  161. FConnection := TLTCP.Create(nil);
  162. FConnection.Creator := Self;
  163. FConnection.OnCanSend := @OnCs;
  164. FOutput := TMemoryStream.Create;
  165. FCommandCharIndex := 0;
  166. FStack := TLControlStack.Create;
  167. FStack.OnFull := @StackFull;
  168. end;
  169. destructor TLTelnet.Destroy;
  170. begin
  171. Disconnect(True);
  172. FOutput.Free;
  173. FConnection.Free;
  174. FStack.Free;
  175. inherited Destroy;
  176. end;
  177. function TLTelnet.GetConnected: Boolean;
  178. begin
  179. Result := FConnection.Connected;
  180. end;
  181. function TLTelnet.GetSession: TLSession;
  182. begin
  183. Result := FConnection.Session;
  184. end;
  185. procedure TLTelnet.SetSesssion(const AValue: TLSession);
  186. begin
  187. FConnection.Session := aValue;
  188. end;
  189. procedure TLTelnet.SetCreator(AValue: TLComponent);
  190. begin
  191. inherited SetCreator(AValue);
  192. FConnection.Creator := aValue;
  193. end;
  194. procedure TLTelnet.InflateBuffer;
  195. var
  196. n: Integer;
  197. begin
  198. n := Max(Length(FBuffer), 25);
  199. SetLength(FBuffer, n * 10);
  200. end;
  201. function TLTelnet.AddToBuffer(const aStr: string): Boolean; inline;
  202. begin
  203. Result := False;
  204. while Length(aStr) + FBufferEnd > Length(FBuffer) do
  205. InflateBuffer;
  206. Move(aStr[1], FBuffer[FBufferEnd], Length(aStr));
  207. Inc(FBufferEnd, Length(aStr));
  208. end;
  209. function TLTelnet.Question(const Command: Char; const Value: Boolean): Char;
  210. begin
  211. Result := TS_NOP;
  212. if Value then begin
  213. if Command in FOrders then
  214. Result := TS_DO
  215. else
  216. Result := TS_WILL;
  217. end else begin
  218. if Command in FOrders then
  219. Result := TS_DONT
  220. else
  221. Result := TS_WONT;
  222. end;
  223. end;
  224. function TLTelnet.GetSocketClass: TLSocketClass;
  225. begin
  226. Result := FConnection.SocketClass;
  227. end;
  228. function TLTelnet.GetTimeout: Integer;
  229. begin
  230. Result := FConnection.Timeout;
  231. end;
  232. procedure TLTelnet.SetSocketClass(Value: TLSocketClass);
  233. begin
  234. FConnection.SocketClass := Value;
  235. end;
  236. procedure TLTelnet.SetTimeout(const Value: Integer);
  237. begin
  238. FConnection.Timeout := Value;
  239. end;
  240. procedure TLTelnet.StackFull;
  241. begin
  242. {$ifdef debug}
  243. Writeln('**STACKFULL**');
  244. {$endif}
  245. if FStack[1] = TS_IAC then
  246. begin
  247. FOutput.WriteByte(Byte(FStack[1]));
  248. FOutput.WriteByte(Byte(FStack[2]));
  249. end else React(FStack[1], FStack[2]);
  250. FStack.Clear;
  251. end;
  252. procedure TLTelnet.DoubleIAC(var s: string);
  253. var
  254. i: Longint;
  255. begin
  256. i := 0;
  257. if Length(s) > 0 then
  258. while i < Length(s) do begin
  259. Inc(i);
  260. if s[i] = TS_IAC then begin
  261. Insert(TS_IAC, s, i);
  262. Inc(i, 2);
  263. end;
  264. end;
  265. end;
  266. function TLTelnet.TelnetParse(const msg: string): Integer;
  267. var
  268. i: Longint;
  269. begin
  270. Result := 0;
  271. for i := 1 to Length(msg) do
  272. if (FStack.ItemIndex > 0) or (msg[i] = TS_IAC) then begin
  273. if msg[i] = TS_GA then
  274. FStack.Clear
  275. else
  276. FStack.Push(msg[i])
  277. end else begin
  278. FOutput.WriteByte(Byte(msg[i]));
  279. Inc(Result);
  280. end;
  281. end;
  282. procedure TLTelnet.OnCs(aSocket: TLSocket);
  283. var
  284. n: Integer;
  285. begin
  286. n := 1;
  287. while (n > 0) and (FBufferIndex < FBufferEnd) do begin
  288. n := FConnection.Send(FBuffer[FBufferIndex], FBufferEnd - FBufferIndex);
  289. if n > 0 then
  290. Inc(FBufferIndex, n);
  291. end;
  292. if FBufferEnd - FBufferIndex < FBufferIndex then begin // if we can move the "right" side of the buffer back to the left
  293. Move(FBuffer[FBufferIndex], FBuffer[0], FBufferEnd - FBufferIndex);
  294. FBufferEnd := FBufferEnd - FBufferIndex;
  295. FBufferIndex := 0;
  296. end;
  297. end;
  298. function TLTelnet.OptionIsSet(const Option: Char): Boolean;
  299. begin
  300. Result := False;
  301. Result := Option in FActiveOpts;
  302. end;
  303. function TLTelnet.RegisterOption(const aOption: Char;
  304. const aCommand: Boolean): Boolean;
  305. begin
  306. Result := False;
  307. if not (aOption in FPossible) then begin
  308. FPossible := FPossible + [aOption];
  309. if aCommand then
  310. FOrders := FOrders + [aOption];
  311. Result := True;
  312. end;
  313. end;
  314. procedure TLTelnet.SetOption(const Option: Char);
  315. begin
  316. if Option in FPossible then
  317. SendCommand(Option, True);
  318. end;
  319. procedure TLTelnet.UnSetOption(const Option: Char);
  320. begin
  321. if Option in FPossible then
  322. SendCommand(Option, False);
  323. end;
  324. (* If already set, the callback can be reverted to nil but it can't be changed *)
  325. (* in a single step. The default response, if specified, is used by the *)
  326. (* LTelnetSubcommandCallback() function and is available to others. *)
  327. //
  328. function TLTelnet.RegisterSubcommand(aOption: char; callback: TLSubcommandCallback; const defaultResponse: string= ''): boolean;
  329. begin
  330. result := (not Assigned(FSubcommandCallbacks[aOption].callback)) or (@callback = nil);
  331. if result then begin
  332. FSubcommandCallbacks[aOption].callback := callback;
  333. FSubcommandCallbacks[aOption].defaultResponse := defaultResponse
  334. end
  335. end { TLTelnet.RegisterSubcommand } ;
  336. procedure TLTelnet.Disconnect(const Forced: Boolean = True);
  337. begin
  338. FConnection.Disconnect(Forced);
  339. end;
  340. procedure TLTelnet.SendCommand(const aCommand: Char; const How: TLHowEnum);
  341. begin
  342. {$ifdef debug}
  343. Writeln('**SENT** ', TNames[Char(How)], ' ', TNames[aCommand]);
  344. {$endif}
  345. AddToBuffer(TS_IAC + Char(How) + aCommand);
  346. OnCs(nil);
  347. end;
  348. //****************************TLTelnetClient*****************************
  349. constructor TLTelnetClient.Create(aOwner: TComponent);
  350. begin
  351. inherited Create(aOwner);
  352. FConnection.OnError := @OnEr;
  353. FConnection.OnDisconnect := @OnDs;
  354. FConnection.OnReceive := @OnRe;
  355. FConnection.OnConnect := @OnCo;
  356. FPossible := [TS_ECHO, TS_HYI, TS_SGA];
  357. FActiveOpts := [];
  358. FOrders := [];
  359. end;
  360. procedure TLTelnetClient.OnEr(const msg: string; aSocket: TLSocket);
  361. begin
  362. if Assigned(FOnError) then
  363. FOnError(msg, aSocket)
  364. else
  365. FOutput.Write(Pointer(msg)^, Length(msg));
  366. end;
  367. procedure TLTelnetClient.OnDs(aSocket: TLSocket);
  368. begin
  369. if Assigned(FOnDisconnect) then
  370. FOnDisconnect(aSocket);
  371. end;
  372. procedure TLTelnetClient.OnRe(aSocket: TLSocket);
  373. var
  374. s: string;
  375. begin
  376. if aSocket.GetMessage(s) > 0 then
  377. if (TelnetParse(s) > 0) and Assigned(FOnReceive) then
  378. FOnReceive(aSocket);
  379. end;
  380. procedure TLTelnetClient.OnCo(aSocket: TLSocket);
  381. begin
  382. if Assigned(FOnConnect) then
  383. FOnConnect(aSocket);
  384. end;
  385. procedure TLTelnetClient.React(const Operation, Command: Char);
  386. procedure Accept(const Operation, Command: Char);
  387. begin
  388. FActiveOpts := FActiveOpts + [Command];
  389. {$ifdef debug}
  390. Writeln('**SENT** ', TNames[Operation], ' ', TNames[Command]);
  391. {$endif}
  392. AddToBuffer(TS_IAC + Operation + Command);
  393. OnCs(nil);
  394. end;
  395. procedure Refuse(const Operation, Command: Char);
  396. begin
  397. FActiveOpts := FActiveOpts - [Command];
  398. {$ifdef debug}
  399. Writeln('**SENT** ', TNames[Operation], ' ', TNames[Command]);
  400. {$endif}
  401. AddToBuffer(TS_IAC + Operation + Command);
  402. OnCs(nil);
  403. end;
  404. (* Retrieve the parameters from the current instance, and pass them explicitly *)
  405. (* to the callback. *)
  406. //
  407. procedure subcommand(command: char);
  408. var parameters, response: string;
  409. i: integer;
  410. begin
  411. if FStack.ItemIndex > 5 then begin
  412. SetLength(parameters, FStack.ItemIndex - 5);
  413. Move(FStack[3], parameters[1], FStack.ItemIndex - 5);
  414. i := 1;
  415. while i <= Length(parameters) - 1 do (* Undouble IACs *)
  416. if (parameters[i] = TS_IAC) and (parameters[i + 1] = TS_IAC) then
  417. Delete(parameters, i, 1)
  418. else
  419. Inc(i)
  420. end else
  421. parameters := '';
  422. response := FSubcommandCallbacks[command].callback(command, parameters, FSubcommandCallbacks[command].defaultResponse);
  423. DoubleIAC(response);
  424. AddToBuffer(TS_IAC + TS_SB + command + response + TS_IAC + TS_SE);
  425. OnCs(nil)
  426. end { subcommand } ;
  427. begin
  428. {$ifdef debug}
  429. Writeln('**GOT** ', TNames[Operation], ' ', TNames[Command]);
  430. {$endif}
  431. case Operation of
  432. TS_DO : if Command in FPossible then Accept(TS_WILL, Command)
  433. else Refuse(TS_WONT, Command);
  434. TS_DONT : if Command in FPossible then Refuse(TS_WONT, Command);
  435. TS_WILL : if Command in FPossible then FActiveOpts := FActiveOpts + [Command]
  436. else Refuse(TS_DONT, Command);
  437. TS_WONT : if Command in FPossible then FActiveOpts := FActiveOpts - [Command];
  438. TS_SB : if not Assigned(FSubcommandCallbacks[command].callback) then
  439. refuse(TS_WONT, command)
  440. else
  441. subcommand(command)
  442. end;
  443. end;
  444. procedure TLTelnetClient.SendCommand(const Command: Char; const Value: Boolean);
  445. begin
  446. if Connected then begin
  447. {$ifdef debug}
  448. Writeln('**SENT** ', TNames[Question(Command, Value)], ' ', TNames[Command]);
  449. {$endif}
  450. case Question(Command, Value) of
  451. TS_WILL : FActiveOpts := FActiveOpts + [Command];
  452. end;
  453. AddToBuffer(TS_IAC + Question(Command, Value) + Command);
  454. OnCs(nil);
  455. end;
  456. end;
  457. function TLTelnetClient.Connect(const anAddress: string; const aPort: Word): Boolean;
  458. begin
  459. Result := FConnection.Connect(anAddress, aPort);
  460. end;
  461. function TLTelnetClient.Connect: Boolean;
  462. begin
  463. Result := FConnection.Connect(FHost, FPort);
  464. end;
  465. function TLTelnetClient.Get(out aData; const aSize: Integer; aSocket: TLSocket): Integer;
  466. begin
  467. Result := FOutput.Read(aData, aSize);
  468. if FOutput.Position = FOutput.Size then
  469. FOutput.Clear;
  470. end;
  471. function TLTelnetClient.GetMessage(out msg: string; aSocket: TLSocket): Integer;
  472. begin
  473. Result := 0;
  474. msg := '';
  475. if FOutput.Size > 0 then begin
  476. FOutput.Position := 0;
  477. SetLength(msg, FOutput.Size);
  478. Result := FOutput.Read(PChar(msg)^, Length(msg));
  479. FOutput.Clear;
  480. end;
  481. end;
  482. function TLTelnetClient.Send(const aData; const aSize: Integer;
  483. aSocket: TLSocket): Integer;
  484. var
  485. Tmp: string;
  486. begin
  487. {$ifdef debug}
  488. Writeln('**SEND START** ');
  489. {$endif}
  490. Result := 0;
  491. if aSize > 0 then begin
  492. SetLength(Tmp, aSize);
  493. Move(aData, PChar(Tmp)^, aSize);
  494. DoubleIAC(Tmp);
  495. if LocalEcho and (not OptionIsSet(TS_ECHO)) and (not OptionIsSet(TS_HYI)) then
  496. FOutput.Write(PChar(Tmp)^, Length(Tmp));
  497. AddToBuffer(Tmp);
  498. OnCs(nil);
  499. Result := aSize;
  500. end;
  501. {$ifdef debug}
  502. Writeln('**SEND END** ');
  503. {$endif}
  504. end;
  505. function TLTelnetClient.SendMessage(const msg: string; aSocket: TLSocket
  506. ): Integer;
  507. begin
  508. Result := Send(PChar(msg)^, Length(msg));
  509. end;
  510. procedure TLTelnetClient.CallAction;
  511. begin
  512. FConnection.CallAction;
  513. end;
  514. (* This is a default callback for use with the RegisterSubcommand() method. It *)
  515. (* may be used where the result is unchanging, for example in order to return *)
  516. (* the terminal type. *)
  517. //
  518. function LTelnetSubcommandCallback(command: char; const parameters, defaultResponse: string): string;
  519. begin
  520. result := defaultResponse
  521. end { LTelnetSubcommandCallback } ;
  522. initialization
  523. for zz := #0 to #255 do
  524. TNames[zz] := IntToStr(Ord(zz));
  525. TNames[#1] := 'TS_ECHO';
  526. TNames[#133] := 'TS_HYI';
  527. TNames[#251] := 'TS_WILL';
  528. TNames[#252] := 'TS_WONT';
  529. TNames[#253] := 'TS_DO';
  530. TNames[#254] := 'TS_DONT';
  531. end.