IdFSP.pas 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333
  1. {
  2. $Project$
  3. $Workfile$
  4. $Revision$
  5. $DateUTC$
  6. $Id$
  7. This file is part of the Indy (Internet Direct) project, and is offered
  8. under the dual-licensing agreement described on the Indy website.
  9. (http://www.indyproject.org/)
  10. Copyright:
  11. (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  12. }
  13. {
  14. $Log$
  15. Rev 1.17 2/10/2005 2:24:38 PM JPMugaas
  16. Minor Restructures for some new UnixTime Service components.
  17. Rev 1.16 1/17/2005 7:29:12 PM JPMugaas
  18. Now uses new TIdBuffer functionality.
  19. Rev 1.15 1/9/2005 6:08:06 PM JPMugaas
  20. Payload size now specified for CC_GET_FILE.
  21. Now will raise exception if you specify a packet size less than 512.
  22. Rev 1.12 11/12/2004 8:37:36 AM JPMugaas
  23. Minor compile error. OOPS!!!
  24. Rev 1.11 11/11/2004 11:22:54 PM JPMugaas
  25. Removed an $IFDEF that's no longer needed.
  26. Rev 1.10 11/8/2004 8:36:04 PM JPMugaas
  27. Added value for command that may appear later.
  28. Rev 1.9 11/7/2004 11:34:16 PM JPMugaas
  29. Now uses inherited methods again. The inherited methods now use the Binding
  30. methods we used here.
  31. Rev 1.8 11/6/2004 1:46:34 AM JPMugaas
  32. Minor bug fix for when there is no data in a reply to CC_GET_PRO.
  33. Rev 1.7 11/5/2004 7:55:02 PM JPMugaas
  34. Changed to use, Connect, Recv, Send, and Disconnect instead of ReceiveFrom
  35. and SendTo. This should improve performance as we do make repeated contacts
  36. to the host and UDP connect will cause the stack to filter out packets that
  37. aren't from the peer. There should only be one DNS resolution per session
  38. making this more efficient (cutting down to about 87 seconds to get a dir).
  39. Rev 1.4 10/31/2004 1:49:58 AM JPMugaas
  40. Now uses item type from TIdFTPList for dirs and files. We don't use Skip
  41. items or end of dir marker items.
  42. Rev 1.2 10/30/2004 10:23:58 PM JPMugaas
  43. Should be much faster.
  44. Rev 1.1 10/30/2004 7:04:26 PM JPMugaas
  45. FSP Upload.
  46. Rev 1.0 10/29/2004 12:34:20 PM JPMugaas
  47. File Services Protocol implementation started
  48. }
  49. unit IdFSP;
  50. interface
  51. {$i IdCompilerDefines.inc}
  52. uses
  53. Classes,
  54. IdException,
  55. IdFTPList,
  56. IdGlobal,
  57. IdThreadSafe,
  58. IdUDPClient;
  59. {This is based on:
  60. http://cvs.sourceforge.net/viewcvs.py/fsp/fsp/doc/PROTOCOL?rev=1.4&view=markup
  61. and the Java Lib at fsp.sourceforge.net was also referenced.
  62. I have verified this on a CygWin build of the FSP Server at fsp.sourceforge.net.
  63. }
  64. {
  65. FSP Packet format:
  66. HEADER - size = Fixed size 12 bytes. Always present.
  67. DATA - size = defined in header (DATA_LENGTH)
  68. XTRA DATA- size = packet_size - header_size (12) - DATA_LENGTH
  69. Maximal data size DATA_LENGTH + XTRA_DATA length is 1024. Clients and servers
  70. are not required to support XTRA DATA (but in current FSP implementation does).
  71. If XTRA DATA are provided, there must be also contained in MESSAGE_CHECKSUM.
  72. HEADER FORMAT (12 bytes)
  73. byte FSP_COMMAND
  74. byte MESSAGE_CHECKSUM
  75. word KEY
  76. word SEQUENCE
  77. word DATA_LENGTH
  78. long FILE_POSITION
  79. MESSAGE_CHECKSUM
  80. Entire packet (HEADER + DATA + XTRA DATA) is checksumed. When computing a
  81. checksum use zero in place of MESSAGE_CHECKSUM header field.
  82. Due to some unknown reason, method of computing checksums is different in each
  83. direction. For packets travelling from server to client initial checksum
  84. value is zero, otherwise it is HEADER + DATA + XTRA DATA size.
  85. Checksums in server->client direction are computed as follows:
  86. /* assume that we have already zeroed checksum in packet */
  87. unsigned int sum,checksum;
  88. for(t = packet_start, sum = 0; t < packet_end; sum += *t++);
  89. checksum= sum + (sum >> 8);
  90. KEY
  91. Client's message to server contain a KEY value that is the same as the KEY
  92. value of the previous message received from the server. KEY is choosen random
  93. by server.
  94. }
  95. {
  96. CC_VERSION 0x10- Get server version string and setup
  97. request
  98. file position: ignored
  99. data: not used
  100. xtra data: not used
  101. reply
  102. file position: size of optional extra version data
  103. data: ASCIIZ Server version string
  104. xtra data: optional extra version data
  105. byte - FLAGS
  106. bit 0 set - server does logging
  107. bit 1 set - server is read only
  108. bit 2 set - reverse lookup required
  109. bit 3 set - server is in private mode
  110. bit 4 set - thruput control
  111. if bit 4 is set thruput info follows
  112. long - max_thruput allowed (in bytes/sec)
  113. word - max. packet size supported by server
  114. }
  115. const
  116. IdPORT_FSP = 21;
  117. HSIZE = 12; //header size
  118. DEF_MAXSPACE = 1012; //data length
  119. DEF_MAXSIZE = DEF_MAXSPACE+HSIZE; //default maximum packet size
  120. //commands
  121. CC_VERSION = $10; //Get server version string and setup
  122. CC_INFO = $11; //return server's extended info block
  123. CC_ERR = $40; //error response from server
  124. CC_GET_DIR = $41; // get a directory listing
  125. CC_GET_FILE = $42; // get a file
  126. CC_UP_LOAD = $43; // open a file for writing
  127. CC_INSTALL = $44; // close and install file opened for writing
  128. CC_DEL_FILE = $45; // delete a file
  129. CC_DEL_DIR = $46; // delete a directory
  130. CC_GET_PRO = $47; // get directory protection
  131. CC_SET_PRO = $48; // set directory protection
  132. CC_MAKE_DIR = $49; // create a directory
  133. CC_BYE = $4A; // finish a session
  134. CC_GRAB_FILE = $4B; // atomic get+delete a file
  135. CC_GRAB_DONE = $4C; // atomic get+delete a file done
  136. CC_STAT = $4D; // get information about file/directory
  137. CC_RENAME = $4E; // rename file or directory
  138. CC_CH_PASSW = $4F; // change password
  139. //Reserved commands:
  140. CC_LIMIT = $80;
  141. { commands > 0x7F will have extended
  142. header. No such extensions or commands
  143. which uses that are known today. This
  144. header will be used in protocol version 3. }
  145. CC_TEST = $81; //reserved for testing of new header
  146. RDTYPE_END = $00;
  147. RDTYPE_FILE = $01;
  148. RDTYPE_DIR = $02;
  149. RDTYPE_SKIP = $2A; //42
  150. MINTIMEOUT = 1340; //1.34 seconds
  151. MAXTIMEOUT = 300000; //300 seconds
  152. type
  153. EIdFSPException = class(EIdException);
  154. EIdFSPFileAlreadyExists = class(EIdFSPException);
  155. EIdFSPFileNotFound = class(EIdFSPException);
  156. EIdFSPProtException = class(EIdFSPException);
  157. EIdFSPPacketTooSmall = class(EIdFSPException);
  158. {
  159. RDIRENT.HEADER types:
  160. RDTYPE_END 0x00
  161. RDTYPE_FILE 0x01
  162. RDTYPE_DIR 0x02
  163. RDTYPE_SKIP 0x2A
  164. }
  165. TIdFSPStatInfo = class(TCollectionItem)
  166. protected
  167. FModifiedDateGMT : TDateTime;
  168. FModifiedDate: TDateTime;
  169. //Size is Int64 in case FSP 3 has an expansion, otherise, it can only handle
  170. //file sizes up 4 GB's. It's not a bug, it's a feature.
  171. FSize: Int64;
  172. FItemType :TIdDirItemType;
  173. published
  174. property ItemType :TIdDirItemType read FItemType write FItemType;
  175. property Size: Int64 read FSize write FSize;
  176. property ModifiedDate: TDateTime read FModifiedDate write FModifiedDate;
  177. property ModifiedDateGMT : TDateTime read FModifiedDateGMT write FModifiedDateGMT;
  178. end;
  179. TIdFSPListItem = class(TIdFSPStatInfo)
  180. protected
  181. FFileName: string;
  182. published
  183. property FileName: string read FFileName write FFileName;
  184. end;
  185. TIdFSPListItems = class(TCollection)
  186. protected
  187. function GetItems(AIndex: Integer): TIdFSPListItem;
  188. procedure SetItems(AIndex: Integer; const Value: TIdFSPListItem);
  189. public
  190. function Add: TIdFSPListItem;
  191. constructor Create; reintroduce;
  192. function ParseEntries(const AData : TIdBytes; const ADataLen : UInt32) : Boolean;
  193. function IndexOf(AItem: TIdFSPListItem): Integer;
  194. property Items[AIndex: Integer]: TIdFSPListItem read GetItems write SetItems; default;
  195. end;
  196. TIdFSPDirInfo = class(TObject)
  197. protected
  198. FOwnsDir,
  199. FCanDeleteFiles,
  200. FCanAddFiles,
  201. FCanMakeDir,
  202. FOnlyOwnerCanReadFiles,
  203. FHasReadMe,
  204. FCanBeListed,
  205. FCanRenameFiles : Boolean;
  206. FReadMe : String;
  207. public
  208. property OwnsDir : Boolean read FOwnsDir write FOwnsDir;
  209. property CanDeleteFiles : Boolean read FCanDeleteFiles write FCanDeleteFiles;
  210. property CanAddFiles : Boolean read FCanAddFiles write FCanAddFiles;
  211. property CanMakeDir : Boolean read FCanMakeDir write FCanMakeDir;
  212. property OnlyOwnerCanReadFiles : Boolean read FOnlyOwnerCanReadFiles write FOnlyOwnerCanReadFiles;
  213. property HasReadMe : Boolean read FHasReadMe write FHasReadMe;
  214. {
  215. Compatibility
  216. Versions older than 2.8.1b6 do not uses bits 6 and 7. This
  217. causes that directory can be listable even it do not have
  218. 6th bit set.
  219. }
  220. property CanBeListed : Boolean read FCanBeListed write FCanBeListed;
  221. property CanRenameFiles : Boolean read FCanRenameFiles write FCanRenameFiles;
  222. property ReadMe : String read FReadMe write FReadMe;
  223. end;
  224. TIdFSPPacket = class(TObject)
  225. protected
  226. FCmd: Byte;
  227. FFilePosition: UInt32;
  228. FData: TIdBytes;
  229. FDataLen : Word;
  230. FExtraData: TIdBytes;
  231. // FExtraDataLen : UInt32;
  232. FSequence: Word;
  233. FKey: Word;
  234. FValid : Boolean;
  235. public
  236. constructor Create;
  237. function WritePacket : TIdBytes;
  238. procedure ReadPacket(const AData : TIdBytes; const ALen : UInt32);
  239. property Valid : Boolean read FValid;
  240. property Cmd : Byte read FCmd write FCmd;
  241. property Key : Word read FKey write FKey;
  242. property Sequence : Word read FSequence write FSequence;
  243. property FilePosition : UInt32 read FFilePosition write FFilePosition;
  244. property Data : TIdBytes read FData write FData;
  245. property DataLen : Word read FDataLen write FDataLen;
  246. property ExtraData : TIdBytes read FExtraData write FExtraData;
  247. // property WritePacket : TIdBytes read GetWritePacket write SetWritePacket;
  248. end;
  249. TIdFSPLogEvent = procedure (Sender : TObject; APacket : TIdFSPPacket) of object;
  250. TIdFSP = class(TIdUDPClient)
  251. protected
  252. FConEstablished : Boolean;
  253. FSequence : Word;
  254. FKey : Word;
  255. FSystemDesc: string;
  256. FSystemServerLogs : Boolean;
  257. FSystemReadOnly : Boolean;
  258. FSystemReverseLookupRequired : Boolean;
  259. FSystemPrivateMode : Boolean;
  260. FSystemAcceptsExtraData : Boolean;
  261. FThruputControl : Boolean;
  262. FServerMaxThruPut : UInt32; //bytes per sec
  263. FServerMaxPacketSize : Word; //maximum packet size server supports
  264. FClientMaxPacketSize : Word; //maximum packet we wish to support
  265. FDirectoryListing: TIdFSPListItems;
  266. FDirInfo : TIdFSPDirInfo;
  267. FStatInfo : TIdFSPStatInfo;
  268. FOnRecv, FOnSend : TIdFSPLogEvent;
  269. FAbortFlag : TIdThreadSafeBoolean;
  270. FInCmd : TIdThreadSafeBoolean;
  271. //note: This is optimized for performance - DO NOT MESS with it even if you don't like it
  272. //or think its wrong. There is a performance penalty that is noticable with downloading,
  273. //uploading, and dirs because those use a series of packets - not one and we limited in
  274. //packet size. We also do not want to eat CPU cycles excessively which I've noticed
  275. //with previous code.
  276. procedure SendCmdOnce(ACmdPacket, ARecvPacket : TIdFSPPacket; var VTempBuf : TIdBytes; const ARaiseException : Boolean=True); overload;
  277. procedure SendCmdOnce(const ACmd : Byte; const AData, AExtraData : TIdBytes;
  278. const AFilePosition : Int64; //in case FSP 3.0 does support more than 4GB
  279. var VData, VExtraData : TIdBytes; const ARaiseException : Boolean=True); overload;
  280. procedure SendCmd(ACmdPacket, ARecvPacket : TIdFSPPacket; var VTempBuf : TIdBytes; const ARaiseException : Boolean=True); overload;
  281. procedure SendCmd(const ACmd : Byte; const AData, AExtraData : TIdBYtes;
  282. const AFilePosition : Int64; //in case FSP 3.0 does support more than 4GB
  283. var VData, VExtraData : TIdBytes; const ARaiseException : Boolean=True); overload;
  284. procedure SendCmd(const ACmd : Byte; const AData : TIdBYtes;
  285. const AFilePosition : Int64; //in case FSP 3.0 does support more than 4GB
  286. var VData, VExtraData : TIdBytes; const ARaiseException : Boolean=True); overload;
  287. procedure ParseDirInfo(const ABuf, AExtraBuf: TIdBytes; ADir : TIdFSPDirInfo);
  288. procedure InitComponent; override;
  289. function MaxBufferSize : Word;
  290. function PrefPayloadSize : Word;
  291. procedure SetClientMaxPacketSize(const AValue: Word);
  292. public
  293. destructor Destroy; override;
  294. procedure Connect; override; //this is so we can use it similarly to FTP
  295. procedure Disconnect; override;
  296. procedure Version;
  297. procedure AbortCmd;
  298. procedure Delete(const AFilename: string);
  299. procedure RemoveDir(const ADirName: string);
  300. procedure Rename(const ASourceFile, ADestFile: string);
  301. procedure MakeDir(const ADirName: string);
  302. //this is so we can use it similarly to FTP
  303. //and also sends a BYE command which is the courteous thing to do.
  304. procedure List; overload;
  305. procedure List(const ASpecifier: string); overload;
  306. procedure GetDirInfo(const ADIR : String); overload;
  307. procedure GetDirInfo(const ADIR : String; ADirInfo : TIdFSPDirInfo); overload;
  308. procedure GetStatInfo(const APath : String);
  309. procedure Get(const ASourceFile, ADestFile: string; const ACanOverwrite: Boolean = False;
  310. AResume: Boolean = False); overload;
  311. procedure Get(const ASourceFile: string; ADest: TStream; AResume: Boolean = False); overload;
  312. procedure Put(const ASource: TStream; const ADestFile: string; const AGMTTime : TDateTime = 0); overload;
  313. procedure Put(const ASourceFile: string; const ADestFile: string=''); overload;
  314. property SystemDesc: string read FSystemDesc;
  315. property SystemServerLogs : Boolean read FSystemServerLogs;
  316. property SystemReadOnly : Boolean read FSystemReadOnly;
  317. property SystemReverseLookupRequired : Boolean read FSystemReverseLookupRequired;
  318. property SystemPrivateMode : Boolean read FSystemPrivateMode;
  319. property SystemAcceptsExtraData : Boolean read FSystemAcceptsExtraData;
  320. property ThruputControl : Boolean read FThruputControl;
  321. property ServerMaxThruPut : UInt32 read FServerMaxThruPut;
  322. property ServerMaxPacketSize : Word read FServerMaxPacketSize;
  323. property ClientMaxPacketSize : Word read FClientMaxPacketSize write SetClientMaxPacketSize;
  324. property DirectoryListing: TIdFSPListItems read FDirectoryListing;
  325. property DirInfo : TIdFSPDirInfo read FDirInfo;
  326. property StatInfo : TIdFSPStatInfo read FStatInfo;
  327. published
  328. property Port default IdPORT_FSP;
  329. property OnWork;
  330. property OnWorkBegin;
  331. property OnWorkEnd;
  332. property OnRecv : TIdFSPLogEvent read FOnRecv write FOnRecv;
  333. property OnSend : TIdFSPLogEvent read FOnSend write FOnSend;
  334. end;
  335. implementation
  336. uses
  337. //facilitate inlining only.
  338. {$IFDEF KYLIXCOMPAT}
  339. Libc,
  340. {$ENDIF}
  341. {$IFDEF USE_VCL_POSIX}
  342. Posix.SysSelect,
  343. Posix.SysTime,
  344. Posix.Unistd,
  345. {$ENDIF}
  346. {$IFDEF WINDOWS}
  347. {$IFDEF USE_INLINE}
  348. Windows,
  349. {$ELSE}
  350. //facilitate inlining only.
  351. {$IFDEF VCL_2009_OR_ABOVE}
  352. Windows,
  353. {$ENDIF}
  354. {$ENDIF}
  355. {$ENDIF}
  356. {$IFDEF DOTNET}
  357. {$IFDEF USE_INLINE}
  358. System.IO,
  359. System.Threading,
  360. {$ENDIF}
  361. {$ENDIF}
  362. IdComponent, IdGlobalProtocols, IdResourceStringsProtocols, IdStack, IdStream, SysUtils;
  363. function ParseASCIIZPos(const ABytes: TIdBytes ; const ALen : UInt32; var VPos : UInt32): String;
  364. var
  365. i : UInt32;
  366. begin
  367. if VPos < ALen then begin
  368. for i := VPos to ALen-1 do begin
  369. if ABytes[i] = 0 then begin
  370. Break;
  371. end;
  372. end;
  373. VPos := i;
  374. Result := BytesToString(ABytes, i);
  375. end else begin
  376. Result := '';
  377. end;
  378. end;
  379. function ParseASCIIZLen(const ABytes : TIdBytes; const ALen : UInt32) : String;
  380. var
  381. LPos : UInt32;
  382. begin
  383. LPos := 0;
  384. Result := ParseASCIIZPos(ABytes, ALen, LPos);
  385. end;
  386. function ParseASCIIZ(const ABytes : TIdBytes) : String;
  387. var
  388. LPos : UInt32;
  389. begin
  390. LPos := 0;
  391. Result := ParseASCIIZPos(ABytes, Length(ABytes), LPos);
  392. end;
  393. procedure ParseStatInfo(const AData : TIdBytes; VL : TIdFSPStatInfo; var VI : UInt32);
  394. var
  395. LC : UInt32;
  396. begin
  397. //we don't parse the file type because there is some variation between CC_GET_DIR and CC_STAT
  398. CopyBytesToHostUInt32(AData, VI, LC);
  399. VL.FModifiedDateGMT := UnixDateTimeToDelphiDateTime(LC);
  400. VL.FModifiedDate := UTCTimeToLocalTime(VL.FModifiedDateGMT);
  401. Inc(VI, 4);
  402. CopyBytesToHostUInt32(AData, VI, LC);
  403. VL.Size := LC;
  404. Inc(VI, 5); //we want to skip over the type byte we processed earlier
  405. end;
  406. { TIdFSP }
  407. procedure TIdFSP.Connect;
  408. begin
  409. FSequence := 1;
  410. FKey := 0;
  411. FServerMaxThruPut := 0;
  412. FServerMaxPacketSize := DEF_MAXSIZE;
  413. inherited Connect;
  414. end;
  415. destructor TIdFSP.Destroy;
  416. begin
  417. Disconnect;
  418. FreeAndNil(FDirInfo);
  419. FreeAndNil(FDirectoryListing);
  420. FreeAndNil(FStatInfo);
  421. FreeAndNil(FAbortFlag);
  422. FreeAndNil(FInCmd);
  423. inherited Destroy;
  424. end;
  425. procedure TIdFSP.Disconnect;
  426. var
  427. LBuf, LData, LExtra : TIdBytes;
  428. begin
  429. AbortCmd;
  430. if FConEstablished then begin
  431. SetLength(LBuf, 0);
  432. SendCmd(CC_BYE, LBuf, 0, LData, LExtra);
  433. inherited Disconnect;
  434. end;
  435. FConEstablished := False;
  436. end;
  437. procedure TIdFSP.Get(const ASourceFile: string; ADest: TStream; AResume: Boolean);
  438. var
  439. LSendPacket : TIdFSPPacket;
  440. LRecvPacket : TIdFSPPacket;
  441. LLen : Integer;
  442. LTmpBuf : TIdBytes;
  443. begin
  444. SetLength(LTmpBuf, MaxBufferSize);
  445. LSendPacket := TIdFSPPacket.Create;
  446. try
  447. LRecvPacket := TIdFSPPacket.Create;
  448. try
  449. if AResume then begin
  450. LSendPacket.FFilePosition := ADest.Position;
  451. end else begin
  452. LSendPacket.FFilePosition := 0;
  453. end;
  454. LSendPacket.Cmd := CC_GET_FILE;
  455. LSendPacket.FData := ToBytes(ASourceFile+#0);
  456. LSendPacket.FDataLen := Length(LSendPacket.FData);
  457. //specify a preferred block size
  458. SetLength(LSendPacket.FExtraData, 2);
  459. CopyTIdNetworkUInt16(PrefPayloadSize, LSendPacket.FExtraData, 0);
  460. BeginWork(wmRead);
  461. try
  462. repeat
  463. SendCmd(LSendPacket, LRecvPacket, LTmpBuf);
  464. LLen := LRecvPacket.FDataLen; //Length(LRecvPacket.Data);
  465. if LLen > 0 then begin
  466. TIdStreamHelper.Write(ADest, LRecvPacket.Data, LLen);
  467. DoWork(wmRead, LLen);
  468. Inc(LSendPacket.FFilePosition, LLen);
  469. end else begin
  470. Break;
  471. end;
  472. until False;
  473. finally
  474. EndWork(wmRead);
  475. end;
  476. finally
  477. FreeAndNil(LRecvPacket);
  478. end;
  479. finally
  480. FreeAndNil(LSendPacket);
  481. end;
  482. end;
  483. procedure TIdFSP.Get(const ASourceFile, ADestFile: string; const ACanOverwrite: Boolean; AResume: Boolean);
  484. var
  485. LDestStream: TStream;
  486. begin
  487. if ACanOverwrite and (not AResume) then begin
  488. SysUtils.DeleteFile(ADestFile);
  489. LDestStream := TIdFileCreateStream.Create(ADestFile);
  490. end
  491. else if (not ACanOverwrite) and AResume then begin
  492. LDestStream := TIdAppendFileStream.Create(ADestFile);
  493. end
  494. else begin
  495. raise EIdFSPFileAlreadyExists.Create(RSDestinationFileAlreadyExists);
  496. end;
  497. try
  498. Get(ASourceFile, LDestStream, AResume);
  499. finally
  500. FreeAndNil(LDestStream);
  501. end;
  502. end;
  503. procedure TIdFSP.GetDirInfo(const ADIR: String);
  504. begin
  505. GetDirInfo(ADir, FDirInfo);
  506. end;
  507. procedure TIdFSP.InitComponent;
  508. begin
  509. inherited InitComponent;
  510. FAbortFlag := TIdThreadSafeBoolean.Create;
  511. FAbortFlag.Value := False;
  512. //you have to use FPort or this will cause a stack overflow
  513. FPort := IdPORT_FSP;
  514. FSequence := 0;
  515. FKey := 0;
  516. FDirInfo := TIdFSPDirInfo.Create;
  517. FDirectoryListing := TIdFSPListItems.Create;
  518. FStatInfo := TIdFSPStatInfo.Create(nil);
  519. BroadcastEnabled := False;
  520. FConEstablished := False;
  521. FClientMaxPacketSize := DEF_MAXSIZE;
  522. FInCmd := TIdThreadSafeBoolean.Create;
  523. FInCmd.Value := False;
  524. end;
  525. procedure TIdFSP.List;
  526. begin
  527. List('/');
  528. end;
  529. procedure TIdFSP.List(const ASpecifier: string);
  530. var
  531. LSendPacket : TIdFSPPacket;
  532. LRecvPacket : TIdFSPPacket;
  533. LTmpBuf : TIdBytes;
  534. LSpecifier: String;
  535. begin
  536. LSpecifier := ASpecifier;
  537. if LSpecifier = '' then begin
  538. LSpecifier := '/';
  539. end;
  540. SetLength(LTmpBuf, MaxBufferSize);
  541. LSendPacket := TIdFSPPacket.Create;
  542. try
  543. LRecvPacket := TIdFSPPacket.Create;
  544. try
  545. LSendPacket.Cmd := CC_GET_DIR;
  546. LSendPacket.FFilePosition := 0;
  547. SetLength(LRecvPacket.FData, MaxBufferSize);
  548. SetLength(LSendPacket.FExtraData, 2);
  549. CopyTIdNetworkUInt16(PrefPayloadSize, LSendPacket.FExtraData, 0);
  550. FDirectoryListing.Clear;
  551. repeat
  552. LSendPacket.Data := ToBytes(LSpecifier+#0);
  553. LSendPacket.DataLen := Length(LSendPacket.Data);
  554. SendCmd(LSendPacket,LRecvPacket,LTmpBuf);
  555. if LRecvPacket.DataLen > 0 then begin
  556. Inc(LSendPacket.FFilePosition, LRecvPacket.DataLen);
  557. end else begin
  558. Break;
  559. end;
  560. if LRecvPacket.DataLen < PrefPayloadSize then begin
  561. Break;
  562. end;
  563. until FDirectoryListing.ParseEntries(LRecvPacket.FData, LRecvPacket.FDataLen);
  564. finally
  565. FreeAndNil(LRecvPacket);
  566. end;
  567. finally
  568. FreeAndNil(LSendPacket);
  569. end;
  570. end;
  571. procedure TIdFSP.SendCmd(const ACmd: Byte; const AData, AExtraData: TIdBytes;
  572. const AFilePosition: Int64; var VData, VExtraData: TIdBytes;
  573. const ARaiseException : Boolean = True);
  574. var
  575. LSendPacket : TIdFSPPacket;
  576. LRecvPacket : TIdFSPPacket;
  577. LTmpBuf : TIdBytes;
  578. begin
  579. SetLength(LTmpBuf, MaxBufferSize);
  580. LSendPacket := TIdFSPPacket.Create;
  581. try
  582. LRecvPacket := TIdFSPPacket.Create;
  583. try
  584. LSendPacket.Cmd := ACmd;
  585. LSendPacket.FilePosition := AFilePosition;
  586. LSendPacket.Data := AData;
  587. LSendPacket.FDataLen := Length(AData);
  588. LSendPacket.ExtraData := AExtraData;
  589. SendCmd(LSendPacket, LRecvPacket, LTmpBuf, ARaiseException);
  590. VData := LRecvPacket.Data;
  591. VExtraData := LRecvPacket.ExtraData;
  592. finally
  593. FreeAndNil(LRecvPacket);
  594. end;
  595. finally
  596. FreeAndNil(LSendPacket);
  597. end;
  598. end;
  599. procedure TIdFSP.SendCmd(const ACmd: Byte; const AData: TIdBytes;
  600. const AFilePosition: Int64; var VData, VExtraData: TIdBytes;
  601. const ARaiseException : Boolean = True);
  602. var
  603. LExtraData : TIdBytes;
  604. begin
  605. SetLength(LExtraData, 0);
  606. SendCmd(ACmd, AData, LExtraData, AFilePosition, VData, VExtraData, ARaiseException);
  607. end;
  608. procedure TIdFSP.Version;
  609. var
  610. LData, LBuf, LExtraBuf : TIdBytes;
  611. LDetails : Byte;
  612. begin
  613. {
  614. we use this instead of SendCmd because of the following note
  615. in the protocol specification
  616. FILE SERVICE PROTOCOL VERSION 2, OFFICIAL PROTOCOL DEFINITION, FSP v2,
  617. Document version 0.17, Last updated 25 Dec 2004
  618. (http://fsp.sourceforge.net/doc/PROTOCOL.txt):
  619. Note
  620. Some fsp servers do not responds to this command,
  621. because this command is used by FSP scanners and
  622. servers do not wishes to be detected.
  623. }
  624. SetLength(LData, 0);
  625. SendCmdOnce(CC_VERSION, LData, LData, 0, LBuf, LExtraBuf);
  626. if Length(LData) > 0 then begin
  627. FSystemDesc := ParseASCIIZ(LBuf);
  628. if Length(LExtraBuf) > 0 then begin
  629. LDetails := LExtraBuf[0];
  630. //bit 0 set - server does logging
  631. FSystemServerLogs := (LDetails and $01) = $01;
  632. //bit 1 set - server is read only
  633. FSystemReadOnly := (LDetails and $02) = $02;
  634. //bit 2 set - reverse lookup required
  635. FSystemReverseLookupRequired := (LDetails and $04) = $04;
  636. //bit 3 set - server is in private mode
  637. FSystemPrivateMode := (LDetails and $08) = $08;
  638. //if bit 4 is set thruput info follows
  639. FThruputControl := (LDetails and $10) = $10;
  640. //bit 5 set - server accept XTRA
  641. //DATA on input
  642. FSystemAcceptsExtraData := (LDetails and $20) = $20;
  643. //long - max_thruput allowed (in bytes/sec)
  644. //word - max. packet size supported by server
  645. if FThruputControl then begin
  646. if Length(LExtraBuf) > 4 then begin
  647. CopyBytesToHostUInt32(LExtraBuf, 1, FServerMaxThruPut);
  648. if Length(LExtraBuf) > 6 then begin
  649. CopyBytesToHostUInt16(LExtraBuf, 5, FServerMaxPacketSize);
  650. end;
  651. end;
  652. end else
  653. begin
  654. if Length(LExtraBuf) > 2 then begin
  655. CopyBytesToHostUInt16(LExtraBuf, 1, FServerMaxPacketSize);
  656. end;
  657. end;
  658. end;
  659. end;
  660. end;
  661. procedure TIdFSP.SendCmd(ACmdPacket, ARecvPacket: TIdFSPPacket;
  662. var VTempBuf : TIdBytes; const ARaiseException : Boolean = True);
  663. var
  664. LLen : Integer;
  665. LSendBuf : TIdBytes;
  666. LMSec : Integer;
  667. begin
  668. FInCmd.Value := True;
  669. try
  670. Inc(FSequence);
  671. FAbortFlag.Value := False;
  672. //we don't set the temp buff size here for speed.
  673. ACmdPacket.Key := FKey;
  674. ACmdPacket.Sequence := FSequence;
  675. LMSec := MINTIMEOUT;
  676. LSendBuf := ACmdPacket.WritePacket;
  677. //It's very important that you have some way of aborting this loop
  678. //if you do not and the server does not reply, this can go for infinity.
  679. //AbortCmd is ThreadSafe.
  680. while not FAbortFlag.Value do
  681. begin
  682. SendBuffer(LSendBuf);
  683. if Assigned(FOnSend) then begin
  684. FOnSend(Self, ACmdPacket);
  685. end;
  686. IndySleep(5); //this is so we don't eat up all of the CPU
  687. LLen := ReceiveBuffer(VTempBuf, LMsec);
  688. ARecvPacket.ReadPacket(VTempBuf, LLen);
  689. if ARecvPacket.Valid then begin
  690. if Assigned(FOnRecv) then begin
  691. FOnRecv(Self, ARecvPacket);
  692. end;
  693. if ARecvPacket.Sequence = FSequence then begin
  694. Break;
  695. end;
  696. end;
  697. LMSec := Round(LMSec * 1.5);
  698. if LMSec > MAXTIMEOUT then begin
  699. LMSec := MAXTIMEOUT;
  700. end;
  701. end;
  702. if not FAbortFlag.Value then begin
  703. FKey := ARecvPacket.Key;
  704. end;
  705. FAbortFlag.Value := False;
  706. if (ARecvPacket.Cmd = CC_ERR) and ARaiseException then begin
  707. raise EIdFSPProtException.Create(ParseASCIIZLen(ARecvPacket.Data, ARecvPacket.DataLen));
  708. end;
  709. finally
  710. FInCmd.Value := False;
  711. end;
  712. end;
  713. procedure TIdFSP.GetStatInfo(const APath: String);
  714. var
  715. LData, LBuf,LExtraBuf : TIdBytes;
  716. i : UInt32;
  717. begin
  718. {
  719. data format is the same as in directory listing with exception
  720. that there is no file name appended. If file do not exists or
  721. there is other problem (no access rights) return type of file is
  722. 0.
  723. struct STAT {
  724. long time;
  725. long size;
  726. byte type;
  727. }
  728. i := 0;
  729. LData := ToBytes(APath + #0);
  730. SendCmd(CC_STAT, LData, 0, LBuf, LExtraBuf);
  731. if Length(LBuf) > 8 then begin
  732. case LBuf[8] of
  733. 0 : //file not found
  734. begin
  735. raise EIdFSPFileNotFound.Create(RSFSPNotFound);
  736. end;
  737. RDTYPE_FILE :
  738. begin
  739. FStatInfo.ItemType := ditFile;
  740. end;
  741. RDTYPE_DIR :
  742. begin
  743. FStatInfo.ItemType := ditDirectory;
  744. end;
  745. end;
  746. ParseStatInfo(LBuf, FStatInfo, i);
  747. end;
  748. end;
  749. procedure TIdFSP.Put(const ASource: TStream; const ADestFile: string; const AGMTTime: TDateTime);
  750. var
  751. LUnixDate : UInt32;
  752. LSendPacket : TIdFSPPacket;
  753. LRecvPacket : TIdFSPPacket;
  754. LPosition : UInt32;
  755. LLen : Integer;
  756. LTmpBuf : TIdBytes;
  757. begin
  758. LPosition := 0;
  759. SetLength(LTmpBuf, MaxBufferSize);
  760. LSendPacket := TIdFSPPacket.Create;
  761. try
  762. LRecvPacket := TIdFSPPacket.Create;
  763. try
  764. SetLength(LSendPacket.FData, PrefPayloadSize);
  765. LSendPacket.Cmd := CC_UP_LOAD;
  766. repeat
  767. LLen := TIdStreamHelper.ReadBytes(ASource, LSendPacket.FData, PrefPayloadSize, 0);
  768. if LLen = 0 then begin
  769. Break;
  770. end;
  771. LSendPacket.FDataLen := LLen;
  772. LSendPacket.FilePosition := LPosition;
  773. SendCmd(LSendPacket, LRecvPacket, LTmpBuf);
  774. if LLen < PrefPayloadSize then begin
  775. Break;
  776. end;
  777. Inc(LPosition, LLen);
  778. until False;
  779. //send the Install packet
  780. LSendPacket.Cmd := CC_INSTALL;
  781. LSendPacket.FilePosition := 0;
  782. LSendPacket.Data := ToBytes(ADestFile+#0);
  783. LSendPacket.FDataLen := Length(LSendPacket.Data);
  784. //File date - optional
  785. if AGMTTime = 0 then begin
  786. SetLength(LSendPacket.FExtraData, 0);
  787. end else begin
  788. LUnixDate := DateTimeToUnix(AGMTTime);
  789. SetLength(LSendPacket.FExtraData, 4);
  790. CopyTIdNetworkUInt32(LUnixDate, LSendPacket.FExtraData, 0);
  791. end;
  792. SendCmd(LSendPacket, LRecvPacket, LTmpBuf);
  793. finally
  794. FreeAndNil(LRecvPacket);
  795. end;
  796. finally
  797. FreeAndNil(LSendPacket);
  798. end;
  799. end;
  800. procedure TIdFSP.Put(const ASourceFile, ADestFile: string);
  801. var
  802. LSourceStream: TStream;
  803. LDestFileName : String;
  804. begin
  805. LDestFileName := ADestFile;
  806. if LDestFileName = '' then begin
  807. LDestFileName := ExtractFileName(ASourceFile);
  808. end;
  809. LSourceStream := TIdReadFileExclusiveStream.Create(ASourceFile);
  810. try
  811. Put(LSourceStream, LDestFileName, GetGMTDateByName(ASourceFile));
  812. finally
  813. FreeAndNil(LSourceStream);
  814. end;
  815. end;
  816. procedure TIdFSP.Delete(const AFilename: string);
  817. var
  818. LData : TIdBytes;
  819. LBuf, LExBuf : TIdBytes;
  820. begin
  821. LData := ToBytes(AFilename+#0);
  822. SendCmd(CC_DEL_FILE, LData, 0, LBuf, LExBuf);
  823. end;
  824. procedure TIdFSP.MakeDir(const ADirName: string);
  825. var
  826. LData : TIdBytes;
  827. LBuf, LExBuf : TIdBytes;
  828. begin
  829. LData := ToBytes(ADirName+#0);
  830. SendCmd(CC_MAKE_DIR, LData, 0, LBuf, LExBuf);
  831. ParseDirInfo(LBuf, LExBuf, FDirInfo);
  832. end;
  833. procedure TIdFSP.RemoveDir(const ADirName: string);
  834. var
  835. LData : TIdBytes;
  836. LBuf, LExBuf : TIdBytes;
  837. begin
  838. LData := ToBytes(ADirName+#0);
  839. SendCmd(CC_DEL_DIR, LData, 0, LBuf, LExBuf);
  840. end;
  841. procedure TIdFSP.Rename(const ASourceFile, ADestFile: string);
  842. var
  843. LBuf, LData, LDataExt : TIdBytes;
  844. begin
  845. SetLength(LData, 0);
  846. SetLength(LDataExt, 0);
  847. LBuf := ToBytes(ASourceFile+#0+ADestFile);
  848. SendCmd(CC_RENAME, LBuf, 0, LData, LDataExt);
  849. end;
  850. procedure TIdFSP.ParseDirInfo(const ABuf, AExtraBuf: TIdBytes; ADir : TIdFSPDirInfo);
  851. begin
  852. ADir.ReadMe := ParseASCIIZ(ABuf);
  853. if Length(AExtraBuf) > 0 then begin
  854. //0 - caller owns the directory
  855. ADir.OwnsDir := (AExtraBuf[0] and $01) = $01;
  856. //1 - files can be deleted from this dir
  857. ADir.CanDeleteFiles := (AExtraBuf[0] and $02) = $02;
  858. // 2 - files can be added to this dir
  859. ADir.CanAddFiles := (AExtraBuf[0] and $04) = $04;
  860. //3 - new subdirectories can be created
  861. ADir.CanMakeDir := (AExtraBuf[0] and $08) = $08;
  862. //4 - files are NOT readable by non-owners
  863. ADir.OnlyOwnerCanReadFiles := (AExtraBuf[0] and $10) = $10;
  864. //5 - directory contain an readme file
  865. ADir.HasReadMe := (AExtraBuf[0] and $20) = $20;
  866. //6 - directory can be listed
  867. ADir.CanBeListed := (AExtraBuf[0] and $40) = $40;
  868. //7 - files can be renamed in this directory
  869. ADir.CanRenameFiles := (AExtraBuf[0] and $80) = $80;
  870. end;
  871. end;
  872. procedure TIdFSP.GetDirInfo(const ADIR: String; ADirInfo: TIdFSPDirInfo);
  873. var
  874. LData, LBuf, LExtraBuf : TIdBytes;
  875. begin
  876. LData := ToBytes(ADIR+#0);
  877. SendCmd(CC_GET_PRO, LData, 0, LBuf, LExtraBuf);
  878. ParseDirInfo(LBuf, LExtraBuf, ADirInfo);
  879. end;
  880. procedure TIdFSP.SendCmdOnce(ACmdPacket, ARecvPacket: TIdFSPPacket;
  881. var VTempBuf: TIdBytes; const ARaiseException: Boolean);
  882. var
  883. LLen : Integer;
  884. LBuf : TIdBytes;
  885. LSendBuf : TIdBytes;
  886. //This is for where there may not be a reply to a command from a server.
  887. begin
  888. Inc(FSequence);
  889. SetLength(LBuf, MaxBufferSize);
  890. ACmdPacket.Key := FKey;
  891. ACmdPacket.Sequence := FSequence;
  892. LSendBuf := ACmdPacket.WritePacket;
  893. SendBuffer(LSendBuf);
  894. if Assigned(FOnSend) then begin
  895. FOnSend(Self, ACmdPacket);
  896. end;
  897. repeat
  898. LLen := ReceiveBuffer(LBuf, MINTIMEOUT);
  899. if LLen = 0 then begin
  900. Break;
  901. end;
  902. ARecvPacket.ReadPacket(LBuf, LLen);
  903. if ARecvPacket.Valid then begin
  904. if Assigned(FOnRecv) then begin
  905. FOnRecv(Self, ARecvPacket);
  906. end;
  907. if (ARecvPacket.Sequence = FSequence) then begin
  908. FKey := ARecvPacket.Key;
  909. Break;
  910. end;
  911. end;
  912. until False;
  913. if (ARecvPacket.Cmd = CC_ERR) and ARaiseException then begin
  914. raise EIdFSPProtException.Create(ParseASCIIZLen(ARecvPacket.Data, ARecvPacket.DataLen));
  915. end;
  916. end;
  917. procedure TIdFSP.SendCmdOnce(const ACmd: Byte; const AData,
  918. AExtraData: TIdBytes; const AFilePosition: Int64; var VData,
  919. VExtraData: TIdBytes; const ARaiseException: Boolean);
  920. var
  921. LSendPacket : TIdFSPPacket;
  922. LRecvPacket : TIdFSPPacket;
  923. LTmpBuf : TIdBytes;
  924. begin
  925. SetLength(LTmpBuf, MaxBufferSize);
  926. LSendPacket := TIdFSPPacket.Create;
  927. try
  928. LRecvPacket := TIdFSPPacket.Create;
  929. try
  930. LSendPacket.Cmd := ACmd;
  931. LSendPacket.FilePosition := AFilePosition;
  932. LSendPacket.Data := AData;
  933. LSendPacket.FDataLen := Length(AData);
  934. LSendPacket.ExtraData := AExtraData;
  935. SendCmdOnce(LSendPacket, LRecvPacket, LTmpBuf, ARaiseException);
  936. VData := LRecvPacket.Data;
  937. VExtraData := LRecvPacket.ExtraData;
  938. finally
  939. FreeAndNil(LRecvPacket);
  940. end;
  941. finally
  942. FreeAndNil(LSendPacket);
  943. end;
  944. end;
  945. function TIdFSP.MaxBufferSize: Word;
  946. //use only for calculating buffer for reading UDP packet
  947. begin
  948. Result := IndyMax(FClientMaxPacketSize, DEF_MAXSIZE);
  949. Result := IndyMax(FServerMaxPacketSize, Result);
  950. Inc(Result, HSIZE); //just in case
  951. end;
  952. function TIdFSP.PrefPayloadSize: Word;
  953. //maximum size of the data feild we want to use
  954. begin
  955. Result := IndyMin(FClientMaxPacketSize, FServerMaxPacketSize);
  956. Dec(Result, HSIZE);
  957. end;
  958. procedure TIdFSP.SetClientMaxPacketSize(const AValue: Word);
  959. begin
  960. //maximal size required by RFC
  961. //note that 512 gives a payload of 500 bytes in a packet
  962. if AValue < 512 then begin
  963. raise EIdFSPPacketTooSmall.Create(RSFSPPacketTooSmall);
  964. end;
  965. FClientMaxPacketSize := AValue;
  966. end;
  967. procedure TIdFSP.AbortCmd;
  968. begin
  969. //we don't want to go into the abort loop if there is no command
  970. //being send. If that happens, your program could hang.
  971. if FInCmd.Value then
  972. begin
  973. FAbortFlag.Value := True;
  974. repeat
  975. IndySleep(5);
  976. //we need to wait until the SendCmd routine catches the Abort
  977. //request so you don't get an AV in a worker thread.
  978. until not FAbortFlag.Value;
  979. end;
  980. end;
  981. { TIdFSPPacket }
  982. constructor TIdFSPPacket.Create;
  983. begin
  984. inherited Create;
  985. FCmd := 0;
  986. FFilePosition := 0;
  987. FDataLen := 0;
  988. SetLength(FData, 0);
  989. SetLength(FExtraData, 0);
  990. FSequence := 0;
  991. FKey := 0;
  992. end;
  993. function TIdFSPPacket.WritePacket : TIdBytes;
  994. var
  995. LExtraDataLen, LW : Word;
  996. LC, LSum : UInt32;
  997. i : Integer;
  998. //ported from:
  999. //http://cvs.sourceforge.net/viewcvs.py/fsp/javalib/FSPpacket.java?rev=1.6&view=markup
  1000. begin
  1001. LExtraDataLen := Length(FExtraData);
  1002. SetLength(Result, HSIZE + FDataLen + LExtraDataLen);
  1003. //cmd
  1004. Result[0] := Cmd;
  1005. //checksum
  1006. Result[1] := 0; //this will be the checksum value
  1007. //key
  1008. LW := GStack.HostToNetwork(FKey);
  1009. CopyTIdUInt16(LW, Result, 2);
  1010. // sequence
  1011. LW := GStack.HostToNetwork(FSequence);
  1012. CopyTIdUInt16(LW, Result, 4);
  1013. // data length
  1014. LW := GStack.HostToNetwork(FDataLen);
  1015. CopyTIdUInt16(LW, Result, 6);
  1016. // position
  1017. LC := GStack.HostToNetwork(FFilePosition);
  1018. CopyTIdUInt32(LC, Result, 8);
  1019. //end of header section
  1020. //data section
  1021. if FDataLen > 0 then begin
  1022. CopyTIdBytes(FData, 0, Result, HSIZE, FDataLen);
  1023. end;
  1024. //extra data section
  1025. if LExtraDataLen > 0 then begin
  1026. CopyTIdBytes(FExtraData, 0, Result, HSIZE+FDataLen, LExtraDataLen);
  1027. end;
  1028. //checksum
  1029. LSum := Length(Result);
  1030. for i := Length(Result)-1 downto 0 do begin
  1031. Inc(LSum, Result[i]);
  1032. end;
  1033. Result[1] := Byte(LSum+(LSum shr 8));
  1034. end;
  1035. procedure TIdFSPPacket.ReadPacket(const AData : TIdBytes; const ALen : UInt32);
  1036. var
  1037. LSum, LnSum, LcSum : UInt32; //UInt32 to prevent a range-check error
  1038. LW : Word;
  1039. LExtraDataLen : UInt32;
  1040. begin
  1041. FValid := False;
  1042. if ALen < HSIZE then begin
  1043. Exit;
  1044. end;
  1045. //check data length
  1046. FDataLen := BytesToUInt16(AData, 6);
  1047. FDataLen := GStack.NetworkToHost(FDataLen);
  1048. if FDataLen > ALen then begin
  1049. Exit;
  1050. end;
  1051. //validate checksum
  1052. LSum := AData[1]; //checksum
  1053. LnSum := ALen;
  1054. for LW := ALen-1 downto 0 do begin
  1055. if LW <> 1 then begin // skip the checksum byte
  1056. Inc(LnSum, AData[LW]);
  1057. end;
  1058. end;
  1059. lcSum := Byte(LnSum + (LnSum shr 8));
  1060. if LcSum <> LSum then begin
  1061. Exit;
  1062. end;
  1063. //command
  1064. FCmd := AData[0];
  1065. //key
  1066. FKey := BytesToUInt16(AData, 2);
  1067. FKey := GStack.NetworkToHost(FKey);
  1068. // sequence
  1069. FSequence := BytesToUInt16(AData, 4);
  1070. FSequence := GStack.NetworkToHost(FSequence);
  1071. //file position
  1072. FFilePosition := BytesToUInt32(AData, 8);
  1073. FFilePosition := GStack.NetworkToHost(FFilePosition);
  1074. //extract data
  1075. if FDataLen > 0 then begin
  1076. SetLength(FData, FDataLen);
  1077. CopyTIdBytes(AData, HSIZE, FData, 0, FDataLen);
  1078. end else begin
  1079. SetLength(FData, 0);
  1080. end;
  1081. //extract extra data
  1082. LExtraDataLen := ALen - (HSIZE+FDataLen);
  1083. if LExtraDataLen > 0 then begin
  1084. SetLength(FExtraData, LExtraDataLen);
  1085. CopyTIdBytes(AData, HSIZE+FDataLen, FExtraData, 0, LExtraDataLen);
  1086. end else begin
  1087. SetLength(FExtraData, 0);
  1088. end;
  1089. FValid := True;
  1090. end;
  1091. { TIdFSPListItems }
  1092. function TIdFSPListItems.Add: TIdFSPListItem;
  1093. begin
  1094. Result := TIdFSPListItem(inherited Add);
  1095. end;
  1096. constructor TIdFSPListItems.Create;
  1097. begin
  1098. inherited Create(TIdFSPListItem);
  1099. end;
  1100. function TIdFSPListItems.GetItems(AIndex: Integer): TIdFSPListItem;
  1101. begin
  1102. Result := TIdFSPListItem(inherited Items[AIndex]);
  1103. end;
  1104. function TIdFSPListItems.IndexOf(AItem: TIdFSPListItem): Integer;
  1105. Var
  1106. i: Integer;
  1107. begin
  1108. for i := 0 to Count - 1 do begin
  1109. if AItem = Items[i] then begin
  1110. Result := i;
  1111. Exit;
  1112. end;
  1113. end;
  1114. Result := -1;
  1115. end;
  1116. function TIdFSPListItems.ParseEntries(const AData: TIdBytes; const ADataLen : UInt32) : Boolean;
  1117. var
  1118. i : UInt32;
  1119. LI : TIdFSPListItem;
  1120. LSkip : Boolean;
  1121. begin
  1122. Result := False;
  1123. i := 0;
  1124. repeat
  1125. if i >= (ADataLen-9) then begin
  1126. Exit;
  1127. end;
  1128. LI := nil;
  1129. LSkip := False;
  1130. case AData[i+8] of
  1131. RDTYPE_END:
  1132. begin
  1133. Result := True;
  1134. Exit;
  1135. end;
  1136. RDTYPE_FILE:
  1137. begin
  1138. LI := Add;
  1139. LI.ItemType := ditFile;
  1140. end;
  1141. RDTYPE_DIR:
  1142. begin
  1143. LI := Add;
  1144. LI.ItemType := ditDirectory;
  1145. end;
  1146. RDTYPE_SKIP:
  1147. begin
  1148. LSkip := True;
  1149. end
  1150. else begin
  1151. Exit;
  1152. end;
  1153. end;
  1154. if LSkip then begin
  1155. Inc(i, 8);
  1156. end else begin
  1157. ParseStatInfo(AData, LI, i);
  1158. LI.FileName := ParseASCIIZPos(AData, ADataLen, i);
  1159. end;
  1160. repeat
  1161. Inc(i);
  1162. until (i and $03) = 0;
  1163. until False;
  1164. end;
  1165. procedure TIdFSPListItems.SetItems(AIndex: Integer; const Value: TIdFSPListItem);
  1166. begin
  1167. inherited Items[AIndex] := Value;
  1168. end;
  1169. end.