aboutsummaryrefslogtreecommitdiff
path: root/license_protected_downloads/buildinfo.py
blob: 19c6f46a96f9ea0bc812265b232833919458fc2c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
import os
import fnmatch


class IncorrectDataFormatException(Exception):
        ''' Build-info data is in incorrect format. '''


class BuildInfoBase(object):
    fields_defined = [
        "format-version", "files-pattern", "build-name", "theme",
        "license-type", "auth-groups", "collect-user-data",
        "license-text",
        # Deprecated
        "openid-launchpad-teams",
    ]

    def __init__(self, full_name, content):
        self.fname = os.path.basename(full_name)

        self.index = 0
        self.build_info_array = [{}]

        lines = [x for x in content.split('\n') if x.strip()]
        self.parseData(lines)

        self.file_info_array = self.getInfoForFile()
        self.remove_false_positives()
        self.max_index = len(self.file_info_array)

    # Get value of specified field in block index for
    # corresponding file
    def get(self, field, index=0):
        if index > self.max_index:
            return False
        block = self.file_info_array[index]
        return block.get(field, False)

    def _set(self, key, value):
        """Record set of directives applying to a file pattern
        key: file pattern
        value: list of dicts of field/val pairs"""
        if key in self.build_info_array[self.index]:
            # A repeated key indicates we have found another chunk of
            # build-info
            self.build_info_array[self.index][key].append(value[0])
        else:
            self.build_info_array[self.index][key] = value

    def assertValidField(self, field):
        if field not in self.fields_defined:
            raise IncorrectDataFormatException(
                "Field '%s' not allowed." % field)

    def parseLine(self, line):
        values = line.split(":", 1)
        if len(values) != 2:
            raise IncorrectDataFormatException(
                "'%s': Line is not in the correct format." % line)
        else:
            field = values[0].strip().lower()
            self.assertValidField(field)
            value = values[1].strip()

            # Rename any deprecated field names to new names
            field_renames = {"openid-launchpad-teams": "auth-groups"}
            field = field_renames.get(field, field)
            return {field: value}

    def parseContinuation(self, lines):
        text = ''
        while self.line_no < len(lines) and len(lines[self.line_no]) > 0:
            if lines[self.line_no][0] == ' ':
                text += '\n' + lines[self.line_no][1:]
                self.line_no += 1
            else:
                break
        return text

    def parseBlock(self, lines):
        result = [{}]
        while self.line_no < len(lines):
            line = lines[self.line_no]
            values = self.parseLine(line)
            if 'license-text' in values:
                text = values['license-text']
                self.line_no += 1
                text += self.parseContinuation(lines)
                result[0]['license-text'] = text
            elif 'files-pattern' in values:
                return result
            else:
                self.line_no += 1
                key = values.keys()[0]
                result[0][key] = values[key]
        return result

    def parseData(self, lines):
        if not isinstance(lines, list):
            raise IncorrectDataFormatException("No array provided.")
        if not list:
            raise IncorrectDataFormatException("Empty BUILD-INFO")
        format_line = lines.pop(0)
        values = self.parseLine(format_line)
        if 'format-version' not in values:
            raise IncorrectDataFormatException(
                'Format-Version field not found.')
        self._set("format-version", values["format-version"])

        self.line_no = 0
        while self.line_no < len(lines):
            line = lines[self.line_no]
            values = self.parseLine(line)
            if "files-pattern" in values:
                self.line_no += 1
                block = self.parseBlock(lines)
                if isinstance(block, list):
                    for pattern in values["files-pattern"].split(","):
                            self._set(pattern.strip(), block)

    def getInfoForFile(self):
        for block in self.build_info_array:
            if self.fname in block:
                # File name matches a key directly - don't need to iterate
                # through each using fnmatch to implement globs.
                return block[self.fname]
            for key in block:
                if key != 'format-version':
                    if fnmatch.fnmatch(self.fname, key):
                        return block[key]
        return [{}]

    def remove_false_positives(self):
        open_type = []
        protected_type = []
        index = 0
        for block in self.file_info_array:
            if 'license-type' in block.keys():
                if block["license-type"] == 'open':
                    open_type.append(index)
                if block["license-type"] == 'protected':
                    protected_type.append(index)
                index += 1
            else:
                return False
        if len(protected_type) != 0 and len(open_type) != 0:
            for index in open_type:
                self.file_info_array.pop(index)


class BuildInfo(BuildInfoBase):
    def __init__(self, fn):

        search_path = self.get_search_path(fn)
        build_info_file = os.path.join(search_path, "BUILD-INFO.txt")

        with open(build_info_file, "r") as f:
            super(BuildInfo, self).__init__(fn, f.read())

    @classmethod
    def get_search_path(cls, path):
        "Return BUILD-INFO.txt search path for a given filesystem path."
        if not os.path.isdir(path):
            path = os.path.dirname(path)
        return path

    @classmethod
    def build_info_exists(cls, path):
        "Check if BUILD-INFO.txt exists for a given filesystem path."
        return os.path.exists(
            os.path.join(cls.get_search_path(path), "BUILD-INFO.txt"))

    @classmethod
    def write_from_array(cls, build_info_array, file_path):
        if len(build_info_array[0]):
            with open(file_path, "w") as outfile:
                outfile.write("Format-Version: 0.5\n\n")
                for key in build_info_array[0]:
                    if key != "format-version":
                        outfile.write("Files-Pattern: %s\n" % key)
                        for item in build_info_array[0][key][0]:
                            text = build_info_array[0][key][0][item]
                            if item == "license-text":
                                text = text.replace("\n", "\n ")
                            outfile.write("%s: %s\n" % (item, text))
                        outfile.write("\n")


if __name__ == "__main__":
    import sys
    bi = BuildInfo(sys.argv[1])
    for field in bi.fields_defined:
        print field + " = " + str(bi.get(field))