SecureShell.cpp 38 KB

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