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 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 :
33 : #include "dostime.h"
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 endianess 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_dostime;
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 199901 : ZipLocalEntry::ZipLocalEntry()
124 199901 : : 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 199901 : }
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 133066 : 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 133066 : , m_is_directory(src.isDirectory())
146 : //, m_compressed_size(0) -- auto-init
147 : {
148 133066 : }
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 333721 : ZipLocalEntry::~ZipLocalEntry()
173 : {
174 333721 : }
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 863241 : bool ZipLocalEntry::isDirectory() const
186 : {
187 863241 : 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 70479 : bool ZipLocalEntry::isEqual(FileEntry const & file_entry) const
209 : {
210 70479 : ZipLocalEntry const * const ze(dynamic_cast<ZipLocalEntry const * const>(&file_entry));
211 70479 : if(!ze)
212 : {
213 1097 : return false;
214 : }
215 69382 : return FileEntry::isEqual(file_entry)
216 69372 : && m_extract_version == ze->m_extract_version
217 69372 : && m_general_purpose_bitfield == ze->m_general_purpose_bitfield
218 138754 : && m_is_directory == ze->m_is_directory;
219 : //&& m_compressed_size == ze->m_compressed_size -- ignore in comparison
220 : }
221 :
222 :
223 : /** \brief Retrive 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 7421 : size_t ZipLocalEntry::getCompressedSize() const
232 : {
233 7421 : 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 133065 : 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 133065 : + m_filename.length() + (m_is_directory ? 1 : 0)
253 133065 : + 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 133065 : void ZipLocalEntry::setCompressedSize(size_t size)
268 : {
269 133065 : m_compressed_size = size;
270 133065 : }
271 :
272 :
273 : /** \brief Save the CRC of the entry.
274 : *
275 : * This funciton savees 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 133065 : void ZipLocalEntry::setCrc(uint32_t crc)
283 : {
284 133065 : m_crc_32 = crc;
285 133065 : m_has_crc_32 = true;
286 133065 : }
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 61097 : bool ZipLocalEntry::hasTrailingDataDescriptor() const
313 : {
314 61097 : 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 130489 : void ZipLocalEntry::read(std::istream& is)
336 : {
337 130489 : 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 130489 : zipRead(is, signature); // 32
346 130489 : 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 130479 : uint16_t compress_method(0);
354 130479 : uint32_t dostime(0);
355 130479 : uint32_t compressed_size(0);
356 130479 : uint32_t uncompressed_size(0);
357 130479 : uint16_t filename_len(0);
358 130479 : uint16_t extra_field_len(0);
359 130479 : std::string filename;
360 :
361 : // See the ZipLocalEntryHeader for more details
362 130479 : zipRead(is, m_extract_version); // 16
363 130479 : zipRead(is, m_general_purpose_bitfield); // 16
364 130479 : zipRead(is, compress_method); // 16
365 130479 : zipRead(is, dostime); // 32
366 130479 : zipRead(is, m_crc_32); // 32
367 130479 : zipRead(is, compressed_size); // 32
368 130479 : zipRead(is, uncompressed_size); // 32
369 130479 : zipRead(is, filename_len); // 16
370 130479 : zipRead(is, extra_field_len); // 16
371 130479 : zipRead(is, filename, filename_len); // string
372 130479 : 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 130479 : m_is_directory = !filename.empty() && filename.back() == g_separator;
381 :
382 130479 : m_compress_method = static_cast<StorageMethod>(compress_method);
383 130479 : m_unix_time = dos2unixtime(dostime);
384 130479 : m_compressed_size = compressed_size;
385 130479 : m_uncompressed_size = uncompressed_size;
386 130479 : m_filename = FilePath(filename);
387 :
388 130479 : m_valid = true;
389 130479 : }
390 :
391 :
392 : /** \brief Write a ZipLocalEntry to \p os.
393 : *
394 : * This function writes this ZipLocalEntry header to the specified
395 : * output stream.
396 : *
397 : * \exception IOException
398 : * If an error occurs while writing to the output stream, the function
399 : * throws an IOException.
400 : *
401 : * \param[in] os The output stream where the ZipLocalEntry is written.
402 : */
403 266131 : void ZipLocalEntry::write(std::ostream& os)
404 : {
405 532262 : if(m_filename.length() > 0x10000
406 266131 : || m_extra_field.size() > 0x10000)
407 : {
408 1 : throw InvalidStateException("ZipLocalEntry::write(): file name or extra field too large to save in a Zip file.");
409 : }
410 :
411 : /** TODO: add support for 64 bit zip archive
412 : */
413 : // Solaris defines _ILP32 for 32 bit platforms
414 : #if !defined(_ILP32)
415 266130 : if(m_compressed_size >= 0x100000000UL
416 266130 : || m_uncompressed_size >= 0x100000000UL)
417 : {
418 : // these are really big files, we do not currently test such so ignore in coverage
419 : //
420 : // Note: The compressed size is known at the end, we seek back to
421 : // this header and resave it with the info; thus the error
422 : // is caught then if it was not out of bounds earlier.
423 : throw InvalidStateException("The size of this file is too large to fit in a zip archive."); // LCOV_EXCL_LINE
424 : }
425 : #endif
426 :
427 266130 : std::string filename(m_filename);
428 266130 : if(m_is_directory)
429 : {
430 12066 : filename += g_separator;
431 : }
432 :
433 266130 : uint16_t compress_method(static_cast<uint8_t>(m_compress_method));
434 266130 : if(m_compression_level == COMPRESSION_LEVEL_NONE)
435 : {
436 14154 : compress_method = static_cast<uint8_t>(StorageMethod::STORED);
437 : }
438 :
439 266130 : uint32_t dostime(unix2dostime(m_unix_time));
440 266130 : uint32_t compressed_size(m_compressed_size);
441 266130 : uint32_t uncompressed_size(m_uncompressed_size);
442 266130 : uint16_t filename_len(filename.length());
443 266130 : uint16_t extra_field_len(m_extra_field.size());
444 :
445 : // See the ZipLocalEntryHeader for more details
446 266130 : zipWrite(os, g_signature); // 32
447 266130 : zipWrite(os, m_extract_version); // 16
448 266130 : zipWrite(os, m_general_purpose_bitfield); // 16
449 266130 : zipWrite(os, compress_method); // 16
450 266130 : zipWrite(os, dostime); // 32
451 266130 : zipWrite(os, m_crc_32); // 32
452 266130 : zipWrite(os, compressed_size); // 32
453 266130 : zipWrite(os, uncompressed_size); // 32
454 266130 : zipWrite(os, filename_len); // 16
455 266130 : zipWrite(os, extra_field_len); // 16
456 266130 : zipWrite(os, filename); // string
457 266130 : zipWrite(os, m_extra_field); // buffer
458 266130 : }
459 :
460 :
461 3 : } // zipios namespace
462 :
463 : // Local Variables:
464 : // mode: cpp
465 : // indent-tabs-mode: nil
466 : // c-basic-offset: 4
467 : // tab-width: 4
468 : // End:
469 :
470 : // vim: ts=4 sw=4 et
|