OLD | NEW |
| (Empty) |
1 #!/usr/bin/env python | |
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 | |
4 # found in the LICENSE file. | |
5 | |
6 '''python %prog [options] platform chromium_os_flag template | |
7 | |
8 platform specifies which platform source is being generated for | |
9 and can be one of (win, mac, linux) | |
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.''' | |
12 | |
13 from __future__ import with_statement | |
14 import json | |
15 from optparse import OptionParser | |
16 import re | |
17 import sys | |
18 import textwrap | |
19 | |
20 | |
21 CHROME_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Google\\\\Chrome' | |
22 CHROMIUM_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Chromium' | |
23 | |
24 | |
25 class PolicyDetails: | |
26 """Parses a policy template and caches all its details.""" | |
27 | |
28 # Maps policy types to a tuple with 3 other types: | |
29 # - the equivalent base::Value::Type or 'TYPE_EXTERNAL' if the policy | |
30 # references external data | |
31 # - the equivalent Protobuf field type | |
32 # - the name of one of the protobufs for shared policy types | |
33 # TODO(joaodasilva): refactor the 'dict' type into a more generic 'json' type | |
34 # that can also be used to represent lists of other JSON objects. | |
35 TYPE_MAP = { | |
36 'dict': ('TYPE_DICTIONARY', 'string', 'String'), | |
37 'external': ('TYPE_EXTERNAL', 'string', 'String'), | |
38 'int': ('TYPE_INTEGER', 'int64', 'Integer'), | |
39 'int-enum': ('TYPE_INTEGER', 'int64', 'Integer'), | |
40 'list': ('TYPE_LIST', 'StringList', 'StringList'), | |
41 'main': ('TYPE_BOOLEAN', 'bool', 'Boolean'), | |
42 'string': ('TYPE_STRING', 'string', 'String'), | |
43 'string-enum': ('TYPE_STRING', 'string', 'String'), | |
44 } | |
45 | |
46 class EnumItem: | |
47 def __init__(self, item): | |
48 self.caption = PolicyDetails._RemovePlaceholders(item['caption']) | |
49 self.value = item['value'] | |
50 | |
51 def __init__(self, policy, os, is_chromium_os): | |
52 self.id = policy['id'] | |
53 self.name = policy['name'] | |
54 self.is_deprecated = policy.get('deprecated', False) | |
55 self.is_device_only = policy.get('device_only', False) | |
56 self.schema = policy.get('schema', {}) | |
57 | |
58 expected_platform = 'chrome_os' if is_chromium_os else os.lower() | |
59 self.platforms = [] | |
60 for platform, version in [ p.split(':') for p in policy['supported_on'] ]: | |
61 if not version.endswith('-'): | |
62 continue | |
63 | |
64 if platform.startswith('chrome.'): | |
65 platform_sub = platform[7:] | |
66 if platform_sub == '*': | |
67 self.platforms.extend(['win', 'mac', 'linux']) | |
68 else: | |
69 self.platforms.append(platform_sub) | |
70 else: | |
71 self.platforms.append(platform) | |
72 | |
73 self.platforms.sort() | |
74 self.is_supported = expected_platform in self.platforms | |
75 | |
76 if not PolicyDetails.TYPE_MAP.has_key(policy['type']): | |
77 raise NotImplementedError('Unknown policy type for %s: %s' % | |
78 (policy['name'], policy['type'])) | |
79 self.policy_type, self.protobuf_type, self.policy_protobuf_type = \ | |
80 PolicyDetails.TYPE_MAP[policy['type']] | |
81 self.schema = policy['schema'] | |
82 | |
83 self.desc = '\n'.join( | |
84 map(str.strip, | |
85 PolicyDetails._RemovePlaceholders(policy['desc']).splitlines())) | |
86 self.caption = PolicyDetails._RemovePlaceholders(policy['caption']) | |
87 self.max_size = policy.get('max_size', 0) | |
88 | |
89 items = policy.get('items') | |
90 if items is None: | |
91 self.items = None | |
92 else: | |
93 self.items = [ PolicyDetails.EnumItem(entry) for entry in items ] | |
94 | |
95 PH_PATTERN = re.compile('<ph[^>]*>([^<]*|[^<]*<ex>([^<]*)</ex>[^<]*)</ph>') | |
96 | |
97 # Simplistic grit placeholder stripper. | |
98 @staticmethod | |
99 def _RemovePlaceholders(text): | |
100 result = '' | |
101 pos = 0 | |
102 for m in PolicyDetails.PH_PATTERN.finditer(text): | |
103 result += text[pos:m.start(0)] | |
104 result += m.group(2) or m.group(1) | |
105 pos = m.end(0) | |
106 result += text[pos:] | |
107 return result | |
108 | |
109 | |
110 def main(): | |
111 parser = OptionParser(usage=__doc__) | |
112 parser.add_option('--pch', '--policy-constants-header', dest='header_path', | |
113 help='generate header file of policy constants', | |
114 metavar='FILE') | |
115 parser.add_option('--pcc', '--policy-constants-source', dest='source_path', | |
116 help='generate source file of policy constants', | |
117 metavar='FILE') | |
118 parser.add_option('--cpp', '--cloud-policy-protobuf', | |
119 dest='cloud_policy_proto_path', | |
120 help='generate cloud policy protobuf file', | |
121 metavar='FILE') | |
122 parser.add_option('--csp', '--chrome-settings-protobuf', | |
123 dest='chrome_settings_proto_path', | |
124 help='generate chrome settings protobuf file', | |
125 metavar='FILE') | |
126 parser.add_option('--cpd', '--cloud-policy-decoder', | |
127 dest='cloud_policy_decoder_path', | |
128 help='generate C++ code decoding the cloud policy protobuf', | |
129 metavar='FILE') | |
130 | |
131 (opts, args) = parser.parse_args() | |
132 | |
133 if len(args) != 3: | |
134 print 'exactly platform, chromium_os flag and input file must be specified.' | |
135 parser.print_help() | |
136 return 2 | |
137 | |
138 os = args[0] | |
139 is_chromium_os = args[1] == '1' | |
140 template_file_name = args[2] | |
141 | |
142 template_file_contents = _LoadJSONFile(template_file_name) | |
143 policy_details = [ PolicyDetails(policy, os, is_chromium_os) | |
144 for policy in _Flatten(template_file_contents) ] | |
145 sorted_policy_details = sorted(policy_details, key=lambda policy: policy.name) | |
146 | |
147 def GenerateFile(path, writer, sorted=False): | |
148 if path: | |
149 with open(path, 'w') as f: | |
150 _OutputGeneratedWarningHeader(f, template_file_name) | |
151 writer(sorted and sorted_policy_details or policy_details, os, f) | |
152 | |
153 GenerateFile(opts.header_path, _WritePolicyConstantHeader, sorted=True) | |
154 GenerateFile(opts.source_path, _WritePolicyConstantSource, sorted=True) | |
155 GenerateFile(opts.cloud_policy_proto_path, _WriteCloudPolicyProtobuf) | |
156 GenerateFile(opts.chrome_settings_proto_path, _WriteChromeSettingsProtobuf) | |
157 GenerateFile(opts.cloud_policy_decoder_path, _WriteCloudPolicyDecoder) | |
158 | |
159 return 0 | |
160 | |
161 | |
162 #------------------ shared helpers ---------------------------------# | |
163 | |
164 def _OutputGeneratedWarningHeader(f, template_file_path): | |
165 f.write('//\n' | |
166 '// DO NOT MODIFY THIS FILE DIRECTLY!\n' | |
167 '// IT IS GENERATED BY generate_policy_source.py\n' | |
168 '// FROM ' + template_file_path + '\n' | |
169 '//\n\n') | |
170 | |
171 | |
172 COMMENT_WRAPPER = textwrap.TextWrapper() | |
173 COMMENT_WRAPPER.width = 80 | |
174 COMMENT_WRAPPER.initial_indent = '// ' | |
175 COMMENT_WRAPPER.subsequent_indent = '// ' | |
176 COMMENT_WRAPPER.replace_whitespace = False | |
177 | |
178 | |
179 # Writes a comment, each line prefixed by // and wrapped to 80 spaces. | |
180 def _OutputComment(f, comment): | |
181 for line in comment.splitlines(): | |
182 if len(line) == 0: | |
183 f.write('//') | |
184 else: | |
185 f.write(COMMENT_WRAPPER.fill(line)) | |
186 f.write('\n') | |
187 | |
188 | |
189 # Returns an iterator over all the policies in |template_file_contents|. | |
190 def _Flatten(template_file_contents): | |
191 for policy in template_file_contents['policy_definitions']: | |
192 if policy['type'] == 'group': | |
193 for sub_policy in policy['policies']: | |
194 yield sub_policy | |
195 else: | |
196 yield policy | |
197 | |
198 | |
199 def _LoadJSONFile(json_file): | |
200 with open(json_file, 'r') as f: | |
201 text = f.read() | |
202 return eval(text) | |
203 | |
204 | |
205 #------------------ policy constants header ------------------------# | |
206 | |
207 def _WritePolicyConstantHeader(policies, os, f): | |
208 f.write('#ifndef CHROME_COMMON_POLICY_CONSTANTS_H_\n' | |
209 '#define CHROME_COMMON_POLICY_CONSTANTS_H_\n' | |
210 '\n' | |
211 '#include <string>\n' | |
212 '\n' | |
213 '#include "base/basictypes.h"\n' | |
214 '#include "base/values.h"\n' | |
215 '#include "components/policy/core/common/policy_details.h"\n' | |
216 '\n' | |
217 'namespace policy {\n' | |
218 '\n' | |
219 'namespace internal {\n' | |
220 'struct SchemaData;\n' | |
221 '}\n\n') | |
222 | |
223 if os == 'win': | |
224 f.write('// The windows registry path where Chrome policy ' | |
225 'configuration resides.\n' | |
226 'extern const wchar_t kRegistryChromePolicyKey[];\n') | |
227 | |
228 f.write('// Returns the PolicyDetails for |policy| if |policy| is a known\n' | |
229 '// Chrome policy, otherwise returns NULL.\n' | |
230 'const PolicyDetails* GetChromePolicyDetails(' | |
231 'const std::string& policy);\n' | |
232 '\n' | |
233 '// Returns the schema data of the Chrome policy schema.\n' | |
234 'const internal::SchemaData* GetChromeSchemaData();\n' | |
235 '\n') | |
236 f.write('// Key names for the policy settings.\n' | |
237 'namespace key {\n\n') | |
238 for policy in policies: | |
239 # TODO(joaodasilva): Include only supported policies in | |
240 # configuration_policy_handler.cc and configuration_policy_handler_list.cc | |
241 # so that these names can be conditional on 'policy.is_supported'. | |
242 # http://crbug.com/223616 | |
243 f.write('extern const char k' + policy.name + '[];\n') | |
244 f.write('\n} // namespace key\n\n' | |
245 '} // namespace policy\n\n' | |
246 '#endif // CHROME_COMMON_POLICY_CONSTANTS_H_\n') | |
247 | |
248 | |
249 #------------------ policy constants source ------------------------# | |
250 | |
251 # A mapping of the simple schema types to base::Value::Types. | |
252 SIMPLE_SCHEMA_NAME_MAP = { | |
253 'boolean': 'TYPE_BOOLEAN', | |
254 'integer': 'TYPE_INTEGER', | |
255 'null' : 'TYPE_NULL', | |
256 'number' : 'TYPE_DOUBLE', | |
257 'string' : 'TYPE_STRING', | |
258 } | |
259 | |
260 | |
261 class SchemaNodesGenerator: | |
262 """Builds the internal structs to represent a JSON schema.""" | |
263 | |
264 def __init__(self, shared_strings): | |
265 """Creates a new generator. | |
266 | |
267 |shared_strings| is a map of strings to a C expression that evaluates to | |
268 that string at runtime. This mapping can be used to reuse existing string | |
269 constants.""" | |
270 self.shared_strings = shared_strings | |
271 self.schema_nodes = [] | |
272 self.property_nodes = [] | |
273 self.properties_nodes = [] | |
274 self.simple_types = { | |
275 'boolean': None, | |
276 'integer': None, | |
277 'null': None, | |
278 'number': None, | |
279 'string': None, | |
280 } | |
281 self.stringlist_type = None | |
282 | |
283 def GetString(self, s): | |
284 return self.shared_strings[s] if s in self.shared_strings else '"%s"' % s | |
285 | |
286 def AppendSchema(self, type, extra, comment=''): | |
287 index = len(self.schema_nodes) | |
288 self.schema_nodes.append((type, extra, comment)) | |
289 return index | |
290 | |
291 def GetSimpleType(self, name): | |
292 if self.simple_types[name] == None: | |
293 self.simple_types[name] = self.AppendSchema( | |
294 SIMPLE_SCHEMA_NAME_MAP[name], | |
295 -1, | |
296 'simple type: ' + name) | |
297 return self.simple_types[name] | |
298 | |
299 def GetStringList(self): | |
300 if self.stringlist_type == None: | |
301 self.stringlist_type = self.AppendSchema( | |
302 'TYPE_LIST', | |
303 self.GetSimpleType('string'), | |
304 'simple type: stringlist') | |
305 return self.stringlist_type | |
306 | |
307 def Generate(self, schema, name): | |
308 """Generates the structs for the given schema. | |
309 | |
310 |schema|: a valid JSON schema in a dictionary. | |
311 |name|: the name of the current node, for the generated comments.""" | |
312 # Simple types use shared nodes. | |
313 if schema['type'] in self.simple_types: | |
314 return self.GetSimpleType(schema['type']) | |
315 | |
316 if schema['type'] == 'array': | |
317 # Special case for lists of strings, which is a common policy type. | |
318 if schema['items']['type'] == 'string': | |
319 return self.GetStringList() | |
320 return self.AppendSchema( | |
321 'TYPE_LIST', | |
322 self.Generate(schema['items'], 'items of ' + name)) | |
323 elif schema['type'] == 'object': | |
324 # Reserve an index first, so that dictionaries come before their | |
325 # properties. This makes sure that the root node is the first in the | |
326 # SchemaNodes array. | |
327 index = self.AppendSchema('TYPE_DICTIONARY', -1) | |
328 | |
329 if 'additionalProperties' in schema: | |
330 additionalProperties = self.Generate( | |
331 schema['additionalProperties'], | |
332 'additionalProperties of ' + name) | |
333 else: | |
334 additionalProperties = -1 | |
335 | |
336 # Properties must be sorted by name, for the binary search lookup. | |
337 # Note that |properties| must be evaluated immediately, so that all the | |
338 # recursive calls to Generate() append the necessary child nodes; if | |
339 # |properties| were a generator then this wouldn't work. | |
340 sorted_properties = sorted(schema.get('properties', {}).items()) | |
341 properties = [ (self.GetString(key), self.Generate(schema, key)) | |
342 for key, schema in sorted_properties ] | |
343 begin = len(self.property_nodes) | |
344 self.property_nodes += properties | |
345 end = len(self.property_nodes) | |
346 if index == 0: | |
347 self.root_properties_begin = begin | |
348 self.root_properties_end = end | |
349 | |
350 extra = len(self.properties_nodes) | |
351 self.properties_nodes.append((begin, end, additionalProperties, name)) | |
352 | |
353 # Set the right data at |index| now. | |
354 self.schema_nodes[index] = ('TYPE_DICTIONARY', extra, name) | |
355 return index | |
356 else: | |
357 assert False | |
358 | |
359 def Write(self, f): | |
360 """Writes the generated structs to the given file. | |
361 | |
362 |f| an open file to write to.""" | |
363 f.write('const internal::SchemaNode kSchemas[] = {\n' | |
364 '// Type Extra\n') | |
365 for type, extra, comment in self.schema_nodes: | |
366 type += ',' | |
367 f.write(' { base::Value::%-18s %3d }, // %s\n' % (type, extra, comment)) | |
368 f.write('};\n\n') | |
369 | |
370 f.write('const internal::PropertyNode kPropertyNodes[] = {\n' | |
371 '// Property #Schema\n') | |
372 for key, schema in self.property_nodes: | |
373 key += ',' | |
374 f.write(' { %-50s %7d },\n' % (key, schema)) | |
375 f.write('};\n\n') | |
376 | |
377 f.write('const internal::PropertiesNode kProperties[] = {\n' | |
378 '// Begin End Additional Properties\n') | |
379 for node in self.properties_nodes: | |
380 f.write(' { %5d, %5d, %5d }, // %s\n' % node) | |
381 f.write('};\n\n') | |
382 | |
383 f.write('const internal::SchemaData kChromeSchemaData = {\n' | |
384 ' kSchemas,\n' | |
385 ' kPropertyNodes,\n' | |
386 ' kProperties,\n' | |
387 '};\n\n') | |
388 | |
389 | |
390 def _WritePolicyConstantSource(policies, os, f): | |
391 f.write('#include "policy/policy_constants.h"\n' | |
392 '\n' | |
393 '#include <algorithm>\n' | |
394 '\n' | |
395 '#include "base/logging.h"\n' | |
396 '#include "components/policy/core/common/schema_internal.h"\n' | |
397 '\n' | |
398 'namespace policy {\n' | |
399 '\n' | |
400 'namespace {\n' | |
401 '\n') | |
402 | |
403 # Generate the Chrome schema. | |
404 chrome_schema = { | |
405 'type': 'object', | |
406 'properties': {}, | |
407 } | |
408 shared_strings = {} | |
409 for policy in policies: | |
410 shared_strings[policy.name] = "key::k%s" % policy.name | |
411 if policy.is_supported: | |
412 chrome_schema['properties'][policy.name] = policy.schema | |
413 | |
414 # Note: this list must be kept in sync with the known property list of the | |
415 # Chrome schema, so that binary seaching in the PropertyNode array gets the | |
416 # right index on this array as well. See the implementation of | |
417 # GetChromePolicyDetails() below. | |
418 f.write('const PolicyDetails kChromePolicyDetails[] = {\n' | |
419 '// is_deprecated is_device_policy id max_external_data_size\n') | |
420 for policy in policies: | |
421 if policy.is_supported: | |
422 f.write(' { %-14s %-16s %3s, %24s },\n' % ( | |
423 'true,' if policy.is_deprecated else 'false,', | |
424 'true,' if policy.is_device_only else 'false,', | |
425 policy.id, | |
426 policy.max_size)) | |
427 f.write('};\n\n') | |
428 | |
429 schema_generator = SchemaNodesGenerator(shared_strings) | |
430 schema_generator.Generate(chrome_schema, 'root node') | |
431 schema_generator.Write(f) | |
432 | |
433 f.write('bool CompareKeys(const internal::PropertyNode& node,\n' | |
434 ' const std::string& key) {\n' | |
435 ' return node.key < key;\n' | |
436 '}\n\n') | |
437 | |
438 f.write('} // namespace\n\n') | |
439 | |
440 if os == 'win': | |
441 f.write('#if defined(GOOGLE_CHROME_BUILD)\n' | |
442 'const wchar_t kRegistryChromePolicyKey[] = ' | |
443 'L"' + CHROME_POLICY_KEY + '";\n' | |
444 '#else\n' | |
445 'const wchar_t kRegistryChromePolicyKey[] = ' | |
446 'L"' + CHROMIUM_POLICY_KEY + '";\n' | |
447 '#endif\n\n') | |
448 | |
449 f.write('const internal::SchemaData* GetChromeSchemaData() {\n' | |
450 ' return &kChromeSchemaData;\n' | |
451 '}\n\n') | |
452 | |
453 f.write('const PolicyDetails* GetChromePolicyDetails(' | |
454 'const std::string& policy) {\n' | |
455 ' // First index in kPropertyNodes of the Chrome policies.\n' | |
456 ' static const int begin_index = %s;\n' | |
457 ' // One-past-the-end of the Chrome policies in kPropertyNodes.\n' | |
458 ' static const int end_index = %s;\n' % | |
459 (schema_generator.root_properties_begin, | |
460 schema_generator.root_properties_end)) | |
461 f.write(' const internal::PropertyNode* begin =\n' | |
462 ' kPropertyNodes + begin_index;\n' | |
463 ' const internal::PropertyNode* end = kPropertyNodes + end_index;\n' | |
464 ' const internal::PropertyNode* it =\n' | |
465 ' std::lower_bound(begin, end, policy, CompareKeys);\n' | |
466 ' if (it == end || it->key != policy)\n' | |
467 ' return NULL;\n' | |
468 ' // This relies on kPropertyNodes from begin_index to end_index\n' | |
469 ' // having exactly the same policies (and in the same order) as\n' | |
470 ' // kChromePolicyDetails, so that binary searching on the first\n' | |
471 ' // gets the same results as a binary search on the second would.\n' | |
472 ' // However, kPropertyNodes has the policy names and\n' | |
473 ' // kChromePolicyDetails doesn\'t, so we obtain the index into\n' | |
474 ' // the second array by searching the first to avoid duplicating\n' | |
475 ' // the policy name pointers.\n' | |
476 ' // Offsetting |it| from |begin| here obtains the index we\'re\n' | |
477 ' // looking for.\n' | |
478 ' size_t index = it - begin;\n' | |
479 ' CHECK_LT(index, arraysize(kChromePolicyDetails));\n' | |
480 ' return kChromePolicyDetails + index;\n' | |
481 '}\n\n') | |
482 | |
483 f.write('namespace key {\n\n') | |
484 for policy in policies: | |
485 # TODO(joaodasilva): Include only supported policies in | |
486 # configuration_policy_handler.cc and configuration_policy_handler_list.cc | |
487 # so that these names can be conditional on 'policy.is_supported'. | |
488 # http://crbug.com/223616 | |
489 f.write('const char k{name}[] = "{name}";\n'.format(name=policy.name)) | |
490 f.write('\n} // namespace key\n\n' | |
491 '} // namespace policy\n') | |
492 | |
493 | |
494 #------------------ policy protobufs --------------------------------# | |
495 | |
496 CHROME_SETTINGS_PROTO_HEAD = ''' | |
497 syntax = "proto2"; | |
498 | |
499 option optimize_for = LITE_RUNTIME; | |
500 | |
501 package enterprise_management; | |
502 | |
503 // For StringList and PolicyOptions. | |
504 import "cloud_policy.proto"; | |
505 | |
506 ''' | |
507 | |
508 | |
509 CLOUD_POLICY_PROTO_HEAD = ''' | |
510 syntax = "proto2"; | |
511 | |
512 option optimize_for = LITE_RUNTIME; | |
513 | |
514 package enterprise_management; | |
515 | |
516 message StringList { | |
517 repeated string entries = 1; | |
518 } | |
519 | |
520 message PolicyOptions { | |
521 enum PolicyMode { | |
522 // The given settings are applied regardless of user choice. | |
523 MANDATORY = 0; | |
524 // The user may choose to override the given settings. | |
525 RECOMMENDED = 1; | |
526 // No policy value is present and the policy should be ignored. | |
527 UNSET = 2; | |
528 } | |
529 optional PolicyMode mode = 1 [default = MANDATORY]; | |
530 } | |
531 | |
532 message BooleanPolicyProto { | |
533 optional PolicyOptions policy_options = 1; | |
534 optional bool value = 2; | |
535 } | |
536 | |
537 message IntegerPolicyProto { | |
538 optional PolicyOptions policy_options = 1; | |
539 optional int64 value = 2; | |
540 } | |
541 | |
542 message StringPolicyProto { | |
543 optional PolicyOptions policy_options = 1; | |
544 optional string value = 2; | |
545 } | |
546 | |
547 message StringListPolicyProto { | |
548 optional PolicyOptions policy_options = 1; | |
549 optional StringList value = 2; | |
550 } | |
551 | |
552 ''' | |
553 | |
554 | |
555 # Field IDs [1..RESERVED_IDS] will not be used in the wrapping protobuf. | |
556 RESERVED_IDS = 2 | |
557 | |
558 | |
559 def _WritePolicyProto(f, policy, fields): | |
560 _OutputComment(f, policy.caption + '\n\n' + policy.desc) | |
561 if policy.items is not None: | |
562 _OutputComment(f, '\nValid values:') | |
563 for item in policy.items: | |
564 _OutputComment(f, ' %s: %s' % (str(item.value), item.caption)) | |
565 if policy.policy_type == 'TYPE_DICTIONARY': | |
566 _OutputComment(f, '\nValue schema:\n%s' % | |
567 json.dumps(policy.schema, sort_keys=True, indent=4, | |
568 separators=(',', ': '))) | |
569 _OutputComment(f, '\nSupported on: %s' % ', '.join(policy.platforms)) | |
570 f.write('message %sProto {\n' % policy.name) | |
571 f.write(' optional PolicyOptions policy_options = 1;\n') | |
572 f.write(' optional %s %s = 2;\n' % (policy.protobuf_type, policy.name)) | |
573 f.write('}\n\n') | |
574 fields += [ ' optional %sProto %s = %s;\n' % | |
575 (policy.name, policy.name, policy.id + RESERVED_IDS) ] | |
576 | |
577 | |
578 def _WriteChromeSettingsProtobuf(policies, os, f): | |
579 f.write(CHROME_SETTINGS_PROTO_HEAD) | |
580 | |
581 fields = [] | |
582 f.write('// PBs for individual settings.\n\n') | |
583 for policy in policies: | |
584 # Note: this protobuf also gets the unsupported policies, since it's an | |
585 # exaustive list of all the supported user policies on any platform. | |
586 if not policy.is_device_only: | |
587 _WritePolicyProto(f, policy, fields) | |
588 | |
589 f.write('// --------------------------------------------------\n' | |
590 '// Big wrapper PB containing the above groups.\n\n' | |
591 'message ChromeSettingsProto {\n') | |
592 f.write(''.join(fields)) | |
593 f.write('}\n\n') | |
594 | |
595 | |
596 def _WriteCloudPolicyProtobuf(policies, os, f): | |
597 f.write(CLOUD_POLICY_PROTO_HEAD) | |
598 f.write('message CloudPolicySettings {\n') | |
599 for policy in policies: | |
600 if policy.is_supported and not policy.is_device_only: | |
601 f.write(' optional %sPolicyProto %s = %s;\n' % | |
602 (policy.policy_protobuf_type, policy.name, | |
603 policy.id + RESERVED_IDS)) | |
604 f.write('}\n\n') | |
605 | |
606 | |
607 #------------------ protobuf decoder -------------------------------# | |
608 | |
609 CPP_HEAD = ''' | |
610 #include <limits> | |
611 #include <string> | |
612 | |
613 #include "base/basictypes.h" | |
614 #include "base/callback.h" | |
615 #include "base/json/json_reader.h" | |
616 #include "base/logging.h" | |
617 #include "base/memory/scoped_ptr.h" | |
618 #include "base/memory/weak_ptr.h" | |
619 #include "base/values.h" | |
620 #include "components/policy/core/common/cloud/cloud_external_data_manager.h" | |
621 #include "components/policy/core/common/external_data_fetcher.h" | |
622 #include "components/policy/core/common/policy_map.h" | |
623 #include "policy/policy_constants.h" | |
624 #include "policy/proto/cloud_policy.pb.h" | |
625 | |
626 using google::protobuf::RepeatedPtrField; | |
627 | |
628 namespace policy { | |
629 | |
630 namespace em = enterprise_management; | |
631 | |
632 base::Value* DecodeIntegerValue(google::protobuf::int64 value) { | |
633 if (value < std::numeric_limits<int>::min() || | |
634 value > std::numeric_limits<int>::max()) { | |
635 LOG(WARNING) << "Integer value " << value | |
636 << " out of numeric limits, ignoring."; | |
637 return NULL; | |
638 } | |
639 | |
640 return base::Value::CreateIntegerValue(static_cast<int>(value)); | |
641 } | |
642 | |
643 base::ListValue* DecodeStringList(const em::StringList& string_list) { | |
644 base::ListValue* list_value = new base::ListValue; | |
645 RepeatedPtrField<std::string>::const_iterator entry; | |
646 for (entry = string_list.entries().begin(); | |
647 entry != string_list.entries().end(); ++entry) { | |
648 list_value->Append(base::Value::CreateStringValue(*entry)); | |
649 } | |
650 return list_value; | |
651 } | |
652 | |
653 base::Value* DecodeJson(const std::string& json) { | |
654 scoped_ptr<base::Value> root( | |
655 base::JSONReader::Read(json, base::JSON_ALLOW_TRAILING_COMMAS)); | |
656 | |
657 if (!root) | |
658 LOG(WARNING) << "Invalid JSON string, ignoring: " << json; | |
659 | |
660 // Accept any Value type that parsed as JSON, and leave it to the handler to | |
661 // convert and check the concrete type. | |
662 return root.release(); | |
663 } | |
664 | |
665 void DecodePolicy(const em::CloudPolicySettings& policy, | |
666 base::WeakPtr<CloudExternalDataManager> external_data_manager, | |
667 PolicyMap* map) { | |
668 ''' | |
669 | |
670 | |
671 CPP_FOOT = '''} | |
672 | |
673 } // namespace policy | |
674 ''' | |
675 | |
676 | |
677 def _CreateValue(type, arg): | |
678 if type == 'TYPE_BOOLEAN': | |
679 return 'base::Value::CreateBooleanValue(%s)' % arg | |
680 elif type == 'TYPE_INTEGER': | |
681 return 'DecodeIntegerValue(%s)' % arg | |
682 elif type == 'TYPE_STRING': | |
683 return 'base::Value::CreateStringValue(%s)' % arg | |
684 elif type == 'TYPE_LIST': | |
685 return 'DecodeStringList(%s)' % arg | |
686 elif type == 'TYPE_DICTIONARY' or type == 'TYPE_EXTERNAL': | |
687 return 'DecodeJson(%s)' % arg | |
688 else: | |
689 raise NotImplementedError('Unknown type %s' % type) | |
690 | |
691 | |
692 def _CreateExternalDataFetcher(type, name): | |
693 if type == 'TYPE_EXTERNAL': | |
694 return 'new ExternalDataFetcher(external_data_manager, key::k%s)' % name | |
695 return 'NULL' | |
696 | |
697 | |
698 def _WritePolicyCode(f, policy): | |
699 membername = policy.name.lower() | |
700 proto_type = '%sPolicyProto' % policy.policy_protobuf_type | |
701 f.write(' if (policy.has_%s()) {\n' % membername) | |
702 f.write(' const em::%s& policy_proto = policy.%s();\n' % | |
703 (proto_type, membername)) | |
704 f.write(' if (policy_proto.has_value()) {\n') | |
705 f.write(' PolicyLevel level = POLICY_LEVEL_MANDATORY;\n' | |
706 ' bool do_set = true;\n' | |
707 ' if (policy_proto.has_policy_options()) {\n' | |
708 ' do_set = false;\n' | |
709 ' switch(policy_proto.policy_options().mode()) {\n' | |
710 ' case em::PolicyOptions::MANDATORY:\n' | |
711 ' do_set = true;\n' | |
712 ' level = POLICY_LEVEL_MANDATORY;\n' | |
713 ' break;\n' | |
714 ' case em::PolicyOptions::RECOMMENDED:\n' | |
715 ' do_set = true;\n' | |
716 ' level = POLICY_LEVEL_RECOMMENDED;\n' | |
717 ' break;\n' | |
718 ' case em::PolicyOptions::UNSET:\n' | |
719 ' break;\n' | |
720 ' }\n' | |
721 ' }\n' | |
722 ' if (do_set) {\n') | |
723 f.write(' base::Value* value = %s;\n' % | |
724 (_CreateValue(policy.policy_type, 'policy_proto.value()'))) | |
725 # TODO(bartfab): |value| == NULL indicates that the policy value could not be | |
726 # parsed successfully. Surface such errors in the UI. | |
727 f.write(' if (value) {\n') | |
728 f.write(' ExternalDataFetcher* external_data_fetcher = %s;\n' % | |
729 _CreateExternalDataFetcher(policy.policy_type, policy.name)) | |
730 f.write(' map->Set(key::k%s, level, POLICY_SCOPE_USER,\n' % | |
731 policy.name) | |
732 f.write(' value, external_data_fetcher);\n' | |
733 ' }\n' | |
734 ' }\n' | |
735 ' }\n' | |
736 ' }\n') | |
737 | |
738 | |
739 def _WriteCloudPolicyDecoder(policies, os, f): | |
740 f.write(CPP_HEAD) | |
741 for policy in policies: | |
742 if policy.is_supported and not policy.is_device_only: | |
743 _WritePolicyCode(f, policy) | |
744 f.write(CPP_FOOT) | |
745 | |
746 | |
747 if __name__ == '__main__': | |
748 sys.exit(main()) | |
OLD | NEW |