Module refinery.units.formats.archive.xtiex
Expand source code Browse git
from __future__ import annotations
from refinery import perc, xtcab
from refinery.lib.id import buffer_contains, is_likely_pe
from refinery.lib.tools import exception_to_string
from refinery.lib.types import buf
from refinery.units import Chunk
from refinery.units.formats.archive import ArchiveUnit
class xtiex(xtcab, docs='{0}{p}{PathExtractorUnit}'):
"""
Extract files from IExpress setup self-extracting archive. If the archive contains more than one
resource named CABINET, the files of each cabinet are listed below the path of its resource.
"""
def unpack(self, data: buf):
with data | perc('RCDATA/CABINET/*') as resources:
cabinets = [Chunk.Wrap(resource) for resource in resources]
if not cabinets:
raise ValueError('no RCDATA resource named CABINET was found')
if len(cabinets) == 1:
yield from super().unpack(cabinets[0])
return
readable = 0
for cabinet in cabinets:
resource = str(cabinet.meta['path'])
try:
results = list(super().unpack(cabinet))
except Exception as error:
self.log_warn(F'failed to read the resource {resource}: {exception_to_string(error)}')
continue
readable += 1
for result in results:
result.path = F'{resource}/{result.path}'
yield result
if not readable:
raise ValueError(F'none of the {len(cabinets)} resources named CABINET could be read')
def filter(self, chunks):
yield from super(ArchiveUnit, self).filter(chunks)
@classmethod
def handles(cls, data: buf) -> bool:
if not is_likely_pe(data):
return False
return buffer_contains(data, B'CABINET')
Classes
class xtiex (*paths, list=False, join_path=False, drop_path=False, fuzzy=0, exact=False, regex=False, path=b'path', exclude=None, date=b'date', pwd=b'')-
Extract files from IExpress setup self-extracting archive. If the archive contains more than one resource named CABINET, the files of each cabinet are listed below the path of its resource.
This unit extracts items with an associated virtual path from a container; each extracted item is emitted as a separate chunk with a corresponding meta variable named "path".
Positional arguments to PathExtractorUnit are patterns to filter the extracted items. Use the
-xflag to add an exclusion pattern. To extract all files with a foo or bar extension, but none that has the word "temp" in its path:PathExtractorUnit .foo .bar -x tempTo view only the paths of all chunks, use the listing switch:
emit data | ... | PathExtractorUnit -lOtherwise, extracted items are written to the standard output port and usually require a frame to properly process. In order to dump all extracted data to disk, the following pipeline can be used:
emit data | ... | PathExtractorUnit [| dump extracted/{path} ]The value
{path}is a placeholder which is substituted by the virtual path of the extracted item. When several items have the same path, each of them gets a number appended to its path in the order in which PathExtractorUnit lists them, and a pattern that matches the path before the number selects all of them. When using PathExtractorUnit to unpack a file on disk, the following pattern can be useful:ef pack.bin [| PathExtractorUnit -j | d2p ]The unit
efis also a path extractor. By specifying-j(or--join), the paths of extracted items are combined. Here,d2pis a shortcut fordump {path}. It deconflicts the joined paths with the local file system: Ifpack.bincontains itemsone.txtandtwo.txt, the following local file tree would be the result:pack.bin pack/one.txt pack/two.txtFinally, the
-d(or--drop) switch can be used to not create (or alter) the path metadata at all, which is useful in cases where path metadata from a previous unit should be preserved.Expand source code Browse git
class xtiex(xtcab, docs='{0}{p}{PathExtractorUnit}'): """ Extract files from IExpress setup self-extracting archive. If the archive contains more than one resource named CABINET, the files of each cabinet are listed below the path of its resource. """ def unpack(self, data: buf): with data | perc('RCDATA/CABINET/*') as resources: cabinets = [Chunk.Wrap(resource) for resource in resources] if not cabinets: raise ValueError('no RCDATA resource named CABINET was found') if len(cabinets) == 1: yield from super().unpack(cabinets[0]) return readable = 0 for cabinet in cabinets: resource = str(cabinet.meta['path']) try: results = list(super().unpack(cabinet)) except Exception as error: self.log_warn(F'failed to read the resource {resource}: {exception_to_string(error)}') continue readable += 1 for result in results: result.path = F'{resource}/{result.path}' yield result if not readable: raise ValueError(F'none of the {len(cabinets)} resources named CABINET could be read') def filter(self, chunks): yield from super(ArchiveUnit, self).filter(chunks) @classmethod def handles(cls, data: buf) -> bool: if not is_likely_pe(data): return False return buffer_contains(data, B'CABINET')Ancestors
Subclasses
Methods
def unpack(self, data)-
Expand source code Browse git
def unpack(self, data: buf): with data | perc('RCDATA/CABINET/*') as resources: cabinets = [Chunk.Wrap(resource) for resource in resources] if not cabinets: raise ValueError('no RCDATA resource named CABINET was found') if len(cabinets) == 1: yield from super().unpack(cabinets[0]) return readable = 0 for cabinet in cabinets: resource = str(cabinet.meta['path']) try: results = list(super().unpack(cabinet)) except Exception as error: self.log_warn(F'failed to read the resource {resource}: {exception_to_string(error)}') continue readable += 1 for result in results: result.path = F'{resource}/{result.path}' yield result if not readable: raise ValueError(F'none of the {len(cabinets)} resources named CABINET could be read')
Inherited members
xtcab:xtcab:CommonPasswordsCustomJoinBehaviourCustomPathSeparatorFilterEverythingRequiresactassemblecarvedclosecodecfinishhandlesis_quietis_reversibleisattylabelledleniencylog_alwayslog_debuglog_detachlog_faillog_infolog_levellog_warnloggernamenozzleprocessreadread1resetreverserunsourcesuperinit
ArchiveUnit: