Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(962)

Side by Side Diff: chrome/common/extensions/extension_unpacker.cc

Issue 115595: Have the browser process rewrite manifest.json and theme images that the... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/common/extensions/extension_unpacker.h ('k') | chrome/common/render_messages.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/common/extensions/extension_unpacker.h" 5 #include "chrome/common/extensions/extension_unpacker.h"
6 6
7 #include "base/file_util.h" 7 #include "base/file_util.h"
8 #include "base/scoped_handle.h" 8 #include "base/scoped_handle.h"
9 #include "base/scoped_temp_dir.h" 9 #include "base/scoped_temp_dir.h"
10 #include "base/string_util.h" 10 #include "base/string_util.h"
11 #include "base/third_party/nss/blapi.h" 11 #include "base/third_party/nss/blapi.h"
12 #include "base/third_party/nss/sha256.h" 12 #include "base/third_party/nss/sha256.h"
13 #include "base/thread.h" 13 #include "base/thread.h"
14 #include "base/values.h" 14 #include "base/values.h"
15 #include "net/base/file_stream.h" 15 #include "net/base/file_stream.h"
16 // TODO(mpcomplete): move to common 16 // TODO(mpcomplete): move to common
17 #include "chrome/browser/extensions/extension.h" 17 #include "chrome/browser/extensions/extension.h"
18 #include "chrome/common/json_value_serializer.h" 18 #include "chrome/common/json_value_serializer.h"
19 #include "chrome/common/notification_service.h" 19 #include "chrome/common/notification_service.h"
20 #include "chrome/common/unzip.h" 20 #include "chrome/common/unzip.h"
21 #include "chrome/common/url_constants.h" 21 #include "chrome/common/url_constants.h"
22 #include "third_party/skia/include/core/SkBitmap.h"
23 #include "webkit/glue/image_decoder.h"
22 24
23 namespace { 25 namespace {
24 const char kCurrentVersionFileName[] = "Current Version"; 26 const char kCurrentVersionFileName[] = "Current Version";
25 27
26 // The name of a temporary directory to install an extension into for 28 // The name of a temporary directory to install an extension into for
27 // validation before finalizing install. 29 // validation before finalizing install.
28 const char kTempExtensionName[] = "TEMP_INSTALL"; 30 const char kTempExtensionName[] = "TEMP_INSTALL";
29 31
30 // Chromium Extension magic number 32 // Chromium Extension magic number
31 const char kExtensionFileMagic[] = "Cr24"; 33 const char kExtensionFileMagic[] = "Cr24";
(...skipping 20 matching lines...) Expand all
52 const wchar_t kRegistryExtensionVersion[] = L"version"; 54 const wchar_t kRegistryExtensionVersion[] = L"version";
53 55
54 #endif 56 #endif
55 57
56 // A marker file to indicate that an extension was installed from an external 58 // A marker file to indicate that an extension was installed from an external
57 // source. 59 // source.
58 const char kExternalInstallFile[] = "EXTERNAL_INSTALL"; 60 const char kExternalInstallFile[] = "EXTERNAL_INSTALL";
59 61
60 // The version of the extension package that this code understands. 62 // The version of the extension package that this code understands.
61 const uint32 kExpectedVersion = 1; 63 const uint32 kExpectedVersion = 1;
64 } // namespace
65
66 static SkBitmap DecodeImage(const FilePath& path) {
67 // Read the file from disk.
68 std::string file_contents;
69 if (!file_util::PathExists(path) ||
70 !file_util::ReadFileToString(path, &file_contents)) {
71 return SkBitmap();
72 }
73
74 // Decode the image using WebKit's image decoder.
75 const unsigned char* data =
76 reinterpret_cast<const unsigned char*>(file_contents.data());
77 webkit_glue::ImageDecoder decoder;
78 return decoder.Decode(data, file_contents.length());
79 }
80
81 static bool PathContainsParentDirectory(const FilePath& path) {
82 const FilePath::StringType kSeparators(FilePath::kSeparators);
83 const FilePath::StringType kParentDirectory(FilePath::kParentDirectory);
84 const size_t npos = FilePath::StringType::npos;
85 const FilePath::StringType& value = path.value();
86
87 for (size_t i = 0; i < value.length(); ) {
88 i = value.find(kParentDirectory, i);
89 if (i != npos) {
90 if ((i == 0 || kSeparators.find(value[i-1]) == npos) &&
91 (i+1 < value.length() || kSeparators.find(value[i+1]) == npos)) {
92 return true;
93 }
94 ++i;
95 }
96 }
97
98 return false;
62 } 99 }
63 100
64 // The extension file format is a header, followed by the manifest, followed 101 // The extension file format is a header, followed by the manifest, followed
65 // by the zip file. The header is a magic number, a version, the size of the 102 // by the zip file. The header is a magic number, a version, the size of the
66 // header, and the size of the manifest. These ints are 4 byte little endian. 103 // header, and the size of the manifest. These ints are 4 byte little endian.
67 DictionaryValue* ExtensionUnpacker::ReadManifest() { 104 DictionaryValue* ExtensionUnpacker::ReadPackageHeader() {
68 ScopedStdioHandle file(file_util::OpenFile(extension_path_, "rb")); 105 ScopedStdioHandle file(file_util::OpenFile(extension_path_, "rb"));
69 if (!file.get()) { 106 if (!file.get()) {
70 SetError("no such extension file"); 107 SetError("no such extension file");
71 return NULL; 108 return NULL;
72 } 109 }
73 110
74 // Read and verify the header. 111 // Read and verify the header.
75 ExtensionHeader header; 112 ExtensionHeader header;
76 size_t len; 113 size_t len;
77 114
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
159 } 196 }
160 197
161 // TODO(erikkay): The manifest will also contain a signature of the hash 198 // TODO(erikkay): The manifest will also contain a signature of the hash
162 // (or perhaps the whole manifest) for authentication purposes. 199 // (or perhaps the whole manifest) for authentication purposes.
163 200
164 // The caller owns val (now cast to manifest). 201 // The caller owns val (now cast to manifest).
165 val.release(); 202 val.release();
166 return manifest; 203 return manifest;
167 } 204 }
168 205
206 DictionaryValue* ExtensionUnpacker::ReadManifest() {
207 FilePath manifest_path =
208 temp_install_dir_.AppendASCII(Extension::kManifestFilename);
209 if (!file_util::PathExists(manifest_path)) {
210 SetError(Extension::kInvalidManifestError);
211 return NULL;
212 }
213
214 JSONFileValueSerializer serializer(manifest_path);
215 std::string error;
216 scoped_ptr<Value> root(serializer.Deserialize(&error));
217 if (!root.get()) {
218 SetError(error);
219 return NULL;
220 }
221
222 if (!root->IsType(Value::TYPE_DICTIONARY)) {
223 SetError(Extension::kInvalidManifestError);
224 return NULL;
225 }
226
227 return static_cast<DictionaryValue*>(root.release());
228 }
229
169 bool ExtensionUnpacker::Run() { 230 bool ExtensionUnpacker::Run() {
170 LOG(INFO) << "Installing extension " << extension_path_.value(); 231 LOG(INFO) << "Installing extension " << extension_path_.value();
171 232
172 // Read and verify the extension. 233 // Read and verify the extension.
173 scoped_ptr<DictionaryValue> manifest(ReadManifest()); 234 scoped_ptr<DictionaryValue> header_manifest(ReadPackageHeader());
174 if (!manifest.get()) { 235 if (!header_manifest.get()) {
175 // ReadManifest has already reported the extension error. 236 // ReadPackageHeader has already reported the extension error.
176 return false;
177 }
178 Extension extension;
179 std::string error;
180 if (!extension.InitFromValue(*manifest,
181 true, // require ID
182 &error)) {
183 SetError("Invalid extension manifest.");
184 return false; 237 return false;
185 } 238 }
186 239
187 // ID is required for installed extensions. 240 // TODO(mpcomplete): it looks like this isn't actually necessary. We don't
188 if (extension.id().empty()) { 241 // use header_extension, and we check that the unzipped manifest is valid.
189 SetError("Required value 'id' is missing."); 242 Extension header_extension;
243 std::string error;
244 if (!header_extension.InitFromValue(*header_manifest,
245 true, // require ID
246 &error)) {
247 SetError(error);
190 return false; 248 return false;
191 } 249 }
192 250
193 // <profile>/Extensions/INSTALL_TEMP/<version> 251 // <profile>/Extensions/INSTALL_TEMP/<version>
194 std::string version = extension.VersionString(); 252 temp_install_dir_ =
195 FilePath temp_install =
196 extension_path_.DirName().AppendASCII(kTempExtensionName); 253 extension_path_.DirName().AppendASCII(kTempExtensionName);
197 if (!file_util::CreateDirectory(temp_install)) { 254 if (!file_util::CreateDirectory(temp_install_dir_)) {
198 SetError("Couldn't create directory for unzipping."); 255 SetError("Couldn't create directory for unzipping.");
199 return false; 256 return false;
200 } 257 }
201 258
202 if (!Unzip(extension_path_, temp_install, NULL)) { 259 if (!Unzip(extension_path_, temp_install_dir_, NULL)) {
203 SetError("Couldn't unzip extension."); 260 SetError("Couldn't unzip extension.");
204 return false; 261 return false;
205 } 262 }
206 263
264 // Parse the manifest.
265 parsed_manifest_.reset(ReadManifest());
266 if (!parsed_manifest_.get())
267 return false; // Error was already reported.
268
269 // Re-read the actual manifest into our extension struct.
270 Extension extension;
271 if (!extension.InitFromValue(*parsed_manifest_,
272 true, // require ID
273 &error)) {
274 SetError(error);
275 return false;
276 }
277
278 // Decode any images that the browser needs to display.
279 DictionaryValue* images = extension.GetThemeImages();
280 if (images) {
281 for (DictionaryValue::key_iterator it = images->begin_keys();
282 it != images->end_keys(); ++it) {
283 std::wstring val;
284 if (images->GetString(*it, &val)) {
285 if (!AddDecodedImage(FilePath::FromWStringHack(val)))
286 return false; // Error was already reported.
287 }
288 }
289 }
290
291 for (PageActionMap::const_iterator it = extension.page_actions().begin();
292 it != extension.page_actions().end(); ++it) {
293 if (!AddDecodedImage(it->second->icon_path()))
294 return false; // Error was already reported.
295 }
296
297 return true;
298 }
299
300 bool ExtensionUnpacker::AddDecodedImage(const FilePath& path) {
301 // Make sure it's not referencing a file outside the extension's subdir.
302 if (path.IsAbsolute() || PathContainsParentDirectory(path)) {
303 SetError("Path names must not be absolute or contain '..'.");
304 return false;
305 }
306
307 SkBitmap image_bitmap = DecodeImage(temp_install_dir_.Append(path));
308 if (image_bitmap.isNull()) {
309 SetError("Could not decode theme image.");
310 return false;
311 }
312
313 decoded_images_.push_back(MakeTuple(image_bitmap, path));
207 return true; 314 return true;
208 } 315 }
209 316
210 void ExtensionUnpacker::SetError(const std::string &error) { 317 void ExtensionUnpacker::SetError(const std::string &error) {
211 error_message_ = error; 318 error_message_ = error;
212 } 319 }
OLDNEW
« no previous file with comments | « chrome/common/extensions/extension_unpacker.h ('k') | chrome/common/render_messages.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698