diff --git a/.gitignore b/.gitignore index b9401df..d1202c0 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,7 @@ CMakeSettings.json /testing /LIBS -/debug.log \ No newline at end of file +/debug.log +/test.tar +/test.zip +/testtar diff --git a/CMakeLists.txt b/CMakeLists.txt index 3425119..0865d1b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,6 @@ if(ZLIB_FOUND) message (STATUS "ZLib found, version ${ZLIB_VERSION_STRING}") endif() -include_directories(${ZLIB_INCLUDE_DIRS}) find_package(CURL REQUIRED) @@ -26,18 +25,10 @@ if(CURL_FOUND) message (STATUS "Curl found, version ${CURL_VERSION_STRING}") endif() -include_directories(${CURL_INCLUDE_DIRS}) - -#find_package(LibArchive REQUIRED) - -#if(LibArchive_FOUND) -# message (STATUS "LibArchive found, version ${LibArchive_VERSION}") -#endif() - -#include_directories(${LibArchive_INCLUDE_DIRS}) - find_package(LibZip REQUIRED) +include_directories(${ZLIB_INCLUDE_DIRS}) +include_directories(${CURL_INCLUDE_DIRS}) include_directories(${LIBZIP_INCLUDE_DIRS}) if(LIBZIP_FOUND) @@ -51,13 +42,16 @@ include_directories ("${PROJECT_SOURCE_DIR}/include") set (SOURCE_FILES src/unlocker.cpp / src/versionparser.cpp / src/buildsparser.cpp / - src/archiveutils.cpp / - src/netutils.cpp / - src/debugutils.cpp / - src/installinfoutils.cpp / - src/servicestoputils.cpp / - src/patchutils.cpp / - src/tar.cpp ) + src/archive.cpp / + src/network.cpp / + src/debug.cpp / + src/installinfo.cpp / + src/winservices.cpp / + src/patcher.cpp / + src/tar.cpp / + src/main.cpp / + src/ziparchive.cpp / + src/toolsdownloader.cpp) IF (MSVC) IF (UNLOCKER_STATIC_LIBS_WIN) @@ -68,13 +62,15 @@ IF (MSVC) ENDIF() ENDIF (MSVC) -add_executable(Unlocker ${SOURCE_FILES} ) +add_executable(Unlocker ${SOURCE_FILES}) + +# Support skipping file offsets when tar contains files larger than ~2 Gig +target_compile_definitions(Unlocker PUBLIC _FILE_OFFSET_BITS=64) set_target_properties(Unlocker PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS ON) target_link_libraries (Unlocker ${ZLIB_LIBRARIES}) target_link_libraries (Unlocker ${CURL_LIBRARIES}) -#target_link_libraries (Unlocker ${LibArchive_LIBRARIES}) target_link_libraries (Unlocker ${LIBZIP_LIBRARY}) if(WIN32) @@ -95,14 +91,96 @@ IF (MSVC) ENDIF() ENDIF(MSVC) +# Main test + set (TEST_SOURCES tests/test_patch.cpp / - src/debugutils.cpp / - src/patchutils.cpp / - src/tar.cpp ) + src/debug.cpp / + src/patcher.cpp / + src/network.cpp / + src/versionparser.cpp / + src/buildsparser.cpp / + src/archive.cpp / + src/installinfo.cpp / + src/winservices.cpp / + src/tar.cpp / + src/unlocker.cpp / + src/ziparchive.cpp / + src/toolsdownloader.cpp) + +add_executable( TestPatch ${TEST_SOURCES} "include/unlocker.h") -add_executable( TestPatch ${TEST_SOURCES} ) +target_compile_definitions(TestPatch PUBLIC _FILE_OFFSET_BITS=64) + +include_directories(TestPatch ${ZLIB_INCLUDE_DIRS}) +include_directories(TestPatch ${CURL_INCLUDE_DIRS}) +include_directories(TestPatch ${LIBZIP_INCLUDE_DIRS}) +target_link_libraries (TestPatch ${ZLIB_LIBRARIES}) +target_link_libraries (TestPatch ${CURL_LIBRARIES}) +target_link_libraries (TestPatch ${LIBZIP_LIBRARY}) + +if(WIN32) + target_link_libraries (TestPatch ws2_32 Wldap32) +endif() set_target_properties( TestPatch PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS ON) enable_testing() -add_test(NAME TestPatchTest COMMAND TestPatch "${PROJECT_SOURCE_DIR}") \ No newline at end of file +add_test(NAME TestPatchTest COMMAND TestPatch "${PROJECT_SOURCE_DIR}") + +# Tar test + +set (TESTTAR_SOURCES tests/test_tar.cpp / + src/debug.cpp / + src/patcher.cpp / + src/versionparser.cpp / + src/buildsparser.cpp / + src/archive.cpp / + src/installinfo.cpp / + src/winservices.cpp / + src/tar.cpp / + src/ziparchive.cpp / + src/toolsdownloader.cpp) + +add_executable( TestTar ${TESTTAR_SOURCES} "include/unlocker.h") + +target_compile_definitions(TestTar PUBLIC _FILE_OFFSET_BITS=64) + +include_directories(TestTar ${ZLIB_INCLUDE_DIRS}) +include_directories(TestTar ${CURL_INCLUDE_DIRS}) +include_directories(TestTar ${LIBZIP_INCLUDE_DIRS}) +target_link_libraries (TestTar ${ZLIB_LIBRARIES}) +target_link_libraries (TestTar ${LIBZIP_LIBRARY}) + +set_target_properties( TestTar PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS ON) + +enable_testing() +add_test(NAME TestTarTest COMMAND TestTar "${PROJECT_SOURCE_DIR}") + +# Zip test + +set (TESTZIP_SOURCES tests/test_zip.cpp / + src/debug.cpp / + src/patcher.cpp / + src/versionparser.cpp / + src/buildsparser.cpp / + src/archive.cpp / + src/installinfo.cpp / + src/winservices.cpp / + src/tar.cpp / + src/ziparchive.cpp / + src/toolsdownloader.cpp) + +add_executable( TestZip ${TESTZIP_SOURCES} "include/unlocker.h") + +target_compile_definitions(TestZip PUBLIC _FILE_OFFSET_BITS=64) + +include_directories(TestZip ${ZLIB_INCLUDE_DIRS}) +include_directories(TestZip ${CURL_INCLUDE_DIRS}) +include_directories(TestZip ${LIBZIP_INCLUDE_DIRS}) +target_link_libraries (TestZip ${ZLIB_LIBRARIES}) +target_link_libraries (TestZip ${LIBZIP_LIBRARY}) + +set_target_properties( TestZip PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS ON) + +enable_testing() +add_test(NAME TestZipTest COMMAND TestZip "${PROJECT_SOURCE_DIR}") \ No newline at end of file diff --git a/include/archive.h b/include/archive.h new file mode 100644 index 0000000..cdd779b --- /dev/null +++ b/include/archive.h @@ -0,0 +1,22 @@ +#ifndef ARCHIVEUTILS_H +#define ARCHIVEUTILS_H + +#include "filesystem.hpp" +#ifdef __linux__ +#include +#endif + +#include "ziparchive.h" +#include "tar.h" +#include "debug.h" +#include + +class Archive +{ +public: + static bool extractZip(fs::path from, std::string filename, fs::path to); + static bool extractTar(fs::path from, std::string filename, fs::path to); + static void Archive::extractionProgress(float progress); +}; + +#endif // ARCHIVEUTILS_H diff --git a/include/archiveutils.h b/include/archiveutils.h deleted file mode 100644 index b97fd38..0000000 --- a/include/archiveutils.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef ARCHIVEUTILS_H -#define ARCHIVEUTILS_H - -#include "filesystem.hpp" -/* -#include -#include -*/ -#ifdef __linux__ -#include -#endif - -#define LIBZIP_STATIC - -#include "zipconf.h" -#include "zip.h" -#include "tar.h" -#include "debugutils.h" - -#define AR_BUFFER_SIZE 4096 - -namespace Archive -{ - /*int copy_data(struct archive* ar, struct archive* aw); - bool extract(const char* from, const char* filename, const char* to); - bool extract_s(fs::path from, std::string filename, fs::path to);*/ - bool extract_zip(fs::path from, std::string filename, fs::path to); - bool extract_tar(fs::path from, std::string filename, fs::path to); -} - -#endif // ARCHIVEUTILS_H diff --git a/include/config.h b/include/config.h index 409a770..e79e728 100644 --- a/include/config.h +++ b/include/config.h @@ -7,7 +7,7 @@ */ // Program options -#define PROG_VERSION "v1.1.3" +#define PROG_VERSION "v1.1.4" // Install - Default option #define INSTALL_OPTION "--install" @@ -34,7 +34,7 @@ #define FUSION_DEF_PRE15_TOOLS_NAME "com.vmware.fusion.tools.darwinPre15.zip.tar" #define FUSION_DEF_PRE15_TOOLS_ZIP "com.vmware.fusion.tools.darwinPre15.zip" -#define FUSION_DEF_CORE_LOC "/core/com.vmware.fusion.zip.tar" +#define FUSION_DEF_CORE_LOC "/x86/core/com.vmware.fusion.zip.tar" #define FUSION_DEF_CORE_NAME "com.vmware.fusion.zip.tar" #define FUSION_DEF_CORE_NAME_ZIP "com.vmware.fusion.zip" diff --git a/include/debugutils.h b/include/debug.h similarity index 100% rename from include/debugutils.h rename to include/debug.h diff --git a/include/installinfoutils.h b/include/installinfo.h similarity index 58% rename from include/installinfoutils.h rename to include/installinfo.h index bafa5c6..2f85ad5 100644 --- a/include/installinfoutils.h +++ b/include/installinfo.h @@ -2,9 +2,17 @@ #define INSTALLINFOUTILS_H #include +#include #include "config.h" +class VMWareInfoException : public std::runtime_error +{ +public: + VMWareInfoException(const char* message) : std::runtime_error(message) {} + VMWareInfoException(const std::string& message) : std::runtime_error(message) {} +}; + class VMWareInfoRetriever { public: diff --git a/include/netutils.h b/include/netutils.h deleted file mode 100644 index b2595b3..0000000 --- a/include/netutils.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef NETUTILS_H -#define NETUTILS_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#define DEBUG false - -namespace Curl -{ - size_t write_data_file(char* ptr, size_t size, size_t nmemb, void* stream); - int progress_callback(void* clientp, double dltotal, double dlnow, double ultotal, double ulnow); - CURLcode curlDownload(std::string url, std::string fileName); - CURLcode curlGet(std::string url, std::string& output); -} - -#endif // NETUTILS_H diff --git a/include/network.h b/include/network.h new file mode 100644 index 0000000..ab01e27 --- /dev/null +++ b/include/network.h @@ -0,0 +1,47 @@ +#ifndef NETUTILS_H +#define NETUTILS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CURL_DEBUG false + +class NetworkException : public std::runtime_error +{ +public: + NetworkException(const char* message, CURLcode code) : std::runtime_error(message), code(code) {} + NetworkException(const std::string& message, CURLcode code) : std::runtime_error(message), code(code) {} + CURLcode getCode() const { return code; } +private: + CURLcode code; +}; + +struct NetworkProgress { + double mBytesDownloadedLastTime = 0.0; + long long lastProgressUpdateTime = 0; +}; + +class Network +{ +public: + Network(); + ~Network(); + void curlDownload(const std::string& url, const std::string& fileName); + std::string curlGet(const std::string& url); +private: + static constexpr double mBytesProgressUpdateDelta = 0.1; // 0.1 MB + static constexpr long long updatePeriodMs = 200; // update every 100ms + + NetworkProgress networkProgress = {}; + static size_t write_data_file(char* ptr, size_t size, size_t nmemb, void* stream); + static int progress_callback(void* clientp, double dltotal, double dlnow, double ultotal, double ulnow); +}; + +#endif // NETUTILS_H diff --git a/include/patcher.h b/include/patcher.h new file mode 100644 index 0000000..2907ae0 --- /dev/null +++ b/include/patcher.h @@ -0,0 +1,91 @@ +#ifndef PATCHUTILS_H +#define PATCHUTILS_H + +#include "filesystem.hpp" +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "config.h" + +class PatchException : std::exception +{ +public: + PatchException(const char* message, ...) + { + va_list args; + va_start(args, message); + char* buf = new char[1024]; + vsprintf(buf, message, args); + va_end(args); + msg = buf; + } + ~PatchException() + { + delete[] msg; + } + const char* what() const noexcept { return msg; } +private: + const char* msg; +}; + +struct LLQQQQLLQQ // couldn't find a better name LOL +{ + unsigned long l1; + unsigned long l2; + unsigned long long q1; + unsigned long long q2; + unsigned long long q3; + unsigned long long q4; + unsigned long l3; + unsigned long l4; + unsigned long long q5; + unsigned long long q6; +}; + +struct QQq // it's starting to feel awkward +{ + unsigned long long Q1, Q2; + long long q3; +}; + +struct smc_key_struct +{ + char s0[4]; + unsigned char B1; + char s2[4]; + unsigned char B3; + unsigned char padding[6]; + unsigned long long Q4; +}; + +class Patcher { +public: + // Utils + static std::string rot13(const std::string& in); + static std::string hexRepresentation(long long bin); + static std::vector makeVector(const char* arr, size_t size); + static std::string hexRepresentation(std::vector bin); + static std::string hexRepresentation(std::string bin); + static std::optional searchForOffset(const std::vector& memstream, const std::vector& sequence, unsigned long long from = 0); + static std::optional searchForLastOffset(const std::vector& memstream, const std::vector& sequence); + static std::optional searchForOffset(std::fstream& stream, const std::vector& sequence, unsigned long long from = 0); + static std::optional searchForLastOffset(std::fstream& stream, const std::vector& sequence); + static std::vector readFile(std::fstream& stream); + static bool cmpcarr(const char* c1, const char* c2, size_t len); + static void printkey(int i, unsigned long long offset, const smc_key_struct& smc_key, const std::vector& smc_data); + + // Core functions + static void patchSMC(fs::path name, bool isSharedObj); + static std::pair patchKeys(std::fstream& file, long long key); + static void patchBase(fs::path name); + static void patchVmkctl(fs::path name); + static void patchElf(std::fstream& file, long long oldoffset, long long newoffset); +}; + +#endif // PATCHUTILS_H diff --git a/include/patchutils.h b/include/patchutils.h deleted file mode 100644 index 0dd3573..0000000 --- a/include/patchutils.h +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef PATCHUTILS_H -#define PATCHUTILS_H - -#include "filesystem.hpp" -#include -#include -#include -#include -#include -#include -#include - -#include "debugutils.h" -#include "config.h" - -namespace Patcher -{ - class PatchException : std::exception - { - public: - PatchException(const char* message, ...) - { - va_list args; - va_start(args, message); - char* buf = new char[1024]; - vsprintf(buf, message, args); - va_end(args); - msg = buf; - } - ~PatchException() - { - delete[] msg; - } - const char* what() const noexcept { return msg; } - private: - const char* msg; - }; - - struct LLQQQQLLQQ // couldn't find a better name LOL - { - unsigned long l1; - unsigned long l2; - unsigned long long q1; - unsigned long long q2; - unsigned long long q3; - unsigned long long q4; - unsigned long l3; - unsigned long l4; - unsigned long long q5; - unsigned long long q6; - }; - - struct QQq // it's starting to feel awkward - { - unsigned long long Q1, Q2; - long long q3; - }; - - struct smc_key_struct - { - char s0[4]; - unsigned char B1; - char s2[4]; - unsigned char B3; - unsigned char padding[6]; - unsigned long long Q4; - }; - - // Utils - std::string rot13(const std::string& in); - std::vector makeVector(const char* arr, size_t size); - std::string hexRepresentation(long long bin); - std::string hexRepresentation(std::vector bin); - std::string hexRepresentation(std::string bin); - std::optional searchForOffset(const std::vector& memstream, const std::vector& sequence, unsigned long long from = 0); - std::optional searchForLastOffset(const std::vector& memstream, const std::vector& sequence); - std::optional searchForOffset(std::fstream& stream, const std::vector& sequence, unsigned long long from = 0); - std::optional searchForLastOffset(std::fstream& stream, const std::vector& sequence); - std::vector readFile(std::fstream& stream); - bool cmpcarr(const char* c1, const char* c2, size_t len); - void printkey(int i, unsigned long long offset, const smc_key_struct& smc_key, const std::vector& smc_data); - - // Core functions - void patchSMC(fs::path name, bool isSharedObj); - std::pair patchKeys(std::fstream& file, long long key); - void patchBase(fs::path name); - void patchVmkctl(fs::path name); - void patchElf(std::fstream& file, long long oldoffset, long long newoffset); -} - -#endif // PATCHUTILS_H diff --git a/include/tar.h b/include/tar.h index d7660ce..f0afd27 100644 --- a/include/tar.h +++ b/include/tar.h @@ -3,10 +3,20 @@ #include #include +#include #include #include #include +#include +#include +#ifdef WIN32 +#define fseek_e64(f, p, t) _fseeki64(f, p, t); +#else +#define fseek_e64(f, p, t) fseeko(f, p, t); +#endif + +constexpr int TAR_BLOCK_SZ = 512; enum tar_type_flag { REGTYPE = '0', /* regular file */ @@ -44,6 +54,12 @@ struct tar_posix_header /* 512 */ }; +class TarException : public std::runtime_error { +public: + TarException(const char* message) : std::runtime_error(message) {} + TarException(const std::string& message) : std::runtime_error(message) {} +}; + class Tar { public: @@ -54,7 +70,7 @@ class Tar int mode; int ownerId; int groupId; - int size; + size_t size; int lastModified; tar_type_flag typeflag; std::string linkedName; @@ -63,17 +79,20 @@ class Tar Tar(std::string filename); ~Tar(); - void extract(const File& file, std::string to); - bool extract(std::string fileName, std::string to); + void extract(const File& file, std::string to, void(*progressCallback)(float) = nullptr); + bool extract(std::string fileName, std::string to, void(*progressCallback)(float) = nullptr); const std::vector& getFileList() const; + bool contains(std::string fileName) const; + std::vector search(const std::string& term); private: + void(*progressCallback)(float) = nullptr; FILE* tarfile = NULL; std::vector fileList; - int oct_to_int(char* oct); - int oct_to_sz(char* oct); + int octToInt(char* oct); + size_t octToSz(char* oct); - static constexpr int EXTRACT_BUFFER_SZ = 1024 * 16; + static constexpr size_t EXTRACT_BUFFER_SZ = 1024 * 16; }; #endif // TAR_H \ No newline at end of file diff --git a/include/toolsdownloader.h b/include/toolsdownloader.h new file mode 100644 index 0000000..027e86c --- /dev/null +++ b/include/toolsdownloader.h @@ -0,0 +1,31 @@ +#ifndef TOOLSDOWNLOADER_H +#define TOOLSDOWNLOADER_H + +#include +#include "filesystem.hpp" +#include "network.h" +#include "archive.h" +#include "buildsparser.h" +#include "debug.h" + +class ToolsDownloaderException : public std::runtime_error +{ +public: + ToolsDownloaderException(const char* message) : std::runtime_error(message) {} + ToolsDownloaderException(const std::string& message) : std::runtime_error(message) {} +}; + +class ToolsDownloader +{ +public: + ToolsDownloader(Network& network, const std::string& baseUrl, const std::string& version); + bool download(const fs::path& to); +private: + Network& network; + std::string baseUrl, versionNumber, versionUrl, buildurl; + + bool downloadStandalone(const fs::path& to); + bool downloadFromCore(const fs::path& to); +}; + +#endif // TOOLSDOWNLOADER_H \ No newline at end of file diff --git a/include/unlocker.h b/include/unlocker.h new file mode 100644 index 0000000..5d0839e --- /dev/null +++ b/include/unlocker.h @@ -0,0 +1,44 @@ +#ifndef UNLOCKER_H +#define UNLOCKER_H + +#include +#include "filesystem.hpp" +#include + +#include + +#include "config.h" +#include "network.h" +#include "versionparser.h" +#include "debug.h" +#include "toolsdownloader.h" +#include "installinfo.h" +#include "winservices.h" +#include "patcher.h" + +#ifdef __linux__ +#include +#include + +#define stricmp(a, b) strcasecmp(a, b) +#endif + +#include + +#define CHECKRES(x) try{ (x); } catch (const PatchException& exc) { logerr(exc.what()); } +#define KILL(x) (x); exit(1); + +// Forward declarations + +void preparePatch(fs::path backupPath); +void doPatch(); +bool downloadTools(fs::path path); +void copyTools(fs::path toolspath); +void stopServices(); +void restartServices(); + +void install(); +void uninstall(); +void showhelp(); + +#endif // UNLOKER_H \ No newline at end of file diff --git a/include/servicestoputils.h b/include/winservices.h similarity index 100% rename from include/servicestoputils.h rename to include/winservices.h diff --git a/include/ziparchive.h b/include/ziparchive.h new file mode 100644 index 0000000..d36d608 --- /dev/null +++ b/include/ziparchive.h @@ -0,0 +1,31 @@ +#ifndef UNLOCKER_ZIP_H +#define UNLOCKER_ZIP_H + +#include +#include +#include + +#define LIBZIP_STATIC + +#include +#include + +class ZipException : public std::runtime_error +{ +public: + ZipException(const char* message) : std::runtime_error(message) {} + ZipException(const std::string& message) : std::runtime_error(message) {} +}; + +class Zip { +public: + Zip(const std::string& zipFile); + ~Zip(); + bool extract(const std::string& fileName, const std::string& to, void(*progressCallback)(float) = nullptr); +private: + static constexpr size_t AR_BUFFER_SIZE = 1024*16; + zip_t* zip_archive = nullptr; +}; + + +#endif // UNLOCKER_ZIP_H \ No newline at end of file diff --git a/src/archive.cpp b/src/archive.cpp new file mode 100644 index 0000000..c8e8364 --- /dev/null +++ b/src/archive.cpp @@ -0,0 +1,42 @@ +#include "archive.h" + +bool Archive::extractTar(fs::path from, std::string filename, fs::path to) +{ + try + { + Tar tarfile(from.string()); + if (!tarfile.extract(filename, to.string(), Archive::extractionProgress)) { + fprintf(stderr, "TAR: Error while extracting %s. Not in the archive\n", filename.c_str()); + return false; + } + printf("\n"); + return true; + } + catch (const std::exception& exc) + { + fprintf(stderr, "TAR: An error occurred while extracting %s. %s", from.string().c_str(), exc.what()); + return false; + } +} + +bool Archive::extractZip(fs::path from, std::string filename, fs::path to) +{ + try { + Zip zip(from.string()); + if (!zip.extract(filename, to.string(), Archive::extractionProgress)) { + fprintf(stderr, "ZIP: Error while extracting %s. Not in the archive\n", filename.c_str()); + return false; + } + printf("\n"); + + return true; + } + catch (const std::exception& exc) { + fprintf(stderr, "ZIP: An error occurred while extracting %s. %s", from.string().c_str(), exc.what()); + return false; + } +} + +void Archive::extractionProgress(float progress) { + printf("Extraction progress: %.0f %% \r", progress*100); +} \ No newline at end of file diff --git a/src/archiveutils.cpp b/src/archiveutils.cpp deleted file mode 100644 index 112326e..0000000 --- a/src/archiveutils.cpp +++ /dev/null @@ -1,158 +0,0 @@ -#include "archiveutils.h" -#include - -/** - Helper function to extract a file using filesystem::path and std::string -*/ -/*bool Archive::extract_s(fs::path from, std::string filename, fs::path to) -{ - std::string from_s = from.string(); - std::string to_s = to.string(); - const char* from_c = from_s.c_str(); - const char* to_c = to_s.c_str(); - - return extract(from_c, filename.c_str(), to_c); -} - -int Archive::copy_data(struct archive* ar, struct archive* aw) -{ - int r; - const void* buff; - size_t size; - la_int64_t offset; - - for (;;) { - r = archive_read_data_block(ar, &buff, &size, &offset); - if (r == ARCHIVE_EOF) - return (ARCHIVE_OK); - if (r < ARCHIVE_OK) - return (r); - r = archive_write_data_block(aw, buff, size, offset); - if (r < ARCHIVE_OK) { - fprintf(stderr, "%s\n", archive_error_string(aw)); - return (r); - } - } -}*/ - -/** - Extract from archive "from", the file "filename" into path "to" (filename has to be included here too) -*/ -/*bool Archive::extract(const char* from, const char* filename, const char* to) -{ - struct archive* a; - struct archive* ext; - struct archive_entry* entry; - int flags; - int r; - - // Select which attributes we want to restore. - flags = ARCHIVE_EXTRACT_TIME; - flags |= ARCHIVE_EXTRACT_PERM; - flags |= ARCHIVE_EXTRACT_ACL; - flags |= ARCHIVE_EXTRACT_FFLAGS; - - a = archive_read_new(); - archive_read_support_format_all(a); - archive_read_support_compression_all(a); - ext = archive_write_disk_new(); - archive_write_disk_set_options(ext, flags); - archive_write_disk_set_standard_lookup(ext); - if ((r = archive_read_open_filename(a, from, 10240)) != ARCHIVE_OK) - return false; - for (;;) { - r = archive_read_next_header(a, &entry); - - if (r == ARCHIVE_EOF) - break; - if (r < ARCHIVE_OK) - logerr(archive_error_string(a)); - if (r < ARCHIVE_WARN) - return false; - - const char* entry_path = archive_entry_pathname(entry); - - if (entry_path == NULL || strcmp(entry_path, filename) != 0) - continue; - else { - archive_entry_set_pathname(entry, to); - r = archive_write_header(ext, entry); - if (r < ARCHIVE_OK) - logerr(archive_error_string(ext)); - else if (archive_entry_size(entry) > 0) { - r = copy_data(a, ext); - if (r < ARCHIVE_OK) - return false; - if (r < ARCHIVE_WARN) - return false; - } - r = archive_write_finish_entry(ext); - if (r < ARCHIVE_OK) - logerr(archive_error_string(ext)); - if (r < ARCHIVE_WARN) - return false; - - break; - } - } - archive_read_close(a); - archive_read_free(a); - archive_write_close(ext); - archive_write_free(ext); - return true; -}*/ - -bool Archive::extract_tar(fs::path from, std::string filename, fs::path to) -{ - std::string from_s = from.string(), to_s = to.string(); - - try - { - Tar tarfile(from_s); - if (!tarfile.extract(filename, to_s)) { - fprintf(stderr, "Error while extracting %s. Not in the archive\n", filename.c_str()); - return false; - } - return true; - } - catch (const std::exception& exc) - { - fprintf(stderr, "%s\n", exc.what()); - return false; - } -} - -bool Archive::extract_zip(fs::path from, std::string filename, fs::path to) -{ - std::string from_s = from.string(), to_s = to.string(); - const char* from_c = from_s.c_str(), * to_c = to_s.c_str(); - int zerr = ZIP_ER_OK; - zip_t* in_archive = zip_open(from_c, ZIP_RDONLY, &zerr); - - if (zerr != ZIP_ER_OK) { - fprintf(stderr, "Error while opening %s\n", from_c); - return false; - } - - zip_file_t* target_file = zip_fopen(in_archive, filename.c_str(), 0); - - if (target_file == NULL) { - fprintf(stderr, "Error while opening %s in %s. %s\n", filename.c_str(), from_c, zip_strerror(in_archive)); - zip_close(in_archive); - return false; - } - - FILE* out_f = fopen(to_c, "wb"); - - char buffer[AR_BUFFER_SIZE]; - int sz = 0; - while ((sz = zip_fread(target_file, buffer, AR_BUFFER_SIZE)) > 0) { - fwrite(buffer, sizeof(char), sz, out_f); - } - - fclose(out_f); - - zip_fclose(target_file); - zip_close(in_archive); - return true; -} \ No newline at end of file diff --git a/src/debugutils.cpp b/src/debug.cpp similarity index 88% rename from src/debugutils.cpp rename to src/debug.cpp index b8f0bcc..3b7c969 100644 --- a/src/debugutils.cpp +++ b/src/debug.cpp @@ -1,4 +1,4 @@ -#include "debugutils.h" +#include "debug.h" void logd(std::string msg) { diff --git a/src/installinfo.cpp b/src/installinfo.cpp new file mode 100644 index 0000000..90cd976 --- /dev/null +++ b/src/installinfo.cpp @@ -0,0 +1,75 @@ +#include "installinfo.h" + +#ifdef _WIN32 +#include "Windows.h" +#endif + +VMWareInfoRetriever::VMWareInfoRetriever() +{ +#ifdef _WIN32 + char regBuf[MAX_PATH]; + memset(regBuf, 0, MAX_PATH); + + DWORD regSize = MAX_PATH; + DWORD regType = 0; + + HKEY hResult; + LONG res = RegOpenKeyEx(HKEY_VMWARE, HKEY_SUBKEY_VMWARE, 0, KEY_QUERY_VALUE, &hResult); + + if (res != ERROR_SUCCESS) + { + throw VMWareInfoException("Couldn't open VMWare registry key. Make sure VMWare is correctly installed"); + } + + res = RegQueryValueEx(hResult, HKEY_QUERY_VALUE_INSTALLPATH, NULL, ®Type, (LPBYTE)regBuf, ®Size); + + if (res != ERROR_SUCCESS) + { + RegCloseKey(hResult); + throw VMWareInfoException("Couldn't read VMWare InstallPath registry value. Make sure VMWare is correctly installed"); + } + + installPath = std::string(reinterpret_cast(regBuf)); + + memset(regBuf, 0, MAX_PATH); + regSize = MAX_PATH; + res = RegQueryValueEx(hResult, HKEY_QUERY_VALUE_INSTALLPATH64, NULL, ®Type, (LPBYTE)regBuf, ®Size); + + if (res != ERROR_SUCCESS) + { + RegCloseKey(hResult); + throw VMWareInfoException("Couldn't read VMWare InstallPath64 registry value. Make sure VMWare is correctly installed"); + } + + installPath64 = std::string(regBuf); + + memset(regBuf, 0, MAX_PATH); + regSize = MAX_PATH; + res = RegQueryValueEx(hResult, HKEY_QUERY_VALUE_PRODUCTVERSION, NULL, ®Type, (LPBYTE)regBuf, ®Size); + + if (res != ERROR_SUCCESS) + { + RegCloseKey(hResult); + throw VMWareInfoException("Couldn't read VMWare ProductVersion registry value. Make sure VMWare is correctly installed"); + } + + prodVersion = std::string(regBuf); + + RegCloseKey(hResult); +#endif +} + +std::string VMWareInfoRetriever::getInstallPath() +{ + return installPath; +} + +std::string VMWareInfoRetriever::getInstallPath64() +{ + return installPath64; +} + +std::string VMWareInfoRetriever::getProductVersion() +{ + return prodVersion; +} diff --git a/src/installinfoutils.cpp b/src/installinfoutils.cpp deleted file mode 100644 index c12b55d..0000000 --- a/src/installinfoutils.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "installinfoutils.h" - -#ifdef _WIN32 -#include "Windows.h" -#endif - -VMWareInfoRetriever::VMWareInfoRetriever() -{ -#ifdef _WIN32 - char regBuf[MAX_PATH]; - memset(regBuf, 0, MAX_PATH); - - DWORD regSize = MAX_PATH; - DWORD regType = 0; - - HKEY hResult; - RegOpenKeyEx(HKEY_VMWARE, HKEY_SUBKEY_VMWARE, 0, KEY_QUERY_VALUE, &hResult); - - RegQueryValueEx(hResult, HKEY_QUERY_VALUE_INSTALLPATH, NULL, ®Type, (LPBYTE)regBuf, ®Size); - installPath = std::string(reinterpret_cast(regBuf)); - - memset(regBuf, 0, MAX_PATH); - regSize = MAX_PATH; - RegQueryValueEx(hResult, HKEY_QUERY_VALUE_INSTALLPATH64, NULL, ®Type, (LPBYTE)regBuf, ®Size); - installPath64 = std::string(regBuf); - - memset(regBuf, 0, MAX_PATH); - regSize = MAX_PATH; - RegQueryValueEx(hResult, HKEY_QUERY_VALUE_PRODUCTVERSION, NULL, ®Type, (LPBYTE)regBuf, ®Size); - prodVersion = std::string(regBuf); - - RegCloseKey(hResult); -#endif -} - -std::string VMWareInfoRetriever::getInstallPath() -{ - return installPath; -} - -std::string VMWareInfoRetriever::getInstallPath64() -{ - return installPath64; -} - -std::string VMWareInfoRetriever::getProductVersion() -{ - return prodVersion; -} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..d762e8b --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,65 @@ +#include +#include "unlocker.h" + +int main(int argc, const char* argv[]) +{ + std::cout << "auto-unlocker " << PROG_VERSION << std::endl + << std::endl; +#ifdef __linux__ + if (geteuid() != 0) + { + // User not root and not elevated permissions + logd("The program is not running as root, the patch may not work properly."); + std::cout << "Running the program with sudo/as root is recommended, in most cases required... Do you want to continue? (y/N) "; + char c = getc(stdin); + + if (c != 'y' && c != 'Y') + { + logd("Aborting..."); + exit(0); + } + } +#endif + if (argc > 1) + { + const char* arg = argv[1]; + + if (stricmp(arg, UNINSTALL_OPTION) == 0) + uninstall(); + else if (stricmp(arg, HELP_OPTION) == 0) + showhelp(); + else if (stricmp(arg, INSTALL_OPTION) == 0) + install(); + else if (stricmp(arg, DOWNLOADONLY_OPTION) == 0) + downloadTools(fs::path(".") / TOOLS_DOWNLOAD_FOLDER); + else + { + logd("Unrecognized command."); + logd(""); + showhelp(); + } + } + else { + fs::path backupFolder = BACKUP_FOLDER; + if (fs::exists(backupFolder)) + { + std::cout << "A backup folder has been found. Do you wish to uninstall the previous patch? Type y to uninstall, n to continue with installation." << std::endl + << "(Y/n) "; + + char c = getc(stdin); + + if (c == 'n' || c == 'N') + install(); + else + uninstall(); + } + else install(); + } + +#ifdef _WIN32 + logd("Press enter to quit."); + getc(stdin); +#endif + + return 0; +} \ No newline at end of file diff --git a/src/netutils.cpp b/src/netutils.cpp deleted file mode 100644 index d84b5d4..0000000 --- a/src/netutils.cpp +++ /dev/null @@ -1,109 +0,0 @@ -#include "netutils.h" - -/** - Writes data received from Curl to a stream -*/ -size_t Curl::write_data_file(char* ptr, size_t size, size_t nmemb, void* stream) -{ - std::iostream* fstr = static_cast(stream); - size_t bytes = size * nmemb; - fstr->write(ptr, bytes); - return bytes; -} - -constexpr double mBytesProgressUpdateDelta = 0.1; // 100 kB -static double mBytesDownloadedLastTime = 0.0; - -int Curl::progress_callback(void* clientp, double dltotal, double dlnow, double ultotal, double ulnow) -{ - if (dltotal > 0) - { - double mBytesTotal = dltotal / 1024 / 1024; - double mBytesNow = dlnow / 1024 / 1024; - - if ((mBytesNow - mBytesDownloadedLastTime) < mBytesProgressUpdateDelta) - { - // Don't update too frequently. - return 0; - } - - std::cout << "Download progress: " << (std::min)(100, (std::max)(0, int(dlnow * 100 / dltotal))) << " %, " << std::fixed << std::setprecision(2) << mBytesNow << " MB / " << mBytesTotal << " MB \r" << std::flush; - - mBytesDownloadedLastTime = mBytesNow; - } - return 0; -} - -CURLcode Curl::curlDownload(std::string url, std::string fileName) -{ - CURL* curl = curl_easy_init(); - if (curl) - { - std::fstream out_file(fileName, std::ios::out | std::ios::binary); - /* set URL to get here */ - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - if (DEBUG) - { - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - /* disable progress meter, set to 0L to enable and disable debug output */ - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); - } - - /* send all data to this function */ - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data_file); - /* write the page body to this ofstream */ - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &out_file); - /* if response is >400, return an error */ - curl_easy_setopt(curl, CURLOPT_FAILONERROR, true); - /* progress monitoring function */ - curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_callback); - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); - /* get it! */ - mBytesDownloadedLastTime = 0.0; - CURLcode res = curl_easy_perform(curl); - out_file.close(); - - std::cout << std::endl; - - curl_easy_cleanup(curl); - - return res; - } - else return CURLE_FAILED_INIT; -} - -CURLcode Curl::curlGet(std::string url, std::string& output) -{ - CURL* curl = curl_easy_init(); - if (curl) - { - std::stringstream sstream; - - /* set URL to get here */ - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - if (DEBUG) - { - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - /* disable progress meter, set to 0L to enable and disable debug output */ - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); - } - - /* send all data to this function */ - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data_file); - /* write the page body to the stringstream */ - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &sstream); - /* if response is >400, return an error */ - curl_easy_setopt(curl, CURLOPT_FAILONERROR, true); - /* progress monitoring function */ - /*curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_callback); - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);*/ - /* get it! */ - CURLcode res = curl_easy_perform(curl); - output = sstream.str(); - - curl_easy_cleanup(curl); - - return res; - } - else return CURLE_FAILED_INIT; -} diff --git a/src/network.cpp b/src/network.cpp new file mode 100644 index 0000000..91cf393 --- /dev/null +++ b/src/network.cpp @@ -0,0 +1,137 @@ +#include "network.h" + +Network::Network() { + CURLcode res = curl_global_init(CURL_GLOBAL_ALL); + + if (res != CURLE_OK) { + throw NetworkException("Could not initialize CURL", res); + } +} + +Network::~Network() { + curl_global_cleanup(); +} + +/** + Writes data received from Curl to a stream +*/ +size_t Network::write_data_file(char* ptr, size_t size, size_t nmemb, void* stream) +{ + std::iostream* fstr = static_cast(stream); + size_t bytes = size * nmemb; + fstr->write(ptr, bytes); + return bytes; +} + +int Network::progress_callback(void* clientp, double dltotal, double dlnow, double ultotal, double ulnow) +{ + NetworkProgress* networkProgress = reinterpret_cast(clientp); + + if (dltotal > 0) + { + double mBytesTotal = dltotal / 1024 / 1024; + double mBytesNow = dlnow / 1024 / 1024; + + long long nowTime = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + // Drop progress update if less than a set time has elapsed since the last one + if (nowTime - networkProgress->lastProgressUpdateTime < updatePeriodMs) + { + // Don't update too frequently. + return 0; + } + + double mBytesDelta = mBytesNow - networkProgress->mBytesDownloadedLastTime; + long long timeDeltaMs = nowTime - networkProgress->lastProgressUpdateTime; + double downloadRate = mBytesDelta / (timeDeltaMs / 1000.); + + printf("Download progress: %d %%, %.2f MB / %.2f MB, %.3f MB/s \r", + (std::min)(100, (std::max)(0, int(dlnow * 100 / dltotal))), + mBytesNow, mBytesTotal, downloadRate); + + networkProgress->mBytesDownloadedLastTime = mBytesNow; + networkProgress->lastProgressUpdateTime = nowTime; + } + return 0; +} + +void Network::curlDownload(const std::string& url, const std::string& fileName) +{ + CURL* curl = curl_easy_init(); + if (curl) + { + std::fstream out_file(fileName, std::ios::out | std::ios::binary); + /* set URL to get here */ + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + +#if CURL_DEBUG + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + /* disable progress meter, set to 0L to enable and disable debug output */ + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); +#endif + + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, Network::write_data_file); + /* write the page body to this ofstream */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &out_file); + /* if response is >400, return an error */ + curl_easy_setopt(curl, CURLOPT_FAILONERROR, true); + /* progress monitoring function */ + curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &networkProgress); + curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, Network::progress_callback); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); + /* get it! */ + networkProgress.mBytesDownloadedLastTime = 0.0; + networkProgress.lastProgressUpdateTime = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + CURLcode res = curl_easy_perform(curl); + out_file.close(); + + std::cout << std::endl; + + curl_easy_cleanup(curl); + + if (res != CURLE_OK) { + throw NetworkException("Error in CURL get request: " + std::string(curl_easy_strerror(res)), res); + } + } + else throw std::runtime_error("Could not initialize CURL instance"); +} + +std::string Network::curlGet(const std::string& url) +{ + CURL* curl = curl_easy_init(); + if (curl) + { + std::stringstream sstream; + + /* set URL to get here */ + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + +#if CURL_DEBUG + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + /* disable progress meter, set to 0L to enable and disable debug output */ + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); +#endif + + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, Network::write_data_file); + /* write the page body to the stringstream */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &sstream); + /* if response is >400, return an error */ + curl_easy_setopt(curl, CURLOPT_FAILONERROR, true); + /* progress monitoring function */ + /*curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_callback); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);*/ + /* get it! */ + CURLcode res = curl_easy_perform(curl); + + curl_easy_cleanup(curl); + + if (res != CURLE_OK) + { + throw NetworkException("Error in CURL get request: " + std::string(curl_easy_strerror(res)), res); + } + + return sstream.str(); + } + else throw std::runtime_error("Could not initialize CURL instance"); +} diff --git a/src/patchutils.cpp b/src/patcher.cpp similarity index 99% rename from src/patchutils.cpp rename to src/patcher.cpp index 5925220..5a5de0f 100644 --- a/src/patchutils.cpp +++ b/src/patcher.cpp @@ -1,4 +1,4 @@ -#include "patchutils.h" +#include "patcher.h" // Settings for memory and IO related stuff #define FREAD_BUF_SIZE 2048 // 2 kB diff --git a/src/tar.cpp b/src/tar.cpp index 7de8e8d..7945ba6 100644 --- a/src/tar.cpp +++ b/src/tar.cpp @@ -6,13 +6,13 @@ Tar::Tar(std::string filename) if (tarfile == NULL) { - throw std::runtime_error("Couldn't open " + filename); + throw TarException("Couldn't open " + filename); } tar_posix_header fileheader = {}; int readsz = 0; - while ((readsz = fread(reinterpret_cast(&fileheader), 1, sizeof(fileheader), tarfile)) == 512) + while ((readsz = fread(reinterpret_cast(&fileheader), 1, sizeof(fileheader), tarfile)) == TAR_BLOCK_SZ) { int checksum = 0; char* header_data = reinterpret_cast(&fileheader); @@ -22,7 +22,7 @@ Tar::Tar(std::string filename) for (int i = 0; i < 8; i++) checksum += (' ' - (unsigned char)fileheader.chksum[i]); - int header_checksum = oct_to_int(fileheader.chksum); + int header_checksum = octToInt(fileheader.chksum); if (checksum != header_checksum) { continue; @@ -31,28 +31,25 @@ Tar::Tar(std::string filename) File file; file.name = std::string(fileheader.name); - switch (fileheader.size[0]) { - case 0xFF: // base-256 - throw std::runtime_error("base-256 size encoding unsupported"); - break; - case 0x80: - throw std::runtime_error("base-256 size encoding unsupported"); - break; - default: // octal - file.size = oct_to_sz(fileheader.size); - break; - } - file.groupId = oct_to_int(fileheader.gid); - file.mode = oct_to_int(fileheader.mode); - file.ownerId = oct_to_int(fileheader.uid); - file.lastModified = oct_to_int(fileheader.mtime); + file.size = octToSz(fileheader.size); + file.groupId = octToInt(fileheader.gid); + file.mode = octToInt(fileheader.mode); + file.ownerId = octToInt(fileheader.uid); + file.lastModified = octToInt(fileheader.mtime); file.position = ftell(tarfile); file.typeflag = static_cast(fileheader.typeflag); fileList.emplace_back(file); - int toskip = (std::ceil(file.size / 512.0) * 512); - fseek(tarfile, toskip, SEEK_CUR); + // Calculate the bytes to skip to reach the next file header location + // The count of bytes to skip is the file size fitted to the 512-block size + size_t toskip = (std::ceil(file.size / static_cast(TAR_BLOCK_SZ)) * static_cast(TAR_BLOCK_SZ)); + + // Using 64-bit data types and functions to support archives containing large files + // Otherwise, the first large file size (> ~2GB on Windows) would probably overflow the signed long and + // break the 512 blocks succession. This is compiler dependent, though + // 64 bit long long instead supports files way beyond the tar individual file size limit + fseek_e64(tarfile, toskip, SEEK_CUR); } fseek(tarfile, 0, SEEK_SET); @@ -66,41 +63,54 @@ Tar::~Tar() } } -void Tar::extract(const File& file, std::string to) +void Tar::extract(const File& file, std::string to, void(*progressCallback)(float)) { + // If the name ends with a / character then it's a directory and we don't support it (for now) + if (*(file.name.cend() - 1) == '/') { + throw TarException("Can't extract a directory. Operation not supported"); + } + + // Throw an exception if the file being extracted is not a regular file (symlink, directory, etc.) if (file.typeflag != REGTYPE && file.typeflag != AREGTYPE) { - throw std::runtime_error("Can't extract this type of file. Only regular files supported"); + throw TarException("Can't extract this type of file. Only regular files supported"); } - fseek(tarfile, file.position, SEEK_SET); - int size = file.size; + // Set the file pointer to the beginning of the file being extracted + fseek_e64(tarfile, file.position, SEEK_SET); + + size_t size, totalSize; + size = totalSize = file.size; FILE* outfile = fopen(to.c_str(), "wb"); if (outfile != NULL) { - char* buffer = new char[EXTRACT_BUFFER_SZ]; + std::array buffer; while (size > 0) { int tocopy = std::min(EXTRACT_BUFFER_SZ, size); - fread(buffer, 1, tocopy, tarfile); - fwrite(buffer, 1, tocopy, outfile); + fread(buffer.data(), 1, tocopy, tarfile); + fwrite(buffer.data(), 1, tocopy, outfile); + + if (progressCallback != nullptr) { + progressCallback(float(totalSize - size) / totalSize); + } size -= tocopy; } - delete[] buffer; + fclose(outfile); } else { - throw std::runtime_error("Error while opening " + to + " for extracting"); + throw TarException("Error while opening " + to + " for writing"); } } -bool Tar::extract(std::string fileName, std::string to) +bool Tar::extract(std::string fileName, std::string to, void(*progressCallback)(float)) { for (const File& file : fileList) { if (file.name == fileName) { - extract(file, to); + extract(file, to, progressCallback); return true; } } @@ -112,15 +122,44 @@ const std::vector& Tar::getFileList() const return fileList; } +bool Tar::contains(std::string fileName) const +{ + for (const File& f : fileList) { + if (f.name == fileName) { + return true; + } + } + return false; +} -int Tar::oct_to_int(char* oct) +std::vector Tar::search(const std::string& term) +{ + std::vector results; + + std::copy_if(fileList.begin(), fileList.end(), std::back_inserter(results), [&term](const File& file) { + return file.name.find(term) != std::string::npos; + }); + + return results; +} + + +int Tar::octToInt(char* oct) { int res; return sscanf(oct, "%o", &res) == 1 ? res : 0; } -int Tar::oct_to_sz(char* oct) +size_t Tar::octToSz(char* oct) { - size_t res; - return sscanf(oct, "%zo", &res) == 1 ? res : 0; + if (*oct == 0xFF) { + throw TarException("base-256 file size not supported."); + } + else if (*oct == 0x80) { + throw TarException("base-256 file size not supported."); + } + else { + size_t res; + return sscanf(oct, "%zo", &res) == 1 ? res : 0; + } } diff --git a/src/toolsdownloader.cpp b/src/toolsdownloader.cpp new file mode 100644 index 0000000..a13c0eb --- /dev/null +++ b/src/toolsdownloader.cpp @@ -0,0 +1,134 @@ +#include "toolsdownloader.h" + +ToolsDownloader::ToolsDownloader(Network& network, const std::string& baseUrl, const std::string& version) + : network(network), versionUrl(baseUrl + version + "/"), versionNumber(version) +{ + std::string versionHtml = network.curlGet(versionUrl); + BuildsParser builds(versionHtml); + + if (builds.size() == 0) { + throw ToolsDownloaderException("No builds found for the version number " + versionNumber); + } + + buildurl = versionUrl + builds.getLatest(); // use the latest build +} + +bool ToolsDownloader::download(const fs::path& to) +{ + bool success = downloadStandalone(to); + if (!success) { + success = downloadFromCore(to); + } + return success; +} + +bool ToolsDownloader::downloadStandalone(const fs::path& to) +{ + fs::path temppath = fs::temp_directory_path(); + + std::string toolsurl = buildurl + FUSION_DEF_TOOLS_LOC; + std::string tools_pre15_url = buildurl + FUSION_DEF_PRE15_TOOLS_LOC; + + std::string tools_diskpath = (temppath / FUSION_DEF_TOOLS_NAME).string(); + std::string toolspre15_diskpath = (temppath / FUSION_DEF_PRE15_TOOLS_NAME).string(); + + logd("Downloading tools from " + toolsurl + " and " + tools_pre15_url); + + try { + network.curlDownload(toolsurl, tools_diskpath); + network.curlDownload(tools_pre15_url, toolspre15_diskpath); + + // if tools were successfully downloaded, extract them to destination folder + bool success = Archive::extractTar(temppath / FUSION_DEF_TOOLS_NAME, FUSION_DEF_TOOLS_ZIP, temppath / FUSION_DEF_TOOLS_ZIP); + success &= Archive::extractTar(temppath / FUSION_DEF_PRE15_TOOLS_NAME, FUSION_DEF_PRE15_TOOLS_ZIP, temppath / FUSION_DEF_PRE15_TOOLS_ZIP); + + if (!success) + { + logerr("Couldn't extract zip files from tars"); + return false; + } + + success = Archive::extractZip(temppath / FUSION_DEF_TOOLS_ZIP, FUSION_TAR_TOOLS_ISO, to / FUSION_ZIP_TOOLS_NAME); + success &= Archive::extractZip(temppath / FUSION_DEF_PRE15_TOOLS_ZIP, FUSION_TAR_PRE15_TOOLS_ISO, to / FUSION_ZIP_PRE15_TOOLS_NAME); + + // Cleanup zips + fs::remove(temppath / FUSION_DEF_TOOLS_ZIP); + fs::remove(temppath / FUSION_DEF_PRE15_TOOLS_ZIP); + + if (!success) + { + logerr("Couldn't extract tools from zip files"); + return false; + } + + // Cleanup tars + fs::remove(temppath / FUSION_DEF_TOOLS_NAME); + fs::remove(temppath / FUSION_DEF_PRE15_TOOLS_NAME); + } + catch (const NetworkException& exc) { + if (exc.getCode() == CURLE_HTTP_RETURNED_ERROR) { + logd("Tools not found as standalone"); + return false; + } + else { + throw std::runtime_error(std::string(exc.what())); + } + } + + return true; +} + +bool ToolsDownloader::downloadFromCore(const fs::path& to) +{ + fs::path temppath = fs::temp_directory_path(); + + // No tools available, try getting them from core fusion file + std::string coreurl = buildurl + FUSION_DEF_CORE_LOC; + std::string core_diskpath = (temppath / FUSION_DEF_CORE_NAME).string(); + + logd("Downloading core tar from " + coreurl); + + try + { + network.curlDownload(coreurl, core_diskpath); + + // If the core package was successfully downloaded, extract the tools from it + logd("Extracting from .tar to temp folder ..."); + + fs::path temppath = fs::temp_directory_path(); + + bool success = Archive::extractTar(temppath / FUSION_DEF_CORE_NAME, FUSION_DEF_CORE_NAME_ZIP, temppath / FUSION_DEF_CORE_NAME_ZIP); + if (!success) { + logerr("Couldn't extract from the tar file"); + // Error in the tar file, try the next version number + return false; + } + + logd("Extracting from .zip to destination folder ..."); + + success = Archive::extractZip(temppath / FUSION_DEF_CORE_NAME_ZIP, FUSION_ZIP_TOOLS_ISO, to / FUSION_ZIP_TOOLS_NAME); + success &= Archive::extractZip(temppath / FUSION_DEF_CORE_NAME_ZIP, FUSION_ZIP_PRE15_TOOLS_ISO, to / FUSION_ZIP_PRE15_TOOLS_NAME); + + // Cleanup zip file + fs::remove(temppath / FUSION_DEF_CORE_NAME_ZIP); + + if (!success) { + logerr("Couldn't extract from the zip file"); // Error in the zip file, try the next version number + return false; + } + + // Cleanup tar file + fs::remove(temppath / FUSION_DEF_CORE_NAME); + } + catch (const NetworkException& exc) { + if (exc.getCode() == CURLE_HTTP_RETURNED_ERROR) { + logd("Couldn't download tools from this version number. Trying the next one"); + return false; + } + else { + throw std::runtime_error(std::string(exc.what())); + } + } + + return true; +} diff --git a/src/unlocker.cpp b/src/unlocker.cpp index 24e2ca3..96f95e7 100644 --- a/src/unlocker.cpp +++ b/src/unlocker.cpp @@ -21,147 +21,55 @@ *************************************************************************************************/ -#include -#include "filesystem.hpp" -#include - -#include - -#include "config.h" -#include "netutils.h" -#include "versionparser.h" -#include "debugutils.h" -#include "buildsparser.h" -#include "archiveutils.h" -#include "installinfoutils.h" -#include "servicestoputils.h" -#include "patchutils.h" - -#ifdef __linux__ -#include -#include - -#define stricmp(a, b) strcasecmp(a, b) -#endif - -#include +#include "unlocker.h" -#define CHECKRES(x) try{ (x); } catch (const Patcher::PatchException& exc) { logerr(exc.what()); } -#define KILL(x) (x); exit(1); +// Main function -// Forward declarations +void install() +{ + try { + // Default output path is ./tools/ + fs::path toolsdirectory = fs::path(".") / TOOLS_DOWNLOAD_FOLDER; + // Default backup path is ./backup/ + fs::path backup = fs::path(".") / BACKUP_FOLDER; -void preparePatch(fs::path backupPath); -void doPatch(); -void downloadTools(fs::path path); -void copyTools(fs::path toolspath); -void stopServices(); -void restartServices(); + logd("Killing services and backing up files..."); + preparePatch(backup); -void install(); -void uninstall(); -void showhelp(); + logd("Patching files..."); + doPatch(); -// Main function + logd("Downloading tools into \"" + toolsdirectory.string() + "\" directory..."); -int main(int argc, const char* argv[]) -{ - std::cout << "auto-unlocker " << PROG_VERSION << std::endl - << std::endl; -#ifdef __linux__ - if (geteuid() != 0) - { - // User not root and not elevated permissions - logd("The program is not running as root, the patch may not work properly."); - std::cout << "Running the program with sudo/as root is recommended, in most cases required... Do you want to continue? (y/N) "; - char c = getc(stdin); - - if (c != 'y' && c != 'Y') + if (fs::exists(fs::path(".") / TOOLS_DOWNLOAD_FOLDER / FUSION_ZIP_TOOLS_NAME) && fs::exists(fs::path(".") / TOOLS_DOWNLOAD_FOLDER / FUSION_ZIP_PRE15_TOOLS_NAME)) { - logd("Aborting..."); - exit(0); - } - } -#endif - if (argc > 1) - { - const char* arg = argv[1]; - - if (stricmp(arg, UNINSTALL_OPTION) == 0) - uninstall(); - else if (stricmp(arg, HELP_OPTION) == 0) - showhelp(); - else if (stricmp(arg, INSTALL_OPTION) == 0) - install(); - else if (stricmp(arg, DOWNLOADONLY_OPTION) == 0) - downloadTools(fs::path(".") / TOOLS_DOWNLOAD_FOLDER); - else - { - logd("Unrecognized command."); - logd(""); - showhelp(); - } - } - else { - fs::path backupFolder = BACKUP_FOLDER; - if (fs::exists(backupFolder)) - { - std::cout << "A backup folder has been found. Do you wish to uninstall the previous patch? Type y to uninstall, n to continue with installation." << std::endl + std::cout << "Tools have been found in current folder. Do you want to use them or you want do download them again?" << std::endl + << "Please check that the existing tools are working and are the most recent ones." << std::endl << "(Y/n) "; char c = getc(stdin); - if (c == 'n' || c == 'N') - install(); - else - uninstall(); + if (c != 'y' && c != 'Y') + { + downloadTools(toolsdirectory); + } + } + else + { + downloadTools(toolsdirectory); } - else install(); - } - -#ifdef _WIN32 - logd("Press enter to quit."); - getc(stdin); -#endif - - return 0; -} - -void install() -{ - // Default output path is ./tools/ - fs::path toolsdirectory = fs::path(".") / TOOLS_DOWNLOAD_FOLDER; - // Default backup path is ./backup/ - fs::path backup = fs::path(".") / BACKUP_FOLDER; - - logd("Killing services and backing up files..."); - preparePatch(backup); - logd("Patching files..."); - doPatch(); + logd("Copying tools into program directory..."); + copyTools(toolsdirectory); - logd("Downloading tools into \"" + toolsdirectory.string() + "\" directory..."); + restartServices(); - if (fs::exists(fs::path(".") / TOOLS_DOWNLOAD_FOLDER / FUSION_ZIP_TOOLS_NAME) && fs::exists(fs::path(".") / TOOLS_DOWNLOAD_FOLDER / FUSION_ZIP_PRE15_TOOLS_NAME)) + logd("Patch complete."); + } + catch (const std::exception& exc) { - std::cout << "Tools have been found in the executable folder. Do you want to use the existing tools instead of downloading them again?" << std::endl - << "Please check that the existing tools are working and are the most recent ones." << std::endl - << "(Y/n) "; - - char c = getc(stdin); - - if (c != 'y' && c != 'Y') - downloadTools(toolsdirectory); + KILL(logerr(std::string(exc.what()))); } - else - downloadTools(toolsdirectory); - - logd("Copying tools into program directory..."); - copyTools(toolsdirectory); - - restartServices(); - - logd("Patch complete."); } void uninstall() @@ -191,9 +99,13 @@ void uninstall() try { if (fs::copy_file(file.path(), vmwareInstallDir / file.path().filename(), fs::copy_options::overwrite_existing)) + { logd("File \"" + file.path().string() + "\" restored successfully"); + } else + { logerr("Error while restoring \"" + file.path().string() + "\"."); + } } catch (fs::filesystem_error ex) { @@ -209,9 +121,13 @@ void uninstall() try { if (fs::copy_file(file.path(), vmwareInstallDir64 / file.path().filename(), fs::copy_options::overwrite_existing)) + { logd("File \"" + file.path().string() + "\" restored successfully"); + } else + { logerr("Error while restoring \"" + file.path().string() + "\"."); + } } catch (fs::filesystem_error ex) { @@ -231,7 +147,9 @@ void uninstall() { size_t is_drw = file.path().filename().string().find("darwin"); if (is_drw != std::string::npos && is_drw == 0) + { fs::remove(file); + } } } @@ -259,9 +177,13 @@ void uninstall() try { if (fs::copy_file(backup / file, vmwareDir / file, fs::copy_options::overwrite_existing)) + { logd("File \"" + (backup / file).string() + "\" restored successfully"); + } else + { logerr("Error while restoring \"" + (backup / file).string() + "\"."); + } } catch (fs::filesystem_error ex) { @@ -276,9 +198,13 @@ void uninstall() try { if (fs::copy_file(backup / fs::path(lib).filename(), fs::path(lib), fs::copy_options::overwrite_existing)) + { logd("File \"" + (backup / fs::path(lib).filename()).string() + "\" restored successfully"); + } else + { logerr("Error while restoring \"" + (backup / fs::path(lib).filename()).string() + "\"."); + } } catch (fs::filesystem_error ex) { @@ -295,7 +221,9 @@ void uninstall() { size_t is_drw = file.path().filename().string().find("darwin"); if (is_drw != std::string::npos && is_drw == 0) + { fs::remove(file); + } } } @@ -321,44 +249,24 @@ void showhelp() // Copy tools to VMWare directory void copyTools(fs::path toolspath) { + fs::path toolsfrom = toolspath; #ifdef _WIN32 VMWareInfoRetriever vmInfo; fs::path copyto = vmInfo.getInstallPath(); - fs::path toolsfrom = toolspath; - - try - { - if (fs::copy_file(toolsfrom / FUSION_ZIP_TOOLS_NAME, copyto / FUSION_ZIP_TOOLS_NAME)) - logd("File \"" + (toolsfrom / FUSION_ZIP_TOOLS_NAME).string() + "\" copy done."); - else - logerr("File \"" + (toolsfrom / FUSION_ZIP_TOOLS_NAME).string() + "\" could not be copied."); - } - catch (const std::exception& e) - { - logerr(e.what()); - } - - try - { - if (fs::copy_file(toolsfrom / FUSION_ZIP_PRE15_TOOLS_NAME, copyto / FUSION_ZIP_PRE15_TOOLS_NAME)) - logd("File \"" + (toolsfrom / FUSION_ZIP_PRE15_TOOLS_NAME).string() + "\" copy done."); - else - logerr("File \"" + (toolsfrom / FUSION_ZIP_PRE15_TOOLS_NAME).string() + "\" could not be copied."); - } - catch (const std::exception& e) - { - logerr(e.what()); - } -#elif defined (__linux__) +#elif defined(__linux__) fs::path copyto = VM_LNX_ISO_DESTPATH; - fs::path toolsfrom = toolspath; +#endif try { - if (fs::copy_file(toolsfrom / FUSION_ZIP_TOOLS_NAME, copyto / FUSION_ZIP_TOOLS_NAME)) + if (fs::copy_file(toolsfrom / FUSION_ZIP_TOOLS_NAME, copyto / FUSION_ZIP_TOOLS_NAME, fs::copy_options::overwrite_existing)) + { logd("File \"" + (toolsfrom / FUSION_ZIP_TOOLS_NAME).string() + "\" copy done."); + } else + { logerr("File \"" + (toolsfrom / FUSION_ZIP_TOOLS_NAME).string() + "\" could not be copied."); + } } catch (const std::exception& e) { @@ -367,16 +275,19 @@ void copyTools(fs::path toolspath) try { - if (fs::copy_file(toolsfrom / FUSION_ZIP_PRE15_TOOLS_NAME, copyto / FUSION_ZIP_PRE15_TOOLS_NAME)) + if (fs::copy_file(toolsfrom / FUSION_ZIP_PRE15_TOOLS_NAME, copyto / FUSION_ZIP_PRE15_TOOLS_NAME, fs::copy_options::overwrite_existing)) + { logd("File \"" + (toolsfrom / FUSION_ZIP_PRE15_TOOLS_NAME).string() + "\" copy done."); + } else + { logerr("File \"" + (toolsfrom / FUSION_ZIP_PRE15_TOOLS_NAME).string() + "\" could not be copied."); + } } catch (const std::exception& e) { logerr(e.what()); } -#endif } // Actual patch code @@ -397,15 +308,15 @@ void doPatch() if (!fs::exists(vmx)) { - KILL(logerr("Vmx file not found")); + throw std::runtime_error("Vmx file not found"); } if (!fs::exists(vmx_debug)) { - KILL(logerr("Vmx-debug file not found")); + throw std::runtime_error("Vmx-debug file not found"); } if (!fs::exists(vmwarebase)) { - KILL(logerr("vmwarebase.dll file not found")); + throw std::runtime_error("vmwarebase.dll file not found"); } logd("File: " + vmx.filename().string()); @@ -445,15 +356,15 @@ void doPatch() if (!fs::exists(vmx)) { - KILL(logerr("Vmx file not found")); + throw std::runtime_error(logerr("Vmx file not found"); } if (!fs::exists(vmx_debug)) { - KILL(logerr("Vmx-debug file not found")); + throw std::runtime_error(logerr("Vmx-debug file not found"); } if (!fs::exists(vmlib)) { - KILL(logerr("Vmlib file not found")); + throw std::runtime_error(logerr("Vmlib file not found"); } logd("File: " + vmx.filename().string()); @@ -501,9 +412,13 @@ void stopServices() { try { if (ServiceStopper::KillProcess(process)) + { logd("Process \"" + process + "\" killed successfully."); + } else + { logerr("Could not kill process \"" + process + "\"."); + } } catch (const ServiceStopper::ServiceStopException& ex) { @@ -557,9 +472,13 @@ void preparePatch(fs::path backupPath) try { if (fs::copy_file(fPath, destpath / fPath.filename(), fs::copy_options::overwrite_existing)) + { logd("File \"" + fPath.string() + "\" backup done."); + } else + { logerr("File \"" + fPath.string() + "\" could not be copied."); + } } catch (const std::exception& e) { @@ -567,7 +486,6 @@ void preparePatch(fs::path backupPath) } } #elif defined (__linux__) - //TODO: linux code here // Backup files std::string filesToBackup[] = VM_LNX_BACKUP_FILES; fs::path destpath = backupPath; @@ -581,9 +499,13 @@ void preparePatch(fs::path backupPath) try { if (fs::copy_file(fPath, destpath / fPath.filename(), fs::copy_options::overwrite_existing)) + { logd("File \"" + fPath.string() + "\" backup done."); + } else + { logerr("File \"" + fPath.string() + "\" could not be copied."); + } } catch (const std::exception& e) { @@ -601,9 +523,13 @@ void preparePatch(fs::path backupPath) try { if (fs::copy_file(libpath, destpath / libpath.filename(), fs::copy_options::overwrite_existing)) + { logd("File \"" + libpath.string() + "\" backup done."); + } else + { logerr("File \"" + libpath.string() + "\" could not be copied."); + } break; } @@ -614,32 +540,33 @@ void preparePatch(fs::path backupPath) } } #else - // Either the compiler macros are not working or the the tool is trying to be compiled on an OS where it's not meant to be compiled + // Either the compiler definitions are not working or the the tool is being compiled on an OS where it's not meant to be compiled logerr("OS not supported"); - exit(1); // Better stop before messing things up + exit(1); #endif - } +} // Download tools into "path" -void downloadTools(fs::path path) +bool downloadTools(fs::path path) { + Network network; + fs::path temppath = fs::temp_directory_path(); // extract files in the temp folder first fs::create_directory(path); // create destination directory if it doesn't exist - curl_global_init(CURL_GLOBAL_ALL); - std::string url = FUSION_BASE_URL; std::string releasesList; - Curl::curlGet(url, releasesList); // get the releases HTML page + + releasesList = network.curlGet(url); // get the releases HTML page VersionParser versionParser(releasesList); // parse HTML page to version numbers if (versionParser.size() == 0) { logerr("No Fusion versions found in Download url location."); - return; + return false; } bool success = false; @@ -649,103 +576,11 @@ void downloadTools(fs::path path) { std::string version = it->getVersionText(); - std::string versionurl = url + version + "/"; - std::string versionhtml; - - Curl::curlGet(versionurl, versionhtml); - - BuildsParser builds(versionhtml); // parse the builds in the page - - if (builds.size() > 0) - { - std::string buildurl = versionurl + builds.getLatest(); // use the latest build - - std::string toolsurl = buildurl + FUSION_DEF_TOOLS_LOC; - std::string tools_pre15_url = buildurl + FUSION_DEF_PRE15_TOOLS_LOC; - - std::string tools_diskpath = (temppath / FUSION_DEF_TOOLS_NAME).string(); - std::string toolspre15_diskpath = (temppath / FUSION_DEF_PRE15_TOOLS_NAME).string(); - - - bool toolsAvailable = (Curl::curlDownload(toolsurl, tools_diskpath) == CURLE_OK); - toolsAvailable &= (Curl::curlDownload(tools_pre15_url, toolspre15_diskpath) == CURLE_OK); - - if (toolsAvailable) // if tools were successfully downloaded, extract them to destination folder - { - success = Archive::extract_tar(temppath / FUSION_DEF_TOOLS_NAME, FUSION_DEF_TOOLS_ZIP, temppath / FUSION_DEF_TOOLS_ZIP); - success &= Archive::extract_tar(temppath / FUSION_DEF_PRE15_TOOLS_NAME, FUSION_DEF_PRE15_TOOLS_ZIP, temppath / FUSION_DEF_PRE15_TOOLS_ZIP); - - if (!success) - { - logerr("Couldn't extract zip files from tars"); - continue; - } - - success = Archive::extract_zip(temppath / FUSION_DEF_TOOLS_ZIP, FUSION_TAR_TOOLS_ISO, path / FUSION_ZIP_TOOLS_NAME); - success &= Archive::extract_zip(temppath / FUSION_DEF_PRE15_TOOLS_ZIP, FUSION_TAR_PRE15_TOOLS_ISO, path / FUSION_ZIP_PRE15_TOOLS_NAME); - - // Cleanup zips - fs::remove(temppath / FUSION_DEF_TOOLS_ZIP); - fs::remove(temppath / FUSION_DEF_PRE15_TOOLS_ZIP); - - if (!success) - { - logerr("Couldn't extract tools from zip files"); - } - else - { - // Cleanup tars - fs::remove(temppath / FUSION_DEF_TOOLS_NAME); - fs::remove(temppath / FUSION_DEF_PRE15_TOOLS_NAME); + ToolsDownloader downloader(network, FUSION_BASE_URL, version); - success = true; - break; - } - } - else { - // No tools available, try getting them from core fusion file - std::string coreurl = buildurl + FUSION_DEF_CORE_LOC; - std::string core_diskpath = (temppath / FUSION_DEF_CORE_NAME).string(); - - if (Curl::curlDownload(coreurl, core_diskpath) == CURLE_OK) // If the core package was successfully downloaded, extract the tools from it - { - logd("Extracting from .tar to temp folder ..."); - - fs::path temppath = fs::temp_directory_path(); - - success = Archive::extract_tar(temppath / FUSION_DEF_CORE_NAME, FUSION_DEF_CORE_NAME_ZIP, temppath / FUSION_DEF_CORE_NAME_ZIP); - if (!success) { - logerr("Couldn't extract from the tar file"); - // Error in the tar file, try the next version number - continue; - } - - logd("Extracting from .zip to destination folder ..."); - - success = Archive::extract_zip(temppath / FUSION_DEF_CORE_NAME_ZIP, FUSION_ZIP_TOOLS_ISO, path / FUSION_ZIP_TOOLS_NAME); - success &= Archive::extract_zip(temppath / FUSION_DEF_CORE_NAME_ZIP, FUSION_ZIP_PRE15_TOOLS_ISO, path / FUSION_ZIP_PRE15_TOOLS_NAME); - - // Cleanup zip file - fs::remove(temppath / FUSION_DEF_CORE_NAME_ZIP); - - if (!success) - logerr("Couldn't extract from the zip file"); // Error in the zip file, try the next version number - else - { - // Cleanup tar file - fs::remove(temppath / FUSION_DEF_CORE_NAME); - - break; // All went good - } - } - - // Cleanup tar file - fs::remove(temppath / FUSION_DEF_CORE_NAME); - } - - // Cleanup tars - fs::remove(temppath / FUSION_DEF_TOOLS_NAME); - fs::remove(temppath / FUSION_DEF_PRE15_TOOLS_NAME); + if (downloader.download(path)) { + success = true; + break; } } @@ -756,5 +591,5 @@ void downloadTools(fs::path path) logerr("Couldn't find tools."); } - curl_global_cleanup(); + return success; } diff --git a/src/servicestoputils.cpp b/src/winservices.cpp similarity index 99% rename from src/servicestoputils.cpp rename to src/winservices.cpp index 56be6b3..8511b91 100644 --- a/src/servicestoputils.cpp +++ b/src/winservices.cpp @@ -1,4 +1,4 @@ -#include "servicestoputils.h" +#include "winservices.h" #ifdef _WIN32 #include diff --git a/src/ziparchive.cpp b/src/ziparchive.cpp new file mode 100644 index 0000000..5de3759 --- /dev/null +++ b/src/ziparchive.cpp @@ -0,0 +1,71 @@ +#include "ziparchive.h" + +Zip::Zip(const std::string& zipFile) +{ + // Open the zip file + const char* from_c = zipFile.c_str(); + int zerr = ZIP_ER_OK; + zip_archive = zip_open(from_c, ZIP_RDONLY, &zerr); + + if (zerr != ZIP_ER_OK) { + throw ZipException("Error while opening " + zipFile); + } +} + +Zip::~Zip() +{ + if (zip_archive != nullptr) { + zip_close(zip_archive); + zip_archive = nullptr; + } +} + +bool Zip::extract(const std::string& fileName, const std::string& to, void(*progressCallback)(float)) +{ + const char* fileName_c = fileName.c_str(), * to_c = to.c_str(); + + // Get the size of the file to extract + zip_stat_t target_file_stat; + zip_stat_init(&target_file_stat); + int res = zip_stat(zip_archive, fileName_c, 0, &target_file_stat); + + if (res != 0) + { + return false; + } + + size_t target_fsize = target_file_stat.size; + + // Open the file to extract + zip_file_t* target_file = zip_fopen(zip_archive, fileName_c, 0); + + if (target_file == NULL) { + return false; + } + + // Open the output file + FILE* out_f = fopen(to_c, "wb"); + + if (out_f == NULL) { + throw ZipException("Can't open the file " + to + " for writing"); + } + + size_t elapsed = 0; + + // Extract the file, printing the progress through the function + std::array buffer; + size_t sz = 0; + while ((sz = zip_fread(target_file, buffer.data(), AR_BUFFER_SIZE)) > 0) { + fwrite(buffer.data(), sizeof(char), sz, out_f); + elapsed += sz; + if (progressCallback != nullptr) { + progressCallback(static_cast(elapsed) / target_fsize); + } + } + + fclose(out_f); + + zip_fclose(target_file); + + return true; +} diff --git a/tests/colors.h b/tests/colors.h new file mode 100644 index 0000000..4496934 --- /dev/null +++ b/tests/colors.h @@ -0,0 +1,12 @@ +#ifndef COLORS_H +#define COLORS_H + +#define ANSI_COLOR_RED "\x1b[31m" +#define ANSI_COLOR_GREEN "\x1b[32m" +#define ANSI_COLOR_YELLOW "\x1b[33m" +#define ANSI_COLOR_BLUE "\x1b[34m" +#define ANSI_COLOR_MAGENTA "\x1b[35m" +#define ANSI_COLOR_CYAN "\x1b[36m" +#define ANSI_COLOR_RESET "\x1b[0m" + +#endif // COLORS_H \ No newline at end of file diff --git a/tests/test.h b/tests/test.h new file mode 100644 index 0000000..376afe5 --- /dev/null +++ b/tests/test.h @@ -0,0 +1,87 @@ +#ifndef TEST_H +#define TEST_H + +#include +#include +#include "colors.h" + +#include +#ifdef WIN32 +#include +#elif defined(__linux__) +#include +#endif + +void test_info(const char* message, ...) { + va_list argp; + va_start(argp, message); + char msg[512] = ANSI_COLOR_BLUE; + strcat(msg, message); + strcat(msg, ANSI_COLOR_RESET "\n"); + vprintf(msg, argp); + va_end(argp); +} + +void test_status(const char* message, ...) { + va_list argp; + va_start(argp, message); + char msg[512] = ANSI_COLOR_YELLOW; + strcat(msg, message); + strcat(msg, ANSI_COLOR_RESET "\n"); + vprintf(msg, argp); + va_end(argp); +} + +void test_error(const char* message, ...) { + va_list argp; + va_start(argp, message); + char msg[512] = ANSI_COLOR_RED; + strcat(msg, message); + strcat(msg, ANSI_COLOR_RESET "\n"); + vprintf(msg, argp); + va_end(argp); +} + +void test_success(const char* message, ...) { + va_list argp; + va_start(argp, message); + char msg[512] = ANSI_COLOR_GREEN; + strcat(msg, message); + strcat(msg, ANSI_COLOR_RESET "\n"); + vprintf(msg, argp); + va_end(argp); +} + +void updateProgress(float progress) +{ + printf("Status: %.0f %% \r", progress * 100); +} + +bool fileExists(const std::string& file) +{ +#ifdef WIN32 + DWORD dwAttrib = GetFileAttributesA(file.c_str()); + return dwAttrib != INVALID_FILE_ATTRIBUTES; +#elif defined(__linux__) + return access("./test.tar", F_OK) == 0; +#endif +} + +std::string getCwd() +{ + char cwd[1024] = {}; +#ifdef WIN32 + GetCurrentDirectoryA(1024, cwd); +#elif defined(__linux__) + getcwd(cwd, 1024); +#endif + return std::string(cwd); +} + +#define TEST_ERROR(reason) test_error("Test failed: %s", reason); exit(1) +#define TEST_SUCCESS() test_success("Test succeeded") + +#define BEGIN_TEST(what) test_status("Testing: %s", what) +#define ASSERT_TRUE(condition) if(!(condition)) { test_error("Test failed at condition: %s", #condition); exit(1); } + +#endif // TEST_H \ No newline at end of file diff --git a/tests/test_patch.cpp b/tests/test_patch.cpp index 0a315c8..ab19c26 100644 --- a/tests/test_patch.cpp +++ b/tests/test_patch.cpp @@ -12,21 +12,25 @@ #include #include #include "filesystem.hpp" -#include "patchutils.h" -#include "installinfoutils.h" +#include "patcher.h" +#include "installinfo.h" #include "tar.h" +#include "unlocker.h" void test_tar(); void do_test_patch(); void test_failed(bool to_cleanup = true); void test_equalness(); bool test_equal_file(fs::path file1, fs::path file2); +bool test_download_files(); void cleanup(); std::string basepath_str = ""; int main(int argc, char** argv) { + return test_download_files() ? 0 : 1; + if (argc > 1) { basepath_str = std::string(argv[1]); @@ -161,6 +165,16 @@ bool test_equal_file(fs::path file1, fs::path file2) { return true; } +bool test_download_files() { + try { + return downloadTools(fs::path("./tools")); + } + catch (const std::exception& exc) { + fprintf(stderr, exc.what()); + return false; + } +} + void cleanup() { std::cout << "Cleaning up test files...\n"; diff --git a/tests/test_tar.cpp b/tests/test_tar.cpp new file mode 100644 index 0000000..d2584b4 --- /dev/null +++ b/tests/test_tar.cpp @@ -0,0 +1,83 @@ +#include "tar.h" +#include "test.h" + +void tar_extraction(); +void testTarSearch(); + +int main(int argc, char** argv) { + test_info("Running TEST: Tar Extraction"); + + tar_extraction(); + //testTarSearch(); + +#ifdef WIN32 + system("pause"); +#endif + return 0; +} + +void tar_extraction() { + BEGIN_TEST("Testing TAR Extraction"); + + test_status("CWD is: %s", getCwd().c_str()); + + if(!fileExists("./test.tar")) + { + TEST_ERROR("File test.tar does not exist in current directory"); + } + + try { + Tar testTar("./test.tar"); + + ASSERT_TRUE(testTar.contains("test.file")); + + printf("\n"); + testTar.extract("test.file", "./test.file", updateProgress); + printf("\n"); + + remove("./test.file"); + + TEST_SUCCESS(); + } + catch (const std::exception& exc) { + TEST_ERROR(exc.what()); + } +} + +void testTarSearch() +{ + try { + Tar testTar("./test.tar"); + + BEGIN_TEST("Testing Tar Search: test.file"); + auto res = testTar.search("test.file"); + + bool found = false; + for (const Tar::File& f : res) { + if (f.name == "test.file") { + found = true; + break; + } + } + ASSERT_TRUE(res.size() == 1); + ASSERT_TRUE(found); + TEST_SUCCESS(); + + BEGIN_TEST("Testing Tar Search: fff"); + auto res2 = testTar.search("fff"); + found = false; + for (const Tar::File& f : res2) { + if (f.name == "test/fff.txt") { + found = true; + break; + } + } + ASSERT_TRUE(res2.size() == 1); + ASSERT_TRUE(found); + + TEST_SUCCESS(); + } + catch (const std::exception& exc) { + TEST_ERROR(exc.what()); + } +} \ No newline at end of file diff --git a/tests/test_zip.cpp b/tests/test_zip.cpp new file mode 100644 index 0000000..8688328 --- /dev/null +++ b/tests/test_zip.cpp @@ -0,0 +1,41 @@ +#include "ziparchive.h" +#include "test.h" + +void zip_extraction(); + +int main(int argc, char** argv) { + test_info("Running TEST: Zip Extraction"); + + zip_extraction(); + +#ifdef WIN32 + system("pause"); +#endif + return 0; +} + +void zip_extraction() { + BEGIN_TEST("Testing ZIP Extraction"); + + test_status("CWD is: %s", getCwd().c_str()); + + if (!fileExists("./test.zip")) + { + TEST_ERROR("File test.zip does not exist in current directory"); + } + + try { + Zip testZip("./test.zip"); + + printf("\n"); + testZip.extract("test.file", "./test.file", updateProgress); + printf("\n"); + + remove("./test.file"); + + TEST_SUCCESS(); + } + catch (const std::exception& exc) { + TEST_ERROR(exc.what()); + } +} \ No newline at end of file