Class: Omnizip::Formats::Msi::CabExtractor

Inherits:
Object
  • Object
show all
Includes:
Constants
Defined in:
lib/omnizip/formats/msi/cab_extractor.rb

Overview

MSI CAB Extractor

Handles extraction of files from embedded or external cabinet files. MSI packages can have cabinets embedded in the _Streams table or as separate .cab files alongside the MSI.

Constant Summary

Constants included from Constants

Omnizip::Formats::Msi::Constants::BINARY_TYPE, Omnizip::Formats::Msi::Constants::CATEGORY_BINARY, Omnizip::Formats::Msi::Constants::CATEGORY_CABINET, Omnizip::Formats::Msi::Constants::CATEGORY_CONDITION, Omnizip::Formats::Msi::Constants::CATEGORY_DEFAULT, Omnizip::Formats::Msi::Constants::CATEGORY_DOUBLE, Omnizip::Formats::Msi::Constants::CATEGORY_FILENAME, Omnizip::Formats::Msi::Constants::CATEGORY_GUID, Omnizip::Formats::Msi::Constants::CATEGORY_IDENTIFIER, Omnizip::Formats::Msi::Constants::CATEGORY_LANGUAGE, Omnizip::Formats::Msi::Constants::CATEGORY_LOWERCASE, Omnizip::Formats::Msi::Constants::CATEGORY_PATH, Omnizip::Formats::Msi::Constants::CATEGORY_SHORTCUT, Omnizip::Formats::Msi::Constants::CATEGORY_TEMPLATE, Omnizip::Formats::Msi::Constants::CATEGORY_TEXT, Omnizip::Formats::Msi::Constants::CATEGORY_UPPERCASE, Omnizip::Formats::Msi::Constants::CATEGORY_VERSION, Omnizip::Formats::Msi::Constants::COLUMNS_STREAM, Omnizip::Formats::Msi::Constants::COMPONENT_TABLE, Omnizip::Formats::Msi::Constants::COMP_ATTR_64BIT, Omnizip::Formats::Msi::Constants::COMP_ATTR_DISABLE_REGISTRY, Omnizip::Formats::Msi::Constants::COMP_ATTR_LOCAL_ONLY, Omnizip::Formats::Msi::Constants::COMP_ATTR_NEVER_OVERWRITE, Omnizip::Formats::Msi::Constants::COMP_ATTR_ODBC, Omnizip::Formats::Msi::Constants::COMP_ATTR_OPTIONAL, Omnizip::Formats::Msi::Constants::COMP_ATTR_PERMANENT, Omnizip::Formats::Msi::Constants::COMP_ATTR_REGISTRY_KEY_PATH, Omnizip::Formats::Msi::Constants::COMP_ATTR_SHARED, Omnizip::Formats::Msi::Constants::COMP_ATTR_SHARED_DLL, Omnizip::Formats::Msi::Constants::COMP_ATTR_SOURCE_ONLY, Omnizip::Formats::Msi::Constants::COMP_ATTR_TRANSITIVE, Omnizip::Formats::Msi::Constants::COMP_ATTR_UNINSTALL_ON_SUPERSEDE, Omnizip::Formats::Msi::Constants::DIRECTORY_TABLE, Omnizip::Formats::Msi::Constants::EMBEDDED_CAB_PREFIX, Omnizip::Formats::Msi::Constants::FILE_ATTR_CHECKSUM, Omnizip::Formats::Msi::Constants::FILE_ATTR_COMPRESSED, Omnizip::Formats::Msi::Constants::FILE_ATTR_HIDDEN, Omnizip::Formats::Msi::Constants::FILE_ATTR_NONCOMPRESSED, Omnizip::Formats::Msi::Constants::FILE_ATTR_PATCHADDED, Omnizip::Formats::Msi::Constants::FILE_ATTR_READONLY, Omnizip::Formats::Msi::Constants::FILE_ATTR_SYSTEM, Omnizip::Formats::Msi::Constants::FILE_ATTR_VITAL, Omnizip::Formats::Msi::Constants::FILE_TABLE, Omnizip::Formats::Msi::Constants::INT16_TYPE, Omnizip::Formats::Msi::Constants::INT32_TYPE, Omnizip::Formats::Msi::Constants::MEDIA_TABLE, Omnizip::Formats::Msi::Constants::OBJECT_TYPE, Omnizip::Formats::Msi::Constants::PROGRAM_FILES, Omnizip::Formats::Msi::Constants::PROGRAM_FILES_X64, Omnizip::Formats::Msi::Constants::SOURCE_DIR, Omnizip::Formats::Msi::Constants::STREAMS_TABLE, Omnizip::Formats::Msi::Constants::STRING_DATA_STREAM, Omnizip::Formats::Msi::Constants::STRING_POOL_STREAM, Omnizip::Formats::Msi::Constants::STRING_TYPE, Omnizip::Formats::Msi::Constants::SYSTEM_FOLDER, Omnizip::Formats::Msi::Constants::SYSTEM_X64_FOLDER, Omnizip::Formats::Msi::Constants::TABLES_STREAM, Omnizip::Formats::Msi::Constants::TARGET_DIR, Omnizip::Formats::Msi::Constants::WINDOWS_FOLDER

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Constants

decode_stream_name

Constructor Details

#initialize(ole, media_table, msi_path, stream_reader) ⇒ CabExtractor

Initialize CAB extractor

Parameters:

  • ole (Ole::Storage)

    OLE storage object

  • media_table (Array<Hash>)

    Parsed Media table rows

  • msi_path (String)

    Path to MSI file

  • stream_reader (Proc)

    Method to read streams



35
36
37
38
39
40
# File 'lib/omnizip/formats/msi/cab_extractor.rb', line 35

def initialize(ole, media_table, msi_path, stream_reader)
  @ole = ole
  @media_table = media_table || []
  @msi_path = msi_path
  @read_stream = stream_reader
end

Instance Attribute Details

#media_tableArray<Hash> (readonly)

Returns Media table data.

Returns:

  • (Array<Hash>)

    Media table data



21
22
23
# File 'lib/omnizip/formats/msi/cab_extractor.rb', line 21

def media_table
  @media_table
end

#msi_pathString (readonly)

Returns Path to MSI file (for external CAB resolution).

Returns:

  • (String)

    Path to MSI file (for external CAB resolution)



24
25
26
# File 'lib/omnizip/formats/msi/cab_extractor.rb', line 24

def msi_path
  @msi_path
end

#oleOle::Storage (readonly)

Returns OLE storage.

Returns:



18
19
20
# File 'lib/omnizip/formats/msi/cab_extractor.rb', line 18

def ole
  @ole
end

#read_streamProc (readonly)

Returns Stream reader method.

Returns:

  • (Proc)

    Stream reader method



27
28
29
# File 'lib/omnizip/formats/msi/cab_extractor.rb', line 27

def read_stream
  @read_stream
end

Instance Method Details

#extract_cabinetsHash

Extract all cabinets to temporary files

Returns:

  • (Hash)

    Map of cabinet name => temp file path



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/omnizip/formats/msi/cab_extractor.rb', line 45

def extract_cabinets
  cabinets = {}

  @media_table.each do |media|
    cab_name = media["Cabinet"]
    next if cab_name.nil? || cab_name.empty?

    cab_data = get_cabinet_data(cab_name)
    next if cab_data.nil? || cab_data.empty?

    # Write to temp file
    temp_file = Tempfile.create(["msi_cab_", ".cab"])
    temp_file.binmode
    temp_file.write(cab_data)
    temp_file.flush
    temp_file.close

    cabinets[media["DiskId"]] = {
      path: temp_file.path,
      name: cab_name,
      last_sequence: media["LastSequence"],
      temp_file: temp_file,
    }
  end

  cabinets
end

#find_cabinet_for_sequence(sequence, cabinets) ⇒ Hash?

Find the cabinet containing a file by sequence number

Parameters:

  • sequence (Integer)

    File sequence number

  • cabinets (Hash)

    Cabinet info from extract_cabinets

Returns:

  • (Hash, nil)

    Cabinet info or nil



94
95
96
97
98
# File 'lib/omnizip/formats/msi/cab_extractor.rb', line 94

def find_cabinet_for_sequence(sequence, cabinets)
  # Sort by LastSequence and find the cabinet where sequence <= LastSequence
  sorted = cabinets.values.sort_by { |c| c[:last_sequence] }
  sorted.find { |c| sequence <= c[:last_sequence] }
end

#get_cabinet_data(cabinet_name) ⇒ String?

Get cabinet data for a media entry

Parameters:

  • cabinet_name (String)

    Cabinet name from Media table

Returns:

  • (String, nil)

    Cabinet binary data or nil



77
78
79
80
81
82
83
84
85
86
87
# File 'lib/omnizip/formats/msi/cab_extractor.rb', line 77

def get_cabinet_data(cabinet_name)
  return nil if cabinet_name.nil? || cabinet_name.empty?

  if cabinet_name.start_with?(EMBEDDED_CAB_PREFIX)
    # Embedded cabinet
    extract_embedded_cabinet(cabinet_name[1..])
  else
    # External cabinet file
    read_external_cabinet(cabinet_name)
  end
end