dom/file/ArchiveZipEvent.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:cf7fc6a3096f
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "ArchiveZipEvent.h"
8 #include "ArchiveZipFile.h"
9
10 #include "nsContentUtils.h"
11 #include "nsCExternalHandlerService.h"
12
13 using namespace mozilla::dom;
14
15 USING_FILE_NAMESPACE
16
17 #ifndef PATH_MAX
18 # define PATH_MAX 65536 // The filename length is stored in 2 bytes
19 #endif
20
21 ArchiveZipItem::ArchiveZipItem(const char* aFilename,
22 const ZipCentral& aCentralStruct,
23 const nsACString& aEncoding)
24 : mFilename(aFilename),
25 mCentralStruct(aCentralStruct),
26 mEncoding(aEncoding)
27 {
28 MOZ_COUNT_CTOR(ArchiveZipItem);
29 }
30
31 ArchiveZipItem::~ArchiveZipItem()
32 {
33 MOZ_COUNT_DTOR(ArchiveZipItem);
34 }
35
36 nsresult
37 ArchiveZipItem::ConvertFilename()
38 {
39 if (mEncoding.IsEmpty()) {
40 return NS_ERROR_FAILURE;
41 }
42
43 nsString filenameU;
44 nsresult rv = nsContentUtils::ConvertStringFromEncoding(
45 mEncoding,
46 mFilename, filenameU);
47 NS_ENSURE_SUCCESS(rv, rv);
48
49 if (filenameU.IsEmpty()) {
50 return NS_ERROR_FAILURE;
51 }
52
53 mFilenameU = filenameU;
54 return NS_OK;
55 }
56
57 nsresult
58 ArchiveZipItem::GetFilename(nsString& aFilename)
59 {
60 if (mFilenameU.IsEmpty()) {
61 // Maybe this string is UTF-8:
62 if (IsUTF8(mFilename, false)) {
63 mFilenameU = NS_ConvertUTF8toUTF16(mFilename);
64 }
65
66 // Let's use the enconding value for the dictionary
67 else {
68 nsresult rv = ConvertFilename();
69 NS_ENSURE_SUCCESS(rv, rv);
70 }
71 }
72
73 aFilename = mFilenameU;
74 return NS_OK;
75 }
76
77 // From zipItem to DOMFile:
78 nsIDOMFile*
79 ArchiveZipItem::File(ArchiveReader* aArchiveReader)
80 {
81 nsString filename;
82
83 if (NS_FAILED(GetFilename(filename))) {
84 return nullptr;
85 }
86
87 return new ArchiveZipFile(filename,
88 NS_ConvertUTF8toUTF16(GetType()),
89 StrToInt32(mCentralStruct.orglen),
90 mCentralStruct,
91 aArchiveReader);
92 }
93
94 uint32_t
95 ArchiveZipItem::StrToInt32(const uint8_t* aStr)
96 {
97 return (uint32_t)( (aStr [0] << 0) |
98 (aStr [1] << 8) |
99 (aStr [2] << 16) |
100 (aStr [3] << 24) );
101 }
102
103 uint16_t
104 ArchiveZipItem::StrToInt16(const uint8_t* aStr)
105 {
106 return (uint16_t) ((aStr [0]) | (aStr [1] << 8));
107 }
108
109 // ArchiveReaderZipEvent
110
111 ArchiveReaderZipEvent::ArchiveReaderZipEvent(ArchiveReader* aArchiveReader,
112 const nsACString& aEncoding)
113 : ArchiveReaderEvent(aArchiveReader),
114 mEncoding(aEncoding)
115 {
116 }
117
118 // NOTE: this runs in a different thread!!
119 nsresult
120 ArchiveReaderZipEvent::Exec()
121 {
122 uint32_t centralOffset(0);
123 nsresult rv;
124
125 nsCOMPtr<nsIInputStream> inputStream;
126 rv = mArchiveReader->GetInputStream(getter_AddRefs(inputStream));
127 if (NS_FAILED(rv) || !inputStream) {
128 return RunShare(NS_ERROR_UNEXPECTED);
129 }
130
131 // From the input stream to a seekable stream
132 nsCOMPtr<nsISeekableStream> seekableStream;
133 seekableStream = do_QueryInterface(inputStream);
134 if (!seekableStream) {
135 return RunShare(NS_ERROR_UNEXPECTED);
136 }
137
138 uint64_t size;
139 rv = mArchiveReader->GetSize(&size);
140 if (NS_FAILED(rv)) {
141 return RunShare(NS_ERROR_UNEXPECTED);
142 }
143
144 // Reading backward.. looking for the ZipEnd signature
145 for (uint64_t curr = size - ZIPEND_SIZE; curr > 4; --curr) {
146 seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, curr);
147
148 uint8_t buffer[ZIPEND_SIZE];
149 uint32_t ret;
150
151 rv = inputStream->Read((char*)buffer, sizeof(buffer), &ret);
152 if (NS_FAILED(rv) || ret != sizeof(buffer)) {
153 return RunShare(NS_ERROR_UNEXPECTED);
154 }
155
156 // Here we are:
157 if (ArchiveZipItem::StrToInt32(buffer) == ENDSIG) {
158 centralOffset = ArchiveZipItem::StrToInt32(((ZipEnd*)buffer)->offset_central_dir);
159 break;
160 }
161 }
162
163 // No central Offset
164 if (!centralOffset || centralOffset >= size - ZIPEND_SIZE) {
165 return RunShare(NS_ERROR_FAILURE);
166 }
167
168 // Seek to the first central directory:
169 seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, centralOffset);
170
171 // For each central directory:
172 while (centralOffset <= size - ZIPCENTRAL_SIZE) {
173 ZipCentral centralStruct;
174 uint32_t ret;
175
176 rv = inputStream->Read((char*)&centralStruct, ZIPCENTRAL_SIZE, &ret);
177 if (NS_FAILED(rv) || ret != ZIPCENTRAL_SIZE) {
178 return RunShare(NS_ERROR_UNEXPECTED);
179 }
180
181 uint16_t filenameLen = ArchiveZipItem::StrToInt16(centralStruct.filename_len);
182 uint16_t extraLen = ArchiveZipItem::StrToInt16(centralStruct.extrafield_len);
183 uint16_t commentLen = ArchiveZipItem::StrToInt16(centralStruct.commentfield_len);
184
185 // Point to the next item at the top of loop
186 centralOffset += ZIPCENTRAL_SIZE + filenameLen + extraLen + commentLen;
187 if (filenameLen == 0 || filenameLen >= PATH_MAX || centralOffset >= size) {
188 return RunShare(NS_ERROR_FILE_CORRUPTED);
189 }
190
191 // Read the name:
192 nsAutoArrayPtr<char> filename(new char[filenameLen + 1]);
193 rv = inputStream->Read(filename, filenameLen, &ret);
194 if (NS_FAILED(rv) || ret != filenameLen) {
195 return RunShare(NS_ERROR_UNEXPECTED);
196 }
197
198 filename[filenameLen] = 0;
199
200 // We ignore the directories:
201 if (filename[filenameLen - 1] != '/') {
202 mFileList.AppendElement(new ArchiveZipItem(filename, centralStruct, mEncoding));
203 }
204
205 // Ignore the rest
206 seekableStream->Seek(nsISeekableStream::NS_SEEK_CUR, extraLen + commentLen);
207 }
208
209 return RunShare(NS_OK);
210 }

mercurial