コード例 #1
0
ファイル: server.cpp プロジェクト: AlberFernandez/MRCore
void* Server::handleClient(void *d)
{
	Socket* aux_sock=(Socket*)d;
	Socket sock=*aux_sock;

	while(!closeServer)
	{		
		char header[HEADER_SIZE];	
		//Blocking reception (last param=-1)
		int ret=sock.Receive(header,HEADER_SIZE,-1);
		if(-1==ret)
		{
			LOG_ERROR("Receive error");//sock.Receive outputs LOG
			break;
		}
		if(ret==0)
		{
			LOG_ERROR("Should never be here");
			continue;
		}
		Message message;//buffering
		if(!message.addHeader(header,ret))
		{
			LOG_ERROR("Incorrect header "<<ret<<" "<<string(header,ret));
			break;
		}
		const int contentSize=message.getContentSize();
		char* buffer=new char[contentSize];
		int timeout=100+contentSize/10;
		if(!sock.ReceiveBytes(buffer,contentSize,timeout))
		{
			LOG_ERROR("Unable to get full message");
			break;
		}
		message.addContents(buffer,contentSize);
		delete buffer;
		
		string response=handleRequest(message.getContent());//call to virtual method
		Message responseMsg(response);
		sock.Send(responseMsg.getBuffer(),responseMsg.bufferSize());	//send response message
		
	}
	sock.close();

	mutex.Lock();
	for(int i=sockClients.size()-1;i>=0;i--)
	{
		if(sockClients[i]==aux_sock)
		{
			sockClients.erase(sockClients.begin()+i);
			clients.erase(clients.begin()+i);
			LOG_INFO("Closing thread and socket");
			break;
		}
	}
	mutex.Unlock();

	LOG_INFO("Server: Finishing client. NumClients: "<<sockClients.size());	
	return NULL;
}
コード例 #2
0
ファイル: zgw.cpp プロジェクト: zhangnian/zgw
void ZGWServer::pullThreadFunc()
{
    LOG_INFO << "PULL线程启动, TID: " << muduo::CurrentThread::tid();
    assert( NULL != zmqContext_ );

    std::string pull_endpoint = ini_.GetValue("backend", "pull_service", "");
    if( pull_endpoint.size() == 0 )
    {
        LOG_ERROR << "未设置PULL socket的地址";
        return;
    }

    void* pullSocket = zmq_socket(zmqContext_, ZMQ_PULL);
    assert( NULL != pullSocket );

    int rc = zmq_bind(pullSocket, pull_endpoint.c_str());
    assert( rc == 0 );

    LOG_INFO << "绑定PULL Socket成功,地址: " << pull_endpoint;
    while(true)
    {
        zmq_msg_t msg_t;
        rc = zmq_msg_init(&msg_t);
        assert( 0 == rc );

        rc = zmq_msg_recv(&msg_t, pullSocket, 0);
        if( rc == -1 )
        {
            LOG_ERROR << "PULL线程接收消息失败, errno: " << errno;
        }
        else
        {
            responseMsg(msg_t);
        }
        zmq_msg_close(&msg_t);
    }
}
コード例 #3
0
bool MMG_AccountProtocol::HandleMessage(SvClient *aClient, MN_ReadMessage *aMessage, MMG_ProtocolDelimiters::Delimiter aDelimiter)
{
	Query myQuery;
	if (!myQuery.FromStream(aDelimiter, aMessage))
		return false;

	// Copy over the data to the main client manager class
	aClient->m_CipherIdentifier				= myQuery.m_CipherIdentifier;
	aClient->m_EncryptionKeySequenceNumber	= myQuery.m_EncryptionKeySequenceNumber;
	memcpy(aClient->m_CipherKeys, myQuery.m_CipherKeys, sizeof(myQuery.m_CipherKeys));

	MN_WriteMessage cryptMessage(1024);
	MMG_ProtocolDelimiters::Delimiter responseDelimiter;

	// Write the base identifier (random number, only a marker)
	cryptMessage.WriteUInt(myQuery.m_RandomKey);

	if (myQuery.m_StatusCode == IncorrectProtocolVersion || !myQuery.VerifyProductId())
	{
		// Invalid game version
		// This takes priority over maintenance due to potential protocol differences
		DebugLog(L_INFO, "ACCOUNT_PATCH_INFORMATION: Client has an old version [%d vs %d]", myQuery.m_Protocol, MassgateProtocolVersion);
		responseDelimiter = MMG_ProtocolDelimiters::ACCOUNT_PATCH_INFORMATION;

		this->WritePatchInformation(&cryptMessage, &myQuery);
	}
	else if (this->m_MaintenanceMode)
	{
		// Massgate/Server maintenance notice
		DebugLog(L_INFO, "ACCOUNT_NOT_CONNECTED: Sending Maintenance Notice");
		responseDelimiter = MMG_ProtocolDelimiters::ACCOUNT_NOT_CONNECTED;

		this->WriteMaintenanceInformation(&cryptMessage);
	}
	else
	{
		switch(myQuery.m_Delimiter)
		{
			// Account authorization: Login
			case MMG_ProtocolDelimiters::ACCOUNT_AUTH_ACCOUNT_REQ:
			{
#ifndef USING_MYSQL_DATABASE
				// Query the database and determine if credentials were valid
				uint accProfileId = 0;//Database::AuthUserAccount(myQuery.m_Authenticate.m_Email, myQuery.m_Authenticate.m_Password);

				DebugLog(L_INFO, "ACCOUNT_AUTH_ACCOUNT_RSP: Sending login response (id %i)", accProfileId);
				responseDelimiter = MMG_ProtocolDelimiters::ACCOUNT_AUTH_ACCOUNT_RSP;

				if (accProfileId == WIC_INVALID_ACCOUNT)
				{
					cryptMessage.WriteUChar(AuthFailed_BadCredentials);

					cryptMessage.WriteUChar(0);	// auth.mySuccessFlag
					cryptMessage.WriteUShort(0);// auth.myLatestVersion
				}
				else
				{
					// Update the maximum client timeout
					aClient->SetLoginStatus(true);
					aClient->SetIsPlayer(true);
					aClient->SetTimeout(WIC_LOGGEDIN_NET_TIMEOUT);

					cryptMessage.WriteUChar(AuthSuccess);
					cryptMessage.WriteUChar(1);	// auth.mySuccessFlag
					cryptMessage.WriteUShort(0);// auth.myLatestVersion

					// Query profile
					//if (!Database::QueryUserProfile(accProfileId, aClient->GetProfile()))
					//	DebugLog(L_ERROR, "Failed to retrieve profile for valid account ");
					wcscpy_s(aClient->GetProfile()->m_Name, L"Nukem");
					aClient->GetProfile()->m_OnlineStatus = 1;
					aClient->GetProfile()->m_Rank = 18;
					aClient->GetProfile()->m_ProfileId = 1234;
					//aClient->GetProfile()->m_ClanId = 4321;
					//aClient->GetProfile()->m_RankInClan = 1;

					// Write the profile info stream
					aClient->GetProfile()->ToStream(&cryptMessage);

					MMG_AuthToken *myAuthToken = aClient->GetToken();

					//sync client->authtoken.profileid to client->profile.profileid
					myAuthToken->m_ProfileId = 1234; //myAuthToken->m_ProfileId = aClient->GetProfile()->m_ProfileId;
					myAuthToken->m_AccountId = 69;
					
					// TigerMD5 of ...? (possibly crypt keys)
					/*myAuthToken->m_Hash.m_Hash[0] = 0x558C0A1C;
					myAuthToken->m_Hash.m_Hash[1] = 0xA59C9FCA;
					myAuthToken->m_Hash.m_Hash[2] = 0x6566857D;
					myAuthToken->m_Hash.m_Hash[3] = 0x8A3FF551;
					myAuthToken->m_Hash.m_Hash[4] = 0xB69D17E5;
					myAuthToken->m_Hash.m_Hash[5] = 0xD7BBF74D;*/
					memset(&myAuthToken->m_Hash.m_Hash, 0, 6 * sizeof(ulong));

					myAuthToken->m_Hash.m_HashLength = 6 * sizeof(ulong);
					myAuthToken->m_Hash.m_GeneratedFromHashAlgorithm = HASH_ALGORITHM_TIGER;
					myAuthToken->ToStream(&cryptMessage);// Write the authorization token info stream

					cryptMessage.WriteUInt(WIC_CREDAUTH_RESEND_S);	// periodicityOfCredentialsRequests (How long until the first is sent)
					cryptMessage.WriteUInt(0);						// myLeaseTimeLeft (Limited access key)
					cryptMessage.WriteUInt(45523626);				// myAntiSpoofToken (Random number)
				}
#else		
				//DebugLog(L_INFO, "ACCOUNT_AUTH_ACCOUNT_RSP: Sending login response (id %i)", AccountId);
				DebugLog(L_INFO, "ACCOUNT_AUTH_ACCOUNT_RSP:");
				responseDelimiter = MMG_ProtocolDelimiters::ACCOUNT_AUTH_ACCOUNT_RSP;

				MMG_AuthToken *myAuthToken = aClient->GetToken();
				MMG_Profile *myProfile = aClient->GetProfile();

				//password check should be done by massgate server, not by database
				PasswordHash hasher(8, false);

				char myPasswordHash[WIC_PASSWORDHASH_MAX_LENGTH];
				memset(myPasswordHash, 0, sizeof(myPasswordHash));

				char myPasswordMD5[64];
				memset(myPasswordMD5, 0, sizeof(myPasswordMD5));

				MC_Misc::MD5String(myQuery.m_Authenticate.m_Password, myPasswordMD5);

				uchar isBanned = 0;

				uint myStatusCode = 0;
				uint mySuccessFlag = 0;

				// Query the database
				bool AuthQueryOK = MySQLDatabase::ourInstance->AuthUserAccount(myQuery.m_Authenticate.m_Email, myPasswordHash, &isBanned, myAuthToken);

				// TODO: generate a better authtoken
				myAuthToken->m_TokenId = MC_MTwister().Random();

				//determine if credentials were valid
				if(myAuthToken->m_AccountId == 0 && AuthQueryOK)		//account doesnt exist
				{
					myStatusCode = AuthFailed_NoSuchAccount;
					mySuccessFlag = 0;
				}
				else if(!hasher.CheckPassword(myPasswordMD5, myPasswordHash) && AuthQueryOK)	//wrong password
				{
					myStatusCode = AuthFailed_BadCredentials;
					mySuccessFlag = 0;
				}
				else if(isBanned && AuthQueryOK)						//account has been banned
				{
					myStatusCode = AuthFailed_AccountBanned;
					mySuccessFlag = 0;
				}
				else if(myAuthToken->m_ProfileId == 0 && AuthQueryOK)	//no profiles exist. bring up add profile box
				{
					myStatusCode = AuthFailed_RequestedProfileNotFound;
					mySuccessFlag = 0;
				}
				else if(SvClientManager::ourInstance->AccountInUse(myAuthToken->m_AccountId))
				{
					myStatusCode = AuthFailed_AccountInUse;
					mySuccessFlag = 0;
				}
				else if(!AuthQueryOK)									// something went wrong executing the query
				{
					myStatusCode = AuthFailed_General; //ServerError
					mySuccessFlag = 0;
				}
				else													//should be ok to retrieve a profile
				{
					// TODO insert missing data
					if (myAuthToken->m_AccountId > 0)
					{
						// update geoip info
						MySQLDatabase::ourInstance->UpdateRealCountry(myAuthToken->m_AccountId, aClient->GetIPAddress());

						// update missing sequence number & cipherkeys for handmade accounts
						MySQLDatabase::ourInstance->UpdateCDKeyInfo(myAuthToken->m_AccountId, myQuery.m_EncryptionKeySequenceNumber, myQuery.m_CipherKeys);

						// if current password hash is md5 based, update to blowfish
						if (!strncmp(myPasswordHash, "$H$", 3))
						{
							memset(myPasswordHash, 0, sizeof(myPasswordHash));
							hasher.HashPassword(myPasswordHash, myPasswordMD5);
							MySQLDatabase::ourInstance->UpdatePassword(myAuthToken->m_AccountId, myPasswordHash);
						}
					}

					bool ProfileQueryOK;

					//AuthFailed_AccountInUse, AuthFailed_ProfileInUse, AuthFailed_CdKeyInUse, AuthFailed_IllegalCDKey(not using)

					//profile selection box was used
					if(myQuery.m_Authenticate.m_UseProfile)
					{
						ProfileQueryOK = MySQLDatabase::ourInstance->QueryUserProfile(myAuthToken->m_AccountId, myQuery.m_Authenticate.m_UseProfile, myProfile);

						//if(myQuery.m_Authenticate.m_UseProfile > 0 && ProfileQueryOK)
						if(ProfileQueryOK)											//ok to login, set active profile
						{
							myAuthToken->m_ProfileId = myProfile->m_ProfileId;
							myStatusCode = AuthSuccess;
							mySuccessFlag = 1;
						}
						else //!ProfileQueryOK										// something went wrong executing the query
						{
							myStatusCode = AuthFailed_General; //ServerError
							mySuccessFlag = 0;
						}
					}

					//login button was used
					else
					{
						//if(myQuery.m_Authenticate.m_HasOldCredentials)
						//myQuery.m_Authenticate.m_Credentials, myQuery.m_Authenticate.m_Profile, myQuery.m_Authenticate.m_UseProfile
						//myAuthToken, myProfile

						ProfileQueryOK = MySQLDatabase::ourInstance->QueryUserProfile(myAuthToken->m_AccountId, myAuthToken->m_ProfileId, myProfile);

						if(myAuthToken->m_ProfileId > 0 && ProfileQueryOK)			// ok to login, set active profile
						{
							myAuthToken->m_ProfileId = myProfile->m_ProfileId;
							myStatusCode = AuthSuccess;
							mySuccessFlag = 1;
						}
						else //!ProfileQueryOK										// something went wrong executing the query
						{
							myStatusCode = AuthFailed_General; //ServerError
							mySuccessFlag = 0;
						}
					}

					//TODO: use SetLoginStatus() + SetTimeout(), we need
					//a way to determine if a profile or account is currently in use
					//auth.myLatestVersion??

					// Update the maximum client timeout
					aClient->SetLoginStatus(true);
					aClient->SetIsPlayer(true);
					aClient->SetTimeout(WIC_LOGGEDIN_NET_TIMEOUT);
				}

				//write response message to the stream;
				cryptMessage.WriteUChar(myStatusCode);
				cryptMessage.WriteUChar(mySuccessFlag);	// auth.mySuccessFlag
				cryptMessage.WriteUShort(0);			// auth.myLatestVersion

				// Write the profile info stream
				myProfile->ToStream(&cryptMessage);

				// TigerMD5 of ...? (possibly crypt keys)
				/*myAuthToken->m_Hash.m_Hash[0] = 0x558C0A1C;
				myAuthToken->m_Hash.m_Hash[1] = 0xA59C9FCA;
				myAuthToken->m_Hash.m_Hash[2] = 0x6566857D;
				myAuthToken->m_Hash.m_Hash[3] = 0x8A3FF551;
				myAuthToken->m_Hash.m_Hash[4] = 0xB69D17E5;
				myAuthToken->m_Hash.m_Hash[5] = 0xD7BBF74D;
				memset(&myAuthToken->m_Hash.m_Hash, 0, 6 * sizeof(ulong));*/

				myAuthToken->m_Hash.m_HashLength = 6 * sizeof(ulong);
				myAuthToken->m_Hash.m_GeneratedFromHashAlgorithm = HASH_ALGORITHM_TIGER;

				// Write the authorization token info stream
				myAuthToken->ToStream(&cryptMessage);

				cryptMessage.WriteUInt(WIC_CREDAUTH_RESEND_S);	// periodicityOfCredentialsRequests (How long until the first is sent)
				cryptMessage.WriteUInt(0);						// myLeaseTimeLeft (Limited access key)
				//cryptMessage.WriteUInt(45523626);				// myAntiSpoofToken (Random number)
				cryptMessage.WriteUInt(myAuthToken->m_TokenId);	// TODO
#endif
			}
			break;

			// Client request to create a new account
			case MMG_ProtocolDelimiters::ACCOUNT_CREATE_ACCOUNT_REQ:
			{
				DebugLog(L_INFO, "ACCOUNT_CREATE_ACCOUNT_RSP:");
				responseDelimiter = MMG_ProtocolDelimiters::ACCOUNT_CREATE_ACCOUNT_RSP;
#ifdef USING_MYSQL_DATABASE
				bool isPrivateKeyUser = false;

				uint myAccountId = 0;
				uint myCdkeyId = 0;
				
				uint myStatusCode = 0;
				uint mySuccessFlag = 0;

				bool CheckEmailQueryOK = MySQLDatabase::ourInstance->CheckIfEmailExists(myQuery.m_Create.m_Email, &myAccountId);
				bool CheckCDKeyQueryOK = MySQLDatabase::ourInstance->CheckIfCDKeyExists(myQuery.m_EncryptionKeySequenceNumber, &myCdkeyId);

				if (myAccountId > 0 && CheckEmailQueryOK)
				{
					myStatusCode = CreateFailed_EmailExists;
					mySuccessFlag = 0;
				}
				else if (myCdkeyId > 0 && CheckCDKeyQueryOK)
				{
					myStatusCode = CreateFailed_CdKeyExhausted;
					mySuccessFlag = 0;
				}
				else if (!CheckEmailQueryOK || !CheckCDKeyQueryOK)
				{
					myStatusCode = CreateFailed_General; //ServerError
					mySuccessFlag = 0;
				}
				else
				{
					uint checkId = 0;
					uchar checkValidated = 0;

					char checkEmail[WIC_EMAIL_MAX_LENGTH];
					memset(checkEmail, 0, sizeof(checkEmail));

					bool CheckPrivateCDKeyQueryOk = MySQLDatabase::ourInstance->CheckIfPrivateCDKeyUser(myQuery.m_EncryptionKeySequenceNumber, &checkId, checkEmail, &checkValidated);

					if (checkId > 0 && CheckPrivateCDKeyQueryOk)
					{
						// private
						isPrivateKeyUser = true;
						
						uint myPrivateCDKeyId = 0;
						uint checkAccountId = 0;

						// todo: extra checks, ip address, cipherkeys? etc
						bool AuthPrivateCDKeyQueryOk = MySQLDatabase::ourInstance->AuthPrivateCDKey(myQuery.m_EncryptionKeySequenceNumber, myQuery.m_Create.m_Email, &myPrivateCDKeyId, &checkAccountId);

						if (myPrivateCDKeyId == 0 && AuthPrivateCDKeyQueryOk)
						{
							myStatusCode = CreateFailed_General;
							mySuccessFlag = 0;
						}
						else if (checkAccountId > 0 && AuthPrivateCDKeyQueryOk)
						{
							myStatusCode = CreateFailed_CdKeyExhausted;
							mySuccessFlag = 0;
						}
						else if (!AuthPrivateCDKeyQueryOk)
						{
							myStatusCode = CreateFailed_General;
							mySuccessFlag = 0;
						}
						else
						{
							myStatusCode = ActionStatusCodes::Creating;
							mySuccessFlag = 1;
						}
					}
					else
					{
						// purchased key from before the shutdown
						myStatusCode = ActionStatusCodes::Creating;
						mySuccessFlag = 1;
					}

					if (myStatusCode == ActionStatusCodes::Creating && mySuccessFlag == 1)
					{
						char realcountry[WIC_COUNTRY_MAX_LENGTH];
						memset(realcountry, 0, sizeof(realcountry));

						strcpy_s(realcountry, GeoIP::ClientLocateIP(aClient->GetIPAddress()));

						PasswordHash hasher(8, false);

						char myPasswordHash[WIC_PASSWORDHASH_MAX_LENGTH];
						memset(myPasswordHash, 0, sizeof(myPasswordHash));

						char myPasswordMD5[64];
						memset(myPasswordMD5, 0, sizeof(myPasswordMD5));

						MC_Misc::MD5String(myQuery.m_Create.m_Password, myPasswordMD5);
						hasher.HashPassword(myPasswordHash, myPasswordMD5);

						bool CreateQueryOK = MySQLDatabase::ourInstance->CreateUserAccount(isPrivateKeyUser, myQuery.m_Create.m_Email, myPasswordHash, myQuery.m_Create.m_Country, realcountry, &myQuery.m_Create.m_EmailMeGameRelated, &myQuery.m_Create.m_AcceptsEmail, myQuery.m_EncryptionKeySequenceNumber, myQuery.m_CipherKeys);

						if (CreateQueryOK)
						{
							myStatusCode = CreateSuccess;
							mySuccessFlag = 1;
						}
						else
						{
							myStatusCode = CreateFailed_General; //ServerError
							mySuccessFlag = 0;
						}
					}
				}

				cryptMessage.WriteUChar(myStatusCode);
				cryptMessage.WriteUChar(mySuccessFlag);
#endif
			}
			break;

			// Prepare (sent before ACCOUNT_CREATE_ACCOUNT_REQ) authorization for cd-key
			case MMG_ProtocolDelimiters::ACCOUNT_PREPARE_CREATE_ACCOUNT_REQ:
			{
				DebugLog(L_INFO, "ACCOUNT_PREPARE_CREATE_ACCOUNT_RSP:");
				responseDelimiter = MMG_ProtocolDelimiters::ACCOUNT_PREPARE_CREATE_ACCOUNT_RSP;

				char country[WIC_COUNTRY_MAX_LENGTH];	// Guessed by IPv4 geolocation information

#ifndef USING_MYSQL_DATABASE
				strcpy_s(country, "US");
#else
				char* countrycode = GeoIP::ClientLocateIP(aClient->GetIPAddress());
				strcpy_s(country, countrycode);
#endif
				cryptMessage.WriteUChar(AuthSuccess);	// Otherwise AuthFailed_CdKeyExpired
				cryptMessage.WriteUChar(1);				// mySuccessFlag
				cryptMessage.WriteString(country);		// yourCountry
			}
			break;

			// Client requests a session update to prevent dropping
			case MMG_ProtocolDelimiters::ACCOUNT_NEW_CREDENTIALS_REQ:
			{
				DebugLog(L_INFO, "ACCOUNT_NEW_CREDENTIALS_RSP:");
				responseDelimiter = MMG_ProtocolDelimiters::ACCOUNT_NEW_CREDENTIALS_RSP;

				// Default to success until it's actually implemented (if ever)
				cryptMessage.WriteUChar(AuthSuccess);

				// Write the authorization token info stream
				aClient->GetToken()->ToStream(&cryptMessage);

				// doCredentialsRequestAgain (in seconds)
				cryptMessage.WriteUInt(WIC_CREDAUTH_RESEND_S);
			}
			break;

			// Retrieve account profiles list (maximum 5)
			case MMG_ProtocolDelimiters::ACCOUNT_RETRIEVE_PROFILES_REQ:
			{
				DebugLog(L_INFO, "ACCOUNT_RETRIEVE_PROFILES_RSP: getting profiles for %s", myQuery.m_RetrieveProfiles.m_Email);
				responseDelimiter = MMG_ProtocolDelimiters::ACCOUNT_RETRIEVE_PROFILES_RSP;
#ifndef USING_MYSQL_DATABASE
				cryptMessage.WriteUChar(AuthSuccess);
				cryptMessage.WriteUChar(1);	// mySuccessFlag
				cryptMessage.WriteUInt(1);	// numUserProfiles
				cryptMessage.WriteUInt(1234);	// lastUsedProfileId

				MMG_Profile *myProfile = aClient->GetProfile();
				wcscpy_s(myProfile->m_Name, L"Nukem");
				myProfile->m_ProfileId = 1234;
				//myProfile->m_ClanId = 4321;
				myProfile->m_OnlineStatus = 0;
				myProfile->m_Rank = 18;
				//myProfile->m_RankInClan = 1;

				// Write the profile info stream
				myProfile->ToStream(&cryptMessage);
#else
				MMG_AuthToken *myAuthToken = aClient->GetToken();
				MMG_Profile myProfiles[5];

				PasswordHash hasher(8, false);

				char myPasswordHash[WIC_PASSWORDHASH_MAX_LENGTH];
				memset(myPasswordHash, 0, sizeof(myPasswordHash));

				char myPasswordMD5[64];
				memset(myPasswordMD5, 0, sizeof(myPasswordMD5));

				MC_Misc::MD5String(myQuery.m_RetrieveProfiles.m_Password, myPasswordMD5);

				uchar isBanned = 0;
				ulong myProfileCount = 0;
				uint lastUsedId = 0;

				uint myStatusCode = 0;
				uint mySuccessFlag = 0;

				bool AuthQueryOK = MySQLDatabase::ourInstance->AuthUserAccount(myQuery.m_RetrieveProfiles.m_Email, myPasswordHash, &isBanned, myAuthToken);

				//determine if credentials were valid
				if(myAuthToken->m_AccountId == 0 && AuthQueryOK)		//account doesnt exist
				{
					myStatusCode = AuthFailed_BadCredentials;	//AuthFailed_NoSuchAccount
					mySuccessFlag = 0;
				}
				else if(!hasher.CheckPassword(myPasswordMD5, myPasswordHash) && AuthQueryOK)	//wrong password
				{
					myStatusCode = AuthFailed_BadCredentials;
					mySuccessFlag = 0;
				}
				else if(isBanned && AuthQueryOK)						//account has been banned
				{
					myStatusCode = AuthFailed_AccountBanned;
					mySuccessFlag = 0;
				}
				else if(SvClientManager::ourInstance->AccountInUse(myAuthToken->m_AccountId))
				{
					myStatusCode = AuthFailed_AccountInUse;
					mySuccessFlag = 0;
				}
				else if(!AuthQueryOK)									// something went wrong executing the query
				{
					myStatusCode = AuthFailed_General; //ServerError
					mySuccessFlag = 0;
				}
				else													//should be ok to retrieve profile list
				{
					// TODO insert missing data
					if (myAuthToken->m_AccountId > 0)
					{
						// update geoip info
						MySQLDatabase::ourInstance->UpdateRealCountry(myAuthToken->m_AccountId, aClient->GetIPAddress());

						// update missing sequence number & cipherkeys for handmade accounts
						MySQLDatabase::ourInstance->UpdateCDKeyInfo(myAuthToken->m_AccountId, myQuery.m_EncryptionKeySequenceNumber, myQuery.m_CipherKeys);

						// if current password hash is md5 based, update to blowfish
						if (!strncmp(myPasswordHash, "$H$", 3))
						{
							memset(myPasswordHash, 0, sizeof(myPasswordHash));
							hasher.HashPassword(myPasswordHash, myPasswordMD5);
							MySQLDatabase::ourInstance->UpdatePassword(myAuthToken->m_AccountId, myPasswordHash);
						}
					}

					bool RetrieveProfilesQueryOK = MySQLDatabase::ourInstance->RetrieveUserProfiles(myAuthToken->m_AccountId, &myProfileCount, myProfiles);

					if(RetrieveProfilesQueryOK)
					{
						myStatusCode = AuthSuccess;
						mySuccessFlag = 1;

						lastUsedId = myProfiles[0].m_ProfileId;	//myAuthToken->m_ProfileId
					}
					else
					{
						myStatusCode = AuthFailed_General; //ServerError
						mySuccessFlag = 0;
						
						myProfileCount = 0;
						lastUsedId = 0;
					}
				}

				cryptMessage.WriteUChar(myStatusCode);
				cryptMessage.WriteUChar(mySuccessFlag);	// mySuccessFlag
				cryptMessage.WriteUInt(myProfileCount);	// numUserProfiles
				cryptMessage.WriteUInt(lastUsedId);	// lastUsedProfileId

				//write profile/s to stream
				for(uint i=0; i < myProfileCount; i++)
					myProfiles[i].ToStream(&cryptMessage);
#endif
			}
			break;

			case MMG_ProtocolDelimiters::ACCOUNT_MODIFY_PROFILE_REQ:
			{
#ifdef USING_MYSQL_DATABASE
				//DebugLog(L_INFO, "ACCOUNT_MODIFY_PROFILE_RSP: modify profiles");
				//responseDelimiter = MMG_ProtocolDelimiters::ACCOUNT_MODIFY_PROFILE_RSP;
				responseDelimiter = MMG_ProtocolDelimiters::ACCOUNT_RETRIEVE_PROFILES_RSP;

				MMG_AuthToken *myAuthToken = aClient->GetToken();
				MMG_Profile myProfiles[5];

				uchar isBanned = 0;
				ulong myProfileCount = 0;
				uint lastUsedId = 0;

				uint myProfileId = 0;

				uint myStatusCode = 0;
				uint mySuccessFlag = 0;

				if (myQuery.m_ModifyProfile.m_Operation == 'add')
				{
					DebugLog(L_INFO, "ACCOUNT_MODIFY_PROFILE_RSP: add new profile for %s", myQuery.m_ModifyProfile.m_Email);
					
					bool CheckProfileQueryOK = MySQLDatabase::ourInstance->CheckIfProfileExists(myQuery.m_ModifyProfile.m_Name, &myProfileId);
					
					if (myProfileId > 0 && CheckProfileQueryOK)			//profile exists with that name
					{
						myStatusCode = ModifyFailed_ProfileNameTaken;
						mySuccessFlag = 1;
					}
					else if (!CheckProfileQueryOK)
					{
						myStatusCode = ModifyFailed_General;		//server / database error
						mySuccessFlag = 1;
					}
					else							//should be ok to create profile
					{
						bool CreateProfileQueryOK = MySQLDatabase::ourInstance->CreateUserProfile(myAuthToken->m_AccountId, myQuery.m_ModifyProfile.m_Name, myQuery.m_ModifyProfile.m_Email);
					
						if(CreateProfileQueryOK)			//create profile success
						{
							myStatusCode = ModifySuccess;
							mySuccessFlag = 1;
						}
						else //!CreateProfileQueryOK		// something went wrong executing the query
						{
							myStatusCode = ModifyFailed_General; //ServerError
							mySuccessFlag = 1;
						}
					}
				}
				else if (myQuery.m_ModifyProfile.m_Operation == 'del')
				{
					DebugLog(L_INFO, "ACCOUNT_MODIFY_PROFILE_RSP: delete profile %d for %s", myQuery.m_ModifyProfile.m_ProfileId, myQuery.m_ModifyProfile.m_Email);

					MMG_Profile myProfile;
					bool ProfileQueryOK = MySQLDatabase::ourInstance->QueryProfileName(myQuery.m_ModifyProfile.m_ProfileId, &myProfile);
					
					if (!ProfileQueryOK)
					{
						myStatusCode = ModifyFailed_General;
						mySuccessFlag = 0;
					}
					else if (ProfileQueryOK && myProfile.m_ClanId > 0)
					{
						myStatusCode = DeleteProfile_Failed_Clan;
						mySuccessFlag = 1;
					}
					else
					{
						bool DeleteProfileQueryOK = MySQLDatabase::ourInstance->DeleteUserProfile(myAuthToken->m_AccountId, myQuery.m_ModifyProfile.m_ProfileId, myQuery.m_ModifyProfile.m_Email);
					
						if(DeleteProfileQueryOK)			//delete profile success
						{
							myStatusCode = ModifySuccess;
							mySuccessFlag = 1;
						}
						else //!DeleteProfileQueryOK		// something went wrong executing the query
						{
							myStatusCode = ModifyFailed_General; //ServerError
							mySuccessFlag = 1;
						}
					}
				}
				else
				{
					DebugLog(L_INFO, "ACCOUNT_MODIFY_PROFILE_RSP: unknown operation");
				}
				
				// retrieve and send profile list
				bool RetrieveProfilesQueryOK = MySQLDatabase::ourInstance->RetrieveUserProfiles(myAuthToken->m_AccountId, &myProfileCount, myProfiles);

				if (RetrieveProfilesQueryOK && mySuccessFlag)
				{
					lastUsedId = myProfiles[0].m_ProfileId;	//myAuthToken->m_ProfileId
				}
				else
				{
					myStatusCode = ModifyFailed_General; //ServerError
					mySuccessFlag = 0;
				
					myProfileCount = 0;
					lastUsedId = 0;
				}
				
				cryptMessage.WriteUChar(myStatusCode);
				cryptMessage.WriteUChar(mySuccessFlag);	// mySuccessFlag
				cryptMessage.WriteUInt(myProfileCount);	// numUserProfiles
				cryptMessage.WriteUInt(lastUsedId);		// lastUsedProfileId

				//write profile/s to stream
				for(uint i=0; i < myProfileCount; i++)
					myProfiles[i].ToStream(&cryptMessage);
#endif
			}
			break;

			default:
				DebugLog(L_WARN, "Unknown delimiter %i", aDelimiter);
			return false;
		}
	}

	// Write the main message header
	MN_WriteMessage	responseMsg(1024);
	responseMsg.WriteDelimiter(responseDelimiter);
	responseMsg.WriteUShort(MassgateProtocolVersion);
	responseMsg.WriteUChar(aClient->m_CipherIdentifier);
	responseMsg.WriteUInt(aClient->m_EncryptionKeySequenceNumber);

	// Encrypt and write the data to the main (outgoing) packet
	// Packet buffer can be modified because it is no longer used
	sizeptr_t dataLength = cryptMessage.GetDataLength();
	voidptr_t dataStream = cryptMessage.GetDataStream();

	if (!MMG_ICipher::EncryptWith(aClient->m_CipherIdentifier, aClient->m_CipherKeys, (uint *)dataStream, dataLength))
		return false;

	responseMsg.WriteUShort(dataLength);
	responseMsg.WriteRawData(dataStream, dataLength);

	// Finally send the message
	if (!aClient->SendData(&responseMsg))
		return false;

	return true;
}