IdCommandHandlers.pas 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  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.36 2/1/05 12:37:48 AM RLebeau
  18. Removed IdCommandHandlersEnabledDefault variable, no longer used.
  19. Rev 1.35 1/3/05 4:43:20 PM RLebeau
  20. Changed use of AnsiSameText() to use TextIsSame() instead
  21. Rev 1.34 12/17/04 12:54:04 PM RLebeau
  22. Updated TIdCommandHandler.Check() to not match misspelled commands when a
  23. CmdDelimiter is specified.
  24. Rev 1.33 12/10/04 1:48:04 PM RLebeau
  25. Bug fix for TIdCommandHandler.DoCommand()
  26. Rev 1.32 10/26/2004 8:42:58 PM JPMugaas
  27. Should be more portable with new references to TIdStrings and TIdStringList.
  28. Rev 1.31 6/17/2004 2:19:50 AM JPMugaas
  29. Problem with unparsed parameters. The initial deliniator between the command
  30. and reply was being added to Unparsed Params leading some strange results and
  31. command failures.
  32. Rev 1.30 6/6/2004 11:44:34 AM JPMugaas
  33. Removed a temporary workaround for a Telnet Sequences issue in the
  34. TIdFTPServer. That workaround is no longer needed as we fixed the issue
  35. another way.
  36. Rev 1.29 5/16/04 5:20:22 PM RLebeau
  37. Removed local variable from TIdCommandHandler constructor, no longer used
  38. Rev 1.28 2004.03.03 3:19:52 PM czhower
  39. sorted
  40. Rev 1.27 3/3/2004 4:59:40 AM JPMugaas
  41. Updated for new properties.
  42. Rev 1.26 3/2/2004 8:10:36 AM JPMugaas
  43. HelpHide renamed to HelpVisable.
  44. Rev 1.25 3/2/2004 6:37:36 AM JPMugaas
  45. Updated with properties for more comprehensive help systems.
  46. Rev 1.24 2004.03.01 7:13:40 PM czhower
  47. Comaptibilty fix.
  48. Rev 1.23 2004.03.01 5:12:26 PM czhower
  49. -Bug fix for shutdown of servers when connections still existed (AV)
  50. -Implicit HELP support in CMDserver
  51. -Several command handler bugs
  52. -Additional command handler functionality.
  53. Rev 1.22 2004.02.29 9:49:06 PM czhower
  54. Bug fix, and now responses are also write buffered.
  55. Rev 1.21 2004.02.03 4:17:10 PM czhower
  56. For unit name changes.
  57. Rev 1.20 1/29/04 10:00:40 PM RLebeau
  58. Added setter methods to various TIdReply properties
  59. Rev 1.19 2003.12.31 7:31:58 PM czhower
  60. AnsiSameText --> TextIsSame
  61. Rev 1.18 10/19/2003 11:36:52 AM DSiders
  62. Added localization comments where setting response codes.
  63. Rev 1.17 2003.10.18 9:33:26 PM czhower
  64. Boatload of bug fixes to command handlers.
  65. Rev 1.16 2003.10.18 8:07:12 PM czhower
  66. Fixed bug with defaults.
  67. Rev 1.15 2003.10.18 8:03:58 PM czhower
  68. Defaults for codes
  69. Rev 1.14 10/5/2003 03:06:18 AM JPMugaas
  70. Should compile.
  71. Rev 1.13 8/9/2003 3:52:44 PM BGooijen
  72. TIdCommandHandlers can now create any TIdCommandHandler descendant. this
  73. makes it possible to override TIdCommandHandler.check and check for the
  74. command a different way ( binary commands, protocols where the string doesn't
  75. start with the command )
  76. Rev 1.12 8/2/2003 2:22:54 PM SPerry
  77. Fixed OnCommandHandlersException problem
  78. Rev 1.11 8/2/2003 1:43:08 PM SPerry
  79. Modifications to get command handlers to work
  80. Rev 1.9 7/30/2003 10:18:30 PM SPerry
  81. Fixed AV when creating commandhandler (again) -- for some reason the bug
  82. fixed in Rev. 1.7 was still there.
  83. Rev 1.8 7/30/2003 8:31:58 PM SPerry
  84. Fixed AV with LFReplyClass.
  85. Rev 1.4 7/9/2003 10:55:26 PM BGooijen
  86. Restored all features
  87. Rev 1.3 7/9/2003 04:36:10 PM JPMugaas
  88. You now can override the TIdReply with your own type. This should illiminate
  89. some warnings about some serious issues. TIdReply is ONLY a base class with
  90. virtual methods.
  91. Rev 1.2 7/9/2003 01:43:22 PM JPMugaas
  92. Should now compile.
  93. Rev 1.1 7/9/2003 2:56:44 PM SPerry
  94. Added OnException event
  95. Rev 1.0 7/6/2003 4:47:38 PM SPerry
  96. Units that use Command handlers
  97. }
  98. unit IdCommandHandlers;
  99. {
  100. Original author: Chad Z. Hower
  101. Separate Unit : Sergio Perry
  102. }
  103. interface
  104. {$I IdCompilerDefines.inc}
  105. //Put FPC into Delphi mode
  106. uses
  107. Classes,
  108. IdComponent, IdReply, IdGlobal,
  109. IdContext, IdReplyRFC;
  110. const
  111. IdEnabledDefault = True;
  112. // DO NOT change this default (ParseParams). Many servers rely on this
  113. IdParseParamsDefault = True;
  114. IdHelpVisibleDef = True;
  115. type
  116. TIdCommandHandlers = class;
  117. TIdCommandHandler = class;
  118. TIdCommand = class;
  119. { Events }
  120. TIdCommandEvent = procedure(ASender: TIdCommand) of object;
  121. TIdAfterCommandHandlerEvent = procedure(ASender: TIdCommandHandlers;
  122. AContext: TIdContext) of object;
  123. TIdBeforeCommandHandlerEvent = procedure(ASender: TIdCommandHandlers;
  124. var AData: string; AContext: TIdContext) of object;
  125. TIdCommandHandlersExceptionEvent = procedure(ACommand: String; AContext: TIdContext) of object;
  126. { TIdCommandHandler }
  127. TIdCommandHandler = class(TCollectionItem)
  128. protected
  129. FCmdDelimiter: Char;
  130. FCommand: string;
  131. // When ARC is enabled, object references MUST be valid objects.
  132. // It is common for users to store non-object values, though, so
  133. // we will provide separate properties for those purposes
  134. //
  135. // TODO; use TValue instead of separating them
  136. //
  137. FDataObject: TObject;
  138. FDataValue: PtrInt;
  139. //
  140. FDescription: TStrings;
  141. FDisconnect: boolean;
  142. FEnabled: boolean;
  143. FExceptionReply: TIdReply;
  144. FHelpSuperScript : String; //may be something like * or + which should appear in help
  145. FHelpVisible : Boolean;
  146. FName: string;
  147. FNormalReply: TIdReply;
  148. FOnCommand: TIdCommandEvent;
  149. FParamDelimiter: Char;
  150. FParseParams: Boolean;
  151. FReplyClass : TIdReplyClass;
  152. FResponse: TStrings;
  153. FTag: integer;
  154. //
  155. function GetDisplayName: string; override;
  156. procedure SetDescription(AValue: TStrings);
  157. procedure SetExceptionReply(AValue: TIdReply);
  158. procedure SetNormalReply(AValue: TIdReply);
  159. procedure SetResponse(AValue: TStrings);
  160. public
  161. function Check(const AData: string; AContext: TIdContext): boolean; virtual;
  162. procedure DoCommand(const AData: string; AContext: TIdContext; AUnparsedParams: string); virtual;
  163. procedure DoParseParams(AUnparsedParams: string; AParams: TStrings); virtual;
  164. constructor Create(ACollection: TCollection); override;
  165. destructor Destroy; override;
  166. // function GetNamePath: string; override;
  167. function NameIs(const ACommand: string): Boolean;
  168. //
  169. property DataObject: TObject read FDataObject write FDataObject;
  170. property DataValue: PtrInt read FDataValue write FDataValue;
  171. {$IFNDEF USE_OBJECT_ARC}
  172. property Data: TObject read FDataObject write FDataObject; // deprecated 'Use DataObject or DataValue property.';
  173. {$ENDIF}
  174. published
  175. property CmdDelimiter: Char read FCmdDelimiter write FCmdDelimiter;
  176. property Command: string read FCommand write FCommand;
  177. property Description: TStrings read FDescription write SetDescription;
  178. property Disconnect: boolean read FDisconnect write FDisconnect;
  179. property Enabled: boolean read FEnabled write FEnabled default IdEnabledDefault;
  180. property ExceptionReply: TIdReply read FExceptionReply write SetExceptionReply;
  181. property Name: string read FName write FName;
  182. property NormalReply: TIdReply read FNormalReply write SetNormalReply;
  183. property ParamDelimiter: Char read FParamDelimiter write FParamDelimiter;
  184. property ParseParams: Boolean read FParseParams write FParseParams;
  185. property Response: TStrings read FResponse write SetResponse;
  186. property Tag: Integer read FTag write FTag;
  187. //
  188. property HelpSuperScript : String read FHelpSuperScript write FHelpSuperScript; //may be something like * or + which should appear in help
  189. property HelpVisible : Boolean read FHelpVisible write FHelpVisible default IdHelpVisibleDef;
  190. property OnCommand: TIdCommandEvent read FOnCommand write FOnCommand;
  191. end;
  192. TIdCommandHandlerClass = class of TIdCommandHandler;
  193. { TIdCommandHandlers }
  194. TIdCommandHandlers = class(TOwnedCollection)
  195. protected
  196. FBase: TIdComponent;
  197. FExceptionReply: TIdReply;
  198. FOnAfterCommandHandler: TIdAfterCommandHandlerEvent;
  199. FOnBeforeCommandHandler: TIdBeforeCommandHandlerEvent;
  200. FOnCommandHandlersException: TIdCommandHandlersExceptionEvent;
  201. FParseParamsDef: Boolean;
  202. FPerformReplies: Boolean;
  203. FReplyClass: TIdReplyClass;
  204. FReplyTexts: TIdReplies;
  205. //
  206. procedure DoAfterCommandHandler(AContext: TIdContext);
  207. procedure DoBeforeCommandHandler(AContext: TIdContext; var VLine: string);
  208. procedure DoOnCommandHandlersException(const ACommand: String; AContext: TIdContext);
  209. function GetItem(AIndex: Integer): TIdCommandHandler;
  210. // This is used instead of the OwnedBy property directly calling GetOwner because
  211. // D5 dies with internal errors and crashes
  212. // function GetOwnedBy: TIdPersistent;
  213. procedure SetItem(AIndex: Integer; const AValue: TIdCommandHandler);
  214. public
  215. function Add: TIdCommandHandler;
  216. constructor Create(
  217. ABase: TIdComponent;
  218. AReplyClass: TIdReplyClass;
  219. AReplyTexts: TIdReplies;
  220. AExceptionReply: TIdReply = nil;
  221. ACommandHandlerClass: TIdCommandHandlerClass = nil
  222. ); reintroduce;
  223. function HandleCommand(AContext: TIdContext; var VCommand: string): Boolean; virtual;
  224. //
  225. property Base: TIdComponent read FBase;
  226. property Items[AIndex: Integer]: TIdCommandHandler read GetItem write SetItem;
  227. // OwnedBy is used so as not to conflict with Owner in D6
  228. //property OwnedBy: TIdPersistent read GetOwnedBy;
  229. property ParseParamsDefault: Boolean read FParseParamsDef write FParseParamsDef;
  230. property PerformReplies: Boolean read FPerformReplies write FPerformReplies;
  231. property ReplyClass: TIdReplyClass read FReplyClass;
  232. property ReplyTexts: TIdReplies read FReplyTexts;
  233. //
  234. property OnAfterCommandHandler: TIdAfterCommandHandlerEvent read FOnAfterCommandHandler
  235. write FOnAfterCommandHandler;
  236. // Occurs in the context of the peer thread
  237. property OnBeforeCommandHandler: TIdBeforeCommandHandlerEvent read FOnBeforeCommandHandler
  238. write FOnBeforeCommandHandler;
  239. property OnCommandHandlersException: TIdCommandHandlersExceptionEvent read FOnCommandHandlersException
  240. write FOnCommandHandlersException;
  241. end;
  242. { TIdCommand }
  243. TIdCommand = class(TObject)
  244. protected
  245. FCommandHandler: TIdCommandHandler;
  246. FDisconnect: Boolean;
  247. FParams: TStrings;
  248. FPerformReply: Boolean;
  249. FRawLine: string;
  250. FReply: TIdReply;
  251. FResponse: TStrings;
  252. FContext: TIdContext;
  253. FUnparsedParams: string;
  254. FSendEmptyResponse: Boolean;
  255. //
  256. procedure DoCommand; virtual;
  257. procedure SetReply(AValue: TIdReply);
  258. procedure SetResponse(AValue: TStrings);
  259. public
  260. constructor Create(AOwner: TIdCommandHandler); virtual;
  261. destructor Destroy; override;
  262. procedure SendReply;
  263. //
  264. property CommandHandler: TIdCommandHandler read FCommandHandler;
  265. property Disconnect: Boolean read FDisconnect write FDisconnect;
  266. property PerformReply: Boolean read FPerformReply write FPerformReply;
  267. property Params: TStrings read FParams;
  268. property RawLine: string read FRawLine;
  269. property Reply: TIdReply read FReply write SetReply;
  270. property Response: TStrings read FResponse write SetResponse;
  271. property Context: TIdContext read FContext;
  272. property UnparsedParams: string read FUnparsedParams;
  273. property SendEmptyResponse: Boolean read FSendEmptyResponse write FSendEmptyResponse;
  274. end;//TIdCommand
  275. implementation
  276. uses
  277. SysUtils;
  278. { TIdCommandHandlers }
  279. constructor TIdCommandHandlers.Create(
  280. ABase: TIdComponent;
  281. AReplyClass: TIdReplyClass;
  282. AReplyTexts: TIdReplies;
  283. AExceptionReply: TIdReply = nil;
  284. ACommandHandlerClass: TIdCommandHandlerClass = nil
  285. );
  286. begin
  287. if ACommandHandlerClass = nil then begin
  288. ACommandHandlerClass := TIdCommandHandler;
  289. end;
  290. inherited Create(ABase, ACommandHandlerClass);
  291. FBase := ABase;
  292. FExceptionReply := AExceptionReply;
  293. FParseParamsDef := IdParseParamsDefault;
  294. FPerformReplies := True; // RLebeau: default to legacy behavior
  295. FReplyClass := AReplyClass;
  296. FReplyTexts := AReplyTexts;
  297. end;
  298. function TIdCommandHandlers.Add: TIdCommandHandler;
  299. begin
  300. Result := TIdCommandHandler(inherited Add);
  301. end;
  302. function TIdCommandHandlers.HandleCommand(AContext: TIdContext;
  303. var VCommand: string): Boolean;
  304. var
  305. i, j: Integer;
  306. begin
  307. j := Count - 1;
  308. Result := False;
  309. DoBeforeCommandHandler(AContext, VCommand); try
  310. i := 0;
  311. while i <= j do begin
  312. if Items[i].Enabled then begin
  313. Result := Items[i].Check(VCommand, AContext);
  314. if Result then begin
  315. Break;
  316. end;
  317. end;
  318. Inc(i);
  319. end;
  320. finally DoAfterCommandHandler(AContext); end;
  321. end;
  322. procedure TIdCommandHandlers.DoAfterCommandHandler(AContext: TIdContext);
  323. begin
  324. if Assigned(OnAfterCommandHandler) then begin
  325. OnAfterCommandHandler(Self, AContext);
  326. end;
  327. end;
  328. procedure TIdCommandHandlers.DoBeforeCommandHandler(AContext: TIdContext;
  329. var VLine: string);
  330. begin
  331. if Assigned(OnBeforeCommandHandler) then begin
  332. OnBeforeCommandHandler(Self, VLine, AContext);
  333. end;
  334. end;
  335. procedure TIdCommandHandlers.DoOnCommandHandlersException(const ACommand: String;
  336. AContext: TIdContext);
  337. begin
  338. if Assigned(FOnCommandHandlersException) then begin
  339. OnCommandHandlersException(ACommand, AContext);
  340. end;
  341. end;
  342. function TIdCommandHandlers.GetItem(AIndex: Integer): TIdCommandHandler;
  343. begin
  344. Result := TIdCommandHandler(inherited Items[AIndex]);
  345. end;
  346. {
  347. function TIdCommandHandlers.GetOwnedBy: TIdPersistent;
  348. begin
  349. Result := GetOwner;
  350. end;
  351. }
  352. procedure TIdCommandHandlers.SetItem(AIndex: Integer; const AValue: TIdCommandHandler);
  353. begin
  354. inherited SetItem(AIndex, AValue);
  355. end;
  356. { TIdCommandHandler }
  357. procedure TIdCommandHandler.DoCommand(const AData: string; AContext: TIdContext; AUnparsedParams: string);
  358. var
  359. LCommand: TIdCommand;
  360. begin
  361. LCommand := TIdCommand.Create(Self);
  362. try
  363. try
  364. LCommand.FRawLine := AData;
  365. LCommand.FContext := AContext;
  366. LCommand.FUnparsedParams := AUnparsedParams;
  367. if ParseParams then begin
  368. DoParseParams(AUnparsedParams, LCommand.Params);
  369. end;
  370. // RLebeau 2/21/08: for the IRC protocol, RFC 2812 section 2.4 says that
  371. // clients are not allowed to issue numeric replies for server-issued
  372. // commands. Added the PerformReplies property so TIdIRC can specify
  373. // that behavior.
  374. if Collection is TIdCommandHandlers then begin
  375. LCommand.PerformReply := TIdCommandHandlers(Collection).PerformReplies;
  376. end;
  377. try
  378. if (LCommand.Reply.Code = '') and (NormalReply.Code <> '') then begin
  379. LCommand.Reply.Assign(NormalReply);
  380. end;
  381. //if code<>'' before DoCommand, then it breaks exception handling
  382. Assert(LCommand.Reply.Code <> '');
  383. LCommand.DoCommand;
  384. if LCommand.Reply.Code = '' then begin
  385. LCommand.Reply.Assign(NormalReply);
  386. end;
  387. // UpdateText here in case user wants to add to it. SendReply also gets it in case
  388. // a different reply is sent (ie exception, etc), or the user changes the code in the event
  389. LCommand.Reply.UpdateText;
  390. except
  391. on E: Exception do begin
  392. // If there is an unhandled exception, we override all replies
  393. // If nothing specified to override with, we throw the exception again.
  394. // If the user wants a custom value on exception other, its their responsibility
  395. // to catch it before it reaches us
  396. LCommand.Reply.Clear;
  397. if LCommand.PerformReply then begin
  398. // Try from command handler first
  399. if ExceptionReply.Code <> '' then begin
  400. LCommand.Reply.Assign(ExceptionReply);
  401. // If still no go, from server
  402. // Can be nil though. Typically only servers pass it in
  403. end else if (Collection is TIdCommandHandlers) and (TIdCommandHandlers(Collection).FExceptionReply <> nil) then begin
  404. LCommand.Reply.Assign(TIdCommandHandlers(Collection).FExceptionReply);
  405. end;
  406. if LCommand.Reply.Code <> '' then begin
  407. //done this way in case an exception message has more than one line.
  408. //otherwise you could get something like this:
  409. //
  410. // 550 System Error. Code: 2
  411. // The system cannot find the file specified
  412. //
  413. //and the second line would throw off some clients.
  414. LCommand.Reply.Text.Text := E.Message;
  415. //Reply.Text.Add(E.Message);
  416. LCommand.SendReply;
  417. end else begin
  418. raise;
  419. end;
  420. end else begin
  421. raise;
  422. end;
  423. end else begin
  424. raise;
  425. end;
  426. end;
  427. if LCommand.PerformReply then begin
  428. LCommand.SendReply;
  429. end;
  430. if (LCommand.Response.Count > 0) or LCommand.SendEmptyResponse then begin
  431. AContext.Connection.WriteRFCStrings(LCommand.Response);
  432. end else if Response.Count > 0 then begin
  433. AContext.Connection.WriteRFCStrings(Response);
  434. end;
  435. finally
  436. if LCommand.Disconnect then begin
  437. AContext.Connection.Disconnect;
  438. end;
  439. end;
  440. finally
  441. LCommand.Free;
  442. end;
  443. end;
  444. procedure TIdCommandHandler.DoParseParams(AUnparsedParams: string; AParams: TStrings);
  445. // AUnparsedParams is not preparsed and is completely left up to the command handler. This will
  446. // allow for future expansion such as multiple delimiters etc, and allow the logic to properly
  447. // remain in each of the command handler implementations. In the future there may be a base type
  448. // and multiple descendants
  449. begin
  450. AParams.Clear;
  451. SplitDelimitedString(AUnparsedParams, AParams, FParamDelimiter <> #32, FParamDelimiter);
  452. end;
  453. function TIdCommandHandler.Check(const AData: string; AContext: TIdContext): boolean;
  454. // AData is not preparsed and is completely left up to the command handler. This will allow for
  455. // future expansion such as wild cards etc, and allow the logic to properly remain in each of the
  456. // command handler implementations. In the future there may be a base type and multiple descendants
  457. var
  458. LUnparsedParams: string;
  459. begin
  460. LUnparsedParams := '';
  461. Result := TextIsSame(AData, Command); // Command by itself
  462. if not Result then begin
  463. if CmdDelimiter <> #0 then begin
  464. Result := TextStartsWith(AData, Command + CmdDelimiter);
  465. if Result then begin
  466. LUnparsedParams := Copy(AData, Length(Command) + 2, MaxInt);
  467. end;
  468. end else begin
  469. // Dont strip any part of the params out.. - just remove the command purely on length and
  470. // no delim
  471. Result := TextStartsWith(AData, Command);
  472. if Result then begin
  473. LUnparsedParams := Copy(AData, Length(Command) + 1, MaxInt);
  474. end;
  475. end;
  476. end;
  477. if Result then begin
  478. DoCommand(AData, AContext, LUnparsedParams);
  479. end;
  480. end;
  481. constructor TIdCommandHandler.Create(ACollection: TCollection);
  482. begin
  483. inherited Create(ACollection);
  484. FReplyClass := TIdCommandHandlers(ACollection).ReplyClass;
  485. if FReplyClass = nil then begin
  486. FReplyClass := TIdReplyRFC;
  487. end;
  488. FCmdDelimiter := #32;
  489. FEnabled := IdEnabledDefault;
  490. FName := ClassName + IntToStr(ID);
  491. FParamDelimiter := #32;
  492. FParseParams := TIdCommandHandlers(ACollection).ParseParamsDefault;
  493. FResponse := TStringList.Create;
  494. FDescription := TStringList.Create;
  495. FNormalReply := FReplyClass.CreateWithReplyTexts(nil, TIdCommandHandlers(ACollection).ReplyTexts);
  496. if FNormalReply is TIdReplyRFC then begin
  497. FNormalReply.Code := '200'; {do not localize}
  498. end;
  499. FHelpVisible := IdHelpVisibleDef;
  500. // Dont initialize, pulls from CmdTCPServer for defaults
  501. FExceptionReply := FReplyClass.CreateWithReplyTexts(nil, TIdCommandHandlers(ACollection).ReplyTexts);
  502. end;
  503. destructor TIdCommandHandler.Destroy;
  504. begin
  505. FResponse.Free;
  506. FNormalReply.Free;
  507. FDescription.Free;
  508. FExceptionReply.Free;
  509. inherited Destroy;
  510. end;
  511. function TIdCommandHandler.GetDisplayName: string;
  512. begin
  513. if Command = '' then begin
  514. Result := Name;
  515. end else begin
  516. Result := Command;
  517. end;
  518. end;
  519. {
  520. function TIdCommandHandler.GetNamePath: string;
  521. begin
  522. if Collection <> nil then begin
  523. // OwnedBy is used because D4/D5 dont expose Owner on TOwnedCollection but D6 does
  524. Result := TIdCommandHandlers(Collection).OwnedBy.GetNamePath + '.' + Name;
  525. end else begin
  526. Result := inherited GetNamePath;
  527. end;
  528. end;
  529. }
  530. function TIdCommandHandler.NameIs(const ACommand: string): Boolean;
  531. begin
  532. Result := TextIsSame(ACommand, FName);
  533. end;
  534. procedure TIdCommandHandler.SetExceptionReply(AValue: TIdReply);
  535. begin
  536. FExceptionReply.Assign(AValue);
  537. end;
  538. procedure TIdCommandHandler.SetNormalReply(AValue: TIdReply);
  539. begin
  540. FNormalReply.Assign(AValue);
  541. end;
  542. procedure TIdCommandHandler.SetResponse(AValue: TStrings);
  543. begin
  544. FResponse.Assign(AValue);
  545. end;
  546. procedure TIdCommandHandler.SetDescription(AValue: TStrings);
  547. begin
  548. FDescription.Assign(AValue);
  549. end;
  550. { TIdCommand }
  551. constructor TIdCommand.Create(AOwner: TIdCommandHandler);
  552. begin
  553. inherited Create;
  554. FParams := TStringList.Create;
  555. FReply := AOwner.FReplyClass.CreateWithReplyTexts(nil, TIdCommandHandlers(AOwner.Collection).ReplyTexts);
  556. FResponse := TStringList.Create;
  557. FCommandHandler := AOwner;
  558. FDisconnect := AOwner.Disconnect;
  559. end;
  560. destructor TIdCommand.Destroy;
  561. begin
  562. FReply.Free;
  563. FResponse.Free;
  564. FParams.Free;
  565. inherited Destroy;
  566. end;
  567. procedure TIdCommand.DoCommand;
  568. begin
  569. if Assigned(CommandHandler.OnCommand) then begin
  570. CommandHandler.OnCommand(Self);
  571. end;
  572. end;
  573. procedure TIdCommand.SendReply;
  574. begin
  575. PerformReply := False;
  576. Reply.UpdateText;
  577. Context.Connection.IOHandler.Write(Reply.FormattedReply);
  578. end;
  579. procedure TIdCommand.SetReply(AValue: TIdReply);
  580. begin
  581. FReply.Assign(AValue);
  582. end;
  583. procedure TIdCommand.SetResponse(AValue: TStrings);
  584. begin
  585. FResponse.Assign(AValue);
  586. end;
  587. end.