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::ZipOutputStreambuf class.
24 : *
25 : * This file includes the functions necessary to write data to a Zip
26 : * archive.
27 : */
28 :
29 : #include "zipoutputstreambuf.hpp"
30 :
31 : #include "zipios++/zipiosexceptions.hpp"
32 :
33 : #include "ziplocalentry.hpp"
34 : #include "zipendofcentraldirectory.hpp"
35 :
36 :
37 : namespace zipios
38 : {
39 :
40 :
41 : namespace
42 : {
43 :
44 :
45 : /** \brief Help function used to write the central directory.
46 : *
47 : * When you create a Zip archive, it includes a central directory where
48 : * all the meta data about each file is saved. This function saves an
49 : * array of entries in an output stream to generate the Zip file
50 : * central directory.
51 : *
52 : * \param[in] os The output stream.
53 : * \param[in] entries The array of entries to save in this central directory.
54 : * \param[in] comment The zip archive global comment.
55 : */
56 256 : void writeZipCentralDirectory(std::ostream &os, FileEntry::vector_t& entries, std::string const& comment)
57 : {
58 256 : ZipEndOfCentralDirectory eocd(comment);
59 256 : eocd.setOffset(os.tellp()); // start position
60 256 : eocd.setCount(entries.size());
61 :
62 256 : size_t central_directory_size(0);
63 133320 : for(auto it = entries.begin(); it != entries.end(); ++it)
64 : {
65 133066 : (*it)->write(os);
66 133064 : central_directory_size += (*it)->getHeaderSize();
67 : }
68 :
69 254 : eocd.setCentralDirectorySize(central_directory_size);
70 258 : eocd.write(os);
71 252 : }
72 :
73 :
74 : } // no name namespace
75 :
76 :
77 : /** \class ZipOutputStreambuf
78 : * \brief Handle the output buffer of a zip archive.
79 : *
80 : * The ZipOutputStreambuf class is a zip archive output
81 : * streambuf filter.
82 : */
83 :
84 :
85 : /** \brief Initialize a ZipOutputStreambuf object.
86 : *
87 : * Note that a new initialized ZipOutputStreambuf is not ready to
88 : * accept data, putNextEntry() must be invoked at least once first.
89 : *
90 : * \param[in] outbuf The streambuf to use for output.
91 : */
92 256 : ZipOutputStreambuf::ZipOutputStreambuf(std::streambuf * outbuf)
93 256 : : DeflateOutputStreambuf(outbuf)
94 : //, m_open_entry(false) -- auto-init
95 : //, m_open(true) -- auto-init
96 : {
97 256 : }
98 :
99 :
100 : /** \brief Clean up the buffer.
101 : *
102 : * This function cleans up this output buffer. In general this ensures
103 : * that the data still cached gets flushed.
104 : *
105 : * \warning
106 : * This function may gobble up some important exceptions. If you want
107 : * to make sure that the file is properly written, you must call the
108 : * finish() function (or the close() function) to fully terminate the
109 : * file. If these functions do not fail, then the output file is
110 : * considered valid and you can keep it. The finish() function can fail
111 : * because of a comment or a file which are too large, for example.
112 : */
113 768 : ZipOutputStreambuf::~ZipOutputStreambuf()
114 : {
115 : // avoid possible exceptions when writing the central directory
116 : try
117 : {
118 256 : finish();
119 : }
120 1 : catch(...)
121 : {
122 : }
123 512 : }
124 :
125 :
126 : /** \brief Close this buffer entry.
127 : *
128 : * Closes the current output buffer entry and positions the stream
129 : * write pointer at the beginning of the next entry.
130 : */
131 133577 : void ZipOutputStreambuf::closeEntry()
132 : {
133 133577 : if(!m_open_entry)
134 : {
135 134089 : return;
136 : }
137 :
138 133065 : switch(m_compression_level)
139 : {
140 : case FileEntry::COMPRESSION_LEVEL_NONE:
141 72765 : overflow(); // flush
142 72765 : break;
143 :
144 : default:
145 60300 : closeStream();
146 60300 : break;
147 :
148 : }
149 :
150 133065 : updateEntryHeaderInfo();
151 133065 : setEntryClosedState();
152 : }
153 :
154 :
155 : /** \brief Close the output stream buffer.
156 : *
157 : * This function calls finish to make sure that any cached
158 : * data is saved and then close the stream buffer.
159 : */
160 252 : void ZipOutputStreambuf::close()
161 : {
162 252 : finish();
163 252 : }
164 :
165 :
166 : /** \brief Finish up an output stream buffer.
167 : *
168 : * Closes the current entry (if one is open), then writes the Zip
169 : * Central Directory Structure closing the ZipOutputStream. The
170 : * output stream (std::ostream) that the zip archive is being
171 : * written to is not closed.
172 : */
173 763 : void ZipOutputStreambuf::finish()
174 : {
175 763 : if(!m_open)
176 : {
177 1266 : return;
178 : }
179 256 : m_open = false;
180 :
181 256 : std::ostream os(m_outbuf);
182 256 : closeEntry();
183 260 : writeZipCentralDirectory(os, m_entries, m_zip_comment);
184 : }
185 :
186 :
187 : /** \brief Start saving an entry in the output buffer.
188 : *
189 : * Opens the next entry in the zip archive and returns a const pointer to a
190 : * FileEntry object for the entry.
191 : *
192 : * If a previous entry was still open, the function calls closeEntry()
193 : * first.
194 : *
195 : * \param[in] entry The entry to be saved and made current.
196 : */
197 133066 : void ZipOutputStreambuf::putNextEntry(FileEntry::pointer_t entry)
198 : {
199 133066 : closeEntry();
200 :
201 : // if the method is STORED force uncompressed data
202 133066 : if(entry->getMethod() == StorageMethod::STORED)
203 : {
204 : // force to "no compression" when the method is STORED
205 72062 : m_compression_level = FileEntry::COMPRESSION_LEVEL_NONE;
206 : }
207 : else
208 : {
209 : // get the user defined compression level
210 61004 : m_compression_level = entry->getLevel();
211 : }
212 133066 : m_overflown_bytes = 0;
213 133066 : switch(m_compression_level)
214 : {
215 : case FileEntry::COMPRESSION_LEVEL_NONE:
216 72766 : setp(&m_invec[0], &m_invec[0] + getBufferSize());
217 72766 : break;
218 :
219 : default:
220 60300 : init(m_compression_level);
221 60300 : break;
222 :
223 : }
224 :
225 133066 : m_entries.push_back(entry);
226 :
227 133066 : std::ostream os(m_outbuf);
228 :
229 : // Update entry header info
230 133066 : entry->setEntryOffset(os.tellp());
231 : /** \TODO
232 : * Rethink the design as we have to force a call to the correct
233 : * write() function?
234 : */
235 133066 : static_cast<ZipLocalEntry *>(entry.get())->ZipLocalEntry::write(os);
236 :
237 133066 : m_open_entry = true;
238 133065 : }
239 :
240 :
241 : /** \brief Set the archive comment.
242 : *
243 : * This function saves a global comment for the Zip archive.
244 : *
245 : * You may set it to an empty string which means that no comment
246 : * will be saved.
247 : *
248 : * The comment is saved when the first entry is saved so it
249 : * has to be put in there early on.
250 : *
251 : * \param[in] comment The comment to save in the Zip archive.
252 : */
253 256 : void ZipOutputStreambuf::setComment(std::string const& comment)
254 : {
255 256 : m_zip_comment = comment;
256 256 : }
257 :
258 :
259 : //
260 : // Protected and private methods
261 : //
262 :
263 : /** \brief Implementation of the overflow() function.
264 : *
265 : * When writing to a buffer, the overflow() function gets called when
266 : * there is no more room in the output buffer. The buffer is expected
267 : * to flush the data to disk and reset the buffer availability.
268 : *
269 : * \param[in] c The character that made it all happen. Maybe EOF.
270 : *
271 : * \return EOF if the function fails, 0 otherwise.
272 : */
273 487183 : int ZipOutputStreambuf::overflow(int c)
274 : {
275 487183 : size_t const size(pptr() - pbase());
276 487183 : m_overflown_bytes += size;
277 487183 : switch(m_compression_level)
278 : {
279 : case FileEntry::COMPRESSION_LEVEL_NONE:
280 : {
281 : // Ok, we are STORED, so we handle it ourselves to avoid "side
282 : // effects" from zlib, which adds markers every now and then.
283 77822 : size_t const bc(m_outbuf->sputn(&m_invec[0], size));
284 77822 : if(size != bc)
285 : {
286 : // Without implementing our own stream in our test, this
287 : // cannot really be reached because it is all happening
288 : // inside the same loop in ZipFile::saveCollectionToArchive()
289 : throw IOException("ZipOutputStreambuf::overflow(): write to buffer failed."); // LCOV_EXCL_LINE
290 : }
291 77822 : setp(&m_invec[0], &m_invec[0] + getBufferSize());
292 :
293 77822 : if(c != EOF)
294 : {
295 5057 : *pptr() = c;
296 5057 : pbump(1);
297 : }
298 :
299 77822 : return 0;
300 : }
301 :
302 : default:
303 409361 : return DeflateOutputStreambuf::overflow(c);
304 :
305 : }
306 : }
307 :
308 :
309 :
310 : /** \brief Implement the sync() functionality.
311 : *
312 : * This virtual function is reimplemented to make sure that the system
313 : * does not run a default sync() function.
314 : *
315 : * This function calls the DeflateOutputStreambuf::sync() function which
316 : * returns -1 because it will not "synchronize" the input buffer.
317 : */
318 : int ZipOutputStreambuf::sync() // LCOV_EXCL_LINE
319 : {
320 : return DeflateOutputStreambuf::sync(); // LCOV_EXCL_LINE
321 : }
322 :
323 :
324 :
325 : /** \brief Mark the current entry as closed.
326 : *
327 : * After the putNextEntry() call and saving of the file content, the
328 : * closeEntry() function can be called to close the entry. The entry
329 : * is really closed when this setEntryClosedState() is called.
330 : */
331 133065 : void ZipOutputStreambuf::setEntryClosedState()
332 : {
333 133065 : m_open_entry = false;
334 :
335 : /** \FIXME
336 : * Update put pointers to trigger overflow on write. Overflow
337 : * should then return EOF while m_open_entry is false.
338 : */
339 133065 : }
340 :
341 :
342 : /** \brief Save the header information.
343 : *
344 : * This function saves parameters that are now available in the header
345 : * of the local entry.
346 : *
347 : * These parameters include:
348 : *
349 : * \li The uncompressed size of the entry
350 : * \li The compressed size of the entry
351 : * \li The CRC32 of the input file (before the compression)
352 : */
353 133065 : void ZipOutputStreambuf::updateEntryHeaderInfo()
354 : {
355 133065 : if(!m_open_entry)
356 : {
357 133065 : return;
358 : }
359 :
360 133065 : std::ostream os(m_outbuf);
361 133065 : int const curr_pos(os.tellp());
362 :
363 : // update fields in m_entries.back()
364 266130 : FileEntry::pointer_t entry(m_entries.back());
365 133065 : entry->setSize(getSize());
366 133065 : entry->setCrc(getCrc32());
367 : /** \TODO
368 : * Rethink the design as we have to force a call to the correct
369 : * getHeaderSize() function?
370 : */
371 133065 : entry->setCompressedSize(curr_pos - entry->getEntryOffset() - static_cast<ZipLocalEntry *>(entry.get())->ZipLocalEntry::getHeaderSize());
372 :
373 : // write ZipLocalEntry header to header position
374 133065 : os.seekp(entry->getEntryOffset());
375 : /** \TODO
376 : * Rethink the design as we have to force a call to the correct write()
377 : * function?
378 : */
379 133065 : static_cast<ZipLocalEntry *>(entry.get())->ZipLocalEntry::write(os);
380 266130 : os.seekp(curr_pos);
381 : }
382 :
383 :
384 3 : } // zipios namespace
385 :
386 : // Local Variables:
387 : // mode: cpp
388 : // indent-tabs-mode: nil
389 : // c-basic-offset: 4
390 : // tab-width: 4
391 : // End:
392 :
393 : // vim: ts=4 sw=4 et
|