libdigidocpp
ZipSerialize.cpp
Go to the documentation of this file.
1 /*
2  * libdigidocpp
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  *
18  */
19 
20 #include "ZipSerialize.h"
21 
22 #include "../log.h"
23 #include "../util/File.h"
24 
25 #include "../minizip/zip.h"
26 #include "../minizip/unzip.h"
27 #ifdef _WIN32
28 #include "../minizip/iowin32.h"
29 #endif
30 
31 #include <iostream>
32 
33 namespace digidoc
34 {
36 {
37 public:
39  {
40 #ifdef _WIN32
42 #else
44 #endif
45  }
46 
47  void extractCurrentFile(unzFile zipFile, const std::string& directory) throw(IOException);
48 
49  struct FileEntry { std::string containerPath, path; };
50  std::vector<FileEntry> filesAdded;
51 
53 };
54 
55 }
56 
62 digidoc::ZipSerialize::ZipSerialize(const std::string& path)
63  : ISerialize(path)
64  , d(new ZipSerializePrivate)
65 {
66 }
67 
74 {
75  delete d;
76 }
77 
86 {
87  DEBUG("ZipSerialize::extract()");
88 
89  // Open ZIP file for extracting.
91  unzFile zipFile = unzOpen2((char*)enc_path.c_str(),&d->pzlib_filefunc);
92  if(!zipFile)
93  THROW_IOEXCEPTION("Failed to open ZIP file '%s'.", path.c_str());
94 
95  DEBUG("Opened ZIP file '%s' for extracting.", path.c_str());
96 
97  // Go to first file inside ZIP container.
98  int unzResult = unzGoToFirstFile(zipFile);
99  if(unzResult != UNZ_OK)
100  {
101  unzClose(zipFile);
102  THROW_IOEXCEPTION("Failed to go to the first file inside ZIP container. ZLib error: %d", unzResult);
103  }
104 
105  // Create temporary folder for extracted files.
106  std::string tmpDirectory = util::File::createTempDirectory();
107  DEBUG("Extracting ZIP file to temporary directory '%s'", tmpDirectory.c_str());
108 
109 
110  // Extract all files inside the ZIP container.
111  for( ;; )
112  {
113  try
114  {
115  d->extractCurrentFile(zipFile, tmpDirectory);
116  }
117  catch(const IOException& e)
118  {
119  unzClose(zipFile);
120  throw e;
121  }
122 
123  // Go to the next file inside ZIP container.
124  unzResult = unzGoToNextFile(zipFile);
125  if(unzResult == UNZ_END_OF_LIST_OF_FILE)
126  {
127  break;
128  }
129  else if(unzResult != UNZ_OK)
130  {
131  unzClose(zipFile);
132  THROW_IOEXCEPTION("Failed to go to the next file inside ZIP container. ZLib error: %d", unzResult);
133  }
134  }
135 
136 
137  // Close ZIP file.
138  unzResult = unzClose(zipFile);
139  if(unzResult != UNZ_OK)
140  THROW_IOEXCEPTION("Failed to close ZIP file. ZLib error: %d", unzResult);
141 
142  DEBUG("Successfully closed ZIP file '%s'.", path.c_str());
143  return tmpDirectory;
144 }
145 
155 {
156  DEBUG("ZipSerializePrivate::extractCurrentFile(zipFile = %p, directory = '%s')", zipFile, directory.c_str());
157 
158  // Get filename of the current file inside ZIP container.
159  // XXX: How long must the filename buffer be?
160  unz_file_info fileInfo;
161  int unzResult = unzGetCurrentFileInfo(zipFile, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
162  if(unzResult != UNZ_OK)
163  THROW_IOEXCEPTION("Failed to get filename of the current file inside ZIP container. ZLib error: %d", unzResult);
164 
165  std::string fileName(fileInfo.size_filename, 0);
166  unzResult = unzGetCurrentFileInfo(zipFile, &fileInfo, &fileName[0], fileName.size(), NULL, 0, NULL, 0);
167  if(unzResult != UNZ_OK)
168  THROW_IOEXCEPTION("Failed to get filename of the current file inside ZIP container. ZLib error: %d", unzResult);
169 
170  // make sure the destination directory exists
171  std::string path = util::File::path(directory, fileName);
173 
174  // Extract current file inside ZIP container.
175  unzResult = unzOpenCurrentFile(zipFile);
176  if(unzResult != UNZ_OK)
177  THROW_IOEXCEPTION("Failed to open current file inside ZIP container. ZLib error: %d", unzResult);
178 
179  if(fileName[fileName.size()-1] == '/')
180  return;
181 
182  // Create new file to write the extracted data to.
183  DEBUG("Extracting file '%s' to '%s'.", fileName.c_str(), path.c_str());
184  FILE* pFile = digidoc::util::File::fopen(path, "wb");
185  if(!pFile)
186  {
188  THROW_IOEXCEPTION("Failed to create new file '%s'.", path.c_str());
189  }
190 
191  // Extract current file inside ZIP container.
192  char buf[10240];
193  for( ;; )
194  {
195  unzResult = unzReadCurrentFile(zipFile, buf, 10240);
196 
197  if(unzResult > 0)
198  {
199  // Write bytes to file.
200  size_t bytesWritten = fwrite(buf, 1, unzResult, pFile);
201  if(int(bytesWritten) != unzResult)
202  {
203  fclose(pFile);
205  THROW_IOEXCEPTION("Failed to write %d bytes to file '%s', could only write %d bytes.", unzResult, path.c_str(), bytesWritten);
206  }
207  }
208  else if(unzResult == UNZ_EOF)
209  {
210  break;
211  }
212  else
213  {
214  fclose(pFile);
216  THROW_IOEXCEPTION("Failed to read bytes from current file inside ZIP container. ZLib error: %d", unzResult);
217  }
218  }
219 
220  DEBUG("Finished writing data to file '%s', closing file.", path.c_str());
221 
222  // Close file.
223  if(fclose(pFile) != 0)
224  {
226  THROW_IOEXCEPTION("Failed to close file '%s'.", path.c_str());
227  }
228 
229  // Close current file in ZIP container.
230  unzResult = unzCloseCurrentFile(zipFile);
231  if(unzResult != UNZ_OK)
232  THROW_IOEXCEPTION("Failed to close current file inside ZIP container. ZLib error: %d", unzResult);
233 }
234 
243 {
244  d->filesAdded.clear();
245 }
246 
256 void digidoc::ZipSerialize::addFile(const std::string& containerPath, const std::string& path)
257 {
258  DEBUG("ZipSerialize::addFile(containerPath = '%s', path = '%s')", containerPath.c_str(), path.c_str());
259 
260  ZipSerializePrivate::FileEntry fileEntry = { containerPath, path };
261  d->filesAdded.push_back(fileEntry);
262 }
263 
273 {
274  DEBUG("ZipSerialize::save()");
275 
276  if(d->filesAdded.empty())
277  THROW_IOEXCEPTION("No files added to ZipSerialize, can not create ZIP file.");
278 
279  // Create new ZIP file.
280  std::string fileName = util::File::tempFileName();
281  f_string enc_fileName = digidoc::util::File::encodeName(fileName);
282  zipFile zipFile = zipOpen2((char*)enc_fileName.c_str(), APPEND_STATUS_CREATE, 0, &d->pzlib_filefunc);
283  DEBUG("Created ZIP file: fileName = '%s', zipFile = %p", fileName.c_str(), zipFile);
284  if(!zipFile)
285  THROW_IOEXCEPTION("Failed to create ZIP file '%s'.", path.c_str());
286 
287  // Add files to ZIP.
288  for(std::vector<ZipSerializePrivate::FileEntry>::const_iterator iter = d->filesAdded.begin();
289  iter != d->filesAdded.end(); iter++)
290  {
291  // Open file to archive.
292  FILE* pFile = digidoc::util::File::fopen(iter->path, "rb");
293  if(!pFile)
294  {
295  zipClose(zipFile, NULL);
296  THROW_IOEXCEPTION("Failed to open file '%s'.", iter->path.c_str());
297  }
298 
299  // Create new file inside ZIP container.
300  // 2048 general purpose bit 11 for unicode
301  tm *t = digidoc::util::File::modifiedTime(iter->path);
302  zip_fileinfo fileInfo = {
303  { uInt(t->tm_sec), uInt(t->tm_min), uInt(t->tm_hour), uInt(t->tm_mday), uInt(t->tm_mon), uInt(t->tm_year) }, 0, 0, 0 };
304  int zipResult = zipOpenNewFileInZip4(zipFile, iter->containerPath.c_str(),
305  &fileInfo, 0, 0, 0, 0, 0, Z_DEFLATED, Z_DEFAULT_COMPRESSION, 0,
306  -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, 0, 0, 0, 2048);
307  if(zipResult != ZIP_OK)
308  {
309  fclose(pFile);
310  zipClose(zipFile, NULL);
311  THROW_IOEXCEPTION("Failed to create new file inside ZIP container. ZLib error: %d", zipResult);
312  }
313 
314  DEBUG("Archiving file '%s' to ZIP file '%s' with path '%s'.", iter->path.c_str(), fileName.c_str(), iter->containerPath.c_str());
315 
316  // Archive file.
317  char buf[10240];
318  for( ;; )
319  {
320  size_t bytesRead = fread(buf, 1, 10240, pFile);
321  if(bytesRead == 0)
322  break;
323 
324  zipResult = zipWriteInFileInZip(zipFile, buf, (unsigned int)bytesRead);
325  if(zipResult != ZIP_OK)
326  {
327  fclose(pFile);
328  zipCloseFileInZip(zipFile);
329  zipClose(zipFile, NULL);
330  THROW_IOEXCEPTION("Failed to write bytes to current file inside ZIP container. ZLib error: %d", zipResult);
331  }
332  }
333 
334  // Close file.
335  if(fclose(pFile) != 0)
336  {
337  zipCloseFileInZip(zipFile);
338  zipClose(zipFile, NULL);
339  THROW_IOEXCEPTION("Failed to close file '%s'.", iter->path.c_str());
340  }
341 
342  // Close created file inside ZIP container.
343  zipResult = zipCloseFileInZip(zipFile);
344  if(zipResult != ZIP_OK)
345  {
346  zipClose(zipFile, NULL);
347  THROW_IOEXCEPTION("Failed to close current file inside ZIP container. ZLib error: %d", zipResult);
348  }
349  }
350 
351 
352  // Close ZIP file.
353  int zipResult = zipClose(zipFile, NULL);
354  if(zipResult != ZIP_OK)
355  THROW_IOEXCEPTION("Failed to close ZIP file. ZLib error: %d", zipResult);
356 
357  DEBUG("Successfully closed ZIP file '%s'.", fileName.c_str());
358 
359  // Move created temporary ZIP file to file 'path'.
360  util::File::moveFile(fileName, path);
361 
362  // Clear added files list.
363  d->filesAdded.clear();
364 }