Exemple #1
0
    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
Exemple #2
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)
Exemple #3
0
    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)
Exemple #4
0
    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
Exemple #5
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)))
Exemple #6
0
 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)
Exemple #7
0
    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)
Exemple #8
0
    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()))
Exemple #9
0
    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()))
Exemple #10
0
    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)))
Exemple #11
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"
Exemple #12
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)
Exemple #13
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)
Exemple #14
0
 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))
Exemple #15
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"