ftptsend.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. {==============================================================================|
  2. | Project : Ararat Synapse | 001.001.000 |
  3. |==============================================================================|
  4. | Content: Trivial FTP (TFTP) client and server |
  5. |==============================================================================|
  6. | Copyright (c)1999-2004, 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-2004. |
  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. unit ftptsend;
  53. interface
  54. uses
  55. SysUtils, Classes,
  56. blcksock, synautil;
  57. const
  58. cTFTPProtocol = '69';
  59. cTFTP_RRQ = word(1);
  60. cTFTP_WRQ = word(2);
  61. cTFTP_DTA = word(3);
  62. cTFTP_ACK = word(4);
  63. cTFTP_ERR = word(5);
  64. type
  65. {:@abstract(Implementation of TFTP client and server)
  66. Note: Are you missing properties for specify server address and port? Look to
  67. parent @link(TSynaClient) too!}
  68. TTFTPSend = class(TSynaClient)
  69. private
  70. FSock: TUDPBlockSocket;
  71. FErrorCode: integer;
  72. FErrorString: string;
  73. FData: TMemoryStream;
  74. FRequestIP: string;
  75. FRequestPort: string;
  76. function SendPacket(Cmd: word; Serial: word; const Value: string): Boolean;
  77. function RecvPacket(Serial: word; var Value: string): Boolean;
  78. public
  79. constructor Create;
  80. destructor Destroy; override;
  81. {:Upload @link(data) as file to TFTP server.}
  82. function SendFile(const Filename: string): Boolean;
  83. {:Download file from TFTP server to @link(data).}
  84. function RecvFile(const Filename: string): Boolean;
  85. {:Acts as TFTP server and wait for client request. When some request
  86. incoming within Timeout, result is @true and parametres is filled with
  87. information from request. You must handle this request, validate it, and
  88. call @link(ReplyError), @link(ReplyRecv) or @link(ReplySend) for send reply
  89. to TFTP Client.}
  90. function WaitForRequest(var Req: word; var filename: string): Boolean;
  91. {:send error to TFTP client, when you acts as TFTP server.}
  92. procedure ReplyError(Error: word; Description: string);
  93. {:Accept uploaded file from TFTP client to @link(data), when you acts as
  94. TFTP server.}
  95. function ReplyRecv: Boolean;
  96. {:Accept download request file from TFTP client and send content of
  97. @link(data), when you acts as TFTP server.}
  98. function ReplySend: Boolean;
  99. published
  100. {:Code of TFTP error.}
  101. property ErrorCode: integer read FErrorCode;
  102. {:Human readable decription of TFTP error. (if is sended by remote side)}
  103. property ErrorString: string read FErrorString;
  104. {:MemoryStream with datas for sending or receiving}
  105. property Data: TMemoryStream read FData;
  106. {:Address of TFTP remote side.}
  107. property RequestIP: string read FRequestIP write FRequestIP;
  108. {:Port of TFTP remote side.}
  109. property RequestPort: string read FRequestPort write FRequestPort;
  110. end;
  111. implementation
  112. constructor TTFTPSend.Create;
  113. begin
  114. inherited Create;
  115. FSock := TUDPBlockSocket.Create;
  116. FTargetPort := cTFTPProtocol;
  117. FData := TMemoryStream.Create;
  118. FErrorCode := 0;
  119. FErrorString := '';
  120. end;
  121. destructor TTFTPSend.Destroy;
  122. begin
  123. FSock.Free;
  124. FData.Free;
  125. inherited Destroy;
  126. end;
  127. function TTFTPSend.SendPacket(Cmd: word; Serial: word; const Value: string): Boolean;
  128. var
  129. s, sh: string;
  130. begin
  131. FErrorCode := 0;
  132. FErrorString := '';
  133. Result := false;
  134. if Cmd <> 2 then
  135. s := CodeInt(Cmd) + CodeInt(Serial) + Value
  136. else
  137. s := CodeInt(Cmd) + Value;
  138. FSock.SendString(s);
  139. s := FSock.RecvPacket(FTimeout);
  140. if FSock.LastError = 0 then
  141. if length(s) >= 4 then
  142. begin
  143. sh := CodeInt(4) + CodeInt(Serial);
  144. if Pos(sh, s) = 1 then
  145. Result := True
  146. else
  147. if s[1] = #5 then
  148. begin
  149. FErrorCode := DecodeInt(s, 3);
  150. Delete(s, 1, 4);
  151. FErrorString := SeparateLeft(s, #0);
  152. end;
  153. end;
  154. end;
  155. function TTFTPSend.RecvPacket(Serial: word; var Value: string): Boolean;
  156. var
  157. s: string;
  158. ser: word;
  159. begin
  160. FErrorCode := 0;
  161. FErrorString := '';
  162. Result := False;
  163. Value := '';
  164. s := FSock.RecvPacket(FTimeout);
  165. if FSock.LastError = 0 then
  166. if length(s) >= 4 then
  167. if DecodeInt(s, 1) = 3 then
  168. begin
  169. ser := DecodeInt(s, 3);
  170. if ser = Serial then
  171. begin
  172. Delete(s, 1, 4);
  173. Value := s;
  174. S := CodeInt(4) + CodeInt(ser);
  175. FSock.SendString(s);
  176. Result := FSock.LastError = 0;
  177. end
  178. else
  179. begin
  180. S := CodeInt(5) + CodeInt(5) + 'Unexcepted serial#' + #0;
  181. FSock.SendString(s);
  182. end;
  183. end;
  184. if DecodeInt(s, 1) = 5 then
  185. begin
  186. FErrorCode := DecodeInt(s, 3);
  187. Delete(s, 1, 4);
  188. FErrorString := SeparateLeft(s, #0);
  189. end;
  190. end;
  191. function TTFTPSend.SendFile(const Filename: string): Boolean;
  192. var
  193. s: string;
  194. ser: word;
  195. n, n1, n2: integer;
  196. begin
  197. Result := False;
  198. FErrorCode := 0;
  199. FErrorString := '';
  200. FSock.CloseSocket;
  201. FSock.Connect(FTargetHost, FTargetPort);
  202. try
  203. if FSock.LastError = 0 then
  204. begin
  205. s := Filename + #0 + 'octet' + #0;
  206. if not Sendpacket(2, 0, s) then
  207. Exit;
  208. ser := 1;
  209. FData.Position := 0;
  210. n1 := FData.Size div 512;
  211. n2 := FData.Size mod 512;
  212. for n := 1 to n1 do
  213. begin
  214. s := ReadStrFromStream(FData, 512);
  215. // SetLength(s, 512);
  216. // FData.Read(pointer(s)^, 512);
  217. if not Sendpacket(3, ser, s) then
  218. Exit;
  219. inc(ser);
  220. end;
  221. s := ReadStrFromStream(FData, n2);
  222. // SetLength(s, n2);
  223. // FData.Read(pointer(s)^, n2);
  224. if not Sendpacket(3, ser, s) then
  225. Exit;
  226. Result := True;
  227. end;
  228. finally
  229. FSock.CloseSocket;
  230. end;
  231. end;
  232. function TTFTPSend.RecvFile(const Filename: string): Boolean;
  233. var
  234. s: string;
  235. ser: word;
  236. begin
  237. Result := False;
  238. FErrorCode := 0;
  239. FErrorString := '';
  240. FSock.CloseSocket;
  241. FSock.Connect(FTargetHost, FTargetPort);
  242. try
  243. if FSock.LastError = 0 then
  244. begin
  245. s := CodeInt(1) + Filename + #0 + 'octet' + #0;
  246. FSock.SendString(s);
  247. if FSock.LastError <> 0 then
  248. Exit;
  249. FData.Clear;
  250. ser := 1;
  251. repeat
  252. if not RecvPacket(ser, s) then
  253. Exit;
  254. inc(ser);
  255. WriteStrToStream(FData, s);
  256. // FData.Write(pointer(s)^, length(s));
  257. until length(s) <> 512;
  258. FData.Position := 0;
  259. Result := true;
  260. end;
  261. finally
  262. FSock.CloseSocket;
  263. end;
  264. end;
  265. function TTFTPSend.WaitForRequest(var Req: word; var filename: string): Boolean;
  266. var
  267. s: string;
  268. begin
  269. Result := False;
  270. FErrorCode := 0;
  271. FErrorString := '';
  272. FSock.CloseSocket;
  273. FSock.Bind('0.0.0.0', FTargetPort);
  274. if FSock.LastError = 0 then
  275. begin
  276. s := FSock.RecvPacket(FTimeout);
  277. if FSock.LastError = 0 then
  278. if Length(s) >= 4 then
  279. begin
  280. FRequestIP := FSock.GetRemoteSinIP;
  281. FRequestPort := IntToStr(FSock.GetRemoteSinPort);
  282. Req := DecodeInt(s, 1);
  283. delete(s, 1, 2);
  284. filename := Trim(SeparateLeft(s, #0));
  285. s := SeparateRight(s, #0);
  286. s := SeparateLeft(s, #0);
  287. Result := lowercase(trim(s)) = 'octet';
  288. end;
  289. end;
  290. end;
  291. procedure TTFTPSend.ReplyError(Error: word; Description: string);
  292. var
  293. s: string;
  294. begin
  295. FSock.CloseSocket;
  296. FSock.Connect(FRequestIP, FRequestPort);
  297. s := CodeInt(5) + CodeInt(Error) + Description + #0;
  298. FSock.SendString(s);
  299. FSock.CloseSocket;
  300. end;
  301. function TTFTPSend.ReplyRecv: Boolean;
  302. var
  303. s: string;
  304. ser: integer;
  305. begin
  306. Result := False;
  307. FErrorCode := 0;
  308. FErrorString := '';
  309. FSock.CloseSocket;
  310. FSock.Connect(FRequestIP, FRequestPort);
  311. try
  312. s := CodeInt(4) + CodeInt(0);
  313. FSock.SendString(s);
  314. FData.Clear;
  315. ser := 1;
  316. repeat
  317. if not RecvPacket(ser, s) then
  318. Exit;
  319. inc(ser);
  320. WriteStrToStream(FData, s);
  321. // FData.Write(pointer(s)^, length(s));
  322. until length(s) <> 512;
  323. FData.Position := 0;
  324. Result := true;
  325. finally
  326. FSock.CloseSocket;
  327. end;
  328. end;
  329. function TTFTPSend.ReplySend: Boolean;
  330. var
  331. s: string;
  332. ser: word;
  333. n, n1, n2: integer;
  334. begin
  335. Result := False;
  336. FErrorCode := 0;
  337. FErrorString := '';
  338. FSock.CloseSocket;
  339. FSock.Connect(FRequestIP, FRequestPort);
  340. try
  341. ser := 1;
  342. FData.Position := 0;
  343. n1 := FData.Size div 512;
  344. n2 := FData.Size mod 512;
  345. for n := 1 to n1 do
  346. begin
  347. s := ReadStrFromStream(FData, 512);
  348. // SetLength(s, 512);
  349. // FData.Read(pointer(s)^, 512);
  350. if not Sendpacket(3, ser, s) then
  351. Exit;
  352. inc(ser);
  353. end;
  354. s := ReadStrFromStream(FData, n2);
  355. // SetLength(s, n2);
  356. // FData.Read(pointer(s)^, n2);
  357. if not Sendpacket(3, ser, s) then
  358. Exit;
  359. Result := True;
  360. finally
  361. FSock.CloseSocket;
  362. end;
  363. end;
  364. {==============================================================================}
  365. end.