Files
UnrealEngineUWP/Engine/Source/Programs/Shared/EpicGames.Perforce.Native/Client.cpp
ben marsh 79a25a6f7d Add support for setting passwords on the native client at creation time.
#ROBOMERGE-AUTHOR: ben.marsh
#ROBOMERGE-SOURCE: CL 18113267 in //UE5/Main/...
#ROBOMERGE-BOT: STARSHIP (Main -> Release-Engine-Test) (v889-18060218)

[CL 18113359 by ben marsh in ue5-release-engine-test branch]
2021-11-09 16:43:52 -05:00

455 lines
10 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "clientapi.h"
#include "strtable.h"
#include <assert.h>
#ifdef _MSC_VER
#define NATIVE_API __declspec(dllexport)
#define strcasecmp _stricmp
#else
#define NATIVE_API
#endif
struct FSettings
{
const char* ServerAndPort;
const char* User;
const char* Password;
const char* Client;
const char* AppName;
const char* AppVersion;
};
struct FReadBuffer
{
unsigned char* Data;
unsigned int Length;
unsigned int Count;
unsigned int MaxLength;
unsigned int MaxCount;
};
struct FWriteBuffer
{
unsigned char* Data;
unsigned int MaxLength;
unsigned int MaxCount;
};
typedef void (FOnBufferReadyFn)(const FReadBuffer* ReadBuffer, FWriteBuffer* WriteBuffer);
class FClientUser : public ClientUser
{
private:
unsigned char* Data;
unsigned int Count;
unsigned int Length;
unsigned int MaxCount;
unsigned int MaxLength;
FOnBufferReadyFn* OnBufferReady;
public:
const char* Func = nullptr;
const char* PromptResponse = nullptr;
FClientUser(FWriteBuffer* WriteBuffer, FOnBufferReadyFn* OnBufferReady)
{
SetWriteBuffer(WriteBuffer);
this->OnBufferReady = OnBufferReady;
}
virtual void Prompt(Error* err, StrBuf& rsp, int noEcho, Error* e) override
{
rsp.Set(PromptResponse);
}
virtual void Prompt(Error* err, StrBuf& rsp, int noEcho, int noOutput, Error* e) override
{
rsp.Set(PromptResponse);
}
virtual void Prompt(const StrPtr& msg, StrBuf& rsp, int noEcho, Error* e) override
{
rsp.Set(PromptResponse);
}
virtual void Prompt(const StrPtr& msg, StrBuf& rsp, int noEcho, int noOutput, Error* e) override
{
rsp.Set(PromptResponse);
}
void SetWriteBuffer(FWriteBuffer* WriteBuffer)
{
this->Data = WriteBuffer->Data;
this->Length = 0;
this->Count = 0;
this->MaxLength = WriteBuffer->MaxLength;
this->MaxCount = (size_t)WriteBuffer->MaxCount;
}
void Flush()
{
FReadBuffer ReadBuffer;
ReadBuffer.Data = Data;
ReadBuffer.Length = Length;
ReadBuffer.Count = Count;
ReadBuffer.MaxLength = MaxLength;
ReadBuffer.MaxCount = MaxCount;
FWriteBuffer WriteBuffer;
memset(&WriteBuffer, 0, sizeof(WriteBuffer));
(*OnBufferReady)(&ReadBuffer, &WriteBuffer);
SetWriteBuffer(&WriteBuffer);
}
virtual void HandleError(Error* err) override
{
while (!TryOutputError(err))
{
Flush();
}
}
virtual void Message(Error* Err) override
{
while (!TryOutputError(Err))
{
Flush();
}
}
bool TryOutputError(Error* Err)
{
static const char CodeKey[] = "code";
static const char SeverityKey[] = "severity";
static const char GenericKey[] = "generic";
static const char DataKey[] = "data";
StrBuf Message;
Err->Fmt(&Message, 0);
unsigned int MessageLen = Message.Length();
unsigned int RecordLen;
if (Err->GetSeverity() == E_INFO)
{
static const char InfoCode[] = "info";
RecordLen = 1 + MeasureStringField(CodeKey, InfoCode) + MeasureStringField(DataKey, MessageLen) + 1;
if (Length + RecordLen > MaxLength)
{
return false;
}
unsigned char* Pos = Data + Length;
*(Pos++) = '{';
Pos = WriteStringField(Pos, CodeKey, InfoCode);
Pos = WriteStringField(Pos, DataKey, Message.Text(), MessageLen);
*(Pos++) = '0';
assert(Pos == Data + Length + RecordLen);
}
else
{
static const char ErrorCode[] = "error";
RecordLen = 1 + MeasureStringField(CodeKey, ErrorCode) + MeasureIntField(SeverityKey) + MeasureIntField(GenericKey) + MeasureStringField(DataKey, MessageLen) + 1;
if (Length + RecordLen > MaxLength)
{
return false;
}
unsigned char* Pos = Data + Length;
*(Pos++) = '{';
Pos = WriteStringField(Pos, CodeKey, ErrorCode);
Pos = WriteIntField(Pos, SeverityKey, Err->GetSeverity());
Pos = WriteIntField(Pos, GenericKey, Err->GetGeneric());
Pos = WriteStringField(Pos, DataKey, Message.Text(), MessageLen);
*(Pos++) = '0';
assert(Pos == Data + Length + RecordLen);
}
Length += RecordLen;
return true;
}
virtual void OutputError(const char* errBuf) override
{
assert(false);
}
virtual void OutputInfo(char Level, const char* Data) override
{
while (!TryOutputInfo(Level, Data))
{
Flush();
}
}
bool TryOutputInfo(char Level, const char* Info)
{
static const char CodeKey[] = "code";
static const char LevelKey[] = "level";
static const char DataKey[] = "data";
static const char InfoCode[] = "info";
unsigned int InfoLen = (unsigned int)strlen(Info);
unsigned int RecordLen = 1 + MeasureStringField(CodeKey, InfoCode) + MeasureIntField(LevelKey) + MeasureStringField(DataKey, InfoLen) + 1;
if (Length + RecordLen > MaxLength)
{
return false;
}
unsigned char* Pos = Data + Length;
*(Pos++) = '{';
Pos = WriteStringField(Pos, CodeKey, InfoCode);
Pos = WriteIntField(Pos, LevelKey, Level);
Pos = WriteStringField(Pos, DataKey, Info, InfoLen);
*(Pos++) = '0';
assert(Pos == Data + Length + RecordLen);
Length += RecordLen;
return true;
}
virtual void OutputBinary(const char* data, int length) override
{
assert(false);
}
virtual void OutputText(const char* Data, int Length) override
{
assert(false);
}
virtual void OutputStat(StrDict* VarList) override
{
while (!TryWriteRecord(VarList))
{
Flush();
}
if (++Count > MaxCount)
{
Flush();
}
}
template<int KeyLen>
static int MeasureStringField(const char(&Key)[KeyLen], int ValueLen)
{
return 10 + (KeyLen - 1) + ValueLen;
}
template<int KeyLen, int ValueLen>
static int MeasureStringField(const char(&Key)[KeyLen], const char(&Value)[ValueLen])
{
return MeasureStringField(Key, ValueLen - 1);
}
template<int KeyLen>
static int MeasureIntField(const char(&Key)[KeyLen])
{
return 10 + (KeyLen - 1);
}
template<int KeyLen>
static unsigned char* WriteStringField(unsigned char* Data, const char(&Key)[KeyLen], const char* Value, int ValueLen)
{
Data = WriteString(Data, Key);
return WriteString(Data, Value, ValueLen);
}
template<int KeyLen, int ValueLen>
static unsigned char* WriteStringField(unsigned char* Data, const char(&Key)[KeyLen], const char(&Value)[ValueLen])
{
Data = WriteString(Data, Key);
return WriteString(Data, Value);
}
template<int KeyLen>
static unsigned char* WriteIntField(unsigned char* Data, const char(&Key)[KeyLen], int Value)
{
Data = WriteString(Data, Key);
return WriteInt(Data, Value);
}
static unsigned char* WriteInt(unsigned char* Data, int Value)
{
*(Data++) = 'i';
return WriteIntValue(Data, Value);
}
static unsigned char* WriteIntValue(unsigned char* Data, int Value)
{
*(Data++) = Value & 0xff;
*(Data++) = (Value >> 8) & 0xff;
*(Data++) = (Value >> 16) & 0xff;
*(Data++) = (Value >> 24) & 0xff;
return Data;
}
static unsigned char* WriteString(unsigned char* Data, const char* Text, int Length)
{
*(Data++) = 's';
Data = WriteIntValue(Data, Length);
memcpy(Data, Text, Length);
return Data + Length;
}
template<int Length>
static unsigned char* WriteString(unsigned char* Data, const char(&Text)[Length])
{
return WriteString(Data, Text, Length - 1);
}
bool TryWriteRecord(StrDict* VarList)
{
return TryWriteRecord("stat", 4, VarList);
}
bool TryWriteRecord(const char* Code, unsigned int CodeLen, StrDict* VarList)
{
unsigned int Pos = Length;
// Record prefix: '{' (open record), string, 4 bytes, 'code', string ...
unsigned char Prefix[] = { '{', 's', 4, 0, 0, 0, 'c', 'o', 'd', 'e', 's' };
unsigned int PrefixLen = sizeof(Prefix) / sizeof(Prefix[0]);
if (Pos + PrefixLen + (unsigned int)sizeof(unsigned int) + CodeLen > MaxLength - 1)
{
return false;
}
memcpy(Data + Pos, Prefix, PrefixLen);
Pos += PrefixLen;
memcpy(Data + Pos, &CodeLen, sizeof(CodeLen));
Pos += sizeof(unsigned int);
memcpy(Data + Pos, Code, CodeLen);
Pos += CodeLen;
for (int Idx = 0;; Idx++)
{
StrRef Field;
StrRef Value;
if (VarList->GetVar(Idx, Field, Value) == 0)
{
break;
}
unsigned int FieldLength = Field.Length();
unsigned int ValueLength = Value.Length();
if (Pos + 1 + 4 + FieldLength + 1 + 4 + ValueLength > MaxLength - 1)
{
return false;
}
Data[Pos] = 's';
Pos++;
memcpy(Data + Pos, &FieldLength, sizeof(FieldLength));
Pos += sizeof(FieldLength);
memcpy(Data + Pos, Field.Text(), FieldLength);
Pos += FieldLength;
Data[Pos] = 's';
Pos++;
memcpy(Data + Pos, &ValueLength, sizeof(ValueLength));
Pos += sizeof(ValueLength);
memcpy(Data + Pos, Value.Text(), ValueLength);
Pos += ValueLength;
}
Data[Pos++] = '0';
Length = Pos;
return true;
}
};
class FClient
{
public:
ClientApi ClientApi;
FClientUser User;
FClient(FWriteBuffer* WriteBuffer, FOnBufferReadyFn* OnBufferReady)
: User(WriteBuffer, OnBufferReady)
{
}
};
extern "C" NATIVE_API FClient* Client_Create(const FSettings* Settings, FWriteBuffer* WriteBuffer, FOnBufferReadyFn* OnBufferReady)
{
FClient* Client = new FClient(WriteBuffer, OnBufferReady);
if (Settings != nullptr)
{
if (Settings->ServerAndPort != nullptr)
{
Client->ClientApi.SetPort(Settings->ServerAndPort);
}
if (Settings->User != nullptr)
{
Client->ClientApi.SetUser(Settings->User);
}
if (Settings->Password != nullptr)
{
Client->ClientApi.SetPassword(Settings->Password);
}
if (Settings->Client != nullptr)
{
Client->ClientApi.SetClient(Settings->Client);
}
if (Settings->AppName != nullptr)
{
Client->ClientApi.SetProg(Settings->AppName);
}
if (Settings->AppVersion != nullptr)
{
Client->ClientApi.SetVersion(Settings->AppVersion);
}
}
Client->ClientApi.SetProtocol("tag", "");
Error Err;
Client->ClientApi.Init(&Err);
Client->User.HandleError(&Err);
Client->User.Flush();
return Client;
}
extern "C" NATIVE_API void Client_Login(FClient * Client, const char* Password)
{
Client->User.PromptResponse = Password;
Client->ClientApi.SetArgv(0, nullptr);
Client->ClientApi.Run("login", &Client->User);
Client->User.Flush();
Client->User.PromptResponse = nullptr;
}
extern "C" NATIVE_API void Client_Command(FClient* Client, const char* Func, int ArgCount, const char** Args)
{
Client->User.Func = Func;
Client->ClientApi.SetArgv(ArgCount, (char* const*)Args);
Client->ClientApi.Run(Func, &Client->User);
Client->User.Flush();
Client->User.Func = nullptr;
}
extern "C" NATIVE_API void Client_Destroy(FClient* Client)
{
Error Err;
Client->ClientApi.Final(&Err);
Client->User.HandleError(&Err);
delete Client;
}