SftpFileSystem.cpp 99 KB

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