1-- Legal licensing note: 2 3-- Copyright (c) 2000 .. 2018 Gautier de Montmollin 4-- SWITZERLAND 5 6-- Permission is hereby granted, free of charge, to any person obtaining a copy 7-- of this software and associated documentation files (the "Software"), to deal 8-- in the Software without restriction, including without limitation the rights 9-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10-- copies of the Software, and to permit persons to whom the Software is 11-- furnished to do so, subject to the following conditions: 12 13-- The above copyright notice and this permission notice shall be included in 14-- all copies or substantial portions of the Software. 15 16-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22-- THE SOFTWARE. 23 24-- NB: this is the MIT License, as found on the site 25-- http://www.opensource.org/licenses/mit-license.php 26 27package body Zip.Headers is 28 29 ----------------------------------------------------------- 30 -- Byte array <-> various integers, with Intel endianess -- 31 ----------------------------------------------------------- 32 33 -- Get numbers with correct trucmuche endian, to ensure 34 -- correct header loading on some non-Intel machines 35 36 generic 37 type Number is mod <>; -- range <> in Ada83 version (fake Interfaces) 38 function Intel_x86_number( b: Byte_Buffer ) return Number; 39 40 function Intel_x86_number( b: Byte_Buffer ) return Number is 41 n: Number:= 0; 42 begin 43 for i in reverse b'Range loop 44 n:= n * 256 + Number(b(i)); 45 end loop; 46 return n; 47 end Intel_x86_number; 48 49 function Intel_nb is new Intel_x86_number( Unsigned_16 ); 50 function Intel_nb is new Intel_x86_number( Unsigned_32 ); 51 52 -- Put numbers with correct endianess as bytes 53 54 generic 55 type Number is mod <>; -- range <> in Ada83 version (fake Interfaces) 56 size: Positive; 57 function Intel_x86_buffer( n: Number ) return Byte_Buffer; 58 59 function Intel_x86_buffer( n: Number ) return Byte_Buffer is 60 b: Byte_Buffer(1..size); 61 m: Number:= n; 62 begin 63 for i in b'Range loop 64 b(i):= Unsigned_8(m and 255); 65 m:= m / 256; 66 end loop; 67 return b; 68 end Intel_x86_buffer; 69 70 function Intel_bf is new Intel_x86_buffer( Unsigned_16, 2 ); 71 function Intel_bf is new Intel_x86_buffer( Unsigned_32, 4 ); 72 73 ------------------- 74 -- PK signatures -- 75 ------------------- 76 77 function PK_signature( buf: Byte_Buffer; code: Unsigned_8 ) return Boolean is 78 begin 79 return buf(buf'First .. buf'First+3) = (16#50#, 16#4B#, code, code+1); 80 -- PK12, PK34, ... 81 end PK_signature; 82 83 procedure PK_signature( buf: in out Byte_Buffer; code: Unsigned_8 ) is 84 begin 85 buf(1..4) := (16#50#, 16#4B#, code, code+1); -- PK12, PK34, ... 86 end PK_signature; 87 88 ------------------------------------------------------- 89 -- PKZIP file header, as in central directory - PK12 -- 90 ------------------------------------------------------- 91 procedure Read_and_check( 92 stream : in out Root_Zipstream_Type'Class; 93 header : out Central_File_Header 94 ) 95 is 96 chb: Byte_Buffer( 1..46 ); 97 begin 98 BlockRead(stream, chb); 99 100 if not PK_signature(chb, 1) then 101 raise bad_central_header; 102 end if; 103 104 header.made_by_version:= Intel_nb( chb( 5.. 6) ); 105 header.short_info.needed_extract_version:= Intel_nb( chb( 7.. 8) ); 106 header.short_info.bit_flag:= Intel_nb( chb( 9..10) ); 107 header.short_info.zip_type:= Intel_nb( chb(11..12) ); 108 header.short_info.file_timedate:= 109 Zip_Streams.Calendar.Convert(Unsigned_32'(Intel_nb( chb(13..16) ))); 110 header.short_info.dd.crc_32:= Intel_nb( chb(17..20) ); 111 header.short_info.dd.compressed_size:= Intel_nb( chb(21..24) ); 112 header.short_info.dd.uncompressed_size:= Intel_nb( chb(25..28) ); 113 header.short_info.filename_length:= Intel_nb( chb(29..30) ); 114 header.short_info.extra_field_length:= Intel_nb( chb(31..32) ); 115 header.comment_length:= Intel_nb( chb(33..34) ); 116 header.disk_number_start:= Intel_nb( chb(35..36) ); 117 header.internal_attributes:= Intel_nb( chb(37..38) ); 118 header.external_attributes:= Intel_nb( chb(39..42) ); 119 header.local_header_offset:= Intel_nb( chb(43..46) ); 120 121 end Read_and_check; 122 123 procedure Write( 124 stream : in out Root_Zipstream_Type'Class; 125 header : in Central_File_Header 126 ) 127 is 128 chb: Byte_Buffer( 1..46 ); 129 begin 130 PK_signature(chb, 1); 131 132 chb( 5.. 6):= Intel_bf( header.made_by_version ); 133 chb( 7.. 8):= Intel_bf( header.short_info.needed_extract_version ); 134 chb( 9..10):= Intel_bf( header.short_info.bit_flag ); 135 chb(11..12):= Intel_bf( header.short_info.zip_type ); 136 chb(13..16):= Intel_bf( Zip_Streams.Calendar.Convert( 137 header.short_info.file_timedate) 138 ); 139 chb(17..20):= Intel_bf( header.short_info.dd.crc_32 ); 140 chb(21..24):= Intel_bf( header.short_info.dd.compressed_size ); 141 chb(25..28):= Intel_bf( header.short_info.dd.uncompressed_size ); 142 chb(29..30):= Intel_bf( header.short_info.filename_length ); 143 chb(31..32):= Intel_bf( header.short_info.extra_field_length ); 144 chb(33..34):= Intel_bf( header.comment_length ); 145 chb(35..36):= Intel_bf( header.disk_number_start ); 146 chb(37..38):= Intel_bf( header.internal_attributes ); 147 chb(39..42):= Intel_bf( header.external_attributes ); 148 chb(43..46):= Intel_bf( header.local_header_offset ); 149 150 BlockWrite(stream, chb); 151 end Write; 152 153 ----------------------------------------------------------------------- 154 -- PKZIP local file header, in front of every file in archive - PK34 -- 155 ----------------------------------------------------------------------- 156 procedure Read_and_check( 157 stream : in out Root_Zipstream_Type'Class; 158 header : out Local_File_Header 159 ) 160 is 161 lhb: Byte_Buffer( 1..30 ); 162 begin 163 BlockRead(stream, lhb); 164 165 if not PK_signature(lhb, 3) then 166 raise bad_local_header; 167 end if; 168 169 header.needed_extract_version:= Intel_nb( lhb( 5.. 6) ); 170 header.bit_flag:= Intel_nb( lhb( 7.. 8) ); 171 header.zip_type:= Intel_nb( lhb( 9..10) ); 172 header.file_timedate:= Zip_Streams.Calendar.Convert(Unsigned_32'( 173 Intel_nb( lhb(11..14) ) 174 )); 175 header.dd.crc_32:= Intel_nb( lhb(15..18) ); 176 header.dd.compressed_size:= Intel_nb( lhb(19..22) ); 177 header.dd.uncompressed_size:= Intel_nb( lhb(23..26) ); 178 header.filename_length:= Intel_nb( lhb(27..28) ); 179 header.extra_field_length:= Intel_nb( lhb(29..30) ); 180 181 end Read_and_check; 182 183 procedure Write( 184 stream : in out Root_Zipstream_Type'Class; 185 header : in Local_File_Header 186 ) 187 is 188 lhb: Byte_Buffer( 1..30 ); 189 begin 190 PK_signature(lhb, 3); 191 192 lhb( 5.. 6):= Intel_bf( header.needed_extract_version ); 193 lhb( 7.. 8):= Intel_bf( header.bit_flag ); 194 lhb( 9..10):= Intel_bf( header.zip_type ); 195 lhb(11..14):= Intel_bf( Zip_Streams.Calendar.Convert(header.file_timedate) ); 196 lhb(15..18):= Intel_bf( header.dd.crc_32 ); 197 lhb(19..22):= Intel_bf( header.dd.compressed_size ); 198 lhb(23..26):= Intel_bf( header.dd.uncompressed_size ); 199 lhb(27..28):= Intel_bf( header.filename_length ); 200 lhb(29..30):= Intel_bf( header.extra_field_length ); 201 202 BlockWrite(stream, lhb); 203 end Write; 204 205 ------------------------------------------- 206 -- PKZIP end-of-central-directory - PK56 -- 207 ------------------------------------------- 208 procedure Copy_and_check( 209 buffer : in Byte_Buffer; 210 the_end : out End_of_Central_Dir 211 ) 212 is 213 o: constant Integer:= buffer'First - 1; 214 begin 215 if not PK_signature(buffer, 5) then 216 raise bad_end; 217 end if; 218 219 the_end.disknum:= Intel_nb( buffer(o+ 5..o+ 6) ); 220 the_end.disknum_with_start:= Intel_nb( buffer(o+ 7..o+ 8) ); 221 the_end.disk_total_entries:= Intel_nb( buffer(o+ 9..o+10) ); 222 the_end.total_entries:= Intel_nb( buffer(o+11..o+12) ); 223 the_end.central_dir_size:= Intel_nb( buffer(o+13..o+16) ); 224 the_end.central_dir_offset:= Intel_nb( buffer(o+17..o+20) ); 225 the_end.main_comment_length:= Intel_nb( buffer(o+21..o+22) ); 226 227 end Copy_and_check; 228 229 procedure Read_and_check( 230 stream : in out Root_Zipstream_Type'Class; 231 the_end : out End_of_Central_Dir 232 ) 233 is 234 eb: Byte_Buffer( 1..22 ); 235 begin 236 BlockRead(stream, eb); 237 Copy_and_check(eb, the_end); 238 end Read_and_check; 239 240 procedure Load( 241 stream : in out Root_Zipstream_Type'Class; 242 the_end : out End_of_Central_Dir 243 ) 244 is 245 min_end_start: ZS_Index_Type; -- min_end_start >= 1 246 max_comment: constant:= 65_535; 247 -- In appnote.txt : 248 -- .ZIP file comment length 2 bytes 249 begin 250 if Size (stream) < 22 then 251 raise bad_end; 252 end if; 253 -- 20-Jun-2001: abandon search below min_end_start. 254 if Size (stream) <= max_comment then 255 min_end_start:= 1; 256 else 257 min_end_start:= Size(stream) - max_comment; 258 end if; 259 Set_Index(stream, min_end_start); 260 declare 261 -- We copy a large chunk of the zip stream's tail into a buffer. 262 large_buffer: Byte_Buffer(0 .. Natural(Size(stream) - min_end_start)); 263 ilb: Integer; 264 x : ZS_Size_Type; 265 begin 266 BlockRead(stream, large_buffer); 267 for i in reverse min_end_start .. Size(stream) - 21 loop 268 -- Yes, we must _search_ for the header... 269 -- because PKWARE put a variable-size comment _after_ it 8-( 270 ilb:= Integer(i - min_end_start); 271 if PK_signature(large_buffer(ilb .. ilb + 3), 5) then 272 Copy_and_check( large_buffer(ilb .. ilb + 21), the_end ); 273 -- At this point, the buffer was successfully read, the_end is 274 -- is set with its standard contents. 275 -- 276 -- This is the *real* position of the end-of-central-directory block, to begin with: 277 x:= i; 278 -- We subtract the *theoretical* (stored) position of the end-of-central-directory. 279 -- The theoretical position is equal to central_dir_offset + central_dir_size. 280 -- The theoretical position should be smaller or equal than the real position - 281 -- unless the archive is corrupted. 282 -- We do it step by step, because ZS_Size_Type was modular until rev. 644. 283 -- Now it's a signed 64 bits, but we don't want to change anything again... 284 -- 285 x := x - 1; -- i >= 1, so no dragons here. The "- 1" is for adapting from the 1-based Ada index. 286 exit when ZS_Size_Type(the_end.central_dir_offset) > x; -- fuzzy value, will trigger bad_end 287 x := x - ZS_Size_Type(the_end.central_dir_offset); 288 exit when ZS_Size_Type(the_end.central_dir_size) > x; -- fuzzy value, will trigger bad_end 289 x := x - ZS_Size_Type(the_end.central_dir_size); 290 -- Now, x is the difference : real - theoretical. 291 -- x > 0 if the archive was appended to another file (typically an executable 292 -- for self-extraction purposes). 293 -- x = 0 if this is a "pure" Zip archive. 294 the_end.offset_shifting:= x; 295 Set_Index(stream, i + 22); 296 return; -- the_end found and filled -> exit 297 end if; 298 end loop; 299 raise bad_end; -- Definitely no "end-of-central-directory" in this stream 300 end; 301 end Load; 302 303 procedure Write( 304 stream : in out Root_Zipstream_Type'Class; 305 the_end : in End_of_Central_Dir 306 ) 307 is 308 eb: Byte_Buffer( 1..22 ); 309 begin 310 PK_signature(eb, 5); 311 312 eb( 5.. 6):= Intel_bf( the_end.disknum ); 313 eb( 7.. 8):= Intel_bf( the_end.disknum_with_start ); 314 eb( 9..10):= Intel_bf( the_end.disk_total_entries ); 315 eb(11..12):= Intel_bf( the_end.total_entries ); 316 eb(13..16):= Intel_bf( the_end.central_dir_size ); 317 eb(17..20):= Intel_bf( the_end.central_dir_offset ); 318 eb(21..22):= Intel_bf( the_end.main_comment_length ); 319 320 BlockWrite(stream, eb); 321 end Write; 322 323 ------------------------------------------------------------------ 324 -- PKZIP data descriptor, after streamed compressed data - PK78 -- 325 ------------------------------------------------------------------ 326 procedure Copy_and_check( 327 buffer : in Byte_Buffer; 328 the_data_desc : out Data_descriptor 329 ) 330 is 331 begin 332 if not PK_signature(buffer, 7) then 333 raise bad_data_descriptor; 334 end if; 335 336 the_data_desc.crc_32:= Intel_nb( buffer(5..8) ); 337 the_data_desc.compressed_size:= Intel_nb( buffer(9..12) ); 338 the_data_desc.uncompressed_size:= Intel_nb( buffer(13..16) ); 339 340 end Copy_and_check; 341 342 procedure Read_and_check( 343 stream : in out Root_Zipstream_Type'Class; 344 the_data_desc : out Data_descriptor 345 ) 346 is 347 ddb: Byte_Buffer( 1..16 ); 348 begin 349 BlockRead(stream, ddb); 350 Copy_and_check(ddb, the_data_desc); 351 end Read_and_check; 352 353 procedure Write( 354 stream : in out Root_Zipstream_Type'Class; 355 the_data_desc : in Data_descriptor 356 ) 357 is 358 ddb: Byte_Buffer( 1..16 ); 359 begin 360 PK_signature(ddb, 7); 361 362 ddb( 5.. 8):= Intel_bf( the_data_desc.crc_32 ); 363 ddb( 9..12):= Intel_bf( the_data_desc.compressed_size ); 364 ddb(13..16):= Intel_bf( the_data_desc.uncompressed_size ); 365 366 BlockWrite(stream, ddb); 367 end Write; 368 369end Zip.Headers; 370