def __init__(self, parameters={}): self.cab_filename = parameters.get("cab_filename", None) self.max_data = parameters.get("max_data", 0) self.cabset = parameters.get("cabset", None) self.size = 0 index_in_set = parameters.get("index_in_set", 0) cfdata_reserve = parameters.get("cfdata_reserve", 0) cfheader_reserve = parameters.get("cfheader_reserve", 0) cffolder_reserve = parameters.get("cffolder_reserve", 0) if cfheader_reserve != 0 or cffolder_reserve != 0 or cfdata_reserve != 0: flags = CFHEADER.cfhdrRESERVE_PRESENT else: flags = 0x0000 reserve = { 'cbCFHeader': cfheader_reserve, 'cbCFFolder': cffolder_reserve, 'cbCFData': cfdata_reserve } self.cfheader = CFHEADER(flags=flags, reserve=reserve) self.cfheader.iCabinet = index_in_set self.cffolder_list = [] self.cffile_list = [] self.cfdata_list = [] # This is a help for calculating fields, is not part of the specification self.folder_id = 0
def test_cffolder_add_file(self): reserve = {'cbCFHeader': 0, 'cbCFFolder': 0, 'cbCFData': 0} cfheader = CFHEADER(flags=0, reserve=reserve) cffolder = CFFOLDER(cfheader=cfheader) cffile = CFFILE(cffolder=cffolder, filename="trav.txt") cffolder.add_file(cffile) self.assertEquals(1, cfheader.cFiles)
def read_cfheader(self, handle): """ This method creates and returns a new CFHEADER object from the data read """ parameters = {} signature = handle.read(4) if signature != "MSCF": raise Exception("Not a valid CAB File") parameters["reserved1"] = self._read_dword(handle) parameters["cbCabinet"] = self._read_dword(handle) parameters["reserved2"] = self._read_dword(handle) parameters["coffFiles"] = self._read_dword(handle) parameters["reserved3"] = self._read_dword(handle) parameters["versionMinor"] = self._read_byte(handle) parameters["versionMajor"] = self._read_byte(handle) parameters["cFolders"] = self._read_word(handle) parameters["cFiles"] =self._read_word(handle) parameters["flags"] = self._read_word(handle) parameters["setID"] = self._read_word(handle) parameters["iCabinet"] = self._read_word(handle) if parameters["flags"] & CFHEADER.cfhdrRESERVE_PRESENT: parameters["cbCFHeader"] = self._read_word(handle) parameters["cbCFFolder"] = self._read_byte(handle) parameters["cbCFData"] = self._read_byte(handle) parameters["abReserve"] = handle.read(parameters["cbCFHeader"]) else: parameters["cbCFHeader"] = 0 parameters["cbCFFolder"] = 0 parameters["cbCFData"] = 0 parameters["abReserve"] = "" if parameters["flags"] & CFHEADER.cfhdrPREV_CABINET: szCabinetPrev = handle.read(1) while szCabinetPrev[-1] != "\x00": szCabinetPrev += handle.read(1) parameters["szCabinetPrev"] = szCabinetPrev szDiskPrev = handle.read(1) while szDiskPrev[-1] != "\x00": szDiskPrev += handle.read(1) parameters["szDiskPrev"] = szDiskPrev else: parameters["szCabinetPrev"] = "" parameters["szDiskPrev"] = "" if parameters["flags"] & CFHEADER.cfhdrNEXT_CABINET: szCabinetNext = handle.read(1) while szCabinetNext[-1] != "\x00": szCabinetNext += handle.read(1) parameters["szCabinetNext"] = szCabinetNext szDiskNext = handle.read(1) while szDiskNext[-1] != "\x00": szDiskNext += handle.read(1) parameters["szDiskNext"] = szDiskNext else: parameters["szCabinetNext"] = "" parameters["szDiskNext"] = "" return CFHEADER.create_from_parameters(parameters=parameters)
def __init__(self, parameters={}): self.cab_filename = parameters.get("cab_filename", None) self.max_data = parameters.get("max_data", 0) self.cabset = parameters.get("cabset", None) self.size = 0 index_in_set = parameters.get("index_in_set", 0) cfdata_reserve = parameters.get("cfdata_reserve", 0) cfheader_reserve = parameters.get("cfheader_reserve", 0) cffolder_reserve = parameters.get("cffolder_reserve", 0) if cfheader_reserve != 0 or cffolder_reserve != 0 or cfdata_reserve != 0: flags = CFHEADER.cfhdrRESERVE_PRESENT else: flags = 0x0000 reserve = { 'cbCFHeader' : cfheader_reserve, 'cbCFFolder' : cffolder_reserve, 'cbCFData' : cfdata_reserve } self.cfheader = CFHEADER(flags=flags, reserve=reserve) self.cfheader.iCabinet = index_in_set self.cffolder_list = [] self.cffile_list = [] self.cfdata_list = [] # This is a help for calculating fields, is not part of the specification self.folder_id = 0
def test_cfdata_with_reserve(self): data = "this is the data" reserve = {'cbCFHeader': 0, 'cbCFFolder': 0, 'cbCFData': 20} cfheader = CFHEADER(flags=CFHEADER.cfhdrRESERVE_PRESENT, reserve=reserve) cffolder = CFFOLDER(cfheader=cfheader) cfdata = CFDATA(cffolder=cffolder, data=data) self.assertEquals(20, len(cfdata.abReserve)) self.assertEquals(len(cfdata), len(repr(cfdata)))
def test_cffolder_add_data(self): reserve = {'cbCFHeader': 0, 'cbCFFolder': 0, 'cbCFData': 0} cfheader = CFHEADER(flags=0, reserve=reserve) cffolder = CFFOLDER(cfheader=cfheader) cfdata = CFDATA(data="testdata1") cffolder.add_data(cfdata) cfdata = CFDATA(data="testdata2") cffolder.add_data(cfdata) self.assertEquals(2, cffolder.cCFData)
def test_cfheader_signature(self): """ This method checks a cfheader complies with basic specification """ reserve = {'cbCFHeader': 0, 'cbCFFolder': 0, 'cbCFData': 0} cfheader = CFHEADER(flags=0, reserve=reserve) self.assertEquals("MSCF", cfheader.signature) self.assertEquals(0x01, cfheader.versionMajor) self.assertEquals(0x03, cfheader.versionMinor)
def test_cfheader_create_from_params(self): """ This method tests the creation with parameters interface """ params = { "reserved1": 0xCAFEBABE, "cbCabinet": 0x1000, "reserved2": 0xCAFEBABE, "coffFiles": 0x1000, "reserved3": 0xCAFEBABE, "versionMinor": 0x4d, "versionMajor": 0x5a, "cFolders": 0x1000, "cFiles": 0x1000, "flags": 0, "setID": 0x4141, "iCabinet": 0, "cbCFHeader": 0xFFFF, "cbCFFolder": 0xFF, "cbCFData": 0xFF, "abReserve": "t r a v e s t i s", "szCabinetPrev": "Cab Not Found", "szDiskPrev": "Disk Not Found", "szCabinetNext": "Cab Not Found", "szDiskNext": "Disk Not Found", } cfheader = CFHEADER.create_from_parameters(parameters=params) self.assertEquals("MSCF", cfheader.signature) self.assertEquals(0xCAFEBABE, cfheader.reserved1) self.assertEquals(0x1000, cfheader.cbCabinet) self.assertEquals(0xCAFEBABE, cfheader.reserved2) self.assertEquals(0x1000, cfheader.coffFiles) self.assertEquals(0xCAFEBABE, cfheader.reserved3) self.assertEquals( "MZ", chr(cfheader.versionMinor) + chr(cfheader.versionMajor)) self.assertEquals(0x1000, cfheader.cFolders) self.assertEquals(0x1000, cfheader.cFiles) self.assertEquals(0x00, cfheader.flags) self.assertEquals(0x4141, cfheader.setID) self.assertEquals(0x00, cfheader.iCabinet) self.assertEquals(0xFFFF, cfheader.cbCFHeader) self.assertEquals(0xFF, cfheader.cbCFFolder) self.assertEquals(0xFF, cfheader.cbCFData) self.assertEquals("t r a v e s t i s", cfheader.abReserve) self.assertEquals("Cab Not Found", cfheader.szCabinetPrev) self.assertEquals("Disk Not Found", cfheader.szDiskPrev) self.assertEquals("Cab Not Found", cfheader.szCabinetNext) self.assertEquals("Disk Not Found", cfheader.szDiskNext) self.assertEquals(len(cfheader), len(cfheader._repr_without_checks()))
def test_cfheader_create_from_params(self): """ This method tests the creation with parameters interface """ params = { "reserved1": 0xCAFEBABE, "cbCabinet": 0x1000, "reserved2": 0xCAFEBABE, "coffFiles": 0x1000, "reserved3": 0xCAFEBABE, "versionMinor": 0x4d, "versionMajor": 0x5a, "cFolders": 0x1000, "cFiles": 0x1000, "flags": 0, "setID": 0x4141, "iCabinet": 0, "cbCFHeader": 0xFFFF, "cbCFFolder": 0xFF, "cbCFData": 0xFF, "abReserve": "t r a v e s t i s", "szCabinetPrev": "Cab Not Found", "szDiskPrev": "Disk Not Found", "szCabinetNext": "Cab Not Found", "szDiskNext": "Disk Not Found", } cfheader = CFHEADER.create_from_parameters(parameters=params) self.assertEquals("MSCF", cfheader.signature) self.assertEquals(0xCAFEBABE, cfheader.reserved1) self.assertEquals(0x1000, cfheader.cbCabinet) self.assertEquals(0xCAFEBABE, cfheader.reserved2) self.assertEquals(0x1000, cfheader.coffFiles) self.assertEquals(0xCAFEBABE, cfheader.reserved3) self.assertEquals("MZ", chr(cfheader.versionMinor)+chr(cfheader.versionMajor)) self.assertEquals(0x1000, cfheader.cFolders) self.assertEquals(0x1000, cfheader.cFiles) self.assertEquals(0x00, cfheader.flags) self.assertEquals(0x4141, cfheader.setID) self.assertEquals(0x00, cfheader.iCabinet) self.assertEquals(0xFFFF, cfheader.cbCFHeader) self.assertEquals(0xFF, cfheader.cbCFFolder) self.assertEquals(0xFF, cfheader.cbCFData) self.assertEquals("t r a v e s t i s", cfheader.abReserve) self.assertEquals("Cab Not Found", cfheader.szCabinetPrev) self.assertEquals("Disk Not Found", cfheader.szDiskPrev) self.assertEquals("Cab Not Found", cfheader.szCabinetNext) self.assertEquals("Disk Not Found", cfheader.szDiskNext) self.assertEquals(len(cfheader), len(cfheader._repr_without_checks()))
def test_cfheader_reserve(self): """ This method checks a cfheader actually reserves the bytes that are specified """ bytes_to_reserve = random.randint(1, 30) reserve = { 'cbCFHeader': bytes_to_reserve, 'cbCFFolder': 0, 'cbCFData': 0 } cfheader = CFHEADER(flags=CFHEADER.cfhdrRESERVE_PRESENT, reserve=reserve) self.assertEquals(CFHEADER.cfhdrRESERVE_PRESENT, cfheader.flags & CFHEADER.cfhdrRESERVE_PRESENT) self.assertEquals(bytes_to_reserve, cfheader.cbCFHeader) self.assertEquals(bytes_to_reserve, len(cfheader.abReserve)) self.assertEquals(len(cfheader), len(repr(cfheader)))
class CABFile(CABFileFormat): @property def slack(self): return self.max_data - self.size def __init__(self, parameters={}): self.cab_filename = parameters.get("cab_filename", None) self.max_data = parameters.get("max_data", 0) self.cabset = parameters.get("cabset", None) self.size = 0 index_in_set = parameters.get("index_in_set", 0) cfdata_reserve = parameters.get("cfdata_reserve", 0) cfheader_reserve = parameters.get("cfheader_reserve", 0) cffolder_reserve = parameters.get("cffolder_reserve", 0) if cfheader_reserve != 0 or cffolder_reserve != 0 or cfdata_reserve != 0: flags = CFHEADER.cfhdrRESERVE_PRESENT else: flags = 0x0000 reserve = { 'cbCFHeader': cfheader_reserve, 'cbCFFolder': cffolder_reserve, 'cbCFData': cfdata_reserve } self.cfheader = CFHEADER(flags=flags, reserve=reserve) self.cfheader.iCabinet = index_in_set self.cffolder_list = [] self.cffile_list = [] self.cfdata_list = [] # This is a help for calculating fields, is not part of the specification self.folder_id = 0 ##### METHODS FOR MANAGING CABs ##### def get_cfheader(self): return self.cfheader def get_cffolder_list(self): return self.cffolder_list def get_cffile_list(self): return self.cffile_list def get_cfdata_list(self): return self.cfdata_list ##################################### def _create_cffolder(self, folder_name): new_cffolder = CFFOLDER(self.cfheader, folder_id=self.folder_id) new_cffolder.name = folder_name self.folder_id += 1 self.cfheader.add_folder(cffolder=new_cffolder) return new_cffolder def update_fields(self): # Update uoffFolderStart in CFFILE self._update_uoffFolderStart() #coffCabStart in CFFOLDER self._update_coffCabStart() # Update cCabinet and coffFiles in CFHEADER self._update_cbCabinet() self._update_coffFiles() def _check_for_scattered_prev_cffile(self, cffolder): # This method workarounds a border case that happens when we have 2 files in a folder # In wich the first file occupies more than one cab... When this happens, when the second # cffile gets added, it cannot share the scattered CFFOLDER because it doesn't work.. # We need to create an anonymous CFFOLDER here and return it last_cffile = cffolder.cffile_list[-1] if last_cffile.iFolder & CFFILE.ifoldCONTINUED_FROM_PREV == CFFILE.ifoldCONTINUED_FROM_PREV: anonymous_folder = self._create_cffolder(Utils.get_random_name(10)) self.cffolder_list.append(anonymous_folder) return anonymous_folder return cffolder def add_file(self, folder_name, filename, total_len, data): if self.size == self.max_data: raise CABException("This cab is full") if (self.size + len(data)) <= self.max_data: try: cffolder = next(_ for _ in self.cffolder_list if _.name == folder_name) # We need to check if the cffolder has a cffile scattered that continues from a PREV # If this is the case, we need to provide a new cffolder anyways.. this is how it works cffolder = self._check_for_scattered_prev_cffile(cffolder) except StopIteration: cffolder = self._create_cffolder(folder_name) self.cffolder_list.append(cffolder) cffile = CFFILE(cffolder=cffolder, total_len=total_len, filename=filename) self.cffile_list.append(cffile) # Max data per CFDATA is 0x8000 -> This is an empirical result if len(data) > 0x8000: data_chunks = [ data[i:i + 0x8000] for i in range(0, len(data), 0x8000) ] for data_chunk in data_chunks: cfdata = CFDATA(cffolder=cffolder, data=data_chunk) self.cfdata_list.append(cfdata) cffolder.add_data(cfdata) else: cfdata = CFDATA(cffolder=cffolder, data=data) self.cfdata_list.append(cfdata) # Update cCFData cffolder.add_data(cfdata) cffolder.add_file(cffile) self.update_fields() self.size += len(data) else: raise CABException("The cab hasn't enough space for the data ") def _update_uoffFolderStart(self): """Updates the Uncompressed byte offset of the start of every file's data""" for key, group in groupby(self.cffile_list, lambda x: x.cffolder.folder_id): offset = 0 for cffile in group: cffile.uoffFolderStart = offset offset += cffile.cbFile def _update_coffCabStart(self): """Update the Absolute file offset of first CFDATA block for every CFFolder""" data_start = len(self.cfheader) + sum([len(cffolder) for cffolder in self.cffolder_list]) + \ sum([len(cffile) for cffile in self.cffile_list]) self.cffolder_list[0].coffCabStart = data_start for index, cffolder in enumerate(self.cffolder_list[1:]): data_start = data_start + sum([ len(cfdata) for cfdata in self.cffolder_list[index].cfdata_list ]) cffolder.coffCabStart = data_start # current = 0 # for index, cfdata in enumerate(self.cfdata_list): # cffolder = cfdata.cffolder # if cffolder.folder_id == current: # offset = len(self.cfheader) + sum([len(cffolder) for cffolder in self.cffolder_list]) + \ # sum([len(cffile) for cffile in self.cffile_list]) # # # now the hard part # for p_data in self.cfdata_list: # if p_data.cffolder.folder_id != current: # offset += len(p_data) # else: # current += 1 # cffolder.coffCabStart = offset # break def _update_cbCabinet(self): """ Total size of this cabinet file in bytes. """ self.cfheader.cbCabinet = self.__len__() def _update_coffFiles(self): """ Absolute file offset of first CFFILE entry. """ value = len(self.cfheader) for i in self.cffolder_list: value += len(i) self.cfheader.coffFiles = value def __repr__(self): data = repr(self.cfheader) for i in self.cffolder_list: data += repr(i) for i in self.cffile_list: data += repr(i) for i in self.cfdata_list: data += repr(i) return data def __str__(self): data = str(self.cfheader) for i in self.cffolder_list: data += str(i) for i in self.cffile_list: data += str(i) for i in self.cfdata_list: data += str(i) return data def __len__(self): result = len(self.cfheader) for i in self.cffolder_list: result += len(i) for i in self.cffile_list: result += len(i) for i in self.cfdata_list: result += len(i) return result @classmethod def get_null_ended_string(cls, sz): return sz + "\x00"
def test_cfheader_add_folder(self): reserve = {'cbCFHeader': 0, 'cbCFFolder': 0, 'cbCFData': 0} cfheader = CFHEADER(flags=0, reserve=reserve) cffolder = CFFOLDER(cfheader=cfheader) cfheader.add_folder(cffolder) self.assertEquals(1, cfheader.cFolders)
def test_cffolder_with_reserve(self): reserve = {'cbCFHeader': 0, 'cbCFFolder': 20, 'cbCFData': 0} cfheader = CFHEADER(flags=CFHEADER.cfhdrRESERVE_PRESENT, reserve=reserve) cffolder = CFFOLDER(cfheader=cfheader) self.assertEquals(20, len(cffolder.abReserve))
class CABFile(CABFileFormat): @property def slack(self): return self.max_data - self.size def __init__(self, parameters={}): self.cab_filename = parameters.get("cab_filename", None) self.max_data = parameters.get("max_data", 0) self.cabset = parameters.get("cabset", None) self.size = 0 index_in_set = parameters.get("index_in_set", 0) cfdata_reserve = parameters.get("cfdata_reserve", 0) cfheader_reserve = parameters.get("cfheader_reserve", 0) cffolder_reserve = parameters.get("cffolder_reserve", 0) if cfheader_reserve != 0 or cffolder_reserve != 0 or cfdata_reserve != 0: flags = CFHEADER.cfhdrRESERVE_PRESENT else: flags = 0x0000 reserve = { 'cbCFHeader' : cfheader_reserve, 'cbCFFolder' : cffolder_reserve, 'cbCFData' : cfdata_reserve } self.cfheader = CFHEADER(flags=flags, reserve=reserve) self.cfheader.iCabinet = index_in_set self.cffolder_list = [] self.cffile_list = [] self.cfdata_list = [] # This is a help for calculating fields, is not part of the specification self.folder_id = 0 ##### METHODS FOR MANAGING CABs ##### def get_cfheader(self): return self.cfheader def get_cffolder_list(self): return self.cffolder_list def get_cffile_list(self): return self.cffile_list def get_cfdata_list(self): return self.cfdata_list ##################################### def _create_cffolder(self, folder_name): new_cffolder = CFFOLDER(self.cfheader, folder_id=self.folder_id) new_cffolder.name = folder_name self.folder_id += 1 self.cfheader.add_folder(cffolder=new_cffolder) return new_cffolder def update_fields(self): # Update uoffFolderStart in CFFILE self._update_uoffFolderStart() #coffCabStart in CFFOLDER self._update_coffCabStart() # Update cCabinet and coffFiles in CFHEADER self._update_cbCabinet() self._update_coffFiles() def _check_for_scattered_prev_cffile(self, cffolder): # This method workarounds a border case that happens when we have 2 files in a folder # In wich the first file occupies more than one cab... When this happens, when the second # cffile gets added, it cannot share the scattered CFFOLDER because it doesn't work.. # We need to create an anonymous CFFOLDER here and return it last_cffile = cffolder.cffile_list[-1] if last_cffile.iFolder & CFFILE.ifoldCONTINUED_FROM_PREV == CFFILE.ifoldCONTINUED_FROM_PREV: anonymous_folder = self._create_cffolder(Utils.get_random_name(10)) self.cffolder_list.append(anonymous_folder) return anonymous_folder return cffolder def add_file(self, folder_name, filename, total_len, data): if self.size == self.max_data: raise CABException("This cab is full") if (self.size + len(data)) <= self.max_data: try: cffolder = next(_ for _ in self.cffolder_list if _.name == folder_name) # We need to check if the cffolder has a cffile scattered that continues from a PREV # If this is the case, we need to provide a new cffolder anyways.. this is how it works cffolder = self._check_for_scattered_prev_cffile(cffolder) except StopIteration: cffolder = self._create_cffolder(folder_name) self.cffolder_list.append(cffolder) cffile = CFFILE(cffolder=cffolder, total_len=total_len, filename=filename) self.cffile_list.append(cffile) # Max data per CFDATA is 0x8000 -> This is an empirical result if len(data) > 0x8000: data_chunks = [data[i:i+0x8000] for i in range(0, len(data), 0x8000)] for data_chunk in data_chunks: cfdata = CFDATA(cffolder=cffolder, data=data_chunk) self.cfdata_list.append(cfdata) cffolder.add_data(cfdata) else: cfdata = CFDATA(cffolder=cffolder, data=data) self.cfdata_list.append(cfdata) # Update cCFData cffolder.add_data(cfdata) cffolder.add_file(cffile) self.update_fields() self.size += len(data) else: raise CABException("The cab hasn't enough space for the data ") def _update_uoffFolderStart(self): """Updates the Uncompressed byte offset of the start of every file's data""" for key, group in groupby(self.cffile_list, lambda x: x.cffolder.folder_id): offset = 0 for cffile in group: cffile.uoffFolderStart = offset offset += cffile.cbFile def _update_coffCabStart(self): """Update the Absolute file offset of first CFDATA block for every CFFolder""" data_start = len(self.cfheader) + sum([len(cffolder) for cffolder in self.cffolder_list]) + \ sum([len(cffile) for cffile in self.cffile_list]) self.cffolder_list[0].coffCabStart = data_start for index, cffolder in enumerate(self.cffolder_list[1:]): data_start = data_start + sum([len(cfdata) for cfdata in self.cffolder_list[index].cfdata_list]) cffolder.coffCabStart = data_start # current = 0 # for index, cfdata in enumerate(self.cfdata_list): # cffolder = cfdata.cffolder # if cffolder.folder_id == current: # offset = len(self.cfheader) + sum([len(cffolder) for cffolder in self.cffolder_list]) + \ # sum([len(cffile) for cffile in self.cffile_list]) # # # now the hard part # for p_data in self.cfdata_list: # if p_data.cffolder.folder_id != current: # offset += len(p_data) # else: # current += 1 # cffolder.coffCabStart = offset # break def _update_cbCabinet(self): """ Total size of this cabinet file in bytes. """ self.cfheader.cbCabinet = self.__len__() def _update_coffFiles(self): """ Absolute file offset of first CFFILE entry. """ value = len(self.cfheader) for i in self.cffolder_list: value += len(i) self.cfheader.coffFiles = value def __repr__(self): data = repr(self.cfheader) for i in self.cffolder_list: data += repr(i) for i in self.cffile_list: data += repr(i) for i in self.cfdata_list: data += repr(i) return data def __str__(self): data = str(self.cfheader) for i in self.cffolder_list: data += str(i) for i in self.cffile_list: data += str(i) for i in self.cfdata_list: data += str(i) return data def __len__(self): result = len(self.cfheader) for i in self.cffolder_list: result += len(i) for i in self.cffile_list: result += len(i) for i in self.cfdata_list: result += len(i) return result @classmethod def get_null_ended_string(cls, sz): return sz + "\x00"