diff --git a/README.md b/README.md index 9ce9ba6..5baa62d 100644 --- a/README.md +++ b/README.md @@ -39,8 +39,7 @@ For libgourou : For utils : - * QT5Core - * QT5Network + * libcurl * OpenSSL * libzip diff --git a/include/libgourou.h b/include/libgourou.h index 9f9a2d8..bc5d61e 100644 --- a/include/libgourou.h +++ b/include/libgourou.h @@ -40,7 +40,7 @@ #define ACS_SERVER "http://adeactivate.adobe.com/adept" #endif -#define LIBGOUROU_VERSION "0.5.3" +#define LIBGOUROU_VERSION "0.6" namespace gourou { diff --git a/include/libgourou_common.h b/include/libgourou_common.h index ad3c3eb..cc98de7 100644 --- a/include/libgourou_common.h +++ b/include/libgourou_common.h @@ -111,7 +111,8 @@ namespace gourou CLIENT_ZIP_ERROR, CLIENT_GENERIC_EXCEPTION, CLIENT_NETWORK_ERROR, - CLIENT_INVALID_PKCS8 + CLIENT_INVALID_PKCS8, + CLIENT_FILE_ERROR }; enum DRM_REMOVAL_ERROR { diff --git a/utils/Makefile b/utils/Makefile index 2ad92f8..318cf92 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -1,10 +1,10 @@ TARGETS=acsmdownloader adept_activate adept_remove -CXXFLAGS=-Wall `pkg-config --cflags Qt5Core Qt5Network` -fPIC -I$(ROOT)/include -I$(ROOT)/lib/pugixml/src/ +CXXFLAGS=-Wall -fPIC -I$(ROOT)/include -I$(ROOT)/lib/pugixml/src/ STATIC_DEP= -LDFLAGS=`pkg-config --libs Qt5Core Qt5Network` -L$(ROOT) -lcrypto -lzip -lz +LDFLAGS=-L$(ROOT) -lcrypto -lzip -lz -lcurl ifneq ($(STATIC_UTILS),) STATIC_DEP = $(ROOT)/libgourou.a @@ -18,7 +18,7 @@ else CXXFLAGS += -O2 endif -COMMON_DEPS = drmprocessorclientimpl.cpp $(STATIC_DEP) +COMMON_DEPS = drmprocessorclientimpl.cpp utils_common.cpp $(STATIC_DEP) all: $(TARGETS) diff --git a/utils/acsmdownloader.cpp b/utils/acsmdownloader.cpp index e47b9ae..aeb31b1 100644 --- a/utils/acsmdownloader.cpp +++ b/utils/acsmdownloader.cpp @@ -26,21 +26,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include -#include + #include #include - -#include -#include -#include -#include -#include +#include #include #include "drmprocessorclientimpl.h" - -#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) +#include "utils_common.h" static const char* deviceFile = "device.xml"; static const char* activationFile = "activation.xml"; @@ -49,23 +42,13 @@ static const char* acsmFile = 0; static bool exportPrivateKey = false; static const char* outputFile = 0; static const char* outputDir = 0; -static const char* defaultDirs[] = { - ".adept/", - "./adobe-digital-editions/", - "./.adobe-digital-editions/" -}; -class ACSMDownloader: public QRunnable +class ACSMDownloader { public: - ACSMDownloader(QCoreApplication* app): - app(app) - { - setAutoDelete(false); - } - - void run() + + int run() { int ret = 0; try @@ -84,9 +67,8 @@ public: if (outputDir) { - QDir dir(outputDir); - if (!dir.exists(outputDir)) - dir.mkpath(outputDir); + if (!fileExists(outputDir)) + mkpath(outputDir); filename = std::string(outputDir) + "/" + filename; } @@ -116,9 +98,8 @@ public: if (outputDir) { - QDir dir(outputDir); - if (!dir.exists(outputDir)) - dir.mkpath(outputDir); + if (!fileExists(outputDir)) + mkpath(outputDir); filename = std::string(outputDir) + "/" + filename; } @@ -132,8 +113,7 @@ public: finalName += ".pdf"; else finalName += ".epub"; - QDir dir; - dir.rename(filename.c_str(), finalName.c_str()); + rename(filename.c_str(), finalName.c_str()); filename = finalName; } std::cout << "Created " << filename << std::endl; @@ -144,37 +124,10 @@ public: ret = 1; } - this->app->exit(ret); + return ret; } - -private: - QCoreApplication* app; }; -static const char* findFile(const char* filename, bool inDefaultDirs=true) -{ - QFile file(filename); - - if (file.exists()) - return strdup(filename); - - if (!inDefaultDirs) return 0; - - for (int i=0; i<(int)ARRAY_SIZE(defaultDirs); i++) - { - QString path = QString(defaultDirs[i]) + QString(filename); - file.setFileName(path); - if (file.exists()) - return strdup(path.toStdString().c_str()); - } - - return 0; -} - -static void version(void) -{ - std::cout << "Current libgourou version : " << gourou::DRMProcessor::VERSION << std::endl ; -} static void usage(const char* cmd) { @@ -275,8 +228,7 @@ int main(int argc, char** argv) return -1; } - QCoreApplication app(argc, argv); - ACSMDownloader downloader(&app); + ACSMDownloader downloader; int i; bool hasErrors = false; @@ -306,8 +258,7 @@ int main(int argc, char** argv) } else { - QFile file(acsmFile); - if (!file.exists()) + if (!fileExists(acsmFile)) { std::cout << "Error : " << acsmFile << " doesn't exists" << std::endl; ret = -1; @@ -315,9 +266,7 @@ int main(int argc, char** argv) } } - QThreadPool::globalInstance()->start(&downloader); - - ret = app.exec(); + ret = downloader.run(); end: for (i=0; i<(int)ARRAY_SIZE(files); i++) diff --git a/utils/adept_activate.cpp b/utils/adept_activate.cpp index d8691ea..3937939 100644 --- a/utils/adept_activate.cpp +++ b/utils/adept_activate.cpp @@ -28,22 +28,16 @@ #include #include -#include #include +#include +#include #include #include -#include -#include -#include -#include -#include - #include #include "drmprocessorclientimpl.h" - -#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) +#include "utils_common.h" static const char* username = 0; static const char* password = 0; @@ -100,16 +94,11 @@ static std::string getpass(const char *prompt, bool show_asterisk=false) } -class ADEPTActivate: public QRunnable +class ADEPTActivate { -public: - ADEPTActivate(QCoreApplication* app): - app(app) - { - setAutoDelete(false); - } - - void run() +public: + + int run() { int ret = 0; try @@ -128,17 +117,10 @@ public: ret = 1; } - this->app->exit(ret); + return ret; } - -private: - QCoreApplication* app; }; -static void version(void) -{ - std::cout << "Current libgourou version : " << gourou::DRMProcessor::VERSION << std::endl ; -} static void usage(const char* cmd) { @@ -162,8 +144,8 @@ static void usage(const char* cmd) static const char* abspath(const char* filename) { const char* root = getcwd(0, PATH_MAX); - QString fullPath = QString(root) + QString("/") + QString(filename); - const char* res = strdup(fullPath.toStdString().c_str()); + std::string fullPath = std::string(root) + std::string("/") + filename; + const char* res = strdup(fullPath.c_str()); free((void*)root); @@ -255,9 +237,8 @@ int main(int argc, char** argv) // Relative path if (_outputDir[0] == '.' || _outputDir[0] != '/') { - QFile file(_outputDir); // realpath doesn't works if file/dir doesn't exists - if (file.exists()) + if (fileExists(_outputDir)) outputDir = strdup(realpath(_outputDir, 0)); else outputDir = strdup(abspath(_outputDir)); @@ -266,10 +247,8 @@ int main(int argc, char** argv) outputDir = strdup(_outputDir); } - QCoreApplication app(argc, argv); - - QFile file(outputDir); - if (file.exists()) + std::string pass; + if (fileExists(outputDir)) { int key; @@ -289,7 +268,6 @@ int main(int argc, char** argv) ; } - std::string pass; if (!password) { char prompt[128]; @@ -298,13 +276,11 @@ int main(int argc, char** argv) password = pass.c_str(); } - ADEPTActivate activate(&app); - QThreadPool::globalInstance()->start(&activate); + ADEPTActivate activate; - ret = app.exec(); + ret = activate.run(); -end: - +end: free((void*)outputDir); return ret; } diff --git a/utils/adept_remove.cpp b/utils/adept_remove.cpp index 6a1a838..556e409 100644 --- a/utils/adept_remove.cpp +++ b/utils/adept_remove.cpp @@ -26,23 +26,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include -#include -#include -#include -#include -#include -#include - #include #include -#include "drmprocessorclientimpl.h" -#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) +#include "drmprocessorclientimpl.h" +#include "utils_common.h" static const char* deviceFile = "device.xml"; static const char* activationFile = "activation.xml"; @@ -50,13 +42,9 @@ static const char* devicekeyFile = "devicesalt"; static const char* inputFile = 0; static const char* outputFile = 0; static const char* outputDir = 0; -static const char* defaultDirs[] = { - ".adept/", - "./adobe-digital-editions/", - "./.adobe-digital-editions/" -}; -static char* encryptionKeyUser = 0; -static unsigned char* encryptionKey = 0; + +static char* encryptionKeyUser = 0; +static unsigned char* encryptionKey = 0; static unsigned encryptionKeySize = 0; static inline unsigned char htoi(unsigned char c) @@ -78,16 +66,11 @@ static inline bool endsWith(const std::string& s, const std::string& suffix) return s.rfind(suffix) == std::abs((int)(s.size()-suffix.size())); } -class ADEPTRemove: public QRunnable +class ADEPTRemove { public: - ADEPTRemove(QCoreApplication* app): - app(app) - { - setAutoDelete(false); - } - void run() + int run() { int ret = 0; try @@ -104,9 +87,8 @@ public: if (outputDir) { - QDir dir(outputDir); - if (!dir.exists(outputDir)) - dir.mkpath(outputDir); + if (!fileExists(outputDir)) + mkpath(outputDir); filename = std::string(outputDir) + "/" + filename; } @@ -122,11 +104,8 @@ public: if (inputFile != filename) { - QFile::remove(filename.c_str()); - if (!QFile::copy(inputFile, filename.c_str())) - { - EXCEPTION(gourou::DRM_FILE_ERROR, "Unable to copy " << inputFile << " into " << filename); - } + unlink(filename.c_str()); + fileCopy(inputFile, filename.c_str()); processor.removeDRM(inputFile, filename, type, encryptionKey, encryptionKeySize); std::cout << "DRM removed into new file " << filename << std::endl; } @@ -135,18 +114,16 @@ public: // Use temp file for PDF if (type == gourou::DRMProcessor::ITEM_TYPE::PDF) { - QTemporaryFile tempFile; - tempFile.open(); - tempFile.setAutoRemove(false); // In case of failure - processor.removeDRM(inputFile, tempFile.fileName().toStdString(), type, encryptionKey, encryptionKeySize); + char* tempFile = tempnam("/tmp", NULL); + processor.removeDRM(inputFile, tempFile, type, encryptionKey, encryptionKeySize); /* Original file must be removed before doing a copy... */ - QFile origFile(inputFile); - origFile.remove(); - if (!QFile::copy(tempFile.fileName(), filename.c_str())) + unlink(inputFile); + if (!rename(tempFile, filename.c_str())) { - EXCEPTION(gourou::DRM_FILE_ERROR, "Unable to copy " << tempFile.fileName().toStdString() << " into " << filename); + free(tempFile); + EXCEPTION(gourou::DRM_FILE_ERROR, "Unable to copy " << tempFile << " into " << filename); } - tempFile.setAutoRemove(true); + free(tempFile); } else processor.removeDRM(inputFile, filename, type, encryptionKey, encryptionKeySize); @@ -158,38 +135,10 @@ public: ret = 1; } - this->app->exit(ret); + return ret; } - -private: - QCoreApplication* app; }; -static const char* findFile(const char* filename, bool inDefaultDirs=true) -{ - QFile file(filename); - - if (file.exists()) - return strdup(filename); - - if (!inDefaultDirs) return 0; - - for (int i=0; i<(int)ARRAY_SIZE(defaultDirs); i++) - { - QString path = QString(defaultDirs[i]) + QString(filename); - file.setFileName(path); - if (file.exists()) - return strdup(path.toStdString().c_str()); - } - - return 0; -} - -static void version(void) -{ - std::cout << "Current libgourou version : " << gourou::DRMProcessor::VERSION << std::endl ; -} - static void usage(const char* cmd) { std::cout << "Remove ADEPT DRM (from Adobe) of EPUB/PDF file" << std::endl; @@ -288,8 +237,7 @@ int main(int argc, char** argv) return -1; } - QCoreApplication app(argc, argv); - ADEPTRemove remover(&app); + ADEPTRemove remover; int i; bool hasErrors = false; @@ -335,10 +283,8 @@ int main(int argc, char** argv) if (hasErrors) goto end; - QThreadPool::globalInstance()->start(&remover); - - ret = app.exec(); - + ret = remover.run(); + end: for (i=0; i<(int)ARRAY_SIZE(files); i++) { diff --git a/utils/drmprocessorclientimpl.cpp b/utils/drmprocessorclientimpl.cpp index b18b1ba..f627275 100644 --- a/utils/drmprocessorclientimpl.cpp +++ b/utils/drmprocessorclientimpl.cpp @@ -25,17 +25,18 @@ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include + +#include +#include +#include #include #include #include #include -#include -#include -#include -#include -#include +#include #include #include @@ -44,6 +45,27 @@ #include #include "drmprocessorclientimpl.h" +// https://stackoverflow.com/questions/216823/how-to-trim-a-stdstring +// trim from start (in place) +static inline void ltrim(std::string &s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { + return !std::isspace(ch); + })); +} + +// trim from end (in place) +static inline void rtrim(std::string &s) { + s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { + return !std::isspace(ch); + }).base(), s.end()); +} + +// trim from both ends (in place) +static inline void trim(std::string &s) { + ltrim(s); + rtrim(s); +} + /* Digest interface */ void* DRMProcessorClientImpl::createDigest(const std::string& digestName) { @@ -90,78 +112,120 @@ void DRMProcessorClientImpl::randBytes(unsigned char* bytesOut, unsigned int len /* HTTP interface */ #define DISPLAY_THRESHOLD 10*1024 // Threshold to display download progression -static void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) { - // For "big" files only - if (bytesTotal >= DISPLAY_THRESHOLD && gourou::logLevel >= gourou::WARN) +static int downloadProgress(void *clientp, curl_off_t dltotal, curl_off_t dlnow, + curl_off_t ultotal, curl_off_t ulnow) +{ +// For "big" files only + if (dltotal >= DISPLAY_THRESHOLD && gourou::logLevel >= gourou::WARN) { int percent = 0; - if (bytesTotal) - percent = (bytesReceived * 100) / bytesTotal; + if (dltotal) + percent = (dlnow * 100) / dltotal; std::cout << "\rDownload " << percent << "%" << std::flush; } + + return 0; +} + +static size_t curlRead(void *data, size_t size, size_t nmemb, void *userp) +{ + gourou::ByteArray* replyData = (gourou::ByteArray*) userp; + + replyData->append((unsigned char*)data, size*nmemb); + + return size*nmemb; +} + +static size_t curlHeaders(char *buffer, size_t size, size_t nitems, void *userdata) +{ + std::map* responseHeaders = (std::map*)userdata; + std::string::size_type pos = 0; + std::string buf(buffer, size*nitems); + + pos = buf.find(":", pos); + + if (pos != std::string::npos) + { + std::string key = std::string(buffer, pos); + std::string value = std::string(&buffer[pos+1], (size*nitems)-(pos+1)); + + trim(key); + trim(value); + + (*responseHeaders)[key] = value; + + if (gourou::logLevel >= gourou::DEBUG) + std::cout << key << " : " << value << std::endl; + } + + return size*nitems; } std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, const std::string& POSTData, const std::string& contentType, std::map* responseHeaders) { - QNetworkRequest request(QUrl(URL.c_str())); - QNetworkAccessManager networkManager; - QByteArray replyData; + gourou::ByteArray replyData; + std::map localHeaders; + if (!responseHeaders) + responseHeaders = &localHeaders; + GOUROU_LOG(gourou::INFO, "Send request to " << URL); if (POSTData.size()) { GOUROU_LOG(gourou::DEBUG, "<<< " << std::endl << POSTData); } - - request.setRawHeader("Accept", "*/*"); - request.setRawHeader("User-Agent", "book2png"); - if (contentType.size()) - request.setRawHeader("Content-Type", contentType.c_str()); - QNetworkReply* reply; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, URL.c_str()); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "book2png"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + + + struct curl_slist *list = NULL; + list = curl_slist_append(list, "Accept: */*"); + std::string _contentType; + if (contentType.size()) + { + _contentType = "Content-Type: " + contentType; + list = curl_slist_append(list, _contentType.c_str()); + } + + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); if (POSTData.size()) - reply = networkManager.post(request, POSTData.c_str()); - else - reply = networkManager.get(request); - - QEventLoop loop; - - QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); - // Handled just below - QObject::connect(reply, &QNetworkReply::errorOccurred, &loop, &QEventLoop::quit); - QObject::connect(reply, &QNetworkReply::downloadProgress, &loop, downloadProgress); - - loop.exec(); - - QByteArray location = reply->rawHeader("Location"); - if (location.size() != 0) { - GOUROU_LOG(gourou::DEBUG, "New location"); - return sendHTTPRequest(location.constData(), POSTData, contentType, responseHeaders); + curl_easy_setopt(curl, CURLOPT_POST, 1L); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, POSTData.size()); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, POSTData.data()); } - if (reply->error() != QNetworkReply::NoError) - EXCEPTION(gourou::CLIENT_NETWORK_ERROR, "Error " << reply->errorString().toStdString()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlRead); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&replyData); - QList headers = reply->rawHeaderList(); - for (int i = 0; i < headers.size(); ++i) { - if (gourou::logLevel >= gourou::DEBUG) - std::cout << headers[i].constData() << " : " << reply->rawHeader(headers[i]).constData() << std::endl; - if (responseHeaders) - (*responseHeaders)[headers[i].constData()] = reply->rawHeader(headers[i]).constData(); - } + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curlHeaders); + curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void*)responseHeaders); + + curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, downloadProgress); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); + + res = curl_easy_perform(curl); + curl_slist_free_all(list); + curl_easy_cleanup(curl); + + if (res != CURLE_OK) + EXCEPTION(gourou::CLIENT_NETWORK_ERROR, "Error " << curl_easy_strerror(res)); - replyData = reply->readAll(); if (replyData.size() >= DISPLAY_THRESHOLD && gourou::logLevel >= gourou::WARN) std::cout << std::endl; - if (reply->rawHeader("Content-Type") == "application/vnd.adobe.adept+xml") + + if ((*responseHeaders)["Content-Type"] == "application/vnd.adobe.adept+xml") { GOUROU_LOG(gourou::DEBUG, ">>> " << std::endl << replyData.data()); } - return std::string(replyData.data(), replyData.length()); + return std::string((char*)replyData.data(), replyData.length()); } void DRMProcessorClientImpl::RSAPrivateEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength, diff --git a/utils/utils_common.cpp b/utils/utils_common.cpp new file mode 100644 index 0000000..49b246d --- /dev/null +++ b/utils/utils_common.cpp @@ -0,0 +1,127 @@ +/* + Copyright (c) 2022, Grégory Soutadé + + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include "utils_common.h" + +static const char* defaultDirs[] = { + ".adept/", + "./adobe-digital-editions/", + "./.adobe-digital-editions/" +}; + +void version(void) +{ + std::cout << "Current libgourou version : " << gourou::DRMProcessor::VERSION << std::endl ; +} + +bool fileExists(const char* filename) +{ + struct stat _stat; + int ret = stat(filename, &_stat); + + return (ret == 0); +} + +const char* findFile(const char* filename, bool inDefaultDirs) +{ + if (fileExists(filename)) + return strdup(filename); + + if (!inDefaultDirs) return 0; + + for (int i=0; i<(int)ARRAY_SIZE(defaultDirs); i++) + { + std::string path = std::string(defaultDirs[i]) + filename; + if (fileExists(path.c_str())) + return strdup(path.c_str()); + } + + return 0; +} + +// https://stackoverflow.com/questions/2336242/recursive-mkdir-system-call-on-unix +void mkpath(const char *dir) +{ + char tmp[PATH_MAX]; + char *p = NULL; + size_t len; + + snprintf(tmp, sizeof(tmp),"%s",dir); + len = strlen(tmp); + if (tmp[len - 1] == '/') + tmp[len - 1] = 0; + for (p = tmp + 1; *p; p++) + if (*p == '/') { + *p = 0; + mkdir(tmp, S_IRWXU); + *p = '/'; + } + mkdir(tmp, S_IRWXU); +} + +void fileCopy(const char* in, const char* out) +{ + char buffer[4096]; + int ret, fdIn, fdOut; + + fdIn = open(in, O_RDONLY); + + if (!fdIn) + EXCEPTION(gourou::CLIENT_FILE_ERROR, "Unable to open " << in); + + fdOut = open(out, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH); + + if (!fdOut) + { + close (fdIn); + EXCEPTION(gourou::CLIENT_FILE_ERROR, "Unable to open " << out); + } + + while (true) + { + ret = ::read(fdIn, buffer, sizeof(buffer)); + if (ret <= 0) + break; + ::write(fdOut, buffer, ret); + } + + close (fdIn); + close (fdOut); +} diff --git a/utils/utils_common.h b/utils/utils_common.h new file mode 100644 index 0000000..376a7c8 --- /dev/null +++ b/utils/utils_common.h @@ -0,0 +1,64 @@ +/* + Copyright (c) 2022, Grégory Soutadé + + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _UTILS_COMMON_H_ +#define _UTILS_COMMON_H_ + +#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) + +/** + * @brief Display libgourou version + */ +void version(void); + +/** + * @brief Find a given filename in current directory and/or in default directories + * + * @param filename Filename to search + * @param inDefaultDirs Search is default directories or not + * + * @return A copy of full path + */ +const char* findFile(const char* filename, bool inDefaultDirs=true); + +/** + * @brief Does the file (or directory exists) + */ +bool fileExists(const char* filename); + +/** + * @brief Recursively created dir + */ +void mkpath(const char *dir); + +/** + * @brief Copy file in into file out + */ +void fileCopy(const char* in, const char* out); + +#endif