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