Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
/*----------------------------------------------------------------------
|   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;
}