/trunk/Common/Overbyte ICS 7/OverbyteIcsHttpProt.pas

http://castlesand.googlecode.com/ · Pascal · 5003 lines · 3286 code · 356 blank · 1361 comment · 557 complexity · 72aa1ad415fc0ebf881a218f47d9a840 MD5 · raw file

  1. {*_* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2. Author: François PIETTE
  3. Creation: November 23, 1997
  4. Version: 7.08
  5. Description: THttpCli is an implementation for the HTTP protocol
  6. RFC 1945 (V1.0), and some of RFC 2068 (V1.1)
  7. Credit: This component was based on a freeware from by Andreas
  8. Hoerstemeier and used with his permission.
  9. andy@hoerstemeier.de http://www.hoerstemeier.com/index.htm
  10. EMail: francois.piette@overbyte.be http://www.overbyte.be
  11. Support: Use the mailing list twsocket@elists.org
  12. Follow "support" link at http://www.overbyte.be for subscription.
  13. Legal issues: Copyright (C) 1997-2010 by François PIETTE
  14. Rue de Grady 24, 4053 Embourg, Belgium. Fax: +32-4-365.74.56
  15. <francois.piette@overbyte.be>
  16. SSL implementation includes code written by Arno Garrels,
  17. Berlin, Germany, contact: <arno.garrels@gmx.de>
  18. This software is provided 'as-is', without any express or
  19. implied warranty. In no event will the author be held liable
  20. for any damages arising from the use of this software.
  21. Permission is granted to anyone to use this software for any
  22. purpose, including commercial applications, and to alter it
  23. and redistribute it freely, subject to the following
  24. restrictions:
  25. 1. The origin of this software must not be misrepresented,
  26. you must not claim that you wrote the original software.
  27. If you use this software in a product, an acknowledgment
  28. in the product documentation would be appreciated but is
  29. not required.
  30. 2. Altered source versions must be plainly marked as such, and
  31. must not be misrepresented as being the original software.
  32. 3. This notice may not be removed or altered from any source
  33. distribution.
  34. 4. You must register this software by sending a picture postcard
  35. to the author. Use a nice stamp and mention your name, street
  36. address, EMail address and any comment you like to say.
  37. Quick Reference:
  38. HTTP component can retrieve documents or files using HTTP protocol; that is
  39. connect to a HTTP server also known as a webserver. It can also trigger a
  40. CGI/ISAPI/NSAPI script and post data using either GET or POST method.
  41. Syntax of an URL: protocol://[user[:password]@]server[:port]/path
  42. Path can include data: question mark followed by URL encoded data.
  43. HTTP component is either asynchonous (non-blocking) or synchonous (blocking).
  44. Highest performance is when using asynchonous operation. This is the
  45. recommended way to use HTTP component.
  46. To request several URL simultaneously, use asynchronous operation and as much
  47. HTTP components as you wants to request URLs. All requests will be executed
  48. simultaneously without using multi-threading and without blocking your app.
  49. Methods:
  50. GetASync Asynchronous, non-blocking Get
  51. Retrieve document or file specified by URL, without blocking.
  52. OnRequestDone event trigered when finished. Use HTTP GET
  53. method (data contained in URL)
  54. PostASync Asynchronous, non-blocking Post
  55. Retrieve document or file specified by URL, without blocking.
  56. OnRequestDone event trigered when finished. Use HTTP POST
  57. method (data contained in request stream)
  58. HeadASync Asynchronous, non-blocking Head
  59. Retrieve document or file header specified by URL, without
  60. blocking. OnRequestDone event trigered when finished. Use HTTP
  61. HEAD method.
  62. Get Synchronous, blocking Get. Same as GetAsync, but blocks until
  63. finished.
  64. Post Synchronous, blocking Post. Same as PostAsync, but blocks until
  65. finished.
  66. Head Synchronous, blocking Head. Same as HeadAsync, but blocks until
  67. finished.
  68. Abort Immediately close communication.
  69. Updates:
  70. 11/29/97 RcvdStream and SendStream properties moved to public section
  71. 11/30/97 Document name bug corrected
  72. 12/02/97 Removed bug occuring with terminating slash in docname
  73. 12/03/97 Added properties RcvdCount and SentCount to easily add a progress
  74. bar feature (On receive, the ContentLength is initialized with the
  75. value from the header. Update the progress bar in the OnDocData event,
  76. or the OnSendData event).
  77. Added the OnSendBegin, OnSendData and OnSendEnd events.
  78. 12/07/97 Corrected Head function to work as expected. Thanks to
  79. R. Barry Jones <rbjones@therightside.demon.co.uk
  80. 29/12/97 V0.96 Added ModifiedSince property as following proposition made by
  81. Aw Kong Koy" <infomap@tm.net.my>.
  82. 30/12/97 V0.97 Added a Cookie property to send cookies
  83. 11/01/98 V0.98 Added WSocket read-only property which enable to access the
  84. socket component used internally. For example to close it to abort
  85. a connection.
  86. 13/01/98 V0.99 Added MultiThreaaded property to tell the component that it is
  87. working in a thread and should take care of it.
  88. 15/01/98 V1.00 Completely revised internal working to make it work properly
  89. with winsock 2. The TimeOut property is gone.
  90. Changed OnAnswerLine event to OnHeaderData to be more consistent.
  91. Replaced AnswserLine property by readonly LastResponse property.
  92. Added OnRequestDone event. Added GetAsync, PostAsync, HeadAsync
  93. asynchronous, non-blocking methods. Added Abort procedure.
  94. 16/01/98 V1.01 Corrected a bug which let some data be lost when receiving
  95. (thanks to Fulvio J. Castelli <fulvio@rocketship.com>)
  96. Added test for HTTP/1.1 response in header.
  97. 31/01/98 V1.02 Added an intermediate message posting for the OnRequestDone
  98. event. Thanks to Ed Hochman <ed@mbhsys.com> for his help.
  99. Added an intermediate PostMessage to set the component to ready state.
  100. 04/02/98 V1.03 Added some code to better handle DocName (truncating at the
  101. first question mark).
  102. 05/02/98 V1.04 Deferred login after a relocation, using WM_HTTP_LOGIN message.
  103. Added workarounf to support faulty webservers which sent only a single
  104. LF in header lines. Submitted by Alwin Hoogerdijk <alwin@lostboys.nl>
  105. 15/03/98 V1.05 Enlarge buffers from 2048 to 8192 bytes (not for D1)
  106. 01/04/98 V1.06 Adapted for BCB V3
  107. 13/04/98 V1.07 Made RcvdHeader property readonly and cleared the content at the
  108. start of a request.
  109. Protected Abort method from calling when component is ready.
  110. Ignore any exception triggered by CancelDnsLookup in Abort method.
  111. 14/04/98 V1.08 Corrected a relocation bug occuring with relative path
  112. 26/04/98 V1.09 Added OnLocationChange event
  113. 30/04/98 V1.10 Added ProxyUsername and ProxyPassword. Suggested by
  114. Myers, Mike <MikeMy@crt.com>.
  115. 26/05/98 V1.11 Corrected relocation problem when used with ASP webpages
  116. 09/07/98 V1.12 Adapted for Delphi 4
  117. Checked argument length in SendCommand
  118. 19/09/98 V1.13 Added support for HTML document without header
  119. Added OnSessionConnected event, httpConnected state and
  120. httpDnsLookupDone state.
  121. Corrected a problem with automatic relocation. The relocation
  122. message was included in data, resulting in wrong document data.
  123. Added two new events: OnRequestHeaderBegin and OnRequestHeaderEnd.
  124. They replace the OnHeaderBegin and OnHeaderEnd events that where
  125. called for both request header (to web server) and response
  126. header (from web server)
  127. 22/11/98 V1.14 Added a Location property than gives the new location in
  128. case of page relocation. Suggested by Jon Robertson <touri@pobox.com>
  129. 21/12/98 V1.15 Set ContentLength equal to -1 at start of command.
  130. 31/01/99 V1.16 Added HostName property
  131. 01/02/99 V1.17 Port was lost in DoRequestAsync when using a proxy.
  132. Thanks to David Wright <wrightd@gamespy.com> for his help.
  133. Report Dns lookup error and session connect error in OnrequestDOne
  134. event handler as suggested by Jack Olivera <jack@token.nl>.
  135. 14/03/99 V1.18 Added OnCookie event.
  136. 16/03/99 V1.19 Added Accept property.
  137. Added a default value to Agent property.
  138. Changed OnCookie event signature (not fully implemented yet !).
  139. 07/05/99 V1.20 Added code to support Content Ranges by Jon Robertson
  140. <touri@pobox.com>.
  141. 24/07/99 V1.21 Yet another change in relocation code.
  142. Aug 20, 1999 V1.22 Changed conditional compilation so that default is same
  143. as latest compiler (currently Delphi 4, Bcb 4). Should be ok for
  144. Delphi 5. Added Sleep(0) in sync wait loop to reduce CPU usage.
  145. Added DnsResult property as suggested by Heedong Lim
  146. <hdlim@dcenlp.chungbuk.ac.kr>. This property is accessible from
  147. OnStateChange when state is httpDnsLookupDone.
  148. Triggered OnDocData after writing to the stream.
  149. Sep 25, 1999 V1.23 Yet another change in relocation code when using proxy
  150. Francois Demers <fdemers@videotron.ca> found that some webserver
  151. do not insert a space after colon in header line. Corrected
  152. code to handle it correctly.
  153. Cleared ContentType before issuing request.
  154. Oct 02, 1999 V1.24 added AcceptRanges property. Thanks to Werner Lehmann
  155. <wl@bwl.uni-kiel.de>
  156. Oct 30, 1999 V1.25 change parameter in OnCommand event from const to var to
  157. allow changing header line, including deleting or adding before
  158. or after a given line sent by the component.
  159. Nov 26, 1999 V1.26 Yet another relocation fix !
  160. Jun 23, 2000 V1.27 Fixed a bug in ParseURL where hostname is followed by a '?'
  161. (that is no path but a query).
  162. Jul 22, 2000 V1.28 Handle exception during DnsLookup from the login procedure.
  163. Suggested by Robert Penz <robert.penz@outertech.com>
  164. Sep 17, 2000 V1.29 Eugene Mayevski <Mayevski@eldos.org> added support for
  165. NOFORMS.
  166. Jun 18, 2001 V1.30 Use AllocateHWnd and DeallocateHWnd from wsocket.
  167. Renamed property WSocket to CtrlSocket (this require code change
  168. in user application too).
  169. Jul 25, 2001 V1.31 Danny Heijl <Danny.Heijl@cevi.be> found that ISA proxy adds
  170. an extra space to the Content-length header so we need a trim
  171. to extract numeric value.
  172. Ran Margalit <ran@margalit.com> found some server sending
  173. empty document (Content-Length = 0) which crashed the component.
  174. Added a check for that case when header is finished.
  175. Andrew N.Silich" <silich@rambler.ru> found we need to handle
  176. handle relative path using "../" and "./" when relocating. Thanks
  177. for his code which was a good starting point.
  178. Jul 28, 2001 V1.32 Sahat Bun <sahat@operamail.com> suggested to change POST to
  179. GET when a relocation occurs.
  180. Created InternalClear procedure as suggested by Frank Plagge
  181. <frank@plagge.net>.
  182. When relocation, clear FRcvdHeader. If port not specified, then
  183. use port 80. By Alexander O.Kazachkin <kao@inreco.ru>
  184. Jul 30, 2001 V1.33 Corected a few glitches with Delphi 1
  185. Aug 18, 2001 V1.34 Corrected a bug in relocation logic: when server send only a
  186. header, with no document at all, relocation was not occuring and
  187. OnHeaderEnd event was not triggered.
  188. Corrected a bug in document name when a CGI was invoked (a '?'
  189. found in the URL). Now, ignore everything after '?' which is CGI
  190. parameter.
  191. Sep 09, 2001 V1.35 Beat Boegli <leeloo999@bluewin.ch> added LocalAddr property
  192. for multihomed hosts.
  193. Sep 29, 2001 V1.36 Alexander Alexishin <sancho@han.kherson.ua> corrected
  194. ParseUrl to handle the case where http:// is not at start of url:
  195. 'first.domain.com/cgi-bin/serv?url=http://second.domain.com'
  196. Yet another relocation code change.
  197. Oct 28, 2001 V1.37 Corrected SocketSessionClosed which called
  198. LocationSessionClosed when it was not needed.
  199. Nov 10, 2001 V1.38 Fixed a bug where component was trying to connect to proxy
  200. using default port instead on specified port after a relocation.
  201. Corrected a bug when relocating to a relative path. Current path
  202. was not taken into account !
  203. Mar 06, 2002 V1.39 Fixed a bug in relocation when content-length was 0: no
  204. relocation occured ! (Just check for relocation before checking
  205. content length empty).
  206. Mar 12, 2002 V1.40 Added UrlEncode and UrlDecode utility functions.
  207. Mar 30, 2002 V1.41 Export a few utility functions: IsDigit, IsXDigit, XDigit,
  208. htoin and htoi2.
  209. Apr 14, 2002 V1.42 Paolo S. Asioli <paolo.asioli@libero.it> found a bug in
  210. relocation code where new user/pass are specified.
  211. On relocation, change DocName according to the relocation.
  212. When DocName has no extension and ContentType is text/html the
  213. add extension .htm (could be expanded do other content type)
  214. Apr 20, 2002 V1.43 Added Socks code from Eugene Mayevski <mayevski@eldos.org>
  215. Apr 21, 2002 V1.44 In LocationSessionClosed, clear status variables from
  216. previous operation.
  217. Sep 06, 2002 V1.45 Made a few more methods virtual.
  218. Sep 10, 2002 V1.46 Added AcceptLanguage property.
  219. Sep 11, 2002 V1.47 Wilfried Mestdagh <wilfried@mestdagh.biz> added
  220. OnBeforeHeaderSend event to help add/remove/change header lines.
  221. He also corrected SocketSessionClosed to report error code.
  222. Feb 08, 2003 V1.48 Implemented more HTTP/1.1 features notably TCP session
  223. persistance.
  224. Feb 22, 2003 V1.49 Corrected a bug related to document length computation.
  225. Thanks to Dav999 <transmaster@ifrance.com> who found a
  226. reproductible case with perso.wanadoo.fr webserver.
  227. Apr 27, 2003 V1.50 OnLocationChange was not called when a relocation occured
  228. and server handle HTTP/1.1 and new location on same server.
  229. May 01, 2003 V1.51 Location and URL properties where incorrect after relocation
  230. to same HTTP/1.1 server.
  231. Change POST to GET after relocation to same HTTP/1.1 server.
  232. May 09, 2003 V1.52 Implemented PUT method
  233. May 31, 2003 V1.53 Corrected a problem with relocation when a proxy was used
  234. and a relative path was given.
  235. Aug 21, 2003 V1.54 Removed HTTPCliDeallocateHWnd virtual attribute for BCB
  236. because of a bug in BCB preventing linking any derived
  237. component with a HWND argument in a virtual method.
  238. With help from Steven S. Showers and Stanislav Korotky.
  239. Nov 26, 2003 V1.55 Implemented OnDataPush event for server push handling.
  240. Added OnSessionClosed event.
  241. Corrected ParseUrl to correctly handle protocol specified in
  242. uppercase. Thanks to "Nu Conteaza" <osfp@personal.ro>.
  243. Implemented OnDataPush2 event. Not the same as OnDataPush:
  244. OnDataPush: Need to call Receive to get data
  245. OnDataPush2: Data already received and delivered in LastResponse
  246. Dec 28, 2003 V1.56 Implemented TransferEncoding = chunked
  247. Moved code for relocation after document receive so that even in
  248. case of a relocation, any available document is preserved.
  249. Thanks to Steve Endicott <Endi@pacbell.net> for his help.
  250. Jan 09, 2004 V1.57 Fixed a relocation not done when end of document is in the
  251. same data packet as the last header line. With help of S. Endicott.
  252. Steve Endicott <Endi@pacbell.net> implemented "Connection: close"
  253. header line.
  254. Jan 12, 2004 V1.58 "Ted T?raasen" <Ted@extreme.no> added proprety
  255. FollowRelocation (default to TRUE) to have the component follow
  256. relocation or just ignore them.
  257. Jan 15, 2004 V1.59 Set FRcvdCount to zero in StartRelocation (S. Endicott).
  258. Started to implement NTLM authentication. Doesn't work yet !
  259. Jan 26, 2004 V1.60 Reordered uses clause for FPC compatibility.
  260. Feb 16, 2004 V1.61 Fixed GetHeaderLineNext to start relocation at the right
  261. moment. See annotation "16/02/2004".
  262. Mar 12, 2004 Fixed GetHeaderLineNext to check for StatusCode < 200, 204 and
  263. 304 in order to not wait for body.
  264. Thanks to Csonka Tibor <bee@rawbite.ro> for finding a test case.
  265. Jul 12, 2004 Just this warning: The component now doesn't consider 401 status
  266. as a fatal error (no exception is triggered). This required a
  267. change in the application code if it was using the exception that
  268. is no more triggered for status 401.
  269. Jul 18, 2004 V1.63 Use CompareText to check for http string is relocation
  270. header. Thanks to Roger Tinembart <tinembart@brain.ch>
  271. Jul 23, 2004 V1.64 Fixed a line too long exception when requesting HEAD or URL
  272. http://de.news.yahoo.com:80/. The server was sending a document
  273. even after we requested just the header. The fix make the
  274. component ignore data and abort the connection. This is really an
  275. error at server side !
  276. Aug 08, 2004 V1.65 Moved utility function related to URL handling into IcsUrl
  277. unit for easy reuse outside of the component.
  278. Aug 20, 2004 V1.66 Use MsgWaitForMultipleObjects in DoRequestSync to avoid
  279. consumming 100% CPU while waiting.
  280. Sep 04, 2004 V1.67 Csonka Tibor <bee@rawbite.ro> worked a lot on my NTLM code,
  281. fixing it and making it work properly.
  282. I removed NTLM specific usercode and password properties to use
  283. FUsername and FPassword which are extracted from the URL.
  284. Define symbol UseNTLMAuthentication for Delphi 5 and up.
  285. Sep 13, 2004 V1.68 Added option httpoNoNTLMAuth by Csonka Tibor
  286. Fixed TriggerRequestDone for NTLM authentication
  287. Moved NTLM code out of DoBeforeConnect which was intended for
  288. socket setup and not for protocol handling.
  289. Oct 02, 2004 V1.69 Removed second copy of IntToStrDef.
  290. Oct 06, 2004 V1.70 Miha Remec fixed THttpCli.GetHeaderLineNext to add
  291. status check for 301 and 302 values.
  292. Oct 15, 2004 V1.71 Lotauro.Maurizio@dnet.it enhanced basic and NTLM
  293. authentifications methods. Event OnNTLMAuthStep has been
  294. removed. Now basic authentication is not automatically sent with
  295. a request. It is only sent when the server request it by replying
  296. with a 401 or 407 response. Sending basic authentication in the
  297. first request was a kind of security threat for NTLM:
  298. usercode/password is sent unencrypted while NTLM is made to send
  299. it encrypted (DES). This has the side effect of requiring two
  300. request where one was needed. This could be a problem when posting
  301. data: data has to be posted twice ! This is transparent to the user
  302. except for performance :-( A future enhancement could be a new
  303. option to always send basic authentication.
  304. Oct 30, 2004 V1.72 Made SendRequest virtual.
  305. Nov 07, 2004 V1.73 Added CleanupRcvdStream. Lotauro.Maurizio@dnet.it found that
  306. document must be cleaned if received in intermediate authentication
  307. steps.
  308. Nov 09, 2004 V1.74 Cleared FDoAuthor from InternalClear. Thanks Maurizio.
  309. Nov 11, 2004 V1.75 Added CleanupRcvdStream when starting relocation.
  310. Thanks Maurizio.
  311. Removed second TriggerHeaderEnd in GetHeaderLineNext.
  312. Thanks Ronny Karl for finding this one.
  313. Nov 20, 2004 V1.76 Angus Robertson found a problem with SendStream because of
  314. authentication (post restarted because authentication need to be
  315. done).
  316. Maurizio fixed the issue above an a fix others:
  317. - added a CleanupSendStream procedure, and added a call to it in
  318. every place where the CleanupRcvdStream is called.
  319. - changed the Content-Length calculation: if the position of the
  320. stream is not 0 then the length was wrong
  321. - changed the the test in DoRequestAsync: if the position of the
  322. stream is at the end then it will send nothing
  323. Nov 22, 2004 V1.77 Only a single error code for httperrInvalidAuthState.
  324. Dec 14, 2004 V1.78 Excluded code 407 and added code 400 in DoRequestSync
  325. Dec 19, 2004 V1.79 Revised CleanupRcvdStream to make it compatible with D1.
  326. Dec 22, 2004 V1.80 Changed SocketDataAvailable so that header lines that are
  327. too long to fit into the receive buffer (8K) are simply truncated
  328. instead of triggering an exception.
  329. Jan 05, 2005 V1.81 Maurizio Lotauro <Lotauro.Maurizio@dnet.it> optimized NTLM
  330. authentication by not sending content in the first step.
  331. Jan 29, 2005 V1.82 Fixed socks properties propagation to control socket.
  332. Feb 05, 2005 V1.83 Fixed GetHeaderLineNext in the case Abort is called from
  333. OnHeaderEnd event (Bug reported by Csonka Tibor).
  334. Fixed relocation to https destination when USE_SSL is not
  335. defined. It is handled as a non implemented protocol.
  336. Mar 19, 2005 V1.84 Changed CleanupRcvdStream to check for COMPILER3_UP instead
  337. of checking DELPHI3_UP (BCB compatibility issue).
  338. Changed StrToIntDef to check for COMPILER5_UP instead of
  339. DELPHI5_UP (BCB compatibility issue). Thanks to Albert Wiersch.
  340. Mar 21, 2005 V1.85 Added port in "host:" header line as suggested by Sulimov
  341. Valery <99valera99@rambler.ru>.
  342. In DoRequestAsync, allow to continue even with no data to be sent.
  343. Apr 14, 2005 V1.86 Fixed PrepareBasicAuth to ignore charcase when checking for
  344. 'basic' keyword in header line. Thanks to abloms@yahoo.com for
  345. finding this bug which affected use thru iPlanet Web Proxy Server.
  346. Apr 16, 2005 V1.87 Applyed the fix above to PrepareNTLMAuth (two places),
  347. StartAuthNTLM and StartProxyAuthNTLM.
  348. May 26, 2005 V1.88 Fixed DoRequestAsync to set http as default protocol.
  349. Aug 15, 2005 V1.89 Implemented bandwidth control for download (Get).
  350. Oct 31, 2005 V1.89a rework of authentication handling to fix a problem that could
  351. happen when both proxy and remote server needed an authentication
  352. causing an infinite loop, by Maurizio Lotauro
  353. - removed the TXXXAuthType type and the variables of that types
  354. - properties Username and Password will no more replaced by the
  355. credential contained in the URL. Two new variables are introduced
  356. FCurrUsername and FCurrPassword. These will contain the credential
  357. in the URL (if specified), otherwise the content of the property.
  358. - same logic for Connection and ProxyConnection (introduced
  359. FCurrConnection and FCurrProxyConnection)
  360. Nov 19, 2005 V1.89b supports SSL v5 by Arno Garrels
  361. Nov 27, 2005 V1.90 implemented redirection limiting to avoid continuous
  362. looping to same URL, by Angus Robertson, Magenta Systems
  363. test example is http://www.callserve.com/ that keeps looping
  364. Implemented ContentEncoding (can be disabled with define
  365. UseContentCoding), mostly done in new HttpContCod.pas,
  366. add HttpCCodzlib to your project for GZIP compression support,
  367. and set httpoEnableContentCoding in Options to enable it
  368. by Maurizio Lotauro <Lotauro.Maurizio@dnet.it>
  369. Dec 20, 2005 V1.91 new configurable DebugOptions to replace IFDEF DEBUG_OUTPUT,
  370. see wsocket for more information
  371. Apr 10, 2006 V6.00.1 Added LowerCase for FTransferEncoding? Thanks to Fastream.
  372. Dec 10, 2006 V6.00.2 Jack <jlist9@gmail.com> fixed BandwidthTimerTimer to clear
  373. the count.
  374. Mar 17, 2007 V6.00.3 Introduced THttpBigInt to support documents longer
  375. than 2GB.
  376. Mar 19, 2007 V6.00.3 A.Garrels fixed a memory leak of FSendBuffer and
  377. FReceiveBuffer. Check for negative value of FReceiveLen in
  378. SocketDataAvailable.
  379. May 27, 2008 V6.00.4 A.Garrels Workaround in GetHeaderLineNext. Ignore body data
  380. sent in the HEAD response by buggy servers.
  381. Jun 25, 2008 V6.00.5 A. Garrels SSL code merged.
  382. Jul 17, 2008 V6.00.6 A. Garrels made a few changes to prepare code for Unicode,
  383. added OverbyteIcsUtils to the uses clause, optimized
  384. MoveTBytesToString a bit, removed some stuff for older compilers,
  385. removed typedef of TBytes, now uses TBytes of OverbyteIcsTypes.pas.
  386. Sep 28, 2008 V6.00.7 Maurizio Lotauro fixed a bug with premature received
  387. 401/407 responses while data was still being sent with POST and PUT
  388. requests. A. Garrels small fix in SendCommand().
  389. Sep 29, 2008 V6.00.8 A. Garrels added OverbyteIcsUtils to the uses clause
  390. for all compilers.
  391. Dec 06, 2008 V7.00.9 A. Garrels fixed function EncodeStr and EncodeLine.
  392. Jan 11, 2009 V7.01 A. Garrels fixed a bug with proxies which do not
  393. send a Content-Length header in non-persistent connections by
  394. simply always setting FReceiveLen to zero in SocketSessionClosed.
  395. Jan 11, 2009 V7.02 A. Garrels - Added Digest Access Authentication.
  396. In order to disable this feature entirely undefine directive
  397. UseDigestAuthentication below.
  398. Added new event OnBeforeAuth that can be used to skip internal
  399. authorization by setting argument Allow to FALSE.
  400. ** Also cleaned up the source code a bit, thus a comparison
  401. with previous version, unfortunately won't be fun. **
  402. Jan 12, 2009 V7.02a Arno added two missing lines for digest auth to the SSL code.
  403. Jan 22, 2009 V7.02b Sorry guys! Re-added property OnBeforeHeaderSend again
  404. which I (Arno) removed in V7.02 accidently :(
  405. Jan 22, 2009 V7.02c Arno - Conditional define UseDigestAuthentication was not
  406. set properly in THttpCli.StateChange.
  407. Apr 25, 2009 V7.03 Steve Endicott fixed a relocation bug with HTTPS.
  408. Sep 17, 2009 V7.04 Arno added property Timeout, works only with synchronous
  409. methods!
  410. Dec 02, 2009 V7.05 Bjornar found a HTTPS POST bug with proxy basic
  411. authentication that added two Content-Length header lines.
  412. Feb 15, 2010 V7.06 Yuri Semenov fixed a bug with content coding and chunked
  413. transfer encoding.
  414. Feb 25, 2010 V7.07 Fix by Bj?rnar Nielsen: TSslHttpCli didn't work when used
  415. against Websense-Content_Gateway (http://www.websense.com) and
  416. some others. The problem was that this (and some other proxies too)
  417. answer 200 OK to notify client that connection to remote server
  418. is established. Usually proxies use 200 OK and an error text when
  419. something is wrong. In that case Content-Length is not 0.
  420. May 24, 2010 V7.08 Angus ensure Ready when relocations exceed maximum to avoid timeout
  421. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  422. unit OverbyteIcsHttpProt;
  423. interface
  424. {$I OverbyteIcsDefs.inc}
  425. {$IFDEF COMPILER14_UP}
  426. {$IFDEF NO_EXTENDED_RTTI}
  427. {$RTTI EXPLICIT METHODS([]) FIELDS([]) PROPERTIES([])}
  428. {$ENDIF}
  429. {$ENDIF}
  430. {$IFNDEF COMPILER7_UP}
  431. 'Sorry, we do not want to support ancient compilers any longer'
  432. {$ENDIF}
  433. {$IFDEF DELPHI6_UP}
  434. {$WARN SYMBOL_PLATFORM OFF}
  435. {$WARN SYMBOL_LIBRARY OFF}
  436. {$WARN SYMBOL_DEPRECATED OFF}
  437. {$ENDIF}
  438. {$B-} { Enable partial boolean evaluation }
  439. {$T-} { Untyped pointers }
  440. {$X+} { Enable extended syntax }
  441. {$H+} { Use long strings }
  442. {$J+} { Allow typed constant to be modified }
  443. {$IFDEF BCB3_UP}
  444. {$ObjExportAll On}
  445. {$ENDIF}
  446. {$IFNDEF NO_ADVANCED_HTTP_CLIENT_FEATURES}
  447. {$DEFINE UseNTLMAuthentication}
  448. {$DEFINE UseDigestAuthentication}
  449. {$DEFINE UseBandwidthControl}
  450. {$DEFINE UseContentCoding}
  451. {$ENDIF}
  452. {$IFDEF CLR}
  453. {$UNDEF UseNTLMAuthentication}
  454. {$UNDEF UseDigestAuthentication}
  455. {$UNDEF UseContentCoding}
  456. {$ENDIF}
  457. uses
  458. Messages,
  459. {$IFDEF USEWINDOWS}
  460. Windows,
  461. {$ELSE}
  462. WinTypes, WinProcs,
  463. {$ENDIF}
  464. {$IFDEF CLR}
  465. System.Text, System.IO, System.Threading,
  466. {$ENDIF}
  467. SysUtils, Classes,
  468. {$IFNDEF NOFORMS}
  469. Forms, Controls,
  470. {$ENDIF}
  471. { You must define USE_SSL so that SSL code is included in the component. }
  472. { Either in OverbyteIcsDefs.inc or in the project/package options. }
  473. {$IFDEF USE_SSL}
  474. OverbyteIcsSSLEAY, OverbyteIcsLIBEAY,
  475. {$ENDIF}
  476. {$IFDEF UseNTLMAuthentication}
  477. OverbyteIcsNtlmMsgs,
  478. {$ENDIF}
  479. {$IFDEF UseContentCoding}
  480. OverbyteIcsHttpContCod,
  481. {$ENDIF}
  482. {$IFNDEF NO_DEBUG_LOG}
  483. OverbyteIcsLogger,
  484. {$ENDIF}
  485. OverbyteIcsUrl, OverbyteIcsTypes,
  486. OverbyteIcsUtils,
  487. {$IFDEF UseDigestAuthentication}
  488. OverbyteIcsDigestAuth,
  489. {$ENDIF}
  490. OverbyteIcsWinSock, OverbyteIcsWndControl, OverbyteIcsWSocket;
  491. const
  492. HttpCliVersion = 708;
  493. CopyRight : String = ' THttpCli (c) 1997-2010 F. Piette V7.08 ';
  494. DefaultProxyPort = '80';
  495. HTTP_RCV_BUF_SIZE = 8193;
  496. HTTP_SND_BUF_SIZE = 8193;
  497. { EHttpException error code }
  498. httperrNoError = 0;
  499. httperrBusy = 1;
  500. httperrNoData = 2;
  501. httperrAborted = 3;
  502. httperrOverflow = 4;
  503. httperrVersion = 5;
  504. httperrInvalidAuthState = 6;
  505. httperrSslHandShake = 7;
  506. httperrCustomTimeOut = 8; { V7.04 }
  507. type
  508. THttpBigInt = Int64;
  509. EHttpException = class(Exception)
  510. ErrorCode : Word;
  511. constructor Create(const Msg : String; ErrCode : Word);
  512. end;
  513. // TBytes = array of Byte;
  514. THttpEncoding = (encUUEncode, encBase64, encMime);
  515. THttpRequest = (httpABORT, httpGET, httpPOST, httpPUT,
  516. httpHEAD, httpCLOSE);
  517. THttpState = (httpReady, httpNotConnected, httpConnected,
  518. httpDnsLookup, httpDnsLookupDone,
  519. httpWaitingHeader, httpWaitingBody, httpBodyReceived,
  520. httpWaitingProxyConnect,
  521. httpClosing, httpAborting);
  522. THttpChunkState = (httpChunkGetSize, httpChunkGetExt, httpChunkGetData,
  523. httpChunkSkipDataEnd, httpChunkDone);
  524. {$IFDEF UseNTLMAuthentication}
  525. THttpNTLMState = (ntlmNone, ntlmMsg1, ntlmMsg2, ntlmMsg3, ntlmDone);
  526. {$ENDIF}
  527. {$IFDEF UseDigestAuthentication}
  528. TAuthDigestInfo = TAuthDigestResponseInfo;
  529. THttpDigestState = (digestNone, digestMsg1, digestDone);
  530. {$ENDIF}
  531. THttpBasicState = (basicNone, basicMsg1, basicDone);
  532. THttpAuthType = (httpAuthNone, httpAuthBasic, httpAuthNtlm, httpAuthDigest);
  533. THttpBeforeAuthEvent = procedure(Sender : TObject;
  534. AuthType : THttpAuthType;
  535. ProxyAuth : Boolean;
  536. const AuthHdr : String;
  537. var Allow : Boolean) of object;
  538. TOnCommand = procedure (Sender : TObject;
  539. var S: String) of object;
  540. TDocDataEvent = procedure (Sender : TObject;
  541. {$IFDEF CLR}
  542. var Buffer : TBytes;
  543. Offset : Integer;
  544. {$ELSE}
  545. Buffer : Pointer;
  546. {$ENDIF}
  547. Len : Integer) of object;
  548. TCookieRcvdEvent = procedure (Sender : TObject;
  549. const Data : String;
  550. var Accept : Boolean) of object;
  551. THttpRequestDone = procedure (Sender : TObject;
  552. RqType : THttpRequest;
  553. ErrCode : Word) of object;
  554. TBeforeHeaderSendEvent = procedure (Sender : TObject;
  555. const Method : String;
  556. Headers : TStrings) of object;
  557. THttpCliOption = (httpoNoBasicAuth, httpoNoNTLMAuth, httpoBandwidthControl,
  558. {$IFDEF UseContentCoding}
  559. httpoEnableContentCoding, httpoUseQuality,
  560. {$ENDIF}
  561. httpoNoDigestAuth);
  562. THttpCliOptions = set of THttpCliOption;
  563. TLocationChangeExceeded = procedure (Sender : TObject;
  564. const RelocationCount : Integer;
  565. var AllowMoreRelocations : Boolean) of object; { V1.90 }
  566. THttpCli = class(TIcsWndControl)
  567. protected
  568. FMsg_WM_HTTP_REQUEST_DONE : UINT;
  569. FMsg_WM_HTTP_SET_READY : UINT;
  570. FMsg_WM_HTTP_LOGIN : UINT;
  571. FCtrlSocket : TWSocket;
  572. //FWindowHandle : HWND;
  573. FMultiThreaded : Boolean;
  574. FState : THttpState;
  575. FLocalAddr : String;
  576. FHostName : String;
  577. FTargetHost : String;
  578. FTargetPort : String;
  579. FPort : String;
  580. FProtocol : String;
  581. FProxy : String;
  582. FProxyPort : String;
  583. FUsername : String;
  584. FPassword : String;
  585. FCurrUsername : String;
  586. FCurrPassword : String;
  587. FProxyUsername : String;
  588. FProxyPassword : String;
  589. FProxyConnected : Boolean;
  590. FLocation : String;
  591. FCurrentHost : String;
  592. FCurrentPort : String;
  593. FCurrentProtocol : String;
  594. FConnected : Boolean;
  595. FDnsResult : String;
  596. // FSendBuffer : array [0..HTTP_SND_BUF_SIZE - 1] of char;
  597. FSendBuffer : TBytes; // FP 09/09/06
  598. FRequestType : THttpRequest;
  599. // FReceiveBuffer : array [0..HTTP_RCV_BUF_SIZE - 1] of char;
  600. FReceiveBuffer : TBytes; // FP 09/09/06
  601. FReceiveLen : Integer;
  602. FLastResponse : String;
  603. FHeaderLineCount : Integer;
  604. FBodyLineCount : Integer;
  605. FAllowedToSend : Boolean;
  606. FDelaySetReady : Boolean; { 09/26/08 ML }
  607. FURL : String;
  608. FPath : String;
  609. FDocName : String;
  610. FSender : String;
  611. FReference : String;
  612. FConnection : String; { for Keep-alive }
  613. FProxyConnection : String; { for proxy keep-alive }
  614. FCurrConnection : String;
  615. FCurrProxyConnection : String;
  616. FAgent : String;
  617. FAccept : String;
  618. FAcceptLanguage : String;
  619. FModifiedSince : TDateTime; { Warning ! Use GMT date/Time }
  620. FNoCache : Boolean;
  621. FStatusCode : Integer;
  622. FReasonPhrase : String;
  623. FResponseVer : String;
  624. FRequestVer : String;
  625. FContentLength : THttpBigInt;
  626. FContentType : String;
  627. FTransferEncoding : String;
  628. {$IFDEF UseContentCoding}
  629. FContentEncoding : String;
  630. FContentCodingHnd : THttpContCodHandler;
  631. FRcvdStreamStartSize : Integer;
  632. {$ENDIF}
  633. FChunkLength : Integer;
  634. FChunkRcvd : Integer;
  635. FChunkState : THttpChunkState;
  636. FDoAuthor : TStringList;
  637. FContentPost : String; { Also used for PUT }
  638. FContentRangeBegin : String;
  639. FContentRangeEnd : String;
  640. FAcceptRanges : String;
  641. FCookie : String;
  642. FLocationFlag : Boolean;
  643. FFollowRelocation : Boolean; {TED}
  644. FHeaderEndFlag : Boolean;
  645. FRcvdHeader : TStrings;
  646. FRcvdStream : TStream; { If assigned, will recv the answer }
  647. FRcvdCount : THttpBigInt; { Number of rcvd bytes for the body }
  648. FSentCount : THttpBigInt;
  649. FSendStream : TStream; { Contains the data to send }
  650. FReqStream : TMemoryStream;
  651. FRequestDoneError : Integer;
  652. FNext : procedure of object;
  653. // FBodyData : PChar;
  654. FBodyData : Integer; // Offset in FReceiveBuffer (FP 09/09/06)
  655. FBodyDataLen : THttpBigInt;
  656. FOptions : THttpCliOptions;
  657. FSocksServer : String;
  658. FSocksLevel : String;
  659. FSocksPort : String;
  660. FSocksUsercode : String;
  661. FSocksPassword : String;
  662. FSocksAuthentication : TSocksAuthentication;
  663. {$IFDEF UseNTLMAuthentication}
  664. FNTLMMsg2Info : TNTLM_Msg2_Info;
  665. FProxyNTLMMsg2Info : TNTLM_Msg2_Info;
  666. FAuthNTLMState : THttpNTLMState;
  667. FProxyAuthNTLMState : THttpNTLMState;
  668. {$ENDIF}
  669. {$IFDEF UseDigestAuthentication}
  670. FAuthDigestState : THttpDigestState;
  671. FProxyAuthDigestState : THttpDigestState;
  672. FAuthDigestInfo : TAuthDigestInfo;
  673. FAuthDigestProxyInfo : TAuthDigestInfo;
  674. { As specified in RFC 2617, section 3.2.2.4, used only with auth-int }
  675. FAuthDigestEntityHash : THashHex;
  676. {$ENDIF}
  677. FOnBeforeAuth : THttpBeforeAuthEvent;
  678. FAuthBasicState : THttpBasicState;
  679. FProxyAuthBasicState : THttpBasicState;
  680. //FServerAuth : String;
  681. //FProxyAuth : String;
  682. FServerAuth : THttpAuthType;
  683. FProxyAuth : THttpAuthType;
  684. {$IFDEF UseBandwidthControl}
  685. FBandwidthLimit : Integer; // Bytes per second
  686. FBandwidthSampling : Integer; // mS sampling interval
  687. FBandwidthCount : Int64; // Byte counter
  688. FBandwidthMaxCount : Int64; // Bytes during sampling period
  689. FBandwidthTimer : TIcsTimer;
  690. FBandwidthPaused : Boolean;
  691. FTimerOldEnabled : Boolean;
  692. {$ENDIF}
  693. FOnStateChange : TNotifyEvent;
  694. FOnSessionConnected : TNotifyEvent;
  695. FOnSessionClosed : TNotifyEvent;
  696. FOnRequestHeaderBegin : TNotifyEvent;
  697. FOnRequestHeaderEnd : TNotifyEvent;
  698. FOnHeaderBegin : TNotifyEvent;
  699. FOnHeaderEnd : TNotifyEvent;
  700. FOnHeaderData : TNotifyEvent;
  701. FOnDocBegin : TNotifyEvent;
  702. FOnDocEnd : TNotifyEvent;
  703. FOnDocData : TDocDataEvent;
  704. FOnSendBegin : TNotifyEvent;
  705. FOnSendEnd : TNotifyEvent;
  706. FOnSendData : TDocDataEvent;
  707. FOnTrace : TNotifyEvent;
  708. FOnCommand : TOnCommand;
  709. FOnCookie : TCookieRcvdEvent;
  710. FOnDataPush : TDataAvailable;
  711. FOnDataPush2 : TNotifyEvent;
  712. FOnRequestDone : THttpRequestDone;
  713. FOnLocationChange : TNotifyEvent;
  714. FLocationChangeMaxCount : Integer; { V1.90 }
  715. FLocationChangeCurCount : Integer; { V1.90 }
  716. FOnLocationChangeExceeded : TLocationChangeExceeded; { V1.90 }
  717. { Added by Eugene Mayevski }
  718. FOnSocksConnected : TSessionConnected;
  719. FOnSocksAuthState : TSocksAuthStateEvent;
  720. FOnSocksError : TSocksErrorEvent;
  721. FOnSocketError : TNotifyEvent;
  722. FOnBeforeHeaderSend : TBeforeHeaderSendEvent; { Wilfried 9 sep 02}
  723. FCloseReq : Boolean; { SAE 01/06/04 }
  724. FTimeout : UINT; { V7.04 } { Sync Timeout Seconds }
  725. procedure AllocateMsgHandlers; override;
  726. procedure FreeMsgHandlers; override;
  727. function MsgHandlersCount: Integer; override;
  728. procedure CheckDelaySetReady; { 09/26/08 ML }
  729. {$IFNDEF NO_DEBUG_LOG}
  730. function GetIcsLogger: TIcsLogger; { V1.91 }
  731. procedure SetIcsLogger(const Value: TIcsLogger); { V1.91 }
  732. procedure DebugLog(LogOption: TLogOption; const Msg : string); virtual; { V1.91 }
  733. function CheckLogOptions(const LogOption: TLogOption): Boolean; virtual; { V1.91 }
  734. {$ENDIF}
  735. {$IFDEF UseBandwidthControl}
  736. procedure BandwidthTimerTimer(Sender : TObject);
  737. {$ENDIF}
  738. procedure CreateSocket; virtual;
  739. procedure DoBeforeConnect; virtual;
  740. procedure DoSocksConnected(Sender: TObject; ErrCode: Word);
  741. procedure DoSocksAuthState(Sender : TObject; AuthState : TSocksAuthState);
  742. procedure DoSocksError(Sender : TObject; ErrCode : Integer; Msg : String);
  743. procedure SocketErrorTransfer(Sender : TObject);
  744. procedure SendRequest(const method, Version: String); virtual;
  745. procedure GetHeaderLineNext; virtual;
  746. procedure GetBodyLineNext; virtual;
  747. procedure SendCommand(const Cmd : String); virtual;
  748. procedure Login; virtual;
  749. procedure Logout; virtual;
  750. procedure InternalClear; virtual;
  751. procedure StartRelocation; virtual;
  752. {$IFDEF UseNTLMAuthentication}
  753. procedure StartAuthNTLM; virtual;
  754. procedure StartProxyAuthNTLM; virtual; {BLD proxy NTLM support }
  755. function GetNTLMMessage1(const ForProxy: Boolean) : String;
  756. function GetNTLMMessage3(const HttpMethod: String;
  757. const ForProxy: Boolean): String;
  758. procedure ElaborateNTLMAuth;
  759. function PrepareNTLMAuth(var FlgClean : Boolean) : Boolean;
  760. {$ENDIF}
  761. {$IFDEF UseDigestAuthentication}
  762. procedure ElaborateDigestAuth;
  763. function GetDigestAuthorizationHeader(const HttpMethod: String;
  764. ProxyAuth : Boolean): String;
  765. function PrepareDigestAuth(var FlgClean : Boolean) : Boolean;
  766. procedure StartAuthDigest; virtual;
  767. procedure StartProxyAuthDigest; virtual;
  768. {$ENDIF}
  769. function GetBasicAuthorizationHeader(
  770. const HttpMethod: String; ProxyAuth: Boolean): String;
  771. procedure CleanupRcvdStream;
  772. procedure CleanupSendStream;
  773. procedure StartAuthBasic; virtual;
  774. procedure StartProxyAuthBasic; virtual;
  775. procedure ElaborateBasicAuth;
  776. function PrepareBasicAuth(var FlgClean : Boolean) : Boolean;
  777. procedure SocketDNSLookupDone(Sender: TObject; ErrCode: Word); virtual;
  778. procedure SocketSessionClosed(Sender: TObject; ErrCode: Word); virtual;
  779. procedure SocketSessionConnected(Sender : TObject; ErrCode : Word); virtual;
  780. procedure SocketDataSent(Sender : TObject; ErrCode : Word); virtual;
  781. procedure SocketDataAvailable(Sender: TObject; ErrCode: Word); virtual;
  782. function StartsWithText(Source : TBytes; Find : PAnsiChar) : Boolean; {Bjornar}
  783. function ContainsText(Source : TBytes; Find : PAnsiChar) : Boolean; {Bjornar}
  784. procedure LocationSessionClosed(Sender: TObject; ErrCode: Word); virtual;
  785. procedure DoRequestAsync(Rq : THttpRequest); virtual;
  786. procedure DoRequestSync(Rq : THttpRequest); virtual;
  787. procedure SetMultiThreaded(newValue : Boolean); virtual;
  788. procedure StateChange(NewState : THttpState); virtual;
  789. procedure TriggerStateChange; virtual;
  790. procedure TriggerCookie(const Data : String;
  791. var bAccept : Boolean); virtual;
  792. procedure TriggerSessionConnected; virtual;
  793. procedure TriggerSessionClosed; virtual;
  794. procedure TriggerBeforeHeaderSend(const Method : String;
  795. Headers : TStrings); virtual;
  796. procedure TriggerRequestHeaderBegin; virtual;
  797. procedure TriggerRequestHeaderEnd; virtual;
  798. procedure TriggerHeaderBegin; virtual;
  799. procedure TriggerHeaderEnd; virtual;
  800. procedure TriggerDocBegin; virtual;
  801. procedure TriggerDocData(
  802. {$IFDEF CLR}
  803. var Data : TBytes;
  804. Offset : Integer;
  805. {$ELSE}
  806. Data : Pointer;
  807. {$ENDIF}
  808. Len : Integer); virtual;
  809. procedure TriggerDocEnd; virtual;
  810. procedure TriggerSendBegin; virtual;
  811. procedure TriggerSendData(
  812. {$IFDEF CLR}
  813. var Data : TBytes;
  814. Offset : Integer;
  815. {$ELSE}
  816. Data : Pointer;
  817. {$ENDIF}
  818. Len : Integer); virtual;
  819. procedure TriggerSendEnd; virtual;
  820. procedure TriggerRequestDone; virtual;
  821. procedure WndProc(var MsgRec: OverbyteIcsTypes.TMessage); override;
  822. procedure SetReady; virtual;
  823. procedure AdjustDocName; virtual;
  824. procedure SetRequestVer(const Ver : String);
  825. procedure WMHttpRequestDone(var msg: TMessage);
  826. procedure WMHttpSetReady(var msg: TMessage);
  827. procedure WMHttpLogin(var msg: TMessage);
  828. {$IFDEF UseContentCoding}
  829. function GetOptions: THttpCliOptions;
  830. procedure SetOptions(const Value : THttpCliOptions);
  831. {$ENDIF}
  832. {$IFDEF USE_SSL}
  833. procedure SslHandshakeDone(Sender : TObject;
  834. ErrCode : Word;
  835. PeerCert : TX509Base;
  836. var Disconnect : Boolean);
  837. {$ENDIF}
  838. public
  839. constructor Create(Aowner:TComponent); override;
  840. destructor Destroy; override;
  841. procedure Get; { Synchronous blocking Get }
  842. procedure Post; { Synchronous blocking Post }
  843. procedure Put; { Synchronous blocking Put }
  844. procedure Head; { Synchronous blocking Head }
  845. procedure Close; { Synchronous blocking Close }
  846. procedure Abort; { Synchrounous blocking Abort }
  847. procedure GetASync; { Asynchronous, non-blocking Get }
  848. procedure PostASync; { Asynchronous, non-blocking Post }
  849. procedure PutASync; { Asynchronous, non-blocking Put }
  850. procedure HeadASync; { Asynchronous, non-blocking Head }
  851. procedure CloseAsync; { Asynchronous, non-blocking Close }
  852. procedure ThreadAttach; override;
  853. procedure ThreadDetach; override;
  854. property CtrlSocket : TWSocket read FCtrlSocket;
  855. //property Handle : HWND read FWindowHandle;
  856. property State : THttpState read FState;
  857. property LastResponse : String read FLastResponse;
  858. property ContentLength : THttpBigInt read FContentLength;
  859. property ContentType : String read FContentType;
  860. property TransferEncoding : String read FTransferEncoding;
  861. {$IFDEF UseContentCoding}
  862. property ContentEncoding : String read FContentEncoding;
  863. property ContentCodingHnd : THttpContCodHandler read FContentCodingHnd;
  864. {$ENDIF}
  865. property RcvdCount : THttpBigInt read FRcvdCount;
  866. property SentCount : THttpBigInt read FSentCount;
  867. property StatusCode : Integer read FStatusCode;
  868. property ReasonPhrase : String read FReasonPhrase;
  869. property DnsResult : String read FDnsResult;
  870. property AuthorizationRequest : TStringList read FDoAuthor;
  871. property DocName : String read FDocName;
  872. property Location : String read FLocation
  873. write FLocation;
  874. property RcvdStream : TStream read FRcvdStream
  875. write FRcvdStream;
  876. property SendStream : TStream read FSendStream
  877. write FSendStream;
  878. property RcvdHeader : TStrings read FRcvdHeader;
  879. property Hostname : String read FHostname;
  880. property Protocol : String read FProtocol;
  881. {$IFDEF UseDigestAuthentication}
  882. property AuthDigestInfo : TAuthDigestInfo
  883. read FAuthDigestInfo
  884. write FAuthDigestInfo;
  885. property AuthDigestProxyInfo : TAuthDigestInfo
  886. read FAuthDigestProxyInfo
  887. write FAuthDigestProxyInfo;
  888. property AuthDigestEntityHash : THashHex read FAuthDigestEntityHash
  889. write FAuthDigestEntityHash;
  890. {$ENDIF}
  891. published
  892. property URL : String read FURL
  893. write FURL;
  894. property LocalAddr : String read FLocalAddr {bb}
  895. write FLocalAddr; {bb}
  896. property Proxy : String read FProxy
  897. write FProxy;
  898. property ProxyPort : String read FProxyPort
  899. write FProxyPort;
  900. property Sender : String read FSender
  901. write FSender;
  902. property Agent : String read FAgent
  903. write FAgent;
  904. property Accept : String read FAccept
  905. write FAccept;
  906. property AcceptLanguage : String read FAcceptLanguage
  907. write FAcceptLanguage;
  908. property Reference : String read FReference
  909. write FReference;
  910. property Connection : String read FConnection
  911. write FConnection;
  912. property ProxyConnection : String read FProxyConnection
  913. write FProxyConnection;
  914. property Username : String read FUsername
  915. write FUsername;
  916. property Password : String read FPassword
  917. write FPassword;
  918. property ProxyUsername : String read FProxyUsername
  919. write FProxyUsername;
  920. property ProxyPassword : String read FProxyPassword
  921. write FProxyPassword;
  922. property NoCache : Boolean read FNoCache
  923. write FNoCache;
  924. property ModifiedSince : TDateTime read FModifiedSince
  925. write FModifiedSince;
  926. property Cookie : String read FCookie
  927. write FCookie;
  928. property ContentTypePost : String read FContentPost
  929. write FContentPost;
  930. property ContentRangeBegin: String read FContentRangeBegin {JMR!! Added this line!!!}
  931. write FContentRangeBegin; {JMR!! Added this line!!!}
  932. property ContentRangeEnd : String read FContentRangeEnd {JMR!! Added this line!!!}
  933. write FContentRangeEnd; {JMR!! Added this line!!!}
  934. property AcceptRanges : String read FAcceptRanges;
  935. property MultiThreaded : Boolean read FMultiThreaded
  936. write SetMultiThreaded;
  937. property RequestVer : String read FRequestVer
  938. write SetRequestVer;
  939. property FollowRelocation : Boolean read FFollowRelocation {TED}
  940. write FFollowRelocation; {TED}
  941. property LocationChangeMaxCount: integer read FLocationChangeMaxCount { V1.90 }
  942. write FLocationChangeMaxCount ;
  943. property LocationChangeCurCount: integer read FLocationChangeCurCount ; { V1.90 }
  944. property OnLocationChangeExceeded: TLocationChangeExceeded
  945. read FOnLocationChangeExceeded { V1.90 }
  946. write FOnLocationChangeExceeded ;
  947. { ServerAuth and ProxyAuth properties are still experimental. They are likely
  948. to change in the future. If you use them now, be prepared to update your
  949. code later }
  950. property ServerAuth : THttpAuthType read FServerAuth
  951. write FServerAuth;
  952. property ProxyAuth : THttpAuthType read FProxyAuth
  953. write FProxyAuth;
  954. {$IFDEF UseBandwidthControl}
  955. property BandwidthLimit : Integer read FBandwidthLimit
  956. write FBandwidthLimit;
  957. property BandwidthSampling : Integer read FBandwidthSampling
  958. write FBandwidthSampling;
  959. {$ENDIF}
  960. {$IFDEF UseContentCoding}
  961. property Options : THttpCliOptions read GetOptions
  962. write SetOptions;
  963. {$ELSE}
  964. property Options : THttpCliOptions read FOptions
  965. write FOptions;
  966. {$ENDIF}
  967. {$IFNDEF NO_DEBUG_LOG}
  968. property IcsLogger : TIcsLogger read GetIcsLogger { V1.91 }
  969. write SetIcsLogger;
  970. {$ENDIF}
  971. property Timeout : UINT read FTimeout { V7.04 sync only! }
  972. write FTimeout;
  973. property OnTrace : TNotifyEvent read FOnTrace
  974. write FOnTrace;
  975. property OnSessionConnected : TNotifyEvent read FOnSessionConnected
  976. write FOnSessionConnected;
  977. property OnSessionClosed : TNotifyEvent read FOnSessionClosed
  978. write FOnSessionClosed;
  979. property OnHeaderData : TNotifyEvent read FOnHeaderData
  980. write FOnHeaderData;
  981. property OnCommand : TOnCommand read FOnCommand
  982. write FOnCommand;
  983. property OnHeaderBegin : TNotifyEvent read FOnHeaderBegin
  984. write FOnHeaderBegin;
  985. property OnHeaderEnd : TNotifyEvent read FOnHeaderEnd
  986. write FOnHeaderEnd;
  987. property OnRequestHeaderBegin : TNotifyEvent read FOnRequestHeaderBegin
  988. write FOnRequestHeaderBegin;
  989. property OnRequestHeaderEnd : TNotifyEvent read FOnRequestHeaderEnd
  990. write FOnRequestHeaderEnd;
  991. property OnDocBegin : TNotifyEvent read FOnDocBegin
  992. write FOnDocBegin;
  993. property OnDocData : TDocDataEvent read FOnDocData
  994. write FOnDocData;
  995. property OnDocEnd : TNotifyEvent read FOnDocEnd
  996. write FOnDocEnd;
  997. property OnSendBegin : TNotifyEvent read FOnSendBegin
  998. write FOnSendBegin;
  999. property OnSendData : TDocDataEvent read FOnSendData
  1000. write FOnSendData;
  1001. property OnSendEnd : TNotifyEvent read FOnSendEnd
  1002. write FOnSendEnd;
  1003. property OnStateChange : TNotifyEvent read FOnStateChange
  1004. write FOnStateChange;
  1005. property OnRequestDone : THttpRequestDone read FOnRequestDone
  1006. write FOnRequestDone;
  1007. property OnLocationChange : TNotifyEvent read FOnLocationChange
  1008. write FOnLocationChange;
  1009. property OnCookie : TCookieRcvdEvent read FOnCookie
  1010. write FOnCookie;
  1011. property OnDataPush : TDataAvailable read FOnDataPush
  1012. write FOnDataPush;
  1013. property OnDataPush2 : TNotifyEvent read FOnDataPush2
  1014. write FOnDataPush2;
  1015. property SocksServer : String read FSocksServer
  1016. write FSocksServer;
  1017. property SocksLevel : String read FSocksLevel
  1018. write FSocksLevel;
  1019. property SocksPort : String read FSocksPort
  1020. write FSocksPort;
  1021. property SocksUsercode : String read FSocksUsercode
  1022. write FSocksUsercode;
  1023. property SocksPassword : String read FSocksPassword
  1024. write FSocksPassword;
  1025. property SocksAuthentication : TSocksAuthentication
  1026. read FSocksAuthentication
  1027. write FSocksAuthentication;
  1028. property OnSocksConnected : TSessionConnected
  1029. read FOnSocksConnected
  1030. write FOnSocksConnected;
  1031. property OnSocksAuthState : TSocksAuthStateEvent
  1032. read FOnSocksAuthState
  1033. write FOnSocksAuthState;
  1034. property OnSocksError : TSocksErrorEvent
  1035. read FOnSocksError
  1036. write FOnSocksError;
  1037. property OnSocketError : TNotifyEvent read FOnSocketError
  1038. write FOnSocketError;
  1039. property OnBeforeHeaderSend : TBeforeHeaderSendEvent
  1040. read FOnBeforeHeaderSend
  1041. write FOnBeforeHeaderSend;
  1042. property OnBeforeAuth : THttpBeforeAuthEvent
  1043. read FOnBeforeAuth
  1044. write FOnBeforeAuth;
  1045. end;
  1046. { You must define USE_SSL so that SSL code is included in the component. }
  1047. { Either in OverbyteIcsDefs.inc or in the project/package options. }
  1048. {$IFDEF USE_SSL}
  1049. {*_* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1050. Description: A component adding SSL support to THttpCli.
  1051. Requires OpenSSL (http://www.openssl.org).
  1052. More details in ReadMeIcsSsl.txt and IcsSslHowTo.txt.
  1053. SSL demo applications can be found in /Delphi/SslInternet.
  1054. If you use Delphi 7 and later, you may want to disable warnings
  1055. for unsage type, unsafe code and unsafe typecast in the project
  1056. options. Those warning are intended for .NET programs. You may
  1057. also want to turn off deprecated symbol and platform symbol
  1058. warnings.
  1059. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1060. {$IFDEF VER80}
  1061. Bomb('This unit require a 32 bit compiler !');
  1062. {$ENDIF}
  1063. {$B-} { Enable partial boolean evaluation }
  1064. {$T-} { Untyped pointers }
  1065. {$X+} { Enable extended syntax }
  1066. {$H+} { Use long strings }
  1067. {$J+} { Allow typed constant to be modified }
  1068. const
  1069. SslHttpCliVersion = 100;
  1070. SslHttpCliDate = 'Feb 15, 2003';
  1071. SslHttpCliCopyRight : String = ' TSslHttpCli (c) 2008 Francois Piette V1.00.0 ';
  1072. type
  1073. TSslHttpCli = class(THttpCli)
  1074. protected
  1075. FOnSslVerifyPeer : TSslVerifyPeerEvent;
  1076. FOnSslCliGetSession : TSslCliGetSession;
  1077. FOnSslCliNewSession : TSslCliNewSession;
  1078. FOnSslHandshakeDone : TSslHandshakeDoneEvent;
  1079. FOnSslCliCertRequest : TSslCliCertRequest;
  1080. procedure CreateSocket; override;
  1081. procedure DoBeforeConnect; override;
  1082. procedure SetSslContext(Value: TSslContext);
  1083. function GetSslContext: TSslContext;
  1084. procedure SetSslAcceptableHosts(Value : TStrings);
  1085. function GetSslAcceptableHosts: TStrings;
  1086. procedure TransferSslVerifyPeer(Sender : TObject;
  1087. var Ok : Integer;
  1088. Cert : TX509Base); virtual;
  1089. procedure TransferSslCliGetSession(Sender : TObject;
  1090. var SslSession : Pointer;
  1091. var FreeSession : Boolean); virtual;
  1092. procedure TransferSslCliNewSession(Sender : TObject;
  1093. SslSession : Pointer;
  1094. WasReused : Boolean;
  1095. var IncRefCount : Boolean); virtual;
  1096. procedure TransferSslCliCertRequest(Sender : TObject;
  1097. var Cert : TX509Base); virtual;
  1098. public
  1099. procedure SetAcceptableHostsList(const SemiColonSeparatedList : String);
  1100. published
  1101. property SslContext : TSslContext read GetSslContext
  1102. write SetSslContext;
  1103. property SslAcceptableHosts : TStrings read GetSslAcceptableHosts
  1104. write SetSslAcceptableHosts;
  1105. property OnSslVerifyPeer : TSslVerifyPeerEvent read FOnSslVerifyPeer
  1106. write FOnSslVerifyPeer;
  1107. property OnSslCliGetSession : TSslCliGetSession
  1108. read FOnSslCliGetSession
  1109. write FOnSslCliGetSession;
  1110. property OnSslCliNewSession : TSslCliNewSession
  1111. read FOnSslCliNewSession
  1112. write FOnSslCliNewSession;
  1113. property OnSslHandshakeDone : TSslHandshakeDoneEvent
  1114. read FOnSslHandshakeDone
  1115. write FOnSslHandshakeDone;
  1116. property OnSslCliCertRequest : TSslCliCertRequest read FOnSslCliCertRequest
  1117. write FOnSslCliCertRequest;
  1118. end;
  1119. {$ENDIF} // USE_SSL
  1120. procedure ReplaceExt(var FName : String; const newExt : String);
  1121. {$IFDEF CLR}
  1122. function EncodeStr(Encoding : THttpEncoding; const Value : String) : String;
  1123. {$ENDIF}
  1124. function RFC1123_Date(aDate : TDateTime) : String;
  1125. function RFC1123_StrToDate(aDate : String) : TDateTime;
  1126. {$IFDEF WIN32}
  1127. function EncodeLine(
  1128. Encoding : THttpEncoding;
  1129. SrcData : PAnsiChar;
  1130. Size : Integer) : AnsiString;
  1131. function EncodeStr(
  1132. Encoding : THttpEncoding;
  1133. const Value : RawByteString) : RawByteString; overload;
  1134. function EncodeStr(
  1135. Encoding : THttpEncoding;
  1136. const Value : UnicodeString;
  1137. ACodePage : LongWord = CP_ACP ) : UnicodeString; overload;
  1138. {$ENDIF}
  1139. implementation
  1140. const
  1141. {$IFDEF WIN32}
  1142. bin2uue : AnsiString = '`!"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_';
  1143. bin2b64 : AnsiString = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  1144. uue2bin : AnsiString = ' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ ';
  1145. b642bin : AnsiString = '~~~~~~~~~~~^~~~_TUVWXYZ[\]~~~|~~~ !"#$%&''()*+,-./0123456789~~~~~~:;<=>?@ABCDEFGHIJKLMNOPQRS';
  1146. {$ENDIF}
  1147. {$IFDEF CLR}
  1148. bin2uue : String = '`!"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_';
  1149. bin2b64 : String = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  1150. uue2bin : String = ' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ ';
  1151. b642bin : String = '~~~~~~~~~~~^~~~_TUVWXYZ[\]~~~|~~~ !"#$%&''()*+,-./0123456789~~~~~~:;<=>?@ABCDEFGHIJKLMNOPQRS';
  1152. {$ENDIF}
  1153. linesize = 45;
  1154. type
  1155. TCharSet = set of AnsiChar;
  1156. const
  1157. UriProtocolSchemeAllowedChars : TCharSet = ['a'..'z','0'..'9','+','-','.'];
  1158. function GetBaseUrl(const Url : String) : String; forward;
  1159. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1160. {$IFDEF DEBUG_OUTPUT}
  1161. procedure OutputDebugString(const Msg : String);
  1162. begin
  1163. if IsConsole then
  1164. WriteLn(Msg);
  1165. end;
  1166. {$ENDIF}
  1167. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1168. constructor EHttpException.Create(const Msg : String; ErrCode : Word);
  1169. begin
  1170. inherited Create(Msg);
  1171. ErrorCode := ErrCode;
  1172. end;
  1173. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1174. {$IFDEF DELPHI1}
  1175. procedure SetLength(var S: string; NewLength: Integer);
  1176. begin
  1177. S[0] := chr(NewLength);
  1178. end;
  1179. {$ENDIF}
  1180. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1181. const
  1182. RFC1123_StrWeekDay : String = 'MonTueWedThuFriSatSun';
  1183. RFC1123_StrMonth : String = 'JanFebMarAprMayJunJulAugSepOctNovDec';
  1184. { We cannot use Delphi own function because the date must be specified in }
  1185. { english and Delphi use the current language. }
  1186. function RFC1123_Date(aDate : TDateTime) : String;
  1187. var
  1188. Year, Month, Day : Word;
  1189. Hour, Min, Sec, MSec : Word;
  1190. DayOfWeek : Word;
  1191. begin
  1192. DecodeDate(aDate, Year, Month, Day);
  1193. DecodeTime(aDate, Hour, Min, Sec, MSec);
  1194. DayOfWeek := ((Trunc(aDate) - 2) mod 7);
  1195. Result := Copy(RFC1123_StrWeekDay, 1 + DayOfWeek * 3, 3) + ', ' +
  1196. Format('%2.2d %s %4.4d %2.2d:%2.2d:%2.2d',
  1197. [Day, Copy(RFC1123_StrMonth, 1 + 3 * (Month - 1), 3),
  1198. Year, Hour, Min, Sec]);
  1199. end;
  1200. { Bug: time zone is ignored !! }
  1201. function RFC1123_StrToDate(aDate : String) : TDateTime;
  1202. var
  1203. Year, Month, Day : Word;
  1204. Hour, Min, Sec : Word;
  1205. begin
  1206. { Fri, 30 Jul 2004 10:10:35 GMT }
  1207. Day := StrToIntDef(Copy(aDate, 6, 2), 0);
  1208. Month := (Pos(Copy(aDate, 9, 3), RFC1123_StrMonth) + 2) div 3;
  1209. Year := StrToIntDef(Copy(aDate, 13, 4), 0);
  1210. Hour := StrToIntDef(Copy(aDate, 18, 2), 0);
  1211. Min := StrToIntDef(Copy(aDate, 21, 2), 0);
  1212. Sec := StrToIntDef(Copy(aDate, 24, 2), 0);
  1213. Result := EncodeDate(Year, Month, Day);
  1214. Result := Result + EncodeTime(Hour, Min, Sec, 0);
  1215. end;
  1216. {$IFDEF NOFORMS}
  1217. { This function is a callback function. It means that it is called by }
  1218. { windows. This is the very low level message handler procedure setup to }
  1219. { handle the message sent by windows (winsock) to handle messages. }
  1220. function HTTPCliWindowProc(
  1221. ahWnd : HWND;
  1222. auMsg : Integer;
  1223. awParam : WPARAM;
  1224. alParam : LPARAM): Integer; stdcall;
  1225. var
  1226. Obj : TObject;
  1227. MsgRec : TMessage;
  1228. begin
  1229. { At window creation asked windows to store a pointer to our object }
  1230. Obj := TObject(GetWindowLong(ahWnd, 0));
  1231. { If the pointer doesn't represent a TCustomFtpCli, just call the default procedure}
  1232. if not (Obj is THTTPCli) then
  1233. Result := DefWindowProc(ahWnd, auMsg, awParam, alParam)
  1234. else begin
  1235. { Delphi use a TMessage type to pass parameter to his own kind of }
  1236. { windows procedure. So we are doing the same... }
  1237. MsgRec.Msg := auMsg;
  1238. MsgRec.wParam := awParam;
  1239. MsgRec.lParam := alParam;
  1240. { May be a try/except around next line is needed. Not sure ! }
  1241. THTTPCli(Obj).WndProc(MsgRec);
  1242. Result := MsgRec.Result;
  1243. end;
  1244. end;
  1245. {$ENDIF}
  1246. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1247. function THttpCli.MsgHandlersCount : Integer;
  1248. begin
  1249. Result := 3 + inherited MsgHandlersCount;
  1250. end;
  1251. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1252. procedure THttpCli.AllocateMsgHandlers;
  1253. begin
  1254. inherited AllocateMsgHandlers;
  1255. FMsg_WM_HTTP_REQUEST_DONE := FWndHandler.AllocateMsgHandler(Self);
  1256. FMsg_WM_HTTP_SET_READY := FWndHandler.AllocateMsgHandler(Self);
  1257. FMsg_WM_HTTP_LOGIN := FWndHandler.AllocateMsgHandler(Self);
  1258. end;
  1259. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1260. procedure THttpCli.FreeMsgHandlers;
  1261. begin
  1262. if Assigned(FWndHandler) then begin
  1263. FWndHandler.UnregisterMessage(FMsg_WM_HTTP_REQUEST_DONE);
  1264. FWndHandler.UnregisterMessage(FMsg_WM_HTTP_SET_READY);
  1265. FWndHandler.UnregisterMessage(FMsg_WM_HTTP_LOGIN);
  1266. end;
  1267. inherited FreeMsgHandlers;
  1268. end;
  1269. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1270. constructor THttpCli.Create(Aowner:TComponent);
  1271. begin
  1272. inherited Create(AOwner);
  1273. AllocateHWnd;
  1274. FProxyPort := DefaultProxyPort;
  1275. FRequestVer := '1.0';
  1276. FContentPost := 'application/x-www-form-urlencoded';
  1277. FAccept := 'image/gif, image/x-xbitmap, ' +
  1278. 'image/jpeg, image/pjpeg, */*';
  1279. FAgent := 'Mozilla/4.0 (compatible; ICS)';
  1280. FDoAuthor := TStringlist.Create;
  1281. FRcvdHeader := TStringList.Create;
  1282. FReqStream := TMemoryStream.Create;
  1283. FState := httpReady;
  1284. FLocalAddr := '0.0.0.0';
  1285. FFollowRelocation := TRUE; {TT 29 sept 2003}
  1286. {$IFDEF UseContentCoding}
  1287. FContentCodingHnd := THttpContCodHandler.Create(@FRcvdStream,
  1288. TriggerDocData);
  1289. GetOptions;
  1290. {$ENDIF}
  1291. CreateSocket;
  1292. FCtrlSocket.OnSessionClosed := SocketSessionClosed;
  1293. FCtrlSocket.OnDataAvailable := SocketDataAvailable;
  1294. FCtrlSocket.OnSessionConnected := SocketSessionConnected;
  1295. FCtrlSocket.OnDataSent := SocketDataSent;
  1296. FCtrlSocket.OnDnsLookupDone := SocketDNSLookupDone;
  1297. FCtrlSocket.OnSocksError := DoSocksError;
  1298. FCtrlSocket.OnSocksConnected := DoSocksConnected;
  1299. FCtrlSocket.OnError := SocketErrorTransfer;
  1300. {$IFDEF UseBandwidthControl}
  1301. FBandwidthLimit := 10000; { Bytes per second }
  1302. FBandwidthSampling := 1000; { mS sampling interval }
  1303. {$ENDIF}
  1304. FLocationChangeMaxCount := 5; { V1.90 }
  1305. FLocationChangeCurCount := 0; { V1.90 }
  1306. FTimeOut := 30;
  1307. end;
  1308. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1309. destructor THttpCli.Destroy;
  1310. begin
  1311. FDoAuthor.Free;
  1312. FCtrlSocket.Free;
  1313. FRcvdHeader.Free;
  1314. FReqStream.Free;
  1315. SetLength(FReceiveBuffer, 0); {AG 03/18/07}
  1316. SetLength(FSendBuffer, 0); {AG 03/18/07}
  1317. {$IFDEF UseBandwidthControl}
  1318. FBandwidthTimer.Free;
  1319. {$ENDIF}
  1320. {$IFDEF UseContentCoding}
  1321. FContentCodingHnd.Free;
  1322. {$ENDIF}
  1323. inherited Destroy;
  1324. end;
  1325. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1326. procedure THttpCli.CreateSocket;
  1327. begin
  1328. FCtrlSocket := TWSocket.Create(Self);
  1329. end;
  1330. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1331. procedure THttpCli.WndProc(var MsgRec: TMessage);
  1332. begin
  1333. with MsgRec do begin
  1334. if Msg = FMsg_WM_HTTP_REQUEST_DONE then
  1335. WMHttpRequestDone(MsgRec)
  1336. else if Msg = FMsg_WM_HTTP_SET_READY then
  1337. WMHttpSetReady(MsgRec)
  1338. else if Msg = FMsg_WM_HTTP_LOGIN then
  1339. WMHttpLogin(MsgRec)
  1340. else
  1341. inherited WndProc(MsgRec);
  1342. end;
  1343. end;
  1344. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1345. procedure THttpCli.DoSocksConnected(Sender: TObject; ErrCode: Word);
  1346. begin
  1347. if Assigned(FOnSocksConnected) then
  1348. FOnSocksConnected(Sender, ErrCode);
  1349. end;
  1350. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1351. procedure THttpCli.SocketErrorTransfer(Sender : TObject);
  1352. begin
  1353. if (assigned(FOnSocketError)) then
  1354. FOnSocketError(Self); { Substitute Self for subcomponent's Sender. }
  1355. end; { SocketErrorTransfer }
  1356. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1357. procedure THttpCli.DoSocksAuthState(
  1358. Sender : TObject;
  1359. AuthState : TSocksAuthState);
  1360. begin
  1361. if Assigned(FOnSocksAuthState) then
  1362. FOnSocksAuthState(Sender, AuthState);
  1363. end;
  1364. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1365. procedure THttpCli.DoSocksError(
  1366. Sender : TObject;
  1367. ErrCode : Integer;
  1368. Msg : String);
  1369. begin
  1370. if Assigned(FOnSocksError) then
  1371. FOnSocksError(Sender, ErrCode, Msg);
  1372. end;
  1373. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1374. procedure THttpCli.SetMultiThreaded(newValue : Boolean);
  1375. begin
  1376. FMultiThreaded := newValue;
  1377. FCtrlSocket.MultiThreaded := newValue;
  1378. end;
  1379. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1380. procedure THttpCli.SetReady;
  1381. begin
  1382. PostMessage(Handle, FMsg_WM_HTTP_SET_READY, 0, 0);
  1383. end;
  1384. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1385. procedure THttpCli.StateChange(NewState : THttpState);
  1386. var
  1387. FlgClean : Boolean;
  1388. SaveDoc : String;
  1389. begin
  1390. if FState <> NewState then begin
  1391. FState := NewState;
  1392. TriggerStateChange;
  1393. if (NewState = httpReady) then begin
  1394. { We must elaborate the result of started authentications
  1395. before a new preparation }
  1396. {$IFDEF UseNTLMAuthentication}
  1397. ElaborateNTLMAuth;
  1398. {$ENDIF}
  1399. {$IFDEF UseDigestAuthentication}
  1400. ElaborateDigestAuth;
  1401. {$ENDIF}
  1402. ElaborateBasicAuth;
  1403. FlgClean := False;
  1404. {$IFDEF UseNTLMAuthentication}
  1405. if PrepareNTLMAuth(FlgClean) or
  1406. {$IFDEF UseDigestAuthentication}
  1407. PrepareDigestAuth(FlgClean) or
  1408. {$ENDIF}
  1409. PrepareBasicAuth(FlgClean) then begin
  1410. {$ELSE}
  1411. {$IFDEF UseDigestAuthentication}
  1412. if PrepareDigestAuth(FlgClean) or PrepareBasicAuth(FlgClean) then begin
  1413. {$ELSE}
  1414. if PrepareBasicAuth(FlgClean) then begin
  1415. {$ENDIF}
  1416. {$ENDIF}
  1417. if FStatusCode = 401 then begin
  1418. { If the connection will be closed then check if we must
  1419. repeat a proxy authentication, otherwise we must clear
  1420. it }
  1421. if FCloseReq then begin
  1422. {$IFDEF UseNTLMAuthentication}
  1423. if FProxyAuthNTLMState = ntlmDone then
  1424. FProxyAuthNTLMState := ntlmMsg1
  1425. else
  1426. {$ENDIF}
  1427. {$IFDEF UseDigestAuthentication}
  1428. if FProxyAuthDigestState = digestDone then
  1429. FProxyAuthDigestState := digestMsg1
  1430. else
  1431. {$ENDIF}
  1432. if FProxyAuthBasicState = basicDone then
  1433. FProxyAuthBasicState := basicMsg1;
  1434. end
  1435. else begin
  1436. {$IFDEF UseNTLMAuthentication}
  1437. if FProxyAuthNTLMState < ntlmDone then
  1438. FProxyAuthNTLMState := ntlmNone
  1439. else
  1440. {$ENDIF}
  1441. {$IFDEF UseDigestAuthentication}
  1442. if FProxyAuthDigestState < digestDone then
  1443. FProxyAuthDigestState := digestNone
  1444. else
  1445. {$ENDIF}
  1446. if FProxyAuthBasicState < basicDone then
  1447. FProxyAuthBasicState := basicNone;
  1448. end;
  1449. end;
  1450. if FlgClean then begin
  1451. CleanupRcvdStream; { What we are received must be removed }
  1452. CleanupSendStream;
  1453. FReceiveLen := 0;
  1454. SaveDoc := FDocName;
  1455. InternalClear;
  1456. FDocName := SaveDoc;
  1457. end;
  1458. end
  1459. else
  1460. TriggerRequestDone;
  1461. end;
  1462. end;
  1463. end;
  1464. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1465. procedure THttpCli.TriggerStateChange;
  1466. begin
  1467. {$IFNDEF NO_DEBUG_LOG}
  1468. if CheckLogOptions(loProtSpecInfo) then begin { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  1469. case FState of
  1470. httpReady : DebugLog(loProtSpecInfo, 'State = httpReady');
  1471. httpNotConnected : DebugLog(loProtSpecInfo, 'State = httpNotConnected');
  1472. httpConnected : DebugLog(loProtSpecInfo, 'State = httpConnected');
  1473. httpDnsLookup : DebugLog(loProtSpecInfo, 'State = httpDnsLookup');
  1474. httpDnsLookupDone : DebugLog(loProtSpecInfo, 'State = httpDnsLookupDone');
  1475. httpWaitingHeader : DebugLog(loProtSpecInfo, 'State = httpWaitingHeader');
  1476. httpWaitingBody : DebugLog(loProtSpecInfo, 'State = httpWaitingBody');
  1477. httpBodyReceived : DebugLog(loProtSpecInfo, 'State = httpBodyReceived');
  1478. httpWaitingProxyConnect : DebugLog(loProtSpecInfo, 'State = httpWaitingProxyConnect');
  1479. httpClosing : DebugLog(loProtSpecInfo, 'State = httpClosing');
  1480. httpAborting : DebugLog(loProtSpecInfo, 'State = httpAborting');
  1481. else DebugLog(loProtSpecInfo, 'State = INVALID STATE');
  1482. end;
  1483. end;
  1484. {$ENDIF}
  1485. if Assigned(FOnStateChange) then
  1486. FOnStateChange(Self);
  1487. end;
  1488. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1489. procedure THttpCli.TriggerCookie(const Data : String; var bAccept : Boolean);
  1490. begin
  1491. if Assigned(FOnCookie) then
  1492. FOnCookie(Self, Data, bAccept);
  1493. end;
  1494. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1495. procedure THttpCli.TriggerSessionConnected;
  1496. begin
  1497. if Assigned(FOnSessionConnected) then
  1498. FOnSessionConnected(Self);
  1499. end;
  1500. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1501. procedure THttpCli.TriggerSessionClosed;
  1502. begin
  1503. if Assigned(FOnSessionClosed) then
  1504. FOnSessionClosed(Self);
  1505. end;
  1506. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1507. procedure THttpCli.TriggerDocBegin;
  1508. begin
  1509. {$IFNDEF NO_DEBUG_LOG}
  1510. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  1511. DebugLog(loProtSpecInfo, 'DocBegin');
  1512. {$ENDIF}
  1513. if Assigned(FOnDocBegin) then
  1514. FOnDocBegin(Self);
  1515. end;
  1516. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1517. procedure THttpCli.TriggerDocEnd;
  1518. begin
  1519. {$IFNDEF NO_DEBUG_LOG}
  1520. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  1521. DebugLog(loProtSpecInfo, 'DocEnd');
  1522. {$ENDIF}
  1523. if Assigned(FOnDocEnd) then
  1524. FOnDocEnd(Self);
  1525. end;
  1526. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1527. procedure THttpCli.TriggerDocData(
  1528. {$IFDEF CLR}
  1529. var Data : TBytes;
  1530. Offset : Integer;
  1531. {$ELSE}
  1532. Data : Pointer;
  1533. {$ENDIF}
  1534. Len : Integer);
  1535. begin
  1536. if Assigned(FOnDocData) then
  1537. FOnDocData(Self, Data, {$IFDEF CLR}Offset, {$ENDIF}Len);
  1538. end;
  1539. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1540. procedure THttpCli.TriggerSendBegin;
  1541. begin
  1542. if Assigned(FOnSendBegin) then
  1543. FOnSendBegin(Self);
  1544. end;
  1545. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1546. procedure THttpCli.TriggerSendEnd;
  1547. begin
  1548. if Assigned(FOnSendEnd) then
  1549. FOnSendEnd(Self);
  1550. end;
  1551. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1552. procedure THttpCli.TriggerSendData(
  1553. {$IFDEF CLR}
  1554. var Data : TBytes;
  1555. Offset : Integer;
  1556. {$ELSE}
  1557. Data : Pointer;
  1558. {$ENDIF}
  1559. Len : Integer);
  1560. begin
  1561. if Assigned(FOnSendData) then
  1562. FOnSendData(Self, Data, {$IFDEF CLR}Offset, {$ENDIF}Len);
  1563. end;
  1564. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1565. procedure THttpCli.TriggerHeaderBegin;
  1566. begin
  1567. FHeaderEndFlag := TRUE;
  1568. if Assigned(FOnHeaderBegin) then
  1569. FOnHeaderBegin(Self);
  1570. end;
  1571. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1572. procedure THttpCli.TriggerHeaderEnd;
  1573. begin
  1574. FHeaderEndFlag := FALSE;
  1575. if Assigned(FOnHeaderEnd) then
  1576. FOnHeaderEnd(Self);
  1577. end;
  1578. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1579. procedure THttpCli.TriggerBeforeHeaderSend(
  1580. const Method : String;
  1581. Headers : TStrings);
  1582. begin
  1583. if Assigned(FOnBeforeHeaderSend) then
  1584. FOnBeforeHeaderSend(Self, Method, Headers);
  1585. end;
  1586. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1587. procedure THttpCli.TriggerRequestHeaderBegin;
  1588. begin
  1589. if Assigned(FOnRequestHeaderBegin) then
  1590. FOnRequestHeaderBegin(Self);
  1591. end;
  1592. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1593. procedure THttpCli.TriggerRequestHeaderEnd;
  1594. begin
  1595. if Assigned(FOnRequestHeaderEnd) then
  1596. FOnRequestHeaderEnd(Self);
  1597. end;
  1598. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1599. {$IFDEF UseNTLMAuthentication}
  1600. procedure THttpCli.ElaborateNTLMAuth;
  1601. begin
  1602. { if you place this code in GetHeaderLineNext, not each time will be }
  1603. { called ... }
  1604. if (FAuthNTLMState = ntlmMsg3) and (FStatusCode <> 401) and (FStatusCode <> 407) then
  1605. FAuthNTLMState := ntlmDone
  1606. else if (FAuthNTLMState = ntlmDone) and (FStatusCode = 401) then
  1607. FAuthNTLMState := ntlmNone;
  1608. if (FProxyAuthNTLMState = ntlmMsg3) and (FStatusCode <> 407) then
  1609. FProxyAuthNTLMState := ntlmDone
  1610. else if (FProxyAuthNTLMState = ntlmDone) and (FStatusCode = 407) then begin
  1611. { if we lost proxy authenticated line, most probaly we lost also }
  1612. { the authenticated line of Proxy to HTTP server, so reset the }
  1613. { NTLM state of HTTP also to none }
  1614. FProxyAuthNTLMState := ntlmNone;
  1615. { FAuthNTLMState := ntlmNone; } { Removed by *ML* on May 02, 2005 }
  1616. end;
  1617. end;
  1618. {$ENDIF}
  1619. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1620. procedure THttpCli.ElaborateBasicAuth;
  1621. begin
  1622. { if you place this code in GetHeaderLineNext, not each time will be }
  1623. { called ... }
  1624. if (FAuthBasicState = basicMsg1) and (FStatusCode <> 401) and (FStatusCode <> 407) then
  1625. FAuthBasicState := basicDone
  1626. else if (FAuthBasicState = basicDone) and (FStatusCode = 401) then
  1627. FAuthBasicState := basicNone;
  1628. if (FProxyAuthBasicState = basicMsg1) and (FStatusCode <> 407) then
  1629. FProxyAuthBasicState := basicDone
  1630. else if (FProxyAuthBasicState = basicDone) and (FStatusCode = 407) then begin
  1631. { if we lost proxy authenticated line, most probaly we lost also }
  1632. { the authenticated line of Proxy to HTTP server, so reset the }
  1633. { Basic state of HTTP also to none }
  1634. FProxyAuthBasicState := basicNone;
  1635. { FAuthBasicState := basicNone; } { Removed by *ML* on May 02, 2005 }
  1636. end;
  1637. end;
  1638. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1639. {$IFDEF UseNTLMAuthentication}
  1640. function THttpCli.PrepareNTLMAuth(var FlgClean : Boolean) : Boolean;
  1641. var
  1642. TmpInt : Integer;
  1643. begin
  1644. {$IFNDEF NO_DEBUG_LOG} { V1.91 }
  1645. //THttpNTLMState = (ntlmNone, ntlmMsg1, ntlmMsg2, ntlmMsg3, ntlmDone);
  1646. if CheckLogOptions(loProtSpecDump) then begin
  1647. DebugLog(loProtSpecDump, Format('PrepareNTLMAuth end, FStatusCode = %d ' +
  1648. 'FProxyAuthNTLMState=%d FAuthNTLMState=%d',
  1649. [FStatusCode, Ord(FProxyAuthNTLMState),
  1650. Ord(FAuthNTLMState)]));
  1651. end;
  1652. {$ENDIF}
  1653. { this flag can tell if we proceed with OnRequestDone or will try }
  1654. { to authenticate }
  1655. Result := FALSE;
  1656. if (httpoNoNTLMAuth in FOptions) and
  1657. (((FStatusCode = 401) and (FServerAuth = httpAuthNtlm)) or
  1658. ((FStatusCode = 407) and (FProxyAuth = httpAuthNtlm))) then
  1659. Exit;
  1660. if (FStatusCode = 401) and (FDoAuthor.Count > 0) and
  1661. (FAuthBasicState = basicNone) and
  1662. {$IFDEF UseDigestAuthentication}
  1663. (FAuthDigestState = digestNone) and
  1664. {$ENDIF}
  1665. (FCurrUserName <> '') and (FCurrPassword <> '') then begin
  1666. { We can handle authorization }
  1667. TmpInt := FDoAuthor.Count - 1;
  1668. while TmpInt >= 0 do begin
  1669. if CompareText(Copy(FDoAuthor.Strings[TmpInt], 1, 4), 'NTLM') = 0 then begin
  1670. Result := TRUE;
  1671. if Assigned(FOnBeforeAuth) then
  1672. FOnBeforeAuth(Self, httpAuthNtlm, FALSE,
  1673. FDoAuthor.Strings[TmpInt], Result);
  1674. if Result then begin
  1675. StartAuthNTLM;
  1676. if FAuthNTLMState in [ntlmMsg1, ntlmMsg3] then
  1677. FlgClean := True;
  1678. Break;
  1679. end;
  1680. end;
  1681. Dec(TmpInt);
  1682. end;
  1683. end
  1684. else if (FStatusCode = 407) and (FDoAuthor.Count > 0) and
  1685. (FProxyAuthBasicState = basicNone) and
  1686. {$IFDEF UseDigestAuthentication}
  1687. (FProxyAuthDigestState = digestNone) and
  1688. {$ENDIF}
  1689. (FProxyUsername <> '') and (FProxyPassword <> '') then begin
  1690. {BLD proxy NTLM authentication}
  1691. { We can handle authorization }
  1692. TmpInt := FDoAuthor.Count - 1;
  1693. while TmpInt >= 0 do begin
  1694. if CompareText(Copy(FDoAuthor.Strings[TmpInt], 1, 4), 'NTLM') = 0 then begin
  1695. Result := TRUE;
  1696. if Assigned(FOnBeforeAuth) then
  1697. FOnBeforeAuth(Self, httpAuthNtlm, TRUE,
  1698. FDoAuthor.Strings[TmpInt], Result);
  1699. if Result then begin
  1700. StartProxyAuthNTLM;
  1701. if FProxyAuthNTLMState in [ntlmMsg1, ntlmMsg3] then
  1702. FlgClean := True;
  1703. Break;
  1704. end;
  1705. end;
  1706. Dec(TmpInt);
  1707. end;
  1708. end;
  1709. {$IFNDEF NO_DEBUG_LOG} { V1.91 }
  1710. //THttpNTLMState = (ntlmNone, ntlmMsg1, ntlmMsg2, ntlmMsg3, ntlmDone);
  1711. if CheckLogOptions(loProtSpecDump) then begin
  1712. DebugLog(loProtSpecDump, Format('PrepareNTLMAuth end, FStatusCode = %d ' +
  1713. 'FProxyAuthNTLMState=%d FAuthNTLMState=%d',
  1714. [FStatusCode, Ord(FProxyAuthNTLMState),
  1715. Ord(FAuthNTLMState)]));
  1716. end;
  1717. {$ENDIF}
  1718. end;
  1719. {$ENDIF}
  1720. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1721. function THttpCli.PrepareBasicAuth(var FlgClean : Boolean) : Boolean;
  1722. var
  1723. TmpInt : Integer;
  1724. begin
  1725. { this flag can tell if we proceed with OnRequestDone or will try }
  1726. { to authenticate }
  1727. Result := FALSE;
  1728. if (httpoNoBasicAuth in FOptions) and
  1729. (((FStatusCode = 401) and (FServerAuth = httpAuthBasic)) or
  1730. ((FStatusCode = 407) and (FProxyAuth = httpAuthBasic))) then
  1731. Exit;
  1732. if (FStatusCode = 401) and (FDoAuthor.Count > 0) and
  1733. {$IFDEF UseNTLMAuthentication}
  1734. (FAuthNTLMState = ntlmNone) and
  1735. {$ENDIF}
  1736. {$IFDEF UseDigestAuthentication}
  1737. (FAuthDigestState = digestNone) and
  1738. {$ENDIF}
  1739. (FCurrUserName <> '') and (FCurrPassword <> '') then begin
  1740. { We can handle authorization }
  1741. TmpInt := FDoAuthor.Count - 1;
  1742. while TmpInt >= 0 do begin
  1743. if CompareText(Copy(FDoAuthor.Strings[TmpInt], 1, 5),
  1744. 'Basic') = 0 then begin
  1745. Result := TRUE;
  1746. if Assigned(FOnBeforeAuth) then
  1747. FOnBeforeAuth(Self, httpAuthBasic, TRUE,
  1748. FDoAuthor.Strings[TmpInt], Result);
  1749. if Result then begin
  1750. StartAuthBasic;
  1751. if FAuthBasicState in [basicMsg1] then
  1752. FlgClean := True;
  1753. Break;
  1754. end;
  1755. end;
  1756. Dec(TmpInt);
  1757. end;
  1758. end
  1759. else if (FStatusCode = 407) and (FDoAuthor.Count > 0) and
  1760. {$IFDEF UseNTLMAuthentication}
  1761. (FProxyAuthNTLMState = ntlmNone) and
  1762. {$ENDIF}
  1763. {$IFDEF UseDigestAuthentication}
  1764. (FProxyAuthDigestState = digestNone) and
  1765. {$ENDIF}
  1766. (FProxyUsername <> '') and (FProxyPassword <> '') then begin
  1767. { We can handle authorization }
  1768. TmpInt := FDoAuthor.Count - 1;
  1769. while TmpInt >= 0 do begin
  1770. if CompareText(Copy(FDoAuthor.Strings[TmpInt], 1, 5),
  1771. 'Basic') = 0 then begin
  1772. Result := TRUE;
  1773. if Assigned(FOnBeforeAuth) then
  1774. FOnBeforeAuth(Self, httpAuthBasic, TRUE,
  1775. FDoAuthor.Strings[TmpInt], Result);
  1776. if Result then begin
  1777. StartProxyAuthBasic;
  1778. if FProxyAuthBasicState in [basicMsg1] then
  1779. FlgClean := True;
  1780. Break;
  1781. end;
  1782. end;
  1783. Dec(TmpInt);
  1784. end;
  1785. end;
  1786. end;
  1787. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1788. procedure THttpCli.TriggerRequestDone;
  1789. begin
  1790. PostMessage(Handle, FMsg_WM_HTTP_REQUEST_DONE, 0, 0);
  1791. end;
  1792. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1793. procedure THttpCli.WMHttpRequestDone(var msg: TMessage);
  1794. begin
  1795. {$IFNDEF NO_DEBUG_LOG}
  1796. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  1797. DebugLog(loProtSpecInfo, 'RequestDone');
  1798. {$ENDIF}
  1799. if Assigned(FOnRequestDone) then
  1800. FOnRequestDone(Self, FRequestType, FRequestDoneError);
  1801. end;
  1802. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1803. procedure THttpCli.WMHttpSetReady(var msg: TMessage);
  1804. begin
  1805. StateChange(httpReady);
  1806. end;
  1807. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1808. procedure ReplaceExt(var FName : String; const newExt : String);
  1809. var
  1810. I : Integer;
  1811. begin
  1812. I := Posn('.', FName, -1);
  1813. if I <= 0 then
  1814. FName := FName + '.' + newExt
  1815. else
  1816. FName := Copy(FName, 1, I) + newExt;
  1817. end;
  1818. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1819. procedure THttpCli.Abort;
  1820. var
  1821. bFlag : Boolean;
  1822. Msg : TMessage;
  1823. begin
  1824. if FState = httpReady then begin
  1825. FState := httpAborting;
  1826. if FCtrlSocket.State <> wsClosed then
  1827. FCtrlSocket.Abort;
  1828. FStatusCode := 200;
  1829. FReasonPhrase := 'OK';
  1830. FRequestDoneError := 0;
  1831. FState := httpReady;
  1832. TriggerStateChange;
  1833. WMHttpRequestDone(Msg); { Synchronous operation ! }
  1834. Exit;
  1835. end;
  1836. bFlag := (FState = httpDnsLookup);
  1837. StateChange(httpAborting);
  1838. if bFlag then begin
  1839. try
  1840. FCtrlSocket.CancelDnsLookup;
  1841. except
  1842. { Ignore any exception }
  1843. end;
  1844. end;
  1845. FStatusCode := 404;
  1846. FReasonPhrase := 'Connection aborted on request';
  1847. FRequestDoneError := httperrAborted;
  1848. if bFlag then
  1849. SocketSessionClosed(Self, 0)
  1850. else
  1851. FCtrlSocket.Close;
  1852. StateChange(httpReady); { 13/02/99 }
  1853. end;
  1854. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1855. procedure THttpCli.Login;
  1856. begin
  1857. {$IFNDEF NO_DEBUG_LOG}
  1858. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  1859. DebugLog(loProtSpecInfo, 'Login ' + FHostName);
  1860. {$ENDIF}
  1861. FCtrlSocket.OnSessionClosed := SocketSessionClosed;
  1862. if FCtrlSocket.State = wsConnected then begin
  1863. SocketSessionConnected(nil, 0);
  1864. Exit;
  1865. end;
  1866. FDnsResult := '';
  1867. StateChange(httpDnsLookup);
  1868. FCtrlSocket.LocalAddr := FLocalAddr; {bb}
  1869. try
  1870. FCtrlSocket.DnsLookup(FHostName);
  1871. except
  1872. on E: Exception do begin
  1873. FStatusCode := 404;
  1874. FReasonPhrase := E.Message;
  1875. FConnected := FALSE;
  1876. StateChange(httpReady);
  1877. end;
  1878. end;
  1879. end;
  1880. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1881. procedure THttpCli.DoBeforeConnect;
  1882. begin
  1883. FCtrlSocket.Addr := FDnsResult;
  1884. FCtrlSocket.LocalAddr := FLocalAddr; {bb}
  1885. FCtrlSocket.Port := FPort;
  1886. FCtrlSocket.Proto := 'tcp';
  1887. FCtrlSocket.SocksServer := FSocksServer;
  1888. FCtrlSocket.SocksLevel := FSocksLevel;
  1889. FCtrlSocket.SocksPort := FSocksPort;
  1890. FCtrlSocket.SocksUsercode := FSocksUsercode;
  1891. FCtrlSocket.SocksPassword := FSocksPassword;
  1892. FCtrlSocket.SocksAuthentication := FSocksAuthentication;
  1893. end;
  1894. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1895. procedure THttpCli.SocketDNSLookupDone(Sender: TObject; ErrCode: Word);
  1896. begin
  1897. if ErrCode <> 0 then begin
  1898. if FState = httpAborting then
  1899. Exit;
  1900. FRequestDoneError := ErrCode;
  1901. FStatusCode := 404;
  1902. FReasonPhrase := 'can''t resolve hostname to IP address';
  1903. SocketSessionClosed(Sender, ErrCode);
  1904. end
  1905. else begin
  1906. FDnsResult := FCtrlSocket.DnsResult;
  1907. StateChange(httpDnsLookupDone); { 19/09/98 }
  1908. {$IFDEF UseNTLMAuthentication}
  1909. { NTLM authentication is alive only for one connection }
  1910. { so when we reconnect to server NTLM auth states must be reseted }
  1911. (* Removed by *ML* on May 02, 2005
  1912. if FAuthNTLMState = ntlmDone then
  1913. FAuthNTLMState := ntlmNone; {BLD NTLM}
  1914. if FProxyAuthNTLMState = ntlmDone then begin
  1915. FProxyAuthNTLMState := ntlmNone; {BLD NTLM}
  1916. FAuthNTLMState := ntlmNone;
  1917. end;
  1918. *)
  1919. {$ENDIF}
  1920. { Basic authentication is alive only for one connection }
  1921. { so when we reconnect to server Basic auth states must be reseted }
  1922. (* Removed by *ML* on May 02, 2005
  1923. if FAuthBasicState = basicDone then
  1924. FAuthBasicState := basicNone;
  1925. if FProxyAuthBasicState = BasicDone then begin
  1926. FProxyAuthBasicState := basicNone;
  1927. FAuthBasicState := basicNone;
  1928. end;
  1929. *)
  1930. DoBeforeConnect;
  1931. FCurrentHost := FHostName;
  1932. FCurrentPort := FPort;
  1933. FCurrentProtocol := FProtocol;
  1934. {$IFDEF USE_SSL}
  1935. FCtrlSocket.SslEnable := ((FProxy = '') and (FProtocol = 'https'));
  1936. {$ENDIF}
  1937. { 05/02/2005 begin }
  1938. if (FProtocol <> 'http')
  1939. {$IFDEF USE_SSL}
  1940. and (FProtocol <> 'https')
  1941. {$ENDIF}
  1942. then begin
  1943. FRequestDoneError := FCtrlSocket.LastError;
  1944. FStatusCode := 501;
  1945. FReasonPhrase := 'Protocol "' + FProtocol + '" not implemented';
  1946. FCtrlSocket.Close;
  1947. SocketSessionClosed(Sender, FCtrlSocket.LastError);
  1948. Exit;
  1949. end;
  1950. { 05/02/2005 end }
  1951. {$IFNDEF NO_DEBUG_LOG}
  1952. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  1953. DebugLog(loProtSpecInfo, 'connect to ' + FDnsResult + '/' + FPort);
  1954. {$ENDIF}
  1955. try
  1956. FCtrlSocket.Connect;
  1957. except
  1958. FRequestDoneError := FCtrlSocket.LastError;
  1959. FStatusCode := 404;
  1960. FReasonPhrase := 'can''t connect: ' +
  1961. WSocketErrorDesc(FCtrlSocket.LastError) +
  1962. ' (Error #' + IntToStr(FCtrlSocket.LastError) + ')';
  1963. FCtrlSocket.Close;
  1964. SocketSessionClosed(Sender, FCtrlSocket.LastError);
  1965. end;
  1966. end;
  1967. end;
  1968. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  1969. procedure THttpCli.SocketSessionConnected(Sender : TObject; ErrCode : Word);
  1970. begin
  1971. {$IFNDEF NO_DEBUG_LOG}
  1972. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  1973. DebugLog(loProtSpecInfo, 'SessionConnected');
  1974. {$ENDIF}
  1975. if ErrCode <> 0 then begin
  1976. FRequestDoneError := ErrCode;
  1977. FStatusCode := 404;
  1978. FReasonPhrase := WSocketErrorDesc(ErrCode) +
  1979. ' (Error #' + IntToStr(ErrCode) + ')';
  1980. {SocketSessionClosed(Sender, ErrCode)} {14/12/2003};
  1981. TriggerSessionConnected; {14/12/2003}
  1982. Exit;
  1983. end;
  1984. FLocationFlag := FALSE;
  1985. FConnected := TRUE;
  1986. StateChange(httpConnected);
  1987. TriggerSessionConnected;
  1988. FNext := GetHeaderLineNext;
  1989. try
  1990. if (FProxy <> '') and
  1991. (FProtocol = 'https') and
  1992. ((FProxyConnected = FALSE) or
  1993. {$IFDEF UseNTLMAuthentication}
  1994. (FProxyConnected and (FProxyAuthNTLMState = ntlmMsg3)) or
  1995. (FProxyConnected and (FProxyAuthNTLMState = ntlmMsg1)) or // <= AG 12/27/05
  1996. {$IFDEF UseDigestAuthentication}
  1997. (FProxyConnected and (FProxyAuthDigestState = digestMsg1)) or
  1998. {$ENDIF}
  1999. (FProxyConnected and (FProxyAuthBasicState = basicMsg1)))
  2000. {$ELSE}
  2001. {$IFDEF UseDigestAuthentication}
  2002. (FProxyConnected and (FProxyAuthDigestState = digestMsg1)) or
  2003. {$ENDIF}
  2004. (FProxyConnected and (FProxyAuthBasicState = basicMsg1)))
  2005. {$ENDIF}
  2006. then begin
  2007. StateChange(httpWaitingProxyConnect);
  2008. FReqStream.Clear;
  2009. if (FRequestVer = '1.0') or (FResponseVer = '1.0') or
  2010. (FResponseVer = '') then
  2011. FCurrProxyConnection := 'Keep-Alive';
  2012. SendRequest('CONNECT', FRequestVer{'1.0'});
  2013. end
  2014. else begin
  2015. StateChange(httpWaitingHeader);
  2016. case FRequestType of
  2017. httpPOST:
  2018. begin
  2019. SendRequest('POST', FRequestVer);
  2020. {$IFDEF UseNTLMAuthentication}
  2021. if not ((FAuthNTLMState = ntlmMsg1) or
  2022. (FProxyAuthNTLMState = ntlmMsg1)) then begin
  2023. TriggerSendBegin;
  2024. FAllowedToSend := TRUE;
  2025. FDelaySetReady := FALSE; { 09/26/08 ML }
  2026. SocketDataSent(FCtrlSocket, 0);
  2027. end;
  2028. {$ELSE}
  2029. TriggerSendBegin;
  2030. FAllowedToSend := TRUE;
  2031. FDelaySetReady := FALSE; { 09/26/08 ML }
  2032. SocketDataSent(FCtrlSocket, 0);
  2033. {$ENDIF}
  2034. end;
  2035. httpPUT:
  2036. begin
  2037. SendRequest('PUT', FRequestVer);
  2038. {$IFDEF UseNTLMAuthentication}
  2039. if not ((FAuthNTLMState = ntlmMsg1) or
  2040. (FProxyAuthNTLMState = ntlmMsg1)) then begin
  2041. TriggerSendBegin;
  2042. FAllowedToSend := TRUE;
  2043. FDelaySetReady := FALSE; { 09/26/08 ML }
  2044. SocketDataSent(FCtrlSocket, 0);
  2045. end;
  2046. {$ELSE}
  2047. TriggerSendBegin;
  2048. FAllowedToSend := TRUE;
  2049. FDelaySetReady := FALSE; { 09/26/08 ML }
  2050. SocketDataSent(FCtrlSocket, 0);
  2051. {$ENDIF}
  2052. end;
  2053. httpHEAD:
  2054. begin
  2055. SendRequest('HEAD', FRequestVer);
  2056. end;
  2057. httpGET:
  2058. begin
  2059. SendRequest('GET', FRequestVer);
  2060. end;
  2061. end;
  2062. end;
  2063. except
  2064. Logout;
  2065. end;
  2066. end;
  2067. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  2068. procedure THttpCli.Logout;
  2069. begin
  2070. FCtrlSocket.Close;
  2071. FConnected := FALSE;
  2072. end;
  2073. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  2074. procedure THttpCli.SendCommand(const Cmd : String);
  2075. const
  2076. CRLF : String[2] = #13#10;
  2077. {$IFDEF CLR}
  2078. var
  2079. Buf : String;
  2080. I : Integer;
  2081. begin
  2082. Buf := Cmd;
  2083. if Assigned(FOnCommand) then
  2084. FOnCommand(Self, Buf);
  2085. for I := 1 to Length(Buf) do
  2086. FReqStream.Write(Byte(Buf[I]));
  2087. FReqStream.Write(Byte(CRLF[1]));
  2088. FReqStream.Write(Byte(CRLF[2]));
  2089. end;
  2090. {$ELSE}
  2091. var
  2092. Buf : String;
  2093. begin
  2094. Buf := Cmd;
  2095. if Assigned(FOnCommand) then
  2096. FOnCommand(Self, Buf);
  2097. if Length(Buf) > 0 then
  2098. {$IFDEF COMPILER12_UP}
  2099. StreamWriteString(FReqStream, Buf, CP_ACP);
  2100. {$ELSE}
  2101. FReqStream.Write(Buf[1], Length(Buf));
  2102. {$ENDIF}
  2103. FReqStream.Write(CRLF[1], 2);
  2104. end;
  2105. {$ENDIF}
  2106. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  2107. procedure THttpCli.SendRequest(const Method, Version: String);
  2108. var
  2109. Headers : TStrings;
  2110. N : Integer;
  2111. begin
  2112. {$IFDEF UseBandwidthControl}
  2113. FBandwidthCount := 0; // Reset byte counter
  2114. if httpoBandwidthControl in FOptions then begin
  2115. if not Assigned(FBandwidthTimer) then
  2116. FBandwidthTimer := TIcsTimer.Create(Self);
  2117. FBandwidthTimer.Enabled := FALSE;
  2118. FBandwidthTimer.Interval := FBandwidthSampling;
  2119. FBandwidthTimer.OnTimer := BandwidthTimerTimer;
  2120. FBandwidthTimer.Enabled := TRUE;
  2121. // Number of bytes we allow during a sampling period
  2122. FBandwidthMaxCount := Int64(FBandwidthLimit) * FBandwidthSampling div 1000;
  2123. FBandwidthPaused := FALSE;
  2124. {$IFDEF CLR}
  2125. FCtrlSocket.ComponentOptions := FCtrlSocket.ComponentOptions or wsoNoReceiveLoop;
  2126. {$ELSE}
  2127. FCtrlSocket.ComponentOptions := FCtrlSocket.ComponentOptions + [wsoNoReceiveLoop];
  2128. {$ENDIF CLR}
  2129. end;
  2130. {$ENDIF}
  2131. Headers := TStringList.Create;
  2132. try
  2133. FReqStream.Clear;
  2134. TriggerRequestHeaderBegin;
  2135. {* OutputDebugString(method + ' ' + FPath + ' HTTP/' + Version); *}
  2136. if Method = 'CONNECT' then
  2137. Headers.Add(Method + ' ' + FTargetHost + ':' + FTargetPort +
  2138. ' HTTP/' + Version)
  2139. else begin
  2140. Headers.Add(method + ' ' + FPath + ' HTTP/' + Version);
  2141. if FSender <> '' then
  2142. Headers.Add('From: ' + FSender);
  2143. if FAccept <> '' then
  2144. Headers.Add('Accept: ' + FAccept);
  2145. if FReference <> '' then
  2146. Headers.Add('Referer: ' + FReference);
  2147. if FCurrConnection <> '' then
  2148. Headers.Add('Connection: ' + FCurrConnection);
  2149. if FAcceptLanguage <> '' then
  2150. Headers.Add('Accept-Language: ' + FAcceptLanguage);
  2151. {$IFDEF UseContentCoding}
  2152. if (FContentCodingHnd.HeaderText <> '') and (FRequestType <> httpHEAD) then
  2153. Headers.Add('Accept-Encoding: ' + FContentCodingHnd.HeaderText);
  2154. {$ENDIF}
  2155. if ((FRequestType = httpPOST) or (FRequestType = httpPUT)) and
  2156. (FContentPost <> '') then
  2157. Headers.Add('Content-Type: ' + FContentPost);
  2158. {if ((method = 'PUT') or (method = 'POST')) and (FContentPost <> '') then
  2159. Headers.Add('Content-Type: ' + FContentPost);}
  2160. end;
  2161. if FAgent <> '' then
  2162. Headers.Add('User-Agent: ' + FAgent);
  2163. if (FTargetPort = '80') or (FTargetPort = '') then {Maurizio}
  2164. Headers.Add('Host: ' + FTargetHost)
  2165. else
  2166. Headers.Add('Host: ' + FTargetHost + ':' + FTargetPort);
  2167. if FNoCache then
  2168. Headers.Add('Pragma: no-cache');
  2169. if FCurrProxyConnection <> '' then
  2170. Headers.Add('Proxy-Connection: ' + FCurrProxyConnection);
  2171. if (Method = 'CONNECT') then // <= 12/29/05 AG
  2172. Headers.Add('Content-Length: 0') // <= 12/29/05 AG}
  2173. else begin { V7.05 begin }
  2174. if (FRequestType = httpPOST) or (FRequestType = httpPUT) then begin
  2175. {$IFDEF UseNTLMAuthentication}
  2176. if (FAuthNTLMState = ntlmMsg1) or
  2177. (FProxyAuthNTLMState = ntlmMsg1) then
  2178. Headers.Add('Content-Length: 0')
  2179. else
  2180. {$ENDIF}
  2181. Headers.Add('Content-Length: ' +
  2182. IntToStr(SendStream.Size - SendStream.Position));
  2183. end;
  2184. end; { V7.05 end }
  2185. { if (method = 'PUT') or (method = 'POST') then
  2186. Headers.Add('Content-Length: ' + IntToStr(SendStream.Size));}
  2187. if FModifiedSince <> 0 then
  2188. Headers.Add('If-Modified-Since: ' +
  2189. RFC1123_Date(FModifiedSince) + ' GMT');
  2190. {$IFDEF UseNTLMAuthentication}
  2191. if (FProxyAuthNTLMState <> ntlmMsg1) then begin
  2192. if (FAuthNTLMState = ntlmMsg1) then
  2193. Headers.Add(GetNTLMMessage1(FALSE))
  2194. else if (FAuthNTLMState = ntlmMsg3) then
  2195. Headers.Add(GetNTLMMessage3(Method, FALSE))
  2196. {$IFDEF UseDigestAuthentication}
  2197. else if (FAuthDigestState = digestMsg1) then
  2198. Headers.Add(GetDigestAuthorizationHeader(Method, FALSE))
  2199. {$ENDIF}
  2200. else if (FAuthBasicState = basicMsg1) then
  2201. Headers.Add(GetBasicAuthorizationHeader(Method, FALSE))
  2202. else begin
  2203. // Maybe an event to add a preemptive Authorization header?
  2204. end;
  2205. end;
  2206. {$ELSE}
  2207. {$IFDEF UseDigestAuthentication}
  2208. if (FAuthDigestState = digestMsg1) then
  2209. Headers.Add(GetDigestAuthorizationHeader(Method, FALSE))
  2210. else
  2211. {$ENDIF}
  2212. if (FAuthBasicState = basicMsg1) then
  2213. Headers.Add(GetBasicAuthorizationHeader(Method, FALSE))
  2214. else begin
  2215. // Maybe an event to add a preemptive Authorization header?
  2216. end;
  2217. {$ENDIF}
  2218. {$IFDEF UseNTLMAuthentication}
  2219. if (FProxyAuthNTLMState = ntlmMsg1) then
  2220. Headers.Add(GetNTLMMessage1(TRUE))
  2221. else if (FProxyAuthNTLMState = ntlmMsg3) then
  2222. Headers.Add(GetNTLMMessage3(Method, TRUE))
  2223. else
  2224. {$ENDIF}
  2225. {$IFDEF UseDigestAuthentication}
  2226. if (FProxyAuthDigestState = digestMsg1) then
  2227. Headers.Add(GetDigestAuthorizationHeader(Method, TRUE))
  2228. else
  2229. {$ENDIF}
  2230. if (FProxyAuthBasicState = basicMsg1) then
  2231. Headers.Add(GetBasicAuthorizationHeader(Method, TRUE))
  2232. else if Length(FProxy) > 0 then begin
  2233. // Maybe an event to add a preemptive Authorization header?
  2234. end;
  2235. if FCookie <> '' then
  2236. Headers.Add('Cookie: ' + FCookie);
  2237. if (FContentRangeBegin <> '') or (FContentRangeEnd <> '') then begin {JMR!! Added this line!!!}
  2238. Headers.Add('Range: bytes=' + FContentRangeBegin + '-' + FContentRangeEnd); {JMR!! Added this line!!!}
  2239. FContentRangeBegin := ''; {JMR!! Added this line!!!}
  2240. FContentRangeEnd := ''; {JMR!! Added this line!!!}
  2241. end; {JMR!! Added this line!!!}
  2242. FAcceptRanges := '';
  2243. {SendCommand('UA-pixels: 1024x768'); }
  2244. {SendCommand('UA-color: color8'); }
  2245. {SendCommand('UA-OS: Windows 95'); }
  2246. {SendCommand('UA-CPU: x86'); }
  2247. {SendCommand('Proxy-Connection: Keep-Alive'); }
  2248. {$IFNDEF NO_DEBUG_LOG}
  2249. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  2250. DebugLog(loProtSpecInfo, IntToStr(Headers.Count) +
  2251. ' header lines to send'#13#10 + Headers.Text);
  2252. {$ENDIF}
  2253. TriggerBeforeHeaderSend(Method, Headers);
  2254. for N := 0 to Headers.Count - 1 do
  2255. SendCommand(Headers[N]);
  2256. TriggerRequestHeaderEnd;
  2257. SendCommand('');
  2258. FCtrlSocket.PutDataInSendBuffer(FReqStream.Memory, FReqStream.Size);
  2259. FReqStream.Clear;
  2260. FCtrlSocket.Send(nil, 0);
  2261. finally
  2262. Headers.Free;
  2263. {$IFNDEF NO_DEBUG_LOG}
  2264. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  2265. DebugLog(loProtSpecInfo, 'SendRequest Done');
  2266. {$ENDIF}
  2267. end;
  2268. end;
  2269. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  2270. { Data is pointed by FBodyData and FBodyDataLen as length }
  2271. procedure THttpCli.GetBodyLineNext;
  2272. var
  2273. // P : PChar;
  2274. P : Integer; // (FP 09/09/06)
  2275. N, K : THttpBigInt;
  2276. begin
  2277. {$IFNDEF NO_DEBUG_LOG}
  2278. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  2279. DebugLog(loProtSpecInfo, 'GetBodyLineNext begin');
  2280. {$ENDIF}
  2281. if FBodyLineCount = 0 then begin
  2282. FChunkLength := 0;
  2283. FChunkRcvd := 0;
  2284. FChunkState := httpChunkGetSize;
  2285. TriggerDocBegin;
  2286. {$IFDEF UseContentCoding}
  2287. FContentCodingHnd.Prepare(FContentEncoding);
  2288. if Assigned(FRcvdStream) then
  2289. FRcvdStreamStartSize := FRcvdStream.Size
  2290. else
  2291. FRcvdStreamStartSize := 0;
  2292. {$ENDIF}
  2293. end;
  2294. Inc(FBodyLineCount);
  2295. {$IFNDEF NO_DEBUG_LOG}
  2296. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  2297. DebugLog(loProtSpecInfo, 'GetBodyLineNext FBodyDataLen=' + IntToStr(FBodyDataLen));
  2298. {$ENDIF}
  2299. if FTransferEncoding = 'chunked' then begin
  2300. // RFC-2616 3.6.1 Chunked Transfer Coding
  2301. // Chunked-Body = *chunk
  2302. // last-chunk
  2303. // trailer
  2304. // CRLF
  2305. //
  2306. // chunk = chunk-size [ chunk-extension ] CRLF
  2307. // chunk-data CRLF
  2308. // chunk-size = 1*HEX
  2309. // last-chunk = 1*("0") [ chunk-extension ] CRLF
  2310. //
  2311. // chunk-extension = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
  2312. // chunk-ext-name = token
  2313. // chunk-ext-val = token | quoted-string
  2314. // chunk-data = chunk-size(OCTET)
  2315. // trailer = *(entity-header CRLF)
  2316. P := FBodyData;
  2317. N := FBodyDataLen;
  2318. while (N > 0) and (FChunkState <> httpChunkDone) do begin
  2319. if FChunkState = httpChunkGetSize then begin
  2320. while N > 0 do begin
  2321. // if not IsXDigit(P^) then begin
  2322. if not IsXDigit(AnsiChar(FReceiveBuffer[P])) then begin // (FP 09/09/06)
  2323. FChunkState := httpChunkGetExt;
  2324. {$IFNDEF NO_DEBUG_LOG}
  2325. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  2326. DebugLog(loProtSpecInfo, 'ChunkLength = ' + IntToStr(FChunkLength));
  2327. {$ENDIF}
  2328. break;
  2329. end;
  2330. // FChunkLength := FChunkLength * 16 + XDigit(P^);
  2331. FChunkLength := FChunkLength * 16 + XDigit(AnsiChar(FReceiveBuffer[P])); // (FP 09/09/06)
  2332. Inc(P);
  2333. Dec(N);
  2334. end;
  2335. end;
  2336. if FChunkState = httpChunkGetExt then begin
  2337. { Here we simply ignore until next LF }
  2338. while N > 0 do begin
  2339. // if P^ = #10 then begin
  2340. if Ord(FReceiveBuffer[P]) = 10 then begin // FP 09/09/06
  2341. FChunkState := httpChunkGetData;
  2342. Inc(P);
  2343. Dec(N);
  2344. break;
  2345. end;
  2346. Inc(P);
  2347. Dec(N);
  2348. end;
  2349. end;
  2350. if FChunkState = httpChunkGetData then begin
  2351. K := FChunkLength - FChunkRcvd;
  2352. if K > N then
  2353. K := N;
  2354. if K > 0 then begin
  2355. N := N - K;
  2356. FRcvdCount := FRcvdCount + K;
  2357. FChunkRcvd := FChunkRcvd + K;
  2358. {$IFDEF UseContentCoding}
  2359. // FContentCodingHnd.WriteBuffer(P, K);
  2360. FContentCodingHnd.WriteBuffer(@FReceiveBuffer[P], K); // FP 09/09/06
  2361. {$ELSE}
  2362. {$IFDEF CLR}
  2363. if Assigned(FRcvdStream) then
  2364. FRcvdStream.Write(FReceiveBuffer[P], P, K);
  2365. TriggerDocData(@FReceiveBuffer[P], P, K);
  2366. {$ELSE}
  2367. if Assigned(FRcvdStream) then
  2368. FRcvdStream.WriteBuffer(FReceiveBuffer[P], K);
  2369. TriggerDocData(@FReceiveBuffer[P], K);
  2370. {$ENDIF}
  2371. {$ENDIF}
  2372. P := P + K;
  2373. end;
  2374. if FChunkRcvd >= FChunkLength then
  2375. FChunkState := httpChunkSkipDataEnd;
  2376. end;
  2377. if FChunkState = httpChunkSkipDataEnd then begin
  2378. while N > 0 do begin
  2379. // if P^ = #10 then begin
  2380. if Ord(FReceiveBuffer[P]) = 10 then begin // FP 09/09/06
  2381. if FChunkLength = 0 then
  2382. { Last chunk is a chunk with length = 0 }
  2383. FChunkState := httpChunkDone
  2384. else
  2385. FChunkState := httpChunkGetSize;
  2386. FChunkLength := 0;
  2387. FChunkRcvd := 0;
  2388. Inc(P);
  2389. Dec(N);
  2390. break;
  2391. end;
  2392. Inc(P);
  2393. Dec(N);
  2394. end;
  2395. end;
  2396. end;
  2397. if FChunkState = httpChunkDone then begin
  2398. {$IFNDEF NO_DEBUG_LOG}
  2399. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  2400. DebugLog(loProtSpecInfo, 'httpChunkDone, end of document');
  2401. {$ENDIF}
  2402. FBodyLineCount := 0;
  2403. FNext := nil;
  2404. StateChange(httpBodyReceived);
  2405. {$IFDEF UseBandwidthControl}
  2406. if (httpoBandwidthControl in FOptions) and Assigned(FBandwidthTimer)
  2407. then FBandwidthTimer.Enabled := FALSE;
  2408. {$ENDIF}
  2409. {$IFDEF UseContentCoding} {V7.06}
  2410. FContentCodingHnd.Complete;
  2411. {$IFNDEF NO_DEBUG_LOG}
  2412. if CheckLogOptions(loProtSpecInfo) then begin
  2413. if Assigned(FRcvdStream) and (FContentEncoding <> '') then begin
  2414. DebugLog(loProtSpecInfo, FContentEncoding +
  2415. ' content uncompressed from ' +
  2416. IntToStr(FContentLength) + ' bytes to ' +
  2417. IntToStr(FRcvdStream.Size) + ' bytes');
  2418. end;
  2419. end;
  2420. {$ENDIF}
  2421. {$ENDIF}
  2422. TriggerDocEnd;
  2423. if {(FResponseVer = '1.0') or (FRequestVer = '1.0') or }
  2424. { SAE's modification is almost right but if you have HTTP/1.0 }
  2425. { not necesary must disconect after request done }
  2426. { [rawbite 31.08.2004 Connection controll] }
  2427. (FCloseReq) then { SAE 01/06/04 }
  2428. FCtrlSocket.CloseDelayed
  2429. end;
  2430. end
  2431. else begin
  2432. if FBodyDataLen > 0 then begin
  2433. FRcvdCount := FRcvdCount + FBodyDataLen;
  2434. {$IFDEF UseContentCoding}
  2435. // FContentCodingHnd.WriteBuffer(FBodyData, FBodyDataLen);
  2436. FContentCodingHnd.WriteBuffer(@FReceiveBuffer[FBodyData], FBodyDataLen); // FP 09/09/06
  2437. {$ELSE}
  2438. {$IFDEF CLR}
  2439. if Assigned(FRcvdStream) then
  2440. FRcvdStream.Write(FReceiveBuffer[FBodyData], FBodyData, FBodyDataLen);
  2441. TriggerDocData(@FReceiveBuffer[FBodyData], FBodyData, FBodyDataLen);
  2442. {$ELSE}
  2443. if Assigned(FRcvdStream) then
  2444. FRcvdStream.WriteBuffer(FReceiveBuffer[FBodyData], FBodyDataLen);
  2445. TriggerDocData(@FReceiveBuffer[FBodyData], FBodyDataLen);
  2446. {$ENDIF}
  2447. {$ENDIF}
  2448. end;
  2449. if FRcvdCount = FContentLength then begin
  2450. { End of document }
  2451. {$IFNDEF NO_DEBUG_LOG}
  2452. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  2453. DebugLog(loProtSpecInfo, 'End of document');
  2454. {$ENDIF}
  2455. FBodyLineCount := 0;
  2456. FNext := nil;
  2457. StateChange(httpBodyReceived);
  2458. {$IFDEF UseBandwidthControl}
  2459. if (httpoBandwidthControl in FOptions) and
  2460. Assigned(FBandwidthTimer) then
  2461. FBandwidthTimer.Enabled := FALSE;
  2462. {$ENDIF}
  2463. {$IFDEF UseContentCoding}
  2464. FContentCodingHnd.Complete;
  2465. {$IFNDEF NO_DEBUG_LOG}
  2466. if CheckLogOptions(loProtSpecInfo) then begin
  2467. if Assigned(FRcvdStream) and (FContentEncoding <> '') then begin
  2468. DebugLog(loProtSpecInfo, FContentEncoding + ' content uncompressed from ' +
  2469. IntToStr(FContentLength) + ' bytes to ' +
  2470. IntToStr(FRcvdStream.Size) + ' bytes');
  2471. end;
  2472. end;
  2473. {$ENDIF}
  2474. {$ENDIF}
  2475. TriggerDocEnd;
  2476. if {(FResponseVer = '1.0') or (FRequestVer = '1.0') or }
  2477. { see above }
  2478. { [rawbite 31.08.2004 Connection controll] }
  2479. (FCloseReq) then { SAE 01/06/04 }
  2480. FCtrlSocket.CloseDelayed
  2481. else
  2482. CheckDelaySetReady; { 09/26/08 ML }
  2483. end;
  2484. end;
  2485. {$IFNDEF NO_DEBUG_LOG}
  2486. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  2487. DebugLog(loProtSpecInfo, 'GetBodyLineNext end');
  2488. {$ENDIF}
  2489. end;
  2490. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  2491. { If UNICODE is defined each byte in Buffer must be ASCII (ord < 128) ! }
  2492. procedure MoveTBytesToString(
  2493. const Buffer : TBytes;
  2494. OffsetFrom : Integer;
  2495. var Dest : String;
  2496. OffsetTo : Integer;
  2497. Count : Integer);
  2498. {$IFDEF UNICODE}
  2499. var
  2500. PSrc : PByte;
  2501. PDest : PChar;
  2502. begin
  2503. PSrc := Pointer(Buffer);
  2504. PDest := Pointer(Dest);
  2505. Dec(OffsetTo); // String index!
  2506. while Count > 0 do begin
  2507. PDest[OffsetTo] := Char(PSrc[OffsetFrom]);
  2508. Inc(OffsetTo);
  2509. Inc(OffsetFrom);
  2510. Dec(Count);
  2511. end;
  2512. {$ELSE}
  2513. begin
  2514. Move(Buffer[OffsetFrom], Dest[OffsetTo], Count);
  2515. {$ENDIF}
  2516. end;
  2517. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  2518. procedure MoveTBytes(
  2519. var Buffer : Tbytes;
  2520. OffsetFrom : Integer;
  2521. OffsetTo : Integer;
  2522. Count : Integer);
  2523. begin
  2524. {$IFDEF CLR}
  2525. if OffsetFrom > OffsetTo then begin
  2526. while Count > 0 do begin
  2527. Buffer[OffsetTo] := Buffer[OffsetFrom];
  2528. Inc(OffsetFrom);
  2529. Inc(OffsetTo);
  2530. Dec(Count);
  2531. end;
  2532. end
  2533. else if OffsetFrom <> OffsetTo then begin
  2534. Inc(OffsetFrom, Count);
  2535. Inc(OffsetTo, Count);
  2536. while Count > 0 do begin
  2537. Buffer[OffsetTo] := Buffer[OffsetFrom];
  2538. Dec(OffsetFrom);
  2539. Dec(OffsetTo);
  2540. Dec(Count);
  2541. end;
  2542. end;
  2543. {$ELSE}
  2544. Move(Buffer[OffsetFrom], Buffer[OffsetTo], Count);
  2545. {$ENDIF}
  2546. end;
  2547. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  2548. procedure THttpCli.GetHeaderLineNext;
  2549. var
  2550. proto : String;
  2551. user : String;
  2552. pass : String;
  2553. port : String;
  2554. Host : String;
  2555. Path : String;
  2556. Field : String;
  2557. Data : String;
  2558. nSep : Integer;
  2559. tmpInt : LongInt;
  2560. bAccept : Boolean;
  2561. DocExt : String;
  2562. begin
  2563. if FHeaderLineCount = 0 then
  2564. TriggerHeaderBegin
  2565. else if FHeaderLineCount = -1 then { HTTP/1.1 second header }
  2566. FHeaderLineCount := 0;
  2567. Inc(FHeaderLineCount);
  2568. { Some server send HTML document without header ! I don't know if it is }
  2569. { legal, but it exists (AltaVista Discovery does that). }
  2570. if (FHeaderLineCount = 1) and
  2571. (FLastResponse <> '') and
  2572. (FLastResponse[1] = '<') and
  2573. (SameText(Copy(FLastResponse, 1, 6), '<HTML>') or
  2574. SameText(Copy(FLastResponse, 1, 9), '<!DOCTYPE')) then begin
  2575. if FContentType = '' then
  2576. FContentType := 'text/html';
  2577. StateChange(httpWaitingBody);
  2578. FNext := GetBodyLineNext;
  2579. TriggerHeaderEnd;
  2580. // FBodyData := @FLastResponse[1]; FP 09/09/06
  2581. // FBodyDataLen := Length(FLastResponse); FP 09/09/06
  2582. GetBodyLineNext;
  2583. Exit;
  2584. end;
  2585. if FLastResponse = '' then begin
  2586. {$IFNDEF NO_DEBUG_LOG}
  2587. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  2588. DebugLog(loProtSpecInfo, 'End of header');
  2589. {$ENDIF}
  2590. if (FResponseVer = '1.1') and (FStatusCode = 100) then begin
  2591. { HTTP/1.1 continue message. A second header follow. }
  2592. { I should create an event to give access to this. }
  2593. FRcvdHeader.Clear; { Cancel this first header }
  2594. FHeaderLineCount := -1; { -1 is to remember we went here }
  2595. Exit;
  2596. end;
  2597. TriggerHeaderEnd; { 28/12/2003 }
  2598. if FState = httpReady then begin { 05/02/2005 }
  2599. { It is likely that Abort has been called in OnHeaderEnd event }
  2600. FReceiveLen := 0; { Clear any buffered data }
  2601. Exit;
  2602. end;
  2603. { FContentLength = -1 when server doesn't send a value }
  2604. if ((FContentLength = -1) and { Added 12/03/2004 }
  2605. (FTransferEncoding <> 'chunked') and { Added 09/10/2006 by FP }
  2606. ((FStatusCode < 200) or { Added 12/03/2004 }
  2607. (FStatusCode = 204) or { Added 12/03/2004 }
  2608. (FStatusCode = 301) or { Added 06/10/2004 }
  2609. (FStatusCode = 302) or { Added 06/10/2004 }
  2610. (FStatusCode = 304) or { Added 12/03/2004 }
  2611. { AFAIR, next two lines have been added since it might }
  2612. { happen that a body is not sent with both responses. }
  2613. { Unfortunately we truncate body data in those cases }
  2614. { and we have to ensure that FReceiveLen is cleared }
  2615. { properly. If it's not done below it's now done in }
  2616. { SocketSessionClosed() a little bit later anyway. (AG)}
  2617. (FStatusCode = 401) or { Added 12/28/2005 } //AG 12/28/05
  2618. (FStatusCode = 407))) { Added 12/28/2005 } //AG 12/28/05
  2619. or
  2620. (FContentLength = 0)
  2621. or
  2622. (FRequestType = httpHEAD) then begin
  2623. { TriggerHeaderEnd; }{ Removed 10/01/2004 }
  2624. if {(FResponseVer = '1.0') or (FRequestVer = '1.0') or}
  2625. { [rawbite 31.08.2004 Connection controll] }
  2626. FCloseReq then begin
  2627. if FLocationFlag then { Added 16/02/2004 }
  2628. StartRelocation { Added 16/02/2004 }
  2629. else begin { Added 16/02/2004 }
  2630. if FRequestType = httpHEAD then begin { Added 23/07/04 }
  2631. { With HEAD command, we don't expect a document }
  2632. { but some server send one ! }
  2633. FReceiveLen := 0; { Cancel received data }
  2634. StateChange(httpWaitingBody);
  2635. FNext := nil;
  2636. end;
  2637. FCtrlSocket.CloseDelayed; { Added 10/01/2004 }
  2638. end;
  2639. end
  2640. else if FRequestType = httpHEAD then begin //AG 05/27/08
  2641. { With HEAD command, we don't expect a document } //AG 05/27/08
  2642. { but some server send one ! } //AG 05/27/08
  2643. FReceiveLen := 0; { Cancel received data } //AG 05/27/08
  2644. StateChange(httpWaitingBody); //AG 05/27/08
  2645. FNext := nil; //AG 05/27/08
  2646. SetReady; //AG 05/27/08
  2647. end //AG 05/27/08
  2648. else
  2649. CheckDelaySetReady; { 09/26/08 ML }
  2650. Exit;
  2651. end;
  2652. DocExt := LowerCase(ExtractFileExt(FDocName));
  2653. if (DocExt = '.exe') or (DocExt = '') then begin
  2654. if FContentType = 'text/html' then
  2655. ReplaceExt(FDocName, 'htm');
  2656. end;
  2657. StateChange(httpWaitingBody);
  2658. FNext := GetBodyLineNext;
  2659. {TriggerHeaderEnd; Removed 11/11/04 because it is already trigger above }
  2660. if FReceiveLen > 0 then begin
  2661. // FBodyData := FReceiveBuffer;
  2662. FBodyData := 0; // FP 09/09/06
  2663. if (FContentLength < 0) or
  2664. ((FRcvdCount + FReceiveLen) <= FContentLength) then
  2665. FBodyDataLen := FReceiveLen
  2666. else
  2667. FBodyDataLen := FContentLength - FRcvdCount; {****}
  2668. GetBodyLineNext;
  2669. FReceiveLen := FReceiveLen - FBodyDataLen;
  2670. { Move remaining data to start of buffer. 17/01/2004 }
  2671. if FReceiveLen > 0 then
  2672. MoveTBytes(FReceiveBuffer, FBodyDataLen, 0, FReceiveLen + 1);
  2673. end;
  2674. if not Assigned(FNext) then begin
  2675. { End of document }
  2676. if FLocationFlag then
  2677. StartRelocation
  2678. else
  2679. CheckDelaySetReady; { 09/26/08 ML }
  2680. end;
  2681. { if FStatusCode >= 400 then } { 01/11/01 }
  2682. { FCtrlSocket.Close; }
  2683. Exit;
  2684. end;
  2685. FRcvdHeader.Add(FLastResponse);
  2686. nSep := Pos(':', FLastResponse);
  2687. if (FHeaderLineCount = 1) then begin
  2688. if (Copy(FLastResponse, 1, 8) = 'HTTP/1.0') or
  2689. (Copy(FLastResponse, 1, 8) = 'HTTP/1.1') then begin
  2690. FResponseVer := Copy(FLastResponse, 6, 3);
  2691. { Reset the default FCloseReq flag depending on the response 12/29/05 AG }
  2692. if (FRequestVer = '1.1') and (FResponseVer = '1.0') then
  2693. FCloseReq := TRUE
  2694. else begin
  2695. if FRequestVer = '1.0' then
  2696. FCloseReq := TRUE
  2697. else if FRequestVer = '1.1' then
  2698. FCloseReq := FALSE;
  2699. end;
  2700. {$IFNDEF NO_DEBUG_LOG} { V1.91 }
  2701. if CheckLogOptions(loProtSpecDump) then begin
  2702. DebugLog(loProtSpecDump, 'FCloseReq=' + IntToStr(Ord(FCloseReq)));
  2703. end;
  2704. {$ENDIF}
  2705. { Changed 12/22/05 AG - M$ Proxy 2.0 invalid status-line ("HTTP/1.0 200") }
  2706. tmpInt := 9;
  2707. while Length(FLastResponse) > tmpInt do begin
  2708. Inc(tmpInt);
  2709. if AnsiChar(FLastResponse[tmpInt]) in ['0'..'9'] then
  2710. break;
  2711. end;
  2712. FStatusCode := StrToInt(Copy(FLastResponse, tmpInt, 3));
  2713. FReasonPhrase := Copy(FLastResponse, tmpInt + 4, Length(FLastResponse));
  2714. { Changed end }
  2715. end
  2716. else begin
  2717. { Received data but not part of a header }
  2718. if Assigned(FOnDataPush2) then
  2719. FOnDataPush2(Self);
  2720. end;
  2721. end
  2722. else if nSep > 0 then begin
  2723. Field := LowerCase(Copy(FLastResponse, 1, nSep - 1));
  2724. { Skip spaces }
  2725. Inc(nSep);
  2726. while (nSep < Length(FLastResponse)) and
  2727. (FLastResponse[nSep] = ' ') do
  2728. Inc(nSep);
  2729. Data := Copy(FLastResponse, nSep, Length(FLastResponse));
  2730. if Field = 'location' then begin { Change the URL ! }
  2731. if FRequestType = httpPUT then begin
  2732. { Location just tell us where the document has been stored }
  2733. FLocation := Data;
  2734. end
  2735. else if FFollowRelocation then begin {TED}
  2736. { OK, we have a real relocation ! }
  2737. { URL with relocations: }
  2738. { http://www.webcom.com/~wol2wol/ }
  2739. { http://www.purescience.com/delphi/ }
  2740. { http://www.maintron.com/ }
  2741. { http://www.infoseek.com/AddURL/addurl }
  2742. { http://www.micronpc.com/ }
  2743. { http://www.amazon.com/ }
  2744. { http://count.paycounter.com/?fn=0&si=44860&bt=msie&bv=5& }
  2745. { co=32&js=1.4&sr=1024x768&re=http://www.thesite.com/you.html }
  2746. FLocationFlag := TRUE;
  2747. if Proxy <> '' then begin
  2748. { We are using a proxy }
  2749. if Data[1] = '/' then begin
  2750. { Absolute location }
  2751. ParseURL(FPath, proto, user, pass, Host, port, Path);
  2752. if Proto = '' then
  2753. Proto := 'http';
  2754. FLocation := Proto + '://' + Host + Data;
  2755. FPath := FLocation;
  2756. if (user <> '') and (pass <> '') then begin
  2757. { save user and password given in location @@@}
  2758. FCurrUsername := user;
  2759. FCurrPassword := pass;
  2760. end;
  2761. end
  2762. else if (CompareText(Copy(Data, 1, 7), 'http://') <> 0)
  2763. and { 05/02/2005 }
  2764. (CompareText(Copy(Data, 1, 8), 'https://') <> 0)
  2765. then begin
  2766. { Relative location }
  2767. FPath := GetBaseUrl(FPath) + Data;
  2768. { if Proto = '' then
  2769. Proto := 'http';
  2770. FLocation := Proto + '://' + FHostName + '/' + FPath;
  2771. }
  2772. FLocation := FPath;
  2773. end
  2774. else begin
  2775. ParseURL(Data, proto, user, pass, Host, port, Path);
  2776. if port <> '' then
  2777. FPort := port
  2778. else begin
  2779. {$IFDEF USE_SSL}
  2780. if proto = 'https' then
  2781. FPort := '443'
  2782. else
  2783. {$ENDIF}
  2784. FPort := '80';
  2785. end;
  2786. if (user <> '') and (pass <> '') then begin
  2787. { save user and password given in location @@@}
  2788. FCurrUsername := user;
  2789. FCurrPassword := pass;
  2790. end;
  2791. if (Proto <> '') and (Host <> '') then begin
  2792. { We have a full relocation URL }
  2793. FTargetHost := Host;
  2794. FLocation := Proto + '://' + Host + Path;
  2795. FPath := FLocation;
  2796. end
  2797. else begin
  2798. if Proto = '' then
  2799. Proto := 'http';
  2800. if FPath = '' then
  2801. FLocation := Proto + '://' + FTargetHost + '/' + Host
  2802. else if Host = '' then
  2803. FLocation := Proto + '://' + FTargetHost + FPath
  2804. else
  2805. FTargetHost := Host;
  2806. end;
  2807. end;
  2808. end
  2809. { We are not using a proxy }
  2810. else begin
  2811. ParseURL(FURL, proto, user, pass, Host, port, Path); { V7.03 }
  2812. if Data[1] = '/' then begin
  2813. { Absolute location }
  2814. FPath := Data;
  2815. if Proto = '' then
  2816. Proto := 'http';
  2817. FLocation := Proto + '://' + FHostName + FPath;
  2818. end
  2819. else if (CompareText(Copy(Data, 1, 7), 'http://') <> 0)
  2820. and { 05/02/2005 }
  2821. (CompareText(Copy(Data, 1, 8), 'https://') <> 0)
  2822. then begin
  2823. { Relative location }
  2824. FPath := GetBaseUrl(FPath) + Data;
  2825. if Proto = '' then
  2826. Proto := 'http';
  2827. FLocation := Proto + '://' + FHostName + {'/' +} FPath;
  2828. end
  2829. else begin
  2830. ParseURL(Data, proto, user, pass, FHostName, port, FPath);
  2831. if port <> '' then
  2832. FPort := port
  2833. else begin
  2834. {$IFDEF USE_SSL}
  2835. if proto = 'https' then
  2836. FPort := '443'
  2837. else
  2838. {$ENDIF}
  2839. FPort := '80';
  2840. end;
  2841. FProtocol := Proto;
  2842. if (user <> '') and (pass <> '') then begin
  2843. { save user and password given in location @@@}
  2844. FCurrUsername := user;
  2845. FCurrPassword := pass;
  2846. end;
  2847. if (Proto <> '') and (FHostName <> '') then begin
  2848. { We have a full relocation URL }
  2849. FTargetHost := FHostName;
  2850. if FPath = '' then begin
  2851. FPath := '/';
  2852. FLocation := Proto + '://' + FHostName;
  2853. end
  2854. else
  2855. FLocation := Proto + '://' + FHostName + FPath;
  2856. end
  2857. else begin
  2858. if Proto = '' then
  2859. Proto := 'http';
  2860. if FPath = '' then begin
  2861. FLocation := Proto + '://' + FTargetHost + '/' + FHostName;
  2862. FHostName := FTargetHost;
  2863. FPath := FLocation; { 26/11/99 }
  2864. end
  2865. else if FHostName = '' then begin
  2866. FLocation := Proto + '://' + FTargetHost + FPath;
  2867. FHostName := FTargetHost;
  2868. end
  2869. else
  2870. FTargetHost := FHostName;
  2871. end;
  2872. end;
  2873. end;
  2874. end;
  2875. end
  2876. else if Field = 'content-length' then
  2877. FContentLength := StrToInt64Def(Trim(Data), -1)
  2878. else if Field = 'transfer-encoding' then
  2879. FTransferEncoding := LowerCase(Data)
  2880. else if Field = 'content-range' then begin {JMR!! Added this line!!!}
  2881. tmpInt := Pos('-', Data) + 1; {JMR!! Added this line!!!}
  2882. FContentRangeBegin := Copy(Data, 7, tmpInt-8); {JMR!! Added this line!!!}
  2883. FContentRangeEnd := Copy(Data, tmpInt, Pos('/', Data) - tmpInt); {JMR!! Added this line!!!}
  2884. end {JMR!! Added this line!!!}
  2885. else if Field = 'accept-ranges' then
  2886. FAcceptRanges := Data
  2887. else if Field = 'content-type' then
  2888. FContentType := LowerCase(Data)
  2889. else if Field = 'www-authenticate' then
  2890. FDoAuthor.add(Data)
  2891. else if Field = 'proxy-authenticate' then { BLD required for proxy NTLM authentication }
  2892. FDoAuthor.add(Data)
  2893. else if Field = 'set-cookie' then begin
  2894. bAccept := TRUE;
  2895. TriggerCookie(Data, bAccept);
  2896. end
  2897. { rawbite 31.08.2004 Connection controll }
  2898. else if (Field = 'connection') or
  2899. (Field = 'proxy-connection') then begin
  2900. if (LowerCase(Trim(Data)) = 'close') then
  2901. FCloseReq := TRUE
  2902. else if (LowerCase(Trim(Data)) = 'keep-alive') then
  2903. FCloseReq := FALSE;
  2904. end
  2905. { else if Field = 'date' then }
  2906. { else if Field = 'mime-version' then }
  2907. { else if Field = 'pragma' then }
  2908. { else if Field = 'allow' then }
  2909. { else if Field = 'server' then }
  2910. { else if Field = 'content-encoding' then }
  2911. {$IFDEF UseContentCoding}
  2912. else if Field = 'content-encoding' then
  2913. FContentEncoding := Data
  2914. {$ENDIF}
  2915. { else if Field = 'expires' then }
  2916. { else if Field = 'last-modified' then }
  2917. end
  2918. else { Ignore all other responses }
  2919. ;
  2920. if Assigned(FOnHeaderData) then
  2921. FOnHeaderData(Self);
  2922. { if FStatusCode >= 400 then Moved above 01/11/01 }
  2923. { FCtrlSocket.Close; }
  2924. end;
  2925. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  2926. procedure THttpCli.InternalClear;
  2927. begin
  2928. FRcvdHeader.Clear;
  2929. FRequestDoneError := 0;
  2930. FDocName := '';
  2931. FStatusCode := 0;
  2932. FRcvdCount := 0;
  2933. FSentCount := 0;
  2934. FHeaderLineCount := 0;
  2935. FBodyLineCount := 0;
  2936. FContentLength := -1;
  2937. FContentType := ''; { 25/09/1999 }
  2938. FTransferEncoding := ''; { 28/12/2003 }
  2939. {$IFDEF UseContentCoding}
  2940. FContentEncoding := '';
  2941. {$ENDIF}
  2942. FAllowedToSend := FALSE;
  2943. FDelaySetReady := FALSE; { 09/26/08 ML }
  2944. FLocation := FURL;
  2945. FDoAuthor.Clear;
  2946. { if protocol version is 1.0 then we suppose that the connection must be }
  2947. { closed. If server response will contain a Connection: keep-alive header }
  2948. { we will set it to False. }
  2949. { If protocol version is 1.1 then we suppose that the connection is kept }
  2950. { alive. If server response will contain a Connection: close we will set }
  2951. { it to True. }
  2952. if FRequestVer = '1.0' then
  2953. FCloseReq := TRUE { SAE 01/06/04 }
  2954. else
  2955. FCloseReq := FALSE { [rawbite 31.08.2004 Connection controll] }
  2956. end;
  2957. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  2958. procedure THttpCli.DoRequestAsync(Rq : THttpRequest);
  2959. var
  2960. Proto, User, Pass, Host, Port, Path: String;
  2961. begin
  2962. if (Rq <> httpCLOSE) and (FState <> httpReady) then
  2963. raise EHttpException.Create('HTTP component ' + Name + ' is busy', httperrBusy);
  2964. if ((Rq = httpPOST) or (Rq = httpPUT)) and
  2965. (not Assigned(FSendStream)
  2966. { or (FSendStream.Position = FSendStream.Size)} { Removed 21/03/05 }
  2967. ) then
  2968. raise EHttpException.Create('HTTP component has nothing to post or put',
  2969. httpErrNoData);
  2970. if Rq = httpCLOSE then begin
  2971. FStatusCode := 200;
  2972. FReasonPhrase := 'OK';
  2973. StateChange(httpClosing);
  2974. if FCtrlSocket.State = wsClosed then
  2975. SetReady
  2976. else
  2977. FCtrlSocket.CloseDelayed;
  2978. Exit;
  2979. end;
  2980. { Clear all internal state variables }
  2981. FRequestType := Rq;
  2982. InternalClear;
  2983. FCurrUsername := FUsername;
  2984. FCurrPassword := FPassword;
  2985. FCurrConnection := FConnection;
  2986. FCurrProxyConnection := FProxyConnection;
  2987. { Parse url and proxy to FHostName, FPath and FPort }
  2988. if FProxy <> '' then begin
  2989. ParseURL(FURL, Proto, User, Pass, Host, Port, Path);
  2990. FTargetHost := Host;
  2991. FTargetPort := Port;
  2992. if FTargetPort = '' then begin
  2993. {$IFDEF USE_SSL}
  2994. if Proto = 'https' then
  2995. FTargetPort := '443'
  2996. else
  2997. {$ENDIF}
  2998. FTargetPort := '80';
  2999. end;
  3000. FPath := FURL;
  3001. FDocName := Path;
  3002. if User <> '' then
  3003. FCurrUserName := User;
  3004. if Pass <> '' then
  3005. FCurrPassword := Pass;
  3006. { We need to remove usercode/Password from the URL given to the proxy }
  3007. { but preserve the port }
  3008. if Port <> '' then
  3009. Port := ':' + Port;
  3010. if Proto = '' then
  3011. FPath := 'http://'+ Host + Port + Path
  3012. else
  3013. FPath := Proto + '://' + Host + Port + Path;
  3014. FProtocol := Proto;
  3015. ParseURL(FProxy, Proto, User, Pass, Host, Port, Path);
  3016. if Port = '' then
  3017. Port := ProxyPort;
  3018. end
  3019. else begin
  3020. ParseURL(FURL, Proto, User, Pass, Host, Port, FPath);
  3021. FTargetHost := Host;
  3022. FDocName := FPath;
  3023. FProtocol := Proto;
  3024. if User <> '' then
  3025. FCurrUserName := User;
  3026. if Pass <> '' then
  3027. FCurrPassword := Pass;
  3028. if Port = '' then begin
  3029. {$IFDEF USE_SSL}
  3030. if Proto = 'https' then
  3031. Port := '443'
  3032. else
  3033. {$ENDIF}
  3034. Port := '80';
  3035. end;
  3036. FTargetPort := Port; {added 11/13/2005 AG}
  3037. end;
  3038. if FProtocol = '' then
  3039. FProtocol := 'http';
  3040. if Proto = '' then
  3041. Proto := 'http';
  3042. if FPath = '' then
  3043. FPath := '/';
  3044. AdjustDocName;
  3045. FHostName := Host;
  3046. FPort := Port;
  3047. {$IFDEF UseNTLMAuthentication}
  3048. FAuthNTLMState := ntlmNone;
  3049. FProxyAuthNTLMState := ntlmNone;
  3050. {$ENDIF}
  3051. {$IFDEF UseDigestAuthentication}
  3052. FAuthDigestState := digestNone;
  3053. FProxyAuthDigestState := digestNone;
  3054. {$ENDIF}
  3055. FAuthBasicState := basicNone;
  3056. FProxyAuthBasicState := basicNone;
  3057. if (FProxy <> '') and (FProxyAuth <> httpAuthNone) and
  3058. (FProxyUsername <> '') and (FProxyPassword <> '') then begin
  3059. { If it is still connected there is no need to restart the
  3060. authentication on the proxy }
  3061. if (FCtrlSocket.State = wsConnected) and
  3062. (FHostName = FCurrentHost) and
  3063. (FPort = FCurrentPort) and
  3064. (FProtocol = FCurrentProtocol) then begin
  3065. {$IFDEF UseNTLMAuthentication}
  3066. if FProxyAuth <> httpAuthNtlm then begin
  3067. FProxyAuthNTLMState := ntlmDone;
  3068. if (FRequestVer = '1.0') or (FResponseVer = '1.0') or // <== 12/29/05 AG
  3069. (FResponseVer = '') then // <== 12/29/05 AG
  3070. FCurrProxyConnection := 'Keep-alive';
  3071. end
  3072. else
  3073. {$ENDIF}
  3074. {$IFDEF UseDigestAuthentication}
  3075. if FProxyAuth = httpAuthDigest then
  3076. FProxyAuthDigestState := digestDone
  3077. else
  3078. {$ENDIF}
  3079. if FProxyAuth = httpAuthBasic then
  3080. FProxyAuthBasicState := basicDone;
  3081. end
  3082. else begin
  3083. {$IFDEF UseNTLMAuthentication}
  3084. if FProxyAuth = httpAuthNtlm then begin
  3085. FProxyAuthNTLMState := ntlmMsg1;
  3086. if (FRequestVer = '1.0') or (FResponseVer = '1.0') or // <== 12/29/05 AG
  3087. (FResponseVer = '') then // <== 12/29/05 AG
  3088. FCurrProxyConnection := 'Keep-alive';
  3089. end
  3090. else
  3091. {$ENDIF}
  3092. {$IFDEF UseDigestAuthentication}
  3093. if FProxyAuth = httpAuthDigest then
  3094. FProxyAuthDigestState := digestMsg1
  3095. else
  3096. {$ENDIF}
  3097. if FProxyAuth = httpAuthBasic then
  3098. FProxyAuthBasicState := basicMsg1;
  3099. end;
  3100. end;
  3101. if (FServerAuth <> httpAuthNone) and (FCurrUsername <> '') and
  3102. (FCurrPassword <> '') then begin
  3103. {$IFDEF UseNTLMAuthentication}
  3104. if FServerAuth = httpAuthNtlm then begin
  3105. FAuthNTLMState := ntlmMsg1;
  3106. if (FRequestVer = '1.0') or (FResponseVer = '1.0') or // <== 12/29/05 AG
  3107. (FResponseVer = '') then // <== 12/29/05 AG
  3108. FCurrConnection := 'Keep-alive';
  3109. end
  3110. else
  3111. {$ENDIF}
  3112. {$IFDEF UseDigestAuthentication}
  3113. if FServerAuth = httpAuthDigest then
  3114. FAuthDigestState := digestMsg1
  3115. else
  3116. {$ENDIF}
  3117. if FServerAuth = httpAuthBasic then
  3118. FAuthBasicState := basicMsg1;
  3119. end;
  3120. if FCtrlSocket.State = wsConnected then begin
  3121. if (FHostName = FCurrentHost) and
  3122. (FPort = FCurrentPort) and
  3123. (FProtocol = FCurrentProtocol) then begin
  3124. { We are already connected to the right host ! }
  3125. SocketSessionConnected(Self, 0);
  3126. Exit;
  3127. end;
  3128. { Connected to another website. Abort the connection }
  3129. FCtrlSocket.Abort;
  3130. end;
  3131. FProxyConnected := FALSE;
  3132. { Ask to connect. When connected, we go at SocketSeesionConnected. }
  3133. StateChange(httpNotConnected);
  3134. Login;
  3135. end;
  3136. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  3137. procedure THttpCli.AdjustDocName;
  3138. var
  3139. I : Integer;
  3140. begin
  3141. I := Pos('?', FDocName);
  3142. if I > 0 then
  3143. FDocName := Copy(FDocName, 1, I - 1);
  3144. if (FDocName = '') or (FDocName[Length(FDocName)] = '/') then
  3145. FDocName := 'document.htm'
  3146. else begin
  3147. if FDocName[Length(FDocName)] = '/' then
  3148. SetLength(FDocName, Length(FDocName) - 1);
  3149. FDocName := Copy(FDocName, Posn('/', FDocName, -1) + 1, 255);
  3150. I := Pos('?', FDocName);
  3151. if I > 0 then
  3152. FDocName := Copy(FDocName, 1, I - 1);
  3153. end;
  3154. end;
  3155. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  3156. procedure THttpCli.DoRequestSync(Rq : THttpRequest); { V7.04 Timeout added }
  3157. var
  3158. DummyHandle : {$IFDEF CLR}Borland.Vcl.Windows.THandle;
  3159. {$ELSE} THandle;{$ENDIF}
  3160. TimeOutMsec : UINT;
  3161. bFlag : Boolean;
  3162. begin
  3163. DoRequestAsync(Rq);
  3164. if not Assigned(FCtrlSocket.Counter) then
  3165. FCtrlSocket.CreateCounter;
  3166. FCtrlSocket.Counter.SetConnected; // Reset counter
  3167. DummyHandle := INVALID_HANDLE_VALUE;
  3168. TimeOutMsec := FTimeOut * 1000;
  3169. while FState <> httpReady do begin
  3170. if MsgWaitForMultipleObjects(0, DummyHandle, FALSE, 1000,
  3171. QS_ALLINPUT) = WAIT_OBJECT_0 then
  3172. FCtrlSocket.MessagePump;
  3173. if (FState <> httpReady) and (
  3174. {$IFNDEF NOFORMS} Application.Terminated or {$ENDIF} FTerminated or
  3175. (IcsCalcTickDiff(FCtrlSocket.Counter.LastAliveTick,
  3176. GetTickCount) >= TimeOutMsec)) then begin
  3177. bFlag := (FState = httpDnsLookup);
  3178. StateChange(httpAborting);
  3179. if bFlag then
  3180. try
  3181. FCtrlSocket.CancelDnsLookup;
  3182. except
  3183. { Ignore any exception }
  3184. end;
  3185. FStatusCode := 404;
  3186. if {$IFNDEF NOFORMS} Application.Terminated or {$ENDIF}
  3187. FTerminated then begin
  3188. FReasonPhrase := 'Request aborted';
  3189. FRequestDoneError := httperrAborted;
  3190. end
  3191. else begin
  3192. FReasonPhrase := 'Request aborted on timeout';
  3193. FRequestDoneError := httperrCustomTimeOut;
  3194. end;
  3195. if bFlag then
  3196. SocketSessionClosed(Self, 0)
  3197. else
  3198. FCtrlSocket.Close;
  3199. StateChange(httpReady);
  3200. end;
  3201. end;
  3202. {* Jul 12, 2004
  3203. WARNING: The component now doesn't consider 401 status
  3204. as a fatal error (no exception is triggered). This required a
  3205. change in the application code if it was using the exception that
  3206. is no more triggered for status 401 and 407.
  3207. *}
  3208. {* if FStatusCode > 401 then Dec 14, 2004 *}
  3209. if (FStatusCode >= 400) and (FStatusCode <> 401) and (FStatusCode <> 407) then
  3210. raise EHttpException.Create(FReasonPhrase, FStatusCode);
  3211. end;
  3212. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  3213. procedure THttpCli.LocationSessionClosed(Sender: TObject; ErrCode: Word);
  3214. var
  3215. Proto, User, Pass, Host, Port, Path : String;
  3216. RealLocation : String;
  3217. I : Integer;
  3218. AllowMoreRelocations : Boolean;
  3219. begin
  3220. { Remove any bookmark from the URL }
  3221. I := Pos('#', FLocation);
  3222. if I > 0 then
  3223. RealLocation := Copy(FLocation, 1, I - 1)
  3224. else
  3225. RealLocation := FLocation;
  3226. { Parse the URL }
  3227. ParseURL(RealLocation, Proto, User, Pass, Host, Port, Path);
  3228. FDocName := Path;
  3229. AdjustDocName;
  3230. FConnected := FALSE;
  3231. FProxyConnected := FALSE;
  3232. FLocationFlag := FALSE;
  3233. { When relocation occurs doing a POST, new relocated page has to be GET }
  3234. if FRequestType = httpPOST then
  3235. FRequestType := httpGET;
  3236. { Restore normal session closed event }
  3237. FCtrlSocket.OnSessionClosed := SocketSessionClosed;
  3238. { V1.90 25 Nov 2005 - restrict number of relocations to avoid continuous loops }
  3239. inc (FLocationChangeCurCount) ;
  3240. if FLocationChangeCurCount > FLocationChangeMaxCount then begin
  3241. AllowMoreRelocations := false;
  3242. if Assigned (FOnLocationChangeExceeded) then
  3243. FOnLocationChangeExceeded(Self, FLocationChangeCurCount,
  3244. AllowMoreRelocations) ;
  3245. if not AllowMoreRelocations then begin
  3246. SetReady; { angus V7.08 }
  3247. exit;
  3248. end;
  3249. end ;
  3250. { Trigger the location changed event }
  3251. if Assigned(FOnLocationChange) then
  3252. FOnLocationChange(Self);
  3253. { Clear header from previous operation }
  3254. FRcvdHeader.Clear;
  3255. { Clear status variables from previous operation }
  3256. FHeaderLineCount := 0;
  3257. FBodyLineCount := 0;
  3258. FContentLength := -1;
  3259. FContentType := '';
  3260. FStatusCode := 0;
  3261. FTransferEncoding := ''; { 28/12/2003 }
  3262. {$IFDEF UseContentCoding}
  3263. FContentEncoding := '';
  3264. {$ENDIF}
  3265. { Adjust for proxy use (Fixed: Nov 10, 2001) }
  3266. if FProxy <> '' then
  3267. FPort := ProxyPort;
  3268. { Must clear what we already received }
  3269. CleanupRcvdStream; {11/11/04}
  3270. CleanupSendStream;
  3271. { Restart at login procedure }
  3272. PostMessage(Handle, FMsg_WM_HTTP_LOGIN, 0, 0);
  3273. end;
  3274. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  3275. procedure THttpCli.WMHttpLogin(var msg: TMessage);
  3276. begin
  3277. Login;
  3278. end;
  3279. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  3280. procedure THttpCli.SocketSessionClosed(Sender: TObject; ErrCode: Word);
  3281. begin
  3282. {$IFNDEF NO_DEBUG_LOG}
  3283. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  3284. DebugLog(loProtSpecInfo, 'SessionClosed Error: ' + IntToStr(ErrCode));
  3285. {$ENDIF}
  3286. FReceiveLen := 0; { AG 11 Jan 2009 always clear the buffer! }
  3287. if ErrCode <> 0 then { WM 15 sep 2002 }
  3288. FRequestDoneError := ErrCode; { WM 15 sep 2002 }
  3289. FConnected := FALSE;
  3290. FProxyConnected := FALSE;
  3291. if FHeaderEndFlag then begin
  3292. { TriggerHeaderEnd has not been called yet }
  3293. TriggerHeaderEnd;
  3294. if FLocationFlag then { 28/10/01 }
  3295. LocationSessionClosed(Self, 0);
  3296. Exit;
  3297. end;
  3298. if FBodyLineCount > 0 then begin
  3299. {$IFDEF UseBandwidthControl}
  3300. if (httpoBandwidthControl in FOptions) and Assigned(FBandwidthTimer)
  3301. then FBandwidthTimer.Enabled := FALSE;
  3302. {$ENDIF}
  3303. TriggerDocEnd;
  3304. end;
  3305. { Fix proposed by Corey Murtagh 20/08/2004 "POST freezing in httpWaitingBody" }
  3306. { Also fix a problem when a relocation occurs without document. }
  3307. { Conditional compile will compile this fix by default. It's there because I }
  3308. { don't want to delete the original code before confirming everything is OK. }
  3309. {$IFNDEF DO_NOT_USE_COREY_FIX}
  3310. if FLocationFlag then
  3311. LocationSessionClosed(Self, 0)
  3312. else begin
  3313. TriggerSessionClosed;
  3314. if FState <> httpReady then
  3315. SetReady;
  3316. end;
  3317. {$ELSE}
  3318. TriggerSessionClosed;
  3319. if (not FLocationFlag) and (FState <> httpReady) then
  3320. { if you don't verify if component is in ready state, }
  3321. { OnRequestDone will be fired twice in some cases }
  3322. SetReady;
  3323. {$ENDIF}
  3324. end;
  3325. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  3326. procedure THttpCli.SocketDataAvailable(Sender: TObject; ErrCode: Word);
  3327. var
  3328. Len : Integer;
  3329. I : Integer;
  3330. // VoidBuf : String; FP 09/09/06
  3331. {$IFDEF CLR}
  3332. TempBuf : TBytes;
  3333. {$ENDIF}
  3334. const
  3335. BUF_SIZE = 8192;
  3336. begin
  3337. {$IFDEF NEVER} // FP 09/09/06
  3338. I := SizeOf(FReceiveBuffer) - FReceiveLen - 3; { Preserve space for #13#10#0 }
  3339. if I <= 0 then begin
  3340. { 22/12/2004, ignore line too long instead of raising an exception }
  3341. { raise EHttpException.Create('HTTP line too long', httperrOverflow); }
  3342. { We receive in a small buffer because this length will be discarded }
  3343. { from already received data. }
  3344. SetLength(VoidBuf, 25);
  3345. Len := FCtrlSocket.Receive(@VoidBuf[1], Length(VoidBuf));
  3346. SetLength(VoidBuf, Len);
  3347. { Check if we received the end of line }
  3348. I := Pos(#10, VoidBuf);
  3349. if I <= 0 then
  3350. Exit; { No end of line found, continue }
  3351. { We have found end of line }
  3352. Move(VoidBuf[I], FReceiveBuffer[FReceiveLen - Len + I], Len - I + 1);
  3353. Len := 1;
  3354. end
  3355. else
  3356. {$ENDIF} // FP 09/09/06
  3357. // Make FReceiveBuffer at least 8KB larger than actually received data
  3358. if Length(FReceiveBuffer) < (FReceiveLen + BUF_SIZE) then begin
  3359. SetLength(FReceiveBuffer, FReceiveLen + BUF_SIZE + 1);
  3360. FReceiveBuffer[FReceiveLen + BUF_SIZE] := 0; // Easy debug with an ending nul byte
  3361. end;
  3362. I := Length(FReceiveBuffer) - FReceiveLen - 1; // Preserve the nul byte
  3363. {$IFDEF CLR}
  3364. if FReceiveLen = 0 then
  3365. Len := FCtrlSocket.Receive(FReceiveBuffer, I)
  3366. else begin
  3367. // Can't receive in the middle of a byte array !
  3368. SetLength(TempBuf, I);
  3369. Len := FCtrlSocket.Receive(TempBuf, I);
  3370. for I := 0 to Len - 1 do
  3371. FReceiveBuffer[FReceiveLen + I] := TempBuf[I];
  3372. end;
  3373. {$ELSE}
  3374. Len := FCtrlSocket.Receive(@FReceiveBuffer[FReceiveLen], I);
  3375. {$ENDIF}
  3376. // Debugging purpose only, fill the buffer with constant character
  3377. //if (Len > 0) and (Len < I) then
  3378. // FillChar(FReceiveBuffer[FReceiveLen + Len], I - Len, '*');
  3379. {$IFNDEF NO_DEBUG_LOG}
  3380. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  3381. DebugLog(loProtSpecInfo, 'Data available. Len=' + IntToStr(Len));
  3382. {$ENDIF}
  3383. if FRequestType = httpAbort then
  3384. Exit;
  3385. if Len <= 0 then begin
  3386. {$IFNDEF NO_DEBUG_LOG}
  3387. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  3388. DebugLog(loProtSpecInfo, '**data available. Len=' + IntToStr(Len));
  3389. {$ENDIF}
  3390. Exit;
  3391. end;
  3392. {$IFDEF UseBandwidthControl}
  3393. Inc(FBandwidthCount, Len);
  3394. if httpoBandwidthControl in FOptions then begin
  3395. //OutputDebugString(PChar('data FBandwidthCount=' + IntToStr(FBandwidthCount)));
  3396. if (FBandwidthCount > FBandwidthMaxCount) and
  3397. (not FBandwidthPaused) then begin
  3398. FBandwidthPaused := TRUE;
  3399. //OutputDebugString('Pause');
  3400. FCtrlSocket.Pause;
  3401. end;
  3402. end;
  3403. {$ENDIF}
  3404. FReceiveLen := FReceiveLen + Len;
  3405. FReceiveBuffer[FReceiveLen] := 0;
  3406. {$IFDEF USE_SSL}
  3407. if FState = httpWaitingProxyConnect then begin
  3408. { If connection failed to remote host, then we receive a normal }
  3409. { HTTP reply from the proxy with a HTML message with error }
  3410. { message ! Something like: }
  3411. { "HTTP/1.0 200 OK<CRLF>header lines<CRLF><CRLF>document" }
  3412. { If connection success we receive }
  3413. { "HTTP/1.0 200 Connection established<CRLF><CRLF>" }
  3414. { Some proxies return HTTP/1.0 200 OK. We must also check for } {Bjornar}
  3415. { Content-Length, since a proxy returns header only as reply to } {Bjornar}
  3416. { CONNECT request. } {Bjornar}
  3417. {$IFNDEF NO_DEBUG_LOG}
  3418. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  3419. DebugLog(loProtSpecInfo, 'Proxy connected: "' + PAnsiChar(FReceiveBuffer) + '"');
  3420. {$ENDIF}
  3421. FProxyConnected := TRUE;
  3422. if ( {Bjornar - Start}
  3423. (StartsWithText(FReceiveBuffer, 'HTTP/1.0 200') or
  3424. StartsWithText(FReceiveBuffer,'HTTP/1.1 200') or
  3425. StartsWithText(FReceiveBuffer, 'HTTP/1.0 200') or //M$ Proxy Server 2.0
  3426. StartsWithText(FReceiveBuffer, 'HTTP/1.1 200')) //M$ Proxy Server 2.0 not tested ??
  3427. and not
  3428. ((StartsWithText(FReceiveBuffer, 'HTTP/1.1 200 OK') or
  3429. StartsWithText(FReceiveBuffer, 'HTTP/1.0 200 OK')) and
  3430. ContainsText(FReceiveBuffer, 'Content-Length:') and
  3431. not ContainsText(FReceiveBuffer, 'Content-Length: 0'))
  3432. ) then {Bjornar - End}
  3433. begin
  3434. { We have a connection to remote host thru proxy, we can start }
  3435. { SSL handshake }
  3436. {$IFDEF UseNTLMAuthentication}
  3437. if not (FProxyAuthNTLMState in [ntlmNone, ntlmDone]) then
  3438. FProxyAuthNTLMState := ntlmDone;
  3439. {$ENDIF}
  3440. {$IFDEF UseDigestAuthentication}
  3441. if not (FProxyAuthDigestState in [digestNone, digestDone]) then
  3442. FProxyAuthDigestState := digestDone;
  3443. {$ENDIF}
  3444. if not (FProxyAuthBasicState in [basicNone, basicDone]) then
  3445. FProxyAuthBasicState := basicDone;
  3446. // 12/27/05 AG begin, reset some more defaults
  3447. FCurrProxyConnection := '';
  3448. (*if FRequestVer = '1.0' then
  3449. FCloseReq := TRUE
  3450. else
  3451. FCloseReq := FALSE; *)
  3452. // 12/27/05 AG end
  3453. {$IFNDEF NO_DEBUG_LOG}
  3454. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  3455. DebugLog(loProtSpecInfo, 'Start SSL handshake');
  3456. {$ENDIF}
  3457. FReceiveLen := 0; { Clear input data }
  3458. FCtrlSocket.OnSslHandshakeDone := SslHandshakeDone;
  3459. FCtrlSocket.SslEnable := TRUE;
  3460. FCtrlSocket.StartSslHandshake;
  3461. FState := httpWaitingHeader;
  3462. Exit;
  3463. end
  3464. else
  3465. { Continue as a normal HTTP request }
  3466. FState := httpWaitingHeader;
  3467. end;
  3468. {$ENDIF USE_SSL}
  3469. if FState = httpWaitingBody then begin
  3470. if FReceiveLen > 0 then begin
  3471. if FRequestType = httpHEAD then begin { 23/07/04 }
  3472. { We are processing a HEAD command. We don't expect a document }
  3473. { but some server send one anyway. We just throw it away and }
  3474. { abort the connection }
  3475. FReceiveLen := 0;
  3476. FCtrlSocket.Abort;
  3477. Exit;
  3478. end;
  3479. // FBodyData := FReceiveBuffer;
  3480. FBodyData := 0; // FP 09/09/06
  3481. if (FContentLength < 0) or
  3482. ((FRcvdCount + FReceiveLen) <= FContentLength) then
  3483. FBodyDataLen := FReceiveLen
  3484. else
  3485. FBodyDataLen := FContentLength - FRcvdCount;
  3486. GetBodyLineNext;
  3487. FReceiveLen := FReceiveLen - FBodyDataLen; {+++++}
  3488. { Move remaining data to start of buffer. 17/01/2004 }
  3489. if FReceiveLen > 0 then
  3490. MoveTBytes(FReceiveBuffer, FBodyDataLen, 0, FReceiveLen + 1);
  3491. FBodyDataLen := 0;
  3492. if Assigned(FNext) then
  3493. FNext
  3494. else if FLocationFlag then { 28/12/2003 }
  3495. StartRelocation
  3496. else
  3497. CheckDelaySetReady; { 09/26/08 ML }
  3498. end;
  3499. { FReceiveLen := 0; 22/02/02 }
  3500. Exit;
  3501. end;
  3502. { 26/11/2003: next 2 lines commented out to allow receiving data outside }
  3503. { of any request (server push) }
  3504. { if FState <> httpWaitingHeader then
  3505. Exit; }{ Should never occur ! }
  3506. while FReceiveLen > 0 do begin
  3507. // I := Pos(#10, FReceiveBuffer);
  3508. I := 0; // FP 09/09/06
  3509. while (I <= FReceiveLen) and (Ord(FReceiveBuffer[I]) <> 10) do // FP 09/09/06
  3510. Inc(I); // FP 09/09/06
  3511. // if I <= 0 then
  3512. // break;
  3513. if I > FReceiveLen then
  3514. break;
  3515. // if (I > 1) and (FReceiveBuffer[I-2] = #13) then
  3516. // FLastResponse := Copy(FReceiveBuffer, 1, I - 2)
  3517. // else
  3518. // FLastResponse := Copy(FReceiveBuffer, 1, I - 1);
  3519. if I = 0 then // FP 09/09/06
  3520. SetLength(FLastResponse, 0) // FP 09/09/06
  3521. else begin // FP 09/09/06
  3522. if (I > 0) and (Ord(FReceiveBuffer[I - 1]) = 13) then // FP 09/09/06
  3523. SetLength(FLastResponse, I - 1) // FP 09/09/06
  3524. else // FP 09/09/06
  3525. SetLength(FLastResponse, I); // FP 09/09/06
  3526. if Length(FLastResponse) > 0 then // FP 09/09/06
  3527. MoveTBytesToString(FReceiveBuffer, 0,
  3528. FLastResponse, 1, Length(FLastResponse)); // FP 09/09/06
  3529. end; // FP 09/09/06
  3530. {$IFNDEF NO_DEBUG_LOG}
  3531. if CheckLogOptions(loProtSpecDump) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  3532. DebugLog(loProtSpecDump, '>|' + FLastResponse + '|');
  3533. {$ENDIF}
  3534. FReceiveLen := FReceiveLen - I - 1; // FP 09/09/06
  3535. if FReceiveLen > 0 then begin
  3536. // Move(FReceiveBuffer[I], FReceiveBuffer[0], FReceiveLen + 1);
  3537. MoveTBytes(FReceiveBuffer, I + 1, 0, FReceiveLen); // FP 09/09/06
  3538. // Debugging purpose only
  3539. //FillChar(FReceiveBuffer[FReceiveLen], I + 1, '*');
  3540. end
  3541. else if FReceiveLen < 0 then // AG 03/19/07
  3542. FReceiveLen := 0; // AG 03/19/07
  3543. if FState in [httpWaitingHeader, httpWaitingBody] then begin
  3544. if Assigned(FNext) then
  3545. FNext
  3546. else
  3547. CheckDelaySetReady; { 09/26/08 ML }
  3548. end
  3549. else begin
  3550. { We are receiving data outside of any request. }
  3551. { It's a server push. }
  3552. if Assigned(FOnDataPush) then
  3553. FOnDataPush(Self, ErrCode);
  3554. end;
  3555. end;
  3556. end;
  3557. {Bjornar - Start}
  3558. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  3559. function THttpCli.StartsWithText(Source : TBytes; Find : PAnsiChar) : Boolean;
  3560. begin
  3561. Result := FALSE;
  3562. if (StrLIComp(PAnsiChar(Source), Find, Length(Find)) = 0) then
  3563. Result := TRUE;
  3564. end;
  3565. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  3566. function THttpCli.ContainsText(Source : TBytes; Find : PAnsiChar) : Boolean;
  3567. begin
  3568. Result := FALSE;
  3569. if (StrPos(PAnsiChar(Source), Find) <> nil) then
  3570. Result := TRUE;
  3571. end;
  3572. {Bjornar - End}
  3573. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  3574. procedure THttpCli.StartRelocation;
  3575. var
  3576. SaveLoc : String;
  3577. AllowMoreRelocations : Boolean;
  3578. begin
  3579. {$IFNDEF NO_DEBUG_LOG}
  3580. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  3581. DebugLog(loProtSpecInfo, 'Starting relocation process');
  3582. {$ENDIF}
  3583. FRcvdCount := 0;
  3584. FReceiveLen := 0;
  3585. FHeaderLineCount := 0;
  3586. FBodyLineCount := 0;
  3587. { V1.90 25 Nov 2005 - restrict number of relocations to avoid continuous loops }
  3588. inc (FLocationChangeCurCount) ;
  3589. if FLocationChangeCurCount > FLocationChangeMaxCount then begin
  3590. AllowMoreRelocations := false;
  3591. if Assigned (FOnLocationChangeExceeded) then
  3592. FOnLocationChangeExceeded(Self, FLocationChangeCurCount,
  3593. AllowMoreRelocations) ;
  3594. if not AllowMoreRelocations then begin
  3595. SetReady;
  3596. exit;
  3597. end ;
  3598. end ;
  3599. if {(FResponseVer = '1.1') and}
  3600. { [rawbite 31.08.2004 Connection controll] }
  3601. (FCurrentHost = FHostName) and
  3602. (FCurrentPort = FPort) and
  3603. (FCurrentProtocol = FProtocol) and
  3604. (not FCloseReq) then begin { SAE 01/06/04 }
  3605. { No need to disconnect }
  3606. { Trigger the location changed event 27/04/2003 }
  3607. if Assigned(FOnLocationChange) then
  3608. FOnLocationChange(Self);
  3609. SaveLoc := FLocation; { 01/05/03 }
  3610. InternalClear;
  3611. FLocation := SaveLoc;
  3612. FDocName := FPath;
  3613. AdjustDocName;
  3614. { When relocation occurs doing a POST, new relocated page }
  3615. { has to be GET. 01/05/03 }
  3616. if FRequestType = httpPOST then
  3617. FRequestType := httpGET;
  3618. { Must clear what we already received }
  3619. CleanupRcvdStream; {11/11/04}
  3620. CleanupSendStream;
  3621. PostMessage(Handle, FMsg_WM_HTTP_LOGIN, 0, 0);
  3622. end
  3623. else begin
  3624. FCtrlSocket.OnSessionClosed := LocationSessionClosed;
  3625. FCtrlSocket.CloseDelayed;
  3626. end;
  3627. end;
  3628. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  3629. procedure THttpCli.CleanupRcvdStream;
  3630. begin
  3631. { What we are received must be removed }
  3632. {$IFDEF UseContentCoding}
  3633. if Assigned(FRcvdStream) and (FRcvdStream.Size <> FRcvdStreamStartSize) then
  3634. {$IFNDEF COMPILER3_UP}
  3635. begin
  3636. if FRcvdStream is THandleStream then begin
  3637. FRcvdStream.Seek(FRcvdStreamStartSize, 0);
  3638. FRcvdStream.Write(FRcvdStreamStartSize, 0); { Truncate !!! }
  3639. end
  3640. else if FRcvdStream is TMemoryStream then
  3641. TMemoryStream(FRcvdStream).SetSize(FRcvdStreamStartSize);
  3642. { Silently fail for other stream types :-( }
  3643. { Should I raise an exception ? }
  3644. end;
  3645. {$ELSE}
  3646. FRcvdStream.Size := FRcvdStreamStartSize;
  3647. {$ENDIF}
  3648. {$ELSE}
  3649. if Assigned(FRcvdStream) and (FRcvdCount > 0) then
  3650. {$IFNDEF COMPILER3_UP}
  3651. begin
  3652. if FRcvdStream is THandleStream then begin
  3653. FRcvdStream.Seek(FRcvdStream.Size - FRcvdCount, 0);
  3654. FRcvdStream.Write(FRcvdCount, 0); { Truncate !!! }
  3655. end
  3656. else if FRcvdStream is TMemoryStream then
  3657. TMemoryStream(FRcvdStream).SetSize(FRcvdStream.Size - FRcvdCount);
  3658. { Silently fail for other stream types :-( }
  3659. { Should I raise an exception ? }
  3660. end;
  3661. {$ELSE}
  3662. FRcvdStream.Size := FRcvdStream.Size - FRcvdCount;
  3663. {$ENDIF}
  3664. {$ENDIF}
  3665. end;
  3666. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  3667. procedure THttpCli.CleanupSendStream;
  3668. begin
  3669. { Reset the start position of the stream }
  3670. if Assigned(FSendStream) and (FSentCount > 0) then
  3671. FSendStream.Seek(-FSentCount, soFromCurrent);
  3672. end;
  3673. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  3674. {$IFDEF UseNTLMAuthentication}
  3675. procedure THttpCli.StartAuthNTLM;
  3676. var
  3677. I : Integer;
  3678. begin
  3679. if FAuthNTLMState = ntlmNone then begin
  3680. {$IFNDEF NO_DEBUG_LOG}
  3681. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  3682. DebugLog(loProtSpecInfo, 'Starting NTLM authentication');
  3683. {$ENDIF}
  3684. FAuthNTLMState := ntlmMsg1;
  3685. FAuthBasicState := basicNone; { Other authentication must be cleared }
  3686. { [rawbite 31.08.2004 Connection controll] }
  3687. { if request version is 1.0 we must tell the server that we want }
  3688. { to keep the connection or NTLM will not work }
  3689. if (FRequestVer = '1.0') or (FResponseVer = '1.0') or // <== 12/29/05 AG
  3690. (FResponseVer = '') then // <== 12/29/05 AG
  3691. FCurrConnection := 'Keep-alive';
  3692. PostMessage(Handle, FMsg_WM_HTTP_LOGIN, 0, 0);
  3693. end
  3694. else if FAuthNTLMState = ntlmMsg1 then begin
  3695. I := FDoAuthor.Count - 1;
  3696. while I >= 0 do begin
  3697. if CompareText(Copy(FDoAuthor.Strings[I], 1, 4), 'NTLM') = 0 then
  3698. Break;
  3699. Dec(I);
  3700. end;
  3701. if I < 0 then
  3702. Exit;
  3703. FNTLMMsg2Info := NtlmGetMessage2(Copy(FDoAuthor.Strings[I], 6, 1000));
  3704. FAuthNTLMState := ntlmMsg3;
  3705. PostMessage(Handle, FMsg_WM_HTTP_LOGIN, 0, 0);
  3706. end
  3707. else if FAuthNTLMState = ntlmMsg3 then begin
  3708. FDoAuthor.Clear;
  3709. FAuthNTLMState := ntlmNone;
  3710. { We comes here when NTLM has failed }
  3711. { so we trigger the end request }
  3712. PostMessage(Handle, FMsg_WM_HTTP_REQUEST_DONE, 0, 0);
  3713. end
  3714. else
  3715. raise EHttpException.Create('Unexpected AuthNTLMState',
  3716. httperrInvalidAuthState);
  3717. end;
  3718. {$ENDIF}
  3719. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  3720. {$IFDEF UseNTLMAuthentication}
  3721. procedure THttpCli.StartProxyAuthNTLM;
  3722. var
  3723. I : Integer;
  3724. begin
  3725. if FProxyAuthNTLMState = ntlmNone then begin
  3726. {$IFNDEF NO_DEBUG_LOG}
  3727. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  3728. DebugLog(loProtSpecInfo, 'Starting Proxy NTLM authentication');
  3729. {$ENDIF}
  3730. FProxyAuthNTLMState := ntlmMsg1;
  3731. FProxyAuthBasicState := basicNone; { Other authentication must be cleared }
  3732. {$IFDEF UseDigestAuthentication}
  3733. FProxyAuthDigestState := digestNone;
  3734. {$ENDIF}
  3735. { [rawbite 31.08.2004 Connection controll] }
  3736. { if request version is 1.0 we must tell the server that we want }
  3737. { to keep the connection or NTLM will not work }
  3738. if (FRequestVer = '1.0') or (FResponseVer = '1.0') or // <== 12/29/05 AG
  3739. (FResponseVer = '') then // <== 12/29/05 AG
  3740. FCurrProxyConnection := 'Keep-alive';
  3741. PostMessage(Handle, FMsg_WM_HTTP_LOGIN, 0, 0);
  3742. end
  3743. else if FProxyAuthNTLMState = ntlmMsg1 then begin
  3744. I := FDoAuthor.Count - 1;
  3745. while I >= 0 do begin
  3746. if CompareText(Copy(FDoAuthor.Strings[I], 1, 4), 'NTLM') = 0 then
  3747. Break;
  3748. Dec(I);
  3749. end;
  3750. if I < 0 then
  3751. Exit;
  3752. FProxyNTLMMsg2Info := NtlmGetMessage2(Copy(FDoAuthor.Strings[I], 6, 1000));
  3753. FProxyAuthNTLMState := ntlmMsg3;
  3754. PostMessage(Handle, FMsg_WM_HTTP_LOGIN, 0, 0);
  3755. end
  3756. else if FProxyAuthNTLMState = ntlmMsg3 then begin
  3757. FDoAuthor.Clear;
  3758. FProxyAuthNTLMState := ntlmNone;
  3759. { We comes here when NTLM has failed }
  3760. { so we trigger the end request }
  3761. PostMessage(Handle, FMsg_WM_HTTP_REQUEST_DONE, 0, 0);
  3762. end
  3763. else
  3764. raise EHttpException.Create('Unexpected ProxyAuthNTLMState',
  3765. httperrInvalidAuthState);
  3766. end;
  3767. {$ENDIF}
  3768. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  3769. procedure THttpCli.StartAuthBasic;
  3770. begin
  3771. if FAuthBasicState = basicNone then begin
  3772. {$IFNDEF NO_DEBUG_LOG}
  3773. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  3774. DebugLog(loProtSpecInfo, 'Starting Basic authentication');
  3775. {$ENDIF}
  3776. FAuthBasicState := basicMsg1;
  3777. {$IFDEF UseNTLMAuthentication}
  3778. FAuthNTLMState := ntlmNone; { Other authentication must be cleared }
  3779. {$ENDIF}
  3780. {$IFDEF UseDigestAuthentication}
  3781. FAuthDigestState := digestNone;
  3782. {$ENDIF}
  3783. PostMessage(Handle, FMsg_WM_HTTP_LOGIN, 0, 0);
  3784. end
  3785. else if FAuthBasicState = basicMsg1 then begin
  3786. FDoAuthor.Clear;
  3787. FAuthBasicState := basicNone;
  3788. { We comes here when Basic has failed }
  3789. { so we trigger the end request }
  3790. PostMessage(Handle, FMsg_WM_HTTP_REQUEST_DONE, 0, 0);
  3791. end
  3792. else
  3793. raise EHttpException.Create('Unexpected AuthBasicState',
  3794. httperrInvalidAuthState);
  3795. end;
  3796. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  3797. procedure THttpCli.StartProxyAuthBasic;
  3798. begin
  3799. if FProxyAuthBasicState = basicNone then begin
  3800. {$IFNDEF NO_DEBUG_LOG}
  3801. if CheckLogOptions(loProtSpecInfo) then { V1.91 } { replaces $IFDEF DEBUG_OUTPUT }
  3802. DebugLog(loProtSpecInfo, 'Starting Proxy Basic authentication');
  3803. {$ENDIF}
  3804. FProxyAuthBasicState := basicMsg1;
  3805. {$IFDEF UseNTLMAuthentication}
  3806. FProxyAuthNTLMState := ntlmNone; { Other authentication must be cleared }
  3807. {$ENDIF}
  3808. {$IFDEF UseDigestAuthentication}
  3809. FProxyAuthDigestState := digestNone;
  3810. {$ENDIF}
  3811. PostMessage(Handle, FMsg_WM_HTTP_LOGIN, 0, 0);
  3812. end
  3813. else if FProxyAuthBasicState = basicMsg1 then begin
  3814. FDoAuthor.Clear;
  3815. FProxyAuthBasicState := basicNone;
  3816. { We comes here when Basic has failed }
  3817. { so we trigger the end request }
  3818. PostMessage(Handle, FMsg_WM_HTTP_REQUEST_DONE, 0, 0);
  3819. end
  3820. else
  3821. raise EHttpException.Create('Unexpected ProxyAuthBasicState',
  3822. httperrInvalidAuthState);
  3823. end;
  3824. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  3825. function THttpCli.GetBasicAuthorizationHeader(
  3826. const HttpMethod: String; ProxyAuth: Boolean): String;
  3827. begin
  3828. if ProxyAuth then
  3829. Result := 'Proxy-Authorization: Basic ' +
  3830. EncodeStr(encBase64, FProxyUsername + ':' + FProxyPassword)
  3831. else
  3832. Result := 'Authorization: Basic ' +
  3833. EncodeStr(encBase64, FCurrUsername + ':' + FCurrPassword);
  3834. end;
  3835. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  3836. {$IFDEF UseDigestAuthentication}
  3837. procedure THttpCli.ElaborateDigestAuth;
  3838. begin
  3839. { if you place this code in GetHeaderLineNext, not each time will be }
  3840. { called ... }
  3841. if (FAuthDigestState = digestMsg1) and (FStatusCode <> 401) and
  3842. (FStatusCode <> 407) then
  3843. FAuthDigestState := digestDone
  3844. else if (FAuthDigestState = digestDone) and (FStatusCode = 401) then
  3845. FAuthDigestState := digestNone;
  3846. if (FProxyAuthDigestState = digestMsg1) and (FStatusCode <> 407) then
  3847. FProxyAuthDigestState := digestDone
  3848. else if (FProxyAuthDigestState = digestDone) and (FStatusCode = 407) then begin
  3849. { if we lost proxy authenticated line, most probaly we lost also }
  3850. { the authenticated line of Proxy to HTTP server, so reset the }
  3851. { Digest state of HTTP also to none }
  3852. FProxyAuthDigestState := digestNone;
  3853. end;
  3854. end;
  3855. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  3856. function THttpCli.PrepareDigestAuth(var FlgClean : Boolean) : Boolean;
  3857. var
  3858. I : Integer;
  3859. begin
  3860. { this flag can tell if we proceed with OnRequestDone or will try }
  3861. { to authenticate }
  3862. Result := FALSE;
  3863. if (httpoNoDigestAuth in FOptions) and
  3864. (((FStatusCode = 401) and (FServerAuth = httpAuthDigest)) or
  3865. ((FStatusCode = 407) and (FProxyAuth = httpAuthDigest))) then
  3866. Exit;
  3867. if (FStatusCode = 401) and (FDoAuthor.Count > 0) and
  3868. {$IFDEF UseNTLMAuthentication}
  3869. (FAuthNTLMState = ntlmNone) and
  3870. {$ENDIF}
  3871. (FAuthBasicState = basicNone) and
  3872. (FCurrUserName <> '') and (FCurrPassword <> '') then begin
  3873. { We can handle Digest Access Authentication }
  3874. for I := FDoAuthor.Count - 1 downto 0 do begin
  3875. if CompareText(Copy(FDoAuthor.Strings[I], 1, 6),
  3876. 'Digest') = 0 then begin
  3877. Result := TRUE;
  3878. if Assigned(FOnBeforeAuth) then
  3879. FOnBeforeAuth(Self, httpAuthDigest, FALSE,
  3880. FDoAuthor.Strings[I], Result);
  3881. if Result then begin
  3882. AuthDigestParseChallenge(Copy(FDoAuthor.Strings[I], 8, 1000),
  3883. FAuthDigestInfo);
  3884. if AuthDigestValidateResponse(FAuthDigestInfo) then begin
  3885. StartAuthDigest;
  3886. if FAuthDigestState = digestMsg1 then
  3887. FlgClean := True;
  3888. Break;
  3889. end
  3890. else
  3891. Result := FALSE;
  3892. end;
  3893. end;
  3894. end;
  3895. end
  3896. else if (FStatusCode = 407) and (FDoAuthor.Count > 0) and
  3897. {$IFDEF UseNTLMAuthentication}
  3898. (FProxyAuthNTLMState = ntlmNone) and
  3899. {$ENDIF}
  3900. (FProxyAuthBasicState = basicNone) and
  3901. (FProxyUsername <> '') and (FProxyPassword <> '') then begin
  3902. { We can handle Digest Access Authentication }
  3903. for I := FDoAuthor.Count - 1 downto 0 do begin
  3904. if CompareText(Copy(FDoAuthor.Strings[I], 1, 6),
  3905. 'Digest') = 0 then begin
  3906. Result := TRUE;
  3907. if Assigned(FOnBeforeAuth) then
  3908. FOnBeforeAuth(Self, httpAuthDigest, TRUE,
  3909. FDoAuthor.Strings[I], Result);
  3910. if Result then begin
  3911. AuthDigestParseChallenge(Copy(FDoAuthor.Strings[I], 8, 1000),
  3912. FAuthDigestProxyInfo);
  3913. if AuthDigestValidateResponse(FAuthDigestProxyInfo) then begin
  3914. StartProxyAuthDigest;
  3915. if FProxyAuthDigestState = digestMsg1 then
  3916. FlgClean := True;
  3917. Break;
  3918. end
  3919. else
  3920. Result := FALSE;
  3921. end;
  3922. end;
  3923. end;
  3924. end;
  3925. end;
  3926. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  3927. function THttpCli.GetDigestAuthorizationHeader(
  3928. const HttpMethod: String; ProxyAuth: Boolean): String;
  3929. var
  3930. Proto, User, Pass, Host, Port, Uri : String;
  3931. begin
  3932. { It's probably faster to use a field FAuthDigestUri which }
  3933. { is set at some other places when ParseUrl is called anyway }
  3934. ParseURL(FPath, Proto, User, Pass, Host, Port, Uri);
  3935. if ProxyAuth then begin
  3936. Inc(FAuthDigestProxyInfo.Nc);
  3937. Result := 'Proxy-Authorization: Digest ' +
  3938. AuthDigestGenerateRequest(FProxyUserName,
  3939. FProxyPassword,
  3940. HttpMethod,
  3941. Uri,
  3942. FAuthDigestEntityHash,
  3943. FAuthDigestProxyInfo);
  3944. end
  3945. else begin
  3946. FAuthDigestInfo.Nc := 1;
  3947. Result := 'Authorization: Digest ' +
  3948. AuthDigestGenerateRequest(FCurrUserName,
  3949. FCurrPassword,
  3950. HttpMethod,
  3951. Uri,
  3952. FAuthDigestEntityHash,
  3953. FAuthDigestInfo);
  3954. end;
  3955. end;
  3956. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  3957. procedure THttpCli.StartAuthDigest;
  3958. begin
  3959. if FAuthDigestState = digestNone then begin
  3960. {$IFNDEF NO_DEBUG_LOG}
  3961. if CheckLogOptions(loProtSpecInfo) then
  3962. DebugLog(loProtSpecInfo, 'Starting Digest authentication');
  3963. {$ENDIF}
  3964. FAuthDigestState := digestMsg1;
  3965. {$IFDEF UseNTLMAuthentication}
  3966. FAuthNTLMState := ntlmNone; { Other authentication must be cleared }
  3967. {$ENDIF}
  3968. FAuthBasicState := basicNone;
  3969. PostMessage(Handle, FMsg_WM_HTTP_LOGIN, 0, 0);
  3970. end
  3971. else if FAuthDigestState = digestMsg1 then begin
  3972. FDoAuthor.Clear;
  3973. FAuthDigestState := digestNone;
  3974. { We comes here when Basic has failed }
  3975. { so we trigger the end request }
  3976. PostMessage(Handle, FMsg_WM_HTTP_REQUEST_DONE, 0, 0);
  3977. end
  3978. else
  3979. raise EHttpException.Create('Unexpected AuthDigestState',
  3980. httperrInvalidAuthState);
  3981. end;
  3982. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  3983. procedure THttpCli.StartProxyAuthDigest;
  3984. begin
  3985. if FProxyAuthDigestState = digestNone then begin
  3986. {$IFNDEF NO_DEBUG_LOG}
  3987. if CheckLogOptions(loProtSpecInfo) then
  3988. DebugLog(loProtSpecInfo, 'Starting Proxy Digest authentication');
  3989. {$ENDIF}
  3990. FProxyAuthDigestState := digestMsg1;
  3991. {$IFDEF UseNTLMAuthentication}
  3992. FProxyAuthNTLMState := ntlmNone; { Other authentication must be cleared }
  3993. {$ENDIF}
  3994. FProxyAuthBasicState := basicNone;
  3995. PostMessage(Handle, FMsg_WM_HTTP_LOGIN, 0, 0);
  3996. end
  3997. else if FProxyAuthBasicState = basicMsg1 then begin
  3998. FDoAuthor.Clear;
  3999. FProxyAuthDigestState := digestNone;
  4000. { We come here when Digest has failed }
  4001. { so we trigger the end request }
  4002. PostMessage(Handle, FMsg_WM_HTTP_REQUEST_DONE, 0, 0);
  4003. end
  4004. else
  4005. raise EHttpException.Create('Unexpected ProxyAuthDigestState',
  4006. httperrInvalidAuthState);
  4007. end;
  4008. {$ENDIF UseDigestAuthentication}
  4009. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4010. {$IFDEF USE_SSL}
  4011. procedure THttpCli.SslHandshakeDone(
  4012. Sender : TObject;
  4013. ErrCode : Word;
  4014. PeerCert : TX509Base;
  4015. var Disconnect : Boolean);
  4016. begin
  4017. if Assigned(TSslHttpCli(Self).FOnSslHandshakeDone) then
  4018. TSslHttpCli(Self).FOnSslHandshakeDone(Self, // FP: was Sender
  4019. ErrCode,
  4020. PeerCert,
  4021. Disconnect);
  4022. if (ErrCode <> 0) or Disconnect then begin
  4023. FStatusCode := 404;
  4024. if Disconnect then
  4025. FReasonPhrase := 'SSL custom abort'
  4026. else
  4027. FReasonPhrase := 'SSL handshake failed';
  4028. FRequestDoneError := httperrAborted;
  4029. FConnected := False;
  4030. Exit;
  4031. end;
  4032. if not FProxyConnected then
  4033. Exit;
  4034. try
  4035. FNext := GetHeaderLineNext;
  4036. StateChange(httpWaitingHeader);
  4037. case FRequestType of
  4038. httpPOST:
  4039. begin
  4040. SendRequest('POST', FRequestVer);
  4041. {$IFDEF UseNTLMAuthentication}
  4042. if not ((FAuthNTLMState = ntlmMsg1) or
  4043. (FProxyAuthNTLMState = ntlmMsg1)) then begin
  4044. TriggerSendBegin;
  4045. FAllowedToSend := TRUE;
  4046. FDelaySetReady := FALSE; { 09/26/08 ML }
  4047. SocketDataSent(FCtrlSocket, 0);
  4048. end;
  4049. {$ELSE}
  4050. TriggerSendBegin;
  4051. FAllowedToSend := TRUE;
  4052. FDelaySetReady := FALSE; { 09/26/08 ML }
  4053. SocketDataSent(FCtrlSocket, 0);
  4054. {$ENDIF}
  4055. end;
  4056. httpPUT:
  4057. begin
  4058. SendRequest('PUT', FRequestVer);
  4059. {$IFDEF UseNTLMAuthentication}
  4060. if not ((FAuthNTLMState = ntlmMsg1) or (FProxyAuthNTLMState = ntlmMsg1)) then begin
  4061. TriggerSendBegin;
  4062. FAllowedToSend := TRUE;
  4063. FDelaySetReady := FALSE; { 09/26/08 ML }
  4064. SocketDataSent(FCtrlSocket, 0);
  4065. end;
  4066. {$ELSE}
  4067. TriggerSendBegin;
  4068. FAllowedToSend := TRUE;
  4069. FDelaySetReady := FALSE; { 09/26/08 ML }
  4070. SocketDataSent(FCtrlSocket, 0);
  4071. {$ENDIF}
  4072. end;
  4073. httpHEAD:
  4074. begin
  4075. SendRequest('HEAD', FRequestVer);
  4076. end;
  4077. httpGET:
  4078. begin
  4079. SendRequest('GET', FRequestVer);
  4080. end;
  4081. end;
  4082. except
  4083. Logout;
  4084. end;
  4085. end;
  4086. {$ENDIF}
  4087. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4088. procedure THttpCli.SocketDataSent(Sender : TObject; ErrCode : Word);
  4089. var
  4090. Len : Integer;
  4091. begin
  4092. if not FAllowedToSend then
  4093. Exit;
  4094. if Length(FSendBuffer) = 0 then
  4095. SetLength(FSendBuffer, 8192);
  4096. {$IFDEF CLR}
  4097. Len := FSendStream.Read(FSendBuffer, Length(FSendBuffer));
  4098. {$ELSE}
  4099. Len := FSendStream.Read(FSendBuffer[0], Length(FSendBuffer));
  4100. {$ENDIF}
  4101. if Len <= 0 then begin
  4102. FAllowedToSend := FALSE;
  4103. TriggerSendEnd;
  4104. if FDelaySetReady then begin { 09/26/08 ML }
  4105. FDelaySetReady := FALSE; { 09/26/08 ML }
  4106. SetReady; { 09/26/08 ML }
  4107. end; { 09/26/08 ML }
  4108. Exit;
  4109. end;
  4110. if Len > 0 then begin
  4111. FSentCount := FSentCount + Len;
  4112. {$IFDEF CLR}
  4113. TriggerSendData(FSendBuffer, 0, Len);
  4114. FCtrlSocket.Send(FSendBuffer, Len);
  4115. {$ELSE}
  4116. TriggerSendData(@FSendBuffer[0], Len);
  4117. FCtrlSocket.Send(@FSendBuffer[0], Len);
  4118. {$ENDIF}
  4119. end;
  4120. end;
  4121. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4122. { This will start the Get process and wait until terminated (blocking) }
  4123. procedure THttpCli.Get;
  4124. begin
  4125. FLocationChangeCurCount := 0 ; { V1.90 }
  4126. DoRequestSync(httpGet);
  4127. end;
  4128. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4129. { This will start the Head process and wait until terminated (blocking) }
  4130. procedure THttpCli.Head;
  4131. begin
  4132. FLocationChangeCurCount := 0 ; { V1.90 }
  4133. DoRequestSync(httpHEAD);
  4134. end;
  4135. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4136. { This will start the Post process and wait until terminated (blocking) }
  4137. procedure THttpCli.Post;
  4138. begin
  4139. FLocationChangeCurCount := 0 ; { V1.90 }
  4140. DoRequestSync(httpPOST);
  4141. end;
  4142. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4143. { This will start the Put process and wait until terminated (blocking) }
  4144. procedure THttpCli.Put;
  4145. begin
  4146. FLocationChangeCurCount := 0 ; { V1.90 }
  4147. DoRequestSync(httpPUT);
  4148. end;
  4149. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4150. { This will start the Close process and wait until terminated (blocking) }
  4151. procedure THttpCli.Close;
  4152. begin
  4153. DoRequestSync(httpCLOSE);
  4154. end;
  4155. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4156. { This will start the get process and returns immediately (non blocking) }
  4157. procedure THttpCli.GetAsync;
  4158. begin
  4159. FLocationChangeCurCount := 0 ; { V1.90 }
  4160. DoRequestASync(httpGet);
  4161. end;
  4162. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4163. { This will start the head process and returns immediately (non blocking) }
  4164. procedure THttpCli.HeadAsync;
  4165. begin
  4166. FLocationChangeCurCount := 0 ; { V1.90 }
  4167. DoRequestASync(httpHEAD);
  4168. end;
  4169. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4170. { This will start the post process and returns immediately (non blocking) }
  4171. procedure THttpCli.PostAsync;
  4172. begin
  4173. FLocationChangeCurCount := 0 ; { V1.90 }
  4174. DoRequestASync(httpPOST);
  4175. end;
  4176. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4177. { This will start the put process and returns immediately (non blocking) }
  4178. procedure THttpCli.PutAsync;
  4179. begin
  4180. FLocationChangeCurCount := 0 ; { V1.90 }
  4181. DoRequestASync(httpPUT);
  4182. end;
  4183. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4184. { This will start the close process and returns immediately (non blocking) }
  4185. procedure THttpCli.CloseAsync;
  4186. begin
  4187. DoRequestASync(httpCLOSE);
  4188. end;
  4189. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4190. function GetBaseUrl(const Url : String) : String;
  4191. var
  4192. I : Integer;
  4193. begin
  4194. I := 1;
  4195. while (I <= Length(Url)) and (Url[I] <> '?') do
  4196. Inc(I);
  4197. Dec(I);
  4198. while (I > 0) and (not (AnsiChar(Url[I]) in ['/', ':'])) do
  4199. Dec(I);
  4200. Result := Copy(Url, 1, I);
  4201. end;
  4202. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4203. procedure THttpCli.SetRequestVer(const Ver : String);
  4204. begin
  4205. if FRequestVer <> Ver then begin
  4206. if (Ver = '1.0') or (Ver = '1.1') then
  4207. FRequestVer := Ver
  4208. else
  4209. raise EHttpException.Create('Insupported HTTP version',
  4210. httperrVersion);
  4211. end;
  4212. end;
  4213. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4214. {$IFDEF WIN32}
  4215. function EncodeLine(
  4216. Encoding : THttpEncoding;
  4217. SrcData : PAnsiChar;
  4218. Size : Integer) : AnsiString;
  4219. var
  4220. Offset : Integer;
  4221. Pos1 : Integer;
  4222. Pos2 : Integer;
  4223. I : Integer;
  4224. begin
  4225. //SetLength(Result, Size * 4 div 3 + 4);
  4226. //FillChar(Result[1], Size * 4 div 3 + 2, #0);
  4227. Result := StringOfChar(AnsiChar(#0), Size * 4 div 3 + 4);
  4228. if Encoding = encUUEncode then begin
  4229. Result[1] := AnsiChar(((Size - 1) and $3f) + $21);
  4230. Size := ((Size + 2) div 3) * 3;
  4231. end;
  4232. Offset := 2;
  4233. Pos1 := 0;
  4234. Pos2 := 0;
  4235. case Encoding of
  4236. encUUEncode: Pos2 := 2;
  4237. encBase64, encMime: Pos2 := 1;
  4238. end;
  4239. Result[Pos2] := #0;
  4240. while Pos1 < Size do begin
  4241. if Offset > 0 then begin
  4242. Result[Pos2] := AnsiChar(ord(Result[Pos2]) or
  4243. ((ord(SrcData[Pos1]) and
  4244. ($3f shl Offset)) shr Offset));
  4245. Offset := Offset - 6;
  4246. Inc(Pos2);
  4247. Result[Pos2] := #0;
  4248. end
  4249. else if Offset < 0 then begin
  4250. Offset := Abs(Offset);
  4251. Result[Pos2] := AnsiChar(ord(Result[Pos2]) or
  4252. ((ord(SrcData[Pos1]) and
  4253. ($3f shr Offset)) shl Offset));
  4254. Offset := 8 - Offset;
  4255. Inc(Pos1);
  4256. end
  4257. else begin
  4258. Result[Pos2] := AnsiChar(ord(Result[Pos2]) or
  4259. ((ord(SrcData[Pos1]) and $3f)));
  4260. Inc(Pos2);
  4261. Inc(Pos1);
  4262. Result[Pos2] := #0;
  4263. Offset := 2;
  4264. end;
  4265. end;
  4266. case Encoding of
  4267. encUUEncode:
  4268. begin
  4269. if Offset = 2 then
  4270. Dec(Pos2);
  4271. for i := 2 to Pos2 do
  4272. Result[i] := bin2uue[ord(Result[i])+1];
  4273. end;
  4274. encBase64, encMime:
  4275. begin
  4276. if Offset = 2 then
  4277. Dec(Pos2);
  4278. for i := 1 to Pos2 do
  4279. Result[i] := bin2b64[ord(Result[i])+1];
  4280. while (Pos2 and 3) <> 0 do begin
  4281. Inc(Pos2);
  4282. Result[Pos2] := '=';
  4283. end;
  4284. end;
  4285. end;
  4286. SetLength(Result, Pos2);
  4287. end;
  4288. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4289. function EncodeStr(
  4290. Encoding : THttpEncoding;
  4291. const Value : RawByteString) : RawByteString;
  4292. begin
  4293. Result := EncodeLine(Encoding, PAnsiChar(Value), Length(Value));
  4294. end;
  4295. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4296. function EncodeStr(
  4297. Encoding : THttpEncoding;
  4298. const Value : UnicodeString;
  4299. ACodePage : LongWord = CP_ACP ) : UnicodeString;
  4300. var
  4301. AStr : AnsiString;
  4302. begin
  4303. AStr := UnicodeToAnsi(Value, ACodePage);
  4304. Result := String(EncodeLine(Encoding, PAnsiChar(AStr), Length(AStr)));
  4305. end;
  4306. {$ENDIF}
  4307. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4308. {$IFDEF CLR}
  4309. function EncodeStr(Encoding : THttpEncoding; const Value : String) : String;
  4310. var
  4311. Offset : Integer;
  4312. Pos1 : Integer;
  4313. Pos2 : Integer;
  4314. I : Integer;
  4315. Buf : StringBuilder;
  4316. Size : Integer;
  4317. begin
  4318. Size := Length(Value);
  4319. Buf := StringBuilder.Create(Size * 4 div 3 + 4);
  4320. if Encoding = encUUEncode then begin
  4321. Buf[0] := Char(((Size - 1) and $3f) + $21);
  4322. Size := ((Size + 2) div 3) * 3;
  4323. end;
  4324. Offset := 2;
  4325. Pos1 := 1;
  4326. Pos2 := 0;
  4327. case Encoding of
  4328. encUUEncode: Pos2 := 1;
  4329. encBase64, encMime: Pos2 := 0;
  4330. end;
  4331. Buf[Pos2] := #0;
  4332. while Pos1 < Size do begin
  4333. if Offset > 0 then begin
  4334. Buf[Pos2] := Char(ord(Result[Pos2]) or
  4335. ((ord(Value[Pos1]) and
  4336. ($3f shl Offset)) shr Offset));
  4337. Offset := Offset - 6;
  4338. Inc(Pos2);
  4339. Buf[Pos2] := #0;
  4340. end
  4341. else if Offset < 0 then begin
  4342. Offset := Abs(Offset);
  4343. Buf[Pos2] := Char(ord(Result[Pos2]) or
  4344. ((ord(Value[Pos1]) and
  4345. ($3f shr Offset)) shl Offset));
  4346. Offset := 8 - Offset;
  4347. Inc(Pos1);
  4348. end
  4349. else begin
  4350. Buf[Pos2] := Char(ord(Result[Pos2]) or
  4351. ((ord(Value[Pos1]) and $3f)));
  4352. Inc(Pos2);
  4353. Inc(Pos1);
  4354. Buf[Pos2] := #0;
  4355. Offset := 2;
  4356. end;
  4357. end;
  4358. case Encoding of
  4359. encUUEncode:
  4360. begin
  4361. if Offset = 2 then
  4362. Dec(Pos2);
  4363. for i := 2 to Pos2 do
  4364. Buf[i] := bin2uue[ord(Buf[i])+1];
  4365. end;
  4366. encBase64, encMime:
  4367. begin
  4368. if Offset = 2 then
  4369. Dec(Pos2);
  4370. for i := 1 to Pos2 do
  4371. Buf[i] := bin2b64[ord(Buf[i])+1];
  4372. while (Pos2 and 3) <> 0 do begin
  4373. Inc(Pos2);
  4374. Buf[Pos2] := '=';
  4375. end;
  4376. end;
  4377. end;
  4378. Buf.Length := Pos2;
  4379. Result := Buf.ToString;
  4380. end;
  4381. {$ENDIF}
  4382. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4383. {$IFDEF UseNTLMAuthentication}
  4384. function THttpCli.GetNTLMMessage1(const ForProxy: Boolean): String;
  4385. begin
  4386. { Result := FNTLM.GetMessage1(FNTLMHost, FNTLMDomain); }
  4387. { it is very common not to send domain and workstation strings on }
  4388. { the first message }
  4389. if ForProxy then
  4390. Result := 'Proxy-Authorization: NTLM ' + NtlmGetMessage1('', '')
  4391. else
  4392. Result := 'Authorization: NTLM ' + NtlmGetMessage1('', '');
  4393. end;
  4394. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4395. function THttpCli.GetNTLMMessage3(const HttpMethod: String;
  4396. const ForProxy: Boolean): String;
  4397. var
  4398. Hostname : String;
  4399. begin
  4400. { get local hostname }
  4401. try
  4402. Hostname := String(LocalHostName);
  4403. except
  4404. Hostname := '';
  4405. end;
  4406. { domain is not used }
  4407. { hostname is the local hostname }
  4408. if ForProxy then
  4409. Result := 'Proxy-Authorization: NTLM ' +
  4410. NtlmGetMessage3('',
  4411. Hostname,
  4412. FProxyUsername,
  4413. FProxyPassword,
  4414. FProxyNTLMMsg2Info.Challenge)
  4415. else
  4416. Result := 'Authorization: NTLM ' +
  4417. NtlmGetMessage3('',
  4418. Hostname,
  4419. { FNTLMUsercode, FNTLMPassword, }
  4420. FCurrUsername, FCurrPassword,
  4421. FNTLMMsg2Info.Challenge);
  4422. end;
  4423. {$ENDIF}
  4424. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4425. {$IFDEF UseBandwidthControl}
  4426. procedure THttpCli.BandwidthTimerTimer(Sender : TObject);
  4427. begin
  4428. if FBandwidthPaused then begin
  4429. FBandwidthPaused := FALSE;
  4430. Dec(FBandwidthCount, FBandwidthMaxCount);
  4431. // OutputDebugString('Resume');
  4432. FCtrlSocket.Resume;
  4433. end
  4434. else
  4435. FBandwidthCount := 0;
  4436. end;
  4437. {$ENDIF}
  4438. {$IFDEF UseContentCoding}
  4439. function THttpCli.GetOptions: THttpCliOptions;
  4440. begin
  4441. if FContentCodingHnd.Enabled then
  4442. Include(FOptions, httpoEnableContentCoding)
  4443. else
  4444. Exclude(FOptions, httpoEnableContentCoding);
  4445. if FContentCodingHnd.UseQuality then
  4446. Include(FOptions, httpoUseQuality)
  4447. else
  4448. Exclude(FOptions, httpoUseQuality);
  4449. Result := FOptions;
  4450. end;
  4451. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4452. procedure THttpCli.SetOptions(const Value : THttpCliOptions);
  4453. begin
  4454. FOptions := Value;
  4455. FContentCodingHnd.Enabled := (httpoEnableContentCoding in Value);
  4456. FContentCodingHnd.UseQuality := (httpoUseQuality in Value);
  4457. end;
  4458. {$ENDIF}
  4459. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4460. {$IFNDEF NO_DEBUG_LOG}
  4461. function THttpCli.GetIcsLogger: TIcsLogger; { V1.91 }
  4462. begin
  4463. Result := FCtrlSocket.IcsLogger;
  4464. end;
  4465. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4466. procedure THttpCli.SetIcsLogger(const Value: TIcsLogger); { V1.91 }
  4467. begin
  4468. FCtrlSocket.IcsLogger := Value;
  4469. end;
  4470. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4471. function THttpCli.CheckLogOptions(const LogOption: TLogOption): Boolean; { V1.91 }
  4472. begin
  4473. Result := Assigned(IcsLogger) and (LogOption in IcsLogger.LogOptions);
  4474. end;
  4475. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4476. procedure THttpCli.DebugLog(LogOption: TLogOption; const Msg: string); { V1.91 }
  4477. begin
  4478. if Assigned(IcsLogger) then
  4479. IcsLogger.DoDebugLog(Self, LogOption, Msg);
  4480. end;
  4481. {$ENDIF}
  4482. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4483. procedure THttpCli.ThreadAttach;
  4484. begin
  4485. inherited ThreadAttach;
  4486. FCtrlSocket.ThreadAttach;
  4487. {$IFDEF UseBandwidthControl}
  4488. FBandwidthTimer.Enabled := FTimerOldEnabled;
  4489. {$ENDIF}
  4490. end;
  4491. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4492. procedure THttpCli.ThreadDetach;
  4493. begin
  4494. {$IFDEF UseBandwidthControl}
  4495. FTimerOldEnabled := FBandwidthTimer.Enabled;
  4496. if FBandwidthTimer.Enabled then
  4497. FBandwidthTimer.Enabled := FALSE;
  4498. {$ENDIF}
  4499. inherited ThreadDetach;
  4500. FCtrlSocket.ThreadDetach;
  4501. end;
  4502. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4503. procedure THttpCli.CheckDelaySetReady; { 09/26/08 ML }
  4504. begin
  4505. if FAllowedToSend and ((FStatusCode = 401) or (FStatusCode = 407)) then
  4506. FDelaySetReady := TRUE
  4507. else
  4508. //SetReady;
  4509. PostMessage(Handle, FMsg_WM_HTTP_SET_READY, 0, 0);
  4510. end;
  4511. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4512. { You must define USE_SSL so that SSL code is included in the component. }
  4513. { Either in OverbyteIcsDefs.inc or in the project/package options. }
  4514. {$IFDEF USE_SSL}
  4515. {$IFDEF VER80}
  4516. Bomb('This unit requires a 32 bit compiler !');
  4517. {$ENDIF}
  4518. {$B-} { Enable partial boolean evaluation }
  4519. {$T-} { Untyped pointers }
  4520. {$X+} { Enable extended syntax }
  4521. {$H+} { Use long strings }
  4522. {$J+} { Allow typed constant to be modified }
  4523. { If you use Delphi 7, you may wants to disable warnings for unsafe type, }
  4524. { unsafe code and unsafe typecast in the project options. Those warning are }
  4525. { intended for .NET programs. You may also want to turn off deprecated }
  4526. { symbol and platform symbol warnings. }
  4527. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4528. procedure TSslHttpCli.CreateSocket;
  4529. begin
  4530. FCtrlSocket := TSslWSocket.Create(Self);
  4531. FCtrlSocket.SslEnable := TRUE;
  4532. end;
  4533. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4534. procedure TSslHttpCli.DoBeforeConnect;
  4535. begin
  4536. inherited DoBeforeConnect;
  4537. FCtrlSocket.OnSslVerifyPeer := TransferSslVerifyPeer;
  4538. FCtrlSocket.OnSslCliGetSession := TransferSslCliGetSession;
  4539. FCtrlSocket.OnSslCliNewSession := TransferSslCliNewSession;
  4540. FCtrlSocket.OnSslHandshakeDone := SslHandshakeDone;
  4541. FCtrlSocket.OnSslCliCertRequest := TransferSslCliCertRequest;
  4542. if FProxy <> '' then
  4543. FCtrlSocket.SslEnable := FALSE
  4544. else
  4545. FCtrlSocket.SslEnable := (FProtocol = 'https');
  4546. end;
  4547. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4548. function TSslHttpCli.GetSslContext: TSslContext;
  4549. begin
  4550. Result := FCtrlSocket.SslContext
  4551. end;
  4552. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4553. procedure TSslHttpCli.SetSslContext(Value: TSslContext);
  4554. begin
  4555. FCtrlSocket.SslContext := Value;
  4556. end;
  4557. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4558. procedure TSslHttpCli.TransferSslCliCertRequest(
  4559. Sender : TObject;
  4560. var Cert : TX509Base);
  4561. begin
  4562. if Assigned(FOnSslCliCertRequest) then
  4563. FOnSslCliCertRequest(Self, Cert);
  4564. end;
  4565. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4566. procedure TSslHttpCli.TransferSslVerifyPeer(
  4567. Sender : TObject;
  4568. var Ok : Integer;
  4569. Cert : TX509Base);
  4570. begin
  4571. if Assigned(FOnSslVerifyPeer) then
  4572. FOnSslVerifyPeer(Self, Ok, Cert);
  4573. end;
  4574. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4575. procedure TSslHttpCli.TransferSslCliGetSession(Sender: TObject;
  4576. var SslSession: Pointer; var FreeSession: Boolean);
  4577. begin
  4578. if Assigned(FOnSslCliGetSession) then
  4579. FOnSslCliGetSession(Self, SslSession, FreeSession);
  4580. end;
  4581. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4582. procedure TSslHttpCli.TransferSslCliNewSession(Sender: TObject;
  4583. SslSession: Pointer; WasReused: Boolean; var IncRefCount: Boolean);
  4584. begin
  4585. if Assigned(FOnSslCliNewSession) then
  4586. FOnSslCliNewSession(Self, SslSession, WasReused, IncRefCount);
  4587. end;
  4588. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4589. // Just to make UI easier: parse a semi-colon delimited texte string with
  4590. // a list of hosts and build the FSslAcceptableHosts list.
  4591. procedure TSslHttpCli.SetAcceptableHostsList(
  4592. const SemiColonSeparatedList : String);
  4593. var
  4594. Host : String;
  4595. Buf : String;
  4596. I : Integer;
  4597. begin
  4598. SslAcceptableHosts.Clear;
  4599. Buf := SemiColonSeparatedList;
  4600. while TRUE do begin
  4601. I := Pos(';', Buf);
  4602. if I > 0 then begin
  4603. Host := Trim(Copy(Buf, 1, I - 1));
  4604. if Host > '' then
  4605. SslAcceptableHosts.Add(Host);
  4606. Delete(Buf, 1, I);
  4607. end
  4608. else begin
  4609. Host := Trim(Buf);
  4610. if Host > '' then
  4611. SslAcceptableHosts.Add(Host);
  4612. break;
  4613. end;
  4614. end;
  4615. end;
  4616. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4617. procedure TSslHttpCli.SetSslAcceptableHosts(Value : TStrings);
  4618. begin
  4619. if Assigned(FCtrlSocket) then
  4620. FCtrlSocket.SslAcceptableHosts := Value;
  4621. end;
  4622. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4623. function TSslHttpCli.GetSslAcceptableHosts: TStrings;
  4624. begin
  4625. if Assigned(FCtrlSocket) then
  4626. Result := FCtrlSocket.SslAcceptableHosts
  4627. else
  4628. Result := nil;
  4629. end;
  4630. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  4631. {$ENDIF} // USE_SSL
  4632. end.