text: /*
   *  Original work: Copyright (c) 2014, Oculus VR, Inc.
   *  All rights reserved.
   *
   *  This source code is licensed under the BSD-style license found in the
   *  RakNet License.txt file in the licenses directory of this source tree. An additional grant 
   *  of patent rights can be found in the RakNet Patents.txt file in the same directory.
   *
   *
   *  Modified work: Copyright (c) 2016-2017, SLikeSoft UG (haftungsbeschränkt)
   *
   *  This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style
   *  license found in the license.txt file in the root directory of this source tree.
   */
  
  #include "Rackspace2.h"
  #include "slikenet/TCPInterface.h"
  #include "slikenet/HTTPConnection2.h"
  #include "slikenet/linux_adapter.h"
  #include "slikenet/osx_adapter.h"
  
  using namespace SLNet;
  
  Rackspace2::Rackspace2()
  {
  	X_Auth_Token[0]=0;
  	eventCallback=0;
  	tcp=0;
  	cloudAccountNumber=0;
  	reexecuteLastRequestOnAuth=false;
  }
  Rackspace2::~Rackspace2()
  {
  
  }
  void Rackspace2::Update(void)
  {
  	RakString stringTransmitted;
  	RakString hostTransmitted;
  	RakString responseReceived;
  	Packet *packet;
  	SystemAddress sa;
  	// This is kind of crappy, but for TCP plugins, always do HasCompletedConnectionAttempt, then Receive(), then HasFailedConnectionAttempt(),HasLostConnection()
  	sa = tcp->HasCompletedConnectionAttempt();
  	if (sa!=UNASSIGNED_SYSTEM_ADDRESS)
  	{
  		//printf("Rackspace2 TCP: Connected to %s\n", sa.ToString());
  //		serverAddress = sa;
  	}
  	for (packet = tcp->Receive(); packet; tcp->DeallocatePacket(packet), packet = tcp->Receive())
  		;
  	sa = tcp->HasFailedConnectionAttempt();
  	//if (sa!=UNASSIGNED_SYSTEM_ADDRESS)
  	//	printf("Rackspace2 TCP: Failed connection attempt to %s\n", sa.ToString());
  	sa = tcp->HasLostConnection();
  	if (sa!=UNASSIGNED_SYSTEM_ADDRESS)
  	{
  		//printf("Rackspace2 TCP: Lost connection to %s\n", sa.ToString());
  		//		serverAddress=UNASSIGNED_SYSTEM_ADDRESS;
  	}
  
  	SystemAddress hostReceived;
  	ptrdiff_t contentOffset;
  	if (httpConnection2->GetResponse(stringTransmitted, hostTransmitted, responseReceived, hostReceived, contentOffset))
  	{
  		if (responseReceived.IsEmpty()==false)
  		{
  			static FILE *fp = NULL;
  			if (fp == NULL)
  				fopen_s(&fp, "responses.txt", "wt");
  			fprintf(fp, responseReceived.C_String());
  			fprintf(fp, "\n");
  			if (contentOffset==-1)
  			{
  				if (eventCallback)
  					eventCallback->OnResponse(R2RC_NO_CONTENT, responseReceived, contentOffset);
  			}
  			else
  			{
  				json_error_t error;
  
  				json_t *root = json_loads(strstr(responseReceived.C_String() + contentOffset, "{"), JSON_REJECT_DUPLICATES | JSON_DISABLE_EOF_CHECK, &error);
  				if (!root)
  				{
  					if (eventCallback)
  						eventCallback->OnResponse(R2RC_BAD_JSON, responseReceived, contentOffset);
  				}
  				else
  				{
  					void *iter = json_object_iter(root);
  					const char *firstKey = json_object_iter_key(iter);
  					if (_stricmp(firstKey, "unauthorized")==0)
  					{
  						if (eventCallback)
  							eventCallback->OnResponse(R2RC_UNAUTHORIZED, responseReceived, contentOffset);					
  					}
  					else if (_stricmp(firstKey, "itemNotFound")==0)
  					{
  						if (eventCallback)
  							eventCallback->OnResponse(R2RC_404_NOT_FOUND, responseReceived, contentOffset);					
  					}
  					else if (_stricmp(firstKey, "access")==0)
  					{
  						json_t *valAuthToken = json_object_get(json_object_get(json_object_get(root, "access"), "token"), "id");
  						strcpy_s(X_Auth_Token, json_string_value(valAuthToken));
  
  						json_t *valAccountNumber = json_object_get(json_object_get(json_object_get(json_object_get(root, "access"), "token"), "tenant"), "id");
  						cloudAccountNumber = atoi(json_string_value(valAccountNumber));	
  
  						if (reexecuteLastRequestOnAuth)
  						{
  							reexecuteLastRequestOnAuth=false;
  
  							json_t *root2 = json_loads(__addOpLast_dataAsStr.C_String(), 0, &error);							
  							AddOperation(__addOpLast_URL, __addOpLast_isPost, root2, true);							
  						}
  						else
  						{
  							if (eventCallback)
  								eventCallback->OnResponse(R2RC_AUTHENTICATED, responseReceived, contentOffset);
  						}
  
  					}
  					else if (_stricmp(firstKey, "domains")==0)
  					{
  						if (eventCallback)
  							eventCallback->OnResponse(R2RC_GOT_DOMAINS, responseReceived, contentOffset);
  					}
  					else if (_stricmp(firstKey, "records")==0)
  					{
  						if (eventCallback)
  							eventCallback->OnResponse(R2RC_GOT_RECORDS, responseReceived, contentOffset);
  					}
  					else if (_stricmp(firstKey, "servers")==0)
  					{
  						if (eventCallback)
  							eventCallback->OnResponse(R2RC_GOT_SERVERS, responseReceived, contentOffset);
  					}
  					else if (_stricmp(firstKey, "images")==0)
  					{
  						if (eventCallback)
  							eventCallback->OnResponse(R2RC_GOT_IMAGES, responseReceived, contentOffset);
  					}
  					else if (_stricmp(firstKey, "message")==0)
  					{
  						const char *message = json_string_value(json_object_iter_value(iter));
  
  						if (strcmp(message, "Invalid authentication token. Please renew.")==0)
  						{
  							// Sets reexecuteLastRequestOnAuth to true
  							// After authenticate completes, will rerun the last run command
  							Reauthenticate();
  						}
  						else
  						{
  							if (eventCallback)
  								eventCallback->OnMessage(message, responseReceived, stringTransmitted, contentOffset);
  						}
  					}
  					else
  					{
  						if (eventCallback)
  							eventCallback->OnResponse(R2RC_UNKNOWN, responseReceived, contentOffset);
  					}
  				}
  
  				json_decref(root);
  			}
  		}
  		else
  		{
  			if (eventCallback)
  				eventCallback->OnEmptyResponse(stringTransmitted);
  		}
  	}
  }
  void Rackspace2::SetEventCallback(Rackspace2EventCallback *callback)
  {
  	eventCallback=callback;
  }
  int Rackspace2::GetCloudAccountNumber(void) const
  {
  	return cloudAccountNumber;
  }
  const char *Rackspace2::GetAuthToken(void) const
  {
  	return (const char*) &X_Auth_Token;
  }
  void Rackspace2::Reauthenticate(void)
  {
  	reexecuteLastRequestOnAuth=true;
  	AuthenticateInt(lastAuthenticationURL.C_String(), lastRackspaceCloudUsername.C_String(), lastApiAccessKey.C_String());
  }
  void Rackspace2::AuthenticateInt(const char *authenticationURL, const char *rackspaceCloudUsername, const char *apiAccessKey)
  {
  	json_t *json_credentials = json_object();
  	json_object_set(json_credentials, "username", json_string(rackspaceCloudUsername));
  	json_object_set(json_credentials, "apiKey", json_string(apiAccessKey));
  	json_t *json_auth = json_object();
  	json_object_set(json_auth, "RAX-KSKEY:apiKeyCredentials", json_credentials);
  	json_t *json_root = json_object();
  	json_object_set(json_root, "auth", json_auth);
  
  	RakString URL = authenticationURL;
  	RakString command = "/tokens";
  	URL += command;
  
  	AddOperation(URL, OT_POST, json_root, false);
  }
  void Rackspace2::Authenticate(const char *authenticationURL, const char *rackspaceCloudUsername, const char *apiAccessKey)
  {
  	lastAuthenticationURL=authenticationURL;
  	lastRackspaceCloudUsername=rackspaceCloudUsername;
  	lastApiAccessKey=apiAccessKey;
  	AuthenticateInt(authenticationURL, rackspaceCloudUsername,apiAccessKey);
  }
  void Rackspace2::AddOperation(SLNet::RakString URL, OpType opType, json_t *data, bool setAuthToken)
  {
  	if (tcp==0)
  	{
  		tcp = SLNet::OP_NEW<TCPInterface>(_FILE_AND_LINE_);
  
  		if (tcp->Start(0, 0, 8)==false)
  		{
  			if (eventCallback)
  				eventCallback->OnTCPFailure();
  		}
  
  		httpConnection2 = SLNet::OP_NEW<HTTPConnection2>(_FILE_AND_LINE_);
  
  		tcp->AttachPlugin(httpConnection2);
  	}
  
  	RakString authURLHeader, authURLDomain, authURLPath;
  	URL.SplitURI(authURLHeader,authURLDomain,authURLPath);
  	char *jsonStr = "";
  	if (data)
  		jsonStr = json_dumps(data, 0);
  	RakString requestStr;
  	RakString extraBody;
  	if (setAuthToken)
  	{
  		RakAssert(X_Auth_Token[0]);
  		// Test expired token
  		//strcpy_s(X_Auth_Token, "fd6ad67c-fbd3-4b35-94e2-059b6090998e");
  		extraBody.Set("Accept: application/json\r\nX-Auth-Token: %s", X_Auth_Token);
  
  		__addOpLast_URL = URL;
  		__addOpLast_isPost = opType;
  		__addOpLast_dataAsStr = jsonStr;
  	}
  	else
  	{
  		extraBody = "Accept: application/json";
  	}
  
  	if (opType==OT_POST)
  		requestStr = RakString::FormatForPOST(URL, "application/json", jsonStr, extraBody);
  	else if (opType==OT_GET)
  		requestStr = RakString::FormatForGET(URL, extraBody);
  	else  if (opType==OT_DELETE)
  		requestStr = RakString::FormatForDELETE(URL, extraBody);
  	else
  		requestStr = RakString::FormatForPUT(URL, "application/json", jsonStr, extraBody);
  
  	bool b = httpConnection2->TransmitRequest(requestStr,authURLDomain, 443, true);
  	if (!b)
  	{
  		if (eventCallback)
  			eventCallback->OnTransmissionFailed(httpConnection2, requestStr, authURLDomain);
  	}
  	if (data)
  	{
  		free(jsonStr);
  		json_decref(data);
  	}
  }
  
  