1// Copyright (c) 2011 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/chromeos/plugin_selection_policy.h"
6
7#include <algorithm>
8#include <iostream>
9#include <map>
10#include <sstream>
11#include <string>
12#include <vector>
13
14#include "base/auto_reset.h"
15#include "base/file_path.h"
16#include "base/file_util.h"
17#include "base/logging.h"
18#include "base/string_util.h"
19#include "content/browser/browser_thread.h"
20#include "googleurl/src/gurl.h"
21
22#if !defined(OS_CHROMEOS)
23#error This file is meant to be compiled on ChromeOS only.
24#endif
25
26using std::vector;
27using std::string;
28using std::pair;
29using std::map;
30
31namespace chromeos {
32
33static const char kPluginSelectionPolicyFile[] =
34    "/usr/share/chromeos-assets/flash/plugin_policy";
35
36PluginSelectionPolicy::PluginSelectionPolicy()
37    : init_from_file_finished_(false) {
38}
39
40void PluginSelectionPolicy::StartInit() {
41  // Initialize the policy on the FILE thread, since it reads from a
42  // policy file.
43  BrowserThread::PostTask(
44      BrowserThread::FILE, FROM_HERE,
45      NewRunnableMethod(this, &chromeos::PluginSelectionPolicy::Init));
46}
47
48bool PluginSelectionPolicy::Init() {
49  return InitFromFile(FilePath(kPluginSelectionPolicyFile));
50}
51
52bool PluginSelectionPolicy::InitFromFile(const FilePath& policy_file) {
53  // This must always be called from the FILE thread.
54  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
55
56  string data;
57  // This should be a really small file, so we're OK with just
58  // slurping it.
59  if (!file_util::ReadFileToString(policy_file, &data)) {
60    LOG(ERROR) << "Unable to read plugin policy file \""
61               << policy_file.value() << "\".";
62    init_from_file_finished_ = true;
63    return false;
64  }
65
66  std::istringstream input_stream(data);
67  string line;
68  map<string, Policy> policies;
69  Policy policy;
70  string last_plugin;
71
72  while (std::getline(input_stream, line)) {
73    // Strip comments.
74    string::size_type pos = line.find("#");
75    if (pos != string::npos) {
76      line = line.substr(0, pos);
77    }
78    TrimWhitespaceASCII(line, TRIM_ALL, &line);
79    if (line.find("allow") == 0) {
80      // Has to be preceeded by a "plugin" statement.
81      if (last_plugin.empty()) {
82        LOG(ERROR) << "Plugin policy file error: 'allow' out of context.";
83        init_from_file_finished_ = true;
84        return false;
85      }
86      line = line.substr(5);
87      TrimWhitespaceASCII(line, TRIM_ALL, &line);
88      line = StringToLowerASCII(line);
89      policy.push_back(make_pair(true, line));
90    }
91    if (line.find("deny") == 0) {
92      // Has to be preceeded by a "plugin" statement.
93      if (last_plugin.empty()) {
94        LOG(ERROR) << "Plugin policy file error: 'deny' out of context.";
95        init_from_file_finished_ = true;
96        return false;
97      }
98      line = line.substr(4);
99      TrimWhitespaceASCII(line, TRIM_ALL, &line);
100      line = StringToLowerASCII(line);
101      policy.push_back(make_pair(false, line));
102    }
103    if (line.find("plugin") == 0) {
104      line = line.substr(6);
105      TrimWhitespaceASCII(line, TRIM_ALL, &line);
106      if (!policy.empty() && !last_plugin.empty())
107        policies.insert(make_pair(last_plugin, policy));
108      last_plugin = line;
109      policy.clear();
110    }
111  }
112
113  if (!last_plugin.empty())
114    policies.insert(make_pair(last_plugin, policy));
115
116  policies_.swap(policies);
117  init_from_file_finished_ = true;
118  return true;
119}
120
121int PluginSelectionPolicy::FindFirstAllowed(
122    const GURL& url,
123    const std::vector<webkit::npapi::WebPluginInfo>& info) {
124  for (std::vector<webkit::npapi::WebPluginInfo>::size_type i = 0;
125       i < info.size(); ++i) {
126    if (IsAllowed(url, info[i].path))
127      return i;
128  }
129  return -1;
130}
131
132bool PluginSelectionPolicy::IsAllowed(const GURL& url,
133                                      const FilePath& path) {
134  // This must always be called from the FILE thread, to be sure
135  // initialization doesn't happen at the same time.
136  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
137
138  // Make sure that we notice if this starts being called before
139  // initialization is complete.  Right now it is guaranteed only by
140  // the startup order and the fact that InitFromFile runs on the FILE
141  // thread too.
142  DCHECK(init_from_file_finished_)
143      << "Tried to check policy before policy is initialized.";
144
145  string name = path.BaseName().value();
146
147  PolicyMap::iterator policy_iter = policies_.find(name);
148  if (policy_iter != policies_.end()) {
149    Policy& policy(policy_iter->second);
150
151    // We deny by default. (equivalent to "deny" at the top of the section)
152    bool allow = false;
153
154    for (Policy::iterator iter = policy.begin(); iter != policy.end(); ++iter) {
155      bool policy_allow = iter->first;
156      string& policy_domain = iter->second;
157      if (policy_domain.empty() || url.DomainIs(policy_domain.c_str(),
158                                                policy_domain.size())) {
159        allow = policy_allow;
160      }
161    }
162    return allow;
163  }
164
165  // If it's not in the policy file, then we assume it's OK to allow
166  // it.
167  return true;
168}
169
170}  // namespace chromeos
171