IdReply.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  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.27 2/3/05 12:16:46 AM RLebeau
  18. Bug fix for UpdateText()
  19. Rev 1.25 1/15/2005 6:02:02 PM JPMugaas
  20. These should compile again.
  21. Rev 1.24 1/15/05 2:03:20 PM RLebeau
  22. Added AIgnore parameter to TIdReplies.Find()
  23. Updated TIdReply.SetNumericCode() to call SetCode() rather than assigning the
  24. FCode member directly.
  25. Updated TIdReply.SetCode() to call Clear() before assigning the FCode member.
  26. Updated TIdReplies.UpdateText() to ignore the TIdReply that was passed in
  27. when looking for a TIdReply to extract Text from.
  28. Rev 1.23 12/29/04 1:36:44 PM RLebeau
  29. Bug fix for when descendant constructors are called twice during creation
  30. Rev 1.22 10/26/2004 8:43:00 PM JPMugaas
  31. Should be more portable with new references to TIdStrings and TIdStringList.
  32. Rev 1.21 6/11/2004 8:48:24 AM DSiders
  33. Added "Do not Localize" comments.
  34. Rev 1.20 2004.03.01 7:10:34 PM czhower
  35. Change for .net compat
  36. Rev 1.19 2004.03.01 5:12:34 PM czhower
  37. -Bug fix for shutdown of servers when connections still existed (AV)
  38. -Implicit HELP support in CMDserver
  39. -Several command handler bugs
  40. -Additional command handler functionality.
  41. Rev 1.18 2004.02.29 8:16:54 PM czhower
  42. Bug fix to fix AV at design time when adding reply texts to CmdTCPServer.
  43. Rev 1.17 2004.02.03 4:17:10 PM czhower
  44. For unit name changes.
  45. Rev 1.16 2004.01.29 12:02:32 AM czhower
  46. .Net constructor problem fix.
  47. Rev 1.15 1/3/2004 8:06:20 PM JPMugaas
  48. Bug fix: Sometimes, replies will appear twice due to the way functionality
  49. was enherited.
  50. Rev 1.14 1/1/2004 9:33:24 PM BGooijen
  51. the abstract class TIdReply was created sometimes, fixed that
  52. Rev 1.13 2003.10.18 9:33:28 PM czhower
  53. Boatload of bug fixes to command handlers.
  54. Rev 1.12 10/15/2003 7:49:38 PM DSiders
  55. Added IdResourceStringsCore to implementation uses clause.
  56. Rev 1.11 10/15/2003 7:46:42 PM DSiders
  57. Added formatted resource string for the exception raised in
  58. TIdReply.SetCode.
  59. Rev 1.10 2003.09.06 1:30:30 PM czhower
  60. Removed abstract modifier from a class method so that C++ Builder can compile
  61. again.
  62. Rev 1.9 2003.06.05 10:08:50 AM czhower
  63. Extended reply mechanisms to the exception handling. Only base and RFC
  64. completed, handing off to J Peter.
  65. Rev 1.8 2003.05.30 10:25:56 PM czhower
  66. Implemented IsEndMarker
  67. Rev 1.7 2003.05.30 10:06:08 PM czhower
  68. Changed code property mechanisms.
  69. Rev 1.6 5/26/2003 04:29:56 PM JPMugaas
  70. Removed GenerateReply and ParseReply. Those are now obsolete duplicate
  71. functions in the new design.
  72. Rev 1.5 5/26/2003 12:19:54 PM JPMugaas
  73. Rev 1.4 2003.05.26 11:38:18 AM czhower
  74. Rev 1.3 2003.05.25 10:23:44 AM czhower
  75. Rev 1.2 5/20/2003 12:43:46 AM BGooijen
  76. changeable reply types
  77. Rev 1.1 5/19/2003 05:54:58 PM JPMugaas
  78. Rev 1.0 5/19/2003 12:26:16 PM JPMugaas
  79. Base class for reply format objects.
  80. }
  81. unit IdReply;
  82. interface
  83. {$I IdCompilerDefines.inc}
  84. //we need to put this in Delphi mode to work
  85. uses
  86. Classes,
  87. IdException;
  88. type
  89. TIdReplies = class;
  90. //TODO: a streamed write only property will be registered to convert old DFMs
  91. // into the new one for old TextCode and to ignore NumericCode which has been
  92. // removed
  93. TIdReply = class(TCollectionItem)
  94. protected
  95. FCode: string;
  96. FFormattedReply: TStrings;
  97. FReplyTexts: TIdReplies;
  98. FText: TStrings;
  99. //
  100. procedure AssignTo(ADest: TPersistent); override;
  101. procedure CommonInit;
  102. function GetFormattedReplyStrings: TStrings; virtual;
  103. function CheckIfCodeIsValid(const ACode: string): Boolean; virtual;
  104. function GetDisplayName: string; override;
  105. function GetFormattedReply: TStrings; virtual;
  106. function GetNumericCode: Integer;
  107. procedure SetCode(const AValue: string);
  108. procedure SetFormattedReply(const AValue: TStrings); virtual; abstract;
  109. procedure SetText(const AValue: TStrings);
  110. procedure SetNumericCode(const AValue: Integer);
  111. public
  112. procedure Clear; virtual;
  113. //Temp workaround for compiler bug
  114. constructor Create(ACollection: TCollection); override;
  115. constructor CreateWithReplyTexts(ACollection: TCollection; AReplyTexts: TIdReplies); virtual;
  116. // Both creates are necessary. This base one is called by the collection editor at design time
  117. // constructor Create(ACollection: TCollection); overload; override;
  118. // constructor Create(ACollection: TCollection; AReplyTexts: TIdReplies); reintroduce; overload; virtual;
  119. destructor Destroy; override;
  120. // Is not abstract because C++ cannot compile abstract class methods
  121. class function IsEndMarker(const ALine: string): Boolean; virtual;
  122. procedure RaiseReplyError; virtual; abstract;
  123. function ReplyExists: Boolean; virtual;
  124. procedure SetReply(const ACode: Integer; const AText: string); overload; virtual;
  125. procedure SetReply(const ACode: string; const AText: string); overload; virtual;
  126. procedure UpdateText;
  127. //
  128. property FormattedReply: TStrings read GetFormattedReply write SetFormattedReply;
  129. property NumericCode: Integer read GetNumericCode write SetNumericCode;
  130. published
  131. //warning: setting Code has a side-effect of calling Clear;
  132. property Code: string read FCode write SetCode nodefault;
  133. property Text: TStrings read FText write SetText;
  134. end;
  135. TIdReplyClass = class of TIdReply;
  136. TIdReplies = class(TOwnedCollection)
  137. protected
  138. function GetItem(Index: Integer): TIdReply;
  139. procedure SetItem(Index: Integer; const Value: TIdReply);
  140. public
  141. function Add: TIdReply; overload;
  142. function Add(const ACode: Integer; const AText: string): TIdReply; overload;
  143. function Add(const ACode, AText: string): TIdReply; overload;
  144. constructor Create(AOwner: TPersistent; const AReplyClass: TIdReplyClass); reintroduce; virtual;
  145. function Find(const ACode: string; AIgnore: TIdReply = nil): TIdReply; virtual;
  146. procedure UpdateText(AReply: TIdReply); virtual;
  147. //
  148. property Items[Index: Integer]: TIdReply read GetItem write SetItem; default;
  149. end;
  150. TIdRepliesClass = class of TIdReplies;
  151. EIdReplyError = class(EIdException);
  152. implementation
  153. uses
  154. IdGlobal, IdResourceStringsCore, SysUtils;
  155. { TIdReply }
  156. procedure TIdReply.AssignTo(ADest: TPersistent);
  157. var
  158. LR : TIdReply;
  159. begin
  160. if ADest is TIdReply then begin
  161. LR := TIdReply(ADest);
  162. //set code first as it possibly clears the reply
  163. LR.Code := Code;
  164. LR.Text.Assign(Text);
  165. end else begin
  166. inherited AssignTo(ADest);
  167. end;
  168. end;
  169. procedure TIdReply.Clear;
  170. begin
  171. FText.Clear;
  172. FCode := '';
  173. end;
  174. constructor TIdReply.CreateWithReplyTexts(ACollection: TCollection; AReplyTexts: TIdReplies);
  175. begin
  176. inherited Create(ACollection);
  177. FReplyTexts := AReplyTexts;
  178. CommonInit;
  179. end;
  180. constructor TIdReply.Create(ACollection: TCollection);
  181. begin
  182. inherited Create(ACollection);
  183. CommonInit;
  184. end;
  185. destructor TIdReply.Destroy;
  186. begin
  187. FreeAndNil(FText);
  188. FreeAndNil(FFormattedReply);
  189. inherited Destroy;
  190. end;
  191. procedure TIdReply.CommonInit;
  192. begin
  193. FFormattedReply := TStringList.Create;
  194. FText := TStringList.Create;
  195. end;
  196. function TIdReply.GetDisplayName: string;
  197. begin
  198. if Text.Count > 0 then begin
  199. Result := Code + ' ' + Text[0];
  200. end else begin
  201. Result := Code;
  202. end;
  203. end;
  204. function TIdReply.ReplyExists: Boolean;
  205. begin
  206. Result := Code <> '';
  207. end;
  208. procedure TIdReply.SetNumericCode(const AValue: Integer);
  209. begin
  210. Code := IntToStr(AValue);
  211. end;
  212. procedure TIdReply.SetText(const AValue: TStrings);
  213. begin
  214. FText.Assign(AValue);
  215. end;
  216. procedure TIdReply.SetReply(const ACode: Integer; const AText: string);
  217. begin
  218. SetReply(IntToStr(ACode), AText);
  219. end;
  220. function TIdReply.GetNumericCode: Integer;
  221. begin
  222. Result := IndyStrToInt(Code, 0);
  223. end;
  224. procedure TIdReply.SetCode(const AValue: string);
  225. var
  226. LMatchedReply: TIdReply;
  227. begin
  228. if FCode <> AValue then begin
  229. if not CheckIfCodeIsValid(AValue) then begin
  230. raise EIdException.CreateFmt(RSReplyInvalidCode, [AValue]); // TODO: create a new Exception class for this
  231. end;
  232. // Only check for duplicates if we are in a collection. NormalReply etc are not in collections
  233. // Also dont check FReplyTexts, as non members can be duplicates of members
  234. if Collection <> nil then begin
  235. LMatchedReply := TIdReplies(Collection).Find(AValue);
  236. if Assigned(LMatchedReply) then begin
  237. raise EIdException.CreateFmt(RSReplyCodeAlreadyExists, [AValue]); // TODO: create a new Exception class for this
  238. end;
  239. end;
  240. Clear;
  241. FCode := AValue;
  242. end;
  243. end;
  244. procedure TIdReply.SetReply(const ACode, AText: string);
  245. begin
  246. Code := ACode;
  247. FText.Text := AText;
  248. end;
  249. function TIdReply.CheckIfCodeIsValid(const ACode: string): Boolean;
  250. begin
  251. Result := True;
  252. end;
  253. class function TIdReply.IsEndMarker(const ALine: string): Boolean;
  254. begin
  255. Result := False;
  256. end;
  257. function TIdReply.GetFormattedReply: TStrings;
  258. begin
  259. // Overrides must call GetFormattedReplyStrings instead. This is just a base implementation
  260. // This is done this way because otherwise double generations can occur if more than one
  261. // ancestor overrides. Example: Reply--> RFC --> FTP. Calling inherited would cause both
  262. // FTP and RFC to generate.
  263. Result := GetFormattedReplyStrings;
  264. end;
  265. function TIdReply.GetFormattedReplyStrings: TStrings;
  266. begin
  267. FFormattedReply.Clear;
  268. Result := FFormattedReply;
  269. end;
  270. procedure TIdReply.UpdateText;
  271. begin
  272. if FReplyTexts <> nil then begin
  273. FReplyTexts.UpdateText(Self);
  274. end;
  275. end;
  276. { TIdReplies }
  277. function TIdReplies.Add: TIdReply;
  278. begin
  279. Result := TIdReply(inherited Add);
  280. end;
  281. function TIdReplies.Add(const ACode: Integer; const AText: string): TIdReply;
  282. begin
  283. Result := Add(IntToStr(ACode), AText);
  284. end;
  285. function TIdReplies.Add(const ACode, AText: string): TIdReply;
  286. begin
  287. Result := Add;
  288. try
  289. Result.SetReply(ACode, AText);
  290. except
  291. FreeAndNil(Result);
  292. raise;
  293. end;
  294. end;
  295. constructor TIdReplies.Create(AOwner: TPersistent; const AReplyClass: TIdReplyClass);
  296. begin
  297. inherited Create(AOwner, AReplyClass);
  298. end;
  299. function TIdReplies.Find(const ACode: string; AIgnore: TIdReply = nil): TIdReply;
  300. var
  301. i: Integer;
  302. begin
  303. Result := nil;
  304. // Never return match on ''
  305. if ACode <> '' then begin
  306. for i := 0 to Count - 1 do begin
  307. if Items[i].Code = ACode then begin
  308. if not (Items[i] = AIgnore) then begin
  309. Result := Items[i];
  310. Exit;
  311. end;
  312. end;
  313. end;
  314. end;
  315. end;
  316. function TIdReplies.GetItem(Index: Integer): TIdReply;
  317. begin
  318. Result := TIdReply(inherited Items[Index]);
  319. end;
  320. procedure TIdReplies.SetItem(Index: Integer; const Value: TIdReply);
  321. begin
  322. inherited SetItem(Index, Value);
  323. end;
  324. procedure TIdReplies.UpdateText(AReply: TIdReply);
  325. var
  326. LReply: TIdReply;
  327. begin
  328. // If text is blank, get it from the ReplyTexts
  329. if AReply.Text.Count = 0 then begin
  330. // RLebeau - ignore AReply, it doesn't have any text
  331. // to assign, or else the code wouldn't be this far
  332. LReply := Find(AReply.Code, AReply);
  333. if LReply <> nil then begin
  334. AReply.Text.Assign(LReply.Text);
  335. end;
  336. end;
  337. end;
  338. end.