ftpsend.pas 60 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101
  1. {==============================================================================|
  2. | Project : Ararat Synapse | 004.001.000 |
  3. |==============================================================================|
  4. | Content: FTP client |
  5. |==============================================================================|
  6. | Copyright (c)1999-2011, Lukas Gebauer |
  7. | All rights reserved. |
  8. | |
  9. | Redistribution and use in source and binary forms, with or without |
  10. | modification, are permitted provided that the following conditions are met: |
  11. | |
  12. | Redistributions of source code must retain the above copyright notice, this |
  13. | list of conditions and the following disclaimer. |
  14. | |
  15. | Redistributions in binary form must reproduce the above copyright notice, |
  16. | this list of conditions and the following disclaimer in the documentation |
  17. | and/or other materials provided with the distribution. |
  18. | |
  19. | Neither the name of Lukas Gebauer nor the names of its contributors may |
  20. | be used to endorse or promote products derived from this software without |
  21. | specific prior written permission. |
  22. | |
  23. | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
  24. | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
  25. | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
  26. | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR |
  27. | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
  28. | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
  29. | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
  30. | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
  31. | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
  32. | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
  33. | DAMAGE. |
  34. |==============================================================================|
  35. | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).|
  36. | Portions created by Lukas Gebauer are Copyright (c) 1999-2010. |
  37. | Portions created by Jan Fiala are Copyright (c) 2019. |
  38. | All Rights Reserved. |
  39. |==============================================================================|
  40. | Contributor(s): |
  41. | Petr Esner <[email protected]> |
  42. | Jan Fiala |
  43. |==============================================================================|
  44. | History: see HISTORY.HTM from distribution package |
  45. | (Found at URL: http://www.ararat.cz/synapse/) |
  46. |==============================================================================}
  47. {: @abstract(FTP client protocol)
  48. Used RFC: RFC-959, RFC-2228, RFC-2428
  49. }
  50. {$IFDEF FPC}
  51. {$MODE DELPHI}
  52. {$ENDIF}
  53. {$H+}
  54. {$TYPEINFO ON}// Borland changed defualt Visibility from Public to Published
  55. // and it requires RTTI to be generated $M+
  56. {$M+}
  57. {$IFDEF UNICODE}
  58. {$WARN IMPLICIT_STRING_CAST OFF}
  59. {$WARN IMPLICIT_STRING_CAST_LOSS OFF}
  60. {$ENDIF}
  61. unit ftpsend;
  62. interface
  63. uses
  64. SysUtils, Classes,
  65. {$IfDef POSIX}
  66. ,System.Generics.Collections, System.Generics.Defaults
  67. {$EndIf}
  68. blcksock, synautil, synaip, synsock;
  69. const
  70. cFtpProtocol = '21';
  71. cFtpDataProtocol = '20';
  72. {:Terminating value for TLogonActions}
  73. FTP_OK = 255;
  74. {:Terminating value for TLogonActions}
  75. FTP_ERR = 254;
  76. type
  77. {:Array for holding definition of logon sequence.}
  78. TLogonActions = array [0..17] of byte;
  79. {:Procedural type for OnStatus event. Sender is calling @link(TFTPSend) object.
  80. Value is FTP command or reply to this comand. (if it is reply, Response
  81. is @True).}
  82. TFTPStatus = procedure(Sender: TObject; Response: Boolean;
  83. const Value: string) of object;
  84. {: @abstract(Object for holding file information) parsed from directory
  85. listing of FTP server.}
  86. TFTPListRec = class(TObject)
  87. private
  88. FFileName: String;
  89. FDirectory: Boolean;
  90. FReadable: Boolean;
  91. FFileSize: int64;
  92. FFileTime: TDateTime;
  93. FOriginalLine: string;
  94. FMask: string;
  95. FPermission: String;
  96. public
  97. {: You can assign another TFTPListRec to this object.}
  98. procedure Assign(Value: TFTPListRec); virtual;
  99. {:name of file}
  100. property FileName: string read FFileName write FFileName;
  101. {:if name is subdirectory not file.}
  102. property Directory: Boolean read FDirectory write FDirectory;
  103. {:if you have rights to read}
  104. property Readable: Boolean read FReadable write FReadable;
  105. {:size of file in bytes}
  106. property FileSize: int64 read FFileSize write FFileSize;
  107. {:date and time of file. Local server timezone is used. Any timezone
  108. conversions was not done!}
  109. property FileTime: TDateTime read FFileTime write FFileTime;
  110. {:original unparsed line}
  111. property OriginalLine: string read FOriginalLine write FOriginalLine;
  112. {:mask what was used for parsing}
  113. property Mask: string read FMask write FMask;
  114. {:permission string (depending on used mask!)}
  115. property Permission: string read FPermission write FPermission;
  116. end;
  117. {$IFDEF POSIX}
  118. TFTPRecList = TList<TFTPListRec>;
  119. {$ELSE}
  120. TFTPRecList = TList;
  121. {$ENDIF}
  122. {:@abstract(This is TList of TFTPListRec objects.)
  123. This object is used for holding lististing of all files information in listed
  124. directory on FTP server.}
  125. TFTPList = class(TObject)
  126. protected
  127. FList: TFTPRecList;
  128. FLines: TStringList;
  129. FMasks: TStringList;
  130. FUnparsedLines: TStringList;
  131. Monthnames: string;
  132. BlockSize: string;
  133. DirFlagValue: string;
  134. FileName: string;
  135. VMSFileName: string;
  136. Day: string;
  137. Month: string;
  138. ThreeMonth: string;
  139. YearTime: string;
  140. Year: string;
  141. Hours: string;
  142. HoursModif: string;
  143. Minutes: string;
  144. Seconds: string;
  145. Size: string;
  146. Permissions: string;
  147. DirFlag: string;
  148. function GetListItem(Index: integer): TFTPListRec; virtual;
  149. function ParseEPLF(Value: string): Boolean; virtual;
  150. procedure ClearStore; virtual;
  151. function ParseByMask(Value, NextValue, Mask: string): Integer; virtual;
  152. function CheckValues: Boolean; virtual;
  153. procedure FillRecord(const Value: TFTPListRec); virtual;
  154. public
  155. {:Constructor. You not need create this object, it is created by TFTPSend
  156. class as their property.}
  157. constructor Create;
  158. destructor Destroy; override;
  159. {:Clear list.}
  160. procedure Clear; virtual;
  161. {:count of holded @link(TFTPListRec) objects}
  162. function Count: integer; virtual;
  163. {:Assigns one list to another}
  164. procedure Assign(Value: TFTPList); virtual;
  165. {:try to parse raw directory listing in @link(lines) to list of
  166. @link(TFTPListRec).}
  167. procedure ParseLines; virtual;
  168. {:try to parse MLSD directory listing in @link(lines) to list of
  169. @link(TFTPListRec).}
  170. procedure ParseMLSDLines; virtual;
  171. {:By this property you have access to list of @link(TFTPListRec).
  172. This is for compatibility only. Please, use @link(Items) instead.}
  173. property List: TFTPRecList read FList;
  174. {:By this property you have access to list of @link(TFTPListRec).}
  175. property Items[Index: Integer]: TFTPListRec read GetListItem; default;
  176. {:Set of lines with RAW directory listing for @link(parseLines)}
  177. property Lines: TStringList read FLines;
  178. {:Set of masks for directory listing parser. It is predefined by default,
  179. however you can modify it as you need. (for example, you can add your own
  180. definition mask.) Mask is same as mask used in TotalCommander.}
  181. property Masks: TStringList read FMasks;
  182. {:After @link(ParseLines) it holding lines what was not sucessfully parsed.}
  183. property UnparsedLines: TStringList read FUnparsedLines;
  184. end;
  185. {:@abstract(Implementation of FTP protocol.)
  186. Note: Are you missing properties for setting Username and Password? Look to
  187. parent @link(TSynaClient) object! (Username and Password have default values
  188. for "anonymous" FTP login)
  189. Are you missing properties for specify server address and port? Look to
  190. parent @link(TSynaClient) too!}
  191. TFTPSend = class(TSynaClient)
  192. protected
  193. FOnStatus: TFTPStatus;
  194. FSock: TTCPBlockSocket;
  195. FDSock: TTCPBlockSocket;
  196. FResultCode: Integer;
  197. FResultString: string;
  198. FFullResult: TStringList;
  199. FAccount: string;
  200. FFWHost: string;
  201. FFWPort: string;
  202. FFWUsername: string;
  203. FFWPassword: string;
  204. FFWMode: integer;
  205. FDataStream: TMemoryStream;
  206. FDataIP: string;
  207. FDataPort: string;
  208. FDirectFile: Boolean;
  209. FDirectFileName: string;
  210. FCanResume: Boolean;
  211. FPassiveMode: Boolean;
  212. FForceDefaultPort: Boolean;
  213. FForceOldPort: Boolean;
  214. FFtpList: TFTPList;
  215. FBinaryMode: Boolean;
  216. FAutoTLS: Boolean;
  217. FIsTLS: Boolean;
  218. FIsDataTLS: Boolean;
  219. FTLSonData: Boolean;
  220. FFullSSL: Boolean;
  221. FUseMLSDList: Boolean;
  222. function Auth(Mode: integer): Boolean; virtual;
  223. function Connect: Boolean; virtual;
  224. function InternalStor(const Command: string; RestoreAt: int64): Boolean; virtual;
  225. function DataSocket: Boolean; virtual;
  226. function AcceptDataSocket: Boolean; virtual;
  227. procedure DoStatus(Response: Boolean; const Value: string); virtual;
  228. public
  229. {:Custom definition of login sequence. You can use this when you set
  230. @link(FWMode) to value -1.}
  231. CustomLogon: TLogonActions;
  232. constructor Create;
  233. destructor Destroy; override;
  234. {:Waits and read FTP server response. You need this only in special cases!}
  235. function ReadResult: Integer; virtual;
  236. {:Parse remote side information of data channel from value string (returned
  237. by PASV command). This function you need only in special cases!}
  238. procedure ParseRemote(Value: string); virtual;
  239. {:Parse remote side information of data channel from value string (returned
  240. by EPSV command). This function you need only in special cases!}
  241. procedure ParseRemoteEPSV(Value: string); virtual;
  242. {:Send Value as FTP command to FTP server. Returned result code is result of
  243. this function.
  244. This command is good for sending site specific command, or non-standard
  245. commands.}
  246. function FTPCommand(const Value: string): integer; virtual;
  247. {:Connect and logon to FTP server. If you specify any FireWall, connect to
  248. firewall and throw them connect to FTP server. Login sequence depending on
  249. @link(FWMode).}
  250. function Login: Boolean; virtual;
  251. {:Logoff and disconnect from FTP server.}
  252. function Logout: Boolean; virtual;
  253. {:Break current transmission of data. (You can call this method from
  254. Sock.OnStatus event, or from another thread.)}
  255. procedure Abort; virtual;
  256. {:Break current transmission of data. It is same as Abort, but it send abort
  257. telnet commands prior ABOR FTP command. Some servers need it. (You can call
  258. this method from Sock.OnStatus event, or from another thread.)}
  259. procedure TelnetAbort; virtual;
  260. {:Download directory listing of Directory on FTP server. If Directory is
  261. empty string, download listing of current working directory.
  262. If NameList is @true, download only names of files in directory.
  263. (internally use NLST command instead LIST command)
  264. If NameList is @false, returned list is also parsed to @link(FTPList)
  265. property.}
  266. function List(Directory: string; NameList: Boolean): Boolean; virtual;
  267. {:Read data from FileName on FTP server. If Restore is @true and server
  268. supports resume dowloads, download is resumed. (received is only rest
  269. of file)}
  270. function RetrieveFile(const FileName: string; Restore: Boolean): Boolean; virtual;
  271. {:Send data to FileName on FTP server. If Restore is @true and server
  272. supports resume upload, upload is resumed. (send only rest of file)
  273. In this case if remote file is same length as local file, nothing will be
  274. done. If remote file is larger then local, resume is disabled and file is
  275. transfered from begin!}
  276. function StoreFile(const FileName: string; Restore: Boolean): Boolean; virtual;
  277. {:Send data to FTP server and assing unique name for this file.}
  278. function StoreUniqueFile: Boolean; virtual;
  279. {:Append data to FileName on FTP server.}
  280. function AppendFile(const FileName: string): Boolean; virtual;
  281. {:Rename on FTP server file with OldName to NewName.}
  282. function RenameFile(const OldName, NewName: string): Boolean; virtual;
  283. {:Delete file FileName on FTP server.}
  284. function DeleteFile(const FileName: string): Boolean; virtual;
  285. {:Return size of Filename file on FTP server. If command failed (i.e. not
  286. implemented), return -1.}
  287. function FileSize(const FileName: string): int64; virtual;
  288. {:Send NOOP command to FTP server for preserve of disconnect by inactivity
  289. timeout.}
  290. function NoOp: Boolean; virtual;
  291. {:Change currect working directory to Directory on FTP server.}
  292. function ChangeWorkingDir(const Directory: string): Boolean; virtual;
  293. {:walk to upper directory on FTP server.}
  294. function ChangeToParentDir: Boolean; virtual;
  295. {:walk to root directory on FTP server. (May not work with all servers properly!)}
  296. function ChangeToRootDir: Boolean; virtual;
  297. {:Delete Directory on FTP server.}
  298. function DeleteDir(const Directory: string): Boolean; virtual;
  299. {:Create Directory on FTP server.}
  300. function CreateDir(const Directory: string): Boolean; virtual;
  301. {:Return current working directory on FTP server.}
  302. function GetCurrentDir: String; virtual;
  303. {:Establish data channel to FTP server and retrieve data.
  304. This function you need only in special cases, i.e. when you need to implement
  305. some special unsupported FTP command!}
  306. function DataRead(const DestStream: TStream): Boolean; virtual;
  307. {:Establish data channel to FTP server and send data.
  308. This function you need only in special cases, i.e. when you need to implement
  309. some special unsupported FTP command.}
  310. function DataWrite(const SourceStream: TStream): Boolean; virtual;
  311. published
  312. {:After FTP command contains result number of this operation.}
  313. property ResultCode: Integer read FResultCode;
  314. {:After FTP command contains main line of result.}
  315. property ResultString: string read FResultString;
  316. {:After any FTP command it contains all lines of FTP server reply.}
  317. property FullResult: TStringList read FFullResult;
  318. {:Account information used in some cases inside login sequence.}
  319. property Account: string read FAccount Write FAccount;
  320. {:Address of firewall. If empty string (default), firewall not used.}
  321. property FWHost: string read FFWHost Write FFWHost;
  322. {:port of firewall. standard value is same port as ftp server used. (21)}
  323. property FWPort: string read FFWPort Write FFWPort;
  324. {:Username for login to firewall. (if needed)}
  325. property FWUsername: string read FFWUsername Write FFWUsername;
  326. {:password for login to firewall. (if needed)}
  327. property FWPassword: string read FFWPassword Write FFWPassword;
  328. {:Type of Firewall. Used only if you set some firewall address. Supported
  329. predefined firewall login sequences are described by comments in source
  330. file where you can see pseudocode decribing each sequence.}
  331. property FWMode: integer read FFWMode Write FFWMode;
  332. {:Socket object used for TCP/IP operation on control channel. Good for
  333. seting OnStatus hook, etc.}
  334. property Sock: TTCPBlockSocket read FSock;
  335. {:Socket object used for TCP/IP operation on data channel. Good for seting
  336. OnStatus hook, etc.}
  337. property DSock: TTCPBlockSocket read FDSock;
  338. {:If you not use @link(DirectFile) mode, all data transfers is made to or
  339. from this stream.}
  340. property DataStream: TMemoryStream read FDataStream;
  341. {:After data connection is established, contains remote side IP of this
  342. connection.}
  343. property DataIP: string read FDataIP;
  344. {:After data connection is established, contains remote side port of this
  345. connection.}
  346. property DataPort: string read FDataPort;
  347. {:Mode of data handling by data connection. If @False, all data operations
  348. are made to or from @link(DataStream) TMemoryStream.
  349. If @true, data operations is made directly to file in your disk. (filename
  350. is specified by @link(DirectFileName) property.) Dafault is @False!}
  351. property DirectFile: Boolean read FDirectFile Write FDirectFile;
  352. {:Filename for direct disk data operations.}
  353. property DirectFileName: string read FDirectFileName Write FDirectFileName;
  354. {:Indicate after @link(Login) if remote server support resume downloads and
  355. uploads.}
  356. property CanResume: Boolean read FCanResume;
  357. {:If true (default value), all transfers is made by passive method.
  358. It is safer method for various firewalls.}
  359. property PassiveMode: Boolean read FPassiveMode Write FPassiveMode;
  360. {:Force to listen for dataconnection on standard port (20). Default is @false,
  361. dataconnections will be made to any non-standard port reported by PORT FTP
  362. command. This setting is not used, if you use passive mode.}
  363. property ForceDefaultPort: Boolean read FForceDefaultPort Write FForceDefaultPort;
  364. {:When is @true, then is disabled EPSV and EPRT support. However without this
  365. commands you cannot use IPv6! (Disabling of this commands is needed only
  366. when you are behind some crap firewall/NAT.}
  367. property ForceOldPort: Boolean read FForceOldPort Write FForceOldPort;
  368. {:You may set this hook for monitoring FTP commands and replies.}
  369. property OnStatus: TFTPStatus read FOnStatus write FOnStatus;
  370. {:After LIST command is here parsed list of files in given directory.}
  371. property FtpList: TFTPList read FFtpList;
  372. {:if @true (default), then data transfers is in binary mode. If this is set
  373. to @false, then ASCII mode is used.}
  374. property BinaryMode: Boolean read FBinaryMode Write FBinaryMode;
  375. {:if is true, then if server support upgrade to SSL/TLS mode, then use them.}
  376. property AutoTLS: Boolean read FAutoTLS Write FAutoTLS;
  377. {:if server listen on SSL/TLS port, then you set this to true.}
  378. property FullSSL: Boolean read FFullSSL Write FFullSSL;
  379. {:Signalise, if control channel is in SSL/TLS mode.}
  380. property IsTLS: Boolean read FIsTLS;
  381. {:Signalise, if data transfers is in SSL/TLS mode.}
  382. property IsDataTLS: Boolean read FIsDataTLS;
  383. {:If @true (default), then try to use SSL/TLS on data transfers too.
  384. If @false, then SSL/TLS is used only for control connection.}
  385. property TLSonData: Boolean read FTLSonData write FTLSonData;
  386. {:Enable MLSD support for directory list.}
  387. property UseMLSDList: Boolean read FUseMLSDList write FUseMLSDList;
  388. end;
  389. {:A very useful function, and example of use can be found in the TFtpSend object.
  390. Dowload specified file from FTP server to LocalFile.}
  391. function FtpGetFile(const IP, Port, FileName, LocalFile,
  392. User, Pass: string): Boolean;
  393. {:A very useful function, and example of use can be found in the TFtpSend object.
  394. Upload specified LocalFile to FTP server.}
  395. function FtpPutFile(const IP, Port, FileName, LocalFile,
  396. User, Pass: string): Boolean;
  397. {:A very useful function, and example of use can be found in the TFtpSend object.
  398. Initiate transfer of file between two FTP servers.}
  399. function FtpInterServerTransfer(
  400. const FromIP, FromPort, FromFile, FromUser, FromPass: string;
  401. const ToIP, ToPort, ToFile, ToUser, ToPass: string): Boolean;
  402. implementation
  403. constructor TFTPSend.Create;
  404. begin
  405. inherited Create;
  406. FFullResult := TStringList.Create;
  407. FDataStream := TMemoryStream.Create;
  408. FSock := TTCPBlockSocket.Create;
  409. FSock.Owner := self;
  410. FSock.ConvertLineEnd := True;
  411. FDSock := TTCPBlockSocket.Create;
  412. FDSock.Owner := self;
  413. FFtpList := TFTPList.Create;
  414. FTimeout := 300000;
  415. FTargetPort := cFtpProtocol;
  416. FUsername := 'anonymous';
  417. FPassword := 'anonymous@' + FSock.LocalName;
  418. FDirectFile := False;
  419. FPassiveMode := True;
  420. FForceDefaultPort := False;
  421. FForceOldPort := false;
  422. FAccount := '';
  423. FFWHost := '';
  424. FFWPort := cFtpProtocol;
  425. FFWUsername := '';
  426. FFWPassword := '';
  427. FFWMode := 0;
  428. FBinaryMode := True;
  429. FAutoTLS := False;
  430. FFullSSL := False;
  431. FIsTLS := False;
  432. FIsDataTLS := False;
  433. FTLSonData := True;
  434. UseMLSDList := false;
  435. end;
  436. destructor TFTPSend.Destroy;
  437. begin
  438. FDSock.Free;
  439. FSock.Free;
  440. FFTPList.Free;
  441. FDataStream.Free;
  442. FFullResult.Free;
  443. inherited Destroy;
  444. end;
  445. procedure TFTPSend.DoStatus(Response: Boolean; const Value: string);
  446. begin
  447. if assigned(OnStatus) then
  448. OnStatus(Self, Response, Value);
  449. end;
  450. function TFTPSend.ReadResult: Integer;
  451. var
  452. s, c: string;
  453. begin
  454. FFullResult.Clear;
  455. c := '';
  456. repeat
  457. s := FSock.RecvString(FTimeout);
  458. if c = '' then
  459. if length(s) > 3 then
  460. if s[4] in [' ', '-'] then
  461. c :=Copy(s, 1, 3);
  462. FResultString := s;
  463. FFullResult.Add(s);
  464. DoStatus(True, s);
  465. if FSock.LastError <> 0 then
  466. Break;
  467. until (c <> '') and (Pos(c + ' ', s) = 1);
  468. Result := StrToIntDef(c, 0);
  469. FResultCode := Result;
  470. end;
  471. function TFTPSend.FTPCommand(const Value: string): integer;
  472. begin
  473. FSock.Purge;
  474. FSock.SendString(Value + CRLF);
  475. DoStatus(False, Value);
  476. Result := ReadResult;
  477. end;
  478. // based on idea by Petr Esner <[email protected]>
  479. function TFTPSend.Auth(Mode: integer): Boolean;
  480. const
  481. //if not USER <username> then
  482. // if not PASS <password> then
  483. // if not ACCT <account> then ERROR!
  484. //OK!
  485. Action0: TLogonActions =
  486. (0, FTP_OK, 3,
  487. 1, FTP_OK, 6,
  488. 2, FTP_OK, FTP_ERR,
  489. 0, 0, 0, 0, 0, 0, 0, 0, 0);
  490. //if not USER <FWusername> then
  491. // if not PASS <FWPassword> then ERROR!
  492. //if SITE <FTPServer> then ERROR!
  493. //if not USER <username> then
  494. // if not PASS <password> then
  495. // if not ACCT <account> then ERROR!
  496. //OK!
  497. Action1: TLogonActions =
  498. (3, 6, 3,
  499. 4, 6, FTP_ERR,
  500. 5, FTP_ERR, 9,
  501. 0, FTP_OK, 12,
  502. 1, FTP_OK, 15,
  503. 2, FTP_OK, FTP_ERR);
  504. //if not USER <FWusername> then
  505. // if not PASS <FWPassword> then ERROR!
  506. //if USER <UserName>'@'<FTPServer> then OK!
  507. //if not PASS <password> then
  508. // if not ACCT <account> then ERROR!
  509. //OK!
  510. Action2: TLogonActions =
  511. (3, 6, 3,
  512. 4, 6, FTP_ERR,
  513. 6, FTP_OK, 9,
  514. 1, FTP_OK, 12,
  515. 2, FTP_OK, FTP_ERR,
  516. 0, 0, 0);
  517. //if not USER <FWusername> then
  518. // if not PASS <FWPassword> then ERROR!
  519. //if not USER <username> then
  520. // if not PASS <password> then
  521. // if not ACCT <account> then ERROR!
  522. //OK!
  523. Action3: TLogonActions =
  524. (3, 6, 3,
  525. 4, 6, FTP_ERR,
  526. 0, FTP_OK, 9,
  527. 1, FTP_OK, 12,
  528. 2, FTP_OK, FTP_ERR,
  529. 0, 0, 0);
  530. //OPEN <FTPserver>
  531. //if not USER <username> then
  532. // if not PASS <password> then
  533. // if not ACCT <account> then ERROR!
  534. //OK!
  535. Action4: TLogonActions =
  536. (7, 3, 3,
  537. 0, FTP_OK, 6,
  538. 1, FTP_OK, 9,
  539. 2, FTP_OK, FTP_ERR,
  540. 0, 0, 0, 0, 0, 0);
  541. //if USER <UserName>'@'<FTPServer> then OK!
  542. //if not PASS <password> then
  543. // if not ACCT <account> then ERROR!
  544. //OK!
  545. Action5: TLogonActions =
  546. (6, FTP_OK, 3,
  547. 1, FTP_OK, 6,
  548. 2, FTP_OK, FTP_ERR,
  549. 0, 0, 0, 0, 0, 0, 0, 0, 0);
  550. //if not USER <FWUserName>@<FTPServer> then
  551. // if not PASS <FWPassword> then ERROR!
  552. //if not USER <username> then
  553. // if not PASS <password> then
  554. // if not ACCT <account> then ERROR!
  555. //OK!
  556. Action6: TLogonActions =
  557. (8, 6, 3,
  558. 4, 6, FTP_ERR,
  559. 0, FTP_OK, 9,
  560. 1, FTP_OK, 12,
  561. 2, FTP_OK, FTP_ERR,
  562. 0, 0, 0);
  563. //if USER <UserName>@<FTPServer> <FWUserName> then ERROR!
  564. //if not PASS <password> then
  565. // if not ACCT <account> then ERROR!
  566. //OK!
  567. Action7: TLogonActions =
  568. (9, FTP_ERR, 3,
  569. 1, FTP_OK, 6,
  570. 2, FTP_OK, FTP_ERR,
  571. 0, 0, 0, 0, 0, 0, 0, 0, 0);
  572. //if not USER <UserName>@<FWUserName>@<FTPServer> then
  573. // if not PASS <Password>@<FWPassword> then
  574. // if not ACCT <account> then ERROR!
  575. //OK!
  576. Action8: TLogonActions =
  577. (10, FTP_OK, 3,
  578. 11, FTP_OK, 6,
  579. 2, FTP_OK, FTP_ERR,
  580. 0, 0, 0, 0, 0, 0, 0, 0, 0);
  581. var
  582. FTPServer: string;
  583. LogonActions: TLogonActions;
  584. i: integer;
  585. s: string;
  586. x: integer;
  587. begin
  588. Result := False;
  589. if FFWHost = '' then
  590. Mode := 0;
  591. if (FTargetPort = cFtpProtocol) or (FTargetPort = '21') then
  592. FTPServer := FTargetHost
  593. else
  594. FTPServer := FTargetHost + ':' + FTargetPort;
  595. case Mode of
  596. -1:
  597. LogonActions := CustomLogon;
  598. 1:
  599. LogonActions := Action1;
  600. 2:
  601. LogonActions := Action2;
  602. 3:
  603. LogonActions := Action3;
  604. 4:
  605. LogonActions := Action4;
  606. 5:
  607. LogonActions := Action5;
  608. 6:
  609. LogonActions := Action6;
  610. 7:
  611. LogonActions := Action7;
  612. 8:
  613. LogonActions := Action8;
  614. else
  615. LogonActions := Action0;
  616. end;
  617. i := 0;
  618. repeat
  619. case LogonActions[i] of
  620. 0: s := 'USER ' + FUserName;
  621. 1: s := 'PASS ' + FPassword;
  622. 2: s := 'ACCT ' + FAccount;
  623. 3: s := 'USER ' + FFWUserName;
  624. 4: s := 'PASS ' + FFWPassword;
  625. 5: s := 'SITE ' + FTPServer;
  626. 6: s := 'USER ' + FUserName + '@' + FTPServer;
  627. 7: s := 'OPEN ' + FTPServer;
  628. 8: s := 'USER ' + FFWUserName + '@' + FTPServer;
  629. 9: s := 'USER ' + FUserName + '@' + FTPServer + ' ' + FFWUserName;
  630. 10: s := 'USER ' + FUserName + '@' + FFWUserName + '@' + FTPServer;
  631. 11: s := 'PASS ' + FPassword + '@' + FFWPassword;
  632. end;
  633. x := FTPCommand(s);
  634. x := x div 100;
  635. if (x <> 2) and (x <> 3) then
  636. Exit;
  637. i := LogonActions[i + x - 1];
  638. case i of
  639. FTP_ERR:
  640. Exit;
  641. FTP_OK:
  642. begin
  643. Result := True;
  644. Exit;
  645. end;
  646. end;
  647. until False;
  648. end;
  649. function TFTPSend.Connect: Boolean;
  650. begin
  651. FSock.CloseSocket;
  652. FSock.Bind(FIPInterface, cAnyPort);
  653. if FSock.LastError = 0 then
  654. if FFWHost = '' then
  655. FSock.Connect(FTargetHost, FTargetPort)
  656. else
  657. FSock.Connect(FFWHost, FFWPort);
  658. if FSock.LastError = 0 then
  659. if FFullSSL then
  660. FSock.SSLDoConnect;
  661. Result := FSock.LastError = 0;
  662. end;
  663. function TFTPSend.Login: Boolean;
  664. var
  665. x: integer;
  666. begin
  667. Result := False;
  668. FCanResume := False;
  669. if not Connect then
  670. Exit;
  671. FIsTLS := FFullSSL;
  672. FIsDataTLS := False;
  673. repeat
  674. x := ReadResult div 100;
  675. until x <> 1;
  676. if x <> 2 then
  677. Exit;
  678. if FAutoTLS and not(FIsTLS) then
  679. if (FTPCommand('AUTH TLS') div 100) = 2 then
  680. begin
  681. FSock.SSLDoConnect;
  682. FIsTLS := FSock.LastError = 0;
  683. if not FIsTLS then
  684. begin
  685. Result := False;
  686. Exit;
  687. end;
  688. end;
  689. if not Auth(FFWMode) then
  690. Exit;
  691. if FIsTLS then
  692. begin
  693. FTPCommand('PBSZ 0');
  694. if FTLSonData then
  695. FIsDataTLS := (FTPCommand('PROT P') div 100) = 2;
  696. if not FIsDataTLS then
  697. FTPCommand('PROT C');
  698. end;
  699. FTPCommand('TYPE I');
  700. FTPCommand('STRU F');
  701. FTPCommand('MODE S');
  702. if FTPCommand('REST 0') = 350 then
  703. if FTPCommand('REST 1') = 350 then
  704. begin
  705. FTPCommand('REST 0');
  706. FCanResume := True;
  707. end;
  708. Result := True;
  709. end;
  710. function TFTPSend.Logout: Boolean;
  711. begin
  712. Result := (FTPCommand('QUIT') div 100) = 2;
  713. FSock.CloseSocket;
  714. end;
  715. procedure TFTPSend.ParseRemote(Value: string);
  716. var
  717. n: integer;
  718. nb, ne: integer;
  719. s: string;
  720. x: integer;
  721. begin
  722. Value := trim(Value);
  723. nb := Pos('(',Value);
  724. ne := Pos(')',Value);
  725. if (nb = 0) or (ne = 0) then
  726. begin
  727. nb:=RPos(' ',Value);
  728. s:=Copy(Value, nb + 1, Length(Value) - nb);
  729. end
  730. else
  731. begin
  732. s:=Copy(Value,nb+1,ne-nb-1);
  733. end;
  734. for n := 1 to 4 do
  735. if n = 1 then
  736. FDataIP := Fetch(s, ',')
  737. else
  738. FDataIP := FDataIP + '.' + Fetch(s, ',');
  739. x := StrToIntDef(Fetch(s, ','), 0) * 256;
  740. x := x + StrToIntDef(Fetch(s, ','), 0);
  741. FDataPort := IntToStr(x);
  742. end;
  743. procedure TFTPSend.ParseRemoteEPSV(Value: string);
  744. var
  745. n: integer;
  746. s, v: string;
  747. begin
  748. s := SeparateRight(Value, '(');
  749. s := Trim(SeparateLeft(s, ')'));
  750. Delete(s, Length(s), 1);
  751. v := '';
  752. for n := Length(s) downto 1 do
  753. if s[n] in ['0'..'9'] then
  754. v := s[n] + v
  755. else
  756. Break;
  757. FDataPort := v;
  758. FDataIP := FTargetHost;
  759. end;
  760. function TFTPSend.DataSocket: boolean;
  761. var
  762. s: string;
  763. begin
  764. Result := False;
  765. if FIsDataTLS then
  766. FPassiveMode := True;
  767. if FPassiveMode then
  768. begin
  769. if FSock.IP6used then
  770. s := '2'
  771. else
  772. s := '1';
  773. if FSock.IP6used and not(FForceOldPort) and ((FTPCommand('EPSV ' + s) div 100) = 2) then
  774. begin
  775. ParseRemoteEPSV(FResultString);
  776. end
  777. else
  778. if FSock.IP6used then
  779. Exit
  780. else
  781. begin
  782. if (FTPCommand('PASV') div 100) <> 2 then
  783. Exit;
  784. ParseRemote(FResultString);
  785. end;
  786. FDSock.CloseSocket;
  787. FDSock.Bind(FIPInterface, cAnyPort);
  788. FDSock.Connect(FDataIP, FDataPort);
  789. Result := FDSock.LastError = 0;
  790. end
  791. else
  792. begin
  793. FDSock.CloseSocket;
  794. if FForceDefaultPort then
  795. s := cFtpDataProtocol
  796. else
  797. s := '0';
  798. //data conection from same interface as command connection
  799. FDSock.Bind(FSock.GetLocalSinIP, s);
  800. if FDSock.LastError <> 0 then
  801. Exit;
  802. FDSock.SetLinger(True, 10000);
  803. FDSock.Listen;
  804. FDSock.GetSins;
  805. FDataIP := FDSock.GetLocalSinIP;
  806. FDataIP := FDSock.ResolveName(FDataIP);
  807. FDataPort := IntToStr(FDSock.GetLocalSinPort);
  808. if FSock.IP6used and (not FForceOldPort) then
  809. begin
  810. if IsIp6(FDataIP) then
  811. s := '2'
  812. else
  813. s := '1';
  814. s := 'EPRT |' + s +'|' + FDataIP + '|' + FDataPort + '|';
  815. Result := (FTPCommand(s) div 100) = 2;
  816. end;
  817. if not Result and IsIP(FDataIP) then
  818. begin
  819. s := ReplaceString(FDataIP, '.', ',');
  820. s := 'PORT ' + s + ',' + IntToStr(FDSock.GetLocalSinPort div 256)
  821. + ',' + IntToStr(FDSock.GetLocalSinPort mod 256);
  822. Result := (FTPCommand(s) div 100) = 2;
  823. end;
  824. end;
  825. end;
  826. function TFTPSend.AcceptDataSocket: Boolean;
  827. var
  828. x: TSocket;
  829. begin
  830. if FPassiveMode then
  831. Result := True
  832. else
  833. begin
  834. Result := False;
  835. if FDSock.CanRead(FTimeout) then
  836. begin
  837. x := FDSock.Accept;
  838. if not FDSock.UsingSocks then
  839. FDSock.CloseSocket;
  840. FDSock.Socket := x;
  841. Result := True;
  842. end;
  843. end;
  844. if Result and FIsDataTLS then
  845. begin
  846. FDSock.SSL.Assign(FSock.SSL);
  847. FDSock.SSLDoConnect;
  848. Result := FDSock.LastError = 0;
  849. end;
  850. end;
  851. function TFTPSend.DataRead(const DestStream: TStream): Boolean;
  852. var
  853. x: integer;
  854. begin
  855. Result := False;
  856. try
  857. if not AcceptDataSocket then
  858. Exit;
  859. FDSock.RecvStreamRaw(DestStream, FTimeout);
  860. FDSock.CloseSocket;
  861. x := ReadResult;
  862. Result := (x div 100) = 2;
  863. finally
  864. FDSock.CloseSocket;
  865. end;
  866. end;
  867. function TFTPSend.DataWrite(const SourceStream: TStream): Boolean;
  868. var
  869. x: integer;
  870. b: Boolean;
  871. begin
  872. Result := False;
  873. try
  874. if not AcceptDataSocket then
  875. Exit;
  876. FDSock.SendStreamRaw(SourceStream);
  877. b := FDSock.LastError = 0;
  878. FDSock.CloseSocket;
  879. x := ReadResult;
  880. Result := b and ((x div 100) = 2);
  881. finally
  882. FDSock.CloseSocket;
  883. end;
  884. end;
  885. function TFTPSend.List(Directory: string; NameList: Boolean): Boolean;
  886. var
  887. x: integer;
  888. begin
  889. Result := False;
  890. FDataStream.Clear;
  891. FFTPList.Clear;
  892. if Directory <> '' then
  893. Directory := ' ' + Directory;
  894. FTPCommand('TYPE A');
  895. if not DataSocket then
  896. Exit;
  897. if NameList then
  898. x := FTPCommand('NLST' + Directory)
  899. else
  900. if FUseMLSDList then
  901. x := FTPCommand('MLSD' + Directory)
  902. else
  903. x := FTPCommand('LIST' + Directory);
  904. if (x div 100) <> 1 then
  905. Exit;
  906. Result := DataRead(FDataStream);
  907. if (not NameList) and Result then
  908. begin
  909. FDataStream.Position := 0;
  910. FFTPList.Lines.LoadFromStream(FDataStream);
  911. if FUseMLSDList then
  912. FFTPList.ParseMLSDLines
  913. else
  914. FFTPList.ParseLines;
  915. end;
  916. FDataStream.Position := 0;
  917. end;
  918. function TFTPSend.RetrieveFile(const FileName: string; Restore: Boolean): Boolean;
  919. var
  920. RetrStream: TStream;
  921. begin
  922. Result := False;
  923. if FileName = '' then
  924. Exit;
  925. if not DataSocket then
  926. Exit;
  927. Restore := Restore and FCanResume;
  928. if FDirectFile then
  929. if Restore and FileExists(FDirectFileName) then
  930. RetrStream := TFileStream.Create(FDirectFileName,
  931. fmOpenReadWrite or fmShareExclusive)
  932. else
  933. RetrStream := TFileStream.Create(FDirectFileName,
  934. fmCreate or fmShareDenyWrite)
  935. else
  936. RetrStream := FDataStream;
  937. try
  938. if FBinaryMode then
  939. FTPCommand('TYPE I')
  940. else
  941. FTPCommand('TYPE A');
  942. if Restore then
  943. begin
  944. RetrStream.Position := RetrStream.Size;
  945. if (FTPCommand('REST ' + IntToStr(RetrStream.Size)) div 100) <> 3 then
  946. Exit;
  947. end
  948. else
  949. if RetrStream is TMemoryStream then
  950. TMemoryStream(RetrStream).Clear;
  951. if (FTPCommand('RETR ' + FileName) div 100) <> 1 then
  952. Exit;
  953. Result := DataRead(RetrStream);
  954. if not FDirectFile then
  955. RetrStream.Position := 0;
  956. finally
  957. if FDirectFile then
  958. RetrStream.Free;
  959. end;
  960. end;
  961. function TFTPSend.InternalStor(const Command: string; RestoreAt: int64): Boolean;
  962. var
  963. SendStream: TStream;
  964. StorSize: int64;
  965. begin
  966. Result := False;
  967. if FDirectFile then
  968. if not FileExists(FDirectFileName) then
  969. Exit
  970. else
  971. SendStream := TFileStream.Create(FDirectFileName,
  972. fmOpenRead or fmShareDenyWrite)
  973. else
  974. SendStream := FDataStream;
  975. try
  976. if not DataSocket then
  977. Exit;
  978. if FBinaryMode then
  979. FTPCommand('TYPE I')
  980. else
  981. FTPCommand('TYPE A');
  982. StorSize := SendStream.Size;
  983. if not FCanResume then
  984. RestoreAt := 0;
  985. if (StorSize > 0) and (RestoreAt = StorSize) then
  986. begin
  987. Result := True;
  988. Exit;
  989. end;
  990. if RestoreAt > StorSize then
  991. RestoreAt := 0;
  992. FTPCommand('ALLO ' + IntToStr(StorSize - RestoreAt));
  993. if FCanResume then
  994. if (FTPCommand('REST ' + IntToStr(RestoreAt)) div 100) <> 3 then
  995. Exit;
  996. SendStream.Position := RestoreAt;
  997. if (FTPCommand(Command) div 100) <> 1 then
  998. Exit;
  999. Result := DataWrite(SendStream);
  1000. finally
  1001. if FDirectFile then
  1002. SendStream.Free;
  1003. end;
  1004. end;
  1005. function TFTPSend.StoreFile(const FileName: string; Restore: Boolean): Boolean;
  1006. var
  1007. RestoreAt: int64;
  1008. begin
  1009. Result := False;
  1010. if FileName = '' then
  1011. Exit;
  1012. RestoreAt := 0;
  1013. Restore := Restore and FCanResume;
  1014. if Restore then
  1015. begin
  1016. RestoreAt := Self.FileSize(FileName);
  1017. if RestoreAt < 0 then
  1018. RestoreAt := 0;
  1019. end;
  1020. Result := InternalStor('STOR ' + FileName, RestoreAt);
  1021. end;
  1022. function TFTPSend.StoreUniqueFile: Boolean;
  1023. begin
  1024. Result := InternalStor('STOU', 0);
  1025. end;
  1026. function TFTPSend.AppendFile(const FileName: string): Boolean;
  1027. begin
  1028. Result := False;
  1029. if FileName = '' then
  1030. Exit;
  1031. Result := InternalStor('APPE ' + FileName, 0);
  1032. end;
  1033. function TFTPSend.NoOp: Boolean;
  1034. begin
  1035. Result := (FTPCommand('NOOP') div 100) = 2;
  1036. end;
  1037. function TFTPSend.RenameFile(const OldName, NewName: string): Boolean;
  1038. begin
  1039. Result := False;
  1040. if (FTPCommand('RNFR ' + OldName) div 100) <> 3 then
  1041. Exit;
  1042. Result := (FTPCommand('RNTO ' + NewName) div 100) = 2;
  1043. end;
  1044. function TFTPSend.DeleteFile(const FileName: string): Boolean;
  1045. begin
  1046. Result := (FTPCommand('DELE ' + FileName) div 100) = 2;
  1047. end;
  1048. function TFTPSend.FileSize(const FileName: string): int64;
  1049. var
  1050. s: string;
  1051. begin
  1052. Result := -1;
  1053. if (FTPCommand('SIZE ' + FileName) div 100) = 2 then
  1054. begin
  1055. s := Trim(SeparateRight(ResultString, ' '));
  1056. s := Trim(SeparateLeft(s, ' '));
  1057. {$IFDEF VER100}
  1058. Result := StrToIntDef(s, -1);
  1059. {$ELSE}
  1060. Result := StrToInt64Def(s, -1);
  1061. {$ENDIF}
  1062. end;
  1063. end;
  1064. function TFTPSend.ChangeWorkingDir(const Directory: string): Boolean;
  1065. begin
  1066. Result := (FTPCommand('CWD ' + Directory) div 100) = 2;
  1067. end;
  1068. function TFTPSend.ChangeToParentDir: Boolean;
  1069. begin
  1070. Result := (FTPCommand('CDUP') div 100) = 2;
  1071. end;
  1072. function TFTPSend.ChangeToRootDir: Boolean;
  1073. begin
  1074. Result := ChangeWorkingDir('/');
  1075. end;
  1076. function TFTPSend.DeleteDir(const Directory: string): Boolean;
  1077. begin
  1078. Result := (FTPCommand('RMD ' + Directory) div 100) = 2;
  1079. end;
  1080. function TFTPSend.CreateDir(const Directory: string): Boolean;
  1081. begin
  1082. Result := (FTPCommand('MKD ' + Directory) div 100) = 2;
  1083. end;
  1084. function TFTPSend.GetCurrentDir: String;
  1085. begin
  1086. Result := '';
  1087. if (FTPCommand('PWD') div 100) = 2 then
  1088. begin
  1089. Result := SeparateRight(FResultString, '"');
  1090. Result := Trim(Separateleft(Result, '"'));
  1091. end;
  1092. end;
  1093. procedure TFTPSend.Abort;
  1094. begin
  1095. FSock.SendString('ABOR' + CRLF);
  1096. FDSock.StopFlag := True;
  1097. end;
  1098. procedure TFTPSend.TelnetAbort;
  1099. begin
  1100. FSock.SendString(#$FF + #$F4 + #$FF + #$F2);
  1101. Abort;
  1102. end;
  1103. {==============================================================================}
  1104. procedure TFTPListRec.Assign(Value: TFTPListRec);
  1105. begin
  1106. FFileName := Value.FileName;
  1107. FDirectory := Value.Directory;
  1108. FReadable := Value.Readable;
  1109. FFileSize := Value.FileSize;
  1110. FFileTime := Value.FileTime;
  1111. FOriginalLine := Value.OriginalLine;
  1112. FMask := Value.Mask;
  1113. end;
  1114. constructor TFTPList.Create;
  1115. begin
  1116. inherited Create;
  1117. FList := TFTPRecList.Create;
  1118. FLines := TStringList.Create;
  1119. FMasks := TStringList.Create;
  1120. FUnparsedLines := TStringList.Create;
  1121. //various UNIX
  1122. FMasks.add('pppppppppp $!!!S*$TTT$DD$hh:mm:ss$YYYY$n*'); //Fiala - pridany dvojtecky do casu
  1123. FMasks.add('pppppppppp $!!!S*$DD$TTT$hh:mm:ss$YYYY$n*'); //Fiala - pridany dvojtecky do casu
  1124. FMasks.add('pppppppppp $!!!S*$TTT$DD$UUUUU$n*'); //mostly used UNIX format
  1125. FMasks.add('pppppppppp $!!!S*$DD$TTT$UUUUU$n*');
  1126. //MacOS
  1127. FMasks.add('pppppppppp $!!S*$TTT$DD$UUUUU$n*');
  1128. FMasks.add('pppppppppp $!S*$TTT$DD$UUUUU$n*');
  1129. //Novell
  1130. FMasks.add('d $!S*$TTT$DD$UUUUU$n*');
  1131. //Windows
  1132. FMasks.add('MM DD YY hh mmH !S* n*');
  1133. FMasks.add('MM DD YY hh mmH $ d!n*');
  1134. FMasks.add('MM DD YYYY hh mmH !S* n*');
  1135. FMasks.add('MM DD YYYY hh mmH $ d!n*');
  1136. FMasks.add('DD MM YYYY hh mmH !S* n*');
  1137. FMasks.add('DD MM YYYY hh mmH $ d!n*');
  1138. //VMS
  1139. FMasks.add('v*$ DD TTT YYYY hh mm');
  1140. FMasks.add('v*$DD TTT YYYY hh mm ss');
  1141. FMasks.add('v*$D TTT YYYY hh mm'); //Fiala
  1142. FMasks.add('v*$!DD TTT YYYY hh mm ss');
  1143. //sample: ABB.DIR;1 1/35 18-SEP-2007 10:46:39 [STEVEH] (RWE,RWE,RWE,RWE)
  1144. FMasks.add('v*\$!DD TTT YYYY hh mm ss');
  1145. //sample: DELMAS.SQL;7 0/0 6-DEC-2007 10:43:44 [STEVEH] (RWED,RWED,RWED,RWED)
  1146. FMasks.add('v*$!D TTT YYYY hh mm ss'); //Fiala
  1147. FMasks.add('v*\$!D TTT YYYY hh mm ss'); //Fiala
  1148. FMasks.add('v*$!D TTT YYYY hh mm ss'); //Fiala
  1149. FMasks.add('n*$ YYYY MM DD hh mm$S*'); //Fiala
  1150. //sample: STANS_DIFF.DIR;1 1 13-APR-2006 13:27 [AGR4] (RWE,RWE,RE,E)
  1151. FMasks.add('v*$!DD TTT YYYY hh mm');
  1152. // FMasks.add('n*$ YYYY MM DD hh mm$S*');
  1153. FMasks.add('$!: :n*');
  1154. //AS400
  1155. FMasks.add('!S*$MM DD YY hh mm ss !n*');
  1156. FMasks.add('!S*$DD MM YY hh mm ss !n*');
  1157. FMasks.add('n*!S*$MM DD YY hh mm ss d');
  1158. FMasks.add('n*!S*$DD MM YY hh mm ss d');
  1159. //VxWorks
  1160. FMasks.add('$S* TTT DD YYYY hh mm ss $n* $ d');
  1161. FMasks.add('$S* TTT DD YYYY hh mm ss $n*');
  1162. //Distinct
  1163. FMasks.add('d $S*$TTT DD YYYY hh mm$n*');
  1164. FMasks.add('d $S*$TTT DD$hh mm$n*');
  1165. //PC-NFSD
  1166. FMasks.add('nnnnnnnn.nnn dSSSSSSSSSSS MM DD YY hh mmH');
  1167. //VOS
  1168. FMasks.add('- SSSSS YY MM DD hh mm ss n*');
  1169. FMasks.add('- d= SSSSS YY MM DD hh mm ss n*');
  1170. //Unissys ClearPath
  1171. FMasks.add('nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn SSSSSSSSS MM DD YYYY hh mm');
  1172. FMasks.add('n*\x SSSSSSSSS MM DD YYYY hh mm');
  1173. //IBM
  1174. FMasks.add('- SSSSSSSSSSSS d MM DD YYYY hh mm n*');
  1175. //OS9
  1176. FMasks.add('- YY MM DD hhmm d SSSSSSSSS n*');
  1177. //tandem
  1178. FMasks.add('nnnnnnnn SSSSSSS DD TTT YY hh mm ss');
  1179. //MVS
  1180. FMasks.add('- YYYY MM DD SSSSS d=O n*');
  1181. //BullGCOS8
  1182. FMasks.add(' $S* MM DD YY hh mm ss !n*');
  1183. FMasks.add('d $S* MM DD YY !n*');
  1184. //IBM AIXs
  1185. // sample: -rw---- 1 ITINERA DAT 58 JAN 09 2008 TSITIS009VAJ_COB
  1186. FMasks.add('ppppppp $!!!S*$TTT$DD$YYYY$n*'); //Fiala
  1187. FMasks.add('pppppppppp SSSSSSSSSS DD !YYYY n*'); //Fiala
  1188. FMasks.add('pppppppppp SSSSSSSSSS DD !hh mm n*'); //Fiala
  1189. // drwxr-xr-x 10 hol prog 45056 20 8 10:00 adis30161
  1190. FMasks.add('pppppppppp SSSSSSSSSS DD MM!hh mm n*'); //Fiala
  1191. // drwxr-xr-x 2 hol prog 4096 20 1 2005 bin
  1192. FMasks.add('pppppppppp SSSSSSSSSS DD MM YYYY n*'); //Fiala
  1193. //IBM VM //Fiala
  1194. // MQ_REPTS TESTVIEW V 72 139 1 2009-01-28 11:58:07 -
  1195. // NEW DIR - - - 2009-11-04 18:31:50 -
  1196. FMasks.add('n*.$n*$ SSSSSSSSSS YYYY-MM-DD hh:mm:ss -');
  1197. FMasks.add('nnnnnnnnnnnnnnnnn d - - - YYYY-MM-DD hh:mm:ss -');
  1198. //sample: Migrated $SRC.AFLG
  1199. FMasks.add(' dxx n*'); //Fiala
  1200. //VMS - new untouched files (name only)
  1201. // ADR10AI2
  1202. FMasks.Add('n*§'); //Fiala
  1203. //IBM VM
  1204. // MQ_REPTS TESTVIEW V 72 139 1 2009-01-28 11:58:07 -
  1205. // NEW DIR - - - 2009-11-04 18:31:50 -
  1206. FMasks.add('nnnnnnnnnnnnnnnnn x SSSSSSSSSS YYYY-MM-DD hh:mm:ss -');
  1207. FMasks.add('nnnnnnnnnnnnnnnnn d - - - YYYY-MM-DD hh:mm:ss -');
  1208. //BullGCOS7
  1209. // FMasks.add(' TTT DD YYYY n*');
  1210. // FMasks.add(' d n*');
  1211. end;
  1212. destructor TFTPList.Destroy;
  1213. begin
  1214. Clear;
  1215. FList.Free;
  1216. FLines.Free;
  1217. FMasks.Free;
  1218. FUnparsedLines.Free;
  1219. inherited Destroy;
  1220. end;
  1221. procedure TFTPList.Clear;
  1222. var
  1223. n:integer;
  1224. begin
  1225. for n := 0 to FList.Count - 1 do
  1226. if Assigned(FList[n]) then
  1227. TFTPListRec(FList[n]).Free;
  1228. FList.Clear;
  1229. FLines.Clear;
  1230. FUnparsedLines.Clear;
  1231. end;
  1232. function TFTPList.Count: integer;
  1233. begin
  1234. Result := FList.Count;
  1235. end;
  1236. function TFTPList.GetListItem(Index: integer): TFTPListRec;
  1237. begin
  1238. Result := nil;
  1239. if Index < Count then
  1240. Result := TFTPListRec(FList[Index]);
  1241. end;
  1242. procedure TFTPList.Assign(Value: TFTPList);
  1243. var
  1244. flr: TFTPListRec;
  1245. n: integer;
  1246. begin
  1247. Clear;
  1248. for n := 0 to Value.Count - 1 do
  1249. begin
  1250. flr := TFTPListRec.Create;
  1251. flr.Assign(Value[n]);
  1252. Flist.Add(flr);
  1253. end;
  1254. Lines.Assign(Value.Lines);
  1255. Masks.Assign(Value.Masks);
  1256. UnparsedLines.Assign(Value.UnparsedLines);
  1257. end;
  1258. procedure TFTPList.ClearStore;
  1259. begin
  1260. Monthnames := '';
  1261. BlockSize := '';
  1262. DirFlagValue := '';
  1263. FileName := '';
  1264. VMSFileName := '';
  1265. Day := '';
  1266. Month := '';
  1267. ThreeMonth := '';
  1268. YearTime := '';
  1269. Year := '';
  1270. Hours := '';
  1271. HoursModif := '';
  1272. Minutes := '';
  1273. Seconds := '';
  1274. Size := '';
  1275. Permissions := '';
  1276. DirFlag := '';
  1277. end;
  1278. function TFTPList.ParseByMask(Value, NextValue, Mask: string): Integer;
  1279. var
  1280. Ivalue, IMask: integer;
  1281. MaskC, LastMaskC: char;
  1282. c: char;
  1283. s: string;
  1284. begin
  1285. ClearStore;
  1286. Result := 0;
  1287. if Value = '' then
  1288. Exit;
  1289. if Mask = '' then
  1290. Exit;
  1291. Ivalue := 1;
  1292. IMask := 1;
  1293. Result := 1;
  1294. LastMaskC := ' ';
  1295. Value := TrimRight(Value); //Fiala
  1296. while Imask <= Length(mask) do
  1297. begin
  1298. if not (Mask[Imask] in ['*', '\', '§']) and (Ivalue > Length(Value)) then //Fiala
  1299. begin
  1300. Result := 0;
  1301. Exit;
  1302. end;
  1303. MaskC := Mask[Imask];
  1304. // if Ivalue > Length(Value) then
  1305. // Exit;
  1306. c := Value[Ivalue];
  1307. case MaskC of
  1308. 'n':
  1309. FileName := FileName + c;
  1310. 'v':
  1311. VMSFileName := VMSFileName + c;
  1312. '.':
  1313. begin
  1314. if c in ['.', ' '] then
  1315. FileName := TrimSP(FileName) + '.'
  1316. else
  1317. begin
  1318. Result := 0;
  1319. Exit;
  1320. end;
  1321. end;
  1322. 'D':
  1323. Day := Day + c;
  1324. 'M':
  1325. Month := Month + c;
  1326. 'T':
  1327. ThreeMonth := ThreeMonth + c;
  1328. 'U':
  1329. YearTime := YearTime + c;
  1330. 'Y':
  1331. Year := Year + c;
  1332. 'h':
  1333. Hours := Hours + c;
  1334. 'H':
  1335. HoursModif := HoursModif + c;
  1336. 'm':
  1337. Minutes := Minutes + c;
  1338. 's':
  1339. Seconds := Seconds + c;
  1340. 'S':
  1341. Size := Size + c;
  1342. 'p':
  1343. Permissions := Permissions + c;
  1344. 'd':
  1345. DirFlag := DirFlag + c;
  1346. 'x':
  1347. if c <> ' ' then
  1348. begin
  1349. Result := 0;
  1350. Exit;
  1351. end;
  1352. 'y': //Fiala
  1353. if c <> ' ' then Result := 0;
  1354. '*':
  1355. begin
  1356. s := '';
  1357. if LastMaskC in ['n', 'v'] then
  1358. begin
  1359. if Imask = Length(Mask) then
  1360. s := Copy(Value, IValue, Maxint)
  1361. else
  1362. while IValue <= Length(Value) do
  1363. begin
  1364. if Value[Ivalue] = ' ' then
  1365. break;
  1366. s := s + Value[Ivalue];
  1367. Inc(Ivalue);
  1368. end;
  1369. if LastMaskC = 'n' then
  1370. FileName := FileName + s
  1371. else
  1372. VMSFileName := VMSFileName + s;
  1373. end
  1374. else
  1375. begin
  1376. while IValue <= Length(Value) do
  1377. begin
  1378. if not(Value[Ivalue] in ['0'..'9']) then
  1379. break;
  1380. s := s + Value[Ivalue];
  1381. Inc(Ivalue);
  1382. end;
  1383. case LastMaskC of
  1384. 'S':
  1385. Size := Size + s;
  1386. end;
  1387. end;
  1388. Dec(IValue);
  1389. end;
  1390. '!':
  1391. begin
  1392. while IValue <= Length(Value) do
  1393. begin
  1394. if Value[Ivalue] = ' ' then
  1395. break;
  1396. Inc(Ivalue);
  1397. end;
  1398. while IValue <= Length(Value) do
  1399. begin
  1400. if Value[Ivalue] <> ' ' then
  1401. break;
  1402. Inc(Ivalue);
  1403. end;
  1404. Dec(IValue);
  1405. end;
  1406. '§': //Fiala
  1407. if IValue < Length(Value) then
  1408. begin
  1409. Result := 0;
  1410. Break;
  1411. end;
  1412. '$':
  1413. begin
  1414. while IValue <= Length(Value) do
  1415. begin
  1416. if not(Value[Ivalue] in [' ', #9]) then
  1417. break;
  1418. Inc(Ivalue);
  1419. end;
  1420. Dec(IValue);
  1421. end;
  1422. '=':
  1423. begin
  1424. s := '';
  1425. case LastmaskC of
  1426. 'S':
  1427. begin
  1428. while Imask <= Length(Mask) do
  1429. begin
  1430. if not(Mask[Imask] in ['0'..'9']) then
  1431. break;
  1432. s := s + Mask[Imask];
  1433. Inc(Imask);
  1434. end;
  1435. Dec(Imask);
  1436. BlockSize := s;
  1437. end;
  1438. 'T':
  1439. begin
  1440. Monthnames := Copy(Mask, IMask, 12 * 3);
  1441. Inc(IMask, 12 * 3);
  1442. end;
  1443. 'd':
  1444. begin
  1445. Inc(Imask);
  1446. DirFlagValue := Mask[Imask];
  1447. end;
  1448. end;
  1449. end;
  1450. ':': //Fiala
  1451. if c <> ':' then
  1452. begin
  1453. Result := 0;
  1454. Exit;
  1455. end;
  1456. '\':
  1457. begin
  1458. Value := NextValue;
  1459. IValue := 0;
  1460. Result := 2;
  1461. end;
  1462. end;
  1463. Inc(Ivalue);
  1464. Inc(Imask);
  1465. LastMaskC := MaskC;
  1466. end;
  1467. end;
  1468. function TFTPList.CheckValues: Boolean;
  1469. var
  1470. x, n: integer;
  1471. begin
  1472. Result := false;
  1473. if (Trim(FileName) = '') and (Trim(VMSFileName) = '') then Exit; //Fiala
  1474. if FileName <> '' then
  1475. begin
  1476. if pos('?', VMSFilename) > 0 then
  1477. Exit;
  1478. if pos('*', VMSFilename) > 0 then
  1479. Exit;
  1480. end;
  1481. if VMSFileName <> '' then
  1482. if pos(';', VMSFilename) <= 0 then
  1483. Exit;
  1484. // if (FileName = '') and (VMSFileName = '') then
  1485. // Exit;
  1486. if Permissions <> '' then
  1487. begin
  1488. if (length(Permissions) <> 10) and (length(Permissions) <> 7) then //Fiala
  1489. Exit;
  1490. for n := 1 to length(Permissions) do //Fiala
  1491. if not (Permissions[n] in
  1492. ['a', 'b', 'c', 'd', 'h', 'l', 'p', 'r', 's', 't', 'w', 'x', 'y', '-', 'S']) then //Fiala
  1493. Exit;
  1494. end;
  1495. if Day <> '' then
  1496. begin
  1497. Day := TrimSP(Day);
  1498. x := StrToIntDef(day, -1);
  1499. if (x < 1) or (x > 31) then
  1500. Exit;
  1501. end;
  1502. if Month <> '' then
  1503. begin
  1504. Month := TrimSP(Month);
  1505. x := StrToIntDef(Month, -1);
  1506. if (x < 1) or (x > 12) then
  1507. Exit;
  1508. end;
  1509. if Hours <> '' then
  1510. begin
  1511. Hours := TrimSP(Hours);
  1512. x := StrToIntDef(Hours, -1);
  1513. if (x < 0) or (x > 24) then
  1514. Exit;
  1515. end;
  1516. if HoursModif <> '' then
  1517. begin
  1518. if not (HoursModif[1] in ['a', 'A', 'p', 'P']) then
  1519. Exit;
  1520. end;
  1521. if Minutes <> '' then
  1522. begin
  1523. Minutes := TrimSP(Minutes);
  1524. x := StrToIntDef(Minutes, -1);
  1525. if (x < 0) or (x > 59) then
  1526. Exit;
  1527. end;
  1528. if Seconds <> '' then
  1529. begin
  1530. Seconds := TrimSP(Seconds);
  1531. x := StrToIntDef(Seconds, -1);
  1532. if (x < 0) or (x > 59) then
  1533. Exit;
  1534. end;
  1535. if Size <> '' then
  1536. begin
  1537. Size := TrimSP(Size);
  1538. for n := 1 to Length(Size) do
  1539. if not (Size[n] in ['0'..'9']) then
  1540. Exit;
  1541. end;
  1542. if length(Monthnames) = (12 * 3) then
  1543. for n := 1 to 12 do
  1544. CustomMonthNames[n] := Copy(Monthnames, ((n - 1) * 3) + 1, 3);
  1545. if ThreeMonth <> '' then
  1546. begin
  1547. x := GetMonthNumber(ThreeMonth);
  1548. if (x = 0) then
  1549. Exit;
  1550. end;
  1551. if YearTime <> '' then
  1552. begin
  1553. YearTime := ReplaceString(YearTime, '-', ':');
  1554. if pos(':', YearTime) > 0 then
  1555. begin
  1556. if (GetTimeFromstr(YearTime) = -1) then
  1557. Exit;
  1558. end
  1559. else
  1560. begin
  1561. YearTime := TrimSP(YearTime);
  1562. x := StrToIntDef(YearTime, -1);
  1563. if (x = -1) then
  1564. Exit;
  1565. if (x < 1900) or (x > 2100) then
  1566. Exit;
  1567. end;
  1568. end;
  1569. if Year <> '' then
  1570. begin
  1571. Year := TrimSP(Year);
  1572. x := StrToIntDef(Year, -1);
  1573. if (x = -1) then
  1574. Exit;
  1575. if Length(Year) = 4 then
  1576. begin
  1577. if not((x > 1900) and (x < 2100)) then
  1578. Exit;
  1579. end
  1580. else
  1581. if Length(Year) = 2 then
  1582. begin
  1583. if not((x >= 0) and (x <= 99)) then
  1584. Exit;
  1585. end
  1586. else
  1587. if Length(Year) = 3 then
  1588. begin
  1589. if not((x >= 100) and (x <= 110)) then
  1590. Exit;
  1591. end
  1592. else
  1593. Exit;
  1594. end;
  1595. Result := True;
  1596. end;
  1597. procedure TFTPList.FillRecord(const Value: TFTPListRec);
  1598. var
  1599. s: string;
  1600. x: integer;
  1601. myear: Word;
  1602. mmonth: Word;
  1603. mday: Word;
  1604. mhours, mminutes, mseconds: word;
  1605. n: integer;
  1606. begin
  1607. s := DirFlagValue;
  1608. if s = '' then
  1609. s := 'D';
  1610. s := Uppercase(s);
  1611. Value.Directory := s = Uppercase(DirFlag);
  1612. if FileName <> '' then
  1613. Value.FileName := SeparateLeft(Filename, ' -> ');
  1614. if VMSFileName <> '' then
  1615. begin
  1616. Value.FileName := VMSFilename;
  1617. Value.Directory := Pos('.DIR;',VMSFilename) > 0;
  1618. end;
  1619. Value.FileName := TrimSPRight(Value.FileName);
  1620. Value.Readable := not Value.Directory;
  1621. if BlockSize <> '' then
  1622. x := StrToIntDef(BlockSize, 1)
  1623. else
  1624. x := 1;
  1625. {$IFDEF VER100}
  1626. Value.FileSize := x * StrToIntDef(Size, 0);
  1627. {$ELSE}
  1628. Value.FileSize := x * StrToInt64Def(Size, 0);
  1629. {$ENDIF}
  1630. DecodeDate(Date,myear,mmonth,mday);
  1631. myear := YearOf(Date); //Fiala
  1632. mMonth := 1; //Fiala
  1633. mDay := 1; //Fiala
  1634. mhours := 0;
  1635. mminutes := 0;
  1636. mseconds := 0;
  1637. if Day <> '' then
  1638. mday := StrToIntDef(day, 1);
  1639. if Month <> '' then
  1640. mmonth := StrToIntDef(Month, 1);
  1641. if length(Monthnames) = (12 * 3) then
  1642. for n := 1 to 12 do
  1643. CustomMonthNames[n] := Copy(Monthnames, ((n - 1) * 3) + 1, 3);
  1644. if ThreeMonth <> '' then
  1645. mmonth := GetMonthNumber(ThreeMonth);
  1646. if Year <> '' then
  1647. begin
  1648. myear := StrToIntDef(Year, 0);
  1649. if (myear <= 99) and (myear > 50) then
  1650. myear := myear + 1900;
  1651. if myear <= 50 then
  1652. myear := myear + 2000;
  1653. end;
  1654. if YearTime <> '' then
  1655. begin
  1656. if pos(':', YearTime) > 0 then
  1657. begin
  1658. YearTime := TrimSP(YearTime);
  1659. mhours := StrToIntDef(Separateleft(YearTime, ':'), 0);
  1660. mminutes := StrToIntDef(SeparateRight(YearTime, ':'), 0);
  1661. try { osetreni spatneho formatu data } //Fiala
  1662. if (Encodedate(myear, mmonth, mday) //tohle kvuli spatnemu casu na FTP serveru
  1663. + EncodeTime(mHours, mminutes, 0, 0)) > now then
  1664. Dec(mYear);
  1665. except //Fiala
  1666. mYear := YearOf(Date()); //Fiala
  1667. end;
  1668. end
  1669. else
  1670. myear := StrToIntDef(YearTime, 0);
  1671. end;
  1672. if Minutes <> '' then
  1673. mminutes := StrToIntDef(Minutes, 0);
  1674. if Seconds <> '' then
  1675. mseconds := StrToIntDef(Seconds, 0);
  1676. if Hours <> '' then
  1677. begin
  1678. mHours := StrToIntDef(Hours, 0);
  1679. if HoursModif <> '' then
  1680. if Uppercase(HoursModif[1]) = 'P' then
  1681. if mHours <> 12 then
  1682. mHours := MHours + 12;
  1683. end;
  1684. { osetrime prechodne roky } //Fiala
  1685. if (mday = 29) and (mmonth = 2) and not IsLeapYear(myear) then
  1686. Dec(Mday);
  1687. Value.FileTime := Encodedate(myear, mmonth, mday)
  1688. + EncodeTime(mHours, mminutes, mseconds, 0);
  1689. if Permissions <> '' then
  1690. begin
  1691. Value.Permission := Permissions;
  1692. Value.Readable := Uppercase(permissions)[2] = 'R';
  1693. if Uppercase(permissions)[1] = 'D' then
  1694. begin
  1695. Value.Directory := True;
  1696. Value.Readable := false;
  1697. end
  1698. else
  1699. if Uppercase(permissions)[1] = 'L' then
  1700. Value.Directory := True;
  1701. end;
  1702. end;
  1703. function TFTPList.ParseEPLF(Value: string): Boolean;
  1704. var
  1705. s, os: string;
  1706. flr: TFTPListRec;
  1707. begin
  1708. Result := False;
  1709. if Value <> '' then
  1710. if Value[1] = '+' then
  1711. begin
  1712. os := Value;
  1713. Delete(Value, 1, 1);
  1714. flr := TFTPListRec.create;
  1715. flr.FileName := SeparateRight(Value, #9);
  1716. s := Fetch(Value, ',');
  1717. while s <> '' do
  1718. begin
  1719. if s[1] = #9 then
  1720. Break;
  1721. case s[1] of
  1722. '/':
  1723. flr.Directory := true;
  1724. 'r':
  1725. flr.Readable := true;
  1726. 's':
  1727. {$IFDEF VER100}
  1728. flr.FileSize := StrToIntDef(Copy(s, 2, Length(s) - 1), 0);
  1729. {$ELSE}
  1730. flr.FileSize := StrToInt64Def(Copy(s, 2, Length(s) - 1), 0);
  1731. {$ENDIF}
  1732. 'm':
  1733. flr.FileTime := (StrToIntDef(Copy(s, 2, Length(s) - 1), 0) / 86400)
  1734. + 25569;
  1735. end;
  1736. s := Fetch(Value, ',');
  1737. end;
  1738. if flr.FileName <> '' then
  1739. if (flr.Directory and ((flr.FileName = '.') or (flr.FileName = '..')))
  1740. or (flr.FileName = '') then
  1741. flr.free
  1742. else
  1743. begin
  1744. flr.OriginalLine := os;
  1745. flr.Mask := 'EPLF';
  1746. Flist.Add(flr);
  1747. Result := True;
  1748. end;
  1749. end;
  1750. end;
  1751. procedure TFTPList.ParseLines;
  1752. var
  1753. flr: TFTPListRec;
  1754. n, m: Integer;
  1755. S: string;
  1756. x: integer;
  1757. b: Boolean;
  1758. begin
  1759. n := 0;
  1760. while n < Lines.Count do
  1761. begin
  1762. if n = Lines.Count - 1 then
  1763. s := ''
  1764. else
  1765. s := Lines[n + 1];
  1766. b := False;
  1767. x := 0;
  1768. if ParseEPLF(Lines[n]) then
  1769. begin
  1770. b := True;
  1771. x := 1;
  1772. end
  1773. else
  1774. for m := 0 to Masks.Count - 1 do
  1775. begin
  1776. x := ParseByMask(Lines[n], s, Masks[m]);
  1777. if x > 0 then
  1778. if CheckValues then
  1779. begin
  1780. flr := TFTPListRec.create;
  1781. FillRecord(flr);
  1782. flr.OriginalLine := Lines[n];
  1783. flr.Mask := Masks[m];
  1784. if flr.Directory and ((flr.FileName = '.') or (flr.FileName = '..')) then
  1785. flr.free
  1786. else
  1787. Flist.Add(flr);
  1788. b := True;
  1789. Break;
  1790. end;
  1791. end;
  1792. if not b then
  1793. FUnparsedLines.Add(Lines[n]);
  1794. Inc(n);
  1795. if x > 1 then
  1796. Inc(n, x - 1);
  1797. end;
  1798. end;
  1799. {==============================================================================}
  1800. function FtpGetFile(const IP, Port, FileName, LocalFile,
  1801. User, Pass: string): Boolean;
  1802. begin
  1803. Result := False;
  1804. with TFTPSend.Create do
  1805. try
  1806. if User <> '' then
  1807. begin
  1808. Username := User;
  1809. Password := Pass;
  1810. end;
  1811. TargetHost := IP;
  1812. TargetPort := Port;
  1813. if not Login then
  1814. Exit;
  1815. DirectFileName := LocalFile;
  1816. DirectFile:=True;
  1817. Result := RetrieveFile(FileName, False);
  1818. Logout;
  1819. finally
  1820. Free;
  1821. end;
  1822. end;
  1823. function FtpPutFile(const IP, Port, FileName, LocalFile,
  1824. User, Pass: string): Boolean;
  1825. begin
  1826. Result := False;
  1827. with TFTPSend.Create do
  1828. try
  1829. if User <> '' then
  1830. begin
  1831. Username := User;
  1832. Password := Pass;
  1833. end;
  1834. TargetHost := IP;
  1835. TargetPort := Port;
  1836. if not Login then
  1837. Exit;
  1838. DirectFileName := LocalFile;
  1839. DirectFile:=True;
  1840. Result := StoreFile(FileName, False);
  1841. Logout;
  1842. finally
  1843. Free;
  1844. end;
  1845. end;
  1846. function FtpInterServerTransfer(
  1847. const FromIP, FromPort, FromFile, FromUser, FromPass: string;
  1848. const ToIP, ToPort, ToFile, ToUser, ToPass: string): Boolean;
  1849. var
  1850. FromFTP, ToFTP: TFTPSend;
  1851. s: string;
  1852. x: integer;
  1853. begin
  1854. Result := False;
  1855. FromFTP := TFTPSend.Create;
  1856. toFTP := TFTPSend.Create;
  1857. try
  1858. if FromUser <> '' then
  1859. begin
  1860. FromFTP.Username := FromUser;
  1861. FromFTP.Password := FromPass;
  1862. end;
  1863. if ToUser <> '' then
  1864. begin
  1865. ToFTP.Username := ToUser;
  1866. ToFTP.Password := ToPass;
  1867. end;
  1868. FromFTP.TargetHost := FromIP;
  1869. FromFTP.TargetPort := FromPort;
  1870. ToFTP.TargetHost := ToIP;
  1871. ToFTP.TargetPort := ToPort;
  1872. if not FromFTP.Login then
  1873. Exit;
  1874. if not ToFTP.Login then
  1875. Exit;
  1876. if (FromFTP.FTPCommand('PASV') div 100) <> 2 then
  1877. Exit;
  1878. FromFTP.ParseRemote(FromFTP.ResultString);
  1879. s := ReplaceString(FromFTP.DataIP, '.', ',');
  1880. s := 'PORT ' + s + ',' + IntToStr(StrToIntDef(FromFTP.DataPort, 0) div 256)
  1881. + ',' + IntToStr(StrToIntDef(FromFTP.DataPort, 0) mod 256);
  1882. if (ToFTP.FTPCommand(s) div 100) <> 2 then
  1883. Exit;
  1884. x := ToFTP.FTPCommand('RETR ' + FromFile);
  1885. if (x div 100) <> 1 then
  1886. Exit;
  1887. x := FromFTP.FTPCommand('STOR ' + ToFile);
  1888. if (x div 100) <> 1 then
  1889. Exit;
  1890. FromFTP.Timeout := 21600000;
  1891. x := FromFTP.ReadResult;
  1892. if (x div 100) <> 2 then
  1893. Exit;
  1894. ToFTP.Timeout := 21600000;
  1895. x := ToFTP.ReadResult;
  1896. if (x div 100) <> 2 then
  1897. Exit;
  1898. Result := True;
  1899. finally
  1900. ToFTP.Free;
  1901. FromFTP.Free;
  1902. end;
  1903. end;
  1904. procedure TFTPList.ParseMLSDLines;
  1905. var
  1906. flr: TFTPListRec;
  1907. i: Integer;
  1908. s: string;
  1909. ye,mo,da,ho,mi,se: Word;
  1910. function GetPart(const ALine, AName: string): string;
  1911. var
  1912. i, j: Integer;
  1913. begin
  1914. i := Pos(AnsiUpperCase(AName), AnsiUpperCase(ALine));
  1915. i := i + Length(AName);
  1916. j := PosEx(';', ALine, i);
  1917. if j < 1 then j := MaxInt;
  1918. Result := Copy(ALine, i, j-i);
  1919. end;
  1920. begin
  1921. for i := 0 to Lines.Count - 1 do
  1922. begin
  1923. s := GetPart(Lines[i], 'modify=');
  1924. ye := StrToIntDef(Copy(s, 1, 4), 1970);
  1925. mo := StrToIntDef(Copy(s, 5, 2), 1);
  1926. da := StrToIntDef(Copy(s, 7, 2), 1);
  1927. ho := StrToIntDef(Copy(s, 9, 2), 0);
  1928. mi := StrToIntDef(Copy(s, 11, 2), 0);
  1929. se := StrToIntDef(Copy(s, 13, 2), 0);
  1930. flr := TFTPListRec.create;
  1931. flr.OriginalLine := Lines[i];
  1932. { osetrime kraviny, protoze autori FTP serveru nerespektuji RFC, tykajici se MLSD prikazu }
  1933. try
  1934. flr.FFileTime := EncodeDateTime(ye,mo,da,ho,mi,se, 0);
  1935. except
  1936. flr.FFileTime := EncodeDateTime(1970,1,1,0,0,0, 0);
  1937. end;
  1938. flr.FDirectory := AnsiSameText(GetPart(Lines[i], 'type='), 'dir') or AnsiSameText(GetPart(Lines[i], 'type='), 'cdir');
  1939. flr.FFileSize := StrToInt64Def(GetPart(Lines[i], 'size='), 0);
  1940. flr.FPermission := GetPart(Lines[i], 'mode=');
  1941. s := flr.FPermission;
  1942. flr.FFileName := GetPart(Lines[i], '; ');
  1943. Flist.Add(flr);
  1944. end;
  1945. end;
  1946. end.