IdCommandHandlers.pas 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  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. {$IFDEF USE_OBJECT_ARC}
  132. // When ARC is enabled, object references MUST be valid objects.
  133. // It is common for users to store non-object values, though, so
  134. // we will provide separate properties for those purposes
  135. //
  136. // TODO; use TValue instead of separating them
  137. //
  138. FDataObject: TObject;
  139. FDataValue: PtrInt;
  140. {$ELSE}
  141. FData: TObject;
  142. {$ENDIF}
  143. FDescription: TStrings;
  144. FDisconnect: boolean;
  145. FEnabled: boolean;
  146. FExceptionReply: TIdReply;
  147. FHelpSuperScript : String; //may be something like * or + which should appear in help
  148. FHelpVisible : Boolean;
  149. FName: string;
  150. FNormalReply: TIdReply;
  151. FOnCommand: TIdCommandEvent;
  152. FParamDelimiter: Char;
  153. FParseParams: Boolean;
  154. FReplyClass : TIdReplyClass;
  155. FResponse: TStrings;
  156. FTag: integer;
  157. //
  158. function GetDisplayName: string; override;
  159. procedure SetDescription(AValue: TStrings);
  160. procedure SetExceptionReply(AValue: TIdReply);
  161. procedure SetNormalReply(AValue: TIdReply);
  162. procedure SetResponse(AValue: TStrings);
  163. public
  164. function Check(const AData: string; AContext: TIdContext): boolean; virtual;
  165. procedure DoCommand(const AData: string; AContext: TIdContext; AUnparsedParams: string); virtual;
  166. procedure DoParseParams(AUnparsedParams: string; AParams: TStrings); virtual;
  167. constructor Create(ACollection: TCollection); override;
  168. destructor Destroy; override;
  169. // function GetNamePath: string; override;
  170. function NameIs(const ACommand: string): Boolean;
  171. //
  172. {$IFDEF USE_OBJECT_ARC}
  173. property DataObject: TObject read FDataObject write FDataObject;
  174. property DataValue: PtrInt read FDataValue write FDataValue;
  175. {$ELSE}
  176. property Data: TObject read FData write FData;
  177. {$ENDIF}
  178. published
  179. property CmdDelimiter: Char read FCmdDelimiter write FCmdDelimiter;
  180. property Command: string read FCommand write FCommand;
  181. property Description: TStrings read FDescription write SetDescription;
  182. property Disconnect: boolean read FDisconnect write FDisconnect;
  183. property Enabled: boolean read FEnabled write FEnabled default IdEnabledDefault;
  184. property ExceptionReply: TIdReply read FExceptionReply write SetExceptionReply;
  185. property Name: string read FName write FName;
  186. property NormalReply: TIdReply read FNormalReply write SetNormalReply;
  187. property ParamDelimiter: Char read FParamDelimiter write FParamDelimiter;
  188. property ParseParams: Boolean read FParseParams write FParseParams;
  189. property Response: TStrings read FResponse write SetResponse;
  190. property Tag: Integer read FTag write FTag;
  191. //
  192. property HelpSuperScript : String read FHelpSuperScript write FHelpSuperScript; //may be something like * or + which should appear in help
  193. property HelpVisible : Boolean read FHelpVisible write FHelpVisible default IdHelpVisibleDef;
  194. property OnCommand: TIdCommandEvent read FOnCommand write FOnCommand;
  195. end;
  196. TIdCommandHandlerClass = class of TIdCommandHandler;
  197. { TIdCommandHandlers }
  198. TIdCommandHandlers = class(TOwnedCollection)
  199. protected
  200. FBase: TIdComponent;
  201. FExceptionReply: TIdReply;
  202. FOnAfterCommandHandler: TIdAfterCommandHandlerEvent;
  203. FOnBeforeCommandHandler: TIdBeforeCommandHandlerEvent;
  204. FOnCommandHandlersException: TIdCommandHandlersExceptionEvent;
  205. FParseParamsDef: Boolean;
  206. FPerformReplies: Boolean;
  207. FReplyClass: TIdReplyClass;
  208. FReplyTexts: TIdReplies;
  209. //
  210. procedure DoAfterCommandHandler(AContext: TIdContext);
  211. procedure DoBeforeCommandHandler(AContext: TIdContext; var VLine: string);
  212. procedure DoOnCommandHandlersException(const ACommand: String; AContext: TIdContext);
  213. function GetItem(AIndex: Integer): TIdCommandHandler;
  214. // This is used instead of the OwnedBy property directly calling GetOwner because
  215. // D5 dies with internal errors and crashes
  216. // function GetOwnedBy: TIdPersistent;
  217. procedure SetItem(AIndex: Integer; const AValue: TIdCommandHandler);
  218. public
  219. function Add: TIdCommandHandler;
  220. constructor Create(
  221. ABase: TIdComponent;
  222. AReplyClass: TIdReplyClass;
  223. AReplyTexts: TIdReplies;
  224. AExceptionReply: TIdReply = nil;
  225. ACommandHandlerClass: TIdCommandHandlerClass = nil
  226. ); reintroduce;
  227. function HandleCommand(AContext: TIdContext; var VCommand: string): Boolean; virtual;
  228. //
  229. property Base: TIdComponent read FBase;
  230. property Items[AIndex: Integer]: TIdCommandHandler read GetItem write SetItem;
  231. // OwnedBy is used so as not to conflict with Owner in D6
  232. //property OwnedBy: TIdPersistent read GetOwnedBy;
  233. property ParseParamsDefault: Boolean read FParseParamsDef write FParseParamsDef;
  234. property PerformReplies: Boolean read FPerformReplies write FPerformReplies;
  235. property ReplyClass: TIdReplyClass read FReplyClass;
  236. property ReplyTexts: TIdReplies read FReplyTexts;
  237. //
  238. property OnAfterCommandHandler: TIdAfterCommandHandlerEvent read FOnAfterCommandHandler
  239. write FOnAfterCommandHandler;
  240. // Occurs in the context of the peer thread
  241. property OnBeforeCommandHandler: TIdBeforeCommandHandlerEvent read FOnBeforeCommandHandler
  242. write FOnBeforeCommandHandler;
  243. property OnCommandHandlersException: TIdCommandHandlersExceptionEvent read FOnCommandHandlersException
  244. write FOnCommandHandlersException;
  245. end;
  246. { TIdCommand }
  247. TIdCommand = class(TObject)
  248. protected
  249. FCommandHandler: TIdCommandHandler;
  250. FDisconnect: Boolean;
  251. FParams: TStrings;
  252. FPerformReply: Boolean;
  253. FRawLine: string;
  254. FReply: TIdReply;
  255. FResponse: TStrings;
  256. FContext: TIdContext;
  257. FUnparsedParams: string;
  258. FSendEmptyResponse: Boolean;
  259. //
  260. procedure DoCommand; virtual;
  261. procedure SetReply(AValue: TIdReply);
  262. procedure SetResponse(AValue: TStrings);
  263. public
  264. constructor Create(AOwner: TIdCommandHandler); virtual;
  265. destructor Destroy; override;
  266. procedure SendReply;
  267. //
  268. property CommandHandler: TIdCommandHandler read FCommandHandler;
  269. property Disconnect: Boolean read FDisconnect write FDisconnect;
  270. property PerformReply: Boolean read FPerformReply write FPerformReply;
  271. property Params: TStrings read FParams;
  272. property RawLine: string read FRawLine;
  273. property Reply: TIdReply read FReply write SetReply;
  274. property Response: TStrings read FResponse write SetResponse;
  275. property Context: TIdContext read FContext;
  276. property UnparsedParams: string read FUnparsedParams;
  277. property SendEmptyResponse: Boolean read FSendEmptyResponse write FSendEmptyResponse;
  278. end;//TIdCommand
  279. implementation
  280. uses
  281. SysUtils;
  282. { TIdCommandHandlers }
  283. constructor TIdCommandHandlers.Create(
  284. ABase: TIdComponent;
  285. AReplyClass: TIdReplyClass;
  286. AReplyTexts: TIdReplies;
  287. AExceptionReply: TIdReply = nil;
  288. ACommandHandlerClass: TIdCommandHandlerClass = nil
  289. );
  290. begin
  291. if ACommandHandlerClass = nil then begin
  292. ACommandHandlerClass := TIdCommandHandler;
  293. end;
  294. inherited Create(ABase, ACommandHandlerClass);
  295. FBase := ABase;
  296. FExceptionReply := AExceptionReply;
  297. FParseParamsDef := IdParseParamsDefault;
  298. FPerformReplies := True; // RLebeau: default to legacy behavior
  299. FReplyClass := AReplyClass;
  300. FReplyTexts := AReplyTexts;
  301. end;
  302. function TIdCommandHandlers.Add: TIdCommandHandler;
  303. begin
  304. Result := TIdCommandHandler(inherited Add);
  305. end;
  306. function TIdCommandHandlers.HandleCommand(AContext: TIdContext;
  307. var VCommand: string): Boolean;
  308. var
  309. i, j: Integer;
  310. begin
  311. j := Count - 1;
  312. Result := False;
  313. DoBeforeCommandHandler(AContext, VCommand); try
  314. i := 0;
  315. while i <= j do begin
  316. if Items[i].Enabled then begin
  317. Result := Items[i].Check(VCommand, AContext);
  318. if Result then begin
  319. Break;
  320. end;
  321. end;
  322. Inc(i);
  323. end;
  324. finally DoAfterCommandHandler(AContext); end;
  325. end;
  326. procedure TIdCommandHandlers.DoAfterCommandHandler(AContext: TIdContext);
  327. begin
  328. if Assigned(OnAfterCommandHandler) then begin
  329. OnAfterCommandHandler(Self, AContext);
  330. end;
  331. end;
  332. procedure TIdCommandHandlers.DoBeforeCommandHandler(AContext: TIdContext;
  333. var VLine: string);
  334. begin
  335. if Assigned(OnBeforeCommandHandler) then begin
  336. OnBeforeCommandHandler(Self, VLine, AContext);
  337. end;
  338. end;
  339. procedure TIdCommandHandlers.DoOnCommandHandlersException(const ACommand: String;
  340. AContext: TIdContext);
  341. begin
  342. if Assigned(FOnCommandHandlersException) then begin
  343. OnCommandHandlersException(ACommand, AContext);
  344. end;
  345. end;
  346. function TIdCommandHandlers.GetItem(AIndex: Integer): TIdCommandHandler;
  347. begin
  348. Result := TIdCommandHandler(inherited Items[AIndex]);
  349. end;
  350. {
  351. function TIdCommandHandlers.GetOwnedBy: TIdPersistent;
  352. begin
  353. Result := GetOwner;
  354. end;
  355. }
  356. procedure TIdCommandHandlers.SetItem(AIndex: Integer; const AValue: TIdCommandHandler);
  357. begin
  358. inherited SetItem(AIndex, AValue);
  359. end;
  360. { TIdCommandHandler }
  361. procedure TIdCommandHandler.DoCommand(const AData: string; AContext: TIdContext; AUnparsedParams: string);
  362. var
  363. LCommand: TIdCommand;
  364. begin
  365. LCommand := TIdCommand.Create(Self);
  366. try
  367. LCommand.FRawLine := AData;
  368. LCommand.FContext := AContext;
  369. LCommand.FUnparsedParams := AUnparsedParams;
  370. if ParseParams then begin
  371. DoParseParams(AUnparsedParams, LCommand.Params);
  372. end;
  373. // RLebeau 2/21/08: for the IRC protocol, RFC 2812 section 2.4 says that
  374. // clients are not allowed to issue numeric replies for server-issued
  375. // commands. Added the PerformReplies property so TIdIRC can specify
  376. // that behavior.
  377. if Collection is TIdCommandHandlers then begin
  378. LCommand.PerformReply := TIdCommandHandlers(Collection).PerformReplies;
  379. end;
  380. try
  381. if (LCommand.Reply.Code = '') and (NormalReply.Code <> '') then begin
  382. LCommand.Reply.Assign(NormalReply);
  383. end;
  384. //if code<>'' before DoCommand, then it breaks exception handling
  385. Assert(LCommand.Reply.Code <> '');
  386. LCommand.DoCommand;
  387. if LCommand.Reply.Code = '' then begin
  388. LCommand.Reply.Assign(NormalReply);
  389. end;
  390. // UpdateText here in case user wants to add to it. SendReply also gets it in case
  391. // a different reply is sent (ie exception, etc), or the user changes the code in the event
  392. LCommand.Reply.UpdateText;
  393. except
  394. on E: Exception do begin
  395. // If there is an unhandled exception, we override all replies
  396. // If nothing specified to override with, we throw the exception again.
  397. // If the user wants a custom value on exception other, its their responsibility
  398. // to catch it before it reaches us
  399. LCommand.Reply.Clear;
  400. if LCommand.PerformReply then begin
  401. // Try from command handler first
  402. if ExceptionReply.Code <> '' then begin
  403. LCommand.Reply.Assign(ExceptionReply);
  404. // If still no go, from server
  405. // Can be nil though. Typically only servers pass it in
  406. end else if (Collection is TIdCommandHandlers) and (TIdCommandHandlers(Collection).FExceptionReply <> nil) then begin
  407. LCommand.Reply.Assign(TIdCommandHandlers(Collection).FExceptionReply);
  408. end;
  409. if LCommand.Reply.Code <> '' then begin
  410. //done this way in case an exception message has more than one line.
  411. //otherwise you could get something like this:
  412. //
  413. // 550 System Error. Code: 2
  414. // The system cannot find the file specified
  415. //
  416. //and the second line would throw off some clients.
  417. LCommand.Reply.Text.Text := E.Message;
  418. //Reply.Text.Add(E.Message);
  419. LCommand.SendReply;
  420. end else begin
  421. raise;
  422. end;
  423. end else begin
  424. raise;
  425. end;
  426. end else begin
  427. raise;
  428. end;
  429. end;
  430. if LCommand.PerformReply then begin
  431. LCommand.SendReply;
  432. end;
  433. if (LCommand.Response.Count > 0) or LCommand.SendEmptyResponse then begin
  434. AContext.Connection.WriteRFCStrings(LCommand.Response);
  435. end else if Response.Count > 0 then begin
  436. AContext.Connection.WriteRFCStrings(Response);
  437. end;
  438. finally
  439. try
  440. if LCommand.Disconnect then begin
  441. AContext.Connection.Disconnect;
  442. end;
  443. finally
  444. LCommand.Free;
  445. end;
  446. end;
  447. end;
  448. procedure TIdCommandHandler.DoParseParams(AUnparsedParams: string; AParams: TStrings);
  449. // AUnparsedParams is not preparsed and is completely left up to the command handler. This will
  450. // allow for future expansion such as multiple delimiters etc, and allow the logic to properly
  451. // remain in each of the command handler implementations. In the future there may be a base type
  452. // and multiple descendants
  453. begin
  454. AParams.Clear;
  455. SplitDelimitedString(AUnparsedParams, AParams, FParamDelimiter <> #32, FParamDelimiter);
  456. end;
  457. function TIdCommandHandler.Check(const AData: string; AContext: TIdContext): boolean;
  458. // AData is not preparsed and is completely left up to the command handler. This will allow for
  459. // future expansion such as wild cards etc, and allow the logic to properly remain in each of the
  460. // command handler implementations. In the future there may be a base type and multiple descendants
  461. var
  462. LUnparsedParams: string;
  463. begin
  464. LUnparsedParams := '';
  465. Result := TextIsSame(AData, Command); // Command by itself
  466. if not Result then begin
  467. if CmdDelimiter <> #0 then begin
  468. Result := TextStartsWith(AData, Command + CmdDelimiter);
  469. if Result then begin
  470. LUnparsedParams := Copy(AData, Length(Command) + 2, MaxInt);
  471. end;
  472. end else begin
  473. // Dont strip any part of the params out.. - just remove the command purely on length and
  474. // no delim
  475. Result := TextStartsWith(AData, Command);
  476. if Result then begin
  477. LUnparsedParams := Copy(AData, Length(Command) + 1, MaxInt);
  478. end;
  479. end;
  480. end;
  481. if Result then begin
  482. DoCommand(AData, AContext, LUnparsedParams);
  483. end;
  484. end;
  485. constructor TIdCommandHandler.Create(ACollection: TCollection);
  486. begin
  487. inherited Create(ACollection);
  488. FReplyClass := TIdCommandHandlers(ACollection).ReplyClass;
  489. if FReplyClass = nil then begin
  490. FReplyClass := TIdReplyRFC;
  491. end;
  492. FCmdDelimiter := #32;
  493. FEnabled := IdEnabledDefault;
  494. FName := ClassName + IntToStr(ID);
  495. FParamDelimiter := #32;
  496. FParseParams := TIdCommandHandlers(ACollection).ParseParamsDefault;
  497. FResponse := TStringList.Create;
  498. FDescription := TStringList.Create;
  499. FNormalReply := FReplyClass.CreateWithReplyTexts(nil, TIdCommandHandlers(ACollection).ReplyTexts);
  500. if FNormalReply is TIdReplyRFC then begin
  501. FNormalReply.Code := '200'; {do not localize}
  502. end;
  503. FHelpVisible := IdHelpVisibleDef;
  504. // Dont initialize, pulls from CmdTCPServer for defaults
  505. FExceptionReply := FReplyClass.CreateWithReplyTexts(nil, TIdCommandHandlers(ACollection).ReplyTexts);
  506. end;
  507. destructor TIdCommandHandler.Destroy;
  508. begin
  509. FreeAndNil(FResponse);
  510. FreeAndNil(FNormalReply);
  511. FreeAndNil(FDescription);
  512. FreeAndNil(FExceptionReply);
  513. inherited Destroy;
  514. end;
  515. function TIdCommandHandler.GetDisplayName: string;
  516. begin
  517. if Command = '' then begin
  518. Result := Name;
  519. end else begin
  520. Result := Command;
  521. end;
  522. end;
  523. {
  524. function TIdCommandHandler.GetNamePath: string;
  525. begin
  526. if Collection <> nil then begin
  527. // OwnedBy is used because D4/D5 dont expose Owner on TOwnedCollection but D6 does
  528. Result := TIdCommandHandlers(Collection).OwnedBy.GetNamePath + '.' + Name;
  529. end else begin
  530. Result := inherited GetNamePath;
  531. end;
  532. end;
  533. }
  534. function TIdCommandHandler.NameIs(const ACommand: string): Boolean;
  535. begin
  536. Result := TextIsSame(ACommand, FName);
  537. end;
  538. procedure TIdCommandHandler.SetExceptionReply(AValue: TIdReply);
  539. begin
  540. FExceptionReply.Assign(AValue);
  541. end;
  542. procedure TIdCommandHandler.SetNormalReply(AValue: TIdReply);
  543. begin
  544. FNormalReply.Assign(AValue);
  545. end;
  546. procedure TIdCommandHandler.SetResponse(AValue: TStrings);
  547. begin
  548. FResponse.Assign(AValue);
  549. end;
  550. procedure TIdCommandHandler.SetDescription(AValue: TStrings);
  551. begin
  552. FDescription.Assign(AValue);
  553. end;
  554. { TIdCommand }
  555. constructor TIdCommand.Create(AOwner: TIdCommandHandler);
  556. begin
  557. inherited Create;
  558. FParams := TStringList.Create;
  559. FReply := AOwner.FReplyClass.CreateWithReplyTexts(nil, TIdCommandHandlers(AOwner.Collection).ReplyTexts);
  560. FResponse := TStringList.Create;
  561. FCommandHandler := AOwner;
  562. FDisconnect := AOwner.Disconnect;
  563. end;
  564. destructor TIdCommand.Destroy;
  565. begin
  566. FreeAndNil(FReply);
  567. FreeAndNil(FResponse);
  568. FreeAndNil(FParams);
  569. inherited Destroy;
  570. end;
  571. procedure TIdCommand.DoCommand;
  572. begin
  573. if Assigned(CommandHandler.OnCommand) then begin
  574. CommandHandler.OnCommand(Self);
  575. end;
  576. end;
  577. procedure TIdCommand.SendReply;
  578. begin
  579. PerformReply := False;
  580. Reply.UpdateText;
  581. Context.Connection.IOHandler.Write(Reply.FormattedReply);
  582. end;
  583. procedure TIdCommand.SetReply(AValue: TIdReply);
  584. begin
  585. FReply.Assign(AValue);
  586. end;
  587. procedure TIdCommand.SetResponse(AValue: TStrings);
  588. begin
  589. FResponse.Assign(AValue);
  590. end;
  591. end.