NPT_Result NPT_StreamToStreamCopy(NPT_InputStream& from, NPT_OutputStream& to, NPT_Position offset /* = 0 */, NPT_Size size /* = 0, 0 means the entire stream */) { // seek into the input if required if (offset) { NPT_CHECK(from.Seek(offset)); } // allocate a buffer for the transfer NPT_Size bytes_transfered = 0; NPT_Byte* buffer = new NPT_Byte[NPT_STREAM_COPY_BUFFER_SIZE]; NPT_Result result = NPT_SUCCESS; if (buffer == NULL) return NPT_ERROR_OUT_OF_MEMORY; // copy until an error occurs or the end of stream is reached for (;;) { // read some data NPT_Size bytes_to_read = NPT_STREAM_COPY_BUFFER_SIZE; NPT_Size bytes_read = 0; if (size) { // a max size was specified if (bytes_to_read > size-bytes_transfered) { bytes_to_read = size-bytes_transfered; } } result = from.Read(buffer, bytes_to_read, &bytes_read); if (NPT_FAILED(result)) { if (result == NPT_ERROR_EOS) result = NPT_SUCCESS; break; } if (bytes_read == 0) continue; // write the data result = to.WriteFully(buffer, bytes_read); if (NPT_FAILED(result)) break; // update the counts if (size) { bytes_transfered += bytes_read; if (bytes_transfered >= size) break; } } // free the buffer and return delete[] buffer; return result; }
/*---------------------------------------------------------------------- | NPT_ZipFile::Parse +---------------------------------------------------------------------*/ NPT_Result NPT_ZipFile::Parse(NPT_InputStream& stream, NPT_ZipFile*& file) { // defautl return value file = NULL; // check that we know the size of the stream NPT_LargeSize stream_size = 0; NPT_Result result = stream.GetSize(stream_size); if (NPT_FAILED(result)) { NPT_LOG_WARNING_1("cannot get stream size (%d)", result); return result; } if (stream_size < 22) { NPT_LOG_WARNING("input stream too short"); return NPT_ERROR_INVALID_FORMAT; } // seek to the most likely start of the end of central directory record unsigned int max_eocdr_size = 22+65536; if (max_eocdr_size > stream_size) { max_eocdr_size = (unsigned int)stream_size; } unsigned char eocdr[22]; bool record_found = false; NPT_Position position = 0; for (unsigned int i=0; i<max_eocdr_size; i++) { position = stream_size-22-i; result = stream.Seek(position); if (NPT_FAILED(result)) { NPT_LOG_WARNING_1("seek failed (%d)", result); return result; } result = stream.ReadFully(eocdr, 22); if (NPT_FAILED(result)) { NPT_LOG_WARNING_1("read failed (%d)", result); return result; } NPT_UInt32 signature = NPT_BytesToInt32Le(eocdr); if (signature == NPT_ZIP_END_OF_CENTRAL_DIRECTORY_SIGNATURE) { record_found = true; break; } } if (!record_found) { NPT_LOG_WARNING("eocd record not found at end of stream"); return NPT_ERROR_INVALID_FORMAT; } // parse the eocdr NPT_UInt32 this_disk = NPT_BytesToInt16Le(&eocdr[ 4]); NPT_UInt32 start_disk = NPT_BytesToInt16Le(&eocdr[ 6]); NPT_UInt64 this_disk_entry_count = NPT_BytesToInt16Le(&eocdr[ 8]); NPT_UInt64 total_entry_count = NPT_BytesToInt16Le(&eocdr[10]); NPT_UInt64 central_directory_size = NPT_BytesToInt32Le(&eocdr[12]); NPT_Position central_directory_offset = NPT_BytesToInt32Le(&eocdr[16]); // format check if (this_disk != 0 || start_disk != 0) { return NPT_ERROR_NOT_SUPPORTED; } if (this_disk_entry_count != total_entry_count) { return NPT_ERROR_NOT_SUPPORTED; } // check if this is a zip64 file if (central_directory_offset == 0xFFFFFFFF) { unsigned char zip64_locator[20]; result = stream.Seek(position-20); if (NPT_FAILED(result)) { NPT_LOG_WARNING_1("seek failed (%d)", result); return result; } result = stream.ReadFully(zip64_locator, 20); if (NPT_FAILED(result)) { NPT_LOG_WARNING_1("read failed (%d)", result); return result; } NPT_UInt32 signature = NPT_BytesToInt32Le(&zip64_locator[0]); if (signature != NPT_ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIGNATURE) { NPT_LOG_WARNING("zip64 directory locator signature not found"); return NPT_ERROR_INVALID_FORMAT; } NPT_UInt32 zip64_disk_start = NPT_BytesToInt32Le(&zip64_locator[ 4]); NPT_UInt64 zip64_directory_offset = NPT_BytesToInt64Le(&zip64_locator[ 8]); NPT_UInt32 zip64_disk_count = NPT_BytesToInt32Le(&zip64_locator[16]); // format check if (zip64_disk_start != 0 || zip64_disk_count != 1) { return NPT_ERROR_NOT_SUPPORTED; } // size check if (zip64_directory_offset > stream_size) { NPT_LOG_WARNING("zip64 directory offset too large"); return NPT_ERROR_INVALID_FORMAT; } // load and parse the eocdr64 unsigned char eocdr64[56]; result = stream.Seek(zip64_directory_offset); if (NPT_FAILED(result)) { NPT_LOG_WARNING_1("seek failed (%d)", result); return result; } result = stream.ReadFully(eocdr64, 56); if (NPT_FAILED(result)) { NPT_LOG_WARNING_1("read failed (%d)", result); return result; } signature = NPT_BytesToInt32Le(&eocdr64[0]); if (signature != NPT_ZIP64_END_OF_CENTRAL_DIRECTORY_SIGNATURE) { NPT_LOG_WARNING("zip64 directory signature not found"); return NPT_ERROR_INVALID_FORMAT; } this_disk = NPT_BytesToInt32Le(&eocdr64[16]); start_disk = NPT_BytesToInt32Le(&eocdr64[20]); this_disk_entry_count = NPT_BytesToInt64Le(&eocdr64[24]); total_entry_count = NPT_BytesToInt64Le(&eocdr64[32]); central_directory_size = NPT_BytesToInt64Le(&eocdr64[40]); central_directory_offset = NPT_BytesToInt64Le(&eocdr64[48]); } // format check if (this_disk != 0 || start_disk != 0) { return NPT_ERROR_NOT_SUPPORTED; } if (this_disk_entry_count != total_entry_count) { return NPT_ERROR_NOT_SUPPORTED; } // check that the size looks reasonable if (central_directory_size > NPT_ZIP_MAX_DIRECTORY_SIZE) { NPT_LOG_WARNING("central directory larger than max supported"); return NPT_ERROR_OUT_OF_RANGE; } if (total_entry_count > NPT_ZIP_MAX_ENTRY_COUNT) { NPT_LOG_WARNING("central directory larger than max supported"); return NPT_ERROR_OUT_OF_RANGE; } // read the central directory NPT_DataBuffer central_directory_buffer; result = central_directory_buffer.SetDataSize((NPT_Size)central_directory_size); if (NPT_FAILED(result)) { NPT_LOG_WARNING_1("central directory too large (%lld)", central_directory_size); return result; } result = stream.Seek(central_directory_offset); if (NPT_FAILED(result)) { NPT_LOG_WARNING_1("seek failed (%d)", result); return result; } result = stream.ReadFully(central_directory_buffer.UseData(), (NPT_Size)central_directory_size); if (NPT_FAILED(result)) { NPT_LOG_WARNING_1("failed to read central directory (%d)", result); return result; } // create a new file object file = new NPT_ZipFile(); file->m_Entries.Reserve((NPT_Cardinal)total_entry_count); // parse all entries const unsigned char* buffer = (const unsigned char*)central_directory_buffer.GetData(); for (unsigned int i=0; i<total_entry_count; i++) { NPT_UInt32 signature = NPT_BytesToInt32Le(buffer); if (signature != NPT_ZIP_CENTRAL_FILE_HEADER_SIGNATURE) { NPT_LOG_WARNING("unexpected signature in central directory"); break; } NPT_ZipFile::Entry entry(buffer); if (entry.m_DirectoryEntrySize > central_directory_size) { NPT_LOG_WARNING_1("entry size too large (%d)", entry.m_DirectoryEntrySize); break; } file->GetEntries().Add(entry); central_directory_size -= entry.m_DirectoryEntrySize; buffer += entry.m_DirectoryEntrySize; } return NPT_SUCCESS; }