// // The MIT License (MIT) // // Copyright (c) 2016 Alexander Kormanovsky // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #include "ios.h" #if TARGET_OS_IPHONE #include #include #include #include #ifndef TAR_DEBUG # define TAR_DEBUG 0 #endif #define INTERNAL_DIR "Library" #define TZDATA_DIR "tzdata" #define TARGZ_EXTENSION "tar.gz" #define TAR_BLOCK_SIZE 512 #define TAR_TYPE_POSITION 156 #define TAR_NAME_POSITION 0 #define TAR_NAME_SIZE 100 #define TAR_SIZE_POSITION 124 #define TAR_SIZE_SIZE 12 namespace arrow20_vendored::date { namespace iOSUtils { struct TarInfo { char objType; std::string objName; size_t realContentSize; // writable size without padding zeroes size_t blocksContentSize; // adjusted size to 512 bytes blocks bool success; }; std::string convertCFStringRefPathToCStringPath(CFStringRef ref); bool extractTzdata(CFURLRef homeUrl, CFURLRef archiveUrl, std::string destPath); TarInfo getTarObjectInfo(std::ifstream &readStream); std::string getTarObject(std::ifstream &readStream, int64_t size); bool writeFile(const std::string &tzdataPath, const std::string &fileName, const std::string &data, size_t realContentSize); std::string get_current_timezone() { CFTimeZoneRef tzRef = CFTimeZoneCopySystem(); CFStringRef tzNameRef = CFTimeZoneGetName(tzRef); CFIndex bufferSize = CFStringGetLength(tzNameRef) + 1; char buffer[bufferSize]; if (CFStringGetCString(tzNameRef, buffer, bufferSize, kCFStringEncodingUTF8)) { CFRelease(tzRef); return std::string(buffer); } CFRelease(tzRef); return ""; } std::string get_tzdata_path() { CFURLRef homeUrlRef = CFCopyHomeDirectoryURL(); CFStringRef homePath = CFURLCopyPath(homeUrlRef); std::string path(std::string(convertCFStringRefPathToCStringPath(homePath)) + INTERNAL_DIR + "/" + TZDATA_DIR); std::string result_path(std::string(convertCFStringRefPathToCStringPath(homePath)) + INTERNAL_DIR); if (access(path.c_str(), F_OK) == 0) { #if TAR_DEBUG printf("tzdata dir exists\n"); #endif CFRelease(homeUrlRef); CFRelease(homePath); return result_path; } CFBundleRef mainBundle = CFBundleGetMainBundle(); CFArrayRef paths = CFBundleCopyResourceURLsOfType(mainBundle, CFSTR(TARGZ_EXTENSION), NULL); if (CFArrayGetCount(paths) != 0) { // get archive path, assume there is no other tar.gz in bundle CFURLRef archiveUrl = static_cast(CFArrayGetValueAtIndex(paths, 0)); CFStringRef archiveName = CFURLCopyPath(archiveUrl); archiveUrl = CFBundleCopyResourceURL(mainBundle, archiveName, NULL, NULL); extractTzdata(homeUrlRef, archiveUrl, path); CFRelease(archiveUrl); CFRelease(archiveName); } CFRelease(homeUrlRef); CFRelease(homePath); CFRelease(paths); return result_path; } std::string convertCFStringRefPathToCStringPath(CFStringRef ref) { CFIndex bufferSize = CFStringGetMaximumSizeOfFileSystemRepresentation(ref); char *buffer = new char[bufferSize]; CFStringGetFileSystemRepresentation(ref, buffer, bufferSize); auto result = std::string(buffer); delete[] buffer; return result; } bool extractTzdata(CFURLRef homeUrl, CFURLRef archiveUrl, std::string destPath) { std::string TAR_TMP_PATH = "/tmp.tar"; CFStringRef homeStringRef = CFURLCopyPath(homeUrl); auto homePath = convertCFStringRefPathToCStringPath(homeStringRef); CFRelease(homeStringRef); CFStringRef archiveStringRef = CFURLCopyPath(archiveUrl); auto archivePath = convertCFStringRefPathToCStringPath(archiveStringRef); CFRelease(archiveStringRef); // create Library path auto libraryPath = homePath + INTERNAL_DIR; // create tzdata path auto tzdataPath = libraryPath + "/" + TZDATA_DIR; // -- replace %20 with " " const std::string search = "%20"; const std::string replacement = " "; size_t pos = 0; while ((pos = archivePath.find(search, pos)) != std::string::npos) { archivePath.replace(pos, search.length(), replacement); pos += replacement.length(); } gzFile tarFile = gzopen(archivePath.c_str(), "rb"); // create tar unpacking path auto tarPath = libraryPath + TAR_TMP_PATH; // create tzdata directory mkdir(destPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); // ======= extract tar ======== std::ofstream os(tarPath.c_str(), std::ofstream::out | std::ofstream::app); unsigned int bufferLength = 1024 * 256; // 256Kb unsigned char *buffer = (unsigned char *)malloc(bufferLength); bool success = true; while (true) { int readBytes = gzread(tarFile, buffer, bufferLength); if (readBytes > 0) { os.write((char *) &buffer[0], readBytes); } else if (readBytes == 0) { break; } else if (readBytes == -1) { printf("decompression failed\n"); success = false; break; } else { printf("unexpected zlib state\n"); success = false; break; } } os.close(); free(buffer); gzclose(tarFile); if (!success) { remove(tarPath.c_str()); return false; } // ======== extract files ========= uint64_t location = 0; // Position in the file // get file size struct stat stat_buf; int res = stat(tarPath.c_str(), &stat_buf); if (res != 0) { printf("error file size\n"); remove(tarPath.c_str()); return false; } int64_t tarSize = stat_buf.st_size; // create read stream std::ifstream is(tarPath.c_str(), std::ifstream::in | std::ifstream::binary); // process files while (location < tarSize) { TarInfo info = getTarObjectInfo(is); if (!info.success || info.realContentSize == 0) { break; // something wrong or all files are read } switch (info.objType) { case '0': // file case '\0': // { std::string obj = getTarObject(is, info.blocksContentSize); #if TAR_DEBUG size += info.realContentSize; printf("#%i %s file size %lld written total %ld from %lld\n", ++count, info.objName.c_str(), info.realContentSize, size, tarSize); #endif writeFile(tzdataPath, info.objName, obj, info.realContentSize); location += info.blocksContentSize; break; } } } remove(tarPath.c_str()); return true; } TarInfo getTarObjectInfo(std::ifstream &readStream) { int64_t length = TAR_BLOCK_SIZE; char buffer[length]; char type; char name[TAR_NAME_SIZE + 1]; char sizeBuf[TAR_SIZE_SIZE + 1]; readStream.read(buffer, length); memcpy(&type, &buffer[TAR_TYPE_POSITION], 1); memset(&name, '\0', TAR_NAME_SIZE + 1); memcpy(&name, &buffer[TAR_NAME_POSITION], TAR_NAME_SIZE); memset(&sizeBuf, '\0', TAR_SIZE_SIZE + 1); memcpy(&sizeBuf, &buffer[TAR_SIZE_POSITION], TAR_SIZE_SIZE); size_t realSize = strtol(sizeBuf, NULL, 8); size_t blocksSize = realSize + (TAR_BLOCK_SIZE - (realSize % TAR_BLOCK_SIZE)); return {type, std::string(name), realSize, blocksSize, true}; } std::string getTarObject(std::ifstream &readStream, int64_t size) { char buffer[size]; readStream.read(buffer, size); return std::string(buffer); } bool writeFile(const std::string &tzdataPath, const std::string &fileName, const std::string &data, size_t realContentSize) { std::ofstream os(tzdataPath + "/" + fileName, std::ofstream::out | std::ofstream::binary); if (!os) { return false; } // trim empty space char trimmedData[realContentSize + 1]; memset(&trimmedData, '\0', realContentSize); memcpy(&trimmedData, data.c_str(), realContentSize); // write os.write(trimmedData, realContentSize); os.close(); return true; } } // namespace iOSUtils } // namespace arrow20_vendored::date #endif // TARGET_OS_IPHONE