summaryrefslogtreecommitdiffstats
path: root/dom/archivereader/ArchiveZipEvent.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/archivereader/ArchiveZipEvent.cpp')
-rw-r--r--dom/archivereader/ArchiveZipEvent.cpp216
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*)&centralStruct, 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);
+}