SecureShell.cpp 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345
  1. //---------------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. #include <stdio.h>
  5. #include "PuttyIntf.h"
  6. #include "Net.h"
  7. #include "Interface.h"
  8. #include "SecureShell.h"
  9. #include "TextsCore.h"
  10. #include "Common.h"
  11. #include "ScpMain.h"
  12. #include "Security.h"
  13. #ifndef AUTO_WINSOCK
  14. #include <winsock2.h>
  15. #endif
  16. //---------------------------------------------------------------------------
  17. #pragma package(smart_init)
  18. //---------------------------------------------------------------------------
  19. #define MAX_BUFSIZE 16384
  20. const TColor LogLineColors[] =
  21. {clGreen, clRed, clMaroon, clBlue, clGray};
  22. //---------------------------------------------------------------------------
  23. __fastcall TSecureShell::TSecureShell()
  24. {
  25. FSessionData = new TSessionData("");
  26. FActive = False;
  27. PendLen = 0;
  28. PendSize = 0;
  29. FStdErrorTemp = "";
  30. FBytesSent = 0;
  31. FBytesReceived = 0;
  32. FLog = new TSessionLog(this);
  33. FOnQueryUser = NULL;
  34. FOnPromptUser = NULL;
  35. FOnShowExtendedException = NULL;
  36. FOnUpdateStatus = NULL;
  37. FOnClose = NULL;
  38. FCSCipher = cipWarn; // = not detected yet
  39. FSCCipher = cipWarn;
  40. FReachedStatus = 0;
  41. UpdateStatus(sshClosed);
  42. FConfig = new Config();
  43. FSocket = new SOCKET;
  44. FMaxPacketSize = 0;
  45. }
  46. //---------------------------------------------------------------------------
  47. __fastcall TSecureShell::~TSecureShell()
  48. {
  49. if (FReachedStatus)
  50. {
  51. TCoreGuard Guard;
  52. SessionsCount--;
  53. if (SessionsCount == 0)
  54. {
  55. NetFinalize();
  56. }
  57. }
  58. ClearStdError();
  59. Active = false;
  60. SAFE_DESTROY(FSessionData);
  61. SAFE_DESTROY(FLog);
  62. delete FConfig;
  63. FConfig = NULL;
  64. UserObject = NULL;
  65. delete FSocket;
  66. FSocket = NULL;
  67. }
  68. //---------------------------------------------------------------------------
  69. void __fastcall TSecureShell::Open()
  70. {
  71. const char * InitError;
  72. char * RealHost;
  73. FPasswordTried = false;
  74. FPasswordTriedForKI = false;
  75. FReachedStatus = 0;
  76. {
  77. TCoreGuard Guard;
  78. SessionsCount++;
  79. if (SessionsCount == 1)
  80. {
  81. UpdateStatus(sshInitWinSock);
  82. NetInitialize();
  83. }
  84. }
  85. Log->AddStartupInfo();
  86. Active = false;
  87. FBackend = &ssh_backend;
  88. FAuthenticationLog = "";
  89. UpdateStatus(sshLookupHost);
  90. SessionData->StoreToConfig(FConfig);
  91. InitError = FBackend->init(this, &FBackendHandle, FConfig,
  92. SessionData->HostName.c_str(), SessionData->PortNumber, &RealHost, 0,
  93. FConfig->tcp_keepalives);
  94. if (InitError)
  95. {
  96. FatalError(InitError);
  97. }
  98. FRealHost = RealHost;
  99. UpdateStatus(sshConnect);
  100. /*FLoggingContext = log_init(this, (void *)FConfig);
  101. FBackend->provide_logctx(FBackendHandle, FLoggingContext);*/
  102. Init();
  103. CheckConnection(CONNECTION_FAILED);
  104. FLastDataSent = Now();
  105. FLoginTime = Now();
  106. Log->AddSeparator();
  107. UpdateStatus(sshAuthenticated);
  108. }
  109. //---------------------------------------------------------------------------
  110. void __fastcall TSecureShell::Init()
  111. {
  112. try
  113. {
  114. try
  115. {
  116. while (!FBackend->sendok(FBackendHandle))
  117. {
  118. WaitForData();
  119. }
  120. }
  121. catch(Exception & E)
  122. {
  123. if ((FReachedStatus == sshAuthenticate) && !FAuthenticationLog.IsEmpty())
  124. {
  125. FatalError(&E, FMTLOAD(AUTHENTICATION_LOG, (FAuthenticationLog)));
  126. }
  127. else
  128. {
  129. throw;
  130. }
  131. }
  132. }
  133. catch(Exception & E)
  134. {
  135. if (FReachedStatus == sshAuthenticate)
  136. {
  137. FatalError(&E, LoadStr(AUTHENTICATION_FAILED));
  138. }
  139. else
  140. {
  141. throw;
  142. }
  143. }
  144. }
  145. //---------------------------------------------------------------------------
  146. void __fastcall TSecureShell::PuttyLogEvent(const AnsiString & Str)
  147. {
  148. #define SERVER_VERSION_MSG "Server version: "
  149. // Gross hack
  150. if (Str.Pos(SERVER_VERSION_MSG) == 1)
  151. {
  152. FSshVersionString = Str.SubString(strlen(SERVER_VERSION_MSG) + 1,
  153. Str.Length() - strlen(SERVER_VERSION_MSG));
  154. }
  155. LogEvent(Str);
  156. }
  157. //---------------------------------------------------------------------------
  158. AnsiString __fastcall TSecureShell::GetSshImplementation()
  159. {
  160. const char * Ptr = strchr(FSshVersionString.c_str(), '-');
  161. if (Ptr != NULL)
  162. {
  163. Ptr = strchr(Ptr + 1, '-');
  164. }
  165. return (Ptr != NULL) ? AnsiString(Ptr + 1) : AnsiString();
  166. }
  167. //---------------------------------------------------------------------
  168. AnsiString __fastcall TSecureShell::GetPassword()
  169. {
  170. return DecryptPassword(FPassword, SessionData->SessionName);
  171. }
  172. //---------------------------------------------------------------------------
  173. bool __fastcall TSecureShell::PromptUser(const AnsiString Prompt,
  174. AnsiString & Response, bool IsPassword)
  175. {
  176. assert(IsPassword);
  177. bool Result;
  178. if (Prompt.Pos("Passphrase for key ") == 1)
  179. {
  180. AnsiString Key(Prompt);
  181. int P = Prompt.Pos("\"");
  182. if (P > 0)
  183. {
  184. Key.Delete(1, P);
  185. P = Key.LastDelimiter("\"");
  186. if (P > 0)
  187. {
  188. Key.SetLength(P - 1);
  189. }
  190. }
  191. LogEvent(FORMAT("Passphrase prompt (%s)", (Prompt)));
  192. Result = DoPromptUser(FMTLOAD(PROMPT_KEY_PASSPHRASE, (Key)),
  193. pkPassphrase, Response);
  194. }
  195. else if (Prompt.Pos("'s password: "))
  196. {
  197. LogEvent(FORMAT("Session password prompt (%s)", (Prompt)));
  198. if (!SessionData->Password.IsEmpty() && !FPasswordTried)
  199. {
  200. LogEvent("Using stored password.");
  201. Result = true;
  202. Response = SessionData->Password;
  203. }
  204. else
  205. {
  206. LogEvent("Asking user for password.");
  207. Result = DoPromptUser(
  208. FMTLOAD(PROMPT_SESSION_PASSWORD, (SessionData->SessionName)),
  209. pkPassword, Response);
  210. }
  211. FPasswordTried = true;
  212. }
  213. else
  214. {
  215. // in other cases we assume TIS/Cryptocard/keyboard-interactive authentification prompt
  216. LogEvent(FORMAT("%s prompt from server (%s)",
  217. (IsPassword ? "Password" : "Normal", Prompt)));
  218. if (!SessionData->Password.IsEmpty() && IsPassword &&
  219. SessionData->AuthKIPassword && !FPasswordTriedForKI)
  220. {
  221. LogEvent("Responding with stored password.");
  222. Result = true;
  223. Response = SessionData->Password;
  224. }
  225. else
  226. {
  227. LogEvent("Asking user for response.");
  228. static const AnsiString ResponseSuffix("\r\nResponse: ");
  229. // Strip Cryptocard/TIS "Response" suffix
  230. AnsiString UserPrompt = Prompt;
  231. if (UserPrompt.SubString(UserPrompt.Length() - ResponseSuffix.Length() + 1,
  232. ResponseSuffix.Length()) == ResponseSuffix)
  233. {
  234. UserPrompt.SetLength(UserPrompt.Length() - ResponseSuffix.Length());
  235. }
  236. Result = DoPromptUser(UserPrompt, pkServerPrompt, Response);
  237. }
  238. FPasswordTriedForKI = true;
  239. };
  240. if (Configuration->RememberPassword)
  241. {
  242. FPassword = EncryptPassword(Response, SessionData->SessionName);
  243. }
  244. return Result;
  245. }
  246. //---------------------------------------------------------------------------
  247. bool __fastcall TSecureShell::DoPromptUser(AnsiString Prompt, TPromptKind Kind,
  248. AnsiString & Response)
  249. {
  250. bool Result = false;
  251. if (OnPromptUser != NULL)
  252. {
  253. OnPromptUser(this, Prompt, Kind, Response, Result);
  254. }
  255. return Result;
  256. }
  257. //---------------------------------------------------------------------------
  258. void __fastcall TSecureShell::GotHostKey()
  259. {
  260. UpdateStatus(sshAuthenticate);
  261. }
  262. //---------------------------------------------------------------------------
  263. void __fastcall TSecureShell::FromBackend(Boolean IsStdErr, char * Data, Integer Length)
  264. {
  265. CheckConnection();
  266. // Following is taken from scp.c from_backend() and modified
  267. if (IsStdErr)
  268. {
  269. AddStdError(AnsiString(Data, Length));
  270. }
  271. else
  272. {
  273. unsigned char *p = (unsigned char *)Data;
  274. unsigned Len = (unsigned)Length;
  275. // If this is before the real session begins, raise exception.
  276. if (!OutPtr)
  277. {
  278. FatalError("Internal error: Session not yet begun.");
  279. }
  280. if ((OutLen > 0) && (Len > 0))
  281. {
  282. unsigned Used = OutLen;
  283. if (Used > Len) Used = Len;
  284. memcpy(OutPtr, p, Used);
  285. OutPtr += Used; OutLen -= Used;
  286. p += Used; Len -= Used;
  287. }
  288. if (Len > 0)
  289. {
  290. if (PendSize < PendLen + Len)
  291. {
  292. PendSize = PendLen + Len + 4096;
  293. Pending = (char *)
  294. (Pending ? srealloc(Pending, PendSize) : smalloc(PendSize));
  295. if (!Pending) FatalError("Out of memory");
  296. }
  297. memcpy(Pending + PendLen, p, Len);
  298. PendLen += Len;
  299. }
  300. }
  301. }
  302. //---------------------------------------------------------------------------
  303. Integer __fastcall TSecureShell::Receive(char * Buf, Integer Len)
  304. {
  305. CheckConnection();
  306. if (Len > 0)
  307. {
  308. // Following is taken from scp.c ssh_scp_recv() and modified
  309. OutPtr = Buf;
  310. OutLen = Len;
  311. /*
  312. * See if the pending-input block contains some of what we
  313. * need.
  314. */
  315. if (PendLen > 0)
  316. {
  317. unsigned PendUsed = PendLen;
  318. if (PendUsed > OutLen)
  319. {
  320. PendUsed = OutLen;
  321. }
  322. memcpy(OutPtr, Pending, PendUsed);
  323. memmove(Pending, Pending + PendUsed, PendLen - PendUsed);
  324. OutPtr += PendUsed;
  325. OutLen -= PendUsed;
  326. PendLen -= PendUsed;
  327. if (PendLen == 0)
  328. {
  329. PendSize = 0;
  330. sfree(Pending);
  331. Pending = NULL;
  332. }
  333. }
  334. // I don't undestand this yet, but it works :-)
  335. while (OutLen > 0) WaitForData();
  336. // This seems ambiguous
  337. if (Len <= 0) FatalError(LoadStr(LOST_CONNECTION));
  338. };
  339. FBytesReceived += Len;
  340. return Len;
  341. }
  342. //---------------------------------------------------------------------------
  343. AnsiString __fastcall TSecureShell::ReceiveLine()
  344. {
  345. unsigned Index;
  346. Char Ch;
  347. AnsiString Line;
  348. Boolean EOL = False;
  349. do
  350. {
  351. // If there is any buffer of received chars
  352. if (PendLen > 0)
  353. {
  354. Index = 0;
  355. // Repeat until we walk thru whole buffer or reach end-of-line
  356. while ((Index < PendLen) && (!Index || (Pending[Index-1] != '\n')))
  357. {
  358. Index++;
  359. }
  360. EOL = (Boolean)(Index && (Pending[Index-1] == '\n'));
  361. Integer PrevLen = Line.Length();
  362. Line.SetLength(PrevLen + Index);
  363. Receive(Line.c_str() + PrevLen, Index);
  364. }
  365. // If buffer don't contain end-of-line character
  366. // we read one more which causes receiving new buffer of chars
  367. if (!EOL)
  368. {
  369. Receive(&Ch, 1);
  370. Line += Ch;
  371. EOL = (Ch == '\n');
  372. }
  373. }
  374. while (!EOL);
  375. // We don't want end-of-line character
  376. Line.SetLength(Line.Length()-1);
  377. Log->Add(llOutput, Line);
  378. return Line;
  379. }
  380. //---------------------------------------------------------------------------
  381. void __fastcall TSecureShell::SendSpecial(int Code)
  382. {
  383. LogEvent(FORMAT("Sending special code: %d", (Code)));
  384. CheckConnection();
  385. FBackend->special(FBackendHandle, (Telnet_Special)Code);
  386. FLastDataSent = Now();
  387. }
  388. //---------------------------------------------------------------------------
  389. void __fastcall TSecureShell::SendEOF()
  390. {
  391. SendSpecial(TS_EOF);
  392. }
  393. //---------------------------------------------------------------------------
  394. void __fastcall TSecureShell::Send(const char * Buf, Integer Len)
  395. {
  396. CheckConnection();
  397. int BufSize = FBackend->send(FBackendHandle, (char *)Buf, Len);
  398. FLastDataSent = Now();
  399. FBytesSent += Len;
  400. while (BufSize > MAX_BUFSIZE)
  401. {
  402. WaitForData();
  403. BufSize = FBackend->sendbuffer(FBackendHandle);
  404. }
  405. }
  406. //---------------------------------------------------------------------------
  407. void __fastcall TSecureShell::SendNull()
  408. {
  409. LogEvent("Sending NULL.");
  410. Send("", 1);
  411. }
  412. //---------------------------------------------------------------------------
  413. void __fastcall TSecureShell::SendStr(AnsiString Str)
  414. {
  415. CheckConnection();
  416. Send(Str.c_str(), Str.Length());
  417. }
  418. //---------------------------------------------------------------------------
  419. void __fastcall TSecureShell::SendLine(AnsiString Line)
  420. {
  421. SendStr(Line);
  422. Send("\n", 1);
  423. Log->Add(llInput, Line);
  424. }
  425. //---------------------------------------------------------------------------
  426. void __fastcall TSecureShell::AddStdError(AnsiString Str)
  427. {
  428. StdError += Str;
  429. Integer P;
  430. Str = DeleteChar(Str, '\r');
  431. // We send only whole line at once to log, so we have to cache
  432. // incoming std error data
  433. FStdErrorTemp += Str;
  434. AnsiString Line;
  435. // Do we have at least one complete line in std error cache?
  436. while ((P = FStdErrorTemp.Pos("\n")) > 0)
  437. {
  438. Line = FStdErrorTemp.SubString(1, P-1);
  439. FStdErrorTemp.Delete(1, P);
  440. if (Status == sshAuthenticate)
  441. {
  442. FAuthenticationLog += (FAuthenticationLog.IsEmpty() ? "" : "\n") + Line;
  443. }
  444. Log->Add(llStdError, Line);
  445. }
  446. }
  447. //---------------------------------------------------------------------------
  448. void __fastcall TSecureShell::ClearStdError()
  449. {
  450. // Flush std error cache
  451. if (!FStdErrorTemp.IsEmpty())
  452. {
  453. if (Status == sshAuthenticate)
  454. {
  455. FAuthenticationLog +=
  456. (FAuthenticationLog.IsEmpty() ? "" : "\n") + FStdErrorTemp;
  457. }
  458. Log->Add(llStdError, FStdErrorTemp);
  459. FStdErrorTemp = "";
  460. }
  461. StdError = "";
  462. }
  463. //---------------------------------------------------------------------------
  464. void __fastcall TSecureShell::FatalError(Exception * E, AnsiString Msg)
  465. {
  466. if (FActive)
  467. {
  468. // We log this instead of exception handler, because Close() would
  469. // probably cause exception handler to loose pointer to TShellLog()
  470. LogEvent("Attempt to close connection due to fatal exception:");
  471. Log->Add(llException, Msg);
  472. Log->AddException(E);
  473. Close();
  474. }
  475. SSH_FATAL_ERROR_EXT(E, Msg);
  476. }
  477. //---------------------------------------------------------------------------
  478. void __fastcall TSecureShell::FatalError(AnsiString Error)
  479. {
  480. FatalError(NULL, Error);
  481. }
  482. //---------------------------------------------------------------------------
  483. void __fastcall TSecureShell::SetSocket(void * value)
  484. {
  485. assert(value);
  486. if (FActive && (*static_cast<SOCKET*>(value) != INVALID_SOCKET))
  487. FatalError("Cannot set socket during connection");
  488. assert(FSocket);
  489. *static_cast<SOCKET*>(FSocket) = *static_cast<SOCKET*>(value);
  490. if (*static_cast<SOCKET*>(FSocket) != INVALID_SOCKET)
  491. {
  492. FActive = true;
  493. }
  494. else
  495. {
  496. Discard();
  497. }
  498. }
  499. //---------------------------------------------------------------------------
  500. void __fastcall TSecureShell::SetSessionData(TSessionData * value)
  501. {
  502. assert(!FActive);
  503. FSessionData->Assign(value);
  504. }
  505. //---------------------------------------------------------------------------
  506. void __fastcall TSecureShell::SetActive(bool value)
  507. {
  508. if (FActive != value)
  509. {
  510. if (value)
  511. {
  512. Open();
  513. }
  514. else
  515. {
  516. Close();
  517. }
  518. }
  519. }
  520. //---------------------------------------------------------------------------
  521. bool __fastcall TSecureShell::GetActive() const
  522. {
  523. return FActive;
  524. }
  525. //---------------------------------------------------------------------------
  526. void __fastcall TSecureShell::Discard()
  527. {
  528. bool WasActive = FActive;
  529. FActive = false;
  530. if (WasActive && OnClose)
  531. {
  532. OnClose(this);
  533. }
  534. FStatus = sshClosed;
  535. }
  536. //---------------------------------------------------------------------------
  537. void __fastcall TSecureShell::Close()
  538. {
  539. LogEvent("Closing connection.");
  540. CheckConnection();
  541. ssh_close(FBackendHandle);
  542. // This should be called insted, but there seem tu be error in freeing
  543. // FBackend->free(FBackendHandle);
  544. Discard();
  545. }
  546. //---------------------------------------------------------------------------
  547. void inline __fastcall TSecureShell::CheckConnection(int Message)
  548. {
  549. if (!FActive || get_ssh_state_closed(FBackendHandle))
  550. {
  551. AnsiString Str = LoadStr(Message >= 0 ? Message : NOT_CONNECTED);
  552. int ExitCode = get_ssh_exitcode(FBackendHandle);
  553. if (ExitCode >= 0)
  554. {
  555. Str += " " + FMTLOAD(SSH_EXITCODE, (ExitCode));
  556. }
  557. FatalError(Str);
  558. }
  559. }
  560. //---------------------------------------------------------------------------
  561. extern int select_result(WPARAM, LPARAM);
  562. void __fastcall TSecureShell::WaitForData()
  563. {
  564. int R;
  565. do
  566. {
  567. CheckConnection();
  568. struct timeval time;
  569. fd_set readfds;
  570. FD_ZERO(&readfds);
  571. FD_SET(*static_cast<SOCKET*>(FSocket), &readfds);
  572. time.tv_sec = FSessionData->Timeout;
  573. time.tv_usec = 0;
  574. R = select(1, &readfds, NULL, NULL, &time);
  575. if (R < 0)
  576. {
  577. SSH_FATAL_ERROR(
  578. Format("Cannot determine status of socket (%d).", ARRAYOFCONST((R))));
  579. }
  580. else if (R == 0)
  581. {
  582. LogEvent("Waiting for data timed out, asking user what to do.");
  583. TQueryParams Params(qpFatalAbort | qpAllowContinueOnError);
  584. if (DoQueryUser(FMTLOAD(CONFIRM_PROLONG_TIMEOUT, (FSessionData->Timeout)),
  585. qaRetry | qaAbort, &Params) != qaRetry)
  586. {
  587. FatalError(LoadStr(USER_TERMINATED));
  588. }
  589. }
  590. }
  591. while (R <= 0);
  592. select_result((WPARAM)(*static_cast<SOCKET*>(FSocket)), (LPARAM)FD_READ);
  593. }
  594. //---------------------------------------------------------------------------
  595. bool __fastcall TSecureShell::SshFallbackCmd() const
  596. {
  597. return ssh_fallback_cmd(FBackendHandle);
  598. }
  599. //---------------------------------------------------------------------------
  600. void __fastcall TSecureShell::Error(const AnsiString Error) const
  601. {
  602. SSH_ERROR(Error);
  603. }
  604. //---------------------------------------------------------------------------
  605. void __fastcall TSecureShell::Idle()
  606. {
  607. // In Putty this is called each 100 ms when in foreground and
  608. // each 10 min when in background
  609. noise_regular();
  610. // Keep session alive
  611. if ((FSessionData->PingType != ptOff) &&
  612. (Now() - FLastDataSent > FSessionData->PingIntervalDT))
  613. {
  614. KeepAlive();
  615. // in case keep alive could not be processed, postpone next attempt
  616. FLastDataSent = Now();
  617. }
  618. }
  619. //---------------------------------------------------------------------------
  620. void __fastcall TSecureShell::KeepAlive()
  621. {
  622. LogEvent("Sending null packet to keep session alive.");
  623. SendSpecial(TS_PING);
  624. }
  625. //---------------------------------------------------------------------------
  626. void __fastcall TSecureShell::SetLog(TSessionLog * value)
  627. {
  628. FLog->Assign(value);
  629. }
  630. //---------------------------------------------------------------------------
  631. void __fastcall TSecureShell::SetConfiguration(TConfiguration *value)
  632. {
  633. FConfiguration = value;
  634. Log->Configuration = value;
  635. }
  636. //---------------------------------------------------------------------------
  637. TDateTime __fastcall TSecureShell::GetDuration() const
  638. {
  639. return Now() - FLoginTime;
  640. }
  641. //---------------------------------------------------------------------------
  642. unsigned long __fastcall TSecureShell::MaxPacketSize()
  643. {
  644. if (SshVersion == 1)
  645. {
  646. return 0;
  647. }
  648. else
  649. {
  650. if (FMaxPacketSize == 0)
  651. {
  652. unsigned long RemWindow = ssh2_remwindow(FBackendHandle);
  653. FMaxPacketSize = ssh2_remmaxpkt(FBackendHandle);
  654. if (RemWindow < FMaxPacketSize)
  655. {
  656. FMaxPacketSize = RemWindow;
  657. }
  658. }
  659. return FMaxPacketSize;
  660. }
  661. }
  662. //---------------------------------------------------------------------------
  663. TCompressionType __fastcall TSecureShell::FuncToCompression(
  664. const void * Compress) const
  665. {
  666. if (SshVersion == 1)
  667. {
  668. return get_ssh1_compressing(FBackendHandle) ? ctZLib : ctNone;
  669. }
  670. else
  671. {
  672. return (ssh_compress *)Compress == &ssh_zlib ? ctZLib : ctNone;
  673. }
  674. }
  675. //---------------------------------------------------------------------------
  676. TCompressionType __fastcall TSecureShell::GetCSCompression() const
  677. {
  678. assert(Active);
  679. return FuncToCompression(get_cscomp(FBackendHandle));
  680. }
  681. //---------------------------------------------------------------------------
  682. TCompressionType __fastcall TSecureShell::GetSCCompression() const
  683. {
  684. assert(Active);
  685. return FuncToCompression(get_sccomp(FBackendHandle));
  686. }
  687. //---------------------------------------------------------------------------
  688. int __fastcall TSecureShell::GetSshVersion() const
  689. {
  690. assert(Active);
  691. return get_ssh_version(FBackendHandle);
  692. }
  693. //---------------------------------------------------------------------------
  694. TCipher __fastcall TSecureShell::FuncToSsh1Cipher(const void * Cipher) const
  695. {
  696. const ssh_cipher *CipherFuncs[] =
  697. {&ssh_3des, &ssh_des, &ssh_blowfish_ssh1};
  698. const TCipher TCiphers[] = {cip3DES, cipDES, cipBlowfish};
  699. assert(LENOF(CipherFuncs) == LENOF(TCiphers));
  700. TCipher Result = cipWarn;
  701. for (int Index = 0; Index < LENOF(TCiphers); Index++)
  702. {
  703. if ((ssh_cipher *)Cipher == CipherFuncs[Index])
  704. {
  705. Result = TCiphers[Index];
  706. }
  707. }
  708. assert(Result != cipWarn);
  709. return Result;
  710. }
  711. //---------------------------------------------------------------------------
  712. TCipher __fastcall TSecureShell::FuncToSsh2Cipher(const void * Cipher) const
  713. {
  714. const ssh2_ciphers *CipherFuncs[] =
  715. {&ssh2_3des, &ssh2_des, &ssh2_aes, &ssh2_blowfish};
  716. const TCipher TCiphers[] = {cip3DES, cipDES, cipAES, cipBlowfish};
  717. assert(LENOF(CipherFuncs) == LENOF(TCiphers));
  718. TCipher Result = cipWarn;
  719. for (int C = 0; C < LENOF(TCiphers); C++)
  720. {
  721. for (int F = 0; F < CipherFuncs[C]->nciphers; F++)
  722. {
  723. if ((ssh2_cipher *)Cipher == CipherFuncs[C]->list[F])
  724. {
  725. Result = TCiphers[C];
  726. }
  727. }
  728. }
  729. assert(Result != cipWarn);
  730. return Result;
  731. }
  732. //---------------------------------------------------------------------------
  733. TCipher __fastcall TSecureShell::GetCSCipher()
  734. {
  735. assert(Active);
  736. if (FCSCipher == cipWarn)
  737. {
  738. if (SshVersion == 1)
  739. {
  740. FCSCipher = FuncToSsh1Cipher(get_cipher(FBackendHandle));
  741. }
  742. else
  743. {
  744. FCSCipher = FuncToSsh2Cipher(get_cscipher(FBackendHandle));
  745. }
  746. }
  747. return FCSCipher;
  748. }
  749. //---------------------------------------------------------------------------
  750. TCipher __fastcall TSecureShell::GetSCCipher()
  751. {
  752. CheckConnection();
  753. if (FSCCipher == cipWarn)
  754. {
  755. if (SshVersion == 1)
  756. {
  757. FSCCipher = FuncToSsh1Cipher(get_cipher(FBackendHandle));
  758. }
  759. else
  760. {
  761. FSCCipher = FuncToSsh2Cipher(get_sccipher(FBackendHandle));
  762. }
  763. }
  764. return FSCCipher;
  765. }
  766. //---------------------------------------------------------------------------
  767. void __fastcall TSecureShell::DoShowExtendedException(Exception * E)
  768. {
  769. DoHandleExtendedException(E);
  770. if (OnShowExtendedException != NULL)
  771. {
  772. OnShowExtendedException(this, E);
  773. }
  774. }
  775. //---------------------------------------------------------------------------
  776. void __fastcall TSecureShell::DoHandleExtendedException(Exception * E)
  777. {
  778. Log->AddException(E);
  779. }
  780. //---------------------------------------------------------------------------
  781. int __fastcall TSecureShell::DoQueryUser(const AnsiString Query,
  782. TStrings * MoreMessages, int Answers, const TQueryParams * Params, TQueryType Type)
  783. {
  784. LogEvent(FORMAT("Asking user:\n%s (%s)", (Query, (MoreMessages ? MoreMessages->CommaText : AnsiString() ))));
  785. int Answer = qaCancel;
  786. if (FOnQueryUser)
  787. {
  788. FOnQueryUser(this, Query, MoreMessages, Answers, Params, Answer, Type);
  789. }
  790. return Answer;
  791. }
  792. //---------------------------------------------------------------------------
  793. int __fastcall TSecureShell::DoQueryUser(const AnsiString Query,
  794. const AnsiString OtherMessage, int Answers, const TQueryParams * Params)
  795. {
  796. TStrings * MoreMessages = new TStringList();
  797. Integer Result;
  798. try {
  799. if (!OtherMessage.IsEmpty()) MoreMessages->Add(OtherMessage);
  800. Result = DoQueryUser(Query, MoreMessages, Answers, Params);
  801. } __finally {
  802. delete MoreMessages;
  803. }
  804. return Result;
  805. }
  806. //---------------------------------------------------------------------------
  807. int __fastcall TSecureShell::DoQueryUser(const AnsiString Query,
  808. int Answers, const TQueryParams * Params)
  809. {
  810. return DoQueryUser(Query, "", Answers, Params);
  811. }
  812. //---------------------------------------------------------------------------
  813. int __fastcall TSecureShell::DoQueryUser(const AnsiString Query,
  814. Exception * E, int Answers, const TQueryParams * Params)
  815. {
  816. int Result;
  817. TStrings * MoreMessages = new TStringList();
  818. try
  819. {
  820. if (E->InheritsFrom(__classid(ExtException)) &&
  821. ((ExtException*)E)->MoreMessages)
  822. {
  823. MoreMessages->Assign(((ExtException*)E)->MoreMessages);
  824. }
  825. if (!E->Message.IsEmpty() && !Query.IsEmpty()) MoreMessages->Insert(0, E->Message);
  826. Result = DoQueryUser(!Query.IsEmpty() ? Query : E->Message,
  827. MoreMessages->Count ? MoreMessages : NULL,
  828. Answers, Params);
  829. }
  830. __finally
  831. {
  832. delete MoreMessages;
  833. }
  834. return Result;
  835. }
  836. //---------------------------------------------------------------------------
  837. void __fastcall TSecureShell::VerifyHostKey(const AnsiString Host, int Port,
  838. const AnsiString KeyType, const AnsiString KeyStr, const AnsiString Fingerprint)
  839. {
  840. GotHostKey();
  841. int Result;
  842. // Verify the key against the registry.
  843. Result = verify_host_key(Host.c_str(), Port, KeyType.c_str(), KeyStr.c_str());
  844. if (Result != 0)
  845. {
  846. int R = DoQueryUser(
  847. FMTLOAD((Result == 1 ? UNKNOWN_KEY : DIFFERENT_KEY), (Fingerprint)),
  848. NULL, qaYes | qaNo | qaCancel, 0, qtWarning);
  849. switch (R) {
  850. case qaYes:
  851. store_host_key(Host.c_str(), Port, KeyType.c_str(), KeyStr.c_str());
  852. break;
  853. case qaCancel:
  854. SSH_FATAL_ERROR(LoadStr(KEY_NOT_VERIFIED));
  855. }
  856. }
  857. }
  858. //---------------------------------------------------------------------------
  859. void __fastcall TSecureShell::AskCipher(const AnsiString CipherName, int CipherType)
  860. {
  861. static int CipherTypeRes[] = {CIPHER_TYPE_BOTH, CIPHER_TYPE_CS, CIPHER_TYPE_SC};
  862. assert(CipherType >= 0 && CipherType < LENOF(CipherTypeRes));
  863. AnsiString Msg = FMTLOAD(CIPHER_BELOW_TRESHOLD,
  864. (LoadStr(CipherTypeRes[CipherType]), CipherName));
  865. if (DoQueryUser(Msg, NULL, qaYes | qaNo, 0, qtWarning) == qaNo)
  866. {
  867. Abort();
  868. }
  869. }
  870. //---------------------------------------------------------------------------
  871. void __fastcall TSecureShell::OldKeyfileWarning()
  872. {
  873. DoQueryUser(LoadStr(OLD_KEY), NULL, qaOK, 0, qtWarning);
  874. }
  875. //---------------------------------------------------------------------------
  876. int __fastcall TSecureShell::GetStatus() const
  877. {
  878. return FStatus;
  879. }
  880. //---------------------------------------------------------------------------
  881. void __fastcall TSecureShell::UpdateStatus(int Value)
  882. {
  883. if (FStatus != Value)
  884. {
  885. FStatus = Value;
  886. if (FStatus > FReachedStatus) FReachedStatus = FStatus;
  887. if (FOnUpdateStatus) FOnUpdateStatus(this);
  888. }
  889. }
  890. //---------------------------------------------------------------------------
  891. void __fastcall TSecureShell::SetUserObject(TObject * value)
  892. {
  893. if (UserObject != value)
  894. {
  895. if (UserObject)
  896. {
  897. delete UserObject;
  898. }
  899. FUserObject = value;
  900. }
  901. }
  902. //=== TSessionLog -----------------------------------------------------------
  903. const char *LogLineMarks = "<>!.*";
  904. __fastcall TSessionLog::TSessionLog(TSecureShell * AOwner): TStringList()
  905. {
  906. FEnabled = true;
  907. FOwner = AOwner;
  908. FFile = NULL;
  909. FLoggedLines = 0;
  910. FTopIndex = -1;
  911. FFileName = "";
  912. }
  913. //---------------------------------------------------------------------------
  914. __fastcall TSessionLog::~TSessionLog()
  915. {
  916. CloseLogFile();
  917. }
  918. //---------------------------------------------------------------------------
  919. AnsiString __fastcall TSessionLog::GetSessionName()
  920. {
  921. assert(FOwner != NULL);
  922. assert(FOwner->SessionData != NULL);
  923. return FOwner->SessionData->SessionName;
  924. }
  925. //---------------------------------------------------------------------------
  926. void __fastcall TSessionLog::SetLine(Integer Index, AnsiString value)
  927. {
  928. Strings[Index] = AnsiString(Strings[Index - FTopIndex][1]) + value;
  929. }
  930. //---------------------------------------------------------------------------
  931. AnsiString __fastcall TSessionLog::GetLine(Integer Index)
  932. {
  933. return Strings[Index - FTopIndex].SubString(2, Strings[Index - FTopIndex].Length() - 1);
  934. }
  935. //---------------------------------------------------------------------------
  936. void __fastcall TSessionLog::SetType(Integer Index, TLogLineType value)
  937. {
  938. Strings[Index - FTopIndex][1] = LogLineMarks[value];
  939. }
  940. //---------------------------------------------------------------------------
  941. TLogLineType __fastcall TSessionLog::GetType(Integer Index)
  942. {
  943. int P = AnsiString(LogLineMarks).Pos(Strings[Index - FTopIndex][1]);
  944. if (P)
  945. {
  946. return TLogLineType(P - 1);
  947. }
  948. else
  949. {
  950. assert(false);
  951. return (TLogLineType)0;
  952. }
  953. }
  954. //---------------------------------------------------------------------------
  955. void __fastcall TSessionLog::DoAdd(bool Formatted, TLogLineType aType, AnsiString aLine)
  956. {
  957. assert(Configuration);
  958. if (IsLogging())
  959. {
  960. try
  961. {
  962. if (Configuration->Logging)
  963. {
  964. BeginUpdate();
  965. }
  966. try
  967. {
  968. while (!aLine.IsEmpty())
  969. {
  970. AnsiString NewStr;
  971. AnsiString FileStr;
  972. if (Formatted)
  973. {
  974. assert(aLine.Length() >= 2);
  975. FileStr = aLine;
  976. NewStr = aLine;
  977. NewStr.Delete(2, 1);
  978. aLine = "";
  979. }
  980. else
  981. {
  982. NewStr = AnsiString(LogLineMarks[aType]) + CutToChar(aLine, '\n', False);
  983. FileStr = NewStr;
  984. FileStr.Insert(' ', 2);
  985. }
  986. if (Configuration->Logging)
  987. {
  988. TStringList::Add(NewStr);
  989. FLoggedLines++;
  990. }
  991. DoAddLine(FileStr);
  992. if (Configuration->Logging && Configuration->LogToFile)
  993. {
  994. if (!FFile) OpenLogFile();
  995. if (FFile) fprintf((FILE *)FFile, "%s\n", FileStr.c_str());
  996. }
  997. }
  998. if (Configuration->Logging)
  999. {
  1000. if (FTopIndex < 0) FTopIndex = 0;
  1001. DeleteUnnecessary();
  1002. }
  1003. }
  1004. __finally
  1005. {
  1006. if (Configuration->Logging)
  1007. {
  1008. EndUpdate();
  1009. }
  1010. }
  1011. }
  1012. catch (Exception &E)
  1013. {
  1014. // We failed logging, turn it of and notify user.
  1015. Configuration->Logging = False;
  1016. try
  1017. {
  1018. throw ExtException(&E, LOG_ERROR);
  1019. }
  1020. catch (Exception &E)
  1021. {
  1022. FOwner->DoShowExtendedException(&E);
  1023. }
  1024. }
  1025. }
  1026. }
  1027. //---------------------------------------------------------------------------
  1028. void __fastcall TSessionLog::Add(TLogLineType aType, AnsiString aLine)
  1029. {
  1030. DoAdd(false, aType, aLine);
  1031. }
  1032. //---------------------------------------------------------------------------
  1033. void __fastcall TSessionLog::AddFromOtherLog(TObject * /*Sender*/,
  1034. const AnsiString AddedLine)
  1035. {
  1036. DoAdd(true, llMessage, AddedLine);
  1037. }
  1038. //---------------------------------------------------------------------------
  1039. void __fastcall TSessionLog::AddException(Exception * E)
  1040. {
  1041. if (E)
  1042. {
  1043. Add(llException, ExceptionLogString(E));
  1044. }
  1045. }
  1046. //---------------------------------------------------------------------------
  1047. void __fastcall TSessionLog::SetConfiguration(TConfiguration * value)
  1048. {
  1049. if (FConfiguration != value)
  1050. {
  1051. FConfiguration = value;
  1052. ReflectSettings();
  1053. }
  1054. }
  1055. //---------------------------------------------------------------------------
  1056. void __fastcall TSessionLog::ReflectSettings()
  1057. {
  1058. // if logging to file was turned off or log file was change -> close current log file
  1059. if (FFile && (!LogToFile || (FFileName != LogFileName))) CloseLogFile();
  1060. // if loggin to file is enabled and we don't log -> open log file
  1061. if (!FFile && LogToFile) OpenLogFile();
  1062. DeleteUnnecessary();
  1063. }
  1064. //---------------------------------------------------------------------------
  1065. void __fastcall TSessionLog::SetEnabled(bool value)
  1066. {
  1067. if (FEnabled != value)
  1068. {
  1069. FEnabled = value;
  1070. ReflectSettings();
  1071. }
  1072. }
  1073. //---------------------------------------------------------------------------
  1074. Boolean __fastcall TSessionLog::GetLogToFile()
  1075. {
  1076. return Enabled && (Configuration != NULL) && Configuration->Logging &&
  1077. Configuration->LogToFile;
  1078. }
  1079. //---------------------------------------------------------------------------
  1080. void __fastcall TSessionLog::CloseLogFile()
  1081. {
  1082. if (FFile)
  1083. {
  1084. fclose((FILE *)FFile);
  1085. FFile = NULL;
  1086. }
  1087. FFileName = "";
  1088. }
  1089. //---------------------------------------------------------------------------
  1090. void TSessionLog::OpenLogFile()
  1091. {
  1092. if (LogToFile)
  1093. {
  1094. try
  1095. {
  1096. assert(!FFile);
  1097. assert(Configuration);
  1098. FFile = fopen(LogFileName.c_str(), (Configuration->LogFileAppend ? "a" : "w"));
  1099. if (FFile)
  1100. {
  1101. setvbuf((FILE *)FFile, NULL, _IONBF, BUFSIZ);
  1102. FFileName = LogFileName;
  1103. }
  1104. else
  1105. {
  1106. throw Exception(FMTLOAD(LOG_OPENERROR, (LogFileName)));
  1107. }
  1108. }
  1109. catch (Exception &E)
  1110. {
  1111. // We failed logging to file, turn it of and notify user.
  1112. FFileName = "";
  1113. Configuration->LogToFile = false;
  1114. try
  1115. {
  1116. throw ExtException(&E, LOG_ERROR);
  1117. }
  1118. catch (Exception &E)
  1119. {
  1120. FOwner->DoShowExtendedException(&E);
  1121. }
  1122. }
  1123. }
  1124. }
  1125. //---------------------------------------------------------------------------
  1126. void TSessionLog::DeleteUnnecessary()
  1127. {
  1128. BeginUpdate();
  1129. try
  1130. {
  1131. if (!Configuration || !Configuration->Logging)
  1132. {
  1133. Clear();
  1134. }
  1135. else
  1136. {
  1137. while (!Configuration->LogWindowComplete && (Count > Configuration->LogWindowLines))
  1138. {
  1139. Delete(0);
  1140. FTopIndex++;
  1141. }
  1142. }
  1143. }
  1144. __finally
  1145. {
  1146. EndUpdate();
  1147. }
  1148. }
  1149. //---------------------------------------------------------------------------
  1150. TColor __fastcall TSessionLog::GetColor(int Index)
  1151. {
  1152. return LogLineColors[Type[Index]];
  1153. }
  1154. //---------------------------------------------------------------------------
  1155. void __fastcall TSessionLog::DoAddLine(const AnsiString AddedLine)
  1156. {
  1157. if (FOnAddLine)
  1158. {
  1159. FOnAddLine(this, AddedLine);
  1160. }
  1161. }
  1162. //---------------------------------------------------------------------------
  1163. void __fastcall TSessionLog::AddStartupInfo()
  1164. {
  1165. assert(FOwner != NULL);
  1166. TSessionData * Data = FOwner->SessionData;
  1167. assert(Data != NULL);
  1168. assert(Configuration);
  1169. if (Configuration->Logging || FOnAddLine)
  1170. {
  1171. BeginUpdate();
  1172. try
  1173. {
  1174. #define ADF(S, F) Add(llMessage, FORMAT(S, F));
  1175. AddSeparator();
  1176. ADF("WinSCP %s (OS %s)", (Configuration->VersionStr, Configuration->OSVersionStr));
  1177. ADF("Login time: %s", (FormatDateTime("dddddd tt", Now())));
  1178. AddSeparator();
  1179. ADF("Session name: %s", (Data->SessionName));
  1180. ADF("Host name: %s (Port: %d)", (Data->HostName, Data->PortNumber));
  1181. ADF("User name: %s (Password: %s, Key file: %s)",
  1182. (Data->UserName, BooleanToEngStr(!Data->Password.IsEmpty()),
  1183. BooleanToEngStr(!Data->PublicKeyFile.IsEmpty())))
  1184. ADF("Transfer Protocol: %s", (Data->FSProtocolStr));
  1185. ADF("SSH protocol version: %s; Compression: %s",
  1186. (Data->SshProtStr, BooleanToEngStr(Data->Compression)));
  1187. ADF("Agent forwarding: %s; TIS/CryptoCard: %s; KI: %s; GSSAPI: %s",
  1188. (BooleanToEngStr(Data->AgentFwd), BooleanToEngStr(Data->AuthTIS),
  1189. BooleanToEngStr(Data->AuthKI), BooleanToEngStr(Data->AuthGSSAPI)));
  1190. ADF("Ciphers: %s; Ssh2DES: %s",
  1191. (Data->CipherList, BooleanToEngStr(Data->Ssh2DES)));
  1192. char * PingTypes = "-NC";
  1193. ADF("Ping type: %s, Ping interval: %d sec; Timeout: %d sec",
  1194. (AnsiString(PingTypes[Data->PingType]), Data->PingInterval, Data->Timeout));
  1195. AnsiString Bugs;
  1196. char const * BugFlags = "A+-";
  1197. for (int Index = 0; Index < BUG_COUNT; Index++)
  1198. {
  1199. Bugs += AnsiString(BugFlags[Data->Bug[(TSshBug)Index]])+(Index<BUG_COUNT?",":"");
  1200. }
  1201. ADF("SSH Bugs: %s", (Bugs));
  1202. ADF("Proxy: %s", (ProxyMethodList[Data->ProxyMethod]));
  1203. if (Data->ProxyMethod != pmNone)
  1204. {
  1205. ADF("HostName: %s (Port: %d); Username: %s; Passwd: %s",
  1206. (Data->ProxyHost, Data->ProxyPort,
  1207. Data->ProxyUsername, BooleanToEngStr(!Data->ProxyPassword.IsEmpty())));
  1208. if (Data->ProxyMethod == pmTelnet)
  1209. {
  1210. ADF("Telnet command: %s", (Data->ProxyTelnetCommand));
  1211. }
  1212. }
  1213. ADF("Return code variable: %s; Lookup user groups: %s",
  1214. ((Data->DetectReturnVar ? AnsiString("Autodetect") : Data->ReturnVar),
  1215. BooleanToEngStr(Data->LookupUserGroups)));
  1216. ADF("Shell: %s, EOL: %d", ((Data->Shell.IsEmpty()? AnsiString("default") : Data->Shell), Data->EOLType));
  1217. ADF("Local directory: %s, Remote directory: %s, Update: %s, Cache: %s",
  1218. ((Data->LocalDirectory.IsEmpty() ? AnsiString("default") : Data->LocalDirectory),
  1219. (Data->RemoteDirectory.IsEmpty() ? AnsiString("home") : Data->RemoteDirectory),
  1220. BooleanToEngStr(Data->UpdateDirectories),
  1221. BooleanToEngStr(Data->CacheDirectories)));
  1222. ADF("Cache directory changes: %s, Permanent: %s",
  1223. (BooleanToEngStr(Data->CacheDirectoryChanges),
  1224. BooleanToEngStr(Data->PreserveDirectoryChanges)));
  1225. ADF("Clear aliases: %s, Unset nat.vars: %s, Resolve symlinks: %s",
  1226. (BooleanToEngStr(Data->ClearAliases), BooleanToEngStr(Data->UnsetNationalVars),
  1227. BooleanToEngStr(Data->ResolveSymlinks)));
  1228. ADF("Alias LS: %s, Ign LS warn: %s, Scp1 Comp: %s",
  1229. (BooleanToEngStr(Data->AliasGroupList),
  1230. BooleanToEngStr(Data->IgnoreLsWarnings),
  1231. BooleanToEngStr(Data->Scp1Compatibility)));
  1232. AddSeparator();
  1233. #undef ADF
  1234. }
  1235. __finally
  1236. {
  1237. EndUpdate();
  1238. }
  1239. }
  1240. }
  1241. //---------------------------------------------------------------------------
  1242. void __fastcall TSessionLog::AddSeparator()
  1243. {
  1244. Add(llMessage, "--------------------------------------------------------------------------");
  1245. }
  1246. //---------------------------------------------------------------------------
  1247. int __fastcall TSessionLog::GetIndexes(int Index)
  1248. {
  1249. assert((Index >= 0) && (Index < Count));
  1250. int Result = TopIndex + Index;
  1251. assert((Result >= 0) && (Result < LoggedLines));
  1252. return Result;
  1253. }
  1254. //---------------------------------------------------------------------------
  1255. int __fastcall TSessionLog::GetBottomIndex()
  1256. {
  1257. return (Count ? Indexes[Count-1] : -1);
  1258. }
  1259. //---------------------------------------------------------------------------
  1260. AnsiString __fastcall TSessionLog::GetLogFileName()
  1261. {
  1262. assert(Configuration);
  1263. AnsiString Result = StripPathQuotes(Configuration->LogFileName);
  1264. return Result;
  1265. }
  1266. //---------------------------------------------------------------------------
  1267. Boolean __fastcall TSessionLog::GetLoggingToFile()
  1268. {
  1269. return
  1270. (Configuration && Configuration->Logging && Configuration->LogToFile && FFile);
  1271. }
  1272. //---------------------------------------------------------------------------
  1273. void __fastcall TSessionLog::Clear()
  1274. {
  1275. FTopIndex += Count;
  1276. TStringList::Clear();
  1277. }