/** \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 #include #include #include #include #include #include #ifdef WINDOWS #include "include/dirent.h" #include #include #include #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 #include #endif #include #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; }