Line data Source code
1 : // Copyright (c) 2011-2022 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/edhttp
4 : // contact@m2osw.com
5 : //
6 : // This program is free software: you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation, either version 3 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License
17 : // along with this program. If not, see <https://www.gnu.org/licenses/>.
18 :
19 : /** \file
20 : * \brief Verify the archiver classes.
21 : *
22 : * This file implements tests to verify that the archiver classes.
23 : */
24 :
25 : // edhttp
26 : //
27 : #include <edhttp/compression/archiver.h>
28 :
29 : #include <edhttp/exception.h>
30 :
31 :
32 : // self
33 : //
34 : #include "catch_main.h"
35 :
36 :
37 : // snapdev
38 : //
39 : #include <snapdev/string_replace_many.h>
40 :
41 :
42 : // snaplogger
43 : //
44 : #include <snaplogger/message.h>
45 :
46 :
47 : // C
48 : //
49 : #include <tar.h>
50 :
51 :
52 : // last include
53 : //
54 : #include <snapdev/poison.h>
55 :
56 :
57 :
58 : namespace
59 : {
60 :
61 :
62 :
63 14 : std::uint32_t check_sum(unsigned char const * s)
64 : {
65 14 : std::uint32_t result = 8 * ' '; // the checksum field
66 :
67 : // name + mode + uid + gid + size + mtime = 148 bytes
68 : //
69 2086 : for(int n(148); n > 0; --n, ++s)
70 : {
71 2072 : result += *s;
72 : }
73 :
74 14 : s += 8; // skip the checksum field
75 :
76 : // everything after the checksum is another 356 bytes
77 : //
78 4998 : for(int n(356); n > 0; --n, ++s)
79 : {
80 4984 : result += *s;
81 : }
82 :
83 14 : return result;
84 : }
85 :
86 :
87 :
88 : } // no name namespace
89 :
90 :
91 :
92 2 : CATCH_TEST_CASE("archiver_file", "[archiver]")
93 : {
94 2 : CATCH_START_SECTION("archiver_file: verify archiver file defaults")
95 : {
96 1 : edhttp::archiver_file file;
97 :
98 1 : CATCH_REQUIRE(file.get_type() == edhttp::file_type_t::FILE_TYPE_REGULAR);
99 1 : CATCH_REQUIRE(file.get_data() == edhttp::buffer_t());
100 1 : CATCH_REQUIRE(file.get_filename() == std::string());
101 1 : CATCH_REQUIRE(file.get_user() == std::string());
102 1 : CATCH_REQUIRE(file.get_group() == std::string());
103 1 : CATCH_REQUIRE(file.get_uid() == 0);
104 1 : CATCH_REQUIRE(file.get_gid() == 0);
105 1 : CATCH_REQUIRE(file.get_mode() == 0);
106 1 : CATCH_REQUIRE(file.get_mtime() == snapdev::timespec_ex());
107 1 : }
108 2 : CATCH_END_SECTION()
109 :
110 2 : CATCH_START_SECTION("archiver_file: verify set/get file metadata")
111 : {
112 1 : edhttp::archiver_file file;
113 :
114 1 : CATCH_REQUIRE(file.get_type() == edhttp::file_type_t::FILE_TYPE_REGULAR);
115 1 : file.set_type(edhttp::file_type_t::FILE_TYPE_DIRECTORY);
116 1 : CATCH_REQUIRE(file.get_type() == edhttp::file_type_t::FILE_TYPE_DIRECTORY);
117 1 : file.set_type(edhttp::file_type_t::FILE_TYPE_REGULAR);
118 1 : CATCH_REQUIRE(file.get_type() == edhttp::file_type_t::FILE_TYPE_REGULAR);
119 :
120 1 : CATCH_REQUIRE(file.get_data() == edhttp::buffer_t());
121 1 : auto const data(SNAP_CATCH2_NAMESPACE::random_buffer(1, 1024 * 16));
122 1 : file.set_data(data);
123 1 : CATCH_REQUIRE(file.get_data() == data);
124 :
125 1 : CATCH_REQUIRE(file.get_filename() == std::string());
126 1 : file.set_filename("/this/file/here");
127 1 : CATCH_REQUIRE(file.get_filename() == "/this/file/here");
128 :
129 1 : CATCH_REQUIRE(file.get_user() == std::string());
130 1 : CATCH_REQUIRE(file.get_uid() == 0);
131 1 : file.set_user("edhttp", 1'000);
132 1 : CATCH_REQUIRE(file.get_user() == "edhttp");
133 1 : CATCH_REQUIRE(file.get_uid() == 1'000);
134 :
135 1 : CATCH_REQUIRE(file.get_group() == std::string());
136 1 : CATCH_REQUIRE(file.get_gid() == 0);
137 1 : file.set_group("edhttp", 1'230);
138 1 : CATCH_REQUIRE(file.get_group() == "edhttp");
139 1 : CATCH_REQUIRE(file.get_gid() == 1'230);
140 :
141 1 : CATCH_REQUIRE(file.get_mode() == 0);
142 1 : file.set_mode(0750);
143 1 : CATCH_REQUIRE(file.get_mode() == 0750);
144 :
145 1 : snapdev::timespec_ex const now(snapdev::now());
146 1 : CATCH_REQUIRE(file.get_mtime() == snapdev::timespec_ex());
147 1 : file.set_mtime(now);
148 1 : CATCH_REQUIRE(file.get_mtime() == now);
149 1 : }
150 2 : CATCH_END_SECTION()
151 2 : }
152 :
153 :
154 3 : CATCH_TEST_CASE("archiver_tar", "[archiver]")
155 : {
156 3 : CATCH_START_SECTION("archiver_tar: verify tar archiver")
157 : {
158 1 : edhttp::archiver * tar(edhttp::get_archiver("tar"));
159 1 : CATCH_REQUIRE(tar != nullptr);
160 1 : CATCH_REQUIRE(strcmp(tar->get_name(), "tar") == 0);
161 :
162 1 : edhttp::archiver_archive archive;
163 :
164 : // add files to the tarball using randomly generated buffers
165 : // and metadata
166 : //
167 1 : constexpr std::size_t const FILE_COUNT = 15;
168 2 : std::vector<edhttp::buffer_t> file_data(FILE_COUNT);
169 2 : std::vector<snapdev::timespec_ex> file_mtime(FILE_COUNT);
170 16 : for(std::size_t i(0); i < FILE_COUNT; ++i)
171 : {
172 15 : file_data[i] = SNAP_CATCH2_NAMESPACE::random_buffer(1, 1024 * 64);
173 15 : file_mtime[i] = snapdev::now();
174 :
175 15 : edhttp::archiver_file file;
176 15 : file.set_type(edhttp::file_type_t::FILE_TYPE_REGULAR);
177 15 : file.set_data(file_data[i]);
178 15 : file.set_filename("this/file-" + std::to_string(i + 1));
179 15 : file.set_user("user-" + std::to_string(i + 1), 1000 + i);
180 15 : file.set_group("group-" + std::to_string(i + 1), 2000 + i);
181 15 : file.set_mode(0640);
182 15 : file.set_mtime(file_mtime[i]);
183 :
184 15 : tar->append_file(archive, file);
185 15 : }
186 :
187 16 : for(std::size_t i(0); i < FILE_COUNT; ++i)
188 : {
189 15 : edhttp::archiver_file file;
190 15 : CATCH_REQUIRE(tar->next_file(archive, file));
191 :
192 15 : CATCH_REQUIRE(file.get_type() == edhttp::file_type_t::FILE_TYPE_REGULAR);
193 15 : CATCH_REQUIRE(file.get_data() == file_data[i]);
194 15 : CATCH_REQUIRE(file.get_filename() == "this/file-" + std::to_string(i + 1));
195 15 : CATCH_REQUIRE(file.get_user() == "user-" + std::to_string(i + 1));
196 15 : CATCH_REQUIRE(file.get_uid() == 1000 + i);
197 15 : CATCH_REQUIRE(file.get_group() == "group-" + std::to_string(i + 1));
198 15 : CATCH_REQUIRE(file.get_gid() == 2000 + i);
199 15 : CATCH_REQUIRE(file.get_mode() == 0640);
200 15 : CATCH_REQUIRE(file.get_mtime().tv_sec == file_mtime[i].tv_sec);
201 15 : CATCH_REQUIRE(file.get_mtime().tv_nsec == 0);
202 15 : }
203 :
204 : {
205 1 : edhttp::archiver_file file;
206 1 : CATCH_REQUIRE_FALSE(tar->next_file(archive, file));
207 1 : CATCH_REQUIRE_FALSE(tar->next_file(archive, file));
208 1 : CATCH_REQUIRE_FALSE(tar->next_file(archive, file));
209 1 : CATCH_REQUIRE_FALSE(tar->next_file(archive, file));
210 1 : }
211 1 : }
212 3 : CATCH_END_SECTION()
213 :
214 3 : CATCH_START_SECTION("archiver_tar: verify tar archiver with long filenames")
215 : {
216 1 : edhttp::archiver * tar(edhttp::get_archiver("tar"));
217 1 : CATCH_REQUIRE(tar != nullptr);
218 1 : CATCH_REQUIRE(strcmp(tar->get_name(), "tar") == 0);
219 :
220 1 : edhttp::archiver_archive archive;
221 :
222 : // add files to the tarball using randomly generated buffers
223 : // and metadata
224 : //
225 1 : constexpr std::size_t const FILE_COUNT = 15;
226 1 : std::size_t file_count(0);
227 1 : std::vector<edhttp::buffer_t> file_data;
228 1 : std::vector<snapdev::timespec_ex> file_mtime;
229 1 : std::vector<std::string> file_filename;
230 16 : for(std::size_t i(0); i < FILE_COUNT; ++i)
231 : {
232 15 : std::string filename;
233 15 : std::size_t max_segments(rand() % 10 + 5);
234 30 : std::vector<std::string> segments(max_segments);
235 : for(;;)
236 : {
237 15 : segments.clear();
238 166 : for(std::size_t j(0); j < max_segments; ++j)
239 : {
240 302 : segments.push_back(snapdev::string_replace_many(SNAP_CATCH2_NAMESPACE::random_string(
241 : 1
242 : , 24
243 : , SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_ASCII)
244 : , {{"/", "-"}}));
245 : }
246 15 : segments[max_segments - 1] += '_';
247 15 : segments[max_segments - 1] += std::to_string(file_count + 1);
248 :
249 15 : std::size_t filename_len(0);
250 15 : std::size_t prefix_len(0);
251 166 : for(std::size_t j(max_segments); j > 0; )
252 : {
253 151 : --j;
254 151 : if(prefix_len == 0
255 151 : && filename_len + segments[j].length() <= 100)
256 : {
257 100 : if(filename_len != 0)
258 : {
259 85 : ++filename_len; // for the '/'
260 : }
261 100 : filename_len += segments[j].length();
262 : }
263 : else
264 : {
265 51 : if(prefix_len != 0)
266 : {
267 38 : ++prefix_len; // for the '/'
268 : }
269 51 : prefix_len += segments[j].length();
270 : }
271 : }
272 :
273 15 : if(prefix_len <= 155)
274 : {
275 15 : filename = snapdev::join_strings(segments, "/");
276 15 : break;
277 : }
278 :
279 : // prefix too long, try again
280 0 : }
281 :
282 : // do we have folders?
283 : //
284 15 : if(max_segments > 1)
285 : {
286 151 : for(std::size_t j(1); j < max_segments; ++j)
287 : {
288 680 : std::string const dir_name(snapdev::join_strings(std::vector<std::string>(segments.begin(), segments.begin() + j), "/"));
289 :
290 : // make the data buffer very small because we don't
291 : // need it for directories (it gets ignored)
292 : //
293 136 : file_data.push_back(SNAP_CATCH2_NAMESPACE::random_buffer(0, 1024));
294 136 : file_mtime.push_back(snapdev::now());
295 136 : file_filename.push_back(dir_name);
296 :
297 136 : edhttp::archiver_file dir;
298 136 : dir.set_type(edhttp::file_type_t::FILE_TYPE_DIRECTORY);
299 136 : dir.set_data(file_data[file_count]); // this must be ignored for directories
300 136 : dir.set_filename(dir_name);
301 136 : dir.set_user("user-" + std::to_string(file_count + 1), 1000 + file_count);
302 136 : dir.set_group("group-" + std::to_string(file_count + 1), 2000 + file_count);
303 136 : dir.set_mode(0750);
304 136 : dir.set_mtime(file_mtime[file_count]);
305 :
306 136 : tar->append_file(archive, dir);
307 136 : ++file_count;
308 136 : }
309 : }
310 :
311 : {
312 15 : file_data.push_back(SNAP_CATCH2_NAMESPACE::random_buffer(1, 1024 * 64));
313 15 : file_mtime.push_back(snapdev::now());
314 15 : file_filename.push_back(filename);
315 :
316 15 : edhttp::archiver_file file;
317 15 : file.set_type(edhttp::file_type_t::FILE_TYPE_REGULAR);
318 15 : file.set_data(file_data[file_count]);
319 15 : file.set_filename(filename);
320 15 : file.set_user("user-" + std::to_string(file_count + 1), 1000 + file_count);
321 15 : file.set_group("group-" + std::to_string(file_count + 1), 2000 + file_count);
322 15 : file.set_mode(0640);
323 15 : file.set_mtime(file_mtime[file_count]);
324 :
325 15 : std::size_t const header_pos(archive.get().size());
326 :
327 15 : tar->append_file(archive, file);
328 15 : ++file_count;
329 :
330 : // once in a while, add a '/' at the end of the prefix
331 : //
332 15 : if(filename.length() > 100 && (rand() & 1) != 0)
333 : {
334 9 : unsigned char * header(archive.get().data() + header_pos);
335 9 : std::size_t const len(strlen(reinterpret_cast<char const *>(header) + 345));
336 9 : if(len < 155)
337 : {
338 9 : header[345 + len] = '/';
339 :
340 : // if this happens with a regular file, also test
341 : // with CONTTYPE instead
342 : //
343 9 : if(header[156] == REGTYPE)
344 : {
345 : header[156] == CONTTYPE;
346 : }
347 :
348 : // adjust the checksum
349 : //
350 9 : std::uint32_t const checksum(check_sum(header));
351 9 : std::stringstream ss;
352 9 : ss << std::oct
353 : << std::setw(6)
354 9 : << std::setfill('0')
355 9 : << checksum;
356 9 : strncpy(reinterpret_cast<char *>(header) + 148, ss.str().c_str(), 6);
357 9 : }
358 : }
359 15 : }
360 15 : }
361 :
362 : #if 0
363 : edhttp::buffer_t data(archive.get());
364 : std::ofstream out("data.tar");
365 : out.write(reinterpret_cast<char const *>(data.data()), data.size());
366 : #endif
367 :
368 3 : for(int a(0); a < 2; ++a)
369 : {
370 304 : for(std::size_t i(0); i < file_count; ++i)
371 : {
372 302 : edhttp::archiver_file file;
373 302 : CATCH_REQUIRE(tar->next_file(archive, file));
374 :
375 302 : if(file.get_type() == edhttp::file_type_t::FILE_TYPE_DIRECTORY)
376 : {
377 272 : CATCH_REQUIRE(file.get_data().size() == 0);
378 272 : CATCH_REQUIRE(file.get_data() == edhttp::buffer_t());
379 272 : CATCH_REQUIRE(file.get_filename() == file_filename[i]);
380 272 : CATCH_REQUIRE(file.get_user() == "user-" + std::to_string(i + 1));
381 272 : CATCH_REQUIRE(file.get_uid() == 1000 + i);
382 272 : CATCH_REQUIRE(file.get_group() == "group-" + std::to_string(i + 1));
383 272 : CATCH_REQUIRE(file.get_gid() == 2000 + i);
384 272 : CATCH_REQUIRE(file.get_mode() == 0750);
385 272 : CATCH_REQUIRE(file.get_mtime().tv_sec == file_mtime[i].tv_sec);
386 272 : CATCH_REQUIRE(file.get_mtime().tv_nsec == 0);
387 : }
388 : else
389 : {
390 30 : CATCH_REQUIRE(file.get_type() == edhttp::file_type_t::FILE_TYPE_REGULAR);
391 30 : CATCH_REQUIRE(file.get_data().size() == file_data[i].size());
392 30 : CATCH_REQUIRE(file.get_data() == file_data[i]);
393 30 : CATCH_REQUIRE(file.get_filename() == file_filename[i]);
394 30 : CATCH_REQUIRE(file.get_user() == "user-" + std::to_string(i + 1));
395 30 : CATCH_REQUIRE(file.get_uid() == 1000 + i);
396 30 : CATCH_REQUIRE(file.get_group() == "group-" + std::to_string(i + 1));
397 30 : CATCH_REQUIRE(file.get_gid() == 2000 + i);
398 30 : CATCH_REQUIRE(file.get_mode() == 0640);
399 30 : CATCH_REQUIRE(file.get_mtime().tv_sec == file_mtime[i].tv_sec);
400 30 : CATCH_REQUIRE(file.get_mtime().tv_nsec == 0);
401 : }
402 302 : }
403 :
404 : // once we reached the end, we get `false`
405 : {
406 2 : edhttp::archiver_file file;
407 2 : CATCH_REQUIRE_FALSE(tar->next_file(archive, file));
408 2 : }
409 :
410 : // append an empty header, which is legal at the end of tar files
411 4 : edhttp::buffer_t zeroes(512);
412 2 : archive.get().insert(archive.get().end(), zeroes.begin(), zeroes.end());
413 :
414 : // we can repeat the verification by rewinding
415 : //
416 2 : tar->rewind(archive);
417 2 : }
418 1 : }
419 3 : CATCH_END_SECTION()
420 :
421 3 : CATCH_START_SECTION("archiver_tar: verify large checksum")
422 : {
423 1 : edhttp::archiver * tar(edhttp::get_archiver("tar"));
424 1 : CATCH_REQUIRE(tar != nullptr);
425 1 : CATCH_REQUIRE(strcmp(tar->get_name(), "tar") == 0);
426 :
427 1 : edhttp::archiver_archive archive;
428 :
429 : // create a filename that will make the checksum go over 32,767
430 : //
431 : // TODO: see whether a defined UTF-8 character can be used instead
432 : // of 0x10FFFD which is considered valid but is not (yet)
433 : // defined
434 : //
435 1 : std::string filename;
436 1 : std::string const last_utf8{ // 0x10FFFD
437 : static_cast<char>(0xF4),
438 : static_cast<char>(0x8F),
439 : static_cast<char>(0xBF),
440 : static_cast<char>(0xBD),
441 2 : };
442 39 : for(int i(0); i < 155 / 4; ++i)
443 : {
444 38 : filename += last_utf8;
445 : }
446 : //filename += std::string(155, '\xFF'); // prefix
447 1 : filename += '/';
448 : //filename += std::string(100, '\xFF'); // filename
449 26 : for(int i(0); i < 100 / 4; ++i)
450 : {
451 25 : filename += last_utf8;
452 : }
453 :
454 1 : edhttp::buffer_t const data(SNAP_CATCH2_NAMESPACE::random_buffer(1, 25));
455 1 : snapdev::timespec_ex const now(snapdev::now());
456 :
457 : {
458 1 : edhttp::archiver_file file;
459 1 : file.set_type(edhttp::file_type_t::FILE_TYPE_REGULAR);
460 1 : file.set_data(data);
461 1 : file.set_filename(filename);
462 1 : file.set_user("edhttp", 1000);
463 1 : file.set_group("edhttp", 1000);
464 1 : file.set_mode(0444);
465 1 : file.set_mtime(now);
466 1 : tar->append_file(archive, file);
467 1 : }
468 :
469 : {
470 1 : edhttp::archiver_file file;
471 1 : CATCH_REQUIRE(tar->next_file(archive, file));
472 :
473 1 : CATCH_REQUIRE(file.get_type() == edhttp::file_type_t::FILE_TYPE_REGULAR);
474 1 : CATCH_REQUIRE(file.get_data().size() == data.size());
475 1 : CATCH_REQUIRE(file.get_data() == data);
476 1 : CATCH_REQUIRE(file.get_filename() == filename);
477 1 : CATCH_REQUIRE(file.get_user() == "edhttp");
478 1 : CATCH_REQUIRE(file.get_uid() == 1000);
479 1 : CATCH_REQUIRE(file.get_group() == "edhttp");
480 1 : CATCH_REQUIRE(file.get_gid() == 1000);
481 1 : CATCH_REQUIRE(file.get_mode() == 0444);
482 1 : CATCH_REQUIRE(file.get_mtime().tv_sec == now.tv_sec);
483 1 : CATCH_REQUIRE(file.get_mtime().tv_nsec == 0);
484 1 : }
485 :
486 : {
487 1 : edhttp::archiver_file file;
488 1 : CATCH_REQUIRE_FALSE(tar->next_file(archive, file));
489 1 : }
490 1 : }
491 3 : CATCH_END_SECTION()
492 3 : }
493 :
494 :
495 2 : CATCH_TEST_CASE("archiver", "[archiver]")
496 : {
497 2 : CATCH_START_SECTION("archiver: verify list of archivers in our library")
498 : {
499 1 : advgetopt::string_list_t const list(edhttp::archiver_list());
500 :
501 1 : CATCH_REQUIRE(list.size() == 1);
502 :
503 : // internally it's in a map so it remains sorted
504 : //
505 1 : CATCH_REQUIRE(list[0] == "tar");
506 :
507 1 : auto const input(SNAP_CATCH2_NAMESPACE::random_buffer(1024, 1024 * 16));
508 2 : for(auto const & name : list)
509 : {
510 1 : edhttp::archiver * a(edhttp::get_archiver(name));
511 1 : CATCH_REQUIRE(a != nullptr);
512 : }
513 1 : }
514 2 : CATCH_END_SECTION()
515 :
516 2 : CATCH_START_SECTION("archiver: verify the \"unknown\" archiver does not exist")
517 : {
518 1 : edhttp::archiver * unknown(edhttp::get_archiver("unknown"));
519 1 : CATCH_REQUIRE(unknown == nullptr);
520 : }
521 2 : CATCH_END_SECTION()
522 :
523 : // CATCH_START_SECTION("compressor: compress() with an empty input buffer")
524 : // {
525 : // // compress() with an empty buffer ignores the other parameters
526 : // //
527 : // advgetopt::string_list_t const list(edhttp::compressor_list());
528 : // for(int i(0); i < 10; ++i)
529 : // {
530 : // edhttp::buffer_t const buffer;
531 : // std::string compressor_name(list[rand() % list.size()]);
532 : // edhttp::result_t const compressed(edhttp::compress({compressor_name}, buffer, rand() % 96 + 5, rand() % 1 == 0));
533 : // CATCH_REQUIRE(compressed.first == buffer);
534 : // CATCH_REQUIRE(compressed.second == edhttp::compressor::NO_COMPRESSION);
535 : // }
536 : // }
537 : // CATCH_END_SECTION()
538 : //
539 : // CATCH_START_SECTION("compressor: compress() with too small a level")
540 : // {
541 : // // compress() with an empty buffer ignores the other parameters
542 : // //
543 : // advgetopt::string_list_t const list(edhttp::compressor_list());
544 : // for(int i(0); i < 10; ++i)
545 : // {
546 : // edhttp::buffer_t const buffer(SNAP_CATCH2_NAMESPACE::random_buffer(1, 1024));
547 : // std::string compressor_name(list[rand() % list.size()]);
548 : // edhttp::result_t const compressed(edhttp::compress({compressor_name}, buffer, rand() % 5, rand() % 1 == 0));
549 : // CATCH_REQUIRE(compressed.first == buffer);
550 : // CATCH_REQUIRE(compressed.second == edhttp::compressor::NO_COMPRESSION);
551 : // }
552 : // }
553 : // CATCH_END_SECTION()
554 : //
555 : // CATCH_START_SECTION("compressor: compress() with unknown compressor")
556 : // {
557 : // // compress() with an empty buffer ignores the other parameters
558 : // //
559 : // for(int i(0); i < 10; ++i)
560 : // {
561 : // edhttp::buffer_t const buffer(SNAP_CATCH2_NAMESPACE::random_buffer(1024, 1024 * 16));
562 : // std::string compressor_name("unknown");
563 : // edhttp::result_t const compressed(edhttp::compress({compressor_name}, buffer, rand() % 96 + 5, rand() % 1 == 0));
564 : // CATCH_REQUIRE(compressed.second == edhttp::compressor::NO_COMPRESSION);
565 : // CATCH_REQUIRE(compressed.first == buffer);
566 : // }
567 : // }
568 : // CATCH_END_SECTION()
569 : //
570 : // CATCH_START_SECTION("compressor: compress() small buffers with bz2 return input")
571 : // {
572 : // // compress() with an empty buffer ignores the other parameters
573 : // //
574 : // for(int i(1); i < 10; ++i)
575 : // {
576 : // edhttp::buffer_t const buffer(SNAP_CATCH2_NAMESPACE::random_buffer(1, i));
577 : // std::string compressor_name("bz2");
578 : // edhttp::result_t const compressed(edhttp::compress({compressor_name}, buffer, 100, rand() % 1 == 0));
579 : // CATCH_REQUIRE(compressed.first == buffer);
580 : // CATCH_REQUIRE(compressed.second == edhttp::compressor::NO_COMPRESSION);
581 : // }
582 : // }
583 : // CATCH_END_SECTION()
584 : //
585 : // CATCH_START_SECTION("compressor: compress() small buffers with gzip return input")
586 : // {
587 : // // compress() with an empty buffer ignores the other parameters
588 : // //
589 : // for(int i(1); i < 10; ++i)
590 : // {
591 : // edhttp::buffer_t const buffer(SNAP_CATCH2_NAMESPACE::random_buffer(1, i));
592 : // edhttp::result_t const compressed(edhttp::compress({"gzip"}, buffer, rand() % 96 + 5, rand() % 1 == 0));
593 : // CATCH_REQUIRE(compressed.first == buffer);
594 : // CATCH_REQUIRE(compressed.second == edhttp::compressor::NO_COMPRESSION);
595 : // }
596 : // }
597 : // CATCH_END_SECTION()
598 : //
599 : // CATCH_START_SECTION("compressor: compress() with deflate; decompress explicitly")
600 : // {
601 : // // get text because it compresses well and the test will work
602 : // //
603 : // snapdev::file_contents source(SNAP_CATCH2_NAMESPACE::g_source_dir() + "/tests/catch_compressor.cpp");
604 : // CATCH_REQUIRE(source.read_all());
605 : // std::string const data(source.contents());
606 : // edhttp::buffer_t const buffer(data.begin(), data.end());
607 : // edhttp::compressor * deflate(edhttp::get_compressor("deflate"));
608 : // CATCH_REQUIRE(deflate != nullptr);
609 : // CATCH_REQUIRE(strcmp(deflate->get_name(), "deflate") == 0);
610 : // for(int i(1); i < 10; ++i)
611 : // {
612 : // std::string compressor_name("deflate");
613 : // edhttp::result_t const compressed(edhttp::compress({compressor_name}, buffer, rand() % 96 + 5, true));
614 : // CATCH_REQUIRE(compressed.second == "deflate");
615 : //
616 : // // deflate data as no magic, so calling decompress() "fails"
617 : // //
618 : // edhttp::result_t const decompressed(edhttp::decompress(compressed.first));
619 : // CATCH_REQUIRE(decompressed.second == edhttp::compressor::NO_COMPRESSION);
620 : // CATCH_REQUIRE(decompressed.first == compressed.first); // still compressed
621 : //
622 : // // instead we have to explicitly decompress
623 : // //
624 : // edhttp::buffer_t const original(deflate->decompress(compressed.first, buffer.size()));
625 : // CATCH_REQUIRE(original == buffer);
626 : // }
627 : // }
628 : // CATCH_END_SECTION()
629 : //
630 : // CATCH_START_SECTION("compressor: compress()/decompress() with bz2")
631 : // {
632 : // // get text because it compresses well and the test will work
633 : // //
634 : // snapdev::file_contents source(SNAP_CATCH2_NAMESPACE::g_source_dir() + "/tests/catch_compressor.cpp");
635 : // CATCH_REQUIRE(source.read_all());
636 : // std::string const data(source.contents());
637 : // edhttp::buffer_t const buffer(data.begin(), data.end());
638 : // edhttp::compressor * bz2(edhttp::get_compressor("bz2"));
639 : // CATCH_REQUIRE(bz2 != nullptr);
640 : // CATCH_REQUIRE(strcmp(bz2->get_name(), "bz2") == 0);
641 : // for(int i(1); i < 10; ++i)
642 : // {
643 : // std::string compressor_name("bz2");
644 : // edhttp::result_t const compressed(edhttp::compress({compressor_name}, buffer, rand() % 96 + 5, true));
645 : // CATCH_REQUIRE(compressed.second == "bz2");
646 : // CATCH_REQUIRE(bz2->compatible(compressed.first));
647 : // CATCH_REQUIRE(compressed.first != buffer);
648 : // edhttp::result_t const decompressed(edhttp::decompress(compressed.first));
649 : // CATCH_REQUIRE(decompressed.second == "bz2");
650 : // CATCH_REQUIRE(decompressed.first == buffer);
651 : // }
652 : // }
653 : // CATCH_END_SECTION()
654 : //
655 : // CATCH_START_SECTION("compressor: compress()/decompress() a large buffer with bz2")
656 : // {
657 : // // get text because it compresses well and the test will work
658 : // //
659 : // snapdev::file_contents source(SNAP_CATCH2_NAMESPACE::g_source_dir() + "/tests/catch_compressor.cpp");
660 : // CATCH_REQUIRE(source.read_all());
661 : // std::string data(source.contents());
662 : // while(data.length() < 1024 * 100 + 1)
663 : // {
664 : // data += data;
665 : // }
666 : // edhttp::buffer_t const buffer(data.begin(), data.end());
667 : // edhttp::compressor * bz2(edhttp::get_compressor("bz2"));
668 : // CATCH_REQUIRE(bz2 != nullptr);
669 : // CATCH_REQUIRE(strcmp(bz2->get_name(), "bz2") == 0);
670 : // for(int i(1); i < 10; ++i)
671 : // {
672 : // edhttp::result_t const compressed(edhttp::compress({"bz2"}, buffer, rand() % 96 + 5, true));
673 : // CATCH_REQUIRE(compressed.second == "bz2");
674 : // CATCH_REQUIRE(bz2->compatible(compressed.first));
675 : // CATCH_REQUIRE(compressed.first != buffer);
676 : // edhttp::result_t const decompressed(edhttp::decompress(compressed.first));
677 : // CATCH_REQUIRE(decompressed.second == "bz2");
678 : // CATCH_REQUIRE(decompressed.first == buffer);
679 : //
680 : // // also test with the size
681 : // //
682 : // edhttp::buffer_t const decompressed2(bz2->decompress(compressed.first, buffer.size()));
683 : // CATCH_REQUIRE(decompressed2 == buffer);
684 : // }
685 : // }
686 : // CATCH_END_SECTION()
687 : //
688 : // CATCH_START_SECTION("compressor: compress()/decompress() with gzip")
689 : // {
690 : // // get text because it compresses well and the test will work
691 : // //
692 : // snapdev::file_contents source(SNAP_CATCH2_NAMESPACE::g_source_dir() + "/tests/catch_compressor.cpp");
693 : // CATCH_REQUIRE(source.read_all());
694 : // std::string const data(source.contents());
695 : // edhttp::buffer_t const buffer(data.begin(), data.end());
696 : // edhttp::compressor * gzip(edhttp::get_compressor("gzip"));
697 : // CATCH_REQUIRE(gzip != nullptr);
698 : // CATCH_REQUIRE(strcmp(gzip->get_name(), "gzip") == 0);
699 : // for(int i(1); i < 10; ++i)
700 : // {
701 : // std::string compressor_name("gzip");
702 : // edhttp::result_t const compressed(edhttp::compress({compressor_name}, buffer, rand() % 96 + 5, true));
703 : // CATCH_REQUIRE(compressed.second == "gzip");
704 : // CATCH_REQUIRE(gzip->compatible(compressed.first));
705 : // CATCH_REQUIRE(compressed.first != buffer);
706 : // edhttp::result_t const decompressed(edhttp::decompress(compressed.first));
707 : // CATCH_REQUIRE(decompressed.second == "gzip");
708 : // CATCH_REQUIRE(decompressed.first == buffer);
709 : // }
710 : // }
711 : // CATCH_END_SECTION()
712 : //
713 : // CATCH_START_SECTION("compressor: compress()/decompress() with best compressor")
714 : // {
715 : // // get text because it compresses well and the test works every time
716 : // //
717 : // snapdev::file_contents source(SNAP_CATCH2_NAMESPACE::g_source_dir() + "/tests/catch_compressor.cpp");
718 : // CATCH_REQUIRE(source.read_all());
719 : // std::string const data(source.contents());
720 : // edhttp::buffer_t const buffer(data.begin(), data.end());
721 : // for(int i(0); i < 10; ++i)
722 : // {
723 : // advgetopt::string_list_t names;
724 : // if((i & 1) == 1)
725 : // {
726 : // names.push_back("bz2");
727 : // names.push_back("gzip");
728 : // std::random_shuffle(names.begin(), names.end());
729 : // }
730 : // edhttp::result_t const compressed(edhttp::compress({}, buffer, rand() % 96 + 5, true));
731 : // if(compressed.second == edhttp::compressor::NO_COMPRESSION)
732 : // {
733 : // // no compression possible
734 : // //
735 : // CATCH_REQUIRE(compressed.first == buffer);
736 : // }
737 : // else
738 : // {
739 : // edhttp::compressor * c(edhttp::get_compressor(compressed.second));
740 : // CATCH_REQUIRE(c != nullptr);
741 : // CATCH_REQUIRE(c->get_name() == compressed.second);
742 : //SNAP_LOG_WARNING << "--- c = " << c->get_name() << SNAP_LOG_SEND;
743 : // if(compressed.second != "deflate")
744 : // {
745 : // CATCH_REQUIRE(c->compatible(compressed.first));
746 : // edhttp::result_t const decompressed(edhttp::decompress(compressed.first));
747 : // CATCH_REQUIRE(decompressed.second == compressed.second);
748 : // CATCH_REQUIRE(decompressed.first == buffer);
749 : // }
750 : // else
751 : // {
752 : // // deflate cannot be accessed from edhttp::decompress() because
753 : // // there is no magic and thus it cannot be a sure thing
754 : // //
755 : // edhttp::buffer_t const decompressed(c->decompress(compressed.first, buffer.size()));
756 : // CATCH_REQUIRE(decompressed == buffer);
757 : // }
758 : // }
759 : // }
760 : // }
761 : // CATCH_END_SECTION()
762 : //
763 : // CATCH_START_SECTION("compressor: compress() too small a buffer with any compressor")
764 : // {
765 : // for(int i(0); i < 10; ++i)
766 : // {
767 : // // generate a very small random buffer to compress so that the
768 : // // result is larger than the input and thus "fails" in a slightly
769 : // // different path
770 : // //
771 : // edhttp::buffer_t const buffer(SNAP_CATCH2_NAMESPACE::random_buffer(1, 5));
772 : // edhttp::result_t const compressed(edhttp::compress({}, buffer, rand() % 96 + 5, true));
773 : // CATCH_REQUIRE(compressed.second == edhttp::compressor::NO_COMPRESSION);
774 : // CATCH_REQUIRE(compressed.first == buffer);
775 : // }
776 : // }
777 : // CATCH_END_SECTION()
778 2 : }
779 :
780 :
781 8 : CATCH_TEST_CASE("archiver_error", "[archiver][error]")
782 : {
783 8 : CATCH_START_SECTION("archiver_error: tar filename missing")
784 : {
785 1 : edhttp::archiver * tar(edhttp::get_archiver("tar"));
786 1 : CATCH_REQUIRE(tar != nullptr);
787 1 : CATCH_REQUIRE(strcmp(tar->get_name(), "tar") == 0);
788 :
789 1 : edhttp::buffer_t file_data(SNAP_CATCH2_NAMESPACE::random_buffer(1, 1024));
790 :
791 1 : edhttp::archiver_file file;
792 1 : file.set_type(edhttp::file_type_t::FILE_TYPE_REGULAR);
793 1 : file.set_data(file_data);
794 : // file.set_filename(...); -- this parameter is mandatory
795 1 : file.set_user("edhttp", 1000);
796 1 : file.set_group("www-data", 128);
797 1 : file.set_mode(0644);
798 1 : file.set_mtime(snapdev::now());
799 :
800 1 : edhttp::archiver_archive archive;
801 :
802 1 : CATCH_REQUIRE_THROWS_MATCHES(
803 : tar->append_file(archive, file)
804 : , edhttp::missing_name
805 : , Catch::Matchers::ExceptionMessage(
806 : "edhttp_exception: a filename is required for an archive file."));
807 1 : }
808 8 : CATCH_END_SECTION()
809 :
810 8 : CATCH_START_SECTION("archiver_error: tar filename too long")
811 : {
812 1 : edhttp::archiver * tar(edhttp::get_archiver("tar"));
813 1 : CATCH_REQUIRE(tar != nullptr);
814 1 : CATCH_REQUIRE(strcmp(tar->get_name(), "tar") == 0);
815 :
816 : // generate a random filename (string)
817 : //
818 1 : std::string filename(SNAP_CATCH2_NAMESPACE::random_string(
819 : 101
820 : , 1024
821 1 : , SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_ASCII));
822 3 : filename = snapdev::string_replace_many(
823 : filename
824 1 : , {{"/", "-"}});
825 :
826 1 : edhttp::buffer_t file_data(SNAP_CATCH2_NAMESPACE::random_buffer(1, 1024));
827 :
828 1 : edhttp::archiver_file file;
829 1 : file.set_type(edhttp::file_type_t::FILE_TYPE_REGULAR);
830 1 : file.set_data(file_data);
831 1 : file.set_filename(filename);
832 1 : file.set_user("edhttp", 1000);
833 1 : file.set_group("www-data", 128);
834 1 : file.set_mode(0644);
835 1 : file.set_mtime(snapdev::now());
836 :
837 1 : edhttp::archiver_archive archive;
838 :
839 1 : CATCH_REQUIRE_THROWS_MATCHES(
840 : tar->append_file(archive, file)
841 : , edhttp::name_too_large
842 : , Catch::Matchers::ExceptionMessage(
843 : "edhttp_exception: this file cannot be added to a tar archive at this point (filename too long)."));
844 1 : }
845 8 : CATCH_END_SECTION()
846 :
847 8 : CATCH_START_SECTION("archiver_error: tar prefix too long")
848 : {
849 1 : edhttp::archiver * tar(edhttp::get_archiver("tar"));
850 1 : CATCH_REQUIRE(tar != nullptr);
851 1 : CATCH_REQUIRE(strcmp(tar->get_name(), "tar") == 0);
852 :
853 : // generate a random filename of the perfect size +1
854 : //
855 1 : std::string filename(SNAP_CATCH2_NAMESPACE::random_string(
856 : 100 + 1 + 155 + 1
857 : , 100 + 1 + 155 + 1
858 1 : , SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_ASCII));
859 :
860 : // remove any slashes
861 : //
862 3 : filename = snapdev::string_replace_many(
863 : filename
864 1 : , {{"/", "-"}});
865 :
866 : // place the slash at the exact right position so the filename
867 : // is exactly 100 characters
868 : //
869 1 : filename[156] = '/';
870 :
871 1 : edhttp::buffer_t file_data(SNAP_CATCH2_NAMESPACE::random_buffer(1, 1024));
872 :
873 1 : edhttp::archiver_file file;
874 1 : file.set_type(edhttp::file_type_t::FILE_TYPE_REGULAR);
875 1 : file.set_data(file_data);
876 1 : file.set_filename(filename);
877 1 : file.set_user("edhttp", 1000);
878 1 : file.set_group("www-data", 128);
879 1 : file.set_mode(0644);
880 1 : file.set_mtime(snapdev::now());
881 :
882 1 : edhttp::archiver_archive archive;
883 :
884 1 : CATCH_REQUIRE_THROWS_MATCHES(
885 : tar->append_file(archive, file)
886 : , edhttp::name_too_large
887 : , Catch::Matchers::ExceptionMessage(
888 : "edhttp_exception: this prefix + file names cannot be added to a tar archive at this point (filename too long)."));
889 1 : }
890 8 : CATCH_END_SECTION()
891 :
892 8 : CATCH_START_SECTION("archiver_error: tar user name too long")
893 : {
894 1 : edhttp::archiver * tar(edhttp::get_archiver("tar"));
895 1 : CATCH_REQUIRE(tar != nullptr);
896 1 : CATCH_REQUIRE(strcmp(tar->get_name(), "tar") == 0);
897 :
898 : // generate a random user_name (string)
899 : //
900 1 : std::string const user_name(SNAP_CATCH2_NAMESPACE::random_string(
901 : 33
902 : , 100
903 1 : , SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_ASCII));
904 :
905 1 : edhttp::buffer_t file_data(SNAP_CATCH2_NAMESPACE::random_buffer(1, 1024));
906 :
907 1 : edhttp::archiver_file file;
908 1 : file.set_type(edhttp::file_type_t::FILE_TYPE_REGULAR);
909 1 : file.set_data(file_data);
910 1 : file.set_filename("long-user-name.pdf");
911 1 : file.set_user(user_name, 1000);
912 1 : file.set_group("www-data", 128);
913 1 : file.set_mode(0644);
914 1 : file.set_mtime(snapdev::now());
915 :
916 1 : edhttp::archiver_archive archive;
917 :
918 1 : CATCH_REQUIRE_THROWS_MATCHES(
919 : tar->append_file(archive, file)
920 : , edhttp::name_too_large
921 : , Catch::Matchers::ExceptionMessage(
922 : "edhttp_exception: this file cannot be added to a tar archive at this point (user name too long)."));
923 1 : }
924 8 : CATCH_END_SECTION()
925 :
926 8 : CATCH_START_SECTION("archiver_error: tar group name too long")
927 : {
928 1 : edhttp::archiver * tar(edhttp::get_archiver("tar"));
929 1 : CATCH_REQUIRE(tar != nullptr);
930 1 : CATCH_REQUIRE(strcmp(tar->get_name(), "tar") == 0);
931 :
932 : // generate a random group_name (string)
933 : //
934 1 : std::string const group_name(SNAP_CATCH2_NAMESPACE::random_string(
935 : 33
936 : , 100
937 1 : , SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_ASCII));
938 :
939 1 : edhttp::buffer_t file_data(SNAP_CATCH2_NAMESPACE::random_buffer(1, 1024));
940 :
941 1 : edhttp::archiver_file file;
942 1 : file.set_type(edhttp::file_type_t::FILE_TYPE_REGULAR);
943 1 : file.set_data(file_data);
944 1 : file.set_filename("long-user-name.pdf");
945 1 : file.set_user("edhttp", 1000);
946 1 : file.set_group(group_name, 128);
947 1 : file.set_mode(0644);
948 1 : file.set_mtime(snapdev::now());
949 :
950 1 : edhttp::archiver_archive archive;
951 :
952 1 : CATCH_REQUIRE_THROWS_MATCHES(
953 : tar->append_file(archive, file)
954 : , edhttp::name_too_large
955 : , Catch::Matchers::ExceptionMessage(
956 : "edhttp_exception: this file cannot be added to a tar archive at this point (group name too long)."));
957 1 : }
958 8 : CATCH_END_SECTION()
959 :
960 8 : CATCH_START_SECTION("archiver_error: tar file data missing")
961 : {
962 1 : edhttp::archiver * tar(edhttp::get_archiver("tar"));
963 1 : CATCH_REQUIRE(tar != nullptr);
964 1 : CATCH_REQUIRE(strcmp(tar->get_name(), "tar") == 0);
965 :
966 1 : edhttp::archiver_archive archive;
967 :
968 : // generate a random file
969 : //
970 1 : edhttp::buffer_t file_data(SNAP_CATCH2_NAMESPACE::random_buffer(1024, 1024));
971 :
972 1 : snapdev::timespec_ex const file_mtime(snapdev::now());
973 :
974 : {
975 1 : edhttp::archiver_file file;
976 1 : file.set_type(edhttp::file_type_t::FILE_TYPE_REGULAR);
977 1 : file.set_data(file_data);
978 1 : file.set_filename("document.pdf");
979 1 : file.set_user("edhttp", 1000);
980 1 : file.set_group("edhttp", 1001);
981 1 : file.set_mode(0664);
982 1 : file.set_mtime(file_mtime);
983 :
984 1 : tar->append_file(archive, file);
985 1 : }
986 :
987 : // re-reading this one works as expected at first
988 : {
989 1 : edhttp::archiver_file file;
990 1 : CATCH_REQUIRE(tar->next_file(archive, file));
991 :
992 1 : CATCH_REQUIRE(file.get_type() == edhttp::file_type_t::FILE_TYPE_REGULAR);
993 1 : CATCH_REQUIRE(file.get_data() == file_data);
994 1 : CATCH_REQUIRE(file.get_filename() == "document.pdf");
995 1 : CATCH_REQUIRE(file.get_user() == "edhttp");
996 1 : CATCH_REQUIRE(file.get_uid() == 1000);
997 1 : CATCH_REQUIRE(file.get_group() == "edhttp");
998 1 : CATCH_REQUIRE(file.get_gid() == 1001);
999 1 : CATCH_REQUIRE(file.get_mode() == 0664);
1000 1 : CATCH_REQUIRE(file.get_mtime().tv_sec == file_mtime.tv_sec);
1001 1 : CATCH_REQUIRE(file.get_mtime().tv_nsec == 0);
1002 1 : }
1003 :
1004 : // now try again, but first let's destroy half the file
1005 : {
1006 1 : tar->rewind(archive);
1007 :
1008 1 : archive.get().resize(1024); // block is 512, file was 1024, now block is 512 and file is 512
1009 :
1010 1 : edhttp::archiver_file file;
1011 1 : CATCH_REQUIRE_THROWS_MATCHES(
1012 : tar->next_file(archive, file)
1013 : , edhttp::out_of_range
1014 : , Catch::Matchers::ExceptionMessage(
1015 : "out_of_range: file data not available (archive too small)."));
1016 1 : }
1017 1 : }
1018 8 : CATCH_END_SECTION()
1019 :
1020 8 : CATCH_START_SECTION("archiver_error: tar file invalid checksum & file type")
1021 : {
1022 1 : char const unsupported_types[] =
1023 : {
1024 : //REGTYPE,
1025 : //AREGTYPE,
1026 : LNKTYPE,
1027 : SYMTYPE,
1028 : CHRTYPE,
1029 : BLKTYPE,
1030 : //DIRTYPE,
1031 : FIFOTYPE,
1032 : //CONTTYPE,
1033 : };
1034 :
1035 1 : edhttp::archiver * tar(edhttp::get_archiver("tar"));
1036 1 : CATCH_REQUIRE(tar != nullptr);
1037 1 : CATCH_REQUIRE(strcmp(tar->get_name(), "tar") == 0);
1038 :
1039 1 : edhttp::archiver_archive archive;
1040 :
1041 : // generate a random file
1042 : //
1043 1 : edhttp::buffer_t file_data(SNAP_CATCH2_NAMESPACE::random_buffer(1024, 1024));
1044 :
1045 1 : snapdev::timespec_ex const file_mtime(snapdev::now());
1046 :
1047 : {
1048 1 : edhttp::archiver_file file;
1049 1 : file.set_type(edhttp::file_type_t::FILE_TYPE_REGULAR);
1050 1 : file.set_data(file_data);
1051 1 : file.set_filename("document.pdf");
1052 1 : file.set_user("edhttp", 1000);
1053 1 : file.set_group("edhttp", 1001);
1054 1 : file.set_mode(0664);
1055 1 : file.set_mtime(file_mtime);
1056 :
1057 1 : tar->append_file(archive, file);
1058 1 : }
1059 :
1060 : // re-reading this one works as expected at first
1061 : {
1062 1 : edhttp::archiver_file file;
1063 1 : CATCH_REQUIRE(tar->next_file(archive, file));
1064 :
1065 1 : CATCH_REQUIRE(file.get_type() == edhttp::file_type_t::FILE_TYPE_REGULAR);
1066 1 : CATCH_REQUIRE(file.get_data() == file_data);
1067 1 : CATCH_REQUIRE(file.get_filename() == "document.pdf");
1068 1 : CATCH_REQUIRE(file.get_user() == "edhttp");
1069 1 : CATCH_REQUIRE(file.get_uid() == 1000);
1070 1 : CATCH_REQUIRE(file.get_group() == "edhttp");
1071 1 : CATCH_REQUIRE(file.get_gid() == 1001);
1072 1 : CATCH_REQUIRE(file.get_mode() == 0664);
1073 1 : CATCH_REQUIRE(file.get_mtime().tv_sec == file_mtime.tv_sec);
1074 1 : CATCH_REQUIRE(file.get_mtime().tv_nsec == 0);
1075 1 : }
1076 :
1077 : // now try again, but first let's destroy half the file
1078 6 : for(auto const c : unsupported_types)
1079 : {
1080 5 : tar->rewind(archive);
1081 :
1082 5 : archive.get()[156] = c;
1083 :
1084 5 : std::uint32_t const new_checksum(check_sum(archive.get().data()));
1085 :
1086 5 : char buf[8];
1087 5 : memcpy(buf, reinterpret_cast<char *>(archive.get().data()) + 148, 8);
1088 5 : long const file_checksum(strtol(buf, nullptr, 8));
1089 :
1090 : // try "too soon" and the checksum breaks
1091 : //
1092 5 : edhttp::archiver_file file;
1093 5 : CATCH_REQUIRE_THROWS_MATCHES(
1094 : tar->next_file(archive, file)
1095 : , edhttp::invalid_checksum
1096 : , Catch::Matchers::ExceptionMessage(
1097 : "edhttp_exception: ustar checksum code ("
1098 : + std::to_string(new_checksum)
1099 : + ") does not match what was expected ("
1100 : + std::to_string(file_checksum)
1101 : + ")."));
1102 :
1103 : // fix the checksum
1104 : //
1105 5 : std::stringstream ss;
1106 5 : ss << std::oct
1107 : << std::setw(6)
1108 5 : << std::setfill('0')
1109 5 : << new_checksum;
1110 5 : strncpy(reinterpret_cast<char *>(archive.get().data()) + 148, ss.str().c_str(), 6);
1111 :
1112 : // next attempt bypasses the checksum check, but now fails on
1113 : // the file type which we do not support
1114 : //
1115 5 : CATCH_REQUIRE_THROWS_MATCHES(
1116 : tar->next_file(archive, file)
1117 : , edhttp::incompatible
1118 : , Catch::Matchers::ExceptionMessage(
1119 : "edhttp_exception: file type in tarball not supported (we accept regular and directory files only)."));
1120 5 : }
1121 1 : }
1122 8 : CATCH_END_SECTION()
1123 :
1124 8 : CATCH_START_SECTION("archiver_error: tar file invalid checksum & file type")
1125 : {
1126 1 : edhttp::archiver * tar(edhttp::get_archiver("tar"));
1127 1 : CATCH_REQUIRE(tar != nullptr);
1128 1 : CATCH_REQUIRE(strcmp(tar->get_name(), "tar") == 0);
1129 :
1130 1 : edhttp::archiver_archive archive;
1131 :
1132 : // generate a random header block
1133 : //
1134 1 : edhttp::buffer_t header;
1135 : for(;;)
1136 : {
1137 1 : header = SNAP_CATCH2_NAMESPACE::random_buffer(512, 512);
1138 1 : if(header[257] != 'u' || header[258] != 's' || header[259] != 't' || header[260] != 'a'
1139 1 : || header[261] != 'r' || (header[262] != ' ' && header[262] != '\0'))
1140 : {
1141 1 : break;
1142 : }
1143 : // try again in the extremely remote chance the randomizer
1144 : // generated the "ustar( |\0)" file magic
1145 : }
1146 :
1147 1 : archive.set(header);
1148 1 : CATCH_REQUIRE(const_cast<edhttp::archiver_archive const &>(archive).get() == header);
1149 :
1150 1 : edhttp::archiver_file file;
1151 1 : CATCH_REQUIRE_THROWS_MATCHES(
1152 : tar->next_file(archive, file)
1153 : , edhttp::incompatible
1154 : , Catch::Matchers::ExceptionMessage(
1155 : "edhttp_exception: ustar magic code missing at position 0."));
1156 1 : }
1157 8 : CATCH_END_SECTION()
1158 8 : }
1159 :
1160 :
1161 : // vim: ts=4 sw=4 et
|