SftpFileSystem.cpp 101 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475
  1. //---------------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. #include "SftpFileSystem.h"
  5. #include <sftp.h>
  6. #include "PuttyIntf.h"
  7. #include "Common.h"
  8. #include "Interface.h"
  9. #include "Terminal.h"
  10. #include "TextsCore.h"
  11. #include <memory>
  12. //---------------------------------------------------------------------------
  13. #pragma package(smart_init)
  14. //---------------------------------------------------------------------------
  15. #define FILE_OPERATION_LOOP_EX(ALLOW_SKIP, MESSAGE, OPERATION) \
  16. FILE_OPERATION_LOOP_CUSTOM(FTerminal, ALLOW_SKIP, MESSAGE, OPERATION)
  17. //---------------------------------------------------------------------------
  18. // additional constants for SFTP protocol not defined in Putty's sftp.h
  19. #define SSH_FXP_READLINK 19
  20. #define SSH_FXP_SYMLINK 20
  21. #define SSH_FILEXFER_ATTR_ACCESSTIME 0x00000008
  22. #define SSH_FILEXFER_ATTR_CREATETIME 0x00000010
  23. #define SSH_FILEXFER_ATTR_MODIFYTIME 0x00000020
  24. #define SSH_FILEXFER_ATTR_OWNERGROUP 0x00000080
  25. #define SSH_FILEXFER_ATTR_SUBSECOND_TIMES 0x00000100
  26. #define SSH_FILEXFER_ATTR_COMMON \
  27. (SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_OWNERGROUP | \
  28. SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACCESSTIME | \
  29. SSH_FILEXFER_ATTR_MODIFYTIME)
  30. #define SSH_FILEXFER_TYPE_REGULAR 1
  31. #define SSH_FILEXFER_TYPE_DIRECTORY 2
  32. #define SSH_FILEXFER_TYPE_SYMLINK 3
  33. #define SSH_FILEXFER_TYPE_SPECIAL 4
  34. #define SSH_FILEXFER_TYPE_UNKNOWN 5
  35. #define SSH_FXF_TEXT 0x00000040
  36. #define SFTP_MAX_PACKET_LEN 102400
  37. //---------------------------------------------------------------------------
  38. #define SFTP_EXT_WINSCP "[email protected]"
  39. #define SFTP_EXT_OWNER_GROUP "owner-group-query@generic-extensions"
  40. #define SFTP_EXT_OWNER_GROUP_REPLY "owner-group-query-reply@generic-extensions"
  41. #define SFTP_EXT_SOFTWARE "software@generic-extensions"
  42. #define SFTP_EXT_SOFTWARE_OS "Microsoft Windows"
  43. #define SFTP_EXT_NEWLINE "newline"
  44. #define SFTP_EXT_SUPPORTED "supported"
  45. //---------------------------------------------------------------------------
  46. #define OGQ_LIST_OWNERS 0x01
  47. #define OGQ_LIST_GROUPS 0x02
  48. //---------------------------------------------------------------------------
  49. const int SFTPMinVersion = 0;
  50. const int SFTPMaxVersion = 4;
  51. const int SFTPNoMessageNumber = -1;
  52. const int asNo = 0;
  53. const int asOK = 1 << SSH_FX_OK;
  54. const int asEOF = 1 << SSH_FX_EOF;
  55. const int asOpUnsupported = 1 << SSH_FX_OP_UNSUPPORTED;
  56. const int asNoSuchFile = 1 << SSH_FX_NO_SUCH_FILE;
  57. const int asAll = 0xFFFF;
  58. const int tfFirstLevel = 0x01;
  59. const int tfNewDirectory = 0x02;
  60. //---------------------------------------------------------------------------
  61. #define GET_32BIT(cp) \
  62. (((unsigned long)(unsigned char)(cp)[0] << 24) | \
  63. ((unsigned long)(unsigned char)(cp)[1] << 16) | \
  64. ((unsigned long)(unsigned char)(cp)[2] << 8) | \
  65. ((unsigned long)(unsigned char)(cp)[3]))
  66. #define PUT_32BIT(cp, value) { \
  67. (cp)[0] = (unsigned char)((value) >> 24); \
  68. (cp)[1] = (unsigned char)((value) >> 16); \
  69. (cp)[2] = (unsigned char)((value) >> 8); \
  70. (cp)[3] = (unsigned char)(value); }
  71. //---------------------------------------------------------------------------
  72. #define SFTP_PACKET_ALLOC_DELTA 256
  73. //---------------------------------------------------------------------------
  74. #pragma warn -inl
  75. //---------------------------------------------------------------------------
  76. struct TSFTPSupport
  77. {
  78. unsigned int AttributeMask;
  79. unsigned int AttributeBits;
  80. unsigned int OpenFlags;
  81. unsigned int AccessMask;
  82. unsigned int MaxReadSize;
  83. TStrings * Extensions;
  84. };
  85. //---------------------------------------------------------------------------
  86. class TSFTPPacket
  87. {
  88. public:
  89. TSFTPPacket()
  90. {
  91. Init();
  92. }
  93. TSFTPPacket(const TSFTPPacket & Source)
  94. {
  95. Init();
  96. *this = Source;
  97. }
  98. TSFTPPacket(unsigned char AType)
  99. {
  100. Init();
  101. ChangeType(AType);
  102. }
  103. TSFTPPacket(const char * Source, unsigned int Len)
  104. {
  105. Init();
  106. FLength = Len;
  107. Capacity = FLength;
  108. memcpy(Data, Source, Len);
  109. }
  110. TSFTPPacket(const AnsiString & Source)
  111. {
  112. Init();
  113. FLength = Source.Length();
  114. Capacity = FLength;
  115. memcpy(Data, Source.c_str(), Source.Length());
  116. }
  117. ~TSFTPPacket()
  118. {
  119. delete[] FData;
  120. if (FReservedBy) FReservedBy->UnreserveResponse(this);
  121. }
  122. void ChangeType(unsigned char AType)
  123. {
  124. FPosition = 0;
  125. FLength = 0;
  126. Capacity = 0;
  127. FType = AType;
  128. AddByte(FType);
  129. if ((FType != 1) && (FType != SSH_FXP_INIT))
  130. {
  131. AssignNumber();
  132. AddCardinal(FMessageNumber);
  133. }
  134. }
  135. void Reuse()
  136. {
  137. AssignNumber();
  138. assert(Length >= 5);
  139. // duplicated in AddCardinal()
  140. unsigned char Buf[4];
  141. PUT_32BIT(Buf, FMessageNumber);
  142. memcpy(FData + 1, Buf, sizeof(Buf));
  143. }
  144. void AddByte(unsigned char Value)
  145. {
  146. Add(&Value, sizeof(Value));
  147. }
  148. void AddCardinal(unsigned long Value)
  149. {
  150. // duplicated in Reuse()
  151. unsigned char Buf[4];
  152. PUT_32BIT(Buf, Value);
  153. Add(&Buf, sizeof(Buf));
  154. }
  155. void AddInt64(__int64 Value)
  156. {
  157. AddCardinal((unsigned long)(Value >> 32));
  158. AddCardinal((unsigned long)(Value & 0xFFFFFFFF));
  159. }
  160. void AddData(const void * Data, int ALength)
  161. {
  162. AddCardinal(ALength);
  163. Add(Data, ALength);
  164. }
  165. void AddString(const AnsiString Value)
  166. {
  167. AddCardinal(Value.Length());
  168. Add(Value.c_str(), Value.Length());
  169. }
  170. void AddProperties(unsigned short * Rights, AnsiString * Owner,
  171. AnsiString * Group, unsigned long * MTime, unsigned long * ATime,
  172. __int64 * Size, bool IsDirectory, int Version)
  173. {
  174. int Flags = 0;
  175. if (Size != NULL)
  176. {
  177. Flags |= SSH_FILEXFER_ATTR_SIZE;
  178. }
  179. if ((Owner != NULL) || (Group != NULL))
  180. {
  181. Flags |= SSH_FILEXFER_ATTR_OWNERGROUP;
  182. }
  183. if (Rights != NULL)
  184. {
  185. Flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
  186. }
  187. if ((Version < 4) && ((MTime != NULL) || (ATime != NULL)))
  188. {
  189. Flags |= SSH_FILEXFER_ATTR_ACMODTIME;
  190. }
  191. if ((Version >= 4) && (ATime != NULL))
  192. {
  193. Flags |= SSH_FILEXFER_ATTR_ACCESSTIME;
  194. }
  195. if ((Version >= 4) && (MTime != NULL))
  196. {
  197. Flags |= SSH_FILEXFER_ATTR_MODIFYTIME;
  198. }
  199. AddCardinal(Flags);
  200. if (Version >= 4)
  201. {
  202. AddByte(static_cast<unsigned char>(IsDirectory ?
  203. SSH_FILEXFER_TYPE_DIRECTORY : SSH_FILEXFER_TYPE_REGULAR));
  204. }
  205. if (Size != NULL)
  206. {
  207. AddInt64(*Size);
  208. }
  209. if ((Owner != NULL) || (Group != NULL))
  210. {
  211. assert(Version >= 4);
  212. AddString(Owner != NULL ? *Owner : AnsiString());
  213. AddString(Group != NULL ? *Group : AnsiString());
  214. }
  215. if (Rights != NULL)
  216. {
  217. AddCardinal(*Rights);
  218. }
  219. if ((Version < 4) && ((MTime != NULL) || (ATime != NULL)))
  220. {
  221. AddCardinal(ATime != NULL ? *ATime : *MTime);
  222. AddCardinal(MTime != NULL ? *MTime : *ATime);
  223. }
  224. if ((Version >= 4) && (ATime != NULL))
  225. {
  226. AddInt64(*ATime);
  227. }
  228. if ((Version >= 4) && (MTime != NULL))
  229. {
  230. AddInt64(*MTime);
  231. }
  232. }
  233. void AddProperties(const TRemoteProperties * Properties,
  234. unsigned short BaseRights, bool IsDirectory, int Version)
  235. {
  236. enum { valNone = 0, valRights = 0x01, valOwner = 0x02, valGroup = 0x04 }
  237. Valid = valNone;
  238. unsigned short RightsNum = 0;
  239. AnsiString Owner;
  240. AnsiString Group;
  241. if (Properties != NULL)
  242. {
  243. if (Properties->Valid.Contains(vpGroup))
  244. {
  245. Valid |= valGroup;
  246. Group = Properties->Group;
  247. }
  248. if (Properties->Valid.Contains(vpOwner))
  249. {
  250. Valid |= valOwner;
  251. Owner = Properties->Owner;
  252. }
  253. if (Properties->Valid.Contains(vpRights))
  254. {
  255. Valid |= valRights;
  256. TRights Rights = BaseRights;
  257. Rights |= Properties->Rights.NumberSet;
  258. Rights &= (unsigned short)~Properties->Rights.NumberUnset;
  259. if (IsDirectory && Properties->AddXToDirectories)
  260. {
  261. Rights.AddExecute();
  262. }
  263. RightsNum = Rights;
  264. }
  265. }
  266. AddProperties(
  267. Valid & valRights ? &RightsNum : NULL,
  268. Valid & valOwner ? &Owner : NULL,
  269. Valid & valGroup ? &Group : NULL,
  270. NULL, NULL, NULL, IsDirectory, Version);
  271. }
  272. char GetByte()
  273. {
  274. assert(FPosition <= FLength - sizeof(char));
  275. char Result = FData[FPosition];
  276. FPosition++;
  277. return Result;
  278. }
  279. unsigned long GetCardinal()
  280. {
  281. unsigned long Result;
  282. assert(FPosition <= FLength - sizeof(Result));
  283. Result = GET_32BIT(FData + FPosition);
  284. FPosition += sizeof(Result);
  285. return Result;
  286. }
  287. __int64 GetInt64()
  288. {
  289. __int64 Hi = GetCardinal();
  290. __int64 Lo = GetCardinal();
  291. return (Hi << 32) + Lo;
  292. }
  293. AnsiString GetString()
  294. {
  295. AnsiString Result;
  296. unsigned long Len = GetCardinal();
  297. Result.SetLength(Len);
  298. assert(FLength >= Len && FPosition <= FLength - Len);
  299. memcpy(Result.c_str(), FData + FPosition, Len);
  300. FPosition += Len;
  301. return Result;
  302. }
  303. void GetFile(TRemoteFile * File, int Version, bool ConsiderDST)
  304. {
  305. assert(File);
  306. unsigned int Flags;
  307. AnsiString ListingStr;
  308. unsigned long Permissions;
  309. bool ParsingFailed = false;
  310. if (Type != SSH_FXP_ATTRS)
  311. {
  312. File->FileName = GetString();
  313. if (Version < 4)
  314. {
  315. ListingStr = GetString();
  316. }
  317. }
  318. Flags = GetCardinal();
  319. if (Version >= 4)
  320. {
  321. char FXType = GetByte();
  322. static char Types[] = "-DLSU";
  323. if (FXType < 1 || FXType > (char)strlen(Types))
  324. {
  325. throw Exception(FMTLOAD(SFTP_UNKNOWN_FILE_TYPE, (int(FXType))));
  326. }
  327. File->Type = Types[FXType-1];
  328. }
  329. if (Flags & SSH_FILEXFER_ATTR_SIZE)
  330. {
  331. File->Size = GetInt64();
  332. }
  333. if (Flags & SSH_FILEXFER_ATTR_UIDGID)
  334. {
  335. assert(Version < 4);
  336. GetCardinal(); // skip UID
  337. GetCardinal(); // skip GUID
  338. }
  339. if (Flags & SSH_FILEXFER_ATTR_OWNERGROUP)
  340. {
  341. assert(Version >= 4);
  342. File->Owner = GetString();
  343. File->Group = GetString();
  344. }
  345. if (Flags & SSH_FILEXFER_ATTR_PERMISSIONS)
  346. {
  347. Permissions = GetCardinal();
  348. }
  349. if (Version < 4)
  350. {
  351. if (Flags & SSH_FILEXFER_ATTR_ACMODTIME)
  352. {
  353. File->LastAccess = UnixToDateTime(GetCardinal(), ConsiderDST);
  354. File->Modification = UnixToDateTime(GetCardinal(), ConsiderDST);
  355. }
  356. }
  357. else
  358. {
  359. if (Flags & SSH_FILEXFER_ATTR_ACCESSTIME)
  360. {
  361. File->LastAccess = UnixToDateTime((unsigned long)GetInt64(), ConsiderDST);
  362. if (Flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES)
  363. {
  364. GetCardinal(); // skip access time subseconds
  365. }
  366. }
  367. else
  368. {
  369. File->LastAccess = Now();
  370. }
  371. if (Flags & SSH_FILEXFER_ATTR_CREATETIME)
  372. {
  373. GetInt64(); // skip create time
  374. if (Flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES)
  375. {
  376. GetCardinal(); // skip create time subseconds
  377. }
  378. }
  379. if (Flags & SSH_FILEXFER_ATTR_MODIFYTIME)
  380. {
  381. File->Modification = UnixToDateTime((unsigned long)GetInt64(), ConsiderDST);
  382. if (Flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES)
  383. {
  384. GetCardinal(); // skip modification time subseconds
  385. }
  386. }
  387. else
  388. {
  389. File->Modification = Now();
  390. }
  391. }
  392. if ((Version < 4) && (Type != SSH_FXP_ATTRS))
  393. {
  394. try
  395. {
  396. // update permissions and user/group name
  397. // modification time and filename is ignored
  398. File->ListingStr = ListingStr;
  399. }
  400. catch(...)
  401. {
  402. // ignore any error while parsing listing line,
  403. // SFTP specification do not recommend to parse it
  404. ParsingFailed = true;
  405. }
  406. }
  407. if (Type == SSH_FXP_ATTRS || Version >= 4 || ParsingFailed)
  408. {
  409. File->Rights->Number = (unsigned short)(Permissions & TRights::rfAllSpecials);
  410. if (Version < 4)
  411. {
  412. File->Type = (Permissions & TRights::rfDirectory ? FILETYPE_DIRECTORY : '-');
  413. }
  414. }
  415. File->Complete();
  416. // TODO: read extended attributes (Flags & SSH_FILEXFER_ATTR_EXTENDED)
  417. // Format: Count=Cardinal, Count*(Name=String, Value=String)
  418. }
  419. void DataUpdated(int ALength)
  420. {
  421. FPosition = 0;
  422. FLength = ALength;
  423. FType = GetByte();
  424. if (FType != SSH_FXP_VERSION)
  425. {
  426. FMessageNumber = GetCardinal();
  427. }
  428. else
  429. {
  430. FMessageNumber = SFTPNoMessageNumber;
  431. }
  432. }
  433. AnsiString __fastcall Dump() const
  434. {
  435. AnsiString Result;
  436. for (unsigned int Index = 0; Index < Length; Index++)
  437. {
  438. Result += IntToHex(int((unsigned char)Data[Index]), 2) + ",";
  439. if (((Index + 1) % 25) == 0)
  440. {
  441. Result += "\n";
  442. }
  443. }
  444. return Result;
  445. }
  446. TSFTPPacket & operator = (const TSFTPPacket & Source)
  447. {
  448. Capacity = 0;
  449. Add(Source.Data, Source.Length);
  450. DataUpdated(Source.Length);
  451. FPosition = Source.FPosition;
  452. return *this;
  453. }
  454. __property unsigned int Length = { read = FLength };
  455. __property char * Data = { read = FData };
  456. __property char * NextData = { read = GetNextData };
  457. __property unsigned int Capacity = { read = FCapacity, write = SetCapacity };
  458. __property unsigned char Type = { read = FType };
  459. __property unsigned char RequestType = { read = GetRequestType };
  460. __property unsigned int MessageNumber = { read = FMessageNumber, write = FMessageNumber };
  461. __property TSFTPFileSystem * ReservedBy = { read = FReservedBy, write = FReservedBy };
  462. __property AnsiString TypeName = { read = GetTypeName };
  463. private:
  464. char * FData;
  465. unsigned int FLength;
  466. unsigned int FCapacity;
  467. unsigned int FPosition;
  468. unsigned char FType;
  469. unsigned int FMessageNumber;
  470. TSFTPFileSystem * FReservedBy;
  471. static int FMessageCounter;
  472. void Init()
  473. {
  474. FData = NULL;
  475. FCapacity = 0;
  476. FLength = 0;
  477. FPosition = 0;
  478. FMessageNumber = SFTPNoMessageNumber;
  479. FType = -1;
  480. FReservedBy = NULL;
  481. }
  482. void AssignNumber()
  483. {
  484. FMessageNumber = (FMessageCounter << 8) + FType;
  485. FMessageCounter++;
  486. }
  487. unsigned char GetRequestType()
  488. {
  489. if (FMessageNumber != SFTPNoMessageNumber)
  490. {
  491. return (unsigned char)(FMessageNumber & 0xFF);
  492. }
  493. else
  494. {
  495. assert(Type == SSH_FXP_VERSION);
  496. return SSH_FXP_INIT;
  497. }
  498. }
  499. void Add(const void * AData, int ALength)
  500. {
  501. if (Length + ALength > Capacity)
  502. {
  503. Capacity = Length + ALength + SFTP_PACKET_ALLOC_DELTA;
  504. }
  505. memcpy(FData + Length, AData, ALength);
  506. FLength += ALength;
  507. }
  508. void SetCapacity(unsigned int ACapacity)
  509. {
  510. if (ACapacity != Capacity)
  511. {
  512. FCapacity = ACapacity;
  513. if (FCapacity > 0)
  514. {
  515. char * NData = new char[FCapacity];
  516. if (FData)
  517. {
  518. memcpy(NData, FData, (FLength < FCapacity ? FLength : FCapacity));
  519. delete[] FData;
  520. }
  521. FData = NData;
  522. }
  523. else
  524. {
  525. if (FData) delete[] FData;
  526. FData = NULL;
  527. }
  528. if (FLength > FCapacity) FLength = FCapacity;
  529. }
  530. }
  531. AnsiString GetTypeName() const
  532. {
  533. #define TYPE_CASE(TYPE) case TYPE: return #TYPE
  534. switch (Type) {
  535. TYPE_CASE(SSH_FXP_INIT);
  536. TYPE_CASE(SSH_FXP_VERSION);
  537. TYPE_CASE(SSH_FXP_OPEN);
  538. TYPE_CASE(SSH_FXP_CLOSE);
  539. TYPE_CASE(SSH_FXP_READ);
  540. TYPE_CASE(SSH_FXP_WRITE);
  541. TYPE_CASE(SSH_FXP_LSTAT);
  542. TYPE_CASE(SSH_FXP_FSTAT);
  543. TYPE_CASE(SSH_FXP_SETSTAT);
  544. TYPE_CASE(SSH_FXP_FSETSTAT);
  545. TYPE_CASE(SSH_FXP_OPENDIR);
  546. TYPE_CASE(SSH_FXP_READDIR);
  547. TYPE_CASE(SSH_FXP_REMOVE);
  548. TYPE_CASE(SSH_FXP_MKDIR);
  549. TYPE_CASE(SSH_FXP_RMDIR);
  550. TYPE_CASE(SSH_FXP_REALPATH);
  551. TYPE_CASE(SSH_FXP_STAT);
  552. TYPE_CASE(SSH_FXP_RENAME);
  553. TYPE_CASE(SSH_FXP_READLINK);
  554. TYPE_CASE(SSH_FXP_SYMLINK);
  555. TYPE_CASE(SSH_FXP_STATUS);
  556. TYPE_CASE(SSH_FXP_HANDLE);
  557. TYPE_CASE(SSH_FXP_DATA);
  558. TYPE_CASE(SSH_FXP_NAME);
  559. TYPE_CASE(SSH_FXP_ATTRS);
  560. TYPE_CASE(SSH_FXP_EXTENDED);
  561. TYPE_CASE(SSH_FXP_EXTENDED_REPLY);
  562. default:
  563. return FORMAT("Unknown message (%d)", (int(Type)));
  564. }
  565. }
  566. char * GetNextData()
  567. {
  568. return FPosition < FLength ? FData + FPosition : NULL;
  569. }
  570. };
  571. //---------------------------------------------------------------------------
  572. int TSFTPPacket::FMessageCounter = 0;
  573. //---------------------------------------------------------------------------
  574. class TSFTPQueue
  575. {
  576. public:
  577. __fastcall TSFTPQueue(TSFTPFileSystem * AFileSystem)
  578. {
  579. FFileSystem = AFileSystem;
  580. assert(FFileSystem);
  581. FRequests = new TList();
  582. FResponses = new TList();
  583. }
  584. __fastcall ~TSFTPQueue()
  585. {
  586. try
  587. {
  588. if (FFileSystem->FTerminal->Active)
  589. {
  590. Dispose();
  591. }
  592. }
  593. __finally
  594. {
  595. TSFTPPacket * Request;
  596. TSFTPPacket * Response;
  597. assert(FResponses->Count == FRequests->Count);
  598. for (int Index = 0; Index < FRequests->Count; Index++)
  599. {
  600. Request = static_cast<TSFTPPacket*>(FRequests->Items[Index]);
  601. assert(Request);
  602. delete Request;
  603. Response = static_cast<TSFTPPacket*>(FResponses->Items[Index]);
  604. assert(Response);
  605. delete Response;
  606. }
  607. delete FRequests;
  608. delete FResponses;
  609. }
  610. }
  611. bool __fastcall Init(int QueueLen)
  612. {
  613. bool Result = false;
  614. while ((QueueLen > 0) && SendRequest())
  615. {
  616. Result = true;
  617. QueueLen--;
  618. }
  619. return Result;
  620. }
  621. void __fastcall Dispose()
  622. {
  623. assert(FFileSystem->FTerminal->Active);
  624. TSFTPPacket * Request;
  625. TSFTPPacket * Response;
  626. while (FRequests->Count)
  627. {
  628. assert(FResponses->Count);
  629. Request = static_cast<TSFTPPacket*>(FRequests->Items[0]);
  630. assert(Request);
  631. Response = static_cast<TSFTPPacket*>(FResponses->Items[0]);
  632. assert(Response);
  633. try
  634. {
  635. FFileSystem->ReceiveResponse(Request, Response);
  636. }
  637. catch(Exception & E)
  638. {
  639. if (FFileSystem->FTerminal->Active)
  640. {
  641. FFileSystem->FTerminal->LogEvent("Error while disposing the SFTP queue.");
  642. FFileSystem->FTerminal->DoHandleExtendedException(&E);
  643. }
  644. else
  645. {
  646. FFileSystem->FTerminal->LogEvent("Fatal error while disposing the SFTP queue.");
  647. throw;
  648. }
  649. }
  650. FRequests->Delete(0);
  651. delete Request;
  652. FResponses->Delete(0);
  653. delete Response;
  654. }
  655. }
  656. bool __fastcall ReceivePacket(TSFTPPacket * Packet,
  657. int ExpectedType = -1, int AllowStatus = -1)
  658. {
  659. assert(FRequests->Count);
  660. bool Result;
  661. TSFTPPacket * Request = NULL;
  662. TSFTPPacket * Response = NULL;
  663. try
  664. {
  665. Request = static_cast<TSFTPPacket*>(FRequests->Items[0]);
  666. FRequests->Delete(0);
  667. assert(Request);
  668. Response = static_cast<TSFTPPacket*>(FResponses->Items[0]);
  669. FResponses->Delete(0);
  670. assert(Response);
  671. FFileSystem->ReceiveResponse(Request, Response,
  672. ExpectedType, AllowStatus);
  673. if (Packet)
  674. {
  675. *Packet = *Response;
  676. }
  677. Result = SendNext(Response);
  678. if (Result)
  679. {
  680. Result = SendRequest();
  681. }
  682. }
  683. __finally
  684. {
  685. delete Request;
  686. delete Response;
  687. }
  688. return Result;
  689. }
  690. bool __fastcall Next(int ExpectedType = -1, int AllowStatus = -1)
  691. {
  692. return ReceivePacket(NULL, ExpectedType, AllowStatus);
  693. }
  694. protected:
  695. virtual bool __fastcall InitRequest(TSFTPPacket * Request) = 0;
  696. virtual bool __fastcall SendNext(TSFTPPacket * Request) = 0;
  697. virtual void __fastcall SendPacket(TSFTPPacket * Packet)
  698. {
  699. FFileSystem->SendPacket(Packet);
  700. }
  701. protected:
  702. TList * FRequests;
  703. TList * FResponses;
  704. TSFTPFileSystem * FFileSystem;
  705. private:
  706. bool __fastcall SendRequest()
  707. {
  708. TSFTPPacket * Request = NULL;
  709. try
  710. {
  711. Request = new TSFTPPacket();
  712. if (!InitRequest(Request))
  713. {
  714. delete Request;
  715. Request = NULL;
  716. }
  717. }
  718. catch(...)
  719. {
  720. delete Request;
  721. throw;
  722. }
  723. if (Request != NULL)
  724. {
  725. TSFTPPacket * Response = new TSFTPPacket();
  726. FRequests->Add(Request);
  727. FResponses->Add(Response);
  728. SendPacket(Request);
  729. FFileSystem->ReserveResponse(Request, Response);
  730. }
  731. return (Request != NULL);
  732. }
  733. };
  734. //---------------------------------------------------------------------------
  735. class TSFTPTransferQueue : public TSFTPQueue
  736. {
  737. public:
  738. TSFTPTransferQueue(TSFTPFileSystem * AFileSystem) : TSFTPQueue(AFileSystem)
  739. {
  740. }
  741. protected:
  742. __int64 FTransfered;
  743. AnsiString FHandle;
  744. unsigned long FBlockSize;
  745. };
  746. //---------------------------------------------------------------------------
  747. class TSFTPDownloadQueue : public TSFTPTransferQueue
  748. {
  749. public:
  750. TSFTPDownloadQueue(TSFTPFileSystem * AFileSystem) :
  751. TSFTPTransferQueue(AFileSystem)
  752. {
  753. }
  754. bool __fastcall Init(int QueueLen, const AnsiString AHandle,
  755. unsigned long ABlockSize, __int64 ATransfered)
  756. {
  757. FHandle = AHandle;
  758. FBlockSize = ABlockSize;
  759. FTransfered = ATransfered;
  760. return TSFTPTransferQueue::Init(QueueLen);
  761. }
  762. void __fastcall InitFillGapRequest(__int64 Offset, unsigned long Missing,
  763. TSFTPPacket * Packet)
  764. {
  765. InitRequest(Packet, Offset, Missing);
  766. }
  767. protected:
  768. virtual bool __fastcall InitRequest(TSFTPPacket * Request)
  769. {
  770. InitRequest(Request, FTransfered, FBlockSize);
  771. FTransfered += FBlockSize;
  772. return true;
  773. }
  774. void __fastcall InitRequest(TSFTPPacket * Request, __int64 Offset,
  775. unsigned long Size)
  776. {
  777. Request->ChangeType(SSH_FXP_READ);
  778. Request->AddString(FHandle);
  779. Request->AddInt64(Offset);
  780. Request->AddCardinal(Size);
  781. }
  782. virtual bool __fastcall SendNext(TSFTPPacket * Request)
  783. {
  784. return (Request->Type == SSH_FXP_DATA);
  785. }
  786. };
  787. //---------------------------------------------------------------------------
  788. class TSFTPUploadQueue : public TSFTPTransferQueue
  789. {
  790. public:
  791. TSFTPUploadQueue(TSFTPFileSystem * AFileSystem) :
  792. TSFTPTransferQueue(AFileSystem)
  793. {
  794. FFile = NULL;
  795. OperationProgress = NULL;
  796. FLastBlockSize = 0;
  797. }
  798. bool __fastcall Init(int QueueLen, const AnsiString AFileName,
  799. HANDLE AFile, TFileOperationProgressType * AOperationProgress,
  800. const AnsiString AHandle, unsigned long ABlockSize, __int64 ATransfered)
  801. {
  802. FFileName = AFileName;
  803. FFile = AFile;
  804. OperationProgress = AOperationProgress;
  805. FHandle = AHandle;
  806. FBlockSize = ABlockSize;
  807. FTransfered = ATransfered;
  808. return TSFTPTransferQueue::Init(QueueLen);
  809. }
  810. protected:
  811. virtual bool __fastcall InitRequest(TSFTPPacket * Request)
  812. {
  813. TTerminal * FTerminal = FFileSystem->FTerminal;
  814. // Buffer for one block of data
  815. TFileBuffer BlockBuf;
  816. FILE_OPERATION_LOOP(FMTLOAD(READ_ERROR, (FFileName)),
  817. BlockBuf.LoadFile(FFile, FBlockSize, false);
  818. );
  819. bool Result = (BlockBuf.Size != 0);
  820. if (Result)
  821. {
  822. OperationProgress->AddLocalyUsed(BlockBuf.Size);
  823. // We do ASCII transfer: convert EOL of current block
  824. if (OperationProgress->AsciiTransfer)
  825. {
  826. __int64 PrevBufSize = BlockBuf.Size;
  827. BlockBuf.Convert(FTerminal->Configuration->LocalEOLType,
  828. FFileSystem->GetEOL(), cpRemoveCtrlZ);
  829. // update transfer size with difference arised from EOL conversion
  830. OperationProgress->ChangeTransferSize(OperationProgress->TransferSize -
  831. PrevBufSize + BlockBuf.Size);
  832. }
  833. Request->ChangeType(SSH_FXP_WRITE);
  834. Request->AddString(FHandle);
  835. Request->AddInt64(FTransfered);
  836. Request->AddData(BlockBuf.Data, BlockBuf.Size);
  837. FLastBlockSize = BlockBuf.Size;
  838. FTransfered += BlockBuf.Size;
  839. }
  840. return Result;
  841. }
  842. virtual void __fastcall SendPacket(TSFTPPacket * Packet)
  843. {
  844. TSFTPTransferQueue::SendPacket(Packet);
  845. OperationProgress->AddTransfered(FLastBlockSize);
  846. }
  847. virtual bool __fastcall SendNext(TSFTPPacket * Request)
  848. {
  849. return true;
  850. }
  851. private:
  852. HANDLE FFile;
  853. TFileOperationProgressType * OperationProgress;
  854. AnsiString FFileName;
  855. unsigned long FLastBlockSize;
  856. };
  857. //---------------------------------------------------------------------------
  858. #pragma warn .inl
  859. //---------------------------------------------------------------------------
  860. class TSFTPBusy
  861. {
  862. public:
  863. __fastcall TSFTPBusy(TSFTPFileSystem * FileSystem)
  864. {
  865. FFileSystem = FileSystem;
  866. assert(FFileSystem != NULL);
  867. FFileSystem->BusyStart();
  868. }
  869. __fastcall ~TSFTPBusy()
  870. {
  871. FFileSystem->BusyEnd();
  872. }
  873. private:
  874. TSFTPFileSystem * FFileSystem;
  875. };
  876. //===========================================================================
  877. struct TOpenRemoteFileParams
  878. {
  879. int LocalFileAttrs;
  880. AnsiString RemoteFileName;
  881. TFileOperationProgressType * OperationProgress;
  882. const TCopyParamType * CopyParam;
  883. int Params;
  884. bool Resume;
  885. TSFTPOverwriteMode OverwriteMode;
  886. __int64 DestFileSize; // output
  887. AnsiString RemoteFileHandle; // output
  888. TOverwriteFileParams * FileParams;
  889. };
  890. //---------------------------------------------------------------------------
  891. struct TSinkFileParams
  892. {
  893. AnsiString TargetDir;
  894. const TCopyParamType * CopyParam;
  895. int Params;
  896. TFileOperationProgressType * OperationProgress;
  897. bool Skipped;
  898. unsigned int Flags;
  899. };
  900. //===========================================================================
  901. __fastcall TSFTPFileSystem::TSFTPFileSystem(TTerminal * ATerminal):
  902. TCustomFileSystem(ATerminal)
  903. {
  904. FPacketReservations = new TList();
  905. FPacketNumbers = VarArrayCreate(OPENARRAY(int, (0, 1)), varInteger);
  906. FPreviousLoggedPacket = 0;
  907. FNotLoggedPackets = 0;
  908. FBusy = 0;
  909. FAvoidBusy = false;
  910. FSupport = new TSFTPSupport();
  911. FSupport->Extensions = new TStringList();
  912. FExtensions = new TStringList();
  913. }
  914. //---------------------------------------------------------------------------
  915. __fastcall TSFTPFileSystem::~TSFTPFileSystem()
  916. {
  917. delete FSupport->Extensions;
  918. delete FSupport;
  919. delete FPacketReservations;
  920. delete FExtensions;
  921. }
  922. //---------------------------------------------------------------------------
  923. AnsiString __fastcall TSFTPFileSystem::GetProtocolName() const
  924. {
  925. return FMTLOAD(SFTP_PROTOCOL_NAME, (FVersion));
  926. }
  927. //---------------------------------------------------------------------------
  928. bool __fastcall TSFTPFileSystem::IsCapable(int Capability) const
  929. {
  930. assert(FTerminal);
  931. switch (Capability) {
  932. case fcAnyCommand:
  933. case fcHardLink:
  934. return false;
  935. case fcModeChanging:
  936. case fcNewerOnlyUpload:
  937. return true;
  938. case fcRename:
  939. return (FVersion >= 2);
  940. case fcSymbolicLink:
  941. case fcResolveSymlink:
  942. return (FVersion >= 3);
  943. case fcOwnerChanging:
  944. case fcGroupChanging:
  945. case fcNativeTextMode:
  946. return (FVersion >= 4);
  947. case fcTextMode:
  948. return (FVersion >= 4) ||
  949. strcmp(GetEOL(), EOLToStr(FTerminal->Configuration->LocalEOLType)) != 0;
  950. case fcUserGroupListing:
  951. return SupportsExtension(SFTP_EXT_OWNER_GROUP);
  952. default:
  953. assert(false);
  954. return false;
  955. }
  956. }
  957. //---------------------------------------------------------------------------
  958. bool __fastcall TSFTPFileSystem::SupportsExtension(const AnsiString & Extension) const
  959. {
  960. return (FSupport->Extensions->IndexOf(Extension) >= 0);
  961. }
  962. //---------------------------------------------------------------------------
  963. void __fastcall TSFTPFileSystem::KeepAlive()
  964. {
  965. TSFTPPacket Packet(SSH_FXP_REALPATH);
  966. Packet.AddString("/");
  967. SendPacketAndReceiveResponse(&Packet, &Packet);
  968. }
  969. //---------------------------------------------------------------------------
  970. void __fastcall TSFTPFileSystem::AdditionalInfo(TStrings * AdditionalInfo,
  971. bool Initial)
  972. {
  973. if (Initial)
  974. {
  975. if (!IsCapable(fcRename))
  976. {
  977. AdditionalInfo->Add(LoadStr(FS_RENAME_NOT_SUPPORTED));
  978. AdditionalInfo->Add("");
  979. }
  980. if (FExtensions->Count > 0)
  981. {
  982. AnsiString Name;
  983. AnsiString Value;
  984. AnsiString Line;
  985. AdditionalInfo->Add(LoadStr(SFTP_EXTENSION_INFO));
  986. for (int Index = 0; Index < FExtensions->Count; Index++)
  987. {
  988. AnsiString Name = FExtensions->Names[Index];
  989. AnsiString Value = FExtensions->Values[Name];
  990. AnsiString Line;
  991. if (Value.IsEmpty())
  992. {
  993. Line = Name;
  994. }
  995. else
  996. {
  997. if (!IsDisplayableStr(Value))
  998. {
  999. Value = "0x" + StrToHex(Value);
  1000. }
  1001. Line = FORMAT("%s=%s", (Name, Value));
  1002. }
  1003. AdditionalInfo->Add(FORMAT(" %s", (Line)));;
  1004. }
  1005. }
  1006. else
  1007. {
  1008. AdditionalInfo->Add(LoadStr(SFTP_NO_EXTENSION_INFO));
  1009. }
  1010. }
  1011. }
  1012. //---------------------------------------------------------------------------
  1013. struct TUnicodeEmitParams
  1014. {
  1015. WideString Buffer;
  1016. int Pos;
  1017. int Len;
  1018. };
  1019. //---------------------------------------------------------------------------
  1020. extern "C" void UnicodeEmit(void * AParams, long int Output)
  1021. {
  1022. if (Output == 0xFFFFL) // see Putty's charset\internal.h
  1023. {
  1024. throw Exception(LoadStr(DECODE_UTF_ERROR));
  1025. }
  1026. TUnicodeEmitParams * Params = (TUnicodeEmitParams *)AParams;
  1027. if (Params->Pos >= Params->Len)
  1028. {
  1029. Params->Len += 50;
  1030. Params->Buffer.SetLength(Params->Len);
  1031. }
  1032. Params->Buffer[Params->Pos + 1] = (wchar_t)Output;
  1033. Params->Pos++;
  1034. }
  1035. //---------------------------------------------------------------------------
  1036. AnsiString __fastcall TSFTPFileSystem::DecodeUTF(const AnsiString UTF)
  1037. {
  1038. charset_state State;
  1039. char * Str;
  1040. TUnicodeEmitParams Params;
  1041. AnsiString Result;
  1042. State.s0 = 0;
  1043. Str = UTF.c_str();
  1044. Params.Pos = 0;
  1045. Params.Len = UTF.Length();
  1046. Params.Buffer.SetLength(Params.Len);
  1047. while (*Str)
  1048. {
  1049. read_utf8(NULL, (unsigned char)*Str, &State, UnicodeEmit, &Params);
  1050. Str++;
  1051. }
  1052. Params.Buffer.SetLength(Params.Pos);
  1053. return Params.Buffer;
  1054. }
  1055. //---------------------------------------------------------------------------
  1056. inline void __fastcall TSFTPFileSystem::BusyStart()
  1057. {
  1058. if (FBusy == 0 && FTerminal->UseBusyCursor && !FAvoidBusy)
  1059. {
  1060. Busy(true);
  1061. }
  1062. FBusy++;
  1063. assert(FBusy < 10);
  1064. }
  1065. //---------------------------------------------------------------------------
  1066. inline void __fastcall TSFTPFileSystem::BusyEnd()
  1067. {
  1068. assert(FBusy > 0);
  1069. FBusy--;
  1070. if (FBusy == 0 && FTerminal->UseBusyCursor && !FAvoidBusy)
  1071. {
  1072. Busy(false);
  1073. }
  1074. }
  1075. //---------------------------------------------------------------------------
  1076. unsigned long __fastcall TSFTPFileSystem::MaxTransferBlockSize(unsigned long Overhead)
  1077. {
  1078. unsigned long MaxPacketSize = FTerminal->MaxPacketSize();
  1079. unsigned long Result = TFileOperationProgressType::StaticBlockSize();
  1080. if (MaxPacketSize > 0)
  1081. {
  1082. MaxPacketSize -= 4 + 1 + Overhead;
  1083. if (Result > MaxPacketSize)
  1084. {
  1085. Result = MaxPacketSize;
  1086. }
  1087. }
  1088. return Result;
  1089. }
  1090. //---------------------------------------------------------------------------
  1091. void __fastcall TSFTPFileSystem::SendPacket(const TSFTPPacket * Packet)
  1092. {
  1093. BusyStart();
  1094. try
  1095. {
  1096. if (FTerminal->IsLogging())
  1097. {
  1098. if ((FPreviousLoggedPacket != SSH_FXP_READ &&
  1099. FPreviousLoggedPacket != SSH_FXP_WRITE) ||
  1100. (Packet->Type != FPreviousLoggedPacket) ||
  1101. FTerminal->Configuration->LogProtocol)
  1102. {
  1103. if (FNotLoggedPackets)
  1104. {
  1105. FTerminal->LogEvent(FORMAT("%d skipped SSH_FXP_WRITE, SSH_FXP_READ, SSH_FXP_DATA and SSH_FXP_STATUS packets.",
  1106. (FNotLoggedPackets)));
  1107. FNotLoggedPackets = 0;
  1108. }
  1109. FTerminal->Log->Add(llInput, FORMAT("Type: %s, Size: %d, Number: %d",
  1110. (Packet->TypeName, (int)Packet->Length, (int)Packet->MessageNumber)));
  1111. if (FTerminal->Configuration->LogProtocol)
  1112. {
  1113. FTerminal->Log->Add(llInput, Packet->Dump());
  1114. }
  1115. FPreviousLoggedPacket = Packet->Type;
  1116. }
  1117. else
  1118. {
  1119. FNotLoggedPackets++;
  1120. }
  1121. }
  1122. char LenBuf[4];
  1123. PUT_32BIT(LenBuf, Packet->Length);
  1124. FTerminal->Send(LenBuf, sizeof(LenBuf));
  1125. FTerminal->Send(Packet->Data, Packet->Length);
  1126. }
  1127. __finally
  1128. {
  1129. BusyEnd();
  1130. }
  1131. }
  1132. //---------------------------------------------------------------------------
  1133. unsigned long __fastcall TSFTPFileSystem::GotStatusPacket(TSFTPPacket * Packet,
  1134. int AllowStatus)
  1135. {
  1136. unsigned long Code = Packet->GetCardinal();
  1137. static int Messages[] = {
  1138. SFTP_STATUS_OK,
  1139. SFTP_STATUS_EOF,
  1140. SFTP_STATUS_NO_SUCH_FILE,
  1141. SFTP_STATUS_PERMISSION_DENIED,
  1142. SFTP_STATUS_FAILURE,
  1143. SFTP_STATUS_BAD_MESSAGE,
  1144. SFTP_STATUS_NO_CONNECTION,
  1145. SFTP_STATUS_CONNECTION_LOST,
  1146. SFTP_STATUS_OP_UNSUPPORTED,
  1147. SFTP_STATUS_INVALID_HANDLE,
  1148. SFTP_STATUS_NO_SUCH_PATH,
  1149. SFTP_STATUS_FILE_ALREADY_EXISTS,
  1150. SFTP_STATUS_WRITE_PROTECT,
  1151. SFTP_STATUS_NO_MEDIA,
  1152. };
  1153. int Message;
  1154. if ((AllowStatus & (0x01 << Code)) == 0)
  1155. {
  1156. if (Code >= sizeof(Messages) / sizeof(*Messages))
  1157. {
  1158. Message = SFTP_STATUS_UNKNOWN;
  1159. }
  1160. else
  1161. {
  1162. Message = Messages[Code];
  1163. }
  1164. AnsiString ServerMessage;
  1165. AnsiString LanguageTag;
  1166. if (FVersion >= 3)
  1167. {
  1168. ServerMessage = DecodeUTF(Packet->GetString());
  1169. LanguageTag = Packet->GetString();
  1170. }
  1171. else
  1172. {
  1173. ServerMessage = LoadStr(SFTP_SERVER_MESSAGE_UNSUPPORTED);
  1174. }
  1175. if (LanguageTag.IsEmpty())
  1176. {
  1177. LanguageTag = "*";
  1178. }
  1179. if (FTerminal->IsLogging())
  1180. {
  1181. FTerminal->Log->Add(llOutput, FORMAT("Status/error code: %d, Message: %d, Server: %s, Language: %s ",
  1182. (int(Code), (int)Packet->MessageNumber, ServerMessage, LanguageTag)));
  1183. }
  1184. AnsiString Error = FMTLOAD(SFTP_ERROR_FORMAT, (LoadStr(Message), int(Code),
  1185. ServerMessage, LanguageTag, int(Packet->RequestType)));
  1186. FTerminal->TerminalError(NULL, Error);
  1187. return 0;
  1188. }
  1189. else
  1190. {
  1191. if (!FNotLoggedPackets || Code)
  1192. {
  1193. FTerminal->Log->Add(llOutput, FORMAT("Status/error code: %d", ((int)Code)));
  1194. }
  1195. return Code;
  1196. }
  1197. }
  1198. //---------------------------------------------------------------------------
  1199. void __fastcall TSFTPFileSystem::RemoveReservation(int Reservation)
  1200. {
  1201. for (int Index = Reservation+1; Index < FPacketReservations->Count; Index++)
  1202. {
  1203. FPacketNumbers.PutElement(FPacketNumbers.GetElement(Index), Index-1);
  1204. }
  1205. TSFTPPacket * Packet = (TSFTPPacket *)FPacketReservations->Items[Reservation];
  1206. if (Packet)
  1207. {
  1208. assert(Packet->ReservedBy == this);
  1209. Packet->ReservedBy = NULL;
  1210. }
  1211. FPacketReservations->Delete(Reservation);
  1212. }
  1213. //---------------------------------------------------------------------------
  1214. int __fastcall TSFTPFileSystem::ReceivePacket(TSFTPPacket * Packet,
  1215. int ExpectedType, int AllowStatus)
  1216. {
  1217. TSFTPBusy Busy(this);
  1218. int Result = SSH_FX_OK;
  1219. int Reservation = FPacketReservations->IndexOf(Packet);
  1220. if (Reservation < 0 || Packet->Capacity == 0)
  1221. {
  1222. bool IsReserved;
  1223. do
  1224. {
  1225. IsReserved = false;
  1226. assert(Packet);
  1227. char LenBuf[5];
  1228. LenBuf[sizeof(LenBuf) - 1] = '\0';
  1229. FTerminal->Receive(LenBuf, sizeof(LenBuf) - 1);
  1230. int Length = GET_32BIT(LenBuf);
  1231. if (Length > SFTP_MAX_PACKET_LEN)
  1232. {
  1233. AnsiString Message = FMTLOAD(SFTP_PACKET_TOO_BIG, (
  1234. int(Length), SFTP_MAX_PACKET_LEN));
  1235. if (ExpectedType == SSH_FXP_VERSION)
  1236. {
  1237. AnsiString LenString = LenBuf;
  1238. if (!IsDisplayableStr(LenString))
  1239. {
  1240. LenString = "0x" + StrToHex(LenString);
  1241. }
  1242. Message = FMTLOAD(SFTP_PACKET_TOO_BIG_INIT_EXPLAIN,
  1243. (Message, LenString));
  1244. }
  1245. FTerminal->FatalError(Message);
  1246. }
  1247. Packet->Capacity = Length;
  1248. FTerminal->Receive(Packet->Data, Length);
  1249. Packet->DataUpdated(Length);
  1250. if (FTerminal->IsLogging())
  1251. {
  1252. if ((FPreviousLoggedPacket != SSH_FXP_READ &&
  1253. FPreviousLoggedPacket != SSH_FXP_WRITE) ||
  1254. (Packet->Type != SSH_FXP_STATUS && Packet->Type != SSH_FXP_DATA) ||
  1255. FTerminal->Configuration->LogProtocol)
  1256. {
  1257. if (FNotLoggedPackets)
  1258. {
  1259. FTerminal->LogEvent(FORMAT("%d skipped SSH_FXP_WRITE, SSH_FXP_READ, SSH_FXP_DATA and SSH_FXP_STATUS packets.",
  1260. (FNotLoggedPackets)));
  1261. FNotLoggedPackets = 0;
  1262. }
  1263. FTerminal->Log->Add(llOutput, FORMAT("Type: %s, Size: %d, Number: %d",
  1264. (Packet->TypeName, (int)Packet->Length, (int)Packet->MessageNumber)));
  1265. if (FTerminal->Configuration->LogProtocol)
  1266. {
  1267. FTerminal->Log->Add(llOutput, Packet->Dump());
  1268. }
  1269. }
  1270. else
  1271. {
  1272. FNotLoggedPackets++;
  1273. }
  1274. }
  1275. if (Reservation < 0 ||
  1276. Packet->MessageNumber != (unsigned long)FPacketNumbers.GetElement(Reservation))
  1277. {
  1278. TSFTPPacket * ReservedPacket;
  1279. unsigned long MessageNumber;
  1280. for (int Index = 0; Index < FPacketReservations->Count; Index++)
  1281. {
  1282. MessageNumber = FPacketNumbers.GetElement(Index);
  1283. if (MessageNumber == Packet->MessageNumber)
  1284. {
  1285. ReservedPacket = (TSFTPPacket *)FPacketReservations->Items[Index];
  1286. IsReserved = true;
  1287. if (ReservedPacket)
  1288. {
  1289. *ReservedPacket = *Packet;
  1290. }
  1291. else
  1292. {
  1293. RemoveReservation(Index);
  1294. }
  1295. break;
  1296. }
  1297. }
  1298. }
  1299. }
  1300. while (IsReserved);
  1301. }
  1302. if (ExpectedType >= 0)
  1303. {
  1304. if (Packet->Type == SSH_FXP_STATUS)
  1305. {
  1306. Result = GotStatusPacket(Packet, (AllowStatus >= 0 ? AllowStatus : asOK));
  1307. }
  1308. else if (ExpectedType != Packet->Type)
  1309. {
  1310. FTerminal->FatalError(FMTLOAD(SFTP_INVALID_TYPE, ((int)Packet->Type)));
  1311. }
  1312. }
  1313. if (Reservation >= 0)
  1314. {
  1315. // order might have changed, when reserved, but not longer needed packet
  1316. // was receive in above loop
  1317. Reservation = FPacketReservations->IndexOf(Packet);
  1318. assert(Reservation >= 0);
  1319. assert(Packet->MessageNumber == (unsigned long)FPacketNumbers.GetElement(Reservation));
  1320. RemoveReservation(Reservation);
  1321. }
  1322. return Result;
  1323. }
  1324. //---------------------------------------------------------------------------
  1325. void __fastcall TSFTPFileSystem::ReserveResponse(const TSFTPPacket * Packet,
  1326. TSFTPPacket * Response)
  1327. {
  1328. assert(FPacketReservations->IndexOf(Response) < 0);
  1329. // mark response as not received yet
  1330. Response->Capacity = 0;
  1331. Response->ReservedBy = this;
  1332. FPacketReservations->Add(Response);
  1333. if (FPacketReservations->Count >= FPacketNumbers.ArrayHighBound())
  1334. {
  1335. FPacketNumbers.ArrayRedim(FPacketReservations->Count + 10);
  1336. }
  1337. FPacketNumbers.PutElement(Packet->MessageNumber, FPacketReservations->Count - 1);
  1338. }
  1339. //---------------------------------------------------------------------------
  1340. void __fastcall TSFTPFileSystem::UnreserveResponse(TSFTPPacket * Response)
  1341. {
  1342. int Reservation = FPacketReservations->IndexOf(Response);
  1343. if (Reservation >= 0)
  1344. {
  1345. FPacketReservations->Items[Reservation] = NULL;
  1346. }
  1347. }
  1348. //---------------------------------------------------------------------------
  1349. int __fastcall TSFTPFileSystem::ReceiveResponse(
  1350. const TSFTPPacket * Packet, TSFTPPacket * Response, int ExpectedType,
  1351. int AllowStatus)
  1352. {
  1353. int Result;
  1354. unsigned int MessageNumber = Packet->MessageNumber;
  1355. TSFTPPacket * AResponse = (Response ? Response : new TSFTPPacket());
  1356. try
  1357. {
  1358. Result = ReceivePacket(AResponse, ExpectedType, AllowStatus);
  1359. if (MessageNumber != AResponse->MessageNumber)
  1360. {
  1361. FTerminal->FatalError(FMTLOAD(SFTP_MESSAGE_NUMBER,
  1362. ((int)AResponse->MessageNumber, (int)MessageNumber)));
  1363. }
  1364. }
  1365. __finally
  1366. {
  1367. if (!Response) delete AResponse;
  1368. }
  1369. return Result;
  1370. }
  1371. //---------------------------------------------------------------------------
  1372. int __fastcall TSFTPFileSystem::SendPacketAndReceiveResponse(
  1373. const TSFTPPacket * Packet, TSFTPPacket * Response, int ExpectedType,
  1374. int AllowStatus)
  1375. {
  1376. int Result;
  1377. TSFTPBusy Busy(this);
  1378. SendPacket(Packet);
  1379. Result = ReceiveResponse(Packet, Response, ExpectedType, AllowStatus);
  1380. return Result;
  1381. }
  1382. //---------------------------------------------------------------------------
  1383. AnsiString __fastcall TSFTPFileSystem::RealPath(const AnsiString Path)
  1384. {
  1385. try
  1386. {
  1387. FTerminal->LogEvent(FORMAT("Getting real path for '%s'",
  1388. (Path)));
  1389. TSFTPPacket Packet(SSH_FXP_REALPATH);
  1390. Packet.AddString(Path);
  1391. SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_NAME);
  1392. if (Packet.GetCardinal() != 1)
  1393. {
  1394. FTerminal->FatalError(LoadStr(SFTP_NON_ONE_FXP_NAME_PACKET));
  1395. }
  1396. AnsiString RealDir = Packet.GetString();
  1397. // ignore rest of SSH_FXP_NAME packet
  1398. FTerminal->LogEvent(FORMAT("Real path is '%s'", (RealDir)));
  1399. return RealDir;
  1400. }
  1401. catch(Exception & E)
  1402. {
  1403. if (FTerminal->Active)
  1404. {
  1405. throw ExtException(&E, FMTLOAD(SFTP_REALPATH_ERROR, (Path)));
  1406. }
  1407. else
  1408. {
  1409. throw;
  1410. }
  1411. }
  1412. }
  1413. //---------------------------------------------------------------------------
  1414. AnsiString __fastcall TSFTPFileSystem::RealPath(const AnsiString Path,
  1415. const AnsiString BaseDir)
  1416. {
  1417. AnsiString APath;
  1418. if (TTerminal::IsAbsolutePath(Path))
  1419. {
  1420. APath = Path;
  1421. }
  1422. else
  1423. {
  1424. if (!Path.IsEmpty())
  1425. {
  1426. // this condition/block was outside (before) current block
  1427. // but it dod not work when Path was empty
  1428. if (!BaseDir.IsEmpty())
  1429. {
  1430. APath = UnixIncludeTrailingBackslash(BaseDir);
  1431. }
  1432. APath = APath + Path;
  1433. }
  1434. if (APath.IsEmpty()) APath = UnixIncludeTrailingBackslash(".");
  1435. }
  1436. return RealPath(APath);
  1437. }
  1438. //---------------------------------------------------------------------------
  1439. AnsiString __fastcall TSFTPFileSystem::LocalCanonify(const AnsiString & Path)
  1440. {
  1441. // TODO: improve (handle .. etc.)
  1442. if (TTerminal::IsAbsolutePath(Path)) return Path;
  1443. else
  1444. {
  1445. return UnixIncludeTrailingBackslash(FCurrentDirectory) + Path;
  1446. }
  1447. }
  1448. //---------------------------------------------------------------------------
  1449. AnsiString __fastcall TSFTPFileSystem::Canonify(AnsiString Path)
  1450. {
  1451. // inspired by canonify() from PSFTP.C
  1452. AnsiString Result;
  1453. FTerminal->LogEvent(FORMAT("Canonifying: \"%s\"", (Path)));
  1454. Path = LocalCanonify(Path);
  1455. bool TryParent = false;
  1456. try
  1457. {
  1458. Result = RealPath(Path);
  1459. }
  1460. catch(...)
  1461. {
  1462. if (FTerminal->Active)
  1463. {
  1464. TryParent = true;
  1465. }
  1466. else
  1467. {
  1468. throw;
  1469. }
  1470. }
  1471. if (TryParent)
  1472. {
  1473. AnsiString APath = UnixExcludeTrailingBackslash(Path);
  1474. AnsiString Name = UnixExtractFileName(APath);
  1475. if (Name == "." || Name == "..")
  1476. {
  1477. Result = Path;
  1478. }
  1479. else
  1480. {
  1481. AnsiString FPath = UnixExtractFilePath(APath);
  1482. try
  1483. {
  1484. Result = RealPath(FPath);
  1485. Result = UnixIncludeTrailingBackslash(Result) + Name;
  1486. }
  1487. catch(...)
  1488. {
  1489. if (FTerminal->Active)
  1490. {
  1491. Result = Path;
  1492. }
  1493. else
  1494. {
  1495. throw;
  1496. }
  1497. }
  1498. }
  1499. }
  1500. FTerminal->LogEvent(FORMAT("Canonified: \"%s\"", (Result)));
  1501. return Result;
  1502. }
  1503. //---------------------------------------------------------------------------
  1504. AnsiString __fastcall TSFTPFileSystem::AbsolutePath(AnsiString Path)
  1505. {
  1506. return RealPath(Path, CurrentDirectory);
  1507. }
  1508. //---------------------------------------------------------------------------
  1509. AnsiString __fastcall TSFTPFileSystem::GetHomeDirectory()
  1510. {
  1511. if (FHomeDirectory.IsEmpty())
  1512. {
  1513. FHomeDirectory = RealPath(".");
  1514. }
  1515. return FHomeDirectory;
  1516. }
  1517. //---------------------------------------------------------------------------
  1518. TRemoteFile * __fastcall TSFTPFileSystem::LoadFile(TSFTPPacket * Packet,
  1519. TRemoteFile * ALinkedByFile, const AnsiString FileName)
  1520. {
  1521. TRemoteFile * File = new TRemoteFile(ALinkedByFile);
  1522. try
  1523. {
  1524. File->Terminal = FTerminal;
  1525. if (!FileName.IsEmpty())
  1526. {
  1527. File->FileName = FileName;
  1528. }
  1529. Packet->GetFile(File, FVersion, FTerminal->SessionData->ConsiderDST);
  1530. }
  1531. catch(...)
  1532. {
  1533. delete File;
  1534. throw;
  1535. }
  1536. return File;
  1537. }
  1538. //---------------------------------------------------------------------------
  1539. void __fastcall TSFTPFileSystem::SetCurrentDirectory(AnsiString value)
  1540. {
  1541. assert(false);
  1542. }
  1543. //---------------------------------------------------------------------------
  1544. AnsiString __fastcall TSFTPFileSystem::GetCurrentDirectory()
  1545. {
  1546. return FCurrentDirectory;
  1547. }
  1548. //---------------------------------------------------------------------------
  1549. void __fastcall TSFTPFileSystem::DoStartup()
  1550. {
  1551. TSFTPPacket Packet(SSH_FXP_INIT);
  1552. Packet.AddCardinal(SFTPMaxVersion);
  1553. try
  1554. {
  1555. SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_VERSION);
  1556. }
  1557. catch(Exception &E)
  1558. {
  1559. FTerminal->FatalError(&E, LoadStr(SFTP_INITIALIZE_ERROR));
  1560. }
  1561. FVersion = Packet.GetCardinal();
  1562. FTerminal->LogEvent(FORMAT("SFTP version %d negotiated.", (FVersion)));
  1563. if (FVersion < SFTPMinVersion || FVersion > SFTPMaxVersion)
  1564. {
  1565. FTerminal->FatalError(FMTLOAD(SFTP_VERSION_NOT_SUPPORTED,
  1566. (FVersion, SFTPMinVersion, SFTPMaxVersion)));
  1567. }
  1568. if (FVersion >= 3)
  1569. {
  1570. FEOL = "\r\n";
  1571. AnsiString ExtensionName;
  1572. AnsiString ExtensionData;
  1573. AnsiString ExtensionDisplayData;
  1574. while (Packet.NextData)
  1575. {
  1576. ExtensionName = Packet.GetString();
  1577. ExtensionData = Packet.GetString();
  1578. ExtensionDisplayData = IsDisplayableStr(ExtensionData) ? ExtensionData :
  1579. "0x" + StrToHex(ExtensionData);
  1580. if (ExtensionName == SFTP_EXT_NEWLINE)
  1581. {
  1582. FEOL = ExtensionData;
  1583. FTerminal->LogEvent(FORMAT("Server requests EOL sequence %s.",
  1584. (ExtensionDisplayData)));
  1585. if (FEOL.Length() < 1 || FEOL.Length() > 2)
  1586. {
  1587. FTerminal->FatalError(FMTLOAD(SFTP_INVALID_EOL, (ExtensionDisplayData)));
  1588. }
  1589. }
  1590. else if (ExtensionName == SFTP_EXT_SUPPORTED)
  1591. {
  1592. TSFTPPacket SupportedStruct(ExtensionData);
  1593. FSupport->AttributeMask = SupportedStruct.GetCardinal();
  1594. FSupport->AttributeBits = SupportedStruct.GetCardinal();
  1595. FSupport->OpenFlags = SupportedStruct.GetCardinal();
  1596. FSupport->AccessMask = SupportedStruct.GetCardinal();
  1597. FSupport->MaxReadSize = SupportedStruct.GetCardinal();
  1598. AnsiString Extension;
  1599. FSupport->Extensions->Clear();
  1600. while (SupportedStruct.NextData)
  1601. {
  1602. Extension = SupportedStruct.GetString();
  1603. FSupport->Extensions->Add(Extension);
  1604. }
  1605. if (FTerminal->IsLogging())
  1606. {
  1607. FTerminal->LogEvent(FORMAT(
  1608. "Server support information:\n"
  1609. " Attribute mask: %s, Attribute bits: %s, Open flags: %s\n"
  1610. " Access mask: %s, Max read size: %s\n"
  1611. " Extensions (%d)\n",
  1612. (IntToHex(__int64(FSupport->AttributeMask), 4),
  1613. IntToHex(__int64(FSupport->AttributeBits), 4),
  1614. IntToHex(__int64(FSupport->OpenFlags), 4),
  1615. IntToHex(__int64(FSupport->AccessMask), 4),
  1616. IntToStr(__int64(FSupport->MaxReadSize)),
  1617. FSupport->Extensions->Count)));
  1618. for (int Index = 0; Index < FSupport->Extensions->Count; Index++)
  1619. {
  1620. FTerminal->LogEvent(
  1621. FORMAT(" %s", (FSupport->Extensions->Strings[Index])));
  1622. }
  1623. }
  1624. }
  1625. else
  1626. {
  1627. FTerminal->LogEvent(FORMAT("Unknown server extension %s=%s",
  1628. (ExtensionName, ExtensionDisplayData)));
  1629. }
  1630. FExtensions->Values[ExtensionName] = ExtensionData;
  1631. }
  1632. Packet.ChangeType(SSH_FXP_EXTENDED);
  1633. Packet.AddString(SFTP_EXT_WINSCP);
  1634. SendPacket(&Packet);
  1635. ReserveResponse(&Packet, &Packet);
  1636. TSFTPPacket PacketSoftware(SSH_FXP_EXTENDED);
  1637. bool SoftwareExt = SupportsExtension(SFTP_EXT_SOFTWARE);
  1638. if (SoftwareExt)
  1639. {
  1640. PacketSoftware.AddString(SFTP_EXT_SOFTWARE);
  1641. PacketSoftware.AddString(FTerminal->Configuration->ProductName);
  1642. PacketSoftware.AddString(FTerminal->Configuration->ProductVersion);
  1643. PacketSoftware.AddString(FTerminal->Configuration->CompanyName);
  1644. PacketSoftware.AddString(SFTP_EXT_SOFTWARE_OS);
  1645. PacketSoftware.AddString(FTerminal->Configuration->OSVersionStr);
  1646. SendPacket(&PacketSoftware);
  1647. ReserveResponse(&PacketSoftware, &PacketSoftware);
  1648. }
  1649. int Status = ReceiveResponse(&Packet, &Packet, SSH_FXP_STATUS,
  1650. asOK | asOpUnsupported);
  1651. if (Status == SSH_FX_OK)
  1652. {
  1653. FTerminal->LogEvent("Server recognises WinSCP.");
  1654. }
  1655. else
  1656. {
  1657. FTerminal->LogEvent("Server does not recognise WinSCP.");
  1658. }
  1659. if (SoftwareExt)
  1660. {
  1661. ReceiveResponse(&PacketSoftware, &PacketSoftware, SSH_FXP_EXTENDED_REPLY,
  1662. asOpUnsupported);
  1663. if ((PacketSoftware.Type != SSH_FXP_EXTENDED_REPLY) ||
  1664. (PacketSoftware.GetString() != SFTP_EXT_SOFTWARE))
  1665. {
  1666. FTerminal->LogEvent(FORMAT("Invalid response to %s", (SFTP_EXT_SOFTWARE)));
  1667. }
  1668. else
  1669. {
  1670. AnsiString Software(PacketSoftware.GetString());
  1671. AnsiString Vendor(PacketSoftware.GetString());
  1672. AnsiString Version(PacketSoftware.GetString());
  1673. AnsiString OS(PacketSoftware.GetString());
  1674. AnsiString OSVersion(PacketSoftware.GetString());
  1675. FTerminal->LogEvent(FORMAT("Server software: %s %s by %s",
  1676. (Software, Version, Vendor)));
  1677. FTerminal->LogEvent(FORMAT("Server OS: %s %s",
  1678. (OS, OSVersion)));
  1679. }
  1680. }
  1681. }
  1682. }
  1683. //---------------------------------------------------------------------------
  1684. char * __fastcall TSFTPFileSystem::GetEOL() const
  1685. {
  1686. if (FVersion >= 4)
  1687. {
  1688. assert(!FEOL.IsEmpty());
  1689. return FEOL.c_str();
  1690. }
  1691. else
  1692. {
  1693. return EOLToStr(FTerminal->SessionData->EOLType);
  1694. }
  1695. }
  1696. //---------------------------------------------------------------------------
  1697. void __fastcall TSFTPFileSystem::LookupUsersGroups()
  1698. {
  1699. assert(SupportsExtension(SFTP_EXT_OWNER_GROUP));
  1700. TSFTPPacket PacketOwners(SSH_FXP_EXTENDED);
  1701. TSFTPPacket PacketGroups(SSH_FXP_EXTENDED);
  1702. TSFTPPacket * Packet;
  1703. TUsersGroupsList * List;
  1704. TSFTPPacket * Packets[] = { &PacketOwners, &PacketGroups };
  1705. TUsersGroupsList * Lists[] = { FTerminal->FUsers, FTerminal->FGroups };
  1706. char ListTypes[] = { OGQ_LIST_OWNERS, OGQ_LIST_GROUPS };
  1707. for (int Index = 0; Index < LENOF(Packets); Index++)
  1708. {
  1709. Packet = Packets[Index];
  1710. Packet->AddString(SFTP_EXT_OWNER_GROUP);
  1711. Packet->AddByte(ListTypes[Index]);
  1712. SendPacket(Packet);
  1713. ReserveResponse(Packet, Packet);
  1714. }
  1715. for (int Index = 0; Index < LENOF(Packets); Index++)
  1716. {
  1717. Packet = Packets[Index];
  1718. ReceiveResponse(Packet, Packet, SSH_FXP_EXTENDED_REPLY, asOpUnsupported);
  1719. if ((Packet->Type != SSH_FXP_EXTENDED_REPLY) ||
  1720. (Packet->GetString() != SFTP_EXT_OWNER_GROUP_REPLY))
  1721. {
  1722. FTerminal->LogEvent(FORMAT("Invalid response to %s", (SFTP_EXT_OWNER_GROUP)));
  1723. }
  1724. else
  1725. {
  1726. List = Lists[Index];
  1727. unsigned long Count = Packet->GetCardinal();
  1728. List->BeginUpdate();
  1729. try
  1730. {
  1731. List->Clear();
  1732. for (unsigned long Item = 0; Item < Count; Item++)
  1733. {
  1734. List->Add(Packet->GetString());
  1735. }
  1736. }
  1737. __finally
  1738. {
  1739. List->EndUpdate();
  1740. }
  1741. }
  1742. }
  1743. }
  1744. //---------------------------------------------------------------------------
  1745. void __fastcall TSFTPFileSystem::ReadCurrentDirectory()
  1746. {
  1747. if (!FDirectoryToChangeTo.IsEmpty())
  1748. {
  1749. FCurrentDirectory = FDirectoryToChangeTo;
  1750. FDirectoryToChangeTo = "";
  1751. }
  1752. else if (FCurrentDirectory.IsEmpty())
  1753. {
  1754. // this happens only after startup when default remote directory is not specified
  1755. FCurrentDirectory = GetHomeDirectory();
  1756. }
  1757. }
  1758. //---------------------------------------------------------------------------
  1759. void __fastcall TSFTPFileSystem::HomeDirectory()
  1760. {
  1761. ChangeDirectory(GetHomeDirectory());
  1762. }
  1763. //---------------------------------------------------------------------------
  1764. void __fastcall TSFTPFileSystem::TryOpenDirectory(const AnsiString Directory)
  1765. {
  1766. FTerminal->LogEvent(FORMAT("Trying to open directory \"%s\".", (Directory)));
  1767. TSFTPPacket Packet(SSH_FXP_OPENDIR);
  1768. Packet.AddString(UnixExcludeTrailingBackslash(Directory));
  1769. SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_HANDLE);
  1770. AnsiString Handle = Packet.GetString();
  1771. Packet.ChangeType(SSH_FXP_CLOSE);
  1772. Packet.AddString(Handle);
  1773. SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_STATUS, asAll);
  1774. }
  1775. //---------------------------------------------------------------------------
  1776. void __fastcall TSFTPFileSystem::ChangeDirectory(const AnsiString Directory)
  1777. {
  1778. AnsiString Path, Current;
  1779. Current = !FDirectoryToChangeTo.IsEmpty() ? FDirectoryToChangeTo : FCurrentDirectory;
  1780. Path = RealPath(Directory, Current);
  1781. // to verify existence of directory try to open it (SSH_FXP_REALPATH succeeds
  1782. // for invalid paths on some systems, like CygWin)
  1783. TryOpenDirectory(Path);
  1784. // if open dir did not fail, directory exists -> success.
  1785. FDirectoryToChangeTo = Path;
  1786. }
  1787. //---------------------------------------------------------------------------
  1788. void __fastcall TSFTPFileSystem::CachedChangeDirectory(const AnsiString Directory)
  1789. {
  1790. FDirectoryToChangeTo = Directory;
  1791. }
  1792. //---------------------------------------------------------------------------
  1793. void __fastcall TSFTPFileSystem::ReadDirectory(TRemoteFileList * FileList)
  1794. {
  1795. assert(FileList && !FileList->Directory.IsEmpty());
  1796. AnsiString Directory;
  1797. Directory = UnixExcludeTrailingBackslash(LocalCanonify(FileList->Directory));
  1798. FTerminal->LogEvent(FORMAT("Listing directory \"%s\".", (Directory)));
  1799. // moved before SSH_FXP_OPENDIR, so directory listing does not retain
  1800. // old data (e.g. parent directory) when reading fails
  1801. FileList->Clear();
  1802. TSFTPPacket Packet(SSH_FXP_OPENDIR);
  1803. AnsiString Handle;
  1804. try
  1805. {
  1806. Packet.AddString(Directory);
  1807. SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_HANDLE);
  1808. Handle = Packet.GetString();
  1809. }
  1810. catch(...)
  1811. {
  1812. if (FTerminal->Active)
  1813. {
  1814. FileList->AddFile(new TRemoteParentDirectory());
  1815. }
  1816. throw;
  1817. }
  1818. TSFTPPacket Response;
  1819. try
  1820. {
  1821. bool isEOF = false;
  1822. int Total = 0;
  1823. TRemoteFile * File;
  1824. Packet.ChangeType(SSH_FXP_READDIR);
  1825. Packet.AddString(Handle);
  1826. SendPacket(&Packet);
  1827. do
  1828. {
  1829. ReceiveResponse(&Packet, &Response);
  1830. if (Response.Type == SSH_FXP_NAME)
  1831. {
  1832. TSFTPPacket ListingPacket = Response;
  1833. Packet.ChangeType(SSH_FXP_READDIR);
  1834. Packet.AddString(Handle);
  1835. SendPacket(&Packet);
  1836. ReserveResponse(&Packet, &Response);
  1837. unsigned int Count = ListingPacket.GetCardinal();
  1838. for (unsigned long Index = 0; Index < Count; Index++)
  1839. {
  1840. File = LoadFile(&ListingPacket, NULL, "");
  1841. FileList->AddFile(File);
  1842. Total++;
  1843. }
  1844. if (Count == 0)
  1845. {
  1846. FTerminal->LogEvent("Empty directory listing packet. Aborting directory reading.");
  1847. isEOF = true;
  1848. }
  1849. }
  1850. else if (Response.Type == SSH_FXP_STATUS)
  1851. {
  1852. isEOF = (GotStatusPacket(&Response, asEOF) == SSH_FX_EOF);
  1853. }
  1854. else
  1855. {
  1856. FTerminal->FatalError(FMTLOAD(SFTP_INVALID_TYPE, ((int)Response.Type)));
  1857. }
  1858. }
  1859. while (!isEOF);
  1860. if (Total == 0)
  1861. {
  1862. // Empty file list -> probably "permision denied", we
  1863. // at least get link to parent directory ("..")
  1864. try
  1865. {
  1866. FTerminal->ExceptionOnFail = true;
  1867. try
  1868. {
  1869. File = NULL;
  1870. FTerminal->ReadFile(
  1871. UnixIncludeTrailingBackslash(FileList->Directory) + PARENTDIRECTORY, File);
  1872. }
  1873. __finally
  1874. {
  1875. FTerminal->ExceptionOnFail = false;
  1876. }
  1877. }
  1878. catch(Exception &E)
  1879. {
  1880. if (E.InheritsFrom(__classid(EFatal))) throw;
  1881. else File = NULL;
  1882. }
  1883. // on some systems even getting ".." fails, we create dummy ".." instead
  1884. bool Failure = (File == NULL);
  1885. if (Failure)
  1886. {
  1887. File = new TRemoteParentDirectory();
  1888. }
  1889. assert(File && File->IsParentDirectory);
  1890. FileList->AddFile(File);
  1891. if (Failure)
  1892. {
  1893. throw Exception(FMTLOAD(EMPTY_DIRECTORY, (FileList->Directory)));
  1894. }
  1895. }
  1896. }
  1897. __finally
  1898. {
  1899. if (FTerminal->Active)
  1900. {
  1901. Packet.ChangeType(SSH_FXP_CLOSE);
  1902. Packet.AddString(Handle);
  1903. SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_STATUS, asAll);
  1904. }
  1905. }
  1906. }
  1907. //---------------------------------------------------------------------------
  1908. void __fastcall TSFTPFileSystem::ReadSymlink(TRemoteFile * SymlinkFile,
  1909. TRemoteFile *& File)
  1910. {
  1911. assert(SymlinkFile && SymlinkFile->IsSymLink);
  1912. assert(FVersion >= 3); // symlinks are supported with SFTP version 3 and later
  1913. AnsiString FileName = LocalCanonify(SymlinkFile->FileName);
  1914. TSFTPPacket ReadLinkPacket(SSH_FXP_READLINK);
  1915. ReadLinkPacket.AddString(FileName);
  1916. SendPacket(&ReadLinkPacket);
  1917. ReserveResponse(&ReadLinkPacket, &ReadLinkPacket);
  1918. // send second request before reading response to first one
  1919. // (performance benefit)
  1920. TSFTPPacket AttrsPacket(SSH_FXP_STAT);
  1921. AttrsPacket.AddString(FileName);
  1922. if (FVersion >= 4)
  1923. {
  1924. AttrsPacket.AddCardinal(SSH_FILEXFER_ATTR_COMMON);
  1925. }
  1926. SendPacket(&AttrsPacket);
  1927. ReserveResponse(&AttrsPacket, &AttrsPacket);
  1928. ReceiveResponse(&ReadLinkPacket, &ReadLinkPacket, SSH_FXP_NAME);
  1929. if (ReadLinkPacket.GetCardinal() != 1)
  1930. {
  1931. FTerminal->FatalError(LoadStr(SFTP_NON_ONE_FXP_NAME_PACKET));
  1932. }
  1933. SymlinkFile->LinkTo = ReadLinkPacket.GetString();
  1934. ReceiveResponse(&AttrsPacket, &AttrsPacket, SSH_FXP_ATTRS);
  1935. // SymlinkFile->FileName was used instead SymlinkFile->LinkTo before, why?
  1936. File = LoadFile(&AttrsPacket, SymlinkFile,
  1937. UnixExtractFileName(SymlinkFile->LinkTo));
  1938. }
  1939. //---------------------------------------------------------------------------
  1940. void __fastcall TSFTPFileSystem::ReadFile(const AnsiString FileName,
  1941. TRemoteFile *& File)
  1942. {
  1943. CustomReadFile(FileName, File, SSH_FXP_LSTAT);
  1944. }
  1945. //---------------------------------------------------------------------------
  1946. bool __fastcall TSFTPFileSystem::RemoteFileExists(const AnsiString FullPath,
  1947. TRemoteFile ** File)
  1948. {
  1949. bool Result;
  1950. try
  1951. {
  1952. TRemoteFile * AFile;
  1953. CustomReadFile(FullPath, AFile, SSH_FXP_LSTAT, NULL, true);
  1954. Result = (AFile != NULL);
  1955. if (Result)
  1956. {
  1957. if (File)
  1958. {
  1959. *File = AFile;
  1960. }
  1961. else
  1962. {
  1963. delete AFile;
  1964. }
  1965. }
  1966. }
  1967. catch(...)
  1968. {
  1969. if (!FTerminal->Active)
  1970. {
  1971. throw;
  1972. }
  1973. Result = false;
  1974. }
  1975. return Result;
  1976. }
  1977. //---------------------------------------------------------------------------
  1978. void __fastcall TSFTPFileSystem::CustomReadFile(const AnsiString FileName,
  1979. TRemoteFile *& File, char Type, TRemoteFile * ALinkedByFile,
  1980. bool AllowNonexistence)
  1981. {
  1982. TSFTPPacket Packet(Type);
  1983. Packet.AddString(LocalCanonify(FileName));
  1984. if (FVersion >= 4)
  1985. {
  1986. Packet.AddCardinal(SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_PERMISSIONS |
  1987. SSH_FILEXFER_ATTR_ACCESSTIME | SSH_FILEXFER_ATTR_MODIFYTIME |
  1988. SSH_FILEXFER_ATTR_OWNERGROUP);
  1989. }
  1990. SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_ATTRS,
  1991. AllowNonexistence ? asNoSuchFile : -1);
  1992. if (Packet.Type == SSH_FXP_ATTRS)
  1993. {
  1994. File = LoadFile(&Packet, ALinkedByFile, UnixExtractFileName(FileName));
  1995. }
  1996. else
  1997. {
  1998. assert(AllowNonexistence);
  1999. File = NULL;
  2000. }
  2001. }
  2002. //---------------------------------------------------------------------------
  2003. void __fastcall TSFTPFileSystem::DeleteFile(const AnsiString FileName,
  2004. const TRemoteFile * File, bool Recursive)
  2005. {
  2006. char Type;
  2007. AnsiString RealFileName = LocalCanonify(FileName);
  2008. if (File && File->IsDirectory && !File->IsSymLink)
  2009. {
  2010. if (Recursive)
  2011. {
  2012. FTerminal->ProcessDirectory(FileName, FTerminal->DeleteFile, &Recursive);
  2013. }
  2014. Type = SSH_FXP_RMDIR;
  2015. }
  2016. else
  2017. {
  2018. Type = SSH_FXP_REMOVE;
  2019. }
  2020. TSFTPPacket Packet(Type);
  2021. Packet.AddString(RealFileName);
  2022. SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_STATUS);
  2023. }
  2024. //---------------------------------------------------------------------------
  2025. void __fastcall TSFTPFileSystem::RenameFile(const AnsiString FileName,
  2026. const AnsiString NewName)
  2027. {
  2028. TSFTPPacket Packet(SSH_FXP_RENAME);
  2029. AnsiString RealName = LocalCanonify(FileName);
  2030. Packet.AddString(RealName);
  2031. AnsiString TargetName;
  2032. if (UnixExtractFilePath(NewName).IsEmpty())
  2033. {
  2034. // rename case (TTerminal::RenameFile)
  2035. TargetName = UnixExtractFilePath(RealName) + NewName;
  2036. }
  2037. else
  2038. {
  2039. // move case (TTerminal::MoveFiles)
  2040. TargetName = NewName;
  2041. }
  2042. Packet.AddString(TargetName);
  2043. SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_STATUS);
  2044. }
  2045. //---------------------------------------------------------------------------
  2046. void __fastcall TSFTPFileSystem::CreateDirectory(const AnsiString DirName,
  2047. const TRemoteProperties * Properties)
  2048. {
  2049. TSFTPPacket Packet(SSH_FXP_MKDIR);
  2050. AnsiString CanonifiedName = Canonify(DirName);
  2051. Packet.AddString(CanonifiedName);
  2052. Packet.AddProperties(NULL, 0, true, FVersion);
  2053. SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_STATUS);
  2054. // explicitly set permissions after directory creation,
  2055. // permissions specified in SSH_FXP_MKDIR are ignored at least by OpenSSH
  2056. Packet.ChangeType(SSH_FXP_SETSTAT);
  2057. Packet.AddString(CanonifiedName);
  2058. Packet.AddProperties(Properties, 0, true, FVersion);
  2059. SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_STATUS);
  2060. }
  2061. //---------------------------------------------------------------------------
  2062. void __fastcall TSFTPFileSystem::CreateLink(const AnsiString FileName,
  2063. const AnsiString PointTo, bool Symbolic)
  2064. {
  2065. USEDPARAM(Symbolic);
  2066. assert(Symbolic); // only symlinks are supported by SFTP
  2067. assert(FVersion >= 3); // symlinks are supported with SFTP version 3 and later
  2068. TSFTPPacket Packet(SSH_FXP_SYMLINK);
  2069. bool Buggy = (FTerminal->SessionData->SFTPSymlinkBug == asOn) ||
  2070. ((FTerminal->SessionData->SFTPSymlinkBug == asAuto) &&
  2071. (FTerminal->SshImplementation.Pos("OpenSSH") == 1));
  2072. if (!Buggy)
  2073. {
  2074. Packet.AddString(Canonify(FileName));
  2075. Packet.AddString(PointTo);
  2076. }
  2077. else
  2078. {
  2079. FTerminal->LogEvent("We believe the server has SFTP symlink bug");
  2080. Packet.AddString(PointTo);
  2081. Packet.AddString(Canonify(FileName));
  2082. }
  2083. SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_STATUS);
  2084. }
  2085. //---------------------------------------------------------------------------
  2086. void __fastcall TSFTPFileSystem::ChangeFileProperties(const AnsiString FileName,
  2087. const TRemoteFile * /*File*/, const TRemoteProperties * Properties)
  2088. {
  2089. assert(Properties);
  2090. TRemoteFile * File;
  2091. AnsiString RealFileName = LocalCanonify(FileName);
  2092. ReadFile(RealFileName, File);
  2093. try
  2094. {
  2095. assert(File);
  2096. if (File->IsDirectory && !File->IsSymLink && Properties->Recursive)
  2097. {
  2098. FTerminal->ProcessDirectory(FileName, FTerminal->ChangeFileProperties,
  2099. (void*)Properties);
  2100. }
  2101. TSFTPPacket Packet(SSH_FXP_SETSTAT);
  2102. Packet.AddString(RealFileName);
  2103. Packet.AddProperties(Properties, *File->Rights, File->IsDirectory, FVersion);
  2104. SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_STATUS);
  2105. }
  2106. __finally
  2107. {
  2108. delete File;
  2109. }
  2110. }
  2111. //---------------------------------------------------------------------------
  2112. void __fastcall TSFTPFileSystem::CustomCommandOnFile(const AnsiString /*FileName*/,
  2113. const TRemoteFile * /*File*/, AnsiString /*Command*/, int /*Params*/)
  2114. {
  2115. assert(false);
  2116. }
  2117. //---------------------------------------------------------------------------
  2118. void __fastcall TSFTPFileSystem::AnyCommand(const AnsiString /*Command*/)
  2119. {
  2120. assert(false);
  2121. }
  2122. //---------------------------------------------------------------------------
  2123. // transfer protocol
  2124. //---------------------------------------------------------------------------
  2125. void __fastcall TSFTPFileSystem::CopyToRemote(TStrings * FilesToCopy,
  2126. const AnsiString TargetDir, const TCopyParamType * CopyParam,
  2127. int Params, TFileOperationProgressType * OperationProgress,
  2128. bool & DisconnectWhenComplete)
  2129. {
  2130. assert(FilesToCopy && OperationProgress);
  2131. AnsiString FileName, FileNameOnly;
  2132. AnsiString FullTargetDir = UnixIncludeTrailingBackslash(TargetDir);
  2133. int Index = 0;
  2134. while (Index < FilesToCopy->Count && !OperationProgress->Cancel)
  2135. {
  2136. bool Success = false;
  2137. FileName = FilesToCopy->Strings[Index];
  2138. FileNameOnly = ExtractFileName(FileName);
  2139. assert(!FAvoidBusy);
  2140. FAvoidBusy = true;
  2141. try
  2142. {
  2143. try
  2144. {
  2145. if (FTerminal->SessionData->CacheDirectories)
  2146. {
  2147. FTerminal->DirectoryModified(TargetDir, false);
  2148. if (DirectoryExists(FileName))
  2149. {
  2150. FTerminal->DirectoryModified(UnixIncludeTrailingBackslash(TargetDir)+
  2151. FileNameOnly, true);
  2152. }
  2153. }
  2154. SFTPSource(FileName, FullTargetDir, CopyParam, Params, OperationProgress,
  2155. tfFirstLevel);
  2156. Success = true;
  2157. }
  2158. catch(EScpSkipFile & E)
  2159. {
  2160. SUSPEND_OPERATION (
  2161. if (!FTerminal->HandleException(&E)) throw;
  2162. );
  2163. }
  2164. }
  2165. __finally
  2166. {
  2167. FAvoidBusy = false;
  2168. OperationProgress->Finish(FileName, Success, DisconnectWhenComplete);
  2169. }
  2170. Index++;
  2171. }
  2172. }
  2173. //---------------------------------------------------------------------------
  2174. void __fastcall TSFTPFileSystem::SFTPConfirmOverwrite(const AnsiString FileName,
  2175. bool TargetBiggerThanSource, TFileOperationProgressType * OperationProgress,
  2176. TSFTPOverwriteMode & OverwriteMode, const TOverwriteFileParams * FileParams)
  2177. {
  2178. if (OperationProgress->NoToAll)
  2179. {
  2180. THROW_SKIP_FILE_NULL;
  2181. }
  2182. else
  2183. {
  2184. int Answer;
  2185. SUSPEND_OPERATION
  2186. (
  2187. int Answers = qaYes | qaNo | qaAbort | qaYesToAll | qaNoToAll | qaAll;
  2188. if ((FVersion < 4) || !OperationProgress->AsciiTransfer)
  2189. {
  2190. Answers |= qaRetry;
  2191. }
  2192. TQueryButtonAlias Aliases[2];
  2193. Aliases[0].Button = qaRetry;
  2194. Aliases[0].Alias = LoadStr(APPEND_BUTTON);
  2195. Aliases[1].Button = qaAll;
  2196. Aliases[1].Alias = LoadStr(YES_TO_NEWER_BUTTON);
  2197. TQueryParams Params(qpNeverAskAgainCheck);
  2198. Params.Aliases = Aliases;
  2199. Params.AliasesCount = LENOF(Aliases);
  2200. Answer = FTerminal->ConfirmFileOverwrite(FileName, FileParams,
  2201. Answers, &Params,
  2202. OperationProgress->Side == osLocal ? osRemote : osLocal,
  2203. OperationProgress);
  2204. );
  2205. if (Answer == qaRetry)
  2206. {
  2207. if (TargetBiggerThanSource || OperationProgress->AsciiTransfer)
  2208. {
  2209. OverwriteMode = omAppend;
  2210. }
  2211. else
  2212. {
  2213. SUSPEND_OPERATION
  2214. (
  2215. Answer = FTerminal->DoQueryUser(FORMAT(LoadStr(APPEND_OR_RESUME), (FileName)),
  2216. qaYes | qaNo | qaCancel, 0);
  2217. );
  2218. if (Answer == qaCancel)
  2219. {
  2220. if (!OperationProgress->Cancel)
  2221. {
  2222. OperationProgress->Cancel = csCancel;
  2223. }
  2224. Abort();
  2225. }
  2226. else
  2227. {
  2228. OverwriteMode = ((Answer == qaYes) ? omAppend : omResume);
  2229. }
  2230. }
  2231. }
  2232. else
  2233. {
  2234. OverwriteMode = omOverwrite;
  2235. switch (Answer)
  2236. {
  2237. case qaAbort:
  2238. if (!OperationProgress->Cancel)
  2239. {
  2240. OperationProgress->Cancel = csCancel;
  2241. }
  2242. Abort();
  2243. break;
  2244. case qaNo:
  2245. THROW_SKIP_FILE_NULL;
  2246. }
  2247. }
  2248. }
  2249. }
  2250. //---------------------------------------------------------------------------
  2251. bool TSFTPFileSystem::SFTPConfirmResume(const AnsiString DestFileName,
  2252. bool PartialBiggerThanSource, TFileOperationProgressType * OperationProgress)
  2253. {
  2254. bool ResumeTransfer;
  2255. assert(OperationProgress);
  2256. if (PartialBiggerThanSource)
  2257. {
  2258. int Answer;
  2259. SUSPEND_OPERATION
  2260. (
  2261. TQueryParams Params(qpAllowContinueOnError);
  2262. Answer = FTerminal->DoQueryUser(
  2263. FMTLOAD(PARTIAL_BIGGER_THAN_SOURCE, (DestFileName)), NULL,
  2264. qaOK | qaAbort, &Params, qtWarning);
  2265. )
  2266. if (Answer == qaAbort)
  2267. {
  2268. if (!OperationProgress->Cancel)
  2269. {
  2270. OperationProgress->Cancel = csCancel;
  2271. }
  2272. Abort();
  2273. }
  2274. ResumeTransfer = false;
  2275. }
  2276. else if (FTerminal->Configuration->ConfirmResume)
  2277. {
  2278. int Answer;
  2279. SUSPEND_OPERATION
  2280. (
  2281. TQueryParams Params(qpAllowContinueOnError | qpNeverAskAgainCheck);
  2282. Answer = FTerminal->DoQueryUser(
  2283. FMTLOAD(RESUME_TRANSFER, (DestFileName)), qaYes | qaNo | qaAbort,
  2284. &Params);
  2285. );
  2286. switch (Answer) {
  2287. case qaNeverAskAgain:
  2288. FTerminal->Configuration->ConfirmResume = false;
  2289. case qaYes:
  2290. ResumeTransfer = true;
  2291. break;
  2292. case qaNo:
  2293. ResumeTransfer = false;
  2294. break;
  2295. case qaAbort:
  2296. if (!OperationProgress->Cancel)
  2297. {
  2298. OperationProgress->Cancel = csCancel;
  2299. }
  2300. Abort();
  2301. break;
  2302. }
  2303. }
  2304. else
  2305. {
  2306. ResumeTransfer = true;
  2307. }
  2308. return ResumeTransfer;
  2309. }
  2310. //---------------------------------------------------------------------------
  2311. void __fastcall TSFTPFileSystem::SFTPSource(const AnsiString FileName,
  2312. const AnsiString TargetDir, const TCopyParamType * CopyParam, int Params,
  2313. TFileOperationProgressType * OperationProgress, unsigned int Flags)
  2314. {
  2315. AnsiString OnlyFileName = ExtractFileName(FileName);
  2316. if (FLAGCLEAR(Params, cpDelete) &&
  2317. !CopyParam->AllowTransfer(OnlyFileName))
  2318. {
  2319. FTerminal->LogEvent(FORMAT("File \"%s\" excluded from transfer", (FileName)));
  2320. THROW_SKIP_FILE_NULL;
  2321. }
  2322. FTerminal->LogEvent(FORMAT("File: \"%s\"", (FileName)));
  2323. OperationProgress->SetFile(FileName);
  2324. TOpenRemoteFileParams OpenParams;
  2325. OpenParams.OverwriteMode = omOverwrite;
  2326. HANDLE File;
  2327. unsigned long MTime, ATime;
  2328. __int64 Size;
  2329. FTerminal->OpenLocalFile(FileName, GENERIC_READ, &OpenParams.LocalFileAttrs,
  2330. &File, NULL, &MTime, &ATime, &Size);
  2331. if (OpenParams.LocalFileAttrs & faDirectory)
  2332. {
  2333. SFTPDirectorySource(IncludeTrailingBackslash(FileName), TargetDir,
  2334. OpenParams.LocalFileAttrs, CopyParam, Params, OperationProgress, Flags);
  2335. }
  2336. else
  2337. {
  2338. try
  2339. {
  2340. // File is regular file (not directory)
  2341. assert(File);
  2342. AnsiString DestFileName = CopyParam->ChangeFileName(OnlyFileName,
  2343. osLocal, FLAGSET(Flags, tfFirstLevel));
  2344. AnsiString DestFullName = LocalCanonify(TargetDir + DestFileName);
  2345. AnsiString DestPartinalFullName;
  2346. bool ResumeAllowed;
  2347. bool ResumeTransfer = false;
  2348. bool DestFileExists = false;
  2349. TRights DestRights;
  2350. __int64 ResumeOffset;
  2351. FTerminal->LogEvent(FORMAT("Copying \"%s\" to remote directory started.", (FileName)));
  2352. OperationProgress->SetLocalSize(Size);
  2353. // Suppose same data size to transfer as to read
  2354. // (not true with ASCII transfer)
  2355. OperationProgress->SetTransferSize(OperationProgress->LocalSize);
  2356. OperationProgress->TransferingFile = false;
  2357. // Will we use ASCII of BINARY file tranfer?
  2358. OperationProgress->SetAsciiTransfer(CopyParam->UseAsciiTransfer(FileName));
  2359. FTerminal->LogEvent(
  2360. AnsiString((OperationProgress->AsciiTransfer ? "Ascii" : "Binary")) +
  2361. " transfer mode selected.");
  2362. ResumeAllowed = !OperationProgress->AsciiTransfer &&
  2363. FLAGCLEAR(Flags, tfNewDirectory) &&
  2364. CopyParam->AllowResume(OperationProgress->LocalSize) &&
  2365. IsCapable(fcRename);
  2366. OperationProgress->SetResumeStatus(ResumeAllowed ? rsEnabled : rsDisabled);
  2367. TOverwriteFileParams FileParams;
  2368. FileParams.SourceSize = OperationProgress->LocalSize;
  2369. FileParams.SourceTimestamp = UnixToDateTime(MTime,
  2370. FTerminal->SessionData->ConsiderDST);
  2371. if (ResumeAllowed)
  2372. {
  2373. DestPartinalFullName = DestFullName + FTerminal->Configuration->PartialExt;
  2374. FTerminal->LogEvent("Checking existence of file.");
  2375. TRemoteFile * File = new TRemoteFile();
  2376. DestFileExists = RemoteFileExists(DestFullName, &File);
  2377. if (DestFileExists)
  2378. {
  2379. OpenParams.DestFileSize = File->Size;
  2380. FileParams.DestSize = OpenParams.DestFileSize;
  2381. FileParams.DestTimestamp = File->Modification;
  2382. DestRights = *File->Rights;
  2383. delete File;
  2384. File = NULL;
  2385. }
  2386. FTerminal->LogEvent("Checking existence of partially transfered file.");
  2387. if (RemoteFileExists(DestPartinalFullName, &File))
  2388. {
  2389. ResumeOffset = File->Size;
  2390. delete File;
  2391. File = NULL;
  2392. ResumeTransfer = SFTPConfirmResume(DestFileName,
  2393. ResumeOffset > OperationProgress->LocalSize, OperationProgress);
  2394. if (!ResumeTransfer)
  2395. {
  2396. DeleteFile(DestPartinalFullName);
  2397. }
  2398. else
  2399. {
  2400. FTerminal->LogEvent("Resuming file transfer.");
  2401. }
  2402. }
  2403. else
  2404. {
  2405. // partial upload file does not exists, check for full file
  2406. if (DestFileExists &&
  2407. (FLAGSET(Params, cpNewerOnly) ||
  2408. (FTerminal->Configuration->ConfirmOverwriting &&
  2409. !OperationProgress->YesToAll && FLAGCLEAR(Params, cpNoConfirmation))))
  2410. {
  2411. SFTPConfirmOverwrite(DestFileName,
  2412. OpenParams.DestFileSize >= OperationProgress->LocalSize,
  2413. OperationProgress, OpenParams.OverwriteMode, &FileParams);
  2414. }
  2415. }
  2416. }
  2417. bool DoResume = (ResumeAllowed && (OpenParams.OverwriteMode == omOverwrite));
  2418. OpenParams.RemoteFileName = DoResume ? DestPartinalFullName : DestFullName;
  2419. OpenParams.Resume = DoResume;
  2420. OpenParams.OperationProgress = OperationProgress;
  2421. OpenParams.CopyParam = CopyParam;
  2422. OpenParams.Params = Params;
  2423. OpenParams.FileParams = &FileParams;
  2424. FTerminal->LogEvent("Opening remote file.");
  2425. FTerminal->FileOperationLoop(SFTPOpenRemote, OperationProgress, true,
  2426. FMTLOAD(SFTP_CREATE_FILE_ERROR, (OpenParams.RemoteFileName)),
  2427. &OpenParams);
  2428. bool TransferFinished = false;
  2429. __int64 DestWriteOffset = 0;
  2430. try
  2431. {
  2432. if (OpenParams.OverwriteMode == omAppend)
  2433. {
  2434. FTerminal->LogEvent("Appending file.");
  2435. DestWriteOffset = OpenParams.DestFileSize;
  2436. }
  2437. else if (ResumeTransfer || (OpenParams.OverwriteMode == omResume))
  2438. {
  2439. if (OpenParams.OverwriteMode == omResume)
  2440. {
  2441. FTerminal->LogEvent("Resuming file transfer (append style).");
  2442. ResumeOffset = OpenParams.DestFileSize;
  2443. }
  2444. FileSeek((THandle)File, ResumeOffset, 0);
  2445. OperationProgress->AddResumed(ResumeOffset);
  2446. }
  2447. // at end of this block queue is disposed
  2448. {
  2449. TSFTPUploadQueue Queue(this);
  2450. unsigned long BlockSize = MaxTransferBlockSize(4);
  2451. int QueueLen = int(OperationProgress->LocalSize / BlockSize);
  2452. if (QueueLen > FTerminal->SessionData->SFTPUploadQueue)
  2453. {
  2454. QueueLen = FTerminal->SessionData->SFTPUploadQueue;
  2455. }
  2456. if (QueueLen < 1)
  2457. {
  2458. QueueLen = 1;
  2459. }
  2460. bool Initialized;
  2461. Initialized = Queue.Init(QueueLen, FileName, File, OperationProgress,
  2462. OpenParams.RemoteFileHandle, BlockSize,
  2463. DestWriteOffset + OperationProgress->TransferedSize);
  2464. if (Initialized)
  2465. {
  2466. while (Queue.Next(SSH_FXP_STATUS))
  2467. {
  2468. if (OperationProgress->Cancel)
  2469. {
  2470. Abort();
  2471. }
  2472. }
  2473. }
  2474. }
  2475. TransferFinished = true;
  2476. }
  2477. __finally
  2478. {
  2479. if (FTerminal->Active)
  2480. {
  2481. SFTPCloseRemote(OpenParams.RemoteFileHandle, DestFileName,
  2482. OperationProgress, TransferFinished);
  2483. // delete file if transfer was not completed and resuming is not allowed
  2484. if (!TransferFinished && !DoResume)
  2485. {
  2486. DeleteFile(OpenParams.RemoteFileName);
  2487. }
  2488. }
  2489. }
  2490. // originally this was before CLOSE (last __finally statement),
  2491. // on VShell it failed
  2492. if (DoResume)
  2493. {
  2494. FILE_OPERATION_LOOP(FMTLOAD(RENAME_AFTER_RESUME_ERROR,
  2495. (UnixExtractFileName(OpenParams.RemoteFileName), DestFileName)),
  2496. if (DestFileExists)
  2497. {
  2498. DeleteFile(DestFullName);
  2499. // DestFileExists used to be set to false here. Had it any reason?
  2500. }
  2501. RenameFile(OpenParams.RemoteFileName, DestFileName);
  2502. );
  2503. }
  2504. bool SetRights = ((DoResume && DestFileExists) || CopyParam->PreserveRights);
  2505. if (CopyParam->PreserveTime || SetRights)
  2506. {
  2507. FILE_OPERATION_LOOP(FMTLOAD(PRESERVE_TIME_PERM_ERROR, (DestFileName)),
  2508. TSFTPPacket Packet(SSH_FXP_SETSTAT);
  2509. Packet.AddString(DestFullName);
  2510. unsigned short Rights = 0;
  2511. if (CopyParam->PreserveRights)
  2512. {
  2513. Rights = CopyParam->RemoteFileRights(OpenParams.LocalFileAttrs);
  2514. }
  2515. else if (DoResume && DestFileExists)
  2516. {
  2517. Rights = DestRights.NumberSet;
  2518. }
  2519. Packet.AddProperties(
  2520. SetRights ? &Rights : NULL, NULL, NULL,
  2521. CopyParam->PreserveTime ? &MTime : NULL,
  2522. CopyParam->PreserveTime ? &ATime : NULL,
  2523. NULL, false, FVersion);
  2524. SendPacketAndReceiveResponse(&Packet, NULL, SSH_FXP_STATUS);
  2525. );
  2526. }
  2527. }
  2528. __finally
  2529. {
  2530. CloseHandle(File);
  2531. }
  2532. }
  2533. /* TODO : Delete also read-only files. */
  2534. /* TODO : Show error message on failure. */
  2535. if (Params & cpDelete) Sysutils::DeleteFile(FileName);
  2536. }
  2537. //---------------------------------------------------------------------------
  2538. AnsiString __fastcall TSFTPFileSystem::SFTPOpenRemoteFile(
  2539. const AnsiString & FileName, unsigned int OpenType, __int64 Size)
  2540. {
  2541. TSFTPPacket Packet(SSH_FXP_OPEN);
  2542. Packet.AddString(FileName);
  2543. Packet.AddCardinal(OpenType);
  2544. Packet.AddProperties(NULL, NULL, NULL, NULL, NULL,
  2545. Size >= 0 ? &Size : NULL, false, FVersion);
  2546. SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_HANDLE);
  2547. return Packet.GetString();
  2548. }
  2549. //---------------------------------------------------------------------------
  2550. int __fastcall TSFTPFileSystem::SFTPOpenRemote(void * AOpenParams, void * /*Param2*/)
  2551. {
  2552. TOpenRemoteFileParams * OpenParams = (TOpenRemoteFileParams *)AOpenParams;
  2553. assert(OpenParams);
  2554. TFileOperationProgressType * OperationProgress = OpenParams->OperationProgress;
  2555. int OpenType;
  2556. bool Confirmed = false;
  2557. bool Success = false;
  2558. do
  2559. {
  2560. try
  2561. {
  2562. OpenType = SSH_FXF_WRITE | SSH_FXF_CREAT;
  2563. if (FTerminal->Configuration->ConfirmOverwriting &&
  2564. !Confirmed && !OperationProgress->YesToAll && !OpenParams->Resume &&
  2565. !(OpenParams->Params & cpNoConfirmation) &&
  2566. (OpenParams->OverwriteMode == omOverwrite))
  2567. {
  2568. OpenType |= SSH_FXF_EXCL;
  2569. }
  2570. if (!OpenParams->Resume && (OpenParams->OverwriteMode == omOverwrite))
  2571. {
  2572. OpenType |= SSH_FXF_TRUNC;
  2573. }
  2574. if ((FVersion >= 4) && OpenParams->OperationProgress->AsciiTransfer)
  2575. {
  2576. OpenType |= SSH_FXF_TEXT;
  2577. }
  2578. OpenParams->RemoteFileHandle = SFTPOpenRemoteFile(
  2579. OpenParams->RemoteFileName, OpenType, OperationProgress->LocalSize);
  2580. Success = true;
  2581. }
  2582. catch(...)
  2583. {
  2584. if (!Confirmed && (OpenType & SSH_FXF_EXCL) && FTerminal->Active)
  2585. {
  2586. bool TargetBiggerThanSource;
  2587. bool ThrowOriginal = false;
  2588. // When exclusive opening of file fails, try to detect if file exists.
  2589. // When file does not exist, failure was probably caused by 'permission denied'
  2590. // or similar error. In this case throw original exception.
  2591. try
  2592. {
  2593. TRemoteFile * File;
  2594. AnsiString RealFileName = LocalCanonify(OpenParams->RemoteFileName);
  2595. ReadFile(RealFileName, File);
  2596. OpenParams->DestFileSize = File->Size;
  2597. if (OpenParams->FileParams != NULL)
  2598. {
  2599. OpenParams->FileParams->DestTimestamp = File->Modification;
  2600. OpenParams->FileParams->DestSize = OpenParams->DestFileSize;
  2601. }
  2602. TargetBiggerThanSource = File->Size >= OperationProgress->LocalSize;
  2603. // file exists (otherwise exception was thrown)
  2604. assert(File);
  2605. SAFE_DESTROY(File);
  2606. }
  2607. catch(...)
  2608. {
  2609. if (!FTerminal->Active)
  2610. {
  2611. throw;
  2612. }
  2613. else
  2614. {
  2615. ThrowOriginal = true;
  2616. }
  2617. }
  2618. if (ThrowOriginal)
  2619. {
  2620. throw;
  2621. }
  2622. // confirmation duplicated in SFTPSource for resumable file transfers.
  2623. SFTPConfirmOverwrite(UnixExtractFileName(OpenParams->RemoteFileName),
  2624. TargetBiggerThanSource, OperationProgress, OpenParams->OverwriteMode,
  2625. OpenParams->FileParams);
  2626. Confirmed = true;
  2627. }
  2628. else
  2629. {
  2630. throw;
  2631. }
  2632. }
  2633. }
  2634. while (!Success);
  2635. return 0;
  2636. }
  2637. //---------------------------------------------------------------------------
  2638. void __fastcall TSFTPFileSystem::SFTPCloseRemote(const AnsiString Handle,
  2639. const AnsiString FileName, TFileOperationProgressType * OperationProgress,
  2640. bool TransferFinished)
  2641. {
  2642. // Moving this out of SFTPSource() fixed external exception 0xC0000029 error
  2643. FILE_OPERATION_LOOP(FMTLOAD(SFTP_CLOSE_FILE_ERROR, (FileName)),
  2644. try
  2645. {
  2646. TSFTPPacket CloseRequest(SSH_FXP_CLOSE);
  2647. CloseRequest.AddString(Handle);
  2648. SendPacketAndReceiveResponse(&CloseRequest, NULL, SSH_FXP_STATUS);
  2649. }
  2650. catch(...)
  2651. {
  2652. if (!FTerminal->Active || TransferFinished)
  2653. {
  2654. throw;
  2655. }
  2656. }
  2657. );
  2658. }
  2659. //---------------------------------------------------------------------------
  2660. void __fastcall TSFTPFileSystem::SFTPDirectorySource(const AnsiString DirectoryName,
  2661. const AnsiString TargetDir, int Attrs, const TCopyParamType * CopyParam,
  2662. int Params, TFileOperationProgressType * OperationProgress, unsigned int Flags)
  2663. {
  2664. AnsiString DestDirectoryName = CopyParam->ChangeFileName(
  2665. ExtractFileName(ExcludeTrailingBackslash(DirectoryName)), osLocal,
  2666. FLAGSET(Flags, tfFirstLevel));
  2667. AnsiString DestFullName = UnixIncludeTrailingBackslash(TargetDir + DestDirectoryName);
  2668. OperationProgress->SetFile(DirectoryName);
  2669. bool CreateDir = false;
  2670. try
  2671. {
  2672. TryOpenDirectory(DestFullName);
  2673. }
  2674. catch(...)
  2675. {
  2676. if (FTerminal->Active)
  2677. {
  2678. // opening directory failed, it probably does not exists, try to
  2679. // create it
  2680. CreateDir = true;
  2681. }
  2682. else
  2683. {
  2684. throw;
  2685. }
  2686. }
  2687. if (CreateDir)
  2688. {
  2689. TRemoteProperties Properties;
  2690. if (CopyParam->PreserveRights)
  2691. {
  2692. Properties.Valid = TValidProperties() << vpRights;
  2693. Properties.Rights = CopyParam->RemoteFileRights(Attrs);
  2694. }
  2695. FTerminal->CreateDirectory(DestFullName, &Properties);
  2696. Flags |= tfNewDirectory;
  2697. }
  2698. int FindAttrs = faReadOnly | faHidden | faSysFile | faDirectory | faArchive;
  2699. TSearchRec SearchRec;
  2700. bool FindOK;
  2701. FILE_OPERATION_LOOP (FMTLOAD(LIST_DIR_ERROR, (DirectoryName)),
  2702. FindOK = (bool)(FindFirst(DirectoryName + "*.*",
  2703. FindAttrs, SearchRec) == 0);
  2704. );
  2705. while (FindOK && !OperationProgress->Cancel)
  2706. {
  2707. AnsiString FileName = DirectoryName + SearchRec.Name;
  2708. try
  2709. {
  2710. if ((SearchRec.Name != ".") && (SearchRec.Name != ".."))
  2711. {
  2712. SFTPSource(FileName, DestFullName, CopyParam, Params, OperationProgress,
  2713. Flags & ~tfFirstLevel);
  2714. }
  2715. }
  2716. catch (EScpSkipFile &E)
  2717. {
  2718. // If ESkipFile occurs, just log it and continue with next file
  2719. SUSPEND_OPERATION (
  2720. // here a message to user was displayed, which was not appropriate
  2721. // when user refused to overwrite the file in subdirectory.
  2722. // hopefuly it won't be missing in other situations.
  2723. if (!FTerminal->HandleException(&E)) throw;
  2724. );
  2725. }
  2726. FILE_OPERATION_LOOP (FMTLOAD(LIST_DIR_ERROR, (DirectoryName)),
  2727. FindOK = (FindNext(SearchRec) == 0);
  2728. );
  2729. };
  2730. FindClose(SearchRec);
  2731. /* TODO : Delete also read-only directories. */
  2732. /* TODO : Show error message on failure. */
  2733. if ((Params & cpDelete) && !OperationProgress->Cancel) RemoveDir(DirectoryName);
  2734. }
  2735. //---------------------------------------------------------------------------
  2736. void __fastcall TSFTPFileSystem::CopyToLocal(TStrings * FilesToCopy,
  2737. const AnsiString TargetDir, const TCopyParamType * CopyParam,
  2738. int Params, TFileOperationProgressType * OperationProgress,
  2739. bool & DisconnectWhenComplete)
  2740. {
  2741. assert(FilesToCopy && OperationProgress);
  2742. AnsiString FileName, FileNameOnly;
  2743. AnsiString FullTargetDir = IncludeTrailingBackslash(TargetDir);
  2744. const TRemoteFile * File;
  2745. bool Success;
  2746. int Index = 0;
  2747. while (Index < FilesToCopy->Count && !OperationProgress->Cancel)
  2748. {
  2749. Success = false;
  2750. FileName = FilesToCopy->Strings[Index];
  2751. File = (TRemoteFile *)FilesToCopy->Objects[Index];
  2752. assert(!FAvoidBusy);
  2753. FAvoidBusy = true;
  2754. try
  2755. {
  2756. try
  2757. {
  2758. SFTPSink(LocalCanonify(FileName), File, FullTargetDir, CopyParam,
  2759. Params, OperationProgress, tfFirstLevel);
  2760. Success = true;
  2761. }
  2762. catch(EScpSkipFile & E)
  2763. {
  2764. SUSPEND_OPERATION (
  2765. if (!FTerminal->HandleException(&E)) throw;
  2766. );
  2767. }
  2768. catch(...)
  2769. {
  2770. throw;
  2771. }
  2772. }
  2773. __finally
  2774. {
  2775. FAvoidBusy = false;
  2776. OperationProgress->Finish(FileName, Success, DisconnectWhenComplete);
  2777. }
  2778. Index++;
  2779. }
  2780. }
  2781. //---------------------------------------------------------------------------
  2782. void __fastcall TSFTPFileSystem::SFTPSink(const AnsiString FileName,
  2783. const TRemoteFile * File, const AnsiString TargetDir,
  2784. const TCopyParamType * CopyParam, int Params,
  2785. TFileOperationProgressType * OperationProgress, unsigned int Flags)
  2786. {
  2787. AnsiString OnlyFileName = UnixExtractFileName(FileName);
  2788. if (FLAGCLEAR(Params, cpDelete) &&
  2789. !CopyParam->AllowTransfer(OnlyFileName))
  2790. {
  2791. FTerminal->LogEvent(FORMAT("File \"%s\" excluded from transfer", (FileName)));
  2792. THROW_SKIP_FILE_NULL;
  2793. }
  2794. assert(File);
  2795. FTerminal->LogEvent(FORMAT("File: \"%s\"", (FileName)));
  2796. OperationProgress->SetFile(OnlyFileName);
  2797. AnsiString DestFileName = CopyParam->ChangeFileName(OnlyFileName,
  2798. osRemote, FLAGSET(Flags, tfFirstLevel));
  2799. AnsiString DestFullName = TargetDir + DestFileName;
  2800. if (File->IsDirectory)
  2801. {
  2802. if (!File->IsSymLink)
  2803. {
  2804. FILE_OPERATION_LOOP (FMTLOAD(NOT_DIRECTORY_ERROR, (DestFullName)),
  2805. int Attrs = FileGetAttr(DestFullName);
  2806. if ((Attrs & faDirectory) == 0) EXCEPTION;
  2807. );
  2808. FILE_OPERATION_LOOP (FMTLOAD(CREATE_DIR_ERROR, (DestFullName)),
  2809. if (!ForceDirectories(DestFullName)) EXCEPTION;
  2810. );
  2811. TSinkFileParams SinkFileParams;
  2812. SinkFileParams.TargetDir = IncludeTrailingBackslash(DestFullName);
  2813. SinkFileParams.CopyParam = CopyParam;
  2814. SinkFileParams.Params = Params;
  2815. SinkFileParams.OperationProgress = OperationProgress;
  2816. SinkFileParams.Skipped = false;
  2817. SinkFileParams.Flags = Flags & ~tfFirstLevel;
  2818. FTerminal->ProcessDirectory(FileName, SFTPSinkFile, &SinkFileParams);
  2819. // Do not delete directory if some of its files were skip.
  2820. // Throw "skip file" for the directory to avoid attempt to deletion
  2821. // of any parent directory
  2822. if ((Params & cpDelete) && SinkFileParams.Skipped)
  2823. {
  2824. THROW_SKIP_FILE_NULL;
  2825. }
  2826. }
  2827. else
  2828. {
  2829. // file is symlink to directory, currently do nothing, but it should be
  2830. // reported to user
  2831. }
  2832. }
  2833. else
  2834. {
  2835. FTerminal->LogEvent(FORMAT("Copying \"%s\" to local directory started.", (FileName)));
  2836. AnsiString DestPartinalFullName;
  2837. bool ResumeAllowed;
  2838. bool ResumeTransfer = false;
  2839. __int64 ResumeOffset;
  2840. // Will we use ASCII of BINARY file tranfer?
  2841. OperationProgress->SetAsciiTransfer(CopyParam->UseAsciiTransfer(DestFileName));
  2842. FTerminal->LogEvent(AnsiString((OperationProgress->AsciiTransfer ? "Ascii" : "Binary")) +
  2843. " transfer mode selected.");
  2844. // Suppose same data size to transfer as to write
  2845. // (not true with ASCII transfer)
  2846. OperationProgress->SetTransferSize(File->Size);
  2847. OperationProgress->SetLocalSize(OperationProgress->TransferSize);
  2848. // resume has no sense for drag&drop downloads, because files are downloaded
  2849. // to temp directory, but anyway TTerminal does know this, so the condition
  2850. // placement is currently wrong.
  2851. ResumeAllowed = ((Params & cpDragDrop) == 0) &&
  2852. !OperationProgress->AsciiTransfer &&
  2853. CopyParam->AllowResume(OperationProgress->TransferSize);
  2854. OperationProgress->SetResumeStatus(ResumeAllowed ? rsEnabled : rsDisabled);
  2855. int Attrs;
  2856. FILE_OPERATION_LOOP (FMTLOAD(NOT_FILE_ERROR, (DestFullName)),
  2857. Attrs = FileGetAttr(DestFullName);
  2858. if ((Attrs >= 0) && (Attrs & faDirectory)) EXCEPTION;
  2859. );
  2860. OperationProgress->TransferingFile = false; // not set with SFTP protocol
  2861. HANDLE LocalHandle = NULL;
  2862. TStream * FileStream = NULL;
  2863. bool DeleteLocalFile = false;
  2864. AnsiString RemoteHandle;
  2865. AnsiString LocalFileName = DestFullName;
  2866. try
  2867. {
  2868. if (ResumeAllowed)
  2869. {
  2870. DestPartinalFullName = DestFullName + FTerminal->Configuration->PartialExt;
  2871. LocalFileName = DestPartinalFullName;
  2872. FTerminal->LogEvent("Checking existence of partially transfered file.");
  2873. if (FileExists(DestPartinalFullName))
  2874. {
  2875. FTerminal->OpenLocalFile(DestPartinalFullName, GENERIC_WRITE,
  2876. NULL, &LocalHandle, NULL, NULL, NULL, &ResumeOffset);
  2877. ResumeTransfer = SFTPConfirmResume(DestFileName,
  2878. ResumeOffset > OperationProgress->TransferSize, OperationProgress);
  2879. if (!ResumeTransfer)
  2880. {
  2881. CloseHandle(LocalHandle);
  2882. LocalHandle = NULL;
  2883. Sysutils::DeleteFile(DestPartinalFullName);
  2884. }
  2885. else
  2886. {
  2887. FTerminal->LogEvent("Resuming file transfer.");
  2888. FileSeek((THandle)LocalHandle, ResumeOffset, 0);
  2889. OperationProgress->AddResumed(ResumeOffset);
  2890. }
  2891. }
  2892. }
  2893. if ((Attrs >= 0) && !ResumeTransfer &&
  2894. (FLAGSET(Params, cpNewerOnly) ||
  2895. (FTerminal->Configuration->ConfirmOverwriting &&
  2896. !OperationProgress->YesToAll &&
  2897. FLAGCLEAR(Params, cpNoConfirmation))))
  2898. {
  2899. __int64 DestFileSize;
  2900. unsigned long MTime;
  2901. FTerminal->OpenLocalFile(DestFullName, GENERIC_WRITE,
  2902. NULL, &LocalHandle, NULL, &MTime, NULL, &DestFileSize, false);
  2903. FTerminal->LogEvent("Checking existence of file.");
  2904. TSFTPOverwriteMode OverwriteMode;
  2905. TOverwriteFileParams FileParams;
  2906. FileParams.SourceSize = OperationProgress->TransferSize;
  2907. FileParams.SourceTimestamp = File->Modification;
  2908. FileParams.DestTimestamp = UnixToDateTime(MTime,
  2909. FTerminal->SessionData->ConsiderDST);
  2910. FileParams.DestSize = DestFileSize;
  2911. SFTPConfirmOverwrite(DestFileName, (DestFileSize >= OperationProgress->TransferSize),
  2912. OperationProgress, OverwriteMode, &FileParams);
  2913. if (OverwriteMode == omOverwrite)
  2914. {
  2915. // is NULL when overwritting read-only file
  2916. if (LocalHandle)
  2917. {
  2918. CloseHandle(LocalHandle);
  2919. LocalHandle = NULL;
  2920. }
  2921. }
  2922. else
  2923. {
  2924. // is NULL when overwritting read-only file, so following will
  2925. // probably fail anyway
  2926. if (LocalHandle == NULL)
  2927. {
  2928. FTerminal->OpenLocalFile(DestFullName, GENERIC_WRITE,
  2929. NULL, &LocalHandle, NULL, NULL, NULL, NULL);
  2930. }
  2931. ResumeAllowed = false;
  2932. FileSeek((THandle)LocalHandle, DestFileSize, 0);
  2933. if (OverwriteMode == omAppend)
  2934. {
  2935. FTerminal->LogEvent("Appending to file.");
  2936. }
  2937. else
  2938. {
  2939. FTerminal->LogEvent("Resuming file transfer (append style).");
  2940. assert(OverwriteMode == omResume);
  2941. OperationProgress->AddResumed(DestFileSize);
  2942. }
  2943. }
  2944. }
  2945. // if not already opened (resume, append...), create new empty file
  2946. if (!LocalHandle)
  2947. {
  2948. if (!FTerminal->CreateLocalFile(LocalFileName, OperationProgress, &LocalHandle))
  2949. {
  2950. THROW_SKIP_FILE_NULL;
  2951. }
  2952. }
  2953. assert(LocalHandle);
  2954. DeleteLocalFile = true;
  2955. FTerminal->LogEvent("Opening remote file.");
  2956. FILE_OPERATION_LOOP (FMTLOAD(SFTP_OPEN_FILE_ERROR, (FileName)),
  2957. int OpenType = SSH_FXF_READ;
  2958. if ((FVersion >= 4) && OperationProgress->AsciiTransfer)
  2959. {
  2960. OpenType |= SSH_FXF_TEXT;
  2961. }
  2962. RemoteHandle = SFTPOpenRemoteFile(FileName, OpenType);
  2963. );
  2964. FileStream = new THandleStream((THandle)LocalHandle);
  2965. unsigned long BlockSize = MaxTransferBlockSize(
  2966. 4 + 4 + RemoteHandle.Length() + 8);
  2967. // at end of this block queue is disposed
  2968. {
  2969. TSFTPDownloadQueue Queue(this);
  2970. TSFTPPacket DataPacket;
  2971. int QueueLen = int(File->Size / BlockSize) + 1;
  2972. if (QueueLen > FTerminal->SessionData->SFTPDownloadQueue)
  2973. {
  2974. QueueLen = FTerminal->SessionData->SFTPDownloadQueue;
  2975. }
  2976. Queue.Init(QueueLen, RemoteHandle, BlockSize,
  2977. OperationProgress->TransferedSize);
  2978. bool Eof = false;
  2979. bool PrevIncomplete = false;
  2980. int GapFillCount = 0;
  2981. int GapCount = 0;
  2982. unsigned long Missing = 0;
  2983. unsigned long DataLen = 0;
  2984. while (!Eof)
  2985. {
  2986. if (Missing > 0)
  2987. {
  2988. Queue.InitFillGapRequest(OperationProgress->TransferedSize, Missing,
  2989. &DataPacket);
  2990. GapFillCount++;
  2991. SendPacketAndReceiveResponse(&DataPacket, &DataPacket,
  2992. SSH_FXP_DATA, asEOF);
  2993. }
  2994. else
  2995. {
  2996. Queue.ReceivePacket(&DataPacket, SSH_FXP_DATA, asEOF);
  2997. }
  2998. if (DataPacket.Type == SSH_FXP_STATUS)
  2999. {
  3000. // must be SSH_FX_EOF, any other status packet would raise exception
  3001. Eof = true;
  3002. }
  3003. if (!Eof)
  3004. {
  3005. if ((Missing == 0) && PrevIncomplete)
  3006. {
  3007. // This can happen only if last request returned less bytes
  3008. // than expected, but exacly number of bytes missing to last
  3009. // known file size, but actually EOF was not reached.
  3010. // Can happen only when filesize has changed since directory
  3011. // listing and server returns less bytes than requested and
  3012. // fiel has some special file size.
  3013. FTerminal->LogEvent(FORMAT(
  3014. "Received incomplete data packet before end of file, "
  3015. "offset: %s, size: %d, requested: %d",
  3016. (IntToStr(OperationProgress->TransferedSize), int(DataLen),
  3017. int(BlockSize))));
  3018. FTerminal->TerminalError(NULL, LoadStr(SFTP_INCOMPLETE_BEFORE_EOF));
  3019. }
  3020. // Buffer for one block of data
  3021. TFileBuffer BlockBuf;
  3022. DataLen = DataPacket.GetCardinal();
  3023. PrevIncomplete = false;
  3024. if (Missing > 0)
  3025. {
  3026. assert(DataLen <= Missing);
  3027. Missing -= DataLen;
  3028. }
  3029. else if (DataLen < BlockSize)
  3030. {
  3031. if (OperationProgress->TransferedSize + DataLen !=
  3032. OperationProgress->TransferSize)
  3033. {
  3034. // with native text transfer mode (SFTP>=4), do not bother about
  3035. // getting less than requested, read offset is ignored anyway
  3036. if ((FVersion < 4) || !OperationProgress->AsciiTransfer)
  3037. {
  3038. GapCount++;
  3039. Missing = BlockSize - DataLen;
  3040. }
  3041. }
  3042. else
  3043. {
  3044. PrevIncomplete = true;
  3045. }
  3046. }
  3047. assert(DataLen <= BlockSize);
  3048. BlockBuf.Insert(0, DataPacket.NextData, DataLen);
  3049. OperationProgress->AddTransfered(DataLen);
  3050. if (OperationProgress->AsciiTransfer)
  3051. {
  3052. assert(!ResumeTransfer && !ResumeAllowed);
  3053. unsigned int PrevBlockSize = BlockBuf.Size;
  3054. BlockBuf.Convert(GetEOL(), FTerminal->Configuration->LocalEOLType, 0);
  3055. OperationProgress->SetLocalSize(
  3056. OperationProgress->LocalSize - PrevBlockSize + BlockBuf.Size);
  3057. }
  3058. FILE_OPERATION_LOOP (FMTLOAD(WRITE_ERROR, (LocalFileName)),
  3059. BlockBuf.WriteToStream(FileStream, BlockBuf.Size);
  3060. );
  3061. OperationProgress->AddLocalyUsed(BlockBuf.Size);
  3062. if (OperationProgress->Cancel == csCancel)
  3063. {
  3064. Abort();
  3065. }
  3066. }
  3067. };
  3068. if (GapCount > 0)
  3069. {
  3070. FTerminal->LogEvent(FORMAT(
  3071. "%d requests to fill %d data gaps were issued.",
  3072. (GapFillCount, GapCount)));
  3073. }
  3074. }
  3075. if (CopyParam->PreserveTime)
  3076. {
  3077. FILETIME AcTime = DateTimeToFileTime(File->LastAccess,
  3078. FTerminal->SessionData->ConsiderDST);
  3079. FILETIME WrTime = DateTimeToFileTime(File->Modification,
  3080. FTerminal->SessionData->ConsiderDST);
  3081. SetFileTime(LocalHandle, NULL, &AcTime, &WrTime);
  3082. }
  3083. CloseHandle(LocalHandle);
  3084. LocalHandle = NULL;
  3085. if (ResumeAllowed)
  3086. {
  3087. FILE_OPERATION_LOOP(FMTLOAD(RENAME_AFTER_RESUME_ERROR,
  3088. (ExtractFileName(DestPartinalFullName), DestFileName)),
  3089. if (FileExists(DestFullName))
  3090. {
  3091. if (!Sysutils::DeleteFile(DestFullName)) EXCEPTION;
  3092. }
  3093. if (!Sysutils::RenameFile(DestPartinalFullName, DestFullName))
  3094. {
  3095. EXCEPTION;
  3096. }
  3097. );
  3098. }
  3099. DeleteLocalFile = false;
  3100. if (Attrs == -1)
  3101. {
  3102. Attrs = faArchive;
  3103. }
  3104. int NewAttrs = CopyParam->LocalFileAttrs(*File->Rights);
  3105. if ((NewAttrs & Attrs) != NewAttrs)
  3106. {
  3107. FILE_OPERATION_LOOP (FMTLOAD(CANT_SET_ATTRS, (DestFullName)),
  3108. FileSetAttr(DestFullName, Attrs | NewAttrs);
  3109. );
  3110. }
  3111. }
  3112. __finally
  3113. {
  3114. if (LocalHandle) CloseHandle(LocalHandle);
  3115. if (FileStream) delete FileStream;
  3116. if (DeleteLocalFile && (!ResumeAllowed || OperationProgress->LocalyUsed == 0))
  3117. {
  3118. Sysutils::DeleteFile(LocalFileName);
  3119. }
  3120. if (FTerminal->Active && !RemoteHandle.IsEmpty())
  3121. {
  3122. FILE_OPERATION_LOOP(FMTLOAD(SFTP_CLOSE_FILE_ERROR, (DestFileName)),
  3123. TSFTPPacket CloseRequest(SSH_FXP_CLOSE);
  3124. CloseRequest.AddString(RemoteHandle);
  3125. SendPacketAndReceiveResponse(&CloseRequest, NULL, SSH_FXP_STATUS);
  3126. );
  3127. }
  3128. }
  3129. }
  3130. if (Params & cpDelete)
  3131. {
  3132. // If file is directory, do not delete it recursively, because it should be
  3133. // empty already. If not, it should not be deleted (some files were
  3134. // skipped or some new files were copied to it, while we were downloading)
  3135. bool Recursive = false;
  3136. FTerminal->DeleteFile(FileName, File, &Recursive);
  3137. }
  3138. }
  3139. //---------------------------------------------------------------------------
  3140. void __fastcall TSFTPFileSystem::SFTPSinkFile(AnsiString FileName,
  3141. const TRemoteFile * File, void * Param)
  3142. {
  3143. TSinkFileParams * Params = (TSinkFileParams *)Param;
  3144. assert(Params->OperationProgress);
  3145. try
  3146. {
  3147. SFTPSink(FileName, File, Params->TargetDir, Params->CopyParam,
  3148. Params->Params, Params->OperationProgress, Params->Flags);
  3149. }
  3150. catch(EScpSkipFile & E)
  3151. {
  3152. TFileOperationProgressType * OperationProgress = Params->OperationProgress;
  3153. Params->Skipped = true;
  3154. SUSPEND_OPERATION (
  3155. if (!FTerminal->HandleException(&E)) throw;
  3156. );
  3157. if (OperationProgress->Cancel)
  3158. {
  3159. Abort();
  3160. }
  3161. }
  3162. }