diff options
Diffstat (limited to 'dom/archivereader/ArchiveZipEvent.cpp')
-rw-r--r-- | dom/archivereader/ArchiveZipEvent.cpp | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/dom/archivereader/ArchiveZipEvent.cpp b/dom/archivereader/ArchiveZipEvent.cpp new file mode 100644 index 000000000..56251eef6 --- /dev/null +++ b/dom/archivereader/ArchiveZipEvent.cpp @@ -0,0 +1,216 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ArchiveZipEvent.h" +#include "ArchiveZipFile.h" + +#include "nsContentUtils.h" +#include "nsCExternalHandlerService.h" + +#include "mozilla/UniquePtr.h" + +using namespace mozilla; +using namespace mozilla::dom; + +USING_ARCHIVEREADER_NAMESPACE + +#ifndef PATH_MAX +# define PATH_MAX 65536 // The filename length is stored in 2 bytes +#endif + +ArchiveZipItem::ArchiveZipItem(const char* aFilename, + const ZipCentral& aCentralStruct, + const nsACString& aEncoding) +: mFilename(aFilename), + mCentralStruct(aCentralStruct), + mEncoding(aEncoding) +{ + MOZ_COUNT_CTOR(ArchiveZipItem); +} + +ArchiveZipItem::~ArchiveZipItem() +{ + MOZ_COUNT_DTOR(ArchiveZipItem); +} + +nsresult +ArchiveZipItem::ConvertFilename() +{ + if (mEncoding.IsEmpty()) { + return NS_ERROR_FAILURE; + } + + nsString filenameU; + nsresult rv = nsContentUtils::ConvertStringFromEncoding( + mEncoding, + mFilename, filenameU); + NS_ENSURE_SUCCESS(rv, rv); + + if (filenameU.IsEmpty()) { + return NS_ERROR_FAILURE; + } + + mFilenameU = filenameU; + return NS_OK; +} + +nsresult +ArchiveZipItem::GetFilename(nsString& aFilename) +{ + if (mFilenameU.IsEmpty()) { + // Maybe this string is UTF-8: + if (IsUTF8(mFilename, false)) { + mFilenameU = NS_ConvertUTF8toUTF16(mFilename); + } + + // Let's use the enconding value for the dictionary + else { + nsresult rv = ConvertFilename(); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + aFilename = mFilenameU; + return NS_OK; +} + +// From zipItem to File: +already_AddRefed<File> +ArchiveZipItem::GetFile(ArchiveReader* aArchiveReader) +{ + nsString filename; + + if (NS_FAILED(GetFilename(filename))) { + return nullptr; + } + + RefPtr<dom::File> file = dom::File::Create(aArchiveReader, + new ArchiveZipBlobImpl(filename, + NS_ConvertUTF8toUTF16(GetType()), + StrToInt32(mCentralStruct.orglen), + mCentralStruct, aArchiveReader->GetBlobImpl())); + MOZ_ASSERT(file); + return file.forget(); +} + +uint32_t +ArchiveZipItem::StrToInt32(const uint8_t* aStr) +{ + return (uint32_t)( (aStr [0] << 0) | + (aStr [1] << 8) | + (aStr [2] << 16) | + (aStr [3] << 24) ); +} + +uint16_t +ArchiveZipItem::StrToInt16(const uint8_t* aStr) +{ + return (uint16_t) ((aStr [0]) | (aStr [1] << 8)); +} + +// ArchiveReaderZipEvent + +ArchiveReaderZipEvent::ArchiveReaderZipEvent(ArchiveReader* aArchiveReader, + const nsACString& aEncoding) +: ArchiveReaderEvent(aArchiveReader), + mEncoding(aEncoding) +{ +} + +// NOTE: this runs in a different thread!! +nsresult +ArchiveReaderZipEvent::Exec() +{ + uint32_t centralOffset(0); + nsresult rv; + + nsCOMPtr<nsIInputStream> inputStream; + rv = mArchiveReader->GetInputStream(getter_AddRefs(inputStream)); + if (NS_FAILED(rv) || !inputStream) { + return RunShare(NS_ERROR_UNEXPECTED); + } + + // From the input stream to a seekable stream + nsCOMPtr<nsISeekableStream> seekableStream; + seekableStream = do_QueryInterface(inputStream); + if (!seekableStream) { + return RunShare(NS_ERROR_UNEXPECTED); + } + + uint64_t size; + rv = mArchiveReader->GetSize(&size); + if (NS_FAILED(rv)) { + return RunShare(NS_ERROR_UNEXPECTED); + } + + // Reading backward.. looking for the ZipEnd signature + for (uint64_t curr = size - ZIPEND_SIZE; curr > 4; --curr) { + seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, curr); + + uint8_t buffer[ZIPEND_SIZE]; + uint32_t ret; + + rv = inputStream->Read((char*)buffer, sizeof(buffer), &ret); + if (NS_FAILED(rv) || ret != sizeof(buffer)) { + return RunShare(NS_ERROR_UNEXPECTED); + } + + // Here we are: + if (ArchiveZipItem::StrToInt32(buffer) == ENDSIG) { + centralOffset = ArchiveZipItem::StrToInt32(((ZipEnd*)buffer)->offset_central_dir); + break; + } + } + + // No central Offset + if (!centralOffset || centralOffset >= size - ZIPEND_SIZE) { + return RunShare(NS_ERROR_FAILURE); + } + + // Seek to the first central directory: + seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, centralOffset); + + // For each central directory: + while (centralOffset <= size - ZIPCENTRAL_SIZE) { + ZipCentral centralStruct; + uint32_t ret; + + rv = inputStream->Read((char*)¢ralStruct, ZIPCENTRAL_SIZE, &ret); + if (NS_FAILED(rv) || ret != ZIPCENTRAL_SIZE) { + return RunShare(NS_ERROR_UNEXPECTED); + } + + uint16_t filenameLen = ArchiveZipItem::StrToInt16(centralStruct.filename_len); + uint16_t extraLen = ArchiveZipItem::StrToInt16(centralStruct.extrafield_len); + uint16_t commentLen = ArchiveZipItem::StrToInt16(centralStruct.commentfield_len); + + // Point to the next item at the top of loop + centralOffset += ZIPCENTRAL_SIZE + filenameLen + extraLen + commentLen; + if (filenameLen == 0 || filenameLen >= PATH_MAX || centralOffset >= size) { + return RunShare(NS_ERROR_FILE_CORRUPTED); + } + + // Read the name: + auto filename = MakeUnique<char[]>(filenameLen + 1); + rv = inputStream->Read(filename.get(), filenameLen, &ret); + if (NS_FAILED(rv) || ret != filenameLen) { + return RunShare(NS_ERROR_UNEXPECTED); + } + + filename[filenameLen] = 0; + + // We ignore the directories: + if (filename[filenameLen - 1] != '/') { + mFileList.AppendElement(new ArchiveZipItem(filename.get(), centralStruct, + mEncoding)); + } + + // Ignore the rest + seekableStream->Seek(nsISeekableStream::NS_SEEK_CUR, extraLen + commentLen); + } + + return RunShare(NS_OK); +} |