OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 '''python %prog [options] platform chromium_os_flag template | 6 '''python %prog [options] platform chromium_os_flag template |
7 | 7 |
8 platform specifies which platform source is being generated for | 8 platform specifies which platform source is being generated for |
9 and can be one of (win, mac, linux) | 9 and can be one of (win, mac, linux) |
10 chromium_os_flag should be 1 if this is a Chromium OS build | 10 chromium_os_flag should be 1 if this is a Chromium OS build |
11 template is the path to a .json policy template file.''' | 11 template is the path to a .json policy template file.''' |
12 | 12 |
13 from __future__ import with_statement | 13 from __future__ import with_statement |
14 from functools import partial | 14 from functools import partial |
15 import json | 15 import json |
16 from optparse import OptionParser | 16 from optparse import OptionParser |
17 import re | 17 import re |
18 import sys | 18 import sys |
19 import textwrap | 19 import textwrap |
20 import types | 20 import types |
21 from xml.sax.saxutils import escape as xml_escape | |
22 | 21 |
23 | 22 |
24 CHROME_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Google\\\\Chrome' | 23 CHROME_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Google\\\\Chrome' |
25 CHROMIUM_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Chromium' | 24 CHROMIUM_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Chromium' |
26 | 25 |
27 | 26 |
28 class PolicyDetails: | 27 class PolicyDetails: |
29 """Parses a policy template and caches all its details.""" | 28 """Parses a policy template and caches all its details.""" |
30 | 29 |
31 # Maps policy types to a tuple with 5 other types: | 30 # Maps policy types to a tuple with 3 other types: |
32 # - the equivalent base::Value::Type or 'TYPE_EXTERNAL' if the policy | 31 # - the equivalent base::Value::Type or 'TYPE_EXTERNAL' if the policy |
33 # references external data | 32 # references external data |
34 # - the equivalent Protobuf field type | 33 # - the equivalent Protobuf field type |
35 # - the name of one of the protobufs for shared policy types | 34 # - the name of one of the protobufs for shared policy types |
36 # - the equivalent type in Android's App Restriction Schema | |
37 # - whether the equivalent app restriction type needs supporting resources | |
38 # TODO(joaodasilva): refactor the 'dict' type into a more generic 'json' type | 35 # TODO(joaodasilva): refactor the 'dict' type into a more generic 'json' type |
39 # that can also be used to represent lists of other JSON objects. | 36 # that can also be used to represent lists of other JSON objects. |
40 TYPE_MAP = { | 37 TYPE_MAP = { |
41 'dict': ('TYPE_DICTIONARY', 'string', 'String', | 38 'dict': ('TYPE_DICTIONARY', 'string', 'String'), |
42 'string', False), | 39 'external': ('TYPE_EXTERNAL', 'string', 'String'), |
43 'external': ('TYPE_EXTERNAL', 'string', 'String', | 40 'int': ('TYPE_INTEGER', 'int64', 'Integer'), |
44 'invalid', False), | 41 'int-enum': ('TYPE_INTEGER', 'int64', 'Integer'), |
45 'int': ('TYPE_INTEGER', 'int64', 'Integer', | 42 'list': ('TYPE_LIST', 'StringList', 'StringList'), |
46 'integer', False), | 43 'main': ('TYPE_BOOLEAN', 'bool', 'Boolean'), |
47 'int-enum': ('TYPE_INTEGER', 'int64', 'Integer', | 44 'string': ('TYPE_STRING', 'string', 'String'), |
48 'choice', True), | 45 'string-enum': ('TYPE_STRING', 'string', 'String'), |
49 'list': ('TYPE_LIST', 'StringList', 'StringList', | 46 'string-enum-list': ('TYPE_LIST', 'StringList', 'StringList'), |
50 'string', False), | |
51 'main': ('TYPE_BOOLEAN', 'bool', 'Boolean', | |
52 'bool', False), | |
53 'string': ('TYPE_STRING', 'string', 'String', | |
54 'string', False), | |
55 'string-enum': ('TYPE_STRING', 'string', 'String', | |
56 'choice', True), | |
57 'string-enum-list': ('TYPE_LIST', 'StringList', 'StringList', | |
58 'multi-select', True), | |
59 } | 47 } |
60 | 48 |
61 class EnumItem: | 49 class EnumItem: |
62 def __init__(self, item): | 50 def __init__(self, item): |
63 self.caption = PolicyDetails._RemovePlaceholders(item['caption']) | 51 self.caption = PolicyDetails._RemovePlaceholders(item['caption']) |
64 self.value = item['value'] | 52 self.value = item['value'] |
65 | 53 |
66 def __init__(self, policy, os, is_chromium_os): | 54 def __init__(self, policy, os, is_chromium_os): |
67 self.id = policy['id'] | 55 self.id = policy['id'] |
68 self.name = policy['name'] | 56 self.name = policy['name'] |
(...skipping 21 matching lines...) Expand all Loading... |
90 self.platforms.append(platform_sub) | 78 self.platforms.append(platform_sub) |
91 else: | 79 else: |
92 self.platforms.append(platform) | 80 self.platforms.append(platform) |
93 | 81 |
94 self.platforms.sort() | 82 self.platforms.sort() |
95 self.is_supported = expected_platform in self.platforms | 83 self.is_supported = expected_platform in self.platforms |
96 | 84 |
97 if not PolicyDetails.TYPE_MAP.has_key(policy['type']): | 85 if not PolicyDetails.TYPE_MAP.has_key(policy['type']): |
98 raise NotImplementedError('Unknown policy type for %s: %s' % | 86 raise NotImplementedError('Unknown policy type for %s: %s' % |
99 (policy['name'], policy['type'])) | 87 (policy['name'], policy['type'])) |
100 self.policy_type, self.protobuf_type, self.policy_protobuf_type, \ | 88 self.policy_type, self.protobuf_type, self.policy_protobuf_type = \ |
101 self.restriction_type, self.has_restriction_resources = \ | 89 PolicyDetails.TYPE_MAP[policy['type']] |
102 PolicyDetails.TYPE_MAP[policy['type']] | |
103 self.schema = policy['schema'] | 90 self.schema = policy['schema'] |
104 | 91 |
105 self.desc = '\n'.join( | 92 self.desc = '\n'.join( |
106 map(str.strip, | 93 map(str.strip, |
107 PolicyDetails._RemovePlaceholders(policy['desc']).splitlines())) | 94 PolicyDetails._RemovePlaceholders(policy['desc']).splitlines())) |
108 self.caption = PolicyDetails._RemovePlaceholders(policy['caption']) | 95 self.caption = PolicyDetails._RemovePlaceholders(policy['caption']) |
109 self.max_size = policy.get('max_size', 0) | 96 self.max_size = policy.get('max_size', 0) |
110 | 97 |
111 items = policy.get('items') | 98 items = policy.get('items') |
112 if items is None: | 99 if items is None: |
(...skipping 29 matching lines...) Expand all Loading... |
142 help='generate cloud policy protobuf file', | 129 help='generate cloud policy protobuf file', |
143 metavar='FILE') | 130 metavar='FILE') |
144 parser.add_option('--csp', '--chrome-settings-protobuf', | 131 parser.add_option('--csp', '--chrome-settings-protobuf', |
145 dest='chrome_settings_proto_path', | 132 dest='chrome_settings_proto_path', |
146 help='generate chrome settings protobuf file', | 133 help='generate chrome settings protobuf file', |
147 metavar='FILE') | 134 metavar='FILE') |
148 parser.add_option('--cpd', '--cloud-policy-decoder', | 135 parser.add_option('--cpd', '--cloud-policy-decoder', |
149 dest='cloud_policy_decoder_path', | 136 dest='cloud_policy_decoder_path', |
150 help='generate C++ code decoding the cloud policy protobuf', | 137 help='generate C++ code decoding the cloud policy protobuf', |
151 metavar='FILE') | 138 metavar='FILE') |
152 parser.add_option('--ard', '--app-restrictions-definition', | |
153 dest='app_restrictions_path', | |
154 help='generate an XML file as specified by ' | |
155 'Android\'s App Restriction Schema', | |
156 metavar='FILE') | |
157 parser.add_option('--arr', '--app-restrictions-resources', | |
158 dest='app_resources_path', | |
159 help='generate an XML file with resources supporting the ' | |
160 'restrictions defined in --app-restrictions-definition ' | |
161 'parameter', | |
162 metavar='FILE') | |
163 | 139 |
164 (opts, args) = parser.parse_args() | 140 (opts, args) = parser.parse_args() |
165 | 141 |
166 if len(args) != 3: | 142 if len(args) != 3: |
167 print 'exactly platform, chromium_os flag and input file must be specified.' | 143 print 'exactly platform, chromium_os flag and input file must be specified.' |
168 parser.print_help() | 144 parser.print_help() |
169 return 2 | 145 return 2 |
170 | 146 |
171 os = args[0] | 147 os = args[0] |
172 is_chromium_os = args[1] == '1' | 148 is_chromium_os = args[1] == '1' |
173 template_file_name = args[2] | 149 template_file_name = args[2] |
174 | 150 |
175 template_file_contents = _LoadJSONFile(template_file_name) | 151 template_file_contents = _LoadJSONFile(template_file_name) |
176 policy_details = [ PolicyDetails(policy, os, is_chromium_os) | 152 policy_details = [ PolicyDetails(policy, os, is_chromium_os) |
177 for policy in _Flatten(template_file_contents) ] | 153 for policy in _Flatten(template_file_contents) ] |
178 sorted_policy_details = sorted(policy_details, key=lambda policy: policy.name) | 154 sorted_policy_details = sorted(policy_details, key=lambda policy: policy.name) |
179 | 155 |
180 def GenerateFile(path, writer, sorted=False, xml=False): | 156 def GenerateFile(path, writer, sorted=False): |
181 if path: | 157 if path: |
182 with open(path, 'w') as f: | 158 with open(path, 'w') as f: |
183 _OutputGeneratedWarningHeader(f, template_file_name, xml) | 159 _OutputGeneratedWarningHeader(f, template_file_name) |
184 writer(sorted and sorted_policy_details or policy_details, os, f) | 160 writer(sorted and sorted_policy_details or policy_details, os, f) |
185 | 161 |
186 GenerateFile(opts.header_path, _WritePolicyConstantHeader, sorted=True) | 162 GenerateFile(opts.header_path, _WritePolicyConstantHeader, sorted=True) |
187 GenerateFile(opts.source_path, _WritePolicyConstantSource, sorted=True) | 163 GenerateFile(opts.source_path, _WritePolicyConstantSource, sorted=True) |
188 GenerateFile(opts.cloud_policy_proto_path, _WriteCloudPolicyProtobuf) | 164 GenerateFile(opts.cloud_policy_proto_path, _WriteCloudPolicyProtobuf) |
189 GenerateFile(opts.chrome_settings_proto_path, _WriteChromeSettingsProtobuf) | 165 GenerateFile(opts.chrome_settings_proto_path, _WriteChromeSettingsProtobuf) |
190 GenerateFile(opts.cloud_policy_decoder_path, _WriteCloudPolicyDecoder) | 166 GenerateFile(opts.cloud_policy_decoder_path, _WriteCloudPolicyDecoder) |
191 | 167 |
192 if os == 'android': | |
193 GenerateFile(opts.app_restrictions_path, _WriteAppRestrictions, xml=True) | |
194 GenerateFile(opts.app_resources_path, _WriteResourcesForPolicies, xml=True) | |
195 | |
196 return 0 | 168 return 0 |
197 | 169 |
198 | 170 |
199 #------------------ shared helpers ---------------------------------# | 171 #------------------ shared helpers ---------------------------------# |
200 | 172 |
201 def _OutputGeneratedWarningHeader(f, template_file_path, xml_style): | 173 def _OutputGeneratedWarningHeader(f, template_file_path): |
202 left_margin = '//' | 174 f.write('//\n' |
203 if xml_style: | 175 '// DO NOT MODIFY THIS FILE DIRECTLY!\n' |
204 left_margin = ' ' | 176 '// IT IS GENERATED BY generate_policy_source.py\n' |
205 f.write('<?xml version="1.0" encoding="utf-8"?>\n' | 177 '// FROM ' + template_file_path + '\n' |
206 '<!--\n') | 178 '//\n\n') |
207 else: | |
208 f.write('//\n') | |
209 | |
210 f.write(left_margin + ' DO NOT MODIFY THIS FILE DIRECTLY!\n') | |
211 f.write(left_margin + ' IT IS GENERATED BY generate_policy_source.py\n') | |
212 f.write(left_margin + ' FROM ' + template_file_path + '\n') | |
213 | |
214 if xml_style: | |
215 f.write('-->\n\n') | |
216 else: | |
217 f.write(left_margin + '\n\n') | |
218 | 179 |
219 | 180 |
220 COMMENT_WRAPPER = textwrap.TextWrapper() | 181 COMMENT_WRAPPER = textwrap.TextWrapper() |
221 COMMENT_WRAPPER.width = 80 | 182 COMMENT_WRAPPER.width = 80 |
222 COMMENT_WRAPPER.initial_indent = '// ' | 183 COMMENT_WRAPPER.initial_indent = '// ' |
223 COMMENT_WRAPPER.subsequent_indent = '// ' | 184 COMMENT_WRAPPER.subsequent_indent = '// ' |
224 COMMENT_WRAPPER.replace_whitespace = False | 185 COMMENT_WRAPPER.replace_whitespace = False |
225 | 186 |
226 | 187 |
227 # Writes a comment, each line prefixed by // and wrapped to 80 spaces. | 188 # Writes a comment, each line prefixed by // and wrapped to 80 spaces. |
(...skipping 365 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
593 f.write('};\n\n') | 554 f.write('};\n\n') |
594 | 555 |
595 def GetByID(self, id_str): | 556 def GetByID(self, id_str): |
596 if not isinstance(id_str, types.StringTypes): | 557 if not isinstance(id_str, types.StringTypes): |
597 return id_str | 558 return id_str |
598 if not self.id_map.has_key(id_str): | 559 if not self.id_map.has_key(id_str): |
599 raise RuntimeError('Invalid $ref: ' + id_str) | 560 raise RuntimeError('Invalid $ref: ' + id_str) |
600 return self.id_map[id_str] | 561 return self.id_map[id_str] |
601 | 562 |
602 def ResolveID(self, index, params): | 563 def ResolveID(self, index, params): |
603 return params[:index] + (self.GetByID(params[index]),) + params[index + 1:] | 564 return params[:index] + (self.GetByID(params[index]),) + params[index+1:] |
604 | 565 |
605 def ResolveReferences(self): | 566 def ResolveReferences(self): |
606 """Resolve reference mapping, required to be called after Generate() | 567 """Resolve reference mapping, required to be called after Generate() |
607 | 568 |
608 After calling Generate(), the type of indices used in schema structures | 569 After calling Generate(), the type of indices used in schema structures |
609 might be either int or string. An int type suggests that it's a resolved | 570 might be either int or string. An int type suggests that it's a resolved |
610 index, but for string type it's unresolved. Resolving a reference is as | 571 index, but for string type it's unresolved. Resolving a reference is as |
611 simple as looking up for corresponding ID in self.id_map, and replace the | 572 simple as looking up for corresponding ID in self.id_map, and replace the |
612 old index with the mapped index. | 573 old index with the mapped index. |
613 """ | 574 """ |
(...skipping 386 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1000 | 961 |
1001 | 962 |
1002 def _WriteCloudPolicyDecoder(policies, os, f): | 963 def _WriteCloudPolicyDecoder(policies, os, f): |
1003 f.write(CPP_HEAD) | 964 f.write(CPP_HEAD) |
1004 for policy in policies: | 965 for policy in policies: |
1005 if policy.is_supported and not policy.is_device_only: | 966 if policy.is_supported and not policy.is_device_only: |
1006 _WritePolicyCode(f, policy) | 967 _WritePolicyCode(f, policy) |
1007 f.write(CPP_FOOT) | 968 f.write(CPP_FOOT) |
1008 | 969 |
1009 | 970 |
1010 def _EscapeResourceString(raw_resource): | |
1011 if type(raw_resource) == int: | |
1012 return raw_resource | |
1013 return xml_escape(raw_resource)\ | |
1014 .replace('\\', '\\\\')\ | |
1015 .replace('\"','\\\"')\ | |
1016 .replace('\'','\\\'') | |
1017 | |
1018 def _WriteAppRestrictions(policies, os, f): | |
1019 | |
1020 def WriteRestrictionCommon(key): | |
1021 f.write(' <restriction\n' | |
1022 ' android:key="%s"\n' % key) | |
1023 f.write(' android:title="@string/%sTitle"\n' % key) | |
1024 f.write(' android:description="@string/%sDesc"\n' % key) | |
1025 | |
1026 def WriteItemsDefinition(key): | |
1027 f.write(' android:entries="@array/%sEntries"\n' % key) | |
1028 f.write(' android:entryValues="@array/%sValues"\n' % key) | |
1029 | |
1030 def WriteAppRestriction(policy): | |
1031 policy_name = policy.name | |
1032 WriteRestrictionCommon(policy_name) | |
1033 | |
1034 if policy.has_restriction_resources: | |
1035 WriteItemsDefinition(policy_name) | |
1036 | |
1037 f.write(' android:restrictionType="%s"/>' % policy.restriction_type) | |
1038 f.write('\n\n') | |
1039 | |
1040 # _WriteAppRestrictions body | |
1041 f.write('<restrictions xmlns:android="' | |
1042 'http://schemas.android.com/apk/res/android">\n\n') | |
1043 for policy in policies: | |
1044 if policy.is_supported and policy.restriction_type != 'invalid': | |
1045 WriteAppRestriction(policy) | |
1046 f.write('</restrictions>') | |
1047 | |
1048 | |
1049 def _WriteResourcesForPolicies(policies, os, f): | |
1050 | |
1051 # TODO(knn): Update this to support i18n. | |
1052 def WriteString(key, value): | |
1053 f.write(' <string name="%s">%s</string>\n' | |
1054 % (key, _EscapeResourceString(value))) | |
1055 | |
1056 def WriteItems(key, items): | |
1057 if items: | |
1058 f.write(' <string-array name="%sEntries">\n' % key) | |
1059 for item in items: | |
1060 f.write(' <item>%s</item>\n' % | |
1061 _EscapeResourceString(item.caption)) | |
1062 f.write(' </string-array>\n') | |
1063 f.write(' <string-array name="%sValues">\n' % key) | |
1064 for item in items: | |
1065 f.write(' <item>%s</item>\n' % _EscapeResourceString(item.value)) | |
1066 f.write(' </string-array>\n') | |
1067 | |
1068 def WriteResourceForPolicy(policy): | |
1069 policy_name = policy.name | |
1070 WriteString(policy_name + 'Title', policy.caption) | |
1071 | |
1072 # Get the first line of the policy description. | |
1073 description = policy.desc.split('\n', 1)[0] | |
1074 WriteString(policy_name + 'Desc', description) | |
1075 | |
1076 if policy.has_restriction_resources: | |
1077 WriteItems(policy_name, policy.items) | |
1078 | |
1079 # _WriteResourcesForPolicies body | |
1080 f.write('<resources>\n\n') | |
1081 for policy in policies: | |
1082 if policy.is_supported and policy.restriction_type != 'invalid': | |
1083 WriteResourceForPolicy(policy) | |
1084 f.write('</resources>') | |
1085 | |
1086 | |
1087 if __name__ == '__main__': | 971 if __name__ == '__main__': |
1088 sys.exit(main()) | 972 sys.exit(main()) |
OLD | NEW |