| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| #include <string.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <stdint.h> |
| #include "DFPlatform.h" |
| #include "zlib.h" |
| |
| #pragma pack(1) |
| typedef struct { |
| uint32_t signature; // 0x06054b50 |
| uint16_t diskNumber; // NOT USED |
| uint16_t centralDirectoryDiskNumber; // NOT USED |
| uint16_t numEntriesThisDisk; // NOT USED |
| uint16_t numEntries; // Number of files in Zip |
| uint32_t centralDirectorySize; // NOT USED |
| uint32_t centralDirectoryOffset; // Offset of directory in file (fseek) |
| uint16_t zipCommentLength; // NOT USED |
| // Followed by .ZIP file comment (variable size) |
| } ZipEndRecord; |
| #pragma pack() |
| static const uint32_t ZipEndRecord_signature = 0x06054B50; |
| |
| #pragma pack(1) |
| typedef struct { |
| uint32_t signature; // 0x02014B50 |
| uint16_t versionMadeBy; // NOT USED |
| uint16_t versionNeededToExtract; // NOT USED |
| uint16_t generalPurposeBitFlag; // NOT USED |
| uint16_t compressionMethod; // NOT USED |
| uint16_t lastModFileTime; // NOT USED |
| uint16_t lastModFileDate; // NOT USED |
| uint32_t crc32; // crc32 |
| uint32_t compressedSize; // NOT USED |
| uint32_t uncompressedSize; // NOT USED |
| uint16_t fileNameLength; // Only used to skip to next record |
| uint16_t extraFieldLength; // Only used to skip to next record |
| uint16_t fileCommentLength; // Only used to skip to next record |
| uint16_t diskNumberStart; // NOT USED |
| uint16_t internalFileAttributes; // NOT USED |
| uint32_t externalFileAttributes; // NOT USED |
| uint32_t relativeOffsetOflocalHeader; // offset to file header |
| } ZipDirectoryRecord; |
| #pragma pack() |
| static const uint32_t ZipDirectoryRecord_signature = 0x02014B50; |
| |
| #pragma pack(1) |
| typedef struct { |
| uint32_t signature; // 0x04034B50 |
| uint16_t versionNeededToExtract; // NOT USED |
| uint16_t generalPurposeBitFlag; // NOT USED |
| uint16_t compressionMethod; // flat file or zip compressed |
| uint16_t lastModFileTime; // NOT USED |
| uint16_t lastModFileDate; // NOT USED |
| uint32_t crc32; // NOT USED |
| uint32_t compressedSize; // size to read from file |
| uint32_t uncompressedSize; // size in memory |
| uint16_t fileNameLength; // Length of file name |
| uint16_t extraFieldLength; // NOT USED |
| } ZipFileHeader; |
| #pragma pack() |
| static const uint32_t ZipFileHeader_signature = 0x04034B50; |
| static const int FILECOUNT_ALLOC_SIZE = 5; |
| |
| |
| |
| static int readDirectory(FILE *zipFile, DFextZipHandleP zipHandle) |
| { |
| unsigned long fileSize, readBytes; |
| unsigned char workBuf[4096]; |
| int i, x, zipOffset; |
| |
| |
| //***** Read EndRecord ***** |
| // the EndRecord contains information, where the directory is located |
| |
| |
| // find end of file, and calculate size |
| if (fseek(zipFile, 0, SEEK_END) |
| || (fileSize = ftell(zipFile)) <= sizeof(ZipEndRecord)) |
| return -1; |
| |
| // Read size of workBuf of filesize from end of file to locate EndRecord |
| readBytes = (fileSize < sizeof(workBuf)) ? fileSize : sizeof(workBuf); |
| if (fseek(zipFile, fileSize - readBytes, SEEK_SET) |
| || fread(workBuf, 1, readBytes, zipFile) < readBytes) |
| return -1; |
| |
| // search for EndRecord signature |
| for (i = readBytes - sizeof(ZipEndRecord); i >= 0; i--) { |
| ZipEndRecord *recEnd = (ZipEndRecord *)(workBuf + i); |
| |
| // check if we have a signature |
| if (recEnd->signature == ZipEndRecord_signature) { |
| // update zipHandle |
| zipOffset = recEnd->centralDirectoryOffset; |
| zipHandle->zipFileCount = recEnd->numEntries; |
| zipHandle->zipFileEntries = xmalloc(zipHandle->zipFileCount * sizeof(DFextZipDirEntry)); |
| break; |
| } |
| } |
| if (i < 0) |
| return -1; |
| |
| |
| //***** Read Directory ***** |
| // Each file has a global and a local entry, read both in a loop |
| |
| |
| // Find firs directory entry |
| if (fseek(zipFile, zipOffset, SEEK_SET)) |
| return -1; |
| |
| // loop through all entries |
| |
| |
| for (i = 0; i < zipHandle->zipFileCount; i++) { |
| ZipDirectoryRecord *recDir = (ZipDirectoryRecord *)workBuf; |
| DFextZipDirEntry *dirEntry = &zipHandle->zipFileEntries[i]; |
| |
| // Find next directory entry, read it and verify signature |
| if (fread(workBuf, 1, sizeof(ZipDirectoryRecord), zipFile) < sizeof(ZipDirectoryRecord) |
| || recDir->signature != ZipDirectoryRecord_signature) |
| return -1; |
| |
| dirEntry->compressedSize = recDir->compressedSize; |
| dirEntry->uncompressedSize = recDir->uncompressedSize; |
| dirEntry->compressionMethod = recDir->compressionMethod; |
| dirEntry->offset = recDir->relativeOffsetOflocalHeader; |
| dirEntry->crc32 = recDir->crc32; |
| |
| // Add filename |
| dirEntry->fileName = xmalloc(recDir->fileNameLength + 1); |
| if (fread(dirEntry->fileName, 1, recDir->fileNameLength, zipFile) < (unsigned long)recDir->fileNameLength) |
| return -1; |
| dirEntry->fileName[recDir->fileNameLength] = '\0'; |
| |
| // Skip extra info and store pointer at next entry |
| x = recDir->extraFieldLength + recDir->fileCommentLength; |
| if (x && fseek(zipFile, x, SEEK_CUR)) |
| return -1; |
| }; |
| |
| return 0; |
| } |
| |
| |
| |
| static void releaseMemory(DFextZipHandleP zipHandle) { |
| if (zipHandle) { |
| int count = zipHandle->zipCreateMode ? zipHandle->zipCreateMode : zipHandle->zipFileCount; |
| if (count) { |
| for (int i = 0; i < count; i++) { |
| DFextZipDirEntry *zipDirEntry = &zipHandle->zipFileEntries[i]; |
| if (zipDirEntry->fileName != NULL) |
| free(zipDirEntry->fileName); |
| } |
| free(zipHandle->zipFileEntries); |
| } |
| free(zipHandle); |
| } |
| } |
| |
| |
| |
| static void writeGlobalDirAndEndRecord(DFextZipHandleP zipHandle) { |
| ZipDirectoryRecord dirRecord; |
| ZipEndRecord endRecord; |
| int i; |
| |
| |
| // Prepare constant part of records |
| endRecord.signature = ZipEndRecord_signature; |
| endRecord.diskNumber = endRecord.centralDirectoryDiskNumber = endRecord.numEntriesThisDisk = 0; |
| endRecord.numEntriesThisDisk = |
| endRecord.numEntries = zipHandle->zipFileCount; |
| endRecord.centralDirectoryOffset = ftell(zipHandle->zipFile); |
| endRecord.zipCommentLength = 0; |
| |
| dirRecord.signature = ZipDirectoryRecord_signature; |
| dirRecord.versionMadeBy = 0; |
| dirRecord.versionNeededToExtract = 20; |
| dirRecord.lastModFileDate = 32; |
| dirRecord.lastModFileTime = dirRecord.crc32 = dirRecord.extraFieldLength = |
| dirRecord.fileCommentLength = dirRecord.diskNumberStart = 0; |
| dirRecord.internalFileAttributes = 1; |
| dirRecord.generalPurposeBitFlag = 0x0000; |
| dirRecord.externalFileAttributes = 0; |
| |
| // loop through all directory entries, write to disk while collecting size |
| endRecord.centralDirectorySize = 0; |
| for (i = 0; i < zipHandle->zipFileCount; i++) { |
| dirRecord.compressionMethod = zipHandle->zipFileEntries[i].compressionMethod; |
| dirRecord.compressedSize = zipHandle->zipFileEntries[i].compressedSize; |
| dirRecord.uncompressedSize = zipHandle->zipFileEntries[i].uncompressedSize; |
| dirRecord.fileNameLength = strlen(zipHandle->zipFileEntries[i].fileName); |
| dirRecord.relativeOffsetOflocalHeader = zipHandle->zipFileEntries[i].offset; |
| dirRecord.crc32 = zipHandle->zipFileEntries[i].crc32; |
| endRecord.centralDirectorySize += sizeof(ZipDirectoryRecord) + dirRecord.fileNameLength; |
| fwrite(&dirRecord, 1, sizeof(ZipDirectoryRecord), zipHandle->zipFile); |
| fwrite(zipHandle->zipFileEntries[i].fileName, 1, dirRecord.fileNameLength, zipHandle->zipFile); |
| } |
| |
| // and finally the end record |
| fwrite(&endRecord, 1, sizeof(ZipEndRecord), zipHandle->zipFile); |
| } |
| |
| |
| |
| DFextZipHandleP DFextZipOpen(const char *zipFilename) { |
| DFextZipHandleP zipHandle = xmalloc(sizeof(DFextZipHandle)); |
| |
| // open zip file for reading |
| zipHandle->zipCreateMode = zipHandle->zipFileCount = 0; |
| zipHandle->zipFile = fopen(zipFilename, "rb"); |
| if (zipHandle->zipFile |
| && !readDirectory(zipHandle->zipFile, zipHandle)) |
| return zipHandle; |
| |
| // release memory |
| releaseMemory(zipHandle); |
| return NULL; |
| } |
| |
| |
| unsigned char *DFextZipReadFile(DFextZipHandleP zipHandle, DFextZipDirEntryP zipEntry) { |
| unsigned char *fileBuf = xmalloc(zipEntry->uncompressedSize); |
| ZipFileHeader recFile; |
| z_stream strm; |
| |
| |
| // Position in front of file |
| if (fseek(zipHandle->zipFile, zipEntry->offset, SEEK_SET) |
| || fread(&recFile, 1, sizeof(ZipFileHeader), zipHandle->zipFile) < sizeof(ZipFileHeader) |
| || recFile.signature != ZipFileHeader_signature |
| || fseek(zipHandle->zipFile, recFile.extraFieldLength + recFile.fileNameLength, SEEK_CUR)) { |
| free(fileBuf); |
| return NULL; |
| } |
| |
| // interesting a zip file that is uncompressed, have to handle that |
| if (zipEntry->compressionMethod != Z_DEFLATED) { |
| if (fread(fileBuf, 1, zipEntry->uncompressedSize, zipHandle->zipFile) < (unsigned long)zipEntry->uncompressedSize |
| || ferror(zipHandle->zipFile)) { |
| free(fileBuf); |
| fileBuf = NULL; |
| } |
| return fileBuf; |
| } |
| |
| |
| //***** Handle zlib inflate ***** |
| |
| |
| strm.zalloc = Z_NULL; |
| strm.zfree = strm.opaque = strm.next_in = Z_NULL; |
| strm.avail_in = 0; |
| |
| // Use inflateInit2 with negative window bits to indicate raw data |
| if (inflateInit2(&strm, -MAX_WBITS) != Z_OK) { |
| free(fileBuf); |
| return NULL; |
| } |
| |
| // Read compressed data |
| unsigned char *comprBuf = xmalloc(zipEntry->compressedSize); |
| if (fread(comprBuf, 1, zipEntry->compressedSize, zipHandle->zipFile) < (unsigned long)zipEntry->compressedSize |
| || ferror(zipHandle->zipFile)) { |
| free(fileBuf); |
| free(comprBuf); |
| return NULL; |
| } |
| |
| // and inflate data |
| strm.avail_in = zipEntry->compressedSize; |
| strm.next_in = comprBuf; |
| strm.avail_out = zipEntry->uncompressedSize; |
| strm.next_out = fileBuf; |
| if (inflate(&strm, Z_NO_FLUSH) == Z_STREAM_ERROR) { |
| free(fileBuf); |
| free(comprBuf); |
| return NULL; |
| } |
| |
| free(comprBuf); |
| inflateEnd(&strm); |
| return fileBuf; |
| } |
| |
| |
| |
| DFextZipHandleP DFextZipCreate(const char *zipFilename) { |
| DFextZipHandleP zipHandle = xmalloc(sizeof(DFextZipHandle)); |
| int memSize; |
| // Open file for write |
| if ((zipHandle->zipFile = fopen(zipFilename, "wb")) == NULL) { |
| free(zipHandle); |
| return NULL; |
| } |
| |
| // prepare to add files |
| zipHandle->zipFileCount = 0; |
| zipHandle->zipCreateMode = FILECOUNT_ALLOC_SIZE; |
| memSize = zipHandle->zipCreateMode * sizeof(DFextZipDirEntry); |
| |
| zipHandle->zipFileEntries = xmalloc(memSize); |
| bzero(zipHandle->zipFileEntries, memSize); |
| return zipHandle; |
| } |
| |
| |
| |
| DFextZipDirEntryP DFextZipWriteFile(DFextZipHandleP zipHandle, const char *fileName, const void *buf, const int len) { |
| z_stream strm; |
| unsigned char *outbuf; |
| ZipFileHeader header; |
| int fileNameLength = strlen(fileName); |
| |
| // do we have space for one more entry ? |
| if (zipHandle->zipFileCount >= zipHandle->zipCreateMode) { |
| zipHandle->zipCreateMode += FILECOUNT_ALLOC_SIZE; |
| zipHandle->zipFileEntries = xrealloc(zipHandle->zipFileEntries, zipHandle->zipCreateMode * sizeof(DFextZipDirEntry)); |
| bzero(&zipHandle->zipFileEntries[zipHandle->zipFileCount], FILECOUNT_ALLOC_SIZE * sizeof(DFextZipDirEntry)); |
| } |
| |
| // prepare local and global file entry |
| DFextZipDirEntryP entryPtr = &zipHandle->zipFileEntries[zipHandle->zipFileCount++]; |
| entryPtr->offset = ftell(zipHandle->zipFile); |
| entryPtr->uncompressedSize = len; |
| entryPtr->fileName = xmalloc(fileNameLength + 1); |
| entryPtr->compressionMethod = Z_DEFLATED; |
| entryPtr->crc32 = crc32(0L, Z_NULL, 0); |
| entryPtr->crc32 = crc32(entryPtr->crc32, buf, len); |
| |
| strcpy(entryPtr->fileName, fileName); |
| |
| // prepare to deflate |
| strm.zalloc = Z_NULL; |
| strm.zfree = strm.opaque = Z_NULL; |
| if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK) |
| return NULL; |
| |
| // deflate buffer |
| strm.next_in = buf; |
| strm.avail_in = len; |
| strm.avail_out = deflateBound(&strm, len); |
| strm.next_out = (Bytef *)xmalloc(strm.avail_out); |
| outbuf = strm.next_out; |
| if (deflate(&strm, Z_FINISH) != Z_STREAM_END) { |
| free(outbuf); |
| return NULL; |
| } |
| deflateEnd(&strm); |
| entryPtr->compressedSize = strm.total_out; |
| |
| // prepare local header |
| header.versionNeededToExtract = 20; |
| header.generalPurposeBitFlag = 0; |
| header.lastModFileDate = 32; |
| header.lastModFileTime = header.extraFieldLength = header.crc32 = 0; |
| header.signature = ZipFileHeader_signature; |
| header.compressionMethod = entryPtr->compressionMethod; |
| header.compressedSize = entryPtr->compressedSize; |
| header.uncompressedSize = entryPtr->uncompressedSize; |
| header.fileNameLength = fileNameLength; |
| header.crc32 = entryPtr->crc32; |
| |
| // put data to file |
| fwrite(&header, 1, sizeof(header), zipHandle->zipFile); |
| fwrite(entryPtr->fileName, 1, fileNameLength, zipHandle->zipFile); |
| fwrite(outbuf, 1, header.compressedSize, zipHandle->zipFile); // skip CMD bytes in front |
| |
| // cleanup |
| free(outbuf); |
| return entryPtr; |
| } |
| |
| |
| |
| void DFextZipClose(DFextZipHandleP zipHandle) |
| { |
| if (zipHandle->zipCreateMode) |
| writeGlobalDirAndEndRecord(zipHandle); |
| |
| fclose(zipHandle->zipFile); |
| releaseMemory(zipHandle); |
| } |