class Ronin::Support::Archive::Zip::Reader

Handles reading zip archives.

@note

This provides a simple interface for reading zip archives using
the `unzip` command. If you need something more powerful, use the
[archive-zip] gem instead.

[archive-zip]: github.com/javanthropus/archive-zip

@api public

@since 1.0.0

Constants

METHODS

Translates the Method column in ‘unzip -v -l` output to Symbols.

Attributes

password[R]

The optional password to use when reading the zip archive.

@return [String, nil]

path[R]

The path to the zip archive that will be read.

@return [String]

Public Class Methods

new(path, password: nil) { |self| ... } click to toggle source

Initializes the zip archive reader.

@param [String] path

The path to the zip archive file.

@param [String, nil] password

Optional password to use when reading the zip archive.

@yield [zip]

If a block is given, it will be yielded the new zip reader.

@yieldparam [Reader] zip

The new zip reacher.
# File lib/ronin/support/archive/zip/reader.rb, line 71
def initialize(path, password: nil)
  @path     = File.expand_path(path)
  @password = password

  yield self if block_given?
end
open(path,**kwargs,&block) click to toggle source

Alias to {#initialize new}.

@param [String] path

The path to the zip archive file.

@param [Hash{Symbol => Object}] kwargs

Additional keyword arguments for {#initialize new}.

@option kwargs [String, nil] :password

Optional password to use when reading the zip archive.

@yield [zip]

If a block is given, it will be yielded the new zip reader.

@yieldparam [Reader] zip

The new zip reacher.

@see initialize

# File lib/ronin/support/archive/zip/reader.rb, line 98
def self.open(path,**kwargs,&block)
  new(path,**kwargs,&block)
end

Public Instance Methods

[](name) click to toggle source

Finds the entry with the given name.

@param [String] name

The file name to search for.

@return [Entry, nil]

The matching entry or `nil` if no entry could be found.
# File lib/ronin/support/archive/zip/reader.rb, line 157
def [](name)
  find { |entry| entry.name == name }
end
each() { |parse_entry_line(line)| ... } click to toggle source

Lists the contents of the zip archive.

@yield [entry]

If a block is given it will be passed each parsed entry in the
zip archive.

@yieldparam [Entry] entry

An entry in the zip archive.

@return [Enumerator, Statistics]

If no block is given an enumerator will be returned.
If a block was given, then a statistics object will be returned.

@note

This method actually executes the `unzip -v -l ZIP` command
and parses it's output.
# File lib/ronin/support/archive/zip/reader.rb, line 120
def each
  return enum_for(__method__) unless block_given?

  io = IO.popen(command_argv('-v','-l',@path))

  # skip lines until the "---------  ---------- -----   ----" line
  until io.eof?
    break if io.readline.start_with?('-')
  end

  until io.eof?
    line = io.readline(chomp: true)

    unless line.start_with?('-')
      yield parse_entry_line(line)
    else
      # reached the "---------                     -------" line
      break
    end
  end

  last_line = io.readline(chomp: true)
  return parse_statistics_line(last_line)
end
Also aliased as: list
list()
Alias for: each
read(name, length: nil) click to toggle source

Reads the contents of an entry from the zip archive.

@param [String] name

The name of the entry to read.

@param [Integer, nil] length

Optional number of bytes to read.

@return [String]

The read data.

@note

This method actually executes the `unzip -p ZIP FILE` command
and reads it's output.
# File lib/ronin/support/archive/zip/reader.rb, line 177
def read(name, length: nil)
  io = IO.popen(command_argv('-p', @path, name))

  if length then io.read(length)
  else           io.read
  end
end

Private Instance Methods

command_argv(*arguments) click to toggle source

Creates an ‘unzip` command with the additional arguments.

@param [Array<String>] arguments

Additional arguments for the `unzip` command.

@return [Array<String>]

The `unzip` command argv.
# File lib/ronin/support/archive/zip/reader.rb, line 196
def command_argv(*arguments)
  argv = ['unzip']

  if @password
    argv << '-P' << @password
  end

  argv.concat(arguments)
end
parse_entry_line(line) click to toggle source

Parses a entry line from the output of ‘unzip -v -l ZIP`.

@param [String] line

The line to parse.

@return [Entry]

The parsed entry.
# File lib/ronin/support/archive/zip/reader.rb, line 221
def parse_entry_line(line)
  length, method, size, compression, date, time, crc32, name =
    line.lstrip.split(/\s+/,8)

  time_fmt = case date
             when /\A\d{2}-\d{2}-\d{4}\z/ then "%m-%d-%Y %H:%M"
             when /\A\d{4}-\d{2}-\d{2}\z/ then "%Y-%m-%d %H:%M"
             else
               raise(NotImplementedError,"unrecognized date format: #{date.inspect}")
             end

  length      = length.to_i
  method      = METHODS.fetch(method)
  size        = size.to_i
  compression = compression.chomp('%').to_i
  time        = Time.strptime("#{date} #{time}",time_fmt)
  date        = time.to_date

  return Entry.new(self, length:      length,
                         method:      method,
                         size:        size,
                         compression: compression,
                         date:        date,
                         time:        time,
                         crc32:       crc32,
                         name:        name)
end
parse_statistics_line(line) click to toggle source

Parses the last line from the output of ‘unzip -v -l ZIP`.

@param [String] line

The line to parse.

@return [Statistics]

The parsed statistics.
# File lib/ronin/support/archive/zip/reader.rb, line 258
def parse_statistics_line(line)
  length, size, compression, files, _rest = line.lstrip.split(/\s+/,5)

  return Statistics.new(
    length:      length.to_i,
    size:        size.to_i,
    compression: compression.chomp('%').to_i,
    files:       files.to_i
  )
end