diff --git a/include/date/ios.mm b/include/date/ios.mm index b4c20b3..105e5c0 100644 --- a/include/date/ios.mm +++ b/include/date/ios.mm @@ -28,7 +28,7 @@ #include -#include +#include #include #include @@ -49,401 +49,289 @@ namespace date { -namespace iOSUtils -{ - -struct TarInfo -{ - char objType; - std::string objName; - int64_t realContentSize; // writable size without padding zeroes - int64_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(CFReadStreamRef readStream, int64_t location); -std::string getTarObject(CFReadStreamRef readStream, int64_t size); -bool writeFile(CFURLRef tzdataUrl, std::string fileName, std::string data, - int64_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)) + namespace iOSUtils { - 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) -{ - const char *TAR_TMP_PATH = "/tmp.tar"; - - // create Library path - CFStringRef libraryStr = CFStringCreateWithCString(NULL, INTERNAL_DIR, - CFStringGetSystemEncoding()); - CFURLRef libraryUrl = CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault, - homeUrl, libraryStr, - false); - - // create tzdata path - CFStringRef tzdataPathRef = CFStringCreateWithCString(NULL, std::string(std::string(INTERNAL_DIR) + "/" + TZDATA_DIR).c_str(), - CFStringGetSystemEncoding()); - CFURLRef tzdataPathUrl = CFURLCreateCopyAppendingPathComponent(NULL, homeUrl, - tzdataPathRef, false); - - // create src archive path - CFStringRef archivePath = CFURLCopyPath(archiveUrl); - gzFile tarFile = gzopen(convertCFStringRefPathToCStringPath(archivePath).c_str(), "rb"); - - // create tar unpacking path - CFStringRef tarName = CFStringCreateWithCString(NULL, TAR_TMP_PATH, - CFStringGetSystemEncoding()); - CFURLRef tarUrl = CFURLCreateCopyAppendingPathComponent(NULL, libraryUrl, tarName, - false); - CFStringRef tarPathRef = CFURLCopyPath(tarUrl); - auto tarPath = convertCFStringRefPathToCStringPath(tarPathRef); - - // create tzdata directory - mkdir(destPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); - - // create stream - CFWriteStreamRef writeStream = CFWriteStreamCreateWithFile(NULL, tarUrl); - bool success = true; - - CFRelease(libraryStr); - CFRelease(libraryUrl); - CFRelease(tzdataPathRef); - CFRelease(archivePath); - CFRelease(tarName); - CFRelease(tarPathRef); - - if (!CFWriteStreamOpen(writeStream)) - { - CFStreamError err = CFWriteStreamGetError(writeStream); - - if (err.domain == kCFStreamErrorDomainPOSIX) + struct TarInfo { - printf("kCFStreamErrorDomainPOSIX %i\n", err.error); - } - else if(err.domain == kCFStreamErrorDomainMacOSStatus) - { - printf("kCFStreamErrorDomainMacOSStatus %i\n", err.error); - } + char objType; + std::string objName; + size_t realContentSize; // writable size without padding zeroes + size_t blocksContentSize; // adjusted size to 512 bytes blocks + bool success; + }; - success = false; - } - - if (!success) - { - CFRelease(tarUrl); - CFRelease(tzdataPathUrl); - CFRelease(writeStream); - return false; - } - - // ======= extract tar ======== - - unsigned int bufferLength = 1024 * 256; // 256Kb - void *buffer = malloc(bufferLength); - - while (true) - { - int readBytes = gzread(tarFile, buffer, bufferLength); - - if (readBytes > 0) + 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() { - CFIndex writtenBytes = CFWriteStreamWrite(writeStream, (unsigned char*)buffer, - readBytes); + CFTimeZoneRef tzRef = CFTimeZoneCopySystem(); + CFStringRef tzNameRef = CFTimeZoneGetName(tzRef); + CFIndex bufferSize = CFStringGetLength(tzNameRef) + 1; + char buffer[bufferSize]; - if (writtenBytes < 0) + if (CFStringGetCString(tzNameRef, buffer, bufferSize, kCFStringEncodingUTF8)) { - CFStreamError err = CFWriteStreamGetError(writeStream); - printf("write stream error %i\n", err.error); - success = false; - break; + CFRelease(tzRef); + return std::string(buffer); } - } - 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; - } - } - - CFWriteStreamClose(writeStream); - CFRelease(writeStream); - free(buffer); - gzclose(tarFile); - - if (!success) - { - CFRelease(tarUrl); - CFRelease(tzdataPathUrl); - 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"); - CFRelease(tarUrl); - CFRelease(tzdataPathUrl); - return false; - } - int64_t tarSize = stat_buf.st_size; - - // create read stream - CFReadStreamRef readStream = CFReadStreamCreateWithFile(kCFAllocatorDefault, tarUrl); - CFRelease(tarUrl); - - if (!CFReadStreamOpen(readStream)) - { - CFStreamError err = CFReadStreamGetError(readStream); - - if (err.domain == kCFStreamErrorDomainPOSIX) - { - printf("kCFStreamErrorDomainPOSIX %i", err.error); - } - else if(err.domain == kCFStreamErrorDomainMacOSStatus) - { - printf("kCFStreamErrorDomainMacOSStatus %i", err.error); + + CFRelease(tzRef); + + return ""; } - success = false; - } - - if (!success) - { - CFRelease(tzdataPathUrl); - CFRelease(readStream); - return false; - } - - // process files - while (location < tarSize) - { - TarInfo info = getTarObjectInfo(readStream, location); - - if (!info.success || info.realContentSize == 0) + std::string + get_tzdata_path() { - break; // something wrong or all files are read - } - - switch (info.objType) - { - case '0': // file - case '\0': // + 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) { - std::string obj = getTarObject(readStream, 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); + printf("tzdata dir exists\n"); #endif - writeFile(tzdataPathUrl, info.objName, obj, info.realContentSize); - location += info.blocksContentSize; - - break; + 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; } - } - - CFRelease(tzdataPathUrl); - CFReadStreamClose(readStream); - CFRelease(readStream); - - return true; -} -TarInfo -getTarObjectInfo(CFReadStreamRef readStream, int64_t location) -{ - int64_t length = TAR_BLOCK_SIZE; - uint8_t buffer[length]; - - char type; - char name[TAR_NAME_SIZE + 1]; - char sizeBuf[TAR_SIZE_SIZE + 1]; - CFIndex bytesRead; - - bytesRead = CFReadStreamRead(readStream, buffer, length); - - if (bytesRead < 0) - { - CFStreamError err = CFReadStreamGetError(readStream); - printf("error reading tar object info %i", err.error); - return {false}; - } - - 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); - int64_t realSize = strtol(sizeBuf, NULL, 8); - int64_t blocksSize = realSize + (TAR_BLOCK_SIZE - (realSize % TAR_BLOCK_SIZE)); - - return {type, std::string(name), realSize, blocksSize, true}; -} - -std::string -getTarObject(CFReadStreamRef readStream, int64_t size) -{ - uint8_t buffer[size]; - - CFIndex bytesRead = CFReadStreamRead(readStream, buffer, size); - - if (bytesRead < 0) - { - CFStreamError err = CFReadStreamGetError(readStream); - printf("error reading tar object info %i", err.error); - } - - return std::string((char *)buffer); -} - -bool -writeFile(CFURLRef tzdataUrl, std::string fileName, std::string data, - int64_t realContentSize) -{ - // create stream - CFStringRef fileNameRef = CFStringCreateWithCString(NULL, fileName.c_str(), - CFStringGetSystemEncoding()); - CFURLRef url = CFURLCreateCopyAppendingPathComponent(NULL, tzdataUrl, fileNameRef, - false); - CFWriteStreamRef writeStream = CFWriteStreamCreateWithFile(NULL, url); - - CFRelease(fileNameRef); - CFRelease(url); - - // open stream - if (!CFWriteStreamOpen(writeStream)) - { - CFStreamError err = CFWriteStreamGetError(writeStream); - - if (err.domain == kCFStreamErrorDomainPOSIX) + std::string + convertCFStringRefPathToCStringPath(CFStringRef ref) { - printf("kCFStreamErrorDomainPOSIX %i\n", err.error); + CFIndex bufferSize = CFStringGetMaximumSizeOfFileSystemRepresentation(ref); + char *buffer = new char[bufferSize]; + CFStringGetFileSystemRepresentation(ref, buffer, bufferSize); + auto result = std::string(buffer); + delete[] buffer; + return result; } - else if(err.domain == kCFStreamErrorDomainMacOSStatus) + + bool + extractTzdata(CFURLRef homeUrl, CFURLRef archiveUrl, std::string destPath) { - printf("kCFStreamErrorDomainMacOSStatus %i\n", err.error); + 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; } - - CFRelease(writeStream); - return false; - } - - // trim empty space - uint8_t trimmedData[realContentSize + 1]; - memset(&trimmedData, '\0', realContentSize); - memcpy(&trimmedData, data.c_str(), realContentSize); - - // write - CFIndex writtenBytes = CFWriteStreamWrite(writeStream, trimmedData, realContentSize); - - if (writtenBytes < 0) - { - CFStreamError err = CFWriteStreamGetError(writeStream); - printf("write stream error %i\n", err.error); - } - - CFWriteStreamClose(writeStream); - CFRelease(writeStream); - writeStream = NULL; - - return true; -} - -} // namespace iOSUtils + + 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