// Copyright 2017-2019 David Romanski (Socke). All Rights Reserved. #include "FileFunctionsSocketClient.h" UFileFunctionsSocketClient* UFileFunctionsSocketClient::fileFunctionsSocketClient; UFileFunctionsSocketClient::UFileFunctionsSocketClient(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { fileFunctionsSocketClient = this; } UFileFunctionsSocketClient* UFileFunctionsSocketClient::getFileFunctionsSocketClientTarget() { return fileFunctionsSocketClient; } FString UFileFunctionsSocketClient::getCleanDirectory(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath) { if (directoryType == EFileFunctionsSocketClientDirectoryType::E_ad) { return FPaths::ConvertRelativePathToFull(filePath); } else { FString ProjectDir = FPaths::ProjectDir(); return FPaths::ConvertRelativePathToFull(ProjectDir + filePath); } } void UFileFunctionsSocketClient::writeBytesToFile(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, TArray bytes, bool& success) { success = FFileHelper::SaveArrayToFile(bytes, *getCleanDirectory(directoryType, filePath)); } void UFileFunctionsSocketClient::addBytesToFileAndCloseIt(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, TArray bytes, bool& success) { FArchive* writer = IFileManager::Get().CreateFileWriter(*getCleanDirectory(directoryType, filePath), EFileWrite::FILEWRITE_Append); if (!writer) { success = false; return; } writer->Seek(writer->TotalSize()); writer->Serialize(bytes.GetData(), bytes.Num()); writer->Close(); delete writer; success = true; } //void UFileFunctionsSocketClient::splittFile(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, int32 parts, bool& success){ // if (parts <= 0) // parts = 1; // FArchive* reader = IFileManager::Get().CreateFileReader(*getCleanDirectory(directoryType, filePath)); // if (!reader) { // success = false; // return; // } // // int64 splittAfterBytes = reader->TotalSize()/ ((int64)parts); // TArray bytes; // // for (int32 i = 0; i < parts; i++){ // bytes.AddUninitialized(splittAfterBytes); // reader->Serialize(bytes.GetData(), splittAfterBytes); // if (FFileHelper::SaveArrayToFile(bytes, *getCleanDirectory(directoryType, filePath)) == false) { // success = false; // return; // } // splittAfterBytes = // reader->Seek(); // } // //} TArray UFileFunctionsSocketClient::readBytesFromFile(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, bool& success) { TArray result; success = FFileHelper::LoadFileToArray(result, *getCleanDirectory(directoryType, filePath)); return result; } void UFileFunctionsSocketClient::readStringFromFile(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, bool& success, FString& data) { data.Empty(); success = FFileHelper::LoadFileToString(data, *getCleanDirectory(directoryType, filePath)); } void UFileFunctionsSocketClient::writeStringToFile(EFileFunctionsSocketClientDirectoryType directoryType, FString data, FString filePath, EFileFunctionsSocketClientEncodingOptions fileEncoding, bool& success) { success = FFileHelper::SaveStringToFile(data, *getCleanDirectory(directoryType, filePath), (FFileHelper::EEncodingOptions)fileEncoding); } void UFileFunctionsSocketClient::getMD5FromFile(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, bool& success, FString& MD5) { getMD5FromFileAbsolutePath(getCleanDirectory(directoryType, filePath), success, MD5); } void UFileFunctionsSocketClient::getMD5FromFileAbsolutePath(FString filePath, bool& success, FString& MD5) { MD5.Empty(); FArchive* reader = IFileManager::Get().CreateFileReader(*filePath); if (!reader) { success = false; return; } TArray byteArrayTmp; int64 totalSize = reader->TotalSize(); int64 loadedBytes = 0; int64 leftUploadBytes = 1024; if (totalSize < leftUploadBytes) leftUploadBytes = totalSize; uint8 Digest[16]; FMD5 Md5Gen; while ((loadedBytes + leftUploadBytes) <= totalSize) { byteArrayTmp.Reset(leftUploadBytes); byteArrayTmp.AddUninitialized(leftUploadBytes); reader->Serialize(byteArrayTmp.GetData(), byteArrayTmp.Num()); loadedBytes += leftUploadBytes; reader->Seek(loadedBytes); Md5Gen.Update(byteArrayTmp.GetData(), byteArrayTmp.Num()); } leftUploadBytes = totalSize - loadedBytes; if (leftUploadBytes > 0) { byteArrayTmp.Reset(leftUploadBytes); byteArrayTmp.AddUninitialized(leftUploadBytes); reader->Serialize(byteArrayTmp.GetData(), byteArrayTmp.Num()); loadedBytes += leftUploadBytes; Md5Gen.Update(byteArrayTmp.GetData(), byteArrayTmp.Num()); } if (reader != nullptr) { reader->Close(); delete reader; } if (totalSize != loadedBytes) { success = false; return; } Md5Gen.Final(Digest); for (int32 i = 0; i < 16; i++) { MD5 += FString::Printf(TEXT("%02x"), Digest[i]); } success = true; } void UFileFunctionsSocketClient::stringToBase64String(FString string, FString& base64String) { base64String.Empty(); FTCHARToUTF8 Convert(*string); TArray bytes; bytes.Append(((uint8*)((ANSICHAR*)Convert.Get())), Convert.Length()); base64String = FBase64::Encode(bytes); } void UFileFunctionsSocketClient::base64StringToString(FString& string, FString base64String) { string.Empty(); TArray bytes; if (FBase64::Decode(*base64String, bytes)) { bytes.Add(0x00);// null-terminator char* Data = (char*)bytes.GetData(); string = FString(UTF8_TO_TCHAR(Data)); } } void UFileFunctionsSocketClient::bytesToBase64String(TArray bytes, FString& base64String) { base64String.Empty(); base64String = FBase64::Encode(bytes); } TArray UFileFunctionsSocketClient::base64StringToBytes(FString base64String, bool& success) { TArray fileData; if (base64String.Len() % 2 != 0 || base64String.Len() < 4) { success = false; return fileData; } success = FBase64::Decode(*base64String, fileData); return fileData; } void UFileFunctionsSocketClient::fileToBase64String(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, bool& success, FString& base64String, FString& fileName) { base64String.Empty(); fileName.Empty(); TArray fileData; if (!FFileHelper::LoadFileToArray(fileData, *getCleanDirectory(directoryType, filePath))) { success = false; return; } base64String = FBase64::Encode(fileData); success = true; } bool UFileFunctionsSocketClient::fileExists(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath) { return FPaths::FileExists(*getCleanDirectory(directoryType, filePath)); } bool UFileFunctionsSocketClient::fileExistsAbsolutePath(FString filePath) { return FPaths::FileExists(*filePath); } bool UFileFunctionsSocketClient::directoryExists(EFileFunctionsSocketClientDirectoryType directoryType, FString path) { return FPaths::DirectoryExists(*getCleanDirectory(directoryType, path)); } int64 UFileFunctionsSocketClient::fileSize(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath) { return FPlatformFileManager::Get().GetPlatformFile().FileSize(*getCleanDirectory(directoryType, filePath)); } int64 UFileFunctionsSocketClient::fileSizeAbsolutePath(FString filePath) { return FPlatformFileManager::Get().GetPlatformFile().FileSize(*filePath); } bool UFileFunctionsSocketClient::deleteFile(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath) { return FPlatformFileManager::Get().GetPlatformFile().DeleteFile(*getCleanDirectory(directoryType, filePath)); } bool UFileFunctionsSocketClient::deleteFileAbsolutePath(FString filePath) { return FPlatformFileManager::Get().GetPlatformFile().DeleteFile(*filePath); } bool UFileFunctionsSocketClient::deleteDirectory(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath) { return FPlatformFileManager::Get().GetPlatformFile().DeleteDirectory(*getCleanDirectory(directoryType, filePath)); } bool UFileFunctionsSocketClient::isReadOnly(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath) { return FPlatformFileManager::Get().GetPlatformFile().IsReadOnly(*getCleanDirectory(directoryType, filePath)); } bool UFileFunctionsSocketClient::moveFile(EFileFunctionsSocketClientDirectoryType directoryTypeTo, FString filePathTo, EFileFunctionsSocketClientDirectoryType directoryTypeFrom, FString filePathFrom) { return FPlatformFileManager::Get().GetPlatformFile().MoveFile(*getCleanDirectory(directoryTypeTo, filePathTo), *getCleanDirectory(directoryTypeFrom, filePathFrom)); } bool UFileFunctionsSocketClient::setReadOnly(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, bool bNewReadOnlyValue) { return FPlatformFileManager::Get().GetPlatformFile().SetReadOnly(*getCleanDirectory(directoryType, filePath), bNewReadOnlyValue); } FDateTime UFileFunctionsSocketClient::getTimeStamp(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath) { return FPlatformFileManager::Get().GetPlatformFile().GetTimeStamp(*getCleanDirectory(directoryType, filePath)); } void UFileFunctionsSocketClient::setTimeStamp(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, FDateTime DateTime) { FPlatformFileManager::Get().GetPlatformFile().SetTimeStamp(*getCleanDirectory(directoryType, filePath), DateTime); } FDateTime UFileFunctionsSocketClient::getAccessTimeStamp(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath) { return FPlatformFileManager::Get().GetPlatformFile().GetAccessTimeStamp(*getCleanDirectory(directoryType, filePath)); } FString UFileFunctionsSocketClient::getFilenameOnDisk(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath) { return FPlatformFileManager::Get().GetPlatformFile().GetFilenameOnDisk(*getCleanDirectory(directoryType, filePath)); } bool UFileFunctionsSocketClient::createDirectory(EFileFunctionsSocketClientDirectoryType directoryType, FString path) { return FPlatformFileManager::Get().GetPlatformFile().CreateDirectory(*getCleanDirectory(directoryType, path)); } void UFileFunctionsSocketClient::getAllFilesFromDirectory(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, int32& count, TArray& files, TArray& filePaths, FString fileType) { if (filePath.Len() < 1) { return; } files.Empty(); filePaths.Empty(); FString dir = getCleanDirectory(directoryType, filePath); FPaths::NormalizeDirectoryName(filePath); if (!FPaths::DirectoryExists(dir)) { return; } IFileManager& FileManager = IFileManager::Get(); dir += "/" + fileType; FileManager.FindFiles(files, *dir, true, false); filePath += "/"; for (int32 i = 0; i < files.Num(); i++) { filePaths.Add((filePath + files[i])); //UE_LOG(LogTemp, Display, TEXT("->%s"), *files[i]); } count = files.Num(); } FFileFunctionsSocketClientOpenFile UFileFunctionsSocketClient::openFile(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath){ FArchive* writer = IFileManager::Get().CreateFileWriter(*getCleanDirectory(directoryType, filePath), EFileWrite::FILEWRITE_Append); FFileFunctionsSocketClientOpenFile file; file.writer = writer; return file; } int64 UFileFunctionsSocketClient::addBytesToFile(FFileFunctionsSocketClientOpenFile openFile, TArray bytes){ //UE_LOG(LogTemp, Warning, TEXT("xxxxx WRITE: %i"), bytes.Num()); if (openFile.writer != nullptr) { openFile.writer->Seek(openFile.writer->TotalSize()); openFile.writer->Serialize(bytes.GetData(), bytes.Num()); //openFile.writer->Flush(); return openFile.writer->TotalSize(); } return 0; } void UFileFunctionsSocketClient::closeFile(FFileFunctionsSocketClientOpenFile openFile){ if (openFile.writer != nullptr) { openFile.writer->Close(); openFile.writer = nullptr; } } bool UFileFunctionsSocketClient::encryptFileWithAES(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, FString newFileName, FString key, bool writeEncryptedFileSizeToFile){ if (filePath.IsEmpty() || newFileName.IsEmpty() || key.Len() != 32) { UE_LOG(LogTemp, Error, TEXT("encryptFileWithAES: FilePath or newFileName empty or wrong key length. The key must consist of 32 ANSI compatible characters.")); return false; } FString dir = getCleanDirectory(directoryType, filePath); FString fileName = FPaths::GetCleanFilename(dir); int64 fileSize = FPlatformFileManager::Get().GetPlatformFile().FileSize(*dir); TArray64 data; if (!FFileHelper::LoadFileToArray(data, *dir)) { UE_LOG(LogTemp, Error, TEXT("encryptFileWithAES: File could not be loaded: %s"),*dir); return false; } const int64 encryptedFileSize = Align(data.Num(), FAES::AESBlockSize); if (data.Num() < encryptedFileSize) { data.AddUninitialized(encryptedFileSize - data.Num()); } FAES::EncryptData(data.GetData(), encryptedFileSize, TCHAR_TO_ANSI(*key)); dir = dir.Replace(*fileName, *newFileName); if (FPaths::FileExists(*dir)) { FPlatformFileManager::Get().GetPlatformFile().DeleteFile(*dir); } FArchive* writer = IFileManager::Get().CreateFileWriter(*dir, EFileWrite::FILEWRITE_Append); if (!writer) { UE_LOG(LogTemp, Error, TEXT("encryptFileWithAES: File could not be saved: %s"), *dir); data.Empty(); return false; } if (writeEncryptedFileSizeToFile) { TArray byteArray; union { int64 tmpVal; uint8 tmpArray[8]; } u; u.tmpVal = fileSize; byteArray.AddZeroed(8); FMemory::Memcpy(byteArray.GetData(), u.tmpArray, 8); writer->Serialize(byteArray.GetData(), byteArray.Num()); byteArray.Empty(); } writer->Serialize(data.GetData(), data.Num()); writer->Close(); delete writer; data.Empty(); return true; } bool UFileFunctionsSocketClient::decryptFileWithAES(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, FString newFileName, FString key, bool hasEncryptedFileSizeInFile, int64 originalFileLength){ if (filePath.IsEmpty() || newFileName.IsEmpty() || key.Len() != 32) { UE_LOG(LogTemp, Error, TEXT("decryptFileWithAES: FilePath or newFileName empty or wrong key length. The key must consist of 32 ANSI compatible characters.")); return false; } FString dir = getCleanDirectory(directoryType, filePath); FString fileName = FPaths::GetCleanFilename(dir); int64 fileSize = FPlatformFileManager::Get().GetPlatformFile().FileSize(*dir); FArchive* reader = IFileManager::Get().CreateFileReader(*dir); if (!reader) { UE_LOG(LogTemp, Error, TEXT("decryptFileWithAES: File could not be loaded: %s"), *dir); return false; } if (hasEncryptedFileSizeInFile) { TArray bytes; bytes.AddZeroed(8); reader->Serialize(bytes.GetData(), bytes.Num()); FMemory::Memcpy(&originalFileLength, bytes.GetData(), 8); bytes.Empty(); fileSize = fileSize-8; } if (fileSize < originalFileLength) { originalFileLength = fileSize; UE_LOG(LogTemp, Warning, TEXT("decryptFileWithAES: The encrypted file size is smaller than the original file size. File may be corrupted: %s"), *dir); return false; } TArray64 data; data.AddUninitialized(fileSize); reader->Serialize(data.GetData(), data.Num()); reader->Close(); delete reader; const int64 encryptedFileSize = Align(data.Num(), FAES::AESBlockSize); if (data.Num() < encryptedFileSize) { UE_LOG(LogTemp, Error, TEXT("decryptFileWithAES: Wrong data length. File could not be decrypted: %s"),*dir); return false; } FAES::DecryptData(data.GetData(), encryptedFileSize, TCHAR_TO_ANSI(*key)); dir = dir.Replace(*fileName, *newFileName); if (FPaths::FileExists(*dir)) { FPlatformFileManager::Get().GetPlatformFile().DeleteFile(*dir); } FArchive* writer = IFileManager::Get().CreateFileWriter(*dir, EFileWrite::FILEWRITE_Append); if (!writer) { UE_LOG(LogTemp, Error, TEXT("encryptFileWithAES: File could not be saved: %s"), *dir); data.Empty(); return false; } writer->Serialize(data.GetData(), originalFileLength); writer->Close(); delete writer; data.Empty(); return true; } FString UFileFunctionsSocketClient::encryptMessageWithAES(FString message, FString key) { if (message.IsEmpty() || key.Len() != 32) { UE_LOG(LogTemp, Error, TEXT("encryptMessageWithAES:Message empty or wrong key length. The key must consist of 32 ANSI compatible characters.")); return FString(); } TArray data = FStringToByteArray(message); data.Add(0x00);// null-terminator const int64 encryptedFileSize = Align(data.Num(), FAES::AESBlockSize); if (data.Num() < encryptedFileSize){ data.AddUninitialized(encryptedFileSize-data.Num()); } FAES::EncryptData(data.GetData(), encryptedFileSize, TCHAR_TO_ANSI(*key)); FString encryptedBase64String = FString(); bytesToBase64String(data, encryptedBase64String); data.Empty(); return encryptedBase64String; } FString UFileFunctionsSocketClient::decryptMessageWithAES(FString message, FString key) { if (message.IsEmpty() || key.Len() != 32) { UE_LOG(LogTemp, Error, TEXT("decryptMessageFromAES: Message empty or wrong key length. The key must consist of 32 ANSI compatible characters.")); return FString(); } bool success = false; TArray data = base64StringToBytes(message, success); const int64 encryptedFileSize = Align(data.Num(), FAES::AESBlockSize); if (data.Num() < encryptedFileSize) { UE_LOG(LogTemp, Error, TEXT("decryptMessageFromAES: Wrong string length. Message could not be decrypted.")); return FString(); } FAES::DecryptData(data.GetData(), encryptedFileSize, TCHAR_TO_ANSI(*key)); FString s = FString(UTF8_TO_TCHAR((char*)data.GetData())); data.Empty(); return s; } TArray UFileFunctionsSocketClient::FStringToByteArray(FString s) { FTCHARToUTF8 Convert(*s); TArray data; data.Append((uint8*)Convert.Get(), Convert.Length()); return data; } FString UFileFunctionsSocketClient::int64ToString(int64 Num) { FString str = FString(); const TCHAR* DigitToChar = TEXT("9876543210123456789"); constexpr int64 ZeroDigitIndex = 9; bool bIsNumberNegative = Num < 0; const int64 TempBufferSize = 32; // 32 is big enough TCHAR TempNum[TempBufferSize]; int64 TempAt = TempBufferSize; // fill the temp string from the top down. // Convert to string assuming base ten. do { TempNum[--TempAt] = DigitToChar[ZeroDigitIndex + (Num % 10)]; Num /= 10; } while (Num); if (bIsNumberNegative) { TempNum[--TempAt] = TEXT('-'); } const TCHAR* CharPtr = TempNum + TempAt; const int64 NumChars = TempBufferSize - TempAt; str.Append(CharPtr, NumChars); return str; } void UFileFunctionsSocketClient::readBytesFromFileInPartsAsync(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, int32 bufferSize, float delayBetweenReadsInSeconds) { UFileFunctionsSocketClient::getFileFunctionsSocketClientTarget()->readBytesFromFileInPartsAsyncInternal(directoryType, filePath, bufferSize, delayBetweenReadsInSeconds); } void UFileFunctionsSocketClient::readBytesFromFileInPartsAsyncInternal(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, int32 bufferSize, float delayBetweenReadsInSeconds) { FString dir = UFileFunctionsSocketClient::getCleanDirectory(directoryType, filePath); if (readFileInPartsThreads.Find(*dir) != nullptr) { UE_LOG(LogTemp, Warning, TEXT("ReadBytesFromFileInPartsAsync: %s is being read already. Operation canceled."), *dir); return; } FReadFileInPartsSocketClientThread* readThread = new FReadFileInPartsSocketClientThread(dir, bufferSize, delayBetweenReadsInSeconds); readFileInPartsThreads.Add(dir, readThread); } void UFileFunctionsSocketClient::cancelReadBytesFromFileInParts(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath) { UFileFunctionsSocketClient::getFileFunctionsSocketClientTarget()->cancelReadBytesFromFileInPartsInternal(directoryType, filePath); } void UFileFunctionsSocketClient::cancelReadBytesFromFileInPartsInternal(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath) { FString dir = UFileFunctionsSocketClient::getCleanDirectory(directoryType, filePath); if (readFileInPartsThreads.Find(*dir) != nullptr) { (*readFileInPartsThreads.Find(*dir))->stopThread(); } } void UFileFunctionsSocketClient::cleanReadBytesFromFileInParts(FString cleanDir) { if (readFileInPartsThreads.Find(*cleanDir) != nullptr) { readFileInPartsThreads.Remove(*cleanDir); } } //void UFileFunctionsSocketClient::changeDelayInBytesFromFileInParts(EFileFunctionsSocketClientDirectoryType directoryType, FString filePath, float delayBetweenReadsInSeconds) { // FString dir = UFileFunctionsSocketClient::getCleanDirectory(directoryType, filePath); // // if (readFileInPartsThreads.Find(*dir) != nullptr) { // (*readFileInPartsThreads.Find(*dir))->setDelayBetweenReadsInSeconds(delayBetweenReadsInSeconds); // } //}