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 Declare zipios::ZipEndOfCentralDirectory whichs handles entries found
24 : * in a Zip archive directory.
25 : *
26 : * This header file contains the zipios::ZipLocalEntry, which is used
27 : * to handle entries found in a Zip archive.
28 : */
29 :
30 : #include "zipendofcentraldirectory.hpp"
31 :
32 : #include "zipios/zipiosexceptions.hpp"
33 :
34 :
35 : namespace zipios
36 : {
37 :
38 :
39 : /** \class ZipEndOfCentralDirectory
40 : * \brief Marker at the end of a Zip archive file.
41 : *
42 : * This class is used to read and write the end of the Central Directory
43 : * structure. In most cases, this structure is stored at the end of the
44 : * zip archive file, and contains some global information about the file,
45 : * including the position of the start of the Central Directory.
46 : */
47 :
48 :
49 : /** \brief Private definitions of the ZipEndOfCentralDirectory class.
50 : *
51 : * This name space includes definitions exclusively used by the
52 : * ZipEndOfCentralDirectory class.
53 : */
54 : namespace
55 : {
56 :
57 :
58 : /** \brief Signature of the ZipEndOfCentralDirectory structure block.
59 : *
60 : * This variable is used to define the signature of the
61 : * ZipEndOfCentralDirectory structure on disk. It is used to
62 : * create such a block or to detect that such a block exists.
63 : *
64 : * The four byte signature represents the following value:
65 : *
66 : * "PK 5.6" -- End of Central Directory
67 : */
68 : uint32_t const g_signature = 0x06054b50;
69 :
70 :
71 : } // no name namespace
72 :
73 :
74 : /** \brief Initialize an ZipEndOfCentralDirectory object.
75 : *
76 : * This function initializes an ZipEndOfCentralDirectory object. By default,
77 : * all the numbers are set to zero and the global Zip file comment is
78 : * set to the empty string.
79 : *
80 : * \param[in] zip_comment The global comment of a Zip archive.
81 : */
82 599 : ZipEndOfCentralDirectory::ZipEndOfCentralDirectory(std::string const& zip_comment)
83 : //: m_central_directory_entries(0) -- auto-init
84 : //, m_central_directory_size(0) -- auto-init
85 : //, m_central_directory_offset(0) -- auto-init
86 599 : : m_zip_comment(zip_comment)
87 : {
88 599 : }
89 :
90 :
91 : /** \brief Retrieve the size of the Central Directory in bytes.
92 : *
93 : * This function returns the size of the Central Directory
94 : * structure in the file. This size varies because each entry
95 : * includes data that change in size (i.e. filename, comment,
96 : * extra data.)
97 : *
98 : * \return The size, in bytes, of the Central Directory.
99 : *
100 : * \sa setCentralDirectorySize()
101 : */
102 303 : size_t ZipEndOfCentralDirectory::getCentralDirectorySize() const
103 : {
104 303 : return m_central_directory_size;
105 : }
106 :
107 :
108 : /** \brief Retrieve the number of entries.
109 : *
110 : * This function returns the number of entries that will be found
111 : * in the Central Directory.
112 : *
113 : * Since Zipios has no support for split Zip archive files (i.e. one
114 : * large archive written on multiple disks), the total number of entries,
115 : * or the number of entries in this archive is always exactly the same.
116 : *
117 : * \return The total number of entries archived in this Zip file.
118 : *
119 : * \sa setCount()
120 : */
121 626 : size_t ZipEndOfCentralDirectory::getCount() const
122 : {
123 626 : return m_central_directory_entries;
124 : }
125 :
126 :
127 : /** \brief Retrieve the offset of the Central Directory.
128 : *
129 : * This function is expected to be called after a call to read().
130 : * It includes the offset of the central directory, which in most
131 : * cases appears before the ZipEndOfCentralDirectory block.
132 : *
133 : * \warning
134 : * There is getOffsetFromEnd() which returns the offset of the
135 : * ZipEndOfCentralDirectory itself and not the Central Directory.
136 : *
137 : * \return The offset in the Zip archive of the Central Directory.
138 : *
139 : * \sa getOffsetFromEnd()
140 : * \sa setOffset()
141 : */
142 616 : offset_t ZipEndOfCentralDirectory::getOffset() const
143 : {
144 616 : return m_central_directory_offset;
145 : }
146 :
147 :
148 : /** \brief Define the size of the central directory.
149 : *
150 : * When creating a Zip archive, it is necessary to call this function
151 : * to define the size of the Central Directory block. This size
152 : * cannot be inferred or calculated without wasting a lot of time
153 : * re-reading the Central Directory, hence the function to avoid
154 : * doing such.
155 : *
156 : * \param[in] size The size of the Central Directory.
157 : *
158 : * \sa getCentralDirectorySize()
159 : */
160 241 : void ZipEndOfCentralDirectory::setCentralDirectorySize(size_t size)
161 : {
162 241 : m_central_directory_size = size;
163 241 : }
164 :
165 :
166 : /** \brief Set the number of entries.
167 : *
168 : * This function is used to define the number of entries one will find
169 : * in the central directory.
170 : *
171 : * \note
172 : * The maximum number of entries is 65535. (until we add support for
173 : * 64 bit Zip archives.)
174 : *
175 : * \param[in] count The number of entries in the Central Directory.
176 : *
177 : * \sa getCount()
178 : */
179 243 : void ZipEndOfCentralDirectory::setCount(size_t count)
180 : {
181 243 : m_central_directory_entries = count;
182 243 : }
183 :
184 :
185 : /** \brief Offset of the Central Directory.
186 : *
187 : * This function defines the offset at which the Central Directory
188 : * starts. Before writing the Central Directory, we expect the user
189 : * to call tell() and save the value using this function. This is
190 : * important when creating a Zip archive.
191 : *
192 : * \note
193 : * Only the offset of the Central Directory can be changed by
194 : * this function.
195 : *
196 : * \param[in] start_offset The start offset of the Central Directory.
197 : *
198 : * \sa getOffset();
199 : */
200 243 : void ZipEndOfCentralDirectory::setOffset(offset_t start_offset)
201 : {
202 243 : m_central_directory_offset = start_offset;
203 243 : }
204 :
205 :
206 : /** \brief Attempt to read an ZipEndOfCentralDirectory structure.
207 : *
208 : * This function tries to read an ZipEndOfCentralDirectory structure from the
209 : * specified buffer. This function expects a BackBuffer, which is used
210 : * because that is generally the fastest way to read the data (instead of
211 : * scanning the entire file).
212 : *
213 : * \note
214 : * If a read from the buffer fails, then an exception is raised. Since
215 : * we are reading from a buffer, it should not happen except if the
216 : * ZipEndOfCentralDirectory indicates that there is a comment and the comment
217 : * is not there or some characters are missing.
218 : *
219 : * \exception FileCollectionException
220 : * This exception is raised if the number of entries is not equal to
221 : * the total number of entries, as expected.
222 : *
223 : * \param[in] buf The buffer with the file data.
224 : * \param[in] pos The position at which we are expected to check.
225 : *
226 : * \return true if the ZipEndOfCentralDirectory was found, false otherwise.
227 : */
228 8761 : bool ZipEndOfCentralDirectory::read(::zipios::buffer_t const& buf, size_t pos)
229 : {
230 : // the number of bytes we are going to read in the buffer
231 : // (including the signature)
232 8761 : ssize_t const HEADER_SIZE(static_cast<ssize_t>(sizeof(uint32_t) * 3 + sizeof(uint16_t) * 5));
233 :
234 : // enough data in the buffer?
235 : //
236 : // Note: this quick check assumes a 0 length comment which is possible;
237 : // if there is a comment and we find the signature too early, then
238 : // it will throw
239 : //
240 8761 : if(static_cast<ssize_t>(buf.size() - pos) < HEADER_SIZE)
241 : {
242 7266 : return false;
243 : }
244 :
245 : // first read and check the signature
246 : uint32_t signature;
247 1495 : zipRead(buf, pos, signature); // 32
248 1495 : if(signature != g_signature)
249 : {
250 1162 : return false;
251 : }
252 :
253 : // next we read the other parameters
254 : uint16_t disk_number;
255 : uint16_t central_directory_entries;
256 : uint16_t central_directory_total_entries;
257 : uint32_t central_directory_size;
258 : uint32_t central_directory_offset;
259 : uint16_t comment_len;
260 :
261 333 : zipRead(buf, pos, disk_number); // 16
262 333 : zipRead(buf, pos, disk_number); // 16
263 333 : zipRead(buf, pos, central_directory_entries); // 16
264 333 : zipRead(buf, pos, central_directory_total_entries); // 16
265 333 : zipRead(buf, pos, central_directory_size); // 32
266 333 : zipRead(buf, pos, central_directory_offset); // 32
267 333 : zipRead(buf, pos, comment_len); // 16
268 333 : zipRead(buf, pos, m_zip_comment, comment_len); // string
269 :
270 : // note that if disk_number is defined, then these following two
271 : // numbers should differ too
272 323 : if(central_directory_entries != central_directory_total_entries)
273 : {
274 10 : throw FileCollectionException("ZipEndOfCentralDirectory with a number of entries and total entries that differ is not supported, spanned zip files are not supported");
275 : }
276 :
277 313 : m_central_directory_entries = central_directory_entries;
278 313 : m_central_directory_size = central_directory_size;
279 313 : m_central_directory_offset = central_directory_offset;
280 :
281 313 : return true;
282 : }
283 :
284 :
285 : /** \brief Write the ZipEndOfCentralDirectory structure to a stream.
286 : *
287 : * This function writes the currently defined end of central
288 : * directory to disk. This entry is expected to be written at
289 : * the very end of a Zip archive file.
290 : *
291 : * \note
292 : * If the output pointer is not valid, the function will throw
293 : * via the various zipWrite() it uses.
294 : *
295 : * \note
296 : * The function does not change the output pointer of the stream
297 : * before writing to it.
298 : *
299 : * \exception FileCollectionException
300 : * This function throws this exception if the data cannot be saved. In
301 : * general this means there are too many entries, the size is too large
302 : * or the comment is more than 64Kb (some of which will be resolved with
303 : * Zip64 support.)
304 : *
305 : * \param[in] os The output stream where the data is to be saved.
306 : */
307 241 : void ZipEndOfCentralDirectory::write(std::ostream& os)
308 : {
309 : /** \todo
310 : * Add support for 64 bit Zip archive. This would allow for pretty
311 : * much all the following conditions to be dropped out.
312 : */
313 241 : if(m_zip_comment.length() > 65535)
314 : {
315 1 : throw InvalidStateException("the Zip archive comment is too large");
316 : }
317 240 : if(m_central_directory_entries > 65535)
318 : {
319 1 : throw InvalidStateException("the number of entries in the Zip archive is too large");
320 : }
321 : // Solaris defines _ILP32 for 32 bit platforms
322 : #if !defined(_ILP32)
323 239 : if(m_central_directory_size >= 0x100000000UL
324 239 : || m_central_directory_offset >= 0x100000000L)
325 : {
326 : throw FileCollectionException("the Zip archive size or offset are too large"); // LCOV_EXCL_LINE
327 : }
328 : #endif
329 :
330 239 : uint16_t const disk_number(0);
331 239 : uint16_t const central_directory_entries(m_central_directory_entries);
332 239 : uint32_t const central_directory_size(m_central_directory_size);
333 239 : uint32_t const central_directory_offset(m_central_directory_offset);
334 239 : uint16_t const comment_len(m_zip_comment.length());
335 :
336 : // the total number of entries, across all disks is the same in our
337 : // case so we use one number for both fields
338 :
339 239 : zipWrite(os, g_signature); // 32
340 239 : zipWrite(os, disk_number); // 16
341 239 : zipWrite(os, disk_number); // 16
342 239 : zipWrite(os, central_directory_entries); // 16
343 239 : zipWrite(os, central_directory_entries); // 16
344 239 : zipWrite(os, central_directory_size); // 32
345 239 : zipWrite(os, central_directory_offset); // 32
346 239 : zipWrite(os, comment_len); // 16
347 239 : zipWrite(os, m_zip_comment); // string
348 239 : }
349 :
350 :
351 3 : } // zipios namespace
352 :
353 : // Local Variables:
354 : // mode: cpp
355 : // indent-tabs-mode: nil
356 : // c-basic-offset: 4
357 : // tab-width: 4
358 : // End:
359 :
360 : // vim: ts=4 sw=4 et
|