IdFTPCommon.pas 91 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881
  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. }
  16. {
  17. Rev 1.40 3/3/2005 10:12:38 AM JPMugaas
  18. Fix for compiler warning about DotNET and ByteType.
  19. Rev 1.39 12/8/2004 10:38:40 AM JPMugaas
  20. Adjustment for PC-NFS. Time is returned with an "a" or "p" instead of AM or
  21. PM.
  22. Rev 1.38 11/24/2004 12:26:18 PM JPMugaas
  23. Removed dead code that caused a NET portability warning.
  24. Rev 1.37 11/22/2004 7:44:26 PM JPMugaas
  25. Modified IsYYMMDD to accept 2 digit years.
  26. Rev 1.35 10/27/2004 1:05:08 AM JPMugaas
  27. "SungDong Kim" <[email protected]> indicated a problem with Korean in
  28. IsTotalLine. He suggested specifically testing for multibyte characters.
  29. This is tentative.
  30. Rev 1.34 10/26/2004 9:19:12 PM JPMugaas
  31. Fixed references.
  32. Rev 1.33 9/7/2004 10:01:12 AM JPMugaas
  33. FIxed problem parsing:
  34. drwx------ 1 user group 0 Sep 07 09:20 xxx
  35. It was mistakenly being detected as Windows NT because there was a - in the
  36. fifth and eigth position in the string. The fix is to detect to see if the
  37. other chactors in thbat column are numbers.
  38. I did the same thing to the another part of the detection so that something
  39. similar doesn't happen there with "-" in Unix listings causing false
  40. WindowsNT detection.
  41. Rev 1.32 8/1/2004 1:07:36 AM JPMugaas
  42. Fix for XBox dir listing problem seen in Unix-xbox-MediaCenter.txt
  43. Rev 1.31 7/30/2004 5:50:54 AM JPMugaas
  44. Fix for UnquotedChar. It was returning nothing instead of what the string
  45. without quotes.
  46. Rev 1.30 7/29/2004 1:33:08 AM JPMugaas
  47. Reordered AUTH command values for a new property under development. This
  48. should make things more logical.
  49. Rev 1.29 6/29/2004 4:09:02 PM JPMugaas
  50. OPTS MODE Z now supported as per draft-preston-ftpext-deflate-02.txt. This
  51. should keep FTP Voyager 11 happy.
  52. Rev 1.28 6/17/2004 3:38:42 PM JPMugaas
  53. Removed Transfer Mode's dmBlock and dmCompressed since we did not support
  54. those at all.
  55. Rev 1.27 6/15/2004 7:18:58 PM JPMugaas
  56. Compiler defines removed.
  57. Rev 1.26 6/15/2004 6:35:30 PM JPMugaas
  58. Change in ZLib parameter values. Window Bits is now positive. We make it
  59. negative as part of a workaround and then upload with the ZLib headers.
  60. Rev 1.25 6/7/2004 3:47:50 PM JPMugaas
  61. VMS Recursive Dir listings now supported. This is done with a [...]. Note
  62. that VMS does have some strange syntaxes with their file system.
  63. Rev 1.24 6/5/2004 7:39:58 AM JPMugaas
  64. Exposes Posix constants because I need them for something else in my private
  65. work.
  66. Rev 1.23 6/4/2004 4:15:42 PM JPMugaas
  67. A ChModNumber conversion function wasn't returning anything.
  68. Added an overloaded function for cases where all of the permissions should be
  69. in one string (such as displaying in a ListView column).
  70. Rev 1.22 2/17/2004 12:25:38 PM JPMugaas
  71. The client now supports MODE Z (deflate) uploads and downloads as specified
  72. by http://www.ietf.org/internet-drafts/draft-preston-ftpext-deflate-00.txt
  73. Rev 1.21 2/12/2004 11:34:26 PM JPMugaas
  74. FTP Deflate preliminary support. Work still needs to be done for upload and
  75. downloading.
  76. Rev 1.20 2004.02.03 5:44:42 PM czhower
  77. Name changes
  78. Rev 1.19 2004.02.03 2:12:08 PM czhower
  79. $I path change
  80. Rev 1.18 2004.01.23 2:37:24 AM czhower
  81. DCCIL compile fix.
  82. Rev 1.17 2004.01.22 5:27:24 PM czhower
  83. Fixed compile errors.
  84. Rev 1.16 1/22/2004 4:16:46 PM SPerry
  85. fixed set problems
  86. Rev 1.15 1/19/2004 8:57:20 PM JPMugaas
  87. Rearranged functions to be in a more sensible way.
  88. Rev 1.14 1/19/2004 4:35:30 AM JPMugaas
  89. FTPDateTimeToMDTMD was created for converting a TDateTime into a time value
  90. for MDTM.
  91. MinutesFromGMT was moved from IdFTPServer because the client now may use it.
  92. Rev 1.13 1/17/2004 7:37:32 PM JPMugaas
  93. Removed some warnings.
  94. Rev 1.12 1/16/2004 12:23:52 AM JPMugaas
  95. New functions for MDTM set date functionality.
  96. Rev 1.11 10/26/2003 9:18:10 PM BGooijen
  97. Compiles in DotNet, and partially works there
  98. Rev 1.10 10/19/2003 1:11:06 PM DSiders
  99. Added localization comments.
  100. Rev 1.9 10/7/2003 05:46:34 AM JPMugaas
  101. SSCN Support added.
  102. Rev 1.8 10/1/2003 05:29:50 PM JPMugaas
  103. Y2KDate will now adjust date if there's 3 diigits instead of 4. This is
  104. required for the OS/2 FTP LIST parser.
  105. Rev 1.7 10/1/2003 12:57:12 AM JPMugaas
  106. Routines for Sterling Commerce FTP Server support.
  107. Rev 1.6 6/27/2003 06:06:50 AM JPMugaas
  108. Should now compile with the IsNumeric code move.
  109. Rev 1.5 3/12/2003 03:22:32 PM JPMugaas
  110. The FTP Server can now handle masks better including file extensions.
  111. Rev 1.4 2/24/2003 07:19:32 AM JPMugaas
  112. Added routine for determining if a Unix file is "hidden". This is determined
  113. by a "." starting a filename.
  114. Rev 1.3 2/19/2003 02:04:24 AM JPMugaas
  115. Added more routines from IdFTPList for the new framework.
  116. Rev 1.2 2/17/2003 04:43:38 PM JPMugaas
  117. TOPS20 support
  118. Rev 1.1 2/14/2003 05:41:36 PM JPMugaas
  119. Moved everything from IdFTPUtils to IdFTPCommon at Kudzu's suggestion.
  120. Rev 1.0 11/13/2002 08:28:38 AM JPMugaas
  121. Initial import from FTP VC.
  122. }
  123. unit IdFTPCommon;
  124. interface
  125. {$i IdCompilerDefines.inc}
  126. uses
  127. Classes,
  128. IdGlobal,
  129. IdGlobalProtocols,
  130. SysUtils
  131. // to facilite inlining
  132. {$IFNDEF HAS_GetLocalTimeOffset}
  133. {$IFDEF HAS_DateUtils_TTimeZone}
  134. ,{$IFDEF VCL_XE2_OR_ABOVE}System.TimeSpan{$ELSE}TimeSpan{$ENDIF}
  135. ,DateUtils
  136. {$ENDIF}
  137. {$ENDIF}
  138. ;
  139. type
  140. TIdFTPTransferType = (ftASCII, ftBinary);
  141. TIdFTPDataStructure = (dsFile, dsRecord, dsPage);
  142. //dmBlock, dmCompressed were removed because we don't use them and they aren't supported on most
  143. //FTP Servers anyway.
  144. TIdFTPTransferMode = (dmStream, dmDeflate); // (dmBlock, dmCompressed, dmStream, dmDeflate);
  145. {Note that some FTP extensions might use some data port protection values that
  146. are defined but not used. For memoment, I commented those out. Leave the comments
  147. in just in case someone may need those later }
  148. TIdFTPDataPortSecurity = ( ftpdpsClear, //'C' - Clear - neither Integrity nor Privacy
  149. //NOT USED - 'S' - Safe - Integrity without Privacy
  150. //NOT USED - 'E' - Confidential - Privacy without Integrity
  151. ftpdpsPrivate //'P' - Private - Integrity and Privacy
  152. );
  153. const
  154. DEF_DIRSEPARATOR = '/';
  155. DEF_CASE_SENSITIVE = True;
  156. type
  157. TIdFTPClientIdentifier = class (TPersistent)
  158. protected
  159. FClientName : String;
  160. FClientVersion : String;
  161. FClientVendor : String;
  162. FPlatformDescription : String;
  163. FExtraFacts: TStrings;
  164. function GetCLNTParams : String;
  165. function GetCSIDParams : String;
  166. procedure SetClientName(const AValue: String);
  167. procedure SetClientVersion(const AValue: String);
  168. procedure SetClientVendor(const AValue : String);
  169. procedure SetPlatformDescription(const AValue: String);
  170. procedure SetExtraFacts(const AValue: TStrings);
  171. procedure SetCLNTParams(const AValue : String);
  172. procedure SetCSIDParams(const AValue : String);
  173. public
  174. constructor Create;
  175. destructor Destroy; override;
  176. procedure Assign(Source: TPersistent); override;
  177. property CLNTParams : String read GetCLNTParams write SetCLNTParams;
  178. property CSIDParams : String read GetCSIDParams write SetCSIDParams;
  179. published
  180. property ClientName : String read FClientName write SetClientName;
  181. property ClientVersion : String read FClientVersion write SetClientVersion;
  182. property ClientVendor : String read FClientVendor write SetClientVendor;
  183. property PlatformDescription : String read FPlatformDescription write SetPlatformDescription;
  184. property ExtraFacts: TStrings read FExtraFacts write SetExtraFacts;
  185. end;
  186. TIdFTPServerIdentifier = class(TPersistent)
  187. protected
  188. FServerName : String;
  189. FServerVersion : String;
  190. FServerVendor : String;
  191. FPlatformName : String;
  192. FPlatformVersion : String;
  193. FCaseSensitive : Boolean;
  194. FDirSeparator : Char;
  195. FExtraFacts : TStrings;
  196. function GetCSIDParams: String;
  197. procedure SetCSIDParams(const AValue: String);
  198. procedure SetServerName(const AValue: String);
  199. procedure SetServerVersion(const AValue: String);
  200. procedure SetServerVendor(const AValue: String);
  201. procedure SetPlatformName(const AValue: String);
  202. procedure SetPlatformVersion(const AValue: String);
  203. procedure SetExtraFacts(const AValue: TStrings);
  204. public
  205. constructor Create;
  206. destructor Destroy; override;
  207. procedure Assign(Source: TPersistent); override;
  208. procedure Clear;
  209. property CSIDParams: String read GetCSIDParams write SetCSIDParams;
  210. published
  211. property ServerName : String read FServerName write SetServerName;
  212. property ServerVersion : String read FServerVersion write SetServerVersion;
  213. property ServerVendor : String read FServerVendor write SetServerVendor;
  214. property PlatformName : String read FPlatformName write SetPlatformName;
  215. property PlatformVersion : String read FPlatformVersion write SetPlatformVersion;
  216. property CaseSensitive : Boolean read FCaseSensitive write FCaseSensitive default False;
  217. property DirSeparator : Char read FDirSeparator write FDirSeparator default '/';
  218. property ExtraFacts : TStrings read FExtraFacts write SetExtraFacts;
  219. end;
  220. {From:
  221. http://www.ford-hutchinson.com/~fh-1-pfh/ftps-ext.html#bad
  222. }
  223. const
  224. TLS_AUTH_NAMES : Array [0..3] of string =
  225. ('TLS', {implies clear data port in some implementations} {Do not translate}
  226. 'SSL', {implies private data port in some implementations} {Do not translate}
  227. 'TLS-C', {implies clear data port in some implementations} {Do not translate}
  228. 'TLS-P'); {implies private data port in some implementations} {Do not translate}
  229. {
  230. We hard-code these path specifiers because they are used for specific servers
  231. irregardless of what the client's Operating system is. It's based on the server.
  232. }
  233. const
  234. // based on http://www.raidenftpd.com/kb/kb000000037.htm
  235. // entry in FEAT response indicating SSCN is supported
  236. SCCN_FEAT = 'SSCN'; {do not localize}
  237. // client method - SSL Connect
  238. // turn on SSCN client method in FTP Server - secure server-to-server transfer
  239. SSCN_ON = 'SSCN ON'; {do not localize}
  240. //server mthod - SSL Accept method
  241. // turn off SSCN client method in FTP Server - secure server-to-server transfer
  242. SSCN_OFF = 'SSCN OFF'; {do not localize}
  243. SSCN_OK_REPLY = 200;
  244. SSCN_ERR_NEGOTIATION_REPLY = 421;
  245. {
  246. VMS Stuff from http://www.djesys.com/vms/freevms/mentor/vms_path.html
  247. Path/filename separators, which could be different from path/subpath separators on
  248. some systems
  249. }
  250. const
  251. PATH_FILENAME_SEP_UNIX = '/';
  252. PATH_FILENAME_SEP_DOS = '\';
  253. PATH_FILENAME_SEP_VMS = ']';
  254. {dir/subdir separators}
  255. const
  256. PATH_SUBDIR_SEP_UNIX = PATH_FILENAME_SEP_UNIX;
  257. PATH_SUBDIR_SEP_DOS = PATH_FILENAME_SEP_DOS;
  258. PATH_SUBDIR_SEP_VMS = '.';
  259. {device/dir separator}
  260. const
  261. PATH_DEVICE_SEP_UNIX = ''; //Unix treats devices as part of one big hierarchy as part of the file system - leave emtpy
  262. PATH_DEVICE_SEP_DOS = ':';
  263. PATH_DEVICE_SEP_VMS = ':[';
  264. {
  265. sample VMS fully qualified filename:
  266. DKA0:[MYDIR.SUBDIR1.SUBDIR2]MYFILE.TXT;1
  267. Note VMS uses 39 chars for name and type
  268. valid chars are:
  269. letters A through Z
  270. numbers 0 through 9
  271. underscore ( _ )
  272. hyphen ( -)
  273. dollar sign ( $ )
  274. See: http://www.uh.edu/infotech/services/documentation/vms/v0505.html
  275. }
  276. { global file specification for all files }
  277. UNIX_ALL_FILES = '*';
  278. MS_DOS_ALL_FILES = '*.*';
  279. VMS_ALL_FILES = '*.*;*';
  280. CUR_DIR = '.';
  281. PARENT_DIR = '..';
  282. VMS_RELPATH_PREFIX = '[.';
  283. MS_DOS_CURDIR = CUR_DIR + PATH_FILENAME_SEP_DOS;
  284. UNIX_CURDIR = CUR_DIR + PATH_FILENAME_SEP_UNIX;
  285. UNIX_DIR_SIZE = 512;
  286. VMS_BLOCK_SIZE = 512;
  287. //1/1/1970 - EPL time stamps are based on this value
  288. const
  289. EPLF_BASE_DATE = 25569;
  290. const
  291. //Settings specified by
  292. // http://www.ietf.org/internet-drafts/draft-preston-ftpext-deflate-00.txt
  293. {
  294. DEF_ZLIB_COMP_LEVEL = 7;
  295. DEF_ZLIB_WINDOW_BITS = -15; //-15 - no extra headers
  296. DEF_ZLIB_MEM_LEVEL = 8;
  297. DEF_ZLIB_STRATAGY = 0; // - default
  298. }
  299. {
  300. Settings specified by
  301. //http://www1.ietf.org/internet-drafts/draft-preston-ftpext-deflate-02.txt
  302. //and for some compatibility with one version of Noisette Software Corporation's ShareIt
  303. //FTP Server
  304. }
  305. DEF_ZLIB_COMP_LEVEL = 7;
  306. DEF_ZLIB_WINDOW_BITS = 15; //-15 - no extra headers
  307. DEF_ZLIB_MEM_LEVEL = 8; // Z_DEFLATED
  308. DEF_ZLIB_STRATAGY = 0; //Z_DEFAULT_STRATEGY - default
  309. DEF_ZLIB_METHOD = 8; // Z_DEFLATED
  310. type
  311. TIdVSEPQDisposition = (
  312. IdPQAppendable,
  313. IdPQProcessAndDelete,
  314. IdPQHoldUntilReleased,
  315. IdPQProcessAndKeep,
  316. IdPQLeaveUntilReleased,
  317. IdPQErrorHoldUntilDK,
  318. IdPQGetOrErrorHoldUntilDK,
  319. IdPQJobProcessing,
  320. IdPQSpoolOutputToInputD,
  321. IdPQSurpressOutputSpooling,
  322. IdPQSpoolOutputToTape);
  323. const
  324. VSERootDirItemTypes : array [0..5] of String =
  325. ('<Directory>', {do not localize} // treat as dir
  326. '<VSE VTOC>', {do not localize} // treat as dir
  327. '<Library>', {do not localize} // treat as dir
  328. '<Power Queues>', {do not localize} // treat as dir
  329. '<VSAM Catalog>', {do not localize} // treat as dir
  330. 'Entry Seq VSAM'); {do not localize} // treat as file
  331. {From: http://groups.google.com/groups?q=MVS+JES+FTP+DIR+Output&hl=en&lr=&ie=UTF-8&oe=utf-8&selm=4qf4b8%246i7%40dsk92.itg.ti.com&rnum=1}
  332. MVS_JES_Status : array [0..3] of string =
  333. ('INPUT', {do not localize} //job received but not run yet
  334. 'HELD', {do not localize} //job is in hold status
  335. 'ACTIVE', {do not localize} //job is running
  336. 'OUTPUT'); {do not localize} //job has finished and has output available
  337. { Note from stame article:
  338. To retrieve the entire job issue the GET command with the .x:
  339. get j26494.x f:/job26494
  340. To retrieve only the third output file of your job:
  341. get j26494.3 f:job26494.3
  342. }
  343. { From:
  344. http://publibz.boulder.ibm.com:80/cgi-bin/bookmgr_OS390/BOOKS/IESPME20/A.0?DT=20010927093004#HDRDISPX
  345. }
  346. VSE_PowerQueue_Dispositions : array [1..11] of char = (
  347. 'A', {do not localize} // (Local only) Appendable. Spool data may be added to the job via spool-access support.
  348. 'D', {do not localize} // Process the job and delete it after processing. Default disposition.
  349. 'H', {do not localize} // Hold in queue until released.
  350. 'K', {do not localize} // Process the job and keep it in the queue after processing. (Default disposition for time event scheduling jobs that have to be processed more than once.)
  351. 'L', {do not localize} // Leave in queue until released.
  352. 'X', {do not localize} // (Local only) Hold until disposition is changed to D or K. Temporarily assigned by VSE/POWER when processing fails.
  353. 'Y', {do not localize} {
  354. (Local only) Hold until the disposition is changed to D or K. Applies only to
  355. output being retrieved via the GET service of the spool-access support.
  356. Assigned by VSE/POWER either on request by the retrieving program or, to
  357. certain queue entries, when processing fails.
  358. Output queue entries may have also been set to a disposition of Y when
  359. ignored records were found and SET IGNREC=DISPY was specified in the
  360. VSE/POWER autostart procedure.
  361. }
  362. '*', {do not localize} // Indicates that a queue entry is being processed.
  363. {
  364. The following local disposition codes may be specified for an output entry,
  365. but they are effective only while the entry is being created.
  366. }
  367. 'I', {do not localize} //Spool this output to the input (reader) queue with disposition D. Applies to punch output.
  368. 'N', {do not localize} //Suppress the spooling of the referenced output when the job entry is being processed.
  369. 'T' {do not localize} //Spool the referenced output to tape. Applies to output.
  370. {
  371. If a queue entry has a temporary local disposition of A, or X, or Y, VSE/POWER
  372. present the original disposition in the ORGDP=field of a PDISPLAY...,FULL=YES
  373. request.
  374. }
  375. );
  376. {TODO: Add method to TIdFTP to set dispositions for VSE Power Queue jobs if possible.
  377. I think it is done with a PALTER DISP=[disposition code] command but I'm not sure.
  378. }
  379. const
  380. UnitreeStoreTypes : array [0..1] of string =
  381. ('AR', 'DK'); {do not localize}
  382. const
  383. UNIX_LINKTO_SYM = ' -> '; {do not localize} //indicates where a symbolic link points to
  384. CDATE_PART_SEP = '/-'; {Do not localize}
  385. {***
  386. Path conversions
  387. ***}
  388. function UnixPathToDOSPath(const APath : String):String;
  389. function DOSPathToUnixPath(const APath : String):String;
  390. {***
  391. Indy path utility functions
  392. ***}
  393. //works like ExtractFilePath except that it will use both "/" and "\" and the last path spec is dropped
  394. function IndyGetFilePath(const AFileName : String):String;
  395. function IndyGetFileName(const AFileName : String):String;
  396. function IndyIsRelativePath(const APathName : String): Boolean;
  397. function IndyGetFileExt(const AFileName : String) : String;
  398. function StripInitPathDelim(const AStr : String): String;
  399. function IsNavPath(const APath : String): Boolean;
  400. function RemoveDuplicatePathSyms(APath : String): String;
  401. {***
  402. EPLF time stamp processing
  403. ***}
  404. function EPLFDateToLocalDateTime(const AData: String): TDateTIme;
  405. function EPLFDateToGMTDateTime(const AData: String): TDateTime;
  406. function GMTDateTimeToEPLFDate(const ADateTime : TDateTime) : String;
  407. function LocalDateTimeToEPLFDate(const ADateTime : TDateTime) : String;
  408. {***
  409. Misc parsing
  410. ***}
  411. function PatternsInStr(const ASearchPattern, AString : String): Integer;
  412. function StripSpaces(const AString : String; const ASpaces : UInt32): String;
  413. function StripPath(const AFileName : String; const APathDelim : String = '/'): String;
  414. function CharsInStr(const ASearchChar : Char; const AString : String) : Integer;
  415. function UnfoldLines(const AData : String; ALine : Integer; AStrings : TStrings): String;
  416. function StrPart(var AInput: string; const AMaxLength : Integer; const ADelete: Boolean = IdFetchDeleteDefault) : String;
  417. function FetchLength(var AInput: string;
  418. const AMaxLength : Integer;
  419. const ADelim: string = IdFetchDelimDefault;
  420. const ADelete: Boolean = IdFetchDeleteDefault;
  421. const ACaseSensitive: Boolean = IdFetchCaseSensitiveDefault): String;
  422. function IsLineStr(const AData : String): Boolean;
  423. {FTP Pattern recognition}
  424. function IsTotalLine(const AData: String): Boolean;
  425. function IsSubDirContentsBanner(const AData: String): Boolean;
  426. {***
  427. Quoted strings
  428. ***}
  429. procedure ParseQuotedArgs(const AParams : String; AStrings : TStrings);
  430. {**
  431. Number extraction
  432. **}
  433. function FindDelimInNumbers(const AData : String) : String;
  434. function ExtractNumber(const AData : String; const ARetZero : Boolean = True): Integer;
  435. function StripNo(const AData : String): String;
  436. {**
  437. Date parsing and processing
  438. **}
  439. function IsValidTimeStamp(const AString : String) : Boolean;
  440. function IsMDTMDate(const ADate : String) : Boolean;
  441. function IsDDMonthYY(const AData : String; const ADelim : String) : Boolean;
  442. function IsMMDDYY(const AData : String; const ADelim : String) : Boolean;
  443. function IsYYYYMMDD(const AData : String) : Boolean;
  444. function Y2Year(const AYear : Integer): Integer;
  445. function DateYYMMDD(const AData: String): TDateTime;
  446. function DateYYStrMonthDD(const AData: String; const ADelim : String='-'): TDateTime;
  447. function DateStrMonthDDYY(const AData:String; const ADelim : String = '-'; const AAddMissingYear : Boolean=False): TDateTime;
  448. function DateDDStrMonthYY(const AData: String; const ADelim : String='-'): TDateTime;
  449. function DateMMDDYY(const AData: String): TDateTime;
  450. function TimeHHMMSS(const AData : String):TDateTime;
  451. function IsIn6MonthWindow(const AMDate : TDateTime):Boolean;
  452. function AddMissingYear(const ADay, AMonth : UInt32): UInt32;
  453. function IsHHMMSS(const AData : String; const ADelim : String) : Boolean;
  454. //This assumes hours in the form 0-23 instead of the 12 AM/PM hour system used in the US.
  455. function MVSDate(const AData: String): TDateTime;
  456. function AS400Date(const AData: String): TDateTime;
  457. //MDTM Set filedate support and SITE ZONE support
  458. function MinutesFromGMT : Integer;
  459. function MDTMOffset(const AOffs : String) : TDateTime;
  460. function FTPDateTimeToMDTMD(const ATimeStamp : TDateTime; const AIncludeMSecs : Boolean=True; const AIncludeGMTOffset : Boolean=True ): String;
  461. function FTPMDTMToGMTDateTime(const ATimeStamp : String):TDateTime;
  462. {***
  463. platform specific parsing and testing
  464. ***}
  465. {Unix}
  466. function IsValidUnixPerms(AData : String; const AStrict : Boolean = False) : Boolean;
  467. function IsUnixLsErr(const AData: String): Boolean;
  468. function IsUnixExec(const LUPer, LGPer, LOPer : String): Boolean;
  469. function IsUnixHiddenFile(const AFileName : String): Boolean;
  470. //Chmod converstion routines
  471. procedure ChmodNoToPerms(const AChmodNo : Integer; var VUser, VGroup, VOther : String); overload;
  472. procedure ChmodNoToPerms(const AChmodNo : Integer; var VPermissions : String); overload;
  473. function PermsToChmodNo(const AUser, AGroup, AOther : String): Integer;
  474. function ChmodNoToModeBits(const AModVal : UInt32): UInt32;
  475. function ModeBitsToChmodNo(const AMode : UInt32): Integer;
  476. function ModeBitsToPermString(const AMode : UInt32) : String;
  477. function PermStringToModeBits(const APerms : String): UInt32;
  478. {Novell Netware}
  479. function IsNovelPSPattern(const AStr : String): Boolean;
  480. function IsValidNovellPermissionStr(const AStr : String): Boolean;
  481. function ExtractNovellPerms(const AData : String) : String;
  482. {QVT/NET}
  483. function ExcludeQVNET(const AData : String) : Boolean;
  484. function ExtractQVNETFileName(const AData : String): String;
  485. {Mainframe support}
  486. function ExtractRecFormat(const ARecFM : String): String;
  487. //Determines if the line is part of a VM/BFS list - also used by WindowsNT parser
  488. //because two columns are shared
  489. function IsVMBFS(AData : String) : Boolean;
  490. {IBM VSE}
  491. function DispositionCodeToTIdVSEPQDisposition(const ADisp : Char) : TIdVSEPQDisposition;
  492. function TIdVSEPQDispositionDispositionCode(const ADisp : TIdVSEPQDisposition) : Char;
  493. {EPLF and MLST/MLSD support}
  494. function ParseFacts(AData : String; AResults : TStrings;
  495. const AFactDelim : String = ';'; const ANameDelim : String=' '): String;
  496. function ParseFactsMLS(AData : String; AResults : TStrings;
  497. const AFactDelim : String = ';'; const ANameDelim : String = ' '): String;
  498. {Sterling Commerce support routines}
  499. function IsValidSterCommFlags(const AString : String) : Boolean;
  500. function IsValidSterCommProt(const AString : String) : Boolean;
  501. function IsValidSterCommData(const AString : String) : Boolean;
  502. //These are from Borland's LIBC.pas header file
  503. //We rename the constants to prevent any conflicts in Kylix and C++
  504. const
  505. Id__S_ISUID = $800; { Set user ID on execution. }
  506. Id__S_ISGID = $400; { Set group ID on execution. }
  507. Id__S_ISVTX = $200; { Save swapped text after use (sticky). }
  508. Id__S_IREAD = $100; { Read by owner. }
  509. Id__S_IWRITE = $80; { Write by owner. }
  510. Id__S_IEXEC = $40; { Execute by owner. }
  511. { Protection bits. }
  512. IdS_ISUID = Id__S_ISUID; { Set user ID on execution. }
  513. IdS_ISGID = Id__S_ISGID; { Set group ID on execution. }
  514. { Save swapped text after use (sticky bit). This is pretty well obsolete. }
  515. IdS_ISVTX = Id__S_ISVTX;
  516. IdS_IRUSR = Id__S_IREAD; { Read by owner. }
  517. IdS_IWUSR = Id__S_IWRITE; { Write by owner. }
  518. IdS_IXUSR = Id__S_IEXEC; { Execute by owner. }
  519. { Read, write, and execute by owner. }
  520. IdS_IRWXU = Id__S_IREAD or Id__S_IWRITE or Id__S_IEXEC;
  521. IdS_IREAD = IdS_IRUSR;
  522. IdS_IWRITE = IdS_IWUSR;
  523. IdS_IEXEC = IdS_IXUSR;
  524. IdS_IRGRP = IdS_IRUSR shr 3; { Read by group. }
  525. IdS_IWGRP = IdS_IWUSR shr 3; { Write by group. }
  526. IdS_IXGRP = IdS_IXUSR shr 3; { Execute by group. }
  527. { Read, write, and execute by group. }
  528. IdS_IRWXG = IdS_IRWXU shr 3;
  529. IdS_IROTH = IdS_IRGRP shr 3; { Read by others. }
  530. IdS_IWOTH = IdS_IWGRP shr 3; { Write by others. }
  531. IdS_IXOTH = IdS_IXGRP shr 3; { Execute by others. }
  532. { Read, write, and execute by others. }
  533. IdS_IRWXO = IdS_IRWXG shr 3;
  534. {Some stuff for internationalization provided by Craig Peterson}
  535. const
  536. {$IFDEF STRING_IS_ANSI}
  537. // These are the CJK "month", "day", and "year" characters, which appear after
  538. // a number in the listings. Constants are UTF-8. According to
  539. // www.FileFormat.info the characters for KoreanTotal, KoreanMonth, and
  540. // KoreanDay aren't valid Unicode, but that's what appears in the listing.
  541. KoreanTotal = #$EC#$B4#$9D; // #$CD1D
  542. KoreanMonth = #$EC#$9B#$94; // #$C6D4 Hangul Syllable Ieung Weo Rieul
  543. KoreanDay = #$EC#$9D#$BC; // #$C77C Hangul Syllable Ieung I Rieul
  544. KoreanYear = #$EB#$85#$84; // #$B144 Hangul Syllable Nieun Yeo Nieun
  545. KoreanEUCMonth = #$EB#$BF#$B9; //#$BFF9
  546. ChineseTotal = #$E6#$80#$BB + #$E6#$95#$B0;
  547. // #$603B CJK Unified Ideograph Collect/Overall +
  548. // #$6570 CJK Unified Ideograph Number/Several/Count
  549. ChineseMonth = #$E6#$9C#$88; // #$6708 CJK Unified Ideograph Month
  550. ChineseDay = #$E6#$97#$A5; // #$65E5 CJK Unified Ideograph Day
  551. ChineseYear = #$E5#$B9#$B4; // #$5E74 CJK Unified Ideograph Year
  552. JapaneseTotal = #$E5#$90#$88 + #$E8#$A8#$88;
  553. //@$5408
  554. //
  555. JapaneseMonth = #$E8#$B2#$8E; // #$8c8e Japanse Month symbol
  556. JapaneseDay = #$E9#$8F#$BA; //93fa - Japanese Day Symbol - not valid Unicode
  557. JapaneseYear = #$E9#$91#$8E; //944e - Japanese Year symbol = not valid Unicode
  558. {$ELSE}
  559. //These are in Unicode since the parsers receive data in Unicode form
  560. KoreanTotal = #$CD1D; // #$CD1D
  561. KoreanMonth = #$C6D4; // #$C6D4 Hangul Syllable Ieung Weo Rieul
  562. KoreanDay = #$C77C; // #$C77C Hangul Syllable Ieung I Rieul
  563. KoreanEUCMonth = #$BFF9; // #$BFF9 EUC-KR Same as #$C6#$D4
  564. KoreanYear = #$B144; // #$B144 Hangul Syllable Nieun Yeo Nieun
  565. ChineseTotal = #$603B + #$6570;
  566. // #$603B CJK Unified Ideograph Collect/Overall +
  567. // #$6570 CJK Unified Ideograph Number/Several/Count
  568. ChineseMonth = #$6708; // #$6708 CJK Unified Ideograph Month
  569. ChineseDay = #$65E5; // #$65E5 CJK Unified Ideograph Day
  570. ChineseYear = #$5E74; // #$5E74 CJK Unified Ideograph Year
  571. JapaneseTotal = #$5408 + #$8A08;
  572. //#$5408
  573. //#$8a08
  574. JapaneseMonth = #$8C8E; // #$8c8e Japanse Day symbol
  575. JapaneseDay = #$93FA; //93fa - Japanese Day Symbol - not valid Unicode
  576. JapaneseYear = #$944E; //944e - Japanese Year symbol = not valid Unicode
  577. {$ENDIF}
  578. procedure DeleteSuffix(var VStr : String; const ASuffix : String); {$IFDEF USE_INLINE}inline;{$ENDIF}
  579. //WS_FTP Pro XAUT Support
  580. {
  581. Note that the XAUT Support is from a file located at:
  582. http://72.32.12.210/archives/fulldisclosure/2004-03/att-1088/xp_ws_ftp_server.zip
  583. (c)2004 Hugh Mann [email protected]
  584. The code itself is designed to show a buffer overflow in a version of WS_FTP Server.
  585. I only translated the XAUT logic from that code into Pascal for use in Indy. This
  586. will not exploit any known flaw in the server.
  587. I did verify that this works with "X2 WS_FTP Server 6.1.1".
  588. }
  589. function ExtractWSFTPServerKey(const AGreeting : String; var VKey : UInt32) : Boolean;
  590. procedure xaut_encrypt(var VDest : TIdBytes; const ASrc : TIdBytes; const AKey : UInt32);
  591. procedure xaut_unpack(var VDest : String; const ASrc : TIdBytes);
  592. procedure xaut_pack(var VDst : TIdBytes; const ASrc : String);
  593. function MakeXAUTCmd(const AGreeting, AUsername, APassword : String; const Ad : UInt32 = 2) : String;
  594. function ExtractAutInfoFromXAUT(const AXAutStr : String; const AKey : UInt32) : String;
  595. function MakeXAUTKey : UInt32;
  596. const
  597. XAUT_2_KEY = $49327576;
  598. //end XAUT Stuff
  599. implementation
  600. {$IFDEF USE_VCL_POSIX}
  601. uses
  602. Posix.SysTime,
  603. Posix.Time;
  604. {$ENDIF}
  605. {WS_FTP Pro XAUT Support}
  606. function ExtractWSFTPServerKey(const AGreeting : String; var VKey : UInt32) : Boolean;
  607. {$IFDEF USE_INLINE} inline; {$ENDIF}
  608. var
  609. LBuf : String;
  610. begin
  611. Result := False;
  612. if IndyPos('WS_FTP Server', AGreeting) > 0 then begin {Do not localize}
  613. LBuf := AGreeting;
  614. Fetch(LBuf, '('); {do not localize}
  615. LBuf := Fetch(LBuf, ')'); {do not localize}
  616. if IsNumeric(LBuf) then begin
  617. VKey := UInt32(IndyStrToInt64(LBuf, 0));
  618. Result := True;
  619. end;
  620. end;
  621. end;
  622. procedure xaut_encrypt(var VDest : TIdBytes; const ASrc : TIdBytes; const AKey : UInt32);
  623. {$IFDEF USE_INLINE} inline; {$ENDIF}
  624. var
  625. LBuf : TIdBytes;
  626. i, l : Integer;
  627. begin
  628. SetLength(LBuf,4);
  629. LBuf[0] := AKey and $FF;
  630. LBuf[1] := (AKey shr 8) and $FF;
  631. LBuf[2] := (AKey shr 16) and $FF;
  632. LBuf[3] := (AKey shr 24) and $FF;
  633. l := Length(ASrc);
  634. SetLength(VDest,l);
  635. for i := 0 to l - 1 do begin
  636. VDest[i] := ASrc[i] xor LBuf[i mod 4];
  637. end;
  638. end;
  639. procedure xaut_unpack(var VDest : String; const ASrc : TIdBytes);
  640. {$IFDEF USE_INLINE} inline; {$ENDIF}
  641. var
  642. i, l : Integer;
  643. LBuf : TIdBytes;
  644. begin
  645. l := Length(ASrc);
  646. SetLength(LBuf, l * 2);
  647. for i := 0 to l-1 do begin
  648. // dest[i*2+0] = ((src[i] >> 4) & 0x0F) + 0x35;
  649. LBuf[(i*2)] := ((ASrc[i] shr 4) and $0F) + $35;
  650. //dst[i*2+1] = (src[i] & 0x0F) + 0x31;
  651. LBuf[(i*2)+1] := ((ASrc[i] and $0F) + $31);
  652. end;
  653. VDest := BytesToString(LBuf);
  654. end;
  655. procedure xaut_pack(var VDst : TIdBytes; const ASrc : String);
  656. {$IFDEF USE_INLINE} inline; {$ENDIF}
  657. var
  658. i, l : Integer;
  659. LSrc : TIdBytes;
  660. begin
  661. LSrc := ToBytes(ASrc);
  662. Assert(Length(LSrc) = Length(ASrc),'both LSRC and ASRC must be identical.');
  663. l := Length(LSrc) div 2;
  664. SetLength(VDst,l);
  665. for i := 0 to l - 1 do begin
  666. VDst[i] := (((LSrc[ (i * 2)] - $35) shl 4) + (LSrc[ (i * 2)+1] - $31));
  667. end;
  668. end;
  669. function MakeXAUTCmd(const AGreeting, AUsername, APassword : String; const Ad : UInt32 = 2) : String;
  670. {$IFDEF USE_INLINE} inline; {$ENDIF}
  671. var
  672. LKey : UInt32;
  673. LDst : TIdBytes;
  674. begin
  675. Result := '';
  676. if ExtractWSFTPServerKey(AGreeting, LKey) then begin
  677. LDst := ToBytes(AUsername+':'+APassword);
  678. if Ad = 2 then begin
  679. xaut_encrypt(LDst, LDst, XAUT_2_KEY);
  680. end;
  681. xaut_encrypt(LDst, LDst, LKey);
  682. // LCmd := 'XAUT 2 '+
  683. xaut_unpack(Result, LDst);
  684. Result := 'XAUT ' + IntToStr(Ad) + ' ' + Result;
  685. end;
  686. end;
  687. function ExtractAutInfoFromXAUT(const AXAutStr : String; const AKey : UInt32) : String;
  688. {$IFDEF USE_INLINE} inline; {$ENDIF}
  689. var
  690. LBuf : TIdBytes;
  691. LNum : UInt32; //first param
  692. begin
  693. Result := AXAutStr;
  694. LNum := UInt32(IndyStrToInt64(Fetch(Result), 0));
  695. xaut_pack(LBuf, Result);
  696. xaut_encrypt(LBuf, LBuf, AKey);
  697. if LNum = 2 then begin
  698. xaut_encrypt(LBuf, LBuf, XAUT_2_KEY);
  699. end;
  700. Result := BytesToString(LBuf);
  701. end;
  702. function MakeXAUTKey : UInt32;
  703. {$IFDEF USE_INLINE} inline; {$ENDIF}
  704. begin
  705. Randomize;
  706. repeat
  707. //we probably should avoid numbers that use the high bit to prevent them
  708. //from being expressed negatively and because I'm not sure what integer
  709. //type other programs us.
  710. Result := (UInt32(Random($7F)) shl 24) or
  711. (UInt32(Random($FF)) shl 16) or
  712. (UInt32(Random($FF)) shl 8) or
  713. UInt32(Random($FF));
  714. until (Result <> XAUT_2_KEY ) and (Result <> 0)
  715. end;
  716. {Misc Parsing}
  717. procedure DeleteSuffix(var VStr : String; const ASuffix : String);
  718. {$IFDEF USE_INLINE} inline; {$ENDIF}
  719. begin
  720. if IndyPos(ASuffix, VStr) = Length(VStr) - Length(ASuffix) + 1 then begin
  721. Delete(VStr, Length(VStr) - Length(ASuffix) + 1, Length(ASuffix));
  722. end;
  723. end;
  724. function StripSpaces(const AString : String; const ASpaces : UInt32): String;
  725. {$IFDEF USE_INLINE} inline; {$ENDIF}
  726. var
  727. i : Integer;
  728. L: UInt32;
  729. begin
  730. L := IndyMin(ASpaces, Length(AString));
  731. for i := 1 to L do begin
  732. if AString[i] <> ' ' then begin
  733. Break;
  734. end;
  735. end;
  736. if i > 1 then begin
  737. Result := Copy(AString, i, MaxInt);
  738. end else begin
  739. Result := AString;
  740. end;
  741. end;
  742. function StripPath(const AFileName : String; const APathDelim : String = '/'): String;
  743. {$IFDEF USE_INLINE} inline; {$ENDIF}
  744. var
  745. LBuf : String;
  746. begin
  747. LBuf := AFileName;
  748. repeat
  749. Result := Fetch(LBuf, APathDelim);
  750. until LBuf = '';
  751. end;
  752. function CharsInStr(const ASearchChar : Char; const AString : String) : Integer;
  753. {$IFDEF USE_INLINE} inline; {$ENDIF}
  754. var
  755. i : Integer;
  756. begin
  757. Result := 0;
  758. for i := 1 to Length(AString) do begin
  759. if AString[i] = ASearchChar then begin
  760. Inc(Result);
  761. end;
  762. end;
  763. end;
  764. function PatternsInStr(const ASearchPattern, AString : String): Integer;
  765. {$IFDEF USE_INLINE} inline; {$ENDIF}
  766. var
  767. LBuf : String;
  768. begin
  769. Result := 0;
  770. LBuf := AString;
  771. repeat
  772. Fetch(LBuf, ASearchPattern);
  773. if LBuf = '' then begin
  774. Break;
  775. end else begin
  776. Inc(Result);
  777. end;
  778. until False;
  779. end;
  780. function UnfoldLines(const AData : String; ALine : Integer; AStrings : TStrings): String;
  781. {$IFDEF USE_INLINE} inline; {$ENDIF}
  782. var
  783. LFoldedLine : String;
  784. begin
  785. Result := AData;
  786. repeat
  787. Inc(ALine);
  788. if ALine = AStrings.Count then begin
  789. Break;
  790. end;
  791. LFoldedLine := AStrings[ALine];
  792. if LFoldedLine = '' then begin
  793. Exit;
  794. end;
  795. if not CharIsInSet(LFoldedLine, 1, LWS) then begin
  796. Break;
  797. end;
  798. Result := Trim(Result) + ' ' + Trim(LFoldedLine); {Do not Localize}
  799. until False;
  800. end;
  801. function StrPart(var AInput: string; const AMaxLength : Integer; const ADelete: Boolean = IdFetchDeleteDefault) : String;
  802. {$IFDEF USE_INLINE} inline; {$ENDIF}
  803. begin
  804. Result := Copy(AInput, 1, AMaxLength);
  805. if ADelete then begin
  806. Delete(AInput, 1, AMaxLength);
  807. end;
  808. end;
  809. function FetchLength(var AInput: string; const AMaxLength : Integer; const ADelim: string = IdFetchDelimDefault;
  810. const ADelete: Boolean = IdFetchDeleteDefault; const ACaseSensitive: Boolean = IdFetchCaseSensitiveDefault): String;
  811. {$IFDEF USE_INLINE} inline; {$ENDIF}
  812. var
  813. i : Integer;
  814. begin
  815. if ADelim = #0 then begin
  816. // AnsiPos does not work with #0
  817. i := Pos(ADelim, AInput);
  818. end else begin
  819. i := IndyPos(ADelim, AInput);
  820. end;
  821. if (i > AMaxLength) or (i = 0) then begin
  822. Result := Copy(AInput, 1, AMaxLength);
  823. if ADelete then begin
  824. Delete(AInput, 1, AMaxLength);
  825. end;
  826. end else begin
  827. Result := Fetch(AInput, ADelim, ADelete, ACaseSensitive);
  828. end;
  829. end;
  830. function IsLineStr(const AData : String): Boolean;
  831. //see if this is just a line with spaces, '-', or tabs so we
  832. //can skip it in the parser
  833. const
  834. //Note that there are two separate char codes are rended as '-' in the line below.
  835. //Be careful when editing because the codes are different.
  836. // LineSet = [' ','-','–','+']; {Do not Localize}
  837. // RLebeau 1/7/09: using Char() for #128-#255 because in D2009, the compiler
  838. // may change characters >= #128 from their Ansi codepage value to their true
  839. // Unicode codepoint value, depending on the codepage used for the source code.
  840. // For instance, #128 may become #$20AC...
  841. LineSet = ' -'+Char($96)+'+'; //BGO: for DotNet, what to do with this {Do not Localize}
  842. var
  843. i: Integer;
  844. LLen: Integer;
  845. Begin
  846. LLen := Length(AData);
  847. if LLen > 0 then begin
  848. Result := True; //only white
  849. for i := 1 to LLen do begin
  850. if not CharIsInSet(AData, i, LineSet) then begin
  851. Result := False;
  852. Exit;
  853. end;
  854. end;
  855. end else begin
  856. Result := True; //empty
  857. end;
  858. end;
  859. {Number extraction}
  860. function FindDelimInNumbers(const AData : String) : String;
  861. {$IFDEF USE_INLINE} inline; {$ENDIF}
  862. var
  863. i : Integer;
  864. begin
  865. Result := '';
  866. for i := 1 to Length(AData) do begin
  867. if not IsNumeric(AData[i]) then begin
  868. Result := AData[i];
  869. Exit;
  870. end;
  871. end;
  872. end;
  873. function ExtractNumber(const AData : String; const ARetZero : Boolean = True): Integer;
  874. {$IFDEF USE_INLINE} inline; {$ENDIF}
  875. var
  876. i : Integer;
  877. LBuf : String;
  878. begin
  879. LBuf := '';
  880. for i := 1 to Length(AData) do begin
  881. if IsNumeric(AData[i]) then begin
  882. LBuf := LBuf + AData[i];
  883. end
  884. else if AData[i] <> ',' then begin
  885. Break;
  886. end;
  887. end;
  888. if ARetZero then begin
  889. Result := IndyStrToInt(LBuf, 0);
  890. end else begin
  891. Result := IndyStrToInt(LBuf, -1);
  892. end;
  893. end;
  894. function StripNo(const AData : String): String;
  895. {$IFDEF USE_INLINE} inline; {$ENDIF}
  896. var
  897. i : Integer;
  898. LPos : Integer;
  899. begin
  900. LPos := 1;
  901. for i := 1 to Length(AData) do begin
  902. LPos := i;
  903. if (not IsNumeric(AData[i])) and (not CharEquals(AData, i, ',')) then begin
  904. Break;
  905. end;
  906. end;
  907. Result := Copy(AData, LPos, Length(AData));
  908. end;
  909. {Path processing}
  910. {
  911. Note that for our purposes, Borland's comporable routines
  912. are inadiquate because they always assume the standard system
  913. path separators. In Win32, the routines use '\' instead of '/' and
  914. likewise, in Linux, the routines use '/' instead of '\'. We need to
  915. use both separators because we need to handle both for crossplatform
  916. client/server work.
  917. }
  918. function LastPathDelim(const APath : String):Integer;
  919. {$IFDEF USE_INLINE} inline; {$ENDIF}
  920. var
  921. i : Integer;
  922. begin
  923. for i := Length(APath) downto 1 do begin
  924. if CharIsInSet(APath, i, PATH_FILENAME_SEP_DOS + PATH_FILENAME_SEP_UNIX) then begin
  925. Result := i;
  926. Exit;
  927. end;
  928. end;
  929. Result := 0;
  930. end;
  931. function IndyGetFilePath(const AFileName : String):String;
  932. {$IFDEF USE_INLINE} inline; {$ENDIF}
  933. var
  934. i : Integer;
  935. begin
  936. i := LastPathDelim(AFileName);
  937. if i > 0 then begin
  938. Result := Copy(AFileName, 1, i-1);
  939. end else begin
  940. Result := '';
  941. end;
  942. end;
  943. function IndyGetFileName(const AFileName : String):String;
  944. {$IFDEF USE_INLINE} inline; {$ENDIF}
  945. var
  946. i : Integer;
  947. begin
  948. i := LastPathDelim(AFileName);
  949. if i = 0 then begin
  950. Result := AFileName;
  951. end else begin
  952. Result := Copy(AFileName, i+1, Length(AFileName));
  953. end;
  954. end;
  955. function IndyIsRelativePath(const APathName : String): Boolean;
  956. {$IFDEF USE_INLINE} inline; {$ENDIF}
  957. begin
  958. if APathName <> '' then begin
  959. Result := CharIsInSet(APathName, 1, PATH_SUBDIR_SEP_UNIX + PATH_SUBDIR_SEP_DOS);
  960. end else begin
  961. Result := False;
  962. end;
  963. end;
  964. function IndyGetFileExt(const AFileName : String) : String;
  965. {$IFDEF USE_INLINE} inline; {$ENDIF}
  966. {
  967. Borland's ExtractFileExtension routine is not adiquate in some cases
  968. because it assumes that there will only be one extension. Some files
  969. have two extensions such as Linux tarballs, ".tar.gz".
  970. With a file name such as test.tar.gz, Borland's routine returns .gz
  971. instead of .tar.gz
  972. Sometimes, in order to shoot yourself in the foot, you have to reinvent the
  973. gun, the bullet, and your foot :-).
  974. }
  975. var
  976. LBuf : String;
  977. LPos : Integer;
  978. begin
  979. Result := '';
  980. LBuf := IndyGetFileName(AFileName);
  981. LPos := IndyPos('.', LBuf);
  982. if LPos > 0 then begin
  983. Result := Copy(LBuf, LPos, MaxInt);
  984. end;
  985. end;
  986. function StripInitPathDelim(const AStr : String): String;
  987. {$IFDEF USE_INLINE} inline; {$ENDIF}
  988. begin
  989. Result := AStr;
  990. if Result <> '' then begin
  991. //strip off any beggining / or \
  992. if CharIsInSet(Result, 1, PATH_FILENAME_SEP_UNIX + PATH_FILENAME_SEP_DOS) then begin
  993. IdDelete(Result, 1, 1);
  994. end;
  995. end;
  996. end;
  997. function IsNavPath(const APath : String): Boolean;
  998. {$IFDEF USE_INLINE} inline; {$ENDIF}
  999. var
  1000. LTmp : String;
  1001. begin
  1002. LTmp := IndyGetFileName(StripInitPathDelim(APath));
  1003. Result := (LTmp = CUR_DIR) or (LTmp = PARENT_DIR);
  1004. end;
  1005. // RLebeau 10/26/09: RemoveDuplicatePathSyms() cannot be inlined if it uses
  1006. // the const variables declared outside of it, as they are private to this unit
  1007. // and not accessible during inlining!
  1008. {
  1009. const
  1010. TrailingPathCorrectionOrg : array [0..3] of string =
  1011. ('//','\\','/\','\/');
  1012. TrailingPathCorrectionNew : array [0..3] of string =
  1013. ('/','\','/','/');
  1014. }
  1015. function RemoveDuplicatePathSyms(APath : String): String;
  1016. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1017. begin
  1018. //Result := StringsReplace(APath, TrailingPathCorrectionOrg, TrailingPathCorrectionNew);
  1019. Result := StringsReplace(APath, ['//','\\','/\','\/'], ['/','\','/','/']); {do not localize}
  1020. end;
  1021. {Path conversion}
  1022. function UnixPathToDOSPath(const APath : String):String;
  1023. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1024. begin
  1025. Result := ReplaceAll(APath, PATH_SUBDIR_SEP_UNIX, PATH_SUBDIR_SEP_DOS);
  1026. end;
  1027. function DOSPathToUnixPath(const APath : String):String;
  1028. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1029. begin
  1030. Result := ReplaceAll(APath, PATH_SUBDIR_SEP_DOS, PATH_SUBDIR_SEP_UNIX);
  1031. end;
  1032. {Pattern recognition}
  1033. function IsSubDirContentsBanner(const AData: String): Boolean;
  1034. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1035. begin
  1036. //A line ending in : might be a standard Unix list item where the filename
  1037. //ends with a ":". Unix-xbox-MediaCenter.txt is an example.
  1038. Result := TextEndsWith(AData, ':') and (not IsValidUnixPerms(AData));
  1039. end;
  1040. function IsTotalLine(const AData: String): Boolean;
  1041. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1042. begin
  1043. //just in case someone is doing a recursive listing and there's a dir with the name total
  1044. Result := (not TextEndsWith(AData, ':')) and
  1045. (TextStartsWith(AData, 'TOTAL') or
  1046. TextStartsWith(AData, 'GESAMT') or // German
  1047. TextStartsWith(AData, 'INSGESAMT') or // German HPUX
  1048. (IndyPos(KoreanTotal, AData) = 1) or // Korean (Unicode)
  1049. (IndyPos(ChineseTotal, AData) = 1) or // Chinese (Unicode)
  1050. TextStartsWith(AData, JapaneseTotal));
  1051. end;
  1052. {Quoted strings}
  1053. procedure ParseQuotedArgs(const AParams : String; AStrings : TStrings);
  1054. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1055. var
  1056. lComma, LOpenQuote : Integer;
  1057. LBuf : String;
  1058. LArg : String;
  1059. //filename.ext
  1060. //"../SomeDir/A ,File.txt", filename.ext
  1061. //filename.ext, ".."
  1062. begin
  1063. AStrings.BeginUpdate;
  1064. try
  1065. AStrings.Clear;
  1066. LBuf := AParams;
  1067. repeat
  1068. if LBuf = '' then begin
  1069. Break;
  1070. end;
  1071. lComma := IndyPos(',', LBuf);
  1072. LOpenQuote := IndyPos('"', LBuf);
  1073. if LComma = 0 then begin
  1074. LComma := Length(LBuf);
  1075. end;
  1076. if (LOpenQuote = 0) or (LComma < LOpenQuote) then begin
  1077. LArg := TrimLeft(Fetch(LBuf,','));
  1078. end else begin
  1079. Fetch(LBuf,'"');
  1080. LArg := '"' + Fetch(LBuf,'"') + '"';
  1081. end;
  1082. if LArg <> '' then begin
  1083. AStrings.Add(LArg);
  1084. end;
  1085. until False;
  1086. finally
  1087. AStrings.EndUpdate;
  1088. end;
  1089. end;
  1090. {$IFNDEF HAS_TryEncodeDate}
  1091. // TODO: move this to IdGlobal or IdGlobalProtocols...
  1092. function TryEncodeDate(Year, Month, Day: Word; out VDate: TDateTime): Boolean;
  1093. begin
  1094. try
  1095. VDate := EncodeDate(Year, Month, Day);
  1096. Result := True;
  1097. except
  1098. Result := False;
  1099. end;
  1100. end;
  1101. {$ENDIF}
  1102. {EPLF Date processing}
  1103. function EPLFDateToLocalDateTime(const AData: String): TDateTime;
  1104. {note - code stolen from TIdTime and modified for our needs.}
  1105. const
  1106. BASE_DATE = 25569; //Jan 1, 1970
  1107. var
  1108. LSecs : Int64;
  1109. begin
  1110. LSecs := IndyStrToInt(AData);
  1111. Result := UTCTimeToLocalTime( Extended( ((LSecs)/ (24 * 60 * 60) ) + Int(BASE_DATE)) );
  1112. end;
  1113. function EPLFDateToGMTDateTime(const AData: String): TDateTime;
  1114. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1115. {note - code stolen from TIdTime and modified for our needs.}
  1116. var
  1117. LSecs : Int64;
  1118. begin
  1119. LSecs := IndyStrToInt(AData);
  1120. Result := Extended( ((LSecs)/ (24 * 60 * 60) ) + Int(EPLF_BASE_DATE));
  1121. end;
  1122. function GMTDateTimeToEPLFDate(const ADateTime : TDateTime) : String;
  1123. const
  1124. BASE_DATE = 25569;
  1125. begin
  1126. Result := FloatToStr( Extended(ADateTime - Int(BASE_DATE)) * 24 * 60 * 60);
  1127. end;
  1128. function LocalDateTimeToEPLFDate(const ADateTime : TDateTime) : String;
  1129. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1130. begin
  1131. Result := FloatToStr( Extended( LocalTimeToUTCTime(ADateTime) - Int(EPLF_BASE_DATE)) * 24 * 60 * 60);
  1132. end;
  1133. {Date routines}
  1134. function IsValidTimeStamp(const AString : String) : Boolean;
  1135. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1136. var
  1137. LMonth, LDay, LHour, LMin, LSec : Integer;
  1138. begin
  1139. Result := False;
  1140. // 1234 56 78 90 12 34
  1141. // ---------- ---------
  1142. // 1998 11 07 08 52 15
  1143. LMonth := IndyStrToInt(Copy(AString, 5, 2), 0);
  1144. if (LMonth < 1) or (LMonth > 12) then begin
  1145. Exit;
  1146. end;
  1147. LDay := IndyStrToInt(Copy(AString, 7, 2), 0);
  1148. if (LDay < 1) or (LDay > 31) then begin
  1149. Exit;
  1150. end;
  1151. LHour := IndyStrToInt(Copy(AString, 9, 2), 0);
  1152. if (LHour < 0) or (LHour > 24) then begin
  1153. Exit;
  1154. end;
  1155. LMin := IndyStrToInt(Copy(AString, 11, 2), 0);
  1156. if (LMin < 0) or (LMin > 59) then begin
  1157. Exit;
  1158. end;
  1159. LSec := IndyStrToInt(Copy(AString, 13, 2), 0);
  1160. if (LSec < 0) or (LSec > 59) then begin
  1161. Exit;
  1162. end;
  1163. Result := True;
  1164. end;
  1165. function IsMDTMDate(const ADate : String) : Boolean;
  1166. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1167. {
  1168. Note from FTP Voyager knowlege base:
  1169. MDTM
  1170. This is from the UNIX world and it lets you query the server for the modification date and time of a file or directory. Unlike UNIX, FTP Serv-U also lets the client set the modification date and time of files on the server, if the user has sufficient access rights to do this. Its use is in synchronizing uploaded files with those on the client. Normally FTP has no way to explicitly set the date of uploaded files, they simply get the date they were created on the server. MDTM lets the client change that so they get the date of the original file on the server. Works for directories too. The syntax to set the date and time is:
  1171. MDTM yyyymmddhhmmss[+-xxx]
  1172. Where ‘yyyymmddhhmmss’ is a line of text with the year, month, day, hour, minutes, and seconds the file should get set to. The next part, “[+-xxx]”, is optional time zone information of the FTP client in minutes relative to UTC.
  1173. If the client provides this info FTP Serv-U takes care to convert the date and time to the proper local time at the server, so dates and times are kept consistent (a file created at 4 in the morning in the Eastern US would be created at 10 in Central Europe). If no time zone info is given FTP Serv-U assumes you are specifying local time at the server.
  1174. An example, showing how to set the time if the client is in the Eastern US during summer time: “MDTM 19980719103029-240”. This sets the date and time to 19 July 1998, 10:30am 29 seconds, and indicates the client is 240 behind UT
  1175. }
  1176. var
  1177. LBuffer, LMSecPart : String;
  1178. begin
  1179. Result := False;
  1180. LBuffer := ADate;
  1181. if IndyPos('-', LBuffer) > 0 then begin
  1182. LMSecPart := LBuffer;
  1183. LBuffer := Fetch(LMSecPart, '-');
  1184. if not IsNumeric(LMSecPart) then begin
  1185. Exit;
  1186. end;
  1187. end;
  1188. if IndyPos('+', LBuffer) > 0 then begin
  1189. LMSecPart := LBuffer;
  1190. LBuffer := Fetch(LMSecPart, '+');
  1191. if not IsNumeric(LMSecPart) then begin
  1192. Exit;
  1193. end;
  1194. end;
  1195. if IndyPos('.', LBuffer) > 0 then begin
  1196. LMSecPart := Fetch(LBuffer, '.');
  1197. end;
  1198. if Length(LBuffer) <> 14 then begin
  1199. Exit;
  1200. end;
  1201. if not IsNumeric(LBuffer) then begin
  1202. Exit;
  1203. end;
  1204. if (LMSecPart <> '') and (not IsNumeric(LMSecPart)) then begin
  1205. Exit;
  1206. end;
  1207. Result := IsValidTimeStamp(LBuffer);
  1208. end;
  1209. function MDTMOffset(const AOffs : String) : TDateTime;
  1210. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1211. var
  1212. LOffs : Integer;
  1213. begin
  1214. LOffs := IndyStrToInt(AOffs);
  1215. {We use ABS because EncodeTime will only accept positve values}
  1216. Result := EncodeTime(Abs(LOffs) div 60, Abs(LOffs) mod 60, 0, 0);
  1217. if LOffs > 0 then begin
  1218. Result := 0 - Result;
  1219. end;
  1220. end;
  1221. function MinutesFromGMT : Integer;
  1222. {$IFDEF HAS_GetLocalTimeOffset}
  1223. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1224. {$ELSE}
  1225. {$IFDEF HAS_DateUtils_TTimeZone}
  1226. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1227. {$ELSE}
  1228. var
  1229. LD : TDateTime;
  1230. LHour, LMin, LSec, LMSec : Word;
  1231. {$ENDIF}
  1232. {$ENDIF}
  1233. begin
  1234. {$IFDEF HAS_GetLocalTimeOffset}
  1235. // RLebeau: Note that on Linux/Unix, this information may be inaccurate around
  1236. // the DST time changes (for optimization). In that case, the unix.ReReadLocalTime()
  1237. // function must be used to re-initialize the timezone information...
  1238. // RLebeau 1/15/2022: the value returned by MinutesFromGMT() is meant to be *subtracted*
  1239. // from a local time, and *added* to a UTC time. However, the value returned by
  1240. // FPC's GetLocalTimeOffset() is the opposite - it is meant to be *added* to local time,
  1241. // and *subtracted* from UTC time. So, we need to flip its sign here...
  1242. Result := -1 * GetLocalTimeOffset();
  1243. {$ELSE}
  1244. {$IFDEF HAS_DateUtils_TTimeZone}
  1245. Result := {-1 *} Trunc(TTimeZone.Local.UtcOffset.TotalMinutes);
  1246. {$ELSE}
  1247. LD := OffsetFromUTC;
  1248. DecodeTime(LD, LHour, LMin, LSec, LMSec);
  1249. if LD < 0.0 then begin
  1250. Result := 0 - (LHour * 60 + LMin);
  1251. end else begin
  1252. Result := LHour * 60 + LMin;
  1253. end;
  1254. {$ENDIF}
  1255. {$ENDIF}
  1256. end;
  1257. function FTPDateTimeToMDTMD(const ATimeStamp : TDateTime; const AIncludeMSecs : Boolean=True; const AIncludeGMTOffset : Boolean=True): String;
  1258. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1259. var
  1260. LYear, LMonth, LDay,
  1261. LHour, LMin, LSec, LMSec : Word;
  1262. LOfs : Integer;
  1263. LFmt : string;
  1264. begin
  1265. DecodeDate(ATimeStamp, LYear, LMonth, LDay);
  1266. DecodeTime(ATimeStamp, LHour, LMin, LSec, LMSec);
  1267. Result := IndyFormat('%4d%2d%2d%2d%2d%2d', [LYear,LMonth,LDay,LHour,LMin,LSec]); {Do not translate}
  1268. if AIncludeMSecs then begin
  1269. Result := Result + IndyFormat('.%3d', [LMSec]); {Do not translate}
  1270. end;
  1271. if AIncludeGMTOffset then begin
  1272. LOfs := MinutesFromGMT;
  1273. if LOfs < 0 then begin
  1274. LFmt := '%d'; {do not localize}
  1275. end else begin
  1276. LFmt := '+%d'; {do not localize}
  1277. end;
  1278. Result := Result + IndyFormat(LFmt, [LOfs]);
  1279. end;
  1280. Result := ReplaceAll(Result, ' ', '0');
  1281. end;
  1282. function FTPMDTMToGMTDateTime(const ATimeStamp : String):TDateTime;
  1283. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1284. var
  1285. LYear, LMonth, LDay, LHour, LMin, LSec, LMSec : Integer;
  1286. LBuffer : String;
  1287. LOffset : String;
  1288. begin
  1289. Result := 0;
  1290. LBuffer := ATimeStamp;
  1291. if LBuffer <> '' then begin
  1292. //extract any offset
  1293. if IndyPos('-', LBuffer) > 0 then begin
  1294. LOffset := LBuffer;
  1295. LBuffer := Fetch(LOffset, '-');
  1296. LOffset := '-' + LOffset;
  1297. end;
  1298. if IndyPos('+', LBuffer) > 0 then begin
  1299. LOffset := LBuffer;
  1300. LBuffer := Fetch(LOffset, '+');
  1301. end;
  1302. // 1234 56 78 90 12 34
  1303. // ---------- ---------
  1304. // 1998 11 07 08 52 15
  1305. LYear := IndyStrToInt(Copy(LBuffer, 1, 4), 0);
  1306. LMonth := IndyStrToInt(Copy(LBuffer, 5, 2), 0);
  1307. LDay := IndyStrToInt(Copy(LBuffer, 7, 2), 0);
  1308. LHour := IndyStrToInt(Copy(LBuffer, 9, 2), 0);
  1309. LMin := IndyStrToInt(Copy(LBuffer, 11, 2), 0);
  1310. LSec := IndyStrToInt(Copy(LBuffer, 13, 2), 0);
  1311. Fetch(LBuffer, '.');
  1312. LMSec := IndyStrToInt(LBuffer, 0);
  1313. Result := EncodeDate(LYear, LMonth, LDay);
  1314. Result := Result + EncodeTime(LHour, LMin, LSec, LMSec);
  1315. if LOffset = '' then begin
  1316. Result := LocalTimeToUTCTime(Result);
  1317. end else begin
  1318. Result := Result - MDTMOffset(LOffset);
  1319. end;
  1320. end;
  1321. end;
  1322. function IsYYYYMMDD(const AData : String) : Boolean;
  1323. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1324. //Does it look something like this:
  1325. //2002-09-02
  1326. //
  1327. //or
  1328. //
  1329. //90-05-19
  1330. //1234567890
  1331. begin
  1332. Result := CharIsInSet(AData, 5, CDATE_PART_SEP) and CharIsInSet(AData, 8, CDATE_PART_SEP);
  1333. if Result then begin
  1334. Result := IsNumeric(AData, 4) and IsNumeric(AData, 2, 6) and IsNumeric(AData, 2, 9);
  1335. end;
  1336. if not Result then begin
  1337. Result := CharIsInSet(AData, 3, CDATE_PART_SEP) and CharIsInSet(AData, 6, CDATE_PART_SEP);
  1338. if Result then begin
  1339. Result := IsNumeric(AData, 2) and IsNumeric(AData, 2, 4) and IsNumeric(AData, 2, 7);
  1340. end;
  1341. end;
  1342. end;
  1343. function IsDDMonthYY(const AData : String; const ADelim : String) : Boolean;
  1344. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1345. var
  1346. LBuf, LPt : String;
  1347. begin
  1348. Result := False;
  1349. if PatternsInStr(ADelim, AData) = 2 then begin
  1350. LBuf := AData;
  1351. LPt := Fetch(LBuf,ADelim);
  1352. //day
  1353. if (IndyStrToInt(LPt, 0) > 0) and (IndyStrToInt(LPt, 0) < 32) then begin
  1354. //month
  1355. LPt := Fetch(LBuf, ADelim);
  1356. if StrToMonth(LPt) > 0 then begin
  1357. //year
  1358. LPt := Fetch(LBuf, ADelim);
  1359. Result := IsNumeric(LPt);
  1360. end;
  1361. end;
  1362. end;
  1363. end;
  1364. function IsMMDDYY(const AData : String; const ADelim : String) : Boolean;
  1365. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1366. var
  1367. LBuf, LPt : String;
  1368. begin
  1369. Result := False;
  1370. if PatternsInStr(ADelim, AData) = 2 then begin
  1371. LBuf := AData;
  1372. LPt := Fetch(LBuf, ADelim);
  1373. if (IndyStrToInt(LPt, 0) > 0) and (IndyStrToInt(LPt, 0) < 13) then begin
  1374. LPt := Fetch(LBuf, ADelim);
  1375. if (IndyStrToInt(LPt, 0) > 0) and (IndyStrToInt(LPt, 0) < 33) then begin
  1376. Result := IsNumeric(LBuf);
  1377. end;
  1378. end;
  1379. end;
  1380. end;
  1381. function Y2Year(const AYear : Integer): Integer;
  1382. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1383. {
  1384. This function ensures that 2 digit dates returned
  1385. by some FTP servers are interpretted just like Borland's year
  1386. handling routines.
  1387. }
  1388. {$IFDEF HAS_TFormatSettings_Object}
  1389. {For Delphi XE, we have a format settings object that includes a member
  1390. for two digit year processing. Use that instead because that is thread-safe.
  1391. Also note, that in this version, TFormatSettings is not an object at all, it's a
  1392. record with associated functions and procedures plus a creator. Since we allocate
  1393. it on the stack with the definition, we can't "free" it with FreeAndNil. }
  1394. var
  1395. LFormatSettings: SysUtils.TFormatSettings;
  1396. {$ENDIF}
  1397. begin
  1398. Result := AYear;
  1399. //Y2K Complience for current code
  1400. //Note that some OS/2 servers return years greater than 100 for
  1401. //years such as 2000 and 2003
  1402. if Result < 1000 then begin
  1403. {$IFDEF HAS_TFormatSettings_Object}
  1404. LFormatSettings:= TFormatSettings.Create(''); //use default locale
  1405. if LFormatSettings.TwoDigitYearCenturyWindow > 0 then begin
  1406. if Result > LFormatSettings.TwoDigitYearCenturyWindow then begin
  1407. {$ELSE}
  1408. if TwoDigitYearCenturyWindow > 0 then begin
  1409. if Result > TwoDigitYearCenturyWindow then begin
  1410. {$ENDIF}
  1411. Inc(Result, ((IndyCurrentYear div 100)-1)*100);
  1412. end else begin
  1413. Inc(Result, (IndyCurrentYear div 100)*100);
  1414. end;
  1415. end else begin
  1416. Inc(Result, (IndyCurrentYear div 100)*100);
  1417. end;
  1418. {$IFDEF HAS_TFormatSettings_Object}
  1419. {$ENDIF}
  1420. end;
  1421. end;
  1422. function DateYYMMDD(const AData: String): TDateTime;
  1423. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1424. var
  1425. LMonth, LDay, LYear : Integer;
  1426. LBuffer : String;
  1427. LDelim : String;
  1428. begin
  1429. LBuffer := AData;
  1430. LDelim := FindDelimInNumbers(AData);
  1431. LYear := IndyStrToInt(Fetch(LBuffer,LDelim), 0);
  1432. LMonth := IndyStrToInt(Fetch(LBuffer,LDelim), 0);
  1433. LDay := IndyStrToInt(Fetch(LBuffer,LDelim), 0);
  1434. LYear := Y2Year(LYear);
  1435. Result := EncodeDate(LYear, LMonth, LDay);
  1436. end;
  1437. function DateYYStrMonthDD(const AData: String; const ADelim : String = '-'): TDateTime;
  1438. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1439. var
  1440. LMonth, LDay, LYear : Integer;
  1441. LBuffer : String;
  1442. begin
  1443. LBuffer := AData;
  1444. LYear := IndyStrToInt(Fetch(LBuffer,ADelim), 0);
  1445. LMonth := StrToMonth(Trim(Fetch(LBuffer,ADelim)));
  1446. LDay := IndyStrToInt(Fetch(LBuffer,ADelim), 0);
  1447. LYear := Y2Year(LYear);
  1448. Result := EncodeDate(LYear, LMonth, LDay);
  1449. end;
  1450. function DateStrMonthDDYY(const AData:String; const ADelim : String = '-'; const AAddMissingYear : Boolean = False): TDateTime;
  1451. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1452. var
  1453. LMonth, LDay, LYear : Integer;
  1454. LBuffer : String;
  1455. LMnth : String;
  1456. begin
  1457. LBuffer := AData;
  1458. LMnth := Trim(Fetch(LBuffer,ADelim));
  1459. LMonth := IndyStrToInt(LMnth, 0);
  1460. if LMonth = 0 then begin
  1461. LMonth := StrToMonth(LMnth);
  1462. end;
  1463. LDay := IndyStrToInt(Fetch(LBuffer,ADelim), 0);
  1464. LYear := IndyStrToInt(Fetch(LBuffer,ADelim), 0);
  1465. if AAddMissingYear and (LYear = 0) then begin
  1466. LYear := AddMissingYear(LDay, LMonth);
  1467. end;
  1468. LYear := Y2Year(LYear);
  1469. Result := EncodeDate(LYear, LMonth, LDay);
  1470. end;
  1471. function DateDDStrMonthYY(const AData: String; const ADelim : String='-'): TDateTime;
  1472. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1473. var
  1474. LMonth, LDay, LYear : Integer;
  1475. LBuffer : String;
  1476. begin
  1477. LBuffer := AData;
  1478. LDay := IndyStrToInt(Fetch(LBuffer,ADelim), 0);
  1479. LMonth := StrToMonth(Trim(Fetch(LBuffer,ADelim)));
  1480. LYear := IndyStrToInt(Fetch(LBuffer,ADelim), 0);
  1481. LYear := Y2Year(LYear);
  1482. Result := EncodeDate(LYear, LMonth, LDay);
  1483. end;
  1484. function DateMMDDYY(const AData: String): TDateTime;
  1485. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1486. var
  1487. LMonth, LDay, LYear : Integer;
  1488. LBuffer : String;
  1489. LDelim : String;
  1490. begin
  1491. LBuffer := AData;
  1492. LDelim := FindDelimInNumbers(AData);
  1493. LMonth := IndyStrToInt(Fetch(LBuffer,LDelim), 0);
  1494. LDay := IndyStrToInt(Fetch(LBuffer,LDelim), 0);
  1495. LYear := IndyStrToInt(Fetch(LBuffer,LDelim), 0);
  1496. LYear := Y2Year(LYear);
  1497. Result := EncodeDate(LYear, LMonth, LDay);
  1498. end;
  1499. function TimeHHMMSS(const AData : String):TDateTime;
  1500. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1501. var
  1502. LCHour, LCMin, LCSec, LCMSec : Word;
  1503. LHour, LMin, LSec, LMSec : Word;
  1504. LBuffer : String;
  1505. LDelim : String;
  1506. LPM : Boolean;
  1507. LAM : Boolean; //necessary because we have to remove 12 hours if the time was 12:01:00 AM
  1508. begin
  1509. LPM := False;
  1510. LAM := False;
  1511. LBuffer := UpperCase(AData);
  1512. if IndyPos('PM', LBuffer) > 0 then begin {do not localize}
  1513. LPM := True;
  1514. LBuffer := Fetch(LBuffer, 'PM'); {do not localize}
  1515. end;
  1516. if IndyPos('AM', LBuffer) > 0 then begin {do not localize}
  1517. LAM := True;
  1518. LBuffer := Fetch(LBuffer, 'AM'); {do not localize}
  1519. end;
  1520. //one server only gives an a or p instead of am or pm
  1521. if IndyPos('P', LBuffer) > 0 then begin {do not localize}
  1522. LPM := True;
  1523. LBuffer := Fetch(LBuffer,'P'); {do not localize}
  1524. end;
  1525. if IndyPos('A', LBuffer) > 0 then begin {do not localize}
  1526. LAM := True;
  1527. LBuffer := Fetch(LBuffer, 'A'); {do not localize}
  1528. end;
  1529. LBuffer := Trim(LBuffer);
  1530. DecodeTime(Now, LCHour, LCMin, LCSec, LCMSec);
  1531. LDelim := FindDelimInNumbers(AData);
  1532. LHour := IndyStrToInt(Fetch(LBuffer, LDelim), 0);
  1533. LMin := IndyStrToInt(Fetch(LBuffer, LDelim), 0);
  1534. if LPM then begin
  1535. //in the 12 hour format, afternoon is 12:00PM followed by 1:00PM
  1536. //while midnight is written as 12:00 AM
  1537. //Not exactly technically correct but pritty accurate
  1538. if LHour < 12 then begin
  1539. Inc(LHour, 12);
  1540. end;
  1541. end;
  1542. if LAM then begin
  1543. if LHour = 12 then begin
  1544. LHour := 0;
  1545. end;
  1546. end;
  1547. LSec := IndyStrToInt(Fetch(LBuffer, LDelim), 0);
  1548. LMSec := IndyStrToInt(Fetch(LBuffer, LDelim), 0);
  1549. Result := EncodeTime(LHour, LMin, LSec, LMSec);
  1550. end;
  1551. function IsIn6MonthWindow(const AMDate : TDateTime):Boolean;
  1552. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1553. //based on http://www.opengroup.org/onlinepubs/007908799/xbd/utilconv.html#usg
  1554. //For dates, we display the time only if the date is within 6 monthes of the current
  1555. //date. Otherwise, we send the year.
  1556. var
  1557. LCurMonth, LCurDay, LCurYear : Word; //Now
  1558. LPMonth, LPYear : Word;
  1559. LMMonth, LMDay, LMYear : Word;//AMDate
  1560. begin
  1561. DecodeDate(Now, LCurYear, LCurMonth, LCurDay);
  1562. DecodeDate(AMDate, LMYear, LMMonth, LMDay);
  1563. if (LCurMonth - 6) < 1 then begin
  1564. LPMonth := 12 + (LCurMonth - 6);
  1565. LPYear := LCurYear - 1;
  1566. end else begin
  1567. LPMonth := LCurMonth - 6;
  1568. LPYear := LCurYear;
  1569. end;
  1570. if LMYear < LPYear then begin
  1571. Result := False;
  1572. Exit;
  1573. end;
  1574. if LMYear = LPYear then begin
  1575. Result := (LMMonth >= LPMonth);
  1576. if Result and (LMMonth = LPMonth) then begin
  1577. Result := (LMDay >= LCurDay);
  1578. Exit;
  1579. end;
  1580. end else begin
  1581. Result := True;
  1582. end;
  1583. end;
  1584. function AddMissingYear(const ADay, AMonth : UInt32): UInt32;
  1585. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1586. var
  1587. LDay, LMonth, LYear : Word;
  1588. DT: TDateTime;
  1589. begin
  1590. DecodeDate(Now, LYear, LMonth, LDay);
  1591. Result := LYear;
  1592. if TryEncodeDate(LYear, AMonth, ADay, DT) and (DT > Trunc(Now + 1)) then begin
  1593. Result := LYear - 1;
  1594. end;
  1595. end;
  1596. function IsHHMMSS(const AData : String; const ADelim : String) : Boolean;
  1597. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1598. //This assumes hours in the form 0-23 instead of the 12 AM/PM hour system used in the US.
  1599. var
  1600. LBuf, LPt : String;
  1601. begin
  1602. Result := False;
  1603. LBuf := AData;
  1604. if PatternsInStr(ADelim, AData) > 0 then begin
  1605. LPt := Fetch(LBuf, ADelim);
  1606. if (IndyStrToInt(LPt, -1) > -1) and (IndyStrToInt(LPt, -1) < 24) then begin
  1607. LPt := Fetch(LBuf, ADelim);
  1608. if (IndyStrToInt(LPt, -1) > -1) and (IndyStrToInt(LPt, 0) < 60) then begin
  1609. LPt := Fetch(LBuf, ADelim);
  1610. if LPt = '' then begin
  1611. Result := True;
  1612. end else begin
  1613. //seconds are also given - check those
  1614. Result := (IndyStrToInt(LPt, -1) > -1) and (IndyStrToInt(LPt, 0) < 60);
  1615. end;
  1616. end;
  1617. end;
  1618. end;
  1619. end;
  1620. function MVSDate(const AData: String): TDateTime;
  1621. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1622. var
  1623. LYear, LMonth, LDay : Integer;
  1624. LCYear, LCMonth, LCDay : Word;
  1625. LBuffer : String;
  1626. begin
  1627. DecodeDate(Now, LCYear, LCMonth, LCDay);
  1628. LBuffer := AData;
  1629. if IndyPos('/', LBuffer) = 3 then begin
  1630. //two digit things could be in order of yy/mm/dd or mm/dd/yy in a partitionned dtaset
  1631. LYear := IndyStrToInt(Fetch(LBuffer, '/'), LCYear);
  1632. if (LYear < 13) and (LYear > 0) then begin
  1633. LMonth := LYear;
  1634. LDay := IndyStrToInt(Fetch(LBuffer, '/'), LCDay);
  1635. LYear := IndyStrToInt(Fetch(LBuffer, '/'), LCYear);
  1636. end else begin
  1637. LMonth := IndyStrToInt(Fetch(LBuffer, '/'), LCMonth);
  1638. LDay := IndyStrToInt(Fetch(LBuffer, '/'), LCDay);
  1639. end;
  1640. end else begin
  1641. LYear := IndyStrToInt(Fetch(LBuffer, '/'), LCYear);
  1642. LMonth := IndyStrToInt(Fetch(LBuffer, '/'), LCMonth);
  1643. LDay := IndyStrToInt(Fetch(LBuffer, '/'), LCDay);
  1644. end;
  1645. LYear := Y2Year(LYear);
  1646. Result := EncodeDate(LYear, LMonth, LDay);
  1647. end;
  1648. function AS400Date(const AData: String): TDateTime;
  1649. var
  1650. LDelim : String;
  1651. LBuffer : String;
  1652. LDay, LMonth, LYear : Integer;
  1653. procedure SwapNos(var An1, An2 : Integer);
  1654. var
  1655. LN : Integer;
  1656. begin
  1657. LN := An2;
  1658. An2 := An1;
  1659. An1 := LN;
  1660. end;
  1661. begin
  1662. Result := 0;
  1663. LDelim := FindDelimInNumbers(AData);
  1664. if LDelim = '' then begin
  1665. Exit;
  1666. end;
  1667. LBuffer := AData;
  1668. LDay := IndyStrToInt(Fetch(LBuffer, LDelim), 0);
  1669. LMonth := IndyStrToInt(Fetch(LBuffer, LDelim), 0);
  1670. LYear := IndyStrToInt(Fetch(LBuffer, LDelim), 0);
  1671. if LMonth > 12 then begin
  1672. SwapNos(LDay, LMonth);
  1673. end;
  1674. if LDay > 31 then begin
  1675. SwapNos(LYear, LDay);
  1676. end;
  1677. LYear := Y2Year(LYear);
  1678. Result := EncodeDate(LYear, LMonth, LDay);
  1679. end;
  1680. //=== platform stuff
  1681. //===== Unix
  1682. function IsValidUnixPerms(AData : String; const AStrict : Boolean = False) : Boolean;
  1683. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1684. //Stict mode is for things such as Novell Netware Unix Print Services FTP Deamon
  1685. //which are not quite like Unix.
  1686. //Non-strict mode is for Unix servers or servers that emulate Unix because some are broken.
  1687. var
  1688. SData : String;
  1689. begin
  1690. if not AStrict then begin
  1691. SData := UpperCase(AData);
  1692. Result := (Length(SData) > 9) and
  1693. {LynxOS may report "f" or "r" for a regular file, "+" for a contiguous file,
  1694. "i" for a non-persistent ipc special file, and "I" for a persistent ipc
  1695. special file. The Linux manpage for stat also reports "m" for XENIX shared
  1696. data subtype of IFNAM, and "w" for a BSD whiteout}
  1697. CharIsInSet(SData, 1, 'LD-BCPS+IMW') and {Do not Localize}
  1698. CharIsInSet(SData, 2, 'TSRWX-') and {Do not Localize}
  1699. {Distinct TCP/IP FTP Server-32 3.0 errs by reporting an 'A" here }
  1700. CharIsInSet(SData, 3, 'TSRWX-A') and {Do not Localize}
  1701. CharIsInSet(SData, 4, 'TSRWX-L') and {Do not Localize}
  1702. {Distinct TCP/IP FTP Server-32 3.0 errs by reporting an 'H" here for hidden files}
  1703. CharIsInSet(SData, 5, 'TSRWX-H') and {Do not Localize}
  1704. CharIsInSet(SData, 6, 'TSRWX-') and {Do not Localize}
  1705. {Distinct's FTP Server Active X may report a "Y" by mistake, saw in manual
  1706. FTP Server, ActiveX Control, File Transfer Protocol (RFC 959), ActiveX Control,
  1707. for Microsoftâ Windowsä, Version 4.01
  1708. Copyright Ó 1996 - 1998 by Distinct Corporation
  1709. All rights reserved
  1710. }
  1711. {Solaris returns "L" instead of "S" for setgid without group execute (mandatory locking)}
  1712. CharIsInSet(SData, 7, 'TSRWX-YL') and {Do not Localize}
  1713. CharIsInSet(SData, 8, 'TSRWX-A') and {Do not Localize}
  1714. {VxWorks 5.3.1 FTP Server has a quirk where a "A" is in the permissions
  1715. See:
  1716. http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=utf-8&threadm=slrn73rfie.
  1717. 1g2.chc%40nasa2.ksc.nasa.gov&rnum=1&prev=/groups%3Fq%3DVxWorks%2BFTP%2BLIST%2
  1718. Bformat%2Bdate%26hl%3Den%26lr%3D%26ie%3DUTF-8%26oe%3Dutf-8%26selm%3D
  1719. slrn73rfie.1g2.chc%2540nasa2.ksc.nasa.gov%26rnum%3D1
  1720. }
  1721. CharIsInSet(SData, 9, 'TSRWX-') and {Do not Localize}
  1722. CharIsInSet(SData, 10, 'TSRWX-'); {Do not Localize}
  1723. end else begin
  1724. Result := (Length(SData) > 9) and
  1725. CharIsInSet(AData, 1, 'd-') and {Do not Localize}
  1726. CharIsInSet(AData, 2, 'tsrwx-') and {Do not Localize}
  1727. CharIsInSet(AData, 3, 'tsrwx-') and {Do not Localize}
  1728. CharIsInSet(AData, 4, 'tsrwx-') and {Do not Localize}
  1729. CharIsInSet(AData, 5, 'tsrwx-') and {Do not Localize}
  1730. CharIsInSet(AData, 6, 'tsrwx-') and {Do not Localize}
  1731. CharIsInSet(AData, 7, 'tsrwx-') and {Do not Localize}
  1732. CharIsInSet(AData, 8, 'tsrwx-') and {Do not Localize}
  1733. CharIsInSet(AData, 9, 'tsrwx-') and {Do not Localize}
  1734. CharIsInSet(AData, 10, 'tsrwx- '); {Do not Localize}
  1735. end;
  1736. end;
  1737. function IsUnixLsErr(const AData: String): Boolean;
  1738. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1739. begin
  1740. Result := TextStartsWith(AData, '/bin/ls:'); {do not localize}
  1741. end;
  1742. function IsUnixHiddenFile(const AFileName : String): Boolean;
  1743. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1744. var
  1745. LName : String;
  1746. begin
  1747. LName := IndyGetFileName(StripInitPathDelim(AFileName));
  1748. Result := (not IsNavPath(AFileName)) and TextStartsWith(LName, '.');
  1749. end;
  1750. function IsUnixExec(const LUPer, LGPer, LOPer : String): Boolean;
  1751. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1752. begin
  1753. if (Length(LUPer) > 2) and (Length(LGPer) > 2) and (Length(LOPer) > 2) then begin
  1754. Result := CharIsInSet(LUPer, 3, 'xSs') or {do not localize}
  1755. CharIsInSet(LGPer, 3, 'xSs') or {do not localize}
  1756. CharIsInSet(LOPer, 3, 'xSs'); {do not localize}
  1757. end else begin
  1758. Result := False;
  1759. end;
  1760. end;
  1761. function PermStringToModeBits(const APerms : String): UInt32;
  1762. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1763. begin
  1764. Result := 0;
  1765. //owner bits
  1766. if (Length(APerms) > 0) and (APerms[1] = 'r') then begin
  1767. Result := Result or IdS_IRUSR;
  1768. end;
  1769. if (Length(APerms) > 1) and (APerms[2] = 'w') then begin
  1770. Result := Result or IdS_IWUSR;
  1771. end;
  1772. if Length(APerms) > 2 then begin
  1773. case APerms[3] of
  1774. 'x' : //exec
  1775. begin
  1776. Result := Result or IdS_IXUSR;
  1777. end;
  1778. 's' : //SUID and exec
  1779. begin
  1780. Result := Result or IdS_IXUSR;
  1781. Result := Result or IdS_ISUID;
  1782. end;
  1783. 'S' : //SUID bit without owner exec
  1784. begin
  1785. Result := Result or IdS_ISUID;
  1786. end;
  1787. end;
  1788. end;
  1789. //group bits
  1790. if (Length(APerms) > 3) and (APerms[4] = 'r') then begin
  1791. Result := Result or IdS_IRGRP;
  1792. end;
  1793. if (Length(APerms) > 4) and (APerms[5] = 'w') then begin
  1794. Result := Result or IdS_IWGRP;
  1795. end;
  1796. if Length(APerms) > 5 then begin
  1797. case APerms[6] of
  1798. 'x' : //exec
  1799. begin
  1800. Result := Result or IdS_IXGRP;
  1801. end;
  1802. 's' : //SUID and exec
  1803. begin
  1804. Result := Result or IdS_IXGRP;
  1805. Result := Result or IdS_ISGID;
  1806. end;
  1807. 'S' : //SGID bit without group exec
  1808. begin
  1809. Result := Result or IdS_ISGID;
  1810. end;
  1811. end;
  1812. end;
  1813. //Other permissions
  1814. if (Length(APerms) > 6) and (APerms[7] = 'r') then begin
  1815. Result := Result or IdS_IROTH;
  1816. end;
  1817. if (Length(APerms) > 7) and (APerms[8] = 'w') then begin
  1818. Result := Result or IdS_IWOTH;
  1819. end;
  1820. if Length(APerms) > 8 then begin
  1821. case APerms[9] of
  1822. 'x' :
  1823. begin
  1824. Result := Result or IdS_IXOTH;
  1825. end;
  1826. 't' :
  1827. begin
  1828. Result := Result or IdS_IXOTH;
  1829. Result := Result or IdS_ISVTX;
  1830. end;
  1831. 'T' :
  1832. begin
  1833. Result := Result or IdS_ISVTX;
  1834. end;
  1835. end;
  1836. end;
  1837. end;
  1838. function ModeBitsToPermString(const AMode : UInt32) : String;
  1839. function GetPerm1Bit(ABit: UInt32; AIfSet: Char): Char;
  1840. begin
  1841. if (AMode and ABit) = ABit then begin
  1842. Result := AIfSet;
  1843. end else begin
  1844. Result := '-';
  1845. end;
  1846. end;
  1847. function GetPerm2Bits(ABit1, ABit2: UInt32; AIfBit1Set, AIfBit2Set: Char): Char;
  1848. begin
  1849. Result := GetPerm1Bit(ABit1, AIfBit1Set);
  1850. if Result = '-' then begin
  1851. Result := GetPerm1Bit(ABit2, AIfBit2Set);
  1852. end;
  1853. end;
  1854. var
  1855. LPerm: Char;
  1856. {$IFDEF STRING_IS_IMMUTABLE}
  1857. LSB: TIdStringBuilder;
  1858. {$ENDIF}
  1859. begin
  1860. {$IFDEF STRING_IS_IMMUTABLE}
  1861. LSB := TIdStringBuilder.Create(9);
  1862. {$ELSE}
  1863. SetLength(Result, 9);
  1864. {$ENDIF}
  1865. //owner Permissions
  1866. //read by owner
  1867. LPerm := GetPerm1Bit(IdS_IRUSR, 'r');
  1868. {$IFDEF STRING_IS_IMMUTABLE}
  1869. LSB.Append(LPerm);
  1870. {$ELSE}
  1871. Result[1] := LPerm;
  1872. {$ENDIF}
  1873. //write by owner
  1874. LPerm := GetPerm1Bit(IdS_IWUSR, 'w');
  1875. {$IFDEF STRING_IS_IMMUTABLE}
  1876. LSB.Append(LPerm);
  1877. {$ELSE}
  1878. Result[2] := LPerm;
  1879. {$ENDIF}
  1880. //execute by owner
  1881. LPerm := GetPerm2Bits(IdS_ISUID, IdS_IXUSR, 's', 'x');
  1882. {$IFDEF STRING_IS_IMMUTABLE}
  1883. LSB.Append(LPerm);
  1884. {$ELSE}
  1885. Result[3] := LPerm;
  1886. {$ENDIF}
  1887. //group permissions
  1888. //read by group
  1889. LPerm := GetPerm1Bit(IdS_IRGRP, 'r');
  1890. {$IFDEF STRING_IS_IMMUTABLE}
  1891. LSB.Append(LPerm);
  1892. {$ELSE}
  1893. Result[4] := LPerm;
  1894. {$ENDIF}
  1895. //write by group
  1896. LPerm := GetPerm1Bit(IdS_IWGRP, 'w');
  1897. {$IFDEF STRING_IS_IMMUTABLE}
  1898. LSB.Append(LPerm);
  1899. {$ELSE}
  1900. Result[5] := LPerm;
  1901. {$ENDIF}
  1902. //execute by group
  1903. LPerm := GetPerm2Bits(IdS_ISGID, IdS_IXGRP, 's', 'x');
  1904. {$IFDEF STRING_IS_IMMUTABLE}
  1905. LSB.Append(LPerm);
  1906. {$ELSE}
  1907. Result[6] := LPerm;
  1908. {$ENDIF}
  1909. //other's permissions
  1910. //read by others
  1911. LPerm := GetPerm1Bit(IdS_IROTH, 'r');
  1912. {$IFDEF STRING_IS_IMMUTABLE}
  1913. LSB.Append(LPerm);
  1914. {$ELSE}
  1915. Result[7] := LPerm;
  1916. {$ENDIF}
  1917. //write by others
  1918. LPerm := GetPerm1Bit(IdS_IWOTH, 'w');
  1919. {$IFDEF STRING_IS_IMMUTABLE}
  1920. LSB.Append(LPerm);
  1921. {$ELSE}
  1922. Result[8] := LPerm;
  1923. {$ENDIF}
  1924. //execute by others
  1925. //Sticky bit - only owner can delete files in dir.
  1926. //on older systems, it means to keep the file in memory as a "cache"
  1927. LPerm := GetPerm2Bits(IdS_ISVTX, IdS_IXOTH, 't', 'x');
  1928. {$IFDEF STRING_IS_IMMUTABLE}
  1929. LSB.Append(LPerm);
  1930. {$ELSE}
  1931. Result[9] := LPerm;
  1932. {$ENDIF}
  1933. {$IFDEF STRING_IS_IMMUTABLE}
  1934. Result := LSB.ToString;
  1935. {$ENDIF}
  1936. end;
  1937. function ModeBitsToChmodNo(const AMode : UInt32): Integer;
  1938. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1939. begin
  1940. Result := 0;
  1941. if (AMode and IdS_ISUID) = IdS_ISUID then begin
  1942. Result := Result + 4000;
  1943. end;
  1944. if (AMode and IdS_ISGID) = IdS_ISGID then begin
  1945. Result := Result + 2000;
  1946. end;
  1947. if (AMode and IdS_ISVTX) = IdS_ISVTX then begin
  1948. Result := Result + 1000;
  1949. end;
  1950. if (AMode and IdS_IRUSR) = IdS_IRUSR then begin
  1951. Result := Result + 400;
  1952. end;
  1953. if (AMode and IdS_IWUSR) = IdS_IWUSR then begin
  1954. Result := Result + 200;
  1955. end;
  1956. if (AMode and IdS_IXUSR) = IdS_IXUSR then begin
  1957. Result := Result + 100;
  1958. end;
  1959. if (AMode and IdS_IRGRP) = IdS_IRGRP then begin
  1960. Result := Result + 40;
  1961. end;
  1962. if (AMode and IdS_IWGRP) = IdS_IWGRP then begin
  1963. Result := Result + 20;
  1964. end;
  1965. if (AMode and IdS_IXGRP) = IdS_IXGRP then begin
  1966. Result := Result + 10;
  1967. end;
  1968. if (AMode and IdS_IROTH) = IdS_IROTH then begin
  1969. Result := Result + 4;
  1970. end;
  1971. if (AMode and IdS_IWOTH) = IdS_IWOTH then begin
  1972. Result := Result + 2;
  1973. end;
  1974. if (AMode and IdS_IXOTH) = IdS_IXOTH then begin
  1975. Result := Result + 1;
  1976. end;
  1977. end;
  1978. function ChmodNoToModeBits(const AModVal : UInt32): UInt32;
  1979. {$IFDEF USE_INLINE} inline; {$ENDIF}
  1980. var
  1981. LSpecBits, LUBits, LGBits, LOBits : UInt32;
  1982. LTmp : UInt32;
  1983. begin
  1984. Result := 0;
  1985. LSpecBits := AModVal div 1000;
  1986. LSpecBits := LSpecBits and 7;
  1987. LTmp := AModVal;
  1988. LTmp := LTmp mod 1000;
  1989. LUBits := LTmp div 100;
  1990. LUBits := LUBits and 7;
  1991. LTmp := LTmp mod 100;
  1992. LGBits := LTmp div 10;
  1993. LGBits := LGBits and 7;
  1994. LTmp := LTmp mod 10;
  1995. LOBits := LTmp and 7;
  1996. if (LSpecBits and 4) = 4 then begin
  1997. Result := Result + IdS_ISUID;
  1998. end;
  1999. if (LSpecBits and 2) = 2 then begin
  2000. Result := Result + IdS_ISGID;
  2001. end;
  2002. if (LSpecBits and 1) = 1 then begin
  2003. Result := Result + IdS_ISVTX;
  2004. end;
  2005. //user bits
  2006. if (LUBits and 4) = 4 then begin
  2007. Result := Result + IdS_IRUSR;
  2008. end;
  2009. if (LUBits and 2) = 2 then begin
  2010. Result := Result + IdS_IWUSR;
  2011. end;
  2012. if (LUBits and 1) = 1 then begin
  2013. Result := Result + IdS_IXUSR;
  2014. end;
  2015. //group bits
  2016. if (LGBits and 4) = 4 then begin
  2017. Result := Result + IdS_IRGRP;
  2018. end;
  2019. if (LGBits and 2) = 2 then begin
  2020. Result := Result + IdS_IWGRP;
  2021. end;
  2022. if (LGBits and 1) = 1 then begin
  2023. Result := Result + IdS_IXGRP;
  2024. end;
  2025. //other bits
  2026. if (LOBits and 4) = 4 then begin
  2027. Result := Result + IdS_IROTH;
  2028. end;
  2029. if (LOBits and 2) = 2 then begin
  2030. Result := Result + IdS_IWOTH;
  2031. end;
  2032. if (LOBits and 1) = 1 then begin
  2033. Result := Result + IdS_IXOTH;
  2034. end;
  2035. end;
  2036. procedure ChmodNoToPerms(const AChmodNo : Integer; var VPermissions : String); overload;
  2037. {$IFDEF USE_INLINE} inline; {$ENDIF}
  2038. begin
  2039. VPermissions := ModeBitsToPermString(ChmodNoToModeBits(AChmodNo));
  2040. end;
  2041. procedure ChmodNoToPerms(const AChmodNo : Integer; var VUser, VGroup, VOther : String);
  2042. {$IFDEF USE_INLINE} inline; {$ENDIF}
  2043. var
  2044. LPerms : String;
  2045. begin
  2046. ChmodNoToPerms(AChmodNo,LPerms);
  2047. VUser := Copy(LPerms, 1, 3);
  2048. VGroup := Copy(LPerms, 4, 3);
  2049. VOther := Copy(LPerms, 7, 3);
  2050. end;
  2051. function PermsToChmodNo(const AUser, AGroup, AOther : String): Integer;
  2052. {$IFDEF USE_INLINE} inline; {$ENDIF}
  2053. begin
  2054. Result := ModeBitsToChmodNo(PermStringToModeBits(AUser+AGroup+AOther));
  2055. end;
  2056. //===== Novell Netware
  2057. //ftp.sips.state.nc.us
  2058. function IsNovelPSPattern(const AStr : String): Boolean;
  2059. {$IFDEF USE_INLINE} inline; {$ENDIF}
  2060. var
  2061. s : TStringList;
  2062. LModStr : String;
  2063. begin
  2064. LModStr := AStr;
  2065. if (Length(LModStr) > 1) and (LModStr[2] = '[') then begin
  2066. IdInsert(' ', LModStr, 2);
  2067. end;
  2068. s := TStringList.Create;
  2069. try
  2070. SplitDelimitedString(LModStr, s, True);
  2071. //0-type
  2072. //1-permissions
  2073. //2-owner
  2074. //3-size
  2075. //4-month
  2076. //5-day of month
  2077. //6-year
  2078. //7-time
  2079. //8-am/pm
  2080. //9- start of filename
  2081. Result := (s.Count > 8) and IsNumeric(s[6]) and IsHHMMSS(s[7], ':') and
  2082. (TextIsSame(s[8], 'AM') or TextIsSame(s[8], 'PM')); {do not localize}
  2083. finally
  2084. FreeAndNil(s);
  2085. end;
  2086. end;
  2087. function IsValidNovellPermissionStr(const AStr : String): Boolean;
  2088. const
  2089. PermSet = '-RWCEAFMS'; {do not localize}
  2090. var
  2091. i : Integer;
  2092. begin
  2093. Result := False;
  2094. if AStr = '' then begin
  2095. Exit;
  2096. end;
  2097. for i := 1 to Length(AStr) do begin
  2098. if not CharIsInSet(AStr, i, PermSet) then begin
  2099. Exit;
  2100. end;
  2101. end;
  2102. Result := True;
  2103. end;
  2104. function ExtractNovellPerms(const AData : String) : String;
  2105. {$IFDEF USE_INLINE} inline; {$ENDIF}
  2106. //extract the Novell Netware permissions from the enclosing brackets
  2107. var
  2108. LOpen, LClose : Integer;
  2109. begin
  2110. Result := '';
  2111. LOpen := IndyPos('[', AData); {Do not translate}
  2112. LClose := IndyPos(']', AData); {Do not translate}
  2113. if (LOpen <> 0) and (LClose <> 0) and (LOpen < LClose) then begin
  2114. Result := Copy(AData, LOpen+1, LClose-LOpen-1);
  2115. end;
  2116. Result := Trim(Result);
  2117. end;
  2118. //===== QVT/NET
  2119. function ExcludeQVNET(const AData : String) : Boolean;
  2120. {$IFDEF USE_INLINE} inline; {$ENDIF}
  2121. //A few tests will return a false positive with WinQVTNet
  2122. //This function prevents this.
  2123. begin
  2124. Result := (not IsMMDDYY(Copy(AData, 36, 10), '-')) or
  2125. (Copy(AData, 46, 1) <> ' ') or (not IsHHMMSS(Copy(AData, 47, 5), ':'));
  2126. end;
  2127. function ExtractQVNETFileName(const AData : String): String;
  2128. {$IFDEF USE_INLINE} inline; {$ENDIF}
  2129. //This is for WinQVT/Net v3.9 - note filenames are in a 8.3 format
  2130. //but unlike the standard MS-DOS form, spaces will appear if running
  2131. //on Win32 Operating systems and filenames have spaces. Note that
  2132. //long file names will not appear at all. I found this out with a rigged test case.
  2133. var
  2134. LBuf : String;
  2135. begin
  2136. LBuf := Copy(AData, 1, 12);
  2137. Result := Fetch(LBuf, '.');
  2138. LBuf := Trim(LBuf);
  2139. if LBuf <> '' then begin
  2140. Result := Result + '.' + Fetch(LBuf);
  2141. end;
  2142. Result := Fetch(Result, '/');
  2143. end;
  2144. //===== Mainframe support
  2145. function ExtractRecFormat(const ARecFM : String): String;
  2146. {$IFDEF USE_INLINE} inline; {$ENDIF}
  2147. begin
  2148. Result := ARecFM;
  2149. if TextStartsWith(Result, '<') then begin
  2150. IdDelete(Result, 1, 1);
  2151. end;
  2152. if TextEndsWith(Result, '>') then begin
  2153. Result := Fetch(Result, '>');
  2154. end;
  2155. end;
  2156. //===== IBM VSE Power Queue
  2157. function DispositionCodeToTIdVSEPQDisposition(const ADisp : Char) : TIdVSEPQDisposition;
  2158. {$IFDEF USE_INLINE} inline; {$ENDIF}
  2159. begin
  2160. case ADisp of
  2161. 'A' : Result := IdPQAppendable;
  2162. 'D' : Result := IdPQProcessAndDelete;
  2163. 'H' : Result := IdPQHoldUntilReleased;
  2164. 'K' : Result := IdPQProcessAndKeep;
  2165. 'L' : Result := IdPQLeaveUntilReleased;
  2166. 'X' : Result := IdPQErrorHoldUntilDK;//(Local only) Hold until disposition is changed to D or K. Temporarily assigned by VSE/POWER when processing fails.
  2167. 'Y' : Result := IdPQGetOrErrorHoldUntilDK;
  2168. '*' : Result := IdPQJobProcessing;
  2169. //only valid for some local jobs being created
  2170. 'I' : Result := IdPQSpoolOutputToInputD;
  2171. 'N' : Result := IdPQSurpressOutputSpooling;
  2172. 'T' : Result := IdPQSpoolOutputToTape;
  2173. else
  2174. Result := IdPQProcessAndDelete;
  2175. end;
  2176. end;
  2177. function TIdVSEPQDispositionDispositionCode(const ADisp : TIdVSEPQDisposition) : Char;
  2178. {$IFDEF USE_INLINE} inline; {$ENDIF}
  2179. begin
  2180. case ADisp of
  2181. IdPQAppendable : Result := 'A';
  2182. IdPQProcessAndDelete : Result := 'D';
  2183. IdPQHoldUntilReleased : Result := 'H';
  2184. IdPQProcessAndKeep : Result := 'K';
  2185. IdPQLeaveUntilReleased : Result := 'L';
  2186. IdPQErrorHoldUntilDK : Result := 'X';
  2187. IdPQGetOrErrorHoldUntilDK : Result := 'Y';
  2188. IdPQJobProcessing : Result := '*';
  2189. //only valid for some local jobs being created
  2190. IdPQSpoolOutputToInputD : Result := 'I';
  2191. IdPQSurpressOutputSpooling : Result := 'N';
  2192. IdPQSpoolOutputToTape : Result := 'T' ;
  2193. else
  2194. Result := 'D';
  2195. end;
  2196. end;
  2197. function IsVMBFS(AData : String) : Boolean;
  2198. {$IFDEF USE_INLINE} inline; {$ENDIF}
  2199. var
  2200. s : TStringList;
  2201. begin
  2202. Result := False;
  2203. s := TStringList.Create;
  2204. try
  2205. SplitDelimitedString(AData, s, True);
  2206. if s.Count > 4 then begin
  2207. Result := (s[2] = 'F') or (s[2] = 'D');
  2208. if Result then begin
  2209. Result := IsNumeric(s[4]) or (s[4] = '-');
  2210. end;
  2211. end;
  2212. finally
  2213. FreeAndNil(s);
  2214. end;
  2215. end;
  2216. //===== EPLF formats
  2217. function ParseFacts(AData : String; AResults : TStrings;
  2218. const AFactDelim : String = ';'; const ANameDelim : String = ' '): String;
  2219. {$IFDEF USE_INLINE} inline; {$ENDIF}
  2220. var
  2221. LBuf : String;
  2222. begin
  2223. LBuf := Fetch(AData, ANameDelim);
  2224. Result := AData;
  2225. AResults.BeginUpdate;
  2226. try
  2227. AResults.Clear;
  2228. repeat
  2229. AResults.Add(Fetch(LBuf, AFactDelim));
  2230. until LBuf = '';
  2231. finally
  2232. AResults.EndUpdate;
  2233. end;
  2234. end;
  2235. //===== MLSD Parse facts, this has to be different because of different charsets
  2236. function ParseFactsMLS(AData : String; AResults : TStrings;
  2237. const AFactDelim : String = ';'; const ANameDelim : String = ' '): String;
  2238. {$IFDEF USE_INLINE} inline; {$ENDIF}
  2239. var
  2240. LBuf : TIdBytes;
  2241. LCharSet : String;
  2242. LEncoding: IIdTextEncoding;
  2243. begin
  2244. LEncoding := IndyTextEncoding_8Bit;
  2245. LBuf := ToBytes(ParseFacts(AData, AResults, AFactDelim, ANameDelim), LEncoding{$IFDEF STRING_IS_ANSI}, LEncoding{$ENDIF});
  2246. LCharSet := AResults.Values['charset'];
  2247. if LCharSet = '' then begin
  2248. LCharSet := 'UTF-8';
  2249. end;
  2250. try
  2251. Result := BytesToString(LBuf, CharsetToEncoding(LCharSet));
  2252. except
  2253. Result := BytesToString(LBuf, LEncoding);
  2254. end;
  2255. end;
  2256. {Sterling Commerce support routines}
  2257. {
  2258. based on information found in:
  2259. "Connect:Enterprise® UNIX Remote User’s Guide Version 2.1 " Copyright
  2260. 1999, 2002, 2003 Sterling Commerce, Inc.
  2261. }
  2262. const
  2263. CValidFlags = 'ACDEGIMNPRTUXS'; //not sure about the S {Do not translate}
  2264. CWhiteSpace = ' -';
  2265. CSterThreeCharProt : array [0..7] of string =
  2266. ('TCP','BSC','FTP','FTP','HTTP','ASY','AS2','FTS'); {Do not translate}
  2267. CSterOneCharProt : array [0..6] of string =
  2268. ( 'A', 'B', 'F', 'G', 'H', 'Q', 'W'); {Do not translate}
  2269. CSterThreeCharDataFlag : array [0..2] of string =
  2270. ('BIN','ASC','EBC'); {Do not translate}
  2271. CSterOneCharDataFlag : array [0..2] of string =
  2272. ( 'Y', 'Z', 'K'); {Do not translate}
  2273. function RawIsValidSterPattern(const AString : String; AOneChar, AThreeChar : array of String) : Boolean;
  2274. begin
  2275. Result := False;
  2276. if AString = '' then begin
  2277. Exit;
  2278. end;
  2279. if Length(AString) = 3 then begin
  2280. if AString = '---' then begin
  2281. Result := True;
  2282. end;
  2283. if PosInStrArray(AString, AThreeChar) > -1 then begin
  2284. Result := True;
  2285. end;
  2286. end;
  2287. if Length(AString) = 1 then begin
  2288. if PosInStrArray(AString, AOneChar) > -1 then begin
  2289. Result := True;
  2290. end;
  2291. end;
  2292. end;
  2293. function IsValidSterCommFlags(const AString : String) : Boolean;
  2294. var
  2295. i : Integer;
  2296. begin
  2297. Result := False;
  2298. if AString = '' then begin
  2299. Exit;
  2300. end;
  2301. for i := 1 to Length(AString) do begin
  2302. if (IndyPos(AString[i], CValidFlags) = 0) and
  2303. (IndyPos(AString[i], CWhiteSpace) = 0) then begin
  2304. Exit;
  2305. end;
  2306. end;
  2307. Result := True;
  2308. end;
  2309. function IsValidSterCommProt(const AString : String) : Boolean;
  2310. begin
  2311. Result := RawIsValidSterPattern(AString, CSterOneCharProt, CSterThreeCharProt);
  2312. end;
  2313. function IsValidSterCommData(const AString : String) : Boolean;
  2314. begin
  2315. Result := RawIsValidSterPattern(AString, CSterOneCharDataFlag, CSterThreeCharDataFlag);
  2316. end;
  2317. { TIdFTPClientIdentifier }
  2318. constructor TIdFTPClientIdentifier.Create;
  2319. begin
  2320. inherited;
  2321. FExtraFacts := TStringList.Create;
  2322. {$IFDEF HAS_TStringList_CaseSensitive}
  2323. TStringList(FExtraFacts).CaseSensitive := False;
  2324. {$ENDIF}
  2325. end;
  2326. destructor TIdFTPClientIdentifier.Destroy;
  2327. begin
  2328. FreeAndNil(FExtraFacts);
  2329. inherited;
  2330. end;
  2331. procedure TIdFTPClientIdentifier.Assign(Source: TPersistent);
  2332. var
  2333. LSource: TIdFTPClientIdentifier;
  2334. begin
  2335. if Source is TIdFTPClientIdentifier then begin
  2336. LSource := TIdFTPClientIdentifier(Source);
  2337. ClientName := LSource.ClientName;
  2338. ClientVersion := LSource.ClientVersion;
  2339. ClientVendor := LSource.ClientVendor;
  2340. PlatformDescription := LSource.PlatformDescription;
  2341. ExtraFacts.Assign(LSource.ExtraFacts);
  2342. end else begin
  2343. inherited Assign(Source);
  2344. end;
  2345. end;
  2346. //assume syntax such as this:
  2347. //214 Syntax: CLNT <sp> <client-name> <sp> <client-version> [<sp> <optional platform info>] (Set client name)
  2348. function TIdFTPClientIdentifier.GetCLNTParams: String;
  2349. begin
  2350. if FClientName <> '' then begin
  2351. Result := FClientName;
  2352. if FClientVersion <> '' then begin
  2353. Result := Result + ' ' + FClientVersion;
  2354. if FPlatformDescription <> '' then begin
  2355. Result := Result + ' ' + FPlatformDescription;
  2356. end;
  2357. end;
  2358. end else begin
  2359. Result := gsIdProductName + ' ' + gsIdVersion;
  2360. end;
  2361. end;
  2362. function TIdFTPClientIdentifier.GetCSIDParams: String;
  2363. var
  2364. I: Integer;
  2365. begin
  2366. if FClientName <> '' then begin
  2367. Result := 'Name=' + FClientName + '; '; {do not localize}
  2368. if FClientVersion <> '' then begin
  2369. Result := Result + 'Version=' + FClientVersion + '; '; {do not localize}
  2370. end;
  2371. end else begin
  2372. Result := 'Name=' + gsIdProductName + '; Version=' + gsIdVersion + '; '; {do not localize}
  2373. end;
  2374. if FClientVendor <> '' then begin
  2375. Result := Result + 'Vendor=' + FClientVendor + '; '; {do not localize}
  2376. end;
  2377. // PlatformDescription is used only with CLNT not CSID...
  2378. {if FPlatformDescription <> '' then begin
  2379. Result := Result + 'Platform=' + FPlatformDescription + '; ';
  2380. end;}
  2381. for I := 0 to FExtraFacts.Count-1 do begin
  2382. Result := Result + TrimLeft(FExtraFacts[I]) + '; '; {do not localize}
  2383. end;
  2384. if Result <> '' then begin
  2385. SetLength(Result, Length(Result) - 1);
  2386. end;
  2387. end;
  2388. procedure TIdFTPClientIdentifier.SetClientName(const AValue: String);
  2389. begin
  2390. FClientName := Trim(AValue);
  2391. // Don't call Fetch; it prevents multi-word client names in CSID
  2392. end;
  2393. procedure TIdFTPClientIdentifier.SetClientVersion(const AValue: String);
  2394. begin
  2395. FClientVersion := Trim(AValue);
  2396. end;
  2397. procedure TIdFTPClientIdentifier.SetClientVendor(const AValue: String);
  2398. begin
  2399. FClientVendor := Trim(AValue);
  2400. end;
  2401. procedure TIdFTPClientIdentifier.SetPlatformDescription(const AValue: String);
  2402. begin
  2403. FPlatformDescription := Trim(AValue);
  2404. end;
  2405. procedure TIdFTPClientIdentifier.SetExtraFacts(const AValue: TStrings);
  2406. begin
  2407. FExtraFacts.Assign(AValue);
  2408. end;
  2409. procedure TIdFTPClientIdentifier.SetCLNTParams(const AValue : String);
  2410. var
  2411. LBuf : String;
  2412. begin
  2413. LBuf := TrimLeft(AValue);
  2414. ClientName := Fetch(LBuf);
  2415. ClientVersion := Fetch(LBuf);
  2416. ClientVendor := '';
  2417. PlatformDescription := LBuf;
  2418. FExtraFacts.Clear;
  2419. end;
  2420. procedure TIdFTPClientIdentifier.SetCSIDParams(const AValue : String);
  2421. var
  2422. LFacts: TStringList;
  2423. LName, LValue: string;
  2424. I: Integer;
  2425. begin
  2426. FClientName := '';
  2427. FClientVersion := '';
  2428. FClientVendor := '';
  2429. FPlatformDescription := '';
  2430. FExtraFacts.Clear;
  2431. if AValue <> '' then begin
  2432. LFacts := TStringList.Create;
  2433. try
  2434. SplitDelimitedString(AValue, LFacts, True, ';'); {do not localize}
  2435. for I := 0 to LFacts.Count-1 do begin
  2436. LName := LFacts.Names[I];
  2437. LValue := IndyValueFromIndex(LFacts, I);
  2438. case PosInStrArray(LName, ['Name', 'Version', 'Vendor'], False) of {do not localize}
  2439. 0: ClientName := LValue;
  2440. 1: ClientVersion := LValue;
  2441. 2: ClientVendor := LValue;
  2442. // PlatformDescription is used only with CLNT not CSID...
  2443. else
  2444. IndyAddPair(FExtraFacts, LName, LValue);
  2445. end;
  2446. end;
  2447. finally
  2448. LFacts.Free;
  2449. end;
  2450. end;
  2451. end;
  2452. { TIdFTPServerIdentifier }
  2453. constructor TIdFTPServerIdentifier.Create;
  2454. begin
  2455. inherited Create;
  2456. FExtraFacts := TStringList.Create;
  2457. {$IFDEF HAS_TStringList_CaseSensitive}
  2458. TStringList(FExtraFacts).CaseSensitive := False;
  2459. {$ENDIF}
  2460. end;
  2461. destructor TIdFTPServerIdentifier.Destroy;
  2462. begin
  2463. FreeAndNil(FExtraFacts);
  2464. inherited Destroy;
  2465. end;
  2466. procedure TIdFTPServerIdentifier.Assign(Source: TPersistent);
  2467. var
  2468. LSource: TIdFTPServerIdentifier;
  2469. begin
  2470. if Source is TIdFTPServerIdentifier then begin
  2471. LSource := TIdFTPServerIdentifier(Source);
  2472. ServerName := LSource.ServerName;
  2473. ServerVersion := LSource.ServerVersion;
  2474. ServerVendor := LSource.ServerVendor;
  2475. PlatformName := LSource.PlatformName;
  2476. PlatformVersion := LSource.PlatformVersion;
  2477. CaseSensitive := LSource.CaseSensitive;
  2478. DirSeparator := LSource.DirSeparator;
  2479. ExtraFacts.Assign(LSource.ExtraFacts);
  2480. end else begin
  2481. inherited Assign(Source);
  2482. end;
  2483. end;
  2484. procedure TIdFTPServerIdentifier.Clear;
  2485. begin
  2486. FServerName := '';
  2487. FServerVersion := '';
  2488. FServerVendor := '';
  2489. FPlatformName := '';
  2490. FPlatformVersion := '';
  2491. FCaseSensitive := True;
  2492. FDirSeparator := '/'; {do not localize}
  2493. FExtraFacts.Clear;
  2494. end;
  2495. function TIdFTPServerIdentifier.GetCSIDParams;
  2496. var
  2497. I: Integer;
  2498. begin
  2499. if FServerName <> '' then begin
  2500. Result := 'Name=' + FServerName + '; '; {do not localize}
  2501. end;
  2502. if FServerVersion <> '' then begin
  2503. Result := Result + 'Version=' + FServerVersion + '; '; {do not localize}
  2504. end;
  2505. if FServerVendor <> '' then begin
  2506. Result := Result + 'Vendor=' + FServerVendor + '; '; {do not localize}
  2507. end;
  2508. if FPlatformName <> '' then begin
  2509. Result := Result + 'OS='+ FPlatformName + '; '; {do not localize}
  2510. end;
  2511. if FPlatformVersion <> '' then begin
  2512. Result := Result + 'OSVer=' + FPlatformVersion + '; '; {do not localize}
  2513. end;
  2514. //https://solarwindscore.my.site.com/SuccessCenter/s/article/CSID-FTP-command
  2515. //states that the CaseSensitive and DirSep facts are required to be reported.
  2516. Result := Result + 'CaseSensitive=' + iif(FCaseSensitive, '1', '0') + '; '; {do not localize}
  2517. Result := Result + 'DirSep=' + FDirSeparator + '; '; {do not localize}
  2518. for I := 0 to FExtraFacts.Count -1 do begin
  2519. Result := Result + TrimLeft(FExtraFacts[I]) + '; '; {do not localize}
  2520. end;
  2521. SetLength(Result, Length(Result) - 1);
  2522. end;
  2523. procedure TIdFTPServerIdentifier.SetCSIDParams(const AValue: String);
  2524. var
  2525. LFacts: TStringList;
  2526. LName, LValue: string;
  2527. I: Integer;
  2528. begin
  2529. Clear;
  2530. if AValue <> '' then begin
  2531. LFacts := TStringList.Create;
  2532. try
  2533. SplitDelimitedString(AValue, LFacts, True, ';'); {do not localize}
  2534. for I := 0 to LFacts.Count-1 do begin
  2535. LName := LFacts.Names[I];
  2536. LValue := IndyValueFromIndex(LFacts, I);
  2537. case PosInStrArray(LName, ['Name', 'Version', 'Vendor', 'OS', 'OSVer', 'CaseSensitive', 'DirSep'], False) of {do not localize}
  2538. 0: ServerName := LValue;
  2539. 1: ServerVersion := LValue;
  2540. 2: ServerVendor := LValue;
  2541. 3: PlatformName := LValue;
  2542. 4: PlatformVersion := LValue;
  2543. 5: CaseSensitive := LValue = '1'; {do not localize}
  2544. 6: begin
  2545. if LValue <> '' then begin
  2546. DirSeparator := LValue[1];
  2547. end;
  2548. end;
  2549. else
  2550. IndyAddPair(FExtraFacts, LName, LValue);
  2551. end;
  2552. end;
  2553. finally
  2554. LFacts.Free;
  2555. end;
  2556. end;
  2557. end;
  2558. procedure TIdFTPServerIdentifier.SetServerName(const AValue: String);
  2559. begin
  2560. FServerName := Trim(AValue);
  2561. // Don't call Fetch; it prevents multi-word client names in CSID
  2562. end;
  2563. procedure TIdFTPServerIdentifier.SetServerVersion(const AValue: String);
  2564. begin
  2565. FServerVersion := Trim(AValue);
  2566. end;
  2567. procedure TIdFTPServerIdentifier.SetServerVendor(const AValue: String);
  2568. begin
  2569. FServerVendor := Trim(AValue);
  2570. end;
  2571. procedure TIdFTPServerIdentifier.SetPlatformName(const AValue: String);
  2572. begin
  2573. FPlatformName := Trim(AValue);
  2574. end;
  2575. procedure TIdFTPServerIdentifier.SetPlatformVersion(const AValue: String);
  2576. begin
  2577. FPlatformVersion := Trim(AValue);
  2578. end;
  2579. procedure TIdFTPServerIdentifier.SetExtraFacts(const AValue: TStrings);
  2580. begin
  2581. FExtraFacts.Assign(AValue);
  2582. end;
  2583. end.