From 7adf68309d624efae2c685a9c0ea425e0324d08b Mon Sep 17 00:00:00 2001 From: TheAssassin Date: Sat, 8 Jul 2023 22:59:19 +0200 Subject: [PATCH 01/12] step 1 --- src/CMakeLists.txt | 2 +- src/appimagetool_fetch_runtime.c | 163 ----------------------------- src/appimagetool_fetch_runtime.cpp | 161 ++++++++++++++++++++++++++++ src/appimagetool_fetch_runtime.h | 9 +- 4 files changed, 170 insertions(+), 165 deletions(-) delete mode 100644 src/appimagetool_fetch_runtime.c create mode 100644 src/appimagetool_fetch_runtime.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e9365a7..14acebb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,7 @@ add_executable(appimagetool appimagetool.c appimagetool_sign.c - appimagetool_fetch_runtime.c + appimagetool_fetch_runtime.cpp hexlify.c elf.c digest.c diff --git a/src/appimagetool_fetch_runtime.c b/src/appimagetool_fetch_runtime.c deleted file mode 100644 index fa224d0..0000000 --- a/src/appimagetool_fetch_runtime.c +++ /dev/null @@ -1,163 +0,0 @@ -// need to define this to enable asprintf -#define _GNU_SOURCE -#include -#include - -#include -#include -#include -#include - -#include "appimagetool_fetch_runtime.h" - -bool fetch_runtime(char* arch, size_t* size, char** buffer, bool verbose) { - // not the cleanest approach to globally init curl here, but this method shouldn't be called more than once anyway - curl_global_init(CURL_GLOBAL_ALL); - - // should be plenty big for the URL - char url[1024]; - int url_size = snprintf(url, sizeof(url), "https://github.com/AppImage/type2-runtime/releases/download/continuous/runtime-%s", arch); - if (url_size <= 0 || url_size >= sizeof(url)) { - fprintf(stderr, "Failed to generate runtime URL\n"); - curl_global_cleanup(); - return false; - } - - fprintf(stderr, "Downloading runtime file from %s\n", url); - - char curl_error_buf[CURL_ERROR_SIZE]; - CURL* handle = NULL; - // should also be plenty big for the redirect target - char effective_url[sizeof(url)]; - int success = -1L; - - curl_off_t content_length = -1; - - // first, we perform a HEAD request to determine the required buffer size to write the file to - // of course, this assumes that a) GitHub sends a Content-Length header and b) that it is correct and will be in - // the GET request, too - // we store the URL we are redirected to (which probably lies on some AWS and is unique to that file) for use in - // the GET request, which should ensure we really download the file whose size we check now - handle = curl_easy_init(); - - if (handle == NULL) { - fprintf(stderr, "Failed to initialize libcurl\n"); - curl_global_cleanup(); - return false; - } - - curl_easy_setopt(handle, CURLOPT_URL, url); - curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1L); - // should be plenty for GitHub - curl_easy_setopt(handle, CURLOPT_MAXREDIRS, 12L); - curl_easy_setopt(handle, CURLOPT_NOBODY, 1L); - curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, curl_error_buf); - if (verbose) { - curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L); - } - - success = curl_easy_perform(handle); - - if (success == CURLE_OK) { - // we want to clean up the handle before we can use effective_url - char* temp; - if (curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &temp) == CURLE_OK) { - strncpy(effective_url, temp, sizeof(effective_url) - 1); - // just a little sanity check - if (strlen(effective_url) != strlen(temp)) { - fprintf(stderr, "Failed to copy effective URL\n"); - // delegate cleanup - effective_url[0] = '\0'; - } else if (strcmp(url, effective_url) != 0) { - fprintf(stderr, "Redirected to %s\n", effective_url); - } - } else { - fprintf(stderr, "Error: failed to determine effective URL\n"); - // we recycle the cleanup call below and check whether effective_url was set to anything meaningful below - } - - if (curl_easy_getinfo(handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &content_length) == CURLE_OK) { - fprintf(stderr, "Downloading runtime binary of size %" CURL_FORMAT_CURL_OFF_T "\n", content_length); - } else { - fprintf(stderr, "Error: no Content-Length header sent by GitHub\n"); - // we recycle the cleanup call below and check whether content_length was set to anything meaningful below - } - } else { - fprintf(stderr, "HEAD request to %s failed: %s\n", url, curl_error_buf); - } - - curl_easy_cleanup(handle); - handle = NULL; - - if (success != CURLE_OK || strlen(effective_url) == 0 || content_length <= 0) { - curl_global_cleanup(); - return false; - } - - // now that we know the required buffer size, we allocate a suitable in-memory buffer and perform the GET request - // we allocate our own so that we don't have to use fread(...) to get the data - char raw_buffer[content_length]; - FILE* file_buffer = fmemopen(raw_buffer, sizeof(raw_buffer), "w+b"); - setbuf(file_buffer, NULL); - - if (file_buffer == NULL) { - fprintf(stderr, "fmemopen failed: %s\n", strerror(errno)); - curl_global_cleanup(); - return false; - } - - handle = curl_easy_init(); - - if (handle == NULL) { - fprintf(stderr, "Failed to initialize libcurl\n"); - curl_global_cleanup(); - return false; - } - - // note: we should not need any more redirects - curl_easy_setopt(handle, CURLOPT_URL, effective_url); - curl_easy_setopt(handle, CURLOPT_WRITEDATA, (void*) file_buffer); - curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, curl_error_buf); - if (verbose) { - curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L); - } - - success = curl_easy_perform(handle); - - int get_content_length; - - if (success != CURLE_OK) { - fprintf(stderr, "GET request to %s failed: %s\n", effective_url, curl_error_buf); - curl_easy_cleanup(handle); - curl_global_cleanup(); - return false; - } else { - if (curl_easy_getinfo(handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &get_content_length) != CURLE_OK) { - fprintf(stderr, "Error: no Content-Length header sent by GitHub\n"); - // we recycle the cleanup call below and check whether content_length was set to anything meaningful below - } - } - - curl_easy_cleanup(handle); - handle = NULL; - - // done with libcurl - curl_global_cleanup(); - - if (get_content_length != content_length) { - fprintf(stderr, "Downloading runtime binary of size %" CURL_FORMAT_CURL_OFF_T "\n", content_length); - } - - *size = content_length; - - *buffer = (char*) calloc(content_length + 1, 1); - - if (*buffer == NULL) { - fprintf(stderr, "Failed to allocate buffer\n"); - return false; - } - - memcpy((void* ) *buffer, raw_buffer, sizeof(raw_buffer)); - - return true; -} diff --git a/src/appimagetool_fetch_runtime.cpp b/src/appimagetool_fetch_runtime.cpp new file mode 100644 index 0000000..471b53e --- /dev/null +++ b/src/appimagetool_fetch_runtime.cpp @@ -0,0 +1,161 @@ +// need to define this to enable asprintf +#include +#include +#include +#include +#include + +#include +#include + +#include "appimagetool_fetch_runtime.h" + +#include + +enum class RequestType { + GET, + HEAD, +}; + +class CurlRequest { +private: + CURL* _handle; + +public: + CurlRequest(std::string url, RequestType requestType) { + // not the cleanest approach to globally init curl here, but this method shouldn't be called more than once anyway + curl_global_init(CURL_GLOBAL_ALL); + + this->_handle = curl_easy_init(); + if (_handle == nullptr) { + throw std::runtime_error("Failed to initialize libcurl\n"); + } + + curl_easy_setopt(this->_handle, CURLOPT_URL, url.c_str()); + + switch (requestType) { + case RequestType::GET: + break; + case RequestType::HEAD: + curl_easy_setopt(this->_handle, CURLOPT_NOBODY, 1L); + break; + } + + // default parameters + curl_easy_setopt(_handle, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(_handle, CURLOPT_MAXREDIRS, 12L); + + // curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, curl_error_buf); + } + + ~CurlRequest() { + curl_global_cleanup(); + curl_easy_cleanup(this->_handle); + } + + void setVerbose(bool verbose) { + curl_easy_setopt(this->_handle, CURLOPT_VERBOSE, verbose ? 1L : 0L); + } + + void setInsecure(bool insecure) { + std::cerr << "Warning: insecure request, please be careful!" << std::endl; + curl_easy_setopt(this->_handle, CURLOPT_SSL_VERIFYPEER, insecure ? 0L : 1L); + } + + std::string effectiveUrl() { + char* temp; + curl_easy_getinfo(_handle, CURLINFO_EFFECTIVE_URL, &temp); + return temp; + } + + curl_off_t contentLength() { + curl_off_t contentLength; + curl_easy_getinfo(_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &contentLength); + return contentLength; + } + + bool perform() { + auto result = curl_easy_perform(this->_handle); + return result == CURLE_OK; + } + + void setFileBuffer(FILE* fileBuffer) { + curl_easy_setopt(_handle, CURLOPT_WRITEDATA, (void*) fileBuffer); +// curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, (void*) fileBuffer); + } +}; + +bool fetch_runtime(char *arch, size_t *size, char **buffer, bool verbose) { + // should be plenty big for the URL + char url[1024]; + int url_size = snprintf(url, sizeof(url), + "https://github.com/AppImage/type2-runtime/releases/download/continuous/runtime-%s", arch); + if (url_size <= 0 || url_size >= sizeof(url)) { + fprintf(stderr, "Failed to generate runtime URL\n"); + return false; + } + + fprintf(stderr, "Downloading runtime file from %s\n", url); + + // first, we perform a HEAD request to determine the required buffer size to write the file to + // of course, this assumes that a) GitHub sends a Content-Length header and b) that it is correct and will be in + // the GET request, too + // we store the URL we are redirected to (which probably lies on some AWS and is unique to that file) for use in + // the GET request, which should ensure we really download the file whose size we check now + CurlRequest headRequest(url, RequestType::HEAD); + + if (verbose) { + headRequest.setVerbose(true); + } + + if (!headRequest.perform()) { + return false; + } + + if (headRequest.effectiveUrl() != url) { + fprintf(stderr, "Redirected to %s\n", headRequest.effectiveUrl().c_str()); + } + + // now that we know the required buffer size, we allocate a suitable in-memory buffer and perform the GET request + // we allocate our own so that we don't have to use fread(...) to get the data + std::vector rawBuffer(headRequest.contentLength()); + FILE *file_buffer = fmemopen(rawBuffer.data(), rawBuffer.size(), "w+b"); + setbuf(file_buffer, NULL); + + if (file_buffer == NULL) { + fprintf(stderr, "fmemopen failed: %s\n", strerror(errno)); + return false; + } + + fprintf(stderr, "Downloading runtime binary of size %" CURL_FORMAT_CURL_OFF_T "\n", headRequest.contentLength()); + + CurlRequest getRequest(headRequest.effectiveUrl(), RequestType::GET); + + if (verbose) { + getRequest.setVerbose(true); + } + + getRequest.setFileBuffer(file_buffer); + + if (!getRequest.perform()) { + return false; + } + + if (getRequest.contentLength() != headRequest.contentLength()) { + fprintf(stderr, "error: content length does not match"); + return false; + } + + *size = getRequest.contentLength(); + + *buffer = (char *) calloc(getRequest.contentLength() + 1, 1); + + if (*buffer == NULL) { + fprintf(stderr, "Failed to allocate buffer\n"); + return false; + } + + memcpy((void *) *buffer, rawBuffer.data(), rawBuffer.size()); + + return true; +} diff --git a/src/appimagetool_fetch_runtime.h b/src/appimagetool_fetch_runtime.h index 4110f9b..dee7212 100644 --- a/src/appimagetool_fetch_runtime.h +++ b/src/appimagetool_fetch_runtime.h @@ -4,4 +4,11 @@ * Download runtime from GitHub into a buffer. * This function allocates a buffer of the right size internally, which needs to be cleaned up by the caller. */ -bool fetch_runtime(char* arch, size_t* size, char** buffer, bool verbose); +#ifdef __cplusplus +extern "C" { +#endif +bool fetch_runtime(char *arch, size_t *size, char **buffer, bool verbose); +#ifdef __cplusplus +} +#endif + From 59939606586cc7c43868f719ec50fde9aff75a9a Mon Sep 17 00:00:00 2001 From: TheAssassin Date: Sat, 8 Jul 2023 23:22:12 +0200 Subject: [PATCH 02/12] step 2 --- src/appimagetool_fetch_runtime.cpp | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/appimagetool_fetch_runtime.cpp b/src/appimagetool_fetch_runtime.cpp index 471b53e..3eecf81 100644 --- a/src/appimagetool_fetch_runtime.cpp +++ b/src/appimagetool_fetch_runtime.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -86,16 +87,11 @@ class CurlRequest { }; bool fetch_runtime(char *arch, size_t *size, char **buffer, bool verbose) { - // should be plenty big for the URL - char url[1024]; - int url_size = snprintf(url, sizeof(url), - "https://github.com/AppImage/type2-runtime/releases/download/continuous/runtime-%s", arch); - if (url_size <= 0 || url_size >= sizeof(url)) { - fprintf(stderr, "Failed to generate runtime URL\n"); - return false; - } + std::ostringstream urlstream; + urlstream << "https://github.com/AppImage/type2-runtime/releases/download/continuous/runtime-%s" << arch; + auto url = urlstream.str(); - fprintf(stderr, "Downloading runtime file from %s\n", url); + std::cerr << "Downloading runtime file from " << url << std::endl; // first, we perform a HEAD request to determine the required buffer size to write the file to // of course, this assumes that a) GitHub sends a Content-Length header and b) that it is correct and will be in @@ -113,7 +109,7 @@ bool fetch_runtime(char *arch, size_t *size, char **buffer, bool verbose) { } if (headRequest.effectiveUrl() != url) { - fprintf(stderr, "Redirected to %s\n", headRequest.effectiveUrl().c_str()); + std::cerr << "Redirected to " << headRequest.effectiveUrl() << std::endl; } // now that we know the required buffer size, we allocate a suitable in-memory buffer and perform the GET request @@ -123,11 +119,11 @@ bool fetch_runtime(char *arch, size_t *size, char **buffer, bool verbose) { setbuf(file_buffer, NULL); if (file_buffer == NULL) { - fprintf(stderr, "fmemopen failed: %s\n", strerror(errno)); + std::cerr << "fmemopen failed " << strerror(errno) << std::endl; return false; } - fprintf(stderr, "Downloading runtime binary of size %" CURL_FORMAT_CURL_OFF_T "\n", headRequest.contentLength()); + std::cerr << "Downloading runtime binary of size " << headRequest.contentLength() << std::endl; CurlRequest getRequest(headRequest.effectiveUrl(), RequestType::GET); @@ -142,7 +138,7 @@ bool fetch_runtime(char *arch, size_t *size, char **buffer, bool verbose) { } if (getRequest.contentLength() != headRequest.contentLength()) { - fprintf(stderr, "error: content length does not match"); + std::cerr << "Error: content length does not match" << std::endl; return false; } @@ -151,11 +147,11 @@ bool fetch_runtime(char *arch, size_t *size, char **buffer, bool verbose) { *buffer = (char *) calloc(getRequest.contentLength() + 1, 1); if (*buffer == NULL) { - fprintf(stderr, "Failed to allocate buffer\n"); + std::cerr << "Failed to allocate buffer" << std::endl; return false; } - memcpy((void *) *buffer, rawBuffer.data(), rawBuffer.size()); + std::copy(rawBuffer.begin(), rawBuffer.end(), *buffer); return true; } From acc783f199adc03030b2df86c5b9060715d76b47 Mon Sep 17 00:00:00 2001 From: TheAssassin Date: Sat, 8 Jul 2023 23:31:40 +0200 Subject: [PATCH 03/12] step 3 --- src/appimagetool_fetch_runtime.cpp | 65 ++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/src/appimagetool_fetch_runtime.cpp b/src/appimagetool_fetch_runtime.cpp index 3eecf81..8483e16 100644 --- a/src/appimagetool_fetch_runtime.cpp +++ b/src/appimagetool_fetch_runtime.cpp @@ -18,6 +18,28 @@ enum class RequestType { HEAD, }; +class CurlResponse { +private: + bool _success; + std::string _effectiveUrl; + curl_off_t _contentLength; + +public: + CurlResponse(bool success, std::string effectiveUrl, curl_off_t contentLength) : _success(success), _effectiveUrl(effectiveUrl), _contentLength(contentLength) {} + + bool success() { + return _success; + } + + std::string effectiveUrl() { + return _effectiveUrl; + }; + + curl_off_t contentLength() { + return _contentLength; + } +}; + class CurlRequest { private: CURL* _handle; @@ -63,21 +85,16 @@ class CurlRequest { curl_easy_setopt(this->_handle, CURLOPT_SSL_VERIFYPEER, insecure ? 0L : 1L); } - std::string effectiveUrl() { - char* temp; - curl_easy_getinfo(_handle, CURLINFO_EFFECTIVE_URL, &temp); - return temp; - } + CurlResponse perform() { + auto result = curl_easy_perform(this->_handle); + + char* effectiveUrl; + curl_easy_getinfo(_handle, CURLINFO_EFFECTIVE_URL, &effectiveUrl); - curl_off_t contentLength() { curl_off_t contentLength; curl_easy_getinfo(_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &contentLength); - return contentLength; - } - bool perform() { - auto result = curl_easy_perform(this->_handle); - return result == CURLE_OK; + return {result == CURLE_OK, effectiveUrl, contentLength}; } void setFileBuffer(FILE* fileBuffer) { @@ -88,7 +105,7 @@ class CurlRequest { bool fetch_runtime(char *arch, size_t *size, char **buffer, bool verbose) { std::ostringstream urlstream; - urlstream << "https://github.com/AppImage/type2-runtime/releases/download/continuous/runtime-%s" << arch; + urlstream << "https://github.com/AppImage/type2-runtime/releases/download/continuous/runtime-" << arch; auto url = urlstream.str(); std::cerr << "Downloading runtime file from " << url << std::endl; @@ -104,17 +121,19 @@ bool fetch_runtime(char *arch, size_t *size, char **buffer, bool verbose) { headRequest.setVerbose(true); } - if (!headRequest.perform()) { + auto headResponse = headRequest.perform(); + + if (!headResponse.success()) { return false; } - if (headRequest.effectiveUrl() != url) { - std::cerr << "Redirected to " << headRequest.effectiveUrl() << std::endl; + if (headResponse.effectiveUrl() != url) { + std::cerr << "Redirected to " << headResponse.effectiveUrl() << std::endl; } // now that we know the required buffer size, we allocate a suitable in-memory buffer and perform the GET request // we allocate our own so that we don't have to use fread(...) to get the data - std::vector rawBuffer(headRequest.contentLength()); + std::vector rawBuffer(headResponse.contentLength()); FILE *file_buffer = fmemopen(rawBuffer.data(), rawBuffer.size(), "w+b"); setbuf(file_buffer, NULL); @@ -123,9 +142,9 @@ bool fetch_runtime(char *arch, size_t *size, char **buffer, bool verbose) { return false; } - std::cerr << "Downloading runtime binary of size " << headRequest.contentLength() << std::endl; + std::cerr << "Downloading runtime binary of size " << headResponse.contentLength() << std::endl; - CurlRequest getRequest(headRequest.effectiveUrl(), RequestType::GET); + CurlRequest getRequest(headResponse.effectiveUrl(), RequestType::GET); if (verbose) { getRequest.setVerbose(true); @@ -133,18 +152,20 @@ bool fetch_runtime(char *arch, size_t *size, char **buffer, bool verbose) { getRequest.setFileBuffer(file_buffer); - if (!getRequest.perform()) { + auto getResponse = getRequest.perform(); + + if (!getResponse.success()) { return false; } - if (getRequest.contentLength() != headRequest.contentLength()) { + if (headResponse.contentLength() != getResponse.contentLength()) { std::cerr << "Error: content length does not match" << std::endl; return false; } - *size = getRequest.contentLength(); + *size = getResponse.contentLength(); - *buffer = (char *) calloc(getRequest.contentLength() + 1, 1); + *buffer = (char *) calloc(getResponse.contentLength() + 1, 1); if (*buffer == NULL) { std::cerr << "Failed to allocate buffer" << std::endl; From 3895dbf0cbf5f0bf87f6597889cf01b5187971f6 Mon Sep 17 00:00:00 2001 From: TheAssassin Date: Sat, 8 Jul 2023 23:53:09 +0200 Subject: [PATCH 04/12] step 4 --- src/appimagetool_fetch_runtime.cpp | 48 +++++++++++++++++------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/src/appimagetool_fetch_runtime.cpp b/src/appimagetool_fetch_runtime.cpp index 8483e16..e5746fa 100644 --- a/src/appimagetool_fetch_runtime.cpp +++ b/src/appimagetool_fetch_runtime.cpp @@ -12,6 +12,7 @@ #include "appimagetool_fetch_runtime.h" #include +#include enum class RequestType { GET, @@ -23,9 +24,16 @@ class CurlResponse { bool _success; std::string _effectiveUrl; curl_off_t _contentLength; + std::vector _data; public: - CurlResponse(bool success, std::string effectiveUrl, curl_off_t contentLength) : _success(success), _effectiveUrl(effectiveUrl), _contentLength(contentLength) {} + CurlResponse(bool success, std::string effectiveUrl, curl_off_t contentLength, std::vector data) + : _success(success) + , _effectiveUrl(effectiveUrl) + , _contentLength(contentLength) + , _data(data) { + std::cerr << "data size: " << data.size() << std::endl; + } bool success() { return _success; @@ -38,11 +46,22 @@ class CurlResponse { curl_off_t contentLength() { return _contentLength; } + + std::vector data() { + return _data; + } }; class CurlRequest { private: CURL* _handle; + std::vector _buffer; + + static size_t writeStuff(char* data, size_t size, size_t nmemb, void* this_ptr) { + const auto bytes = size * nmemb; + std::copy(data, data + bytes, std::back_inserter(static_cast(this_ptr)->_buffer)); + return bytes; + } public: CurlRequest(std::string url, RequestType requestType) { @@ -68,6 +87,9 @@ class CurlRequest { curl_easy_setopt(_handle, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(_handle, CURLOPT_MAXREDIRS, 12L); + curl_easy_setopt(_handle, CURLOPT_WRITEFUNCTION, CurlRequest::writeStuff); + curl_easy_setopt(_handle, CURLOPT_WRITEDATA, static_cast(this)); + // curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, curl_error_buf); } @@ -94,12 +116,9 @@ class CurlRequest { curl_off_t contentLength; curl_easy_getinfo(_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &contentLength); - return {result == CURLE_OK, effectiveUrl, contentLength}; - } - void setFileBuffer(FILE* fileBuffer) { - curl_easy_setopt(_handle, CURLOPT_WRITEDATA, (void*) fileBuffer); -// curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, (void*) fileBuffer); + + return {result == CURLE_OK, effectiveUrl, contentLength, _buffer}; } }; @@ -131,17 +150,6 @@ bool fetch_runtime(char *arch, size_t *size, char **buffer, bool verbose) { std::cerr << "Redirected to " << headResponse.effectiveUrl() << std::endl; } - // now that we know the required buffer size, we allocate a suitable in-memory buffer and perform the GET request - // we allocate our own so that we don't have to use fread(...) to get the data - std::vector rawBuffer(headResponse.contentLength()); - FILE *file_buffer = fmemopen(rawBuffer.data(), rawBuffer.size(), "w+b"); - setbuf(file_buffer, NULL); - - if (file_buffer == NULL) { - std::cerr << "fmemopen failed " << strerror(errno) << std::endl; - return false; - } - std::cerr << "Downloading runtime binary of size " << headResponse.contentLength() << std::endl; CurlRequest getRequest(headResponse.effectiveUrl(), RequestType::GET); @@ -150,8 +158,6 @@ bool fetch_runtime(char *arch, size_t *size, char **buffer, bool verbose) { getRequest.setVerbose(true); } - getRequest.setFileBuffer(file_buffer); - auto getResponse = getRequest.perform(); if (!getResponse.success()) { @@ -172,7 +178,9 @@ bool fetch_runtime(char *arch, size_t *size, char **buffer, bool verbose) { return false; } - std::copy(rawBuffer.begin(), rawBuffer.end(), *buffer); + auto data = getResponse.data(); + + std::copy(data.begin(), data.end(), *buffer); return true; } From 536e7bebc5e3a948fd467786b81e66d838a03f6e Mon Sep 17 00:00:00 2001 From: TheAssassin Date: Sat, 8 Jul 2023 23:55:31 +0200 Subject: [PATCH 05/12] step 5 --- src/appimagetool_fetch_runtime.cpp | 46 +++++++++--------------------- 1 file changed, 13 insertions(+), 33 deletions(-) diff --git a/src/appimagetool_fetch_runtime.cpp b/src/appimagetool_fetch_runtime.cpp index e5746fa..4f53474 100644 --- a/src/appimagetool_fetch_runtime.cpp +++ b/src/appimagetool_fetch_runtime.cpp @@ -129,58 +129,38 @@ bool fetch_runtime(char *arch, size_t *size, char **buffer, bool verbose) { std::cerr << "Downloading runtime file from " << url << std::endl; - // first, we perform a HEAD request to determine the required buffer size to write the file to - // of course, this assumes that a) GitHub sends a Content-Length header and b) that it is correct and will be in - // the GET request, too - // we store the URL we are redirected to (which probably lies on some AWS and is unique to that file) for use in - // the GET request, which should ensure we really download the file whose size we check now - CurlRequest headRequest(url, RequestType::HEAD); + CurlRequest request(url, RequestType::GET); if (verbose) { - headRequest.setVerbose(true); + request.setVerbose(true); } - auto headResponse = headRequest.perform(); + auto response = request.perform(); - if (!headResponse.success()) { - return false; - } - - if (headResponse.effectiveUrl() != url) { - std::cerr << "Redirected to " << headResponse.effectiveUrl() << std::endl; - } - - std::cerr << "Downloading runtime binary of size " << headResponse.contentLength() << std::endl; - - CurlRequest getRequest(headResponse.effectiveUrl(), RequestType::GET); + std::cerr << "Downloaded runtime binary of size " << response.contentLength() << std::endl; - if (verbose) { - getRequest.setVerbose(true); - } - - auto getResponse = getRequest.perform(); - - if (!getResponse.success()) { + if (!response.success()) { return false; } - if (headResponse.contentLength() != getResponse.contentLength()) { - std::cerr << "Error: content length does not match" << std::endl; + auto runtimeData = response.data(); + + if (runtimeData.size() != response.contentLength()) { + std::cerr << "Error: downloaded data size of " << runtimeData.size() + << " does not match content-length of " << response.contentLength() << std::endl; return false; } - *size = getResponse.contentLength(); + *size = response.contentLength(); - *buffer = (char *) calloc(getResponse.contentLength() + 1, 1); + *buffer = (char *) calloc(response.contentLength() + 1, 1); if (*buffer == NULL) { std::cerr << "Failed to allocate buffer" << std::endl; return false; } - auto data = getResponse.data(); - - std::copy(data.begin(), data.end(), *buffer); + std::copy(runtimeData.begin(), runtimeData.end(), *buffer); return true; } From 4097ea14adf27bb514a407480fea709686cd62ca Mon Sep 17 00:00:00 2001 From: TheAssassin Date: Sat, 8 Jul 2023 23:56:30 +0200 Subject: [PATCH 06/12] step 6 --- src/appimagetool_fetch_runtime.cpp | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/appimagetool_fetch_runtime.cpp b/src/appimagetool_fetch_runtime.cpp index 4f53474..fe3806d 100644 --- a/src/appimagetool_fetch_runtime.cpp +++ b/src/appimagetool_fetch_runtime.cpp @@ -27,22 +27,15 @@ class CurlResponse { std::vector _data; public: - CurlResponse(bool success, std::string effectiveUrl, curl_off_t contentLength, std::vector data) + CurlResponse(bool success, curl_off_t contentLength, std::vector data) : _success(success) - , _effectiveUrl(effectiveUrl) , _contentLength(contentLength) - , _data(data) { - std::cerr << "data size: " << data.size() << std::endl; - } + , _data(data) {} bool success() { return _success; } - std::string effectiveUrl() { - return _effectiveUrl; - }; - curl_off_t contentLength() { return _contentLength; } @@ -110,15 +103,10 @@ class CurlRequest { CurlResponse perform() { auto result = curl_easy_perform(this->_handle); - char* effectiveUrl; - curl_easy_getinfo(_handle, CURLINFO_EFFECTIVE_URL, &effectiveUrl); - curl_off_t contentLength; curl_easy_getinfo(_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &contentLength); - - - return {result == CURLE_OK, effectiveUrl, contentLength, _buffer}; + return {result == CURLE_OK, contentLength, _buffer}; } }; From 6c576d4570483218bf93ad41d6c8e493692dc4a3 Mon Sep 17 00:00:00 2001 From: TheAssassin Date: Sun, 9 Jul 2023 00:06:21 +0200 Subject: [PATCH 07/12] step 7 --- src/appimagetool_fetch_runtime.cpp | 34 ++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/src/appimagetool_fetch_runtime.cpp b/src/appimagetool_fetch_runtime.cpp index fe3806d..fdd1766 100644 --- a/src/appimagetool_fetch_runtime.cpp +++ b/src/appimagetool_fetch_runtime.cpp @@ -45,10 +45,16 @@ class CurlResponse { } }; +class CurlException : public std::runtime_error { +public: + CurlException(CURLcode code, const std::string& errorMessage) : std::runtime_error(std::string(curl_easy_strerror(code)) + ": " + errorMessage) {} +}; + class CurlRequest { private: CURL* _handle; std::vector _buffer; + std::vector _errorBuffer; static size_t writeStuff(char* data, size_t size, size_t nmemb, void* this_ptr) { const auto bytes = size * nmemb; @@ -56,8 +62,14 @@ class CurlRequest { return bytes; } + void checkForCurlError(CURLcode code) { + if (code != CURLE_OK) { + throw CurlException(code, _errorBuffer.data()); + } + } + public: - CurlRequest(std::string url, RequestType requestType) { + CurlRequest(std::string url, RequestType requestType) : _errorBuffer(CURL_ERROR_SIZE) { // not the cleanest approach to globally init curl here, but this method shouldn't be called more than once anyway curl_global_init(CURL_GLOBAL_ALL); @@ -66,24 +78,24 @@ class CurlRequest { throw std::runtime_error("Failed to initialize libcurl\n"); } - curl_easy_setopt(this->_handle, CURLOPT_URL, url.c_str()); + checkForCurlError(curl_easy_setopt(this->_handle, CURLOPT_URL, url.c_str())); switch (requestType) { case RequestType::GET: break; case RequestType::HEAD: - curl_easy_setopt(this->_handle, CURLOPT_NOBODY, 1L); + checkForCurlError(curl_easy_setopt(this->_handle, CURLOPT_NOBODY, 1L)); break; } // default parameters - curl_easy_setopt(_handle, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(_handle, CURLOPT_MAXREDIRS, 12L); + checkForCurlError(curl_easy_setopt(_handle, CURLOPT_FOLLOWLOCATION, 1L)); + checkForCurlError(curl_easy_setopt(_handle, CURLOPT_MAXREDIRS, 12L)); - curl_easy_setopt(_handle, CURLOPT_WRITEFUNCTION, CurlRequest::writeStuff); - curl_easy_setopt(_handle, CURLOPT_WRITEDATA, static_cast(this)); + checkForCurlError(curl_easy_setopt(_handle, CURLOPT_WRITEFUNCTION, CurlRequest::writeStuff)); + checkForCurlError(curl_easy_setopt(_handle, CURLOPT_WRITEDATA, static_cast(this))); - // curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, curl_error_buf); + checkForCurlError(curl_easy_setopt(_handle, CURLOPT_ERRORBUFFER, _errorBuffer.data())); } ~CurlRequest() { @@ -92,19 +104,19 @@ class CurlRequest { } void setVerbose(bool verbose) { - curl_easy_setopt(this->_handle, CURLOPT_VERBOSE, verbose ? 1L : 0L); + checkForCurlError(curl_easy_setopt(this->_handle, CURLOPT_VERBOSE, verbose ? 1L : 0L)); } void setInsecure(bool insecure) { std::cerr << "Warning: insecure request, please be careful!" << std::endl; - curl_easy_setopt(this->_handle, CURLOPT_SSL_VERIFYPEER, insecure ? 0L : 1L); + checkForCurlError(curl_easy_setopt(this->_handle, CURLOPT_SSL_VERIFYPEER, insecure ? 0L : 1L)); } CurlResponse perform() { auto result = curl_easy_perform(this->_handle); curl_off_t contentLength; - curl_easy_getinfo(_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &contentLength); + checkForCurlError(curl_easy_getinfo(_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &contentLength)); return {result == CURLE_OK, contentLength, _buffer}; } From 13811884b3405f821ff8816059e6705c268a6cc4 Mon Sep 17 00:00:00 2001 From: TheAssassin Date: Sun, 9 Jul 2023 00:07:17 +0200 Subject: [PATCH 08/12] step 8 --- src/appimagetool_fetch_runtime.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/appimagetool_fetch_runtime.cpp b/src/appimagetool_fetch_runtime.cpp index fdd1766..deb863c 100644 --- a/src/appimagetool_fetch_runtime.cpp +++ b/src/appimagetool_fetch_runtime.cpp @@ -1,7 +1,3 @@ -// need to define this to enable asprintf -#include -#include -#include #include #include #include @@ -11,8 +7,8 @@ #include "appimagetool_fetch_runtime.h" +#include #include -#include enum class RequestType { GET, @@ -30,7 +26,7 @@ class CurlResponse { CurlResponse(bool success, curl_off_t contentLength, std::vector data) : _success(success) , _contentLength(contentLength) - , _data(data) {} + , _data(std::move(data)) {} bool success() { return _success; @@ -69,7 +65,7 @@ class CurlRequest { } public: - CurlRequest(std::string url, RequestType requestType) : _errorBuffer(CURL_ERROR_SIZE) { + CurlRequest(const std::string& url, RequestType requestType) : _errorBuffer(CURL_ERROR_SIZE) { // not the cleanest approach to globally init curl here, but this method shouldn't be called more than once anyway curl_global_init(CURL_GLOBAL_ALL); @@ -155,7 +151,7 @@ bool fetch_runtime(char *arch, size_t *size, char **buffer, bool verbose) { *buffer = (char *) calloc(response.contentLength() + 1, 1); - if (*buffer == NULL) { + if (*buffer == nullptr) { std::cerr << "Failed to allocate buffer" << std::endl; return false; } From 42f22e25719f157b1ab66ce1b1a3f11216b98c42 Mon Sep 17 00:00:00 2001 From: TheAssassin Date: Sun, 9 Jul 2023 00:07:42 +0200 Subject: [PATCH 09/12] step 9 --- src/appimagetool_fetch_runtime.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/appimagetool_fetch_runtime.cpp b/src/appimagetool_fetch_runtime.cpp index deb863c..95bb6b4 100644 --- a/src/appimagetool_fetch_runtime.cpp +++ b/src/appimagetool_fetch_runtime.cpp @@ -103,11 +103,6 @@ class CurlRequest { checkForCurlError(curl_easy_setopt(this->_handle, CURLOPT_VERBOSE, verbose ? 1L : 0L)); } - void setInsecure(bool insecure) { - std::cerr << "Warning: insecure request, please be careful!" << std::endl; - checkForCurlError(curl_easy_setopt(this->_handle, CURLOPT_SSL_VERIFYPEER, insecure ? 0L : 1L)); - } - CurlResponse perform() { auto result = curl_easy_perform(this->_handle); From 82b3258f8e4fc996d595ee88a91ac52e5df8d002 Mon Sep 17 00:00:00 2001 From: probonopd Date: Sun, 9 Jul 2023 10:03:27 +0000 Subject: [PATCH 10/12] Begin to use c++ in appimagetool.cpp --- .gitignore | 1 + ci/build.sh | 5 +- src/CMakeLists.txt | 20 +++- src/{appimagetool.c => appimagetool.cpp} | 120 ++++++++++++----------- src/appimagetool_sign.c | 7 ++ src/appimagetool_sign.h | 14 ++- src/elf.c | 19 ++-- src/elf.h | 14 +++ src/util.h | 8 ++ 9 files changed, 138 insertions(+), 70 deletions(-) rename src/{appimagetool.c => appimagetool.cpp} (94%) create mode 100644 src/elf.h diff --git a/.gitignore b/.gitignore index 15fb151..138dd75 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ cmake-build-*/ *.*swp* *.bak *.AppImage* +.vscode/ \ No newline at end of file diff --git a/ci/build.sh b/ci/build.sh index e7b06b9..951a0b2 100755 --- a/ci/build.sh +++ b/ci/build.sh @@ -41,7 +41,7 @@ cp "$(which mksquashfs)" AppDir/usr/bin cp "$repo_root"/resources/AppRun.sh AppDir/AppRun chmod +x AppDir/AppRun -wget https://github.com/AppImage/type2-runtime/releases/download/continuous/runtime-"$ARCH" +# wget https://github.com/AppImage/type2-runtime/releases/download/continuous/runtime-"$ARCH" pushd AppDir ln -s usr/share/applications/appimagetool.desktop . @@ -52,6 +52,7 @@ popd find AppDir cat AppDir/appimagetool.desktop -AppDir/AppRun --runtime-file runtime-"$ARCH" AppDir +# AppDir/AppRun --runtime-file runtime-"$ARCH" AppDir --verbose +AppDir/AppRun AppDir --verbose mv appimagetool-*.AppImage "$old_cwd" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 14acebb..f21186f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,13 +1,23 @@ add_executable(appimagetool - appimagetool.c - appimagetool_sign.c + appimagetool.cpp appimagetool_fetch_runtime.cpp - hexlify.c - elf.c + appimagetool_fetch_runtime.h + appimagetool_sign.c + appimagetool_sign.h digest.c + elf.c + hexlify.c + light_byteswap.h + light_elf.h md5.c + md5.h + util.h ) +# Ensure that the C compiler, not the C++ compiler, is used for compiling C files +set_source_files_properties(digest.c elf.c hexlify.c md5.c appimagetool_sign.c PROPERTIES LANGUAGE C) +set(CMAKE_C_COMPILER gcc) + # trick: list libraries on which imported static ones depend on in the PUBLIC section # CMake then adds them after the PRIVATE ones in the linker command target_link_libraries(appimagetool @@ -19,6 +29,7 @@ target_link_libraries(appimagetool PkgConfig::libcurl ) + target_compile_definitions(appimagetool PRIVATE -D_FILE_OFFSET_BITS=64 PRIVATE -DGIT_VERSION="${GIT_VERSION}" @@ -49,3 +60,4 @@ install( FILES "${PROJECT_SOURCE_DIR}/resources/appimagetool.appdata.xml" DESTINATION share/metainfo ) + diff --git a/src/appimagetool.c b/src/appimagetool.cpp similarity index 94% rename from src/appimagetool.c rename to src/appimagetool.cpp index 74f5d15..6296dff 100644 --- a/src/appimagetool.c +++ b/src/appimagetool.cpp @@ -34,6 +34,7 @@ #include #include +#include #include #include @@ -57,6 +58,14 @@ #include "appimagetool_fetch_runtime.h" #include "appimagetool_sign.h" +#include "elf.h" + +// C++ +#include +#include +#include +#include +#include typedef enum { fARCH_i686, @@ -122,56 +131,57 @@ int sfs_mksquashfs(char *source, char *destination, int offset) { guint sqfs_opts_len = sqfs_opts ? g_strv_length(sqfs_opts) : 0; int max_num_args = sqfs_opts_len + 22; - char* args[max_num_args]; + std::vector args; - int i = 0; -#ifndef AUXILIARY_FILES_DESTINATION - args[i++] = "mksquashfs"; -#else - args[i++] = pathToMksquashfs; -#endif - args[i++] = source; - args[i++] = destination; - args[i++] = "-offset"; - args[i++] = offset_string; + #ifndef AUXILIARY_FILES_DESTINATION + args.push_back("mksquashfs"); + #else + args.push_back(pathToMksquashfs); + #endif + args.push_back(source); + args.push_back(destination); + args.push_back("-offset"); + args.push_back(offset_string); + + int i = args.size(); if (sqfs_comp == NULL) { sqfs_comp = "zstd"; } - args[i++] = "-comp"; - args[i++] = sqfs_comp; + args.push_back("-comp"); + args.push_back(sqfs_comp); - args[i++] = "-root-owned"; - args[i++] = "-noappend"; + args.push_back("-root-owned"); + args.push_back("-noappend"); // compression-specific optimization if (strcmp(sqfs_comp, "xz") == 0) { // https://jonathancarter.org/2015/04/06/squashfs-performance-testing/ says: // improved performance by using a 16384 block size with a sacrifice of around 3% more squashfs image space - args[i++] = "-Xdict-size"; - args[i++] = "100%"; - args[i++] = "-b"; - args[i++] = "16384"; + args.push_back("-Xdict-size"); + args.push_back("100%"); + args.push_back("-b"); + args.push_back("16384"); } else if (strcmp(sqfs_comp, "zstd") == 0) { /* * > Build with 1MiB block size * > Using a bigger block size than mksquashfs's default improves read speed and can produce smaller AppImages as well * -- https://github.com/probonopd/go-appimage/commit/c4a112e32e8c2c02d1d388c8fa45a9222a529af3 */ - args[i++] = "-b"; - args[i++] = "1M"; + args.push_back("-b"); + args.push_back("1M"); } // check if ignore file exists and use it if possible if (access(APPIMAGEIGNORE, F_OK) >= 0) { printf("Including %s", APPIMAGEIGNORE); - args[i++] = "-wildcards"; - args[i++] = "-ef"; + args.push_back("-wildcards"); + args.push_back("-ef"); // avoid warning: assignment discards ‘const’ qualifier char* buf = strdup(APPIMAGEIGNORE); - args[i++] = buf; + args.push_back(buf); } // if an exclude file has been passed on the command line, should be used, too @@ -181,40 +191,35 @@ int sfs_mksquashfs(char *source, char *destination, int offset) { return -1; } - args[i++] = "-wildcards"; - args[i++] = "-ef"; - args[i++] = exclude_file; + args.push_back("-wildcards"); + args.push_back("-ef"); + args.push_back(exclude_file); + } - args[i++] = "-mkfs-time"; - args[i++] = "0"; + args.push_back("-mkfs-time"); + args.push_back("0"); for (guint sqfs_opts_idx = 0; sqfs_opts_idx < sqfs_opts_len; ++sqfs_opts_idx) { args[i++] = sqfs_opts[sqfs_opts_idx]; } - - args[i++] = 0; - - if (verbose) { - printf("mksquashfs commandline: "); - for (char** t = args; *t != 0; t++) { - printf("%s ", *t); + // Build the command string + std::stringstream cmdStream; + for (const auto& arg : args) { + // Quote each argument to handle spaces and special characters + cmdStream << "\"" << arg << "\" "; + } + if (verbose) { + std::cout << "Executing command: " << cmdStream.str() << std::endl; + } + if (std::system(cmdStream.str().c_str()) != 0) { + std::cerr << "ERROR: Failed to execute command: " << cmdStream.str() << std::endl; + exit(1); + } } - printf("\n"); + return 0; } -#ifndef AUXILIARY_FILES_DESTINATION - execvp("mksquashfs", args); - perror("execvp(\"mksquashfs\") failed"); -#else - execvp(pathToMksquashfs, args); - fprintf(stderr, "execvp(\"%s\") failed: %s\n", pathToMksquashfs, strerror(errno)); -#endif - return -1; // exec never returns - } - return 0; -} - /* Validate desktop file using desktop-file-validate on the $PATH * execlp(), execvp(), and execvpe() search on the $PATH */ int validate_desktop_file(char *file) { @@ -455,12 +460,12 @@ bool readFile(char* filename, size_t* size, char** buffer) { long fsize = ftell(f); fseek(f, 0, SEEK_SET); - char *indata = malloc(fsize); + char *indata = (char*) malloc(fsize); fread(indata, fsize, 1, f); fclose(f); *size = (int)fsize; *buffer = indata; - return TRUE; + return true; } /* run a command outside the current appimage, block environs like LD_LIBRARY_PATH */ @@ -515,7 +520,7 @@ static GOptionEntry entries[] = { "runtime-file", 0, 0, G_OPTION_ARG_STRING, &runtime_file, "Runtime file to use", NULL }, { "sign-key", 0, 0, G_OPTION_ARG_STRING, &sign_key, "Key ID to use for gpg[2] signatures", NULL}, { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &remaining_args, NULL, NULL }, - { 0,0,0,0,0,0,0 } + { 0,0,0, G_OPTION_ARG_NONE, 0,0,0 }, }; int @@ -700,11 +705,11 @@ main (int argc, char *argv[]) /* Read information from .desktop file */ GKeyFile *kf = g_key_file_new (); - if (!g_key_file_load_from_file (kf, desktop_file, G_KEY_FILE_KEEP_TRANSLATIONS | G_KEY_FILE_KEEP_COMMENTS, NULL)) + GKeyFileFlags flags = (GKeyFileFlags) (G_KEY_FILE_KEEP_TRANSLATIONS | G_KEY_FILE_KEEP_COMMENTS); + if (!g_key_file_load_from_file (kf, desktop_file, flags, NULL)) die(".desktop file cannot be parsed"); if (!get_desktop_entry(kf, "Categories")) die(".desktop file is missing a Categories= key"); - if(verbose){ fprintf (stderr,"Name: %s\n", get_desktop_entry(kf, "Name")); fprintf (stderr,"Icon: %s\n", get_desktop_entry(kf, "Icon")); @@ -866,7 +871,7 @@ main (int argc, char *argv[]) * so we need a patched one. https://github.com/plougher/squashfs-tools/pull/13 * should hopefully change that. */ - fprintf (stderr, "Generating squashfs...\n"); + fprintf (stderr, "Loading runtime file...\n"); size_t size = 0; char* data = NULL; // TODO: just write to the output file directly, we don't really need a memory buffer @@ -882,6 +887,7 @@ main (int argc, char *argv[]) if (verbose) printf("Size of the embedded runtime: %d bytes\n", size); + fprintf (stderr, "Generating squashfs...\n"); int result = sfs_mksquashfs(source, destination, size); if(result != 0) die("sfs_mksquashfs error"); @@ -1074,9 +1080,9 @@ main (int argc, char *argv[]) GSubprocessFlags flags = G_SUBPROCESS_FLAGS_NONE; if (!verbose) { - flags = G_SUBPROCESS_FLAGS_STDERR_SILENCE | G_SUBPROCESS_FLAGS_STDOUT_SILENCE; + flags = static_cast(G_SUBPROCESS_FLAGS_STDERR_SILENCE | G_SUBPROCESS_FLAGS_STDOUT_SILENCE); } - + GSubprocess* proc = g_subprocess_newv(zsyncmake_command, flags, &error); if (proc == NULL) { diff --git a/src/appimagetool_sign.c b/src/appimagetool_sign.c index 0a3e1cd..ad6fdf9 100644 --- a/src/appimagetool_sign.c +++ b/src/appimagetool_sign.c @@ -1,3 +1,7 @@ +#ifdef __cplusplus +extern "C" { +#endif + #include #include #include @@ -471,3 +475,6 @@ bool init_gcrypt() { return true; } +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/src/appimagetool_sign.h b/src/appimagetool_sign.h index 59e860b..829812e 100644 --- a/src/appimagetool_sign.h +++ b/src/appimagetool_sign.h @@ -1,4 +1,10 @@ -#pragma once +#ifndef APPIMAGETOOL_SIGN_H +#define APPIMAGETOOL_SIGN_H + +#ifdef __cplusplus +extern "C" { +#endif + bool sign_appimage(char* appimage_filename, char* key_id, bool verbose); @@ -25,3 +31,9 @@ void gpg_release_resources(); * @return true on success, false otherwise */ bool init_gcrypt(); + +#ifdef __cplusplus +} +#endif + +#endif /* APPIMAGETOOL_SIGN_H */ \ No newline at end of file diff --git a/src/elf.c b/src/elf.c index aed9e89..75cbba1 100644 --- a/src/elf.c +++ b/src/elf.c @@ -1,3 +1,7 @@ +#ifdef __cplusplus +extern "C" { +#endif + #include #include #include @@ -12,7 +16,6 @@ #include "light_elf.h" #include "light_byteswap.h" - typedef Elf32_Nhdr Elf_Nhdr; static char *fname; @@ -125,13 +128,13 @@ bool appimage_get_elf_section_offset_and_length(const char* fname, const char* s int fd = open(fname, O_RDONLY); size_t map_size = (size_t) lseek(fd, 0, SEEK_END); - data = mmap(NULL, map_size, PROT_READ, MAP_SHARED, fd, 0); + data = (uint8_t*) mmap(NULL, map_size, PROT_READ, MAP_SHARED, fd, 0); close(fd); // this trick works as both 32 and 64 bit ELF files start with the e_ident[EI_NINDENT] section - unsigned char class = data[EI_CLASS]; + uint8_t elf_class = data[EI_CLASS]; - if (class == ELFCLASS32) { + if (elf_class == ELFCLASS32) { Elf32_Ehdr* elf; Elf32_Shdr* shdr; @@ -145,7 +148,7 @@ bool appimage_get_elf_section_offset_and_length(const char* fname, const char* s *length = shdr[i].sh_size; } } - } else if (class == ELFCLASS64) { + } else if (elf_class == ELFCLASS64) { Elf64_Ehdr* elf; Elf64_Shdr* shdr; @@ -177,7 +180,7 @@ char* read_file_offset_length(const char* fname, unsigned long offset, unsigned fseek(f, offset, SEEK_SET); - char* buffer = calloc(length + 1, sizeof(char)); + char* buffer = (char*) calloc(length + 1, sizeof(char)); fread(buffer, length, sizeof(char), f); fclose(f); @@ -214,3 +217,7 @@ int appimage_print_binary(const char* fname, unsigned long offset, unsigned long return 0; } + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/src/elf.h b/src/elf.h new file mode 100644 index 0000000..c5e27a1 --- /dev/null +++ b/src/elf.h @@ -0,0 +1,14 @@ +#ifndef ELF_H +#define ELF_H + +#include + +bool appimage_get_elf_section_offset_and_length(const char* fname, const char* section_name, unsigned long* offset, unsigned long* length); + +char* read_file_offset_length(const char* fname, unsigned long offset, unsigned long length); + +int appimage_print_hex(const char* fname, unsigned long offset, unsigned long length); + +int appimage_print_binary(const char* fname, unsigned long offset, unsigned long length); + +#endif /* ELF_H */ \ No newline at end of file diff --git a/src/util.h b/src/util.h index 953e9da..03799b1 100644 --- a/src/util.h +++ b/src/util.h @@ -1,3 +1,11 @@ +#ifdef __cplusplus +extern "C" { +#endif + char* appimage_hexlify(const char* bytes, const size_t numBytes); bool appimage_get_elf_section_offset_and_length(const char* fname, const char* section_name, unsigned long* offset, unsigned long* length); bool appimage_type2_digest_md5(const char* path, char* digest); + +#ifdef __cplusplus +} +#endif \ No newline at end of file From f385247e890e36647849453345355eb00e6d24a4 Mon Sep 17 00:00:00 2001 From: probonopd Date: Sun, 9 Jul 2023 11:14:22 +0000 Subject: [PATCH 11/12] More c++ --- src/appimagetool.cpp | 309 +++++++++++++++++++++++-------------------- 1 file changed, 162 insertions(+), 147 deletions(-) diff --git a/src/appimagetool.cpp b/src/appimagetool.cpp index 6296dff..bb43ffa 100644 --- a/src/appimagetool.cpp +++ b/src/appimagetool.cpp @@ -32,6 +32,7 @@ #include +// TODO: Remove glib dependency in favor of C++ #include #include #include @@ -77,15 +78,15 @@ typedef enum { static gchar const APPIMAGEIGNORE[] = ".appimageignore"; static char _exclude_file_desc[256]; -static gboolean list = FALSE; -static gboolean verbose = FALSE; -static gboolean showVersionOnly = FALSE; -static gboolean sign = FALSE; -static gboolean no_appstream = FALSE; +static bool list = false; +static bool verbose = false; +static bool showVersionOnly = false; +static bool sign = false; +static bool no_appstream = false; gchar **remaining_args = NULL; gchar *updateinformation = NULL; -static gboolean guess_update_information = FALSE; -gchar *sqfs_comp = NULL; +static bool guess_update_information = false; +std::string sqfs_comp = "zstd"; gchar **sqfs_opts = NULL; gchar *exclude_file = NULL; gchar *runtime_file = NULL; @@ -95,7 +96,7 @@ gchar *pathToMksquashfs = NULL; // ##################################################################### static void die(const char *msg) { - fprintf(stderr, "%s\n", msg); + std::cerr << msg << std::endl; exit(1); } @@ -118,17 +119,16 @@ int sfs_mksquashfs(char *source, char *destination, int offset) { int retcode = WEXITSTATUS(status); if (retcode) { - fprintf(stderr, "mksquashfs (pid %d) exited with code %d\n", pid, retcode); + std::cerr << "mksquashfs (pid " << pid << ") exited with code " << retcode << std::endl; return(-1); } return 0; } else { // we are the child - gchar* offset_string; - offset_string = g_strdup_printf("%i", offset); + std::string offset_string = std::to_string(offset); - guint sqfs_opts_len = sqfs_opts ? g_strv_length(sqfs_opts) : 0; + unsigned int sqfs_opts_len = sqfs_opts ? g_strv_length(sqfs_opts) : 0; int max_num_args = sqfs_opts_len + 22; std::vector args; @@ -145,10 +145,6 @@ int sfs_mksquashfs(char *source, char *destination, int offset) { int i = args.size(); - if (sqfs_comp == NULL) { - sqfs_comp = "zstd"; - } - args.push_back("-comp"); args.push_back(sqfs_comp); @@ -156,14 +152,14 @@ int sfs_mksquashfs(char *source, char *destination, int offset) { args.push_back("-noappend"); // compression-specific optimization - if (strcmp(sqfs_comp, "xz") == 0) { + if (sqfs_comp == "xz") { // https://jonathancarter.org/2015/04/06/squashfs-performance-testing/ says: // improved performance by using a 16384 block size with a sacrifice of around 3% more squashfs image space args.push_back("-Xdict-size"); args.push_back("100%"); args.push_back("-b"); args.push_back("16384"); - } else if (strcmp(sqfs_comp, "zstd") == 0) { + } else if (sqfs_comp == "zstd") { /* * > Build with 1MiB block size * > Using a bigger block size than mksquashfs's default improves read speed and can produce smaller AppImages as well @@ -200,7 +196,7 @@ int sfs_mksquashfs(char *source, char *destination, int offset) { args.push_back("-mkfs-time"); args.push_back("0"); - for (guint sqfs_opts_idx = 0; sqfs_opts_idx < sqfs_opts_len; ++sqfs_opts_idx) { + for (unsigned int sqfs_opts_idx = 0; sqfs_opts_idx < sqfs_opts_len; ++sqfs_opts_idx) { args[i++] = sqfs_opts[sqfs_opts_idx]; } // Build the command string @@ -322,25 +318,25 @@ void extract_arch_from_e_machine_field(int16_t e_machine, const gchar* sourcenam if (e_machine == 3) { archs[fARCH_i686] = 1; if(verbose) - fprintf(stderr, "%s used for determining architecture %s\n", sourcename, archToName(fARCH_i686)); + std::cout << sourcename << " used for determining architecture " << archToName(fARCH_i686) << std::endl; } if (e_machine == 62) { archs[fARCH_x86_64] = 1; if(verbose) - fprintf(stderr, "%s used for determining architecture %s\n", sourcename, archToName(fARCH_x86_64)); + std::cout << sourcename << " used for determining architecture " << archToName(fARCH_x86_64) << std::endl; } if (e_machine == 40) { archs[fARCH_armhf] = 1; if(verbose) - fprintf(stderr, "%s used for determining architecture %s\n", sourcename, archToName(fARCH_armhf)); + std::cout << sourcename << " used for determining architecture " << archToName(fARCH_armhf) << std::endl; } if (e_machine == 183) { archs[fARCH_aarch64] = 1; if(verbose) - fprintf(stderr, "%s used for determining architecture %s\n", sourcename, archToName(fARCH_aarch64)); + std::cout << sourcename << " used for determining architecture " << archToName(fARCH_aarch64) << std::endl; } } @@ -361,19 +357,19 @@ void extract_arch_from_text(gchar *archname, const gchar* sourcename, bool* arch ) { archs[fARCH_i686] = 1; if (verbose) - fprintf(stderr, "%s used for determining architecture i386\n", sourcename); + std::cout << sourcename << " used for determining architecture i386" << std::endl; } else if (g_ascii_strncasecmp("x86_64", archname, 20) == 0) { archs[fARCH_x86_64] = 1; if (verbose) - fprintf(stderr, "%s used for determining architecture x86_64\n", sourcename); + std::cout << sourcename << " used for determining architecture x86_64" << std::endl; } else if (g_ascii_strncasecmp("arm", archname, 20) == 0) { archs[fARCH_armhf] = 1; if (verbose) - fprintf(stderr, "%s used for determining architecture ARM\n", sourcename); + std::cout << sourcename << " used for determining architecture ARM" << std::endl; } else if (g_ascii_strncasecmp("arm_aarch64", archname, 20) == 0) { archs[fARCH_aarch64] = 1; if (verbose) - fprintf(stderr, "%s used for determining architecture ARM aarch64\n", sourcename); + std::cerr << sourcename << " used for determining architecture ARM aarch64\n"; } } } @@ -443,7 +439,7 @@ gchar* find_first_matching_file_nonrecursive(const gchar *real_path, const gchar gchar* get_desktop_entry(GKeyFile *kf, char *key) { gchar *value = g_key_file_get_string (kf, "Desktop Entry", key, NULL); if (! value){ - fprintf(stderr, "%s entry not found in desktop file\n", key); + std::cout << key << " entry not found in desktop file" << std::endl; } return value; } @@ -535,26 +531,47 @@ main (int argc, char *argv[]) * We cannot use g_environ_getenv (g_get_environ() since it is too new for CentOS 6 */ // char* travis_commit; // travis_commit = getenv("TRAVIS_COMMIT"); - char* travis_repo_slug; - travis_repo_slug = getenv("TRAVIS_REPO_SLUG"); - char* travis_tag; - travis_tag = getenv("TRAVIS_TAG"); - char* travis_pull_request; - travis_pull_request = getenv("TRAVIS_PULL_REQUEST"); + std::string travis_repo_slug; + char* travis_repo_slug_cstr = getenv("TRAVIS_REPO_SLUG"); + if (travis_repo_slug_cstr != NULL) { + travis_repo_slug = std::string(travis_repo_slug_cstr); + } + std::string travis_tag; + char* travis_tag_cstr = getenv("TRAVIS_TAG"); + if (travis_tag_cstr != NULL) { + travis_tag = std::string(travis_tag_cstr); + } + std::string travis_pull_request; + char* travis_pull_request_cstr = getenv("TRAVIS_PULL_REQUEST"); + if (travis_pull_request_cstr != NULL) { + travis_pull_request = std::string(travis_pull_request_cstr); + } /* https://github.com/probonopd/uploadtool */ - char* github_token; - github_token = getenv("GITHUB_TOKEN"); + std::string github_token; + char* github_token_cstr = getenv("GITHUB_TOKEN"); + if (github_token_cstr != NULL) { + github_token = std::string(github_token_cstr); + } /* Parse GitLab CI environment variables. * https://docs.gitlab.com/ee/ci/variables/#predefined-variables-environment-variables * echo "${CI_PROJECT_URL}/-/jobs/artifacts/${CI_COMMIT_REF_NAME}/raw/QtQuickApp-x86_64.AppImage?job=${CI_JOB_NAME}" */ - char* CI_PROJECT_URL; - CI_PROJECT_URL = getenv("CI_PROJECT_URL"); - char* CI_COMMIT_REF_NAME; - CI_COMMIT_REF_NAME = getenv("CI_COMMIT_REF_NAME"); // The branch or tag name for which project is built - char* CI_JOB_NAME; - CI_JOB_NAME = getenv("CI_JOB_NAME"); // The name of the job as defined in .gitlab-ci.yml + std::string CI_PROJECT_URL; + char* CI_PROJECT_URL_cstr = getenv("CI_PROJECT_URL"); + if (CI_PROJECT_URL_cstr != NULL) { + CI_PROJECT_URL = std::string(CI_PROJECT_URL_cstr); + } + std::string CI_COMMIT_REF_NAME; + char* CI_COMMIT_REF_NAME_cstr = getenv("CI_COMMIT_REF_NAME"); + if (CI_COMMIT_REF_NAME_cstr != NULL) { + CI_COMMIT_REF_NAME = std::string(CI_COMMIT_REF_NAME_cstr); + } + std::string CI_JOB_NAME; + char* CI_JOB_NAME_cstr = getenv("CI_JOB_NAME"); + if (CI_JOB_NAME_cstr != NULL) { + CI_JOB_NAME = std::string(CI_JOB_NAME_cstr); + } /* Parse OWD environment variable. * If it is available then cd there. It is the original CWD prior to running AppRun */ @@ -564,7 +581,7 @@ main (int argc, char *argv[]) int ret; ret = chdir(owd_env); if (ret != 0){ - fprintf(stderr, "Could not cd into %s\n", owd_env); + std::cerr << "Could not cd into " << owd_env << std::endl; exit(1); } } @@ -580,15 +597,11 @@ main (int argc, char *argv[]) // g_option_context_add_group (context, gtk_get_option_group (TRUE)); if (!g_option_context_parse (context, &argc, &argv, &error)) { - fprintf(stderr, "Option parsing failed: %s\n", error->message); + std::cerr << "Option parsing failed: " << error->message << std::endl; exit(1); } - fprintf( - stderr, - "appimagetool, %s (git version %s), build %s built on %s\n", - RELEASE_NAME, GIT_VERSION, BUILD_NUMBER, BUILD_DATE - ); + std::cerr << "appimagetool, " << RELEASE_NAME << " (git version " << GIT_VERSION << "), build " << BUILD_NUMBER << " built on " << BUILD_DATE << std::endl; // always show version, but exit immediately if only the version number was requested if (showVersionOnly) @@ -664,17 +677,17 @@ main (int argc, char *argv[]) // g_spawn_command_line_sync might have set error already, in that case we don't want to overwrite if (error != NULL || !g_spawn_check_exit_status(exit_status, &error)) { if (error == NULL) { - g_printerr("Failed to run 'git rev-parse --short HEAD, but failed to interpret GLib error state: %d\n", exit_status); + std::cerr << "Failed to run 'git rev-parse --short HEAD, but failed to interpret GLib error state: " << exit_status << std::endl; } else { - g_printerr("Failed to run 'git rev-parse --short HEAD: %s (code %d)\n", error->message, error->code); + std::cerr << "Failed to run 'git rev-parse --short HEAD: " << error->message << " (code " << error->code << ")" << std::endl; } } else { version_env = g_strstrip(out); if (version_env != NULL) { - g_printerr("NOTE: Using the output of 'git rev-parse --short HEAD' as the version:\n"); - g_printerr(" %s\n", version_env); - g_printerr(" Please set the $VERSION environment variable if this is not intended\n"); + std::cerr << "NOTE: Using the output of 'git rev-parse --short HEAD' as the version:" << std::endl; + std::cerr << " " << version_env << std::endl; + std::cerr << " Please set the $VERSION environment variable if this is not intended" << std::endl; } } } @@ -693,12 +706,12 @@ main (int argc, char *argv[]) die("Desktop file not found, aborting"); } if(verbose) - fprintf (stdout, "Desktop file: %s\n", desktop_file); + std::cout << "Desktop file: " << desktop_file << std::endl; if(g_find_program_in_path ("desktop-file-validate")) { if(validate_desktop_file(desktop_file) != 0){ - fprintf(stderr, "ERROR: Desktop file contains errors. Please fix them. Please see\n"); - fprintf(stderr, " https://standards.freedesktop.org/desktop-entry-spec/1.0/n"); + std::cerr << "ERROR: Desktop file contains errors. Please fix them. Please see" << std::endl; + std::cerr << " https://standards.freedesktop.org/desktop-entry-spec/1.0/n" << std::endl; die(" for more information."); } } @@ -711,12 +724,12 @@ main (int argc, char *argv[]) if (!get_desktop_entry(kf, "Categories")) die(".desktop file is missing a Categories= key"); if(verbose){ - fprintf (stderr,"Name: %s\n", get_desktop_entry(kf, "Name")); - fprintf (stderr,"Icon: %s\n", get_desktop_entry(kf, "Icon")); - fprintf (stderr,"Exec: %s\n", get_desktop_entry(kf, "Exec")); - fprintf (stderr,"Comment: %s\n", get_desktop_entry(kf, "Comment")); - fprintf (stderr,"Type: %s\n", get_desktop_entry(kf, "Type")); - fprintf (stderr,"Categories: %s\n", get_desktop_entry(kf, "Categories")); + std::cerr << "Name: " << get_desktop_entry(kf, "Name") << std::endl; + std::cerr << "Icon: " << get_desktop_entry(kf, "Icon") << std::endl; + std::cerr << "Exec: " << get_desktop_entry(kf, "Exec") << std::endl; + std::cerr << "Comment: " << get_desktop_entry(kf, "Comment") << std::endl; + std::cerr << "Type: " << get_desktop_entry(kf, "Type") << std::endl; + std::cerr << "Categories: " << get_desktop_entry(kf, "Categories") << std::endl; } /* Determine the architecture */ @@ -729,21 +742,21 @@ main (int argc, char *argv[]) int countArchs = count_archs(archs); if (countArchs != 1) { if (countArchs < 1) - fprintf(stderr, "Unable to guess the architecture of the AppDir source directory \"%s\"\n", remaining_args[0]); + std::cerr << "Unable to guess the architecture of the AppDir source directory \"" << remaining_args[0] << "\"" << std::endl; else - fprintf(stderr, "More than one architectures were found of the AppDir source directory \"%s\"\n", remaining_args[0]); - fprintf(stderr, "A valid architecture with the ARCH environmental variable should be provided\ne.g. ARCH=x86_64 %s", argv[0]), + std::cerr << "More than one architectures were found of the AppDir source directory \"" << remaining_args[0] << "\"" << std::endl; + std::cerr << "A valid architecture with the ARCH environmental variable should be provided\ne.g. ARCH=x86_64 " << argv[0] << std::endl; die(" ..."); } } gchar* arch = getArchName(archs); - fprintf(stderr, "Using architecture %s\n", arch); + std::cerr << "Using architecture " << arch << std::endl; char app_name_for_filename[PATH_MAX]; { const char* const env_app_name = getenv("APPIMAGETOOL_APP_NAME"); if (env_app_name != NULL) { - fprintf(stderr, "Using user-specified app name: %s\n", env_app_name); + std::cerr << "Using user-specified app name: " << env_app_name << std::endl; strncpy(app_name_for_filename, env_app_name, PATH_MAX); } else { const gchar* const desktop_file_app_name = get_desktop_entry(kf, "Name"); @@ -751,7 +764,7 @@ main (int argc, char *argv[]) replacestr(app_name_for_filename, " ", "_"); if (verbose) { - fprintf(stderr, "Using app name extracted from desktop file: %s\n", app_name_for_filename); + std::cerr << "Using app name extracted from desktop file: " << app_name_for_filename << std::endl; } } } @@ -779,12 +792,12 @@ main (int argc, char *argv[]) g_key_file_set_string(kf, G_KEY_FILE_DESKTOP_GROUP, "X-AppImage-Version", version_env); if (!g_key_file_save_to_file(kf, desktop_file, NULL)) { - fprintf(stderr, "Could not save modified desktop file\n"); + std::cerr << "Could not save modified desktop file" << std::endl; exit(1); } } - fprintf (stdout, "%s should be packaged as %s\n", source, destination); + std::cout << source << " should be packaged as " << destination << std::endl; /* Check if the Icon file is how it is expected */ gchar* icon_name = get_desktop_entry(kf, "Icon"); gchar* icon_file_path = NULL; @@ -801,11 +814,11 @@ main (int argc, char *argv[]) } else if(g_file_test(icon_file_xpm, G_FILE_TEST_IS_REGULAR)) { icon_file_path = icon_file_xpm; } else { - fprintf (stderr, "%s{.png,.svg,.xpm} defined in desktop file but not found\n", icon_name); - fprintf (stderr, "For example, you could put a 256x256 pixel png into\n"); + std::cerr << icon_name << "{.png,.svg,.xpm} defined in desktop file but not found" << std::endl; + std::cerr << "For example, you could put a 256x256 pixel png into" << std::endl; gchar *icon_name_with_png = g_strconcat(icon_name, ".png", NULL); gchar *example_path = g_build_filename(source, "/", icon_name_with_png, NULL); - fprintf (stderr, "%s\n", example_path); + std::cerr << example_path << std::endl; exit(1); } @@ -813,11 +826,11 @@ main (int argc, char *argv[]) gchar *diricon_path = g_build_filename(source, ".DirIcon", NULL); if (! g_file_test(diricon_path, G_FILE_TEST_EXISTS)){ - fprintf (stderr, "Deleting pre-existing .DirIcon\n"); + std::cerr << "Deleting pre-existing .DirIcon" << std::endl; g_unlink(diricon_path); } if (! g_file_test(diricon_path, G_FILE_TEST_IS_REGULAR)){ - fprintf (stderr, "Creating .DirIcon symlink based on information from desktop file\n"); + std::cerr << "Creating .DirIcon symlink based on information from desktop file" << std::endl; int res = symlink(basename(icon_file_path), diricon_path); if(res) die("Could not symlink .DirIcon"); @@ -830,12 +843,12 @@ main (int argc, char *argv[]) replacestr(application_id, ".desktop", ".appdata.xml"); gchar *appdata_path = g_build_filename(source, "/usr/share/metainfo/", application_id, NULL); if (! g_file_test(appdata_path, G_FILE_TEST_IS_REGULAR)){ - fprintf (stderr, "WARNING: AppStream upstream metadata is missing, please consider creating it\n"); - fprintf (stderr, " in usr/share/metainfo/%s\n", application_id); - fprintf (stderr, " Please see https://www.freedesktop.org/software/appstream/docs/chap-Quickstart.html#sect-Quickstart-DesktopApps\n"); - fprintf (stderr, " for more information or use the generator at http://output.jsbin.com/qoqukof.\n"); - } else { - fprintf (stderr, "AppStream upstream metadata found in usr/share/metainfo/%s\n", application_id); + std::cerr << "WARNING: AppStream upstream metadata is missing, please consider creating it" << std::endl; + std::cerr << " in usr/share/metainfo/" << application_id << std::endl; + std::cerr << " Please see https://www.freedesktop.org/software/appstream/docs/chap-Quickstart.html#sect-Quickstart-DesktopApps" << std::endl; + std::cerr << " for more information or use the generator at http://output.jsbin.com/qoqukof." << std::endl; + } else { + std::cerr << "AppStream upstream metadata found in usr/share/metainfo/" << application_id << std::endl; /* Use ximion's appstreamcli to make sure that desktop file and appdata match together */ if(g_find_program_in_path ("appstreamcli")) { char *args[] = { @@ -871,7 +884,7 @@ main (int argc, char *argv[]) * so we need a patched one. https://github.com/plougher/squashfs-tools/pull/13 * should hopefully change that. */ - fprintf (stderr, "Loading runtime file...\n"); + std::cerr << "Loading runtime file..." << std::endl; size_t size = 0; char* data = NULL; // TODO: just write to the output file directly, we don't really need a memory buffer @@ -885,14 +898,14 @@ main (int argc, char *argv[]) } } if (verbose) - printf("Size of the embedded runtime: %d bytes\n", size); - - fprintf (stderr, "Generating squashfs...\n"); + std::cout << "Size of the embedded runtime: " << size << " bytes" << std::endl; + + std::cerr << "Generating squashfs..." << std::endl; int result = sfs_mksquashfs(source, destination, size); if(result != 0) die("sfs_mksquashfs error"); - fprintf (stderr, "Embedding ELF...\n"); + std::cerr << "Embedding ELF..." << std::endl; FILE *fpdst = fopen(destination, "rb+"); if (fpdst == NULL) { die("Not able to open the AppImage for writing, aborting"); @@ -904,19 +917,21 @@ main (int argc, char *argv[]) // TODO: avoid memory buffer (see above) free(data); - fprintf (stderr, "Marking the AppImage as executable...\n"); + std::cerr << "Marking the AppImage as executable..." << std::endl; if (chmod (destination, 0755) < 0) { - printf("Could not set executable bit, aborting\n"); + std::cout << "Could not set executable bit, aborting" << std::endl; exit(1); } + + std::string update_info; /* If the user has not provided update information but we know this is a Travis CI build, * then fill in update information based on TRAVIS_REPO_SLUG */ if(guess_update_information){ - if(travis_repo_slug){ - if(!github_token) { + if (!travis_repo_slug.empty()) { + if(github_token.empty()) { printf("Will not guess update information since $GITHUB_TOKEN is missing,\n"); - if(0 != strcmp(travis_pull_request, "false")){ + if(travis_pull_request != "false"){ printf("please set it in the Travis CI Repository Settings for this project.\n"); printf("You can get one from https://github.com/settings/tokens\n"); } else { @@ -926,52 +941,58 @@ main (int argc, char *argv[]) gchar *zsyncmake_path = g_find_program_in_path ("zsyncmake"); if(zsyncmake_path){ char buf[1024]; - gchar **parts = g_strsplit (travis_repo_slug, "/", 2); + std::string delimiter = "/"; + std::string owner = travis_repo_slug.substr(0, travis_repo_slug.find(delimiter)); + std::string repo = travis_repo_slug.substr(travis_repo_slug.find(delimiter) + 1); /* https://github.com/AppImage/AppImageSpec/blob/master/draft.md#github-releases * gh-releases-zsync|probono|AppImages|latest|Subsurface*-x86_64.AppImage.zsync */ gchar *channel = "continuous"; - if(travis_tag != NULL){ - if((strcmp(travis_tag, "") != 0) && (strcmp(travis_tag, "continuous") != 0)) { - channel = "latest"; - } + if ((!travis_tag.empty()) && (travis_tag != "continuous")) { + channel = "latest"; } - sprintf(buf, "gh-releases-zsync|%s|%s|%s|%s*-%s.AppImage.zsync", parts[0], parts[1], channel, app_name_for_filename, arch); + std::ostringstream oss; + oss << "gh-releases-zsync|" << owner << "|" << repo << "|" << channel << "|" << app_name_for_filename << "*-" << arch << ".AppImage.zsync"; + update_info = oss.str(); updateinformation = buf; - printf("Guessing update information based on $TRAVIS_TAG=%s and $TRAVIS_REPO_SLUG=%s\n", travis_tag, travis_repo_slug); - printf("%s\n", updateinformation); + std::cout << "Guessing update information based on $TRAVIS_TAG=" << travis_tag << " and $TRAVIS_REPO_SLUG=" << travis_repo_slug << std::endl; + std::cout << updateinformation << std::endl; } else { printf("Will not guess update information since zsyncmake is missing\n"); } } - } else if(CI_COMMIT_REF_NAME){ + } else if(! CI_COMMIT_REF_NAME.empty()){ // ${CI_PROJECT_URL}/-/jobs/artifacts/${CI_COMMIT_REF_NAME}/raw/QtQuickApp-x86_64.AppImage?job=${CI_JOB_NAME} gchar *zsyncmake_path = g_find_program_in_path ("zsyncmake"); - if(zsyncmake_path){ - char buf[1024]; - sprintf(buf, "zsync|%s/-/jobs/artifacts/%s/raw/%s-%s.AppImage.zsync?job=%s", CI_PROJECT_URL, CI_COMMIT_REF_NAME, app_name_for_filename, arch, CI_JOB_NAME); - updateinformation = buf; - printf("Guessing update information based on $CI_COMMIT_REF_NAME=%s and $CI_JOB_NAME=%s\n", CI_COMMIT_REF_NAME, CI_JOB_NAME); - printf("%s\n", updateinformation); + if (zsyncmake_path) { + update_info = "zsync|" + CI_PROJECT_URL + "/-/jobs/artifacts/" + CI_COMMIT_REF_NAME + "/raw/" + app_name_for_filename + "-" + arch + ".AppImage.zsync?job=" + CI_JOB_NAME; + std::cout << "Guessing update information based on $CI_COMMIT_REF_NAME=" << CI_COMMIT_REF_NAME << " and $CI_JOB_NAME=" << CI_JOB_NAME << std::endl; + std::cout << update_info << std::endl; } else { - printf("Will not guess update information since zsyncmake is missing\n"); + std::cout << "Will not guess update information since zsyncmake is missing\n"; } } } /* If updateinformation was provided, then we check and embed it */ - if(updateinformation != NULL){ - if(!g_str_has_prefix(updateinformation,"zsync|")) - if(!g_str_has_prefix(updateinformation,"gh-releases-zsync|")) - if(!g_str_has_prefix(updateinformation,"pling-v1-zsync|")) - die("The provided updateinformation is not in a recognized format"); + if(!update_info.empty()){ + if(update_info.find("zsync|") != 0) + if(update_info.find("gh-releases-zsync|") != 0) + if(update_info.find("pling-v1-zsync|") != 0) + die("The provided update information is not in a recognized format"); - gchar **ui_type = g_strsplit_set(updateinformation, "|", -1); + std::string ui_type; + for (int i = 0; i < update_info.length(); i++) { + if (update_info[i] == '|') { + break; + } + ui_type += update_info[i]; + } - if(verbose) - printf("updateinformation type: %s\n", ui_type[0]); + if(verbose) { + std::cout << "updateinformation type: " << ui_type << std::endl; + } /* TODO: Further checking of the updateinformation */ - - + unsigned long ui_offset = 0; unsigned long ui_length = 0; @@ -982,8 +1003,8 @@ main (int argc, char *argv[]) } if(verbose) { - printf("ui_offset: %lu\n", ui_offset); - printf("ui_length: %lu\n", ui_length); + std::cout << "ui_offset: " << ui_offset << std::endl; + std::cout << "ui_length: " << ui_length << std::endl; } if(ui_offset == 0) { die("Could not determine offset for updateinformation"); @@ -1004,7 +1025,7 @@ main (int argc, char *argv[]) // calculate and embed MD5 digest { - fprintf(stderr, "Embedding MD5 digest\n"); + std::cerr << "Embedding MD5 digest" << std::endl; unsigned long digest_md5_offset = 0; unsigned long digest_md5_length = 0; @@ -1018,12 +1039,8 @@ main (int argc, char *argv[]) static const unsigned long section_size = 16; if (digest_md5_length < section_size) { - fprintf( - stderr, - ".digest_md5 section in runtime's ELF header is too small" - "(found %lu bytes, minimum required: %lu bytes)\n", - digest_md5_length, section_size - ); + std::cerr << ".digest_md5 section in runtime's ELF header is too small" + << "(found " << digest_md5_length << " bytes, minimum required: " << section_size << " bytes)" << std::endl; exit(1); } @@ -1062,19 +1079,19 @@ main (int argc, char *argv[]) if (updateinformation != NULL) { gchar* zsyncmake_path = g_find_program_in_path("zsyncmake"); if (!zsyncmake_path) { - fprintf(stderr, "zsyncmake is not installed/bundled, skipping\n"); + std::cerr << "zsyncmake is not installed/bundled, skipping\n"; } else { - fprintf(stderr, "zsyncmake is available and updateinformation is provided, " - "hence generating zsync file\n"); + std::cerr << "zsyncmake is available and updateinformation is provided, " + "hence generating zsync file\n"; const gchar* const zsyncmake_command[] = {zsyncmake_path, destination, "-u", basename(destination), NULL}; if (verbose) { - fprintf(stderr, "Running zsyncmake process: "); + std::cerr << "Running zsyncmake process: "; for (gint j = 0; j < (sizeof(zsyncmake_command) / sizeof(char*) - 1); ++j) { - fprintf(stderr, "'%s' ", zsyncmake_command[j]); + std::cerr << "'" << zsyncmake_command[j] << "' "; } - fprintf(stderr, "\n"); + std::cerr << "\n"; } GSubprocessFlags flags = G_SUBPROCESS_FLAGS_NONE; @@ -1086,12 +1103,12 @@ main (int argc, char *argv[]) GSubprocess* proc = g_subprocess_newv(zsyncmake_command, flags, &error); if (proc == NULL) { - fprintf(stderr, "ERROR: failed to create zsyncmake process: %s\n", error->message); + std::cerr << "ERROR: failed to create zsyncmake process: " << error->message << "\n"; exit(1); } if (!g_subprocess_wait_check(proc, NULL, &error)) { - fprintf(stderr, "ERROR: zsyncmake returned abnormal exit code: %s\n", error->message); + std::cerr << "ERROR: zsyncmake returned abnormal exit code: " << error->message << "\n"; g_object_unref(proc); exit(1); } @@ -1100,19 +1117,17 @@ main (int argc, char *argv[]) } } - fprintf(stderr, "Success\n\n"); - fprintf(stderr, "Please consider submitting your AppImage to AppImageHub, the crowd-sourced\n"); - fprintf(stderr, "central directory of available AppImages, by opening a pull request\n"); - fprintf(stderr, "at https://github.com/AppImage/appimage.github.io\n"); - - return 0; - } else if (g_file_test(remaining_args[0], G_FILE_TEST_IS_REGULAR)) { - /* If the first argument is a regular file, then we assume that we should unpack it */ - fprintf(stdout, "%s is a file, assuming it is an AppImage and should be unpacked\n", remaining_args[0]); - die("To be implemented"); - return 1; + std::string absolute_path = std::string(realpath(destination, NULL)); + if (!absolute_path.empty()) { + std::cerr << "Success: " << absolute_path << std::endl; + std::cerr << "Please consider submitting your AppImage to AppImageHub, the crowd-sourced\n"; + std::cerr << "central directory of available AppImages, by opening a pull request\n"; + std::cerr << "at https://github.com/AppImage/appimage.github.io\n"; + return 0; + } + } else { - fprintf(stderr, "Error: no such file or directory: %s\n", remaining_args[0]); + std::cerr << "Error: no such file or directory: " << remaining_args[0] << "\n"; return 1; } From 21ca1c00b1f7d3b8090a0efacb0d681446a14e8f Mon Sep 17 00:00:00 2001 From: probonopd Date: Sun, 9 Jul 2023 11:34:12 +0000 Subject: [PATCH 12/12] More c++, fewer gchar* --- src/appimagetool.cpp | 54 +++++++++++++++++------------- src/appimagetool_fetch_runtime.cpp | 13 +++++-- src/appimagetool_fetch_runtime.h | 15 +++++---- 3 files changed, 48 insertions(+), 34 deletions(-) diff --git a/src/appimagetool.cpp b/src/appimagetool.cpp index bb43ffa..0de29fa 100644 --- a/src/appimagetool.cpp +++ b/src/appimagetool.cpp @@ -67,6 +67,7 @@ #include #include #include +#include typedef enum { fARCH_i686, @@ -288,7 +289,7 @@ int count_archs(bool* archs) { return countArchs; } -gchar* archToName(fARCH arch) { +std::string archToName(fARCH arch) { switch (arch) { case fARCH_aarch64: return "aarch64"; @@ -301,7 +302,7 @@ gchar* archToName(fARCH arch) { } } -gchar* getArchName(bool* archs) { +std::string getArchName(bool* archs) { if (archs[fARCH_i686]) return archToName(fARCH_i686); else if (archs[fARCH_x86_64]) @@ -314,7 +315,7 @@ gchar* getArchName(bool* archs) { return "all"; } -void extract_arch_from_e_machine_field(int16_t e_machine, const gchar* sourcename, bool* archs) { +void extract_arch_from_e_machine_field(int16_t e_machine, const std::string sourcename, bool* archs) { if (e_machine == 3) { archs[fARCH_i686] = 1; if(verbose) @@ -340,33 +341,37 @@ void extract_arch_from_e_machine_field(int16_t e_machine, const gchar* sourcenam } } -void extract_arch_from_text(gchar *archname, const gchar* sourcename, bool* archs) { - if (archname) { - archname = g_strstrip(archname); - if (archname) { - replacestr(archname, "-", "_"); - replacestr(archname, " ", "_"); - if (g_ascii_strncasecmp("i386", archname, 20) == 0 - || g_ascii_strncasecmp("i486", archname, 20) == 0 - || g_ascii_strncasecmp("i586", archname, 20) == 0 - || g_ascii_strncasecmp("i686", archname, 20) == 0 - || g_ascii_strncasecmp("intel_80386", archname, 20) == 0 - || g_ascii_strncasecmp("intel_80486", archname, 20) == 0 - || g_ascii_strncasecmp("intel_80586", archname, 20) == 0 - || g_ascii_strncasecmp("intel_80686", archname, 20) == 0 - ) { +void extract_arch_from_text(std::string archname, const std::string sourcename, bool* archs) { + if (!archname.empty()) { + // Trim whitespace + archname.erase(archname.begin(), std::find_if(archname.begin(), archname.end(), [](int ch) { + return !std::isspace(ch); + })); + if (!archname.empty()) { + // Replace all dashes and spaces with underscores using C++11 + std::replace(archname.begin(), archname.end(), '-', '_'); + std::replace(archname.begin(), archname.end(), ' ', '_'); + + if (archname.substr(0, 4) == "i386" + || archname.substr(0, 4) == "i486" + || archname.substr(0, 4) == "i586" + || archname.substr(0, 4) == "i686" + || archname.substr(0, 14) == "intel_80386" + || archname.substr(0, 14) == "intel_80486" + || archname.substr(0, 14) == "intel_80586" + || archname.substr(0, 14) == "intel_80686") { archs[fARCH_i686] = 1; if (verbose) std::cout << sourcename << " used for determining architecture i386" << std::endl; - } else if (g_ascii_strncasecmp("x86_64", archname, 20) == 0) { + } else if (archname.substr(0, 5) == "x86_64") { archs[fARCH_x86_64] = 1; if (verbose) std::cout << sourcename << " used for determining architecture x86_64" << std::endl; - } else if (g_ascii_strncasecmp("arm", archname, 20) == 0) { + } else if (archname.substr(0, 3) == "arm") { archs[fARCH_armhf] = 1; if (verbose) std::cout << sourcename << " used for determining architecture ARM" << std::endl; - } else if (g_ascii_strncasecmp("arm_aarch64", archname, 20) == 0) { + } else if (archname.substr(0, 11) == "arm_aarch64") { archs[fARCH_aarch64] = 1; if (verbose) std::cerr << sourcename << " used for determining architecture ARM aarch64\n"; @@ -734,7 +739,8 @@ main (int argc, char *argv[]) /* Determine the architecture */ bool archs[4] = {0, 0, 0, 0}; - extract_arch_from_text(getenv("ARCH"), "Environmental variable ARCH", archs); + std::string arch_str = getenv("ARCH") ? getenv("ARCH") : ""; + extract_arch_from_text(arch_str.c_str(), "Environmental variable ARCH", archs); if (count_archs(archs) != 1) { /* If no $ARCH variable is set check a file */ /* We use the next best .so that we can find to determine the architecture */ @@ -749,7 +755,7 @@ main (int argc, char *argv[]) die(" ..."); } } - gchar* arch = getArchName(archs); + std::string arch = getArchName(archs); std::cerr << "Using architecture " << arch << std::endl; char app_name_for_filename[PATH_MAX]; @@ -1125,7 +1131,7 @@ main (int argc, char *argv[]) std::cerr << "at https://github.com/AppImage/appimage.github.io\n"; return 0; } - + } else { std::cerr << "Error: no such file or directory: " << remaining_args[0] << "\n"; return 1; diff --git a/src/appimagetool_fetch_runtime.cpp b/src/appimagetool_fetch_runtime.cpp index 95bb6b4..8b621bc 100644 --- a/src/appimagetool_fetch_runtime.cpp +++ b/src/appimagetool_fetch_runtime.cpp @@ -113,7 +113,13 @@ class CurlRequest { } }; -bool fetch_runtime(char *arch, size_t *size, char **buffer, bool verbose) { +#include +#include +#include + +#include "appimagetool_fetch_runtime.h" + +bool fetch_runtime(std::string arch, size_t* size, char** buffer, bool verbose) { std::ostringstream urlstream; urlstream << "https://github.com/AppImage/type2-runtime/releases/download/continuous/runtime-" << arch; auto url = urlstream.str(); @@ -144,14 +150,15 @@ bool fetch_runtime(char *arch, size_t *size, char **buffer, bool verbose) { *size = response.contentLength(); - *buffer = (char *) calloc(response.contentLength() + 1, 1); + *buffer = new char[response.contentLength() + 1]; if (*buffer == nullptr) { std::cerr << "Failed to allocate buffer" << std::endl; return false; } - std::copy(runtimeData.begin(), runtimeData.end(), *buffer); + std::memcpy(*buffer, runtimeData.data(), response.contentLength()); + (*buffer)[response.contentLength()] = '\0'; return true; } diff --git a/src/appimagetool_fetch_runtime.h b/src/appimagetool_fetch_runtime.h index dee7212..5c0dc6f 100644 --- a/src/appimagetool_fetch_runtime.h +++ b/src/appimagetool_fetch_runtime.h @@ -1,14 +1,15 @@ #pragma once +// C++ +#include +#include +#include + /** * Download runtime from GitHub into a buffer. * This function allocates a buffer of the right size internally, which needs to be cleaned up by the caller. */ -#ifdef __cplusplus -extern "C" { -#endif -bool fetch_runtime(char *arch, size_t *size, char **buffer, bool verbose); -#ifdef __cplusplus -} -#endif + +bool fetch_runtime(std::string arch, size_t *size, char **buffer, bool verbose); +