Line data Source code
1 : /*
2 : Zipios -- a small C++ library that provides easy access to .zip files.
3 :
4 : Copyright (C) 2000-2007 Thomas Sondergaard
5 : Copyright (C) 2015-2019 Made to Order Software Corporation
6 :
7 : This library is free software; you can redistribute it and/or
8 : modify it under the terms of the GNU Lesser General Public
9 : License as published by the Free Software Foundation; either
10 : version 2.1 of the License, or (at your option) any later version.
11 :
12 : This library is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : Lesser General Public License for more details.
16 :
17 : You should have received a copy of the GNU Lesser General Public
18 : License along with this library; if not, write to the Free Software
19 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 : */
21 :
22 : /** \file
23 : * \brief Implementation of the zipios::ZipLocalEntry class.
24 : *
25 : * This file is the implementation of the zipios::ZipLocalEntry class
26 : * which handles zipios::FileEntry's found in Zip archives.
27 : */
28 :
29 : #include "ziplocalentry.hpp"
30 :
31 : #include "zipios/zipiosexceptions.hpp"
32 : #include "zipios/dosdatetime.hpp"
33 :
34 : #include "zipios_common.hpp"
35 :
36 :
37 : namespace zipios
38 : {
39 :
40 :
41 : /** \brief Various definitions for local blocks.
42 : *
43 : * The ZipLocalEntry needs a signature, a flag, and makes use
44 : * of a structure declaration (although we cannot really use
45 : * that structure.)
46 : */
47 : namespace
48 : {
49 :
50 : /** \brief The signature of a local entry.
51 : *
52 : * This value represents the signature of a Zip archive block defining
53 : * a ZipLocalEntry.
54 : *
55 : * \code
56 : * "PK 3.4"
57 : * \endcode
58 : */
59 : uint32_t const g_signature = 0x04034b50;
60 :
61 :
62 : /** \brief A bit in the general purpose flags.
63 : *
64 : * This mask is used to know whether the size and CRC are saved in
65 : * the header or after the header. At this time Zipios does not
66 : * support such trailing data as it makes use of the compressed
67 : * and uncompressed sizes to properly stream the output data.
68 : *
69 : * This is bit 3. (see point 4.4.4 in doc/zip-format.txt)
70 : */
71 : uint16_t const g_trailing_data_descriptor = 1 << 3;
72 :
73 :
74 : /** \brief ZipLocalEntry Header
75 : *
76 : * This structure shows how the header of the ZipLocalEntry is defined.
77 : * Note that the file name and extra field have a variable size which
78 : * is defined in two 16 bit values just before they appear.
79 : *
80 : * The filename cannot be empty, however, the extra field can (and
81 : * usually is).
82 : *
83 : * \note
84 : * This structure is NOT used directly only for its sizeof() and
85 : * documentation because that way zipios can work on little and big
86 : * endians without the need to know the endianness of your computer.
87 : */
88 : struct ZipLocalEntryHeader
89 : {
90 : uint32_t m_signature;
91 : uint16_t m_extract_version;
92 : uint16_t m_general_purpose_bitfield;
93 : uint16_t m_compress_method;
94 : uint32_t m_dosdatetime;
95 : uint32_t m_crc_32;
96 : uint32_t m_compressed_size;
97 : uint32_t m_uncompressed_size;
98 : uint16_t m_filename_len;
99 : uint16_t m_extra_field_len;
100 : //uint8_t m_filename[m_filename_len];
101 : //uint8_t m_extra_field[m_extra_field_len];
102 : };
103 :
104 :
105 : } // no name namespace
106 :
107 :
108 :
109 : /** \class ZipLocalEntry
110 : * \brief An implementation of the FileEntry for Zip archives.
111 : *
112 : * A concrete implementation of the abstract FileEntry base class for
113 : * ZipFile entries, specifically for representing the information
114 : * present in the local headers of file entries in a zip file.
115 : */
116 :
117 :
118 :
119 : /** \brief Create a default ZipLocalEntry objects.
120 : *
121 : * This constructor is used to create a default ZipLocalEntry object.
122 : */
123 313973 : ZipLocalEntry::ZipLocalEntry()
124 313973 : : FileEntry(FilePath(""))
125 : //, m_extract_version(g_zip_format_version) -- auto-init
126 : //, m_general_purpose_bitfield(0) -- auto-init
127 : //, m_is_directory(false)
128 : //, m_compressed_size(0) -- auto-init
129 : {
130 313973 : }
131 :
132 :
133 : /** \brief Copy of the ZipLocalEntry from any kind of FileEntry object.
134 : *
135 : * This function is used when copying a DirectoryEntry to a
136 : * ZipCentralDirectoryEntry object when creating a copy while
137 : * saving a Zip archive.
138 : *
139 : * \param[in] src The source to copy in this new ZipLocalEntry.
140 : */
141 171467 : ZipLocalEntry::ZipLocalEntry(FileEntry const & src)
142 : : FileEntry(src)
143 : //, m_extract_version(g_zip_format_version) -- auto-init
144 : //, m_general_purpose_bitfield(0) -- auto-init
145 171467 : , m_is_directory(src.isDirectory())
146 : //, m_compressed_size(0) -- auto-init
147 : {
148 171467 : }
149 :
150 :
151 : /** \brief Create a clone of a ZipLocalEntry object.
152 : *
153 : * This function allocates a new ZipLocalEntry on the heap and returns
154 : * a copy of this ZipLocalEntry object in it.
155 : *
156 : * \return A new ZipLocalEntry which is a clone of this ZipLocalEntry object.
157 : */
158 : FileEntry::pointer_t ZipLocalEntry::clone() const // LCOV_EXCL_LINE
159 : {
160 : // It makes sense to keep the clone() function for this class
161 : // but since it is internal and never allocated as is (we use
162 : // the ZipCentralDirectoryEntry instead) it is marked as none
163 : // reachable by the coverage tests
164 : return FileEntry::pointer_t(new ZipLocalEntry(*this)); // LCOV_EXCL_LINE
165 : }
166 :
167 :
168 : /** \brief Clean up a ZipLocalEntry object.
169 : *
170 : * This function ensures proper clean up of a ZipLocationEntry object.
171 : */
172 486513 : ZipLocalEntry::~ZipLocalEntry()
173 : {
174 486513 : }
175 :
176 :
177 : /** \brief Check whether the filename represents a directory.
178 : *
179 : * This function checks the last character of the filename, if it
180 : * is a separator ('/') then the function returns true meaning
181 : * that the file represents a directory.
182 : *
183 : * \return true if the entry represents a directory.
184 : */
185 1350672 : bool ZipLocalEntry::isDirectory() const
186 : {
187 1350672 : return m_is_directory;
188 : }
189 :
190 :
191 : /** \brief Compare two file entries for equality.
192 : *
193 : * This function compares most of the fields between two file
194 : * entries to see whether they are equal or not.
195 : *
196 : * \note
197 : * This function calls the base class isEqual() and also verifies
198 : * that the ZipLocalEntry fields are equal.
199 : *
200 : * \note
201 : * This function is also used to compare ZipCDirEntry since none
202 : * of the additional field participate in the comparison.
203 : *
204 : * \param[in] file_entry The file entry to compare this against.
205 : *
206 : * \return true if both FileEntry objects are considered equal.
207 : */
208 109722 : bool ZipLocalEntry::isEqual(FileEntry const & file_entry) const
209 : {
210 109722 : ZipLocalEntry const * const ze(dynamic_cast<ZipLocalEntry const * const>(&file_entry));
211 109722 : if(ze == nullptr)
212 : {
213 1137 : return false;
214 : }
215 108585 : return FileEntry::isEqual(file_entry)
216 108575 : && m_extract_version == ze->m_extract_version
217 108575 : && m_general_purpose_bitfield == ze->m_general_purpose_bitfield
218 217160 : && m_is_directory == ze->m_is_directory;
219 : //&& m_compressed_size == ze->m_compressed_size -- ignore in comparison
220 : }
221 :
222 :
223 : /** \brief Retrieve the size of the file when compressed.
224 : *
225 : * This function returns the compressed size of the entry. If the
226 : * entry is not stored in a compressed format, the uncompressed
227 : * size is returned.
228 : *
229 : * \return The compressed size of the entry.
230 : */
231 12482 : size_t ZipLocalEntry::getCompressedSize() const
232 : {
233 12482 : return m_compressed_size;
234 : }
235 :
236 :
237 : /** \brief Retrieve the size of the header.
238 : *
239 : * This function dynamically determines the size of the Zip archive
240 : * header necessary for this FileEntry.
241 : *
242 : * This function returns the size of the Zip archive header.
243 : *
244 : * \return The size of the header in bytes.
245 : */
246 171466 : size_t ZipLocalEntry::getHeaderSize() const
247 : {
248 : // Note that the structure is 32 bytes because of an alignment
249 : // and attempting to use options to avoid the alignment would
250 : // not be portable so we use a hard coded value (yuck!)
251 : return 30 /* sizeof(ZipLocalEntryHeader) */
252 171466 : + m_filename.length() + (m_is_directory ? 1 : 0)
253 171466 : + m_extra_field.size();
254 : }
255 :
256 :
257 : /** \brief Set the size when the file is compressed.
258 : *
259 : * This function saves the compressed size of the entry in this object.
260 : *
261 : * By default the compressed size is viewed as the same as the
262 : * uncompressed size (i.e. as if STORED was used for the compression
263 : * method.)
264 : *
265 : * \param[in] size Value to set the compressed size field of the entry to.
266 : */
267 171466 : void ZipLocalEntry::setCompressedSize(size_t size)
268 : {
269 171466 : m_compressed_size = size;
270 171466 : }
271 :
272 :
273 : /** \brief Save the CRC of the entry.
274 : *
275 : * This function saves the CRC field in this FileEntry field.
276 : *
277 : * This function has the side of setting the m_has_crc_32 flag
278 : * to true meaning that the CRC was defined as expected.
279 : *
280 : * \param[in] crc Value to set the CRC field to.
281 : */
282 171466 : void ZipLocalEntry::setCrc(uint32_t crc)
283 : {
284 171466 : m_crc_32 = crc;
285 171466 : m_has_crc_32 = true;
286 171466 : }
287 :
288 :
289 : /** \brief Is there a trailing data descriptor?
290 : *
291 : * This function checks the bit in the General Purpose Flags to know
292 : * whether the local entry header includes the compressed and uncompressed
293 : * sizes or whether these are defined after the compressed data.
294 : *
295 : * The trailing data buffer looks like this:
296 : *
297 : * \code
298 : * signature (PK 8.7) -- OPTIONAL -- 32 bit
299 : * CRC 32 -- 32 bit
300 : * compressed size -- 32 or 64 bit
301 : * uncompressed size -- 32 or 64 bit
302 : * \endcode
303 : *
304 : * When a trailing data buffer is defined, the header has the compressed
305 : * and uncompressed sizes set to zero.
306 : *
307 : * \note
308 : * Zipios does not support such a scheme.
309 : *
310 : * \return true if this file makes use of a trailing data buffer.
311 : */
312 96763 : bool ZipLocalEntry::hasTrailingDataDescriptor() const
313 : {
314 96763 : return (m_general_purpose_bitfield & g_trailing_data_descriptor) != 0;
315 : }
316 :
317 :
318 : /** \brief Read one local entry from \p is.
319 : *
320 : * This function verifies that the input stream starts with a local entry
321 : * signature. If so, it reads the input stream for a complete local entry.
322 : *
323 : * Calling this function first marks the FileEntry object as invalid. If
324 : * the read succeeds in full, then the entry is again marked as valid.
325 : *
326 : * If a read fails, the function throws an exception as defined in
327 : * the various zipRead() functions.
328 : *
329 : * \note
330 : * Some of the data found in the local entry on disk are not kept in
331 : * this class because there is nothing we can do with it.
332 : *
333 : * \param[in] is The input stream to read from.
334 : */
335 205358 : void ZipLocalEntry::read(std::istream& is)
336 : {
337 205358 : m_valid = false; // set to true upon successful completion.
338 :
339 : // // Before reading anything we record the position in the stream
340 : // // This is a field in the central directory entry, but not
341 : // // in the local entry. After all, we know where we are, anyway.
342 : // zlh.rel_offset_loc_head = is.tellg() ;
343 :
344 : uint32_t signature;
345 205358 : zipRead(is, signature); // 32
346 205358 : if(g_signature != signature)
347 : {
348 : // put stream in error state and return
349 10 : is.setstate(std::ios::failbit);
350 10 : throw IOException("ZipLocalEntry::read() expected a signature but got some other data");
351 : }
352 :
353 205348 : uint16_t compress_method(0);
354 205348 : uint32_t dosdatetime(0);
355 205348 : uint32_t compressed_size(0);
356 205348 : uint32_t uncompressed_size(0);
357 205348 : uint16_t filename_len(0);
358 205348 : uint16_t extra_field_len(0);
359 410696 : std::string filename;
360 :
361 : // See the ZipLocalEntryHeader for more details
362 205348 : zipRead(is, m_extract_version); // 16
363 205348 : zipRead(is, m_general_purpose_bitfield); // 16
364 205348 : zipRead(is, compress_method); // 16
365 205348 : zipRead(is, dosdatetime); // 32
366 205348 : zipRead(is, m_crc_32); // 32
367 205348 : zipRead(is, compressed_size); // 32
368 205348 : zipRead(is, uncompressed_size); // 32
369 205348 : zipRead(is, filename_len); // 16
370 205348 : zipRead(is, extra_field_len); // 16
371 205348 : zipRead(is, filename, filename_len); // string
372 205348 : zipRead(is, m_extra_field, extra_field_len); // buffer
373 : /** \todo add support for zip64, some of those parameters
374 : * may be 0xFFFFF...FFFF in which case the 64 bit
375 : * header should be read
376 : */
377 :
378 : // the FilePath() will remove the trailing slash so make sure
379 : // to defined the m_is_directory ahead of time!
380 205348 : m_is_directory = !filename.empty() && filename.back() == g_separator;
381 :
382 205348 : m_compress_method = static_cast<StorageMethod>(compress_method);
383 205348 : DOSDateTime t;
384 205348 : t.setDOSDateTime(dosdatetime);
385 205348 : m_unix_time = t.getUnixTimestamp();
386 205348 : m_compressed_size = compressed_size;
387 205348 : m_uncompressed_size = uncompressed_size;
388 205348 : m_filename = FilePath(filename);
389 :
390 205348 : m_valid = true;
391 205348 : }
392 :
393 :
394 : /** \brief Write a ZipLocalEntry to \p os.
395 : *
396 : * This function writes this ZipLocalEntry header to the specified
397 : * output stream.
398 : *
399 : * \exception IOException
400 : * If an error occurs while writing to the output stream, the function
401 : * throws an IOException.
402 : *
403 : * \param[in] os The output stream where the ZipLocalEntry is written.
404 : */
405 342933 : void ZipLocalEntry::write(std::ostream& os)
406 : {
407 685866 : if(m_filename.length() > 0x10000
408 342933 : || m_extra_field.size() > 0x10000)
409 : {
410 1 : throw InvalidStateException("ZipLocalEntry::write(): file name or extra field too large to save in a Zip file.");
411 : }
412 :
413 : /** todo: add support for 64 bit zip archive
414 : */
415 : // Solaris defines _ILP32 for 32 bit platforms
416 : #if !defined(_ILP32)
417 342932 : if(m_compressed_size >= 0x100000000UL
418 342932 : || m_uncompressed_size >= 0x100000000UL)
419 : {
420 : // these are really big files, we do not currently test such so ignore in coverage
421 : //
422 : // Note: The compressed size is known at the end, we seek back to
423 : // this header and resave it with the info; thus the error
424 : // is caught then if it was not out of bounds earlier.
425 : throw InvalidStateException("The size of this file is too large to fit in a zip archive."); // LCOV_EXCL_LINE
426 : }
427 : #endif
428 :
429 685864 : std::string filename(m_filename);
430 342932 : if(m_is_directory)
431 : {
432 20556 : filename += g_separator;
433 : }
434 :
435 342932 : uint16_t compress_method(static_cast<uint8_t>(m_compress_method));
436 342932 : if(m_compression_level == COMPRESSION_LEVEL_NONE)
437 : {
438 24108 : compress_method = static_cast<uint8_t>(StorageMethod::STORED);
439 : }
440 :
441 342932 : DOSDateTime t;
442 342932 : t.setUnixTimestamp(m_unix_time);
443 342932 : uint32_t dosdatetime(t.getDOSDateTime()); // type could use DOSDateTime::dosdatetime_t
444 342932 : uint32_t compressed_size(m_compressed_size);
445 342932 : uint32_t uncompressed_size(m_uncompressed_size);
446 342932 : uint16_t filename_len(filename.length());
447 342932 : uint16_t extra_field_len(m_extra_field.size());
448 :
449 : // See the ZipLocalEntryHeader for more details
450 342932 : zipWrite(os, g_signature); // 32
451 342932 : zipWrite(os, m_extract_version); // 16
452 342932 : zipWrite(os, m_general_purpose_bitfield); // 16
453 342932 : zipWrite(os, compress_method); // 16
454 342932 : zipWrite(os, dosdatetime); // 32
455 342932 : zipWrite(os, m_crc_32); // 32
456 342932 : zipWrite(os, compressed_size); // 32
457 342932 : zipWrite(os, uncompressed_size); // 32
458 342932 : zipWrite(os, filename_len); // 16
459 342932 : zipWrite(os, extra_field_len); // 16
460 342932 : zipWrite(os, filename); // string
461 342932 : zipWrite(os, m_extra_field); // buffer
462 342932 : }
463 :
464 :
465 3 : } // zipios namespace
466 :
467 : // Local Variables:
468 : // mode: cpp
469 : // indent-tabs-mode: nil
470 : // c-basic-offset: 4
471 : // tab-width: 4
472 : // End:
473 :
474 : // vim: ts=4 sw=4 et
|