Esempio n. 1
0
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"
Esempio n. 2
0
 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)
Esempio n. 3
0
 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)
Esempio n. 4
0
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"