2
0

ftptsend.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. {==============================================================================|
  2. | Project : Ararat Synapse | 001.001.001 |
  3. |==============================================================================|
  4. | Content: Trivial FTP (TFTP) client and server |
  5. |==============================================================================|
  6. | Copyright (c)1999-2010, Lukas Gebauer |
  7. | All rights reserved. |
  8. | |
  9. | Redistribution and use in source and binary forms, with or without |
  10. | modification, are permitted provided that the following conditions are met: |
  11. | |
  12. | Redistributions of source code must retain the above copyright notice, this |
  13. | list of conditions and the following disclaimer. |
  14. | |
  15. | Redistributions in binary form must reproduce the above copyright notice, |
  16. | this list of conditions and the following disclaimer in the documentation |
  17. | and/or other materials provided with the distribution. |
  18. | |
  19. | Neither the name of Lukas Gebauer nor the names of its contributors may |
  20. | be used to endorse or promote products derived from this software without |
  21. | specific prior written permission. |
  22. | |
  23. | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
  24. | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
  25. | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
  26. | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR |
  27. | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
  28. | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
  29. | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
  30. | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
  31. | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
  32. | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
  33. | DAMAGE. |
  34. |==============================================================================|
  35. | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).|
  36. | Portions created by Lukas Gebauer are Copyright (c)2003-2010. |
  37. | All Rights Reserved. |
  38. |==============================================================================|
  39. | Contributor(s): |
  40. |==============================================================================|
  41. | History: see HISTORY.HTM from distribution package |
  42. | (Found at URL: http://www.ararat.cz/synapse/) |
  43. |==============================================================================}
  44. {: @abstract(TFTP client and server protocol)
  45. Used RFC: RFC-1350
  46. }
  47. {$IFDEF FPC}
  48. {$MODE DELPHI}
  49. {$ENDIF}
  50. {$Q-}
  51. {$H+}
  52. {$IFDEF UNICODE}
  53. {$WARN IMPLICIT_STRING_CAST OFF}
  54. {$WARN IMPLICIT_STRING_CAST_LOSS OFF}
  55. {$ENDIF}
  56. unit ftptsend;
  57. interface
  58. uses
  59. SysUtils, Classes,
  60. blcksock, synautil;
  61. const
  62. cTFTPProtocol = '69';
  63. cTFTP_RRQ = word(1);
  64. cTFTP_WRQ = word(2);
  65. cTFTP_DTA = word(3);
  66. cTFTP_ACK = word(4);
  67. cTFTP_ERR = word(5);
  68. type
  69. {:@abstract(Implementation of TFTP client and server)
  70. Note: Are you missing properties for specify server address and port? Look to
  71. parent @link(TSynaClient) too!}
  72. TTFTPSend = class(TSynaClient)
  73. private
  74. FSock: TUDPBlockSocket;
  75. FErrorCode: integer;
  76. FErrorString: string;
  77. FData: TMemoryStream;
  78. FRequestIP: string;
  79. FRequestPort: string;
  80. function SendPacket(Cmd: word; Serial: word; const Value: string): Boolean;
  81. function RecvPacket(Serial: word; var Value: string): Boolean;
  82. public
  83. constructor Create;
  84. destructor Destroy; override;
  85. {:Upload @link(data) as file to TFTP server.}
  86. function SendFile(const Filename: string): Boolean;
  87. {:Download file from TFTP server to @link(data).}
  88. function RecvFile(const Filename: string): Boolean;
  89. {:Acts as TFTP server and wait for client request. When some request
  90. incoming within Timeout, result is @true and parametres is filled with
  91. information from request. You must handle this request, validate it, and
  92. call @link(ReplyError), @link(ReplyRecv) or @link(ReplySend) for send reply
  93. to TFTP Client.}
  94. function WaitForRequest(var Req: word; var filename: string): Boolean;
  95. {:send error to TFTP client, when you acts as TFTP server.}
  96. procedure ReplyError(Error: word; Description: string);
  97. {:Accept uploaded file from TFTP client to @link(data), when you acts as
  98. TFTP server.}
  99. function ReplyRecv: Boolean;
  100. {:Accept download request file from TFTP client and send content of
  101. @link(data), when you acts as TFTP server.}
  102. function ReplySend: Boolean;
  103. published
  104. {:Code of TFTP error.}
  105. property ErrorCode: integer read FErrorCode;
  106. {:Human readable decription of TFTP error. (if is sended by remote side)}
  107. property ErrorString: string read FErrorString;
  108. {:MemoryStream with datas for sending or receiving}
  109. property Data: TMemoryStream read FData;
  110. {:Address of TFTP remote side.}
  111. property RequestIP: string read FRequestIP write FRequestIP;
  112. {:Port of TFTP remote side.}
  113. property RequestPort: string read FRequestPort write FRequestPort;
  114. end;
  115. implementation
  116. constructor TTFTPSend.Create;
  117. begin
  118. inherited Create;
  119. FSock := TUDPBlockSocket.Create;
  120. FSock.Owner := self;
  121. FTargetPort := cTFTPProtocol;
  122. FData := TMemoryStream.Create;
  123. FErrorCode := 0;
  124. FErrorString := '';
  125. end;
  126. destructor TTFTPSend.Destroy;
  127. begin
  128. FSock.Free;
  129. FData.Free;
  130. inherited Destroy;
  131. end;
  132. function TTFTPSend.SendPacket(Cmd: word; Serial: word; const Value: string): Boolean;
  133. var
  134. s, sh: string;
  135. begin
  136. FErrorCode := 0;
  137. FErrorString := '';
  138. Result := false;
  139. if Cmd <> 2 then
  140. s := CodeInt(Cmd) + CodeInt(Serial) + Value
  141. else
  142. s := CodeInt(Cmd) + Value;
  143. FSock.SendString(s);
  144. s := FSock.RecvPacket(FTimeout);
  145. if FSock.LastError = 0 then
  146. if length(s) >= 4 then
  147. begin
  148. sh := CodeInt(4) + CodeInt(Serial);
  149. if Pos(sh, s) = 1 then
  150. Result := True
  151. else
  152. if s[1] = #5 then
  153. begin
  154. FErrorCode := DecodeInt(s, 3);
  155. Delete(s, 1, 4);
  156. FErrorString := SeparateLeft(s, #0);
  157. end;
  158. end;
  159. end;
  160. function TTFTPSend.RecvPacket(Serial: word; var Value: string): Boolean;
  161. var
  162. s: string;
  163. ser: word;
  164. begin
  165. FErrorCode := 0;
  166. FErrorString := '';
  167. Result := False;
  168. Value := '';
  169. s := FSock.RecvPacket(FTimeout);
  170. if FSock.LastError = 0 then
  171. if length(s) >= 4 then
  172. if DecodeInt(s, 1) = 3 then
  173. begin
  174. ser := DecodeInt(s, 3);
  175. if ser = Serial then
  176. begin
  177. Delete(s, 1, 4);
  178. Value := s;
  179. S := CodeInt(4) + CodeInt(ser);
  180. FSock.SendString(s);
  181. Result := FSock.LastError = 0;
  182. end
  183. else
  184. begin
  185. S := CodeInt(5) + CodeInt(5) + 'Unexcepted serial#' + #0;
  186. FSock.SendString(s);
  187. end;
  188. end;
  189. if DecodeInt(s, 1) = 5 then
  190. begin
  191. FErrorCode := DecodeInt(s, 3);
  192. Delete(s, 1, 4);
  193. FErrorString := SeparateLeft(s, #0);
  194. end;
  195. end;
  196. function TTFTPSend.SendFile(const Filename: string): Boolean;
  197. var
  198. s: string;
  199. ser: word;
  200. n, n1, n2: integer;
  201. begin
  202. Result := False;
  203. FErrorCode := 0;
  204. FErrorString := '';
  205. FSock.CloseSocket;
  206. FSock.Connect(FTargetHost, FTargetPort);
  207. try
  208. if FSock.LastError = 0 then
  209. begin
  210. s := Filename + #0 + 'octet' + #0;
  211. if not Sendpacket(2, 0, s) then
  212. Exit;
  213. ser := 1;
  214. FData.Position := 0;
  215. n1 := FData.Size div 512;
  216. n2 := FData.Size mod 512;
  217. for n := 1 to n1 do
  218. begin
  219. s := ReadStrFromStream(FData, 512);
  220. // SetLength(s, 512);
  221. // FData.Read(pointer(s)^, 512);
  222. if not Sendpacket(3, ser, s) then
  223. Exit;
  224. inc(ser);
  225. end;
  226. s := ReadStrFromStream(FData, n2);
  227. // SetLength(s, n2);
  228. // FData.Read(pointer(s)^, n2);
  229. if not Sendpacket(3, ser, s) then
  230. Exit;
  231. Result := True;
  232. end;
  233. finally
  234. FSock.CloseSocket;
  235. end;
  236. end;
  237. function TTFTPSend.RecvFile(const Filename: string): Boolean;
  238. var
  239. s: string;
  240. ser: word;
  241. begin
  242. Result := False;
  243. FErrorCode := 0;
  244. FErrorString := '';
  245. FSock.CloseSocket;
  246. FSock.Connect(FTargetHost, FTargetPort);
  247. try
  248. if FSock.LastError = 0 then
  249. begin
  250. s := CodeInt(1) + Filename + #0 + 'octet' + #0;
  251. FSock.SendString(s);
  252. if FSock.LastError <> 0 then
  253. Exit;
  254. FData.Clear;
  255. ser := 1;
  256. repeat
  257. if not RecvPacket(ser, s) then
  258. Exit;
  259. inc(ser);
  260. WriteStrToStream(FData, s);
  261. // FData.Write(pointer(s)^, length(s));
  262. until length(s) <> 512;
  263. FData.Position := 0;
  264. Result := true;
  265. end;
  266. finally
  267. FSock.CloseSocket;
  268. end;
  269. end;
  270. function TTFTPSend.WaitForRequest(var Req: word; var filename: string): Boolean;
  271. var
  272. s: string;
  273. begin
  274. Result := False;
  275. FErrorCode := 0;
  276. FErrorString := '';
  277. FSock.CloseSocket;
  278. {$IFDEF ULTIBO}
  279. FSock.EnableReuse(True); //Closed socket is not destroyed immediately, need to allow port reuse
  280. {$ENDIF}
  281. FSock.Bind('0.0.0.0', FTargetPort);
  282. if FSock.LastError = 0 then
  283. begin
  284. s := FSock.RecvPacket(FTimeout);
  285. if FSock.LastError = 0 then
  286. if Length(s) >= 4 then
  287. begin
  288. FRequestIP := FSock.GetRemoteSinIP;
  289. FRequestPort := IntToStr(FSock.GetRemoteSinPort);
  290. Req := DecodeInt(s, 1);
  291. delete(s, 1, 2);
  292. filename := Trim(SeparateLeft(s, #0));
  293. s := SeparateRight(s, #0);
  294. s := SeparateLeft(s, #0);
  295. Result := lowercase(trim(s)) = 'octet';
  296. end;
  297. end;
  298. end;
  299. procedure TTFTPSend.ReplyError(Error: word; Description: string);
  300. var
  301. s: string;
  302. begin
  303. FSock.CloseSocket;
  304. {$IFDEF ULTIBO}
  305. FSock.EnableReuse(True); //Closed socket is not destroyed immediately, need to allow port reuse
  306. FSock.Bind('0.0.0.0', FTargetPort); //Some clients (eg Windows) only accept replies from the port they sent to
  307. {$ENDIF}
  308. FSock.Connect(FRequestIP, FRequestPort);
  309. s := CodeInt(5) + CodeInt(Error) + Description + #0;
  310. FSock.SendString(s);
  311. FSock.CloseSocket;
  312. end;
  313. function TTFTPSend.ReplyRecv: Boolean;
  314. var
  315. s: string;
  316. ser: integer;
  317. begin
  318. Result := False;
  319. FErrorCode := 0;
  320. FErrorString := '';
  321. FSock.CloseSocket;
  322. {$IFDEF ULTIBO}
  323. FSock.EnableReuse(True); //Closed socket is not destroyed immediately, need to allow port reuse
  324. FSock.Bind('0.0.0.0', FTargetPort); //Some clients (eg Windows) only accept replies from the port they sent to
  325. {$ENDIF}
  326. FSock.Connect(FRequestIP, FRequestPort);
  327. try
  328. s := CodeInt(4) + CodeInt(0);
  329. FSock.SendString(s);
  330. FData.Clear;
  331. ser := 1;
  332. repeat
  333. if not RecvPacket(ser, s) then
  334. Exit;
  335. inc(ser);
  336. WriteStrToStream(FData, s);
  337. // FData.Write(pointer(s)^, length(s));
  338. until length(s) <> 512;
  339. FData.Position := 0;
  340. Result := true;
  341. finally
  342. FSock.CloseSocket;
  343. end;
  344. end;
  345. function TTFTPSend.ReplySend: Boolean;
  346. var
  347. s: string;
  348. ser: word;
  349. n, n1, n2: integer;
  350. begin
  351. Result := False;
  352. FErrorCode := 0;
  353. FErrorString := '';
  354. FSock.CloseSocket;
  355. {$IFDEF ULTIBO}
  356. FSock.EnableReuse(True); //Closed socket is not destroyed immediately, need to allow port reuse
  357. FSock.Bind('0.0.0.0', FTargetPort); //Some clients (eg Windows) only accept replies from the port they sent to
  358. {$ENDIF}
  359. FSock.Connect(FRequestIP, FRequestPort);
  360. try
  361. ser := 1;
  362. FData.Position := 0;
  363. n1 := FData.Size div 512;
  364. n2 := FData.Size mod 512;
  365. for n := 1 to n1 do
  366. begin
  367. s := ReadStrFromStream(FData, 512);
  368. // SetLength(s, 512);
  369. // FData.Read(pointer(s)^, 512);
  370. if not Sendpacket(3, ser, s) then
  371. Exit;
  372. inc(ser);
  373. end;
  374. s := ReadStrFromStream(FData, n2);
  375. // SetLength(s, n2);
  376. // FData.Read(pointer(s)^, n2);
  377. if not Sendpacket(3, ser, s) then
  378. Exit;
  379. Result := True;
  380. finally
  381. FSock.CloseSocket;
  382. end;
  383. end;
  384. {==============================================================================}
  385. end.