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()) 749