IdFSP.pas 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313
  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. function MaxBufferSize : Word;
  289. function PrefPayloadSize : Word;
  290. procedure SetClientMaxPacketSize(const AValue: Word);
  291. public
  292. constructor Create(AOwner: TComponent); override;
  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. {$IF DEFINED(KYLIXCOMPAT)}
  339. Libc,
  340. {$ELSEIF DEFINED(USE_VCL_POSIX)}
  341. Posix.SysSelect,
  342. Posix.SysTime,
  343. Posix.Unistd,
  344. {$ELSEIF DEFINED(WINDOWS) AND (DEFINED(USE_INLINE) OR DEFINED(DCC_2009_OR_ABOVE))}
  345. Windows,
  346. {$IFEND}
  347. IdComponent, IdGlobalProtocols, IdResourceStringsProtocols, IdStack, SysUtils;
  348. function ParseASCIIZPos(const ABytes: TIdBytes ; const ALen : UInt32; var VPos : UInt32): String;
  349. var
  350. i : UInt32;
  351. begin
  352. if VPos < ALen then begin
  353. for i := VPos to ALen-1 do begin
  354. if ABytes[i] = 0 then begin
  355. Break;
  356. end;
  357. end;
  358. VPos := i;
  359. Result := BytesToString(ABytes, i);
  360. end else begin
  361. Result := '';
  362. end;
  363. end;
  364. function ParseASCIIZLen(const ABytes : TIdBytes; const ALen : UInt32) : String;
  365. var
  366. LPos : UInt32;
  367. begin
  368. LPos := 0;
  369. Result := ParseASCIIZPos(ABytes, ALen, LPos);
  370. end;
  371. function ParseASCIIZ(const ABytes : TIdBytes) : String;
  372. var
  373. LPos : UInt32;
  374. begin
  375. LPos := 0;
  376. Result := ParseASCIIZPos(ABytes, Length(ABytes), LPos);
  377. end;
  378. procedure ParseStatInfo(const AData : TIdBytes; VL : TIdFSPStatInfo; var VI : UInt32);
  379. var
  380. LC : UInt32;
  381. begin
  382. //we don't parse the file type because there is some variation between CC_GET_DIR and CC_STAT
  383. CopyBytesToHostUInt32(AData, VI, LC);
  384. VL.FModifiedDateGMT := UnixDateTimeToDelphiDateTime(LC);
  385. VL.FModifiedDate := UTCTimeToLocalTime(VL.FModifiedDateGMT);
  386. Inc(VI, 4);
  387. CopyBytesToHostUInt32(AData, VI, LC);
  388. VL.Size := LC;
  389. Inc(VI, 5); //we want to skip over the type byte we processed earlier
  390. end;
  391. { TIdFSP }
  392. constructor TIdFSP.Create(AOwner: TComponent);
  393. begin
  394. inherited Create(AOwner);
  395. FAbortFlag := TIdThreadSafeBoolean.Create;
  396. FAbortFlag.Value := False;
  397. //you have to use FPort or this will cause a stack overflow
  398. FPort := IdPORT_FSP;
  399. FSequence := 0;
  400. FKey := 0;
  401. FDirInfo := TIdFSPDirInfo.Create;
  402. FDirectoryListing := TIdFSPListItems.Create;
  403. FStatInfo := TIdFSPStatInfo.Create(nil);
  404. BroadcastEnabled := False;
  405. FConEstablished := False;
  406. FClientMaxPacketSize := DEF_MAXSIZE;
  407. FInCmd := TIdThreadSafeBoolean.Create;
  408. FInCmd.Value := False;
  409. end;
  410. destructor TIdFSP.Destroy;
  411. begin
  412. Disconnect;
  413. FDirInfo.Free;
  414. FDirectoryListing.Free;
  415. FStatInfo.Free;
  416. FAbortFlag.Free;
  417. FInCmd.Free;
  418. inherited Destroy;
  419. end;
  420. procedure TIdFSP.Connect;
  421. begin
  422. FSequence := 1;
  423. FKey := 0;
  424. FServerMaxThruPut := 0;
  425. FServerMaxPacketSize := DEF_MAXSIZE;
  426. inherited Connect;
  427. end;
  428. procedure TIdFSP.Disconnect;
  429. var
  430. LBuf, LData, LExtra : TIdBytes;
  431. begin
  432. AbortCmd;
  433. if FConEstablished then begin
  434. SetLength(LBuf, 0);
  435. SendCmd(CC_BYE, LBuf, 0, LData, LExtra);
  436. inherited Disconnect;
  437. end;
  438. FConEstablished := False;
  439. end;
  440. procedure TIdFSP.Get(const ASourceFile: string; ADest: TStream; AResume: Boolean);
  441. var
  442. LSendPacket : TIdFSPPacket;
  443. LRecvPacket : TIdFSPPacket;
  444. LLen : Integer;
  445. LTmpBuf : TIdBytes;
  446. begin
  447. SetLength(LTmpBuf, MaxBufferSize);
  448. LSendPacket := TIdFSPPacket.Create;
  449. try
  450. LRecvPacket := TIdFSPPacket.Create;
  451. try
  452. if AResume then begin
  453. LSendPacket.FFilePosition := ADest.Position;
  454. end else begin
  455. LSendPacket.FFilePosition := 0;
  456. end;
  457. LSendPacket.Cmd := CC_GET_FILE;
  458. LSendPacket.FData := ToBytes(ASourceFile+#0);
  459. LSendPacket.FDataLen := Length(LSendPacket.FData);
  460. //specify a preferred block size
  461. SetLength(LSendPacket.FExtraData, 2);
  462. CopyTIdNetworkUInt16(PrefPayloadSize, LSendPacket.FExtraData, 0);
  463. BeginWork(wmRead);
  464. try
  465. repeat
  466. SendCmd(LSendPacket, LRecvPacket, LTmpBuf);
  467. LLen := LRecvPacket.FDataLen; //Length(LRecvPacket.Data);
  468. if LLen > 0 then begin
  469. ADest.WriteBuffer(PByte(LRecvPacket.Data)^, LLen);
  470. DoWork(wmRead, LLen);
  471. Inc(LSendPacket.FFilePosition, LLen);
  472. end else begin
  473. Break;
  474. end;
  475. until False;
  476. finally
  477. EndWork(wmRead);
  478. end;
  479. finally
  480. LRecvPacket.Free;
  481. end;
  482. finally
  483. LSendPacket.Free;
  484. end;
  485. end;
  486. procedure TIdFSP.Get(const ASourceFile, ADestFile: string; const ACanOverwrite: Boolean; AResume: Boolean);
  487. var
  488. LDestStream: TStream;
  489. begin
  490. if ACanOverwrite and (not AResume) then begin
  491. SysUtils.DeleteFile(ADestFile);
  492. LDestStream := TIdFileCreateStream.Create(ADestFile);
  493. end
  494. else if (not ACanOverwrite) and AResume then begin
  495. LDestStream := TIdAppendFileStream.Create(ADestFile);
  496. end
  497. else begin
  498. raise EIdFSPFileAlreadyExists.Create(RSDestinationFileAlreadyExists);
  499. end;
  500. try
  501. Get(ASourceFile, LDestStream, AResume);
  502. finally
  503. LDestStream.Free;
  504. end;
  505. end;
  506. procedure TIdFSP.GetDirInfo(const ADIR: String);
  507. begin
  508. GetDirInfo(ADir, FDirInfo);
  509. end;
  510. procedure TIdFSP.List;
  511. begin
  512. List('/');
  513. end;
  514. procedure TIdFSP.List(const ASpecifier: string);
  515. var
  516. LSendPacket : TIdFSPPacket;
  517. LRecvPacket : TIdFSPPacket;
  518. LTmpBuf : TIdBytes;
  519. LSpecifier: String;
  520. begin
  521. LSpecifier := ASpecifier;
  522. if LSpecifier = '' then begin
  523. LSpecifier := '/';
  524. end;
  525. SetLength(LTmpBuf, MaxBufferSize);
  526. LSendPacket := TIdFSPPacket.Create;
  527. try
  528. LRecvPacket := TIdFSPPacket.Create;
  529. try
  530. LSendPacket.Cmd := CC_GET_DIR;
  531. LSendPacket.FFilePosition := 0;
  532. SetLength(LRecvPacket.FData, MaxBufferSize);
  533. SetLength(LSendPacket.FExtraData, 2);
  534. CopyTIdNetworkUInt16(PrefPayloadSize, LSendPacket.FExtraData, 0);
  535. FDirectoryListing.Clear;
  536. repeat
  537. LSendPacket.Data := ToBytes(LSpecifier+#0);
  538. LSendPacket.DataLen := Length(LSendPacket.Data);
  539. SendCmd(LSendPacket,LRecvPacket,LTmpBuf);
  540. if LRecvPacket.DataLen > 0 then begin
  541. Inc(LSendPacket.FFilePosition, LRecvPacket.DataLen);
  542. end else begin
  543. Break;
  544. end;
  545. if LRecvPacket.DataLen < PrefPayloadSize then begin
  546. Break;
  547. end;
  548. until FDirectoryListing.ParseEntries(LRecvPacket.FData, LRecvPacket.FDataLen);
  549. finally
  550. LRecvPacket.Free;
  551. end;
  552. finally
  553. LSendPacket.Free;
  554. end;
  555. end;
  556. procedure TIdFSP.SendCmd(const ACmd: Byte; const AData, AExtraData: TIdBytes;
  557. const AFilePosition: Int64; var VData, VExtraData: TIdBytes;
  558. const ARaiseException : Boolean = True);
  559. var
  560. LSendPacket : TIdFSPPacket;
  561. LRecvPacket : TIdFSPPacket;
  562. LTmpBuf : TIdBytes;
  563. begin
  564. SetLength(LTmpBuf, MaxBufferSize);
  565. LSendPacket := TIdFSPPacket.Create;
  566. try
  567. LRecvPacket := TIdFSPPacket.Create;
  568. try
  569. LSendPacket.Cmd := ACmd;
  570. LSendPacket.FilePosition := AFilePosition;
  571. LSendPacket.Data := AData;
  572. LSendPacket.FDataLen := Length(AData);
  573. LSendPacket.ExtraData := AExtraData;
  574. SendCmd(LSendPacket, LRecvPacket, LTmpBuf, ARaiseException);
  575. VData := LRecvPacket.Data;
  576. VExtraData := LRecvPacket.ExtraData;
  577. finally
  578. LRecvPacket.Free;
  579. end;
  580. finally
  581. LSendPacket.Free;
  582. end;
  583. end;
  584. procedure TIdFSP.SendCmd(const ACmd: Byte; const AData: TIdBytes;
  585. const AFilePosition: Int64; var VData, VExtraData: TIdBytes;
  586. const ARaiseException : Boolean = True);
  587. var
  588. LExtraData : TIdBytes;
  589. begin
  590. SetLength(LExtraData, 0);
  591. SendCmd(ACmd, AData, LExtraData, AFilePosition, VData, VExtraData, ARaiseException);
  592. end;
  593. procedure TIdFSP.Version;
  594. var
  595. LData, LBuf, LExtraBuf : TIdBytes;
  596. LDetails : Byte;
  597. begin
  598. {
  599. we use this instead of SendCmd because of the following note
  600. in the protocol specification
  601. FILE SERVICE PROTOCOL VERSION 2, OFFICIAL PROTOCOL DEFINITION, FSP v2,
  602. Document version 0.17, Last updated 25 Dec 2004
  603. (http://fsp.sourceforge.net/doc/PROTOCOL.txt):
  604. Note
  605. Some fsp servers do not responds to this command,
  606. because this command is used by FSP scanners and
  607. servers do not wishes to be detected.
  608. }
  609. SetLength(LData, 0);
  610. SendCmdOnce(CC_VERSION, LData, LData, 0, LBuf, LExtraBuf);
  611. if LData <> nil then begin
  612. FSystemDesc := ParseASCIIZ(LBuf);
  613. if LExtraBuf <> nil then begin
  614. LDetails := LExtraBuf[0];
  615. //bit 0 set - server does logging
  616. FSystemServerLogs := (LDetails and $01) = $01;
  617. //bit 1 set - server is read only
  618. FSystemReadOnly := (LDetails and $02) = $02;
  619. //bit 2 set - reverse lookup required
  620. FSystemReverseLookupRequired := (LDetails and $04) = $04;
  621. //bit 3 set - server is in private mode
  622. FSystemPrivateMode := (LDetails and $08) = $08;
  623. //if bit 4 is set thruput info follows
  624. FThruputControl := (LDetails and $10) = $10;
  625. //bit 5 set - server accept XTRA
  626. //DATA on input
  627. FSystemAcceptsExtraData := (LDetails and $20) = $20;
  628. //long - max_thruput allowed (in bytes/sec)
  629. //word - max. packet size supported by server
  630. if FThruputControl then begin
  631. if Length(LExtraBuf) > 4 then begin
  632. CopyBytesToHostUInt32(LExtraBuf, 1, FServerMaxThruPut);
  633. if Length(LExtraBuf) > 6 then begin
  634. CopyBytesToHostUInt16(LExtraBuf, 5, FServerMaxPacketSize);
  635. end;
  636. end;
  637. end else
  638. begin
  639. if Length(LExtraBuf) > 2 then begin
  640. CopyBytesToHostUInt16(LExtraBuf, 1, FServerMaxPacketSize);
  641. end;
  642. end;
  643. end;
  644. end;
  645. end;
  646. procedure TIdFSP.SendCmd(ACmdPacket, ARecvPacket: TIdFSPPacket;
  647. var VTempBuf : TIdBytes; const ARaiseException : Boolean = True);
  648. var
  649. LLen : Integer;
  650. LSendBuf : TIdBytes;
  651. LMSec : Integer;
  652. begin
  653. FInCmd.Value := True;
  654. try
  655. Inc(FSequence);
  656. FAbortFlag.Value := False;
  657. //we don't set the temp buff size here for speed.
  658. ACmdPacket.Key := FKey;
  659. ACmdPacket.Sequence := FSequence;
  660. LMSec := MINTIMEOUT;
  661. LSendBuf := ACmdPacket.WritePacket;
  662. //It's very important that you have some way of aborting this loop
  663. //if you do not and the server does not reply, this can go for infinity.
  664. //AbortCmd is ThreadSafe.
  665. while not FAbortFlag.Value do
  666. begin
  667. SendBuffer(LSendBuf);
  668. if Assigned(FOnSend) then begin
  669. FOnSend(Self, ACmdPacket);
  670. end;
  671. IndySleep(5); //this is so we don't eat up all of the CPU
  672. LLen := ReceiveBuffer(VTempBuf, LMsec);
  673. ARecvPacket.ReadPacket(VTempBuf, LLen);
  674. if ARecvPacket.Valid then begin
  675. if Assigned(FOnRecv) then begin
  676. FOnRecv(Self, ARecvPacket);
  677. end;
  678. if ARecvPacket.Sequence = FSequence then begin
  679. Break;
  680. end;
  681. end;
  682. LMSec := Round(LMSec * 1.5);
  683. if LMSec > MAXTIMEOUT then begin
  684. LMSec := MAXTIMEOUT;
  685. end;
  686. end;
  687. if not FAbortFlag.Value then begin
  688. FKey := ARecvPacket.Key;
  689. end;
  690. FAbortFlag.Value := False;
  691. if (ARecvPacket.Cmd = CC_ERR) and ARaiseException then begin
  692. raise EIdFSPProtException.Create(ParseASCIIZLen(ARecvPacket.Data, ARecvPacket.DataLen));
  693. end;
  694. finally
  695. FInCmd.Value := False;
  696. end;
  697. end;
  698. procedure TIdFSP.GetStatInfo(const APath: String);
  699. var
  700. LData, LBuf,LExtraBuf : TIdBytes;
  701. i : UInt32;
  702. begin
  703. {
  704. data format is the same as in directory listing with exception
  705. that there is no file name appended. If file do not exists or
  706. there is other problem (no access rights) return type of file is
  707. 0.
  708. struct STAT {
  709. long time;
  710. long size;
  711. byte type;
  712. }
  713. i := 0;
  714. LData := ToBytes(APath + #0);
  715. SendCmd(CC_STAT, LData, 0, LBuf, LExtraBuf);
  716. if Length(LBuf) > 8 then begin
  717. case LBuf[8] of
  718. 0 : //file not found
  719. begin
  720. raise EIdFSPFileNotFound.Create(RSFSPNotFound);
  721. end;
  722. RDTYPE_FILE :
  723. begin
  724. FStatInfo.ItemType := ditFile;
  725. end;
  726. RDTYPE_DIR :
  727. begin
  728. FStatInfo.ItemType := ditDirectory;
  729. end;
  730. end;
  731. ParseStatInfo(LBuf, FStatInfo, i);
  732. end;
  733. end;
  734. procedure TIdFSP.Put(const ASource: TStream; const ADestFile: string; const AGMTTime: TDateTime);
  735. var
  736. LUnixDate : UInt32;
  737. LSendPacket : TIdFSPPacket;
  738. LRecvPacket : TIdFSPPacket;
  739. LPosition : UInt32;
  740. LLen : Integer;
  741. LTmpBuf : TIdBytes;
  742. begin
  743. LPosition := 0;
  744. SetLength(LTmpBuf, MaxBufferSize);
  745. LSendPacket := TIdFSPPacket.Create;
  746. try
  747. LRecvPacket := TIdFSPPacket.Create;
  748. try
  749. SetLength(LSendPacket.FData, PrefPayloadSize);
  750. LSendPacket.Cmd := CC_UP_LOAD;
  751. repeat
  752. LLen := ASource.Read(PByte(LSendPacket.FData)^, PrefPayloadSize);
  753. if LLen <= 0 then begin
  754. Break;
  755. end;
  756. LSendPacket.FDataLen := LLen;
  757. LSendPacket.FilePosition := LPosition;
  758. SendCmd(LSendPacket, LRecvPacket, LTmpBuf);
  759. Inc(LPosition, LLen);
  760. until False;
  761. //send the Install packet
  762. LSendPacket.Cmd := CC_INSTALL;
  763. LSendPacket.FilePosition := 0;
  764. LSendPacket.Data := ToBytes(ADestFile+#0);
  765. LSendPacket.FDataLen := Length(LSendPacket.Data);
  766. //File date - optional
  767. if AGMTTime = 0 then begin
  768. SetLength(LSendPacket.FExtraData, 0);
  769. end else begin
  770. LUnixDate := DateTimeToUnix(AGMTTime);
  771. SetLength(LSendPacket.FExtraData, 4);
  772. CopyTIdNetworkUInt32(LUnixDate, LSendPacket.FExtraData, 0);
  773. end;
  774. SendCmd(LSendPacket, LRecvPacket, LTmpBuf);
  775. finally
  776. LRecvPacket.Free;
  777. end;
  778. finally
  779. LSendPacket.Free;
  780. end;
  781. end;
  782. procedure TIdFSP.Put(const ASourceFile, ADestFile: string);
  783. var
  784. LSourceStream: TStream;
  785. LDestFileName : String;
  786. begin
  787. LDestFileName := ADestFile;
  788. if LDestFileName = '' then begin
  789. LDestFileName := ExtractFileName(ASourceFile);
  790. end;
  791. LSourceStream := TIdReadFileExclusiveStream.Create(ASourceFile);
  792. try
  793. Put(LSourceStream, LDestFileName, GetGMTDateByName(ASourceFile));
  794. finally
  795. LSourceStream.Free;
  796. end;
  797. end;
  798. procedure TIdFSP.Delete(const AFilename: string);
  799. var
  800. LData : TIdBytes;
  801. LBuf, LExBuf : TIdBytes;
  802. begin
  803. LData := ToBytes(AFilename+#0);
  804. SendCmd(CC_DEL_FILE, LData, 0, LBuf, LExBuf);
  805. end;
  806. procedure TIdFSP.MakeDir(const ADirName: string);
  807. var
  808. LData : TIdBytes;
  809. LBuf, LExBuf : TIdBytes;
  810. begin
  811. LData := ToBytes(ADirName+#0);
  812. SendCmd(CC_MAKE_DIR, LData, 0, LBuf, LExBuf);
  813. ParseDirInfo(LBuf, LExBuf, FDirInfo);
  814. end;
  815. procedure TIdFSP.RemoveDir(const ADirName: string);
  816. var
  817. LData : TIdBytes;
  818. LBuf, LExBuf : TIdBytes;
  819. begin
  820. LData := ToBytes(ADirName+#0);
  821. SendCmd(CC_DEL_DIR, LData, 0, LBuf, LExBuf);
  822. end;
  823. procedure TIdFSP.Rename(const ASourceFile, ADestFile: string);
  824. var
  825. LBuf, LData, LDataExt : TIdBytes;
  826. begin
  827. SetLength(LData, 0);
  828. SetLength(LDataExt, 0);
  829. LBuf := ToBytes(ASourceFile+#0+ADestFile);
  830. SendCmd(CC_RENAME, LBuf, 0, LData, LDataExt);
  831. end;
  832. procedure TIdFSP.ParseDirInfo(const ABuf, AExtraBuf: TIdBytes; ADir : TIdFSPDirInfo);
  833. begin
  834. ADir.ReadMe := ParseASCIIZ(ABuf);
  835. if AExtraBuf <> nil then begin
  836. //0 - caller owns the directory
  837. ADir.OwnsDir := (AExtraBuf[0] and $01) = $01;
  838. //1 - files can be deleted from this dir
  839. ADir.CanDeleteFiles := (AExtraBuf[0] and $02) = $02;
  840. // 2 - files can be added to this dir
  841. ADir.CanAddFiles := (AExtraBuf[0] and $04) = $04;
  842. //3 - new subdirectories can be created
  843. ADir.CanMakeDir := (AExtraBuf[0] and $08) = $08;
  844. //4 - files are NOT readable by non-owners
  845. ADir.OnlyOwnerCanReadFiles := (AExtraBuf[0] and $10) = $10;
  846. //5 - directory contain an readme file
  847. ADir.HasReadMe := (AExtraBuf[0] and $20) = $20;
  848. //6 - directory can be listed
  849. ADir.CanBeListed := (AExtraBuf[0] and $40) = $40;
  850. //7 - files can be renamed in this directory
  851. ADir.CanRenameFiles := (AExtraBuf[0] and $80) = $80;
  852. end;
  853. end;
  854. procedure TIdFSP.GetDirInfo(const ADIR: String; ADirInfo: TIdFSPDirInfo);
  855. var
  856. LData, LBuf, LExtraBuf : TIdBytes;
  857. begin
  858. LData := ToBytes(ADIR+#0);
  859. SendCmd(CC_GET_PRO, LData, 0, LBuf, LExtraBuf);
  860. ParseDirInfo(LBuf, LExtraBuf, ADirInfo);
  861. end;
  862. procedure TIdFSP.SendCmdOnce(ACmdPacket, ARecvPacket: TIdFSPPacket;
  863. var VTempBuf: TIdBytes; const ARaiseException: Boolean);
  864. var
  865. LLen : Integer;
  866. LBuf : TIdBytes;
  867. LSendBuf : TIdBytes;
  868. //This is for where there may not be a reply to a command from a server.
  869. begin
  870. Inc(FSequence);
  871. SetLength(LBuf, MaxBufferSize);
  872. ACmdPacket.Key := FKey;
  873. ACmdPacket.Sequence := FSequence;
  874. LSendBuf := ACmdPacket.WritePacket;
  875. SendBuffer(LSendBuf);
  876. if Assigned(FOnSend) then begin
  877. FOnSend(Self, ACmdPacket);
  878. end;
  879. repeat
  880. LLen := ReceiveBuffer(LBuf, MINTIMEOUT);
  881. if LLen = 0 then begin
  882. Break;
  883. end;
  884. ARecvPacket.ReadPacket(LBuf, LLen);
  885. if ARecvPacket.Valid then begin
  886. if Assigned(FOnRecv) then begin
  887. FOnRecv(Self, ARecvPacket);
  888. end;
  889. if (ARecvPacket.Sequence = FSequence) then begin
  890. FKey := ARecvPacket.Key;
  891. Break;
  892. end;
  893. end;
  894. until False;
  895. if (ARecvPacket.Cmd = CC_ERR) and ARaiseException then begin
  896. raise EIdFSPProtException.Create(ParseASCIIZLen(ARecvPacket.Data, ARecvPacket.DataLen));
  897. end;
  898. end;
  899. procedure TIdFSP.SendCmdOnce(const ACmd: Byte; const AData,
  900. AExtraData: TIdBytes; const AFilePosition: Int64; var VData,
  901. VExtraData: TIdBytes; const ARaiseException: Boolean);
  902. var
  903. LSendPacket : TIdFSPPacket;
  904. LRecvPacket : TIdFSPPacket;
  905. LTmpBuf : TIdBytes;
  906. begin
  907. SetLength(LTmpBuf, MaxBufferSize);
  908. LSendPacket := TIdFSPPacket.Create;
  909. try
  910. LRecvPacket := TIdFSPPacket.Create;
  911. try
  912. LSendPacket.Cmd := ACmd;
  913. LSendPacket.FilePosition := AFilePosition;
  914. LSendPacket.Data := AData;
  915. LSendPacket.FDataLen := Length(AData);
  916. LSendPacket.ExtraData := AExtraData;
  917. SendCmdOnce(LSendPacket, LRecvPacket, LTmpBuf, ARaiseException);
  918. VData := LRecvPacket.Data;
  919. VExtraData := LRecvPacket.ExtraData;
  920. finally
  921. LRecvPacket.Free;
  922. end;
  923. finally
  924. LSendPacket.Free;
  925. end;
  926. end;
  927. function TIdFSP.MaxBufferSize: Word;
  928. //use only for calculating buffer for reading UDP packet
  929. begin
  930. Result := IndyMax(FClientMaxPacketSize, DEF_MAXSIZE);
  931. Result := IndyMax(FServerMaxPacketSize, Result);
  932. Inc(Result, HSIZE); //just in case
  933. end;
  934. function TIdFSP.PrefPayloadSize: Word;
  935. //maximum size of the data feild we want to use
  936. begin
  937. Result := IndyMin(FClientMaxPacketSize, FServerMaxPacketSize);
  938. Dec(Result, HSIZE);
  939. end;
  940. procedure TIdFSP.SetClientMaxPacketSize(const AValue: Word);
  941. begin
  942. //maximal size required by RFC
  943. //note that 512 gives a payload of 500 bytes in a packet
  944. if AValue < 512 then begin
  945. raise EIdFSPPacketTooSmall.Create(RSFSPPacketTooSmall);
  946. end;
  947. FClientMaxPacketSize := AValue;
  948. end;
  949. procedure TIdFSP.AbortCmd;
  950. begin
  951. //we don't want to go into the abort loop if there is no command
  952. //being send. If that happens, your program could hang.
  953. if FInCmd.Value then
  954. begin
  955. FAbortFlag.Value := True;
  956. repeat
  957. IndySleep(5);
  958. //we need to wait until the SendCmd routine catches the Abort
  959. //request so you don't get an AV in a worker thread.
  960. until not FAbortFlag.Value;
  961. end;
  962. end;
  963. { TIdFSPPacket }
  964. constructor TIdFSPPacket.Create;
  965. begin
  966. inherited Create;
  967. FCmd := 0;
  968. FFilePosition := 0;
  969. FDataLen := 0;
  970. SetLength(FData, 0);
  971. SetLength(FExtraData, 0);
  972. FSequence := 0;
  973. FKey := 0;
  974. end;
  975. function TIdFSPPacket.WritePacket : TIdBytes;
  976. var
  977. LExtraDataLen, LW : Word;
  978. LC, LSum : UInt32;
  979. i : Integer;
  980. //ported from:
  981. //http://cvs.sourceforge.net/viewcvs.py/fsp/javalib/FSPpacket.java?rev=1.6&view=markup
  982. begin
  983. LExtraDataLen := Length(FExtraData);
  984. SetLength(Result, HSIZE + FDataLen + LExtraDataLen);
  985. //cmd
  986. Result[0] := Cmd;
  987. //checksum
  988. Result[1] := 0; //this will be the checksum value
  989. //key
  990. LW := GStack.HostToNetwork(FKey);
  991. CopyTIdUInt16(LW, Result, 2);
  992. // sequence
  993. LW := GStack.HostToNetwork(FSequence);
  994. CopyTIdUInt16(LW, Result, 4);
  995. // data length
  996. LW := GStack.HostToNetwork(FDataLen);
  997. CopyTIdUInt16(LW, Result, 6);
  998. // position
  999. LC := GStack.HostToNetwork(FFilePosition);
  1000. CopyTIdUInt32(LC, Result, 8);
  1001. //end of header section
  1002. //data section
  1003. if FDataLen > 0 then begin
  1004. CopyTIdBytes(FData, 0, Result, HSIZE, FDataLen);
  1005. end;
  1006. //extra data section
  1007. if LExtraDataLen > 0 then begin
  1008. CopyTIdBytes(FExtraData, 0, Result, HSIZE+FDataLen, LExtraDataLen);
  1009. end;
  1010. //checksum
  1011. LSum := Length(Result);
  1012. for i := Length(Result)-1 downto 0 do begin
  1013. Inc(LSum, Result[i]);
  1014. end;
  1015. Result[1] := Byte(LSum+(LSum shr 8));
  1016. end;
  1017. procedure TIdFSPPacket.ReadPacket(const AData : TIdBytes; const ALen : UInt32);
  1018. var
  1019. LSum, LnSum, LcSum : UInt32; //UInt32 to prevent a range-check error
  1020. LW : Word;
  1021. LExtraDataLen : UInt32;
  1022. begin
  1023. FValid := False;
  1024. if ALen < HSIZE then begin
  1025. Exit;
  1026. end;
  1027. //check data length
  1028. FDataLen := BytesToUInt16(AData, 6);
  1029. FDataLen := GStack.NetworkToHost(FDataLen);
  1030. if FDataLen > ALen then begin
  1031. Exit;
  1032. end;
  1033. //validate checksum
  1034. LSum := AData[1]; //checksum
  1035. LnSum := ALen;
  1036. for LW := ALen-1 downto 0 do begin
  1037. if LW <> 1 then begin // skip the checksum byte
  1038. Inc(LnSum, AData[LW]);
  1039. end;
  1040. end;
  1041. lcSum := Byte(LnSum + (LnSum shr 8));
  1042. if LcSum <> LSum then begin
  1043. Exit;
  1044. end;
  1045. //command
  1046. FCmd := AData[0];
  1047. //key
  1048. FKey := BytesToUInt16(AData, 2);
  1049. FKey := GStack.NetworkToHost(FKey);
  1050. // sequence
  1051. FSequence := BytesToUInt16(AData, 4);
  1052. FSequence := GStack.NetworkToHost(FSequence);
  1053. //file position
  1054. FFilePosition := BytesToUInt32(AData, 8);
  1055. FFilePosition := GStack.NetworkToHost(FFilePosition);
  1056. //extract data
  1057. if FDataLen > 0 then begin
  1058. SetLength(FData, FDataLen);
  1059. CopyTIdBytes(AData, HSIZE, FData, 0, FDataLen);
  1060. end else begin
  1061. SetLength(FData, 0);
  1062. end;
  1063. //extract extra data
  1064. LExtraDataLen := ALen - (HSIZE+FDataLen);
  1065. if LExtraDataLen > 0 then begin
  1066. SetLength(FExtraData, LExtraDataLen);
  1067. CopyTIdBytes(AData, HSIZE+FDataLen, FExtraData, 0, LExtraDataLen);
  1068. end else begin
  1069. SetLength(FExtraData, 0);
  1070. end;
  1071. FValid := True;
  1072. end;
  1073. { TIdFSPListItems }
  1074. function TIdFSPListItems.Add: TIdFSPListItem;
  1075. begin
  1076. Result := TIdFSPListItem(inherited Add);
  1077. end;
  1078. constructor TIdFSPListItems.Create;
  1079. begin
  1080. inherited Create(TIdFSPListItem);
  1081. end;
  1082. function TIdFSPListItems.GetItems(AIndex: Integer): TIdFSPListItem;
  1083. begin
  1084. Result := TIdFSPListItem(inherited Items[AIndex]);
  1085. end;
  1086. function TIdFSPListItems.IndexOf(AItem: TIdFSPListItem): Integer;
  1087. Var
  1088. i: Integer;
  1089. begin
  1090. for i := 0 to Count - 1 do begin
  1091. if AItem = Items[i] then begin
  1092. Result := i;
  1093. Exit;
  1094. end;
  1095. end;
  1096. Result := -1;
  1097. end;
  1098. function TIdFSPListItems.ParseEntries(const AData: TIdBytes; const ADataLen : UInt32) : Boolean;
  1099. var
  1100. i : UInt32;
  1101. LI : TIdFSPListItem;
  1102. LSkip : Boolean;
  1103. begin
  1104. Result := False;
  1105. i := 0;
  1106. repeat
  1107. if i >= (ADataLen-9) then begin
  1108. Exit;
  1109. end;
  1110. LI := nil;
  1111. LSkip := False;
  1112. case AData[i+8] of
  1113. RDTYPE_END:
  1114. begin
  1115. Result := True;
  1116. Exit;
  1117. end;
  1118. RDTYPE_FILE:
  1119. begin
  1120. LI := Add;
  1121. LI.ItemType := ditFile;
  1122. end;
  1123. RDTYPE_DIR:
  1124. begin
  1125. LI := Add;
  1126. LI.ItemType := ditDirectory;
  1127. end;
  1128. RDTYPE_SKIP:
  1129. begin
  1130. LSkip := True;
  1131. end
  1132. else begin
  1133. Exit;
  1134. end;
  1135. end;
  1136. if LSkip then begin
  1137. Inc(i, 8);
  1138. end else begin
  1139. ParseStatInfo(AData, LI, i);
  1140. LI.FileName := ParseASCIIZPos(AData, ADataLen, i);
  1141. end;
  1142. repeat
  1143. Inc(i);
  1144. until (i and $03) = 0;
  1145. until False;
  1146. end;
  1147. procedure TIdFSPListItems.SetItems(AIndex: Integer; const Value: TIdFSPListItem);
  1148. begin
  1149. inherited Items[AIndex] := Value;
  1150. end;
  1151. end.