SftpFileSystem.cpp 92 KB

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