Line data Source code
1 : /**
2 : Copyright (c) 2022 Roman Katuntsev <sbkarr@stappler.org>
3 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
4 :
5 : Permission is hereby granted, free of charge, to any person obtaining a copy
6 : of this software and associated documentation files (the "Software"), to deal
7 : in the Software without restriction, including without limitation the rights
8 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 : copies of the Software, and to permit persons to whom the Software is
10 : furnished to do so, subject to the following conditions:
11 :
12 : The above copyright notice and this permission notice shall be included in
13 : all copies or substantial portions of the Software.
14 :
15 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 : THE SOFTWARE.
22 : **/
23 :
24 : #ifndef STAPPLER_ZIP_SPZIP_H_
25 : #define STAPPLER_ZIP_SPZIP_H_
26 :
27 : #include "SPBytesView.h"
28 : #include "SPBuffer.h"
29 : #include "SPTime.h"
30 : #include "SPLog.h"
31 : #include "zip.h"
32 :
33 : namespace STAPPLER_VERSIONIZED stappler {
34 :
35 : template <typename Interface>
36 : class ZipArchive : public Interface::AllocBaseType {
37 : public:
38 : using Bytes = typename Interface::BytesType;
39 : using Buffer = BufferTemplate<Interface>;
40 :
41 : ZipArchive();
42 : ZipArchive(BytesView);
43 : ZipArchive(FILE *, bool readonly);
44 :
45 : ~ZipArchive();
46 :
47 : bool addDir(StringView name);
48 : bool addFile(StringView name, BytesView data, bool uncompressed = false);
49 : bool addFile(StringView name, StringView data, bool uncompressed = false);
50 :
51 : Buffer save();
52 :
53 : explicit operator bool () { return _handle != nullptr; }
54 :
55 : size_t size(bool original = false) const;
56 : StringView getName(size_t idx, bool original = false) const;
57 :
58 : void ftw(const Callback<void(StringView path, size_t size, Time time)> &, bool original = false) const;
59 :
60 : protected:
61 : Buffer _data;
62 : Buffer _buffer;
63 :
64 : zip_t *_handle = nullptr;
65 : };
66 :
67 : template <typename Interface>
68 25 : ZipArchive<Interface>::ZipArchive() : ZipArchive(BytesView()) { }
69 :
70 : template <typename Interface>
71 25 : ZipArchive<Interface>::ZipArchive(BytesView b) {
72 25 : if (!b.empty()) {
73 0 : _data.put(b.data(), b.size());
74 : }
75 25 : auto source = zip_source_function_create([] (void *ud, void *data, zip_uint64_t size, zip_source_cmd_t cmd) -> zip_int64_t {
76 1275 : auto d = (ZipArchive<Interface> *)ud;
77 1275 : switch (cmd) {
78 75 : case ZIP_SOURCE_REMOVE:
79 : case ZIP_SOURCE_OPEN:
80 : case ZIP_SOURCE_CLOSE:
81 : case ZIP_SOURCE_FREE:
82 : /* do nothing */
83 75 : return 0;
84 : break;
85 0 : case ZIP_SOURCE_READ: {
86 0 : auto v = d->_data.read(size);
87 0 : memcpy(data, v.data(), v.size());
88 0 : return v.size();
89 : break;
90 : }
91 25 : case ZIP_SOURCE_STAT: {
92 25 : zip_stat_t *stat = (zip_stat_t *)data;
93 25 : zip_stat_init(stat);
94 25 : stat->valid = ZIP_STAT_SIZE;
95 25 : stat->size = d->_data.input();
96 25 : return sizeof(struct zip_stat);
97 : break; /* get meta information */
98 : }
99 0 : case ZIP_SOURCE_ERROR: {
100 0 : int * errdata = (int *)data;
101 0 : errdata[0] = ZIP_ER_INTERNAL;
102 0 : errdata[1] = EINVAL;
103 0 : break; /* get error information */
104 : }
105 150 : case ZIP_SOURCE_SEEK_WRITE:
106 150 : d->_buffer.seek(zip_source_seek_compute_offset(d->_buffer.size(), d->_buffer.input(), data, size, nullptr));
107 150 : return 0;
108 : break; /* get write position */
109 0 : case ZIP_SOURCE_SEEK:
110 0 : d->_data.seek(zip_source_seek_compute_offset(d->_data.size(), d->_data.input(), data, size, nullptr));
111 0 : return 0;
112 : break; /* set position for reading */
113 400 : case ZIP_SOURCE_TELL_WRITE:
114 400 : return d->_buffer.size();
115 : break; /* get write position */
116 0 : case ZIP_SOURCE_TELL:
117 0 : return d->_data.size();
118 : break; /* get read position */
119 25 : case ZIP_SOURCE_SUPPORTS: {
120 25 : auto supports = zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR,
121 : ZIP_SOURCE_FREE, ZIP_SOURCE_SEEK, ZIP_SOURCE_TELL, ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_BEGIN_WRITE, ZIP_SOURCE_COMMIT_WRITE,
122 : ZIP_SOURCE_ROLLBACK_WRITE, ZIP_SOURCE_SEEK_WRITE, ZIP_SOURCE_TELL_WRITE, ZIP_SOURCE_REMOVE, ZIP_SOURCE_WRITE);
123 25 : return supports;
124 : break; /* check whether source supports command */
125 : }
126 25 : case ZIP_SOURCE_BEGIN_WRITE:
127 25 : d->_buffer.clear();
128 25 : d->_buffer = d->_data;
129 25 : return 0;
130 : break; /* prepare for writing */
131 25 : case ZIP_SOURCE_COMMIT_WRITE:
132 25 : d->_data = move(d->_buffer);
133 25 : d->_buffer.clear();
134 25 : return 0;
135 : break; /* writing is done */
136 0 : case ZIP_SOURCE_ROLLBACK_WRITE:
137 0 : d->_buffer.clear();
138 0 : return 0;
139 : break; /* discard written changes */
140 550 : case ZIP_SOURCE_WRITE:
141 550 : return d->_buffer.put((const uint8_t *)data, size);
142 : break; /* write data */
143 0 : default:
144 0 : break;
145 : }
146 0 : return -1;
147 : }, this, nullptr);
148 :
149 : zip_error_t err;
150 :
151 25 : _handle = zip_open_from_source(source, ZIP_CREATE | ZIP_TRUNCATE, &err);
152 25 : if (!_handle) {
153 0 : log::warn("ZipArchive", "Fail to create archive: ", err.str);
154 : }
155 25 : }
156 :
157 : template <typename Interface>
158 : ZipArchive<Interface>::ZipArchive(FILE *file, bool readonly) {
159 : auto source = zip_source_filep_create(file, 0, -1, nullptr);
160 :
161 : _handle = zip_open_from_source(source, readonly ? ZIP_RDONLY : 0, nullptr);
162 : }
163 :
164 : template <typename Interface>
165 25 : ZipArchive<Interface>::~ZipArchive() {
166 25 : if (_handle) {
167 0 : zip_discard(_handle);
168 0 : _handle = nullptr;
169 : }
170 25 : }
171 :
172 : template <typename Interface>
173 : bool ZipArchive<Interface>::addDir(StringView name) {
174 : return zip_dir_add(_handle, name.terminated() ? name.data() : name.str<Interface>().data(), ZIP_FL_ENC_UTF_8) >= 0;
175 : }
176 :
177 : template <typename Interface>
178 75 : bool ZipArchive<Interface>::addFile(StringView name, BytesView data, bool uncompressed) {
179 75 : zip_source_t *source = nullptr;
180 75 : uint8_t *buf = nullptr;
181 :
182 : if constexpr (std::is_same<Interface, memory::PoolInterface>::value) {
183 : buf = (uint8_t *)memory::pool::palloc(memory::pool::acquire(), data.size());
184 : memcpy(buf, data.data(), data.size());
185 : source = zip_source_buffer(_handle, buf, data.size(), 0);
186 : } else {
187 75 : buf = new uint8_t[data.size()];
188 75 : memcpy(buf, data.data(), data.size());
189 75 : source = zip_source_buffer(_handle, buf, data.size(), 1);
190 : }
191 75 : if (source) {
192 75 : auto idx = zip_file_add(_handle, name.terminated() ? name.data() : name.str<Interface>().data(), source, ZIP_FL_ENC_UTF_8);
193 75 : if (idx < 0) {
194 0 : auto err = zip_get_error(_handle);
195 0 : if (err) {
196 0 : std::cout << "ZIP error: " << zip_error_strerror(err) << "\n";
197 : }
198 :
199 0 : zip_source_free(source);
200 0 : return false;
201 : }
202 :
203 75 : if (uncompressed) {
204 50 : zip_set_file_compression(_handle, idx, ZIP_CM_STORE, 0);
205 : }
206 :
207 75 : return true;
208 : }
209 0 : return false;
210 : }
211 :
212 : template <typename Interface>
213 25 : bool ZipArchive<Interface>::addFile(StringView name, StringView data, bool uncompressed) {
214 25 : return addFile(name, BytesView((const uint8_t *)data.data(), data.size()), uncompressed);
215 : }
216 :
217 : template <typename Interface>
218 25 : auto ZipArchive<Interface>::save() -> ZipArchive<Interface>::Buffer {
219 25 : auto err = zip_close(_handle);
220 25 : if (err < 0) {
221 0 : zip_discard(_handle);
222 0 : return Buffer();
223 : }
224 25 : _handle = nullptr;
225 25 : return move(_data);
226 : }
227 :
228 : template <typename Interface>
229 : size_t ZipArchive<Interface>::size(bool original) const {
230 : return zip_get_num_entries(_handle, original ? ZIP_FL_UNCHANGED : 0);
231 : }
232 :
233 : template <typename Interface>
234 : StringView ZipArchive<Interface>::getName(size_t idx, bool original) const {
235 : return zip_get_name(_handle, idx, original ? ZIP_FL_UNCHANGED | ZIP_FL_ENC_GUESS : ZIP_FL_ENC_GUESS);
236 : }
237 :
238 : template <typename Interface>
239 : void ZipArchive<Interface>::ftw(const Callback<void(StringView path, size_t size, Time time)> &cb, bool original) const {
240 : zip_stat_t stat;
241 : for (size_t i = 0; i < size(original); ++ i) {
242 : zip_stat_index(_handle, i, ZIP_STAT_SIZE | ZIP_STAT_MTIME | ZIP_STAT_NAME, &stat);
243 : cb(stat.name, stat.size, Time::seconds(stat.mtime));
244 : }
245 : }
246 :
247 : }
248 :
249 : #endif /* STAPPLER_ZIP_SPZIP_H_ */
|