Created
November 25, 2017 22:09
-
-
Save schmidt9/448ef610d72dcc2f3c68e08e00f1ac49 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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 <Foundation/Foundation.h> | |
#include <iostream> | |
#include <fstream> | |
#include <zlib.h> | |
#include <sys/stat.h> | |
#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 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<CFURLRef>(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 date | |
#endif // TARGET_OS_IPHONE |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment