- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / policy / policy_loader_mac.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/policy/policy_loader_mac.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback.h"
10 #include "base/file_util.h"
11 #include "base/mac/foundation_util.h"
12 #include "base/mac/scoped_cftyperef.h"
13 #include "base/path_service.h"
14 #include "base/platform_file.h"
15 #include "base/sequenced_task_runner.h"
16 #include "base/strings/sys_string_conversions.h"
17 #include "base/values.h"
18 #include "chrome/browser/policy/external_data_fetcher.h"
19 #include "chrome/browser/policy/policy_bundle.h"
20 #include "chrome/browser/policy/policy_domain_descriptor.h"
21 #include "chrome/browser/policy/policy_load_status.h"
22 #include "chrome/browser/policy/policy_map.h"
23 #include "chrome/browser/policy/preferences_mac.h"
24 #include "components/policy/core/common/schema.h"
25 #include "policy/policy_constants.h"
26
27 using base::mac::CFCast;
28 using base::ScopedCFTypeRef;
29
30 namespace policy {
31
32 namespace {
33
34 // Callback function for CFDictionaryApplyFunction. |key| and |value| are an
35 // entry of the CFDictionary that should be converted into an equivalent entry
36 // in the DictionaryValue in |context|.
37 void DictionaryEntryToValue(const void* key, const void* value, void* context) {
38   if (CFStringRef cf_key = CFCast<CFStringRef>(key)) {
39     base::Value* converted =
40         PolicyLoaderMac::CreateValueFromProperty(
41             static_cast<CFPropertyListRef>(value));
42     if (converted) {
43       const std::string string = base::SysCFStringRefToUTF8(cf_key);
44       static_cast<base::DictionaryValue *>(context)->Set(string, converted);
45     }
46   }
47 }
48
49 // Callback function for CFArrayApplyFunction. |value| is an entry of the
50 // CFArray that should be converted into an equivalent entry in the ListValue
51 // in |context|.
52 void ArrayEntryToValue(const void* value, void* context) {
53   base::Value* converted =
54       PolicyLoaderMac::CreateValueFromProperty(
55           static_cast<CFPropertyListRef>(value));
56   if (converted)
57     static_cast<base::ListValue *>(context)->Append(converted);
58 }
59
60 }  // namespace
61
62 PolicyLoaderMac::PolicyLoaderMac(
63     scoped_refptr<base::SequencedTaskRunner> task_runner,
64     const PolicyDefinitionList* policy_list,
65     const base::FilePath& managed_policy_path,
66     MacPreferences* preferences)
67     : AsyncPolicyLoader(task_runner),
68       policy_list_(policy_list),
69       preferences_(preferences),
70       managed_policy_path_(managed_policy_path) {}
71
72 PolicyLoaderMac::~PolicyLoaderMac() {}
73
74 void PolicyLoaderMac::InitOnBackgroundThread() {
75   if (!managed_policy_path_.empty()) {
76     watcher_.Watch(
77         managed_policy_path_, false,
78         base::Bind(&PolicyLoaderMac::OnFileUpdated, base::Unretained(this)));
79   }
80 }
81
82 scoped_ptr<PolicyBundle> PolicyLoaderMac::Load() {
83   preferences_->AppSynchronize(kCFPreferencesCurrentApplication);
84   scoped_ptr<PolicyBundle> bundle(new PolicyBundle());
85
86   // Load Chrome's policy.
87   // TODO(joaodasilva): use a schema for Chrome once it's generated and
88   // available from a PolicyDomainDescriptor.
89   PolicyMap& chrome_policy =
90       bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
91
92   PolicyLoadStatusSample status;
93   bool policy_present = false;
94   const PolicyDefinitionList::Entry* current;
95   for (current = policy_list_->begin; current != policy_list_->end; ++current) {
96     base::ScopedCFTypeRef<CFStringRef> name(
97         base::SysUTF8ToCFStringRef(current->name));
98     base::ScopedCFTypeRef<CFPropertyListRef> value(
99         preferences_->CopyAppValue(name, kCFPreferencesCurrentApplication));
100     if (!value.get())
101       continue;
102     policy_present = true;
103     bool forced =
104         preferences_->AppValueIsForced(name, kCFPreferencesCurrentApplication);
105     PolicyLevel level = forced ? POLICY_LEVEL_MANDATORY :
106                                  POLICY_LEVEL_RECOMMENDED;
107     // TODO(joaodasilva): figure the policy scope.
108     base::Value* policy = CreateValueFromProperty(value);
109     if (policy)
110       chrome_policy.Set(current->name, level, POLICY_SCOPE_USER, policy, NULL);
111     else
112       status.Add(POLICY_LOAD_STATUS_PARSE_ERROR);
113   }
114
115   if (!policy_present)
116     status.Add(POLICY_LOAD_STATUS_NO_POLICY);
117
118   // Load policy for the registered components.
119   static const struct {
120     PolicyDomain domain;
121     const char* domain_name;
122   } kSupportedDomains[] = {
123     { POLICY_DOMAIN_EXTENSIONS, "extensions" },
124   };
125   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSupportedDomains); ++i) {
126     DescriptorMap::const_iterator it =
127         descriptor_map().find(kSupportedDomains[i].domain);
128     if (it != descriptor_map().end()) {
129       LoadPolicyForDomain(
130           it->second, kSupportedDomains[i].domain_name, bundle.get());
131     }
132   }
133
134   return bundle.Pass();
135 }
136
137 base::Time PolicyLoaderMac::LastModificationTime() {
138   base::PlatformFileInfo file_info;
139   if (!file_util::GetFileInfo(managed_policy_path_, &file_info) ||
140       file_info.is_directory) {
141     return base::Time();
142   }
143
144   return file_info.last_modified;
145 }
146
147 // static
148 base::Value* PolicyLoaderMac::CreateValueFromProperty(
149     CFPropertyListRef property) {
150   if (CFCast<CFNullRef>(property))
151     return base::Value::CreateNullValue();
152
153   if (CFBooleanRef boolean = CFCast<CFBooleanRef>(property))
154     return base::Value::CreateBooleanValue(CFBooleanGetValue(boolean));
155
156   if (CFNumberRef number = CFCast<CFNumberRef>(property)) {
157     // CFNumberGetValue() converts values implicitly when the conversion is not
158     // lossy. Check the type before trying to convert.
159     if (CFNumberIsFloatType(number)) {
160       double double_value;
161       if (CFNumberGetValue(number, kCFNumberDoubleType, &double_value))
162         return base::Value::CreateDoubleValue(double_value);
163     } else {
164       int int_value;
165       if (CFNumberGetValue(number, kCFNumberIntType, &int_value))
166         return base::Value::CreateIntegerValue(int_value);
167     }
168   }
169
170   if (CFStringRef string = CFCast<CFStringRef>(property))
171     return base::Value::CreateStringValue(base::SysCFStringRefToUTF8(string));
172
173   if (CFDictionaryRef dict = CFCast<CFDictionaryRef>(property)) {
174     base::DictionaryValue* dict_value = new base::DictionaryValue();
175     CFDictionaryApplyFunction(dict, DictionaryEntryToValue, dict_value);
176     return dict_value;
177   }
178
179   if (CFArrayRef array = CFCast<CFArrayRef>(property)) {
180     base::ListValue* list_value = new base::ListValue();
181     CFArrayApplyFunction(array,
182                          CFRangeMake(0, CFArrayGetCount(array)),
183                          ArrayEntryToValue,
184                          list_value);
185     return list_value;
186   }
187
188   return NULL;
189 }
190
191 void PolicyLoaderMac::LoadPolicyForDomain(
192     scoped_refptr<const PolicyDomainDescriptor> descriptor,
193     const std::string& domain_name,
194     PolicyBundle* bundle) {
195   std::string id_prefix(base::mac::BaseBundleID());
196   id_prefix.append(".").append(domain_name).append(".");
197
198   for (PolicyDomainDescriptor::SchemaMap::const_iterator it_schema =
199            descriptor->components().begin();
200        it_schema != descriptor->components().end(); ++it_schema) {
201     PolicyMap policy;
202     LoadPolicyForComponent(
203         id_prefix + it_schema->first, it_schema->second, &policy);
204     if (!policy.empty()) {
205       bundle->Get(PolicyNamespace(descriptor->domain(), it_schema->first))
206           .Swap(&policy);
207     }
208   }
209 }
210
211 void PolicyLoaderMac::LoadPolicyForComponent(
212     const std::string& bundle_id_string,
213     Schema schema,
214     PolicyMap* policy) {
215   // TODO(joaodasilva): extensions may be registered in a PolicyDomainDescriptor
216   // without a schema, to allow a graceful update of the Legacy Browser Support
217   // extension on Windows. Remove this check once that support is removed.
218   if (!schema.valid())
219     return;
220
221   base::ScopedCFTypeRef<CFStringRef> bundle_id(
222       base::SysUTF8ToCFStringRef(bundle_id_string));
223   preferences_->AppSynchronize(bundle_id);
224
225   for (Schema::Iterator it = schema.GetPropertiesIterator();
226        !it.IsAtEnd(); it.Advance()) {
227     base::ScopedCFTypeRef<CFStringRef> pref_name(
228         base::SysUTF8ToCFStringRef(it.key()));
229     base::ScopedCFTypeRef<CFPropertyListRef> value(
230         preferences_->CopyAppValue(pref_name, bundle_id));
231     if (!value.get())
232       continue;
233     bool forced =
234         preferences_->AppValueIsForced(pref_name, bundle_id);
235     PolicyLevel level = forced ? POLICY_LEVEL_MANDATORY :
236                                  POLICY_LEVEL_RECOMMENDED;
237     scoped_ptr<base::Value> policy_value(CreateValueFromProperty(value));
238     if (policy_value) {
239       policy->Set(it.key(), level, POLICY_SCOPE_USER,
240                   policy_value.release(), NULL);
241     }
242   }
243 }
244
245 void PolicyLoaderMac::OnFileUpdated(const base::FilePath& path, bool error) {
246   if (!error)
247     Reload(false);
248 }
249
250 }  // namespace policy