1
0
mirror of https://github.com/winscp/winscp.git synced 2026-10-24 02:28:55 +03:00
Files
winscp/source/core/SessionInfo.cpp

1886 lines
57 KiB
C++

//---------------------------------------------------------------------------
#include <CorePCH.h>
#pragma hdrstop
#include <lmcons.h>
#define SECURITY_WIN32
#include <sspi.h>
#include <secext.h>
#include "SessionInfo.h"
#include "Script.h"
//---------------------------------------------------------------------------
UnicodeString __fastcall DoXmlEscape(UnicodeString Str, bool NewLine)
{
for (int i = 1; i <= Str.Length(); i++)
{
UnicodeString Repl;
wchar_t Ch = Str[i];
switch (Ch)
{
case L'\x00': // \0 Is not valid in XML anyway
case L'\x01':
case L'\x02':
case L'\x03':
case L'\x04':
case L'\x05':
case L'\x06':
case L'\x07':
case L'\x08':
// \n is handled below
case L'\x0B':
case L'\x0C':
// \r is handled below
case L'\x0E':
case L'\x0F':
case L'\x10':
case L'\x11':
case L'\x12':
case L'\x13':
case L'\x14':
case L'\x15':
case L'\x16':
case L'\x17':
case L'\x18':
case L'\x19':
case L'\x1A':
case L'\x1B':
case L'\x1C':
case L'\x1D':
case L'\x1E':
case L'\x1F':
Repl = L"#x" + ByteToHex(static_cast<unsigned char>(Ch)) + L";";
break;
case L'\xFFFE':
case L'\xFFFF':
Repl = L"#x" + CharToHex(Ch) + L";";
break;
case L'&':
Repl = L"amp;";
break;
case L'>':
Repl = L"gt;";
break;
case L'<':
Repl = L"lt;";
break;
case L'"':
Repl = L"quot;";
break;
case L'\n':
if (NewLine)
{
Repl = L"#10;";
}
break;
case L'\r':
Str.Delete(i, 1);
i--;
break;
}
if (!Repl.IsEmpty())
{
Str[i] = L'&';
Str.Insert(Repl, i + 1);
i += Repl.Length();
}
}
return Str;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall XmlEscape(UnicodeString Str)
{
return DoXmlEscape(Str, false);
}
//---------------------------------------------------------------------------
UnicodeString __fastcall XmlAttributeEscape(UnicodeString Str)
{
return DoXmlEscape(Str, true);
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#pragma warn -inl
class TSessionActionRecord
{
public:
__fastcall TSessionActionRecord(TActionLog * Log, TLogAction Action) :
FLog(Log),
FAction(Action),
FState(Opened),
FRecursive(false),
FErrorMessages(NULL),
FNames(new TStringList()),
FValues(new TStringList()),
FFileList(NULL),
FFile(NULL)
{
FLog->AddPendingAction(this);
}
__fastcall ~TSessionActionRecord()
{
delete FErrorMessages;
delete FNames;
delete FValues;
delete FFileList;
delete FFile;
}
void __fastcall Restart()
{
FState = Opened;
FRecursive = false;
delete FErrorMessages;
FErrorMessages = NULL;
delete FFileList;
FFileList = NULL;
delete FFile;
FFile = NULL;
FNames->Clear();
FValues->Clear();
}
bool __fastcall Record()
{
bool Result = (FState != Opened);
if (Result)
{
if (FLog->FLogging && (FState != Cancelled))
{
const wchar_t * Name = ActionName();
UnicodeString Attrs;
if (FRecursive)
{
Attrs = L" recursive=\"true\"";
}
FLog->AddIndented(FORMAT(L"<%s%s>", (Name, Attrs)));
for (int Index = 0; Index < FNames->Count; Index++)
{
UnicodeString Value = FValues->Strings[Index];
if (Value.IsEmpty())
{
FLog->AddIndented(FORMAT(L" <%s />", (FNames->Strings[Index])));
}
else
{
FLog->AddIndented(FORMAT(L" <%s value=\"%s\" />",
(FNames->Strings[Index], XmlAttributeEscape(Value))));
}
}
if (FFileList != NULL)
{
FLog->AddIndented(L" <files>");
for (int Index = 0; Index < FFileList->Count; Index++)
{
TRemoteFile * File = FFileList->Files[Index];
RecordFile(L" ", File, true);
}
FLog->AddIndented(L" </files>");
}
if (FFile != NULL)
{
RecordFile(L" ", FFile, false);
}
if (FState == RolledBack)
{
if (FErrorMessages != NULL)
{
FLog->AddIndented(L" <result success=\"false\">");
FLog->AddMessages(L" ", FErrorMessages);
FLog->AddIndented(L" </result>");
}
else
{
FLog->AddIndented(L" <result success=\"false\" />");
}
}
else
{
FLog->AddIndented(L" <result success=\"true\" />");
}
FLog->AddIndented(FORMAT(L"</%s>", (Name)));
}
delete this;
}
return Result;
}
void __fastcall Commit()
{
Close(Committed);
}
void __fastcall Rollback(Exception * E)
{
DebugAssert(FErrorMessages == NULL);
FErrorMessages = ExceptionToMoreMessages(E);
Close(RolledBack);
}
void __fastcall Cancel()
{
Close(Cancelled);
}
void __fastcall FileName(const UnicodeString & FileName)
{
Parameter(L"filename", FileName);
}
void __fastcall Destination(const UnicodeString & Destination)
{
Parameter(L"destination", Destination);
}
void Size(__int64 Size)
{
Parameter(L"size", Size);
}
void __fastcall Rights(const TRights & Rights)
{
Parameter(L"permissions", Rights.Text);
}
void __fastcall Modification(const TDateTime & DateTime)
{
Parameter(L"modification", StandardTimestamp(DateTime));
}
void __fastcall Recursive()
{
FRecursive = true;
}
void __fastcall Command(const UnicodeString & Command)
{
Parameter(L"command", Command);
}
void __fastcall AddOutput(UnicodeString Output, bool StdError)
{
const wchar_t * Name = (StdError ? L"erroroutput" : L"output");
int Index = FNames->IndexOf(Name);
if (Index >= 0)
{
FValues->Strings[Index] = FValues->Strings[Index] + L"\r\n" + Output;
}
else
{
Parameter(Name, Output);
}
}
void __fastcall ExitCode(int ExitCode)
{
Parameter(L"exitcode", IntToStr(ExitCode));
}
void __fastcall Checksum(const UnicodeString & Alg, const UnicodeString & Checksum)
{
Parameter(L"algorithm", Alg);
Parameter(L"checksum", Checksum);
}
void __fastcall Cwd(const UnicodeString & Path)
{
Parameter(L"cwd", Path);
}
void __fastcall FileList(TRemoteFileList * FileList)
{
if (FFileList == NULL)
{
FFileList = new TRemoteFileList();
}
FileList->DuplicateTo(FFileList);
}
void __fastcall File(TRemoteFile * File)
{
if (FFile != NULL)
{
delete FFile;
}
FFile = File->Duplicate(true);
}
void __fastcall SynchronizeChecklistItem(const TSynchronizeChecklist::TItem * Item)
{
UnicodeString Action;
bool RecordLocal = false;
bool RecordRemote = false;
switch (Item->Action)
{
case TSynchronizeChecklist::saUploadNew:
Action = L"uploadnew";
RecordLocal = true;
break;
case TSynchronizeChecklist::saDownloadNew:
Action = L"downloadnew";
RecordRemote = true;
break;
case TSynchronizeChecklist::saUploadUpdate:
Action = L"uploadupdate";
RecordLocal = true;
RecordRemote = true;
break;
case TSynchronizeChecklist::saDownloadUpdate:
Action = L"downloadupdate";
RecordLocal = true;
RecordRemote = true;
break;
case TSynchronizeChecklist::saDeleteRemote:
Action = L"deleteremote";
RecordRemote = true;
break;
case TSynchronizeChecklist::saDeleteLocal:
Action = L"deletelocal";
RecordLocal = true;
break;
default:
DebugFail();
break;
}
Parameter(L"action", Action);
if (RecordLocal)
{
SynchronizeChecklistItemFileInfo(Item->GetLocalPath(), Item->IsDirectory, Item->Info1);
}
if (RecordRemote)
{
SynchronizeChecklistItemFileInfo(Item->GetRemotePath(), Item->IsDirectory, Item->Info2);
}
}
protected:
enum TState { Opened, Committed, RolledBack, Cancelled };
inline void __fastcall Close(TState State)
{
DebugAssert(FState == Opened);
FState = State;
FLog->RecordPendingActions();
}
const wchar_t * __fastcall ActionName()
{
switch (FAction)
{
case laUpload: return L"upload";
case laDownload: return L"download";
case laTouch: return L"touch";
case laChmod: return L"chmod";
case laMkdir: return L"mkdir";
case laRm: return L"rm";
case laMv: return L"mv";
case laCp: return L"cp";
case laCall: return L"call";
case laLs: return L"ls";
case laStat: return L"stat";
case laChecksum: return L"checksum";
case laCwd: return L"cwd";
case laDifference: return L"difference";
default: DebugFail(); return L"";
}
}
void __fastcall Parameter(const UnicodeString & Name, const UnicodeString & Value = L"")
{
FNames->Add(Name);
FValues->Add(Value);
}
void __fastcall RecordFile(const UnicodeString & Indent, TRemoteFile * File, bool IncludeFileName)
{
FLog->AddIndented(Indent + L"<file>");
if (IncludeFileName)
{
FLog->AddIndented(Indent + FORMAT(L" <filename value=\"%s\" />", (XmlAttributeEscape(File->FileName))));
}
FLog->AddIndented(Indent + FORMAT(L" <type value=\"%s\" />", (XmlAttributeEscape(towupper(File->Type)))));
if (!File->IsDirectory)
{
FLog->AddIndented(Indent + FORMAT(L" <size value=\"%s\" />", (IntToStr(File->Size))));
}
if (File->ModificationFmt != mfNone)
{
FLog->AddIndented(Indent + FORMAT(L" <modification value=\"%s\" />", (StandardTimestamp(File->Modification))));
}
if (!File->Rights->Unknown)
{
FLog->AddIndented(Indent + FORMAT(L" <permissions value=\"%s\" />", (XmlAttributeEscape(File->Rights->Text))));
}
if (File->Owner.IsSet)
{
FLog->AddIndented(Indent + FORMAT(L" <owner value=\"%s\" />", (XmlAttributeEscape(File->Owner.DisplayText))));
}
if (File->Group.IsSet)
{
FLog->AddIndented(Indent + FORMAT(L" <group value=\"%s\" />", (XmlAttributeEscape(File->Group.DisplayText))));
}
FLog->AddIndented(Indent + L"</file>");
}
void __fastcall SynchronizeChecklistItemFileInfo(
const UnicodeString & AFileName, bool IsDirectory, const TSynchronizeChecklist::TItem::TFileInfo FileInfo)
{
Parameter(L"type", (IsDirectory ? L'D' : L'-'));
FileName(AFileName);
if (!IsDirectory)
{
Parameter(L"size", IntToStr(FileInfo.Size));
}
if (FileInfo.ModificationFmt != mfNone)
{
Modification(FileInfo.Modification);
}
}
private:
TActionLog * FLog;
TLogAction FAction;
TState FState;
bool FRecursive;
TStrings * FErrorMessages;
TStrings * FNames;
TStrings * FValues;
TRemoteFileList * FFileList;
TRemoteFile * FFile;
};
#pragma warn .inl
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
__fastcall TSessionAction::TSessionAction(TActionLog * Log, TLogAction Action)
{
FCancelled = false;
if (Log->FLogging)
{
FRecord = new TSessionActionRecord(Log, Action);
}
else
{
FRecord = NULL;
}
}
//---------------------------------------------------------------------------
__fastcall TSessionAction::~TSessionAction()
{
if (FRecord != NULL)
{
TSessionActionRecord * Record = FRecord;
FRecord = NULL;
Record->Commit();
}
}
//---------------------------------------------------------------------------
void __fastcall TSessionAction::Restart()
{
if (FRecord != NULL)
{
FRecord->Restart();
}
}
//---------------------------------------------------------------------------
void __fastcall TSessionAction::Rollback(Exception * E)
{
if (FRecord != NULL)
{
TSessionActionRecord * Record = FRecord;
FRecord = NULL;
Record->Rollback(E);
}
}
//---------------------------------------------------------------------------
void __fastcall TSessionAction::Cancel()
{
if (FRecord != NULL)
{
TSessionActionRecord * Record = FRecord;
FRecord = NULL;
FCancelled = true;
Record->Cancel();
}
}
//---------------------------------------------------------------------------
bool __fastcall TSessionAction::IsValid()
{
return !FCancelled;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
__fastcall TFileSessionAction::TFileSessionAction(TActionLog * Log, TLogAction Action) :
TSessionAction(Log, Action)
{
}
//---------------------------------------------------------------------------
__fastcall TFileSessionAction::TFileSessionAction(
TActionLog * Log, TLogAction Action, const UnicodeString & AFileName) :
TSessionAction(Log, Action)
{
FileName(AFileName);
}
//---------------------------------------------------------------------------
void __fastcall TFileSessionAction::FileName(const UnicodeString & FileName)
{
if (FRecord != NULL)
{
FRecord->FileName(FileName);
}
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
__fastcall TFileLocationSessionAction::TFileLocationSessionAction(
TActionLog * Log, TLogAction Action) :
TFileSessionAction(Log, Action)
{
}
//---------------------------------------------------------------------------
__fastcall TFileLocationSessionAction::TFileLocationSessionAction(
TActionLog * Log, TLogAction Action, const UnicodeString & FileName) :
TFileSessionAction(Log, Action, FileName)
{
}
//---------------------------------------------------------------------------
void __fastcall TFileLocationSessionAction::Destination(const UnicodeString & Destination)
{
if (FRecord != NULL)
{
FRecord->Destination(Destination);
}
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
TTransferSessionAction::TTransferSessionAction(TActionLog * Log, TLogAction Action) :
TFileLocationSessionAction(Log, Action)
{
}
//---------------------------------------------------------------------------
void TTransferSessionAction::Size(__int64 Size)
{
if (FRecord != NULL)
{
FRecord->Size(Size);
}
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
__fastcall TUploadSessionAction::TUploadSessionAction(TActionLog * Log) :
TTransferSessionAction(Log, laUpload)
{
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
__fastcall TDownloadSessionAction::TDownloadSessionAction(TActionLog * Log) :
TTransferSessionAction(Log, laDownload)
{
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
__fastcall TChmodSessionAction::TChmodSessionAction(
TActionLog * Log, const UnicodeString & FileName) :
TFileSessionAction(Log, laChmod, FileName)
{
}
//---------------------------------------------------------------------------
void __fastcall TChmodSessionAction::Recursive()
{
if (FRecord != NULL)
{
FRecord->Recursive();
}
}
//---------------------------------------------------------------------------
__fastcall TChmodSessionAction::TChmodSessionAction(
TActionLog * Log, const UnicodeString & FileName, const TRights & ARights) :
TFileSessionAction(Log, laChmod, FileName)
{
Rights(ARights);
}
//---------------------------------------------------------------------------
void __fastcall TChmodSessionAction::Rights(const TRights & Rights)
{
if (FRecord != NULL)
{
FRecord->Rights(Rights);
}
}
//---------------------------------------------------------------------------
__fastcall TTouchSessionAction::TTouchSessionAction(
TActionLog * Log, const UnicodeString & FileName, const TDateTime & Modification) :
TFileSessionAction(Log, laTouch, FileName)
{
if (FRecord != NULL)
{
FRecord->Modification(Modification);
}
}
//---------------------------------------------------------------------------
__fastcall TMkdirSessionAction::TMkdirSessionAction(
TActionLog * Log, const UnicodeString & FileName) :
TFileSessionAction(Log, laMkdir, FileName)
{
}
//---------------------------------------------------------------------------
__fastcall TRmSessionAction::TRmSessionAction(
TActionLog * Log, const UnicodeString & FileName) :
TFileSessionAction(Log, laRm, FileName)
{
}
//---------------------------------------------------------------------------
void __fastcall TRmSessionAction::Recursive()
{
if (FRecord != NULL)
{
FRecord->Recursive();
}
}
//---------------------------------------------------------------------------
__fastcall TMvSessionAction::TMvSessionAction(TActionLog * Log,
const UnicodeString & FileName, const UnicodeString & ADestination) :
TFileLocationSessionAction(Log, laMv, FileName)
{
Destination(ADestination);
}
//---------------------------------------------------------------------------
__fastcall TCpSessionAction::TCpSessionAction(TActionLog * Log,
const UnicodeString & FileName, const UnicodeString & ADestination) :
TFileLocationSessionAction(Log, laCp, FileName)
{
Destination(ADestination);
}
//---------------------------------------------------------------------------
__fastcall TCallSessionAction::TCallSessionAction(TActionLog * Log,
const UnicodeString & Command, const UnicodeString & Destination) :
TSessionAction(Log, laCall)
{
if (FRecord != NULL)
{
FRecord->Command(Command);
FRecord->Destination(Destination);
}
}
//---------------------------------------------------------------------------
void __fastcall TCallSessionAction::AddOutput(const UnicodeString & Output, bool StdError)
{
if (FRecord != NULL)
{
FRecord->AddOutput(Output, StdError);
}
}
//---------------------------------------------------------------------------
void __fastcall TCallSessionAction::ExitCode(int ExitCode)
{
if (FRecord != NULL)
{
FRecord->ExitCode(ExitCode);
}
}
//---------------------------------------------------------------------------
__fastcall TLsSessionAction::TLsSessionAction(TActionLog * Log,
const UnicodeString & Destination) :
TSessionAction(Log, laLs)
{
if (FRecord != NULL)
{
FRecord->Destination(Destination);
}
}
//---------------------------------------------------------------------------
void __fastcall TLsSessionAction::FileList(TRemoteFileList * FileList)
{
if (FRecord != NULL)
{
FRecord->FileList(FileList);
}
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
__fastcall TStatSessionAction::TStatSessionAction(TActionLog * Log, const UnicodeString & FileName) :
TFileSessionAction(Log, laStat, FileName)
{
}
//---------------------------------------------------------------------------
void __fastcall TStatSessionAction::File(TRemoteFile * File)
{
if (FRecord != NULL)
{
FRecord->File(File);
}
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
__fastcall TChecksumSessionAction::TChecksumSessionAction(TActionLog * Log) :
TFileSessionAction(Log, laChecksum)
{
}
//---------------------------------------------------------------------------
void __fastcall TChecksumSessionAction::Checksum(const UnicodeString & Alg, const UnicodeString & Checksum)
{
if (FRecord != NULL)
{
FRecord->Checksum(Alg, Checksum);
}
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
__fastcall TCwdSessionAction::TCwdSessionAction(TActionLog * Log, const UnicodeString & Path) :
TSessionAction(Log, laCwd)
{
if (FRecord != NULL)
{
FRecord->Cwd(Path);
}
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
__fastcall TDifferenceSessionAction::TDifferenceSessionAction(TActionLog * Log, const TSynchronizeChecklist::TItem * Item) :
TSessionAction(Log, laDifference)
{
if (FRecord != NULL)
{
FRecord->SynchronizeChecklistItem(Item);
}
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
TSessionInfo::TSessionInfo()
{
LoginTime = Now();
CertificateVerifiedManually = false;
}
//---------------------------------------------------------------------------
TFileSystemInfo::TFileSystemInfo()
{
memset(&IsCapable, false, sizeof(IsCapable));
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
FILE * __fastcall OpenFile(UnicodeString LogFileName, TDateTime Started, TSessionData * SessionData, bool Append, UnicodeString & NewFileName)
{
FILE * Result;
UnicodeString ANewFileName = GetExpandedLogFileName(LogFileName, Started, SessionData);
Result = _wfopen(ApiPath(ANewFileName).c_str(), (Append ? L"ab" : L"wb"));
if (Result != NULL)
{
setvbuf(Result, NULL, _IONBF, BUFSIZ);
NewFileName = ANewFileName;
}
else
{
throw ECRTExtException(FMTLOAD(LOG_OPENERROR, (ANewFileName)));
}
return Result;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
static const wchar_t *LogLineMarks = L"<>!.*";
__fastcall TSessionLog::TSessionLog(TSessionUI* UI, TDateTime Started, TSessionData * SessionData,
TConfiguration * Configuration)
{
FCriticalSection = new TCriticalSection;
FLogging = false;
FConfiguration = Configuration;
FParent = NULL;
FUI = UI;
FSessionData = SessionData;
FStarted = Started;
FFile = NULL;
FCurrentLogFileName = L"";
FCurrentFileName = L"";
FClosed = false;
}
//---------------------------------------------------------------------------
__fastcall TSessionLog::~TSessionLog()
{
FClosed = true;
ReflectSettings();
DebugAssert(FFile == NULL);
delete FCriticalSection;
}
//---------------------------------------------------------------------------
void __fastcall TSessionLog::SetParent(TSessionLog * Parent, const UnicodeString & Name)
{
FParent = Parent;
FName = Name;
}
//---------------------------------------------------------------------------
void __fastcall TSessionLog::DoAddToParent(TLogLineType Type, const UnicodeString & Line)
{
DebugAssert(FParent != NULL);
FParent->Add(Type, Line);
}
//---------------------------------------------------------------------------
void __fastcall TSessionLog::DoAddToSelf(TLogLineType Type, const UnicodeString & Line)
{
if (LogToFile())
{
if (FFile == NULL)
{
OpenLogFile();
}
if (FFile != NULL)
{
UnicodeString Timestamp = FormatDateTime(L" yyyy-mm-dd hh:nn:ss.zzz ", Now());
UTF8String UtfLine = UTF8String(UnicodeString(LogLineMarks[Type]) + Timestamp + Line + L"\r\n");
for (int Index = 1; Index <= UtfLine.Length(); Index++)
{
if ((UtfLine[Index] == '\n') &&
((Index == 1) || (UtfLine[Index - 1] != '\r')))
{
UtfLine.Insert('\r', Index);
}
}
int Writing = UtfLine.Length();
CheckSize(Writing);
FCurrentFileSize += fwrite(UtfLine.c_str(), 1, Writing, static_cast<FILE *>(FFile));
}
}
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TSessionLog::LogPartFileName(const UnicodeString & BaseName, int Index)
{
UnicodeString Result;
if (Index >= 1)
{
Result = FORMAT(L"%s.%d", (BaseName, Index));
}
else
{
Result = BaseName;
}
return Result;
}
//---------------------------------------------------------------------------
void __fastcall TSessionLog::CheckSize(__int64 Addition)
{
__int64 MaxSize = FConfiguration->LogMaxSize;
if ((MaxSize > 0) && (FCurrentFileSize + Addition >= MaxSize))
{
// Before we close it
UnicodeString BaseName = FCurrentFileName;
CloseLogFile();
FCurrentFileSize = 0;
int Index = 0;
while (FileExists(LogPartFileName(BaseName, Index + 1)))
{
Index++;
}
int MaxCount = FConfiguration->LogMaxCount;
do
{
UnicodeString LogPart = LogPartFileName(BaseName, Index);
if ((MaxCount > 0) && (Index >= MaxCount))
{
DeleteFileChecked(LogPart);
}
else
{
THROWOSIFFALSE(RenameFile(LogPart, LogPartFileName(BaseName, Index + 1)));
}
Index--;
}
while (Index >= 0);
OpenLogFile();
}
}
//---------------------------------------------------------------------------
void __fastcall TSessionLog::DoAdd(TLogLineType Type, UnicodeString Line,
void __fastcall (__closure *f)(TLogLineType Type, const UnicodeString & Line))
{
UnicodeString Prefix;
if (!FName.IsEmpty())
{
Prefix = L"[" + FName + L"] ";
}
while (!Line.IsEmpty())
{
f(Type, Prefix + CutToChar(Line, L'\n', false));
}
}
//---------------------------------------------------------------------------
void __fastcall TSessionLog::Add(TLogLineType Type, const UnicodeString & Line)
{
DebugAssert(FConfiguration);
if (Logging)
{
try
{
if (FParent != NULL)
{
DoAdd(Type, Line, DoAddToParent);
}
else
{
TGuard Guard(FCriticalSection);
DoAdd(Type, Line, DoAddToSelf);
}
}
catch (Exception &E)
{
// We failed logging, turn it off and notify user.
FConfiguration->Logging = false;
try
{
throw ExtException(&E, MainInstructions(LoadStr(LOG_GEN_ERROR)));
}
catch (Exception &E)
{
AddException(&E);
FUI->HandleExtendedException(&E);
}
}
}
}
//---------------------------------------------------------------------------
void __fastcall TSessionLog::AddException(Exception * E)
{
if (E != NULL)
{
Add(llException, ExceptionLogString(E));
}
}
//---------------------------------------------------------------------------
void __fastcall TSessionLog::ReflectSettings()
{
TGuard Guard(FCriticalSection);
FLogging =
!FClosed &&
((FParent != NULL) || FConfiguration->Logging);
// if logging to file was turned off or log file was changed -> close current log file
if ((FFile != NULL) &&
(!LogToFile() || (FCurrentLogFileName != FConfiguration->LogFileName)))
{
CloseLogFile();
}
if (FFile != NULL)
{
CheckSize(0);
}
}
//---------------------------------------------------------------------------
bool __fastcall TSessionLog::LogToFile()
{
return Logging && FConfiguration->LogToFile && (FParent == NULL);
}
//---------------------------------------------------------------------------
void __fastcall TSessionLog::CloseLogFile()
{
if (FFile != NULL)
{
fclose(static_cast<FILE *>(FFile));
FFile = NULL;
}
FCurrentLogFileName = L"";
FCurrentFileName = L"";
}
//---------------------------------------------------------------------------
void __fastcall TSessionLog::OpenLogFile()
{
try
{
DebugAssert(FFile == NULL);
DebugAssert(FConfiguration != NULL);
FCurrentLogFileName = FConfiguration->LogFileName;
FFile = OpenFile(FCurrentLogFileName, FStarted, FSessionData, FConfiguration->LogFileAppend, FCurrentFileName);
TSearchRec SearchRec;
if (FileSearchRec(FCurrentFileName, SearchRec))
{
FCurrentFileSize = SearchRec.Size;
}
else
{
FCurrentFileSize = 0;
}
}
catch (Exception & E)
{
// We failed logging to file, turn it off and notify user.
FCurrentLogFileName = L"";
FCurrentFileName = L"";
FConfiguration->LogFileName = UnicodeString();
try
{
throw ExtException(&E, MainInstructions(LoadStr(LOG_GEN_ERROR)));
}
catch (Exception & E)
{
AddException(&E);
// not to deadlock with TSessionLog::ReflectSettings invoked by FConfiguration->LogFileName setter above
TUnguard Unguard(FCriticalSection);
FUI->HandleExtendedException(&E);
}
}
// in case we are appending and the existing log file is already too large
if (FFile != NULL)
{
CheckSize(0);
}
}
//---------------------------------------------------------------------------
void __fastcall TSessionLog::AddSystemInfo()
{
AddStartupInfo(true);
}
//---------------------------------------------------------------------------
void __fastcall TSessionLog::AddStartupInfo()
{
AddStartupInfo(false);
}
//---------------------------------------------------------------------------
void __fastcall TSessionLog::AddStartupInfo(bool System)
{
TSessionData * Data = (System ? NULL : FSessionData);
if (Logging)
{
if (FParent != NULL)
{
// do not add session info for secondary session
// (this should better be handled in the TSecondaryTerminal)
}
else
{
DoAddStartupInfo(Data);
}
}
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TSessionLog::LogSensitive(const UnicodeString & Str)
{
if (FConfiguration->LogSensitive && !Str.IsEmpty())
{
return NormalizeString(Str);
}
else
{
return BooleanToEngStr(!Str.IsEmpty());
}
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TSessionLog::GetCmdLineLog(TConfiguration * AConfiguration)
{
UnicodeString Result = CmdLine;
if (!AConfiguration->LogSensitive)
{
TManagementScript Script(StoredSessions, false);
Script.MaskPasswordInCommandLine(Result, true);
}
return Result;
}
//---------------------------------------------------------------------------
template <typename T>
UnicodeString __fastcall EnumName(T Value, UnicodeString Names)
{
int ValueI = int(Value);
int N = ValueI;
do
{
UnicodeString Name = CutToChar(Names, L';', true);
if (N == 0)
{
return Name;
}
N--;
}
while ((N >= 0) && !Names.IsEmpty());
return FORMAT(L"(unknown %d)", (ValueI));
}
#define ADSTR(S) AddLogEntry(S)
#define ADF(S, F) ADSTR(FORMAT(S, F));
//---------------------------------------------------------------------------
void __fastcall TSessionLog::DoAddStartupInfo(TAddLogEntryEvent AddLogEntry, TConfiguration * AConfiguration, bool DoNotMaskPaswords)
{
ADSTR(GetEnvironmentInfo());
THierarchicalStorage * Storage = AConfiguration->CreateConfigStorage();
try
{
ADF(L"Configuration: %s", (Storage->Source));
}
__finally
{
delete Storage;
}
wchar_t UserName[UNLEN + 1];
unsigned long UserNameSize = std::size(UserName);
if (DebugAlwaysFalse(!GetUserNameEx(NameSamCompatible, UserName, &UserNameSize)))
{
wcscpy(UserName, L"<Failed to retrieve username>");
}
UnicodeString LogStr;
if (AConfiguration->LogProtocol <= -1)
{
LogStr = L"Reduced";
}
else if (AConfiguration->LogProtocol <= 0)
{
LogStr = L"Normal";
}
else if (AConfiguration->LogProtocol == 1)
{
LogStr = L"Debug 1";
}
else if (AConfiguration->LogProtocol >= 2)
{
LogStr = L"Debug 2";
}
if (AConfiguration->LogSensitive)
{
LogStr += L", Logging passwords";
}
if (AConfiguration->LogMaxSize > 0)
{
LogStr += FORMAT(L", Rotating after: %s", (SizeToStr(AConfiguration->LogMaxSize)));
if (AConfiguration->LogMaxCount > 0)
{
LogStr += FORMAT(L", Keeping at most %d logs", (AConfiguration->LogMaxCount));
}
}
ADF(L"Log level: %s", (LogStr));
ADF(L"Local account: %s", (UserName));
ADF(L"Working directory: %s", (GetCurrentDir()));
ADF(L"Process ID: %d", (int(GetCurrentProcessId())));
ADF(L"Ancestor processes: %s", (GetAncestorProcessNames()));
// This logs even passwords, contrary to a session log.
// GetCmdLineLog requires master password, but we do not know it yet atm.
UnicodeString ACmdLine;
if (DoNotMaskPaswords)
{
ACmdLine = CmdLine;
}
else
{
ACmdLine = GetCmdLineLog(AConfiguration);
}
ADF(L"Command-line: %s", (ACmdLine));
if (AConfiguration->ActualLogProtocol >= 1)
{
GetGlobalOptions()->LogOptions(AddLogEntry);
}
ADF(L"Time zone: %s", (GetTimeZoneLogString()));
if (!AdjustClockForDSTEnabled())
{
ADSTR(L"Warning: System option \"Automatically adjust clock for Daylight Saving Time\" is disabled, timestamps will not be represented correctly");
}
}
//---------------------------------------------------------------------------
#undef ADSTR
#define ADSTR(S) DoAdd(llMessage, S, DoAddToSelf);
//---------------------------------------------------------------------------
void __fastcall TSessionLog::DoAddStartupInfoEntry(const UnicodeString & S)
{
ADSTR(S);
}
//---------------------------------------------------------------------------
void __fastcall TSessionLog::DoAddStartupInfo(TSessionData * Data)
{
if (Data == NULL)
{
AddSeparator();
DoAddStartupInfo(DoAddStartupInfoEntry, FConfiguration, false);
ADF(L"Login time: %s", (FormatDateTime(L"dddddd tt", Now())));
AddSeparator();
}
else
{
ADF(L"Session name: %s (%s)", (Data->SessionName, Data->SourceName));
UnicodeString AddressFamily;
if (Data->AddressFamily != afAuto)
{
AddressFamily = FORMAT(L"%s, ", (Data->AddressFamily == afIPv4 ? L"IPv4" : L"IPv6"));
}
UnicodeString HostName = Data->HostNameExpanded;
if (!Data->HostNameSource.IsEmpty())
{
HostName = FORMAT(L"%s [%s]", (HostName, Data->HostNameSource));
}
ADF(L"Hostname: %s (%sPort: %d)", (HostName, AddressFamily, Data->PortNumber));
UnicodeString UserName = Data->UserNameExpanded;
if (!Data->UserNameSource.IsEmpty())
{
UserName = FORMAT(L"%s [%s]", (UserName, Data->UserNameSource));
}
ADF(L"Username: %s (Password: %s, Key file: %s, Passphrase: %s)",
(UserName, LogSensitive(Data->Password),
LogSensitive(Data->ResolvePublicKeyFile()), LogSensitive(Data->Passphrase)));
if (Data->UsesSsh)
{
ADF(L"Tunnel: %s", (BooleanToEngStr(Data->Tunnel)));
if (Data->Tunnel)
{
ADF(L"Tunnel: Hostname: %s (Port: %d)", (Data->TunnelHostName, Data->TunnelPortNumber));
ADF(L"Tunnel: Username: %s (Password: %s, Key file: %s)",
(Data->TunnelUserName,
LogSensitive(Data->TunnelPassword),
LogSensitive(Data->TunnelPublicKeyFile)));
ADF(L"Tunnel: Local port number: %d", (Data->TunnelLocalPortNumber));
}
}
ADF(L"Transfer Protocol: %s", (Data->FSProtocolStr));
if (Data->UsesSsh || (Data->FSProtocol == fsFTP))
{
UnicodeString PingType;
int PingInterval;
if (Data->FSProtocol == fsFTP)
{
PingType = EnumName(Data->FtpPingType, FtpPingTypeNames);
PingInterval = Data->FtpPingInterval;
}
else
{
PingType = EnumName(Data->PingType, PingTypeNames);
PingInterval = Data->PingInterval;
}
ADF(L"Ping type: %s, Ping interval: %d sec; Timeout: %d sec",
(PingType, PingInterval, Data->Timeout));
ADF(L"Disable Nagle: %s",
(BooleanToEngStr(Data->TcpNoDelay)));
}
ADF(L"Proxy: %s",
((Data->FtpProxyLogonType != 0) ?
FORMAT(L"FTP proxy %d", (Data->FtpProxyLogonType)) :
EnumName(Data->ProxyMethod, ProxyMethodNames)));
if ((Data->FtpProxyLogonType != 0) || (Data->ProxyMethod != ::pmNone))
{
ADF(L"HostName: %s (Port: %d); Username: %s; Passwd: %s",
(Data->ProxyHost, Data->ProxyPort,
Data->ProxyUsername, LogSensitive(Data->ProxyPassword)));
if (Data->ProxyMethod == pmTelnet)
{
ADF(L"Telnet command: %s", (Data->ProxyTelnetCommand));
}
if (Data->ProxyMethod == pmCmd)
{
ADF(L"Local command: %s", (Data->ProxyLocalCommand));
}
}
if (Data->UsesSsh || (Data->FSProtocol == fsFTP) || (Data->FSProtocol == fsS3))
{
ADF(L"Send buffer: %d", (Data->SendBuf));
}
if (Data->UsesSsh && !Data->SourceAddress.IsEmpty())
{
ADF(L"Source address: %s", (Data->SourceAddress));
}
if (Data->UsesSsh)
{
ADF(L"Compression: %s", (BooleanToEngStr(Data->Compression)));
ADF(L"Bypass authentication: %s",
(BooleanToEngStr(Data->SshNoUserAuth)));
ADF(L"Try agent: %s; Agent forwarding: %s; KI: %s; GSSAPI: %s",
(BooleanToEngStr(Data->TryAgent), BooleanToEngStr(Data->AgentFwd),
BooleanToEngStr(Data->AuthKI), BooleanToEngStr(Data->AuthGSSAPI)));
if (Data->AuthGSSAPI)
{
ADF(L"GSSAPI: KEX: %s; Forwarding: %s; Libs: %s; Custom: %s",
(BooleanToEngStr(Data->AuthGSSAPIKEX), BooleanToEngStr(Data->GSSAPIFwdTGT), Data->GssLibList, Data->GssLibCustom));
}
ADF(L"Ciphers: %s; Ssh2DES: %s",
(Data->CipherList, BooleanToEngStr(Data->Ssh2DES)));
ADF(L"KEX: %s", (Data->KexList));
UnicodeString Bugs;
for (int Index = 0; Index < BUG_COUNT; Index++)
{
AddToList(Bugs, EnumName(Data->Bug[static_cast<TSshBug>(Index)], AutoSwitchNames), L",");
}
ADF(L"SSH Bugs: %s", (Bugs));
ADF(L"Simple channel: %s", (BooleanToEngStr(Data->SshSimple)));
ADF(L"Return code variable: %s; Lookup user groups: %s",
((Data->DetectReturnVar ? UnicodeString(L"Autodetect") : Data->ReturnVar),
EnumName(Data->LookupUserGroups, AutoSwitchNames)));
ADF(L"Shell: %s", ((Data->Shell.IsEmpty()? UnicodeString(L"default") : Data->Shell)));
ADF(L"EOL: %s, UTF: %s", (EnumName(Data->EOLType, EOLTypeNames), EnumName(Data->NotUtf, NotAutoSwitchNames))); // NotUtf duplicated in FTP branch
ADF(L"Clear aliases: %s, Unset nat.vars: %s, Resolve symlinks: %s; Follow directory symlinks: %s",
(BooleanToEngStr(Data->ClearAliases), BooleanToEngStr(Data->UnsetNationalVars),
BooleanToEngStr(Data->ResolveSymlinks), BooleanToEngStr(Data->FollowDirectorySymlinks)));
ADF(L"LS: %s, Ign LS warn: %s, Scp1 Comp: %s; Exit code 1 is error: %s",
(Data->ListingCommand,
BooleanToEngStr(Data->IgnoreLsWarnings),
BooleanToEngStr(Data->Scp1Compatibility),
BooleanToEngStr(Data->ExitCode1IsError)));
}
if ((Data->FSProtocol == fsSFTP) || (Data->FSProtocol == fsSFTPonly))
{
UnicodeString Bugs;
for (int Index = 0; Index < SFTP_BUG_COUNT; Index++)
{
AddToList(Bugs, EnumName(Data->SFTPBug[static_cast<TSftpBug>(Index)], AutoSwitchNames), L",");
}
ADF(L"SFTP Bugs: %s", (Bugs));
ADF(L"SFTP Server: %s", ((Data->SftpServer.IsEmpty()? UnicodeString(L"default") : Data->SftpServer)));
if (Data->SFTPRealPath != asAuto)
{
ADF(L"SFTP Real path: %s", (EnumName(Data->SFTPRealPath, AutoSwitchNames)));
}
if (Data->UsePosixRename)
{
ADF(L"Use POSIX rename: %s", (BooleanToEngStr(Data->UsePosixRename)));
}
}
bool FtpsOn = false;
if (Data->FSProtocol == fsFTP)
{
ADF(L"UTF: %s", (EnumName(Data->NotUtf, NotAutoSwitchNames))); // duplicated in UsesSsh branch
UnicodeString Ftps;
switch (Data->Ftps)
{
case ftpsImplicit:
Ftps = L"Implicit TLS/SSL";
FtpsOn = true;
break;
case ftpsExplicitSsl:
Ftps = L"Explicit SSL/TLS";
FtpsOn = true;
break;
case ftpsExplicitTls:
Ftps = L"Explicit TLS/SSL";
FtpsOn = true;
break;
default:
DebugAssert(Data->Ftps == ftpsNone);
Ftps = L"None";
break;
}
// kind of hidden option, so do not reveal it unless it is set
if (Data->FtpTransferActiveImmediately != asAuto)
{
ADF(L"Transfer active immediately: %s", (EnumName(Data->FtpTransferActiveImmediately, AutoSwitchNames)));
}
ADF(L"FTPS: %s [Client certificate: %s]",
(Ftps, LogSensitive(Data->TlsCertificateFile)));
ADF(L"FTP: Passive: %s [Force IP: %s]; MLSD: %s [List all: %s]; HOST: %s",
(BooleanToEngStr(Data->FtpPasvMode),
EnumName(Data->FtpForcePasvIp, AutoSwitchNames),
EnumName(Data->FtpUseMlsd, AutoSwitchNames),
EnumName(Data->FtpListAll, AutoSwitchNames),
EnumName(Data->FtpHost, AutoSwitchNames)));
if (Data->FtpWorkFromCwd != asAuto)
{
ADF(L"FTP: Relative paths: %s", (EnumName(Data->FtpWorkFromCwd, AutoSwitchNames)));
}
}
if (Data->FSProtocol == fsWebDAV)
{
FtpsOn = (Data->Ftps != ftpsNone);
ADF(L"HTTPS: %s [Client certificate: %s]",
(BooleanToEngStr(FtpsOn), LogSensitive(Data->TlsCertificateFile)));
if (Data->WebDavLiberalEscaping)
{
ADF(L"WebDAV: Tolerate non-encoded: %s", (BooleanToEngStr(Data->WebDavLiberalEscaping)));
}
if (Data->WebDavCrossDomainRedirects)
{
ADF(L"WebDAV: Cross-domain redirects: %s", (BooleanToEngStr(Data->WebDavCrossDomainRedirects)));
}
if (Data->WebDavUnencryptedRedirects)
{
ADF(L"WebDAV: Unencrypted redirects: %s", (BooleanToEngStr(Data->WebDavUnencryptedRedirects)));
}
}
if (Data->FSProtocol == fsS3)
{
FtpsOn = (Data->Ftps != ftpsNone);
ADF(L"HTTPS: %s", (BooleanToEngStr(FtpsOn)));
ADF(L"S3: URL Style: %s", (EnumName(Data->S3UrlStyle, L"Virtual Host;Path")));
if (!Data->S3DefaultRegion.IsEmpty())
{
ADF(L"S3: Default region: %s", (Data->S3DefaultRegion));
}
if (!Data->S3SessionToken.IsEmpty())
{
ADF(L"S3: Session token: %s", (Data->S3SessionToken));
}
if (!Data->S3RoleArn.IsEmpty())
{
ADF(L"S3: Role ARN: %s (session name: %s)", (Data->S3RoleArn, DefaultStr(Data->S3RoleSessionName, L"default")));
}
if (Data->S3CredentialsEnv)
{
ADF(L"S3: Credentials from AWS environment: %s", (DefaultStr(Data->S3Profile, L"General")));
}
}
if (FtpsOn)
{
if (Data->FSProtocol == fsFTP)
{
ADF(L"Session reuse: %s", (BooleanToEngStr(Data->SslSessionReuse)));
}
ADF(L"TLS/SSL versions: %s-%s", (GetTlsVersionName(Data->MinTlsVersion), GetTlsVersionName(Data->MaxTlsVersion)));
}
ADF(L"Local directory: %s, Remote directory: %s, Update: %s, Cache: %s",
((Data->LocalDirectory.IsEmpty() ? UnicodeString(L"default") : Data->LocalDirectory),
(Data->RemoteDirectory.IsEmpty() ? UnicodeString(L"home") : Data->RemoteDirectory),
BooleanToEngStr(Data->UpdateDirectories),
BooleanToEngStr(Data->CacheDirectories)));
ADF(L"Cache directory changes: %s, Permanent: %s",
(BooleanToEngStr(Data->CacheDirectoryChanges),
BooleanToEngStr(Data->PreserveDirectoryChanges)));
ADF(L"Recycle bin: Delete to: %s, Overwritten to: %s, Bin path: %s",
(BooleanToEngStr(Data->DeleteToRecycleBin),
BooleanToEngStr(Data->OverwrittenToRecycleBin),
Data->RecycleBinPath));
if (Data->TrimVMSVersions || Data->VMSAllRevisions)
{
ADF(L"Trim VMS versions: %s; VMS all revisions: %s",
(BooleanToEngStr(Data->TrimVMSVersions), BooleanToEngStr(Data->VMSAllRevisions)));
}
UnicodeString TimeInfo;
if ((Data->FSProtocol == fsSFTP) || (Data->FSProtocol == fsSFTPonly) || (Data->FSProtocol == fsSCPonly) || (Data->FSProtocol == fsWebDAV))
{
AddToList(TimeInfo, FORMAT(L"DST mode: %s", (EnumName(Data->DSTMode, DSTModeNames))), L";");
}
if ((Data->FSProtocol == fsSCPonly) || (Data->FSProtocol == fsFTP))
{
int TimeDifferenceMin = TimeToMinutes(Data->TimeDifference);
AddToList(TimeInfo, FORMAT(L"Timezone offset: %dh %dm", ((TimeDifferenceMin / MinsPerHour), (TimeDifferenceMin % MinsPerHour))), L";");
}
ADSTR(TimeInfo);
if (Data->FSProtocol == fsWebDAV)
{
ADF(L"Compression: %s",
(BooleanToEngStr(Data->Compression)));
}
if ((Data->FSProtocol == fsFTP) && !Data->FtpPasvMode)
{
if (!Configuration->ExternalIpAddress.IsEmpty() || Configuration->HasLocalPortNumberLimits())
{
AddSeparator();
ADF(L"FTP active mode interface: %s:%d-%d", (DefaultStr(Configuration->ExternalIpAddress, L"<system address>"), Configuration->LocalPortNumberMin, Configuration->LocalPortNumberMax));
}
}
AddSeparator();
}
}
//---------------------------------------------------------------------------
#undef ADF
#undef ADSTR
//---------------------------------------------------------------------------
UnicodeString TSessionLog::GetSeparator()
{
return L"--------------------------------------------------------------------------";
}
//---------------------------------------------------------------------------
void __fastcall TSessionLog::AddSeparator()
{
Add(llMessage, GetSeparator());
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
__fastcall TActionLog::TActionLog(TSessionUI * UI, TDateTime Started, TSessionData * SessionData,
TConfiguration * Configuration)
{
DebugAssert(UI != NULL);
DebugAssert(SessionData != NULL);
Init(UI, Started, SessionData, Configuration);
}
//---------------------------------------------------------------------------
__fastcall TActionLog::TActionLog(TDateTime Started, TConfiguration * Configuration)
{
Init(NULL, Started, NULL, Configuration);
// not associated with session, so no need to waiting for anything
ReflectSettings();
}
//---------------------------------------------------------------------------
void __fastcall TActionLog::Init(TSessionUI * UI, TDateTime Started, TSessionData * SessionData,
TConfiguration * Configuration)
{
FCriticalSection = new TCriticalSection;
FConfiguration = Configuration;
FUI = UI;
FSessionData = SessionData;
FStarted = Started;
FFile = NULL;
FCurrentLogFileName = L"";
FCurrentFileName = L"";
FLogging = false;
FClosed = false;
FFailed = false;
FPendingActions = new TList();
FIndent = L" ";
FInGroup = false;
FEnabled = true;
}
//---------------------------------------------------------------------------
__fastcall TActionLog::~TActionLog()
{
DebugAssert(FPendingActions->Count == 0);
delete FPendingActions;
FClosed = true;
ReflectSettings();
DebugAssert(FFile == NULL);
delete FCriticalSection;
}
//---------------------------------------------------------------------------
void __fastcall TActionLog::Add(const UnicodeString & Line)
{
DebugAssert(FConfiguration);
if (FLogging)
{
TGuard Guard(FCriticalSection);
if (FFile == NULL)
{
OpenLogFile();
}
if (FFile != NULL)
{
try
{
UTF8String UtfLine = UTF8String(Line);
size_t Written =
fwrite(UtfLine.c_str(), 1, UtfLine.Length(), static_cast<FILE *>(FFile));
if (Written != static_cast<size_t>(UtfLine.Length()))
{
throw ECRTExtException(L"");
}
#ifdef _DEBUG
#endif
Written =
fwrite("\n", 1, 1, static_cast<FILE *>(FFile));
if (Written != 1)
{
throw ECRTExtException(L"");
}
}
catch (Exception &E)
{
FCriticalSection->Release();
// avoid endless loop when trying to close tags when closing log, when logging has failed
if (!FFailed)
{
FFailed = true;
// We failed logging, turn it off and notify user.
FConfiguration->LogActions = false;
if (FConfiguration->LogActionsRequired)
{
throw EFatal(&E, LoadStr(LOG_FATAL_ERROR));
}
else
{
try
{
throw ExtException(&E, LoadStr(LOG_GEN_ERROR));
}
catch (Exception &E)
{
if (FUI != NULL)
{
FUI->HandleExtendedException(&E);
}
}
}
}
}
}
}
}
//---------------------------------------------------------------------------
void __fastcall TActionLog::AddIndented(const UnicodeString & Line)
{
Add(FIndent + Line);
}
//---------------------------------------------------------------------------
void __fastcall TActionLog::AddFailure(TStrings * Messages)
{
AddIndented(L"<failure>");
AddMessages(L" ", Messages);
AddIndented(L"</failure>");
}
//---------------------------------------------------------------------------
void __fastcall TActionLog::AddFailure(Exception * E)
{
TStrings * Messages = ExceptionToMoreMessages(E);
if (Messages != NULL)
{
try
{
AddFailure(Messages);
}
__finally
{
delete Messages;
}
}
}
//---------------------------------------------------------------------------
void __fastcall TActionLog::AddMessages(UnicodeString Indent, TStrings * Messages)
{
for (int Index = 0; Index < Messages->Count; Index++)
{
AddIndented(
FORMAT(Indent + L"<message>%s</message>", (XmlEscape(Messages->Strings[Index]))));
}
}
//---------------------------------------------------------------------------
void __fastcall TActionLog::ReflectSettings()
{
TGuard Guard(FCriticalSection);
bool ALogging =
!FClosed && FConfiguration->LogActions && Enabled;
if (ALogging && !FLogging)
{
FLogging = true;
Add(XmlDeclaration);
UnicodeString SessionName =
(FSessionData != NULL) ? XmlAttributeEscape(FSessionData->SessionName) : UnicodeString(L"nosession");
Add(FORMAT(L"<session xmlns=\"http://winscp.net/schema/session/1.0\" name=\"%s\" start=\"%s\">",
(SessionName, StandardTimestamp())));
}
else if (!ALogging && FLogging)
{
if (FInGroup)
{
EndGroup();
}
// do not try to close the file, if it has not been opened, to avoid recursion
if (FFile != NULL)
{
Add(L"</session>");
}
CloseLogFile();
FLogging = false;
}
}
//---------------------------------------------------------------------------
void __fastcall TActionLog::CloseLogFile()
{
if (FFile != NULL)
{
fclose(static_cast<FILE *>(FFile));
FFile = NULL;
}
FCurrentLogFileName = L"";
FCurrentFileName = L"";
}
//---------------------------------------------------------------------------
void __fastcall TActionLog::OpenLogFile()
{
try
{
DebugAssert(FFile == NULL);
DebugAssert(FConfiguration != NULL);
FCurrentLogFileName = FConfiguration->ActionsLogFileName;
FFile = OpenFile(FCurrentLogFileName, FStarted, FSessionData, false, FCurrentFileName);
}
catch (Exception & E)
{
// We failed logging to file, turn it off and notify user.
FCurrentLogFileName = L"";
FCurrentFileName = L"";
FConfiguration->LogActions = false;
if (FConfiguration->LogActionsRequired)
{
throw EFatal(&E, LoadStr(LOG_FATAL_ERROR));
}
else
{
try
{
throw ExtException(&E, LoadStr(LOG_GEN_ERROR));
}
catch (Exception & E)
{
if (FUI != NULL)
{
// not to deadlock with TSessionLog::ReflectSettings invoked by FConfiguration->LogFileName setter above
TUnguard Unguard(FCriticalSection);
FUI->HandleExtendedException(&E);
}
}
}
}
}
//---------------------------------------------------------------------------
void __fastcall TActionLog::AddPendingAction(TSessionActionRecord * Action)
{
FPendingActions->Add(Action);
}
//---------------------------------------------------------------------------
void __fastcall TActionLog::RecordPendingActions()
{
while ((FPendingActions->Count > 0) &&
static_cast<TSessionActionRecord *>(FPendingActions->Items[0])->Record())
{
FPendingActions->Delete(0);
}
}
//---------------------------------------------------------------------------
void __fastcall TActionLog::BeginGroup(UnicodeString Name)
{
DebugAssert(!FInGroup);
FInGroup = true;
DebugAssert(FIndent == L" ");
AddIndented(FORMAT(L"<group name=\"%s\" start=\"%s\">",
(XmlAttributeEscape(Name), StandardTimestamp())));
FIndent = L" ";
}
//---------------------------------------------------------------------------
void __fastcall TActionLog::EndGroup()
{
DebugAssert(FInGroup);
FInGroup = false;
DebugAssert(FIndent == L" ");
FIndent = L" ";
// this is called from ReflectSettings that in turn is called when logging fails,
// so do not try to close the group, if it has not been opened, to avoid recursion
if (FFile != NULL)
{
AddIndented(L"</group>");
}
}
//---------------------------------------------------------------------------
void __fastcall TActionLog::SetEnabled(bool value)
{
if (Enabled != value)
{
FEnabled = value;
ReflectSettings();
}
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
TApplicationLog::TApplicationLog()
{
FFile = NULL;
FLogging = false;
FPeekReservedMemory = 0;
FCriticalSection.reset(new TCriticalSection());
}
//---------------------------------------------------------------------------
TApplicationLog::~TApplicationLog()
{
if (FFile != NULL)
{
Log(L"Closing log");
fclose(static_cast<FILE *>(FFile));
FFile = NULL;
}
FLogging = false;
}
//---------------------------------------------------------------------------
void TApplicationLog::Enable(const UnicodeString & Path)
{
UnicodeString Dummy;
FPath = Path;
FFile = OpenFile(FPath, Now(), NULL, false, Dummy);
FLogging = true;
}
//---------------------------------------------------------------------------
void TApplicationLog::AddStartupInfo()
{
if (Logging)
{
TSessionLog::DoAddStartupInfo(Log, Configuration, true);
}
}
//---------------------------------------------------------------------------
void __fastcall TApplicationLog::Log(const UnicodeString & S)
{
if (Logging)
{
TDateTime N = Now();
UnicodeString Timestamp = FormatDateTime(L"yyyy-mm-dd hh:nn:ss.zzz", N);
UnicodeString Line = FORMAT(L"[%s] [%x] %s\r\n", (Timestamp, static_cast<int>(GetCurrentThreadId()), S));
UTF8String UtfLine = UTF8String(Line);
int Writting = UtfLine.Length();
bool CheckMemory;
{
TGuard Guard(FCriticalSection.get());
if (FFile != NULL)
{
fwrite(UtfLine.c_str(), 1, Writting, static_cast<FILE *>(FFile));
}
__int64 SecondsSinceLastMemoryCheck = SecondsBetween(N, FLastMemoryCheck);
CheckMemory = (SecondsSinceLastMemoryCheck >= 10);
if (CheckMemory)
{
FLastMemoryCheck = N;
}
}
if (CheckMemory)
{
BYTE * Address = NULL;
MEMORY_BASIC_INFORMATION MemoryInfo;
size_t ReservedMemory = 0;
size_t CommittedMemory = 0;
while (VirtualQuery(Address, &MemoryInfo, sizeof(MemoryInfo)) == sizeof(MemoryInfo))
{
if ((MemoryInfo.State == MEM_RESERVE) || (MemoryInfo.State == MEM_COMMIT))
{
ReservedMemory += MemoryInfo.RegionSize;
}
if ((MemoryInfo.State == MEM_COMMIT) && (MemoryInfo.Type == MEM_PRIVATE))
{
CommittedMemory += MemoryInfo.RegionSize;
}
Address += MemoryInfo.RegionSize;
}
bool NewMemoryPeek;
{
TGuard Guard(FCriticalSection.get());
const size_t Threshold = 10 * 1024 * 1024;
NewMemoryPeek =
((ReservedMemory > FPeekReservedMemory) &&
((ReservedMemory - FPeekReservedMemory) > Threshold)) |
((CommittedMemory > FPeekCommittedMemory) &&
((CommittedMemory - FPeekCommittedMemory) > Threshold));
if (NewMemoryPeek)
{
FPeekReservedMemory = ReservedMemory;
FPeekCommittedMemory = CommittedMemory;
}
}
if (NewMemoryPeek)
{
Log(FORMAT(L"Memory increased: Reserved address space: %s, Committed private: %s",
(FormatNumber(__int64(ReservedMemory)), FormatNumber(__int64(CommittedMemory)))));
}
}
}
}