comparison OrthancFramework/Sources/Compression/ZipWriter.cpp @ 4044:d25f4c0fa160 framework

splitting code into OrthancFramework and OrthancServer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 10 Jun 2020 20:30:34 +0200
parents Core/Compression/ZipWriter.cpp@0b3256c3ee14
children bf7b9edf6b81
comparison
equal deleted inserted replaced
4043:6c6239aec462 4044:d25f4c0fa160
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * In addition, as a special exception, the copyright holders of this
13 * program give permission to link the code of its release with the
14 * OpenSSL project's "OpenSSL" library (or with modified versions of it
15 * that use the same license as the "OpenSSL" library), and distribute
16 * the linked executables. You must obey the GNU General Public License
17 * in all respects for all of the code used other than "OpenSSL". If you
18 * modify file(s) with this exception, you may extend this exception to
19 * your version of the file(s), but you are not obligated to do so. If
20 * you do not wish to do so, delete this exception statement from your
21 * version. If you delete this exception statement from all source files
22 * in the program, then also delete it here.
23 *
24 * This program is distributed in the hope that it will be useful, but
25 * WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31 **/
32
33
34 #include "../PrecompiledHeaders.h"
35
36 #ifndef NOMINMAX
37 #define NOMINMAX
38 #endif
39
40 #include "ZipWriter.h"
41
42 #include <limits>
43 #include <boost/filesystem.hpp>
44 #include <boost/date_time/posix_time/posix_time.hpp>
45
46 #include "../../Resources/ThirdParty/minizip/zip.h"
47 #include "../OrthancException.h"
48 #include "../Logging.h"
49
50
51 static void PrepareFileInfo(zip_fileinfo& zfi)
52 {
53 memset(&zfi, 0, sizeof(zfi));
54
55 using namespace boost::posix_time;
56 ptime now = second_clock::local_time();
57
58 boost::gregorian::date today = now.date();
59 ptime midnight(today);
60
61 time_duration sinceMidnight = now - midnight;
62 zfi.tmz_date.tm_sec = static_cast<unsigned int>(sinceMidnight.seconds()); // seconds after the minute - [0,59]
63 zfi.tmz_date.tm_min = static_cast<unsigned int>(sinceMidnight.minutes()); // minutes after the hour - [0,59]
64 zfi.tmz_date.tm_hour = static_cast<unsigned int>(sinceMidnight.hours()); // hours since midnight - [0,23]
65
66 // http://www.boost.org/doc/libs/1_35_0/doc/html/boost/gregorian/greg_day.html
67 zfi.tmz_date.tm_mday = today.day(); // day of the month - [1,31]
68
69 // http://www.boost.org/doc/libs/1_35_0/doc/html/boost/gregorian/greg_month.html
70 zfi.tmz_date.tm_mon = today.month() - 1; // months since January - [0,11]
71
72 // http://www.boost.org/doc/libs/1_35_0/doc/html/boost/gregorian/greg_year.html
73 zfi.tmz_date.tm_year = today.year(); // years - [1980..2044]
74 }
75
76
77
78 namespace Orthanc
79 {
80 struct ZipWriter::PImpl
81 {
82 zipFile file_;
83
84 PImpl() : file_(NULL)
85 {
86 }
87 };
88
89 ZipWriter::ZipWriter() :
90 pimpl_(new PImpl),
91 isZip64_(false),
92 hasFileInZip_(false),
93 append_(false),
94 compressionLevel_(6)
95 {
96 }
97
98 ZipWriter::~ZipWriter()
99 {
100 Close();
101 }
102
103 void ZipWriter::Close()
104 {
105 if (IsOpen())
106 {
107 zipClose(pimpl_->file_, "Created by Orthanc");
108 pimpl_->file_ = NULL;
109 hasFileInZip_ = false;
110 }
111 }
112
113 bool ZipWriter::IsOpen() const
114 {
115 return pimpl_->file_ != NULL;
116 }
117
118 void ZipWriter::Open()
119 {
120 if (IsOpen())
121 {
122 return;
123 }
124
125 if (path_.size() == 0)
126 {
127 throw OrthancException(ErrorCode_BadSequenceOfCalls,
128 "Please call SetOutputPath() before creating the file");
129 }
130
131 hasFileInZip_ = false;
132
133 int mode = APPEND_STATUS_CREATE;
134 if (append_ &&
135 boost::filesystem::exists(path_))
136 {
137 mode = APPEND_STATUS_ADDINZIP;
138 }
139
140 if (isZip64_)
141 {
142 pimpl_->file_ = zipOpen64(path_.c_str(), mode);
143 }
144 else
145 {
146 pimpl_->file_ = zipOpen(path_.c_str(), mode);
147 }
148
149 if (!pimpl_->file_)
150 {
151 throw OrthancException(ErrorCode_CannotWriteFile,
152 "Cannot create new ZIP archive: " + path_);
153 }
154 }
155
156 void ZipWriter::SetOutputPath(const char* path)
157 {
158 Close();
159 path_ = path;
160 }
161
162 void ZipWriter::SetZip64(bool isZip64)
163 {
164 Close();
165 isZip64_ = isZip64;
166 }
167
168 void ZipWriter::SetCompressionLevel(uint8_t level)
169 {
170 if (level >= 10)
171 {
172 throw OrthancException(ErrorCode_ParameterOutOfRange,
173 "ZIP compression level must be between 0 (no compression) "
174 "and 9 (highest compression)");
175 }
176
177 Close();
178 compressionLevel_ = level;
179 }
180
181 void ZipWriter::OpenFile(const char* path)
182 {
183 Open();
184
185 zip_fileinfo zfi;
186 PrepareFileInfo(zfi);
187
188 int result;
189
190 if (isZip64_)
191 {
192 result = zipOpenNewFileInZip64(pimpl_->file_, path,
193 &zfi,
194 NULL, 0,
195 NULL, 0,
196 "", // Comment
197 Z_DEFLATED,
198 compressionLevel_, 1);
199 }
200 else
201 {
202 result = zipOpenNewFileInZip(pimpl_->file_, path,
203 &zfi,
204 NULL, 0,
205 NULL, 0,
206 "", // Comment
207 Z_DEFLATED,
208 compressionLevel_);
209 }
210
211 if (result != 0)
212 {
213 throw OrthancException(ErrorCode_CannotWriteFile,
214 "Cannot add new file inside ZIP archive: " + std::string(path));
215 }
216
217 hasFileInZip_ = true;
218 }
219
220
221 void ZipWriter::Write(const std::string& data)
222 {
223 if (data.size())
224 {
225 Write(&data[0], data.size());
226 }
227 }
228
229
230 void ZipWriter::Write(const void* data, size_t length)
231 {
232 if (!hasFileInZip_)
233 {
234 throw OrthancException(ErrorCode_BadSequenceOfCalls, "Call first OpenFile()");
235 }
236
237 const size_t maxBytesInAStep = std::numeric_limits<int32_t>::max();
238
239 const char* p = reinterpret_cast<const char*>(data);
240
241 while (length > 0)
242 {
243 int bytes = static_cast<int32_t>(length <= maxBytesInAStep ? length : maxBytesInAStep);
244
245 if (zipWriteInFileInZip(pimpl_->file_, p, bytes))
246 {
247 throw OrthancException(ErrorCode_CannotWriteFile,
248 "Cannot write data to ZIP archive: " + path_);
249 }
250
251 p += bytes;
252 length -= bytes;
253 }
254 }
255
256
257 void ZipWriter::SetAppendToExisting(bool append)
258 {
259 Close();
260 append_ = append;
261 }
262 }