• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/extensions/external_pref_loader.h"
6 
7 #include "base/bind.h"
8 #include "base/files/file_enumerator.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/json/json_file_value_serializer.h"
12 #include "base/json/json_string_value_serializer.h"
13 #include "base/logging.h"
14 #include "base/metrics/histogram.h"
15 #include "base/path_service.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "chrome/common/chrome_paths.h"
19 #include "content/public/browser/browser_thread.h"
20 
21 using content::BrowserThread;
22 
23 namespace {
24 
25 base::FilePath::CharType kExternalExtensionJson[] =
26     FILE_PATH_LITERAL("external_extensions.json");
27 
GetPrefsCandidateFilesFromFolder(const base::FilePath & external_extension_search_path)28 std::set<base::FilePath> GetPrefsCandidateFilesFromFolder(
29       const base::FilePath& external_extension_search_path) {
30   CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
31 
32   std::set<base::FilePath> external_extension_paths;
33 
34   if (!base::PathExists(external_extension_search_path)) {
35     // Does not have to exist.
36     return external_extension_paths;
37   }
38 
39   base::FileEnumerator json_files(
40       external_extension_search_path,
41       false,  // Recursive.
42       base::FileEnumerator::FILES);
43 #if defined(OS_WIN)
44   base::FilePath::StringType extension = base::UTF8ToWide(std::string(".json"));
45 #elif defined(OS_POSIX)
46   base::FilePath::StringType extension(".json");
47 #endif
48   do {
49     base::FilePath file = json_files.Next();
50     if (file.BaseName().value() == kExternalExtensionJson)
51       continue;  // Already taken care of elsewhere.
52     if (file.empty())
53       break;
54     if (file.MatchesExtension(extension)) {
55       external_extension_paths.insert(file.BaseName());
56     } else {
57       DVLOG(1) << "Not considering: " << file.LossyDisplayName()
58                << " (does not have a .json extension)";
59     }
60   } while (true);
61 
62   return external_extension_paths;
63 }
64 
65 // Extracts extension information from a json file serialized by |serializer|.
66 // |path| is only used for informational purposes (outputted when an error
67 // occurs). An empty dictionary is returned in case of failure (e.g. invalid
68 // path or json content).
69 // Caller takes ownership of the returned dictionary.
ExtractExtensionPrefs(base::ValueSerializer * serializer,const base::FilePath & path)70 base::DictionaryValue* ExtractExtensionPrefs(base::ValueSerializer* serializer,
71                                              const base::FilePath& path) {
72   std::string error_msg;
73   base::Value* extensions = serializer->Deserialize(NULL, &error_msg);
74   if (!extensions) {
75     LOG(WARNING) << "Unable to deserialize json data: " << error_msg
76                  << " in file " << path.value() << ".";
77     return new base::DictionaryValue;
78   }
79 
80   base::DictionaryValue* ext_dictionary = NULL;
81   if (extensions->GetAsDictionary(&ext_dictionary))
82     return ext_dictionary;
83 
84   LOG(WARNING) << "Expected a JSON dictionary in file "
85                << path.value() << ".";
86   return new base::DictionaryValue;
87 }
88 
89 }  // namespace
90 
91 namespace extensions {
92 
ExternalPrefLoader(int base_path_id,Options options)93 ExternalPrefLoader::ExternalPrefLoader(int base_path_id, Options options)
94     : base_path_id_(base_path_id), options_(options) {
95   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
96 }
97 
GetBaseCrxFilePath()98 const base::FilePath ExternalPrefLoader::GetBaseCrxFilePath() {
99   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
100 
101   // |base_path_| was set in LoadOnFileThread().
102   return base_path_;
103 }
104 
StartLoading()105 void ExternalPrefLoader::StartLoading() {
106   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
107   BrowserThread::PostTask(
108       BrowserThread::FILE, FROM_HERE,
109       base::Bind(&ExternalPrefLoader::LoadOnFileThread, this));
110 }
111 
LoadOnFileThread()112 void ExternalPrefLoader::LoadOnFileThread() {
113   CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
114 
115   scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue);
116 
117   // TODO(skerner): Some values of base_path_id_ will cause
118   // PathService::Get() to return false, because the path does
119   // not exist.  Find and fix the build/install scripts so that
120   // this can become a CHECK().  Known examples include chrome
121   // OS developer builds and linux install packages.
122   // Tracked as crbug.com/70402 .
123   if (PathService::Get(base_path_id_, &base_path_)) {
124     ReadExternalExtensionPrefFile(prefs.get());
125 
126     if (!prefs->empty())
127       LOG(WARNING) << "You are using an old-style extension deployment method "
128                       "(external_extensions.json), which will soon be "
129                       "deprecated. (see http://developer.chrome.com/"
130                       "extensions/external_extensions.html)";
131 
132     ReadStandaloneExtensionPrefFiles(prefs.get());
133   }
134 
135   prefs_.swap(prefs);
136 
137   if (base_path_id_ == chrome::DIR_EXTERNAL_EXTENSIONS) {
138     UMA_HISTOGRAM_COUNTS_100("Extensions.ExternalJsonCount",
139                              prefs_->size());
140   }
141 
142   // If we have any records to process, then we must have
143   // read at least one .json file.  If so, then we should have
144   // set |base_path_|.
145   if (!prefs_->empty())
146     CHECK(!base_path_.empty());
147 
148   BrowserThread::PostTask(
149       BrowserThread::UI, FROM_HERE,
150       base::Bind(&ExternalPrefLoader::LoadFinished, this));
151 }
152 
ReadExternalExtensionPrefFile(base::DictionaryValue * prefs)153 void ExternalPrefLoader::ReadExternalExtensionPrefFile(
154     base::DictionaryValue* prefs) {
155   CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
156   CHECK(NULL != prefs);
157 
158   base::FilePath json_file = base_path_.Append(kExternalExtensionJson);
159 
160   if (!base::PathExists(json_file)) {
161     // This is not an error.  The file does not exist by default.
162     return;
163   }
164 
165   if (IsOptionSet(ENSURE_PATH_CONTROLLED_BY_ADMIN)) {
166 #if defined(OS_MACOSX)
167     if (!base::VerifyPathControlledByAdmin(json_file)) {
168       LOG(ERROR) << "Can not read external extensions source.  The file "
169                  << json_file.value() << " and every directory in its path, "
170                  << "must be owned by root, have group \"admin\", and not be "
171                  << "writable by all users. These restrictions prevent "
172                  << "unprivleged users from making chrome install extensions "
173                  << "on other users' accounts.";
174       return;
175     }
176 #else
177     // The only platform that uses this check is Mac OS.  If you add one,
178     // you need to implement base::VerifyPathControlledByAdmin() for
179     // that platform.
180     NOTREACHED();
181 #endif  // defined(OS_MACOSX)
182   }
183 
184   JSONFileValueSerializer serializer(json_file);
185   scoped_ptr<base::DictionaryValue> ext_prefs(
186       ExtractExtensionPrefs(&serializer, json_file));
187   if (ext_prefs)
188     prefs->MergeDictionary(ext_prefs.get());
189 }
190 
ReadStandaloneExtensionPrefFiles(base::DictionaryValue * prefs)191 void ExternalPrefLoader::ReadStandaloneExtensionPrefFiles(
192     base::DictionaryValue* prefs) {
193   CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
194   CHECK(NULL != prefs);
195 
196   // First list the potential .json candidates.
197   std::set<base::FilePath>
198       candidates = GetPrefsCandidateFilesFromFolder(base_path_);
199   if (candidates.empty()) {
200     DVLOG(1) << "Extension candidates list empty";
201     return;
202   }
203 
204   // For each file read the json description & build the proper
205   // associated prefs.
206   for (std::set<base::FilePath>::const_iterator it = candidates.begin();
207        it != candidates.end();
208        ++it) {
209     base::FilePath extension_candidate_path = base_path_.Append(*it);
210 
211     std::string id =
212 #if defined(OS_WIN)
213         base::UTF16ToASCII(
214             extension_candidate_path.RemoveExtension().BaseName().value());
215 #elif defined(OS_POSIX)
216         extension_candidate_path.RemoveExtension().BaseName().value().c_str();
217 #endif
218 
219     DVLOG(1) << "Reading json file: "
220              << extension_candidate_path.LossyDisplayName().c_str();
221 
222     JSONFileValueSerializer serializer(extension_candidate_path);
223     scoped_ptr<base::DictionaryValue> ext_prefs(
224         ExtractExtensionPrefs(&serializer, extension_candidate_path));
225     if (ext_prefs) {
226       DVLOG(1) << "Adding extension with id: " << id;
227       prefs->Set(id, ext_prefs.release());
228     }
229   }
230 }
231 
ExternalTestingLoader(const std::string & json_data,const base::FilePath & fake_base_path)232 ExternalTestingLoader::ExternalTestingLoader(
233     const std::string& json_data,
234     const base::FilePath& fake_base_path)
235     : fake_base_path_(fake_base_path) {
236   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
237   JSONStringValueSerializer serializer(json_data);
238   base::FilePath fake_json_path = fake_base_path.AppendASCII("fake.json");
239   testing_prefs_.reset(ExtractExtensionPrefs(&serializer, fake_json_path));
240 }
241 
StartLoading()242 void ExternalTestingLoader::StartLoading() {
243   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
244   prefs_.reset(testing_prefs_->DeepCopy());
245   LoadFinished();
246 }
247 
~ExternalTestingLoader()248 ExternalTestingLoader::~ExternalTestingLoader() {}
249 
GetBaseCrxFilePath()250 const base::FilePath ExternalTestingLoader::GetBaseCrxFilePath() {
251   return fake_base_path_;
252 }
253 
254 }  // namespace extensions
255