diff --git a/engine/server/application/CentralServer/src/shared/GameServerConnection.cpp b/engine/server/application/CentralServer/src/shared/GameServerConnection.cpp index ac6edcb8..bb70c1c5 100755 --- a/engine/server/application/CentralServer/src/shared/GameServerConnection.cpp +++ b/engine/server/application/CentralServer/src/shared/GameServerConnection.cpp @@ -33,9 +33,10 @@ #include "serverNetworkMessages/UploadCharacterMessage.h" #include "sharedLog/Log.h" #include "sharedNetworkMessages/ConsoleChannelMessages.h" -#include "sharedNetworkMessages/GenericValueTypeMessage.h" #include "unicodeArchive/UnicodeArchive.h" +#include "sharedNetworkMessages/GenericValueTypeMessage.h" + #include "sharedFoundation/CrcConstexpr.hpp" // ====================================================================== @@ -126,11 +127,16 @@ void GameServerConnection::onReceive(Archive::ByteStream const &message) } case constcrc("TransferReplyNameValidation") : { - GenericValueTypeMessage > const replyNameValidation(ri); + GenericValueTypeMessage > const replyNameValidation(ri); + auto i = replyNameValidation.getValue().begin(); - if (replyNameValidation.getValue().second.getTransferRequestSource() == TransferRequestMoveValidation::TRS_transfer_server) + if (i == replyNameValidation.getValue().end()) { + break; + } + + if (i->second.getTransferRequestSource() == TransferRequestMoveValidation::TRS_transfer_server) { - LOG("CustomerService", ("CharacterTransfer: Received TransferReplyNameValidation from GameServer, forwarding to TransferServer : %s", replyNameValidation.getValue().second.toString().c_str())); + LOG("CustomerService", ("CharacterTransfer: Received TransferReplyNameValidation from GameServer, forwarding to TransferServer : %s", i->second.toString().c_str())); CentralServer::getInstance().sendToTransferServer(replyNameValidation); } else @@ -138,14 +144,14 @@ void GameServerConnection::onReceive(Archive::ByteStream const &message) // pass reply back to the source galaxy for handling, which is to // either display an error message to the user if the request failed, // or to start the transfer process if the request succeeds - LOG("CustomerService", ("CharacterTransfer: Received TransferReplyNameValidation from GameServer, forwarding to source galaxy CentralServer : %s", replyNameValidation.getValue().second.toString().c_str())); + LOG("CustomerService", ("CharacterTransfer: Received TransferReplyNameValidation from GameServer, forwarding to source galaxy CentralServer : %s", i->second.toString().c_str())); CentralServer::getInstance().sendToArbitraryLoginServer(replyNameValidation); // if the request succeeded, also disconnect any clients with a connection to SWG services on this (the target) galaxy - if (replyNameValidation.getValue().second.getIsValidName() && (replyNameValidation.getValue().second.getTransferRequestSource() != TransferRequestMoveValidation::TRS_ingame_freects_command_validate) && (replyNameValidation.getValue().second.getTransferRequestSource() != TransferRequestMoveValidation::TRS_ingame_cts_command_validate)) + if (i->second.getIsValidName() && (i->second.getTransferRequestSource() != TransferRequestMoveValidation::TRS_ingame_freects_command_validate) && (i->second.getTransferRequestSource() != TransferRequestMoveValidation::TRS_ingame_cts_command_validate)) { - GenericValueTypeMessage kickSource("TransferKickConnectedClients", replyNameValidation.getValue().second.getSourceStationId()); - GenericValueTypeMessage kickDestination("TransferKickConnectedClients", replyNameValidation.getValue().second.getDestinationStationId()); + GenericValueTypeMessage kickSource("TransferKickConnectedClients", i->second.getSourceStationId()); + GenericValueTypeMessage kickDestination("TransferKickConnectedClients", i->second.getDestinationStationId()); CentralServer::getInstance().sendToAllLoginServers(kickSource); CentralServer::getInstance().sendToAllLoginServers(kickDestination); CentralServer::getInstance().sendToAllConnectionServers(kickSource, true); diff --git a/engine/server/application/ConnectionServer/src/shared/ClientConnection.cpp b/engine/server/application/ConnectionServer/src/shared/ClientConnection.cpp index 21af7631..5dd3e235 100755 --- a/engine/server/application/ConnectionServer/src/shared/ClientConnection.cpp +++ b/engine/server/application/ConnectionServer/src/shared/ClientConnection.cpp @@ -54,11 +54,14 @@ #include +#include "webAPI.h" + +using namespace StellaBellum; + //----------------------------------------------------------------------- -namespace ClientConnectionNamespace -{ - unsigned long gs_receiveDelayMaxMs = 16384; +namespace ClientConnectionNamespace { + unsigned long gs_receiveDelayMaxMs = 16384; } using namespace ClientConnectionNamespace; @@ -69,228 +72,217 @@ using namespace ClientConnectionNamespace; //----------------------------------------------------------------------- -std::map< std::string, uint32 > ClientConnection::sm_outgoingBytesMap_Working; // working stats that will rotate after 1 minute -std::map< std::string, uint32 > ClientConnection::sm_outgoingBytesMap_Stats; // computed stats from the last minute -uint32 ClientConnection::sm_outgoingBytesMap_Worktime = 0 ; // time we started filling in the working map +std::map ClientConnection::sm_outgoingBytesMap_Working; // working stats that will rotate after 1 minute +std::map ClientConnection::sm_outgoingBytesMap_Stats; // computed stats from the last minute +uint32 ClientConnection::sm_outgoingBytesMap_Worktime = 0; // time we started filling in the working map //----------------------------------------------------------------------- -ClientConnection::ClientConnection(UdpConnectionMT * u, TcpClient * t) : -ServerConnection(u, t), -m_accountName(""), -m_canCreateRegularCharacter(false), -m_canCreateJediCharacter(false), -m_hasRequestedCharacterCreate(false), -m_hasCreatedCharacter(false), -m_pendingCharacterCreate(nullptr), -m_canSkipTutorial(false), -m_characterId(NetworkId::cms_invalid), -m_characterName(), -m_startPlayTime(0), -m_lastActiveTime(0), -m_activePlayTimeDuration(0), -m_client(0), -m_containerId(NetworkId::cms_invalid), -m_featureBitsGame(0), -m_featureBitsSubscription(0), -m_hasBeenSentToGameServer(false), -m_hasBeenValidated(false), -m_hasSelectedCharacter(false), -m_isSecure(false), -m_isAdminAccount(false), -m_hasCSLoggedAccountFeatureIds(false), -m_suid(0), -m_requestedSuid(0), -m_usingAdminLogin(false), -m_targetCoordinates(), -m_targetScene(""), -m_validatingCharacter(false), -m_receiveHistoryBytes(0), -m_receiveHistoryPackets(0), -m_receiveHistoryMs(0), -m_receiveLastTimeMs(0), -m_sendLastTimeMs(0), -m_sessionId(""), -m_sessionValidated(false), -m_connectionServerLag(0), -m_gameServerLag(0), -m_countSpamLimitResetTime(0), -m_entitlementTotalTime(0), -m_entitlementEntitledTime(0), -m_entitlementTotalTimeSinceLastLogin(0), -m_entitlementEntitledTimeSinceLastLogin(0), -m_buddyPoints(0), -m_sendToStarport(false), -m_pendingChatEnterRoomRequests(), -m_pendingChatQueryRoomRequests() -{ - static const std::string loginTrace("TRACE_LOGIN"); - LOG(loginTrace, ("new ClientConnection")); +ClientConnection::ClientConnection(UdpConnectionMT *u, TcpClient *t) : + ServerConnection(u, t), + m_accountName(""), + m_canCreateRegularCharacter(false), + m_canCreateJediCharacter(false), + m_hasRequestedCharacterCreate(false), + m_hasCreatedCharacter(false), + m_pendingCharacterCreate(nullptr), + m_canSkipTutorial(false), + m_characterId(NetworkId::cms_invalid), + m_characterName(), + m_startPlayTime(0), + m_lastActiveTime(0), + m_activePlayTimeDuration(0), + m_client(0), + m_containerId(NetworkId::cms_invalid), + m_featureBitsGame(0), + m_featureBitsSubscription(0), + m_hasBeenSentToGameServer(false), + m_hasBeenValidated(false), + m_hasSelectedCharacter(false), + m_isSecure(false), + m_isAdminAccount(false), + m_hasCSLoggedAccountFeatureIds(false), + m_suid(0), + m_requestedSuid(0), + m_usingAdminLogin(false), + m_targetCoordinates(), + m_targetScene(""), + m_validatingCharacter(false), + m_receiveHistoryBytes(0), + m_receiveHistoryPackets(0), + m_receiveHistoryMs(0), + m_receiveLastTimeMs(0), + m_sendLastTimeMs(0), + m_sessionId(""), + m_sessionValidated(false), + m_connectionServerLag(0), + m_gameServerLag(0), + m_countSpamLimitResetTime(0), + m_entitlementTotalTime(0), + m_entitlementEntitledTime(0), + m_entitlementTotalTimeSinceLastLogin(0), + m_entitlementEntitledTimeSinceLastLogin(0), + m_buddyPoints(0), + m_sendToStarport(false), + m_pendingChatEnterRoomRequests(), + m_pendingChatQueryRoomRequests() { + static const std::string loginTrace("TRACE_LOGIN"); + LOG(loginTrace, ("new ClientConnection")); - setNoDataTimeout(600000); + setNoDataTimeout(600000); } //----------------------------------------------------------------------- -ClientConnection::~ClientConnection() -{ - bool hasBeenKicked = false; +ClientConnection::~ClientConnection() { + bool hasBeenKicked = false; - if (ConnectionServer::getClientConnection(m_suid) == this) - { - ConnectionServer::removeConnectedCharacter(m_suid); - } - if (m_client) - { - hasBeenKicked = m_client->hasBeenKicked(); - delete m_client; - m_client = nullptr; - } + if (ConnectionServer::getClientConnection(m_suid) == this) { + ConnectionServer::removeConnectedCharacter(m_suid); + } + if (m_client) { + hasBeenKicked = m_client->hasBeenKicked(); + delete m_client; + m_client = nullptr; + } - // tell Session to stop recording play time for the character - if (m_hasBeenValidated && m_sessionValidated && ConnectionServer::getSessionApiClient() && (m_lastActiveTime > 0) && ConfigConnectionServer::getSessionRecordPlayTime()) - { - LOG("CustomerService", ("Login:%s calling SessionStopPlay() for %s/%s/%s (%s). Active play time: %s", ClientConnection::describeAccount(this).c_str(), this->getSessionId().c_str(), ConfigConnectionServer::getClusterName(), this->getCharacterName().c_str(), this->getCharacterId().getValueString().c_str(), this->getCurrentActivePlayTimeDuration().c_str())); + // tell Session to stop recording play time for the character + if (m_hasBeenValidated && m_sessionValidated && ConnectionServer::getSessionApiClient() && (m_lastActiveTime > 0) && + ConfigConnectionServer::getSessionRecordPlayTime()) { + LOG("CustomerService", + ("Login:%s calling SessionStopPlay() for %s/%s/%s (%s). Active play time: %s", ClientConnection::describeAccount( + this).c_str(), this->getSessionId().c_str(), ConfigConnectionServer::getClusterName(), this->getCharacterName().c_str(), this->getCharacterId().getValueString().c_str(), this->getCurrentActivePlayTimeDuration().c_str())); - // log total active play time for the session to the balance log - LOG("GameBalance", ("balancelog:%s calling SessionStopPlay() for %s/%s/%s (%s). Active play time: %s", ClientConnection::describeAccount(this).c_str(), this->getSessionId().c_str(), ConfigConnectionServer::getClusterName(), this->getCharacterName().c_str(), this->getCharacterId().getValueString().c_str(), this->getActivePlayTimeDuration().c_str())); + // log total active play time for the session to the balance log + LOG("GameBalance", + ("balancelog:%s calling SessionStopPlay() for %s/%s/%s (%s). Active play time: %s", ClientConnection::describeAccount( + this).c_str(), this->getSessionId().c_str(), ConfigConnectionServer::getClusterName(), this->getCharacterName().c_str(), this->getCharacterId().getValueString().c_str(), this->getActivePlayTimeDuration().c_str())); - ConnectionServer::getSessionApiClient()->stopPlay(*this); - } + ConnectionServer::getSessionApiClient()->stopPlay(*this); + } - if (ConnectionServer::getSessionApiClient()) - { - ConnectionServer::getSessionApiClient()->dropClient(this, hasBeenKicked); - } + if (ConnectionServer::getSessionApiClient()) { + ConnectionServer::getSessionApiClient()->dropClient(this, hasBeenKicked); + } - std::map::const_iterator iter; - for (iter = m_pendingChatEnterRoomRequests.begin(); iter != m_pendingChatEnterRoomRequests.end(); ++iter) - { - delete iter->second; - } - m_pendingChatEnterRoomRequests.clear(); + std::map::const_iterator iter; + for (iter = m_pendingChatEnterRoomRequests.begin(); iter != m_pendingChatEnterRoomRequests.end(); ++iter) { + delete iter->second; + } + m_pendingChatEnterRoomRequests.clear(); - for (iter = m_pendingChatQueryRoomRequests.begin(); iter != m_pendingChatQueryRoomRequests.end(); ++iter) - { - delete iter->second; - } - m_pendingChatQueryRoomRequests.clear(); + for (iter = m_pendingChatQueryRoomRequests.begin(); iter != m_pendingChatQueryRoomRequests.end(); ++iter) { + delete iter->second; + } + m_pendingChatQueryRoomRequests.clear(); - delete m_pendingCharacterCreate; - m_pendingCharacterCreate = nullptr; + delete m_pendingCharacterCreate; + m_pendingCharacterCreate = nullptr; } //----------------------------------------------------------------------- -const NetworkId & ClientConnection::getCharacterId() const -{ - return m_characterId; +const NetworkId &ClientConnection::getCharacterId() const { + return m_characterId; } //----------------------------------------------------------------------- -const std::string & ClientConnection::getCharacterName() const -{ - return m_characterName; +const std::string &ClientConnection::getCharacterName() const { + return m_characterName; } //----------------------------------------------------------------------- -std::string ClientConnection::getPlayTimeDuration() const -{ - int playTimeDuration = 0; +std::string ClientConnection::getPlayTimeDuration() const { + int playTimeDuration = 0; - if (m_startPlayTime > 0) - playTimeDuration = static_cast(::time(nullptr) - m_startPlayTime); + if (m_startPlayTime > 0) + playTimeDuration = static_cast(::time(nullptr) - m_startPlayTime); - return CalendarTime::convertSecondsToHMS(static_cast(playTimeDuration)); + return CalendarTime::convertSecondsToHMS(static_cast(playTimeDuration)); } //----------------------------------------------------------------------- -std::string ClientConnection::getActivePlayTimeDuration() const -{ - int activePlayTimeDuration = static_cast(m_activePlayTimeDuration); +std::string ClientConnection::getActivePlayTimeDuration() const { + int activePlayTimeDuration = static_cast(m_activePlayTimeDuration); - if (m_lastActiveTime > 0) - activePlayTimeDuration += static_cast(::time(nullptr) - m_lastActiveTime); + if (m_lastActiveTime > 0) + activePlayTimeDuration += static_cast(::time(nullptr) - m_lastActiveTime); - return CalendarTime::convertSecondsToHMS(static_cast(activePlayTimeDuration)); + return CalendarTime::convertSecondsToHMS(static_cast(activePlayTimeDuration)); } //----------------------------------------------------------------------- -std::string ClientConnection::getCurrentActivePlayTimeDuration() const -{ - int activePlayTimeDuration = 0; +std::string ClientConnection::getCurrentActivePlayTimeDuration() const { + int activePlayTimeDuration = 0; - if (m_lastActiveTime > 0) - activePlayTimeDuration = static_cast(::time(nullptr) - m_lastActiveTime); + if (m_lastActiveTime > 0) + activePlayTimeDuration = static_cast(::time(nullptr) - m_lastActiveTime); - return CalendarTime::convertSecondsToHMS(static_cast(activePlayTimeDuration)); + return CalendarTime::convertSecondsToHMS(static_cast(activePlayTimeDuration)); } //----------------------------------------------------------------------- -void ClientConnection::sendPlayTimeInfoToGameServer() const -{ - if (m_client && m_client->getGameConnection()) - { - // update the game server with play time info - GenericValueTypeMessage > > const msgPlayTimeInfo( - "UpdateSessionPlayTimeInfo", - std::make_pair(static_cast(m_startPlayTime), - std::make_pair(static_cast(m_lastActiveTime), m_activePlayTimeDuration) - ) - ); +void ClientConnection::sendPlayTimeInfoToGameServer() const { + if (m_client && m_client->getGameConnection()) { + // update the game server with play time info + GenericValueTypeMessage > > const msgPlayTimeInfo( + "UpdateSessionPlayTimeInfo", + std::make_pair(static_cast(m_startPlayTime), + std::make_pair(static_cast(m_lastActiveTime), m_activePlayTimeDuration) + ) + ); - std::vector v; - v.push_back(m_client->getNetworkId()); - GameClientMessage const gcm(v, true, msgPlayTimeInfo); - m_client->getGameConnection()->send(gcm, true); - } + std::vector v; + v.push_back(m_client->getNetworkId()); + GameClientMessage const gcm(v, true, msgPlayTimeInfo); + m_client->getGameConnection()->send(gcm, true); + } } // ---------------------------------------------------------------------- -void ClientConnection::handleSelectCharacterMessage(const SelectCharacter& msg) -{ - //Only accept this message from clients who have been validated and - //haven't already selected. - if (m_hasSelectedCharacter || m_validatingCharacter || !m_hasBeenValidated || !m_sessionValidated) - { - if(m_hasSelectedCharacter) - { - LOG("TraceCharacterSelection", ("%d cannot select a character because the client has already selected a character", getSUID())); - } - if(m_validatingCharacter) - { - LOG("TraceCharacterSelection", ("%d cannot select a character because the client has not yet received validation", getSUID())); - } - if(!m_hasBeenValidated) - { - LOG("TraceCharacterSelection", ("%d cannot select a character because the client has not been validated", getSUID())); - } - if (!m_sessionValidated) - { - LOG("TraceCharacterSelection", ("%d cannot select a character because the client has not been session validated", getSUID())); - } +void ClientConnection::handleSelectCharacterMessage(const SelectCharacter &msg) { + //Only accept this message from clients who have been validated and + //haven't already selected. + if (m_hasSelectedCharacter || m_validatingCharacter || !m_hasBeenValidated || !m_sessionValidated) { + if (m_hasSelectedCharacter) { + LOG("TraceCharacterSelection", + ("%d cannot select a character because the client has already selected a character", getSUID())); + } + if (m_validatingCharacter) { + LOG("TraceCharacterSelection", + ("%d cannot select a character because the client has not yet received validation", getSUID())); + } + if (!m_hasBeenValidated) { + LOG("TraceCharacterSelection", + ("%d cannot select a character because the client has not been validated", getSUID())); + } + if (!m_sessionValidated) { + LOG("TraceCharacterSelection", + ("%d cannot select a character because the client has not been session validated", getSUID())); + } - return; - } + return; + } - m_validatingCharacter = true; + m_validatingCharacter = true; - // The client is picking a character from the list the Login Server gave him. - // But we don't trust him not to cheat, so we double-check that he really - // owns the character he selected. + // The client is picking a character from the list the Login Server gave him. + // But we don't trust him not to cheat, so we double-check that he really + // owns the character he selected. - ValidateCharacterForLoginMessage vclm(getSUID(), msg.getId()); - ConnectionServer::sendToCentralProcess(vclm); - LOG("TraceCharacterSelection", ("%d selected %s for login. Sending a validation request to CentralServer to verify this client can use this character", getSUID(), msg.getId().getValueString().c_str())); + ValidateCharacterForLoginMessage vclm(getSUID(), msg.getId()); + ConnectionServer::sendToCentralProcess(vclm); + LOG("TraceCharacterSelection", + ("%d selected %s for login. Sending a validation request to CentralServer to verify this client can use this character", getSUID(), msg.getId().getValueString().c_str())); } @@ -301,10 +293,9 @@ void ClientConnection::handleSelectCharacterMessage(const SelectCharacter& msg) * is in the process of logging in). * Connect the player with the game server. */ -void ClientConnection::handleGameServerForLoginMessage(uint32 serverId) -{ - DEBUG_WARNING(serverId==0,("Got handleGameServerForLoginMessage with serverId=0.\n")); - IGNORE_RETURN( sendToGameServer(serverId) ); +void ClientConnection::handleGameServerForLoginMessage(uint32 serverId) { + DEBUG_WARNING(serverId == 0, ("Got handleGameServerForLoginMessage with serverId=0.\n")); + IGNORE_RETURN(sendToGameServer(serverId)); } //---------------------------------------------------------------------- @@ -314,916 +305,883 @@ void ClientConnection::handleGameServerForLoginMessage(uint32 serverId) * to be validated. Central will reply with a list of permissions. * @see onIdValidated */ -void ClientConnection::handleClientIdMessage(const ClientIdMsg& msg) -{ - //Only check clients that have not been validated. - if (m_hasBeenValidated) - return; +void ClientConnection::handleClientIdMessage(const ClientIdMsg &msg) { + //Only check clients that have not been validated. + if (m_hasBeenValidated) + return; - DEBUG_FATAL(m_hasSelectedCharacter, ("Trying to validate a client who already has a character selected.\n")); - bool result = false; - char sessionId[apiSessionIdWidth]; + DEBUG_FATAL(m_hasSelectedCharacter, ("Trying to validate a client who already has a character selected.\n")); + bool result = false; + StationId apiSuid = 0; + char sessionId[apiSessionIdWidth]; - m_gameBitsToClear = msg.getGameBitsToClear(); + m_gameBitsToClear = msg.getGameBitsToClear(); - if(msg.getTokenSize() > 0) - { - Archive::ByteStream t(msg.getToken(), msg.getTokenSize()); - Archive::ReadIterator ri(t); - KeyShare::Token token(ri); + if (msg.getTokenSize() > 0) { + Archive::ByteStream t(msg.getToken(), msg.getTokenSize()); + Archive::ReadIterator ri(t); + KeyShare::Token token(ri); - if (!ConfigConnectionServer::getValidateStationKey()) - { - // get SUID from token - result = ConnectionServer::decryptToken(token, m_suid, m_isSecure, m_accountName); - } - else - { - result = ConnectionServer::decryptToken(token, sessionId, m_requestedSuid); - } + static const std::string sessURL(ConfigConnectionServer::getSessionURL()); - static const std::string loginTrace("TRACE_LOGIN"); - LOG(loginTrace, ("ClientConnection SUID = %d", m_suid)); + printf("url is %s", sessURL.c_str()); - } - if (result) - { - //check for duplicate login - ClientConnection * oldConnection = ConnectionServer::getClientConnection(m_suid); - if (oldConnection) - { - //There is already someone connected to this cluster with this suid. - LOG("Network", ("SUID %d already logged in, disconnecting client.\n", m_suid)); + if (ConfigConnectionServer::getValidateStationKey() && !sessURL.empty()) { + printf("\nAttempting to test our session...\n"); + webAPI api(sessURL); - ConnectionServer::dropClient(oldConnection, "Already Connected"); + std::string clientIP = getRemoteAddress(); - disconnect(); - return; - } + // add our data + api.addJsonData("session_key", std::string(sessionId)); + api.addJsonData("ip", clientIP); - // verify version - if (ConfigConnectionServer::getValidateClientVersion() && msg.getVersion() != GameNetworkMessage::NetworkVersionId) - { - std::string strSessionId(sessionId, apiSessionIdWidth); - strSessionId += '\0'; + if (api.submit()) { + printf("\tSubmission ok...\n"); + bool status = api.getNullableValue("status"); - const int bufferSize = 255 + apiSessionIdWidth; - char * buffer = new char[bufferSize]; - snprintf(buffer, bufferSize-1, "network version mismatch: got (ip=[%s], sessionId=[%s], version=[%s]), required (version=[%s])", getRemoteAddress().c_str(), strSessionId.c_str(), msg.getVersion().c_str(), GameNetworkMessage::NetworkVersionId.c_str()); - buffer[bufferSize-1] = '\0'; + if (status) { + printf("\tStatus ok....\n"); + apiSuid = api.getNullableValue("user_id"); + int expired = api.getNullableValue("expired"); + std::string apiUser = api.getString("user_name"); + std::string apiIP = api.getString("ip"); - ConnectionServer::dropClient(this, std::string(buffer)); - disconnect(); + if (expired == 0) { + printf("\tNot expired...\n"); + if (apiSuid && apiIP == clientIP) { + printf("\tTrying the decrypt bit...\n"); + m_suid = apiSuid; + result = ConnectionServer::decryptToken(token, m_suid, m_isSecure, m_accountName); + } + } else { + printf("\tExpired token!\n"); + } + } else { + printf("\tStatus bad!...\n"); + } + } else { + printf("\tNo api submit :(\n"); + } + } else { // assume local testing + printf("something isn't right or we're just testing...\n"); + result = ConnectionServer::decryptToken(token, m_suid, m_isSecure, m_accountName); + } - delete[] buffer; + static const std::string loginTrace("TRACE_LOGIN"); + LOG(loginTrace, ("ClientConnection SUID = %d", m_suid)); - return; - } + } + if (result) { + //check for duplicate login + ClientConnection *oldConnection = ConnectionServer::getClientConnection(m_suid); + if (oldConnection) { + //There is already someone connected to this cluster with this suid. + LOG("Network", ("SUID %d already logged in, disconnecting client.\n", m_suid)); - if (ConfigConnectionServer::getValidateStationKey()) - { - SessionApiClient * session = ConnectionServer::getSessionApiClient(); - NOT_NULL(session); - if(session) - { - session->validateClient(this, sessionId); - } - else - { - ConnectionServer::dropClient(this, "SessionApiClient is not available!"); - disconnect(); - } - } - else - { - m_suid = atoi(m_accountName.c_str()); - if (m_suid == 0) - { - std::hash h; - m_suid = h(m_accountName.c_str()); - } - onValidateClient(m_suid, m_accountName, m_isSecure, nullptr, ConfigConnectionServer::getDefaultGameFeatures(), ConfigConnectionServer::getDefaultSubscriptionFeatures(), 0, 0, 0, 0, ConfigConnectionServer::getFakeBuddyPoints()); - } - } - else - { - // They sent us a token that was no good -- either a hack attempt, or - // possibly it was just too old. - LOG("ClientDisconnect", ("SUID %d passed a bad token to the connections erver. Disconnecting.", m_suid)); - disconnect(); - } + ConnectionServer::dropClient(oldConnection, "Already Connected"); + + disconnect(); + return; + } + + // verify version + if (ConfigConnectionServer::getValidateClientVersion() && + msg.getVersion() != GameNetworkMessage::NetworkVersionId) { + std::string strSessionId(sessionId, apiSessionIdWidth); + strSessionId += '\0'; + + const int bufferSize = 255 + apiSessionIdWidth; + char *buffer = new char[bufferSize]; + snprintf(buffer, bufferSize - 1, + "network version mismatch: got (ip=[%s], sessionId=[%s], version=[%s]), required (version=[%s])", + getRemoteAddress().c_str(), strSessionId.c_str(), msg.getVersion().c_str(), + GameNetworkMessage::NetworkVersionId.c_str()); + buffer[bufferSize - 1] = '\0'; + + ConnectionServer::dropClient(this, std::string(buffer)); + disconnect(); + + delete[] buffer; + + return; + } + + if (!m_suid && !ConfigConnectionServer::getValidateStationKey()) { + m_suid = atoi(m_accountName.c_str()); + if (m_suid == 0) { + std::hash h; + m_suid = h(m_accountName.c_str()); + } + } + + onValidateClient(m_suid, m_accountName, m_isSecure, nullptr, + ConfigConnectionServer::getDefaultGameFeatures(), + ConfigConnectionServer::getDefaultSubscriptionFeatures(), 0, 0, 0, 0, + ConfigConnectionServer::getFakeBuddyPoints()); + } else { + // They sent us a token that was no good -- either a hack attempt, or + // possibly it was just too old. + LOG("ClientDisconnect", ("SUID %d passed a bad token to the connections erver. Disconnecting.", m_suid)); + disconnect(); + } } //----------------------------------------------------------------------- -void ClientConnection::onIdValidated(bool canLogin, bool canCreateRegularCharacter, bool canCreateJediCharacter, bool canSkipTutorial, std::vector > const & consumedRewardEvents, std::vector > const & claimedRewardItems) -{ - //@todo start session with station. - //@todo add more permissions to this message as needed. +void ClientConnection::onIdValidated(bool canLogin, bool canCreateRegularCharacter, bool canCreateJediCharacter, + bool canSkipTutorial, + std::vector > const &consumedRewardEvents, + std::vector > const &claimedRewardItems) { + //@todo start session with station. + //@todo add more permissions to this message as needed. - // resume character creation - if (m_pendingCharacterCreate) - { - if (!m_pendingCharacterCreate->getUseNewbieTutorial() && !canSkipTutorial) - { - LOG("TraceCharacterCreation", ("%d failed character creation. The client is not allowed to skip the tutorial", getSUID())); - // This is probably a hack attempt, because the Client was already told they couldn't skip the tutorial - LOG("ClientDisconnect", ("Disconnecting %u because they tried to skip the tutorial without permission.\n",getSUID())); - disconnect(); - } - else if (m_pendingCharacterCreate->getJedi() && !canCreateJediCharacter) - { - LOG("TraceCharacterCreation", ("%d failed character creation. The request character type was Jedi, but this client cannot create a Jedi character", getSUID())); - // This is probably a hack attempt, because the Client was already told they couldn't create a character - LOG("ClientDisconnect", ("Disconnecting %u because they tried to create a Jedi character without permission.\n",getSUID())); - disconnect(); - } - else if (!m_pendingCharacterCreate->getJedi() && !canCreateRegularCharacter) - { - LOG("TraceCharacterCreation", ("%d failed character creation. The client is not allowed to create any characters", getSUID())); - // This is probably a hack attempt, because the Client was already told they couldn't create a character - LOG("ClientDisconnect", ("Disconnecting %u because they tried to create a regular character without permission.\n",getSUID())); - disconnect(); - } - else - { - ConnectionServer::sendToCentralProcess(*m_pendingCharacterCreate); - LOG("TraceCharacterCreation", ("%d character creation request sent to CentralServer", getSUID())); + // resume character creation + if (m_pendingCharacterCreate) { + if (!m_pendingCharacterCreate->getUseNewbieTutorial() && !canSkipTutorial) { + LOG("TraceCharacterCreation", + ("%d failed character creation. The client is not allowed to skip the tutorial", getSUID())); + // This is probably a hack attempt, because the Client was already told they couldn't skip the tutorial + LOG("ClientDisconnect", + ("Disconnecting %u because they tried to skip the tutorial without permission.\n", getSUID())); + disconnect(); + } else if (m_pendingCharacterCreate->getJedi() && !canCreateJediCharacter) { + LOG("TraceCharacterCreation", + ("%d failed character creation. The request character type was Jedi, but this client cannot create a Jedi character", getSUID())); + // This is probably a hack attempt, because the Client was already told they couldn't create a character + LOG("ClientDisconnect", + ("Disconnecting %u because they tried to create a Jedi character without permission.\n", getSUID())); + disconnect(); + } else if (!m_pendingCharacterCreate->getJedi() && !canCreateRegularCharacter) { + LOG("TraceCharacterCreation", + ("%d failed character creation. The client is not allowed to create any characters", getSUID())); + // This is probably a hack attempt, because the Client was already told they couldn't create a character + LOG("ClientDisconnect", + ("Disconnecting %u because they tried to create a regular character without permission.\n", getSUID())); + disconnect(); + } else { + ConnectionServer::sendToCentralProcess(*m_pendingCharacterCreate); + LOG("TraceCharacterCreation", ("%d character creation request sent to CentralServer", getSUID())); - m_hasRequestedCharacterCreate = true; - } + m_hasRequestedCharacterCreate = true; + } - delete m_pendingCharacterCreate; - m_pendingCharacterCreate = nullptr; + delete m_pendingCharacterCreate; + m_pendingCharacterCreate = nullptr; - return; - } + return; + } - // Save lists of claimed rewards, which won't be used again until later in the login sequence - m_consumedRewardEvents = consumedRewardEvents; - m_claimedRewardItems = claimedRewardItems; + // Save lists of claimed rewards, which won't be used again until later in the login sequence + m_consumedRewardEvents = consumedRewardEvents; + m_claimedRewardItems = claimedRewardItems; - int level=0; - if (AdminAccountManager::isAdminAccount(Unicode::toLower(getAccountName()),level) && (level !=0)) // Note: not checking IP, so that owners of god accounts can create characters to play from home without having to erase the characters they use for work - { - canLogin = true; - canCreateRegularCharacter = true; - canSkipTutorial = true; - m_isAdminAccount = true; - } + int level = 0; + if (AdminAccountManager::isAdminAccount(Unicode::toLower(getAccountName()), level) && (level != + 0)) // Note: not checking IP, so that owners of god accounts can create characters to play from home without having to erase the characters they use for work + { + canLogin = true; + canCreateRegularCharacter = true; + canSkipTutorial = true; + m_isAdminAccount = true; + } - ClientPermissionsMessage c(canLogin, canCreateRegularCharacter, canCreateJediCharacter, canSkipTutorial); - send(c, true); + ClientPermissionsMessage c(canLogin, canCreateRegularCharacter, canCreateJediCharacter, canSkipTutorial); + send(c, true); - DEBUG_REPORT_LOG(true,("Permissions for %lu:\n",getSUID())); - DEBUG_REPORT_LOG(canLogin,("\tcanLogin\n")); - DEBUG_REPORT_LOG(canCreateRegularCharacter,("\tcanCreateRegularCharacter\n")); - DEBUG_REPORT_LOG(canCreateJediCharacter,("\tcanCreateJediCharacter\n")); - DEBUG_REPORT_LOG(canSkipTutorial,("\tcanSkipTutorial\n")); - DEBUG_REPORT_LOG(!(canLogin || canCreateRegularCharacter || canCreateJediCharacter || canSkipTutorial),("\tnone\n")); + DEBUG_REPORT_LOG(true, ("Permissions for %lu:\n", getSUID())); + DEBUG_REPORT_LOG(canLogin, ("\tcanLogin\n")); + DEBUG_REPORT_LOG(canCreateRegularCharacter, ("\tcanCreateRegularCharacter\n")); + DEBUG_REPORT_LOG(canCreateJediCharacter, ("\tcanCreateJediCharacter\n")); + DEBUG_REPORT_LOG(canSkipTutorial, ("\tcanSkipTutorial\n")); + DEBUG_REPORT_LOG(!(canLogin || canCreateRegularCharacter || canCreateJediCharacter || canSkipTutorial), + ("\tnone\n")); - if (canLogin) - { - m_hasBeenValidated = true; - m_canCreateRegularCharacter = canCreateRegularCharacter; - m_canCreateJediCharacter = canCreateJediCharacter; - m_canSkipTutorial = canSkipTutorial; - } - else - { - LOG("TRACE_LOGIN", ("%d does not have permissions to log in", getSUID())); - LOG("ClientDisconnect", ("Client (SUID %u) does not have permissions to log in. Disconnecting.", getSUID())); - disconnect(); - } + if (canLogin) { + m_hasBeenValidated = true; + m_canCreateRegularCharacter = canCreateRegularCharacter; + m_canCreateJediCharacter = canCreateJediCharacter; + m_canSkipTutorial = canSkipTutorial; + } else { + LOG("TRACE_LOGIN", ("%d does not have permissions to log in", getSUID())); + LOG("ClientDisconnect", ("Client (SUID %u) does not have permissions to log in. Disconnecting.", getSUID())); + disconnect(); + } } //----------------------------------------------------------------------- -void ClientConnection::onConnectionClosed() -{ - ServerConnection::onConnectionClosed(); - static MessageConnectionCallback m("ClientConnectionClosed"); - emitMessage(m); +void ClientConnection::onConnectionClosed() { + ServerConnection::onConnectionClosed(); + static MessageConnectionCallback m("ClientConnectionClosed"); + emitMessage(m); - LOG("TRACE_LOGIN", ("%d closed connection", getSUID())); - if (m_client) - { - if (!m_client->hasBeenKicked()) - { - LOG("CustomerService", ("Login:%s Dropped Reason: Client Dropped Connection. Character: %s (%s). Play time: %s. Active play time: %s", describeAccount(this).c_str(), getCharacterName().c_str(), getCharacterId().getValueString().c_str(), getPlayTimeDuration().c_str(), getActivePlayTimeDuration().c_str())); - } - ChatServerConnection * chatConnection = m_client->getChatConnection(); - if(chatConnection) - { - ChatDisconnectAvatar m(m_characterId); - chatConnection->send(m, true); - } - // We cannot do this here, as this connection will be deleted on - // return from this function already. - //m_client->kick(); - } + LOG("TRACE_LOGIN", ("%d closed connection", getSUID())); + if (m_client) { + if (!m_client->hasBeenKicked()) { + LOG("CustomerService", + ("Login:%s Dropped Reason: Client Dropped Connection. Character: %s (%s). Play time: %s. Active play time: %s", describeAccount( + this).c_str(), getCharacterName().c_str(), getCharacterId().getValueString().c_str(), getPlayTimeDuration().c_str(), getActivePlayTimeDuration().c_str())); + } + ChatServerConnection *chatConnection = m_client->getChatConnection(); + if (chatConnection) { + ChatDisconnectAvatar m(m_characterId); + chatConnection->send(m, true); + } + // We cannot do this here, as this connection will be deleted on + // return from this function already. + //m_client->kick(); + } } //----------------------------------------------------------------------- -void ClientConnection::onConnectionOpened() -{ - ServerConnection::onConnectionOpened(); - static MessageConnectionCallback m("ClientConnectionOpened"); - emitMessage(m); - setOverflowLimit(ConfigConnectionServer::getClientOverflowLimit()); +void ClientConnection::onConnectionOpened() { + ServerConnection::onConnectionOpened(); + static MessageConnectionCallback m("ClientConnectionOpened"); + emitMessage(m); + setOverflowLimit(ConfigConnectionServer::getClientOverflowLimit()); } //----------------------------------------------------------------------- -void ClientConnection::onConnectionOverflowing (const unsigned int bytesPending) -{ - char errbuf[1024]; - snprintf(errbuf, sizeof(errbuf), "Connection overflow from server to client, %d bytes. Disconnected.", bytesPending); - LOG("Network", ("Disconnect: Client connection overflowing. %d bytes pending", bytesPending)); +void ClientConnection::onConnectionOverflowing(const unsigned int bytesPending) { + char errbuf[1024]; + snprintf(errbuf, sizeof(errbuf), "Connection overflow from server to client, %d bytes. Disconnected.", + bytesPending); + LOG("Network", ("Disconnect: Client connection overflowing. %d bytes pending", bytesPending)); - std::vector >::const_iterator i; - for(i = m_pendingPackets.begin(); i != m_pendingPackets.end(); ++i) - { - LOG("Network", ("Overflow packets this frame: [%s] %d bytes", i->first.c_str(), i->second)); - } + std::vector >::const_iterator i; + for (i = m_pendingPackets.begin(); i != m_pendingPackets.end(); ++i) { + LOG("Network", ("Overflow packets this frame: [%s] %d bytes", i->first.c_str(), i->second)); + } // ErrorMessage err(name, desc, false); // send(err, true); - WARNING(true, (errbuf)); - LOG("ClientDisconnect", ("About to drop client (character) %s because the connection is overflowing\n", m_characterName.c_str())); + WARNING(true, (errbuf)); + LOG("ClientDisconnect", + ("About to drop client (character) %s because the connection is overflowing\n", m_characterName.c_str())); - snprintf(errbuf, sizeof(errbuf)-1, "Connection Overflow (bytes pending=%u)", bytesPending); - errbuf[sizeof(errbuf)-1] = '\0'; - ConnectionServer::dropClient(this, std::string(errbuf)); + snprintf(errbuf, sizeof(errbuf) - 1, "Connection Overflow (bytes pending=%u)", bytesPending); + errbuf[sizeof(errbuf) - 1] = '\0'; + ConnectionServer::dropClient(this, std::string(errbuf)); } //----------------------------------------------------------------------- -bool ClientConnection::checkSpamLimit(unsigned int messageSize) -{ - if (!ConfigConnectionServer::getSpamLimitEnabled()) - return true; +bool ClientConnection::checkSpamLimit(unsigned int messageSize) { + if (!ConfigConnectionServer::getSpamLimitEnabled()) + return true; - unsigned long curTimeMs = Clock::timeMs(); - if (m_receiveLastTimeMs) - { - ++m_receiveHistoryPackets; - m_receiveHistoryBytes += messageSize; - m_receiveHistoryMs += curTimeMs-m_receiveLastTimeMs; + unsigned long curTimeMs = Clock::timeMs(); + if (m_receiveLastTimeMs) { + ++m_receiveHistoryPackets; + m_receiveHistoryBytes += messageSize; + m_receiveHistoryMs += curTimeMs - m_receiveLastTimeMs; - // rescale the history information if we've exceeded the reset time; this - // must be done before the spam check below or else we may run into overflow - // issues because m_receiveHistoryMs could be pretty large if we haven't - // received anything from the client for a while - while (m_receiveHistoryMs > ConfigConnectionServer::getSpamLimitResetTimeMs()) - { - ++m_countSpamLimitResetTime; + // rescale the history information if we've exceeded the reset time; this + // must be done before the spam check below or else we may run into overflow + // issues because m_receiveHistoryMs could be pretty large if we haven't + // received anything from the client for a while + while (m_receiveHistoryMs > ConfigConnectionServer::getSpamLimitResetTimeMs()) { + ++m_countSpamLimitResetTime; - unsigned int resetScale = ConfigConnectionServer::getSpamLimitResetScaleFactor(); - m_receiveHistoryMs /= resetScale; - m_receiveHistoryBytes /= resetScale; - m_receiveHistoryPackets /= resetScale; - } + unsigned int resetScale = ConfigConnectionServer::getSpamLimitResetScaleFactor(); + m_receiveHistoryMs /= resetScale; + m_receiveHistoryBytes /= resetScale; + m_receiveHistoryPackets /= resetScale; + } - // check for exceeding limits, but wait for at least - // one reset cycle so that there has been enough - // elapsed time, so we won't get a false positive - if (m_countSpamLimitResetTime) - { - if (m_receiveHistoryBytes >= m_receiveHistoryMs*ConfigConnectionServer::getSpamLimitBytesPerSec()/1000) - { - LOG("Network", ("Client %s disconnected for exceeding bytes/sec limit (bytes=%u, time=%lums)\n", getCharacterId().getValueString().c_str(), m_receiveHistoryBytes, m_receiveHistoryMs)); - return false; - } - if (m_receiveHistoryPackets >= m_receiveHistoryMs*ConfigConnectionServer::getSpamLimitPacketsPerSec()/1000) - { - LOG("Network", ("Client %s disconnected for exceeding packets/sec limit (packets=%u, time=%lums)\n", getCharacterId().getValueString().c_str(), m_receiveHistoryPackets, m_receiveHistoryMs)); - return false; - } - } - } - m_receiveLastTimeMs = curTimeMs; - return true; + // check for exceeding limits, but wait for at least + // one reset cycle so that there has been enough + // elapsed time, so we won't get a false positive + if (m_countSpamLimitResetTime) { + if (m_receiveHistoryBytes >= + m_receiveHistoryMs * ConfigConnectionServer::getSpamLimitBytesPerSec() / 1000) { + LOG("Network", + ("Client %s disconnected for exceeding bytes/sec limit (bytes=%u, time=%lums)\n", getCharacterId().getValueString().c_str(), m_receiveHistoryBytes, m_receiveHistoryMs)); + return false; + } + if (m_receiveHistoryPackets >= + m_receiveHistoryMs * ConfigConnectionServer::getSpamLimitPacketsPerSec() / 1000) { + LOG("Network", + ("Client %s disconnected for exceeding packets/sec limit (packets=%u, time=%lums)\n", getCharacterId().getValueString().c_str(), m_receiveHistoryPackets, m_receiveHistoryMs)); + return false; + } + } + } + m_receiveLastTimeMs = curTimeMs; + return true; } //----------------------------------------------------------------------- -void ClientConnection::onReceive(const Archive::ByteStream & message) -{ - try - { - if (!checkSpamLimit(message.getSize())) - { - ConnectionServer::dropClient(this, "Spam Detected"); - return; - } +void ClientConnection::onReceive(const Archive::ByteStream &message) { + try { + if (!checkSpamLimit(message.getSize())) { + ConnectionServer::dropClient(this, "Spam Detected"); + return; + } - unsigned long curTimeMs = Clock::timeMs(); - if (m_sendLastTimeMs + std::min(gs_receiveDelayMaxMs, static_cast(Clock::frameTime()*1000.0f)) < curTimeMs) - { - static HeartBeat h; - send(h, false); - } + unsigned long curTimeMs = Clock::timeMs(); + if (m_sendLastTimeMs + + std::min(gs_receiveDelayMaxMs, static_cast(Clock::frameTime() * 1000.0f)) < curTimeMs) { + static HeartBeat h; + send(h, false); + } - Archive::ReadIterator ri = message.begin(); - GameNetworkMessage m(ri); - ri = message.begin(); - - const uint32 messageType = m.getType(); + Archive::ReadIterator ri = message.begin(); + GameNetworkMessage m(ri); + ri = message.begin(); - //Clients with a selected character get routed to a game server. - //@todo check for filtering out bad messages. - if (m_hasSelectedCharacter) - { - // if it is a chat message, send it directly to the chat server - switch(messageType) { - case constcrc("ChatAddFriend") : - case constcrc("ChatAddModeratorToRoom") : - case constcrc("ChatBanAvatarFromRoom") : - case constcrc("ChatCreateRoom") : - case constcrc("ChatDeletePersistentMessage") : - case constcrc("ChatDeleteAllPersistentMessages") : - case constcrc("ChatDestroyRoom") : - case constcrc("ChatInstantMessageToCharacter") : - case constcrc("ChatInviteAvatarToRoom") : - case constcrc("ChatKickAvatarFromRoom") : - case constcrc("ChatRemoveAvatarFromRoom") : - case constcrc("ChatRemoveFriend") : - case constcrc("ChatRemoveModeratorFromRoom") : - case constcrc("ChatRequestPersistentMessage") : - case constcrc("ChatRequestRoomList") : - case constcrc("ChatSendToRoom") : - case constcrc("ChatUninviteFromRoom") : - case constcrc("ChatUnbanAvatarFromRoom") : - case constcrc("VerifyPlayerNameMessage") : - { - DEBUG_REPORT_LOG(true, ("ConnServ: ClientConnection::onReceive()\n")); + const uint32 messageType = m.getType(); - NOT_NULL(m_client); - if(m_client) - { - static std::vector v; - v.clear(); - v.push_back(m_client->getNetworkId()); - GameClientMessage gcm(v, true, ri); - if(m_client->getChatConnection()) - { - m_client->getChatConnection()->send(gcm , true); - } - else - { - // defer chat messages until a server is back online - Archive::ByteStream bs; - m.pack(bs); - m_client->deferChatMessage(bs); - } - } - else - { - ConnectionServer::dropClient(this, "m_client is nullptr while receiving a message!"); - disconnect(); - } - break; - } - // ChatEnterRoom and ChatEnterRoomById needs to go to the game server to determine - // if the character is not allowed to enter the room because of game rule restrictions; - // only if that test pass do we forward the message on to the chat server to request - // to enter the room - case constcrc("ChatEnterRoom") : - case constcrc("ChatEnterRoomById") : - { - NOT_NULL(m_client); + //Clients with a selected character get routed to a game server. + //@todo check for filtering out bad messages. + if (m_hasSelectedCharacter) { + // if it is a chat message, send it directly to the chat server + switch (messageType) { + case constcrc("ChatAddFriend") : + case constcrc("ChatAddModeratorToRoom") : + case constcrc("ChatBanAvatarFromRoom") : + case constcrc("ChatCreateRoom") : + case constcrc("ChatDeletePersistentMessage") : + case constcrc("ChatDeleteAllPersistentMessages") : + case constcrc("ChatDestroyRoom") : + case constcrc("ChatInstantMessageToCharacter") : + case constcrc("ChatInviteAvatarToRoom") : + case constcrc("ChatKickAvatarFromRoom") : + case constcrc("ChatRemoveAvatarFromRoom") : + case constcrc("ChatRemoveFriend") : + case constcrc("ChatRemoveModeratorFromRoom") : + case constcrc("ChatRequestPersistentMessage") : + case constcrc("ChatRequestRoomList") : + case constcrc("ChatSendToRoom") : + case constcrc("ChatUninviteFromRoom") : + case constcrc("ChatUnbanAvatarFromRoom") : + case constcrc("VerifyPlayerNameMessage") : { + DEBUG_REPORT_LOG(true, ("ConnServ: ClientConnection::onReceive()\n")); - unsigned int sequence; - std::string roomName; + NOT_NULL(m_client); + if (m_client) { + static std::vector v; + v.clear(); + v.push_back(m_client->getNetworkId()); + GameClientMessage gcm(v, true, ri); + if (m_client->getChatConnection()) { + m_client->getChatConnection()->send(gcm, true); + } else { + // defer chat messages until a server is back online + Archive::ByteStream bs; + m.pack(bs); + m_client->deferChatMessage(bs); + } + } else { + ConnectionServer::dropClient(this, "m_client is nullptr while receiving a message!"); + disconnect(); + } + break; + } + // ChatEnterRoom and ChatEnterRoomById needs to go to the game server to determine + // if the character is not allowed to enter the room because of game rule restrictions; + // only if that test pass do we forward the message on to the chat server to request + // to enter the room + case constcrc("ChatEnterRoom") : + case constcrc("ChatEnterRoomById") : { + NOT_NULL(m_client); - Archive::ReadIterator cri = message.begin(); + unsigned int sequence; + std::string roomName; - if (messageType == constcrc("ChatEnterRoom")) - { - ChatEnterRoom const cer(cri); - sequence = cer.getSequence(); - roomName = cer.getRoomName(); - } - else - { - ChatEnterRoomById const cerbi(cri); - sequence = cerbi.getSequence(); - roomName = cerbi.getRoomName(); - } + Archive::ReadIterator cri = message.begin(); - if(m_client && m_client->getGameConnection()) - { - if (m_pendingChatEnterRoomRequests.count(sequence) == 0) - { - GenericValueTypeMessage, unsigned int> > const cervr( - "ChatEnterRoomValidationRequest", - std::make_pair( - std::make_pair(m_client->getNetworkId(), roomName), - sequence)); + if (messageType == constcrc("ChatEnterRoom")) { + ChatEnterRoom const cer(cri); + sequence = cer.getSequence(); + roomName = cer.getRoomName(); + } else { + ChatEnterRoomById const cerbi(cri); + sequence = cerbi.getSequence(); + roomName = cerbi.getRoomName(); + } - m_client->getGameConnection()->send(cervr, true); + if (m_client && m_client->getGameConnection()) { + if (m_pendingChatEnterRoomRequests.count(sequence) == 0) { + GenericValueTypeMessage, unsigned int> > const cervr( + "ChatEnterRoomValidationRequest", + std::make_pair( + std::make_pair(m_client->getNetworkId(), roomName), + sequence)); - // queue up request until game server responds - static std::vector v; - v.clear(); - v.push_back(m_client->getNetworkId()); - m_pendingChatEnterRoomRequests[sequence] = new GameClientMessage(v, true, ri); - } - } - else - { - // send back response to client saying game server not available + m_client->getGameConnection()->send(cervr, true); - // the client only cares about sequence and result when it's a failure - ChatOnEnteredRoom fail(sequence, SWG_CHAT_ERR_NO_GAME_SERVER, 0, ChatAvatarId()); - send(fail, true); - } - - break; - } - // ChatQueryRoom needs to go to the game server to determine if the character is - // not allowed to query the room because of game rule restrictions; only if that - // test pass do we forward the message on to the chat server for completion - case constcrc("ChatQueryRoom") : - { - NOT_NULL(m_client); + // queue up request until game server responds + static std::vector v; + v.clear(); + v.push_back(m_client->getNetworkId()); + m_pendingChatEnterRoomRequests[sequence] = new GameClientMessage(v, true, ri); + } + } else { + // send back response to client saying game server not available - Archive::ReadIterator cri = message.begin(); - ChatQueryRoom cqr(cri); + // the client only cares about sequence and result when it's a failure + ChatOnEnteredRoom fail(sequence, SWG_CHAT_ERR_NO_GAME_SERVER, 0, ChatAvatarId()); + send(fail, true); + } - if(m_client && m_client->getGameConnection()) - { - if (m_pendingChatQueryRoomRequests.count(cqr.getSequence()) == 0) - { - GenericValueTypeMessage, unsigned int> > const cqrvr( - "ChatQueryRoomValidationRequest", - std::make_pair( - std::make_pair(m_client->getNetworkId(), cqr.getRoomName()), - cqr.getSequence())); + break; + } + // ChatQueryRoom needs to go to the game server to determine if the character is + // not allowed to query the room because of game rule restrictions; only if that + // test pass do we forward the message on to the chat server for completion + case constcrc("ChatQueryRoom") : { + NOT_NULL(m_client); - m_client->getGameConnection()->send(cqrvr, true); + Archive::ReadIterator cri = message.begin(); + ChatQueryRoom cqr(cri); - // queue up request until game server responds - static std::vector v; - v.clear(); - v.push_back(m_client->getNetworkId()); - m_pendingChatQueryRoomRequests[cqr.getSequence()] = new GameClientMessage(v, true, ri); - } - } - break; - } - // ChatInviteGroupToRoom needs to go to the game server to get group information - case constcrc("ChatInviteGroupToRoom") : - { - NOT_NULL(m_client); - if(m_client) - { - if (m_client->getGameConnection()) - { - static std::vector v; - v.clear(); - v.push_back(m_client->getNetworkId()); - GameClientMessage gcm(v, true, ri); - m_client->getGameConnection()->send(gcm, true); - } - else - { - // defer chat messages until a server is back online - Archive::ByteStream bs; - m.pack(bs); - m_client->deferChatMessage(bs); - } - } - else - { - ConnectionServer::dropClient(this, "m_client is nullptr while receiving a message!"); - disconnect(); - } - break; - } - // ChatPersistentMessageToServer may need to be passed off to the game server for guild or citizens messages - case constcrc("ChatPersistentMessageToServer") : - { - NOT_NULL(m_client); - if(m_client) - { - static std::vector v; - v.clear(); - v.push_back(m_client->getNetworkId()); + if (m_client && m_client->getGameConnection()) { + if (m_pendingChatQueryRoomRequests.count(cqr.getSequence()) == 0) { + GenericValueTypeMessage, unsigned int> > const cqrvr( + "ChatQueryRoomValidationRequest", + std::make_pair( + std::make_pair(m_client->getNetworkId(), cqr.getRoomName()), + cqr.getSequence())); - Archive::ReadIterator cri = message.begin(); - ChatPersistentMessageToServer chat(cri); - std::string const &toName = chat.getToCharacterName().name; - if (!_stricmp(toName.c_str(), "guild") || !_strnicmp(toName.c_str(), "guild ", 6) || !_stricmp(toName.c_str(), "citizens")) - { - if (m_client->getGameConnection()) - { - GameClientMessage gcm(v, true, ri); - m_client->getGameConnection()->send(gcm, true); - } - } - else - { - if (m_client->getChatConnection()) - { - GameClientMessage gcm(v, true, ri); - m_client->getChatConnection()->send(gcm, true); - } - else - { - // defer chat messages until a server is back online - Archive::ByteStream bs; - m.pack(bs); - m_client->deferChatMessage(bs); - } - } - } - else - { - ConnectionServer::dropClient(this, "m_client is nullptr while receiving a message!"); - disconnect(); - } - break; - } - // if it is a cs message, send it directly to the cs server - case constcrc("ConnectPlayerMessage") : - case constcrc("DisconnectPlayerMessage") : - case constcrc("CreateTicketMessage") : - case constcrc("AppendCommentMessage") : - case constcrc("CancelTicketMessage") : - case constcrc("GetTicketsMessage") : - case constcrc("GetCommentsMessage") : - case constcrc("SearchKnowledgeBaseMessage") : - case constcrc("GetArticleMessage") : - case constcrc("RequestCategoriesMessage") : - case constcrc("NewTicketActivityMessage") : - { - NOT_NULL(m_client); - if(m_client) - { - CustomerServiceConnection *customerServiceConnection = m_client->getCustomerServiceConnection(); + m_client->getGameConnection()->send(cqrvr, true); - //DEBUG_REPORT_LOG(true, ("CONSRV::CS - suid: %i\n", getSUID())); + // queue up request until game server responds + static std::vector v; + v.clear(); + v.push_back(m_client->getNetworkId()); + m_pendingChatQueryRoomRequests[cqr.getSequence()] = new GameClientMessage(v, true, ri); + } + } + break; + } + // ChatInviteGroupToRoom needs to go to the game server to get group information + case constcrc("ChatInviteGroupToRoom") : { + NOT_NULL(m_client); + if (m_client) { + if (m_client->getGameConnection()) { + static std::vector v; + v.clear(); + v.push_back(m_client->getNetworkId()); + GameClientMessage gcm(v, true, ri); + m_client->getGameConnection()->send(gcm, true); + } else { + // defer chat messages until a server is back online + Archive::ByteStream bs; + m.pack(bs); + m_client->deferChatMessage(bs); + } + } else { + ConnectionServer::dropClient(this, "m_client is nullptr while receiving a message!"); + disconnect(); + } + break; + } + // ChatPersistentMessageToServer may need to be passed off to the game server for guild or citizens messages + case constcrc("ChatPersistentMessageToServer") : { + NOT_NULL(m_client); + if (m_client) { + static std::vector v; + v.clear(); + v.push_back(m_client->getNetworkId()); - if (customerServiceConnection != nullptr) - { - static std::vector v; - v.clear(); - v.push_back(m_client->getNetworkId()); + Archive::ReadIterator cri = message.begin(); + ChatPersistentMessageToServer chat(cri); + std::string const &toName = chat.getToCharacterName().name; + if (!_stricmp(toName.c_str(), "guild") || !_strnicmp(toName.c_str(), "guild ", 6) || + !_stricmp(toName.c_str(), "citizens")) { + if (m_client->getGameConnection()) { + GameClientMessage gcm(v, true, ri); + m_client->getGameConnection()->send(gcm, true); + } + } else { + if (m_client->getChatConnection()) { + GameClientMessage gcm(v, true, ri); + m_client->getChatConnection()->send(gcm, true); + } else { + // defer chat messages until a server is back online + Archive::ByteStream bs; + m.pack(bs); + m_client->deferChatMessage(bs); + } + } + } else { + ConnectionServer::dropClient(this, "m_client is nullptr while receiving a message!"); + disconnect(); + } + break; + } + // if it is a cs message, send it directly to the cs server + case constcrc("ConnectPlayerMessage") : + case constcrc("DisconnectPlayerMessage") : + case constcrc("CreateTicketMessage") : + case constcrc("AppendCommentMessage") : + case constcrc("CancelTicketMessage") : + case constcrc("GetTicketsMessage") : + case constcrc("GetCommentsMessage") : + case constcrc("SearchKnowledgeBaseMessage") : + case constcrc("GetArticleMessage") : + case constcrc("RequestCategoriesMessage") : + case constcrc("NewTicketActivityMessage") : { + NOT_NULL(m_client); + if (m_client) { + CustomerServiceConnection *customerServiceConnection = m_client->getCustomerServiceConnection(); - // TODO: this shit could be made into a template - switch (messageType) { - case constcrc("ConnectPlayerMessage") : { - ConnectPlayerMessage message(ri); - message.setStationId(getSUID()); + //DEBUG_REPORT_LOG(true, ("CONSRV::CS - suid: %i\n", getSUID())); - GameClientMessage gcm(v, true, message); - customerServiceConnection->send(gcm , true); - break; - } - case constcrc("CreateTicketMessage") : { - CreateTicketMessage message(ri); - message.setStationId(getSUID()); + if (customerServiceConnection != nullptr) { + static std::vector v; + v.clear(); + v.push_back(m_client->getNetworkId()); - GameClientMessage gcm(v, true, message); - customerServiceConnection->send(gcm , true); - break; - } - case constcrc("AppendCommentMessage") : { - AppendCommentMessage message(ri); - message.setStationId(getSUID()); + // TODO: this shit could be made into a template + switch (messageType) { + case constcrc("ConnectPlayerMessage") : { + ConnectPlayerMessage message(ri); + message.setStationId(getSUID()); - GameClientMessage gcm(v, true, message); - customerServiceConnection->send(gcm , true); - break; - } - case constcrc("CancelTicketMessage") : { - CancelTicketMessage message(ri); - message.setStationId(getSUID()); + GameClientMessage gcm(v, true, message); + customerServiceConnection->send(gcm, true); + break; + } + case constcrc("CreateTicketMessage") : { + CreateTicketMessage message(ri); + message.setStationId(getSUID()); - GameClientMessage gcm(v, true, message); - customerServiceConnection->send(gcm , true); - break; - } - case constcrc("GetTicketsMessage") : { - GetTicketsMessage message(ri); - message.setStationId(getSUID()); + GameClientMessage gcm(v, true, message); + customerServiceConnection->send(gcm, true); + break; + } + case constcrc("AppendCommentMessage") : { + AppendCommentMessage message(ri); + message.setStationId(getSUID()); - GameClientMessage gcm(v, true, message); - customerServiceConnection->send(gcm , true); - break; - } - case constcrc("NewTicketActivityMessage") : { - NewTicketActivityMessage message(ri); - message.setStationId(getSUID()); + GameClientMessage gcm(v, true, message); + customerServiceConnection->send(gcm, true); + break; + } + case constcrc("CancelTicketMessage") : { + CancelTicketMessage message(ri); + message.setStationId(getSUID()); - GameClientMessage gcm(v, true, message); - customerServiceConnection->send(gcm , true); - break; - } - default : { - GameClientMessage gcm(v, true, ri); - customerServiceConnection->send(gcm , true); - break; - } - } - } - } - else - { - ConnectionServer::dropClient(this, "m_client is nullptr while receiving a message!"); - disconnect(); - } - break; - } - case constcrc("28afefcc187a11dc888b001") : // obfuscation for ClientInactivityMessage message - { - GenericValueTypeMessage msg(ri); + GameClientMessage gcm(v, true, message); + customerServiceConnection->send(gcm, true); + break; + } + case constcrc("GetTicketsMessage") : { + GetTicketsMessage message(ri); + message.setStationId(getSUID()); - if (m_hasBeenValidated && m_sessionValidated) - { - // client went inactive - if (msg.getValue()) - { - if (m_lastActiveTime > 0) - { - // record the amount of active time - m_activePlayTimeDuration += static_cast(::time(nullptr) - m_lastActiveTime); + GameClientMessage gcm(v, true, message); + customerServiceConnection->send(gcm, true); + break; + } + case constcrc("NewTicketActivityMessage") : { + NewTicketActivityMessage message(ri); + message.setStationId(getSUID()); - // tell Session to stop recording play time for the character - if (ConnectionServer::getSessionApiClient() && ConfigConnectionServer::getSessionRecordPlayTime()) - { - LOG("CustomerService", ("Login:%s calling SessionStopPlay() for %s/%s/%s (%s). Active play time: %s", ClientConnection::describeAccount(this).c_str(), this->getSessionId().c_str(), ConfigConnectionServer::getClusterName(), this->getCharacterName().c_str(), this->getCharacterId().getValueString().c_str(), this->getCurrentActivePlayTimeDuration().c_str())); - ConnectionServer::getSessionApiClient()->stopPlay(*this); - } + GameClientMessage gcm(v, true, message); + customerServiceConnection->send(gcm, true); + break; + } + default : { + GameClientMessage gcm(v, true, ri); + customerServiceConnection->send(gcm, true); + break; + } + } + } + } else { + ConnectionServer::dropClient(this, "m_client is nullptr while receiving a message!"); + disconnect(); + } + break; + } + case constcrc("28afefcc187a11dc888b001") : // obfuscation for ClientInactivityMessage message + { + GenericValueTypeMessage msg(ri); - // client is no longer active; this needs to be set after the LOG() statement - // above because getCurrentActivePlayTimeDuration() uses m_lastActiveTime - m_lastActiveTime = 0; + if (m_hasBeenValidated && m_sessionValidated) { + // client went inactive + if (msg.getValue()) { + if (m_lastActiveTime > 0) { + // record the amount of active time + m_activePlayTimeDuration += static_cast(::time(nullptr) - + m_lastActiveTime); - // update the play time info on the game server - sendPlayTimeInfoToGameServer(); + // tell Session to stop recording play time for the character + if (ConnectionServer::getSessionApiClient() && + ConfigConnectionServer::getSessionRecordPlayTime()) { + LOG("CustomerService", + ("Login:%s calling SessionStopPlay() for %s/%s/%s (%s). Active play time: %s", ClientConnection::describeAccount( + this).c_str(), this->getSessionId().c_str(), ConfigConnectionServer::getClusterName(), this->getCharacterName().c_str(), this->getCharacterId().getValueString().c_str(), this->getCurrentActivePlayTimeDuration().c_str())); + ConnectionServer::getSessionApiClient()->stopPlay(*this); + } - // drop inactive character - if (ConfigConnectionServer::getDisconnectOnInactive()) - { - LOG("ClientDisconnect", ("Disconnecting %u because the player was inactive for too long.",getSUID())); - ConnectionServer::dropClient(this, "Client inactivity"); - disconnect(); - } - else if (ConfigConnectionServer::getDisconnectFreeTrialOnInactive() && ((m_featureBitsSubscription & ClientSubscriptionFeature::Base) == 0)) - { - LOG("ClientDisconnect", ("Disconnecting (free trial) %u because the player was inactive for too long.",getSUID())); - ConnectionServer::dropClient(this, "Client inactivity (free trial)"); - disconnect(); - } - } - } - // client went active - else - { - if (m_lastActiveTime == 0) - { - // record the time client went active - m_lastActiveTime = ::time(nullptr); + // client is no longer active; this needs to be set after the LOG() statement + // above because getCurrentActivePlayTimeDuration() uses m_lastActiveTime + m_lastActiveTime = 0; - // tell Session to start recording play time for the character - if (ConnectionServer::getSessionApiClient() && ConfigConnectionServer::getSessionRecordPlayTime()) - { - LOG("CustomerService", ("Login:%s calling SessionStartPlay() for %s/%s/%s (%s)", ClientConnection::describeAccount(this).c_str(), this->getSessionId().c_str(), ConfigConnectionServer::getClusterName(), this->getCharacterName().c_str(), this->getCharacterId().getValueString().c_str())); - ConnectionServer::getSessionApiClient()->startPlay(*this); - } + // update the play time info on the game server + sendPlayTimeInfoToGameServer(); - // update the play time info on the game server - sendPlayTimeInfoToGameServer(); - } - } - } - break; - } - default : - { - //Forward on to Game Server - DEBUG_REPORT_LOG((!m_client || !m_client->getGameConnection()), ("Warn, received game message with no game connection. This may happen for a short time after a GameServer crashes. If it continues to happen, it indicates a bug.\n")); + // drop inactive character + if (ConfigConnectionServer::getDisconnectOnInactive()) { + LOG("ClientDisconnect", + ("Disconnecting %u because the player was inactive for too long.", getSUID())); + ConnectionServer::dropClient(this, "Client inactivity"); + disconnect(); + } else if (ConfigConnectionServer::getDisconnectFreeTrialOnInactive() && + ((m_featureBitsSubscription & ClientSubscriptionFeature::Base) == 0)) { + LOG("ClientDisconnect", + ("Disconnecting (free trial) %u because the player was inactive for too long.", getSUID())); + ConnectionServer::dropClient(this, "Client inactivity (free trial)"); + disconnect(); + } + } + } + // client went active + else { + if (m_lastActiveTime == 0) { + // record the time client went active + m_lastActiveTime = ::time(nullptr); - if (m_client && m_client->getGameConnection()) - { - static std::vector v; - v.clear(); - v.push_back(m_client->getNetworkId()); - GameClientMessage gcm(v, true, ri); - m_client->getGameConnection()->send(gcm, true); - } - - break; - } - } - } else { - switch (messageType) { - case constcrc("ClientIdMsg") : - { - DEBUG_REPORT_LOG(true,("Recieved ClientIdMsg\n")); - ClientIdMsg k(ri); + // tell Session to start recording play time for the character + if (ConnectionServer::getSessionApiClient() && + ConfigConnectionServer::getSessionRecordPlayTime()) { + LOG("CustomerService", + ("Login:%s calling SessionStartPlay() for %s/%s/%s (%s)", ClientConnection::describeAccount( + this).c_str(), this->getSessionId().c_str(), ConfigConnectionServer::getClusterName(), this->getCharacterName().c_str(), this->getCharacterId().getValueString().c_str())); + ConnectionServer::getSessionApiClient()->startPlay(*this); + } - handleClientIdMessage(k); - break; - } - case constcrc("SelectCharacter") : - { - SelectCharacter s(ri); - DEBUG_REPORT_LOG(true,("Recvd SelectCharacter message for %s.\n", s.getId().getValueString().c_str())); + // update the play time info on the game server + sendPlayTimeInfoToGameServer(); + } + } + } + break; + } + default : { + //Forward on to Game Server + DEBUG_REPORT_LOG((!m_client || !m_client->getGameConnection()), + ("Warn, received game message with no game connection. This may happen for a short time after a GameServer crashes. If it continues to happen, it indicates a bug.\n")); - handleSelectCharacterMessage(s); - break; - } - case constcrc("ClientCreateCharacter") : - { - if (m_hasBeenValidated && !m_hasSelectedCharacter) //lint !e774 no this doesn't always eval to true - { - ClientCreateCharacter clientCreate(ri); - DEBUG_REPORT_LOG(true,("Got ClientCreateCharacter message for %lu with name %s\n", m_suid, Unicode::wideToNarrow(clientCreate.getCharacterName()).c_str())); - LOG("TraceCharacterCreation", ("%d sent ClientCreateCharacter(charaterName=%s, templateName=%s, scaleFactor=%f, startingLocation=%s, hairTemplateName=%s, profession=%s, jedi=%d, useNewbieTutorial=%d, skillTemplate=%s, workingSkill=%s)", - getSUID(), - Unicode::wideToNarrow(clientCreate.getCharacterName()).c_str(), - clientCreate.getTemplateName().c_str(), - clientCreate.getScaleFactor(), - clientCreate.getStartingLocation().c_str(), - clientCreate.getHairTemplateName().c_str(), - clientCreate.getProfession().c_str(), - static_cast(clientCreate.getJedi()), - static_cast(clientCreate.getUseNewbieTutorial()), - clientCreate.getSkillTemplate().c_str(), - clientCreate.getWorkingSkill().c_str())); + if (m_client && m_client->getGameConnection()) { + static std::vector v; + v.clear(); + v.push_back(m_client->getNetworkId()); + GameClientMessage gcm(v, true, ri); + m_client->getGameConnection()->send(gcm, true); + } - if (!clientCreate.getUseNewbieTutorial() && !m_canSkipTutorial) - { - LOG("TraceCharacterCreation", ("%d failed character creation. The client is not allowed to skip the tutorial", getSUID())); - // This is probably a hack attempt, because the Client was already told they couldn't skip the tutorial - LOG("ClientDisconnect", ("Disconnecting %u because they tried to skip the tutorial without permission.\n",getSUID())); - disconnect(); - } - else if (clientCreate.getJedi() && !m_canCreateJediCharacter) - { - LOG("TraceCharacterCreation", ("%d failed character creation. The request character type was Jedi, but this client cannot create a Jedi character", getSUID())); - // This is probably a hack attempt, because the Client was already told they couldn't create a character - LOG("ClientDisconnect", ("Disconnecting %u because they tried to create a Jedi character without permission.\n",getSUID())); - disconnect(); - } - else if (!clientCreate.getJedi() && !m_canCreateRegularCharacter) - { - LOG("TraceCharacterCreation", ("%d failed character creation. The client is not allowed to create any characters", getSUID())); - // This is probably a hack attempt, because the Client was already told they couldn't create a character - LOG("ClientDisconnect", ("Disconnecting %u because they tried to create a regular character without permission.\n",getSUID())); - disconnect(); - } - else if (m_hasRequestedCharacterCreate) - { - LOG("TraceCharacterCreation", ("%d failed character creation. The client has already requested character creation on this connection", getSUID())); - LOG("ClientDisconnect", ("Disconnecting %u because the client has already requested character creation on this connection.\n",getSUID())); - disconnect(); - } - else if (m_hasCreatedCharacter) - { - LOG("TraceCharacterCreation", ("%d failed character creation. A character has been created on this or another galaxy for this account while this connection was up", getSUID())); - LOG("ClientDisconnect", ("Disconnecting %u because a character has been created on this or another galaxy for this account while this connection was up.\n",getSUID())); - disconnect(); - } - else if (clientCreate.getCharacterName().length()==0) - { - LOG("TraceCharacterCreation", ("%d failed character creation. The character's name is empty", getSUID())); - LOG("ClientDisconnect",("Disconnecting %u because they tried to create a character with no name.\n",getSUID())); - disconnect(); - } - else - { - Unicode::String biography(clientCreate.getBiography()); - if (biography.length() > 1024) - { - IGNORE_RETURN( biography.erase(1024) ); - DEBUG_REPORT_LOG(true,("Biography shortened to 1024 characters.\n")); - } - - if (m_isAdminAccount) - { - ConnectionCreateCharacter connectionCreate( - m_suid, - clientCreate.getCharacterName(), - clientCreate.getTemplateName(), - clientCreate.getScaleFactor(), - clientCreate.getStartingLocation(), - clientCreate.getAppearanceData(), - clientCreate.getHairTemplateName(), - clientCreate.getHairAppearanceData(), - clientCreate.getProfession(), - clientCreate.getJedi(), - biography, - clientCreate.getUseNewbieTutorial(), - clientCreate.getSkillTemplate(), - clientCreate.getWorkingSkill(), - m_isAdminAccount, - false, - m_featureBitsGame); + break; + } + } + } else { + switch (messageType) { + case constcrc("ClientIdMsg") : { + DEBUG_REPORT_LOG(true, ("Recieved ClientIdMsg\n")); + ClientIdMsg k(ri); - ConnectionServer::sendToCentralProcess(connectionCreate); - LOG("TraceCharacterCreation", ("%d character creation request sent to CentralServer", getSUID())); - } - else - { - // for regular players, do one final check with the LoginServer - // to make sure the character can be created (i.e. that character - // limits have not been exceeded) - delete m_pendingCharacterCreate; - m_pendingCharacterCreate = new ConnectionCreateCharacter( - m_suid, - clientCreate.getCharacterName(), - clientCreate.getTemplateName(), - clientCreate.getScaleFactor(), - clientCreate.getStartingLocation(), - clientCreate.getAppearanceData(), - clientCreate.getHairTemplateName(), - clientCreate.getHairAppearanceData(), - clientCreate.getProfession(), - clientCreate.getJedi(), - biography, - clientCreate.getUseNewbieTutorial(), - clientCreate.getSkillTemplate(), - clientCreate.getWorkingSkill(), - m_isAdminAccount, - false, - m_featureBitsGame); + handleClientIdMessage(k); + break; + } + case constcrc("SelectCharacter") : { + SelectCharacter s(ri); + DEBUG_REPORT_LOG(true, + ("Recvd SelectCharacter message for %s.\n", s.getId().getValueString().c_str())); - LOG("TraceCharacterCreation", ("%d character creation request awaiting final verification from LoginServer", getSUID())); + handleSelectCharacterMessage(s); + break; + } + case constcrc("ClientCreateCharacter") : { + if (m_hasBeenValidated && !m_hasSelectedCharacter) //lint !e774 no this doesn't always eval to true + { + ClientCreateCharacter clientCreate(ri); + DEBUG_REPORT_LOG(true, + ("Got ClientCreateCharacter message for %lu with name %s\n", m_suid, Unicode::wideToNarrow( + clientCreate.getCharacterName()).c_str())); + LOG("TraceCharacterCreation", + ("%d sent ClientCreateCharacter(charaterName=%s, templateName=%s, scaleFactor=%f, startingLocation=%s, hairTemplateName=%s, profession=%s, jedi=%d, useNewbieTutorial=%d, skillTemplate=%s, workingSkill=%s)", + getSUID(), + Unicode::wideToNarrow(clientCreate.getCharacterName()).c_str(), + clientCreate.getTemplateName().c_str(), + clientCreate.getScaleFactor(), + clientCreate.getStartingLocation().c_str(), + clientCreate.getHairTemplateName().c_str(), + clientCreate.getProfession().c_str(), + static_cast(clientCreate.getJedi()), + static_cast(clientCreate.getUseNewbieTutorial()), + clientCreate.getSkillTemplate().c_str(), + clientCreate.getWorkingSkill().c_str())); - ValidateAccountMessage vcm(m_suid, 0, m_featureBitsSubscription); - ConnectionServer::sendToCentralProcess(vcm); - } + if (!clientCreate.getUseNewbieTutorial() && !m_canSkipTutorial) { + LOG("TraceCharacterCreation", + ("%d failed character creation. The client is not allowed to skip the tutorial", getSUID())); + // This is probably a hack attempt, because the Client was already told they couldn't skip the tutorial + LOG("ClientDisconnect", + ("Disconnecting %u because they tried to skip the tutorial without permission.\n", getSUID())); + disconnect(); + } else if (clientCreate.getJedi() && !m_canCreateJediCharacter) { + LOG("TraceCharacterCreation", + ("%d failed character creation. The request character type was Jedi, but this client cannot create a Jedi character", getSUID())); + // This is probably a hack attempt, because the Client was already told they couldn't create a character + LOG("ClientDisconnect", + ("Disconnecting %u because they tried to create a Jedi character without permission.\n", getSUID())); + disconnect(); + } else if (!clientCreate.getJedi() && !m_canCreateRegularCharacter) { + LOG("TraceCharacterCreation", + ("%d failed character creation. The client is not allowed to create any characters", getSUID())); + // This is probably a hack attempt, because the Client was already told they couldn't create a character + LOG("ClientDisconnect", + ("Disconnecting %u because they tried to create a regular character without permission.\n", getSUID())); + disconnect(); + } else if (m_hasRequestedCharacterCreate) { + LOG("TraceCharacterCreation", + ("%d failed character creation. The client has already requested character creation on this connection", getSUID())); + LOG("ClientDisconnect", + ("Disconnecting %u because the client has already requested character creation on this connection.\n", getSUID())); + disconnect(); + } else if (m_hasCreatedCharacter) { + LOG("TraceCharacterCreation", + ("%d failed character creation. A character has been created on this or another galaxy for this account while this connection was up", getSUID())); + LOG("ClientDisconnect", + ("Disconnecting %u because a character has been created on this or another galaxy for this account while this connection was up.\n", getSUID())); + disconnect(); + } else if (clientCreate.getCharacterName().length() == 0) { + LOG("TraceCharacterCreation", + ("%d failed character creation. The character's name is empty", getSUID())); + LOG("ClientDisconnect", + ("Disconnecting %u because they tried to create a character with no name.\n", getSUID())); + disconnect(); + } else { + Unicode::String biography(clientCreate.getBiography()); + if (biography.length() > 1024) { + IGNORE_RETURN(biography.erase(1024)); + DEBUG_REPORT_LOG(true, ("Biography shortened to 1024 characters.\n")); + } - m_hasRequestedCharacterCreate = true; - } - } - break; - } - case constcrc("ClientRandomNameRequest") : - { - ClientRandomNameRequest clientRandomName(ri); + if (m_isAdminAccount) { + ConnectionCreateCharacter connectionCreate( + m_suid, + clientCreate.getCharacterName(), + clientCreate.getTemplateName(), + clientCreate.getScaleFactor(), + clientCreate.getStartingLocation(), + clientCreate.getAppearanceData(), + clientCreate.getHairTemplateName(), + clientCreate.getHairAppearanceData(), + clientCreate.getProfession(), + clientCreate.getJedi(), + biography, + clientCreate.getUseNewbieTutorial(), + clientCreate.getSkillTemplate(), + clientCreate.getWorkingSkill(), + m_isAdminAccount, + false, + m_featureBitsGame); - RandomNameRequest randomNameRequest(m_suid, clientRandomName.getCreatureTemplate()); - ConnectionServer::sendToCentralProcess(randomNameRequest); - LOG("TraceCharacterCreation", ("%d requested a random name. Request sent to CentralServer", getSUID())); - break; - } - case constcrc("ClientVerifyAndLockNameRequest") : - { - ClientVerifyAndLockNameRequest clientVerifyAndLockNameRequest(ri); + ConnectionServer::sendToCentralProcess(connectionCreate); + LOG("TraceCharacterCreation", + ("%d character creation request sent to CentralServer", getSUID())); + } else { + // for regular players, do one final check with the LoginServer + // to make sure the character can be created (i.e. that character + // limits have not been exceeded) + delete m_pendingCharacterCreate; + m_pendingCharacterCreate = new ConnectionCreateCharacter( + m_suid, + clientCreate.getCharacterName(), + clientCreate.getTemplateName(), + clientCreate.getScaleFactor(), + clientCreate.getStartingLocation(), + clientCreate.getAppearanceData(), + clientCreate.getHairTemplateName(), + clientCreate.getHairAppearanceData(), + clientCreate.getProfession(), + clientCreate.getJedi(), + biography, + clientCreate.getUseNewbieTutorial(), + clientCreate.getSkillTemplate(), + clientCreate.getWorkingSkill(), + m_isAdminAccount, + false, + m_featureBitsGame); - VerifyAndLockNameRequest verifyAndLockNameRequest(m_suid, NetworkId::cms_invalid, clientVerifyAndLockNameRequest.getTemplateName(), clientVerifyAndLockNameRequest.getCharacterName(), m_featureBitsGame); - ConnectionServer::sendToCentralProcess(verifyAndLockNameRequest); - LOG("TraceCharacterCreation", ("%d requested a verify and lock of name: %s. Request sent to CentralServer", getSUID(), Unicode::wideToNarrow(verifyAndLockNameRequest.getCharacterName()).c_str())); - break; - } - case constcrc("LagRequest") : - { - // TODO: why is this commented out? - // handleLagRequest(); - break; - } - } - } - } - catch(const Archive::ReadException & readException) - { - WARNING(true, ("Archive read error (%s) from client. Disconnecting client", readException.what())); - LOG("ClientDisconnect", ("Archive read error (%s) from client. Disconnecting client", readException.what())); - disconnect(); - } + LOG("TraceCharacterCreation", + ("%d character creation request awaiting final verification from LoginServer", getSUID())); + + ValidateAccountMessage vcm(m_suid, 0, m_featureBitsSubscription); + ConnectionServer::sendToCentralProcess(vcm); + } + + m_hasRequestedCharacterCreate = true; + } + } + break; + } + case constcrc("ClientRandomNameRequest") : { + ClientRandomNameRequest clientRandomName(ri); + + RandomNameRequest randomNameRequest(m_suid, clientRandomName.getCreatureTemplate()); + ConnectionServer::sendToCentralProcess(randomNameRequest); + LOG("TraceCharacterCreation", + ("%d requested a random name. Request sent to CentralServer", getSUID())); + break; + } + case constcrc("ClientVerifyAndLockNameRequest") : { + ClientVerifyAndLockNameRequest clientVerifyAndLockNameRequest(ri); + + VerifyAndLockNameRequest verifyAndLockNameRequest(m_suid, NetworkId::cms_invalid, + clientVerifyAndLockNameRequest.getTemplateName(), + clientVerifyAndLockNameRequest.getCharacterName(), + m_featureBitsGame); + ConnectionServer::sendToCentralProcess(verifyAndLockNameRequest); + LOG("TraceCharacterCreation", + ("%d requested a verify and lock of name: %s. Request sent to CentralServer", getSUID(), Unicode::wideToNarrow( + verifyAndLockNameRequest.getCharacterName()).c_str())); + break; + } + case constcrc("LagRequest") : { + // TODO: why is this commented out? + // handleLagRequest(); + break; + } + } + } + } + catch (const Archive::ReadException &readException) { + WARNING(true, ("Archive read error (%s) from client. Disconnecting client", readException.what())); + LOG("ClientDisconnect", ("Archive read error (%s) from client. Disconnecting client", readException.what())); + disconnect(); + } } //----------------------------------------------------------------------- -void ClientConnection::handleLagRequest() -{ - // client is requesting a lag ping (reliable trace) - GameNetworkMessage response("ConnectionServerLagResponse"); - send(response, true); +void ClientConnection::handleLagRequest() { + // client is requesting a lag ping (reliable trace) + GameNetworkMessage response("ConnectionServerLagResponse"); + send(response, true); - if(m_hasSelectedCharacter && m_client && m_client->getGameConnection()) - { - // send to game server - GameNetworkMessage request("LagRequest"); - std::vector v; - v.push_back(m_characterId); - GameClientMessage gcm(v, true, request); - m_client->getGameConnection()->send(gcm, true); - } - else - { - // send game response immediately - GameNetworkMessage gameResponse("GameServerLagResponse"); - send(gameResponse, true); - } + if (m_hasSelectedCharacter && m_client && m_client->getGameConnection()) { + // send to game server + GameNetworkMessage request("LagRequest"); + std::vector v; + v.push_back(m_characterId); + GameClientMessage gcm(v, true, request); + m_client->getGameConnection()->send(gcm, true); + } else { + // send game response immediately + GameNetworkMessage gameResponse("GameServerLagResponse"); + send(gameResponse, true); + } } @@ -1233,130 +1191,113 @@ void ClientConnection::handleLagRequest() * to associate the connection with the new client they created with that * character. We no longer need the character map. */ -void ClientConnection::setClient(Client* newClient) -{ - //This fatal is here to try to catch the reconnect bug. - WARNING_STRICT_FATAL(m_client, ("Attempting to set the client on a connection that already has one. Client %s\n", getCharacterName().c_str())); - // jrandall - I've removed the fatal because it is blocking some people from getting some work - // done. I'm on a high priority fix at the moment. If this warning starts appearing, - // set a break point or something. - //DEBUG_FATAL(client, ("Attempting to set the client on a connection that already has one.\n")); - m_client = newClient; +void ClientConnection::setClient(Client *newClient) { + //This fatal is here to try to catch the reconnect bug. + WARNING_STRICT_FATAL(m_client, + ("Attempting to set the client on a connection that already has one. Client %s\n", getCharacterName().c_str())); + // jrandall - I've removed the fatal because it is blocking some people from getting some work + // done. I'm on a high priority fix at the moment. If this warning starts appearing, + // set a break point or something. + //DEBUG_FATAL(client, ("Attempting to set the client on a connection that already has one.\n")); + m_client = newClient; - // todo put this in: characterMap.clear(); + // todo put this in: characterMap.clear(); } //----------------------------------------------------------------------- -void ClientConnection::send(const GameNetworkMessage & message, const bool reliable) -{ - m_sendLastTimeMs = Clock::timeMs(); +void ClientConnection::send(const GameNetworkMessage &message, const bool reliable) { + m_sendLastTimeMs = Clock::timeMs(); - if ( sm_outgoingBytesMap_Worktime == 0 ) - sm_outgoingBytesMap_Worktime = m_sendLastTimeMs; - else if ( (m_sendLastTimeMs - sm_outgoingBytesMap_Worktime) > 60000 ) // 60 seconds - { - sm_outgoingBytesMap_Stats = sm_outgoingBytesMap_Working; - std::map< std::string, uint32 >::iterator iter; - for ( iter = sm_outgoingBytesMap_Working.begin(); iter != sm_outgoingBytesMap_Working.end(); ++iter ) - { - iter->second = 0; - } - sm_outgoingBytesMap_Worktime = m_sendLastTimeMs; - } - sm_outgoingBytesMap_Working[ message.getCmdName() ] += message.getByteStream().getSize(); + if (sm_outgoingBytesMap_Worktime == 0) + sm_outgoingBytesMap_Worktime = m_sendLastTimeMs; + else if ((m_sendLastTimeMs - sm_outgoingBytesMap_Worktime) > 60000) // 60 seconds + { + sm_outgoingBytesMap_Stats = sm_outgoingBytesMap_Working; + std::map::iterator iter; + for (iter = sm_outgoingBytesMap_Working.begin(); iter != sm_outgoingBytesMap_Working.end(); ++iter) { + iter->second = 0; + } + sm_outgoingBytesMap_Worktime = m_sendLastTimeMs; + } + sm_outgoingBytesMap_Working[message.getCmdName()] += message.getByteStream().getSize(); - ServerConnection::send(message, reliable); + ServerConnection::send(message, reliable); } //----------------------------------------------------------------------- -std::map< std::string, uint32 >& ClientConnection::getPacketBytesPerMinStats() -{ - uint32 now = Clock::timeMs(); - if ( sm_outgoingBytesMap_Worktime == 0 ) - sm_outgoingBytesMap_Worktime = now; - else if ( (now - sm_outgoingBytesMap_Worktime) > 60000 ) // 60 seconds - { - sm_outgoingBytesMap_Stats = sm_outgoingBytesMap_Working; - std::map< std::string, uint32 >::iterator iter; - for ( iter = sm_outgoingBytesMap_Working.begin(); iter != sm_outgoingBytesMap_Working.end(); ++iter ) - { - iter->second = 0; - } - sm_outgoingBytesMap_Worktime = now; +std::map &ClientConnection::getPacketBytesPerMinStats() { + uint32 now = Clock::timeMs(); + if (sm_outgoingBytesMap_Worktime == 0) + sm_outgoingBytesMap_Worktime = now; + else if ((now - sm_outgoingBytesMap_Worktime) > 60000) // 60 seconds + { + sm_outgoingBytesMap_Stats = sm_outgoingBytesMap_Working; + std::map::iterator iter; + for (iter = sm_outgoingBytesMap_Working.begin(); iter != sm_outgoingBytesMap_Working.end(); ++iter) { + iter->second = 0; + } + sm_outgoingBytesMap_Worktime = now; + } + + return sm_outgoingBytesMap_Stats; +} + +//----------------------------------------------------------------------- + +void ClientConnection::handleChatEnterRoomValidationResponse(unsigned int sequence, unsigned int result) { + std::map::iterator iterFind = m_pendingChatEnterRoomRequests.find(sequence); + if (iterFind != m_pendingChatEnterRoomRequests.end()) { + if (result == CHATRESULT_SUCCESS) { + if (m_client && m_client->getChatConnection()) { + // game server says it's ok to enter the chat room, + // so forward the request on to the chat server + m_client->getChatConnection()->send(*(iterFind->second), true); + } else { + // send back response to client saying chat server not available + + // the client only cares about sequence and result when it's a failure + ChatOnEnteredRoom fail(sequence, SWG_CHAT_ERR_CHAT_SERVER_UNAVAILABLE, 0, ChatAvatarId()); + send(fail, true); + } + } else { + // send back response to client saying game server denied enter room request + + // the client only cares about sequence and result when it's a failure + ChatOnEnteredRoom fail(sequence, result, 0, ChatAvatarId()); + send(fail, true); } - return sm_outgoingBytesMap_Stats; + delete iterFind->second; + m_pendingChatEnterRoomRequests.erase(iterFind); + } } //----------------------------------------------------------------------- -void ClientConnection::handleChatEnterRoomValidationResponse(unsigned int sequence, unsigned int result) -{ - std::map::iterator iterFind = m_pendingChatEnterRoomRequests.find(sequence); - if (iterFind != m_pendingChatEnterRoomRequests.end()) - { - if (result == CHATRESULT_SUCCESS) - { - if (m_client && m_client->getChatConnection()) - { - // game server says it's ok to enter the chat room, - // so forward the request on to the chat server - m_client->getChatConnection()->send(*(iterFind->second), true); - } - else - { - // send back response to client saying chat server not available +void ClientConnection::handleChatQueryRoomValidationResponse(unsigned int sequence, bool success) { + std::map::iterator iterFind = m_pendingChatQueryRoomRequests.find(sequence); + if (iterFind != m_pendingChatQueryRoomRequests.end()) { + if (success) { + if (m_client && m_client->getChatConnection()) { + // game server says it's ok to query the chat room, + // so forward the request on to the chat server + m_client->getChatConnection()->send(*(iterFind->second), true); + } + } - // the client only cares about sequence and result when it's a failure - ChatOnEnteredRoom fail(sequence, SWG_CHAT_ERR_CHAT_SERVER_UNAVAILABLE, 0, ChatAvatarId()); - send(fail, true); - } - } - else - { - // send back response to client saying game server denied enter room request - - // the client only cares about sequence and result when it's a failure - ChatOnEnteredRoom fail(sequence, result, 0, ChatAvatarId()); - send(fail, true); - } - - delete iterFind->second; - m_pendingChatEnterRoomRequests.erase(iterFind); - } + delete iterFind->second; + m_pendingChatQueryRoomRequests.erase(iterFind); + } } //----------------------------------------------------------------------- -void ClientConnection::handleChatQueryRoomValidationResponse(unsigned int sequence, bool success) -{ - std::map::iterator iterFind = m_pendingChatQueryRoomRequests.find(sequence); - if (iterFind != m_pendingChatQueryRoomRequests.end()) - { - if (success) - { - if (m_client && m_client->getChatConnection()) - { - // game server says it's ok to query the chat room, - // so forward the request on to the chat server - m_client->getChatConnection()->send(*(iterFind->second), true); - } - } - - delete iterFind->second; - m_pendingChatQueryRoomRequests.erase(iterFind); - } -} - -//----------------------------------------------------------------------- - -void ClientConnection::sendByteStream(const Archive::ByteStream& bs, bool reliable) -{ - Connection::send(bs, reliable); +void ClientConnection::sendByteStream(const Archive::ByteStream &bs, bool reliable) { + Connection::send(bs, reliable); } //----------------------------------------------------------------------- @@ -1364,10 +1305,9 @@ void ClientConnection::sendByteStream(const Archive::ByteStream& bs, bool reliab /** * Send the client to an arbitratry game server based on the current scene */ -const bool ClientConnection::sendToGameServer() -{ - GameConnection * c = const_cast(ConnectionServer::getGameConnection(m_targetScene)); - return sendToGameServer(c); +const bool ClientConnection::sendToGameServer() { + GameConnection *c = const_cast(ConnectionServer::getGameConnection(m_targetScene)); + return sendToGameServer(c); } // ---------------------------------------------------------------------- @@ -1376,33 +1316,30 @@ const bool ClientConnection::sendToGameServer() * Send the client to a particular game server, sepcified by process id. */ -const bool ClientConnection::sendToGameServer(uint32 gameServerId) -{ - GameConnection * c = const_cast(ConnectionServer::getGameConnection(gameServerId)); - return sendToGameServer(c); +const bool ClientConnection::sendToGameServer(uint32 gameServerId) { + GameConnection *c = const_cast(ConnectionServer::getGameConnection(gameServerId)); + return sendToGameServer(c); } // ---------------------------------------------------------------------- -bool ClientConnection::sendToGameServer(GameConnection *c) -{ - bool result = false; +bool ClientConnection::sendToGameServer(GameConnection *c) { + bool result = false; - if(c && m_hasSelectedCharacter && m_hasBeenValidated) - { - //create a new client - ConnectionServer::addNewClient(this, - m_characterId, - c, - m_targetScene, - m_sendToStarport); + if (c && m_hasSelectedCharacter && m_hasBeenValidated) { + //create a new client + ConnectionServer::addNewClient(this, + m_characterId, + c, + m_targetScene, + m_sendToStarport); - LoggedInMessage m(m_suid); - ConnectionServer::sendToCentralProcess(m); - result = true; - m_hasBeenSentToGameServer = true; - } - return result; + LoggedInMessage m(m_suid); + ConnectionServer::sendToCentralProcess(m); + result = true; + m_hasBeenSentToGameServer = true; + } + return result; } //----------------------------------------------------------------------- @@ -1412,256 +1349,250 @@ bool ClientConnection::sendToGameServer(GameConnection *c) * Now we know whether the character is valid and where in the world it * is located. */ -void ClientConnection::onCharacterValidated(bool isValid, const NetworkId &character, const std::string &characterName, const NetworkId &container, const std::string &scene, const Vector &coordinates) -{ - if (!m_validatingCharacter) - { - LOG("TraceCharacterSelection", ("%d received a validation response, but is not in the process of validation", getSUID())); - DEBUG_REPORT_LOG(true,("Got unexpected onCharacterValidated() for account %lu.\n",getSUID())); - return; - } - m_validatingCharacter=false; +void ClientConnection::onCharacterValidated(bool isValid, const NetworkId &character, const std::string &characterName, + const NetworkId &container, const std::string &scene, + const Vector &coordinates) { + if (!m_validatingCharacter) { + LOG("TraceCharacterSelection", + ("%d received a validation response, but is not in the process of validation", getSUID())); + DEBUG_REPORT_LOG(true, ("Got unexpected onCharacterValidated() for account %lu.\n", getSUID())); + return; + } + m_validatingCharacter = false; - if (isValid) - { - LOG("TraceCharacterSelection", ("%d received a validation response. The character (%s: %s) at (%s,%f,%f,%f,%s) selected is valid", getSUID(), character.getValueString().c_str(), characterName.c_str(), scene.c_str(), coordinates.x, coordinates.y, coordinates.z, container.getValueString().c_str())); - LOG("CustomerService", ("Login:%s received a validation response. The character (%s: %s) at (%s,%f,%f,%f,%s) selected is valid", describeAccount(this).c_str(), character.getValueString().c_str(), characterName.c_str(), scene.c_str(), coordinates.x, coordinates.y, coordinates.z, container.getValueString().c_str())); - m_targetScene = scene; - m_targetCoordinates = coordinates; - m_characterId = character; - m_containerId = container; - m_characterName = characterName; + if (isValid) { + LOG("TraceCharacterSelection", + ("%d received a validation response. The character (%s: %s) at (%s,%f,%f,%f,%s) selected is valid", getSUID(), character.getValueString().c_str(), characterName.c_str(), scene.c_str(), coordinates.x, coordinates.y, coordinates.z, container.getValueString().c_str())); + LOG("CustomerService", + ("Login:%s received a validation response. The character (%s: %s) at (%s,%f,%f,%f,%s) selected is valid", describeAccount( + this).c_str(), character.getValueString().c_str(), characterName.c_str(), scene.c_str(), coordinates.x, coordinates.y, coordinates.z, container.getValueString().c_str())); + m_targetScene = scene; + m_targetCoordinates = coordinates; + m_characterId = character; + m_containerId = container; + m_characterName = characterName; - m_hasSelectedCharacter = true; + m_hasSelectedCharacter = true; - // ask CentralServer to suggest a game server for this character - // (Central will forward the request to a Planet Server) + // ask CentralServer to suggest a game server for this character + // (Central will forward the request to a Planet Server) - RequestGameServerForLoginMessage requestmsg(getSUID(), m_characterId, m_containerId, m_targetScene, m_targetCoordinates, false); - if(ConnectionServer::getCentralConnection()) - ConnectionServer::getCentralConnection()->send(requestmsg, true); - else - { - LOG("ClientDisconnect",("Can't handle login of character %s because there is no connection to Central.\n",m_characterId.getValueString().c_str())); - ErrorMessage err("Validation Failed", "The connection to the central server is down. Please try again later."); - send(err, true); + RequestGameServerForLoginMessage requestmsg(getSUID(), m_characterId, m_containerId, m_targetScene, + m_targetCoordinates, false); + if (ConnectionServer::getCentralConnection()) + ConnectionServer::getCentralConnection()->send(requestmsg, true); + else { + LOG("ClientDisconnect", + ("Can't handle login of character %s because there is no connection to Central.\n", m_characterId.getValueString().c_str())); + ErrorMessage err("Validation Failed", + "The connection to the central server is down. Please try again later."); + send(err, true); - disconnect(); - } - } - else - { - LOG("TraceCharacterSelection", ("%d validation failed, disconnecting client", getSUID())); - ErrorMessage err("Validation Failed", "Your character was denied login by the database."); - send(err, true); + disconnect(); + } + } else { + LOG("TraceCharacterSelection", ("%d validation failed, disconnecting client", getSUID())); + ErrorMessage err("Validation Failed", "Your character was denied login by the database."); + send(err, true); - LOG("ClientDisconnect", ("Denying login for account %u.\n",getSUID())); - disconnect(); - } + LOG("ClientDisconnect", ("Denying login for account %u.\n", getSUID())); + disconnect(); + } } //------------------------------------------------------------------------------------------ -void ClientConnection::onValidateClient (uint32 suid, const std::string & username, bool secure, const char* id, const uint32 gameFeatures, const uint32 subscriptionFeatures, unsigned int entitlementTotalTime, unsigned int entitlementEntitledTime, unsigned int entitlementTotalTimeSinceLastLogin, unsigned int entitlementEntitledTimeSinceLastLogin, int buddyPoints) -{ - UNREF(id); - m_sessionValidated = true; - m_suid = suid; - m_accountName = username; - m_featureBitsGame = gameFeatures; - m_featureBitsSubscription = subscriptionFeatures; - m_isSecure = secure; - m_entitlementTotalTime = entitlementTotalTime; - m_entitlementEntitledTime = entitlementEntitledTime; - m_entitlementTotalTimeSinceLastLogin = entitlementTotalTimeSinceLastLogin; - m_entitlementEntitledTimeSinceLastLogin = entitlementEntitledTimeSinceLastLogin; - m_buddyPoints = buddyPoints; +void ClientConnection::onValidateClient(StationId suid, const std::string &username, bool secure, const char *id, + const uint32 gameFeatures, const uint32 subscriptionFeatures, + unsigned int entitlementTotalTime, unsigned int entitlementEntitledTime, + unsigned int entitlementTotalTimeSinceLastLogin, + unsigned int entitlementEntitledTimeSinceLastLogin, int buddyPoints) { + UNREF(id); + m_sessionValidated = true; + m_suid = suid; + m_accountName = username; + m_featureBitsGame = gameFeatures; + m_featureBitsSubscription = subscriptionFeatures; + m_isSecure = secure; + m_entitlementTotalTime = entitlementTotalTime; + m_entitlementEntitledTime = entitlementEntitledTime; + m_entitlementTotalTimeSinceLastLogin = entitlementTotalTimeSinceLastLogin; + m_entitlementEntitledTimeSinceLastLogin = entitlementEntitledTimeSinceLastLogin; + m_buddyPoints = buddyPoints; - if (id) - m_sessionId = id; + if (id) + m_sessionId = id; - if (m_requestedSuid != 0 && suid != m_requestedSuid) - { - //verify internal, secure, is on the god list - bool loginOK=false; - if(!secure) - LOG("CustomerService",("AdminLogin: User %s (account %li) attempted to log into account %li, but was not using a SecureID token", username.c_str(),suid, m_requestedSuid)); - else - { - if (!AdminAccountManager::isInternalIp(getRemoteAddress())) - LOG("CustomerService",("AdminLogin: User %s (account %li) attempted to log into account %li, but was not logging in from an internal IP", username.c_str(),suid, m_requestedSuid)); - else - { - int adminLevel=0; - if (!AdminAccountManager::isAdminAccount(Unicode::toLower(username), adminLevel) || adminLevel < 10) - LOG("CustomerService",("AdminLogin: User %s (account %li) attempted to log into account %li, but did not have sufficient permissions", username.c_str(),suid, m_requestedSuid)); - else - { - LOG("CustomerService",("AdminLogin: User %s (account %li) logged into account %li", username.c_str(),m_suid,m_requestedSuid)); - DEBUG_REPORT_LOG(true,("AdminLogin: User %s (account %li) logged into account %li\n", username.c_str(),m_suid,m_requestedSuid)); - m_suid = m_requestedSuid; - m_usingAdminLogin = true; - loginOK=true; - } - } - } - if (!loginOK) - { - disconnect(); - return; - } - } + if (m_requestedSuid != 0 && suid != m_requestedSuid) { + //verify internal, secure, is on the god list + bool loginOK = false; + if (!secure) + LOG("CustomerService", + ("AdminLogin: User %s (account %li) attempted to log into account %li, but was not using a SecureID token", username.c_str(), suid, m_requestedSuid)); + else { + if (!AdminAccountManager::isInternalIp(getRemoteAddress())) + LOG("CustomerService", + ("AdminLogin: User %s (account %li) attempted to log into account %li, but was not logging in from an internal IP", username.c_str(), suid, m_requestedSuid)); + else { + int adminLevel = 0; + if (!AdminAccountManager::isAdminAccount(Unicode::toLower(username), adminLevel) || adminLevel < 10) + LOG("CustomerService", + ("AdminLogin: User %s (account %li) attempted to log into account %li, but did not have sufficient permissions", username.c_str(), suid, m_requestedSuid)); + else { + LOG("CustomerService", + ("AdminLogin: User %s (account %li) logged into account %li", username.c_str(), m_suid, m_requestedSuid)); + DEBUG_REPORT_LOG(true, + ("AdminLogin: User %s (account %li) logged into account %li\n", username.c_str(), m_suid, m_requestedSuid)); + m_suid = m_requestedSuid; + m_usingAdminLogin = true; + loginOK = true; + } + } + } + if (!loginOK) { + disconnect(); + return; + } + } - m_featureBitsGame &= ~ConfigConnectionServer::getDisabledFeatureBits(); - - //Configoption to enable JTL features for beta players so that our code can pretend everything uses the JTL Retail bit - if (ConfigConnectionServer::getSetJtlRetailIfBetaIsSet()) - { - if (ClientGameFeature::SpaceExpansionBeta & m_featureBitsGame) - { - m_featureBitsGame |= ClientGameFeature::SpaceExpansionRetail; - } - } + m_featureBitsGame &= ~ConfigConnectionServer::getDisabledFeatureBits(); - //Configoption to enable Obiwan features for beta players so that our code can pretend everything uses the Obiwan Retail bit - if (ConfigConnectionServer::getSetTrialsOfObiwanRetailIfBetaIsSet()) - { - if (ClientGameFeature::TrialsOfObiwanBeta & m_featureBitsGame) - { - //-- add retail bit only if player does not have the preorder bit - if ((m_featureBitsGame & ClientGameFeature::TrialsOfObiwanPreorder) == 0) - m_featureBitsGame |= ClientGameFeature::TrialsOfObiwanRetail; - } - else - { - // Clear bits from players who might have them for real, but aren't in the beta - m_featureBitsGame &= ~ClientGameFeature::TrialsOfObiwanRetail; - m_featureBitsGame &= ~ClientGameFeature::TrialsOfObiwanPreorder; - } - } - - //-- Obiwan Preorders get the Retail bit as well... All rewards etc... - if (ClientGameFeature::TrialsOfObiwanPreorder & m_featureBitsGame) - { - m_featureBitsGame |= ClientGameFeature::TrialsOfObiwanRetail; - } + //Configoption to enable JTL features for beta players so that our code can pretend everything uses the JTL Retail bit + if (ConfigConnectionServer::getSetJtlRetailIfBetaIsSet()) { + if (ClientGameFeature::SpaceExpansionBeta & m_featureBitsGame) { + m_featureBitsGame |= ClientGameFeature::SpaceExpansionRetail; + } + } - // Restrictions for "new free trial" account - if ( ((m_featureBitsSubscription & ClientSubscriptionFeature::FreeTrial2) != 0) - && ((m_featureBitsSubscription & ClientSubscriptionFeature::Base) == 0)) - { - // "new free trial" account don't have access to RoW until they convert - m_featureBitsGame &= ~ClientGameFeature::Episode3ExpansionRetail; - m_featureBitsGame &= ~ClientGameFeature::Episode3PreorderDownload; + //Configoption to enable Obiwan features for beta players so that our code can pretend everything uses the Obiwan Retail bit + if (ConfigConnectionServer::getSetTrialsOfObiwanRetailIfBetaIsSet()) { + if (ClientGameFeature::TrialsOfObiwanBeta & m_featureBitsGame) { + //-- add retail bit only if player does not have the preorder bit + if ((m_featureBitsGame & ClientGameFeature::TrialsOfObiwanPreorder) == 0) + m_featureBitsGame |= ClientGameFeature::TrialsOfObiwanRetail; + } else { + // Clear bits from players who might have them for real, but aren't in the beta + m_featureBitsGame &= ~ClientGameFeature::TrialsOfObiwanRetail; + m_featureBitsGame &= ~ClientGameFeature::TrialsOfObiwanPreorder; + } + } - // ClientGameFeature::FreeTrial2 indicates this is a converted "new free trial" - // account, and since this account hasn't converted yet, we remove this bit - m_featureBitsGame &= ~ClientGameFeature::FreeTrial2; - } + //-- Obiwan Preorders get the Retail bit as well... All rewards etc... + if (ClientGameFeature::TrialsOfObiwanPreorder & m_featureBitsGame) { + m_featureBitsGame |= ClientGameFeature::TrialsOfObiwanRetail; + } - // Clear feature bits that only apply if the account is paying (i.e. the sub base bit is set) - if ((m_featureBitsSubscription & ClientSubscriptionFeature::Base) == 0) - { - m_featureBitsGame &= ~ClientGameFeature::HousePackupReward; - m_featureBitsGame &= ~ClientGameFeature::BuddyProgramReward; - } - - ValidateAccountMessage vcm(m_suid, 0, m_featureBitsSubscription); - ConnectionServer::sendToCentralProcess(vcm); - ConnectionServer::addConnectedClient(m_suid, this); + // Restrictions for "new free trial" account + if (((m_featureBitsSubscription & ClientSubscriptionFeature::FreeTrial2) != 0) + && ((m_featureBitsSubscription & ClientSubscriptionFeature::Base) == 0)) { + // "new free trial" account don't have access to RoW until they convert + m_featureBitsGame &= ~ClientGameFeature::Episode3ExpansionRetail; + m_featureBitsGame &= ~ClientGameFeature::Episode3PreorderDownload; - uint32 const requiredSubscriptionBits = ConfigConnectionServer::getRequiredSubscriptionBits(); - if (requiredSubscriptionBits != 0) - { - if ((subscriptionFeatures & requiredSubscriptionBits) != requiredSubscriptionBits) - { - LOG("ClientDisconnect", ("Suid %d (%s) by session denial reason 'Invalid Subscription Bits'.", suid, username.c_str())); - LOG("CustomerService", ("Login: %s by session denial reason 'Invalid Subscription Bits'.", describeAccount(this).c_str())); - disconnect(); - return; - } - } + // ClientGameFeature::FreeTrial2 indicates this is a converted "new free trial" + // account, and since this account hasn't converted yet, we remove this bit + m_featureBitsGame &= ~ClientGameFeature::FreeTrial2; + } - uint32 const requiredGameBits = ConfigConnectionServer::getRequiredGameBits(); - if (requiredGameBits != 0) - { - if ((gameFeatures & requiredGameBits) != requiredGameBits) - { - LOG("ClientDisconnect", ("Suid %d (%s) by session denial reason 'Invalid Game Bits'.", suid, username.c_str())); - LOG("CustomerService", ("Login: %s by session denial reason 'Invalid Game Bits'.", describeAccount(this).c_str())); - disconnect(); - return; - } - } + // Clear feature bits that only apply if the account is paying (i.e. the sub base bit is set) + if ((m_featureBitsSubscription & ClientSubscriptionFeature::Base) == 0) { + m_featureBitsGame &= ~ClientGameFeature::HousePackupReward; + m_featureBitsGame &= ~ClientGameFeature::BuddyProgramReward; + } - // tell client the server-side game and subscription feature bits and which ConnectionServer we are and the current server Epoch time - GenericValueTypeMessage, std::pair > > const msgFeatureBits("AccountFeatureBits", std::make_pair(std::make_pair(gameFeatures, subscriptionFeatures), std::make_pair(ConfigConnectionServer::getConnectionServerNumber(), static_cast(::time(nullptr))))); - send(msgFeatureBits, true); + ValidateAccountMessage vcm(m_suid, 0, m_featureBitsSubscription); + ConnectionServer::sendToCentralProcess(vcm); + ConnectionServer::addConnectedClient(m_suid, this); - std::string const gameFeaturesDescription = ClientGameFeature::getDescription(gameFeatures); - std::string const subscriptionFeaturesDescription = ClientSubscriptionFeature::getDescription(subscriptionFeatures); + uint32 const requiredSubscriptionBits = ConfigConnectionServer::getRequiredSubscriptionBits(); + if (requiredSubscriptionBits != 0) { + if ((subscriptionFeatures & requiredSubscriptionBits) != requiredSubscriptionBits) { + LOG("ClientDisconnect", + ("Suid %d (%s) by session denial reason 'Invalid Subscription Bits'.", suid, username.c_str())); + LOG("CustomerService", + ("Login: %s by session denial reason 'Invalid Subscription Bits'.", describeAccount(this).c_str())); + disconnect(); + return; + } + } - LOG("CustomerService", ("Login:%s at IP: %s:%hu has connected with game code 0x%x (%s) and sub code 0x%x (%s) (entitlement total: %u/%u, since last login: %u/%u)", describeAccount(this).c_str(), getRemoteAddress().c_str(), getRemotePort(), gameFeatures, gameFeaturesDescription.c_str(), subscriptionFeatures, subscriptionFeaturesDescription.c_str(), m_entitlementEntitledTime, m_entitlementTotalTime, m_entitlementEntitledTimeSinceLastLogin, m_entitlementTotalTimeSinceLastLogin)); + uint32 const requiredGameBits = ConfigConnectionServer::getRequiredGameBits(); + if (requiredGameBits != 0) { + if ((gameFeatures & requiredGameBits) != requiredGameBits) { + LOG("ClientDisconnect", + ("Suid %d (%s) by session denial reason 'Invalid Game Bits'.", suid, username.c_str())); + LOG("CustomerService", + ("Login: %s by session denial reason 'Invalid Game Bits'.", describeAccount(this).c_str())); + disconnect(); + return; + } + } - // ask CentralServer to tell all other ConnectionServers on this galaxy to drop duplicate connections for this account - // and ask CentralServer (via LoginServer) to tell all ConnectionServers on other galaxies to drop duplicate connections for this account - if (!m_usingAdminLogin && !m_isSecure) - { - GenericValueTypeMessage > const dropDuplicateConnections("ConnSrvDropDupeConns", std::make_pair(m_suid, m_sessionId)); - ConnectionServer::sendToCentralProcess(dropDuplicateConnections); - } + // tell client the server-side game and subscription feature bits and which ConnectionServer we are and the current server Epoch time + GenericValueTypeMessage, std::pair > > const msgFeatureBits( + "AccountFeatureBits", std::make_pair(std::make_pair(gameFeatures, subscriptionFeatures), + std::make_pair(ConfigConnectionServer::getConnectionServerNumber(), + static_cast(::time(nullptr))))); + send(msgFeatureBits, true); + + std::string const gameFeaturesDescription = ClientGameFeature::getDescription(gameFeatures); + std::string const subscriptionFeaturesDescription = ClientSubscriptionFeature::getDescription(subscriptionFeatures); + + LOG("CustomerService", + ("Login:%s at IP: %s:%hu has connected with game code 0x%x (%s) and sub code 0x%x (%s) (entitlement total: %u/%u, since last login: %u/%u)", describeAccount( + this).c_str(), getRemoteAddress().c_str(), getRemotePort(), gameFeatures, gameFeaturesDescription.c_str(), subscriptionFeatures, subscriptionFeaturesDescription.c_str(), m_entitlementEntitledTime, m_entitlementTotalTime, m_entitlementEntitledTimeSinceLastLogin, m_entitlementTotalTimeSinceLastLogin)); + + // ask CentralServer to tell all other ConnectionServers on this galaxy to drop duplicate connections for this account + // and ask CentralServer (via LoginServer) to tell all ConnectionServers on other galaxies to drop duplicate connections for this account + if (!m_usingAdminLogin && !m_isSecure) { + GenericValueTypeMessage > const dropDuplicateConnections("ConnSrvDropDupeConns", + std::make_pair(m_suid, + m_sessionId)); + ConnectionServer::sendToCentralProcess(dropDuplicateConnections); + } } //----------------------------------------------------------------------- -std::string ClientConnection::describeAccount(const ClientConnection * c) -{ - std::string result = ""; - if(c) - { - char idbuf[512] = {"\0"}; - const std::string & sessionId = c->getSessionId(); - if (sessionId.empty()) - { - snprintf(idbuf, sizeof(idbuf), " (%lu)", c->m_suid); - } - else - { - snprintf(idbuf, sizeof(idbuf), " (%lu, %s)", c->m_suid, sessionId.c_str()); - } +std::string ClientConnection::describeAccount(const ClientConnection *c) { + std::string result = ""; + if (c) { + char idbuf[512] = {"\0"}; + const std::string &sessionId = c->getSessionId(); + if (sessionId.empty()) { + snprintf(idbuf, sizeof(idbuf), " (%lu)", c->m_suid); + } else { + snprintf(idbuf, sizeof(idbuf), " (%lu, %s)", c->m_suid, sessionId.c_str()); + } - result = c->m_accountName; - result += idbuf; - } - return result; + result = c->m_accountName; + result += idbuf; + } + return result; } // ---------------------------------------------------------------------- -std::vector > const & ClientConnection::getConsumedRewardEvents() const -{ - return m_consumedRewardEvents; +std::vector > const &ClientConnection::getConsumedRewardEvents() const { + return m_consumedRewardEvents; } // ---------------------------------------------------------------------- -std::vector > const & ClientConnection::getClaimedRewardItems() const -{ - return m_claimedRewardItems; +std::vector > const &ClientConnection::getClaimedRewardItems() const { + return m_claimedRewardItems; } // ---------------------------------------------------------------------- -bool ClientConnection::isUsingAdminLogin() const -{ - return m_usingAdminLogin; +bool ClientConnection::isUsingAdminLogin() const { + return m_usingAdminLogin; } // ---------------------------------------------------------------------- -int ClientConnection::getBuddyPoints() const -{ - return m_buddyPoints; +int ClientConnection::getBuddyPoints() const { + return m_buddyPoints; } // ====================================================================== diff --git a/engine/server/application/ConnectionServer/src/shared/ClientConnection.h b/engine/server/application/ConnectionServer/src/shared/ClientConnection.h index a0184cec..384f41b6 100755 --- a/engine/server/application/ConnectionServer/src/shared/ClientConnection.h +++ b/engine/server/application/ConnectionServer/src/shared/ClientConnection.h @@ -91,7 +91,7 @@ public: void handleGameServerForLoginMessage(uint32 serverId); void onIdValidated(bool canLogin, bool canCreateRegularCharacter, bool canCreateJediCharacter, bool canSkipTutorial, std::vector > const & consumedRewardEvents, std::vector > const & claimedRewardItems); - void onValidateClient (uint32 id, const std::string & username, bool, const char*, uint32 gameFeatures, uint32 subscriptionFeatures, unsigned int entitlementTotalTime, unsigned int entitlementEntitledTime, unsigned int entitlementTotalTimeSinceLastLogin, unsigned int entitlementEntitledTimeSinceLastLogin, int buddyPoints); + void onValidateClient (StationId id, const std::string & username, bool, const char*, uint32 gameFeatures, uint32 subscriptionFeatures, unsigned int entitlementTotalTime, unsigned int entitlementEntitledTime, unsigned int entitlementTotalTimeSinceLastLogin, unsigned int entitlementEntitledTimeSinceLastLogin, int buddyPoints); void onCharacterValidated(bool isValid, const NetworkId &character, const std::string &characterName, const NetworkId &container, const std::string &scene, const Vector &coordinates); static std::string describeAccount(const ClientConnection *); diff --git a/engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.cpp b/engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.cpp index b06dbe14..52d2ef2f 100755 --- a/engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.cpp +++ b/engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.cpp @@ -56,6 +56,7 @@ void ConfigConnectionServer::install(void) data = new ConfigConnectionServer::Data; + KEY_STRING (sessionURL, ""); KEY_STRING (centralServerAddress, "localhost"); KEY_INT (centralServerPort, 0); KEY_STRING (clientServiceBindInterface, ""); diff --git a/engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.h b/engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.h index 6f1ce686..114af1d8 100755 --- a/engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.h +++ b/engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.h @@ -2,477 +2,483 @@ // copyright 2000 Verant Interactive // Author: Justin Randall -#ifndef _ConfigConnectionServer_H -#define _ConfigConnectionServer_H +#ifndef _ConfigConnectionServer_H +#define _ConfigConnectionServer_H //----------------------------------------------------------------------- -class ConfigConnectionServer -{ +class ConfigConnectionServer { public: - struct Data - { - const char* centralServerAddress; - int centralServerPort; - int clientServicePortPublic; - int clientServicePortPrivate; - int clientOverflowLimit; - int gameServicePort; - const char * clusterName; - bool disableWorldSnapshot; - int maxClients; - int pingPort; - bool spamLimitEnabled; - int spamLimitResetTimeMs; - int spamLimitResetScaleFactor; - int spamLimitBytesPerSec; - int spamLimitPacketsPerSec; - bool startPublicServer; - const char * chatServiceBindInterface; - const char * clientServiceBindInterface; - const char * customerServiceBindInterface; - const char * gameServiceBindInterface; - bool compressClientNetworkTraffic; - int crashRecoveryTimeout; - bool shouldSleep; - int clientMaxOutstandingPackets; - int clientMaxRawPacketSize; - int clientMaxConnections; - int clientFragmentSize; - int clientMaxDataHoldTime; - int clientHashTableSize; - int lagReportThreshold; - int maxConnectionsPerIP; + struct Data { + const char *sessionURL; + const char *centralServerAddress; + int centralServerPort; + int clientServicePortPublic; + int clientServicePortPrivate; + int clientOverflowLimit; + int gameServicePort; + const char *clusterName; + bool disableWorldSnapshot; + int maxClients; + int pingPort; + bool spamLimitEnabled; + int spamLimitResetTimeMs; + int spamLimitResetScaleFactor; + int spamLimitBytesPerSec; + int spamLimitPacketsPerSec; + bool startPublicServer; + const char *chatServiceBindInterface; + const char *clientServiceBindInterface; + const char *customerServiceBindInterface; + const char *gameServiceBindInterface; + bool compressClientNetworkTraffic; + int crashRecoveryTimeout; + bool shouldSleep; + int clientMaxOutstandingPackets; + int clientMaxRawPacketSize; + int clientMaxConnections; + int clientFragmentSize; + int clientMaxDataHoldTime; + int clientHashTableSize; + int lagReportThreshold; + int maxConnectionsPerIP; - bool validateStationKey; - const char * sessionServers; - int sessionType; - bool disableSessionLogout; - bool sessionRecordPlayTime; - bool disconnectOnInactive; - bool disconnectFreeTrialOnInactive; - const char * adminAccountDataTable; + bool validateStationKey; + const char *sessionServers; + int sessionType; + bool disableSessionLogout; + bool sessionRecordPlayTime; + bool disconnectOnInactive; + bool disconnectFreeTrialOnInactive; + const char *adminAccountDataTable; - float timeBetweenSessionUpdates; - - int defaultGameFeatures; - int defaultSubscriptionFeatures; - int requiredSubscriptionBits; - int requiredGameBits; - bool setJtlRetailIfBetaIsSet; - bool setEpisode3RetailIfBetaIsSet; - bool setTrialsOfObiwanRetailIfBetaIsSet; + float timeBetweenSessionUpdates; - int disabledFeatureBits; + int defaultGameFeatures; + int defaultSubscriptionFeatures; + int requiredSubscriptionBits; + int requiredGameBits; + bool setJtlRetailIfBetaIsSet; + bool setEpisode3RetailIfBetaIsSet; + bool setTrialsOfObiwanRetailIfBetaIsSet; - bool validateClientVersion; + int disabledFeatureBits; - int connectionServerNumber; - int fakeBuddyPoints; + bool validateClientVersion; + + int connectionServerNumber; + int fakeBuddyPoints; - const char * altPublicBindAddress; - }; + const char *altPublicBindAddress; + }; - static const char * getCentralServerAddress (); - static const uint16 getCentralServerPort (); - static const int getClientOverflowLimit (); - static const char * getClientServiceBindInterface (); - static const uint16 getClientServicePortPrivate (); - static const uint16 getClientServicePortPublic (); - static const char * getClusterName (); - static bool getDisableWorldSnapshot (); - static const uint16 getGameServicePort (); - static const int getMaxClients (); - static const uint16 getPingPort (); - static const bool getSpamLimitEnabled (); - static const unsigned int getSpamLimitResetTimeMs (); - static const unsigned int getSpamLimitResetScaleFactor (); - static const unsigned int getSpamLimitBytesPerSec (); - static const unsigned int getSpamLimitPacketsPerSec (); - static const bool getStartPublicServer (); - static const char * getChatServiceBindInterface (); - static const char * getCustomerServiceBindInterface (); - static const char * getGameServiceBindInterface (); - static const bool getCompressClientNetworkTraffic (); - static void install (); - static void remove (); - static const uint getCrashRecoveryTimeout (); - static bool getShouldSleep (); - static const int getClientMaxOutstandingPackets(); - static const int getClientMaxRawPacketSize (); - static const int getClientMaxConnections (); - static const int getClientFragmentSize (); - static const int getClientMaxDataHoldTime (); - static const int getClientHashTableSize (); - static const int getLagReportThreshold (); + static const char *getSessionURL(); - static bool getValidateStationKey(); - static const char * getSessionServers(); - static const int getSessionType(); - static bool getDisableSessionLogout(); - static bool getSessionRecordPlayTime(); - static bool getDisconnectOnInactive(); - static bool getDisconnectFreeTrialOnInactive(); - - static const char * getAdminAccountDataTable (void); + static const char *getCentralServerAddress(); - static int getNumberOfSessionServers(); - static char const * getSessionServer(int index); - static float getTimeBetweenSessionUpdates(); + static const uint16 getCentralServerPort(); - static const uint32 getDefaultGameFeatures (); - static const uint32 getDefaultSubscriptionFeatures(); - static uint32 getRequiredSubscriptionBits(); - static uint32 getRequiredGameBits(); - static bool getSetJtlRetailIfBetaIsSet(); - static bool getSetEpisode3RetailIfBetaIsSet(); - static bool getSetTrialsOfObiwanRetailIfBetaIsSet(); + static const int getClientOverflowLimit(); - static int getDisabledFeatureBits(); + static const char *getClientServiceBindInterface(); - static bool getValidateClientVersion(); + static const uint16 getClientServicePortPrivate(); - static int getConnectionServerNumber(); - static int getFakeBuddyPoints(); + static const uint16 getClientServicePortPublic(); - static const char * getPublicBindAddress(); + static const char *getClusterName(); + + static bool getDisableWorldSnapshot(); + + static const uint16 getGameServicePort(); + + static const int getMaxClients(); + + static const uint16 getPingPort(); + + static const bool getSpamLimitEnabled(); + + static const unsigned int getSpamLimitResetTimeMs(); + + static const unsigned int getSpamLimitResetScaleFactor(); + + static const unsigned int getSpamLimitBytesPerSec(); + + static const unsigned int getSpamLimitPacketsPerSec(); + + static const bool getStartPublicServer(); + + static const char *getChatServiceBindInterface(); + + static const char *getCustomerServiceBindInterface(); + + static const char *getGameServiceBindInterface(); + + static const bool getCompressClientNetworkTraffic(); + + static void install(); + + static void remove(); + + static const uint getCrashRecoveryTimeout(); + + static bool getShouldSleep(); + + static const int getClientMaxOutstandingPackets(); + + static const int getClientMaxRawPacketSize(); + + static const int getClientMaxConnections(); + + static const int getClientFragmentSize(); + + static const int getClientMaxDataHoldTime(); + + static const int getClientHashTableSize(); + + static const int getLagReportThreshold(); + + static bool getValidateStationKey(); + + static const char *getSessionServers(); + + static const int getSessionType(); + + static bool getDisableSessionLogout(); + + static bool getSessionRecordPlayTime(); + + static bool getDisconnectOnInactive(); + + static bool getDisconnectFreeTrialOnInactive(); + + static const char *getAdminAccountDataTable(void); + + static int getNumberOfSessionServers(); + + static char const *getSessionServer(int index); + + static float getTimeBetweenSessionUpdates(); + + static const uint32 getDefaultGameFeatures(); + + static const uint32 getDefaultSubscriptionFeatures(); + + static uint32 getRequiredSubscriptionBits(); + + static uint32 getRequiredGameBits(); + + static bool getSetJtlRetailIfBetaIsSet(); + + static bool getSetEpisode3RetailIfBetaIsSet(); + + static bool getSetTrialsOfObiwanRetailIfBetaIsSet(); + + static int getDisabledFeatureBits(); + + static bool getValidateClientVersion(); + + static int getConnectionServerNumber(); + + static int getFakeBuddyPoints(); + + static const char *getPublicBindAddress(); + + static int getMaxConnectionsPerIP(); - static int getMaxConnectionsPerIP(); private: - static Data * data; + static Data *data; }; -//----------------------------------------------------------------------- -inline bool ConfigConnectionServer::getShouldSleep() -{ - return data->shouldSleep; +inline const char *ConfigConnectionServer::getSessionURL() { + return data->sessionURL; } //----------------------------------------------------------------------- -inline const bool ConfigConnectionServer::getCompressClientNetworkTraffic() -{ - return data->compressClientNetworkTraffic; +inline bool ConfigConnectionServer::getShouldSleep() { + return data->shouldSleep; } //----------------------------------------------------------------------- -inline const char * ConfigConnectionServer::getCentralServerAddress () -{ - return data->centralServerAddress; +inline const bool ConfigConnectionServer::getCompressClientNetworkTraffic() { + return data->compressClientNetworkTraffic; } //----------------------------------------------------------------------- -inline const uint16 ConfigConnectionServer::getCentralServerPort () -{ - return static_cast(data->centralServerPort); +inline const char *ConfigConnectionServer::getCentralServerAddress() { + return data->centralServerAddress; } //----------------------------------------------------------------------- -inline const char* ConfigConnectionServer::getClientServiceBindInterface() -{ - return data->clientServiceBindInterface; +inline const uint16 ConfigConnectionServer::getCentralServerPort() { + return static_cast(data->centralServerPort); } //----------------------------------------------------------------------- -inline const int ConfigConnectionServer::getClientOverflowLimit() -{ - return data->clientOverflowLimit; +inline const char *ConfigConnectionServer::getClientServiceBindInterface() { + return data->clientServiceBindInterface; } //----------------------------------------------------------------------- -inline const uint16 ConfigConnectionServer::getClientServicePortPrivate() -{ - return static_cast(data->clientServicePortPrivate); +inline const int ConfigConnectionServer::getClientOverflowLimit() { + return data->clientOverflowLimit; } //----------------------------------------------------------------------- -inline const uint16 ConfigConnectionServer::getClientServicePortPublic() -{ - return static_cast(data->clientServicePortPublic); +inline const uint16 ConfigConnectionServer::getClientServicePortPrivate() { + return static_cast(data->clientServicePortPrivate); } //----------------------------------------------------------------------- -inline const char * ConfigConnectionServer::getClusterName() -{ - return data->clusterName; +inline const uint16 ConfigConnectionServer::getClientServicePortPublic() { + return static_cast(data->clientServicePortPublic); } //----------------------------------------------------------------------- -inline bool ConfigConnectionServer::getDisableWorldSnapshot() -{ - return data->disableWorldSnapshot; +inline const char *ConfigConnectionServer::getClusterName() { + return data->clusterName; } //----------------------------------------------------------------------- -inline const uint16 ConfigConnectionServer::getGameServicePort() -{ - return static_cast(data->gameServicePort); +inline bool ConfigConnectionServer::getDisableWorldSnapshot() { + return data->disableWorldSnapshot; } //----------------------------------------------------------------------- -inline const int ConfigConnectionServer::getMaxClients() -{ - return data->maxClients; +inline const uint16 ConfigConnectionServer::getGameServicePort() { + return static_cast(data->gameServicePort); } //----------------------------------------------------------------------- -inline const uint16 ConfigConnectionServer::getPingPort () -{ - return static_cast(data->pingPort); +inline const int ConfigConnectionServer::getMaxClients() { + return data->maxClients; } //----------------------------------------------------------------------- -inline const bool ConfigConnectionServer::getSpamLimitEnabled () -{ - return data->spamLimitEnabled; +inline const uint16 ConfigConnectionServer::getPingPort() { + return static_cast(data->pingPort); } //----------------------------------------------------------------------- -inline const unsigned int ConfigConnectionServer::getSpamLimitResetTimeMs () -{ - return static_cast(data->spamLimitResetTimeMs); +inline const bool ConfigConnectionServer::getSpamLimitEnabled() { + return data->spamLimitEnabled; } //----------------------------------------------------------------------- -inline const unsigned int ConfigConnectionServer::getSpamLimitResetScaleFactor () -{ - return static_cast(data->spamLimitResetScaleFactor); +inline const unsigned int ConfigConnectionServer::getSpamLimitResetTimeMs() { + return static_cast(data->spamLimitResetTimeMs); } //----------------------------------------------------------------------- -inline const unsigned int ConfigConnectionServer::getSpamLimitBytesPerSec () -{ - return static_cast(data->spamLimitBytesPerSec); +inline const unsigned int ConfigConnectionServer::getSpamLimitResetScaleFactor() { + return static_cast(data->spamLimitResetScaleFactor); } //----------------------------------------------------------------------- -inline const unsigned int ConfigConnectionServer::getSpamLimitPacketsPerSec () -{ - return static_cast(data->spamLimitPacketsPerSec); +inline const unsigned int ConfigConnectionServer::getSpamLimitBytesPerSec() { + return static_cast(data->spamLimitBytesPerSec); } //----------------------------------------------------------------------- -inline const bool ConfigConnectionServer::getStartPublicServer() -{ - return data->startPublicServer; +inline const unsigned int ConfigConnectionServer::getSpamLimitPacketsPerSec() { + return static_cast(data->spamLimitPacketsPerSec); } //----------------------------------------------------------------------- -inline const char * ConfigConnectionServer::getChatServiceBindInterface() -{ - return data->chatServiceBindInterface; +inline const bool ConfigConnectionServer::getStartPublicServer() { + return data->startPublicServer; } //----------------------------------------------------------------------- -inline const char * ConfigConnectionServer::getCustomerServiceBindInterface() -{ - return data->customerServiceBindInterface; +inline const char *ConfigConnectionServer::getChatServiceBindInterface() { + return data->chatServiceBindInterface; } //----------------------------------------------------------------------- -inline const char * ConfigConnectionServer::getGameServiceBindInterface() -{ - return data->gameServiceBindInterface; +inline const char *ConfigConnectionServer::getCustomerServiceBindInterface() { + return data->customerServiceBindInterface; +} + +//----------------------------------------------------------------------- + +inline const char *ConfigConnectionServer::getGameServiceBindInterface() { + return data->gameServiceBindInterface; } // ---------------------------------------------------------------------- -inline const uint ConfigConnectionServer::getCrashRecoveryTimeout() -{ - return static_cast(data->crashRecoveryTimeout); +inline const uint ConfigConnectionServer::getCrashRecoveryTimeout() { + return static_cast(data->crashRecoveryTimeout); } //----------------------------------------------------------------------- -inline const int ConfigConnectionServer::getClientMaxOutstandingPackets() -{ - return data->clientMaxOutstandingPackets; +inline const int ConfigConnectionServer::getClientMaxOutstandingPackets() { + return data->clientMaxOutstandingPackets; } //----------------------------------------------------------------------- -inline const int ConfigConnectionServer::getClientMaxRawPacketSize() -{ - return data->clientMaxRawPacketSize; +inline const int ConfigConnectionServer::getClientMaxRawPacketSize() { + return data->clientMaxRawPacketSize; } //----------------------------------------------------------------------- -inline const int ConfigConnectionServer::getClientMaxConnections() -{ - return data->clientMaxConnections; +inline const int ConfigConnectionServer::getClientMaxConnections() { + return data->clientMaxConnections; } //----------------------------------------------------------------------- -inline const int ConfigConnectionServer::getClientFragmentSize() -{ - return data->clientFragmentSize; +inline const int ConfigConnectionServer::getClientFragmentSize() { + return data->clientFragmentSize; } //----------------------------------------------------------------------- -inline const int ConfigConnectionServer::getClientMaxDataHoldTime() -{ - return data->clientMaxDataHoldTime; +inline const int ConfigConnectionServer::getClientMaxDataHoldTime() { + return data->clientMaxDataHoldTime; } //----------------------------------------------------------------------- -inline const int ConfigConnectionServer::getClientHashTableSize() -{ - return data->clientHashTableSize; +inline const int ConfigConnectionServer::getClientHashTableSize() { + return data->clientHashTableSize; } //----------------------------------------------------------------------- -inline const int ConfigConnectionServer::getLagReportThreshold() -{ - return data->lagReportThreshold; +inline const int ConfigConnectionServer::getLagReportThreshold() { + return data->lagReportThreshold; } // ---------------------------------------------------------------------- //------------------------------------------------------------------------------------------ -inline bool ConfigConnectionServer::getValidateStationKey() -{ - return data->validateStationKey; +inline bool ConfigConnectionServer::getValidateStationKey() { + return data->validateStationKey; } //------------------------------------------------------------------------------------------ -inline const char * ConfigConnectionServer::getSessionServers() -{ - return data->sessionServers; +inline const char *ConfigConnectionServer::getSessionServers() { + return data->sessionServers; } //------------------------------------------------------------------------------------------ -inline const int ConfigConnectionServer::getSessionType() -{ - return data->sessionType; +inline const int ConfigConnectionServer::getSessionType() { + return data->sessionType; } //------------------------------------------------------------------------------------------ -inline bool ConfigConnectionServer::getDisableSessionLogout() -{ - return data->disableSessionLogout; +inline bool ConfigConnectionServer::getDisableSessionLogout() { + return data->disableSessionLogout; } //------------------------------------------------------------------------------------------ -inline bool ConfigConnectionServer::getSessionRecordPlayTime() -{ - return data->sessionRecordPlayTime; +inline bool ConfigConnectionServer::getSessionRecordPlayTime() { + return data->sessionRecordPlayTime; } // ---------------------------------------------------------------------- -inline const char * ConfigConnectionServer::getAdminAccountDataTable(void) -{ - return data->adminAccountDataTable; +inline const char *ConfigConnectionServer::getAdminAccountDataTable(void) { + return data->adminAccountDataTable; } //----------------------------------------------------------------------- -inline float ConfigConnectionServer::getTimeBetweenSessionUpdates() -{ - return data->timeBetweenSessionUpdates; +inline float ConfigConnectionServer::getTimeBetweenSessionUpdates() { + return data->timeBetweenSessionUpdates; } //----------------------------------------------------------------------- -inline uint32 ConfigConnectionServer::getRequiredSubscriptionBits() -{ - return static_cast(data->requiredSubscriptionBits); +inline uint32 ConfigConnectionServer::getRequiredSubscriptionBits() { + return static_cast(data->requiredSubscriptionBits); } //----------------------------------------------------------------------- -inline uint32 ConfigConnectionServer::getRequiredGameBits() -{ - return static_cast(data->requiredGameBits); +inline uint32 ConfigConnectionServer::getRequiredGameBits() { + return static_cast(data->requiredGameBits); } //----------------------------------------------------------------------- -inline const uint32 ConfigConnectionServer::getDefaultGameFeatures() -{ - return static_cast(data->defaultGameFeatures); +inline const uint32 ConfigConnectionServer::getDefaultGameFeatures() { + return static_cast(data->defaultGameFeatures); } // ---------------------------------------------------------------------- -inline const uint32 ConfigConnectionServer::getDefaultSubscriptionFeatures() -{ - return static_cast(data->defaultSubscriptionFeatures); +inline const uint32 ConfigConnectionServer::getDefaultSubscriptionFeatures() { + return static_cast(data->defaultSubscriptionFeatures); } //------------------------------------------------------------------------------------------ -inline bool ConfigConnectionServer::getSetJtlRetailIfBetaIsSet() -{ - return data->setJtlRetailIfBetaIsSet; +inline bool ConfigConnectionServer::getSetJtlRetailIfBetaIsSet() { + return data->setJtlRetailIfBetaIsSet; } //------------------------------------------------------------------------------------------ -inline bool ConfigConnectionServer::getSetEpisode3RetailIfBetaIsSet() -{ - return data->setEpisode3RetailIfBetaIsSet; +inline bool ConfigConnectionServer::getSetEpisode3RetailIfBetaIsSet() { + return data->setEpisode3RetailIfBetaIsSet; } //------------------------------------------------------------------------------------------ -inline bool ConfigConnectionServer::getSetTrialsOfObiwanRetailIfBetaIsSet() -{ - return data->setTrialsOfObiwanRetailIfBetaIsSet; +inline bool ConfigConnectionServer::getSetTrialsOfObiwanRetailIfBetaIsSet() { + return data->setTrialsOfObiwanRetailIfBetaIsSet; } //------------------------------------------------------------------------------------------ -inline bool ConfigConnectionServer::getValidateClientVersion() -{ - return data->validateClientVersion; +inline bool ConfigConnectionServer::getValidateClientVersion() { + return data->validateClientVersion; } // ---------------------------------------------------------------------- -inline int ConfigConnectionServer::getConnectionServerNumber() -{ - return data->connectionServerNumber; +inline int ConfigConnectionServer::getConnectionServerNumber() { + return data->connectionServerNumber; } -inline int ConfigConnectionServer::getMaxConnectionsPerIP() -{ - return data->maxConnectionsPerIP; +inline int ConfigConnectionServer::getMaxConnectionsPerIP() { + return data->maxConnectionsPerIP; } #endif // _ConfigConnectionServer_H diff --git a/engine/server/application/ConnectionServer/src/shared/ConnectionServer.cpp b/engine/server/application/ConnectionServer/src/shared/ConnectionServer.cpp index ef838c04..18a42389 100755 --- a/engine/server/application/ConnectionServer/src/shared/ConnectionServer.cpp +++ b/engine/server/application/ConnectionServer/src/shared/ConnectionServer.cpp @@ -98,7 +98,6 @@ ConnectionServer::ConnectionServer() : networkBarrier(0), pingSocket(new UdpSock), m_recoverTime(0), - m_sessionApiClient(0), m_pingTrafficNumBytes(0), m_recoveringClientList() { @@ -120,11 +119,6 @@ ConnectionServer::ConnectionServer() : Address a("", ConfigConnectionServer::getPingPort()); IGNORE_RETURN(pingSocket->bind(a)); - - if (ConfigConnectionServer::getValidateStationKey()) - { - installSessionValidation(); - } } //----------------------------------------------------------------------- @@ -1106,10 +1100,10 @@ void ConnectionServer::update() } } - if (m_sessionApiClient) + /*if (m_sessionApiClient) { m_sessionApiClient->update(); - } + }*/ static const int ping_throttle_max = 1024; @@ -1430,7 +1424,7 @@ CentralConnection * ConnectionServer::getCentralConnection() void ConnectionServer::installSessionValidation() { - int i = 0; + /*int i = 0; std::vector sessionServers; int const numberOfSessionServers = ConfigConnectionServer::getNumberOfSessionServers(); for (i = 0; i < numberOfSessionServers; ++i) @@ -1445,7 +1439,8 @@ void ConnectionServer::installSessionValidation() // if there were none specified, use defaults FATAL(i == 0, ("No session servers specified for session API")); - m_sessionApiClient = new SessionApiClient(&sessionServers[0], i); + m_sessionApiClient = new SessionApiClient(&sessionServers[0], i);*/ + return; } // ---------------------------------------------------------------------- @@ -1575,14 +1570,14 @@ SessionApiClient* ConnectionServer::getSessionApiClient() { // this is causing crashes when ConnectionServer is shutdown and something calls this function // because instance() returns 0. - if (s_connectionServer) - { - return instance().m_sessionApiClient; - } - else - { + //if (s_connectionServer) + //{ + // return instance().m_sessionApiClient; + //} + //else + //{ return 0; - } + //} } // ---------------------------------------------------------------------- diff --git a/engine/server/application/LoginServer/src/shared/ClientConnection.cpp b/engine/server/application/LoginServer/src/shared/ClientConnection.cpp index 18c3ebb2..55bd2094 100755 --- a/engine/server/application/LoginServer/src/shared/ClientConnection.cpp +++ b/engine/server/application/LoginServer/src/shared/ClientConnection.cpp @@ -58,12 +58,12 @@ void ClientConnection::onConnectionClosed() { LoginServer::getInstance().removeClient(m_clientId); - if ((ConfigLoginServer::getValidateStationKey() || ConfigLoginServer::getDoSessionLogin()) && !m_isValidated) { + /* if ((ConfigLoginServer::getValidateStationKey() || ConfigLoginServer::getDoSessionLogin()) && !m_isValidated) { SessionApiClient *session = LoginServer::getInstance().getSessionApiClient(); if (session) { session->dropClient(this); } - } + }*/ } @@ -167,17 +167,18 @@ void ClientConnection::onReceive(const Archive::ByteStream &message) { // originally was used to validate station API credentials, now uses our custom api void ClientConnection::validateClient(const std::string &id, const std::string &key) { bool authOK = false; - StationId suid = atoi(id.c_str()); static const std::string authURL(ConfigLoginServer::getExternalAuthUrl()); std::string uname; std::string parentAccount; - std::vector childAccounts; + std::string sessionID; + StationId user_id; + StationId parent_id; + std::unordered_map childAccounts; if (!authURL.empty()) { // create the object - webAPI api( - authURL); // TODO: is loginserver single threaded? if so then let's make this static, and clear/reset it each run + webAPI api(authURL); // add our data api.addJsonData("user_name", id); @@ -187,12 +188,16 @@ void ClientConnection::validateClient(const std::string &id, const std::string & if (api.submit()) { bool status = api.getNullableValue("status"); uname = api.getString("username"); + sessionID = api.getString("session_key"); - if (status && !uname.empty()) { + if (status && !sessionID.empty() && !uname.empty()) { authOK = true; parentAccount = api.getString("mainAccount"); - childAccounts = api.getStringVector("subAccounts"); + childAccounts = api.getStringMap("subAccounts"); + + user_id = static_cast(api.getNullableValue("user_id")); + parent_id = static_cast(api.getNullableValue("parent_id")); } else { std::string msg(api.getString("message")); if (msg.empty()) { @@ -213,55 +218,39 @@ void ClientConnection::validateClient(const std::string &id, const std::string & } if (authOK) { - if (suid == 0) { - if (uname.length() > MAX_ACCOUNT_NAME_LENGTH) - uname.resize(MAX_ACCOUNT_NAME_LENGTH); + REPORT_LOG(true, ("Client connected. Username: %s (%i) \n", uname.c_str(), user_id)); - std::hash hasher; - suid = hasher(uname.c_str()); + if (!parentAccount.empty()) { + if (parentAccount != uname) { + REPORT_LOG(true, ("\t%s's parent is %s (%i) \n", uname.c_str(), parentAccount.c_str(), parent_id)); + } + } else { + parentAccount = "(Empty Parent!) " + uname; } - REPORT_LOG(true, ("Client connected. Username: %s (%lu) \n", uname.c_str(), suid)); - - StationId parent = -1; - - if (!parentAccount.empty()) { - if (parentAccount.length() > MAX_ACCOUNT_NAME_LENGTH) - parentAccount.resize(MAX_ACCOUNT_NAME_LENGTH); - - std::hash hasher; - parent = hasher(parentAccount.c_str()); - - if (parentAccount != uname) { - REPORT_LOG(true, ("\t%s's parent is %s (%lu) \n", uname.c_str(), parentAccount.c_str(), parent)); - } - } else { - parentAccount = "(Empty Parent!) "+uname; - } - for (auto i : childAccounts) { - std::string child(i); + StationId child_id = static_cast(i.first); + std::string child(i.second); if (!child.empty()) { - if (child.length() > MAX_ACCOUNT_NAME_LENGTH) - child.resize(MAX_ACCOUNT_NAME_LENGTH); + REPORT_LOG((parent_id != child_id), + ("\tchild of %s (%i) is %s (%i) \n", parentAccount.c_str(), parent_id, child.c_str(), child_id)); - std::hash hasher; - StationId childID = hasher(child.c_str()); - - REPORT_LOG(true, ("\tchild of %s (%lu) is %s (%lu) \n", parentAccount.c_str(), parent, child.c_str(), childID)); - - // insert all related accounts, if not already there, into the db - DatabaseConnection::getInstance().upsertAccountRelationship(parent, childID); - } else { - WARNING(true, ("Login API returned empty child account(s).")); - } + // insert all related accounts, if not already there, into the db + if (parent_id != child_id) { + DatabaseConnection::getInstance().upsertAccountRelationship(parent_id, child_id); + } + } else { + WARNING(true, ("Login API returned empty child account(s).")); + } } LOG("LoginClientConnection", - ("validateClient() for stationId (%lu) at IP (%s), id (%s)", m_stationId, getRemoteAddress().c_str(), uname.c_str())); + ("validateClient() for stationId (%i) at IP (%s), id (%s)", user_id, getRemoteAddress().c_str(), uname.c_str())); - LoginServer::getInstance().onValidateClient(suid, uname, this, true, NULL, 0xFFFFFFFF, 0xFFFFFFFF); + m_stationId = user_id; + + LoginServer::getInstance().onValidateClient(m_stationId, uname, this, true, sessionID.c_str(), 0xFFFFFFFF, 0xFFFFFFFF); } } diff --git a/engine/server/application/LoginServer/src/shared/LoginServer.cpp b/engine/server/application/LoginServer/src/shared/LoginServer.cpp index db98bbb0..afa12138 100755 --- a/engine/server/application/LoginServer/src/shared/LoginServer.cpp +++ b/engine/server/application/LoginServer/src/shared/LoginServer.cpp @@ -140,7 +140,6 @@ LoginServer::LoginServer() : keyServer(0), m_clientMap(), m_clusterList(), - m_sessionApiClient(0), m_validatedClientMap(), m_clusterStatusChanged(false), m_soeMonitor(0) @@ -202,11 +201,6 @@ LoginServer::LoginServer() : connectToMessage("FeatureIdTransactionRequest"); connectToMessage("FeatureIdTransactionSyncUpdate"); keyServer = new KeyServer; - - if (ConfigLoginServer::getValidateStationKey() || ConfigLoginServer::getDoSessionLogin()) - { - installSessionValidation(); - } } //----------------------------------------------------------------------- @@ -269,7 +263,7 @@ void LoginServer::removeClient(int clientId) void LoginServer::installSessionValidation() { - int i = 0; + /*int i = 0; std::vector sessionServers; int numberOfSessionServers = ConfigLoginServer::getNumberOfSessionServers(); @@ -286,7 +280,9 @@ void LoginServer::installSessionValidation() // if there were none specified, use defaults FATAL(i == 0, ("No session servers specified for session API")); - m_sessionApiClient = new SessionApiClient(&sessionServers[0], i); + m_sessionApiClient = new SessionApiClient(&sessionServers[0], i);*/ + + return; } //----------------------------------------------------------------------- @@ -913,7 +909,7 @@ void LoginServer::receiveMessage(const MessageDispatch::Emitter & source, const { if (m_sessionApiClient) { - if (consumeAccountFeatureId) + /*if (consumeAccountFeatureId) { // request session/Platform to update the account feature id // SessionApiClient will own (and delete) msg @@ -931,7 +927,7 @@ void LoginServer::receiveMessage(const MessageDispatch::Emitter & source, const DatabaseConnection::getInstance().claimRewards(conn->getClusterId(), msg->getGameServer(), msg->getStationId(), msg->getPlayer(), msg->getRewardEvent(), msg->getConsumeEvent(), msg->getRewardItem(), msg->getConsumeItem(), requiredAccountFeatureId, false, oldFeature.GetConsumeCount(), newFeature.GetConsumeCount()); delete msg; - } + }*/ } else { @@ -1200,8 +1196,8 @@ void LoginServer::run(void) getInstance().m_clusterStatusChanged = false; } - if (getInstance().m_sessionApiClient) - getInstance().m_sessionApiClient->Process(); + //if (getInstance().m_sessionApiClient) + // getInstance().m_sessionApiClient->Process(); totalTime += limit; //TODO: make a better way to do this if (!ConfigLoginServer::getDevelopmentMode() && (totalTime > 10000)) @@ -1420,10 +1416,13 @@ void LoginServer::onValidateClient(StationId suid, const std::string & username, if (ConfigLoginServer::getDoConsumption() || ConfigLoginServer::getDoSessionLogin()) { + std::string const strSessionKey(sessionKey); + size_t sessSize = sizeof(strSessionKey.c_str()); + // pass the sessionkey - len = apiSessionIdWidth + sizeof(StationId); - memcpy(keyBufferPointer, sessionKey, apiSessionIdWidth); - keyBufferPointer += apiSessionIdWidth; + len = sessSize + sizeof(StationId); + memcpy(keyBufferPointer, sessionKey, sessSize); + keyBufferPointer += len; memcpy(keyBufferPointer, &suid, sizeof(StationId)); // if LoginServer did session login, send the session key back to the client; @@ -1431,7 +1430,6 @@ void LoginServer::onValidateClient(StationId suid, const std::string & username, // where the LoginServer does the session login, it will get it from the LoginServer if (ConfigLoginServer::getDoSessionLogin()) { - std::string const strSessionKey(sessionKey, apiSessionIdWidth); GenericValueTypeMessage const msg("SetSessionKey", strSessionKey); conn->send(msg, true); } diff --git a/engine/server/application/LoginServer/src/shared/PurgeManager.cpp b/engine/server/application/LoginServer/src/shared/PurgeManager.cpp index 2d649c52..043d468f 100755 --- a/engine/server/application/LoginServer/src/shared/PurgeManager.cpp +++ b/engine/server/application/LoginServer/src/shared/PurgeManager.cpp @@ -120,10 +120,7 @@ void PurgeManager::onGetAccountForPurge(StationId account, int purgePhase) PurgeRecord record(account, static_cast(purgePhase)); m_purgeRecords.insert(std::make_pair(account,record)); - if (ConfigLoginServer::getValidateStationKey()) - NON_NULL(LoginServer::getInstance().getSessionApiClient())->checkStatusForPurge(account); - else - onCheckStatusForPurge(account, false); + onCheckStatusForPurge(account, false); } } diff --git a/engine/server/application/TransferServer/src/shared/CentralServerConnection.cpp b/engine/server/application/TransferServer/src/shared/CentralServerConnection.cpp index ece4ff3d..ea3b1637 100755 --- a/engine/server/application/TransferServer/src/shared/CentralServerConnection.cpp +++ b/engine/server/application/TransferServer/src/shared/CentralServerConnection.cpp @@ -228,27 +228,32 @@ void CentralServerConnection::onReceive(const Archive::ByteStream & message) } case constcrc("TransferReplyNameValidation") : { - const GenericValueTypeMessage > replyNameValidation(ri); - if(!replyNameValidation.getValue().second.getIsMoveRequest()) + const GenericValueTypeMessage > replyNameValidation(ri); + auto i = replyNameValidation.getValue().begin(); + + if (i == replyNameValidation.getValue().end()) { + break; + } + + if(!i->second.getIsMoveRequest()) { - LOG("CustomerService", ("CharacterTransfer: Received replyNameValidation for move validation request. (%s) %s", replyNameValidation.getValue().first.c_str(), replyNameValidation.getValue().second.toString().c_str())); - TransferServer::replyValidateMove(replyNameValidation.getValue().second); + LOG("CustomerService", ("CharacterTransfer: Received replyNameValidation for move validation request. (%s) %s", i->first.c_str(), i->second.toString().c_str())); + TransferServer::replyValidateMove(i->second); } else { - if(TransferServer::isRename(replyNameValidation.getValue().second)) + if(TransferServer::isRename(i->second)) { - LOG("CustomerService", ("CharacterTransfer: Received replyNameValidation for rename request, starting character rename protocol. (%s) %s", replyNameValidation.getValue().first.c_str(), replyNameValidation.getValue().second.toString().c_str())); - const GenericValueTypeMessage renameCharacter("TransferRenameCharacter", replyNameValidation.getValue().second); - CentralServerConnection * centralServerConnection = CentralServerConnection::getCentralServerConnectionForGalaxy(replyNameValidation.getValue().second.getSourceGalaxy()); + LOG("CustomerService", ("CharacterTransfer: Received replyNameValidation for rename request, starting character rename protocol. (%s) %s", i->first.c_str(), i->second.toString().c_str())); + const GenericValueTypeMessage renameCharacter("TransferRenameCharacter", i->second); + CentralServerConnection * centralServerConnection = CentralServerConnection::getCentralServerConnectionForGalaxy(i->second.getSourceGalaxy()); if(centralServerConnection) { centralServerConnection->send(renameCharacter, true); } else { - TransferServer::transferCreateCharacterFailed(replyNameValidation.getValue().second); - } + TransferServer::transferCreateCharacterFailed(i->second); } } else { diff --git a/engine/server/library/serverGame/src/shared/object/TangibleObject_Conversation.cpp b/engine/server/library/serverGame/src/shared/object/TangibleObject_Conversation.cpp index 3d041780..ec4373bc 100755 --- a/engine/server/library/serverGame/src/shared/object/TangibleObject_Conversation.cpp +++ b/engine/server/library/serverGame/src/shared/object/TangibleObject_Conversation.cpp @@ -189,7 +189,7 @@ void TangibleObject::endNpcConversation() } else { - WARNING(true,("TangibleObject::endNpcConversation: creature %s has a nullptr m_npcConversation pointer but is a player-controlled object!", + DEBUG_WARNING(true,("TangibleObject::endNpcConversation: creature %s has a nullptr m_npcConversation pointer but is a player-controlled object!", getNetworkId().getValueString().c_str())); m_conversations.clear(); } diff --git a/engine/server/library/serverNetworkMessages/src/shared/transferServer/TransferCharacterData.h b/engine/server/library/serverNetworkMessages/src/shared/transferServer/TransferCharacterData.h index 528f873d..d4e3109a 100755 --- a/engine/server/library/serverNetworkMessages/src/shared/transferServer/TransferCharacterData.h +++ b/engine/server/library/serverNetworkMessages/src/shared/transferServer/TransferCharacterData.h @@ -103,10 +103,8 @@ public: void setWorkingSkill (const std::string & workingSkill); void setCSToolId (const unsigned int toolId); - -private: TransferCharacterData(); - +private: friend class Archive::AutoVariable; friend struct std::pair; friend void Archive::get(Archive::ReadIterator & source, TransferCharacterData & target); diff --git a/engine/shared/library/sharedDebug/src/linux/DebugHelp.h b/engine/shared/library/sharedDebug/src/linux/DebugHelp.h index 5068ea1d..972b4979 100755 --- a/engine/shared/library/sharedDebug/src/linux/DebugHelp.h +++ b/engine/shared/library/sharedDebug/src/linux/DebugHelp.h @@ -12,10 +12,6 @@ // ====================================================================== -typedef unsigned long uint32; - -// ====================================================================== - class DebugHelp { public: diff --git a/external/3rd/library/platform/projects/Session/CommonAPI/CommonAPI.h b/external/3rd/library/platform/projects/Session/CommonAPI/CommonAPI.h index aaa9d668..fa6e214d 100755 --- a/external/3rd/library/platform/projects/Session/CommonAPI/CommonAPI.h +++ b/external/3rd/library/platform/projects/Session/CommonAPI/CommonAPI.h @@ -306,7 +306,7 @@ class apiSubscription unsigned mParentalLimitSeconds; }; -static const int apiSessionIdWidth = 17; +static const int apiSessionIdWidth = 45; struct apiSession_v1; class apiSession { diff --git a/external/3rd/library/webAPI/json.hpp b/external/3rd/library/webAPI/json.hpp index 878fb899..f8d948f1 100644 --- a/external/3rd/library/webAPI/json.hpp +++ b/external/3rd/library/webAPI/json.hpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.9 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . @@ -29,30 +29,32 @@ SOFTWARE. #ifndef NLOHMANN_JSON_HPP #define NLOHMANN_JSON_HPP -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include // all_of, for_each, transform +#include // array +#include // assert +#include // isdigit +#include // and, not, or +#include // isfinite, ldexp, signbit +#include // nullptr_t, ptrdiff_t, size_t +#include // int64_t, uint64_t +#include // strtod, strtof, strtold, strtoul +#include // strlen +#include // function, hash, less +#include // initializer_list +#include // setw +#include // istream, ostream +#include // advance, begin, bidirectional_iterator_tag, distance, end, inserter, iterator, iterator_traits, next, random_access_iterator_tag, reverse_iterator +#include // numeric_limits +#include // locale +#include // map +#include // addressof, allocator, allocator_traits, unique_ptr +#include // accumulate +#include // stringstream +#include // domain_error, invalid_argument, out_of_range +#include // getline, stoi, string, to_string +#include // add_pointer, enable_if, is_arithmetic, is_base_of, is_const, is_constructible, is_convertible, is_floating_point, is_integral, is_nothrow_move_assignable, std::is_nothrow_move_constructible, std::is_pointer, std::is_reference, std::is_same, remove_const, remove_pointer, remove_reference +#include // declval, forward, make_pair, move, pair, swap +#include // vector // exclude unsupported compilers #if defined(__clang__) @@ -73,6 +75,21 @@ SOFTWARE. #pragma GCC diagnostic ignored "-Wfloat-equal" #endif +// disable documentation warnings on clang +#if defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdocumentation" +#endif + +// allow for portable deprecation warnings +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #define JSON_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) + #define JSON_DEPRECATED __declspec(deprecated) +#else + #define JSON_DEPRECATED +#endif + /*! @brief namespace for Niels Lohmann @see https://github.com/nlohmann @@ -96,36 +113,19 @@ such as sequence containers. For instance, `std::map` passes the test as it contains a `mapped_type`, whereas `std::vector` fails the test. @sa http://stackoverflow.com/a/7728728/266378 -@since version 1.0.0 +@since version 1.0.0, overworked in version 2.0.6 */ template struct has_mapped_type { private: - template static char test(typename C::mapped_type*); - template static char (&test(...))[2]; + template + static int detect(U&&); + + static void detect(...); public: - static constexpr bool value = sizeof(test(0)) == 1; -}; - -/*! -@brief helper class to create locales with decimal point - -This struct is used a default locale during the JSON serialization. JSON -requires the decimal point to be `.`, so this function overloads the -`do_decimal_point()` function to return `.`. This function is called by -float-to-string conversions to retrieve the decimal separator between integer -and fractional parts. - -@sa https://github.com/nlohmann/json/issues/51#issuecomment-86869315 -@since version 2.0.0 -*/ -struct DecimalSeparator : std::numpunct -{ - char do_decimal_point() const - { - return '.'; - } + static constexpr bool value = + std::is_integral()))>::value; }; } @@ -228,6 +228,7 @@ class basic_json public: // forward declarations + template class iter_impl; template class json_reverse_iterator; class json_pointer; @@ -262,9 +263,9 @@ class basic_json using const_pointer = typename std::allocator_traits::const_pointer; /// an iterator for a basic_json container - class iterator; + using iterator = iter_impl; /// a const iterator for a basic_json container - class const_iterator; + using const_iterator = iter_impl; /// a reverse iterator for a basic_json container using reverse_iterator = json_reverse_iterator; /// a const reverse iterator for a basic_json container @@ -950,7 +951,7 @@ class basic_json With a parser callback function, the result of parsing a JSON text can be influenced. When passed to @ref parse(std::istream&, const - parser_callback_t) or @ref parse(const string_t&, const parser_callback_t), + parser_callback_t) or @ref parse(const CharT, const parser_callback_t), it is called on certain events (passed as @ref parse_event_t via parameter @a event) with a set recursion depth @a depth and context JSON value @a parsed. The return value of the callback function is a boolean @@ -993,7 +994,7 @@ class basic_json skipped completely or replaced by an empty discarded object. @sa @ref parse(std::istream&, parser_callback_t) or - @ref parse(const string_t&, parser_callback_t) for examples + @ref parse(const CharT, const parser_callback_t) for examples @since version 1.0.0 */ @@ -1057,40 +1058,10 @@ class basic_json } /*! - @brief create a null object (implicitly) + @brief create a null object - Create a `null` JSON value. This is the implicit version of the `null` - value constructor as it takes no parameters. - - @note The class invariant is satisfied, because it poses no requirements - for null values. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this constructor never throws - exceptions. - - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - As postcondition, it holds: `basic_json().empty() == true`. - - @liveexample{The following code shows the constructor for a `null` JSON - value.,basic_json} - - @sa @ref basic_json(std::nullptr_t) -- create a `null` value - - @since version 1.0.0 - */ - basic_json() = default; - - /*! - @brief create a null object (explicitly) - - Create a `null` JSON value. This is the explicitly version of the `null` - value constructor as it takes a null pointer as parameter. It allows to - create `null` values by explicitly assigning a `nullptr` to a JSON value. + Create a `null` JSON value. It either takes a null pointer as parameter + (explicitly creating `null`) or no parameter (implicitly creating `null`). The passed null pointer itself is not read -- it is only used to choose the right constructor. @@ -1099,15 +1070,12 @@ class basic_json @exceptionsafety No-throw guarantee: this constructor never throws exceptions. - @liveexample{The following code shows the constructor with null pointer - parameter.,basic_json__nullptr_t} - - @sa @ref basic_json() -- default constructor (implicitly creating a `null` - value) + @liveexample{The following code shows the constructor with and without a + null pointer parameter.,basic_json__nullptr_t} @since version 1.0.0 */ - basic_json(std::nullptr_t) noexcept + basic_json(std::nullptr_t = nullptr) noexcept : basic_json(value_t::null) { assert_invariant(); @@ -1164,11 +1132,9 @@ class basic_json @since version 1.0.0 */ - template ::value and - std::is_constructible::value, int>::type - = 0> + template::value and + std::is_constructible::value, int>::type = 0> basic_json(const CompatibleObjectType& val) : m_type(value_t::object) { @@ -1229,16 +1195,14 @@ class basic_json @since version 1.0.0 */ - template ::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - std::is_constructible::value, int>::type - = 0> + template::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + std::is_constructible::value, int>::type = 0> basic_json(const CompatibleArrayType& val) : m_type(value_t::array) { @@ -1324,10 +1288,8 @@ class basic_json @since version 1.0.0 */ - template ::value, int>::type - = 0> + template::value, int>::type = 0> basic_json(const CompatibleStringType& val) : basic_json(string_t(val)) { @@ -1377,12 +1339,9 @@ class basic_json @since version 1.0.0 */ - template::value) - and std::is_same::value - , int>::type - = 0> + template::value) and + std::is_same::value, int>::type = 0> basic_json(const number_integer_t val) noexcept : m_type(value_t::number_integer), m_value(val) { @@ -1446,13 +1405,11 @@ class basic_json @since version 1.0.0 */ - template::value and std::numeric_limits::is_integer and std::numeric_limits::is_signed, - CompatibleNumberIntegerType>::type - = 0> + CompatibleNumberIntegerType>::type = 0> basic_json(const CompatibleNumberIntegerType val) noexcept : m_type(value_t::number_integer), m_value(static_cast(val)) @@ -1477,12 +1434,9 @@ class basic_json @since version 2.0.0 */ - template::value) - and std::is_same::value - , int>::type - = 0> + template::value) and + std::is_same::value, int>::type = 0> basic_json(const number_unsigned_t val) noexcept : m_type(value_t::number_unsigned), m_value(val) { @@ -1509,13 +1463,11 @@ class basic_json @since version 2.0.0 */ - template ::value and - std::numeric_limits::is_integer and - not std::numeric_limits::is_signed, - CompatibleNumberUnsignedType>::type - = 0> + template::value and + std::numeric_limits::is_integer and + not std::numeric_limits::is_signed, + CompatibleNumberUnsignedType>::type = 0> basic_json(const CompatibleNumberUnsignedType val) noexcept : m_type(value_t::number_unsigned), m_value(static_cast(val)) @@ -1591,11 +1543,9 @@ class basic_json @since version 1.0.0 */ - template::value and - std::is_floating_point::value>::type - > + std::is_floating_point::value>::type> basic_json(const CompatibleNumberFloatType val) noexcept : basic_json(number_float_t(val)) { @@ -1843,7 +1793,8 @@ class basic_json @param[in] first begin of the range to copy from (included) @param[in] last end of the range to copy from (excluded) - @pre Iterators @a first and @a last must be initialized. + @pre Iterators @a first and @a last must be initialized. **This + precondition is enforced with an assertion.** @throw std::domain_error if iterators are not compatible; that is, do not belong to the same JSON value; example: `"iterators are not compatible"` @@ -1861,12 +1812,9 @@ class basic_json @since version 1.0.0 */ - template ::value or - std::is_same::value - , int>::type - = 0> + template::value or + std::is_same::value, int>::type = 0> basic_json(InputIT first, InputIT last) { assert(first.m_object != nullptr); @@ -1970,12 +1918,21 @@ class basic_json @note A UTF-8 byte order mark is silently ignored. + @deprecated This constructor is deprecated and will be removed in version + 3.0.0 to unify the interface of the library. Deserialization will be + done by stream operators or by calling one of the `parse` functions, + e.g. @ref parse(std::istream&, const parser_callback_t). That is, calls + like `json j(i);` for an input stream @a i need to be replaced by + `json j = json::parse(i);`. See the example below. + @liveexample{The example below demonstrates constructing a JSON value from a `std::stringstream` with and without callback function.,basic_json__istream} - @since version 2.0.0 + @since version 2.0.0, deprecated in version 2.0.3, to be removed in + version 3.0.0 */ + JSON_DEPRECATED explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr) { *this = parser(i, cb).parse(); @@ -2231,7 +2188,7 @@ class basic_json { std::stringstream ss; // fix locale problems - ss.imbue(std::locale(std::locale(), new DecimalSeparator)); + ss.imbue(std::locale::classic()); // 6, 15 or 16 digits of precision allows round-trip IEEE 754 // string->float->string, string->double->string or string->long @@ -2614,11 +2571,9 @@ class basic_json ////////////////// /// get an object (explicit) - template ::value and - std::is_convertible::value - , int>::type = 0> + template::value and + std::is_convertible::value, int>::type = 0> T get_impl(T*) const { if (is_object()) @@ -2645,14 +2600,12 @@ class basic_json } /// get an array (explicit) - template ::value and - not std::is_same::value and - not std::is_arithmetic::value and - not std::is_convertible::value and - not has_mapped_type::value - , int>::type = 0> + template::value and + not std::is_same::value and + not std::is_arithmetic::value and + not std::is_convertible::value and + not has_mapped_type::value, int>::type = 0> T get_impl(T*) const { if (is_array()) @@ -2672,11 +2625,9 @@ class basic_json } /// get an array (explicit) - template ::value and - not std::is_same::value - , int>::type = 0> + template::value and + not std::is_same::value, int>::type = 0> std::vector get_impl(std::vector*) const { if (is_array()) @@ -2697,11 +2648,9 @@ class basic_json } /// get an array (explicit) - template ::value and - not has_mapped_type::value - , int>::type = 0> + template::value and + not has_mapped_type::value, int>::type = 0> T get_impl(T*) const { if (is_array()) @@ -2728,10 +2677,8 @@ class basic_json } /// get a string (explicit) - template ::value - , int>::type = 0> + template::value, int>::type = 0> T get_impl(T*) const { if (is_string()) @@ -2745,10 +2692,8 @@ class basic_json } /// get a number (explicit) - template::value - , int>::type = 0> + template::value, int>::type = 0> T get_impl(T*) const { switch (m_type) @@ -2937,10 +2882,8 @@ class basic_json @since version 1.0.0 */ - template::value - , int>::type = 0> + template::value, int>::type = 0> ValueType get() const { return get_impl(static_cast(nullptr)); @@ -2973,10 +2916,8 @@ class basic_json @since version 1.0.0 */ - template::value - , int>::type = 0> + template::value, int>::type = 0> PointerType get() noexcept { // delegate the call to get_ptr @@ -2987,10 +2928,8 @@ class basic_json @brief get a pointer value (explicit) @copydoc get() */ - template::value - , int>::type = 0> + template::value, int>::type = 0> constexpr const PointerType get() const noexcept { // delegate the call to get_ptr @@ -3023,10 +2962,8 @@ class basic_json @since version 1.0.0 */ - template::value - , int>::type = 0> + template::value, int>::type = 0> PointerType get_ptr() noexcept { // get the type of the PointerType (remove pointer and const) @@ -3052,11 +2989,9 @@ class basic_json @brief get a pointer value (implicit) @copydoc get_ptr() */ - template::value - and std::is_const::type>::value - , int>::type = 0> + template::value and + std::is_const::type>::value, int>::type = 0> constexpr const PointerType get_ptr() const noexcept { // get the type of the PointerType (remove pointer and const) @@ -3104,10 +3039,8 @@ class basic_json @since version 1.1.0 */ - template::value - , int>::type = 0> + template::value, int>::type = 0> ReferenceType get_ref() { // delegate call to get_ref_impl @@ -3118,11 +3051,9 @@ class basic_json @brief get a reference value (implicit) @copydoc get_ref() */ - template::value - and std::is_const::type>::value - , int>::type = 0> + template::value and + std::is_const::type>::value, int>::type = 0> ReferenceType get_ref() const { // delegate call to get_ref_impl @@ -3157,10 +3088,9 @@ class basic_json @since version 1.0.0 */ - template < typename ValueType, typename - std::enable_if < - not std::is_pointer::value - and not std::is_same::value + template < typename ValueType, typename std::enable_if < + not std::is_pointer::value and + not std::is_same::value #ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015 and not std::is_same>::value #endif @@ -3509,6 +3439,9 @@ class basic_json @return const reference to the element at key @a key + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + @throw std::domain_error if JSON is not an object; example: `"cannot use operator[] with null"` @@ -3667,6 +3600,9 @@ class basic_json @return const reference to the element at key @a key + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + @throw std::domain_error if JSON is not an object; example: `"cannot use operator[] with null"` @@ -3744,10 +3680,8 @@ class basic_json @since version 1.0.0 */ - template ::value - , int>::type = 0> + template::value, int>::type = 0> ValueType value(const typename object_t::key_type& key, ValueType default_value) const { // at only works for objects @@ -3820,10 +3754,8 @@ class basic_json @since version 2.0.2 */ - template ::value - , int>::type = 0> + template::value, int>::type = 0> ValueType value(const json_pointer& ptr, ValueType default_value) const { // at only works for objects @@ -3861,13 +3793,14 @@ class basic_json container `c`, the expression `c.front()` is equivalent to `*c.begin()`. @return In case of a structured type (array or object), a reference to the - first element is returned. In cast of number, string, or boolean values, a + first element is returned. In case of number, string, or boolean values, a reference to the value is returned. @complexity Constant. @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, guarded by assertions). + or an empty array or object (undefined behavior, **guarded by + assertions**). @post The JSON value remains unchanged. @throw std::out_of_range when called on `null` value @@ -3903,13 +3836,14 @@ class basic_json @endcode @return In case of a structured type (array or object), a reference to the - last element is returned. In cast of number, string, or boolean values, a + last element is returned. In case of number, string, or boolean values, a reference to the value is returned. @complexity Constant. @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, guarded by assertions). + or an empty array or object (undefined behavior, **guarded by + assertions**). @post The JSON value remains unchanged. @throw std::out_of_range when called on `null` value. @@ -3951,7 +3885,7 @@ class basic_json @return Iterator following the last removed element. If the iterator @a pos refers to the last element, the `end()` iterator is returned. - @tparam InteratorType an @ref iterator or @ref const_iterator + @tparam IteratorType an @ref iterator or @ref const_iterator @post Invalidates iterators and references at or after the point of the erase, including the `end()` iterator. @@ -3973,7 +3907,7 @@ class basic_json @liveexample{The example shows the result of `erase()` for different JSON types.,erase__IteratorType} - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in the given range @sa @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key @@ -3982,13 +3916,11 @@ class basic_json @since version 1.0.0 */ - template ::value or - std::is_same::value - , int>::type - = 0> - InteratorType erase(InteratorType pos) + template::value or + std::is_same::value, int>::type + = 0> + IteratorType erase(IteratorType pos) { // make sure iterator fits the current value if (this != pos.m_object) @@ -3996,7 +3928,7 @@ class basic_json throw std::domain_error("iterator does not fit current value"); } - InteratorType result = end(); + IteratorType result = end(); switch (m_type) { @@ -4060,7 +3992,7 @@ class basic_json @return Iterator following the last removed element. If the iterator @a second refers to the last element, the `end()` iterator is returned. - @tparam InteratorType an @ref iterator or @ref const_iterator + @tparam IteratorType an @ref iterator or @ref const_iterator @post Invalidates iterators and references at or after the point of the erase, including the `end()` iterator. @@ -4083,7 +4015,7 @@ class basic_json @liveexample{The example shows the result of `erase()` for different JSON types.,erase__IteratorType_IteratorType} - @sa @ref erase(InteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType) -- removes the element at a given position @sa @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key @sa @ref erase(const size_type) -- removes the element from an array at @@ -4091,13 +4023,11 @@ class basic_json @since version 1.0.0 */ - template ::value or - std::is_same::value - , int>::type - = 0> - InteratorType erase(InteratorType first, InteratorType last) + template::value or + std::is_same::value, int>::type + = 0> + IteratorType erase(IteratorType first, IteratorType last) { // make sure iterator fits the current value if (this != first.m_object or this != last.m_object) @@ -4105,7 +4035,7 @@ class basic_json throw std::domain_error("iterators do not fit current value"); } - InteratorType result = end(); + IteratorType result = end(); switch (m_type) { @@ -4177,8 +4107,8 @@ class basic_json @liveexample{The example shows the effect of `erase()`.,erase__key_type} - @sa @ref erase(InteratorType) -- removes the element at a given position - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in the given range @sa @ref erase(const size_type) -- removes the element from an array at the given index @@ -4214,8 +4144,8 @@ class basic_json @liveexample{The example shows the effect of `erase()`.,erase__size_type} - @sa @ref erase(InteratorType) -- removes the element at a given position - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in the given range @sa @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key @@ -4257,10 +4187,14 @@ class basic_json element is not found or the JSON value is not an object, end() is returned. + @note This method always returns @ref end() when executed on a JSON type + that is not an object. + @param[in] key key value of the element to search for @return Iterator to an element with key equivalent to @a key. If no such - element is found, past-the-end (see end()) iterator is returned. + element is found or the JSON value is not an object, past-the-end (see + @ref end()) iterator is returned. @complexity Logarithmic in the size of the JSON object. @@ -4303,6 +4237,9 @@ class basic_json default `std::map` type, the return value will always be `0` (@a key was not found) or `1` (@a key was found). + @note This method always returns `0` when executed on a JSON type that is + not an object. + @param[in] key key value of the element to count @return Number of elements with key @a key. If the JSON value is not an @@ -4862,9 +4799,6 @@ class basic_json object | `{}` array | `[]` - @note Floating-point numbers are set to `0.0` which will be serialized to - `0`. The vale type remains @ref number_float_t. - @complexity Linear in the size of the JSON value. @liveexample{The example below shows the effect of `clear()` to different @@ -5109,6 +5043,102 @@ class basic_json return *this; } + /*! + @brief add an object to an array + + Creates a JSON value from the passed parameters @a args to the end of the + JSON value. If the function is called on a JSON null value, an empty array + is created before appending the value created from @a args. + + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object + + @throw std::domain_error when called on a type other than JSON array or + null; example: `"cannot use emplace_back() with number"` + + @complexity Amortized constant. + + @liveexample{The example shows how `push_back()` can be used to add + elements to a JSON array. Note how the `null` value was silently converted + to a JSON array.,emplace_back} + + @since version 2.0.8 + */ + template + void emplace_back(Args&& ... args) + { + // emplace_back only works for null objects or arrays + if (not(is_null() or is_array())) + { + throw std::domain_error("cannot use emplace_back() with " + type_name()); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (perfect forwarding) + m_value.array->emplace_back(std::forward(args)...); + } + + /*! + @brief add an object to an object if key does not exist + + Inserts a new element into a JSON object constructed in-place with the given + @a args if there is no element with the key in the container. If the + function is called on a JSON null value, an empty object is created before + appending the value created from @a args. + + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object + + @return a pair consisting of an iterator to the inserted element, or the + already-existing element if no insertion happened, and a bool + denoting whether the insertion took place. + + @throw std::domain_error when called on a type other than JSON object or + null; example: `"cannot use emplace() with number"` + + @complexity Logarithmic in the size of the container, O(log(`size()`)). + + @liveexample{The example shows how `emplace()` can be used to add elements + to a JSON object. Note how the `null` value was silently converted to a + JSON object. Further note how no value is added if there was already one + value stored with the same key.,emplace} + + @since version 2.0.8 + */ + template + std::pair emplace(Args&& ... args) + { + // emplace only works for null objects or arrays + if (not(is_null() or is_object())) + { + throw std::domain_error("cannot use emplace() with " + type_name()); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to array (perfect forwarding) + auto res = m_value.object->emplace(std::forward(args)...); + // create result iterator and set iterator to the result of emplace + auto it = begin(); + it.m_it.object_iterator = res.first; + + // return pair of iterator and boolean + return {it, res.second}; + } + /*! @brief inserts element @@ -5885,7 +5915,7 @@ class basic_json o.width(0); // fix locale problems - const auto old_locale = o.imbue(std::locale(std::locale(), new DecimalSeparator)); + const auto old_locale = o.imbue(std::locale::classic()); // set precision // 6, 15 or 16 digits of precision allows round-trip IEEE 754 @@ -5923,10 +5953,16 @@ class basic_json /// @{ /*! - @brief deserialize from string + @brief deserialize from an array - @param[in] s string to read a serialized JSON value from - @param[in] cb a parser callback function of type @ref parser_callback_t + This function reads from an array of 1-byte values. + + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @param[in] array array to read from + @param[in] cb a parser callback function of type @ref parser_callback_t which is used to control the deserialization by filtering unwanted values (optional) @@ -5938,18 +5974,54 @@ class basic_json @note A UTF-8 byte order mark is silently ignored. + @liveexample{The example below demonstrates the `parse()` function reading + from an array.,parse__array__parser_callback_t} + + @since version 2.0.3 + */ + template + static basic_json parse(T (&array)[N], + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(array), std::end(array), cb); + } + + /*! + @brief deserialize from string literal + + @tparam CharT character/literal type with size of 1 byte + @param[in] s string literal to read a serialized JSON value from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + @note String containers like `std::string` or @ref string_t can be parsed + with @ref parse(const ContiguousContainer&, const parser_callback_t) + @liveexample{The example below demonstrates the `parse()` function with and without callback function.,parse__string__parser_callback_t} @sa @ref parse(std::istream&, const parser_callback_t) for a version that reads from an input stream - @since version 1.0.0 + @since version 1.0.0 (originally for @ref string_t) */ - static basic_json parse(const string_t& s, + template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> + static basic_json parse(const CharT s, const parser_callback_t cb = nullptr) { - return parser(s, cb).parse(); + return parser(reinterpret_cast(s), cb).parse(); } /*! @@ -5971,7 +6043,7 @@ class basic_json @liveexample{The example below demonstrates the `parse()` function with and without callback function.,parse__istream__parser_callback_t} - @sa @ref parse(const string_t&, const parser_callback_t) for a version + @sa @ref parse(const CharT, const parser_callback_t) for a version that reads from a string @since version 1.0.0 @@ -5991,6 +6063,130 @@ class basic_json return parser(i, cb).parse(); } + /*! + @brief deserialize from an iterator range with contiguous storage + + This function reads from an iterator range of a container with contiguous + storage of 1-byte values. Compatible container types include + `std::vector`, `std::string`, `std::array`, `std::valarray`, and + `std::initializer_list`. Furthermore, C-style arrays can be used with + `std::begin()`/`std::end()`. User-defined containers can be used as long + as they implement random-access iterators and a contiguous storage. + + @pre The iterator range is contiguous. Violating this precondition yields + undefined behavior. **This precondition is enforced with an assertion.** + @pre Each element in the range has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @warning There is no way to enforce all preconditions at compile-time. If + the function is called with noncompliant iterators and with + assertions switched off, the behavior is undefined and will most + likely yield segmentation violation. + + @tparam IteratorType iterator of container with contiguous storage + @param[in] first begin of the range to parse (included) + @param[in] last end of the range to parse (excluded) + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from an iterator range.,parse__iteratortype__parser_callback_t} + + @since version 2.0.3 + */ + template::iterator_category>::value, int>::type = 0> + static basic_json parse(IteratorType first, IteratorType last, + const parser_callback_t cb = nullptr) + { + // assertion to check that the iterator range is indeed contiguous, + // see http://stackoverflow.com/a/35008842/266378 for more discussion + assert(std::accumulate(first, last, std::make_pair(true, 0), + [&first](std::pair res, decltype(*first) val) + { + res.first &= (val == *(std::next(std::addressof(*first), res.second++))); + return res; + }).first); + + // assertion to check that each element is 1 byte long + static_assert(sizeof(typename std::iterator_traits::value_type) == 1, + "each element in the iterator range must have the size of 1 byte"); + + // if iterator range is empty, create a parser with an empty string + // to generate "unexpected EOF" error message + if (std::distance(first, last) <= 0) + { + return parser("").parse(); + } + + return parser(first, last, cb).parse(); + } + + /*! + @brief deserialize from a container with contiguous storage + + This function reads from a container with contiguous storage of 1-byte + values. Compatible container types include `std::vector`, `std::string`, + `std::array`, and `std::initializer_list`. User-defined containers can be + used as long as they implement random-access iterators and a contiguous + storage. + + @pre The container storage is contiguous. Violating this precondition + yields undefined behavior. **This precondition is enforced with an + assertion.** + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @warning There is no way to enforce all preconditions at compile-time. If + the function is called with a noncompliant container and with + assertions switched off, the behavior is undefined and will most + likely yield segmentation violation. + + @tparam ContiguousContainer container type with contiguous storage + @param[in] c container to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from a contiguous container.,parse__contiguouscontainer__parser_callback_t} + + @since version 2.0.3 + */ + template::value and + std::is_base_of< + std::random_access_iterator_tag, + typename std::iterator_traits()))>::iterator_category>::value + , int>::type = 0> + static basic_json parse(const ContiguousContainer& c, + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(c), std::end(c), cb); + } + /*! @brief deserialize from stream @@ -6032,6 +6228,1499 @@ class basic_json /// @} + ////////////////////////////////////////// + // binary serialization/deserialization // + ////////////////////////////////////////// + + /// @name binary serialization/deserialization support + /// @{ + + private: + template + static void add_to_vector(std::vector& vec, size_t bytes, const T number) + { + assert(bytes == 1 or bytes == 2 or bytes == 4 or bytes == 8); + + switch (bytes) + { + case 8: + { + vec.push_back(static_cast((number >> 070) & 0xff)); + vec.push_back(static_cast((number >> 060) & 0xff)); + vec.push_back(static_cast((number >> 050) & 0xff)); + vec.push_back(static_cast((number >> 040) & 0xff)); + // intentional fall-through + } + + case 4: + { + vec.push_back(static_cast((number >> 030) & 0xff)); + vec.push_back(static_cast((number >> 020) & 0xff)); + // intentional fall-through + } + + case 2: + { + vec.push_back(static_cast((number >> 010) & 0xff)); + // intentional fall-through + } + + case 1: + { + vec.push_back(static_cast(number & 0xff)); + break; + } + } + } + + /*! + @brief take sufficient bytes from a vector to fill an integer variable + + In the context of binary serialization formats, we need to read several + bytes from a byte vector and combine them to multi-byte integral data + types. + + @param[in] vec byte vector to read from + @param[in] current_index the position in the vector after which to read + + @return the next sizeof(T) bytes from @a vec, in reverse order as T + + @tparam T the integral return type + + @throw std::out_of_range if there are less than sizeof(T)+1 bytes in the + vector @a vec to read + + In the for loop, the bytes from the vector are copied in reverse order into + the return value. In the figures below, let sizeof(T)=4 and `i` be the loop + variable. + + Precondition: + + vec: | | | a | b | c | d | T: | | | | | + ^ ^ ^ ^ + current_index i ptr sizeof(T) + + Postcondition: + + vec: | | | a | b | c | d | T: | d | c | b | a | + ^ ^ ^ + | i ptr + current_index + + @sa Code adapted from . + */ + template + static T get_from_vector(const std::vector& vec, const size_t current_index) + { + if (current_index + sizeof(T) + 1 > vec.size()) + { + throw std::out_of_range("cannot read " + std::to_string(sizeof(T)) + " bytes from vector"); + } + + T result; + uint8_t* ptr = reinterpret_cast(&result); + for (size_t i = 0; i < sizeof(T); ++i) + { + *ptr++ = vec[current_index + sizeof(T) - i]; + } + return result; + } + + /*! + @brief create a MessagePack serialization of a given JSON value + + This is a straightforward implementation of the MessagePack specification. + + @param[in] j JSON value to serialize + @param[in,out] v byte vector to write the serialization to + + @sa https://github.com/msgpack/msgpack/blob/master/spec.md + */ + static void to_msgpack_internal(const basic_json& j, std::vector& v) + { + switch (j.type()) + { + case value_t::null: + { + // nil + v.push_back(0xc0); + break; + } + + case value_t::boolean: + { + // true and false + v.push_back(j.m_value.boolean ? 0xc3 : 0xc2); + break; + } + + case value_t::number_integer: + { + if (j.m_value.number_integer >= 0) + { + // MessagePack does not differentiate between positive + // signed integers and unsigned integers. Therefore, we used + // the code from the value_t::number_unsigned case here. + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + add_to_vector(v, 1, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT8_MAX) + { + // uint 8 + v.push_back(0xcc); + add_to_vector(v, 1, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT16_MAX) + { + // uint 16 + v.push_back(0xcd); + add_to_vector(v, 2, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT32_MAX) + { + // uint 32 + v.push_back(0xce); + add_to_vector(v, 4, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT64_MAX) + { + // uint 64 + v.push_back(0xcf); + add_to_vector(v, 8, j.m_value.number_unsigned); + } + } + else + { + if (j.m_value.number_integer >= -32) + { + // negative fixnum + add_to_vector(v, 1, j.m_value.number_integer); + } + else if (j.m_value.number_integer >= INT8_MIN and j.m_value.number_integer <= INT8_MAX) + { + // int 8 + v.push_back(0xd0); + add_to_vector(v, 1, j.m_value.number_integer); + } + else if (j.m_value.number_integer >= INT16_MIN and j.m_value.number_integer <= INT16_MAX) + { + // int 16 + v.push_back(0xd1); + add_to_vector(v, 2, j.m_value.number_integer); + } + else if (j.m_value.number_integer >= INT32_MIN and j.m_value.number_integer <= INT32_MAX) + { + // int 32 + v.push_back(0xd2); + add_to_vector(v, 4, j.m_value.number_integer); + } + else if (j.m_value.number_integer >= INT64_MIN and j.m_value.number_integer <= INT64_MAX) + { + // int 64 + v.push_back(0xd3); + add_to_vector(v, 8, j.m_value.number_integer); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + add_to_vector(v, 1, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT8_MAX) + { + // uint 8 + v.push_back(0xcc); + add_to_vector(v, 1, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT16_MAX) + { + // uint 16 + v.push_back(0xcd); + add_to_vector(v, 2, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT32_MAX) + { + // uint 32 + v.push_back(0xce); + add_to_vector(v, 4, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT64_MAX) + { + // uint 64 + v.push_back(0xcf); + add_to_vector(v, 8, j.m_value.number_unsigned); + } + break; + } + + case value_t::number_float: + { + // float 64 + v.push_back(0xcb); + const uint8_t* helper = reinterpret_cast(&(j.m_value.number_float)); + for (size_t i = 0; i < 8; ++i) + { + v.push_back(helper[7 - i]); + } + break; + } + + case value_t::string: + { + const auto N = j.m_value.string->size(); + if (N <= 31) + { + // fixstr + v.push_back(static_cast(0xa0 | N)); + } + else if (N <= 255) + { + // str 8 + v.push_back(0xd9); + add_to_vector(v, 1, N); + } + else if (N <= 65535) + { + // str 16 + v.push_back(0xda); + add_to_vector(v, 2, N); + } + else if (N <= 4294967295) + { + // str 32 + v.push_back(0xdb); + add_to_vector(v, 4, N); + } + + // append string + std::copy(j.m_value.string->begin(), j.m_value.string->end(), + std::back_inserter(v)); + break; + } + + case value_t::array: + { + const auto N = j.m_value.array->size(); + if (N <= 15) + { + // fixarray + v.push_back(static_cast(0x90 | N)); + } + else if (N <= 0xffff) + { + // array 16 + v.push_back(0xdc); + add_to_vector(v, 2, N); + } + else if (N <= 0xffffffff) + { + // array 32 + v.push_back(0xdd); + add_to_vector(v, 4, N); + } + + // append each element + for (const auto& el : *j.m_value.array) + { + to_msgpack_internal(el, v); + } + break; + } + + case value_t::object: + { + const auto N = j.m_value.object->size(); + if (N <= 15) + { + // fixmap + v.push_back(static_cast(0x80 | (N & 0xf))); + } + else if (N <= 65535) + { + // map 16 + v.push_back(0xde); + add_to_vector(v, 2, N); + } + else if (N <= 4294967295) + { + // map 32 + v.push_back(0xdf); + add_to_vector(v, 4, N); + } + + // append each element + for (const auto& el : *j.m_value.object) + { + to_msgpack_internal(el.first, v); + to_msgpack_internal(el.second, v); + } + break; + } + + default: + { + break; + } + } + } + + /*! + @brief create a CBOR serialization of a given JSON value + + This is a straightforward implementation of the CBOR specification. + + @param[in] j JSON value to serialize + @param[in,out] v byte vector to write the serialization to + + @sa https://tools.ietf.org/html/rfc7049 + */ + static void to_cbor_internal(const basic_json& j, std::vector& v) + { + switch (j.type()) + { + case value_t::null: + { + v.push_back(0xf6); + break; + } + + case value_t::boolean: + { + v.push_back(j.m_value.boolean ? 0xf5 : 0xf4); + break; + } + + case value_t::number_integer: + { + if (j.m_value.number_integer >= 0) + { + // CBOR does not differentiate between positive signed + // integers and unsigned integers. Therefore, we used the + // code from the value_t::number_unsigned case here. + if (j.m_value.number_integer <= 0x17) + { + add_to_vector(v, 1, j.m_value.number_integer); + } + else if (j.m_value.number_integer <= UINT8_MAX) + { + v.push_back(0x18); + // one-byte uint8_t + add_to_vector(v, 1, j.m_value.number_integer); + } + else if (j.m_value.number_integer <= UINT16_MAX) + { + v.push_back(0x19); + // two-byte uint16_t + add_to_vector(v, 2, j.m_value.number_integer); + } + else if (j.m_value.number_integer <= UINT32_MAX) + { + v.push_back(0x1a); + // four-byte uint32_t + add_to_vector(v, 4, j.m_value.number_integer); + } + else + { + v.push_back(0x1b); + // eight-byte uint64_t + add_to_vector(v, 8, j.m_value.number_integer); + } + } + else + { + // The conversions below encode the sign in the first byte, + // and the value is converted to a positive number. + const auto positive_number = -1 - j.m_value.number_integer; + if (j.m_value.number_integer >= -24) + { + v.push_back(static_cast(0x20 + positive_number)); + } + else if (positive_number <= UINT8_MAX) + { + // int 8 + v.push_back(0x38); + add_to_vector(v, 1, positive_number); + } + else if (positive_number <= UINT16_MAX) + { + // int 16 + v.push_back(0x39); + add_to_vector(v, 2, positive_number); + } + else if (positive_number <= UINT32_MAX) + { + // int 32 + v.push_back(0x3a); + add_to_vector(v, 4, positive_number); + } + else + { + // int 64 + v.push_back(0x3b); + add_to_vector(v, 8, positive_number); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned <= 0x17) + { + v.push_back(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= 0xff) + { + v.push_back(0x18); + // one-byte uint8_t + add_to_vector(v, 1, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= 0xffff) + { + v.push_back(0x19); + // two-byte uint16_t + add_to_vector(v, 2, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= 0xffffffff) + { + v.push_back(0x1a); + // four-byte uint32_t + add_to_vector(v, 4, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= 0xffffffffffffffff) + { + v.push_back(0x1b); + // eight-byte uint64_t + add_to_vector(v, 8, j.m_value.number_unsigned); + } + break; + } + + case value_t::number_float: + { + // Double-Precision Float + v.push_back(0xfb); + const uint8_t* helper = reinterpret_cast(&(j.m_value.number_float)); + for (size_t i = 0; i < 8; ++i) + { + v.push_back(helper[7 - i]); + } + break; + } + + case value_t::string: + { + const auto N = j.m_value.string->size(); + if (N <= 0x17) + { + v.push_back(0x60 + N); // 1 byte for string + size + } + else if (N <= 0xff) + { + v.push_back(0x78); // one-byte uint8_t for N + add_to_vector(v, 1, N); + } + else if (N <= 0xffff) + { + v.push_back(0x79); // two-byte uint16_t for N + add_to_vector(v, 2, N); + } + else if (N <= 0xffffffff) + { + v.push_back(0x7a); // four-byte uint32_t for N + add_to_vector(v, 4, N); + } + // LCOV_EXCL_START + else if (N <= 0xffffffffffffffff) + { + v.push_back(0x7b); // eight-byte uint64_t for N + add_to_vector(v, 8, N); + } + // LCOV_EXCL_STOP + + // append string + std::copy(j.m_value.string->begin(), j.m_value.string->end(), + std::back_inserter(v)); + break; + } + + case value_t::array: + { + const auto N = j.m_value.array->size(); + if (N <= 0x17) + { + v.push_back(0x80 + N); // 1 byte for array + size + } + else if (N <= 0xff) + { + v.push_back(0x98); // one-byte uint8_t for N + add_to_vector(v, 1, N); + } + else if (N <= 0xffff) + { + v.push_back(0x99); // two-byte uint16_t for N + add_to_vector(v, 2, N); + } + else if (N <= 0xffffffff) + { + v.push_back(0x9a); // four-byte uint32_t for N + add_to_vector(v, 4, N); + } + // LCOV_EXCL_START + else if (N <= 0xffffffffffffffff) + { + v.push_back(0x9b); // eight-byte uint64_t for N + add_to_vector(v, 8, N); + } + // LCOV_EXCL_STOP + + // append each element + for (const auto& el : *j.m_value.array) + { + to_cbor_internal(el, v); + } + break; + } + + case value_t::object: + { + const auto N = j.m_value.object->size(); + if (N <= 0x17) + { + v.push_back(0xa0 + N); // 1 byte for object + size + } + else if (N <= 0xff) + { + v.push_back(0xb8); + add_to_vector(v, 1, N); // one-byte uint8_t for N + } + else if (N <= 0xffff) + { + v.push_back(0xb9); + add_to_vector(v, 2, N); // two-byte uint16_t for N + } + else if (N <= 0xffffffff) + { + v.push_back(0xba); + add_to_vector(v, 4, N); // four-byte uint32_t for N + } + // LCOV_EXCL_START + else if (N <= 0xffffffffffffffff) + { + v.push_back(0xbb); + add_to_vector(v, 8, N); // eight-byte uint64_t for N + } + // LCOV_EXCL_STOP + + // append each element + for (const auto& el : *j.m_value.object) + { + to_cbor_internal(el.first, v); + to_cbor_internal(el.second, v); + } + break; + } + + default: + { + break; + } + } + } + + + /* + @brief checks if given lengths do not exceed the size of a given vector + + To secure the access to the byte vector during CBOR/MessagePack + deserialization, bytes are copied from the vector into buffers. This + function checks if the number of bytes to copy (@a len) does not exceed the + size @s size of the vector. Additionally, an @a offset is given from where + to start reading the bytes. + + This function checks whether reading the bytes is safe; that is, offset is a + valid index in the vector, offset+len + + @param[in] size size of the byte vector + @param[in] len number of bytes to read + @param[in] offset offset where to start reading + + vec: x x x x x X X X X X + ^ ^ ^ + 0 offset len + + @throws out_of_range if `len > v.size()` + */ + static void check_length(const size_t size, const size_t len, const size_t offset) + { + // simple case: requested length is greater than the vector's length + if (len > size or offset > size) + { + throw std::out_of_range("len out of range"); + } + + // second case: adding offset would result in overflow + if ((size > (std::numeric_limits::max() - offset))) + { + throw std::out_of_range("len+offset out of range"); + } + + // last case: reading past the end of the vector + if (len + offset > size) + { + throw std::out_of_range("len+offset out of range"); + } + } + + /*! + @brief create a JSON value from a given MessagePack vector + + @param[in] v MessagePack serialization + @param[in] idx byte index to start reading from @a v + + @return deserialized JSON value + + @throw std::invalid_argument if unsupported features from MessagePack were + used in the given vector @a v or if the input is not valid MessagePack + @throw std::out_of_range if the given vector ends prematurely + + @sa https://github.com/msgpack/msgpack/blob/master/spec.md + */ + static basic_json from_msgpack_internal(const std::vector& v, size_t& idx) + { + // make sure reading 1 byte is safe + check_length(v.size(), 1, idx); + + // store and increment index + const size_t current_idx = idx++; + + if (v[current_idx] <= 0xbf) + { + if (v[current_idx] <= 0x7f) // positive fixint + { + return v[current_idx]; + } + else if (v[current_idx] <= 0x8f) // fixmap + { + basic_json result = value_t::object; + const size_t len = v[current_idx] & 0x0f; + for (size_t i = 0; i < len; ++i) + { + std::string key = from_msgpack_internal(v, idx); + result[key] = from_msgpack_internal(v, idx); + } + return result; + } + else if (v[current_idx] <= 0x9f) // fixarray + { + basic_json result = value_t::array; + const size_t len = v[current_idx] & 0x0f; + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_msgpack_internal(v, idx)); + } + return result; + } + else // fixstr + { + const size_t len = v[current_idx] & 0x1f; + const size_t offset = current_idx + 1; + idx += len; // skip content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast(v.data()) + offset, len); + } + } + else if (v[current_idx] >= 0xe0) // negative fixint + { + return static_cast(v[current_idx]); + } + else + { + switch (v[current_idx]) + { + case 0xc0: // nil + { + return value_t::null; + } + + case 0xc2: // false + { + return false; + } + + case 0xc3: // true + { + return true; + } + + case 0xca: // float 32 + { + // copy bytes in reverse order into the double variable + check_length(v.size(), sizeof(float), 1); + float res; + for (size_t byte = 0; byte < sizeof(float); ++byte) + { + reinterpret_cast(&res)[sizeof(float) - byte - 1] = v[current_idx + 1 + byte]; + } + idx += sizeof(float); // skip content bytes + return res; + } + + case 0xcb: // float 64 + { + // copy bytes in reverse order into the double variable + check_length(v.size(), sizeof(double), 1); + double res; + for (size_t byte = 0; byte < sizeof(double); ++byte) + { + reinterpret_cast(&res)[sizeof(double) - byte - 1] = v[current_idx + 1 + byte]; + } + idx += sizeof(double); // skip content bytes + return res; + } + + case 0xcc: // uint 8 + { + idx += 1; // skip content byte + return get_from_vector(v, current_idx); + } + + case 0xcd: // uint 16 + { + idx += 2; // skip 2 content bytes + return get_from_vector(v, current_idx); + } + + case 0xce: // uint 32 + { + idx += 4; // skip 4 content bytes + return get_from_vector(v, current_idx); + } + + case 0xcf: // uint 64 + { + idx += 8; // skip 8 content bytes + return get_from_vector(v, current_idx); + } + + case 0xd0: // int 8 + { + idx += 1; // skip content byte + return get_from_vector(v, current_idx); + } + + case 0xd1: // int 16 + { + idx += 2; // skip 2 content bytes + return get_from_vector(v, current_idx); + } + + case 0xd2: // int 32 + { + idx += 4; // skip 4 content bytes + return get_from_vector(v, current_idx); + } + + case 0xd3: // int 64 + { + idx += 8; // skip 8 content bytes + return get_from_vector(v, current_idx); + } + + case 0xd9: // str 8 + { + const auto len = static_cast(get_from_vector(v, current_idx)); + const size_t offset = current_idx + 2; + idx += len + 1; // skip size byte + content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast(v.data()) + offset, len); + } + + case 0xda: // str 16 + { + const auto len = static_cast(get_from_vector(v, current_idx)); + const size_t offset = current_idx + 3; + idx += len + 2; // skip 2 size bytes + content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast(v.data()) + offset, len); + } + + case 0xdb: // str 32 + { + const auto len = static_cast(get_from_vector(v, current_idx)); + const size_t offset = current_idx + 5; + idx += len + 4; // skip 4 size bytes + content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast(v.data()) + offset, len); + } + + case 0xdc: // array 16 + { + basic_json result = value_t::array; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 2; // skip 2 size bytes + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_msgpack_internal(v, idx)); + } + return result; + } + + case 0xdd: // array 32 + { + basic_json result = value_t::array; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 4; // skip 4 size bytes + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_msgpack_internal(v, idx)); + } + return result; + } + + case 0xde: // map 16 + { + basic_json result = value_t::object; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 2; // skip 2 size bytes + for (size_t i = 0; i < len; ++i) + { + std::string key = from_msgpack_internal(v, idx); + result[key] = from_msgpack_internal(v, idx); + } + return result; + } + + case 0xdf: // map 32 + { + basic_json result = value_t::object; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 4; // skip 4 size bytes + for (size_t i = 0; i < len; ++i) + { + std::string key = from_msgpack_internal(v, idx); + result[key] = from_msgpack_internal(v, idx); + } + return result; + } + + default: + { + throw std::invalid_argument("error parsing a msgpack @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast(v[current_idx]))); + } + } + } + } + + /*! + @brief create a JSON value from a given CBOR vector + + @param[in] v CBOR serialization + @param[in] idx byte index to start reading from @a v + + @return deserialized JSON value + + @throw std::invalid_argument if unsupported features from CBOR were used in + the given vector @a v or if the input is not valid CBOR + @throw std::out_of_range if the given vector ends prematurely + + @sa https://tools.ietf.org/html/rfc7049 + */ + static basic_json from_cbor_internal(const std::vector& v, size_t& idx) + { + // make sure reading 1 byte is safe + check_length(v.size(), 1, idx); + + // store and increment index + const size_t current_idx = idx++; + + switch (v[current_idx]) + { + // Integer 0x00..0x17 (0..23) + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + { + return v[current_idx]; + } + + case 0x18: // Unsigned integer (one-byte uint8_t follows) + { + idx += 1; // skip content byte + return get_from_vector(v, current_idx); + } + + case 0x19: // Unsigned integer (two-byte uint16_t follows) + { + idx += 2; // skip 2 content bytes + return get_from_vector(v, current_idx); + } + + case 0x1a: // Unsigned integer (four-byte uint32_t follows) + { + idx += 4; // skip 4 content bytes + return get_from_vector(v, current_idx); + } + + case 0x1b: // Unsigned integer (eight-byte uint64_t follows) + { + idx += 8; // skip 8 content bytes + return get_from_vector(v, current_idx); + } + + // Negative integer -1-0x00..-1-0x17 (-1..-24) + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2a: + case 0x2b: + case 0x2c: + case 0x2d: + case 0x2e: + case 0x2f: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + { + return static_cast(0x20 - 1 - v[current_idx]); + } + + case 0x38: // Negative integer (one-byte uint8_t follows) + { + idx += 1; // skip content byte + // must be uint8_t ! + return static_cast(-1) - get_from_vector(v, current_idx); + } + + case 0x39: // Negative integer -1-n (two-byte uint16_t follows) + { + idx += 2; // skip 2 content bytes + return static_cast(-1) - get_from_vector(v, current_idx); + } + + case 0x3a: // Negative integer -1-n (four-byte uint32_t follows) + { + idx += 4; // skip 4 content bytes + return static_cast(-1) - get_from_vector(v, current_idx); + } + + case 0x3b: // Negative integer -1-n (eight-byte uint64_t follows) + { + idx += 8; // skip 8 content bytes + return static_cast(-1) - static_cast(get_from_vector(v, current_idx)); + } + + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6a: + case 0x6b: + case 0x6c: + case 0x6d: + case 0x6e: + case 0x6f: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + { + const auto len = static_cast(v[current_idx] - 0x60); + const size_t offset = current_idx + 1; + idx += len; // skip content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast(v.data()) + offset, len); + } + + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + { + const auto len = static_cast(get_from_vector(v, current_idx)); + const size_t offset = current_idx + 2; + idx += len + 1; // skip size byte + content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast(v.data()) + offset, len); + } + + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + { + const auto len = static_cast(get_from_vector(v, current_idx)); + const size_t offset = current_idx + 3; + idx += len + 2; // skip 2 size bytes + content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast(v.data()) + offset, len); + } + + case 0x7a: // UTF-8 string (four-byte uint32_t for n follow) + { + const auto len = static_cast(get_from_vector(v, current_idx)); + const size_t offset = current_idx + 5; + idx += len + 4; // skip 4 size bytes + content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast(v.data()) + offset, len); + } + + case 0x7b: // UTF-8 string (eight-byte uint64_t for n follow) + { + const auto len = static_cast(get_from_vector(v, current_idx)); + const size_t offset = current_idx + 9; + idx += len + 8; // skip 8 size bytes + content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast(v.data()) + offset, len); + } + + case 0x7f: // UTF-8 string (indefinite length) + { + std::string result; + while (v[idx] != 0xff) + { + string_t s = from_cbor_internal(v, idx); + result += s; + } + // skip break byte (0xFF) + idx += 1; + return result; + } + + // array (0x00..0x17 data items follow) + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8a: + case 0x8b: + case 0x8c: + case 0x8d: + case 0x8e: + case 0x8f: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + { + basic_json result = value_t::array; + const auto len = static_cast(v[current_idx] - 0x80); + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_cbor_internal(v, idx)); + } + return result; + } + + case 0x98: // array (one-byte uint8_t for n follows) + { + basic_json result = value_t::array; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 1; // skip 1 size byte + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_cbor_internal(v, idx)); + } + return result; + } + + case 0x99: // array (two-byte uint16_t for n follow) + { + basic_json result = value_t::array; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 2; // skip 4 size bytes + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_cbor_internal(v, idx)); + } + return result; + } + + case 0x9a: // array (four-byte uint32_t for n follow) + { + basic_json result = value_t::array; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 4; // skip 4 size bytes + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_cbor_internal(v, idx)); + } + return result; + } + + case 0x9b: // array (eight-byte uint64_t for n follow) + { + basic_json result = value_t::array; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 8; // skip 8 size bytes + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_cbor_internal(v, idx)); + } + return result; + } + + case 0x9f: // array (indefinite length) + { + basic_json result = value_t::array; + while (v[idx] != 0xff) + { + result.push_back(from_cbor_internal(v, idx)); + } + // skip break byte (0xFF) + idx += 1; + return result; + } + + // map (0x00..0x17 pairs of data items follow) + case 0xa0: + case 0xa1: + case 0xa2: + case 0xa3: + case 0xa4: + case 0xa5: + case 0xa6: + case 0xa7: + case 0xa8: + case 0xa9: + case 0xaa: + case 0xab: + case 0xac: + case 0xad: + case 0xae: + case 0xaf: + case 0xb0: + case 0xb1: + case 0xb2: + case 0xb3: + case 0xb4: + case 0xb5: + case 0xb6: + case 0xb7: + { + basic_json result = value_t::object; + const auto len = static_cast(v[current_idx] - 0xa0); + for (size_t i = 0; i < len; ++i) + { + std::string key = from_cbor_internal(v, idx); + result[key] = from_cbor_internal(v, idx); + } + return result; + } + + case 0xb8: // map (one-byte uint8_t for n follows) + { + basic_json result = value_t::object; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 1; // skip 1 size byte + for (size_t i = 0; i < len; ++i) + { + std::string key = from_cbor_internal(v, idx); + result[key] = from_cbor_internal(v, idx); + } + return result; + } + + case 0xb9: // map (two-byte uint16_t for n follow) + { + basic_json result = value_t::object; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 2; // skip 2 size bytes + for (size_t i = 0; i < len; ++i) + { + std::string key = from_cbor_internal(v, idx); + result[key] = from_cbor_internal(v, idx); + } + return result; + } + + case 0xba: // map (four-byte uint32_t for n follow) + { + basic_json result = value_t::object; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 4; // skip 4 size bytes + for (size_t i = 0; i < len; ++i) + { + std::string key = from_cbor_internal(v, idx); + result[key] = from_cbor_internal(v, idx); + } + return result; + } + + case 0xbb: // map (eight-byte uint64_t for n follow) + { + basic_json result = value_t::object; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 8; // skip 8 size bytes + for (size_t i = 0; i < len; ++i) + { + std::string key = from_cbor_internal(v, idx); + result[key] = from_cbor_internal(v, idx); + } + return result; + } + + case 0xbf: // map (indefinite length) + { + basic_json result = value_t::object; + while (v[idx] != 0xff) + { + std::string key = from_cbor_internal(v, idx); + result[key] = from_cbor_internal(v, idx); + } + // skip break byte (0xFF) + idx += 1; + return result; + } + + case 0xf4: // false + { + return false; + } + + case 0xf5: // true + { + return true; + } + + case 0xf6: // null + { + return value_t::null; + } + + case 0xf9: // Half-Precision Float (two-byte IEEE 754) + { + check_length(v.size(), 2, 1); + idx += 2; // skip two content bytes + + // code from RFC 7049, Appendix D, Figure 3: + // As half-precision floating-point numbers were only added to + // IEEE 754 in 2008, today's programming platforms often still + // only have limited support for them. It is very easy to + // include at least decoding support for them even without such + // support. An example of a small decoder for half-precision + // floating-point numbers in the C language is shown in Fig. 3. + const int half = (v[current_idx + 1] << 8) + v[current_idx + 2]; + const int exp = (half >> 10) & 0x1f; + const int mant = half & 0x3ff; + double val; + if (exp == 0) + { + val = std::ldexp(mant, -24); + } + else if (exp != 31) + { + val = std::ldexp(mant + 1024, exp - 25); + } + else + { + val = mant == 0 ? INFINITY : NAN; + } + return half & 0x8000 ? -val : val; + } + + case 0xfa: // Single-Precision Float (four-byte IEEE 754) + { + // copy bytes in reverse order into the float variable + check_length(v.size(), sizeof(float), 1); + float res; + for (size_t byte = 0; byte < sizeof(float); ++byte) + { + reinterpret_cast(&res)[sizeof(float) - byte - 1] = v[current_idx + 1 + byte]; + } + idx += sizeof(float); // skip content bytes + return res; + } + + case 0xfb: // Double-Precision Float (eight-byte IEEE 754) + { + check_length(v.size(), sizeof(double), 1); + // copy bytes in reverse order into the double variable + double res; + for (size_t byte = 0; byte < sizeof(double); ++byte) + { + reinterpret_cast(&res)[sizeof(double) - byte - 1] = v[current_idx + 1 + byte]; + } + idx += sizeof(double); // skip content bytes + return res; + } + + default: // anything else (0xFF is handled inside the other types) + { + throw std::invalid_argument("error parsing a CBOR @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast(v[current_idx]))); + } + } + } + + public: + /*! + @brief create a MessagePack serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the MessagePack + serialization format. MessagePack is a binary serialization format which + aims to be more compact than JSON itself, yet more efficient to parse. + + @param[in] j JSON value to serialize + @return MessagePack serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in MessagePack format.,to_msgpack} + + @sa http://msgpack.org + @sa @ref from_msgpack(const std::vector&) for the analogous + deserialization + @sa @ref to_cbor(const basic_json& for the related CBOR format + */ + static std::vector to_msgpack(const basic_json& j) + { + std::vector result; + to_msgpack_internal(j, result); + return result; + } + + /*! + @brief create a JSON value from a byte vector in MessagePack format + + Deserializes a given byte vector @a v to a JSON value using the MessagePack + serialization format. + + @param[in] v a byte vector in MessagePack format + @return deserialized JSON value + + @throw std::invalid_argument if unsupported features from MessagePack were + used in the given vector @a v or if the input is not valid MessagePack + @throw std::out_of_range if the given vector ends prematurely + + @complexity Linear in the size of the byte vector @a v. + + @liveexample{The example shows the deserialization of a byte vector in + MessagePack format to a JSON value.,from_msgpack} + + @sa http://msgpack.org + @sa @ref to_msgpack(const basic_json&) for the analogous serialization + @sa @ref from_cbor(const std::vector&) for the related CBOR format + */ + static basic_json from_msgpack(const std::vector& v) + { + size_t i = 0; + return from_msgpack_internal(v, i); + } + + /*! + @brief create a MessagePack serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the CBOR (Concise + Binary Object Representation) serialization format. CBOR is a binary + serialization format which aims to be more compact than JSON itself, yet + more efficient to parse. + + @param[in] j JSON value to serialize + @return MessagePack serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in CBOR format.,to_cbor} + + @sa http://cbor.io + @sa @ref from_cbor(const std::vector&) for the analogous + deserialization + @sa @ref to_msgpack(const basic_json& for the related MessagePack format + */ + static std::vector to_cbor(const basic_json& j) + { + std::vector result; + to_cbor_internal(j, result); + return result; + } + + /*! + @brief create a JSON value from a byte vector in CBOR format + + Deserializes a given byte vector @a v to a JSON value using the CBOR + (Concise Binary Object Representation) serialization format. + + @param[in] v a byte vector in CBOR format + @return deserialized JSON value + + @throw std::invalid_argument if unsupported features from CBOR were used in + the given vector @a v or if the input is not valid MessagePack + @throw std::out_of_range if the given vector ends prematurely + + @complexity Linear in the size of the byte vector @a v. + + @liveexample{The example shows the deserialization of a byte vector in CBOR + format to a JSON value.,from_cbor} + + @sa http://cbor.io + @sa @ref to_cbor(const basic_json&) for the analogous serialization + @sa @ref from_msgpack(const std::vector&) for the related + MessagePack format + */ + static basic_json from_cbor(const std::vector& v) + { + size_t i = 0; + return from_cbor_internal(v, i); + } + + /// @} private: /////////////////////////// @@ -6044,7 +7733,7 @@ class basic_json Returns the type name as string to be used in error messages - usually to indicate that a function was called on a wrong JSON type. - @return basically a string representation of a the @ref m_type member + @return basically a string representation of a the @a m_type member @complexity Constant. @@ -6584,43 +8273,53 @@ class basic_json public: /*! - @brief a const random access iterator for the @ref basic_json class + @brief a template for a random access iterator for the @ref basic_json class - This class implements a const iterator for the @ref basic_json class. From - this class, the @ref iterator class is derived. + This class implements a both iterators (iterator and const_iterator) for the + @ref basic_json class. @note An iterator is called *initialized* when a pointer to a JSON value has been set (e.g., by a constructor or a copy assignment). If the iterator is default-constructed, it is *uninitialized* and most - methods are undefined. The library uses assertions to detect calls - on uninitialized iterators. + methods are undefined. **The library uses assertions to detect calls + on uninitialized iterators.** @requirement The class satisfies the following concept requirements: - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): The iterator that can be moved to point (forward and backward) to any element in constant time. - @since version 1.0.0 + @since version 1.0.0, simplified in version 2.0.9 */ - class const_iterator : public std::iterator + template + class iter_impl : public std::iterator { /// allow basic_json to access private members friend class basic_json; + // make sure U is basic_json or const basic_json + static_assert(std::is_same::value + or std::is_same::value, + "iter_impl only accepts (const) basic_json"); + public: /// the type of the values when the iterator is dereferenced using value_type = typename basic_json::value_type; /// a type to represent differences between iterators using difference_type = typename basic_json::difference_type; /// defines a pointer to the type iterated over (value_type) - using pointer = typename basic_json::const_pointer; + using pointer = typename std::conditional::value, + typename basic_json::const_pointer, + typename basic_json::pointer>::type; /// defines a reference to the type iterated over (value_type) - using reference = typename basic_json::const_reference; + using reference = typename std::conditional::value, + typename basic_json::const_reference, + typename basic_json::reference>::type; /// the category of the iterator using iterator_category = std::bidirectional_iterator_tag; /// default constructor - const_iterator() = default; + iter_impl() = default; /*! @brief constructor for a given JSON instance @@ -6628,7 +8327,7 @@ class basic_json @pre object != nullptr @post The iterator is initialized; i.e. `m_object != nullptr`. */ - explicit const_iterator(pointer object) noexcept + explicit iter_impl(pointer object) noexcept : m_object(object) { assert(m_object != nullptr); @@ -6655,37 +8354,25 @@ class basic_json } } - /*! - @brief copy constructor given a non-const iterator - @param[in] other iterator to copy from - @note It is not checked whether @a other is initialized. + /* + Use operator `const_iterator` instead of `const_iterator(const iterator& + other) noexcept` to avoid two class definitions for @ref iterator and + @ref const_iterator. + + This function is only called if this class is an @ref iterator. If this + class is a @ref const_iterator this function is not called. */ - explicit const_iterator(const iterator& other) noexcept - : m_object(other.m_object) + operator const_iterator() const { - if (m_object != nullptr) + const_iterator ret; + + if (m_object) { - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - m_it.object_iterator = other.m_it.object_iterator; - break; - } - - case basic_json::value_t::array: - { - m_it.array_iterator = other.m_it.array_iterator; - break; - } - - default: - { - m_it.primitive_iterator = other.m_it.primitive_iterator; - break; - } - } + ret.m_object = m_object; + ret.m_it = m_it; } + + return ret; } /*! @@ -6693,7 +8380,7 @@ class basic_json @param[in] other iterator to copy from @note It is not checked whether @a other is initialized. */ - const_iterator(const const_iterator& other) noexcept + iter_impl(const iter_impl& other) noexcept : m_object(other.m_object), m_it(other.m_it) {} @@ -6702,7 +8389,7 @@ class basic_json @param[in,out] other iterator to copy from @note It is not checked whether @a other is initialized. */ - const_iterator& operator=(const_iterator other) noexcept( + iter_impl& operator=(iter_impl other) noexcept( std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value and std::is_nothrow_move_constructible::value and @@ -6864,7 +8551,7 @@ class basic_json @brief post-increment (it++) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator operator++(int) + iter_impl operator++(int) { auto result = *this; ++(*this); @@ -6875,7 +8562,7 @@ class basic_json @brief pre-increment (++it) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator& operator++() + iter_impl& operator++() { assert(m_object != nullptr); @@ -6907,7 +8594,7 @@ class basic_json @brief post-decrement (it--) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator operator--(int) + iter_impl operator--(int) { auto result = *this; --(*this); @@ -6918,7 +8605,7 @@ class basic_json @brief pre-decrement (--it) @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator& operator--() + iter_impl& operator--() { assert(m_object != nullptr); @@ -6950,7 +8637,7 @@ class basic_json @brief comparison: equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator==(const const_iterator& other) const + bool operator==(const iter_impl& other) const { // if objects are not the same, the comparison is undefined if (m_object != other.m_object) @@ -6983,7 +8670,7 @@ class basic_json @brief comparison: not equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator!=(const const_iterator& other) const + bool operator!=(const iter_impl& other) const { return not operator==(other); } @@ -6992,7 +8679,7 @@ class basic_json @brief comparison: smaller @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator<(const const_iterator& other) const + bool operator<(const iter_impl& other) const { // if objects are not the same, the comparison is undefined if (m_object != other.m_object) @@ -7025,7 +8712,7 @@ class basic_json @brief comparison: less than or equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator<=(const const_iterator& other) const + bool operator<=(const iter_impl& other) const { return not other.operator < (*this); } @@ -7034,7 +8721,7 @@ class basic_json @brief comparison: greater than @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator>(const const_iterator& other) const + bool operator>(const iter_impl& other) const { return not operator<=(other); } @@ -7043,7 +8730,7 @@ class basic_json @brief comparison: greater than or equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator>=(const const_iterator& other) const + bool operator>=(const iter_impl& other) const { return not operator<(other); } @@ -7052,7 +8739,7 @@ class basic_json @brief add to iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator& operator+=(difference_type i) + iter_impl& operator+=(difference_type i) { assert(m_object != nullptr); @@ -7083,7 +8770,7 @@ class basic_json @brief subtract from iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator& operator-=(difference_type i) + iter_impl& operator-=(difference_type i) { return operator+=(-i); } @@ -7092,7 +8779,7 @@ class basic_json @brief add to iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator operator+(difference_type i) + iter_impl operator+(difference_type i) { auto result = *this; result += i; @@ -7103,7 +8790,7 @@ class basic_json @brief subtract from iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - const_iterator operator-(difference_type i) + iter_impl operator-(difference_type i) { auto result = *this; result -= i; @@ -7114,7 +8801,7 @@ class basic_json @brief return difference @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - difference_type operator-(const const_iterator& other) const + difference_type operator-(const iter_impl& other) const { assert(m_object != nullptr); @@ -7210,141 +8897,6 @@ class basic_json internal_iterator m_it = internal_iterator(); }; - /*! - @brief a mutable random access iterator for the @ref basic_json class - - @requirement The class satisfies the following concept requirements: - - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): - The iterator that can be moved to point (forward and backward) to any - element in constant time. - - [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator): - It is possible to write to the pointed-to element. - - @since version 1.0.0 - */ - class iterator : public const_iterator - { - public: - using base_iterator = const_iterator; - using pointer = typename basic_json::pointer; - using reference = typename basic_json::reference; - - /// default constructor - iterator() = default; - - /// constructor for a given JSON instance - explicit iterator(pointer object) noexcept - : base_iterator(object) - {} - - /// copy constructor - iterator(const iterator& other) noexcept - : base_iterator(other) - {} - - /// copy assignment - iterator& operator=(iterator other) noexcept( - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value and - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value - ) - { - base_iterator::operator=(other); - return *this; - } - - /// return a reference to the value pointed to by the iterator - reference operator*() const - { - return const_cast(base_iterator::operator*()); - } - - /// dereference the iterator - pointer operator->() const - { - return const_cast(base_iterator::operator->()); - } - - /// post-increment (it++) - iterator operator++(int) - { - iterator result = *this; - base_iterator::operator++(); - return result; - } - - /// pre-increment (++it) - iterator& operator++() - { - base_iterator::operator++(); - return *this; - } - - /// post-decrement (it--) - iterator operator--(int) - { - iterator result = *this; - base_iterator::operator--(); - return result; - } - - /// pre-decrement (--it) - iterator& operator--() - { - base_iterator::operator--(); - return *this; - } - - /// add to iterator - iterator& operator+=(difference_type i) - { - base_iterator::operator+=(i); - return *this; - } - - /// subtract from iterator - iterator& operator-=(difference_type i) - { - base_iterator::operator-=(i); - return *this; - } - - /// add to iterator - iterator operator+(difference_type i) - { - auto result = *this; - result += i; - return result; - } - - /// subtract from iterator - iterator operator-(difference_type i) - { - auto result = *this; - result -= i; - return result; - } - - /// return difference - difference_type operator-(const iterator& other) const - { - return base_iterator::operator-(other); - } - - /// access to successor - reference operator[](difference_type n) const - { - return const_cast(base_iterator::operator[](n)); - } - - /// return the value of an iterator - reference value() const - { - return const_cast(base_iterator::value()); - } - }; - /*! @brief a template for a reverse iterator class @@ -7495,32 +9047,39 @@ class basic_json /// the char type to use in the lexer using lexer_char_t = unsigned char; - /// constructor with a given buffer - explicit lexer(const string_t& s) noexcept - : m_stream(nullptr), m_buffer(s) + /// a lexer from a buffer with given length + lexer(const lexer_char_t* buff, const size_t len) noexcept + : m_content(buff) { - m_content = reinterpret_cast(m_buffer.c_str()); assert(m_content != nullptr); m_start = m_cursor = m_content; - m_limit = m_content + s.size(); + m_limit = m_content + len; } - /// constructor with a given stream - explicit lexer(std::istream* s) noexcept - : m_stream(s), m_buffer() + /// a lexer from an input stream + explicit lexer(std::istream& s) + : m_stream(&s), m_line_buffer() { - assert(m_stream != nullptr); - std::getline(*m_stream, m_buffer); - m_content = reinterpret_cast(m_buffer.c_str()); - assert(m_content != nullptr); - m_start = m_cursor = m_content; - m_limit = m_content + m_buffer.size(); + // immediately abort if stream is erroneous + if (s.fail()) + { + throw std::invalid_argument("stream error"); + } + + // fill buffer + fill_line_buffer(); + + // skip UTF-8 byte-order mark + if (m_line_buffer.size() >= 3 and m_line_buffer.substr(0, 3) == "\xEF\xBB\xBF") + { + m_line_buffer[0] = ' '; + m_line_buffer[1] = ' '; + m_line_buffer[2] = ' '; + } } - /// default constructor - lexer() = default; - - // switch off unwanted functions + // switch off unwanted functions (due to pointer members) + lexer() = delete; lexer(const lexer&) = delete; lexer operator=(const lexer&) = delete; @@ -7673,7 +9232,7 @@ class basic_json infinite sequence of whitespace or byte-order-marks. This contradicts the assumption of finite input, q.e.d. */ - token_type scan() noexcept + token_type scan() { while (true) { @@ -7706,33 +9265,33 @@ class basic_json 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, }; if ((m_limit - m_cursor) < 5) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(5); // LCOV_EXCL_LINE } yych = *m_cursor; if (yybm[0 + yych] & 32) { goto basic_json_parser_6; } - if (yych <= '\\') + if (yych <= '[') { if (yych <= '-') { @@ -7781,63 +9340,59 @@ class basic_json { goto basic_json_parser_17; } - if (yych == '[') + if (yych <= 'Z') { - goto basic_json_parser_19; + goto basic_json_parser_4; } - goto basic_json_parser_4; + goto basic_json_parser_19; } } } else { - if (yych <= 't') + if (yych <= 'n') { - if (yych <= 'f') + if (yych <= 'e') { - if (yych <= ']') + if (yych == ']') { goto basic_json_parser_21; } - if (yych <= 'e') - { - goto basic_json_parser_4; - } - goto basic_json_parser_23; - } - else - { - if (yych == 'n') - { - goto basic_json_parser_24; - } - if (yych <= 's') - { - goto basic_json_parser_4; - } - goto basic_json_parser_25; - } - } - else - { - if (yych <= '|') - { - if (yych == '{') - { - goto basic_json_parser_26; - } goto basic_json_parser_4; } else { - if (yych <= '}') + if (yych <= 'f') + { + goto basic_json_parser_23; + } + if (yych <= 'm') + { + goto basic_json_parser_4; + } + goto basic_json_parser_24; + } + } + else + { + if (yych <= 'z') + { + if (yych == 't') + { + goto basic_json_parser_25; + } + goto basic_json_parser_4; + } + else + { + if (yych <= '{') + { + goto basic_json_parser_26; + } + if (yych == '}') { goto basic_json_parser_28; } - if (yych == 0xEF) - { - goto basic_json_parser_30; - } goto basic_json_parser_4; } } @@ -7859,7 +9414,7 @@ basic_json_parser_6: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yybm[0 + yych] & 32) @@ -7876,7 +9431,19 @@ basic_json_parser_9: { goto basic_json_parser_5; } - goto basic_json_parser_32; + if (yych <= 0x7F) + { + goto basic_json_parser_31; + } + if (yych <= 0xC1) + { + goto basic_json_parser_5; + } + if (yych <= 0xF4) + { + goto basic_json_parser_31; + } + goto basic_json_parser_5; basic_json_parser_10: ++m_cursor; { @@ -7905,18 +9472,18 @@ basic_json_parser_13: { if (yych == '.') { - goto basic_json_parser_37; + goto basic_json_parser_43; } } else { if (yych <= 'E') { - goto basic_json_parser_38; + goto basic_json_parser_44; } if (yych == 'e') { - goto basic_json_parser_38; + goto basic_json_parser_44; } } basic_json_parser_14: @@ -7929,7 +9496,7 @@ basic_json_parser_15: m_marker = ++m_cursor; if ((m_limit - m_cursor) < 3) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(3); // LCOV_EXCL_LINE } yych = *m_cursor; if (yybm[0 + yych] & 64) @@ -7940,7 +9507,7 @@ basic_json_parser_15: { if (yych == '.') { - goto basic_json_parser_37; + goto basic_json_parser_43; } goto basic_json_parser_14; } @@ -7948,11 +9515,11 @@ basic_json_parser_15: { if (yych <= 'E') { - goto basic_json_parser_38; + goto basic_json_parser_44; } if (yych == 'e') { - goto basic_json_parser_38; + goto basic_json_parser_44; } goto basic_json_parser_14; } @@ -7979,7 +9546,7 @@ basic_json_parser_23: yych = *(m_marker = ++m_cursor); if (yych == 'a') { - goto basic_json_parser_39; + goto basic_json_parser_45; } goto basic_json_parser_5; basic_json_parser_24: @@ -7987,7 +9554,7 @@ basic_json_parser_24: yych = *(m_marker = ++m_cursor); if (yych == 'u') { - goto basic_json_parser_40; + goto basic_json_parser_46; } goto basic_json_parser_5; basic_json_parser_25: @@ -7995,7 +9562,7 @@ basic_json_parser_25: yych = *(m_marker = ++m_cursor); if (yych == 'r') { - goto basic_json_parser_41; + goto basic_json_parser_47; } goto basic_json_parser_5; basic_json_parser_26: @@ -8011,35 +9578,71 @@ basic_json_parser_28: break; } basic_json_parser_30: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 0xBB) - { - goto basic_json_parser_42; - } - goto basic_json_parser_5; -basic_json_parser_31: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; -basic_json_parser_32: +basic_json_parser_31: if (yybm[0 + yych] & 128) { - goto basic_json_parser_31; + goto basic_json_parser_30; } - if (yych <= 0x1F) + if (yych <= 0xE0) { - goto basic_json_parser_33; + if (yych <= '\\') + { + if (yych <= 0x1F) + { + goto basic_json_parser_32; + } + if (yych <= '"') + { + goto basic_json_parser_33; + } + goto basic_json_parser_35; + } + else + { + if (yych <= 0xC1) + { + goto basic_json_parser_32; + } + if (yych <= 0xDF) + { + goto basic_json_parser_36; + } + goto basic_json_parser_37; + } } - if (yych <= '"') + else { - goto basic_json_parser_34; + if (yych <= 0xEF) + { + if (yych == 0xED) + { + goto basic_json_parser_39; + } + goto basic_json_parser_38; + } + else + { + if (yych <= 0xF0) + { + goto basic_json_parser_40; + } + if (yych <= 0xF3) + { + goto basic_json_parser_41; + } + if (yych <= 0xF4) + { + goto basic_json_parser_42; + } + } } - goto basic_json_parser_36; -basic_json_parser_33: +basic_json_parser_32: m_cursor = m_marker; if (yyaccept == 0) { @@ -8049,17 +9652,17 @@ basic_json_parser_33: { goto basic_json_parser_14; } -basic_json_parser_34: +basic_json_parser_33: ++m_cursor; { last_token_type = token_type::value_string; break; } -basic_json_parser_36: +basic_json_parser_35: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= 'e') @@ -8068,13 +9671,13 @@ basic_json_parser_36: { if (yych == '"') { - goto basic_json_parser_31; + goto basic_json_parser_30; } if (yych <= '.') { - goto basic_json_parser_33; + goto basic_json_parser_32; } - goto basic_json_parser_31; + goto basic_json_parser_30; } else { @@ -8082,17 +9685,17 @@ basic_json_parser_36: { if (yych <= '[') { - goto basic_json_parser_33; + goto basic_json_parser_32; } - goto basic_json_parser_31; + goto basic_json_parser_30; } else { if (yych == 'b') { - goto basic_json_parser_31; + goto basic_json_parser_30; } - goto basic_json_parser_33; + goto basic_json_parser_32; } } } @@ -8102,13 +9705,13 @@ basic_json_parser_36: { if (yych <= 'f') { - goto basic_json_parser_31; + goto basic_json_parser_30; } if (yych == 'n') { - goto basic_json_parser_31; + goto basic_json_parser_30; } - goto basic_json_parser_33; + goto basic_json_parser_32; } else { @@ -8116,130 +9719,235 @@ basic_json_parser_36: { if (yych <= 'r') { - goto basic_json_parser_31; + goto basic_json_parser_30; } - goto basic_json_parser_33; + goto basic_json_parser_32; } else { if (yych <= 't') { - goto basic_json_parser_31; + goto basic_json_parser_30; } if (yych <= 'u') { - goto basic_json_parser_43; + goto basic_json_parser_48; } - goto basic_json_parser_33; + goto basic_json_parser_32; } } } +basic_json_parser_36: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; basic_json_parser_37: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x9F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_36; + } + goto basic_json_parser_32; +basic_json_parser_38: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_36; + } + goto basic_json_parser_32; +basic_json_parser_39: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0x9F) + { + goto basic_json_parser_36; + } + goto basic_json_parser_32; +basic_json_parser_40: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x8F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_38; + } + goto basic_json_parser_32; +basic_json_parser_41: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_38; + } + goto basic_json_parser_32; +basic_json_parser_42: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0x8F) + { + goto basic_json_parser_38; + } + goto basic_json_parser_32; +basic_json_parser_43: yych = *++m_cursor; if (yych <= '/') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= '9') { - goto basic_json_parser_44; + goto basic_json_parser_49; } - goto basic_json_parser_33; -basic_json_parser_38: + goto basic_json_parser_32; +basic_json_parser_44: yych = *++m_cursor; if (yych <= ',') { if (yych == '+') { - goto basic_json_parser_46; + goto basic_json_parser_51; } - goto basic_json_parser_33; + goto basic_json_parser_32; } else { if (yych <= '-') { - goto basic_json_parser_46; + goto basic_json_parser_51; } if (yych <= '/') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= '9') { - goto basic_json_parser_47; + goto basic_json_parser_52; } - goto basic_json_parser_33; + goto basic_json_parser_32; } -basic_json_parser_39: +basic_json_parser_45: yych = *++m_cursor; if (yych == 'l') { - goto basic_json_parser_49; + goto basic_json_parser_54; } - goto basic_json_parser_33; -basic_json_parser_40: + goto basic_json_parser_32; +basic_json_parser_46: yych = *++m_cursor; if (yych == 'l') { - goto basic_json_parser_50; + goto basic_json_parser_55; } - goto basic_json_parser_33; -basic_json_parser_41: + goto basic_json_parser_32; +basic_json_parser_47: yych = *++m_cursor; if (yych == 'u') { - goto basic_json_parser_51; + goto basic_json_parser_56; } - goto basic_json_parser_33; -basic_json_parser_42: - yych = *++m_cursor; - if (yych == 0xBF) - { - goto basic_json_parser_52; - } - goto basic_json_parser_33; -basic_json_parser_43: + goto basic_json_parser_32; +basic_json_parser_48: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= '@') { if (yych <= '/') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= '9') { - goto basic_json_parser_54; + goto basic_json_parser_57; } - goto basic_json_parser_33; + goto basic_json_parser_32; } else { if (yych <= 'F') { - goto basic_json_parser_54; + goto basic_json_parser_57; } if (yych <= '`') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= 'f') { - goto basic_json_parser_54; + goto basic_json_parser_57; } - goto basic_json_parser_33; + goto basic_json_parser_32; } -basic_json_parser_44: +basic_json_parser_49: yyaccept = 1; m_marker = ++m_cursor; if ((m_limit - m_cursor) < 3) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(3); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= 'D') @@ -8250,7 +9958,7 @@ basic_json_parser_44: } if (yych <= '9') { - goto basic_json_parser_44; + goto basic_json_parser_49; } goto basic_json_parser_14; } @@ -8258,29 +9966,29 @@ basic_json_parser_44: { if (yych <= 'E') { - goto basic_json_parser_38; + goto basic_json_parser_44; } if (yych == 'e') { - goto basic_json_parser_38; + goto basic_json_parser_44; } goto basic_json_parser_14; } -basic_json_parser_46: +basic_json_parser_51: yych = *++m_cursor; if (yych <= '/') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych >= ':') { - goto basic_json_parser_33; + goto basic_json_parser_32; } -basic_json_parser_47: +basic_json_parser_52: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= '/') @@ -8289,107 +9997,48 @@ basic_json_parser_47: } if (yych <= '9') { - goto basic_json_parser_47; + goto basic_json_parser_52; } goto basic_json_parser_14; -basic_json_parser_49: +basic_json_parser_54: yych = *++m_cursor; if (yych == 's') { - goto basic_json_parser_55; + goto basic_json_parser_58; } - goto basic_json_parser_33; -basic_json_parser_50: + goto basic_json_parser_32; +basic_json_parser_55: yych = *++m_cursor; if (yych == 'l') { - goto basic_json_parser_56; + goto basic_json_parser_59; } - goto basic_json_parser_33; -basic_json_parser_51: - yych = *++m_cursor; - if (yych == 'e') - { - goto basic_json_parser_58; - } - goto basic_json_parser_33; -basic_json_parser_52: - ++m_cursor; - { - continue; - } -basic_json_parser_54: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych <= '9') - { - goto basic_json_parser_60; - } - goto basic_json_parser_33; - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_60; - } - if (yych <= '`') - { - goto basic_json_parser_33; - } - if (yych <= 'f') - { - goto basic_json_parser_60; - } - goto basic_json_parser_33; - } -basic_json_parser_55: + goto basic_json_parser_32; +basic_json_parser_56: yych = *++m_cursor; if (yych == 'e') { goto basic_json_parser_61; } - goto basic_json_parser_33; -basic_json_parser_56: - ++m_cursor; - { - last_token_type = token_type::literal_null; - break; - } -basic_json_parser_58: - ++m_cursor; - { - last_token_type = token_type::literal_true; - break; - } -basic_json_parser_60: + goto basic_json_parser_32; +basic_json_parser_57: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= '@') { if (yych <= '/') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= '9') { goto basic_json_parser_63; } - goto basic_json_parser_33; + goto basic_json_parser_32; } else { @@ -8399,54 +10048,108 @@ basic_json_parser_60: } if (yych <= '`') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= 'f') { goto basic_json_parser_63; } - goto basic_json_parser_33; + goto basic_json_parser_32; + } +basic_json_parser_58: + yych = *++m_cursor; + if (yych == 'e') + { + goto basic_json_parser_64; + } + goto basic_json_parser_32; +basic_json_parser_59: + ++m_cursor; + { + last_token_type = token_type::literal_null; + break; } basic_json_parser_61: ++m_cursor; { - last_token_type = token_type::literal_false; + last_token_type = token_type::literal_true; break; } basic_json_parser_63: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(1); // LCOV_EXCL_LINE } yych = *m_cursor; if (yych <= '@') { if (yych <= '/') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= '9') { - goto basic_json_parser_31; + goto basic_json_parser_66; } - goto basic_json_parser_33; + goto basic_json_parser_32; } else { if (yych <= 'F') { - goto basic_json_parser_31; + goto basic_json_parser_66; } if (yych <= '`') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= 'f') { - goto basic_json_parser_31; + goto basic_json_parser_66; } - goto basic_json_parser_33; + goto basic_json_parser_32; + } +basic_json_parser_64: + ++m_cursor; + { + last_token_type = token_type::literal_false; + break; + } +basic_json_parser_66: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 'F') + { + goto basic_json_parser_30; + } + if (yych <= '`') + { + goto basic_json_parser_32; + } + if (yych <= 'f') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; } } @@ -8455,30 +10158,93 @@ basic_json_parser_63: return last_token_type; } - /// append data from the stream to the internal buffer - void yyfill() noexcept - { - if (m_stream == nullptr or not * m_stream) - { - return; - } + /*! + @brief append data from the stream to the line buffer - const auto offset_start = m_start - m_content; - const auto offset_marker = m_marker - m_start; + This function is called by the scan() function when the end of the + buffer (`m_limit`) is reached and the `m_cursor` pointer cannot be + incremented without leaving the limits of the line buffer. Note re2c + decides when to call this function. + + If the lexer reads from contiguous storage, there is no trailing null + byte. Therefore, this function must make sure to add these padding + null bytes. + + If the lexer reads from an input stream, this function reads the next + line of the input. + + @pre + p p p p p p u u u u u x . . . . . . + ^ ^ ^ ^ + m_content m_start | m_limit + m_cursor + + @post + u u u u u x x x x x x x . . . . . . + ^ ^ ^ + | m_cursor m_limit + m_start + m_content + */ + void fill_line_buffer(size_t n = 0) + { + // if line buffer is used, m_content points to its data + assert(m_line_buffer.empty() + or m_content == reinterpret_cast(m_line_buffer.data())); + + // if line buffer is used, m_limit is set past the end of its data + assert(m_line_buffer.empty() + or m_limit == m_content + m_line_buffer.size()); + + // pointer relationships + assert(m_content <= m_start); + assert(m_start <= m_cursor); + assert(m_cursor <= m_limit); + assert(m_marker == nullptr or m_marker <= m_limit); + + // number of processed characters (p) + const size_t num_processed_chars = static_cast(m_start - m_content); + // offset for m_marker wrt. to m_start + const auto offset_marker = (m_marker == nullptr) ? 0 : m_marker - m_start; + // number of unprocessed characters (u) const auto offset_cursor = m_cursor - m_start; - m_buffer.erase(0, static_cast(offset_start)); - std::string line; - assert(m_stream != nullptr); - std::getline(*m_stream, line); - m_buffer += "\n" + line; // add line with newline symbol + // no stream is used or end of file is reached + if (m_stream == nullptr or m_stream->eof()) + { + // m_start may or may not be pointing into m_line_buffer at + // this point. We trust the standand library to do the right + // thing. See http://stackoverflow.com/q/28142011/266378 + m_line_buffer.assign(m_start, m_limit); - m_content = reinterpret_cast(m_buffer.c_str()); + // append n characters to make sure that there is sufficient + // space between m_cursor and m_limit + m_line_buffer.append(1, '\x00'); + if (n > 0) + { + m_line_buffer.append(n - 1, '\x01'); + } + } + else + { + // delete processed characters from line buffer + m_line_buffer.erase(0, num_processed_chars); + // read next line from input stream + m_line_buffer_tmp.clear(); + std::getline(*m_stream, m_line_buffer_tmp, '\n'); + + // add line with newline symbol to the line buffer + m_line_buffer += m_line_buffer_tmp; + m_line_buffer.push_back('\n'); + } + + // set pointers + m_content = reinterpret_cast(m_line_buffer.data()); assert(m_content != nullptr); m_start = m_content; m_marker = m_start + offset_marker; m_cursor = m_start + offset_cursor; - m_limit = m_start + m_buffer.size() - 1; + m_limit = m_start + m_line_buffer.size(); } /// return string representation of last read token @@ -8556,9 +10322,20 @@ basic_json_parser_63: // iterate the result between the quotes for (const lexer_char_t* i = m_start + 1; i < m_cursor - 1; ++i) { - // process escaped characters - if (*i == '\\') + // find next escape character + auto e = std::find(i, m_cursor - 1, '\\'); + if (e != i) { + // see https://github.com/nlohmann/json/issues/365#issuecomment-262874705 + for (auto k = i; k < e; k++) + { + result.push_back(static_cast(*k)); + } + i = e - 1; // -1 because of ++i + } + else + { + // processing escaped character // read next character ++i; @@ -8629,6 +10406,11 @@ basic_json_parser_63: // skip the next 10 characters (xxxx\uyyyy) i += 10; } + else if (codepoint >= 0xDC00 and codepoint <= 0xDFFF) + { + // we found a lone low surrogate + throw std::invalid_argument("missing high surrogate"); + } else { // add unicode character(s) @@ -8640,12 +10422,6 @@ basic_json_parser_63: } } } - else - { - // all other characters are just copied to the end of the - // string - result.append(1, static_cast(*i)); - } } return result; @@ -8659,8 +10435,6 @@ basic_json_parser_63: supplied via the first parameter. Set this to @a static_cast(nullptr). - @param[in] type the @ref number_float_t in use - @param[in,out] endptr recieves a pointer to the first character after the number @@ -8679,8 +10453,6 @@ basic_json_parser_63: supplied via the first parameter. Set this to @a static_cast(nullptr). - @param[in] type the @ref number_float_t in use - @param[in,out] endptr recieves a pointer to the first character after the number @@ -8699,8 +10471,6 @@ basic_json_parser_63: supplied via the first parameter. Set this to @a static_cast(nullptr). - @param[in] type the @ref number_float_t in use - @param[in,out] endptr recieves a pointer to the first character after the number @@ -8781,19 +10551,19 @@ basic_json_parser_63: // skip if definitely not an integer if (type != value_t::number_float) { - // multiply last value by ten and add the new digit - auto temp = value * 10 + *curptr - '0'; + auto digit = static_cast(*curptr - '0'); - // test for overflow - if (temp < value || temp > max) + // overflow if value * 10 + digit > max, move terms around + // to avoid overflow in intermediate values + if (value > (max - digit) / 10) { // overflow type = value_t::number_float; } else { - // no overflow - save it - value = temp; + // no overflow + value = value * 10 + digit; } } } @@ -8805,12 +10575,34 @@ basic_json_parser_63: } else if (type == value_t::number_integer) { - result.m_value.number_integer = -static_cast(value); + // invariant: if we parsed a '-', the absolute value is between + // 0 (we allow -0) and max == -INT64_MIN + assert(value >= 0); + assert(value <= max); + + if (value == max) + { + // we cannot simply negate value (== max == -INT64_MIN), + // see https://github.com/nlohmann/json/issues/389 + result.m_value.number_integer = static_cast(INT64_MIN); + } + else + { + // all other values can be negated safely + result.m_value.number_integer = -static_cast(value); + } } else { // parse with strtod result.m_value.number_float = str_to_float_t(static_cast(nullptr), NULL); + + // replace infinity and NAN by null + if (not std::isfinite(result.m_value.number_float)) + { + type = value_t::null; + result.m_value = basic_json::json_value(); + } } // save the type @@ -8820,8 +10612,10 @@ basic_json_parser_63: private: /// optional input stream std::istream* m_stream = nullptr; - /// the buffer - string_t m_buffer; + /// line buffer buffer for m_stream + string_t m_line_buffer {}; + /// used for filling m_line_buffer + string_t m_line_buffer_tmp {}; /// the buffer pointer const lexer_char_t* m_content = nullptr; /// pointer to the beginning of the current symbol @@ -8844,25 +10638,34 @@ basic_json_parser_63: class parser { public: - /// constructor for strings - parser(const string_t& s, const parser_callback_t cb = nullptr) noexcept - : callback(cb), m_lexer(s) - { - // read first token - get_token(); - } + /// a parser reading from a string literal + parser(const char* buff, const parser_callback_t cb = nullptr) + : callback(cb), + m_lexer(reinterpret_cast(buff), std::strlen(buff)) + {} /// a parser reading from an input stream - parser(std::istream& _is, const parser_callback_t cb = nullptr) noexcept - : callback(cb), m_lexer(&_is) - { - // read first token - get_token(); - } + parser(std::istream& is, const parser_callback_t cb = nullptr) + : callback(cb), m_lexer(is) + {} + + /// a parser reading from an iterator range with contiguous storage + template::iterator_category, std::random_access_iterator_tag>::value + , int>::type + = 0> + parser(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr) + : callback(cb), + m_lexer(reinterpret_cast(&(*first)), + static_cast(std::distance(first, last))) + {} /// public parser interface basic_json parse() { + // read first token + get_token(); + basic_json result = parse_internal(true); result.assert_invariant(); @@ -8883,7 +10686,8 @@ basic_json_parser_63: { case lexer::token_type::begin_object: { - if (keep and (not callback or (keep = callback(depth++, parse_event_t::object_start, result)))) + if (keep and (not callback + or ((keep = callback(depth++, parse_event_t::object_start, result)) != 0))) { // explicitly set result to object to cope with {} result.m_type = value_t::object; @@ -8961,7 +10765,8 @@ basic_json_parser_63: case lexer::token_type::begin_array: { - if (keep and (not callback or (keep = callback(depth++, parse_event_t::array_start, result)))) + if (keep and (not callback + or ((keep = callback(depth++, parse_event_t::array_start, result)) != 0))) { // explicitly set result to object to cope with [] result.m_type = value_t::array; @@ -9067,7 +10872,7 @@ basic_json_parser_63: } /// get next token from lexer - typename lexer::token_type get_token() noexcept + typename lexer::token_type get_token() { last_token = m_lexer.scan(); return last_token; @@ -9280,6 +11085,12 @@ basic_json_parser_63: /*! @brief return a reference to the pointed to value + @note This version does not throw if a value is not present, but tries + to create nested values instead. For instance, calling this function + with pointer `"/this/that"` on a null value is equivalent to calling + `operator[]("this").operator[]("that")` on that value, effectively + changing the null value to an object. + @param[in] ptr a JSON value @return reference to the JSON value pointed to by the JSON pointer @@ -9294,6 +11105,29 @@ basic_json_parser_63: { for (const auto& reference_token : reference_tokens) { + // convert null values to arrays or objects before continuing + if (ptr->m_type == value_t::null) + { + // check if reference token is a number + const bool nums = std::all_of(reference_token.begin(), + reference_token.end(), + [](const char x) + { + return std::isdigit(x); + }); + + // change value to array for numbers or "-" or to object + // otherwise + if (nums or reference_token == "-") + { + *ptr = value_t::array; + } + else + { + *ptr = value_t::object; + } + } + switch (ptr->m_type) { case value_t::object: @@ -9475,7 +11309,7 @@ basic_json_parser_63: } /// split the string input to reference tokens - static std::vector split(std::string reference_string) + static std::vector split(const std::string& reference_string) { std::vector result; @@ -9539,13 +11373,11 @@ basic_json_parser_63: /*! @brief replace all occurrences of a substring by another string - @param[in,out] s the string to manipulate + @param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t @param[in] f the substring to replace with @a t @param[in] t the string to replace @a f - @return The string @a s where all occurrences of @a f are replaced - with @a t. - @pre The search string @a f must not be empty. @since version 2.0.0 @@ -9960,7 +11792,7 @@ basic_json_parser_63: json_pointer top_pointer = ptr.top(); if (top_pointer != ptr) { - basic_json& x = result.at(top_pointer); + result.at(top_pointer); } // get reference to parent of JSON pointer ptr @@ -10203,7 +12035,7 @@ basic_json_parser_63: */ static basic_json diff(const basic_json& source, const basic_json& target, - std::string path = "") + const std::string& path = "") { // the patch basic_json result(value_t::array); @@ -10365,7 +12197,7 @@ namespace std @since version 1.0.0 */ -template <> +template<> inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( is_nothrow_move_constructible::value and @@ -10376,7 +12208,7 @@ inline void swap(nlohmann::json& j1, } /// hash value for JSON objects -template <> +template<> struct hash { /*! @@ -10401,30 +12233,32 @@ can be used by adding `"_json"` to a string literal and returns a JSON object if no parse error occurred. @param[in] s a string representation of a JSON object +@param[in] n the length of string @a s @return a JSON object @since version 1.0.0 */ -inline nlohmann::json operator "" _json(const char* s, std::size_t) +inline nlohmann::json operator "" _json(const char* s, std::size_t n) { - return nlohmann::json::parse(reinterpret_cast(s)); + return nlohmann::json::parse(s, s + n); } /*! @brief user-defined string literal for JSON pointer This operator implements a user-defined string literal for JSON Pointers. It -can be used by adding `"_json"` to a string literal and returns a JSON pointer +can be used by adding `"_json_pointer"` to a string literal and returns a JSON pointer object if no parse error occurred. @param[in] s a string representation of a JSON Pointer +@param[in] n the length of string @a s @return a JSON pointer object @since version 2.0.0 */ -inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t) +inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) { - return nlohmann::json::json_pointer(s); + return nlohmann::json::json_pointer(std::string(s, n)); } // restore GCC/clang diagnostic settings diff --git a/external/3rd/library/webAPI/webAPI.cpp b/external/3rd/library/webAPI/webAPI.cpp index a572d9d4..bab5709c 100644 --- a/external/3rd/library/webAPI/webAPI.cpp +++ b/external/3rd/library/webAPI/webAPI.cpp @@ -1,5 +1,5 @@ /* - * Version: 1.5 + * Version: 1.6 * * This code is just a simple wrapper around nlohmann's wonderful json lib * (https://github.com/nlohmann/json) and libcurl. While originally included directly, @@ -55,13 +55,23 @@ std::string webAPI::getString(const std::string &slot) { return std::string(""); } -std::vector webAPI::getStringVector(const std::string &slot) { +std::unordered_map webAPI::getStringMap(const std::string &slot) { + std::unordered_map ret = std::unordered_map(); + if (!this->responseData.empty() && !slot.empty() && responseData.count(slot) && !this->responseData[slot].is_null()) { - return this->responseData[slot].get>(); - } + + nlohmann::json j = this->responseData[slot]; - return std::vector(); + for (nlohmann::json::iterator it = j.begin(); it != j.end(); ++it) { + int k = std::stoi(it.key()); + std::string val = it.value(); + + ret.insert({k, val}); + } + } + + return ret; } bool webAPI::submit(const int &reqType, const int &getPost, const int &respType) { @@ -112,6 +122,8 @@ bool webAPI::fetch(const int &getPost, const int &mimeType) // 0 for json 1 for writeCallback); // place the data into readBuffer using writeCallback curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); // specify readBuffer as the container for data curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); switch (getPost) { case HTTP::GET: @@ -182,4 +194,4 @@ bool webAPI::processJSON() { } return false; -} \ No newline at end of file +} diff --git a/external/3rd/library/webAPI/webAPI.h b/external/3rd/library/webAPI/webAPI.h index cd9a6a4c..ac5bf6d9 100644 --- a/external/3rd/library/webAPI/webAPI.h +++ b/external/3rd/library/webAPI/webAPI.h @@ -1,5 +1,5 @@ /* - * Version: 1.5 + * Version: 1.6 * * This code is just a simple wrapper around nlohmann's wonderful json lib * (https://github.com/nlohmann/json) and libcurl. While originally included directly, @@ -24,6 +24,7 @@ #include #else +#include #include #endif @@ -63,7 +64,7 @@ namespace StellaBellum { std::string getString(const std::string &slot); // get a vector of strings from a given slot - std::vector getStringVector(const std::string &slot); + std::unordered_map getStringMap(const std::string &slot); // set json key and value for request template