Home | History | Annotate | Download | only in tools
      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