21bool IsAbsolutePath(std::string
const& path)
26 if (path[0] ==
'/' || path[0] ==
'\\')
29 return path.size() > 2 && std::isalpha(
static_cast<unsigned char>(path[0])) && path[1] ==
':';
32std::string JoinPath(std::string base, std::string
const& path)
34 if (path.empty() || IsAbsolutePath(path) || base.empty())
37 if (base.back() !=
'/' && base.back() !=
'\\')
43std::string SanitizePathPart(std::string value)
45 for (
char& ch : value)
46 if (!std::isalnum(
static_cast<unsigned char>(ch)) && ch !=
'.' && ch !=
'-' && ch !=
'_')
55std::string SanitizeMarker(std::string marker)
57 for (
char& ch : marker)
58 if (ch ==
'\r' || ch ==
'\n')
64std::string NormalizeFilterName(std::string value)
66 std::transform(value.begin(), value.end(), value.begin(), [](
unsigned char ch)
68 return static_cast<char>(std::tolower(ch));
79 return std::to_string(sessionInfo.
AccountId);
108 std::ostringstream identity;
109 identity << GetFilePrefix(sessionInfo)
113 << GetAccountPathPart(sessionInfo)
117 << GetSessionPathPart(sessionInfo);
119 return identity.str();
122std::filesystem::path BuildSessionLogPath(std::string
const& sessionLogDir,
void const* sessionKey,
125 auto now = std::chrono::system_clock::now();
126 std::time_t timestamp = std::chrono::system_clock::to_time_t(now);
128#if PLATFORM == PLATFORM_WINDOWS
129 localtime_s(&localTime, ×tamp);
131 localtime_r(×tamp, &localTime);
134 std::ostringstream filename;
135 filename << GetFilePrefix(sessionInfo)
137 << std::put_time(&localTime,
"%Y%m%d_%H%M%S")
141 << SanitizePathPart(GetAccountPathPart(sessionInfo))
145 << SanitizePathPart(GetSessionPathPart(sessionInfo))
147 <<
reinterpret_cast<uintptr_t
>(sessionKey)
150 return std::filesystem::path(sessionLogDir) / filename.str();
179 std::string logsDir =
sConfigMgr->GetStringDefault(
"LogsDir",
"");
181 sConfigMgr->GetStringDefault(
"PacketLogServerControlFile", controlFileDefault),
182 sConfigMgr->GetStringDefault(
"PacketLogServerOutputDir", outputDirDefault),
189 if (!logsDir.empty())
190 if ((logsDir.at(logsDir.length() - 1) !=
'/') && (logsDir.at(logsDir.length() - 1) !=
'\\'))
191 logsDir.push_back(
'/');
201 if (!controlFile.empty())
205 std::string legacyControlFile = JoinPath(
_logsDir, controlFile);
206 if (legacyControlFile != controlFile)
221 if (!packetLogFile.empty())
235 std::error_code error;
236 if (!controlFile.empty() && std::filesystem::exists(controlFile, error) && !error)
257 std::error_code error;
258 std::filesystem::remove(controlFile, error);
273 std::string normalizedName = NormalizeFilterName(characterName);
297 std::string filterName = GetFilterName(sessionInfo);
298 if (filterName.empty())
308 return itr->second.file;
310 std::error_code error;
315 std::filesystem::path path = BuildSessionLogPath(
_sessionLogDir, sessionKey, sessionInfo);
316 FILE* file = fopen(path.string().c_str(),
"w");
320 fprintf(file,
"# SkyFire packet log session\n");
321 fprintf(file,
"# RemoteAddress: %s\n", sessionInfo.
RemoteAddress.c_str());
322 fprintf(file,
"# AccountId: %u\n", sessionInfo.
AccountId);
324 fprintf(file,
"# AccountName: %s\n", sessionInfo.
AccountName.c_str());
325 fprintf(file,
"# RealmId: %u\n", sessionInfo.
RealmId);
328 fprintf(file,
"# SessionName: %s\n", sessionInfo.
SessionName.c_str());
329 fprintf(file,
"# Format: sequence unix_time direction opcode_number opcode_name size payload_hex\n");
335 session.
path = path.string();
336 session.
info = sessionInfo;
343 void const* payload,
size_t payloadSize)
351 if (itr->second.file)
352 fclose(itr->second.file);
365 session.
info = sessionInfo;
367 fprintf(file,
"%llu %u %s 0x%04X %s %u ",
368 static_cast<unsigned long long>(++session.
sequence),
369 static_cast<uint32>(time(NULL)),
373 static_cast<uint32>(payloadSize));
375 uint8 const* bytes =
static_cast<uint8 const*
>(payload);
376 for (
size_t i = 0; i < payloadSize && bytes; ++i)
377 fprintf(file,
"%02X", bytes[i]);
390 data <<
int32(opcode);
391 data <<
int32(payloadSize);
392 data <<
uint32(time(NULL));
395 if (payload && payloadSize)
396 data.
append(
static_cast<uint8 const*
>(payload), payloadSize);
410 if (itr->second.file)
411 fclose(itr->second.file);
424 session.
info = sessionInfo;
425 fprintf(file,
"# %llu %u MARKER %s\n",
426 static_cast<unsigned long long>(++session.
sequence),
427 static_cast<uint32>(time(NULL)),
428 SanitizeMarker(marker).c_str());
440 bool const identityChanged = BuildSessionIdentity(session.
info) != BuildSessionIdentity(sessionInfo);
441 session.
info = sessionInfo;
443 if (!identityChanged || session.
path.empty())
448 fflush(session.
file);
449 fclose(session.
file);
453 std::filesystem::path oldPath = session.
path;
454 std::filesystem::path newPath = BuildSessionLogPath(
_sessionLogDir, sessionKey, sessionInfo);
455 std::error_code error;
456 std::filesystem::rename(oldPath, newPath, error);
458 session.
path = newPath.string();
460 session.
file = fopen(session.
path.c_str(),
"a");
467 fprintf(session.
file,
"# %llu %u MARKER refreshed session metadata accountId=%u accountName=%s realm=%u character=%s session=%s\n",
468 static_cast<unsigned long long>(++session.
sequence),
469 static_cast<uint32>(time(NULL)),
475 fflush(session.
file);
485 if (itr->second.file)
486 fclose(itr->second.file);
494 if (session.second.file)
495 fclose(session.second.file);
504 if (NormalizeFilterName(GetFilterName(itr->second.info)) != normalizedName)
510 if (itr->second.file)
511 fclose(itr->second.file);
void EnableCharacterLogging(std::string const &characterName)
std::string _sessionLogDir
bool IsGlobalLoggingEnabled() const
void Initialize(std::string const &controlFileDefault, std::string const &outputDirDefault)
std::vector< std::string > _controlFiles
void LogBinaryPacket(PacketLogServerDirection direction, uint32 opcode, void const *payload, size_t payloadSize)
void DisableCharacterLogging(std::string const &characterName)
std::unordered_set< std::string > _characterSessionLogging
bool _globalSessionLogging
bool IsControlFileLoggingEnabled() const
void CloseSession(void const *sessionKey)
void LogPacket(void const *sessionKey, PacketLogServerSessionInfo const &sessionInfo, PacketLogServerDirection direction, uint32 opcode, std::string const &opcodeName, void const *payload, size_t payloadSize)
void EnableGlobalLogging()
void Configure(std::string const &controlFile, std::string const &outputDir, std::string logsDir="")
bool CanLogPacket() const
void RefreshSessionInfo(void const *sessionKey, PacketLogServerSessionInfo const &sessionInfo)
size_t GetCharacterLoggingCount() const
void CloseCharacterSessions(std::string const &normalizedName)
std::unordered_map< void const *, SessionLog > _sessionLogs
void LogMarker(void const *sessionKey, PacketLogServerSessionInfo const &sessionInfo, std::string const &marker)
void ConfigureLegacyPacketLogFile(std::string const &packetLogFile)
FILE * OpenSessionLog(void const *sessionKey, PacketLogServerSessionInfo const &sessionInfo)
bool ShouldLogSession(PacketLogServerSessionInfo const &sessionInfo) const
@ PACKET_LOG_CLIENT_TO_SERVER
PacketLogServerSessionInfo info
std::string CharacterName
PacketLogServerSessionInfo()
std::string RemoteAddress