1edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra#!/usr/bin/env python
2edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
3edf7b4c861144764d0bc17436064d52e7147f916Geremy Condrafrom xml.sax import saxutils, handler, make_parser
4edf7b4c861144764d0bc17436064d52e7147f916Geremy Condrafrom optparse import OptionParser
5edf7b4c861144764d0bc17436064d52e7147f916Geremy Condraimport ConfigParser
6edf7b4c861144764d0bc17436064d52e7147f916Geremy Condraimport logging
7edf7b4c861144764d0bc17436064d52e7147f916Geremy Condraimport base64
8edf7b4c861144764d0bc17436064d52e7147f916Geremy Condraimport sys
9edf7b4c861144764d0bc17436064d52e7147f916Geremy Condraimport os
10edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
11edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra__VERSION = (0, 1)
12edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
13edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra'''
14edf7b4c861144764d0bc17436064d52e7147f916Geremy CondraThis tool reads a mac_permissions.xml and replaces keywords in the signature
15edf7b4c861144764d0bc17436064d52e7147f916Geremy Condraclause with keys provided by pem files.
16edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra'''
17edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
18edf7b4c861144764d0bc17436064d52e7147f916Geremy Condraclass GenerateKeys(object):
19edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    def __init__(self, path):
20edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        '''
21edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        Generates an object with Base16 and Base64 encoded versions of the keys
22edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        found in the supplied pem file argument. PEM files can contain multiple
23edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        certs, however this seems to be unused in Android as pkg manager grabs
24edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        the first cert in the APK. This will however support multiple certs in
25edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        the resulting generation with index[0] being the first cert in the pem
26edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        file.
27edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        '''
28edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
29edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        self._base64Key = list()
30edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        self._base16Key = list()
31edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
32edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        if not os.path.isfile(path):
33edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra            sys.exit("Path " + path + " does not exist or is not a file!")
34edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
35edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        pkFile = open(path, 'rb').readlines()
36edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        base64Key = ""
371ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts        lineNo = 1
381ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts        certNo = 1
39edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        inCert = False
40edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        for line in pkFile:
411ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts            line = line.strip()
421ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts            # Are we starting the certificate?
4314138335bd3c7204d5bff4690ffa2314dd4a0a9eWilliam Roberts            if line == "-----BEGIN CERTIFICATE-----":
441ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                if inCert:
451ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                    sys.exit("Encountered another BEGIN CERTIFICATE without END CERTIFICATE on " +
461ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                             "line: " + str(lineNo))
471ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts
481ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                inCert = True
491ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts
501ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts            # Are we ending the ceritifcate?
5114138335bd3c7204d5bff4690ffa2314dd4a0a9eWilliam Roberts            elif line == "-----END CERTIFICATE-----":
521ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                if not inCert:
531ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                    sys.exit("Encountered END CERTIFICATE before BEGIN CERTIFICATE on line: "
541ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                            + str(lineNo))
551ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts
561ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                # If we ended the certificate trip the flag
571ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                inCert = False
581ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts
591ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                # Sanity check the input
601ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                if len(base64Key) == 0:
611ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                    sys.exit("Empty certficate , certificate "+ str(certNo) + " found in file: "
621ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                            + path)
631ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts
641ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                # ... and append the certificate to the list
651ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                # Base 64 includes uppercase. DO NOT tolower()
661ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                self._base64Key.append(base64Key)
671ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                try:
681ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                    # Pkgmanager and setool see hex strings with lowercase, lets be consistent
691ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                    self._base16Key.append(base64.b16encode(base64.b64decode(base64Key)).lower())
701ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                except TypeError:
711ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                    sys.exit("Invalid certificate, certificate "+ str(certNo) + " found in file: "
721ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                            + path)
731ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts
741ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                # After adding the key, reset the accumulator as pem files may have subsequent keys
751ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                base64Key=""
761ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts
771ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                # And increment your cert number
781ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                certNo = certNo + 1
791ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts
801ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts            # If we haven't started the certificate, then we should not encounter any data
811ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts            elif not inCert:
82070c01f8f133dfc0da82beca6f747b679d27f904Mike Palmiotto                if line is not "":
83070c01f8f133dfc0da82beca6f747b679d27f904Mike Palmiotto                    sys.exit("Detected erroneous line \""+ line + "\" on " + str(lineNo)
841ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                        + " in pem file: " + path)
851ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts
861ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts            # else we have started the certicate and need to append the data
871ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts            elif inCert:
881ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                base64Key += line
891ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts
901ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts            else:
911ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                # We should never hit this assert, if we do then an unaccounted for state
921ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                # was entered that was NOT addressed by the if/elif statements above
931ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts                assert(False == True)
941ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts
951ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts            # The last thing to do before looping up is to increment line number
961ecb4e8ad15a44347e0a2460c204d819e4ebd269William Roberts            lineNo = lineNo + 1
97edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
98edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    def __len__(self):
99edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        return len(self._base16Key)
100edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
101edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    def __str__(self):
102edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        return str(self.getBase16Keys())
103edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
104edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    def getBase16Keys(self):
105edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        return self._base16Key
106edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
107edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    def getBase64Keys(self):
108edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        return self._base64Key
109edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
110edf7b4c861144764d0bc17436064d52e7147f916Geremy Condraclass ParseConfig(ConfigParser.ConfigParser):
111edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
112edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    # This must be lowercase
113edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    OPTION_WILDCARD_TAG = "all"
114edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
115020b5ff6311044ef7a2200dd4db69f5cccf46213Geremy Condra    def generateKeyMap(self, target_build_variant, key_directory):
116edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
117edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        keyMap = dict()
118edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
119edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        for tag in self.sections():
120edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
121edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra            options = self.options(tag)
122edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
123edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra            for option in options:
124edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
125edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                # Only generate the key map for debug or release,
126edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                # not both!
127edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                if option != target_build_variant and \
128edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                option != ParseConfig.OPTION_WILDCARD_TAG:
129edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                    logging.info("Skipping " + tag + " : " + option +
130edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                        " because target build variant is set to " +
131edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                        str(target_build_variant))
132edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                    continue
133edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
134edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                if tag in keyMap:
135edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                    sys.exit("Duplicate tag detected " + tag)
136edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
1371b46b2fe4723b0dda74c2f66a09df8259508fd4bRichard Haines                tag_path = os.path.expandvars(self.get(tag, option))
1381b46b2fe4723b0dda74c2f66a09df8259508fd4bRichard Haines                path = os.path.join(key_directory, tag_path)
139edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
140edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                keyMap[tag] = GenerateKeys(path)
141edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
142edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                # Multiple certificates may exist in
143edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                # the pem file. GenerateKeys supports
144edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                # this however, the mac_permissions.xml
145edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                # as well as PMS do not.
146edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                assert len(keyMap[tag]) == 1
147edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
148edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        return keyMap
149edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
150edf7b4c861144764d0bc17436064d52e7147f916Geremy Condraclass ReplaceTags(handler.ContentHandler):
151edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
152edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    DEFAULT_TAG = "default"
153edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    PACKAGE_TAG = "package"
154edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    POLICY_TAG = "policy"
155edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    SIGNER_TAG = "signer"
156edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    SIGNATURE_TAG = "signature"
157edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
158edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    TAGS_WITH_CHILDREN = [ DEFAULT_TAG, PACKAGE_TAG, POLICY_TAG, SIGNER_TAG ]
159edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
160edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    XML_ENCODING_TAG = '<?xml version="1.0" encoding="iso-8859-1"?>'
161edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
162edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    def __init__(self, keyMap, out=sys.stdout):
163edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
164edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        handler.ContentHandler.__init__(self)
165edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        self._keyMap = keyMap
166edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        self._out = out
167edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        self._out.write(ReplaceTags.XML_ENCODING_TAG)
168edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        self._out.write("<!-- AUTOGENERATED FILE DO NOT MODIFY -->")
1697f2392eeb03eeb88f2699061f4adaeb1fcbd1de2Robert Craig        self._out.write("<policy>")
1707f2392eeb03eeb88f2699061f4adaeb1fcbd1de2Robert Craig
1717f2392eeb03eeb88f2699061f4adaeb1fcbd1de2Robert Craig    def __del__(self):
1727f2392eeb03eeb88f2699061f4adaeb1fcbd1de2Robert Craig        self._out.write("</policy>")
173edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
174edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    def startElement(self, tag, attrs):
1757f2392eeb03eeb88f2699061f4adaeb1fcbd1de2Robert Craig        if tag == ReplaceTags.POLICY_TAG:
1767f2392eeb03eeb88f2699061f4adaeb1fcbd1de2Robert Craig            return
177edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
178edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        self._out.write('<' + tag)
179edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
180edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        for (name, value) in attrs.items():
181edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
182edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra            if name == ReplaceTags.SIGNATURE_TAG and value in self._keyMap:
183edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                for key in self._keyMap[value].getBase16Keys():
184edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                    logging.info("Replacing " + name + " " + value + " with " + key)
185edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                    self._out.write(' %s="%s"' % (name, saxutils.escape(key)))
186edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra            else:
187edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                self._out.write(' %s="%s"' % (name, saxutils.escape(value)))
188edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
189edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        if tag in ReplaceTags.TAGS_WITH_CHILDREN:
190edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra            self._out.write('>')
191edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        else:
192edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra            self._out.write('/>')
193edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
194edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    def endElement(self, tag):
1957f2392eeb03eeb88f2699061f4adaeb1fcbd1de2Robert Craig        if tag == ReplaceTags.POLICY_TAG:
1967f2392eeb03eeb88f2699061f4adaeb1fcbd1de2Robert Craig            return
1977f2392eeb03eeb88f2699061f4adaeb1fcbd1de2Robert Craig
198edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        if tag in ReplaceTags.TAGS_WITH_CHILDREN:
199edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra            self._out.write('</%s>' % tag)
200edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
201edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    def characters(self, content):
202edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        if not content.isspace():
203edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra            self._out.write(saxutils.escape(content))
204edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
205edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    def ignorableWhitespace(self, content):
206edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        pass
207edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
208edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    def processingInstruction(self, target, data):
209edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        self._out.write('<?%s %s?>' % (target, data))
210edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
211edf7b4c861144764d0bc17436064d52e7147f916Geremy Condraif __name__ == "__main__":
212edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
213edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    # Intentional double space to line up equls signs and opening " for
214edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    # readability.
2157f2392eeb03eeb88f2699061f4adaeb1fcbd1de2Robert Craig    usage  = "usage: %prog [options] CONFIG_FILE MAC_PERMISSIONS_FILE [MAC_PERMISSIONS_FILE...]\n"
2167f2392eeb03eeb88f2699061f4adaeb1fcbd1de2Robert Craig    usage += "This tool allows one to configure an automatic inclusion\n"
2177f2392eeb03eeb88f2699061f4adaeb1fcbd1de2Robert Craig    usage += "of signing keys into the mac_permision.xml file(s) from the\n"
2187f2392eeb03eeb88f2699061f4adaeb1fcbd1de2Robert Craig    usage += "pem files. If mulitple mac_permision.xml files are included\n"
2197f2392eeb03eeb88f2699061f4adaeb1fcbd1de2Robert Craig    usage += "then they are unioned to produce a final version."
220edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
221edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    version = "%prog " + str(__VERSION)
222edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
223edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    parser = OptionParser(usage=usage, version=version)
224edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
225edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    parser.add_option("-v", "--verbose",
226edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                      action="store_true", dest="verbose", default=False,
227edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                      help="Print internal operations to stdout")
228edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
229edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    parser.add_option("-o", "--output", default="stdout", dest="output_file",
230edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                      metavar="FILE", help="Specify an output file, default is stdout")
231edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
232edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    parser.add_option("-c", "--cwd", default=os.getcwd(), dest="root",
233edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                      metavar="DIR", help="Specify a root (CWD) directory to run this from, it" \
234edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                                          "chdirs' AFTER loading the config file")
235edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
236edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    parser.add_option("-t", "--target-build-variant", default="eng", dest="target_build_variant",
237edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra                      help="Specify the TARGET_BUILD_VARIANT, defaults to eng")
238edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
239020b5ff6311044ef7a2200dd4db69f5cccf46213Geremy Condra    parser.add_option("-d", "--key-directory", default="", dest="key_directory",
240020b5ff6311044ef7a2200dd4db69f5cccf46213Geremy Condra                      help="Specify a parent directory for keys")
241020b5ff6311044ef7a2200dd4db69f5cccf46213Geremy Condra
242edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    (options, args) = parser.parse_args()
243edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
2447f2392eeb03eeb88f2699061f4adaeb1fcbd1de2Robert Craig    if len(args) < 2:
2457f2392eeb03eeb88f2699061f4adaeb1fcbd1de2Robert Craig        parser.error("Must specify a config file (keys.conf) AND mac_permissions.xml file(s)!")
246edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
247edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    logging.basicConfig(level=logging.INFO if options.verbose == True else logging.WARN)
248edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
249edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    # Read the config file
250edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    config = ParseConfig()
251edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    config.read(args[0])
252edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
253edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    os.chdir(options.root)
254edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
255edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    output_file = sys.stdout if options.output_file == "stdout" else open(options.output_file, "w")
256edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    logging.info("Setting output file to: " + options.output_file)
257edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra
258edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    # Generate the key list
259020b5ff6311044ef7a2200dd4db69f5cccf46213Geremy Condra    key_map = config.generateKeyMap(options.target_build_variant.lower(), options.key_directory)
260edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    logging.info("Generate key map:")
261edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    for k in key_map:
262edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra        logging.info(k + " : " + str(key_map[k]))
263edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    # Generate the XML file with markup replaced with keys
264edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    parser = make_parser()
265edf7b4c861144764d0bc17436064d52e7147f916Geremy Condra    parser.setContentHandler(ReplaceTags(key_map, output_file))
2667f2392eeb03eeb88f2699061f4adaeb1fcbd1de2Robert Craig    for f in args[1:]:
2677f2392eeb03eeb88f2699061f4adaeb1fcbd1de2Robert Craig        parser.parse(f)
268