Index: ps/trunk/source/network/NetMessages.h =================================================================== --- ps/trunk/source/network/NetMessages.h (revision 21843) +++ ps/trunk/source/network/NetMessages.h (revision 21844) @@ -1,253 +1,254 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2018 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ /** * @file * The list of messages used by the network subsystem. */ #ifndef NETMESSAGES_H #define NETMESSAGES_H #include "ps/CStr.h" #include "scriptinterface/ScriptVal.h" #define PS_PROTOCOL_MAGIC 0x5073013f // 'P', 's', 0x01, '?' #define PS_PROTOCOL_MAGIC_RESPONSE 0x50630121 // 'P', 'c', 0x01, '!' #define PS_PROTOCOL_VERSION 0x01010015 // Arbitrary protocol #define PS_DEFAULT_PORT 0x5073 // 'P', 's' // Set when lobby authentication is required. Used in the SrvHandshakeResponseMessage. #define PS_NETWORK_FLAG_REQUIRE_LOBBYAUTH 0x1 // Defines the list of message types. The order of the list must not change. // The message types having a negative value are used internally and not sent // over the network. The message types used for network communication have // positive values. enum NetMessageType { NMT_CONNECT_COMPLETE = -256, NMT_CONNECTION_LOST, NMT_INVALID = 0, NMT_SERVER_HANDSHAKE, NMT_CLIENT_HANDSHAKE, NMT_SERVER_HANDSHAKE_RESPONSE, NMT_AUTHENTICATE, NMT_AUTHENTICATE_RESULT, NMT_CHAT, NMT_READY, NMT_CLEAR_ALL_READY, NMT_GAME_SETUP, NMT_ASSIGN_PLAYER, NMT_PLAYER_ASSIGNMENT, NMT_FILE_TRANSFER_REQUEST, NMT_FILE_TRANSFER_RESPONSE, NMT_FILE_TRANSFER_DATA, NMT_FILE_TRANSFER_ACK, NMT_JOIN_SYNC_START, NMT_REJOINED, NMT_KICKED, NMT_CLIENT_TIMEOUT, NMT_CLIENT_PERFORMANCE, NMT_CLIENTS_LOADING, NMT_CLIENT_PAUSED, NMT_LOADED_GAME, NMT_GAME_START, NMT_END_COMMAND_BATCH, NMT_SYNC_CHECK, // OOS-detection hash checking NMT_SYNC_ERROR, // OOS-detection error NMT_SIMULATION_COMMAND }; // Authentication result codes enum AuthenticateResultCode { ARC_OK, ARC_OK_REJOINING, ARC_PASSWORD_INVALID, }; #endif // NETMESSAGES_H #ifdef CREATING_NMT #define ALLNETMSGS_DONT_CREATE_NMTS #define START_NMT_CLASS_(_nm, _message) START_NMT_CLASS(C##_nm##Message, _message) #define DERIVE_NMT_CLASS_(_base, _nm, _message) START_NMT_CLASS_DERIVED(C ## _base ## Message, C ## _nm ## Message, _message) START_NMTS() START_NMT_CLASS_(SrvHandshake, NMT_SERVER_HANDSHAKE) NMT_FIELD_INT(m_Magic, u32, 4) NMT_FIELD_INT(m_ProtocolVersion, u32, 4) NMT_FIELD_INT(m_SoftwareVersion, u32, 4) END_NMT_CLASS() START_NMT_CLASS_(CliHandshake, NMT_CLIENT_HANDSHAKE) NMT_FIELD_INT(m_MagicResponse, u32, 4) NMT_FIELD_INT(m_ProtocolVersion, u32, 4) NMT_FIELD_INT(m_SoftwareVersion, u32, 4) END_NMT_CLASS() START_NMT_CLASS_(SrvHandshakeResponse, NMT_SERVER_HANDSHAKE_RESPONSE) NMT_FIELD_INT(m_UseProtocolVersion, u32, 4) NMT_FIELD_INT(m_Flags, u32, 4) NMT_FIELD(CStr, m_GUID) END_NMT_CLASS() START_NMT_CLASS_(Authenticate, NMT_AUTHENTICATE) NMT_FIELD(CStrW, m_Name) + // TODO: The password should not be printed to logfiles NMT_FIELD(CStrW, m_Password) NMT_FIELD_INT(m_IsLocalClient, u8, 1) END_NMT_CLASS() START_NMT_CLASS_(AuthenticateResult, NMT_AUTHENTICATE_RESULT) NMT_FIELD_INT(m_Code, u32, 4) NMT_FIELD_INT(m_HostID, u32, 2) NMT_FIELD(CStrW, m_Message) END_NMT_CLASS() START_NMT_CLASS_(Chat, NMT_CHAT) NMT_FIELD(CStr, m_GUID) // ignored when client->server, valid when server->client NMT_FIELD(CStrW, m_Message) END_NMT_CLASS() START_NMT_CLASS_(Ready, NMT_READY) NMT_FIELD(CStr, m_GUID) NMT_FIELD_INT(m_Status, u8, 1) END_NMT_CLASS() START_NMT_CLASS_(ClearAllReady, NMT_CLEAR_ALL_READY) END_NMT_CLASS() START_NMT_CLASS_(PlayerAssignment, NMT_PLAYER_ASSIGNMENT) NMT_START_ARRAY(m_Hosts) NMT_FIELD(CStr, m_GUID) NMT_FIELD(CStrW, m_Name) NMT_FIELD_INT(m_PlayerID, i8, 1) NMT_FIELD_INT(m_Status, u8, 1) NMT_END_ARRAY() END_NMT_CLASS() START_NMT_CLASS_(FileTransferRequest, NMT_FILE_TRANSFER_REQUEST) NMT_FIELD_INT(m_RequestID, u32, 4) END_NMT_CLASS() START_NMT_CLASS_(FileTransferResponse, NMT_FILE_TRANSFER_RESPONSE) NMT_FIELD_INT(m_RequestID, u32, 4) NMT_FIELD_INT(m_Length, u32, 4) END_NMT_CLASS() START_NMT_CLASS_(FileTransferData, NMT_FILE_TRANSFER_DATA) NMT_FIELD_INT(m_RequestID, u32, 4) NMT_FIELD(CStr, m_Data) END_NMT_CLASS() START_NMT_CLASS_(FileTransferAck, NMT_FILE_TRANSFER_ACK) NMT_FIELD_INT(m_RequestID, u32, 4) NMT_FIELD_INT(m_NumPackets, u32, 4) END_NMT_CLASS() START_NMT_CLASS_(JoinSyncStart, NMT_JOIN_SYNC_START) END_NMT_CLASS() START_NMT_CLASS_(Rejoined, NMT_REJOINED) NMT_FIELD(CStr, m_GUID) END_NMT_CLASS() START_NMT_CLASS_(Kicked, NMT_KICKED) NMT_FIELD(CStrW, m_Name) NMT_FIELD_INT(m_Ban, u8, 1) END_NMT_CLASS() START_NMT_CLASS_(ClientTimeout, NMT_CLIENT_TIMEOUT) NMT_FIELD(CStr, m_GUID) NMT_FIELD_INT(m_LastReceivedTime, u32, 4) END_NMT_CLASS() START_NMT_CLASS_(ClientPerformance, NMT_CLIENT_PERFORMANCE) NMT_START_ARRAY(m_Clients) NMT_FIELD(CStr, m_GUID) NMT_FIELD_INT(m_MeanRTT, u32, 4) NMT_END_ARRAY() END_NMT_CLASS() START_NMT_CLASS_(ClientsLoading, NMT_CLIENTS_LOADING) NMT_START_ARRAY(m_Clients) NMT_FIELD(CStr, m_GUID) NMT_END_ARRAY() END_NMT_CLASS() START_NMT_CLASS_(ClientPaused, NMT_CLIENT_PAUSED) NMT_FIELD(CStr, m_GUID) NMT_FIELD_INT(m_Pause, u8, 1) END_NMT_CLASS() START_NMT_CLASS_(LoadedGame, NMT_LOADED_GAME) NMT_FIELD_INT(m_CurrentTurn, u32, 4) END_NMT_CLASS() START_NMT_CLASS_(GameStart, NMT_GAME_START) END_NMT_CLASS() START_NMT_CLASS_(EndCommandBatch, NMT_END_COMMAND_BATCH) NMT_FIELD_INT(m_Turn, u32, 4) NMT_FIELD_INT(m_TurnLength, u32, 2) END_NMT_CLASS() START_NMT_CLASS_(SyncCheck, NMT_SYNC_CHECK) NMT_FIELD_INT(m_Turn, u32, 4) NMT_FIELD(CStr, m_Hash) END_NMT_CLASS() START_NMT_CLASS_(SyncError, NMT_SYNC_ERROR) NMT_FIELD_INT(m_Turn, u32, 4) NMT_FIELD(CStr, m_HashExpected) NMT_START_ARRAY(m_PlayerNames) NMT_FIELD(CStrW, m_Name) NMT_END_ARRAY() END_NMT_CLASS() START_NMT_CLASS_(AssignPlayer, NMT_ASSIGN_PLAYER) NMT_FIELD_INT(m_PlayerID, i8, 1) NMT_FIELD(CStr, m_GUID) END_NMT_CLASS() END_NMTS() #else #ifndef ALLNETMSGS_DONT_CREATE_NMTS # ifdef ALLNETMSGS_IMPLEMENT # define NMT_CREATOR_IMPLEMENT # endif # define NMT_CREATE_HEADER_NAME "NetMessages.h" # include "NMTCreator.h" #endif // #ifndef ALLNETMSGS_DONT_CREATE_NMTS #endif // #ifdef CREATING_NMT Index: ps/trunk/source/ps/ConfigDB.cpp =================================================================== --- ps/trunk/source/ps/ConfigDB.cpp (revision 21843) +++ ps/trunk/source/ps/ConfigDB.cpp (revision 21844) @@ -1,458 +1,464 @@ /* Copyright (C) 2018 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "ConfigDB.h" #include #include "lib/allocators/shared_ptr.h" #include "ps/CLogger.h" #include "ps/Filesystem.h" #include "ps/ThreadUtil.h" typedef std::map TConfigMap; TConfigMap CConfigDB::m_Map[CFG_LAST]; VfsPath CConfigDB::m_ConfigFile[CFG_LAST]; bool CConfigDB::m_HasChanges[CFG_LAST]; static pthread_mutex_t cfgdb_mutex = PTHREAD_MUTEX_INITIALIZER; +// These entries will not be printed to logfiles +static const std::set g_UnloggedEntries = { + "lobby.password", + "lobby.buddies" +}; + CConfigDB::CConfigDB() { // Recursive mutex needed for WriteFile pthread_mutexattr_t attr; int err; err = pthread_mutexattr_init(&attr); ENSURE(err == 0); err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); ENSURE(err == 0); err = pthread_mutex_init(&cfgdb_mutex, &attr); ENSURE(err == 0); err = pthread_mutexattr_destroy(&attr); ENSURE(err == 0); } #define CHECK_NS(rval)\ do {\ if (ns < 0 || ns >= CFG_LAST)\ {\ debug_warn(L"CConfigDB: Invalid ns value");\ return rval;\ }\ } while (false) namespace { template void Get(const CStr& value, T& ret) { std::stringstream ss(value); ss >> ret; } template<> void Get<>(const CStr& value, bool& ret) { ret = value == "true"; } template<> void Get<>(const CStr& value, std::string& ret) { ret = value; } std::string EscapeString(const CStr& str) { std::string ret; for (size_t i = 0; i < str.length(); ++i) { if (str[i] == '\\') ret += "\\\\"; else if (str[i] == '"') ret += "\\\""; else ret += str[i]; } return ret; } } // namespace #define GETVAL(type)\ void CConfigDB::GetValue(EConfigNamespace ns, const CStr& name, type& value)\ {\ CHECK_NS(;);\ CScopeLock s(&cfgdb_mutex);\ TConfigMap::iterator it = m_Map[CFG_COMMAND].find(name);\ if (it != m_Map[CFG_COMMAND].end())\ {\ Get(it->second[0], value);\ return;\ }\ for (int search_ns = ns; search_ns >= 0; --search_ns)\ {\ it = m_Map[search_ns].find(name);\ if (it != m_Map[search_ns].end())\ {\ Get(it->second[0], value);\ return;\ }\ }\ } GETVAL(bool) GETVAL(int) GETVAL(u32) GETVAL(float) GETVAL(double) GETVAL(std::string) #undef GETVAL bool CConfigDB::HasChanges(EConfigNamespace ns) const { CHECK_NS(false); CScopeLock s(&cfgdb_mutex); return m_HasChanges[ns]; } void CConfigDB::SetChanges(EConfigNamespace ns, bool value) { CHECK_NS(;); CScopeLock s(&cfgdb_mutex); m_HasChanges[ns] = value; } void CConfigDB::GetValues(EConfigNamespace ns, const CStr& name, CConfigValueSet& values) const { CHECK_NS(;); CScopeLock s(&cfgdb_mutex); TConfigMap::iterator it = m_Map[CFG_COMMAND].find(name); if (it != m_Map[CFG_COMMAND].end()) { values = it->second; return; } for (int search_ns = ns; search_ns >= 0; --search_ns) { it = m_Map[search_ns].find(name); if (it != m_Map[search_ns].end()) { values = it->second; return; } } } EConfigNamespace CConfigDB::GetValueNamespace(EConfigNamespace ns, const CStr& name) const { CHECK_NS(CFG_LAST); CScopeLock s(&cfgdb_mutex); TConfigMap::iterator it = m_Map[CFG_COMMAND].find(name); if (it != m_Map[CFG_COMMAND].end()) return CFG_COMMAND; for (int search_ns = ns; search_ns >= 0; --search_ns) { it = m_Map[search_ns].find(name); if (it != m_Map[search_ns].end()) return (EConfigNamespace)search_ns; } return CFG_LAST; } std::map CConfigDB::GetValuesWithPrefix(EConfigNamespace ns, const CStr& prefix) const { CScopeLock s(&cfgdb_mutex); std::map ret; CHECK_NS(ret); // Loop upwards so that values in later namespaces can override // values in earlier namespaces for (int search_ns = 0; search_ns <= ns; ++search_ns) for (const std::pair& p : m_Map[search_ns]) if (boost::algorithm::starts_with(p.first, prefix)) ret[p.first] = p.second; for (const std::pair& p : m_Map[CFG_COMMAND]) if (boost::algorithm::starts_with(p.first, prefix)) ret[p.first] = p.second; return ret; } void CConfigDB::SetValueString(EConfigNamespace ns, const CStr& name, const CStr& value) { CHECK_NS(;); CScopeLock s(&cfgdb_mutex); TConfigMap::iterator it = m_Map[ns].find(name); if (it == m_Map[ns].end()) it = m_Map[ns].insert(m_Map[ns].begin(), make_pair(name, CConfigValueSet(1))); it->second[0] = value; } void CConfigDB::SetValueBool(EConfigNamespace ns, const CStr& name, const bool value) { CStr valueString = value ? "true" : "false"; SetValueString(ns, name, valueString); } void CConfigDB::RemoveValue(EConfigNamespace ns, const CStr& name) { CHECK_NS(;); CScopeLock s(&cfgdb_mutex); TConfigMap::iterator it = m_Map[ns].find(name); if (it == m_Map[ns].end()) return; m_Map[ns].erase(it); } void CConfigDB::SetConfigFile(EConfigNamespace ns, const VfsPath& path) { CHECK_NS(;); CScopeLock s(&cfgdb_mutex); m_ConfigFile[ns] = path; } bool CConfigDB::Reload(EConfigNamespace ns) { CHECK_NS(false); CScopeLock s(&cfgdb_mutex); shared_ptr buffer; size_t buflen; { // Handle missing files quietly if (g_VFS->GetFileInfo(m_ConfigFile[ns], NULL) < 0) { LOGMESSAGE("Cannot find config file \"%s\" - ignoring", m_ConfigFile[ns].string8()); return false; } LOGMESSAGE("Loading config file \"%s\"", m_ConfigFile[ns].string8()); Status ret = g_VFS->LoadFile(m_ConfigFile[ns], buffer, buflen); if (ret != INFO::OK) { LOGERROR("CConfigDB::Reload(): vfs_load for \"%s\" failed: return was %lld", m_ConfigFile[ns].string8(), (long long)ret); return false; } } TConfigMap newMap; char *filebuf = (char*)buffer.get(); char *filebufend = filebuf+buflen; bool quoted = false; CStr header; CStr name; CStr value; int line = 1; std::vector values; for (char* pos = filebuf; pos < filebufend; ++pos) { switch (*pos) { case '\n': case ';': break; // We finished parsing this line case ' ': case '\r': case '\t': continue; // ignore case '[': header.clear(); for (++pos; pos < filebufend && *pos != '\n' && *pos != ']'; ++pos) header.push_back(*pos); if (pos == filebufend || *pos == '\n') { LOGERROR("Config header with missing close tag encountered on line %d in '%s'", line, m_ConfigFile[ns].string8()); header.clear(); ++line; continue; } LOGMESSAGE("Found config header '%s'", header.c_str()); header.push_back('.'); while (++pos < filebufend && *pos != '\n' && *pos != ';') if (*pos != ' ' && *pos != '\r') { LOGERROR("Config settings on the same line as a header on line %d in '%s'", line, m_ConfigFile[ns].string8()); break; } while (pos < filebufend && *pos != '\n') ++pos; ++line; continue; case '=': // Parse parameters (comma separated, possibly quoted) for (++pos; pos < filebufend && *pos != '\n' && *pos != ';'; ++pos) { switch (*pos) { case '"': quoted = true; // parse until not quoted anymore for (++pos; pos < filebufend && *pos != '\n' && *pos != '"'; ++pos) { if (*pos == '\\' && ++pos == filebufend) { LOGERROR("Escape character at end of input (line %d in '%s')", line, m_ConfigFile[ns].string8()); break; } value.push_back(*pos); } if (pos < filebufend && *pos == '"') quoted = false; else --pos; // We should terminate the outer loop too break; case ' ': case '\r': case '\t': break; // ignore case ',': if (!value.empty()) values.push_back(value); value.clear(); break; default: value.push_back(*pos); break; } } if (quoted) // We ignore the invalid parameter LOGERROR("Unmatched quote while parsing config file '%s' on line %d", m_ConfigFile[ns].string8(), line); else if (!value.empty()) values.push_back(value); value.clear(); quoted = false; break; // We are either at the end of the line, or we still have a comment to parse default: name.push_back(*pos); continue; } // Consume the rest of the line while (pos < filebufend && *pos != '\n') ++pos; // Store the setting if (!name.empty() && !values.empty()) { CStr key(header + name); newMap[key] = values; - if (key == "lobby.password") + if (g_UnloggedEntries.find(key) != g_UnloggedEntries.end()) LOGMESSAGE("Loaded config string \"%s\"", key); else { std::string vals; for (size_t i = 0; i < newMap[key].size() - 1; ++i) vals += "\"" + EscapeString(newMap[key][i]) + "\", "; vals += "\"" + EscapeString(newMap[key][values.size()-1]) + "\""; LOGMESSAGE("Loaded config string \"%s\" = %s", key, vals); } } else if (!name.empty()) LOGERROR("Encountered config setting '%s' without value while parsing '%s' on line %d", name, m_ConfigFile[ns].string8(), line); name.clear(); values.clear(); ++line; } if (!name.empty()) LOGERROR("Config file does not have a new line after the last config setting '%s'", name); m_Map[ns].swap(newMap); return true; } bool CConfigDB::WriteFile(EConfigNamespace ns) const { CHECK_NS(false); CScopeLock s(&cfgdb_mutex); return WriteFile(ns, m_ConfigFile[ns]); } bool CConfigDB::WriteFile(EConfigNamespace ns, const VfsPath& path) const { CHECK_NS(false); CScopeLock s(&cfgdb_mutex); shared_ptr buf; AllocateAligned(buf, 1*MiB, maxSectorSize); char* pos = (char*)buf.get(); for (const std::pair& p : m_Map[ns]) { size_t i; pos += sprintf(pos, "%s = ", p.first.c_str()); for (i = 0; i < p.second.size() - 1; ++i) pos += sprintf(pos, "\"%s\", ", EscapeString(p.second[i]).c_str()); pos += sprintf(pos, "\"%s\"\n", EscapeString(p.second[i]).c_str()); } const size_t len = pos - (char*)buf.get(); Status ret = g_VFS->CreateFile(path, buf, len); if (ret < 0) { LOGERROR("CConfigDB::WriteFile(): CreateFile \"%s\" failed (error: %d)", path.string8(), (int)ret); return false; } return true; } bool CConfigDB::WriteValueToFile(EConfigNamespace ns, const CStr& name, const CStr& value) { CHECK_NS(false); CScopeLock s(&cfgdb_mutex); return WriteValueToFile(ns, name, value, m_ConfigFile[ns]); } bool CConfigDB::WriteValueToFile(EConfigNamespace ns, const CStr& name, const CStr& value, const VfsPath& path) { CHECK_NS(false); CScopeLock s(&cfgdb_mutex); TConfigMap newMap; m_Map[ns].swap(newMap); Reload(ns); SetValueString(ns, name, value); bool ret = WriteFile(ns, path); m_Map[ns].swap(newMap); return ret; } #undef CHECK_NS