IdTCPConnection.pas 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950
  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. $Log$
  13. //TODO: Elim read/write methods - they are duped
  14. //TODO: See second uses comment
  15. Rev 1.68 3/7/2005 5:48:18 PM JPMugaas
  16. Made a backdoor so we can adjust command output in specific ways.
  17. Rev 1.67 1/15/2005 6:02:02 PM JPMugaas
  18. These should compile again.
  19. Rev 1.66 1/15/05 2:16:04 PM RLebeau
  20. Misc. tweaks
  21. Rev 1.65 12/21/04 3:20:54 AM RLebeau
  22. Removed compiler warning
  23. Rev 1.64 12/12/04 2:24:28 PM RLebeau
  24. Updated WriteRFCStrings() to call new method in the IOHandler.
  25. Rev 1.63 10/26/2004 8:43:02 PM JPMugaas
  26. Should be more portable with new references to TIdStrings and TIdStringList.
  27. Rev 1.62 6/11/2004 8:48:36 AM DSiders
  28. Added "Do not Localize" comments.
  29. Rev 1.61 2004.06.07 1:34:20 PM czhower
  30. OnWork fix now sends running total as it should.
  31. Rev 1.60 2004.06.06 5:18:04 PM czhower
  32. OnWork bug fix
  33. Rev 1.59 2004.06.05 9:46:30 AM czhower
  34. IOHandler OnWork fix
  35. Rev 1.58 11/05/2004 17:13:32 HHariri
  36. Fix brought from IW for overflow of DoWork
  37. Rev 1.57 4/19/2004 9:50:08 AM BGooijen
  38. Fixed AV in .Disconnect
  39. Rev 1.56 2004.04.18 12:52:04 AM czhower
  40. Big bug fix with server disconnect and several other bug fixed that I found
  41. along the way.
  42. Rev 1.55 2004.03.06 10:40:30 PM czhower
  43. Changed IOHandler management to fix bug in server shutdowns.
  44. Rev 1.54 2004.03.06 1:32:58 PM czhower
  45. -Change to disconnect
  46. -Addition of DisconnectNotifyPeer
  47. -WriteHeader now write bufers
  48. Rev 1.53 3/1/04 7:12:00 PM RLebeau
  49. Bug fix for SetIOHandler() not updating the FSocket member correctly.
  50. Rev 1.52 2004.02.03 4:16:56 PM czhower
  51. For unit name changes.
  52. Rev 1.51 1/29/04 9:37:18 PM RLebeau
  53. Added setter method for Greeting property
  54. Rev 1.50 2004.01.28 9:42:32 PM czhower
  55. Now checks for connection.
  56. Rev 1.49 2004.01.20 10:03:36 PM czhower
  57. InitComponent
  58. Rev 1.48 2003.12.31 3:47:44 PM czhower
  59. Changed to use TextIsSame
  60. Rev 1.47 12/28/2003 4:47:40 PM BGooijen
  61. Removed ChangeReplyClass
  62. Rev 1.46 14/12/2003 18:14:54 CCostelloe
  63. Added ChangeReplyClass procedure.
  64. Rev 1.45 11/4/2003 10:28:34 PM DSiders
  65. Removed exceptions moved to IdException.pas.
  66. Rev 1.44 2003.10.18 9:33:28 PM czhower
  67. Boatload of bug fixes to command handlers.
  68. Rev 1.43 10/15/2003 7:32:48 PM DSiders
  69. Added a resource string for the exception raised in
  70. TIdTCPConnection.CreateIOHandler.
  71. Rev 1.42 2003.10.14 1:27:02 PM czhower
  72. Uupdates + Intercept support
  73. Rev 1.41 10/10/2003 11:00:36 PM BGooijen
  74. Added GetReplyClass
  75. Rev 1.40 2003.10.02 8:29:40 PM czhower
  76. Added IdReply back
  77. Rev 1.39 2003.10.02 8:08:52 PM czhower
  78. Removed unneeded unit in uses.
  79. Rev 1.38 2003.10.01 9:11:28 PM czhower
  80. .Net
  81. Rev 1.37 2003.10.01 5:05:18 PM czhower
  82. .Net
  83. Rev 1.36 2003.10.01 2:30:42 PM czhower
  84. .Net
  85. Rev 1.35 2003.10.01 11:16:38 AM czhower
  86. .Net
  87. Rev 1.34 2003.09.30 1:23:06 PM czhower
  88. Stack split for DotNet
  89. Rev 1.33 2003.09.18 7:12:42 PM czhower
  90. AV Fix in SetIOHandler
  91. Rev 1.32 2003.09.18 5:18:00 PM czhower
  92. Implemented OnWork
  93. Rev 1.31 2003.06.30 6:17:48 PM czhower
  94. Moved socket property to public. Dont know how/why it got protected.
  95. Rev 1.30 2003.06.30 5:41:56 PM czhower
  96. -Fixed AV that occurred sometimes when sockets were closed with chains
  97. -Consolidated code that was marked by a todo for merging as it no longer
  98. needed to be separate
  99. -Removed some older code that was no longer necessary
  100. Passes bubble tests.
  101. Rev 1.29 2003.06.05 10:08:52 AM czhower
  102. Extended reply mechanisms to the exception handling. Only base and RFC
  103. completed, handing off to J Peter.
  104. Rev 1.28 6/4/2003 03:54:42 PM JPMugaas
  105. Now should compile.
  106. Rev 1.27 2003.06.04 8:10:00 PM czhower
  107. Modified CheckResponse string version to allow ''
  108. Rev 1.26 2003.06.04 12:02:30 PM czhower
  109. Additions for text code and command handling.
  110. Rev 1.25 2003.06.03 3:44:26 PM czhower
  111. Removed unused variable.
  112. Rev 1.24 2003.05.30 10:25:58 PM czhower
  113. Implemented IsEndMarker
  114. Rev 1.23 5/26/2003 04:29:52 PM JPMugaas
  115. Removed GenerateReply and ParseReply. Those are now obsolete duplicate
  116. functions in the new design.
  117. Rev 1.22 5/26/2003 12:19:56 PM JPMugaas
  118. Rev 1.21 2003.05.26 11:38:20 AM czhower
  119. Rev 1.20 5/25/2003 03:34:54 AM JPMugaas
  120. Rev 1.19 5/25/2003 03:16:22 AM JPMugaas
  121. Rev 1.18 5/20/2003 02:40:10 PM JPMugaas
  122. Rev 1.17 5/20/2003 12:43:50 AM BGooijen
  123. changeable reply types
  124. Rev 1.16 4/4/2003 8:10:14 PM BGooijen
  125. procedure CreateIOHandler is now public
  126. Rev 1.15 3/27/2003 3:17:32 PM BGooijen
  127. Removed MaxLineLength, MaxLineAction, SendBufferSize, RecvBufferSize,
  128. ReadLnSplit, ReadLnTimedOut
  129. Rev 1.14 3/19/2003 1:04:16 PM BGooijen
  130. changed procedure CreateIOHandler a little (default parameter, and other
  131. behavour when parameter = nil (constructs default now))
  132. Rev 1.13 3/5/2003 11:07:18 PM BGooijen
  133. removed intercept from this file
  134. Rev 1.12 2003.02.25 7:28:02 PM czhower
  135. Fixed WriteRFCReply
  136. Rev 1.11 2003.02.25 1:36:20 AM czhower
  137. Rev 1.10 2/13/2003 02:14:44 PM JPMugaas
  138. Now calls ReadLn in GetInternelResponse so a space is not dropped. Dropping
  139. a space throws off some things in FTP such as the FEAT reply.
  140. Rev 1.9 2003.01.18 12:29:52 PM czhower
  141. Rev 1.8 1-17-2003 22:22:08 BGooijen
  142. new design
  143. Rev 1.7 12-16-2002 20:44:38 BGooijen
  144. Added procedure CreateIOHandler(....)
  145. Rev 1.6 12-15-2002 23:32:32 BGooijen
  146. Added RecvBufferSize
  147. Rev 1.5 12-14-2002 22:16:32 BGooijen
  148. improved method to detect timeouts in ReadLn.
  149. Rev 1.4 12/6/2002 02:11:46 PM JPMugaas
  150. Protected Port and Host properties added to TCPClient because those are
  151. needed by protocol implementations. Socket property added to TCPConnection.
  152. Rev 1.3 6/12/2002 11:00:16 AM SGrobety
  153. Rev 1.0 21/11/2002 12:36:48 PM SGrobety Version: Indy 10
  154. Rev 1.2 11/15/2002 01:26:42 PM JPMugaas
  155. Restored Trim to ReadLnWait and changed GetInternelResponse to use ReadLn
  156. instead of ReadLn wait.
  157. Rev 1.1 11/14/2002 06:44:54 PM JPMugaas
  158. Removed Trim from ReadLnWait. It was breaking the new RFC Reply parsing code
  159. by removing the space at the beggining of a line.
  160. Rev 1.0 11/13/2002 09:00:30 AM JPMugaas
  161. }
  162. unit IdTCPConnection;
  163. interface
  164. {$i IdCompilerDefines.inc}
  165. {
  166. 2003-12-14 - Ciaran Costelloe
  167. - Added procedure ChangeReplyClass, because in .NET, you cannot set FReplyClass
  168. before calling the constructor, so call this procedure after the constructor
  169. to set FReplyClass to (say) TIdReplyIMAP4.
  170. 2002-06 -Andrew P.Rybin
  171. -WriteStream optimization and new "friendly" interface, InputLn fix (CrLf only if AEcho)
  172. 2002-04-12 - Andrew P.Rybin
  173. - ReadLn bugfix and optimization
  174. 2002-01-20 - Chad Z. Hower a.k.a Kudzu
  175. -WriteBuffer change was not correct. Removed. Need info on original problem to fix properly.
  176. -Modified ReadLnWait
  177. 2002-01-19 - Grahame Grieve
  178. - Fix to WriteBuffer to accept -1 from the stack.
  179. Also fixed to clean up FWriteBuffer if connection lost.
  180. 2002-01-19 - Chad Z. Hower a.k.a Kudzu
  181. -Fix to ReadLn
  182. 2002-01-16 - Andrew P.Rybin
  183. -ReadStream optimization, TIdManagedBuffer new
  184. 2002-01-03 - Chad Z. Hower a.k.a Kudzu
  185. -Added MaxLineAction
  186. -Added ReadLnSplit
  187. 2001-12-27 - Chad Z. Hower a.k.a Kudzu
  188. -Changes and bug fixes to InputLn
  189. -Modifed how buffering works
  190. -Added property InputBuffer
  191. -Moved some things to TIdBuffer
  192. -Modified ReadLn
  193. -Added LineCount to Capture
  194. 2001-12-25 - Andrew P.Rybin
  195. -MaxLineLength,ReadLn,InputLn and Merry Christmas!
  196. Original Author and Maintainer:
  197. -Chad Z. Hower a.k.a Kudzu
  198. }
  199. uses
  200. Classes,
  201. IdComponent,
  202. IdException,
  203. IdExceptionCore,
  204. IdGlobal,
  205. IdIntercept,
  206. IdIOHandler,
  207. IdIOHandlerSocket,
  208. IdIOHandlerStack,
  209. IdReply,
  210. IdSocketHandle,
  211. IdBaseComponent;
  212. type
  213. TIdTCPConnection = class(TIdComponent)
  214. protected
  215. FGreeting: TIdReply; // TODO: Only TIdFTP uses it, so it should be moved!
  216. {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FIntercept: TIdConnectionIntercept;
  217. {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FIOHandler: TIdIOHandler;
  218. FLastCmdResult: TIdReply;
  219. FManagedIOHandler: Boolean;
  220. FOnDisconnected: TNotifyEvent;
  221. {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FSocket: TIdIOHandlerSocket;
  222. FReplyClass: TIdReplyClass;
  223. //
  224. procedure CheckConnected;
  225. procedure DoOnDisconnected; virtual;
  226. procedure InitComponent; override;
  227. function GetIntercept: TIdConnectionIntercept; virtual;
  228. function GetReplyClass: TIdReplyClass; virtual;
  229. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  230. procedure SetIntercept(AValue: TIdConnectionIntercept); virtual;
  231. procedure SetIOHandler(AValue: TIdIOHandler); virtual;
  232. procedure SetGreeting(AValue: TIdReply);
  233. procedure WorkBeginEvent(ASender: TObject; AWorkMode: TWorkMode; AWorkCountMax: Int64);
  234. procedure WorkEndEvent(ASender: TObject; AWorkMode: TWorkMode);
  235. procedure WorkEvent(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
  236. procedure PrepareCmd(var aCmd: string); virtual;
  237. public
  238. procedure CreateIOHandler(ABaseType: TIdIOHandlerClass = nil);
  239. procedure CheckForGracefulDisconnect(ARaiseExceptionIfDisconnected: Boolean = True); virtual;
  240. //
  241. function CheckResponse(const AResponse: Int16;
  242. const AAllowedResponses: array of Int16): Int16; overload; virtual;
  243. function CheckResponse(const AResponse, AAllowedResponse: string): string; overload; virtual;
  244. //
  245. function Connected: Boolean; virtual;
  246. destructor Destroy; override;
  247. // Dont allow override of this one, its for overload only
  248. procedure Disconnect; overload; // .Net overload
  249. procedure Disconnect(ANotifyPeer: Boolean); overload; virtual;
  250. // This is called when a protocol sends a command to tell the other side (typically client to
  251. // server) that it is about to disconnect. The implementation should go here.
  252. procedure DisconnectNotifyPeer; virtual;
  253. // GetInternalResponse is not in IOHandler as some protocols may need to
  254. // override it. It could be still moved and proxied from here, but at this
  255. // point it is here.
  256. procedure GetInternalResponse(AEncoding: IIdTextEncoding = nil); virtual;
  257. // Reads response using GetInternalResponse which each reply type can define
  258. // the behaviour. Then checks against expected Code.
  259. //
  260. // Seperate one for singles as one of the older Delphi compilers cannot
  261. // match a single number into an array. IIRC newer ones do.
  262. function GetResponse(const AAllowedResponse: Int16 = -1;
  263. AEncoding: IIdTextEncoding = nil): Int16; overload;
  264. function GetResponse(const AAllowedResponses: array of Int16;
  265. AEncoding: IIdTextEncoding = nil): Int16; overload; virtual;
  266. // No array type for strings as ones that use strings are usually bastard
  267. // protocols like POP3/IMAP which dont include proper substatus anyways.
  268. //
  269. // If a case can be made for some other condition this may be expanded
  270. // in the future
  271. function GetResponse(const AAllowedResponse: string;
  272. AEncoding: IIdTextEncoding = nil): string; overload; virtual;
  273. //
  274. property Greeting: TIdReply read FGreeting write SetGreeting;
  275. // RaiseExceptionForCmdResult - Overload necesary as a exception as a default param doesnt work
  276. procedure RaiseExceptionForLastCmdResult; overload; virtual;
  277. procedure RaiseExceptionForLastCmdResult(AException: TClassIdException);
  278. overload; virtual;
  279. // These are extended GetResponses, so see the comments for GetResponse
  280. function SendCmd(AOut: string; const AResponse: Int16 = -1;
  281. AEncoding: IIdTextEncoding = nil): Int16; overload;
  282. function SendCmd(AOut: string; const AResponse: array of Int16;
  283. AEncoding: IIdTextEncoding = nil): Int16; overload; virtual;
  284. function SendCmd(AOut: string; const AResponse: string;
  285. AEncoding: IIdTextEncoding = nil): string; overload;
  286. //
  287. procedure WriteHeader(AHeader: TStrings);
  288. procedure WriteRFCStrings(AStrings: TStrings);
  289. //
  290. property LastCmdResult: TIdReply read FLastCmdResult;
  291. property ManagedIOHandler: Boolean read FManagedIOHandler write FManagedIOHandler;
  292. property Socket: TIdIOHandlerSocket read FSocket;
  293. published
  294. property Intercept: TIdConnectionIntercept read GetIntercept write SetIntercept;
  295. property IOHandler: TIdIOHandler read FIOHandler write SetIOHandler;
  296. // Events
  297. property OnDisconnected: TNotifyEvent read FOnDisconnected write FOnDisconnected;
  298. property OnWork;
  299. property OnWorkBegin;
  300. property OnWorkEnd;
  301. end;
  302. implementation
  303. uses
  304. IdAntiFreezeBase, IdResourceStringsCore, IdStackConsts, IdReplyRFC,
  305. SysUtils;
  306. function TIdTCPConnection.GetIntercept: TIdConnectionIntercept;
  307. var
  308. // under ARC, convert a weak reference to a strong reference before working with it
  309. LIOHandler: TIdIOHandler;
  310. begin
  311. LIOHandler := IOHandler;
  312. if LIOHandler <> nil then begin
  313. Result := LIOHandler.Intercept;
  314. end else begin
  315. Result := FIntercept;
  316. end;
  317. end;
  318. function TIdTCPConnection.GetReplyClass:TIdReplyClass;
  319. begin
  320. Result := TIdReplyRFC;
  321. end;
  322. procedure TIdTCPConnection.CreateIOHandler(ABaseType:TIdIOHandlerClass=nil);
  323. begin
  324. if Connected then begin
  325. raise EIdException.Create(RSIOHandlerCannotChange); // TODO: create a new Exception class for this
  326. end;
  327. if Assigned(ABaseType) then begin
  328. IOHandler := TIdIOHandler.MakeIOHandler(ABaseType, Self);
  329. end else begin
  330. IOHandler := TIdIOHandler.MakeDefaultIOHandler(Self);
  331. end;
  332. ManagedIOHandler := True;
  333. end;
  334. function TIdTCPConnection.Connected: Boolean;
  335. var
  336. // under ARC, convert a weak reference to a strong reference before working with it
  337. LIOHandler: TIdIOHandler;
  338. begin
  339. // Its been changed now that IOHandler is not usually nil, but can be before the initial connect
  340. // and also this keeps it here so the user does not have to access the IOHandler for this and
  341. // also to allow future control from the connection.
  342. LIOHandler := IOHandler;
  343. Result := Assigned(LIOHandler);
  344. if Result then begin
  345. Result := LIOHandler.Connected;
  346. end;
  347. end;
  348. destructor TIdTCPConnection.Destroy;
  349. var
  350. // under ARC, convert a weak reference to a strong reference before working with it
  351. LIOHandler: TIdIOHandler;
  352. begin
  353. // Just close IOHandler directly. Dont call Disconnect - Disconnect may be override and
  354. // try to read/write to the socket.
  355. LIOHandler := IOHandler;
  356. if Assigned(LIOHandler) then begin
  357. LIOHandler.Close;
  358. // This will free any managed IOHandlers
  359. {$IFDEF USE_OBJECT_ARC}LIOHandler := nil;{$ENDIF}
  360. SetIOHandler(nil);
  361. end;
  362. FreeAndNil(FLastCmdResult);
  363. FreeAndNil(FGreeting);
  364. inherited Destroy;
  365. end;
  366. procedure TIdTCPConnection.Disconnect(ANotifyPeer: Boolean);
  367. var
  368. // under ARC, convert a weak reference to a strong reference before working with it
  369. LIOHandler: TIdIOHandler;
  370. begin
  371. try
  372. // Separately to avoid calling .Connected unless needed
  373. if ANotifyPeer then begin
  374. // TODO: do not call Connected() here if DisconnectNotifyPeer() is not
  375. // overriden. Ideally, Connected() should be called by overridden
  376. // DisconnectNotifyPeer() implementations if they really need it. But
  377. // to avoid any breakages in third-party overrides, we could check here
  378. // if DisconnectNotifyPeer() has been overridden and then call Connected()
  379. // to maintain existing behavior...
  380. //
  381. try
  382. if Connected then begin
  383. DisconnectNotifyPeer;
  384. end;
  385. except
  386. // TODO: maybe allow only EIdConnClosedGracefully and EIdSocketError?
  387. end;
  388. end;
  389. finally
  390. {
  391. there are a few possible situations here:
  392. 1) we are still connected, then everything works as before,
  393. status disconnecting, then disconnect, status disconnected
  394. 2) we are not connected, and this is just some "rogue" call to
  395. disconnect(), then nothing happens
  396. 3) we are not connected, because ClosedGracefully, then
  397. LConnected will be false, but the implicit call to
  398. CheckForDisconnect (inside Connected) will call the events
  399. }
  400. // We dont check connected here - we realy dont care about actual socket state
  401. // Here we just want to close the actual IOHandler. It is very possible for a
  402. // socket to be disconnected but the IOHandler still open. In this case we only
  403. // care of the IOHandler is still open.
  404. //
  405. // This is especially important if the socket has been disconnected with error, at this
  406. // point we just want to ignore it and checking .Connected would trigger this. We
  407. // just want to close. For some reason NS 7.1 (And only 7.1, not 7.0 or Mozilla) cause
  408. // CONNABORTED. So its extra important we just disconnect without checking socket state.
  409. LIOHandler := IOHandler;
  410. if Assigned(LIOHandler) then begin
  411. if LIOHandler.Opened then begin
  412. DoStatus(hsDisconnecting);
  413. LIOHandler.Close;
  414. DoOnDisconnected;
  415. DoStatus(hsDisconnected);
  416. //LIOHandler.InputBuffer.Clear;
  417. end;
  418. end;
  419. end;
  420. end;
  421. procedure TIdTCPConnection.DoOnDisconnected;
  422. begin
  423. if Assigned(OnDisconnected) then begin
  424. OnDisconnected(Self);
  425. end;
  426. end;
  427. function TIdTCPConnection.GetResponse(const AAllowedResponses: array of Int16;
  428. AEncoding: IIdTextEncoding = nil): Int16;
  429. begin
  430. GetInternalResponse(AEncoding);
  431. Result := CheckResponse(LastCmdResult.NumericCode, AAllowedResponses);
  432. end;
  433. procedure TIdTCPConnection.RaiseExceptionForLastCmdResult(
  434. AException: TClassIdException);
  435. begin
  436. raise AException.Create(LastCmdResult.Text.Text);
  437. end;
  438. procedure TIdTCPConnection.RaiseExceptionForLastCmdResult;
  439. begin
  440. LastCmdResult.RaiseReplyError;
  441. end;
  442. function TIdTCPConnection.SendCmd(AOut: string; const AResponse: Array of Int16;
  443. AEncoding: IIdTextEncoding = nil): Int16;
  444. begin
  445. CheckConnected;
  446. PrepareCmd(AOut);
  447. IOHandler.WriteLn(AOut, AEncoding);
  448. Result := GetResponse(AResponse, AEncoding);
  449. end;
  450. // under ARC, all weak references to a freed object get nil'ed automatically
  451. // so this is mostly redundant
  452. procedure TIdTCPConnection.Notification(AComponent: TComponent; Operation: TOperation);
  453. begin
  454. if (Operation = opRemove) then begin
  455. {$IFNDEF USE_OBJECT_ARC}
  456. if (AComponent = FIntercept) then begin
  457. FIntercept := nil;
  458. end else
  459. {$ENDIF}
  460. if (AComponent = FIOHandler) then begin
  461. FIOHandler := nil;
  462. FSocket := nil;
  463. FManagedIOHandler := False;
  464. end;
  465. end;
  466. inherited Notification(AComponent, Operation);
  467. end;
  468. procedure TIdTCPConnection.SetIntercept(AValue: TIdConnectionIntercept);
  469. var
  470. // under ARC, convert weak references to strong references before working with them
  471. LIntercept: TIdConnectionIntercept;
  472. LIOHandler: TIdIOHandler;
  473. begin
  474. LIntercept := FIntercept;
  475. if LIntercept <> AValue then
  476. begin
  477. LIOHandler := IOHandler;
  478. // RLebeau 8/25/09 - normally, short-circuit logic should skip all subsequent
  479. // evaluations in a multi-condition statement once one of the conditions
  480. // evaluates to False. However, a user just ran into a situation where that
  481. // was not the case! It caused an AV in SetIOHandler() further below when
  482. // AValue was nil (from Destroy() further above) because Assigned(AValue.Intercept)
  483. // was still being evaluated even though Assigned(AValue) was returning False.
  484. // SetIntercept() is using the same kind of short-circuit logic here as well.
  485. // Let's not rely on short-circuiting anymore, just to be on the safe side.
  486. //
  487. // old code: if Assigned(IOHandler) and Assigned(IOHandler.Intercept) and Assigned(AValue) and (AValue <> IOHandler.Intercept) then begin
  488. //
  489. if Assigned(LIOHandler) and Assigned(AValue) then begin
  490. if Assigned(LIOHandler.Intercept) and (LIOHandler.Intercept <> AValue) then begin
  491. raise EIdException.Create(RSInterceptIsDifferent); // TODO: create a new Exception class for this
  492. end;
  493. end;
  494. // TODO: should LIntercept.Connection be set to nil here if LIntercept
  495. // is not nil and LIntercept.Connection is set to Self?
  496. {$IFDEF USE_OBJECT_ARC}
  497. // under ARC, all weak references to a freed object get nil'ed automatically
  498. FIntercept := AValue;
  499. {$ELSE}
  500. // remove self from the Intercept's free notification list
  501. if Assigned(LIntercept) then begin
  502. LIntercept.RemoveFreeNotification(Self);
  503. end;
  504. FIntercept := AValue;
  505. // add self to the Intercept's free notification list
  506. if Assigned(AValue) then begin
  507. AValue.FreeNotification(Self);
  508. end;
  509. {$ENDIF}
  510. if Assigned(LIOHandler) then begin
  511. LIOHandler.Intercept := AValue;
  512. end;
  513. // TODO: should FIntercept.Connection be set to Self here if FIntercept
  514. // is not nil?
  515. end;
  516. end;
  517. procedure TIdTCPConnection.SetIOHandler(AValue: TIdIOHandler);
  518. var
  519. // under ARC, convert weak references to strong references before working with them
  520. LIOHandler: TIdIOHandler;
  521. LIntercept, LOtherIntercept: TIdConnectionIntercept;
  522. begin
  523. LIOHandler := FIOHandler;
  524. if LIOHandler <> AValue then begin
  525. LIntercept := FIntercept;
  526. // RLebeau 8/25/09 - normally, short-circuit logic should skip all subsequent
  527. // evaluations in a multi-condition statement once one of the conditions
  528. // evaluates to False. However, a user just ran into a situation where that
  529. // was not the case! It caused an AV when AValue was nil (from Destroy()
  530. // further above) because Assigned(AValue.Intercept) was still being evaluated
  531. // even though Assigned(AValue) was returning False. Let's not rely on
  532. // short-circuiting anymore, just to be on the safe side.
  533. //
  534. // old code: if Assigned(AValue) and Assigned(AValue.Intercept) and Assigned(FIntercept) and (AValue.Intercept <> FIntercept) then begin
  535. //
  536. if Assigned(AValue) and Assigned(LIntercept) then begin
  537. LOtherIntercept := AValue.Intercept;
  538. if Assigned(LOtherIntercept) then begin
  539. if LOtherIntercept <> LIntercept then begin
  540. raise EIdException.Create(RSInterceptIsDifferent); // TODO: create a new Exception class for this
  541. end;
  542. {$IFDEF USE_OBJECT_ARC}LOtherIntercept := nil;{$ENDIF}
  543. end;
  544. end;
  545. if ManagedIOHandler then begin
  546. if Assigned(LIOHandler) then begin
  547. FIOHandler := nil;
  548. IdDisposeAndNil(LIOHandler);
  549. end;
  550. ManagedIOHandler := False;
  551. end;
  552. // under ARC, all weak references to a freed object get nil'ed automatically
  553. // Reset this if nil (to match nil, but not needed) or when a new IOHandler is specified
  554. // If true, code must set it after the IOHandler is set
  555. // Must do after call to FreeManagedIOHandler
  556. FSocket := nil;
  557. // Clear out old values whether setting AValue to nil, or setting a new value
  558. if Assigned(LIOHandler) then begin
  559. LIOHandler.WorkTarget := nil;
  560. {$IFNDEF USE_OBJECT_ARC}
  561. LIOHandler.RemoveFreeNotification(Self);
  562. {$ENDIF}
  563. end;
  564. if Assigned(AValue) then begin
  565. {$IFNDEF USE_OBJECT_ARC}
  566. // add self to the IOHandler's free notification list
  567. AValue.FreeNotification(Self);
  568. {$ENDIF}
  569. // Must set to handlers and not events directly as user may change
  570. // the events of TCPConnection after we have initialized these and then
  571. // these would point to old values
  572. AValue.WorkTarget := Self;
  573. if Assigned(LIntercept) then begin
  574. AValue.Intercept := LIntercept;
  575. end;
  576. if AValue is TIdIOHandlerSocket then begin
  577. FSocket := TIdIOHandlerSocket(AValue);
  578. end;
  579. end;
  580. // Last as some code uses FIOHandler to finalize items
  581. FIOHandler := AValue;
  582. end;
  583. end;
  584. procedure TIdTCPConnection.WriteHeader(AHeader: TStrings);
  585. var
  586. i: Integer;
  587. LBufferingStarted: Boolean;
  588. // under ARC, convert a weak reference to a strong reference before working with it
  589. LIOHandler: TIdIOHandler;
  590. begin
  591. CheckConnected;
  592. LIOHandler := IOHandler;
  593. LBufferingStarted := not LIOHandler.WriteBufferingActive;
  594. if LBufferingStarted then begin
  595. LIOHandler.WriteBufferOpen;
  596. end;
  597. try
  598. for i := 0 to AHeader.Count -1 do begin
  599. // No ReplaceAll flag - we only want to replace the first one
  600. LIOHandler.WriteLn(ReplaceOnlyFirst(AHeader[i], '=', ': '));
  601. end;
  602. LIOHandler.WriteLn;
  603. if LBufferingStarted then begin
  604. LIOHandler.WriteBufferClose;
  605. end;
  606. except
  607. if LBufferingStarted then begin
  608. LIOHandler.WriteBufferCancel;
  609. end;
  610. raise;
  611. end;
  612. end;
  613. function TIdTCPConnection.SendCmd(AOut: string; const AResponse: Int16 = -1;
  614. AEncoding: IIdTextEncoding = nil): Int16;
  615. begin
  616. if AResponse < 0 then begin
  617. Result := SendCmd(AOut, [], AEncoding);
  618. end else begin
  619. Result := SendCmd(AOut, [AResponse], AEncoding);
  620. end;
  621. end;
  622. procedure TIdTCPConnection.CheckForGracefulDisconnect(ARaiseExceptionIfDisconnected: Boolean);
  623. var
  624. // under ARC, convert a weak reference to a strong reference before working with it
  625. LIOHandler: TIdIOHandler;
  626. begin
  627. LIOHandler := IOHandler;
  628. if Assigned(LIOHandler) then begin
  629. LIOHandler.CheckForDisconnect(ARaiseExceptionIfDisconnected);
  630. end else if ARaiseExceptionIfDisconnected then begin
  631. raise EIdException.Create(RSNotConnected); // TODO: create a new Exception class for this
  632. end;
  633. end;
  634. function TIdTCPConnection.CheckResponse(const AResponse: Int16;
  635. const AAllowedResponses: array of Int16): Int16;
  636. begin
  637. if High(AAllowedResponses) > -1 then begin
  638. if PosInSmallIntArray(AResponse, AAllowedResponses) = -1 then begin
  639. RaiseExceptionForLastCmdResult;
  640. end;
  641. end;
  642. Result := AResponse;
  643. end;
  644. procedure TIdTCPConnection.GetInternalResponse(AEncoding: IIdTextEncoding = nil);
  645. var
  646. LLine: string;
  647. LResponse: TStringList;
  648. // under ARC, convert a weak reference to a strong reference before working with it
  649. LIOHandler: TIdIOHandler;
  650. begin
  651. CheckConnected;
  652. LResponse := TStringList.Create;
  653. try
  654. // Some servers with bugs send blank lines before reply. Dont remember which
  655. // ones, but I do remember we changed this for a reason
  656. // RLebeau 9/14/06: this can happen in between lines of the reply as well
  657. LIOHandler := IOHandler;
  658. repeat
  659. LLine := LIOHandler.ReadLnWait(MaxInt, AEncoding);
  660. LResponse.Add(LLine);
  661. until FLastCmdResult.IsEndMarker(LLine);
  662. //Note that FormattedReply uses an assign in it's property set method.
  663. FLastCmdResult.FormattedReply := LResponse;
  664. finally
  665. FreeAndNil(LResponse);
  666. end;
  667. end;
  668. procedure TIdTCPConnection.WriteRFCStrings(AStrings: TStrings);
  669. begin
  670. CheckConnected;
  671. IOHandler.WriteRFCStrings(AStrings, True);
  672. end;
  673. function TIdTCPConnection.GetResponse(const AAllowedResponse: Int16 = -1;
  674. AEncoding: IIdTextEncoding = nil): Int16;
  675. begin
  676. if AAllowedResponse < 0 then begin
  677. Result := GetResponse([], AEncoding);
  678. end else begin
  679. Result := GetResponse([AAllowedResponse], AEncoding);
  680. end;
  681. end;
  682. function TIdTCPConnection.GetResponse(const AAllowedResponse: string;
  683. AEncoding: IIdTextEncoding = nil): string;
  684. begin
  685. GetInternalResponse(AEncoding);
  686. Result := CheckResponse(LastCmdResult.Code, AAllowedResponse);
  687. end;
  688. function TIdTCPConnection.SendCmd(AOut: string; const AResponse: string;
  689. AEncoding: IIdTextEncoding = nil): string;
  690. begin
  691. CheckConnected;
  692. PrepareCmd(AOut);
  693. IOHandler.WriteLn(AOut, AEncoding);
  694. Result := GetResponse(AResponse, AEncoding);
  695. end;
  696. function TIdTCPConnection.CheckResponse(const AResponse, AAllowedResponse: string): string;
  697. begin
  698. if (AAllowedResponse <> '')
  699. and (not TextIsSame(AResponse, AAllowedResponse)) then begin
  700. RaiseExceptionForLastCmdResult;
  701. end;
  702. Result := AResponse;
  703. end;
  704. procedure TIdTCPConnection.WorkBeginEvent(ASender: TObject; AWorkMode: TWorkMode;
  705. AWorkCountMax: Int64);
  706. begin
  707. BeginWork(AWorkMode, AWorkCountMax)
  708. end;
  709. procedure TIdTCPConnection.WorkEndEvent(ASender: TObject; AWorkMode: TWorkMode);
  710. begin
  711. EndWork(AWorkMode)
  712. end;
  713. procedure TIdTCPConnection.WorkEvent(ASender: TObject; AWorkMode: TWorkMode;
  714. AWorkCount: Int64);
  715. begin
  716. DoWork(AWorkMode, AWorkCount)
  717. end;
  718. procedure TIdTCPConnection.InitComponent;
  719. begin
  720. inherited InitComponent;
  721. FReplyClass := GetReplyClass;
  722. FGreeting := FReplyClass.CreateWithReplyTexts(nil, nil);
  723. FLastCmdResult := FReplyClass.CreateWithReplyTexts(nil, nil);
  724. end;
  725. procedure TIdTCPConnection.CheckConnected;
  726. begin
  727. if not Assigned(IOHandler) then begin
  728. raise EIdNotConnected.Create(RSNotConnected);
  729. end;
  730. end;
  731. procedure TIdTCPConnection.SetGreeting(AValue: TIdReply);
  732. begin
  733. FGreeting.Assign(AValue);
  734. end;
  735. procedure TIdTCPConnection.Disconnect;
  736. begin
  737. // The default should be to tell the other side we are disconnecting
  738. Disconnect(True);
  739. end;
  740. procedure TIdTCPConnection.DisconnectNotifyPeer;
  741. begin
  742. end;
  743. procedure TIdTCPConnection.PrepareCmd(var aCmd: string);
  744. begin
  745. //Leave this empty here. It's for cases where we may need to
  746. // override what is sent to a server in a transparent manner.
  747. end;
  748. end.