SftpFileSystem.cpp 82 KB

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