diff options
Diffstat (limited to 'app/bin/archive.c')
-rw-r--r-- | app/bin/archive.c | 454 |
1 files changed, 454 insertions, 0 deletions
diff --git a/app/bin/archive.c b/app/bin/archive.c new file mode 100644 index 0000000..4e82bd3 --- /dev/null +++ b/app/bin/archive.c @@ -0,0 +1,454 @@ +/** \file archive.c + * ARCHIVE PROCESSING + */ + +/* XTrkCad - Model Railroad CAD + * Copyright (C) 2018 Adam Richards and Martin Fischer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <errno.h> +#include <fcntl.h> + +#include <string.h> +#include <stdint.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <zip.h> + +#ifdef WINDOWS + #include "include/dirent.h" + #include <direct.h> + #include <io.h> + #include <process.h> + #define unlink(a) _unlink((a)) + #define rmdir(a) _rmdir((a)) + #define open(name, flag, mode) _open((name), (flag), (mode)) + #define write(file, buffer, count) _write((file),(buffer), (count)) + #define close(file) _close((file)) + #define getpid() _getpid() +#else + #include <dirent.h> + #include <unistd.h> +#endif + +#include <wlib.h> +#include "archive.h" +#include "directory.h" +#include "dynstring.h" +#include "i18n.h" +#include "messages.h" +#include "misc.h" +#include "misc2.h" +#include "paths.h" +#include "include/utf8convert.h" + +int log_zip = 0; + +//char * +//NativeToUtf8(const char *nativeString) +//{ +// +//#ifdef WINDOWS +// +// int cnt = 2 * (strlen(nativeString) + 1); +// char *tempBuffer = MyMalloc( cnt ); +// char *destBuffer = MyMalloc( cnt ); +// +// //// find the +// //cnt = MultiByteToWideChar(CP_ACP, +// // 0, +// // nativeString, +// // -1, +// // tempBuffer, +// // 0); +// +// //tempBuffer = realloc(tempBuffer, cnt * 2 + 4); +// +// // convert to wide character (UTF16) +// MultiByteToWideChar(CP_ACP, +// 0, +// nativeString, +// -1, +// (LPWSTR)tempBuffer, +// cnt); +// +// // convert from wide char to UTF-8 +// WideCharToMultiByte(CP_UTF8, +// 0, +// (LPCWCH)tempBuffer, +// -1, +// (LPSTR)destBuffer, +// cnt, +// NULL, +// NULL); +// +// MyFree(tempBuffer); +//#else +// char * destBuffer = MyStrdup(nativeString); +//#endif +// +// return(destBuffer); +//} + +/** + * Create the full path for temporary directories used in zip archive operations + * + * \param archive operation + * \return pointer to full path, must be free'd by caller + */ + +char * +GetZipDirectoryName(enum ArchiveOps op) +{ + char *opDesc; + char *directory; + DynString zipDirectory; + + DynStringMalloc(&zipDirectory, 0); + + switch (op) { + case ARCHIVE_READ: + opDesc = "in"; + break; + case ARCHIVE_WRITE: + opDesc = "out"; + break; + default: + opDesc = "err"; + break; + } + + DynStringPrintf(&zipDirectory, + "%s" FILE_SEP_CHAR "zip_%s.%d", + workingDir, + opDesc, + getpid()); + + directory = strdup(DynStringToCStr(&zipDirectory)); + DynStringFree(&zipDirectory); + return (directory); +} + +/***************************************************************************** + * Add directory to archive + * + * \param IN zip The open zip archive handle + * \param IN dir_path The path to add + * \param IN prefix The prefix in the archive + * + * \returns TRUE if OK + */ + +BOOL_T AddDirectoryToArchive( + struct zip * za, + const char * dir_path, + const char * prefix) +{ + + char *full_path; + char *arch_path; + DIR *dir; + const char * buf; + struct stat stat_path, stat_entry; + struct dirent *entry; + + zip_source_t * zt; + + // stat for the path + stat(dir_path, &stat_path); + + // if path does not exists or is not dir - exit with status -1 + if (S_ISDIR(stat_path.st_mode) == 0) { + NoticeMessage(MSG_NOT_DIR_FAIL, + _("Continue"), NULL, dir_path); + return FALSE; + } + + // if not possible to read the directory for this user + if ((dir = opendir(dir_path)) == NULL) { + NoticeMessage(MSG_OPEN_DIR_FAIL, + _("Continue"), NULL, dir_path); + return FALSE; + } + + // iteration through entries in the directory + while ((entry = readdir(dir)) != NULL) { + // skip entries "." and ".." + if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { + continue; + } + + // determinate a full path of an entry + MakeFullpath(&full_path, dir_path, entry->d_name, NULL); + + // stat for the entry + stat(full_path, &stat_entry); + + if (prefix && prefix[0]) { + MakeFullpath(&arch_path, prefix, entry->d_name, NULL); + } else { + MakeFullpath(&arch_path, entry->d_name, NULL); + } + + // recursively add a nested directory + if (S_ISDIR(stat_entry.st_mode) != 0) { + if (zip_dir_add(za, arch_path, 0) < 0) { + zip_error_t *ziperr = zip_get_error(za); + buf = zip_error_strerror(ziperr); + NoticeMessage(MSG_ZIP_DIR_ADD_FAIL, + _("Continue"), NULL, arch_path, buf); +#if DEBUG + printf("Added Directory %s \n", arch_path); +#endif + } + + if (AddDirectoryToArchive(za, full_path, arch_path) != TRUE) { + free(full_path); + free(arch_path); + return FALSE; + } + free(arch_path); + continue; + } else { + char *archPathUtf8 = MyStrdup(arch_path); + char *fullPathUtf8 = MyStrdup(full_path); +#ifdef WINDOWS + archPathUtf8 = Convert2UTF8(archPathUtf8); + fullPathUtf8 = Convert2UTF8(fullPathUtf8); + ConvertPathForward(archPathUtf8); +#endif // WINDOWS + zt = zip_source_file(za, fullPathUtf8, 0, -1); + if (zip_file_add(za, archPathUtf8, zt, ZIP_FL_ENC_UTF_8) == -1) { + zip_error_t *ziperr = zip_get_error(za); + buf = zip_error_strerror(ziperr); + NoticeMessage(MSG_ZIP_FILE_ADD_FAIL, _("Continue"), NULL, full_path, arch_path, + buf); + free(full_path); + free(arch_path); + MyFree(fullPathUtf8); + MyFree(archPathUtf8); + return FALSE; + } + MyFree(fullPathUtf8); + MyFree(archPathUtf8); +#if DEBUG + printf("Added File %s", full_path); +#endif + } + free(arch_path); + free(full_path); + } + + closedir(dir); + return TRUE; +} + +/*********************************************************************** + * Create Archive + * + * \param IN dir_path The place to create the archive + * \param IN fileName The name of the archive + * + * \return TRUE if ok + */ + +BOOL_T CreateArchive( + const char * dir_path, + const char * fileName) +{ + struct zip *za; + int err; + char buf[100]; + + char * archive = MyStrdup(fileName); // Because of const char + char * archive_name = FindFilename(archive); + char * archive_path; + char * archiveUtf8; + + MakeFullpath(&archive_path, workingDir, archive_name, NULL); + + archiveUtf8 = MyStrdup(archive_path); +#ifdef WINDOWS + archiveUtf8 = Convert2UTF8(archiveUtf8); +#endif // WINDOWS + + MyFree(archive); + + if ((za = zip_open(archiveUtf8, ZIP_CREATE, &err)) == NULL) { + zip_error_to_str(buf, sizeof(buf), err, errno); + NoticeMessage(MSG_ZIP_CREATE_FAIL, _("Continue"), NULL, archiveUtf8, buf); + MyFree(archiveUtf8); + return FALSE; + } +#if DEBUG + printf("====================== \n"); + printf("Started Archive %s", archive_path); +#endif + + AddDirectoryToArchive(za, dir_path, ""); + + if (zip_close(za) == -1) { + zip_error_to_str(buf, sizeof(buf), err, errno); + NoticeMessage(MSG_ZIP_CLOSE_FAIL, _("Continue"), NULL, archiveUtf8, buf); + free(archive_path); + MyFree(archiveUtf8); + return FALSE; + } + + unlink(fileName); //Delete Old + if (rename(archive_path, fileName) == -1) { //Move zip into place + NoticeMessage(MSG_ZIP_RENAME_FAIL, _("Continue"), NULL, archiveUtf8, fileName, + strerror(errno)); + free(archive_path); + MyFree(archiveUtf8); + return FALSE; + } + free(archive_path); + MyFree(archiveUtf8); + +#if DEBUG + printf("Moved Archive to %s", fileName); + printf("====================== \n"); +#endif + return TRUE; +} + +/************************************************************************** + * Unpack_Archive_for + * + * \param IN pathName the name of the archive + * \param IN fileName just the filename and extension of the layout + * \param IN tempDir The directory to use to unpack into + * + * \returns TRUE if all worked + */ +BOOL_T UnpackArchiveFor( + const char * pathName, /*Full name of archive*/ + const char * fileName, /*Layout name and extension */ + const char * tempDir, /*Directory to unpack into */ + BOOL_T file_only) +{ + char *dirName; + struct zip *za; + struct zip_file *zf; + struct zip_stat sb; + char buf[100]; + int err; + int i; + int64_t len; + FILE *fd; + long long sum; + + char *destBuffer = MyStrdup(pathName); +#ifdef WINDOWS + destBuffer = Convert2UTF8(destBuffer); +#endif // WINDOWS + + + if ((za = zip_open(destBuffer, 0, &err)) == NULL) { + zip_error_to_str(buf, sizeof(buf), err, errno); + NoticeMessage(MSG_ZIP_OPEN_FAIL, _("Continue"), NULL, pathName, buf); + fprintf(stderr, "xtrkcad: can't open xtrkcad zip archive `%s': %s \n", + pathName, buf); + + MyFree(destBuffer); + return FALSE; + } + + for (i = 0; i < zip_get_num_entries(za, 0); i++) { + if (zip_stat_index(za, i, 0, &sb) == 0) { + len = strlen(sb.name); + +#if DEBUG + printf("==================\n"); + printf("Name: [%s], ", sb.name); + printf("Size: [%llu], ", sb.size); + printf("mtime: [%u]\n", (unsigned int)sb.mtime); + printf("mtime: [%u]\n", (unsigned int)sb.mtime); +#endif + + LOG(log_zip, 1, ("================= \n")) + LOG(log_zip, 1, ("Zip-Name [%s] \n", sb.name)) + LOG(log_zip, 1, ("Zip-Size [%llu] \n", sb.size)) + LOG(log_zip, 1, ("Zip-mtime [%u] \n", (unsigned int)sb.mtime)) + + if (sb.name[len - 1] == '/' && !file_only) { + MakeFullpath(&dirName, tempDir, &sb.name[0], NULL); + if (SafeCreateDir(dirName) != TRUE) { + free(dirName); + return FALSE; + } + free(dirName); + } else { + zf = zip_fopen_index(za, i, 0); + if (!zf) { + NoticeMessage(MSG_ZIP_INDEX_FAIL, _("Continue"), NULL); + fprintf(stderr, "xtrkcad zip archive open index error \n"); + return FALSE; + } + + if (file_only) { + if (strncmp(sb.name, fileName, strlen(fileName)) != 0) { + continue; /* Ignore any other files than the one we asked for */ + } + } + MakeFullpath(&dirName, tempDir, &sb.name[0], NULL); +#ifdef WINDOWS + ConvertUTF8ToSystem(dirName); +#endif // WINDOWS + fd = fopen(dirName, "wb"); + if (!fd) { + NoticeMessage(MSG_ZIP_FILE_OPEN_FAIL, _("Continue"), NULL, dirName, + strerror(errno)); + free(dirName); + return FALSE; + } + + sum = 0; + while (sum != sb.size) { + len = zip_fread(zf, buf, 100); + if (len < 0) { + NoticeMessage(MSG_ZIP_READ_FAIL, _("Continue"), NULL, dirName, &sb.name[0]); + free(dirName); + fclose(fd); + return FALSE; + } + fwrite(buf, 1, (unsigned int)len, fd); + sum += len; + } + fclose(fd); + free(dirName); + zip_fclose(zf); + } + } else { + LOG(log_zip, 1, ("Zip-Unknown File[%s] Line[%d] \n", __FILE__, __LINE__)) +#if DEBUG + printf("File[%s] Line[%d]\n", __FILE__, __LINE__); +#endif + } + } + + MyFree(destBuffer); + + if (zip_close(za) == -1) { + NoticeMessage(MSG_ZIP_CLOSE_FAIL, _("Continue"), NULL, dirName, &sb.name[0]); + return FALSE; + } + return TRUE; +} + |