| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- /**
- * ZuluSCSI™ - Copyright (c) 2024-2025 Rabbit Hole Computing™
- *
- * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version.
- *
- * https://www.gnu.org/licenses/gpl-3.0.html
- * ----
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- **/
- #include "zip_parser.h"
- #include <ctype.h>
- #define ZIP_PARSER_METHOD_DEFLATE_BYTE 0x08
- #define ZIP_PARSER_METHOD_UNCOMPRESSED_BYTE 0x00
- namespace zipparser
- {
- Parser::Parser()
- {
- filename_len = 0;
- Reset();
- }
- Parser::Parser(char const *filename, const size_t length, const size_t target_total_length)
- {
- Reset();
- SetMatchingFilename(filename, length, target_total_length);
- }
- void Parser::Reset()
- {
- target = parsing_target::signature;
- position = 0;
- filename_match = false;
- crc = 0;
- }
- void Parser::SetMatchingFilename(char const *filename, const size_t length, const size_t target_total_length)
- {
- if (filename[0] == '\0')
- filename_len = 0;
- else
- {
- this->filename = filename;
- filename_len = length;
- target_zip_filename_len = target_total_length;
- }
- }
- int32_t Parser::Parse(uint8_t const *buf, const size_t size)
- {
- if (filename_len == 0)
- return PARSE_ERROR;
- static bool matching = true;
- static bool central_dir = false;
- static bool local_file_header = false;
- for (size_t idx = 0; idx < size; idx++)
- {
- switch (target)
- {
- case parsing_target::signature:
- if (++position == 1 && buf[idx] == 'P')
- break;
- if (position == 2 && buf[idx] == 'K')
- {
- local_file_header = false;
- central_dir = false;
- break;
- }
- if (position == 3 && buf[idx] == 0x03)
- {
- local_file_header = true;
- break;
- }
- if (position == 3 && buf[idx] == 0x01)
- {
- central_dir = true;
- break;
- }
- if (central_dir && position == 4 && buf[idx] == 0x2)
- {
- return PARSE_CENTRAL_DIR;
- }
- if (local_file_header && position == 4 && buf[idx] == 0x04)
- {
- position = 0;
- target = parsing_target::version;
- break;
- }
- return PARSE_ERROR;
- break;
- case parsing_target::version:
- if (++position == 2)
- {
- position = 0;
- target = parsing_target::flag;
- }
- break;
- case parsing_target::flag:
- if (++position == 2)
- {
- position = 0;
- target = parsing_target::method;
- }
- break;
- case parsing_target::method:
- if (++position == 1)
- {
- // Currently only uncompresseed files in the zip package are supported
- if (!buf[idx] == ZIP_PARSER_METHOD_UNCOMPRESSED_BYTE)
- {
- return PARSE_UNSUPPORTED_COMPRESSION;
- }
- }
- if (position == 2)
- {
- if (buf[idx] == 0)
- {
- position = 0;
- target = parsing_target::modify_time;
- }
- else
- return PARSE_UNSUPPORTED_COMPRESSION;
- }
- break;
- case parsing_target::modify_time:
- if (++position == 2)
- {
- position = 0;
- target = parsing_target::modify_date;
- }
- break;
- case parsing_target::modify_date:
- if (++position == 2)
- {
- position = 0;
- target = parsing_target::crc;
- crc = 0;
- }
- break;
- case parsing_target::crc:
- crc |= buf[idx] << (8 * position++);
- if (position == 4)
- {
- target = parsing_target::size_compressed;
- compressed_data_size = 0;
- position = 0;
- }
- break;
- case parsing_target::size_compressed:
- compressed_data_size |= buf[idx] << (8 * position++);
- if (position == 4)
- {
- target = parsing_target::size_uncompressed;
- uncompressed_data_size = 0;
- position = 0;
- }
- break;
- case parsing_target::size_uncompressed:
- uncompressed_data_size |= buf[idx] << (8 * position++);
- if (position == 4)
- {
- target = parsing_target::filename_len;
- current_zip_filename_len = 0;
- position = 0;
- }
- break;
- case parsing_target::filename_len:
- current_zip_filename_len |= buf[idx] << (8 * position++);
- if (position == 2)
- {
- target = parsing_target::extra_field_len;
- extra_field_len = 0;
- position = 0;
- }
- break;
- case parsing_target::extra_field_len:
- extra_field_len |= buf[idx] << (8 * position++);
- if (position == 2)
- {
- target = parsing_target::filename;
- position = 0;
- filename_match = false;
- matching = true;
- }
- break;
- case parsing_target::filename:
- if (position <= current_zip_filename_len - 1)
- {
- // make sure zipped filename is the correct length
- if (current_zip_filename_len != target_zip_filename_len)
- matching = false;
- if (matching && position < filename_len && tolower(filename[position]) != tolower(buf[idx]))
- matching = false;
- if (position == filename_len - 1 && matching)
- filename_match = true;
- if (position == current_zip_filename_len -1)
- {
- target = parsing_target::extra_field;
- position = 0;
- if (extra_field_len == 0)
- {
- target = parsing_target::end;
- return idx + 1;
- }
- }
- else
- position++;
- }
- break;
- case parsing_target::extra_field:
- // extra_field_len should be at least 1 by this time
- if (++position == extra_field_len)
- {
- target = parsing_target::end;
- return idx + 1;
- }
- break;
- case parsing_target::end:
- return 0;
- break;
- }
- }
- return size;
- }
- bool Parser::FoundMatch()
- {
- return filename_match;
- }
- }
|