IdReplyRFC.pas 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  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.29 1/15/05 2:28:28 PM RLebeau
  18. Added local variables to TIdReplyRFC.GetFormattedReply() to reduce the number
  19. of repeated string operations that were being performed.
  20. Updated TIdRepliesRFC.UpdateText() to ignore the TIdReply that was passed in
  21. when looking for a TIdReply to extract Text from.
  22. Rev 1.28 10/26/2004 8:43:00 PM JPMugaas
  23. Should be more portable with new references to TIdStrings and TIdStringList.
  24. Rev 1.27 6/11/2004 8:48:28 AM DSiders
  25. Added "Do not Localize" comments.
  26. Rev 1.26 18/05/2004 23:17:18 CCostelloe
  27. Bug fix
  28. Rev 1.25 5/18/04 2:39:02 PM RLebeau
  29. Added second constructor to TIdRepliesRFC
  30. Rev 1.24 5/17/04 9:50:08 AM RLebeau
  31. Changed TIdRepliesRFC constructor to use 'reintroduce' instead
  32. Rev 1.23 5/16/04 5:12:04 PM RLebeau
  33. Added construvtor to TIdRepliesRFC class
  34. Rev 1.22 2004.03.01 5:12:36 PM czhower
  35. -Bug fix for shutdown of servers when connections still existed (AV)
  36. -Implicit HELP support in CMDserver
  37. -Several command handler bugs
  38. -Additional command handler functionality.
  39. Rev 1.21 2004.02.29 8:17:20 PM czhower
  40. Minor cosmetic changes to code.
  41. Rev 1.20 2004.02.03 4:16:50 PM czhower
  42. For unit name changes.
  43. Rev 1.19 1/3/2004 8:06:18 PM JPMugaas
  44. Bug fix: Sometimes, replies will appear twice due to the way functionality
  45. was enherited.
  46. Rev 1.18 2003.10.18 9:33:28 PM czhower
  47. Boatload of bug fixes to command handlers.
  48. Rev 1.17 9/20/2003 10:01:04 AM JPMugaas
  49. Minor change. WIll now accept all 3 digit numbers (not just ones below 600).
  50. The reason is that developers may want something in 600-999 range. RFC 2228
  51. defines a 6xx reply range for protected replies.
  52. Rev 1.16 2003.09.20 10:33:14 AM czhower
  53. Bug fix to allow clearing code field (Return to default value)
  54. Rev 1.15 2003.06.05 10:08:52 AM czhower
  55. Extended reply mechanisms to the exception handling. Only base and RFC
  56. completed, handing off to J Peter.
  57. Rev 1.14 6/3/2003 04:09:30 PM JPMugaas
  58. class function TIdReplyRFC.IsEndMarker(const ALine: string): Boolean had the
  59. wrong parameters causing FTP to freeze. It probably effected other stuff.
  60. Rev 1.13 5/30/2003 8:37:42 PM BGooijen
  61. Changed virtual to override
  62. Rev 1.12 2003.05.30 10:25:58 PM czhower
  63. Implemented IsEndMarker
  64. Rev 1.11 2003.05.30 10:06:08 PM czhower
  65. Changed code property mechanisms.
  66. Rev 1.10 2003.05.26 10:48:12 PM czhower
  67. 1) Removed deprecated code.
  68. 2) Removed POP3 bastardizations as they are now in IdReplyPOP3.
  69. Rev 1.9 5/26/2003 12:19:52 PM JPMugaas
  70. Rev 1.8 2003.05.26 11:38:20 AM czhower
  71. Rev 1.7 5/25/2003 03:16:54 AM JPMugaas
  72. Rev 1.6 2003.05.25 10:23:46 AM czhower
  73. Rev 1.5 5/21/2003 08:43:38 PM JPMugaas
  74. Overridable hook for the SMTP Reply object.
  75. Rev 1.4 5/20/2003 12:43:48 AM BGooijen
  76. changeable reply types
  77. Rev 1.3 5/19/2003 12:26:50 PM JPMugaas
  78. Now uses base class.
  79. Rev 1.2 11/05/2003 23:29:04 CCostelloe
  80. IMAP-specific code moved up to TIdIMAP4.pas
  81. Rev 1.1 11/14/2002 02:51:54 PM JPMugaas
  82. Added FormatType property. If it is rfIndentMidLines, it will accept
  83. properly parse reply lines that begin with a space. Setting this to
  84. rfIndentMidLines will also cause the reply object to generate lines that
  85. start with a space if the Text.Line starts with a space. This should
  86. accommodate the FTP MLSD and FEAT commands on both the client and server.
  87. Rev 1.0 11/13/2002 08:45:50 AM JPMugaas
  88. }
  89. unit IdReplyRFC;
  90. interface
  91. {$I IdCompilerDefines.inc}
  92. uses
  93. Classes,
  94. IdReply;
  95. type
  96. TIdReplyRFC = class(TIdReply)
  97. protected
  98. procedure AssignTo(ADest: TPersistent); override;
  99. function CheckIfCodeIsValid(const ACode: string): Boolean; override;
  100. function GetFormattedReply: TStrings; override;
  101. procedure SetFormattedReply(const AValue: TStrings); override;
  102. public
  103. class function IsEndMarker(const ALine: string): Boolean; override;
  104. procedure RaiseReplyError; override;
  105. function ReplyExists: Boolean; override;
  106. end;
  107. TIdRepliesRFC = class(TIdReplies)
  108. public
  109. constructor Create(AOwner: TPersistent); reintroduce; overload; virtual;
  110. constructor Create(AOwner: TPersistent; const AReplyClass: TIdReplyClass); overload; override;
  111. procedure UpdateText(AReply: TIdReply); override;
  112. end;
  113. // This exception is for protocol errors such as 404 HTTP error and also
  114. // SendCmd / GetResponse
  115. EIdReplyRFCError = class(EIdReplyError)
  116. protected
  117. FErrorCode: Integer;
  118. public
  119. // Params must be in this order to avoid conflict with CreateHelp
  120. // constructor in CBuilder as CB does not differentiate constructors
  121. // by name as Delphi does
  122. constructor CreateError(const AErrorCode: Integer;
  123. const AReplyMessage: string); reintroduce; virtual;
  124. //
  125. property ErrorCode: Integer read FErrorCode;
  126. end;
  127. implementation
  128. uses
  129. IdGlobal,
  130. SysUtils;
  131. { TIdReplyRFC }
  132. procedure TIdReplyRFC.AssignTo(ADest: TPersistent);
  133. var
  134. LR: TIdReplyRFC;
  135. begin
  136. if ADest is TIdReplyRFC then begin
  137. LR := TIdReplyRFC(ADest);
  138. //set code first as it possibly clears the reply
  139. LR.NumericCode := NumericCode;
  140. LR.Text.Assign(Text);
  141. end else begin
  142. inherited AssignTo(ADest);
  143. end;
  144. end;
  145. function TIdReplyRFC.CheckIfCodeIsValid(const ACode: string): Boolean;
  146. var
  147. LCode: Integer;
  148. begin
  149. LCode := IndyStrToInt(ACode, 0);
  150. {Replaced 600 with 999 because some developers may want 6xx, 7xx, and 8xx reply
  151. codes for their protocols. It also turns out that RFC 2228 defines 6xx reply codes.
  152. From RFC 2228
  153. A new class of reply types (6yz) is also introduced for protected
  154. replies.
  155. }
  156. Result := ((LCode >= 100) and (LCode < 1000)) or (Trim(ACode) = '');
  157. end;
  158. function TIdReplyRFC.GetFormattedReply: TStrings;
  159. var
  160. I, LCode: Integer;
  161. LCodeStr: String;
  162. begin
  163. Result := GetFormattedReplyStrings;
  164. LCode := NumericCode;
  165. if LCode > 0 then begin
  166. LCodeStr := IntToStr(LCode);
  167. if Text.Count > 0 then begin
  168. for I := 0 to Text.Count - 1 do begin
  169. if I < Text.Count - 1 then begin
  170. Result.Add(LCodeStr + '-' + Text[I]);
  171. end else begin
  172. Result.Add(LCodeStr + ' ' + Text[I]);
  173. end;
  174. end;
  175. end else begin
  176. Result.Add(LCodeStr);
  177. end;
  178. end else if FText.Count > 0 then begin
  179. Result.AddStrings(FText);
  180. end;
  181. end;
  182. class function TIdReplyRFC.IsEndMarker(const ALine: string): Boolean;
  183. begin
  184. if Length(ALine) >= 4 then begin
  185. Result := ALine[4] = ' ';
  186. end else begin
  187. Result := True;
  188. end;
  189. end;
  190. procedure TIdReplyRFC.RaiseReplyError;
  191. begin
  192. raise EIdReplyRFCError.CreateError(NumericCode, Text.Text);
  193. end;
  194. function TIdReplyRFC.ReplyExists: Boolean;
  195. begin
  196. Result := (NumericCode > 0) or (FText.Count > 0);
  197. end;
  198. procedure TIdReplyRFC.SetFormattedReply(const AValue: TStrings);
  199. // Just parse and put in items, no need to store after parse
  200. var
  201. i: Integer;
  202. s: string;
  203. begin
  204. Clear;
  205. if AValue.Count > 0 then begin
  206. s := Trim(Copy(AValue[0], 1, 3));
  207. Code := s;
  208. for i := 0 to AValue.Count - 1 do begin
  209. Text.Add(Copy(AValue[i], 5, MaxInt));
  210. end;
  211. end;
  212. end;
  213. { EIdReplyRFCError }
  214. constructor EIdReplyRFCError.CreateError(const AErrorCode: Integer;
  215. const AReplyMessage: string);
  216. begin
  217. inherited Create(AReplyMessage);
  218. FErrorCode := AErrorCode;
  219. end;
  220. { TIdReplies }
  221. constructor TIdRepliesRFC.Create(AOwner: TPersistent);
  222. begin
  223. inherited Create(AOwner, TIdReplyRFC);
  224. end;
  225. constructor TIdRepliesRFC.Create(AOwner: TPersistent; const AReplyClass: TIdReplyClass);
  226. begin
  227. inherited Create(AOwner, AReplyClass);
  228. end;
  229. procedure TIdRepliesRFC.UpdateText(AReply: TIdReply);
  230. var
  231. LGenericNumCode: Integer;
  232. LReply: TIdReply;
  233. begin
  234. inherited UpdateText(AReply);
  235. // If text is still blank after inherited see if we can find a generic version
  236. if AReply.Text.Count = 0 then begin
  237. LGenericNumCode := (AReply.NumericCode div 100) * 100;
  238. // RLebeau - in cases where the AReply.Code is the same as the
  239. // generic code, ignore the AReply as it doesn't have any text
  240. // to assign, or else the code wouldn't be this far
  241. LReply := Find(IntToStr(LGenericNumCode), AReply);
  242. if LReply = nil then begin
  243. // If no generic was found, then use defaults.
  244. case LGenericNumCode of
  245. 100: AReply.Text.Text := 'Information'; {do not localize}
  246. 200: AReply.Text.Text := 'Ok'; {do not localize}
  247. 300: AReply.Text.Text := 'Temporary Error'; {do not localize}
  248. 400: AReply.Text.Text := 'Permanent Error'; {do not localize}
  249. 500: AReply.Text.Text := 'Unknown Internal Error'; {do not localize}
  250. end;
  251. end else begin
  252. AReply.Text.Assign(LReply.Text);
  253. end;
  254. end;
  255. end;
  256. end.