|
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*)¢ralStruct, 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 } |