# -*- coding: binary -*-

module Rex
module Zip


#
# This structure holds the following data pertaining to a Zip entry's data.
#
# data crc
# compressed size
# uncompressed size
#
class CompInfo

  def initialize(crc, compsz, uncompsz)
    @crc, @compsz, @uncompsz = crc, compsz, uncompsz
  end

  def pack
    [ @crc, @compsz, @uncompsz ].pack('VVV')
  end

end


#
# This structure holds the following data pertaining to a Zip entry.
#
# general purpose bit flag
# compression method
# modification time
# modification date
#
class CompFlags

  attr_accessor :compmeth

  def initialize(gpbf, compmeth, timestamp)
    @gpbf = gpbf
    @compmeth = compmeth
    @mod_time = ((timestamp.hour << 11) | (timestamp.min << 5) | (timestamp.sec))
    @mod_date = (((timestamp.year - 1980) << 9) | (timestamp.mon << 5) | (timestamp.day))
  end

  def pack
    [ @gpbf, @compmeth, @mod_time, @mod_date ].pack('vvvv')
  end

end



#
# This structure is sometimes stored after the file data and used
# instead of the fields within the Local File Header.
#
class DataDesc

  SIGNATURE = 0x8074b50

  def initialize(compinfo)
    @compinfo = compinfo
  end

  def pack
    ret = [ SIGNATURE ].pack('V')
    ret << @compinfo.pack
    ret
  end

end


#
# This structure records the compression data and flags about
# a Zip entry to a file.
#
class LocalFileHdr

  SIGNATURE = 0x4034b50

  def initialize(entry)
    @entry = entry
  end

  def pack
    path = @entry.relative_path

    ret = [ SIGNATURE, ZIP_VERSION ].pack('Vv')
    ret << @entry.flags.pack
    ret << @entry.info.pack
    ret << [ path.length, @entry.xtra.length ].pack('vv')
    ret << path
    ret << @entry.xtra
    ret
  end

end


#
# This structure holds all of the information about a particular Zip Entry
# as it is contained within the central directory.
#
class CentralDir

  SIGNATURE = 0x2014b50

  def initialize(entry, offset)
    @entry = entry
    @disknum_start = 0
    @attr_int = 0
    @attr_ext = 0x20
    @hdr_offset = offset
  end

  def pack
    if @entry.central_dir_name.to_s.strip.empty?
      path = @entry.relative_path
    else
      path = @entry.central_dir_path
    end

    ret = [ SIGNATURE, ZIP_VERSION ].pack('Vv')
    ret << [ ZIP_VERSION ].pack('v')
    ret << @entry.flags.pack
    ret << @entry.info.pack
    arr = []
    arr << path.length
    arr << @entry.xtra.length
    arr << @entry.comment.length
    arr << @disknum_start
    arr << @attr_int
    arr << @entry.attrs
    arr << @hdr_offset
    ret << arr.pack('vvvvvVV')
    ret << path
    ret << @entry.xtra
    ret << @entry.comment
    # digital signature not supported
    ret
  end

end


#
# This structure is written after the per-entry central directory records to
# provide information about the archive as a whole.
#
class CentralDirEnd

  SIGNATURE = 0x6054b50

  def initialize(ncfd, cfdsz, offset, comment=nil)
    @disk_no = 0
    @disk_dir_start = 0
    @ncfd_this_disk = ncfd
    @ncfd_total = ncfd
    @cfd_size = cfdsz
    @start_offset = offset
    @comment = comment
    @comment ||= ''
  end


  def pack
    arr = []
    arr << SIGNATURE
    arr << @disk_no
    arr << @disk_dir_start
    arr << @ncfd_this_disk
    arr << @ncfd_total
    arr << @cfd_size
    arr << @start_offset
    arr << @comment.length
    (arr.pack('VvvvvVVv') + @comment)
  end

end

end
end
