zip_parser.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /**
  2. * ZuluSCSI™ - Copyright (c) 2024-2025 Rabbit Hole Computing™
  3. *
  4. * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  5. *
  6. * https://www.gnu.org/licenses/gpl-3.0.html
  7. * ----
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version. 
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16. * GNU General Public License for more details. 
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  20. **/
  21. #include "zip_parser.h"
  22. #include <ctype.h>
  23. #define ZIP_PARSER_METHOD_DEFLATE_BYTE 0x08
  24. #define ZIP_PARSER_METHOD_UNCOMPRESSED_BYTE 0x00
  25. namespace zipparser
  26. {
  27. Parser::Parser()
  28. {
  29. filename_len = 0;
  30. Reset();
  31. }
  32. Parser::Parser(char const *filename, const size_t length, const size_t target_total_length)
  33. {
  34. Reset();
  35. SetMatchingFilename(filename, length, target_total_length);
  36. }
  37. void Parser::Reset()
  38. {
  39. target = parsing_target::signature;
  40. position = 0;
  41. filename_match = false;
  42. crc = 0;
  43. }
  44. void Parser::SetMatchingFilename(char const *filename, const size_t length, const size_t target_total_length)
  45. {
  46. if (filename[0] == '\0')
  47. filename_len = 0;
  48. else
  49. {
  50. this->filename = filename;
  51. filename_len = length;
  52. target_zip_filename_len = target_total_length;
  53. }
  54. }
  55. int32_t Parser::Parse(uint8_t const *buf, const size_t size)
  56. {
  57. if (filename_len == 0)
  58. return PARSE_ERROR;
  59. static bool matching = true;
  60. static bool central_dir = false;
  61. static bool local_file_header = false;
  62. for (size_t idx = 0; idx < size; idx++)
  63. {
  64. switch (target)
  65. {
  66. case parsing_target::signature:
  67. if (++position == 1 && buf[idx] == 'P')
  68. break;
  69. if (position == 2 && buf[idx] == 'K')
  70. {
  71. local_file_header = false;
  72. central_dir = false;
  73. break;
  74. }
  75. if (position == 3 && buf[idx] == 0x03)
  76. {
  77. local_file_header = true;
  78. break;
  79. }
  80. if (position == 3 && buf[idx] == 0x01)
  81. {
  82. central_dir = true;
  83. break;
  84. }
  85. if (central_dir && position == 4 && buf[idx] == 0x2)
  86. {
  87. return PARSE_CENTRAL_DIR;
  88. }
  89. if (local_file_header && position == 4 && buf[idx] == 0x04)
  90. {
  91. position = 0;
  92. target = parsing_target::version;
  93. break;
  94. }
  95. return PARSE_ERROR;
  96. break;
  97. case parsing_target::version:
  98. if (++position == 2)
  99. {
  100. position = 0;
  101. target = parsing_target::flag;
  102. }
  103. break;
  104. case parsing_target::flag:
  105. if (++position == 2)
  106. {
  107. position = 0;
  108. target = parsing_target::method;
  109. }
  110. break;
  111. case parsing_target::method:
  112. if (++position == 1)
  113. {
  114. // Currently only uncompresseed files in the zip package are supported
  115. if (!buf[idx] == ZIP_PARSER_METHOD_UNCOMPRESSED_BYTE)
  116. {
  117. return PARSE_UNSUPPORTED_COMPRESSION;
  118. }
  119. }
  120. if (position == 2)
  121. {
  122. if (buf[idx] == 0)
  123. {
  124. position = 0;
  125. target = parsing_target::modify_time;
  126. }
  127. else
  128. return PARSE_UNSUPPORTED_COMPRESSION;
  129. }
  130. break;
  131. case parsing_target::modify_time:
  132. if (++position == 2)
  133. {
  134. position = 0;
  135. target = parsing_target::modify_date;
  136. }
  137. break;
  138. case parsing_target::modify_date:
  139. if (++position == 2)
  140. {
  141. position = 0;
  142. target = parsing_target::crc;
  143. crc = 0;
  144. }
  145. break;
  146. case parsing_target::crc:
  147. crc |= buf[idx] << (8 * position++);
  148. if (position == 4)
  149. {
  150. target = parsing_target::size_compressed;
  151. compressed_data_size = 0;
  152. position = 0;
  153. }
  154. break;
  155. case parsing_target::size_compressed:
  156. compressed_data_size |= buf[idx] << (8 * position++);
  157. if (position == 4)
  158. {
  159. target = parsing_target::size_uncompressed;
  160. uncompressed_data_size = 0;
  161. position = 0;
  162. }
  163. break;
  164. case parsing_target::size_uncompressed:
  165. uncompressed_data_size |= buf[idx] << (8 * position++);
  166. if (position == 4)
  167. {
  168. target = parsing_target::filename_len;
  169. current_zip_filename_len = 0;
  170. position = 0;
  171. }
  172. break;
  173. case parsing_target::filename_len:
  174. current_zip_filename_len |= buf[idx] << (8 * position++);
  175. if (position == 2)
  176. {
  177. target = parsing_target::extra_field_len;
  178. extra_field_len = 0;
  179. position = 0;
  180. }
  181. break;
  182. case parsing_target::extra_field_len:
  183. extra_field_len |= buf[idx] << (8 * position++);
  184. if (position == 2)
  185. {
  186. target = parsing_target::filename;
  187. position = 0;
  188. filename_match = false;
  189. matching = true;
  190. }
  191. break;
  192. case parsing_target::filename:
  193. if (position <= current_zip_filename_len - 1)
  194. {
  195. // make sure zipped filename is the correct length
  196. if (current_zip_filename_len != target_zip_filename_len)
  197. matching = false;
  198. if (matching && position < filename_len && tolower(filename[position]) != tolower(buf[idx]))
  199. matching = false;
  200. if (position == filename_len - 1 && matching)
  201. filename_match = true;
  202. if (position == current_zip_filename_len -1)
  203. {
  204. target = parsing_target::extra_field;
  205. position = 0;
  206. if (extra_field_len == 0)
  207. {
  208. target = parsing_target::end;
  209. return idx + 1;
  210. }
  211. }
  212. else
  213. position++;
  214. }
  215. break;
  216. case parsing_target::extra_field:
  217. // extra_field_len should be at least 1 by this time
  218. if (++position == extra_field_len)
  219. {
  220. target = parsing_target::end;
  221. return idx + 1;
  222. }
  223. break;
  224. case parsing_target::end:
  225. return 0;
  226. break;
  227. }
  228. }
  229. return size;
  230. }
  231. bool Parser::FoundMatch()
  232. {
  233. return filename_match;
  234. }
  235. }