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